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/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 = {