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.
Files changed (2) hide show
  1. package/dist/cli.js +165 -12
  2. 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
- imports: new Set
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
- const parts2 = match[1].split(".");
5377
- context.imports.add(parts2[parts2.length - 1]);
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 resolveReceiverType = (rcv) => {
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 = resolveReceiverType(receiver);
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 = resolveReceiverType(rcvUse.variable);
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.41.0";
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.41.0",
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.41.0"
68
+ "circle-ir": "^3.43.0"
69
69
  },
70
70
  "devDependencies": {
71
71
  "@types/node": "^25.5.0",