mirror of
https://github.com/github/codeql.git
synced 2026-04-30 19:26:02 +02:00
Refactor isMybatisXmlOrAnnotationSqlInjection
This commit is contained in:
@@ -44,10 +44,12 @@ private class MyBatisAnnotationSqlInjectionConfiguration extends TaintTracking::
|
||||
|
||||
from
|
||||
MyBatisAnnotationSqlInjectionConfiguration cfg, DataFlow::PathNode source,
|
||||
DataFlow::PathNode sink, IbatisSqlOperationAnnotation isoa
|
||||
DataFlow::PathNode sink, IbatisSqlOperationAnnotation isoa, MethodAccess ma
|
||||
where
|
||||
cfg.hasFlowPath(source, sink) and
|
||||
isMybatisXmlOrAnnotationSqlInjection(sink.getNode(), _, isoa)
|
||||
ma.getAnArgument() = sink.getNode().asExpr() and
|
||||
myBatisSqlOperationAnnotationFromMethod(ma.getMethod(), isoa) and
|
||||
isMybatisXmlOrAnnotationSqlInjection(sink.getNode(), ma, getAMybatisAnnotationSqlValue(isoa), _)
|
||||
select sink.getNode(), source, sink,
|
||||
"MyBatis annotation SQL injection might include code from $@ to $@.", source.getNode(),
|
||||
"this user input", isoa, "this SQL operation"
|
||||
|
||||
@@ -30,7 +30,7 @@ private class PropertiesFlowConfig extends DataFlow2::Configuration {
|
||||
}
|
||||
|
||||
/** Get the key value of Mybatis Configuration Variable. */
|
||||
string getAnMybatisConfigurationVariableKey() {
|
||||
string getAMybatisConfigurationVariableKey() {
|
||||
exists(PropertiesFlowConfig conf, DataFlow::Node n |
|
||||
propertiesKey(n, result) and
|
||||
conf.hasFlowTo(n)
|
||||
@@ -44,134 +44,124 @@ class ListType extends RefType {
|
||||
}
|
||||
}
|
||||
|
||||
/** Gets a call to the MyBatis mapper xml method. */
|
||||
MethodAccess getMyBatisMapperXmlMethodAccess(XMLElement xmle) {
|
||||
exists(MyBatisMapperSqlOperation mbmxe |
|
||||
mbmxe.getMapperMethod() = result.getMethod() and
|
||||
(
|
||||
mbmxe.getAChild*() = xmle
|
||||
or
|
||||
exists(MyBatisMapperSql mbms |
|
||||
mbmxe.getInclude().getRefid() = mbms.getId() and
|
||||
mbms.getAChild*() = xmle
|
||||
)
|
||||
/** Holds if the specified method uses MyBatis Mapper XMLElement. */
|
||||
predicate myBatisMapperXMLElementFromMethod(Method method, MyBatisMapperXMLElement mmxx) {
|
||||
exists(MyBatisMapperSqlOperation mbmxe | mbmxe.getMapperMethod() = method |
|
||||
mbmxe.getAChild*() = mmxx
|
||||
or
|
||||
exists(MyBatisMapperSql mbms |
|
||||
mbmxe.getInclude().getRefid() = mbms.getId() and
|
||||
mbms.getAChild*() = mmxx
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
/** Gets a call to the MyBatis SQL operation annotation method. */
|
||||
MethodAccess getMyBatisSqlOperationAnnotationMethodAccess(IbatisSqlOperationAnnotation isoa) {
|
||||
/** Holds if the specified method uses Ibatis Sql Operation Annotation. */
|
||||
predicate myBatisSqlOperationAnnotationFromMethod(Method method, IbatisSqlOperationAnnotation isoa) {
|
||||
exists(MyBatisSqlOperationAnnotationMethod msoam |
|
||||
msoam = result.getMethod() and
|
||||
msoam = method and
|
||||
msoam.getAnAnnotation() = isoa
|
||||
)
|
||||
}
|
||||
|
||||
/** Get the #{...} or ${...} parameters in the Mybatis mapper xml file. */
|
||||
private string getAnMybatisXmlSetValue(XMLElement xmle) {
|
||||
result = xmle.getTextValue().trim().regexpFind("(#|\\$)\\{[^\\}]*\\}", _, _)
|
||||
string getAMybatisXmlSetValue(XMLElement xmle) {
|
||||
result = xmle.getTextValue().regexpFind("(#|\\$)\\{[^\\}]*\\}", _, _)
|
||||
}
|
||||
|
||||
/** Get the #{...} or ${...} parameters in the Mybatis sql operation annotation value. */
|
||||
private string getAMybatisAnnotationSqlValue(IbatisSqlOperationAnnotation isoa) {
|
||||
result = isoa.getSqlValue().trim().regexpFind("(#|\\$)\\{[^\\}]*\\}", _, _)
|
||||
string getAMybatisAnnotationSqlValue(IbatisSqlOperationAnnotation isoa) {
|
||||
result = isoa.getSqlValue().regexpFind("(#|\\$)\\{[^\\}]*\\}", _, _)
|
||||
}
|
||||
|
||||
/** Holds if it is SQL injection of MyBatis xml or MyBatis annotation. */
|
||||
bindingset[setValue]
|
||||
predicate isMybatisXmlOrAnnotationSqlInjection(
|
||||
DataFlow::Node node, XMLElement xmle, IbatisSqlOperationAnnotation isoa
|
||||
//DataFlow::Node node, MyBatisMapperXMLElement xmle, IbatisSqlOperationAnnotation isoa
|
||||
DataFlow::Node node, MethodAccess ma, string setValue, MyBatisMapperXMLElement mmxe
|
||||
) {
|
||||
exists(MethodAccess ma, string setValue |
|
||||
(
|
||||
ma = getMyBatisMapperXmlMethodAccess(xmle) and
|
||||
setValue = getAnMybatisXmlSetValue(xmle)
|
||||
or
|
||||
ma = getMyBatisSqlOperationAnnotationMethodAccess(isoa) and
|
||||
setValue = getAMybatisAnnotationSqlValue(isoa)
|
||||
) and
|
||||
not setValue.regexpMatch("\\$\\{" + getAnMybatisConfigurationVariableKey() + "\\}") and
|
||||
(
|
||||
// The method parameters use `@Param` annotation. Due to improper use of this parameter, SQL injection vulnerabilities are caused.
|
||||
// e.g.
|
||||
//
|
||||
// ```java
|
||||
// @Select(select id,name from test order by ${orderby,jdbcType=VARCHAR})
|
||||
// void test(@Param("orderby") String name);
|
||||
// ```
|
||||
exists(int i, Annotation annotation |
|
||||
setValue
|
||||
.matches("%${" + annotation.getValue("value").(CompileTimeConstantExpr).getStringValue()
|
||||
+ "%}") and
|
||||
annotation.getType() instanceof TypeParam and
|
||||
ma.getArgument(i) = node.asExpr()
|
||||
)
|
||||
or
|
||||
// MyBatis default parameter sql injection vulnerabilities.the default parameter form of the method is arg[0...n] or param[1...n].
|
||||
// e.g.
|
||||
//
|
||||
// ```java
|
||||
// @Select(select id,name from test order by ${arg0,jdbcType=VARCHAR})
|
||||
// void test(String name);
|
||||
// ```
|
||||
exists(int i |
|
||||
not ma.getMethod().getParameter(i).getAnAnnotation().getType() instanceof TypeParam and
|
||||
(
|
||||
setValue.matches("%${param" + (i + 1) + "%}")
|
||||
or
|
||||
setValue.matches("%${arg" + i + "%}")
|
||||
) and
|
||||
not setValue = "${" + getAnMybatisConfigurationVariableKey() + "}" and
|
||||
ma.getArgument(i) = node.asExpr()
|
||||
)
|
||||
or
|
||||
// SQL injection vulnerability caused by improper use of MyBatis instance class fields.
|
||||
// e.g.
|
||||
//
|
||||
// ```java
|
||||
// @Select(select id,name from test order by ${name,jdbcType=VARCHAR})
|
||||
// void test(Test test);
|
||||
// ```
|
||||
exists(int i, RefType t |
|
||||
not ma.getMethod().getParameter(i).getAnAnnotation().getType() instanceof TypeParam and
|
||||
ma.getMethod().getParameterType(i).getName() = t.getName() and
|
||||
setValue.matches("%${" + t.getAField().getName() + "%}") and
|
||||
ma.getArgument(i) = node.asExpr()
|
||||
)
|
||||
or
|
||||
// The parameter type of the MyBatis method parameter is Map or List or Array.
|
||||
// SQL injection vulnerability caused by improper use of this parameter.
|
||||
// e.g.
|
||||
//
|
||||
// ```java
|
||||
// @Select(select id,name from test where name like '%${value}%')
|
||||
// Test test(Map map);
|
||||
// ```
|
||||
exists(int i, MyBatisMapperForeach mbmf |
|
||||
mbmf = xmle and
|
||||
not ma.getMethod().getParameter(i).getAnAnnotation().getType() instanceof TypeParam and
|
||||
(
|
||||
ma.getMethod().getParameterType(i) instanceof MapType or
|
||||
ma.getMethod().getParameterType(i) instanceof ListType or
|
||||
ma.getMethod().getParameterType(i) instanceof Array
|
||||
) and
|
||||
setValue.matches("%${%}") and
|
||||
ma.getArgument(i) = node.asExpr()
|
||||
)
|
||||
or
|
||||
// This method has only one parameter and the parameter is not annotated with `@Param`.
|
||||
// Improper use of this parameter has a SQL injection vulnerability.
|
||||
// e.g.
|
||||
//
|
||||
// ```java
|
||||
// @Select(select id,name from test where name like '%${value}%')
|
||||
// Test test(String name);
|
||||
// ```
|
||||
exists(int i | i = 1 |
|
||||
ma.getMethod().getNumberOfParameters() = i and
|
||||
not ma.getMethod().getAParameter().getAnAnnotation().getType() instanceof TypeParam and
|
||||
setValue.matches("%${%}") and
|
||||
ma.getAnArgument() = node.asExpr()
|
||||
)
|
||||
not setValue.regexpMatch("\\$\\{" + getAMybatisConfigurationVariableKey() + "\\}") and
|
||||
(
|
||||
// The method parameters use `@Param` annotation. Due to improper use of this parameter, SQL injection vulnerabilities are caused.
|
||||
// e.g.
|
||||
//
|
||||
// ```java
|
||||
// @Select(select id,name from test order by ${orderby,jdbcType=VARCHAR})
|
||||
// void test(@Param("orderby") String name);
|
||||
// ```
|
||||
exists(int i, Annotation annotation |
|
||||
setValue
|
||||
.matches("${" + annotation.getValue("value").(CompileTimeConstantExpr).getStringValue() +
|
||||
"%}") and
|
||||
annotation.getType() instanceof TypeParam and
|
||||
ma.getArgument(i) = node.asExpr()
|
||||
)
|
||||
or
|
||||
// MyBatis default parameter sql injection vulnerabilities.the default parameter form of the method is arg[0...n] or param[1...n].
|
||||
// e.g.
|
||||
//
|
||||
// ```java
|
||||
// @Select(select id,name from test order by ${arg0,jdbcType=VARCHAR})
|
||||
// void test(String name);
|
||||
// ```
|
||||
exists(int i |
|
||||
not ma.getMethod().getParameter(i).getAnAnnotation().getType() instanceof TypeParam and
|
||||
(
|
||||
setValue.matches("${param" + (i + 1) + "%}")
|
||||
or
|
||||
setValue.matches("${arg" + i + "%}")
|
||||
) and
|
||||
ma.getArgument(i) = node.asExpr()
|
||||
)
|
||||
or
|
||||
// SQL injection vulnerability caused by improper use of MyBatis instance class fields.
|
||||
// e.g.
|
||||
//
|
||||
// ```java
|
||||
// @Select(select id,name from test order by ${name,jdbcType=VARCHAR})
|
||||
// void test(Test test);
|
||||
// ```
|
||||
exists(int i, RefType t |
|
||||
not ma.getMethod().getParameter(i).getAnAnnotation().getType() instanceof TypeParam and
|
||||
ma.getMethod().getParameterType(i).getName() = t.getName() and
|
||||
setValue.matches("${" + t.getAField().getName() + "%}") and
|
||||
ma.getArgument(i) = node.asExpr()
|
||||
)
|
||||
or
|
||||
// The parameter type of the MyBatis method parameter is Map or List or Array.
|
||||
// SQL injection vulnerability caused by improper use of this parameter.
|
||||
// e.g.
|
||||
//
|
||||
// ```java
|
||||
// @Select(select id,name from test where name like '%${value}%')
|
||||
// Test test(Map map);
|
||||
// ```
|
||||
exists(int i, MyBatisMapperForeach mbmf |
|
||||
mbmf = mmxe and
|
||||
not ma.getMethod().getParameter(i).getAnAnnotation().getType() instanceof TypeParam and
|
||||
(
|
||||
ma.getMethod().getParameterType(i) instanceof MapType or
|
||||
ma.getMethod().getParameterType(i) instanceof ListType or
|
||||
ma.getMethod().getParameterType(i) instanceof Array
|
||||
) and
|
||||
setValue.matches("${%}") and
|
||||
ma.getArgument(i) = node.asExpr()
|
||||
)
|
||||
or
|
||||
// This method has only one parameter and the parameter is not annotated with `@Param`. The parameter can be named arbitrarily in the SQL statement.
|
||||
// If the number of method variables is greater than one, they cannot be named arbitrarily.
|
||||
// Improper use of this parameter has a SQL injection vulnerability.
|
||||
// e.g.
|
||||
//
|
||||
// ```java
|
||||
// @Select(select id,name from test where name like '%${value}%')
|
||||
// Test test(String name);
|
||||
// ```
|
||||
exists(int i | i = 1 |
|
||||
ma.getMethod().getNumberOfParameters() = i and
|
||||
not ma.getMethod().getAParameter().getAnAnnotation().getType() instanceof TypeParam and
|
||||
setValue.matches("${%}") and
|
||||
ma.getAnArgument() = node.asExpr()
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -45,10 +45,12 @@ private class MyBatisMapperXmlSqlInjectionConfiguration extends TaintTracking::C
|
||||
|
||||
from
|
||||
MyBatisMapperXmlSqlInjectionConfiguration cfg, DataFlow::PathNode source, DataFlow::PathNode sink,
|
||||
XMLElement xmle
|
||||
MyBatisMapperXMLElement mmxe, MethodAccess ma
|
||||
where
|
||||
cfg.hasFlowPath(source, sink) and
|
||||
isMybatisXmlOrAnnotationSqlInjection(sink.getNode(), xmle, _)
|
||||
ma.getAnArgument() = sink.getNode().asExpr() and
|
||||
myBatisMapperXMLElementFromMethod(ma.getMethod(), mmxe) and
|
||||
isMybatisXmlOrAnnotationSqlInjection(sink.getNode(), ma, getAMybatisXmlSetValue(mmxe), mmxe)
|
||||
select sink.getNode(), source, sink,
|
||||
"MyBatis Mapper XML SQL injection might include code from $@ to $@.", source.getNode(),
|
||||
"this user input", xmle, "this SQL operation"
|
||||
"this user input", mmxe, "this SQL operation"
|
||||
|
||||
@@ -37,7 +37,10 @@ class MyBatisMapperXMLElement extends XMLElement {
|
||||
* An MyBatis Mapper sql operation element.
|
||||
*/
|
||||
abstract class MyBatisMapperSqlOperation extends MyBatisMapperXMLElement {
|
||||
abstract string getId();
|
||||
/**
|
||||
* Gets the value of the `id` attribute of MyBatis Mapper sql operation element.
|
||||
*/
|
||||
string getId() { result = this.getAttribute("id").getValue() }
|
||||
|
||||
/**
|
||||
* Gets the `<include>` element in a `MyBatisMapperSqlOperation`.
|
||||
@@ -58,11 +61,6 @@ abstract class MyBatisMapperSqlOperation extends MyBatisMapperXMLElement {
|
||||
*/
|
||||
class MyBatisMapperInsert extends MyBatisMapperSqlOperation {
|
||||
MyBatisMapperInsert() { this.getName() = "insert" }
|
||||
|
||||
/**
|
||||
* Gets the value of the `id` attribute of this `<insert>`.
|
||||
*/
|
||||
override string getId() { result = this.getAttribute("id").getValue() }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -70,11 +68,6 @@ class MyBatisMapperInsert extends MyBatisMapperSqlOperation {
|
||||
*/
|
||||
class MyBatisMapperUpdate extends MyBatisMapperSqlOperation {
|
||||
MyBatisMapperUpdate() { this.getName() = "update" }
|
||||
|
||||
/**
|
||||
* Gets the value of the `id` attribute of this `<update>`.
|
||||
*/
|
||||
override string getId() { result = this.getAttribute("id").getValue() }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -82,11 +75,6 @@ class MyBatisMapperUpdate extends MyBatisMapperSqlOperation {
|
||||
*/
|
||||
class MyBatisMapperDelete extends MyBatisMapperSqlOperation {
|
||||
MyBatisMapperDelete() { this.getName() = "delete" }
|
||||
|
||||
/**
|
||||
* Gets the value of the `id` attribute of this `<delete>`.
|
||||
*/
|
||||
override string getId() { result = this.getAttribute("id").getValue() }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -94,15 +82,10 @@ class MyBatisMapperDelete extends MyBatisMapperSqlOperation {
|
||||
*/
|
||||
class MyBatisMapperSelect extends MyBatisMapperSqlOperation {
|
||||
MyBatisMapperSelect() { this.getName() = "select" }
|
||||
|
||||
/**
|
||||
* Gets the value of the `id` attribute of this `<select>`.
|
||||
*/
|
||||
override string getId() { result = this.getAttribute("id").getValue() }
|
||||
}
|
||||
|
||||
/**
|
||||
* A `<select>` element in a `MyBatisMapperXMLElement`.
|
||||
* A `<sql>` element in a `MyBatisMapperXMLElement`.
|
||||
*/
|
||||
class MyBatisMapperSql extends MyBatisMapperXMLElement {
|
||||
MyBatisMapperSql() { this.getName() = "sql" }
|
||||
|
||||
Reference in New Issue
Block a user