circle-ir 3.69.0 → 3.71.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/analysis/passes/cache-no-vary-pass.d.ts +40 -0
- package/dist/analysis/passes/cache-no-vary-pass.d.ts.map +1 -0
- package/dist/analysis/passes/cache-no-vary-pass.js +347 -0
- package/dist/analysis/passes/cache-no-vary-pass.js.map +1 -0
- package/dist/analysis/passes/language-sources-pass.d.ts.map +1 -1
- package/dist/analysis/passes/language-sources-pass.js +31 -1
- package/dist/analysis/passes/language-sources-pass.js.map +1 -1
- package/dist/analysis/passes/taint-propagation-pass.d.ts.map +1 -1
- package/dist/analysis/passes/taint-propagation-pass.js +131 -0
- package/dist/analysis/passes/taint-propagation-pass.js.map +1 -1
- package/dist/analyzer.d.ts.map +1 -1
- package/dist/analyzer.js +3 -0
- package/dist/analyzer.js.map +1 -1
- package/dist/browser/circle-ir.js +353 -1
- package/package.json +1 -1
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Pass: cache-no-vary (CWE-524, category: security)
|
|
3
|
+
*
|
|
4
|
+
* Pattern pass — flags HTTP handlers that set a *shared-cacheable*
|
|
5
|
+
* `Cache-Control` directive (`public` or implicit-public + positive `max-age`)
|
|
6
|
+
* **AND** read authenticated or user-scoped state (cookies / Authorization
|
|
7
|
+
* header / session), **AND** do not set `Vary: Cookie` / `Vary: Authorization`.
|
|
8
|
+
*
|
|
9
|
+
* In that configuration a shared cache (CDN, reverse proxy, ISP cache) keys
|
|
10
|
+
* the response by URL only and is free to serve user A's body to user B —
|
|
11
|
+
* the canonical CWE-524 leak.
|
|
12
|
+
*
|
|
13
|
+
* Languages: JavaScript / TypeScript (Express-style `res.setHeader` etc.),
|
|
14
|
+
* Python (Flask / Django / FastAPI), Go (`net/http`, gin), Java (Servlet /
|
|
15
|
+
* Spring).
|
|
16
|
+
*
|
|
17
|
+
* Trigger mode: strict + auth-qualifier. Skips:
|
|
18
|
+
* - `Cache-Control: private` / `no-store` / `no-cache`
|
|
19
|
+
* - `max-age=0` (effectively non-cacheable)
|
|
20
|
+
* - Handlers with no auth/session/cookie read
|
|
21
|
+
* - Handlers that set `Vary: Cookie|Authorization|*`
|
|
22
|
+
* - Test files (`*.test.*`, `*.spec.*`, `__tests__/`, `tests/`)
|
|
23
|
+
*
|
|
24
|
+
* Closes: cognium-dev #96 L91.
|
|
25
|
+
*/
|
|
26
|
+
import type { AnalysisPass, PassContext } from '../../graph/analysis-pass.js';
|
|
27
|
+
export interface CacheNoVaryResult {
|
|
28
|
+
findings: Array<{
|
|
29
|
+
line: number;
|
|
30
|
+
language: string;
|
|
31
|
+
handler: string | null;
|
|
32
|
+
cacheValue: string;
|
|
33
|
+
}>;
|
|
34
|
+
}
|
|
35
|
+
export declare class CacheNoVaryPass implements AnalysisPass<CacheNoVaryResult> {
|
|
36
|
+
readonly name = "cache-no-vary";
|
|
37
|
+
readonly category: "security";
|
|
38
|
+
run(ctx: PassContext): CacheNoVaryResult;
|
|
39
|
+
}
|
|
40
|
+
//# sourceMappingURL=cache-no-vary-pass.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cache-no-vary-pass.d.ts","sourceRoot":"","sources":["../../../src/analysis/passes/cache-no-vary-pass.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AAEH,OAAO,KAAK,EAAE,YAAY,EAAE,WAAW,EAAE,MAAM,8BAA8B,CAAC;AAmD9E,MAAM,WAAW,iBAAiB;IAChC,QAAQ,EAAE,KAAK,CAAC;QACd,IAAI,EAAE,MAAM,CAAC;QACb,QAAQ,EAAE,MAAM,CAAC;QACjB,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;QACvB,UAAU,EAAE,MAAM,CAAC;KACpB,CAAC,CAAC;CACJ;AA2ND,qBAAa,eAAgB,YAAW,YAAY,CAAC,iBAAiB,CAAC;IACrE,QAAQ,CAAC,IAAI,mBAAmB;IAChC,QAAQ,CAAC,QAAQ,EAAG,UAAU,CAAU;IAExC,GAAG,CAAC,GAAG,EAAE,WAAW,GAAG,iBAAiB;CAkHzC"}
|
|
@@ -0,0 +1,347 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Pass: cache-no-vary (CWE-524, category: security)
|
|
3
|
+
*
|
|
4
|
+
* Pattern pass — flags HTTP handlers that set a *shared-cacheable*
|
|
5
|
+
* `Cache-Control` directive (`public` or implicit-public + positive `max-age`)
|
|
6
|
+
* **AND** read authenticated or user-scoped state (cookies / Authorization
|
|
7
|
+
* header / session), **AND** do not set `Vary: Cookie` / `Vary: Authorization`.
|
|
8
|
+
*
|
|
9
|
+
* In that configuration a shared cache (CDN, reverse proxy, ISP cache) keys
|
|
10
|
+
* the response by URL only and is free to serve user A's body to user B —
|
|
11
|
+
* the canonical CWE-524 leak.
|
|
12
|
+
*
|
|
13
|
+
* Languages: JavaScript / TypeScript (Express-style `res.setHeader` etc.),
|
|
14
|
+
* Python (Flask / Django / FastAPI), Go (`net/http`, gin), Java (Servlet /
|
|
15
|
+
* Spring).
|
|
16
|
+
*
|
|
17
|
+
* Trigger mode: strict + auth-qualifier. Skips:
|
|
18
|
+
* - `Cache-Control: private` / `no-store` / `no-cache`
|
|
19
|
+
* - `max-age=0` (effectively non-cacheable)
|
|
20
|
+
* - Handlers with no auth/session/cookie read
|
|
21
|
+
* - Handlers that set `Vary: Cookie|Authorization|*`
|
|
22
|
+
* - Test files (`*.test.*`, `*.spec.*`, `__tests__/`, `tests/`)
|
|
23
|
+
*
|
|
24
|
+
* Closes: cognium-dev #96 L91.
|
|
25
|
+
*/
|
|
26
|
+
// Header value parsing -------------------------------------------------------
|
|
27
|
+
function isSharedCacheable(value) {
|
|
28
|
+
const v = value.toLowerCase();
|
|
29
|
+
if (/\b(private|no-store|no-cache)\b/.test(v))
|
|
30
|
+
return false;
|
|
31
|
+
const pub = /\bpublic\b/.test(v);
|
|
32
|
+
const maxMatch = /\b(?:s-maxage|max-age)\s*=\s*(\d+)/.exec(v);
|
|
33
|
+
const positiveMax = maxMatch ? Number(maxMatch[1]) > 0 : false;
|
|
34
|
+
return pub || positiveMax;
|
|
35
|
+
}
|
|
36
|
+
function isVaryCovering(value) {
|
|
37
|
+
const v = value.toLowerCase();
|
|
38
|
+
return /\b(cookie|authorization|\*)\b/.test(v);
|
|
39
|
+
}
|
|
40
|
+
// Source-text auth-signal regexes (per-language) -----------------------------
|
|
41
|
+
const JS_AUTH_SIGNAL_RE = /\b(?:req|request)\s*\.\s*(?:cookies|session|user(?:Id|Name)?)\b|\b(?:req|request)\s*\.\s*headers\s*\.\s*(?:cookie|authorization)\b|\bres(?:ponse)?\s*\.\s*cookie\s*\(/i;
|
|
42
|
+
const PY_AUTH_SIGNAL_RE = /\brequest\s*\.\s*cookies\b|\brequest\s*\.\s*headers\s*\.\s*get\s*\(\s*['"]Authorization['"]|\brequest\s*\.\s*authorization\b|\bsession\s*\[|\b(?:g\.user|current_user)\b|\bset_cookie\s*\(/i;
|
|
43
|
+
const GO_AUTH_SIGNAL_RE = /\br\s*\.\s*Cookie\s*\(|\br\s*\.\s*Header\s*(?:\(\)|\.)\s*\.?\s*Get\s*\(\s*"(?:Cookie|Authorization)"|\br\s*\.\s*BasicAuth\s*\(|\bhttp\s*\.\s*SetCookie\s*\(|\bc\s*\.\s*(?:GetHeader|Cookie|SetCookie)\s*\(/;
|
|
44
|
+
const JAVA_AUTH_SIGNAL_RE = /@CookieValue\b|@RequestHeader\s*\(\s*"(?:Cookie|Authorization)"|\brequest\s*\.\s*getCookies\s*\(|\brequest\s*\.\s*getHeader\s*\(\s*"(?:Cookie|Authorization)"|\bresponse\s*\.\s*addCookie\s*\(|\bSecurityContextHolder\b|\bPrincipal\s+\w+|\bAuthentication\s+\w+/;
|
|
45
|
+
// Source-text cache/vary patterns (for shapes that are not extracted as IR
|
|
46
|
+
// calls — Python subscript assignments + decorators).
|
|
47
|
+
const PY_CACHE_HEADER_ASSIGN_RE = /\w+(?:\s*\.\s*\w+)*\s*\.\s*headers\s*\[\s*['"]Cache-Control['"]\s*\]\s*=\s*(['"])([^'"]*)\1/i;
|
|
48
|
+
const PY_VARY_HEADER_ASSIGN_RE = /\w+(?:\s*\.\s*\w+)*\s*\.\s*headers\s*\[\s*['"]Vary['"]\s*\]\s*=\s*(['"])([^'"]*)\1/i;
|
|
49
|
+
const PY_VARY_DECORATOR_RE = /^\s*@\s*(?:vary_on_cookie|vary_on_headers)\b/;
|
|
50
|
+
const PY_CACHE_CONTROL_DECORATOR_RE = /^\s*@\s*cache_control\s*\(([^)]*)\)/;
|
|
51
|
+
// Per-language header-setting method tables ----------------------------------
|
|
52
|
+
const JS_HEADER_METHODS = new Set(['setHeader', 'set', 'header']);
|
|
53
|
+
const GO_HEADER_METHODS = new Set(['Set', 'Add']);
|
|
54
|
+
const JAVA_HEADER_METHODS = new Set(['setHeader', 'addHeader']);
|
|
55
|
+
// JS receivers we treat as response objects.
|
|
56
|
+
const JS_RES_RECEIVERS = new Set(['res', 'response', 'ctx']);
|
|
57
|
+
function classifyCall(call, language) {
|
|
58
|
+
const method = call.method_name;
|
|
59
|
+
const receiver = (call.receiver ?? '').trim();
|
|
60
|
+
const arg0 = call.arguments[0]?.literal ?? null;
|
|
61
|
+
const arg1 = call.arguments[1]?.literal ?? null;
|
|
62
|
+
if (language === 'javascript' || language === 'typescript') {
|
|
63
|
+
if (JS_RES_RECEIVERS.has(receiver) && JS_HEADER_METHODS.has(method)) {
|
|
64
|
+
const header = (arg0 ?? '').toLowerCase();
|
|
65
|
+
if (header === 'cache-control' && arg1 && isSharedCacheable(arg1)) {
|
|
66
|
+
return { kind: 'cache-public', value: arg1 };
|
|
67
|
+
}
|
|
68
|
+
if (header === 'vary' && arg1 && isVaryCovering(arg1)) {
|
|
69
|
+
return { kind: 'vary' };
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
if (JS_RES_RECEIVERS.has(receiver) && method === 'vary') {
|
|
73
|
+
const v = arg0 ?? '';
|
|
74
|
+
if (isVaryCovering(v) || v === '')
|
|
75
|
+
return { kind: 'vary' };
|
|
76
|
+
}
|
|
77
|
+
if (JS_RES_RECEIVERS.has(receiver) && method === 'cookie') {
|
|
78
|
+
// Set-Cookie — auth-bearing response.
|
|
79
|
+
return { kind: 'auth' };
|
|
80
|
+
}
|
|
81
|
+
return null;
|
|
82
|
+
}
|
|
83
|
+
if (language === 'python') {
|
|
84
|
+
// Auth signals via call.
|
|
85
|
+
if (receiver === 'request.cookies' || receiver === 'request.session') {
|
|
86
|
+
return { kind: 'auth' };
|
|
87
|
+
}
|
|
88
|
+
if (receiver === 'request.headers' && method === 'get') {
|
|
89
|
+
const v = (arg0 ?? '').toLowerCase();
|
|
90
|
+
if (v === 'authorization' || v === 'cookie')
|
|
91
|
+
return { kind: 'auth' };
|
|
92
|
+
}
|
|
93
|
+
if ((receiver === 'response' || receiver === 'resp') &&
|
|
94
|
+
method === 'set_cookie') {
|
|
95
|
+
return { kind: 'auth' };
|
|
96
|
+
}
|
|
97
|
+
// Vary / cache via call.
|
|
98
|
+
if (method === 'patch_vary_headers')
|
|
99
|
+
return { kind: 'vary' };
|
|
100
|
+
if (method === 'patch_cache_control') {
|
|
101
|
+
const argTxt = call.arguments.map((a) => a.expression ?? '').join(',');
|
|
102
|
+
if (/\bpublic\s*=\s*True\b/.test(argTxt)) {
|
|
103
|
+
return { kind: 'cache-public', value: argTxt };
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
return null;
|
|
107
|
+
}
|
|
108
|
+
if (language === 'go') {
|
|
109
|
+
// Cache / Vary header via w.Header().Set/Add or c.Header (gin).
|
|
110
|
+
if ((receiver === 'w.Header()' || receiver === 'rw.Header()') &&
|
|
111
|
+
GO_HEADER_METHODS.has(method)) {
|
|
112
|
+
const header = (arg0 ?? '').toLowerCase();
|
|
113
|
+
if (header === 'cache-control' && arg1 && isSharedCacheable(arg1)) {
|
|
114
|
+
return { kind: 'cache-public', value: arg1 };
|
|
115
|
+
}
|
|
116
|
+
if (header === 'vary' && arg1 && isVaryCovering(arg1)) {
|
|
117
|
+
return { kind: 'vary' };
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
if (receiver === 'c' && method === 'Header') {
|
|
121
|
+
const header = (arg0 ?? '').toLowerCase();
|
|
122
|
+
if (header === 'cache-control' && arg1 && isSharedCacheable(arg1)) {
|
|
123
|
+
return { kind: 'cache-public', value: arg1 };
|
|
124
|
+
}
|
|
125
|
+
if (header === 'vary' && arg1 && isVaryCovering(arg1)) {
|
|
126
|
+
return { kind: 'vary' };
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
// Auth signals.
|
|
130
|
+
if (receiver === 'r' && (method === 'Cookie' || method === 'BasicAuth')) {
|
|
131
|
+
return { kind: 'auth' };
|
|
132
|
+
}
|
|
133
|
+
if ((receiver === 'r.Header' || receiver === 'r.Header()') &&
|
|
134
|
+
method === 'Get') {
|
|
135
|
+
const v = (arg0 ?? '').toLowerCase();
|
|
136
|
+
if (v === 'cookie' || v === 'authorization')
|
|
137
|
+
return { kind: 'auth' };
|
|
138
|
+
}
|
|
139
|
+
if (receiver === 'http' && method === 'SetCookie')
|
|
140
|
+
return { kind: 'auth' };
|
|
141
|
+
if (receiver === 'c' &&
|
|
142
|
+
(method === 'Cookie' || method === 'GetHeader' || method === 'SetCookie')) {
|
|
143
|
+
return { kind: 'auth' };
|
|
144
|
+
}
|
|
145
|
+
return null;
|
|
146
|
+
}
|
|
147
|
+
if (language === 'java') {
|
|
148
|
+
if ((receiver === 'response' || receiver === 'resp') &&
|
|
149
|
+
JAVA_HEADER_METHODS.has(method)) {
|
|
150
|
+
const header = (arg0 ?? '').toLowerCase();
|
|
151
|
+
if (header === 'cache-control' && arg1 && isSharedCacheable(arg1)) {
|
|
152
|
+
return { kind: 'cache-public', value: arg1 };
|
|
153
|
+
}
|
|
154
|
+
if (header === 'vary' && arg1 && isVaryCovering(arg1)) {
|
|
155
|
+
return { kind: 'vary' };
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
if ((receiver === 'headers' || receiver === 'httpHeaders') &&
|
|
159
|
+
method === 'setCacheControl') {
|
|
160
|
+
return { kind: 'cache-public', value: 'HttpHeaders.setCacheControl(...)' };
|
|
161
|
+
}
|
|
162
|
+
if ((receiver === 'headers' || receiver === 'httpHeaders') &&
|
|
163
|
+
method === 'setVary') {
|
|
164
|
+
return { kind: 'vary' };
|
|
165
|
+
}
|
|
166
|
+
// Auth signals.
|
|
167
|
+
if (receiver === 'request' && method === 'getCookies') {
|
|
168
|
+
return { kind: 'auth' };
|
|
169
|
+
}
|
|
170
|
+
if (receiver === 'request' && method === 'getHeader') {
|
|
171
|
+
const v = (arg0 ?? '').toLowerCase();
|
|
172
|
+
if (v === 'cookie' || v === 'authorization')
|
|
173
|
+
return { kind: 'auth' };
|
|
174
|
+
}
|
|
175
|
+
if ((receiver === 'response' || receiver === 'resp') &&
|
|
176
|
+
method === 'addCookie') {
|
|
177
|
+
return { kind: 'auth' };
|
|
178
|
+
}
|
|
179
|
+
return null;
|
|
180
|
+
}
|
|
181
|
+
return null;
|
|
182
|
+
}
|
|
183
|
+
function authSignalRegex(language) {
|
|
184
|
+
switch (language) {
|
|
185
|
+
case 'javascript':
|
|
186
|
+
case 'typescript':
|
|
187
|
+
return JS_AUTH_SIGNAL_RE;
|
|
188
|
+
case 'python':
|
|
189
|
+
return PY_AUTH_SIGNAL_RE;
|
|
190
|
+
case 'go':
|
|
191
|
+
return GO_AUTH_SIGNAL_RE;
|
|
192
|
+
case 'java':
|
|
193
|
+
return JAVA_AUTH_SIGNAL_RE;
|
|
194
|
+
default:
|
|
195
|
+
return null;
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
function scanWindow(code, language, startLine, endLine) {
|
|
199
|
+
const lines = code.split('\n');
|
|
200
|
+
const lo = Math.max(0, startLine - 1);
|
|
201
|
+
const hi = Math.min(lines.length, endLine);
|
|
202
|
+
const out = { vary: false, auth: false };
|
|
203
|
+
const authRe = authSignalRegex(language);
|
|
204
|
+
for (let i = lo; i < hi; i++) {
|
|
205
|
+
const ln = lines[i];
|
|
206
|
+
if (authRe && authRe.test(ln))
|
|
207
|
+
out.auth = true;
|
|
208
|
+
if (language === 'python') {
|
|
209
|
+
if (!out.cachePublic) {
|
|
210
|
+
const mc = PY_CACHE_HEADER_ASSIGN_RE.exec(ln);
|
|
211
|
+
if (mc && isSharedCacheable(mc[2])) {
|
|
212
|
+
out.cachePublic = { line: i + 1, value: mc[2] };
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
if (!out.cachePublic) {
|
|
216
|
+
const md = PY_CACHE_CONTROL_DECORATOR_RE.exec(ln);
|
|
217
|
+
if (md) {
|
|
218
|
+
const argTxt = md[1];
|
|
219
|
+
if (/\bpublic\s*=\s*True\b/.test(argTxt) &&
|
|
220
|
+
(/\bmax_age\s*=\s*[1-9]\d*\b/.test(argTxt) || !/max_age/.test(argTxt))) {
|
|
221
|
+
out.cachePublic = { line: i + 1, value: argTxt };
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
if (!out.vary) {
|
|
226
|
+
const mv = PY_VARY_HEADER_ASSIGN_RE.exec(ln);
|
|
227
|
+
if (mv && isVaryCovering(mv[2]))
|
|
228
|
+
out.vary = true;
|
|
229
|
+
if (PY_VARY_DECORATOR_RE.test(ln))
|
|
230
|
+
out.vary = true;
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
return out;
|
|
235
|
+
}
|
|
236
|
+
export class CacheNoVaryPass {
|
|
237
|
+
name = 'cache-no-vary';
|
|
238
|
+
category = 'security';
|
|
239
|
+
run(ctx) {
|
|
240
|
+
const { graph, language, code } = ctx;
|
|
241
|
+
const file = graph.ir.meta.file;
|
|
242
|
+
const findings = [];
|
|
243
|
+
const isSupported = language === 'javascript' ||
|
|
244
|
+
language === 'typescript' ||
|
|
245
|
+
language === 'python' ||
|
|
246
|
+
language === 'go' ||
|
|
247
|
+
language === 'java';
|
|
248
|
+
if (!isSupported)
|
|
249
|
+
return { findings };
|
|
250
|
+
// Skip test / spec files — low-FP guardrail.
|
|
251
|
+
if (/(?:\.test|\.spec)\.[jt]sx?$/i.test(file) ||
|
|
252
|
+
/__tests__\/|\/tests?\//i.test(file)) {
|
|
253
|
+
return { findings };
|
|
254
|
+
}
|
|
255
|
+
// Group calls by handler.
|
|
256
|
+
const callsByHandler = new Map();
|
|
257
|
+
for (const call of graph.ir.calls) {
|
|
258
|
+
const key = call.in_method ?? '<top>';
|
|
259
|
+
let arr = callsByHandler.get(key);
|
|
260
|
+
if (!arr) {
|
|
261
|
+
arr = [];
|
|
262
|
+
callsByHandler.set(key, arr);
|
|
263
|
+
}
|
|
264
|
+
arr.push(call);
|
|
265
|
+
}
|
|
266
|
+
const emit = (line, handler, cacheValue) => {
|
|
267
|
+
if (findings.some((f) => f.line === line && f.handler === handler))
|
|
268
|
+
return;
|
|
269
|
+
findings.push({ line, language, handler, cacheValue });
|
|
270
|
+
ctx.addFinding({
|
|
271
|
+
id: `${this.name}-${file}-${line}`,
|
|
272
|
+
pass: this.name,
|
|
273
|
+
category: this.category,
|
|
274
|
+
rule_id: this.name,
|
|
275
|
+
cwe: 'CWE-524',
|
|
276
|
+
severity: 'medium',
|
|
277
|
+
level: 'warning',
|
|
278
|
+
message: `Response sets a shared-cacheable Cache-Control ('${cacheValue}') in ` +
|
|
279
|
+
`a handler that reads authenticated or user-scoped state, but does ` +
|
|
280
|
+
`not set 'Vary: Cookie' or 'Vary: Authorization'. A shared cache ` +
|
|
281
|
+
`(CDN, reverse proxy, ISP cache) keys the response by URL only and ` +
|
|
282
|
+
`may serve one user's body to another. (CWE-524)`,
|
|
283
|
+
file,
|
|
284
|
+
line,
|
|
285
|
+
fix: `Either add 'Vary: Cookie' (or 'Vary: Authorization') so caches key ` +
|
|
286
|
+
`on the user identity, or change the directive to 'private' / ` +
|
|
287
|
+
`'no-store' so the response is never shared-cached.`,
|
|
288
|
+
evidence: {
|
|
289
|
+
language,
|
|
290
|
+
handler: handler ?? '<top>',
|
|
291
|
+
cacheValue,
|
|
292
|
+
},
|
|
293
|
+
});
|
|
294
|
+
};
|
|
295
|
+
for (const [handlerKey, calls] of callsByHandler) {
|
|
296
|
+
const handler = handlerKey === '<top>' ? null : handlerKey;
|
|
297
|
+
const cachePublicHits = [];
|
|
298
|
+
let varyFromCalls = false;
|
|
299
|
+
let authFromCalls = false;
|
|
300
|
+
for (const call of calls) {
|
|
301
|
+
const cls = classifyCall(call, language);
|
|
302
|
+
if (!cls)
|
|
303
|
+
continue;
|
|
304
|
+
if (cls.kind === 'cache-public') {
|
|
305
|
+
cachePublicHits.push({
|
|
306
|
+
line: call.location.line,
|
|
307
|
+
value: cls.value ?? '',
|
|
308
|
+
});
|
|
309
|
+
}
|
|
310
|
+
else if (cls.kind === 'vary') {
|
|
311
|
+
varyFromCalls = true;
|
|
312
|
+
}
|
|
313
|
+
else if (cls.kind === 'auth') {
|
|
314
|
+
authFromCalls = true;
|
|
315
|
+
}
|
|
316
|
+
}
|
|
317
|
+
// Compute a widened source-text window around this handler's calls.
|
|
318
|
+
let minLine = Infinity;
|
|
319
|
+
let maxLine = -Infinity;
|
|
320
|
+
for (const c of calls) {
|
|
321
|
+
if (c.location?.line) {
|
|
322
|
+
minLine = Math.min(minLine, c.location.line);
|
|
323
|
+
maxLine = Math.max(maxLine, c.location.line);
|
|
324
|
+
}
|
|
325
|
+
}
|
|
326
|
+
if (minLine === Infinity)
|
|
327
|
+
continue;
|
|
328
|
+
const winStart = Math.max(1, minLine - 5);
|
|
329
|
+
const winEnd = maxLine + 5;
|
|
330
|
+
const winScan = scanWindow(code, language, winStart, winEnd);
|
|
331
|
+
if (winScan.cachePublic)
|
|
332
|
+
cachePublicHits.push(winScan.cachePublic);
|
|
333
|
+
const vary = varyFromCalls || winScan.vary;
|
|
334
|
+
const auth = authFromCalls || winScan.auth;
|
|
335
|
+
if (cachePublicHits.length === 0)
|
|
336
|
+
continue;
|
|
337
|
+
if (vary)
|
|
338
|
+
continue;
|
|
339
|
+
if (!auth)
|
|
340
|
+
continue;
|
|
341
|
+
// Emit one finding per handler (use first cache-public location).
|
|
342
|
+
emit(cachePublicHits[0].line, handler, cachePublicHits[0].value);
|
|
343
|
+
}
|
|
344
|
+
return { findings };
|
|
345
|
+
}
|
|
346
|
+
}
|
|
347
|
+
//# sourceMappingURL=cache-no-vary-pass.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cache-no-vary-pass.js","sourceRoot":"","sources":["../../../src/analysis/passes/cache-no-vary-pass.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"language-sources-pass.d.ts","sourceRoot":"","sources":["../../../src/analysis/passes/language-sources-pass.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAEH,OAAO,KAAK,EAAE,WAAW,EAAE,SAAS,EAAE,cAAc,EAAwB,WAAW,EAAO,MAAM,sBAAsB,CAAC;AAC3H,OAAO,KAAK,EAAE,YAAY,EAAE,WAAW,EAAE,MAAM,8BAA8B,CAAC;AAqB9E,eAAO,MAAM,mBAAmB;;;;;;;;;;;;;;;;;;;;;;;;;;;IA0C/B,CAAC;AA4BF,MAAM,WAAW,qBAAqB;IACpC,iBAAiB,EAAE,WAAW,EAAE,CAAC;IACjC,eAAe,EAAE,SAAS,EAAE,CAAC;IAC7B;;;;OAIG;IACH,oBAAoB,EAAE,cAAc,EAAE,CAAC;IACvC;;;OAGG;IACH,aAAa,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACnC;;;OAGG;IACH,eAAe,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC;IAC7B;;;OAGG;IACH,aAAa,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CACpC;AAMD,qBAAa,mBAAoB,YAAW,YAAY,CAAC,qBAAqB,CAAC;IAC7E,QAAQ,CAAC,IAAI,sBAAsB;IACnC,QAAQ,CAAC,QAAQ,EAAG,UAAU,CAAU;IAExC,GAAG,CAAC,GAAG,EAAE,WAAW,GAAG,qBAAqB;CAoG7C;
|
|
1
|
+
{"version":3,"file":"language-sources-pass.d.ts","sourceRoot":"","sources":["../../../src/analysis/passes/language-sources-pass.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAEH,OAAO,KAAK,EAAE,WAAW,EAAE,SAAS,EAAE,cAAc,EAAwB,WAAW,EAAO,MAAM,sBAAsB,CAAC;AAC3H,OAAO,KAAK,EAAE,YAAY,EAAE,WAAW,EAAE,MAAM,8BAA8B,CAAC;AAqB9E,eAAO,MAAM,mBAAmB;;;;;;;;;;;;;;;;;;;;;;;;;;;IA0C/B,CAAC;AA4BF,MAAM,WAAW,qBAAqB;IACpC,iBAAiB,EAAE,WAAW,EAAE,CAAC;IACjC,eAAe,EAAE,SAAS,EAAE,CAAC;IAC7B;;;;OAIG;IACH,oBAAoB,EAAE,cAAc,EAAE,CAAC;IACvC;;;OAGG;IACH,aAAa,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACnC;;;OAGG;IACH,eAAe,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC;IAC7B;;;OAGG;IACH,aAAa,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CACpC;AAMD,qBAAa,mBAAoB,YAAW,YAAY,CAAC,qBAAqB,CAAC;IAC7E,QAAQ,CAAC,IAAI,sBAAsB;IACnC,QAAQ,CAAC,QAAQ,EAAG,UAAU,CAAU;IAExC,GAAG,CAAC,GAAG,EAAE,WAAW,GAAG,qBAAqB;CAoG7C;AA8hBD,wBAAgB,sBAAsB,CAAC,UAAU,EAAE,MAAM,GAAG,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAkG9E;AAED,wBAAgB,wBAAwB,CAAC,UAAU,EAAE,MAAM,EAAE,aAAa,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,GAAG,CAAC,MAAM,CAAC,CAwC5G;AAED,wBAAgB,iCAAiC,CAC/C,UAAU,EAAE,MAAM,EAClB,WAAW,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,GAC/B,KAAK,CAAC;IAAE,UAAU,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAA;CAAE,CAAC,CAoBjD;AA6DD,wBAAgB,0BAA0B,CAAC,UAAU,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAmBpG;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,oBAAoB,CAClC,UAAU,EAAE,MAAM,EAClB,QAAQ,EAAE,GAAG,CAAC,MAAM,CAAC,GACpB,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAmCrB;AAmKD,wBAAgB,uBAAuB,CAAC,UAAU,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,WAAW,EAAE,CA0GvF"}
|
|
@@ -359,6 +359,26 @@ function findOopFieldReadSources(types, sourceCode, language) {
|
|
|
359
359
|
// both single-line Java getters (`public String getName() { return this.name; }`)
|
|
360
360
|
// and multi-line getters / @property bodies.
|
|
361
361
|
const returnRe = new RegExp(`\\breturn\\s+${SELF}\\.([A-Za-z_]\\w*)\\s*[;}]?`);
|
|
362
|
+
// cognium-dev #105 (Sprint 21 B.1) — recognise allowlist-style
|
|
363
|
+
// guards inside the getter body. When the getter contains an
|
|
364
|
+
// `if <ref> (not in|in) <UPPER_CONST>:` membership check followed
|
|
365
|
+
// within ≤2 lines by `raise` / `abort` / `return None` / `return ""`,
|
|
366
|
+
// treat the getter as a sanitizer rather than a taint source: the
|
|
367
|
+
// unmatched-host branch raises, so the value that flows past the
|
|
368
|
+
// guard is constrained to the allowlist. Conventional UPPER_SNAKE
|
|
369
|
+
// constant naming distinguishes a true allowlist from an incidental
|
|
370
|
+
// cache lookup (e.g. `if x in self.CACHE: return self.url` — the
|
|
371
|
+
// GUARD.1-noisy fixture).
|
|
372
|
+
// Python: `if [self.]url not in self.ALLOWED:` + `raise`/`abort`/`return None`
|
|
373
|
+
// Java/JS: `if (!ALLOWED.contains(x))` / `!ALLOWED.includes(x)`/`!ALLOWED.has(x)`
|
|
374
|
+
// + `throw`/`return null`
|
|
375
|
+
const guardRePy = /\bif\s+[\w.]+\s+(?:not\s+)?in\s+(?:self\.)?[A-Z_][A-Z0-9_]*\s*:/;
|
|
376
|
+
const guardThrowRePy = /^\s*(?:raise\b|abort\b|return\s+(?:None\b|''|""|\)?$))/;
|
|
377
|
+
const guardReJv = /\bif\s*\(\s*!\s*(?:this\.)?[A-Z_][A-Z0-9_]*\s*\.\s*(?:contains|includes|has|matches)\s*\(/;
|
|
378
|
+
const guardThrowReJv = /^\s*(?:throw\b|return\s+null\b)/;
|
|
379
|
+
const guardRe = isPython ? guardRePy : guardReJv;
|
|
380
|
+
const guardThrowRe = isPython ? guardThrowRePy : guardThrowReJv;
|
|
381
|
+
let hasAllowlistGuard = false;
|
|
362
382
|
for (let i = mStart - 1; i < Math.min(mEnd, lines.length); i++) {
|
|
363
383
|
const raw = lines[i] ?? '';
|
|
364
384
|
const trimmed = raw.trim();
|
|
@@ -376,8 +396,18 @@ function findOopFieldReadSources(types, sourceCode, language) {
|
|
|
376
396
|
returnStatementCount = 99;
|
|
377
397
|
break;
|
|
378
398
|
}
|
|
399
|
+
// Allowlist guard detection — look ahead ≤2 lines for the throw/raise.
|
|
400
|
+
if (guardRe.test(trimmed)) {
|
|
401
|
+
for (let j = i + 1; j < Math.min(i + 4, mEnd, lines.length); j++) {
|
|
402
|
+
const next = (lines[j] ?? '');
|
|
403
|
+
if (guardThrowRe.test(next)) {
|
|
404
|
+
hasAllowlistGuard = true;
|
|
405
|
+
break;
|
|
406
|
+
}
|
|
407
|
+
}
|
|
408
|
+
}
|
|
379
409
|
}
|
|
380
|
-
if (returnStatementCount === 1 && returnedField && fieldTaint.has(returnedField)) {
|
|
410
|
+
if (returnStatementCount === 1 && returnedField && fieldTaint.has(returnedField) && !hasAllowlistGuard) {
|
|
381
411
|
const fieldInfo = fieldTaint.get(returnedField);
|
|
382
412
|
// Java: caller writes `getName()` / `this.getName()` / `obj.getName()`.
|
|
383
413
|
// → match `\bgetName\b`.
|