circle-ir 3.136.0 → 3.137.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.
@@ -23684,6 +23684,12 @@ var LanguageSourcesPass = class {
23684
23684
  )) {
23685
23685
  ctx.addFinding(finding);
23686
23686
  }
23687
+ for (const finding of findGoLdapInjectionFindings(
23688
+ code,
23689
+ graph.ir.meta.file
23690
+ )) {
23691
+ ctx.addFinding(finding);
23692
+ }
23687
23693
  }
23688
23694
  if (language === "python") {
23689
23695
  additionalSanitizers.push(...findPythonNetlocAllowlistGuardSanitizers(code));
@@ -23765,6 +23771,18 @@ var LanguageSourcesPass = class {
23765
23771
  )) {
23766
23772
  ctx.addFinding(finding);
23767
23773
  }
23774
+ for (const finding of findRustLdapInjectionFindings(
23775
+ code,
23776
+ graph.ir.meta.file
23777
+ )) {
23778
+ ctx.addFinding(finding);
23779
+ }
23780
+ for (const finding of findRustLogInjectionFindings(
23781
+ code,
23782
+ graph.ir.meta.file
23783
+ )) {
23784
+ ctx.addFinding(finding);
23785
+ }
23768
23786
  }
23769
23787
  if (language === "javascript" || language === "typescript" || language === "htmljs") {
23770
23788
  additionalSanitizers.push(...findJsSafeJsonParseSanitizers(code));
@@ -23801,6 +23819,12 @@ var LanguageSourcesPass = class {
23801
23819
  )) {
23802
23820
  ctx.addFinding(finding);
23803
23821
  }
23822
+ for (const finding of findJsLdapInjectionFindings(
23823
+ code,
23824
+ graph.ir.meta.file
23825
+ )) {
23826
+ ctx.addFinding(finding);
23827
+ }
23804
23828
  }
23805
23829
  if (language === "java") {
23806
23830
  additionalSanitizers.push(...findJavaSafeJsonParseSanitizers(code));
@@ -27923,6 +27947,513 @@ function findPythonHeaderCrlfInjectionFindings(code, file) {
27923
27947
  }
27924
27948
  return findings;
27925
27949
  }
