cognium-dev 3.148.0 → 3.150.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 +284 -6
  2. package/package.json +2 -2
package/dist/cli.js CHANGED
@@ -30343,7 +30343,7 @@ class SinkSemanticsPass {
30343
30343
  return { droppedCount: 0, registrySize: 0 };
30344
30344
  }
30345
30345
  const registry = buildRegistry(entries);
30346
- const sinks = graph.ir.taint.sinks;
30346
+ const sinks = ctx.hasResult("sink-filter") ? ctx.getResult("sink-filter").sinks : graph.ir.taint.sinks;
30347
30347
  let droppedCount = 0;
30348
30348
  const kept = sinks.filter((sink) => {
30349
30349
  if (!sink.class || !sink.method)
@@ -30366,6 +30366,194 @@ class SinkSemanticsPass {
30366
30366
  }
30367
30367
  }
30368
30368
 
30369
+ // ../circle-ir/dist/analysis/passes/cli-main-reflection-suppress-pass.js
30370
+ var REFLECTION_SINK_METHODS = new Set([
30371
+ "forName",
30372
+ "newInstance",
30373
+ "invoke",
30374
+ "getMethod",
30375
+ "getDeclaredMethod",
30376
+ "getConstructor",
30377
+ "getDeclaredConstructor",
30378
+ "loadClass",
30379
+ "defineClass"
30380
+ ]);
30381
+ var TIER_1_CLASS_ANNOTATIONS = new Set([
30382
+ "RestController",
30383
+ "Controller",
30384
+ "Service",
30385
+ "Repository",
30386
+ "Component",
30387
+ "Path",
30388
+ "WebServlet",
30389
+ "ServerEndpoint",
30390
+ "FeignClient"
30391
+ ]);
30392
+ var TIER_1_METHOD_ANNOTATIONS = new Set([
30393
+ "RequestMapping",
30394
+ "GetMapping",
30395
+ "PostMapping",
30396
+ "PutMapping",
30397
+ "DeleteMapping",
30398
+ "PatchMapping",
30399
+ "MessageMapping",
30400
+ "SubscribeMapping",
30401
+ "KafkaListener",
30402
+ "KafkaHandler",
30403
+ "RabbitListener",
30404
+ "RabbitHandler",
30405
+ "JmsListener",
30406
+ "StreamListener",
30407
+ "SqsListener",
30408
+ "SqsHandler",
30409
+ "EventListener",
30410
+ "Scheduled",
30411
+ "Path",
30412
+ "GET",
30413
+ "POST",
30414
+ "PUT",
30415
+ "DELETE",
30416
+ "PATCH",
30417
+ "HEAD",
30418
+ "OPTIONS",
30419
+ "DataBoundConstructor",
30420
+ "DataBoundSetter"
30421
+ ]);
30422
+ var TIER_1_SUPERTYPES = new Set([
30423
+ "HttpServlet",
30424
+ "GenericServlet",
30425
+ "Filter",
30426
+ "HandlerInterceptor",
30427
+ "AsyncHandlerInterceptor",
30428
+ "CommandLineRunner",
30429
+ "ApplicationRunner",
30430
+ "SimpleChannelInboundHandler",
30431
+ "ChannelInboundHandler",
30432
+ "ChannelInboundHandlerAdapter",
30433
+ "ChannelDuplexHandler",
30434
+ "NettyRequestProcessor",
30435
+ "Converter",
30436
+ "SingleValueConverter",
30437
+ "ConverterMatcher",
30438
+ "AbstractReflectionConverter",
30439
+ "AbstractSingleValueConverter",
30440
+ "AbstractCollectionConverter"
30441
+ ]);
30442
+ function normalizeAnnotation(raw) {
30443
+ let s = raw.trim();
30444
+ if (s.startsWith("@"))
30445
+ s = s.slice(1);
30446
+ const parenIdx = s.indexOf("(");
30447
+ if (parenIdx >= 0)
30448
+ s = s.slice(0, parenIdx);
30449
+ const genericIdx = s.indexOf("<");
30450
+ if (genericIdx >= 0)
30451
+ s = s.slice(0, genericIdx);
30452
+ const dotIdx = s.lastIndexOf(".");
30453
+ if (dotIdx >= 0)
30454
+ s = s.slice(dotIdx + 1);
30455
+ return s.trim();
30456
+ }
30457
+ function normalizeSupertype(raw) {
30458
+ let s = raw.trim();
30459
+ const genericIdx = s.indexOf("<");
30460
+ if (genericIdx >= 0)
30461
+ s = s.slice(0, genericIdx);
30462
+ const dotIdx = s.lastIndexOf(".");
30463
+ if (dotIdx >= 0)
30464
+ s = s.slice(dotIdx + 1);
30465
+ return s.trim();
30466
+ }
30467
+ function isMainMethod(name2, paramTypes) {
30468
+ if (name2 !== "main")
30469
+ return false;
30470
+ if (paramTypes.length !== 1)
30471
+ return false;
30472
+ const t = paramTypes[0];
30473
+ if (!t)
30474
+ return false;
30475
+ const bare = t.replace(/\s+/g, "");
30476
+ return bare === "String[]" || bare === "java.lang.String[]";
30477
+ }
30478
+
30479
+ class CliMainReflectionSuppressPass {
30480
+ name = "cli-main-reflection-suppress";
30481
+ category = "security";
30482
+ run(ctx) {
30483
+ const { graph, language } = ctx;
30484
+ if (language !== "java") {
30485
+ return { cliMainSignal: false, droppedCount: 0 };
30486
+ }
30487
+ const types = graph.ir.types;
30488
+ if (!types || types.length === 0) {
30489
+ return { cliMainSignal: false, droppedCount: 0 };
30490
+ }
30491
+ let hasMain = false;
30492
+ let hasFrameworkSignal = false;
30493
+ for (const type of types) {
30494
+ for (const ann of type.annotations) {
30495
+ if (TIER_1_CLASS_ANNOTATIONS.has(normalizeAnnotation(ann))) {
30496
+ hasFrameworkSignal = true;
30497
+ break;
30498
+ }
30499
+ }
30500
+ if (hasFrameworkSignal)
30501
+ break;
30502
+ if (type.extends && TIER_1_SUPERTYPES.has(normalizeSupertype(type.extends))) {
30503
+ hasFrameworkSignal = true;
30504
+ break;
30505
+ }
30506
+ for (const impl of type.implements) {
30507
+ if (TIER_1_SUPERTYPES.has(normalizeSupertype(impl))) {
30508
+ hasFrameworkSignal = true;
30509
+ break;
30510
+ }
30511
+ }
30512
+ if (hasFrameworkSignal)
30513
+ break;
30514
+ for (const method of type.methods) {
30515
+ for (const ann of method.annotations) {
30516
+ if (TIER_1_METHOD_ANNOTATIONS.has(normalizeAnnotation(ann))) {
30517
+ hasFrameworkSignal = true;
30518
+ break;
30519
+ }
30520
+ }
30521
+ if (hasFrameworkSignal)
30522
+ break;
30523
+ if (!hasMain) {
30524
+ const paramTypes = method.parameters.map((p) => p.type);
30525
+ if (isMainMethod(method.name, paramTypes)) {
30526
+ hasMain = true;
30527
+ }
30528
+ }
30529
+ }
30530
+ if (hasFrameworkSignal)
30531
+ break;
30532
+ }
30533
+ const cliMainSignal = hasMain && !hasFrameworkSignal;
30534
+ if (!cliMainSignal) {
30535
+ return { cliMainSignal: false, droppedCount: 0 };
30536
+ }
30537
+ const sinks = ctx.hasResult("sink-filter") ? ctx.getResult("sink-filter").sinks : graph.ir.taint.sinks;
30538
+ let droppedCount = 0;
30539
+ const kept = sinks.filter((sink) => {
30540
+ if (sink.type !== "code_injection")
30541
+ return true;
30542
+ if (!sink.method)
30543
+ return true;
30544
+ if (!REFLECTION_SINK_METHODS.has(sink.method))
30545
+ return true;
30546
+ droppedCount++;
30547
+ return false;
30548
+ });
30549
+ if (droppedCount > 0) {
30550
+ sinks.length = 0;
30551
+ sinks.push(...kept);
30552
+ }
30553
+ return { cliMainSignal: true, droppedCount };
30554
+ }
30555
+ }
30556
+
30369
30557
  // ../circle-ir/dist/analysis/passes/taint-propagation-pass.js
30370
30558
  class TaintPropagationPass {
30371
30559
  name = "taint-propagation";
@@ -31733,7 +31921,7 @@ function findTaintBridges2(result) {
31733
31921
  }
31734
31922
 
31735
31923
  // ../circle-ir/dist/analysis/entry-point-detection.js
31736
- var TIER_1_METHOD_ANNOTATIONS = new Set([
31924
+ var TIER_1_METHOD_ANNOTATIONS2 = new Set([
31737
31925
  "RequestMapping",
31738
31926
  "GetMapping",
31739
31927
  "PostMapping",
@@ -31763,7 +31951,7 @@ var TIER_1_METHOD_ANNOTATIONS = new Set([
31763
31951
  "DataBoundConstructor",
31764
31952
  "DataBoundSetter"
31765
31953
  ]);
31766
- var TIER_1_CLASS_ANNOTATIONS = new Set([
31954
+ var TIER_1_CLASS_ANNOTATIONS2 = new Set([
31767
31955
  "RestController",
31768
31956
  "Controller",
31769
31957
  "Service",
@@ -31913,10 +32101,10 @@ function classifyEntryPointTier(method, enclosingType, ctx) {
31913
32101
  if (classShapeIsLibraryFacade(enclosingType)) {
31914
32102
  return "TIER_3_LIBRARY_API";
31915
32103
  }
31916
- if (annotationsInclude(method.annotations, TIER_1_METHOD_ANNOTATIONS)) {
32104
+ if (annotationsInclude(method.annotations, TIER_1_METHOD_ANNOTATIONS2)) {
31917
32105
  return "TIER_1_ENTRY_POINT";
31918
32106
  }
31919
- if (enclosingType && annotationsInclude(enclosingType.annotations, TIER_1_CLASS_ANNOTATIONS)) {
32107
+ if (enclosingType && annotationsInclude(enclosingType.annotations, TIER_1_CLASS_ANNOTATIONS2)) {
31920
32108
  return "TIER_1_ENTRY_POINT";
31921
32109
  }
31922
32110
  if (methodIsSupertypeLifecycleEntryPoint(method, enclosingType)) {
@@ -32899,6 +33087,37 @@ var CLOSE_METHODS = new Set([
32899
33087
  "shutdownNow",
32900
33088
  "terminate"
32901
33089
  ]);
33090
+ var WRAPPER_CTORS = new Set([
33091
+ "BufferedInputStream",
33092
+ "BufferedOutputStream",
33093
+ "BufferedReader",
33094
+ "BufferedWriter",
33095
+ "InputStreamReader",
33096
+ "OutputStreamWriter",
33097
+ "DataInputStream",
33098
+ "DataOutputStream",
33099
+ "PrintStream",
33100
+ "PrintWriter",
33101
+ "LineNumberReader",
33102
+ "PushbackInputStream",
33103
+ "PushbackReader",
33104
+ "SequenceInputStream",
33105
+ "GZIPInputStream",
33106
+ "GZIPOutputStream",
33107
+ "ZipInputStream",
33108
+ "ZipOutputStream",
33109
+ "InflaterInputStream",
33110
+ "DeflaterOutputStream",
33111
+ "CheckedInputStream",
33112
+ "CheckedOutputStream"
33113
+ ]);
33114
+ var WORKER_METHODS = new Set([
33115
+ "run",
33116
+ "call",
33117
+ "accept",
33118
+ "get",
33119
+ "apply"
33120
+ ]);
32902
33121
 
32903
33122
  class ResourceLeakPass {
32904
33123
  name = "resource-leak";
@@ -32935,6 +33154,12 @@ class ResourceLeakPass {
32935
33154
  if (this.isFactoryMethod(methodInfo.method)) {
32936
33155
  continue;
32937
33156
  }
33157
+ if (this.isWrappedByCloseableCtor(graph.ir.calls, resourceVar, openLine, methodEnd)) {
33158
+ continue;
33159
+ }
33160
+ if (this.isClosedInNestedWorker(graph, codeLines, resourceVar, methodInfo, openLine, methodEnd)) {
33161
+ continue;
33162
+ }
32938
33163
  const closeCall = graph.ir.calls.find((c) => CLOSE_METHODS.has(c.method_name) && c.receiver === resourceVar && c.location.line > openLine && c.location.line <= methodEnd);
32939
33164
  const snippet = (codeLines[openLine - 1] ?? "").trim();
32940
33165
  if (!closeCall) {
@@ -33031,6 +33256,57 @@ class ResourceLeakPass {
33031
33256
  return false;
33032
33257
  return FACTORY_METHOD_NAME_RE.test(method.name);
33033
33258
  }
33259
+ isWrappedByCloseableCtor(calls, variable, fromLine, toLine) {
33260
+ for (const call of calls) {
33261
+ if (!WRAPPER_CTORS.has(call.method_name))
33262
+ continue;
33263
+ if (call.location.line < fromLine || call.location.line > toLine)
33264
+ continue;
33265
+ for (const arg of call.arguments) {
33266
+ if (arg.variable === variable)
33267
+ return true;
33268
+ }
33269
+ }
33270
+ return false;
33271
+ }
33272
+ isClosedInNestedWorker(graph, lines, variable, methodInfo, fromLine, toLine) {
33273
+ const fieldNames = new Set(methodInfo.type.fields.map((f) => f.name));
33274
+ let candidateField = null;
33275
+ if (fieldNames.has(variable)) {
33276
+ candidateField = variable;
33277
+ } else {
33278
+ const thisAssignRe = new RegExp(`(?:\\bthis\\s*\\.\\s*)?(\\w+)\\s*=\\s*${escapeRegex2(variable)}\\b`);
33279
+ for (let l = fromLine;l <= toLine && l <= lines.length; l++) {
33280
+ const m = thisAssignRe.exec(lines[l - 1] ?? "");
33281
+ if (m && fieldNames.has(m[1])) {
33282
+ candidateField = m[1];
33283
+ break;
33284
+ }
33285
+ }
33286
+ }
33287
+ if (!candidateField)
33288
+ return false;
33289
+ for (const call of graph.ir.calls) {
33290
+ if (call.receiver !== candidateField)
33291
+ continue;
33292
+ if (!CLOSE_METHODS.has(call.method_name))
33293
+ continue;
33294
+ const closeLine = call.location.line;
33295
+ if (closeLine < fromLine || closeLine > toLine)
33296
+ continue;
33297
+ const enclosing = graph.methodAtLine(closeLine);
33298
+ if (!enclosing)
33299
+ continue;
33300
+ if (enclosing.method === methodInfo.method)
33301
+ continue;
33302
+ if (!WORKER_METHODS.has(enclosing.method.name))
33303
+ continue;
33304
+ if (enclosing.method.start_line > fromLine && enclosing.method.start_line <= toLine) {
33305
+ return true;
33306
+ }
33307
+ }
33308
+ return false;
33309
+ }
33034
33310
  }
33035
33311
 
33036
33312
  // ../circle-ir/dist/graph/scope-graph.js
@@ -41355,6 +41631,8 @@ async function analyze(code, filePath, language, options = {}) {
41355
41631
  pipeline.add(new SinkFilterPass);
41356
41632
  if (!disabledPasses.has("sink-semantics"))
41357
41633
  pipeline.add(new SinkSemanticsPass);
41634
+ if (!disabledPasses.has("cli-main-reflection-suppress"))
41635
+ pipeline.add(new CliMainReflectionSuppressPass);
41358
41636
  pipeline.add(new TaintPropagationPass);
41359
41637
  pipeline.add(new InterproceduralPass({
41360
41638
  enableEntryPointGate: options.enableEntryPointGate ?? true
@@ -42282,7 +42560,7 @@ var colors = {
42282
42560
  };
42283
42561
 
42284
42562
  // src/version.ts
42285
- var version = "3.148.0";
42563
+ var version = "3.150.0";
42286
42564
 
42287
42565
  // src/formatters.ts
42288
42566
  var LIBRARY_API_SURFACE_TAG2 = "library-api-surface:caller-responsibility";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "cognium-dev",
3
- "version": "3.148.0",
3
+ "version": "3.150.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",
@@ -66,7 +66,7 @@
66
66
  },
67
67
  "dependencies": {
68
68
  "@cognium/project-profile-detect": "^1.1.0",
69
- "circle-ir": "^3.148.0"
69
+ "circle-ir": "^3.150.0"
70
70
  },
71
71
  "devDependencies": {
72
72
  "@types/node": "^25.5.0",