circle-ir 3.1.0 → 3.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.
@@ -10175,15 +10175,16 @@ function findSources(calls, types, patterns) {
10175
10175
  const skipMethods = ["toString", "hashCode", "equals", "compareTo", "getDescription", "getVulnerabilityCount"];
10176
10176
  if (skipMethods.includes(method.name)) continue;
10177
10177
  for (const param of method.parameters) {
10178
- if (param.type && isInterproceduralTaintableType(param.type)) {
10178
+ const isTaintable = param.type ? isInterproceduralTaintableType(param.type) : true;
10179
+ if (isTaintable) {
10179
10180
  const paramLine = param.line ?? method.start_line;
10180
10181
  sources.push({
10181
10182
  type: "interprocedural_param",
10182
- location: `${param.type} ${param.name} in ${method.name}`,
10183
+ location: `${param.type || "any"} ${param.name} in ${method.name}`,
10183
10184
  severity: "medium",
10184
10185
  line: paramLine,
10185
- confidence: 0.7
10186
- // Lower confidence since we don't know the call site
10186
+ confidence: param.type ? 0.7 : 0.5
10187
+ // Lower confidence for untyped params
10187
10188
  });
10188
10189
  }
10189
10190
  }
@@ -10209,7 +10210,15 @@ function findSources(calls, types, patterns) {
10209
10210
  }
10210
10211
  }
10211
10212
  }
10212
- return sources;
10213
+ const sourceMap = /* @__PURE__ */ new Map();
10214
+ for (const source of sources) {
10215
+ const key = `${source.line}:${source.type}`;
10216
+ const existing = sourceMap.get(key);
10217
+ if (!existing || source.confidence > existing.confidence) {
10218
+ sourceMap.set(key, source);
10219
+ }
10220
+ }
10221
+ return Array.from(sourceMap.values());
10213
10222
  }
10214
10223
  function isInterproceduralTaintableType(typeName) {
10215
10224
  const baseType = typeName.split("<")[0].trim();
@@ -10292,22 +10301,44 @@ function isInterproceduralTaintableType(typeName) {
10292
10301
  }
10293
10302
  return false;
10294
10303
  }
10304
+ function isParameterizedQueryCall(call, pattern) {
10305
+ if (pattern.type !== "sql_injection") return false;
10306
+ if (call.arguments.length < 2) return false;
10307
+ const secondArg = call.arguments.find((a) => a.position === 1);
10308
+ if (!secondArg) return false;
10309
+ if (secondArg.expression) {
10310
+ const expr = secondArg.expression.trim();
10311
+ if (expr.startsWith("[")) {
10312
+ return true;
10313
+ }
10314
+ }
10315
+ return false;
10316
+ }
10295
10317
  function findSinks(calls, patterns) {
10296
- const sinks = [];
10318
+ const sinkMap = /* @__PURE__ */ new Map();
10297
10319
  for (const call of calls) {
10298
10320
  for (const pattern of patterns) {
10299
10321
  if (matchesSinkPattern(call, pattern)) {
10300
- sinks.push({
10301
- type: pattern.type,
10302
- cwe: pattern.cwe,
10303
- location: formatCallLocation(call),
10304
- line: call.location.line,
10305
- confidence: calculateSinkConfidence(call, pattern)
10306
- });
10322
+ if (isParameterizedQueryCall(call, pattern)) {
10323
+ continue;
10324
+ }
10325
+ const location = formatCallLocation(call);
10326
+ const key = `${location}:${call.location.line}:${pattern.cwe}`;
10327
+ const confidence = calculateSinkConfidence(call, pattern);
10328
+ const existing = sinkMap.get(key);
10329
+ if (!existing || confidence > existing.confidence) {
10330
+ sinkMap.set(key, {
10331
+ type: pattern.type,
10332
+ cwe: pattern.cwe,
10333
+ location,
10334
+ line: call.location.line,
10335
+ confidence
10336
+ });
10337
+ }
10307
10338
  }
10308
10339
  }
10309
10340
  }
10310
- return sinks;
10341
+ return Array.from(sinkMap.values());
10311
10342
  }
