xploitscan-shared-rules 1.13.0 → 1.13.1
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 +163 -81
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +2 -3
- package/dist/index.d.ts +2 -3
- package/dist/index.js +163 -81
- package/dist/index.js.map +1 -1
- package/package.json +3 -3
package/dist/index.cjs
CHANGED
|
@@ -264,15 +264,50 @@ __export(index_exports, {
|
|
|
264
264
|
module.exports = __toCommonJS(index_exports);
|
|
265
265
|
|
|
266
266
|
// src/snippet.ts
|
|
267
|
-
|
|
267
|
+
var cacheContent = null;
|
|
268
|
+
var cacheLines = [];
|
|
269
|
+
var cacheOffsets = [];
|
|
270
|
+
function lineIndex(content) {
|
|
271
|
+
if (content === cacheContent) return { lines: cacheLines, offsets: cacheOffsets };
|
|
268
272
|
const lines = content.split("\n");
|
|
273
|
+
const offsets = new Array(lines.length);
|
|
274
|
+
let off = 0;
|
|
275
|
+
for (let i = 0; i < lines.length; i++) {
|
|
276
|
+
offsets[i] = off;
|
|
277
|
+
off += lines[i].length + 1;
|
|
278
|
+
}
|
|
279
|
+
cacheContent = content;
|
|
280
|
+
cacheLines = lines;
|
|
281
|
+
cacheOffsets = offsets;
|
|
282
|
+
return { lines, offsets };
|
|
283
|
+
}
|
|
284
|
+
function lineNumberAt(content, index) {
|
|
285
|
+
const { offsets } = lineIndex(content);
|
|
286
|
+
let lo = 0;
|
|
287
|
+
let hi = offsets.length - 1;
|
|
288
|
+
let ans = 0;
|
|
289
|
+
while (lo <= hi) {
|
|
290
|
+
const mid = lo + hi >> 1;
|
|
291
|
+
if (offsets[mid] <= index) {
|
|
292
|
+
ans = mid;
|
|
293
|
+
lo = mid + 1;
|
|
294
|
+
} else {
|
|
295
|
+
hi = mid - 1;
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
return ans + 1;
|
|
299
|
+
}
|
|
300
|
+
function getSnippet(content, line, contextLines = 2) {
|
|
301
|
+
const { lines } = lineIndex(content);
|
|
269
302
|
const start = Math.max(0, line - 1 - contextLines);
|
|
270
303
|
const end = Math.min(lines.length, line + contextLines);
|
|
271
|
-
|
|
272
|
-
|
|
304
|
+
const out = [];
|
|
305
|
+
for (let i = start; i < end; i++) {
|
|
306
|
+
const lineNum = i + 1;
|
|
273
307
|
const marker = lineNum === line ? ">" : " ";
|
|
274
|
-
|
|
275
|
-
}
|
|
308
|
+
out.push(`${marker} ${lineNum.toString().padStart(4)} | ${lines[i]}`);
|
|
309
|
+
}
|
|
310
|
+
return out.join("\n");
|
|
276
311
|
}
|
|
277
312
|
|
|
278
313
|
// src/rule-impacts.ts
|
|
@@ -1080,7 +1115,7 @@ var SERVER_SIDE_PATH_RE = new RegExp(
|
|
|
1080
1115
|
].join("|"),
|
|
1081
1116
|
"i"
|
|
1082
1117
|
);
|
|
1083
|
-
var SERVER_SIDE_CONTENT_RE = /\b(?:req|request)\.(?:body|query|params|headers|cookies)\b|\b(?:app|router)\.(?:get|post|put|patch|delete|use|all)\s*\(|\bctx\.request\b|\bevent\.(?:body|queryStringParameters|pathParameters)\b|\(\s*req\s*,\s*res\b|(?:import|require)\b[^;\n]
|
|
1118
|
+
var SERVER_SIDE_CONTENT_RE = /\b(?:req|request)\.(?:body|query|params|headers|cookies)\b|\b(?:app|router)\.(?:get|post|put|patch|delete|use|all)\s*\(|\bctx\.request\b|\bevent\.(?:body|queryStringParameters|pathParameters)\b|\(\s*req\s*,\s*res\b|(?:import|require)\b[^;\n]{0,200}['"]express['"]|\bNextFunction\b/;
|
|
1084
1119
|
function isServerSideFile(filePath, content) {
|
|
1085
1120
|
if (isTestFile(filePath)) return false;
|
|
1086
1121
|
if (SERVER_SIDE_PATH_RE.test(filePath)) return true;
|
|
@@ -1127,13 +1162,23 @@ function filterSilenced(matches, content, ruleId) {
|
|
|
1127
1162
|
}
|
|
1128
1163
|
function findMatches(content, pattern, rule, filePath, fixTemplate) {
|
|
1129
1164
|
const matches = [];
|
|
1130
|
-
const lines = content.split("\n");
|
|
1131
1165
|
let m;
|
|
1132
1166
|
const re = new RegExp(pattern.source, pattern.flags.includes("g") ? pattern.flags : `${pattern.flags}g`);
|
|
1167
|
+
let scanned = 0;
|
|
1168
|
+
let lineNum = 1;
|
|
1169
|
+
let lastMatchIndex = -1;
|
|
1170
|
+
const MAX_MATCHES = 500;
|
|
1133
1171
|
while ((m = re.exec(content)) !== null) {
|
|
1172
|
+
if (m.index === lastMatchIndex && m[0].length === 0) {
|
|
1173
|
+
re.lastIndex++;
|
|
1174
|
+
continue;
|
|
1175
|
+
}
|
|
1176
|
+
lastMatchIndex = m.index;
|
|
1134
1177
|
if (isCommentLine(content, m.index)) continue;
|
|
1135
1178
|
if (isInsideFixMessage(content, m.index)) continue;
|
|
1136
|
-
|
|
1179
|
+
for (; scanned < m.index; scanned++) {
|
|
1180
|
+
if (content.charCodeAt(scanned) === 10) lineNum++;
|
|
1181
|
+
}
|
|
1137
1182
|
matches.push({
|
|
1138
1183
|
rule: rule.id,
|
|
1139
1184
|
title: rule.title,
|
|
@@ -1144,6 +1189,7 @@ function findMatches(content, pattern, rule, filePath, fixTemplate) {
|
|
|
1144
1189
|
snippet: getSnippet(content, lineNum),
|
|
1145
1190
|
fix: fixTemplate?.(m)
|
|
1146
1191
|
});
|
|
1192
|
+
if (matches.length >= MAX_MATCHES) break;
|
|
1147
1193
|
}
|
|
1148
1194
|
return matches;
|
|
1149
1195
|
}
|
|
@@ -1159,10 +1205,26 @@ function astMatch(content, filePath, line, rule, fix) {
|
|
|
1159
1205
|
fix
|
|
1160
1206
|
};
|
|
1161
1207
|
}
|
|
1208
|
+
var parseCacheContent = null;
|
|
1209
|
+
var parseCachePath = null;
|
|
1210
|
+
var parseCacheResult = null;
|
|
1211
|
+
var parseCacheValid = false;
|
|
1162
1212
|
function tryParse(content, filePath) {
|
|
1163
|
-
|
|
1164
|
-
|
|
1165
|
-
|
|
1213
|
+
if (parseCacheValid && content === parseCacheContent && filePath === parseCachePath) {
|
|
1214
|
+
return parseCacheResult;
|
|
1215
|
+
}
|
|
1216
|
+
let result = null;
|
|
1217
|
+
try {
|
|
1218
|
+
const parsed = parseFile(content, filePath);
|
|
1219
|
+
result = parsed ? { parsed, taint: buildTaintMap(parsed) } : null;
|
|
1220
|
+
} catch {
|
|
1221
|
+
result = null;
|
|
1222
|
+
}
|
|
1223
|
+
parseCacheResult = result;
|
|
1224
|
+
parseCacheContent = content;
|
|
1225
|
+
parseCachePath = filePath;
|
|
1226
|
+
parseCacheValid = true;
|
|
1227
|
+
return result;
|
|
1166
1228
|
}
|
|
1167
1229
|
var hardcodedSecrets = {
|
|
1168
1230
|
id: "VC001",
|
|
@@ -1530,7 +1592,7 @@ var xssVulnerability = {
|
|
|
1530
1592
|
const hasSanitizerBypass = /bypassSecurityTrust(?:Html|Script|Style|Url|ResourceUrl)\s*\(/.test(content);
|
|
1531
1593
|
if (/(?:sanitize|purify|escape|xss)/i.test(filePath)) return [];
|
|
1532
1594
|
if (!hasSanitizerBypass && /DOMPurify\.sanitize|sanitizeHtml|xss\(|escapeHtml/i.test(content)) return [];
|
|
1533
|
-
if (!hasSanitizerBypass && /(?:import|require)\s*\(
|
|
1595
|
+
if (!hasSanitizerBypass && /(?:import|require)\s*\(?[^\n]{0,200}(?:DOMPurify|dompurify|sanitize|sanitizer)/i.test(content)) return [];
|
|
1534
1596
|
const patterns = [
|
|
1535
1597
|
// React dangerouslySetInnerHTML
|
|
1536
1598
|
/dangerouslySetInnerHTML\s*=\s*\{\s*\{\s*__html\s*:/g,
|
|
@@ -1559,9 +1621,9 @@ var xssVulnerability = {
|
|
|
1559
1621
|
() => "Sanitize user input before rendering as HTML. Use a library like DOMPurify: DOMPurify.sanitize(userInput). If this site is intentional (JSON-LD structured data, a const boot script, a sandboxed preview), add an inline `// VC007-OK: <reason>` comment above the line to silence."
|
|
1560
1622
|
);
|
|
1561
1623
|
for (const m of raw) {
|
|
1562
|
-
const lineText = allLines[m.line - 1] || "";
|
|
1624
|
+
const lineText = (allLines[m.line - 1] || "").slice(0, 4e3);
|
|
1563
1625
|
if (/\.innerHTML\s*=\s*['"]/.test(lineText) && !/\$\{/.test(lineText)) continue;
|
|
1564
|
-
if (/\.innerHTML\s*=\s*['"][^'"]
|
|
1626
|
+
if (/\.innerHTML\s*=\s*['"][^'"]{0,2000}['"]\s*$/.test(lineText)) continue;
|
|
1565
1627
|
if (/dangerouslySetInnerHTML/.test(lineText)) {
|
|
1566
1628
|
const windowText = allLines.slice(m.line - 1, m.line + 2).join("\n");
|
|
1567
1629
|
if (/JSON\.stringify/i.test(windowText)) continue;
|
|
@@ -1812,7 +1874,7 @@ var evalUsage = {
|
|
|
1812
1874
|
/new\s+Function\s*\(\s*(?!["'`])/g
|
|
1813
1875
|
];
|
|
1814
1876
|
const hasEvalInString = /["'`]eval(?:-source-map|["'`])/i.test(content);
|
|
1815
|
-
if (hasEvalInString && !/\beval\s*\([^)]
|
|
1877
|
+
if (hasEvalInString && !/\beval\s*\([^)]{0,500}(?:req\.|body\.|input|params|user|data)/i.test(content)) return [];
|
|
1816
1878
|
const matches = [];
|
|
1817
1879
|
for (const p of patterns) {
|
|
1818
1880
|
const rawMatches = findMatches(
|
|
@@ -1853,7 +1915,7 @@ var unvalidatedRedirect = {
|
|
|
1853
1915
|
const isPureLiteral = /^["'`][^"'`]*["'`]$/.test(value);
|
|
1854
1916
|
if (!hasInterpolation && isPureLiteral) continue;
|
|
1855
1917
|
if (isInlineSilenced(content, m.index, "VC016")) continue;
|
|
1856
|
-
const line = content
|
|
1918
|
+
const line = lineNumberAt(content, m.index);
|
|
1857
1919
|
matches.push({
|
|
1858
1920
|
rule: "VC016",
|
|
1859
1921
|
title: unvalidatedRedirect.title,
|
|
@@ -2076,11 +2138,11 @@ var prototypePollution = {
|
|
|
2076
2138
|
];
|
|
2077
2139
|
const hasValidation = /schema|validate|sanitize|whitelist|allowedKeys|pick\(|Object\.freeze|zod|yup|joi|ajv/i.test(content);
|
|
2078
2140
|
if (!hasValidation) {
|
|
2079
|
-
const hasUnsafeMerge = /Object\.assign\s*\([^)]
|
|
2141
|
+
const hasUnsafeMerge = /Object\.assign\s*\([^)]{0,500}JSON\.parse|\.\.\.JSON\.parse|\{[^\n]{0,300}\.\.\.(?:stored|saved|cached|parsed|data)/i.test(content);
|
|
2080
2142
|
if (hasUnsafeMerge) {
|
|
2081
2143
|
matches.push(...findMatches(
|
|
2082
2144
|
content,
|
|
2083
|
-
/Object\.assign\s*\([^)]
|
|
2145
|
+
/Object\.assign\s*\([^)]{0,500}JSON\.parse|\.\.\.JSON\.parse/g,
|
|
2084
2146
|
prototypePollution,
|
|
2085
2147
|
filePath,
|
|
2086
2148
|
() => "Validate parsed data against an expected schema before merging into objects. Use Object.freeze(), a validation library (Zod, Yup), or manually check for __proto__ and constructor keys."
|
|
@@ -3626,7 +3688,7 @@ var dangerousInnerHTML = {
|
|
|
3626
3688
|
if (/^[A-Z][A-Z0-9_]+$/.test(value)) continue;
|
|
3627
3689
|
if (/^["'`][^$]*["'`]$/.test(value)) continue;
|
|
3628
3690
|
if (/JSON\.stringify/i.test(value)) continue;
|
|
3629
|
-
const lineNum = content
|
|
3691
|
+
const lineNum = lineNumberAt(content, m.index);
|
|
3630
3692
|
findings.push({
|
|
3631
3693
|
rule: "VC063",
|
|
3632
3694
|
title: dangerousInnerHTML.title,
|
|
@@ -4368,7 +4430,7 @@ var missingSRI = {
|
|
|
4368
4430
|
const re = new RegExp(scriptPattern.source, scriptPattern.flags);
|
|
4369
4431
|
while ((m = re.exec(content)) !== null) {
|
|
4370
4432
|
if (!m[0].includes("integrity")) {
|
|
4371
|
-
const lineNum = content
|
|
4433
|
+
const lineNum = lineNumberAt(content, m.index);
|
|
4372
4434
|
matches.push({
|
|
4373
4435
|
rule: "VC084",
|
|
4374
4436
|
title: missingSRI.title,
|
|
@@ -5239,7 +5301,7 @@ var s3BucketNoEncryption = {
|
|
|
5239
5301
|
const blockEnd = Math.min(m.index + 2e3, content.length);
|
|
5240
5302
|
const blockContent = content.substring(m.index, blockEnd);
|
|
5241
5303
|
if (!/server_side_encryption/.test(blockContent)) {
|
|
5242
|
-
const lineNum = content
|
|
5304
|
+
const lineNum = lineNumberAt(content, m.index);
|
|
5243
5305
|
matches.push({
|
|
5244
5306
|
rule: "VC107",
|
|
5245
5307
|
title: s3BucketNoEncryption.title,
|
|
@@ -5270,7 +5332,7 @@ var securityGroupAllInbound = {
|
|
|
5270
5332
|
while ((m = ingressPattern.exec(content)) !== null) {
|
|
5271
5333
|
if (isCommentLine(content, m.index)) continue;
|
|
5272
5334
|
if (/from_port\s*=\s*0/.test(m[0]) || !/from_port/.test(m[0])) {
|
|
5273
|
-
const lineNum = content
|
|
5335
|
+
const lineNum = lineNumberAt(content, m.index);
|
|
5274
5336
|
matches.push({
|
|
5275
5337
|
rule: "VC108",
|
|
5276
5338
|
title: securityGroupAllInbound.title,
|
|
@@ -5357,7 +5419,7 @@ var lambdaWithoutVPC = {
|
|
|
5357
5419
|
const blockEnd = Math.min(m.index + 2e3, content.length);
|
|
5358
5420
|
const blockContent = content.substring(m.index, blockEnd);
|
|
5359
5421
|
if (!/vpc_config\s*\{/.test(blockContent)) {
|
|
5360
|
-
const lineNum = content
|
|
5422
|
+
const lineNum = lineNumberAt(content, m.index);
|
|
5361
5423
|
matches.push({
|
|
5362
5424
|
rule: "VC111",
|
|
5363
5425
|
title: lambdaWithoutVPC.title,
|
|
@@ -5387,7 +5449,7 @@ var dockerLatestTag = {
|
|
|
5387
5449
|
while ((m = fromPattern.exec(content)) !== null) {
|
|
5388
5450
|
const image = m[1];
|
|
5389
5451
|
if (image.endsWith(":latest") || !image.includes(":") && !image.startsWith("$")) {
|
|
5390
|
-
const lineNum = content
|
|
5452
|
+
const lineNum = lineNumberAt(content, m.index);
|
|
5391
5453
|
matches.push({
|
|
5392
5454
|
rule: "VC112",
|
|
5393
5455
|
title: dockerLatestTag.title,
|
|
@@ -5515,17 +5577,26 @@ var pathTraversal = {
|
|
|
5515
5577
|
const findings = [];
|
|
5516
5578
|
const patterns = [
|
|
5517
5579
|
/(?:readFile|readFileSync|createReadStream|writeFile|writeFileSync|appendFile|unlink|unlinkSync|access|stat|statSync|existsSync)\s*\(\s*(?:req\.|request\.|ctx\.|params\.|query\.)/gi,
|
|
5518
|
-
/(?:path\.join|path\.resolve)\s*\([^)]
|
|
5519
|
-
/(?:res\.sendFile|res\.download)\s*\([^)]
|
|
5580
|
+
/(?:path\.join|path\.resolve)\s*\([^)]{0,500}(?:req\.|request\.|ctx\.|params\.|query\.)[^)]{0,500}\)/gi,
|
|
5581
|
+
/(?:res\.sendFile|res\.download)\s*\([^)]{0,500}(?:req\.|request\.)/gi,
|
|
5520
5582
|
/open\s*\(\s*(?:request\.|params\[|args\.)/gi
|
|
5521
5583
|
];
|
|
5584
|
+
const MAX_MATCHES = 500;
|
|
5522
5585
|
for (const pat of patterns) {
|
|
5586
|
+
if (findings.length >= MAX_MATCHES) break;
|
|
5523
5587
|
let m;
|
|
5588
|
+
let lastIdx = -1;
|
|
5524
5589
|
while ((m = pat.exec(content)) !== null) {
|
|
5590
|
+
if (findings.length >= MAX_MATCHES) break;
|
|
5591
|
+
if (m.index === lastIdx && m[0].length === 0) {
|
|
5592
|
+
pat.lastIndex++;
|
|
5593
|
+
continue;
|
|
5594
|
+
}
|
|
5595
|
+
lastIdx = m.index;
|
|
5525
5596
|
if (isCommentLine(content, m.index)) continue;
|
|
5526
5597
|
const surrounding = content.substring(Math.max(0, m.index - 200), m.index + 200);
|
|
5527
5598
|
if (/path\.normalize|sanitize|\.replace\(\s*['"]\.\.|\.startsWith\s*\(|realpath/i.test(surrounding)) continue;
|
|
5528
|
-
const lineNum = content
|
|
5599
|
+
const lineNum = lineNumberAt(content, m.index);
|
|
5529
5600
|
findings.push({
|
|
5530
5601
|
rule: "VC117",
|
|
5531
5602
|
title: pathTraversal.title,
|
|
@@ -5556,7 +5627,7 @@ var piiInLogs = {
|
|
|
5556
5627
|
while ((m = logPattern.exec(content)) !== null) {
|
|
5557
5628
|
if (isCommentLine(content, m.index)) continue;
|
|
5558
5629
|
if (isInsideFixMessage(content, m.index)) continue;
|
|
5559
|
-
const lineNum = content
|
|
5630
|
+
const lineNum = lineNumberAt(content, m.index);
|
|
5560
5631
|
findings.push({
|
|
5561
5632
|
rule: "VC118",
|
|
5562
5633
|
title: piiInLogs.title,
|
|
@@ -5594,7 +5665,7 @@ var hardcodedOAuthSecret = {
|
|
|
5594
5665
|
while ((m = pat.exec(content)) !== null) {
|
|
5595
5666
|
if (isCommentLine(content, m.index)) continue;
|
|
5596
5667
|
if (/process\.env|os\.environ|ENV\[|getenv|\$\{|<your|CHANGE_ME|xxx|placeholder/i.test(m[0])) continue;
|
|
5597
|
-
const lineNum = content
|
|
5668
|
+
const lineNum = lineNumberAt(content, m.index);
|
|
5598
5669
|
findings.push({
|
|
5599
5670
|
rule: "VC119",
|
|
5600
5671
|
title: hardcodedOAuthSecret.title,
|
|
@@ -5626,7 +5697,7 @@ var missingOAuthState = {
|
|
|
5626
5697
|
if (isCommentLine(content, m.index)) continue;
|
|
5627
5698
|
const surrounding = content.substring(m.index, Math.min(content.length, m.index + 500));
|
|
5628
5699
|
if (/state\s*[=:]/i.test(surrounding)) continue;
|
|
5629
|
-
const lineNum = content
|
|
5700
|
+
const lineNum = lineNumberAt(content, m.index);
|
|
5630
5701
|
findings.push({
|
|
5631
5702
|
rule: "VC120",
|
|
5632
5703
|
title: missingOAuthState.title,
|
|
@@ -5654,7 +5725,7 @@ var unpinnedGitHubAction = {
|
|
|
5654
5725
|
let m;
|
|
5655
5726
|
while ((m = usesPattern.exec(content)) !== null) {
|
|
5656
5727
|
if (isCommentLine(content, m.index)) continue;
|
|
5657
|
-
const lineNum = content
|
|
5728
|
+
const lineNum = lineNumberAt(content, m.index);
|
|
5658
5729
|
findings.push({
|
|
5659
5730
|
rule: "VC121",
|
|
5660
5731
|
title: unpinnedGitHubAction.title,
|
|
@@ -5688,7 +5759,7 @@ var deprecatedTLS = {
|
|
|
5688
5759
|
let m;
|
|
5689
5760
|
while ((m = pat.exec(content)) !== null) {
|
|
5690
5761
|
if (isCommentLine(content, m.index)) continue;
|
|
5691
|
-
const lineNum = content
|
|
5762
|
+
const lineNum = lineNumberAt(content, m.index);
|
|
5692
5763
|
findings.push({
|
|
5693
5764
|
rule: "VC122",
|
|
5694
5765
|
title: deprecatedTLS.title,
|
|
@@ -5724,7 +5795,7 @@ var weakRSAKeySize = {
|
|
|
5724
5795
|
let m;
|
|
5725
5796
|
while ((m = pat.exec(content)) !== null) {
|
|
5726
5797
|
if (isCommentLine(content, m.index)) continue;
|
|
5727
|
-
const lineNum = content
|
|
5798
|
+
const lineNum = lineNumberAt(content, m.index);
|
|
5728
5799
|
findings.push({
|
|
5729
5800
|
rule: "VC123",
|
|
5730
5801
|
title: weakRSAKeySize.title,
|
|
@@ -5760,7 +5831,7 @@ var ecbModeEncryption = {
|
|
|
5760
5831
|
let m;
|
|
5761
5832
|
while ((m = pat.exec(content)) !== null) {
|
|
5762
5833
|
if (isCommentLine(content, m.index)) continue;
|
|
5763
|
-
const lineNum = content
|
|
5834
|
+
const lineNum = lineNumberAt(content, m.index);
|
|
5764
5835
|
findings.push({
|
|
5765
5836
|
rule: "VC124",
|
|
5766
5837
|
title: ecbModeEncryption.title,
|
|
@@ -5791,7 +5862,7 @@ var insecurePasswordReset = {
|
|
|
5791
5862
|
let m;
|
|
5792
5863
|
while ((m = weakTokens.exec(content)) !== null) {
|
|
5793
5864
|
if (isCommentLine(content, m.index)) continue;
|
|
5794
|
-
const lineNum = content
|
|
5865
|
+
const lineNum = lineNumberAt(content, m.index);
|
|
5795
5866
|
findings.push({
|
|
5796
5867
|
rule: "VC125",
|
|
5797
5868
|
title: insecurePasswordReset.title,
|
|
@@ -5808,7 +5879,7 @@ var insecurePasswordReset = {
|
|
|
5808
5879
|
if (isCommentLine(content, m.index)) continue;
|
|
5809
5880
|
const surrounding = content.substring(Math.max(0, m.index - 500), m.index);
|
|
5810
5881
|
if (!/(?:reset|forgot).*password/i.test(surrounding)) continue;
|
|
5811
|
-
const lineNum = content
|
|
5882
|
+
const lineNum = lineNumberAt(content, m.index);
|
|
5812
5883
|
findings.push({
|
|
5813
5884
|
rule: "VC125",
|
|
5814
5885
|
title: insecurePasswordReset.title,
|
|
@@ -5869,7 +5940,7 @@ var insecureHTTPMethods = {
|
|
|
5869
5940
|
if (isCommentLine(content, m.index)) continue;
|
|
5870
5941
|
const handlerBlock = content.substring(m.index, Math.min(content.length, m.index + 500));
|
|
5871
5942
|
if (/auth|authenticate|authorize|requireAuth|isAuthenticated|protect|guard|middleware|session|jwt|verify|clerk|getAuth/i.test(handlerBlock)) continue;
|
|
5872
|
-
const lineNum = content
|
|
5943
|
+
const lineNum = lineNumberAt(content, m.index);
|
|
5873
5944
|
findings.push({
|
|
5874
5945
|
rule: "VC127",
|
|
5875
5946
|
title: insecureHTTPMethods.title,
|
|
@@ -5903,7 +5974,7 @@ var httpRequestSmuggling = {
|
|
|
5903
5974
|
let m;
|
|
5904
5975
|
while ((m = pat.exec(content)) !== null) {
|
|
5905
5976
|
if (isCommentLine(content, m.index)) continue;
|
|
5906
|
-
const lineNum = content
|
|
5977
|
+
const lineNum = lineNumberAt(content, m.index);
|
|
5907
5978
|
findings.push({
|
|
5908
5979
|
rule: "VC128",
|
|
5909
5980
|
title: httpRequestSmuggling.title,
|
|
@@ -5937,7 +6008,7 @@ var unencryptedPII = {
|
|
|
5937
6008
|
let m;
|
|
5938
6009
|
while ((m = pat.exec(content)) !== null) {
|
|
5939
6010
|
if (isCommentLine(content, m.index)) continue;
|
|
5940
|
-
const lineNum = content
|
|
6011
|
+
const lineNum = lineNumberAt(content, m.index);
|
|
5941
6012
|
findings.push({
|
|
5942
6013
|
rule: "VC129",
|
|
5943
6014
|
title: unencryptedPII.title,
|
|
@@ -5969,7 +6040,7 @@ var missingAuthRateLimit = {
|
|
|
5969
6040
|
if (isCommentLine(content, m.index)) continue;
|
|
5970
6041
|
const surrounding = content.substring(Math.max(0, m.index - 300), Math.min(content.length, m.index + 300));
|
|
5971
6042
|
if (/rateLimit|rateLimiter|throttle|slowDown|express-rate-limit|rate_limit|RateLimiter|limiter/i.test(surrounding)) continue;
|
|
5972
|
-
const lineNum = content
|
|
6043
|
+
const lineNum = lineNumberAt(content, m.index);
|
|
5973
6044
|
findings.push({
|
|
5974
6045
|
rule: "VC130",
|
|
5975
6046
|
title: missingAuthRateLimit.title,
|
|
@@ -6007,7 +6078,7 @@ var vulnerableDependencies = {
|
|
|
6007
6078
|
for (const [pat, message] of vulnerablePackages) {
|
|
6008
6079
|
let m;
|
|
6009
6080
|
while ((m = pat.exec(content)) !== null) {
|
|
6010
|
-
const lineNum = content
|
|
6081
|
+
const lineNum = lineNumberAt(content, m.index);
|
|
6011
6082
|
findings.push({
|
|
6012
6083
|
rule: "VC131",
|
|
6013
6084
|
title: vulnerableDependencies.title,
|
|
@@ -6039,7 +6110,7 @@ function secretRuleCheck(content, filePath, pattern, ruleId, title, severity, fi
|
|
|
6039
6110
|
const lineStart = content.lastIndexOf("\n", m.index - 1) + 1;
|
|
6040
6111
|
const lineText = content.substring(lineStart, content.indexOf("\n", m.index));
|
|
6041
6112
|
if (PLACEHOLDER_RE.test(lineText)) continue;
|
|
6042
|
-
const lineNum = content
|
|
6113
|
+
const lineNum = lineNumberAt(content, m.index);
|
|
6043
6114
|
findings.push({
|
|
6044
6115
|
rule: ruleId,
|
|
6045
6116
|
title,
|
|
@@ -6140,7 +6211,7 @@ var hardcodedGCPServiceAccount = {
|
|
|
6140
6211
|
const findings = [];
|
|
6141
6212
|
const m = content.match(/"type"\s*:\s*"service_account"/);
|
|
6142
6213
|
if (m && m.index !== void 0) {
|
|
6143
|
-
const lineNum = content
|
|
6214
|
+
const lineNum = lineNumberAt(content, m.index);
|
|
6144
6215
|
findings.push({
|
|
6145
6216
|
rule: "VC136",
|
|
6146
6217
|
title: this.title,
|
|
@@ -6246,7 +6317,7 @@ var hardcodedDatadogKey = {
|
|
|
6246
6317
|
const lineStart = content.lastIndexOf("\n", m.index - 1) + 1;
|
|
6247
6318
|
const lineText = content.substring(lineStart, content.indexOf("\n", m.index));
|
|
6248
6319
|
if (PLACEHOLDER_RE.test(lineText)) continue;
|
|
6249
|
-
const lineNum = content
|
|
6320
|
+
const lineNum = lineNumberAt(content, m.index);
|
|
6250
6321
|
findings.push({
|
|
6251
6322
|
rule: "VC141",
|
|
6252
6323
|
title: this.title,
|
|
@@ -6280,7 +6351,7 @@ var hardcodedVercelToken = {
|
|
|
6280
6351
|
const lineStart = content.lastIndexOf("\n", m.index - 1) + 1;
|
|
6281
6352
|
const lineText = content.substring(lineStart, content.indexOf("\n", m.index));
|
|
6282
6353
|
if (PLACEHOLDER_RE.test(lineText)) continue;
|
|
6283
|
-
const lineNum = content
|
|
6354
|
+
const lineNum = lineNumberAt(content, m.index);
|
|
6284
6355
|
findings.push({
|
|
6285
6356
|
rule: "VC142",
|
|
6286
6357
|
title: this.title,
|
|
@@ -6314,7 +6385,7 @@ var hardcodedSupabaseServiceRole = {
|
|
|
6314
6385
|
const lineStart = content.lastIndexOf("\n", m.index - 1) + 1;
|
|
6315
6386
|
const lineText = content.substring(lineStart, content.indexOf("\n", m.index));
|
|
6316
6387
|
if (PLACEHOLDER_RE.test(lineText)) continue;
|
|
6317
|
-
const lineNum = content
|
|
6388
|
+
const lineNum = lineNumberAt(content, m.index);
|
|
6318
6389
|
findings.push({
|
|
6319
6390
|
rule: "VC143",
|
|
6320
6391
|
title: this.title,
|
|
@@ -6378,7 +6449,7 @@ function contextSecretRuleCheck(content, filePath, pattern, ruleId, title, sever
|
|
|
6378
6449
|
const lineStart = content.lastIndexOf("\n", m.index - 1) + 1;
|
|
6379
6450
|
const lineText = content.substring(lineStart, content.indexOf("\n", m.index));
|
|
6380
6451
|
if (PLACEHOLDER_RE.test(lineText)) continue;
|
|
6381
|
-
const lineNum = content
|
|
6452
|
+
const lineNum = lineNumberAt(content, m.index);
|
|
6382
6453
|
findings.push({
|
|
6383
6454
|
rule: ruleId,
|
|
6384
6455
|
title,
|
|
@@ -6858,7 +6929,7 @@ var ghaPullRequestTargetCheckout = {
|
|
|
6858
6929
|
const checkoutPattern = /uses\s*:\s*actions\/checkout@[^\n]*[\s\S]{0,400}?ref\s*:\s*\$\{\{\s*github\.event\.pull_request\.head\.(?:ref|sha)\s*\}\}/g;
|
|
6859
6930
|
let m;
|
|
6860
6931
|
while ((m = checkoutPattern.exec(content)) !== null) {
|
|
6861
|
-
const lineNum = content
|
|
6932
|
+
const lineNum = lineNumberAt(content, m.index);
|
|
6862
6933
|
findings.push({
|
|
6863
6934
|
rule: "VC184",
|
|
6864
6935
|
title: ghaPullRequestTargetCheckout.title,
|
|
@@ -6942,7 +7013,7 @@ var ghaThirdPartyActionWithSecrets = {
|
|
|
6942
7013
|
const pattern = /uses\s*:\s*(?!actions\/|github\/|aws-actions\/|azure\/|google-github-actions\/|hashicorp\/)([\w\-]+\/[\w\-./]+)@[^\n]*\n[\s\S]{0,800}?with\s*:[\s\S]{0,800}?\$\{\{\s*secrets\./g;
|
|
6943
7014
|
let m;
|
|
6944
7015
|
while ((m = pattern.exec(content)) !== null) {
|
|
6945
|
-
const lineNum = content
|
|
7016
|
+
const lineNum = lineNumberAt(content, m.index);
|
|
6946
7017
|
findings.push({
|
|
6947
7018
|
rule: "VC187",
|
|
6948
7019
|
title: ghaThirdPartyActionWithSecrets.title,
|
|
@@ -7107,7 +7178,7 @@ var pyRequestsVerifyFalse = {
|
|
|
7107
7178
|
let m;
|
|
7108
7179
|
while ((m = pattern.exec(content)) !== null) {
|
|
7109
7180
|
if (isCommentLine(content, m.index)) continue;
|
|
7110
|
-
const lineNum = content
|
|
7181
|
+
const lineNum = lineNumberAt(content, m.index);
|
|
7111
7182
|
findings.push({
|
|
7112
7183
|
rule: "VC191",
|
|
7113
7184
|
title: pyRequestsVerifyFalse.title,
|
|
@@ -7138,7 +7209,7 @@ var pyJinja2AutoescapeOff = {
|
|
|
7138
7209
|
let m;
|
|
7139
7210
|
while ((m = pattern.exec(content)) !== null) {
|
|
7140
7211
|
if (isCommentLine(content, m.index)) continue;
|
|
7141
|
-
const lineNum = content
|
|
7212
|
+
const lineNum = lineNumberAt(content, m.index);
|
|
7142
7213
|
findings.push({
|
|
7143
7214
|
rule: "VC192",
|
|
7144
7215
|
title: pyJinja2AutoescapeOff.title,
|
|
@@ -7167,7 +7238,7 @@ var pyTempfileMktemp = {
|
|
|
7167
7238
|
let m;
|
|
7168
7239
|
while ((m = pattern.exec(content)) !== null) {
|
|
7169
7240
|
if (isCommentLine(content, m.index)) continue;
|
|
7170
|
-
const lineNum = content
|
|
7241
|
+
const lineNum = lineNumberAt(content, m.index);
|
|
7171
7242
|
findings.push({
|
|
7172
7243
|
rule: "VC193",
|
|
7173
7244
|
title: pyTempfileMktemp.title,
|
|
@@ -7209,7 +7280,7 @@ var pyDjangoMarkSafe = {
|
|
|
7209
7280
|
let m;
|
|
7210
7281
|
while ((m = pattern.exec(content)) !== null) {
|
|
7211
7282
|
if (isCommentLine(content, m.index)) continue;
|
|
7212
|
-
const lineNum = content
|
|
7283
|
+
const lineNum = lineNumberAt(content, m.index);
|
|
7213
7284
|
if (seenLines.has(lineNum)) continue;
|
|
7214
7285
|
seenLines.add(lineNum);
|
|
7215
7286
|
findings.push({
|
|
@@ -7246,7 +7317,7 @@ var pyParamikoAutoAdd = {
|
|
|
7246
7317
|
let m;
|
|
7247
7318
|
while ((m = pattern.exec(content)) !== null) {
|
|
7248
7319
|
if (isCommentLine(content, m.index)) continue;
|
|
7249
|
-
const lineNum = content
|
|
7320
|
+
const lineNum = lineNumberAt(content, m.index);
|
|
7250
7321
|
if (seenLines.has(lineNum)) continue;
|
|
7251
7322
|
seenLines.add(lineNum);
|
|
7252
7323
|
findings.push({
|
|
@@ -7279,7 +7350,7 @@ var pyDjangoAllowedHostsWildcard = {
|
|
|
7279
7350
|
let m;
|
|
7280
7351
|
while ((m = pattern.exec(content)) !== null) {
|
|
7281
7352
|
if (isCommentLine(content, m.index)) continue;
|
|
7282
|
-
const lineNum = content
|
|
7353
|
+
const lineNum = lineNumberAt(content, m.index);
|
|
7283
7354
|
findings.push({
|
|
7284
7355
|
rule: "VC196",
|
|
7285
7356
|
title: pyDjangoAllowedHostsWildcard.title,
|
|
@@ -7317,7 +7388,7 @@ var pyJWTDecodeWeakConfig = {
|
|
|
7317
7388
|
if (isCommentLine(content, m.index)) continue;
|
|
7318
7389
|
const args = m[1];
|
|
7319
7390
|
if (/\balgorithms\s*=/.test(args)) continue;
|
|
7320
|
-
const lineNum = content
|
|
7391
|
+
const lineNum = lineNumberAt(content, m.index);
|
|
7321
7392
|
if (seenLines.has(lineNum)) continue;
|
|
7322
7393
|
seenLines.add(lineNum);
|
|
7323
7394
|
findings.push({
|
|
@@ -7368,7 +7439,7 @@ var llmPromptInjection = {
|
|
|
7368
7439
|
let m;
|
|
7369
7440
|
while ((m = pattern.exec(content)) !== null) {
|
|
7370
7441
|
if (isCommentLine(content, m.index)) continue;
|
|
7371
|
-
const lineNum = content
|
|
7442
|
+
const lineNum = lineNumberAt(content, m.index);
|
|
7372
7443
|
if (seenLines.has(lineNum)) continue;
|
|
7373
7444
|
seenLines.add(lineNum);
|
|
7374
7445
|
findings.push({
|
|
@@ -7407,7 +7478,7 @@ var llmSystemPromptInjection = {
|
|
|
7407
7478
|
let m;
|
|
7408
7479
|
while ((m = pattern.exec(content)) !== null) {
|
|
7409
7480
|
if (isCommentLine(content, m.index)) continue;
|
|
7410
|
-
const lineNum = content
|
|
7481
|
+
const lineNum = lineNumberAt(content, m.index);
|
|
7411
7482
|
findings.push({
|
|
7412
7483
|
rule: "VC199",
|
|
7413
7484
|
title: llmSystemPromptInjection.title,
|
|
@@ -7449,7 +7520,7 @@ var llmOutputAsHTML = {
|
|
|
7449
7520
|
let m;
|
|
7450
7521
|
while ((m = pattern.exec(content)) !== null) {
|
|
7451
7522
|
if (isCommentLine(content, m.index)) continue;
|
|
7452
|
-
const lineNum = content
|
|
7523
|
+
const lineNum = lineNumberAt(content, m.index);
|
|
7453
7524
|
if (seenLines.has(lineNum)) continue;
|
|
7454
7525
|
seenLines.add(lineNum);
|
|
7455
7526
|
findings.push({
|
|
@@ -7486,7 +7557,7 @@ var vectorStoreQueryNoUserFilter = {
|
|
|
7486
7557
|
if (/\b(?:user[_-]?id|userId|tenant[_-]?id|tenantId|org[_-]?id|orgId|owner[_-]?id|ownerId|account[_-]?id|customer[_-]?id|workspace[_-]?id)\b/i.test(args)) continue;
|
|
7487
7558
|
if (/\bnamespace\s*[:=]\s*[`"']?[^,)`"']*(?:user|tenant|org|owner|account|customer|workspace)/i.test(args)) continue;
|
|
7488
7559
|
if (/\bfilter\s*[:=][\s\S]*?(?:\buser|\btenant|\borg|\bowner|\baccount|\bcustomer|\bworkspace)/i.test(args)) continue;
|
|
7489
|
-
const lineNum = content
|
|
7560
|
+
const lineNum = lineNumberAt(content, m.index);
|
|
7490
7561
|
findings.push({
|
|
7491
7562
|
rule: "VC201",
|
|
7492
7563
|
title: vectorStoreQueryNoUserFilter.title,
|
|
@@ -7523,7 +7594,7 @@ var vectorStoreUpsertNoMetadata = {
|
|
|
7523
7594
|
if (/\bnamespace\s*[:=]\s*[`"']?[^,)`"']*(?:user|tenant|org)/i.test(args)) {
|
|
7524
7595
|
continue;
|
|
7525
7596
|
}
|
|
7526
|
-
const lineNum = content
|
|
7597
|
+
const lineNum = lineNumberAt(content, m.index);
|
|
7527
7598
|
findings.push({
|
|
7528
7599
|
rule: "VC202",
|
|
7529
7600
|
title: vectorStoreUpsertNoMetadata.title,
|
|
@@ -7577,7 +7648,7 @@ var llmCallNoMaxTokens = {
|
|
|
7577
7648
|
if (depth !== 0) continue;
|
|
7578
7649
|
const args = content.substring(openIdx + 1, i).replace(/\/\*[\s\S]*?\*\//g, "").replace(/\/\/[^\n]*/g, "").replace(/#[^\n]*/g, "");
|
|
7579
7650
|
if (TOKEN_KW_RE.test(args)) continue;
|
|
7580
|
-
const lineNum = content
|
|
7651
|
+
const lineNum = lineNumberAt(content, m.index);
|
|
7581
7652
|
findings.push({
|
|
7582
7653
|
rule: "VC203",
|
|
7583
7654
|
title: llmCallNoMaxTokens.title,
|
|
@@ -7617,7 +7688,7 @@ var graphqlNoDepthLimit = {
|
|
|
7617
7688
|
let m;
|
|
7618
7689
|
while ((m = anchorRe.exec(content)) !== null) {
|
|
7619
7690
|
if (isCommentLine(content, m.index)) continue;
|
|
7620
|
-
const lineNum = content
|
|
7691
|
+
const lineNum = lineNumberAt(content, m.index);
|
|
7621
7692
|
findings.push({
|
|
7622
7693
|
rule: "VC204",
|
|
7623
7694
|
title: graphqlNoDepthLimit.title,
|
|
@@ -7651,7 +7722,7 @@ var graphqlNoComplexityLimit = {
|
|
|
7651
7722
|
let m;
|
|
7652
7723
|
while ((m = anchorRe.exec(content)) !== null) {
|
|
7653
7724
|
if (isCommentLine(content, m.index)) continue;
|
|
7654
|
-
const lineNum = content
|
|
7725
|
+
const lineNum = lineNumberAt(content, m.index);
|
|
7655
7726
|
findings.push({
|
|
7656
7727
|
rule: "VC205",
|
|
7657
7728
|
title: graphqlNoComplexityLimit.title,
|
|
@@ -7681,7 +7752,7 @@ var graphqlCSRFDisabled = {
|
|
|
7681
7752
|
let m;
|
|
7682
7753
|
while ((m = pattern.exec(content)) !== null) {
|
|
7683
7754
|
if (isCommentLine(content, m.index)) continue;
|
|
7684
|
-
const lineNum = content
|
|
7755
|
+
const lineNum = lineNumberAt(content, m.index);
|
|
7685
7756
|
findings.push({
|
|
7686
7757
|
rule: "VC206",
|
|
7687
7758
|
title: graphqlCSRFDisabled.title,
|
|
@@ -7777,7 +7848,7 @@ var webhookMissingIdempotency = {
|
|
|
7777
7848
|
if (/idempoten|event\.id|evt\.id|delivery_?id|alreadyProcessed|processedEvents|ON\s+CONFLICT|INSERT\s+OR\s+IGNORE|\bseen\b|\bprocessed\b/i.test(content)) return [];
|
|
7778
7849
|
const m = content.match(/constructEvent|new\s+Webhook\s*\(|verifyHeader/i);
|
|
7779
7850
|
if (!m || m.index === void 0) return [];
|
|
7780
|
-
const line = content
|
|
7851
|
+
const line = lineNumberAt(content, m.index);
|
|
7781
7852
|
return filterSilenced([{
|
|
7782
7853
|
rule: "VC209",
|
|
7783
7854
|
title: webhookMissingIdempotency.title,
|
|
@@ -7804,7 +7875,7 @@ var middlewareMatcherExcludesApi = {
|
|
|
7804
7875
|
if (!/\(\?!\s*[^)]*\bapi\b/.test(content)) return [];
|
|
7805
7876
|
const m = content.match(/matcher\s*:/);
|
|
7806
7877
|
if (!m || m.index === void 0) return [];
|
|
7807
|
-
const line = content
|
|
7878
|
+
const line = lineNumberAt(content, m.index);
|
|
7808
7879
|
return filterSilenced([{
|
|
7809
7880
|
rule: "VC210",
|
|
7810
7881
|
title: middlewareMatcherExcludesApi.title,
|
|
@@ -7842,7 +7913,7 @@ var secretInURLParam = {
|
|
|
7842
7913
|
if (isCommentLine(content, m.index)) continue;
|
|
7843
7914
|
if (isInsideFixMessage(content, m.index)) continue;
|
|
7844
7915
|
if (isInlineSilenced(content, m.index, "VC146")) continue;
|
|
7845
|
-
const lineNum = content
|
|
7916
|
+
const lineNum = lineNumberAt(content, m.index);
|
|
7846
7917
|
findings.push({
|
|
7847
7918
|
rule: "VC146",
|
|
7848
7919
|
title: this.title,
|
|
@@ -7873,7 +7944,7 @@ var secretLoggedToConsole = {
|
|
|
7873
7944
|
while ((m = pattern.exec(content)) !== null) {
|
|
7874
7945
|
if (isCommentLine(content, m.index)) continue;
|
|
7875
7946
|
if (isInsideFixMessage(content, m.index)) continue;
|
|
7876
|
-
const lineNum = content
|
|
7947
|
+
const lineNum = lineNumberAt(content, m.index);
|
|
7877
7948
|
findings.push({
|
|
7878
7949
|
rule: "VC147",
|
|
7879
7950
|
title: this.title,
|
|
@@ -7903,7 +7974,7 @@ var secretInErrorResponse = {
|
|
|
7903
7974
|
while ((m = pattern.exec(content)) !== null) {
|
|
7904
7975
|
if (isCommentLine(content, m.index)) continue;
|
|
7905
7976
|
if (isInsideFixMessage(content, m.index)) continue;
|
|
7906
|
-
const lineNum = content
|
|
7977
|
+
const lineNum = lineNumberAt(content, m.index);
|
|
7907
7978
|
findings.push({
|
|
7908
7979
|
rule: "VC148",
|
|
7909
7980
|
title: this.title,
|
|
@@ -7942,7 +8013,7 @@ var secretInBundleConfig = {
|
|
|
7942
8013
|
while ((m = re.exec(content)) !== null) {
|
|
7943
8014
|
if (isCommentLine(content, m.index)) continue;
|
|
7944
8015
|
if (isInsideFixMessage(content, m.index)) continue;
|
|
7945
|
-
const lineNum = content
|
|
8016
|
+
const lineNum = lineNumberAt(content, m.index);
|
|
7946
8017
|
findings.push({
|
|
7947
8018
|
rule: "VC149",
|
|
7948
8019
|
title: this.title,
|
|
@@ -7980,7 +8051,7 @@ var secretInHTMLAttribute = {
|
|
|
7980
8051
|
while ((m = re.exec(content)) !== null) {
|
|
7981
8052
|
if (isCommentLine(content, m.index)) continue;
|
|
7982
8053
|
if (isInsideFixMessage(content, m.index)) continue;
|
|
7983
|
-
const lineNum = content
|
|
8054
|
+
const lineNum = lineNumberAt(content, m.index);
|
|
7984
8055
|
findings.push({
|
|
7985
8056
|
rule: "VC150",
|
|
7986
8057
|
title: this.title,
|
|
@@ -8007,9 +8078,9 @@ var secretInCLIArgument = {
|
|
|
8007
8078
|
if (!filePath.match(/\.(js|ts|jsx|tsx|py|rb)$/)) return [];
|
|
8008
8079
|
const findings = [];
|
|
8009
8080
|
const patterns = [
|
|
8010
|
-
/(?:exec|execSync|spawn|spawnSync|child_process)\s*\([^)]
|
|
8011
|
-
/(?:exec|execSync|spawn|spawnSync|child_process)\s*\([^)]
|
|
8012
|
-
/(?:subprocess|os\.system|os\.popen)\s*\([^)]
|
|
8081
|
+
/(?:exec|execSync|spawn|spawnSync|child_process)\s*\([^)]{0,500}\$\{[^}]{0,200}(?:api[_-]?key|secret|token|password|credentials)/gi,
|
|
8082
|
+
/(?:exec|execSync|spawn|spawnSync|child_process)\s*\([^)]{0,500}["']\s*\+\s*(?:api[_-]?key|secret|token|password|credentials)/gi,
|
|
8083
|
+
/(?:subprocess|os\.system|os\.popen)\s*\([^)]{0,500}\{[^}]{0,200}(?:api[_-]?key|secret|token|password|credentials)/gi
|
|
8013
8084
|
];
|
|
8014
8085
|
for (const p of patterns) {
|
|
8015
8086
|
let m;
|
|
@@ -8017,7 +8088,7 @@ var secretInCLIArgument = {
|
|
|
8017
8088
|
while ((m = re.exec(content)) !== null) {
|
|
8018
8089
|
if (isCommentLine(content, m.index)) continue;
|
|
8019
8090
|
if (isInsideFixMessage(content, m.index)) continue;
|
|
8020
|
-
const lineNum = content
|
|
8091
|
+
const lineNum = lineNumberAt(content, m.index);
|
|
8021
8092
|
findings.push({
|
|
8022
8093
|
rule: "VC151",
|
|
8023
8094
|
title: this.title,
|
|
@@ -8063,7 +8134,7 @@ var webhookSignatureVerification = {
|
|
|
8063
8134
|
if (svc.verify.test(content)) continue;
|
|
8064
8135
|
const m = content.match(/export\s+(?:async\s+)?function\s+POST/);
|
|
8065
8136
|
if (!m || m.index === void 0) continue;
|
|
8066
|
-
const lineNum = content
|
|
8137
|
+
const lineNum = lineNumberAt(content, m.index);
|
|
8067
8138
|
findings.push({
|
|
8068
8139
|
rule: "VC152",
|
|
8069
8140
|
title: `${svc.name} Webhook Missing Signature Verification`,
|
|
@@ -8130,7 +8201,7 @@ var missingRequestValidation = {
|
|
|
8130
8201
|
if (/if\s*\(\s*!(?:body|parsed|data|payload)\.|throw.*(?:missing|invalid|required)/i.test(content)) return [];
|
|
8131
8202
|
const m = content.match(/export\s+(?:async\s+)?function\s+(?:POST|PUT|PATCH)/);
|
|
8132
8203
|
if (!m || m.index === void 0) return [];
|
|
8133
|
-
const lineNum = content
|
|
8204
|
+
const lineNum = lineNumberAt(content, m.index);
|
|
8134
8205
|
return [{
|
|
8135
8206
|
rule: "VC154",
|
|
8136
8207
|
title: this.title,
|
|
@@ -8157,7 +8228,7 @@ var missingAIRateLimit = {
|
|
|
8157
8228
|
if (/rateLimit|rateLimiter|throttle|checkRateLimit|limiter|slowDown|express-rate-limit/i.test(content)) return [];
|
|
8158
8229
|
const m = content.match(/export\s+(?:async\s+)?function\s+(?:POST|GET)/i) || content.match(/\.(post|get)\s*\(/i);
|
|
8159
8230
|
if (!m || m.index === void 0) return [];
|
|
8160
|
-
const lineNum = content
|
|
8231
|
+
const lineNum = lineNumberAt(content, m.index);
|
|
8161
8232
|
return [{
|
|
8162
8233
|
rule: "VC155",
|
|
8163
8234
|
title: this.title,
|
|
@@ -8186,7 +8257,7 @@ var missingPagination = {
|
|
|
8186
8257
|
if (/findUnique|findFirst|findById|\.findOne|WHERE.*id\s*=/i.test(content)) return [];
|
|
8187
8258
|
const m = content.match(/export\s+(?:async\s+)?function\s+GET/);
|
|
8188
8259
|
if (!m || m.index === void 0) return [];
|
|
8189
|
-
const lineNum = content
|
|
8260
|
+
const lineNum = lineNumberAt(content, m.index);
|
|
8190
8261
|
return [{
|
|
8191
8262
|
rule: "VC156",
|
|
8192
8263
|
title: this.title,
|
|
@@ -8247,7 +8318,7 @@ var insecureDirectObjectReference = {
|
|
|
8247
8318
|
if (/requireUser|requireUserForApi|getServerSession|auth\(\)/i.test(content)) return [];
|
|
8248
8319
|
const m = content.match(/params\.id|params\.(?:slug|uuid)|req\.params\./);
|
|
8249
8320
|
if (!m || m.index === void 0) return [];
|
|
8250
|
-
const lineNum = content
|
|
8321
|
+
const lineNum = lineNumberAt(content, m.index);
|
|
8251
8322
|
return [{
|
|
8252
8323
|
rule: "VC158",
|
|
8253
8324
|
title: this.title,
|
|
@@ -8553,6 +8624,17 @@ function runCustomRules(content, filePath, disabledRules = [], tier = "free", ex
|
|
|
8553
8624
|
return findings;
|
|
8554
8625
|
}
|
|
8555
8626
|
if (/pro-rules-bundle|\.bundle\./i.test(filePath)) return findings;
|
|
8627
|
+
let maxLineLen = 0;
|
|
8628
|
+
{
|
|
8629
|
+
let lineStart = 0;
|
|
8630
|
+
for (let i = 0; i <= content.length; i++) {
|
|
8631
|
+
if (i === content.length || content.charCodeAt(i) === 10) {
|
|
8632
|
+
if (i - lineStart > maxLineLen) maxLineLen = i - lineStart;
|
|
8633
|
+
lineStart = i + 1;
|
|
8634
|
+
}
|
|
8635
|
+
}
|
|
8636
|
+
}
|
|
8637
|
+
if (maxLineLen > 5e4) return findings;
|
|
8556
8638
|
if (/onboarding|demo-data|example-vulnerable|code-sample/i.test(filePath)) return findings;
|
|
8557
8639
|
const ruleset = tier === "pro" && extraRules.length > 0 ? [...freeRules, ...extraRules] : freeRules;
|
|
8558
8640
|
for (const rule of ruleset) {
|