@node9/proxy 1.58.3 → 1.58.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli.js +15 -5
- package/dist/cli.mjs +15 -5
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -53196,8 +53196,8 @@ function promptTakesUntrusted(steps) {
|
|
|
53196
53196
|
}
|
|
53197
53197
|
function hasActorGate(wf, raw) {
|
|
53198
53198
|
const on = wf.on ?? raw["on"] ?? raw[true];
|
|
53199
|
-
const
|
|
53200
|
-
const labeled =
|
|
53199
|
+
const prTypes = (t) => t && Array.isArray(t.types) ? t.types.map(String) : [];
|
|
53200
|
+
const labeled = prTypes(on?.["pull_request_target"]).includes("labeled") || prTypes(on?.["pull_request"]).includes("labeled");
|
|
53201
53201
|
const ifs = [
|
|
53202
53202
|
wf.jobs ? Object.values(wf.jobs).map((j) => j.if) : [],
|
|
53203
53203
|
allSteps(wf).map((s) => s.step.if)
|
|
@@ -53266,6 +53266,9 @@ function analyzeWorkflow(path70, content) {
|
|
|
53266
53266
|
const triggers = triggerKeys(wf, raw);
|
|
53267
53267
|
const secretExposed = triggers.some((t) => /pull_request_target|workflow_run/i.test(t));
|
|
53268
53268
|
const forkInput = triggers.some((t) => /issue|pull_request|workflow_call/i.test(t));
|
|
53269
|
+
const privileged = triggers.some(
|
|
53270
|
+
(t) => /pull_request_target|pull_request_review|workflow_run|workflow_call|issue|discussion/i.test(t)
|
|
53271
|
+
);
|
|
53269
53272
|
const nonWrite = str(agentSteps.map((s) => s.with?.["allowed_non_write_users"]).find(Boolean));
|
|
53270
53273
|
const nonWriteStar = nonWrite === "*";
|
|
53271
53274
|
const nonWriteList = !!nonWrite && nonWrite !== "*";
|
|
@@ -53303,6 +53306,7 @@ function analyzeWorkflow(path70, content) {
|
|
|
53303
53306
|
const githubWriteTool = GH_WRITE_TOOL_RE.test(toolsBlob);
|
|
53304
53307
|
const canDamage = exfilOrRce || (hasGithubWritePerm(wf, agentJobs) || pat) && githubWriteTool;
|
|
53305
53308
|
if (!canDamage && severity && SEVERITY_RANK2[severity] > SEVERITY_RANK2.medium) severity = "medium";
|
|
53309
|
+
if (untrustedTrigger && !privileged && severity && severity !== "advisory") severity = "advisory";
|
|
53306
53310
|
if (!severity) severity = "advisory";
|
|
53307
53311
|
const signals = [];
|
|
53308
53312
|
if (secretExposed)
|
|
@@ -53310,6 +53314,10 @@ function analyzeWorkflow(path70, content) {
|
|
|
53310
53314
|
`runs with base-repo secrets (${triggers.filter((t) => /target|workflow_run/i.test(t)).join(", ")})`
|
|
53311
53315
|
);
|
|
53312
53316
|
else if (forkInput) signals.push(`triggered by untrusted input (${triggers.join(", ")})`);
|
|
53317
|
+
if (untrustedTrigger && !privileged)
|
|
53318
|
+
signals.push(
|
|
53319
|
+
"triggered by `pull_request` \u2014 fork PRs run with a read-only token (lower risk than pull_request_target)"
|
|
53320
|
+
);
|
|
53313
53321
|
if (head === "root") signals.push("checks out the untrusted PR head into the workspace root");
|
|
53314
53322
|
if (head === "subdir") signals.push("checks out the untrusted PR head into an isolated subdir");
|
|
53315
53323
|
if (promptUntrusted) signals.push("feeds untrusted PR/issue text to the agent");
|
|
@@ -53335,7 +53343,7 @@ function analyzeWorkflow(path70, content) {
|
|
|
53335
53343
|
file: path70,
|
|
53336
53344
|
signals,
|
|
53337
53345
|
mitigations: mitigations.length ? mitigations : void 0,
|
|
53338
|
-
fix: head === "root" ? "Do not check out the PR head into the workspace root under pull_request_target \u2014 check out the base ref, or isolate the head in a subdir (--add-dir). Add an actor gate and scope the agent tools." : "Add/verify an actor gate, scope the agent tools to read-only, and env-deny secrets. See Anthropic\u2019s claude-code-action security doc."
|
|
53346
|
+
fix: head === "root" && privileged ? "Do not check out the untrusted PR head into the workspace root under a privileged trigger (pull_request_target/workflow_run) \u2014 check out the base ref, or isolate the head in a subdir (--add-dir). Add an actor gate and scope the agent tools." : head === "root" ? "This runs under `pull_request` (fork PRs get a read-only token), so the head checkout is low-risk today \u2014 keep it on `pull_request` (not `pull_request_target`) and keep the actor gate + scoped tools." : "Add/verify an actor gate, scope the agent tools to read-only, and env-deny secrets. See Anthropic\u2019s claude-code-action security doc."
|
|
53339
53347
|
};
|
|
53340
53348
|
}
|
|
53341
53349
|
|
|
@@ -53509,12 +53517,14 @@ function renderScan(res) {
|
|
|
53509
53517
|
const head = res.worst === "critical" || res.worst === "high" ? import_chalk29.default.red.bold("\u26A0\uFE0F agent-security risk found") : res.worst ? import_chalk29.default.yellow("agent-security notes") : res.incomplete ? import_chalk29.default.yellow.bold("\u26A0\uFE0F INCOMPLETE \u2014 could not read all files") : import_chalk29.default.green("\u2705 agent-security: clean");
|
|
53510
53518
|
L.push(`\u{1F6E1}\uFE0F ${import_chalk29.default.bold("node9 scan-repo")} \xB7 ${res.source} \xB7 ${head}`);
|
|
53511
53519
|
L.push(import_chalk29.default.gray(` inspected ${res.inspected.length} config file(s), ${n} finding(s)`));
|
|
53512
|
-
if (res.incomplete)
|
|
53520
|
+
if (res.incomplete) {
|
|
53521
|
+
const rateLimited = res.notes.some((nt) => /rate limit/i.test(nt));
|
|
53513
53522
|
L.push(
|
|
53514
53523
|
import_chalk29.default.yellow.bold(
|
|
53515
|
-
" \u26A0\uFE0F
|
|
53524
|
+
rateLimited ? " \u26A0\uFE0F Rate-limited \u2014 some files were unread. NOT a clean bill of health; set GITHUB_TOKEN (or run `gh auth login`) and re-run." : " \u26A0\uFE0F A network error left some files unread. NOT a clean bill of health; re-run."
|
|
53516
53525
|
)
|
|
53517
53526
|
);
|
|
53527
|
+
}
|
|
53518
53528
|
L.push("");
|
|
53519
53529
|
for (const f of res.findings) {
|
|
53520
53530
|
L.push(
|
package/dist/cli.mjs
CHANGED
|
@@ -53189,8 +53189,8 @@ function promptTakesUntrusted(steps) {
|
|
|
53189
53189
|
}
|
|
53190
53190
|
function hasActorGate(wf, raw) {
|
|
53191
53191
|
const on = wf.on ?? raw["on"] ?? raw[true];
|
|
53192
|
-
const
|
|
53193
|
-
const labeled =
|
|
53192
|
+
const prTypes = (t) => t && Array.isArray(t.types) ? t.types.map(String) : [];
|
|
53193
|
+
const labeled = prTypes(on?.["pull_request_target"]).includes("labeled") || prTypes(on?.["pull_request"]).includes("labeled");
|
|
53194
53194
|
const ifs = [
|
|
53195
53195
|
wf.jobs ? Object.values(wf.jobs).map((j) => j.if) : [],
|
|
53196
53196
|
allSteps(wf).map((s) => s.step.if)
|
|
@@ -53259,6 +53259,9 @@ function analyzeWorkflow(path70, content) {
|
|
|
53259
53259
|
const triggers = triggerKeys(wf, raw);
|
|
53260
53260
|
const secretExposed = triggers.some((t) => /pull_request_target|workflow_run/i.test(t));
|
|
53261
53261
|
const forkInput = triggers.some((t) => /issue|pull_request|workflow_call/i.test(t));
|
|
53262
|
+
const privileged = triggers.some(
|
|
53263
|
+
(t) => /pull_request_target|pull_request_review|workflow_run|workflow_call|issue|discussion/i.test(t)
|
|
53264
|
+
);
|
|
53262
53265
|
const nonWrite = str(agentSteps.map((s) => s.with?.["allowed_non_write_users"]).find(Boolean));
|
|
53263
53266
|
const nonWriteStar = nonWrite === "*";
|
|
53264
53267
|
const nonWriteList = !!nonWrite && nonWrite !== "*";
|
|
@@ -53296,6 +53299,7 @@ function analyzeWorkflow(path70, content) {
|
|
|
53296
53299
|
const githubWriteTool = GH_WRITE_TOOL_RE.test(toolsBlob);
|
|
53297
53300
|
const canDamage = exfilOrRce || (hasGithubWritePerm(wf, agentJobs) || pat) && githubWriteTool;
|
|
53298
53301
|
if (!canDamage && severity && SEVERITY_RANK2[severity] > SEVERITY_RANK2.medium) severity = "medium";
|
|
53302
|
+
if (untrustedTrigger && !privileged && severity && severity !== "advisory") severity = "advisory";
|
|
53299
53303
|
if (!severity) severity = "advisory";
|
|
53300
53304
|
const signals = [];
|
|
53301
53305
|
if (secretExposed)
|
|
@@ -53303,6 +53307,10 @@ function analyzeWorkflow(path70, content) {
|
|
|
53303
53307
|
`runs with base-repo secrets (${triggers.filter((t) => /target|workflow_run/i.test(t)).join(", ")})`
|
|
53304
53308
|
);
|
|
53305
53309
|
else if (forkInput) signals.push(`triggered by untrusted input (${triggers.join(", ")})`);
|
|
53310
|
+
if (untrustedTrigger && !privileged)
|
|
53311
|
+
signals.push(
|
|
53312
|
+
"triggered by `pull_request` \u2014 fork PRs run with a read-only token (lower risk than pull_request_target)"
|
|
53313
|
+
);
|
|
53306
53314
|
if (head === "root") signals.push("checks out the untrusted PR head into the workspace root");
|
|
53307
53315
|
if (head === "subdir") signals.push("checks out the untrusted PR head into an isolated subdir");
|
|
53308
53316
|
if (promptUntrusted) signals.push("feeds untrusted PR/issue text to the agent");
|
|
@@ -53328,7 +53336,7 @@ function analyzeWorkflow(path70, content) {
|
|
|
53328
53336
|
file: path70,
|
|
53329
53337
|
signals,
|
|
53330
53338
|
mitigations: mitigations.length ? mitigations : void 0,
|
|
53331
|
-
fix: head === "root" ? "Do not check out the PR head into the workspace root under pull_request_target \u2014 check out the base ref, or isolate the head in a subdir (--add-dir). Add an actor gate and scope the agent tools." : "Add/verify an actor gate, scope the agent tools to read-only, and env-deny secrets. See Anthropic\u2019s claude-code-action security doc."
|
|
53339
|
+
fix: head === "root" && privileged ? "Do not check out the untrusted PR head into the workspace root under a privileged trigger (pull_request_target/workflow_run) \u2014 check out the base ref, or isolate the head in a subdir (--add-dir). Add an actor gate and scope the agent tools." : head === "root" ? "This runs under `pull_request` (fork PRs get a read-only token), so the head checkout is low-risk today \u2014 keep it on `pull_request` (not `pull_request_target`) and keep the actor gate + scoped tools." : "Add/verify an actor gate, scope the agent tools to read-only, and env-deny secrets. See Anthropic\u2019s claude-code-action security doc."
|
|
53332
53340
|
};
|
|
53333
53341
|
}
|
|
53334
53342
|
|
|
@@ -53502,12 +53510,14 @@ function renderScan(res) {
|
|
|
53502
53510
|
const head = res.worst === "critical" || res.worst === "high" ? chalk29.red.bold("\u26A0\uFE0F agent-security risk found") : res.worst ? chalk29.yellow("agent-security notes") : res.incomplete ? chalk29.yellow.bold("\u26A0\uFE0F INCOMPLETE \u2014 could not read all files") : chalk29.green("\u2705 agent-security: clean");
|
|
53503
53511
|
L.push(`\u{1F6E1}\uFE0F ${chalk29.bold("node9 scan-repo")} \xB7 ${res.source} \xB7 ${head}`);
|
|
53504
53512
|
L.push(chalk29.gray(` inspected ${res.inspected.length} config file(s), ${n} finding(s)`));
|
|
53505
|
-
if (res.incomplete)
|
|
53513
|
+
if (res.incomplete) {
|
|
53514
|
+
const rateLimited = res.notes.some((nt) => /rate limit/i.test(nt));
|
|
53506
53515
|
L.push(
|
|
53507
53516
|
chalk29.yellow.bold(
|
|
53508
|
-
" \u26A0\uFE0F
|
|
53517
|
+
rateLimited ? " \u26A0\uFE0F Rate-limited \u2014 some files were unread. NOT a clean bill of health; set GITHUB_TOKEN (or run `gh auth login`) and re-run." : " \u26A0\uFE0F A network error left some files unread. NOT a clean bill of health; re-run."
|
|
53509
53518
|
)
|
|
53510
53519
|
);
|
|
53520
|
+
}
|
|
53511
53521
|
L.push("");
|
|
53512
53522
|
for (const f of res.findings) {
|
|
53513
53523
|
L.push(
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@node9/proxy",
|
|
3
|
-
"version": "1.58.
|
|
3
|
+
"version": "1.58.5",
|
|
4
4
|
"description": "The Sudo Command for AI Agents. Execution Security for Claude Code, Codex, Gemini, Cursor, Opencode, Pi, and any MCP server.",
|
|
5
5
|
"main": "./dist/index.js",
|
|
6
6
|
"module": "./dist/index.mjs",
|