cognium-dev 3.81.0 → 3.83.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 +319 -10
- package/package.json +2 -2
package/dist/cli.js
CHANGED
|
@@ -10517,10 +10517,6 @@ var DEFAULT_SINKS = [
|
|
|
10517
10517
|
{ method: "setCommandline", class: "DefaultExecutor", type: "command_injection", cwe: "CWE-78", severity: "critical", arg_positions: [0] },
|
|
10518
10518
|
{ method: "parse", class: "CommandLine", type: "command_injection", cwe: "CWE-78", severity: "critical", arg_positions: [0] },
|
|
10519
10519
|
{ method: "addArgument", class: "CommandLine", type: "command_injection", cwe: "CWE-78", severity: "critical", arg_positions: [0] },
|
|
10520
|
-
{ method: "waitFor", class: "Process", type: "command_injection", cwe: "CWE-78", severity: "medium", arg_positions: [] },
|
|
10521
|
-
{ method: "inheritIO", class: "ProcessBuilder", type: "command_injection", cwe: "CWE-78", severity: "medium", arg_positions: [] },
|
|
10522
|
-
{ method: "redirectOutput", class: "ProcessBuilder", type: "command_injection", cwe: "CWE-78", severity: "medium", arg_positions: [0] },
|
|
10523
|
-
{ method: "redirectInput", class: "ProcessBuilder", type: "command_injection", cwe: "CWE-78", severity: "medium", arg_positions: [0] },
|
|
10524
10520
|
{ method: "File", class: "constructor", type: "path_traversal", cwe: "CWE-22", severity: "high", arg_positions: [0, 1] },
|
|
10525
10521
|
{ method: "FileInputStream", class: "constructor", type: "path_traversal", cwe: "CWE-22", severity: "high", arg_positions: [0] },
|
|
10526
10522
|
{ method: "FileOutputStream", class: "constructor", type: "path_traversal", cwe: "CWE-22", severity: "high", arg_positions: [0] },
|
|
@@ -10764,7 +10760,6 @@ var DEFAULT_SINKS = [
|
|
|
10764
10760
|
{ method: "entity", class: "ResponseBuilder", type: "xss", cwe: "CWE-79", severity: "high", arg_positions: [0] },
|
|
10765
10761
|
{ method: "ok", class: "Response", type: "xss", cwe: "CWE-79", severity: "high", arg_positions: [0] },
|
|
10766
10762
|
{ method: "eval", class: "ScriptEngine", type: "code_injection", cwe: "CWE-94", severity: "critical", arg_positions: [0] },
|
|
10767
|
-
{ method: "compile", class: "Pattern", type: "code_injection", cwe: "CWE-94", severity: "high", arg_positions: [0] },
|
|
10768
10763
|
{ method: "parseExpression", class: "ExpressionParser", type: "code_injection", cwe: "CWE-94", severity: "critical", arg_positions: [0] },
|
|
10769
10764
|
{ method: "parseExpression", class: "SpelExpressionParser", type: "code_injection", cwe: "CWE-94", severity: "critical", arg_positions: [0] },
|
|
10770
10765
|
{ method: "getValue", class: "Expression", type: "code_injection", cwe: "CWE-94", severity: "critical", arg_positions: [] },
|
|
@@ -11239,7 +11234,6 @@ var DEFAULT_SINKS = [
|
|
|
11239
11234
|
{ method: "log", class: "logging", type: "log_injection", cwe: "CWE-117", severity: "low", arg_positions: [1] },
|
|
11240
11235
|
{ method: "exception", class: "logging", type: "log_injection", cwe: "CWE-117", severity: "low", arg_positions: [0] },
|
|
11241
11236
|
{ method: "command", class: "ProcessBuilder", type: "command_injection", cwe: "CWE-78", severity: "critical", arg_positions: [0] },
|
|
11242
|
-
{ method: "inheritIO", class: "ProcessBuilder", type: "command_injection", cwe: "CWE-78", severity: "critical", arg_positions: [] },
|
|
11243
11237
|
{ method: "step", class: "StepExecution", type: "command_injection", cwe: "CWE-78", severity: "critical", arg_positions: [0] },
|
|
11244
11238
|
{ method: "invokeMethod", class: "Script", type: "command_injection", cwe: "CWE-78", severity: "critical", arg_positions: [0, 1] },
|
|
11245
11239
|
{ method: "evaluate", class: "Script", type: "code_injection", cwe: "CWE-94", severity: "critical", arg_positions: [0] },
|
|
@@ -11478,9 +11472,15 @@ var DEFAULT_SANITIZERS = [
|
|
|
11478
11472
|
{ method: "sanitize", class: "DOMPurify", removes: ["xss"] },
|
|
11479
11473
|
{ method: "escape", class: "validator", removes: ["xss"] },
|
|
11480
11474
|
{ method: "parse", class: "JSON", removes: ["xss", "code_injection"] },
|
|
11481
|
-
{ method: "parseInt", removes: ["sql_injection", "nosql_injection", "command_injection", "xss"] },
|
|
11482
|
-
{ method: "parseFloat", removes: ["sql_injection", "nosql_injection", "command_injection"] },
|
|
11483
|
-
{ method: "Number", removes: ["sql_injection", "nosql_injection", "command_injection"] },
|
|
11475
|
+
{ method: "parseInt", removes: ["sql_injection", "nosql_injection", "command_injection", "xss", "external_taint_escape", "path_traversal", "code_injection"] },
|
|
11476
|
+
{ method: "parseFloat", removes: ["sql_injection", "nosql_injection", "command_injection", "external_taint_escape", "path_traversal", "code_injection"] },
|
|
11477
|
+
{ method: "Number", removes: ["sql_injection", "nosql_injection", "command_injection", "external_taint_escape", "path_traversal", "code_injection"] },
|
|
11478
|
+
{ method: "min", class: "Math", removes: ["external_taint_escape"] },
|
|
11479
|
+
{ method: "max", class: "Math", removes: ["external_taint_escape"] },
|
|
11480
|
+
{ method: "includes", removes: ["external_taint_escape"] },
|
|
11481
|
+
{ method: "has", removes: ["external_taint_escape"] },
|
|
11482
|
+
{ method: "contains", removes: ["external_taint_escape"] },
|
|
11483
|
+
{ method: "indexOf", removes: ["external_taint_escape"] },
|
|
11484
11484
|
{ method: "basename", class: "path", removes: ["path_traversal"] },
|
|
11485
11485
|
{ method: "normalize", class: "path", removes: ["path_traversal"] },
|
|
11486
11486
|
{ method: "resolve", class: "path", removes: ["path_traversal"] },
|
|
@@ -30522,6 +30522,311 @@ class WeakPasswordEncodingPass {
|
|
|
30522
30522
|
}
|
|
30523
30523
|
}
|
|
30524
30524
|
|
|
30525
|
+
// ../circle-ir/dist/analysis/passes/info-disclosure-stacktrace-pass.js
|
|
30526
|
+
var RESPONSE_RECEIVER_RE = /^(res|response|w|writer|ctx|c)$/i;
|
|
30527
|
+
var LOGGER_RECEIVER_RE = /^(log|logger|slog|console|pino|winston|sentry)$/i;
|
|
30528
|
+
var RESPONSE_SEND_METHODS = new Set([
|
|
30529
|
+
"send",
|
|
30530
|
+
"json",
|
|
30531
|
+
"write",
|
|
30532
|
+
"writeHead",
|
|
30533
|
+
"end",
|
|
30534
|
+
"sendFile",
|
|
30535
|
+
"println",
|
|
30536
|
+
"print",
|
|
30537
|
+
"getWriter",
|
|
30538
|
+
"Fprintln",
|
|
30539
|
+
"Fprintf",
|
|
30540
|
+
"Fprint"
|
|
30541
|
+
]);
|
|
30542
|
+
function isExceptionExpression(expr) {
|
|
30543
|
+
if (!expr)
|
|
30544
|
+
return false;
|
|
30545
|
+
const e = expr.trim();
|
|
30546
|
+
return /\b(err|error|exc|exception|e|t|throwable)\.(stack|message|toString\(|getMessage\(|getStackTrace\(|getLocalizedMessage\(|getCause\()/i.test(e) || /\btraceback\.(format_exc|format_exception|print_exc)\b/i.test(e) || /\bdebug\.Stack\(\)/.test(e) || /\bstr\(\s*(err|error|exc|exception|e)\s*\)/i.test(e) || /\bString\(\s*(err|error|exc|exception|e)\s*\)/i.test(e);
|
|
30547
|
+
}
|
|
30548
|
+
function argIsException(arg) {
|
|
30549
|
+
if (!arg)
|
|
30550
|
+
return false;
|
|
30551
|
+
if (arg.variable && /^(err|error|exc|exception|e|t|throwable)$/i.test(arg.variable)) {
|
|
30552
|
+
return true;
|
|
30553
|
+
}
|
|
30554
|
+
return isExceptionExpression(arg.expression);
|
|
30555
|
+
}
|
|
30556
|
+
function detectJavaPrintStackTrace(call) {
|
|
30557
|
+
if (call.method_name !== "printStackTrace")
|
|
30558
|
+
return null;
|
|
30559
|
+
const rec = call.receiver ?? "";
|
|
30560
|
+
if (!/^(e|ex|exc|exception|err|error|t|throwable)$/i.test(rec))
|
|
30561
|
+
return null;
|
|
30562
|
+
const arg0 = call.arguments.find((a) => a.position === 0);
|
|
30563
|
+
if (!arg0)
|
|
30564
|
+
return null;
|
|
30565
|
+
const expr = (arg0.expression ?? arg0.variable ?? "").trim();
|
|
30566
|
+
if (/\bresponse\.getWriter\(\)/.test(expr) || /\bresp\.getWriter\(\)/.test(expr) || /\bout\b/.test(expr) || /\bgetWriter\(\)/.test(expr)) {
|
|
30567
|
+
return "e.printStackTrace(response.getWriter())";
|
|
30568
|
+
}
|
|
30569
|
+
return null;
|
|
30570
|
+
}
|
|
30571
|
+
function detectResponseLeakCall(call) {
|
|
30572
|
+
const method = call.method_name ?? "";
|
|
30573
|
+
const receiver = call.receiver ?? "";
|
|
30574
|
+
if (!RESPONSE_SEND_METHODS.has(method))
|
|
30575
|
+
return null;
|
|
30576
|
+
if (LOGGER_RECEIVER_RE.test(receiver))
|
|
30577
|
+
return null;
|
|
30578
|
+
const recTail = receiver.split(".").pop() ?? receiver;
|
|
30579
|
+
const recHead = receiver.split(".")[0] ?? receiver;
|
|
30580
|
+
if (!RESPONSE_RECEIVER_RE.test(recTail) && !RESPONSE_RECEIVER_RE.test(recHead)) {
|
|
30581
|
+
if (!/(?:^|[.\s])(res|response)\.(?:status|set|header|cookie)\b/i.test(receiver)) {
|
|
30582
|
+
return null;
|
|
30583
|
+
}
|
|
30584
|
+
}
|
|
30585
|
+
for (const a of call.arguments) {
|
|
30586
|
+
if (argIsException(a)) {
|
|
30587
|
+
return `${receiver || ""}${receiver ? "." : ""}${method}(${(a.expression ?? a.variable ?? "").trim()})`;
|
|
30588
|
+
}
|
|
30589
|
+
}
|
|
30590
|
+
return null;
|
|
30591
|
+
}
|
|
30592
|
+
function detectPythonTracebackReturn(ctx) {
|
|
30593
|
+
const out2 = [];
|
|
30594
|
+
const lines = ctx.code.split(`
|
|
30595
|
+
`);
|
|
30596
|
+
for (let i2 = 0;i2 < lines.length; i2++) {
|
|
30597
|
+
const ln = lines[i2] ?? "";
|
|
30598
|
+
if (/\breturn\s+traceback\.format_exc\s*\(\s*\)/.test(ln) || /\breturn\s+\{[^}]*traceback\.format_exc\s*\(\s*\)[^}]*\}/.test(ln) || /\bjsonify\s*\([^)]*traceback\.format_exc\s*\(\s*\)/.test(ln)) {
|
|
30599
|
+
out2.push({ line: i2 + 1, api: "return traceback.format_exc()" });
|
|
30600
|
+
continue;
|
|
30601
|
+
}
|
|
30602
|
+
if (/\breturn\s+(?:str|repr)\s*\(\s*(?:e|err|error|exc|exception)\s*\)/.test(ln)) {
|
|
30603
|
+
const start2 = Math.max(0, i2 - 8);
|
|
30604
|
+
const end = Math.min(lines.length, i2 + 2);
|
|
30605
|
+
const window2 = lines.slice(start2, end).join(`
|
|
30606
|
+
`);
|
|
30607
|
+
if (/@(?:app|router|blueprint)\.(?:route|get|post|put|delete|patch)\b/.test(window2)) {
|
|
30608
|
+
out2.push({ line: i2 + 1, api: "return str(e) in handler" });
|
|
30609
|
+
}
|
|
30610
|
+
}
|
|
30611
|
+
}
|
|
30612
|
+
return out2;
|
|
30613
|
+
}
|
|
30614
|
+
|
|
30615
|
+
class InfoDisclosureStacktracePass {
|
|
30616
|
+
name = "info-disclosure-stacktrace";
|
|
30617
|
+
category = "security";
|
|
30618
|
+
run(ctx) {
|
|
30619
|
+
const { graph, language } = ctx;
|
|
30620
|
+
const file = graph.ir.meta.file;
|
|
30621
|
+
const findings = [];
|
|
30622
|
+
if (language === "python") {
|
|
30623
|
+
for (const f of detectPythonTracebackReturn(ctx)) {
|
|
30624
|
+
findings.push({ line: f.line, api: f.api, language });
|
|
30625
|
+
ctx.addFinding(this.makeFinding(file, f.line, f.api));
|
|
30626
|
+
}
|
|
30627
|
+
}
|
|
30628
|
+
for (const call of graph.ir.calls) {
|
|
30629
|
+
let api = null;
|
|
30630
|
+
if (language === "java") {
|
|
30631
|
+
api = detectJavaPrintStackTrace(call);
|
|
30632
|
+
if (!api)
|
|
30633
|
+
api = detectResponseLeakCall(call);
|
|
30634
|
+
} else if (language === "javascript" || language === "typescript") {
|
|
30635
|
+
api = detectResponseLeakCall(call);
|
|
30636
|
+
} else if (language === "go") {
|
|
30637
|
+
const method = call.method_name ?? "";
|
|
30638
|
+
const rec = call.receiver ?? "";
|
|
30639
|
+
if (rec === "http" && method === "Error") {
|
|
30640
|
+
const arg1 = call.arguments.find((a) => a.position === 1);
|
|
30641
|
+
if (argIsException(arg1))
|
|
30642
|
+
api = "http.Error(w, err.Error())";
|
|
30643
|
+
} else if (rec === "fmt" && (method === "Fprintln" || method === "Fprintf" || method === "Fprint")) {
|
|
30644
|
+
const arg0 = call.arguments.find((a) => a.position === 0);
|
|
30645
|
+
if (arg0 && /^(w|writer|resp|response)$/i.test((arg0.variable ?? arg0.expression ?? "").trim())) {
|
|
30646
|
+
for (const a of call.arguments) {
|
|
30647
|
+
if (a.position === 0)
|
|
30648
|
+
continue;
|
|
30649
|
+
if (argIsException(a)) {
|
|
30650
|
+
api = `fmt.${method}(w, err)`;
|
|
30651
|
+
break;
|
|
30652
|
+
}
|
|
30653
|
+
}
|
|
30654
|
+
}
|
|
30655
|
+
} else {
|
|
30656
|
+
api = detectResponseLeakCall(call);
|
|
30657
|
+
}
|
|
30658
|
+
} else if (language === "python") {
|
|
30659
|
+
api = detectResponseLeakCall(call);
|
|
30660
|
+
}
|
|
30661
|
+
if (!api)
|
|
30662
|
+
continue;
|
|
30663
|
+
const line = call.location.line;
|
|
30664
|
+
findings.push({ line, api, language });
|
|
30665
|
+
ctx.addFinding(this.makeFinding(file, line, api));
|
|
30666
|
+
}
|
|
30667
|
+
return { findings };
|
|
30668
|
+
}
|
|
30669
|
+
makeFinding(file, line, api) {
|
|
30670
|
+
return {
|
|
30671
|
+
id: `${this.name}-${file}-${line}`,
|
|
30672
|
+
pass: this.name,
|
|
30673
|
+
category: this.category,
|
|
30674
|
+
rule_id: this.name,
|
|
30675
|
+
cwe: "CWE-209",
|
|
30676
|
+
severity: "medium",
|
|
30677
|
+
level: "warning",
|
|
30678
|
+
message: `Exception detail returned to client via \`${api}\`. ` + "Leaking stack traces / exception messages reveals framework internals, " + "file paths, and class names — useful reconnaissance for an attacker.",
|
|
30679
|
+
file,
|
|
30680
|
+
line,
|
|
30681
|
+
fix: "Return a generic error response to the client (e.g. status 500 + a " + "request id) and log the full exception server-side via your logger " + '(e.g. `logger.error("…", e)` or `console.error(err)`).',
|
|
30682
|
+
evidence: { api }
|
|
30683
|
+
};
|
|
30684
|
+
}
|
|
30685
|
+
}
|
|
30686
|
+
|
|
30687
|
+
// ../circle-ir/dist/analysis/passes/unrestricted-file-upload-pass.js
|
|
30688
|
+
var UPLOAD_NAME_RE = /(?:getOriginalFilename|getSubmittedFileName|originalname|originalName|\.filename|\.Filename|FileHeader\.Filename|UploadFile)/;
|
|
30689
|
+
var FILE_SAFE_CALL_RE = /(?:secure_filename|FilenameUtils\.getExtension|\.lastIndexOf\(['"]\.['"]\)|ALLOWED_EXT|ALLOWED_EXTENSIONS|allowedExtensions|\bfileFilter\b|filepath\.Ext|path\.extname)/;
|
|
30690
|
+
function lineWindow(code, startLine, endLine) {
|
|
30691
|
+
const lines = code.split(`
|
|
30692
|
+
`);
|
|
30693
|
+
const s = Math.max(0, startLine - 1);
|
|
30694
|
+
const e = Math.min(lines.length, endLine);
|
|
30695
|
+
return lines.slice(s, e).join(`
|
|
30696
|
+
`);
|
|
30697
|
+
}
|
|
30698
|
+
function callHasUploadName(call) {
|
|
30699
|
+
for (const a of call.arguments) {
|
|
30700
|
+
const expr = (a.expression ?? a.variable ?? "").trim();
|
|
30701
|
+
if (UPLOAD_NAME_RE.test(expr))
|
|
30702
|
+
return true;
|
|
30703
|
+
}
|
|
30704
|
+
if (UPLOAD_NAME_RE.test(call.receiver ?? ""))
|
|
30705
|
+
return true;
|
|
30706
|
+
return false;
|
|
30707
|
+
}
|
|
30708
|
+
|
|
30709
|
+
class UnrestrictedFileUploadPass {
|
|
30710
|
+
name = "unrestricted-file-upload";
|
|
30711
|
+
category = "security";
|
|
30712
|
+
run(ctx) {
|
|
30713
|
+
const { graph, language, code } = ctx;
|
|
30714
|
+
const file = graph.ir.meta.file;
|
|
30715
|
+
const findings = [];
|
|
30716
|
+
const safeFunctionRanges = [];
|
|
30717
|
+
for (const t of graph.ir.types) {
|
|
30718
|
+
for (const m of t.methods) {
|
|
30719
|
+
const body2 = lineWindow(code, m.start_line, m.end_line);
|
|
30720
|
+
if (FILE_SAFE_CALL_RE.test(body2)) {
|
|
30721
|
+
safeFunctionRanges.push({ start: m.start_line, end: m.end_line });
|
|
30722
|
+
}
|
|
30723
|
+
}
|
|
30724
|
+
}
|
|
30725
|
+
const inSafeRange = (line) => {
|
|
30726
|
+
for (const r of safeFunctionRanges) {
|
|
30727
|
+
if (line >= r.start && line <= r.end)
|
|
30728
|
+
return true;
|
|
30729
|
+
}
|
|
30730
|
+
const win = lineWindow(code, Math.max(1, line - 20), line + 5);
|
|
30731
|
+
return FILE_SAFE_CALL_RE.test(win);
|
|
30732
|
+
};
|
|
30733
|
+
if (language === "java") {
|
|
30734
|
+
for (const call of graph.ir.calls) {
|
|
30735
|
+
const m = call.method_name ?? "";
|
|
30736
|
+
if (m === "transferTo" && callHasUploadName(call)) {
|
|
30737
|
+
if (inSafeRange(call.location.line))
|
|
30738
|
+
continue;
|
|
30739
|
+
this.emit(ctx, findings, file, call.location.line, language, "MultipartFile.transferTo(<original filename>)");
|
|
30740
|
+
continue;
|
|
30741
|
+
}
|
|
30742
|
+
if (m === "copy" && (call.receiver === "Files" || (call.receiver ?? "").endsWith(".Files"))) {
|
|
30743
|
+
if (callHasUploadName(call)) {
|
|
30744
|
+
if (inSafeRange(call.location.line))
|
|
30745
|
+
continue;
|
|
30746
|
+
this.emit(ctx, findings, file, call.location.line, language, "Files.copy(input, Path.of(dir, <original filename>))");
|
|
30747
|
+
}
|
|
30748
|
+
}
|
|
30749
|
+
}
|
|
30750
|
+
}
|
|
30751
|
+
if (language === "javascript" || language === "typescript") {
|
|
30752
|
+
for (const call of graph.ir.calls) {
|
|
30753
|
+
const m = call.method_name ?? "";
|
|
30754
|
+
const rec = call.receiver ?? "";
|
|
30755
|
+
if (m === "multer" || rec === "" && m === "multer") {
|
|
30756
|
+
const arg0 = call.arguments.find((a) => a.position === 0);
|
|
30757
|
+
const expr = (arg0?.expression ?? "").trim();
|
|
30758
|
+
if (/\bdest\s*:/.test(expr) && !/\bfileFilter\s*:/.test(expr)) {
|
|
30759
|
+
if (inSafeRange(call.location.line))
|
|
30760
|
+
continue;
|
|
30761
|
+
this.emit(ctx, findings, file, call.location.line, language, "multer({ dest }) without fileFilter");
|
|
30762
|
+
continue;
|
|
30763
|
+
}
|
|
30764
|
+
}
|
|
30765
|
+
if (rec === "fs" && (m === "writeFile" || m === "writeFileSync" || m === "appendFile")) {
|
|
30766
|
+
if (callHasUploadName(call) || call.arguments.some((a) => /\breq\.file(?:s)?\b/.test(a.expression ?? a.variable ?? ""))) {
|
|
30767
|
+
if (inSafeRange(call.location.line))
|
|
30768
|
+
continue;
|
|
30769
|
+
this.emit(ctx, findings, file, call.location.line, language, `fs.${m}(<path>, req.file.buffer)`);
|
|
30770
|
+
}
|
|
30771
|
+
}
|
|
30772
|
+
}
|
|
30773
|
+
}
|
|
30774
|
+
if (language === "python") {
|
|
30775
|
+
for (const call of graph.ir.calls) {
|
|
30776
|
+
const m = call.method_name ?? "";
|
|
30777
|
+
if (m === "save") {
|
|
30778
|
+
const rec = call.receiver ?? "";
|
|
30779
|
+
if (!/^(f|file|upload|attachment)$/i.test(rec) && rec !== "")
|
|
30780
|
+
continue;
|
|
30781
|
+
if (!callHasUploadName(call))
|
|
30782
|
+
continue;
|
|
30783
|
+
if (inSafeRange(call.location.line))
|
|
30784
|
+
continue;
|
|
30785
|
+
this.emit(ctx, findings, file, call.location.line, language, "f.save(<dir>, f.filename) without secure_filename");
|
|
30786
|
+
}
|
|
30787
|
+
}
|
|
30788
|
+
}
|
|
30789
|
+
if (language === "go") {
|
|
30790
|
+
for (const call of graph.ir.calls) {
|
|
30791
|
+
const m = call.method_name ?? "";
|
|
30792
|
+
const rec = call.receiver ?? "";
|
|
30793
|
+
if (rec === "os" && (m === "Create" || m === "OpenFile")) {
|
|
30794
|
+
if (callHasUploadName(call)) {
|
|
30795
|
+
if (inSafeRange(call.location.line))
|
|
30796
|
+
continue;
|
|
30797
|
+
this.emit(ctx, findings, file, call.location.line, language, `os.${m}(<uploaded filename>)`);
|
|
30798
|
+
}
|
|
30799
|
+
}
|
|
30800
|
+
if ((rec === "os" || rec === "ioutil") && m === "WriteFile") {
|
|
30801
|
+
if (callHasUploadName(call)) {
|
|
30802
|
+
if (inSafeRange(call.location.line))
|
|
30803
|
+
continue;
|
|
30804
|
+
this.emit(ctx, findings, file, call.location.line, language, `${rec}.WriteFile(<uploaded filename>, …)`);
|
|
30805
|
+
}
|
|
30806
|
+
}
|
|
30807
|
+
}
|
|
30808
|
+
}
|
|
30809
|
+
return { findings };
|
|
30810
|
+
}
|
|
30811
|
+
emit(ctx, findings, file, line, language, api) {
|
|
30812
|
+
findings.push({ line, api, language });
|
|
30813
|
+
ctx.addFinding({
|
|
30814
|
+
id: `${this.name}-${file}-${line}`,
|
|
30815
|
+
pass: this.name,
|
|
30816
|
+
category: this.category,
|
|
30817
|
+
rule_id: this.name,
|
|
30818
|
+
cwe: "CWE-434",
|
|
30819
|
+
severity: "high",
|
|
30820
|
+
level: "error",
|
|
30821
|
+
message: `File upload saved using untrusted name (${api}) — no extension allow-list or ` + "filename canonicalization detected. An attacker can upload a `.jsp`/`.php`/`.html` " + "file and request it back, achieving RCE or stored XSS.",
|
|
30822
|
+
file,
|
|
30823
|
+
line,
|
|
30824
|
+
fix: "Validate the uploaded extension against an allow-list (e.g. " + '`Set.of("png","jpg")`), then save with a sanitized filename. In Python use ' + "`werkzeug.utils.secure_filename`. In multer pass a `fileFilter`. Never " + "concatenate the upload's original filename into a save path without " + "validation.",
|
|
30825
|
+
evidence: { api, language }
|
|
30826
|
+
});
|
|
30827
|
+
}
|
|
30828
|
+
}
|
|
30829
|
+
|
|
30525
30830
|
// ../circle-ir/dist/analysis/passes/plaintext-password-storage-pass.js
|
|
30526
30831
|
function isWriteStorageCall(call, language) {
|
|
30527
30832
|
const method = call.method_name ?? "";
|
|
@@ -33084,6 +33389,10 @@ async function analyze(code, filePath, language, options = {}) {
|
|
|
33084
33389
|
pipeline.add(new XmlEntityExpansionPass);
|
|
33085
33390
|
if (!disabledPasses.has("mass-assignment"))
|
|
33086
33391
|
pipeline.add(new MassAssignmentPass);
|
|
33392
|
+
if (!disabledPasses.has("info-disclosure-stacktrace"))
|
|
33393
|
+
pipeline.add(new InfoDisclosureStacktracePass);
|
|
33394
|
+
if (!disabledPasses.has("unrestricted-file-upload"))
|
|
33395
|
+
pipeline.add(new UnrestrictedFileUploadPass);
|
|
33087
33396
|
const { results, findings } = pipeline.run(graph, code, language, config);
|
|
33088
33397
|
const sinkFilter = results.get("sink-filter");
|
|
33089
33398
|
const interProc = results.get("interprocedural");
|
|
@@ -33277,7 +33586,7 @@ var colors = {
|
|
|
33277
33586
|
};
|
|
33278
33587
|
|
|
33279
33588
|
// src/version.ts
|
|
33280
|
-
var version = "3.
|
|
33589
|
+
var version = "3.83.0";
|
|
33281
33590
|
|
|
33282
33591
|
// src/formatters.ts
|
|
33283
33592
|
var SINK_SEVERITY = {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "cognium-dev",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.83.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.83.0"
|
|
69
69
|
},
|
|
70
70
|
"devDependencies": {
|
|
71
71
|
"@types/node": "^25.5.0",
|