cognium-dev 4.14.1 → 4.14.2

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 +264 -43
  2. package/package.json +2 -2
package/dist/cli.js CHANGED
@@ -7421,6 +7421,17 @@ function extractGoCallInfo(node) {
7421
7421
  if (operand.type === "identifier") {
7422
7422
  const resolved = resolveGoLocalReceiverType(opText, node);
7423
7423
  receiver = resolved !== null ? resolved : opText;
7424
+ } else if (operand.type === "selector_expression" && methodName === "Get") {
7425
+ const fieldNode = operand.childForFieldName("field");
7426
+ const inner = operand.childForFieldName("operand");
7427
+ const fieldName = fieldNode ? getNodeText(fieldNode) : "";
7428
+ const innerName = inner && inner.type === "identifier" ? getNodeText(inner) : null;
7429
+ const innerType = innerName !== null ? resolveGoLocalReceiverType(innerName, node) : null;
7430
+ if (innerType === "Request" && (fieldName === "PostForm" || fieldName === "Form")) {
7431
+ receiver = fieldName === "PostForm" ? "RequestPostForm" : "RequestForm";
7432
+ } else {
7433
+ receiver = opText;
7434
+ }
7424
7435
  } else {
7425
7436
  receiver = opText;
7426
7437
  }
@@ -12048,7 +12059,11 @@ var DEFAULT_SINKS = [
12048
12059
  { method: "setExpireTimeSeconds", class: "StaticFilesConfiguration", type: "path_traversal", cwe: "CWE-22", severity: "medium", arg_positions: [0] },
12049
12060
  { method: "configureJarCase", class: "StaticFilesConfiguration", type: "path_traversal", cwe: "CWE-22", severity: "high", arg_positions: [0] },
12050
12061
  { method: "toString", class: "StringUtils", type: "path_traversal", cwe: "CWE-22", severity: "medium", arg_positions: [0] },
12051
- { method: "getResource", class: "ServletResourceManager", type: "path_traversal", cwe: "CWE-22", severity: "high", arg_positions: [0] },
12062
+ { method: "getResource", class: "ServletResourceManager", type: "path_traversal", cwe: "CWE-22", severity: "high", arg_positions: [0], languages: ["java"] },
12063
+ { method: "getResource", class: "ResourceManager", type: "path_traversal", cwe: "CWE-22", severity: "high", arg_positions: [0], languages: ["java"] },
12064
+ { method: "getResource", class: "PathResourceManager", type: "path_traversal", cwe: "CWE-22", severity: "high", arg_positions: [0], languages: ["java"] },
12065
+ { method: "getResource", class: "FileResourceManager", type: "path_traversal", cwe: "CWE-22", severity: "high", arg_positions: [0], languages: ["java"] },
12066
+ { method: "getResource", class: "ClassPathResourceManager", type: "path_traversal", cwe: "CWE-22", severity: "high", arg_positions: [0], languages: ["java"] },
12052
12067
  { method: "deleteObject", class: "FileSystemBucket", type: "path_traversal", cwe: "CWE-22", severity: "high", arg_positions: [0] },
12053
12068
  { method: "regularExpressionMatcher", class: "Validators", type: "path_traversal", cwe: "CWE-22", severity: "high", arg_positions: [0] },
12054
12069
  { method: "checkMessage", class: "Validators", type: "path_traversal", cwe: "CWE-22", severity: "high", arg_positions: [0] },
@@ -14049,7 +14064,8 @@ function isInterproceduralTaintableType(typeName, language) {
14049
14064
  function isParameterizedQueryCall(call, pattern) {
14050
14065
  if (pattern.type !== "sql_injection")
14051
14066
  return false;
14052
- const queryPos = pattern.arg_positions.length > 0 ? Math.min(...pattern.arg_positions) : 0;
14067
+ const positions = pattern.arg_positions ?? [];
14068
+ const queryPos = positions.length > 0 ? Math.min(...positions) : 0;
14053
14069
  const queryArg = call.arguments.find((a) => a.position === queryPos);
14054
14070
  if (queryArg) {
14055
14071
  const queryText = queryArg.literal ?? queryArg.expression ?? "";
@@ -18374,7 +18390,17 @@ function propagateTaint(graphOrDfg, callsOrSources, sourcesOrSinks, sinksOrSanit
18374
18390
  }
18375
18391
  const taintInfo = taintByDefId.get(use.def_id);
18376
18392
  if (taintInfo) {
18377
- const isSanitized = checkSanitized(taintInfo.line, sink.line, sink.type, sanitizersByLine, {
18393
+ let sanMap = sanitizersByLine;
18394
+ if (identifierOutsideSanitizerCalls(use.variable, callsAtSink, sanitizersByLine.get(sink.line) ?? [], sink.type)) {
18395
+ const stripped = new Map(sanitizersByLine);
18396
+ const kept = (sanitizersByLine.get(sink.line) ?? []).filter((s) => !sanitizerCoversSink(s, sink.type));
18397
+ if (kept.length === 0)
18398
+ stripped.delete(sink.line);
18399
+ else
18400
+ stripped.set(sink.line, kept);
18401
+ sanMap = stripped;
18402
+ }
18403
+ const isSanitized = checkSanitized(taintInfo.line, sink.line, sink.type, sanMap, {
18378
18404
  startDefId: use.def_id,
18379
18405
  chainsByToDef: graph.chainsByToDef,
18380
18406
  defById
@@ -18520,6 +18546,97 @@ var KNOWN_SINK_TYPES = new Set([
18520
18546
  "crlf",
18521
18547
  "mass_assignment"
18522
18548
  ]);
18549
+ function readBalancedArg(text, openParen) {
18550
+ let depth = 0;
18551
+ let quote = null;
18552
+ let escaped = false;
18553
+ for (let i2 = openParen;i2 < text.length; i2++) {
18554
+ const c = text[i2];
18555
+ if (quote) {
18556
+ if (escaped) {
18557
+ escaped = false;
18558
+ continue;
18559
+ }
18560
+ if (c === "\\") {
18561
+ escaped = true;
18562
+ continue;
18563
+ }
18564
+ if (c === quote)
18565
+ quote = null;
18566
+ continue;
18567
+ }
18568
+ if (c === '"' || c === "'") {
18569
+ quote = c;
18570
+ continue;
18571
+ }
18572
+ if (c === "(")
18573
+ depth++;
18574
+ else if (c === ")") {
18575
+ depth--;
18576
+ if (depth === 0)
18577
+ return text.slice(openParen + 1, i2);
18578
+ }
18579
+ }
18580
+ return null;
18581
+ }
18582
+ function sanitizerCallRefs(sanitizers, sinkType) {
18583
+ const refs = [];
18584
+ for (const san of sanitizers) {
18585
+ if (!sanitizerCoversSink(san, sinkType))
18586
+ continue;
18587
+ const m = /(?:(\w+)\.)?(\w+)\(\)/.exec(san.method);
18588
+ if (!m?.[2])
18589
+ continue;
18590
+ refs.push({ receiver: m[1], methodName: m[2] });
18591
+ }
18592
+ return refs;
18593
+ }
18594
+ function maskSanitizerCalls(expr, refs) {
18595
+ let out2 = expr;
18596
+ for (const ref of refs) {
18597
+ const needles = ref.receiver ? [`${ref.receiver}.${ref.methodName}`, ref.methodName] : [ref.methodName];
18598
+ for (const needle of needles) {
18599
+ const re = new RegExp(`\\b${needle.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}\\s*\\(`);
18600
+ for (let guard = 0;guard < 8; guard++) {
18601
+ const m = re.exec(out2);
18602
+ if (!m)
18603
+ break;
18604
+ const open = m.index + m[0].length - 1;
18605
+ const arg = readBalancedArg(out2, open);
18606
+ if (arg === null)
18607
+ break;
18608
+ out2 = out2.slice(0, m.index) + " " + out2.slice(open + arg.length + 2);
18609
+ }
18610
+ }
18611
+ }
18612
+ return out2;
18613
+ }
18614
+ function blankStringLiterals(expr) {
18615
+ return expr.replace(/"(?:[^"\\]|\\.)*"/g, '""').replace(/'(?:[^'\\]|\\.)*'/g, "''");
18616
+ }
18617
+ function identifierOutsideSanitizerCalls(variable, calls, sanitizers, sinkType) {
18618
+ if (!/^[A-Za-z_$][\w$]*$/.test(variable))
18619
+ return false;
18620
+ const refs = sanitizerCallRefs(sanitizers, sinkType);
18621
+ if (refs.length === 0)
18622
+ return false;
18623
+ const re = new RegExp(`(?:^|[^A-Za-z0-9_$])${variable.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}(?:[^A-Za-z0-9_$]|$)`);
18624
+ for (const call of calls) {
18625
+ if (refs.some((r) => r.methodName === call.method_name))
18626
+ continue;
18627
+ for (const arg of call.arguments) {
18628
+ const expr = arg.expression ?? "";
18629
+ if (!expr)
18630
+ continue;
18631
+ const masked = maskSanitizerCalls(expr, refs);
18632
+ if (masked === expr)
18633
+ continue;
18634
+ if (re.test(blankStringLiterals(masked)))
18635
+ return true;
18636
+ }
18637
+ }
18638
+ return false;
18639
+ }
18523
18640
  function checkSanitized(fromLine, toLine, sinkType, sanitizersByLine, ctx) {
18524
18641
  const isKnownSinkType = KNOWN_SINK_TYPES.has(sinkType);
18525
18642
  const sanitizersAtTarget = sanitizersByLine.get(toLine);
@@ -24440,6 +24557,22 @@ class GoPlugin extends BaseLanguagePlugin {
24440
24557
  confidence: 0.9,
24441
24558
  returnTainted: true
24442
24559
  },
24560
+ {
24561
+ method: "Get",
24562
+ class: "RequestPostForm",
24563
+ type: "http_body",
24564
+ severity: "high",
24565
+ confidence: 0.9,
24566
+ returnTainted: true
24567
+ },
24568
+ {
24569
+ method: "Get",
24570
+ class: "RequestForm",
24571
+ type: "http_param",
24572
+ severity: "high",
24573
+ confidence: 0.9,
24574
+ returnTainted: true
24575
+ },
24443
24576
  {
24444
24577
  method: "GetRawData",
24445
24578
  class: "Context",
@@ -26338,6 +26471,7 @@ class LanguageSourcesPass {
26338
26471
  additionalSources.push(...findGoMultipartUploadSources(code, language));
26339
26472
  additionalSources.push(...findGoGrpcRequestSources(code, language));
26340
26473
  additionalSources.push(...findGoRequestBodySources(code, language));
26474
+ additionalSources.push(...findGoFormIndexSources(code, language));
26341
26475
  const jsDOMSinks = findJavaScriptDOMSinks(code, language);
26342
26476
  for (const s of jsDOMSinks) {
26343
26477
  const alreadyExists = additionalSinks.some((x) => x.line === s.line && x.cwe === s.cwe);
@@ -27144,6 +27278,48 @@ function findGoGrpcRequestSources(sourceCode, language) {
27144
27278
  }
27145
27279
  return sources;
27146
27280
  }
27281
+ function findGoFormIndexSources(sourceCode, language) {
27282
+ if (language !== "go")
27283
+ return [];
27284
+ if (!/\.(?:PostForm|Form|MultipartForm)\b/.test(sourceCode))
27285
+ return [];
27286
+ const lines = sourceCode.split(`
27287
+ `);
27288
+ const reqNames = new Set;
27289
+ const paramRe = /([A-Za-z_]\w*)\s+\*http\.Request\b/g;
27290
+ for (const line of lines) {
27291
+ let m;
27292
+ paramRe.lastIndex = 0;
27293
+ while ((m = paramRe.exec(line)) !== null)
27294
+ reqNames.add(m[1]);
27295
+ }
27296
+ if (reqNames.size === 0)
27297
+ return [];
27298
+ const alt = [...reqNames].map((n) => n.replace(/\$/g, "\\$")).join("|");
27299
+ const indexRe = new RegExp(`^\\s*(?:var\\s+)?([A-Za-z_]\\w*)\\s*(?:,\\s*\\w+\\s*)?:?=\\s*(?:${alt})\\s*\\.\\s*(?:(PostForm|Form)|MultipartForm\\s*\\.\\s*Value)\\s*\\[`);
27300
+ const sources = [];
27301
+ for (let i2 = 0;i2 < lines.length; i2++) {
27302
+ const line = lines[i2];
27303
+ if (/^\s*\/\//.test(line))
27304
+ continue;
27305
+ const m = indexRe.exec(line);
27306
+ if (!m)
27307
+ continue;
27308
+ const variable = m[1];
27309
+ if (variable === "_" || variable === "")
27310
+ continue;
27311
+ const kind = m[2];
27312
+ sources.push({
27313
+ type: kind === "Form" ? "http_param" : "http_body",
27314
+ location: "HTTP request form index",
27315
+ severity: "high",
27316
+ line: i2 + 1,
27317
+ confidence: 0.9,
27318
+ variable
27319
+ });
27320
+ }
27321
+ return sources;
27322
+ }
27147
27323
  function findGoRequestBodySources(sourceCode, language) {
27148
27324
  if (language !== "go")
27149
27325
  return [];
@@ -35353,7 +35529,7 @@ class SinkFilterPass {
35353
35529
  let filtered = sinks.filter((sink) => !constProp.unreachableLines.has(sink.line));
35354
35530
  filtered = filterCleanArraySinks(filtered, calls, constProp.taintedArrayElements, constProp.symbols);
35355
35531
  filtered = filterCleanVariableSinks(filtered, calls, constProp.tainted, constProp.symbols, dfg, constProp.sanitizedVars, constProp.synchronizedLines, language);
35356
- filtered = filterSanitizedSinks(filtered, sanitizers, calls);
35532
+ filtered = filterSanitizedSinks(filtered, sanitizers, calls, sources);
35357
35533
  if (language === "python") {
35358
35534
  const { pyTaintedVars, pySanitizedVars } = langSources;
35359
35535
  const sourceLines = ctx.code.split(`
@@ -35423,8 +35599,13 @@ class SinkFilterPass {
35423
35599
  if (sink.type !== "xss")
35424
35600
  return true;
35425
35601
  const sinkLineText = sourceLines[sink.line - 1] ?? "";
35426
- if (JS_XSS_SANITIZERS.some((p) => p.test(sinkLineText)))
35427
- return false;
35602
+ if (JS_XSS_SANITIZERS.some((p) => p.test(sinkLineText))) {
35603
+ const callsAtSink = callsByLine.get(sink.line) ?? [];
35604
+ const covering = sanitizers.filter((s) => s.line === sink.line && sanitizerCoversSink(s, sink.type));
35605
+ const rawOperand = sources.some((s) => !!s.variable && identifierOutsideSanitizerCalls(s.variable, callsAtSink, covering, sink.type));
35606
+ if (!rawOperand)
35607
+ return false;
35608
+ }
35428
35609
  const assignmentMatch = sinkLineText.match(/\.(?:innerHTML|outerHTML)\s*=\s*(.+)/);
35429
35610
  if (assignmentMatch) {
35430
35611
  const rhs = assignmentMatch[1].trim().replace(/;$/, "").trim();
@@ -36642,9 +36823,14 @@ function filterCleanVariableSinks(sinks, calls, taintedVars, symbols, dfg, sanit
36642
36823
  return true;
36643
36824
  });
36644
36825
  }
36645
- function filterSanitizedSinks(sinks, sanitizers, calls) {
36826
+ function filterSanitizedSinks(sinks, sanitizers, calls, sources) {
36646
36827
  if (!sanitizers || sanitizers.length === 0)
36647
36828
  return sinks;
36829
+ const sourceVars = [];
36830
+ for (const source of sources ?? []) {
36831
+ if (source.variable && /^[A-Za-z_$][\w$]*$/.test(source.variable))
36832
+ sourceVars.push(source.variable);
36833
+ }
36648
36834
  const sanitizersByLine = new Map;
36649
36835
  for (const san of sanitizers) {
36650
36836
  const existing = sanitizersByLine.get(san.line) ?? [];
@@ -36661,23 +36847,30 @@ function filterSanitizedSinks(sinks, sanitizers, calls) {
36661
36847
  const lineSanitizers = sanitizersByLine.get(sink.line);
36662
36848
  if (!lineSanitizers || lineSanitizers.length === 0)
36663
36849
  return true;
36664
- for (const san of lineSanitizers) {
36665
- if (sanitizerCoversSink(san, sink.type)) {
36666
- const lineCalls = callsByLine.get(sink.line) ?? [];
36667
- for (const call of lineCalls) {
36668
- for (const arg of call.arguments) {
36669
- const expr = arg.expression || "";
36670
- const sanMethodMatch = san.method.match(/(?:(\w+)\.)?(\w+)\(\)/);
36671
- if (sanMethodMatch) {
36672
- const sanMethodName = sanMethodMatch[2];
36673
- const sanClassName = sanMethodMatch[1];
36674
- if (sanClassName) {
36675
- if (expr.includes(`${sanClassName}.${sanMethodName}(`))
36676
- return false;
36677
- } else if (expr.includes(`${sanMethodName}(`)) {
36678
- return false;
36679
- }
36680
- }
36850
+ const lineCalls = callsByLine.get(sink.line) ?? [];
36851
+ const covering = lineSanitizers.filter((san) => sanitizerCoversSink(san, sink.type));
36852
+ if (covering.length === 0)
36853
+ return true;
36854
+ for (const variable of sourceVars) {
36855
+ if (identifierOutsideSanitizerCalls(variable, lineCalls, covering, sink.type))
36856
+ return true;
36857
+ }
36858
+ for (const san of covering) {
36859
+ const sanMethodMatch = san.method.match(/(?:(\w+)\.)?(\w+)\(\)/);
36860
+ if (!sanMethodMatch)
36861
+ continue;
36862
+ const sanMethodName = sanMethodMatch[2];
36863
+ const sanClassName = sanMethodMatch[1];
36864
+ for (const call of lineCalls) {
36865
+ if (call.method_name === sanMethodName)
36866
+ continue;
36867
+ for (const arg of call.arguments) {
36868
+ const expr = arg.expression || "";
36869
+ if (sanClassName) {
36870
+ if (expr.includes(`${sanClassName}.${sanMethodName}(`))
36871
+ return false;
36872
+ } else if (expr.includes(`${sanMethodName}(`)) {
36873
+ return false;
36681
36874
  }
36682
36875
  }
36683
36876
  }
@@ -38123,9 +38316,14 @@ class TaintPropagationPass {
38123
38316
  const sansAtSink = sanitizersByLine.get(f.sink_line);
38124
38317
  if (sansAtSink && sansAtSink.length > 0) {
38125
38318
  for (const san of sansAtSink) {
38126
- if (sanitizerCoversSink(san, f.sink_type)) {
38127
- return false;
38319
+ if (!sanitizerCoversSink(san, f.sink_type))
38320
+ continue;
38321
+ const sinkVar2 = f.path.length > 0 ? f.path[f.path.length - 1].variable : undefined;
38322
+ const callsAtSink = graph.callsByLine.get(f.sink_line) ?? [];
38323
+ if (sinkVar2 && identifierOutsideSanitizerCalls(sinkVar2, callsAtSink, sansAtSink, f.sink_type)) {
38324
+ continue;
38128
38325
  }
38326
+ return false;
38129
38327
  }
38130
38328
  }
38131
38329
  const usesAtSink = graph.usesByLine.get(f.sink_line);
@@ -40606,10 +40804,15 @@ class InterproceduralPass {
40606
40804
  const sansAtSink = sanitizersByLine.get(f.sink_line);
40607
40805
  if (!sansAtSink || sansAtSink.length === 0)
40608
40806
  return true;
40807
+ const callsAtSink = graph.callsByLine.get(f.sink_line) ?? [];
40808
+ const sinkVar = f.path.length > 0 ? f.path[f.path.length - 1]?.variable : undefined;
40609
40809
  for (const san of sansAtSink) {
40610
- if (sanitizerCoversSink(san, f.sink_type)) {
40611
- return false;
40810
+ if (!sanitizerCoversSink(san, f.sink_type))
40811
+ continue;
40812
+ if (sinkVar && identifierOutsideSanitizerCalls(sinkVar, callsAtSink, sansAtSink, f.sink_type)) {
40813
+ continue;
40612
40814
  }
40815
+ return false;
40613
40816
  }
40614
40817
  return true;
40615
40818
  });
@@ -45045,6 +45248,13 @@ var PROVIDER_PATTERNS = [
45045
45248
  level: "error",
45046
45249
  fix: "Revoke the GitHub refresh token and store secrets outside source control."
45047
45250
  },
45251
+ {
45252
+ name: "GitHub fine-grained personal access token",
45253
+ regex: /\bgithub_pat_[A-Za-z0-9]{22}_[A-Za-z0-9]{59}\b/,
45254
+ severity: "critical",
45255
+ level: "error",
45256
+ fix: "Revoke the fine-grained personal access token at https://github.com/settings/tokens and store secrets outside source control."
45257
+ },
45048
45258
  {
45049
45259
  name: "GitLab personal access token",
45050
45260
  regex: /\bglpat-[A-Za-z0-9_-]{20,}\b/,
@@ -45069,9 +45279,9 @@ var PROVIDER_PATTERNS = [
45069
45279
  {
45070
45280
  name: "Stripe live publishable key",
45071
45281
  regex: /\bpk_live_[A-Za-z0-9]{24,}\b/,
45072
- severity: "high",
45073
- level: "warning",
45074
- fix: "Publishable keys are not secret but should still not be checked in to back-end source files; verify front-end vs back-end context."
45282
+ severity: "low",
45283
+ level: "note",
45284
+ fix: "Publishable keys are not secret. Prefer loading them from configuration rather than a backend source file."
45075
45285
  },
45076
45286
  {
45077
45287
  name: "Stripe test secret key",
@@ -45094,6 +45304,13 @@ var PROVIDER_PATTERNS = [
45094
45304
  level: "error",
45095
45305
  fix: "Revoke the OpenAI key at https://platform.openai.com/api-keys and load from environment."
45096
45306
  },
45307
+ {
45308
+ name: "OpenAI project API key",
45309
+ regex: /\bsk-proj-[A-Za-z0-9_-]{48,}\b/,
45310
+ severity: "critical",
45311
+ level: "error",
45312
+ fix: "Revoke the OpenAI project key at https://platform.openai.com/api-keys and load from environment."
45313
+ },
45097
45314
  {
45098
45315
  name: "Anthropic API key",
45099
45316
  regex: /\bsk-ant-[A-Za-z0-9_-]{90,}\b/,
@@ -45431,7 +45648,7 @@ class ScanSecretsPass {
45431
45648
  const m = pattern.regex.exec(lineText);
45432
45649
  if (!m)
45433
45650
  continue;
45434
- if ((pattern.name === "Stripe live restricted key" || pattern.name === "Stripe test secret key" || pattern.name === "Stripe test restricted key" || pattern.name === "GitLab personal access token") && PLACEHOLDER_RE.test(m[0])) {
45651
+ if ((pattern.name === "Stripe live restricted key" || pattern.name === "Stripe test secret key" || pattern.name === "Stripe test restricted key" || pattern.name === "GitLab personal access token" || pattern.name === "OpenAI project API key" || pattern.name === "GitHub fine-grained personal access token") && PLACEHOLDER_RE.test(m[0])) {
45435
45652
  continue;
45436
45653
  }
45437
45654
  if (pattern.name === "PEM private key" && !pemHasInlineBody(lines, i2)) {
@@ -45867,8 +46084,6 @@ var PY_SET_COOKIE_RECEIVERS = new Set([
45867
46084
  ]);
45868
46085
  var PY_SECURE_TRUE_RE = /\bsecure\s*=\s*True\b/;
45869
46086
  var PY_HTTPONLY_TRUE_RE = /\bhttponly\s*=\s*True\b/i;
45870
- var JAVA_SET_SECURE_TRUE_RE = /\.setSecure\s*\(\s*true\s*\)/;
45871
- var JAVA_SET_HTTPONLY_TRUE_RE = /\.setHttpOnly\s*\(\s*true\s*\)/;
45872
46087
  var GO_SECURE_TRUE_RE = /\bSecure\s*:\s*true\b/;
45873
46088
  var GO_HTTPONLY_TRUE_RE = /\bHttpOnly\s*:\s*true\b/;
45874
46089
  var RUST_SET_COOKIE_MACRO_RE = /(format!|write!|writeln!)\s*\(([^()]*Set-Cookie[^()]*)\)/gis;
@@ -45900,10 +46115,8 @@ class InsecureCookiePass {
45900
46115
  this.emit(ctx, file, det, "python");
45901
46116
  }
45902
46117
  } else if (language === "java") {
45903
- const hasSetSecureTrue = JAVA_SET_SECURE_TRUE_RE.test(code);
45904
- const hasSetHttpOnlyTrue = JAVA_SET_HTTPONLY_TRUE_RE.test(code);
45905
46118
  for (const call of graph.ir.calls) {
45906
- const det = this.detectJavaCookieCtor(call, hasSetSecureTrue, hasSetHttpOnlyTrue);
46119
+ const det = this.detectJavaCookieCtor(call, code);
45907
46120
  if (!det)
45908
46121
  continue;
45909
46122
  insecureCookies.push(det);
@@ -45989,7 +46202,15 @@ class InsecureCookiePass {
45989
46202
  optionsPresent: call.arguments.length >= 2
45990
46203
  };
45991
46204
  }
45992
- detectJavaCookieCtor(call, hasSetSecureTrue, hasSetHttpOnlyTrue) {
46205
+ javaCookieSetterTrue(code, call, setter) {
46206
+ const line = code.split(`
46207
+ `)[(call.location.line ?? 1) - 1] ?? "";
46208
+ const assign = /\b([A-Za-z_]\w*)\s*=\s*new\s+(?:[\w.]+\.)?Cookie\b/.exec(line);
46209
+ const varName = assign?.[1];
46210
+ const re = varName ? new RegExp(`\\b${varName}\\.${setter}\\s*\\(\\s*true\\s*\\)`) : new RegExp(`\\.${setter}\\s*\\(\\s*true\\s*\\)`);
46211
+ return re.test(varName ? code : line);
46212
+ }
46213
+ detectJavaCookieCtor(call, code) {
45993
46214
  const method = call.method_name ?? "";
45994
46215
  const isCookieCtor = method === "Cookie" || method.endsWith(".Cookie");
45995
46216
  if (!isCookieCtor)
@@ -45999,8 +46220,8 @@ class InsecureCookiePass {
45999
46220
  return null;
46000
46221
  if (call.arguments.length < 2)
46001
46222
  return null;
46002
- const missingSecure = !hasSetSecureTrue;
46003
- const missingHttpOnly = !hasSetHttpOnlyTrue;
46223
+ const missingSecure = !this.javaCookieSetterTrue(code, call, "setSecure");
46224
+ const missingHttpOnly = !this.javaCookieSetterTrue(code, call, "setHttpOnly");
46004
46225
  if (!missingSecure && !missingHttpOnly)
46005
46226
  return null;
46006
46227
  return {
@@ -52155,7 +52376,7 @@ var colors = {
52155
52376
  };
52156
52377
 
52157
52378
  // src/version.ts
52158
- var version = "4.14.1";
52379
+ var version = "4.14.2";
52159
52380
 
52160
52381
  // src/formatters.ts
52161
52382
  var LIBRARY_API_SURFACE_TAG2 = "library-api-surface:caller-responsibility";
@@ -52722,8 +52943,8 @@ function generateSarifResults(results, crossFileData) {
52722
52943
 
52723
52944
  // src/build-info.ts
52724
52945
  var buildInfo = {
52725
- gitSha: "c8c74e9",
52726
- builtAt: "2026-10-11T07:15:24.915Z"
52946
+ gitSha: "b1967eb",
52947
+ builtAt: "2026-10-13T06:43:43.278Z"
52727
52948
  };
52728
52949
 
52729
52950
  // src/utils/args.ts
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "cognium-dev",
3
- "version": "4.14.1",
3
+ "version": "4.14.2",
4
4
  "description": "Static Application Security Testing CLI for detecting security vulnerabilities via taint tracking",
5
5
  "bin": {
6
6
  "cognium-dev": "dist/cli.js"
@@ -65,7 +65,7 @@
65
65
  },
66
66
  "dependencies": {
67
67
  "@cognium/project-profile-detect": "1.1.2",
68
- "circle-ir": "4.14.1"
68
+ "circle-ir": "4.14.2"
69
69
  },
70
70
  "devDependencies": {
71
71
  "@types/node": "^26.6.4",