gitnexus 1.6.9-rc.6 → 1.6.9-rc.7
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/_shared/scope-resolution/types.d.ts +9 -0
- package/dist/_shared/scope-resolution/types.d.ts.map +1 -1
- package/dist/core/ingestion/languages/java/interpret.js +1 -0
- package/dist/core/ingestion/taint/emit.js +10 -4
- package/dist/core/ingestion/taint/java-model.d.ts +11 -0
- package/dist/core/ingestion/taint/java-model.js +21 -0
- package/dist/core/ingestion/taint/match.d.ts +19 -2
- package/dist/core/ingestion/taint/match.js +39 -5
- package/dist/core/ingestion/taint/propagate.d.ts +17 -5
- package/dist/core/ingestion/taint/propagate.js +46 -11
- package/dist/core/ingestion/taint/source-sink-config.d.ts +20 -4
- package/dist/core/ingestion/taint/summary-harvest.js +14 -4
- package/dist/core/ingestion/taint/typescript-model.d.ts +4 -3
- package/dist/core/ingestion/taint/typescript-model.js +6 -3
- package/package.json +1 -1
|
@@ -85,6 +85,13 @@ export type ParsedImport =
|
|
|
85
85
|
readonly localName: string;
|
|
86
86
|
readonly importedName: string;
|
|
87
87
|
readonly targetRaw: string;
|
|
88
|
+
/**
|
|
89
|
+
* Set by providers when `targetRaw` already names the imported symbol
|
|
90
|
+
* rather than only its containing module. Consumers that compose
|
|
91
|
+
* `<local>.<member>` paths can then use `targetRaw.<member>` instead of
|
|
92
|
+
* duplicating `importedName`.
|
|
93
|
+
*/
|
|
94
|
+
readonly targetIncludesImportedName?: boolean;
|
|
88
95
|
}
|
|
89
96
|
/**
|
|
90
97
|
* Per-name import with rename.
|
|
@@ -99,6 +106,8 @@ export type ParsedImport =
|
|
|
99
106
|
readonly importedName: string;
|
|
100
107
|
readonly alias: string;
|
|
101
108
|
readonly targetRaw: string;
|
|
109
|
+
/** See the same field on the `named` variant. */
|
|
110
|
+
readonly targetIncludesImportedName?: boolean;
|
|
102
111
|
}
|
|
103
112
|
/**
|
|
104
113
|
* Qualified module handle, with or without rename. `importedName` is the
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/scope-resolution/types.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;GAsBG;AAEH,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,mBAAmB,CAAC;AACnD,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,wBAAwB,CAAC;AAI/D,4FAA4F;AAC5F,MAAM,MAAM,OAAO,GAAG,MAAM,CAAC;AAE7B,0DAA0D;AAC1D,MAAM,MAAM,KAAK,GAAG,MAAM,CAAC;AAE3B,2DAA2D;AAC3D,MAAM,MAAM,SAAS,GACjB,QAAQ,GACR,WAAW,GACX,OAAO,GACP,UAAU,GACV,OAAO,GACP,YAAY,CAAC;AAIjB,qFAAqF;AACrF,MAAM,WAAW,KAAK;IACpB,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;CACzB;AAED;;;;;;GAMG;AACH,MAAM,WAAW,OAAO;IACtB,6FAA6F;IAC7F,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,KAAK,EAAE,KAAK,CAAC;IACtB,gCAAgC;IAChC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;CACvB;AAED;;;;;GAKG;AACH,MAAM,MAAM,YAAY,GAAG,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC;AAI7D;;;;;;;;;;;;;GAaG;AACH,MAAM,MAAM,YAAY;AACtB;;;;;;;GAOG;AACD;IACE,QAAQ,CAAC,IAAI,EAAE,OAAO,CAAC;IACvB,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAC;IAC9B,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/scope-resolution/types.ts"],"names":[],"mappings":"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"}
|
|
@@ -173,10 +173,16 @@ solve = computeReachingDefs) {
|
|
|
173
173
|
// `exec(req.body, req.query)`'s two findings; `property` is free-text
|
|
174
174
|
// (string-literal subscripts) and rides LAST so it cannot collide into
|
|
175
175
|
// another component.
|
|
176
|
-
const id =
|
|
177
|
-
`${
|
|
178
|
-
|
|
179
|
-
|
|
176
|
+
const id = source.type === 'member-read'
|
|
177
|
+
? generateId('TAINTED', `${fnAnchor}:${finding.sinkKind}:` +
|
|
178
|
+
`${pointKey(source.point)}.${source.siteIndex}:${bKey(source.objectBindingIdx)}:` +
|
|
179
|
+
`${pointKey(sink.point)}.${sink.siteIndex}.${sink.argIndex}:${bKey(sink.bindingIdx)}:` +
|
|
180
|
+
`${sink.entryName}:${source.property}`)
|
|
181
|
+
: generateId('TAINTED', `${fnAnchor}:${finding.sinkKind}:` +
|
|
182
|
+
`${pointKey(source.point)}.${source.siteIndex}:call-result:` +
|
|
183
|
+
`${bKey(source.resultBindingIdx)}:${source.calleeName}:` +
|
|
184
|
+
`${pointKey(sink.point)}.${sink.siteIndex}.${sink.argIndex}:${bKey(sink.bindingIdx)}:` +
|
|
185
|
+
`${sink.entryName}`);
|
|
180
186
|
if (seenEdgeIds.has(id))
|
|
181
187
|
continue;
|
|
182
188
|
seenEdgeIds.add(id);
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Built-in Java taint model (#2261 first slice).
|
|
3
|
+
*
|
|
4
|
+
* This deliberately starts small. Servlet request input is modeled only when a
|
|
5
|
+
* conventional request receiver's call result is assigned to a binding. Sinks
|
|
6
|
+
* are limited to static-import-proven JDK filesystem operations that current
|
|
7
|
+
* harvested call-site/import data can identify without broad same-name matching.
|
|
8
|
+
* No sanitizers are registered in this slice.
|
|
9
|
+
*/
|
|
10
|
+
import type { SourceSinkSanitizerSpec } from './source-sink-config.js';
|
|
11
|
+
export declare const JAVA_TAINT_MODEL: SourceSinkSanitizerSpec;
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Built-in Java taint model (#2261 first slice).
|
|
3
|
+
*
|
|
4
|
+
* This deliberately starts small. Servlet request input is modeled only when a
|
|
5
|
+
* conventional request receiver's call result is assigned to a binding. Sinks
|
|
6
|
+
* are limited to static-import-proven JDK filesystem operations that current
|
|
7
|
+
* harvested call-site/import data can identify without broad same-name matching.
|
|
8
|
+
* No sanitizers are registered in this slice.
|
|
9
|
+
*/
|
|
10
|
+
export const JAVA_TAINT_MODEL = {
|
|
11
|
+
sources: [
|
|
12
|
+
{
|
|
13
|
+
type: 'call-result',
|
|
14
|
+
kind: 'remote-input',
|
|
15
|
+
receivers: ['request', 'req'],
|
|
16
|
+
methods: ['getParameter', 'getHeader'],
|
|
17
|
+
},
|
|
18
|
+
],
|
|
19
|
+
sinks: [{ name: 'readString', kind: 'path-traversal', args: [0], module: 'java.nio.file.Files' }],
|
|
20
|
+
sanitizers: [],
|
|
21
|
+
};
|
|
@@ -74,7 +74,7 @@
|
|
|
74
74
|
*/
|
|
75
75
|
import type { ParsedImport } from '../../../_shared/index.js';
|
|
76
76
|
import type { FunctionCfg } from '../cfg/types.js';
|
|
77
|
-
import type { SourceSinkSanitizerSpec, TaintMemberSourceEntry, TaintSanitizerEntry, TaintSinkEntry } from './source-sink-config.js';
|
|
77
|
+
import type { TaintCallResultSourceEntry, SourceSinkSanitizerSpec, TaintMemberSourceEntry, TaintSanitizerEntry, TaintSinkEntry } from './source-sink-config.js';
|
|
78
78
|
/** What a local name imported into the file denotes. */
|
|
79
79
|
export interface TaintImportBinding {
|
|
80
80
|
/** Normalized module specifier (`node:` scheme stripped). */
|
|
@@ -85,15 +85,32 @@ export interface TaintImportBinding {
|
|
|
85
85
|
* CJS interop makes the default export ≈ the module object).
|
|
86
86
|
*/
|
|
87
87
|
readonly member?: string;
|
|
88
|
+
/**
|
|
89
|
+
* True when the provider says `module` already includes `member`; used for
|
|
90
|
+
* class-like imports where a receiver call should resolve as
|
|
91
|
+
* `<module>.<method>`, not `<module>.<member>.<method>`.
|
|
92
|
+
*/
|
|
93
|
+
readonly targetIncludesMember?: boolean;
|
|
88
94
|
}
|
|
89
95
|
/** Local name → import provenance for one file. Build once per file (U4). */
|
|
90
96
|
export type TaintImportIndex = ReadonlyMap<string, TaintImportBinding>;
|
|
91
97
|
/** A member-read site matched as a taint source. */
|
|
92
98
|
export interface MatchedSourceRead {
|
|
99
|
+
readonly type: 'member-read';
|
|
93
100
|
/** Index into the owning statement's `sites` array. */
|
|
94
101
|
readonly siteIndex: number;
|
|
95
102
|
readonly entry: TaintMemberSourceEntry;
|
|
96
103
|
}
|
|
104
|
+
/** A call-result source matched on a call site with direct result definitions. */
|
|
105
|
+
export interface MatchedSourceCall {
|
|
106
|
+
readonly type: 'call-result';
|
|
107
|
+
/** Index into the owning statement's `sites` array. */
|
|
108
|
+
readonly siteIndex: number;
|
|
109
|
+
readonly entry: TaintCallResultSourceEntry;
|
|
110
|
+
/** Bindings directly defined by this call result. Never empty. */
|
|
111
|
+
readonly resultDefs: readonly number[];
|
|
112
|
+
}
|
|
113
|
+
export type MatchedSource = MatchedSourceRead | MatchedSourceCall;
|
|
97
114
|
/** A call/new site matched as a sink. */
|
|
98
115
|
export interface MatchedSinkCall {
|
|
99
116
|
/** Index into the owning statement's `sites` array. */
|
|
@@ -129,7 +146,7 @@ export interface StatementMatches {
|
|
|
129
146
|
readonly blockIndex: number;
|
|
130
147
|
readonly statementIndex: number;
|
|
131
148
|
readonly line: number;
|
|
132
|
-
readonly sources: readonly
|
|
149
|
+
readonly sources: readonly MatchedSource[];
|
|
133
150
|
readonly sinks: readonly MatchedSinkCall[];
|
|
134
151
|
readonly sanitizers: readonly MatchedSanitizerCall[];
|
|
135
152
|
}
|
|
@@ -73,6 +73,7 @@
|
|
|
73
73
|
* matcher-visible local binding and are skipped by the index.
|
|
74
74
|
*/
|
|
75
75
|
const stripNodeScheme = (specifier) => specifier.startsWith('node:') ? specifier.slice('node:'.length) : specifier;
|
|
76
|
+
const isCallResultSource = (entry) => entry.type === 'call-result';
|
|
76
77
|
/**
|
|
77
78
|
* Build the local-name → module/member index from a file's `parsedImports`.
|
|
78
79
|
* Only `named`/`alias`/`namespace` kinds bind matcher-visible local names;
|
|
@@ -83,7 +84,13 @@ export function buildTaintImportIndex(imports) {
|
|
|
83
84
|
for (const imp of imports) {
|
|
84
85
|
if (imp.kind === 'named' || imp.kind === 'alias') {
|
|
85
86
|
const module = stripNodeScheme(imp.targetRaw);
|
|
86
|
-
index.set(imp.localName, imp.importedName === 'default'
|
|
87
|
+
index.set(imp.localName, imp.importedName === 'default'
|
|
88
|
+
? { module }
|
|
89
|
+
: {
|
|
90
|
+
module,
|
|
91
|
+
member: imp.importedName,
|
|
92
|
+
...(imp.targetIncludesImportedName === true ? { targetIncludesMember: true } : {}),
|
|
93
|
+
});
|
|
87
94
|
}
|
|
88
95
|
else if (imp.kind === 'namespace') {
|
|
89
96
|
index.set(imp.localName, { module: stripNodeScheme(imp.targetRaw) });
|
|
@@ -142,14 +149,16 @@ export function matchFunctionSites(cfg, spec, imports) {
|
|
|
142
149
|
const rest = path.slice(1);
|
|
143
150
|
const canonical = [];
|
|
144
151
|
let globalRoot = false;
|
|
152
|
+
const canonicalBase = (imp) => imp.member === undefined || imp.targetIncludesMember === true
|
|
153
|
+
? [imp.module]
|
|
154
|
+
: [imp.module, imp.member];
|
|
145
155
|
if (site.receiver !== undefined) {
|
|
146
156
|
// Member chain with an identifier root — origin known by binding index.
|
|
147
157
|
const rb = bindings[site.receiver];
|
|
148
158
|
if (rb.synthetic === true) {
|
|
149
159
|
const imp = imports.get(rb.name);
|
|
150
160
|
if (imp !== undefined) {
|
|
151
|
-
|
|
152
|
-
canonical.push([...base, ...rest].join('.'));
|
|
161
|
+
canonical.push([...canonicalBase(imp), ...rest].join('.'));
|
|
153
162
|
}
|
|
154
163
|
}
|
|
155
164
|
else {
|
|
@@ -174,7 +183,11 @@ export function matchFunctionSites(cfg, spec, imports) {
|
|
|
174
183
|
else {
|
|
175
184
|
const imp = imports.get(root);
|
|
176
185
|
if (imp !== undefined) {
|
|
177
|
-
canonical.push(imp.member === undefined
|
|
186
|
+
canonical.push(imp.member === undefined
|
|
187
|
+
? `${imp.module}.default`
|
|
188
|
+
: imp.targetIncludesMember === true
|
|
189
|
+
? imp.module
|
|
190
|
+
: `${imp.module}.${imp.member}`);
|
|
178
191
|
}
|
|
179
192
|
else {
|
|
180
193
|
globalRoot = true;
|
|
@@ -240,8 +253,10 @@ export function matchFunctionSites(cfg, spec, imports) {
|
|
|
240
253
|
const objectName = bindings[site.object].name;
|
|
241
254
|
const property = site.property;
|
|
242
255
|
for (const entry of spec.sources) {
|
|
256
|
+
if (isCallResultSource(entry))
|
|
257
|
+
continue;
|
|
243
258
|
if (entry.objects.includes(objectName) && entry.properties.includes(property)) {
|
|
244
|
-
sources.push({ siteIndex, entry });
|
|
259
|
+
sources.push({ type: 'member-read', siteIndex, entry });
|
|
245
260
|
}
|
|
246
261
|
}
|
|
247
262
|
return;
|
|
@@ -250,6 +265,25 @@ export function matchFunctionSites(cfg, spec, imports) {
|
|
|
250
265
|
const resolved = resolveCallee(site);
|
|
251
266
|
if (resolved === undefined)
|
|
252
267
|
return;
|
|
268
|
+
if (site.kind === 'call') {
|
|
269
|
+
const resultDefs = site.resultDefs;
|
|
270
|
+
if (resultDefs !== undefined && resultDefs.length > 0) {
|
|
271
|
+
for (const entry of spec.sources) {
|
|
272
|
+
if (!isCallResultSource(entry))
|
|
273
|
+
continue;
|
|
274
|
+
if (resolved.path.length === 2 &&
|
|
275
|
+
entry.receivers.includes(resolved.path[0]) &&
|
|
276
|
+
entry.methods.includes(resolved.path[1])) {
|
|
277
|
+
sources.push({
|
|
278
|
+
type: 'call-result',
|
|
279
|
+
siteIndex,
|
|
280
|
+
entry,
|
|
281
|
+
resultDefs,
|
|
282
|
+
});
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
}
|
|
253
287
|
for (const entry of spec.sinks) {
|
|
254
288
|
if (!sinkMechanismHit(entry, site, resolved))
|
|
255
289
|
continue;
|
|
@@ -140,18 +140,29 @@ export interface TaintHop {
|
|
|
140
140
|
readonly viaCall?: boolean;
|
|
141
141
|
}
|
|
142
142
|
/**
|
|
143
|
-
* The
|
|
144
|
-
* occurrence itself
|
|
145
|
-
* worklist findings this is the ROOT source
|
|
143
|
+
* The source identity material for a finding: either the matched member-read
|
|
144
|
+
* occurrence itself (statement point + site index + object/property) or an
|
|
145
|
+
* assigned call-result source. For worklist findings this is the ROOT source
|
|
146
|
+
* the taint chain was seeded from.
|
|
146
147
|
*/
|
|
147
|
-
|
|
148
|
+
interface BaseSourceOccurrence {
|
|
148
149
|
readonly point: ProgramPoint;
|
|
149
150
|
/** Index into the source statement's `sites` array. */
|
|
150
151
|
readonly siteIndex: number;
|
|
152
|
+
readonly type: 'member-read' | 'call-result';
|
|
153
|
+
readonly kind: SourceKind;
|
|
154
|
+
}
|
|
155
|
+
interface MemberReadSourceOccurrence extends BaseSourceOccurrence {
|
|
156
|
+
readonly type: 'member-read';
|
|
151
157
|
readonly objectBindingIdx: number;
|
|
152
158
|
readonly property: string;
|
|
153
|
-
readonly kind: SourceKind;
|
|
154
159
|
}
|
|
160
|
+
interface CallResultSourceOccurrence extends BaseSourceOccurrence {
|
|
161
|
+
readonly type: 'call-result';
|
|
162
|
+
readonly resultBindingIdx: number;
|
|
163
|
+
readonly calleeName: string;
|
|
164
|
+
}
|
|
165
|
+
export type TaintSourceOccurrence = MemberReadSourceOccurrence | CallResultSourceOccurrence;
|
|
155
166
|
/** The sink side of a finding's identity: point + site + argument + binding. */
|
|
156
167
|
export interface TaintSinkOccurrence {
|
|
157
168
|
readonly point: ProgramPoint;
|
|
@@ -214,3 +225,4 @@ export interface FunctionTaintResult {
|
|
|
214
225
|
* model, the kind-set exclusion semantics, and the precision floor.
|
|
215
226
|
*/
|
|
216
227
|
export declare function computeTaintFlows(cfg: FunctionCfg, defUse: FunctionDefUse, matches: FunctionSiteMatches, limits?: TaintLimits): FunctionTaintResult;
|
|
228
|
+
export {};
|
|
@@ -332,17 +332,33 @@ export function computeTaintFlows(cfg, defUse, matches, limits) {
|
|
|
332
332
|
// KTD6 statement-level finding identity: source occurrence + sink occurrence
|
|
333
333
|
// + kind (NOT entryName). Computed standalone so the worklist can dedup-check
|
|
334
334
|
// BEFORE the cost of chainHops (first write wins; dedup-before-budget).
|
|
335
|
-
const findingKey = (sinkKind, source, sink) =>
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
335
|
+
const findingKey = (sinkKind, source, sink) => {
|
|
336
|
+
if (source.type === 'member-read') {
|
|
337
|
+
return [
|
|
338
|
+
sinkKind,
|
|
339
|
+
pointKey(source.point),
|
|
340
|
+
source.siteIndex,
|
|
341
|
+
source.objectBindingIdx,
|
|
342
|
+
source.property,
|
|
343
|
+
pointKey(sink.point),
|
|
344
|
+
sink.siteIndex,
|
|
345
|
+
sink.argIndex,
|
|
346
|
+
sink.bindingIdx,
|
|
347
|
+
].join('|');
|
|
348
|
+
}
|
|
349
|
+
return [
|
|
350
|
+
sinkKind,
|
|
351
|
+
pointKey(source.point),
|
|
352
|
+
source.siteIndex,
|
|
353
|
+
source.type,
|
|
354
|
+
source.resultBindingIdx,
|
|
355
|
+
source.calleeName,
|
|
356
|
+
pointKey(sink.point),
|
|
357
|
+
sink.siteIndex,
|
|
358
|
+
sink.argIndex,
|
|
359
|
+
sink.bindingIdx,
|
|
360
|
+
].join('|');
|
|
361
|
+
};
|
|
346
362
|
const recordFinding = (sinkKind, source, sink, hops, hopsTruncated) => {
|
|
347
363
|
const key = findingKey(sinkKind, source, sink);
|
|
348
364
|
if (findingsByIdentity.has(key))
|
|
@@ -498,12 +514,31 @@ export function computeTaintFlows(cfg, defUse, matches, limits) {
|
|
|
498
514
|
if (!ctx)
|
|
499
515
|
continue;
|
|
500
516
|
for (const src of sm.sources) {
|
|
517
|
+
if (src.type === 'call-result') {
|
|
518
|
+
const srcSite = ctx.sites[src.siteIndex];
|
|
519
|
+
if (srcSite?.callee === undefined)
|
|
520
|
+
continue;
|
|
521
|
+
const calleeName = srcSite.callee;
|
|
522
|
+
for (const d of src.resultDefs) {
|
|
523
|
+
const sourceOcc = {
|
|
524
|
+
point: ctx.point,
|
|
525
|
+
siteIndex: src.siteIndex,
|
|
526
|
+
type: 'call-result',
|
|
527
|
+
resultBindingIdx: d,
|
|
528
|
+
calleeName,
|
|
529
|
+
kind: src.entry.kind,
|
|
530
|
+
};
|
|
531
|
+
deriveTaint(d, ctx.point, EMPTY_KINDS, undefined, sourceOcc, false);
|
|
532
|
+
}
|
|
533
|
+
continue;
|
|
534
|
+
}
|
|
501
535
|
const srcSite = ctx.sites[src.siteIndex];
|
|
502
536
|
if (srcSite?.object === undefined || srcSite.property === undefined)
|
|
503
537
|
continue;
|
|
504
538
|
const sourceOcc = {
|
|
505
539
|
point: ctx.point,
|
|
506
540
|
siteIndex: src.siteIndex,
|
|
541
|
+
type: 'member-read',
|
|
507
542
|
objectBindingIdx: srcSite.object,
|
|
508
543
|
property: srcSite.property,
|
|
509
544
|
kind: src.entry.kind,
|
|
@@ -86,22 +86,38 @@ export interface TaintSanitizerEntry {
|
|
|
86
86
|
* the property is one of `properties` (`body`, `query`, …). Matching is
|
|
87
87
|
* name-based on the harvested `member-read` site (Semgrep-convention, not
|
|
88
88
|
* type-aware — the accepted M3 FP/FN trade recorded in the plan's risk
|
|
89
|
-
* table). One entry fans out over the objects × properties product.
|
|
89
|
+
* table). One entry fans out over the objects × properties product. `type`
|
|
90
|
+
* remains optional so existing/custom model objects that predate the
|
|
91
|
+
* discriminant continue to load as member-read sources.
|
|
90
92
|
*/
|
|
91
93
|
export interface TaintMemberSourceEntry {
|
|
94
|
+
readonly type?: 'member-read';
|
|
92
95
|
readonly kind: SourceKind;
|
|
93
96
|
readonly objects: readonly string[];
|
|
94
97
|
readonly properties: readonly string[];
|
|
95
98
|
}
|
|
99
|
+
/**
|
|
100
|
+
* A call-result taint source: the result of `<receiver>.<method>(...)` becomes
|
|
101
|
+
* tainted, but only when the call site records direct `resultDefs`. This keeps
|
|
102
|
+
* source seeding tied to proven data-flow destinations instead of treating an
|
|
103
|
+
* arbitrary nested call expression as a value occurrence.
|
|
104
|
+
*/
|
|
105
|
+
export interface TaintCallResultSourceEntry {
|
|
106
|
+
readonly type: 'call-result';
|
|
107
|
+
readonly kind: SourceKind;
|
|
108
|
+
readonly receivers: readonly string[];
|
|
109
|
+
readonly methods: readonly string[];
|
|
110
|
+
}
|
|
111
|
+
export type TaintSourceEntry = TaintMemberSourceEntry | TaintCallResultSourceEntry;
|
|
96
112
|
/**
|
|
97
113
|
* The taint configuration for a single language: which member reads introduce
|
|
98
114
|
* taint (sources), which callables are dangerous to reach with tainted input
|
|
99
115
|
* (sinks), and which callables clear it (sanitizers). M3 sources are
|
|
100
|
-
* member-read entries
|
|
101
|
-
*
|
|
116
|
+
* member-read entries for JS/TS/Python and call-result entries for languages
|
|
117
|
+
* whose request APIs return tainted values from calls.
|
|
102
118
|
*/
|
|
103
119
|
export interface SourceSinkSanitizerSpec {
|
|
104
|
-
readonly sources: readonly
|
|
120
|
+
readonly sources: readonly TaintSourceEntry[];
|
|
105
121
|
readonly sinks: readonly TaintSinkEntry[];
|
|
106
122
|
readonly sanitizers: readonly TaintSanitizerEntry[];
|
|
107
123
|
}
|
|
@@ -249,19 +249,29 @@ export function harvestFunctionSummary(cfg, defUse, matches) {
|
|
|
249
249
|
stmtIndex: sm.statementIndex,
|
|
250
250
|
line: facts.line,
|
|
251
251
|
};
|
|
252
|
+
const memberSources = sm.sources.filter((src) => src.type === 'member-read');
|
|
252
253
|
if (returnUseStmtKeys.has(stmtKey)) {
|
|
253
|
-
for (const src of
|
|
254
|
+
for (const src of memberSources)
|
|
254
255
|
sourceReturn.add(src.entry.kind);
|
|
255
256
|
}
|
|
256
|
-
|
|
257
|
-
|
|
257
|
+
if (memberSources.length > 0) {
|
|
258
|
+
for (const d of [...facts.defs, ...(facts.mayDefs ?? [])]) {
|
|
259
|
+
enqueue({ bindingIdx: d, point, seedId: -1, exclusions: new Set() });
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
for (const src of sm.sources) {
|
|
263
|
+
if (src.type !== 'call-result')
|
|
264
|
+
continue;
|
|
265
|
+
for (const d of src.resultDefs) {
|
|
266
|
+
enqueue({ bindingIdx: d, point, seedId: -1, exclusions: new Set() });
|
|
267
|
+
}
|
|
258
268
|
}
|
|
259
269
|
// DIRECT source-in-call-arg (`runIt(req.body)`): no intermediate binding is
|
|
260
270
|
// defined, so the floor seed above records nothing. Climb the source
|
|
261
271
|
// member-read's `parent` chain — each enclosing call/new site is a
|
|
262
272
|
// `sourceToCallArg` (the cross-function fixpoint seed). A sink ancestor is
|
|
263
273
|
// M3's intra-procedural concern and harmless to also record here.
|
|
264
|
-
for (const src of
|
|
274
|
+
for (const src of memberSources) {
|
|
265
275
|
let cur = facts.sites?.[src.siteIndex];
|
|
266
276
|
const guard = new Set([src.siteIndex]);
|
|
267
277
|
while (cur?.parent) {
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Built-in TS/JS taint model (#2083 M3 U2, plan KTD7).
|
|
3
3
|
*
|
|
4
|
-
* The canonical Express/Node source/sink/sanitizer set
|
|
5
|
-
*
|
|
4
|
+
* The canonical Express/Node source/sink/sanitizer set plus the Java and
|
|
5
|
+
* Python models, registered for their language ids via the EXPLICIT
|
|
6
6
|
* {@link registerBuiltinTaintModels} seam — deliberately not an import
|
|
7
7
|
* side-effect, so the U4 emit path controls WHEN registration happens (call
|
|
8
8
|
* it once before the pdg window runs; it is idempotent — the registry is
|
|
@@ -28,6 +28,7 @@ export declare const TS_JS_TAINT_MODEL: SourceSinkSanitizerSpec;
|
|
|
28
28
|
*/
|
|
29
29
|
export declare function computeTaintModelVersion(spec: SourceSinkSanitizerSpec): string;
|
|
30
30
|
export declare const BUILTIN_TAINT_MODELS: {
|
|
31
|
+
readonly java: SourceSinkSanitizerSpec;
|
|
31
32
|
readonly javascript: SourceSinkSanitizerSpec;
|
|
32
33
|
readonly python: SourceSinkSanitizerSpec;
|
|
33
34
|
readonly typescript: SourceSinkSanitizerSpec;
|
|
@@ -38,7 +39,7 @@ export declare const BUILTIN_TAINT_MODELS: {
|
|
|
38
39
|
*/
|
|
39
40
|
export declare const taintModelVersion: string;
|
|
40
41
|
/**
|
|
41
|
-
* Register the built-in models for TypeScript, JavaScript, and Python.
|
|
42
|
+
* Register the built-in models for Java, TypeScript, JavaScript, and Python.
|
|
42
43
|
* Explicit init seam for the U4 emit path (call before the pdg window
|
|
43
44
|
* consumes the registry); idempotent. Other language ids remain unregistered
|
|
44
45
|
* until they have a dedicated model.
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Built-in TS/JS taint model (#2083 M3 U2, plan KTD7).
|
|
3
3
|
*
|
|
4
|
-
* The canonical Express/Node source/sink/sanitizer set
|
|
5
|
-
*
|
|
4
|
+
* The canonical Express/Node source/sink/sanitizer set plus the Java and
|
|
5
|
+
* Python models, registered for their language ids via the EXPLICIT
|
|
6
6
|
* {@link registerBuiltinTaintModels} seam — deliberately not an import
|
|
7
7
|
* side-effect, so the U4 emit path controls WHEN registration happens (call
|
|
8
8
|
* it once before the pdg window runs; it is idempotent — the registry is
|
|
@@ -16,6 +16,7 @@
|
|
|
16
16
|
*/
|
|
17
17
|
import { createHash } from 'node:crypto';
|
|
18
18
|
import { SupportedLanguages } from '../../../_shared/index.js';
|
|
19
|
+
import { JAVA_TAINT_MODEL } from './java-model.js';
|
|
19
20
|
import { PYTHON_TAINT_MODEL } from './python-model.js';
|
|
20
21
|
import { registerSourceSinkConfig } from './source-sink-registry.js';
|
|
21
22
|
/**
|
|
@@ -93,6 +94,7 @@ function canonicalJson(value) {
|
|
|
93
94
|
return JSON.stringify(value);
|
|
94
95
|
}
|
|
95
96
|
export const BUILTIN_TAINT_MODELS = {
|
|
97
|
+
[SupportedLanguages.Java]: JAVA_TAINT_MODEL,
|
|
96
98
|
[SupportedLanguages.JavaScript]: TS_JS_TAINT_MODEL,
|
|
97
99
|
[SupportedLanguages.Python]: PYTHON_TAINT_MODEL,
|
|
98
100
|
[SupportedLanguages.TypeScript]: TS_JS_TAINT_MODEL,
|
|
@@ -103,12 +105,13 @@ export const BUILTIN_TAINT_MODELS = {
|
|
|
103
105
|
*/
|
|
104
106
|
export const taintModelVersion = computeModelDigest(BUILTIN_TAINT_MODELS);
|
|
105
107
|
/**
|
|
106
|
-
* Register the built-in models for TypeScript, JavaScript, and Python.
|
|
108
|
+
* Register the built-in models for Java, TypeScript, JavaScript, and Python.
|
|
107
109
|
* Explicit init seam for the U4 emit path (call before the pdg window
|
|
108
110
|
* consumes the registry); idempotent. Other language ids remain unregistered
|
|
109
111
|
* until they have a dedicated model.
|
|
110
112
|
*/
|
|
111
113
|
export function registerBuiltinTaintModels() {
|
|
114
|
+
registerSourceSinkConfig(SupportedLanguages.Java, JAVA_TAINT_MODEL);
|
|
112
115
|
registerSourceSinkConfig(SupportedLanguages.TypeScript, TS_JS_TAINT_MODEL);
|
|
113
116
|
registerSourceSinkConfig(SupportedLanguages.JavaScript, TS_JS_TAINT_MODEL);
|
|
114
117
|
registerSourceSinkConfig(SupportedLanguages.Python, PYTHON_TAINT_MODEL);
|
package/package.json
CHANGED