cognium-dev 3.147.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 +304 -6
  2. package/package.json +2 -2
package/dist/cli.js CHANGED
@@ -26043,6 +26043,18 @@ function findJavaResponseWriterXssFindings(code, file) {
26043
26043
  if (respNames.size === 0)
26044
26044
  return out2;
26045
26045
  const safeWrapRe = /\b(?:Encode\.(?:forHtml|forHtmlAttribute|forHtmlContent|forJavaScript)|StringEscapeUtils\.escape(?:Html3|Html4|EcmaScript|Xml)|HtmlUtils\.htmlEscape(?:Decimal|Hex)?|Escaper\.escapeHtml|HtmlEscapers\.(?:escapeHtml|htmlEscaper)|Encoder\.encodeForHTML(?:Attribute)?|Jsoup\.clean)\s*\(/;
26046
+ const sanitizedVars = new Set;
26047
+ const sanAssignRe = /(?:^|[\s;{},(])(?:final\s+)?(?:[A-Za-z_][\w.<>\[\]]*\s+)?([A-Za-z_]\w*)\s*=\s*[^=]/;
26048
+ for (const line of lines) {
26049
+ const t = line.trim();
26050
+ if (!t || t.startsWith("//") || t.startsWith("*"))
26051
+ continue;
26052
+ if (!safeWrapRe.test(t))
26053
+ continue;
26054
+ const am = t.match(sanAssignRe);
26055
+ if (am)
26056
+ sanitizedVars.add(am[1]);
26057
+ }
26046
26058
  for (let i2 = 0;i2 < lines.length; i2++) {
26047
26059
  const raw = lines[i2];
26048
26060
  const trimmed = raw.trim();
@@ -26063,6 +26075,14 @@ function findJavaResponseWriterXssFindings(code, file) {
26063
26075
  continue;
26064
26076
  if (!/[A-Za-z_]\w*/.test(argsTrim))
26065
26077
  continue;
26078
+ if (sanitizedVars.size > 0) {
26079
+ const stripped = argsTrim.replace(/"(?:\\.|[^"\\])*"/g, "");
26080
+ const idents = stripped.match(/\b[A-Za-z_]\w*\b/g) ?? [];
26081
+ const nonKeyword = idents.filter((x) => x !== "null" && x !== "true" && x !== "false");
26082
+ if (nonKeyword.length > 0 && nonKeyword.every((x) => sanitizedVars.has(x))) {
26083
+ continue;
26084
+ }
26085
+ }
26066
26086
  out2.push({
26067
26087
  id: `xss-${file}-${i2 + 1}`,
26068
26088
  pass: "language-sources",
@@ -30323,7 +30343,7 @@ class SinkSemanticsPass {
30323
30343
  return { droppedCount: 0, registrySize: 0 };
30324
30344
  }
30325
30345
  const registry = buildRegistry(entries);
30326
- const sinks = graph.ir.taint.sinks;
30346
+ const sinks = ctx.hasResult("sink-filter") ? ctx.getResult("sink-filter").sinks : graph.ir.taint.sinks;
30327
30347
  let droppedCount = 0;
30328
30348
  const kept = sinks.filter((sink) => {
30329
30349
  if (!sink.class || !sink.method)
@@ -30346,6 +30366,194 @@ class SinkSemanticsPass {
30346
30366
  }
30347
30367
  }
30348
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
+
30349
30557
  // ../circle-ir/dist/analysis/passes/taint-propagation-pass.js
30350
30558
  class TaintPropagationPass {
30351
30559
  name = "taint-propagation";
@@ -31713,7 +31921,7 @@ function findTaintBridges2(result) {
31713
31921
  }
31714
31922
 
31715
31923
  // ../circle-ir/dist/analysis/entry-point-detection.js
31716
- var TIER_1_METHOD_ANNOTATIONS = new Set([
31924
+ var TIER_1_METHOD_ANNOTATIONS2 = new Set([
31717
31925
  "RequestMapping",
31718
31926
  "GetMapping",
31719
31927
  "PostMapping",
@@ -31743,7 +31951,7 @@ var TIER_1_METHOD_ANNOTATIONS = new Set([
31743
31951
  "DataBoundConstructor",
31744
31952
  "DataBoundSetter"
31745
31953
  ]);
31746
- var TIER_1_CLASS_ANNOTATIONS = new Set([
31954
+ var TIER_1_CLASS_ANNOTATIONS2 = new Set([
31747
31955
  "RestController",
31748
31956
  "Controller",
31749
31957
  "Service",
@@ -31893,10 +32101,10 @@ function classifyEntryPointTier(method, enclosingType, ctx) {
31893
32101
  if (classShapeIsLibraryFacade(enclosingType)) {
31894
32102
  return "TIER_3_LIBRARY_API";
31895
32103
  }
31896
- if (annotationsInclude(method.annotations, TIER_1_METHOD_ANNOTATIONS)) {
32104
+ if (annotationsInclude(method.annotations, TIER_1_METHOD_ANNOTATIONS2)) {
31897
32105
  return "TIER_1_ENTRY_POINT";
31898
32106
  }
31899
- if (enclosingType && annotationsInclude(enclosingType.annotations, TIER_1_CLASS_ANNOTATIONS)) {
32107
+ if (enclosingType && annotationsInclude(enclosingType.annotations, TIER_1_CLASS_ANNOTATIONS2)) {
31900
32108
  return "TIER_1_ENTRY_POINT";
31901
32109
  }
31902
32110
  if (methodIsSupertypeLifecycleEntryPoint(method, enclosingType)) {
@@ -32879,6 +33087,37 @@ var CLOSE_METHODS = new Set([
32879
33087
  "shutdownNow",
32880
33088
  "terminate"
32881
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
+ ]);
32882
33121
 
32883
33122
  class ResourceLeakPass {
32884
33123
  name = "resource-leak";
@@ -32915,6 +33154,12 @@ class ResourceLeakPass {
32915
33154
  if (this.isFactoryMethod(methodInfo.method)) {
32916
33155
  continue;
32917
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
+ }
32918
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);
32919
33164
  const snippet = (codeLines[openLine - 1] ?? "").trim();
32920
33165
  if (!closeCall) {
@@ -33011,6 +33256,57 @@ class ResourceLeakPass {
33011
33256
  return false;
33012
33257
  return FACTORY_METHOD_NAME_RE.test(method.name);
33013
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
+ }
33014
33310
  }
33015
33311
 
33016
33312
  // ../circle-ir/dist/graph/scope-graph.js
@@ -41335,6 +41631,8 @@ async function analyze(code, filePath, language, options = {}) {
41335
41631
  pipeline.add(new SinkFilterPass);
41336
41632
  if (!disabledPasses.has("sink-semantics"))
41337
41633
  pipeline.add(new SinkSemanticsPass);
41634
+ if (!disabledPasses.has("cli-main-reflection-suppress"))
41635
+ pipeline.add(new CliMainReflectionSuppressPass);
41338
41636
  pipeline.add(new TaintPropagationPass);
41339
41637
  pipeline.add(new InterproceduralPass({
41340
41638
  enableEntryPointGate: options.enableEntryPointGate ?? true
@@ -42262,7 +42560,7 @@ var colors = {
42262
42560
  };
42263
42561
 
42264
42562
  // src/version.ts
42265
- var version = "3.147.0";
42563
+ var version = "3.150.0";
42266
42564
 
42267
42565
  // src/formatters.ts
42268
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.147.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.147.0"
69
+ "circle-ir": "^3.150.0"
70
70
  },
71
71
  "devDependencies": {
72
72
  "@types/node": "^25.5.0",