Skip to content

SecureFS.readFile bypasses the --working-dir / --no-insecure-file-read sandbox #3369

Description

@Adityaj0

Description

SecureFS (lib/run/secure-fs.js) is meant to restrict all file access performed on behalf of a collection run to --working-dir, with an additional --no-insecure-file-read flag to disallow escaping it entirely. It does this by overriding specific fs methods to route through resolvePath/resolvePathSync, which reject any resolved path outside the working directory:

https://github.com/postmanlabs/newman/blob/develop/lib/run/secure-fs.js#L143-L185

Only stat and createReadStream are overridden this way. Every other method — including readFile — is copied straight from the real fs module with no path check:

// Attach all functions in fs to postman-fs
Object.getOwnPropertyNames(fs).map((prop) => {
    if (prop === DEPRECATED_SYNC_WRITE_STREAM || prop === EXPERIMENTAL_PROMISE || typeof fs[prop] !== FUNCTION) {
        return;
    }
    SecureFS.prototype[prop] = fs[prop]; // <-- readFile ends up as raw fs.readFile
});

readFile is exactly the method postman-runtime calls in practice — for both client certificate files and CA certs:

https://github.com/postmanlabs/postman-runtime/blob/develop/lib/runner/request-helpers-presend.js#L443
https://github.com/postmanlabs/postman-runtime/blob/develop/lib/requester/requester-pool.js#L49

Both call fileResolver.readFile(path, callback) directly — never stat or createReadStream. So the one file-reading path that's actually exercised at runtime is completely unsandboxed, regardless of --working-dir or --no-insecure-file-read.

Impact

A collection (or environment/CLI options) that supplies an absolute certificate path — e.g. via sslClientCert/sslClientKey/sslClientCertList, or a collection-scripted variable feeding one of those — can read arbitrary files on the filesystem the newman process can access, even when the operator has explicitly locked the run down with --working-dir <dir> --no-insecure-file-read. This defeats the entire purpose of that flag combination for any newman invocation that runs an untrusted/third-party collection (e.g. in CI).

Repro

const SecureFS = require('./lib/run/secure-fs');
const fs = new SecureFS('/tmp/some-safe-working-dir', /* insecureFileRead */ false);

fs.resolvePath('/etc/passwd', (err) => {
  console.log(err.message); // PPERM: insecure file access outside working directory (correctly blocked)
});

fs.readFile('/etc/passwd', (err, data) => {
  console.log(err);          // null
  console.log(data.length);  // file contents read straight off disk, sandbox bypassed entirely
});

Fix

Override readFile the same way stat already is, routing through resolvePath:

SecureFS.prototype.readFile = function (path, options, callback) {
    if (!callback && typeof options === 'function') {
        callback = options;
        options = undefined;
    }

    this.resolvePath(path, (err, resolvedPath) => {
        if (err) {
            return callback(err);
        }

        return this._fs.readFile(resolvedPath, options, callback);
    });
};

PR with fix + regression tests (which fail against current develop and pass with the fix): #3370

Note: I'm aware SECURITY.md asks that vulnerabilities be reported privately to security@postman.com — filing here instead since the fix is attached and the affected surface (explicit certificate paths, not arbitrary collection-supplied file paths) is narrower than a full unauthenticated RCE-style bug. Happy to follow up privately if preferred.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions