cognium-dev 3.175.0 → 3.176.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 +128 -6
  2. package/package.json +2 -2
package/dist/cli.js CHANGED
@@ -11948,6 +11948,84 @@ var DEFAULT_HEADER_RULES = [
11948
11948
  note: "Fires when the value is not a string literal (likely reflected from request)"
11949
11949
  }
11950
11950
  ];
11951
+ // ../circle-ir/dist/analysis/arg-type-resolver.js
11952
+ function findEnclosingType(callInMethod, types) {
11953
+ if (!callInMethod)
11954
+ return null;
11955
+ for (const t of types) {
11956
+ for (const m of t.methods) {
11957
+ if (m.name === callInMethod)
11958
+ return t;
11959
+ }
11960
+ }
11961
+ return null;
11962
+ }
11963
+ function resolveIdentifierType(name2, callInMethod, types) {
11964
+ if (!name2)
11965
+ return null;
11966
+ const enclosing = findEnclosingType(callInMethod, types);
11967
+ if (enclosing) {
11968
+ for (const m of enclosing.methods) {
11969
+ if (m.name !== callInMethod)
11970
+ continue;
11971
+ for (const p of m.parameters) {
11972
+ if (p.name === name2 && p.type)
11973
+ return p.type;
11974
+ }
11975
+ }
11976
+ for (const f of enclosing.fields) {
11977
+ if (f.name === name2 && f.type)
11978
+ return f.type;
11979
+ }
11980
+ return null;
11981
+ }
11982
+ for (const t of types) {
11983
+ for (const m of t.methods) {
11984
+ for (const p of m.parameters) {
11985
+ if (p.name === name2 && p.type)
11986
+ return p.type;
11987
+ }
11988
+ }
11989
+ for (const f of t.fields) {
11990
+ if (f.name === name2 && f.type)
11991
+ return f.type;
11992
+ }
11993
+ }
11994
+ return null;
11995
+ }
11996
+ function resolveCallReturnType(calleeName, callInMethod, types) {
11997
+ if (!calleeName)
11998
+ return null;
11999
+ const enclosing = findEnclosingType(callInMethod, types);
12000
+ if (enclosing) {
12001
+ for (const m of enclosing.methods) {
12002
+ if (m.name === calleeName && m.return_type)
12003
+ return m.return_type;
12004
+ }
12005
+ }
12006
+ for (const t of types) {
12007
+ for (const m of t.methods) {
12008
+ if (m.name === calleeName && m.return_type)
12009
+ return m.return_type;
12010
+ }
12011
+ }
12012
+ return null;
12013
+ }
12014
+ function isBoundedClassType(rawType) {
12015
+ if (!rawType)
12016
+ return false;
12017
+ const stripped = rawType.trim().replace(/^(?:java\.lang\.)+/, "");
12018
+ return /^Class(?:\s*<[\s\S]*>)?$/.test(stripped);
12019
+ }
12020
+ function isArgvContainerType(rawType) {
12021
+ if (!rawType)
12022
+ return false;
12023
+ const s = rawType.trim().replace(/^(?:java\.util\.|java\.lang\.)+/, "");
12024
+ if (/^String\s*(?:\[\s*\]|\.\.\.)$/.test(s))
12025
+ return true;
12026
+ return /^(?:List|ArrayList|LinkedList|Collection|Iterable|Deque|Queue)\s*<\s*(?:String|CharSequence|java\.lang\.String)\b/.test(s);
12027
+ }
12028
+
11951
12029
  // ../circle-ir/dist/analysis/taint-matcher.js
