11import java
22import semmle.code.xml.MyBatisMapperXML
33import 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`. */
636private 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+
2863predicate 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