mirror of
https://github.com/github/codeql.git
synced 2026-04-25 08:45:14 +02:00
Merge branch 'main' into atorralba/java/promote-xxe-experimental-sinks
This commit is contained in:
@@ -1,3 +1,7 @@
|
||||
## 0.6.1
|
||||
|
||||
No user-facing changes.
|
||||
|
||||
## 0.6.0
|
||||
|
||||
### New Queries
|
||||
|
||||
@@ -18,10 +18,10 @@ import semmle.code.java.frameworks.spring.Spring
|
||||
class InstanceFieldWrite extends FieldWrite {
|
||||
InstanceFieldWrite() {
|
||||
// Must be in an instance callable
|
||||
not getEnclosingCallable().isStatic() and
|
||||
not this.getEnclosingCallable().isStatic() and
|
||||
// Must be declared in this type or a supertype.
|
||||
getEnclosingCallable().getDeclaringType().inherits(getField()) and
|
||||
isOwnFieldAccess()
|
||||
this.getEnclosingCallable().getDeclaringType().inherits(this.getField()) and
|
||||
this.isOwnFieldAccess()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -62,7 +62,7 @@ class SpringPureClass extends Class {
|
||||
SpringPureClass() {
|
||||
// The only permitted statement in static initializers is the initialization of a static
|
||||
// final or effectively final logger fields, or effectively immutable types.
|
||||
forall(Stmt s | s = getANestedStmt(getAMember().(StaticInitializer).getBody()) |
|
||||
forall(Stmt s | s = getANestedStmt(this.getAMember().(StaticInitializer).getBody()) |
|
||||
exists(Field f | f = s.(ExprStmt).getExpr().(AssignExpr).getDest().(FieldWrite).getField() |
|
||||
(
|
||||
// A logger field
|
||||
@@ -79,8 +79,8 @@ class SpringPureClass extends Class {
|
||||
// No constructor, instance initializer or Spring bean init or setter method that is impure.
|
||||
not exists(Callable c, ImpureStmt impureStmt |
|
||||
(
|
||||
inherits(c.(Method)) or
|
||||
c = getAMember()
|
||||
this.inherits(c.(Method)) or
|
||||
c = this.getAMember()
|
||||
) and
|
||||
impureStmt.getEnclosingCallable() = c
|
||||
|
|
||||
@@ -110,7 +110,7 @@ class SpringPureClass extends Class {
|
||||
*/
|
||||
class SpringBeanFactory extends ClassOrInterface {
|
||||
SpringBeanFactory() {
|
||||
getAnAncestor().hasQualifiedName("org.springframework.beans.factory", "BeanFactory")
|
||||
this.getAnAncestor().hasQualifiedName("org.springframework.beans.factory", "BeanFactory")
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -136,20 +136,20 @@ class LiveSpringBean extends SpringBean {
|
||||
LiveSpringBean() {
|
||||
// Must not be needed for side effects due to construction
|
||||
// Only loaded by the container when required, so construction cannot have any useful side-effects
|
||||
not isLazyInit() and
|
||||
not this.isLazyInit() and
|
||||
// or has no side-effects when constructed
|
||||
not getClass() instanceof SpringPureClass
|
||||
not this.getClass() instanceof SpringPureClass
|
||||
or
|
||||
(
|
||||
// If the class does not exist for this bean, or the class is not a source bean, then this is
|
||||
// likely to be a definition using a library class, in which case we should consider it to be
|
||||
// live.
|
||||
not exists(getClass())
|
||||
not exists(this.getClass())
|
||||
or
|
||||
not getClass().fromSource()
|
||||
not this.getClass().fromSource()
|
||||
or
|
||||
// In alfresco, "webscript" beans should be considered live
|
||||
getBeanParent*().getBeanParentName() = "webscript"
|
||||
this.getBeanParent*().getBeanParentName() = "webscript"
|
||||
or
|
||||
// A live child bean implies this bean is live
|
||||
exists(LiveSpringBean child | this = child.getBeanParent())
|
||||
|
||||
@@ -3,8 +3,8 @@ import java
|
||||
/** A class that implements `java.lang.Iterable`. */
|
||||
class Iterable extends Class {
|
||||
Iterable() {
|
||||
isSourceDeclaration() and
|
||||
getASourceSupertype+().hasQualifiedName("java.lang", "Iterable")
|
||||
this.isSourceDeclaration() and
|
||||
this.getASourceSupertype+().hasQualifiedName("java.lang", "Iterable")
|
||||
}
|
||||
|
||||
/** The return value of a one-statement `iterator()` method. */
|
||||
|
||||
@@ -16,7 +16,7 @@ import IterableClass
|
||||
|
||||
/** An `Iterable` that is also its own `Iterator`. */
|
||||
class IterableIterator extends Iterable {
|
||||
IterableIterator() { simpleIterator() instanceof ThisAccess }
|
||||
IterableIterator() { this.simpleIterator() instanceof ThisAccess }
|
||||
}
|
||||
|
||||
/** An `IterableIterator` that never returns any elements. */
|
||||
|
||||
@@ -17,9 +17,9 @@ library class BoundKind extends string {
|
||||
|
||||
predicate isUpper() { this = "<=" }
|
||||
|
||||
predicate providesLowerBound() { isEqual() or isLower() }
|
||||
predicate providesLowerBound() { this.isEqual() or this.isLower() }
|
||||
|
||||
predicate providesUpperBound() { isEqual() or isUpper() }
|
||||
predicate providesUpperBound() { this.isEqual() or this.isUpper() }
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -19,19 +19,19 @@ import java
|
||||
*/
|
||||
class ArrayCast extends CastExpr {
|
||||
ArrayCast() {
|
||||
getExpr() instanceof ArrayCreationExpr and
|
||||
getType() instanceof Array
|
||||
this.getExpr() instanceof ArrayCreationExpr and
|
||||
this.getType() instanceof Array
|
||||
}
|
||||
|
||||
/** The type of the operand expression of this cast. */
|
||||
Array getSourceType() { result = getExpr().getType() }
|
||||
Array getSourceType() { result = this.getExpr().getType() }
|
||||
|
||||
/** The result type of this cast. */
|
||||
Array getTargetType() { result = getType() }
|
||||
Array getTargetType() { result = this.getType() }
|
||||
|
||||
Type getSourceComponentType() { result = getSourceType().getComponentType() }
|
||||
Type getSourceComponentType() { result = this.getSourceType().getComponentType() }
|
||||
|
||||
Type getTargetComponentType() { result = getTargetType().getComponentType() }
|
||||
Type getTargetComponentType() { result = this.getTargetType().getComponentType() }
|
||||
}
|
||||
|
||||
predicate uncheckedCastType(RefType t) {
|
||||
|
||||
@@ -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
|
||||
) {
|
||||
|
||||
@@ -32,9 +32,9 @@ class LTWideningComparison extends WideningComparison {
|
||||
leftWidth(this) < rightWidth(this)
|
||||
}
|
||||
|
||||
override Expr getNarrower() { result = getLeftOperand() }
|
||||
override Expr getNarrower() { result = this.getLeftOperand() }
|
||||
|
||||
override Expr getWider() { result = getRightOperand() }
|
||||
override Expr getWider() { result = this.getRightOperand() }
|
||||
}
|
||||
|
||||
class GTWideningComparison extends WideningComparison {
|
||||
@@ -43,9 +43,9 @@ class GTWideningComparison extends WideningComparison {
|
||||
leftWidth(this) > rightWidth(this)
|
||||
}
|
||||
|
||||
override Expr getNarrower() { result = getRightOperand() }
|
||||
override Expr getNarrower() { result = this.getRightOperand() }
|
||||
|
||||
override Expr getWider() { result = getLeftOperand() }
|
||||
override Expr getWider() { result = this.getLeftOperand() }
|
||||
}
|
||||
|
||||
from WideningComparison c, LoopStmt l
|
||||
|
||||
@@ -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() { getValue().length() < 100 }
|
||||
}
|
||||
|
||||
class BrokenAlgoLiteral extends ShortStringLiteral {
|
||||
BrokenAlgoLiteral() {
|
||||
getValue().regexpMatch(getInsecureAlgorithmRegex()) and
|
||||
// Exclude German and French sentences.
|
||||
not 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 = getExpr().getType() and targetType = 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() {
|
||||
getLhs() = result.getAnAccess() or
|
||||
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
|
||||
|
||||
60
java/ql/src/Telemetry/AutomodelEndpointTypes.qll
Normal file
60
java/ql/src/Telemetry/AutomodelEndpointTypes.qll
Normal file
@@ -0,0 +1,60 @@
|
||||
/**
|
||||
* For internal use only.
|
||||
*
|
||||
* Defines the set of classes that endpoint scoring models can predict. Endpoint scoring models must
|
||||
* only predict classes defined within this file. This file is the source of truth for the integer
|
||||
* representation of each of these classes.
|
||||
*/
|
||||
|
||||
/** A class that can be predicted by a classifier. */
|
||||
abstract class EndpointType extends string {
|
||||
/**
|
||||
* Holds when the string matches the name of the sink / source type.
|
||||
*/
|
||||
bindingset[this]
|
||||
EndpointType() { any() }
|
||||
|
||||
/**
|
||||
* Gets the name of the sink/source kind for this endpoint type as used in models-as-data.
|
||||
*
|
||||
* See https://github.com/github/codeql/blob/44213f0144fdd54bb679ca48d68b28dcf820f7a8/java/ql/lib/semmle/code/java/dataflow/ExternalFlow.qll#LL353C11-L357C31
|
||||
*/
|
||||
final string getKind() { result = this }
|
||||
}
|
||||
|
||||
/** A class for sink types that can be predicted by a classifier. */
|
||||
abstract class SinkType extends EndpointType {
|
||||
bindingset[this]
|
||||
SinkType() { any() }
|
||||
}
|
||||
|
||||
/** A class for source types that can be predicted by a classifier. */
|
||||
abstract class SourceType extends EndpointType {
|
||||
bindingset[this]
|
||||
SourceType() { any() }
|
||||
}
|
||||
|
||||
/** The `Negative` class for non-sinks. */
|
||||
class NegativeSinkType extends SinkType {
|
||||
NegativeSinkType() { this = "non-sink" }
|
||||
}
|
||||
|
||||
/** A sink relevant to the SQL injection query */
|
||||
class SqlSinkType extends SinkType {
|
||||
SqlSinkType() { this = "sql" }
|
||||
}
|
||||
|
||||
/** A sink relevant to the tainted path injection query. */
|
||||
class TaintedPathSinkType extends SinkType {
|
||||
TaintedPathSinkType() { this = "tainted-path" }
|
||||
}
|
||||
|
||||
/** A sink relevant to the SSRF query. */
|
||||
class RequestForgerySinkType extends SinkType {
|
||||
RequestForgerySinkType() { this = "ssrf" }
|
||||
}
|
||||
|
||||
/** A sink relevant to the command injection query. */
|
||||
class CommandInjectionSinkType extends SinkType {
|
||||
CommandInjectionSinkType() { this = "command-injection" }
|
||||
}
|
||||
331
java/ql/src/Telemetry/AutomodelFrameworkModeCharacteristics.qll
Normal file
331
java/ql/src/Telemetry/AutomodelFrameworkModeCharacteristics.qll
Normal file
@@ -0,0 +1,331 @@
|
||||
/**
|
||||
* For internal use only.
|
||||
*/
|
||||
|
||||
private import java
|
||||
private import semmle.code.Location as Location
|
||||
private import semmle.code.java.dataflow.DataFlow
|
||||
private import semmle.code.java.dataflow.TaintTracking
|
||||
private import semmle.code.java.security.PathCreation
|
||||
private import semmle.code.java.dataflow.ExternalFlow as ExternalFlow
|
||||
private import semmle.code.java.dataflow.internal.FlowSummaryImpl as FlowSummaryImpl
|
||||
private import semmle.code.java.security.ExternalAPIs as ExternalAPIs
|
||||
private import semmle.code.java.Expr as Expr
|
||||
private import semmle.code.java.security.QueryInjection
|
||||
private import semmle.code.java.security.RequestForgery
|
||||
private import semmle.code.java.dataflow.internal.ModelExclusions as ModelExclusions
|
||||
import AutomodelSharedCharacteristics as SharedCharacteristics
|
||||
import AutomodelEndpointTypes as AutomodelEndpointTypes
|
||||
|
||||
/**
|
||||
* A meta data extractor. Any Java extraction mode needs to implement exactly
|
||||
* one instance of this class.
|
||||
*/
|
||||
abstract class MetadataExtractor extends string {
|
||||
bindingset[this]
|
||||
MetadataExtractor() { any() }
|
||||
|
||||
abstract predicate hasMetadata(
|
||||
DataFlow::ParameterNode e, string package, string type, boolean subtypes, string name,
|
||||
string signature, int input
|
||||
);
|
||||
}
|
||||
|
||||
newtype JavaRelatedLocationType =
|
||||
MethodDoc() or
|
||||
ClassDoc()
|
||||
|
||||
/**
|
||||
* A candidates implementation for framework mode.
|
||||
*
|
||||
* Some important notes:
|
||||
* - This mode is using parameters as endpoints.
|
||||
* - Sink- and neutral-information is being used from MaD models.
|
||||
* - When available, we use method- and class-java-docs as related locations.
|
||||
*/
|
||||
module FrameworkCandidatesImpl implements SharedCharacteristics::CandidateSig {
|
||||
// for documentation of the implementations here, see the QLDoc in the CandidateSig signature module.
|
||||
class Endpoint = DataFlow::ParameterNode;
|
||||
|
||||
class EndpointType = AutomodelEndpointTypes::EndpointType;
|
||||
|
||||
class NegativeEndpointType = AutomodelEndpointTypes::NegativeSinkType;
|
||||
|
||||
class RelatedLocation = Location::Top;
|
||||
|
||||
class RelatedLocationType = JavaRelatedLocationType;
|
||||
|
||||
// Sanitizers are currently not modeled in MaD. TODO: check if this has large negative impact.
|
||||
predicate isSanitizer(Endpoint e, EndpointType t) { none() }
|
||||
|
||||
RelatedLocation asLocation(Endpoint e) { result = e.asParameter() }
|
||||
|
||||
predicate isKnownKind(string kind, string humanReadableKind, EndpointType type) {
|
||||
kind = "read-file" and
|
||||
humanReadableKind = "read file" and
|
||||
type instanceof AutomodelEndpointTypes::TaintedPathSinkType
|
||||
or
|
||||
kind = "create-file" and
|
||||
humanReadableKind = "create file" and
|
||||
type instanceof AutomodelEndpointTypes::TaintedPathSinkType
|
||||
or
|
||||
kind = "sql" and
|
||||
humanReadableKind = "mad modeled sql" and
|
||||
type instanceof AutomodelEndpointTypes::SqlSinkType
|
||||
or
|
||||
kind = "open-url" and
|
||||
humanReadableKind = "open url" and
|
||||
type instanceof AutomodelEndpointTypes::RequestForgerySinkType
|
||||
or
|
||||
kind = "jdbc-url" and
|
||||
humanReadableKind = "jdbc url" and
|
||||
type instanceof AutomodelEndpointTypes::RequestForgerySinkType
|
||||
or
|
||||
kind = "command-injection" and
|
||||
humanReadableKind = "command injection" and
|
||||
type instanceof AutomodelEndpointTypes::CommandInjectionSinkType
|
||||
}
|
||||
|
||||
predicate isSink(Endpoint e, string kind) {
|
||||
exists(string package, string type, string name, string signature, string ext, string input |
|
||||
sinkSpec(e, package, type, name, signature, ext, input) and
|
||||
ExternalFlow::sinkModel(package, type, _, name, [signature, ""], ext, input, kind, _)
|
||||
)
|
||||
}
|
||||
|
||||
predicate isNeutral(Endpoint e) {
|
||||
exists(string package, string type, string name, string signature |
|
||||
sinkSpec(e, package, type, name, signature, _, _) and
|
||||
ExternalFlow::neutralModel(package, type, name, [signature, ""], _, _)
|
||||
)
|
||||
}
|
||||
|
||||
additional predicate sinkSpec(
|
||||
Endpoint e, string package, string type, string name, string signature, string ext, string input
|
||||
) {
|
||||
FrameworkCandidatesImpl::getCallable(e).hasQualifiedName(package, type, name) and
|
||||
signature = ExternalFlow::paramsString(getCallable(e)) and
|
||||
ext = "" and
|
||||
exists(int paramIdx | e.isParameterOf(_, paramIdx) |
|
||||
if paramIdx = -1 then input = "Argument[this]" else input = "Argument[" + paramIdx + "]"
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the related location for the given endpoint.
|
||||
*
|
||||
* Related locations can be JavaDoc comments of the class or the method.
|
||||
*/
|
||||
RelatedLocation getRelatedLocation(Endpoint e, RelatedLocationType type) {
|
||||
type = MethodDoc() and
|
||||
result = FrameworkCandidatesImpl::getCallable(e).(Documentable).getJavadoc()
|
||||
or
|
||||
type = ClassDoc() and
|
||||
result = FrameworkCandidatesImpl::getCallable(e).getDeclaringType().(Documentable).getJavadoc()
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the callable that contains the given endpoint.
|
||||
*
|
||||
* Each Java mode should implement this predicate.
|
||||
*/
|
||||
additional Callable getCallable(Endpoint e) { result = e.getEnclosingCallable() }
|
||||
}
|
||||
|
||||
module CharacteristicsImpl = SharedCharacteristics::SharedCharacteristics<FrameworkCandidatesImpl>;
|
||||
|
||||
class EndpointCharacteristic = CharacteristicsImpl::EndpointCharacteristic;
|
||||
|
||||
class Endpoint = FrameworkCandidatesImpl::Endpoint;
|
||||
|
||||
/*
|
||||
* Predicates that are used to surface prompt examples and candidates for classification with an ML model.
|
||||
*/
|
||||
|
||||
/**
|
||||
* A MetadataExtractor that extracts metadata for framework mode.
|
||||
*/
|
||||
class FrameworkModeMetadataExtractor extends MetadataExtractor {
|
||||
FrameworkModeMetadataExtractor() { this = "FrameworkModeMetadataExtractor" }
|
||||
|
||||
/**
|
||||
* By convention, the subtypes property of the MaD declaration should only be
|
||||
* true when there _can_ exist any subtypes with a different implementation.
|
||||
*
|
||||
* It would technically be ok to always use the value 'true', but this would
|
||||
* break convention.
|
||||
*/
|
||||
boolean considerSubtypes(Callable callable) {
|
||||
if
|
||||
callable.isStatic() or
|
||||
callable.getDeclaringType().isStatic() or
|
||||
callable.isFinal() or
|
||||
callable.getDeclaringType().isFinal()
|
||||
then result = false
|
||||
else result = true
|
||||
}
|
||||
|
||||
override predicate hasMetadata(
|
||||
Endpoint e, string package, string type, boolean subtypes, string name, string signature,
|
||||
int input
|
||||
) {
|
||||
exists(Callable callable |
|
||||
e.asParameter() = callable.getParameter(input) and
|
||||
package = callable.getDeclaringType().getPackage().getName() and
|
||||
type = callable.getDeclaringType().getErasure().(RefType).nestedName() and
|
||||
subtypes = this.considerSubtypes(callable) and
|
||||
name = e.toString() and
|
||||
signature = ExternalFlow::paramsString(callable)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* EndpointCharacteristic classes that are specific to Automodel for Java.
|
||||
*/
|
||||
|
||||
/**
|
||||
* A negative characteristic that indicates that an is-style boolean method is unexploitable even if it is a sink.
|
||||
*
|
||||
* A sink is highly unlikely to be exploitable if its callable's name starts with `is` and the callable has a boolean return
|
||||
* type (e.g. `isDirectory`). These kinds of calls normally do only checks, and appear before the proper call that does
|
||||
* the dangerous/interesting thing, so we want the latter to be modeled as the sink.
|
||||
*
|
||||
* TODO: this might filter too much, it's possible that methods with more than one parameter contain interesting sinks
|
||||
*/
|
||||
private class UnexploitableIsCharacteristic extends CharacteristicsImpl::NotASinkCharacteristic {
|
||||
UnexploitableIsCharacteristic() { this = "unexploitable (is-style boolean method)" }
|
||||
|
||||
override predicate appliesToEndpoint(Endpoint e) {
|
||||
not FrameworkCandidatesImpl::isSink(e, _) and
|
||||
FrameworkCandidatesImpl::getCallable(e).getName().matches("is%") and
|
||||
FrameworkCandidatesImpl::getCallable(e).getReturnType() instanceof BooleanType
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A negative characteristic that indicates that an existence-checking boolean method is unexploitable even if it is a
|
||||
* sink.
|
||||
*
|
||||
* A sink is highly unlikely to be exploitable if its callable's name is `exists` or `notExists` and the callable has a
|
||||
* boolean return type. These kinds of calls normally do only checks, and appear before the proper call that does the
|
||||
* dangerous/interesting thing, so we want the latter to be modeled as the sink.
|
||||
*/
|
||||
private class UnexploitableExistsCharacteristic extends CharacteristicsImpl::NotASinkCharacteristic {
|
||||
UnexploitableExistsCharacteristic() { this = "unexploitable (existence-checking boolean method)" }
|
||||
|
||||
override predicate appliesToEndpoint(Endpoint e) {
|
||||
not FrameworkCandidatesImpl::isSink(e, _) and
|
||||
exists(Callable callable |
|
||||
callable = FrameworkCandidatesImpl::getCallable(e) and
|
||||
callable.getName().toLowerCase() = ["exists", "notexists"] and
|
||||
callable.getReturnType() instanceof BooleanType
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A negative characteristic that indicates that an endpoint is an argument to an exception, which is not a sink.
|
||||
*/
|
||||
private class ExceptionCharacteristic extends CharacteristicsImpl::NotASinkCharacteristic {
|
||||
ExceptionCharacteristic() { this = "exception" }
|
||||
|
||||
override predicate appliesToEndpoint(Endpoint e) {
|
||||
FrameworkCandidatesImpl::getCallable(e).getDeclaringType().getASupertype*() instanceof
|
||||
TypeThrowable
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A characteristic that limits candidates to parameters of methods that are recognized as `ModelApi`, iow., APIs that
|
||||
* are considered worth modeling.
|
||||
*/
|
||||
private class NotAModelApiParameter extends CharacteristicsImpl::UninterestingToModelCharacteristic {
|
||||
NotAModelApiParameter() { this = "not a model API parameter" }
|
||||
|
||||
override predicate appliesToEndpoint(Endpoint e) {
|
||||
not exists(ModelExclusions::ModelApi api | api.getAParameter() = e.asParameter())
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A negative characteristic that filters out non-public methods. Non-public methods are not interesting to include in
|
||||
* the standard Java modeling, because they cannot be called from outside the package.
|
||||
*/
|
||||
private class NonPublicMethodCharacteristic extends CharacteristicsImpl::UninterestingToModelCharacteristic
|
||||
{
|
||||
NonPublicMethodCharacteristic() { this = "non-public method" }
|
||||
|
||||
override predicate appliesToEndpoint(Endpoint e) {
|
||||
not FrameworkCandidatesImpl::getCallable(e).isPublic()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if the given endpoint has a self-contradictory combination of characteristics. Detects errors in our endpoint
|
||||
* characteristics. Lists the problematic characteristics and their implications for all such endpoints, together with
|
||||
* an error message indicating why this combination is problematic.
|
||||
*
|
||||
* Copied from
|
||||
* javascript/ql/experimental/adaptivethreatmodeling/test/endpoint_large_scale/ContradictoryEndpointCharacteristics.ql
|
||||
*/
|
||||
predicate erroneousEndpoints(
|
||||
Endpoint endpoint, EndpointCharacteristic characteristic,
|
||||
AutomodelEndpointTypes::EndpointType endpointType, float confidence, string errorMessage,
|
||||
boolean ignoreKnownModelingErrors
|
||||
) {
|
||||
// An endpoint's characteristics should not include positive indicators with medium/high confidence for more than one
|
||||
// sink/source type (including the negative type).
|
||||
exists(
|
||||
EndpointCharacteristic characteristic2, AutomodelEndpointTypes::EndpointType endpointClass2,
|
||||
float confidence2
|
||||
|
|
||||
endpointType != endpointClass2 and
|
||||
(
|
||||
endpointType instanceof AutomodelEndpointTypes::SinkType and
|
||||
endpointClass2 instanceof AutomodelEndpointTypes::SinkType
|
||||
or
|
||||
endpointType instanceof AutomodelEndpointTypes::SourceType and
|
||||
endpointClass2 instanceof AutomodelEndpointTypes::SourceType
|
||||
) and
|
||||
characteristic.appliesToEndpoint(endpoint) and
|
||||
characteristic2.appliesToEndpoint(endpoint) and
|
||||
characteristic.hasImplications(endpointType, true, confidence) and
|
||||
characteristic2.hasImplications(endpointClass2, true, confidence2) and
|
||||
confidence > SharedCharacteristics::mediumConfidence() and
|
||||
confidence2 > SharedCharacteristics::mediumConfidence() and
|
||||
(
|
||||
ignoreKnownModelingErrors = true and
|
||||
not knownOverlappingCharacteristics(characteristic, characteristic2)
|
||||
or
|
||||
ignoreKnownModelingErrors = false
|
||||
)
|
||||
) and
|
||||
errorMessage = "Endpoint has high-confidence positive indicators for multiple classes"
|
||||
or
|
||||
// An endpoint's characteristics should not include positive indicators with medium/high confidence for some class and
|
||||
// also include negative indicators with medium/high confidence for this same class.
|
||||
exists(EndpointCharacteristic characteristic2, float confidence2 |
|
||||
characteristic.appliesToEndpoint(endpoint) and
|
||||
characteristic2.appliesToEndpoint(endpoint) and
|
||||
characteristic.hasImplications(endpointType, true, confidence) and
|
||||
characteristic2.hasImplications(endpointType, false, confidence2) and
|
||||
confidence > SharedCharacteristics::mediumConfidence() and
|
||||
confidence2 > SharedCharacteristics::mediumConfidence()
|
||||
) and
|
||||
ignoreKnownModelingErrors = false and
|
||||
errorMessage = "Endpoint has high-confidence positive and negative indicators for the same class"
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `characteristic1` and `characteristic2` are among the pairs of currently known positive characteristics that
|
||||
* have some overlap in their results. This indicates a problem with the underlying Java modeling. Specifically,
|
||||
* `PathCreation` is prone to FPs.
|
||||
*/
|
||||
private predicate knownOverlappingCharacteristics(
|
||||
EndpointCharacteristic characteristic1, EndpointCharacteristic characteristic2
|
||||
) {
|
||||
characteristic1 != characteristic2 and
|
||||
characteristic1 = ["mad taint step", "create path", "read file", "known non-sink"] and
|
||||
characteristic2 = ["mad taint step", "create path", "read file", "known non-sink"]
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
/**
|
||||
* Surfaces the endpoints that are not already known to be sinks, and are therefore used as candidates for
|
||||
* classification with an ML model.
|
||||
*
|
||||
* Note: This query does not actually classify the endpoints using the model.
|
||||
*
|
||||
* @name Automodel candidates
|
||||
* @description A query to extract automodel candidates.
|
||||
* @kind problem
|
||||
* @severity info
|
||||
* @id java/ml/extract-automodel-candidates
|
||||
* @tags internal automodel extract candidates
|
||||
*/
|
||||
|
||||
private import AutomodelFrameworkModeCharacteristics
|
||||
private import AutomodelSharedUtil
|
||||
|
||||
from
|
||||
Endpoint endpoint, string message, MetadataExtractor meta, string package, string type,
|
||||
boolean subtypes, string name, string signature, int input
|
||||
where
|
||||
not exists(CharacteristicsImpl::UninterestingToModelCharacteristic u |
|
||||
u.appliesToEndpoint(endpoint)
|
||||
) and
|
||||
// If a node is already a known sink for any of our existing ATM queries and is already modeled as a MaD sink, we
|
||||
// don't include it as a candidate. Otherwise, we might include it as a candidate for query A, but the model will
|
||||
// label it as a sink for one of the sink types of query B, for which it's already a known sink. This would result in
|
||||
// overlap between our detected sinks and the pre-existing modeling. We assume that, if a sink has already been
|
||||
// modeled in a MaD model, then it doesn't belong to any additional sink types, and we don't need to reexamine it.
|
||||
not CharacteristicsImpl::isSink(endpoint, _) and
|
||||
meta.hasMetadata(endpoint, package, type, subtypes, name, signature, input) and
|
||||
// The message is the concatenation of all sink types for which this endpoint is known neither to be a sink nor to be
|
||||
// a non-sink, and we surface only endpoints that have at least one such sink type.
|
||||
message =
|
||||
strictconcat(AutomodelEndpointTypes::SinkType sinkType |
|
||||
not CharacteristicsImpl::isKnownSink(endpoint, sinkType) and
|
||||
CharacteristicsImpl::isSinkCandidate(endpoint, sinkType)
|
||||
|
|
||||
sinkType, ", "
|
||||
)
|
||||
select endpoint,
|
||||
message + "\nrelated locations: $@, $@." + "\nmetadata: $@, $@, $@, $@, $@, $@.", //
|
||||
CharacteristicsImpl::getRelatedLocationOrCandidate(endpoint, MethodDoc()), "MethodDoc", //
|
||||
CharacteristicsImpl::getRelatedLocationOrCandidate(endpoint, ClassDoc()), "ClassDoc", //
|
||||
package.(DollarAtString), "package", //
|
||||
type.(DollarAtString), "type", //
|
||||
subtypes.toString().(DollarAtString), "subtypes", //
|
||||
name.(DollarAtString), "name", //
|
||||
signature.(DollarAtString), "signature", //
|
||||
input.toString().(DollarAtString), "input" //
|
||||
@@ -0,0 +1,47 @@
|
||||
/**
|
||||
* Surfaces endpoints that are non-sinks with high confidence, for use as negative examples in the prompt.
|
||||
*
|
||||
* @name Negative examples (experimental)
|
||||
* @kind problem
|
||||
* @severity info
|
||||
* @id java/ml/non-sink
|
||||
* @tags internal automodel extract examples negative
|
||||
*/
|
||||
|
||||
private import AutomodelFrameworkModeCharacteristics
|
||||
private import AutomodelEndpointTypes
|
||||
private import AutomodelSharedUtil
|
||||
|
||||
from
|
||||
Endpoint endpoint, EndpointCharacteristic characteristic, float confidence, string message,
|
||||
MetadataExtractor meta, string package, string type, boolean subtypes, string name,
|
||||
string signature, int input
|
||||
where
|
||||
characteristic.appliesToEndpoint(endpoint) and
|
||||
confidence >= SharedCharacteristics::highConfidence() and
|
||||
characteristic.hasImplications(any(NegativeSinkType negative), true, confidence) and
|
||||
// Exclude endpoints that have contradictory endpoint characteristics, because we only want examples we're highly
|
||||
// certain about in the prompt.
|
||||
not erroneousEndpoints(endpoint, _, _, _, _, false) and
|
||||
meta.hasMetadata(endpoint, package, type, subtypes, name, signature, input) and
|
||||
// It's valid for a node to satisfy the logic for both `isSink` and `isSanitizer`, but in that case it will be
|
||||
// treated by the actual query as a sanitizer, since the final logic is something like
|
||||
// `isSink(n) and not isSanitizer(n)`. We don't want to include such nodes as negative examples in the prompt, because
|
||||
// they're ambiguous and might confuse the model, so we explicitly exclude all known sinks from the negative examples.
|
||||
not exists(EndpointCharacteristic characteristic2, float confidence2, SinkType positiveType |
|
||||
not positiveType instanceof NegativeSinkType and
|
||||
characteristic2.appliesToEndpoint(endpoint) and
|
||||
confidence2 >= SharedCharacteristics::maximalConfidence() and
|
||||
characteristic2.hasImplications(positiveType, true, confidence2)
|
||||
) and
|
||||
message = characteristic
|
||||
select endpoint,
|
||||
message + "\nrelated locations: $@, $@." + "\nmetadata: $@, $@, $@, $@, $@, $@.", //
|
||||
CharacteristicsImpl::getRelatedLocationOrCandidate(endpoint, MethodDoc()), "MethodDoc", //
|
||||
CharacteristicsImpl::getRelatedLocationOrCandidate(endpoint, ClassDoc()), "ClassDoc", //
|
||||
package.(DollarAtString), "package", //
|
||||
type.(DollarAtString), "type", //
|
||||
subtypes.toString().(DollarAtString), "subtypes", //
|
||||
name.(DollarAtString), "name", //
|
||||
signature.(DollarAtString), "signature", //
|
||||
input.toString().(DollarAtString), "input" //
|
||||
@@ -0,0 +1,34 @@
|
||||
/**
|
||||
* Surfaces endpoints that are sinks with high confidence, for use as positive examples in the prompt.
|
||||
*
|
||||
* @name Positive examples (experimental)
|
||||
* @kind problem
|
||||
* @severity info
|
||||
* @id java/ml/known-sink
|
||||
* @tags internal automodel extract examples positive
|
||||
*/
|
||||
|
||||
private import AutomodelFrameworkModeCharacteristics
|
||||
private import AutomodelEndpointTypes
|
||||
private import AutomodelSharedUtil
|
||||
|
||||
from
|
||||
Endpoint endpoint, SinkType sinkType, MetadataExtractor meta, string package, string type,
|
||||
boolean subtypes, string name, string signature, int input
|
||||
where
|
||||
// Exclude endpoints that have contradictory endpoint characteristics, because we only want examples we're highly
|
||||
// certain about in the prompt.
|
||||
not erroneousEndpoints(endpoint, _, _, _, _, false) and
|
||||
meta.hasMetadata(endpoint, package, type, subtypes, name, signature, input) and
|
||||
// Extract positive examples of sinks belonging to the existing ATM query configurations.
|
||||
CharacteristicsImpl::isKnownSink(endpoint, sinkType)
|
||||
select endpoint,
|
||||
sinkType + "\nrelated locations: $@, $@." + "\nmetadata: $@, $@, $@, $@, $@, $@.", //
|
||||
CharacteristicsImpl::getRelatedLocationOrCandidate(endpoint, MethodDoc()), "MethodDoc", //
|
||||
CharacteristicsImpl::getRelatedLocationOrCandidate(endpoint, ClassDoc()), "ClassDoc", //
|
||||
package.(DollarAtString), "package", //
|
||||
type.(DollarAtString), "type", //
|
||||
subtypes.toString().(DollarAtString), "subtypes", //
|
||||
name.(DollarAtString), "name", //
|
||||
signature.(DollarAtString), "signature", //
|
||||
input.toString().(DollarAtString), "input" //
|
||||
305
java/ql/src/Telemetry/AutomodelSharedCharacteristics.qll
Normal file
305
java/ql/src/Telemetry/AutomodelSharedCharacteristics.qll
Normal file
@@ -0,0 +1,305 @@
|
||||
float maximalConfidence() { result = 1.0 }
|
||||
|
||||
float highConfidence() { result = 0.9 }
|
||||
|
||||
float mediumConfidence() { result = 0.6 }
|
||||
|
||||
/**
|
||||
* A specification of how to instantiate the shared characteristics for a given candidate class.
|
||||
*
|
||||
* The `CandidateSig` implementation specifies a type to use for Endpoints (eg., `ParameterNode`), as well as a type
|
||||
* to label endpoint classes (the `EndpointType`). One of the endpoint classes needs to be a 'negative' class, meaning
|
||||
* "not any of the other known endpoint types".
|
||||
*/
|
||||
signature module CandidateSig {
|
||||
/**
|
||||
* An endpoint is a potential candidate for modeling. This will typically be bound to the language's
|
||||
* DataFlow node class, or a subtype thereof.
|
||||
*/
|
||||
class Endpoint;
|
||||
|
||||
/**
|
||||
* A related location for an endpoint. This will typically be bound to the supertype of all AST nodes (eg., `Top`).
|
||||
*/
|
||||
class RelatedLocation;
|
||||
|
||||
/**
|
||||
* A label for a related location.
|
||||
*
|
||||
* Eg., method-doc, class-doc, etc.
|
||||
*/
|
||||
class RelatedLocationType;
|
||||
|
||||
/**
|
||||
* A class kind for an endpoint.
|
||||
*/
|
||||
class EndpointType extends string;
|
||||
|
||||
/**
|
||||
* An EndpointType that denotes the absence of any sink.
|
||||
*/
|
||||
class NegativeEndpointType extends EndpointType;
|
||||
|
||||
/**
|
||||
* Gets the endpoint as a location.
|
||||
*
|
||||
* This is a utility function to convert an endpoint to its corresponding location.
|
||||
*/
|
||||
RelatedLocation asLocation(Endpoint e);
|
||||
|
||||
/**
|
||||
* Defines what MaD kinds are known, and what endpoint type they correspond to.
|
||||
*/
|
||||
predicate isKnownKind(string kind, string humanReadableLabel, EndpointType type);
|
||||
|
||||
/**
|
||||
* Holds if `e` is a flow sanitizer, and has type `t`.
|
||||
*/
|
||||
predicate isSanitizer(Endpoint e, EndpointType t);
|
||||
|
||||
/**
|
||||
* Holds if `e` is a sink with the label `kind`.
|
||||
*/
|
||||
predicate isSink(Endpoint e, string kind);
|
||||
|
||||
/**
|
||||
* Holds if `e` is not a sink of any kind.
|
||||
*/
|
||||
predicate isNeutral(Endpoint e);
|
||||
|
||||
/**
|
||||
* Gets a related location.
|
||||
*
|
||||
* A related location is a source code location that may hold extra information about an endpoint that can be useful
|
||||
* to the machine learning model.
|
||||
*
|
||||
* For example, a related location for a method call may be the documentation comment of a method.
|
||||
*/
|
||||
RelatedLocation getRelatedLocation(Endpoint e, RelatedLocationType name);
|
||||
}
|
||||
|
||||
/**
|
||||
* A set of shared characteristics for a given candidate class.
|
||||
*
|
||||
* This module is language-agnostic, although the `CandidateSig` module will be language-specific.
|
||||
*
|
||||
* The language specific implementation can also further extend the behavior of this module by adding additional
|
||||
* implementations of endpoint characteristics exported by this module.
|
||||
*/
|
||||
module SharedCharacteristics<CandidateSig Candidate> {
|
||||
predicate isSink = Candidate::isSink/2;
|
||||
|
||||
predicate isNeutral = Candidate::isNeutral/1;
|
||||
|
||||
/**
|
||||
* Holds if `sink` is a known sink of type `endpointType`.
|
||||
*/
|
||||
predicate isKnownSink(Candidate::Endpoint sink, Candidate::EndpointType endpointType) {
|
||||
// If the list of characteristics includes positive indicators with maximal confidence for this class, then it's a
|
||||
// known sink for the class.
|
||||
not endpointType instanceof Candidate::NegativeEndpointType and
|
||||
exists(EndpointCharacteristic characteristic |
|
||||
characteristic.appliesToEndpoint(sink) and
|
||||
characteristic.hasImplications(endpointType, true, maximalConfidence())
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if the candidate sink `candidateSink` should be considered as a possible sink of type `sinkType`, and
|
||||
* classified by the ML model. A candidate sink is a node that cannot be excluded from `sinkType` based on its
|
||||
* characteristics.
|
||||
*/
|
||||
predicate isSinkCandidate(Candidate::Endpoint candidateSink, Candidate::EndpointType sinkType) {
|
||||
not sinkType instanceof Candidate::NegativeEndpointType and
|
||||
not exists(getAReasonSinkExcluded(candidateSink, sinkType))
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the related location of `e` with name `name`, if it exists.
|
||||
* Otherwise, gets the candidate itself.
|
||||
*/
|
||||
Candidate::RelatedLocation getRelatedLocationOrCandidate(
|
||||
Candidate::Endpoint e, Candidate::RelatedLocationType type
|
||||
) {
|
||||
if exists(Candidate::getRelatedLocation(e, type))
|
||||
then result = Candidate::getRelatedLocation(e, type)
|
||||
else result = Candidate::asLocation(e)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the list of characteristics that cause `candidateSink` to be excluded as an effective sink for a given sink
|
||||
* type.
|
||||
*/
|
||||
EndpointCharacteristic getAReasonSinkExcluded(
|
||||
Candidate::Endpoint candidateSink, Candidate::EndpointType sinkType
|
||||
) {
|
||||
// An endpoint is a sink candidate if none of its characteristics give much indication whether or not it is a sink.
|
||||
not sinkType instanceof Candidate::NegativeEndpointType and
|
||||
result.appliesToEndpoint(candidateSink) and
|
||||
(
|
||||
// Exclude endpoints that have a characteristic that implies they're not sinks for _any_ sink type.
|
||||
exists(float confidence |
|
||||
confidence >= mediumConfidence() and
|
||||
result.hasImplications(any(Candidate::NegativeEndpointType t), true, confidence)
|
||||
)
|
||||
or
|
||||
// Exclude endpoints that have a characteristic that implies they're not sinks for _this particular_ sink type.
|
||||
exists(float confidence |
|
||||
confidence >= mediumConfidence() and
|
||||
result.hasImplications(sinkType, false, confidence)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* A set of characteristics that a particular endpoint might have. This set of characteristics is used to make decisions
|
||||
* about whether to include the endpoint in the training set and with what kind, as well as whether to score the
|
||||
* endpoint at inference time.
|
||||
*/
|
||||
abstract class EndpointCharacteristic extends string {
|
||||
/**
|
||||
* Holds for the string that is the name of the characteristic. This should describe some property of an endpoint
|
||||
* that is meaningful for determining whether it's a sink, and if so, of which sink type.
|
||||
*/
|
||||
bindingset[this]
|
||||
EndpointCharacteristic() { any() }
|
||||
|
||||
/**
|
||||
* Holds for endpoints that have this characteristic.
|
||||
*/
|
||||
abstract predicate appliesToEndpoint(Candidate::Endpoint n);
|
||||
|
||||
/**
|
||||
* This predicate describes what the characteristic tells us about an endpoint.
|
||||
*
|
||||
* Params:
|
||||
* endpointType: The sink/source type.
|
||||
* isPositiveIndicator: If true, this characteristic indicates that this endpoint _is_ a member of the class; if
|
||||
* false, it indicates that it _isn't_ a member of the class.
|
||||
* confidence: A float in [0, 1], which tells us how strong an indicator this characteristic is for the endpoint
|
||||
* belonging / not belonging to the given class. A confidence near zero means this characteristic is a very weak
|
||||
* indicator of whether or not the endpoint belongs to the class. A confidence of 1 means that all endpoints with
|
||||
* this characteristic definitively do/don't belong to the class.
|
||||
*/
|
||||
abstract predicate hasImplications(
|
||||
Candidate::EndpointType endpointType, boolean isPositiveIndicator, float confidence
|
||||
);
|
||||
|
||||
/** Indicators with confidence at or above this threshold are considered to be high-confidence indicators. */
|
||||
final float getHighConfidenceThreshold() { result = 0.8 }
|
||||
}
|
||||
|
||||
/**
|
||||
* A high-confidence characteristic that indicates that an endpoint is a sink of a specified type. These endpoints can
|
||||
* be used as positive samples for training or for a few-shot prompt.
|
||||
*/
|
||||
abstract class SinkCharacteristic extends EndpointCharacteristic {
|
||||
bindingset[this]
|
||||
SinkCharacteristic() { any() }
|
||||
|
||||
abstract Candidate::EndpointType getSinkType();
|
||||
|
||||
final override predicate hasImplications(
|
||||
Candidate::EndpointType endpointType, boolean isPositiveIndicator, float confidence
|
||||
) {
|
||||
endpointType = this.getSinkType() and
|
||||
isPositiveIndicator = true and
|
||||
confidence = maximalConfidence()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A high-confidence characteristic that indicates that an endpoint is not a sink of any type. These endpoints can be
|
||||
* used as negative samples for training or for a few-shot prompt.
|
||||
*/
|
||||
abstract class NotASinkCharacteristic extends EndpointCharacteristic {
|
||||
bindingset[this]
|
||||
NotASinkCharacteristic() { any() }
|
||||
|
||||
override predicate hasImplications(
|
||||
Candidate::EndpointType endpointType, boolean isPositiveIndicator, float confidence
|
||||
) {
|
||||
endpointType instanceof Candidate::NegativeEndpointType and
|
||||
isPositiveIndicator = true and
|
||||
confidence = highConfidence()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A medium-confidence characteristic that indicates that an endpoint is unlikely to be a sink of any type. These
|
||||
* endpoints can be excluded from scoring at inference time, both to save time and to avoid false positives. They should
|
||||
* not, however, be used as negative samples for training or for a few-shot prompt, because they may include a small
|
||||
* number of sinks.
|
||||
*/
|
||||
abstract class LikelyNotASinkCharacteristic extends EndpointCharacteristic {
|
||||
bindingset[this]
|
||||
LikelyNotASinkCharacteristic() { any() }
|
||||
|
||||
override predicate hasImplications(
|
||||
Candidate::EndpointType endpointType, boolean isPositiveIndicator, float confidence
|
||||
) {
|
||||
endpointType instanceof Candidate::NegativeEndpointType and
|
||||
isPositiveIndicator = true and
|
||||
confidence = mediumConfidence()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A characteristic that indicates not necessarily that an endpoint is not a sink, but rather that it is not a sink
|
||||
* that's interesting to model in the standard Java libraries. These filters should be removed when extracting sink
|
||||
* candidates within a user's codebase for customized modeling.
|
||||
*
|
||||
* These endpoints should not be used as negative samples for training or for a few-shot prompt, because they are not
|
||||
* necessarily non-sinks.
|
||||
*/
|
||||
abstract class UninterestingToModelCharacteristic extends EndpointCharacteristic {
|
||||
bindingset[this]
|
||||
UninterestingToModelCharacteristic() { any() }
|
||||
|
||||
override predicate hasImplications(
|
||||
Candidate::EndpointType endpointType, boolean isPositiveIndicator, float confidence
|
||||
) {
|
||||
endpointType instanceof Candidate::NegativeEndpointType and
|
||||
isPositiveIndicator = true and
|
||||
confidence = mediumConfidence()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Contains default implementations that are derived solely from the `CandidateSig` implementation.
|
||||
*/
|
||||
private module DefaultCharacteristicImplementations {
|
||||
/**
|
||||
* Endpoints identified as sinks by the `CandidateSig` implementation are sinks with maximal confidence.
|
||||
*/
|
||||
private class KnownSinkCharacteristic extends SinkCharacteristic {
|
||||
string madKind;
|
||||
Candidate::EndpointType endpointType;
|
||||
|
||||
KnownSinkCharacteristic() { Candidate::isKnownKind(madKind, this, endpointType) }
|
||||
|
||||
override predicate appliesToEndpoint(Candidate::Endpoint e) { Candidate::isSink(e, madKind) }
|
||||
|
||||
override Candidate::EndpointType getSinkType() { result = endpointType }
|
||||
}
|
||||
|
||||
/**
|
||||
* A negative characteristic that indicates that an endpoint was manually modeled as a neutral model.
|
||||
*/
|
||||
private class NeutralModelCharacteristic extends NotASinkCharacteristic {
|
||||
NeutralModelCharacteristic() { this = "known non-sink" }
|
||||
|
||||
override predicate appliesToEndpoint(Candidate::Endpoint e) { Candidate::isNeutral(e) }
|
||||
}
|
||||
|
||||
/**
|
||||
* A negative characteristic that indicates that an endpoint is not part of the source code for the project being
|
||||
* analyzed.
|
||||
*/
|
||||
private class IsSanitizerCharacteristic extends NotASinkCharacteristic {
|
||||
IsSanitizerCharacteristic() { this = "external" }
|
||||
|
||||
override predicate appliesToEndpoint(Candidate::Endpoint e) { Candidate::isSanitizer(e, _) }
|
||||
}
|
||||
}
|
||||
}
|
||||
21
java/ql/src/Telemetry/AutomodelSharedUtil.qll
Normal file
21
java/ql/src/Telemetry/AutomodelSharedUtil.qll
Normal file
@@ -0,0 +1,21 @@
|
||||
/**
|
||||
* A helper class to represent a string value that can be returned by a query using $@ notation.
|
||||
*
|
||||
* It extends `string`, but adds a mock `hasLocationInfo` method that returns the string itself as the file name.
|
||||
*
|
||||
* Use this, when you want to return a string value from a query using $@ notation - the string value
|
||||
* will be included in the sarif file.
|
||||
*
|
||||
*
|
||||
* Background information on `hasLocationInfo`:
|
||||
* https://codeql.github.com/docs/writing-codeql-queries/providing-locations-in-codeql-queries/#providing-location-information
|
||||
*/
|
||||
class DollarAtString extends string {
|
||||
bindingset[this]
|
||||
DollarAtString() { any() }
|
||||
|
||||
bindingset[this]
|
||||
predicate hasLocationInfo(string path, int sl, int sc, int el, int ec) {
|
||||
path = this and sl = 1 and sc = 1 and el = 1 and ec = 1
|
||||
}
|
||||
}
|
||||
@@ -10,10 +10,6 @@ private import semmle.code.java.dataflow.internal.FlowSummaryImpl as FlowSummary
|
||||
private import semmle.code.java.dataflow.TaintTracking
|
||||
private import semmle.code.java.dataflow.internal.ModelExclusions
|
||||
|
||||
private string containerAsJar(Container container) {
|
||||
if container instanceof JarFile then result = container.getBaseName() else result = "rt.jar"
|
||||
}
|
||||
|
||||
/** Holds if the given callable is not worth supporting. */
|
||||
private predicate isUninteresting(Callable c) {
|
||||
c.getDeclaringType() instanceof TestLibrary or
|
||||
@@ -35,10 +31,18 @@ class ExternalApi extends Callable {
|
||||
"#" + this.getName() + paramsString(this)
|
||||
}
|
||||
|
||||
private string getJarName() {
|
||||
result = this.getCompilationUnit().getParentContainer*().(JarFile).getBaseName()
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the jar file containing this API. Normalizes the Java Runtime to "rt.jar" despite the presence of modules.
|
||||
*/
|
||||
string jarContainer() { result = containerAsJar(this.getCompilationUnit().getParentContainer*()) }
|
||||
string jarContainer() {
|
||||
result = this.getJarName()
|
||||
or
|
||||
not exists(this.getJarName()) and result = "rt.jar"
|
||||
}
|
||||
|
||||
/** Gets a node that is an input to a call to this API. */
|
||||
private DataFlow::Node getAnInput() {
|
||||
|
||||
4
java/ql/src/change-notes/2023-05-15-xpath-xxe-sink.md
Normal file
4
java/ql/src/change-notes/2023-05-15-xpath-xxe-sink.md
Normal file
@@ -0,0 +1,4 @@
|
||||
---
|
||||
category: minorAnalysis
|
||||
---
|
||||
* The queries `java/xxe` and `java/xxe-local` now recognize the second argument of calls to `XPath.evaluate` as a sink.
|
||||
3
java/ql/src/change-notes/released/0.6.1.md
Normal file
3
java/ql/src/change-notes/released/0.6.1.md
Normal file
@@ -0,0 +1,3 @@
|
||||
## 0.6.1
|
||||
|
||||
No user-facing changes.
|
||||
@@ -1,2 +1,2 @@
|
||||
---
|
||||
lastReleaseVersion: 0.6.0
|
||||
lastReleaseVersion: 0.6.1
|
||||
|
||||
@@ -17,6 +17,10 @@ import semmle.code.java.dataflow.FlowSources
|
||||
import semmle.code.java.dataflow.ExternalFlow
|
||||
import RemoteUrlToOpenStreamFlow::PathGraph
|
||||
|
||||
private class ActivateModels extends ActiveExperimentalModels {
|
||||
ActivateModels() { this = "openstream-called-on-tainted-url" }
|
||||
}
|
||||
|
||||
class UrlConstructor extends ClassInstanceExpr {
|
||||
UrlConstructor() { this.getConstructor().getDeclaringType() instanceof TypeUrl }
|
||||
|
||||
|
||||
@@ -17,11 +17,11 @@ class WebRequestSource extends DataFlow::Node {
|
||||
m.hasName("getParameterNames") or
|
||||
m.hasName("getParameterMap")
|
||||
) and
|
||||
ma = asExpr()
|
||||
ma = this.asExpr()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
class WebRequest extends RefType {
|
||||
WebRequest() { hasQualifiedName("org.springframework.web.context.request", "WebRequest") }
|
||||
WebRequest() { this.hasQualifiedName("org.springframework.web.context.request", "WebRequest") }
|
||||
}
|
||||
|
||||
@@ -24,7 +24,7 @@ class SetRevocationEnabledSink extends DataFlow::ExprNode {
|
||||
SetRevocationEnabledSink() {
|
||||
exists(MethodAccess setRevocationEnabledCall |
|
||||
setRevocationEnabledCall.getMethod() instanceof SetRevocationEnabledMethod and
|
||||
setRevocationEnabledCall.getArgument(0) = getExpr() and
|
||||
setRevocationEnabledCall.getArgument(0) = this.getExpr() and
|
||||
not exists(MethodAccess ma, Method m | m = ma.getMethod() |
|
||||
(m instanceof AddCertPathCheckerMethod or m instanceof SetCertPathCheckersMethod) and
|
||||
ma.getQualifier().(VarAccess).getVariable() =
|
||||
@@ -36,25 +36,25 @@ class SetRevocationEnabledSink extends DataFlow::ExprNode {
|
||||
|
||||
class SetRevocationEnabledMethod extends Method {
|
||||
SetRevocationEnabledMethod() {
|
||||
getDeclaringType() instanceof PKIXParameters and
|
||||
hasName("setRevocationEnabled")
|
||||
this.getDeclaringType() instanceof PKIXParameters and
|
||||
this.hasName("setRevocationEnabled")
|
||||
}
|
||||
}
|
||||
|
||||
class AddCertPathCheckerMethod extends Method {
|
||||
AddCertPathCheckerMethod() {
|
||||
getDeclaringType() instanceof PKIXParameters and
|
||||
hasName("addCertPathChecker")
|
||||
this.getDeclaringType() instanceof PKIXParameters and
|
||||
this.hasName("addCertPathChecker")
|
||||
}
|
||||
}
|
||||
|
||||
class SetCertPathCheckersMethod extends Method {
|
||||
SetCertPathCheckersMethod() {
|
||||
getDeclaringType() instanceof PKIXParameters and
|
||||
hasName("setCertPathCheckers")
|
||||
this.getDeclaringType() instanceof PKIXParameters and
|
||||
this.hasName("setCertPathCheckers")
|
||||
}
|
||||
}
|
||||
|
||||
class PKIXParameters extends RefType {
|
||||
PKIXParameters() { hasQualifiedName("java.security.cert", "PKIXParameters") }
|
||||
PKIXParameters() { this.hasQualifiedName("java.security.cert", "PKIXParameters") }
|
||||
}
|
||||
|
||||
@@ -27,7 +27,7 @@ class SslContextGetInstanceSink extends DataFlow::ExprNode {
|
||||
exists(StaticMethodAccess ma, Method m | m = ma.getMethod() |
|
||||
m.getDeclaringType() instanceof SslContext and
|
||||
m.hasName("getInstance") and
|
||||
ma.getArgument(0) = asExpr()
|
||||
ma.getArgument(0) = this.asExpr()
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -39,7 +39,7 @@ class SslContextGetInstanceSink extends DataFlow::ExprNode {
|
||||
class CreateSslParametersSink extends DataFlow::ExprNode {
|
||||
CreateSslParametersSink() {
|
||||
exists(ConstructorCall cc | cc.getConstructedType() instanceof SslParameters |
|
||||
cc.getArgument(1) = asExpr()
|
||||
cc.getArgument(1) = this.asExpr()
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -53,7 +53,7 @@ class SslParametersSetProtocolsSink extends DataFlow::ExprNode {
|
||||
exists(MethodAccess ma, Method m | m = ma.getMethod() |
|
||||
m.getDeclaringType() instanceof SslParameters and
|
||||
m.hasName("setProtocols") and
|
||||
ma.getArgument(0) = asExpr()
|
||||
ma.getArgument(0) = this.asExpr()
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -73,7 +73,7 @@ class SetEnabledProtocolsSink extends DataFlow::ExprNode {
|
||||
type instanceof SslEngine
|
||||
) and
|
||||
m.hasName("setEnabledProtocols") and
|
||||
ma.getArgument(0) = asExpr()
|
||||
ma.getArgument(0) = this.asExpr()
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -83,15 +83,15 @@ class SetEnabledProtocolsSink extends DataFlow::ExprNode {
|
||||
*/
|
||||
class UnsafeTlsVersion extends StringLiteral {
|
||||
UnsafeTlsVersion() {
|
||||
getValue() = "SSL" or
|
||||
getValue() = "SSLv2" or
|
||||
getValue() = "SSLv3" or
|
||||
getValue() = "TLS" or
|
||||
getValue() = "TLSv1" or
|
||||
getValue() = "TLSv1.1"
|
||||
this.getValue() = "SSL" or
|
||||
this.getValue() = "SSLv2" or
|
||||
this.getValue() = "SSLv3" or
|
||||
this.getValue() = "TLS" or
|
||||
this.getValue() = "TLSv1" or
|
||||
this.getValue() = "TLSv1.1"
|
||||
}
|
||||
}
|
||||
|
||||
class SslServerSocket extends RefType {
|
||||
SslServerSocket() { hasQualifiedName("javax.net.ssl", "SSLServerSocket") }
|
||||
SslServerSocket() { this.hasQualifiedName("javax.net.ssl", "SSLServerSocket") }
|
||||
}
|
||||
|
||||
@@ -31,14 +31,14 @@ private predicate setsAllowCredentials(MethodAccess header) {
|
||||
|
||||
private class CorsProbableCheckAccess extends MethodAccess {
|
||||
CorsProbableCheckAccess() {
|
||||
getMethod().hasName("contains") and
|
||||
getMethod().getDeclaringType().getASourceSupertype*() instanceof CollectionType
|
||||
this.getMethod().hasName("contains") and
|
||||
this.getMethod().getDeclaringType().getASourceSupertype*() instanceof CollectionType
|
||||
or
|
||||
getMethod().hasName("containsKey") and
|
||||
getMethod().getDeclaringType().getASourceSupertype*() instanceof MapType
|
||||
this.getMethod().hasName("containsKey") and
|
||||
this.getMethod().getDeclaringType().getASourceSupertype*() instanceof MapType
|
||||
or
|
||||
getMethod().hasName("equals") and
|
||||
getQualifier().getType() instanceof TypeString
|
||||
this.getMethod().hasName("equals") and
|
||||
this.getQualifier().getType() instanceof TypeString
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -24,10 +24,10 @@ import BindingUnsafeRemoteObjectFlow::PathGraph
|
||||
private class BindMethod extends Method {
|
||||
BindMethod() {
|
||||
(
|
||||
getDeclaringType().hasQualifiedName("java.rmi", "Naming") or
|
||||
getDeclaringType().hasQualifiedName("java.rmi.registry", "Registry")
|
||||
this.getDeclaringType().hasQualifiedName("java.rmi", "Naming") or
|
||||
this.getDeclaringType().hasQualifiedName("java.rmi.registry", "Registry")
|
||||
) and
|
||||
hasName(["bind", "rebind"])
|
||||
this.hasName(["bind", "rebind"])
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
name: codeql/java-queries
|
||||
version: 0.6.1-dev
|
||||
version: 0.6.2-dev
|
||||
groups:
|
||||
- java
|
||||
- queries
|
||||
@@ -12,3 +12,4 @@ dependencies:
|
||||
codeql/util: ${workspace}
|
||||
dataExtensions:
|
||||
- Telemetry/ExtractorInformation.yml
|
||||
warnOnImplicitThis: true
|
||||
|
||||
@@ -7,8 +7,9 @@
|
||||
import java
|
||||
import semmle.code.java.dataflow.ExternalFlow
|
||||
|
||||
from string package, string type, string name, string signature, string provenance
|
||||
from string package, string type, string name, string signature, string kind, string provenance
|
||||
where
|
||||
neutralModel(package, type, name, signature, provenance) and
|
||||
neutralModel(package, type, name, signature, kind, provenance) and
|
||||
not provenance.matches("%generated")
|
||||
select package, type, name, signature, provenance order by package, type, name, signature
|
||||
select package, type, name, signature, kind, provenance order by
|
||||
package, type, name, signature, kind
|
||||
|
||||
@@ -139,9 +139,9 @@ module ThroughFlowConfig implements DataFlow::StateConfigSig {
|
||||
predicate isAdditionalFlowStep(
|
||||
DataFlow::Node node1, FlowState state1, DataFlow::Node node2, FlowState state2
|
||||
) {
|
||||
exists(DataFlowImplCommon::TypedContent tc |
|
||||
DataFlowImplCommon::store(node1, tc, node2, _) and
|
||||
isRelevantContent(tc.getContent()) and
|
||||
exists(DataFlow::Content c |
|
||||
DataFlowImplCommon::store(node1, c, node2, _, _) and
|
||||
isRelevantContent(c) and
|
||||
(
|
||||
state1 instanceof TaintRead and state2.(TaintStore).getStep() = 1
|
||||
or
|
||||
@@ -178,7 +178,7 @@ string captureThroughFlow(DataFlowTargetApi api) {
|
||||
string output
|
||||
|
|
||||
ThroughFlow::flow(p, returnNodeExt) and
|
||||
returnNodeExt.getEnclosingCallable() = api and
|
||||
returnNodeExt.(DataFlow::Node).getEnclosingCallable() = api and
|
||||
input = parameterNodeAsInput(p) and
|
||||
output = returnNodeAsOutput(returnNodeExt) and
|
||||
input != output and
|
||||
|
||||
@@ -25,8 +25,11 @@ module PrintingImpl<PrintingSig Printing> {
|
||||
+ Printing::getProvenance()
|
||||
}
|
||||
|
||||
string asNeutralModel(Printing::Api api) {
|
||||
result = asPartialNeutralModel(api) + Printing::getProvenance()
|
||||
string asNeutralSummaryModel(Printing::Api api) {
|
||||
result =
|
||||
asPartialNeutralModel(api) //
|
||||
+ "summary" + ";" //
|
||||
+ Printing::getProvenance()
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -73,7 +73,9 @@ private string isExtensible(J::RefType ref) {
|
||||
}
|
||||
|
||||
private string typeAsModel(J::RefType type) {
|
||||
result = type.getCompilationUnit().getPackage().getName() + ";" + type.nestedName()
|
||||
result =
|
||||
type.getCompilationUnit().getPackage().getName() + ";" +
|
||||
type.getErasure().(J::RefType).nestedName()
|
||||
}
|
||||
|
||||
private J::RefType bestTypeForModel(TargetApiSpecific api) {
|
||||
@@ -184,7 +186,7 @@ string returnNodeAsOutput(DataFlowImplCommon::ReturnNodeExt node) {
|
||||
exists(int pos |
|
||||
pos = node.getKind().(DataFlowImplCommon::ParamUpdateReturnKind).getPosition()
|
||||
|
|
||||
result = parameterAccess(node.getEnclosingCallable().getParameter(pos))
|
||||
result = parameterAccess(node.(DataFlow::Node).getEnclosingCallable().getParameter(pos))
|
||||
or
|
||||
result = qualifierString() and pos = -1
|
||||
)
|
||||
@@ -236,7 +238,7 @@ predicate apiSource(DataFlow::Node source) {
|
||||
string asInputArgumentSpecific(DataFlow::Node source) {
|
||||
exists(int pos |
|
||||
source.(DataFlow::ParameterNode).isParameterOf(_, pos) and
|
||||
result = "Argument[" + pos + "]"
|
||||
if pos >= 0 then result = "Argument[" + pos + "]" else result = qualifierString()
|
||||
)
|
||||
or
|
||||
source.asExpr() instanceof J::FieldAccess and
|
||||
|
||||
@@ -78,5 +78,5 @@ string captureFlow(DataFlowTargetApi api) {
|
||||
*/
|
||||
string captureNoFlow(DataFlowTargetApi api) {
|
||||
not exists(captureFlow(api)) and
|
||||
result = ModelPrinting::asNeutralModel(api)
|
||||
result = ModelPrinting::asNeutralSummaryModel(api)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user