xploitscan-shared-rules 1.6.2 → 1.7.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 +32 -5
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +32 -5
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -626,6 +626,17 @@ function isInsideFixMessage(content, matchIndex) {
|
|
|
626
626
|
const lineText = content.substring(lineStart, content.indexOf("\n", matchIndex));
|
|
627
627
|
return /(?:fix|description|message|suggestion|hint|help|example|doc|comment)\s*[:=(]/i.test(lineText) || /return\s*["'`].*\b(?:Use|Replace|Add|Move|Set|Enable|Disable|Never|Don't|Do not|Instead)\b/i.test(lineText);
|
|
628
628
|
}
|
|
629
|
+
function isInlineSilenced(content, matchIndex, ruleId) {
|
|
630
|
+
const lines = content.split("\n");
|
|
631
|
+
const matchLineNum = content.substring(0, matchIndex).split("\n").length - 1;
|
|
632
|
+
const matchLine = lines[matchLineNum] ?? "";
|
|
633
|
+
const prevLine = matchLineNum > 0 ? lines[matchLineNum - 1] ?? "" : "";
|
|
634
|
+
const marker = new RegExp(
|
|
635
|
+
`(?://|/\\*|#)\\s*(?:${ruleId}|scanner)-OK\\b`,
|
|
636
|
+
"i"
|
|
637
|
+
);
|
|
638
|
+
return marker.test(matchLine) || marker.test(prevLine);
|
|
639
|
+
}
|
|
629
640
|
function findMatches(content, pattern, rule, filePath, fixTemplate) {
|
|
630
641
|
const matches = [];
|
|
631
642
|
const lines = content.split("\n");
|
|
@@ -754,6 +765,16 @@ var hardcodedSecrets = {
|
|
|
754
765
|
const secretMatch = lineText.match(/[:=]\s*["'`]([^"'`]*)["'`]/);
|
|
755
766
|
if (secretMatch && secretMatch[1].length < floor) continue;
|
|
756
767
|
}
|
|
768
|
+
if (pi === 6) {
|
|
769
|
+
const valMatch = lineText.match(/[:=]\s*["'`]([^"'`]+)["'`]/);
|
|
770
|
+
const nameMatch = lineText.match(/\b([A-Z][A-Z0-9_]*_(?:SECRET|TOKEN|KEY|PASSWORD|PASSWD))\b/);
|
|
771
|
+
if (valMatch && nameMatch) {
|
|
772
|
+
const value = valMatch[1];
|
|
773
|
+
const isKeySuffix = nameMatch[1].endsWith("_KEY");
|
|
774
|
+
const isKebabIdentifier = value.length < 40 && /^[a-z0-9]+(-[a-z0-9]+)+$/.test(value);
|
|
775
|
+
if (isKeySuffix && isKebabIdentifier) continue;
|
|
776
|
+
}
|
|
777
|
+
}
|
|
757
778
|
matches.push(rm);
|
|
758
779
|
}
|
|
759
780
|
}
|
|
@@ -947,7 +968,7 @@ var sqlInjection = {
|
|
|
947
968
|
/(?:SELECT|INSERT|UPDATE|DELETE|WHERE)\s+.*\$\{(?!.*parameterized)/gi
|
|
948
969
|
];
|
|
949
970
|
const matches = [];
|
|
950
|
-
const usesParams = /\?\s
|
|
971
|
+
const usesParams = /\?\s*,|\?[^?,;]{1,20}\?|\$\d+|:[\w]+|\bprepare\b|\bplaceholders?\b/i.test(content);
|
|
951
972
|
if (usesParams) return [];
|
|
952
973
|
for (const pattern of patterns) {
|
|
953
974
|
const raw = findMatches(
|
|
@@ -3038,6 +3059,7 @@ var dangerousInnerHTML = {
|
|
|
3038
3059
|
let m;
|
|
3039
3060
|
while ((m = re.exec(content)) !== null) {
|
|
3040
3061
|
if (isCommentLine(content, m.index)) continue;
|
|
3062
|
+
if (isInlineSilenced(content, m.index, "VC063")) continue;
|
|
3041
3063
|
const value = m[1].trim();
|
|
3042
3064
|
if (/^[A-Z][A-Z0-9_]+$/.test(value)) continue;
|
|
3043
3065
|
if (/^["'`][^$]*["'`]$/.test(value)) continue;
|
|
@@ -3051,7 +3073,7 @@ var dangerousInnerHTML = {
|
|
|
3051
3073
|
file: filePath,
|
|
3052
3074
|
line: lineNum,
|
|
3053
3075
|
snippet: getSnippet(content, lineNum),
|
|
3054
|
-
fix: "Sanitize HTML before using dangerouslySetInnerHTML: dangerouslySetInnerHTML={{ __html: DOMPurify.sanitize(content) }}. Install: npm install dompurify"
|
|
3076
|
+
fix: "Sanitize HTML before using dangerouslySetInnerHTML: dangerouslySetInnerHTML={{ __html: DOMPurify.sanitize(content) }}. Install: npm install dompurify. If this site is intentional (server-built template, nonce-attribute boot script, etc.), add an inline `// VC063-OK: <reason>` comment above the line to silence."
|
|
3055
3077
|
});
|
|
3056
3078
|
}
|
|
3057
3079
|
return findings;
|
|
@@ -7053,6 +7075,7 @@ var secretInURLParam = {
|
|
|
7053
7075
|
while ((m = re.exec(content)) !== null) {
|
|
7054
7076
|
if (isCommentLine(content, m.index)) continue;
|
|
7055
7077
|
if (isInsideFixMessage(content, m.index)) continue;
|
|
7078
|
+
if (isInlineSilenced(content, m.index, "VC146")) continue;
|
|
7056
7079
|
const lineNum = content.substring(0, m.index).split("\n").length;
|
|
7057
7080
|
findings.push({
|
|
7058
7081
|
rule: "VC146",
|
|
@@ -7062,7 +7085,7 @@ var secretInURLParam = {
|
|
|
7062
7085
|
file: filePath,
|
|
7063
7086
|
line: lineNum,
|
|
7064
7087
|
snippet: getSnippet(content, lineNum),
|
|
7065
|
-
fix: "Pass secrets in the Authorization header (Bearer token) or request body, never in URL parameters. URL parameters are logged in server access logs, browser history, and referrer headers."
|
|
7088
|
+
fix: "Pass secrets in the Authorization header (Bearer token) or request body, never in URL parameters. URL parameters are logged in server access logs, browser history, and referrer headers. If this finding is legitimate (magic-link / claim-token flow), add an inline `// VC146-OK: <reason>` comment above the line to silence it."
|
|
7066
7089
|
});
|
|
7067
7090
|
}
|
|
7068
7091
|
}
|
|
@@ -7253,10 +7276,14 @@ var webhookSignatureVerification = {
|
|
|
7253
7276
|
check(content, filePath) {
|
|
7254
7277
|
if (!filePath.match(/\.(js|ts|jsx|tsx)$/)) return [];
|
|
7255
7278
|
if (isTestFile(filePath)) return [];
|
|
7256
|
-
if (
|
|
7279
|
+
if (!/webhook/i.test(filePath)) return [];
|
|
7257
7280
|
if (!/export\s+(?:async\s+)?function\s+POST/i.test(content)) return [];
|
|
7258
7281
|
const services = [
|
|
7259
|
-
|
|
7282
|
+
// Tightened Clerk events: require the specific subevent suffix
|
|
7283
|
+
// rather than bare `session.`. The bare-prefix version matched
|
|
7284
|
+
// Stripe Checkout `session.url`, which has nothing to do with
|
|
7285
|
+
// Clerk webhooks.
|
|
7286
|
+
{ name: "Clerk", content: /clerk/i, events: /user\.(created|updated|deleted)|session\.(created|removed|ended|revoked)/i, verify: /svix|Webhook\(\)\.verify|webhook-id|wh_secret/i },
|
|
7260
7287
|
{ name: "GitHub", content: /github/i, events: /push|pull_request|issues|installation/i, verify: /createHmac|x-hub-signature|verify.*signature|GITHUB_WEBHOOK_SECRET/i },
|
|
7261
7288
|
{ name: "Resend", content: /resend/i, events: /email\.sent|email\.delivered|email\.bounced/i, verify: /svix|webhook.*verify|x-webhook-signature|RESEND_WEBHOOK_SECRET/i },
|
|
7262
7289
|
{ name: "SendGrid", content: /sendgrid/i, events: /inbound.*parse|event.*webhook/i, verify: /EventWebhook|x-twilio-email|verifySignature|SENDGRID_WEBHOOK_VERIFICATION/i },
|