@node9/proxy 1.58.2 → 1.58.4
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 +42 -8
- package/dist/cli.mjs +42 -8
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -52950,7 +52950,25 @@ var import_chalk30 = __toESM(require("chalk"));
|
|
|
52950
52950
|
// src/ci-check/fetch.ts
|
|
52951
52951
|
var import_fs59 = __toESM(require("fs"));
|
|
52952
52952
|
var import_path56 = __toESM(require("path"));
|
|
52953
|
+
var import_node_child_process = require("child_process");
|
|
52953
52954
|
var import_undici = __toESM(require_undici());
|
|
52955
|
+
var cachedGhToken;
|
|
52956
|
+
function resolveGitHubToken() {
|
|
52957
|
+
const env = process.env.GITHUB_TOKEN || process.env.GH_TOKEN;
|
|
52958
|
+
if (env) return env;
|
|
52959
|
+
if (cachedGhToken === void 0) {
|
|
52960
|
+
try {
|
|
52961
|
+
cachedGhToken = (0, import_node_child_process.execFileSync)("gh", ["auth", "token"], {
|
|
52962
|
+
encoding: "utf8",
|
|
52963
|
+
stdio: ["ignore", "pipe", "ignore"],
|
|
52964
|
+
timeout: 3e3
|
|
52965
|
+
}).trim() || null;
|
|
52966
|
+
} catch {
|
|
52967
|
+
cachedGhToken = null;
|
|
52968
|
+
}
|
|
52969
|
+
}
|
|
52970
|
+
return cachedGhToken ?? void 0;
|
|
52971
|
+
}
|
|
52954
52972
|
var SURFACE_FILES = [
|
|
52955
52973
|
".claude/settings.json",
|
|
52956
52974
|
".claude/settings.local.json",
|
|
@@ -52996,7 +53014,7 @@ function ghHeaders() {
|
|
|
52996
53014
|
"User-Agent": "node9-scan-repo",
|
|
52997
53015
|
"X-GitHub-Api-Version": "2022-11-28"
|
|
52998
53016
|
};
|
|
52999
|
-
const tok =
|
|
53017
|
+
const tok = resolveGitHubToken();
|
|
53000
53018
|
if (tok) h.Authorization = `Bearer ${tok}`;
|
|
53001
53019
|
return h;
|
|
53002
53020
|
}
|
|
@@ -53019,7 +53037,7 @@ async function ghGet(url) {
|
|
|
53019
53037
|
}
|
|
53020
53038
|
return { status: res.statusCode, json };
|
|
53021
53039
|
}
|
|
53022
|
-
var RATE_LIMIT_NOTE = "GitHub rate limit hit \u2014 results may be INCOMPLETE (a missing file could be unread, not absent). Set GITHUB_TOKEN
|
|
53040
|
+
var RATE_LIMIT_NOTE = "GitHub rate limit hit \u2014 results may be INCOMPLETE (a missing file could be unread, not absent). Set GITHUB_TOKEN or run `gh auth login`.";
|
|
53023
53041
|
var NETWORK_NOTE = "A network error/timeout occurred \u2014 results may be INCOMPLETE (some files were not fetched).";
|
|
53024
53042
|
async function fetchOne(owner, repo, filePath, notes) {
|
|
53025
53043
|
const url = `https://api.github.com/repos/${owner}/${repo}/contents/${filePath}`;
|
|
@@ -53178,8 +53196,8 @@ function promptTakesUntrusted(steps) {
|
|
|
53178
53196
|
}
|
|
53179
53197
|
function hasActorGate(wf, raw) {
|
|
53180
53198
|
const on = wf.on ?? raw["on"] ?? raw[true];
|
|
53181
|
-
const
|
|
53182
|
-
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");
|
|
53183
53201
|
const ifs = [
|
|
53184
53202
|
wf.jobs ? Object.values(wf.jobs).map((j) => j.if) : [],
|
|
53185
53203
|
allSteps(wf).map((s) => s.step.if)
|
|
@@ -53248,6 +53266,9 @@ function analyzeWorkflow(path70, content) {
|
|
|
53248
53266
|
const triggers = triggerKeys(wf, raw);
|
|
53249
53267
|
const secretExposed = triggers.some((t) => /pull_request_target|workflow_run/i.test(t));
|
|
53250
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
|
+
);
|
|
53251
53272
|
const nonWrite = str(agentSteps.map((s) => s.with?.["allowed_non_write_users"]).find(Boolean));
|
|
53252
53273
|
const nonWriteStar = nonWrite === "*";
|
|
53253
53274
|
const nonWriteList = !!nonWrite && nonWrite !== "*";
|
|
@@ -53285,6 +53306,7 @@ function analyzeWorkflow(path70, content) {
|
|
|
53285
53306
|
const githubWriteTool = GH_WRITE_TOOL_RE.test(toolsBlob);
|
|
53286
53307
|
const canDamage = exfilOrRce || (hasGithubWritePerm(wf, agentJobs) || pat) && githubWriteTool;
|
|
53287
53308
|
if (!canDamage && severity && SEVERITY_RANK2[severity] > SEVERITY_RANK2.medium) severity = "medium";
|
|
53309
|
+
if (untrustedTrigger && !privileged && severity && severity !== "advisory") severity = "advisory";
|
|
53288
53310
|
if (!severity) severity = "advisory";
|
|
53289
53311
|
const signals = [];
|
|
53290
53312
|
if (secretExposed)
|
|
@@ -53292,6 +53314,10 @@ function analyzeWorkflow(path70, content) {
|
|
|
53292
53314
|
`runs with base-repo secrets (${triggers.filter((t) => /target|workflow_run/i.test(t)).join(", ")})`
|
|
53293
53315
|
);
|
|
53294
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
|
+
);
|
|
53295
53321
|
if (head === "root") signals.push("checks out the untrusted PR head into the workspace root");
|
|
53296
53322
|
if (head === "subdir") signals.push("checks out the untrusted PR head into an isolated subdir");
|
|
53297
53323
|
if (promptUntrusted) signals.push("feeds untrusted PR/issue text to the agent");
|
|
@@ -53460,7 +53486,8 @@ function scanTree(tree) {
|
|
|
53460
53486
|
findings.sort(
|
|
53461
53487
|
(a, b) => SEVERITY_RANK2[b.severity] - SEVERITY_RANK2[a.severity] || a.file.localeCompare(b.file)
|
|
53462
53488
|
);
|
|
53463
|
-
|
|
53489
|
+
const incomplete = notes.some((nt) => /may be INCOMPLETE/i.test(nt));
|
|
53490
|
+
return { source: tree.source, findings, inspected, notes, worst: worstOf(findings), incomplete };
|
|
53464
53491
|
}
|
|
53465
53492
|
async function scanRepo(input, onProgress) {
|
|
53466
53493
|
const tree = await fetchTree(input, onProgress);
|
|
@@ -53487,9 +53514,15 @@ function ownedHint(source) {
|
|
|
53487
53514
|
function renderScan(res) {
|
|
53488
53515
|
const L = [];
|
|
53489
53516
|
const n = res.findings.length;
|
|
53490
|
-
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") : import_chalk29.default.green("\u2705 agent-security: clean");
|
|
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");
|
|
53491
53518
|
L.push(`\u{1F6E1}\uFE0F ${import_chalk29.default.bold("node9 scan-repo")} \xB7 ${res.source} \xB7 ${head}`);
|
|
53492
53519
|
L.push(import_chalk29.default.gray(` inspected ${res.inspected.length} config file(s), ${n} finding(s)`));
|
|
53520
|
+
if (res.incomplete)
|
|
53521
|
+
L.push(
|
|
53522
|
+
import_chalk29.default.yellow.bold(
|
|
53523
|
+
" \u26A0\uFE0F This scan was rate-limited and is NOT a clean bill of health \u2014 set GITHUB_TOKEN and re-run."
|
|
53524
|
+
)
|
|
53525
|
+
);
|
|
53493
53526
|
L.push("");
|
|
53494
53527
|
for (const f of res.findings) {
|
|
53495
53528
|
L.push(
|
|
@@ -53501,7 +53534,7 @@ function renderScan(res) {
|
|
|
53501
53534
|
L.push(import_chalk29.default.cyan(` \u2192 ${f.fix}`));
|
|
53502
53535
|
L.push("");
|
|
53503
53536
|
}
|
|
53504
|
-
if (n === 0) {
|
|
53537
|
+
if (n === 0 && !res.incomplete) {
|
|
53505
53538
|
L.push(
|
|
53506
53539
|
import_chalk29.default.gray(" No committed agent hooks, injectable workflows, or unpinned MCP servers.")
|
|
53507
53540
|
);
|
|
@@ -53521,7 +53554,7 @@ function renderScan(res) {
|
|
|
53521
53554
|
}
|
|
53522
53555
|
function renderScanMarkdown(res) {
|
|
53523
53556
|
const L = [];
|
|
53524
|
-
const status = res.worst === "critical" || res.worst === "high" ? "\u26A0\uFE0F" : res.worst ? "\u{1F7E1}" : "\u2705";
|
|
53557
|
+
const status = res.worst === "critical" || res.worst === "high" ? "\u26A0\uFE0F" : res.worst ? "\u{1F7E1}" : res.incomplete ? "\u26A0\uFE0F" : "\u2705";
|
|
53525
53558
|
L.push(`### \u{1F6E1}\uFE0F node9 agent-security \xB7 \`${res.source}\` \xB7 ${status}`);
|
|
53526
53559
|
L.push("");
|
|
53527
53560
|
L.push(
|
|
@@ -53544,6 +53577,7 @@ function renderScanMarkdown(res) {
|
|
|
53544
53577
|
function exitCodeFor(res) {
|
|
53545
53578
|
if (res.worst === "critical" || res.worst === "high") return 2;
|
|
53546
53579
|
if (res.worst === "medium") return 1;
|
|
53580
|
+
if (res.incomplete) return 3;
|
|
53547
53581
|
return 0;
|
|
53548
53582
|
}
|
|
53549
53583
|
|
package/dist/cli.mjs
CHANGED
|
@@ -52944,6 +52944,24 @@ import chalk30 from "chalk";
|
|
|
52944
52944
|
var import_undici = __toESM(require_undici());
|
|
52945
52945
|
import fs60 from "fs";
|
|
52946
52946
|
import path57 from "path";
|
|
52947
|
+
import { execFileSync as execFileSync2 } from "child_process";
|
|
52948
|
+
var cachedGhToken;
|
|
52949
|
+
function resolveGitHubToken() {
|
|
52950
|
+
const env = process.env.GITHUB_TOKEN || process.env.GH_TOKEN;
|
|
52951
|
+
if (env) return env;
|
|
52952
|
+
if (cachedGhToken === void 0) {
|
|
52953
|
+
try {
|
|
52954
|
+
cachedGhToken = execFileSync2("gh", ["auth", "token"], {
|
|
52955
|
+
encoding: "utf8",
|
|
52956
|
+
stdio: ["ignore", "pipe", "ignore"],
|
|
52957
|
+
timeout: 3e3
|
|
52958
|
+
}).trim() || null;
|
|
52959
|
+
} catch {
|
|
52960
|
+
cachedGhToken = null;
|
|
52961
|
+
}
|
|
52962
|
+
}
|
|
52963
|
+
return cachedGhToken ?? void 0;
|
|
52964
|
+
}
|
|
52947
52965
|
var SURFACE_FILES = [
|
|
52948
52966
|
".claude/settings.json",
|
|
52949
52967
|
".claude/settings.local.json",
|
|
@@ -52989,7 +53007,7 @@ function ghHeaders() {
|
|
|
52989
53007
|
"User-Agent": "node9-scan-repo",
|
|
52990
53008
|
"X-GitHub-Api-Version": "2022-11-28"
|
|
52991
53009
|
};
|
|
52992
|
-
const tok =
|
|
53010
|
+
const tok = resolveGitHubToken();
|
|
52993
53011
|
if (tok) h.Authorization = `Bearer ${tok}`;
|
|
52994
53012
|
return h;
|
|
52995
53013
|
}
|
|
@@ -53012,7 +53030,7 @@ async function ghGet(url) {
|
|
|
53012
53030
|
}
|
|
53013
53031
|
return { status: res.statusCode, json };
|
|
53014
53032
|
}
|
|
53015
|
-
var RATE_LIMIT_NOTE = "GitHub rate limit hit \u2014 results may be INCOMPLETE (a missing file could be unread, not absent). Set GITHUB_TOKEN
|
|
53033
|
+
var RATE_LIMIT_NOTE = "GitHub rate limit hit \u2014 results may be INCOMPLETE (a missing file could be unread, not absent). Set GITHUB_TOKEN or run `gh auth login`.";
|
|
53016
53034
|
var NETWORK_NOTE = "A network error/timeout occurred \u2014 results may be INCOMPLETE (some files were not fetched).";
|
|
53017
53035
|
async function fetchOne(owner, repo, filePath, notes) {
|
|
53018
53036
|
const url = `https://api.github.com/repos/${owner}/${repo}/contents/${filePath}`;
|
|
@@ -53171,8 +53189,8 @@ function promptTakesUntrusted(steps) {
|
|
|
53171
53189
|
}
|
|
53172
53190
|
function hasActorGate(wf, raw) {
|
|
53173
53191
|
const on = wf.on ?? raw["on"] ?? raw[true];
|
|
53174
|
-
const
|
|
53175
|
-
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");
|
|
53176
53194
|
const ifs = [
|
|
53177
53195
|
wf.jobs ? Object.values(wf.jobs).map((j) => j.if) : [],
|
|
53178
53196
|
allSteps(wf).map((s) => s.step.if)
|
|
@@ -53241,6 +53259,9 @@ function analyzeWorkflow(path70, content) {
|
|
|
53241
53259
|
const triggers = triggerKeys(wf, raw);
|
|
53242
53260
|
const secretExposed = triggers.some((t) => /pull_request_target|workflow_run/i.test(t));
|
|
53243
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
|
+
);
|
|
53244
53265
|
const nonWrite = str(agentSteps.map((s) => s.with?.["allowed_non_write_users"]).find(Boolean));
|
|
53245
53266
|
const nonWriteStar = nonWrite === "*";
|
|
53246
53267
|
const nonWriteList = !!nonWrite && nonWrite !== "*";
|
|
@@ -53278,6 +53299,7 @@ function analyzeWorkflow(path70, content) {
|
|
|
53278
53299
|
const githubWriteTool = GH_WRITE_TOOL_RE.test(toolsBlob);
|
|
53279
53300
|
const canDamage = exfilOrRce || (hasGithubWritePerm(wf, agentJobs) || pat) && githubWriteTool;
|
|
53280
53301
|
if (!canDamage && severity && SEVERITY_RANK2[severity] > SEVERITY_RANK2.medium) severity = "medium";
|
|
53302
|
+
if (untrustedTrigger && !privileged && severity && severity !== "advisory") severity = "advisory";
|
|
53281
53303
|
if (!severity) severity = "advisory";
|
|
53282
53304
|
const signals = [];
|
|
53283
53305
|
if (secretExposed)
|
|
@@ -53285,6 +53307,10 @@ function analyzeWorkflow(path70, content) {
|
|
|
53285
53307
|
`runs with base-repo secrets (${triggers.filter((t) => /target|workflow_run/i.test(t)).join(", ")})`
|
|
53286
53308
|
);
|
|
53287
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
|
+
);
|
|
53288
53314
|
if (head === "root") signals.push("checks out the untrusted PR head into the workspace root");
|
|
53289
53315
|
if (head === "subdir") signals.push("checks out the untrusted PR head into an isolated subdir");
|
|
53290
53316
|
if (promptUntrusted) signals.push("feeds untrusted PR/issue text to the agent");
|
|
@@ -53453,7 +53479,8 @@ function scanTree(tree) {
|
|
|
53453
53479
|
findings.sort(
|
|
53454
53480
|
(a, b) => SEVERITY_RANK2[b.severity] - SEVERITY_RANK2[a.severity] || a.file.localeCompare(b.file)
|
|
53455
53481
|
);
|
|
53456
|
-
|
|
53482
|
+
const incomplete = notes.some((nt) => /may be INCOMPLETE/i.test(nt));
|
|
53483
|
+
return { source: tree.source, findings, inspected, notes, worst: worstOf(findings), incomplete };
|
|
53457
53484
|
}
|
|
53458
53485
|
async function scanRepo(input, onProgress) {
|
|
53459
53486
|
const tree = await fetchTree(input, onProgress);
|
|
@@ -53480,9 +53507,15 @@ function ownedHint(source) {
|
|
|
53480
53507
|
function renderScan(res) {
|
|
53481
53508
|
const L = [];
|
|
53482
53509
|
const n = res.findings.length;
|
|
53483
|
-
const head = res.worst === "critical" || res.worst === "high" ? chalk29.red.bold("\u26A0\uFE0F agent-security risk found") : res.worst ? chalk29.yellow("agent-security notes") : chalk29.green("\u2705 agent-security: clean");
|
|
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");
|
|
53484
53511
|
L.push(`\u{1F6E1}\uFE0F ${chalk29.bold("node9 scan-repo")} \xB7 ${res.source} \xB7 ${head}`);
|
|
53485
53512
|
L.push(chalk29.gray(` inspected ${res.inspected.length} config file(s), ${n} finding(s)`));
|
|
53513
|
+
if (res.incomplete)
|
|
53514
|
+
L.push(
|
|
53515
|
+
chalk29.yellow.bold(
|
|
53516
|
+
" \u26A0\uFE0F This scan was rate-limited and is NOT a clean bill of health \u2014 set GITHUB_TOKEN and re-run."
|
|
53517
|
+
)
|
|
53518
|
+
);
|
|
53486
53519
|
L.push("");
|
|
53487
53520
|
for (const f of res.findings) {
|
|
53488
53521
|
L.push(
|
|
@@ -53494,7 +53527,7 @@ function renderScan(res) {
|
|
|
53494
53527
|
L.push(chalk29.cyan(` \u2192 ${f.fix}`));
|
|
53495
53528
|
L.push("");
|
|
53496
53529
|
}
|
|
53497
|
-
if (n === 0) {
|
|
53530
|
+
if (n === 0 && !res.incomplete) {
|
|
53498
53531
|
L.push(
|
|
53499
53532
|
chalk29.gray(" No committed agent hooks, injectable workflows, or unpinned MCP servers.")
|
|
53500
53533
|
);
|
|
@@ -53514,7 +53547,7 @@ function renderScan(res) {
|
|
|
53514
53547
|
}
|
|
53515
53548
|
function renderScanMarkdown(res) {
|
|
53516
53549
|
const L = [];
|
|
53517
|
-
const status = res.worst === "critical" || res.worst === "high" ? "\u26A0\uFE0F" : res.worst ? "\u{1F7E1}" : "\u2705";
|
|
53550
|
+
const status = res.worst === "critical" || res.worst === "high" ? "\u26A0\uFE0F" : res.worst ? "\u{1F7E1}" : res.incomplete ? "\u26A0\uFE0F" : "\u2705";
|
|
53518
53551
|
L.push(`### \u{1F6E1}\uFE0F node9 agent-security \xB7 \`${res.source}\` \xB7 ${status}`);
|
|
53519
53552
|
L.push("");
|
|
53520
53553
|
L.push(
|
|
@@ -53537,6 +53570,7 @@ function renderScanMarkdown(res) {
|
|
|
53537
53570
|
function exitCodeFor(res) {
|
|
53538
53571
|
if (res.worst === "critical" || res.worst === "high") return 2;
|
|
53539
53572
|
if (res.worst === "medium") return 1;
|
|
53573
|
+
if (res.incomplete) return 3;
|
|
53540
53574
|
return 0;
|
|
53541
53575
|
}
|
|
53542
53576
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@node9/proxy",
|
|
3
|
-
"version": "1.58.
|
|
3
|
+
"version": "1.58.4",
|
|
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",
|