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.
- package/dist/cli.js +151 -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,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
|
-
|
|
34479
|
-
|
|
34480
|
-
|
|
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
|
-
|
|
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
|
-
|
|
35790
|
-
|
|
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.
|
|
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: "
|
|
52410
|
-
builtAt: "2026-10-
|
|
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.
|
|
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.
|
|
68
|
+
"circle-ir": "4.13.2"
|
|
69
69
|
},
|
|
70
70
|
"devDependencies": {
|
|
71
71
|
"@types/node": "^26.6.4",
|