mirror of
https://github.com/github/codeql.git
synced 2026-05-01 19:55:15 +02:00
Merge pull request #6319 from haby0/java/MyBatisSqlInjection
[Java] CWE-089 MyBatis Mapper Sql Injection
This commit is contained in:
@@ -24,3 +24,81 @@ private class SqlSinkCsv extends SinkModelCsv {
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
/** The class `org.apache.ibatis.session.Configuration`. */
|
||||
class IbatisConfiguration extends RefType {
|
||||
IbatisConfiguration() { this.hasQualifiedName("org.apache.ibatis.session", "Configuration") }
|
||||
}
|
||||
|
||||
/**
|
||||
* The method `getVariables()` declared in `org.apache.ibatis.session.Configuration`.
|
||||
*/
|
||||
class IbatisConfigurationGetVariablesMethod extends Method {
|
||||
IbatisConfigurationGetVariablesMethod() {
|
||||
this.getDeclaringType() instanceof IbatisConfiguration and
|
||||
this.hasName("getVariables") and
|
||||
this.getNumberOfParameters() = 0
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* An annotation type that identifies Ibatis select.
|
||||
*/
|
||||
private class IbatisSelectAnnotationType extends AnnotationType {
|
||||
IbatisSelectAnnotationType() { this.hasQualifiedName("org.apache.ibatis.annotations", "Select") }
|
||||
}
|
||||
|
||||
/**
|
||||
* An annotation type that identifies Ibatis delete.
|
||||
*/
|
||||
private class IbatisDeleteAnnotationType extends AnnotationType {
|
||||
IbatisDeleteAnnotationType() { this.hasQualifiedName("org.apache.ibatis.annotations", "Delete") }
|
||||
}
|
||||
|
||||
/**
|
||||
* An annotation type that identifies Ibatis insert.
|
||||
*/
|
||||
private class IbatisInsertAnnotationType extends AnnotationType {
|
||||
IbatisInsertAnnotationType() { this.hasQualifiedName("org.apache.ibatis.annotations", "Insert") }
|
||||
}
|
||||
|
||||
/**
|
||||
* An annotation type that identifies Ibatis update.
|
||||
*/
|
||||
private class IbatisUpdateAnnotationType extends AnnotationType {
|
||||
IbatisUpdateAnnotationType() { this.hasQualifiedName("org.apache.ibatis.annotations", "Update") }
|
||||
}
|
||||
|
||||
/**
|
||||
* An Ibatis SQL operation annotation.
|
||||
*/
|
||||
class IbatisSqlOperationAnnotation extends Annotation {
|
||||
IbatisSqlOperationAnnotation() {
|
||||
this.getType() instanceof IbatisSelectAnnotationType or
|
||||
this.getType() instanceof IbatisDeleteAnnotationType or
|
||||
this.getType() instanceof IbatisInsertAnnotationType or
|
||||
this.getType() instanceof IbatisUpdateAnnotationType
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets this annotation's SQL statement string.
|
||||
*/
|
||||
string getSqlValue() {
|
||||
result = this.getAValue("value").(CompileTimeConstantExpr).getStringValue()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Methods annotated with `@org.apache.ibatis.annotations.Select` or `@org.apache.ibatis.annotations.Delete`
|
||||
* or `@org.apache.ibatis.annotations.Update` or `@org.apache.ibatis.annotations.Insert`.
|
||||
*/
|
||||
class MyBatisSqlOperationAnnotationMethod extends Method {
|
||||
MyBatisSqlOperationAnnotationMethod() {
|
||||
this.getAnAnnotation() instanceof IbatisSqlOperationAnnotation
|
||||
}
|
||||
}
|
||||
|
||||
/** The interface `org.apache.ibatis.annotations.Param`. */
|
||||
class TypeParam extends Interface {
|
||||
TypeParam() { this.hasQualifiedName("org.apache.ibatis.annotations", "Param") }
|
||||
}
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
import org.apache.ibatis.annotations.Select;
|
||||
|
||||
public interface MyBatisAnnotationSqlInjection {
|
||||
|
||||
@Select("select * from test where name = ${name}")
|
||||
public Test bad1(String name);
|
||||
|
||||
@Select("select * from test where name = #{name}")
|
||||
public Test good1(String name);
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
<!DOCTYPE qhelp PUBLIC
|
||||
"-//Semmle//qhelp//EN"
|
||||
"qhelp.dtd">
|
||||
<qhelp>
|
||||
<overview>
|
||||
<p>MyBatis uses methods with the annotations <code>@Select</code>, <code>@Insert</code>, etc. to construct dynamic SQL statements.
|
||||
If the syntax <code>${param}</code> is used in those statements, and <code>param</code> is a parameter of the annotated method, attackers can exploit this to tamper with the SQL statements or execute arbitrary SQL commands.</p>
|
||||
</overview>
|
||||
|
||||
<recommendation>
|
||||
<p>
|
||||
When writing MyBatis mapping statements, use the syntax <code>#{xxx}</code> whenever possible. If the syntax <code>${xxx}</code> must be used, any parameters included in it should be sanitized to prevent SQL injection attacks.
|
||||
</p>
|
||||
</recommendation>
|
||||
|
||||
<example>
|
||||
<p>
|
||||
The following sample shows a bad and a good example of MyBatis annotations usage. The <code>bad1</code> method uses <code>$(name)</code>
|
||||
in the <code>@Select</code> annotation to dynamically build a SQL statement, which causes a SQL injection vulnerability.
|
||||
The <code>good1</code> method uses <code>#{name}</code> in the <code>@Select</code> annotation to dynamically include the parameter in a SQL statement, which causes the MyBatis framework to sanitize the input provided, preventing the vulnerability.
|
||||
</p>
|
||||
<sample src="MyBatisAnnotationSqlInjection.java" />
|
||||
</example>
|
||||
|
||||
<references>
|
||||
<li>
|
||||
Fortify:
|
||||
<a href="https://vulncat.fortify.com/en/detail?id=desc.config.java.sql_injection_mybatis_mapper">SQL Injection: MyBatis Mapper</a>.
|
||||
</li>
|
||||
</references>
|
||||
</qhelp>
|
||||
@@ -0,0 +1,60 @@
|
||||
/**
|
||||
* @name SQL injection in MyBatis annotation
|
||||
* @description Constructing a dynamic SQL statement with input that comes from an
|
||||
* untrusted source could allow an attacker to modify the statement's
|
||||
* meaning or to execute arbitrary SQL commands.
|
||||
* @kind path-problem
|
||||
* @problem.severity error
|
||||
* @precision high
|
||||
* @id java/mybatis-annotation-sql-injection
|
||||
* @tags security
|
||||
* external/cwe/cwe-089
|
||||
*/
|
||||
|
||||
import java
|
||||
import DataFlow::PathGraph
|
||||
import MyBatisCommonLib
|
||||
import MyBatisAnnotationSqlInjectionLib
|
||||
import semmle.code.java.dataflow.FlowSources
|
||||
|
||||
private class MyBatisAnnotationSqlInjectionConfiguration extends TaintTracking::Configuration {
|
||||
MyBatisAnnotationSqlInjectionConfiguration() { this = "MyBatis annotation sql injection" }
|
||||
|
||||
override predicate isSource(DataFlow::Node source) { source instanceof RemoteFlowSource }
|
||||
|
||||
override predicate isSink(DataFlow::Node sink) {
|
||||
sink instanceof MyBatisAnnotatedMethodCallArgument
|
||||
}
|
||||
|
||||
override predicate isSanitizer(DataFlow::Node node) {
|
||||
node.getType() instanceof PrimitiveType or
|
||||
node.getType() instanceof BoxedType or
|
||||
node.getType() instanceof NumberType
|
||||
}
|
||||
|
||||
override predicate isAdditionalTaintStep(DataFlow::Node node1, DataFlow::Node node2) {
|
||||
exists(MethodAccess ma |
|
||||
ma.getMethod().getDeclaringType() instanceof TypeObject and
|
||||
ma.getMethod().getName() = "toString" and
|
||||
ma.getQualifier() = node1.asExpr() and
|
||||
ma = node2.asExpr()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
from
|
||||
MyBatisAnnotationSqlInjectionConfiguration cfg, DataFlow::PathNode source,
|
||||
DataFlow::PathNode sink, IbatisSqlOperationAnnotation isoa, MethodAccess ma,
|
||||
string unsafeExpression
|
||||
where
|
||||
cfg.hasFlowPath(source, sink) and
|
||||
ma.getAnArgument() = sink.getNode().asExpr() and
|
||||
myBatisSqlOperationAnnotationFromMethod(ma.getMethod(), isoa) and
|
||||
unsafeExpression = getAMybatisAnnotationSqlValue(isoa) and
|
||||
(
|
||||
isMybatisXmlOrAnnotationSqlInjection(sink.getNode(), ma, unsafeExpression) or
|
||||
isMybatisCollectionTypeSqlInjection(sink.getNode(), ma, unsafeExpression)
|
||||
)
|
||||
select sink.getNode(), source, sink,
|
||||
"MyBatis annotation SQL injection might include code from $@ to $@.", source.getNode(),
|
||||
"this user input", isoa, "this SQL operation"
|
||||
@@ -0,0 +1,17 @@
|
||||
/**
|
||||
* Provides classes for SQL injection detection regarding MyBatis annotated methods.
|
||||
*/
|
||||
|
||||
import java
|
||||
import MyBatisCommonLib
|
||||
import semmle.code.java.dataflow.FlowSources
|
||||
import semmle.code.java.frameworks.Properties
|
||||
|
||||
/** An argument of a MyBatis annotated method. */
|
||||
class MyBatisAnnotatedMethodCallArgument extends DataFlow::Node {
|
||||
MyBatisAnnotatedMethodCallArgument() {
|
||||
exists(MyBatisSqlOperationAnnotationMethod msoam, MethodAccess ma | ma.getMethod() = msoam |
|
||||
ma.getAnArgument() = this.asExpr()
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,184 @@
|
||||
/**
|
||||
* Provides public classes for MyBatis SQL injection detection.
|
||||
*/
|
||||
|
||||
import java
|
||||
import semmle.code.xml.MyBatisMapperXML
|
||||
import semmle.code.java.dataflow.FlowSources
|
||||
import semmle.code.java.frameworks.MyBatis
|
||||
import semmle.code.java.frameworks.Properties
|
||||
|
||||
private predicate propertiesKey(DataFlow::Node prop, string key) {
|
||||
exists(MethodAccess m |
|
||||
m.getMethod() instanceof PropertiesSetPropertyMethod and
|
||||
key = m.getArgument(0).(CompileTimeConstantExpr).getStringValue() and
|
||||
prop.asExpr() = m.getQualifier()
|
||||
)
|
||||
}
|
||||
|
||||
/** A data flow configuration tracing flow from ibatis `Configuration.getVariables()` to a store into a `Properties` object. */
|
||||
private class PropertiesFlowConfig extends DataFlow2::Configuration {
|
||||
PropertiesFlowConfig() { this = "PropertiesFlowConfig" }
|
||||
|
||||
override predicate isSource(DataFlow::Node src) {
|
||||
exists(MethodAccess ma | ma.getMethod() instanceof IbatisConfigurationGetVariablesMethod |
|
||||
src.asExpr() = ma
|
||||
)
|
||||
}
|
||||
|
||||
override predicate isSink(DataFlow::Node sink) { propertiesKey(sink, _) }
|
||||
}
|
||||
|
||||
/** Gets a `Properties` key that may map onto a Mybatis `Configuration` variable. */
|
||||
string getAMybatisConfigurationVariableKey() {
|
||||
exists(PropertiesFlowConfig conf, DataFlow::Node n |
|
||||
propertiesKey(n, result) and
|
||||
conf.hasFlowTo(n)
|
||||
)
|
||||
}
|
||||
|
||||
/** A reference type that extends a parameterization of `java.util.List`. */
|
||||
class ListType extends RefType {
|
||||
ListType() {
|
||||
this.getSourceDeclaration().getASourceSupertype*().hasQualifiedName("java.util", "List")
|
||||
}
|
||||
}
|
||||
|
||||
/** Holds if the specified `method` uses MyBatis Mapper XMLElement `mmxx`. */
|
||||
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
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
/** Holds if the specified `method` has Ibatis Sql operation annotation `isoa`. */
|
||||
predicate myBatisSqlOperationAnnotationFromMethod(Method method, IbatisSqlOperationAnnotation isoa) {
|
||||
exists(MyBatisSqlOperationAnnotationMethod msoam |
|
||||
msoam = method and
|
||||
msoam.getAnAnnotation() = isoa
|
||||
)
|
||||
}
|
||||
|
||||
/** Gets a `#{...}` or `${...}` expression argument in XML element `xmle`. */
|
||||
string getAMybatisXmlSetValue(XMLElement xmle) {
|
||||
result = xmle.getTextValue().regexpFind("(#|\\$)\\{[^\\}]*\\}", _, _)
|
||||
}
|
||||
|
||||
/** Gets a `#{...}` or `${...}` expression argument in annotation `isoa`. */
|
||||
string getAMybatisAnnotationSqlValue(IbatisSqlOperationAnnotation isoa) {
|
||||
result = isoa.getSqlValue().regexpFind("(#|\\$)\\{[^\\}]*\\}", _, _)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `node` is an argument to `ma` that is vulnerable to SQL injection attacks if `unsafeExpression` occurs in a MyBatis SQL expression.
|
||||
*
|
||||
* This case currently assumes all `${...}` expressions are potentially dangerous when there is a non-`@Param` annotated, collection-typed parameter to `ma`.
|
||||
*/
|
||||
bindingset[unsafeExpression]
|
||||
predicate isMybatisCollectionTypeSqlInjection(
|
||||
DataFlow::Node node, MethodAccess ma, string unsafeExpression
|
||||
) {
|
||||
not unsafeExpression.regexpMatch("\\$\\{" + getAMybatisConfigurationVariableKey() + "\\}") and
|
||||
// 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 |
|
||||
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
|
||||
unsafeExpression.matches("${%}") and
|
||||
ma.getArgument(i) = node.asExpr()
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `node` is an argument to `ma` that is vulnerable to SQL injection attacks if `unsafeExpression` occurs in a MyBatis SQL expression.
|
||||
*
|
||||
* This accounts for:
|
||||
* - arguments referred to by a name given in a `@Param` annotation,
|
||||
* - arguments referred to by ordinal position, like `${param1}`
|
||||
* - references to class instance fields
|
||||
* - any `${}` expression where there is a single, non-`@Param`-annotated argument to `ma`.
|
||||
*/
|
||||
bindingset[unsafeExpression]
|
||||
predicate isMybatisXmlOrAnnotationSqlInjection(
|
||||
DataFlow::Node node, MethodAccess ma, string unsafeExpression
|
||||
) {
|
||||
not unsafeExpression.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(Annotation annotation |
|
||||
unsafeExpression
|
||||
.matches("${" + annotation.getValue("value").(CompileTimeConstantExpr).getStringValue() +
|
||||
"%}") and
|
||||
annotation.getType() instanceof TypeParam and
|
||||
ma.getAnArgument() = 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
|
||||
(
|
||||
unsafeExpression.matches("${param" + (i + 1) + "%}")
|
||||
or
|
||||
unsafeExpression.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
|
||||
unsafeExpression.matches("${" + t.getAField().getName() + "%}") 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
|
||||
unsafeExpression.matches("${%}") and
|
||||
ma.getAnArgument() = node.asExpr()
|
||||
)
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
<!DOCTYPE qhelp PUBLIC
|
||||
"-//Semmle//qhelp//EN"
|
||||
"qhelp.dtd">
|
||||
<qhelp>
|
||||
<overview>
|
||||
<p>MyBatis allows operating the database by creating XML files to construct dynamic SQL statements.
|
||||
If the syntax <code>${param}</code> is used in those statements, and <code>param</code> is under the user's control, attackers can exploit this to tamper with the SQL statements or execute arbitrary SQL commands.</p>
|
||||
</overview>
|
||||
|
||||
<recommendation>
|
||||
<p>
|
||||
When writing MyBatis mapping statements, try to use the syntax <code>#{xxx}</code>. If the syntax <code>${xxx}</code> must be used, any parameters included in it should be sanitized to prevent SQL injection attacks.
|
||||
</p>
|
||||
</recommendation>
|
||||
|
||||
<example>
|
||||
<p>
|
||||
The following sample shows several bad and good examples of MyBatis XML files usage. In <code>bad1</code>,
|
||||
<code>bad2</code>, <code>bad3</code>, <code>bad4</code>, and <code >bad5</code> the syntax
|
||||
<code>${xxx}</code> is used to build dynamic SQL statements, which causes a SQL injection vulnerability. In <code>good1</code>,
|
||||
the program uses the <code>${xxx}</code> syntax, but there are subtle restrictions on the data,
|
||||
while in <code>good2</code> the syntax <code>#{xxx}</code> is used. In both cases the SQL injection vulnerability is prevented.
|
||||
</p>
|
||||
<sample src="MyBatisMapperXmlSqlInjection.xml" />
|
||||
</example>
|
||||
|
||||
<references>
|
||||
<li>
|
||||
Fortify:
|
||||
<a href="https://vulncat.fortify.com/en/detail?id=desc.config.java.sql_injection_mybatis_mapper">SQL Injection: MyBatis Mapper</a>.
|
||||
</li>
|
||||
</references>
|
||||
</qhelp>
|
||||
@@ -0,0 +1,62 @@
|
||||
/**
|
||||
* @name SQL injection in MyBatis Mapper XML
|
||||
* @description Constructing a dynamic SQL statement with input that comes from an
|
||||
* untrusted source could allow an attacker to modify the statement's
|
||||
* meaning or to execute arbitrary SQL commands.
|
||||
* @kind path-problem
|
||||
* @problem.severity error
|
||||
* @precision high
|
||||
* @id java/mybatis-xml-sql-injection
|
||||
* @tags security
|
||||
* external/cwe/cwe-089
|
||||
*/
|
||||
|
||||
import java
|
||||
import DataFlow::PathGraph
|
||||
import MyBatisCommonLib
|
||||
import MyBatisMapperXmlSqlInjectionLib
|
||||
import semmle.code.xml.MyBatisMapperXML
|
||||
import semmle.code.java.dataflow.FlowSources
|
||||
|
||||
private class MyBatisMapperXmlSqlInjectionConfiguration extends TaintTracking::Configuration {
|
||||
MyBatisMapperXmlSqlInjectionConfiguration() { this = "MyBatis mapper xml sql injection" }
|
||||
|
||||
override predicate isSource(DataFlow::Node source) { source instanceof RemoteFlowSource }
|
||||
|
||||
override predicate isSink(DataFlow::Node sink) {
|
||||
sink instanceof MyBatisMapperMethodCallAnArgument
|
||||
}
|
||||
|
||||
override predicate isSanitizer(DataFlow::Node node) {
|
||||
node.getType() instanceof PrimitiveType or
|
||||
node.getType() instanceof BoxedType or
|
||||
node.getType() instanceof NumberType
|
||||
}
|
||||
|
||||
override predicate isAdditionalTaintStep(DataFlow::Node node1, DataFlow::Node node2) {
|
||||
exists(MethodAccess ma |
|
||||
ma.getMethod().getDeclaringType() instanceof TypeObject and
|
||||
ma.getMethod().getName() = "toString" and
|
||||
ma.getQualifier() = node1.asExpr() and
|
||||
ma = node2.asExpr()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
from
|
||||
MyBatisMapperXmlSqlInjectionConfiguration cfg, DataFlow::PathNode source, DataFlow::PathNode sink,
|
||||
MyBatisMapperXMLElement mmxe, MethodAccess ma, string unsafeExpression
|
||||
where
|
||||
cfg.hasFlowPath(source, sink) and
|
||||
ma.getAnArgument() = sink.getNode().asExpr() and
|
||||
myBatisMapperXMLElementFromMethod(ma.getMethod(), mmxe) and
|
||||
unsafeExpression = getAMybatisXmlSetValue(mmxe) and
|
||||
(
|
||||
isMybatisXmlOrAnnotationSqlInjection(sink.getNode(), ma, unsafeExpression)
|
||||
or
|
||||
mmxe instanceof MyBatisMapperForeach and
|
||||
isMybatisCollectionTypeSqlInjection(sink.getNode(), ma, unsafeExpression)
|
||||
)
|
||||
select sink.getNode(), source, sink,
|
||||
"MyBatis Mapper XML SQL injection might include code from $@ to $@.", source.getNode(),
|
||||
"this user input", mmxe, "this SQL operation"
|
||||
@@ -0,0 +1,75 @@
|
||||
<?xml version="1.0" encoding="UTF-8" ?>
|
||||
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
||||
"http://mybatis.org/dtd/mybatis-3-mapper.dtd" >
|
||||
<mapper namespace="SqlInjectionMapper">
|
||||
|
||||
<resultMap id="BaseResultMap" type="Test">
|
||||
<id column="id" jdbcType="INTEGER" property="id"/>
|
||||
<result column="name" jdbcType="VARCHAR" property="name"/>
|
||||
<result column="pass" jdbcType="VARCHAR" property="pass"/>
|
||||
</resultMap>
|
||||
|
||||
<sql id="Update_By_Example_Where_Clause">
|
||||
<where>
|
||||
<if test="name != null">
|
||||
-- bad
|
||||
and name = ${name}
|
||||
</if>
|
||||
<if test="id != null">
|
||||
and id = #{id}
|
||||
</if>
|
||||
</where>
|
||||
</sql>
|
||||
|
||||
<select id="bad1" parameterType="java.lang.String" resultMap="BaseResultMap">
|
||||
-- bad
|
||||
select id,name from test where name like '%${name}%'
|
||||
</select>
|
||||
|
||||
<select id="bad2" parameterType="java.lang.String" resultMap="BaseResultMap">
|
||||
-- bad
|
||||
select id,name from test order by ${name} desc
|
||||
</select>
|
||||
|
||||
<select id="bad3" parameterType="java.lang.String" resultMap="BaseResultMap">
|
||||
-- bad
|
||||
select id,name from test where name in ${name}
|
||||
</select>
|
||||
|
||||
<update id="bad4" parameterType="Test">
|
||||
update test
|
||||
<set>
|
||||
<if test="pass != null">
|
||||
pass = #{pass},
|
||||
</if>
|
||||
</set>
|
||||
<if test="_parameter != null">
|
||||
-- bad
|
||||
<include refid="Update_By_Example_Where_Clause" />
|
||||
</if>
|
||||
</update>
|
||||
|
||||
<insert id="bad5" parameterType="Test">
|
||||
insert into test (name, pass)
|
||||
<trim prefix="values (" suffix=")" suffixOverrides=",">
|
||||
<if test="name != null">
|
||||
-- bad
|
||||
name = ${name},
|
||||
</if>
|
||||
<if test="pass != null">
|
||||
-- bad
|
||||
pass = ${pass},
|
||||
</if>
|
||||
</trim>
|
||||
</insert>
|
||||
|
||||
<select id="good1" parameterType="java.lang.Integer" resultMap="BaseResultMap">
|
||||
-- good
|
||||
select id,name from test where id = ${id}
|
||||
</select>
|
||||
|
||||
<select id="good2" parameterType="java.lang.String" resultMap="BaseResultMap">
|
||||
-- good
|
||||
select id,name from test where name = #{name}
|
||||
</select>
|
||||
</mapper>
|
||||
@@ -0,0 +1,19 @@
|
||||
/**
|
||||
* Provide classes for SQL injection detection in MyBatis Mapper XML.
|
||||
*/
|
||||
|
||||
import java
|
||||
import semmle.code.xml.MyBatisMapperXML
|
||||
import semmle.code.java.dataflow.FlowSources
|
||||
import semmle.code.java.frameworks.Properties
|
||||
|
||||
/** A sink for MyBatis Mapper method call an argument. */
|
||||
class MyBatisMapperMethodCallAnArgument extends DataFlow::Node {
|
||||
MyBatisMapperMethodCallAnArgument() {
|
||||
exists(MyBatisMapperSqlOperation mbmxe, MethodAccess ma |
|
||||
mbmxe.getMapperMethod() = ma.getMethod()
|
||||
|
|
||||
ma.getAnArgument() = this.asExpr()
|
||||
)
|
||||
}
|
||||
}
|
||||
116
java/ql/src/semmle/code/xml/MyBatisMapperXML.qll
Normal file
116
java/ql/src/semmle/code/xml/MyBatisMapperXML.qll
Normal file
@@ -0,0 +1,116 @@
|
||||
/**
|
||||
* Provides classes for working with MyBatis mapper xml files and their content.
|
||||
*/
|
||||
|
||||
import java
|
||||
|
||||
/**
|
||||
* MyBatis Mapper XML file.
|
||||
*/
|
||||
class MyBatisMapperXMLFile extends XMLFile {
|
||||
MyBatisMapperXMLFile() {
|
||||
count(XMLElement e | e = this.getAChild()) = 1 and
|
||||
this.getAChild().getName() = "mapper"
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* An XML element in a `MyBatisMapperXMLFile`.
|
||||
*/
|
||||
class MyBatisMapperXMLElement extends XMLElement {
|
||||
MyBatisMapperXMLElement() { this.getFile() instanceof MyBatisMapperXMLFile }
|
||||
|
||||
/**
|
||||
* Gets the value for this element, with leading and trailing whitespace trimmed.
|
||||
*/
|
||||
string getValue() { result = this.allCharactersString().trim() }
|
||||
|
||||
/**
|
||||
* Gets the reference type bound to MyBatis Mapper XML File.
|
||||
*/
|
||||
RefType getNamespaceRefType() {
|
||||
result.getQualifiedName() = this.getAttribute("namespace").getValue()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* An MyBatis Mapper sql operation element.
|
||||
*/
|
||||
abstract class MyBatisMapperSqlOperation extends MyBatisMapperXMLElement {
|
||||
/**
|
||||
* 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`.
|
||||
*/
|
||||
MyBatisMapperInclude getInclude() { result = this.getAChild*() }
|
||||
|
||||
/**
|
||||
* Gets the method bound to MyBatis Mapper XML File.
|
||||
*/
|
||||
Method getMapperMethod() {
|
||||
result.getName() = this.getId() and
|
||||
result.getDeclaringType() = this.getParent().(MyBatisMapperXMLElement).getNamespaceRefType()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A `<insert>` element in a `MyBatisMapperSqlOperation`.
|
||||
*/
|
||||
class MyBatisMapperInsert extends MyBatisMapperSqlOperation {
|
||||
MyBatisMapperInsert() { this.getName() = "insert" }
|
||||
}
|
||||
|
||||
/**
|
||||
* A `<update>` element in a `MyBatisMapperSqlOperation`.
|
||||
*/
|
||||
class MyBatisMapperUpdate extends MyBatisMapperSqlOperation {
|
||||
MyBatisMapperUpdate() { this.getName() = "update" }
|
||||
}
|
||||
|
||||
/**
|
||||
* A `<delete>` element in a `MyBatisMapperSqlOperation`.
|
||||
*/
|
||||
class MyBatisMapperDelete extends MyBatisMapperSqlOperation {
|
||||
MyBatisMapperDelete() { this.getName() = "delete" }
|
||||
}
|
||||
|
||||
/**
|
||||
* A `<select>` element in a `MyBatisMapperSqlOperation`.
|
||||
*/
|
||||
class MyBatisMapperSelect extends MyBatisMapperSqlOperation {
|
||||
MyBatisMapperSelect() { this.getName() = "select" }
|
||||
}
|
||||
|
||||
/**
|
||||
* A `<sql>` element in a `MyBatisMapperXMLElement`.
|
||||
*/
|
||||
class MyBatisMapperSql extends MyBatisMapperXMLElement {
|
||||
MyBatisMapperSql() { this.getName() = "sql" }
|
||||
|
||||
/**
|
||||
* Gets the value of the `id` attribute of this `<sql>`.
|
||||
*/
|
||||
string getId() { result = this.getAttribute("id").getValue() }
|
||||
}
|
||||
|
||||
/**
|
||||
* A `<include>` element in a `MyBatisMapperXMLElement`.
|
||||
*/
|
||||
class MyBatisMapperInclude extends MyBatisMapperXMLElement {
|
||||
MyBatisMapperInclude() { this.getName() = "include" }
|
||||
|
||||
/**
|
||||
* Gets the value of the `refid` attribute of this `<include>`.
|
||||
*/
|
||||
string getRefid() { result = this.getAttribute("refid").getValue() }
|
||||
}
|
||||
|
||||
/**
|
||||
* A `<foreach>` element in a `MyBatisMapperXMLElement`.
|
||||
*/
|
||||
class MyBatisMapperForeach extends MyBatisMapperXMLElement {
|
||||
MyBatisMapperForeach() { getName() = "foreach" }
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
edges
|
||||
| MybatisSqlInjection.java:62:19:62:43 | name : String | MybatisSqlInjection.java:63:35:63:38 | name : String |
|
||||
| MybatisSqlInjection.java:63:35:63:38 | name : String | MybatisSqlInjectionService.java:48:19:48:29 | name : String |
|
||||
| MybatisSqlInjectionService.java:48:19:48:29 | name : String | MybatisSqlInjectionService.java:50:23:50:26 | name : String |
|
||||
| MybatisSqlInjectionService.java:50:3:50:9 | hashMap [post update] [<map.value>] : String | MybatisSqlInjectionService.java:51:27:51:33 | hashMap |
|
||||
| MybatisSqlInjectionService.java:50:23:50:26 | name : String | MybatisSqlInjectionService.java:50:3:50:9 | hashMap [post update] [<map.value>] : String |
|
||||
nodes
|
||||
| MybatisSqlInjection.java:62:19:62:43 | name : String | semmle.label | name : String |
|
||||
| MybatisSqlInjection.java:63:35:63:38 | name : String | semmle.label | name : String |
|
||||
| MybatisSqlInjectionService.java:48:19:48:29 | name : String | semmle.label | name : String |
|
||||
| MybatisSqlInjectionService.java:50:3:50:9 | hashMap [post update] [<map.value>] : String | semmle.label | hashMap [post update] [<map.value>] : String |
|
||||
| MybatisSqlInjectionService.java:50:23:50:26 | name : String | semmle.label | name : String |
|
||||
| MybatisSqlInjectionService.java:51:27:51:33 | hashMap | semmle.label | hashMap |
|
||||
subpaths
|
||||
#select
|
||||
| MybatisSqlInjectionService.java:51:27:51:33 | hashMap | MybatisSqlInjection.java:62:19:62:43 | name : String | MybatisSqlInjectionService.java:51:27:51:33 | hashMap | MyBatis annotation SQL injection might include code from $@ to $@. | MybatisSqlInjection.java:62:19:62:43 | name | this user input | SqlInjectionMapper.java:29:2:29:54 | Select | this SQL operation |
|
||||
@@ -0,0 +1 @@
|
||||
experimental/Security/CWE/CWE-089/MyBatisAnnotationSqlInjection.ql
|
||||
@@ -0,0 +1,69 @@
|
||||
edges
|
||||
| MybatisSqlInjection.java:19:25:19:49 | name : String | MybatisSqlInjection.java:20:55:20:58 | name : String |
|
||||
| MybatisSqlInjection.java:20:55:20:58 | name : String | MybatisSqlInjectionService.java:13:25:13:35 | name : String |
|
||||
| MybatisSqlInjection.java:25:25:25:49 | name : String | MybatisSqlInjection.java:26:55:26:58 | name : String |
|
||||
| MybatisSqlInjection.java:26:55:26:58 | name : String | MybatisSqlInjectionService.java:18:25:18:35 | name : String |
|
||||
| MybatisSqlInjection.java:31:25:31:49 | test : Test | MybatisSqlInjection.java:32:55:32:58 | test : Test |
|
||||
| MybatisSqlInjection.java:32:55:32:58 | test : Test | MybatisSqlInjectionService.java:23:25:23:33 | test : Test |
|
||||
| MybatisSqlInjection.java:37:19:37:40 | test : Test | MybatisSqlInjection.java:38:35:38:38 | test : Test |
|
||||
| MybatisSqlInjection.java:38:35:38:38 | test : Test | MybatisSqlInjectionService.java:28:19:28:27 | test : Test |
|
||||
| MybatisSqlInjection.java:42:19:42:40 | test : Test | MybatisSqlInjection.java:43:35:43:38 | test : Test |
|
||||
| MybatisSqlInjection.java:43:35:43:38 | test : Test | MybatisSqlInjectionService.java:32:19:32:27 | test : Test |
|
||||
| MybatisSqlInjection.java:47:19:47:57 | params : Map | MybatisSqlInjection.java:48:35:48:40 | params : Map |
|
||||
| MybatisSqlInjection.java:48:35:48:40 | params : Map | MybatisSqlInjectionService.java:36:19:36:44 | params : Map |
|
||||
| MybatisSqlInjection.java:52:19:52:50 | params : List | MybatisSqlInjection.java:53:35:53:40 | params : List |
|
||||
| MybatisSqlInjection.java:53:35:53:40 | params : List | MybatisSqlInjectionService.java:40:19:40:37 | params : List |
|
||||
| MybatisSqlInjection.java:57:19:57:46 | params : String[] | MybatisSqlInjection.java:58:35:58:40 | params : String[] |
|
||||
| MybatisSqlInjection.java:58:35:58:40 | params : String[] | MybatisSqlInjectionService.java:44:19:44:33 | params : String[] |
|
||||
| MybatisSqlInjectionService.java:13:25:13:35 | name : String | MybatisSqlInjectionService.java:14:47:14:50 | name |
|
||||
| MybatisSqlInjectionService.java:18:25:18:35 | name : String | MybatisSqlInjectionService.java:19:47:19:50 | name |
|
||||
| MybatisSqlInjectionService.java:23:25:23:33 | test : Test | MybatisSqlInjectionService.java:24:47:24:50 | test |
|
||||
| MybatisSqlInjectionService.java:28:19:28:27 | test : Test | MybatisSqlInjectionService.java:29:27:29:30 | test |
|
||||
| MybatisSqlInjectionService.java:32:19:32:27 | test : Test | MybatisSqlInjectionService.java:33:27:33:30 | test |
|
||||
| MybatisSqlInjectionService.java:36:19:36:44 | params : Map | MybatisSqlInjectionService.java:37:27:37:32 | params |
|
||||
| MybatisSqlInjectionService.java:40:19:40:37 | params : List | MybatisSqlInjectionService.java:41:27:41:32 | params |
|
||||
| MybatisSqlInjectionService.java:44:19:44:33 | params : String[] | MybatisSqlInjectionService.java:45:27:45:32 | params |
|
||||
nodes
|
||||
| MybatisSqlInjection.java:19:25:19:49 | name : String | semmle.label | name : String |
|
||||
| MybatisSqlInjection.java:20:55:20:58 | name : String | semmle.label | name : String |
|
||||
| MybatisSqlInjection.java:25:25:25:49 | name : String | semmle.label | name : String |
|
||||
| MybatisSqlInjection.java:26:55:26:58 | name : String | semmle.label | name : String |
|
||||
| MybatisSqlInjection.java:31:25:31:49 | test : Test | semmle.label | test : Test |
|
||||
| MybatisSqlInjection.java:32:55:32:58 | test : Test | semmle.label | test : Test |
|
||||
| MybatisSqlInjection.java:37:19:37:40 | test : Test | semmle.label | test : Test |
|
||||
| MybatisSqlInjection.java:38:35:38:38 | test : Test | semmle.label | test : Test |
|
||||
| MybatisSqlInjection.java:42:19:42:40 | test : Test | semmle.label | test : Test |
|
||||
| MybatisSqlInjection.java:43:35:43:38 | test : Test | semmle.label | test : Test |
|
||||
| MybatisSqlInjection.java:47:19:47:57 | params : Map | semmle.label | params : Map |
|
||||
| MybatisSqlInjection.java:48:35:48:40 | params : Map | semmle.label | params : Map |
|
||||
| MybatisSqlInjection.java:52:19:52:50 | params : List | semmle.label | params : List |
|
||||
| MybatisSqlInjection.java:53:35:53:40 | params : List | semmle.label | params : List |
|
||||
| MybatisSqlInjection.java:57:19:57:46 | params : String[] | semmle.label | params : String[] |
|
||||
| MybatisSqlInjection.java:58:35:58:40 | params : String[] | semmle.label | params : String[] |
|
||||
| MybatisSqlInjectionService.java:13:25:13:35 | name : String | semmle.label | name : String |
|
||||
| MybatisSqlInjectionService.java:14:47:14:50 | name | semmle.label | name |
|
||||
| MybatisSqlInjectionService.java:18:25:18:35 | name : String | semmle.label | name : String |
|
||||
| MybatisSqlInjectionService.java:19:47:19:50 | name | semmle.label | name |
|
||||
| MybatisSqlInjectionService.java:23:25:23:33 | test : Test | semmle.label | test : Test |
|
||||
| MybatisSqlInjectionService.java:24:47:24:50 | test | semmle.label | test |
|
||||
| MybatisSqlInjectionService.java:28:19:28:27 | test : Test | semmle.label | test : Test |
|
||||
| MybatisSqlInjectionService.java:29:27:29:30 | test | semmle.label | test |
|
||||
| MybatisSqlInjectionService.java:32:19:32:27 | test : Test | semmle.label | test : Test |
|
||||
| MybatisSqlInjectionService.java:33:27:33:30 | test | semmle.label | test |
|
||||
| MybatisSqlInjectionService.java:36:19:36:44 | params : Map | semmle.label | params : Map |
|
||||
| MybatisSqlInjectionService.java:37:27:37:32 | params | semmle.label | params |
|
||||
| MybatisSqlInjectionService.java:40:19:40:37 | params : List | semmle.label | params : List |
|
||||
| MybatisSqlInjectionService.java:41:27:41:32 | params | semmle.label | params |
|
||||
| MybatisSqlInjectionService.java:44:19:44:33 | params : String[] | semmle.label | params : String[] |
|
||||
| MybatisSqlInjectionService.java:45:27:45:32 | params | semmle.label | params |
|
||||
subpaths
|
||||
#select
|
||||
| MybatisSqlInjectionService.java:14:47:14:50 | name | MybatisSqlInjection.java:19:25:19:49 | name : String | MybatisSqlInjectionService.java:14:47:14:50 | name | MyBatis Mapper XML SQL injection might include code from $@ to $@. | MybatisSqlInjection.java:19:25:19:49 | name | this user input | SqlInjectionMapper.xml:23:3:25:12 | select | this SQL operation |
|
||||
| MybatisSqlInjectionService.java:19:47:19:50 | name | MybatisSqlInjection.java:25:25:25:49 | name : String | MybatisSqlInjectionService.java:19:47:19:50 | name | MyBatis Mapper XML SQL injection might include code from $@ to $@. | MybatisSqlInjection.java:25:25:25:49 | name | this user input | SqlInjectionMapper.xml:27:3:29:12 | select | this SQL operation |
|
||||
| MybatisSqlInjectionService.java:24:47:24:50 | test | MybatisSqlInjection.java:31:25:31:49 | test : Test | MybatisSqlInjectionService.java:24:47:24:50 | test | MyBatis Mapper XML SQL injection might include code from $@ to $@. | MybatisSqlInjection.java:31:25:31:49 | test | this user input | SqlInjectionMapper.xml:31:3:33:12 | select | this SQL operation |
|
||||
| MybatisSqlInjectionService.java:29:27:29:30 | test | MybatisSqlInjection.java:37:19:37:40 | test : Test | MybatisSqlInjectionService.java:29:27:29:30 | test | MyBatis Mapper XML SQL injection might include code from $@ to $@. | MybatisSqlInjection.java:37:19:37:40 | test | this user input | SqlInjectionMapper.xml:14:7:16:12 | if | this SQL operation |
|
||||
| MybatisSqlInjectionService.java:33:27:33:30 | test | MybatisSqlInjection.java:42:19:42:40 | test : Test | MybatisSqlInjectionService.java:33:27:33:30 | test | MyBatis Mapper XML SQL injection might include code from $@ to $@. | MybatisSqlInjection.java:42:19:42:40 | test | this user input | SqlInjectionMapper.xml:50:7:52:12 | if | this SQL operation |
|
||||
| MybatisSqlInjectionService.java:33:27:33:30 | test | MybatisSqlInjection.java:42:19:42:40 | test : Test | MybatisSqlInjectionService.java:33:27:33:30 | test | MyBatis Mapper XML SQL injection might include code from $@ to $@. | MybatisSqlInjection.java:42:19:42:40 | test | this user input | SqlInjectionMapper.xml:53:7:55:12 | if | this SQL operation |
|
||||
| MybatisSqlInjectionService.java:37:27:37:32 | params | MybatisSqlInjection.java:47:19:47:57 | params : Map | MybatisSqlInjectionService.java:37:27:37:32 | params | MyBatis Mapper XML SQL injection might include code from $@ to $@. | MybatisSqlInjection.java:47:19:47:57 | params | this user input | SqlInjectionMapper.xml:59:3:61:12 | select | this SQL operation |
|
||||
| MybatisSqlInjectionService.java:41:27:41:32 | params | MybatisSqlInjection.java:52:19:52:50 | params : List | MybatisSqlInjectionService.java:41:27:41:32 | params | MyBatis Mapper XML SQL injection might include code from $@ to $@. | MybatisSqlInjection.java:52:19:52:50 | params | this user input | SqlInjectionMapper.xml:65:5:67:15 | foreach | this SQL operation |
|
||||
| MybatisSqlInjectionService.java:45:27:45:32 | params | MybatisSqlInjection.java:57:19:57:46 | params : String[] | MybatisSqlInjectionService.java:45:27:45:32 | params | MyBatis Mapper XML SQL injection might include code from $@ to $@. | MybatisSqlInjection.java:57:19:57:46 | params | this user input | SqlInjectionMapper.xml:72:5:74:15 | foreach | this SQL operation |
|
||||
@@ -0,0 +1 @@
|
||||
experimental/Security/CWE/CWE-089/MyBatisMapperXmlSqlInjection.ql
|
||||
@@ -0,0 +1,71 @@
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Controller;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.ModelAttribute;
|
||||
import org.springframework.web.bind.annotation.RequestBody;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RequestMethod;
|
||||
import org.springframework.web.bind.annotation.RequestParam;
|
||||
|
||||
@Controller
|
||||
public class MybatisSqlInjection {
|
||||
|
||||
@Autowired
|
||||
private MybatisSqlInjectionService mybatisSqlInjectionService;
|
||||
|
||||
@GetMapping(value = "msi1")
|
||||
public List<Test> bad1(@RequestParam String name) {
|
||||
List<Test> result = mybatisSqlInjectionService.bad1(name);
|
||||
return result;
|
||||
}
|
||||
|
||||
@GetMapping(value = "msi2")
|
||||
public List<Test> bad2(@RequestParam String name) {
|
||||
List<Test> result = mybatisSqlInjectionService.bad2(name);
|
||||
return result;
|
||||
}
|
||||
|
||||
@GetMapping(value = "msi3")
|
||||
public List<Test> bad3(@ModelAttribute Test test) {
|
||||
List<Test> result = mybatisSqlInjectionService.bad3(test);
|
||||
return result;
|
||||
}
|
||||
|
||||
@RequestMapping(value = "msi4", method = RequestMethod.POST, produces = "application/json")
|
||||
public void bad4(@RequestBody Test test) {
|
||||
mybatisSqlInjectionService.bad4(test);
|
||||
}
|
||||
|
||||
@RequestMapping(value = "msi5", method = RequestMethod.PUT, produces = "application/json")
|
||||
public void bad5(@RequestBody Test test) {
|
||||
mybatisSqlInjectionService.bad5(test);
|
||||
}
|
||||
|
||||
@RequestMapping(value = "msi6", method = RequestMethod.POST, produces = "application/json")
|
||||
public void bad6(@RequestBody Map<String, String> params) {
|
||||
mybatisSqlInjectionService.bad6(params);
|
||||
}
|
||||
|
||||
@RequestMapping(value = "msi7", method = RequestMethod.POST, produces = "application/json")
|
||||
public void bad7(@RequestBody List<String> params) {
|
||||
mybatisSqlInjectionService.bad7(params);
|
||||
}
|
||||
|
||||
@RequestMapping(value = "msi8", method = RequestMethod.POST, produces = "application/json")
|
||||
public void bad8(@RequestBody String[] params) {
|
||||
mybatisSqlInjectionService.bad8(params);
|
||||
}
|
||||
|
||||
@GetMapping(value = "msi9")
|
||||
public void bad9(@RequestParam String name) {
|
||||
mybatisSqlInjectionService.bad9(name);
|
||||
}
|
||||
|
||||
@GetMapping(value = "good1")
|
||||
public List<Test> good1(Integer id) {
|
||||
List<Test> result = mybatisSqlInjectionService.good1(id);
|
||||
return result;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.HashMap;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
@Service
|
||||
public class MybatisSqlInjectionService {
|
||||
|
||||
@Autowired
|
||||
private SqlInjectionMapper sqlInjectionMapper;
|
||||
|
||||
public List<Test> bad1(String name) {
|
||||
List<Test> result = sqlInjectionMapper.bad1(name);
|
||||
return result;
|
||||
}
|
||||
|
||||
public List<Test> bad2(String name) {
|
||||
List<Test> result = sqlInjectionMapper.bad2(name);
|
||||
return result;
|
||||
}
|
||||
|
||||
public List<Test> bad3(Test test) {
|
||||
List<Test> result = sqlInjectionMapper.bad3(test);
|
||||
return result;
|
||||
}
|
||||
|
||||
public void bad4(Test test) {
|
||||
sqlInjectionMapper.bad4(test);
|
||||
}
|
||||
|
||||
public void bad5(Test test) {
|
||||
sqlInjectionMapper.bad5(test);
|
||||
}
|
||||
|
||||
public void bad6(Map<String, String> params) {
|
||||
sqlInjectionMapper.bad6(params);
|
||||
}
|
||||
|
||||
public void bad7(List<String> params) {
|
||||
sqlInjectionMapper.bad7(params);
|
||||
}
|
||||
|
||||
public void bad8(String[] params) {
|
||||
sqlInjectionMapper.bad8(params);
|
||||
}
|
||||
|
||||
public void bad9(String name) {
|
||||
HashMap hashMap = new HashMap();
|
||||
hashMap.put("name", name);
|
||||
sqlInjectionMapper.bad9(hashMap);
|
||||
}
|
||||
|
||||
public List<Test> good1(Integer id) {
|
||||
List<Test> result = sqlInjectionMapper.good1(id);
|
||||
return result;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.HashMap;
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
import org.apache.ibatis.annotations.Param;
|
||||
import org.springframework.stereotype.Repository;
|
||||
import org.apache.ibatis.annotations.Select;
|
||||
|
||||
@Mapper
|
||||
@Repository
|
||||
public interface SqlInjectionMapper {
|
||||
|
||||
List<Test> bad1(String name);
|
||||
|
||||
List<Test> bad2(@Param("orderby") String name);
|
||||
|
||||
List<Test> bad3(Test test);
|
||||
|
||||
void bad4(@Param("test") Test test);
|
||||
|
||||
void bad5(Test test);
|
||||
|
||||
void bad6(Map<String, String> params);
|
||||
|
||||
void bad7(List<String> params);
|
||||
|
||||
void bad8(String[] params);
|
||||
|
||||
@Select({"select * from test", "where id = ${name}"})
|
||||
public Test bad9(HashMap<String, Object> map);
|
||||
|
||||
List<Test> good1(Integer id);
|
||||
}
|
||||
@@ -0,0 +1,80 @@
|
||||
<?xml version="1.0" encoding="UTF-8" ?>
|
||||
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
||||
"http://mybatis.org/dtd/mybatis-3-mapper.dtd" >
|
||||
<mapper namespace="SqlInjectionMapper">
|
||||
|
||||
<resultMap id="BaseResultMap" type="Test">
|
||||
<id column="id" jdbcType="INTEGER" property="id"/>
|
||||
<result column="name" jdbcType="VARCHAR" property="name"/>
|
||||
<result column="pass" jdbcType="VARCHAR" property="pass"/>
|
||||
</resultMap>
|
||||
|
||||
<sql id="Update_By_Example_Where_Clause">
|
||||
<where>
|
||||
<if test="test.name != null">
|
||||
and name = ${test.name,jdbcType=VARCHAR}
|
||||
</if>
|
||||
<if test="test.id != null">
|
||||
and id = #{test.id}
|
||||
</if>
|
||||
</where>
|
||||
</sql>
|
||||
|
||||
<select id="bad1" parameterType="java.lang.String" resultMap="BaseResultMap">
|
||||
select id,name from test where name like '%${name}%'
|
||||
</select>
|
||||
|
||||
<select id="bad2" resultMap="BaseResultMap">
|
||||
select id,name from test order by ${orderby,jdbcType=VARCHAR} desc
|
||||
</select>
|
||||
|
||||
<select id="bad3" parameterType="Test" resultMap="BaseResultMap">
|
||||
select id,name from test where name in ${name}
|
||||
</select>
|
||||
|
||||
<update id="bad4" parameterType="Test">
|
||||
update test
|
||||
<set>
|
||||
<if test="test.pass != null">
|
||||
pass = #{test.pass},
|
||||
</if>
|
||||
</set>
|
||||
<if test="_parameter != null">
|
||||
<include refid="Update_By_Example_Where_Clause" />
|
||||
</if>
|
||||
</update>
|
||||
|
||||
<insert id="bad5" parameterType="Test">
|
||||
insert into test (name, pass)
|
||||
<trim prefix="values (" suffix=")" suffixOverrides=",">
|
||||
<if test="name != null">
|
||||
name = ${name,jdbcType=VARCHAR},
|
||||
</if>
|
||||
<if test="pass != null">
|
||||
pass = ${pass},
|
||||
</if>
|
||||
</trim>
|
||||
</insert>
|
||||
|
||||
<select id="bad6" resultMap="BaseResultMap">
|
||||
select id,name from test where name like '%${name}%'
|
||||
</select>
|
||||
|
||||
<select id="bad7" resultMap="BaseResultMap">
|
||||
select id,name from test where name in
|
||||
<foreach collection="list" item="value" open="(" close=")" separator=",">
|
||||
${value}
|
||||
</foreach>
|
||||
</select>
|
||||
|
||||
<select id="bad8" resultMap="BaseResultMap">
|
||||
select id,name from test where name in
|
||||
<foreach collection="array" item="value" open="(" close=")" separator=",">
|
||||
${value}
|
||||
</foreach>
|
||||
</select>
|
||||
|
||||
<select id="good1" parameterType="java.lang.Integer" resultMap="BaseResultMap">
|
||||
select id,name from test where id = ${id}
|
||||
</select>
|
||||
</mapper>
|
||||
@@ -0,0 +1,43 @@
|
||||
import java.io.Serializable;
|
||||
|
||||
public class Test implements Serializable {
|
||||
|
||||
private Integer id;
|
||||
|
||||
private String name;
|
||||
|
||||
private String pass;
|
||||
|
||||
public Integer getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(Integer id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public String getPass() {
|
||||
return pass;
|
||||
}
|
||||
|
||||
public void setPass(String pass) {
|
||||
this.pass = pass;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "Test{" +
|
||||
"id=" + id +
|
||||
", name='" + name + '\'' +
|
||||
", pass='" + pass + '\'' +
|
||||
'}';
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
//semmle-extractor-options: --javac-args -cp ${testdir}/../../../../../../stubs/springframework-5.3.8/:${testdir}/../../../../../../stubs/org.mybatis-3.5.4/
|
||||
15
java/ql/test/stubs/org.mybatis-3.5.4/org/apache/ibatis/annotations/Mapper.java
generated
Normal file
15
java/ql/test/stubs/org.mybatis-3.5.4/org/apache/ibatis/annotations/Mapper.java
generated
Normal file
@@ -0,0 +1,15 @@
|
||||
package org.apache.ibatis.annotations;
|
||||
|
||||
import java.lang.annotation.Documented;
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Inherited;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
@Documented
|
||||
@Inherited
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target({ElementType.TYPE, ElementType.METHOD, ElementType.FIELD, ElementType.PARAMETER})
|
||||
public @interface Mapper {
|
||||
}
|
||||
14
java/ql/test/stubs/org.mybatis-3.5.4/org/apache/ibatis/annotations/Param.java
generated
Normal file
14
java/ql/test/stubs/org.mybatis-3.5.4/org/apache/ibatis/annotations/Param.java
generated
Normal file
@@ -0,0 +1,14 @@
|
||||
package org.apache.ibatis.annotations;
|
||||
|
||||
import java.lang.annotation.Documented;
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
@Documented
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target({ElementType.PARAMETER})
|
||||
public @interface Param {
|
||||
String value();
|
||||
}
|
||||
14
java/ql/test/stubs/org.mybatis-3.5.4/org/apache/ibatis/annotations/Select.java
generated
Normal file
14
java/ql/test/stubs/org.mybatis-3.5.4/org/apache/ibatis/annotations/Select.java
generated
Normal file
@@ -0,0 +1,14 @@
|
||||
package org.apache.ibatis.annotations;
|
||||
|
||||
import java.lang.annotation.Documented;
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
@Documented
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target({ElementType.METHOD})
|
||||
public @interface Select {
|
||||
String[] value();
|
||||
}
|
||||
19
java/ql/test/stubs/springframework-5.3.8/org/springframework/stereotype/Repository.java
generated
Normal file
19
java/ql/test/stubs/springframework-5.3.8/org/springframework/stereotype/Repository.java
generated
Normal file
@@ -0,0 +1,19 @@
|
||||
package org.springframework.stereotype;
|
||||
|
||||
import java.lang.annotation.Documented;
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
import org.springframework.core.annotation.AliasFor;
|
||||
|
||||
@Target({ElementType.TYPE})
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Documented
|
||||
@Component
|
||||
public @interface Repository {
|
||||
@AliasFor(
|
||||
annotation = Component.class
|
||||
)
|
||||
String value() default "";
|
||||
}
|
||||
19
java/ql/test/stubs/springframework-5.3.8/org/springframework/stereotype/Service.java
generated
Normal file
19
java/ql/test/stubs/springframework-5.3.8/org/springframework/stereotype/Service.java
generated
Normal file
@@ -0,0 +1,19 @@
|
||||
package org.springframework.stereotype;
|
||||
|
||||
import java.lang.annotation.Documented;
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
import org.springframework.core.annotation.AliasFor;
|
||||
|
||||
@Target({ElementType.TYPE})
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Documented
|
||||
@Component
|
||||
public @interface Service {
|
||||
@AliasFor(
|
||||
annotation = Component.class
|
||||
)
|
||||
String value() default "";
|
||||
}
|
||||
21
java/ql/test/stubs/springframework-5.3.8/org/springframework/web/bind/annotation/ModelAttribute.java
generated
Normal file
21
java/ql/test/stubs/springframework-5.3.8/org/springframework/web/bind/annotation/ModelAttribute.java
generated
Normal file
@@ -0,0 +1,21 @@
|
||||
package org.springframework.web.bind.annotation;
|
||||
|
||||
import java.lang.annotation.Documented;
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
import org.springframework.core.annotation.AliasFor;
|
||||
|
||||
@Target({ElementType.PARAMETER, ElementType.METHOD})
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Documented
|
||||
public @interface ModelAttribute {
|
||||
@AliasFor("name")
|
||||
String value() default "";
|
||||
|
||||
@AliasFor("value")
|
||||
String name() default "";
|
||||
|
||||
boolean binding() default true;
|
||||
}
|
||||
Reference in New Issue
Block a user