xploitscan-shared-rules 1.7.2 → 1.7.4

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
@@ -637,6 +637,12 @@ function isInlineSilenced(content, matchIndex, ruleId) {
637
637
  );
638
638
  return marker.test(matchLine) || marker.test(prevLine);
639
639
  }
640
+ function filterSilenced(matches, content, ruleId) {
641
+ if (matches.length === 0) return matches;
642
+ const lines = content.split("\n");
643
+ const lineStartIndex = (line) => lines.slice(0, line - 1).reduce((acc, l) => acc + l.length + 1, 0);
644
+ return matches.filter((m) => !isInlineSilenced(content, lineStartIndex(m.line), ruleId));
645
+ }
640
646
  function findMatches(content, pattern, rule, filePath, fixTemplate) {
641
647
  const matches = [];
642
648
  const lines = content.split("\n");
@@ -1881,9 +1887,15 @@ var insecureDeserialization = {
1881
1887
  () => "Never deserialize untrusted data. Use JSON instead of pickle/Marshal/unserialize. For YAML, use yaml.safe_load(). Validate and sanitize all input before deserialization."
1882
1888
  ));
1883
1889
  }
1890
+ const isJsTs = /\.(jsx?|tsx?|mjs|cjs)$/.test(filePath);
1884
1891
  return matches.filter((m) => {
1885
1892
  if (!/yaml\.load\s*\(/.test(m.snippet ?? "")) return true;
1886
1893
  const lineText = (m.snippet ?? "").toLowerCase();
1894
+ if (isJsTs) {
1895
+ const ctxLines = content.split("\n").slice(m.line - 1, m.line + 2).join("\n");
1896
+ if (/default_full_schema|\bfull_schema\b/i.test(ctxLines)) return true;
1897
+ return false;
1898
+ }
1887
1899
  if (/safe_schema|failsafe_schema|safe_load|safeloader/.test(lineText)) {
1888
1900
  return false;
1889
1901
  }
@@ -3651,7 +3663,11 @@ var xxeVulnerability = {
3651
3663
  const patterns = [
3652
3664
  /\.parseXm?l\s*\(/gi,
3653
3665
  // catches parseXml (libxmljs) AND parseXML
3654
- /new\s+DOMParser\s*\(\)/g,
3666
+ // NOTE: the browser `new DOMParser()` is intentionally NOT flagged.
3667
+ // Per the HTML/XML spec, DOMParser.parseFromString does not resolve
3668
+ // external entities, so it is not an XXE sink — flagging it produced a
3669
+ // critical false positive on ordinary client-side XML/HTML parsing.
3670
+ // Real XXE sinks (libxmljs parseXml with noent, etree, SAX) remain below.
3655
3671
  /etree\.parse\s*\(/g,
3656
3672
  /lxml\.etree/g,
3657
3673
  /SAXParserFactory/g,
@@ -3910,10 +3926,10 @@ var sensitiveURLParams = {
3910
3926
  p,
3911
3927
  sensitiveURLParams,
3912
3928
  filePath,
3913
- () => "Never pass sensitive data in URL parameters. Use request headers (Authorization: Bearer ...) or POST body instead."
3929
+ () => "Never pass sensitive data in URL parameters. Use request headers (Authorization: Bearer ...) or POST body instead. If this value is intentionally URL-safe (e.g. a one-time, server-verified reference like a Stripe checkout session_id), add an inline `// VC088-OK: <reason>` comment to silence."
3914
3930
  ));
3915
3931
  }
3916
- return matches;
3932
+ return filterSilenced(matches, content, "VC088");
3917
3933
  }
3918
3934
  };
3919
3935
  var missingContentDisposition = {
@@ -6822,7 +6838,11 @@ var llmOutputAsHTML = {
6822
6838
  const findings = [];
6823
6839
  const patterns = [
6824
6840
  // dangerouslySetInnerHTML with .choices[0].message.content / .text / etc.
6825
- /dangerouslySetInnerHTML\s*=\s*\{\{\s*__html\s*:\s*[^}]*\b(?:choices\[\d*\]?\.message|completion|response|message\.content|content_block|delta\.text|generated_text|output_text|text)\b/g,
6841
+ // NOTE: a bare `text` token used to be in this alternation and matched
6842
+ // any `.text` property (e.g. `post.text`) in a file that merely imported
6843
+ // an LLM SDK — a high-severity false positive. Only LLM-specific shapes
6844
+ // remain (delta.text / output_text / generated_text are qualified).
6845
+ /dangerouslySetInnerHTML\s*=\s*\{\{\s*__html\s*:\s*[^}]*\b(?:choices\[\d*\]?\.message|completion|response|message\.content|content_block|delta\.text|generated_text|output_text)\b/g,
6826
6846
  // .innerHTML = response.choices[0].message.content
6827
6847
  /\.innerHTML\s*=\s*[^;]*\b(?:choices\[\d*\]?\.message|completion|response\.message|message\.content|delta\.text|generated_text|output_text)\b/g
6828
6848
  ];