cognium-dev 3.96.0 → 3.98.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.
- package/dist/cli.js +144 -1
- package/package.json +2 -2
package/dist/cli.js
CHANGED
|
@@ -12138,6 +12138,134 @@ function isFunctionCallbackArgument(arg) {
|
|
|
12138
12138
|
return true;
|
|
12139
12139
|
return false;
|
|
12140
12140
|
}
|
|
12141
|
+
function findGoLocalDeclaredType(varName, callLine, sourceLines) {
|
|
12142
|
+
if (!varName)
|
|
12143
|
+
return null;
|
|
12144
|
+
const WINDOW = 50;
|
|
12145
|
+
const startIdx = Math.max(0, callLine - 2);
|
|
12146
|
+
const endIdx = Math.max(0, startIdx - WINDOW);
|
|
12147
|
+
const name2 = varName.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
12148
|
+
const VAR_DECL_RE = new RegExp(`^\\s*var\\s+${name2}\\s+([^=\\n][^\\n]*)$`);
|
|
12149
|
+
const VAR_INIT_RE = new RegExp(`^\\s*var\\s+${name2}(?:\\s+[^=\\n]*?)?\\s*=\\s*(.+)$`);
|
|
12150
|
+
const SHORT_INIT_RE = new RegExp(`^\\s*${name2}\\s*:=\\s*(.+)$`);
|
|
12151
|
+
for (let i2 = startIdx;i2 >= endIdx; i2--) {
|
|
12152
|
+
const raw = sourceLines[i2];
|
|
12153
|
+
if (raw === undefined)
|
|
12154
|
+
continue;
|
|
12155
|
+
const line = raw.replace(/\/\/.*$/, "").trimEnd();
|
|
12156
|
+
let m = VAR_DECL_RE.exec(line);
|
|
12157
|
+
if (m) {
|
|
12158
|
+
return { kind: "typed-decl", text: m[1].trim() };
|
|
12159
|
+
}
|
|
12160
|
+
m = VAR_INIT_RE.exec(line);
|
|
12161
|
+
if (m) {
|
|
12162
|
+
return classifyGoRhs(m[1].trim());
|
|
12163
|
+
}
|
|
12164
|
+
m = SHORT_INIT_RE.exec(line);
|
|
12165
|
+
if (m) {
|
|
12166
|
+
return classifyGoRhs(m[1].trim());
|
|
12167
|
+
}
|
|
12168
|
+
}
|
|
12169
|
+
return null;
|
|
12170
|
+
}
|
|
12171
|
+
function classifyGoRhs(rhs) {
|
|
12172
|
+
const body2 = rhs.startsWith("&") ? rhs.slice(1).trimStart() : rhs;
|
|
12173
|
+
if (/^make\s*\(/.test(body2)) {
|
|
12174
|
+
return { kind: "untyped" };
|
|
12175
|
+
}
|
|
12176
|
+
const parenIdx = body2.indexOf("(");
|
|
12177
|
+
const braceIdx = body2.indexOf("{");
|
|
12178
|
+
if (parenIdx >= 0 && (braceIdx < 0 || parenIdx < braceIdx)) {
|
|
12179
|
+
return { kind: "untyped" };
|
|
12180
|
+
}
|
|
12181
|
+
if (braceIdx > 0) {
|
|
12182
|
+
const tag = body2.slice(0, braceIdx).trim();
|
|
12183
|
+
if (tag.length > 0) {
|
|
12184
|
+
if (/\binterface\s*\{\s*\}/.test(tag))
|
|
12185
|
+
return { kind: "untyped" };
|
|
12186
|
+
if (/\bany\b/.test(tag))
|
|
12187
|
+
return { kind: "untyped" };
|
|
12188
|
+
return { kind: "typed-init", text: tag };
|
|
12189
|
+
}
|
|
12190
|
+
}
|
|
12191
|
+
return { kind: "untyped" };
|
|
12192
|
+
}
|
|
12193
|
+
function classifyGoDestinationType(decl) {
|
|
12194
|
+
if (decl === null)
|
|
12195
|
+
return "unknown";
|
|
12196
|
+
if (decl.kind === "untyped")
|
|
12197
|
+
return "unknown";
|
|
12198
|
+
const text = decl.text;
|
|
12199
|
+
if (/\binterface\s*\{\s*\}/.test(text))
|
|
12200
|
+
return "unsafe";
|
|
12201
|
+
if (/\bany\b/.test(text))
|
|
12202
|
+
return "unsafe";
|
|
12203
|
+
return "safe";
|
|
12204
|
+
}
|
|
12205
|
+
function isSafeGoJsonUnmarshalCall(call, pattern, language, sourceLines) {
|
|
12206
|
+
if (language !== "go")
|
|
12207
|
+
return false;
|
|
12208
|
+
if (pattern.type !== "deserialization")
|
|
12209
|
+
return false;
|
|
12210
|
+
if (!sourceLines)
|
|
12211
|
+
return false;
|
|
12212
|
+
const method = call.method_name;
|
|
12213
|
+
let destPos;
|
|
12214
|
+
if (method === "Unmarshal" && pattern.class === "json") {
|
|
12215
|
+
destPos = 1;
|
|
12216
|
+
} else if (method === "Decode" && pattern.class === "Decoder") {
|
|
12217
|
+
destPos = 0;
|
|
12218
|
+
} else {
|
|
12219
|
+
return false;
|
|
12220
|
+
}
|
|
12221
|
+
const destArg = call.arguments.find((a) => a.position === destPos);
|
|
12222
|
+
if (!destArg)
|
|
12223
|
+
return false;
|
|
12224
|
+
const expr = (destArg.expression ?? "").trim();
|
|
12225
|
+
if (expr.length === 0)
|
|
12226
|
+
return false;
|
|
12227
|
+
if (expr.startsWith("&")) {
|
|
12228
|
+
const inner = expr.slice(1).trimStart();
|
|
12229
|
+
const compIdx = inner.indexOf("{");
|
|
12230
|
+
if (compIdx > 0) {
|
|
12231
|
+
const decl2 = { kind: "typed-init", text: inner.slice(0, compIdx).trim() };
|
|
12232
|
+
return classifyGoDestinationType(decl2) === "safe";
|
|
12233
|
+
}
|
|
12234
|
+
const m = /^&([A-Za-z_]\w*)$/.exec(expr);
|
|
12235
|
+
if (!m)
|
|
12236
|
+
return false;
|
|
12237
|
+
const varName = m[1];
|
|
12238
|
+
const decl = findGoLocalDeclaredType(varName, call.location.line, sourceLines);
|
|
12239
|
+
return classifyGoDestinationType(decl) === "safe";
|
|
12240
|
+
}
|
|
12241
|
+
const idMatch = /^([A-Za-z_]\w*)$/.exec(expr);
|
|
12242
|
+
if (idMatch) {
|
|
12243
|
+
const decl = findGoLocalDeclaredType(idMatch[1], call.location.line, sourceLines);
|
|
12244
|
+
return classifyGoDestinationType(decl) === "safe";
|
|
12245
|
+
}
|
|
12246
|
+
return false;
|
|
12247
|
+
}
|
|
12248
|
+
var TEMPLATE_LITERAL_RECEIVER_RE = /^Template\(\s*(?:"[^"\\]*"|'[^'\\]*')\s*\)$/;
|
|
12249
|
+
function isSafeJinjaRenderCall(call, pattern, language) {
|
|
12250
|
+
if (language !== "python")
|
|
12251
|
+
return false;
|
|
12252
|
+
if (pattern.type !== "xss" && pattern.type !== "code_injection")
|
|
12253
|
+
return false;
|
|
12254
|
+
const method = call.method_name;
|
|
12255
|
+
if (method === "render_template_string") {
|
|
12256
|
+
const arg0 = call.arguments.find((a) => a.position === 0);
|
|
12257
|
+
return typeof arg0?.literal === "string";
|
|
12258
|
+
}
|
|
12259
|
+
if (method === "Template" || method === "from_string") {
|
|
12260
|
+
const arg0 = call.arguments.find((a) => a.position === 0);
|
|
12261
|
+
return typeof arg0?.literal === "string";
|
|
12262
|
+
}
|
|
12263
|
+
if (method === "render") {
|
|
12264
|
+
const receiver = (call.receiver ?? "").trim();
|
|
12265
|
+
return TEMPLATE_LITERAL_RECEIVER_RE.test(receiver);
|
|
12266
|
+
}
|
|
12267
|
+
return false;
|
|
12268
|
+
}
|
|
12141
12269
|
function findSinks(calls, patterns, typeHierarchy, language, sourceLines) {
|
|
12142
12270
|
const sinkMap = new Map;
|
|
12143
12271
|
for (const call of calls) {
|
|
@@ -12176,6 +12304,12 @@ function findSinks(calls, patterns, typeHierarchy, language, sourceLines) {
|
|
|
12176
12304
|
continue;
|
|
12177
12305
|
}
|
|
12178
12306
|
}
|
|
12307
|
+
if (isSafeGoJsonUnmarshalCall(call, pattern, language, sourceLines)) {
|
|
12308
|
+
continue;
|
|
12309
|
+
}
|
|
12310
|
+
if (isSafeJinjaRenderCall(call, pattern, language)) {
|
|
12311
|
+
continue;
|
|
12312
|
+
}
|
|
12179
12313
|
const location = formatCallLocation(call);
|
|
12180
12314
|
const key = `${location}:${call.location.line}:${pattern.cwe}`;
|
|
12181
12315
|
const confidence = calculateSinkConfidence(call, pattern);
|
|
@@ -22601,6 +22735,13 @@ function findPythonTrustBoundaryViolations(sourceCode, taintedVars) {
|
|
|
22601
22735
|
}
|
|
22602
22736
|
return violations;
|
|
22603
22737
|
}
|
|
22738
|
+
var JINJA_LITERAL_ARG_RE_FRAG = `(?:"[^"\\\\]*"|'[^'\\\\]*')`;
|
|
22739
|
+
var JINJA_SAFE_RTS_RE = new RegExp(`^(?:flask\\.|jinja2\\.)?render_template_string\\s*\\(\\s*${JINJA_LITERAL_ARG_RE_FRAG}\\s*[,)]`);
|
|
22740
|
+
var JINJA_SAFE_TEMPLATE_RE = new RegExp(`^(?:jinja2\\.)?Template\\s*\\(\\s*${JINJA_LITERAL_ARG_RE_FRAG}\\s*\\)(?:\\s*\\.render\\s*\\(|\\s*$)`);
|
|
22741
|
+
function isSafeJinjaReturnExpr(expr) {
|
|
22742
|
+
const trimmed = expr.trim();
|
|
22743
|
+
return JINJA_SAFE_RTS_RE.test(trimmed) || JINJA_SAFE_TEMPLATE_RE.test(trimmed);
|
|
22744
|
+
}
|
|
22604
22745
|
function findPythonReturnXSSSinks(sourceCode, taintedVars) {
|
|
22605
22746
|
if (taintedVars.size === 0)
|
|
22606
22747
|
return [];
|
|
@@ -22622,6 +22763,8 @@ function findPythonReturnXSSSinks(sourceCode, taintedVars) {
|
|
|
22622
22763
|
const looksLikeHTML = expr.includes("<") || /['"]\s*\+/.test(expr) || /\+\s*['"]/.test(expr) || /f['"][^'"]*\{/.test(expr);
|
|
22623
22764
|
if (!looksLikeHTML)
|
|
22624
22765
|
continue;
|
|
22766
|
+
if (isSafeJinjaReturnExpr(expr))
|
|
22767
|
+
continue;
|
|
22625
22768
|
sinks.push({ sinkLine: i2 + 1 });
|
|
22626
22769
|
}
|
|
22627
22770
|
return sinks;
|
|
@@ -34598,7 +34741,7 @@ var colors = {
|
|
|
34598
34741
|
};
|
|
34599
34742
|
|
|
34600
34743
|
// src/version.ts
|
|
34601
|
-
var version = "3.
|
|
34744
|
+
var version = "3.98.0";
|
|
34602
34745
|
|
|
34603
34746
|
// src/formatters.ts
|
|
34604
34747
|
var SINK_SEVERITY = {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "cognium-dev",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.98.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.
|
|
68
|
+
"circle-ir": "^3.98.0"
|
|
69
69
|
},
|
|
70
70
|
"devDependencies": {
|
|
71
71
|
"@types/node": "^25.5.0",
|