xploitscan 1.1.10 → 1.2.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 CHANGED
@@ -131,6 +131,30 @@ Create a `.xploitscanrc` file in your project root:
131
131
  }
132
132
  ```
133
133
 
134
+ ### `.xploitscanignore`
135
+
136
+ A `.gitignore`-style file in your project root. Plain lines exclude whole
137
+ files from scanning (negate with `!`). A line may also carry a trailing
138
+ rule-ID list to suppress **only those rules** on matching paths while still
139
+ scanning the files for everything else:
140
+
141
+ ```gitignore
142
+ # Don't scan generated code at all
143
+ generated/**
144
+
145
+ # Allow log-injection (VC044) in internal cron scripts only
146
+ scripts/cron/** VC044
147
+
148
+ # Silence two rules across all test files
149
+ **/*.test.ts VC031, VC043
150
+
151
+ # Suppress every rule on a legacy tree (the `scanner` wildcard)
152
+ legacy/** scanner
153
+ ```
154
+
155
+ For a single reviewed-and-accepted finding, prefer an inline
156
+ `// VC<id>-OK: <reason>` comment instead of a file- or path-wide rule.
157
+
134
158
  ## Web Dashboard
135
159
 
136
160
  Scan via the web at [xploitscan.com](https://xploitscan.com):
@@ -14,7 +14,7 @@ import {
14
14
  storeToken,
15
15
  syncUser,
16
16
  uploadScanResults
17
- } from "./chunk-GPJLFSUZ.js";
17
+ } from "./chunk-KE2IZNH6.js";
18
18
  export {
19
19
  checkUsage,
20
20
  clearProRulesCache,
@@ -29,4 +29,4 @@ export {
29
29
  syncUser,
30
30
  uploadScanResults
31
31
  };
32
- //# sourceMappingURL=api-62XVZKRV.js.map
32
+ //# sourceMappingURL=api-4UFZFIDO.js.map
@@ -43509,8 +43509,69 @@ var TAINTED_REQUEST_OBJECTS = /* @__PURE__ */ new Set([
43509
43509
  "event"
43510
43510
  // Lambda
43511
43511
  ]);
