cognium-dev 3.65.0 → 3.66.0
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 +273 -3
- package/package.json +2 -2
package/dist/cli.js
CHANGED
|
@@ -19080,10 +19080,26 @@ class CrossFileResolver {
|
|
|
19080
19080
|
}
|
|
19081
19081
|
resolveWithReceiver(call, fromFile) {
|
|
19082
19082
|
const receiver = call.receiver;
|
|
19083
|
-
const receiverType = this.inferReceiverType(receiver, fromFile);
|
|
19083
|
+
const receiverType = call.receiver_type_fqn ?? this.inferReceiverType(receiver, fromFile);
|
|
19084
19084
|
if (receiverType) {
|
|
19085
19085
|
const methodSymbol = this.symbolTable.findMethod(receiverType, call.method_name);
|
|
19086
19086
|
if (methodSymbol) {
|
|
19087
|
+
const parent = methodSymbol.parentType ? this.symbolTable.getSymbol(methodSymbol.parentType) : undefined;
|
|
19088
|
+
if (parent && parent.kind === "interface") {
|
|
19089
|
+
const candidates2 = this.findPolymorphicCandidates(receiverType, call.method_name);
|
|
19090
|
+
if (candidates2.length > 0) {
|
|
19091
|
+
const primary = candidates2[0];
|
|
19092
|
+
return {
|
|
19093
|
+
call,
|
|
19094
|
+
sourceFile: fromFile,
|
|
19095
|
+
targetFile: primary.file,
|
|
19096
|
+
targetMethod: primary.fqn,
|
|
19097
|
+
targetClass: primary.parentType || receiverType,
|
|
19098
|
+
resolution: "polymorphic",
|
|
19099
|
+
candidates: candidates2.map((c) => c.fqn)
|
|
19100
|
+
};
|
|
19101
|
+
}
|
|
19102
|
+
}
|
|
19087
19103
|
return {
|
|
19088
19104
|
call,
|
|
19089
19105
|
sourceFile: fromFile,
|
|
@@ -20096,7 +20112,8 @@ class CrossFilePass {
|
|
|
20096
20112
|
});
|
|
20097
20113
|
const ipPaths = [
|
|
20098
20114
|
...resolver.findInterproceduralTaintPaths(),
|
|
20099
|
-
...resolver.findFieldBindingTaintPaths()
|
|
20115
|
+
...resolver.findFieldBindingTaintPaths(),
|
|
20116
|
+
...findCrossInstanceAliasingPaths(projectGraph, sourceLines)
|
|
20100
20117
|
];
|
|
20101
20118
|
for (let i2 = 0;i2 < ipPaths.length; i2++) {
|
|
20102
20119
|
const p = ipPaths[i2];
|
|
@@ -20172,6 +20189,108 @@ class CrossFilePass {
|
|
|
20172
20189
|
return { crossFileCalls, taintPaths, typeHierarchy };
|
|
20173
20190
|
}
|
|
20174
20191
|
}
|
|
20192
|
+
function findCrossInstanceAliasingPaths(projectGraph, _sourceLines) {
|
|
20193
|
+
const paths = [];
|
|
20194
|
+
const javaHttpPattern = /\b(?:req|request|httpRequest|servletRequest|httpServletRequest)\.(?:getParameter|getParameterValues|getParameterMap|getHeader|getHeaders|getCookies|getQueryString|getPathInfo|getRequestURI|getRequestURL|getInputStream|getReader)\b/;
|
|
20195
|
+
const aliasWriteRe = /^\s*this\.([A-Za-z_]\w*)\.([A-Za-z_]\w*)\s*=\s*(.+?)(?:;\s*)?$/;
|
|
20196
|
+
const typeIndex = new Map;
|
|
20197
|
+
for (const filePath of projectGraph.filePaths) {
|
|
20198
|
+
const ir = projectGraph.getIR(filePath);
|
|
20199
|
+
if (!ir)
|
|
20200
|
+
continue;
|
|
20201
|
+
if (ir.meta.language !== "java")
|
|
20202
|
+
continue;
|
|
20203
|
+
for (const t of ir.types) {
|
|
20204
|
+
if (t.kind === "class")
|
|
20205
|
+
typeIndex.set(t.name, { file: filePath, type: t, ir });
|
|
20206
|
+
}
|
|
20207
|
+
}
|
|
20208
|
+
if (typeIndex.size === 0)
|
|
20209
|
+
return paths;
|
|
20210
|
+
for (const filePath of projectGraph.filePaths) {
|
|
20211
|
+
const ir = projectGraph.getIR(filePath);
|
|
20212
|
+
if (!ir)
|
|
20213
|
+
continue;
|
|
20214
|
+
if (ir.meta.language !== "java")
|
|
20215
|
+
continue;
|
|
20216
|
+
const lines = _sourceLines.get(filePath);
|
|
20217
|
+
if (!lines || lines.length === 0)
|
|
20218
|
+
continue;
|
|
20219
|
+
for (const type of ir.types) {
|
|
20220
|
+
if (type.kind !== "class")
|
|
20221
|
+
continue;
|
|
20222
|
+
const aliasFields = new Map;
|
|
20223
|
+
for (const f of type.fields) {
|
|
20224
|
+
if (!f.type)
|
|
20225
|
+
continue;
|
|
20226
|
+
const simple = f.type.replace(/<.*>/g, "").replace(/\[\]$/, "").trim();
|
|
20227
|
+
if (typeIndex.has(simple))
|
|
20228
|
+
aliasFields.set(f.name, simple);
|
|
20229
|
+
}
|
|
20230
|
+
if (aliasFields.size === 0)
|
|
20231
|
+
continue;
|
|
20232
|
+
for (const m of type.methods) {
|
|
20233
|
+
if (m.name === type.name)
|
|
20234
|
+
continue;
|
|
20235
|
+
const mStart = m.start_line;
|
|
20236
|
+
const mEnd = m.end_line;
|
|
20237
|
+
for (let i2 = mStart - 1;i2 < Math.min(mEnd, lines.length); i2++) {
|
|
20238
|
+
const trimmed = (lines[i2] ?? "").trim();
|
|
20239
|
+
if (!trimmed || trimmed.startsWith("//"))
|
|
20240
|
+
continue;
|
|
20241
|
+
const wm = trimmed.match(aliasWriteRe);
|
|
20242
|
+
if (!wm)
|
|
20243
|
+
continue;
|
|
20244
|
+
const aliasField = wm[1];
|
|
20245
|
+
const innerField = wm[2];
|
|
20246
|
+
const rhs = wm[3].trim().replace(/;\s*$/, "");
|
|
20247
|
+
const aliasType = aliasFields.get(aliasField);
|
|
20248
|
+
if (!aliasType)
|
|
20249
|
+
continue;
|
|
20250
|
+
const target = typeIndex.get(aliasType);
|
|
20251
|
+
if (!target)
|
|
20252
|
+
continue;
|
|
20253
|
+
if (!target.type.fields.some((f) => f.name === innerField))
|
|
20254
|
+
continue;
|
|
20255
|
+
if (!javaHttpPattern.test(rhs))
|
|
20256
|
+
continue;
|
|
20257
|
+
const innerRe = new RegExp(`\\b${innerField}\\b`);
|
|
20258
|
+
for (const tm of target.type.methods) {
|
|
20259
|
+
const sinksInTarget = target.ir.taint.sinks.filter((s) => s.line >= tm.start_line && s.line <= tm.end_line);
|
|
20260
|
+
for (const sink of sinksInTarget) {
|
|
20261
|
+
const callsAtSink = target.ir.calls.filter((c) => c.location.line === sink.line);
|
|
20262
|
+
let matched = false;
|
|
20263
|
+
for (const c of callsAtSink) {
|
|
20264
|
+
for (const a of c.arguments ?? []) {
|
|
20265
|
+
if (innerRe.test(a.expression ?? "") || a.variable === innerField) {
|
|
20266
|
+
matched = true;
|
|
20267
|
+
break;
|
|
20268
|
+
}
|
|
20269
|
+
}
|
|
20270
|
+
if (matched)
|
|
20271
|
+
break;
|
|
20272
|
+
}
|
|
20273
|
+
if (!matched)
|
|
20274
|
+
continue;
|
|
20275
|
+
paths.push({
|
|
20276
|
+
source: { file: filePath, line: i2 + 1, type: "http_param" },
|
|
20277
|
+
sink: { file: target.file, line: sink.line, type: sink.type, cwe: sink.cwe },
|
|
20278
|
+
hops: [
|
|
20279
|
+
{ file: filePath, line: i2 + 1, method: m.name, kind: "source" },
|
|
20280
|
+
{ file: filePath, line: i2 + 1, method: m.name, kind: "field_write" },
|
|
20281
|
+
{ file: target.file, line: tm.start_line, method: tm.name, kind: "field_read" },
|
|
20282
|
+
{ file: target.file, line: sink.line, method: tm.name, kind: "sink" }
|
|
20283
|
+
],
|
|
20284
|
+
confidence: 0.65
|
|
20285
|
+
});
|
|
20286
|
+
}
|
|
20287
|
+
}
|
|
20288
|
+
}
|
|
20289
|
+
}
|
|
20290
|
+
}
|
|
20291
|
+
}
|
|
20292
|
+
return paths;
|
|
20293
|
+
}
|
|
20175
20294
|
|
|
20176
20295
|
// ../circle-ir/dist/analysis/html/html-extractor.js
|
|
20177
20296
|
var EVENT_HANDLER_ATTRS = new Set([
|
|
@@ -20878,6 +20997,8 @@ class LanguageSourcesPass {
|
|
|
20878
20997
|
const additionalSanitizers = [];
|
|
20879
20998
|
additionalSources.push(...findGetterSources(types, constProp.instanceFieldTaint, code));
|
|
20880
20999
|
additionalSources.push(...findOopFieldReadSources(types, code, language));
|
|
21000
|
+
additionalSources.push(...findStaticFieldSources(types, code, language));
|
|
21001
|
+
additionalSources.push(...findSetterChainSources(types, code, language));
|
|
20881
21002
|
additionalSources.push(...findJavaScriptAssignmentSources(code, language));
|
|
20882
21003
|
const jsDOMSinks = findJavaScriptDOMSinks(code, language);
|
|
20883
21004
|
for (const s of jsDOMSinks) {
|
|
@@ -21106,6 +21227,155 @@ function findOopFieldReadSources(types, sourceCode, language) {
|
|
|
21106
21227
|
}
|
|
21107
21228
|
return sources;
|
|
21108
21229
|
}
|
|
21230
|
+
function findStaticFieldSources(types, sourceCode, language) {
|
|
21231
|
+
if (language !== "java")
|
|
21232
|
+
return [];
|
|
21233
|
+
const sources = [];
|
|
21234
|
+
const lines = sourceCode.split(`
|
|
21235
|
+
`);
|
|
21236
|
+
const javaHttpPattern = /\b(?:req|request|httpRequest|servletRequest|httpServletRequest)\.(?:getParameter|getParameterValues|getParameterMap|getHeader|getHeaders|getCookies|getQueryString|getPathInfo|getRequestURI|getRequestURL|getInputStream|getReader)\b/;
|
|
21237
|
+
for (const type of types) {
|
|
21238
|
+
if (type.kind !== "class")
|
|
21239
|
+
continue;
|
|
21240
|
+
if (type.name === "<module>")
|
|
21241
|
+
continue;
|
|
21242
|
+
const staticFields = new Set;
|
|
21243
|
+
for (const f of type.fields) {
|
|
21244
|
+
if (f.modifiers.includes("static"))
|
|
21245
|
+
staticFields.add(f.name);
|
|
21246
|
+
}
|
|
21247
|
+
if (staticFields.size === 0)
|
|
21248
|
+
continue;
|
|
21249
|
+
const qualifiedAssignRe = new RegExp(`^\\s*${type.name}\\.([A-Za-z_]\\w*)\\s*=\\s*(.+?)(?:;\\s*)?$`);
|
|
21250
|
+
const bareAssignRe = /^\s*([A-Za-z_]\w*)\s*=\s*(.+?)(?:;\s*)?$/;
|
|
21251
|
+
for (const m of type.methods) {
|
|
21252
|
+
if (!m.modifiers.includes("static"))
|
|
21253
|
+
continue;
|
|
21254
|
+
const mStart = m.start_line;
|
|
21255
|
+
const mEnd = m.end_line;
|
|
21256
|
+
for (let i2 = mStart - 1;i2 < Math.min(mEnd, lines.length); i2++) {
|
|
21257
|
+
const line = lines[i2] ?? "";
|
|
21258
|
+
const trimmed = line.trim();
|
|
21259
|
+
if (!trimmed || trimmed.startsWith("//"))
|
|
21260
|
+
continue;
|
|
21261
|
+
let fieldName = null;
|
|
21262
|
+
let rhs = null;
|
|
21263
|
+
const qm = trimmed.match(qualifiedAssignRe);
|
|
21264
|
+
if (qm) {
|
|
21265
|
+
fieldName = qm[1];
|
|
21266
|
+
rhs = qm[2];
|
|
21267
|
+
} else {
|
|
21268
|
+
const bm = trimmed.match(bareAssignRe);
|
|
21269
|
+
if (bm) {
|
|
21270
|
+
fieldName = bm[1];
|
|
21271
|
+
rhs = bm[2];
|
|
21272
|
+
}
|
|
21273
|
+
}
|
|
21274
|
+
if (!fieldName || !rhs)
|
|
21275
|
+
continue;
|
|
21276
|
+
if (!staticFields.has(fieldName))
|
|
21277
|
+
continue;
|
|
21278
|
+
rhs = rhs.trim().replace(/;\s*$/, "");
|
|
21279
|
+
if (!javaHttpPattern.test(rhs))
|
|
21280
|
+
continue;
|
|
21281
|
+
sources.push({
|
|
21282
|
+
type: "http_param",
|
|
21283
|
+
location: `${type.name}.${fieldName} static field set in ${m.name}() — #78 round 2`,
|
|
21284
|
+
severity: "high",
|
|
21285
|
+
line: i2 + 1,
|
|
21286
|
+
confidence: 0.85,
|
|
21287
|
+
variable: fieldName
|
|
21288
|
+
});
|
|
21289
|
+
sources.push({
|
|
21290
|
+
type: "http_param",
|
|
21291
|
+
location: `${type.name}.${fieldName} static field (qualified read alias) — #78 round 2`,
|
|
21292
|
+
severity: "high",
|
|
21293
|
+
line: i2 + 1,
|
|
21294
|
+
confidence: 0.85,
|
|
21295
|
+
variable: `${type.name}.${fieldName}`
|
|
21296
|
+
});
|
|
21297
|
+
}
|
|
21298
|
+
}
|
|
21299
|
+
}
|
|
21300
|
+
return sources;
|
|
21301
|
+
}
|
|
21302
|
+
function findSetterChainSources(types, sourceCode, language) {
|
|
21303
|
+
if (language !== "java")
|
|
21304
|
+
return [];
|
|
21305
|
+
const sources = [];
|
|
21306
|
+
const lines = sourceCode.split(`
|
|
21307
|
+
`);
|
|
21308
|
+
const javaHttpPattern = /\b(?:req|request|httpRequest|servletRequest|httpServletRequest)\.(?:getParameter|getParameterValues|getParameterMap|getHeader|getHeaders|getCookies|getQueryString|getPathInfo|getRequestURI|getRequestURL|getInputStream|getReader)\b/;
|
|
21309
|
+
for (const type of types) {
|
|
21310
|
+
if (type.kind !== "class")
|
|
21311
|
+
continue;
|
|
21312
|
+
if (type.name === "<module>")
|
|
21313
|
+
continue;
|
|
21314
|
+
const pairs = new Map;
|
|
21315
|
+
const setterRe = /this\.([A-Za-z_]\w*)\s*=\s*([A-Za-z_]\w*)\s*;?/;
|
|
21316
|
+
const getterRe = /return\s+this\.([A-Za-z_]\w*)\s*;?/;
|
|
21317
|
+
for (const m of type.methods) {
|
|
21318
|
+
if (m.name === type.name)
|
|
21319
|
+
continue;
|
|
21320
|
+
const mStart = m.start_line;
|
|
21321
|
+
const mEnd = m.end_line;
|
|
21322
|
+
const fullBody = lines.slice(mStart - 1, Math.min(mEnd, lines.length)).join(`
|
|
21323
|
+
`);
|
|
21324
|
+
const open = fullBody.indexOf("{");
|
|
21325
|
+
const close = fullBody.lastIndexOf("}");
|
|
21326
|
+
if (open < 0 || close < 0 || close <= open)
|
|
21327
|
+
continue;
|
|
21328
|
+
const inner = fullBody.slice(open + 1, close).replace(/\/\/[^\n]*/g, "").trim();
|
|
21329
|
+
if (!inner)
|
|
21330
|
+
continue;
|
|
21331
|
+
const sm = inner.match(setterRe);
|
|
21332
|
+
if (sm && m.parameters.length === 1 && sm[2] === m.parameters[0].name) {
|
|
21333
|
+
const remainder = inner.replace(sm[0], "").trim();
|
|
21334
|
+
if (!remainder) {
|
|
21335
|
+
const entry = pairs.get(sm[1]) ?? {};
|
|
21336
|
+
entry.setter = m.name;
|
|
21337
|
+
pairs.set(sm[1], entry);
|
|
21338
|
+
continue;
|
|
21339
|
+
}
|
|
21340
|
+
}
|
|
21341
|
+
const gm = inner.match(getterRe);
|
|
21342
|
+
if (gm && m.parameters.length === 0) {
|
|
21343
|
+
const remainder = inner.replace(gm[0], "").trim();
|
|
21344
|
+
if (!remainder) {
|
|
21345
|
+
const entry = pairs.get(gm[1]) ?? {};
|
|
21346
|
+
entry.getter = m.name;
|
|
21347
|
+
pairs.set(gm[1], entry);
|
|
21348
|
+
}
|
|
21349
|
+
}
|
|
21350
|
+
}
|
|
21351
|
+
for (const [, { setter, getter }] of pairs) {
|
|
21352
|
+
if (!setter || !getter)
|
|
21353
|
+
continue;
|
|
21354
|
+
const setterCallRe = new RegExp(`\\b([A-Za-z_]\\w*)\\.${setter}\\s*\\(\\s*([^)]+?)\\s*\\)\\s*;?`);
|
|
21355
|
+
for (let i2 = 0;i2 < lines.length; i2++) {
|
|
21356
|
+
const line = lines[i2] ?? "";
|
|
21357
|
+
const trimmed = line.trim();
|
|
21358
|
+
if (!trimmed || trimmed.startsWith("//"))
|
|
21359
|
+
continue;
|
|
21360
|
+
const cm = trimmed.match(setterCallRe);
|
|
21361
|
+
if (!cm)
|
|
21362
|
+
continue;
|
|
21363
|
+
const arg = cm[2];
|
|
21364
|
+
if (!javaHttpPattern.test(arg))
|
|
21365
|
+
continue;
|
|
21366
|
+
sources.push({
|
|
21367
|
+
type: "http_param",
|
|
21368
|
+
location: `${type.name}.${setter}(tainted) → ${type.name}.${getter}() chain — #78 round 2`,
|
|
21369
|
+
severity: "high",
|
|
21370
|
+
line: i2 + 1,
|
|
21371
|
+
confidence: 0.75,
|
|
21372
|
+
variable: getter
|
|
21373
|
+
});
|
|
21374
|
+
}
|
|
21375
|
+
}
|
|
21376
|
+
}
|
|
21377
|
+
return sources;
|
|
21378
|
+
}
|
|
21109
21379
|
function findJavaScriptAssignmentSources(sourceCode, language) {
|
|
21110
21380
|
if (!["javascript", "typescript"].includes(language))
|
|
21111
21381
|
return [];
|
|
@@ -31047,7 +31317,7 @@ var colors = {
|
|
|
31047
31317
|
};
|
|
31048
31318
|
|
|
31049
31319
|
// src/version.ts
|
|
31050
|
-
var version = "3.
|
|
31320
|
+
var version = "3.66.0";
|
|
31051
31321
|
|
|
31052
31322
|
// src/formatters.ts
|
|
31053
31323
|
var SINK_SEVERITY = {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "cognium-dev",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.66.0",
|
|
4
4
|
"description": "Static Application Security Testing CLI for detecting security vulnerabilities via taint tracking",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|
|
@@ -65,7 +65,7 @@
|
|
|
65
65
|
"registry": "https://registry.npmjs.org/"
|
|
66
66
|
},
|
|
67
67
|
"dependencies": {
|
|
68
|
-
"circle-ir": "^3.
|
|
68
|
+
"circle-ir": "^3.66.0"
|
|
69
69
|
},
|
|
70
70
|
"devDependencies": {
|
|
71
71
|
"@types/node": "^25.5.0",
|