xploitscan-shared-rules 1.8.0 → 1.9.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/README.md +1 -1
- package/dist/index.cjs +118 -9
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +118 -9
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -329,8 +329,69 @@ var TAINTED_REQUEST_OBJECTS = /* @__PURE__ */ new Set([
|
|
|
329
329
|
"event"
|
|
330
330
|
// Lambda
|
|
331
331
|
]);
|
|
332
|
+
var FS_READ_METHODS = /* @__PURE__ */ new Set(["readFile", "readFileSync"]);
|
|
333
|
+
var PARSE_OBJECTS = /* @__PURE__ */ new Set([
|
|
334
|
+
"JSON",
|
|
335
|
+
"csv",
|
|
336
|
+
"papa",
|
|
337
|
+
"Papa",
|
|
338
|
+
"yaml",
|
|
339
|
+
"YAML",
|
|
340
|
+
"toml",
|
|
341
|
+
"qs",
|
|
342
|
+
"querystring"
|
|
343
|
+
]);
|
|
344
|
+
var PARSE_BARE = /* @__PURE__ */ new Set(["parse", "parseSync"]);
|
|
345
|
+
var ARRAY_ELEMENT_METHODS = /* @__PURE__ */ new Set([
|
|
346
|
+
"map",
|
|
347
|
+
"filter",
|
|
348
|
+
"forEach",
|
|
349
|
+
"find",
|
|
350
|
+
"findIndex",
|
|
351
|
+
"flatMap",
|
|
352
|
+
"some",
|
|
353
|
+
"every"
|
|
354
|
+
]);
|
|
355
|
+
var ARRAY_REDUCE_METHODS = /* @__PURE__ */ new Set(["reduce", "reduceRight"]);
|
|
356
|
+
function callPreservesTaint(node, rec) {
|
|
357
|
+
const callee = node.callee;
|
|
358
|
+
const anyArgTainted = () => node.arguments.some((a) => a.type !== "SpreadElement" && rec(a));
|
|
359
|
+
if (callee.type === "MemberExpression" && callee.property.type === "Identifier") {
|
|
360
|
+
const pname = callee.property.name;
|
|
361
|
+
if (FS_READ_METHODS.has(pname)) return anyArgTainted();
|
|
362
|
+
if (pname === "parse" || pname === "parseSync") {
|
|
363
|
+
const obj = callee.object;
|
|
364
|
+
if (obj.type === "Identifier" && PARSE_OBJECTS.has(obj.name)) return anyArgTainted();
|
|
365
|
+
}
|
|
366
|
+
}
|
|
367
|
+
if (callee.type === "Identifier") {
|
|
368
|
+
if (FS_READ_METHODS.has(callee.name)) return anyArgTainted();
|
|
369
|
+
if (PARSE_BARE.has(callee.name)) return anyArgTainted();
|
|
370
|
+
}
|
|
371
|
+
return false;
|
|
372
|
+
}
|
|
332
373
|
function buildTaintMap(parsed) {
|
|
333
374
|
const tainted = /* @__PURE__ */ new Set();
|
|
375
|
+
function markPatternTainted(target) {
|
|
376
|
+
if (target.type === "Identifier") {
|
|
377
|
+
tainted.add(target.name);
|
|
378
|
+
} else if (target.type === "ObjectPattern") {
|
|
379
|
+
for (const prop of target.properties) {
|
|
380
|
+
if (prop.type === "ObjectProperty" && prop.value.type === "Identifier") {
|
|
381
|
+
tainted.add(prop.value.name);
|
|
382
|
+
} else if (prop.type === "RestElement" && prop.argument.type === "Identifier") {
|
|
383
|
+
tainted.add(prop.argument.name);
|
|
384
|
+
}
|
|
385
|
+
}
|
|
386
|
+
} else if (target.type === "ArrayPattern") {
|
|
387
|
+
for (const el of target.elements) {
|
|
388
|
+
if (el && el.type === "Identifier") tainted.add(el.name);
|
|
389
|
+
else if (el && el.type === "RestElement" && el.argument.type === "Identifier") {
|
|
390
|
+
tainted.add(el.argument.name);
|
|
391
|
+
}
|
|
392
|
+
}
|
|
393
|
+
}
|
|
394
|
+
}
|
|
334
395
|
function reachesRequestIdent(node) {
|
|
335
396
|
if (!node) return false;
|
|
336
397
|
if (node.type === "Identifier") return TAINTED_REQUEST_OBJECTS.has(node.name);
|
|
@@ -401,6 +462,7 @@ function buildTaintMap(parsed) {
|
|
|
401
462
|
}
|
|
402
463
|
if (node.type === "CallExpression") {
|
|
403
464
|
if (nodeIsTaintedSource(node)) return true;
|
|
465
|
+
if (callPreservesTaint(node, exprIsTainted)) return true;
|
|
404
466
|
if (node.callee.type === "MemberExpression") {
|
|
405
467
|
if (exprIsTainted(node.callee.object)) return true;
|
|
406
468
|
const obj = node.callee.object;
|
|
@@ -421,6 +483,16 @@ function buildTaintMap(parsed) {
|
|
|
421
483
|
if (node.type === "AwaitExpression") {
|
|
422
484
|
return exprIsTainted(node.argument);
|
|
423
485
|
}
|
|
486
|
+
if (node.type === "ArrayExpression") {
|
|
487
|
+
return node.elements.some(
|
|
488
|
+
(el) => el != null && el.type === "SpreadElement" && exprIsTainted(el.argument)
|
|
489
|
+
);
|
|
490
|
+
}
|
|
491
|
+
if (node.type === "ObjectExpression") {
|
|
492
|
+
return node.properties.some(
|
|
493
|
+
(p) => p.type === "SpreadElement" && exprIsTainted(p.argument)
|
|
494
|
+
);
|
|
495
|
+
}
|
|
424
496
|
return false;
|
|
425
497
|
}
|
|
426
498
|
traverse(parsed.ast, {
|
|
@@ -455,6 +527,32 @@ function buildTaintMap(parsed) {
|
|
|
455
527
|
if (exprIsTainted(node.right)) {
|
|
456
528
|
tainted.add(node.left.name);
|
|
457
529
|
}
|
|
530
|
+
},
|
|
531
|
+
// for (const row of <tainted>) → row tainted (and destructured names).
|
|
532
|
+
ForOfStatement(path) {
|
|
533
|
+
const node = path.node;
|
|
534
|
+
if (node.type !== "ForOfStatement") return;
|
|
535
|
+
if (!exprIsTainted(node.right)) return;
|
|
536
|
+
const decl = node.left;
|
|
537
|
+
const target = decl.type === "VariableDeclaration" && decl.declarations[0] ? decl.declarations[0].id : decl;
|
|
538
|
+
markPatternTainted(target);
|
|
539
|
+
},
|
|
540
|
+
// Array-iteration element taint: <tainted>.map((row) => …) marks `row`
|
|
541
|
+
// tainted inside the callback. Gated on the receiver already being
|
|
542
|
+
// tainted, so this never originates taint.
|
|
543
|
+
CallExpression(path) {
|
|
544
|
+
const node = path.node;
|
|
545
|
+
if (node.type !== "CallExpression") return;
|
|
546
|
+
const callee = node.callee;
|
|
547
|
+
if (callee.type !== "MemberExpression" || callee.property.type !== "Identifier") return;
|
|
548
|
+
const method = callee.property.name;
|
|
549
|
+
const isReduce = ARRAY_REDUCE_METHODS.has(method);
|
|
550
|
+
if (!ARRAY_ELEMENT_METHODS.has(method) && !isReduce) return;
|
|
551
|
+
if (!exprIsTainted(callee.object)) return;
|
|
552
|
+
const cb = node.arguments[0];
|
|
553
|
+
if (!cb || cb.type !== "ArrowFunctionExpression" && cb.type !== "FunctionExpression") return;
|
|
554
|
+
const elemParam = cb.params[isReduce ? 1 : 0];
|
|
555
|
+
if (elemParam) markPatternTainted(elemParam);
|
|
458
556
|
}
|
|
459
557
|
});
|
|
460
558
|
const isTainted = (node) => {
|
|
@@ -480,6 +578,7 @@ function buildTaintMap(parsed) {
|
|
|
480
578
|
return isTainted(node.object);
|
|
481
579
|
}
|
|
482
580
|
if (node.type === "CallExpression") {
|
|
581
|
+
if (callPreservesTaint(node, isTainted)) return true;
|
|
483
582
|
if (node.callee.type === "MemberExpression") {
|
|
484
583
|
if (isTainted(node.callee.object)) return true;
|
|
485
584
|
const obj = node.callee.object;
|
|
@@ -497,6 +596,16 @@ function buildTaintMap(parsed) {
|
|
|
497
596
|
}
|
|
498
597
|
}
|
|
499
598
|
}
|
|
599
|
+
if (node.type === "ArrayExpression") {
|
|
600
|
+
return node.elements.some(
|
|
601
|
+
(el) => el != null && el.type === "SpreadElement" && isTainted(el.argument)
|
|
602
|
+
);
|
|
603
|
+
}
|
|
604
|
+
if (node.type === "ObjectExpression") {
|
|
605
|
+
return node.properties.some(
|
|
606
|
+
(p) => p.type === "SpreadElement" && isTainted(p.argument)
|
|
607
|
+
);
|
|
608
|
+
}
|
|
500
609
|
return false;
|
|
501
610
|
};
|
|
502
611
|
return {
|
|
@@ -3689,9 +3798,9 @@ var xxeVulnerability = {
|
|
|
3689
3798
|
));
|
|
3690
3799
|
}
|
|
3691
3800
|
}
|
|
3692
|
-
if (!/parseXml\s*\(/.test(content)) return matches;
|
|
3801
|
+
if (!/parseXml\s*\(/.test(content)) return filterSilenced(matches, content, "VC081");
|
|
3693
3802
|
const ctx = tryParse(content, filePath);
|
|
3694
|
-
if (!ctx) return matches;
|
|
3803
|
+
if (!ctx) return filterSilenced(matches, content, "VC081");
|
|
3695
3804
|
visitCalls(
|
|
3696
3805
|
ctx.parsed,
|
|
3697
3806
|
(callee) => isCalleeNamed(callee, "parseXml") || isCalleeNamed(callee, "parseXML"),
|
|
@@ -3715,7 +3824,7 @@ var xxeVulnerability = {
|
|
|
3715
3824
|
);
|
|
3716
3825
|
}
|
|
3717
3826
|
);
|
|
3718
|
-
return matches;
|
|
3827
|
+
return filterSilenced(matches, content, "VC081");
|
|
3719
3828
|
}
|
|
3720
3829
|
};
|
|
3721
3830
|
var ssti = {
|
|
@@ -3744,7 +3853,7 @@ var ssti = {
|
|
|
3744
3853
|
));
|
|
3745
3854
|
}
|
|
3746
3855
|
if (!/(?:\.compile|\.render|renderString|render_template_string)\s*\(/.test(content)) {
|
|
3747
|
-
return matches;
|
|
3856
|
+
return filterSilenced(matches, content, "VC082");
|
|
3748
3857
|
}
|
|
3749
3858
|
const ctx = tryParse(content, filePath);
|
|
3750
3859
|
if (!ctx) return matches;
|
|
@@ -3783,7 +3892,7 @@ var ssti = {
|
|
|
3783
3892
|
);
|
|
3784
3893
|
}
|
|
3785
3894
|
);
|
|
3786
|
-
return matches;
|
|
3895
|
+
return filterSilenced(matches, content, "VC082");
|
|
3787
3896
|
}
|
|
3788
3897
|
};
|
|
3789
3898
|
var javaDeserialization = {
|
|
@@ -4104,7 +4213,7 @@ var commandInjection = {
|
|
|
4104
4213
|
matches.push(m);
|
|
4105
4214
|
}
|
|
4106
4215
|
}
|
|
4107
|
-
return matches;
|
|
4216
|
+
return filterSilenced(matches, content, "VC094");
|
|
4108
4217
|
}
|
|
4109
4218
|
};
|
|
4110
4219
|
var corsLocalhost = {
|
|
@@ -6799,7 +6908,7 @@ var llmPromptInjection = {
|
|
|
6799
6908
|
});
|
|
6800
6909
|
}
|
|
6801
6910
|
}
|
|
6802
|
-
return findings;
|
|
6911
|
+
return filterSilenced(findings, content, "VC198");
|
|
6803
6912
|
}
|
|
6804
6913
|
};
|
|
6805
6914
|
var llmSystemPromptInjection = {
|
|
@@ -6836,7 +6945,7 @@ var llmSystemPromptInjection = {
|
|
|
6836
6945
|
});
|
|
6837
6946
|
}
|
|
6838
6947
|
}
|
|
6839
|
-
return findings;
|
|
6948
|
+
return filterSilenced(findings, content, "VC199");
|
|
6840
6949
|
}
|
|
6841
6950
|
};
|
|
6842
6951
|
var llmOutputAsHTML = {
|
|
@@ -6880,7 +6989,7 @@ var llmOutputAsHTML = {
|
|
|
6880
6989
|
});
|
|
6881
6990
|
}
|
|
6882
6991
|
}
|
|
6883
|
-
return findings;
|
|
6992
|
+
return filterSilenced(findings, content, "VC200");
|
|
6884
6993
|
}
|
|
6885
6994
|
};
|
|
6886
6995
|
var vectorStoreQueryNoUserFilter = {
|