mirror of
https://github.com/github/codeql.git
synced 2026-04-25 16:55:19 +02:00
Merge pull request #12721 from egregius313/egregius313/java/move-configurations-to-libraries
Java: Move more dataflow configurations to `*Query.qll` files
This commit is contained in:
@@ -12,6 +12,7 @@
|
||||
* external/cwe/cwe-088
|
||||
*/
|
||||
|
||||
import java
|
||||
import semmle.code.java.security.CommandLineQuery
|
||||
import LocalUserInputToArgumentToExecFlow::PathGraph
|
||||
|
||||
|
||||
@@ -12,17 +12,7 @@
|
||||
*/
|
||||
|
||||
import java
|
||||
import semmle.code.java.dataflow.FlowSources
|
||||
import semmle.code.java.security.XSS
|
||||
|
||||
module XssLocalConfig implements DataFlow::ConfigSig {
|
||||
predicate isSource(DataFlow::Node source) { source instanceof LocalUserInput }
|
||||
|
||||
predicate isSink(DataFlow::Node sink) { sink instanceof XssSink }
|
||||
}
|
||||
|
||||
module XssLocalFlow = TaintTracking::Global<XssLocalConfig>;
|
||||
|
||||
import semmle.code.java.security.XssLocalQuery
|
||||
import XssLocalFlow::PathGraph
|
||||
|
||||
from XssLocalFlow::PathNode source, XssLocalFlow::PathNode sink
|
||||
|
||||
@@ -15,28 +15,7 @@
|
||||
import java
|
||||
import semmle.code.java.security.SqlConcatenatedLib
|
||||
import semmle.code.java.security.SqlInjectionQuery
|
||||
|
||||
class UncontrolledStringBuilderSource extends DataFlow::ExprNode {
|
||||
UncontrolledStringBuilderSource() {
|
||||
exists(StringBuilderVar sbv |
|
||||
uncontrolledStringBuilderQuery(sbv, _) and
|
||||
this.getExpr() = sbv.getToStringCall()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
module UncontrolledStringBuilderSourceFlowConfig implements DataFlow::ConfigSig {
|
||||
predicate isSource(DataFlow::Node src) { src instanceof UncontrolledStringBuilderSource }
|
||||
|
||||
predicate isSink(DataFlow::Node sink) { sink instanceof QueryInjectionSink }
|
||||
|
||||
predicate isBarrier(DataFlow::Node node) {
|
||||
node.getType() instanceof PrimitiveType or node.getType() instanceof BoxedType
|
||||
}
|
||||
}
|
||||
|
||||
module UncontrolledStringBuilderSourceFlow =
|
||||
TaintTracking::Global<UncontrolledStringBuilderSourceFlowConfig>;
|
||||
import semmle.code.java.security.SqlConcatenatedQuery
|
||||
|
||||
from QueryInjectionSink query, Expr uncontrolled
|
||||
where
|
||||
|
||||
@@ -12,27 +12,8 @@
|
||||
* external/cwe/cwe-564
|
||||
*/
|
||||
|
||||
import semmle.code.java.Expr
|
||||
import semmle.code.java.dataflow.FlowSources
|
||||
import semmle.code.java.security.SqlInjectionQuery
|
||||
|
||||
module LocalUserInputToQueryInjectionFlowConfig implements DataFlow::ConfigSig {
|
||||
predicate isSource(DataFlow::Node src) { src instanceof LocalUserInput }
|
||||
|
||||
predicate isSink(DataFlow::Node sink) { sink instanceof QueryInjectionSink }
|
||||
|
||||
predicate isBarrier(DataFlow::Node node) {
|
||||
node.getType() instanceof PrimitiveType or node.getType() instanceof BoxedType
|
||||
}
|
||||
|
||||
predicate isAdditionalFlowStep(DataFlow::Node node1, DataFlow::Node node2) {
|
||||
any(AdditionalQueryInjectionTaintStep s).step(node1, node2)
|
||||
}
|
||||
}
|
||||
|
||||
module LocalUserInputToQueryInjectionFlow =
|
||||
TaintTracking::Global<LocalUserInputToQueryInjectionFlowConfig>;
|
||||
|
||||
import java
|
||||
import semmle.code.java.security.SqlTaintedLocalQuery
|
||||
import LocalUserInputToQueryInjectionFlow::PathGraph
|
||||
|
||||
from
|
||||
|
||||
@@ -12,26 +12,11 @@
|
||||
*/
|
||||
|
||||
import java
|
||||
import semmle.code.java.dataflow.FlowSources
|
||||
import semmle.code.java.security.ResponseSplitting
|
||||
import semmle.code.java.security.ResponseSplittingLocalQuery
|
||||
import ResponseSplittingLocalFlow::PathGraph
|
||||
|
||||
module ResponseSplittingLocalConfig implements DataFlow::ConfigSig {
|
||||
predicate isSource(DataFlow::Node source) { source instanceof LocalUserInput }
|
||||
|
||||
predicate isSink(DataFlow::Node sink) { sink instanceof HeaderSplittingSink }
|
||||
|
||||
predicate isBarrier(DataFlow::Node node) {
|
||||
node.getType() instanceof PrimitiveType or
|
||||
node.getType() instanceof BoxedType
|
||||
}
|
||||
}
|
||||
|
||||
module ResponseSplitting = TaintTracking::Global<ResponseSplittingLocalConfig>;
|
||||
|
||||
import ResponseSplitting::PathGraph
|
||||
|
||||
from ResponseSplitting::PathNode source, ResponseSplitting::PathNode sink
|
||||
where ResponseSplitting::flowPath(source, sink)
|
||||
from ResponseSplittingLocalFlow::PathNode source, ResponseSplittingLocalFlow::PathNode sink
|
||||
where ResponseSplittingLocalFlow::flowPath(source, sink)
|
||||
select sink.getNode(), source, sink,
|
||||
"This header depends on a $@, which may cause a response-splitting vulnerability.",
|
||||
source.getNode(), "user-provided value"
|
||||
|
||||
@@ -1,155 +0,0 @@
|
||||
import java
|
||||
import semmle.code.java.dataflow.DataFlow
|
||||
import semmle.code.java.dataflow.DefUse
|
||||
import semmle.code.java.security.RandomDataSource
|
||||
private import BoundingChecks
|
||||
|
||||
/**
|
||||
* If the `Array` accessed by the `ArrayAccess` is a fixed size, return the array size.
|
||||
*/
|
||||
int fixedArraySize(ArrayAccess arrayAccess) {
|
||||
exists(Variable v |
|
||||
v.getAnAccess() = arrayAccess.getArray() and
|
||||
result = v.getAnAssignedValue().(ArrayCreationExpr).getFirstDimensionSize()
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if an `ArrayIndexOutOfBoundsException` is ever caught.
|
||||
*/
|
||||
private predicate arrayIndexOutOfBoundExceptionCaught(ArrayAccess arrayAccess) {
|
||||
exists(TryStmt ts, CatchClause cc, RefType exc |
|
||||
(
|
||||
ts.getBlock().getAChild*() = arrayAccess.getEnclosingStmt() or
|
||||
ts.getAResourceDecl().getAChild*() = arrayAccess.getEnclosingStmt() or
|
||||
ts.getAResourceExpr().getAChildExpr*() = arrayAccess
|
||||
) and
|
||||
cc = ts.getACatchClause() and
|
||||
exc = cc.getVariable().getType() and
|
||||
exc.hasQualifiedName("java.lang", "ArrayIndexOutOfBoundsException")
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* A pointless loop, of the type seen frequently in Juliet tests, of the form:
|
||||
*
|
||||
* ```
|
||||
* while(true) {
|
||||
* ...
|
||||
* break;
|
||||
* }
|
||||
* ```
|
||||
*/
|
||||
class PointlessLoop extends WhileStmt {
|
||||
PointlessLoop() {
|
||||
this.getCondition().(BooleanLiteral).getBooleanValue() = true and
|
||||
// The only `break` must be the last statement.
|
||||
forall(BreakStmt break | break.getTarget() = this |
|
||||
this.getStmt().(BlockStmt).getLastStmt() = break
|
||||
) and
|
||||
// No `continue` statements.
|
||||
not exists(ContinueStmt continue | continue.getTarget() = this)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* An `ArrayAccess` for which we can determine whether the index is appropriately bound checked.
|
||||
*
|
||||
* We only consider first dimension array accesses, and we only consider indices in loops, if it's
|
||||
* obvious that the loop only executes once.
|
||||
*/
|
||||
class CheckableArrayAccess extends ArrayAccess {
|
||||
CheckableArrayAccess() {
|
||||
// We are not interested in array accesses that don't access the first dimension.
|
||||
not this.getArray() instanceof ArrayAccess and
|
||||
// Array accesses within loops can make it difficult to verify whether the index is checked
|
||||
// prior to access. Ignore "pointless" loops of the sort found in Juliet test cases.
|
||||
not exists(LoopStmt loop |
|
||||
loop.getBody().getAChild*() = this.getEnclosingStmt() and
|
||||
not loop instanceof PointlessLoop
|
||||
) and
|
||||
// The possible exception is not caught
|
||||
not arrayIndexOutOfBoundExceptionCaught(this)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if we believe this indexing expression can throw an `ArrayIndexOutOfBoundsException`.
|
||||
*/
|
||||
predicate canThrowOutOfBounds(Expr index) {
|
||||
index = this.getIndexExpr() and
|
||||
not (
|
||||
// There is a condition dominating this expression ensuring that the index is >= 0.
|
||||
lowerBound(index) >= 0 and
|
||||
// There is a condition dominating this expression that ensures the index is less than the length.
|
||||
lessthanLength(this)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if we believe this indexing expression can throw an `ArrayIndexOutOfBoundsException` due
|
||||
* to the array being initialized with `sizeExpr`, which may be zero.
|
||||
*/
|
||||
predicate canThrowOutOfBoundsDueToEmptyArray(Expr sizeExpr, ArrayCreationExpr arrayCreation) {
|
||||
// Find an `ArrayCreationExpr` for the array used in this indexing operation.
|
||||
exists(VariableAssign assign |
|
||||
assign.getSource() = arrayCreation and
|
||||
defUsePair(assign, this.getArray())
|
||||
) and
|
||||
// If the array access is protected by a conditional that verifies the index is less than the array
|
||||
// length, then the array will never be accessed if the size is zero.
|
||||
not lessthanLength(this) and
|
||||
// Verify that the size expression is never checked to be greater than 0.
|
||||
sizeExpr = arrayCreation.getDimension(0) and
|
||||
not lowerBound(sizeExpr) > 0
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A source of "flow" which has an upper or lower bound.
|
||||
*/
|
||||
abstract class BoundedFlowSource extends DataFlow::Node {
|
||||
/**
|
||||
* Return a lower bound for the input, if possible.
|
||||
*/
|
||||
abstract int lowerBound();
|
||||
|
||||
/**
|
||||
* Return an upper bound for the input, if possible.
|
||||
*/
|
||||
abstract int upperBound();
|
||||
|
||||
/**
|
||||
* Return a description for this flow source, suitable for putting in an alert message.
|
||||
*/
|
||||
abstract string getDescription();
|
||||
}
|
||||
|
||||
/**
|
||||
* Input that is constructed using a random value.
|
||||
*/
|
||||
class RandomValueFlowSource extends BoundedFlowSource {
|
||||
RandomDataSource nextAccess;
|
||||
|
||||
RandomValueFlowSource() { this.asExpr() = nextAccess }
|
||||
|
||||
override int lowerBound() { result = nextAccess.getLowerBound() }
|
||||
|
||||
override int upperBound() { result = nextAccess.getUpperBound() }
|
||||
|
||||
override string getDescription() { result = "Random value" }
|
||||
}
|
||||
|
||||
/**
|
||||
* A compile time constant expression that evaluates to a numeric type.
|
||||
*/
|
||||
class NumericLiteralFlowSource extends BoundedFlowSource {
|
||||
NumericLiteralFlowSource() { exists(this.asExpr().(CompileTimeConstantExpr).getIntValue()) }
|
||||
|
||||
override int lowerBound() { result = this.asExpr().(CompileTimeConstantExpr).getIntValue() }
|
||||
|
||||
override int upperBound() { result = this.asExpr().(CompileTimeConstantExpr).getIntValue() }
|
||||
|
||||
override string getDescription() {
|
||||
result = "Literal value " + this.asExpr().(CompileTimeConstantExpr).getIntValue()
|
||||
}
|
||||
}
|
||||
@@ -1,65 +0,0 @@
|
||||
/**
|
||||
* Provides classes and predicates for determining upper and lower bounds on a value determined by bounding checks that
|
||||
* have been made on dominant paths.
|
||||
*/
|
||||
|
||||
import java
|
||||
private import semmle.code.java.controlflow.Guards
|
||||
|
||||
/**
|
||||
* Holds if the given `ComparisonExpr` is thought to be true when `VarAccess` is accessed.
|
||||
*/
|
||||
private predicate conditionHolds(ComparisonExpr ce, VarAccess va) {
|
||||
exists(ConditionBlock cond |
|
||||
cond.getCondition() = ce and
|
||||
cond.controls(va.getBasicBlock(), true)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine an inclusive lower-bound - if possible - for the value accessed by the given `VarAccess`,
|
||||
* based upon the conditionals that hold at the point the variable is accessed.
|
||||
*/
|
||||
int lowerBound(VarAccess va) {
|
||||
exists(ComparisonExpr greaterThanValue |
|
||||
// This condition should hold when the variable is later accessed.
|
||||
conditionHolds(greaterThanValue, va)
|
||||
|
|
||||
greaterThanValue.getGreaterOperand() = va.getVariable().getAnAccess() and
|
||||
if greaterThanValue.isStrict()
|
||||
then
|
||||
// value > i, so value has a lower bound of i + 1
|
||||
result = greaterThanValue.getLesserOperand().(CompileTimeConstantExpr).getIntValue() + 1
|
||||
else
|
||||
// value >= i, so value has a lower bound of i
|
||||
result = greaterThanValue.getLesserOperand().(CompileTimeConstantExpr).getIntValue()
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if the index expression is a `VarAccess`, where the variable has been confirmed to be less
|
||||
* than the length.
|
||||
*/
|
||||
predicate lessthanLength(ArrayAccess a) {
|
||||
exists(ComparisonExpr lessThanLength, VarAccess va |
|
||||
va = a.getIndexExpr() and
|
||||
conditionHolds(lessThanLength, va)
|
||||
|
|
||||
lessThanLength.getGreaterOperand().(FieldAccess).getQualifier() = arrayReference(a) and
|
||||
lessThanLength.getGreaterOperand().(FieldAccess).getField().hasName("length") and
|
||||
lessThanLength.getLesserOperand() = va.getVariable().getAnAccess() and
|
||||
lessThanLength.isStrict()
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Return all other references to the array accessed in the `ArrayAccess`.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
private Expr arrayReference(ArrayAccess arrayAccess) {
|
||||
// Array is stored in a variable.
|
||||
result = arrayAccess.getArray().(VarAccess).getVariable().getAnAccess()
|
||||
or
|
||||
// Array is returned from a method.
|
||||
result.(MethodAccess).getMethod() = arrayAccess.getArray().(MethodAccess).getMethod()
|
||||
}
|
||||
@@ -11,20 +11,8 @@
|
||||
*/
|
||||
|
||||
import java
|
||||
import ArraySizing
|
||||
import semmle.code.java.dataflow.FlowSources
|
||||
|
||||
private module ImproperValidationOfArrayConstructionConfig implements DataFlow::ConfigSig {
|
||||
predicate isSource(DataFlow::Node source) { source instanceof RemoteFlowSource }
|
||||
|
||||
predicate isSink(DataFlow::Node sink) {
|
||||
any(CheckableArrayAccess caa).canThrowOutOfBoundsDueToEmptyArray(sink.asExpr(), _)
|
||||
}
|
||||
}
|
||||
|
||||
module ImproperValidationOfArrayConstructionFlow =
|
||||
TaintTracking::Global<ImproperValidationOfArrayConstructionConfig>;
|
||||
|
||||
import semmle.code.java.security.internal.ArraySizing
|
||||
import semmle.code.java.security.ImproperValidationOfArrayConstructionQuery
|
||||
import ImproperValidationOfArrayConstructionFlow::PathGraph
|
||||
|
||||
from
|
||||
|
||||
@@ -12,23 +12,8 @@
|
||||
*/
|
||||
|
||||
import java
|
||||
import ArraySizing
|
||||
import semmle.code.java.dataflow.TaintTracking
|
||||
|
||||
module BoundedFlowSourceConfig implements DataFlow::ConfigSig {
|
||||
predicate isSource(DataFlow::Node source) {
|
||||
source instanceof BoundedFlowSource and
|
||||
// There is not a fixed lower bound which is greater than zero.
|
||||
not source.(BoundedFlowSource).lowerBound() > 0
|
||||
}
|
||||
|
||||
predicate isSink(DataFlow::Node sink) {
|
||||
any(CheckableArrayAccess caa).canThrowOutOfBoundsDueToEmptyArray(sink.asExpr(), _)
|
||||
}
|
||||
}
|
||||
|
||||
module BoundedFlowSourceFlow = DataFlow::Global<BoundedFlowSourceConfig>;
|
||||
|
||||
import semmle.code.java.security.internal.ArraySizing
|
||||
import semmle.code.java.security.ImproperValidationOfArrayConstructionCodeSpecifiedQuery
|
||||
import BoundedFlowSourceFlow::PathGraph
|
||||
|
||||
from
|
||||
|
||||
@@ -12,20 +12,8 @@
|
||||
*/
|
||||
|
||||
import java
|
||||
import ArraySizing
|
||||
import semmle.code.java.dataflow.FlowSources
|
||||
|
||||
module ImproperValidationOfArrayConstructionLocalConfig implements DataFlow::ConfigSig {
|
||||
predicate isSource(DataFlow::Node source) { source instanceof LocalUserInput }
|
||||
|
||||
predicate isSink(DataFlow::Node sink) {
|
||||
any(CheckableArrayAccess caa).canThrowOutOfBoundsDueToEmptyArray(sink.asExpr(), _)
|
||||
}
|
||||
}
|
||||
|
||||
module ImproperValidationOfArrayConstructionLocalFlow =
|
||||
TaintTracking::Global<ImproperValidationOfArrayConstructionLocalConfig>;
|
||||
|
||||
import semmle.code.java.security.internal.ArraySizing
|
||||
import semmle.code.java.security.ImproperValidationOfArrayConstructionLocalQuery
|
||||
import ImproperValidationOfArrayConstructionLocalFlow::PathGraph
|
||||
|
||||
from
|
||||
|
||||
@@ -11,22 +11,8 @@
|
||||
*/
|
||||
|
||||
import java
|
||||
import ArraySizing
|
||||
import semmle.code.java.dataflow.FlowSources
|
||||
|
||||
module ImproperValidationOfArrayIndexConfig implements DataFlow::ConfigSig {
|
||||
predicate isSource(DataFlow::Node source) { source instanceof RemoteFlowSource }
|
||||
|
||||
predicate isSink(DataFlow::Node sink) {
|
||||
any(CheckableArrayAccess caa).canThrowOutOfBounds(sink.asExpr())
|
||||
}
|
||||
|
||||
predicate isBarrier(DataFlow::Node node) { node.getType() instanceof BooleanType }
|
||||
}
|
||||
|
||||
module ImproperValidationOfArrayIndexFlow =
|
||||
TaintTracking::Global<ImproperValidationOfArrayIndexConfig>;
|
||||
|
||||
import semmle.code.java.security.internal.ArraySizing
|
||||
import semmle.code.java.security.ImproperValidationOfArrayIndexQuery
|
||||
import ImproperValidationOfArrayIndexFlow::PathGraph
|
||||
|
||||
from
|
||||
|
||||
@@ -12,20 +12,9 @@
|
||||
*/
|
||||
|
||||
import java
|
||||
import ArraySizing
|
||||
import BoundingChecks
|
||||
import semmle.code.java.dataflow.TaintTracking
|
||||
|
||||
module BoundedFlowSourceConfig implements DataFlow::ConfigSig {
|
||||
predicate isSource(DataFlow::Node source) { source instanceof BoundedFlowSource }
|
||||
|
||||
predicate isSink(DataFlow::Node sink) {
|
||||
exists(CheckableArrayAccess arrayAccess | arrayAccess.canThrowOutOfBounds(sink.asExpr()))
|
||||
}
|
||||
}
|
||||
|
||||
module BoundedFlowSourceFlow = DataFlow::Global<BoundedFlowSourceConfig>;
|
||||
|
||||
import semmle.code.java.security.internal.ArraySizing
|
||||
import semmle.code.java.security.internal.BoundingChecks
|
||||
import semmle.code.java.security.ImproperValidationOfArrayIndexCodeSpecifiedQuery
|
||||
import BoundedFlowSourceFlow::PathGraph
|
||||
|
||||
from
|
||||
|
||||
@@ -12,20 +12,8 @@
|
||||
*/
|
||||
|
||||
import java
|
||||
import ArraySizing
|
||||
import semmle.code.java.dataflow.FlowSources
|
||||
|
||||
module ImproperValidationOfArrayIndexLocalConfig implements DataFlow::ConfigSig {
|
||||
predicate isSource(DataFlow::Node source) { source instanceof LocalUserInput }
|
||||
|
||||
predicate isSink(DataFlow::Node sink) {
|
||||
any(CheckableArrayAccess caa).canThrowOutOfBounds(sink.asExpr())
|
||||
}
|
||||
}
|
||||
|
||||
module ImproperValidationOfArrayIndexLocalFlow =
|
||||
TaintTracking::Global<ImproperValidationOfArrayIndexLocalConfig>;
|
||||
|
||||
import semmle.code.java.security.internal.ArraySizing
|
||||
import semmle.code.java.security.ImproperValidationOfArrayIndexLocalQuery
|
||||
import ImproperValidationOfArrayIndexLocalFlow::PathGraph
|
||||
|
||||
from
|
||||
|
||||
@@ -11,20 +11,8 @@
|
||||
*/
|
||||
|
||||
import java
|
||||
import semmle.code.java.dataflow.FlowSources
|
||||
import semmle.code.java.StringFormat
|
||||
|
||||
module ExternallyControlledFormatStringLocalConfig implements DataFlow::ConfigSig {
|
||||
predicate isSource(DataFlow::Node source) { source instanceof LocalUserInput }
|
||||
|
||||
predicate isSink(DataFlow::Node sink) {
|
||||
sink.asExpr() = any(StringFormat formatCall).getFormatArgument()
|
||||
}
|
||||
}
|
||||
|
||||
module ExternallyControlledFormatStringLocalFlow =
|
||||
TaintTracking::Global<ExternallyControlledFormatStringLocalConfig>;
|
||||
|
||||
import semmle.code.java.security.ExternallyControlledFormatStringLocalQuery
|
||||
import ExternallyControlledFormatStringLocalFlow::PathGraph
|
||||
|
||||
from
|
||||
|
||||
@@ -1,235 +0,0 @@
|
||||
import semmle.code.java.arithmetic.Overflow
|
||||
import semmle.code.java.controlflow.Guards
|
||||
private import semmle.code.java.dataflow.SSA
|
||||
private import semmle.code.java.dataflow.DataFlow
|
||||
private import semmle.code.java.dataflow.RangeAnalysis
|
||||
private import semmle.code.java.dataflow.RangeUtils
|
||||
private import semmle.code.java.dataflow.SignAnalysis
|
||||
private import semmle.code.java.controlflow.internal.GuardsLogic
|
||||
|
||||
/**
|
||||
* Holds if the type of `exp` is narrower than or equal to `numType`,
|
||||
* or there is an enclosing cast to a type at least as narrow as 'numType'.
|
||||
*/
|
||||
predicate narrowerThanOrEqualTo(ArithExpr exp, NumType numType) {
|
||||
exp.getType().(NumType).widerThan(numType)
|
||||
implies
|
||||
exists(CastingExpr cast | cast.getAChildExpr() = exp | numType.widerThanOrEqualTo(cast.getType()))
|
||||
}
|
||||
|
||||
private Guard sizeGuard(SsaVariable v, boolean branch, boolean upper) {
|
||||
exists(ComparisonExpr comp | comp = result |
|
||||
comp.getLesserOperand() = ssaRead(v, 0) and
|
||||
(
|
||||
branch = true and upper = true
|
||||
or
|
||||
branch = false and upper = false
|
||||
)
|
||||
or
|
||||
comp.getGreaterOperand() = ssaRead(v, 0) and
|
||||
(
|
||||
branch = true and upper = false
|
||||
or
|
||||
branch = false and upper = true
|
||||
)
|
||||
or
|
||||
exists(MethodAccess ma |
|
||||
ma.getMethod() instanceof MethodAbs and
|
||||
ma.getArgument(0) = ssaRead(v, 0) and
|
||||
(
|
||||
comp.getLesserOperand() = ma and branch = true
|
||||
or
|
||||
comp.getGreaterOperand() = ma and branch = false
|
||||
) and
|
||||
(upper = false or upper = true)
|
||||
)
|
||||
or
|
||||
// overflow test
|
||||
exists(AddExpr add, RValue use, Expr pos |
|
||||
use = ssaRead(v, 0) and
|
||||
add.hasOperands(use, pos) and
|
||||
positive(use) and
|
||||
positive(pos) and
|
||||
upper = true
|
||||
|
|
||||
comp.getLesserOperand() = add and
|
||||
comp.getGreaterOperand().(IntegerLiteral).getIntValue() = 0 and
|
||||
branch = false
|
||||
or
|
||||
comp.getGreaterOperand() = add and
|
||||
comp.getLesserOperand().(IntegerLiteral).getIntValue() = 0 and
|
||||
branch = true
|
||||
)
|
||||
)
|
||||
or
|
||||
result.isEquality(ssaRead(v, 0), _, branch) and
|
||||
(upper = true or upper = false)
|
||||
or
|
||||
exists(MethodAccess call, Method m, int ix |
|
||||
call = result and
|
||||
call.getArgument(ix) = ssaRead(v, 0) and
|
||||
call.getMethod().getSourceDeclaration() = m and
|
||||
m = customSizeGuard(ix, branch, upper)
|
||||
)
|
||||
}
|
||||
|
||||
private Guard derivedSizeGuard(SsaVariable v, boolean branch, boolean upper) {
|
||||
result = sizeGuard(v, branch, upper) or
|
||||
exists(boolean branch0 | implies_v3(result, branch, derivedSizeGuard(v, branch0, upper), branch0))
|
||||
}
|
||||
|
||||
private Method customSizeGuard(int index, boolean retval, boolean upper) {
|
||||
exists(Parameter p, SsaImplicitInit v |
|
||||
result.getReturnType().(PrimitiveType).hasName("boolean") and
|
||||
not result.isOverridable() and
|
||||
p.getCallable() = result and
|
||||
not p.isVarargs() and
|
||||
p.getType() instanceof NumericOrCharType and
|
||||
p.getPosition() = index and
|
||||
v.isParameterDefinition(p) and
|
||||
forex(ReturnStmt ret |
|
||||
ret.getEnclosingCallable() = result and
|
||||
exists(Expr res | res = ret.getResult() |
|
||||
not res.(BooleanLiteral).getBooleanValue() = retval.booleanNot()
|
||||
)
|
||||
|
|
||||
ret.getResult() = derivedSizeGuard(v, retval, upper)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `e` is bounded in a way that is likely to prevent overflow.
|
||||
*/
|
||||
predicate guardedLessThanSomething(Expr e) {
|
||||
exists(SsaVariable v, Guard guard, boolean branch |
|
||||
e = v.getAUse() and
|
||||
guard = sizeGuard(v.getAPhiInputOrPriorDef*(), branch, true) and
|
||||
guard.controls(e.getBasicBlock(), branch)
|
||||
)
|
||||
or
|
||||
negative(e)
|
||||
or
|
||||
e.(MethodAccess).getMethod() instanceof MethodMathMin
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `e` is bounded in a way that is likely to prevent underflow.
|
||||
*/
|
||||
predicate guardedGreaterThanSomething(Expr e) {
|
||||
exists(SsaVariable v, Guard guard, boolean branch |
|
||||
e = v.getAUse() and
|
||||
guard = sizeGuard(v.getAPhiInputOrPriorDef*(), branch, false) and
|
||||
guard.controls(e.getBasicBlock(), branch)
|
||||
)
|
||||
or
|
||||
positive(e)
|
||||
or
|
||||
e.(MethodAccess).getMethod() instanceof MethodMathMax
|
||||
}
|
||||
|
||||
/** Holds if `e` occurs in a context where it will be upcast to a wider type. */
|
||||
predicate upcastToWiderType(Expr e) {
|
||||
exists(NumType t1, NumType t2 |
|
||||
t1 = e.getType() and
|
||||
(
|
||||
t2.widerThan(t1)
|
||||
or
|
||||
t1 instanceof CharacterType and t2.getWidthRank() >= 3
|
||||
)
|
||||
|
|
||||
exists(Variable v | v.getAnAssignedValue() = e and t2 = v.getType())
|
||||
or
|
||||
exists(CastingExpr c | c.getExpr() = e and t2 = c.getType())
|
||||
or
|
||||
exists(ReturnStmt ret | ret.getResult() = e and t2 = ret.getEnclosingCallable().getReturnType())
|
||||
or
|
||||
exists(Parameter p | p.getAnArgument() = e and t2 = p.getType())
|
||||
or
|
||||
exists(ConditionalExpr cond | cond.getABranchExpr() = e | t2 = cond.getType())
|
||||
)
|
||||
}
|
||||
|
||||
/** Holds if the result of `exp` has certain bits filtered by a bitwise and. */
|
||||
private predicate inBitwiseAnd(Expr exp) {
|
||||
exists(AndBitwiseExpr a | a.getAnOperand() = exp) or
|
||||
inBitwiseAnd(exp.(LeftShiftExpr).getAnOperand()) or
|
||||
inBitwiseAnd(exp.(RightShiftExpr).getAnOperand()) or
|
||||
inBitwiseAnd(exp.(UnsignedRightShiftExpr).getAnOperand())
|
||||
}
|
||||
|
||||
/** Holds if overflow/underflow is irrelevant for this expression. */
|
||||
predicate overflowIrrelevant(Expr exp) {
|
||||
inBitwiseAnd(exp) or
|
||||
exp.getEnclosingCallable() instanceof HashCodeMethod
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `n` is unlikely to be part in a path from some source containing
|
||||
* numeric data to some arithmetic expression that may overflow/underflow.
|
||||
*/
|
||||
private predicate unlikelyNode(DataFlow::Node n) {
|
||||
n.getTypeBound() instanceof TypeObject and
|
||||
not exists(CastingExpr cast |
|
||||
DataFlow::localFlow(n, DataFlow::exprNode(cast.getExpr())) and
|
||||
cast.getType() instanceof NumericOrCharType
|
||||
)
|
||||
}
|
||||
|
||||
/** Holds if `n` is likely guarded against overflow. */
|
||||
predicate overflowBarrier(DataFlow::Node n) {
|
||||
n.getType() instanceof BooleanType or
|
||||
guardedLessThanSomething(n.asExpr()) or
|
||||
unlikelyNode(n) or
|
||||
upcastToWiderType(n.asExpr()) or
|
||||
overflowIrrelevant(n.asExpr())
|
||||
}
|
||||
|
||||
/** Holds if `n` is likely guarded against underflow. */
|
||||
predicate underflowBarrier(DataFlow::Node n) {
|
||||
n.getType() instanceof BooleanType or
|
||||
guardedGreaterThanSomething(n.asExpr()) or
|
||||
unlikelyNode(n) or
|
||||
upcastToWiderType(n.asExpr()) or
|
||||
overflowIrrelevant(n.asExpr())
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `use` is an operand of `exp` that acts as a sink for
|
||||
* overflow-related dataflow.
|
||||
*/
|
||||
predicate overflowSink(ArithExpr exp, VarAccess use) {
|
||||
exp.getAnOperand() = use and
|
||||
(
|
||||
// overflow unlikely for subtraction and division
|
||||
exp instanceof AddExpr or
|
||||
exp instanceof PreIncExpr or
|
||||
exp instanceof PostIncExpr or
|
||||
exp instanceof MulExpr
|
||||
) and
|
||||
not guardedLessThanSomething(use) and
|
||||
// Exclude widening conversions of tainted values due to binary numeric promotion (JLS 5.6.2)
|
||||
// unless there is an enclosing cast down to a narrower type.
|
||||
narrowerThanOrEqualTo(exp, use.getType()) and
|
||||
not overflowIrrelevant(exp)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `use` is an operand of `exp` that acts as a sink for
|
||||
* underflow-related dataflow.
|
||||
*/
|
||||
predicate underflowSink(ArithExpr exp, VarAccess use) {
|
||||
exp.getAnOperand() = use and
|
||||
(
|
||||
// underflow unlikely for addition and division
|
||||
exp.(SubExpr).getLeftOperand() = use or
|
||||
exp instanceof PreDecExpr or
|
||||
exp instanceof PostDecExpr or
|
||||
exp instanceof MulExpr
|
||||
) and
|
||||
not guardedGreaterThanSomething(use) and
|
||||
// Exclude widening conversions of tainted values due to binary numeric promotion (JLS 5.6.2)
|
||||
// unless there is an enclosing cast down to a narrower type.
|
||||
narrowerThanOrEqualTo(exp, use.getType()) and
|
||||
not overflowIrrelevant(exp)
|
||||
}
|
||||
@@ -13,28 +13,9 @@
|
||||
*/
|
||||
|
||||
import java
|
||||
import semmle.code.java.dataflow.FlowSources
|
||||
import ArithmeticCommon
|
||||
|
||||
module RemoteUserInputOverflowConfig implements DataFlow::ConfigSig {
|
||||
predicate isSource(DataFlow::Node source) { source instanceof RemoteFlowSource }
|
||||
|
||||
predicate isSink(DataFlow::Node sink) { overflowSink(_, sink.asExpr()) }
|
||||
|
||||
predicate isBarrier(DataFlow::Node n) { overflowBarrier(n) }
|
||||
}
|
||||
|
||||
module RemoteUserInputUnderflowConfig implements DataFlow::ConfigSig {
|
||||
predicate isSource(DataFlow::Node source) { source instanceof RemoteFlowSource }
|
||||
|
||||
predicate isSink(DataFlow::Node sink) { underflowSink(_, sink.asExpr()) }
|
||||
|
||||
predicate isBarrier(DataFlow::Node n) { underflowBarrier(n) }
|
||||
}
|
||||
|
||||
module RemoteUserInputOverflow = TaintTracking::Global<RemoteUserInputOverflowConfig>;
|
||||
|
||||
module RemoteUserInputUnderflow = TaintTracking::Global<RemoteUserInputUnderflowConfig>;
|
||||
import semmle.code.java.dataflow.DataFlow
|
||||
import semmle.code.java.security.ArithmeticCommon
|
||||
import semmle.code.java.security.ArithmeticTaintedQuery
|
||||
|
||||
module Flow =
|
||||
DataFlow::MergePathGraph<RemoteUserInputOverflow::PathNode, RemoteUserInputUnderflow::PathNode,
|
||||
|
||||
@@ -13,30 +13,9 @@
|
||||
*/
|
||||
|
||||
import java
|
||||
import semmle.code.java.dataflow.FlowSources
|
||||
import ArithmeticCommon
|
||||
|
||||
module ArithmeticTaintedLocalOverflowConfig implements DataFlow::ConfigSig {
|
||||
predicate isSource(DataFlow::Node source) { source instanceof LocalUserInput }
|
||||
|
||||
predicate isSink(DataFlow::Node sink) { overflowSink(_, sink.asExpr()) }
|
||||
|
||||
predicate isBarrier(DataFlow::Node n) { overflowBarrier(n) }
|
||||
}
|
||||
|
||||
module ArithmeticTaintedLocalOverflowFlow =
|
||||
TaintTracking::Global<ArithmeticTaintedLocalOverflowConfig>;
|
||||
|
||||
module ArithmeticTaintedLocalUnderflowConfig implements DataFlow::ConfigSig {
|
||||
predicate isSource(DataFlow::Node source) { source instanceof LocalUserInput }
|
||||
|
||||
predicate isSink(DataFlow::Node sink) { underflowSink(_, sink.asExpr()) }
|
||||
|
||||
predicate isBarrier(DataFlow::Node n) { underflowBarrier(n) }
|
||||
}
|
||||
|
||||
module ArithmeticTaintedLocalUnderflowFlow =
|
||||
TaintTracking::Global<ArithmeticTaintedLocalUnderflowConfig>;
|
||||
import semmle.code.java.dataflow.DataFlow
|
||||
import semmle.code.java.security.ArithmeticCommon
|
||||
import semmle.code.java.security.ArithmeticTaintedLocalQuery
|
||||
|
||||
module Flow =
|
||||
DataFlow::MergePathGraph<ArithmeticTaintedLocalOverflowFlow::PathNode,
|
||||
|
||||
@@ -13,38 +13,9 @@
|
||||
*/
|
||||
|
||||
import java
|
||||
import semmle.code.java.dataflow.TaintTracking
|
||||
import semmle.code.java.security.RandomQuery
|
||||
import semmle.code.java.security.SecurityTests
|
||||
import ArithmeticCommon
|
||||
|
||||
class TaintSource extends DataFlow::ExprNode {
|
||||
TaintSource() {
|
||||
exists(RandomDataSource m | not m.resultMayBeBounded() | m.getOutput() = this.getExpr())
|
||||
}
|
||||
}
|
||||
|
||||
module ArithmeticUncontrolledOverflowConfig implements DataFlow::ConfigSig {
|
||||
predicate isSource(DataFlow::Node source) { source instanceof TaintSource }
|
||||
|
||||
predicate isSink(DataFlow::Node sink) { overflowSink(_, sink.asExpr()) }
|
||||
|
||||
predicate isBarrier(DataFlow::Node n) { overflowBarrier(n) }
|
||||
}
|
||||
|
||||
module ArithmeticUncontrolledOverflowFlow =
|
||||
TaintTracking::Global<ArithmeticUncontrolledOverflowConfig>;
|
||||
|
||||
module ArithmeticUncontrolledUnderflowConfig implements DataFlow::ConfigSig {
|
||||
predicate isSource(DataFlow::Node source) { source instanceof TaintSource }
|
||||
|
||||
predicate isSink(DataFlow::Node sink) { underflowSink(_, sink.asExpr()) }
|
||||
|
||||
predicate isBarrier(DataFlow::Node n) { underflowBarrier(n) }
|
||||
}
|
||||
|
||||
module ArithmeticUncontrolledUnderflowFlow =
|
||||
TaintTracking::Global<ArithmeticUncontrolledUnderflowConfig>;
|
||||
import semmle.code.java.dataflow.DataFlow
|
||||
import semmle.code.java.security.ArithmeticCommon
|
||||
import semmle.code.java.security.ArithmeticUncontrolledQuery
|
||||
|
||||
module Flow =
|
||||
DataFlow::MergePathGraph<ArithmeticUncontrolledOverflowFlow::PathNode,
|
||||
|
||||
@@ -15,58 +15,14 @@
|
||||
|
||||
import java
|
||||
import semmle.code.java.dataflow.DataFlow
|
||||
import ArithmeticCommon
|
||||
|
||||
abstract class ExtremeValueField extends Field {
|
||||
ExtremeValueField() { this.getType() instanceof IntegralType }
|
||||
}
|
||||
|
||||
class MinValueField extends ExtremeValueField {
|
||||
MinValueField() { this.getName() = "MIN_VALUE" }
|
||||
}
|
||||
|
||||
class MaxValueField extends ExtremeValueField {
|
||||
MaxValueField() { this.getName() = "MAX_VALUE" }
|
||||
}
|
||||
|
||||
class ExtremeSource extends VarAccess {
|
||||
ExtremeSource() { this.getVariable() instanceof ExtremeValueField }
|
||||
}
|
||||
|
||||
module MaxValueFlowConfig implements DataFlow::ConfigSig {
|
||||
predicate isSource(DataFlow::Node source) {
|
||||
source.asExpr().(ExtremeSource).getVariable() instanceof MaxValueField
|
||||
}
|
||||
|
||||
predicate isSink(DataFlow::Node sink) { overflowSink(_, sink.asExpr()) }
|
||||
|
||||
predicate isBarrierIn(DataFlow::Node n) { isSource(n) }
|
||||
|
||||
predicate isBarrier(DataFlow::Node n) { overflowBarrier(n) }
|
||||
}
|
||||
|
||||
module MaxValueFlow = DataFlow::Global<MaxValueFlowConfig>;
|
||||
|
||||
module MinValueFlowConfig implements DataFlow::ConfigSig {
|
||||
predicate isSource(DataFlow::Node source) {
|
||||
source.asExpr().(ExtremeSource).getVariable() instanceof MinValueField
|
||||
}
|
||||
|
||||
predicate isSink(DataFlow::Node sink) { underflowSink(_, sink.asExpr()) }
|
||||
|
||||
predicate isBarrierIn(DataFlow::Node n) { isSource(n) }
|
||||
|
||||
predicate isBarrier(DataFlow::Node n) { underflowBarrier(n) }
|
||||
}
|
||||
|
||||
module MinValueFlow = DataFlow::Global<MinValueFlowConfig>;
|
||||
import semmle.code.java.security.ArithmeticCommon
|
||||
import semmle.code.java.security.ArithmeticWithExtremeValuesQuery
|
||||
import Flow::PathGraph
|
||||
|
||||
module Flow =
|
||||
DataFlow::MergePathGraph<MaxValueFlow::PathNode, MinValueFlow::PathNode, MaxValueFlow::PathGraph,
|
||||
MinValueFlow::PathGraph>;
|
||||
|
||||
import Flow::PathGraph
|
||||
|
||||
predicate query(
|
||||
Flow::PathNode source, Flow::PathNode sink, ArithExpr exp, string effect, Type srctyp
|
||||
) {
|
||||
|
||||
@@ -12,228 +12,9 @@
|
||||
*/
|
||||
|
||||
import java
|
||||
import semmle.code.java.os.OSCheck
|
||||
import TempDirUtils
|
||||
import semmle.code.java.dataflow.TaintTracking
|
||||
|
||||
abstract private class MethodFileSystemFileCreation extends Method {
|
||||
MethodFileSystemFileCreation() { this.getDeclaringType() instanceof TypeFile }
|
||||
}
|
||||
|
||||
private class MethodFileDirectoryCreation extends MethodFileSystemFileCreation {
|
||||
MethodFileDirectoryCreation() { this.hasName(["mkdir", "mkdirs"]) }
|
||||
}
|
||||
|
||||
private class MethodFileFileCreation extends MethodFileSystemFileCreation {
|
||||
MethodFileFileCreation() { this.hasName("createNewFile") }
|
||||
}
|
||||
|
||||
abstract private class FileCreationSink extends DataFlow::Node { }
|
||||
|
||||
/**
|
||||
* The qualifier of a call to one of `File`'s file-creating or directory-creating methods,
|
||||
* treated as a sink by `TempDirSystemGetPropertyToCreateConfig`.
|
||||
*/
|
||||
private class FileFileCreationSink extends FileCreationSink {
|
||||
FileFileCreationSink() {
|
||||
exists(MethodAccess ma |
|
||||
ma.getMethod() instanceof MethodFileSystemFileCreation and
|
||||
ma.getQualifier() = this.asExpr()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The argument to a call to one of `Files` file-creating or directory-creating methods,
|
||||
* treated as a sink by `TempDirSystemGetPropertyToCreateConfig`.
|
||||
*/
|
||||
private class FilesFileCreationSink extends FileCreationSink {
|
||||
FilesFileCreationSink() {
|
||||
exists(FilesVulnerableCreationMethodAccess ma | ma.getArgument(0) = this.asExpr())
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A call to a `Files` method that create files/directories without explicitly
|
||||
* setting the newly-created file or directory's permissions.
|
||||
*/
|
||||
private class FilesVulnerableCreationMethodAccess extends MethodAccess {
|
||||
FilesVulnerableCreationMethodAccess() {
|
||||
exists(Method m |
|
||||
m = this.getMethod() and
|
||||
m.getDeclaringType().hasQualifiedName("java.nio.file", "Files")
|
||||
|
|
||||
m.hasName(["write", "newBufferedWriter", "newOutputStream"])
|
||||
or
|
||||
m.hasName(["createFile", "createDirectory", "createDirectories"]) and
|
||||
this.getNumArgument() = 1
|
||||
or
|
||||
m.hasName("newByteChannel") and
|
||||
this.getNumArgument() = 2
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A call to a `File` method that create files/directories with a specific set of permissions explicitly set.
|
||||
* We can safely assume that any calls to these methods with explicit `PosixFilePermissions.asFileAttribute`
|
||||
* contains a certain level of intentionality behind it.
|
||||
*/
|
||||
private class FilesSanitizingCreationMethodAccess extends MethodAccess {
|
||||
FilesSanitizingCreationMethodAccess() {
|
||||
exists(Method m |
|
||||
m = this.getMethod() and
|
||||
m.getDeclaringType().hasQualifiedName("java.nio.file", "Files")
|
||||
|
|
||||
m.hasName(["createFile", "createDirectory", "createDirectories"]) and
|
||||
this.getNumArgument() = 2
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The temp directory argument to a call to `java.io.File::createTempFile`,
|
||||
* treated as a sink by `TempDirSystemGetPropertyToCreateConfig`.
|
||||
*/
|
||||
private class FileCreateTempFileSink extends FileCreationSink {
|
||||
FileCreateTempFileSink() {
|
||||
exists(MethodAccess ma |
|
||||
ma.getMethod() instanceof MethodFileCreateTempFile and ma.getArgument(2) = this.asExpr()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A sanitizer that holds when the program is definitely running under some version of Windows.
|
||||
*/
|
||||
abstract private class WindowsOsSanitizer extends DataFlow::Node { }
|
||||
|
||||
private class IsNotUnixSanitizer extends WindowsOsSanitizer {
|
||||
IsNotUnixSanitizer() { any(IsUnixGuard guard).controls(this.asExpr().getBasicBlock(), false) }
|
||||
}
|
||||
|
||||
private class IsWindowsSanitizer extends WindowsOsSanitizer {
|
||||
IsWindowsSanitizer() { any(IsWindowsGuard guard).controls(this.asExpr().getBasicBlock(), true) }
|
||||
}
|
||||
|
||||
private class IsSpecificWindowsSanitizer extends WindowsOsSanitizer {
|
||||
IsSpecificWindowsSanitizer() {
|
||||
any(IsSpecificWindowsVariant guard).controls(this.asExpr().getBasicBlock(), true)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A taint tracking configuration tracking the access of the system temporary directory
|
||||
* flowing to the creation of files or directories.
|
||||
*/
|
||||
module TempDirSystemGetPropertyToCreateConfig implements DataFlow::ConfigSig {
|
||||
predicate isSource(DataFlow::Node source) {
|
||||
source.asExpr() instanceof ExprSystemGetPropertyTempDirTainted
|
||||
}
|
||||
|
||||
predicate isSink(DataFlow::Node sink) {
|
||||
sink instanceof FileCreationSink and
|
||||
not TempDirSystemGetPropertyDirectlyToMkdir::flowTo(sink)
|
||||
}
|
||||
|
||||
predicate isBarrier(DataFlow::Node sanitizer) {
|
||||
exists(FilesSanitizingCreationMethodAccess sanitisingMethodAccess |
|
||||
sanitizer.asExpr() = sanitisingMethodAccess.getArgument(0)
|
||||
)
|
||||
or
|
||||
sanitizer instanceof WindowsOsSanitizer
|
||||
}
|
||||
}
|
||||
|
||||
module TempDirSystemGetPropertyToCreate =
|
||||
TaintTracking::Global<TempDirSystemGetPropertyToCreateConfig>;
|
||||
|
||||
/**
|
||||
* Configuration that tracks calls to to `mkdir` or `mkdirs` that are are directly on the temp directory system property.
|
||||
* Examples:
|
||||
* - `File tempDir = new File(System.getProperty("java.io.tmpdir")); tempDir.mkdir();`
|
||||
* - `File tempDir = new File(System.getProperty("java.io.tmpdir")); tempDir.mkdirs();`
|
||||
*
|
||||
* These are examples of code that is simply verifying that the temp directory exists.
|
||||
* As such, this code pattern is filtered out as an explicit vulnerability in
|
||||
* `TempDirSystemGetPropertyToCreateConfig::isSink`.
|
||||
*/
|
||||
module TempDirSystemGetPropertyDirectlyToMkdirConfig implements DataFlow::ConfigSig {
|
||||
predicate isSource(DataFlow::Node node) {
|
||||
exists(ExprSystemGetPropertyTempDirTainted propertyGetExpr, DataFlow::Node callSite |
|
||||
DataFlow::localFlow(DataFlow::exprNode(propertyGetExpr), callSite)
|
||||
|
|
||||
isFileConstructorArgument(callSite.asExpr(), node.asExpr(), 1)
|
||||
)
|
||||
}
|
||||
|
||||
predicate isSink(DataFlow::Node node) {
|
||||
exists(MethodAccess ma | ma.getMethod() instanceof MethodFileDirectoryCreation |
|
||||
ma.getQualifier() = node.asExpr()
|
||||
)
|
||||
}
|
||||
|
||||
predicate isBarrier(DataFlow::Node sanitizer) {
|
||||
isFileConstructorArgument(sanitizer.asExpr(), _, _)
|
||||
}
|
||||
}
|
||||
|
||||
module TempDirSystemGetPropertyDirectlyToMkdir =
|
||||
TaintTracking::Global<TempDirSystemGetPropertyDirectlyToMkdirConfig>;
|
||||
|
||||
//
|
||||
// Begin configuration for tracking single-method calls that are vulnerable.
|
||||
//
|
||||
/**
|
||||
* A `MethodAccess` against a method that creates a temporary file or directory in a shared temporary directory.
|
||||
*/
|
||||
abstract class MethodAccessInsecureFileCreation extends MethodAccess {
|
||||
/**
|
||||
* Gets the type of entity created (e.g. `file`, `directory`, ...).
|
||||
*/
|
||||
abstract string getFileSystemEntityType();
|
||||
|
||||
DataFlow::Node getNode() { result.asExpr() = this }
|
||||
}
|
||||
|
||||
/**
|
||||
* An insecure call to `java.io.File.createTempFile`.
|
||||
*/
|
||||
class MethodAccessInsecureFileCreateTempFile extends MethodAccessInsecureFileCreation {
|
||||
MethodAccessInsecureFileCreateTempFile() {
|
||||
this.getMethod() instanceof MethodFileCreateTempFile and
|
||||
(
|
||||
// `File.createTempFile(string, string)` always uses the default temporary directory
|
||||
this.getNumArgument() = 2
|
||||
or
|
||||
// The default temporary directory is used when the last argument of `File.createTempFile(string, string, File)` is `null`
|
||||
DataFlow::localExprFlow(any(NullLiteral n), this.getArgument(2))
|
||||
)
|
||||
}
|
||||
|
||||
override string getFileSystemEntityType() { result = "file" }
|
||||
}
|
||||
|
||||
/**
|
||||
* The `com.google.common.io.Files.createTempDir` method.
|
||||
*/
|
||||
class MethodGuavaFilesCreateTempFile extends Method {
|
||||
MethodGuavaFilesCreateTempFile() {
|
||||
this.getDeclaringType().hasQualifiedName("com.google.common.io", "Files") and
|
||||
this.hasName("createTempDir")
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A call to the `com.google.common.io.Files.createTempDir` method.
|
||||
*/
|
||||
class MethodAccessInsecureGuavaFilesCreateTempFile extends MethodAccessInsecureFileCreation {
|
||||
MethodAccessInsecureGuavaFilesCreateTempFile() {
|
||||
this.getMethod() instanceof MethodGuavaFilesCreateTempFile
|
||||
}
|
||||
|
||||
override string getFileSystemEntityType() { result = "directory" }
|
||||
}
|
||||
import semmle.code.java.dataflow.DataFlow
|
||||
import semmle.code.java.security.TempDirUtils
|
||||
import semmle.code.java.security.TempDirLocalInformationDisclosureQuery
|
||||
|
||||
/**
|
||||
* We include use of inherently insecure methods, which don't have any associated
|
||||
|
||||
@@ -1,83 +0,0 @@
|
||||
/**
|
||||
* Provides classes and predicates for reasoning about temporary file/directory creations.
|
||||
*/
|
||||
|
||||
import java
|
||||
private import semmle.code.java.environment.SystemProperty
|
||||
import semmle.code.java.dataflow.FlowSources
|
||||
|
||||
/**
|
||||
* A method or field access that returns a `String` or `File` that has been tainted by `System.getProperty("java.io.tmpdir")`.
|
||||
*/
|
||||
class ExprSystemGetPropertyTempDirTainted extends Expr {
|
||||
ExprSystemGetPropertyTempDirTainted() { this = getSystemProperty("java.io.tmpdir") }
|
||||
}
|
||||
|
||||
/**
|
||||
* A `java.io.File::createTempFile` method.
|
||||
*/
|
||||
class MethodFileCreateTempFile extends Method {
|
||||
MethodFileCreateTempFile() {
|
||||
this.getDeclaringType() instanceof TypeFile and
|
||||
this.hasName("createTempFile")
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `expDest` is some constructor call `new java.io.File(expSource)`, where the specific `File` constructor being used has `paramCount` parameters.
|
||||
*/
|
||||
predicate isFileConstructorArgument(Expr expSource, Expr exprDest, int paramCount) {
|
||||
exists(ConstructorCall construtorCall |
|
||||
construtorCall.getConstructedType() instanceof TypeFile and
|
||||
construtorCall.getArgument(0) = expSource and
|
||||
construtorCall = exprDest and
|
||||
construtorCall.getConstructor().getNumberOfParameters() = paramCount
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* A method call to `java.io.File::setReadable`.
|
||||
*/
|
||||
private class FileSetRedableMethodAccess extends MethodAccess {
|
||||
FileSetRedableMethodAccess() {
|
||||
exists(Method m | this.getMethod() = m |
|
||||
m.getDeclaringType() instanceof TypeFile and
|
||||
m.hasName("setReadable")
|
||||
)
|
||||
}
|
||||
|
||||
predicate isCallWithArguments(boolean arg1, boolean arg2) {
|
||||
this.isCallWithArgument(0, arg1) and this.isCallToSecondArgumentWithValue(arg2)
|
||||
}
|
||||
|
||||
private predicate isCallToSecondArgumentWithValue(boolean value) {
|
||||
this.getMethod().getNumberOfParameters() = 1 and value = true
|
||||
or
|
||||
this.isCallWithArgument(1, value)
|
||||
}
|
||||
|
||||
private predicate isCallWithArgument(int index, boolean arg) {
|
||||
DataFlow::localExprFlow(any(CompileTimeConstantExpr e | e.getBooleanValue() = arg),
|
||||
this.getArgument(index))
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Hold's if temporary directory's use is protected if there is an explicit call to
|
||||
* `setReadable(false, false)`, then `setRedabale(true, true)`.
|
||||
*/
|
||||
predicate isPermissionsProtectedTempDirUse(DataFlow::Node sink) {
|
||||
exists(FileSetRedableMethodAccess setReadable1, FileSetRedableMethodAccess setReadable2 |
|
||||
setReadable1.isCallWithArguments(false, false) and
|
||||
setReadable2.isCallWithArguments(true, true)
|
||||
|
|
||||
exists(DataFlow::Node setReadableNode1, DataFlow::Node setReadableNode2 |
|
||||
setReadableNode1.asExpr() = setReadable1.getQualifier() and
|
||||
setReadableNode2.asExpr() = setReadable2.getQualifier()
|
||||
|
|
||||
DataFlow::localFlow(sink, setReadableNode1) and // Flow from sink to setReadable(false, false)
|
||||
DataFlow::localFlow(sink, setReadableNode2) and // Flow from sink to setReadable(true, true)
|
||||
DataFlow::localFlow(setReadableNode1, setReadableNode2) // Flow from setReadable(false, false) to setReadable(true, true)
|
||||
)
|
||||
)
|
||||
}
|
||||
@@ -15,129 +15,11 @@
|
||||
|
||||
import java
|
||||
import semmle.code.java.dataflow.DataFlow
|
||||
import semmle.code.java.dataflow.TaintTracking
|
||||
import semmle.code.java.security.InformationLeak
|
||||
|
||||
/**
|
||||
* One of the `printStackTrace()` overloads on `Throwable`.
|
||||
*/
|
||||
class PrintStackTraceMethod extends Method {
|
||||
PrintStackTraceMethod() {
|
||||
this.getDeclaringType()
|
||||
.getSourceDeclaration()
|
||||
.getASourceSupertype*()
|
||||
.hasQualifiedName("java.lang", "Throwable") and
|
||||
this.getName() = "printStackTrace"
|
||||
}
|
||||
}
|
||||
|
||||
module ServletWriterSourceToPrintStackTraceMethodFlowConfig implements DataFlow::ConfigSig {
|
||||
predicate isSource(DataFlow::Node src) { src.asExpr() instanceof XssVulnerableWriterSource }
|
||||
|
||||
predicate isSink(DataFlow::Node sink) {
|
||||
exists(MethodAccess ma |
|
||||
sink.asExpr() = ma.getAnArgument() and ma.getMethod() instanceof PrintStackTraceMethod
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
module ServletWriterSourceToPrintStackTraceMethodFlow =
|
||||
TaintTracking::Global<ServletWriterSourceToPrintStackTraceMethodFlowConfig>;
|
||||
|
||||
/**
|
||||
* A call that uses `Throwable.printStackTrace()` on a stream that is connected
|
||||
* to external output.
|
||||
*/
|
||||
predicate printsStackToWriter(MethodAccess call) {
|
||||
exists(PrintStackTraceMethod printStackTrace |
|
||||
call.getMethod() = printStackTrace and
|
||||
ServletWriterSourceToPrintStackTraceMethodFlow::flowToExpr(call.getAnArgument())
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* A `PrintWriter` that wraps a given string writer. This pattern is used
|
||||
* in the most common idiom for converting a `Throwable` to a string.
|
||||
*/
|
||||
predicate printWriterOnStringWriter(Expr printWriter, Variable stringWriterVar) {
|
||||
printWriter.getType().(Class).hasQualifiedName("java.io", "PrintWriter") and
|
||||
stringWriterVar.getType().(Class).hasQualifiedName("java.io", "StringWriter") and
|
||||
(
|
||||
printWriter.(ClassInstanceExpr).getAnArgument() = stringWriterVar.getAnAccess() or
|
||||
printWriterOnStringWriter(printWriter.(VarAccess).getVariable().getInitializer(),
|
||||
stringWriterVar)
|
||||
)
|
||||
}
|
||||
|
||||
predicate stackTraceExpr(Expr exception, MethodAccess stackTraceString) {
|
||||
exists(Expr printWriter, Variable stringWriterVar, MethodAccess printStackCall |
|
||||
printWriterOnStringWriter(printWriter, stringWriterVar) and
|
||||
printStackCall.getMethod() instanceof PrintStackTraceMethod and
|
||||
printStackCall.getAnArgument() = printWriter and
|
||||
printStackCall.getQualifier() = exception and
|
||||
stackTraceString.getQualifier() = stringWriterVar.getAnAccess() and
|
||||
stackTraceString.getMethod() instanceof ToStringMethod
|
||||
)
|
||||
}
|
||||
|
||||
module StackTraceStringToHttpResponseSinkFlowConfig implements DataFlow::ConfigSig {
|
||||
predicate isSource(DataFlow::Node src) { stackTraceExpr(_, src.asExpr()) }
|
||||
|
||||
predicate isSink(DataFlow::Node sink) { sink instanceof InformationLeakSink }
|
||||
}
|
||||
|
||||
module StackTraceStringToHttpResponseSinkFlow =
|
||||
TaintTracking::Global<StackTraceStringToHttpResponseSinkFlowConfig>;
|
||||
|
||||
/**
|
||||
* A write of stack trace data to an external stream.
|
||||
*/
|
||||
predicate printsStackExternally(MethodAccess call, Expr stackTrace) {
|
||||
printsStackToWriter(call) and
|
||||
call.getQualifier() = stackTrace and
|
||||
not call.getQualifier() instanceof SuperAccess
|
||||
}
|
||||
|
||||
/**
|
||||
* A stringified stack trace flows to an external sink.
|
||||
*/
|
||||
predicate stringifiedStackFlowsExternally(DataFlow::Node externalExpr, Expr stackTrace) {
|
||||
exists(MethodAccess stackTraceString |
|
||||
stackTraceExpr(stackTrace, stackTraceString) and
|
||||
StackTraceStringToHttpResponseSinkFlow::flow(DataFlow::exprNode(stackTraceString), externalExpr)
|
||||
)
|
||||
}
|
||||
|
||||
class GetMessageFlowSource extends MethodAccess {
|
||||
GetMessageFlowSource() {
|
||||
exists(Method method |
|
||||
method = this.getMethod() and
|
||||
method.hasName("getMessage") and
|
||||
method.hasNoParameters() and
|
||||
method.getDeclaringType().hasQualifiedName("java.lang", "Throwable")
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
module GetMessageFlowSourceToHttpResponseSinkFlowConfig implements DataFlow::ConfigSig {
|
||||
predicate isSource(DataFlow::Node src) { src.asExpr() instanceof GetMessageFlowSource }
|
||||
|
||||
predicate isSink(DataFlow::Node sink) { sink instanceof InformationLeakSink }
|
||||
}
|
||||
|
||||
module GetMessageFlowSourceToHttpResponseSinkFlow =
|
||||
TaintTracking::Global<GetMessageFlowSourceToHttpResponseSinkFlowConfig>;
|
||||
|
||||
/**
|
||||
* A call to `getMessage()` that then flows to a servlet response.
|
||||
*/
|
||||
predicate getMessageFlowsExternally(DataFlow::Node externalExpr, GetMessageFlowSource getMessage) {
|
||||
GetMessageFlowSourceToHttpResponseSinkFlow::flow(DataFlow::exprNode(getMessage), externalExpr)
|
||||
}
|
||||
import semmle.code.java.security.StackTraceExposureQuery
|
||||
|
||||
from Expr externalExpr, Expr errorInformation
|
||||
where
|
||||
printsStackExternally(externalExpr, errorInformation) or
|
||||
stringifiedStackFlowsExternally(DataFlow::exprNode(externalExpr), errorInformation) or
|
||||
getMessageFlowsExternally(DataFlow::exprNode(externalExpr), errorInformation)
|
||||
getMessageFlowsExternally(DataFlow::exprNode(externalExpr), DataFlow::exprNode(errorInformation))
|
||||
select externalExpr, "$@ can be exposed to an external user.", errorInformation, "Error information"
|
||||
|
||||
@@ -11,109 +11,9 @@
|
||||
*/
|
||||
|
||||
import java
|
||||
import semmle.code.java.controlflow.Guards
|
||||
import semmle.code.java.dataflow.DataFlow
|
||||
import semmle.code.java.dataflow.FlowSources
|
||||
import semmle.code.java.security.Encryption
|
||||
import semmle.code.java.security.SecurityFlag
|
||||
private import semmle.code.java.dataflow.ExternalFlow
|
||||
|
||||
/**
|
||||
* Holds if `m` always returns `true` ignoring any exceptional flow.
|
||||
*/
|
||||
private predicate alwaysReturnsTrue(HostnameVerifierVerify m) {
|
||||
forex(ReturnStmt rs | rs.getEnclosingCallable() = m |
|
||||
rs.getResult().(CompileTimeConstantExpr).getBooleanValue() = true
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* A class that overrides the `javax.net.ssl.HostnameVerifier.verify` method and **always** returns `true` (though it could also exit due to an uncaught exception), thus
|
||||
* accepting any certificate despite a hostname mismatch.
|
||||
*/
|
||||
class TrustAllHostnameVerifier extends RefType {
|
||||
TrustAllHostnameVerifier() {
|
||||
this.getAnAncestor() instanceof HostnameVerifier and
|
||||
exists(HostnameVerifierVerify m |
|
||||
m.getDeclaringType() = this and
|
||||
alwaysReturnsTrue(m)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A configuration to model the flow of a `TrustAllHostnameVerifier` to a `set(Default)HostnameVerifier` call.
|
||||
*/
|
||||
module TrustAllHostnameVerifierConfig implements DataFlow::ConfigSig {
|
||||
predicate isSource(DataFlow::Node source) {
|
||||
source.asExpr().(ClassInstanceExpr).getConstructedType() instanceof TrustAllHostnameVerifier
|
||||
}
|
||||
|
||||
predicate isSink(DataFlow::Node sink) { sink instanceof HostnameVerifierSink }
|
||||
|
||||
predicate isBarrier(DataFlow::Node barrier) {
|
||||
// ignore nodes that are in functions that intentionally disable hostname verification
|
||||
barrier
|
||||
.getEnclosingCallable()
|
||||
.getName()
|
||||
/*
|
||||
* Regex: (_)* :
|
||||
* some methods have underscores.
|
||||
* Regex: (no|ignore|disable)(strictssl|ssl|verify|verification|hostname)
|
||||
* noStrictSSL ignoreSsl
|
||||
* Regex: (set)?(accept|trust|ignore|allow)(all|every|any)
|
||||
* acceptAll trustAll ignoreAll setTrustAnyHttps
|
||||
* Regex: (use|do|enable)insecure
|
||||
* useInsecureSSL
|
||||
* Regex: (set|do|use)?no.*(check|validation|verify|verification)
|
||||
* setNoCertificateCheck
|
||||
* Regex: disable
|
||||
* disableChecks
|
||||
*/
|
||||
|
||||
.regexpMatch("^(?i)(_)*((no|ignore|disable)(strictssl|ssl|verify|verification|hostname)" +
|
||||
"|(set)?(accept|trust|ignore|allow)(all|every|any)" +
|
||||
"|(use|do|enable)insecure|(set|do|use)?no.*(check|validation|verify|verification)|disable).*$")
|
||||
}
|
||||
}
|
||||
|
||||
module TrustAllHostnameVerifierFlow = DataFlow::Global<TrustAllHostnameVerifierConfig>;
|
||||
|
||||
import semmle.code.java.security.UnsafeHostnameVerificationQuery
|
||||
import TrustAllHostnameVerifierFlow::PathGraph
|
||||
|
||||
/**
|
||||
* A sink that sets the `HostnameVerifier` on `HttpsURLConnection`.
|
||||
*/
|
||||
private class HostnameVerifierSink extends DataFlow::Node {
|
||||
HostnameVerifierSink() { sinkNode(this, "set-hostname-verifier") }
|
||||
}
|
||||
|
||||
/**
|
||||
* Flags suggesting a deliberately unsafe `HostnameVerifier` usage.
|
||||
*/
|
||||
private class UnsafeHostnameVerificationFlag extends FlagKind {
|
||||
UnsafeHostnameVerificationFlag() { this = "UnsafeHostnameVerificationFlag" }
|
||||
|
||||
bindingset[result]
|
||||
override string getAFlagName() {
|
||||
result
|
||||
.regexpMatch("(?i).*(secure|disable|selfCert|selfSign|validat|verif|trust|ignore|nocertificatecheck).*") and
|
||||
result != "equalsIgnoreCase"
|
||||
}
|
||||
}
|
||||
|
||||
/** Gets a guard that represents a (likely) flag controlling an unsafe `HostnameVerifier` use. */
|
||||
private Guard getAnUnsafeHostnameVerifierFlagGuard() {
|
||||
result = any(UnsafeHostnameVerificationFlag flag).getAFlag().asExpr()
|
||||
}
|
||||
|
||||
/** Holds if `node` is guarded by a flag that suggests an intentionally insecure use. */
|
||||
private predicate isNodeGuardedByFlag(DataFlow::Node node) {
|
||||
exists(Guard g | g.controls(node.asExpr().getBasicBlock(), _) |
|
||||
g = getASecurityFeatureFlagGuard() or g = getAnUnsafeHostnameVerifierFlagGuard()
|
||||
)
|
||||
}
|
||||
|
||||
from
|
||||
TrustAllHostnameVerifierFlow::PathNode source, TrustAllHostnameVerifierFlow::PathNode sink,
|
||||
RefType verifier
|
||||
|
||||
@@ -13,41 +13,15 @@
|
||||
|
||||
import java
|
||||
import semmle.code.java.security.Encryption
|
||||
import semmle.code.java.dataflow.TaintTracking
|
||||
import DataFlow
|
||||
|
||||
private class ShortStringLiteral extends StringLiteral {
|
||||
ShortStringLiteral() { this.getValue().length() < 100 }
|
||||
}
|
||||
|
||||
class BrokenAlgoLiteral extends ShortStringLiteral {
|
||||
BrokenAlgoLiteral() {
|
||||
this.getValue().regexpMatch(getInsecureAlgorithmRegex()) and
|
||||
// Exclude German and French sentences.
|
||||
not this.getValue().regexpMatch(".*\\p{IsLowercase} des \\p{IsLetter}.*")
|
||||
}
|
||||
}
|
||||
|
||||
module InsecureCryptoConfig implements ConfigSig {
|
||||
predicate isSource(Node n) { n.asExpr() instanceof BrokenAlgoLiteral }
|
||||
|
||||
predicate isSink(Node n) { exists(CryptoAlgoSpec c | n.asExpr() = c.getAlgoSpec()) }
|
||||
|
||||
predicate isBarrier(DataFlow::Node node) {
|
||||
node.getType() instanceof PrimitiveType or node.getType() instanceof BoxedType
|
||||
}
|
||||
}
|
||||
|
||||
module InsecureCryptoFlow = TaintTracking::Global<InsecureCryptoConfig>;
|
||||
|
||||
import semmle.code.java.security.BrokenCryptoAlgorithmQuery
|
||||
import InsecureCryptoFlow::PathGraph
|
||||
|
||||
from
|
||||
InsecureCryptoFlow::PathNode source, InsecureCryptoFlow::PathNode sink, CryptoAlgoSpec c,
|
||||
BrokenAlgoLiteral s
|
||||
InsecureCryptoFlow::PathNode source, InsecureCryptoFlow::PathNode sink, CryptoAlgoSpec spec,
|
||||
BrokenAlgoLiteral algo
|
||||
where
|
||||
sink.getNode().asExpr() = c.getAlgoSpec() and
|
||||
source.getNode().asExpr() = s and
|
||||
sink.getNode().asExpr() = spec.getAlgoSpec() and
|
||||
source.getNode().asExpr() = algo and
|
||||
InsecureCryptoFlow::flowPath(source, sink)
|
||||
select c, source, sink, "Cryptographic algorithm $@ is weak and should not be used.", s,
|
||||
s.getValue()
|
||||
select spec, source, sink, "Cryptographic algorithm $@ is weak and should not be used.", algo,
|
||||
algo.getValue()
|
||||
|
||||
@@ -13,56 +13,7 @@
|
||||
|
||||
import java
|
||||
import semmle.code.java.security.Encryption
|
||||
import semmle.code.java.dataflow.TaintTracking
|
||||
import DataFlow
|
||||
import semmle.code.java.dispatch.VirtualDispatch
|
||||
|
||||
private class ShortStringLiteral extends StringLiteral {
|
||||
ShortStringLiteral() { this.getValue().length() < 100 }
|
||||
}
|
||||
|
||||
class InsecureAlgoLiteral extends ShortStringLiteral {
|
||||
InsecureAlgoLiteral() {
|
||||
// Algorithm identifiers should be at least two characters.
|
||||
this.getValue().length() > 1 and
|
||||
exists(string s | s = this.getValue() |
|
||||
not s.regexpMatch(getSecureAlgorithmRegex()) and
|
||||
// Exclude results covered by another query.
|
||||
not s.regexpMatch(getInsecureAlgorithmRegex())
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
predicate objectToString(MethodAccess ma) {
|
||||
exists(ToStringMethod m |
|
||||
m = ma.getMethod() and
|
||||
m.getDeclaringType() instanceof TypeObject and
|
||||
exprNode(ma.getQualifier()).getTypeBound().getErasure() instanceof TypeObject
|
||||
)
|
||||
}
|
||||
|
||||
class StringContainer extends RefType {
|
||||
StringContainer() {
|
||||
this instanceof TypeString or
|
||||
this instanceof StringBuildingType or
|
||||
this.hasQualifiedName("java.util", "StringTokenizer") or
|
||||
this.(Array).getComponentType() instanceof StringContainer
|
||||
}
|
||||
}
|
||||
|
||||
module InsecureCryptoConfig implements ConfigSig {
|
||||
predicate isSource(Node n) { n.asExpr() instanceof InsecureAlgoLiteral }
|
||||
|
||||
predicate isSink(Node n) { exists(CryptoAlgoSpec c | n.asExpr() = c.getAlgoSpec()) }
|
||||
|
||||
predicate isBarrier(Node n) {
|
||||
objectToString(n.asExpr()) or
|
||||
not n.getType().getErasure() instanceof StringContainer
|
||||
}
|
||||
}
|
||||
|
||||
module InsecureCryptoFlow = TaintTracking::Global<InsecureCryptoConfig>;
|
||||
|
||||
import semmle.code.java.security.MaybeBrokenCryptoAlgorithmQuery
|
||||
import InsecureCryptoFlow::PathGraph
|
||||
|
||||
from
|
||||
|
||||
@@ -12,17 +12,7 @@
|
||||
*/
|
||||
|
||||
import java
|
||||
import semmle.code.java.dataflow.FlowSources
|
||||
import semmle.code.java.security.UrlRedirect
|
||||
|
||||
module UrlRedirectConfig implements DataFlow::ConfigSig {
|
||||
predicate isSource(DataFlow::Node source) { source instanceof RemoteFlowSource }
|
||||
|
||||
predicate isSink(DataFlow::Node sink) { sink instanceof UrlRedirectSink }
|
||||
}
|
||||
|
||||
module UrlRedirectFlow = TaintTracking::Global<UrlRedirectConfig>;
|
||||
|
||||
import semmle.code.java.security.UrlRedirectQuery
|
||||
import UrlRedirectFlow::PathGraph
|
||||
|
||||
from UrlRedirectFlow::PathNode source, UrlRedirectFlow::PathNode sink
|
||||
|
||||
@@ -12,17 +12,7 @@
|
||||
*/
|
||||
|
||||
import java
|
||||
import semmle.code.java.dataflow.FlowSources
|
||||
import semmle.code.java.security.UrlRedirect
|
||||
|
||||
module UrlRedirectLocalConfig implements DataFlow::ConfigSig {
|
||||
predicate isSource(DataFlow::Node source) { source instanceof LocalUserInput }
|
||||
|
||||
predicate isSink(DataFlow::Node sink) { sink instanceof UrlRedirectSink }
|
||||
}
|
||||
|
||||
module UrlRedirectLocalFlow = TaintTracking::Global<UrlRedirectLocalConfig>;
|
||||
|
||||
import semmle.code.java.security.UrlRedirectLocalQuery
|
||||
import UrlRedirectLocalFlow::PathGraph
|
||||
|
||||
from UrlRedirectLocalFlow::PathNode source, UrlRedirectLocalFlow::PathNode sink
|
||||
|
||||
@@ -13,41 +13,7 @@
|
||||
|
||||
import java
|
||||
import semmle.code.java.frameworks.Servlets
|
||||
import semmle.code.java.dataflow.DataFlow
|
||||
|
||||
predicate isSafeSecureCookieSetting(Expr e) {
|
||||
e.(CompileTimeConstantExpr).getBooleanValue() = true
|
||||
or
|
||||
exists(Method isSecure |
|
||||
isSecure.getName() = "isSecure" and
|
||||
isSecure.getDeclaringType().getASourceSupertype*() instanceof ServletRequest
|
||||
|
|
||||
e.(MethodAccess).getMethod() = isSecure
|
||||
)
|
||||
}
|
||||
|
||||
module SecureCookieConfig implements DataFlow::ConfigSig {
|
||||
predicate isSource(DataFlow::Node source) {
|
||||
exists(MethodAccess ma, Method m | ma.getMethod() = m |
|
||||
m.getDeclaringType() instanceof TypeCookie and
|
||||
m.getName() = "setSecure" and
|
||||
source.asExpr() = ma.getQualifier() and
|
||||
forex(DataFlow::Node argSource |
|
||||
DataFlow::localFlow(argSource, DataFlow::exprNode(ma.getArgument(0))) and
|
||||
not DataFlow::localFlowStep(_, argSource)
|
||||
|
|
||||
isSafeSecureCookieSetting(argSource.asExpr())
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
predicate isSink(DataFlow::Node sink) {
|
||||
sink.asExpr() =
|
||||
any(MethodAccess add | add.getMethod() instanceof ResponseAddCookieMethod).getArgument(0)
|
||||
}
|
||||
}
|
||||
|
||||
module SecureCookieFlow = DataFlow::Global<SecureCookieConfig>;
|
||||
import semmle.code.java.security.InsecureCookieQuery
|
||||
|
||||
from MethodAccess add
|
||||
where
|
||||
|
||||
@@ -12,18 +12,7 @@
|
||||
*/
|
||||
|
||||
import java
|
||||
import semmle.code.java.dataflow.FlowSources
|
||||
import semmle.code.java.dataflow.TaintTracking
|
||||
import semmle.code.java.security.XPath
|
||||
|
||||
module XPathInjectionConfig implements DataFlow::ConfigSig {
|
||||
predicate isSource(DataFlow::Node source) { source instanceof RemoteFlowSource }
|
||||
|
||||
predicate isSink(DataFlow::Node sink) { sink instanceof XPathInjectionSink }
|
||||
}
|
||||
|
||||
module XPathInjectionFlow = TaintTracking::Global<XPathInjectionConfig>;
|
||||
|
||||
import semmle.code.java.security.XPathInjectionQuery
|
||||
import XPathInjectionFlow::PathGraph
|
||||
|
||||
from XPathInjectionFlow::PathNode source, XPathInjectionFlow::PathNode sink
|
||||
|
||||
@@ -1,69 +0,0 @@
|
||||
import java
|
||||
import semmle.code.java.arithmetic.Overflow
|
||||
import semmle.code.java.dataflow.SSA
|
||||
import semmle.code.java.controlflow.Guards
|
||||
import semmle.code.java.dataflow.RangeAnalysis
|
||||
|
||||
class NumericNarrowingCastExpr extends CastExpr {
|
||||
NumericNarrowingCastExpr() {
|
||||
exists(NumericType sourceType, NumericType targetType |
|
||||
sourceType = this.getExpr().getType() and targetType = this.getType()
|
||||
|
|
||||
not targetType.(NumType).widerThanOrEqualTo(sourceType)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
class RightShiftOp extends Expr {
|
||||
RightShiftOp() {
|
||||
this instanceof RightShiftExpr or
|
||||
this instanceof UnsignedRightShiftExpr or
|
||||
this instanceof AssignRightShiftExpr or
|
||||
this instanceof AssignUnsignedRightShiftExpr
|
||||
}
|
||||
|
||||
private Expr getLhs() {
|
||||
this.(BinaryExpr).getLeftOperand() = result or
|
||||
this.(Assignment).getDest() = result
|
||||
}
|
||||
|
||||
Variable getShiftedVariable() {
|
||||
this.getLhs() = result.getAnAccess() or
|
||||
this.getLhs().(AndBitwiseExpr).getAnOperand() = result.getAnAccess()
|
||||
}
|
||||
}
|
||||
|
||||
predicate boundedRead(RValue read) {
|
||||
exists(SsaVariable v, ConditionBlock cb, ComparisonExpr comp, boolean testIsTrue |
|
||||
read = v.getAUse() and
|
||||
cb.controls(read.getBasicBlock(), testIsTrue) and
|
||||
cb.getCondition() = comp
|
||||
|
|
||||
comp.getLesserOperand() = v.getAUse() and testIsTrue = true
|
||||
or
|
||||
comp.getGreaterOperand() = v.getAUse() and testIsTrue = false
|
||||
)
|
||||
}
|
||||
|
||||
predicate castCheck(RValue read) {
|
||||
exists(EqualityTest eq, CastExpr cast |
|
||||
cast.getExpr() = read and
|
||||
eq.hasOperands(cast, read.getVariable().getAnAccess())
|
||||
)
|
||||
}
|
||||
|
||||
class SmallType extends Type {
|
||||
SmallType() {
|
||||
this instanceof BooleanType or
|
||||
this.(PrimitiveType).hasName("byte") or
|
||||
this.(BoxedType).getPrimitiveType().hasName("byte")
|
||||
}
|
||||
}
|
||||
|
||||
predicate smallExpr(Expr e) {
|
||||
exists(int low, int high |
|
||||
bounded(e, any(ZeroBound zb), low, false, _) and
|
||||
bounded(e, any(ZeroBound zb), high, true, _) and
|
||||
high - low < 256
|
||||
)
|
||||
}
|
||||
@@ -13,29 +13,7 @@
|
||||
*/
|
||||
|
||||
import java
|
||||
import semmle.code.java.dataflow.FlowSources
|
||||
import NumericCastCommon
|
||||
|
||||
module NumericCastFlowConfig implements DataFlow::ConfigSig {
|
||||
predicate isSource(DataFlow::Node src) { src instanceof RemoteFlowSource }
|
||||
|
||||
predicate isSink(DataFlow::Node sink) {
|
||||
sink.asExpr() = any(NumericNarrowingCastExpr cast).getExpr() and
|
||||
sink.asExpr() instanceof VarAccess
|
||||
}
|
||||
|
||||
predicate isBarrier(DataFlow::Node node) {
|
||||
boundedRead(node.asExpr()) or
|
||||
castCheck(node.asExpr()) or
|
||||
node.getType() instanceof SmallType or
|
||||
smallExpr(node.asExpr()) or
|
||||
node.getEnclosingCallable() instanceof HashCodeMethod or
|
||||
exists(RightShiftOp e | e.getShiftedVariable().getAnAccess() = node.asExpr())
|
||||
}
|
||||
}
|
||||
|
||||
module NumericCastFlow = TaintTracking::Global<NumericCastFlowConfig>;
|
||||
|
||||
import semmle.code.java.security.NumericCastTaintedQuery
|
||||
import NumericCastFlow::PathGraph
|
||||
|
||||
from NumericCastFlow::PathNode source, NumericCastFlow::PathNode sink, NumericNarrowingCastExpr exp
|
||||
|
||||
@@ -13,36 +13,16 @@
|
||||
*/
|
||||
|
||||
import java
|
||||
import semmle.code.java.dataflow.FlowSources
|
||||
import NumericCastCommon
|
||||
|
||||
module NumericCastFlowConfig implements DataFlow::ConfigSig {
|
||||
predicate isSource(DataFlow::Node src) { src instanceof LocalUserInput }
|
||||
|
||||
predicate isSink(DataFlow::Node sink) {
|
||||
sink.asExpr() = any(NumericNarrowingCastExpr cast).getExpr()
|
||||
}
|
||||
|
||||
predicate isBarrier(DataFlow::Node node) {
|
||||
boundedRead(node.asExpr()) or
|
||||
castCheck(node.asExpr()) or
|
||||
node.getType() instanceof SmallType or
|
||||
smallExpr(node.asExpr()) or
|
||||
node.getEnclosingCallable() instanceof HashCodeMethod
|
||||
}
|
||||
}
|
||||
|
||||
module NumericCastFlow = TaintTracking::Global<NumericCastFlowConfig>;
|
||||
|
||||
import NumericCastFlow::PathGraph
|
||||
import semmle.code.java.security.NumericCastTaintedQuery
|
||||
import NumericCastLocalFlow::PathGraph
|
||||
|
||||
from
|
||||
NumericCastFlow::PathNode source, NumericCastFlow::PathNode sink, NumericNarrowingCastExpr exp,
|
||||
VarAccess tainted
|
||||
NumericCastLocalFlow::PathNode source, NumericCastLocalFlow::PathNode sink,
|
||||
NumericNarrowingCastExpr exp, VarAccess tainted
|
||||
where
|
||||
exp.getExpr() = tainted and
|
||||
sink.getNode().asExpr() = tainted and
|
||||
NumericCastFlow::flowPath(source, sink) and
|
||||
NumericCastLocalFlow::flowPath(source, sink) and
|
||||
not exists(RightShiftOp e | e.getShiftedVariable() = tainted.getVariable())
|
||||
select exp, source, sink,
|
||||
"This cast to a narrower type depends on a $@, potentially causing truncation.", source.getNode(),
|
||||
|
||||
@@ -13,55 +13,7 @@
|
||||
*/
|
||||
|
||||
import java
|
||||
import semmle.code.java.dataflow.FlowSources
|
||||
import semmle.code.java.dataflow.TaintTracking
|
||||
|
||||
class TypeShiroSubject extends RefType {
|
||||
TypeShiroSubject() { this.getQualifiedName() = "org.apache.shiro.subject.Subject" }
|
||||
}
|
||||
|
||||
class TypeShiroWCPermission extends RefType {
|
||||
TypeShiroWCPermission() {
|
||||
this.getQualifiedName() = "org.apache.shiro.authz.permission.WildcardPermission"
|
||||
}
|
||||
}
|
||||
|
||||
abstract class PermissionsConstruction extends Top {
|
||||
abstract Expr getInput();
|
||||
}
|
||||
|
||||
class PermissionsCheckMethodAccess extends MethodAccess, PermissionsConstruction {
|
||||
PermissionsCheckMethodAccess() {
|
||||
exists(Method m | m = this.getMethod() |
|
||||
m.getDeclaringType() instanceof TypeShiroSubject and
|
||||
m.getName() = "isPermitted"
|
||||
or
|
||||
m.getName().toLowerCase().matches("%permitted%") and
|
||||
m.getNumberOfParameters() = 1
|
||||
)
|
||||
}
|
||||
|
||||
override Expr getInput() { result = this.getArgument(0) }
|
||||
}
|
||||
|
||||
class WCPermissionConstruction extends ClassInstanceExpr, PermissionsConstruction {
|
||||
WCPermissionConstruction() {
|
||||
this.getConstructor().getDeclaringType() instanceof TypeShiroWCPermission
|
||||
}
|
||||
|
||||
override Expr getInput() { result = this.getArgument(0) }
|
||||
}
|
||||
|
||||
module TaintedPermissionsCheckFlowConfig implements DataFlow::ConfigSig {
|
||||
predicate isSource(DataFlow::Node source) { source instanceof UserInput }
|
||||
|
||||
predicate isSink(DataFlow::Node sink) {
|
||||
sink.asExpr() = any(PermissionsConstruction p).getInput()
|
||||
}
|
||||
}
|
||||
|
||||
module TaintedPermissionsCheckFlow = TaintTracking::Global<TaintedPermissionsCheckFlowConfig>;
|
||||
|
||||
import semmle.code.java.security.TaintedPermissionsCheckQuery
|
||||
import TaintedPermissionsCheckFlow::PathGraph
|
||||
|
||||
from
|
||||
|
||||
Reference in New Issue
Block a user