Java: Deprecate BarrierGuard class.

This commit is contained in:
Anders Schack-Mulligen
2022-06-15 14:41:19 +02:00
parent c4782871d4
commit 33deff9bae
18 changed files with 224 additions and 172 deletions

View File

@@ -90,14 +90,20 @@ abstract class Configuration extends string {
/** Holds if data flow out of `node` is prohibited. */ /** Holds if data flow out of `node` is prohibited. */
predicate isBarrierOut(Node node) { none() } predicate isBarrierOut(Node node) { none() }
/** Holds if data flow through nodes guarded by `guard` is prohibited. */ /**
predicate isBarrierGuard(BarrierGuard guard) { none() } * DEPRECATED: Use `isBarrier` and `BarrierGuard` module instead.
*
* Holds if data flow through nodes guarded by `guard` is prohibited.
*/
deprecated predicate isBarrierGuard(BarrierGuard guard) { none() }
/** /**
* DEPRECATED: Use `isBarrier` and `BarrierGuard` module instead.
*
* Holds if data flow through nodes guarded by `guard` is prohibited when * Holds if data flow through nodes guarded by `guard` is prohibited when
* the flow state is `state` * the flow state is `state`
*/ */
predicate isBarrierGuard(BarrierGuard guard, FlowState state) { none() } deprecated predicate isBarrierGuard(BarrierGuard guard, FlowState state) { none() }
/** /**
* Holds if data may flow from `node1` to `node2` in addition to the normal data-flow steps. * Holds if data may flow from `node1` to `node2` in addition to the normal data-flow steps.
@@ -335,6 +341,29 @@ private predicate outBarrier(NodeEx node, Configuration config) {
) )
} }
/** A bridge class to access the deprecated `isBarrierGuard`. */
private class BarrierGuardGuardedNodeBridge extends Unit {
abstract predicate guardedNode(Node n, Configuration config);
abstract predicate guardedNode(Node n, FlowState state, Configuration config);
}
private class BarrierGuardGuardedNode extends BarrierGuardGuardedNodeBridge {
deprecated override predicate guardedNode(Node n, Configuration config) {
exists(BarrierGuard g |
config.isBarrierGuard(g) and
n = g.getAGuardedNode()
)
}
deprecated override predicate guardedNode(Node n, FlowState state, Configuration config) {
exists(BarrierGuard g |
config.isBarrierGuard(g, state) and
n = g.getAGuardedNode()
)
}
}
pragma[nomagic] pragma[nomagic]
private predicate fullBarrier(NodeEx node, Configuration config) { private predicate fullBarrier(NodeEx node, Configuration config) {
exists(Node n | node.asNode() = n | exists(Node n | node.asNode() = n |
@@ -348,10 +377,7 @@ private predicate fullBarrier(NodeEx node, Configuration config) {
not config.isSink(n) and not config.isSink(n) and
not config.isSink(n, _) not config.isSink(n, _)
or or
exists(BarrierGuard g | any(BarrierGuardGuardedNodeBridge b).guardedNode(n, config)
config.isBarrierGuard(g) and
n = g.getAGuardedNode()
)
) )
} }
@@ -360,10 +386,7 @@ private predicate stateBarrier(NodeEx node, FlowState state, Configuration confi
exists(Node n | node.asNode() = n | exists(Node n | node.asNode() = n |
config.isBarrier(n, state) config.isBarrier(n, state)
or or
exists(BarrierGuard g | any(BarrierGuardGuardedNodeBridge b).guardedNode(n, state, config)
config.isBarrierGuard(g, state) and
n = g.getAGuardedNode()
)
) )
} }

View File

@@ -332,6 +332,8 @@ module BarrierGuard<guardChecksSig/3 guardChecks> {
} }
/** /**
* DEPRECATED: Use `BarrierGuard` module instead.
*
* A guard that validates some expression. * A guard that validates some expression.
* *
* To use this in a configuration, extend the class and provide a * To use this in a configuration, extend the class and provide a
@@ -340,7 +342,7 @@ module BarrierGuard<guardChecksSig/3 guardChecks> {
* *
* It is important that all extending classes in scope are disjoint. * It is important that all extending classes in scope are disjoint.
*/ */
class BarrierGuard extends Guard { deprecated class BarrierGuard extends Guard {
/** Holds if this guard validates `e` upon evaluating to `branch`. */ /** Holds if this guard validates `e` upon evaluating to `branch`. */
abstract predicate checks(Expr e, boolean branch); abstract predicate checks(Expr e, boolean branch);

View File

@@ -112,12 +112,6 @@ private module Cached {
} }
} }
/**
* Holds if `guard` should be a sanitizer guard in all global taint flow configurations
* but not in local taint.
*/
predicate defaultTaintSanitizerGuard(DataFlow::BarrierGuard guard) { none() }
import Cached import Cached
private RefType getElementType(RefType container) { private RefType getElementType(RefType container) {

View File

@@ -116,20 +116,30 @@ abstract class Configuration extends DataFlow::Configuration {
final override predicate isBarrierOut(DataFlow::Node node) { this.isSanitizerOut(node) } final override predicate isBarrierOut(DataFlow::Node node) { this.isSanitizerOut(node) }
/** Holds if taint propagation through nodes guarded by `guard` is prohibited. */ /**
predicate isSanitizerGuard(DataFlow::BarrierGuard guard) { none() } * DEPRECATED: Use `isSanitizer` and `BarrierGuard` module instead.
*
* Holds if taint propagation through nodes guarded by `guard` is prohibited.
*/
deprecated predicate isSanitizerGuard(DataFlow::BarrierGuard guard) { none() }
final override predicate isBarrierGuard(DataFlow::BarrierGuard guard) { deprecated final override predicate isBarrierGuard(DataFlow::BarrierGuard guard) {
this.isSanitizerGuard(guard) or defaultTaintSanitizerGuard(guard) this.isSanitizerGuard(guard)
} }
/** /**
* DEPRECATED: Use `isSanitizer` and `BarrierGuard` module instead.
*
* Holds if taint propagation through nodes guarded by `guard` is prohibited * Holds if taint propagation through nodes guarded by `guard` is prohibited
* when the flow state is `state`. * when the flow state is `state`.
*/ */
predicate isSanitizerGuard(DataFlow::BarrierGuard guard, DataFlow::FlowState state) { none() } deprecated predicate isSanitizerGuard(DataFlow::BarrierGuard guard, DataFlow::FlowState state) {
none()
}
final override predicate isBarrierGuard(DataFlow::BarrierGuard guard, DataFlow::FlowState state) { deprecated final override predicate isBarrierGuard(
DataFlow::BarrierGuard guard, DataFlow::FlowState state
) {
this.isSanitizerGuard(guard, state) this.isSanitizerGuard(guard, state)
} }

View File

@@ -116,20 +116,30 @@ abstract class Configuration extends DataFlow::Configuration {
final override predicate isBarrierOut(DataFlow::Node node) { this.isSanitizerOut(node) } final override predicate isBarrierOut(DataFlow::Node node) { this.isSanitizerOut(node) }
/** Holds if taint propagation through nodes guarded by `guard` is prohibited. */ /**
predicate isSanitizerGuard(DataFlow::BarrierGuard guard) { none() } * DEPRECATED: Use `isSanitizer` and `BarrierGuard` module instead.
*
* Holds if taint propagation through nodes guarded by `guard` is prohibited.
*/
deprecated predicate isSanitizerGuard(DataFlow::BarrierGuard guard) { none() }
final override predicate isBarrierGuard(DataFlow::BarrierGuard guard) { deprecated final override predicate isBarrierGuard(DataFlow::BarrierGuard guard) {
this.isSanitizerGuard(guard) or defaultTaintSanitizerGuard(guard) this.isSanitizerGuard(guard)
} }
/** /**
* DEPRECATED: Use `isSanitizer` and `BarrierGuard` module instead.
*
* Holds if taint propagation through nodes guarded by `guard` is prohibited * Holds if taint propagation through nodes guarded by `guard` is prohibited
* when the flow state is `state`. * when the flow state is `state`.
*/ */
predicate isSanitizerGuard(DataFlow::BarrierGuard guard, DataFlow::FlowState state) { none() } deprecated predicate isSanitizerGuard(DataFlow::BarrierGuard guard, DataFlow::FlowState state) {
none()
}
final override predicate isBarrierGuard(DataFlow::BarrierGuard guard, DataFlow::FlowState state) { deprecated final override predicate isBarrierGuard(
DataFlow::BarrierGuard guard, DataFlow::FlowState state
) {
this.isSanitizerGuard(guard, state) this.isSanitizerGuard(guard, state)
} }

View File

@@ -116,20 +116,30 @@ abstract class Configuration extends DataFlow::Configuration {
final override predicate isBarrierOut(DataFlow::Node node) { this.isSanitizerOut(node) } final override predicate isBarrierOut(DataFlow::Node node) { this.isSanitizerOut(node) }
/** Holds if taint propagation through nodes guarded by `guard` is prohibited. */ /**
predicate isSanitizerGuard(DataFlow::BarrierGuard guard) { none() } * DEPRECATED: Use `isSanitizer` and `BarrierGuard` module instead.
*
* Holds if taint propagation through nodes guarded by `guard` is prohibited.
*/
deprecated predicate isSanitizerGuard(DataFlow::BarrierGuard guard) { none() }
final override predicate isBarrierGuard(DataFlow::BarrierGuard guard) { deprecated final override predicate isBarrierGuard(DataFlow::BarrierGuard guard) {
this.isSanitizerGuard(guard) or defaultTaintSanitizerGuard(guard) this.isSanitizerGuard(guard)
} }
/** /**
* DEPRECATED: Use `isSanitizer` and `BarrierGuard` module instead.
*
* Holds if taint propagation through nodes guarded by `guard` is prohibited * Holds if taint propagation through nodes guarded by `guard` is prohibited
* when the flow state is `state`. * when the flow state is `state`.
*/ */
predicate isSanitizerGuard(DataFlow::BarrierGuard guard, DataFlow::FlowState state) { none() } deprecated predicate isSanitizerGuard(DataFlow::BarrierGuard guard, DataFlow::FlowState state) {
none()
}
final override predicate isBarrierGuard(DataFlow::BarrierGuard guard, DataFlow::FlowState state) { deprecated final override predicate isBarrierGuard(
DataFlow::BarrierGuard guard, DataFlow::FlowState state
) {
this.isSanitizerGuard(guard, state) this.isSanitizerGuard(guard, state)
} }

View File

@@ -24,12 +24,14 @@ abstract class IntentUriPermissionManipulationSink extends DataFlow::Node { }
abstract class IntentUriPermissionManipulationSanitizer extends DataFlow::Node { } abstract class IntentUriPermissionManipulationSanitizer extends DataFlow::Node { }
/** /**
* DEPRECATED: Use `IntentUriPermissionManipulationSanitizer` instead.
*
* A guard that makes sure that an Intent is safe to be returned to another Activity. * A guard that makes sure that an Intent is safe to be returned to another Activity.
* *
* Usually, this is done by checking that the Intent's data URI and/or its flags contain * Usually, this is done by checking that the Intent's data URI and/or its flags contain
* expected values. * expected values.
*/ */
abstract class IntentUriPermissionManipulationGuard extends DataFlow::BarrierGuard { } abstract deprecated class IntentUriPermissionManipulationGuard extends DataFlow::BarrierGuard { }
/** /**
* An additional taint step for flows related to Intent URI permission manipulation * An additional taint step for flows related to Intent URI permission manipulation
@@ -95,10 +97,10 @@ private class IntentFlagsOrDataChangedSanitizer extends IntentUriPermissionManip
* intent.getFlags() & Intent.FLAG_GRANT_WRITE_URI_PERMISSION != 0) {} * intent.getFlags() & Intent.FLAG_GRANT_WRITE_URI_PERMISSION != 0) {}
* ``` * ```
*/ */
private class IntentFlagsOrDataCheckedGuard extends IntentUriPermissionManipulationGuard { private class IntentFlagsOrDataCheckedSanitizer extends IntentUriPermissionManipulationSanitizer {
IntentFlagsOrDataCheckedGuard() { intentFlagsOrDataChecked(this, _, _) } IntentFlagsOrDataCheckedSanitizer() {
this = DataFlow::BarrierGuard<intentFlagsOrDataChecked/3>::getABarrierNode()
override predicate checks(Expr e, boolean branch) { intentFlagsOrDataChecked(this, e, branch) } }
} }
/** /**

View File

@@ -24,7 +24,7 @@ class IntentUriPermissionManipulationConf extends TaintTracking::Configuration {
barrier instanceof IntentUriPermissionManipulationSanitizer barrier instanceof IntentUriPermissionManipulationSanitizer
} }
override predicate isSanitizerGuard(DataFlow::BarrierGuard guard) { deprecated override predicate isSanitizerGuard(DataFlow::BarrierGuard guard) {
guard instanceof IntentUriPermissionManipulationGuard guard instanceof IntentUriPermissionManipulationGuard
} }

View File

@@ -30,10 +30,8 @@ class InjectFilePathConfig extends TaintTracking::Configuration {
override predicate isSanitizer(DataFlow::Node node) { override predicate isSanitizer(DataFlow::Node node) {
exists(Type t | t = node.getType() | t instanceof BoxedType or t instanceof PrimitiveType) exists(Type t | t = node.getType() | t instanceof BoxedType or t instanceof PrimitiveType)
} or
node instanceof PathTraversalSanitizer
override predicate isSanitizerGuard(DataFlow::BarrierGuard guard) {
guard instanceof PathTraversalBarrierGuard
} }
} }

View File

@@ -24,9 +24,7 @@ class InsecureWebResourceResponseConfig extends TaintTracking::Configuration {
override predicate isSink(DataFlow::Node sink) { sink instanceof WebResourceResponseSink } override predicate isSink(DataFlow::Node sink) { sink instanceof WebResourceResponseSink }
override predicate isSanitizerGuard(DataFlow::BarrierGuard guard) { override predicate isSanitizer(DataFlow::Node node) { node instanceof PathTraversalSanitizer }
guard instanceof PathTraversalBarrierGuard
}
} }
from DataFlow::PathNode source, DataFlow::PathNode sink, InsecureWebResourceResponseConfig conf from DataFlow::PathNode source, DataFlow::PathNode sink, InsecureWebResourceResponseConfig conf

View File

@@ -15,17 +15,13 @@ import AndroidFileIntentSink
import AndroidFileIntentSource import AndroidFileIntentSource
import DataFlow::PathGraph import DataFlow::PathGraph
private class StartsWithSanitizer extends DataFlow::BarrierGuard { private predicate startsWithSanitizer(Guard g, Expr e, boolean branch) {
StartsWithSanitizer() { this.(MethodAccess).getMethod().hasName("startsWith") } exists(MethodAccess ma |
g = ma and
override predicate checks(Expr e, boolean branch) { ma.getMethod().hasName("startsWith") and
e = e = [ma.getQualifier(), ma.getQualifier().(MethodAccess).getQualifier()] and
[
this.(MethodAccess).getQualifier(),
this.(MethodAccess).getQualifier().(MethodAccess).getQualifier()
] and
branch = false branch = false
} )
} }
class AndroidFileLeakConfig extends TaintTracking::Configuration { class AndroidFileLeakConfig extends TaintTracking::Configuration {
@@ -75,8 +71,8 @@ class AndroidFileLeakConfig extends TaintTracking::Configuration {
) )
} }
override predicate isSanitizerGuard(DataFlow::BarrierGuard guard) { override predicate isSanitizer(DataFlow::Node node) {
guard instanceof StartsWithSanitizer node = DataFlow::BarrierGuard<startsWithSanitizer/3>::getABarrierNode()
} }
} }

View File

@@ -59,10 +59,8 @@ class ThreadResourceAbuse extends TaintTracking::Configuration {
ma.getMethod().hasQualifiedName("java.lang", "Math", "min") and ma.getMethod().hasQualifiedName("java.lang", "Math", "min") and
node.asExpr() = ma.getAnArgument() node.asExpr() = ma.getAnArgument()
) )
} or
node instanceof LessThanSanitizer // if (sleepTime > 0 && sleepTime < 5000) { ... }
override predicate isSanitizerGuard(DataFlow::BarrierGuard guard) {
guard instanceof LessThanSanitizer // if (sleepTime > 0 && sleepTime < 5000) { ... }
} }
} }

View File

@@ -33,10 +33,8 @@ class ThreadResourceAbuse extends TaintTracking::Configuration {
ma.getMethod().hasQualifiedName("java.lang", "Math", "min") and ma.getMethod().hasQualifiedName("java.lang", "Math", "min") and
node.asExpr() = ma.getAnArgument() node.asExpr() = ma.getAnArgument()
) )
} or
node instanceof LessThanSanitizer // if (sleepTime > 0 && sleepTime < 5000) { ... }
override predicate isSanitizerGuard(DataFlow::BarrierGuard guard) {
guard instanceof LessThanSanitizer // if (sleepTime > 0 && sleepTime < 5000) { ... }
} }
} }

View File

@@ -4,6 +4,7 @@ import java
import semmle.code.java.dataflow.DataFlow import semmle.code.java.dataflow.DataFlow
private import semmle.code.java.dataflow.ExternalFlow private import semmle.code.java.dataflow.ExternalFlow
import semmle.code.java.dataflow.FlowSteps import semmle.code.java.dataflow.FlowSteps
import semmle.code.java.controlflow.Guards
/** `java.lang.Math` data model for value comparison in the new CSV format. */ /** `java.lang.Math` data model for value comparison in the new CSV format. */
private class MathCompDataModel extends SummaryModelCsv { private class MathCompDataModel extends SummaryModelCsv {
@@ -32,15 +33,17 @@ class PauseThreadSink extends DataFlow::Node {
PauseThreadSink() { sinkNode(this, "thread-pause") } PauseThreadSink() { sinkNode(this, "thread-pause") }
} }
private predicate lessThanGuard(Guard g, Expr e, boolean branch) {
e = g.(ComparisonExpr).getLesserOperand() and
branch = true
or
e = g.(ComparisonExpr).getGreaterOperand() and
branch = false
}
/** A sanitizer for lessThan check. */ /** A sanitizer for lessThan check. */
class LessThanSanitizer extends DataFlow::BarrierGuard instanceof ComparisonExpr { class LessThanSanitizer extends DataFlow::Node {
override predicate checks(Expr e, boolean branch) { LessThanSanitizer() { this = DataFlow::BarrierGuard<lessThanGuard/3>::getABarrierNode() }
e = super.getLesserOperand() and
branch = true
or
e = super.getGreaterOperand() and
branch = false
}
} }
/** Value step from the constructor call of a `Runnable` to the instance parameter (this) of `run`. */ /** Value step from the constructor call of a `Runnable` to the instance parameter (this) of `run`. */

View File

@@ -15,23 +15,19 @@ import DataFlow
import UnsafeReflectionLib import UnsafeReflectionLib
import semmle.code.java.dataflow.DataFlow import semmle.code.java.dataflow.DataFlow
import semmle.code.java.dataflow.FlowSources import semmle.code.java.dataflow.FlowSources
import semmle.code.java.controlflow.Guards
import DataFlow::PathGraph import DataFlow::PathGraph
private class ContainsSanitizer extends DataFlow::BarrierGuard { private predicate containsSanitizer(Guard g, Expr e, boolean branch) {
ContainsSanitizer() { this.(MethodAccess).getMethod().hasName("contains") } g.(MethodAccess).getMethod().hasName("contains") and
e = g.(MethodAccess).getArgument(0) and
override predicate checks(Expr e, boolean branch) { branch = true
e = this.(MethodAccess).getArgument(0) and branch = true
}
} }
private class EqualsSanitizer extends DataFlow::BarrierGuard { private predicate equalsSanitizer(Guard g, Expr e, boolean branch) {
EqualsSanitizer() { this.(MethodAccess).getMethod().hasName("equals") } g.(MethodAccess).getMethod().hasName("equals") and
e = [g.(MethodAccess).getArgument(0), g.(MethodAccess).getQualifier()] and
override predicate checks(Expr e, boolean branch) { branch = true
e = [this.(MethodAccess).getArgument(0), this.(MethodAccess).getQualifier()] and
branch = true
}
} }
class UnsafeReflectionConfig extends TaintTracking::Configuration { class UnsafeReflectionConfig extends TaintTracking::Configuration {
@@ -78,8 +74,9 @@ class UnsafeReflectionConfig extends TaintTracking::Configuration {
) )
} }
override predicate isSanitizerGuard(DataFlow::BarrierGuard guard) { override predicate isSanitizer(DataFlow::Node node) {
guard instanceof ContainsSanitizer or guard instanceof EqualsSanitizer node = DataFlow::BarrierGuard<containsSanitizer/3>::getABarrierNode() or
node = DataFlow::BarrierGuard<equalsSanitizer/3>::getABarrierNode()
} }
} }

View File

@@ -35,10 +35,9 @@ class UnsafeUrlForwardFlowConfig extends TaintTracking::Configuration {
override predicate isSink(DataFlow::Node sink) { sink instanceof UnsafeUrlForwardSink } override predicate isSink(DataFlow::Node sink) { sink instanceof UnsafeUrlForwardSink }
override predicate isSanitizer(DataFlow::Node node) { node instanceof UnsafeUrlForwardSanitizer } override predicate isSanitizer(DataFlow::Node node) {
node instanceof UnsafeUrlForwardSanitizer or
override predicate isSanitizerGuard(DataFlow::BarrierGuard guard) { node instanceof PathTraversalSanitizer
guard instanceof PathTraversalBarrierGuard
} }
override DataFlow::FlowFeature getAFeature() { override DataFlow::FlowFeature getAFeature() {

View File

@@ -13,18 +13,15 @@
import java import java
import SpringUrlRedirect import SpringUrlRedirect
import semmle.code.java.dataflow.FlowSources import semmle.code.java.dataflow.FlowSources
import semmle.code.java.controlflow.Guards
import DataFlow::PathGraph import DataFlow::PathGraph
private class StartsWithSanitizer extends DataFlow::BarrierGuard { private predicate startsWithSanitizer(Guard g, Expr e, boolean branch) {
StartsWithSanitizer() { g.(MethodAccess).getMethod().hasName("startsWith") and
this.(MethodAccess).getMethod().hasName("startsWith") and g.(MethodAccess).getMethod().getDeclaringType() instanceof TypeString and
this.(MethodAccess).getMethod().getDeclaringType() instanceof TypeString and g.(MethodAccess).getMethod().getNumberOfParameters() = 1 and
this.(MethodAccess).getMethod().getNumberOfParameters() = 1 e = g.(MethodAccess).getQualifier() and
} branch = true
override predicate checks(Expr e, boolean branch) {
e = this.(MethodAccess).getQualifier() and branch = true
}
} }
class SpringUrlRedirectFlowConfig extends TaintTracking::Configuration { class SpringUrlRedirectFlowConfig extends TaintTracking::Configuration {
@@ -38,10 +35,6 @@ class SpringUrlRedirectFlowConfig extends TaintTracking::Configuration {
springUrlRedirectTaintStep(fromNode, toNode) springUrlRedirectTaintStep(fromNode, toNode)
} }
override predicate isSanitizerGuard(DataFlow::BarrierGuard guard) {
guard instanceof StartsWithSanitizer
}
override predicate isSanitizer(DataFlow::Node node) { override predicate isSanitizer(DataFlow::Node node) {
// Exclude the case where the left side of the concatenated string is not `redirect:`. // Exclude the case where the left side of the concatenated string is not `redirect:`.
// E.g: `String url = "/path?token=" + request.getParameter("token");` // E.g: `String url = "/path?token=" + request.getParameter("token");`
@@ -63,6 +56,8 @@ class SpringUrlRedirectFlowConfig extends TaintTracking::Configuration {
) )
or or
nonLocationHeaderSanitizer(node) nonLocationHeaderSanitizer(node)
or
node = DataFlow::BarrierGuard<startsWithSanitizer/3>::getABarrierNode()
} }
} }

View File

@@ -3,21 +3,32 @@ private import semmle.code.java.controlflow.Guards
private import semmle.code.java.dataflow.FlowSources private import semmle.code.java.dataflow.FlowSources
private import semmle.code.java.dataflow.ExternalFlow private import semmle.code.java.dataflow.ExternalFlow
/** A barrier guard that protects against path traversal vulnerabilities. */ /**
abstract class PathTraversalBarrierGuard extends DataFlow::BarrierGuard { } * DEPRECATED: Use `PathTraversalSanitizer` instead.
*
* A barrier guard that protects against path traversal vulnerabilities.
*/
abstract deprecated class PathTraversalBarrierGuard extends DataFlow::BarrierGuard { }
/** A sanitizer that protects against path traversal vulnerabilities. */
abstract class PathTraversalSanitizer extends DataFlow::Node { }
/** /**
* A guard that considers safe a string being exactly compared to a trusted value. * Holds if `g` is guard that compares a string to a trusted value.
*/ */
private class ExactStringPathMatchGuard extends PathTraversalBarrierGuard instanceof MethodAccess { private predicate exactStringPathMatchGuard(Guard g, Expr e, boolean branch) {
ExactStringPathMatchGuard() { exists(MethodAccess ma |
super.getMethod().getDeclaringType() instanceof TypeString and ma = g and
super.getMethod().getName() = ["equals", "equalsIgnoreCase"] ma.getMethod().getDeclaringType() instanceof TypeString and
} ma.getMethod().getName() = ["equals", "equalsIgnoreCase"] and
e = ma.getQualifier() and
override predicate checks(Expr e, boolean branch) {
e = super.getQualifier() and
branch = true branch = true
)
}
private class ExactStringPathMatchSanitizer extends PathTraversalSanitizer {
ExactStringPathMatchSanitizer() {
this = DataFlow::BarrierGuard<exactStringPathMatchGuard/3>::getABarrierNode()
} }
} }
@@ -42,41 +53,45 @@ private class AllowListGuard extends Guard instanceof MethodAccess {
} }
/** /**
* A guard that considers a path safe because it is checked against an allowlist of partial trusted values. * Holds if `g` is a guard that considers a path safe because it is checked against an allowlist of partial trusted values.
* This requires additional protection against path traversal, either another guard (`PathTraversalGuard`) * This requires additional protection against path traversal, either another guard (`PathTraversalGuard`)
* or a sanitizer (`PathNormalizeSanitizer`), to ensure any internal `..` components are removed from the path. * or a sanitizer (`PathNormalizeSanitizer`), to ensure any internal `..` components are removed from the path.
*/ */
private class AllowListBarrierGuard extends PathTraversalBarrierGuard instanceof AllowListGuard { private predicate allowListGuard(Guard g, Expr e, boolean branch) {
override predicate checks(Expr e, boolean branch) { e = g.(AllowListGuard).getCheckedExpr() and
e = super.getCheckedExpr() and branch = true and
branch = true and (
( // Either a path normalization sanitizer comes before the guard,
// Either a path normalization sanitizer comes before the guard, exists(PathNormalizeSanitizer sanitizer | DataFlow::localExprFlow(sanitizer, e))
exists(PathNormalizeSanitizer sanitizer | DataFlow::localExprFlow(sanitizer, e)) or
or // or a check like `!path.contains("..")` comes before the guard
// or a check like `!path.contains("..")` comes before the guard exists(PathTraversalGuard previousGuard |
exists(PathTraversalGuard previousGuard | DataFlow::localExprFlow(previousGuard.getCheckedExpr(), e) and
DataFlow::localExprFlow(previousGuard.getCheckedExpr(), e) and previousGuard.controls(g.getBasicBlock().(ConditionBlock), false)
previousGuard.controls(this.getBasicBlock().(ConditionBlock), false)
)
) )
} )
}
private class AllowListSanitizer extends PathTraversalSanitizer {
AllowListSanitizer() { this = DataFlow::BarrierGuard<allowListGuard/3>::getABarrierNode() }
} }
/** /**
* A guard that considers a path safe because it is checked for `..` components, having previously * Holds if `g` is a guard that considers a path safe because it is checked for `..` components, having previously
* been checked for a trusted prefix. * been checked for a trusted prefix.
*/ */
private class DotDotCheckBarrierGuard extends PathTraversalBarrierGuard instanceof PathTraversalGuard { private predicate dotDotCheckGuard(Guard g, Expr e, boolean branch) {
override predicate checks(Expr e, boolean branch) { e = g.(PathTraversalGuard).getCheckedExpr() and
e = super.getCheckedExpr() and branch = false and
branch = false and // The same value has previously been checked against a list of allowed prefixes:
// The same value has previously been checked against a list of allowed prefixes: exists(AllowListGuard previousGuard |
exists(AllowListGuard previousGuard | DataFlow::localExprFlow(previousGuard.getCheckedExpr(), e) and
DataFlow::localExprFlow(previousGuard.getCheckedExpr(), e) and previousGuard.controls(g.getBasicBlock().(ConditionBlock), true)
previousGuard.controls(this.getBasicBlock().(ConditionBlock), true) )
) }
}
private class DotDotCheckSanitizer extends PathTraversalSanitizer {
DotDotCheckSanitizer() { this = DataFlow::BarrierGuard<dotDotCheckGuard/3>::getABarrierNode() }
} }
private class BlockListGuard extends Guard instanceof MethodAccess { private class BlockListGuard extends Guard instanceof MethodAccess {
@@ -89,40 +104,44 @@ private class BlockListGuard extends Guard instanceof MethodAccess {
} }
/** /**
* A guard that considers a string safe because it is checked against a blocklist of known dangerous values. * Holds if `g` is a guard that considers a string safe because it is checked against a blocklist of known dangerous values.
* This requires a prior check for URL encoding concealing a forbidden value, either a guard (`UrlEncodingGuard`) * This requires a prior check for URL encoding concealing a forbidden value, either a guard (`UrlEncodingGuard`)
* or a sanitizer (`UrlDecodeSanitizer`). * or a sanitizer (`UrlDecodeSanitizer`).
*/ */
private class BlockListBarrierGuard extends PathTraversalBarrierGuard instanceof BlockListGuard { private predicate blockListGuard(Guard g, Expr e, boolean branch) {
override predicate checks(Expr e, boolean branch) { e = g.(BlockListGuard).getCheckedExpr() and
e = super.getCheckedExpr() and branch = false and
branch = false and (
( // Either `e` has been URL decoded:
// Either `e` has been URL decoded: exists(UrlDecodeSanitizer sanitizer | DataFlow::localExprFlow(sanitizer, e))
exists(UrlDecodeSanitizer sanitizer | DataFlow::localExprFlow(sanitizer, e)) or
or // or `e` has previously been checked for URL encoding sequences:
// or `e` has previously been checked for URL encoding sequences: exists(UrlEncodingGuard previousGuard |
exists(UrlEncodingGuard previousGuard | DataFlow::localExprFlow(previousGuard.getCheckedExpr(), e) and
DataFlow::localExprFlow(previousGuard.getCheckedExpr(), e) and previousGuard.controls(g.getBasicBlock(), false)
previousGuard.controls(this.getBasicBlock(), false)
)
) )
} )
}
private class BlockListSanitizer extends PathTraversalSanitizer {
BlockListSanitizer() { this = DataFlow::BarrierGuard<blockListGuard/3>::getABarrierNode() }
} }
/** /**
* A guard that considers a string safe because it is checked for URL encoding sequences, * Holds if `g` is a guard that considers a string safe because it is checked for URL encoding sequences,
* having previously been checked against a block-list of forbidden values. * having previously been checked against a block-list of forbidden values.
*/ */
private class UrlEncodingBarrierGuard extends PathTraversalBarrierGuard instanceof UrlEncodingGuard { private predicate urlEncodingGuard(Guard g, Expr e, boolean branch) {
override predicate checks(Expr e, boolean branch) { e = g.(UrlEncodingGuard).getCheckedExpr() and
e = super.getCheckedExpr() and branch = false and
branch = false and exists(BlockListGuard previousGuard |
exists(BlockListGuard previousGuard | DataFlow::localExprFlow(previousGuard.getCheckedExpr(), e) and
DataFlow::localExprFlow(previousGuard.getCheckedExpr(), e) and previousGuard.controls(g.getBasicBlock(), false)
previousGuard.controls(this.getBasicBlock(), false) )
) }
}
private class UrlEncodingSanitizer extends PathTraversalSanitizer {
UrlEncodingSanitizer() { this = DataFlow::BarrierGuard<urlEncodingGuard/3>::getABarrierNode() }
} }
/** /**