10312
10343
  function matchesSourcePattern(call, pattern) {
10313
10344
  if (pattern.method) {
@@ -14269,16 +14300,36 @@ var JavaScriptPlugin = class extends BaseLanguagePlugin {
14269
14300
  confidence = Math.max(confidence, 0.95);
14270
14301
  indicators.push(`import: ${path}`);
14271
14302
  }
14272
- if (path === "react" || path.startsWith("react/")) {
14303
+ if (path === "react" || path.startsWith("react/") || path === "react-dom") {
14273
14304
  framework = framework || "react";
14274
14305
  confidence = Math.max(confidence, 0.8);
14275
14306
  indicators.push(`import: ${path}`);
14276
14307
  }
14308
+ if (path === "react-native" || path.startsWith("react-native/") || path.startsWith("@react-native/")) {
14309
+ framework = "react-native";
14310
+ confidence = Math.max(confidence, 0.95);
14311
+ indicators.push(`import: ${path}`);
14312
+ }
14313
+ if (path.startsWith("@react-navigation/")) {
14314
+ framework = framework || "react-native";
14315
+ confidence = Math.max(confidence, 0.9);
14316
+ indicators.push(`import: ${path}`);
14317
+ }
14318
+ if (path === "react-router" || path === "react-router-dom" || path.startsWith("react-router/")) {
14319
+ framework = framework || "react";
14320
+ confidence = Math.max(confidence, 0.85);
14321
+ indicators.push(`import: ${path}`);
14322
+ }
14277
14323
  if (path === "next" || path.startsWith("next/")) {
14278
14324
  framework = "nextjs";
14279
14325
  confidence = Math.max(confidence, 0.9);
14280
14326
  indicators.push(`import: ${path}`);
14281
14327
  }
14328
+ if (path === "expo" || path.startsWith("expo-")) {
14329
+ framework = framework || "react-native";
14330
+ confidence = Math.max(confidence, 0.85);
14331
+ indicators.push(`import: ${path}`);
14332
+ }
14282
14333
  }
14283
14334
  if (framework) {
14284
14335
  return { name: framework, confidence, indicators };
@@ -14390,6 +14441,167 @@ var JavaScriptPlugin = class extends BaseLanguagePlugin {
14390
14441
  severity: "medium",
14391
14442
  confidence: 0.8,
14392
14443
  returnTainted: true
14444
+ },
14445
+ // =========================================================
14446
+ // React Router Sources
14447
+ // =========================================================
14448
+ {
14449
+ method: "useParams",
14450
+ type: "http_path",
14451
+ severity: "high",
14452
+ confidence: 0.95,
14453
+ returnTainted: true
14454
+ },
14455
+ {
14456
+ method: "useSearchParams",
14457
+ type: "http_param",
14458
+ severity: "high",
14459
+ confidence: 0.95,
14460
+ returnTainted: true
14461
+ },
14462
+ {
14463
+ method: "useLocation",
14464
+ type: "url_param",
14465
+ severity: "high",
14466
+ confidence: 0.9,
14467
+ returnTainted: true
14468
+ },
14469
+ // =========================================================
14470
+ // Next.js Sources
14471
+ // =========================================================
14472
+ {
14473
+ method: "useRouter",
14474
+ type: "http_param",
14475
+ severity: "high",
14476
+ confidence: 0.9,
14477
+ returnTainted: true
14478
+ // router.query, router.asPath
14479
+ },
14480
+ {
14481
+ method: "useSearchParams",
14482
+ // Next.js App Router
14483
+ type: "http_param",
14484
+ severity: "high",
14485
+ confidence: 0.95,
14486
+ returnTainted: true
14487
+ },
14488
+ {
14489
+ method: "usePathname",
14490
+ type: "http_path",
14491
+ severity: "high",
14492
+ confidence: 0.9,
14493
+ returnTainted: true
14494
+ },
14495
+ {
14496
+ // getServerSideProps/getStaticProps context.params
14497
+ method: "params",
14498
+ type: "http_path",
14499
+ severity: "high",
14500
+ confidence: 0.85,
14501
+ returnTainted: true
14502
+ },
14503
+ // =========================================================
14504
+ // React Native Sources
14505
+ // =========================================================
14506
+ {
14507
+ // React Navigation route params
14508
+ method: "useRoute",
14509
+ type: "navigation_param",
14510
+ severity: "high",
14511
+ confidence: 0.9,
14512
+ returnTainted: true
14513
+ },
14514
+ {
14515
+ // Deep linking
14516
+ method: "getInitialURL",
14517
+ class: "Linking",
14518
+ type: "url_param",
14519
+ severity: "high",
14520
+ confidence: 0.95,
14521
+ returnTainted: true
14522
+ },
14523
+ {
14524
+ method: "addEventListener",
14525
+ class: "Linking",
14526
+ type: "url_param",
14527
+ severity: "high",
14528
+ confidence: 0.9,
14529
+ returnTainted: true
14530
+ },
14531
+ {
14532
+ method: "parse",
14533
+ class: "Linking",
14534
+ type: "url_param",
14535
+ severity: "high",
14536
+ confidence: 0.9,
14537
+ returnTainted: true
14538
+ },
14539
+ {
14540
+ // Clipboard content
14541
+ method: "getString",
14542
+ class: "Clipboard",
14543
+ type: "user_input",
14544
+ severity: "medium",
14545
+ confidence: 0.85,
14546
+ returnTainted: true
14547
+ },
14548
+ {
14549
+ method: "getStringAsync",
14550
+ class: "Clipboard",
14551
+ type: "user_input",
14552
+ severity: "medium",
14553
+ confidence: 0.85,
14554
+ returnTainted: true
14555
+ },
14556
+ {
14557
+ // AsyncStorage (may contain user data)
14558
+ method: "getItem",
14559
+ class: "AsyncStorage",
14560
+ type: "storage_input",
14561
+ severity: "medium",
14562
+ confidence: 0.7,
14563
+ returnTainted: true
14564
+ },
14565
+ {
14566
+ method: "multiGet",
14567
+ class: "AsyncStorage",
14568
+ type: "storage_input",
14569
+ severity: "medium",
14570
+ confidence: 0.7,
14571
+ returnTainted: true
14572
+ },
14573
+ {
14574
+ // SecureStore (Expo)
14575
+ method: "getItemAsync",
14576
+ class: "SecureStore",
14577
+ type: "storage_input",
14578
+ severity: "medium",
14579
+ confidence: 0.7,
14580
+ returnTainted: true
14581
+ },
14582
+ // =========================================================
14583
+ // Browser/DOM Sources (React web apps)
14584
+ // =========================================================
14585
+ {
14586
+ method: "localStorage.getItem",
14587
+ type: "storage_input",
14588
+ severity: "medium",
14589
+ confidence: 0.7,
14590
+ returnTainted: true
14591
+ },
14592
+ {
14593
+ method: "sessionStorage.getItem",
14594
+ type: "storage_input",
14595
+ severity: "medium",
14596
+ confidence: 0.7,
14597
+ returnTainted: true
14598
+ },
14599
+ {
14600
+ method: "postMessage",
14601
+ type: "message_input",
14602
+ severity: "high",
14603
+ confidence: 0.85,
14604
+ returnTainted: true
14393
14605
  }
14394
14606
  ];
14395
14607
  }
@@ -14569,6 +14781,158 @@ var JavaScriptPlugin = class extends BaseLanguagePlugin {
14569
14781
  cwe: "CWE-1321",
14570
14782
  severity: "high",
14571
14783
  argPositions: [0, 1]
14784
+ },
14785
+ // =========================================================
14786
+ // React XSS Sinks
14787
+ // =========================================================
14788
+ {
14789
+ // Most common React XSS vector
14790
+ method: "dangerouslySetInnerHTML",
14791
+ type: "xss",
14792
+ cwe: "CWE-79",
14793
+ severity: "critical",
14794
+ argPositions: [0]
14795
+ // The __html property value
14796
+ },
14797
+ {
14798
+ // Rendering user-controlled href with javascript:
14799
+ method: "href",
14800
+ type: "xss",
14801
+ cwe: "CWE-79",
14802
+ severity: "high",
14803
+ argPositions: [0]
14804
+ },
14805
+ {
14806
+ // createRef().current.innerHTML
14807
+ method: "current.innerHTML",
14808
+ type: "xss",
14809
+ cwe: "CWE-79",
14810
+ severity: "high",
14811
+ argPositions: [0]
14812
+ },
14813
+ // =========================================================
14814
+ // React Native Sinks
14815
+ // =========================================================
14816
+ {
14817
+ // WebView with user-controlled source
14818
+ method: "source",
14819
+ class: "WebView",
14820
+ type: "xss",
14821
+ cwe: "CWE-79",
14822
+ severity: "critical",
14823
+ argPositions: [0]
14824
+ // { html: userInput } or { uri: userInput }
14825
+ },
14826
+ {
14827
+ // Open arbitrary URLs
14828
+ method: "openURL",
14829
+ class: "Linking",
14830
+ type: "open_redirect",
14831
+ cwe: "CWE-601",
14832
+ severity: "high",
14833
+ argPositions: [0]
14834
+ },
14835
+ {
14836
+ method: "canOpenURL",
14837
+ class: "Linking",
14838
+ type: "ssrf",
14839
+ cwe: "CWE-918",
14840
+ severity: "medium",
14841
+ argPositions: [0]
14842
+ },
14843
+ {
14844
+ // Expo WebBrowser
14845
+ method: "openBrowserAsync",
14846
+ class: "WebBrowser",
14847
+ type: "open_redirect",
14848
+ cwe: "CWE-601",
14849
+ severity: "high",
14850
+ argPositions: [0]
14851
+ },
14852
+ {
14853
+ method: "openAuthSessionAsync",
14854
+ class: "WebBrowser",
14855
+ type: "open_redirect",
14856
+ cwe: "CWE-601",
14857
+ severity: "high",
14858
+ argPositions: [0]
14859
+ },
14860
+ // =========================================================
14861
+ // Next.js Sinks
14862
+ // =========================================================
14863
+ {
14864
+ // Server-side redirect
14865
+ method: "redirect",
14866
+ type: "open_redirect",
14867
+ cwe: "CWE-601",
14868
+ severity: "high",
14869
+ argPositions: [0]
14870
+ },
14871
+ {
14872
+ // Router push with user-controlled URL
14873
+ method: "push",
14874
+ class: "router",
14875
+ type: "open_redirect",
14876
+ cwe: "CWE-601",
14877
+ severity: "medium",
14878
+ argPositions: [0]
14879
+ },
14880
+ {
14881
+ method: "replace",
14882
+ class: "router",
14883
+ type: "open_redirect",
14884
+ cwe: "CWE-601",
14885
+ severity: "medium",
14886
+ argPositions: [0]
14887
+ },
14888
+ // =========================================================
14889
+ // React/General JS Security Sinks
14890
+ // =========================================================
14891
+ {
14892
+ // Dynamic component loading
14893
+ method: "createElement",
14894
+ class: "React",
14895
+ type: "code_injection",
14896
+ cwe: "CWE-94",
14897
+ severity: "high",
14898
+ argPositions: [0]
14899
+ // When first arg is user-controlled string
14900
+ },
14901
+ {
14902
+ // Importing user-controlled modules
14903
+ method: "import",
14904
+ type: "code_injection",
14905
+ cwe: "CWE-94",
14906
+ severity: "critical",
14907
+ argPositions: [0]
14908
+ },
14909
+ {
14910
+ method: "require",
14911
+ type: "code_injection",
14912
+ cwe: "CWE-94",
14913
+ severity: "critical",
14914
+ argPositions: [0]
14915
+ },
14916
+ // =========================================================
14917
+ // Data Exposure Sinks (React Native)
14918
+ // =========================================================
14919
+ {
14920
+ // Logging sensitive data
14921
+ method: "log",
14922
+ class: "console",
14923
+ type: "information_exposure",
14924
+ cwe: "CWE-532",
14925
+ severity: "low",
14926
+ argPositions: [0]
14927
+ },
14928
+ {
14929
+ // Storing sensitive data insecurely
14930
+ method: "setItem",
14931
+ class: "AsyncStorage",
14932
+ type: "insecure_storage",
14933
+ cwe: "CWE-922",
14934
+ severity: "medium",
14935
+ argPositions: [1]
14572
14936
  }
14573
14937
  ];
