Skip to content

Security ​

openapi-merge-cli reads, merges, and writes files using the paths specified in your openapi-merge.yaml/.json (or via --config). The tool assumes that this configuration file is trusted, the same way you trust a Makefile, package.json, or webpack.config.js in your repository. Do not run the CLI against a configuration file from an untrusted source without restricting the input and output locations, below.

Widened reads from resolveExternalReferences ​

resolveExternalReferences widens what gets read, not just written: with it on, the files and URLs the CLI loads are no longer limited to what inputs names — it follows wherever a $ref in any loaded document points, transitively. Leave it off (the default) unless your inputs are trusted to the same degree the configuration file itself is.

Restricting output: outputRoot / --restrict-output-to ​

For defence-in-depth in less-trusted contexts (for example, a server that accepts user-supplied configs), restrict where the CLI will write the merged output:

  • Add "outputRoot": "/path/to/safe/dir" to your configuration, or
  • Pass --restrict-output-to /path/to/safe/dir on the command line (the flag takes precedence over the config field).

When set, any resolved output path that doesn't lie under the configured root is rejected at config-load time with a clear error message, and the CLI exits with code 5 (ErrorUnsafePath). Symlink-out-of-jail tricks are defeated by realpath-ing the closest existing ancestor of the output.

When unset, the CLI keeps its historical permissive default and writes wherever you tell it to.

Restricting input: inputRoot / --restrict-input-to ​

The read-side counterpart, and the one that matters most once resolveExternalReferences is on — that setting is what makes the reachable file set transitive rather than confined to what inputs lists:

  • Add "inputRoot": "/path/to/safe/dir" to your configuration, or
  • Pass --restrict-input-to /path/to/safe/dir on the command line (the flag takes precedence over the config field).

When set, any local file the CLI would read — a declared inputFile or a file resolveExternalReferences discovers — that doesn't lie under the configured root is refused, and the CLI exits with code 10 (ErrorUnsafeInputPath). The offending file is never opened: the check runs before the read is attempted, using the same realpath-based containment check as outputRoot, extended to also realpath the file itself (not just its parent directory) before comparing — an input, unlike an output, normally already exists, so a symlink planted as the file itself, not just an ancestor directory, has to be defeated too.

A declared inputFile outside the root is reported before the merge starts at all; a discovered file outside the root aborts the merge the same way, rather than being left as an unresolved $ref the way an ordinary missing or unparseable discovered file is.

inputURL and URLs discovered via resolveExternalReferences are unaffected by inputRoot — it bounds the filesystem, not the network.

When unset, the CLI keeps its historical permissive default and reads whatever the inputs point to.

Summary ​

SettingBoundsConfig fieldCLI flagExit code on violation
Output containmentWhere the merged file can be writtenoutputRoot--restrict-output-to5
Input containmentWhich local files can be readinputRoot--restrict-input-to10

Neither is on by default; both are additive defence-in-depth for the case where the configuration or its inputs aren't fully trusted — not something a typical local/CI usage needs to set.

Released under the MIT License.