cognium-dev 4.13.0 → 4.13.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 +151 -19
  2. package/package.json +2 -2
package/dist/cli.js CHANGED
@@ -20039,7 +20039,13 @@ class ConstantPropagator {
20039
20039
  }
20040
20040
  return;
20041
20041
  }
20042
- const condValue = this.evaluateExpression(condition);
20042
+ let condValue = this.evaluateExpression(condition);
20043
+ if (!(isKnown(condValue) && condValue.type === "bool")) {
20044
+ const literal = this.literalCondition(condition);
20045
+ if (literal !== null) {
20046
+ condValue = { type: "bool", value: literal, sourceLine: getNodeLine(condition) };
20047
+ }
20048
+ }
20043
20049
  if (isKnown(condValue) && condValue.type === "bool") {
20044
20050
  if (condValue.value === true) {
20045
20051
  if (alternative) {
@@ -20188,12 +20194,27 @@ class ConstantPropagator {
20188
20194
  }
20189
20195
  return;
20190
20196
  }
20191
- const caseGroups = switchBlock.children.filter((c) => c.type === "switch_block_statement_group" || c.type === "switch_rule" || c.type === "switch_section");
20197
+ const caseGroups = switchBlock.children.filter((c) => c.type === "switch_block_statement_group" || c.type === "switch_rule" || c.type === "switch_section" || c.type === "switch_case" || c.type === "switch_default");
20198
+ if (caseGroups.length === 0) {
20199
+ this.visit(switchBlock);
20200
+ return;
20201
+ }
20192
20202
  if (switchValue && isKnown(switchValue)) {
20193
20203
  let matchingIdx = -1;
20194
20204
  let defaultIdx = -1;
20195
20205
  for (let i2 = 0;i2 < caseGroups.length; i2++) {
20196
20206
  const caseGroup = caseGroups[i2];
20207
+ if (caseGroup.type === "switch_default") {
20208
+ defaultIdx = i2;
20209
+ continue;
20210
+ }
20211
+ if (caseGroup.type === "switch_case") {
20212
+ const label = caseGroup.childForFieldName("value");
20213
+ const caseValue = label ? this.literalValue(label) : null;
20214
+ if (caseValue !== null && caseValue === switchValue.value)
20215
+ matchingIdx = i2;
20216
+ continue;
20217
+ }
20197
20218
  for (const child of caseGroup.children) {
20198
20219
  if (child.type === "switch_label") {
20199
20220
  const labelText = getNodeText2(child, this.source);
@@ -20297,6 +20318,30 @@ class ConstantPropagator {
20297
20318
  }
20298
20319
  return false;
20299
20320
  }
20321
+ literalCondition(node) {
20322
+ if (node.type === "parenthesized_expression") {
20323
+ const inner = node.children.find((c) => c.isNamed);
20324
+ return inner ? this.literalCondition(inner) : null;
20325
+ }
20326
+ if (node.type === "boolean_literal") {
20327
+ const text = getNodeText2(node, this.source);
20328
+ return text === "true" ? true : text === "false" ? false : null;
20329
+ }
20330
+ if (node.type === "binary_expression") {
20331
+ const left = node.childForFieldName("left");
20332
+ const right = node.childForFieldName("right");
20333
+ const op = node.childForFieldName("operator");
20334
+ const opText = op ? getNodeText2(op, this.source) : "";
20335
+ if (!left || !right || opText !== "==" && opText !== "!=")
20336
+ return null;
20337
+ const l = this.literalValue(left);
20338
+ const r = this.literalValue(right);
20339
+ if (l === null || r === null)
20340
+ return null;
20341
+ return opText === "==" ? l === r : l !== r;
20342
+ }
20343
+ return null;
20344
+ }
20300
20345
  literalValue(node) {
20301
20346
  const text = getNodeText2(node, this.source);
20302
20347
  if (node.type === "integer_literal" || node.type === "decimal_integer_literal") {
@@ -34134,6 +34179,9 @@ var ENDS_STATEMENT_RE = /[;{}]\s*(?:\/\/.*)?$/;
34134
34179
  function escapeRegex(s) {
34135
34180
  return s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
34136
34181
  }
34182
+ function withoutStringLiterals(text) {
34183
+ return text.replace(/@"(?:[^"]|"")*"|"(?:[^"\\]|\\.)*"/g, '""');
34184
+ }
34137
34185
  function mentions(text, name2) {
34138
34186
  return new RegExp(`(?<![\\w])${escapeRegex(name2)}(?![\\w])`).test(text);
34139
34187
  }
@@ -34147,6 +34195,19 @@ function insideParens(text, open) {
34147
34195
  }
34148
34196
  return null;
34149
34197
  }
34198
+ function lastCallArguments(text) {
34199
+ const close = text.lastIndexOf(")");
34200
+ if (close < 0)
34201
+ return null;
34202
+ let depth = 0;
34203
+ for (let i2 = close;i2 >= 0; i2--) {
34204
+ if (text[i2] === ")")
34205
+ depth++;
34206
+ else if (text[i2] === "(" && --depth === 0)
34207
+ return text.slice(i2 + 1, close);
34208
+ }
34209
+ return null;
34210
+ }
34150
34211
  function leadingIf(text) {
34151
34212
  const m = /^\s*(?:\}\s*)?(?:else\s+)?if\s*\(/.exec(text);
34152
34213
  if (!m)
@@ -34271,6 +34332,81 @@ class CSharpCredit {
34271
34332
  sanitizedVarsAt(type, sinkLine) {
34272
34333
  return this.sanitizedVarsIn(type, this.methodAt(sinkLine)?.start_line ?? 1, sinkLine, true);
34273
34334
  }
34335
+ rawTaintReaches(type, sinkLine, credited, seeds) {
34336
+ if (seeds.length === 0)
34337
+ return false;
34338
+ const method = this.methodAt(sinkLine);
34339
+ const from = method?.start_line ?? 1;
34340
+ const tainted = new Set;
34341
+ const stmts = this.statements(from, sinkLine);
34342
+ for (let i2 = 0;i2 < stmts.length; i2++) {
34343
+ const st = stmts[i2];
34344
+ const end = i2 + 1 < stmts.length ? stmts[i2 + 1].line : sinkLine;
34345
+ const m = ASSIGN_RE.exec(st.text);
34346
+ if (m) {
34347
+ const rhs = withoutStringLiterals(m[2]);
34348
+ if ([...tainted].some((v) => mentions(rhs, v)))
34349
+ tainted.add(m[1]);
34350
+ else
34351
+ tainted.delete(m[1]);
34352
+ }
34353
+ for (const s of seeds)
34354
+ if (s.line >= st.line && s.line < end)
34355
+ tainted.add(s.variable);
34356
+ }
34357
+ if (tainted.size === 0)
34358
+ return false;
34359
+ const rest = this.withoutSanitizerCalls(type, this.receivedOperands(type, sinkLine));
34360
+ if (this.builtinSanitizes(type, rest))
34361
+ return false;
34362
+ for (const v of tainted)
34363
+ if (!credited.has(v) && mentions(rest, v))
34364
+ return true;
34365
+ return false;
34366
+ }
34367
+ receivedOperands(type, sinkLine) {
34368
+ const text = withoutStringLiterals(this.sinkText(sinkLine)).replace(/\s*;?\s*(?:\/\/.*)?$/, "");
34369
+ const property = /^\s*[A-Za-z_][\w.]*\.[A-Za-z_]\w*\s*=(?!=)\s*(.*)$/.exec(text);
34370
+ if (property)
34371
+ return property[1];
34372
+ if (!text.endsWith(")"))
34373
+ return text;
34374
+ const args2 = lastCallArguments(text);
34375
+ if (args2 === null)
34376
+ return text;
34377
+ const open = text.length - args2.length - 2;
34378
+ const callStart = text.slice(0, open).search(/[A-Za-z_][\w.]*\s*$/);
34379
+ const lastCall = callStart < 0 ? "" : text.slice(callStart);
34380
+ if (lastCall && this.withoutSanitizerCalls(type, lastCall).trim() === "")
34381
+ return text;
34382
+ return args2;
34383
+ }
34384
+ withoutSanitizerCalls(type, text) {
34385
+ const res = [];
34386
+ for (const { re, type: t } of CSHARP_SANITIZER_RES)
34387
+ if (t === type)
34388
+ res.push(re);
34389
+ for (const name2 of this.helpers(type))
34390
+ res.push(new RegExp(`(?<![\\w.])${escapeRegex(name2)}\\s*\\(`));
34391
+ let out2 = text;
34392
+ for (const re of res) {
34393
+ for (let guard = 0;guard < 16; guard++) {
34394
+ const m = re.exec(out2);
34395
+ if (!m)
34396
+ break;
34397
+ if (!m[0].endsWith("(")) {
34398
+ out2 = out2.slice(0, m.index) + out2.slice(m.index + m[0].length);
34399
+ continue;
34400
+ }
34401
+ const open = m.index + m[0].length - 1;
34402
+ const inner = insideParens(out2, open);
34403
+ if (inner === null)
34404
+ break;
34405
+ out2 = out2.slice(0, m.index) + out2.slice(open + inner.length + 2);
34406
+ }
34407
+ }
34408
+ return out2;
34409
+ }
34274
34410
  sinkText(line) {
34275
34411
  const text = this.lines[line - 1] ?? "";
34276
34412
  const guarded = leadingIf(text);
@@ -34463,21 +34599,19 @@ class CSharpCredit {
34463
34599
  }
34464
34600
  return new Set([...state].filter(([, safe]) => safe).map(([name2]) => name2));
34465
34601
  }
34466
- creditedOnLine(line) {
34602
+ creditedOnLine(line, seeds = []) {
34467
34603
  const text = this.sinkText(line);
34468
34604
  const types = new Set(CSHARP_SANITIZER_RES.map((r) => r.type));
34469
34605
  for (const t of INERT_WHEN_ALPHANUMERIC)
34470
34606
  types.add(t);
34471
34607
  types.add("ssrf");
34472
34608
  for (const type of types) {
34473
- if (this.sanitizes(type, text))
34474
- return true;
34475
34609
  const credited = new Set([...this.sanitizedVarsAt(type, line), ...this.guardedVarsAt(type, line)]);
34476
34610
  for (const v of this.safeBuilders(credited, line))
34477
34611
  credited.add(v);
34478
- for (const v of credited)
34479
- if (mentions(text, v))
34480
- return true;
34612
+ const covered = this.sanitizes(type, text) || [...credited].some((v) => mentions(text, v));
34613
+ if (covered && !this.rawTaintReaches(type, line, credited, seeds))
34614
+ return true;
34481
34615
  }
34482
34616
  return this.fixedHostRequest(line);
34483
34617
  }
@@ -35764,13 +35898,13 @@ class SinkFilterPass {
35764
35898
  const sanitizedByType = csharpSanitizedVarsByType(ctx.code);
35765
35899
  const csFields = new Set(ctx.graph.ir.types.flatMap((t) => (t.fields ?? []).map((f) => f.name)));
35766
35900
  const credit = new CSharpCredit(ctx.code, ctx.graph.ir.types.flatMap((t) => t.methods));
35901
+ const csSeeds = mergedSources.flatMap((s) => s.variable ? [{ variable: s.variable, line: s.line }] : []);
35767
35902
  const xxeHardened = /\bXmlResolver\s*=\s*null\b/.test(ctx.code) || /\bDtdProcessing\s*=\s*(?:DtdProcessing\s*\.\s*)?(?:Prohibit|Ignore)\b/.test(ctx.code);
35768
35903
  filtered = filtered.filter((sink) => {
35769
35904
  if (sink.type === "xxe" && xxeHardened)
35770
35905
  return false;
35771
35906
  const sinkLineText = sourceLines[sink.line - 1] ?? "";
35772
- if (credit.sanitizes(sink.type, sinkLineText))
35773
- return false;
35907
+ const inlineSanitized = credit.sanitizes(sink.type, sinkLineText);
35774
35908
  if (sink.type === "ssrf" && credit.fixedHostRequest(sink.line))
35775
35909
  return false;
35776
35910
  const scoped = credit.sanitizedVarsAt(sink.type, sink.line);
@@ -35786,11 +35920,8 @@ class SinkFilterPass {
35786
35920
  for (const v of credit.safeBuilders(sanitizedVars, sink.line))
35787
35921
  sanitizedVars.add(v);
35788
35922
  const received = credit.sinkText(sink.line);
35789
- for (const v of sanitizedVars) {
35790
- if (new RegExp(`(?<![\\w])${escapeRegex2(v)}(?![\\w])`).test(received))
35791
- return false;
35792
- }
35793
- return true;
35923
+ const covered = inlineSanitized || [...sanitizedVars].some((v) => new RegExp(`(?<![\\w])${escapeRegex2(v)}(?![\\w])`).test(received));
35924
+ return !covered || credit.rawTaintReaches(sink.type, sink.line, sanitizedVars, csSeeds);
35794
35925
  });
35795
35926
  }
35796
35927
  if (language === "csharp") {
@@ -40364,9 +40495,10 @@ class InterproceduralPass {
40364
40495
  });
40365
40496
  }
40366
40497
  if (csCredit) {
40498
+ const csSeeds = sources.flatMap((s) => s.variable ? [{ variable: s.variable, line: s.line }] : []);
40367
40499
  const credited = new Set;
40368
40500
  filteredAdditionalFlows = filteredAdditionalFlows.filter((f) => {
40369
- if (f.sink_type !== "external_taint_escape" || !csCredit.creditedOnLine(f.sink_line))
40501
+ if (f.sink_type !== "external_taint_escape" || !csCredit.creditedOnLine(f.sink_line, csSeeds))
40370
40502
  return true;
40371
40503
  credited.add(f.sink_line);
40372
40504
  return false;
@@ -51854,7 +51986,7 @@ var colors = {
51854
51986
  };
51855
51987
 
51856
51988
  // src/version.ts
51857
- var version = "4.13.0";
51989
+ var version = "4.13.2";
51858
51990
 
51859
51991
  // src/formatters.ts
51860
51992
  var LIBRARY_API_SURFACE_TAG2 = "library-api-surface:caller-responsibility";
@@ -52406,8 +52538,8 @@ function generateSarifResults(results, crossFileData) {
52406
52538
 
52407
52539
  // src/build-info.ts
52408
52540
  var buildInfo = {
52409
- gitSha: "a45b21b",
52410
- builtAt: "2026-10-08T13:38:40.827Z"
52541
+ gitSha: "3937b7b",
52542
+ builtAt: "2026-10-08T19:28:03.582Z"
52411
52543
  };
52412
52544
 
52413
52545
  // src/utils/args.ts
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "cognium-dev",
3
- "version": "4.13.0",
3
+ "version": "4.13.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.13.0"
68
+ "circle-ir": "4.13.2"
69
69
  },
70
70
  "devDependencies": {
71
71
  "@types/node": "^26.6.4",