cognium-dev 3.145.0 → 3.147.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 +183 -2
- package/package.json +2 -2
package/dist/cli.js
CHANGED
|
@@ -12318,6 +12318,38 @@ function isSafeGoExecCommandCall(call, pattern, language) {
|
|
|
12318
12318
|
]);
|
|
12319
12319
|
if (SHELL_PROGRAMS.has(program))
|
|
12320
12320
|
return false;
|
|
12321
|
+
const PACKAGE_MANAGER_EXEC_SUBCOMMANDS = {
|
|
12322
|
+
go: new Set(["install", "run", "get"]),
|
|
12323
|
+
npm: new Set(["install", "i", "exec"]),
|
|
12324
|
+
npx: new Set(["*"]),
|
|
12325
|
+
pip: new Set(["install"]),
|
|
12326
|
+
pip3: new Set(["install"]),
|
|
12327
|
+
gem: new Set(["install"]),
|
|
12328
|
+
cargo: new Set(["install", "run"]),
|
|
12329
|
+
yarn: new Set(["add", "install", "exec", "dlx"]),
|
|
12330
|
+
pnpm: new Set(["add", "install", "exec", "dlx"])
|
|
12331
|
+
};
|
|
12332
|
+
const pmSubcommands = PACKAGE_MANAGER_EXEC_SUBCOMMANDS[program];
|
|
12333
|
+
if (pmSubcommands !== undefined) {
|
|
12334
|
+
const subcommandArgPos = pattern.method === "CommandContext" ? 2 : 1;
|
|
12335
|
+
const subcommandArg = call.arguments.find((a) => a.position === subcommandArgPos);
|
|
12336
|
+
if (!subcommandArg)
|
|
12337
|
+
return false;
|
|
12338
|
+
let subcommand;
|
|
12339
|
+
if (subcommandArg.literal !== null && subcommandArg.literal !== undefined) {
|
|
12340
|
+
subcommand = String(subcommandArg.literal);
|
|
12341
|
+
} else {
|
|
12342
|
+
const expr = (subcommandArg.expression ?? "").trim();
|
|
12343
|
+
if (expr.startsWith('"') || expr.startsWith("`") || expr.startsWith("'")) {
|
|
12344
|
+
subcommand = expr.slice(1, -1);
|
|
12345
|
+
}
|
|
12346
|
+
}
|
|
12347
|
+
if (subcommand === undefined)
|
|
12348
|
+
return false;
|
|
12349
|
+
if (pmSubcommands.has("*") || pmSubcommands.has(subcommand)) {
|
|
12350
|
+
return false;
|
|
12351
|
+
}
|
|
12352
|
+
}
|
|
12321
12353
|
return true;
|
|
12322
12354
|
}
|
|
12323
12355
|
function isSafeJSChildProcessCall(call, pattern, language) {
|
|
@@ -22868,6 +22900,8 @@ class LanguageSourcesPass {
|
|
|
22868
22900
|
additionalSanitizers.push(...findJsSsrfAllowlistGuardSanitizers(code));
|
|
22869
22901
|
additionalSanitizers.push(...findJsArgvFormExecSanitizers(code));
|
|
22870
22902
|
additionalSanitizers.push(...findJsParameterizedSqlSanitizers(code));
|
|
22903
|
+
additionalSanitizers.push(...findJsPathResolveStartsWithGuardSanitizers(code));
|
|
22904
|
+
additionalSanitizers.push(...findJsCommandAllowlistGuardSanitizers(code));
|
|
22871
22905
|
for (const finding of findJsPatternFindings(code, graph.ir.meta.file)) {
|
|
22872
22906
|
ctx.addFinding(finding);
|
|
22873
22907
|
}
|
|
@@ -24987,6 +25021,126 @@ function findJsParameterizedSqlSanitizers(code) {
|
|
|
24987
25021
|
}
|
|
24988
25022
|
return sanitizers;
|
|
24989
25023
|
}
|
|
25024
|
+
function findJsPathResolveStartsWithGuardSanitizers(code) {
|
|
25025
|
+
const sanitizers = [];
|
|
25026
|
+
const lines = code.split(`
|
|
25027
|
+
`);
|
|
25028
|
+
const resolveDeclRe = /\b(?:const|let|var)\s+([A-Za-z_]\w*)\s*=\s*(?:(?:node:)?path|nodePath|Path)\s*\.\s*(?:resolve|join)\s*\(\s*([A-Za-z_]\w*(?:\.\w+)*)\s*,/;
|
|
25029
|
+
const startsWithGuardRe = (varName, rootName) => new RegExp(`if\\s*\\(\\s*!\\s*${varName}\\s*\\.\\s*startsWith\\s*\\(\\s*${rootName}\\b`);
|
|
25030
|
+
const terminatorRe = /\b(?:throw|return|res\s*\.\s*status\s*\([^)]*\)\s*\.\s*(?:send|end|json))/;
|
|
25031
|
+
const candidates = [];
|
|
25032
|
+
for (let i2 = 0;i2 < lines.length; i2++) {
|
|
25033
|
+
const m = resolveDeclRe.exec(lines[i2]);
|
|
25034
|
+
if (!m)
|
|
25035
|
+
continue;
|
|
25036
|
+
candidates.push({ line: i2 + 1, fullVar: m[1], rootVar: m[2] });
|
|
25037
|
+
}
|
|
25038
|
+
if (candidates.length === 0)
|
|
25039
|
+
return sanitizers;
|
|
25040
|
+
for (const c of candidates) {
|
|
25041
|
+
const guardRe = startsWithGuardRe(c.fullVar, c.rootVar);
|
|
25042
|
+
let guardLine = -1;
|
|
25043
|
+
for (let l = c.line;l < Math.min(lines.length, c.line + 6); l++) {
|
|
25044
|
+
if (!guardRe.test(lines[l]))
|
|
25045
|
+
continue;
|
|
25046
|
+
if (terminatorRe.test(lines[l]) || l + 1 < lines.length && terminatorRe.test(lines[l + 1])) {
|
|
25047
|
+
guardLine = l + 1;
|
|
25048
|
+
break;
|
|
25049
|
+
}
|
|
25050
|
+
}
|
|
25051
|
+
if (guardLine < 0)
|
|
25052
|
+
continue;
|
|
25053
|
+
const varRefRe = new RegExp(`\\b${c.fullVar}\\b`);
|
|
25054
|
+
sanitizers.push({
|
|
25055
|
+
type: "js_path_resolve_startswith_guard",
|
|
25056
|
+
method: "startsWith",
|
|
25057
|
+
line: c.line,
|
|
25058
|
+
sanitizes: ["path_traversal", "external_taint_escape"]
|
|
25059
|
+
});
|
|
25060
|
+
for (let l = c.line;l < lines.length; l++) {
|
|
25061
|
+
if (!varRefRe.test(lines[l]))
|
|
25062
|
+
continue;
|
|
25063
|
+
sanitizers.push({
|
|
25064
|
+
type: "js_path_resolve_startswith_guard",
|
|
25065
|
+
method: "startsWith",
|
|
25066
|
+
line: l + 1,
|
|
25067
|
+
sanitizes: ["path_traversal", "external_taint_escape"]
|
|
25068
|
+
});
|
|
25069
|
+
}
|
|
25070
|
+
}
|
|
25071
|
+
return sanitizers;
|
|
25072
|
+
}
|
|
25073
|
+
function findJsCommandAllowlistGuardSanitizers(code) {
|
|
25074
|
+
const sanitizers = [];
|
|
25075
|
+
const lines = code.split(`
|
|
25076
|
+
`);
|
|
25077
|
+
const shoutyCase = /^[A-Z][A-Z0-9_]+$/;
|
|
25078
|
+
const allowlistTokens = /(?:allowed|accepted|whitelist|permitted|valid|approved|cmds|commands|tools)/i;
|
|
25079
|
+
const isAllowlistName = (n) => shoutyCase.test(n) || allowlistTokens.test(n);
|
|
25080
|
+
const guardHas = /\bif\s*\(\s*!\s*([A-Za-z_]\w*)\s*\.\s*(?:has|includes)\s*\(\s*([A-Za-z_]\w*)\s*\)\s*\)/;
|
|
25081
|
+
const guardIndexOf = /\bif\s*\(\s*([A-Za-z_]\w*)\s*\.\s*indexOf\s*\(\s*([A-Za-z_]\w*)\s*\)\s*(?:<\s*0|===\s*-1|==\s*-1)\s*\)/;
|
|
25082
|
+
const terminator = /\b(?:return|throw|res\s*\.\s*status\s*\([^)]*\)\s*\.\s*(?:send|end|json))/;
|
|
25083
|
+
for (let i2 = 0;i2 < lines.length; i2++) {
|
|
25084
|
+
const line = lines[i2];
|
|
25085
|
+
let m = guardHas.exec(line);
|
|
25086
|
+
let allow = null;
|
|
25087
|
+
let guardedVar = null;
|
|
25088
|
+
if (m) {
|
|
25089
|
+
allow = m[1];
|
|
25090
|
+
guardedVar = m[2];
|
|
25091
|
+
} else {
|
|
25092
|
+
m = guardIndexOf.exec(line);
|
|
25093
|
+
if (m) {
|
|
25094
|
+
allow = m[1];
|
|
25095
|
+
guardedVar = m[2];
|
|
25096
|
+
}
|
|
25097
|
+
}
|
|
25098
|
+
if (!allow || !guardedVar)
|
|
25099
|
+
continue;
|
|
25100
|
+
if (!isAllowlistName(allow))
|
|
25101
|
+
continue;
|
|
25102
|
+
let bodyHasTerminator = terminator.test(line);
|
|
25103
|
+
let blockEnd = i2;
|
|
25104
|
+
if (!bodyHasTerminator) {
|
|
25105
|
+
let braceDepth = 0;
|
|
25106
|
+
let started = false;
|
|
25107
|
+
const maxScan = Math.min(lines.length, i2 + 26);
|
|
25108
|
+
for (let j = i2;j < maxScan; j++) {
|
|
25109
|
+
const ln = lines[j];
|
|
25110
|
+
for (const ch of ln) {
|
|
25111
|
+
if (ch === "{") {
|
|
25112
|
+
braceDepth++;
|
|
25113
|
+
started = true;
|
|
25114
|
+
} else if (ch === "}") {
|
|
25115
|
+
braceDepth--;
|
|
25116
|
+
if (started && braceDepth === 0) {
|
|
25117
|
+
blockEnd = j;
|
|
25118
|
+
break;
|
|
25119
|
+
}
|
|
25120
|
+
}
|
|
25121
|
+
}
|
|
25122
|
+
if (started && j > i2 && terminator.test(ln))
|
|
25123
|
+
bodyHasTerminator = true;
|
|
25124
|
+
if (started && braceDepth === 0 && blockEnd !== i2)
|
|
25125
|
+
break;
|
|
25126
|
+
}
|
|
25127
|
+
if (!started || !bodyHasTerminator)
|
|
25128
|
+
continue;
|
|
25129
|
+
}
|
|
25130
|
+
const varRefRe = new RegExp(`\\b${guardedVar}\\b`);
|
|
25131
|
+
for (let l = blockEnd + 1;l < lines.length; l++) {
|
|
25132
|
+
if (!varRefRe.test(lines[l]))
|
|
25133
|
+
continue;
|
|
25134
|
+
sanitizers.push({
|
|
25135
|
+
type: "js_command_allowlist_guard",
|
|
25136
|
+
method: "if",
|
|
25137
|
+
line: l + 1,
|
|
25138
|
+
sanitizes: ["command_injection", "external_taint_escape"]
|
|
25139
|
+
});
|
|
25140
|
+
}
|
|
25141
|
+
}
|
|
25142
|
+
return sanitizers;
|
|
25143
|
+
}
|
|
24990
25144
|
function findJavaPathNormalizeStartsWithGuardSanitizers(code) {
|
|
24991
25145
|
const sanitizers = [];
|
|
24992
25146
|
const lines = code.split(`
|
|
@@ -28756,6 +28910,16 @@ var COMPILED_TEMPLATE_TYPES = new Set([
|
|
|
28756
28910
|
"VelocityTemplate",
|
|
28757
28911
|
"BeetlTemplate"
|
|
28758
28912
|
]);
|
|
28913
|
+
var TEMPLATE_ENGINE_LITERAL_TARGETS = new Set([
|
|
28914
|
+
"VelocityEngine",
|
|
28915
|
+
"Velocity",
|
|
28916
|
+
"TemplateEngine",
|
|
28917
|
+
"SpringTemplateEngine",
|
|
28918
|
+
"Configuration",
|
|
28919
|
+
"PebbleEngine",
|
|
28920
|
+
"Pebble",
|
|
28921
|
+
"Handlebars"
|
|
28922
|
+
]);
|
|
28759
28923
|
var REFLECTION_LITERAL_METHODS = new Set([
|
|
28760
28924
|
"forName",
|
|
28761
28925
|
"loadClass",
|
|
@@ -28788,7 +28952,7 @@ var PROTOCOL_CLIENT_PACKAGES = [
|
|
|
28788
28952
|
];
|
|
28789
28953
|
var OS_EXEC_RECEIVER_RE = /\b(?:Runtime|ProcessBuilder|DefaultExecutor|Executor|Exec|Launcher|ProcStarter|ProcessExecutor|RuntimeUtil)\s*[.(]/;
|
|
28790
28954
|
var PROCESS_BUILDER_ARGV_FORM_RE = /\bnew\s+ProcessBuilder\s*\(\s*(?:Arrays\.asList\b|List\.of\b|Collections\.singletonList\b|new\s+ArrayList\b|new\s+String\s*\[\s*\]\s*\{|"[^"]*"\s*,)/;
|
|
28791
|
-
var JAVA_THROW_STATEMENT_RE = /^\s*throw\s+new\s+\w+(?:Exception|Error)\b
|
|
28955
|
+
var JAVA_THROW_STATEMENT_RE = /^\s*throw\s+new\s+\w+(?:Exception|Error)\b[^;]*;\s*(?:\/\/.*|\/\*.*)?$/;
|
|
28792
28956
|
var JEXL_ENGINE_TYPES = new Set(["JexlEngine", "Jexl", "JxltEngine"]);
|
|
28793
28957
|
var JEXL_EXPRESSION_TYPE_RE = /(?:Jexl)?(?:Expression|Script|Template)(?:Script)?$/;
|
|
28794
28958
|
var TEMPLATE_COMPILE_RECEIVER_TYPES = new Set([
|
|
@@ -29214,6 +29378,23 @@ class SinkFilterPass {
|
|
|
29214
29378
|
const recvType = resolveJavaReceiverType(receiver, sink.line, sourceLines);
|
|
29215
29379
|
if (recvType && COMPILED_TEMPLATE_TYPES.has(recvType))
|
|
29216
29380
|
return false;
|
|
29381
|
+
if (recvType && TEMPLATE_ENGINE_LITERAL_TARGETS.has(recvType)) {
|
|
29382
|
+
const args2 = extractJavaCallArgs(method, sinkLineText);
|
|
29383
|
+
if (args2 !== null && args2.length >= 1 && isJavaLiteralOrAnnotationAccessor(args2[0])) {
|
|
29384
|
+
return false;
|
|
29385
|
+
}
|
|
29386
|
+
}
|
|
29387
|
+
}
|
|
29388
|
+
if (method === "evaluate" && receiver) {
|
|
29389
|
+
const recvType = resolveJavaReceiverType(receiver, sink.line, sourceLines);
|
|
29390
|
+
if (recvType && TEMPLATE_ENGINE_LITERAL_TARGETS.has(recvType)) {
|
|
29391
|
+
const args2 = extractJavaCallArgs(method, sinkLineText);
|
|
29392
|
+
if (args2 !== null && args2.length >= 1) {
|
|
29393
|
+
const last = args2[args2.length - 1] ?? "";
|
|
29394
|
+
if (isJavaLiteralOrAnnotationAccessor(last))
|
|
29395
|
+
return false;
|
|
29396
|
+
}
|
|
29397
|
+
}
|
|
29217
29398
|
}
|
|
29218
29399
|
if (method && REFLECTION_LITERAL_METHODS.has(method)) {
|
|
29219
29400
|
const args2 = extractJavaCallArgs(method, sinkLineText);
|
|
@@ -42081,7 +42262,7 @@ var colors = {
|
|
|
42081
42262
|
};
|
|
42082
42263
|
|
|
42083
42264
|
// src/version.ts
|
|
42084
|
-
var version = "3.
|
|
42265
|
+
var version = "3.147.0";
|
|
42085
42266
|
|
|
42086
42267
|
// src/formatters.ts
|
|
42087
42268
|
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.
|
|
3
|
+
"version": "3.147.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.
|
|
69
|
+
"circle-ir": "^3.147.0"
|
|
70
70
|
},
|
|
71
71
|
"devDependencies": {
|
|
72
72
|
"@types/node": "^25.5.0",
|