cognium-dev 3.57.0 → 3.58.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 +463 -45
- package/package.json +2 -2
package/dist/cli.js
CHANGED
|
@@ -10233,10 +10233,10 @@ var DEFAULT_SOURCES = [
|
|
|
10233
10233
|
{ method: "cookie", class: "HttpRequest", type: "http_cookie", severity: "high", return_tainted: true },
|
|
10234
10234
|
{ method: "param", class: "Request", type: "http_param", severity: "high", return_tainted: true },
|
|
10235
10235
|
{ method: "cookies", class: "Request", type: "http_cookie", severity: "high", return_tainted: true },
|
|
10236
|
-
{ method: "Json", type: "http_body", severity: "high", return_tainted: true },
|
|
10237
|
-
{ method: "Query", type: "http_param", severity: "high", return_tainted: true },
|
|
10238
|
-
{ method: "Path", type: "http_path", severity: "high", return_tainted: true },
|
|
10239
|
-
{ method: "Form", type: "http_param", severity: "high", return_tainted: true },
|
|
10236
|
+
{ method: "Json", type: "http_body", severity: "high", return_tainted: true, languages: ["rust"] },
|
|
10237
|
+
{ method: "Query", type: "http_param", severity: "high", return_tainted: true, languages: ["rust"] },
|
|
10238
|
+
{ method: "Path", type: "http_path", severity: "high", return_tainted: true, languages: ["rust"] },
|
|
10239
|
+
{ method: "Form", type: "http_param", severity: "high", return_tainted: true, languages: ["rust"] },
|
|
10240
10240
|
{ method: "var", class: "env", type: "env_input", severity: "medium", return_tainted: true },
|
|
10241
10241
|
{ method: "var_os", class: "env", type: "env_input", severity: "medium", return_tainted: true },
|
|
10242
10242
|
{ method: "args", class: "env", type: "env_input", severity: "medium", return_tainted: true },
|
|
@@ -10389,10 +10389,10 @@ var DEFAULT_SINKS = [
|
|
|
10389
10389
|
{ method: "UrlResource", class: "constructor", type: "path_traversal", cwe: "CWE-22", severity: "high", arg_positions: [0] },
|
|
10390
10390
|
{ method: "PathResource", class: "constructor", type: "path_traversal", cwe: "CWE-22", severity: "high", arg_positions: [0] },
|
|
10391
10391
|
{ method: "forFile", type: "path_traversal", cwe: "CWE-22", severity: "high", arg_positions: [0] },
|
|
10392
|
-
{ method: "resolve", class: "Path", type: "path_traversal", cwe: "CWE-22", severity: "high", arg_positions: [0] },
|
|
10393
|
-
{ method: "resolve", type: "path_traversal", cwe: "CWE-22", severity: "high", arg_positions: [0] },
|
|
10394
|
-
{ method: "resolveSibling", class: "Path", type: "path_traversal", cwe: "CWE-22", severity: "high", arg_positions: [0] },
|
|
10395
|
-
{ method: "relativize", class: "Path", type: "path_traversal", cwe: "CWE-22", severity: "medium", arg_positions: [0] },
|
|
10392
|
+
{ method: "resolve", class: "Path", type: "path_traversal", cwe: "CWE-22", severity: "high", arg_positions: [0], languages: ["java"] },
|
|
10393
|
+
{ method: "resolve", type: "path_traversal", cwe: "CWE-22", severity: "high", arg_positions: [0], languages: ["java"] },
|
|
10394
|
+
{ method: "resolveSibling", class: "Path", type: "path_traversal", cwe: "CWE-22", severity: "high", arg_positions: [0], languages: ["java"] },
|
|
10395
|
+
{ method: "relativize", class: "Path", type: "path_traversal", cwe: "CWE-22", severity: "medium", arg_positions: [0], languages: ["java"] },
|
|
10396
10396
|
{ method: "staticFiles", type: "path_traversal", cwe: "CWE-22", severity: "high", arg_positions: [0] },
|
|
10397
10397
|
{ method: "setRoot", type: "path_traversal", cwe: "CWE-22", severity: "high", arg_positions: [0] },
|
|
10398
10398
|
{ method: "setWebRoot", type: "path_traversal", cwe: "CWE-22", severity: "high", arg_positions: [0] },
|
|
@@ -11208,6 +11208,11 @@ var DEFAULT_SANITIZERS = [
|
|
|
11208
11208
|
{ method: "toRealPath", class: "Path", removes: ["path_traversal"] },
|
|
11209
11209
|
{ method: "file_name", removes: ["path_traversal"] },
|
|
11210
11210
|
{ method: "canonicalize", removes: ["path_traversal"] },
|
|
11211
|
+
{ method: "Base", class: "filepath", removes: ["path_traversal"] },
|
|
11212
|
+
{ method: "Base", class: "path", removes: ["path_traversal"] },
|
|
11213
|
+
{ method: "Clean", class: "filepath", removes: ["path_traversal"] },
|
|
11214
|
+
{ method: "Clean", class: "path", removes: ["path_traversal"] },
|
|
11215
|
+
{ method: "EvalSymlinks", class: "filepath", removes: ["path_traversal"] },
|
|
11211
11216
|
{ method: "replace", removes: ["log_injection"] },
|
|
11212
11217
|
{ method: "encodeForLDAP", removes: ["ldap_injection"] },
|
|
11213
11218
|
{ method: "encodeForDN", removes: ["ldap_injection"] },
|
|
@@ -11268,6 +11273,7 @@ var DEFAULT_SANITIZERS = [
|
|
|
11268
11273
|
{ method: "abspath", class: "os.path", removes: ["path_traversal"] },
|
|
11269
11274
|
{ method: "realpath", class: "path", removes: ["path_traversal"] },
|
|
11270
11275
|
{ method: "abspath", class: "path", removes: ["path_traversal"] },
|
|
11276
|
+
{ method: "resolve", class: "Path", removes: ["path_traversal"] },
|
|
11271
11277
|
{ method: "int", removes: ["sql_injection", "command_injection", "xss"] },
|
|
11272
11278
|
{ method: "float", removes: ["sql_injection", "command_injection"] },
|
|
11273
11279
|
{ method: "query!", removes: ["sql_injection"] },
|
|
@@ -11287,7 +11293,25 @@ var DEFAULT_SANITIZERS = [
|
|
|
11287
11293
|
{ method: "encode_text", class: "html_escape", removes: ["xss"] },
|
|
11288
11294
|
{ method: "encode_attribute", class: "html_escape", removes: ["xss"] },
|
|
11289
11295
|
{ method: "escape_html", removes: ["xss"] },
|
|
11290
|
-
{ method: "parse", removes: ["sql_injection", "command_injection", "xss"] }
|
|
11296
|
+
{ method: "parse", removes: ["sql_injection", "command_injection", "xss"] },
|
|
11297
|
+
{ method: "parseInt", class: "Integer", removes: ["sql_injection", "command_injection", "path_traversal", "code_injection"] },
|
|
11298
|
+
{ method: "parseLong", class: "Long", removes: ["sql_injection", "command_injection", "path_traversal", "code_injection"] },
|
|
11299
|
+
{ method: "parseFloat", class: "Float", removes: ["sql_injection", "command_injection", "path_traversal", "code_injection"] },
|
|
11300
|
+
{ method: "parseDouble", class: "Double", removes: ["sql_injection", "command_injection", "path_traversal", "code_injection"] },
|
|
11301
|
+
{ method: "parseShort", class: "Short", removes: ["sql_injection", "command_injection", "path_traversal", "code_injection"] },
|
|
11302
|
+
{ method: "parseByte", class: "Byte", removes: ["sql_injection", "command_injection", "path_traversal", "code_injection"] },
|
|
11303
|
+
{ method: "fromString", class: "UUID", removes: ["sql_injection", "command_injection", "path_traversal", "code_injection"] },
|
|
11304
|
+
{ method: "BigInt", removes: ["sql_injection", "nosql_injection", "command_injection", "path_traversal", "code_injection"] },
|
|
11305
|
+
{ method: "Atoi", class: "strconv", removes: ["sql_injection", "command_injection", "path_traversal", "code_injection"] },
|
|
11306
|
+
{ method: "ParseInt", class: "strconv", removes: ["sql_injection", "command_injection", "path_traversal", "code_injection"] },
|
|
11307
|
+
{ method: "ParseFloat", class: "strconv", removes: ["sql_injection", "command_injection", "path_traversal", "code_injection"] },
|
|
11308
|
+
{ method: "ParseUint", class: "strconv", removes: ["sql_injection", "command_injection", "path_traversal", "code_injection"] },
|
|
11309
|
+
{ method: "ParseBool", class: "strconv", removes: ["sql_injection", "command_injection", "path_traversal", "code_injection"] },
|
|
11310
|
+
{ method: "Parse", class: "uuid", removes: ["sql_injection", "command_injection", "path_traversal", "code_injection"] },
|
|
11311
|
+
{ method: "MustParse", class: "uuid", removes: ["sql_injection", "command_injection", "path_traversal", "code_injection"] },
|
|
11312
|
+
{ method: "bool", removes: ["sql_injection", "command_injection", "xss", "code_injection"] },
|
|
11313
|
+
{ method: "UUID", class: "uuid", removes: ["sql_injection", "command_injection", "path_traversal", "code_injection"] },
|
|
11314
|
+
{ method: "Decimal", class: "decimal", removes: ["sql_injection", "command_injection", "path_traversal", "code_injection"] }
|
|
11291
11315
|
];
|
|
11292
11316
|
function getDefaultConfig() {
|
|
11293
11317
|
return {
|
|
@@ -11398,7 +11422,7 @@ function analyzeTaint(calls, types, config = getDefaultConfig(), typeHierarchy,
|
|
|
11398
11422
|
`) : undefined;
|
|
11399
11423
|
const sources = findSources(calls, types, config.sources, sourceLines, language);
|
|
11400
11424
|
const sinks = findSinks(calls, config.sinks, typeHierarchy, language, sourceLines);
|
|
11401
|
-
const sanitizers = findSanitizers(calls, types, config.sanitizers);
|
|
11425
|
+
const sanitizers = findSanitizers(calls, types, config.sanitizers, sourceLines);
|
|
11402
11426
|
return { sources, sinks, sanitizers };
|
|
11403
11427
|
}
|
|
11404
11428
|
function attachSourceLineCode(sources, sinks, code) {
|
|
@@ -11419,6 +11443,9 @@ function findSources(calls, types, patterns, sourceLines, language) {
|
|
|
11419
11443
|
const sources = [];
|
|
11420
11444
|
for (const call of calls) {
|
|
11421
11445
|
for (const pattern of patterns) {
|
|
11446
|
+
if (pattern.languages && pattern.languages.length > 0 && language !== undefined && !pattern.languages.includes(language)) {
|
|
11447
|
+
continue;
|
|
11448
|
+
}
|
|
11422
11449
|
if (matchesSourcePattern(call, pattern)) {
|
|
11423
11450
|
sources.push({
|
|
11424
11451
|
type: pattern.type,
|
|
@@ -12048,6 +12075,15 @@ function receiverMightBeClass(receiver, className) {
|
|
|
12048
12075
|
if (receiver === className) {
|
|
12049
12076
|
return true;
|
|
12050
12077
|
}
|
|
12078
|
+
if (receiver.endsWith(")")) {
|
|
12079
|
+
const ctorMatch = receiver.match(/^(\w+)\(/);
|
|
12080
|
+
if (ctorMatch) {
|
|
12081
|
+
const ctorName = ctorMatch[1];
|
|
12082
|
+
if (ctorName === className || ctorName.toLowerCase() === className.toLowerCase()) {
|
|
12083
|
+
return true;
|
|
12084
|
+
}
|
|
12085
|
+
}
|
|
12086
|
+
}
|
|
12051
12087
|
if (receiver.includes("::")) {
|
|
12052
12088
|
const scopePrefix = receiver.match(/^(\w+)::/);
|
|
12053
12089
|
if (scopePrefix) {
|
|
@@ -12290,7 +12326,7 @@ function calculateSinkConfidence(call, pattern) {
|
|
|
12290
12326
|
}
|
|
12291
12327
|
return Math.min(confidence, 1);
|
|
12292
12328
|
}
|
|
12293
|
-
function findSanitizers(calls, types, patterns) {
|
|
12329
|
+
function findSanitizers(calls, types, patterns, sourceLines) {
|
|
12294
12330
|
const sanitizers = [];
|
|
12295
12331
|
const sanitizerMethods = new Set;
|
|
12296
12332
|
for (const type of types) {
|
|
@@ -12300,6 +12336,77 @@ function findSanitizers(calls, types, patterns) {
|
|
|
12300
12336
|
}
|
|
12301
12337
|
}
|
|
12302
12338
|
}
|
|
12339
|
+
const wrapperSanitizers = new Map;
|
|
12340
|
+
for (const type of types) {
|
|
12341
|
+
for (const method of type.methods) {
|
|
12342
|
+
const bodySize = method.end_line - method.start_line;
|
|
12343
|
+
if (bodySize < 0 || bodySize > 2)
|
|
12344
|
+
continue;
|
|
12345
|
+
const paramNames = new Set(method.parameters.map((p) => p.name));
|
|
12346
|
+
if (paramNames.size === 0)
|
|
12347
|
+
continue;
|
|
12348
|
+
const inside = [];
|
|
12349
|
+
for (const c of calls) {
|
|
12350
|
+
if (c.location.line < method.start_line || c.location.line > method.end_line)
|
|
12351
|
+
continue;
|
|
12352
|
+
if (c.method_name === method.name)
|
|
12353
|
+
continue;
|
|
12354
|
+
inside.push(c);
|
|
12355
|
+
}
|
|
12356
|
+
if (inside.length !== 1)
|
|
12357
|
+
continue;
|
|
12358
|
+
const innerCall = inside[0];
|
|
12359
|
+
let matched;
|
|
12360
|
+
for (const pattern of patterns) {
|
|
12361
|
+
if (matchesSanitizerPattern(innerCall, pattern)) {
|
|
12362
|
+
matched = pattern;
|
|
12363
|
+
break;
|
|
12364
|
+
}
|
|
12365
|
+
}
|
|
12366
|
+
if (!matched || !matched.removes || matched.removes.length === 0)
|
|
12367
|
+
continue;
|
|
12368
|
+
let argOk = false;
|
|
12369
|
+
for (const arg of innerCall.arguments) {
|
|
12370
|
+
if (arg.variable && paramNames.has(arg.variable)) {
|
|
12371
|
+
argOk = true;
|
|
12372
|
+
break;
|
|
12373
|
+
}
|
|
12374
|
+
}
|
|
12375
|
+
if (!argOk)
|
|
12376
|
+
continue;
|
|
12377
|
+
if (sourceLines) {
|
|
12378
|
+
const lineText = sourceLines[innerCall.location.line - 1] ?? "";
|
|
12379
|
+
const stripped = lineText.trim();
|
|
12380
|
+
const returnMatch = stripped.match(/^return\s+(?:await\s+)?(.*)$/);
|
|
12381
|
+
if (!returnMatch)
|
|
12382
|
+
continue;
|
|
12383
|
+
const after = returnMatch[1].replace(/;\s*$/, "").trimEnd();
|
|
12384
|
+
const callPrefix = innerCall.receiver ? `${innerCall.receiver}.${innerCall.method_name}(` : `${innerCall.method_name}(`;
|
|
12385
|
+
if (!after.startsWith(callPrefix))
|
|
12386
|
+
continue;
|
|
12387
|
+
if (!after.endsWith(")"))
|
|
12388
|
+
continue;
|
|
12389
|
+
}
|
|
12390
|
+
const existing = wrapperSanitizers.get(method.name);
|
|
12391
|
+
if (existing) {
|
|
12392
|
+
const set = new Set([...existing, ...matched.removes]);
|
|
12393
|
+
wrapperSanitizers.set(method.name, Array.from(set));
|
|
12394
|
+
} else {
|
|
12395
|
+
wrapperSanitizers.set(method.name, [...matched.removes]);
|
|
12396
|
+
}
|
|
12397
|
+
}
|
|
12398
|
+
}
|
|
12399
|
+
for (const call of calls) {
|
|
12400
|
+
const removes = wrapperSanitizers.get(call.method_name);
|
|
12401
|
+
if (!removes)
|
|
12402
|
+
continue;
|
|
12403
|
+
sanitizers.push({
|
|
12404
|
+
type: "derived_wrapper",
|
|
12405
|
+
method: formatSanitizerMethod(call),
|
|
12406
|
+
line: call.location.line,
|
|
12407
|
+
sanitizes: removes
|
|
12408
|
+
});
|
|
12409
|
+
}
|
|
12303
12410
|
for (const call of calls) {
|
|
12304
12411
|
if (sanitizerMethods.has(call.method_name)) {
|
|
12305
12412
|
sanitizers.push({
|
|
@@ -12957,7 +13064,24 @@ var SANITIZER_METHODS = new Set([
|
|
|
12957
13064
|
"validatePath",
|
|
12958
13065
|
"validateCityName",
|
|
12959
13066
|
"validateInput",
|
|
12960
|
-
"sanitizeInput"
|
|
13067
|
+
"sanitizeInput",
|
|
13068
|
+
"parseInt",
|
|
13069
|
+
"parseLong",
|
|
13070
|
+
"parseFloat",
|
|
13071
|
+
"parseDouble",
|
|
13072
|
+
"parseShort",
|
|
13073
|
+
"parseByte",
|
|
13074
|
+
"fromString",
|
|
13075
|
+
"Number",
|
|
13076
|
+
"BigInt",
|
|
13077
|
+
"Atoi",
|
|
13078
|
+
"ParseInt",
|
|
13079
|
+
"ParseFloat",
|
|
13080
|
+
"ParseUint",
|
|
13081
|
+
"ParseBool",
|
|
13082
|
+
"int",
|
|
13083
|
+
"float",
|
|
13084
|
+
"bool"
|
|
12961
13085
|
]);
|
|
12962
13086
|
var ANTI_SANITIZER_METHODS = new Set([
|
|
12963
13087
|
"decode",
|
|
@@ -13030,6 +13154,7 @@ class ConstantPropagator {
|
|
|
13030
13154
|
currentClassName = null;
|
|
13031
13155
|
inConstructor = false;
|
|
13032
13156
|
constructorParamPositions = new Map;
|
|
13157
|
+
safePatternFieldsCache = null;
|
|
13033
13158
|
analyze(tree, sourceCode, additionalTaintPatterns = [], sanitizerMethods = [], taintedParameters = []) {
|
|
13034
13159
|
this.source = sourceCode;
|
|
13035
13160
|
this.additionalTaintPatterns = additionalTaintPatterns;
|
|
@@ -13060,12 +13185,14 @@ class ConstantPropagator {
|
|
|
13060
13185
|
this.currentClassName = null;
|
|
13061
13186
|
this.inConstructor = false;
|
|
13062
13187
|
this.constructorParamPositions.clear();
|
|
13188
|
+
this.safePatternFieldsCache = null;
|
|
13063
13189
|
this.collectClassFields(tree.rootNode);
|
|
13064
13190
|
for (const methodName of sanitizerMethods) {
|
|
13065
13191
|
this.methodReturnsSanitized.add(methodName);
|
|
13066
13192
|
}
|
|
13067
13193
|
this.evaluator = new ExpressionEvaluator(this.source, (name2) => this.lookupSymbol(name2));
|
|
13068
13194
|
this.analyzeMethodReturns(tree.rootNode);
|
|
13195
|
+
this.seedPythonModuleConstants(tree.rootNode);
|
|
13069
13196
|
this.visit(tree.rootNode);
|
|
13070
13197
|
this.refineTaintFromConstants();
|
|
13071
13198
|
const resultTainted = new Set(this.tainted);
|
|
@@ -13410,6 +13537,141 @@ class ConstantPropagator {
|
|
|
13410
13537
|
}
|
|
13411
13538
|
}
|
|
13412
13539
|
}
|
|
13540
|
+
fieldDeclHasPrimitiveLiteralValue(node) {
|
|
13541
|
+
const primitive = new Set([
|
|
13542
|
+
"true",
|
|
13543
|
+
"false",
|
|
13544
|
+
"null_literal",
|
|
13545
|
+
"decimal_integer_literal",
|
|
13546
|
+
"hex_integer_literal",
|
|
13547
|
+
"octal_integer_literal",
|
|
13548
|
+
"binary_integer_literal",
|
|
13549
|
+
"decimal_floating_point_literal",
|
|
13550
|
+
"hex_floating_point_literal",
|
|
13551
|
+
"character_literal",
|
|
13552
|
+
"string_literal",
|
|
13553
|
+
"number",
|
|
13554
|
+
"string"
|
|
13555
|
+
]);
|
|
13556
|
+
for (const child of node.children) {
|
|
13557
|
+
if (child.type !== "variable_declarator")
|
|
13558
|
+
continue;
|
|
13559
|
+
const value = child.childForFieldName("value");
|
|
13560
|
+
if (!value)
|
|
13561
|
+
continue;
|
|
13562
|
+
if (!primitive.has(value.type))
|
|
13563
|
+
return false;
|
|
13564
|
+
}
|
|
13565
|
+
return true;
|
|
13566
|
+
}
|
|
13567
|
+
getSafePatternFields() {
|
|
13568
|
+
if (this.safePatternFieldsCache !== null)
|
|
13569
|
+
return this.safePatternFieldsCache;
|
|
13570
|
+
const set = new Set;
|
|
13571
|
+
const re = /\b(?:public\s+|private\s+|protected\s+)?(?:static\s+final|final\s+static)\s+(?:java\.util\.regex\.)?Pattern\s+(\w+)\s*=\s*(?:java\.util\.regex\.)?Pattern\s*\.\s*compile\s*\(\s*"((?:[^"\\]|\\.)*)"/g;
|
|
13572
|
+
let m;
|
|
13573
|
+
while ((m = re.exec(this.source)) !== null) {
|
|
13574
|
+
const name2 = m[1];
|
|
13575
|
+
const regex = m[2];
|
|
13576
|
+
if (this.isStrictAnchoredRegex(regex))
|
|
13577
|
+
set.add(name2);
|
|
13578
|
+
}
|
|
13579
|
+
this.safePatternFieldsCache = set;
|
|
13580
|
+
return set;
|
|
13581
|
+
}
|
|
13582
|
+
isStrictAnchoredRegex(re) {
|
|
13583
|
+
if (!re.startsWith("^") || !re.endsWith("$"))
|
|
13584
|
+
return false;
|
|
13585
|
+
const stripped = re.replace(/\[(?:[^\]\\]|\\.)*\]/g, "");
|
|
13586
|
+
const cleaned = stripped.replace(/\\./g, "");
|
|
13587
|
+
if (cleaned.includes("."))
|
|
13588
|
+
return false;
|
|
13589
|
+
if (cleaned.includes("|"))
|
|
13590
|
+
return false;
|
|
13591
|
+
return true;
|
|
13592
|
+
}
|
|
13593
|
+
detectRegexAllowlistGuard(condition, consequence) {
|
|
13594
|
+
if (!consequence)
|
|
13595
|
+
return null;
|
|
13596
|
+
let condText = getNodeText2(condition, this.source).replace(/\s+/g, "");
|
|
13597
|
+
while (condText.startsWith("(") && condText.endsWith(")")) {
|
|
13598
|
+
const inner = condText.slice(1, -1);
|
|
13599
|
+
let depth = 0;
|
|
13600
|
+
let balanced = true;
|
|
13601
|
+
for (let i2 = 0;i2 < inner.length; i2++) {
|
|
13602
|
+
if (inner[i2] === "(")
|
|
13603
|
+
depth++;
|
|
13604
|
+
else if (inner[i2] === ")")
|
|
13605
|
+
depth--;
|
|
13606
|
+
if (depth < 0) {
|
|
13607
|
+
balanced = false;
|
|
13608
|
+
break;
|
|
13609
|
+
}
|
|
13610
|
+
}
|
|
13611
|
+
if (!balanced || depth !== 0)
|
|
13612
|
+
break;
|
|
13613
|
+
condText = inner;
|
|
13614
|
+
}
|
|
13615
|
+
const m = condText.match(/^!(\w+)\.matcher\((\w+)\)\.matches\(\)$/);
|
|
13616
|
+
if (!m)
|
|
13617
|
+
return null;
|
|
13618
|
+
const patternName = m[1];
|
|
13619
|
+
const varName = m[2];
|
|
13620
|
+
if (!this.getSafePatternFields().has(patternName))
|
|
13621
|
+
return null;
|
|
13622
|
+
if (!this.consequenceContainsThrow(consequence))
|
|
13623
|
+
return null;
|
|
13624
|
+
return varName;
|
|
13625
|
+
}
|
|
13626
|
+
consequenceContainsThrow(node) {
|
|
13627
|
+
if (node.type === "throw_statement")
|
|
13628
|
+
return true;
|
|
13629
|
+
const stack = [node];
|
|
13630
|
+
while (stack.length > 0) {
|
|
13631
|
+
const n = stack.pop();
|
|
13632
|
+
if (!n)
|
|
13633
|
+
continue;
|
|
13634
|
+
if (n.type === "throw_statement")
|
|
13635
|
+
return true;
|
|
13636
|
+
if (n.type === "if_statement" || n.type === "switch_statement")
|
|
13637
|
+
continue;
|
|
13638
|
+
for (const c of n.children)
|
|
13639
|
+
stack.push(c);
|
|
13640
|
+
}
|
|
13641
|
+
return false;
|
|
13642
|
+
}
|
|
13643
|
+
seedPythonModuleConstants(root) {
|
|
13644
|
+
if (root.type !== "module")
|
|
13645
|
+
return;
|
|
13646
|
+
for (const child of root.children) {
|
|
13647
|
+
const target = child.type === "assignment" ? child : child.type === "expression_statement" && child.children.length > 0 ? child.children[0] : null;
|
|
13648
|
+
if (!target || target.type !== "assignment")
|
|
13649
|
+
continue;
|
|
13650
|
+
const left = target.childForFieldName("left");
|
|
13651
|
+
const right = target.childForFieldName("right");
|
|
13652
|
+
if (!left || !right)
|
|
13653
|
+
continue;
|
|
13654
|
+
if (left.type !== "identifier")
|
|
13655
|
+
continue;
|
|
13656
|
+
const allowed = new Set([
|
|
13657
|
+
"true",
|
|
13658
|
+
"false",
|
|
13659
|
+
"none",
|
|
13660
|
+
"integer",
|
|
13661
|
+
"float",
|
|
13662
|
+
"string"
|
|
13663
|
+
]);
|
|
13664
|
+
if (!allowed.has(right.type))
|
|
13665
|
+
continue;
|
|
13666
|
+
const name2 = getNodeText2(left, this.source);
|
|
13667
|
+
if (!name2)
|
|
13668
|
+
continue;
|
|
13669
|
+
const value = this.evaluateExpression(right);
|
|
13670
|
+
if (!isKnown(value))
|
|
13671
|
+
continue;
|
|
13672
|
+
this.symbols.set(name2, value);
|
|
13673
|
+
}
|
|
13674
|
+
}
|
|
13413
13675
|
findAllMethods(node) {
|
|
13414
13676
|
const methods = [];
|
|
13415
13677
|
const stack = [node];
|
|
@@ -13495,6 +13757,11 @@ class ConstantPropagator {
|
|
|
13495
13757
|
case "local_variable_declaration":
|
|
13496
13758
|
this.handleVariableDeclaration(node);
|
|
13497
13759
|
return false;
|
|
13760
|
+
case "field_declaration":
|
|
13761
|
+
if (this.fieldDeclHasPrimitiveLiteralValue(node)) {
|
|
13762
|
+
this.handleVariableDeclaration(node);
|
|
13763
|
+
}
|
|
13764
|
+
return false;
|
|
13498
13765
|
case "assignment_expression":
|
|
13499
13766
|
this.handleAssignment(node);
|
|
13500
13767
|
return false;
|
|
@@ -13964,6 +14231,16 @@ class ConstantPropagator {
|
|
|
13964
14231
|
}
|
|
13965
14232
|
this.inConditionalBranch = wasInConditional;
|
|
13966
14233
|
this.tainted = new Set([...taintedBefore, ...taintedAfterThen, ...taintedAfterElse]);
|
|
14234
|
+
const guardedVar = this.detectRegexAllowlistGuard(condition, consequence);
|
|
14235
|
+
if (guardedVar) {
|
|
14236
|
+
this.tainted.delete(guardedVar);
|
|
14237
|
+
this.sanitizedVars.add(guardedVar);
|
|
14238
|
+
const scoped = this.getScopedName(guardedVar);
|
|
14239
|
+
if (scoped !== guardedVar) {
|
|
14240
|
+
this.tainted.delete(scoped);
|
|
14241
|
+
this.sanitizedVars.add(scoped);
|
|
14242
|
+
}
|
|
14243
|
+
}
|
|
13967
14244
|
}
|
|
13968
14245
|
}
|
|
13969
14246
|
normalizeCondition(cond) {
|
|
@@ -14135,15 +14412,26 @@ class ConstantPropagator {
|
|
|
14135
14412
|
return null;
|
|
14136
14413
|
}
|
|
14137
14414
|
isSanitizerMethodCall(node) {
|
|
14138
|
-
|
|
14415
|
+
const methodName = this.extractCallName(node);
|
|
14416
|
+
if (!methodName)
|
|
14139
14417
|
return false;
|
|
14418
|
+
return SANITIZER_METHODS.has(methodName) || this.methodReturnsSanitized.has(methodName);
|
|
14419
|
+
}
|
|
14420
|
+
extractCallName(node) {
|
|
14421
|
+
let fnNode = null;
|
|
14422
|
+
if (node.type === "method_invocation") {
|
|
14423
|
+
fnNode = node.childForFieldName("name");
|
|
14424
|
+
} else if (node.type === "call_expression" || node.type === "call") {
|
|
14425
|
+
fnNode = node.childForFieldName("function");
|
|
14140
14426
|
}
|
|
14141
|
-
|
|
14142
|
-
|
|
14143
|
-
|
|
14427
|
+
if (!fnNode)
|
|
14428
|
+
return null;
|
|
14429
|
+
if (fnNode.type === "selector_expression" || fnNode.type === "member_expression" || fnNode.type === "attribute") {
|
|
14430
|
+
const tail = fnNode.childForFieldName("field") || fnNode.childForFieldName("property") || fnNode.childForFieldName("attribute");
|
|
14431
|
+
if (tail)
|
|
14432
|
+
return getNodeText2(tail, this.source);
|
|
14144
14433
|
}
|
|
14145
|
-
|
|
14146
|
-
return SANITIZER_METHODS.has(methodName) || this.methodReturnsSanitized.has(methodName);
|
|
14434
|
+
return getNodeText2(fnNode, this.source);
|
|
14147
14435
|
}
|
|
14148
14436
|
isAntiSanitizerCall(node) {
|
|
14149
14437
|
if (node.type !== "method_invocation") {
|
|
@@ -20821,26 +21109,44 @@ function findBashTaintSources(sourceCode, dfg) {
|
|
|
20821
21109
|
const lines = sourceCode.split(`
|
|
20822
21110
|
`);
|
|
20823
21111
|
const definedVars = new Set(dfg.defs.filter((d) => d.kind === "local").map((d) => d.variable));
|
|
21112
|
+
const fnHeaderRe = /^\s*(?:function\s+)?[A-Za-z_][\w-]*\s*\(\s*\)\s*\{?\s*$|^\s*function\s+[A-Za-z_][\w-]*\s*\{?\s*$/;
|
|
21113
|
+
let braceDepth = 0;
|
|
20824
21114
|
for (let i2 = 0;i2 < lines.length; i2++) {
|
|
20825
21115
|
const line = lines[i2];
|
|
20826
21116
|
const trimmed = line.trim();
|
|
20827
21117
|
const lineNumber = i2 + 1;
|
|
20828
21118
|
if (trimmed.startsWith("#"))
|
|
20829
21119
|
continue;
|
|
20830
|
-
const
|
|
20831
|
-
|
|
20832
|
-
|
|
20833
|
-
|
|
20834
|
-
|
|
20835
|
-
|
|
20836
|
-
sources.
|
|
20837
|
-
|
|
20838
|
-
|
|
20839
|
-
|
|
20840
|
-
|
|
20841
|
-
|
|
20842
|
-
|
|
20843
|
-
|
|
21120
|
+
const insideFunction = braceDepth > 0;
|
|
21121
|
+
if (!insideFunction) {
|
|
21122
|
+
const positionalRe = /\$([1-9@*])|\$\{([1-9@*])\}/g;
|
|
21123
|
+
let m;
|
|
21124
|
+
while ((m = positionalRe.exec(line)) !== null) {
|
|
21125
|
+
const param = m[1] ?? m[2];
|
|
21126
|
+
const alreadyExists = sources.some((s) => s.line === lineNumber && s.variable === param);
|
|
21127
|
+
if (!alreadyExists) {
|
|
21128
|
+
sources.push({
|
|
21129
|
+
type: "io_input",
|
|
21130
|
+
location: `positional parameter $${param}`,
|
|
21131
|
+
severity: "high",
|
|
21132
|
+
line: lineNumber,
|
|
21133
|
+
confidence: 1,
|
|
21134
|
+
variable: param
|
|
21135
|
+
});
|
|
21136
|
+
}
|
|
21137
|
+
}
|
|
21138
|
+
}
|
|
21139
|
+
if (fnHeaderRe.test(line) || /^\s*[A-Za-z_][\w-]*\s*\(\s*\)\s*\{/.test(line)) {
|
|
21140
|
+
const openBracesOnLine = (line.match(/\{/g) ?? []).length;
|
|
21141
|
+
const closeBracesOnLine = (line.match(/\}/g) ?? []).length;
|
|
21142
|
+
braceDepth += openBracesOnLine - closeBracesOnLine;
|
|
21143
|
+
} else {
|
|
21144
|
+
if (braceDepth > 0) {
|
|
21145
|
+
const openBracesOnLine = (line.match(/\{/g) ?? []).length;
|
|
21146
|
+
const closeBracesOnLine = (line.match(/\}/g) ?? []).length;
|
|
21147
|
+
braceDepth += openBracesOnLine - closeBracesOnLine;
|
|
21148
|
+
if (braceDepth < 0)
|
|
21149
|
+
braceDepth = 0;
|
|
20844
21150
|
}
|
|
20845
21151
|
}
|
|
20846
21152
|
const cmdSubAssign = trimmed.match(/^(\w+)=\$\((\w+)\s/);
|
|
@@ -21287,10 +21593,15 @@ function filterCleanVariableSinks(sinks, calls, taintedVars, symbols, dfg, sanit
|
|
|
21287
21593
|
const callsAtSink = callsByLine.get(sink.line) ?? [];
|
|
21288
21594
|
const isInSynchronizedBlock = synchronizedLines?.has(sink.line) ?? false;
|
|
21289
21595
|
const relevantCalls = sink.method ? callsAtSink.filter((c) => c.method_name === sink.method) : callsAtSink;
|
|
21596
|
+
const trustArgPositions = language !== "bash" && language !== "shell";
|
|
21290
21597
|
for (const call of relevantCalls) {
|
|
21291
21598
|
let allArgsAreClean = true;
|
|
21599
|
+
let dangerousArgCount = 0;
|
|
21292
21600
|
const methodName = call.in_method;
|
|
21293
21601
|
for (const arg of call.arguments) {
|
|
21602
|
+
if (trustArgPositions && sink.argPositions && sink.argPositions.length > 0 && !sink.argPositions.includes(arg.position))
|
|
21603
|
+
continue;
|
|
21604
|
+
dangerousArgCount++;
|
|
21294
21605
|
if (language === "bash" && arg.expression === call.method_name && !arg.variable && arg.literal == null)
|
|
21295
21606
|
continue;
|
|
21296
21607
|
if (arg.variable && !arg.expression?.includes("[")) {
|
|
@@ -21318,7 +21629,7 @@ function filterCleanVariableSinks(sinks, calls, taintedVars, symbols, dfg, sanit
|
|
|
21318
21629
|
allArgsAreClean = false;
|
|
21319
21630
|
}
|
|
21320
21631
|
}
|
|
21321
|
-
if (allArgsAreClean &&
|
|
21632
|
+
if (allArgsAreClean && dangerousArgCount > 0)
|
|
21322
21633
|
return false;
|
|
21323
21634
|
}
|
|
21324
21635
|
return true;
|
|
@@ -21369,6 +21680,17 @@ function filterSanitizedSinks(sinks, sanitizers, calls) {
|
|
|
21369
21680
|
}
|
|
21370
21681
|
|
|
21371
21682
|
// ../circle-ir/dist/analysis/taint-propagation.js
|
|
21683
|
+
function buildSanitizersByLine(sanitizers) {
|
|
21684
|
+
const out2 = new Map;
|
|
21685
|
+
for (const san of sanitizers) {
|
|
21686
|
+
const existing = out2.get(san.line);
|
|
21687
|
+
if (existing)
|
|
21688
|
+
existing.push(san);
|
|
21689
|
+
else
|
|
21690
|
+
out2.set(san.line, [san]);
|
|
21691
|
+
}
|
|
21692
|
+
return out2;
|
|
21693
|
+
}
|
|
21372
21694
|
function propagateTaint2(graphOrDfg, callsOrSources, sourcesOrSinks, sinksOrSanitizers, sanitizersArg) {
|
|
21373
21695
|
let graph;
|
|
21374
21696
|
let sources;
|
|
@@ -21404,7 +21726,7 @@ function propagateTaint2(graphOrDfg, callsOrSources, sourcesOrSinks, sinksOrSani
|
|
|
21404
21726
|
const defsByLine = graph.defsByLine;
|
|
21405
21727
|
const usesByLine = graph.usesByLine;
|
|
21406
21728
|
const callsByLine = graph.callsByLine;
|
|
21407
|
-
const sanitizersByLine = graph.sanitizersByLine;
|
|
21729
|
+
const sanitizersByLine = sanitizers.length > 0 ? buildSanitizersByLine(sanitizers) : graph.sanitizersByLine;
|
|
21408
21730
|
const defById = graph.defById;
|
|
21409
21731
|
const rawInitialTaint = findInitialTaint(sources, callsByLine, defsByLine);
|
|
21410
21732
|
const initialTaint = rawInitialTaint.filter((tv) => {
|
|
@@ -21672,7 +21994,7 @@ class TaintPropagationPass {
|
|
|
21672
21994
|
flows.push(f);
|
|
21673
21995
|
}
|
|
21674
21996
|
}
|
|
21675
|
-
const collectionFlows = detectCollectionFlows(calls, sources, sinks, constProp.tainted, constProp.unreachableLines) ?? [];
|
|
21997
|
+
const collectionFlows = detectCollectionFlows(calls, sources, sinks, constProp.tainted, constProp.unreachableLines, ctx.code) ?? [];
|
|
21676
21998
|
for (const f of collectionFlows) {
|
|
21677
21999
|
if (flows.some((x) => x.source_line === f.source_line && x.sink_line === f.sink_line))
|
|
21678
22000
|
continue;
|
|
@@ -21694,13 +22016,13 @@ class TaintPropagationPass {
|
|
|
21694
22016
|
continue;
|
|
21695
22017
|
flows.push(f);
|
|
21696
22018
|
}
|
|
21697
|
-
const paramFlows = detectParameterSinkFlows(types, calls, sources, sinks, constProp.unreachableLines) ?? [];
|
|
22019
|
+
const paramFlows = detectParameterSinkFlows(types, calls, sources, sinks, constProp.unreachableLines, constProp.tainted, ctx.code) ?? [];
|
|
21698
22020
|
for (const f of paramFlows) {
|
|
21699
22021
|
if (!flows.some((x) => x.source_line === f.source_line && x.sink_line === f.sink_line)) {
|
|
21700
22022
|
flows.push(f);
|
|
21701
22023
|
}
|
|
21702
22024
|
}
|
|
21703
|
-
const exprScanFlows = detectExpressionScanFlows(calls, sources, sinks, sanitizers, constProp.unreachableLines, ctx.code, ctx.language) ?? [];
|
|
22025
|
+
const exprScanFlows = detectExpressionScanFlows(calls, sources, sinks, sanitizers, constProp.unreachableLines, constProp.tainted, ctx.code, ctx.language) ?? [];
|
|
21704
22026
|
for (const f of exprScanFlows) {
|
|
21705
22027
|
if (flows.some((x) => x.source_line === f.source_line && x.sink_line === f.sink_line && x.sink_type === f.sink_type))
|
|
21706
22028
|
continue;
|
|
@@ -21722,10 +22044,25 @@ class TaintPropagationPass {
|
|
|
21722
22044
|
continue;
|
|
21723
22045
|
flows.push(f);
|
|
21724
22046
|
}
|
|
21725
|
-
|
|
22047
|
+
const sanitizedNames = constProp.sanitizedVars;
|
|
22048
|
+
const finalFlows = sanitizedNames.size === 0 ? flows : flows.filter((f) => {
|
|
22049
|
+
if (f.path.length === 0)
|
|
22050
|
+
return true;
|
|
22051
|
+
const sourceVar = f.path[0].variable;
|
|
22052
|
+
if (!sourceVar)
|
|
22053
|
+
return true;
|
|
22054
|
+
if (sanitizedNames.has(sourceVar))
|
|
22055
|
+
return false;
|
|
22056
|
+
for (const s of sanitizedNames) {
|
|
22057
|
+
if (s.endsWith(`:${sourceVar}`))
|
|
22058
|
+
return false;
|
|
22059
|
+
}
|
|
22060
|
+
return true;
|
|
22061
|
+
});
|
|
22062
|
+
return { flows: finalFlows };
|
|
21726
22063
|
}
|
|
21727
22064
|
}
|
|
21728
|
-
function detectCollectionFlows(calls, sources, sinks, taintedVars, unreachableLines) {
|
|
22065
|
+
function detectCollectionFlows(calls, sources, sinks, taintedVars, unreachableLines, code) {
|
|
21729
22066
|
const flows = [];
|
|
21730
22067
|
const callsByLine = new Map;
|
|
21731
22068
|
for (const call of calls) {
|
|
@@ -21748,6 +22085,9 @@ function detectCollectionFlows(calls, sources, sinks, taintedVars, unreachableLi
|
|
|
21748
22085
|
if (taintedVars.has(varName) || taintedVars.has(scopedName)) {
|
|
21749
22086
|
const source = sources[0];
|
|
21750
22087
|
if (source) {
|
|
22088
|
+
if (typeof code === "string" && isReassignedToLiteralBetween(code, varName, source.line, sink.line)) {
|
|
22089
|
+
continue;
|
|
22090
|
+
}
|
|
21751
22091
|
flows.push({
|
|
21752
22092
|
source_line: source.line,
|
|
21753
22093
|
sink_line: sink.line,
|
|
@@ -21782,6 +22122,9 @@ function detectCollectionFlows(calls, sources, sinks, taintedVars, unreachableLi
|
|
|
21782
22122
|
if (taintedVars.has(collectionVar) || taintedVars.has(scopedCollection)) {
|
|
21783
22123
|
const source = sources[0];
|
|
21784
22124
|
if (source) {
|
|
22125
|
+
if (typeof code === "string" && isReassignedToLiteralBetween(code, collectionVar, source.line, sink.line)) {
|
|
22126
|
+
continue;
|
|
22127
|
+
}
|
|
21785
22128
|
flows.push({
|
|
21786
22129
|
source_line: source.line,
|
|
21787
22130
|
sink_line: sink.line,
|
|
@@ -21852,7 +22195,7 @@ function detectArrayElementFlows(calls, sources, sinks, taintedArrayElements, un
|
|
|
21852
22195
|
}
|
|
21853
22196
|
return flows;
|
|
21854
22197
|
}
|
|
21855
|
-
function detectParameterSinkFlows(types, calls, sources, sinks, unreachableLines) {
|
|
22198
|
+
function detectParameterSinkFlows(types, calls, sources, sinks, unreachableLines, tainted, code) {
|
|
21856
22199
|
const flows = [];
|
|
21857
22200
|
const paramSourcesByMethod = new Map;
|
|
21858
22201
|
for (const source of sources) {
|
|
@@ -21898,6 +22241,9 @@ function detectParameterSinkFlows(types, calls, sources, sinks, unreachableLines
|
|
|
21898
22241
|
if (paramSource) {
|
|
21899
22242
|
const exists = flows.some((f) => f.source_line === paramSource.line && f.sink_line === sink.line);
|
|
21900
22243
|
if (!exists) {
|
|
22244
|
+
if (typeof code === "string" && isReassignedToLiteralBetween(code, arg.variable, paramSource.line, sink.line)) {
|
|
22245
|
+
continue;
|
|
22246
|
+
}
|
|
21901
22247
|
flows.push({
|
|
21902
22248
|
source_line: paramSource.line,
|
|
21903
22249
|
sink_line: sink.line,
|
|
@@ -21918,7 +22264,28 @@ function detectParameterSinkFlows(types, calls, sources, sinks, unreachableLines
|
|
|
21918
22264
|
}
|
|
21919
22265
|
return flows;
|
|
21920
22266
|
}
|
|
21921
|
-
function
|
|
22267
|
+
function isReassignedToLiteralBetween(code, variable, srcLine, sinkLine) {
|
|
22268
|
+
if (!variable || sinkLine - srcLine < 2)
|
|
22269
|
+
return false;
|
|
22270
|
+
if (!/^[A-Za-z_][\w]*$/.test(variable))
|
|
22271
|
+
return false;
|
|
22272
|
+
const lines = code.split(`
|
|
22273
|
+
`);
|
|
22274
|
+
const lo = Math.max(0, srcLine);
|
|
22275
|
+
const hi = Math.min(lines.length, sinkLine - 1);
|
|
22276
|
+
const strLit = `(?:"[^"\\\\]*(?:\\\\.[^"\\\\]*)*"|'[^'\\\\]*(?:\\\\.[^'\\\\]*)*'|\`[^\`\\\\]*(?:\\\\.[^\`\\\\]*)*\`)`;
|
|
22277
|
+
const reNaked = new RegExp(`^\\s*${variable}\\s*(?::?=)\\s*${strLit}\\s*;?\\s*$`);
|
|
22278
|
+
const reGuarded = new RegExp(`^\\s*if\\b.*\\b${variable}\\s*=\\s*${strLit}\\s*;?\\s*$`);
|
|
22279
|
+
for (let i2 = lo;i2 < hi; i2++) {
|
|
22280
|
+
const line = lines[i2];
|
|
22281
|
+
if (!line)
|
|
22282
|
+
continue;
|
|
22283
|
+
if (reNaked.test(line) || reGuarded.test(line))
|
|
22284
|
+
return true;
|
|
22285
|
+
}
|
|
22286
|
+
return false;
|
|
22287
|
+
}
|
|
22288
|
+
function detectExpressionScanFlows(calls, sources, sinks, sanitizers, unreachableLines, tainted, code, language) {
|
|
21922
22289
|
const flows = [];
|
|
21923
22290
|
const sourcesWithVar = sources.filter((s) => typeof s.variable === "string" && s.variable.length > 0);
|
|
21924
22291
|
const aliasSanitizedFor = new Map;
|
|
@@ -21973,10 +22340,10 @@ function detectExpressionScanFlows(calls, sources, sinks, sanitizers, unreachabl
|
|
|
21973
22340
|
continue;
|
|
21974
22341
|
const rhs = rhsMatch[1];
|
|
21975
22342
|
for (const san of lineSans) {
|
|
21976
|
-
const sanMatch = san.method.match(
|
|
22343
|
+
const sanMatch = san.method.match(/(\w+)\(\)$/);
|
|
21977
22344
|
if (!sanMatch)
|
|
21978
22345
|
continue;
|
|
21979
|
-
const sanName = sanMatch[1]
|
|
22346
|
+
const sanName = sanMatch[1];
|
|
21980
22347
|
if (!rhs.includes(`${sanName}(`))
|
|
21981
22348
|
continue;
|
|
21982
22349
|
let set = aliasSanitizedFor.get(varName);
|
|
@@ -22048,6 +22415,9 @@ function detectExpressionScanFlows(calls, sources, sinks, sanitizers, unreachabl
|
|
|
22048
22415
|
if (aliasSanitizedFor.get(source.variable)?.has(sink.type)) {
|
|
22049
22416
|
break;
|
|
22050
22417
|
}
|
|
22418
|
+
if (typeof code === "string" && isReassignedToLiteralBetween(code, source.variable, source.line, sink.line)) {
|
|
22419
|
+
break;
|
|
22420
|
+
}
|
|
22051
22421
|
flows.push({
|
|
22052
22422
|
source_line: source.line,
|
|
22053
22423
|
sink_line: sink.line,
|
|
@@ -22082,6 +22452,9 @@ function detectExpressionScanFlows(calls, sources, sinks, sanitizers, unreachabl
|
|
|
22082
22452
|
for (const source of colocSources) {
|
|
22083
22453
|
if (!canSourceReachSink(source.type, sink.type))
|
|
22084
22454
|
continue;
|
|
22455
|
+
if (source.type === "file_input" && sink.type === "path_traversal" && sink.method && source.location.includes(`${sink.method}(`)) {
|
|
22456
|
+
continue;
|
|
22457
|
+
}
|
|
22085
22458
|
if (flows.some((f) => f.source_line === source.line && f.sink_line === sink.line && f.sink_type === sink.type))
|
|
22086
22459
|
continue;
|
|
22087
22460
|
flows.push({
|
|
@@ -26325,6 +26698,25 @@ var JS_ROUTE_METHODS = new Set([
|
|
|
26325
26698
|
"head",
|
|
26326
26699
|
"options"
|
|
26327
26700
|
]);
|
|
26701
|
+
var SECURITY_MIDDLEWARE_METHODS = new Set([
|
|
26702
|
+
"helmet",
|
|
26703
|
+
"frameguard",
|
|
26704
|
+
"contentSecurityPolicy",
|
|
26705
|
+
"hsts",
|
|
26706
|
+
"noSniff",
|
|
26707
|
+
"xssFilter",
|
|
26708
|
+
"referrerPolicy",
|
|
26709
|
+
"permittedCrossDomainPolicies",
|
|
26710
|
+
"dnsPrefetchControl",
|
|
26711
|
+
"frameOptions",
|
|
26712
|
+
"headers",
|
|
26713
|
+
"httpStrictTransportSecurity",
|
|
26714
|
+
"contentTypeOptions",
|
|
26715
|
+
"xssProtection",
|
|
26716
|
+
"Talisman",
|
|
26717
|
+
"Secure"
|
|
26718
|
+
]);
|
|
26719
|
+
var SECURITY_MIDDLEWARE_ANNOTATIONS_RE = /\b(EnableWebSecurity|SecurityFilterChain|after_request|before_request)\b/;
|
|
26328
26720
|
|
|
26329
26721
|
class SecurityHeadersPass {
|
|
26330
26722
|
name = "security-headers";
|
|
@@ -26355,6 +26747,7 @@ class SecurityHeadersPass {
|
|
|
26355
26747
|
list.push(call);
|
|
26356
26748
|
}
|
|
26357
26749
|
const hasHandler = detectHandler(graph, calls);
|
|
26750
|
+
const hasGlobalMiddleware = detectGlobalSecurityMiddleware(graph, calls);
|
|
26358
26751
|
for (const rule of this.rules) {
|
|
26359
26752
|
const headerKey = rule.header.toLowerCase();
|
|
26360
26753
|
const writes = writtenHeaders.get(headerKey) ?? [];
|
|
@@ -26363,6 +26756,8 @@ class SecurityHeadersPass {
|
|
|
26363
26756
|
continue;
|
|
26364
26757
|
if (rule.requiresHandler !== false && !hasHandler)
|
|
26365
26758
|
continue;
|
|
26759
|
+
if (hasGlobalMiddleware)
|
|
26760
|
+
continue;
|
|
26366
26761
|
ctx.addFinding({
|
|
26367
26762
|
id: `${rule.rule_id}-${file}`,
|
|
26368
26763
|
pass: this.name,
|
|
@@ -26537,6 +26932,28 @@ function detectHandler(graph, calls) {
|
|
|
26537
26932
|
}
|
|
26538
26933
|
return false;
|
|
26539
26934
|
}
|
|
26935
|
+
function detectGlobalSecurityMiddleware(graph, calls) {
|
|
26936
|
+
for (const call of calls) {
|
|
26937
|
+
if (SECURITY_MIDDLEWARE_METHODS.has(call.method_name))
|
|
26938
|
+
return true;
|
|
26939
|
+
if (call.method_name === "use" && call.arguments.length > 0) {
|
|
26940
|
+
const firstArg = call.arguments[0].expression ?? "";
|
|
26941
|
+
if (/\b(helmet|Talisman|secure)\b/.test(firstArg))
|
|
26942
|
+
return true;
|
|
26943
|
+
}
|
|
26944
|
+
}
|
|
26945
|
+
for (const type of graph.ir.types) {
|
|
26946
|
+
if (type.annotations.some((a) => SECURITY_MIDDLEWARE_ANNOTATIONS_RE.test(a)))
|
|
26947
|
+
return true;
|
|
26948
|
+
for (const method of type.methods) {
|
|
26949
|
+
if (method.annotations.some((a) => SECURITY_MIDDLEWARE_ANNOTATIONS_RE.test(a)))
|
|
26950
|
+
return true;
|
|
26951
|
+
if (/^security[A-Za-z]*FilterChain$/i.test(method.name))
|
|
26952
|
+
return true;
|
|
26953
|
+
}
|
|
26954
|
+
}
|
|
26955
|
+
return false;
|
|
26956
|
+
}
|
|
26540
26957
|
function checkInheritedCorsHeaders(fileAnalyses, typeHierarchy, sourceLines) {
|
|
26541
26958
|
const findings = [];
|
|
26542
26959
|
for (const { file: parentFile, analysis: parentIR } of fileAnalyses) {
|
|
@@ -30023,7 +30440,7 @@ var colors = {
|
|
|
30023
30440
|
};
|
|
30024
30441
|
|
|
30025
30442
|
// src/version.ts
|
|
30026
|
-
var version = "3.
|
|
30443
|
+
var version = "3.58.0";
|
|
30027
30444
|
|
|
30028
30445
|
// src/formatters.ts
|
|
30029
30446
|
var SINK_SEVERITY = {
|
|
@@ -30797,7 +31214,8 @@ var TEST_PATTERNS = [
|
|
|
30797
31214
|
/_test\.py$/,
|
|
30798
31215
|
/_tests\.py$/,
|
|
30799
31216
|
/test_.*\.py$/,
|
|
30800
|
-
/_test\.rs
|
|
31217
|
+
/_test\.rs$/,
|
|
31218
|
+
/_test\.go$/
|
|
30801
31219
|
];
|
|
30802
31220
|
function isTestFile2(filePath) {
|
|
30803
31221
|
return TEST_PATTERNS.some((pattern) => pattern.test(filePath));
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "cognium-dev",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.58.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.58.0"
|
|
69
69
|
},
|
|
70
70
|
"devDependencies": {
|
|
71
71
|
"@types/node": "^25.5.0",
|