Skip to content

Commit 04a3f76

Browse files
committed
Eliminate false positives of Mybatis Configuration Variable
1 parent d36a7ed commit 04a3f76

File tree

2 files changed

+65
-9
lines changed

2 files changed

+65
-9
lines changed

java/ql/lib/semmle/code/java/frameworks/MyBatis.qll

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,3 +24,19 @@ private class SqlSinkCsv extends SinkModelCsv {
2424
]
2525
}
2626
}
27+
28+
/** The class `org.apache.ibatis.session.Configuration`. */
29+
class IbatisConfiguration extends RefType {
30+
IbatisConfiguration() { this.hasQualifiedName("org.apache.ibatis.session", "Configuration") }
31+
}
32+
33+
/**
34+
* The method `getVariables()` declared in `org.apache.ibatis.session.Configuration`.
35+
*/
36+
class IbatisConfigurationGetVariablesMethod extends Method {
37+
IbatisConfigurationGetVariablesMethod() {
38+
getDeclaringType() instanceof IbatisConfiguration and
39+
hasName("getVariables") and
40+
getNumberOfParameters() = 0
41+
}
42+
}

java/ql/src/experimental/Security/CWE/CWE-089/MyBatisMapperXmlSqlInjectionLib.qll

Lines changed: 49 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,36 @@
11
import java
22
import semmle.code.xml.MyBatisMapperXML
33
import semmle.code.java.dataflow.FlowSources
4+
import semmle.code.java.frameworks.MyBatis
5+
import semmle.code.java.frameworks.Properties
6+
7+
private predicate propertiesKey(DataFlow::Node prop, string key) {
8+
exists(MethodAccess m |
9+
m.getMethod() instanceof PropertiesSetPropertyMethod and
10+
key = m.getArgument(0).(CompileTimeConstantExpr).getStringValue() and
11+
prop.asExpr() = m.getQualifier()
12+
)
13+
}
14+
15+
private class PropertiesFlowConfig extends DataFlow2::Configuration {
16+
PropertiesFlowConfig() { this = "PropertiesFlowConfig" }
17+
18+
override predicate isSource(DataFlow::Node src) {
19+
exists(MethodAccess ma | ma.getMethod() instanceof IbatisConfigurationGetVariablesMethod |
20+
src.asExpr() = ma
21+
)
22+
}
23+
24+
override predicate isSink(DataFlow::Node sink) { propertiesKey(sink, _) }
25+
}
26+
27+
/** Get the key value of Mybatis Configuration Variable. */
28+
private string getAnMybatisConfigurationVariableKey() {
29+
exists(PropertiesFlowConfig conf, DataFlow::Node n |
30+
propertiesKey(n, result) and
31+
conf.hasFlow(_, n)
32+
)
33+
}
434

535
/** The interface `org.apache.ibatis.annotations.Param`. */
636
private class TypeParam extends Interface {
@@ -25,6 +55,11 @@ class MyBatisMapperMethodCallAnArgument extends DataFlow::Node {
2555
}
2656
}
2757

