circle-ir 4.9.15 → 4.9.17
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/findings.d.ts +2 -2
- package/dist/analysis/findings.d.ts.map +1 -1
- package/dist/analysis/findings.js +166 -6
- package/dist/analysis/findings.js.map +1 -1
- package/dist/analysis/passes/language-sources-pass.d.ts +3 -0
- package/dist/analysis/passes/language-sources-pass.d.ts.map +1 -1
- package/dist/analysis/passes/language-sources-pass.js +247 -1
- package/dist/analysis/passes/language-sources-pass.js.map +1 -1
- package/dist/analysis/passes/library-profile-sink-gate-pass.d.ts.map +1 -1
- package/dist/analysis/passes/library-profile-sink-gate-pass.js +48 -13
- package/dist/analysis/passes/library-profile-sink-gate-pass.js.map +1 -1
- package/dist/analysis/passes/library-profile-source-gate-pass.d.ts.map +1 -1
- package/dist/analysis/passes/library-profile-source-gate-pass.js +35 -10
- package/dist/analysis/passes/library-profile-source-gate-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 +108 -1
- package/dist/analysis/passes/taint-propagation-pass.js.map +1 -1
- package/dist/analyzer.d.ts.map +1 -1
- package/dist/analyzer.js +16 -2
- package/dist/analyzer.js.map +1 -1
- package/dist/browser/circle-ir.js +261 -18
- package/dist/languages/plugins/go.d.ts.map +1 -1
- package/dist/languages/plugins/go.js +71 -0
- package/dist/languages/plugins/go.js.map +1 -1
- package/package.json +1 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"library-profile-sink-gate-pass.d.ts","sourceRoot":"","sources":["../../../src/analysis/passes/library-profile-sink-gate-pass.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAqEG;AAEH,OAAO,KAAK,EAAE,YAAY,EAAE,WAAW,EAAE,MAAM,8BAA8B,CAAC;
|
|
1
|
+
{"version":3,"file":"library-profile-sink-gate-pass.d.ts","sourceRoot":"","sources":["../../../src/analysis/passes/library-profile-sink-gate-pass.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAqEG;AAEH,OAAO,KAAK,EAAE,YAAY,EAAE,WAAW,EAAE,MAAM,8BAA8B,CAAC;AAG9E,OAAO,KAAK,EAAE,cAAc,EAAE,QAAQ,EAAE,UAAU,EAA4B,MAAM,sBAAsB,CAAC;AAY3G,MAAM,WAAW,4BAA4B;IAC3C;;;;OAIG;IACH,OAAO,EAAE,cAAc,GAAG,SAAS,CAAC;IACpC;;;;OAIG;IACH,OAAO,EAAE,OAAO,CAAC;IACjB;;;OAGG;IACH,OAAO,EAAE,MAAM,CAAC;IAChB;;;OAGG;IACH,aAAa,EAAE,OAAO,CAAC,MAAM,CAAC,QAAQ,EAAE,MAAM,CAAC,CAAC,CAAC;CAClD;AAaD,qBAAa,0BACX,YAAW,YAAY,CAAC,4BAA4B,CAAC;IAErD,QAAQ,CAAC,IAAI,+BAA+B;IAC5C,QAAQ,CAAC,QAAQ,EAAG,UAAU,CAAU;IAExC,GAAG,CAAC,GAAG,EAAE,WAAW,GAAG,4BAA4B;CA0DpD;AAcD,MAAM,WAAW,iCAAiC;IAChD;;;;OAIG;IACH,OAAO,EAAE,cAAc,GAAG,SAAS,CAAC;IACpC;;;;OAIG;IACH,OAAO,EAAE,OAAO,CAAC;IACjB;;;OAGG;IACH,OAAO,EAAE,MAAM,CAAC;IAChB;;;OAGG;IACH,mBAAmB,EAAE,OAAO,CAAC,MAAM,CAAC,UAAU,EAAE,MAAM,CAAC,CAAC,CAAC;CAC1D;AAED,qBAAa,+BACX,YAAW,YAAY,CAAC,iCAAiC,CAAC;IAE1D,QAAQ,CAAC,IAAI,qCAAqC;IAClD,QAAQ,CAAC,QAAQ,EAAG,UAAU,CAAU;IAExC,GAAG,CAAC,GAAG,EAAE,WAAW,GAAG,iCAAiC;CAwFzD"}
|
|
@@ -68,6 +68,7 @@
|
|
|
68
68
|
* to a follow-up if the harness rerun shows material Rust
|
|
69
69
|
* residuals; the Tier 2 8-repo cohort is Java-heavy.
|
|
70
70
|
*/
|
|
71
|
+
import { LIBRARY_API_SURFACE_TAG } from '../library-api-surface-downgrade.js';
|
|
71
72
|
/**
|
|
72
73
|
* Sink types eligible for the library-profile drop. Seeded with
|
|
73
74
|
* `log_injection` (CWE-117) in 3.152.0. Extending this set is a
|
|
@@ -167,9 +168,22 @@ export class LibraryProfileCwe22PathGatePass {
|
|
|
167
168
|
droppedBySourceType: {},
|
|
168
169
|
};
|
|
169
170
|
}
|
|
170
|
-
//
|
|
171
|
-
// `
|
|
172
|
-
|
|
171
|
+
// cognium-dev#288 — the authoritative flow list is
|
|
172
|
+
// `InterproceduralPassResult.additionalFlows`, which is what the
|
|
173
|
+
// post-pipeline rebuild assigns to `taint.flows`. The comment here used to
|
|
174
|
+
// claim `TaintPropagationPass` / `InterproceduralPass` populate
|
|
175
|
+
// `graph.ir.taint.flows`; they do not — nothing ever assigns it, so this
|
|
176
|
+
// pass read `undefined` and returned early on every run.
|
|
177
|
+
//
|
|
178
|
+
// With the source gate now effective BEFORE flow generation (#288), no
|
|
179
|
+
// flow from a gated source should reach this point at all. That makes this
|
|
180
|
+
// a genuine belt-and-suspenders net rather than the primary mechanism —
|
|
181
|
+
// but it is wired correctly now, so if a new flow-producing path ever
|
|
182
|
+
// bypasses the source list it will still be caught instead of silently
|
|
183
|
+
// doing nothing.
|
|
184
|
+
const flows = ctx.hasResult('interprocedural')
|
|
185
|
+
? ctx.getResult('interprocedural').additionalFlows
|
|
186
|
+
: graph.ir.taint.flows;
|
|
173
187
|
if (!flows || flows.length === 0) {
|
|
174
188
|
return {
|
|
175
189
|
profile,
|
|
@@ -178,24 +192,45 @@ export class LibraryProfileCwe22PathGatePass {
|
|
|
178
192
|
droppedBySourceType: {},
|
|
179
193
|
};
|
|
180
194
|
}
|
|
195
|
+
// cognium-dev#288 (option A) — TAG, do not delete.
|
|
196
|
+
//
|
|
197
|
+
// Previously this spliced matching flows out of the list. A flow removed
|
|
198
|
+
// here is unrecoverable downstream: the finding simply never exists, with
|
|
199
|
+
// nothing in the output to say a library-shape judgement was applied. That
|
|
200
|
+
// is the wrong default for the deterministic layer, whose job is to expose
|
|
201
|
+
// signals for adjudication (CLAUDE.md), and it is inconsistent with
|
|
202
|
+
// ADR-008, which keeps such findings and downgrades them.
|
|
203
|
+
//
|
|
204
|
+
// `TaintFlowInfo.tags` exists for exactly this (3.105.0): the
|
|
205
|
+
// `library-api-surface:caller-responsibility` tag is already understood by
|
|
206
|
+
// CLI/SARIF consumers as "downgrade and badge". So the flow survives,
|
|
207
|
+
// carrying the reason it was judged speculative.
|
|
208
|
+
//
|
|
209
|
+
// Note for whoever revisits the policy: ADR-008's
|
|
210
|
+
// `DOWNGRADE_ELIGIBLE_RULE_IDS` deliberately EXCLUDES `path_traversal`
|
|
211
|
+
// (only code_injection / template_injection / xpath_injection /
|
|
212
|
+
// sql_injection are eligible), on the reasoning that a library with a path
|
|
213
|
+
// traversal is a bug whatever its shape. So tagging a CWE-22 flow does not
|
|
214
|
+
// downgrade it under `applyProjectProfileTransform` today — the tag is
|
|
215
|
+
// carried, and the severity policy stays where ADR-008 put it. Deleting
|
|
216
|
+
// these flows, as this pass used to, contradicted that decision outright.
|
|
181
217
|
const droppedBySourceType = {};
|
|
182
|
-
|
|
218
|
+
let tagged = 0;
|
|
183
219
|
for (const flow of flows) {
|
|
184
220
|
if (flow.sink_type === 'path_traversal' &&
|
|
185
221
|
CWE22_SPECULATIVE_SOURCE_TYPES.has(flow.source_type)) {
|
|
186
222
|
droppedBySourceType[flow.source_type] =
|
|
187
223
|
(droppedBySourceType[flow.source_type] ?? 0) + 1;
|
|
188
|
-
|
|
224
|
+
const tags = flow.tags ?? [];
|
|
225
|
+
if (!tags.includes(LIBRARY_API_SURFACE_TAG)) {
|
|
226
|
+
flow.tags = [...tags, LIBRARY_API_SURFACE_TAG];
|
|
227
|
+
}
|
|
228
|
+
tagged++;
|
|
189
229
|
}
|
|
190
|
-
kept.push(flow);
|
|
191
|
-
}
|
|
192
|
-
const dropped = flows.length - kept.length;
|
|
193
|
-
// Mutate the flow array in place so downstream consumers
|
|
194
|
-
// (`CrossFilePass`, SARIF writer) see the filtered list.
|
|
195
|
-
if (dropped > 0) {
|
|
196
|
-
flows.length = 0;
|
|
197
|
-
flows.push(...kept);
|
|
198
230
|
}
|
|
231
|
+
// `dropped` keeps its name for result-shape compatibility but now reports
|
|
232
|
+
// how many flows were TAGGED; none are removed.
|
|
233
|
+
const dropped = tagged;
|
|
199
234
|
return {
|
|
200
235
|
profile,
|
|
201
236
|
applied: true,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"library-profile-sink-gate-pass.js","sourceRoot":"","sources":["../../../src/analysis/passes/library-profile-sink-gate-pass.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAqEG;
|
|
1
|
+
{"version":3,"file":"library-profile-sink-gate-pass.js","sourceRoot":"","sources":["../../../src/analysis/passes/library-profile-sink-gate-pass.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"library-profile-source-gate-pass.d.ts","sourceRoot":"","sources":["../../../src/analysis/passes/library-profile-source-gate-pass.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAsDG;AAEH,OAAO,KAAK,EAAE,YAAY,EAAE,WAAW,EAAE,MAAM,8BAA8B,CAAC;AAC9E,OAAO,KAAK,EAAE,cAAc,EAAE,UAAU,
|
|
1
|
+
{"version":3,"file":"library-profile-source-gate-pass.d.ts","sourceRoot":"","sources":["../../../src/analysis/passes/library-profile-source-gate-pass.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAsDG;AAEH,OAAO,KAAK,EAAE,YAAY,EAAE,WAAW,EAAE,MAAM,8BAA8B,CAAC;AAC9E,OAAO,KAAK,EAAE,cAAc,EAAE,UAAU,EAAe,MAAM,sBAAsB,CAAC;AAcpF,MAAM,WAAW,8BAA8B;IAC7C;;;;OAIG;IACH,OAAO,EAAE,cAAc,GAAG,SAAS,CAAC;IACpC;;;;OAIG;IACH,OAAO,EAAE,OAAO,CAAC;IACjB;;;OAGG;IACH,OAAO,EAAE,MAAM,CAAC;IAChB;;;OAGG;IACH,aAAa,EAAE,OAAO,CAAC,MAAM,CAAC,UAAU,EAAE,MAAM,CAAC,CAAC,CAAC;CACpD;AAaD,qBAAa,4BACX,YAAW,YAAY,CAAC,8BAA8B,CAAC;IAEvD,QAAQ,CAAC,IAAI,iCAAiC;IAC9C,QAAQ,CAAC,QAAQ,EAAG,UAAU,CAAU;IAExC,GAAG,CAAC,GAAG,EAAE,WAAW,GAAG,8BAA8B;CAuEtD"}
|
|
@@ -88,7 +88,19 @@ export class LibraryProfileSourceGatePass {
|
|
|
88
88
|
droppedByType: {},
|
|
89
89
|
};
|
|
90
90
|
}
|
|
91
|
-
|
|
91
|
+
// cognium-dev#288 — read the AUTHORITATIVE source list, mirroring the
|
|
92
|
+
// fetch pattern `LibraryProfileSinkGatePass` already uses and proves.
|
|
93
|
+
//
|
|
94
|
+
// `graph.ir.taint.sources` is NOT that list: `analyzer.ts` constructs the
|
|
95
|
+
// graph with `taint: { sources: [], sinks: [], sanitizers: [] }` and
|
|
96
|
+
// nothing ever populates it, so this pass used to filter a permanently
|
|
97
|
+
// empty array and report `dropped: 0` on every run. The list that both
|
|
98
|
+
// `InterproceduralPass` and the final `taint` rebuild consume is
|
|
99
|
+
// `SinkFilterResult.sources`. The fallback keeps the stand-alone unit
|
|
100
|
+
// tests working, which build an IR by hand and never run SinkFilterPass.
|
|
101
|
+
const sources = ctx.hasResult('sink-filter')
|
|
102
|
+
? ctx.getResult('sink-filter').sources
|
|
103
|
+
: graph.ir.taint.sources;
|
|
92
104
|
if (sources.length === 0) {
|
|
93
105
|
return {
|
|
94
106
|
profile,
|
|
@@ -97,21 +109,34 @@ export class LibraryProfileSourceGatePass {
|
|
|
97
109
|
droppedByType: {},
|
|
98
110
|
};
|
|
99
111
|
}
|
|
112
|
+
// cognium-dev#288 (option A) — COUNT, do not delete.
|
|
113
|
+
//
|
|
114
|
+
// This pass used to splice the speculative sources out of the authoritative
|
|
115
|
+
// list. Deleting them destroys information the downstream adjudication
|
|
116
|
+
// layer needs and is invisible in the output: a consumer cannot tell a
|
|
117
|
+
// clean file from a gated one, and `@cognium/project-profile-detect` can
|
|
118
|
+
// misclassify a repo as a library. It is also inconsistent with how this
|
|
119
|
+
// codebase already answers the same "caller's responsibility" question —
|
|
120
|
+
// `applyLibraryApiSurfaceDowngrade` and `applyProjectProfileTransform`
|
|
121
|
+
// (ADR-008) TAG a finding and downgrade its severity while keeping it,
|
|
122
|
+
// recording `original_severity` so the decision is auditable and
|
|
123
|
+
// reversible.
|
|
124
|
+
//
|
|
125
|
+
// So the sources survive and the signal is carried on the FLOWS instead,
|
|
126
|
+
// via the `library-api-surface:caller-responsibility` tag that
|
|
127
|
+
// `TaintFlowInfo.tags` has existed for since 3.105.0. The count is still
|
|
128
|
+
// reported for diagnostics.
|
|
100
129
|
const droppedByType = {};
|
|
101
|
-
const kept = [];
|
|
102
130
|
for (const src of sources) {
|
|
103
131
|
if (SPECULATIVE_SOURCE_TYPES.has(src.type)) {
|
|
104
132
|
droppedByType[src.type] = (droppedByType[src.type] ?? 0) + 1;
|
|
105
|
-
continue;
|
|
106
133
|
}
|
|
107
|
-
kept.push(src);
|
|
108
134
|
}
|
|
109
|
-
const dropped =
|
|
110
|
-
//
|
|
111
|
-
//
|
|
112
|
-
//
|
|
113
|
-
|
|
114
|
-
sources.push(...kept);
|
|
135
|
+
const dropped = 0;
|
|
136
|
+
// No mutation: `sources` is handed to `TaintPropagationPass` and
|
|
137
|
+
// `InterproceduralPass` and assigned to `taint.sources` by the rebuild.
|
|
138
|
+
// Leaving it intact is the point of option A — the sources/flows stay and
|
|
139
|
+
// the library-shape judgement is expressed as a tag, not a deletion.
|
|
115
140
|
return {
|
|
116
141
|
profile,
|
|
117
142
|
applied: true,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"library-profile-source-gate-pass.js","sourceRoot":"","sources":["../../../src/analysis/passes/library-profile-source-gate-pass.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAsDG;
|
|
1
|
+
{"version":3,"file":"library-profile-source-gate-pass.js","sourceRoot":"","sources":["../../../src/analysis/passes/library-profile-source-gate-pass.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAsDG;AAMH;;;;;GAKG;AACH,MAAM,wBAAwB,GAA4B,IAAI,GAAG,CAAa;IAC5E,uBAAuB;IACvB,mBAAmB;CACpB,CAAC,CAAC;AA2BH;;;;;GAKG;AACH,SAAS,cAAc,CAAC,OAAmC;IACzD,IAAI,CAAC,OAAO,IAAI,OAAO,KAAK,SAAS;QAAE,OAAO,KAAK,CAAC;IACpD,OAAO,OAAO,CAAC,UAAU,CAAC,UAAU,CAAC,CAAC;AACxC,CAAC;AAED,MAAM,OAAO,4BAA4B;IAG9B,IAAI,GAAG,6BAA6B,CAAC;IACrC,QAAQ,GAAG,UAAmB,CAAC;IAExC,GAAG,CAAC,GAAgB;QAClB,MAAM,EAAE,KAAK,EAAE,GAAG,GAAG,CAAC;QACtB,MAAM,OAAO,GAAG,KAAK,CAAC,EAAE,CAAC,IAAI,CAAC,cAAc,CAAC;QAE7C,IAAI,CAAC,cAAc,CAAC,OAAO,CAAC,EAAE,CAAC;YAC7B,OAAO;gBACL,OAAO;gBACP,OAAO,EAAE,KAAK;gBACd,OAAO,EAAE,CAAC;gBACV,aAAa,EAAE,EAAE;aAClB,CAAC;QACJ,CAAC;QAED,sEAAsE;QACtE,sEAAsE;QACtE,EAAE;QACF,0EAA0E;QAC1E,qEAAqE;QACrE,uEAAuE;QACvE,uEAAuE;QACvE,iEAAiE;QACjE,sEAAsE;QACtE,yEAAyE;QACzE,MAAM,OAAO,GAAkB,GAAG,CAAC,SAAS,CAAC,aAAa,CAAC;YACzD,CAAC,CAAC,GAAG,CAAC,SAAS,CAAmB,aAAa,CAAC,CAAC,OAAO;YACxD,CAAC,CAAC,KAAK,CAAC,EAAE,CAAC,KAAK,CAAC,OAAO,CAAC;QAC3B,IAAI,OAAO,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YACzB,OAAO;gBACL,OAAO;gBACP,OAAO,EAAE,IAAI;gBACb,OAAO,EAAE,CAAC;gBACV,aAAa,EAAE,EAAE;aAClB,CAAC;QACJ,CAAC;QAED,qDAAqD;QACrD,EAAE;QACF,4EAA4E;QAC5E,uEAAuE;QACvE,uEAAuE;QACvE,yEAAyE;QACzE,yEAAyE;QACzE,yEAAyE;QACzE,uEAAuE;QACvE,uEAAuE;QACvE,iEAAiE;QACjE,cAAc;QACd,EAAE;QACF,yEAAyE;QACzE,+DAA+D;QAC/D,yEAAyE;QACzE,4BAA4B;QAC5B,MAAM,aAAa,GAAwC,EAAE,CAAC;QAC9D,KAAK,MAAM,GAAG,IAAI,OAAO,EAAE,CAAC;YAC1B,IAAI,wBAAwB,CAAC,GAAG,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC;gBAC3C,aAAa,CAAC,GAAG,CAAC,IAAI,CAAC,GAAG,CAAC,aAAa,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC;YAC/D,CAAC;QACH,CAAC;QACD,MAAM,OAAO,GAAG,CAAC,CAAC;QAElB,iEAAiE;QACjE,wEAAwE;QACxE,0EAA0E;QAC1E,qEAAqE;QACrE,OAAO;YACL,OAAO;YACP,OAAO,EAAE,IAAI;YACb,OAAO;YACP,aAAa;SACd,CAAC;IACJ,CAAC;CACF"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"taint-propagation-pass.d.ts","sourceRoot":"","sources":["../../../src/analysis/passes/taint-propagation-pass.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AAE1D,OAAO,KAAK,EAAE,YAAY,EAAE,WAAW,EAAE,MAAM,8BAA8B,CAAC;AAU9E,MAAM,WAAW,0BAA0B;IACzC,KAAK,EAAE,aAAa,EAAE,CAAC;CACxB;AAED,qBAAa,oBAAqB,YAAW,YAAY,CAAC,0BAA0B,CAAC;IACnF,QAAQ,CAAC,IAAI,uBAAuB;IACpC,QAAQ,CAAC,QAAQ,EAAG,UAAU,CAAU;IAExC,GAAG,CAAC,GAAG,EAAE,WAAW,GAAG,0BAA0B;
|
|
1
|
+
{"version":3,"file":"taint-propagation-pass.d.ts","sourceRoot":"","sources":["../../../src/analysis/passes/taint-propagation-pass.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AAE1D,OAAO,KAAK,EAAE,YAAY,EAAE,WAAW,EAAE,MAAM,8BAA8B,CAAC;AAU9E,MAAM,WAAW,0BAA0B;IACzC,KAAK,EAAE,aAAa,EAAE,CAAC;CACxB;AAED,qBAAa,oBAAqB,YAAW,YAAY,CAAC,0BAA0B,CAAC;IACnF,QAAQ,CAAC,IAAI,uBAAuB;IACpC,QAAQ,CAAC,QAAQ,EAAG,UAAU,CAAU;IAExC,GAAG,CAAC,GAAG,EAAE,WAAW,GAAG,0BAA0B;CA4hBlD;AA6sBD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8CG;AACH,wBAAgB,8BAA8B,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CA6FnE"}
|
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
*/
|
|
13
13
|
import { propagateTaint } from '../taint-propagation.js';
|
|
14
14
|
import { isFalsePositive, isCorrelatedPredicateFP } from '../constant-propagation.js';
|
|
15
|
-
import { buildJavaTaintedVars, buildPythonTaintedVars, buildRustTaintedVars } from './language-sources-pass.js';
|
|
15
|
+
import { buildJavaTaintedVars, buildPythonTaintedVars, buildRustTaintedVars, findPythonTrustBoundaryViolations, findPythonReturnXSSSinks } from './language-sources-pass.js';
|
|
16
16
|
import { canSourceReachSink, sourceSemanticsAllowed } from '../findings.js';
|
|
17
17
|
import { walkBackwardDefs } from '../dfg-walk.js';
|
|
18
18
|
import { sanitizerCoversSink } from '../sanitizer-index.js';
|
|
@@ -86,6 +86,113 @@ export class TaintPropagationPass {
|
|
|
86
86
|
flows.push(f);
|
|
87
87
|
return true;
|
|
88
88
|
};
|
|
89
|
+
// Supplement: Python trust-boundary (CWE-501) flows — cognium-dev#363.
|
|
90
|
+
//
|
|
91
|
+
// `LanguageSourcesPass` detects `session[<tainted>] = …` and synthesises a
|
|
92
|
+
// `trust_boundary` sink for it, but that sink has no `method` and no
|
|
93
|
+
// arguments because a subscript assignment is not a call. Every flow
|
|
94
|
+
// builder here matches a source against a sink's ARGUMENTS, so nothing
|
|
95
|
+
// could ever connect to it: the sink was reported while `taint.flows`
|
|
96
|
+
// stayed empty, which is why OWASP BenchmarkPython scores the whole
|
|
97
|
+
// `trustbound` category 0% — its runner (correctly, post-#265) requires an
|
|
98
|
+
// unsanitised flow of the expected type rather than source+sink
|
|
99
|
+
// co-occurrence.
|
|
100
|
+
//
|
|
101
|
+
// The detector already computes both ends of the flow, so emit it here
|
|
102
|
+
// rather than teaching the argument matchers about subscript assignment.
|
|
103
|
+
// Reuses the exported detector so the sink and the flow can never disagree
|
|
104
|
+
// about what qualifies.
|
|
105
|
+
if (ctx.language === 'python' && typeof ctx.code === 'string') {
|
|
106
|
+
const pyTainted = buildPythonTaintedVars(ctx.code);
|
|
107
|
+
if (pyTainted.size > 0) {
|
|
108
|
+
for (const v of findPythonTrustBoundaryViolations(ctx.code, pyTainted)) {
|
|
109
|
+
const sink = sinks.find(sk => sk.line === v.sinkLine && sk.type === 'trust_boundary');
|
|
110
|
+
if (!sink)
|
|
111
|
+
continue;
|
|
112
|
+
// Prefer a real registered source on the detector's line; fall back
|
|
113
|
+
// to any source at or before it, so a derived tainted var (whose
|
|
114
|
+
// origin line is the read, not the assignment) still resolves.
|
|
115
|
+
const src = sources.find(sc => sc.line === v.sourceLine) ??
|
|
116
|
+
sources
|
|
117
|
+
.filter(sc => sc.line <= v.sinkLine)
|
|
118
|
+
.sort((a, b) => b.line - a.line)[0];
|
|
119
|
+
if (!src)
|
|
120
|
+
continue;
|
|
121
|
+
if (constProp.unreachableLines.has(v.sinkLine))
|
|
122
|
+
continue;
|
|
123
|
+
pushIfNew({
|
|
124
|
+
source_line: src.line,
|
|
125
|
+
sink_line: v.sinkLine,
|
|
126
|
+
source_type: src.type,
|
|
127
|
+
sink_type: 'trust_boundary',
|
|
128
|
+
path: [
|
|
129
|
+
{ variable: src.variable ?? 'session_key', line: src.line, type: 'source' },
|
|
130
|
+
{ variable: src.variable ?? 'session_key', line: v.sinkLine, type: 'sink' },
|
|
131
|
+
],
|
|
132
|
+
confidence: (src.confidence ?? 1) * (sink.confidence ?? 1),
|
|
133
|
+
sanitized: false,
|
|
134
|
+
});
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
// Supplement: Python return-XSS (CWE-79) flows — cognium-dev#368.
|
|
139
|
+
//
|
|
140
|
+
// Same wiring gap as #363, on a different detector. `LanguageSourcesPass`
|
|
141
|
+
// synthesises an `xss` sink for `return f"<div>{taint}</div>"` and
|
|
142
|
+
// `return "<div>" + taint`, but that sink has no `method` and no
|
|
143
|
+
// arguments, so no argument matcher can connect a source to it — the sink
|
|
144
|
+
// was reported while `taint.flows` stayed empty. Returning interpolated
|
|
145
|
+
// request data from a Flask view is real reflected XSS (a `str` return
|
|
146
|
+
// gets `text/html`), so the flow belongs here.
|
|
147
|
+
//
|
|
148
|
+
// Sanitizer-aware by construction: the registered Python escapers
|
|
149
|
+
// (`html.escape`, `markupsafe.escape`, `flask.escape`, …) are detected ON
|
|
150
|
+
// the return line, so `return f"<div>{html.escape(p)}</div>"` carries an
|
|
151
|
+
// xss-covering sanitizer at the sink line and is skipped. Without that
|
|
152
|
+
// check this would trade a false-negative class for a worse
|
|
153
|
+
// false-positive one.
|
|
154
|
+
if (ctx.language === 'python' && typeof ctx.code === 'string') {
|
|
155
|
+
const pyTainted = buildPythonTaintedVars(ctx.code);
|
|
156
|
+
if (pyTainted.size > 0) {
|
|
157
|
+
const xssSanitizerLines = sanitizers
|
|
158
|
+
.filter(sa => sa.sanitizes.includes('xss'))
|
|
159
|
+
.map(sa => sa.line);
|
|
160
|
+
for (const rx of findPythonReturnXSSSinks(ctx.code, pyTainted)) {
|
|
161
|
+
if (constProp.unreachableLines.has(rx.sinkLine))
|
|
162
|
+
continue;
|
|
163
|
+
const sink = sinks.find(sk => sk.line === rx.sinkLine && sk.type === 'xss');
|
|
164
|
+
if (!sink)
|
|
165
|
+
continue;
|
|
166
|
+
const src = sources
|
|
167
|
+
.filter(sc => sc.line <= rx.sinkLine)
|
|
168
|
+
.sort((a, b) => b.line - a.line)[0];
|
|
169
|
+
if (!src)
|
|
170
|
+
continue;
|
|
171
|
+
// An xss-covering sanitizer anywhere on the source -> sink range
|
|
172
|
+
// suppresses the flow. Checking only the SINK line is not enough:
|
|
173
|
+
// `n = int(request.args.get('n','0'))` sanitises on the source line
|
|
174
|
+
// and renders on the next (#100.2), and that case passed before only
|
|
175
|
+
// because no flow existed at all. Deliberately coarse — for an
|
|
176
|
+
// additive emitter the safe direction is to emit fewer flows, so an
|
|
177
|
+
// unrelated in-range sanitizer costs a missed finding rather than a
|
|
178
|
+
// false positive.
|
|
179
|
+
if (xssSanitizerLines.some(l => l >= src.line && l <= rx.sinkLine))
|
|
180
|
+
continue;
|
|
181
|
+
pushIfNew({
|
|
182
|
+
source_line: src.line,
|
|
183
|
+
sink_line: rx.sinkLine,
|
|
184
|
+
source_type: src.type,
|
|
185
|
+
sink_type: 'xss',
|
|
186
|
+
path: [
|
|
187
|
+
{ variable: src.variable ?? 'response', line: src.line, type: 'source' },
|
|
188
|
+
{ variable: src.variable ?? 'response', line: rx.sinkLine, type: 'sink' },
|
|
189
|
+
],
|
|
190
|
+
confidence: (src.confidence ?? 1) * (sink.confidence ?? 1),
|
|
191
|
+
sanitized: false,
|
|
192
|
+
});
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
}
|
|
89
196
|
// Supplement: array element flows
|
|
90
197
|
// Sprint 82 (#189) — dedup keys on (source_line, sink_line, sink_type) so
|
|
91
198
|
// distinct sink-types at the same call site (e.g. `res.redirect` registered
|