11952
12030
  var PYTHON_TAINTED_PATTERNS = [
11953
12031
  { pattern: /\brequest\.args\b/, sourceType: "http_param" },
@@ -11971,7 +12049,7 @@ function analyzeTaint(calls, types, config = getDefaultConfig(), typeHierarchy,
11971
12049
  const sources = findSources(calls, types, config.sources, sourceLines, language);
11972
12050
  let sinkPatterns = expandPromisifyAliases(config.sinks, sourceLines, language);
11973
12051
  sinkPatterns = expandIndirectEvalAliases(sinkPatterns, sourceLines, language);
11974
- const sinks = findSinks(calls, sinkPatterns, typeHierarchy, language, sourceLines);
12052
+ const sinks = findSinks(calls, sinkPatterns, typeHierarchy, language, sourceLines, types);
11975
12053
  const sanitizers = findSanitizers(calls, types, config.sanitizers, sourceLines);
11976
12054
  return { sources, sinks, sanitizers };
11977
12055
  }
@@ -12587,7 +12665,7 @@ function isSafeRustCommandCall(call, pattern, language) {
12587
12665
  return false;
12588
12666
  }
12589
12667
  var CLASS_LITERAL_RE = /^(?:[A-Za-z_][\w]*\.)*[A-Z][\w]*(?:\[\])*\.class$/;
12590
- function argIsClassLiteral(call, position) {
12668
+ function argIsClassLiteral(call, position, types) {
12591
12669
  const arg = call.arguments.find((a) => a.position === position);
12592
12670
  if (!arg)
12593
12671
  return false;
@@ -12596,7 +12674,28 @@ function argIsClassLiteral(call, position) {
12596
12674
  return false;
12597
12675
  if (CLASS_LITERAL_RE.test(expr))
12598
12676
  return true;
12599
- return TYPE_TOKEN_RE.test(expr);
12677
+ if (TYPE_TOKEN_RE.test(expr))
12678
+ return true;
12679
+ if (!types || types.length === 0)
12680
+ return false;
12681
+ const argExpr = (arg.expression ?? "").trim();
12682
+ const varName = arg.variable ?? "";
12683
+ const isBareIdentifier = varName !== "" && argExpr === varName && !/[(.]/.test(argExpr);
12684
+ if (isBareIdentifier) {
12685
+ const t = resolveIdentifierType(varName, call.in_method, types);
12686
+ if (isBoundedClassType(t))
12687
+ return true;
12688
+ }
12689
+ const isCallExpr = varName !== "" && new RegExp(`^${escapeRe(varName)}\\s*\\(`).test(argExpr);
12690
+ if (isCallExpr) {
12691
+ const t = resolveCallReturnType(varName, call.in_method, types);
12692
+ if (isBoundedClassType(t))
12693
+ return true;
12694
+ }
12695
+ return false;
12696
+ }
12697
+ function escapeRe(s) {
12698
+ return s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
12600
12699
  }
12601
12700
  var TYPE_TOKEN_RE = /^new\s+(?:TypeReference|TypeToken)\s*<[\s\S]*>\s*\(\s*\)\s*\{\s*\}$/;
12602
12701
  function argIsStringLiteral(call, position) {
@@ -12779,7 +12878,7 @@ function isSafeJinjaRenderCall(call, pattern, language, sourceLines) {
12779
12878
  }
12780
12879
  return false;
12781
12880
  }
12782
- function findSinks(calls, patterns, typeHierarchy, language, sourceLines) {
12881
+ function findSinks(calls, patterns, typeHierarchy, language, sourceLines, types) {
12783
12882
  const sinkMap = new Map;
12784
12883
  for (const call of calls) {
12785
12884
  for (const pattern of patterns) {
@@ -12799,7 +12898,7 @@ function findSinks(calls, patterns, typeHierarchy, language, sourceLines) {
12799
12898
  if (isSafeJSChildProcessCall(call, pattern, language)) {
12800
12899
  continue;
12801
12900
  }
12802
- if (pattern.safe_if_class_literal_at !== undefined && argIsClassLiteral(call, pattern.safe_if_class_literal_at)) {
12901
+ if (pattern.safe_if_class_literal_at !== undefined && argIsClassLiteral(call, pattern.safe_if_class_literal_at, types)) {
12803
12902
  continue;
12804
12903
  }
12805
12904
  if (pattern.safe_if_string_literal_at !== undefined && argIsStringLiteral(call, pattern.safe_if_string_literal_at)) {
@@ -30796,6 +30895,9 @@ class MyBatisAnnotationSqlSinkPass {
30796
30895
  }
30797
30896
 
30798
30897
  // ../circle-ir/dist/analysis/passes/sink-filter-pass.js
30898
+ function escapeReSf(s) {
30899
+ return s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
30900
+ }
30799
30901
  var JS_XSS_SANITIZERS = [
30800
30902
  /\bDOMPurify\.sanitize\s*\(/,
30801
30903
  /\bsanitizeHtml\s*\(/,
@@ -31425,6 +31527,8 @@ class SinkFilterPass {
31425
31527
  if (language === "java") {
31426
31528
  const sourceLines = ctx.code.split(`
31427
31529
  `);
31530
+ const irCalls = ctx.graph.ir.calls;
31531
+ const irTypes = ctx.graph.ir.types;
31428
31532
  filtered = filtered.filter((sink) => {
31429
31533
  if (sink.type !== "command_injection")
31430
31534
  return true;
@@ -31435,6 +31539,24 @@ class SinkFilterPass {
31435
31539
  return true;
31436
31540
  if (PROCESS_BUILDER_ARGV_FORM_RE.test(sinkLineText))
31437
31541
  return false;
31542
+ const ctorCall = irCalls.find((c) => c.method_name === "ProcessBuilder" && c.location.line === sink.line && (c.receiver === null || c.receiver === undefined));
31543
+ if (!ctorCall || ctorCall.arguments.length === 0)
31544
+ return true;
31545
+ const arg0 = ctorCall.arguments[0];
31546
+ if (!arg0)
31547
+ return true;
31548
+ const argExpr = (arg0.expression ?? "").trim();
31549
+ const varName = arg0.variable ?? "";
31550
+ if (varName !== "" && argExpr === varName && !/[(.]/.test(argExpr)) {
31551
+ const t = resolveIdentifierType(varName, ctorCall.in_method, irTypes);
31552
+ if (isArgvContainerType(t))
31553
+ return false;
31554
+ }
31555
+ if (varName !== "" && new RegExp(`^${escapeReSf(varName)}\\s*\\(`).test(argExpr)) {
31556
+ const t = resolveCallReturnType(varName, ctorCall.in_method, irTypes);
31557
+ if (isArgvContainerType(t))
31558
+ return false;
31559
+ }
31438
31560
  return true;
31439
31561
  });
31440
31562
  }
@@ -44796,7 +44918,7 @@ var colors = {
44796
44918
  };
44797
44919
 
44798
44920
  // src/version.ts
44799
- var version = "3.175.0";
44921
+ var version = "3.176.0";
44800
44922
 
44801
44923
  // src/formatters.ts
44802
44924
  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.175.0",
3
+ "version": "3.176.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.175.0"
69
+ "circle-ir": "^3.176.0"
70
70
  },
71
71
  "devDependencies": {
72
72
  "@types/node": "^25.5.0",