fix(audit): skip env option arguments when reading a Node shebang

This commit is contained in:
Matt Van Horn committed 2026-11-03 10:13:38 -08:00
1 parent 02cd0e9360
commit 412f22ea78
2 files changed
+43 -3

No files matched your search

@@ -3068,6 +3068,11 @@ describe("ReleaseExposureReader Node shebang scripts", () => {
["#!/usr/bin/env node"],
["#!/usr/bin/env node\r"],
["#!/usr/bin/env -S node --no-warnings"],
["#!/usr/bin/env -S -u NODE_OPTIONS node"],
["#!/usr/bin/env -u NODE_OPTIONS node"],
["#!/usr/bin/env -C /srv/app node"],
["#!/usr/bin/env -Snode --no-warnings"],
["#!/usr/bin/env --unset NODE_OPTIONS -- node"],
["#!/usr/local/bin/node --no-warnings"],
["#! /usr/bin/nodejs"],
])("reads the map of %j in a directory", async (shebang) => {
+38 -3
View File
@@ -1308,12 +1308,47 @@ const hasNodeShebang = (prefix: Buffer): boolean => {
const [line = ""] = prefix.toString("latin1", 2).split("\n", 1);
const [interpreter = "", ...args] = line.trim().split(/[ \t]+/u);
const program =
basename(interpreter) === "env"
? args.find((arg) => !arg.startsWith("-") && !arg.includes("="))
: interpreter;
basename(interpreter) === "env" ? envProgram(args) : interpreter;
return program !== undefined && /^node(?:js)?$/u.test(basename(program));
};
/** `env` options whose argument is the next field, as in `-u NODE_OPTIONS`. */
const ENV_OPTION_WITH_ARGUMENT = new Set([
"-u",
"--unset",
"-C",
"--chdir",
"-P",
]);
/**
* The program an `env` shebang runs: the first field that is neither an
* option, an option's argument, nor a `NAME=value` setting. `-S` splits the
* rest of the line, so its attached text is read as the next fields.
*/
const envProgram = (args: string[]): string | undefined => {
const fields = [...args];
while (fields.length > 0) {
const field = fields.shift() ?? "";
if (field === "--") return fields[0];
if (ENV_OPTION_WITH_ARGUMENT.has(field)) {
fields.shift();
continue;
}
if (/^-[0iv]*S./u.test(field)) {
fields.unshift(field.slice(field.indexOf("S") + 1));
continue;
}
if (field.startsWith("--split-string=")) {
fields.unshift(field.slice("--split-string=".length));
continue;
}
if (field.startsWith("-") || field.includes("=")) continue;
return field;
}
return undefined;
};
/**
* A name is not a format: a package member called `payload.json` or
* `payload.js.map` can be a PE or an ASAR, and a script or map declaration