@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 +180 -51
- package/dist/cli.mjs +180 -51
- package/dist/index.js +121 -7
- package/dist/index.mjs +121 -7
- package/node_modules/mvdan-sh/LICENSE +27 -0
- package/node_modules/mvdan-sh/README.md +84 -0
- package/node_modules/mvdan-sh/index.js +56 -0
- package/node_modules/mvdan-sh/package.json +24 -0
- package/package.json +4 -1
package/dist/cli.mjs
CHANGED
|
@@ -1074,6 +1074,17 @@ import path4 from "path";
|
|
|
1074
1074
|
function isAssignmentContext(text) {
|
|
1075
1075
|
return ASSIGNMENT_CONTEXT_RE.test(text);
|
|
1076
1076
|
}
|
|
1077
|
+
function shannonEntropy(s) {
|
|
1078
|
+
if (s.length === 0) return 0;
|
|
1079
|
+
const freq = /* @__PURE__ */ new Map();
|
|
1080
|
+
for (const ch of s) freq.set(ch, (freq.get(ch) ?? 0) + 1);
|
|
1081
|
+
let h = 0;
|
|
1082
|
+
for (const count of freq.values()) {
|
|
1083
|
+
const p = count / s.length;
|
|
1084
|
+
h -= p * Math.log2(p);
|
|
1085
|
+
}
|
|
1086
|
+
return h;
|
|
1087
|
+
}
|
|
1077
1088
|
function scanFilePath(filePath, cwd = process.cwd()) {
|
|
1078
1089
|
if (!filePath) return null;
|
|
1079
1090
|
let resolved;
|
|
@@ -1142,8 +1153,9 @@ function scanArgs(args, depth = 0, fieldPath = "args") {
|
|
|
1142
1153
|
continue;
|
|
1143
1154
|
}
|
|
1144
1155
|
if (pattern.regex.test(text)) {
|
|
1145
|
-
const
|
|
1146
|
-
if (DLP_STOPWORDS.some((sw) =>
|
|
1156
|
+
const raw = text.match(pattern.regex)?.[0] ?? "";
|
|
1157
|
+
if (DLP_STOPWORDS.some((sw) => raw.toLowerCase().includes(sw))) continue;
|
|
1158
|
+
if (pattern.minEntropy !== void 0 && shannonEntropy(raw) < pattern.minEntropy) continue;
|
|
1147
1159
|
const severity = pattern.contextBoost && assignmentCtx ? "block" : pattern.severity;
|
|
1148
1160
|
return {
|
|
1149
1161
|
patternName: pattern.name,
|
|
@@ -1175,8 +1187,9 @@ function scanText(text) {
|
|
|
1175
1187
|
continue;
|
|
1176
1188
|
}
|
|
1177
1189
|
if (pattern.regex.test(t)) {
|
|
1178
|
-
const
|
|
1179
|
-
if (DLP_STOPWORDS.some((sw) =>
|
|
1190
|
+
const raw = t.match(pattern.regex)?.[0] ?? "";
|
|
1191
|
+
if (DLP_STOPWORDS.some((sw) => raw.toLowerCase().includes(sw))) continue;
|
|
1192
|
+
if (pattern.minEntropy !== void 0 && shannonEntropy(raw) < pattern.minEntropy) continue;
|
|
1180
1193
|
return {
|
|
1181
1194
|
patternName: pattern.name,
|
|
1182
1195
|
fieldPath: "response-text",
|
|
@@ -1214,7 +1227,9 @@ var init_dlp = __esm({
|
|
|
1214
1227
|
"%{",
|
|
1215
1228
|
"<your",
|
|
1216
1229
|
"test_key",
|
|
1217
|
-
"test_token"
|
|
1230
|
+
"test_token",
|
|
1231
|
+
"your",
|
|
1232
|
+
"here"
|
|
1218
1233
|
];
|
|
1219
1234
|
DLP_PATTERNS = [
|
|
1220
1235
|
// ── AWS ───────────────────────────────────────────────────────────────────
|
|
@@ -1265,7 +1280,8 @@ var init_dlp = __esm({
|
|
|
1265
1280
|
name: "OpenAI API Key",
|
|
1266
1281
|
regex: /\bsk-[a-zA-Z0-9_-]{20,}\b/,
|
|
1267
1282
|
severity: "block",
|
|
1268
|
-
keywords: ["sk-"]
|
|
1283
|
+
keywords: ["sk-"],
|
|
1284
|
+
minEntropy: 3.5
|
|
1269
1285
|
},
|
|
1270
1286
|
// ── Stripe ────────────────────────────────────────────────────────────────
|
|
1271
1287
|
{
|
|
@@ -1336,7 +1352,13 @@ var init_dlp = __esm({
|
|
|
1336
1352
|
keywords: ["hvb."]
|
|
1337
1353
|
},
|
|
1338
1354
|
// ── Hugging Face ──────────────────────────────────────────────────────────
|
|
1339
|
-
{
|
|
1355
|
+
{
|
|
1356
|
+
name: "HuggingFace Token",
|
|
1357
|
+
regex: /\bhf_[A-Za-z]{34}\b/,
|
|
1358
|
+
severity: "block",
|
|
1359
|
+
keywords: ["hf_"],
|
|
1360
|
+
minEntropy: 3
|
|
1361
|
+
},
|
|
1340
1362
|
// ── Postman ───────────────────────────────────────────────────────────────
|
|
1341
1363
|
{
|
|
1342
1364
|
name: "Postman API Token",
|
|
@@ -1488,7 +1510,8 @@ var init_dlp = __esm({
|
|
|
1488
1510
|
name: "PyPI Upload Token",
|
|
1489
1511
|
regex: /\bpypi-[A-Za-z0-9_-]{50,}\b/,
|
|
1490
1512
|
severity: "block",
|
|
1491
|
-
keywords: ["pypi-"]
|
|
1513
|
+
keywords: ["pypi-"],
|
|
1514
|
+
minEntropy: 3
|
|
1492
1515
|
},
|
|
1493
1516
|
// ── Bearer Token ─────────────────────────────────────────────────────────
|
|
1494
1517
|
// contextBoost: promoted to block when assigned (e.g. AUTH_TOKEN=Bearer eyJ...)
|
|
@@ -1497,7 +1520,99 @@ var init_dlp = __esm({
|
|
|
1497
1520
|
regex: /Bearer\s+[a-zA-Z0-9\-._~+/]{20,}=*/i,
|
|
1498
1521
|
severity: "review",
|
|
1499
1522
|
keywords: ["bearer"],
|
|
1500
|
-
contextBoost: true
|
|
1523
|
+
contextBoost: true,
|
|
1524
|
+
minEntropy: 3
|
|
1525
|
+
},
|
|
1526
|
+
// ── Resend ────────────────────────────────────────────────────────────────
|
|
1527
|
+
{
|
|
1528
|
+
name: "Resend API Key",
|
|
1529
|
+
regex: /\bre_[a-zA-Z0-9]{24}\b/,
|
|
1530
|
+
severity: "block",
|
|
1531
|
+
keywords: ["re_"]
|
|
1532
|
+
},
|
|
1533
|
+
// ── Telegram ──────────────────────────────────────────────────────────────
|
|
1534
|
+
{
|
|
1535
|
+
name: "Telegram Bot Token",
|
|
1536
|
+
regex: /\b[0-9]{7,10}:AA[a-zA-Z0-9_-]{33}\b/,
|
|
1537
|
+
severity: "block",
|
|
1538
|
+
keywords: [":aa"]
|
|
1539
|
+
},
|
|
1540
|
+
// ── Mapbox ────────────────────────────────────────────────────────────────
|
|
1541
|
+
{
|
|
1542
|
+
name: "Mapbox Access Token",
|
|
1543
|
+
regex: /\bpk\.eyJ1[a-zA-Z0-9._-]{20,}\b/,
|
|
1544
|
+
severity: "block",
|
|
1545
|
+
keywords: ["pk.eyj1"]
|
|
1546
|
+
},
|
|
1547
|
+
// ── Notion ────────────────────────────────────────────────────────────────
|
|
1548
|
+
{
|
|
1549
|
+
name: "Notion Integration Token",
|
|
1550
|
+
regex: /\bsecret_[a-zA-Z0-9]{43}\b/,
|
|
1551
|
+
severity: "block",
|
|
1552
|
+
keywords: ["secret_"]
|
|
1553
|
+
},
|
|
1554
|
+
// ── Square ────────────────────────────────────────────────────────────────
|
|
1555
|
+
{
|
|
1556
|
+
name: "Square Access Token",
|
|
1557
|
+
regex: /\bsq0atp-[0-9A-Za-z_-]{22}\b/,
|
|
1558
|
+
severity: "block",
|
|
1559
|
+
keywords: ["sq0atp-"]
|
|
1560
|
+
},
|
|
1561
|
+
{
|
|
1562
|
+
name: "Square OAuth Secret",
|
|
1563
|
+
regex: /\bsq0csp-[0-9A-Za-z_-]{43}\b/,
|
|
1564
|
+
severity: "block",
|
|
1565
|
+
keywords: ["sq0csp-"]
|
|
1566
|
+
},
|
|
1567
|
+
// ── Typeform ──────────────────────────────────────────────────────────────
|
|
1568
|
+
{
|
|
1569
|
+
name: "Typeform Token",
|
|
1570
|
+
regex: /\btfp_[a-zA-Z0-9_]{59}\b/,
|
|
1571
|
+
severity: "block",
|
|
1572
|
+
keywords: ["tfp_"]
|
|
1573
|
+
},
|
|
1574
|
+
// ── Cloudinary ────────────────────────────────────────────────────────────
|
|
1575
|
+
{
|
|
1576
|
+
name: "Cloudinary URL",
|
|
1577
|
+
regex: /\bcloudinary:\/\/[0-9]+:[a-zA-Z0-9_-]+@[a-zA-Z0-9_-]+/,
|
|
1578
|
+
severity: "block",
|
|
1579
|
+
keywords: ["cloudinary://"]
|
|
1580
|
+
},
|
|
1581
|
+
// ── Airtable ──────────────────────────────────────────────────────────────
|
|
1582
|
+
// New PAT format: pat + 14 alphanum + . + 64 alphanum
|
|
1583
|
+
{
|
|
1584
|
+
name: "Airtable PAT",
|
|
1585
|
+
regex: /\bpat[a-zA-Z0-9]{14}\.[a-zA-Z0-9]{64}\b/,
|
|
1586
|
+
severity: "block",
|
|
1587
|
+
keywords: ["pat"]
|
|
1588
|
+
},
|
|
1589
|
+
// ── RubyGems ──────────────────────────────────────────────────────────────
|
|
1590
|
+
{
|
|
1591
|
+
name: "RubyGems API Key",
|
|
1592
|
+
regex: /\brubygems_[a-f0-9]{48}\b/,
|
|
1593
|
+
severity: "block",
|
|
1594
|
+
keywords: ["rubygems_"]
|
|
1595
|
+
},
|
|
1596
|
+
// ── Shippo ────────────────────────────────────────────────────────────────
|
|
1597
|
+
{
|
|
1598
|
+
name: "Shippo Token",
|
|
1599
|
+
regex: /\bshippo_(?:live|test)_[a-f0-9]{40}\b/,
|
|
1600
|
+
severity: "block",
|
|
1601
|
+
keywords: ["shippo_"]
|
|
1602
|
+
},
|
|
1603
|
+
// ── Plaid ─────────────────────────────────────────────────────────────────
|
|
1604
|
+
{
|
|
1605
|
+
name: "Plaid Access Token",
|
|
1606
|
+
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/,
|
|
1607
|
+
severity: "block",
|
|
1608
|
+
keywords: ["access-sandbox", "access-development", "access-production"]
|
|
1609
|
+
},
|
|
1610
|
+
// ── Age ───────────────────────────────────────────────────────────────────
|
|
1611
|
+
{
|
|
1612
|
+
name: "Age Identity Key",
|
|
1613
|
+
regex: /\bAGE-SECRET-KEY-1[QPZRY9X8GF2TVDW0S3JNLH]{58}\b/,
|
|
1614
|
+
severity: "block",
|
|
1615
|
+
keywords: ["age-secret-key-"]
|
|
1501
1616
|
}
|
|
1502
1617
|
];
|
|
1503
1618
|
SENSITIVE_PATH_PATTERNS = [
|
|
@@ -7647,6 +7762,10 @@ var init_ui = __esm({
|
|
|
7647
7762
|
btn.appendChild(badge);
|
|
7648
7763
|
}
|
|
7649
7764
|
}
|
|
7765
|
+
// Auto-open when browser was launched directly from \`node9 scan\`
|
|
7766
|
+
if (new URLSearchParams(location.search).get('openscan') === '1') {
|
|
7767
|
+
openScanModal();
|
|
7768
|
+
}
|
|
7650
7769
|
} catch {}
|
|
7651
7770
|
})();
|
|
7652
7771
|
|
|
@@ -9488,14 +9607,9 @@ function registerScanCommand(program2) {
|
|
|
9488
9607
|
);
|
|
9489
9608
|
console.log(heroLine);
|
|
9490
9609
|
console.log("");
|
|
9491
|
-
if (
|
|
9610
|
+
if (scan.totalCostUSD > 0) {
|
|
9492
9611
|
console.log(
|
|
9493
|
-
" " + chalk2.
|
|
9494
|
-
);
|
|
9495
|
-
}
|
|
9496
|
-
if (reviewCount > 0) {
|
|
9497
|
-
console.log(
|
|
9498
|
-
" " + chalk2.yellow("\u{1F441} Would have flagged") + " " + chalk2.yellow.bold(String(reviewCount).padStart(5)) + chalk2.dim(" sent to you for approval")
|
|
9612
|
+
" " + chalk2.bold(fmtCost(scan.totalCostUSD)) + chalk2.dim(" AI spend \xB7 ") + chalk2.dim(`${totalRisky} risky operations`)
|
|
9499
9613
|
);
|
|
9500
9614
|
}
|
|
9501
9615
|
if (scan.dlpFindings.length > 0) {
|
|
@@ -9503,47 +9617,27 @@ function registerScanCommand(program2) {
|
|
|
9503
9617
|
" " + chalk2.red("\u{1F511} Credential leak") + " " + chalk2.red.bold(String(scan.dlpFindings.length).padStart(5)) + chalk2.dim(" secret detected in tool call")
|
|
9504
9618
|
);
|
|
9505
9619
|
}
|
|
9506
|
-
if (
|
|
9620
|
+
if (blockedCount > 0) {
|
|
9507
9621
|
console.log(
|
|
9508
|
-
" " + chalk2.
|
|
9622
|
+
" " + chalk2.red("\u{1F6D1} Would have blocked") + " " + chalk2.red.bold(String(blockedCount).padStart(5)) + chalk2.dim(" operations stopped before execution")
|
|
9509
9623
|
);
|
|
9510
9624
|
}
|
|
9511
|
-
|
|
9512
|
-
for (const section of summary.sections) {
|
|
9513
|
-
const countParts = [];
|
|
9514
|
-
if (section.blockedCount > 0)
|
|
9515
|
-
countParts.push(chalk2.red(`${section.blockedCount} blocked`));
|
|
9516
|
-
if (section.reviewCount > 0)
|
|
9517
|
-
countParts.push(chalk2.yellow(`${section.reviewCount} review`));
|
|
9518
|
-
const countStr = countParts.join(chalk2.dim(" \xB7 "));
|
|
9519
|
-
const enableHint = section.shieldKey ? chalk2.dim(` \u2192 node9 shield enable ${section.shieldKey}`) : "";
|
|
9520
|
-
console.log(" " + chalk2.dim("\u2500".repeat(70)));
|
|
9625
|
+
if (scan.loopFindings.length > 0) {
|
|
9521
9626
|
console.log(
|
|
9522
|
-
"
|
|
9627
|
+
" " + chalk2.yellow("\u{1F501} Loop detected") + " " + chalk2.yellow.bold(String(scan.loopFindings.length).padStart(5)) + chalk2.dim(" repeated tool call patterns found")
|
|
9523
9628
|
);
|
|
9524
|
-
for (const rule of section.rules) {
|
|
9525
|
-
printRuleGroup(rule, topN, drillDown, previewWidth);
|
|
9526
|
-
}
|
|
9527
|
-
console.log("");
|
|
9528
9629
|
}
|
|
9529
|
-
|
|
9530
|
-
summary.sections.filter((s) => s.sourceType === "shield" && s.shieldKey).map((s) => s.shieldKey)
|
|
9531
|
-
);
|
|
9532
|
-
const emptyShields = Object.keys(SHIELDS).filter((n) => !activeShieldIds.has(n)).sort();
|
|
9533
|
-
if (emptyShields.length > 0) {
|
|
9534
|
-
console.log(" " + chalk2.dim("\u2500".repeat(70)));
|
|
9630
|
+
if (reviewCount > 0) {
|
|
9535
9631
|
console.log(
|
|
9536
|
-
"
|
|
9632
|
+
" " + chalk2.yellow("\u{1F441} Would have flagged") + " " + chalk2.yellow.bold(String(reviewCount).padStart(5)) + chalk2.dim(" sent to you for approval")
|
|
9537
9633
|
);
|
|
9538
|
-
console.log(" " + chalk2.dim(emptyShields.join(" \xB7 ")));
|
|
9539
|
-
console.log(" " + chalk2.dim("\u2192 node9 shield enable <name> to activate any shield"));
|
|
9540
|
-
console.log("");
|
|
9541
9634
|
}
|
|
9635
|
+
console.log("");
|
|
9542
9636
|
if (scan.dlpFindings.length > 0) {
|
|
9543
9637
|
console.log(" " + chalk2.dim("\u2500".repeat(70)));
|
|
9544
9638
|
console.log(
|
|
9545
9639
|
" " + chalk2.red.bold("\u{1F511} Credential Leaks") + chalk2.dim(" \xB7 ") + chalk2.red(
|
|
9546
|
-
`${num(scan.dlpFindings.length)}
|
|
9640
|
+
`${num(scan.dlpFindings.length)} secret${scan.dlpFindings.length !== 1 ? "s" : ""} found in plain text`
|
|
9547
9641
|
)
|
|
9548
9642
|
);
|
|
9549
9643
|
const shownDlp = drillDown ? scan.dlpFindings : scan.dlpFindings.slice(0, topN);
|
|
@@ -9553,7 +9647,7 @@ function registerScanCommand(program2) {
|
|
|
9553
9647
|
const agentBadge = f.agent === "gemini" ? chalk2.blue("[Gemini] ") : f.agent === "codex" ? chalk2.magenta("[Codex] ") : chalk2.cyan("[Claude] ");
|
|
9554
9648
|
const sessionSuffix = f.sessionId ? chalk2.dim(` \u2192 ${f.sessionId.slice(0, 8)}`) : "";
|
|
9555
9649
|
console.log(
|
|
9556
|
-
` ${ts}${proj}${agentBadge}` + chalk2.yellow(f.patternName) + chalk2.dim(" ") + chalk2.gray(f.redactedSample) + sessionSuffix
|
|
9650
|
+
` \u{1F6A8} ${ts}${proj}${agentBadge}` + chalk2.yellow(f.patternName) + chalk2.dim(" ") + chalk2.gray(f.redactedSample) + sessionSuffix
|
|
9557
9651
|
);
|
|
9558
9652
|
}
|
|
9559
9653
|
if (!drillDown && scan.dlpFindings.length > topN) {
|
|
@@ -9565,6 +9659,21 @@ function registerScanCommand(program2) {
|
|
|
9565
9659
|
}
|
|
9566
9660
|
console.log("");
|
|
9567
9661
|
}
|
|
9662
|
+
const blockedRuleSections = summary.sections.map((s) => ({ ...s, rules: s.rules.filter((r) => r.verdict === "block") })).filter((s) => s.rules.length > 0);
|
|
9663
|
+
if (blockedRuleSections.length > 0) {
|
|
9664
|
+
console.log(" " + chalk2.dim("\u2500".repeat(70)));
|
|
9665
|
+
console.log(
|
|
9666
|
+
" " + chalk2.red.bold("\u{1F6D1} Blocked") + chalk2.dim(" \xB7 ") + chalk2.red(
|
|
9667
|
+
`${blockedCount} operation${blockedCount !== 1 ? "s" : ""} node9 would have stopped`
|
|
9668
|
+
)
|
|
9669
|
+
);
|
|
9670
|
+
for (const section of blockedRuleSections) {
|
|
9671
|
+
for (const rule of section.rules) {
|
|
9672
|
+
printRuleGroup(rule, topN, drillDown, previewWidth);
|
|
9673
|
+
}
|
|
9674
|
+
}
|
|
9675
|
+
console.log("");
|
|
9676
|
+
}
|
|
9568
9677
|
if (scan.loopFindings.length > 0) {
|
|
9569
9678
|
console.log(" " + chalk2.dim("\u2500".repeat(70)));
|
|
9570
9679
|
console.log(
|
|
@@ -9591,12 +9700,32 @@ function registerScanCommand(program2) {
|
|
|
9591
9700
|
}
|
|
9592
9701
|
console.log("");
|
|
9593
9702
|
}
|
|
9594
|
-
|
|
9595
|
-
|
|
9596
|
-
|
|
9597
|
-
|
|
9703
|
+
for (const section of summary.sections) {
|
|
9704
|
+
const reviewRules = section.rules.filter((r) => r.verdict !== "block");
|
|
9705
|
+
if (reviewRules.length === 0) continue;
|
|
9706
|
+
const enableHint = section.shieldKey ? chalk2.dim(` \u2192 node9 shield enable ${section.shieldKey}`) : "";
|
|
9707
|
+
console.log(" " + chalk2.dim("\u2500".repeat(70)));
|
|
9708
|
+
console.log(
|
|
9709
|
+
" " + chalk2.bold(section.label) + (section.subtitle ? chalk2.dim(` \xB7 ${section.subtitle}`) : "") + " " + chalk2.yellow(`${section.reviewCount} review`) + enableHint
|
|
9710
|
+
);
|
|
9711
|
+
for (const rule of reviewRules) {
|
|
9712
|
+
printRuleGroup(rule, topN, drillDown, previewWidth);
|
|
9713
|
+
}
|
|
9714
|
+
console.log("");
|
|
9715
|
+
}
|
|
9716
|
+
const activeShieldIds = new Set(
|
|
9717
|
+
summary.sections.filter((s) => s.sourceType === "shield" && s.shieldKey).map((s) => s.shieldKey)
|
|
9598
9718
|
);
|
|
9599
|
-
|
|
9719
|
+
const emptyShields = Object.keys(SHIELDS).filter((n) => !activeShieldIds.has(n)).sort();
|
|
9720
|
+
if (emptyShields.length > 0) {
|
|
9721
|
+
console.log(" " + chalk2.dim("\u2500".repeat(70)));
|
|
9722
|
+
console.log(
|
|
9723
|
+
" " + chalk2.bold("\u{1F6E1} Inactive Shields") + chalk2.dim(" \xB7 enable for more coverage")
|
|
9724
|
+
);
|
|
9725
|
+
console.log(" " + chalk2.dim(emptyShields.join(" \xB7 ")));
|
|
9726
|
+
console.log(" " + chalk2.dim("\u2192 node9 shield enable <name> to activate"));
|
|
9727
|
+
console.log("");
|
|
9728
|
+
}
|
|
9600
9729
|
}
|
|
9601
9730
|
if (isInstalled) {
|
|
9602
9731
|
console.log(chalk2.green(" \u2705 node9 is active \u2014 your future sessions are protected."));
|
|
@@ -9637,7 +9766,7 @@ function registerScanCommand(program2) {
|
|
|
9637
9766
|
console.log("");
|
|
9638
9767
|
if (!isTestingMode()) {
|
|
9639
9768
|
if (isInstalled) {
|
|
9640
|
-
const url = `http://${DAEMON_HOST}:${DAEMON_PORT}
|
|
9769
|
+
const url = `http://${DAEMON_HOST}:${DAEMON_PORT}/?openscan=1`;
|
|
9641
9770
|
if (isDaemonRunning()) {
|
|
9642
9771
|
const internalToken = getInternalToken();
|
|
9643
9772
|
if (internalToken) {
|
package/dist/index.js
CHANGED
|
@@ -995,6 +995,17 @@ var ASSIGNMENT_CONTEXT_RE = /\b(?:password|passwd|secret|token|api[_-]?key|auth(
|
|
|
995
995
|
function isAssignmentContext(text) {
|
|
996
996
|
return ASSIGNMENT_CONTEXT_RE.test(text);
|
|
997
997
|
}
|
|
998
|
+
function shannonEntropy(s) {
|
|
999
|
+
if (s.length === 0) return 0;
|
|
1000
|
+
const freq = /* @__PURE__ */ new Map();
|
|
1001
|
+
for (const ch of s) freq.set(ch, (freq.get(ch) ?? 0) + 1);
|
|
1002
|
+
let h = 0;
|
|
1003
|
+
for (const count of freq.values()) {
|
|
1004
|
+
const p = count / s.length;
|
|
1005
|
+
h -= p * Math.log2(p);
|
|
1006
|
+
}
|
|
1007
|
+
return h;
|
|
1008
|
+
}
|
|
998
1009
|
var DLP_STOPWORDS = [
|
|
999
1010
|
"example",
|
|
1000
1011
|
"placeholder",
|
|
@@ -1017,7 +1028,9 @@ var DLP_STOPWORDS = [
|
|
|
1017
1028
|
"%{",
|
|
1018
1029
|
"<your",
|
|
1019
1030
|
"test_key",
|
|
1020
|
-
"test_token"
|
|
1031
|
+
"test_token",
|
|
1032
|
+
"your",
|
|
1033
|
+
"here"
|
|
1021
1034
|
];
|
|
1022
1035
|
var DLP_PATTERNS = [
|
|
1023
1036
|
// ── AWS ───────────────────────────────────────────────────────────────────
|
|
@@ -1068,7 +1081,8 @@ var DLP_PATTERNS = [
|
|
|
1068
1081
|
name: "OpenAI API Key",
|
|
1069
1082
|
regex: /\bsk-[a-zA-Z0-9_-]{20,}\b/,
|
|
1070
1083
|
severity: "block",
|
|
1071
|
-
keywords: ["sk-"]
|
|
1084
|
+
keywords: ["sk-"],
|
|
1085
|
+
minEntropy: 3.5
|
|
1072
1086
|
},
|
|
1073
1087
|
// ── Stripe ────────────────────────────────────────────────────────────────
|
|
1074
1088
|
{
|
|
@@ -1139,7 +1153,13 @@ var DLP_PATTERNS = [
|
|
|
1139
1153
|
keywords: ["hvb."]
|
|
1140
1154
|
},
|
|
1141
1155
|
// ── Hugging Face ──────────────────────────────────────────────────────────
|
|
1142
|
-
{
|
|
1156
|
+
{
|
|
1157
|
+
name: "HuggingFace Token",
|
|
1158
|
+
regex: /\bhf_[A-Za-z]{34}\b/,
|
|
1159
|
+
severity: "block",
|
|
1160
|
+
keywords: ["hf_"],
|
|
1161
|
+
minEntropy: 3
|
|
1162
|
+
},
|
|
1143
1163
|
// ── Postman ───────────────────────────────────────────────────────────────
|
|
1144
1164
|
{
|
|
1145
1165
|
name: "Postman API Token",
|
|
@@ -1291,7 +1311,8 @@ var DLP_PATTERNS = [
|
|
|
1291
1311
|
name: "PyPI Upload Token",
|
|
1292
1312
|
regex: /\bpypi-[A-Za-z0-9_-]{50,}\b/,
|
|
1293
1313
|
severity: "block",
|
|
1294
|
-
keywords: ["pypi-"]
|
|
1314
|
+
keywords: ["pypi-"],
|
|
1315
|
+
minEntropy: 3
|
|
1295
1316
|
},
|
|
1296
1317
|
// ── Bearer Token ─────────────────────────────────────────────────────────
|
|
1297
1318
|
// contextBoost: promoted to block when assigned (e.g. AUTH_TOKEN=Bearer eyJ...)
|
|
@@ -1300,7 +1321,99 @@ var DLP_PATTERNS = [
|
|
|
1300
1321
|
regex: /Bearer\s+[a-zA-Z0-9\-._~+/]{20,}=*/i,
|
|
1301
1322
|
severity: "review",
|
|
1302
1323
|
keywords: ["bearer"],
|
|
1303
|
-
contextBoost: true
|
|
1324
|
+
contextBoost: true,
|
|
1325
|
+
minEntropy: 3
|
|
1326
|
+
},
|
|
1327
|
+
// ── Resend ────────────────────────────────────────────────────────────────
|
|
1328
|
+
{
|
|
1329
|
+
name: "Resend API Key",
|
|
1330
|
+
regex: /\bre_[a-zA-Z0-9]{24}\b/,
|
|
1331
|
+
severity: "block",
|
|
1332
|
+
keywords: ["re_"]
|
|
1333
|
+
},
|
|
1334
|
+
// ── Telegram ──────────────────────────────────────────────────────────────
|
|
1335
|
+
{
|
|
1336
|
+
name: "Telegram Bot Token",
|
|
1337
|
+
regex: /\b[0-9]{7,10}:AA[a-zA-Z0-9_-]{33}\b/,
|
|
1338
|
+
severity: "block",
|
|
1339
|
+
keywords: [":aa"]
|
|
1340
|
+
},
|
|
1341
|
+
// ── Mapbox ────────────────────────────────────────────────────────────────
|
|
1342
|
+
{
|
|
1343
|
+
name: "Mapbox Access Token",
|
|
1344
|
+
regex: /\bpk\.eyJ1[a-zA-Z0-9._-]{20,}\b/,
|
|
1345
|
+
severity: "block",
|
|
1346
|
+
keywords: ["pk.eyj1"]
|
|
1347
|
+
},
|
|
1348
|
+
// ── Notion ────────────────────────────────────────────────────────────────
|
|
1349
|
+
{
|
|
1350
|
+
name: "Notion Integration Token",
|
|
1351
|
+
regex: /\bsecret_[a-zA-Z0-9]{43}\b/,
|
|
1352
|
+
severity: "block",
|
|
1353
|
+
keywords: ["secret_"]
|
|
1354
|
+
},
|
|
1355
|
+
// ── Square ────────────────────────────────────────────────────────────────
|
|
1356
|
+
{
|
|
1357
|
+
name: "Square Access Token",
|
|
1358
|
+
regex: /\bsq0atp-[0-9A-Za-z_-]{22}\b/,
|
|
1359
|
+
severity: "block",
|
|
1360
|
+
keywords: ["sq0atp-"]
|
|
1361
|
+
},
|
|
1362
|
+
{
|
|
1363
|
+
name: "Square OAuth Secret",
|
|
1364
|
+
regex: /\bsq0csp-[0-9A-Za-z_-]{43}\b/,
|
|
1365
|
+
severity: "block",
|
|
1366
|
+
keywords: ["sq0csp-"]
|
|
1367
|
+
},
|
|
1368
|
+
// ── Typeform ──────────────────────────────────────────────────────────────
|
|
1369
|
+
{
|
|
1370
|
+
name: "Typeform Token",
|
|
1371
|
+
regex: /\btfp_[a-zA-Z0-9_]{59}\b/,
|
|
1372
|
+
severity: "block",
|
|
1373
|
+
keywords: ["tfp_"]
|
|
1374
|
+
},
|
|
1375
|
+
// ── Cloudinary ────────────────────────────────────────────────────────────
|
|
1376
|
+
{
|
|
1377
|
+
name: "Cloudinary URL",
|
|
1378
|
+
regex: /\bcloudinary:\/\/[0-9]+:[a-zA-Z0-9_-]+@[a-zA-Z0-9_-]+/,
|
|
1379
|
+
severity: "block",
|
|
1380
|
+
keywords: ["cloudinary://"]
|
|
1381
|
+
},
|
|
1382
|
+
// ── Airtable ──────────────────────────────────────────────────────────────
|
|
1383
|
+
// New PAT format: pat + 14 alphanum + . + 64 alphanum
|
|
1384
|
+
{
|
|
1385
|
+
name: "Airtable PAT",
|
|
1386
|
+
regex: /\bpat[a-zA-Z0-9]{14}\.[a-zA-Z0-9]{64}\b/,
|
|
1387
|
+
severity: "block",
|
|
1388
|
+
keywords: ["pat"]
|
|
1389
|
+
},
|
|
1390
|
+
// ── RubyGems ──────────────────────────────────────────────────────────────
|
|
1391
|
+
{
|
|
1392
|
+
name: "RubyGems API Key",
|
|
1393
|
+
regex: /\brubygems_[a-f0-9]{48}\b/,
|
|
1394
|
+
severity: "block",
|
|
1395
|
+
keywords: ["rubygems_"]
|
|
1396
|
+
},
|
|
1397
|
+
// ── Shippo ────────────────────────────────────────────────────────────────
|
|
1398
|
+
{
|
|
1399
|
+
name: "Shippo Token",
|
|
1400
|
+
regex: /\bshippo_(?:live|test)_[a-f0-9]{40}\b/,
|
|
1401
|
+
severity: "block",
|
|
1402
|
+
keywords: ["shippo_"]
|
|
1403
|
+
},
|
|
1404
|
+
// ── Plaid ─────────────────────────────────────────────────────────────────
|
|
1405
|
+
{
|
|
1406
|
+
name: "Plaid Access Token",
|
|
1407
|
+
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/,
|
|
1408
|
+
severity: "block",
|
|
1409
|
+
keywords: ["access-sandbox", "access-development", "access-production"]
|
|
1410
|
+
},
|
|
1411
|
+
// ── Age ───────────────────────────────────────────────────────────────────
|
|
1412
|
+
{
|
|
1413
|
+
name: "Age Identity Key",
|
|
1414
|
+
regex: /\bAGE-SECRET-KEY-1[QPZRY9X8GF2TVDW0S3JNLH]{58}\b/,
|
|
1415
|
+
severity: "block",
|
|
1416
|
+
keywords: ["age-secret-key-"]
|
|
1304
1417
|
}
|
|
1305
1418
|
];
|
|
1306
1419
|
var SENSITIVE_PATH_PATTERNS = [
|
|
@@ -1397,8 +1510,9 @@ function scanArgs(args, depth = 0, fieldPath = "args") {
|
|
|
1397
1510
|
continue;
|
|
1398
1511
|
}
|
|
1399
1512
|
if (pattern.regex.test(text)) {
|
|
1400
|
-
const
|
|
1401
|
-
if (DLP_STOPWORDS.some((sw) =>
|
|
1513
|
+
const raw = text.match(pattern.regex)?.[0] ?? "";
|
|
1514
|
+
if (DLP_STOPWORDS.some((sw) => raw.toLowerCase().includes(sw))) continue;
|
|
1515
|
+
if (pattern.minEntropy !== void 0 && shannonEntropy(raw) < pattern.minEntropy) continue;
|
|
1402
1516
|
const severity = pattern.contextBoost && assignmentCtx ? "block" : pattern.severity;
|
|
1403
1517
|
return {
|
|
1404
1518
|
patternName: pattern.name,
|