58+
/** Get the #{...} or ${...} parameters in the Mybatis mapper xml file */
59+
private string getAnMybatiXmlSetValue(XMLElement xmle) {
60+
result = xmle.getTextValue().trim().regexpFind("(#|\\$)(\\{([^\\}]*\\}))", _, _)
61+
}
62+
2863
predicate isSqlInjection(DataFlow::Node node, XMLElement xmle) {
2964
// MyBatis Mapper method parameter name sql injection vulnerabilities.
3065
// e.g. MyBatis Mapper method: `void test(String name);` and MyBatis Mapper XML file:`select id,name from test where name like '%${name}%'`
@@ -38,7 +73,7 @@ predicate isSqlInjection(DataFlow::Node node, XMLElement xmle) {
3873
mbms.getAChild*() = xmle
3974
) and
4075
not mc.getMethod().getParameter(i).hasAnnotation() and
41-
xmle.getTextValue().trim().matches("%${" + mc.getMethod().getParameter(i).getName() + "%") and
76+
getAnMybatiXmlSetValue(xmle).matches("${" + mc.getMethod().getParameter(i).getName() + "%}") and
4277
mc.getArgument(i) = node.asExpr()
4378
)
4479
or
@@ -59,10 +94,9 @@ predicate isSqlInjection(DataFlow::Node node, XMLElement xmle) {
5994
mc.getMethod().getParameter(i).hasAnnotation() and
6095
mc.getMethod().getParameter(i).getAnAnnotation() = annotation and
6196
annotation.getType() instanceof TypeParam and
62-
xmle.getTextValue()
63-
.trim()
97+
getAnMybatiXmlSetValue(xmle)
6498
.matches("%${" + annotation.getValue("value").(CompileTimeConstantExpr).getStringValue() +
65-
"%") and
99+
"%}") and
66100
mc.getArgument(i) = node.asExpr()
67101
)
68102
or
@@ -79,13 +113,15 @@ predicate isSqlInjection(DataFlow::Node node, XMLElement xmle) {
79113
) and
80114
not mc.getMethod().getParameter(i).hasAnnotation() and
81115
mc.getMethod().getParameterType(i).getName() = c.getName() and
82-
xmle.getTextValue().trim().matches("%${" + c.getAField().getName() + "%") and
116+
getAnMybatiXmlSetValue(xmle).matches("%${" + c.getAField().getName() + "%}") and
83117
mc.getArgument(i) = node.asExpr()
84118
)
85119
or
86120
// The parameter type of MyBatis Mapper method is Map or List or Array, which may cause SQL injection vulnerability.
87121
// e.g. MyBatis Mapper method: `void test(Map<String, String> params);` and MyBatis Mapper XML file:`select id,name from test where name like '%${name}%'`
88-
exists(MyBatisMapperSqlOperation mbmxe, MyBatisMapperSql mbms, MethodAccess mc, int i |
122+
exists(
123+
MyBatisMapperSqlOperation mbmxe, MyBatisMapperSql mbms, MethodAccess mc, int i, string res
124+
|
89125
mbmxe.getMapperMethod() = mc.getMethod()
90126
|
91127
(
@@ -100,13 +136,15 @@ predicate isSqlInjection(DataFlow::Node node, XMLElement xmle) {
100136
mc.getMethod().getParameterType(i) instanceof ListType or
101137
mc.getMethod().getParameterType(i) instanceof Array
102138
) and
103-
xmle.getTextValue().trim().matches("%${%") and
139+
res = getAnMybatiXmlSetValue(xmle) and
140+
res.matches("%${%}") and
141+
not res.matches("${" + getAnMybatisConfigurationVariableKey() + "}") and
104142
mc.getArgument(i) = node.asExpr()
105143
)
106144
or
107145
// MyBatis Mapper method string type sql injection vulnerabilities.
108146
// e.g. MyBatis Mapper method: `void test(String name);` and MyBatis Mapper XML file:`select id,name from test where name like '%${value}%'`
109-
exists(MyBatisMapperSqlOperation mbmxe, MyBatisMapperSql mbms, MethodAccess mc |
147+
exists(MyBatisMapperSqlOperation mbmxe, MyBatisMapperSql mbms, MethodAccess mc, string res |
110148
mbmxe.getMapperMethod() = mc.getMethod()
111149
|
112150
(
@@ -115,10 +153,12 @@ predicate isSqlInjection(DataFlow::Node node, XMLElement xmle) {
115153
mbmxe.getInclude().getRefid() = mbms.getId() and
116154
mbms.getAChild*() = xmle
117155
) and
156+
res = getAnMybatiXmlSetValue(xmle) and
118157
mc.getMethod().getAParamType() instanceof TypeString and
119158
mc.getMethod().getNumberOfParameters() = 1 and
120159
not mc.getMethod().getAParameter().hasAnnotation() and
121-
xmle.getTextValue().trim().matches("%${%") and
160+
res.matches("%${%}") and
161+
not res.matches("${" + getAnMybatisConfigurationVariableKey() + "}") and
122162
mc.getAnArgument() = node.asExpr()
123163
)
124164
}

0 commit comments

Comments
 (0)