circle-ir 3.155.0 → 3.156.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/analysis/passes/mybatis-annotation-sql-sink-pass.d.ts +49 -0
- package/dist/analysis/passes/mybatis-annotation-sql-sink-pass.d.ts.map +1 -0
- package/dist/analysis/passes/mybatis-annotation-sql-sink-pass.js +329 -0
- package/dist/analysis/passes/mybatis-annotation-sql-sink-pass.js.map +1 -0
- package/dist/analysis/taint-matcher.d.ts.map +1 -1
- package/dist/analysis/taint-matcher.js +30 -0
- package/dist/analysis/taint-matcher.js.map +1 -1
- package/dist/analyzer.d.ts.map +1 -1
- package/dist/analyzer.js +8 -0
- package/dist/analyzer.js.map +1 -1
- package/dist/browser/circle-ir.js +337 -0
- package/dist/core/circle-ir-core.cjs +3 -0
- package/dist/core/circle-ir-core.js +3 -0
- package/dist/resolution/type-hierarchy.d.ts +15 -0
- package/dist/resolution/type-hierarchy.d.ts.map +1 -1
- package/dist/resolution/type-hierarchy.js +159 -0
- package/dist/resolution/type-hierarchy.js.map +1 -1
- package/package.json +1 -1
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* MyBatisAnnotationSqlSinkPass — cognium-dev #241 Java
|
|
3
|
+
*
|
|
4
|
+
* Detects SQL injection sinks on MyBatis annotation-driven Mapper interface
|
|
5
|
+
* methods. MyBatis distinguishes two placeholder syntaxes in `@Select` /
|
|
6
|
+
* `@Update` / `@Insert` / `@Delete` (and their `*Provider` variants):
|
|
7
|
+
*
|
|
8
|
+
* - `#{name}` — JDBC PreparedStatement parameter binding. SAFE.
|
|
9
|
+
* - `${name}` — raw string interpolation into the SQL string. **SQLi.**
|
|
10
|
+
*
|
|
11
|
+
* The taint-matcher's YAML sink registry (`configs/sinks/sql.yaml`) covers
|
|
12
|
+
* standard Mapper method names (insert/update/select-wildcard/delete) via a
|
|
13
|
+
* generic `mybatis_mapper_call` discovery marker, but cannot inspect the
|
|
14
|
+
* contents of an annotation string. Custom method names (`findByName`,
|
|
15
|
+
* `getUserById`) with `${}` interpolation therefore fall through both the
|
|
16
|
+
* name-based Mapper matcher and any downstream SQLi flow generator.
|
|
17
|
+
*
|
|
18
|
+
* This pass closes that gap. For every Java interface whose method carries
|
|
19
|
+
* a MyBatis SQL annotation containing at least one `${varname}`, the pass
|
|
20
|
+
* records the tainted parameter positions (correlated to `@Param("name")`
|
|
21
|
+
* annotations or MyBatis positional convention `${param1}` / `${0}`), then
|
|
22
|
+
* walks `graph.calls` for call sites targeting those Mapper methods and
|
|
23
|
+
* emits a synthetic `sql_injection` (CWE-89) `TaintSink` at each match.
|
|
24
|
+
*
|
|
25
|
+
* The synthetic sinks are pushed onto the `TaintMatcherResult.sinks` list
|
|
26
|
+
* so `SinkFilterPass` (which merges taint-matcher + language-sources) and
|
|
27
|
+
* downstream flow generators pick them up without any registry changes.
|
|
28
|
+
*
|
|
29
|
+
* Pipeline slot: runs after `LanguageSourcesPass` and before
|
|
30
|
+
* `SinkFilterPass` so the synthetic sinks are visible to the four-stage
|
|
31
|
+
* FP-elimination filter and to `TaintPropagationPass` / `InterproceduralPass`.
|
|
32
|
+
*
|
|
33
|
+
* Language scope: Java only. Non-Java files short-circuit at run().
|
|
34
|
+
*
|
|
35
|
+
* Kill switch: opt out via `disabledPasses: ['mybatis-annotation-sql-sink']`.
|
|
36
|
+
*/
|
|
37
|
+
import type { AnalysisPass, PassContext } from '../../graph/analysis-pass.js';
|
|
38
|
+
export interface MyBatisAnnotationSqlSinkResult {
|
|
39
|
+
/** Number of annotated Mapper methods with `${}` interpolation found. */
|
|
40
|
+
annotatedMethodCount: number;
|
|
41
|
+
/** Number of synthetic `sql_injection` sinks added. */
|
|
42
|
+
addedSinkCount: number;
|
|
43
|
+
}
|
|
44
|
+
export declare class MyBatisAnnotationSqlSinkPass implements AnalysisPass<MyBatisAnnotationSqlSinkResult> {
|
|
45
|
+
readonly name = "mybatis-annotation-sql-sink";
|
|
46
|
+
readonly category: "security";
|
|
47
|
+
run(ctx: PassContext): MyBatisAnnotationSqlSinkResult;
|
|
48
|
+
}
|
|
49
|
+
//# sourceMappingURL=mybatis-annotation-sql-sink-pass.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mybatis-annotation-sql-sink-pass.d.ts","sourceRoot":"","sources":["../../../src/analysis/passes/mybatis-annotation-sql-sink-pass.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAmCG;AAQH,OAAO,KAAK,EAAE,YAAY,EAAE,WAAW,EAAE,MAAM,8BAA8B,CAAC;AAG9E,MAAM,WAAW,8BAA8B;IAC7C,yEAAyE;IACzE,oBAAoB,EAAE,MAAM,CAAC;IAC7B,uDAAuD;IACvD,cAAc,EAAE,MAAM,CAAC;CACxB;AAqND,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;CA0HtD"}
|
|
@@ -0,0 +1,329 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* MyBatisAnnotationSqlSinkPass — cognium-dev #241 Java
|
|
3
|
+
*
|
|
4
|
+
* Detects SQL injection sinks on MyBatis annotation-driven Mapper interface
|
|
5
|
+
* methods. MyBatis distinguishes two placeholder syntaxes in `@Select` /
|
|
6
|
+
* `@Update` / `@Insert` / `@Delete` (and their `*Provider` variants):
|
|
7
|
+
*
|
|
8
|
+
* - `#{name}` — JDBC PreparedStatement parameter binding. SAFE.
|
|
9
|
+
* - `${name}` — raw string interpolation into the SQL string. **SQLi.**
|
|
10
|
+
*
|
|
11
|
+
* The taint-matcher's YAML sink registry (`configs/sinks/sql.yaml`) covers
|
|
12
|
+
* standard Mapper method names (insert/update/select-wildcard/delete) via a
|
|
13
|
+
* generic `mybatis_mapper_call` discovery marker, but cannot inspect the
|
|
14
|
+
* contents of an annotation string. Custom method names (`findByName`,
|
|
15
|
+
* `getUserById`) with `${}` interpolation therefore fall through both the
|
|
16
|
+
* name-based Mapper matcher and any downstream SQLi flow generator.
|
|
17
|
+
*
|
|
18
|
+
* This pass closes that gap. For every Java interface whose method carries
|
|
19
|
+
* a MyBatis SQL annotation containing at least one `${varname}`, the pass
|
|
20
|
+
* records the tainted parameter positions (correlated to `@Param("name")`
|
|
21
|
+
* annotations or MyBatis positional convention `${param1}` / `${0}`), then
|
|
22
|
+
* walks `graph.calls` for call sites targeting those Mapper methods and
|
|
23
|
+
* emits a synthetic `sql_injection` (CWE-89) `TaintSink` at each match.
|
|
24
|
+
*
|
|
25
|
+
* The synthetic sinks are pushed onto the `TaintMatcherResult.sinks` list
|
|
26
|
+
* so `SinkFilterPass` (which merges taint-matcher + language-sources) and
|
|
27
|
+
* downstream flow generators pick them up without any registry changes.
|
|
28
|
+
*
|
|
29
|
+
* Pipeline slot: runs after `LanguageSourcesPass` and before
|
|
30
|
+
* `SinkFilterPass` so the synthetic sinks are visible to the four-stage
|
|
31
|
+
* FP-elimination filter and to `TaintPropagationPass` / `InterproceduralPass`.
|
|
32
|
+
*
|
|
33
|
+
* Language scope: Java only. Non-Java files short-circuit at run().
|
|
34
|
+
*
|
|
35
|
+
* Kill switch: opt out via `disabledPasses: ['mybatis-annotation-sql-sink']`.
|
|
36
|
+
*/
|
|
37
|
+
/**
|
|
38
|
+
* MyBatis SQL annotations. The `*Provider` variants take a
|
|
39
|
+
* `type = X.class, method = "y"` pair whose provider method returns the
|
|
40
|
+
* SQL string; we still scan the annotation-attached literal (if any) for
|
|
41
|
+
* `${}` markers as a best-effort recall bump.
|
|
42
|
+
*/
|
|
43
|
+
const MYBATIS_SQL_ANNOTATIONS = new Set([
|
|
44
|
+
'Select',
|
|
45
|
+
'Update',
|
|
46
|
+
'Insert',
|
|
47
|
+
'Delete',
|
|
48
|
+
'SelectProvider',
|
|
49
|
+
'UpdateProvider',
|
|
50
|
+
'InsertProvider',
|
|
51
|
+
'DeleteProvider',
|
|
52
|
+
]);
|
|
53
|
+
/**
|
|
54
|
+
* Regex to extract `${varname}` interpolation markers from an annotation
|
|
55
|
+
* string. Deliberately does NOT match `#{name}` (safe binding).
|
|
56
|
+
* Variable names follow the MyBatis convention (identifier + optional
|
|
57
|
+
* dotted property access, e.g. `${user.id}`) — we only use the leading
|
|
58
|
+
* identifier when correlating to `@Param`.
|
|
59
|
+
*/
|
|
60
|
+
const DOLLAR_BRACE_RE = /\$\{([A-Za-z_][A-Za-z0-9_]*)(?:\.[A-Za-z0-9_.]+)?\}/g;
|
|
61
|
+
/**
|
|
62
|
+
* MyBatis positional convention: `${param1}` refers to the first argument
|
|
63
|
+
* (1-based). `${0}` — some MyBatis versions — refers to the first argument
|
|
64
|
+
* (0-based). We map both forms to a 0-based index.
|
|
65
|
+
*/
|
|
66
|
+
function parsePositionalRef(name) {
|
|
67
|
+
const paramMatch = /^param(\d+)$/.exec(name);
|
|
68
|
+
if (paramMatch) {
|
|
69
|
+
const oneBased = Number.parseInt(paramMatch[1], 10);
|
|
70
|
+
if (Number.isFinite(oneBased) && oneBased >= 1)
|
|
71
|
+
return oneBased - 1;
|
|
72
|
+
}
|
|
73
|
+
const zeroMatch = /^(\d+)$/.exec(name);
|
|
74
|
+
if (zeroMatch) {
|
|
75
|
+
const zeroBased = Number.parseInt(zeroMatch[1], 10);
|
|
76
|
+
if (Number.isFinite(zeroBased) && zeroBased >= 0)
|
|
77
|
+
return zeroBased;
|
|
78
|
+
}
|
|
79
|
+
return null;
|
|
80
|
+
}
|
|
81
|
+
/**
|
|
82
|
+
* Extract the raw annotation-argument string from a stored annotation.
|
|
83
|
+
* Annotations are stored as e.g. `Select("SELECT ... ${x} ...")` (without
|
|
84
|
+
* the leading `@`). We return the substring between the outermost
|
|
85
|
+
* parentheses, or `null` for a marker annotation with no arguments.
|
|
86
|
+
*/
|
|
87
|
+
function extractAnnotationBody(annotation) {
|
|
88
|
+
const openIdx = annotation.indexOf('(');
|
|
89
|
+
if (openIdx < 0)
|
|
90
|
+
return null;
|
|
91
|
+
const closeIdx = annotation.lastIndexOf(')');
|
|
92
|
+
if (closeIdx <= openIdx)
|
|
93
|
+
return null;
|
|
94
|
+
return annotation.substring(openIdx + 1, closeIdx);
|
|
95
|
+
}
|
|
96
|
+
/**
|
|
97
|
+
* Given a stored annotation `Foo(...)`, return the leading identifier
|
|
98
|
+
* `Foo`. For marker annotations without parens, returns the whole string.
|
|
99
|
+
*/
|
|
100
|
+
function annotationName(annotation) {
|
|
101
|
+
const openIdx = annotation.indexOf('(');
|
|
102
|
+
return openIdx < 0 ? annotation : annotation.substring(0, openIdx);
|
|
103
|
+
}
|
|
104
|
+
/**
|
|
105
|
+
* Return the value of the first string argument to a `@Param("name")`
|
|
106
|
+
* annotation, or `null` if the annotation is not `@Param` or has no
|
|
107
|
+
* literal string argument.
|
|
108
|
+
*/
|
|
109
|
+
function extractParamName(annotation) {
|
|
110
|
+
if (annotationName(annotation) !== 'Param')
|
|
111
|
+
return null;
|
|
112
|
+
const body = extractAnnotationBody(annotation);
|
|
113
|
+
if (body === null)
|
|
114
|
+
return null;
|
|
115
|
+
// Match "..." or '...' at the start of the body. MyBatis @Param only
|
|
116
|
+
// takes a single string literal, so a simple match is sufficient.
|
|
117
|
+
const strMatch = /^\s*["']([^"']*)["']/.exec(body);
|
|
118
|
+
return strMatch ? strMatch[1] : null;
|
|
119
|
+
}
|
|
120
|
+
/**
|
|
121
|
+
* Extract every `${varname}` reference from a MyBatis annotation body
|
|
122
|
+
* (typically a single quoted string literal, sometimes concatenated).
|
|
123
|
+
* Returns the deduplicated ordered list of leading identifiers.
|
|
124
|
+
*/
|
|
125
|
+
function extractDollarBraceRefs(annotationBody) {
|
|
126
|
+
const seen = new Set();
|
|
127
|
+
const out = [];
|
|
128
|
+
DOLLAR_BRACE_RE.lastIndex = 0;
|
|
129
|
+
let m;
|
|
130
|
+
while ((m = DOLLAR_BRACE_RE.exec(annotationBody)) !== null) {
|
|
131
|
+
const name = m[1];
|
|
132
|
+
if (!seen.has(name)) {
|
|
133
|
+
seen.add(name);
|
|
134
|
+
out.push(name);
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
return out;
|
|
138
|
+
}
|
|
139
|
+
/**
|
|
140
|
+
* Check if the file imports MyBatis annotations, either directly or via
|
|
141
|
+
* wildcard. Used to distinguish MyBatis `@Select` from unrelated libraries
|
|
142
|
+
* (e.g. Reactor's `@Select`, custom application `@Select`) that happen to
|
|
143
|
+
* share the simple annotation name.
|
|
144
|
+
*/
|
|
145
|
+
function fileImportsMyBatis(imports) {
|
|
146
|
+
for (const imp of imports) {
|
|
147
|
+
const pkg = imp.from_package;
|
|
148
|
+
if (!pkg)
|
|
149
|
+
continue;
|
|
150
|
+
if (pkg === 'org.apache.ibatis.annotations' ||
|
|
151
|
+
pkg.startsWith('org.apache.ibatis.annotations.') ||
|
|
152
|
+
pkg === 'org.apache.ibatis' ||
|
|
153
|
+
pkg.startsWith('org.apache.ibatis.')) {
|
|
154
|
+
return true;
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
return false;
|
|
158
|
+
}
|
|
159
|
+
/**
|
|
160
|
+
* Correlate every `${name}` reference to a 0-based parameter index. When
|
|
161
|
+
* the method carries `@Param("name")` on any parameter, that mapping wins.
|
|
162
|
+
* Otherwise MyBatis positional convention is used (`${param1}`, `${0}`).
|
|
163
|
+
* Returns the deduplicated ascending list of tainted arg positions.
|
|
164
|
+
*/
|
|
165
|
+
function correlateDollarBraceToArgPositions(method, refs) {
|
|
166
|
+
const paramNameToIndex = new Map();
|
|
167
|
+
method.parameters.forEach((param, idx) => {
|
|
168
|
+
for (const ann of param.annotations) {
|
|
169
|
+
const paramName = extractParamName(ann);
|
|
170
|
+
if (paramName !== null) {
|
|
171
|
+
paramNameToIndex.set(paramName, idx);
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
});
|
|
175
|
+
const positions = new Set();
|
|
176
|
+
for (const ref of refs) {
|
|
177
|
+
const namedIdx = paramNameToIndex.get(ref);
|
|
178
|
+
if (namedIdx !== undefined) {
|
|
179
|
+
positions.add(namedIdx);
|
|
180
|
+
continue;
|
|
181
|
+
}
|
|
182
|
+
const positionalIdx = parsePositionalRef(ref);
|
|
183
|
+
if (positionalIdx !== null &&
|
|
184
|
+
positionalIdx < method.parameters.length) {
|
|
185
|
+
positions.add(positionalIdx);
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
return Array.from(positions).sort((a, b) => a - b);
|
|
189
|
+
}
|
|
190
|
+
/**
|
|
191
|
+
* Return the fully-qualified name of a Mapper interface method for use as
|
|
192
|
+
* a callee lookup key. Falls back to `<simpleName>.<methodName>` when the
|
|
193
|
+
* interface has no declared package.
|
|
194
|
+
*/
|
|
195
|
+
function mapperMethodKey(type, method) {
|
|
196
|
+
const iface = type.package ? `${type.package}.${type.name}` : type.name;
|
|
197
|
+
return `${iface}.${method.name}`;
|
|
198
|
+
}
|
|
199
|
+
/**
|
|
200
|
+
* Match a call site against a Mapper interface method by callee tail. We
|
|
201
|
+
* accept:
|
|
202
|
+
* - `resolution.target` equal to or ending with the mapper key
|
|
203
|
+
* - simple `method_name` equal to the mapper's method AND
|
|
204
|
+
* `receiver_type` or `receiver_type_fqn` equal to the mapper's
|
|
205
|
+
* interface (simple- or fully-qualified name).
|
|
206
|
+
*
|
|
207
|
+
* Conservative on both sides — unresolved receivers fall through.
|
|
208
|
+
*/
|
|
209
|
+
function isMapperMethodCall(call, interfaceSimpleName, interfaceFqn, methodName) {
|
|
210
|
+
// Path 1 — resolution target tail match.
|
|
211
|
+
const target = call.resolution?.target;
|
|
212
|
+
if (target) {
|
|
213
|
+
const suffix = `${interfaceSimpleName}.${methodName}`;
|
|
214
|
+
const suffixFqn = `${interfaceFqn}.${methodName}`;
|
|
215
|
+
if (target === suffix ||
|
|
216
|
+
target === suffixFqn ||
|
|
217
|
+
target.endsWith('.' + suffix) ||
|
|
218
|
+
target.endsWith('.' + suffixFqn)) {
|
|
219
|
+
return true;
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
// Path 2 — method name + receiver type match.
|
|
223
|
+
if (call.method_name !== methodName)
|
|
224
|
+
return false;
|
|
225
|
+
if (call.receiver_type === interfaceSimpleName)
|
|
226
|
+
return true;
|
|
227
|
+
if (call.receiver_type_fqn === interfaceFqn)
|
|
228
|
+
return true;
|
|
229
|
+
return false;
|
|
230
|
+
}
|
|
231
|
+
export class MyBatisAnnotationSqlSinkPass {
|
|
232
|
+
name = 'mybatis-annotation-sql-sink';
|
|
233
|
+
category = 'security';
|
|
234
|
+
run(ctx) {
|
|
235
|
+
if (ctx.language !== 'java') {
|
|
236
|
+
return { annotatedMethodCount: 0, addedSinkCount: 0 };
|
|
237
|
+
}
|
|
238
|
+
const { types, imports, calls } = ctx.graph.ir;
|
|
239
|
+
// Gate on file-level MyBatis import to distinguish real Mapper
|
|
240
|
+
// interfaces from unrelated libraries reusing the `@Select` name.
|
|
241
|
+
if (!fileImportsMyBatis(imports)) {
|
|
242
|
+
return { annotatedMethodCount: 0, addedSinkCount: 0 };
|
|
243
|
+
}
|
|
244
|
+
const mapperMethods = [];
|
|
245
|
+
for (const type of types) {
|
|
246
|
+
if (type.kind !== 'interface')
|
|
247
|
+
continue;
|
|
248
|
+
for (const method of type.methods) {
|
|
249
|
+
// Find MyBatis SQL annotation, extract body, scan for `${}`.
|
|
250
|
+
let matchedRefs = [];
|
|
251
|
+
for (const ann of method.annotations) {
|
|
252
|
+
if (!MYBATIS_SQL_ANNOTATIONS.has(annotationName(ann)))
|
|
253
|
+
continue;
|
|
254
|
+
const body = extractAnnotationBody(ann);
|
|
255
|
+
if (body === null)
|
|
256
|
+
continue;
|
|
257
|
+
const refs = extractDollarBraceRefs(body);
|
|
258
|
+
if (refs.length > 0) {
|
|
259
|
+
matchedRefs = [...matchedRefs, ...refs];
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
if (matchedRefs.length === 0)
|
|
263
|
+
continue;
|
|
264
|
+
const positions = correlateDollarBraceToArgPositions(method, matchedRefs);
|
|
265
|
+
if (positions.length === 0)
|
|
266
|
+
continue;
|
|
267
|
+
const interfaceFqn = type.package
|
|
268
|
+
? `${type.package}.${type.name}`
|
|
269
|
+
: type.name;
|
|
270
|
+
mapperMethods.push({
|
|
271
|
+
interfaceSimpleName: type.name,
|
|
272
|
+
interfaceFqn,
|
|
273
|
+
methodName: method.name,
|
|
274
|
+
taintedArgPositions: positions,
|
|
275
|
+
});
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
if (mapperMethods.length === 0) {
|
|
279
|
+
return {
|
|
280
|
+
annotatedMethodCount: 0,
|
|
281
|
+
addedSinkCount: 0,
|
|
282
|
+
};
|
|
283
|
+
}
|
|
284
|
+
// Grab the taint-matcher sinks array to append into. When the pass is
|
|
285
|
+
// run stand-alone (unit-test harness) without TaintMatcherPass, we
|
|
286
|
+
// fall back to graph.ir.taint.sinks so tests can still observe the
|
|
287
|
+
// added sinks.
|
|
288
|
+
const sinks = ctx.hasResult('taint-matcher')
|
|
289
|
+
? ctx.getResult('taint-matcher').sinks
|
|
290
|
+
: ctx.graph.ir.taint.sinks;
|
|
291
|
+
let addedSinkCount = 0;
|
|
292
|
+
for (const call of calls) {
|
|
293
|
+
for (const rec of mapperMethods) {
|
|
294
|
+
if (!isMapperMethodCall(call, rec.interfaceSimpleName, rec.interfaceFqn, rec.methodName)) {
|
|
295
|
+
continue;
|
|
296
|
+
}
|
|
297
|
+
// Dedup: skip if a `sql_injection` sink already exists at this
|
|
298
|
+
// call site.
|
|
299
|
+
const line = call.location.line;
|
|
300
|
+
if (sinks.some((s) => s.line === line &&
|
|
301
|
+
s.type === 'sql_injection' &&
|
|
302
|
+
s.method === rec.methodName)) {
|
|
303
|
+
continue;
|
|
304
|
+
}
|
|
305
|
+
const receiverLoc = call.receiver
|
|
306
|
+
? `${call.receiver}.${rec.methodName}()`
|
|
307
|
+
: `${rec.methodName}()`;
|
|
308
|
+
sinks.push({
|
|
309
|
+
type: 'sql_injection',
|
|
310
|
+
cwe: 'CWE-89',
|
|
311
|
+
location: call.in_method
|
|
312
|
+
? `${receiverLoc} in ${call.in_method}`
|
|
313
|
+
: receiverLoc,
|
|
314
|
+
line,
|
|
315
|
+
confidence: 0.95,
|
|
316
|
+
method: rec.methodName,
|
|
317
|
+
argPositions: rec.taintedArgPositions,
|
|
318
|
+
class: rec.interfaceSimpleName,
|
|
319
|
+
});
|
|
320
|
+
addedSinkCount++;
|
|
321
|
+
}
|
|
322
|
+
}
|
|
323
|
+
return {
|
|
324
|
+
annotatedMethodCount: mapperMethods.length,
|
|
325
|
+
addedSinkCount,
|
|
326
|
+
};
|
|
327
|
+
}
|
|
328
|
+
}
|
|
329
|
+
//# sourceMappingURL=mybatis-annotation-sql-sink-pass.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mybatis-annotation-sql-sink-pass.js","sourceRoot":"","sources":["../../../src/analysis/passes/mybatis-annotation-sql-sink-pass.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"taint-matcher.d.ts","sourceRoot":"","sources":["../../src/analysis/taint-matcher.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,KAAK,EAAgB,QAAQ,EAAE,QAAQ,EAAE,WAAW,EAAE,SAAS,EAAkB,KAAK,EAAwB,iBAAiB,EAAE,MAAM,mBAAmB,CAAC;AAClK,OAAO,KAAK,EAAE,WAAW,EAAiB,WAAW,EAAoB,MAAM,oBAAoB,CAAC;AACpG,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,iCAAiC,CAAC;AA2B7E;;;;;;;GAOG;AACH,wBAAgB,YAAY,CAC1B,KAAK,EAAE,QAAQ,EAAE,EACjB,KAAK,EAAE,QAAQ,EAAE,EACjB,MAAM,GAAE,WAAgC,EACxC,aAAa,CAAC,EAAE,qBAAqB,EACrC,QAAQ,CAAC,EAAE,iBAAiB,EAC5B,IAAI,CAAC,EAAE,MAAM,GACZ,KAAK,CASP;AAqID;;;;GAIG;AACH,wBAAgB,oBAAoB,CAClC,OAAO,EAAE,WAAW,EAAE,EACtB,KAAK,EAAE,SAAS,EAAE,EAClB,IAAI,EAAE,MAAM,GACX,IAAI,CAYN;
|
|
1
|
+
{"version":3,"file":"taint-matcher.d.ts","sourceRoot":"","sources":["../../src/analysis/taint-matcher.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,KAAK,EAAgB,QAAQ,EAAE,QAAQ,EAAE,WAAW,EAAE,SAAS,EAAkB,KAAK,EAAwB,iBAAiB,EAAE,MAAM,mBAAmB,CAAC;AAClK,OAAO,KAAK,EAAE,WAAW,EAAiB,WAAW,EAAoB,MAAM,oBAAoB,CAAC;AACpG,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,iCAAiC,CAAC;AA2B7E;;;;;;;GAOG;AACH,wBAAgB,YAAY,CAC1B,KAAK,EAAE,QAAQ,EAAE,EACjB,KAAK,EAAE,QAAQ,EAAE,EACjB,MAAM,GAAE,WAAgC,EACxC,aAAa,CAAC,EAAE,qBAAqB,EACrC,QAAQ,CAAC,EAAE,iBAAiB,EAC5B,IAAI,CAAC,EAAE,MAAM,GACZ,KAAK,CASP;AAqID;;;;GAIG;AACH,wBAAgB,oBAAoB,CAClC,OAAO,EAAE,WAAW,EAAE,EACtB,KAAK,EAAE,SAAS,EAAE,EAClB,IAAI,EAAE,MAAM,GACX,IAAI,CAYN;AA6sED;;GAEG;AACH,wBAAgB,qBAAqB,CACnC,WAAW,EAAE,MAAM,EACnB,OAAO,EAAE,WAAW,GACnB,OAAO,CAET"}
|
|
@@ -1799,12 +1799,42 @@ function matchesSinkPattern(call, pattern, typeHierarchy, language) {
|
|
|
1799
1799
|
// Prefer the IR-resolved receiver type when the language plugin populates it
|
|
1800
1800
|
// (Java/TS resolve simple types). Falls back to the receiver-name heuristic
|
|
1801
1801
|
// when the type is unresolved.
|
|
1802
|
+
//
|
|
1803
|
+
// Subtype-based matches (`isSubtypeOf`) are additionally gated on the call
|
|
1804
|
+
// site providing the arguments the sink actually inspects — otherwise the
|
|
1805
|
+
// JDBC-style `PreparedStatement.executeQuery()` (0 args) would spuriously
|
|
1806
|
+
// match `Statement.executeQuery(String)` (arg_positions [0]) via the
|
|
1807
|
+
// `PreparedStatement extends Statement` fact. The direct-name path is not
|
|
1808
|
+
// gated to preserve legacy behaviour where sink pattern arity was checked
|
|
1809
|
+
// downstream. — cognium-dev #241 Java
|
|
1810
|
+
const subtypeArgArityOk = !pattern.arg_positions ||
|
|
1811
|
+
pattern.arg_positions.length === 0 ||
|
|
1812
|
+
pattern.arg_positions.every((pos) => pos < (call.arguments?.length ?? 0));
|
|
1802
1813
|
if (call.receiver_type && call.receiver_type === pattern.class) {
|
|
1803
1814
|
// Resolved type matches — accept directly.
|
|
1804
1815
|
}
|
|
1816
|
+
else if (call.receiver_type &&
|
|
1817
|
+
typeHierarchy &&
|
|
1818
|
+
subtypeArgArityOk &&
|
|
1819
|
+
typeHierarchy.isSubtypeOf(call.receiver_type, pattern.class)) {
|
|
1820
|
+
// Resolved type is a subtype of the sink class via TypeHierarchyResolver.
|
|
1821
|
+
// Enables sink patterns keyed on a base interface/class (e.g.
|
|
1822
|
+
// `HttpClient.execute`) to match subtype receivers (Apache
|
|
1823
|
+
// `CloseableHttpClient`, `InternalHttpClient`, etc.) whose hierarchy is
|
|
1824
|
+
// pre-registered via `registerCommonLibraries()`. — cognium-dev #241 Java
|
|
1825
|
+
}
|
|
1805
1826
|
else if (call.receiver_type_fqn && call.receiver_type_fqn.endsWith('.' + pattern.class)) {
|
|
1806
1827
|
// FQN tail matches simple class name.
|
|
1807
1828
|
}
|
|
1829
|
+
else if (call.receiver_type_fqn &&
|
|
1830
|
+
typeHierarchy &&
|
|
1831
|
+
subtypeArgArityOk &&
|
|
1832
|
+
typeHierarchy.isSubtypeOf(call.receiver_type_fqn, pattern.class)) {
|
|
1833
|
+
// FQN receiver type is a subtype of the sink class via
|
|
1834
|
+
// TypeHierarchyResolver. Same rationale as the simple-name subtype
|
|
1835
|
+
// check above; covers the case where the language plugin resolves the
|
|
1836
|
+
// receiver to a fully qualified name.
|
|
1837
|
+
}
|
|
1808
1838
|
else if (call.receiver && !receiverMightBeClass(call.receiver, pattern.class)) {
|
|
1809
1839
|
// Heuristic match failed; fall back to TypeHierarchyResolver if available
|
|
1810
1840
|
if (typeHierarchy && typeHierarchy.couldBeType(call.receiver, pattern.class)) {
|