cognium-dev 3.41.0 → 3.43.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 +165 -12
- package/package.json +2 -2
package/dist/cli.js
CHANGED
|
@@ -5321,11 +5321,22 @@ function inferJSTypeFromReceiver(receiver) {
|
|
|
5321
5321
|
function buildResolutionContext(tree, cache) {
|
|
5322
5322
|
const context = {
|
|
5323
5323
|
className: null,
|
|
5324
|
+
packageName: null,
|
|
5324
5325
|
methodNames: new Set,
|
|
5325
5326
|
fieldTypes: new Map,
|
|
5326
5327
|
localVarTypes: new Map,
|
|
5327
|
-
|
|
5328
|
+
paramTypes: new Map,
|
|
5329
|
+
imports: new Map,
|
|
5330
|
+
wildcardImports: []
|
|
5328
5331
|
};
|
|
5332
|
+
const packages = getNodesFromCache(tree.rootNode, "package_declaration", cache);
|
|
5333
|
+
if (packages.length > 0) {
|
|
5334
|
+
const text = getNodeText(packages[0]);
|
|
5335
|
+
const match = text.match(/package\s+([a-zA-Z0-9_.]+)/);
|
|
5336
|
+
if (match) {
|
|
5337
|
+
context.packageName = match[1];
|
|
5338
|
+
}
|
|
5339
|
+
}
|
|
5329
5340
|
const classes = getNodesFromCache(tree.rootNode, "class_declaration", cache);
|
|
5330
5341
|
if (classes.length > 0) {
|
|
5331
5342
|
const nameNode = classes[0].childForFieldName("name");
|
|
@@ -5339,6 +5350,17 @@ function buildResolutionContext(tree, cache) {
|
|
|
5339
5350
|
if (nameNode) {
|
|
5340
5351
|
context.methodNames.add(getNodeText(nameNode));
|
|
5341
5352
|
}
|
|
5353
|
+
const paramsNode = method.childForFieldName("parameters");
|
|
5354
|
+
if (paramsNode) {
|
|
5355
|
+
collectParameterTypes(paramsNode, context.paramTypes);
|
|
5356
|
+
}
|
|
5357
|
+
}
|
|
5358
|
+
const constructors = getNodesFromCache(tree.rootNode, "constructor_declaration", cache);
|
|
5359
|
+
for (const ctor of constructors) {
|
|
5360
|
+
const paramsNode = ctor.childForFieldName("parameters");
|
|
5361
|
+
if (paramsNode) {
|
|
5362
|
+
collectParameterTypes(paramsNode, context.paramTypes);
|
|
5363
|
+
}
|
|
5342
5364
|
}
|
|
5343
5365
|
const fields = getNodesFromCache(tree.rootNode, "field_declaration", cache);
|
|
5344
5366
|
for (const field of fields) {
|
|
@@ -5371,14 +5393,112 @@ function buildResolutionContext(tree, cache) {
|
|
|
5371
5393
|
const imports = getNodesFromCache(tree.rootNode, "import_declaration", cache);
|
|
5372
5394
|
for (const imp of imports) {
|
|
5373
5395
|
const text = getNodeText(imp);
|
|
5374
|
-
const match = text.match(/import\s+(?:static\s+)?([a-zA-Z0-9_.]+)
|
|
5375
|
-
if (match)
|
|
5376
|
-
|
|
5377
|
-
|
|
5396
|
+
const match = text.match(/import\s+(?:static\s+)?([a-zA-Z0-9_.]+)(\.\*)?/);
|
|
5397
|
+
if (!match)
|
|
5398
|
+
continue;
|
|
5399
|
+
const fqn = match[1];
|
|
5400
|
+
const isWildcard = match[2] === ".*";
|
|
5401
|
+
if (isWildcard) {
|
|
5402
|
+
context.wildcardImports.push(fqn);
|
|
5403
|
+
} else {
|
|
5404
|
+
const parts2 = fqn.split(".");
|
|
5405
|
+
const simple = parts2[parts2.length - 1];
|
|
5406
|
+
context.imports.set(simple, fqn);
|
|
5378
5407
|
}
|
|
5379
5408
|
}
|
|
5380
5409
|
return context;
|
|
5381
5410
|
}
|
|
5411
|
+
function collectParameterTypes(paramsNode, out2) {
|
|
5412
|
+
for (let i2 = 0;i2 < paramsNode.childCount; i2++) {
|
|
5413
|
+
const child = paramsNode.child(i2);
|
|
5414
|
+
if (!child)
|
|
5415
|
+
continue;
|
|
5416
|
+
if (child.type !== "formal_parameter" && child.type !== "spread_parameter") {
|
|
5417
|
+
continue;
|
|
5418
|
+
}
|
|
5419
|
+
const typeNode = child.childForFieldName("type");
|
|
5420
|
+
const nameNode = child.childForFieldName("name");
|
|
5421
|
+
if (typeNode && nameNode) {
|
|
5422
|
+
out2.set(getNodeText(nameNode), getNodeText(typeNode));
|
|
5423
|
+
}
|
|
5424
|
+
}
|
|
5425
|
+
}
|
|
5426
|
+
function stripGenerics(type) {
|
|
5427
|
+
const ltIdx = type.indexOf("<");
|
|
5428
|
+
return ltIdx === -1 ? type : type.substring(0, ltIdx);
|
|
5429
|
+
}
|
|
5430
|
+
function resolveReceiverType(receiver, context) {
|
|
5431
|
+
if (!receiver)
|
|
5432
|
+
return { simpleName: null, fqn: null };
|
|
5433
|
+
if (receiver === "this") {
|
|
5434
|
+
return resolveFqn(context.className, context);
|
|
5435
|
+
}
|
|
5436
|
+
if (receiver.startsWith("this.")) {
|
|
5437
|
+
const fieldName = receiver.substring("this.".length);
|
|
5438
|
+
const fieldType = context.fieldTypes.get(fieldName);
|
|
5439
|
+
if (fieldType)
|
|
5440
|
+
return resolveFqn(stripGenerics(fieldType), context);
|
|
5441
|
+
return { simpleName: null, fqn: null };
|
|
5442
|
+
}
|
|
5443
|
+
if (receiver === "super")
|
|
5444
|
+
return { simpleName: null, fqn: null };
|
|
5445
|
+
const declaredType = context.localVarTypes.get(receiver) ?? context.paramTypes.get(receiver) ?? context.fieldTypes.get(receiver);
|
|
5446
|
+
if (declaredType) {
|
|
5447
|
+
return resolveFqn(stripGenerics(declaredType), context);
|
|
5448
|
+
}
|
|
5449
|
+
if (/^[A-Z]/.test(receiver)) {
|
|
5450
|
+
const simple = receiver.includes(".") ? receiver.substring(receiver.lastIndexOf(".") + 1) : receiver;
|
|
5451
|
+
if (/^[A-Z][A-Za-z0-9_]*$/.test(simple)) {
|
|
5452
|
+
return resolveFqn(simple, context);
|
|
5453
|
+
}
|
|
5454
|
+
}
|
|
5455
|
+
return { simpleName: null, fqn: null };
|
|
5456
|
+
}
|
|
5457
|
+
function resolveFqn(simpleName, context) {
|
|
5458
|
+
if (!simpleName)
|
|
5459
|
+
return { simpleName: null, fqn: null };
|
|
5460
|
+
const importedFqn = context.imports.get(simpleName);
|
|
5461
|
+
if (importedFqn) {
|
|
5462
|
+
return { simpleName, fqn: importedFqn };
|
|
5463
|
+
}
|
|
5464
|
+
if (context.className === simpleName && context.packageName) {
|
|
5465
|
+
return { simpleName, fqn: `${context.packageName}.${simpleName}` };
|
|
5466
|
+
}
|
|
5467
|
+
if (JAVA_LANG_TYPES.has(simpleName)) {
|
|
5468
|
+
return { simpleName, fqn: `java.lang.${simpleName}` };
|
|
5469
|
+
}
|
|
5470
|
+
return { simpleName, fqn: null };
|
|
5471
|
+
}
|
|
5472
|
+
var JAVA_LANG_TYPES = new Set([
|
|
5473
|
+
"String",
|
|
5474
|
+
"StringBuilder",
|
|
5475
|
+
"StringBuffer",
|
|
5476
|
+
"Object",
|
|
5477
|
+
"Class",
|
|
5478
|
+
"Integer",
|
|
5479
|
+
"Long",
|
|
5480
|
+
"Double",
|
|
5481
|
+
"Float",
|
|
5482
|
+
"Boolean",
|
|
5483
|
+
"Character",
|
|
5484
|
+
"Byte",
|
|
5485
|
+
"Short",
|
|
5486
|
+
"Number",
|
|
5487
|
+
"Math",
|
|
5488
|
+
"System",
|
|
5489
|
+
"Thread",
|
|
5490
|
+
"Runnable",
|
|
5491
|
+
"Throwable",
|
|
5492
|
+
"Exception",
|
|
5493
|
+
"RuntimeException",
|
|
5494
|
+
"Error",
|
|
5495
|
+
"Process",
|
|
5496
|
+
"ProcessBuilder",
|
|
5497
|
+
"Iterable",
|
|
5498
|
+
"Comparable",
|
|
5499
|
+
"CharSequence",
|
|
5500
|
+
"Enum"
|
|
5501
|
+
]);
|
|
5382
5502
|
function extractCallInfo(node, context) {
|
|
5383
5503
|
const nameNode = node.childForFieldName("name");
|
|
5384
5504
|
const methodName = nameNode ? getNodeText(nameNode) : "unknown";
|
|
@@ -5388,9 +5508,12 @@ function extractCallInfo(node, context) {
|
|
|
5388
5508
|
const args2 = argsNode ? extractArguments(argsNode) : [];
|
|
5389
5509
|
const enclosingMethod = findEnclosingMethod(node);
|
|
5390
5510
|
const { resolved, resolution } = resolveMethodCall(methodName, receiver, context);
|
|
5511
|
+
const { simpleName, fqn } = resolveReceiverType(receiver, context);
|
|
5391
5512
|
return {
|
|
5392
5513
|
method_name: methodName,
|
|
5393
5514
|
receiver,
|
|
5515
|
+
receiver_type: simpleName,
|
|
5516
|
+
receiver_type_fqn: fqn,
|
|
5394
5517
|
arguments: args2,
|
|
5395
5518
|
location: {
|
|
5396
5519
|
line: node.startPosition.row + 1,
|
|
@@ -5413,9 +5536,13 @@ function extractObjectCreation(node, context) {
|
|
|
5413
5536
|
status: "resolved",
|
|
5414
5537
|
target: `${typeName}.<init>`
|
|
5415
5538
|
};
|
|
5539
|
+
const simpleType = stripGenerics(typeName);
|
|
5540
|
+
const { simpleName, fqn } = resolveFqn(simpleType, context);
|
|
5416
5541
|
return {
|
|
5417
5542
|
method_name: typeName,
|
|
5418
5543
|
receiver: null,
|
|
5544
|
+
receiver_type: simpleName,
|
|
5545
|
+
receiver_type_fqn: fqn,
|
|
5419
5546
|
arguments: args2,
|
|
5420
5547
|
location: {
|
|
5421
5548
|
line: node.startPosition.row + 1,
|
|
@@ -10059,6 +10186,17 @@ var DEFAULT_SINKS = [
|
|
|
10059
10186
|
{ method: "queryForObject", type: "sql_injection", cwe: "CWE-89", severity: "high", arg_positions: [0] },
|
|
10060
10187
|
{ method: "queryForList", type: "sql_injection", cwe: "CWE-89", severity: "high", arg_positions: [0] },
|
|
10061
10188
|
{ method: "queryForLong", type: "sql_injection", cwe: "CWE-89", severity: "high", arg_positions: [0] },
|
|
10189
|
+
{ method: "insert", class: "*Mapper", type: "mybatis_mapper_call", cwe: "CWE-89", severity: "medium", arg_positions: [0], languages: ["java"] },
|
|
10190
|
+
{ method: "insertSelective", class: "*Mapper", type: "mybatis_mapper_call", cwe: "CWE-89", severity: "medium", arg_positions: [0], languages: ["java"] },
|
|
10191
|
+
{ method: "update", class: "*Mapper", type: "mybatis_mapper_call", cwe: "CWE-89", severity: "medium", arg_positions: [0], languages: ["java"] },
|
|
10192
|
+
{ method: "updateByPrimaryKey", class: "*Mapper", type: "mybatis_mapper_call", cwe: "CWE-89", severity: "medium", arg_positions: [0], languages: ["java"] },
|
|
10193
|
+
{ method: "updateByPrimaryKeySelective", class: "*Mapper", type: "mybatis_mapper_call", cwe: "CWE-89", severity: "medium", arg_positions: [0], languages: ["java"] },
|
|
10194
|
+
{ method: "delete", class: "*Mapper", type: "mybatis_mapper_call", cwe: "CWE-89", severity: "medium", arg_positions: [0], languages: ["java"] },
|
|
10195
|
+
{ method: "deleteByPrimaryKey", class: "*Mapper", type: "mybatis_mapper_call", cwe: "CWE-89", severity: "medium", arg_positions: [0], languages: ["java"] },
|
|
10196
|
+
{ method: "selectOne", class: "*Mapper", type: "mybatis_mapper_call", cwe: "CWE-89", severity: "medium", arg_positions: [0], languages: ["java"] },
|
|
10197
|
+
{ method: "selectList", class: "*Mapper", type: "mybatis_mapper_call", cwe: "CWE-89", severity: "medium", arg_positions: [0], languages: ["java"] },
|
|
10198
|
+
{ method: "selectByPrimaryKey", class: "*Mapper", type: "mybatis_mapper_call", cwe: "CWE-89", severity: "medium", arg_positions: [0], languages: ["java"] },
|
|
10199
|
+
{ method: "selectByExample", class: "*Mapper", type: "mybatis_mapper_call", cwe: "CWE-89", severity: "medium", arg_positions: [0], languages: ["java"] },
|
|
10062
10200
|
{ method: "exec", class: "Runtime", type: "command_injection", cwe: "CWE-78", severity: "critical", arg_positions: [0, 1] },
|
|
10063
10201
|
{ method: "start", class: "ProcessBuilder", type: "command_injection", cwe: "CWE-78", severity: "critical", arg_positions: [] },
|
|
10064
10202
|
{ method: "ProcessBuilder", class: "constructor", type: "command_injection", cwe: "CWE-78", severity: "critical", arg_positions: [0] },
|
|
@@ -11681,6 +11819,14 @@ function matchesAnnotation(annotations, targetAnnotation) {
|
|
|
11681
11819
|
return false;
|
|
11682
11820
|
}
|
|
11683
11821
|
function receiverMightBeClass(receiver, className) {
|
|
11822
|
+
if (className.startsWith("*") && className.length > 1) {
|
|
11823
|
+
const suffix = className.slice(1).toLowerCase();
|
|
11824
|
+
let simpleReceiver = receiver;
|
|
11825
|
+
if (simpleReceiver.includes(".") && !simpleReceiver.endsWith(")")) {
|
|
11826
|
+
simpleReceiver = simpleReceiver.substring(simpleReceiver.lastIndexOf(".") + 1);
|
|
11827
|
+
}
|
|
11828
|
+
return simpleReceiver.toLowerCase().endsWith(suffix);
|
|
11829
|
+
}
|
|
11684
11830
|
if (receiver === className) {
|
|
11685
11831
|
return true;
|
|
11686
11832
|
}
|
|
@@ -18725,7 +18871,7 @@ class CrossFileResolver {
|
|
|
18725
18871
|
fieldName = exprMatch[2];
|
|
18726
18872
|
}
|
|
18727
18873
|
}
|
|
18728
|
-
const
|
|
18874
|
+
const resolveReceiverType2 = (rcv) => {
|
|
18729
18875
|
const param = method.parameters.find((p) => p.name === rcv);
|
|
18730
18876
|
if (param?.type)
|
|
18731
18877
|
return param.type;
|
|
@@ -18736,13 +18882,13 @@ class CrossFileResolver {
|
|
|
18736
18882
|
};
|
|
18737
18883
|
let receiverType = null;
|
|
18738
18884
|
if (receiver && fieldName) {
|
|
18739
|
-
receiverType =
|
|
18885
|
+
receiverType = resolveReceiverType2(receiver);
|
|
18740
18886
|
}
|
|
18741
18887
|
if (!receiverType) {
|
|
18742
18888
|
for (const rcvUse of usesAtLine) {
|
|
18743
18889
|
if (!rcvUse.variable || rcvUse.variable === def.variable)
|
|
18744
18890
|
continue;
|
|
18745
|
-
const rt =
|
|
18891
|
+
const rt = resolveReceiverType2(rcvUse.variable);
|
|
18746
18892
|
if (!rt)
|
|
18747
18893
|
continue;
|
|
18748
18894
|
const fieldUse = usesAtLine.find((u) => u !== rcvUse && !!u.variable && u.variable !== def.variable && u.variable !== rcvUse.variable && this.typeHasField(rt, u.variable));
|
|
@@ -21046,7 +21192,8 @@ var KNOWN_SINK_TYPES = new Set([
|
|
|
21046
21192
|
"log_injection",
|
|
21047
21193
|
"xxe",
|
|
21048
21194
|
"deserialization",
|
|
21049
|
-
"code_injection"
|
|
21195
|
+
"code_injection",
|
|
21196
|
+
"mybatis_mapper_call"
|
|
21050
21197
|
]);
|
|
21051
21198
|
function checkSanitized(_fromLine, toLine, sinkType, sanitizersByLine) {
|
|
21052
21199
|
const sanitizersAtTarget = sanitizersByLine.get(toLine);
|
|
@@ -27632,7 +27779,7 @@ var colors = {
|
|
|
27632
27779
|
};
|
|
27633
27780
|
|
|
27634
27781
|
// src/version.ts
|
|
27635
|
-
var version = "3.
|
|
27782
|
+
var version = "3.43.0";
|
|
27636
27783
|
|
|
27637
27784
|
// src/formatters.ts
|
|
27638
27785
|
var SINK_SEVERITY = {
|
|
@@ -27654,7 +27801,8 @@ var SINK_SEVERITY = {
|
|
|
27654
27801
|
weak_crypto: "low",
|
|
27655
27802
|
insecure_cookie: "low",
|
|
27656
27803
|
trust_boundary: "medium",
|
|
27657
|
-
external_taint_escape: "medium"
|
|
27804
|
+
external_taint_escape: "medium",
|
|
27805
|
+
mybatis_mapper_call: "medium"
|
|
27658
27806
|
};
|
|
27659
27807
|
var SINK_CWE = {
|
|
27660
27808
|
sql_injection: "CWE-89",
|
|
@@ -27675,7 +27823,8 @@ var SINK_CWE = {
|
|
|
27675
27823
|
weak_crypto: "CWE-327",
|
|
27676
27824
|
insecure_cookie: "CWE-614",
|
|
27677
27825
|
trust_boundary: "CWE-501",
|
|
27678
|
-
external_taint_escape: "CWE-20"
|
|
27826
|
+
external_taint_escape: "CWE-20",
|
|
27827
|
+
mybatis_mapper_call: "CWE-89"
|
|
27679
27828
|
};
|
|
27680
27829
|
var VULNERABILITY_HELP = {
|
|
27681
27830
|
sql_injection: {
|
|
@@ -27754,6 +27903,10 @@ var VULNERABILITY_HELP = {
|
|
|
27754
27903
|
description: "External input reaches a sensitive sink without proper validation",
|
|
27755
27904
|
fix: "Validate, sanitize, or escape external input before use in sensitive operations"
|
|
27756
27905
|
},
|
|
27906
|
+
mybatis_mapper_call: {
|
|
27907
|
+
description: "Tainted argument passed to a MyBatis mapper interface method — actual SQL lives in the mapper XML/annotation binding, so exploitability depends on whether ${...} string interpolation is used",
|
|
27908
|
+
fix: "Audit the mapper XML/annotations: use #{...} parameter binding (PreparedStatement-backed) for any user-controlled value. Reject from SQL-injection reports unless ${...} interpolation is confirmed"
|
|
27909
|
+
},
|
|
27757
27910
|
"dead-code": {
|
|
27758
27911
|
description: "Unreachable code block has no execution path from any entry point",
|
|
27759
27912
|
fix: "Remove the unreachable block or fix the control flow that precedes it"
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "cognium-dev",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.43.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.43.0"
|
|
69
69
|
},
|
|
70
70
|
"devDependencies": {
|
|
71
71
|
"@types/node": "^25.5.0",
|