@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 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 = process.env.GITHUB_TOKEN || process.env.GH_TOKEN;
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 prt = on?.["pull_request_target"];
53182
- const labeled = prt && Array.isArray(prt.types) && prt.types.map(String).includes("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
- return { source: tree.source, findings, inspected, notes, worst: worstOf(findings) };
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 = process.env.GITHUB_TOKEN || process.env.GH_TOKEN;
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 prt = on?.["pull_request_target"];
53175
- const labeled = prt && Array.isArray(prt.types) && prt.types.map(String).includes("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
- return { source: tree.source, findings, inspected, notes, worst: worstOf(findings) };
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.2",
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",