circle-ir 3.81.0 → 3.82.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.
@@ -0,0 +1,48 @@
1
+ /**
2
+ * Pass: info-disclosure-stacktrace (CWE-209, category: security)
3
+ *
4
+ * Detects exception stack traces / messages being returned to remote clients
5
+ * via an HTTP response handler. This leaks framework internals, file paths,
6
+ * SQL fragments, and class names — useful reconnaissance for an attacker.
7
+ *
8
+ * Detection per language:
9
+ * Java:
10
+ * - `e.printStackTrace(response.getWriter())` / `.printStackTrace(out)`
11
+ * where `out` is a response writer.
12
+ * - `response.getWriter().write(e.toString())`
13
+ * - `response.getWriter().println(e.getMessage())`
14
+ * - `new ResponseEntity<>(e.getStackTrace(), …)`
15
+ *
16
+ * Python:
17
+ * - `return traceback.format_exc()` from a handler-like function
18
+ * - `flask.jsonify(error=traceback.format_exc())`
19
+ * - Bare `return str(e)` / `return {"error": str(e)}` in handler
20
+ *
21
+ * JS/TS:
22
+ * - `res.send(err.stack)` / `res.json({error: err.stack})`
23
+ * - `res.json(err)` (whole error object)
24
+ * - `res.status(N).send(err.message + err.stack)` / similar
25
+ *
26
+ * Go:
27
+ * - `http.Error(w, err.Error()+debug.Stack(), 500)` — narrow
28
+ * - `fmt.Fprintln(w, err)` in an HTTP handler
29
+ *
30
+ * Negative guard: if the consumer is a logger (`console.error`,
31
+ * `logger.error`, `log.Error`), do NOT fire — logging stack traces
32
+ * server-side is not a leak.
33
+ */
34
+ import type { AnalysisPass, PassContext } from '../../graph/analysis-pass.js';
35
+ export interface InfoDisclosureStacktraceResult {
36
+ findings: Array<{
37
+ line: number;
38
+ api: string;
39
+ language: string;
40
+ }>;
41
+ }
42
+ export declare class InfoDisclosureStacktracePass implements AnalysisPass<InfoDisclosureStacktraceResult> {
43
+ readonly name = "info-disclosure-stacktrace";
44
+ readonly category: "security";
45
+ run(ctx: PassContext): InfoDisclosureStacktraceResult;
46
+ private makeFinding;
47
+ }
48
+ //# sourceMappingURL=info-disclosure-stacktrace-pass.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"info-disclosure-stacktrace-pass.d.ts","sourceRoot":"","sources":["../../../src/analysis/passes/info-disclosure-stacktrace-pass.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgCG;AAEH,OAAO,KAAK,EAAE,YAAY,EAAE,WAAW,EAAE,MAAM,8BAA8B,CAAC;AAG9E,MAAM,WAAW,8BAA8B;IAC7C,QAAQ,EAAE,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,GAAG,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;CAClE;AAoHD,qBAAa,4BAA6B,YAAW,YAAY,CAAC,8BAA8B,CAAC;IAC/F,QAAQ,CAAC,IAAI,gCAAgC;IAC7C,QAAQ,CAAC,QAAQ,EAAG,UAAU,CAAU;IAExC,GAAG,CAAC,GAAG,EAAE,WAAW,GAAG,8BAA8B;IAqDrD,OAAO,CAAC,WAAW;CAsBpB"}
@@ -0,0 +1,222 @@
1
+ /**
2
+ * Pass: info-disclosure-stacktrace (CWE-209, category: security)
3
+ *
4
+ * Detects exception stack traces / messages being returned to remote clients
5
+ * via an HTTP response handler. This leaks framework internals, file paths,
6
+ * SQL fragments, and class names — useful reconnaissance for an attacker.
7
+ *
8
+ * Detection per language:
9
+ * Java:
10
+ * - `e.printStackTrace(response.getWriter())` / `.printStackTrace(out)`
11
+ * where `out` is a response writer.
12
+ * - `response.getWriter().write(e.toString())`
13
+ * - `response.getWriter().println(e.getMessage())`
14
+ * - `new ResponseEntity<>(e.getStackTrace(), …)`
15
+ *
16
+ * Python:
17
+ * - `return traceback.format_exc()` from a handler-like function
18
+ * - `flask.jsonify(error=traceback.format_exc())`
19
+ * - Bare `return str(e)` / `return {"error": str(e)}` in handler
20
+ *
21
+ * JS/TS:
22
+ * - `res.send(err.stack)` / `res.json({error: err.stack})`
23
+ * - `res.json(err)` (whole error object)
24
+ * - `res.status(N).send(err.message + err.stack)` / similar
25
+ *
26
+ * Go:
27
+ * - `http.Error(w, err.Error()+debug.Stack(), 500)` — narrow
28
+ * - `fmt.Fprintln(w, err)` in an HTTP handler
29
+ *
30
+ * Negative guard: if the consumer is a logger (`console.error`,
31
+ * `logger.error`, `log.Error`), do NOT fire — logging stack traces
32
+ * server-side is not a leak.
33
+ */
34
+ /** Receiver names that almost always indicate an HTTP response handle. */
35
+ const RESPONSE_RECEIVER_RE = /^(res|response|w|writer|ctx|c)$/i;
36
+ /** Logger receivers (negative guard). */
37
+ const LOGGER_RECEIVER_RE = /^(log|logger|slog|console|pino|winston|sentry)$/i;
38
+ /** Method names on a response that send data to the client. */
39
+ const RESPONSE_SEND_METHODS = new Set([
40
+ 'send', 'json', 'write', 'writeHead', 'end', 'sendFile',
41
+ 'println', 'print', 'getWriter',
42
+ 'Fprintln', 'Fprintf', 'Fprint',
43
+ ]);
44
+ /** Expression heuristics for "this is an exception value". */
45
+ function isExceptionExpression(expr) {
46
+ if (!expr)
47
+ return false;
48
+ const e = expr.trim();
49
+ // err.stack | err.message | e.toString() | e.getMessage() | e.getStackTrace()
50
+ // exc.format_exc() | traceback.format_exc() | str(e)
51
+ return (/\b(err|error|exc|exception|e|t|throwable)\.(stack|message|toString\(|getMessage\(|getStackTrace\(|getLocalizedMessage\(|getCause\()/i.test(e) ||
52
+ /\btraceback\.(format_exc|format_exception|print_exc)\b/i.test(e) ||
53
+ /\bdebug\.Stack\(\)/.test(e) ||
54
+ /\bstr\(\s*(err|error|exc|exception|e)\s*\)/i.test(e) ||
55
+ /\bString\(\s*(err|error|exc|exception|e)\s*\)/i.test(e));
56
+ }
57
+ /** True if an argument carries an exception-like value. */
58
+ function argIsException(arg) {
59
+ if (!arg)
60
+ return false;
61
+ if (arg.variable && /^(err|error|exc|exception|e|t|throwable)$/i.test(arg.variable)) {
62
+ return true;
63
+ }
64
+ return isExceptionExpression(arg.expression);
65
+ }
66
+ /** Detect Java: e.printStackTrace(out) where out is a response writer. */
67
+ function detectJavaPrintStackTrace(call) {
68
+ if (call.method_name !== 'printStackTrace')
69
+ return null;
70
+ // Receiver should look like an exception variable name.
71
+ const rec = call.receiver ?? '';
72
+ if (!/^(e|ex|exc|exception|err|error|t|throwable)$/i.test(rec))
73
+ return null;
74
+ // Arg 0 should be a response writer expression.
75
+ const arg0 = call.arguments.find((a) => a.position === 0);
76
+ if (!arg0)
77
+ return null;
78
+ const expr = (arg0.expression ?? arg0.variable ?? '').trim();
79
+ if (/\bresponse\.getWriter\(\)/.test(expr) ||
80
+ /\bresp\.getWriter\(\)/.test(expr) ||
81
+ /\bout\b/.test(expr) || // common name; conservative
82
+ /\bgetWriter\(\)/.test(expr)) {
83
+ return 'e.printStackTrace(response.getWriter())';
84
+ }
85
+ return null;
86
+ }
87
+ /** Detect calls of the shape `response.send(err.stack)` / `.json(err)`. */
88
+ function detectResponseLeakCall(call) {
89
+ const method = call.method_name ?? '';
90
+ const receiver = call.receiver ?? '';
91
+ if (!RESPONSE_SEND_METHODS.has(method))
92
+ return null;
93
+ if (LOGGER_RECEIVER_RE.test(receiver))
94
+ return null;
95
+ // Accept either a bare known receiver name, or one whose tail is a known name
96
+ // (e.g. `ctx.response`, `event.res`, `response.status(500)` chained returns).
97
+ const recTail = receiver.split('.').pop() ?? receiver;
98
+ const recHead = receiver.split('.')[0] ?? receiver;
99
+ if (!RESPONSE_RECEIVER_RE.test(recTail) && !RESPONSE_RECEIVER_RE.test(recHead)) {
100
+ // Allow chained: res.status(500).send(...) — receiver text often contains
101
+ // `.status(` substring.
102
+ if (!/(?:^|[.\s])(res|response)\.(?:status|set|header|cookie)\b/i.test(receiver)) {
103
+ return null;
104
+ }
105
+ }
106
+ // Any argument contains an exception expression?
107
+ for (const a of call.arguments) {
108
+ if (argIsException(a)) {
109
+ return `${receiver || ''}${receiver ? '.' : ''}${method}(${(a.expression ?? a.variable ?? '').trim()})`;
110
+ }
111
+ }
112
+ return null;
113
+ }
114
+ /** Detect Python: `return traceback.format_exc()` inside any function. */
115
+ function detectPythonTracebackReturn(ctx) {
116
+ const out = [];
117
+ const lines = ctx.code.split('\n');
118
+ for (let i = 0; i < lines.length; i++) {
119
+ const ln = lines[i] ?? '';
120
+ if (/\breturn\s+traceback\.format_exc\s*\(\s*\)/.test(ln) ||
121
+ /\breturn\s+\{[^}]*traceback\.format_exc\s*\(\s*\)[^}]*\}/.test(ln) ||
122
+ /\bjsonify\s*\([^)]*traceback\.format_exc\s*\(\s*\)/.test(ln)) {
123
+ out.push({ line: i + 1, api: 'return traceback.format_exc()' });
124
+ continue;
125
+ }
126
+ // `return str(e)` in a handler context — conservative: require the
127
+ // surrounding 5-line window to contain a Flask/FastAPI/Django marker.
128
+ if (/\breturn\s+(?:str|repr)\s*\(\s*(?:e|err|error|exc|exception)\s*\)/.test(ln)) {
129
+ const start = Math.max(0, i - 8);
130
+ const end = Math.min(lines.length, i + 2);
131
+ const window = lines.slice(start, end).join('\n');
132
+ if (/@(?:app|router|blueprint)\.(?:route|get|post|put|delete|patch)\b/.test(window)) {
133
+ out.push({ line: i + 1, api: 'return str(e) in handler' });
134
+ }
135
+ }
136
+ }
137
+ return out;
138
+ }
139
+ export class InfoDisclosureStacktracePass {
140
+ name = 'info-disclosure-stacktrace';
141
+ category = 'security';
142
+ run(ctx) {
143
+ const { graph, language } = ctx;
144
+ const file = graph.ir.meta.file;
145
+ const findings = [];
146
+ if (language === 'python') {
147
+ for (const f of detectPythonTracebackReturn(ctx)) {
148
+ findings.push({ line: f.line, api: f.api, language });
149
+ ctx.addFinding(this.makeFinding(file, f.line, f.api));
150
+ }
151
+ }
152
+ for (const call of graph.ir.calls) {
153
+ let api = null;
154
+ if (language === 'java') {
155
+ api = detectJavaPrintStackTrace(call);
156
+ if (!api)
157
+ api = detectResponseLeakCall(call);
158
+ }
159
+ else if (language === 'javascript' || language === 'typescript') {
160
+ api = detectResponseLeakCall(call);
161
+ }
162
+ else if (language === 'go') {
163
+ // http.Error(w, err.Error()+debug.Stack(), 500)
164
+ // fmt.Fprintln(w, err)
165
+ const method = call.method_name ?? '';
166
+ const rec = call.receiver ?? '';
167
+ if (rec === 'http' && method === 'Error') {
168
+ const arg1 = call.arguments.find((a) => a.position === 1);
169
+ if (argIsException(arg1))
170
+ api = 'http.Error(w, err.Error())';
171
+ }
172
+ else if (rec === 'fmt' && (method === 'Fprintln' || method === 'Fprintf' || method === 'Fprint')) {
173
+ const arg0 = call.arguments.find((a) => a.position === 0);
174
+ if (arg0 && /^(w|writer|resp|response)$/i.test((arg0.variable ?? arg0.expression ?? '').trim())) {
175
+ for (const a of call.arguments) {
176
+ if (a.position === 0)
177
+ continue;
178
+ if (argIsException(a)) {
179
+ api = `fmt.${method}(w, err)`;
180
+ break;
181
+ }
182
+ }
183
+ }
184
+ }
185
+ else {
186
+ api = detectResponseLeakCall(call);
187
+ }
188
+ }
189
+ else if (language === 'python') {
190
+ // Handle response leak shape too: e.g. `return jsonify(stack=...)`
191
+ api = detectResponseLeakCall(call);
192
+ }
193
+ if (!api)
194
+ continue;
195
+ const line = call.location.line;
196
+ findings.push({ line, api, language });
197
+ ctx.addFinding(this.makeFinding(file, line, api));
198
+ }
199
+ return { findings };
200
+ }
201
+ makeFinding(file, line, api) {
202
+ return {
203
+ id: `${this.name}-${file}-${line}`,
204
+ pass: this.name,
205
+ category: this.category,
206
+ rule_id: this.name,
207
+ cwe: 'CWE-209',
208
+ severity: 'medium',
209
+ level: 'warning',
210
+ message: `Exception detail returned to client via \`${api}\`. ` +
211
+ 'Leaking stack traces / exception messages reveals framework internals, ' +
212
+ 'file paths, and class names — useful reconnaissance for an attacker.',
213
+ file,
214
+ line,
215
+ fix: 'Return a generic error response to the client (e.g. status 500 + a ' +
216
+ 'request id) and log the full exception server-side via your logger ' +
217
+ '(e.g. `logger.error("…", e)` or `console.error(err)`).',
218
+ evidence: { api },
219
+ };
220
+ }
221
+ }
222
+ //# sourceMappingURL=info-disclosure-stacktrace-pass.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"info-disclosure-stacktrace-pass.js","sourceRoot":"","sources":["../../../src/analysis/passes/info-disclosure-stacktrace-pass.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,46 @@
1
+ /**
2
+ * Pass: unrestricted-file-upload (CWE-434, category: security)
3
+ *
4
+ * Detects when an HTTP-uploaded file is saved to disk WITHOUT a filename
5
+ * allow-list (extension check) or `secure_filename` normalization.
6
+ *
7
+ * Detection (per language):
8
+ *
9
+ * Java (Spring MultipartFile / Servlet Part):
10
+ * - `file.transferTo(new File(dir, file.getOriginalFilename()))`
11
+ * - `Files.copy(part.getInputStream(), Path.of(dir, part.getSubmittedFileName()))`
12
+ * - Without preceding `ALLOWED_*.contains(ext)` or
13
+ * `FilenameUtils.getExtension(name)` + check.
14
+ *
15
+ * JS/TS (multer / express-fileupload):
16
+ * - `multer({ dest: '…' })` with NO `fileFilter` option.
17
+ * - `fs.writeFile(path, req.file.buffer)` / `req.files.x.mv(path)`
18
+ * without prior `path.extname` allow-list check.
19
+ *
20
+ * Python (Flask / Django / FastAPI):
21
+ * - `f.save(os.path.join(UPLOAD_DIR, f.filename))` without prior
22
+ * `secure_filename(f.filename)` wrapping.
23
+ *
24
+ * Go:
25
+ * - `io.Copy(dst, file)` where `dst = os.Create(fileHeader.Filename)`
26
+ * without an extension allow-list.
27
+ *
28
+ * The pass is intentionally conservative — it only fires when an upload-name
29
+ * expression flows directly into a save sink in the same function and no
30
+ * known allow-list / canonicalizer call appears earlier in the function.
31
+ */
32
+ import type { AnalysisPass, PassContext } from '../../graph/analysis-pass.js';
33
+ export interface UnrestrictedFileUploadResult {
34
+ findings: Array<{
35
+ line: number;
36
+ api: string;
37
+ language: string;
38
+ }>;
39
+ }
40
+ export declare class UnrestrictedFileUploadPass implements AnalysisPass<UnrestrictedFileUploadResult> {
41
+ readonly name = "unrestricted-file-upload";
42
+ readonly category: "security";
43
+ run(ctx: PassContext): UnrestrictedFileUploadResult;
44
+ private emit;
45
+ }
46
+ //# sourceMappingURL=unrestricted-file-upload-pass.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"unrestricted-file-upload-pass.d.ts","sourceRoot":"","sources":["../../../src/analysis/passes/unrestricted-file-upload-pass.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8BG;AAEH,OAAO,KAAK,EAAE,YAAY,EAAE,WAAW,EAAE,MAAM,8BAA8B,CAAC;AAG9E,MAAM,WAAW,4BAA4B;IAC3C,QAAQ,EAAE,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,GAAG,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;CAClE;AA2BD,qBAAa,0BAA2B,YAAW,YAAY,CAAC,4BAA4B,CAAC;IAC3F,QAAQ,CAAC,IAAI,8BAA8B;IAC3C,QAAQ,CAAC,QAAQ,EAAG,UAAU,CAAU;IAExC,GAAG,CAAC,GAAG,EAAE,WAAW,GAAG,4BAA4B;IAyHnD,OAAO,CAAC,IAAI;CAgCb"}
@@ -0,0 +1,193 @@
1
+ /**
2
+ * Pass: unrestricted-file-upload (CWE-434, category: security)
3
+ *
4
+ * Detects when an HTTP-uploaded file is saved to disk WITHOUT a filename
5
+ * allow-list (extension check) or `secure_filename` normalization.
6
+ *
7
+ * Detection (per language):
8
+ *
9
+ * Java (Spring MultipartFile / Servlet Part):
10
+ * - `file.transferTo(new File(dir, file.getOriginalFilename()))`
11
+ * - `Files.copy(part.getInputStream(), Path.of(dir, part.getSubmittedFileName()))`
12
+ * - Without preceding `ALLOWED_*.contains(ext)` or
13
+ * `FilenameUtils.getExtension(name)` + check.
14
+ *
15
+ * JS/TS (multer / express-fileupload):
16
+ * - `multer({ dest: '…' })` with NO `fileFilter` option.
17
+ * - `fs.writeFile(path, req.file.buffer)` / `req.files.x.mv(path)`
18
+ * without prior `path.extname` allow-list check.
19
+ *
20
+ * Python (Flask / Django / FastAPI):
21
+ * - `f.save(os.path.join(UPLOAD_DIR, f.filename))` without prior
22
+ * `secure_filename(f.filename)` wrapping.
23
+ *
24
+ * Go:
25
+ * - `io.Copy(dst, file)` where `dst = os.Create(fileHeader.Filename)`
26
+ * without an extension allow-list.
27
+ *
28
+ * The pass is intentionally conservative — it only fires when an upload-name
29
+ * expression flows directly into a save sink in the same function and no
30
+ * known allow-list / canonicalizer call appears earlier in the function.
31
+ */
32
+ /** Receivers / expressions that look like an uploaded file value. */
33
+ const UPLOAD_NAME_RE = /(?:getOriginalFilename|getSubmittedFileName|originalname|originalName|\.filename|\.Filename|FileHeader\.Filename|UploadFile)/;
34
+ /** Identifier-level allow-list / canonicalization calls that defang upload paths. */
35
+ const FILE_SAFE_CALL_RE = /(?:secure_filename|FilenameUtils\.getExtension|\.lastIndexOf\(['"]\.['"]\)|ALLOWED_EXT|ALLOWED_EXTENSIONS|allowedExtensions|\bfileFilter\b|filepath\.Ext|path\.extname)/;
36
+ function lineWindow(code, startLine, endLine) {
37
+ const lines = code.split('\n');
38
+ const s = Math.max(0, startLine - 1);
39
+ const e = Math.min(lines.length, endLine);
40
+ return lines.slice(s, e).join('\n');
41
+ }
42
+ function callHasUploadName(call) {
43
+ for (const a of call.arguments) {
44
+ const expr = (a.expression ?? a.variable ?? '').trim();
45
+ if (UPLOAD_NAME_RE.test(expr))
46
+ return true;
47
+ }
48
+ // Receiver too (e.g. multipart `file.transferTo(...)`).
49
+ if (UPLOAD_NAME_RE.test(call.receiver ?? ''))
50
+ return true;
51
+ return false;
52
+ }
53
+ export class UnrestrictedFileUploadPass {
54
+ name = 'unrestricted-file-upload';
55
+ category = 'security';
56
+ run(ctx) {
57
+ const { graph, language, code } = ctx;
58
+ const file = graph.ir.meta.file;
59
+ const findings = [];
60
+ // Build per-function safety windows: a function is "safe" if its body
61
+ // contains any FILE_SAFE_CALL_RE marker. Used to suppress findings inside
62
+ // that scope.
63
+ const safeFunctionRanges = [];
64
+ for (const t of graph.ir.types) {
65
+ for (const m of t.methods) {
66
+ const body = lineWindow(code, m.start_line, m.end_line);
67
+ if (FILE_SAFE_CALL_RE.test(body)) {
68
+ safeFunctionRanges.push({ start: m.start_line, end: m.end_line });
69
+ }
70
+ }
71
+ }
72
+ const inSafeRange = (line) => {
73
+ for (const r of safeFunctionRanges) {
74
+ if (line >= r.start && line <= r.end)
75
+ return true;
76
+ }
77
+ // No method information — fall back to a ±20-line window around the call.
78
+ const win = lineWindow(code, Math.max(1, line - 20), line + 5);
79
+ return FILE_SAFE_CALL_RE.test(win);
80
+ };
81
+ // --- Per-language detection -----------------------------------------------
82
+ if (language === 'java') {
83
+ for (const call of graph.ir.calls) {
84
+ const m = call.method_name ?? '';
85
+ // file.transferTo(...)
86
+ if (m === 'transferTo' && callHasUploadName(call)) {
87
+ if (inSafeRange(call.location.line))
88
+ continue;
89
+ this.emit(ctx, findings, file, call.location.line, language, 'MultipartFile.transferTo(<original filename>)');
90
+ continue;
91
+ }
92
+ // Files.copy(getInputStream, Path.of(dir, getOriginalFilename))
93
+ if (m === 'copy' && (call.receiver === 'Files' || (call.receiver ?? '').endsWith('.Files'))) {
94
+ if (callHasUploadName(call)) {
95
+ if (inSafeRange(call.location.line))
96
+ continue;
97
+ this.emit(ctx, findings, file, call.location.line, language, 'Files.copy(input, Path.of(dir, <original filename>))');
98
+ }
99
+ }
100
+ }
101
+ }
102
+ if (language === 'javascript' || language === 'typescript') {
103
+ for (const call of graph.ir.calls) {
104
+ const m = call.method_name ?? '';
105
+ const rec = call.receiver ?? '';
106
+ // multer({ dest: '...' }) — flag if same call object lacks fileFilter.
107
+ if (m === 'multer' || (rec === '' && m === 'multer')) {
108
+ const arg0 = call.arguments.find((a) => a.position === 0);
109
+ const expr = (arg0?.expression ?? '').trim();
110
+ if (/\bdest\s*:/.test(expr) && !/\bfileFilter\s*:/.test(expr)) {
111
+ if (inSafeRange(call.location.line))
112
+ continue;
113
+ this.emit(ctx, findings, file, call.location.line, language, 'multer({ dest }) without fileFilter');
114
+ continue;
115
+ }
116
+ }
117
+ // fs.writeFile(<path>, req.file.buffer) / req.files.x.mv(<path>)
118
+ if (rec === 'fs' && (m === 'writeFile' || m === 'writeFileSync' || m === 'appendFile')) {
119
+ if (callHasUploadName(call) ||
120
+ call.arguments.some((a) => /\breq\.file(?:s)?\b/.test((a.expression ?? a.variable ?? '')))) {
121
+ if (inSafeRange(call.location.line))
122
+ continue;
123
+ this.emit(ctx, findings, file, call.location.line, language, `fs.${m}(<path>, req.file.buffer)`);
124
+ }
125
+ }
126
+ }
127
+ }
128
+ if (language === 'python') {
129
+ for (const call of graph.ir.calls) {
130
+ const m = call.method_name ?? '';
131
+ // f.save(os.path.join(UPLOAD_DIR, f.filename))
132
+ if (m === 'save') {
133
+ const rec = call.receiver ?? '';
134
+ // Accept any receiver that looks file-ish or empty (`f.save`, `file.save`).
135
+ if (!/^(f|file|upload|attachment)$/i.test(rec) && rec !== '')
136
+ continue;
137
+ if (!callHasUploadName(call))
138
+ continue;
139
+ if (inSafeRange(call.location.line))
140
+ continue;
141
+ this.emit(ctx, findings, file, call.location.line, language, 'f.save(<dir>, f.filename) without secure_filename');
142
+ }
143
+ }
144
+ }
145
+ if (language === 'go') {
146
+ for (const call of graph.ir.calls) {
147
+ const m = call.method_name ?? '';
148
+ const rec = call.receiver ?? '';
149
+ // os.Create(header.Filename)
150
+ if (rec === 'os' && (m === 'Create' || m === 'OpenFile')) {
151
+ if (callHasUploadName(call)) {
152
+ if (inSafeRange(call.location.line))
153
+ continue;
154
+ this.emit(ctx, findings, file, call.location.line, language, `os.${m}(<uploaded filename>)`);
155
+ }
156
+ }
157
+ // ioutil.WriteFile(header.Filename, …)
158
+ if ((rec === 'os' || rec === 'ioutil') && m === 'WriteFile') {
159
+ if (callHasUploadName(call)) {
160
+ if (inSafeRange(call.location.line))
161
+ continue;
162
+ this.emit(ctx, findings, file, call.location.line, language, `${rec}.WriteFile(<uploaded filename>, …)`);
163
+ }
164
+ }
165
+ }
166
+ }
167
+ return { findings };
168
+ }
169
+ emit(ctx, findings, file, line, language, api) {
170
+ findings.push({ line, api, language });
171
+ ctx.addFinding({
172
+ id: `${this.name}-${file}-${line}`,
173
+ pass: this.name,
174
+ category: this.category,
175
+ rule_id: this.name,
176
+ cwe: 'CWE-434',
177
+ severity: 'high',
178
+ level: 'error',
179
+ message: `File upload saved using untrusted name (${api}) — no extension allow-list or ` +
180
+ 'filename canonicalization detected. An attacker can upload a `.jsp`/`.php`/`.html` ' +
181
+ 'file and request it back, achieving RCE or stored XSS.',
182
+ file,
183
+ line,
184
+ fix: 'Validate the uploaded extension against an allow-list (e.g. ' +
185
+ '`Set.of("png","jpg")`), then save with a sanitized filename. In Python use ' +
186
+ '`werkzeug.utils.secure_filename`. In multer pass a `fileFilter`. Never ' +
187
+ 'concatenate the upload\'s original filename into a save path without ' +
188
+ 'validation.',
189
+ evidence: { api, language },
190
+ });
191
+ }
192
+ }
193
+ //# sourceMappingURL=unrestricted-file-upload-pass.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"unrestricted-file-upload-pass.js","sourceRoot":"","sources":["../../../src/analysis/passes/unrestricted-file-upload-pass.ts"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"analyzer.d.ts","sourceRoot":"","sources":["../src/analyzer.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAqDG;AAEH,OAAO,KAAK,EAAE,QAAQ,EAAE,gBAAgB,EAA2B,eAAe,EAAe,MAAM,kBAAkB,CAAC;AAC1H,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AACrD,OAAO,EAcL,KAAK,iBAAiB,EACvB,MAAM,iBAAiB,CAAC;AACzB,OAAO,EAKL,eAAe,EAChB,MAAM,qBAAqB,CAAC;AAgC7B,OAAO,EAAwB,KAAK,uBAAuB,EAAE,MAAM,8CAA8C,CAAC;AAKlH,OAAO,EAA2B,KAAK,0BAA0B,EAAE,MAAM,gDAAgD,CAAC;AAe1H,OAAO,EAAwB,KAAK,uBAAuB,EAAE,MAAM,6CAA6C,CAAC;AACjH,OAAO,EAAuB,KAAK,sBAAsB,EAA6B,MAAM,4CAA4C,CAAC;AAsCzI,MAAM,WAAW,eAAe;IAC9B;;OAEG;IACH,QAAQ,CAAC,EAAE,MAAM,CAAC;IAElB;;;OAGG;IACH,UAAU,CAAC,EAAE,WAAW,CAAC,MAAM,CAAC;IAEhC;;OAEG;IACH,aAAa,CAAC,EAAE,OAAO,CAAC,MAAM,CAAC,iBAAiB,EAAE,MAAM,CAAC,CAAC,CAAC;IAE3D;;;OAGG;IACH,eAAe,CAAC,EAAE,OAAO,CAAC,MAAM,CAAC,iBAAiB,EAAE,WAAW,CAAC,MAAM,CAAC,CAAC,CAAC;IAEzE;;OAEG;IACH,WAAW,CAAC,EAAE,WAAW,CAAC;IAE1B;;OAEG;IACH,WAAW,CAAC,EAAE,WAAW,CAAC;IAE1B;;OAEG;IACH,cAAc,CAAC,EAAE,MAAM,EAAE,CAAC;CAC3B;AAED;;;GAGG;AACH,MAAM,WAAW,WAAW;IAC1B,8CAA8C;IAC9C,gBAAgB,CAAC,EAAE,uBAAuB,CAAC;IAC3C,8CAA8C;IAC9C,gBAAgB,CAAC,EAAE,uBAAuB,CAAC;IAC3C,iDAAiD;IACjD,mBAAmB,CAAC,EAAE,0BAA0B,CAAC;IACjD,6CAA6C;IAC7C,eAAe,CAAC,EAAE,sBAAsB,CAAC;CAC1C;AAID;;GAEG;AACH,wBAAsB,YAAY,CAAC,OAAO,GAAE,eAAoB,GAAG,OAAO,CAAC,IAAI,CAAC,CAc/E;AAkID;;GAEG;AACH,wBAAsB,OAAO,CAC3B,IAAI,EAAE,MAAM,EACZ,QAAQ,EAAE,MAAM,EAChB,QAAQ,EAAE,iBAAiB,EAC3B,OAAO,GAAE,eAAoB,GAC5B,OAAO,CAAC,QAAQ,CAAC,CAyLnB;AA4GD;;GAEG;AACH,wBAAsB,aAAa,CACjC,IAAI,EAAE,MAAM,EACZ,QAAQ,EAAE,MAAM,EAChB,QAAQ,EAAE,iBAAiB,EAC3B,OAAO,GAAE,eAAoB,GAC5B,OAAO,CAAC,gBAAgB,CAAC,CAwG3B;AAkID;;GAEG;AACH,wBAAgB,qBAAqB,IAAI,OAAO,CAE/C;AAED;;GAEG;AACH,wBAAgB,aAAa,IAAI,IAAI,CAEpC;AAMD;;;;;;;;;;GAUG;AACH,wBAAsB,cAAc,CAClC,KAAK,EAAE,KAAK,CAAC;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,iBAAiB,CAAA;CAAE,CAAC,EAC7E,OAAO,GAAE,eAAoB,GAC5B,OAAO,CAAC,eAAe,CAAC,CAmE1B;AAsBD,OAAO,EAAE,eAAe,EAAE,CAAC"}
1
+ {"version":3,"file":"analyzer.d.ts","sourceRoot":"","sources":["../src/analyzer.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAqDG;AAEH,OAAO,KAAK,EAAE,QAAQ,EAAE,gBAAgB,EAA2B,eAAe,EAAe,MAAM,kBAAkB,CAAC;AAC1H,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AACrD,OAAO,EAcL,KAAK,iBAAiB,EACvB,MAAM,iBAAiB,CAAC;AACzB,OAAO,EAKL,eAAe,EAChB,MAAM,qBAAqB,CAAC;AAgC7B,OAAO,EAAwB,KAAK,uBAAuB,EAAE,MAAM,8CAA8C,CAAC;AAKlH,OAAO,EAA2B,KAAK,0BAA0B,EAAE,MAAM,gDAAgD,CAAC;AAe1H,OAAO,EAAwB,KAAK,uBAAuB,EAAE,MAAM,6CAA6C,CAAC;AACjH,OAAO,EAAuB,KAAK,sBAAsB,EAA6B,MAAM,4CAA4C,CAAC;AAwCzI,MAAM,WAAW,eAAe;IAC9B;;OAEG;IACH,QAAQ,CAAC,EAAE,MAAM,CAAC;IAElB;;;OAGG;IACH,UAAU,CAAC,EAAE,WAAW,CAAC,MAAM,CAAC;IAEhC;;OAEG;IACH,aAAa,CAAC,EAAE,OAAO,CAAC,MAAM,CAAC,iBAAiB,EAAE,MAAM,CAAC,CAAC,CAAC;IAE3D;;;OAGG;IACH,eAAe,CAAC,EAAE,OAAO,CAAC,MAAM,CAAC,iBAAiB,EAAE,WAAW,CAAC,MAAM,CAAC,CAAC,CAAC;IAEzE;;OAEG;IACH,WAAW,CAAC,EAAE,WAAW,CAAC;IAE1B;;OAEG;IACH,WAAW,CAAC,EAAE,WAAW,CAAC;IAE1B;;OAEG;IACH,cAAc,CAAC,EAAE,MAAM,EAAE,CAAC;CAC3B;AAED;;;GAGG;AACH,MAAM,WAAW,WAAW;IAC1B,8CAA8C;IAC9C,gBAAgB,CAAC,EAAE,uBAAuB,CAAC;IAC3C,8CAA8C;IAC9C,gBAAgB,CAAC,EAAE,uBAAuB,CAAC;IAC3C,iDAAiD;IACjD,mBAAmB,CAAC,EAAE,0BAA0B,CAAC;IACjD,6CAA6C;IAC7C,eAAe,CAAC,EAAE,sBAAsB,CAAC;CAC1C;AAID;;GAEG;AACH,wBAAsB,YAAY,CAAC,OAAO,GAAE,eAAoB,GAAG,OAAO,CAAC,IAAI,CAAC,CAc/E;AAkID;;GAEG;AACH,wBAAsB,OAAO,CAC3B,IAAI,EAAE,MAAM,EACZ,QAAQ,EAAE,MAAM,EAChB,QAAQ,EAAE,iBAAiB,EAC3B,OAAO,GAAE,eAAoB,GAC5B,OAAO,CAAC,QAAQ,CAAC,CA2LnB;AA4GD;;GAEG;AACH,wBAAsB,aAAa,CACjC,IAAI,EAAE,MAAM,EACZ,QAAQ,EAAE,MAAM,EAChB,QAAQ,EAAE,iBAAiB,EAC3B,OAAO,GAAE,eAAoB,GAC5B,OAAO,CAAC,gBAAgB,CAAC,CAwG3B;AAkID;;GAEG;AACH,wBAAgB,qBAAqB,IAAI,OAAO,CAE/C;AAED;;GAEG;AACH,wBAAgB,aAAa,IAAI,IAAI,CAEpC;AAMD;;;;;;;;;;GAUG;AACH,wBAAsB,cAAc,CAClC,KAAK,EAAE,KAAK,CAAC;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,iBAAiB,CAAA;CAAE,CAAC,EAC7E,OAAO,GAAE,eAAoB,GAC5B,OAAO,CAAC,eAAe,CAAC,CAmE1B;AAsBD,OAAO,EAAE,eAAe,EAAE,CAAC"}
package/dist/analyzer.js CHANGED
@@ -112,6 +112,8 @@ import { WeakCryptoPass } from './analysis/passes/weak-crypto-pass.js';
112
112
  import { WeakRandomPass } from './analysis/passes/weak-random-pass.js';
113
113
  import { WeakPasswordHashPass } from './analysis/passes/weak-password-hash-pass.js';
114
114
  import { WeakPasswordEncodingPass } from './analysis/passes/weak-password-encoding-pass.js';
115
+ import { InfoDisclosureStacktracePass } from './analysis/passes/info-disclosure-stacktrace-pass.js';
116
+ import { UnrestrictedFileUploadPass } from './analysis/passes/unrestricted-file-upload-pass.js';
115
117
  import { PlaintextPasswordStoragePass } from './analysis/passes/plaintext-password-storage-pass.js';
116
118
  import { CleartextCredentialTransportPass } from './analysis/passes/cleartext-credential-transport-pass.js';
117
119
  import { TlsVerifyDisabledPass } from './analysis/passes/tls-verify-disabled-pass.js';
@@ -443,6 +445,10 @@ export async function analyze(code, filePath, language, options = {}) {
443
445
  pipeline.add(new XmlEntityExpansionPass());
444
446
  if (!disabledPasses.has('mass-assignment'))
445
447
  pipeline.add(new MassAssignmentPass());
448
+ if (!disabledPasses.has('info-disclosure-stacktrace'))
449
+ pipeline.add(new InfoDisclosureStacktracePass());
450
+ if (!disabledPasses.has('unrestricted-file-upload'))
451
+ pipeline.add(new UnrestrictedFileUploadPass());
446
452
  // Run the pipeline
447
453
  const { results, findings } = pipeline.run(graph, code, language, config);
448
454
  const sinkFilter = results.get('sink-filter');