cognium-dev 4.13.0 → 4.13.1
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.
- package/dist/cli.js +135 -19
- package/package.json +2 -2
package/dist/cli.js
CHANGED
|
@@ -20039,7 +20039,13 @@ class ConstantPropagator {
|
|
|
20039
20039
|
}
|
|
20040
20040
|
return;
|
|
20041
20041
|
}
|
|
20042
|
-
|
|
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,65 @@ 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 call = withoutStringLiterals(this.sinkText(sinkLine));
|
|
34360
|
+
const rest = this.withoutSanitizerCalls(type, lastCallArguments(call) ?? call);
|
|
34361
|
+
if (this.builtinSanitizes(type, rest))
|
|
34362
|
+
return false;
|
|
34363
|
+
for (const v of tainted)
|
|
34364
|
+
if (!credited.has(v) && mentions(rest, v))
|
|
34365
|
+
return true;
|
|
34366
|
+
return false;
|
|
34367
|
+
}
|
|
34368
|
+
withoutSanitizerCalls(type, text) {
|
|
34369
|
+
const res = [];
|
|
34370
|
+
for (const { re, type: t } of CSHARP_SANITIZER_RES)
|
|
34371
|
+
if (t === type)
|
|
34372
|
+
res.push(re);
|
|
34373
|
+
for (const name2 of this.helpers(type))
|
|
34374
|
+
res.push(new RegExp(`(?<![\\w.])${escapeRegex(name2)}\\s*\\(`));
|
|
34375
|
+
let out2 = text;
|
|
34376
|
+
for (const re of res) {
|
|
34377
|
+
for (let guard = 0;guard < 16; guard++) {
|
|
34378
|
+
const m = re.exec(out2);
|
|
34379
|
+
if (!m)
|
|
34380
|
+
break;
|
|
34381
|
+
if (!m[0].endsWith("(")) {
|
|
34382
|
+
out2 = out2.slice(0, m.index) + out2.slice(m.index + m[0].length);
|
|
34383
|
+
continue;
|
|
34384
|
+
}
|
|
34385
|
+
const open = m.index + m[0].length - 1;
|
|
34386
|
+
const inner = insideParens(out2, open);
|
|
34387
|
+
if (inner === null)
|
|
34388
|
+
break;
|
|
34389
|
+
out2 = out2.slice(0, m.index) + out2.slice(open + inner.length + 2);
|
|
34390
|
+
}
|
|
34391
|
+
}
|
|
34392
|
+
return out2;
|
|
34393
|
+
}
|
|
34274
34394
|
sinkText(line) {
|
|
34275
34395
|
const text = this.lines[line - 1] ?? "";
|
|
34276
34396
|
const guarded = leadingIf(text);
|
|
@@ -34463,21 +34583,19 @@ class CSharpCredit {
|
|
|
34463
34583
|
}
|
|
34464
34584
|
return new Set([...state].filter(([, safe]) => safe).map(([name2]) => name2));
|
|
34465
34585
|
}
|
|
34466
|
-
creditedOnLine(line) {
|
|
34586
|
+
creditedOnLine(line, seeds = []) {
|
|
34467
34587
|
const text = this.sinkText(line);
|
|
34468
34588
|
const types = new Set(CSHARP_SANITIZER_RES.map((r) => r.type));
|
|
34469
34589
|
for (const t of INERT_WHEN_ALPHANUMERIC)
|
|
34470
34590
|
types.add(t);
|
|
34471
34591
|
types.add("ssrf");
|
|
34472
34592
|
for (const type of types) {
|
|
34473
|
-
if (this.sanitizes(type, text))
|
|
34474
|
-
return true;
|
|
34475
34593
|
const credited = new Set([...this.sanitizedVarsAt(type, line), ...this.guardedVarsAt(type, line)]);
|
|
34476
34594
|
for (const v of this.safeBuilders(credited, line))
|
|
34477
34595
|
credited.add(v);
|
|
34478
|
-
|
|
34479
|
-
|
|
34480
|
-
|
|
34596
|
+
const covered = this.sanitizes(type, text) || [...credited].some((v) => mentions(text, v));
|
|
34597
|
+
if (covered && !this.rawTaintReaches(type, line, credited, seeds))
|
|
34598
|
+
return true;
|
|
34481
34599
|
}
|
|
34482
34600
|
return this.fixedHostRequest(line);
|
|
34483
34601
|
}
|
|
@@ -35764,13 +35882,13 @@ class SinkFilterPass {
|
|
|
35764
35882
|
const sanitizedByType = csharpSanitizedVarsByType(ctx.code);
|
|
35765
35883
|
const csFields = new Set(ctx.graph.ir.types.flatMap((t) => (t.fields ?? []).map((f) => f.name)));
|
|
35766
35884
|
const credit = new CSharpCredit(ctx.code, ctx.graph.ir.types.flatMap((t) => t.methods));
|
|
35885
|
+
const csSeeds = mergedSources.flatMap((s) => s.variable ? [{ variable: s.variable, line: s.line }] : []);
|
|
35767
35886
|
const xxeHardened = /\bXmlResolver\s*=\s*null\b/.test(ctx.code) || /\bDtdProcessing\s*=\s*(?:DtdProcessing\s*\.\s*)?(?:Prohibit|Ignore)\b/.test(ctx.code);
|
|
35768
35887
|
filtered = filtered.filter((sink) => {
|
|
35769
35888
|
if (sink.type === "xxe" && xxeHardened)
|
|
35770
35889
|
return false;
|
|
35771
35890
|
const sinkLineText = sourceLines[sink.line - 1] ?? "";
|
|
35772
|
-
|
|
35773
|
-
return false;
|
|
35891
|
+
const inlineSanitized = credit.sanitizes(sink.type, sinkLineText);
|
|
35774
35892
|
if (sink.type === "ssrf" && credit.fixedHostRequest(sink.line))
|
|
35775
35893
|
return false;
|
|
35776
35894
|
const scoped = credit.sanitizedVarsAt(sink.type, sink.line);
|
|
@@ -35786,11 +35904,8 @@ class SinkFilterPass {
|
|
|
35786
35904
|
for (const v of credit.safeBuilders(sanitizedVars, sink.line))
|
|
35787
35905
|
sanitizedVars.add(v);
|
|
35788
35906
|
const received = credit.sinkText(sink.line);
|
|
35789
|
-
|
|
35790
|
-
|
|
35791
|
-
return false;
|
|
35792
|
-
}
|
|
35793
|
-
return true;
|
|
35907
|
+
const covered = inlineSanitized || [...sanitizedVars].some((v) => new RegExp(`(?<![\\w])${escapeRegex2(v)}(?![\\w])`).test(received));
|
|
35908
|
+
return !covered || credit.rawTaintReaches(sink.type, sink.line, sanitizedVars, csSeeds);
|
|
35794
35909
|
});
|
|
35795
35910
|
}
|
|
35796
35911
|
if (language === "csharp") {
|
|
@@ -40364,9 +40479,10 @@ class InterproceduralPass {
|
|
|
40364
40479
|
});
|
|
40365
40480
|
}
|
|
40366
40481
|
if (csCredit) {
|
|
40482
|
+
const csSeeds = sources.flatMap((s) => s.variable ? [{ variable: s.variable, line: s.line }] : []);
|
|
40367
40483
|
const credited = new Set;
|
|
40368
40484
|
filteredAdditionalFlows = filteredAdditionalFlows.filter((f) => {
|
|
40369
|
-
if (f.sink_type !== "external_taint_escape" || !csCredit.creditedOnLine(f.sink_line))
|
|
40485
|
+
if (f.sink_type !== "external_taint_escape" || !csCredit.creditedOnLine(f.sink_line, csSeeds))
|
|
40370
40486
|
return true;
|
|
40371
40487
|
credited.add(f.sink_line);
|
|
40372
40488
|
return false;
|
|
@@ -51854,7 +51970,7 @@ var colors = {
|
|
|
51854
51970
|
};
|
|
51855
51971
|
|
|
51856
51972
|
// src/version.ts
|
|
51857
|
-
var version = "4.13.
|
|
51973
|
+
var version = "4.13.1";
|
|
51858
51974
|
|
|
51859
51975
|
// src/formatters.ts
|
|
51860
51976
|
var LIBRARY_API_SURFACE_TAG2 = "library-api-surface:caller-responsibility";
|
|
@@ -52406,8 +52522,8 @@ function generateSarifResults(results, crossFileData) {
|
|
|
52406
52522
|
|
|
52407
52523
|
// src/build-info.ts
|
|
52408
52524
|
var buildInfo = {
|
|
52409
|
-
gitSha: "
|
|
52410
|
-
builtAt: "2026-10-
|
|
52525
|
+
gitSha: "51b2da7",
|
|
52526
|
+
builtAt: "2026-10-08T18:47:29.958Z"
|
|
52411
52527
|
};
|
|
52412
52528
|
|
|
52413
52529
|
// src/utils/args.ts
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "cognium-dev",
|
|
3
|
-
"version": "4.13.
|
|
3
|
+
"version": "4.13.1",
|
|
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.
|
|
68
|
+
"circle-ir": "4.13.1"
|
|
69
69
|
},
|
|
70
70
|
"devDependencies": {
|
|
71
71
|
"@types/node": "^26.6.4",
|