circle-ir 4.15.0 → 4.16.1
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/configs/sinks/golang.json +4 -4
- package/dist/analysis/config-loader.d.ts.map +1 -1
- package/dist/analysis/config-loader.js +11 -7
- package/dist/analysis/config-loader.js.map +1 -1
- package/dist/analysis/constant-caller-gate.d.ts +39 -0
- package/dist/analysis/constant-caller-gate.d.ts.map +1 -0
- package/dist/analysis/constant-caller-gate.js +298 -0
- package/dist/analysis/constant-caller-gate.js.map +1 -0
- package/dist/analysis/constant-propagation/evaluator.d.ts.map +1 -1
- package/dist/analysis/constant-propagation/evaluator.js +3 -1
- package/dist/analysis/constant-propagation/evaluator.js.map +1 -1
- package/dist/analysis/constant-propagation/patterns.d.ts +19 -0
- package/dist/analysis/constant-propagation/patterns.d.ts.map +1 -1
- package/dist/analysis/constant-propagation/patterns.js +33 -0
- package/dist/analysis/constant-propagation/patterns.js.map +1 -1
- package/dist/analysis/constant-propagation/propagator.d.ts +59 -0
- package/dist/analysis/constant-propagation/propagator.d.ts.map +1 -1
- package/dist/analysis/constant-propagation/propagator.js +396 -49
- package/dist/analysis/constant-propagation/propagator.js.map +1 -1
- package/dist/analysis/constant-propagation/types.d.ts +9 -0
- package/dist/analysis/constant-propagation/types.d.ts.map +1 -1
- package/dist/analysis/findings.d.ts.map +1 -1
- package/dist/analysis/findings.js +134 -0
- package/dist/analysis/findings.js.map +1 -1
- package/dist/analysis/interprocedural.d.ts.map +1 -1
- package/dist/analysis/interprocedural.js +127 -3
- package/dist/analysis/interprocedural.js.map +1 -1
- package/dist/analysis/java-array-element-keys.d.ts +14 -0
- package/dist/analysis/java-array-element-keys.d.ts.map +1 -0
- package/dist/analysis/java-array-element-keys.js +36 -0
- package/dist/analysis/java-array-element-keys.js.map +1 -0
- package/dist/analysis/js-crlf-strip.d.ts +26 -0
- package/dist/analysis/js-crlf-strip.d.ts.map +1 -0
- package/dist/analysis/js-crlf-strip.js +244 -0
- package/dist/analysis/js-crlf-strip.js.map +1 -0
- package/dist/analysis/passes/csharp-sanitizer-credit.d.ts +25 -0
- package/dist/analysis/passes/csharp-sanitizer-credit.d.ts.map +1 -1
- package/dist/analysis/passes/csharp-sanitizer-credit.js +157 -12
- package/dist/analysis/passes/csharp-sanitizer-credit.js.map +1 -1
- package/dist/analysis/passes/language-sources-pass.d.ts +38 -3
- package/dist/analysis/passes/language-sources-pass.d.ts.map +1 -1
- package/dist/analysis/passes/language-sources-pass.js +1234 -127
- package/dist/analysis/passes/language-sources-pass.js.map +1 -1
- package/dist/analysis/passes/param-source-flow-gate-pass.d.ts.map +1 -1
- package/dist/analysis/passes/param-source-flow-gate-pass.js +53 -1
- package/dist/analysis/passes/param-source-flow-gate-pass.js.map +1 -1
- package/dist/analysis/passes/plaintext-password-storage-pass.d.ts +6 -0
- package/dist/analysis/passes/plaintext-password-storage-pass.d.ts.map +1 -1
- package/dist/analysis/passes/plaintext-password-storage-pass.js +160 -0
- package/dist/analysis/passes/plaintext-password-storage-pass.js.map +1 -1
- package/dist/analysis/passes/scan-secrets-pass.d.ts +1 -1
- package/dist/analysis/passes/scan-secrets-pass.d.ts.map +1 -1
- package/dist/analysis/passes/scan-secrets-pass.js +7 -1
- package/dist/analysis/passes/scan-secrets-pass.js.map +1 -1
- package/dist/analysis/passes/sink-filter-pass.d.ts +1 -1
- package/dist/analysis/passes/sink-filter-pass.d.ts.map +1 -1
- package/dist/analysis/passes/sink-filter-pass.js +326 -34
- package/dist/analysis/passes/sink-filter-pass.js.map +1 -1
- package/dist/analysis/passes/taint-matcher-pass.d.ts.map +1 -1
- package/dist/analysis/passes/taint-matcher-pass.js +2 -2
- package/dist/analysis/passes/taint-matcher-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 +1376 -58
- package/dist/analysis/passes/taint-propagation-pass.js.map +1 -1
- package/dist/analysis/passes/weak-hash-pass.d.ts +6 -0
- package/dist/analysis/passes/weak-hash-pass.d.ts.map +1 -1
- package/dist/analysis/passes/weak-hash-pass.js +191 -4
- package/dist/analysis/passes/weak-hash-pass.js.map +1 -1
- package/dist/analysis/passes/weak-random-pass.d.ts +5 -0
- package/dist/analysis/passes/weak-random-pass.d.ts.map +1 -1
- package/dist/analysis/passes/weak-random-pass.js +30 -0
- package/dist/analysis/passes/weak-random-pass.js.map +1 -1
- package/dist/analysis/project-callee-escape-gate.d.ts +40 -0
- package/dist/analysis/project-callee-escape-gate.d.ts.map +1 -0
- package/dist/analysis/project-callee-escape-gate.js +243 -0
- package/dist/analysis/project-callee-escape-gate.js.map +1 -0
- package/dist/analysis/rust-literal-guard-rebind.d.ts +34 -0
- package/dist/analysis/rust-literal-guard-rebind.d.ts.map +1 -0
- package/dist/analysis/rust-literal-guard-rebind.js +197 -0
- package/dist/analysis/rust-literal-guard-rebind.js.map +1 -0
- package/dist/analysis/rust-literal-guard.d.ts +33 -0
- package/dist/analysis/rust-literal-guard.d.ts.map +1 -0
- package/dist/analysis/rust-literal-guard.js +422 -0
- package/dist/analysis/rust-literal-guard.js.map +1 -0
- package/dist/analysis/scalar-derivation.d.ts +8 -0
- package/dist/analysis/scalar-derivation.d.ts.map +1 -1
- package/dist/analysis/scalar-derivation.js +172 -0
- package/dist/analysis/scalar-derivation.js.map +1 -1
- package/dist/analysis/taint-matcher.d.ts +2 -2
- package/dist/analysis/taint-matcher.d.ts.map +1 -1
- package/dist/analysis/taint-matcher.js +531 -31
- package/dist/analysis/taint-matcher.js.map +1 -1
- package/dist/analysis/taint-propagation.d.ts +26 -1
- package/dist/analysis/taint-propagation.d.ts.map +1 -1
- package/dist/analysis/taint-propagation.js +234 -13
- package/dist/analysis/taint-propagation.js.map +1 -1
- package/dist/analyzer.d.ts +16 -2
- package/dist/analyzer.d.ts.map +1 -1
- package/dist/analyzer.js +13 -3
- package/dist/analyzer.js.map +1 -1
- package/dist/browser/circle-ir.js +24432 -19928
- package/dist/core/circle-ir-core.cjs +4936 -3575
- package/dist/core/circle-ir-core.js +4936 -3575
- package/dist/core/extractors/calls.d.ts +12 -1
- package/dist/core/extractors/calls.d.ts.map +1 -1
- package/dist/core/extractors/calls.js +332 -22
- package/dist/core/extractors/calls.js.map +1 -1
- package/dist/core/extractors/dfg.js +102 -10
- package/dist/core/extractors/dfg.js.map +1 -1
- package/dist/core/extractors/types.d.ts +5 -1
- package/dist/core/extractors/types.d.ts.map +1 -1
- package/dist/core/extractors/types.js +62 -0
- package/dist/core/extractors/types.js.map +1 -1
- package/dist/graph/analysis-pass.d.ts +6 -1
- package/dist/graph/analysis-pass.d.ts.map +1 -1
- package/dist/graph/analysis-pass.js +2 -1
- package/dist/graph/analysis-pass.js.map +1 -1
- package/dist/navigation/navigation-index.d.ts +134 -14
- package/dist/navigation/navigation-index.d.ts.map +1 -1
- package/dist/navigation/navigation-index.js +1158 -104
- package/dist/navigation/navigation-index.js.map +1 -1
- package/dist/navigation/types.d.ts +59 -2
- package/dist/navigation/types.d.ts.map +1 -1
- package/dist/resolution/cross-file.d.ts +9 -0
- package/dist/resolution/cross-file.d.ts.map +1 -1
- package/dist/resolution/cross-file.js +102 -43
- package/dist/resolution/cross-file.js.map +1 -1
- package/dist/resolution/type-hierarchy.d.ts +3 -0
- package/dist/resolution/type-hierarchy.d.ts.map +1 -1
- package/dist/resolution/type-hierarchy.js +45 -1
- package/dist/resolution/type-hierarchy.js.map +1 -1
- package/dist/types/index.d.ts +33 -0
- package/dist/types/index.d.ts.map +1 -1
- package/dist/types/index.js.map +1 -1
- package/package.json +1 -1
|
@@ -0,0 +1,244 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Whether a JavaScript / TypeScript `String.replace` chain strips CR and LF
|
|
3
|
+
* (cognium-dev#562, the JS half of the Java rule in `java-crlf-strip.ts`).
|
|
4
|
+
*
|
|
5
|
+
* The decision is made by running the chain's own literal patterns against a
|
|
6
|
+
* short probe string, so a regex is judged by what it matches rather than by how
|
|
7
|
+
* it is spelled (`/[\r\n]/g`, `/\r|\n/g`, `/\s+/g` and `/[\x00-\x1f]/g` all
|
|
8
|
+
* strip both). Only literal arguments are understood: a pattern held in a
|
|
9
|
+
* variable, a `new RegExp(...)`, or a replacement that is a function or a
|
|
10
|
+
* non-literal makes the whole chain `undecided`, and the caller keeps the credit
|
|
11
|
+
* the class-less `replace` row gave before.
|
|
12
|
+
*/
|
|
13
|
+
const MAX_PATTERN_LENGTH = 200;
|
|
14
|
+
/** String methods that may follow a replace in a judged chain without changing its CR / LF coverage. */
|
|
15
|
+
const NEUTRAL_AFTER_REPLACE = new Set([
|
|
16
|
+
'replace', 'replaceAll', 'trim', 'trimStart', 'trimEnd', 'toString',
|
|
17
|
+
'toLowerCase', 'toUpperCase', 'slice', 'substring',
|
|
18
|
+
]);
|
|
19
|
+
/** Probe text: lone and doubled CR / LF, a CRLF pair and neutral characters. */
|
|
20
|
+
const PROBE = 'a\r\rb\n\nc\r\nd\n\re x\t';
|
|
21
|
+
function readQuoted(s, i) {
|
|
22
|
+
const q = s[i];
|
|
23
|
+
if (q !== '"' && q !== "'" && q !== '`')
|
|
24
|
+
return null;
|
|
25
|
+
let out = '';
|
|
26
|
+
for (let j = i + 1; j < s.length; j++) {
|
|
27
|
+
const c = s[j];
|
|
28
|
+
if (c === q)
|
|
29
|
+
return { value: out, end: j + 1 };
|
|
30
|
+
if (q === '`' && c === '$' && s[j + 1] === '{')
|
|
31
|
+
return null;
|
|
32
|
+
if (c === '\\') {
|
|
33
|
+
const n = s[j + 1];
|
|
34
|
+
if (n === undefined)
|
|
35
|
+
return null;
|
|
36
|
+
switch (n) {
|
|
37
|
+
case 'n':
|
|
38
|
+
out += '\n';
|
|
39
|
+
j++;
|
|
40
|
+
continue;
|
|
41
|
+
case 'r':
|
|
42
|
+
out += '\r';
|
|
43
|
+
j++;
|
|
44
|
+
continue;
|
|
45
|
+
case 't':
|
|
46
|
+
out += '\t';
|
|
47
|
+
j++;
|
|
48
|
+
continue;
|
|
49
|
+
case '0':
|
|
50
|
+
out += '\0';
|
|
51
|
+
j++;
|
|
52
|
+
continue;
|
|
53
|
+
case 'x': {
|
|
54
|
+
const h = /^[0-9a-fA-F]{2}/.exec(s.slice(j + 2, j + 4));
|
|
55
|
+
if (!h)
|
|
56
|
+
return null;
|
|
57
|
+
out += String.fromCharCode(parseInt(h[0], 16));
|
|
58
|
+
j += 3;
|
|
59
|
+
continue;
|
|
60
|
+
}
|
|
61
|
+
case 'u': {
|
|
62
|
+
const h = /^[0-9a-fA-F]{4}/.exec(s.slice(j + 2, j + 6));
|
|
63
|
+
if (!h)
|
|
64
|
+
return null;
|
|
65
|
+
out += String.fromCharCode(parseInt(h[0], 16));
|
|
66
|
+
j += 5;
|
|
67
|
+
continue;
|
|
68
|
+
}
|
|
69
|
+
default:
|
|
70
|
+
out += n;
|
|
71
|
+
j++;
|
|
72
|
+
continue;
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
if (c === '\n' && q !== '`')
|
|
76
|
+
return null;
|
|
77
|
+
out += c;
|
|
78
|
+
}
|
|
79
|
+
return null;
|
|
80
|
+
}
|
|
81
|
+
function readRegexLiteral(s, i) {
|
|
82
|
+
if (s[i] !== '/')
|
|
83
|
+
return null;
|
|
84
|
+
let inClass = false;
|
|
85
|
+
for (let j = i + 1; j < s.length; j++) {
|
|
86
|
+
const c = s[j];
|
|
87
|
+
if (c === '\n')
|
|
88
|
+
return null;
|
|
89
|
+
if (c === '\\') {
|
|
90
|
+
j++;
|
|
91
|
+
continue;
|
|
92
|
+
}
|
|
93
|
+
if (inClass) {
|
|
94
|
+
if (c === ']')
|
|
95
|
+
inClass = false;
|
|
96
|
+
continue;
|
|
97
|
+
}
|
|
98
|
+
if (c === '[') {
|
|
99
|
+
inClass = true;
|
|
100
|
+
continue;
|
|
101
|
+
}
|
|
102
|
+
if (c === '/') {
|
|
103
|
+
const flags = /^[dgimsuvy]*/.exec(s.slice(j + 1))[0];
|
|
104
|
+
return { source: s.slice(i + 1, j), flags, end: j + 1 + flags.length };
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
return null;
|
|
108
|
+
}
|
|
109
|
+
const skipWs = (s, i) => {
|
|
110
|
+
while (i < s.length && /\s/.test(s[i]))
|
|
111
|
+
i++;
|
|
112
|
+
return i;
|
|
113
|
+
};
|
|
114
|
+
/**
|
|
115
|
+
* Parse every `.replace(<literal>, <string literal>)` / `.replaceAll(...)` step
|
|
116
|
+
* of a chain, in source order. Returns null when any step is not literal.
|
|
117
|
+
*/
|
|
118
|
+
function parseSteps(chainText) {
|
|
119
|
+
const steps = [];
|
|
120
|
+
const callRe = /\.\s*(replaceAll|replace)\s*\(/g;
|
|
121
|
+
let m;
|
|
122
|
+
while ((m = callRe.exec(chainText)) !== null) {
|
|
123
|
+
const kind = m[1];
|
|
124
|
+
let i = skipWs(chainText, m.index + m[0].length);
|
|
125
|
+
let pattern;
|
|
126
|
+
const rx = readRegexLiteral(chainText, i);
|
|
127
|
+
if (rx) {
|
|
128
|
+
if (rx.source.length > MAX_PATTERN_LENGTH)
|
|
129
|
+
return null;
|
|
130
|
+
try {
|
|
131
|
+
pattern = new RegExp(rx.source, rx.flags);
|
|
132
|
+
}
|
|
133
|
+
catch {
|
|
134
|
+
return null;
|
|
135
|
+
}
|
|
136
|
+
i = rx.end;
|
|
137
|
+
}
|
|
138
|
+
else {
|
|
139
|
+
const str = readQuoted(chainText, i);
|
|
140
|
+
if (!str)
|
|
141
|
+
return null;
|
|
142
|
+
pattern = str.value;
|
|
143
|
+
i = str.end;
|
|
144
|
+
}
|
|
145
|
+
i = skipWs(chainText, i);
|
|
146
|
+
if (chainText[i] !== ',')
|
|
147
|
+
return null;
|
|
148
|
+
i = skipWs(chainText, i + 1);
|
|
149
|
+
const rep = readQuoted(chainText, i);
|
|
150
|
+
if (!rep)
|
|
151
|
+
return null;
|
|
152
|
+
i = skipWs(chainText, rep.end);
|
|
153
|
+
if (chainText[i] !== ')')
|
|
154
|
+
return null;
|
|
155
|
+
steps.push({ kind, pattern, replacement: rep.value });
|
|
156
|
+
callRe.lastIndex = i + 1;
|
|
157
|
+
}
|
|
158
|
+
return steps;
|
|
159
|
+
}
|
|
160
|
+
function run(steps, input) {
|
|
161
|
+
let s = input;
|
|
162
|
+
try {
|
|
163
|
+
for (const st of steps) {
|
|
164
|
+
if (st.kind === 'replaceAll' && st.pattern instanceof RegExp && !st.pattern.global)
|
|
165
|
+
return null;
|
|
166
|
+
s = st.kind === 'replaceAll'
|
|
167
|
+
? s.replaceAll(st.pattern, st.replacement)
|
|
168
|
+
: s.replace(st.pattern, st.replacement);
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
catch {
|
|
172
|
+
return null;
|
|
173
|
+
}
|
|
174
|
+
return s;
|
|
175
|
+
}
|
|
176
|
+
/**
|
|
177
|
+
* Verdict for the replace chain whose text is `chainText` (the receiver plus the
|
|
178
|
+
* `.method(...)` segments after it).
|
|
179
|
+
*/
|
|
180
|
+
export function jsReplaceChainVerdict(chainText) {
|
|
181
|
+
const steps = parseSteps(chainText);
|
|
182
|
+
if (!steps || steps.length === 0)
|
|
183
|
+
return 'undecided';
|
|
184
|
+
// Another transform after the first replace may strip CR / LF on its own
|
|
185
|
+
// (`.split("\r").join("")`): the chain is only judged when every later
|
|
186
|
+
// segment is a replace or a harmless string method.
|
|
187
|
+
const first = chainText.search(/\.\s*(?:replaceAll|replace)\s*\(/);
|
|
188
|
+
for (const seg of chainText.slice(first + 1).matchAll(/\.\s*([A-Za-z_$][\w$]*)\s*\(/g)) {
|
|
189
|
+
if (!NEUTRAL_AFTER_REPLACE.has(seg[1]))
|
|
190
|
+
return 'undecided';
|
|
191
|
+
}
|
|
192
|
+
const out = run(steps, PROBE);
|
|
193
|
+
if (out === null)
|
|
194
|
+
return 'undecided';
|
|
195
|
+
return out.includes('\r') || out.includes('\n') ? 'leaves-crlf' : 'strips-crlf';
|
|
196
|
+
}
|
|
197
|
+
/**
|
|
198
|
+
* The text of the call chain that starts at (line, column): the receiver
|
|
199
|
+
* expression plus the `.method(...)` segments after it, up to the first
|
|
200
|
+
* character that cannot continue the chain at nesting depth 0. String and regex
|
|
201
|
+
* literals are skipped as units. The chain may continue over following lines.
|
|
202
|
+
*/
|
|
203
|
+
export function jsChainTextAt(sourceLines, line, column) {
|
|
204
|
+
const text = [(sourceLines[line - 1] ?? '').slice(column), ...sourceLines.slice(line, line + 7)].join('\n');
|
|
205
|
+
let depth = 0;
|
|
206
|
+
let prevSig = '';
|
|
207
|
+
let i = 0;
|
|
208
|
+
for (; i < text.length; i++) {
|
|
209
|
+
const c = text[i];
|
|
210
|
+
if (c === '"' || c === "'" || c === '`') {
|
|
211
|
+
for (i++; i < text.length && text[i] !== c; i++)
|
|
212
|
+
if (text[i] === '\\')
|
|
213
|
+
i++;
|
|
214
|
+
prevSig = c;
|
|
215
|
+
continue;
|
|
216
|
+
}
|
|
217
|
+
if (c === '/' && depth > 0 && (prevSig === '(' || prevSig === ',')) {
|
|
218
|
+
const rx = readRegexLiteral(text, i);
|
|
219
|
+
if (rx) {
|
|
220
|
+
i = rx.end - 1;
|
|
221
|
+
prevSig = '/';
|
|
222
|
+
continue;
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
if (c === '(' || c === '[' || c === '{') {
|
|
226
|
+
depth++;
|
|
227
|
+
prevSig = c;
|
|
228
|
+
continue;
|
|
229
|
+
}
|
|
230
|
+
if (c === ')' || c === ']' || c === '}') {
|
|
231
|
+
if (depth === 0)
|
|
232
|
+
break;
|
|
233
|
+
depth--;
|
|
234
|
+
prevSig = c;
|
|
235
|
+
continue;
|
|
236
|
+
}
|
|
237
|
+
if (depth === 0 && !/[\w$.\s]/.test(c))
|
|
238
|
+
break;
|
|
239
|
+
if (!/\s/.test(c))
|
|
240
|
+
prevSig = c;
|
|
241
|
+
}
|
|
242
|
+
return text.slice(0, i);
|
|
243
|
+
}
|
|
244
|
+
//# sourceMappingURL=js-crlf-strip.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"js-crlf-strip.js","sourceRoot":"","sources":["../../src/analysis/js-crlf-strip.ts"],"names":[],"mappings":"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"}
|
|
@@ -43,6 +43,11 @@ export declare const CSHARP_ALLOWLIST_STRIP_RE: RegExp;
|
|
|
43
43
|
export declare const CSHARP_SANITIZER_RES: Array<{
|
|
44
44
|
re: RegExp;
|
|
45
45
|
type: string;
|
|
46
|
+
unlessDeclared?: string;
|
|
47
|
+
}>;
|
|
48
|
+
export declare function csharpSanitizersFor(code: string): ReadonlyArray<{
|
|
49
|
+
re: RegExp;
|
|
50
|
+
type: string;
|
|
46
51
|
}>;
|
|
47
52
|
export interface CsMethodRange {
|
|
48
53
|
name: string;
|
|
@@ -54,6 +59,7 @@ export declare class CSharpCredit {
|
|
|
54
59
|
private readonly lines;
|
|
55
60
|
private readonly helperCache;
|
|
56
61
|
private readonly guardHelperCache;
|
|
62
|
+
private readonly sanitizers;
|
|
57
63
|
constructor(code: string, methods: ReadonlyArray<CsMethodRange>);
|
|
58
64
|
/** The innermost method containing `line`, or null. */
|
|
59
65
|
private methodAt;
|
|
@@ -74,6 +80,12 @@ export declare class CSharpCredit {
|
|
|
74
80
|
private returnExprs;
|
|
75
81
|
/** Does `text` apply a sanitizer for `type`, built in or a same-file helper? */
|
|
76
82
|
sanitizes(type: string, text: string): boolean;
|
|
83
|
+
/** Decode applied to a built-in sanitizer call undoes the encode (cognium-dev#736). */
|
|
84
|
+
private rhsDecodeUndoesSanitizer;
|
|
85
|
+
/** True when a value built from `rhs` is still safe for `type` given `sanitized` so far. */
|
|
86
|
+
private rhsKeepsSanitizerCredit;
|
|
87
|
+
/** Assignments and appends in one statement line, in source order. */
|
|
88
|
+
private creditOpsInStatement;
|
|
77
89
|
private sanitizedVarsIn;
|
|
78
90
|
/**
|
|
79
91
|
* The variables sanitized for `type` at `sinkLine`, judged inside the method
|
|
@@ -84,6 +96,19 @@ export declare class CSharpCredit {
|
|
|
84
96
|
* and read in another.
|
|
85
97
|
*/
|
|
86
98
|
sanitizedVarsAt(type: string, sinkLine: number): Set<string>;
|
|
99
|
+
/**
|
|
100
|
+
* Class fields judged across every method that writes them (cognium-dev#808).
|
|
101
|
+
* A field is read in one method and written in others, so each write is
|
|
102
|
+
* judged in its own method with the same in-method credit as a local, and
|
|
103
|
+
* the field is safe for `type` only when every write is: `sanitized` holds
|
|
104
|
+
* the fields with at least one credited write and no uncredited one,
|
|
105
|
+
* `unsanitized` the fields with an uncredited write. A literal write
|
|
106
|
+
* (`_v = "";`, `_v = null;`) is neutral.
|
|
107
|
+
*/
|
|
108
|
+
fieldWrites(type: string, fields: ReadonlySet<string>): {
|
|
109
|
+
sanitized: Set<string>;
|
|
110
|
+
unsanitized: Set<string>;
|
|
111
|
+
};
|
|
87
112
|
/**
|
|
88
113
|
* True when the sink on `sinkLine` also receives a tainted value that no
|
|
89
114
|
* sanitizer covers. In `Write(HtmlEncode(a) + b)` the encoder covers `a`
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"csharp-sanitizer-credit.d.ts","sourceRoot":"","sources":["../../../src/analysis/passes/csharp-sanitizer-credit.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAEH;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,eAAO,MAAM,yBAAyB,QACyE,CAAC;AA8BhH,eAAO,MAAM,oBAAoB,EAAE,KAAK,CAAC;IAAE,EAAE,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,
|
|
1
|
+
{"version":3,"file":"csharp-sanitizer-credit.d.ts","sourceRoot":"","sources":["../../../src/analysis/passes/csharp-sanitizer-credit.ts"],"names":[],"mappings":"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"}
|
|
@@ -69,6 +69,15 @@ export const CSHARP_SANITIZER_RES = [
|
|
|
69
69
|
{ re: /\b(?:HtmlEncode|JavaScriptStringEncode)\s*\(/, type: 'xss' },
|
|
70
70
|
// `HtmlEncoder.Default.Encode(x)` is the ASP.NET Core spelling (#520).
|
|
71
71
|
{ re: /\bHtmlEncoder\s*\.\s*(?:Default\s*\.\s*)?Encode\s*\(/, type: 'xss' },
|
|
72
|
+
// `UrlEncoder.Default.Encode(x)` percent-encodes `<`, `"` and `'` (#564). A
|
|
73
|
+
// project type that declares its own `UrlEncoder` is not the framework's.
|
|
74
|
+
// Qualified `Evil.UrlEncoder` must not match (#736 case 6); only the framework
|
|
75
|
+
// type or an unqualified `UrlEncoder` after `using System.Text.Encodings.Web`.
|
|
76
|
+
{
|
|
77
|
+
re: /(?:System\.Text\.Encodings\.Web\.UrlEncoder|(?<![\w.])UrlEncoder)\s*\.\s*Default\s*\.\s*Encode\s*\(/,
|
|
78
|
+
type: 'xss',
|
|
79
|
+
unlessDeclared: 'UrlEncoder',
|
|
80
|
+
},
|
|
72
81
|
{ re: /\bPath\s*\.\s*GetFileName\s*\(/, type: 'path_traversal' },
|
|
73
82
|
// XML-escapes `< > & " '`. A value escaped this way cannot close the quoted
|
|
74
83
|
// literal it is placed in, which is what XPath injection needs.
|
|
@@ -88,6 +97,31 @@ export const CSHARP_SANITIZER_RES = [
|
|
|
88
97
|
{ re: CSHARP_ALLOWLIST_FILTER_RE, type: 'crlf' },
|
|
89
98
|
...CSHARP_ONE_WAY_HASH_RES.map(re => ({ re, type: 'external_taint_escape' })),
|
|
90
99
|
];
|
|
100
|
+
/**
|
|
101
|
+
* The built-in sanitizers that apply to `code`: those gated on a type name
|
|
102
|
+
* (`unlessDeclared`) are dropped when the file declares a type of that name.
|
|
103
|
+
*/
|
|
104
|
+
const FRAMEWORK_URL_ENCODER = 'System.Text.Encodings.Web.UrlEncoder';
|
|
105
|
+
/** True when `UrlEncoder` in source text is not the framework encoder (#736 case 5). */
|
|
106
|
+
function csharpUrlEncoderShadowed(code) {
|
|
107
|
+
if (new RegExp(`\\b(?:class|struct|record|interface|enum)\\s+UrlEncoder\\b`).test(code)) {
|
|
108
|
+
return true;
|
|
109
|
+
}
|
|
110
|
+
const aliasRe = /\busing\s+UrlEncoder\s*=\s*([^;\n]+)\s*;/g;
|
|
111
|
+
for (const m of code.matchAll(aliasRe)) {
|
|
112
|
+
const target = m[1].trim();
|
|
113
|
+
if (target === FRAMEWORK_URL_ENCODER || target.endsWith('.' + FRAMEWORK_URL_ENCODER))
|
|
114
|
+
continue;
|
|
115
|
+
return true;
|
|
116
|
+
}
|
|
117
|
+
return false;
|
|
118
|
+
}
|
|
119
|
+
export function csharpSanitizersFor(code) {
|
|
120
|
+
const declared = (name) => name === 'UrlEncoder'
|
|
121
|
+
? csharpUrlEncoderShadowed(code)
|
|
122
|
+
: new RegExp(`\\b(?:class|struct|record|interface|enum)\\s+${name}\\b`).test(code);
|
|
123
|
+
return CSHARP_SANITIZER_RES.filter(r => !r.unlessDeclared || !declared(r.unlessDeclared));
|
|
124
|
+
}
|
|
91
125
|
/**
|
|
92
126
|
* Sink types for which a value made only of letters, digits, `_` and `-` is
|
|
93
127
|
* inert: each needs a metacharacter (quote, paren, slash, angle bracket, CR/LF)
|
|
@@ -99,7 +133,11 @@ const INERT_WHEN_ALPHANUMERIC = new Set([
|
|
|
99
133
|
]);
|
|
100
134
|
const IDENT = '[A-Za-z_]\\w*';
|
|
101
135
|
const ASSIGN_RE = new RegExp(`^\\s*(?:var\\s+|[A-Za-z_][\\w.<>\\[\\]?]*\\s+)?(${IDENT})\\s*=(?!=)\\s*(.+?);?\\s*$`);
|
|
136
|
+
const INNER_ASSIGN_RE = new RegExp(`\\b(${IDENT})\\s*=(?!=)\\s*([^;{}]+)`, 'g');
|
|
137
|
+
const APPEND_RE = new RegExp(`^\\s*(${IDENT})\\s*\\+=\\s*(.+?);?\\s*$`);
|
|
102
138
|
const ENDS_STATEMENT_RE = /[;{}]\s*(?:\/\/.*)?$/;
|
|
139
|
+
/** Percent-decoding after encode restores raw bytes (cognium-dev#736 case 4). */
|
|
140
|
+
const CSHARP_DECODE_CALL_HEAD_RE = /\b(?:System\.Net\.)?WebUtility\s*\.\s*UrlDecode\s*\(|HttpUtility\s*\.\s*UrlDecode\s*\(|Uri\s*\.\s*UnescapeDataString\s*\(/g;
|
|
103
141
|
function escapeRegex(s) {
|
|
104
142
|
return s.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
105
143
|
}
|
|
@@ -109,6 +147,30 @@ function withoutStringLiterals(text) {
|
|
|
109
147
|
function mentions(text, name) {
|
|
110
148
|
return new RegExp(`(?<![\\w])${escapeRegex(name)}(?![\\w])`).test(text);
|
|
111
149
|
}
|
|
150
|
+
/** True when `text` still names a variable after literals are stripped (cognium-dev#736). */
|
|
151
|
+
function hasBareIdentifier(text) {
|
|
152
|
+
if (/\$"/.test(text))
|
|
153
|
+
return true;
|
|
154
|
+
return /\b[A-Za-z_]\w*\b/.test(withoutStringLiterals(text));
|
|
155
|
+
}
|
|
156
|
+
/** Identifier tokens in `text` after literals are stripped (cognium-dev#736 case 2). */
|
|
157
|
+
function bareIdentifiers(text) {
|
|
158
|
+
if (/\$"/.test(text))
|
|
159
|
+
return ['__interpolated__'];
|
|
160
|
+
const stripped = withoutStringLiterals(text);
|
|
161
|
+
const out = [];
|
|
162
|
+
for (const m of stripped.matchAll(/\b([A-Za-z_]\w*)\b/g))
|
|
163
|
+
out.push(m[1]);
|
|
164
|
+
return out;
|
|
165
|
+
}
|
|
166
|
+
/** `""`, `null`, `0`, `string.Empty`: a value that carries nothing from input. */
|
|
167
|
+
function isLiteralValue(rhs) {
|
|
168
|
+
if (/\$"/.test(rhs))
|
|
169
|
+
return false;
|
|
170
|
+
const ids = bareIdentifiers(rhs);
|
|
171
|
+
return ids.every(id => id === 'null' || id === 'true' || id === 'false' || id === 'default')
|
|
172
|
+
|| /^\s*(?:string|String)\s*\.\s*Empty\s*$/.test(rhs);
|
|
173
|
+
}
|
|
112
174
|
/** Text inside the parentheses opening at `open`, or null when unbalanced. */
|
|
113
175
|
function insideParens(text, open) {
|
|
114
176
|
let depth = 0;
|
|
@@ -151,8 +213,10 @@ export class CSharpCredit {
|
|
|
151
213
|
lines;
|
|
152
214
|
helperCache = new Map();
|
|
153
215
|
guardHelperCache = new Map();
|
|
216
|
+
sanitizers;
|
|
154
217
|
constructor(code, methods) {
|
|
155
218
|
this.methods = methods;
|
|
219
|
+
this.sanitizers = csharpSanitizersFor(code);
|
|
156
220
|
// CRLF files leave `\r` on every line, which `.` and `$` will not cross.
|
|
157
221
|
this.lines = code.split(/\r?\n/);
|
|
158
222
|
}
|
|
@@ -190,7 +254,7 @@ export class CSharpCredit {
|
|
|
190
254
|
}
|
|
191
255
|
/** Does `text` apply a built-in sanitizer for `type`? */
|
|
192
256
|
builtinSanitizes(type, text) {
|
|
193
|
-
for (const { re, type: t } of
|
|
257
|
+
for (const { re, type: t } of this.sanitizers) {
|
|
194
258
|
if (t === type && re.test(text))
|
|
195
259
|
return true;
|
|
196
260
|
}
|
|
@@ -259,19 +323,60 @@ export class CSharpCredit {
|
|
|
259
323
|
}
|
|
260
324
|
return false;
|
|
261
325
|
}
|
|
326
|
+
/** Decode applied to a built-in sanitizer call undoes the encode (cognium-dev#736). */
|
|
327
|
+
rhsDecodeUndoesSanitizer(type, rhs) {
|
|
328
|
+
for (const m of rhs.matchAll(CSHARP_DECODE_CALL_HEAD_RE)) {
|
|
329
|
+
const open = m.index + m[0].length - 1;
|
|
330
|
+
const inner = insideParens(rhs, open);
|
|
331
|
+
if (inner !== null && this.builtinSanitizes(type, inner))
|
|
332
|
+
return true;
|
|
333
|
+
}
|
|
334
|
+
return false;
|
|
335
|
+
}
|
|
336
|
+
/** True when a value built from `rhs` is still safe for `type` given `sanitized` so far. */
|
|
337
|
+
rhsKeepsSanitizerCredit(type, rhs, sanitized, withHelpers, append) {
|
|
338
|
+
if (!append && this.rhsDecodeUndoesSanitizer(type, rhs))
|
|
339
|
+
return false;
|
|
340
|
+
if (withHelpers ? this.sanitizes(type, rhs) : this.builtinSanitizes(type, rhs))
|
|
341
|
+
return true;
|
|
342
|
+
if (append) {
|
|
343
|
+
if (!hasBareIdentifier(rhs))
|
|
344
|
+
return true;
|
|
345
|
+
return bareIdentifiers(rhs).every(id => sanitized.has(id));
|
|
346
|
+
}
|
|
347
|
+
const ids = bareIdentifiers(rhs);
|
|
348
|
+
return ids.length > 0 && ids.every(id => sanitized.has(id));
|
|
349
|
+
}
|
|
350
|
+
/** Assignments and appends in one statement line, in source order. */
|
|
351
|
+
creditOpsInStatement(text) {
|
|
352
|
+
const append = APPEND_RE.exec(text);
|
|
353
|
+
if (append)
|
|
354
|
+
return [{ kind: 'append', lhs: append[1], rhs: append[2] }];
|
|
355
|
+
const head = ASSIGN_RE.exec(text);
|
|
356
|
+
if (head)
|
|
357
|
+
return [{ kind: 'assign', lhs: head[1], rhs: head[2] }];
|
|
358
|
+
const out = [];
|
|
359
|
+
for (const m of text.matchAll(INNER_ASSIGN_RE)) {
|
|
360
|
+
out.push({ kind: 'assign', lhs: m[1], rhs: m[2].trim() });
|
|
361
|
+
}
|
|
362
|
+
return out;
|
|
363
|
+
}
|
|
262
364
|
sanitizedVarsIn(type, from, to, withHelpers) {
|
|
263
365
|
const sanitized = new Set();
|
|
264
366
|
for (const st of this.statements(from, to)) {
|
|
265
|
-
const
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
367
|
+
for (const op of this.creditOpsInStatement(st.text)) {
|
|
368
|
+
if (op.kind === 'append') {
|
|
369
|
+
if (!sanitized.has(op.lhs))
|
|
370
|
+
continue;
|
|
371
|
+
if (!this.rhsKeepsSanitizerCredit(type, op.rhs, sanitized, withHelpers, true))
|
|
372
|
+
sanitized.delete(op.lhs);
|
|
373
|
+
continue;
|
|
374
|
+
}
|
|
375
|
+
if (this.rhsKeepsSanitizerCredit(type, op.rhs, sanitized, withHelpers, false))
|
|
376
|
+
sanitized.add(op.lhs);
|
|
377
|
+
else
|
|
378
|
+
sanitized.delete(op.lhs);
|
|
379
|
+
}
|
|
275
380
|
}
|
|
276
381
|
return sanitized;
|
|
277
382
|
}
|
|
@@ -286,6 +391,46 @@ export class CSharpCredit {
|
|
|
286
391
|
sanitizedVarsAt(type, sinkLine) {
|
|
287
392
|
return this.sanitizedVarsIn(type, this.methodAt(sinkLine)?.start_line ?? 1, sinkLine, true);
|
|
288
393
|
}
|
|
394
|
+
/**
|
|
395
|
+
* Class fields judged across every method that writes them (cognium-dev#808).
|
|
396
|
+
* A field is read in one method and written in others, so each write is
|
|
397
|
+
* judged in its own method with the same in-method credit as a local, and
|
|
398
|
+
* the field is safe for `type` only when every write is: `sanitized` holds
|
|
399
|
+
* the fields with at least one credited write and no uncredited one,
|
|
400
|
+
* `unsanitized` the fields with an uncredited write. A literal write
|
|
401
|
+
* (`_v = "";`, `_v = null;`) is neutral.
|
|
402
|
+
*/
|
|
403
|
+
fieldWrites(type, fields) {
|
|
404
|
+
const credited = new Set();
|
|
405
|
+
const unsanitized = new Set();
|
|
406
|
+
for (const m of this.methods) {
|
|
407
|
+
const sanitized = new Set();
|
|
408
|
+
for (const st of this.statements(m.start_line, m.end_line + 1)) {
|
|
409
|
+
// A nested method's statements belong to it, not to `m`.
|
|
410
|
+
if (this.methodAt(st.line) !== m)
|
|
411
|
+
continue;
|
|
412
|
+
for (const op of this.creditOpsInStatement(st.text)) {
|
|
413
|
+
const lhs = op.lhs;
|
|
414
|
+
const keeps = op.kind === 'append'
|
|
415
|
+
? sanitized.has(lhs) && this.rhsKeepsSanitizerCredit(type, op.rhs, sanitized, true, true)
|
|
416
|
+
: this.rhsKeepsSanitizerCredit(type, op.rhs, sanitized, true, false);
|
|
417
|
+
if (keeps)
|
|
418
|
+
sanitized.add(lhs);
|
|
419
|
+
else
|
|
420
|
+
sanitized.delete(lhs);
|
|
421
|
+
if (!fields.has(lhs) || isLiteralValue(op.rhs))
|
|
422
|
+
continue;
|
|
423
|
+
if (keeps)
|
|
424
|
+
credited.add(lhs);
|
|
425
|
+
else
|
|
426
|
+
unsanitized.add(lhs);
|
|
427
|
+
}
|
|
428
|
+
}
|
|
429
|
+
}
|
|
430
|
+
for (const f of unsanitized)
|
|
431
|
+
credited.delete(f);
|
|
432
|
+
return { sanitized: credited, unsanitized };
|
|
433
|
+
}
|
|
289
434
|
/**
|
|
290
435
|
* True when the sink on `sinkLine` also receives a tainted value that no
|
|
291
436
|
* sanitizer covers. In `Write(HtmlEncode(a) + b)` the encoder covers `a`
|
|
@@ -361,7 +506,7 @@ export class CSharpCredit {
|
|
|
361
506
|
/** `text` with every sanitizer call for `type`, arguments included, removed. */
|
|
362
507
|
withoutSanitizerCalls(type, text) {
|
|
363
508
|
const res = [];
|
|
364
|
-
for (const { re, type: t } of
|
|
509
|
+
for (const { re, type: t } of this.sanitizers)
|
|
365
510
|
if (t === type)
|
|
366
511
|
res.push(re);
|
|
367
512
|
for (const name of this.helpers(type))
|