27950
+ function findJsLdapInjectionFindings(code, file) {
27951
+ const findings = [];
27952
+ if (typeof code !== "string" || code.length === 0) return findings;
27953
+ if (!/\.\s*search\s*\(/.test(code)) return findings;
27954
+ if (!/\breq\s*\.\s*(?:query|body|params|headers|cookies)\b/.test(code)) {
27955
+ return findings;
27956
+ }
27957
+ if (!/\b(?:ldapjs|ldapts|require\(['"]ldapjs['"]\)|from\s+['"]ldapts['"])\b/.test(
27958
+ code
27959
+ )) {
27960
+ return findings;
27961
+ }
27962
+ const lines = code.split("\n");
27963
+ const reqExtractRe = /\breq\s*\.\s*(?:query|body|params|headers|cookies)\b/;
27964
+ const taintedVars = /* @__PURE__ */ new Set();
27965
+ for (let pass = 0; pass < 3; pass++) {
27966
+ const before = taintedVars.size;
27967
+ for (let i2 = 0; i2 < lines.length; i2++) {
27968
+ const t = lines[i2].trim();
27969
+ if (t.startsWith("//")) continue;
27970
+ const m = t.match(/^(?:const|let|var)\s+(\w+)(?:\s*:\s*[^=]+)?\s*=\s*(.+?);?$/);
27971
+ if (!m) continue;
27972
+ const lhs = m[1];
27973
+ const rhs = m[2];
27974
+ if (taintedVars.has(lhs)) continue;
27975
+ if (reqExtractRe.test(rhs)) {
27976
+ taintedVars.add(lhs);
27977
+ continue;
27978
+ }
27979
+ for (const v of taintedVars) {
27980
+ if (new RegExp(`\\b${v}\\b`).test(rhs)) {
27981
+ taintedVars.add(lhs);
27982
+ break;
27983
+ }
27984
+ }
27985
+ }
27986
+ if (taintedVars.size === before) break;
27987
+ }
27988
+ if (taintedVars.size === 0) return findings;
27989
+ const filterPropRe = /\bfilter\s*:\s*([^,}\n]+)/;
27990
+ const filterShorthandRe = /(?:^|[\{,])\s*filter\s*[,}]/;
27991
+ const seen = /* @__PURE__ */ new Set();
27992
+ for (let i2 = 0; i2 < lines.length; i2++) {
27993
+ const line = lines[i2];
27994
+ const fm = line.match(filterPropRe);
27995
+ let expr = null;
27996
+ if (fm) {
27997
+ expr = fm[1].trim();
27998
+ } else if (filterShorthandRe.test(line)) {
27999
+ expr = "filter";
28000
+ } else {
28001
+ continue;
28002
+ }
28003
+ let tainted = false;
28004
+ for (const v of taintedVars) {
28005
+ if (new RegExp(`\\b${v}\\b`).test(expr)) {
28006
+ tainted = true;
28007
+ break;
28008
+ }
28009
+ }
28010
+ if (!tainted) continue;
28011
+ if (seen.has(i2 + 1)) continue;
28012
+ seen.add(i2 + 1);
28013
+ findings.push({
28014
+ id: `ldap_injection-${file}-${i2 + 1}-js-ldap-filter`,
28015
+ pass: "language-sources",
28016
+ category: "security",
28017
+ rule_id: "ldap_injection",
28018
+ cwe: "CWE-90",
28019
+ severity: "critical",
28020
+ level: "error",
28021
+ message: "LDAP injection: ldapjs/ldapts `client.search(..., { filter: <tainted>, ... })` lets the attacker break out of the filter expression (e.g. `*)(uid=*`) and enumerate the directory. Escape the user input with a tight allowlist (`[A-Za-z0-9_-]+`) or use a structured filter builder.",
28022
+ file,
28023
+ line: i2 + 1,
28024
+ snippet: line.trim()
28025
+ });
28026
+ }
28027
+ return findings;
28028
+ }
28029
+ function findGoLdapInjectionFindings(code, file) {
28030
+ const findings = [];
28031
+ if (typeof code !== "string" || code.length === 0) return findings;
28032
+ if (!/\bldap\s*\.\s*NewSearchRequest\s*\(/.test(code)) return findings;
28033
+ if (!/\br\s*\.\s*(?:URL\s*\.\s*Query\s*\(\s*\)|Form|PostForm|FormValue|PostFormValue|Header)/.test(
28034
+ code
28035
+ )) {
28036
+ return findings;
28037
+ }
28038
+ const lines = code.split("\n");
28039
+ const reqExtractRe = /\br\s*\.\s*(?:URL\s*\.\s*Query\s*\(\s*\)\s*\.\s*Get|Form\s*\.\s*Get|PostForm\s*\.\s*Get|FormValue|PostFormValue|Header\s*\.\s*Get)\s*\(/;
28040
+ const taintedVars = /* @__PURE__ */ new Set();
28041
+ for (let pass = 0; pass < 3; pass++) {
28042
+ const before = taintedVars.size;
28043
+ for (let i2 = 0; i2 < lines.length; i2++) {
28044
+ const t = lines[i2].trim();
28045
+ if (t.startsWith("//")) continue;
28046
+ const m = t.match(/^(\w+)\s*(?::=|=)\s*(.+?)$/);
28047
+ if (!m) continue;
28048
+ const lhs = m[1];
28049
+ const rhs = m[2];
28050
+ if (taintedVars.has(lhs)) continue;
28051
+ if (reqExtractRe.test(rhs)) {
28052
+ taintedVars.add(lhs);
28053
+ continue;
28054
+ }
28055
+ for (const v of taintedVars) {
28056
+ if (new RegExp(`\\b${v}\\b`).test(rhs)) {
28057
+ taintedVars.add(lhs);
28058
+ break;
28059
+ }
28060
+ }
28061
+ }
28062
+ if (taintedVars.size === before) break;
28063
+ }
28064
+ if (taintedVars.size === 0) return findings;
28065
+ const seen = /* @__PURE__ */ new Set();
28066
+ for (let i2 = 0; i2 < lines.length; i2++) {
28067
+ const openIdx = lines[i2].indexOf("NewSearchRequest(");
28068
+ if (openIdx === -1) continue;
28069
+ let depth = 0;
28070
+ let buf = "";
28071
+ let endLine = i2;
28072
+ let started = false;
28073
+ for (let j = i2; j < Math.min(i2 + 25, lines.length); j++) {
28074
+ for (const ch of lines[j]) {
28075
+ if (ch === "(") {
28076
+ depth++;
28077
+ started = true;
28078
+ } else if (ch === ")") {
28079
+ depth--;
28080
+ }
28081
+ buf += ch;
28082
+ if (started && depth === 0) {
28083
+ endLine = j;
28084
+ break;
28085
+ }
28086
+ }
28087
+ if (started && depth === 0) break;
28088
+ buf += "\n";
28089
+ }
28090
+ const argsStart = buf.indexOf("(");
28091
+ if (argsStart === -1) continue;
28092
+ const args2 = buf.substring(argsStart + 1, buf.length - 1);
28093
+ const parts2 = [];
28094
+ {
28095
+ let d = 0;
28096
+ let b = "";
28097
+ let inStr = null;
28098
+ for (let k = 0; k < args2.length; k++) {
28099
+ const ch = args2[k];
28100
+ if (inStr) {
28101
+ if (ch === "\\") {
28102
+ b += ch + (args2[k + 1] ?? "");
28103
+ k++;
28104
+ continue;
28105
+ }
28106
+ if (ch === inStr) inStr = null;
28107
+ b += ch;
28108
+ continue;
28109
+ }
28110
+ if (ch === '"' || ch === "`") {
28111
+ inStr = ch;
28112
+ b += ch;
28113
+ continue;
28114
+ }
28115
+ if (ch === "(" || ch === "[" || ch === "{") d++;
28116
+ else if (ch === ")" || ch === "]" || ch === "}") d--;
28117
+ if (ch === "," && d === 0) {
28118
+ parts2.push(b);
28119
+ b = "";
28120
+ continue;
28121
+ }
28122
+ b += ch;
28123
+ }
28124
+ if (b.trim().length > 0) parts2.push(b);
28125
+ }
28126
+ if (parts2.length < 7) continue;
28127
+ const filterExpr = parts2[6].trim();
28128
+ let tainted = false;
28129
+ for (const v of taintedVars) {
28130
+ if (new RegExp(`\\b${v}\\b`).test(filterExpr)) {
28131
+ tainted = true;
28132
+ break;
28133
+ }
28134
+ }
28135
+ if (!tainted) continue;
28136
+ if (seen.has(i2 + 1)) continue;
28137
+ seen.add(i2 + 1);
28138
+ findings.push({
28139
+ id: `ldap_injection-${file}-${i2 + 1}-go-newsearchrequest`,
28140
+ pass: "language-sources",
28141
+ category: "security",
28142
+ rule_id: "ldap_injection",
28143
+ cwe: "CWE-90",
28144
+ severity: "critical",
28145
+ level: "error",
28146
+ message: "LDAP injection: go-ldap `ldap.NewSearchRequest(..., <tainted filter>, ...)` lets the attacker break out of the filter expression and enumerate the directory. Escape the user input with `ldap.EscapeFilter(...)` or use a structured filter builder.",
28147
+ file,
28148
+ line: i2 + 1,
28149
+ snippet: (lines[i2] + (endLine > i2 ? " \u2026" : "")).trim()
28150
+ });
28151
+ }
28152
+ return findings;
28153
+ }
28154
+ function findRustLdapInjectionFindings(code, file) {
28155
+ const findings = [];
28156
+ if (typeof code !== "string" || code.length === 0) return findings;
28157
+ if (!/\.\s*search\s*\(/.test(code)) return findings;
28158
+ if (!/\b(?:ldap3|LdapConn|Ldap)\b/.test(code)) return findings;
28159
+ const lines = code.split("\n");
28160
+ const extractorTypeRe = /:\s*(?:String|Bytes|bytes::Bytes|axum::body::Bytes|web::Query\b|web::Path\b|web::Form\b|web::Json\b|HttpRequest\b|actix_web::HttpRequest\b)/;
28161
+ const fns = [];
28162
+ let cur = null;
28163
+ for (let i2 = 0; i2 < lines.length; i2++) {
28164
+ const t = lines[i2];
28165
+ if (/^\s*(?:pub\s+)?(?:async\s+)?fn\s+\w+\s*\(/.test(t)) {
28166
+ if (cur) {
28167
+ cur.end = i2 - 1;
28168
+ fns.push(cur);
28169
+ }
28170
+ cur = { start: i2, end: lines.length - 1, tainted: /* @__PURE__ */ new Set() };
28171
+ let headerJoined = "";
28172
+ for (let j = i2; j < Math.min(i2 + 12, lines.length); j++) {
28173
+ headerJoined += lines[j];
28174
+ if (/\{\s*$/.test(lines[j])) break;
28175
+ }
28176
+ const open = headerJoined.indexOf("(");
28177
+ const close = headerJoined.lastIndexOf(")");
28178
+ if (open !== -1 && close > open) {
28179
+ const params = headerJoined.substring(open + 1, close);
28180
+ let depth = 0;
28181
+ let buf = "";
28182
+ const parts2 = [];
28183
+ for (const ch of params) {
28184
+ if (ch === "<" || ch === "(") depth++;
28185
+ else if (ch === ">" || ch === ")") depth--;
28186
+ if (ch === "," && depth === 0) {
28187
+ parts2.push(buf);
28188
+ buf = "";
28189
+ continue;
28190
+ }
28191
+ buf += ch;
28192
+ }
28193
+ if (buf.trim().length > 0) parts2.push(buf);
28194
+ for (const p of parts2) {
28195
+ const pm = p.match(/(?:mut\s+)?(\w+)\s*:/);
28196
+ if (!pm) continue;
28197
+ if (extractorTypeRe.test(p)) cur.tainted.add(pm[1]);
28198
+ }
28199
+ }
28200
+ }
28201
+ }
28202
+ if (cur) fns.push(cur);
28203
+ for (const fn of fns) {
28204
+ for (let pass = 0; pass < 3; pass++) {
28205
+ const before = fn.tainted.size;
28206
+ for (let i2 = fn.start; i2 <= fn.end; i2++) {
28207
+ const t = lines[i2].trim();
28208
+ const m = t.match(/^let\s+(?:mut\s+)?(\w+)\s*(?::\s*[^=]+)?=\s*(.+?);?$/);
28209
+ if (!m) continue;
28210
+ const lhs = m[1];
28211
+ const rhs = m[2];
28212
+ if (fn.tainted.has(lhs)) continue;
28213
+ for (const v of fn.tainted) {
28214
+ if (new RegExp(`\\b${v}\\b`).test(rhs)) {
28215
+ fn.tainted.add(lhs);
28216
+ break;
28217
+ }
28218
+ }
28219
+ }
28220
+ if (fn.tainted.size === before) break;
28221
+ }
28222
+ }
28223
+ const seen = /* @__PURE__ */ new Set();
28224
+ for (const fn of fns) {
28225
+ if (fn.tainted.size === 0) continue;
28226
+ for (let i2 = fn.start; i2 <= fn.end; i2++) {
28227
+ const searchIdx = lines[i2].indexOf(".search(");
28228
+ if (searchIdx === -1) continue;
28229
+ let depth = 0;
28230
+ let buf = "";
28231
+ let started = false;
28232
+ for (let j = i2; j < Math.min(i2 + 15, lines.length); j++) {
28233
+ const startCol = j === i2 ? searchIdx : 0;
28234
+ for (let k = startCol; k < lines[j].length; k++) {
28235
+ const ch = lines[j][k];
28236
+ if (ch === "(") {
28237
+ depth++;
28238
+ started = true;
28239
+ } else if (ch === ")") {
28240
+ depth--;
28241
+ }
28242
+ buf += ch;
28243
+ if (started && depth === 0) break;
28244
+ }
28245
+ if (started && depth === 0) break;
28246
+ buf += "\n";
28247
+ }
28248
+ const argsStart = buf.indexOf("(");
28249
+ if (argsStart === -1) continue;
28250
+ const args2 = buf.substring(argsStart + 1, buf.length - 1);
28251
+ const parts2 = [];
28252
+ {
28253
+ let d = 0;
28254
+ let b = "";
28255
+ let inStr = null;
28256
+ for (let k = 0; k < args2.length; k++) {
28257
+ const ch = args2[k];
28258
+ if (inStr) {
28259
+ if (ch === "\\") {
28260
+ b += ch + (args2[k + 1] ?? "");
28261
+ k++;
28262
+ continue;
28263
+ }
28264
+ if (ch === inStr) inStr = null;
28265
+ b += ch;
28266
+ continue;
28267
+ }
28268
+ if (ch === '"') {
28269
+ inStr = ch;
28270
+ b += ch;
28271
+ continue;
28272
+ }
28273
+ if (ch === "(" || ch === "[" || ch === "{") d++;
28274
+ else if (ch === ")" || ch === "]" || ch === "}") d--;
28275
+ if (ch === "," && d === 0) {
28276
+ parts2.push(b);
28277
+ b = "";
28278
+ continue;
28279
+ }
28280
+ b += ch;
28281
+ }
28282
+ if (b.trim().length > 0) parts2.push(b);
28283
+ }
28284
+ if (parts2.length < 3) continue;
28285
+ const filterExpr = parts2[2].trim();
28286
+ let tainted = false;
28287
+ for (const v of fn.tainted) {
28288
+ if (new RegExp(`\\b${v}\\b`).test(filterExpr)) {
28289
+ tainted = true;
28290
+ break;
28291
+ }
28292
+ }
28293
+ if (!tainted) continue;
28294
+ if (seen.has(i2 + 1)) continue;
28295
+ seen.add(i2 + 1);
28296
+ findings.push({
28297
+ id: `ldap_injection-${file}-${i2 + 1}-rust-ldap3-search`,
28298
+ pass: "language-sources",
28299
+ category: "security",
28300
+ rule_id: "ldap_injection",
28301
+ cwe: "CWE-90",
28302
+ severity: "critical",
28303
+ level: "error",
28304
+ message: "LDAP injection: ldap3 `LdapConn::search(..., &<tainted filter>, ...)` lets the attacker break out of the filter expression and enumerate the directory. Escape the user input or use a structured filter builder.",
28305
+ file,
28306
+ line: i2 + 1,
28307
+ snippet: lines[i2].trim()
28308
+ });
28309
+ }
28310
+ }
28311
+ return findings;
28312
+ }
28313
+ function findRustLogInjectionFindings(code, file) {
28314
+ const findings = [];
28315
+ if (typeof code !== "string" || code.length === 0) return findings;
28316
+ if (!/\b(?:info|warn|error|debug|trace)\s*!\s*\(/.test(code)) return findings;
28317
+ if (!/\b(?:log|tracing)\b/.test(code)) return findings;
28318
+ const lines = code.split("\n");
28319
+ const extractorTypeRe = /:\s*(?:String|Bytes|bytes::Bytes|axum::body::Bytes|web::Query\b|web::Path\b|web::Form\b|web::Json\b|HttpRequest\b|actix_web::HttpRequest\b)/;
28320
+ const fns = [];
28321
+ let cur = null;
28322
+ for (let i2 = 0; i2 < lines.length; i2++) {
28323
+ const t = lines[i2];
28324
+ if (/^\s*(?:pub\s+)?(?:async\s+)?fn\s+\w+\s*\(/.test(t)) {
28325
+ if (cur) {
28326
+ cur.end = i2 - 1;
28327
+ fns.push(cur);
28328
+ }
28329
+ cur = { start: i2, end: lines.length - 1, tainted: /* @__PURE__ */ new Set() };
28330
+ let headerJoined = "";
28331
+ for (let j = i2; j < Math.min(i2 + 12, lines.length); j++) {
28332
+ headerJoined += lines[j];
28333
+ if (/\{\s*$/.test(lines[j])) break;
28334
+ }
28335
+ const open = headerJoined.indexOf("(");
28336
+ const close = headerJoined.lastIndexOf(")");
28337
+ if (open !== -1 && close > open) {
28338
+ const params = headerJoined.substring(open + 1, close);
28339
+ let depth = 0;
28340
+ let buf = "";
28341
+ const parts2 = [];
28342
+ for (const ch of params) {
28343
+ if (ch === "<" || ch === "(") depth++;
28344
+ else if (ch === ">" || ch === ")") depth--;
28345
+ if (ch === "," && depth === 0) {
28346
+ parts2.push(buf);
28347
+ buf = "";
28348
+ continue;
28349
+ }
28350
+ buf += ch;
28351
+ }
28352
+ if (buf.trim().length > 0) parts2.push(buf);
28353
+ for (const p of parts2) {
28354
+ const pm = p.match(/(?:mut\s+)?(\w+)\s*:/);
28355
+ if (!pm) continue;
28356
+ if (extractorTypeRe.test(p)) cur.tainted.add(pm[1]);
28357
+ }
28358
+ }
28359
+ }
28360
+ }
28361
+ if (cur) fns.push(cur);
28362
+ for (const fn of fns) {
28363
+ for (let pass = 0; pass < 3; pass++) {
28364
+ const before = fn.tainted.size;
28365
+ for (let i2 = fn.start; i2 <= fn.end; i2++) {
28366
+ const t = lines[i2].trim();
28367
+ const m = t.match(/^let\s+(?:mut\s+)?(\w+)\s*(?::\s*[^=]+)?=\s*(.+?);?$/);
28368
+ if (!m) continue;
28369
+ const lhs = m[1];
28370
+ const rhs = m[2];
28371
+ if (fn.tainted.has(lhs)) continue;
28372
+ for (const v of fn.tainted) {
28373
+ if (new RegExp(`\\b${v}\\b`).test(rhs)) {
28374
+ fn.tainted.add(lhs);
28375
+ break;
28376
+ }
28377
+ }
28378
+ }
28379
+ if (fn.tainted.size === before) break;
28380
+ }
28381
+ }
28382
+ const macroRe = /\b(info|warn|error|debug|trace)\s*!\s*\(\s*([^;]+?)\)\s*;?\s*$/;
28383
+ const seen = /* @__PURE__ */ new Set();
28384
+ for (const fn of fns) {
28385
+ if (fn.tainted.size === 0) continue;
28386
+ for (let i2 = fn.start; i2 <= fn.end; i2++) {
28387
+ const line = lines[i2];
28388
+ const m = line.match(macroRe);
28389
+ if (!m) continue;
28390
+ const macroName = m[1];
28391
+ const argSpan = m[2];
28392
+ const parts2 = [];
28393
+ {
28394
+ let d = 0;
28395
+ let b = "";
28396
+ let inStr = null;
28397
+ for (let k = 0; k < argSpan.length; k++) {
28398
+ const ch = argSpan[k];
28399
+ if (inStr) {
28400
+ if (ch === "\\") {
28401
+ b += ch + (argSpan[k + 1] ?? "");
28402
+ k++;
28403
+ continue;
28404
+ }
28405
+ if (ch === inStr) inStr = null;
28406
+ b += ch;
28407
+ continue;
28408
+ }
28409
+ if (ch === '"') {
28410
+ inStr = ch;
28411
+ b += ch;
28412
+ continue;
28413
+ }
28414
+ if (ch === "(" || ch === "[" || ch === "{") d++;
28415
+ else if (ch === ")" || ch === "]" || ch === "}") d--;
28416
+ if (ch === "," && d === 0) {
28417
+ parts2.push(b);
28418
+ b = "";
28419
+ continue;
28420
+ }
28421
+ b += ch;
28422
+ }
28423
+ if (b.trim().length > 0) parts2.push(b);
28424
+ }
28425
+ if (parts2.length < 2) continue;
28426
+ let tainted = false;
28427
+ for (let p = 1; p < parts2.length; p++) {
28428
+ for (const v of fn.tainted) {
28429
+ if (new RegExp(`\\b${v}\\b`).test(parts2[p])) {
28430
+ tainted = true;
28431
+ break;
28432
+ }
28433
+ }
28434
+ if (tainted) break;
28435
+ }
28436
+ if (!tainted) continue;
28437
+ const key = `${i2 + 1}:${macroName}`;
28438
+ if (seen.has(key)) continue;
28439
+ seen.add(key);
28440
+ findings.push({
28441
+ id: `log_injection-${file}-${i2 + 1}-rust-${macroName}`,
28442
+ pass: "language-sources",
28443
+ category: "security",
28444
+ rule_id: "log_injection",
28445
+ cwe: "CWE-117",
28446
+ severity: "medium",
28447
+ level: "warning",
28448
+ message: `Log injection: Rust \`${macroName}!(...)\` interpolates a tainted value into the log line. Unsanitized CRLF lets an attacker forge log entries or split records. Strip control characters or use a structured logging API.`,
28449
+ file,
28450
+ line: i2 + 1,
28451
+ snippet: line.trim()
28452
+ });
28453
+ }
28454
+ }
28455
+ return findings;
28456
+ }
27926
28457
 
27927
28458
  // src/analysis/passes/sink-filter-pass.ts
27928
28459
  var JS_XSS_SANITIZERS = [
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "circle-ir",
3
- "version": "3.136.0",
3
+ "version": "3.137.0",
4
4
  "description": "High-performance Static Application Security Testing (SAST) library for detecting security vulnerabilities through taint analysis",
5
5
  "main": "dist/index.js",
6
6
  "module": "dist/index.js",