circle-ir 3.41.0 → 3.43.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/configs/sinks/sql.yaml +22 -22
- package/dist/analysis/config-loader.d.ts.map +1 -1
- package/dist/analysis/config-loader.js +17 -0
- package/dist/analysis/config-loader.js.map +1 -1
- package/dist/analysis/findings.js +7 -7
- package/dist/analysis/findings.js.map +1 -1
- package/dist/analysis/rules.d.ts.map +1 -1
- package/dist/analysis/rules.js +9 -0
- package/dist/analysis/rules.js.map +1 -1
- package/dist/analysis/taint-matcher.d.ts.map +1 -1
- package/dist/analysis/taint-matcher.js +15 -0
- package/dist/analysis/taint-matcher.js.map +1 -1
- package/dist/analysis/taint-propagation.js +1 -1
- package/dist/analysis/taint-propagation.js.map +1 -1
- package/dist/browser/circle-ir.js +153 -6
- package/dist/core/circle-ir-core.cjs +153 -6
- package/dist/core/circle-ir-core.js +153 -6
- package/dist/core/extractors/calls.d.ts.map +1 -1
- package/dist/core/extractors/calls.js +161 -8
- package/dist/core/extractors/calls.js.map +1 -1
- package/dist/types/index.d.ts +14 -1
- package/dist/types/index.d.ts.map +1 -1
- package/package.json +1 -1
|
@@ -6096,11 +6096,22 @@ function inferJSTypeFromReceiver(receiver) {
|
|
|
6096
6096
|
function buildResolutionContext(tree, cache) {
|
|
6097
6097
|
const context = {
|
|
6098
6098
|
className: null,
|
|
6099
|
+
packageName: null,
|
|
6099
6100
|
methodNames: /* @__PURE__ */ new Set(),
|
|
6100
6101
|
fieldTypes: /* @__PURE__ */ new Map(),
|
|
6101
6102
|
localVarTypes: /* @__PURE__ */ new Map(),
|
|
6102
|
-
|
|
6103
|
+
paramTypes: /* @__PURE__ */ new Map(),
|
|
6104
|
+
imports: /* @__PURE__ */ new Map(),
|
|
6105
|
+
wildcardImports: []
|
|
6103
6106
|
};
|
|
6107
|
+
const packages = getNodesFromCache(tree.rootNode, "package_declaration", cache);
|
|
6108
|
+
if (packages.length > 0) {
|
|
6109
|
+
const text = getNodeText(packages[0]);
|
|
6110
|
+
const match = text.match(/package\s+([a-zA-Z0-9_.]+)/);
|
|
6111
|
+
if (match) {
|
|
6112
|
+
context.packageName = match[1];
|
|
6113
|
+
}
|
|
6114
|
+
}
|
|
6104
6115
|
const classes = getNodesFromCache(tree.rootNode, "class_declaration", cache);
|
|
6105
6116
|
if (classes.length > 0) {
|
|
6106
6117
|
const nameNode = classes[0].childForFieldName("name");
|
|
@@ -6114,6 +6125,17 @@ function buildResolutionContext(tree, cache) {
|
|
|
6114
6125
|
if (nameNode) {
|
|
6115
6126
|
context.methodNames.add(getNodeText(nameNode));
|
|
6116
6127
|
}
|
|
6128
|
+
const paramsNode = method.childForFieldName("parameters");
|
|
6129
|
+
if (paramsNode) {
|
|
6130
|
+
collectParameterTypes(paramsNode, context.paramTypes);
|
|
6131
|
+
}
|
|
6132
|
+
}
|
|
6133
|
+
const constructors = getNodesFromCache(tree.rootNode, "constructor_declaration", cache);
|
|
6134
|
+
for (const ctor of constructors) {
|
|
6135
|
+
const paramsNode = ctor.childForFieldName("parameters");
|
|
6136
|
+
if (paramsNode) {
|
|
6137
|
+
collectParameterTypes(paramsNode, context.paramTypes);
|
|
6138
|
+
}
|
|
6117
6139
|
}
|
|
6118
6140
|
const fields = getNodesFromCache(tree.rootNode, "field_declaration", cache);
|
|
6119
6141
|
for (const field of fields) {
|
|
@@ -6146,14 +6168,106 @@ function buildResolutionContext(tree, cache) {
|
|
|
6146
6168
|
const imports = getNodesFromCache(tree.rootNode, "import_declaration", cache);
|
|
6147
6169
|
for (const imp of imports) {
|
|
6148
6170
|
const text = getNodeText(imp);
|
|
6149
|
-
const match = text.match(/import\s+(?:static\s+)?([a-zA-Z0-9_.]+)
|
|
6150
|
-
if (match)
|
|
6151
|
-
|
|
6152
|
-
|
|
6171
|
+
const match = text.match(/import\s+(?:static\s+)?([a-zA-Z0-9_.]+)(\.\*)?/);
|
|
6172
|
+
if (!match) continue;
|
|
6173
|
+
const fqn = match[1];
|
|
6174
|
+
const isWildcard = match[2] === ".*";
|
|
6175
|
+
if (isWildcard) {
|
|
6176
|
+
context.wildcardImports.push(fqn);
|
|
6177
|
+
} else {
|
|
6178
|
+
const parts2 = fqn.split(".");
|
|
6179
|
+
const simple = parts2[parts2.length - 1];
|
|
6180
|
+
context.imports.set(simple, fqn);
|
|
6153
6181
|
}
|
|
6154
6182
|
}
|
|
6155
6183
|
return context;
|
|
6156
6184
|
}
|
|
6185
|
+
function collectParameterTypes(paramsNode, out2) {
|
|
6186
|
+
for (let i2 = 0; i2 < paramsNode.childCount; i2++) {
|
|
6187
|
+
const child = paramsNode.child(i2);
|
|
6188
|
+
if (!child) continue;
|
|
6189
|
+
if (child.type !== "formal_parameter" && child.type !== "spread_parameter") {
|
|
6190
|
+
continue;
|
|
6191
|
+
}
|
|
6192
|
+
const typeNode = child.childForFieldName("type");
|
|
6193
|
+
const nameNode = child.childForFieldName("name");
|
|
6194
|
+
if (typeNode && nameNode) {
|
|
6195
|
+
out2.set(getNodeText(nameNode), getNodeText(typeNode));
|
|
6196
|
+
}
|
|
6197
|
+
}
|
|
6198
|
+
}
|
|
6199
|
+
function stripGenerics(type) {
|
|
6200
|
+
const ltIdx = type.indexOf("<");
|
|
6201
|
+
return ltIdx === -1 ? type : type.substring(0, ltIdx);
|
|
6202
|
+
}
|
|
6203
|
+
function resolveReceiverType(receiver, context) {
|
|
6204
|
+
if (!receiver) return { simpleName: null, fqn: null };
|
|
6205
|
+
if (receiver === "this") {
|
|
6206
|
+
return resolveFqn(context.className, context);
|
|
6207
|
+
}
|
|
6208
|
+
if (receiver.startsWith("this.")) {
|
|
6209
|
+
const fieldName = receiver.substring("this.".length);
|
|
6210
|
+
const fieldType = context.fieldTypes.get(fieldName);
|
|
6211
|
+
if (fieldType) return resolveFqn(stripGenerics(fieldType), context);
|
|
6212
|
+
return { simpleName: null, fqn: null };
|
|
6213
|
+
}
|
|
6214
|
+
if (receiver === "super") return { simpleName: null, fqn: null };
|
|
6215
|
+
const declaredType = context.localVarTypes.get(receiver) ?? context.paramTypes.get(receiver) ?? context.fieldTypes.get(receiver);
|
|
6216
|
+
if (declaredType) {
|
|
6217
|
+
return resolveFqn(stripGenerics(declaredType), context);
|
|
6218
|
+
}
|
|
6219
|
+
if (/^[A-Z]/.test(receiver)) {
|
|
6220
|
+
const simple = receiver.includes(".") ? receiver.substring(receiver.lastIndexOf(".") + 1) : receiver;
|
|
6221
|
+
if (/^[A-Z][A-Za-z0-9_]*$/.test(simple)) {
|
|
6222
|
+
return resolveFqn(simple, context);
|
|
6223
|
+
}
|
|
6224
|
+
}
|
|
6225
|
+
return { simpleName: null, fqn: null };
|
|
6226
|
+
}
|
|
6227
|
+
function resolveFqn(simpleName, context) {
|
|
6228
|
+
if (!simpleName) return { simpleName: null, fqn: null };
|
|
6229
|
+
const importedFqn = context.imports.get(simpleName);
|
|
6230
|
+
if (importedFqn) {
|
|
6231
|
+
return { simpleName, fqn: importedFqn };
|
|
6232
|
+
}
|
|
6233
|
+
if (context.className === simpleName && context.packageName) {
|
|
6234
|
+
return { simpleName, fqn: `${context.packageName}.${simpleName}` };
|
|
6235
|
+
}
|
|
6236
|
+
if (JAVA_LANG_TYPES.has(simpleName)) {
|
|
6237
|
+
return { simpleName, fqn: `java.lang.${simpleName}` };
|
|
6238
|
+
}
|
|
6239
|
+
return { simpleName, fqn: null };
|
|
6240
|
+
}
|
|
6241
|
+
var JAVA_LANG_TYPES = /* @__PURE__ */ new Set([
|
|
6242
|
+
"String",
|
|
6243
|
+
"StringBuilder",
|
|
6244
|
+
"StringBuffer",
|
|
6245
|
+
"Object",
|
|
6246
|
+
"Class",
|
|
6247
|
+
"Integer",
|
|
6248
|
+
"Long",
|
|
6249
|
+
"Double",
|
|
6250
|
+
"Float",
|
|
6251
|
+
"Boolean",
|
|
6252
|
+
"Character",
|
|
6253
|
+
"Byte",
|
|
6254
|
+
"Short",
|
|
6255
|
+
"Number",
|
|
6256
|
+
"Math",
|
|
6257
|
+
"System",
|
|
6258
|
+
"Thread",
|
|
6259
|
+
"Runnable",
|
|
6260
|
+
"Throwable",
|
|
6261
|
+
"Exception",
|
|
6262
|
+
"RuntimeException",
|
|
6263
|
+
"Error",
|
|
6264
|
+
"Process",
|
|
6265
|
+
"ProcessBuilder",
|
|
6266
|
+
"Iterable",
|
|
6267
|
+
"Comparable",
|
|
6268
|
+
"CharSequence",
|
|
6269
|
+
"Enum"
|
|
6270
|
+
]);
|
|
6157
6271
|
function extractCallInfo(node, context) {
|
|
6158
6272
|
const nameNode = node.childForFieldName("name");
|
|
6159
6273
|
const methodName = nameNode ? getNodeText(nameNode) : "unknown";
|
|
@@ -6163,9 +6277,12 @@ function extractCallInfo(node, context) {
|
|
|
6163
6277
|
const args2 = argsNode ? extractArguments(argsNode) : [];
|
|
6164
6278
|
const enclosingMethod = findEnclosingMethod(node);
|
|
6165
6279
|
const { resolved, resolution } = resolveMethodCall(methodName, receiver, context);
|
|
6280
|
+
const { simpleName, fqn } = resolveReceiverType(receiver, context);
|
|
6166
6281
|
return {
|
|
6167
6282
|
method_name: methodName,
|
|
6168
6283
|
receiver,
|
|
6284
|
+
receiver_type: simpleName,
|
|
6285
|
+
receiver_type_fqn: fqn,
|
|
6169
6286
|
arguments: args2,
|
|
6170
6287
|
location: {
|
|
6171
6288
|
line: node.startPosition.row + 1,
|
|
@@ -6187,10 +6304,14 @@ function extractObjectCreation(node, context) {
|
|
|
6187
6304
|
status: "resolved",
|
|
6188
6305
|
target: `${typeName}.<init>`
|
|
6189
6306
|
};
|
|
6307
|
+
const simpleType = stripGenerics(typeName);
|
|
6308
|
+
const { simpleName, fqn } = resolveFqn(simpleType, context);
|
|
6190
6309
|
return {
|
|
6191
6310
|
method_name: typeName,
|
|
6192
6311
|
// Constructor name is the class name
|
|
6193
6312
|
receiver: null,
|
|
6313
|
+
receiver_type: simpleName,
|
|
6314
|
+
receiver_type_fqn: fqn,
|
|
6194
6315
|
arguments: args2,
|
|
6195
6316
|
location: {
|
|
6196
6317
|
line: node.startPosition.row + 1,
|
|
@@ -10123,6 +10244,23 @@ var DEFAULT_SINKS = [
|
|
|
10123
10244
|
{ method: "queryForObject", type: "sql_injection", cwe: "CWE-89", severity: "high", arg_positions: [0] },
|
|
10124
10245
|
{ method: "queryForList", type: "sql_injection", cwe: "CWE-89", severity: "high", arg_positions: [0] },
|
|
10125
10246
|
{ method: "queryForLong", type: "sql_injection", cwe: "CWE-89", severity: "high", arg_positions: [0] },
|
|
10247
|
+
// MyBatis mapper-interface methods (CWE-89, classified as mybatis_mapper_call)
|
|
10248
|
+
// The actual SQL lives in the mapper's XML or @Select/@Update annotation —
|
|
10249
|
+
// exploitability depends on whether the binding uses ${...} interpolation
|
|
10250
|
+
// vs #{...} parameter binding. Surface as a distinct sink type so consumers
|
|
10251
|
+
// can resolve the binding before reporting. See cognium-dev#24.
|
|
10252
|
+
// The `class: '*Mapper'` suffix wildcard matches userMapper, OrderMapper, …
|
|
10253
|
+
{ method: "insert", class: "*Mapper", type: "mybatis_mapper_call", cwe: "CWE-89", severity: "medium", arg_positions: [0], languages: ["java"] },
|
|
10254
|
+
{ method: "insertSelective", class: "*Mapper", type: "mybatis_mapper_call", cwe: "CWE-89", severity: "medium", arg_positions: [0], languages: ["java"] },
|
|
10255
|
+
{ method: "update", class: "*Mapper", type: "mybatis_mapper_call", cwe: "CWE-89", severity: "medium", arg_positions: [0], languages: ["java"] },
|
|
10256
|
+
{ method: "updateByPrimaryKey", class: "*Mapper", type: "mybatis_mapper_call", cwe: "CWE-89", severity: "medium", arg_positions: [0], languages: ["java"] },
|
|
10257
|
+
{ method: "updateByPrimaryKeySelective", class: "*Mapper", type: "mybatis_mapper_call", cwe: "CWE-89", severity: "medium", arg_positions: [0], languages: ["java"] },
|
|
10258
|
+
{ method: "delete", class: "*Mapper", type: "mybatis_mapper_call", cwe: "CWE-89", severity: "medium", arg_positions: [0], languages: ["java"] },
|
|
10259
|
+
{ method: "deleteByPrimaryKey", class: "*Mapper", type: "mybatis_mapper_call", cwe: "CWE-89", severity: "medium", arg_positions: [0], languages: ["java"] },
|
|
10260
|
+
{ method: "selectOne", class: "*Mapper", type: "mybatis_mapper_call", cwe: "CWE-89", severity: "medium", arg_positions: [0], languages: ["java"] },
|
|
10261
|
+
{ method: "selectList", class: "*Mapper", type: "mybatis_mapper_call", cwe: "CWE-89", severity: "medium", arg_positions: [0], languages: ["java"] },
|
|
10262
|
+
{ method: "selectByPrimaryKey", class: "*Mapper", type: "mybatis_mapper_call", cwe: "CWE-89", severity: "medium", arg_positions: [0], languages: ["java"] },
|
|
10263
|
+
{ method: "selectByExample", class: "*Mapper", type: "mybatis_mapper_call", cwe: "CWE-89", severity: "medium", arg_positions: [0], languages: ["java"] },
|
|
10126
10264
|
// Command Injection (CWE-78)
|
|
10127
10265
|
{ method: "exec", class: "Runtime", type: "command_injection", cwe: "CWE-78", severity: "critical", arg_positions: [0, 1] },
|
|
10128
10266
|
{ method: "start", class: "ProcessBuilder", type: "command_injection", cwe: "CWE-78", severity: "critical", arg_positions: [] },
|
|
@@ -12017,6 +12155,14 @@ function matchesAnnotation(annotations, targetAnnotation) {
|
|
|
12017
12155
|
return false;
|
|
12018
12156
|
}
|
|
12019
12157
|
function receiverMightBeClass(receiver, className) {
|
|
12158
|
+
if (className.startsWith("*") && className.length > 1) {
|
|
12159
|
+
const suffix = className.slice(1).toLowerCase();
|
|
12160
|
+
let simpleReceiver = receiver;
|
|
12161
|
+
if (simpleReceiver.includes(".") && !simpleReceiver.endsWith(")")) {
|
|
12162
|
+
simpleReceiver = simpleReceiver.substring(simpleReceiver.lastIndexOf(".") + 1);
|
|
12163
|
+
}
|
|
12164
|
+
return simpleReceiver.toLowerCase().endsWith(suffix);
|
|
12165
|
+
}
|
|
12020
12166
|
if (receiver === className) {
|
|
12021
12167
|
return true;
|
|
12022
12168
|
}
|
|
@@ -12790,7 +12936,8 @@ var KNOWN_SINK_TYPES = /* @__PURE__ */ new Set([
|
|
|
12790
12936
|
"log_injection",
|
|
12791
12937
|
"xxe",
|
|
12792
12938
|
"deserialization",
|
|
12793
|
-
"code_injection"
|
|
12939
|
+
"code_injection",
|
|
12940
|
+
"mybatis_mapper_call"
|
|
12794
12941
|
]);
|
|
12795
12942
|
function checkSanitized(_fromLine, toLine, sinkType, sanitizersByLine) {
|
|
12796
12943
|
const sanitizersAtTarget = sanitizersByLine.get(toLine);
|
|
@@ -6030,11 +6030,22 @@ function inferJSTypeFromReceiver(receiver) {
|
|
|
6030
6030
|
function buildResolutionContext(tree, cache) {
|
|
6031
6031
|
const context = {
|
|
6032
6032
|
className: null,
|
|
6033
|
+
packageName: null,
|
|
6033
6034
|
methodNames: /* @__PURE__ */ new Set(),
|
|
6034
6035
|
fieldTypes: /* @__PURE__ */ new Map(),
|
|
6035
6036
|
localVarTypes: /* @__PURE__ */ new Map(),
|
|
6036
|
-
|
|
6037
|
+
paramTypes: /* @__PURE__ */ new Map(),
|
|
6038
|
+
imports: /* @__PURE__ */ new Map(),
|
|
6039
|
+
wildcardImports: []
|
|
6037
6040
|
};
|
|
6041
|
+
const packages = getNodesFromCache(tree.rootNode, "package_declaration", cache);
|
|
6042
|
+
if (packages.length > 0) {
|
|
6043
|
+
const text = getNodeText(packages[0]);
|
|
6044
|
+
const match = text.match(/package\s+([a-zA-Z0-9_.]+)/);
|
|
6045
|
+
if (match) {
|
|
6046
|
+
context.packageName = match[1];
|
|
6047
|
+
}
|
|
6048
|
+
}
|
|
6038
6049
|
const classes = getNodesFromCache(tree.rootNode, "class_declaration", cache);
|
|
6039
6050
|
if (classes.length > 0) {
|
|
6040
6051
|
const nameNode = classes[0].childForFieldName("name");
|
|
@@ -6048,6 +6059,17 @@ function buildResolutionContext(tree, cache) {
|
|
|
6048
6059
|
if (nameNode) {
|
|
6049
6060
|
context.methodNames.add(getNodeText(nameNode));
|
|
6050
6061
|
}
|
|
6062
|
+
const paramsNode = method.childForFieldName("parameters");
|
|
6063
|
+
if (paramsNode) {
|
|
6064
|
+
collectParameterTypes(paramsNode, context.paramTypes);
|
|
6065
|
+
}
|
|
6066
|
+
}
|
|
6067
|
+
const constructors = getNodesFromCache(tree.rootNode, "constructor_declaration", cache);
|
|
6068
|
+
for (const ctor of constructors) {
|
|
6069
|
+
const paramsNode = ctor.childForFieldName("parameters");
|
|
6070
|
+
if (paramsNode) {
|
|
6071
|
+
collectParameterTypes(paramsNode, context.paramTypes);
|
|
6072
|
+
}
|
|
6051
6073
|
}
|
|
6052
6074
|
const fields = getNodesFromCache(tree.rootNode, "field_declaration", cache);
|
|
6053
6075
|
for (const field of fields) {
|
|
@@ -6080,14 +6102,106 @@ function buildResolutionContext(tree, cache) {
|
|
|
6080
6102
|
const imports = getNodesFromCache(tree.rootNode, "import_declaration", cache);
|
|
6081
6103
|
for (const imp of imports) {
|
|
6082
6104
|
const text = getNodeText(imp);
|
|
6083
|
-
const match = text.match(/import\s+(?:static\s+)?([a-zA-Z0-9_.]+)
|
|
6084
|
-
if (match)
|
|
6085
|
-
|
|
6086
|
-
|
|
6105
|
+
const match = text.match(/import\s+(?:static\s+)?([a-zA-Z0-9_.]+)(\.\*)?/);
|
|
6106
|
+
if (!match) continue;
|
|
6107
|
+
const fqn = match[1];
|
|
6108
|
+
const isWildcard = match[2] === ".*";
|
|
6109
|
+
if (isWildcard) {
|
|
6110
|
+
context.wildcardImports.push(fqn);
|
|
6111
|
+
} else {
|
|
6112
|
+
const parts2 = fqn.split(".");
|
|
6113
|
+
const simple = parts2[parts2.length - 1];
|
|
6114
|
+
context.imports.set(simple, fqn);
|
|
6087
6115
|
}
|
|
6088
6116
|
}
|
|
6089
6117
|
return context;
|
|
6090
6118
|
}
|
|
6119
|
+
function collectParameterTypes(paramsNode, out2) {
|
|
6120
|
+
for (let i2 = 0; i2 < paramsNode.childCount; i2++) {
|
|
6121
|
+
const child = paramsNode.child(i2);
|
|
6122
|
+
if (!child) continue;
|
|
6123
|
+
if (child.type !== "formal_parameter" && child.type !== "spread_parameter") {
|
|
6124
|
+
continue;
|
|
6125
|
+
}
|
|
6126
|
+
const typeNode = child.childForFieldName("type");
|
|
6127
|
+
const nameNode = child.childForFieldName("name");
|
|
6128
|
+
if (typeNode && nameNode) {
|
|
6129
|
+
out2.set(getNodeText(nameNode), getNodeText(typeNode));
|
|
6130
|
+
}
|
|
6131
|
+
}
|
|
6132
|
+
}
|
|
6133
|
+
function stripGenerics(type) {
|
|
6134
|
+
const ltIdx = type.indexOf("<");
|
|
6135
|
+
return ltIdx === -1 ? type : type.substring(0, ltIdx);
|
|
6136
|
+
}
|
|
6137
|
+
function resolveReceiverType(receiver, context) {
|
|
6138
|
+
if (!receiver) return { simpleName: null, fqn: null };
|
|
6139
|
+
if (receiver === "this") {
|
|
6140
|
+
return resolveFqn(context.className, context);
|
|
6141
|
+
}
|
|
6142
|
+
if (receiver.startsWith("this.")) {
|
|
6143
|
+
const fieldName = receiver.substring("this.".length);
|
|
6144
|
+
const fieldType = context.fieldTypes.get(fieldName);
|
|
6145
|
+
if (fieldType) return resolveFqn(stripGenerics(fieldType), context);
|
|
6146
|
+
return { simpleName: null, fqn: null };
|
|
6147
|
+
}
|
|
6148
|
+
if (receiver === "super") return { simpleName: null, fqn: null };
|
|
6149
|
+
const declaredType = context.localVarTypes.get(receiver) ?? context.paramTypes.get(receiver) ?? context.fieldTypes.get(receiver);
|
|
6150
|
+
if (declaredType) {
|
|
6151
|
+
return resolveFqn(stripGenerics(declaredType), context);
|
|
6152
|
+
}
|
|
6153
|
+
if (/^[A-Z]/.test(receiver)) {
|
|
6154
|
+
const simple = receiver.includes(".") ? receiver.substring(receiver.lastIndexOf(".") + 1) : receiver;
|
|
6155
|
+
if (/^[A-Z][A-Za-z0-9_]*$/.test(simple)) {
|
|
6156
|
+
return resolveFqn(simple, context);
|
|
6157
|
+
}
|
|
6158
|
+
}
|
|
6159
|
+
return { simpleName: null, fqn: null };
|
|
6160
|
+
}
|
|
6161
|
+
function resolveFqn(simpleName, context) {
|
|
6162
|
+
if (!simpleName) return { simpleName: null, fqn: null };
|
|
6163
|
+
const importedFqn = context.imports.get(simpleName);
|
|
6164
|
+
if (importedFqn) {
|
|
6165
|
+
return { simpleName, fqn: importedFqn };
|
|
6166
|
+
}
|
|
6167
|
+
if (context.className === simpleName && context.packageName) {
|
|
6168
|
+
return { simpleName, fqn: `${context.packageName}.${simpleName}` };
|
|
6169
|
+
}
|
|
6170
|
+
if (JAVA_LANG_TYPES.has(simpleName)) {
|
|
6171
|
+
return { simpleName, fqn: `java.lang.${simpleName}` };
|
|
6172
|
+
}
|
|
6173
|
+
return { simpleName, fqn: null };
|
|
6174
|
+
}
|
|
6175
|
+
var JAVA_LANG_TYPES = /* @__PURE__ */ new Set([
|
|
6176
|
+
"String",
|
|
6177
|
+
"StringBuilder",
|
|
6178
|
+
"StringBuffer",
|
|
6179
|
+
"Object",
|
|
6180
|
+
"Class",
|
|
6181
|
+
"Integer",
|
|
6182
|
+
"Long",
|
|
6183
|
+
"Double",
|
|
6184
|
+
"Float",
|
|
6185
|
+
"Boolean",
|
|
6186
|
+
"Character",
|
|
6187
|
+
"Byte",
|
|
6188
|
+
"Short",
|
|
6189
|
+
"Number",
|
|
6190
|
+
"Math",
|
|
6191
|
+
"System",
|
|
6192
|
+
"Thread",
|
|
6193
|
+
"Runnable",
|
|
6194
|
+
"Throwable",
|
|
6195
|
+
"Exception",
|
|
6196
|
+
"RuntimeException",
|
|
6197
|
+
"Error",
|
|
6198
|
+
"Process",
|
|
6199
|
+
"ProcessBuilder",
|
|
6200
|
+
"Iterable",
|
|
6201
|
+
"Comparable",
|
|
6202
|
+
"CharSequence",
|
|
6203
|
+
"Enum"
|
|
6204
|
+
]);
|
|
6091
6205
|
function extractCallInfo(node, context) {
|
|
6092
6206
|
const nameNode = node.childForFieldName("name");
|
|
6093
6207
|
const methodName = nameNode ? getNodeText(nameNode) : "unknown";
|
|
@@ -6097,9 +6211,12 @@ function extractCallInfo(node, context) {
|
|
|
6097
6211
|
const args2 = argsNode ? extractArguments(argsNode) : [];
|
|
6098
6212
|
const enclosingMethod = findEnclosingMethod(node);
|
|
6099
6213
|
const { resolved, resolution } = resolveMethodCall(methodName, receiver, context);
|
|
6214
|
+
const { simpleName, fqn } = resolveReceiverType(receiver, context);
|
|
6100
6215
|
return {
|
|
6101
6216
|
method_name: methodName,
|
|
6102
6217
|
receiver,
|
|
6218
|
+
receiver_type: simpleName,
|
|
6219
|
+
receiver_type_fqn: fqn,
|
|
6103
6220
|
arguments: args2,
|
|
6104
6221
|
location: {
|
|
6105
6222
|
line: node.startPosition.row + 1,
|
|
@@ -6121,10 +6238,14 @@ function extractObjectCreation(node, context) {
|
|
|
6121
6238
|
status: "resolved",
|
|
6122
6239
|
target: `${typeName}.<init>`
|
|
6123
6240
|
};
|
|
6241
|
+
const simpleType = stripGenerics(typeName);
|
|
6242
|
+
const { simpleName, fqn } = resolveFqn(simpleType, context);
|
|
6124
6243
|
return {
|
|
6125
6244
|
method_name: typeName,
|
|
6126
6245
|
// Constructor name is the class name
|
|
6127
6246
|
receiver: null,
|
|
6247
|
+
receiver_type: simpleName,
|
|
6248
|
+
receiver_type_fqn: fqn,
|
|
6128
6249
|
arguments: args2,
|
|
6129
6250
|
location: {
|
|
6130
6251
|
line: node.startPosition.row + 1,
|
|
@@ -10057,6 +10178,23 @@ var DEFAULT_SINKS = [
|
|
|
10057
10178
|
{ method: "queryForObject", type: "sql_injection", cwe: "CWE-89", severity: "high", arg_positions: [0] },
|
|
10058
10179
|
{ method: "queryForList", type: "sql_injection", cwe: "CWE-89", severity: "high", arg_positions: [0] },
|
|
10059
10180
|
{ method: "queryForLong", type: "sql_injection", cwe: "CWE-89", severity: "high", arg_positions: [0] },
|
|
10181
|
+
// MyBatis mapper-interface methods (CWE-89, classified as mybatis_mapper_call)
|
|
10182
|
+
// The actual SQL lives in the mapper's XML or @Select/@Update annotation —
|
|
10183
|
+
// exploitability depends on whether the binding uses ${...} interpolation
|
|
10184
|
+
// vs #{...} parameter binding. Surface as a distinct sink type so consumers
|
|
10185
|
+
// can resolve the binding before reporting. See cognium-dev#24.
|
|
10186
|
+
// The `class: '*Mapper'` suffix wildcard matches userMapper, OrderMapper, …
|
|
10187
|
+
{ method: "insert", class: "*Mapper", type: "mybatis_mapper_call", cwe: "CWE-89", severity: "medium", arg_positions: [0], languages: ["java"] },
|
|
10188
|
+
{ method: "insertSelective", class: "*Mapper", type: "mybatis_mapper_call", cwe: "CWE-89", severity: "medium", arg_positions: [0], languages: ["java"] },
|
|
10189
|
+
{ method: "update", class: "*Mapper", type: "mybatis_mapper_call", cwe: "CWE-89", severity: "medium", arg_positions: [0], languages: ["java"] },
|
|
10190
|
+
{ method: "updateByPrimaryKey", class: "*Mapper", type: "mybatis_mapper_call", cwe: "CWE-89", severity: "medium", arg_positions: [0], languages: ["java"] },
|
|
10191
|
+
{ method: "updateByPrimaryKeySelective", class: "*Mapper", type: "mybatis_mapper_call", cwe: "CWE-89", severity: "medium", arg_positions: [0], languages: ["java"] },
|
|
10192
|
+
{ method: "delete", class: "*Mapper", type: "mybatis_mapper_call", cwe: "CWE-89", severity: "medium", arg_positions: [0], languages: ["java"] },
|
|
10193
|
+
{ method: "deleteByPrimaryKey", class: "*Mapper", type: "mybatis_mapper_call", cwe: "CWE-89", severity: "medium", arg_positions: [0], languages: ["java"] },
|
|
10194
|
+
{ method: "selectOne", class: "*Mapper", type: "mybatis_mapper_call", cwe: "CWE-89", severity: "medium", arg_positions: [0], languages: ["java"] },
|
|
10195
|
+
{ method: "selectList", class: "*Mapper", type: "mybatis_mapper_call", cwe: "CWE-89", severity: "medium", arg_positions: [0], languages: ["java"] },
|
|
10196
|
+
{ method: "selectByPrimaryKey", class: "*Mapper", type: "mybatis_mapper_call", cwe: "CWE-89", severity: "medium", arg_positions: [0], languages: ["java"] },
|
|
10197
|
+
{ method: "selectByExample", class: "*Mapper", type: "mybatis_mapper_call", cwe: "CWE-89", severity: "medium", arg_positions: [0], languages: ["java"] },
|
|
10060
10198
|
// Command Injection (CWE-78)
|
|
10061
10199
|
{ method: "exec", class: "Runtime", type: "command_injection", cwe: "CWE-78", severity: "critical", arg_positions: [0, 1] },
|
|
10062
10200
|
{ method: "start", class: "ProcessBuilder", type: "command_injection", cwe: "CWE-78", severity: "critical", arg_positions: [] },
|
|
@@ -11951,6 +12089,14 @@ function matchesAnnotation(annotations, targetAnnotation) {
|
|
|
11951
12089
|
return false;
|
|
11952
12090
|
}
|
|
11953
12091
|
function receiverMightBeClass(receiver, className) {
|
|
12092
|
+
if (className.startsWith("*") && className.length > 1) {
|
|
12093
|
+
const suffix = className.slice(1).toLowerCase();
|
|
12094
|
+
let simpleReceiver = receiver;
|
|
12095
|
+
if (simpleReceiver.includes(".") && !simpleReceiver.endsWith(")")) {
|
|
12096
|
+
simpleReceiver = simpleReceiver.substring(simpleReceiver.lastIndexOf(".") + 1);
|
|
12097
|
+
}
|
|
12098
|
+
return simpleReceiver.toLowerCase().endsWith(suffix);
|
|
12099
|
+
}
|
|
11954
12100
|
if (receiver === className) {
|
|
11955
12101
|
return true;
|
|
11956
12102
|
}
|
|
@@ -12724,7 +12870,8 @@ var KNOWN_SINK_TYPES = /* @__PURE__ */ new Set([
|
|
|
12724
12870
|
"log_injection",
|
|
12725
12871
|
"xxe",
|
|
12726
12872
|
"deserialization",
|
|
12727
|
-
"code_injection"
|
|
12873
|
+
"code_injection",
|
|
12874
|
+
"mybatis_mapper_call"
|
|
12728
12875
|
]);
|
|
12729
12876
|
function checkSanitized(_fromLine, toLine, sinkType, sanitizersByLine) {
|
|
12730
12877
|
const sanitizersAtTarget = sanitizersByLine.get(toLine);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"calls.d.ts","sourceRoot":"","sources":["../../../src/core/extractors/calls.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,KAAK,EAAQ,IAAI,EAAE,MAAM,iBAAiB,CAAC;AAClD,OAAO,KAAK,EAAE,QAAQ,EAAgC,MAAM,sBAAsB,CAAC;AACnF,OAAO,EAA2D,KAAK,SAAS,EAAE,MAAM,cAAc,CAAC;
|
|
1
|
+
{"version":3,"file":"calls.d.ts","sourceRoot":"","sources":["../../../src/core/extractors/calls.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,KAAK,EAAQ,IAAI,EAAE,MAAM,iBAAiB,CAAC;AAClD,OAAO,KAAK,EAAE,QAAQ,EAAgC,MAAM,sBAAsB,CAAC;AACnF,OAAO,EAA2D,KAAK,SAAS,EAAE,MAAM,cAAc,CAAC;AA8EvG;;;;;GAKG;AACH,wBAAgB,YAAY,CAAC,IAAI,EAAE,IAAI,EAAE,KAAK,CAAC,EAAE,SAAS,EAAE,QAAQ,CAAC,EAAE,MAAM,GAAG,QAAQ,EAAE,CAgDzF"}
|