cognium-dev 3.48.0 → 3.49.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 +353 -26
- package/package.json +2 -2
package/dist/cli.js
CHANGED
|
@@ -4035,15 +4035,54 @@ function extractJSClassInfo(node) {
|
|
|
4035
4035
|
end_line: node.endPosition.row + 1
|
|
4036
4036
|
};
|
|
4037
4037
|
}
|
|
4038
|
+
function extractDecoratorName(node) {
|
|
4039
|
+
const child = node.namedChildCount > 0 ? node.namedChild(0) : null;
|
|
4040
|
+
if (!child)
|
|
4041
|
+
return null;
|
|
4042
|
+
if (child.type === "identifier")
|
|
4043
|
+
return getNodeText(child);
|
|
4044
|
+
if (child.type === "call_expression") {
|
|
4045
|
+
const fn = child.childForFieldName("function");
|
|
4046
|
+
if (fn) {
|
|
4047
|
+
if (fn.type === "identifier")
|
|
4048
|
+
return getNodeText(fn);
|
|
4049
|
+
if (fn.type === "member_expression") {
|
|
4050
|
+
const propNode = fn.childForFieldName("property");
|
|
4051
|
+
if (propNode)
|
|
4052
|
+
return getNodeText(propNode);
|
|
4053
|
+
}
|
|
4054
|
+
}
|
|
4055
|
+
}
|
|
4056
|
+
if (child.type === "member_expression") {
|
|
4057
|
+
const propNode = child.childForFieldName("property");
|
|
4058
|
+
if (propNode)
|
|
4059
|
+
return getNodeText(propNode);
|
|
4060
|
+
}
|
|
4061
|
+
return null;
|
|
4062
|
+
}
|
|
4038
4063
|
function extractJSMethods(body2) {
|
|
4039
4064
|
const methods = [];
|
|
4065
|
+
let pendingDecorators = [];
|
|
4040
4066
|
for (let i2 = 0;i2 < body2.childCount; i2++) {
|
|
4041
4067
|
const child = body2.child(i2);
|
|
4042
4068
|
if (!child)
|
|
4043
4069
|
continue;
|
|
4070
|
+
if (child.type === "decorator") {
|
|
4071
|
+
const name2 = extractDecoratorName(child);
|
|
4072
|
+
if (name2)
|
|
4073
|
+
pendingDecorators.push(name2);
|
|
4074
|
+
continue;
|
|
4075
|
+
}
|
|
4076
|
+
if (child.type === "comment")
|
|
4077
|
+
continue;
|
|
4044
4078
|
if (child.type === "method_definition") {
|
|
4045
|
-
|
|
4079
|
+
const m = extractJSMethodInfo(child);
|
|
4080
|
+
if (pendingDecorators.length > 0) {
|
|
4081
|
+
m.annotations = pendingDecorators;
|
|
4082
|
+
}
|
|
4083
|
+
methods.push(m);
|
|
4046
4084
|
}
|
|
4085
|
+
pendingDecorators = [];
|
|
4047
4086
|
}
|
|
4048
4087
|
return methods;
|
|
4049
4088
|
}
|
|
@@ -4214,10 +4253,19 @@ function extractJSParameters(params) {
|
|
|
4214
4253
|
if (typeNode) {
|
|
4215
4254
|
paramType = getNodeText(typeNode).replace(/^:\s*/, "");
|
|
4216
4255
|
}
|
|
4256
|
+
const decorators = [];
|
|
4257
|
+
for (let j = 0;j < child.childCount; j++) {
|
|
4258
|
+
const c = child.child(j);
|
|
4259
|
+
if (c && c.type === "decorator") {
|
|
4260
|
+
const name2 = extractDecoratorName(c);
|
|
4261
|
+
if (name2)
|
|
4262
|
+
decorators.push(name2);
|
|
4263
|
+
}
|
|
4264
|
+
}
|
|
4217
4265
|
parameters.push({
|
|
4218
4266
|
name: paramName,
|
|
4219
4267
|
type: paramType,
|
|
4220
|
-
annotations:
|
|
4268
|
+
annotations: decorators,
|
|
4221
4269
|
line: child.startPosition.row + 1
|
|
4222
4270
|
});
|
|
4223
4271
|
}
|
|
@@ -10798,6 +10846,18 @@ var DEFAULT_SINKS = [
|
|
|
10798
10846
|
{ method: "getExecutionPreamble", class: "Shell", type: "command_injection", cwe: "CWE-78", severity: "critical", arg_positions: [] },
|
|
10799
10847
|
{ method: "setQuotedArgumentsEnabled", class: "Shell", type: "command_injection", cwe: "CWE-78", severity: "high", arg_positions: [0] },
|
|
10800
10848
|
{ method: "onNewInstance", class: "SandboxInterceptor", type: "command_injection", cwe: "CWE-78", severity: "critical", arg_positions: [0] },
|
|
10849
|
+
{ method: "info", class: "Logger", type: "log_injection", cwe: "CWE-117", severity: "low", arg_positions: [0, 1, 2, 3], languages: ["java"] },
|
|
10850
|
+
{ method: "warn", class: "Logger", type: "log_injection", cwe: "CWE-117", severity: "low", arg_positions: [0, 1, 2, 3], languages: ["java"] },
|
|
10851
|
+
{ method: "error", class: "Logger", type: "log_injection", cwe: "CWE-117", severity: "low", arg_positions: [0, 1, 2, 3], languages: ["java"] },
|
|
10852
|
+
{ method: "debug", class: "Logger", type: "log_injection", cwe: "CWE-117", severity: "low", arg_positions: [0, 1, 2, 3], languages: ["java"] },
|
|
10853
|
+
{ method: "trace", class: "Logger", type: "log_injection", cwe: "CWE-117", severity: "low", arg_positions: [0, 1, 2, 3], languages: ["java"] },
|
|
10854
|
+
{ method: "severe", class: "Logger", type: "log_injection", cwe: "CWE-117", severity: "low", arg_positions: [0], languages: ["java"] },
|
|
10855
|
+
{ method: "warning", class: "Logger", type: "log_injection", cwe: "CWE-117", severity: "low", arg_positions: [0], languages: ["java"] },
|
|
10856
|
+
{ method: "config", class: "Logger", type: "log_injection", cwe: "CWE-117", severity: "low", arg_positions: [0], languages: ["java"] },
|
|
10857
|
+
{ method: "fine", class: "Logger", type: "log_injection", cwe: "CWE-117", severity: "low", arg_positions: [0], languages: ["java"] },
|
|
10858
|
+
{ method: "finer", class: "Logger", type: "log_injection", cwe: "CWE-117", severity: "low", arg_positions: [0], languages: ["java"] },
|
|
10859
|
+
{ method: "finest", class: "Logger", type: "log_injection", cwe: "CWE-117", severity: "low", arg_positions: [0], languages: ["java"] },
|
|
10860
|
+
{ method: "log", class: "Logger", type: "log_injection", cwe: "CWE-117", severity: "low", arg_positions: [1, 2, 3], languages: ["java"] },
|
|
10801
10861
|
{ method: "exec", class: "child_process", type: "command_injection", cwe: "CWE-78", severity: "critical", arg_positions: [0] },
|
|
10802
10862
|
{ method: "execSync", class: "child_process", type: "command_injection", cwe: "CWE-78", severity: "critical", arg_positions: [0] },
|
|
10803
10863
|
{ method: "spawn", class: "child_process", type: "command_injection", cwe: "CWE-78", severity: "critical", arg_positions: [0] },
|
|
@@ -10838,6 +10898,29 @@ var DEFAULT_SINKS = [
|
|
|
10838
10898
|
{ method: "updateMany", class: "Collection", type: "nosql_injection", cwe: "CWE-943", severity: "high", arg_positions: [0] },
|
|
10839
10899
|
{ method: "deleteOne", class: "Collection", type: "nosql_injection", cwe: "CWE-943", severity: "high", arg_positions: [0] },
|
|
10840
10900
|
{ method: "deleteMany", class: "Collection", type: "nosql_injection", cwe: "CWE-943", severity: "high", arg_positions: [0] },
|
|
10901
|
+
{ method: "find", class: "Model", type: "nosql_injection", cwe: "CWE-943", severity: "high", arg_positions: [0], languages: ["javascript", "typescript"] },
|
|
10902
|
+
{ method: "findOne", class: "Model", type: "nosql_injection", cwe: "CWE-943", severity: "high", arg_positions: [0], languages: ["javascript", "typescript"] },
|
|
10903
|
+
{ method: "findById", class: "Model", type: "nosql_injection", cwe: "CWE-943", severity: "high", arg_positions: [0], languages: ["javascript", "typescript"] },
|
|
10904
|
+
{ method: "findOneAndUpdate", class: "Model", type: "nosql_injection", cwe: "CWE-943", severity: "high", arg_positions: [0, 1], languages: ["javascript", "typescript"] },
|
|
10905
|
+
{ method: "findOneAndDelete", class: "Model", type: "nosql_injection", cwe: "CWE-943", severity: "high", arg_positions: [0], languages: ["javascript", "typescript"] },
|
|
10906
|
+
{ method: "findOneAndReplace", class: "Model", type: "nosql_injection", cwe: "CWE-943", severity: "high", arg_positions: [0, 1], languages: ["javascript", "typescript"] },
|
|
10907
|
+
{ method: "updateOne", class: "Model", type: "nosql_injection", cwe: "CWE-943", severity: "high", arg_positions: [0, 1], languages: ["javascript", "typescript"] },
|
|
10908
|
+
{ method: "updateMany", class: "Model", type: "nosql_injection", cwe: "CWE-943", severity: "high", arg_positions: [0, 1], languages: ["javascript", "typescript"] },
|
|
10909
|
+
{ method: "deleteOne", class: "Model", type: "nosql_injection", cwe: "CWE-943", severity: "high", arg_positions: [0], languages: ["javascript", "typescript"] },
|
|
10910
|
+
{ method: "deleteMany", class: "Model", type: "nosql_injection", cwe: "CWE-943", severity: "high", arg_positions: [0], languages: ["javascript", "typescript"] },
|
|
10911
|
+
{ method: "countDocuments", class: "Model", type: "nosql_injection", cwe: "CWE-943", severity: "high", arg_positions: [0], languages: ["javascript", "typescript"] },
|
|
10912
|
+
{ method: "aggregate", class: "Model", type: "nosql_injection", cwe: "CWE-943", severity: "high", arg_positions: [0], languages: ["javascript", "typescript"] },
|
|
10913
|
+
{ method: "where", class: "Query", type: "nosql_injection", cwe: "CWE-943", severity: "high", arg_positions: [0], languages: ["javascript", "typescript"] },
|
|
10914
|
+
{ method: "equals", class: "Query", type: "nosql_injection", cwe: "CWE-943", severity: "high", arg_positions: [0], languages: ["javascript", "typescript"] },
|
|
10915
|
+
{ method: "findOne", type: "nosql_injection", cwe: "CWE-943", severity: "high", arg_positions: [0], languages: ["javascript", "typescript"] },
|
|
10916
|
+
{ method: "findOneAndUpdate", type: "nosql_injection", cwe: "CWE-943", severity: "high", arg_positions: [0, 1], languages: ["javascript", "typescript"] },
|
|
10917
|
+
{ method: "findOneAndDelete", type: "nosql_injection", cwe: "CWE-943", severity: "high", arg_positions: [0], languages: ["javascript", "typescript"] },
|
|
10918
|
+
{ method: "findOneAndReplace", type: "nosql_injection", cwe: "CWE-943", severity: "high", arg_positions: [0, 1], languages: ["javascript", "typescript"] },
|
|
10919
|
+
{ method: "updateOne", type: "nosql_injection", cwe: "CWE-943", severity: "high", arg_positions: [0, 1], languages: ["javascript", "typescript"] },
|
|
10920
|
+
{ method: "updateMany", type: "nosql_injection", cwe: "CWE-943", severity: "high", arg_positions: [0, 1], languages: ["javascript", "typescript"] },
|
|
10921
|
+
{ method: "deleteOne", type: "nosql_injection", cwe: "CWE-943", severity: "high", arg_positions: [0], languages: ["javascript", "typescript"] },
|
|
10922
|
+
{ method: "deleteMany", type: "nosql_injection", cwe: "CWE-943", severity: "high", arg_positions: [0], languages: ["javascript", "typescript"] },
|
|
10923
|
+
{ method: "aggregate", type: "nosql_injection", cwe: "CWE-943", severity: "high", arg_positions: [0], languages: ["javascript", "typescript"] },
|
|
10841
10924
|
{ method: "get", class: "axios", type: "ssrf", cwe: "CWE-918", severity: "high", arg_positions: [0] },
|
|
10842
10925
|
{ method: "post", class: "axios", type: "ssrf", cwe: "CWE-918", severity: "high", arg_positions: [0] },
|
|
10843
10926
|
{ method: "request", class: "axios", type: "ssrf", cwe: "CWE-918", severity: "high", arg_positions: [0] },
|
|
@@ -10854,6 +10937,13 @@ var DEFAULT_SINKS = [
|
|
|
10854
10937
|
{ method: "get", class: "superagent", type: "ssrf", cwe: "CWE-918", severity: "high", arg_positions: [0] },
|
|
10855
10938
|
{ method: "post", class: "superagent", type: "ssrf", cwe: "CWE-918", severity: "high", arg_positions: [0] },
|
|
10856
10939
|
{ method: "default", class: "node-fetch", type: "ssrf", cwe: "CWE-918", severity: "high", arg_positions: [0] },
|
|
10940
|
+
{ method: "log", class: "console", type: "log_injection", cwe: "CWE-117", severity: "low", arg_positions: [0, 1, 2, 3], languages: ["javascript", "typescript"] },
|
|
10941
|
+
{ method: "warn", class: "console", type: "log_injection", cwe: "CWE-117", severity: "low", arg_positions: [0, 1, 2, 3], languages: ["javascript", "typescript"] },
|
|
10942
|
+
{ method: "error", class: "console", type: "log_injection", cwe: "CWE-117", severity: "low", arg_positions: [0, 1, 2, 3], languages: ["javascript", "typescript"] },
|
|
10943
|
+
{ method: "info", class: "console", type: "log_injection", cwe: "CWE-117", severity: "low", arg_positions: [0, 1, 2, 3], languages: ["javascript", "typescript"] },
|
|
10944
|
+
{ method: "debug", class: "console", type: "log_injection", cwe: "CWE-117", severity: "low", arg_positions: [0, 1, 2, 3], languages: ["javascript", "typescript"] },
|
|
10945
|
+
{ method: "trace", class: "console", type: "log_injection", cwe: "CWE-117", severity: "low", arg_positions: [0, 1, 2, 3], languages: ["javascript", "typescript"] },
|
|
10946
|
+
{ method: "redirect", type: "open_redirect", cwe: "CWE-601", severity: "medium", arg_positions: [0], languages: ["javascript", "typescript"] },
|
|
10857
10947
|
{ method: "system", class: "os", type: "command_injection", cwe: "CWE-78", severity: "critical", arg_positions: [0] },
|
|
10858
10948
|
{ method: "popen", class: "os", type: "command_injection", cwe: "CWE-78", severity: "critical", arg_positions: [0] },
|
|
10859
10949
|
{ method: "run", class: "subprocess", type: "command_injection", cwe: "CWE-78", severity: "critical", arg_positions: [0] },
|
|
@@ -10880,7 +10970,7 @@ var DEFAULT_SINKS = [
|
|
|
10880
10970
|
{ method: "rmdir", class: "os", type: "path_traversal", cwe: "CWE-22", severity: "high", arg_positions: [0] },
|
|
10881
10971
|
{ method: "rmtree", class: "shutil", type: "path_traversal", cwe: "CWE-22", severity: "critical", arg_positions: [0] },
|
|
10882
10972
|
{ method: "send_file", type: "path_traversal", cwe: "CWE-22", severity: "high", arg_positions: [0], languages: ["python"] },
|
|
10883
|
-
{ method: "render_template_string", type: "
|
|
10973
|
+
{ method: "render_template_string", type: "code_injection", cwe: "CWE-94", severity: "critical", arg_positions: [0], languages: ["python"] },
|
|
10884
10974
|
{ method: "Markup", type: "xss", cwe: "CWE-79", severity: "high", arg_positions: [0], languages: ["python"] },
|
|
10885
10975
|
{ method: "mark_safe", type: "xss", cwe: "CWE-79", severity: "high", arg_positions: [0], languages: ["python"] },
|
|
10886
10976
|
{ method: "get", class: "requests", type: "ssrf", cwe: "CWE-918", severity: "high", arg_positions: [0] },
|
|
@@ -11142,6 +11232,10 @@ var DEFAULT_SANITIZERS = [
|
|
|
11142
11232
|
{ method: "secure_filename", class: "werkzeug.utils", removes: ["path_traversal"] },
|
|
11143
11233
|
{ method: "basename", class: "os.path", removes: ["path_traversal"] },
|
|
11144
11234
|
{ method: "normpath", class: "os.path", removes: ["path_traversal"] },
|
|
11235
|
+
{ method: "realpath", class: "os.path", removes: ["path_traversal"] },
|
|
11236
|
+
{ method: "abspath", class: "os.path", removes: ["path_traversal"] },
|
|
11237
|
+
{ method: "realpath", class: "path", removes: ["path_traversal"] },
|
|
11238
|
+
{ method: "abspath", class: "path", removes: ["path_traversal"] },
|
|
11145
11239
|
{ method: "int", removes: ["sql_injection", "command_injection", "xss"] },
|
|
11146
11240
|
{ method: "float", removes: ["sql_injection", "command_injection"] },
|
|
11147
11241
|
{ method: "query!", removes: ["sql_injection"] },
|
|
@@ -11270,7 +11364,7 @@ var PYTHON_TAINTED_PATTERNS = [
|
|
|
11270
11364
|
function analyzeTaint(calls, types, config = getDefaultConfig(), typeHierarchy, language, code) {
|
|
11271
11365
|
const sourceLines = code !== undefined ? code.split(`
|
|
11272
11366
|
`) : undefined;
|
|
11273
|
-
const sources = findSources(calls, types, config.sources, sourceLines);
|
|
11367
|
+
const sources = findSources(calls, types, config.sources, sourceLines, language);
|
|
11274
11368
|
const sinks = findSinks(calls, config.sinks, typeHierarchy, language, sourceLines);
|
|
11275
11369
|
const sanitizers = findSanitizers(calls, types, config.sanitizers);
|
|
11276
11370
|
return { sources, sinks, sanitizers };
|
|
@@ -11289,7 +11383,7 @@ function attachSourceLineCode(sources, sinks, code) {
|
|
|
11289
11383
|
}
|
|
11290
11384
|
}
|
|
11291
11385
|
}
|
|
11292
|
-
function findSources(calls, types, patterns, sourceLines) {
|
|
11386
|
+
function findSources(calls, types, patterns, sourceLines, language) {
|
|
11293
11387
|
const sources = [];
|
|
11294
11388
|
for (const call of calls) {
|
|
11295
11389
|
for (const pattern of patterns) {
|
|
@@ -11344,23 +11438,31 @@ function findSources(calls, types, patterns, sourceLines) {
|
|
|
11344
11438
|
}
|
|
11345
11439
|
}
|
|
11346
11440
|
}
|
|
11347
|
-
const
|
|
11441
|
+
const RUST_EXTRACTOR_KIND = /(?:^|::)(Json|Form|Query|Path|Extension|Multipart|Body|Bytes)(?:<|$)/;
|
|
11348
11442
|
for (const type of types) {
|
|
11349
11443
|
for (const method of type.methods) {
|
|
11350
11444
|
for (const param of method.parameters) {
|
|
11351
|
-
if (param.type
|
|
11352
|
-
|
|
11353
|
-
|
|
11354
|
-
|
|
11355
|
-
|
|
11356
|
-
|
|
11357
|
-
|
|
11358
|
-
|
|
11359
|
-
|
|
11360
|
-
|
|
11361
|
-
|
|
11362
|
-
|
|
11363
|
-
|
|
11445
|
+
if (!param.type)
|
|
11446
|
+
continue;
|
|
11447
|
+
const kindMatch = RUST_EXTRACTOR_KIND.exec(param.type);
|
|
11448
|
+
if (!kindMatch)
|
|
11449
|
+
continue;
|
|
11450
|
+
const kind = kindMatch[1];
|
|
11451
|
+
if (kind === "Extension")
|
|
11452
|
+
continue;
|
|
11453
|
+
const sourceType = kind === "Form" || kind === "Query" || kind === "Path" ? "http_param" : "http_body";
|
|
11454
|
+
const paramLine = param.line ?? method.start_line;
|
|
11455
|
+
const alreadyExists = sources.some((s) => s.line === paramLine && s.variable === param.name);
|
|
11456
|
+
if (alreadyExists)
|
|
11457
|
+
continue;
|
|
11458
|
+
sources.push({
|
|
11459
|
+
type: sourceType,
|
|
11460
|
+
location: `${param.type} ${param.name} in ${method.name}`,
|
|
11461
|
+
severity: "high",
|
|
11462
|
+
line: paramLine,
|
|
11463
|
+
confidence: 1,
|
|
11464
|
+
variable: param.name
|
|
11465
|
+
});
|
|
11364
11466
|
}
|
|
11365
11467
|
}
|
|
11366
11468
|
}
|
|
@@ -11442,6 +11544,17 @@ function findSources(calls, types, patterns, sourceLines) {
|
|
|
11442
11544
|
s.code = sourceLines[s.line - 1]?.trim();
|
|
11443
11545
|
}
|
|
11444
11546
|
}
|
|
11547
|
+
if (language === "rust" && sourceLines) {
|
|
11548
|
+
const LET_BINDING = /^\s*let\s+(?:mut\s+)?([A-Za-z_]\w*)\s*(?::\s*[^=]+)?=/;
|
|
11549
|
+
for (const s of result) {
|
|
11550
|
+
if (s.variable && s.variable.length > 0)
|
|
11551
|
+
continue;
|
|
11552
|
+
const lineText = sourceLines[s.line - 1] ?? "";
|
|
11553
|
+
const m = LET_BINDING.exec(lineText);
|
|
11554
|
+
if (m)
|
|
11555
|
+
s.variable = m[1];
|
|
11556
|
+
}
|
|
11557
|
+
}
|
|
11445
11558
|
return result;
|
|
11446
11559
|
}
|
|
11447
11560
|
function isInterproceduralTaintableType(typeName) {
|
|
@@ -11538,6 +11651,26 @@ function isParameterizedQueryCall(call, pattern) {
|
|
|
11538
11651
|
}
|
|
11539
11652
|
return false;
|
|
11540
11653
|
}
|
|
11654
|
+
function isSafePythonSubprocessCall(call, pattern, language) {
|
|
11655
|
+
if (language !== "python")
|
|
11656
|
+
return false;
|
|
11657
|
+
if (pattern.type !== "command_injection")
|
|
11658
|
+
return false;
|
|
11659
|
+
if (pattern.class !== "subprocess")
|
|
11660
|
+
return false;
|
|
11661
|
+
const arg0 = call.arguments.find((a) => a.position === 0);
|
|
11662
|
+
if (!arg0)
|
|
11663
|
+
return false;
|
|
11664
|
+
const expr0 = (arg0.literal ?? arg0.expression ?? "").trim();
|
|
11665
|
+
if (!expr0.startsWith("["))
|
|
11666
|
+
return false;
|
|
11667
|
+
for (const a of call.arguments) {
|
|
11668
|
+
const e = (a.expression ?? "").trim();
|
|
11669
|
+
if (/^shell\s*=\s*True\b/.test(e))
|
|
11670
|
+
return false;
|
|
11671
|
+
}
|
|
11672
|
+
return true;
|
|
11673
|
+
}
|
|
11541
11674
|
var CLASS_LITERAL_RE = /^(?:[A-Za-z_][\w]*\.)*[A-Z][\w]*(?:\[\])*\.class$/;
|
|
11542
11675
|
function argIsClassLiteral(call, position) {
|
|
11543
11676
|
const arg = call.arguments.find((a) => a.position === position);
|
|
@@ -11556,6 +11689,9 @@ function findSinks(calls, patterns, typeHierarchy, language, sourceLines) {
|
|
|
11556
11689
|
if (isParameterizedQueryCall(call, pattern)) {
|
|
11557
11690
|
continue;
|
|
11558
11691
|
}
|
|
11692
|
+
if (isSafePythonSubprocessCall(call, pattern, language)) {
|
|
11693
|
+
continue;
|
|
11694
|
+
}
|
|
11559
11695
|
if (pattern.safe_if_class_literal_at !== undefined && argIsClassLiteral(call, pattern.safe_if_class_literal_at)) {
|
|
11560
11696
|
continue;
|
|
11561
11697
|
}
|
|
@@ -11950,7 +12086,8 @@ function receiverMightBeClass(receiver, className) {
|
|
|
11950
12086
|
"controller",
|
|
11951
12087
|
"task",
|
|
11952
12088
|
"thread",
|
|
11953
|
-
"job"
|
|
12089
|
+
"job",
|
|
12090
|
+
"cur"
|
|
11954
12091
|
]);
|
|
11955
12092
|
const isAmbiguous = ambiguousIdentifiers.has(lowerReceiver);
|
|
11956
12093
|
if (!isAmbiguous && lowerReceiver.length >= 3 && lowerClass.includes(lowerReceiver)) {
|
|
@@ -11960,7 +12097,9 @@ function receiverMightBeClass(receiver, className) {
|
|
|
11960
12097
|
}
|
|
11961
12098
|
if (!isAmbiguous && lowerReceiver.length >= 2) {
|
|
11962
12099
|
if (lowerClass.startsWith(lowerReceiver) || lowerClass.endsWith(lowerReceiver)) {
|
|
11963
|
-
|
|
12100
|
+
if (lowerReceiver.length / lowerClass.length >= 0.4) {
|
|
12101
|
+
return true;
|
|
12102
|
+
}
|
|
11964
12103
|
}
|
|
11965
12104
|
}
|
|
11966
12105
|
if (!isAmbiguous && lowerReceiver.length >= 3) {
|
|
@@ -11981,6 +12120,8 @@ function receiverMightBeClass(receiver, className) {
|
|
|
11981
12120
|
ps: ["PreparedStatement"],
|
|
11982
12121
|
rs: ["ResultSet"],
|
|
11983
12122
|
template: ["JdbcTemplate"],
|
|
12123
|
+
cur: ["Cursor"],
|
|
12124
|
+
cursor: ["Cursor"],
|
|
11984
12125
|
writer: ["PrintWriter"],
|
|
11985
12126
|
out: ["PrintWriter", "OutputStream"],
|
|
11986
12127
|
reader: ["BufferedReader"],
|
|
@@ -14468,7 +14609,7 @@ function isFalsePositive(result, sinkLine, taintedVar) {
|
|
|
14468
14609
|
if (varValue && varValue.type !== "unknown" && !result.tainted.has(taintedVar)) {
|
|
14469
14610
|
return { isFalsePositive: true, reason: `variable_is_constant: ${varValue.value}` };
|
|
14470
14611
|
}
|
|
14471
|
-
if (result.symbols.
|
|
14612
|
+
if (result.symbols.has(taintedVar) && !result.tainted.has(taintedVar)) {
|
|
14472
14613
|
return { isFalsePositive: true, reason: "variable_not_tainted" };
|
|
14473
14614
|
}
|
|
14474
14615
|
return { isFalsePositive: false, reason: null };
|
|
@@ -20540,6 +20681,40 @@ function buildJavaScriptTaintedVars(sourceCode, language) {
|
|
|
20540
20681
|
}
|
|
20541
20682
|
return tainted;
|
|
20542
20683
|
}
|
|
20684
|
+
function buildRustTaintedVars(sourceCode, seedVars) {
|
|
20685
|
+
const derived = new Map;
|
|
20686
|
+
const knownTainted = new Set(seedVars);
|
|
20687
|
+
const lines = sourceCode.split(`
|
|
20688
|
+
`);
|
|
20689
|
+
let changed = true;
|
|
20690
|
+
while (changed) {
|
|
20691
|
+
changed = false;
|
|
20692
|
+
for (let i2 = 0;i2 < lines.length; i2++) {
|
|
20693
|
+
const line = lines[i2];
|
|
20694
|
+
const trimmed = line.trimStart();
|
|
20695
|
+
if (trimmed.startsWith("//"))
|
|
20696
|
+
continue;
|
|
20697
|
+
const letMatch = line.match(/^\s*let\s+(?:mut\s+)?([A-Za-z_]\w*)\s*(?::\s*[^=]+)?=\s*(.+?)(?:;|$)/);
|
|
20698
|
+
const assignMatch = !letMatch ? line.match(/^\s*([A-Za-z_]\w*)\s*=\s*(.+?)(?:;|$)/) : null;
|
|
20699
|
+
const m = letMatch ?? assignMatch;
|
|
20700
|
+
if (!m)
|
|
20701
|
+
continue;
|
|
20702
|
+
const lhs = m[1];
|
|
20703
|
+
const rhs = m[2];
|
|
20704
|
+
if (lhs === "if" || lhs === "while" || lhs === "for" || lhs === "match" || lhs === "return")
|
|
20705
|
+
continue;
|
|
20706
|
+
if (knownTainted.has(lhs))
|
|
20707
|
+
continue;
|
|
20708
|
+
const ref = [...knownTainted].some((v) => new RegExp(`\\b${v}\\b`).test(rhs));
|
|
20709
|
+
if (ref) {
|
|
20710
|
+
derived.set(lhs, i2 + 1);
|
|
20711
|
+
knownTainted.add(lhs);
|
|
20712
|
+
changed = true;
|
|
20713
|
+
}
|
|
20714
|
+
}
|
|
20715
|
+
}
|
|
20716
|
+
return derived;
|
|
20717
|
+
}
|
|
20543
20718
|
var BASH_POSITIONAL_PARAMS = new Set(["1", "2", "3", "4", "5", "6", "7", "8", "9", "@", "*"]);
|
|
20544
20719
|
var BASH_UNTRUSTED_ENV_PATTERNS = [
|
|
20545
20720
|
/^USER_INPUT$/i,
|
|
@@ -20962,7 +21137,23 @@ function evaluateSimpleExpression(expr, symbols) {
|
|
|
20962
21137
|
}
|
|
20963
21138
|
function isStringLiteralExpression(expr) {
|
|
20964
21139
|
const trimmed = expr.trim();
|
|
20965
|
-
|
|
21140
|
+
if (trimmed.length < 2)
|
|
21141
|
+
return false;
|
|
21142
|
+
const quote = trimmed[0];
|
|
21143
|
+
if (quote !== '"' && quote !== "'")
|
|
21144
|
+
return false;
|
|
21145
|
+
let i2 = 1;
|
|
21146
|
+
while (i2 < trimmed.length) {
|
|
21147
|
+
const c = trimmed[i2];
|
|
21148
|
+
if (c === "\\") {
|
|
21149
|
+
i2 += 2;
|
|
21150
|
+
continue;
|
|
21151
|
+
}
|
|
21152
|
+
if (c === quote)
|
|
21153
|
+
return i2 === trimmed.length - 1;
|
|
21154
|
+
i2++;
|
|
21155
|
+
}
|
|
21156
|
+
return false;
|
|
20966
21157
|
}
|
|
20967
21158
|
function filterCleanArraySinks(sinks, calls, taintedArrayElements, symbols) {
|
|
20968
21159
|
const callsByLine = new Map;
|
|
@@ -21393,7 +21584,7 @@ class TaintPropagationPass {
|
|
|
21393
21584
|
flows.push(f);
|
|
21394
21585
|
}
|
|
21395
21586
|
}
|
|
21396
|
-
const exprScanFlows = detectExpressionScanFlows(calls, sources, sinks, constProp.unreachableLines, ctx.code, ctx.language) ?? [];
|
|
21587
|
+
const exprScanFlows = detectExpressionScanFlows(calls, sources, sinks, sanitizers, constProp.unreachableLines, ctx.code, ctx.language) ?? [];
|
|
21397
21588
|
for (const f of exprScanFlows) {
|
|
21398
21589
|
if (flows.some((x) => x.source_line === f.source_line && x.sink_line === f.sink_line && x.sink_type === f.sink_type))
|
|
21399
21590
|
continue;
|
|
@@ -21611,13 +21802,70 @@ function detectParameterSinkFlows(types, calls, sources, sinks, unreachableLines
|
|
|
21611
21802
|
}
|
|
21612
21803
|
return flows;
|
|
21613
21804
|
}
|
|
21614
|
-
function detectExpressionScanFlows(calls, sources, sinks, unreachableLines, code, language) {
|
|
21805
|
+
function detectExpressionScanFlows(calls, sources, sinks, sanitizers, unreachableLines, code, language) {
|
|
21615
21806
|
const flows = [];
|
|
21616
21807
|
const sourcesWithVar = sources.filter((s) => typeof s.variable === "string" && s.variable.length > 0);
|
|
21617
21808
|
if (sourcesWithVar.length === 0)
|
|
21618
21809
|
return flows;
|
|
21810
|
+
const aliasSanitizedFor = new Map;
|
|
21619
21811
|
if (language === "python" && typeof code === "string") {
|
|
21620
21812
|
const derived = buildPythonTaintedVars(code);
|
|
21813
|
+
if (derived.size > 0) {
|
|
21814
|
+
let anchor = sourcesWithVar[0];
|
|
21815
|
+
for (const s of sourcesWithVar) {
|
|
21816
|
+
if (s.line < anchor.line)
|
|
21817
|
+
anchor = s;
|
|
21818
|
+
}
|
|
21819
|
+
const existingVars = new Set(sourcesWithVar.map((s) => s.variable));
|
|
21820
|
+
for (const [varName] of derived) {
|
|
21821
|
+
if (!varName || existingVars.has(varName))
|
|
21822
|
+
continue;
|
|
21823
|
+
sourcesWithVar.push({
|
|
21824
|
+
...anchor,
|
|
21825
|
+
variable: varName
|
|
21826
|
+
});
|
|
21827
|
+
existingVars.add(varName);
|
|
21828
|
+
}
|
|
21829
|
+
if (sanitizers && sanitizers.length > 0) {
|
|
21830
|
+
const sanitizersByLine = new Map;
|
|
21831
|
+
for (const s of sanitizers) {
|
|
21832
|
+
const arr = sanitizersByLine.get(s.line) ?? [];
|
|
21833
|
+
arr.push(s);
|
|
21834
|
+
sanitizersByLine.set(s.line, arr);
|
|
21835
|
+
}
|
|
21836
|
+
const codeLines = code.split(`
|
|
21837
|
+
`);
|
|
21838
|
+
for (const [varName, originLine] of derived) {
|
|
21839
|
+
const lineSans = sanitizersByLine.get(originLine);
|
|
21840
|
+
if (!lineSans || lineSans.length === 0)
|
|
21841
|
+
continue;
|
|
21842
|
+
const lineText = codeLines[originLine - 1] ?? "";
|
|
21843
|
+
const rhsMatch = lineText.match(/^\s*\w+\s*=\s*(.+)$/);
|
|
21844
|
+
if (!rhsMatch)
|
|
21845
|
+
continue;
|
|
21846
|
+
const rhs = rhsMatch[1];
|
|
21847
|
+
for (const san of lineSans) {
|
|
21848
|
+
const sanMatch = san.method.match(/^(?:(\w+)\.)?(\w+)\(\)$/);
|
|
21849
|
+
if (!sanMatch)
|
|
21850
|
+
continue;
|
|
21851
|
+
const sanName = sanMatch[1] ? `${sanMatch[1]}.${sanMatch[2]}` : sanMatch[2];
|
|
21852
|
+
if (!rhs.includes(`${sanName}(`))
|
|
21853
|
+
continue;
|
|
21854
|
+
let set = aliasSanitizedFor.get(varName);
|
|
21855
|
+
if (!set) {
|
|
21856
|
+
set = new Set;
|
|
21857
|
+
aliasSanitizedFor.set(varName, set);
|
|
21858
|
+
}
|
|
21859
|
+
for (const t of san.sanitizes)
|
|
21860
|
+
set.add(t);
|
|
21861
|
+
}
|
|
21862
|
+
}
|
|
21863
|
+
}
|
|
21864
|
+
}
|
|
21865
|
+
}
|
|
21866
|
+
if (language === "rust" && typeof code === "string") {
|
|
21867
|
+
const seedVars = new Set(sourcesWithVar.map((s) => s.variable));
|
|
21868
|
+
const derived = buildRustTaintedVars(code, seedVars);
|
|
21621
21869
|
if (derived.size > 0) {
|
|
21622
21870
|
let anchor = sourcesWithVar[0];
|
|
21623
21871
|
for (const s of sourcesWithVar) {
|
|
@@ -21669,6 +21917,9 @@ function detectExpressionScanFlows(calls, sources, sinks, unreachableLines, code
|
|
|
21669
21917
|
continue;
|
|
21670
21918
|
if (flows.some((f) => f.source_line === source.line && f.sink_line === sink.line && f.sink_type === sink.type))
|
|
21671
21919
|
continue;
|
|
21920
|
+
if (aliasSanitizedFor.get(source.variable)?.has(sink.type)) {
|
|
21921
|
+
break;
|
|
21922
|
+
}
|
|
21672
21923
|
flows.push({
|
|
21673
21924
|
source_line: source.line,
|
|
21674
21925
|
sink_line: sink.line,
|
|
@@ -22242,6 +22493,9 @@ class InterproceduralPass {
|
|
|
22242
22493
|
}
|
|
22243
22494
|
}
|
|
22244
22495
|
}
|
|
22496
|
+
if (additionalSinks.length > 0) {
|
|
22497
|
+
attachSourceLineCode([], additionalSinks, ctx.code);
|
|
22498
|
+
}
|
|
22245
22499
|
return { additionalSinks, additionalFlows, interprocedural };
|
|
22246
22500
|
}
|
|
22247
22501
|
}
|
|
@@ -26781,6 +27035,77 @@ function isPotentialPojo(type) {
|
|
|
26781
27035
|
return first >= 65 && first <= 90;
|
|
26782
27036
|
}
|
|
26783
27037
|
|
|
27038
|
+
// ../circle-ir/dist/analysis/passes/insecure-cookie-pass.js
|
|
27039
|
+
var COOKIE_RESPONSE_RECEIVERS = new Set([
|
|
27040
|
+
"res",
|
|
27041
|
+
"response",
|
|
27042
|
+
"reply"
|
|
27043
|
+
]);
|
|
27044
|
+
var SECURE_TRUE_RE = /\bsecure\s*:\s*true\b/;
|
|
27045
|
+
var HTTPONLY_TRUE_RE = /\bhttpOnly\s*:\s*true\b/i;
|
|
27046
|
+
|
|
27047
|
+
class InsecureCookiePass {
|
|
27048
|
+
name = "insecure-cookie";
|
|
27049
|
+
category = "security";
|
|
27050
|
+
run(ctx) {
|
|
27051
|
+
const { graph, language } = ctx;
|
|
27052
|
+
if (language !== "javascript" && language !== "typescript") {
|
|
27053
|
+
return { insecureCookies: [] };
|
|
27054
|
+
}
|
|
27055
|
+
const file = graph.ir.meta.file;
|
|
27056
|
+
const insecureCookies = [];
|
|
27057
|
+
for (const call of graph.ir.calls) {
|
|
27058
|
+
if (call.method_name !== "cookie")
|
|
27059
|
+
continue;
|
|
27060
|
+
const receiver = call.receiver ?? "";
|
|
27061
|
+
if (!COOKIE_RESPONSE_RECEIVERS.has(receiver))
|
|
27062
|
+
continue;
|
|
27063
|
+
if (call.arguments.length < 2)
|
|
27064
|
+
continue;
|
|
27065
|
+
const opts = call.arguments.find((a) => a.position === 2);
|
|
27066
|
+
const optsExpr = (opts?.expression ?? "").trim();
|
|
27067
|
+
const optionsPresent = optsExpr.length > 0;
|
|
27068
|
+
const missingSecure = !SECURE_TRUE_RE.test(optsExpr);
|
|
27069
|
+
const missingHttpOnly = !HTTPONLY_TRUE_RE.test(optsExpr);
|
|
27070
|
+
if (!missingSecure && !missingHttpOnly)
|
|
27071
|
+
continue;
|
|
27072
|
+
const line = call.location.line;
|
|
27073
|
+
insecureCookies.push({
|
|
27074
|
+
line,
|
|
27075
|
+
receiver,
|
|
27076
|
+
missingSecure,
|
|
27077
|
+
missingHttpOnly,
|
|
27078
|
+
optionsPresent
|
|
27079
|
+
});
|
|
27080
|
+
const missing = [];
|
|
27081
|
+
if (missingSecure)
|
|
27082
|
+
missing.push("`secure: true`");
|
|
27083
|
+
if (missingHttpOnly)
|
|
27084
|
+
missing.push("`httpOnly: true`");
|
|
27085
|
+
ctx.addFinding({
|
|
27086
|
+
id: `${this.name}-${file}-${line}`,
|
|
27087
|
+
pass: this.name,
|
|
27088
|
+
category: this.category,
|
|
27089
|
+
rule_id: this.name,
|
|
27090
|
+
cwe: "CWE-614",
|
|
27091
|
+
severity: "medium",
|
|
27092
|
+
level: "warning",
|
|
27093
|
+
message: `Cookie set without ${missing.join(" and ")} — vulnerable to ` + `cleartext transmission (CWE-614) and client-side JS access ` + `(CWE-1004).`,
|
|
27094
|
+
file,
|
|
27095
|
+
line,
|
|
27096
|
+
fix: 'Pass `{ secure: true, httpOnly: true, sameSite: "lax" }` as the ' + "third argument to `res.cookie()`.",
|
|
27097
|
+
evidence: {
|
|
27098
|
+
receiver,
|
|
27099
|
+
options_present: optionsPresent,
|
|
27100
|
+
missing_secure: missingSecure,
|
|
27101
|
+
missing_http_only: missingHttpOnly
|
|
27102
|
+
}
|
|
27103
|
+
});
|
|
27104
|
+
}
|
|
27105
|
+
return { insecureCookies };
|
|
27106
|
+
}
|
|
27107
|
+
}
|
|
27108
|
+
|
|
26784
27109
|
// ../circle-ir/dist/graph/import-graph.js
|
|
26785
27110
|
function dirname(filePath) {
|
|
26786
27111
|
const idx = filePath.lastIndexOf("/");
|
|
@@ -27891,6 +28216,8 @@ async function analyze(code, filePath, language, options = {}) {
|
|
|
27891
28216
|
pipeline.add(new SecurityHeadersPass(passOpts.securityHeaders));
|
|
27892
28217
|
if (!disabledPasses.has("spring4shell"))
|
|
27893
28218
|
pipeline.add(new Spring4ShellPass);
|
|
28219
|
+
if (!disabledPasses.has("insecure-cookie"))
|
|
28220
|
+
pipeline.add(new InsecureCookiePass);
|
|
27894
28221
|
const { results, findings } = pipeline.run(graph, code, language, config);
|
|
27895
28222
|
const sinkFilter = results.get("sink-filter");
|
|
27896
28223
|
const interProc = results.get("interprocedural");
|
|
@@ -28084,7 +28411,7 @@ var colors = {
|
|
|
28084
28411
|
};
|
|
28085
28412
|
|
|
28086
28413
|
// src/version.ts
|
|
28087
|
-
var version = "3.
|
|
28414
|
+
var version = "3.49.0";
|
|
28088
28415
|
|
|
28089
28416
|
// src/formatters.ts
|
|
28090
28417
|
var SINK_SEVERITY = {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "cognium-dev",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.49.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.49.0"
|
|
69
69
|
},
|
|
70
70
|
"devDependencies": {
|
|
71
71
|
"@types/node": "^25.5.0",
|