cognium-dev 3.193.0 → 3.195.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.
Files changed (2) hide show
  1. package/dist/cli.js +500 -572
  2. package/package.json +2 -2
package/dist/cli.js CHANGED
@@ -9350,6 +9350,7 @@ var GO_KEYWORDS = new Set([
9350
9350
  function buildGoDFG(tree) {
9351
9351
  const defs = [];
9352
9352
  const uses = [];
9353
+ const extraChains = [];
9353
9354
  let defIdCounter = 1;
9354
9355
  let useIdCounter = 1;
9355
9356
  const scopeStack = [new Map];
@@ -9371,7 +9372,7 @@ function buildGoDFG(tree) {
9371
9372
  }
9372
9373
  const body2 = func2.childForFieldName("body");
9373
9374
  if (body2) {
9374
- processGoBlock(body2, defs, uses, scopeStack, { defId: defIdCounter, useId: useIdCounter });
9375
+ processGoBlock(body2, defs, uses, scopeStack, { defId: defIdCounter, useId: useIdCounter }, extraChains);
9375
9376
  defIdCounter = defs.length + 1;
9376
9377
  useIdCounter = uses.length + 1;
9377
9378
  }
@@ -9387,6 +9388,15 @@ function buildGoDFG(tree) {
9387
9388
  }
9388
9389
  }
9389
9390
  const chains = computeChains(defs, uses);
9391
+ const seen = new Set(chains.map((c) => `${c.from_def}|${c.to_def}|${c.via}`));
9392
+ for (const ch of extraChains) {
9393
+ const key = `${ch.from_def}|${ch.to_def}|${ch.via}`;
9394
+ if (!seen.has(key)) {
9395
+ seen.add(key);
9396
+ chains.push(ch);
9397
+ }
9398
+ }
9399
+ chains.sort((a, b) => a.from_def - b.from_def || a.to_def - b.to_def);
9390
9400
  return { defs, uses, chains };
9391
9401
  }
9392
9402
  function extractGoParamDefs(params, defs, _startId, scopeStack) {
@@ -9415,28 +9425,51 @@ function extractGoParamDefs(params, defs, _startId, scopeStack) {
9415
9425
  }
9416
9426
  }
9417
9427
  }