14574
14938
  }
@@ -15650,6 +16014,113 @@ function findGetterSources(types, instanceFieldTaint, sourceCode) {
15650
16014
  }
15651
16015
  return sources;
15652
16016
  }
16017
+ var JS_DOM_XSS_SINKS = [
16018
+ { pattern: /\.innerHTML\s*=/, type: "xss", cwe: "CWE-79", severity: "critical" },
16019
+ { pattern: /\.outerHTML\s*=/, type: "xss", cwe: "CWE-79", severity: "critical" },
16020
+ { pattern: /document\.write\s*\(/, type: "xss", cwe: "CWE-79", severity: "critical" },
16021
+ { pattern: /document\.writeln\s*\(/, type: "xss", cwe: "CWE-79", severity: "critical" },
16022
+ { pattern: /\.insertAdjacentHTML\s*\(/, type: "xss", cwe: "CWE-79", severity: "critical" },
16023
+ { pattern: /\.src\s*=/, type: "xss", cwe: "CWE-79", severity: "high" },
16024
+ { pattern: /\.href\s*=/, type: "xss", cwe: "CWE-79", severity: "high" }
16025
+ ];
16026
+ var JS_TAINTED_PATTERNS = [
16027
+ { pattern: /\breq\.query\b/, type: "http_param" },
16028
+ { pattern: /\breq\.params\b/, type: "http_param" },
16029
+ { pattern: /\breq\.body\b/, type: "http_body" },
16030
+ { pattern: /\breq\.headers\b/, type: "http_header" },
16031
+ { pattern: /\breq\.cookies\b/, type: "http_cookie" },
16032
+ { pattern: /\breq\.url\b/, type: "http_path" },
16033
+ { pattern: /\breq\.path\b/, type: "http_path" },
16034
+ { pattern: /\breq\.originalUrl\b/, type: "http_path" },
16035
+ { pattern: /\breq\.files?\b/, type: "file_input" },
16036
+ { pattern: /\brequest\.query\b/, type: "http_param" },
16037
+ { pattern: /\brequest\.params\b/, type: "http_param" },
16038
+ { pattern: /\brequest\.body\b/, type: "http_body" },
16039
+ { pattern: /\brequest\.headers\b/, type: "http_header" },
16040
+ { pattern: /\bctx\.query\b/, type: "http_param" },
16041
+ { pattern: /\bctx\.params\b/, type: "http_param" },
16042
+ { pattern: /\bctx\.request\b/, type: "http_body" },
16043
+ { pattern: /\bprocess\.env\b/, type: "env_input" },
16044
+ { pattern: /\bprocess\.argv\b/, type: "io_input" },
16045
+ { pattern: /\blocation\.search\b/, type: "http_param" },
16046
+ { pattern: /\blocation\.hash\b/, type: "http_param" },
16047
+ { pattern: /\blocation\.href\b/, type: "http_path" },
16048
+ { pattern: /\bdocument\.getElementById\b/, type: "dom_input" },
16049
+ { pattern: /\bdocument\.querySelector\b/, type: "dom_input" },
16050
+ { pattern: /\.value\b/, type: "dom_input" }
16051
+ ];
16052
+ function findJavaScriptAssignmentSources(sourceCode, language) {
16053
+ const sources = [];
16054
+ if (!["javascript", "typescript"].includes(language)) {
16055
+ return sources;
16056
+ }
16057
+ const lines = sourceCode.split("\n");
16058
+ for (let lineNum = 0; lineNum < lines.length; lineNum++) {
16059
+ const line = lines[lineNum];
16060
+ const lineNumber = lineNum + 1;
16061
+ const assignmentMatch = line.match(/(?:(?:var|let|const)\s+)?(\w+)\s*=\s*(.+)/);
16062
+ if (assignmentMatch) {
16063
+ const varName = assignmentMatch[1];
16064
+ const rhs = assignmentMatch[2];
16065
+ for (const { pattern, type } of JS_TAINTED_PATTERNS) {
16066
+ if (pattern.test(rhs)) {
16067
+ const alreadyExists = sources.some(
16068
+ (s) => s.line === lineNumber && s.type === type
16069
+ );
16070
+ if (!alreadyExists) {
16071
+ sources.push({
16072
+ type,
16073
+ location: `${varName} = ${rhs.trim().substring(0, 50)}${rhs.length > 50 ? "..." : ""}`,
16074
+ severity: "high",
16075
+ line: lineNumber,
16076
+ confidence: 1,
16077
+ variable: varName
16078
+ });
16079
+ }
16080
+ break;
16081
+ }
16082
+ }
16083
+ }
16084
+ }
16085
+ return sources;
16086
+ }
16087
+ function findJavaScriptDOMSinks(sourceCode, language) {
16088
+ const sinks = [];
16089
+ if (!["javascript", "typescript"].includes(language)) {
16090
+ return sinks;
16091
+ }
16092
+ const lines = sourceCode.split("\n");
16093
+ for (let lineNum = 0; lineNum < lines.length; lineNum++) {
16094
+ const line = lines[lineNum];
16095
+ const lineNumber = lineNum + 1;
16096
+ for (const { pattern, type, cwe, severity } of JS_DOM_XSS_SINKS) {
16097
+ if (pattern.test(line)) {
16098
+ let method = "innerHTML";
16099
+ if (line.includes(".outerHTML")) method = "outerHTML";
16100
+ else if (line.includes("document.write(")) method = "document.write";
16101
+ else if (line.includes("document.writeln(")) method = "document.writeln";
16102
+ else if (line.includes(".insertAdjacentHTML")) method = "insertAdjacentHTML";
16103
+ else if (line.includes(".src")) method = "src";
16104
+ else if (line.includes(".href")) method = "href";
16105
+ const alreadyExists = sinks.some(
16106
+ (s) => s.line === lineNumber && s.cwe === cwe
16107
+ );
16108
+ if (!alreadyExists) {
16109
+ sinks.push({
16110
+ type,
16111
+ cwe,
16112
+ severity,
16113
+ line: lineNumber,
16114
+ location: line.trim().substring(0, 80),
16115
+ method
16116
+ });
16117
+ }
16118
+ break;
16119
+ }
16120
+ }
16121
+ }
16122
+ return sinks;
16123
+ }
15653
16124
  var initialized = false;
15654
16125
  async function initAnalyzer(options = {}) {
15655
16126
  if (initialized) return;
@@ -15802,11 +16273,30 @@ async function analyze(code, filePath, language, options = {}) {
15802
16273
  const taint = analyzeTaint(calls, types, baseConfig);
15803
16274
  const getterSources = findGetterSources(types, constPropResult.instanceFieldTaint, code);
15804
16275
  taint.sources.push(...getterSources);
16276
+ const jsAssignmentSources = findJavaScriptAssignmentSources(code, language);
16277
+ taint.sources.push(...jsAssignmentSources);
16278
+ const jsDOMSinks = findJavaScriptDOMSinks(code, language);
16279
+ for (const domSink of jsDOMSinks) {
16280
+ const alreadyExists = taint.sinks.some(
16281
+ (s) => s.line === domSink.line && s.cwe === domSink.cwe
16282
+ );
16283
+ if (!alreadyExists) {
16284
+ taint.sinks.push({
16285
+ type: "xss",
16286
+ cwe: domSink.cwe,
16287
+ line: domSink.line,
16288
+ location: domSink.location,
16289
+ method: domSink.method,
16290
+ confidence: 1
16291
+ });
16292
+ }
16293
+ }
15805
16294
  logger.debug("Initial taint analysis", {
15806
16295
  sources: taint.sources.length,
15807
16296
  sinks: taint.sinks.length,
15808
16297
  sanitizers: taint.sanitizers?.length ?? 0,
15809
- getterSources: getterSources.length
16298
+ getterSources: getterSources.length,
16299
+ jsDOMSinks: jsDOMSinks.length
15810
16300
  });
15811
16301
  taint.sinks = taint.sinks.filter((sink) => !constPropResult.unreachableLines.has(sink.line));
15812
16302
  taint.sinks = filterCleanArraySinks(
@@ -15948,6 +16438,49 @@ async function analyze(code, filePath, language, options = {}) {
15948
16438
  taint.sinks.push(sink);
15949
16439
  }
15950
16440
  }
16441
+ if (interProc.propagatedSinks.length > 0 && taint.sources.length > 0) {
16442
+ if (!taint.flows) {
16443
+ taint.flows = [];
16444
+ }
16445
+ const sanitizerMethodNames = /* @__PURE__ */ new Set();
16446
+ for (const san of taint.sanitizers ?? []) {
16447
+ if (san.type === "javadoc_sanitizer") {
16448
+ const match = san.method.match(/^(\w+)\(\)$/);
16449
+ if (match) sanitizerMethodNames.add(match[1]);
16450
+ else sanitizerMethodNames.add(san.method);
16451
+ }
16452
+ }
16453
+ for (const sink of interProc.propagatedSinks) {
16454
+ if (sink.type === "external_taint_escape") continue;
16455
+ for (const edge of interProc.callEdges) {
16456
+ if (!interProc.taintedMethods.has(edge.calleeMethod)) continue;
16457
+ const method = interProc.methodNodes.get(edge.calleeMethod);
16458
+ if (!method) continue;
16459
+ if (sink.line < method.startLine || sink.line > method.endLine) continue;
16460
+ if (sanitizerMethodNames.has(method.name)) continue;
16461
+ for (const source of taint.sources) {
16462
+ if (source.line > edge.callLine) continue;
16463
+ if (source.type === "interprocedural_param" && source.confidence < 0.6) continue;
16464
+ if (taint.flows.some((f) => f.source_line === source.line && f.sink_line === sink.line)) continue;
16465
+ taint.flows.push({
16466
+ source_line: source.line,
16467
+ sink_line: sink.line,
16468
+ source_type: source.type,
16469
+ sink_type: sink.type,
16470
+ path: [
16471
+ { variable: source.location, line: source.line, type: "source" },
16472
+ { variable: `call to ${method.name}()`, line: edge.callLine, type: "use" },
16473
+ { variable: sink.location, line: sink.line, type: "sink" }
16474
+ ],
16475
+ confidence: sink.confidence * source.confidence * 0.85,
16476
+ sanitized: false
16477
+ });
16478
+ break;
16479
+ }
16480
+ break;
16481
+ }
16482
+ }
16483
+ }
15951
16484
  const taintBridges = findTaintBridges(interProc);
15952
16485
  taint.interprocedural = {
15953
16486
  tainted_methods: Array.from(interProc.taintedMethods),