43512
+ var FS_READ_METHODS = /* @__PURE__ */ new Set(["readFile", "readFileSync"]);
43513
+ var PARSE_OBJECTS = /* @__PURE__ */ new Set([
43514
+ "JSON",
43515
+ "csv",
43516
+ "papa",
43517
+ "Papa",
43518
+ "yaml",
43519
+ "YAML",
43520
+ "toml",
43521
+ "qs",
43522
+ "querystring"
43523
+ ]);
43524
+ var PARSE_BARE = /* @__PURE__ */ new Set(["parse", "parseSync"]);
43525
+ var ARRAY_ELEMENT_METHODS = /* @__PURE__ */ new Set([
43526
+ "map",
43527
+ "filter",
43528
+ "forEach",
43529
+ "find",
43530
+ "findIndex",
43531
+ "flatMap",
43532
+ "some",
43533
+ "every"
43534
+ ]);
43535
+ var ARRAY_REDUCE_METHODS = /* @__PURE__ */ new Set(["reduce", "reduceRight"]);
43536
+ function callPreservesTaint(node, rec) {
43537
+ const callee = node.callee;
43538
+ const anyArgTainted = () => node.arguments.some((a) => a.type !== "SpreadElement" && rec(a));
43539
+ if (callee.type === "MemberExpression" && callee.property.type === "Identifier") {
43540
+ const pname = callee.property.name;
43541
+ if (FS_READ_METHODS.has(pname)) return anyArgTainted();
43542
+ if (pname === "parse" || pname === "parseSync") {
43543
+ const obj = callee.object;
43544
+ if (obj.type === "Identifier" && PARSE_OBJECTS.has(obj.name)) return anyArgTainted();
43545
+ }
43546
+ }
43547
+ if (callee.type === "Identifier") {
43548
+ if (FS_READ_METHODS.has(callee.name)) return anyArgTainted();
43549
+ if (PARSE_BARE.has(callee.name)) return anyArgTainted();
43550
+ }
43551
+ return false;
43552
+ }
43512
43553
  function buildTaintMap(parsed) {
43513
43554
  const tainted = /* @__PURE__ */ new Set();
43555
+ function markPatternTainted(target) {
43556
+ if (target.type === "Identifier") {
43557
+ tainted.add(target.name);
43558
+ } else if (target.type === "ObjectPattern") {
43559
+ for (const prop of target.properties) {
43560
+ if (prop.type === "ObjectProperty" && prop.value.type === "Identifier") {
43561
+ tainted.add(prop.value.name);
43562
+ } else if (prop.type === "RestElement" && prop.argument.type === "Identifier") {
43563
+ tainted.add(prop.argument.name);
43564
+ }
43565
+ }
43566
+ } else if (target.type === "ArrayPattern") {
43567
+ for (const el of target.elements) {
43568
+ if (el && el.type === "Identifier") tainted.add(el.name);
43569
+ else if (el && el.type === "RestElement" && el.argument.type === "Identifier") {
43570
+ tainted.add(el.argument.name);
43571
+ }
43572
+ }
43573
+ }
43574
+ }
43514
43575
  function reachesRequestIdent(node) {
43515
43576
  if (!node) return false;
43516
43577
  if (node.type === "Identifier") return TAINTED_REQUEST_OBJECTS.has(node.name);
@@ -43581,6 +43642,7 @@ function buildTaintMap(parsed) {
43581
43642
  }
43582
43643
  if (node.type === "CallExpression") {
43583
43644
  if (nodeIsTaintedSource(node)) return true;
43645
+ if (callPreservesTaint(node, exprIsTainted)) return true;
43584
43646
  if (node.callee.type === "MemberExpression") {
43585
43647
  if (exprIsTainted(node.callee.object)) return true;
43586
43648
  const obj = node.callee.object;
@@ -43601,6 +43663,16 @@ function buildTaintMap(parsed) {
43601
43663
  if (node.type === "AwaitExpression") {
43602
43664
  return exprIsTainted(node.argument);
43603
43665
  }
43666
+ if (node.type === "ArrayExpression") {
43667
+ return node.elements.some(
43668
+ (el) => el != null && el.type === "SpreadElement" && exprIsTainted(el.argument)
43669
+ );
43670
+ }
43671
+ if (node.type === "ObjectExpression") {
43672
+ return node.properties.some(
43673
+ (p) => p.type === "SpreadElement" && exprIsTainted(p.argument)
43674
+ );
43675
+ }
43604
43676
  return false;
43605
43677
  }
43606
43678
  traverse(parsed.ast, {
@@ -43635,6 +43707,32 @@ function buildTaintMap(parsed) {
43635
43707
  if (exprIsTainted(node.right)) {
43636
43708
  tainted.add(node.left.name);
43637
43709
  }
43710
+ },
43711
+ // for (const row of <tainted>) → row tainted (and destructured names).
43712
+ ForOfStatement(path) {
43713
+ const node = path.node;
43714
+ if (node.type !== "ForOfStatement") return;
43715
+ if (!exprIsTainted(node.right)) return;
43716
+ const decl = node.left;
43717
+ const target = decl.type === "VariableDeclaration" && decl.declarations[0] ? decl.declarations[0].id : decl;
43718
+ markPatternTainted(target);
43719
+ },
43720
+ // Array-iteration element taint: <tainted>.map((row) => …) marks `row`
43721
+ // tainted inside the callback. Gated on the receiver already being
43722
+ // tainted, so this never originates taint.
43723
+ CallExpression(path) {
43724
+ const node = path.node;
43725
+ if (node.type !== "CallExpression") return;
43726
+ const callee = node.callee;
43727
+ if (callee.type !== "MemberExpression" || callee.property.type !== "Identifier") return;
43728
+ const method = callee.property.name;
43729
+ const isReduce = ARRAY_REDUCE_METHODS.has(method);
43730
+ if (!ARRAY_ELEMENT_METHODS.has(method) && !isReduce) return;
43731
+ if (!exprIsTainted(callee.object)) return;
43732
+ const cb = node.arguments[0];
43733
+ if (!cb || cb.type !== "ArrowFunctionExpression" && cb.type !== "FunctionExpression") return;
43734
+ const elemParam = cb.params[isReduce ? 1 : 0];
43735
+ if (elemParam) markPatternTainted(elemParam);
43638
43736
  }
43639
43737
  });
43640
43738
  const isTainted = (node) => {
@@ -43660,6 +43758,7 @@ function buildTaintMap(parsed) {
43660
43758
  return isTainted(node.object);
43661
43759
  }
43662
43760
  if (node.type === "CallExpression") {
43761
+ if (callPreservesTaint(node, isTainted)) return true;
43663
43762
  if (node.callee.type === "MemberExpression") {
43664
43763
  if (isTainted(node.callee.object)) return true;
43665
43764
  const obj = node.callee.object;
@@ -43677,6 +43776,16 @@ function buildTaintMap(parsed) {
43677
43776
  }
43678
43777
  }
43679
43778
  }
43779
+ if (node.type === "ArrayExpression") {
43780
+ return node.elements.some(
43781
+ (el) => el != null && el.type === "SpreadElement" && isTainted(el.argument)
43782
+ );
43783
+ }
43784
+ if (node.type === "ObjectExpression") {
43785
+ return node.properties.some(
43786
+ (p) => p.type === "SpreadElement" && isTainted(p.argument)
43787
+ );
43788
+ }
43680
43789
  return false;
43681
43790
  };
43682
43791
  return {
@@ -45896,4 +46005,4 @@ export {
45896
46005
  loadCachedProRules,
45897
46006
  clearProRulesCache
45898
46007
  };
45899
- //# sourceMappingURL=chunk-GPJLFSUZ.js.map
46008
+ //# sourceMappingURL=chunk-KE2IZNH6.js.map