mirror of
https://github.com/github/codeql.git
synced 2025-12-21 19:26:31 +01:00
Refactored SpelInjection.qll to use CSV sink models
This commit is contained in:
@@ -11,9 +11,25 @@
|
||||
*/
|
||||
|
||||
import java
|
||||
import SpelInjectionLib
|
||||
import semmle.code.java.security.SpelInjection
|
||||
import DataFlow::PathGraph
|
||||
|
||||
from DataFlow::PathNode source, DataFlow::PathNode sink, ExpressionInjectionConfig conf
|
||||
/**
|
||||
* A taint-tracking configuration for unsafe user input
|
||||
* that is used to construct and evaluate a SpEL expression.
|
||||
*/
|
||||
class SpELInjectionConfig extends TaintTracking::Configuration {
|
||||
SpELInjectionConfig() { this = "SpELInjectionConfig" }
|
||||
|
||||
override predicate isSource(DataFlow::Node source) { source instanceof RemoteFlowSource }
|
||||
|
||||
override predicate isSink(DataFlow::Node sink) { sink instanceof SpelExpressionEvaluationSink }
|
||||
|
||||
override predicate isAdditionalTaintStep(DataFlow::Node node1, DataFlow::Node node2) {
|
||||
any(SpelExpressionInjectionAdditionalTaintStep c).step(node1, node2)
|
||||
}
|
||||
}
|
||||
|
||||
from DataFlow::PathNode source, DataFlow::PathNode sink, SpELInjectionConfig conf
|
||||
where conf.hasFlowPath(source, sink)
|
||||
select sink.getNode(), source, sink, "SpEL injection from $@.", source.getNode(), "this user input"
|
||||
|
||||
@@ -1,97 +0,0 @@
|
||||
import java
|
||||
import semmle.code.java.dataflow.FlowSources
|
||||
import semmle.code.java.dataflow.TaintTracking2
|
||||
import SpringFrameworkLib
|
||||
|
||||
/**
|
||||
* A taint-tracking configuration for unsafe user input
|
||||
* that is used to construct and evaluate a SpEL expression.
|
||||
*/
|
||||
class ExpressionInjectionConfig extends TaintTracking::Configuration {
|
||||
ExpressionInjectionConfig() { this = "ExpressionInjectionConfig" }
|
||||
|
||||
override predicate isSource(DataFlow::Node source) { source instanceof RemoteFlowSource }
|
||||
|
||||
override predicate isSink(DataFlow::Node sink) { sink instanceof ExpressionEvaluationSink }
|
||||
|
||||
override predicate isAdditionalTaintStep(DataFlow::Node node1, DataFlow::Node node2) {
|
||||
expressionParsingStep(node1, node2) or
|
||||
springPropertiesStep(node1, node2)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A sink for SpEL injection vulnerabilities,
|
||||
* i.e. methods that run evaluation of a SpEL expression in a powerfull context.
|
||||
*/
|
||||
class ExpressionEvaluationSink extends DataFlow::ExprNode {
|
||||
ExpressionEvaluationSink() {
|
||||
exists(MethodAccess ma, Method m | m = ma.getMethod() |
|
||||
m instanceof ExpressionEvaluationMethod and
|
||||
getExpr() = ma.getQualifier() and
|
||||
not exists(SafeEvaluationContextFlowConfig config |
|
||||
config.hasFlowTo(DataFlow::exprNode(ma.getArgument(0)))
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `node1` to `node2` is a dataflow step that parses a SpEL expression,
|
||||
* i.e. `parser.parseExpression(tainted)`.
|
||||
*/
|
||||
predicate expressionParsingStep(DataFlow::Node node1, DataFlow::Node node2) {
|
||||
exists(MethodAccess ma, Method m | ma.getMethod() = m |
|
||||
m.getDeclaringType().getAnAncestor*() instanceof ExpressionParser and
|
||||
m.hasName("parseExpression") and
|
||||
ma.getAnArgument() = node1.asExpr() and
|
||||
node2.asExpr() = ma
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* A configuration for safe evaluation context that may be used in expression evaluation.
|
||||
*/
|
||||
class SafeEvaluationContextFlowConfig extends DataFlow2::Configuration {
|
||||
SafeEvaluationContextFlowConfig() { this = "SpelInjection::SafeEvaluationContextFlowConfig" }
|
||||
|
||||
override predicate isSource(DataFlow::Node source) { source instanceof SafeContextSource }
|
||||
|
||||
override predicate isSink(DataFlow::Node sink) {
|
||||
exists(MethodAccess ma, Method m | m = ma.getMethod() |
|
||||
m instanceof ExpressionEvaluationMethod and
|
||||
ma.getArgument(0) = sink.asExpr()
|
||||
)
|
||||
}
|
||||
|
||||
override int fieldFlowBranchLimit() { result = 0 }
|
||||
}
|
||||
|
||||
class SafeContextSource extends DataFlow::ExprNode {
|
||||
SafeContextSource() {
|
||||
isSimpleEvaluationContextConstructorCall(getExpr()) or
|
||||
isSimpleEvaluationContextBuilderCall(getExpr())
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `expr` constructs `SimpleEvaluationContext`.
|
||||
*/
|
||||
predicate isSimpleEvaluationContextConstructorCall(Expr expr) {
|
||||
exists(ConstructorCall cc |
|
||||
cc.getConstructedType() instanceof SimpleEvaluationContext and
|
||||
cc = expr
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `expr` builds `SimpleEvaluationContext` via `SimpleEvaluationContext.Builder`,
|
||||
* e.g. `SimpleEvaluationContext.forReadWriteDataBinding().build()`.
|
||||
*/
|
||||
predicate isSimpleEvaluationContextBuilderCall(Expr expr) {
|
||||
exists(MethodAccess ma, Method m | ma.getMethod() = m |
|
||||
m.getDeclaringType() instanceof SimpleEvaluationContextBuilder and
|
||||
m.hasName("build") and
|
||||
ma = expr
|
||||
)
|
||||
}
|
||||
@@ -1,111 +0,0 @@
|
||||
import java
|
||||
import semmle.code.java.dataflow.DataFlow
|
||||
|
||||
/**
|
||||
* Methods that trigger evaluation of an expression.
|
||||
*/
|
||||
class ExpressionEvaluationMethod extends Method {
|
||||
ExpressionEvaluationMethod() {
|
||||
getDeclaringType() instanceof Expression and
|
||||
(
|
||||
hasName("getValue") or
|
||||
hasName("getValueTypeDescriptor") or
|
||||
hasName("getValueType") or
|
||||
hasName("setValue")
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `node1` to `node2` is a dataflow step that converts `PropertyValues`
|
||||
* to an array of `PropertyValue`, i.e. `tainted.getPropertyValues()`.
|
||||
*/
|
||||
predicate getPropertyValuesStep(DataFlow::Node node1, DataFlow::Node node2) {
|
||||
exists(MethodAccess ma, Method m | m = ma.getMethod() |
|
||||
node1.asExpr() = ma.getQualifier() and
|
||||
node2.asExpr() = ma and
|
||||
m.getDeclaringType() instanceof PropertyValues and
|
||||
m.hasName("getPropertyValues")
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `node1` to `node2` is a dataflow step that constructs `MutablePropertyValues`,
|
||||
* i.e. `new MutablePropertyValues(tainted)`.
|
||||
*/
|
||||
predicate createMutablePropertyValuesStep(DataFlow::Node node1, DataFlow::Node node2) {
|
||||
exists(ConstructorCall cc | cc.getConstructedType() instanceof MutablePropertyValues |
|
||||
node1.asExpr() = cc.getAnArgument() and
|
||||
node2.asExpr() = cc
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `node1` to `node2` is a dataflow step that returns a name of `PropertyValue`,
|
||||
* i.e. `tainted.getName()`.
|
||||
*/
|
||||
predicate getPropertyNameStep(DataFlow::Node node1, DataFlow::Node node2) {
|
||||
exists(MethodAccess ma, Method m | m = ma.getMethod() |
|
||||
node1.asExpr() = ma.getQualifier() and
|
||||
node2.asExpr() = ma and
|
||||
m.getDeclaringType() instanceof PropertyValue and
|
||||
m.hasName("getName")
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `node1` to `node2` is a dataflow step that converts `MutablePropertyValues`
|
||||
* to a list of `PropertyValue`, i.e. `tainted.getPropertyValueList()`.
|
||||
*/
|
||||
predicate getPropertyValueListStep(DataFlow::Node node1, DataFlow::Node node2) {
|
||||
exists(MethodAccess ma, Method m | m = ma.getMethod() |
|
||||
node1.asExpr() = ma.getQualifier() and
|
||||
node2.asExpr() = ma and
|
||||
m.getDeclaringType() instanceof MutablePropertyValues and
|
||||
m.hasName("getPropertyValueList")
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `node1` to `node2` is one of the dataflow steps that propagate
|
||||
* tainted data via Spring properties.
|
||||
*/
|
||||
predicate springPropertiesStep(DataFlow::Node node1, DataFlow::Node node2) {
|
||||
createMutablePropertyValuesStep(node1, node2) or
|
||||
getPropertyNameStep(node1, node2) or
|
||||
getPropertyValuesStep(node1, node2) or
|
||||
getPropertyValueListStep(node1, node2)
|
||||
}
|
||||
|
||||
class PropertyValue extends RefType {
|
||||
PropertyValue() { hasQualifiedName("org.springframework.beans", "PropertyValue") }
|
||||
}
|
||||
|
||||
class PropertyValues extends RefType {
|
||||
PropertyValues() { hasQualifiedName("org.springframework.beans", "PropertyValues") }
|
||||
}
|
||||
|
||||
class MutablePropertyValues extends RefType {
|
||||
MutablePropertyValues() { hasQualifiedName("org.springframework.beans", "MutablePropertyValues") }
|
||||
}
|
||||
|
||||
class SimpleEvaluationContext extends RefType {
|
||||
SimpleEvaluationContext() {
|
||||
hasQualifiedName("org.springframework.expression.spel.support", "SimpleEvaluationContext")
|
||||
}
|
||||
}
|
||||
|
||||
class SimpleEvaluationContextBuilder extends RefType {
|
||||
SimpleEvaluationContextBuilder() {
|
||||
hasQualifiedName("org.springframework.expression.spel.support",
|
||||
"SimpleEvaluationContext$Builder")
|
||||
}
|
||||
}
|
||||
|
||||
class Expression extends RefType {
|
||||
Expression() { hasQualifiedName("org.springframework.expression", "Expression") }
|
||||
}
|
||||
|
||||
class ExpressionParser extends RefType {
|
||||
ExpressionParser() { hasQualifiedName("org.springframework.expression", "ExpressionParser") }
|
||||
}
|
||||
Reference in New Issue
Block a user