circle-ir 4.13.0 → 4.13.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/dist/analysis/constant-propagation/propagator.d.ts +6 -0
- package/dist/analysis/constant-propagation/propagator.d.ts.map +1 -1
- package/dist/analysis/constant-propagation/propagator.js +56 -2
- package/dist/analysis/constant-propagation/propagator.js.map +1 -1
- package/dist/analysis/passes/csharp-sanitizer-credit.d.ts +22 -1
- package/dist/analysis/passes/csharp-sanitizer-credit.d.ts.map +1 -1
- package/dist/analysis/passes/csharp-sanitizer-credit.js +99 -6
- package/dist/analysis/passes/csharp-sanitizer-credit.js.map +1 -1
- package/dist/analysis/passes/interprocedural-pass.d.ts.map +1 -1
- package/dist/analysis/passes/interprocedural-pass.js +2 -1
- package/dist/analysis/passes/interprocedural-pass.js.map +1 -1
- package/dist/analysis/passes/sink-filter-pass.d.ts.map +1 -1
- package/dist/analysis/passes/sink-filter-pass.js +7 -7
- package/dist/analysis/passes/sink-filter-pass.js.map +1 -1
- package/dist/browser/circle-ir.js +129 -11
- package/dist/core/circle-ir-core.cjs +49 -2
- package/dist/core/circle-ir-core.js +49 -2
- package/package.json +1 -1
|
@@ -22021,7 +22021,13 @@ var ConstantPropagator = class _ConstantPropagator {
|
|
|
22021
22021
|
}
|
|
22022
22022
|
return;
|
|
22023
22023
|
}
|
|
22024
|
-
|
|
22024
|
+
let condValue = this.evaluateExpression(condition);
|
|
22025
|
+
if (!(isKnown(condValue) && condValue.type === "bool")) {
|
|
22026
|
+
const literal = this.literalCondition(condition);
|
|
22027
|
+
if (literal !== null) {
|
|
22028
|
+
condValue = { type: "bool", value: literal, sourceLine: getNodeLine(condition) };
|
|
22029
|
+
}
|
|
22030
|
+
}
|
|
22025
22031
|
if (isKnown(condValue) && condValue.type === "bool") {
|
|
22026
22032
|
if (condValue.value === true) {
|
|
22027
22033
|
if (alternative) {
|
|
@@ -22185,13 +22191,27 @@ var ConstantPropagator = class _ConstantPropagator {
|
|
|
22185
22191
|
return;
|
|
22186
22192
|
}
|
|
22187
22193
|
const caseGroups = switchBlock.children.filter(
|
|
22188
|
-
(c) => c.type === "switch_block_statement_group" || c.type === "switch_rule" || c.type === "switch_section"
|
|
22194
|
+
(c) => c.type === "switch_block_statement_group" || c.type === "switch_rule" || c.type === "switch_section" || c.type === "switch_case" || c.type === "switch_default"
|
|
22189
22195
|
);
|
|
22196
|
+
if (caseGroups.length === 0) {
|
|
22197
|
+
this.visit(switchBlock);
|
|
22198
|
+
return;
|
|
22199
|
+
}
|
|
22190
22200
|
if (switchValue && isKnown(switchValue)) {
|
|
22191
22201
|
let matchingIdx = -1;
|
|
22192
22202
|
let defaultIdx = -1;
|
|
22193
22203
|
for (let i2 = 0; i2 < caseGroups.length; i2++) {
|
|
22194
22204
|
const caseGroup = caseGroups[i2];
|
|
22205
|
+
if (caseGroup.type === "switch_default") {
|
|
22206
|
+
defaultIdx = i2;
|
|
22207
|
+
continue;
|
|
22208
|
+
}
|
|
22209
|
+
if (caseGroup.type === "switch_case") {
|
|
22210
|
+
const label = caseGroup.childForFieldName("value");
|
|
22211
|
+
const caseValue = label ? this.literalValue(label) : null;
|
|
22212
|
+
if (caseValue !== null && caseValue === switchValue.value) matchingIdx = i2;
|
|
22213
|
+
continue;
|
|
22214
|
+
}
|
|
22195
22215
|
for (const child of caseGroup.children) {
|
|
22196
22216
|
if (child.type === "switch_label") {
|
|
22197
22217
|
const labelText = getNodeText2(child, this.source);
|
|
@@ -22294,6 +22314,33 @@ var ConstantPropagator = class _ConstantPropagator {
|
|
|
22294
22314
|
}
|
|
22295
22315
|
return false;
|
|
22296
22316
|
}
|
|
22317
|
+
/**
|
|
22318
|
+
* A condition written with literals only, in the C# grammar's node names:
|
|
22319
|
+
* `true`, `false`, `5 == 5`, `5 != 5`. Null when it is anything else. The
|
|
22320
|
+
* shared evaluator does not know `boolean_literal` / `integer_literal`.
|
|
22321
|
+
*/
|
|
22322
|
+
literalCondition(node) {
|
|
22323
|
+
if (node.type === "parenthesized_expression") {
|
|
22324
|
+
const inner = node.children.find((c) => c.isNamed);
|
|
22325
|
+
return inner ? this.literalCondition(inner) : null;
|
|
22326
|
+
}
|
|
22327
|
+
if (node.type === "boolean_literal") {
|
|
22328
|
+
const text = getNodeText2(node, this.source);
|
|
22329
|
+
return text === "true" ? true : text === "false" ? false : null;
|
|
22330
|
+
}
|
|
22331
|
+
if (node.type === "binary_expression") {
|
|
22332
|
+
const left = node.childForFieldName("left");
|
|
22333
|
+
const right = node.childForFieldName("right");
|
|
22334
|
+
const op = node.childForFieldName("operator");
|
|
22335
|
+
const opText = op ? getNodeText2(op, this.source) : "";
|
|
22336
|
+
if (!left || !right || opText !== "==" && opText !== "!=") return null;
|
|
22337
|
+
const l = this.literalValue(left);
|
|
22338
|
+
const r = this.literalValue(right);
|
|
22339
|
+
if (l === null || r === null) return null;
|
|
22340
|
+
return opText === "==" ? l === r : l !== r;
|
|
22341
|
+
}
|
|
22342
|
+
return null;
|
|
22343
|
+
}
|
|
22297
22344
|
/** The value of an integer, character or string literal node; null for anything else. */
|
|
22298
22345
|
literalValue(node) {
|
|
22299
22346
|
const text = getNodeText2(node, this.source);
|
|
@@ -35149,6 +35196,9 @@ var ENDS_STATEMENT_RE = /[;{}]\s*(?:\/\/.*)?$/;
|
|
|
35149
35196
|
function escapeRegex(s) {
|
|
35150
35197
|
return s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
35151
35198
|
}
|
|
35199
|
+
function withoutStringLiterals(text) {
|
|
35200
|
+
return text.replace(/@"(?:[^"]|"")*"|"(?:[^"\\]|\\.)*"/g, '""');
|
|
35201
|
+
}
|
|
35152
35202
|
function mentions(text, name2) {
|
|
35153
35203
|
return new RegExp(`(?<![\\w])${escapeRegex(name2)}(?![\\w])`).test(text);
|
|
35154
35204
|
}
|
|
@@ -35160,6 +35210,16 @@ function insideParens(text, open) {
|
|
|
35160
35210
|
}
|
|
35161
35211
|
return null;
|
|
35162
35212
|
}
|
|
35213
|
+
function lastCallArguments(text) {
|
|
35214
|
+
const close = text.lastIndexOf(")");
|
|
35215
|
+
if (close < 0) return null;
|
|
35216
|
+
let depth = 0;
|
|
35217
|
+
for (let i2 = close; i2 >= 0; i2--) {
|
|
35218
|
+
if (text[i2] === ")") depth++;
|
|
35219
|
+
else if (text[i2] === "(" && --depth === 0) return text.slice(i2 + 1, close);
|
|
35220
|
+
}
|
|
35221
|
+
return null;
|
|
35222
|
+
}
|
|
35163
35223
|
function leadingIf(text) {
|
|
35164
35224
|
const m = /^\s*(?:\}\s*)?(?:else\s+)?if\s*\(/.exec(text);
|
|
35165
35225
|
if (!m) return null;
|
|
@@ -35285,6 +35345,64 @@ var CSharpCredit = class {
|
|
|
35285
35345
|
sanitizedVarsAt(type, sinkLine) {
|
|
35286
35346
|
return this.sanitizedVarsIn(type, this.methodAt(sinkLine)?.start_line ?? 1, sinkLine, true);
|
|
35287
35347
|
}
|
|
35348
|
+
/**
|
|
35349
|
+
* True when the sink on `sinkLine` also receives a tainted value that no
|
|
35350
|
+
* sanitizer covers. In `Write(HtmlEncode(a) + b)` the encoder covers `a`
|
|
35351
|
+
* only: `b` arrives raw, so the sanitizer on the line must not clear the
|
|
35352
|
+
* sink. `credited` are the names already known safe for `type` there;
|
|
35353
|
+
* `seeds` are the taint sources that name a variable.
|
|
35354
|
+
*
|
|
35355
|
+
* Taint is followed through the sink's method top to bottom, last
|
|
35356
|
+
* assignment wins. The sink text is then read without its string literals
|
|
35357
|
+
* and without the sanitizer calls for `type`; a tainted, uncredited name
|
|
35358
|
+
* left over is a raw operand.
|
|
35359
|
+
*/
|
|
35360
|
+
rawTaintReaches(type, sinkLine, credited, seeds) {
|
|
35361
|
+
if (seeds.length === 0) return false;
|
|
35362
|
+
const method = this.methodAt(sinkLine);
|
|
35363
|
+
const from = method?.start_line ?? 1;
|
|
35364
|
+
const tainted = /* @__PURE__ */ new Set();
|
|
35365
|
+
const stmts = this.statements(from, sinkLine);
|
|
35366
|
+
for (let i2 = 0; i2 < stmts.length; i2++) {
|
|
35367
|
+
const st = stmts[i2];
|
|
35368
|
+
const end = i2 + 1 < stmts.length ? stmts[i2 + 1].line : sinkLine;
|
|
35369
|
+
const m = ASSIGN_RE.exec(st.text);
|
|
35370
|
+
if (m) {
|
|
35371
|
+
const rhs = withoutStringLiterals(m[2]);
|
|
35372
|
+
if ([...tainted].some((v) => mentions(rhs, v))) tainted.add(m[1]);
|
|
35373
|
+
else tainted.delete(m[1]);
|
|
35374
|
+
}
|
|
35375
|
+
for (const s of seeds) if (s.line >= st.line && s.line < end) tainted.add(s.variable);
|
|
35376
|
+
}
|
|
35377
|
+
if (tainted.size === 0) return false;
|
|
35378
|
+
const call = withoutStringLiterals(this.sinkText(sinkLine));
|
|
35379
|
+
const rest = this.withoutSanitizerCalls(type, lastCallArguments(call) ?? call);
|
|
35380
|
+
if (this.builtinSanitizes(type, rest)) return false;
|
|
35381
|
+
for (const v of tainted) if (!credited.has(v) && mentions(rest, v)) return true;
|
|
35382
|
+
return false;
|
|
35383
|
+
}
|
|
35384
|
+
/** `text` with every sanitizer call for `type`, arguments included, removed. */
|
|
35385
|
+
withoutSanitizerCalls(type, text) {
|
|
35386
|
+
const res = [];
|
|
35387
|
+
for (const { re, type: t } of CSHARP_SANITIZER_RES) if (t === type) res.push(re);
|
|
35388
|
+
for (const name2 of this.helpers(type)) res.push(new RegExp(`(?<![\\w.])${escapeRegex(name2)}\\s*\\(`));
|
|
35389
|
+
let out2 = text;
|
|
35390
|
+
for (const re of res) {
|
|
35391
|
+
for (let guard = 0; guard < 16; guard++) {
|
|
35392
|
+
const m = re.exec(out2);
|
|
35393
|
+
if (!m) break;
|
|
35394
|
+
if (!m[0].endsWith("(")) {
|
|
35395
|
+
out2 = out2.slice(0, m.index) + out2.slice(m.index + m[0].length);
|
|
35396
|
+
continue;
|
|
35397
|
+
}
|
|
35398
|
+
const open = m.index + m[0].length - 1;
|
|
35399
|
+
const inner = insideParens(out2, open);
|
|
35400
|
+
if (inner === null) break;
|
|
35401
|
+
out2 = out2.slice(0, m.index) + out2.slice(open + inner.length + 2);
|
|
35402
|
+
}
|
|
35403
|
+
}
|
|
35404
|
+
return out2;
|
|
35405
|
+
}
|
|
35288
35406
|
/**
|
|
35289
35407
|
* The text of the statement at `line` that a sink call can be in: for
|
|
35290
35408
|
* `if (COND) sink(x);` that is `sink(x);`, without the condition. A name
|
|
@@ -35501,16 +35619,16 @@ var CSharpCredit = class {
|
|
|
35501
35619
|
* sink type. Used for the untyped "tainted data reaches an external call"
|
|
35502
35620
|
* fallback, which has no sink type of its own to ask about.
|
|
35503
35621
|
*/
|
|
35504
|
-
creditedOnLine(line) {
|
|
35622
|
+
creditedOnLine(line, seeds = []) {
|
|
35505
35623
|
const text = this.sinkText(line);
|
|
35506
35624
|
const types = new Set(CSHARP_SANITIZER_RES.map((r) => r.type));
|
|
35507
35625
|
for (const t of INERT_WHEN_ALPHANUMERIC) types.add(t);
|
|
35508
35626
|
types.add("ssrf");
|
|
35509
35627
|
for (const type of types) {
|
|
35510
|
-
if (this.sanitizes(type, text)) return true;
|
|
35511
35628
|
const credited = /* @__PURE__ */ new Set([...this.sanitizedVarsAt(type, line), ...this.guardedVarsAt(type, line)]);
|
|
35512
35629
|
for (const v of this.safeBuilders(credited, line)) credited.add(v);
|
|
35513
|
-
|
|
35630
|
+
const covered = this.sanitizes(type, text) || [...credited].some((v) => mentions(text, v));
|
|
35631
|
+
if (covered && !this.rawTaintReaches(type, line, credited, seeds)) return true;
|
|
35514
35632
|
}
|
|
35515
35633
|
return this.fixedHostRequest(line);
|
|
35516
35634
|
}
|
|
@@ -36676,11 +36794,12 @@ var SinkFilterPass = class {
|
|
|
36676
36794
|
const sanitizedByType = csharpSanitizedVarsByType(ctx.code);
|
|
36677
36795
|
const csFields = new Set(ctx.graph.ir.types.flatMap((t) => (t.fields ?? []).map((f) => f.name)));
|
|
36678
36796
|
const credit = new CSharpCredit(ctx.code, ctx.graph.ir.types.flatMap((t) => t.methods));
|
|
36797
|
+
const csSeeds = mergedSources.flatMap((s) => s.variable ? [{ variable: s.variable, line: s.line }] : []);
|
|
36679
36798
|
const xxeHardened = /\bXmlResolver\s*=\s*null\b/.test(ctx.code) || /\bDtdProcessing\s*=\s*(?:DtdProcessing\s*\.\s*)?(?:Prohibit|Ignore)\b/.test(ctx.code);
|
|
36680
36799
|
filtered = filtered.filter((sink) => {
|
|
36681
36800
|
if (sink.type === "xxe" && xxeHardened) return false;
|
|
36682
36801
|
const sinkLineText = sourceLines[sink.line - 1] ?? "";
|
|
36683
|
-
|
|
36802
|
+
const inlineSanitized = credit.sanitizes(sink.type, sinkLineText);
|
|
36684
36803
|
if (sink.type === "ssrf" && credit.fixedHostRequest(sink.line)) return false;
|
|
36685
36804
|
const scoped = credit.sanitizedVarsAt(sink.type, sink.line);
|
|
36686
36805
|
const fileWide = sanitizedByType.get(sink.type);
|
|
@@ -36691,10 +36810,8 @@ var SinkFilterPass = class {
|
|
|
36691
36810
|
for (const v of credit.guardedVarsAt(sink.type, sink.line)) sanitizedVars.add(v);
|
|
36692
36811
|
for (const v of credit.safeBuilders(sanitizedVars, sink.line)) sanitizedVars.add(v);
|
|
36693
36812
|
const received = credit.sinkText(sink.line);
|
|
36694
|
-
|
|
36695
|
-
|
|
36696
|
-
}
|
|
36697
|
-
return true;
|
|
36813
|
+
const covered = inlineSanitized || [...sanitizedVars].some((v) => new RegExp(`(?<![\\w])${escapeRegex2(v)}(?![\\w])`).test(received));
|
|
36814
|
+
return !covered || credit.rawTaintReaches(sink.type, sink.line, sanitizedVars, csSeeds);
|
|
36698
36815
|
});
|
|
36699
36816
|
}
|
|
36700
36817
|
if (language === "csharp") {
|
|
@@ -40223,9 +40340,10 @@ var InterproceduralPass = class {
|
|
|
40223
40340
|
});
|
|
40224
40341
|
}
|
|
40225
40342
|
if (csCredit) {
|
|
40343
|
+
const csSeeds = sources.flatMap((s) => s.variable ? [{ variable: s.variable, line: s.line }] : []);
|
|
40226
40344
|
const credited = /* @__PURE__ */ new Set();
|
|
40227
40345
|
filteredAdditionalFlows = filteredAdditionalFlows.filter((f) => {
|
|
40228
|
-
if (f.sink_type !== "external_taint_escape" || !csCredit.creditedOnLine(f.sink_line)) return true;
|
|
40346
|
+
if (f.sink_type !== "external_taint_escape" || !csCredit.creditedOnLine(f.sink_line, csSeeds)) return true;
|
|
40229
40347
|
credited.add(f.sink_line);
|
|
40230
40348
|
return false;
|
|
40231
40349
|
});
|
|
@@ -19200,7 +19200,13 @@ var ConstantPropagator = class _ConstantPropagator {
|
|
|
19200
19200
|
}
|
|
19201
19201
|
return;
|
|
19202
19202
|
}
|
|
19203
|
-
|
|
19203
|
+
let condValue = this.evaluateExpression(condition);
|
|
19204
|
+
if (!(isKnown(condValue) && condValue.type === "bool")) {
|
|
19205
|
+
const literal = this.literalCondition(condition);
|
|
19206
|
+
if (literal !== null) {
|
|
19207
|
+
condValue = { type: "bool", value: literal, sourceLine: getNodeLine(condition) };
|
|
19208
|
+
}
|
|
19209
|
+
}
|
|
19204
19210
|
if (isKnown(condValue) && condValue.type === "bool") {
|
|
19205
19211
|
if (condValue.value === true) {
|
|
19206
19212
|
if (alternative) {
|
|
@@ -19364,13 +19370,27 @@ var ConstantPropagator = class _ConstantPropagator {
|
|
|
19364
19370
|
return;
|
|
19365
19371
|
}
|
|
19366
19372
|
const caseGroups = switchBlock.children.filter(
|
|
19367
|
-
(c) => c.type === "switch_block_statement_group" || c.type === "switch_rule" || c.type === "switch_section"
|
|
19373
|
+
(c) => c.type === "switch_block_statement_group" || c.type === "switch_rule" || c.type === "switch_section" || c.type === "switch_case" || c.type === "switch_default"
|
|
19368
19374
|
);
|
|
19375
|
+
if (caseGroups.length === 0) {
|
|
19376
|
+
this.visit(switchBlock);
|
|
19377
|
+
return;
|
|
19378
|
+
}
|
|
19369
19379
|
if (switchValue && isKnown(switchValue)) {
|
|
19370
19380
|
let matchingIdx = -1;
|
|
19371
19381
|
let defaultIdx = -1;
|
|
19372
19382
|
for (let i2 = 0; i2 < caseGroups.length; i2++) {
|
|
19373
19383
|
const caseGroup = caseGroups[i2];
|
|
19384
|
+
if (caseGroup.type === "switch_default") {
|
|
19385
|
+
defaultIdx = i2;
|
|
19386
|
+
continue;
|
|
19387
|
+
}
|
|
19388
|
+
if (caseGroup.type === "switch_case") {
|
|
19389
|
+
const label = caseGroup.childForFieldName("value");
|
|
19390
|
+
const caseValue = label ? this.literalValue(label) : null;
|
|
19391
|
+
if (caseValue !== null && caseValue === switchValue.value) matchingIdx = i2;
|
|
19392
|
+
continue;
|
|
19393
|
+
}
|
|
19374
19394
|
for (const child of caseGroup.children) {
|
|
19375
19395
|
if (child.type === "switch_label") {
|
|
19376
19396
|
const labelText = getNodeText2(child, this.source);
|
|
@@ -19473,6 +19493,33 @@ var ConstantPropagator = class _ConstantPropagator {
|
|
|
19473
19493
|
}
|
|
19474
19494
|
return false;
|
|
19475
19495
|
}
|
|
19496
|
+
/**
|
|
19497
|
+
* A condition written with literals only, in the C# grammar's node names:
|
|
19498
|
+
* `true`, `false`, `5 == 5`, `5 != 5`. Null when it is anything else. The
|
|
19499
|
+
* shared evaluator does not know `boolean_literal` / `integer_literal`.
|
|
19500
|
+
*/
|
|
19501
|
+
literalCondition(node) {
|
|
19502
|
+
if (node.type === "parenthesized_expression") {
|
|
19503
|
+
const inner = node.children.find((c) => c.isNamed);
|
|
19504
|
+
return inner ? this.literalCondition(inner) : null;
|
|
19505
|
+
}
|
|
19506
|
+
if (node.type === "boolean_literal") {
|
|
19507
|
+
const text = getNodeText2(node, this.source);
|
|
19508
|
+
return text === "true" ? true : text === "false" ? false : null;
|
|
19509
|
+
}
|
|
19510
|
+
if (node.type === "binary_expression") {
|
|
19511
|
+
const left = node.childForFieldName("left");
|
|
19512
|
+
const right = node.childForFieldName("right");
|
|
19513
|
+
const op = node.childForFieldName("operator");
|
|
19514
|
+
const opText = op ? getNodeText2(op, this.source) : "";
|
|
19515
|
+
if (!left || !right || opText !== "==" && opText !== "!=") return null;
|
|
19516
|
+
const l = this.literalValue(left);
|
|
19517
|
+
const r = this.literalValue(right);
|
|
19518
|
+
if (l === null || r === null) return null;
|
|
19519
|
+
return opText === "==" ? l === r : l !== r;
|
|
19520
|
+
}
|
|
19521
|
+
return null;
|
|
19522
|
+
}
|
|
19476
19523
|
/** The value of an integer, character or string literal node; null for anything else. */
|
|
19477
19524
|
literalValue(node) {
|
|
19478
19525
|
const text = getNodeText2(node, this.source);
|
|
@@ -19134,7 +19134,13 @@ var ConstantPropagator = class _ConstantPropagator {
|
|
|
19134
19134
|
}
|
|
19135
19135
|
return;
|
|
19136
19136
|
}
|
|
19137
|
-
|
|
19137
|
+
let condValue = this.evaluateExpression(condition);
|
|
19138
|
+
if (!(isKnown(condValue) && condValue.type === "bool")) {
|
|
19139
|
+
const literal = this.literalCondition(condition);
|
|
19140
|
+
if (literal !== null) {
|
|
19141
|
+
condValue = { type: "bool", value: literal, sourceLine: getNodeLine(condition) };
|
|
19142
|
+
}
|
|
19143
|
+
}
|
|
19138
19144
|
if (isKnown(condValue) && condValue.type === "bool") {
|
|
19139
19145
|
if (condValue.value === true) {
|
|
19140
19146
|
if (alternative) {
|
|
@@ -19298,13 +19304,27 @@ var ConstantPropagator = class _ConstantPropagator {
|
|
|
19298
19304
|
return;
|
|
19299
19305
|
}
|
|
19300
19306
|
const caseGroups = switchBlock.children.filter(
|
|
19301
|
-
(c) => c.type === "switch_block_statement_group" || c.type === "switch_rule" || c.type === "switch_section"
|
|
19307
|
+
(c) => c.type === "switch_block_statement_group" || c.type === "switch_rule" || c.type === "switch_section" || c.type === "switch_case" || c.type === "switch_default"
|
|
19302
19308
|
);
|
|
19309
|
+
if (caseGroups.length === 0) {
|
|
19310
|
+
this.visit(switchBlock);
|
|
19311
|
+
return;
|
|
19312
|
+
}
|
|
19303
19313
|
if (switchValue && isKnown(switchValue)) {
|
|
19304
19314
|
let matchingIdx = -1;
|
|
19305
19315
|
let defaultIdx = -1;
|
|
19306
19316
|
for (let i2 = 0; i2 < caseGroups.length; i2++) {
|
|
19307
19317
|
const caseGroup = caseGroups[i2];
|
|
19318
|
+
if (caseGroup.type === "switch_default") {
|
|
19319
|
+
defaultIdx = i2;
|
|
19320
|
+
continue;
|
|
19321
|
+
}
|
|
19322
|
+
if (caseGroup.type === "switch_case") {
|
|
19323
|
+
const label = caseGroup.childForFieldName("value");
|
|
19324
|
+
const caseValue = label ? this.literalValue(label) : null;
|
|
19325
|
+
if (caseValue !== null && caseValue === switchValue.value) matchingIdx = i2;
|
|
19326
|
+
continue;
|
|
19327
|
+
}
|
|
19308
19328
|
for (const child of caseGroup.children) {
|
|
19309
19329
|
if (child.type === "switch_label") {
|
|
19310
19330
|
const labelText = getNodeText2(child, this.source);
|
|
@@ -19407,6 +19427,33 @@ var ConstantPropagator = class _ConstantPropagator {
|
|
|
19407
19427
|
}
|
|
19408
19428
|
return false;
|
|
19409
19429
|
}
|
|
19430
|
+
/**
|
|
19431
|
+
* A condition written with literals only, in the C# grammar's node names:
|
|
19432
|
+
* `true`, `false`, `5 == 5`, `5 != 5`. Null when it is anything else. The
|
|
19433
|
+
* shared evaluator does not know `boolean_literal` / `integer_literal`.
|
|
19434
|
+
*/
|
|
19435
|
+
literalCondition(node) {
|
|
19436
|
+
if (node.type === "parenthesized_expression") {
|
|
19437
|
+
const inner = node.children.find((c) => c.isNamed);
|
|
19438
|
+
return inner ? this.literalCondition(inner) : null;
|
|
19439
|
+
}
|
|
19440
|
+
if (node.type === "boolean_literal") {
|
|
19441
|
+
const text = getNodeText2(node, this.source);
|
|
19442
|
+
return text === "true" ? true : text === "false" ? false : null;
|
|
19443
|
+
}
|
|
19444
|
+
if (node.type === "binary_expression") {
|
|
19445
|
+
const left = node.childForFieldName("left");
|
|
19446
|
+
const right = node.childForFieldName("right");
|
|
19447
|
+
const op = node.childForFieldName("operator");
|
|
19448
|
+
const opText = op ? getNodeText2(op, this.source) : "";
|
|
19449
|
+
if (!left || !right || opText !== "==" && opText !== "!=") return null;
|
|
19450
|
+
const l = this.literalValue(left);
|
|
19451
|
+
const r = this.literalValue(right);
|
|
19452
|
+
if (l === null || r === null) return null;
|
|
19453
|
+
return opText === "==" ? l === r : l !== r;
|
|
19454
|
+
}
|
|
19455
|
+
return null;
|
|
19456
|
+
}
|
|
19410
19457
|
/** The value of an integer, character or string literal node; null for anything else. */
|
|
19411
19458
|
literalValue(node) {
|
|
19412
19459
|
const text = getNodeText2(node, this.source);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "circle-ir",
|
|
3
|
-
"version": "4.13.
|
|
3
|
+
"version": "4.13.1",
|
|
4
4
|
"description": "High-performance Static Application Security Testing (SAST) library for detecting security vulnerabilities through taint analysis",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"module": "dist/index.js",
|