@node9/proxy 1.12.6 → 1.12.8

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
@@ -1094,6 +1094,17 @@ var init_regex = __esm({
1094
1094
  function isAssignmentContext(text) {
1095
1095
  return ASSIGNMENT_CONTEXT_RE.test(text);
1096
1096
  }
1097
+ function shannonEntropy(s) {
1098
+ if (s.length === 0) return 0;
1099
+ const freq = /* @__PURE__ */ new Map();
1100
+ for (const ch of s) freq.set(ch, (freq.get(ch) ?? 0) + 1);
1101
+ let h = 0;
1102
+ for (const count of freq.values()) {
1103
+ const p = count / s.length;
1104
+ h -= p * Math.log2(p);
1105
+ }
1106
+ return h;
1107
+ }
1097
1108
  function scanFilePath(filePath, cwd = process.cwd()) {
1098
1109
  if (!filePath) return null;
1099
1110
  let resolved;
@@ -1162,8 +1173,9 @@ function scanArgs(args, depth = 0, fieldPath = "args") {
1162
1173
  continue;
1163
1174
  }
1164
1175
  if (pattern.regex.test(text)) {
1165
- const matchedValue = (text.match(pattern.regex)?.[0] ?? "").toLowerCase();
1166
- if (DLP_STOPWORDS.some((sw) => matchedValue.includes(sw))) continue;
1176
+ const raw = text.match(pattern.regex)?.[0] ?? "";
1177
+ if (DLP_STOPWORDS.some((sw) => raw.toLowerCase().includes(sw))) continue;
1178
+ if (pattern.minEntropy !== void 0 && shannonEntropy(raw) < pattern.minEntropy) continue;
1167
1179
  const severity = pattern.contextBoost && assignmentCtx ? "block" : pattern.severity;
1168
1180
  return {
1169
1181
  patternName: pattern.name,
@@ -1195,8 +1207,9 @@ function scanText(text) {
1195
1207
  continue;
1196
1208
  }
1197
1209
  if (pattern.regex.test(t)) {
1198
- const matchedValue = (t.match(pattern.regex)?.[0] ?? "").toLowerCase();
1199
- if (DLP_STOPWORDS.some((sw) => matchedValue.includes(sw))) continue;
1210
+ const raw = t.match(pattern.regex)?.[0] ?? "";
1211
+ if (DLP_STOPWORDS.some((sw) => raw.toLowerCase().includes(sw))) continue;
1212
+ if (pattern.minEntropy !== void 0 && shannonEntropy(raw) < pattern.minEntropy) continue;
1200
1213
  return {
1201
1214
  patternName: pattern.name,
1202
1215
  fieldPath: "response-text",
@@ -1236,7 +1249,9 @@ var init_dlp = __esm({
1236
1249
  "%{",
1237
1250
  "<your",
1238
1251
  "test_key",
1239
- "test_token"
1252
+ "test_token",
1253
+ "your",
1254
+ "here"
1240
1255
  ];
1241
1256
  DLP_PATTERNS = [
1242
1257
  // ── AWS ───────────────────────────────────────────────────────────────────
@@ -1287,7 +1302,8 @@ var init_dlp = __esm({
1287
1302
  name: "OpenAI API Key",
1288
1303
  regex: /\bsk-[a-zA-Z0-9_-]{20,}\b/,
1289
1304
  severity: "block",
1290
- keywords: ["sk-"]
1305
+ keywords: ["sk-"],
1306
+ minEntropy: 3.5
1291
1307
  },
1292
1308
  // ── Stripe ────────────────────────────────────────────────────────────────
1293
1309
  {
@@ -1358,7 +1374,13 @@ var init_dlp = __esm({
1358
1374
  keywords: ["hvb."]
1359
1375
  },
1360
1376
  // ── Hugging Face ──────────────────────────────────────────────────────────
1361
- { name: "HuggingFace Token", regex: /\bhf_[A-Za-z]{34}\b/, severity: "block", keywords: ["hf_"] },
1377
+ {
1378
+ name: "HuggingFace Token",
1379
+ regex: /\bhf_[A-Za-z]{34}\b/,
1380
+ severity: "block",
1381
+ keywords: ["hf_"],
1382
+ minEntropy: 3
1383
+ },
1362
1384
  // ── Postman ───────────────────────────────────────────────────────────────
1363
1385
  {
1364
1386
  name: "Postman API Token",
@@ -1510,7 +1532,8 @@ var init_dlp = __esm({
1510
1532
  name: "PyPI Upload Token",
1511
1533
  regex: /\bpypi-[A-Za-z0-9_-]{50,}\b/,
1512
1534
  severity: "block",
1513
- keywords: ["pypi-"]
1535
+ keywords: ["pypi-"],
1536
+ minEntropy: 3
1514
1537
  },
1515
1538
  // ── Bearer Token ─────────────────────────────────────────────────────────
1516
1539
  // contextBoost: promoted to block when assigned (e.g. AUTH_TOKEN=Bearer eyJ...)
@@ -1519,7 +1542,99 @@ var init_dlp = __esm({
1519
1542
  regex: /Bearer\s+[a-zA-Z0-9\-._~+/]{20,}=*/i,
1520
1543
  severity: "review",
1521
1544
  keywords: ["bearer"],
1522
- contextBoost: true
1545
+ contextBoost: true,
1546
+ minEntropy: 3
1547
+ },
1548
+ // ── Resend ────────────────────────────────────────────────────────────────
1549
+ {
1550
+ name: "Resend API Key",
1551
+ regex: /\bre_[a-zA-Z0-9]{24}\b/,
1552
+ severity: "block",
1553
+ keywords: ["re_"]
1554
+ },
1555
+ // ── Telegram ──────────────────────────────────────────────────────────────
1556
+ {
1557
+ name: "Telegram Bot Token",
1558
+ regex: /\b[0-9]{7,10}:AA[a-zA-Z0-9_-]{33}\b/,
1559
+ severity: "block",
1560
+ keywords: [":aa"]
1561
+ },
1562
+ // ── Mapbox ────────────────────────────────────────────────────────────────
1563
+ {
1564
+ name: "Mapbox Access Token",
1565
+ regex: /\bpk\.eyJ1[a-zA-Z0-9._-]{20,}\b/,
1566
+ severity: "block",
1567
+ keywords: ["pk.eyj1"]
1568
+ },
1569
+ // ── Notion ────────────────────────────────────────────────────────────────
1570
+ {
1571
+ name: "Notion Integration Token",
1572
+ regex: /\bsecret_[a-zA-Z0-9]{43}\b/,
1573
+ severity: "block",
1574
+ keywords: ["secret_"]
1575
+ },
1576
+ // ── Square ────────────────────────────────────────────────────────────────
1577
+ {
1578
+ name: "Square Access Token",
1579
+ regex: /\bsq0atp-[0-9A-Za-z_-]{22}\b/,
1580
+ severity: "block",
1581
+ keywords: ["sq0atp-"]
1582
+ },
1583
+ {
1584
+ name: "Square OAuth Secret",
1585
+ regex: /\bsq0csp-[0-9A-Za-z_-]{43}\b/,
1586
+ severity: "block",
1587
+ keywords: ["sq0csp-"]
1588
+ },
1589
+ // ── Typeform ──────────────────────────────────────────────────────────────
1590
+ {
1591
+ name: "Typeform Token",
1592
+ regex: /\btfp_[a-zA-Z0-9_]{59}\b/,
1593
+ severity: "block",
1594
+ keywords: ["tfp_"]
1595
+ },
1596
+ // ── Cloudinary ────────────────────────────────────────────────────────────
1597
+ {
1598
+ name: "Cloudinary URL",
1599
+ regex: /\bcloudinary:\/\/[0-9]+:[a-zA-Z0-9_-]+@[a-zA-Z0-9_-]+/,
1600
+ severity: "block",
1601
+ keywords: ["cloudinary://"]
1602
+ },
1603
+ // ── Airtable ──────────────────────────────────────────────────────────────
1604
+ // New PAT format: pat + 14 alphanum + . + 64 alphanum
1605
+ {
1606
+ name: "Airtable PAT",
1607
+ regex: /\bpat[a-zA-Z0-9]{14}\.[a-zA-Z0-9]{64}\b/,
1608
+ severity: "block",
1609
+ keywords: ["pat"]
1610
+ },
1611
+ // ── RubyGems ──────────────────────────────────────────────────────────────
1612
+ {
1613
+ name: "RubyGems API Key",
1614
+ regex: /\brubygems_[a-f0-9]{48}\b/,
1615
+ severity: "block",
1616
+ keywords: ["rubygems_"]
1617
+ },
1618
+ // ── Shippo ────────────────────────────────────────────────────────────────
1619
+ {
1620
+ name: "Shippo Token",
1621
+ regex: /\bshippo_(?:live|test)_[a-f0-9]{40}\b/,
1622
+ severity: "block",
1623
+ keywords: ["shippo_"]
1624
+ },
1625
+ // ── Plaid ─────────────────────────────────────────────────────────────────
1626
+ {
1627
+ name: "Plaid Access Token",
1628
+ regex: /\baccess-(?:sandbox|development|production)-[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}\b/,
1629
+ severity: "block",
1630
+ keywords: ["access-sandbox", "access-development", "access-production"]
1631
+ },
1632
+ // ── Age ───────────────────────────────────────────────────────────────────
1633
+ {
1634
+ name: "Age Identity Key",
1635
+ regex: /\bAGE-SECRET-KEY-1[QPZRY9X8GF2TVDW0S3JNLH]{58}\b/,
1636
+ severity: "block",
1637
+ keywords: ["age-secret-key-"]
1523
1638
  }
1524
1639
  ];
1525
1640
  SENSITIVE_PATH_PATTERNS = [
@@ -7670,6 +7785,10 @@ var init_ui = __esm({
7670
7785
  btn.appendChild(badge);
7671
7786
  }
7672
7787
  }
7788
+ // Auto-open when browser was launched directly from \`node9 scan\`
7789
+ if (new URLSearchParams(location.search).get('openscan') === '1') {
7790
+ openScanModal();
7791
+ }
7673
7792
  } catch {}
7674
7793
  })();
7675
7794
 
@@ -9508,14 +9627,9 @@ function registerScanCommand(program2) {
9508
9627
  );
9509
9628
  console.log(heroLine);
9510
9629
  console.log("");
9511
- if (blockedCount > 0) {
9630
+ if (scan.totalCostUSD > 0) {
9512
9631
  console.log(
9513
- " " + import_chalk2.default.red("\u{1F6D1} Would have blocked") + " " + import_chalk2.default.red.bold(String(blockedCount).padStart(5)) + import_chalk2.default.dim(" operations stopped before execution")
9514
- );
9515
- }
9516
- if (reviewCount > 0) {
9517
- console.log(
9518
- " " + import_chalk2.default.yellow("\u{1F441} Would have flagged") + " " + import_chalk2.default.yellow.bold(String(reviewCount).padStart(5)) + import_chalk2.default.dim(" sent to you for approval")
9632
+ " " + import_chalk2.default.bold(fmtCost(scan.totalCostUSD)) + import_chalk2.default.dim(" AI spend \xB7 ") + import_chalk2.default.dim(`${totalRisky} risky operations`)
9519
9633
  );
9520
9634
  }
9521
9635
  if (scan.dlpFindings.length > 0) {
@@ -9523,47 +9637,27 @@ function registerScanCommand(program2) {
9523
9637
  " " + import_chalk2.default.red("\u{1F511} Credential leak") + " " + import_chalk2.default.red.bold(String(scan.dlpFindings.length).padStart(5)) + import_chalk2.default.dim(" secret detected in tool call")
9524
9638
  );
9525
9639
  }
9526
- if (scan.loopFindings.length > 0) {
9640
+ if (blockedCount > 0) {
9527
9641
  console.log(
9528
- " " + import_chalk2.default.yellow("\u{1F501} Loop detected") + " " + import_chalk2.default.yellow.bold(String(scan.loopFindings.length).padStart(5)) + import_chalk2.default.dim(" repeated tool call patterns found")
9642
+ " " + import_chalk2.default.red("\u{1F6D1} Would have blocked") + " " + import_chalk2.default.red.bold(String(blockedCount).padStart(5)) + import_chalk2.default.dim(" operations stopped before execution")
9529
9643
  );
9530
9644
  }
9531
- console.log("");
9532
- for (const section of summary.sections) {
9533
- const countParts = [];
9534
- if (section.blockedCount > 0)
9535
- countParts.push(import_chalk2.default.red(`${section.blockedCount} blocked`));
9536
- if (section.reviewCount > 0)
9537
- countParts.push(import_chalk2.default.yellow(`${section.reviewCount} review`));
9538
- const countStr = countParts.join(import_chalk2.default.dim(" \xB7 "));
9539
- const enableHint = section.shieldKey ? import_chalk2.default.dim(` \u2192 node9 shield enable ${section.shieldKey}`) : "";
9540
- console.log(" " + import_chalk2.default.dim("\u2500".repeat(70)));
9645
+ if (scan.loopFindings.length > 0) {
9541
9646
  console.log(
9542
- " " + import_chalk2.default.bold(section.label) + (section.subtitle ? import_chalk2.default.dim(` \xB7 ${section.subtitle}`) : "") + " " + countStr + enableHint
9647
+ " " + import_chalk2.default.yellow("\u{1F501} Loop detected") + " " + import_chalk2.default.yellow.bold(String(scan.loopFindings.length).padStart(5)) + import_chalk2.default.dim(" repeated tool call patterns found")
9543
9648
  );
9544
- for (const rule of section.rules) {
9545
- printRuleGroup(rule, topN, drillDown, previewWidth);
9546
- }
9547
- console.log("");
9548
9649
  }
9549
- const activeShieldIds = new Set(
9550
- summary.sections.filter((s) => s.sourceType === "shield" && s.shieldKey).map((s) => s.shieldKey)
9551
- );
9552
- const emptyShields = Object.keys(SHIELDS).filter((n) => !activeShieldIds.has(n)).sort();
9553
- if (emptyShields.length > 0) {
9554
- console.log(" " + import_chalk2.default.dim("\u2500".repeat(70)));
9650
+ if (reviewCount > 0) {
9555
9651
  console.log(
9556
- " " + import_chalk2.default.bold("Shields") + import_chalk2.default.dim(" \xB7 no findings in your history") + " " + import_chalk2.default.green("\u2713")
9652
+ " " + import_chalk2.default.yellow("\u{1F441} Would have flagged") + " " + import_chalk2.default.yellow.bold(String(reviewCount).padStart(5)) + import_chalk2.default.dim(" sent to you for approval")
9557
9653
  );
9558
- console.log(" " + import_chalk2.default.dim(emptyShields.join(" \xB7 ")));
9559
- console.log(" " + import_chalk2.default.dim("\u2192 node9 shield enable <name> to activate any shield"));
9560
- console.log("");
9561
9654
  }
9655
+ console.log("");
9562
9656
  if (scan.dlpFindings.length > 0) {
9563
9657
  console.log(" " + import_chalk2.default.dim("\u2500".repeat(70)));
9564
9658
  console.log(
9565
9659
  " " + import_chalk2.default.red.bold("\u{1F511} Credential Leaks") + import_chalk2.default.dim(" \xB7 ") + import_chalk2.default.red(
9566
- `${num(scan.dlpFindings.length)} potential secret leak${scan.dlpFindings.length !== 1 ? "s" : ""}`
9660
+ `${num(scan.dlpFindings.length)} secret${scan.dlpFindings.length !== 1 ? "s" : ""} found in plain text`
9567
9661
  )
9568
9662
  );
9569
9663
  const shownDlp = drillDown ? scan.dlpFindings : scan.dlpFindings.slice(0, topN);
@@ -9573,7 +9667,7 @@ function registerScanCommand(program2) {
9573
9667
  const agentBadge = f.agent === "gemini" ? import_chalk2.default.blue("[Gemini] ") : f.agent === "codex" ? import_chalk2.default.magenta("[Codex] ") : import_chalk2.default.cyan("[Claude] ");
9574
9668
  const sessionSuffix = f.sessionId ? import_chalk2.default.dim(` \u2192 ${f.sessionId.slice(0, 8)}`) : "";
9575
9669
  console.log(
9576
- ` ${ts}${proj}${agentBadge}` + import_chalk2.default.yellow(f.patternName) + import_chalk2.default.dim(" ") + import_chalk2.default.gray(f.redactedSample) + sessionSuffix
9670
+ ` \u{1F6A8} ${ts}${proj}${agentBadge}` + import_chalk2.default.yellow(f.patternName) + import_chalk2.default.dim(" ") + import_chalk2.default.gray(f.redactedSample) + sessionSuffix
9577
9671
  );
9578
9672
  }
9579
9673
  if (!drillDown && scan.dlpFindings.length > topN) {
@@ -9585,6 +9679,21 @@ function registerScanCommand(program2) {
9585
9679
  }
9586
9680
  console.log("");
9587
9681
  }
9682
+ const blockedRuleSections = summary.sections.map((s) => ({ ...s, rules: s.rules.filter((r) => r.verdict === "block") })).filter((s) => s.rules.length > 0);
9683
+ if (blockedRuleSections.length > 0) {
9684
+ console.log(" " + import_chalk2.default.dim("\u2500".repeat(70)));
9685
+ console.log(
9686
+ " " + import_chalk2.default.red.bold("\u{1F6D1} Blocked") + import_chalk2.default.dim(" \xB7 ") + import_chalk2.default.red(
9687
+ `${blockedCount} operation${blockedCount !== 1 ? "s" : ""} node9 would have stopped`
9688
+ )
9689
+ );
9690
+ for (const section of blockedRuleSections) {
9691
+ for (const rule of section.rules) {
9692
+ printRuleGroup(rule, topN, drillDown, previewWidth);
9693
+ }
9694
+ }
9695
+ console.log("");
9696
+ }
9588
9697
  if (scan.loopFindings.length > 0) {
9589
9698
  console.log(" " + import_chalk2.default.dim("\u2500".repeat(70)));
9590
9699
  console.log(
@@ -9611,12 +9720,32 @@ function registerScanCommand(program2) {
9611
9720
  }
9612
9721
  console.log("");
9613
9722
  }
9614
- }
9615
- if (scan.totalCostUSD > 0) {
9616
- console.log(
9617
- " " + import_chalk2.default.bold("Agent spend:") + " " + import_chalk2.default.yellow(fmtCost(scan.totalCostUSD)) + import_chalk2.default.dim(" (for per-period breakdown: node9 report)")
9723
+ for (const section of summary.sections) {
9724
+ const reviewRules = section.rules.filter((r) => r.verdict !== "block");
9725
+ if (reviewRules.length === 0) continue;
9726
+ const enableHint = section.shieldKey ? import_chalk2.default.dim(` \u2192 node9 shield enable ${section.shieldKey}`) : "";
9727
+ console.log(" " + import_chalk2.default.dim("\u2500".repeat(70)));
9728
+ console.log(
9729
+ " " + import_chalk2.default.bold(section.label) + (section.subtitle ? import_chalk2.default.dim(` \xB7 ${section.subtitle}`) : "") + " " + import_chalk2.default.yellow(`${section.reviewCount} review`) + enableHint
9730
+ );
9731
+ for (const rule of reviewRules) {
9732
+ printRuleGroup(rule, topN, drillDown, previewWidth);
9733
+ }
9734
+ console.log("");
9735
+ }
9736
+ const activeShieldIds = new Set(
9737
+ summary.sections.filter((s) => s.sourceType === "shield" && s.shieldKey).map((s) => s.shieldKey)
9618
9738
  );
9619
- console.log("");
9739
+ const emptyShields = Object.keys(SHIELDS).filter((n) => !activeShieldIds.has(n)).sort();
9740
+ if (emptyShields.length > 0) {
9741
+ console.log(" " + import_chalk2.default.dim("\u2500".repeat(70)));
9742
+ console.log(
9743
+ " " + import_chalk2.default.bold("\u{1F6E1} Inactive Shields") + import_chalk2.default.dim(" \xB7 enable for more coverage")
9744
+ );
9745
+ console.log(" " + import_chalk2.default.dim(emptyShields.join(" \xB7 ")));
9746
+ console.log(" " + import_chalk2.default.dim("\u2192 node9 shield enable <name> to activate"));
9747
+ console.log("");
9748
+ }
9620
9749
  }
9621
9750
  if (isInstalled) {
9622
9751
  console.log(import_chalk2.default.green(" \u2705 node9 is active \u2014 your future sessions are protected."));
@@ -9657,7 +9786,7 @@ function registerScanCommand(program2) {
9657
9786
  console.log("");
9658
9787
  if (!isTestingMode()) {
9659
9788
  if (isInstalled) {
9660
- const url = `http://${DAEMON_HOST}:${DAEMON_PORT}/`;
9789
+ const url = `http://${DAEMON_HOST}:${DAEMON_PORT}/?openscan=1`;
9661
9790
  if (isDaemonRunning()) {
9662
9791
  const internalToken = getInternalToken();
9663
9792
  if (internalToken) {