9418
- function processGoBlock(node, defs, uses, scopeStack, counters) {
9428
+ function processGoBlock(node, defs, uses, scopeStack, counters, extraChains = []) {
9429
+ const linkMultiLineRhs = (right, useStart, useEnd, defStart, defEnd) => {
9430
+ if (right.endPosition.row <= right.startPosition.row)
9431
+ return;
9432
+ for (let ui = useStart;ui < useEnd; ui++) {
9433
+ const u = uses[ui];
9434
+ if (!u || u.def_id === null)
9435
+ continue;
9436
+ for (let di = defStart;di < defEnd; di++) {
9437
+ const d = defs[di];
9438
+ if (d && d.kind === "local" && d.id !== u.def_id) {
9439
+ extraChains.push({ from_def: u.def_id, to_def: d.id, via: u.variable });
9440
+ }
9441
+ }
9442
+ }
9443
+ };
9419
9444
  walkTree(node, (child) => {
9420
9445
  if (child.type === "short_var_declaration") {
9421
9446
  const left = child.childForFieldName("left");
9422
9447
  const right = child.childForFieldName("right");
9448
+ const usesBefore = uses.length;
9423
9449
  if (right) {
9424
9450
  extractGoUses(right, uses, scopeStack);
9425
9451
  }
9452
+ const defsBefore = defs.length;
9426
9453
  if (left) {
9427
9454
  extractGoLhsDefs(left, defs, scopeStack, child.startPosition.row + 1);
9428
9455
  }
9456
+ if (right)
9457
+ linkMultiLineRhs(right, usesBefore, uses.length, defsBefore, defs.length);
9429
9458
  } else if (child.type === "var_declaration") {
9430
9459
  processGoVarDecl(child, defs, scopeStack, counters);
9431
9460
  } else if (child.type === "assignment_statement") {
9432
9461
  const left = child.childForFieldName("left");
9433
9462
  const right = child.childForFieldName("right");
9463
+ const usesBefore = uses.length;
9434
9464
  if (right) {
9435
9465
  extractGoUses(right, uses, scopeStack);
9436
9466
  }
9467
+ const defsBefore = defs.length;
9437
9468
  if (left) {
9438
9469
  extractGoLhsDefs(left, defs, scopeStack, child.startPosition.row + 1);
9439
9470
  }
9471
+ if (right)
9472
+ linkMultiLineRhs(right, usesBefore, uses.length, defsBefore, defs.length);
9440
9473
  } else if (child.type === "for_statement") {
9441
9474
  const rangeClause = findChildByTypeGo(child, "range_clause");
9442
9475
  if (rangeClause) {
@@ -10686,7 +10719,7 @@ var DEFAULT_SOURCES = [
10686
10719
  { method: "Json", type: "http_body", severity: "high", return_tainted: true, languages: ["rust"] },
10687
10720
  { method: "Query", type: "http_param", severity: "high", return_tainted: true, languages: ["rust"] },
10688
10721
  { method: "Path", type: "http_path", severity: "high", return_tainted: true, languages: ["rust"] },
10689
- { method: "Form", type: "http_param", severity: "high", return_tainted: true, languages: ["rust"] },
10722
+ { method: "Form", type: "http_body", severity: "high", return_tainted: true, languages: ["rust"] },
10690
10723
  { method: "var", class: "env", type: "env_input", severity: "medium", return_tainted: true },
10691
10724
  { method: "var_os", class: "env", type: "env_input", severity: "medium", return_tainted: true },
10692
10725
  { method: "args", class: "env", type: "env_input", severity: "medium", return_tainted: true },
@@ -11475,7 +11508,7 @@ var DEFAULT_SINKS = [
11475
11508
  { method: "execSync", class: "child_process", type: "command_injection", cwe: "CWE-78", severity: "critical", arg_positions: [0] },
11476
11509
  { method: "spawn", class: "child_process", type: "command_injection", cwe: "CWE-78", severity: "critical", arg_positions: [0] },
11477
11510
  { method: "spawnSync", class: "child_process", type: "command_injection", cwe: "CWE-78", severity: "critical", arg_positions: [0] },
11478
- { method: "exec", type: "command_injection", cwe: "CWE-78", severity: "high", arg_positions: [0] },
11511
+ { method: "exec", type: "command_injection", cwe: "CWE-78", severity: "high", arg_positions: [0], exclude_languages: ["python"] },
11479
11512
  { method: "execSync", type: "command_injection", cwe: "CWE-78", severity: "high", arg_positions: [0], languages: ["javascript", "typescript"] },
11480
11513
  { method: "spawn", type: "command_injection", cwe: "CWE-78", severity: "high", arg_positions: [0], languages: ["javascript", "typescript"] },
11481
11514
  { method: "spawnSync", type: "command_injection", cwe: "CWE-78", severity: "high", arg_positions: [0], languages: ["javascript", "typescript"] },
@@ -11495,7 +11528,7 @@ var DEFAULT_SINKS = [
11495
11528
  { method: "query", class: "Client", type: "sql_injection", cwe: "CWE-89", severity: "critical", arg_positions: [0], languages: ["javascript", "typescript"], allow_unresolved_receiver: true },
11496
11529
  { method: "execute", class: "Pool", type: "sql_injection", cwe: "CWE-89", severity: "critical", arg_positions: [0], languages: ["javascript", "typescript"], allow_unresolved_receiver: true },
11497
11530
  { method: "execute", class: "Connection", type: "sql_injection", cwe: "CWE-89", severity: "critical", arg_positions: [0], languages: ["javascript", "typescript"], allow_unresolved_receiver: true },
11498
- { method: "raw", type: "sql_injection", cwe: "CWE-89", severity: "high", arg_positions: [0], languages: ["javascript", "typescript"] },
11531
+ { method: "raw", type: "sql_injection", cwe: "CWE-89", severity: "critical", arg_positions: [0], languages: ["javascript", "typescript"] },
11499
11532
  { method: "all", class: "Connection", type: "sql_injection", cwe: "CWE-89", severity: "critical", arg_positions: [0], languages: ["javascript", "typescript"], allow_unresolved_receiver: true },
11500
11533
  { method: "run", class: "Connection", type: "sql_injection", cwe: "CWE-89", severity: "critical", arg_positions: [0], languages: ["javascript", "typescript"], allow_unresolved_receiver: true },
11501
11534
  { method: "each", class: "Connection", type: "sql_injection", cwe: "CWE-89", severity: "critical", arg_positions: [0], languages: ["javascript", "typescript"], allow_unresolved_receiver: true },
@@ -12450,6 +12483,17 @@ function analyzeTaint(calls, types, config = getDefaultConfig(), typeHierarchy,
12450
12483
  const sanitizers = findSanitizers(calls, types, config.sanitizers, sourceLines);
12451
12484
  return { sources, sinks, sanitizers };
12452
12485
  }
12486
+ function sinkPatternAppliesTo(pattern, language) {
12487
+ if (language === undefined)
12488
+ return true;
12489
+ if (pattern.languages && pattern.languages.length > 0 && !pattern.languages.includes(language)) {
12490
+ return false;
12491
+ }
12492
+ if (pattern.exclude_languages && pattern.exclude_languages.includes(language)) {
12493
+ return false;
12494
+ }
12495
+ return true;
12496
+ }
12453
12497
  function expandPromisifyAliases(patterns, sourceLines, language) {
12454
12498
  if (!sourceLines || sourceLines.length === 0)
12455
12499
  return patterns;
@@ -12458,7 +12502,7 @@ function expandPromisifyAliases(patterns, sourceLines, language) {
12458
12502
  const PROMISIFY_RE = /\b(?:const|let|var)\s+([A-Za-z_$][\w$]*)\s*=\s*(?:util\s*\.\s*)?promisify\s*\(\s*([A-Za-z_$][\w$]*)\s*\)/;
12459
12503
  const innerToPattern = new Map;
12460
12504
  for (const p of patterns) {
12461
- if (p.languages && !p.languages.includes(language))
12505
+ if (!sinkPatternAppliesTo(p, language))
12462
12506
  continue;
12463
12507
  if (p.class !== undefined)
12464
12508
  continue;
@@ -12466,7 +12510,7 @@ function expandPromisifyAliases(patterns, sourceLines, language) {
12466
12510
  innerToPattern.set(p.method, p);
12467
12511
  }
12468
12512
  for (const p of patterns) {
12469
- if (p.languages && !p.languages.includes(language))
12513
+ if (!sinkPatternAppliesTo(p, language))
12470
12514
  continue;
12471
12515
  if (innerToPattern.has(p.method))
12472
12516
  continue;
@@ -12508,7 +12552,7 @@ function expandIndirectEvalAliases(patterns, sourceLines, language) {
12508
12552
  const INDIRECT_EVAL_RE = /\b(?:const|let|var)\s+([A-Za-z_$][\w$]*)\s*(?::[^=]+)?=\s*(eval|Function)\b(?!\s*\()/;
12509
12553
  const innerToPattern = new Map;
12510
12554
  for (const p of patterns) {
12511
- if (p.languages && !p.languages.includes(language))
12555
+ if (!sinkPatternAppliesTo(p, language))
12512
12556
  continue;
12513
12557
  if (p.class !== undefined)
12514
12558
  continue;
@@ -13268,7 +13312,7 @@ function isSafeJinjaRenderCall(call, pattern, language, sourceLines) {
13268
13312
  return false;
13269
13313
  }
13270
13314
  function findSinks(calls, patterns, typeHierarchy, language, sourceLines, types) {
13271
- const patternsForLanguage = language === undefined ? patterns : patterns.filter((p) => !p.languages || p.languages.length === 0 || p.languages.includes(language));
13315
+ const patternsForLanguage = language === undefined ? patterns : patterns.filter((p) => sinkPatternAppliesTo(p, language));
13272
13316
  const sinkMap = new Map;
13273
13317
  for (const call of calls) {
13274
13318
  for (const pattern of patternsForLanguage) {
@@ -13603,10 +13647,8 @@ var SINK_FQN_EXCLUSIONS = {
13603
13647
  ]
13604
13648
  };
13605
13649
  function matchesSinkPattern(call, pattern, typeHierarchy, language) {
13606
- if (pattern.languages && pattern.languages.length > 0 && language !== undefined) {
13607
- if (!pattern.languages.includes(language)) {
13608
- return false;
13609
- }
13650
+ if (!sinkPatternAppliesTo(pattern, language)) {
13651
+ return false;
13610
13652
  }
13611
13653
  const callMethodName = call.method_name;
13612
13654
  const patternMethod = pattern.method;
@@ -20954,99 +20996,11 @@ class JavaPlugin extends BaseLanguagePlugin {
20954
20996
  severity: "high",
20955
20997
  confidence: 0.9,
20956
20998
  returnTainted: true
20957
- },
20958
- {
20959
- method: "getParameter",
20960
- class: "HttpServletRequest",
20961
- type: "http_param",
20962
- severity: "high",
20963
- confidence: 0.95,
20964
- returnTainted: true
20965
- },
20966
- {
20967
- method: "getParameterValues",
20968
- class: "HttpServletRequest",
20969
- type: "http_param",
20970
- severity: "high",
20971
- confidence: 0.95,
20972
- returnTainted: true
20973
- },
20974
- {
20975
- method: "getHeader",
20976
- class: "HttpServletRequest",
20977
- type: "http_header",
20978
- severity: "high",
20979
- confidence: 0.9,
20980
- returnTainted: true
20981
- },
20982
- {
20983
- method: "getCookies",
20984
- class: "HttpServletRequest",
20985
- type: "http_cookie",
20986
- severity: "high",
20987
- confidence: 0.9,
20988
- returnTainted: true
20989
- },
20990
- {
20991
- method: "getInputStream",
20992
- class: "HttpServletRequest",
20993
- type: "http_body",
20994
- severity: "high",
20995
- confidence: 0.85,
20996
- returnTainted: true
20997
- },
20998
- {
20999
- method: "getReader",
21000
- class: "HttpServletRequest",
21001
- type: "http_body",
21002
- severity: "high",
21003
- confidence: 0.85,
21004
- returnTainted: true
21005
20999
  }
21006
21000
  ];
21007
21001
  }
21008
21002
  getBuiltinSinks() {
21009
21003
  return [
21010
- {
21011
- method: "executeQuery",
21012
- class: "Statement",
21013
- type: "sql_injection",
21014
- cwe: "CWE-89",
21015
- severity: "critical",
21016
- argPositions: [0]
21017
- },
21018
- {
21019
- method: "executeUpdate",
21020
- class: "Statement",
21021
- type: "sql_injection",
21022
- cwe: "CWE-89",
21023
- severity: "critical",
21024
- argPositions: [0]
21025
- },
21026
- {
21027
- method: "execute",
21028
- class: "Statement",
21029
- type: "sql_injection",
21030
- cwe: "CWE-89",
21031
- severity: "critical",
21032
- argPositions: [0]
21033
- },
21034
- {
21035
- method: "exec",
21036
- class: "Runtime",
21037
- type: "command_injection",
21038
- cwe: "CWE-78",
21039
- severity: "critical",
21040
- argPositions: [0]
21041
- },
21042
- {
21043
- method: "start",
21044
- class: "ProcessBuilder",
21045
- type: "command_injection",
21046
- cwe: "CWE-78",
21047
- severity: "critical",
21048
- argPositions: []
21049
- },
21050
21004
  {
21051
21005
  method: "FileInputStream",
21052
21006
  type: "path_traversal",
@@ -21061,38 +21015,6 @@ class JavaPlugin extends BaseLanguagePlugin {
21061
21015
  severity: "high",
21062
21016
  argPositions: [0]
21063
21017
  },
21064
- {
21065
- method: "write",
21066
- class: "PrintWriter",
21067
- type: "xss",
21068
- cwe: "CWE-79",
21069
- severity: "high",
21070
- argPositions: [0]
21071
- },
21072
- {
21073
- method: "println",
21074
- class: "PrintWriter",
21075
- type: "xss",
21076
- cwe: "CWE-79",
21077
- severity: "high",
21078
- argPositions: [0]
21079
- },
21080
- {
21081
- method: "search",
21082
- class: "DirContext",
21083
- type: "ldap_injection",
21084
- cwe: "CWE-90",
21085
- severity: "high",
21086
- argPositions: [0, 1]
21087
- },
21088
- {
21089
- method: "evaluate",
21090
- class: "XPath",
21091
- type: "xpath_injection",
21092
- cwe: "CWE-643",
21093
- severity: "high",
21094
- argPositions: [0]
21095
- },
21096
21018
  {
21097
21019
  method: "readObject",
21098
21020
  class: "ObjectInputStream",
@@ -21602,37 +21524,6 @@ class JavaScriptPlugin extends BaseLanguagePlugin {
21602
21524
  }
21603
21525
  getBuiltinSinks() {
21604
21526
  return [
21605
- {
21606
- method: "exec",
21607
- class: "child_process",
21608
- type: "command_injection",
21609
- cwe: "CWE-78",
21610
- severity: "critical",
21611
- argPositions: [0]
21612
- },
21613
- {
21614
- method: "execSync",
21615
- class: "child_process",
21616
- type: "command_injection",
21617
- cwe: "CWE-78",
21618
- severity: "critical",
21619
- argPositions: [0]
21620
- },
21621
- {
21622
- method: "spawn",
21623
- class: "child_process",
21624
- type: "command_injection",
21625
- cwe: "CWE-78",
21626
- severity: "critical",
21627
- argPositions: [0, 1]
21628
- },
21629
- {
21630
- method: "eval",
21631
- type: "code_injection",
21632
- cwe: "CWE-94",
21633
- severity: "critical",
21634
- argPositions: [0]
21635
- },
21636
21527
  {
21637
21528
  method: "Function",
21638
21529
  type: "code_injection",
@@ -21654,44 +21545,6 @@ class JavaScriptPlugin extends BaseLanguagePlugin {
21654
21545
  severity: "high",
21655
21546
  argPositions: [0]
21656
21547
  },
21657
- {
21658
- method: "readFileSync",
21659
- class: "fs",
21660
- type: "path_traversal",
21661
- cwe: "CWE-22",
21662
- severity: "high",
21663
- argPositions: [0]
21664
- },
21665
- {
21666
- method: "writeFileSync",
21667
- class: "fs",
21668
- type: "path_traversal",
21669
- cwe: "CWE-22",
21670
- severity: "high",
21671
- argPositions: [0]
21672
- },
21673
- {
21674
- method: "createReadStream",
21675
- class: "fs",
21676
- type: "path_traversal",
21677
- cwe: "CWE-22",
21678
- severity: "high",
21679
- argPositions: [0]
21680
- },
21681
- {
21682
- method: "innerHTML",
21683
- type: "xss",
21684
- cwe: "CWE-79",
21685
- severity: "high",
21686
- argPositions: [0]
21687
- },
21688
- {
21689
- method: "outerHTML",
21690
- type: "xss",
21691
- cwe: "CWE-79",
21692
- severity: "high",
21693
- argPositions: [0]
21694
- },
21695
21548
  {
21696
21549
  method: "document.write",
21697
21550
  type: "xss",
@@ -21741,13 +21594,6 @@ class JavaScriptPlugin extends BaseLanguagePlugin {
21741
21594
  severity: "critical",
21742
21595
  argPositions: [0]
21743
21596
  },
21744
- {
21745
- method: "raw",
21746
- type: "sql_injection",
21747
- cwe: "CWE-89",
21748
- severity: "critical",
21749
- argPositions: [0]
21750
- },
21751
21597
  {
21752
21598
  method: "$executeRawUnsafe",
21753
21599
  type: "sql_injection",
@@ -21762,29 +21608,6 @@ class JavaScriptPlugin extends BaseLanguagePlugin {
21762
21608
  severity: "critical",
21763
21609
  argPositions: [0]
21764
21610
  },
21765
- {
21766
- method: "fetch",
21767
- type: "ssrf",
21768
- cwe: "CWE-918",
21769
- severity: "high",
21770
- argPositions: [0]
21771
- },
21772
- {
21773
- method: "get",
21774
- class: "axios",
21775
- type: "ssrf",
21776
- cwe: "CWE-918",
21777
- severity: "high",
21778
- argPositions: [0]
21779
- },
21780
- {
21781
- method: "request",
21782
- class: "http",
21783
- type: "ssrf",
21784
- cwe: "CWE-918",
21785
- severity: "high",
21786
- argPositions: [0]
21787
- },
21788
21611
  {
21789
21612
  method: "find",
21790
21613
  type: "nosql_injection",
@@ -21792,13 +21615,6 @@ class JavaScriptPlugin extends BaseLanguagePlugin {
21792
21615
  severity: "high",
21793
21616
  argPositions: [0]
21794
21617
  },
21795
- {
21796
- method: "findOne",
21797
- type: "nosql_injection",
21798
- cwe: "CWE-943",
21799
- severity: "high",
21800
- argPositions: [0]
21801
- },
21802
21618
  {
21803
21619
  method: "merge",
21804
21620
  type: "prototype_pollution",
@@ -21874,13 +21690,6 @@ class JavaScriptPlugin extends BaseLanguagePlugin {
21874
21690
  severity: "high",
21875
21691
  argPositions: [0]
21876
21692
  },
21877
- {
21878
- method: "redirect",
21879
- type: "open_redirect",
21880
- cwe: "CWE-601",
21881
- severity: "high",
21882
- argPositions: [0]
21883
- },
21884
21693
  {
21885
21694
  method: "push",
21886
21695
  class: "router",
@@ -22123,13 +21932,6 @@ class PythonPlugin extends BaseLanguagePlugin {
22123
21932
  confidence: 0.9,
22124
21933
  returnTainted: true
22125
21934
  },
22126
- {
22127
- method: "input",
22128
- type: "user_input",
22129
- severity: "high",
22130
- confidence: 0.95,
22131
- returnTainted: true
22132
- },
22133
21935
  {
22134
21936
  method: "argv",
22135
21937
  class: "sys",
@@ -22145,129 +21947,11 @@ class PythonPlugin extends BaseLanguagePlugin {
22145
21947
  severity: "medium",
22146
21948
  confidence: 0.85,
22147
21949
  returnTainted: true
22148
- },
22149
- {
22150
- method: "getenv",
22151
- class: "os",
22152
- type: "env_var",
22153
- severity: "medium",
22154
- confidence: 0.85,
22155
- returnTainted: true
22156
- },
22157
- {
22158
- method: "read",
22159
- type: "file_input",
22160
- severity: "medium",
22161
- confidence: 0.8,
22162
- returnTainted: true
22163
- },
22164
- {
22165
- method: "readline",
22166
- type: "file_input",
22167
- severity: "medium",
22168
- confidence: 0.8,
22169
- returnTainted: true
22170
- },
22171
- {
22172
- method: "readlines",
22173
- type: "file_input",
22174
- severity: "medium",
22175
- confidence: 0.8,
22176
- returnTainted: true
22177
21950
  }
22178
21951
  ];
22179
21952
  }
22180
21953
  getBuiltinSinks() {
22181
21954
  return [
22182
- {
22183
- method: "system",
22184
- class: "os",
22185
- type: "command_injection",
22186
- cwe: "CWE-78",
22187
- severity: "critical",
22188
- argPositions: [0]
22189
- },
22190
- {
22191
- method: "popen",
22192
- class: "os",
22193
- type: "command_injection",
22194
- cwe: "CWE-78",
22195
- severity: "critical",
22196
- argPositions: [0]
22197
- },
22198
- {
22199
- method: "run",
22200
- class: "subprocess",
22201
- type: "command_injection",
22202
- cwe: "CWE-78",
22203
- severity: "critical",
22204
- argPositions: [0]
22205
- },
22206
- {
22207
- method: "call",
22208
- class: "subprocess",
22209
- type: "command_injection",
22210
- cwe: "CWE-78",
22211
- severity: "critical",
22212
- argPositions: [0]
22213
- },
22214
- {
22215
- method: "Popen",
22216
- class: "subprocess",
22217
- type: "command_injection",
22218
- cwe: "CWE-78",
22219
- severity: "critical",
22220
- argPositions: [0]
22221
- },
22222
- {
22223
- method: "eval",
22224
- type: "code_injection",
22225
- cwe: "CWE-94",
22226
- severity: "critical",
22227
- argPositions: [0]
22228
- },
22229
- {
22230
- method: "exec",
22231
- type: "code_injection",
22232
- cwe: "CWE-94",
22233
- severity: "critical",
22234
- argPositions: [0]
22235
- },
22236
- {
22237
- method: "compile",
22238
- type: "code_injection",
22239
- cwe: "CWE-94",
22240
- severity: "high",
22241
- argPositions: [0]
22242
- },
22243
- {
22244
- method: "execute",
22245
- type: "sql_injection",
22246
- cwe: "CWE-89",
22247
- severity: "critical",
22248
- argPositions: [0]
22249
- },
22250
- {
22251
- method: "executemany",
22252
- type: "sql_injection",
22253
- cwe: "CWE-89",
22254
- severity: "critical",
22255
- argPositions: [0]
22256
- },
22257
- {
22258
- method: "raw",
22259
- type: "sql_injection",
22260
- cwe: "CWE-89",
22261
- severity: "critical",
22262
- argPositions: [0]
22263
- },
22264
- {
22265
- method: "open",
22266
- type: "path_traversal",
22267
- cwe: "CWE-22",
22268
- severity: "high",
22269
- argPositions: [0]
22270
- },
22271
21955
  {
22272
21956
  method: "read",
22273
21957
  class: "pathlib",
@@ -22276,13 +21960,6 @@ class PythonPlugin extends BaseLanguagePlugin {
22276
21960
  severity: "high",
22277
21961
  argPositions: [0]
22278
21962
  },
22279
- {
22280
- method: "Markup",
22281
- type: "xss",
22282
- cwe: "CWE-79",
22283
- severity: "high",
22284
- argPositions: [0]
22285
- },
22286
21963
  {
22287
21964
  method: "safe",
22288
21965
  type: "xss",
@@ -22290,22 +21967,6 @@ class PythonPlugin extends BaseLanguagePlugin {
22290
21967
  severity: "high",
22291
21968
  argPositions: [0]
22292
21969
  },
22293
- {
22294
- method: "get",
22295
- class: "requests",
22296
- type: "ssrf",
22297
- cwe: "CWE-918",
22298
- severity: "high",
22299
- argPositions: [0]
22300
- },
22301
- {
22302
- method: "post",
22303
- class: "requests",
22304
- type: "ssrf",
22305
- cwe: "CWE-918",
22306
- severity: "high",
22307
- argPositions: [0]
22308
- },
22309
21970
  {
22310
21971
  method: "urlopen",
22311
21972
  class: "urllib",
@@ -22330,30 +21991,6 @@ class PythonPlugin extends BaseLanguagePlugin {
22330
21991
  severity: "high",
22331
21992
  argPositions: [1]
22332
21993
  },
22333
- {
22334
- method: "loads",
22335
- class: "pickle",
22336
- type: "deserialization",
22337
- cwe: "CWE-502",
22338
- severity: "critical",
22339
- argPositions: [0]
22340
- },
22341
- {
22342
- method: "load",
22343
- class: "pickle",
22344
- type: "deserialization",
22345
- cwe: "CWE-502",
22346
- severity: "critical",
22347
- argPositions: [0]
22348
- },
22349
- {
22350
- method: "load",
22351
- class: "yaml",
22352
- type: "deserialization",
22353
- cwe: "CWE-502",
22354
- severity: "critical",
22355
- argPositions: [0]
22356
- },
22357
21994
  {
22358
21995
  method: "unsafe_load",
22359
21996
  class: "yaml",
@@ -22489,34 +22126,6 @@ class RustPlugin extends BaseLanguagePlugin {
22489
22126
  }
22490
22127
  getBuiltinSources() {
22491
22128
  return [
22492
- {
22493
- method: "Query",
22494
- type: "http_param",
22495
- severity: "high",
22496
- confidence: 0.95,
22497
- returnTainted: true
22498
- },
22499
- {
22500
- method: "Json",
22501
- type: "http_body",
22502
- severity: "high",
22503
- confidence: 0.95,
22504
- returnTainted: true
22505
- },
22506
- {
22507
- method: "Path",
22508
- type: "http_path",
22509
- severity: "high",
22510
- confidence: 0.95,
22511
- returnTainted: true
22512
- },
22513
- {
22514
- method: "Form",
22515
- type: "http_body",
22516
- severity: "high",
22517
- confidence: 0.95,
22518
- returnTainted: true
22519
- },
22520
22129
  {
22521
22130
  method: "args",
22522
22131
  class: "std::env",
@@ -22564,14 +22173,6 @@ class RustPlugin extends BaseLanguagePlugin {
22564
22173
  severity: "medium",
22565
22174
  confidence: 0.8,
22566
22175
  returnTainted: true
22567
- },
22568
- {
22569
- method: "read",
22570
- class: "TcpStream",
22571
- type: "network_input",
22572
- severity: "high",
22573
- confidence: 0.85,
22574
- returnTainted: true
22575
22176
  }
22576
22177
  ];
22577
22178
  }
@@ -22585,29 +22186,6 @@ class RustPlugin extends BaseLanguagePlugin {
22585
22186
  severity: "critical",
22586
22187
  argPositions: [0]
22587
22188
  },
22588
- {
22589
- method: "arg",
22590
- class: "Command",
22591
- type: "command_injection",
22592
- cwe: "CWE-78",
22593
- severity: "critical",
22594
- argPositions: [0]
22595
- },
22596
- {
22597
- method: "args",
22598
- class: "Command",
22599
- type: "command_injection",
22600
- cwe: "CWE-78",
22601
- severity: "critical",
22602
- argPositions: [0]
22603
- },
22604
- {
22605
- method: "execute",
22606
- type: "sql_injection",
22607
- cwe: "CWE-89",
22608
- severity: "critical",
22609
- argPositions: [0]
22610
- },
22611
22189
  {
22612
22190
  method: "query",
22613
22191
  type: "sql_injection",
@@ -22622,22 +22200,6 @@ class RustPlugin extends BaseLanguagePlugin {
22622
22200
  severity: "critical",
22623
22201
  argPositions: [0]
22624
22202
  },
22625
- {
22626
- method: "open",
22627
- class: "File",
22628
- type: "path_traversal",
22629
- cwe: "CWE-22",
22630
- severity: "high",
22631
- argPositions: [0]
22632
- },
22633
- {
22634
- method: "create",
22635
- class: "File",
22636
- type: "path_traversal",
22637
- cwe: "CWE-22",
22638
- severity: "high",
22639
- argPositions: [0]
22640
- },
22641
22203
  {
22642
22204
  method: "read_to_string",
22643
22205
  class: "std::fs",
@@ -22675,38 +22237,6 @@ class RustPlugin extends BaseLanguagePlugin {
22675
22237
  severity: "critical",
22676
22238
  argPositions: [0]
22677
22239
  },
22678
- {
22679
- method: "from_str",
22680
- class: "serde_json",
22681
- type: "deserialization",
22682
- cwe: "CWE-502",
22683
- severity: "medium",
22684
- argPositions: [0]
22685
- },
22686
- {
22687
- method: "from_slice",
22688
- class: "serde_json",
22689
- type: "deserialization",
22690
- cwe: "CWE-502",
22691
- severity: "medium",
22692
- argPositions: [0]
22693
- },
22694
- {
22695
- method: "get",
22696
- class: "reqwest",
22697
- type: "ssrf",
22698
- cwe: "CWE-918",
22699
- severity: "high",
22700
- argPositions: [0]
22701
- },
22702
- {
22703
- method: "post",
22704
- class: "reqwest",
22705
- type: "ssrf",
22706
- cwe: "CWE-918",
22707
- severity: "high",
22708
- argPositions: [0]
22709
- },
22710
22240
  {
22711
22241
  method: "new",
22712
22242
  class: "Regex",
@@ -22799,13 +22329,6 @@ class BashPlugin extends BaseLanguagePlugin {
22799
22329
  }
22800
22330
  getBuiltinSources() {
22801
22331
  return [
22802
- {
22803
- method: "read",
22804
- type: "io_input",
22805
- severity: "high",
22806
- confidence: 0.9,
22807
- returnTainted: true
22808
- },
22809
22332
  {
22810
22333
  method: "curl",
22811
22334
  type: "http_response",
@@ -22824,13 +22347,6 @@ class BashPlugin extends BaseLanguagePlugin {
22824
22347
  }
22825
22348
  getBuiltinSinks() {
22826
22349
  return [
22827
- {
22828
- method: "eval",
22829
- type: "code_injection",
22830
- cwe: "CWE-94",
22831
- severity: "critical",
22832
- argPositions: [0]
22833
- },
22834
22350
  {
22835
22351
  method: "bash",
22836
22352
  type: "command_injection",
@@ -23226,30 +22742,6 @@ class GoPlugin extends BaseLanguagePlugin {
23226
22742
  confidence: 0.9,
23227
22743
  returnTainted: true
23228
22744
  },
23229
- {
23230
- method: "Query",
23231
- class: "Context",
23232
- type: "http_param",
23233
- severity: "high",
23234
- confidence: 0.95,
23235
- returnTainted: true
23236
- },
23237
- {
23238
- method: "Param",
23239
- class: "Context",
23240
- type: "http_path",
23241
- severity: "high",
23242
- confidence: 0.95,
23243
- returnTainted: true
23244
- },
23245
- {
23246
- method: "PostForm",
23247
- class: "Context",
23248
- type: "http_param",
23249
- severity: "high",
23250
- confidence: 0.95,
23251
- returnTainted: true
23252
- },
23253
22745
  {
23254
22746
  method: "GetRawData",
23255
22747
  class: "Context",
@@ -23446,14 +22938,6 @@ class GoPlugin extends BaseLanguagePlugin {
23446
22938
  severity: "medium",
23447
22939
  argPositions: [0]
23448
22940
  },
23449
- {
23450
- method: "Decode",
23451
- class: "Decoder",
23452
- type: "deserialization",
23453
- cwe: "CWE-502",
23454
- severity: "medium",
23455
- argPositions: [0]
23456
- },
23457
22941
  {
23458
22942
  method: "Print",
23459
22943
  class: "log",
@@ -25088,6 +24572,7 @@ class LanguageSourcesPass {
25088
24572
  additionalSanitizers.push(...findPythonRangeCheckGuardSanitizers(code));
25089
24573
  additionalSanitizers.push(...findPythonRegexAllowlistWrapperSanitizers(code));
25090
24574
  additionalSanitizers.push(...findPythonSetMembershipXssGuardSanitizers(code));
24575
+ additionalSanitizers.push(...findPythonTraversalRejectGuardSanitizers(code));
25091
24576
  additionalSanitizers.push(...findPythonDefusedXmlSanitizers(code));
25092
24577
  additionalSanitizers.push(...findPythonJinjaAutoescapeSanitizers(code));
25093
24578
  const pyMisconfigFindings = findPythonPatternFindings(code, graph.ir.meta.file);
@@ -26991,6 +26476,69 @@ function findPythonSetMembershipXssGuardSanitizers(code) {
26991
26476
  }
26992
26477
  return sanitizers;
26993
26478
  }
26479
+ function findPythonTraversalRejectGuardSanitizers(code) {
26480
+ const sanitizers = [];
26481
+ const lines = code.split(`
26482
+ `);
26483
+ const guardOpen = /^(\s*)if\s+(['"])(\.\.[/\\]?)\2\s+in\s+([A-Za-z_][A-Za-z0-9_]*)\s*:\s*$/;
26484
+ const terminator = /\b(return|raise|abort\s*\(|sys\.exit\s*\()/;
26485
+ for (let i2 = 0;i2 < lines.length; i2++) {
26486
+ const m = guardOpen.exec(lines[i2]);
26487
+ if (!m)
26488
+ continue;
26489
+ const guardIndent = m[1].length;
26490
+ const guardedVar = m[4];
26491
+ let bodyHasTerminator = false;
26492
+ let blockEnd = -1;
26493
+ const maxScan = Math.min(lines.length, i2 + 26);
26494
+ for (let j = i2 + 1;j < maxScan; j++) {
26495
+ const line = lines[j];
26496
+ if (line.trim() === "")
26497
+ continue;
26498
+ const indent = line.length - line.trimStart().length;
26499
+ if (indent <= guardIndent) {
26500
+ blockEnd = j - 1;
26501
+ break;
26502
+ }
26503
+ if (terminator.test(line))
26504
+ bodyHasTerminator = true;
26505
+ }
26506
+ if (blockEnd === -1)
26507
+ blockEnd = Math.min(lines.length - 1, i2 + 25);
26508
+ if (!bodyHasTerminator)
26509
+ continue;
26510
+ const guarded = new Set([guardedVar]);
26511
+ const assignRe = /^\s*([A-Za-z_][A-Za-z0-9_]*)\s*=(?!=)\s*(.*)$/;
26512
+ const mentionsGuarded = (text) => {
26513
+ for (const name2 of guarded) {
26514
+ if (new RegExp(`\\b${name2}\\b`).test(text))
26515
+ return true;
26516
+ }
26517
+ return false;
26518
+ };
26519
+ for (let l = blockEnd + 2;l <= lines.length; l++) {
26520
+ const lineText = lines[l - 1];
26521
+ const assign = assignRe.exec(lineText);
26522
+ if (assign) {
26523
+ const target = assign[1];
26524
+ const rhs = assign[2];
26525
+ if (mentionsGuarded(rhs))
26526
+ guarded.add(target);
26527
+ else if (target !== guardedVar)
26528
+ guarded.delete(target);
26529
+ }
26530
+ if (!mentionsGuarded(lineText))
26531
+ continue;
26532
+ sanitizers.push({
26533
+ type: "python_traversal_reject_guard",
26534
+ method: "if",
26535
+ line: l,
26536
+ sanitizes: ["path_traversal"]
26537
+ });
26538
+ }
26539
+ }
26540
+ return sanitizers;
26541
+ }
26994
26542
  function findPythonDefusedXmlSanitizers(code) {
26995
26543
  const sanitizers = [];
26996
26544
  const lines = code.split(`
@@ -33426,6 +32974,375 @@ class DeserializationSafetyGatePass {
33426
32974
  }
33427
32975
  }
33428
32976
 
32977
+ // ../circle-ir/dist/analysis/passes/prompt-injection-safety-gate-pass.js
32978
+ function isDelimiterLiteral(lit) {
32979
+ const s = lit.trim();
32980
+ if (s.length === 0)
32981
+ return true;
32982
+ return /^<\/?[\w.-]+\s*\/?>$/.test(s) || /^\[\/?[\w.-]+\]$/.test(s) || /^`{1,3}[\w-]*$/.test(s) || /^"""[\w-]*$/.test(s) || /^[#*=_~-]{2,}$/.test(s) || /^<{2,}[\w-]*$/.test(s) || /^[\w-]*>{2,}$/.test(s);
32983
+ }
32984
+ function scanValue(code, start2) {
32985
+ let depth = 0;
32986
+ let str = null;
32987
+ let out2 = "";
32988
+ for (let i2 = start2;i2 < code.length; i2++) {
32989
+ const c = code[i2];
32990
+ if (str) {
32991
+ out2 += c;
32992
+ if (c === str && code[i2 - 1] !== "\\")
32993
+ str = null;
32994
+ continue;
32995
+ }
32996
+ if (c === '"' || c === "'" || c === "`") {
32997
+ str = c;
32998
+ out2 += c;
32999
+ continue;
33000
+ }
33001
+ if (c === "(" || c === "[" || c === "{") {
33002
+ depth++;
33003
+ out2 += c;
33004
+ continue;
33005
+ }
33006
+ if (c === ")" || c === "]" || c === "}") {
33007
+ if (depth === 0)
33008
+ break;
33009
+ depth--;
33010
+ out2 += c;
33011
+ continue;
33012
+ }
33013
+ if (c === "," && depth === 0)
33014
+ break;
33015
+ out2 += c;
33016
+ }
33017
+ return out2.trim();
33018
+ }
33019
+ function stringLiterals(expr) {
33020
+ return [...expr.matchAll(/"([^"]*)"|'([^']*)'/g)].map((m) => m[1] ?? m[2] ?? "");
33021
+ }
33022
+ function hasIdentifier(expr) {
33023
+ return /[A-Za-z_$][\w$]*/.test(expr.replace(/"[^"]*"|'[^']*'|`[^`]*`/g, " "));
33024
+ }
33025
+ function isPureLiteral(valueExpr) {
33026
+ const stripped = valueExpr.replace(/"[^"]*"|'[^']*'|`[^`]*`/g, " ");
33027
+ return !/[A-Za-z_$][\w$]*/.test(stripped);
33028
+ }
33029
+ function isInstructionConcat(valueExpr) {
33030
+ if (!valueExpr.includes("+"))
33031
+ return false;
33032
+ if (!hasIdentifier(valueExpr))
33033
+ return false;
33034
+ const lits = stringLiterals(valueExpr);
33035
+ if (lits.length === 0)
33036
+ return false;
33037
+ return !lits.every(isDelimiterLiteral);
33038
+ }
33039
+ function isMixedTemplate(v) {
33040
+ const fstr = v.match(/\bf(["'])((?:\\.|(?!\1).)*)\1/);
33041
+ if (fstr) {
33042
+ const body2 = fstr[2];
33043
+ if (/\{[^}]+\}/.test(body2) && /\w/.test(body2.replace(/\{[^}]*\}/g, "")))
33044
+ return true;
33045
+ }
33046
+ const tmpl = v.match(/`([^`]*)`/);
33047
+ if (tmpl) {
33048
+ const body2 = tmpl[1];
33049
+ if (/\$\{[^}]+\}/.test(body2) && /\w/.test(body2.replace(/\$\{[^}]*\}/g, "")))
33050
+ return true;
33051
+ }
33052
+ return false;
33053
+ }
33054
+ function assignmentRe(varName, flags2 = "") {
33055
+ return new RegExp(`\\b${varName}\\s*(?::=|(?<![=<>!])=(?!=))\\s*`, flags2);
33056
+ }
33057
+ function resolveAssignmentRHS(varName, codeLines) {
33058
+ const re = assignmentRe(varName);
33059
+ for (const line of codeLines) {
33060
+ const m = line.match(re);
33061
+ if (m && m.index !== undefined) {
33062
+ return scanValue(line, m.index + m[0].length);
33063
+ }
33064
+ }
33065
+ return;
33066
+ }
33067
+ function classifyExpr(v) {
33068
+ if (isMixedTemplate(v))
33069
+ return "unsafe";
33070
+ if (v.includes("+")) {
33071
+ if (isInstructionConcat(v))
33072
+ return "unsafe";
33073
+ const lits = stringLiterals(v);
33074
+ if (lits.length > 0 && lits.every(isDelimiterLiteral) && hasIdentifier(v))
33075
+ return "safe";
33076
+ return "unknown";
33077
+ }
33078
+ return "unknown";
33079
+ }
33080
+ function classifyContentPlacement(val, codeLines) {
33081
+ const v = val.trim();
33082
+ if (/^[A-Za-z_$][\w$]*$/.test(v)) {
33083
+ const rhs = resolveAssignmentRHS(v, codeLines);
33084
+ if (rhs === undefined)
33085
+ return "unknown";
33086
+ return classifyExpr(rhs);
33087
+ }
33088
+ return classifyExpr(v);
33089
+ }
33090
+ function classifyPromptCall(callCode, codeLines) {
33091
+ const contentRe = /["']?[Cc]ontent["']?\s*[:=]\s*/g;
33092
+ let m;
33093
+ let sawDynamic = false;
33094
+ let allSafe = true;
33095
+ while ((m = contentRe.exec(callCode)) !== null) {
33096
+ const val = scanValue(callCode, m.index + m[0].length);
33097
+ if (val === "" || isPureLiteral(val))
33098
+ continue;
33099
+ sawDynamic = true;
33100
+ const ctx = callCode.slice(Math.max(0, m.index - 80), m.index);
33101
+ const roleM = ctx.match(/["']?[Rr]ole["']?\s*[:=]\s*["'](\w+)["'][^"']*$/);
33102
+ const role = roleM ? roleM[1].toLowerCase() : "user";
33103
+ if (role === "system" || role === "assistant")
33104
+ return "unsafe";
33105
+ const placement = classifyContentPlacement(val, codeLines);
33106
+ if (placement === "unsafe")
33107
+ return "unsafe";
33108
+ if (placement !== "safe")
33109
+ allSafe = false;
33110
+ }
33111
+ if (!sawDynamic)
33112
+ return "unknown";
33113
+ return allSafe ? "safe" : "unknown";
33114
+ }
33115
+ function scanRhsAcrossLines(text, start2) {
33116
+ let depth = 0;
33117
+ let str = null;
33118
+ let out2 = "";
33119
+ for (let i2 = start2;i2 < text.length; i2++) {
33120
+ const c = text[i2];
33121
+ if (str) {
33122
+ out2 += c;
33123
+ if (c === str && text[i2 - 1] !== "\\")
33124
+ str = null;
33125
+ continue;
33126
+ }
33127
+ if (c === '"' || c === "'" || c === "`") {
33128
+ str = c;
33129
+ out2 += c;
33130
+ continue;
33131
+ }
33132
+ if (c === "(" || c === "[" || c === "{") {
33133
+ depth++;
33134
+ out2 += c;
33135
+ continue;
33136
+ }
33137
+ if (c === ")" || c === "]" || c === "}") {
33138
+ if (depth === 0)
33139
+ break;
33140
+ depth--;
33141
+ out2 += c;
33142
+ continue;
33143
+ }
33144
+ if (c === `
33145
+ ` && depth === 0)
33146
+ break;
33147
+ out2 += c;
33148
+ }
33149
+ return out2.trim();
33150
+ }
33151
+ function resolveBuilderRhs(varName, joined) {
33152
+ const re = assignmentRe(varName, "g");
33153
+ const m = re.exec(joined);
33154
+ if (m)
33155
+ return scanRhsAcrossLines(joined, m.index + m[0].length);
33156
+ return;
33157
+ }
33158
+ function collectBuilderRegion(callCode, joined) {
33159
+ const parts2 = [];
33160
+ const seen = new Set;
33161
+ const stack = [...callCode.matchAll(/[A-Za-z_$][\w$]*/g)].map((x) => x[0]);
33162
+ let budget = 40;
33163
+ while (stack.length > 0 && budget-- > 0) {
33164
+ const v = stack.pop();
33165
+ if (seen.has(v))
33166
+ continue;
33167
+ seen.add(v);
33168
+ const rhs = resolveBuilderRhs(v, joined);
33169
+ if (rhs) {
33170
+ parts2.push(rhs);
33171
+ for (const mm of rhs.matchAll(/[A-Za-z_$][\w$]*/g))
33172
+ stack.push(mm[0]);
33173
+ }
33174
+ }
33175
+ return parts2.join(`
33176
+ `);
33177
+ }
33178
+ function classifyPromptSink(callCode, codeLines) {
33179
+ const direct = classifyPromptCall(callCode, codeLines);
33180
+ if (direct !== "unknown")
33181
+ return direct;
33182
+ const region = collectBuilderRegion(callCode, codeLines.join(`
33183
+ `));
33184
+ if (!region)
33185
+ return "unknown";
33186
+ return classifyPromptCall(region, codeLines);
33187
+ }
33188
+
33189
+ class PromptInjectionSafetyGatePass {
33190
+ name = "prompt-injection-safety-gate";
33191
+ category = "security";
33192
+ run(ctx) {
33193
+ const { graph, code } = ctx;
33194
+ const sinks = ctx.hasResult("sink-filter") ? ctx.getResult("sink-filter").sinks : graph.ir.taint.sinks;
33195
+ if (sinks.length === 0)
33196
+ return { droppedSafe: 0 };
33197
+ const codeLines = code.split(`
33198
+ `);
33199
+ let droppedSafe = 0;
33200
+ const kept = sinks.filter((sink) => {
33201
+ if (sink.type !== "prompt_injection")
33202
+ return true;
33203
+ const callCode = sink.code ?? codeLines[sink.line - 1] ?? "";
33204
+ if (!callCode)
33205
+ return true;
33206
+ if (classifyPromptSink(callCode, codeLines) === "safe") {
33207
+ droppedSafe++;
33208
+ return false;
33209
+ }
33210
+ return true;
33211
+ });
33212
+ if (droppedSafe > 0) {
33213
+ sinks.length = 0;
33214
+ sinks.push(...kept);
33215
+ }
33216
+ return { droppedSafe };
33217
+ }
33218
+ }
33219
+
33220
+ // ../circle-ir/dist/analysis/passes/speculative-prompt-param-source-pass.js
33221
+ function returnOrAssignRhs(text) {
33222
+ const t = text.trim();
33223
+ const ret = t.match(/^return\s+(.+?);?$/);
33224
+ if (ret)
33225
+ return ret[1];
33226
+ const asg = t.match(/^[A-Za-z_$][\w$.]*\s*(?::=|(?<![=<>!])=(?!=))\s*(.+?);?$/);
33227
+ if (asg)
33228
+ return asg[1];
33229
+ return;
33230
+ }
33231
+ function looksLikePromptText(lit) {
33232
+ const s = lit.trim();
33233
+ if (/\b(you are|you're|assistant|system prompt|follow (the )?(policy|instructions|rules)|act as|your task|instructions?\s*:|ignore (all )?previous|do not reveal|respond (to|with|as)|answer the (question|user)|you (must|should|will)|helpful (ai|assistant))\b/i.test(s)) {
33234
+ return true;
33235
+ }
33236
+ const words = s.split(/\s+/).filter(Boolean);
33237
+ return words.length >= 4 && s.length >= 20;
33238
+ }
33239
+ function literalsOf(expr) {
33240
+ return [...expr.matchAll(/"([^"]*)"|'([^']*)'|`([^`]*)`/g)].map((m) => m[1] ?? m[2] ?? m[3] ?? "");
33241
+ }
33242
+ function detectPromptConstructionFlows(codeLines, types) {
33243
+ const flows = [];
33244
+ const seen = new Set;
33245
+ for (const type of types) {
33246
+ for (const method of type.methods) {
33247
+ const params = (method.parameters ?? []).map((p) => ({ name: p.name, line: p.line ?? method.start_line })).filter((p) => p.name && p.name !== "_");
33248
+ if (params.length === 0)
33249
+ continue;
33250
+ for (let line = method.start_line;line <= method.end_line; line++) {
33251
+ const text = codeLines[line - 1] ?? "";
33252
+ const rhs = returnOrAssignRhs(text);
33253
+ if (!rhs || !isInstructionConcat(rhs))
33254
+ continue;
33255
+ if (!literalsOf(rhs).some(looksLikePromptText))
33256
+ continue;
33257
+ const param = params.find((p) => new RegExp(`\\b${p.name}\\b`).test(rhs));
33258
+ if (!param)
33259
+ continue;
33260
+ const key = `${param.line}|${line}`;
33261
+ if (seen.has(key))
33262
+ continue;
33263
+ seen.add(key);
33264
+ flows.push({
33265
+ source_line: param.line,
33266
+ sink_line: line,
33267
+ source_type: "http_body",
33268
+ sink_type: "prompt_injection",
33269
+ path: [
33270
+ { variable: param.name, line: param.line, type: "source" },
33271
+ { variable: param.name, line, type: "sink" }
33272
+ ],
33273
+ confidence: 1,
33274
+ sanitized: false
33275
+ });
33276
+ }
33277
+ }
33278
+ }
33279
+ return flows;
33280
+ }
33281
+ function looksLikeTextParam(type) {
33282
+ if (!type)
33283
+ return true;
33284
+ const t = type.trim();
33285
+ if (t.startsWith("*") || t.startsWith("&"))
33286
+ return false;
33287
+ if (/\b(Client|Context|Writer|Reader|Conn|DB|Logger|Handler|Server|Request|Response|Pool|Session|Engine|Service|Repository|Config)\b/.test(t)) {
33288
+ return false;
33289
+ }
33290
+ return /(?:^|\b)(string|str|String|text|any|object|interface\{\}|\[\]byte|\[\]string|List\[str\]|Optional\[str\])(?:\b|$)/i.test(t) || /^[A-Za-z_$][\w$<>[\], |]*$/.test(t);
33291
+ }
33292
+
33293
+ class SpeculativePromptParamSourcePass {
33294
+ enabled;
33295
+ name = "speculative-prompt-param-source";
33296
+ category = "security";
33297
+ constructor(enabled) {
33298
+ this.enabled = enabled;
33299
+ }
33300
+ run(ctx) {
33301
+ if (!this.enabled)
33302
+ return { added: 0 };
33303
+ if (!ctx.hasResult("sink-filter"))
33304
+ return { added: 0 };
33305
+ const sinkFilter = ctx.getResult("sink-filter");
33306
+ const { types } = ctx.graph.ir;
33307
+ const promptSinks = sinkFilter.sinks.filter((s) => s.type === "prompt_injection");
33308
+ if (promptSinks.length === 0)
33309
+ return { added: 0 };
33310
+ const existing = new Set(sinkFilter.sources.filter((s) => typeof s.variable === "string").map((s) => `${s.variable}:${s.line}`));
33311
+ const added = [];
33312
+ for (const type of types) {
33313
+ for (const method of type.methods) {
33314
+ const hasPromptSink = promptSinks.some((s) => s.line >= method.start_line && s.line <= method.end_line);
33315
+ if (!hasPromptSink)
33316
+ continue;
33317
+ for (const param of method.parameters) {
33318
+ if (!param.name || param.name === "_")
33319
+ continue;
33320
+ if (!looksLikeTextParam(param.type))
33321
+ continue;
33322
+ const line = param.line ?? method.start_line;
33323
+ const key = `${param.name}:${line}`;
33324
+ if (existing.has(key))
33325
+ continue;
33326
+ existing.add(key);
33327
+ added.push({
33328
+ type: "http_body",
33329
+ location: `speculative untrusted parameter '${param.name}' in ${method.name}`,
33330
+ severity: "high",
33331
+ line,
33332
+ confidence: 1,
33333
+ variable: param.name,
33334
+ in_method: method.name
33335
+ });
33336
+ }
33337
+ }
33338
+ }
33339
+ if (added.length > 0) {
33340
+ sinkFilter.sources.push(...added);
33341
+ }
33342
+ return { added: added.length };
33343
+ }
33344
+ }
33345
+
33429
33346
  // ../circle-ir/dist/analysis/passes/cli-main-reflection-suppress-pass.js
33430
33347
  var REFLECTION_SINK_METHODS = new Set([
33431
33348
  "forName",
@@ -45134,6 +45051,9 @@ async function analyze(code, filePath, language, options = {}) {
45134
45051
  pipeline.add(new SinkSemanticsPass);
45135
45052
  if (!disabledPasses.has("deserialization-safety-gate"))
45136
45053
  pipeline.add(new DeserializationSafetyGatePass(options.dependencyContext));
45054
+ if (!disabledPasses.has("prompt-injection-safety-gate"))
45055
+ pipeline.add(new PromptInjectionSafetyGatePass);
45056
+ pipeline.add(new SpeculativePromptParamSourcePass(options.speculativeParamSources === true));
45137
45057
  if (!disabledPasses.has("cli-main-reflection-suppress"))
45138
45058
  pipeline.add(new CliMainReflectionSuppressPass);
45139
45059
  if (!disabledPasses.has("library-profile-sink-gate"))
@@ -45268,6 +45188,14 @@ async function analyze(code, filePath, language, options = {}) {
45268
45188
  flows: interProc.additionalFlows,
45269
45189
  interprocedural: interProc.interprocedural
45270
45190
  };
45191
+ if (options.speculativeParamSources === true) {
45192
+ const existingFlowKeys = new Set((taint.flows ?? []).map((f) => `${f.source_line}|${f.sink_line}|${f.sink_type}`));
45193
+ const constructionFlows = detectPromptConstructionFlows(code.split(`
45194
+ `), types).filter((f) => !existingFlowKeys.has(`${f.source_line}|${f.sink_line}|${f.sink_type}`));
45195
+ if (constructionFlows.length > 0) {
45196
+ taint.flows = [...taint.flows ?? [], ...constructionFlows];
45197
+ }
45198
+ }
45271
45199
  if (taint.flows && taint.flows.length > 0 && taint.sinks.length > 0) {
45272
45200
  const sinkTagsByKey = new Map;
45273
45201
  for (const s of taint.sinks) {
@@ -46079,7 +46007,7 @@ var colors = {
46079
46007
  };
46080
46008
 
46081
46009
  // src/version.ts
46082
- var version = "3.193.0";
46010
+ var version = "3.195.0";
46083
46011
 
46084
46012
  // src/formatters.ts
46085
46013
  var LIBRARY_API_SURFACE_TAG2 = "library-api-surface:caller-responsibility";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "cognium-dev",
3
- "version": "3.193.0",
3
+ "version": "3.195.0",
4
4
  "description": "Static Application Security Testing CLI for detecting security vulnerabilities via taint tracking",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -66,7 +66,7 @@
66
66
  },
67
67
  "dependencies": {
68
68
  "@cognium/project-profile-detect": "^1.1.0",
69
- "circle-ir": "^3.193.0"
69
+ "circle-ir": "^3.195.0"
70
70
  },
71
71
  "devDependencies": {
72
72
  "@types/node": "^25.5.0",