mirror of
https://github.com/github/codeql.git
synced 2025-12-21 11:16:30 +01:00
Modify according to suggestions
This commit is contained in:
@@ -35,9 +35,9 @@ class IbatisConfiguration extends RefType {
|
|||||||
*/
|
*/
|
||||||
class IbatisConfigurationGetVariablesMethod extends Method {
|
class IbatisConfigurationGetVariablesMethod extends Method {
|
||||||
IbatisConfigurationGetVariablesMethod() {
|
IbatisConfigurationGetVariablesMethod() {
|
||||||
getDeclaringType() instanceof IbatisConfiguration and
|
this.getDeclaringType() instanceof IbatisConfiguration and
|
||||||
hasName("getVariables") and
|
this.hasName("getVariables") and
|
||||||
getNumberOfParameters() = 0
|
this.getNumberOfParameters() = 0
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -45,114 +45,62 @@ class IbatisConfigurationGetVariablesMethod extends Method {
|
|||||||
* An annotation type that identifies Ibatis select.
|
* An annotation type that identifies Ibatis select.
|
||||||
*/
|
*/
|
||||||
private class IbatisSelectAnnotationType extends AnnotationType {
|
private class IbatisSelectAnnotationType extends AnnotationType {
|
||||||
IbatisSelectAnnotationType() {
|
IbatisSelectAnnotationType() { this.hasQualifiedName("org.apache.ibatis.annotations", "Select") }
|
||||||
this.hasQualifiedName("org.apache.ibatis.annotations", "Select") or
|
|
||||||
this.getAnAnnotation().getType() instanceof IbatisSelectAnnotationType
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* An annotation type that identifies Ibatis delete.
|
* An annotation type that identifies Ibatis delete.
|
||||||
*/
|
*/
|
||||||
private class IbatisDeleteAnnotationType extends AnnotationType {
|
private class IbatisDeleteAnnotationType extends AnnotationType {
|
||||||
IbatisDeleteAnnotationType() {
|
IbatisDeleteAnnotationType() { this.hasQualifiedName("org.apache.ibatis.annotations", "Delete") }
|
||||||
this.hasQualifiedName("org.apache.ibatis.annotations", "Delete") or
|
|
||||||
this.getAnAnnotation().getType() instanceof IbatisDeleteAnnotationType
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* An annotation type that identifies Ibatis insert.
|
* An annotation type that identifies Ibatis insert.
|
||||||
*/
|
*/
|
||||||
private class IbatisInsertAnnotationType extends AnnotationType {
|
private class IbatisInsertAnnotationType extends AnnotationType {
|
||||||
IbatisInsertAnnotationType() {
|
IbatisInsertAnnotationType() { this.hasQualifiedName("org.apache.ibatis.annotations", "Insert") }
|
||||||
this.hasQualifiedName("org.apache.ibatis.annotations", "Insert") or
|
|
||||||
this.getAnAnnotation().getType() instanceof IbatisInsertAnnotationType
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* An annotation type that identifies Ibatis update.
|
* An annotation type that identifies Ibatis update.
|
||||||
*/
|
*/
|
||||||
private class IbatisUpdateAnnotationType extends AnnotationType {
|
private class IbatisUpdateAnnotationType extends AnnotationType {
|
||||||
IbatisUpdateAnnotationType() {
|
IbatisUpdateAnnotationType() { this.hasQualifiedName("org.apache.ibatis.annotations", "Update") }
|
||||||
this.hasQualifiedName("org.apache.ibatis.annotations", "Update") or
|
|
||||||
this.getAnAnnotation().getType() instanceof IbatisUpdateAnnotationType
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Ibatis sql operation annotation.
|
* Ibatis sql operation annotation.
|
||||||
*/
|
*/
|
||||||
abstract class IbatisSqlOperationAnnotation extends Annotation {
|
class IbatisSqlOperationAnnotation extends Annotation {
|
||||||
abstract string getSqlValue();
|
IbatisSqlOperationAnnotation() {
|
||||||
}
|
this.getType() instanceof IbatisSelectAnnotationType or
|
||||||
|
this.getType() instanceof IbatisDeleteAnnotationType or
|
||||||
|
this.getType() instanceof IbatisInsertAnnotationType or
|
||||||
|
this.getType() instanceof IbatisUpdateAnnotationType
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A `@org.apache.ibatis.annotations.Select` annotation.
|
* Get the SQL statement string.
|
||||||
*/
|
*/
|
||||||
private class IbatisSelectAnnotation extends IbatisSqlOperationAnnotation {
|
string getSqlValue() {
|
||||||
IbatisSelectAnnotation() { this.getType() instanceof IbatisSelectAnnotationType }
|
|
||||||
|
|
||||||
string getSelectValue() {
|
|
||||||
result = this.getValue("value").(CompileTimeConstantExpr).getStringValue() or
|
result = this.getValue("value").(CompileTimeConstantExpr).getStringValue() or
|
||||||
result =
|
result =
|
||||||
this.getValue("value").(ArrayInit).getInit(_).(CompileTimeConstantExpr).getStringValue()
|
this.getValue("value").(ArrayInit).getInit(_).(CompileTimeConstantExpr).getStringValue()
|
||||||
}
|
}
|
||||||
|
|
||||||
override string getSqlValue() { result = getSelectValue() }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A `@org.apache.ibatis.annotations.Delete` annotation.
|
* 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`.
|
||||||
*/
|
*/
|
||||||
private class IbatisDeleteAnnotation extends IbatisSqlOperationAnnotation {
|
class MyBatisSqlOperationAnnotationMethod extends Method {
|
||||||
IbatisDeleteAnnotation() { this.getType() instanceof IbatisDeleteAnnotationType }
|
MyBatisSqlOperationAnnotationMethod() {
|
||||||
|
this.getAnAnnotation() instanceof IbatisSqlOperationAnnotation
|
||||||
string getDeleteValue() {
|
|
||||||
result = this.getValue("value").(CompileTimeConstantExpr).getStringValue() or
|
|
||||||
result =
|
|
||||||
this.getValue("value").(ArrayInit).getInit(_).(CompileTimeConstantExpr).getStringValue()
|
|
||||||
}
|
|
||||||
|
|
||||||
override string getSqlValue() { result = getDeleteValue() }
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A `@org.apache.ibatis.annotations.Insert` annotation.
|
|
||||||
*/
|
|
||||||
private class IbatisInsertAnnotation extends IbatisSqlOperationAnnotation {
|
|
||||||
IbatisInsertAnnotation() { this.getType() instanceof IbatisInsertAnnotationType }
|
|
||||||
|
|
||||||
string getInsertValue() {
|
|
||||||
result = this.getValue("value").(CompileTimeConstantExpr).getStringValue() or
|
|
||||||
result =
|
|
||||||
this.getValue("value").(ArrayInit).getInit(_).(CompileTimeConstantExpr).getStringValue()
|
|
||||||
}
|
|
||||||
|
|
||||||
override string getSqlValue() { result = getInsertValue() }
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A `@org.apache.ibatis.annotations.Update` annotation.
|
|
||||||
*/
|
|
||||||
private class IbatisUpdateAnnotation extends IbatisSqlOperationAnnotation {
|
|
||||||
IbatisUpdateAnnotation() { this.getType() instanceof IbatisUpdateAnnotationType }
|
|
||||||
|
|
||||||
string getUpdateValue() {
|
|
||||||
result = this.getValue("value").(CompileTimeConstantExpr).getStringValue() or
|
|
||||||
result =
|
|
||||||
this.getValue("value").(ArrayInit).getInit(_).(CompileTimeConstantExpr).getStringValue()
|
|
||||||
}
|
|
||||||
|
|
||||||
override string getSqlValue() { result = getUpdateValue() }
|
|
||||||
}
|
|
||||||
|
|
||||||
// Mybatis uses sql operation to annotate the method of interacting with the database.
|
|
||||||
class MybatisSqlOperationAnnotationMethod extends Method {
|
|
||||||
MybatisSqlOperationAnnotationMethod() {
|
|
||||||
exists(IbatisSqlOperationAnnotation isoa |
|
|
||||||
this.getAnAnnotation() = isoa
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** The interface `org.apache.ibatis.annotations.Param`. */
|
||||||
|
class TypeParam extends Interface {
|
||||||
|
TypeParam() { this.hasQualifiedName("org.apache.ibatis.annotations", "Param") }
|
||||||
|
}
|
||||||
|
|||||||
@@ -4,23 +4,21 @@
|
|||||||
"qhelp.dtd">
|
"qhelp.dtd">
|
||||||
<qhelp>
|
<qhelp>
|
||||||
<overview>
|
<overview>
|
||||||
<p>MyBatis operates the database by using @Select, @Insert, etc. annotations in the method, and can use the $ character
|
<p>MyBatis allows operating the database by annotating a method with the annotations <code>@Select</code>, <code>@Insert</code>, etc. to construct dynamic SQL statements.
|
||||||
to construct dynamic SQL statements. Attackers can modify the meaning of statements or execute arbitrary SQL commands.</p>
|
If the syntax `${param}` is used in those statements, and `param` is a parameter of the annotated method, attackers can exploit this to tamper with the SQL statements or execute arbitrary SQL commands.</p>
|
||||||
</overview>
|
</overview>
|
||||||
|
|
||||||
<<recommendation>
|
<<recommendation>
|
||||||
<p>
|
<p>
|
||||||
When writing MyBatis mapping statements, try to use the format "#{xxx}". If you have to use parameters
|
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.
|
||||||
such as "${xxx}", you must manually filter to prevent SQL injection attacks.
|
|
||||||
</p>
|
</p>
|
||||||
</recommendation>
|
</recommendation>
|
||||||
|
|
||||||
<example>
|
<example>
|
||||||
<p>
|
<p>
|
||||||
The following examples show the bad situation and the good situation respectively. The <code>bad1</code> method uses <code>$(name)</code>
|
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 splice SQL statements, and there is a SQL injection vulnerability.
|
in the <code>@Select</code> annotation to dynamically build a SQL statement, which causes a SQL injection vulnerability.
|
||||||
The good1 method uses the <code>#{name}</code> method in the <code>@Select</code> annotation to splice SQL statements,
|
The <code>good1</code> method uses <code>#{name}</code> in the <code>@Select</code> annotation to to dynamically include the parameter in a SQL statement, which allows the MyBatis framework to handle the sanitization, preventing the vulnerability.
|
||||||
and the MyBatis framework will handle the dangerous characters entered by the user, And did not cause SQL injection vulnerabilities.
|
|
||||||
</p>
|
</p>
|
||||||
<sample src="MyBatisAnnotationSqlInjection.java" />
|
<sample src="MyBatisAnnotationSqlInjection.java" />
|
||||||
</example>
|
</example>
|
||||||
|
|||||||
@@ -1,12 +1,12 @@
|
|||||||
/**
|
/**
|
||||||
* @name MyBatis annotation sql injection
|
* @name SQL injection in MyBatis annotation
|
||||||
* @description Constructing a dynamic SQL statement with input that comes from an
|
* @description Constructing a dynamic SQL statement with input that comes from an
|
||||||
* untrusted source could allow an attacker to modify the statement's
|
* untrusted source could allow an attacker to modify the statement's
|
||||||
* meaning or to execute arbitrary SQL commands.
|
* meaning or to execute arbitrary SQL commands.
|
||||||
* @kind path-problem
|
* @kind path-problem
|
||||||
* @problem.severity error
|
* @problem.severity error
|
||||||
* @precision high
|
* @precision high
|
||||||
* @id java/sql-injection
|
* @id java/mybatis-annotation-sql-injection
|
||||||
* @tags security
|
* @tags security
|
||||||
* external/cwe/cwe-089
|
* external/cwe/cwe-089
|
||||||
*/
|
*/
|
||||||
@@ -14,7 +14,6 @@
|
|||||||
import java
|
import java
|
||||||
import DataFlow::PathGraph
|
import DataFlow::PathGraph
|
||||||
import MyBatisAnnotationSqlInjectionLib
|
import MyBatisAnnotationSqlInjectionLib
|
||||||
import semmle.code.java.security.SanitizerGuard
|
|
||||||
import semmle.code.java.dataflow.FlowSources
|
import semmle.code.java.dataflow.FlowSources
|
||||||
|
|
||||||
private class MyBatisAnnotationSqlInjectionConfiguration extends TaintTracking::Configuration {
|
private class MyBatisAnnotationSqlInjectionConfiguration extends TaintTracking::Configuration {
|
||||||
@@ -32,10 +31,6 @@ private class MyBatisAnnotationSqlInjectionConfiguration extends TaintTracking::
|
|||||||
node.getType() instanceof NumberType
|
node.getType() instanceof NumberType
|
||||||
}
|
}
|
||||||
|
|
||||||
override predicate isSanitizerGuard(DataFlow::BarrierGuard guard) {
|
|
||||||
guard instanceof ContainsSanitizer or guard instanceof EqualsSanitizer
|
|
||||||
}
|
|
||||||
|
|
||||||
override predicate isAdditionalTaintStep(DataFlow::Node node1, DataFlow::Node node2) {
|
override predicate isAdditionalTaintStep(DataFlow::Node node1, DataFlow::Node node2) {
|
||||||
exists(MethodAccess ma |
|
exists(MethodAccess ma |
|
||||||
ma.getMethod().getDeclaringType() instanceof MapType and
|
ma.getMethod().getDeclaringType() instanceof MapType and
|
||||||
@@ -60,5 +55,5 @@ where
|
|||||||
cfg.hasFlowPath(source, sink) and
|
cfg.hasFlowPath(source, sink) and
|
||||||
isMybatisAnnotationSqlInjection(sink.getNode(), isoa)
|
isMybatisAnnotationSqlInjection(sink.getNode(), isoa)
|
||||||
select sink.getNode(), source, sink,
|
select sink.getNode(), source, sink,
|
||||||
"MyBatis annotation sql injection might include code from $@ to $@.", source.getNode(),
|
"MyBatis annotation SQL injection might include code from $@ to $@.", source.getNode(),
|
||||||
"this user input", isoa, "this sql operation"
|
"this user input", isoa, "this SQL operation"
|
||||||
|
|||||||
@@ -1,3 +1,7 @@
|
|||||||
|
/**
|
||||||
|
* Provides classes for SQL injection detection in MyBatis annotation.
|
||||||
|
*/
|
||||||
|
|
||||||
import java
|
import java
|
||||||
import MyBatisCommonLib
|
import MyBatisCommonLib
|
||||||
import semmle.code.xml.MyBatisMapperXML
|
import semmle.code.xml.MyBatisMapperXML
|
||||||
@@ -7,7 +11,7 @@ import semmle.code.java.frameworks.Properties
|
|||||||
/** A sink for MyBatis annotation method call an argument. */
|
/** A sink for MyBatis annotation method call an argument. */
|
||||||
class MyBatisAnnotationMethodCallAnArgument extends DataFlow::Node {
|
class MyBatisAnnotationMethodCallAnArgument extends DataFlow::Node {
|
||||||
MyBatisAnnotationMethodCallAnArgument() {
|
MyBatisAnnotationMethodCallAnArgument() {
|
||||||
exists(MybatisSqlOperationAnnotationMethod msoam, MethodAccess ma | ma.getMethod() = msoam |
|
exists(MyBatisSqlOperationAnnotationMethod msoam, MethodAccess ma | ma.getMethod() = msoam |
|
||||||
ma.getAnArgument() = this.asExpr()
|
ma.getAnArgument() = this.asExpr()
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@@ -15,7 +19,7 @@ class MyBatisAnnotationMethodCallAnArgument extends DataFlow::Node {
|
|||||||
|
|
||||||
/** Get the #{...} or ${...} parameters in the Mybatis annotation value. */
|
/** Get the #{...} or ${...} parameters in the Mybatis annotation value. */
|
||||||
private string getAnMybatiAnnotationSetValue(IbatisSqlOperationAnnotation isoa) {
|
private string getAnMybatiAnnotationSetValue(IbatisSqlOperationAnnotation isoa) {
|
||||||
result = isoa.getSqlValue().trim().regexpFind("(#|\\$)(\\{([^\\}]*\\}))", _, _)
|
result = isoa.getSqlValue().trim().regexpFind("(#|\\$)\\{[^\\}]*\\}", _, _)
|
||||||
}
|
}
|
||||||
|
|
||||||
predicate isMybatisAnnotationSqlInjection(DataFlow::Node node, IbatisSqlOperationAnnotation isoa) {
|
predicate isMybatisAnnotationSqlInjection(DataFlow::Node node, IbatisSqlOperationAnnotation isoa) {
|
||||||
@@ -27,15 +31,15 @@ predicate isMybatisAnnotationSqlInjection(DataFlow::Node node, IbatisSqlOperatio
|
|||||||
// @Select(select id,name from test where name like '%${value}%')
|
// @Select(select id,name from test where name like '%${value}%')
|
||||||
// Test test(String name);
|
// Test test(String name);
|
||||||
// ```
|
// ```
|
||||||
exists(MybatisSqlOperationAnnotationMethod msoam, MethodAccess ma, string res |
|
exists(MyBatisSqlOperationAnnotationMethod msoam, MethodAccess ma, string res |
|
||||||
msoam = ma.getMethod()
|
msoam = ma.getMethod()
|
||||||
|
|
|
|
||||||
msoam.getAnAnnotation() = isoa and
|
msoam.getAnAnnotation() = isoa and
|
||||||
res = getAnMybatiAnnotationSetValue(isoa) and
|
res = getAnMybatiAnnotationSetValue(isoa) and
|
||||||
msoam.getNumberOfParameters() = 1 and
|
msoam.getNumberOfParameters() = 1 and
|
||||||
not ma.getMethod().getAParameter().hasAnnotation() and
|
not ma.getMethod().getAParameter().getAnAnnotation().getType() instanceof TypeParam and
|
||||||
res.matches("%${%}") and
|
res.matches("%${%}") and
|
||||||
not res.matches("${" + getAnMybatisConfigurationVariableKey() + "}") and
|
not res = "${" + getAnMybatisConfigurationVariableKey() + "}" and
|
||||||
ma.getAnArgument() = node.asExpr()
|
ma.getAnArgument() = node.asExpr()
|
||||||
)
|
)
|
||||||
or
|
or
|
||||||
@@ -47,11 +51,11 @@ predicate isMybatisAnnotationSqlInjection(DataFlow::Node node, IbatisSqlOperatio
|
|||||||
// @Select(select id,name from test where name like '%${value}%')
|
// @Select(select id,name from test where name like '%${value}%')
|
||||||
// Test test(Map map);
|
// Test test(Map map);
|
||||||
// ```
|
// ```
|
||||||
exists(MybatisSqlOperationAnnotationMethod msoam, MethodAccess ma, int i, string res |
|
exists(MyBatisSqlOperationAnnotationMethod msoam, MethodAccess ma, int i, string res |
|
||||||
msoam = ma.getMethod()
|
msoam = ma.getMethod()
|
||||||
|
|
|
|
||||||
msoam.getAnAnnotation() = isoa and
|
msoam.getAnAnnotation() = isoa and
|
||||||
not ma.getMethod().getParameter(i).hasAnnotation() and
|
not ma.getMethod().getParameter(i).getAnAnnotation().getType() instanceof TypeParam and
|
||||||
(
|
(
|
||||||
ma.getMethod().getParameterType(i) instanceof MapType or
|
ma.getMethod().getParameterType(i) instanceof MapType or
|
||||||
ma.getMethod().getParameterType(i) instanceof ListType or
|
ma.getMethod().getParameterType(i) instanceof ListType or
|
||||||
@@ -59,7 +63,7 @@ predicate isMybatisAnnotationSqlInjection(DataFlow::Node node, IbatisSqlOperatio
|
|||||||
) and
|
) and
|
||||||
res = getAnMybatiAnnotationSetValue(isoa) and
|
res = getAnMybatiAnnotationSetValue(isoa) and
|
||||||
res.matches("%${%}") and
|
res.matches("%${%}") and
|
||||||
not res.matches("${" + getAnMybatisConfigurationVariableKey() + "}") and
|
not res = "${" + getAnMybatisConfigurationVariableKey() + "}" and
|
||||||
ma.getArgument(i) = node.asExpr()
|
ma.getArgument(i) = node.asExpr()
|
||||||
)
|
)
|
||||||
or
|
or
|
||||||
@@ -71,13 +75,13 @@ predicate isMybatisAnnotationSqlInjection(DataFlow::Node node, IbatisSqlOperatio
|
|||||||
// @Select(select id,name from test order by ${name,jdbcType=VARCHAR})
|
// @Select(select id,name from test order by ${name,jdbcType=VARCHAR})
|
||||||
// void test(Test test);
|
// void test(Test test);
|
||||||
// ```
|
// ```
|
||||||
exists(MybatisSqlOperationAnnotationMethod msoam, MethodAccess ma, int i, Class c |
|
exists(MyBatisSqlOperationAnnotationMethod msoam, MethodAccess ma, int i, RefType t |
|
||||||
msoam = ma.getMethod()
|
msoam = ma.getMethod()
|
||||||
|
|
|
|
||||||
msoam.getAnAnnotation() = isoa and
|
msoam.getAnAnnotation() = isoa and
|
||||||
not ma.getMethod().getParameter(i).hasAnnotation() and
|
not ma.getMethod().getParameter(i).getAnAnnotation().getType() instanceof TypeParam and
|
||||||
ma.getMethod().getParameterType(i).getName() = c.getName() and
|
ma.getMethod().getParameterType(i).getName() = t.getName() and
|
||||||
getAnMybatiAnnotationSetValue(isoa).matches("%${" + c.getAField().getName() + "%}") and
|
getAnMybatiAnnotationSetValue(isoa).matches("%${" + t.getAField().getName() + "%}") and
|
||||||
ma.getArgument(i) = node.asExpr()
|
ma.getArgument(i) = node.asExpr()
|
||||||
)
|
)
|
||||||
or
|
or
|
||||||
@@ -89,12 +93,10 @@ predicate isMybatisAnnotationSqlInjection(DataFlow::Node node, IbatisSqlOperatio
|
|||||||
// @Select(select id,name from test order by ${orderby,jdbcType=VARCHAR})
|
// @Select(select id,name from test order by ${orderby,jdbcType=VARCHAR})
|
||||||
// void test(@Param("orderby") String name);
|
// void test(@Param("orderby") String name);
|
||||||
// ```
|
// ```
|
||||||
exists(MybatisSqlOperationAnnotationMethod msoam, MethodAccess ma, int i, Annotation annotation |
|
exists(MyBatisSqlOperationAnnotationMethod msoam, MethodAccess ma, int i, Annotation annotation |
|
||||||
msoam = ma.getMethod()
|
msoam = ma.getMethod() and ma.getMethod().getParameter(i).getAnAnnotation() = annotation
|
||||||
|
|
|
|
||||||
msoam.getAnAnnotation() = isoa and
|
msoam.getAnAnnotation() = isoa and
|
||||||
ma.getMethod().getParameter(i).hasAnnotation() and
|
|
||||||
ma.getMethod().getParameter(i).getAnAnnotation() = annotation and
|
|
||||||
annotation.getType() instanceof TypeParam and
|
annotation.getType() instanceof TypeParam and
|
||||||
getAnMybatiAnnotationSetValue(isoa)
|
getAnMybatiAnnotationSetValue(isoa)
|
||||||
.matches("%${" + annotation.getValue("value").(CompileTimeConstantExpr).getStringValue() +
|
.matches("%${" + annotation.getValue("value").(CompileTimeConstantExpr).getStringValue() +
|
||||||
@@ -109,18 +111,18 @@ predicate isMybatisAnnotationSqlInjection(DataFlow::Node node, IbatisSqlOperatio
|
|||||||
// @Select(select id,name from test order by ${arg0,jdbcType=VARCHAR})
|
// @Select(select id,name from test order by ${arg0,jdbcType=VARCHAR})
|
||||||
// void test(String name);
|
// void test(String name);
|
||||||
// ```
|
// ```
|
||||||
exists(MybatisSqlOperationAnnotationMethod msoam, MethodAccess ma, int i, string res |
|
exists(MyBatisSqlOperationAnnotationMethod msoam, MethodAccess ma, int i, string res |
|
||||||
msoam = ma.getMethod()
|
msoam = ma.getMethod()
|
||||||
|
|
|
|
||||||
msoam.getAnAnnotation() = isoa and
|
msoam.getAnAnnotation() = isoa and
|
||||||
not ma.getMethod().getParameter(i).hasAnnotation() and
|
not ma.getMethod().getParameter(i).getAnAnnotation().getType() instanceof TypeParam and
|
||||||
res = getAnMybatiAnnotationSetValue(isoa) and
|
res = getAnMybatiAnnotationSetValue(isoa) and
|
||||||
(
|
(
|
||||||
res.matches("%${param" + (i + 1) + "%}")
|
res.matches("%${param" + (i + 1) + "%}")
|
||||||
or
|
or
|
||||||
res.matches("%${arg" + i + "%}")
|
res.matches("%${arg" + i + "%}")
|
||||||
) and
|
) and
|
||||||
not res.matches("${" + getAnMybatisConfigurationVariableKey() + "}") and
|
not res = "${" + getAnMybatisConfigurationVariableKey() + "}" and
|
||||||
ma.getArgument(i) = node.asExpr()
|
ma.getArgument(i) = node.asExpr()
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,3 +1,7 @@
|
|||||||
|
/**
|
||||||
|
* Provides public classes for MyBatis SQL injection detection.
|
||||||
|
*/
|
||||||
|
|
||||||
import java
|
import java
|
||||||
import semmle.code.java.dataflow.FlowSources
|
import semmle.code.java.dataflow.FlowSources
|
||||||
import semmle.code.java.frameworks.MyBatis
|
import semmle.code.java.frameworks.MyBatis
|
||||||
@@ -28,15 +32,10 @@ private class PropertiesFlowConfig extends DataFlow2::Configuration {
|
|||||||
string getAnMybatisConfigurationVariableKey() {
|
string getAnMybatisConfigurationVariableKey() {
|
||||||
exists(PropertiesFlowConfig conf, DataFlow::Node n |
|
exists(PropertiesFlowConfig conf, DataFlow::Node n |
|
||||||
propertiesKey(n, result) and
|
propertiesKey(n, result) and
|
||||||
conf.hasFlow(_, n)
|
conf.hasFlowTo(n)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
/** The interface `org.apache.ibatis.annotations.Param`. */
|
|
||||||
class TypeParam extends Interface {
|
|
||||||
TypeParam() { this.hasQualifiedName("org.apache.ibatis.annotations", "Param") }
|
|
||||||
}
|
|
||||||
|
|
||||||
/** A reference type that extends a parameterization of `java.util.List`. */
|
/** A reference type that extends a parameterization of `java.util.List`. */
|
||||||
class ListType extends RefType {
|
class ListType extends RefType {
|
||||||
ListType() {
|
ListType() {
|
||||||
|
|||||||
@@ -9,8 +9,7 @@
|
|||||||
|
|
||||||
<<recommendation>
|
<<recommendation>
|
||||||
<p>
|
<p>
|
||||||
When writing MyBatis mapping statements, try to use the format "#{xxx}". If you have to use parameters
|
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.
|
||||||
such as "${xxx}", you must manually filter to prevent SQL injection attacks.
|
|
||||||
</p>
|
</p>
|
||||||
</recommendation>
|
</recommendation>
|
||||||
|
|
||||||
@@ -18,7 +17,7 @@ such as "${xxx}", you must manually filter to prevent SQL injection attacks.
|
|||||||
<p>
|
<p>
|
||||||
The following examples show the bad situation and the good situation respectively. In <code>bad1</code>
|
The following examples show the bad situation and the good situation respectively. In <code>bad1</code>
|
||||||
and <code>bad2</code> and <code>bad3</code> and <code>bad4</code> and <code >bad5</code>, the program
|
and <code>bad2</code> and <code>bad3</code> and <code>bad4</code> and <code >bad5</code>, the program
|
||||||
${ xxx} are dynamic SQL statements, these five examples of SQL injection vulnerabilities. In <code>good1</code>,
|
${xxx} are dynamic SQL statements, these five examples of SQL injection vulnerabilities. In <code>good1</code>,
|
||||||
the program uses the ${xxx} dynamic feature SQL statement, but there are subtle restrictions on the data,
|
the program uses the ${xxx} dynamic feature SQL statement, but there are subtle restrictions on the data,
|
||||||
and there is no SQL injection vulnerability.
|
and there is no SQL injection vulnerability.
|
||||||
</p>
|
</p>
|
||||||
|
|||||||
@@ -1,12 +1,12 @@
|
|||||||
/**
|
/**
|
||||||
* @name MyBatis Mapper xml sql injection
|
* @name SQL injection in MyBatis Mapper XML
|
||||||
* @description Constructing a dynamic SQL statement with input that comes from an
|
* @description Constructing a dynamic SQL statement with input that comes from an
|
||||||
* untrusted source could allow an attacker to modify the statement's
|
* untrusted source could allow an attacker to modify the statement's
|
||||||
* meaning or to execute arbitrary SQL commands.
|
* meaning or to execute arbitrary SQL commands.
|
||||||
* @kind path-problem
|
* @kind path-problem
|
||||||
* @problem.severity error
|
* @problem.severity error
|
||||||
* @precision high
|
* @precision high
|
||||||
* @id java/sql-injection
|
* @id java/mybatis-xml-sql-injection
|
||||||
* @tags security
|
* @tags security
|
||||||
* external/cwe/cwe-089
|
* external/cwe/cwe-089
|
||||||
*/
|
*/
|
||||||
@@ -15,7 +15,6 @@ import java
|
|||||||
import DataFlow::PathGraph
|
import DataFlow::PathGraph
|
||||||
import MyBatisMapperXmlSqlInjectionLib
|
import MyBatisMapperXmlSqlInjectionLib
|
||||||
import semmle.code.xml.MyBatisMapperXML
|
import semmle.code.xml.MyBatisMapperXML
|
||||||
import semmle.code.java.security.SanitizerGuard
|
|
||||||
import semmle.code.java.dataflow.FlowSources
|
import semmle.code.java.dataflow.FlowSources
|
||||||
|
|
||||||
private class MyBatisMapperXmlSqlInjectionConfiguration extends TaintTracking::Configuration {
|
private class MyBatisMapperXmlSqlInjectionConfiguration extends TaintTracking::Configuration {
|
||||||
@@ -33,10 +32,6 @@ private class MyBatisMapperXmlSqlInjectionConfiguration extends TaintTracking::C
|
|||||||
node.getType() instanceof NumberType
|
node.getType() instanceof NumberType
|
||||||
}
|
}
|
||||||
|
|
||||||
override predicate isSanitizerGuard(DataFlow::BarrierGuard guard) {
|
|
||||||
guard instanceof ContainsSanitizer or guard instanceof EqualsSanitizer
|
|
||||||
}
|
|
||||||
|
|
||||||
override predicate isAdditionalTaintStep(DataFlow::Node node1, DataFlow::Node node2) {
|
override predicate isAdditionalTaintStep(DataFlow::Node node1, DataFlow::Node node2) {
|
||||||
exists(MethodAccess ma |
|
exists(MethodAccess ma |
|
||||||
ma.getMethod().getDeclaringType() instanceof MapType and
|
ma.getMethod().getDeclaringType() instanceof MapType and
|
||||||
@@ -61,5 +56,5 @@ where
|
|||||||
cfg.hasFlowPath(source, sink) and
|
cfg.hasFlowPath(source, sink) and
|
||||||
isMapperXmlSqlInjection(sink.getNode(), xmle)
|
isMapperXmlSqlInjection(sink.getNode(), xmle)
|
||||||
select sink.getNode(), source, sink,
|
select sink.getNode(), source, sink,
|
||||||
"MyBatis Mapper XML sql injection might include code from $@ to $@.", source.getNode(),
|
"MyBatis Mapper XML SQL injection might include code from $@ to $@.", source.getNode(),
|
||||||
"this user input", xmle, "this sql operation"
|
"this user input", xmle, "this SQL operation"
|
||||||
|
|||||||
@@ -1,3 +1,7 @@
|
|||||||
|
/**
|
||||||
|
* Provide classes for SQL injection detection in MyBatis Mapper XML.
|
||||||
|
*/
|
||||||
|
|
||||||
import java
|
import java
|
||||||
import MyBatisCommonLib
|
import MyBatisCommonLib
|
||||||
import semmle.code.xml.MyBatisMapperXML
|
import semmle.code.xml.MyBatisMapperXML
|
||||||
@@ -17,116 +21,120 @@ class MyBatisMapperMethodCallAnArgument extends DataFlow::Node {
|
|||||||
|
|
||||||
/** Get the #{...} or ${...} parameters in the Mybatis mapper xml file */
|
/** Get the #{...} or ${...} parameters in the Mybatis mapper xml file */
|
||||||
private string getAnMybatiXmlSetValue(XMLElement xmle) {
|
private string getAnMybatiXmlSetValue(XMLElement xmle) {
|
||||||
result = xmle.getTextValue().trim().regexpFind("(#|\\$)(\\{([^\\}]*\\}))", _, _)
|
result = xmle.getTextValue().trim().regexpFind("(#|\\$)\\{[^\\}]*\\}", _, _)
|
||||||
}
|
}
|
||||||
|
|
||||||
predicate isMapperXmlSqlInjection(DataFlow::Node node, XMLElement xmle) {
|
predicate isMapperXmlSqlInjection(DataFlow::Node node, XMLElement xmle) {
|
||||||
// MyBatis Mapper method Param Annotation sql injection vulnerabilities.
|
// MyBatis Mapper method Param Annotation sql injection vulnerabilities.
|
||||||
// e.g. MyBatis Mapper method: `void test(@Param("orderby") String name);` and MyBatis Mapper XML file:`select id,name from test order by ${orderby,jdbcType=VARCHAR}`
|
// e.g. MyBatis Mapper method: `void test(@Param("orderby") String name);` and MyBatis Mapper XML file:`select id,name from test order by ${orderby,jdbcType=VARCHAR}`
|
||||||
exists(
|
exists(MyBatisMapperSqlOperation mbmxe, MethodAccess ma, int i, Annotation annotation |
|
||||||
MyBatisMapperSqlOperation mbmxe, MyBatisMapperSql mbms, MethodAccess mc, int i,
|
mbmxe.getMapperMethod() = ma.getMethod()
|
||||||
Annotation annotation
|
|
||||||
|
|
|
||||||
mbmxe.getMapperMethod() = mc.getMethod()
|
|
||||||
|
|
|
|
||||||
(
|
(
|
||||||
mbmxe.getAChild*() = xmle
|
mbmxe.getAChild*() = xmle
|
||||||
or
|
or
|
||||||
mbmxe.getInclude().getRefid() = mbms.getId() and
|
exists(MyBatisMapperSql mbms |
|
||||||
mbms.getAChild*() = xmle
|
mbmxe.getInclude().getRefid() = mbms.getId() and
|
||||||
|
mbms.getAChild*() = xmle
|
||||||
|
)
|
||||||
) and
|
) and
|
||||||
mc.getMethod().getParameter(i).hasAnnotation() and
|
ma.getMethod().getParameter(i).getAnAnnotation() = annotation and
|
||||||
mc.getMethod().getParameter(i).getAnAnnotation() = annotation and
|
|
||||||
annotation.getType() instanceof TypeParam and
|
annotation.getType() instanceof TypeParam and
|
||||||
getAnMybatiXmlSetValue(xmle)
|
getAnMybatiXmlSetValue(xmle)
|
||||||
.matches("%${" + annotation.getValue("value").(CompileTimeConstantExpr).getStringValue() +
|
.matches("%${" + annotation.getValue("value").(CompileTimeConstantExpr).getStringValue() +
|
||||||
"%}") and
|
"%}") and
|
||||||
mc.getArgument(i) = node.asExpr()
|
ma.getArgument(i) = node.asExpr()
|
||||||
)
|
)
|
||||||
or
|
or
|
||||||
// MyBatis Mapper method Class Field sql injection vulnerabilities.
|
// MyBatis Mapper method Class Field sql injection vulnerabilities.
|
||||||
// e.g. MyBatis Mapper method: `void test(Test test);` and MyBatis Mapper XML file:`select id,name from test order by ${name,jdbcType=VARCHAR}`
|
// e.g. MyBatis Mapper method: `void test(Test test);` and MyBatis Mapper XML file:`select id,name from test order by ${name,jdbcType=VARCHAR}`
|
||||||
exists(MyBatisMapperSqlOperation mbmxe, MyBatisMapperSql mbms, MethodAccess mc, int i, Class c |
|
exists(MyBatisMapperSqlOperation mbmxe, MethodAccess ma, int i, RefType t |
|
||||||
mbmxe.getMapperMethod() = mc.getMethod()
|
mbmxe.getMapperMethod() = ma.getMethod()
|
||||||
|
|
|
|
||||||
(
|
(
|
||||||
mbmxe.getAChild*() = xmle
|
mbmxe.getAChild*() = xmle
|
||||||
or
|
or
|
||||||
mbmxe.getInclude().getRefid() = mbms.getId() and
|
exists(MyBatisMapperSql mbms |
|
||||||
mbms.getAChild*() = xmle
|
mbmxe.getInclude().getRefid() = mbms.getId() and
|
||||||
|
mbms.getAChild*() = xmle
|
||||||
|
)
|
||||||
) and
|
) and
|
||||||
not mc.getMethod().getParameter(i).hasAnnotation() and
|
not ma.getMethod().getParameter(i).getAnAnnotation().getType() instanceof TypeParam and
|
||||||
mc.getMethod().getParameterType(i).getName() = c.getName() and
|
ma.getMethod().getParameterType(i).getName() = t.getName() and
|
||||||
getAnMybatiXmlSetValue(xmle).matches("%${" + c.getAField().getName() + "%}") and
|
getAnMybatiXmlSetValue(xmle).matches("%${" + t.getAField().getName() + "%}") and
|
||||||
mc.getArgument(i) = node.asExpr()
|
ma.getArgument(i) = node.asExpr()
|
||||||
)
|
)
|
||||||
or
|
or
|
||||||
// The parameter type of MyBatis Mapper method is Map or List or Array, which may cause SQL injection vulnerability.
|
// The parameter type of MyBatis Mapper method is Map or List or Array, which may cause SQL injection vulnerability.
|
||||||
// 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}%'`
|
// 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}%'`
|
||||||
exists(
|
exists(
|
||||||
MyBatisMapperSqlOperation mbmxe, MyBatisMapperForeach mbmf, MyBatisMapperSql mbms,
|
MyBatisMapperSqlOperation mbmxe, MyBatisMapperForeach mbmf, MethodAccess ma, int i, string res
|
||||||
MethodAccess mc, int i, string res
|
|
||||||
|
|
|
|
||||||
mbmxe.getMapperMethod() = mc.getMethod()
|
mbmxe.getMapperMethod() = ma.getMethod()
|
||||||
|
|
|
|
||||||
mbmf = xmle and
|
mbmf = xmle and
|
||||||
(
|
(
|
||||||
mbmxe.getAChild*() = xmle
|
mbmxe.getAChild*() = xmle
|
||||||
or
|
or
|
||||||
mbmxe.getInclude().getRefid() = mbms.getId() and
|
exists(MyBatisMapperSql mbms |
|
||||||
mbms.getAChild*() = xmle
|
mbmxe.getInclude().getRefid() = mbms.getId() and
|
||||||
|
mbms.getAChild*() = xmle
|
||||||
|
)
|
||||||
) and
|
) and
|
||||||
not mc.getMethod().getParameter(i).hasAnnotation() and
|
not ma.getMethod().getParameter(i).getAnAnnotation().getType() instanceof TypeParam and
|
||||||
(
|
(
|
||||||
mc.getMethod().getParameterType(i) instanceof MapType or
|
ma.getMethod().getParameterType(i) instanceof MapType or
|
||||||
mc.getMethod().getParameterType(i) instanceof ListType or
|
ma.getMethod().getParameterType(i) instanceof ListType or
|
||||||
mc.getMethod().getParameterType(i) instanceof Array
|
ma.getMethod().getParameterType(i) instanceof Array
|
||||||
) and
|
) and
|
||||||
res = getAnMybatiXmlSetValue(xmle) and
|
res = getAnMybatiXmlSetValue(xmle) and
|
||||||
res.matches("%${%}") and
|
res.matches("%${%}") and
|
||||||
not res.matches("${" + getAnMybatisConfigurationVariableKey() + "}") and
|
not res = "${" + getAnMybatisConfigurationVariableKey() + "}" and
|
||||||
mc.getArgument(i) = node.asExpr()
|
ma.getArgument(i) = node.asExpr()
|
||||||
)
|
)
|
||||||
or
|
or
|
||||||
// SQL injection vulnerability where the MyBatis Mapper method has only one parameter and the parameter is not annotated with `@Param`.
|
// SQL injection vulnerability where the MyBatis Mapper method has only one parameter and the parameter is not annotated with `@Param`.
|
||||||
// e.g. MyBatis Mapper method: `void test(String name);` and MyBatis Mapper XML file:`select id,name from test where name like '%${value}%'`
|
// e.g. MyBatis Mapper method: `void test(String name);` and MyBatis Mapper XML file:`select id,name from test where name like '%${value}%'`
|
||||||
exists(MyBatisMapperSqlOperation mbmxe, MyBatisMapperSql mbms, MethodAccess mc, string res |
|
exists(MyBatisMapperSqlOperation mbmxe, MethodAccess ma, string res |
|
||||||
mbmxe.getMapperMethod() = mc.getMethod()
|
mbmxe.getMapperMethod() = ma.getMethod()
|
||||||
|
|
|
|
||||||
(
|
(
|
||||||
mbmxe.getAChild*() = xmle
|
mbmxe.getAChild*() = xmle
|
||||||
or
|
or
|
||||||
mbmxe.getInclude().getRefid() = mbms.getId() and
|
exists(MyBatisMapperSql mbms |
|
||||||
mbms.getAChild*() = xmle
|
mbmxe.getInclude().getRefid() = mbms.getId() and
|
||||||
|
mbms.getAChild*() = xmle
|
||||||
|
)
|
||||||
) and
|
) and
|
||||||
res = getAnMybatiXmlSetValue(xmle) and
|
res = getAnMybatiXmlSetValue(xmle) and
|
||||||
mc.getMethod().getNumberOfParameters() = 1 and
|
ma.getMethod().getNumberOfParameters() = 1 and
|
||||||
not mc.getMethod().getAParameter().hasAnnotation() and
|
not ma.getMethod().getAParameter().getAnAnnotation().getType() instanceof TypeParam and
|
||||||
res.matches("%${%}") and
|
res.matches("%${%}") and
|
||||||
not res.matches("${" + getAnMybatisConfigurationVariableKey() + "}") and
|
not res = "${" + getAnMybatisConfigurationVariableKey() + "}" and
|
||||||
mc.getAnArgument() = node.asExpr()
|
ma.getAnArgument() = node.asExpr()
|
||||||
)
|
)
|
||||||
or
|
or
|
||||||
// MyBatis Mapper method default parameter sql injection vulnerabilities.the default parameter form of the method is arg[0...n] or param[1...n].
|
// MyBatis Mapper method default parameter sql injection vulnerabilities.the default parameter form of the method is arg[0...n] or param[1...n].
|
||||||
// e.g. MyBatis Mapper method: `void test(String name);` and MyBatis Mapper XML file:`select id,name from test where name like '%${arg0 or param1}%'`
|
// e.g. MyBatis Mapper method: `void test(String name);` and MyBatis Mapper XML file:`select id,name from test where name like '%${arg0 or param1}%'`
|
||||||
exists(
|
exists(MyBatisMapperSqlOperation mbmxe, MethodAccess ma, int i, string res |
|
||||||
MyBatisMapperSqlOperation mbmxe, MyBatisMapperSql mbms, MethodAccess mc, int i, string res
|
mbmxe.getMapperMethod() = ma.getMethod()
|
||||||
|
|
|
||||||
mbmxe.getMapperMethod() = mc.getMethod()
|
|
||||||
|
|
|
|
||||||
(
|
(
|
||||||
mbmxe.getAChild*() = xmle
|
mbmxe.getAChild*() = xmle
|
||||||
or
|
or
|
||||||
mbmxe.getInclude().getRefid() = mbms.getId() and
|
exists(MyBatisMapperSql mbms |
|
||||||
mbms.getAChild*() = xmle
|
mbmxe.getInclude().getRefid() = mbms.getId() and
|
||||||
|
mbms.getAChild*() = xmle
|
||||||
|
)
|
||||||
) and
|
) and
|
||||||
not mc.getMethod().getParameter(i).hasAnnotation() and
|
not ma.getMethod().getParameter(i).getAnAnnotation().getType() instanceof TypeParam and
|
||||||
res = getAnMybatiXmlSetValue(xmle) and
|
res = getAnMybatiXmlSetValue(xmle) and
|
||||||
(
|
(
|
||||||
res.matches("%${param" + (i + 1) + "%}")
|
res.matches("%${param" + (i + 1) + "%}")
|
||||||
or
|
or
|
||||||
res.matches("%${arg" + i + "%}")
|
res.matches("%${arg" + i + "%}")
|
||||||
) and
|
) and
|
||||||
mc.getArgument(i) = node.asExpr()
|
not res = "${" + getAnMybatisConfigurationVariableKey() + "}" and
|
||||||
|
ma.getArgument(i) = node.asExpr()
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,33 +0,0 @@
|
|||||||
/**
|
|
||||||
* Provide universal sanitizer guards.
|
|
||||||
*/
|
|
||||||
|
|
||||||
import java
|
|
||||||
import semmle.code.java.dataflow.DataFlow
|
|
||||||
|
|
||||||
/**
|
|
||||||
* An contains method sanitizer guard.
|
|
||||||
*
|
|
||||||
* e.g. `if(test.contains("test")) {...`
|
|
||||||
*/
|
|
||||||
class ContainsSanitizer extends DataFlow::BarrierGuard {
|
|
||||||
ContainsSanitizer() { this.(MethodAccess).getMethod().hasName("contains") }
|
|
||||||
|
|
||||||
override predicate checks(Expr e, boolean branch) {
|
|
||||||
e = this.(MethodAccess).getArgument(0) and branch = true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* An equals method sanitizer guard.
|
|
||||||
*
|
|
||||||
* e.g. `if("test".equals(test)) {...`
|
|
||||||
*/
|
|
||||||
class EqualsSanitizer extends DataFlow::BarrierGuard {
|
|
||||||
EqualsSanitizer() { this.(MethodAccess).getMethod().hasName("equals") }
|
|
||||||
|
|
||||||
override predicate checks(Expr e, boolean branch) {
|
|
||||||
e = [this.(MethodAccess).getArgument(0), this.(MethodAccess).getQualifier()] and
|
|
||||||
branch = true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,3 +1,7 @@
|
|||||||
|
/**
|
||||||
|
* Provides classes for working with MyBatis mapper xml files and their content.
|
||||||
|
*/
|
||||||
|
|
||||||
import java
|
import java
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -20,12 +24,14 @@ class MyBatisMapperXMLElement extends XMLElement {
|
|||||||
/**
|
/**
|
||||||
* Gets the value for this element, with leading and trailing whitespace trimmed.
|
* Gets the value for this element, with leading and trailing whitespace trimmed.
|
||||||
*/
|
*/
|
||||||
string getValue() { result = allCharactersString().trim() }
|
string getValue() { result = this.allCharactersString().trim() }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets the reference type bound to MyBatis Mapper XML File.
|
* Gets the reference type bound to MyBatis Mapper XML File.
|
||||||
*/
|
*/
|
||||||
RefType getNamespaceRefType() { result.getQualifiedName() = getAttribute("namespace").getValue() }
|
RefType getNamespaceRefType() {
|
||||||
|
result.getQualifiedName() = this.getAttribute("namespace").getValue()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -37,8 +43,11 @@ abstract class MyBatisMapperSqlOperation extends MyBatisMapperXMLElement {
|
|||||||
/**
|
/**
|
||||||
* Gets the `<include>` element in a `MyBatisMapperSqlOperation`.
|
* Gets the `<include>` element in a `MyBatisMapperSqlOperation`.
|
||||||
*/
|
*/
|
||||||
MyBatisMapperInclude getInclude() { result = getAChild*() }
|
MyBatisMapperInclude getInclude() { result = this.getAChild*() }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the method bound to MyBatis Mapper XML File.
|
||||||
|
*/
|
||||||
Method getMapperMethod() {
|
Method getMapperMethod() {
|
||||||
result.getName() = this.getId() and
|
result.getName() = this.getId() and
|
||||||
result.getDeclaringType() = this.getParent().(MyBatisMapperXMLElement).getNamespaceRefType()
|
result.getDeclaringType() = this.getParent().(MyBatisMapperXMLElement).getNamespaceRefType()
|
||||||
@@ -49,72 +58,72 @@ abstract class MyBatisMapperSqlOperation extends MyBatisMapperXMLElement {
|
|||||||
* A `<insert>` element in a `MyBatisMapperSqlOperation`.
|
* A `<insert>` element in a `MyBatisMapperSqlOperation`.
|
||||||
*/
|
*/
|
||||||
class MyBatisMapperInsert extends MyBatisMapperSqlOperation {
|
class MyBatisMapperInsert extends MyBatisMapperSqlOperation {
|
||||||
MyBatisMapperInsert() { getName() = "insert" }
|
MyBatisMapperInsert() { this.getName() = "insert" }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets the value of the `id` attribute of this `<insert>`.
|
* Gets the value of the `id` attribute of this `<insert>`.
|
||||||
*/
|
*/
|
||||||
override string getId() { result = getAttribute("id").getValue() }
|
override string getId() { result = this.getAttribute("id").getValue() }
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A `<update>` element in a `MyBatisMapperSqlOperation`.
|
* A `<update>` element in a `MyBatisMapperSqlOperation`.
|
||||||
*/
|
*/
|
||||||
class MyBatisMapperUpdate extends MyBatisMapperSqlOperation {
|
class MyBatisMapperUpdate extends MyBatisMapperSqlOperation {
|
||||||
MyBatisMapperUpdate() { getName() = "update" }
|
MyBatisMapperUpdate() { this.getName() = "update" }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets the value of the `id` attribute of this `<update>`.
|
* Gets the value of the `id` attribute of this `<update>`.
|
||||||
*/
|
*/
|
||||||
override string getId() { result = getAttribute("id").getValue() }
|
override string getId() { result = this.getAttribute("id").getValue() }
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A `<delete>` element in a `MyBatisMapperSqlOperation`.
|
* A `<delete>` element in a `MyBatisMapperSqlOperation`.
|
||||||
*/
|
*/
|
||||||
class MyBatisMapperDelete extends MyBatisMapperSqlOperation {
|
class MyBatisMapperDelete extends MyBatisMapperSqlOperation {
|
||||||
MyBatisMapperDelete() { getName() = "delete" }
|
MyBatisMapperDelete() { this.getName() = "delete" }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets the value of the `id` attribute of this `<delete>`.
|
* Gets the value of the `id` attribute of this `<delete>`.
|
||||||
*/
|
*/
|
||||||
override string getId() { result = getAttribute("id").getValue() }
|
override string getId() { result = this.getAttribute("id").getValue() }
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A `<select>` element in a `MyBatisMapperSqlOperation`.
|
* A `<select>` element in a `MyBatisMapperSqlOperation`.
|
||||||
*/
|
*/
|
||||||
class MyBatisMapperSelect extends MyBatisMapperSqlOperation {
|
class MyBatisMapperSelect extends MyBatisMapperSqlOperation {
|
||||||
MyBatisMapperSelect() { getName() = "select" }
|
MyBatisMapperSelect() { this.getName() = "select" }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets the value of the `id` attribute of this `<select>`.
|
* Gets the value of the `id` attribute of this `<select>`.
|
||||||
*/
|
*/
|
||||||
override string getId() { result = getAttribute("id").getValue() }
|
override string getId() { result = this.getAttribute("id").getValue() }
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A `<select>` element in a `MyBatisMapperXMLElement`.
|
* A `<select>` element in a `MyBatisMapperXMLElement`.
|
||||||
*/
|
*/
|
||||||
class MyBatisMapperSql extends MyBatisMapperXMLElement {
|
class MyBatisMapperSql extends MyBatisMapperXMLElement {
|
||||||
MyBatisMapperSql() { getName() = "sql" }
|
MyBatisMapperSql() { this.getName() = "sql" }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets the value of the `id` attribute of this `<sql>`.
|
* Gets the value of the `id` attribute of this `<sql>`.
|
||||||
*/
|
*/
|
||||||
string getId() { result = getAttribute("id").getValue() }
|
string getId() { result = this.getAttribute("id").getValue() }
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A `<include>` element in a `MyBatisMapperXMLElement`.
|
* A `<include>` element in a `MyBatisMapperXMLElement`.
|
||||||
*/
|
*/
|
||||||
class MyBatisMapperInclude extends MyBatisMapperXMLElement {
|
class MyBatisMapperInclude extends MyBatisMapperXMLElement {
|
||||||
MyBatisMapperInclude() { getName() = "include" }
|
MyBatisMapperInclude() { this.getName() = "include" }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets the value of the `refid` attribute of this `<include>`.
|
* Gets the value of the `refid` attribute of this `<include>`.
|
||||||
*/
|
*/
|
||||||
string getRefid() { result = getAttribute("refid").getValue() }
|
string getRefid() { result = this.getAttribute("refid").getValue() }
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -13,4 +13,4 @@ nodes
|
|||||||
| MybatisSqlInjectionService.java:51:27:51:33 | hashMap | semmle.label | hashMap |
|
| MybatisSqlInjectionService.java:51:27:51:33 | hashMap | semmle.label | hashMap |
|
||||||
subpaths
|
subpaths
|
||||||
#select
|
#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 |
|
| 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 |
|
||||||
|
|||||||
@@ -15,8 +15,6 @@ edges
|
|||||||
| MybatisSqlInjection.java:53:35:53:40 | params : List | MybatisSqlInjectionService.java:40:19:40:37 | 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: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[] |
|
| MybatisSqlInjection.java:58:35:58:40 | params : String[] | MybatisSqlInjectionService.java:44:19:44:33 | params : String[] |
|
||||||
| MybatisSqlInjection.java:73:26:73:36 | name : String | MybatisSqlInjection.java:74:56:74:59 | name : String |
|
|
||||||
| MybatisSqlInjection.java:74:56:74:59 | name : String | MybatisSqlInjectionService.java:59:26:59:36 | name : String |
|
|
||||||
| MybatisSqlInjectionService.java:13:25:13:35 | name : String | MybatisSqlInjectionService.java:14:47:14:50 | name |
|
| 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: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:23:25:23:33 | test : Test | MybatisSqlInjectionService.java:24:47:24:50 | test |
|
||||||
@@ -25,7 +23,6 @@ edges
|
|||||||
| MybatisSqlInjectionService.java:36:19:36:44 | params : Map | MybatisSqlInjectionService.java:37:27:37:32 | params |
|
| 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: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 |
|
| MybatisSqlInjectionService.java:44:19:44:33 | params : String[] | MybatisSqlInjectionService.java:45:27:45:32 | params |
|
||||||
| MybatisSqlInjectionService.java:59:26:59:36 | name : String | MybatisSqlInjectionService.java:60:48:60:51 | name |
|
|
||||||
nodes
|
nodes
|
||||||
| MybatisSqlInjection.java:19:25:19:49 | name : String | semmle.label | name : String |
|
| 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:20:55:20:58 | name : String | semmle.label | name : String |
|
||||||
@@ -43,8 +40,6 @@ nodes
|
|||||||
| MybatisSqlInjection.java:53:35:53:40 | 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:57:19:57:46 | params : String[] | semmle.label | params : String[] |
|
||||||
| MybatisSqlInjection.java:58:35:58:40 | params : String[] | semmle.label | params : String[] |
|
| MybatisSqlInjection.java:58:35:58:40 | params : String[] | semmle.label | params : String[] |
|
||||||
| MybatisSqlInjection.java:73:26:73:36 | name : String | semmle.label | name : String |
|
|
||||||
| MybatisSqlInjection.java:74:56:74:59 | name : String | semmle.label | name : String |
|
|
||||||
| MybatisSqlInjectionService.java:13:25:13:35 | name : String | semmle.label | name : 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:14:47:14:50 | name | semmle.label | name |
|
||||||
| MybatisSqlInjectionService.java:18:25:18:35 | name : String | semmle.label | name : String |
|
| MybatisSqlInjectionService.java:18:25:18:35 | name : String | semmle.label | name : String |
|
||||||
@@ -61,16 +56,14 @@ nodes
|
|||||||
| MybatisSqlInjectionService.java:41:27:41:32 | params | semmle.label | params |
|
| MybatisSqlInjectionService.java:41:27:41:32 | params | semmle.label | params |
|
||||||
| MybatisSqlInjectionService.java:44:19:44:33 | params : String[] | semmle.label | params : String[] |
|
| MybatisSqlInjectionService.java:44:19:44:33 | params : String[] | semmle.label | params : String[] |
|
||||||
| MybatisSqlInjectionService.java:45:27:45:32 | params | semmle.label | params |
|
| MybatisSqlInjectionService.java:45:27:45:32 | params | semmle.label | params |
|
||||||
| MybatisSqlInjectionService.java:59:26:59:36 | name : String | semmle.label | name : String |
|
|
||||||
| MybatisSqlInjectionService.java:60:48:60:51 | name | semmle.label | name |
|
|
||||||
subpaths
|
subpaths
|
||||||
#select
|
#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: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: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: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: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: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: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: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: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 |
|
| 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 |
|
||||||
|
|||||||
@@ -68,10 +68,4 @@ public class MybatisSqlInjection {
|
|||||||
List<Test> result = mybatisSqlInjectionService.good1(id);
|
List<Test> result = mybatisSqlInjectionService.good1(id);
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
@GetMapping(value = "good2")
|
|
||||||
public List<Test> good2(String name) {
|
|
||||||
List<Test> result = mybatisSqlInjectionService.good2(name);
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -55,9 +55,4 @@ public class MybatisSqlInjectionService {
|
|||||||
List<Test> result = sqlInjectionMapper.good1(id);
|
List<Test> result = sqlInjectionMapper.good1(id);
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<Test> good2(String name) {
|
|
||||||
List<Test> result = sqlInjectionMapper.good2(name);
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -30,6 +30,4 @@ public interface SqlInjectionMapper {
|
|||||||
public Test bad9(HashMap<String, Object> map);
|
public Test bad9(HashMap<String, Object> map);
|
||||||
|
|
||||||
List<Test> good1(Integer id);
|
List<Test> good1(Integer id);
|
||||||
|
|
||||||
List<Test> good2(String name);
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -77,8 +77,4 @@
|
|||||||
<select id="good1" parameterType="java.lang.Integer" resultMap="BaseResultMap">
|
<select id="good1" parameterType="java.lang.Integer" resultMap="BaseResultMap">
|
||||||
select id,name from test where id = ${id}
|
select id,name from test where id = ${id}
|
||||||
</select>
|
</select>
|
||||||
|
|
||||||
<select id="good2" parameterType="java.lang.String" resultMap="BaseResultMap">
|
|
||||||
select id,name from test where name = #{name}
|
|
||||||
</select>
|
|
||||||
</mapper>
|
</mapper>
|
||||||
Reference in New Issue
Block a user