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:
Edward Minnix III
2023-05-04 20:14:22 -04:00
committed by GitHub
62 changed files with 1440 additions and 1076 deletions

View File

@@ -12,6 +12,7 @@
* external/cwe/cwe-088
*/
import java
import semmle.code.java.security.CommandLineQuery
import LocalUserInputToArgumentToExecFlow::PathGraph

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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"

View File

@@ -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()
}
}

View File

@@ -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()
}

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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)
}

View File

@@ -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,

View File

@@ -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,

View File

@@ -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,

View File

@@ -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
) {

View File

@@ -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

View File

@@ -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)
)
)
}

View File

@@ -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"

View File

@@ -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

View File

@@ -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()

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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
)
}

View File

@@ -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

View File

@@ -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(),

View File

@@ -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