mirror of
https://github.com/github/codeql.git
synced 2026-04-28 10:15:14 +02:00
Merge branch 'main' into MybatisSqli
This commit is contained in:
@@ -0,0 +1,6 @@
|
||||
@AllDefaultsAnnotation
|
||||
public class User {
|
||||
|
||||
public static void test() { new AllDefaultsConstructor(); new AllDefaultsExplicitNoargConstructor(); }
|
||||
|
||||
}
|
||||
@@ -3,3 +3,13 @@ public class Test {
|
||||
@JvmOverloads fun f(x: Int = 0, y: Int) { }
|
||||
|
||||
}
|
||||
|
||||
public class AllDefaultsConstructor(val x: Int = 1, val y: Int = 2) { }
|
||||
|
||||
public annotation class AllDefaultsAnnotation(val x: Int = 1, val y: Int = 2) { }
|
||||
|
||||
public class AllDefaultsExplicitNoargConstructor(val x: Int = 1, val y: Int = 2) {
|
||||
|
||||
constructor() : this(3, 4) { }
|
||||
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
from create_database_utils import *
|
||||
|
||||
os.mkdir('bin')
|
||||
run_codeql_database_create(["kotlinc test.kt -d bin", "kotlinc user.kt -cp bin"], lang="java")
|
||||
run_codeql_database_create(["kotlinc test.kt -d bin", "kotlinc user.kt -cp bin", "javac User.java -cp bin"], lang="java")
|
||||
|
||||
@@ -0,0 +1,4 @@
|
||||
---
|
||||
category: minorAnalysis
|
||||
---
|
||||
* The query `java/insecure-cookie` now uses global dataflow to track secure cookies being set to the HTTP response object.
|
||||
@@ -0,0 +1,4 @@
|
||||
---
|
||||
category: minorAnalysis
|
||||
---
|
||||
* The library `PathSanitizer.qll` has been improved to detect more path validation patterns in Kotlin.
|
||||
@@ -80,53 +80,6 @@ private import internal.FlowSummaryImplSpecific as FlowSummaryImplSpecific
|
||||
private import internal.AccessPathSyntax
|
||||
private import FlowSummary
|
||||
|
||||
/**
|
||||
* A module importing the frameworks that provide external flow data,
|
||||
* ensuring that they are visible to the taint tracking / data flow library.
|
||||
*/
|
||||
private module Frameworks {
|
||||
private import internal.ContainerFlow
|
||||
private import semmle.code.java.frameworks.android.Android
|
||||
private import semmle.code.java.frameworks.android.ContentProviders
|
||||
private import semmle.code.java.frameworks.android.ExternalStorage
|
||||
private import semmle.code.java.frameworks.android.Intent
|
||||
private import semmle.code.java.frameworks.android.SharedPreferences
|
||||
private import semmle.code.java.frameworks.android.Slice
|
||||
private import semmle.code.java.frameworks.android.SQLite
|
||||
private import semmle.code.java.frameworks.android.Widget
|
||||
private import semmle.code.java.frameworks.ApacheHttp
|
||||
private import semmle.code.java.frameworks.apache.Collections
|
||||
private import semmle.code.java.frameworks.apache.Lang
|
||||
private import semmle.code.java.frameworks.Flexjson
|
||||
private import semmle.code.java.frameworks.guava.Guava
|
||||
private import semmle.code.java.frameworks.jackson.JacksonSerializability
|
||||
private import semmle.code.java.frameworks.javaee.jsf.JSFRenderer
|
||||
private import semmle.code.java.frameworks.JaxWS
|
||||
private import semmle.code.java.frameworks.JoddJson
|
||||
private import semmle.code.java.frameworks.Stream
|
||||
private import semmle.code.java.frameworks.ratpack.RatpackExec
|
||||
private import semmle.code.java.frameworks.spring.SpringHttp
|
||||
private import semmle.code.java.frameworks.spring.SpringWebClient
|
||||
private import semmle.code.java.security.AndroidIntentRedirection
|
||||
private import semmle.code.java.security.ResponseSplitting
|
||||
private import semmle.code.java.security.InformationLeak
|
||||
private import semmle.code.java.security.FragmentInjection
|
||||
private import semmle.code.java.security.GroovyInjection
|
||||
private import semmle.code.java.security.ImplicitPendingIntents
|
||||
private import semmle.code.java.security.JndiInjection
|
||||
private import semmle.code.java.security.LdapInjection
|
||||
private import semmle.code.java.security.MvelInjection
|
||||
private import semmle.code.java.security.OgnlInjection
|
||||
private import semmle.code.java.security.TemplateInjection
|
||||
private import semmle.code.java.security.XPath
|
||||
private import semmle.code.java.security.XsltInjection
|
||||
private import semmle.code.java.frameworks.Jdbc
|
||||
private import semmle.code.java.frameworks.SpringJdbc
|
||||
private import semmle.code.java.frameworks.MyBatis
|
||||
private import semmle.code.java.frameworks.Hibernate
|
||||
private import semmle.code.java.frameworks.jOOQ
|
||||
}
|
||||
|
||||
/**
|
||||
* DEPRECATED: Define source models as data extensions instead.
|
||||
*
|
||||
|
||||
@@ -36,6 +36,13 @@ abstract class RemoteFlowSource extends DataFlow::Node {
|
||||
abstract string getSourceType();
|
||||
}
|
||||
|
||||
/**
|
||||
* A module for importing frameworks that define remote flow sources.
|
||||
*/
|
||||
private module RemoteFlowSources {
|
||||
private import semmle.code.java.frameworks.android.Widget
|
||||
}
|
||||
|
||||
private class ExternalRemoteFlowSource extends RemoteFlowSource {
|
||||
ExternalRemoteFlowSource() { sourceNode(this, "remote") }
|
||||
|
||||
|
||||
@@ -10,17 +10,19 @@ private import semmle.code.java.dataflow.DataFlow
|
||||
* ensuring that they are visible to the taint tracking library.
|
||||
*/
|
||||
private module Frameworks {
|
||||
private import semmle.code.java.JDK
|
||||
private import semmle.code.java.frameworks.jackson.JacksonSerializability
|
||||
private import semmle.code.java.frameworks.android.AsyncTask
|
||||
private import semmle.code.java.frameworks.android.Intent
|
||||
private import semmle.code.java.frameworks.android.Slice
|
||||
private import semmle.code.java.frameworks.android.SQLite
|
||||
private import semmle.code.java.frameworks.Guice
|
||||
private import semmle.code.java.frameworks.Properties
|
||||
private import semmle.code.java.frameworks.Protobuf
|
||||
private import semmle.code.java.frameworks.guava.Guava
|
||||
private import semmle.code.java.frameworks.apache.Lang
|
||||
private import semmle.code.java.frameworks.ApacheHttp
|
||||
private import semmle.code.java.frameworks.guava.Guava
|
||||
private import semmle.code.java.frameworks.Guice
|
||||
private import semmle.code.java.frameworks.jackson.JacksonSerializability
|
||||
private import semmle.code.java.frameworks.Properties
|
||||
private import semmle.code.java.frameworks.Protobuf
|
||||
private import semmle.code.java.frameworks.ratpack.RatpackExec
|
||||
private import semmle.code.java.JDK
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -6,11 +6,6 @@ import java
|
||||
private import internal.FlowSummaryImpl as Impl
|
||||
private import internal.DataFlowUtil
|
||||
|
||||
// import all instances of SummarizedCallable below
|
||||
private module Summaries {
|
||||
private import semmle.code.java.dataflow.ExternalFlow
|
||||
}
|
||||
|
||||
class SummaryComponent = Impl::Public::SummaryComponent;
|
||||
|
||||
/** Provides predicates for constructing summary components. */
|
||||
@@ -102,6 +97,14 @@ abstract class SyntheticCallable extends string {
|
||||
Type getReturnType() { none() }
|
||||
}
|
||||
|
||||
/**
|
||||
* A module for importing frameworks that define synthetic callables.
|
||||
*/
|
||||
private module SyntheticCallables {
|
||||
private import semmle.code.java.frameworks.android.Intent
|
||||
private import semmle.code.java.frameworks.Stream
|
||||
}
|
||||
|
||||
private newtype TSummarizedCallableBase =
|
||||
TSimpleCallable(Callable c) { c.isSourceDeclaration() } or
|
||||
TSyntheticCallable(SyntheticCallable c)
|
||||
|
||||
@@ -53,16 +53,6 @@ private class TypeFlowNode extends TTypeFlowNode {
|
||||
}
|
||||
}
|
||||
|
||||
private int getNodeKind(TypeFlowNode n) {
|
||||
result = 1 and n instanceof TField
|
||||
or
|
||||
result = 2 and n instanceof TSsa
|
||||
or
|
||||
result = 3 and n instanceof TExpr
|
||||
or
|
||||
result = 4 and n instanceof TMethod
|
||||
}
|
||||
|
||||
/** Gets `t` if it is a `RefType` or the boxed type if `t` is a primitive type. */
|
||||
private RefType boxIfNeeded(Type t) {
|
||||
t.(PrimitiveType).getBoxedType() = result or
|
||||
@@ -158,107 +148,45 @@ private predicate joinStep(TypeFlowNode n1, TypeFlowNode n2) {
|
||||
|
||||
private predicate anyStep(TypeFlowNode n1, TypeFlowNode n2) { joinStep(n1, n2) or step(n1, n2) }
|
||||
|
||||
private import SccReduction
|
||||
private predicate sccEdge(TypeFlowNode n1, TypeFlowNode n2) { anyStep(n1, n2) and anyStep+(n2, n1) }
|
||||
|
||||
/**
|
||||
* SCC reduction.
|
||||
*
|
||||
* This ought to be as easy as `equivalenceRelation(sccEdge/2)(n, scc)`, but
|
||||
* this HOP is not currently supported for newtypes.
|
||||
*
|
||||
* A straightforward implementation would be:
|
||||
* ```ql
|
||||
* predicate sccRepr(TypeFlowNode n, TypeFlowNode scc) {
|
||||
* scc =
|
||||
* max(TypeFlowNode n2 |
|
||||
* sccEdge+(n, n2)
|
||||
* |
|
||||
* n2
|
||||
* order by
|
||||
* n2.getLocation().getStartLine(), n2.getLocation().getStartColumn(), getNodeKind(n2)
|
||||
* )
|
||||
* }
|
||||
*
|
||||
* ```
|
||||
* but this is quadratic in the size of the SCCs.
|
||||
*
|
||||
* Instead we find local maxima by following SCC edges and determine the SCC
|
||||
* representatives from those.
|
||||
* (This is still worst-case quadratic in the size of the SCCs, but generally
|
||||
* performs better.)
|
||||
*/
|
||||
private module SccReduction {
|
||||
private predicate sccEdge(TypeFlowNode n1, TypeFlowNode n2) {
|
||||
anyStep(n1, n2) and anyStep+(n2, n1)
|
||||
}
|
||||
private module Scc = QlBuiltins::EquivalenceRelation<TypeFlowNode, sccEdge/2>;
|
||||
|
||||
private predicate sccEdgeWithMax(TypeFlowNode n1, TypeFlowNode n2, TypeFlowNode m) {
|
||||
sccEdge(n1, n2) and
|
||||
m =
|
||||
max(TypeFlowNode n |
|
||||
n = [n1, n2]
|
||||
|
|
||||
n order by n.getLocation().getStartLine(), n.getLocation().getStartColumn(), getNodeKind(n)
|
||||
)
|
||||
}
|
||||
private class TypeFlowScc = Scc::EquivalenceClass;
|
||||
|
||||
private predicate hasLargerNeighbor(TypeFlowNode n) {
|
||||
exists(TypeFlowNode n2 |
|
||||
sccEdgeWithMax(n, n2, n2) and
|
||||
not sccEdgeWithMax(n, n2, n)
|
||||
or
|
||||
sccEdgeWithMax(n2, n, n2) and
|
||||
not sccEdgeWithMax(n2, n, n)
|
||||
)
|
||||
}
|
||||
/** Holds if `n` is part of an SCC of size 2 or more represented by `scc`. */
|
||||
private predicate sccRepr(TypeFlowNode n, TypeFlowScc scc) { scc = Scc::getEquivalenceClass(n) }
|
||||
|
||||
private predicate localMax(TypeFlowNode m) {
|
||||
sccEdgeWithMax(_, _, m) and
|
||||
not hasLargerNeighbor(m)
|
||||
}
|
||||
|
||||
private predicate sccReprFromLocalMax(TypeFlowNode scc) {
|
||||
exists(TypeFlowNode m |
|
||||
localMax(m) and
|
||||
scc =
|
||||
max(TypeFlowNode n2 |
|
||||
sccEdge+(m, n2) and localMax(n2)
|
||||
|
|
||||
n2
|
||||
order by
|
||||
n2.getLocation().getStartLine(), n2.getLocation().getStartColumn(), getNodeKind(n2)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
/** Holds if `n` is part of an SCC of size 2 or more represented by `scc`. */
|
||||
predicate sccRepr(TypeFlowNode n, TypeFlowNode scc) {
|
||||
sccEdge+(n, scc) and sccReprFromLocalMax(scc)
|
||||
}
|
||||
|
||||
predicate sccJoinStep(TypeFlowNode n, TypeFlowNode scc) {
|
||||
exists(TypeFlowNode mid |
|
||||
joinStep(n, mid) and
|
||||
sccRepr(mid, scc) and
|
||||
not sccRepr(n, scc)
|
||||
)
|
||||
}
|
||||
private predicate sccJoinStep(TypeFlowNode n, TypeFlowScc scc) {
|
||||
exists(TypeFlowNode mid |
|
||||
joinStep(n, mid) and
|
||||
sccRepr(mid, scc) and
|
||||
not sccRepr(n, scc)
|
||||
)
|
||||
}
|
||||
|
||||
private signature predicate edgeSig(TypeFlowNode n1, TypeFlowNode n2);
|
||||
private signature class NodeSig;
|
||||
|
||||
private signature module RankedEdge {
|
||||
predicate edgeRank(int r, TypeFlowNode n1, TypeFlowNode n2);
|
||||
private signature module Edge {
|
||||
class Node;
|
||||
|
||||
int lastRank(TypeFlowNode n);
|
||||
predicate edge(TypeFlowNode n1, Node n2);
|
||||
}
|
||||
|
||||
private module RankEdge<edgeSig/2 edge> implements RankedEdge {
|
||||
private signature module RankedEdge<NodeSig Node> {
|
||||
predicate edgeRank(int r, TypeFlowNode n1, Node n2);
|
||||
|
||||
int lastRank(Node n);
|
||||
}
|
||||
|
||||
private module RankEdge<Edge E> implements RankedEdge<E::Node> {
|
||||
private import E
|
||||
|
||||
/**
|
||||
* Holds if `r` is a ranking of the incoming edges `(n1,n2)` to `n2`. The used
|
||||
* ordering is not necessarily total, so the ranking may have gaps.
|
||||
*/
|
||||
private predicate edgeRank1(int r, TypeFlowNode n1, TypeFlowNode n2) {
|
||||
private predicate edgeRank1(int r, TypeFlowNode n1, Node n2) {
|
||||
n1 =
|
||||
rank[r](TypeFlowNode n |
|
||||
edge(n, n2)
|
||||
@@ -271,19 +199,19 @@ private module RankEdge<edgeSig/2 edge> implements RankedEdge {
|
||||
* Holds if `r2` is a ranking of the ranks from `edgeRank1`. This removes the
|
||||
* gaps from the ranking.
|
||||
*/
|
||||
private predicate edgeRank2(int r2, int r1, TypeFlowNode n) {
|
||||
private predicate edgeRank2(int r2, int r1, Node n) {
|
||||
r1 = rank[r2](int r | edgeRank1(r, _, n) | r)
|
||||
}
|
||||
|
||||
/** Holds if `r` is a ranking of the incoming edges `(n1,n2)` to `n2`. */
|
||||
predicate edgeRank(int r, TypeFlowNode n1, TypeFlowNode n2) {
|
||||
predicate edgeRank(int r, TypeFlowNode n1, Node n2) {
|
||||
exists(int r1 |
|
||||
edgeRank1(r1, n1, n2) and
|
||||
edgeRank2(r, r1, n2)
|
||||
)
|
||||
}
|
||||
|
||||
int lastRank(TypeFlowNode n) { result = max(int r | edgeRank(r, _, n)) }
|
||||
int lastRank(Node n) { result = max(int r | edgeRank(r, _, n)) }
|
||||
}
|
||||
|
||||
private signature module TypePropagation {
|
||||
@@ -296,16 +224,16 @@ private signature module TypePropagation {
|
||||
}
|
||||
|
||||
/** Implements recursion through `forall` by way of edge ranking. */
|
||||
private module ForAll<RankedEdge Edge, TypePropagation T> {
|
||||
private module ForAll<NodeSig Node, RankedEdge<Node> E, TypePropagation T> {
|
||||
/**
|
||||
* Holds if `t` is a bound that holds on one of the incoming edges to `n` and
|
||||
* thus is a candidate bound for `n`.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
private predicate candJoinType(TypeFlowNode n, T::Typ t) {
|
||||
private predicate candJoinType(Node n, T::Typ t) {
|
||||
exists(TypeFlowNode mid |
|
||||
T::candType(mid, t) and
|
||||
Edge::edgeRank(_, mid, n)
|
||||
E::edgeRank(_, mid, n)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -314,13 +242,13 @@ private module ForAll<RankedEdge Edge, TypePropagation T> {
|
||||
* through the edges into `n` ranked from `1` to `r`.
|
||||
*/
|
||||
pragma[assume_small_delta]
|
||||
private predicate flowJoin(int r, TypeFlowNode n, T::Typ t) {
|
||||
private predicate flowJoin(int r, Node n, T::Typ t) {
|
||||
(
|
||||
r = 1 and candJoinType(n, t)
|
||||
or
|
||||
flowJoin(r - 1, n, t) and Edge::edgeRank(r, _, n)
|
||||
flowJoin(r - 1, n, t) and E::edgeRank(r, _, n)
|
||||
) and
|
||||
forall(TypeFlowNode mid | Edge::edgeRank(r, mid, n) | T::supportsType(mid, t))
|
||||
forall(TypeFlowNode mid | E::edgeRank(r, mid, n) | T::supportsType(mid, t))
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -328,12 +256,24 @@ private module ForAll<RankedEdge Edge, TypePropagation T> {
|
||||
* coming through all the incoming edges, and therefore is a valid bound for
|
||||
* `n`.
|
||||
*/
|
||||
predicate flowJoin(TypeFlowNode n, T::Typ t) { flowJoin(Edge::lastRank(n), n, t) }
|
||||
predicate flowJoin(Node n, T::Typ t) { flowJoin(E::lastRank(n), n, t) }
|
||||
}
|
||||
|
||||
module RankedJoinStep = RankEdge<joinStep/2>;
|
||||
private module JoinStep implements Edge {
|
||||
class Node = TypeFlowNode;
|
||||
|
||||
module RankedSccJoinStep = RankEdge<sccJoinStep/2>;
|
||||
predicate edge = joinStep/2;
|
||||
}
|
||||
|
||||
private module SccJoinStep implements Edge {
|
||||
class Node = TypeFlowScc;
|
||||
|
||||
predicate edge = sccJoinStep/2;
|
||||
}
|
||||
|
||||
private module RankedJoinStep = RankEdge<JoinStep>;
|
||||
|
||||
private module RankedSccJoinStep = RankEdge<SccJoinStep>;
|
||||
|
||||
private predicate exactTypeBase(TypeFlowNode n, RefType t) {
|
||||
exists(ClassInstanceExpr e |
|
||||
@@ -363,13 +303,13 @@ private predicate exactType(TypeFlowNode n, RefType t) {
|
||||
or
|
||||
// The following is an optimized version of
|
||||
// `forex(TypeFlowNode mid | joinStep(mid, n) | exactType(mid, t))`
|
||||
ForAll<RankedJoinStep, ExactTypePropagation>::flowJoin(n, t)
|
||||
ForAll<TypeFlowNode, RankedJoinStep, ExactTypePropagation>::flowJoin(n, t)
|
||||
or
|
||||
exists(TypeFlowNode scc |
|
||||
exists(TypeFlowScc scc |
|
||||
sccRepr(n, scc) and
|
||||
// Optimized version of
|
||||
// `forex(TypeFlowNode mid | sccJoinStep(mid, scc) | exactType(mid, t))`
|
||||
ForAll<RankedSccJoinStep, ExactTypePropagation>::flowJoin(scc, t)
|
||||
ForAll<TypeFlowScc, RankedSccJoinStep, ExactTypePropagation>::flowJoin(scc, t)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -563,11 +503,11 @@ private predicate typeFlow(TypeFlowNode n, RefType t) {
|
||||
or
|
||||
exists(TypeFlowNode mid | typeFlow(mid, t) and step(mid, n))
|
||||
or
|
||||
ForAll<RankedJoinStep, TypeFlowPropagation>::flowJoin(n, t)
|
||||
ForAll<TypeFlowNode, RankedJoinStep, TypeFlowPropagation>::flowJoin(n, t)
|
||||
or
|
||||
exists(TypeFlowNode scc |
|
||||
exists(TypeFlowScc scc |
|
||||
sccRepr(n, scc) and
|
||||
ForAll<RankedSccJoinStep, TypeFlowPropagation>::flowJoin(scc, t)
|
||||
ForAll<TypeFlowScc, RankedSccJoinStep, TypeFlowPropagation>::flowJoin(scc, t)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -703,13 +643,13 @@ private predicate hasUnionTypeFlow(TypeFlowNode n) {
|
||||
(
|
||||
// Optimized version of
|
||||
// `forex(TypeFlowNode mid | joinStep(mid, n) | unionTypeFlowBaseCand(mid, _, _) or hasUnionTypeFlow(mid))`
|
||||
ForAll<RankedJoinStep, HasUnionTypePropagation>::flowJoin(n, _)
|
||||
ForAll<TypeFlowNode, RankedJoinStep, HasUnionTypePropagation>::flowJoin(n, _)
|
||||
or
|
||||
exists(TypeFlowNode scc |
|
||||
exists(TypeFlowScc scc |
|
||||
sccRepr(n, scc) and
|
||||
// Optimized version of
|
||||
// `forex(TypeFlowNode mid | sccJoinStep(mid, scc) | unionTypeFlowBaseCand(mid, _, _) or hasUnionTypeFlow(mid))`
|
||||
ForAll<RankedSccJoinStep, HasUnionTypePropagation>::flowJoin(scc, _)
|
||||
ForAll<TypeFlowScc, RankedSccJoinStep, HasUnionTypePropagation>::flowJoin(scc, _)
|
||||
)
|
||||
or
|
||||
exists(TypeFlowNode mid | step(mid, n) and hasUnionTypeFlow(mid))
|
||||
|
||||
@@ -3,7 +3,6 @@ import semmle.code.java.Collections
|
||||
import semmle.code.java.Maps
|
||||
private import semmle.code.java.dataflow.SSA
|
||||
private import DataFlowUtil
|
||||
private import semmle.code.java.dataflow.ExternalFlow
|
||||
|
||||
private class EntryType extends RefType {
|
||||
EntryType() {
|
||||
|
||||
@@ -14,6 +14,13 @@ private import semmle.code.java.dataflow.internal.AccessPathSyntax as AccessPath
|
||||
|
||||
class SummarizedCallableBase = FlowSummary::SummarizedCallableBase;
|
||||
|
||||
/**
|
||||
* A module for importing frameworks that define synthetic globals.
|
||||
*/
|
||||
private module SyntheticGlobals {
|
||||
private import semmle.code.java.frameworks.android.Intent
|
||||
}
|
||||
|
||||
DataFlowCallable inject(SummarizedCallable c) { result.asSummarizedCallable() = c }
|
||||
|
||||
/** Gets the parameter position of the instance parameter. */
|
||||
|
||||
@@ -10,7 +10,6 @@ private import semmle.code.java.dataflow.internal.ContainerFlow
|
||||
private import semmle.code.java.frameworks.spring.SpringController
|
||||
private import semmle.code.java.frameworks.spring.SpringHttp
|
||||
private import semmle.code.java.frameworks.Networking
|
||||
private import semmle.code.java.dataflow.ExternalFlow
|
||||
private import semmle.code.java.dataflow.FlowSources
|
||||
private import semmle.code.java.dataflow.internal.DataFlowPrivate
|
||||
import semmle.code.java.dataflow.FlowSteps
|
||||
|
||||
@@ -4,7 +4,6 @@
|
||||
|
||||
import java
|
||||
private import semmle.code.java.dataflow.FlowSteps
|
||||
private import semmle.code.java.dataflow.ExternalFlow
|
||||
|
||||
class ApacheHttpGetParams extends Method {
|
||||
ApacheHttpGetParams() {
|
||||
|
||||
@@ -3,7 +3,6 @@
|
||||
*/
|
||||
|
||||
import java
|
||||
private import semmle.code.java.dataflow.ExternalFlow
|
||||
|
||||
/** The class `flexjson.JSONDeserializer`. */
|
||||
class FlexjsonDeserializer extends RefType {
|
||||
|
||||
@@ -3,7 +3,6 @@
|
||||
*/
|
||||
|
||||
import java
|
||||
private import semmle.code.java.dataflow.ExternalFlow
|
||||
|
||||
/** The interface `org.hibernate.query.QueryProducer`. */
|
||||
class HibernateQueryProducer extends RefType {
|
||||
|
||||
@@ -4,7 +4,6 @@
|
||||
*/
|
||||
|
||||
import java
|
||||
private import semmle.code.java.dataflow.ExternalFlow
|
||||
private import semmle.code.java.security.XSS
|
||||
|
||||
/**
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
* Provides classes and predicates for working with the Java JDBC API.
|
||||
*/
|
||||
|
||||
private import semmle.code.java.dataflow.ExternalFlow
|
||||
import java
|
||||
|
||||
/*--- Types ---*/
|
||||
/** The interface `java.sql.Connection`. */
|
||||
|
||||
@@ -3,7 +3,6 @@
|
||||
*/
|
||||
|
||||
import java
|
||||
private import semmle.code.java.dataflow.ExternalFlow
|
||||
|
||||
/** The class `jodd.json.Parser`. */
|
||||
class JoddJsonParser extends RefType {
|
||||
|
||||
@@ -5,7 +5,6 @@
|
||||
import java
|
||||
private import semmle.code.java.dataflow.DataFlow
|
||||
private import semmle.code.java.dataflow.TaintTracking
|
||||
private import semmle.code.java.dataflow.ExternalFlow
|
||||
|
||||
/** The class `org.apache.ibatis.jdbc.SqlRunner`. */
|
||||
class MyBatisSqlRunner extends RefType {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/** Definitions related to `java.util.regex`. */
|
||||
|
||||
private import semmle.code.java.dataflow.ExternalFlow
|
||||
import java
|
||||
|
||||
/** The class `java.util.regex.Pattern`. */
|
||||
class TypeRegexPattern extends Class {
|
||||
|
||||
@@ -3,7 +3,6 @@
|
||||
*/
|
||||
|
||||
import java
|
||||
private import semmle.code.java.dataflow.ExternalFlow
|
||||
|
||||
/** The class `org.springframework.jdbc.core.JdbcTemplate`. */
|
||||
class JdbcTemplate extends RefType {
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
/** Definitions related to `java.util.stream`. */
|
||||
|
||||
private import semmle.code.java.dataflow.ExternalFlow
|
||||
private import semmle.code.java.dataflow.FlowSummary
|
||||
|
||||
private class CollectCall extends MethodAccess {
|
||||
|
||||
@@ -3,7 +3,6 @@
|
||||
*/
|
||||
|
||||
import java
|
||||
private import semmle.code.java.dataflow.ExternalFlow
|
||||
private import semmle.code.xml.AndroidManifest
|
||||
|
||||
/**
|
||||
|
||||
@@ -3,7 +3,6 @@
|
||||
*/
|
||||
|
||||
import java
|
||||
private import semmle.code.java.dataflow.ExternalFlow
|
||||
|
||||
/** The class `android.content.ContentValues`. */
|
||||
class ContentValues extends Class {
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
/** Provides classes and predicates for working with SQLite databases. */
|
||||
|
||||
import java
|
||||
private import semmle.code.java.dataflow.ExternalFlow
|
||||
private import semmle.code.java.dataflow.FlowSteps
|
||||
private import semmle.code.java.frameworks.android.Android
|
||||
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
/** Provides classes related to `android.content.SharedPreferences`. */
|
||||
|
||||
import java
|
||||
private import semmle.code.java.dataflow.ExternalFlow
|
||||
|
||||
/** The interface `android.content.SharedPreferences`. */
|
||||
class SharedPreferences extends Interface {
|
||||
|
||||
@@ -3,7 +3,6 @@
|
||||
import java
|
||||
private import semmle.code.java.dataflow.DataFlow
|
||||
private import semmle.code.java.dataflow.FlowSteps
|
||||
private import semmle.code.java.dataflow.ExternalFlow
|
||||
|
||||
/** The class `androidx.slice.SliceProvider`. */
|
||||
class SliceProvider extends Class {
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
|
||||
import java
|
||||
private import semmle.code.java.dataflow.FlowSteps
|
||||
private import semmle.code.java.dataflow.ExternalFlow
|
||||
|
||||
/**
|
||||
* The method `isEmpty` in either `org.apache.commons.collections.CollectionUtils`
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
|
||||
import java
|
||||
private import semmle.code.java.dataflow.FlowSteps
|
||||
private import semmle.code.java.dataflow.ExternalFlow
|
||||
|
||||
/**
|
||||
* The class `org.apache.commons.lang.RandomStringUtils` or `org.apache.commons.lang3.RandomStringUtils`.
|
||||
|
||||
@@ -3,7 +3,6 @@
|
||||
import java
|
||||
private import semmle.code.java.dataflow.DataFlow
|
||||
private import semmle.code.java.dataflow.FlowSteps
|
||||
private import semmle.code.java.dataflow.ExternalFlow
|
||||
private import semmle.code.java.Collections
|
||||
|
||||
private string guavaCollectPackage() { result = "com.google.common.collect" }
|
||||
|
||||
@@ -3,7 +3,6 @@
|
||||
*/
|
||||
|
||||
import java
|
||||
private import semmle.code.java.dataflow.ExternalFlow
|
||||
|
||||
/**
|
||||
* Methods annotated with this allow for generation of "plain SQL"
|
||||
|
||||
@@ -9,7 +9,6 @@ import semmle.code.java.Reflection
|
||||
import semmle.code.java.dataflow.DataFlow
|
||||
private import semmle.code.java.dataflow.internal.DataFlowForSerializability
|
||||
import semmle.code.java.dataflow.FlowSteps
|
||||
private import semmle.code.java.dataflow.ExternalFlow
|
||||
|
||||
/**
|
||||
* A `@com.fasterxml.jackson.annotation.JsonIgnore` annoation.
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
/** Provides classes and predicates for working with JavaServer Faces renderer. */
|
||||
|
||||
import java
|
||||
private import semmle.code.java.dataflow.ExternalFlow
|
||||
|
||||
/**
|
||||
* The JSF class `FacesContext` for processing HTTP requests.
|
||||
|
||||
8
java/ql/lib/semmle/code/java/frameworks/kotlin/IO.qll
Normal file
8
java/ql/lib/semmle/code/java/frameworks/kotlin/IO.qll
Normal file
@@ -0,0 +1,8 @@
|
||||
/** Provides classes and predicates related to `kotlin.io`. */
|
||||
|
||||
import java
|
||||
|
||||
/** The type `kotlin.io.FilesKt`, where `File` extension methods are declared. */
|
||||
class FilesKt extends RefType {
|
||||
FilesKt() { this.hasQualifiedName("kotlin.io", "FilesKt") }
|
||||
}
|
||||
21
java/ql/lib/semmle/code/java/frameworks/kotlin/Text.qll
Normal file
21
java/ql/lib/semmle/code/java/frameworks/kotlin/Text.qll
Normal file
@@ -0,0 +1,21 @@
|
||||
/** Provides classes and predicates related to `kotlin.text`. */
|
||||
|
||||
import java
|
||||
|
||||
/** The type `kotlin.text.StringsKt`, where `String` extension methods are declared. */
|
||||
class StringsKt extends RefType {
|
||||
StringsKt() { this.hasQualifiedName("kotlin.text", "StringsKt") }
|
||||
}
|
||||
|
||||
/** A call to the extension method `String.toRegex` from `kotlin.text`. */
|
||||
class KtToRegex extends MethodAccess {
|
||||
KtToRegex() {
|
||||
this.getMethod().getDeclaringType() instanceof StringsKt and
|
||||
this.getMethod().hasName("toRegex")
|
||||
}
|
||||
|
||||
/** Gets the constant string value being converted to a regex by this call. */
|
||||
string getExpressionString() {
|
||||
result = this.getArgument(0).(CompileTimeConstantExpr).getStringValue()
|
||||
}
|
||||
}
|
||||
@@ -5,7 +5,6 @@
|
||||
import java
|
||||
private import semmle.code.java.dataflow.DataFlow
|
||||
private import semmle.code.java.dataflow.FlowSteps
|
||||
private import semmle.code.java.dataflow.ExternalFlow
|
||||
|
||||
/** A reference type that extends a parameterization the Promise type. */
|
||||
private class RatpackPromise extends RefType {
|
||||
|
||||
@@ -4,7 +4,6 @@
|
||||
*/
|
||||
|
||||
import java
|
||||
private import semmle.code.java.dataflow.ExternalFlow
|
||||
private import semmle.code.java.dataflow.DataFlow
|
||||
private import semmle.code.java.frameworks.spring.SpringController
|
||||
private import semmle.code.java.security.XSS as XSS
|
||||
|
||||
@@ -4,7 +4,6 @@
|
||||
|
||||
import java
|
||||
import SpringHttp
|
||||
private import semmle.code.java.dataflow.ExternalFlow
|
||||
|
||||
/** The class `org.springframework.web.client.RestTemplate`. */
|
||||
class SpringRestTemplate extends Class {
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
private import semmle.code.java.security.Encryption
|
||||
private import semmle.code.java.dataflow.DataFlow
|
||||
private import semmle.code.java.security.internal.EncryptionKeySizes
|
||||
|
||||
/** A source for an insufficient key size. */
|
||||
abstract class InsufficientKeySizeSource extends DataFlow::Node {
|
||||
@@ -21,39 +22,67 @@ private module Asymmetric {
|
||||
private module NonEllipticCurve {
|
||||
/** A source for an insufficient key size used in RSA, DSA, and DH algorithms. */
|
||||
private class Source extends InsufficientKeySizeSource {
|
||||
Source() { this.asExpr().(IntegerLiteral).getIntValue() < getMinKeySize() }
|
||||
string algoName;
|
||||
|
||||
override predicate hasState(DataFlow::FlowState state) { state = getMinKeySize().toString() }
|
||||
Source() { this.asExpr().(IntegerLiteral).getIntValue() < getMinKeySize(algoName) }
|
||||
|
||||
override predicate hasState(DataFlow::FlowState state) {
|
||||
state = getMinKeySize(algoName).toString()
|
||||
}
|
||||
}
|
||||
|
||||
/** A sink for an insufficient key size used in RSA, DSA, and DH algorithms. */
|
||||
private class Sink extends InsufficientKeySizeSink {
|
||||
string algoName;
|
||||
|
||||
Sink() {
|
||||
exists(KeyPairGenInit kpgInit, KeyPairGen kpg |
|
||||
kpg.getAlgoName().matches(["RSA", "DSA", "DH"]) and
|
||||
algoName in ["RSA", "DSA", "DH"] and
|
||||
kpg.getAlgoName() = algoName and
|
||||
DataFlow::localExprFlow(kpg, kpgInit.getQualifier()) and
|
||||
this.asExpr() = kpgInit.getKeySizeArg()
|
||||
)
|
||||
or
|
||||
exists(Spec spec | this.asExpr() = spec.getKeySizeArg())
|
||||
exists(Spec spec | this.asExpr() = spec.getKeySizeArg() and algoName = spec.getAlgoName())
|
||||
}
|
||||
|
||||
override predicate hasState(DataFlow::FlowState state) { state = getMinKeySize().toString() }
|
||||
override predicate hasState(DataFlow::FlowState state) {
|
||||
state = getMinKeySize(algoName).toString()
|
||||
}
|
||||
}
|
||||
|
||||
/** Returns the minimum recommended key size for RSA, DSA, and DH algorithms. */
|
||||
private int getMinKeySize() { result = 2048 }
|
||||
private int getMinKeySize(string algoName) {
|
||||
algoName = "RSA" and
|
||||
result = minSecureKeySizeRsa()
|
||||
or
|
||||
algoName = "DSA" and
|
||||
result = minSecureKeySizeDsa()
|
||||
or
|
||||
algoName = "DH" and
|
||||
result = minSecureKeySizeDh()
|
||||
}
|
||||
|
||||
/** An instance of an RSA, DSA, or DH algorithm specification. */
|
||||
private class Spec extends ClassInstanceExpr {
|
||||
string algoName;
|
||||
|
||||
Spec() {
|
||||
this.getConstructedType() instanceof RsaKeyGenParameterSpec or
|
||||
this.getConstructedType() instanceof DsaGenParameterSpec or
|
||||
this.getConstructedType() instanceof DhGenParameterSpec
|
||||
this.getConstructedType() instanceof RsaKeyGenParameterSpec and
|
||||
algoName = "RSA"
|
||||
or
|
||||
this.getConstructedType() instanceof DsaGenParameterSpec and
|
||||
algoName = "DSA"
|
||||
or
|
||||
this.getConstructedType() instanceof DhGenParameterSpec and
|
||||
algoName = "DH"
|
||||
}
|
||||
|
||||
/** Gets the `keysize` argument of this instance. */
|
||||
Argument getKeySizeArg() { result = this.getArgument(0) }
|
||||
|
||||
/** Gets the algorithm name of this spec. */
|
||||
string getAlgoName() { result = algoName }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -87,7 +116,7 @@ private module Asymmetric {
|
||||
}
|
||||
|
||||
/** Returns the minimum recommended key size for elliptic curve (EC) algorithms. */
|
||||
private int getMinKeySize() { result = 256 }
|
||||
private int getMinKeySize() { result = minSecureKeySizeEcc() }
|
||||
|
||||
/** Returns the key size from an EC algorithm's curve name string */
|
||||
bindingset[algorithm]
|
||||
@@ -168,7 +197,7 @@ private module Symmetric {
|
||||
}
|
||||
|
||||
/** Returns the minimum recommended key size for AES algorithms. */
|
||||
private int getMinKeySize() { result = 128 }
|
||||
private int getMinKeySize() { result = minSecureKeySizeAes() }
|
||||
|
||||
/** A call to the `init` method declared in `javax.crypto.KeyGenerator`. */
|
||||
private class KeyGenInit extends MethodAccess {
|
||||
|
||||
@@ -3,7 +3,6 @@
|
||||
import java
|
||||
import semmle.code.java.security.PartialPathTraversal
|
||||
import semmle.code.java.dataflow.DataFlow
|
||||
import semmle.code.java.dataflow.ExternalFlow
|
||||
import semmle.code.java.dataflow.TaintTracking
|
||||
import semmle.code.java.dataflow.FlowSources
|
||||
|
||||
|
||||
@@ -2,9 +2,10 @@
|
||||
|
||||
import java
|
||||
private import semmle.code.java.controlflow.Guards
|
||||
private import semmle.code.java.dataflow.ExternalFlow
|
||||
private import semmle.code.java.dataflow.FlowSources
|
||||
private import semmle.code.java.dataflow.SSA
|
||||
private import semmle.code.java.frameworks.kotlin.IO
|
||||
private import semmle.code.java.frameworks.kotlin.Text
|
||||
|
||||
/** A sanitizer that protects against path injection vulnerabilities. */
|
||||
abstract class PathInjectionSanitizer extends DataFlow::Node { }
|
||||
@@ -51,12 +52,14 @@ private predicate exactPathMatchGuard(Guard g, Expr e, boolean branch) {
|
||||
t instanceof TypeUri or
|
||||
t instanceof TypePath or
|
||||
t instanceof TypeFile or
|
||||
t.hasQualifiedName("android.net", "Uri")
|
||||
t.hasQualifiedName("android.net", "Uri") or
|
||||
t instanceof StringsKt or
|
||||
t instanceof FilesKt
|
||||
|
|
||||
e = getVisualQualifier(ma).getUnderlyingExpr() and
|
||||
ma.getMethod().getDeclaringType() = t and
|
||||
ma = g and
|
||||
ma.getMethod().getName() = ["equals", "equalsIgnoreCase"] and
|
||||
e = ma.getQualifier() and
|
||||
getSourceMethod(ma.getMethod()).hasName(["equals", "equalsIgnoreCase"]) and
|
||||
branch = true
|
||||
)
|
||||
}
|
||||
@@ -87,7 +90,7 @@ private class AllowedPrefixGuard extends PathGuard instanceof MethodAccess {
|
||||
not isDisallowedWord(super.getAnArgument())
|
||||
}
|
||||
|
||||
override Expr getCheckedExpr() { result = super.getQualifier() }
|
||||
override Expr getCheckedExpr() { result = getVisualQualifier(this).getUnderlyingExpr() }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -154,7 +157,7 @@ private class BlockListGuard extends PathGuard instanceof MethodAccess {
|
||||
isDisallowedWord(super.getAnArgument())
|
||||
}
|
||||
|
||||
override Expr getCheckedExpr() { result = super.getQualifier() }
|
||||
override Expr getCheckedExpr() { result = getVisualQualifier(this).getUnderlyingExpr() }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -188,15 +191,31 @@ private class BlockListSanitizer extends PathInjectionSanitizer {
|
||||
}
|
||||
}
|
||||
|
||||
private class ConstantOrRegex extends Expr {
|
||||
ConstantOrRegex() {
|
||||
this instanceof CompileTimeConstantExpr or
|
||||
this instanceof KtToRegex
|
||||
}
|
||||
|
||||
string getStringValue() {
|
||||
result = this.(CompileTimeConstantExpr).getStringValue() or
|
||||
result = this.(KtToRegex).getExpressionString()
|
||||
}
|
||||
}
|
||||
|
||||
private predicate isStringPrefixMatch(MethodAccess ma) {
|
||||
exists(Method m | m = ma.getMethod() and m.getDeclaringType() instanceof TypeString |
|
||||
m.hasName("startsWith")
|
||||
exists(Method m, RefType t |
|
||||
m.getDeclaringType() = t and
|
||||
(t instanceof TypeString or t instanceof StringsKt) and
|
||||
m = ma.getMethod()
|
||||
|
|
||||
getSourceMethod(m).hasName("startsWith")
|
||||
or
|
||||
m.hasName("regionMatches") and
|
||||
ma.getArgument(0).(CompileTimeConstantExpr).getIntValue() = 0
|
||||
getSourceMethod(m).hasName("regionMatches") and
|
||||
getVisualArgument(ma, 0).(CompileTimeConstantExpr).getIntValue() = 0
|
||||
or
|
||||
m.hasName("matches") and
|
||||
not ma.getArgument(0).(CompileTimeConstantExpr).getStringValue().matches(".*%")
|
||||
not getVisualArgument(ma, 0).(ConstantOrRegex).getStringValue().matches(".*%")
|
||||
)
|
||||
}
|
||||
|
||||
@@ -206,52 +225,52 @@ private predicate isStringPrefixMatch(MethodAccess ma) {
|
||||
private predicate isStringPartialMatch(MethodAccess ma) {
|
||||
isStringPrefixMatch(ma)
|
||||
or
|
||||
ma.getMethod().getDeclaringType() instanceof TypeString and
|
||||
ma.getMethod().hasName(["contains", "matches", "regionMatches", "indexOf", "lastIndexOf"])
|
||||
exists(RefType t | t = ma.getMethod().getDeclaringType() |
|
||||
t instanceof TypeString or t instanceof StringsKt
|
||||
) and
|
||||
getSourceMethod(ma.getMethod())
|
||||
.hasName(["contains", "matches", "regionMatches", "indexOf", "lastIndexOf"])
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `ma` is a call to a method that checks whether a path starts with a prefix.
|
||||
*/
|
||||
private predicate isPathPrefixMatch(MethodAccess ma) {
|
||||
exists(RefType t |
|
||||
t instanceof TypePath
|
||||
or
|
||||
t.hasQualifiedName("kotlin.io", "FilesKt")
|
||||
|
|
||||
t = ma.getMethod().getDeclaringType() and
|
||||
ma.getMethod().hasName("startsWith")
|
||||
)
|
||||
exists(RefType t | t = ma.getMethod().getDeclaringType() |
|
||||
t instanceof TypePath or t instanceof FilesKt
|
||||
) and
|
||||
getSourceMethod(ma.getMethod()).hasName("startsWith")
|
||||
}
|
||||
|
||||
private predicate isDisallowedWord(CompileTimeConstantExpr word) {
|
||||
private predicate isDisallowedWord(ConstantOrRegex word) {
|
||||
word.getStringValue().matches(["/", "\\", "%WEB-INF%", "%/data%"])
|
||||
}
|
||||
|
||||
/** A complementary guard that protects against path traversal, by looking for the literal `..`. */
|
||||
private class PathTraversalGuard extends PathGuard {
|
||||
Expr checkedExpr;
|
||||
|
||||
PathTraversalGuard() {
|
||||
exists(MethodAccess ma |
|
||||
ma.getMethod().getDeclaringType() instanceof TypeString and
|
||||
exists(MethodAccess ma, Method m, RefType t |
|
||||
m = ma.getMethod() and
|
||||
t = m.getDeclaringType() and
|
||||
(t instanceof TypeString or t instanceof StringsKt) and
|
||||
checkedExpr = getVisualQualifier(ma).getUnderlyingExpr() and
|
||||
ma.getAnArgument().(CompileTimeConstantExpr).getStringValue() = ".."
|
||||
|
|
||||
this = ma and
|
||||
ma.getMethod().hasName("contains")
|
||||
getSourceMethod(m).hasName("contains")
|
||||
or
|
||||
exists(EqualityTest eq |
|
||||
this = eq and
|
||||
ma.getMethod().hasName(["indexOf", "lastIndexOf"]) and
|
||||
getSourceMethod(m).hasName(["indexOf", "lastIndexOf"]) and
|
||||
eq.getAnOperand() = ma and
|
||||
eq.getAnOperand().(CompileTimeConstantExpr).getIntValue() = -1
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
override Expr getCheckedExpr() {
|
||||
exists(MethodAccess ma | ma = this.(EqualityTest).getAnOperand() or ma = this |
|
||||
result = ma.getQualifier()
|
||||
)
|
||||
}
|
||||
override Expr getCheckedExpr() { result = checkedExpr }
|
||||
|
||||
boolean getBranch() {
|
||||
this instanceof MethodAccess and result = false
|
||||
@@ -263,15 +282,46 @@ private class PathTraversalGuard extends PathGuard {
|
||||
/** A complementary sanitizer that protects against path traversal using path normalization. */
|
||||
private class PathNormalizeSanitizer extends MethodAccess {
|
||||
PathNormalizeSanitizer() {
|
||||
exists(RefType t |
|
||||
t instanceof TypePath or
|
||||
t.hasQualifiedName("kotlin.io", "FilesKt")
|
||||
|
|
||||
this.getMethod().getDeclaringType() = t and
|
||||
exists(RefType t | this.getMethod().getDeclaringType() = t |
|
||||
(t instanceof TypePath or t instanceof FilesKt) and
|
||||
this.getMethod().hasName("normalize")
|
||||
or
|
||||
t instanceof TypeFile and
|
||||
this.getMethod().hasName(["getCanonicalPath", "getCanonicalFile"])
|
||||
)
|
||||
or
|
||||
this.getMethod().getDeclaringType() instanceof TypeFile and
|
||||
this.getMethod().hasName(["getCanonicalPath", "getCanonicalFile"])
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the qualifier of `ma` as seen in the source code.
|
||||
* This is a helper predicate to solve discrepancies between
|
||||
* what `getQualifier` actually gets in Java and Kotlin.
|
||||
*/
|
||||
private Expr getVisualQualifier(MethodAccess ma) {
|
||||
if getSourceMethod(ma.getMethod()) instanceof ExtensionMethod
|
||||
then result = ma.getArgument(0)
|
||||
else result = ma.getQualifier()
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the argument of `ma` at position `argPos` as seen in the source code.
|
||||
* This is a helper predicate to solve discrepancies between
|
||||
* what `getArgument` actually gets in Java and Kotlin.
|
||||
*/
|
||||
bindingset[argPos]
|
||||
private Argument getVisualArgument(MethodAccess ma, int argPos) {
|
||||
if getSourceMethod(ma.getMethod()) instanceof ExtensionMethod
|
||||
then result = ma.getArgument(argPos + 1)
|
||||
else result = ma.getArgument(argPos)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the proxied method if `m` is a Kotlin proxy that supplies default parameter values.
|
||||
* Otherwise, just gets `m`.
|
||||
*/
|
||||
private Method getSourceMethod(Method m) {
|
||||
m = result.getKotlinParameterDefaultsProxy()
|
||||
or
|
||||
not exists(Method src | m = src.getKotlinParameterDefaultsProxy()) and
|
||||
result = m
|
||||
}
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
|
||||
import java
|
||||
private import semmle.code.java.dataflow.DataFlow
|
||||
private import semmle.code.java.dataflow.ExternalFlow
|
||||
private import semmle.code.java.frameworks.spring.SpringExpression
|
||||
|
||||
/** A data flow sink for unvalidated user input that is used to construct SpEL expressions. */
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
/** Provides taint tracking configurations to be used in unsafe content URI resolution queries. */
|
||||
|
||||
import java
|
||||
import semmle.code.java.dataflow.ExternalFlow
|
||||
import semmle.code.java.dataflow.FlowSources
|
||||
import semmle.code.java.dataflow.TaintTracking
|
||||
import semmle.code.java.security.UnsafeContentUriResolution
|
||||
|
||||
@@ -0,0 +1,21 @@
|
||||
/**
|
||||
* INTERNAL: Do not use.
|
||||
*
|
||||
* Provides predicates for recommended encryption key sizes.
|
||||
* Such that we can share this logic across our CodeQL analysis of different languages.
|
||||
*/
|
||||
|
||||
/** Returns the minimum recommended key size for RSA. */
|
||||
int minSecureKeySizeRsa() { result = 2048 }
|
||||
|
||||
/** Returns the minimum recommended key size for DSA. */
|
||||
int minSecureKeySizeDsa() { result = 2048 }
|
||||
|
||||
/** Returns the minimum recommended key size for DH. */
|
||||
int minSecureKeySizeDh() { result = 2048 }
|
||||
|
||||
/** Returns the minimum recommended key size for elliptic curve cryptography. */
|
||||
int minSecureKeySizeEcc() { result = 256 }
|
||||
|
||||
/** Returns the minimum recommended key size for AES. */
|
||||
int minSecureKeySizeAes() { result = 128 }
|
||||
@@ -26,18 +26,31 @@ predicate isSafeSecureCookieSetting(Expr e) {
|
||||
)
|
||||
}
|
||||
|
||||
class SecureCookieConfiguration extends DataFlow::Configuration {
|
||||
SecureCookieConfiguration() { this = "SecureCookieConfiguration" }
|
||||
|
||||
override 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())
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
override predicate isSink(DataFlow::Node sink) {
|
||||
sink.asExpr() =
|
||||
any(MethodAccess add | add.getMethod() instanceof ResponseAddCookieMethod).getArgument(0)
|
||||
}
|
||||
}
|
||||
|
||||
from MethodAccess add
|
||||
where
|
||||
add.getMethod() instanceof ResponseAddCookieMethod and
|
||||
not exists(Variable cookie, MethodAccess m |
|
||||
add.getArgument(0) = cookie.getAnAccess() and
|
||||
m.getMethod().getName() = "setSecure" and
|
||||
forex(DataFlow::Node argSource |
|
||||
DataFlow::localFlow(argSource, DataFlow::exprNode(m.getArgument(0))) and
|
||||
not DataFlow::localFlowStep(_, argSource)
|
||||
|
|
||||
isSafeSecureCookieSetting(argSource.asExpr())
|
||||
) and
|
||||
m.getQualifier() = cookie.getAnAccess()
|
||||
)
|
||||
not any(SecureCookieConfiguration df).hasFlowToExpr(add.getArgument(0))
|
||||
select add, "Cookie is added to response without the 'secure' flag being set."
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import java
|
||||
private import semmle.code.java.dataflow.ExternalFlow
|
||||
private import semmle.code.java.dataflow.FlowSources
|
||||
|
||||
/** The class `com.jfinal.core.Controller`. */
|
||||
|
||||
@@ -3,7 +3,6 @@
|
||||
*/
|
||||
|
||||
import java
|
||||
private import semmle.code.java.dataflow.ExternalFlow
|
||||
private import semmle.code.java.dataflow.FlowSources
|
||||
|
||||
/** A utility class for resolving resource locations to files in the file system in the Spring framework. */
|
||||
|
||||
@@ -4,7 +4,6 @@
|
||||
|
||||
import java
|
||||
private import semmle.code.java.dataflow.internal.DataFlowUtil
|
||||
private import semmle.code.java.dataflow.ExternalFlow
|
||||
private import semmle.code.java.dataflow.FlowSummary
|
||||
private import semmle.code.java.dataflow.internal.FlowSummaryImpl
|
||||
private import FlowTestCaseUtils
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import java
|
||||
import semmle.code.java.dataflow.TaintTracking
|
||||
import semmle.code.java.dataflow.ExternalFlow
|
||||
|
||||
class Conf extends TaintTracking::Configuration {
|
||||
Conf() { this = "qltest:extension-method" }
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import java
|
||||
import semmle.code.java.dataflow.TaintTracking
|
||||
import semmle.code.java.dataflow.ExternalFlow
|
||||
|
||||
class Conf extends TaintTracking::Configuration {
|
||||
Conf() { this = "qltest:foreach-array-iterator" }
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import java
|
||||
import semmle.code.java.dataflow.TaintTracking
|
||||
import semmle.code.java.dataflow.ExternalFlow
|
||||
|
||||
class Conf extends TaintTracking::Configuration {
|
||||
Conf() { this = "qltest:lambdaFlow" }
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import java
|
||||
import semmle.code.java.dataflow.TaintTracking
|
||||
import semmle.code.java.dataflow.ExternalFlow
|
||||
|
||||
class Conf extends TaintTracking::Configuration {
|
||||
Conf() { this = "qltest:notNullExprFlow" }
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import java
|
||||
import semmle.code.java.dataflow.TaintTracking
|
||||
import semmle.code.java.dataflow.ExternalFlow
|
||||
|
||||
class Conf extends TaintTracking::Configuration {
|
||||
Conf() { this = "qltest:notNullExprFlow" }
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import java
|
||||
import semmle.code.java.dataflow.DataFlow
|
||||
import semmle.code.java.dataflow.ExternalFlow
|
||||
import TestUtilities.InlineExpectationsTest
|
||||
|
||||
class Conf extends DataFlow::Configuration {
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import java
|
||||
import semmle.code.java.dataflow.DataFlow
|
||||
import semmle.code.java.dataflow.ExternalFlow
|
||||
import TestUtilities.InlineFlowTest
|
||||
|
||||
class HasFlowTest extends InlineFlowTest {
|
||||
|
||||
@@ -279,7 +279,7 @@ public class Test {
|
||||
}
|
||||
|
||||
private void blockListGuardValidation(String path) throws Exception {
|
||||
if (path.contains("..") || !path.startsWith("/data"))
|
||||
if (path.contains("..") || path.startsWith("/data"))
|
||||
throw new Exception();
|
||||
}
|
||||
|
||||
|
||||
499
java/ql/test/library-tests/pathsanitizer/TestKt.kt
Normal file
499
java/ql/test/library-tests/pathsanitizer/TestKt.kt
Normal file
@@ -0,0 +1,499 @@
|
||||
import java.io.File
|
||||
import java.net.URI
|
||||
import java.nio.file.Path
|
||||
import java.nio.file.Paths
|
||||
import android.net.Uri
|
||||
|
||||
class TestKt {
|
||||
fun source(): Any? {
|
||||
return null
|
||||
}
|
||||
|
||||
fun sink(o: Any?) {}
|
||||
|
||||
@Throws(Exception::class)
|
||||
private fun exactPathMatchGuardValidation(path: String?) {
|
||||
if (!path.equals("/safe/path")) throw Exception()
|
||||
}
|
||||
|
||||
@Throws(Exception::class)
|
||||
fun exactPathMatchGuard() {
|
||||
run {
|
||||
val source = source() as String?
|
||||
if (source!!.equals("/safe/path"))
|
||||
sink(source) // Safe
|
||||
else
|
||||
sink(source) // $ hasTaintFlow
|
||||
}
|
||||
run {
|
||||
val source = source() as URI?
|
||||
if (source!!.equals(URI("http://safe/uri")))
|
||||
sink(source) // Safe
|
||||
else
|
||||
sink(source) // $ hasTaintFlow
|
||||
}
|
||||
run {
|
||||
val source = source() as File?
|
||||
if (source!!.equals(File("/safe/file")))
|
||||
sink(source) // Safe
|
||||
else
|
||||
sink(source) // $ hasTaintFlow
|
||||
}
|
||||
run {
|
||||
val source = source() as Uri?
|
||||
if (source!!.equals(Uri.parse("http://safe/uri")))
|
||||
sink(source) // Safe
|
||||
else
|
||||
sink(source) // $ hasTaintFlow
|
||||
}
|
||||
run {
|
||||
val source = source() as String?
|
||||
exactPathMatchGuardValidation(source)
|
||||
sink(source) // Safe
|
||||
}
|
||||
}
|
||||
|
||||
@Throws(Exception::class)
|
||||
private fun allowListGuardValidation(path: String?) {
|
||||
if (path!!.contains("..") || !path.startsWith("/safe")) throw Exception()
|
||||
}
|
||||
|
||||
@Throws(Exception::class)
|
||||
fun allowListGuard() {
|
||||
// Prefix check by itself is not enough
|
||||
run {
|
||||
val source = source() as String?
|
||||
if (source!!.startsWith("/safe")) {
|
||||
sink(source) // $ hasTaintFlow
|
||||
} else
|
||||
sink(source) // $ hasTaintFlow
|
||||
}
|
||||
// PathTraversalGuard + allowListGuard
|
||||
run {
|
||||
val source = source() as String?
|
||||
if (!source!!.contains("..") && source.startsWith("/safe"))
|
||||
sink(source) // Safe
|
||||
else
|
||||
sink(source) // $ hasTaintFlow
|
||||
}
|
||||
run {
|
||||
val source = source() as String?
|
||||
if (source!!.indexOf("..") == -1 && source.startsWith("/safe"))
|
||||
sink(source) // Safe
|
||||
else
|
||||
sink(source) // $ hasTaintFlow
|
||||
}
|
||||
run {
|
||||
val source = source() as String?
|
||||
if (source!!.lastIndexOf("..") == -1 && source.startsWith("/safe"))
|
||||
sink(source) // Safe
|
||||
else
|
||||
sink(source) // $ hasTaintFlow
|
||||
}
|
||||
// PathTraversalSanitizer + allowListGuard
|
||||
run {
|
||||
val source: File? = source() as File?
|
||||
val normalized: String = source!!.canonicalPath
|
||||
if (normalized.startsWith("/safe")) {
|
||||
sink(source) // Safe
|
||||
sink(normalized) // Safe
|
||||
} else {
|
||||
sink(source) // $ hasTaintFlow
|
||||
sink(normalized) // $ hasTaintFlow
|
||||
}
|
||||
}
|
||||
run {
|
||||
val source: File? = source() as File?
|
||||
val normalized: File = source!!.canonicalFile
|
||||
if (normalized.startsWith("/safe")) {
|
||||
sink(source) // Safe
|
||||
sink(normalized) // Safe
|
||||
} else {
|
||||
sink(source) // $ hasTaintFlow
|
||||
sink(normalized) // $ hasTaintFlow
|
||||
}
|
||||
}
|
||||
run {
|
||||
val source: File? = source() as File?
|
||||
val normalized: String = source!!.canonicalFile.toString()
|
||||
if (normalized.startsWith("/safe")) {
|
||||
sink(source) // Safe
|
||||
sink(normalized) // Safe
|
||||
} else {
|
||||
sink(source) // $ hasTaintFlow
|
||||
sink(normalized) // $ hasTaintFlow
|
||||
}
|
||||
}
|
||||
run {
|
||||
val source = source() as String?
|
||||
val normalized: Path = Paths.get(source).normalize()
|
||||
if (normalized.startsWith("/safe")) {
|
||||
sink(source) // Safe
|
||||
sink(normalized) // Safe
|
||||
} else {
|
||||
sink(source) // $ hasTaintFlow
|
||||
sink(normalized) // $ hasTaintFlow
|
||||
}
|
||||
}
|
||||
run {
|
||||
val source = source() as String?
|
||||
// normalize().toString() gets extracted as Object.toString, stopping taint here
|
||||
val normalized: String = Paths.get(source).normalize().toString()
|
||||
if (normalized.startsWith("/safe")) {
|
||||
sink(source) // $ SPURIOUS: hasTaintFlow
|
||||
sink(normalized) // Safe
|
||||
} else {
|
||||
sink(source) // $ hasTaintFlow
|
||||
sink(normalized) // $ MISSING: hasTaintFlow
|
||||
}
|
||||
}
|
||||
run {
|
||||
val source = source() as String?
|
||||
// normalize().toString() gets extracted as Object.toString, stopping taint here
|
||||
val normalized: String = Paths.get(source).normalize().toString()
|
||||
if (normalized.regionMatches(0, "/safe", 0, 5)) {
|
||||
sink(source) // $ SPURIOUS: hasTaintFlow
|
||||
sink(normalized) // Safe
|
||||
} else {
|
||||
sink(source) // $ hasTaintFlow
|
||||
sink(normalized) // $ MISSING: hasTaintFlow
|
||||
}
|
||||
}
|
||||
run {
|
||||
val source = source() as String?
|
||||
// normalize().toString() gets extracted as Object.toString, stopping taint here
|
||||
val normalized: String = Paths.get(source).normalize().toString()
|
||||
if (normalized.matches("/safe/.*".toRegex())) {
|
||||
sink(source) // $ SPURIOUS: hasTaintFlow
|
||||
sink(normalized) // Safe
|
||||
} else {
|
||||
sink(source) // $ hasTaintFlow
|
||||
sink(normalized) // $ MISSING: hasTaintFlow
|
||||
}
|
||||
}
|
||||
// validation method
|
||||
run {
|
||||
val source = source() as String?
|
||||
allowListGuardValidation(source)
|
||||
sink(source) // Safe
|
||||
}
|
||||
// PathInjectionSanitizer + partial string match is considered unsafe
|
||||
run {
|
||||
val source = source() as String?
|
||||
// normalize().toString() gets extracted as Object.toString, stopping taint here
|
||||
val normalized: String = Paths.get(source).normalize().toString()
|
||||
if (normalized.contains("/safe")) {
|
||||
sink(source) // $ hasTaintFlow
|
||||
sink(normalized) // $ MISSING: hasTaintFlow
|
||||
} else {
|
||||
sink(source) // $ hasTaintFlow
|
||||
sink(normalized) // $ MISSING: hasTaintFlow
|
||||
}
|
||||
}
|
||||
run {
|
||||
val source = source() as String?
|
||||
// normalize().toString() gets extracted as Object.toString, stopping taint here
|
||||
val normalized: String = Paths.get(source).normalize().toString()
|
||||
if (normalized.regionMatches(1, "/safe", 0, 5)) {
|
||||
sink(source) // $ hasTaintFlow
|
||||
sink(normalized) // $ MISSING: hasTaintFlow
|
||||
} else {
|
||||
sink(source) // $ hasTaintFlow
|
||||
sink(normalized) // $ MISSING: hasTaintFlow
|
||||
}
|
||||
}
|
||||
run {
|
||||
val source = source() as String?
|
||||
// normalize().toString() gets extracted as Object.toString, stopping taint here
|
||||
val normalized: String = Paths.get(source).normalize().toString()
|
||||
if (normalized.matches(".*/safe/.*".toRegex())) {
|
||||
sink(source) // $ hasTaintFlow
|
||||
sink(normalized) // $ MISSING: hasTaintFlow
|
||||
} else {
|
||||
sink(source) // $ hasTaintFlow
|
||||
sink(normalized) // $ MISSING: hasTaintFlow
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Throws(Exception::class)
|
||||
private fun dotDotCheckGuardValidation(path: String?) {
|
||||
if (!path!!.startsWith("/safe") || path.contains("..")) throw Exception()
|
||||
}
|
||||
|
||||
@Throws(Exception::class)
|
||||
fun dotDotCheckGuard() {
|
||||
// dot dot check by itself is not enough
|
||||
run {
|
||||
val source = source() as String?
|
||||
if (!source!!.contains("..")) {
|
||||
sink(source) // $ hasTaintFlow
|
||||
} else
|
||||
sink(source) // $ hasTaintFlow
|
||||
}
|
||||
// allowListGuard + dotDotCheckGuard
|
||||
run {
|
||||
val source = source() as String?
|
||||
if (source!!.startsWith("/safe") && !source.contains(".."))
|
||||
sink(source) // Safe
|
||||
else
|
||||
sink(source) // $ hasTaintFlow
|
||||
}
|
||||
run {
|
||||
val source = source() as String?
|
||||
if (source!!.startsWith("/safe") && source.indexOf("..") == -1)
|
||||
sink(source) // Safe
|
||||
else
|
||||
sink(source) // $ hasTaintFlow
|
||||
}
|
||||
run {
|
||||
val source = source() as String?
|
||||
if (!source!!.startsWith("/safe") || source.indexOf("..") != -1)
|
||||
sink(source) // $ hasTaintFlow
|
||||
else
|
||||
sink(source) // Safe
|
||||
}
|
||||
run {
|
||||
val source = source() as String?
|
||||
if (source!!.startsWith("/safe") && source.lastIndexOf("..") == -1)
|
||||
sink(source) // Safe
|
||||
else
|
||||
sink(source) // $ hasTaintFlow
|
||||
}
|
||||
// blockListGuard + dotDotCheckGuard
|
||||
run {
|
||||
val source = source() as String?
|
||||
if (!source!!.startsWith("/data") && !source.contains(".."))
|
||||
sink(source) // Safe
|
||||
else
|
||||
sink(source) // $ hasTaintFlow
|
||||
}
|
||||
run {
|
||||
val source = source() as String?
|
||||
if (!source!!.startsWith("/data") && source.indexOf("..") == -1)
|
||||
sink(source) // Safe
|
||||
else
|
||||
sink(source) // $ hasTaintFlow
|
||||
}
|
||||
run {
|
||||
val source = source() as String?
|
||||
if (source!!.startsWith("/data") || source.indexOf("..") != -1)
|
||||
sink(source) // $ hasTaintFlow
|
||||
else
|
||||
sink(source) // Safe
|
||||
}
|
||||
run {
|
||||
val source = source() as String?
|
||||
if (!source!!.startsWith("/data") && source.lastIndexOf("..") == -1)
|
||||
sink(source) // Safe
|
||||
else
|
||||
sink(source) // $ hasTaintFlow
|
||||
}
|
||||
// validation method
|
||||
run {
|
||||
val source = source() as String?
|
||||
dotDotCheckGuardValidation(source)
|
||||
sink(source) // Safe
|
||||
}
|
||||
}
|
||||
|
||||
@Throws(Exception::class)
|
||||
private fun blockListGuardValidation(path: String?) {
|
||||
if (path!!.contains("..") || path.startsWith("/data")) throw Exception()
|
||||
}
|
||||
|
||||
@Throws(Exception::class)
|
||||
fun blockListGuard() {
|
||||
// Prefix check by itself is not enough
|
||||
run {
|
||||
val source = source() as String?
|
||||
if (!source!!.startsWith("/data")) {
|
||||
sink(source) // $ hasTaintFlow
|
||||
} else
|
||||
sink(source) // $ hasTaintFlow
|
||||
}
|
||||
// PathTraversalGuard + blockListGuard
|
||||
run {
|
||||
val source = source() as String?
|
||||
if (!source!!.contains("..") && !source.startsWith("/data"))
|
||||
sink(source) // Safe
|
||||
else
|
||||
sink(source) // $ hasTaintFlow
|
||||
}
|
||||
run {
|
||||
val source = source() as String?
|
||||
if (source!!.indexOf("..") == -1 && !source.startsWith("/data"))
|
||||
sink(source) // Safe
|
||||
else
|
||||
sink(source) // $ hasTaintFlow
|
||||
}
|
||||
run {
|
||||
val source = source() as String?
|
||||
if (source!!.lastIndexOf("..") == -1 && !source.startsWith("/data"))
|
||||
sink(source) // Safe
|
||||
else
|
||||
sink(source) // $ hasTaintFlow
|
||||
}
|
||||
// PathTraversalSanitizer + blockListGuard
|
||||
run {
|
||||
val source: File? = source() as File?
|
||||
val normalized: String = source!!.canonicalPath
|
||||
if (!normalized.startsWith("/data")) {
|
||||
sink(source) // Safe
|
||||
sink(normalized) // Safe
|
||||
} else {
|
||||
sink(source) // $ hasTaintFlow
|
||||
sink(normalized) // $ hasTaintFlow
|
||||
}
|
||||
}
|
||||
run {
|
||||
val source: File? = source() as File?
|
||||
val normalized: File = source!!.canonicalFile
|
||||
if (!normalized.startsWith("/data")) {
|
||||
sink(source) // Safe
|
||||
sink(normalized) // Safe
|
||||
} else {
|
||||
sink(source) // $ hasTaintFlow
|
||||
sink(normalized) // $ hasTaintFlow
|
||||
}
|
||||
}
|
||||
run {
|
||||
val source: File? = source() as File?
|
||||
val normalized: String = source!!.canonicalFile.toString()
|
||||
if (!normalized.startsWith("/data")) {
|
||||
sink(source) // Safe
|
||||
sink(normalized) // Safe
|
||||
} else {
|
||||
sink(source) // $ hasTaintFlow
|
||||
sink(normalized) // $ hasTaintFlow
|
||||
}
|
||||
}
|
||||
run {
|
||||
val source = source() as String?
|
||||
val normalized: Path = Paths.get(source).normalize()
|
||||
if (!normalized.startsWith("/data")) {
|
||||
sink(source) // Safe
|
||||
sink(normalized) // Safe
|
||||
} else {
|
||||
sink(source) // $ hasTaintFlow
|
||||
sink(normalized) // $ hasTaintFlow
|
||||
}
|
||||
}
|
||||
run {
|
||||
val source = source() as String?
|
||||
// normalize().toString() gets extracted as Object.toString, stopping taint here
|
||||
val normalized: String = Paths.get(source).normalize().toString()
|
||||
if (!normalized.startsWith("/data")) {
|
||||
sink(source) // $ SPURIOUS: hasTaintFlow
|
||||
sink(normalized) // Safe
|
||||
} else {
|
||||
sink(source) // $ hasTaintFlow
|
||||
sink(normalized) // $ MISSING: hasTaintFlow
|
||||
}
|
||||
}
|
||||
run {
|
||||
val source = source() as String?
|
||||
// normalize().toString() gets extracted as Object.toString, stopping taint here
|
||||
val normalized: String = Paths.get(source).normalize().toString()
|
||||
if (!normalized.regionMatches(0, "/data", 0, 5)) {
|
||||
sink(source) // $ SPURIOUS: hasTaintFlow
|
||||
sink(normalized) // Safe
|
||||
} else {
|
||||
sink(source) // $ hasTaintFlow
|
||||
sink(normalized) // $ MISSING: hasTaintFlow
|
||||
}
|
||||
}
|
||||
run {
|
||||
val source = source() as String?
|
||||
// normalize().toString() gets extracted as Object.toString, stopping taint here
|
||||
val normalized: String = Paths.get(source).normalize().toString()
|
||||
if (!normalized.matches("/data/.*".toRegex())) {
|
||||
sink(source) // $ SPURIOUS: hasTaintFlow
|
||||
sink(normalized) // Safe
|
||||
} else {
|
||||
sink(source) // $ hasTaintFlow
|
||||
sink(normalized) // $ MISSING: hasTaintFlow
|
||||
}
|
||||
}
|
||||
// validation method
|
||||
run {
|
||||
val source = source() as String?
|
||||
blockListGuardValidation(source)
|
||||
sink(source) // Safe
|
||||
}
|
||||
// PathInjectionSanitizer + partial string match with disallowed words
|
||||
run {
|
||||
val source = source() as String?
|
||||
// normalize().toString() gets extracted as Object.toString, stopping taint here
|
||||
val normalized: String = Paths.get(source).normalize().toString()
|
||||
if (!normalized.contains("/")) {
|
||||
sink(source) // $ SPURIOUS: hasTaintFlow
|
||||
sink(normalized) // Safe
|
||||
} else {
|
||||
sink(source) // $ hasTaintFlow
|
||||
sink(normalized) // $ MISSING: hasTaintFlow
|
||||
}
|
||||
}
|
||||
run {
|
||||
val source = source() as String?
|
||||
// normalize().toString() gets extracted as Object.toString, stopping taint here
|
||||
val normalized: String = Paths.get(source).normalize().toString()
|
||||
if (!normalized.regionMatches(1, "/", 0, 5)) {
|
||||
sink(source) // $ SPURIOUS: hasTaintFlow
|
||||
sink(normalized) // Safe
|
||||
} else {
|
||||
sink(source) // $ hasTaintFlow
|
||||
sink(normalized) // $ MISSING: hasTaintFlow
|
||||
}
|
||||
}
|
||||
run {
|
||||
val source = source() as String?
|
||||
// normalize().toString() gets extracted as Object.toString, stopping taint here
|
||||
val normalized: String = Paths.get(source).normalize().toString()
|
||||
if (!normalized.matches("/".toRegex())) {
|
||||
sink(source) // $ SPURIOUS: hasTaintFlow
|
||||
sink(normalized) // Safe
|
||||
} else {
|
||||
sink(source) // $ hasTaintFlow
|
||||
sink(normalized) // $ MISSING: hasTaintFlow
|
||||
}
|
||||
}
|
||||
// PathInjectionSanitizer + partial string match with disallowed prefixes
|
||||
run {
|
||||
val source = source() as String?
|
||||
// normalize().toString() gets extracted as Object.toString, stopping taint here
|
||||
val normalized: String = Paths.get(source).normalize().toString()
|
||||
if (!normalized.contains("/data")) {
|
||||
sink(source) // $ SPURIOUS: hasTaintFlow
|
||||
sink(normalized) // Safe
|
||||
} else {
|
||||
sink(source) // $ hasTaintFlow
|
||||
sink(normalized) // $ MISSING: hasTaintFlow
|
||||
}
|
||||
}
|
||||
run {
|
||||
val source = source() as String?
|
||||
// normalize().toString() gets extracted as Object.toString, stopping taint here
|
||||
val normalized: String = Paths.get(source).normalize().toString()
|
||||
if (!normalized.regionMatches(1, "/data", 0, 5)) {
|
||||
sink(source) // $ SPURIOUS: hasTaintFlow
|
||||
sink(normalized) // Safe
|
||||
} else {
|
||||
sink(source) // $ hasTaintFlow
|
||||
sink(normalized) // $ MISSING: hasTaintFlow
|
||||
}
|
||||
}
|
||||
run {
|
||||
val source = source() as String?
|
||||
// normalize().toString() gets extracted as Object.toString, stopping taint here
|
||||
val normalized: String = Paths.get(source).normalize().toString()
|
||||
if (!normalized.matches(".*/data/.*".toRegex())) {
|
||||
sink(source) // $ SPURIOUS: hasTaintFlow
|
||||
sink(normalized) // Safe
|
||||
} else {
|
||||
sink(source) // $ hasTaintFlow
|
||||
sink(normalized) // $ MISSING: hasTaintFlow
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1 +1,2 @@
|
||||
//semmle-extractor-options: --javac-args -cp ${testdir}/../../stubs/google-android-9.0.0
|
||||
//codeql-extractor-kotlin-options: ${testdir}/../../stubs/google-android-9.0.0
|
||||
|
||||
@@ -84,5 +84,15 @@ class Test {
|
||||
response.addCookie(cookie);
|
||||
}
|
||||
|
||||
{
|
||||
// GOOD: set secure flag in call to `createSecureCookie`
|
||||
response.addCookie(createSecureCookie());
|
||||
}
|
||||
}
|
||||
|
||||
private static Cookie createSecureCookie() {
|
||||
Cookie cookie = new Cookie("secret", "fakesecret");
|
||||
cookie.setSecure(constTrue);
|
||||
return cookie;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user