xploitscan-shared-rules 1.13.1 → 1.14.0
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/index.cjs +124 -3
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +3 -1
- package/dist/index.d.ts +3 -1
- package/dist/index.js +122 -3
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -97,6 +97,7 @@ __export(index_exports, {
|
|
|
97
97
|
hardcodedAnthropicKey: () => hardcodedAnthropicKey,
|
|
98
98
|
hardcodedCloudflareToken: () => hardcodedCloudflareToken,
|
|
99
99
|
hardcodedCohereKey: () => hardcodedCohereKey,
|
|
100
|
+
hardcodedCreditCard: () => hardcodedCreditCard,
|
|
100
101
|
hardcodedDatadogKey: () => hardcodedDatadogKey,
|
|
101
102
|
hardcodedDiscordToken: () => hardcodedDiscordToken,
|
|
102
103
|
hardcodedEncryptionKey: () => hardcodedEncryptionKey,
|
|
@@ -126,6 +127,7 @@ __export(index_exports, {
|
|
|
126
127
|
hardcodedRailwayToken: () => hardcodedRailwayToken,
|
|
127
128
|
hardcodedReplicateKey: () => hardcodedReplicateKey,
|
|
128
129
|
hardcodedResendKey: () => hardcodedResendKey,
|
|
130
|
+
hardcodedSSN: () => hardcodedSSN,
|
|
129
131
|
hardcodedSecrets: () => hardcodedSecrets,
|
|
130
132
|
hardcodedSendGridKey: () => hardcodedSendGridKey,
|
|
131
133
|
hardcodedSentryAuthToken: () => hardcodedSentryAuthToken,
|
|
@@ -521,7 +523,9 @@ var RULE_IMPACTS = {
|
|
|
521
523
|
VC207: "Model output is attacker-influenceable via prompt injection. Feeding it into eval, new Function, a shell command, a raw SQL string, or a filesystem path turns a crafted or hallucinated response into remote code execution, command injection, SQL injection, or path traversal \u2014 your most dangerous sinks, driven by untrusted text.",
|
|
522
524
|
VC208: "Interpolating a secret into a prompt ships your API key, token, or password to a third-party model provider, where it persists in their request logs and training-eligible data. A credential that leaves your infrastructure in prompt text should be considered compromised and rotated.",
|
|
523
525
|
VC209: "Webhooks are delivered at-least-once. Without de-duplicating on the event id, a retried or replayed delivery re-runs the side effect \u2014 a customer is charged twice, a record is duplicated, or an entitlement is granted again. Stripe and Svix both retry on any non-2xx, so this fires in normal operation, not just under attack.",
|
|
524
|
-
VC210: "If your auth middleware skips /api, those routes run with no gate unless each one re-checks auth itself. It is the most common way a Next.js app ends up with publicly callable API routes that everyone assumed the middleware was protecting."
|
|
526
|
+
VC210: "If your auth middleware skips /api, those routes run with no gate unless each one re-checks auth itself. It is the most common way a Next.js app ends up with publicly callable API routes that everyone assumed the middleware was protecting.",
|
|
527
|
+
VC211: "A real credit card number in source is cardholder data sitting in git history, CI logs, and every backup \u2014 a direct PCI-DSS violation. Anyone with repo access can read it, and it cannot be un-leaked once committed; the card must be treated as compromised.",
|
|
528
|
+
VC212: "A hardcoded Social Security Number is regulated PII permanently embedded in your git history and backups. It exposes a real person to identity theft, and its presence in source can trigger breach-notification and privacy-law obligations the moment the repo is accessed."
|
|
525
529
|
};
|
|
526
530
|
|
|
527
531
|
// src/exposure.ts
|
|
@@ -5062,8 +5066,13 @@ var complianceMap = {
|
|
|
5062
5066
|
// VC209–VC210: advisory heuristics
|
|
5063
5067
|
VC209: { owasp: "A04:2021", cwe: "CWE-799" },
|
|
5064
5068
|
// webhook missing idempotency
|
|
5065
|
-
VC210: { owasp: "A01:2021", cwe: "CWE-862" }
|
|
5069
|
+
VC210: { owasp: "A01:2021", cwe: "CWE-862" },
|
|
5066
5070
|
// middleware matcher excludes /api
|
|
5071
|
+
// VC211–VC212: hardcoded sensitive personal data (PII) in source
|
|
5072
|
+
VC211: { owasp: "A02:2021", cwe: "CWE-540" },
|
|
5073
|
+
// hardcoded credit card number
|
|
5074
|
+
VC212: { owasp: "A02:2021", cwe: "CWE-540" }
|
|
5075
|
+
// hardcoded US SSN
|
|
5067
5076
|
};
|
|
5068
5077
|
var consoleLogProduction = {
|
|
5069
5078
|
id: "VC097",
|
|
@@ -7888,6 +7897,113 @@ var middlewareMatcherExcludesApi = {
|
|
|
7888
7897
|
}], content, "VC210");
|
|
7889
7898
|
}
|
|
7890
7899
|
};
|
|
7900
|
+
function passesLuhn(digits) {
|
|
7901
|
+
let sum = 0;
|
|
7902
|
+
let alt = false;
|
|
7903
|
+
for (let i = digits.length - 1; i >= 0; i--) {
|
|
7904
|
+
let d = digits.charCodeAt(i) - 48;
|
|
7905
|
+
if (d < 0 || d > 9) return false;
|
|
7906
|
+
if (alt) {
|
|
7907
|
+
d *= 2;
|
|
7908
|
+
if (d > 9) d -= 9;
|
|
7909
|
+
}
|
|
7910
|
+
sum += d;
|
|
7911
|
+
alt = !alt;
|
|
7912
|
+
}
|
|
7913
|
+
return sum % 10 === 0;
|
|
7914
|
+
}
|
|
7915
|
+
function looksLikeIssuerCard(d) {
|
|
7916
|
+
if (/^4/.test(d) && (d.length === 13 || d.length === 16 || d.length === 19)) return true;
|
|
7917
|
+
if (/^5[1-5]/.test(d) && d.length === 16) return true;
|
|
7918
|
+
if (/^2(2[2-9]|[3-6]\d|7[01]|720)/.test(d) && d.length === 16) return true;
|
|
7919
|
+
if (/^3[47]/.test(d) && d.length === 15) return true;
|
|
7920
|
+
if (/^(6011|65|64[4-9])/.test(d) && d.length === 16) return true;
|
|
7921
|
+
return false;
|
|
7922
|
+
}
|
|
7923
|
+
var TEST_CARD_NUMBERS = /* @__PURE__ */ new Set([
|
|
7924
|
+
"4242424242424242",
|
|
7925
|
+
"4111111111111111",
|
|
7926
|
+
"4012888888881881",
|
|
7927
|
+
"4000056655665556",
|
|
7928
|
+
"4000000000000002",
|
|
7929
|
+
"4000000000009995",
|
|
7930
|
+
"5555555555554444",
|
|
7931
|
+
"5200828282828210",
|
|
7932
|
+
"5105105105105100",
|
|
7933
|
+
"2223003122003222",
|
|
7934
|
+
"378282246310005",
|
|
7935
|
+
"371449635398431",
|
|
7936
|
+
"6011111111111117",
|
|
7937
|
+
"6011000990139424",
|
|
7938
|
+
"3056930009020004",
|
|
7939
|
+
"38520000023237"
|
|
7940
|
+
]);
|
|
7941
|
+
var hardcodedCreditCard = {
|
|
7942
|
+
id: "VC211",
|
|
7943
|
+
title: "Hardcoded credit card number",
|
|
7944
|
+
severity: "high",
|
|
7945
|
+
category: "Information Leakage",
|
|
7946
|
+
description: "A literal credit card number (Luhn-valid, with a real issuer prefix, and not a known network test card) appears in source. Cardholder data does not belong in code \u2014 it lands in git history, logs, and backups, and is a PCI-DSS violation. Use your processor's published test cards for tests, and tokenize real cards.",
|
|
7947
|
+
check(content, filePath) {
|
|
7948
|
+
if (isTestFile(filePath)) return [];
|
|
7949
|
+
const findings = [];
|
|
7950
|
+
const pattern = /(?<![\d.])\d[\d -]{11,21}\d(?![\d.])/g;
|
|
7951
|
+
let m;
|
|
7952
|
+
while ((m = pattern.exec(content)) !== null) {
|
|
7953
|
+
const digits = m[0].replace(/[ -]/g, "");
|
|
7954
|
+
if (digits.length < 13 || digits.length > 19) continue;
|
|
7955
|
+
if (TEST_CARD_NUMBERS.has(digits)) continue;
|
|
7956
|
+
if (!looksLikeIssuerCard(digits)) continue;
|
|
7957
|
+
if (!passesLuhn(digits)) continue;
|
|
7958
|
+
if (isCommentLine(content, m.index)) continue;
|
|
7959
|
+
const lineNum = lineNumberAt(content, m.index);
|
|
7960
|
+
findings.push({
|
|
7961
|
+
rule: "VC211",
|
|
7962
|
+
title: hardcodedCreditCard.title,
|
|
7963
|
+
severity: "high",
|
|
7964
|
+
category: "Information Leakage",
|
|
7965
|
+
file: filePath,
|
|
7966
|
+
line: lineNum,
|
|
7967
|
+
snippet: getSnippet(content, lineNum),
|
|
7968
|
+
fix: "Never store a real card number in source. Tokenize through your payment processor (Stripe, Braintree) and keep only the returned token. For tests, use the processor's published test cards (e.g. Stripe's 4242 4242 4242 4242)."
|
|
7969
|
+
});
|
|
7970
|
+
}
|
|
7971
|
+
return findings;
|
|
7972
|
+
}
|
|
7973
|
+
};
|
|
7974
|
+
var hardcodedSSN = {
|
|
7975
|
+
id: "VC212",
|
|
7976
|
+
title: "Hardcoded US Social Security Number",
|
|
7977
|
+
severity: "high",
|
|
7978
|
+
category: "Information Leakage",
|
|
7979
|
+
description: "A value in SSN format (NNN-NN-NNNN) is assigned to an SSN-named field in source. Social Security Numbers are regulated PII; a literal one leaks into git history, logs, and backups. Store SSNs encrypted at rest and load test data from generated fakes, never a code literal.",
|
|
7980
|
+
check(content, filePath) {
|
|
7981
|
+
if (isTestFile(filePath)) return [];
|
|
7982
|
+
const findings = [];
|
|
7983
|
+
const pattern = /(?:ssn|social[_-]?sec(?:urity)?(?:[_-]?(?:no|num|number))?|taxpayer[_-]?id)\b["']?\s*[:=]\s*["']?(\d{3}-\d{2}-\d{4})/gi;
|
|
7984
|
+
let m;
|
|
7985
|
+
while ((m = pattern.exec(content)) !== null) {
|
|
7986
|
+
const ssn = m[1];
|
|
7987
|
+
const [area, group, serial] = ssn.split("-");
|
|
7988
|
+
if (area === "000" || area === "666" || Number(area) >= 900) continue;
|
|
7989
|
+
if (group === "00" || serial === "0000") continue;
|
|
7990
|
+
if (ssn === "123-45-6789") continue;
|
|
7991
|
+
if (isCommentLine(content, m.index)) continue;
|
|
7992
|
+
const lineNum = lineNumberAt(content, m.index);
|
|
7993
|
+
findings.push({
|
|
7994
|
+
rule: "VC212",
|
|
7995
|
+
title: hardcodedSSN.title,
|
|
7996
|
+
severity: "high",
|
|
7997
|
+
category: "Information Leakage",
|
|
7998
|
+
file: filePath,
|
|
7999
|
+
line: lineNum,
|
|
8000
|
+
snippet: getSnippet(content, lineNum),
|
|
8001
|
+
fix: "Don't hardcode SSNs. Generate fake values for tests (a faker/factory), and store real SSNs with application-level encryption or in a secrets vault \u2014 never as a code literal."
|
|
8002
|
+
});
|
|
8003
|
+
}
|
|
8004
|
+
return findings;
|
|
8005
|
+
}
|
|
8006
|
+
};
|
|
7891
8007
|
var secretInURLParam = {
|
|
7892
8008
|
id: "VC146",
|
|
7893
8009
|
title: "Secret Passed in URL Query Parameter",
|
|
@@ -8616,7 +8732,10 @@ var allCustomRules = [
|
|
|
8616
8732
|
// VC204–VC206: GraphQL server hardening
|
|
8617
8733
|
graphqlNoDepthLimit,
|
|
8618
8734
|
graphqlNoComplexityLimit,
|
|
8619
|
-
graphqlCSRFDisabled
|
|
8735
|
+
graphqlCSRFDisabled,
|
|
8736
|
+
// VC211–VC212: hardcoded sensitive personal data (PII)
|
|
8737
|
+
hardcodedCreditCard,
|
|
8738
|
+
hardcodedSSN
|
|
8620
8739
|
];
|
|
8621
8740
|
function runCustomRules(content, filePath, disabledRules = [], tier = "free", extraRules = []) {
|
|
8622
8741
|
const findings = [];
|
|
@@ -9045,6 +9164,7 @@ function scanEntropy(files) {
|
|
|
9045
9164
|
hardcodedAnthropicKey,
|
|
9046
9165
|
hardcodedCloudflareToken,
|
|
9047
9166
|
hardcodedCohereKey,
|
|
9167
|
+
hardcodedCreditCard,
|
|
9048
9168
|
hardcodedDatadogKey,
|
|
9049
9169
|
hardcodedDiscordToken,
|
|
9050
9170
|
hardcodedEncryptionKey,
|
|
@@ -9074,6 +9194,7 @@ function scanEntropy(files) {
|
|
|
9074
9194
|
hardcodedRailwayToken,
|
|
9075
9195
|
hardcodedReplicateKey,
|
|
9076
9196
|
hardcodedResendKey,
|
|
9197
|
+
hardcodedSSN,
|
|
9077
9198
|
hardcodedSecrets,
|
|
9078
9199
|
hardcodedSendGridKey,
|
|
9079
9200
|
hardcodedSentryAuthToken,
|