cognium-dev 4.16.0 → 4.16.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 +153 -25
- package/package.json +2 -2
package/dist/cli.js
CHANGED
|
@@ -20551,20 +20551,25 @@ function findCSharpEarlyReturnGuardSanitizers(code, language) {
|
|
|
20551
20551
|
const lines = code.split(`
|
|
20552
20552
|
`);
|
|
20553
20553
|
const terminatorRe = /\b(throw|return)\b/;
|
|
20554
|
+
const literalArrays = collectCSharpLiteralArrayFields(code);
|
|
20555
|
+
const indexOfRe = /\bif\s*\(\s*(?:System\s*\.\s*)?Array\s*\.\s*IndexOf\s*\(\s*([A-Za-z_]\w*)\s*,\s*([A-Za-z_][\w.]*)\s*\)\s*(?:<\s*0|==\s*-\s*1)\s*\)/;
|
|
20554
20556
|
const allowlistRes = [
|
|
20555
|
-
/\bif\s*\(\s*Array\s*\.\s*IndexOf\s*\([^,]+,\s*([A-Za-z_][\w.]*)\s*\)\s*<\s*0\s*\)/,
|
|
20556
20557
|
/\bif\s*\(\s*!\s*[A-Za-z_][\w.]*\s*\.\s*Contains\s*\(\s*([A-Za-z_][\w.]*)\s*\)\s*\)/
|
|
20557
20558
|
];
|
|
20558
20559
|
const charClassRe = /\bif\s*\(\s*!\s*([A-Za-z_][\w.]*)\s*\.\s*All\s*\(\s*char\s*\.\s*Is[A-Za-z]+\s*\)\s*\)/;
|
|
20559
20560
|
for (let i2 = 0;i2 < lines.length; i2++) {
|
|
20560
20561
|
let guardedExpr = null;
|
|
20561
|
-
|
|
20562
|
-
|
|
20563
|
-
|
|
20564
|
-
|
|
20565
|
-
|
|
20562
|
+
const im = indexOfRe.exec(lines[i2]);
|
|
20563
|
+
if (im && literalArrays.has(im[1]))
|
|
20564
|
+
guardedExpr = im[2];
|
|
20565
|
+
if (!guardedExpr)
|
|
20566
|
+
for (const re of allowlistRes) {
|
|
20567
|
+
const m = re.exec(lines[i2]);
|
|
20568
|
+
if (m) {
|
|
20569
|
+
guardedExpr = m[1];
|
|
20570
|
+
break;
|
|
20571
|
+
}
|
|
20566
20572
|
}
|
|
20567
|
-
}
|
|
20568
20573
|
if (!guardedExpr) {
|
|
20569
20574
|
const m = charClassRe.exec(lines[i2]);
|
|
20570
20575
|
if (m)
|
|
@@ -20594,6 +20599,8 @@ function findCSharpEarlyReturnGuardSanitizers(code, language) {
|
|
|
20594
20599
|
const a = assignRe.exec(lines[l]);
|
|
20595
20600
|
if (a && mentions(a[2]))
|
|
20596
20601
|
guarded.add(a[1]);
|
|
20602
|
+
else if (a)
|
|
20603
|
+
guarded.delete(a[1]);
|
|
20597
20604
|
if (mentions(lines[l])) {
|
|
20598
20605
|
sanitizers.push({
|
|
20599
20606
|
type: "csharp_early_return_guard",
|
|
@@ -20614,6 +20621,26 @@ function findCSharpEarlyReturnGuardSanitizers(code, language) {
|
|
|
20614
20621
|
}
|
|
20615
20622
|
return sanitizers;
|
|
20616
20623
|
}
|
|
20624
|
+
function collectCSharpLiteralArrayFields(code) {
|
|
20625
|
+
const names = new Set;
|
|
20626
|
+
const lit = String.raw`(?:@"(?:[^"]|"")*"|"(?:[^"\\\n]|\\.)*")`;
|
|
20627
|
+
const elems = new RegExp(String.raw`^\s*${lit}(?:\s*,\s*${lit})*\s*,?\s*$`);
|
|
20628
|
+
const declRe = /((?:\b(?:public|private|protected|internal|static|readonly)\s+)+)string\s*\[\s*\]\s+([A-Za-z_]\w*)\s*=\s*(?:new\s+(?:string\s*)?\[\s*\]\s*)?\{([^{}]*)\}\s*;/g;
|
|
20629
|
+
for (const m of code.matchAll(declRe)) {
|
|
20630
|
+
const mods = m[1];
|
|
20631
|
+
const name2 = m[2];
|
|
20632
|
+
if (!/\bstatic\b/.test(mods) || !/\breadonly\b/.test(mods))
|
|
20633
|
+
continue;
|
|
20634
|
+
if (!elems.test(m[3]))
|
|
20635
|
+
continue;
|
|
20636
|
+
const rest = code.slice(0, m.index) + code.slice(m.index + m[0].length);
|
|
20637
|
+
const written = new RegExp(String.raw`\b${name2}\s*(?:\[[^\]]*\]\s*)?(?:[-+*/%&|^]|\?\?)?=(?!=)` + String.raw`|\b(?:ref|out)\s+${name2}\b` + String.raw`|\b${name2}\s*\.\s*SetValue\s*\(` + String.raw`|\bArray\s*\.\s*(?:Clear|Copy|ConstrainedCopy|Fill|Resize|Reverse|Sort)\s*\([^;]*\b${name2}\b`);
|
|
20638
|
+
if (written.test(rest))
|
|
20639
|
+
continue;
|
|
20640
|
+
names.add(name2);
|
|
20641
|
+
}
|
|
20642
|
+
return names;
|
|
20643
|
+
}
|
|
20617
20644
|
function findCSharpFullPathStartsWithGuardSanitizers(code) {
|
|
20618
20645
|
const sanitizers = [];
|
|
20619
20646
|
const lines = code.split(`
|
|
@@ -38184,6 +38211,12 @@ function bareIdentifiers(text) {
|
|
|
38184
38211
|
out2.push(m[1]);
|
|
38185
38212
|
return out2;
|
|
38186
38213
|
}
|
|
38214
|
+
function isLiteralValue(rhs) {
|
|
38215
|
+
if (/\$"/.test(rhs))
|
|
38216
|
+
return false;
|
|
38217
|
+
const ids = bareIdentifiers(rhs);
|
|
38218
|
+
return ids.every((id) => id === "null" || id === "true" || id === "false" || id === "default") || /^\s*(?:string|String)\s*\.\s*Empty\s*$/.test(rhs);
|
|
38219
|
+
}
|
|
38187
38220
|
function insideParens(text, open) {
|
|
38188
38221
|
let depth = 0;
|
|
38189
38222
|
for (let i2 = open;i2 < text.length; i2++) {
|
|
@@ -38372,6 +38405,34 @@ class CSharpCredit {
|
|
|
38372
38405
|
sanitizedVarsAt(type, sinkLine) {
|
|
38373
38406
|
return this.sanitizedVarsIn(type, this.methodAt(sinkLine)?.start_line ?? 1, sinkLine, true);
|
|
38374
38407
|
}
|
|
38408
|
+
fieldWrites(type, fields) {
|
|
38409
|
+
const credited = new Set;
|
|
38410
|
+
const unsanitized = new Set;
|
|
38411
|
+
for (const m of this.methods) {
|
|
38412
|
+
const sanitized = new Set;
|
|
38413
|
+
for (const st of this.statements(m.start_line, m.end_line + 1)) {
|
|
38414
|
+
if (this.methodAt(st.line) !== m)
|
|
38415
|
+
continue;
|
|
38416
|
+
for (const op of this.creditOpsInStatement(st.text)) {
|
|
38417
|
+
const lhs = op.lhs;
|
|
38418
|
+
const keeps = op.kind === "append" ? sanitized.has(lhs) && this.rhsKeepsSanitizerCredit(type, op.rhs, sanitized, true, true) : this.rhsKeepsSanitizerCredit(type, op.rhs, sanitized, true, false);
|
|
38419
|
+
if (keeps)
|
|
38420
|
+
sanitized.add(lhs);
|
|
38421
|
+
else
|
|
38422
|
+
sanitized.delete(lhs);
|
|
38423
|
+
if (!fields.has(lhs) || isLiteralValue(op.rhs))
|
|
38424
|
+
continue;
|
|
38425
|
+
if (keeps)
|
|
38426
|
+
credited.add(lhs);
|
|
38427
|
+
else
|
|
38428
|
+
unsanitized.add(lhs);
|
|
38429
|
+
}
|
|
38430
|
+
}
|
|
38431
|
+
}
|
|
38432
|
+
for (const f of unsanitized)
|
|
38433
|
+
credited.delete(f);
|
|
38434
|
+
return { sanitized: credited, unsanitized };
|
|
38435
|
+
}
|
|
38375
38436
|
rawTaintReaches(type, sinkLine, credited, seeds) {
|
|
38376
38437
|
if (seeds.length === 0)
|
|
38377
38438
|
return false;
|
|
@@ -38921,10 +38982,12 @@ function isJavaStaticFormatterCall(call, declaredTypeNames, imports, calls) {
|
|
|
38921
38982
|
if (other.arguments.some((a) => a.expression.includes(needle)))
|
|
38922
38983
|
return false;
|
|
38923
38984
|
}
|
|
38924
|
-
|
|
38985
|
+
return isJdkClassReceiver(call.receiver, JAVA_FORMATTER_CLASSES, declaredTypeNames, imports);
|
|
38986
|
+
}
|
|
38987
|
+
function isJdkClassReceiver(receiver, classes, declaredTypeNames, imports) {
|
|
38925
38988
|
const dot = receiver.lastIndexOf(".");
|
|
38926
38989
|
const simple = dot < 0 ? receiver : receiver.slice(dot + 1);
|
|
38927
|
-
const pkg =
|
|
38990
|
+
const pkg = classes.get(simple);
|
|
38928
38991
|
if (pkg === undefined)
|
|
38929
38992
|
return false;
|
|
38930
38993
|
if (dot >= 0)
|
|
@@ -38941,9 +39004,44 @@ function isJavaStaticFormatterCall(call, declaredTypeNames, imports, calls) {
|
|
|
38941
39004
|
return true;
|
|
38942
39005
|
return imports.some((imp) => imp.from_package === pkg && (imp.imported_name === simple || imp.is_wildcard));
|
|
38943
39006
|
}
|
|
39007
|
+
var JAVA_CHARACTER_CLASS = new Map([["Character", "java.lang"]]);
|
|
39008
|
+
var JAVA_CHARACTER_PREDICATES = new Set([
|
|
39009
|
+
"isLetter",
|
|
39010
|
+
"isDigit",
|
|
39011
|
+
"isLetterOrDigit",
|
|
39012
|
+
"isAlphabetic",
|
|
39013
|
+
"isWhitespace",
|
|
39014
|
+
"isSpaceChar",
|
|
39015
|
+
"isUpperCase",
|
|
39016
|
+
"isLowerCase",
|
|
39017
|
+
"isTitleCase",
|
|
39018
|
+
"isISOControl",
|
|
39019
|
+
"isDefined",
|
|
39020
|
+
"isIdeographic",
|
|
39021
|
+
"isMirrored",
|
|
39022
|
+
"isJavaIdentifierStart",
|
|
39023
|
+
"isJavaIdentifierPart",
|
|
39024
|
+
"isUnicodeIdentifierStart",
|
|
39025
|
+
"isUnicodeIdentifierPart",
|
|
39026
|
+
"isIdentifierIgnorable",
|
|
39027
|
+
"isHighSurrogate",
|
|
39028
|
+
"isLowSurrogate",
|
|
39029
|
+
"isSurrogate",
|
|
39030
|
+
"isSurrogatePair",
|
|
39031
|
+
"isValidCodePoint",
|
|
39032
|
+
"isBmpCodePoint",
|
|
39033
|
+
"isSupplementaryCodePoint"
|
|
39034
|
+
]);
|
|
39035
|
+
function isJavaCharacterPredicateCall(call, declaredTypeNames, imports) {
|
|
39036
|
+
if (!call.receiver || !JAVA_CHARACTER_PREDICATES.has(call.method_name))
|
|
39037
|
+
return false;
|
|
39038
|
+
return isJdkClassReceiver(call.receiver, JAVA_CHARACTER_CLASS, declaredTypeNames, imports);
|
|
39039
|
+
}
|
|
38944
39040
|
function isJavaNonEscapeCall(call, declaredTypeNames, imports, calls) {
|
|
38945
39041
|
if (isJavaStaticFormatterCall(call, declaredTypeNames, imports, calls))
|
|
38946
39042
|
return true;
|
|
39043
|
+
if (isJavaCharacterPredicateCall(call, declaredTypeNames, imports))
|
|
39044
|
+
return true;
|
|
38947
39045
|
if (call.receiver)
|
|
38948
39046
|
return false;
|
|
38949
39047
|
const isCtor = call.is_constructor === true || (call.resolution?.target ?? "").endsWith(".<init>");
|
|
@@ -42804,6 +42902,15 @@ class SinkFilterPass {
|
|
|
42804
42902
|
const csFields = new Set(ctx.graph.ir.types.flatMap((t) => (t.fields ?? []).map((f) => f.name)));
|
|
42805
42903
|
const credit = new CSharpCredit(ctx.code, ctx.graph.ir.types.flatMap((t) => t.methods));
|
|
42806
42904
|
const csSeeds = mergedSources.flatMap((s) => s.variable ? [{ variable: s.variable, line: s.line }] : []);
|
|
42905
|
+
const fieldWritesByType = new Map;
|
|
42906
|
+
const fieldWritesFor = (type) => {
|
|
42907
|
+
let w = fieldWritesByType.get(type);
|
|
42908
|
+
if (!w) {
|
|
42909
|
+
w = credit.fieldWrites(type, csFields);
|
|
42910
|
+
fieldWritesByType.set(type, w);
|
|
42911
|
+
}
|
|
42912
|
+
return w;
|
|
42913
|
+
};
|
|
42807
42914
|
const xxeHardened = /\bXmlResolver\s*=\s*null\b/.test(ctx.code) || /\bDtdProcessing\s*=\s*(?:DtdProcessing\s*\.\s*)?(?:Prohibit|Ignore)\b/.test(ctx.code);
|
|
42808
42915
|
filtered = filtered.filter((sink) => {
|
|
42809
42916
|
if (sink.type === "xxe" && xxeHardened)
|
|
@@ -42815,11 +42922,15 @@ class SinkFilterPass {
|
|
|
42815
42922
|
const scoped = credit.sanitizedVarsAt(sink.type, sink.line);
|
|
42816
42923
|
const fileWide = sanitizedByType.get(sink.type);
|
|
42817
42924
|
const sanitizedVars = new Set(scoped);
|
|
42925
|
+
const fieldWrites = fieldWritesFor(sink.type);
|
|
42818
42926
|
if (fileWide) {
|
|
42819
|
-
for (const v of fileWide)
|
|
42820
|
-
if (csFields.has(v))
|
|
42927
|
+
for (const v of fileWide) {
|
|
42928
|
+
if (csFields.has(v) && !fieldWrites.unsanitized.has(v))
|
|
42821
42929
|
sanitizedVars.add(v);
|
|
42930
|
+
}
|
|
42822
42931
|
}
|
|
42932
|
+
for (const v of fieldWrites.sanitized)
|
|
42933
|
+
sanitizedVars.add(v);
|
|
42823
42934
|
for (const v of credit.guardedVarsAt(sink.type, sink.line))
|
|
42824
42935
|
sanitizedVars.add(v);
|
|
42825
42936
|
for (const v of credit.safeBuilders(sanitizedVars, sink.line))
|
|
@@ -45124,26 +45235,38 @@ function resolveJavaDerivedAnchor(varName, derived, realSourceByVar, codeLines,
|
|
|
45124
45235
|
}
|
|
45125
45236
|
return best;
|
|
45126
45237
|
}
|
|
45127
|
-
function javaReceiverChainDerivedVar(
|
|
45238
|
+
function javaReceiverChainDerivedVar(rhsByLocal, localVar, seedVar) {
|
|
45128
45239
|
if (!seedVar || localVar === seedVar)
|
|
45129
45240
|
return false;
|
|
45241
|
+
const rhsList = rhsByLocal.get(localVar);
|
|
45242
|
+
if (!rhsList)
|
|
45243
|
+
return false;
|
|
45130
45244
|
const esc = seedVar.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
45131
45245
|
const receiverOnRhs = new RegExp(`(?:^|[^\\w.$])${esc}\\s*\\.`, "u");
|
|
45246
|
+
return rhsList.some((rhs) => receiverOnRhs.test(rhs));
|
|
45247
|
+
}
|
|
45248
|
+
function indexJavaLocalAssignments(code) {
|
|
45132
45249
|
const declRe = /^\s*(?:public|private|protected|static|final|volatile|transient|\s)*\s*(?:[A-Za-z_][\w.]*(?:\s*<[^>]*>)?(?:\s*\[\s*\])*)\s+([A-Za-z_]\w*)\s*=\s*(.+?);\s*$/;
|
|
45133
45250
|
const assignRe = /^\s*([A-Za-z_]\w*)\s*=\s*(.+?);\s*$/;
|
|
45251
|
+
const index = new Map;
|
|
45134
45252
|
for (const line of code.split(`
|
|
45135
45253
|
`)) {
|
|
45136
45254
|
const trimmed = line.trimStart();
|
|
45137
45255
|
if (trimmed.startsWith("//") || trimmed.startsWith("*"))
|
|
45138
45256
|
continue;
|
|
45257
|
+
if (!line.includes("=") || !line.includes(";"))
|
|
45258
|
+
continue;
|
|
45139
45259
|
const m = declRe.exec(line) ?? assignRe.exec(line);
|
|
45140
|
-
if (!m
|
|
45260
|
+
if (!m)
|
|
45141
45261
|
continue;
|
|
45262
|
+
const list = index.get(m[1]);
|
|
45142
45263
|
const rhs = stripJavaTrailingComment(m[2]);
|
|
45143
|
-
if (
|
|
45144
|
-
|
|
45264
|
+
if (list)
|
|
45265
|
+
list.push(rhs);
|
|
45266
|
+
else
|
|
45267
|
+
index.set(m[1], [rhs]);
|
|
45145
45268
|
}
|
|
45146
|
-
return
|
|
45269
|
+
return index;
|
|
45147
45270
|
}
|
|
45148
45271
|
function makeConstPropVeto(constProp, graph, sources, code, language, sanitizers) {
|
|
45149
45272
|
const lines = typeof code === "string" ? code.split(`
|
|
@@ -45264,15 +45387,20 @@ class TaintPropagationPass {
|
|
|
45264
45387
|
const sinkFilter = ctx.getResult("sink-filter");
|
|
45265
45388
|
const { sources, sinks, sanitizers } = sinkFilter;
|
|
45266
45389
|
if (ctx.language === "java" && typeof ctx.code === "string") {
|
|
45390
|
+
const rhsByLocal = indexJavaLocalAssignments(ctx.code);
|
|
45391
|
+
const seen = new Set;
|
|
45267
45392
|
for (const s of sources) {
|
|
45268
|
-
if (!s.variable)
|
|
45393
|
+
if (!s.variable || seen.has(s.variable))
|
|
45269
45394
|
continue;
|
|
45270
|
-
|
|
45271
|
-
|
|
45272
|
-
|
|
45273
|
-
|
|
45395
|
+
seen.add(s.variable);
|
|
45396
|
+
const seedVar = s.variable;
|
|
45397
|
+
const candidates = [...rhsByLocal.keys()].filter((v) => !constProp.tainted.has(v) && javaReceiverChainDerivedVar(rhsByLocal, v, seedVar));
|
|
45398
|
+
if (candidates.length === 0)
|
|
45399
|
+
continue;
|
|
45400
|
+
const derived = buildJavaTaintedVars(ctx.code, new Set([seedVar]));
|
|
45401
|
+
for (const v of candidates) {
|
|
45402
|
+
if (derived.has(v))
|
|
45274
45403
|
constProp.tainted.add(v);
|
|
45275
|
-
}
|
|
45276
45404
|
}
|
|
45277
45405
|
}
|
|
45278
45406
|
}
|
|
@@ -60106,7 +60234,7 @@ var colors = {
|
|
|
60106
60234
|
};
|
|
60107
60235
|
|
|
60108
60236
|
// src/version.ts
|
|
60109
|
-
var version = "4.16.
|
|
60237
|
+
var version = "4.16.1";
|
|
60110
60238
|
|
|
60111
60239
|
// src/formatters.ts
|
|
60112
60240
|
var LIBRARY_API_SURFACE_TAG2 = "library-api-surface:caller-responsibility";
|
|
@@ -60673,8 +60801,8 @@ function generateSarifResults(results, crossFileData) {
|
|
|
60673
60801
|
|
|
60674
60802
|
// src/build-info.ts
|
|
60675
60803
|
var buildInfo = {
|
|
60676
|
-
gitSha: "
|
|
60677
|
-
builtAt: "2026-10-
|
|
60804
|
+
gitSha: "7c01fdc",
|
|
60805
|
+
builtAt: "2026-10-18T03:55:29.307Z"
|
|
60678
60806
|
};
|
|
60679
60807
|
|
|
60680
60808
|
// src/utils/args.ts
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "cognium-dev",
|
|
3
|
-
"version": "4.16.
|
|
3
|
+
"version": "4.16.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.16.
|
|
68
|
+
"circle-ir": "4.16.1"
|
|
69
69
|
},
|
|
70
70
|
"devDependencies": {
|
|
71
71
|
"@types/node": "^26.6.5",
|