cognium-dev 3.70.0 → 3.72.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 +199 -21
  2. package/package.json +2 -2
package/dist/cli.js CHANGED
@@ -11116,6 +11116,29 @@ var DEFAULT_SINKS = [
11116
11116
  { method: "debug", class: "console", type: "log_injection", cwe: "CWE-117", severity: "low", arg_positions: [0, 1, 2, 3], languages: ["javascript", "typescript"] },
11117
11117
  { method: "trace", class: "console", type: "log_injection", cwe: "CWE-117", severity: "low", arg_positions: [0, 1, 2, 3], languages: ["javascript", "typescript"] },
11118
11118
  { method: "redirect", type: "open_redirect", cwe: "CWE-601", severity: "medium", arg_positions: [0], languages: ["javascript", "typescript"] },
11119
+ { method: "search", class: "ldap", type: "ldap_injection", cwe: "CWE-90", severity: "high", arg_positions: [1, 2], languages: ["javascript", "typescript"] },
11120
+ { method: "searchSync", class: "ldap", type: "ldap_injection", cwe: "CWE-90", severity: "high", arg_positions: [1, 2], languages: ["javascript", "typescript"] },
11121
+ { method: "search", class: "ldapjs", type: "ldap_injection", cwe: "CWE-90", severity: "high", arg_positions: [1, 2], languages: ["javascript", "typescript"] },
11122
+ { method: "searchSync", class: "ldapjs", type: "ldap_injection", cwe: "CWE-90", severity: "high", arg_positions: [1, 2], languages: ["javascript", "typescript"] },
11123
+ { method: "select", class: "xpath", type: "xpath_injection", cwe: "CWE-643", severity: "high", arg_positions: [0], languages: ["javascript", "typescript"] },
11124
+ { method: "select1", class: "xpath", type: "xpath_injection", cwe: "CWE-643", severity: "high", arg_positions: [0], languages: ["javascript", "typescript"] },
11125
+ { method: "evaluate", class: "xpath", type: "xpath_injection", cwe: "CWE-643", severity: "high", arg_positions: [0], languages: ["javascript", "typescript"] },
11126
+ { method: "parse", class: "xpath", type: "xpath_injection", cwe: "CWE-643", severity: "high", arg_positions: [0], languages: ["javascript", "typescript"] },
11127
+ { method: "parseXml", class: "libxml", type: "xxe", cwe: "CWE-611", severity: "high", arg_positions: [0], languages: ["javascript", "typescript"] },
11128
+ { method: "parseXmlString", class: "libxml", type: "xxe", cwe: "CWE-611", severity: "high", arg_positions: [0], languages: ["javascript", "typescript"] },
11129
+ { method: "parseXml", class: "libxmljs", type: "xxe", cwe: "CWE-611", severity: "high", arg_positions: [0], languages: ["javascript", "typescript"] },
11130
+ { method: "parseXmlString", class: "libxmljs", type: "xxe", cwe: "CWE-611", severity: "high", arg_positions: [0], languages: ["javascript", "typescript"] },
11131
+ { method: "parseFromString", class: "DOMParser", type: "xxe", cwe: "CWE-611", severity: "high", arg_positions: [0], languages: ["javascript", "typescript"] },
11132
+ { method: "parseFromString", class: "xmldom", type: "xxe", cwe: "CWE-611", severity: "high", arg_positions: [0], languages: ["javascript", "typescript"] },
11133
+ { method: "render", class: "ejs", type: "code_injection", cwe: "CWE-94", severity: "critical", arg_positions: [0], languages: ["javascript", "typescript"] },
11134
+ { method: "compile", class: "ejs", type: "code_injection", cwe: "CWE-94", severity: "critical", arg_positions: [0], languages: ["javascript", "typescript"] },
11135
+ { method: "render", class: "handlebars", type: "code_injection", cwe: "CWE-94", severity: "critical", arg_positions: [0], languages: ["javascript", "typescript"] },
11136
+ { method: "compile", class: "handlebars", type: "code_injection", cwe: "CWE-94", severity: "critical", arg_positions: [0], languages: ["javascript", "typescript"] },
11137
+ { method: "render", class: "pug", type: "code_injection", cwe: "CWE-94", severity: "critical", arg_positions: [0], languages: ["javascript", "typescript"] },
11138
+ { method: "compile", class: "pug", type: "code_injection", cwe: "CWE-94", severity: "critical", arg_positions: [0], languages: ["javascript", "typescript"] },
11139
+ { method: "render", class: "mustache", type: "code_injection", cwe: "CWE-94", severity: "critical", arg_positions: [0], languages: ["javascript", "typescript"] },
11140
+ { method: "render", class: "nunjucks", type: "code_injection", cwe: "CWE-94", severity: "critical", arg_positions: [0], languages: ["javascript", "typescript"] },
11141
+ { method: "renderString", class: "nunjucks", type: "code_injection", cwe: "CWE-94", severity: "critical", arg_positions: [0], languages: ["javascript", "typescript"] },
11119
11142
  { method: "system", class: "os", type: "command_injection", cwe: "CWE-78", severity: "critical", arg_positions: [0] },
11120
11143
  { method: "popen", class: "os", type: "command_injection", cwe: "CWE-78", severity: "critical", arg_positions: [0] },
11121
11144
  { method: "run", class: "subprocess", type: "command_injection", cwe: "CWE-78", severity: "critical", arg_positions: [0] },
@@ -11192,6 +11215,13 @@ var DEFAULT_SINKS = [
11192
11215
  { method: "delete_one", class: "Collection", type: "nosql_injection", cwe: "CWE-943", severity: "critical", arg_positions: [0] },
11193
11216
  { method: "delete_many", class: "Collection", type: "nosql_injection", cwe: "CWE-943", severity: "critical", arg_positions: [0] },
11194
11217
  { method: "aggregate", class: "Collection", type: "nosql_injection", cwe: "CWE-943", severity: "critical", arg_positions: [0] },
11218
+ { method: "find_one", type: "nosql_injection", cwe: "CWE-943", severity: "critical", arg_positions: [0], languages: ["python"] },
11219
+ { method: "update_one", type: "nosql_injection", cwe: "CWE-943", severity: "critical", arg_positions: [0, 1], languages: ["python"] },
11220
+ { method: "update_many", type: "nosql_injection", cwe: "CWE-943", severity: "critical", arg_positions: [0, 1], languages: ["python"] },
11221
+ { method: "delete_one", type: "nosql_injection", cwe: "CWE-943", severity: "critical", arg_positions: [0], languages: ["python"] },
11222
+ { method: "delete_many", type: "nosql_injection", cwe: "CWE-943", severity: "critical", arg_positions: [0], languages: ["python"] },
11223
+ { method: "replace_one", type: "nosql_injection", cwe: "CWE-943", severity: "critical", arg_positions: [0, 1], languages: ["python"] },
11224
+ { method: "count_documents", type: "nosql_injection", cwe: "CWE-943", severity: "critical", arg_positions: [0], languages: ["python"] },
11195
11225
  { method: "from_string", class: "Template", type: "code_injection", cwe: "CWE-94", severity: "critical", arg_positions: [0] },
11196
11226
  { method: "Template", class: "jinja2", type: "code_injection", cwe: "CWE-94", severity: "critical", arg_positions: [0] },
11197
11227
  { method: "Template", class: "mako", type: "code_injection", cwe: "CWE-94", severity: "critical", arg_positions: [0] },
@@ -11200,6 +11230,13 @@ var DEFAULT_SINKS = [
11200
11230
  { method: "error", class: "logger", type: "log_injection", cwe: "CWE-117", severity: "low", arg_positions: [0] },
11201
11231
  { method: "debug", class: "logger", type: "log_injection", cwe: "CWE-117", severity: "low", arg_positions: [0] },
11202
11232
  { method: "critical", class: "logger", type: "log_injection", cwe: "CWE-117", severity: "low", arg_positions: [0] },
11233
+ { method: "info", class: "logging", type: "log_injection", cwe: "CWE-117", severity: "low", arg_positions: [0] },
11234
+ { method: "warning", class: "logging", type: "log_injection", cwe: "CWE-117", severity: "low", arg_positions: [0] },
11235
+ { method: "error", class: "logging", type: "log_injection", cwe: "CWE-117", severity: "low", arg_positions: [0] },
11236
+ { method: "debug", class: "logging", type: "log_injection", cwe: "CWE-117", severity: "low", arg_positions: [0] },
11237
+ { method: "critical", class: "logging", type: "log_injection", cwe: "CWE-117", severity: "low", arg_positions: [0] },
11238
+ { method: "log", class: "logging", type: "log_injection", cwe: "CWE-117", severity: "low", arg_positions: [1] },
11239
+ { method: "exception", class: "logging", type: "log_injection", cwe: "CWE-117", severity: "low", arg_positions: [0] },
11203
11240
  { method: "command", class: "ProcessBuilder", type: "command_injection", cwe: "CWE-78", severity: "critical", arg_positions: [0] },
11204
11241
  { method: "inheritIO", class: "ProcessBuilder", type: "command_injection", cwe: "CWE-78", severity: "critical", arg_positions: [] },
11205
11242
  { method: "step", class: "StepExecution", type: "command_injection", cwe: "CWE-78", severity: "critical", arg_positions: [0] },
@@ -15192,6 +15229,9 @@ function isFalsePositive(result, sinkLine, taintedVar) {
15192
15229
  if (varValue && varValue.type !== "unknown" && !result.tainted.has(taintedVar)) {
15193
15230
  return { isFalsePositive: true, reason: `variable_is_constant: ${varValue.value}` };
15194
15231
  }
15232
+ if (taintedVar.startsWith("self.") || taintedVar.startsWith("this.")) {
15233
+ return { isFalsePositive: false, reason: null };
15234
+ }
15195
15235
  if (result.symbols.has(taintedVar) && !result.tainted.has(taintedVar)) {
15196
15236
  return { isFalsePositive: true, reason: "variable_not_tainted" };
15197
15237
  }
@@ -21139,15 +21179,18 @@ function findGetterSources(types, instanceFieldTaint, _sourceCode) {
21139
21179
  return sources;
21140
21180
  }
21141
21181
  function findOopFieldReadSources(types, sourceCode, language) {
21142
- if (language !== "java" && language !== "python")
21182
+ if (language !== "java" && language !== "python" && language !== "javascript" && language !== "typescript")
21143
21183
  return [];
21144
21184
  const sources = [];
21145
21185
  const lines = sourceCode.split(`
21146
21186
  `);
21147
21187
  const isPython = language === "python";
21188
+ const isJs = language === "javascript" || language === "typescript";
21189
+ const isJava = language === "java";
21148
21190
  const SELF = isPython ? "self" : "this";
21149
21191
  const javaHttpPattern = /\b(?:req|request|httpRequest|servletRequest|httpServletRequest)\.(?:getParameter|getParameterValues|getParameterMap|getHeader|getHeaders|getCookies|getQueryString|getPathInfo|getRequestURI|getRequestURL|getInputStream|getReader)\b/;
21150
21192
  const fieldAssignRe = new RegExp(`^\\s*${SELF}\\.([A-Za-z_]\\w*)\\s*=\\s*(.+?)(?:;\\s*)?$`);
21193
+ const fieldAssignReG = new RegExp(`${SELF}\\.([A-Za-z_]\\w*)\\s*=\\s*([^;}\\n]+)`, "g");
21151
21194
  const commentPrefix = isPython ? "#" : "//";
21152
21195
  for (const type of types) {
21153
21196
  if (type.kind !== "class")
@@ -21161,7 +21204,12 @@ function findOopFieldReadSources(types, sourceCode, language) {
21161
21204
  ctor = m;
21162
21205
  break;
21163
21206
  }
21164
- } else {
21207
+ } else if (isJs) {
21208
+ if (m.name === "constructor") {
21209
+ ctor = m;
21210
+ break;
21211
+ }
21212
+ } else if (isJava) {
21165
21213
  if (m.name === type.name) {
21166
21214
  ctor = m;
21167
21215
  break;
@@ -21183,26 +21231,44 @@ function findOopFieldReadSources(types, sourceCode, language) {
21183
21231
  const line = lines[i2] ?? "";
21184
21232
  if (line.trim().startsWith(commentPrefix))
21185
21233
  continue;
21186
- const m = line.match(fieldAssignRe);
21187
- if (!m)
21234
+ const pairs = [];
21235
+ const anchored = line.match(fieldAssignRe);
21236
+ if (anchored)
21237
+ pairs.push({ field: anchored[1], rhs: anchored[2].trim().replace(/;\s*$/, "") });
21238
+ if (isJs) {
21239
+ for (const m of line.matchAll(fieldAssignReG)) {
21240
+ const field = m[1];
21241
+ const rhs = m[2].trim().replace(/;\s*$/, "");
21242
+ if (!pairs.some((p) => p.field === field))
21243
+ pairs.push({ field, rhs });
21244
+ }
21245
+ }
21246
+ if (pairs.length === 0)
21188
21247
  continue;
21189
- const fieldName = m[1];
21190
- const rhs = m[2].trim().replace(/;\s*$/, "");
21191
- let sourceType = null;
21192
- if (paramNames.has(rhs)) {
21193
- sourceType = "interprocedural_param";
21194
- } else if (!isPython && javaHttpPattern.test(rhs)) {
21195
- sourceType = "http_param";
21196
- } else if (isPython) {
21197
- for (const { pattern, type: type2 } of PYTHON_TAINTED_PATTERNS2) {
21198
- if (pattern.test(rhs)) {
21199
- sourceType = type2;
21200
- break;
21248
+ for (const { field: fieldName, rhs } of pairs) {
21249
+ let sourceType = null;
21250
+ if (paramNames.has(rhs)) {
21251
+ sourceType = "interprocedural_param";
21252
+ } else if (isJava && javaHttpPattern.test(rhs)) {
21253
+ sourceType = "http_param";
21254
+ } else if (isPython) {
21255
+ for (const { pattern, type: type2 } of PYTHON_TAINTED_PATTERNS2) {
21256
+ if (pattern.test(rhs)) {
21257
+ sourceType = type2;
21258
+ break;
21259
+ }
21260
+ }
21261
+ } else if (isJs) {
21262
+ for (const { pattern, type: type2 } of JS_TAINTED_PATTERNS) {
21263
+ if (pattern.test(rhs)) {
21264
+ sourceType = type2;
21265
+ break;
21266
+ }
21201
21267
  }
21202
21268
  }
21203
- }
21204
- if (sourceType) {
21205
- fieldTaint.set(fieldName, { line: i2 + 1, type: sourceType });
21269
+ if (sourceType) {
21270
+ fieldTaint.set(fieldName, { line: i2 + 1, type: sourceType });
21271
+ }
21206
21272
  }
21207
21273
  }
21208
21274
  if (fieldTaint.size === 0)
@@ -21228,6 +21294,13 @@ function findOopFieldReadSources(types, sourceCode, language) {
21228
21294
  let returnedField = null;
21229
21295
  let returnStatementCount = 0;
21230
21296
  const returnRe = new RegExp(`\\breturn\\s+${SELF}\\.([A-Za-z_]\\w*)\\s*[;}]?`);
21297
+ const guardRePy = /\bif\s+[\w.]+\s+(?:not\s+)?in\s+(?:self\.)?[A-Z_][A-Z0-9_]*\s*:/;
21298
+ const guardThrowRePy = /^\s*(?:raise\b|abort\b|return\s+(?:None\b|''|""|\)?$))/;
21299
+ const guardReJv = /\bif\s*\(\s*!\s*(?:this\.)?[A-Z_][A-Z0-9_]*\s*\.\s*(?:contains|includes|has|matches)\s*\(/;
21300
+ const guardThrowReJv = /^\s*(?:throw\b|return\s+null\b)/;
21301
+ const guardRe = isPython ? guardRePy : guardReJv;
21302
+ const guardThrowRe = isPython ? guardThrowRePy : guardThrowReJv;
21303
+ let hasAllowlistGuard = false;
21231
21304
  for (let i2 = mStart - 1;i2 < Math.min(mEnd, lines.length); i2++) {
21232
21305
  const raw = lines[i2] ?? "";
21233
21306
  const trimmed = raw.trim();
@@ -21243,8 +21316,17 @@ function findOopFieldReadSources(types, sourceCode, language) {
21243
21316
  returnStatementCount = 99;
21244
21317
  break;
21245
21318
  }
21319
+ if (guardRe.test(trimmed)) {
21320
+ for (let j = i2 + 1;j < Math.min(i2 + 4, mEnd, lines.length); j++) {
21321
+ const next = lines[j] ?? "";
21322
+ if (guardThrowRe.test(next)) {
21323
+ hasAllowlistGuard = true;
21324
+ break;
21325
+ }
21326
+ }
21327
+ }
21246
21328
  }
21247
- if (returnStatementCount === 1 && returnedField && fieldTaint.has(returnedField)) {
21329
+ if (returnStatementCount === 1 && returnedField && fieldTaint.has(returnedField) && !hasAllowlistGuard) {
21248
21330
  const fieldInfo = fieldTaint.get(returnedField);
21249
21331
  const getterVar = isPython ? `${SELF}.${m.name}` : m.name;
21250
21332
  sources.push({
@@ -22059,11 +22141,20 @@ class SinkFilterPass {
22059
22141
  const { pyTaintedVars, pySanitizedVars } = langSources;
22060
22142
  const sourceLines = ctx.code.split(`
22061
22143
  `);
22144
+ const oopFieldVars = new Set;
22145
+ for (const s of sources) {
22146
+ if (s.variable && s.variable.startsWith("self.")) {
22147
+ oopFieldVars.add(s.variable);
22148
+ }
22149
+ }
22062
22150
  filtered = filtered.filter((sink) => {
22063
22151
  if (sink.type !== "xpath_injection")
22064
22152
  return true;
22065
22153
  const sinkLineText = sourceLines[sink.line - 1] ?? "";
22066
22154
  const taintedVarOnLine = [...pyTaintedVars.keys()].find((v) => new RegExp(`\\b${v}\\b`).test(sinkLineText));
22155
+ const oopVarOnLine = [...oopFieldVars].find((v) => sinkLineText.includes(v));
22156
+ if (oopVarOnLine)
22157
+ return true;
22067
22158
  if (!taintedVarOnLine)
22068
22159
  return false;
22069
22160
  if (pySanitizedVars.has(taintedVarOnLine))
@@ -22835,6 +22926,23 @@ class TaintPropagationPass {
22835
22926
  return false;
22836
22927
  });
22837
22928
  }
22929
+ if (typeof ctx.code === "string") {
22930
+ const sinkByLine = new Map;
22931
+ for (const s of sinks) {
22932
+ if (s.type === "nosql_injection")
22933
+ sinkByLine.set(s.line, s);
22934
+ }
22935
+ if (sinkByLine.size > 0) {
22936
+ finalFlows = finalFlows.filter((f) => {
22937
+ if (f.sink_type !== "nosql_injection")
22938
+ return true;
22939
+ const sink = sinkByLine.get(f.sink_line);
22940
+ if (!sink || !sink.code || !sink.method)
22941
+ return true;
22942
+ return !isMongoValueBoundFilter(sink.code, sink.method);
22943
+ });
22944
+ }
22945
+ }
22838
22946
  if (finalFlows.length > 1) {
22839
22947
  const bestByKey = new Map;
22840
22948
  for (const f of finalFlows) {
@@ -22852,6 +22960,76 @@ class TaintPropagationPass {
22852
22960
  return { flows: finalFlows };
22853
22961
  }
22854
22962
  }
22963
+ function isMongoValueBoundFilter(sinkCode, sinkMethod) {
22964
+ if (!sinkCode || !sinkMethod)
22965
+ return false;
22966
+ const callIdx = sinkCode.indexOf(`${sinkMethod}(`);
22967
+ if (callIdx < 0)
22968
+ return false;
22969
+ const openIdx = callIdx + sinkMethod.length;
22970
+ let depth = 0;
22971
+ let braceDepth = 0;
22972
+ let bracketDepth = 0;
22973
+ let inString = null;
22974
+ let firstArgEnd = -1;
22975
+ let firstArgComma = -1;
22976
+ const limit = Math.min(sinkCode.length, openIdx + 4096);
22977
+ for (let i2 = openIdx;i2 < limit; i2++) {
22978
+ const ch = sinkCode[i2];
22979
+ if (inString) {
22980
+ if (ch === "\\" && i2 + 1 < limit) {
22981
+ i2++;
22982
+ continue;
22983
+ }
22984
+ if (ch === inString)
22985
+ inString = null;
22986
+ continue;
22987
+ }
22988
+ if (ch === '"' || ch === "'" || ch === "`") {
22989
+ inString = ch;
22990
+ continue;
22991
+ }
22992
+ if (ch === "(")
22993
+ depth++;
22994
+ else if (ch === ")") {
22995
+ depth--;
22996
+ if (depth === 0) {
22997
+ firstArgEnd = i2;
22998
+ break;
22999
+ }
23000
+ } else if (ch === "{")
23001
+ braceDepth++;
23002
+ else if (ch === "}")
23003
+ braceDepth--;
23004
+ else if (ch === "[")
23005
+ bracketDepth++;
23006
+ else if (ch === "]")
23007
+ bracketDepth--;
23008
+ else if (ch === "," && depth === 1 && braceDepth === 0 && bracketDepth === 0) {
23009
+ if (firstArgComma < 0)
23010
+ firstArgComma = i2;
23011
+ }
23012
+ }
23013
+ if (firstArgEnd < 0)
23014
+ return false;
23015
+ const argEnd = firstArgComma >= 0 ? firstArgComma : firstArgEnd;
23016
+ const firstArg = sinkCode.slice(openIdx + 1, argEnd).trim();
23017
+ if (!firstArg)
23018
+ return false;
23019
+ if (firstArg[0] !== "{" || firstArg[firstArg.length - 1] !== "}")
23020
+ return false;
23021
+ const body2 = firstArg.slice(1, -1).trim();
23022
+ if (!body2)
23023
+ return false;
23024
+ const stripped = body2.replace(/(['"`])(?:\\.|(?!\1).)*\1/g, '""');
23025
+ if (/\.\.\./.test(stripped))
23026
+ return false;
23027
+ if (/(^|[,{\s])\$[A-Za-z_]\w*\s*:/.test(stripped))
23028
+ return false;
23029
+ if (/(['"])\$[A-Za-z_]\w*\1\s*:/.test(body2))
23030
+ return false;
23031
+ return true;
23032
+ }
22855
23033
  function isInJavaSanitizedMethod(code, types, sinkLine, sinkType) {
22856
23034
  if (!types || types.length === 0)
22857
23035
  return false;
@@ -31898,7 +32076,7 @@ var colors = {
31898
32076
  };
31899
32077
 
31900
32078
  // src/version.ts
31901
- var version = "3.70.0";
32079
+ var version = "3.72.0";
31902
32080
 
31903
32081
  // src/formatters.ts
31904
32082
  var SINK_SEVERITY = {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "cognium-dev",
3
- "version": "3.70.0",
3
+ "version": "3.72.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.70.0"
68
+ "circle-ir": "^3.72.0"
69
69
  },
70
70
  "devDependencies": {
71
71
  "@types/node": "^25.5.0",