mirror of
https://github.com/github/codeql.git
synced 2026-02-23 18:33:42 +01:00
Merge branch 'main' into java/UnsafeDeserialization
This commit is contained in:
@@ -146,9 +146,11 @@ class Container extends @container, Top {
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a textual representation of the path of this container.
|
||||
* Gets a textual representation of this container.
|
||||
*
|
||||
* This is the absolute path of the container.
|
||||
* The default implementation gets the absolute path to the container, but subclasses may override
|
||||
* to provide a different result. To get the absolute path of any `Container`, call
|
||||
* `Container.getAbsolutePath()` directly.
|
||||
*/
|
||||
override string toString() { result = getAbsolutePath() }
|
||||
}
|
||||
@@ -159,6 +161,8 @@ class Folder extends Container, @folder {
|
||||
|
||||
/** Gets the URL of this folder. */
|
||||
override string getURL() { result = "folder://" + getAbsolutePath() }
|
||||
|
||||
override string getAPrimaryQlClass() { result = "Folder" }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -171,6 +175,8 @@ class File extends Container, @file {
|
||||
|
||||
/** Gets the URL of this file. */
|
||||
override string getURL() { result = "file://" + this.getAbsolutePath() + ":0:0:0:0" }
|
||||
|
||||
override string getAPrimaryQlClass() { result = "File" }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -204,4 +210,6 @@ class JarFile extends File {
|
||||
string getManifestEntryAttribute(string entry, string key) {
|
||||
jarManifestEntries(this, entry, key, result)
|
||||
}
|
||||
|
||||
override string getAPrimaryQlClass() { result = "JarFile" }
|
||||
}
|
||||
|
||||
@@ -27,4 +27,6 @@ class Exception extends Element, @exception {
|
||||
override predicate hasName(string name) { this.getType().hasName(name) }
|
||||
|
||||
override string toString() { result = this.getType().toString() }
|
||||
|
||||
override string getAPrimaryQlClass() { result = "Exception" }
|
||||
}
|
||||
|
||||
@@ -638,7 +638,21 @@ class BooleanLiteral extends Literal, @booleanliteral {
|
||||
override string getAPrimaryQlClass() { result = "BooleanLiteral" }
|
||||
}
|
||||
|
||||
/** An integer literal. For example, `23`. */
|
||||
/**
|
||||
* An integer literal. For example, `23`.
|
||||
*
|
||||
* An integer literal can never be negative except when:
|
||||
* - It is written in binary, octal or hexadecimal notation
|
||||
* - It is written in decimal notation, has the value `2147483648` and is preceded
|
||||
* by a minus; in this case the value of the IntegerLiteral is -2147483648 and
|
||||
* the preceding minus will *not* be modeled as `MinusExpr`.
|
||||
*
|
||||
* In all other cases the preceding minus, if any, will be modeled as a separate
|
||||
* `MinusExpr`.
|
||||
*
|
||||
* The last exception is necessary because `2147483648` on its own would not be
|
||||
* a valid integer literal (and could also not be parsed as CodeQL `int`).
|
||||
*/
|
||||
class IntegerLiteral extends Literal, @integerliteral {
|
||||
/** Gets the int representation of this literal. */
|
||||
int getIntValue() { result = getValue().toInt() }
|
||||
@@ -646,12 +660,32 @@ class IntegerLiteral extends Literal, @integerliteral {
|
||||
override string getAPrimaryQlClass() { result = "IntegerLiteral" }
|
||||
}
|
||||
|
||||
/** A long literal. For example, `23l`. */
|
||||
/**
|
||||
* A long literal. For example, `23L`.
|
||||
*
|
||||
* A long literal can never be negative except when:
|
||||
* - It is written in binary, octal or hexadecimal notation
|
||||
* - It is written in decimal notation, has the value `9223372036854775808` and
|
||||
* is preceded by a minus; in this case the value of the LongLiteral is
|
||||
* -9223372036854775808 and the preceding minus will *not* be modeled as
|
||||
* `MinusExpr`.
|
||||
*
|
||||
* In all other cases the preceding minus, if any, will be modeled as a separate
|
||||
* `MinusExpr`.
|
||||
*
|
||||
* The last exception is necessary because `9223372036854775808` on its own
|
||||
* would not be a valid long literal.
|
||||
*/
|
||||
class LongLiteral extends Literal, @longliteral {
|
||||
override string getAPrimaryQlClass() { result = "LongLiteral" }
|
||||
}
|
||||
|
||||
/** A floating point literal. For example, `4.2f`. */
|
||||
/**
|
||||
* A float literal. For example, `4.2f`.
|
||||
*
|
||||
* A float literal is never negative; a preceding minus, if any, will always
|
||||
* be modeled as separate `MinusExpr`.
|
||||
*/
|
||||
class FloatingPointLiteral extends Literal, @floatingpointliteral {
|
||||
/**
|
||||
* Gets the value of this literal as CodeQL 64-bit `float`. The value will
|
||||
@@ -662,7 +696,12 @@ class FloatingPointLiteral extends Literal, @floatingpointliteral {
|
||||
override string getAPrimaryQlClass() { result = "FloatingPointLiteral" }
|
||||
}
|
||||
|
||||
/** A double literal. For example, `4.2`. */
|
||||
/**
|
||||
* A double literal. For example, `4.2`.
|
||||
*
|
||||
* A double literal is never negative; a preceding minus, if any, will always
|
||||
* be modeled as separate `MinusExpr`.
|
||||
*/
|
||||
class DoubleLiteral extends Literal, @doubleliteral {
|
||||
/**
|
||||
* Gets the value of this literal as CodeQL 64-bit `float`. The result will
|
||||
@@ -1241,7 +1280,7 @@ class ConditionalExpr extends Expr, @conditionalexpr {
|
||||
/**
|
||||
* A `switch` expression.
|
||||
*/
|
||||
class SwitchExpr extends Expr, @switchexpr {
|
||||
class SwitchExpr extends Expr, StmtParent, @switchexpr {
|
||||
/** Gets an immediate child statement of this `switch` expression. */
|
||||
Stmt getAStmt() { result.getParent() = this }
|
||||
|
||||
@@ -1769,7 +1808,7 @@ class WildcardTypeAccess extends Expr, @wildcardtypeaccess {
|
||||
* This includes method calls, constructor and super constructor invocations,
|
||||
* and constructors invoked through class instantiation.
|
||||
*/
|
||||
class Call extends Top, @caller {
|
||||
class Call extends ExprParent, @caller {
|
||||
/** Gets an argument supplied in this call. */
|
||||
/*abstract*/ Expr getAnArgument() { none() }
|
||||
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
*/
|
||||
|
||||
import Member
|
||||
import semmle.code.java.security.ExternalProcess
|
||||
|
||||
// --- Standard types ---
|
||||
/** The class `java.lang.Object`. */
|
||||
@@ -176,24 +177,37 @@ class TypeFile extends Class {
|
||||
}
|
||||
|
||||
// --- Standard methods ---
|
||||
/**
|
||||
* Any constructor of class `java.lang.ProcessBuilder`.
|
||||
*/
|
||||
class ProcessBuilderConstructor extends Constructor, ExecCallable {
|
||||
ProcessBuilderConstructor() { this.getDeclaringType() instanceof TypeProcessBuilder }
|
||||
|
||||
override int getAnExecutedArgument() { result = 0 }
|
||||
}
|
||||
|
||||
/**
|
||||
* Any of the methods named `command` on class `java.lang.ProcessBuilder`.
|
||||
*/
|
||||
class MethodProcessBuilderCommand extends Method {
|
||||
class MethodProcessBuilderCommand extends Method, ExecCallable {
|
||||
MethodProcessBuilderCommand() {
|
||||
hasName("command") and
|
||||
getDeclaringType() instanceof TypeProcessBuilder
|
||||
}
|
||||
|
||||
override int getAnExecutedArgument() { result = 0 }
|
||||
}
|
||||
|
||||
/**
|
||||
* Any method named `exec` on class `java.lang.Runtime`.
|
||||
*/
|
||||
class MethodRuntimeExec extends Method {
|
||||
class MethodRuntimeExec extends Method, ExecCallable {
|
||||
MethodRuntimeExec() {
|
||||
hasName("exec") and
|
||||
getDeclaringType() instanceof TypeRuntime
|
||||
}
|
||||
|
||||
override int getAnExecutedArgument() { result = 0 }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -353,7 +367,7 @@ class EqualsMethod extends Method {
|
||||
class HashCodeMethod extends Method {
|
||||
HashCodeMethod() {
|
||||
this.hasName("hashCode") and
|
||||
this.getNumberOfParameters() = 0
|
||||
this.hasNoParameters()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -365,6 +379,14 @@ class CloneMethod extends Method {
|
||||
}
|
||||
}
|
||||
|
||||
/** A method with the same signature as `java.lang.Object.toString`. */
|
||||
class ToStringMethod extends Method {
|
||||
ToStringMethod() {
|
||||
this.hasName("toString") and
|
||||
this.hasNoParameters()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The public static `main` method, with a single formal parameter
|
||||
* of type `String[]` and return type `void`.
|
||||
|
||||
@@ -8,6 +8,8 @@ import Element
|
||||
class Modifier extends Element, @modifier {
|
||||
/** Gets the element to which this modifier applies. */
|
||||
Element getElement() { hasModifier(result, this) }
|
||||
|
||||
override string getAPrimaryQlClass() { result = "Modifier" }
|
||||
}
|
||||
|
||||
/** An element of the Java syntax tree that may have a modifier. */
|
||||
@@ -16,7 +18,9 @@ abstract class Modifiable extends Element {
|
||||
* Holds if this element has modifier `m`.
|
||||
*
|
||||
* For most purposes, the more specialized predicates `isAbstract`, `isPublic`, etc.
|
||||
* should be used, which also take implicit modifiers into account.
|
||||
* should be used.
|
||||
*
|
||||
* Both this method and those specialized predicates take implicit modifiers into account.
|
||||
* For instance, non-default instance methods in interfaces are implicitly
|
||||
* abstract, so `isAbstract()` will hold for them even if `hasModifier("abstract")`
|
||||
* does not.
|
||||
|
||||
@@ -29,4 +29,6 @@ class Package extends Element, Annotatable, @package {
|
||||
* since packages do not have locations.
|
||||
*/
|
||||
string getURL() { result = "file://:0:0:0:0" }
|
||||
|
||||
override string getAPrimaryQlClass() { result = "Package" }
|
||||
}
|
||||
|
||||
@@ -433,9 +433,7 @@ final class ClassInterfaceNode extends ElementNode {
|
||||
or
|
||||
result.(FieldDeclaration).getAField().getDeclaringType() = ty
|
||||
or
|
||||
result.(NestedType).getEnclosingType().getSourceDeclaration() = ty and
|
||||
not result instanceof AnonymousClass and
|
||||
not result instanceof LocalClass
|
||||
result.(MemberType).getEnclosingType().getSourceDeclaration() = ty
|
||||
or
|
||||
isInitBlock(ty, result)
|
||||
}
|
||||
|
||||
@@ -73,10 +73,10 @@ class BlockStmt extends Stmt, @block {
|
||||
/** Gets the last statement in this block. */
|
||||
Stmt getLastStmt() { result = getStmt(getNumStmt() - 1) }
|
||||
|
||||
/** Gets a printable representation of this statement. May include more detail than `toString()`. */
|
||||
override string pp() { result = "{ ... }" }
|
||||
|
||||
/** This statement's Halstead ID (used to compute Halstead metrics). */
|
||||
override string toString() { result = "{ ... }" }
|
||||
|
||||
override string getHalsteadID() { result = "BlockStmt" }
|
||||
|
||||
override string getAPrimaryQlClass() { result = "BlockStmt" }
|
||||
@@ -130,14 +130,14 @@ class IfStmt extends ConditionalStmt, @ifstmt {
|
||||
/** Gets the `else` branch of this `if` statement. */
|
||||
Stmt getElse() { result.isNthChildOf(this, 2) }
|
||||
|
||||
/** Gets a printable representation of this statement. May include more detail than `toString()`. */
|
||||
override string pp() {
|
||||
result = "if (...) " + this.getThen().pp() + " else " + this.getElse().pp()
|
||||
or
|
||||
not exists(this.getElse()) and result = "if (...) " + this.getThen().pp()
|
||||
}
|
||||
|
||||
/** This statement's Halstead ID (used to compute Halstead metrics). */
|
||||
override string toString() { result = "if (...)" }
|
||||
|
||||
override string getHalsteadID() { result = "IfStmt" }
|
||||
|
||||
override string getAPrimaryQlClass() { result = "IfStmt" }
|
||||
@@ -201,10 +201,10 @@ class ForStmt extends ConditionalStmt, @forstmt {
|
||||
getCondition().getAChildExpr*() = result.getAnAccess()
|
||||
}
|
||||
|
||||
/** Gets a printable representation of this statement. May include more detail than `toString()`. */
|
||||
override string pp() { result = "for (...;...;...) " + this.getStmt().pp() }
|
||||
|
||||
/** This statement's Halstead ID (used to compute Halstead metrics). */
|
||||
override string toString() { result = "for (...;...;...)" }
|
||||
|
||||
override string getHalsteadID() { result = "ForStmt" }
|
||||
|
||||
override string getAPrimaryQlClass() { result = "ForStmt" }
|
||||
@@ -221,10 +221,10 @@ class EnhancedForStmt extends Stmt, @enhancedforstmt {
|
||||
/** Gets the body of this enhanced `for` loop. */
|
||||
Stmt getStmt() { result.getParent() = this }
|
||||
|
||||
/** Gets a printable representation of this statement. May include more detail than `toString()`. */
|
||||
override string pp() { result = "for (...) " + this.getStmt().pp() }
|
||||
override string pp() { result = "for (... : ...) " + this.getStmt().pp() }
|
||||
|
||||
override string toString() { result = "for (... : ...)" }
|
||||
|
||||
/** This statement's Halstead ID (used to compute Halstead metrics). */
|
||||
override string getHalsteadID() { result = "EnhancedForStmt" }
|
||||
|
||||
override string getAPrimaryQlClass() { result = "EnhancedForStmt" }
|
||||
@@ -244,10 +244,10 @@ class WhileStmt extends ConditionalStmt, @whilestmt {
|
||||
*/
|
||||
deprecated override Stmt getTrueSuccessor() { result = getStmt() }
|
||||
|
||||
/** Gets a printable representation of this statement. May include more detail than `toString()`. */
|
||||
override string pp() { result = "while (...) " + this.getStmt().pp() }
|
||||
|
||||
/** This statement's Halstead ID (used to compute Halstead metrics). */
|
||||
override string toString() { result = "while (...)" }
|
||||
|
||||
override string getHalsteadID() { result = "WhileStmt" }
|
||||
|
||||
override string getAPrimaryQlClass() { result = "WhileStmt" }
|
||||
@@ -267,10 +267,10 @@ class DoStmt extends ConditionalStmt, @dostmt {
|
||||
*/
|
||||
deprecated override Stmt getTrueSuccessor() { result = getStmt() }
|
||||
|
||||
/** Gets a printable representation of this statement. May include more detail than `toString()`. */
|
||||
override string pp() { result = "do " + this.getStmt().pp() + " while (...)" }
|
||||
|
||||
/** This statement's Halstead ID (used to compute Halstead metrics). */
|
||||
override string toString() { result = "do ... while (...)" }
|
||||
|
||||
override string getHalsteadID() { result = "DoStmt" }
|
||||
|
||||
override string getAPrimaryQlClass() { result = "DoStmt" }
|
||||
@@ -356,10 +356,10 @@ class TryStmt extends Stmt, @trystmt {
|
||||
result = getAResourceExpr().getVariable()
|
||||
}
|
||||
|
||||
/** Gets a printable representation of this statement. May include more detail than `toString()`. */
|
||||
override string pp() { result = "try " + this.getBlock().pp() + " catch (...)" }
|
||||
|
||||
/** This statement's Halstead ID (used to compute Halstead metrics). */
|
||||
override string toString() { result = "try ..." }
|
||||
|
||||
override string getHalsteadID() { result = "TryStmt" }
|
||||
|
||||
override string getAPrimaryQlClass() { result = "TryStmt" }
|
||||
@@ -387,10 +387,10 @@ class CatchClause extends Stmt, @catchclause {
|
||||
)
|
||||
}
|
||||
|
||||
/** Gets a printable representation of this statement. May include more detail than `toString()`. */
|
||||
override string pp() { result = "catch (...) " + this.getBlock().pp() }
|
||||
|
||||
/** This statement's Halstead ID (used to compute Halstead metrics). */
|
||||
override string toString() { result = "catch (...)" }
|
||||
|
||||
override string getHalsteadID() { result = "CatchClause" }
|
||||
|
||||
override string getAPrimaryQlClass() { result = "CatchClause" }
|
||||
@@ -422,10 +422,10 @@ class SwitchStmt extends Stmt, @switchstmt {
|
||||
/** Gets the expression of this `switch` statement. */
|
||||
Expr getExpr() { result.getParent() = this }
|
||||
|
||||
/** Gets a printable representation of this statement. May include more detail than `toString()`. */
|
||||
override string pp() { result = "switch (...)" }
|
||||
|
||||
/** This statement's Halstead ID (used to compute Halstead metrics). */
|
||||
override string toString() { result = "switch (...)" }
|
||||
|
||||
override string getHalsteadID() { result = "SwitchStmt" }
|
||||
|
||||
override string getAPrimaryQlClass() { result = "SwitchStmt" }
|
||||
@@ -485,10 +485,10 @@ class ConstCase extends SwitchCase {
|
||||
*/
|
||||
Expr getValue(int i) { result.getParent() = this and result.getIndex() = i and i >= 0 }
|
||||
|
||||
/** Gets a printable representation of this statement. May include more detail than `toString()`. */
|
||||
override string pp() { result = "case ..." }
|
||||
|
||||
/** This statement's Halstead ID (used to compute Halstead metrics). */
|
||||
override string toString() { result = "case ..." }
|
||||
|
||||
override string getHalsteadID() { result = "ConstCase" }
|
||||
|
||||
override string getAPrimaryQlClass() { result = "ConstCase" }
|
||||
@@ -498,10 +498,10 @@ class ConstCase extends SwitchCase {
|
||||
class DefaultCase extends SwitchCase {
|
||||
DefaultCase() { not exists(Expr e | e.getParent() = this | e.getIndex() >= 0) }
|
||||
|
||||
/** Gets a printable representation of this statement. May include more detail than `toString()`. */
|
||||
override string pp() { result = "default" }
|
||||
|
||||
/** This statement's Halstead ID (used to compute Halstead metrics). */
|
||||
override string toString() { result = "default" }
|
||||
|
||||
override string getHalsteadID() { result = "DefaultCase" }
|
||||
|
||||
override string getAPrimaryQlClass() { result = "DefaultCase" }
|
||||
@@ -515,10 +515,10 @@ class SynchronizedStmt extends Stmt, @synchronizedstmt {
|
||||
/** Gets the block of this `synchronized` statement. */
|
||||
Stmt getBlock() { result.getParent() = this }
|
||||
|
||||
/** Gets a printable representation of this statement. May include more detail than `toString()`. */
|
||||
override string pp() { result = "synchronized (...) " + this.getBlock().pp() }
|
||||
|
||||
/** This statement's Halstead ID (used to compute Halstead metrics). */
|
||||
override string toString() { result = "synchronized (...)" }
|
||||
|
||||
override string getHalsteadID() { result = "SynchronizedStmt" }
|
||||
|
||||
override string getAPrimaryQlClass() { result = "SynchronizedStmt" }
|
||||
@@ -529,10 +529,10 @@ class ReturnStmt extends Stmt, @returnstmt {
|
||||
/** Gets the expression returned by this `return` statement, if any. */
|
||||
Expr getResult() { result.getParent() = this }
|
||||
|
||||
/** Gets a printable representation of this statement. May include more detail than `toString()`. */
|
||||
override string pp() { result = "return ..." }
|
||||
|
||||
/** This statement's Halstead ID (used to compute Halstead metrics). */
|
||||
override string toString() { result = "return ..." }
|
||||
|
||||
override string getHalsteadID() { result = "ReturnStmt" }
|
||||
|
||||
override string getAPrimaryQlClass() { result = "ReturnStmt" }
|
||||
@@ -543,10 +543,10 @@ class ThrowStmt extends Stmt, @throwstmt {
|
||||
/** Gets the expression thrown by this `throw` statement. */
|
||||
Expr getExpr() { result.getParent() = this }
|
||||
|
||||
/** Gets a printable representation of this statement. May include more detail than `toString()`. */
|
||||
override string pp() { result = "throw ..." }
|
||||
|
||||
/** This statement's Halstead ID (used to compute Halstead metrics). */
|
||||
override string toString() { result = "throw ..." }
|
||||
|
||||
override string getHalsteadID() { result = "ThrowStmt" }
|
||||
|
||||
/** Gets the type of the expression thrown by this `throw` statement. */
|
||||
@@ -638,12 +638,12 @@ class BreakStmt extends Stmt, @breakstmt {
|
||||
/** Holds if this `break` statement has an explicit label. */
|
||||
predicate hasLabel() { exists(string s | s = this.getLabel()) }
|
||||
|
||||
/** Gets a printable representation of this statement. May include more detail than `toString()`. */
|
||||
override string pp() {
|
||||
if this.hasLabel() then result = "break " + this.getLabel() else result = "break"
|
||||
}
|
||||
|
||||
/** This statement's Halstead ID (used to compute Halstead metrics). */
|
||||
override string toString() { result = "break" }
|
||||
|
||||
override string getHalsteadID() { result = "BreakStmt" }
|
||||
|
||||
override string getAPrimaryQlClass() { result = "BreakStmt" }
|
||||
@@ -660,6 +660,8 @@ class YieldStmt extends Stmt, @yieldstmt {
|
||||
|
||||
override string pp() { result = "yield ..." }
|
||||
|
||||
override string toString() { result = "yield ..." }
|
||||
|
||||
override string getHalsteadID() { result = "YieldStmt" }
|
||||
|
||||
override string getAPrimaryQlClass() { result = "YieldStmt" }
|
||||
@@ -673,12 +675,12 @@ class ContinueStmt extends Stmt, @continuestmt {
|
||||
/** Holds if this `continue` statement has an explicit label. */
|
||||
predicate hasLabel() { exists(string s | s = this.getLabel()) }
|
||||
|
||||
/** Gets a printable representation of this statement. May include more detail than `toString()`. */
|
||||
override string pp() {
|
||||
if this.hasLabel() then result = "continue " + this.getLabel() else result = "continue"
|
||||
}
|
||||
|
||||
/** This statement's Halstead ID (used to compute Halstead metrics). */
|
||||
override string toString() { result = "continue" }
|
||||
|
||||
override string getHalsteadID() { result = "ContinueStmt" }
|
||||
|
||||
override string getAPrimaryQlClass() { result = "ContinueStmt" }
|
||||
@@ -686,10 +688,10 @@ class ContinueStmt extends Stmt, @continuestmt {
|
||||
|
||||
/** The empty statement. */
|
||||
class EmptyStmt extends Stmt, @emptystmt {
|
||||
/** Gets a printable representation of this statement. May include more detail than `toString()`. */
|
||||
override string pp() { result = ";" }
|
||||
|
||||
/** This statement's Halstead ID (used to compute Halstead metrics). */
|
||||
override string toString() { result = ";" }
|
||||
|
||||
override string getHalsteadID() { result = "EmptyStmt" }
|
||||
|
||||
override string getAPrimaryQlClass() { result = "EmptyStmt" }
|
||||
@@ -704,10 +706,10 @@ class ExprStmt extends Stmt, @exprstmt {
|
||||
/** Gets the expression of this expression statement. */
|
||||
Expr getExpr() { result.getParent() = this }
|
||||
|
||||
/** Gets a printable representation of this statement. May include more detail than `toString()`. */
|
||||
override string pp() { result = "...;" }
|
||||
override string pp() { result = "<Expr>;" }
|
||||
|
||||
override string toString() { result = "<Expr>;" }
|
||||
|
||||
/** This statement's Halstead ID (used to compute Halstead metrics). */
|
||||
override string getHalsteadID() { result = "ExprStmt" }
|
||||
|
||||
/** Holds if this statement represents a field declaration with an initializer. */
|
||||
@@ -733,13 +735,13 @@ class LabeledStmt extends Stmt, @labeledstmt {
|
||||
/** Gets the label of this labeled statement. */
|
||||
string getLabel() { namestrings(result, _, this) }
|
||||
|
||||
/** Gets a printable representation of this statement. May include more detail than `toString()`. */
|
||||
override string pp() { result = this.getLabel() + ": " + this.getStmt().pp() }
|
||||
|
||||
/** This statement's Halstead ID (used to compute Halstead metrics). */
|
||||
override string getHalsteadID() { result = this.getLabel() + ":" }
|
||||
|
||||
override string getAPrimaryQlClass() { result = "LabelStmt" }
|
||||
override string toString() { result = "<Label>: ..." }
|
||||
|
||||
override string getAPrimaryQlClass() { result = "LabeledStmt" }
|
||||
}
|
||||
|
||||
/** An `assert` statement. */
|
||||
@@ -750,12 +752,12 @@ class AssertStmt extends Stmt, @assertstmt {
|
||||
/** Gets the assertion message expression, if any. */
|
||||
Expr getMessage() { exprs(result, _, _, this, _) and result.getIndex() = 1 }
|
||||
|
||||
/** Gets a printable representation of this statement. May include more detail than `toString()`. */
|
||||
override string pp() {
|
||||
if exists(this.getMessage()) then result = "assert ... : ..." else result = "assert ..."
|
||||
}
|
||||
|
||||
/** This statement's Halstead ID (used to compute Halstead metrics). */
|
||||
override string toString() { result = "assert ..." }
|
||||
|
||||
override string getHalsteadID() { result = "AssertStmt" }
|
||||
|
||||
override string getAPrimaryQlClass() { result = "AssertStmt" }
|
||||
@@ -775,10 +777,10 @@ class LocalVariableDeclStmt extends Stmt, @localvariabledeclstmt {
|
||||
/** Gets an index of a variable declared in this local variable declaration statement. */
|
||||
int getAVariableIndex() { exists(getVariable(result)) }
|
||||
|
||||
/** Gets a printable representation of this statement. May include more detail than `toString()`. */
|
||||
override string pp() { result = "local variable declaration" }
|
||||
override string pp() { result = "var ...;" }
|
||||
|
||||
override string toString() { result = "var ...;" }
|
||||
|
||||
/** This statement's Halstead ID (used to compute Halstead metrics). */
|
||||
override string getHalsteadID() { result = "LocalVariableDeclStmt" }
|
||||
|
||||
override string getAPrimaryQlClass() { result = "LocalVariableDeclStmt" }
|
||||
@@ -789,10 +791,10 @@ class LocalClassDeclStmt extends Stmt, @localclassdeclstmt {
|
||||
/** Gets the local class declared by this statement. */
|
||||
LocalClass getLocalClass() { isLocalClass(result, this) }
|
||||
|
||||
/** Gets a printable representation of this statement. May include more detail than `toString()`. */
|
||||
override string pp() { result = "local class declaration: " + this.getLocalClass().toString() }
|
||||
override string pp() { result = "class " + this.getLocalClass().toString() }
|
||||
|
||||
override string toString() { result = "class ..." }
|
||||
|
||||
/** This statement's Halstead ID (used to compute Halstead metrics). */
|
||||
override string getHalsteadID() { result = "LocalClassDeclStmt" }
|
||||
|
||||
override string getAPrimaryQlClass() { result = "LocalClassDeclStmt" }
|
||||
@@ -829,13 +831,10 @@ class ThisConstructorInvocationStmt extends Stmt, ConstructorCall, @constructori
|
||||
/** Gets the immediately enclosing statement of this constructor invocation. */
|
||||
override Stmt getEnclosingStmt() { result = this }
|
||||
|
||||
/** Gets a printable representation of this statement. May include more detail than `toString()`. */
|
||||
override string pp() { result = "this(...)" }
|
||||
|
||||
/** Gets a printable representation of this statement. */
|
||||
override string toString() { result = pp() }
|
||||
override string toString() { result = "this(...)" }
|
||||
|
||||
/** This statement's Halstead ID (used to compute Halstead metrics). */
|
||||
override string getHalsteadID() { result = "ConstructorInvocationStmt" }
|
||||
|
||||
override string getAPrimaryQlClass() { result = "ThisConstructorInvocationStmt" }
|
||||
@@ -873,13 +872,10 @@ class SuperConstructorInvocationStmt extends Stmt, ConstructorCall, @superconstr
|
||||
/** Gets the immediately enclosing statement of this constructor invocation. */
|
||||
override Stmt getEnclosingStmt() { result = this }
|
||||
|
||||
/** Gets a printable representation of this statement. May include more detail than `toString()`. */
|
||||
override string pp() { result = "super(...)" }
|
||||
|
||||
/** Gets a printable representation of this statement. */
|
||||
override string toString() { result = pp() }
|
||||
override string toString() { result = "super(...)" }
|
||||
|
||||
/** This statement's Halstead ID (used to compute Halstead metrics). */
|
||||
override string getHalsteadID() { result = "SuperConstructorInvocationStmt" }
|
||||
|
||||
override string getAPrimaryQlClass() { result = "SuperConstructorInvocationStmt" }
|
||||
|
||||
@@ -517,7 +517,12 @@ class RefType extends Type, Annotatable, Modifiable, @reftype {
|
||||
/** Holds if this is a top-level type, which is not nested inside any other types. */
|
||||
predicate isTopLevel() { this instanceof TopLevelType }
|
||||
|
||||
/** Holds if this type is declared in a specified package with the specified name. */
|
||||
/**
|
||||
* Holds if this type is declared in a specified package with the specified name.
|
||||
*
|
||||
* For nested types the name of the nested type is prefixed with a `$` and appended
|
||||
* to the name of the enclosing type, which might be a nested type as well.
|
||||
*/
|
||||
predicate hasQualifiedName(string package, string type) {
|
||||
this.getPackage().hasName(package) and type = this.nestedName()
|
||||
}
|
||||
@@ -532,7 +537,12 @@ class RefType extends Type, Annotatable, Modifiable, @reftype {
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the qualified name of this type.
|
||||
* Gets the qualified name of this type, consisting of the package name followed by
|
||||
* a `.` and the name of this type.
|
||||
*
|
||||
* For nested types the name of the nested type is prefixed with a `$` and appended
|
||||
* to the name of the enclosing type, which might be a nested type as well. For example:
|
||||
* `java.lang.Thread$State`.
|
||||
*/
|
||||
string getQualifiedName() {
|
||||
exists(string pkgName | pkgName = getPackage().getName() |
|
||||
@@ -540,7 +550,13 @@ class RefType extends Type, Annotatable, Modifiable, @reftype {
|
||||
)
|
||||
}
|
||||
|
||||
/** Gets the nested name of this type. */
|
||||
/**
|
||||
* Gets the nested name of this type.
|
||||
*
|
||||
* If this type is not a nested type, the result is the same as `getName()`.
|
||||
* Otherwise the name of the nested type is prefixed with a `$` and appended to
|
||||
* the name of the enclosing type, which might be a nested type as well.
|
||||
*/
|
||||
string nestedName() {
|
||||
not this instanceof NestedType and result = this.getName()
|
||||
or
|
||||
@@ -788,6 +804,21 @@ class NestedType extends RefType {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A nested type which is a direct member of the enclosing type,
|
||||
* that is, neither an anonymous nor local class.
|
||||
*/
|
||||
class MemberType extends NestedType, Member {
|
||||
/**
|
||||
* Gets the qualified name of this member type.
|
||||
*
|
||||
* The qualified name consists of the package name, a `.`, the name of the declaring
|
||||
* type (which might be a nested or member type as well), followed by a `$` and the
|
||||
* name of this member type. For example: `java.lang.Thread$State`.
|
||||
*/
|
||||
override string getQualifiedName() { result = NestedType.super.getQualifiedName() }
|
||||
}
|
||||
|
||||
/**
|
||||
* A class declared within another type.
|
||||
*
|
||||
@@ -797,8 +828,9 @@ class NestedType extends RefType {
|
||||
class NestedClass extends NestedType, Class { }
|
||||
|
||||
/**
|
||||
* An inner class is a nested class that is neither
|
||||
* explicitly nor implicitly declared static.
|
||||
* An inner class is a nested class that is neither explicitly nor
|
||||
* implicitly declared static. This includes anonymous and local
|
||||
* classes.
|
||||
*/
|
||||
class InnerClass extends NestedClass {
|
||||
InnerClass() { not this.isStatic() }
|
||||
|
||||
@@ -53,6 +53,8 @@ class LocalVariableDecl extends @localvar, LocalScopeVariable {
|
||||
|
||||
/** Gets the initializer expression of this local variable declaration. */
|
||||
override Expr getInitializer() { result = getDeclExpr().getInit() }
|
||||
|
||||
override string getAPrimaryQlClass() { result = "LocalVariableDecl" }
|
||||
}
|
||||
|
||||
/** A formal parameter of a callable. */
|
||||
|
||||
10
java/ql/src/semmle/code/java/dataflow/DataFlow6.qll
Normal file
10
java/ql/src/semmle/code/java/dataflow/DataFlow6.qll
Normal file
@@ -0,0 +1,10 @@
|
||||
/**
|
||||
* Provides classes for performing local (intra-procedural) and
|
||||
* global (inter-procedural) data flow analyses.
|
||||
*/
|
||||
|
||||
import java
|
||||
|
||||
module DataFlow6 {
|
||||
import semmle.code.java.dataflow.internal.DataFlowImpl6
|
||||
}
|
||||
@@ -67,20 +67,38 @@
|
||||
import java
|
||||
private import semmle.code.java.dataflow.DataFlow::DataFlow
|
||||
private import internal.DataFlowPrivate
|
||||
private import internal.FlowSummaryImpl::Private::External
|
||||
private import internal.FlowSummaryImplSpecific
|
||||
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.ApacheHttp
|
||||
private import semmle.code.java.frameworks.apache.Lang
|
||||
private import semmle.code.java.frameworks.guava.Guava
|
||||
private import semmle.code.java.frameworks.jackson.JacksonSerializability
|
||||
private import semmle.code.java.security.ResponseSplitting
|
||||
private import semmle.code.java.security.InformationLeak
|
||||
private import semmle.code.java.security.XSS
|
||||
private import semmle.code.java.security.LdapInjection
|
||||
private import semmle.code.java.security.XPath
|
||||
private import semmle.code.java.security.JexlInjection
|
||||
}
|
||||
|
||||
private predicate sourceModelCsv(string row) {
|
||||
row =
|
||||
[
|
||||
// org.springframework.security.web.savedrequest.SavedRequest
|
||||
"org.springframework.security.web.savedrequest;SavedRequest;true;getRedirectUrl;;;ReturnValue;remote",
|
||||
"org.springframework.security.web.savedrequest;SavedRequest;true;getCookies;;;ReturnValue;remote",
|
||||
"org.springframework.security.web.savedrequest;SavedRequest;true;getHeaderValues;;;ReturnValue;remote",
|
||||
"org.springframework.security.web.savedrequest;SavedRequest;true;getHeaderNames;;;ReturnValue;remote",
|
||||
"org.springframework.security.web.savedrequest;SavedRequest;true;getParameterValues;;;ReturnValue;remote",
|
||||
"org.springframework.security.web.savedrequest;SavedRequest;true;getParameterMap;;;ReturnValue;remote",
|
||||
// ServletRequestGetParameterMethod
|
||||
"javax.servlet;ServletRequest;false;getParameter;(String);;ReturnValue;remote",
|
||||
"javax.servlet;ServletRequest;false;getParameterValues;(String);;ReturnValue;remote",
|
||||
@@ -184,7 +202,33 @@ private predicate sourceModelCsv(string row) {
|
||||
]
|
||||
}
|
||||
|
||||
private predicate sinkModelCsv(string row) { none() }
|
||||
private predicate sinkModelCsv(string row) {
|
||||
row =
|
||||
[
|
||||
// Open URL
|
||||
"java.net;URL;false;openConnection;;;Argument[-1];open-url",
|
||||
"java.net;URL;false;openStream;;;Argument[-1];open-url",
|
||||
// Create file
|
||||
"java.io;FileOutputStream;false;FileOutputStream;;;Argument[0];create-file",
|
||||
"java.io;RandomAccessFile;false;RandomAccessFile;;;Argument[0];create-file",
|
||||
"java.io;FileWriter;false;FileWriter;;;Argument[0];create-file",
|
||||
"java.nio.file;Files;false;move;;;Argument[1];create-file",
|
||||
"java.nio.file;Files;false;copy;;;Argument[1];create-file",
|
||||
"java.nio.file;Files;false;newOutputStream;;;Argument[0];create-file",
|
||||
"java.nio.file;Files;false;newBufferedReader;;;Argument[0];create-file",
|
||||
"java.nio.file;Files;false;createDirectory;;;Argument[0];create-file",
|
||||
"java.nio.file;Files;false;createFile;;;Argument[0];create-file",
|
||||
"java.nio.file;Files;false;createLink;;;Argument[0];create-file",
|
||||
"java.nio.file;Files;false;createSymbolicLink;;;Argument[0];create-file",
|
||||
"java.nio.file;Files;false;createTempDirectory;;;Argument[0];create-file",
|
||||
"java.nio.file;Files;false;createTempFile;;;Argument[0];create-file",
|
||||
// Bean validation
|
||||
"javax.validation;ConstraintValidatorContext;true;buildConstraintViolationWithTemplate;;;Argument[0];bean-validation",
|
||||
// Set hostname
|
||||
"javax.net.ssl;HttpsURLConnection;true;setDefaultHostnameVerifier;;;Argument[0];set-hostname-verifier",
|
||||
"javax.net.ssl;HttpsURLConnection;true;setHostnameVerifier;;;Argument[0];set-hostname-verifier"
|
||||
]
|
||||
}
|
||||
|
||||
private predicate summaryModelCsv(string row) {
|
||||
row =
|
||||
@@ -261,6 +305,7 @@ private predicate summaryModelCsv(string row) {
|
||||
"java.util;StringTokenizer;false;StringTokenizer;;;Argument[0];Argument[-1];taint",
|
||||
"java.beans;XMLDecoder;false;XMLDecoder;;;Argument[0];Argument[-1];taint",
|
||||
"com.esotericsoftware.kryo.io;Input;false;Input;;;Argument[0];Argument[-1];taint",
|
||||
"com.esotericsoftware.kryo5.io;Input;false;Input;;;Argument[0];Argument[-1];taint",
|
||||
"java.io;BufferedInputStream;false;BufferedInputStream;;;Argument[0];Argument[-1];taint",
|
||||
"java.io;DataInputStream;false;DataInputStream;;;Argument[0];Argument[-1];taint",
|
||||
"java.io;ByteArrayInputStream;false;ByteArrayInputStream;;;Argument[0];Argument[-1];taint",
|
||||
@@ -317,7 +362,8 @@ private predicate summaryModel(string row) {
|
||||
any(SummaryModelCsv s).row(row)
|
||||
}
|
||||
|
||||
private predicate sourceModel(
|
||||
/** Holds if a source model exists for the given parameters. */
|
||||
predicate sourceModel(
|
||||
string namespace, string type, boolean subtypes, string name, string signature, string ext,
|
||||
string output, string kind
|
||||
) {
|
||||
@@ -335,7 +381,8 @@ private predicate sourceModel(
|
||||
)
|
||||
}
|
||||
|
||||
private predicate sinkModel(
|
||||
/** Holds if a sink model exists for the given parameters. */
|
||||
predicate sinkModel(
|
||||
string namespace, string type, boolean subtypes, string name, string signature, string ext,
|
||||
string input, string kind
|
||||
) {
|
||||
@@ -353,7 +400,8 @@ private predicate sinkModel(
|
||||
)
|
||||
}
|
||||
|
||||
private predicate summaryModel(
|
||||
/** Holds if a summary model exists for the given parameters. */
|
||||
predicate summaryModel(
|
||||
string namespace, string type, boolean subtypes, string name, string signature, string ext,
|
||||
string input, string output, string kind
|
||||
) {
|
||||
@@ -440,7 +488,7 @@ module CsvValidation {
|
||||
not namespace.regexpMatch("[a-zA-Z0-9_\\.]+") and
|
||||
msg = "Dubious namespace \"" + namespace + "\" in " + pred + " model."
|
||||
or
|
||||
not type.regexpMatch("[a-zA-Z0-9_\\$]+") and
|
||||
not type.regexpMatch("[a-zA-Z0-9_\\$<>]+") and
|
||||
msg = "Dubious type \"" + type + "\" in " + pred + " model."
|
||||
or
|
||||
not name.regexpMatch("[a-zA-Z0-9_]*") and
|
||||
@@ -458,9 +506,15 @@ module CsvValidation {
|
||||
or
|
||||
summaryModel(_, _, _, _, _, _, input, _, _) and pred = "summary"
|
||||
|
|
||||
specSplit(input, part, _) and
|
||||
not part.regexpMatch("|Argument|ReturnValue") and
|
||||
not parseArg(part, _) and
|
||||
(
|
||||
invalidSpecComponent(input, part) and
|
||||
not part = "" and
|
||||
not (part = "Argument" and pred = "sink") and
|
||||
not parseArg(part, _)
|
||||
or
|
||||
specSplit(input, part, _) and
|
||||
parseParam(part, _)
|
||||
) and
|
||||
msg = "Unrecognized input specification \"" + part + "\" in " + pred + " model."
|
||||
)
|
||||
or
|
||||
@@ -469,10 +523,9 @@ module CsvValidation {
|
||||
or
|
||||
summaryModel(_, _, _, _, _, _, _, output, _) and pred = "summary"
|
||||
|
|
||||
specSplit(output, part, _) and
|
||||
not part.regexpMatch("|Argument|Parameter|ReturnValue") and
|
||||
not parseArg(part, _) and
|
||||
not parseParam(part, _) and
|
||||
invalidSpecComponent(output, part) and
|
||||
not part = "" and
|
||||
not (part = ["Argument", "Parameter"] and pred = "source") and
|
||||
msg = "Unrecognized output specification \"" + part + "\" in " + pred + " model."
|
||||
)
|
||||
or
|
||||
@@ -519,7 +572,7 @@ private RefType interpretType(string namespace, string type, boolean subtypes) {
|
||||
private string paramsStringPart(Callable c, int i) {
|
||||
i = -1 and result = "("
|
||||
or
|
||||
exists(int n, string p | c.getParameterType(n).toString() = p |
|
||||
exists(int n, string p | c.getParameterType(n).getErasure().toString() = p |
|
||||
i = 2 * n and result = p
|
||||
or
|
||||
i = 2 * n - 1 and result = "," and n != 0
|
||||
@@ -553,7 +606,8 @@ private Element interpretElement0(
|
||||
)
|
||||
}
|
||||
|
||||
private Element interpretElement(
|
||||
/** Gets the source/sink/summary element corresponding to the supplied parameters. */
|
||||
Element interpretElement(
|
||||
string namespace, string type, boolean subtypes, string name, string signature, string ext
|
||||
) {
|
||||
elementSpec(namespace, type, subtypes, name, signature, ext) and
|
||||
@@ -564,212 +618,25 @@ private Element interpretElement(
|
||||
)
|
||||
}
|
||||
|
||||
private predicate sourceElement(Element e, string output, string kind) {
|
||||
exists(
|
||||
string namespace, string type, boolean subtypes, string name, string signature, string ext
|
||||
|
|
||||
sourceModel(namespace, type, subtypes, name, signature, ext, output, kind) and
|
||||
e = interpretElement(namespace, type, subtypes, name, signature, ext)
|
||||
)
|
||||
cached
|
||||
private module Cached {
|
||||
/**
|
||||
* Holds if `node` is specified as a source with the given kind in a CSV flow
|
||||
* model.
|
||||
*/
|
||||
cached
|
||||
predicate sourceNode(Node node, string kind) {
|
||||
exists(InterpretNode n | isSourceNode(n, kind) and n.asNode() = node)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `node` is specified as a sink with the given kind in a CSV flow
|
||||
* model.
|
||||
*/
|
||||
cached
|
||||
predicate sinkNode(Node node, string kind) {
|
||||
exists(InterpretNode n | isSinkNode(n, kind) and n.asNode() = node)
|
||||
}
|
||||
}
|
||||
|
||||
private predicate sinkElement(Element e, string input, string kind) {
|
||||
exists(
|
||||
string namespace, string type, boolean subtypes, string name, string signature, string ext
|
||||
|
|
||||
sinkModel(namespace, type, subtypes, name, signature, ext, input, kind) and
|
||||
e = interpretElement(namespace, type, subtypes, name, signature, ext)
|
||||
)
|
||||
}
|
||||
|
||||
private predicate summaryElement(Element e, string input, string output, string kind) {
|
||||
exists(
|
||||
string namespace, string type, boolean subtypes, string name, string signature, string ext
|
||||
|
|
||||
summaryModel(namespace, type, subtypes, name, signature, ext, input, output, kind) and
|
||||
e = interpretElement(namespace, type, subtypes, name, signature, ext)
|
||||
)
|
||||
}
|
||||
|
||||
private string inOutSpec() {
|
||||
sourceModel(_, _, _, _, _, _, result, _) or
|
||||
sinkModel(_, _, _, _, _, _, result, _) or
|
||||
summaryModel(_, _, _, _, _, _, result, _, _) or
|
||||
summaryModel(_, _, _, _, _, _, _, result, _)
|
||||
}
|
||||
|
||||
private predicate specSplit(string s, string c, int n) {
|
||||
inOutSpec() = s and s.splitAt(" of ", n) = c
|
||||
}
|
||||
|
||||
private predicate len(string s, int len) { len = 1 + max(int n | specSplit(s, _, n)) }
|
||||
|
||||
private string getLast(string s) {
|
||||
exists(int len |
|
||||
len(s, len) and
|
||||
specSplit(s, result, len - 1)
|
||||
)
|
||||
}
|
||||
|
||||
private predicate parseParam(string c, int n) {
|
||||
specSplit(_, c, _) and
|
||||
(
|
||||
c.regexpCapture("Parameter\\[([-0-9]+)\\]", 1).toInt() = n
|
||||
or
|
||||
exists(int n1, int n2 |
|
||||
c.regexpCapture("Parameter\\[([-0-9]+)\\.\\.([0-9]+)\\]", 1).toInt() = n1 and
|
||||
c.regexpCapture("Parameter\\[([-0-9]+)\\.\\.([0-9]+)\\]", 2).toInt() = n2 and
|
||||
n = [n1 .. n2]
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
private predicate parseArg(string c, int n) {
|
||||
specSplit(_, c, _) and
|
||||
(
|
||||
c.regexpCapture("Argument\\[([-0-9]+)\\]", 1).toInt() = n
|
||||
or
|
||||
exists(int n1, int n2 |
|
||||
c.regexpCapture("Argument\\[([-0-9]+)\\.\\.([0-9]+)\\]", 1).toInt() = n1 and
|
||||
c.regexpCapture("Argument\\[([-0-9]+)\\.\\.([0-9]+)\\]", 2).toInt() = n2 and
|
||||
n = [n1 .. n2]
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
private predicate inputNeedsReference(string c) {
|
||||
c = "Argument" or
|
||||
parseArg(c, _)
|
||||
}
|
||||
|
||||
private predicate outputNeedsReference(string c) {
|
||||
c = "Argument" or
|
||||
parseArg(c, _) or
|
||||
c = "ReturnValue"
|
||||
}
|
||||
|
||||
private predicate sourceElementRef(Top ref, string output, string kind) {
|
||||
exists(Element e |
|
||||
sourceElement(e, output, kind) and
|
||||
if outputNeedsReference(getLast(output))
|
||||
then ref.(Call).getCallee().getSourceDeclaration() = e
|
||||
else ref = e
|
||||
)
|
||||
}
|
||||
|
||||
private predicate sinkElementRef(Top ref, string input, string kind) {
|
||||
exists(Element e |
|
||||
sinkElement(e, input, kind) and
|
||||
if inputNeedsReference(getLast(input))
|
||||
then ref.(Call).getCallee().getSourceDeclaration() = e
|
||||
else ref = e
|
||||
)
|
||||
}
|
||||
|
||||
private predicate summaryElementRef(Top ref, string input, string output, string kind) {
|
||||
exists(Element e |
|
||||
summaryElement(e, input, output, kind) and
|
||||
if inputNeedsReference(getLast(input))
|
||||
then ref.(Call).getCallee().getSourceDeclaration() = e
|
||||
else ref = e
|
||||
)
|
||||
}
|
||||
|
||||
private newtype TAstOrNode =
|
||||
TAst(Top t) or
|
||||
TNode(Node n)
|
||||
|
||||
private predicate interpretOutput(string output, int idx, Top ref, TAstOrNode node) {
|
||||
(
|
||||
sourceElementRef(ref, output, _) or
|
||||
summaryElementRef(ref, _, output, _)
|
||||
) and
|
||||
len(output, idx) and
|
||||
node = TAst(ref)
|
||||
or
|
||||
exists(Top mid, string c, Node n |
|
||||
interpretOutput(output, idx + 1, ref, TAst(mid)) and
|
||||
specSplit(output, c, idx) and
|
||||
node = TNode(n)
|
||||
|
|
||||
exists(int pos | n.(PostUpdateNode).getPreUpdateNode().(ArgumentNode).argumentOf(mid, pos) |
|
||||
c = "Argument" or parseArg(c, pos)
|
||||
)
|
||||
or
|
||||
exists(int pos | n.(ParameterNode).isParameterOf(mid, pos) |
|
||||
c = "Parameter" or parseParam(c, pos)
|
||||
)
|
||||
or
|
||||
(c = "Parameter" or c = "") and
|
||||
n.asParameter() = mid
|
||||
or
|
||||
c = "ReturnValue" and
|
||||
n.asExpr().(Call) = mid
|
||||
or
|
||||
c = "" and
|
||||
n.asExpr().(FieldRead).getField() = mid
|
||||
)
|
||||
}
|
||||
|
||||
private predicate interpretInput(string input, int idx, Top ref, TAstOrNode node) {
|
||||
(
|
||||
sinkElementRef(ref, input, _) or
|
||||
summaryElementRef(ref, input, _, _)
|
||||
) and
|
||||
len(input, idx) and
|
||||
node = TAst(ref)
|
||||
or
|
||||
exists(Top mid, string c, Node n |
|
||||
interpretInput(input, idx + 1, ref, TAst(mid)) and
|
||||
specSplit(input, c, idx) and
|
||||
node = TNode(n)
|
||||
|
|
||||
exists(int pos | n.(ArgumentNode).argumentOf(mid, pos) | c = "Argument" or parseArg(c, pos))
|
||||
or
|
||||
exists(ReturnStmt ret |
|
||||
c = "ReturnValue" and
|
||||
n.asExpr() = ret.getResult() and
|
||||
mid = ret.getEnclosingCallable()
|
||||
)
|
||||
or
|
||||
exists(FieldWrite fw |
|
||||
c = "" and
|
||||
fw.getField() = mid and
|
||||
n.asExpr() = fw.getRHS()
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `node` is specified as a source with the given kind in a CSV flow
|
||||
* model.
|
||||
*/
|
||||
predicate sourceNode(Node node, string kind) {
|
||||
exists(Top ref, string output |
|
||||
sourceElementRef(ref, output, kind) and
|
||||
interpretOutput(output, 0, ref, TNode(node))
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `node` is specified as a sink with the given kind in a CSV flow
|
||||
* model.
|
||||
*/
|
||||
predicate sinkNode(Node node, string kind) {
|
||||
exists(Top ref, string input |
|
||||
sinkElementRef(ref, input, kind) and
|
||||
interpretInput(input, 0, ref, TNode(node))
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `node1` to `node2` is specified as a flow step with the given kind
|
||||
* in a CSV flow model.
|
||||
*/
|
||||
predicate summaryStep(Node node1, Node node2, string kind) {
|
||||
exists(Top ref, string input, string output |
|
||||
summaryElementRef(ref, input, output, kind) and
|
||||
interpretInput(input, 0, ref, TNode(node1)) and
|
||||
interpretOutput(output, 0, ref, TNode(node2))
|
||||
)
|
||||
}
|
||||
import Cached
|
||||
|
||||
51
java/ql/src/semmle/code/java/dataflow/FlowSummary.qll
Normal file
51
java/ql/src/semmle/code/java/dataflow/FlowSummary.qll
Normal file
@@ -0,0 +1,51 @@
|
||||
/**
|
||||
* Provides classes and predicates for definining flow summaries.
|
||||
*/
|
||||
|
||||
import java
|
||||
private import internal.FlowSummaryImpl as Impl
|
||||
private import internal.DataFlowDispatch
|
||||
private import internal.DataFlowPrivate
|
||||
|
||||
// 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. */
|
||||
module SummaryComponent {
|
||||
import Impl::Public::SummaryComponent
|
||||
|
||||
/** Gets a summary component that represents a qualifier. */
|
||||
SummaryComponent qualifier() { result = argument(-1) }
|
||||
|
||||
/** Gets a summary component for field `f`. */
|
||||
SummaryComponent field(Field f) { result = content(any(FieldContent c | c.getField() = f)) }
|
||||
|
||||
/** Gets a summary component that represents the return value of a call. */
|
||||
SummaryComponent return() { result = return(_) }
|
||||
}
|
||||
|
||||
class SummaryComponentStack = Impl::Public::SummaryComponentStack;
|
||||
|
||||
/** Provides predicates for constructing stacks of summary components. */
|
||||
module SummaryComponentStack {
|
||||
import Impl::Public::SummaryComponentStack
|
||||
|
||||
/** Gets a singleton stack representing a qualifier. */
|
||||
SummaryComponentStack qualifier() { result = singleton(SummaryComponent::qualifier()) }
|
||||
|
||||
/** Gets a stack representing a field `f` of `object`. */
|
||||
SummaryComponentStack fieldOf(Field f, SummaryComponentStack object) {
|
||||
result = push(SummaryComponent::field(f), object)
|
||||
}
|
||||
|
||||
/** Gets a singleton stack representing a (normal) return. */
|
||||
SummaryComponentStack return() { result = singleton(SummaryComponent::return()) }
|
||||
}
|
||||
|
||||
class SummarizedCallable = Impl::Public::SummarizedCallable;
|
||||
|
||||
class RequiredSummaryComponentStack = Impl::Public::RequiredSummaryComponentStack;
|
||||
@@ -1,6 +1,9 @@
|
||||
import java
|
||||
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() {
|
||||
@@ -88,6 +91,286 @@ class ContainerType extends RefType {
|
||||
}
|
||||
}
|
||||
|
||||
private class ContainerFlowSummaries extends SummaryModelCsv {
|
||||
override predicate row(string row) {
|
||||
row =
|
||||
[
|
||||
"java.util;Map<>$Entry;true;getValue;;;MapValue of Argument[-1];ReturnValue;value",
|
||||
"java.util;Map<>$Entry;true;setValue;;;MapValue of Argument[-1];ReturnValue;value",
|
||||
"java.util;Map<>$Entry;true;setValue;;;Argument[0];MapValue of Argument[-1];value",
|
||||
"java.lang;Iterable;true;iterator;();;Element of Argument[-1];Element of ReturnValue;value",
|
||||
"java.lang;Iterable;true;spliterator;();;Element of Argument[-1];Element of ReturnValue;value",
|
||||
"java.util;Iterator;true;next;;;Element of Argument[-1];ReturnValue;value",
|
||||
"java.util;ListIterator;true;previous;;;Element of Argument[-1];ReturnValue;value",
|
||||
"java.util;ListIterator;true;add;(Object);;Argument[0];Element of Argument[-1];value",
|
||||
"java.util;ListIterator;true;set;(Object);;Argument[0];Element of Argument[-1];value",
|
||||
"java.util;Enumeration;true;asIterator;;;Element of Argument[-1];Element of ReturnValue;value",
|
||||
"java.util;Enumeration;true;nextElement;;;Element of Argument[-1];ReturnValue;value",
|
||||
"java.util;Map;true;computeIfAbsent;;;MapValue of Argument[-1];ReturnValue;value",
|
||||
"java.util;Map;true;computeIfAbsent;;;ReturnValue of Argument[1];ReturnValue;value",
|
||||
"java.util;Map;true;computeIfAbsent;;;ReturnValue of Argument[1];MapValue of Argument[-1];value",
|
||||
"java.util;Map;true;entrySet;;;MapValue of Argument[-1];MapValue of Element of ReturnValue;value",
|
||||
"java.util;Map;true;entrySet;;;MapKey of Argument[-1];MapKey of Element of ReturnValue;value",
|
||||
"java.util;Map;true;get;;;MapValue of Argument[-1];ReturnValue;value",
|
||||
"java.util;Map;true;getOrDefault;;;MapValue of Argument[-1];ReturnValue;value",
|
||||
"java.util;Map;true;getOrDefault;;;Argument[1];ReturnValue;value",
|
||||
"java.util;Map;true;put;;;MapValue of Argument[-1];ReturnValue;value",
|
||||
"java.util;Map;true;put;;;Argument[0];MapKey of Argument[-1];value",
|
||||
"java.util;Map;true;put;;;Argument[1];MapValue of Argument[-1];value",
|
||||
"java.util;Map;true;putIfAbsent;;;MapValue of Argument[-1];ReturnValue;value",
|
||||
"java.util;Map;true;putIfAbsent;;;Argument[0];MapKey of Argument[-1];value",
|
||||
"java.util;Map;true;putIfAbsent;;;Argument[1];MapValue of Argument[-1];value",
|
||||
"java.util;Map;true;remove;(Object);;MapValue of Argument[-1];ReturnValue;value",
|
||||
"java.util;Map;true;replace;(Object,Object);;MapValue of Argument[-1];ReturnValue;value",
|
||||
"java.util;Map;true;replace;(Object,Object);;Argument[0];MapKey of Argument[-1];value",
|
||||
"java.util;Map;true;replace;(Object,Object);;Argument[1];MapValue of Argument[-1];value",
|
||||
"java.util;Map;true;replace;(Object,Object,Object);;Argument[0];MapKey of Argument[-1];value",
|
||||
"java.util;Map;true;replace;(Object,Object,Object);;Argument[2];MapValue of Argument[-1];value",
|
||||
"java.util;Map;true;keySet;();;MapKey of Argument[-1];Element of ReturnValue;value",
|
||||
"java.util;Map;true;values;();;MapValue of Argument[-1];Element of ReturnValue;value",
|
||||
"java.util;Map;true;merge;(Object,Object,BiFunction);;Argument[1];MapValue of Argument[-1];value",
|
||||
"java.util;Map;true;putAll;(Map);;MapKey of Argument[0];MapKey of Argument[-1];value",
|
||||
"java.util;Map;true;putAll;(Map);;MapValue of Argument[0];MapValue of Argument[-1];value",
|
||||
"java.util;Collection;true;parallelStream;();;Element of Argument[-1];Element of ReturnValue;value",
|
||||
"java.util;Collection;true;stream;();;Element of Argument[-1];Element of ReturnValue;value",
|
||||
"java.util;Collection;true;toArray;;;Element of Argument[-1];ArrayElement of ReturnValue;value",
|
||||
"java.util;Collection;true;toArray;;;Element of Argument[-1];ArrayElement of Argument[0];value",
|
||||
"java.util;Collection;true;add;;;Argument[0];Element of Argument[-1];value",
|
||||
"java.util;Collection;true;addAll;;;Element of Argument[0];Element of Argument[-1];value",
|
||||
"java.util;List;true;get;(int);;Element of Argument[-1];ReturnValue;value",
|
||||
"java.util;List;true;listIterator;;;Element of Argument[-1];Element of ReturnValue;value",
|
||||
"java.util;List;true;remove;(int);;Element of Argument[-1];ReturnValue;value",
|
||||
"java.util;List;true;set;(int,Object);;Element of Argument[-1];ReturnValue;value",
|
||||
"java.util;List;true;set;(int,Object);;Argument[1];Element of Argument[-1];value",
|
||||
"java.util;List;true;subList;;;Element of Argument[-1];Element of ReturnValue;value",
|
||||
"java.util;List;true;add;(int,Object);;Argument[1];Element of Argument[-1];value",
|
||||
"java.util;List;true;addAll;(int,Collection);;Element of Argument[1];Element of Argument[-1];value",
|
||||
"java.util;Vector;true;elementAt;(int);;Element of Argument[-1];ReturnValue;value",
|
||||
"java.util;Vector;true;elements;();;Element of Argument[-1];Element of ReturnValue;value",
|
||||
"java.util;Vector;true;firstElement;();;Element of Argument[-1];ReturnValue;value",
|
||||
"java.util;Vector;true;lastElement;();;Element of Argument[-1];ReturnValue;value",
|
||||
"java.util;Vector;true;addElement;(Object);;Argument[0];Element of Argument[-1];value",
|
||||
"java.util;Vector;true;insertElementAt;(Object,int);;Argument[0];Element of Argument[-1];value",
|
||||
"java.util;Vector;true;setElementAt;(Object,int);;Argument[0];Element of Argument[-1];value",
|
||||
"java.util;Vector;true;copyInto;(Object[]);;Element of Argument[-1];ArrayElement of Argument[0];value",
|
||||
"java.util;Stack;true;peek;();;Element of Argument[-1];ReturnValue;value",
|
||||
"java.util;Stack;true;pop;();;Element of Argument[-1];ReturnValue;value",
|
||||
"java.util;Stack;true;push;(Object);;Argument[0];Element of Argument[-1];value",
|
||||
"java.util;Queue;true;element;();;Element of Argument[-1];ReturnValue;value",
|
||||
"java.util;Queue;true;peek;();;Element of Argument[-1];ReturnValue;value",
|
||||
"java.util;Queue;true;poll;();;Element of Argument[-1];ReturnValue;value",
|
||||
"java.util;Queue;true;remove;();;Element of Argument[-1];ReturnValue;value",
|
||||
"java.util;Queue;true;offer;(Object);;Argument[0];Element of Argument[-1];value",
|
||||
"java.util;Deque;true;descendingIterator;();;Element of Argument[-1];Element of ReturnValue;value",
|
||||
"java.util;Deque;true;getFirst;();;Element of Argument[-1];ReturnValue;value",
|
||||
"java.util;Deque;true;getLast;();;Element of Argument[-1];ReturnValue;value",
|
||||
"java.util;Deque;true;peekFirst;();;Element of Argument[-1];ReturnValue;value",
|
||||
"java.util;Deque;true;peekLast;();;Element of Argument[-1];ReturnValue;value",
|
||||
"java.util;Deque;true;pollFirst;();;Element of Argument[-1];ReturnValue;value",
|
||||
"java.util;Deque;true;pollLast;();;Element of Argument[-1];ReturnValue;value",
|
||||
"java.util;Deque;true;pop;();;Element of Argument[-1];ReturnValue;value",
|
||||
"java.util;Deque;true;removeFirst;();;Element of Argument[-1];ReturnValue;value",
|
||||
"java.util;Deque;true;removeLast;();;Element of Argument[-1];ReturnValue;value",
|
||||
"java.util;Deque;true;push;(Object);;Argument[0];Element of Argument[-1];value",
|
||||
"java.util;Deque;true;offerLast;(Object);;Argument[0];Element of Argument[-1];value",
|
||||
"java.util;Deque;true;offerFirst;(Object);;Argument[0];Element of Argument[-1];value",
|
||||
"java.util;Deque;true;addLast;(Object);;Argument[0];Element of Argument[-1];value",
|
||||
"java.util;Deque;true;addFirst;(Object);;Argument[0];Element of Argument[-1];value",
|
||||
"java.util.concurrent;BlockingDeque;true;pollFirst;(long,TimeUnit);;Element of Argument[-1];ReturnValue;value",
|
||||
"java.util.concurrent;BlockingDeque;true;pollLast;(long,TimeUnit);;Element of Argument[-1];ReturnValue;value",
|
||||
"java.util.concurrent;BlockingDeque;true;takeFirst;();;Element of Argument[-1];ReturnValue;value",
|
||||
"java.util.concurrent;BlockingDeque;true;takeLast;();;Element of Argument[-1];ReturnValue;value",
|
||||
"java.util.concurrent;BlockingQueue;true;poll;(long,TimeUnit);;Element of Argument[-1];ReturnValue;value",
|
||||
"java.util.concurrent;BlockingQueue;true;take;();;Element of Argument[-1];ReturnValue;value",
|
||||
"java.util.concurrent;BlockingQueue;true;offer;(Object,long,TimeUnit);;Argument[0];Element of Argument[-1];value",
|
||||
"java.util.concurrent;BlockingQueue;true;put;(Object);;Argument[0];Element of Argument[-1];value",
|
||||
"java.util.concurrent;BlockingDeque;true;offerLast;(Object,long,TimeUnit);;Argument[0];Element of Argument[-1];value",
|
||||
"java.util.concurrent;BlockingDeque;true;offerFirst;(Object,long,TimeUnit);;Argument[0];Element of Argument[-1];value",
|
||||
"java.util.concurrent;BlockingDeque;true;putLast;(Object);;Argument[0];Element of Argument[-1];value",
|
||||
"java.util.concurrent;BlockingDeque;true;putFirst;(Object);;Argument[0];Element of Argument[-1];value",
|
||||
"java.util.concurrent;BlockingQueue;true;drainTo;(Collection,int);;Element of Argument[-1];Element of Argument[0];value",
|
||||
"java.util.concurrent;BlockingQueue;true;drainTo;(Collection);;Element of Argument[-1];Element of Argument[0];value",
|
||||
"java.util.concurrent;ConcurrentHashMap;true;elements;();;MapValue of Argument[-1];Element of ReturnValue;value",
|
||||
"java.util;Dictionary;true;elements;();;MapValue of Argument[-1];Element of ReturnValue;value",
|
||||
"java.util;Dictionary;true;get;(Object);;MapValue of Argument[-1];ReturnValue;value",
|
||||
"java.util;Dictionary;true;put;(Object,Object);;MapValue of Argument[-1];ReturnValue;value",
|
||||
"java.util;Dictionary;true;put;(Object,Object);;Argument[0];MapKey of Argument[-1];value",
|
||||
"java.util;Dictionary;true;put;(Object,Object);;Argument[1];MapValue of Argument[-1];value",
|
||||
"java.util;Dictionary;true;remove;(Object);;MapValue of Argument[-1];ReturnValue;value",
|
||||
"java.util;NavigableMap;true;ceilingEntry;(Object);;MapKey of Argument[-1];MapKey of ReturnValue;value",
|
||||
"java.util;NavigableMap;true;ceilingEntry;(Object);;MapValue of Argument[-1];MapValue of ReturnValue;value",
|
||||
"java.util;NavigableMap;true;descendingMap;();;MapKey of Argument[-1];MapKey of ReturnValue;value",
|
||||
"java.util;NavigableMap;true;descendingMap;();;MapValue of Argument[-1];MapValue of ReturnValue;value",
|
||||
"java.util;NavigableMap;true;firstEntry;();;MapKey of Argument[-1];MapKey of ReturnValue;value",
|
||||
"java.util;NavigableMap;true;firstEntry;();;MapValue of Argument[-1];MapValue of ReturnValue;value",
|
||||
"java.util;NavigableMap;true;floorEntry;(Object);;MapKey of Argument[-1];MapKey of ReturnValue;value",
|
||||
"java.util;NavigableMap;true;floorEntry;(Object);;MapValue of Argument[-1];MapValue of ReturnValue;value",
|
||||
"java.util;NavigableMap;true;headMap;(Object,boolean);;MapKey of Argument[-1];MapKey of ReturnValue;value",
|
||||
"java.util;NavigableMap;true;headMap;(Object,boolean);;MapValue of Argument[-1];MapValue of ReturnValue;value",
|
||||
"java.util;NavigableMap;true;higherEntry;(Object);;MapKey of Argument[-1];MapKey of ReturnValue;value",
|
||||
"java.util;NavigableMap;true;higherEntry;(Object);;MapValue of Argument[-1];MapValue of ReturnValue;value",
|
||||
"java.util;NavigableMap;true;lastEntry;();;MapKey of Argument[-1];MapKey of ReturnValue;value",
|
||||
"java.util;NavigableMap;true;lastEntry;();;MapValue of Argument[-1];MapValue of ReturnValue;value",
|
||||
"java.util;NavigableMap;true;lowerEntry;(Object);;MapKey of Argument[-1];MapKey of ReturnValue;value",
|
||||
"java.util;NavigableMap;true;lowerEntry;(Object);;MapValue of Argument[-1];MapValue of ReturnValue;value",
|
||||
"java.util;NavigableMap;true;pollFirstEntry;();;MapKey of Argument[-1];MapKey of ReturnValue;value",
|
||||
"java.util;NavigableMap;true;pollFirstEntry;();;MapValue of Argument[-1];MapValue of ReturnValue;value",
|
||||
"java.util;NavigableMap;true;pollLastEntry;();;MapKey of Argument[-1];MapKey of ReturnValue;value",
|
||||
"java.util;NavigableMap;true;pollLastEntry;();;MapValue of Argument[-1];MapValue of ReturnValue;value",
|
||||
"java.util;NavigableMap;true;subMap;(Object,boolean,Object,boolean);;MapKey of Argument[-1];MapKey of ReturnValue;value",
|
||||
"java.util;NavigableMap;true;subMap;(Object,boolean,Object,boolean);;MapValue of Argument[-1];MapValue of ReturnValue;value",
|
||||
"java.util;NavigableMap;true;tailMap;(Object,boolean);;MapKey of Argument[-1];MapKey of ReturnValue;value",
|
||||
"java.util;NavigableMap;true;tailMap;(Object,boolean);;MapValue of Argument[-1];MapValue of ReturnValue;value",
|
||||
"java.util;NavigableSet;true;ceiling;(Object);;Element of Argument[-1];ReturnValue;value",
|
||||
"java.util;NavigableSet;true;descendingIterator;();;Element of Argument[-1];Element of ReturnValue;value",
|
||||
"java.util;NavigableSet;true;descendingSet;();;Element of Argument[-1];Element of ReturnValue;value",
|
||||
"java.util;NavigableSet;true;floor;(Object);;Element of Argument[-1];ReturnValue;value",
|
||||
"java.util;NavigableSet;true;headSet;(Object,boolean);;Element of Argument[-1];Element of ReturnValue;value",
|
||||
"java.util;NavigableSet;true;higher;(Object);;Element of Argument[-1];ReturnValue;value",
|
||||
"java.util;NavigableSet;true;lower;(Object);;Element of Argument[-1];ReturnValue;value",
|
||||
"java.util;NavigableSet;true;pollFirst;();;Element of Argument[-1];ReturnValue;value",
|
||||
"java.util;NavigableSet;true;pollLast;();;Element of Argument[-1];ReturnValue;value",
|
||||
"java.util;NavigableSet;true;subSet;(Object,boolean,Object,boolean);;Element of Argument[-1];Element of ReturnValue;value",
|
||||
"java.util;NavigableSet;true;tailSet;(Object,boolean);;Element of Argument[-1];Element of ReturnValue;value",
|
||||
"java.util;Scanner;true;next;(Pattern);;Argument[-1];ReturnValue;taint",
|
||||
"java.util;Scanner;true;next;(String);;Argument[-1];ReturnValue;taint",
|
||||
"java.util;SortedMap;true;headMap;(Object);;MapKey of Argument[-1];MapKey of ReturnValue;value",
|
||||
"java.util;SortedMap;true;headMap;(Object);;MapValue of Argument[-1];MapValue of ReturnValue;value",
|
||||
"java.util;SortedMap;true;subMap;(Object,Object);;MapKey of Argument[-1];MapKey of ReturnValue;value",
|
||||
"java.util;SortedMap;true;subMap;(Object,Object);;MapValue of Argument[-1];MapValue of ReturnValue;value",
|
||||
"java.util;SortedMap;true;tailMap;(Object);;MapKey of Argument[-1];MapKey of ReturnValue;value",
|
||||
"java.util;SortedMap;true;tailMap;(Object);;MapValue of Argument[-1];MapValue of ReturnValue;value",
|
||||
"java.util;SortedSet;true;first;();;Element of Argument[-1];ReturnValue;value",
|
||||
"java.util;SortedSet;true;headSet;(Object);;Element of Argument[-1];Element of ReturnValue;value",
|
||||
"java.util;SortedSet;true;last;();;Element of Argument[-1];ReturnValue;value",
|
||||
"java.util;SortedSet;true;subSet;(Object,Object);;Element of Argument[-1];Element of ReturnValue;value",
|
||||
"java.util;SortedSet;true;tailSet;(Object);;Element of Argument[-1];Element of ReturnValue;value",
|
||||
"java.util.concurrent;TransferQueue;true;tryTransfer;(Object,long,TimeUnit);;Argument[0];Element of Argument[-1];value",
|
||||
"java.util.concurrent;TransferQueue;true;transfer;(Object);;Argument[0];Element of Argument[-1];value",
|
||||
"java.util.concurrent;TransferQueue;true;tryTransfer;(Object);;Argument[0];Element of Argument[-1];value",
|
||||
"java.util;List;false;copyOf;(Collection);;Element of Argument[0];Element of ReturnValue;value",
|
||||
"java.util;List;false;of;(Object[]);;ArrayElement of Argument[0];Element of ReturnValue;value",
|
||||
"java.util;List;false;of;(Object);;Argument[0];Element of ReturnValue;value",
|
||||
"java.util;List;false;of;(Object,Object);;Argument[0..1];Element of ReturnValue;value",
|
||||
"java.util;List;false;of;(Object,Object,Object);;Argument[0..2];Element of ReturnValue;value",
|
||||
"java.util;List;false;of;(Object,Object,Object,Object);;Argument[0..3];Element of ReturnValue;value",
|
||||
"java.util;List;false;of;(Object,Object,Object,Object,Object);;Argument[0..4];Element of ReturnValue;value",
|
||||
"java.util;List;false;of;(Object,Object,Object,Object,Object,Object);;Argument[0..5];Element of ReturnValue;value",
|
||||
"java.util;List;false;of;(Object,Object,Object,Object,Object,Object,Object);;Argument[0..6];Element of ReturnValue;value",
|
||||
"java.util;List;false;of;(Object,Object,Object,Object,Object,Object,Object,Object);;Argument[0..7];Element of ReturnValue;value",
|
||||
"java.util;List;false;of;(Object,Object,Object,Object,Object,Object,Object,Object,Object);;Argument[0..8];Element of ReturnValue;value",
|
||||
"java.util;List;false;of;(Object,Object,Object,Object,Object,Object,Object,Object,Object,Object);;Argument[0..9];Element of ReturnValue;value",
|
||||
"java.util;Map;false;copyOf;(Map);;MapKey of Argument[0];MapKey of ReturnValue;value",
|
||||
"java.util;Map;false;copyOf;(Map);;MapValue of Argument[0];MapValue of ReturnValue;value",
|
||||
"java.util;Map;false;entry;(Object,Object);;Argument[0];MapKey of ReturnValue;value",
|
||||
"java.util;Map;false;entry;(Object,Object);;Argument[1];MapValue of ReturnValue;value",
|
||||
"java.util;Map;false;of;;;Argument[0];MapKey of ReturnValue;value",
|
||||
"java.util;Map;false;of;;;Argument[1];MapValue of ReturnValue;value",
|
||||
"java.util;Map;false;of;;;Argument[2];MapKey of ReturnValue;value",
|
||||
"java.util;Map;false;of;;;Argument[3];MapValue of ReturnValue;value",
|
||||
"java.util;Map;false;of;;;Argument[4];MapKey of ReturnValue;value",
|
||||
"java.util;Map;false;of;;;Argument[5];MapValue of ReturnValue;value",
|
||||
"java.util;Map;false;of;;;Argument[6];MapKey of ReturnValue;value",
|
||||
"java.util;Map;false;of;;;Argument[7];MapValue of ReturnValue;value",
|
||||
"java.util;Map;false;of;;;Argument[8];MapKey of ReturnValue;value",
|
||||
"java.util;Map;false;of;;;Argument[9];MapValue of ReturnValue;value",
|
||||
"java.util;Map;false;of;;;Argument[10];MapKey of ReturnValue;value",
|
||||
"java.util;Map;false;of;;;Argument[11];MapValue of ReturnValue;value",
|
||||
"java.util;Map;false;of;;;Argument[12];MapKey of ReturnValue;value",
|
||||
"java.util;Map;false;of;;;Argument[13];MapValue of ReturnValue;value",
|
||||
"java.util;Map;false;of;;;Argument[14];MapKey of ReturnValue;value",
|
||||
"java.util;Map;false;of;;;Argument[15];MapValue of ReturnValue;value",
|
||||
"java.util;Map;false;of;;;Argument[16];MapKey of ReturnValue;value",
|
||||
"java.util;Map;false;of;;;Argument[17];MapValue of ReturnValue;value",
|
||||
"java.util;Map;false;of;;;Argument[18];MapKey of ReturnValue;value",
|
||||
"java.util;Map;false;of;;;Argument[19];MapValue of ReturnValue;value",
|
||||
"java.util;Map;false;ofEntries;;;MapKey of ArrayElement of Argument[0];MapKey of ReturnValue;value",
|
||||
"java.util;Map;false;ofEntries;;;MapValue of ArrayElement of Argument[0];MapValue of ReturnValue;value",
|
||||
"java.util;Set;false;copyOf;(Collection);;Element of Argument[0];Element of ReturnValue;value",
|
||||
"java.util;Set;false;of;(Object[]);;ArrayElement of Argument[0];Element of ReturnValue;value",
|
||||
"java.util;Set;false;of;(Object);;Argument[0];Element of ReturnValue;value",
|
||||
"java.util;Set;false;of;(Object,Object);;Argument[0..1];Element of ReturnValue;value",
|
||||
"java.util;Set;false;of;(Object,Object,Object);;Argument[0..2];Element of ReturnValue;value",
|
||||
"java.util;Set;false;of;(Object,Object,Object,Object);;Argument[0..3];Element of ReturnValue;value",
|
||||
"java.util;Set;false;of;(Object,Object,Object,Object,Object);;Argument[0..4];Element of ReturnValue;value",
|
||||
"java.util;Set;false;of;(Object,Object,Object,Object,Object,Object);;Argument[0..5];Element of ReturnValue;value",
|
||||
"java.util;Set;false;of;(Object,Object,Object,Object,Object,Object,Object);;Argument[0..6];Element of ReturnValue;value",
|
||||
"java.util;Set;false;of;(Object,Object,Object,Object,Object,Object,Object,Object);;Argument[0..7];Element of ReturnValue;value",
|
||||
"java.util;Set;false;of;(Object,Object,Object,Object,Object,Object,Object,Object,Object);;Argument[0..8];Element of ReturnValue;value",
|
||||
"java.util;Set;false;of;(Object,Object,Object,Object,Object,Object,Object,Object,Object,Object);;Argument[0..9];Element of ReturnValue;value",
|
||||
"java.util;Arrays;false;stream;;;ArrayElement of Argument[0];Element of ReturnValue;value",
|
||||
"java.util;Arrays;false;spliterator;;;ArrayElement of Argument[0];Element of ReturnValue;value",
|
||||
"java.util;Arrays;false;copyOfRange;;;ArrayElement of Argument[0];ArrayElement of ReturnValue;value",
|
||||
"java.util;Arrays;false;copyOf;;;ArrayElement of Argument[0];ArrayElement of ReturnValue;value",
|
||||
"java.util;Collections;false;list;(Enumeration);;Element of Argument[0];Element of ReturnValue;value",
|
||||
"java.util;Collections;false;enumeration;(Collection);;Element of Argument[0];Element of ReturnValue;value",
|
||||
"java.util;Collections;false;nCopies;(int,Object);;Argument[1];Element of ReturnValue;value",
|
||||
"java.util;Collections;false;singletonMap;(Object,Object);;Argument[0];MapKey of ReturnValue;value",
|
||||
"java.util;Collections;false;singletonMap;(Object,Object);;Argument[1];MapValue of ReturnValue;value",
|
||||
"java.util;Collections;false;singletonList;(Object);;Argument[0];Element of ReturnValue;value",
|
||||
"java.util;Collections;false;singleton;(Object);;Argument[0];Element of ReturnValue;value",
|
||||
"java.util;Collections;false;checkedNavigableMap;(NavigableMap,Class,Class);;MapKey of Argument[0];MapKey of ReturnValue;value",
|
||||
"java.util;Collections;false;checkedNavigableMap;(NavigableMap,Class,Class);;MapValue of Argument[0];MapValue of ReturnValue;value",
|
||||
"java.util;Collections;false;checkedSortedMap;(SortedMap,Class,Class);;MapKey of Argument[0];MapKey of ReturnValue;value",
|
||||
"java.util;Collections;false;checkedSortedMap;(SortedMap,Class,Class);;MapValue of Argument[0];MapValue of ReturnValue;value",
|
||||
"java.util;Collections;false;checkedMap;(Map,Class,Class);;MapKey of Argument[0];MapKey of ReturnValue;value",
|
||||
"java.util;Collections;false;checkedMap;(Map,Class,Class);;MapValue of Argument[0];MapValue of ReturnValue;value",
|
||||
"java.util;Collections;false;checkedList;(List,Class);;Element of Argument[0];Element of ReturnValue;value",
|
||||
"java.util;Collections;false;checkedNavigableSet;(NavigableSet,Class);;Element of Argument[0];Element of ReturnValue;value",
|
||||
"java.util;Collections;false;checkedSortedSet;(SortedSet,Class);;Element of Argument[0];Element of ReturnValue;value",
|
||||
"java.util;Collections;false;checkedSet;(Set,Class);;Element of Argument[0];Element of ReturnValue;value",
|
||||
"java.util;Collections;false;checkedCollection;(Collection,Class);;Element of Argument[0];Element of ReturnValue;value",
|
||||
"java.util;Collections;false;synchronizedNavigableMap;(NavigableMap);;MapKey of Argument[0];MapKey of ReturnValue;value",
|
||||
"java.util;Collections;false;synchronizedNavigableMap;(NavigableMap);;MapValue of Argument[0];MapValue of ReturnValue;value",
|
||||
"java.util;Collections;false;synchronizedSortedMap;(SortedMap);;MapKey of Argument[0];MapKey of ReturnValue;value",
|
||||
"java.util;Collections;false;synchronizedSortedMap;(SortedMap);;MapValue of Argument[0];MapValue of ReturnValue;value",
|
||||
"java.util;Collections;false;synchronizedMap;(Map);;MapKey of Argument[0];MapKey of ReturnValue;value",
|
||||
"java.util;Collections;false;synchronizedMap;(Map);;MapValue of Argument[0];MapValue of ReturnValue;value",
|
||||
"java.util;Collections;false;synchronizedList;(List);;Element of Argument[0];Element of ReturnValue;value",
|
||||
"java.util;Collections;false;synchronizedNavigableSet;(NavigableSet);;Element of Argument[0];Element of ReturnValue;value",
|
||||
"java.util;Collections;false;synchronizedSortedSet;(SortedSet);;Element of Argument[0];Element of ReturnValue;value",
|
||||
"java.util;Collections;false;synchronizedSet;(Set);;Element of Argument[0];Element of ReturnValue;value",
|
||||
"java.util;Collections;false;synchronizedCollection;(Collection);;Element of Argument[0];Element of ReturnValue;value",
|
||||
"java.util;Collections;false;unmodifiableNavigableMap;(NavigableMap);;MapKey of Argument[0];MapKey of ReturnValue;value",
|
||||
"java.util;Collections;false;unmodifiableNavigableMap;(NavigableMap);;MapValue of Argument[0];MapValue of ReturnValue;value",
|
||||
"java.util;Collections;false;unmodifiableSortedMap;(SortedMap);;MapKey of Argument[0];MapKey of ReturnValue;value",
|
||||
"java.util;Collections;false;unmodifiableSortedMap;(SortedMap);;MapValue of Argument[0];MapValue of ReturnValue;value",
|
||||
"java.util;Collections;false;unmodifiableMap;(Map);;MapKey of Argument[0];MapKey of ReturnValue;value",
|
||||
"java.util;Collections;false;unmodifiableMap;(Map);;MapValue of Argument[0];MapValue of ReturnValue;value",
|
||||
"java.util;Collections;false;unmodifiableList;(List);;Element of Argument[0];Element of ReturnValue;value",
|
||||
"java.util;Collections;false;unmodifiableNavigableSet;(NavigableSet);;Element of Argument[0];Element of ReturnValue;value",
|
||||
"java.util;Collections;false;unmodifiableSortedSet;(SortedSet);;Element of Argument[0];Element of ReturnValue;value",
|
||||
"java.util;Collections;false;unmodifiableSet;(Set);;Element of Argument[0];Element of ReturnValue;value",
|
||||
"java.util;Collections;false;unmodifiableCollection;(Collection);;Element of Argument[0];Element of ReturnValue;value",
|
||||
"java.util;Collections;false;max;;;Element of Argument[0];ReturnValue;value",
|
||||
"java.util;Collections;false;min;;;Element of Argument[0];ReturnValue;value",
|
||||
"java.util;Arrays;false;fill;(Object[],int,int,Object);;Argument[3];ArrayElement of Argument[0];value",
|
||||
"java.util;Arrays;false;fill;(Object[],Object);;Argument[1];ArrayElement of Argument[0];value",
|
||||
"java.util;Arrays;false;fill;(float[],int,int,float);;Argument[3];ArrayElement of Argument[0];value",
|
||||
"java.util;Arrays;false;fill;(float[],float);;Argument[1];ArrayElement of Argument[0];value",
|
||||
"java.util;Arrays;false;fill;(double[],int,int,double);;Argument[3];ArrayElement of Argument[0];value",
|
||||
"java.util;Arrays;false;fill;(double[],double);;Argument[1];ArrayElement of Argument[0];value",
|
||||
"java.util;Arrays;false;fill;(boolean[],int,int,boolean);;Argument[3];ArrayElement of Argument[0];value",
|
||||
"java.util;Arrays;false;fill;(boolean[],boolean);;Argument[1];ArrayElement of Argument[0];value",
|
||||
"java.util;Arrays;false;fill;(byte[],int,int,byte);;Argument[3];ArrayElement of Argument[0];value",
|
||||
"java.util;Arrays;false;fill;(byte[],byte);;Argument[1];ArrayElement of Argument[0];value",
|
||||
"java.util;Arrays;false;fill;(char[],int,int,char);;Argument[3];ArrayElement of Argument[0];value",
|
||||
"java.util;Arrays;false;fill;(char[],char);;Argument[1];ArrayElement of Argument[0];value",
|
||||
"java.util;Arrays;false;fill;(short[],int,int,short);;Argument[3];ArrayElement of Argument[0];value",
|
||||
"java.util;Arrays;false;fill;(short[],short);;Argument[1];ArrayElement of Argument[0];value",
|
||||
"java.util;Arrays;false;fill;(int[],int,int,int);;Argument[3];ArrayElement of Argument[0];value",
|
||||
"java.util;Arrays;false;fill;(int[],int);;Argument[1];ArrayElement of Argument[0];value",
|
||||
"java.util;Arrays;false;fill;(long[],int,int,long);;Argument[3];ArrayElement of Argument[0];value",
|
||||
"java.util;Arrays;false;fill;(long[],long);;Argument[1];ArrayElement of Argument[0];value",
|
||||
"java.util;Collections;false;replaceAll;(List,Object,Object);;Argument[2];Element of Argument[0];value",
|
||||
"java.util;Collections;false;copy;(List,List);;Element of Argument[1];Element of Argument[0];value",
|
||||
"java.util;Collections;false;fill;(List,Object);;Argument[1];Element of Argument[0];value",
|
||||
"java.util;Arrays;false;asList;;;ArrayElement of Argument[0];Element of ReturnValue;value",
|
||||
"java.util;Collections;false;addAll;(Collection,Object[]);;ArrayElement of Argument[1];Element of Argument[0];value"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
private predicate taintPreservingQualifierToMethod(Method m) {
|
||||
// java.util.Map.Entry
|
||||
m.getDeclaringType() instanceof EntryType and
|
||||
@@ -426,3 +709,58 @@ predicate containerStep(Expr n1, Expr n2) {
|
||||
containerReturnValueStep(n1, n2) or
|
||||
containerUpdateStep(n1, n2)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if the step from `node1` to `node2` stores a value in an array.
|
||||
* This covers array assignments and initializers as well as implicit array
|
||||
* creations for varargs.
|
||||
*/
|
||||
predicate arrayStoreStep(Node node1, Node node2) {
|
||||
exists(Argument arg |
|
||||
node1.asExpr() = arg and
|
||||
arg.isVararg() and
|
||||
node2.(ImplicitVarargsArray).getCall() = arg.getCall()
|
||||
)
|
||||
or
|
||||
node2.asExpr().(ArrayInit).getAnInit() = node1.asExpr()
|
||||
or
|
||||
exists(Assignment assign | assign.getSource() = node1.asExpr() |
|
||||
node2.(PostUpdateNode).getPreUpdateNode().asExpr() = assign.getDest().(ArrayAccess).getArray()
|
||||
)
|
||||
}
|
||||
|
||||
private predicate enhancedForStmtStep(Node node1, Node node2, Type containerType) {
|
||||
exists(EnhancedForStmt for, Expr e, SsaExplicitUpdate v |
|
||||
for.getExpr() = e and
|
||||
node1.asExpr() = e and
|
||||
containerType = e.getType() and
|
||||
v.getDefiningExpr() = for.getVariable() and
|
||||
v.getAFirstUse() = node2.asExpr()
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if the step from `node1` to `node2` reads a value from an array.
|
||||
* This covers ordinary array reads as well as array iteration through enhanced
|
||||
* `for` statements.
|
||||
*/
|
||||
predicate arrayReadStep(Node node1, Node node2, Type elemType) {
|
||||
exists(ArrayAccess aa |
|
||||
aa.getArray() = node1.asExpr() and
|
||||
aa.getType() = elemType and
|
||||
node2.asExpr() = aa
|
||||
)
|
||||
or
|
||||
exists(Array arr |
|
||||
enhancedForStmtStep(node1, node2, arr) and
|
||||
arr.getComponentType() = elemType
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if the step from `node1` to `node2` reads a value from a collection.
|
||||
* This only covers iteration through enhanced `for` statements.
|
||||
*/
|
||||
predicate collectionReadStep(Node node1, Node node2) {
|
||||
enhancedForStmtStep(node1, node2, any(Type t | not t instanceof Array))
|
||||
}
|
||||
|
||||
@@ -2,9 +2,17 @@ private import java
|
||||
private import DataFlowPrivate
|
||||
private import DataFlowUtil
|
||||
private import semmle.code.java.dataflow.InstanceAccess
|
||||
import semmle.code.java.dispatch.VirtualDispatch
|
||||
private import semmle.code.java.dataflow.FlowSummary
|
||||
private import semmle.code.java.dispatch.VirtualDispatch as VirtualDispatch
|
||||
|
||||
private module DispatchImpl {
|
||||
/** Gets a viable implementation of the target of the given `Call`. */
|
||||
Callable viableCallable(Call c) {
|
||||
result = VirtualDispatch::viableCallable(c)
|
||||
or
|
||||
result.(SummarizedCallable) = c.getCallee().getSourceDeclaration()
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if the set of viable implementations that can be called by `ma`
|
||||
* might be improved by knowing the call context. This is the case if the
|
||||
@@ -12,7 +20,7 @@ private module DispatchImpl {
|
||||
*/
|
||||
private predicate mayBenefitFromCallContext(MethodAccess ma, Callable c, int i) {
|
||||
exists(Parameter p |
|
||||
2 <= strictcount(viableImpl(ma)) and
|
||||
2 <= strictcount(VirtualDispatch::viableImpl(ma)) and
|
||||
ma.getQualifier().(VarAccess).getVariable() = p and
|
||||
p.getPosition() = i and
|
||||
c.getAParameter() = p and
|
||||
@@ -21,7 +29,7 @@ private module DispatchImpl {
|
||||
)
|
||||
or
|
||||
exists(OwnInstanceAccess ia |
|
||||
2 <= strictcount(viableImpl(ma)) and
|
||||
2 <= strictcount(VirtualDispatch::viableImpl(ma)) and
|
||||
(ia.isExplicit(ma.getQualifier()) or ia.isImplicitMethodQualifier(ma)) and
|
||||
i = -1 and
|
||||
c = ma.getEnclosingCallable()
|
||||
@@ -60,7 +68,7 @@ private module DispatchImpl {
|
||||
or
|
||||
ctx.getArgument(i) = arg
|
||||
|
|
||||
src = variableTrack(arg) and
|
||||
src = VirtualDispatch::variableTrack(arg) and
|
||||
srctype = getPreciseType(src) and
|
||||
if src instanceof ClassInstanceExpr then exact = true else exact = false
|
||||
)
|
||||
@@ -93,18 +101,18 @@ private module DispatchImpl {
|
||||
* restricted to those `ma`s for which a context might make a difference.
|
||||
*/
|
||||
Method viableImplInCallContext(MethodAccess ma, Call ctx) {
|
||||
result = viableImpl(ma) and
|
||||
result = VirtualDispatch::viableImpl(ma) and
|
||||
exists(int i, Callable c, Method def, RefType t, boolean exact |
|
||||
mayBenefitFromCallContext(ma, c, i) and
|
||||
c = viableCallable(ctx) and
|
||||
contextArgHasType(ctx, i, t, exact) and
|
||||
ma.getMethod() = def
|
||||
ma.getMethod().getSourceDeclaration() = def
|
||||
|
|
||||
exact = true and result = exactMethodImpl(def, t.getSourceDeclaration())
|
||||
exact = true and result = VirtualDispatch::exactMethodImpl(def, t.getSourceDeclaration())
|
||||
or
|
||||
exact = false and
|
||||
exists(RefType t2 |
|
||||
result = viableMethodImpl(def, t.getSourceDeclaration(), t2) and
|
||||
result = VirtualDispatch::viableMethodImpl(def, t.getSourceDeclaration(), t2) and
|
||||
not failsUnification(t, t2)
|
||||
)
|
||||
)
|
||||
@@ -117,7 +125,7 @@ private module DispatchImpl {
|
||||
|
||||
pragma[noinline]
|
||||
private predicate unificationTargetRight(ParameterizedType t2, GenericType g) {
|
||||
exists(viableMethodImpl(_, _, t2)) and t2.getGenericType() = g
|
||||
exists(VirtualDispatch::viableMethodImpl(_, _, t2)) and t2.getGenericType() = g
|
||||
}
|
||||
|
||||
private predicate unificationTargets(Type t1, Type t2) {
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
4292
java/ql/src/semmle/code/java/dataflow/internal/DataFlowImpl6.qll
Normal file
4292
java/ql/src/semmle/code/java/dataflow/internal/DataFlowImpl6.qll
Normal file
File diff suppressed because it is too large
Load Diff
@@ -31,26 +31,26 @@ predicate accessPathCostLimits(int apLimit, int tupleLimit) {
|
||||
* currently excludes read-steps, store-steps, and flow-through.
|
||||
*
|
||||
* The analysis uses non-linear recursion: When computing a flow path in or out
|
||||
* of a call, we use the results of the analysis recursively to resolve lamba
|
||||
* of a call, we use the results of the analysis recursively to resolve lambda
|
||||
* calls. For this reason, we cannot reuse the code from `DataFlowImpl.qll` directly.
|
||||
*/
|
||||
private module LambdaFlow {
|
||||
private predicate viableParamNonLambda(DataFlowCall call, int i, ParameterNode p) {
|
||||
private predicate viableParamNonLambda(DataFlowCall call, int i, ParamNode p) {
|
||||
p.isParameterOf(viableCallable(call), i)
|
||||
}
|
||||
|
||||
private predicate viableParamLambda(DataFlowCall call, int i, ParameterNode p) {
|
||||
private predicate viableParamLambda(DataFlowCall call, int i, ParamNode p) {
|
||||
p.isParameterOf(viableCallableLambda(call, _), i)
|
||||
}
|
||||
|
||||
private predicate viableParamArgNonLambda(DataFlowCall call, ParameterNode p, ArgumentNode arg) {
|
||||
private predicate viableParamArgNonLambda(DataFlowCall call, ParamNode p, ArgNode arg) {
|
||||
exists(int i |
|
||||
viableParamNonLambda(call, i, p) and
|
||||
arg.argumentOf(call, i)
|
||||
)
|
||||
}
|
||||
|
||||
private predicate viableParamArgLambda(DataFlowCall call, ParameterNode p, ArgumentNode arg) {
|
||||
private predicate viableParamArgLambda(DataFlowCall call, ParamNode p, ArgNode arg) {
|
||||
exists(int i |
|
||||
viableParamLambda(call, i, p) and
|
||||
arg.argumentOf(call, i)
|
||||
@@ -118,8 +118,8 @@ private module LambdaFlow {
|
||||
boolean toJump, DataFlowCallOption lastCall
|
||||
) {
|
||||
revLambdaFlow0(lambdaCall, kind, node, t, toReturn, toJump, lastCall) and
|
||||
if node instanceof CastNode or node instanceof ArgumentNode or node instanceof ReturnNode
|
||||
then compatibleTypes(t, getNodeType(node))
|
||||
if castNode(node) or node instanceof ArgNode or node instanceof ReturnNode
|
||||
then compatibleTypes(t, getNodeDataFlowType(node))
|
||||
else any()
|
||||
}
|
||||
|
||||
@@ -129,7 +129,7 @@ private module LambdaFlow {
|
||||
boolean toJump, DataFlowCallOption lastCall
|
||||
) {
|
||||
lambdaCall(lambdaCall, kind, node) and
|
||||
t = getNodeType(node) and
|
||||
t = getNodeDataFlowType(node) and
|
||||
toReturn = false and
|
||||
toJump = false and
|
||||
lastCall = TDataFlowCallNone()
|
||||
@@ -146,7 +146,7 @@ private module LambdaFlow {
|
||||
getNodeEnclosingCallable(node) = getNodeEnclosingCallable(mid)
|
||||
|
|
||||
preservesValue = false and
|
||||
t = getNodeType(node)
|
||||
t = getNodeDataFlowType(node)
|
||||
or
|
||||
preservesValue = true and
|
||||
t = t0
|
||||
@@ -160,7 +160,7 @@ private module LambdaFlow {
|
||||
toJump = true and
|
||||
lastCall = TDataFlowCallNone()
|
||||
|
|
||||
jumpStep(node, mid) and
|
||||
jumpStepCached(node, mid) and
|
||||
t = t0
|
||||
or
|
||||
exists(boolean preservesValue |
|
||||
@@ -168,7 +168,7 @@ private module LambdaFlow {
|
||||
getNodeEnclosingCallable(node) != getNodeEnclosingCallable(mid)
|
||||
|
|
||||
preservesValue = false and
|
||||
t = getNodeType(node)
|
||||
t = getNodeDataFlowType(node)
|
||||
or
|
||||
preservesValue = true and
|
||||
t = t0
|
||||
@@ -176,7 +176,7 @@ private module LambdaFlow {
|
||||
)
|
||||
or
|
||||
// flow into a callable
|
||||
exists(ParameterNode p, DataFlowCallOption lastCall0, DataFlowCall call |
|
||||
exists(ParamNode p, DataFlowCallOption lastCall0, DataFlowCall call |
|
||||
revLambdaFlowIn(lambdaCall, kind, p, t, toJump, lastCall0) and
|
||||
(
|
||||
if lastCall0 = TDataFlowCallNone() and toJump = false
|
||||
@@ -227,7 +227,7 @@ private module LambdaFlow {
|
||||
|
||||
pragma[nomagic]
|
||||
predicate revLambdaFlowIn(
|
||||
DataFlowCall lambdaCall, LambdaCallKind kind, ParameterNode p, DataFlowType t, boolean toJump,
|
||||
DataFlowCall lambdaCall, LambdaCallKind kind, ParamNode p, DataFlowType t, boolean toJump,
|
||||
DataFlowCallOption lastCall
|
||||
) {
|
||||
revLambdaFlow(lambdaCall, kind, p, t, false, toJump, lastCall)
|
||||
@@ -242,6 +242,89 @@ private DataFlowCallable viableCallableExt(DataFlowCall call) {
|
||||
|
||||
cached
|
||||
private module Cached {
|
||||
/**
|
||||
* If needed, call this predicate from `DataFlowImplSpecific.qll` in order to
|
||||
* force a stage-dependency on the `DataFlowImplCommon.qll` stage and therby
|
||||
* collapsing the two stages.
|
||||
*/
|
||||
cached
|
||||
predicate forceCachingInSameStage() { any() }
|
||||
|
||||
cached
|
||||
predicate nodeEnclosingCallable(Node n, DataFlowCallable c) { c = n.getEnclosingCallable() }
|
||||
|
||||
cached
|
||||
predicate callEnclosingCallable(DataFlowCall call, DataFlowCallable c) {
|
||||
c = call.getEnclosingCallable()
|
||||
}
|
||||
|
||||
cached
|
||||
predicate nodeDataFlowType(Node n, DataFlowType t) { t = getNodeType(n) }
|
||||
|
||||
cached
|
||||
predicate jumpStepCached(Node node1, Node node2) { jumpStep(node1, node2) }
|
||||
|
||||
cached
|
||||
predicate clearsContentCached(Node n, Content c) { clearsContent(n, c) }
|
||||
|
||||
cached
|
||||
predicate isUnreachableInCallCached(Node n, DataFlowCall call) { isUnreachableInCall(n, call) }
|
||||
|
||||
cached
|
||||
predicate outNodeExt(Node n) {
|
||||
n instanceof OutNode
|
||||
or
|
||||
n.(PostUpdateNode).getPreUpdateNode() instanceof ArgNode
|
||||
}
|
||||
|
||||
cached
|
||||
predicate hiddenNode(Node n) { nodeIsHidden(n) }
|
||||
|
||||
cached
|
||||
OutNodeExt getAnOutNodeExt(DataFlowCall call, ReturnKindExt k) {
|
||||
result = getAnOutNode(call, k.(ValueReturnKind).getKind())
|
||||
or
|
||||
exists(ArgNode arg |
|
||||
result.(PostUpdateNode).getPreUpdateNode() = arg and
|
||||
arg.argumentOf(call, k.(ParamUpdateReturnKind).getPosition())
|
||||
)
|
||||
}
|
||||
|
||||
cached
|
||||
predicate returnNodeExt(Node n, ReturnKindExt k) {
|
||||
k = TValueReturn(n.(ReturnNode).getKind())
|
||||
or
|
||||
exists(ParamNode p, int pos |
|
||||
parameterValueFlowsToPreUpdate(p, n) and
|
||||
p.isParameterOf(_, pos) and
|
||||
k = TParamUpdate(pos)
|
||||
)
|
||||
}
|
||||
|
||||
cached
|
||||
predicate castNode(Node n) { n instanceof CastNode }
|
||||
|
||||
cached
|
||||
predicate castingNode(Node n) {
|
||||
castNode(n) or
|
||||
n instanceof ParamNode or
|
||||
n instanceof OutNodeExt or
|
||||
// For reads, `x.f`, we want to check that the tracked type after the read (which
|
||||
// is obtained by popping the head of the access path stack) is compatible with
|
||||
// the type of `x.f`.
|
||||
read(_, _, n)
|
||||
}
|
||||
|
||||
cached
|
||||
predicate parameterNode(Node n, DataFlowCallable c, int i) {
|
||||
n.(ParameterNode).isParameterOf(c, i)
|
||||
}
|
||||
|
||||
cached
|
||||
predicate argumentNode(Node n, DataFlowCall call, int pos) {
|
||||
n.(ArgumentNode).argumentOf(call, pos)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a viable target for the lambda call `call`.
|
||||
*
|
||||
@@ -261,7 +344,7 @@ private module Cached {
|
||||
* The instance parameter is considered to have index `-1`.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
private predicate viableParam(DataFlowCall call, int i, ParameterNode p) {
|
||||
private predicate viableParam(DataFlowCall call, int i, ParamNode p) {
|
||||
p.isParameterOf(viableCallableExt(call), i)
|
||||
}
|
||||
|
||||
@@ -270,11 +353,11 @@ private module Cached {
|
||||
* dispatch into account.
|
||||
*/
|
||||
cached
|
||||
predicate viableParamArg(DataFlowCall call, ParameterNode p, ArgumentNode arg) {
|
||||
predicate viableParamArg(DataFlowCall call, ParamNode p, ArgNode arg) {
|
||||
exists(int i |
|
||||
viableParam(call, i, p) and
|
||||
arg.argumentOf(call, i) and
|
||||
compatibleTypes(getNodeType(arg), getNodeType(p))
|
||||
compatibleTypes(getNodeDataFlowType(arg), getNodeDataFlowType(p))
|
||||
)
|
||||
}
|
||||
|
||||
@@ -312,7 +395,7 @@ private module Cached {
|
||||
* `read` indicates whether it is contents of `p` that can flow to `node`.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
private predicate parameterValueFlowCand(ParameterNode p, Node node, boolean read) {
|
||||
private predicate parameterValueFlowCand(ParamNode p, Node node, boolean read) {
|
||||
p = node and
|
||||
read = false
|
||||
or
|
||||
@@ -325,30 +408,30 @@ private module Cached {
|
||||
// read
|
||||
exists(Node mid |
|
||||
parameterValueFlowCand(p, mid, false) and
|
||||
readStep(mid, _, node) and
|
||||
read(mid, _, node) and
|
||||
read = true
|
||||
)
|
||||
or
|
||||
// flow through: no prior read
|
||||
exists(ArgumentNode arg |
|
||||
exists(ArgNode arg |
|
||||
parameterValueFlowArgCand(p, arg, false) and
|
||||
argumentValueFlowsThroughCand(arg, node, read)
|
||||
)
|
||||
or
|
||||
// flow through: no read inside method
|
||||
exists(ArgumentNode arg |
|
||||
exists(ArgNode arg |
|
||||
parameterValueFlowArgCand(p, arg, read) and
|
||||
argumentValueFlowsThroughCand(arg, node, false)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate parameterValueFlowArgCand(ParameterNode p, ArgumentNode arg, boolean read) {
|
||||
private predicate parameterValueFlowArgCand(ParamNode p, ArgNode arg, boolean read) {
|
||||
parameterValueFlowCand(p, arg, read)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
predicate parameterValueFlowsToPreUpdateCand(ParameterNode p, PostUpdateNode n) {
|
||||
predicate parameterValueFlowsToPreUpdateCand(ParamNode p, PostUpdateNode n) {
|
||||
parameterValueFlowCand(p, n.getPreUpdateNode(), false)
|
||||
}
|
||||
|
||||
@@ -360,7 +443,7 @@ private module Cached {
|
||||
* `read` indicates whether it is contents of `p` that can flow to the return
|
||||
* node.
|
||||
*/
|
||||
predicate parameterValueFlowReturnCand(ParameterNode p, ReturnKind kind, boolean read) {
|
||||
predicate parameterValueFlowReturnCand(ParamNode p, ReturnKind kind, boolean read) {
|
||||
exists(ReturnNode ret |
|
||||
parameterValueFlowCand(p, ret, read) and
|
||||
kind = ret.getKind()
|
||||
@@ -369,9 +452,9 @@ private module Cached {
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate argumentValueFlowsThroughCand0(
|
||||
DataFlowCall call, ArgumentNode arg, ReturnKind kind, boolean read
|
||||
DataFlowCall call, ArgNode arg, ReturnKind kind, boolean read
|
||||
) {
|
||||
exists(ParameterNode param | viableParamArg(call, param, arg) |
|
||||
exists(ParamNode param | viableParamArg(call, param, arg) |
|
||||
parameterValueFlowReturnCand(param, kind, read)
|
||||
)
|
||||
}
|
||||
@@ -382,14 +465,14 @@ private module Cached {
|
||||
*
|
||||
* `read` indicates whether it is contents of `arg` that can flow to `out`.
|
||||
*/
|
||||
predicate argumentValueFlowsThroughCand(ArgumentNode arg, Node out, boolean read) {
|
||||
predicate argumentValueFlowsThroughCand(ArgNode arg, Node out, boolean read) {
|
||||
exists(DataFlowCall call, ReturnKind kind |
|
||||
argumentValueFlowsThroughCand0(call, arg, kind, read) and
|
||||
out = getAnOutNode(call, kind)
|
||||
)
|
||||
}
|
||||
|
||||
predicate cand(ParameterNode p, Node n) {
|
||||
predicate cand(ParamNode p, Node n) {
|
||||
parameterValueFlowCand(p, n, _) and
|
||||
(
|
||||
parameterValueFlowReturnCand(p, _, _)
|
||||
@@ -416,21 +499,21 @@ private module Cached {
|
||||
* If a read step was taken, then `read` captures the `Content`, the
|
||||
* container type, and the content type.
|
||||
*/
|
||||
predicate parameterValueFlow(ParameterNode p, Node node, ReadStepTypesOption read) {
|
||||
predicate parameterValueFlow(ParamNode p, Node node, ReadStepTypesOption read) {
|
||||
parameterValueFlow0(p, node, read) and
|
||||
if node instanceof CastingNode
|
||||
then
|
||||
// normal flow through
|
||||
read = TReadStepTypesNone() and
|
||||
compatibleTypes(getNodeType(p), getNodeType(node))
|
||||
compatibleTypes(getNodeDataFlowType(p), getNodeDataFlowType(node))
|
||||
or
|
||||
// getter
|
||||
compatibleTypes(read.getContentType(), getNodeType(node))
|
||||
compatibleTypes(read.getContentType(), getNodeDataFlowType(node))
|
||||
else any()
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate parameterValueFlow0(ParameterNode p, Node node, ReadStepTypesOption read) {
|
||||
private predicate parameterValueFlow0(ParamNode p, Node node, ReadStepTypesOption read) {
|
||||
p = node and
|
||||
Cand::cand(p, _) and
|
||||
read = TReadStepTypesNone()
|
||||
@@ -447,7 +530,7 @@ private module Cached {
|
||||
readStepWithTypes(mid, read.getContainerType(), read.getContent(), node,
|
||||
read.getContentType()) and
|
||||
Cand::parameterValueFlowReturnCand(p, _, true) and
|
||||
compatibleTypes(getNodeType(p), read.getContainerType())
|
||||
compatibleTypes(getNodeDataFlowType(p), read.getContainerType())
|
||||
)
|
||||
or
|
||||
parameterValueFlow0_0(TReadStepTypesNone(), p, node, read)
|
||||
@@ -455,34 +538,32 @@ private module Cached {
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate parameterValueFlow0_0(
|
||||
ReadStepTypesOption mustBeNone, ParameterNode p, Node node, ReadStepTypesOption read
|
||||
ReadStepTypesOption mustBeNone, ParamNode p, Node node, ReadStepTypesOption read
|
||||
) {
|
||||
// flow through: no prior read
|
||||
exists(ArgumentNode arg |
|
||||
exists(ArgNode arg |
|
||||
parameterValueFlowArg(p, arg, mustBeNone) and
|
||||
argumentValueFlowsThrough(arg, read, node)
|
||||
)
|
||||
or
|
||||
// flow through: no read inside method
|
||||
exists(ArgumentNode arg |
|
||||
exists(ArgNode arg |
|
||||
parameterValueFlowArg(p, arg, read) and
|
||||
argumentValueFlowsThrough(arg, mustBeNone, node)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate parameterValueFlowArg(
|
||||
ParameterNode p, ArgumentNode arg, ReadStepTypesOption read
|
||||
) {
|
||||
private predicate parameterValueFlowArg(ParamNode p, ArgNode arg, ReadStepTypesOption read) {
|
||||
parameterValueFlow(p, arg, read) and
|
||||
Cand::argumentValueFlowsThroughCand(arg, _, _)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate argumentValueFlowsThrough0(
|
||||
DataFlowCall call, ArgumentNode arg, ReturnKind kind, ReadStepTypesOption read
|
||||
DataFlowCall call, ArgNode arg, ReturnKind kind, ReadStepTypesOption read
|
||||
) {
|
||||
exists(ParameterNode param | viableParamArg(call, param, arg) |
|
||||
exists(ParamNode param | viableParamArg(call, param, arg) |
|
||||
parameterValueFlowReturn(param, kind, read)
|
||||
)
|
||||
}
|
||||
@@ -496,18 +577,18 @@ private module Cached {
|
||||
* container type, and the content type.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
predicate argumentValueFlowsThrough(ArgumentNode arg, ReadStepTypesOption read, Node out) {
|
||||
predicate argumentValueFlowsThrough(ArgNode arg, ReadStepTypesOption read, Node out) {
|
||||
exists(DataFlowCall call, ReturnKind kind |
|
||||
argumentValueFlowsThrough0(call, arg, kind, read) and
|
||||
out = getAnOutNode(call, kind)
|
||||
|
|
||||
// normal flow through
|
||||
read = TReadStepTypesNone() and
|
||||
compatibleTypes(getNodeType(arg), getNodeType(out))
|
||||
compatibleTypes(getNodeDataFlowType(arg), getNodeDataFlowType(out))
|
||||
or
|
||||
// getter
|
||||
compatibleTypes(getNodeType(arg), read.getContainerType()) and
|
||||
compatibleTypes(read.getContentType(), getNodeType(out))
|
||||
compatibleTypes(getNodeDataFlowType(arg), read.getContainerType()) and
|
||||
compatibleTypes(read.getContentType(), getNodeDataFlowType(out))
|
||||
)
|
||||
}
|
||||
|
||||
@@ -516,7 +597,7 @@ private module Cached {
|
||||
* value-preserving steps and a single read step, not taking call
|
||||
* contexts into account, thus representing a getter-step.
|
||||
*/
|
||||
predicate getterStep(ArgumentNode arg, Content c, Node out) {
|
||||
predicate getterStep(ArgNode arg, Content c, Node out) {
|
||||
argumentValueFlowsThrough(arg, TReadStepTypesSome(_, c, _), out)
|
||||
}
|
||||
|
||||
@@ -529,7 +610,7 @@ private module Cached {
|
||||
* container type, and the content type.
|
||||
*/
|
||||
private predicate parameterValueFlowReturn(
|
||||
ParameterNode p, ReturnKind kind, ReadStepTypesOption read
|
||||
ParamNode p, ReturnKind kind, ReadStepTypesOption read
|
||||
) {
|
||||
exists(ReturnNode ret |
|
||||
parameterValueFlow(p, ret, read) and
|
||||
@@ -553,7 +634,7 @@ private module Cached {
|
||||
private predicate mayBenefitFromCallContextExt(DataFlowCall call, DataFlowCallable callable) {
|
||||
mayBenefitFromCallContext(call, callable)
|
||||
or
|
||||
callable = call.getEnclosingCallable() and
|
||||
callEnclosingCallable(call, callable) and
|
||||
exists(viableCallableLambda(call, TDataFlowCallSome(_)))
|
||||
}
|
||||
|
||||
@@ -611,7 +692,7 @@ private module Cached {
|
||||
mayBenefitFromCallContextExt(call, _) and
|
||||
c = viableCallableExt(call) and
|
||||
ctxtgts = count(DataFlowCall ctx | c = viableImplInCallContextExt(call, ctx)) and
|
||||
tgts = strictcount(DataFlowCall ctx | viableCallableExt(ctx) = call.getEnclosingCallable()) and
|
||||
tgts = strictcount(DataFlowCall ctx | callEnclosingCallable(call, viableCallableExt(ctx))) and
|
||||
ctxtgts < tgts
|
||||
)
|
||||
}
|
||||
@@ -635,8 +716,7 @@ private module Cached {
|
||||
* Holds if `p` can flow to the pre-update node associated with post-update
|
||||
* node `n`, in the same callable, using only value-preserving steps.
|
||||
*/
|
||||
cached
|
||||
predicate parameterValueFlowsToPreUpdate(ParameterNode p, PostUpdateNode n) {
|
||||
private predicate parameterValueFlowsToPreUpdate(ParamNode p, PostUpdateNode n) {
|
||||
parameterValueFlow(p, n.getPreUpdateNode(), TReadStepTypesNone())
|
||||
}
|
||||
|
||||
@@ -644,9 +724,9 @@ private module Cached {
|
||||
Node node1, Content c, Node node2, DataFlowType contentType, DataFlowType containerType
|
||||
) {
|
||||
storeStep(node1, c, node2) and
|
||||
readStep(_, c, _) and
|
||||
contentType = getNodeType(node1) and
|
||||
containerType = getNodeType(node2)
|
||||
read(_, c, _) and
|
||||
contentType = getNodeDataFlowType(node1) and
|
||||
containerType = getNodeDataFlowType(node2)
|
||||
or
|
||||
exists(Node n1, Node n2 |
|
||||
n1 = node1.(PostUpdateNode).getPreUpdateNode() and
|
||||
@@ -654,12 +734,15 @@ private module Cached {
|
||||
|
|
||||
argumentValueFlowsThrough(n2, TReadStepTypesSome(containerType, c, contentType), n1)
|
||||
or
|
||||
readStep(n2, c, n1) and
|
||||
contentType = getNodeType(n1) and
|
||||
containerType = getNodeType(n2)
|
||||
read(n2, c, n1) and
|
||||
contentType = getNodeDataFlowType(n1) and
|
||||
containerType = getNodeDataFlowType(n2)
|
||||
)
|
||||
}
|
||||
|
||||
cached
|
||||
predicate read(Node node1, Content c, Node node2) { readStep(node1, c, node2) }
|
||||
|
||||
/**
|
||||
* Holds if data can flow from `node1` to `node2` via a direct assignment to
|
||||
* `f`.
|
||||
@@ -678,8 +761,9 @@ private module Cached {
|
||||
* are aliases. A typical example is a function returning `this`, implementing a fluent
|
||||
* interface.
|
||||
*/
|
||||
cached
|
||||
predicate reverseStepThroughInputOutputAlias(PostUpdateNode fromNode, PostUpdateNode toNode) {
|
||||
private predicate reverseStepThroughInputOutputAlias(
|
||||
PostUpdateNode fromNode, PostUpdateNode toNode
|
||||
) {
|
||||
exists(Node fromPre, Node toPre |
|
||||
fromPre = fromNode.getPreUpdateNode() and
|
||||
toPre = toNode.getPreUpdateNode()
|
||||
@@ -688,14 +772,20 @@ private module Cached {
|
||||
// Does the language-specific simpleLocalFlowStep already model flow
|
||||
// from function input to output?
|
||||
fromPre = getAnOutNode(c, _) and
|
||||
toPre.(ArgumentNode).argumentOf(c, _) and
|
||||
simpleLocalFlowStep(toPre.(ArgumentNode), fromPre)
|
||||
toPre.(ArgNode).argumentOf(c, _) and
|
||||
simpleLocalFlowStep(toPre.(ArgNode), fromPre)
|
||||
)
|
||||
or
|
||||
argumentValueFlowsThrough(toPre, TReadStepTypesNone(), fromPre)
|
||||
)
|
||||
}
|
||||
|
||||
cached
|
||||
predicate simpleLocalFlowStepExt(Node node1, Node node2) {
|
||||
simpleLocalFlowStep(node1, node2) or
|
||||
reverseStepThroughInputOutputAlias(node1, node2)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if the call context `call` either improves virtual dispatch in
|
||||
* `callable` or if it allows us to prune unreachable nodes in `callable`.
|
||||
@@ -704,7 +794,7 @@ private module Cached {
|
||||
predicate recordDataFlowCallSite(DataFlowCall call, DataFlowCallable callable) {
|
||||
reducedViableImplInCallContext(_, callable, call)
|
||||
or
|
||||
exists(Node n | getNodeEnclosingCallable(n) = callable | isUnreachableInCall(n, call))
|
||||
exists(Node n | getNodeEnclosingCallable(n) = callable | isUnreachableInCallCached(n, call))
|
||||
}
|
||||
|
||||
cached
|
||||
@@ -726,12 +816,12 @@ private module Cached {
|
||||
cached
|
||||
newtype TLocalFlowCallContext =
|
||||
TAnyLocalCall() or
|
||||
TSpecificLocalCall(DataFlowCall call) { isUnreachableInCall(_, call) }
|
||||
TSpecificLocalCall(DataFlowCall call) { isUnreachableInCallCached(_, call) }
|
||||
|
||||
cached
|
||||
newtype TReturnKindExt =
|
||||
TValueReturn(ReturnKind kind) or
|
||||
TParamUpdate(int pos) { exists(ParameterNode p | p.isParameterOf(_, pos)) }
|
||||
TParamUpdate(int pos) { exists(ParamNode p | p.isParameterOf(_, pos)) }
|
||||
|
||||
cached
|
||||
newtype TBooleanOption =
|
||||
@@ -761,23 +851,15 @@ private module Cached {
|
||||
* A `Node` at which a cast can occur such that the type should be checked.
|
||||
*/
|
||||
class CastingNode extends Node {
|
||||
CastingNode() {
|
||||
this instanceof ParameterNode or
|
||||
this instanceof CastNode or
|
||||
this instanceof OutNodeExt or
|
||||
// For reads, `x.f`, we want to check that the tracked type after the read (which
|
||||
// is obtained by popping the head of the access path stack) is compatible with
|
||||
// the type of `x.f`.
|
||||
readStep(_, _, this)
|
||||
}
|
||||
CastingNode() { castingNode(this) }
|
||||
}
|
||||
|
||||
private predicate readStepWithTypes(
|
||||
Node n1, DataFlowType container, Content c, Node n2, DataFlowType content
|
||||
) {
|
||||
readStep(n1, c, n2) and
|
||||
container = getNodeType(n1) and
|
||||
content = getNodeType(n2)
|
||||
read(n1, c, n2) and
|
||||
container = getNodeDataFlowType(n1) and
|
||||
content = getNodeDataFlowType(n2)
|
||||
}
|
||||
|
||||
private newtype TReadStepTypesOption =
|
||||
@@ -854,7 +936,7 @@ class CallContextSomeCall extends CallContextCall, TSomeCall {
|
||||
override string toString() { result = "CcSomeCall" }
|
||||
|
||||
override predicate relevantFor(DataFlowCallable callable) {
|
||||
exists(ParameterNode p | getNodeEnclosingCallable(p) = callable)
|
||||
exists(ParamNode p | getNodeEnclosingCallable(p) = callable)
|
||||
}
|
||||
|
||||
override predicate matchesCall(DataFlowCall call) { any() }
|
||||
@@ -866,7 +948,7 @@ class CallContextReturn extends CallContextNoCall, TReturn {
|
||||
}
|
||||
|
||||
override predicate relevantFor(DataFlowCallable callable) {
|
||||
exists(DataFlowCall call | this = TReturn(_, call) and call.getEnclosingCallable() = callable)
|
||||
exists(DataFlowCall call | this = TReturn(_, call) and callEnclosingCallable(call, callable))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -899,7 +981,7 @@ class LocalCallContextSpecificCall extends LocalCallContext, TSpecificLocalCall
|
||||
}
|
||||
|
||||
private predicate relevantLocalCCtx(DataFlowCall call, DataFlowCallable callable) {
|
||||
exists(Node n | getNodeEnclosingCallable(n) = callable and isUnreachableInCall(n, call))
|
||||
exists(Node n | getNodeEnclosingCallable(n) = callable and isUnreachableInCallCached(n, call))
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -913,26 +995,37 @@ LocalCallContext getLocalCallContext(CallContext ctx, DataFlowCallable callable)
|
||||
else result instanceof LocalCallContextAny
|
||||
}
|
||||
|
||||
/**
|
||||
* The value of a parameter at function entry, viewed as a node in a data
|
||||
* flow graph.
|
||||
*/
|
||||
class ParamNode extends Node {
|
||||
ParamNode() { parameterNode(this, _, _) }
|
||||
|
||||
/**
|
||||
* Holds if this node is the parameter of callable `c` at the specified
|
||||
* (zero-based) position.
|
||||
*/
|
||||
predicate isParameterOf(DataFlowCallable c, int i) { parameterNode(this, c, i) }
|
||||
}
|
||||
|
||||
/** A data-flow node that represents a call argument. */
|
||||
class ArgNode extends Node {
|
||||
ArgNode() { argumentNode(this, _, _) }
|
||||
|
||||
/** Holds if this argument occurs at the given position in the given call. */
|
||||
final predicate argumentOf(DataFlowCall call, int pos) { argumentNode(this, call, pos) }
|
||||
}
|
||||
|
||||
/**
|
||||
* A node from which flow can return to the caller. This is either a regular
|
||||
* `ReturnNode` or a `PostUpdateNode` corresponding to the value of a parameter.
|
||||
*/
|
||||
class ReturnNodeExt extends Node {
|
||||
ReturnNodeExt() {
|
||||
this instanceof ReturnNode or
|
||||
parameterValueFlowsToPreUpdate(_, this)
|
||||
}
|
||||
ReturnNodeExt() { returnNodeExt(this, _) }
|
||||
|
||||
/** Gets the kind of this returned value. */
|
||||
ReturnKindExt getKind() {
|
||||
result = TValueReturn(this.(ReturnNode).getKind())
|
||||
or
|
||||
exists(ParameterNode p, int pos |
|
||||
parameterValueFlowsToPreUpdate(p, this) and
|
||||
p.isParameterOf(_, pos) and
|
||||
result = TParamUpdate(pos)
|
||||
)
|
||||
}
|
||||
ReturnKindExt getKind() { returnNodeExt(this, result) }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -940,11 +1033,7 @@ class ReturnNodeExt extends Node {
|
||||
* or a post-update node associated with a call argument.
|
||||
*/
|
||||
class OutNodeExt extends Node {
|
||||
OutNodeExt() {
|
||||
this instanceof OutNode
|
||||
or
|
||||
this.(PostUpdateNode).getPreUpdateNode() instanceof ArgumentNode
|
||||
}
|
||||
OutNodeExt() { outNodeExt(this) }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -957,7 +1046,7 @@ abstract class ReturnKindExt extends TReturnKindExt {
|
||||
abstract string toString();
|
||||
|
||||
/** Gets a node corresponding to data flow out of `call`. */
|
||||
abstract OutNodeExt getAnOutNode(DataFlowCall call);
|
||||
final OutNodeExt getAnOutNode(DataFlowCall call) { result = getAnOutNodeExt(call, this) }
|
||||
}
|
||||
|
||||
class ValueReturnKind extends ReturnKindExt, TValueReturn {
|
||||
@@ -968,10 +1057,6 @@ class ValueReturnKind extends ReturnKindExt, TValueReturn {
|
||||
ReturnKind getKind() { result = kind }
|
||||
|
||||
override string toString() { result = kind.toString() }
|
||||
|
||||
override OutNodeExt getAnOutNode(DataFlowCall call) {
|
||||
result = getAnOutNode(call, this.getKind())
|
||||
}
|
||||
}
|
||||
|
||||
class ParamUpdateReturnKind extends ReturnKindExt, TParamUpdate {
|
||||
@@ -982,13 +1067,6 @@ class ParamUpdateReturnKind extends ReturnKindExt, TParamUpdate {
|
||||
int getPosition() { result = pos }
|
||||
|
||||
override string toString() { result = "param update " + pos }
|
||||
|
||||
override OutNodeExt getAnOutNode(DataFlowCall call) {
|
||||
exists(ArgumentNode arg |
|
||||
result.(PostUpdateNode).getPreUpdateNode() = arg and
|
||||
arg.argumentOf(call, this.getPosition())
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/** A callable tagged with a relevant return kind. */
|
||||
@@ -1015,10 +1093,13 @@ class ReturnPosition extends TReturnPosition0 {
|
||||
*/
|
||||
pragma[inline]
|
||||
DataFlowCallable getNodeEnclosingCallable(Node n) {
|
||||
exists(Node n0 |
|
||||
pragma[only_bind_into](n0) = n and
|
||||
pragma[only_bind_into](result) = n0.getEnclosingCallable()
|
||||
)
|
||||
nodeEnclosingCallable(pragma[only_bind_out](n), pragma[only_bind_into](result))
|
||||
}
|
||||
|
||||
/** Gets the type of `n` used for type pruning. */
|
||||
pragma[inline]
|
||||
DataFlowType getNodeDataFlowType(Node n) {
|
||||
nodeDataFlowType(pragma[only_bind_out](n), pragma[only_bind_into](result))
|
||||
}
|
||||
|
||||
pragma[noinline]
|
||||
@@ -1042,7 +1123,7 @@ predicate resolveReturn(CallContext cc, DataFlowCallable callable, DataFlowCall
|
||||
cc instanceof CallContextAny and callable = viableCallableExt(call)
|
||||
or
|
||||
exists(DataFlowCallable c0, DataFlowCall call0 |
|
||||
call0.getEnclosingCallable() = callable and
|
||||
callEnclosingCallable(call0, callable) and
|
||||
cc = TReturn(c0, call0) and
|
||||
c0 = prunedViableImplInCallContextReverse(call0, call)
|
||||
)
|
||||
@@ -1063,8 +1144,6 @@ DataFlowCallable resolveCall(DataFlowCall call, CallContext cc) {
|
||||
result = viableCallableExt(call) and cc instanceof CallContextReturn
|
||||
}
|
||||
|
||||
predicate read = readStep/3;
|
||||
|
||||
/** An optional Boolean value. */
|
||||
class BooleanOption extends TBooleanOption {
|
||||
string toString() {
|
||||
@@ -1116,7 +1195,7 @@ abstract class AccessPathFront extends TAccessPathFront {
|
||||
|
||||
TypedContent getHead() { this = TFrontHead(result) }
|
||||
|
||||
predicate isClearedAt(Node n) { clearsContent(n, getHead().getContent()) }
|
||||
predicate isClearedAt(Node n) { clearsContentCached(n, getHead().getContent()) }
|
||||
}
|
||||
|
||||
class AccessPathFrontNil extends AccessPathFront, TFrontNil {
|
||||
|
||||
424
java/ql/src/semmle/code/java/dataflow/internal/DataFlowNodes.qll
Normal file
424
java/ql/src/semmle/code/java/dataflow/internal/DataFlowNodes.qll
Normal file
@@ -0,0 +1,424 @@
|
||||
private import java
|
||||
private import semmle.code.java.dataflow.InstanceAccess
|
||||
private import semmle.code.java.dataflow.FlowSummary
|
||||
private import semmle.code.java.dataflow.TypeFlow
|
||||
private import DataFlowPrivate
|
||||
private import FlowSummaryImpl as FlowSummaryImpl
|
||||
private import DataFlowImplCommon as DataFlowImplCommon
|
||||
|
||||
cached
|
||||
newtype TNode =
|
||||
TExprNode(Expr e) {
|
||||
DataFlowImplCommon::forceCachingInSameStage() and
|
||||
not e.getType() instanceof VoidType and
|
||||
not e.getParent*() instanceof Annotation
|
||||
} or
|
||||
TExplicitParameterNode(Parameter p) {
|
||||
exists(p.getCallable().getBody()) or p.getCallable() instanceof SummarizedCallable
|
||||
} or
|
||||
TImplicitVarargsArray(Call c) {
|
||||
c.getCallee().isVarargs() and
|
||||
not exists(Argument arg | arg.getCall() = c and arg.isExplicitVarargsArray())
|
||||
} or
|
||||
TInstanceParameterNode(Callable c) {
|
||||
(exists(c.getBody()) or c instanceof SummarizedCallable) and
|
||||
not c.isStatic()
|
||||
} or
|
||||
TImplicitInstanceAccess(InstanceAccessExt ia) { not ia.isExplicit(_) } or
|
||||
TMallocNode(ClassInstanceExpr cie) or
|
||||
TExplicitExprPostUpdate(Expr e) {
|
||||
explicitInstanceArgument(_, e)
|
||||
or
|
||||
e instanceof Argument and not e.getType() instanceof ImmutableType
|
||||
or
|
||||
exists(FieldAccess fa | fa.getField() instanceof InstanceField and e = fa.getQualifier())
|
||||
or
|
||||
exists(ArrayAccess aa | e = aa.getArray())
|
||||
} or
|
||||
TImplicitExprPostUpdate(InstanceAccessExt ia) {
|
||||
implicitInstanceArgument(_, ia)
|
||||
or
|
||||
exists(FieldAccess fa |
|
||||
fa.getField() instanceof InstanceField and ia.isImplicitFieldQualifier(fa)
|
||||
)
|
||||
} or
|
||||
TSummaryInternalNode(SummarizedCallable c, FlowSummaryImpl::Private::SummaryNodeState state) {
|
||||
FlowSummaryImpl::Private::summaryNodeRange(c, state)
|
||||
}
|
||||
|
||||
private predicate explicitInstanceArgument(Call call, Expr instarg) {
|
||||
call instanceof MethodAccess and
|
||||
instarg = call.getQualifier() and
|
||||
not call.getCallee().isStatic()
|
||||
}
|
||||
|
||||
private predicate implicitInstanceArgument(Call call, InstanceAccessExt ia) {
|
||||
ia.isImplicitMethodQualifier(call) or
|
||||
ia.isImplicitThisConstructorArgument(call)
|
||||
}
|
||||
|
||||
module Public {
|
||||
/**
|
||||
* An element, viewed as a node in a data flow graph. Either an expression,
|
||||
* a parameter, or an implicit varargs array creation.
|
||||
*/
|
||||
class Node extends TNode {
|
||||
/** Gets a textual representation of this element. */
|
||||
string toString() { none() }
|
||||
|
||||
/** Gets the source location for this element. */
|
||||
Location getLocation() { none() }
|
||||
|
||||
/** Gets the expression corresponding to this node, if any. */
|
||||
Expr asExpr() { result = this.(ExprNode).getExpr() }
|
||||
|
||||
/** Gets the parameter corresponding to this node, if any. */
|
||||
Parameter asParameter() { result = this.(ExplicitParameterNode).getParameter() }
|
||||
|
||||
/** Gets the type of this node. */
|
||||
Type getType() {
|
||||
result = this.asExpr().getType()
|
||||
or
|
||||
result = this.asParameter().getType()
|
||||
or
|
||||
exists(Parameter p |
|
||||
result = p.getType() and
|
||||
p.isVarargs() and
|
||||
p = this.(ImplicitVarargsArray).getCall().getCallee().getAParameter()
|
||||
)
|
||||
or
|
||||
result = this.(InstanceParameterNode).getCallable().getDeclaringType()
|
||||
or
|
||||
result = this.(ImplicitInstanceAccess).getInstanceAccess().getType()
|
||||
or
|
||||
result = this.(MallocNode).getClassInstanceExpr().getType()
|
||||
or
|
||||
result = this.(ImplicitPostUpdateNode).getPreUpdateNode().getType()
|
||||
}
|
||||
|
||||
/** Gets the callable in which this node occurs. */
|
||||
Callable getEnclosingCallable() {
|
||||
result = this.asExpr().getEnclosingCallable() or
|
||||
result = this.asParameter().getCallable() or
|
||||
result = this.(ImplicitVarargsArray).getCall().getEnclosingCallable() or
|
||||
result = this.(InstanceParameterNode).getCallable() or
|
||||
result = this.(ImplicitInstanceAccess).getInstanceAccess().getEnclosingCallable() or
|
||||
result = this.(MallocNode).getClassInstanceExpr().getEnclosingCallable() or
|
||||
result = this.(ImplicitPostUpdateNode).getPreUpdateNode().getEnclosingCallable() or
|
||||
this = TSummaryInternalNode(result, _)
|
||||
}
|
||||
|
||||
private Type getImprovedTypeBound() {
|
||||
exprTypeFlow(this.asExpr(), result, _) or
|
||||
result = this.(ImplicitPostUpdateNode).getPreUpdateNode().getImprovedTypeBound()
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets an upper bound on the type of this node.
|
||||
*/
|
||||
Type getTypeBound() {
|
||||
result = getImprovedTypeBound()
|
||||
or
|
||||
result = getType() and not exists(getImprovedTypeBound())
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if this element is at the specified location.
|
||||
* The location spans column `startcolumn` of line `startline` to
|
||||
* column `endcolumn` of line `endline` in file `filepath`.
|
||||
* For more information, see
|
||||
* [Locations](https://help.semmle.com/QL/learn-ql/ql/locations.html).
|
||||
*/
|
||||
predicate hasLocationInfo(
|
||||
string filepath, int startline, int startcolumn, int endline, int endcolumn
|
||||
) {
|
||||
getLocation().hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* An expression, viewed as a node in a data flow graph.
|
||||
*/
|
||||
class ExprNode extends Node, TExprNode {
|
||||
Expr expr;
|
||||
|
||||
ExprNode() { this = TExprNode(expr) }
|
||||
|
||||
override string toString() { result = expr.toString() }
|
||||
|
||||
override Location getLocation() { result = expr.getLocation() }
|
||||
|
||||
/** Gets the expression corresponding to this node. */
|
||||
Expr getExpr() { result = expr }
|
||||
}
|
||||
|
||||
/** Gets the node corresponding to `e`. */
|
||||
ExprNode exprNode(Expr e) { result.getExpr() = e }
|
||||
|
||||
/** An explicit or implicit parameter. */
|
||||
abstract class ParameterNode extends Node {
|
||||
/**
|
||||
* Holds if this node is the parameter of `c` at the specified (zero-based)
|
||||
* position. The implicit `this` parameter is considered to have index `-1`.
|
||||
*/
|
||||
abstract predicate isParameterOf(Callable c, int pos);
|
||||
}
|
||||
|
||||
/**
|
||||
* A parameter, viewed as a node in a data flow graph.
|
||||
*/
|
||||
class ExplicitParameterNode extends ParameterNode, TExplicitParameterNode {
|
||||
Parameter param;
|
||||
|
||||
ExplicitParameterNode() { this = TExplicitParameterNode(param) }
|
||||
|
||||
override string toString() { result = param.toString() }
|
||||
|
||||
override Location getLocation() { result = param.getLocation() }
|
||||
|
||||
/** Gets the parameter corresponding to this node. */
|
||||
Parameter getParameter() { result = param }
|
||||
|
||||
override predicate isParameterOf(Callable c, int pos) { c.getParameter(pos) = param }
|
||||
}
|
||||
|
||||
/** Gets the node corresponding to `p`. */
|
||||
ExplicitParameterNode parameterNode(Parameter p) { result.getParameter() = p }
|
||||
|
||||
/**
|
||||
* An implicit varargs array creation expression.
|
||||
*
|
||||
* A call `f(x1, x2)` to a method `f(A... xs)` desugars to `f(new A[]{x1, x2})`,
|
||||
* and this node corresponds to such an implicit array creation.
|
||||
*/
|
||||
class ImplicitVarargsArray extends Node, TImplicitVarargsArray {
|
||||
Call call;
|
||||
|
||||
ImplicitVarargsArray() { this = TImplicitVarargsArray(call) }
|
||||
|
||||
override string toString() { result = "new ..[] { .. }" }
|
||||
|
||||
override Location getLocation() { result = call.getLocation() }
|
||||
|
||||
/** Gets the call containing this varargs array creation argument. */
|
||||
Call getCall() { result = call }
|
||||
}
|
||||
|
||||
/**
|
||||
* An instance parameter for an instance method or constructor.
|
||||
*/
|
||||
class InstanceParameterNode extends ParameterNode, TInstanceParameterNode {
|
||||
Callable callable;
|
||||
|
||||
InstanceParameterNode() { this = TInstanceParameterNode(callable) }
|
||||
|
||||
override string toString() { result = "parameter this" }
|
||||
|
||||
override Location getLocation() { result = callable.getLocation() }
|
||||
|
||||
/** Gets the callable containing this `this` parameter. */
|
||||
Callable getCallable() { result = callable }
|
||||
|
||||
override predicate isParameterOf(Callable c, int pos) { callable = c and pos = -1 }
|
||||
}
|
||||
|
||||
/**
|
||||
* An implicit read of `this` or `A.this`.
|
||||
*/
|
||||
class ImplicitInstanceAccess extends Node, TImplicitInstanceAccess {
|
||||
InstanceAccessExt ia;
|
||||
|
||||
ImplicitInstanceAccess() { this = TImplicitInstanceAccess(ia) }
|
||||
|
||||
override string toString() { result = ia.toString() }
|
||||
|
||||
override Location getLocation() { result = ia.getLocation() }
|
||||
|
||||
/** Gets the instance access corresponding to this node. */
|
||||
InstanceAccessExt getInstanceAccess() { result = ia }
|
||||
}
|
||||
|
||||
/**
|
||||
* A node associated with an object after an operation that might have
|
||||
* changed its state.
|
||||
*
|
||||
* This can be either the argument to a callable after the callable returns
|
||||
* (which might have mutated the argument), or the qualifier of a field after
|
||||
* an update to the field.
|
||||
*
|
||||
* Nodes corresponding to AST elements, for example `ExprNode`, usually refer
|
||||
* to the value before the update with the exception of `ClassInstanceExpr`,
|
||||
* which represents the value after the constructor has run.
|
||||
*/
|
||||
abstract class PostUpdateNode extends Node {
|
||||
/**
|
||||
* Gets the node before the state update.
|
||||
*/
|
||||
abstract Node getPreUpdateNode();
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the node that occurs as the qualifier of `fa`.
|
||||
*/
|
||||
Node getFieldQualifier(FieldAccess fa) {
|
||||
fa.getField() instanceof InstanceField and
|
||||
(
|
||||
result.asExpr() = fa.getQualifier() or
|
||||
result.(ImplicitInstanceAccess).getInstanceAccess().isImplicitFieldQualifier(fa)
|
||||
)
|
||||
}
|
||||
|
||||
/** Gets the instance argument of a non-static call. */
|
||||
Node getInstanceArgument(Call call) {
|
||||
result.(MallocNode).getClassInstanceExpr() = call or
|
||||
explicitInstanceArgument(call, result.asExpr()) or
|
||||
implicitInstanceArgument(call, result.(ImplicitInstanceAccess).getInstanceAccess())
|
||||
}
|
||||
}
|
||||
|
||||
private import Public
|
||||
|
||||
private class NewExpr extends PostUpdateNode, TExprNode {
|
||||
NewExpr() { exists(ClassInstanceExpr cie | this = TExprNode(cie)) }
|
||||
|
||||
override Node getPreUpdateNode() { this = TExprNode(result.(MallocNode).getClassInstanceExpr()) }
|
||||
}
|
||||
|
||||
/**
|
||||
* A `PostUpdateNode` that is not a `ClassInstanceExpr`.
|
||||
*/
|
||||
abstract private class ImplicitPostUpdateNode extends PostUpdateNode {
|
||||
override Location getLocation() { result = getPreUpdateNode().getLocation() }
|
||||
|
||||
override string toString() { result = getPreUpdateNode().toString() + " [post update]" }
|
||||
}
|
||||
|
||||
private class ExplicitExprPostUpdate extends ImplicitPostUpdateNode, TExplicitExprPostUpdate {
|
||||
override Node getPreUpdateNode() { this = TExplicitExprPostUpdate(result.asExpr()) }
|
||||
}
|
||||
|
||||
private class ImplicitExprPostUpdate extends ImplicitPostUpdateNode, TImplicitExprPostUpdate {
|
||||
override Node getPreUpdateNode() {
|
||||
this = TImplicitExprPostUpdate(result.(ImplicitInstanceAccess).getInstanceAccess())
|
||||
}
|
||||
}
|
||||
|
||||
module Private {
|
||||
/**
|
||||
* A data flow node that occurs as the argument of a call and is passed as-is
|
||||
* to the callable. Arguments that are wrapped in an implicit varargs array
|
||||
* creation are not included, but the implicitly created array is.
|
||||
* Instance arguments are also included.
|
||||
*/
|
||||
class ArgumentNode extends Node {
|
||||
ArgumentNode() {
|
||||
exists(Argument arg | this.asExpr() = arg | not arg.isVararg())
|
||||
or
|
||||
this instanceof ImplicitVarargsArray
|
||||
or
|
||||
this = getInstanceArgument(_)
|
||||
or
|
||||
this.(SummaryNode).isArgumentOf(_, _)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if this argument occurs at the given position in the given call.
|
||||
* The instance argument is considered to have index `-1`.
|
||||
*/
|
||||
predicate argumentOf(DataFlowCall call, int pos) {
|
||||
exists(Argument arg | this.asExpr() = arg | call = arg.getCall() and pos = arg.getPosition())
|
||||
or
|
||||
call = this.(ImplicitVarargsArray).getCall() and
|
||||
pos = call.getCallee().getNumberOfParameters() - 1
|
||||
or
|
||||
pos = -1 and this = getInstanceArgument(call)
|
||||
or
|
||||
this.(SummaryNode).isArgumentOf(call, pos)
|
||||
}
|
||||
|
||||
/** Gets the call in which this node is an argument. */
|
||||
DataFlowCall getCall() { this.argumentOf(result, _) }
|
||||
}
|
||||
|
||||
/** A data flow node that occurs as the result of a `ReturnStmt`. */
|
||||
class ReturnNode extends Node {
|
||||
ReturnNode() {
|
||||
exists(ReturnStmt ret | this.asExpr() = ret.getResult()) or
|
||||
this.(SummaryNode).isReturn()
|
||||
}
|
||||
|
||||
/** Gets the kind of this returned value. */
|
||||
ReturnKind getKind() { any() }
|
||||
}
|
||||
|
||||
/** A data flow node that represents the output of a call. */
|
||||
class OutNode extends Node {
|
||||
OutNode() {
|
||||
this.asExpr() instanceof MethodAccess
|
||||
or
|
||||
this.(SummaryNode).isOut(_)
|
||||
}
|
||||
|
||||
/** Gets the underlying call. */
|
||||
DataFlowCall getCall() {
|
||||
result = this.asExpr()
|
||||
or
|
||||
this.(SummaryNode).isOut(result)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A data-flow node used to model flow summaries.
|
||||
*/
|
||||
class SummaryNode extends Node, TSummaryInternalNode {
|
||||
private SummarizedCallable c;
|
||||
private FlowSummaryImpl::Private::SummaryNodeState state;
|
||||
|
||||
SummaryNode() { this = TSummaryInternalNode(c, state) }
|
||||
|
||||
override Location getLocation() { result = c.getLocation() }
|
||||
|
||||
override string toString() { result = "[summary] " + state + " in " + c }
|
||||
|
||||
/** Holds if this summary node is the `i`th argument of `call`. */
|
||||
predicate isArgumentOf(DataFlowCall call, int i) {
|
||||
FlowSummaryImpl::Private::summaryArgumentNode(call, this, i)
|
||||
}
|
||||
|
||||
/** Holds if this summary node is a return node. */
|
||||
predicate isReturn() { FlowSummaryImpl::Private::summaryReturnNode(this, _) }
|
||||
|
||||
/** Holds if this summary node is an out node for `call`. */
|
||||
predicate isOut(DataFlowCall call) { FlowSummaryImpl::Private::summaryOutNode(call, this, _) }
|
||||
}
|
||||
|
||||
SummaryNode getSummaryNode(SummarizedCallable c, FlowSummaryImpl::Private::SummaryNodeState state) {
|
||||
result = TSummaryInternalNode(c, state)
|
||||
}
|
||||
}
|
||||
|
||||
private import Private
|
||||
|
||||
/**
|
||||
* A node that corresponds to the value of a `ClassInstanceExpr` before the
|
||||
* constructor has run.
|
||||
*/
|
||||
private class MallocNode extends Node, TMallocNode {
|
||||
ClassInstanceExpr cie;
|
||||
|
||||
MallocNode() { this = TMallocNode(cie) }
|
||||
|
||||
override string toString() { result = cie.toString() + " [pre constructor]" }
|
||||
|
||||
override Location getLocation() { result = cie.getLocation() }
|
||||
|
||||
ClassInstanceExpr getClassInstanceExpr() { result = cie }
|
||||
}
|
||||
|
||||
private class SummaryPostUpdateNode extends SummaryNode, PostUpdateNode {
|
||||
private Node pre;
|
||||
|
||||
SummaryPostUpdateNode() { FlowSummaryImpl::Private::summaryPostUpdateNode(this, pre) }
|
||||
|
||||
override Node getPreUpdateNode() { result = pre }
|
||||
}
|
||||
@@ -4,7 +4,9 @@ private import DataFlowImplCommon
|
||||
private import DataFlowDispatch
|
||||
private import semmle.code.java.controlflow.Guards
|
||||
private import semmle.code.java.dataflow.SSA
|
||||
private import semmle.code.java.dataflow.TypeFlow
|
||||
private import ContainerFlow
|
||||
private import FlowSummaryImpl as FlowSummaryImpl
|
||||
import DataFlowNodes::Private
|
||||
|
||||
private newtype TReturnKind = TNormalReturnKind()
|
||||
|
||||
@@ -26,54 +28,6 @@ OutNode getAnOutNode(DataFlowCall call, ReturnKind kind) {
|
||||
kind = TNormalReturnKind()
|
||||
}
|
||||
|
||||
/**
|
||||
* A data flow node that occurs as the argument of a call and is passed as-is
|
||||
* to the callable. Arguments that are wrapped in an implicit varargs array
|
||||
* creation are not included, but the implicitly created array is.
|
||||
* Instance arguments are also included.
|
||||
*/
|
||||
class ArgumentNode extends Node {
|
||||
ArgumentNode() {
|
||||
exists(Argument arg | this.asExpr() = arg | not arg.isVararg())
|
||||
or
|
||||
this instanceof ImplicitVarargsArray
|
||||
or
|
||||
this = getInstanceArgument(_)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if this argument occurs at the given position in the given call.
|
||||
* The instance argument is considered to have index `-1`.
|
||||
*/
|
||||
predicate argumentOf(DataFlowCall call, int pos) {
|
||||
exists(Argument arg | this.asExpr() = arg | call = arg.getCall() and pos = arg.getPosition())
|
||||
or
|
||||
call = this.(ImplicitVarargsArray).getCall() and
|
||||
pos = call.getCallee().getNumberOfParameters() - 1
|
||||
or
|
||||
pos = -1 and this = getInstanceArgument(call)
|
||||
}
|
||||
|
||||
/** Gets the call in which this node is an argument. */
|
||||
DataFlowCall getCall() { this.argumentOf(result, _) }
|
||||
}
|
||||
|
||||
/** A data flow node that occurs as the result of a `ReturnStmt`. */
|
||||
class ReturnNode extends ExprNode {
|
||||
ReturnNode() { exists(ReturnStmt ret | this.getExpr() = ret.getResult()) }
|
||||
|
||||
/** Gets the kind of this returned value. */
|
||||
ReturnKind getKind() { any() }
|
||||
}
|
||||
|
||||
/** A data flow node that represents the output of a call. */
|
||||
class OutNode extends ExprNode {
|
||||
OutNode() { this.getExpr() instanceof MethodAccess }
|
||||
|
||||
/** Gets the underlying call. */
|
||||
DataFlowCall getCall() { result = this.getExpr() }
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if data can flow from `node1` to `node2` through a static field.
|
||||
*/
|
||||
@@ -131,8 +85,10 @@ private predicate instanceFieldAssign(Expr src, FieldAccess fa) {
|
||||
|
||||
private newtype TContent =
|
||||
TFieldContent(InstanceField f) or
|
||||
TArrayContent() or
|
||||
TCollectionContent() or
|
||||
TArrayContent()
|
||||
TMapKeyContent() or
|
||||
TMapValueContent()
|
||||
|
||||
/**
|
||||
* A reference contained in an object. Examples include instance fields, the
|
||||
@@ -147,7 +103,7 @@ class Content extends TContent {
|
||||
}
|
||||
}
|
||||
|
||||
private class FieldContent extends Content, TFieldContent {
|
||||
class FieldContent extends Content, TFieldContent {
|
||||
InstanceField f;
|
||||
|
||||
FieldContent() { this = TFieldContent(f) }
|
||||
@@ -161,12 +117,20 @@ private class FieldContent extends Content, TFieldContent {
|
||||
}
|
||||
}
|
||||
|
||||
private class CollectionContent extends Content, TCollectionContent {
|
||||
override string toString() { result = "collection" }
|
||||
class ArrayContent extends Content, TArrayContent {
|
||||
override string toString() { result = "[]" }
|
||||
}
|
||||
|
||||
private class ArrayContent extends Content, TArrayContent {
|
||||
override string toString() { result = "array" }
|
||||
class CollectionContent extends Content, TCollectionContent {
|
||||
override string toString() { result = "<element>" }
|
||||
}
|
||||
|
||||
class MapKeyContent extends Content, TMapKeyContent {
|
||||
override string toString() { result = "<map.key>" }
|
||||
}
|
||||
|
||||
class MapValueContent extends Content, TMapValueContent {
|
||||
override string toString() { result = "<map.value>" }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -174,12 +138,16 @@ private class ArrayContent extends Content, TArrayContent {
|
||||
* Thus, `node2` references an object with a field `f` that contains the
|
||||
* value of `node1`.
|
||||
*/
|
||||
predicate storeStep(Node node1, Content f, PostUpdateNode node2) {
|
||||
predicate storeStep(Node node1, Content f, Node node2) {
|
||||
exists(FieldAccess fa |
|
||||
instanceFieldAssign(node1.asExpr(), fa) and
|
||||
node2.getPreUpdateNode() = getFieldQualifier(fa) and
|
||||
node2.(PostUpdateNode).getPreUpdateNode() = getFieldQualifier(fa) and
|
||||
f.(FieldContent).getField() = fa.getField()
|
||||
)
|
||||
or
|
||||
f instanceof ArrayContent and arrayStoreStep(node1, node2)
|
||||
or
|
||||
FlowSummaryImpl::Private::Steps::summaryStoreStep(node1, f, node2)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -205,6 +173,12 @@ predicate readStep(Node node1, Content f, Node node2) {
|
||||
node1.asExpr() = get.getQualifier() and
|
||||
node2.asExpr() = get
|
||||
)
|
||||
or
|
||||
f instanceof ArrayContent and arrayReadStep(node1, node2, _)
|
||||
or
|
||||
f instanceof CollectionContent and collectionReadStep(node1, node2)
|
||||
or
|
||||
FlowSummaryImpl::Private::Steps::summaryReadStep(node1, f, node2)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -213,7 +187,14 @@ predicate readStep(Node node1, Content f, Node node2) {
|
||||
* in `x.f = newValue`.
|
||||
*/
|
||||
predicate clearsContent(Node n, Content c) {
|
||||
n = any(PostUpdateNode pun | storeStep(_, c, pun)).getPreUpdateNode()
|
||||
c instanceof FieldContent and
|
||||
(
|
||||
n = any(PostUpdateNode pun | storeStep(_, c, pun)).getPreUpdateNode()
|
||||
or
|
||||
FlowSummaryImpl::Private::Steps::summaryStoresIntoArg(c, n)
|
||||
)
|
||||
or
|
||||
FlowSummaryImpl::Private::Steps::summaryClearsContent(n, c)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -221,7 +202,7 @@ predicate clearsContent(Node n, Content c) {
|
||||
* possible flow. A single type is used for all numeric types to account for
|
||||
* numeric conversions, and otherwise the erasure is used.
|
||||
*/
|
||||
private DataFlowType getErasedRepr(Type t) {
|
||||
DataFlowType getErasedRepr(Type t) {
|
||||
exists(Type e | e = t.getErasure() |
|
||||
if e instanceof NumericOrCharType
|
||||
then result.(BoxedType).getPrimitiveType().getName() = "double"
|
||||
@@ -235,7 +216,11 @@ private DataFlowType getErasedRepr(Type t) {
|
||||
}
|
||||
|
||||
pragma[noinline]
|
||||
DataFlowType getNodeType(Node n) { result = getErasedRepr(n.getTypeBound()) }
|
||||
DataFlowType getNodeType(Node n) {
|
||||
result = getErasedRepr(n.getTypeBound())
|
||||
or
|
||||
result = FlowSummaryImpl::Private::summaryNodeType(n)
|
||||
}
|
||||
|
||||
/** Gets a string representation of a type returned by `getErasedRepr`. */
|
||||
string ppReprType(Type t) {
|
||||
@@ -306,7 +291,6 @@ private class ConstantBooleanArgumentNode extends ArgumentNode, ExprNode {
|
||||
/**
|
||||
* Holds if the node `n` is unreachable when the call context is `call`.
|
||||
*/
|
||||
cached
|
||||
predicate isUnreachableInCall(Node n, DataFlowCall call) {
|
||||
exists(
|
||||
ExplicitParameterNode paramNode, ConstantBooleanArgumentNode arg, SsaImplicitInit param,
|
||||
@@ -319,7 +303,9 @@ predicate isUnreachableInCall(Node n, DataFlowCall call) {
|
||||
// which is used in a guard
|
||||
param.getAUse() = guard and
|
||||
// which controls `n` with the opposite value of `arg`
|
||||
guard.controls(n.asExpr().getBasicBlock(), arg.getBooleanValue().booleanNot())
|
||||
guard
|
||||
.controls(n.asExpr().getBasicBlock(),
|
||||
pragma[only_bind_out](arg.getBooleanValue()).booleanNot())
|
||||
)
|
||||
}
|
||||
|
||||
@@ -337,7 +323,7 @@ predicate isImmutableOrUnobservable(Node n) {
|
||||
}
|
||||
|
||||
/** Holds if `n` should be hidden from path explanations. */
|
||||
predicate nodeIsHidden(Node n) { none() }
|
||||
predicate nodeIsHidden(Node n) { n instanceof SummaryNode }
|
||||
|
||||
class LambdaCallKind = Unit;
|
||||
|
||||
@@ -347,5 +333,5 @@ predicate lambdaCreation(Node creation, LambdaCallKind kind, DataFlowCallable c)
|
||||
/** Holds if `call` is a lambda call of kind `kind` where `receiver` is the lambda expression. */
|
||||
predicate lambdaCall(DataFlowCall call, LambdaCallKind kind, Node receiver) { none() }
|
||||
|
||||
/** Extra data-flow steps needed for lamba flow analysis. */
|
||||
/** Extra data-flow steps needed for lambda flow analysis. */
|
||||
predicate additionalLambdaFlowStep(Node nodeFrom, Node nodeTo, boolean preservesValue) { none() }
|
||||
|
||||
@@ -5,284 +5,15 @@
|
||||
private import java
|
||||
private import DataFlowPrivate
|
||||
private import semmle.code.java.dataflow.SSA
|
||||
private import semmle.code.java.dataflow.TypeFlow
|
||||
private import semmle.code.java.controlflow.Guards
|
||||
private import semmle.code.java.dataflow.ExternalFlow
|
||||
private import semmle.code.java.dataflow.FlowSteps
|
||||
import semmle.code.java.dataflow.InstanceAccess
|
||||
|
||||
cached
|
||||
private newtype TNode =
|
||||
TExprNode(Expr e) {
|
||||
not e.getType() instanceof VoidType and
|
||||
not e.getParent*() instanceof Annotation
|
||||
} or
|
||||
TExplicitParameterNode(Parameter p) { exists(p.getCallable().getBody()) } or
|
||||
TImplicitVarargsArray(Call c) {
|
||||
c.getCallee().isVarargs() and
|
||||
not exists(Argument arg | arg.getCall() = c and arg.isExplicitVarargsArray())
|
||||
} or
|
||||
TInstanceParameterNode(Callable c) { exists(c.getBody()) and not c.isStatic() } or
|
||||
TImplicitInstanceAccess(InstanceAccessExt ia) { not ia.isExplicit(_) } or
|
||||
TMallocNode(ClassInstanceExpr cie) or
|
||||
TExplicitExprPostUpdate(Expr e) {
|
||||
explicitInstanceArgument(_, e)
|
||||
or
|
||||
e instanceof Argument and not e.getType() instanceof ImmutableType
|
||||
or
|
||||
exists(FieldAccess fa | fa.getField() instanceof InstanceField and e = fa.getQualifier())
|
||||
or
|
||||
exists(ArrayAccess aa | e = aa.getArray())
|
||||
} or
|
||||
TImplicitExprPostUpdate(InstanceAccessExt ia) {
|
||||
implicitInstanceArgument(_, ia)
|
||||
or
|
||||
exists(FieldAccess fa |
|
||||
fa.getField() instanceof InstanceField and ia.isImplicitFieldQualifier(fa)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* An element, viewed as a node in a data flow graph. Either an expression,
|
||||
* a parameter, or an implicit varargs array creation.
|
||||
*/
|
||||
class Node extends TNode {
|
||||
/** Gets a textual representation of this element. */
|
||||
string toString() { none() }
|
||||
|
||||
/** Gets the source location for this element. */
|
||||
Location getLocation() { none() }
|
||||
|
||||
/** Gets the expression corresponding to this node, if any. */
|
||||
Expr asExpr() { result = this.(ExprNode).getExpr() }
|
||||
|
||||
/** Gets the parameter corresponding to this node, if any. */
|
||||
Parameter asParameter() { result = this.(ExplicitParameterNode).getParameter() }
|
||||
|
||||
/** Gets the type of this node. */
|
||||
Type getType() {
|
||||
result = this.asExpr().getType()
|
||||
or
|
||||
result = this.asParameter().getType()
|
||||
or
|
||||
exists(Parameter p |
|
||||
result = p.getType() and
|
||||
p.isVarargs() and
|
||||
p = this.(ImplicitVarargsArray).getCall().getCallee().getAParameter()
|
||||
)
|
||||
or
|
||||
result = this.(InstanceParameterNode).getCallable().getDeclaringType()
|
||||
or
|
||||
result = this.(ImplicitInstanceAccess).getInstanceAccess().getType()
|
||||
or
|
||||
result = this.(MallocNode).getClassInstanceExpr().getType()
|
||||
or
|
||||
result = this.(ImplicitPostUpdateNode).getPreUpdateNode().getType()
|
||||
}
|
||||
|
||||
private Callable getEnclosingCallableImpl() {
|
||||
result = this.asExpr().getEnclosingCallable() or
|
||||
result = this.asParameter().getCallable() or
|
||||
result = this.(ImplicitVarargsArray).getCall().getEnclosingCallable() or
|
||||
result = this.(InstanceParameterNode).getCallable() or
|
||||
result = this.(ImplicitInstanceAccess).getInstanceAccess().getEnclosingCallable() or
|
||||
result = this.(MallocNode).getClassInstanceExpr().getEnclosingCallable() or
|
||||
result = this.(ImplicitPostUpdateNode).getPreUpdateNode().getEnclosingCallableImpl()
|
||||
}
|
||||
|
||||
/** Gets the callable in which this node occurs. */
|
||||
Callable getEnclosingCallable() {
|
||||
result = unique(DataFlowCallable c | c = this.getEnclosingCallableImpl() | c)
|
||||
}
|
||||
|
||||
private Type getImprovedTypeBound() {
|
||||
exprTypeFlow(this.asExpr(), result, _) or
|
||||
result = this.(ImplicitPostUpdateNode).getPreUpdateNode().getImprovedTypeBound()
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets an upper bound on the type of this node.
|
||||
*/
|
||||
Type getTypeBound() {
|
||||
result = getImprovedTypeBound()
|
||||
or
|
||||
result = getType() and not exists(getImprovedTypeBound())
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if this element is at the specified location.
|
||||
* The location spans column `startcolumn` of line `startline` to
|
||||
* column `endcolumn` of line `endline` in file `filepath`.
|
||||
* For more information, see
|
||||
* [Locations](https://help.semmle.com/QL/learn-ql/ql/locations.html).
|
||||
*/
|
||||
predicate hasLocationInfo(
|
||||
string filepath, int startline, int startcolumn, int endline, int endcolumn
|
||||
) {
|
||||
getLocation().hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* An expression, viewed as a node in a data flow graph.
|
||||
*/
|
||||
class ExprNode extends Node, TExprNode {
|
||||
Expr expr;
|
||||
|
||||
ExprNode() { this = TExprNode(expr) }
|
||||
|
||||
override string toString() { result = expr.toString() }
|
||||
|
||||
override Location getLocation() { result = expr.getLocation() }
|
||||
|
||||
/** Gets the expression corresponding to this node. */
|
||||
Expr getExpr() { result = expr }
|
||||
}
|
||||
|
||||
/** Gets the node corresponding to `e`. */
|
||||
ExprNode exprNode(Expr e) { result.getExpr() = e }
|
||||
|
||||
/** An explicit or implicit parameter. */
|
||||
abstract class ParameterNode extends Node {
|
||||
/**
|
||||
* Holds if this node is the parameter of `c` at the specified (zero-based)
|
||||
* position. The implicit `this` parameter is considered to have index `-1`.
|
||||
*/
|
||||
abstract predicate isParameterOf(Callable c, int pos);
|
||||
}
|
||||
|
||||
/**
|
||||
* A parameter, viewed as a node in a data flow graph.
|
||||
*/
|
||||
class ExplicitParameterNode extends ParameterNode, TExplicitParameterNode {
|
||||
Parameter param;
|
||||
|
||||
ExplicitParameterNode() { this = TExplicitParameterNode(param) }
|
||||
|
||||
override string toString() { result = param.toString() }
|
||||
|
||||
override Location getLocation() { result = param.getLocation() }
|
||||
|
||||
/** Gets the parameter corresponding to this node. */
|
||||
Parameter getParameter() { result = param }
|
||||
|
||||
override predicate isParameterOf(Callable c, int pos) { c.getParameter(pos) = param }
|
||||
}
|
||||
|
||||
/** Gets the node corresponding to `p`. */
|
||||
ExplicitParameterNode parameterNode(Parameter p) { result.getParameter() = p }
|
||||
|
||||
/**
|
||||
* An implicit varargs array creation expression.
|
||||
*
|
||||
* A call `f(x1, x2)` to a method `f(A... xs)` desugars to `f(new A[]{x1, x2})`,
|
||||
* and this node corresponds to such an implicit array creation.
|
||||
*/
|
||||
class ImplicitVarargsArray extends Node, TImplicitVarargsArray {
|
||||
Call call;
|
||||
|
||||
ImplicitVarargsArray() { this = TImplicitVarargsArray(call) }
|
||||
|
||||
override string toString() { result = "new ..[] { .. }" }
|
||||
|
||||
override Location getLocation() { result = call.getLocation() }
|
||||
|
||||
/** Gets the call containing this varargs array creation argument. */
|
||||
Call getCall() { result = call }
|
||||
}
|
||||
|
||||
/**
|
||||
* An instance parameter for an instance method or constructor.
|
||||
*/
|
||||
class InstanceParameterNode extends ParameterNode, TInstanceParameterNode {
|
||||
Callable callable;
|
||||
|
||||
InstanceParameterNode() { this = TInstanceParameterNode(callable) }
|
||||
|
||||
override string toString() { result = "parameter this" }
|
||||
|
||||
override Location getLocation() { result = callable.getLocation() }
|
||||
|
||||
/** Gets the callable containing this `this` parameter. */
|
||||
Callable getCallable() { result = callable }
|
||||
|
||||
override predicate isParameterOf(Callable c, int pos) { callable = c and pos = -1 }
|
||||
}
|
||||
|
||||
/**
|
||||
* An implicit read of `this` or `A.this`.
|
||||
*/
|
||||
class ImplicitInstanceAccess extends Node, TImplicitInstanceAccess {
|
||||
InstanceAccessExt ia;
|
||||
|
||||
ImplicitInstanceAccess() { this = TImplicitInstanceAccess(ia) }
|
||||
|
||||
override string toString() { result = ia.toString() }
|
||||
|
||||
override Location getLocation() { result = ia.getLocation() }
|
||||
|
||||
InstanceAccessExt getInstanceAccess() { result = ia }
|
||||
}
|
||||
|
||||
/**
|
||||
* A node that corresponds to the value of a `ClassInstanceExpr` before the
|
||||
* constructor has run.
|
||||
*/
|
||||
private class MallocNode extends Node, TMallocNode {
|
||||
ClassInstanceExpr cie;
|
||||
|
||||
MallocNode() { this = TMallocNode(cie) }
|
||||
|
||||
override string toString() { result = cie.toString() + " [pre constructor]" }
|
||||
|
||||
override Location getLocation() { result = cie.getLocation() }
|
||||
|
||||
ClassInstanceExpr getClassInstanceExpr() { result = cie }
|
||||
}
|
||||
|
||||
/**
|
||||
* A node associated with an object after an operation that might have
|
||||
* changed its state.
|
||||
*
|
||||
* This can be either the argument to a callable after the callable returns
|
||||
* (which might have mutated the argument), or the qualifier of a field after
|
||||
* an update to the field.
|
||||
*
|
||||
* Nodes corresponding to AST elements, for example `ExprNode`, usually refer
|
||||
* to the value before the update with the exception of `ClassInstanceExpr`,
|
||||
* which represents the value after the constructor has run.
|
||||
*/
|
||||
abstract class PostUpdateNode extends Node {
|
||||
/**
|
||||
* Gets the node before the state update.
|
||||
*/
|
||||
abstract Node getPreUpdateNode();
|
||||
}
|
||||
|
||||
private class NewExpr extends PostUpdateNode, TExprNode {
|
||||
NewExpr() { exists(ClassInstanceExpr cie | this = TExprNode(cie)) }
|
||||
|
||||
override Node getPreUpdateNode() { this = TExprNode(result.(MallocNode).getClassInstanceExpr()) }
|
||||
}
|
||||
|
||||
/**
|
||||
* A `PostUpdateNode` that is not a `ClassInstanceExpr`.
|
||||
*/
|
||||
abstract private class ImplicitPostUpdateNode extends PostUpdateNode {
|
||||
override Location getLocation() { result = getPreUpdateNode().getLocation() }
|
||||
|
||||
override string toString() { result = getPreUpdateNode().toString() + " [post update]" }
|
||||
}
|
||||
|
||||
private class ExplicitExprPostUpdate extends ImplicitPostUpdateNode, TExplicitExprPostUpdate {
|
||||
override Node getPreUpdateNode() { this = TExplicitExprPostUpdate(result.asExpr()) }
|
||||
}
|
||||
|
||||
private class ImplicitExprPostUpdate extends ImplicitPostUpdateNode, TImplicitExprPostUpdate {
|
||||
override Node getPreUpdateNode() {
|
||||
this = TImplicitExprPostUpdate(result.(ImplicitInstanceAccess).getInstanceAccess())
|
||||
}
|
||||
}
|
||||
private import semmle.code.java.dataflow.FlowSummary
|
||||
private import semmle.code.java.dataflow.InstanceAccess
|
||||
private import FlowSummaryImpl as FlowSummaryImpl
|
||||
private import TaintTrackingUtil as TaintTrackingUtil
|
||||
import DataFlowNodes::Public
|
||||
import semmle.code.Unit
|
||||
|
||||
/** Holds if `n` is an access to an unqualified `this` at `cfgnode`. */
|
||||
private predicate thisAccess(Node n, ControlFlowNode cfgnode) {
|
||||
@@ -367,7 +98,13 @@ predicate hasNonlocalValue(FieldRead fr) {
|
||||
/**
|
||||
* Holds if data can flow from `node1` to `node2` in one local step.
|
||||
*/
|
||||
predicate localFlowStep(Node node1, Node node2) { simpleLocalFlowStep(node1, node2) }
|
||||
predicate localFlowStep(Node node1, Node node2) {
|
||||
simpleLocalFlowStep(node1, node2)
|
||||
or
|
||||
// Simple flow through library code is included in the exposed local
|
||||
// step relation, even though flow is technically inter-procedural
|
||||
FlowSummaryImpl::Private::Steps::summaryThroughStep(node1, node2, true)
|
||||
}
|
||||
|
||||
/**
|
||||
* INTERNAL: do not use.
|
||||
@@ -377,6 +114,7 @@ predicate localFlowStep(Node node1, Node node2) { simpleLocalFlowStep(node1, nod
|
||||
*/
|
||||
cached
|
||||
predicate simpleLocalFlowStep(Node node1, Node node2) {
|
||||
TaintTrackingUtil::forceCachingInSameStage() and
|
||||
// Variable flow steps through adjacent def-use and use-use pairs.
|
||||
exists(SsaExplicitUpdate upd |
|
||||
upd.getDefiningExpr().(VariableAssign).getSource() = node1.asExpr() or
|
||||
@@ -407,7 +145,7 @@ predicate simpleLocalFlowStep(Node node1, Node node2) {
|
||||
or
|
||||
node2.asExpr().(AssignExpr).getSource() = node1.asExpr()
|
||||
or
|
||||
summaryStep(node1, node2, "value")
|
||||
node2.asExpr().(ArrayCreationExpr).getInit() = node1.asExpr()
|
||||
or
|
||||
exists(MethodAccess ma, ValuePreservingMethod m, int argNo |
|
||||
ma.getCallee().getSourceDeclaration() = m and m.returnsValue(argNo)
|
||||
@@ -415,33 +153,8 @@ predicate simpleLocalFlowStep(Node node1, Node node2) {
|
||||
node2.asExpr() = ma and
|
||||
node1.(ArgumentNode).argumentOf(ma, argNo)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the node that occurs as the qualifier of `fa`.
|
||||
*/
|
||||
Node getFieldQualifier(FieldAccess fa) {
|
||||
fa.getField() instanceof InstanceField and
|
||||
(
|
||||
result.asExpr() = fa.getQualifier() or
|
||||
result.(ImplicitInstanceAccess).getInstanceAccess().isImplicitFieldQualifier(fa)
|
||||
)
|
||||
}
|
||||
|
||||
private predicate explicitInstanceArgument(Call call, Expr instarg) {
|
||||
call instanceof MethodAccess and instarg = call.getQualifier() and not call.getCallee().isStatic()
|
||||
}
|
||||
|
||||
private predicate implicitInstanceArgument(Call call, InstanceAccessExt ia) {
|
||||
ia.isImplicitMethodQualifier(call) or
|
||||
ia.isImplicitThisConstructorArgument(call)
|
||||
}
|
||||
|
||||
/** Gets the instance argument of a non-static call. */
|
||||
Node getInstanceArgument(Call call) {
|
||||
result.(MallocNode).getClassInstanceExpr() = call or
|
||||
explicitInstanceArgument(call, result.asExpr()) or
|
||||
implicitInstanceArgument(call, result.(ImplicitInstanceAccess).getInstanceAccess())
|
||||
or
|
||||
FlowSummaryImpl::Private::Steps::summaryLocalStep(node1, node2, true)
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -0,0 +1,825 @@
|
||||
/**
|
||||
* Provides classes and predicates for defining flow summaries.
|
||||
*
|
||||
* The definitions in this file are language-independent, and language-specific
|
||||
* definitions are passed in via the `DataFlowImplSpecific` and
|
||||
* `FlowSummaryImplSpecific` modules.
|
||||
*/
|
||||
|
||||
private import FlowSummaryImplSpecific
|
||||
private import DataFlowImplSpecific::Private
|
||||
private import DataFlowImplSpecific::Public
|
||||
private import DataFlowImplCommon as DataFlowImplCommon
|
||||
|
||||
/** Provides classes and predicates for defining flow summaries. */
|
||||
module Public {
|
||||
private import Private
|
||||
|
||||
/**
|
||||
* A component used in a flow summary.
|
||||
*
|
||||
* Either a parameter or an argument at a given position, a specific
|
||||
* content type, or a return kind.
|
||||
*/
|
||||
class SummaryComponent extends TSummaryComponent {
|
||||
/** Gets a textual representation of this summary component. */
|
||||
string toString() {
|
||||
exists(Content c | this = TContentSummaryComponent(c) and result = c.toString())
|
||||
or
|
||||
exists(int i | this = TParameterSummaryComponent(i) and result = "parameter " + i)
|
||||
or
|
||||
exists(int i | this = TArgumentSummaryComponent(i) and result = "argument " + i)
|
||||
or
|
||||
exists(ReturnKind rk | this = TReturnSummaryComponent(rk) and result = "return (" + rk + ")")
|
||||
}
|
||||
}
|
||||
|
||||
/** Provides predicates for constructing summary components. */
|
||||
module SummaryComponent {
|
||||
/** Gets a summary component for content `c`. */
|
||||
SummaryComponent content(Content c) { result = TContentSummaryComponent(c) }
|
||||
|
||||
/** Gets a summary component for parameter `i`. */
|
||||
SummaryComponent parameter(int i) { result = TParameterSummaryComponent(i) }
|
||||
|
||||
/** Gets a summary component for argument `i`. */
|
||||
SummaryComponent argument(int i) { result = TArgumentSummaryComponent(i) }
|
||||
|
||||
/** Gets a summary component for a return of kind `rk`. */
|
||||
SummaryComponent return(ReturnKind rk) { result = TReturnSummaryComponent(rk) }
|
||||
}
|
||||
|
||||
/**
|
||||
* A (non-empty) stack of summary components.
|
||||
*
|
||||
* A stack is used to represent where data is read from (input) or where it
|
||||
* is written to (output). For example, an input stack `[Field f, Argument 0]`
|
||||
* means that data is read from field `f` from the `0`th argument, while an
|
||||
* output stack `[Field g, Return]` means that data is written to the field
|
||||
* `g` of the returned object.
|
||||
*/
|
||||
class SummaryComponentStack extends TSummaryComponentStack {
|
||||
/** Gets the head of this stack. */
|
||||
SummaryComponent head() {
|
||||
this = TSingletonSummaryComponentStack(result) or
|
||||
this = TConsSummaryComponentStack(result, _)
|
||||
}
|
||||
|
||||
/** Gets the tail of this stack, if any. */
|
||||
SummaryComponentStack tail() { this = TConsSummaryComponentStack(_, result) }
|
||||
|
||||
/** Gets the length of this stack. */
|
||||
int length() {
|
||||
this = TSingletonSummaryComponentStack(_) and result = 1
|
||||
or
|
||||
result = 1 + this.tail().length()
|
||||
}
|
||||
|
||||
/** Gets the stack obtained by dropping the first `i` elements, if any. */
|
||||
SummaryComponentStack drop(int i) {
|
||||
i = 0 and result = this
|
||||
or
|
||||
result = this.tail().drop(i - 1)
|
||||
}
|
||||
|
||||
/** Holds if this stack contains summary component `c`. */
|
||||
predicate contains(SummaryComponent c) { c = this.drop(_).head() }
|
||||
|
||||
/** Gets a textual representation of this stack. */
|
||||
string toString() {
|
||||
exists(SummaryComponent head, SummaryComponentStack tail |
|
||||
head = this.head() and
|
||||
tail = this.tail() and
|
||||
result = head + " of " + tail
|
||||
)
|
||||
or
|
||||
exists(SummaryComponent c |
|
||||
this = TSingletonSummaryComponentStack(c) and
|
||||
result = c.toString()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/** Provides predicates for constructing stacks of summary components. */
|
||||
module SummaryComponentStack {
|
||||
/** Gets a singleton stack containing `c`. */
|
||||
SummaryComponentStack singleton(SummaryComponent c) {
|
||||
result = TSingletonSummaryComponentStack(c)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the stack obtained by pushing `head` onto `tail`.
|
||||
*
|
||||
* Make sure to override `RequiredSummaryComponentStack::required()` in order
|
||||
* to ensure that the constructed stack exists.
|
||||
*/
|
||||
SummaryComponentStack push(SummaryComponent head, SummaryComponentStack tail) {
|
||||
result = TConsSummaryComponentStack(head, tail)
|
||||
}
|
||||
|
||||
/** Gets a singleton stack for argument `i`. */
|
||||
SummaryComponentStack argument(int i) { result = singleton(SummaryComponent::argument(i)) }
|
||||
|
||||
/** Gets a singleton stack representing a return of kind `rk`. */
|
||||
SummaryComponentStack return(ReturnKind rk) { result = singleton(SummaryComponent::return(rk)) }
|
||||
}
|
||||
|
||||
/**
|
||||
* A class that exists for QL technical reasons only (the IPA type used
|
||||
* to represent component stacks needs to be bounded).
|
||||
*/
|
||||
abstract class RequiredSummaryComponentStack extends SummaryComponentStack {
|
||||
/**
|
||||
* Holds if the stack obtained by pushing `head` onto `tail` is required.
|
||||
*/
|
||||
abstract predicate required(SummaryComponent c);
|
||||
}
|
||||
|
||||
/** A callable with a flow summary. */
|
||||
abstract class SummarizedCallable extends DataFlowCallable {
|
||||
/**
|
||||
* Holds if data may flow from `input` to `output` through this callable.
|
||||
*
|
||||
* `preservesValue` indicates whether this is a value-preserving step
|
||||
* or a taint-step.
|
||||
*
|
||||
* Input specifications are restricted to stacks that end with
|
||||
* `SummaryComponent::argument(_)`, preceded by zero or more
|
||||
* `SummaryComponent::return(_)` or `SummaryComponent::content(_)` components.
|
||||
*
|
||||
* Output specifications are restricted to stacks that end with
|
||||
* `SummaryComponent::return(_)` or `SummaryComponent::argument(_)`.
|
||||
*
|
||||
* Output stacks ending with `SummaryComponent::return(_)` can be preceded by zero
|
||||
* or more `SummaryComponent::content(_)` components.
|
||||
*
|
||||
* Output stacks ending with `SummaryComponent::argument(_)` can be preceded by an
|
||||
* optional `SummaryComponent::parameter(_)` component, which in turn can be preceded
|
||||
* by zero or more `SummaryComponent::content(_)` components.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
predicate propagatesFlow(
|
||||
SummaryComponentStack input, SummaryComponentStack output, boolean preservesValue
|
||||
) {
|
||||
none()
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if values stored inside `content` are cleared on objects passed as
|
||||
* the `i`th argument to this callable.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
predicate clearsContent(int i, Content content) { none() }
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides predicates for compiling flow summaries down to atomic local steps,
|
||||
* read steps, and store steps.
|
||||
*/
|
||||
module Private {
|
||||
private import Public
|
||||
|
||||
newtype TSummaryComponent =
|
||||
TContentSummaryComponent(Content c) or
|
||||
TParameterSummaryComponent(int i) { parameterPosition(i) } or
|
||||
TArgumentSummaryComponent(int i) { parameterPosition(i) } or
|
||||
TReturnSummaryComponent(ReturnKind rk)
|
||||
|
||||
newtype TSummaryComponentStack =
|
||||
TSingletonSummaryComponentStack(SummaryComponent c) or
|
||||
TConsSummaryComponentStack(SummaryComponent head, SummaryComponentStack tail) {
|
||||
tail.(RequiredSummaryComponentStack).required(head)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate summary(
|
||||
SummarizedCallable c, SummaryComponentStack input, SummaryComponentStack output,
|
||||
boolean preservesValue
|
||||
) {
|
||||
c.propagatesFlow(input, output, preservesValue)
|
||||
}
|
||||
|
||||
private newtype TSummaryNodeState =
|
||||
TSummaryNodeInputState(SummaryComponentStack s) {
|
||||
exists(SummaryComponentStack input |
|
||||
summary(_, input, _, _) and
|
||||
s = input.drop(_)
|
||||
)
|
||||
} or
|
||||
TSummaryNodeOutputState(SummaryComponentStack s) {
|
||||
exists(SummaryComponentStack output |
|
||||
summary(_, _, output, _) and
|
||||
s = output.drop(_)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* A state used to break up (complex) flow summaries into atomic flow steps.
|
||||
* For a flow summary
|
||||
*
|
||||
* ```ql
|
||||
* propagatesFlow(
|
||||
* SummaryComponentStack input, SummaryComponentStack output, boolean preservesValue
|
||||
* )
|
||||
* ```
|
||||
*
|
||||
* the following states are used:
|
||||
*
|
||||
* - `TSummaryNodeInputState(SummaryComponentStack s)`:
|
||||
* this state represents that the components in `s` _have been read_ from the
|
||||
* input.
|
||||
* - `TSummaryNodeOutputState(SummaryComponentStack s)`:
|
||||
* this state represents that the components in `s` _remain to be written_ to
|
||||
* the output.
|
||||
*/
|
||||
class SummaryNodeState extends TSummaryNodeState {
|
||||
/** Holds if this state is a valid input state for `c`. */
|
||||
pragma[nomagic]
|
||||
predicate isInputState(SummarizedCallable c, SummaryComponentStack s) {
|
||||
this = TSummaryNodeInputState(s) and
|
||||
exists(SummaryComponentStack input |
|
||||
summary(c, input, _, _) and
|
||||
s = input.drop(_)
|
||||
)
|
||||
}
|
||||
|
||||
/** Holds if this state is a valid output state for `c`. */
|
||||
pragma[nomagic]
|
||||
predicate isOutputState(SummarizedCallable c, SummaryComponentStack s) {
|
||||
this = TSummaryNodeOutputState(s) and
|
||||
exists(SummaryComponentStack output |
|
||||
summary(c, _, output, _) and
|
||||
s = output.drop(_)
|
||||
)
|
||||
}
|
||||
|
||||
/** Gets a textual representation of this state. */
|
||||
string toString() {
|
||||
exists(SummaryComponentStack s |
|
||||
this = TSummaryNodeInputState(s) and
|
||||
result = "read: " + s
|
||||
)
|
||||
or
|
||||
exists(SummaryComponentStack s |
|
||||
this = TSummaryNodeOutputState(s) and
|
||||
result = "to write: " + s
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `state` represents having read the `i`th argument for `c`. In this case
|
||||
* we are not synthesizing a data-flow node, but instead assume that a relevant
|
||||
* parameter node already exists.
|
||||
*/
|
||||
private predicate parameterReadState(SummarizedCallable c, SummaryNodeState state, int i) {
|
||||
state.isInputState(c, SummaryComponentStack::argument(i))
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if a synthesized summary node is needed for the state `state` in summarized
|
||||
* callable `c`.
|
||||
*/
|
||||
predicate summaryNodeRange(SummarizedCallable c, SummaryNodeState state) {
|
||||
state.isInputState(c, _) and
|
||||
not parameterReadState(c, state, _)
|
||||
or
|
||||
state.isOutputState(c, _)
|
||||
}
|
||||
|
||||
pragma[noinline]
|
||||
private Node summaryNodeInputState(SummarizedCallable c, SummaryComponentStack s) {
|
||||
exists(SummaryNodeState state | state.isInputState(c, s) |
|
||||
result = summaryNode(c, state)
|
||||
or
|
||||
exists(int i |
|
||||
parameterReadState(c, state, i) and
|
||||
result.(ParameterNode).isParameterOf(c, i)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[noinline]
|
||||
private Node summaryNodeOutputState(SummarizedCallable c, SummaryComponentStack s) {
|
||||
exists(SummaryNodeState state |
|
||||
state.isOutputState(c, s) and
|
||||
result = summaryNode(c, state)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if a write targets `post`, which is a post-update node for the `i`th
|
||||
* parameter of `c`.
|
||||
*/
|
||||
private predicate isParameterPostUpdate(Node post, SummarizedCallable c, int i) {
|
||||
post = summaryNodeOutputState(c, SummaryComponentStack::argument(i))
|
||||
}
|
||||
|
||||
/** Holds if a parameter node is required for the `i`th parameter of `c`. */
|
||||
predicate summaryParameterNodeRange(SummarizedCallable c, int i) {
|
||||
parameterReadState(c, _, i)
|
||||
or
|
||||
isParameterPostUpdate(_, c, i)
|
||||
}
|
||||
|
||||
private predicate callbackOutput(
|
||||
SummarizedCallable c, SummaryComponentStack s, Node receiver, ReturnKind rk
|
||||
) {
|
||||
any(SummaryNodeState state).isInputState(c, s) and
|
||||
s.head() = TReturnSummaryComponent(rk) and
|
||||
receiver = summaryNodeInputState(c, s.drop(1))
|
||||
}
|
||||
|
||||
private Node pre(Node post) {
|
||||
summaryPostUpdateNode(post, result)
|
||||
or
|
||||
not summaryPostUpdateNode(post, _) and
|
||||
result = post
|
||||
}
|
||||
|
||||
private predicate callbackInput(
|
||||
SummarizedCallable c, SummaryComponentStack s, Node receiver, int i
|
||||
) {
|
||||
any(SummaryNodeState state).isOutputState(c, s) and
|
||||
s.head() = TParameterSummaryComponent(i) and
|
||||
receiver = pre(summaryNodeOutputState(c, s.drop(1)))
|
||||
}
|
||||
|
||||
/** Holds if a call targeting `receiver` should be synthesized inside `c`. */
|
||||
predicate summaryCallbackRange(SummarizedCallable c, Node receiver) {
|
||||
callbackOutput(c, _, receiver, _)
|
||||
or
|
||||
callbackInput(c, _, receiver, _)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the type of synthesized summary node `n`.
|
||||
*
|
||||
* The type is computed based on the language-specific predicates
|
||||
* `getContentType()`, `getReturnType()`, `getCallbackParameterType()`, and
|
||||
* `getCallbackReturnType()`.
|
||||
*/
|
||||
DataFlowType summaryNodeType(Node n) {
|
||||
exists(Node pre |
|
||||
summaryPostUpdateNode(n, pre) and
|
||||
result = getNodeType(pre)
|
||||
)
|
||||
or
|
||||
exists(SummarizedCallable c, SummaryComponentStack s, SummaryComponent head | head = s.head() |
|
||||
n = summaryNodeInputState(c, s) and
|
||||
(
|
||||
exists(Content cont |
|
||||
head = TContentSummaryComponent(cont) and result = getContentType(cont)
|
||||
)
|
||||
or
|
||||
exists(ReturnKind rk |
|
||||
head = TReturnSummaryComponent(rk) and
|
||||
result = getCallbackReturnType(getNodeType(summaryNodeInputState(c, s.drop(1))), rk)
|
||||
)
|
||||
)
|
||||
or
|
||||
n = summaryNodeOutputState(c, s) and
|
||||
(
|
||||
exists(Content cont |
|
||||
head = TContentSummaryComponent(cont) and result = getContentType(cont)
|
||||
)
|
||||
or
|
||||
s.length() = 1 and
|
||||
exists(ReturnKind rk |
|
||||
head = TReturnSummaryComponent(rk) and
|
||||
result = getReturnType(c, rk)
|
||||
)
|
||||
or
|
||||
exists(int i | head = TParameterSummaryComponent(i) |
|
||||
result = getCallbackParameterType(getNodeType(summaryNodeOutputState(c, s.drop(1))), i)
|
||||
)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
/** Holds if summary node `out` contains output of kind `rk` from call `c`. */
|
||||
predicate summaryOutNode(DataFlowCall c, Node out, ReturnKind rk) {
|
||||
exists(SummarizedCallable callable, SummaryComponentStack s, Node receiver |
|
||||
callbackOutput(callable, s, receiver, rk) and
|
||||
out = summaryNodeInputState(callable, s) and
|
||||
c = summaryDataFlowCall(receiver)
|
||||
)
|
||||
}
|
||||
|
||||
/** Holds if summary node `arg` is the `i`th argument of call `c`. */
|
||||
predicate summaryArgumentNode(DataFlowCall c, Node arg, int i) {
|
||||
exists(SummarizedCallable callable, SummaryComponentStack s, Node receiver |
|
||||
callbackInput(callable, s, receiver, i) and
|
||||
arg = summaryNodeOutputState(callable, s) and
|
||||
c = summaryDataFlowCall(receiver)
|
||||
)
|
||||
}
|
||||
|
||||
/** Holds if summary node `post` is a post-update node with pre-update node `pre`. */
|
||||
predicate summaryPostUpdateNode(Node post, ParameterNode pre) {
|
||||
exists(SummarizedCallable c, int i |
|
||||
isParameterPostUpdate(post, c, i) and
|
||||
pre.isParameterOf(c, i)
|
||||
)
|
||||
}
|
||||
|
||||
/** Holds if summary node `ret` is a return node of kind `rk`. */
|
||||
predicate summaryReturnNode(Node ret, ReturnKind rk) {
|
||||
exists(SummarizedCallable callable, SummaryComponentStack s |
|
||||
ret = summaryNodeOutputState(callable, s) and
|
||||
s = TSingletonSummaryComponentStack(TReturnSummaryComponent(rk))
|
||||
)
|
||||
}
|
||||
|
||||
/** Provides a compilation of flow summaries to atomic data-flow steps. */
|
||||
module Steps {
|
||||
/**
|
||||
* Holds if there is a local step from `pred` to `succ`, which is synthesized
|
||||
* from a flow summary.
|
||||
*/
|
||||
predicate summaryLocalStep(Node pred, Node succ, boolean preservesValue) {
|
||||
exists(
|
||||
SummarizedCallable c, SummaryComponentStack inputContents,
|
||||
SummaryComponentStack outputContents
|
||||
|
|
||||
summary(c, inputContents, outputContents, preservesValue) and
|
||||
pred = summaryNodeInputState(c, inputContents) and
|
||||
succ = summaryNodeOutputState(c, outputContents)
|
||||
|
|
||||
preservesValue = true
|
||||
or
|
||||
preservesValue = false and not summary(c, inputContents, outputContents, true)
|
||||
)
|
||||
or
|
||||
// If flow through a method updates a parameter from some input A, and that
|
||||
// parameter also is returned through B, then we'd like a combined flow from A
|
||||
// to B as well. As an example, this simplifies modeling of fluent methods:
|
||||
// for `StringBuilder.append(x)` with a specified value flow from qualifier to
|
||||
// return value and taint flow from argument 0 to the qualifier, then this
|
||||
// allows us to infer taint flow from argument 0 to the return value.
|
||||
summaryPostUpdateNode(pred, succ) and preservesValue = true
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if there is a read step of content `c` from `pred` to `succ`, which
|
||||
* is synthesized from a flow summary.
|
||||
*/
|
||||
predicate summaryReadStep(Node pred, Content c, Node succ) {
|
||||
exists(SummarizedCallable sc, SummaryComponentStack s |
|
||||
pred = summaryNodeInputState(sc, s.drop(1)) and
|
||||
succ = summaryNodeInputState(sc, s) and
|
||||
SummaryComponent::content(c) = s.head()
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if there is a store step of content `c` from `pred` to `succ`, which
|
||||
* is synthesized from a flow summary.
|
||||
*/
|
||||
predicate summaryStoreStep(Node pred, Content c, Node succ) {
|
||||
exists(SummarizedCallable sc, SummaryComponentStack s |
|
||||
pred = summaryNodeOutputState(sc, s) and
|
||||
succ = summaryNodeOutputState(sc, s.drop(1)) and
|
||||
SummaryComponent::content(c) = s.head()
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if values stored inside content `c` are cleared when passed as
|
||||
* input of type `input` in `call`.
|
||||
*/
|
||||
predicate summaryClearsContent(ArgumentNode arg, Content c) {
|
||||
exists(DataFlowCall call, int i |
|
||||
viableCallable(call).(SummarizedCallable).clearsContent(i, c) and
|
||||
arg.argumentOf(call, i)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private ParameterNode summaryArgParam(
|
||||
ArgumentNode arg, DataFlowImplCommon::ReturnKindExt rk, DataFlowImplCommon::OutNodeExt out
|
||||
) {
|
||||
exists(DataFlowCall call, int pos, SummarizedCallable callable |
|
||||
arg.argumentOf(call, pos) and
|
||||
viableCallable(call) = callable and
|
||||
result.isParameterOf(callable, pos) and
|
||||
out = rk.getAnOutNode(call)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `arg` flows to `out` using a simple flow summary, that is, a flow
|
||||
* summary without reads and stores.
|
||||
*
|
||||
* NOTE: This step should not be used in global data-flow/taint-tracking, but may
|
||||
* be useful to include in the exposed local data-flow/taint-tracking relations.
|
||||
*/
|
||||
predicate summaryThroughStep(ArgumentNode arg, Node out, boolean preservesValue) {
|
||||
exists(DataFlowImplCommon::ReturnKindExt rk, DataFlowImplCommon::ReturnNodeExt ret |
|
||||
summaryLocalStep(summaryArgParam(arg, rk, out), ret, preservesValue) and
|
||||
ret.getKind() = rk
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if there is a read(+taint) of `c` from `arg` to `out` using a
|
||||
* flow summary.
|
||||
*
|
||||
* NOTE: This step should not be used in global data-flow/taint-tracking, but may
|
||||
* be useful to include in the exposed local data-flow/taint-tracking relations.
|
||||
*/
|
||||
predicate summaryGetterStep(ArgumentNode arg, Content c, Node out) {
|
||||
exists(DataFlowImplCommon::ReturnKindExt rk, Node mid, DataFlowImplCommon::ReturnNodeExt ret |
|
||||
summaryReadStep(summaryArgParam(arg, rk, out), c, mid) and
|
||||
summaryLocalStep(mid, ret, _) and
|
||||
ret.getKind() = rk
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if there is a (taint+)store of `arg` into content `c` of `out` using a
|
||||
* flow summary.
|
||||
*
|
||||
* NOTE: This step should not be used in global data-flow/taint-tracking, but may
|
||||
* be useful to include in the exposed local data-flow/taint-tracking relations.
|
||||
*/
|
||||
predicate summarySetterStep(ArgumentNode arg, Content c, Node out) {
|
||||
exists(DataFlowImplCommon::ReturnKindExt rk, Node mid, DataFlowImplCommon::ReturnNodeExt ret |
|
||||
summaryLocalStep(summaryArgParam(arg, rk, out), mid, _) and
|
||||
summaryStoreStep(mid, c, ret) and
|
||||
ret.getKind() = rk
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if data is written into content `c` of argument `arg` using a flow summary.
|
||||
*
|
||||
* Depending on the type of `c`, this predicate may be relevant to include in the
|
||||
* definition of `clearsContent()`.
|
||||
*/
|
||||
predicate summaryStoresIntoArg(Content c, Node arg) {
|
||||
exists(
|
||||
DataFlowImplCommon::ParamUpdateReturnKind rk, DataFlowImplCommon::ReturnNodeExt ret,
|
||||
PostUpdateNode out
|
||||
|
|
||||
exists(DataFlowCall call, SummarizedCallable callable |
|
||||
DataFlowImplCommon::getNodeEnclosingCallable(ret) = callable and
|
||||
viableCallable(call) = callable and
|
||||
summaryStoreStep(_, c, ret) and
|
||||
ret.getKind() = pragma[only_bind_into](rk) and
|
||||
out = rk.getAnOutNode(call) and
|
||||
arg = out.getPreUpdateNode()
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides a means of translating externally (e.g., CSV) defined flow
|
||||
* summaries into a `SummarizedCallable`s.
|
||||
*/
|
||||
module External {
|
||||
/** Holds if `spec` is a relevant external specification. */
|
||||
private predicate relevantSpec(string spec) {
|
||||
summaryElement(_, spec, _, _) or
|
||||
summaryElement(_, _, spec, _) or
|
||||
sourceElement(_, spec, _) or
|
||||
sinkElement(_, spec, _)
|
||||
}
|
||||
|
||||
/** Holds if the `n`th component of specification `s` is `c`. */
|
||||
predicate specSplit(string s, string c, int n) { relevantSpec(s) and s.splitAt(" of ", n) = c }
|
||||
|
||||
/** Holds if specification `s` has length `len`. */
|
||||
predicate specLength(string s, int len) { len = 1 + max(int n | specSplit(s, _, n)) }
|
||||
|
||||
/** Gets the last component of specification `s`. */
|
||||
string specLast(string s) {
|
||||
exists(int len |
|
||||
specLength(s, len) and
|
||||
specSplit(s, result, len - 1)
|
||||
)
|
||||
}
|
||||
|
||||
/** Holds if specification component `c` parses as parameter `n`. */
|
||||
predicate parseParam(string c, int n) {
|
||||
specSplit(_, c, _) and
|
||||
(
|
||||
c.regexpCapture("Parameter\\[([-0-9]+)\\]", 1).toInt() = n
|
||||
or
|
||||
exists(int n1, int n2 |
|
||||
c.regexpCapture("Parameter\\[([-0-9]+)\\.\\.([0-9]+)\\]", 1).toInt() = n1 and
|
||||
c.regexpCapture("Parameter\\[([-0-9]+)\\.\\.([0-9]+)\\]", 2).toInt() = n2 and
|
||||
n = [n1 .. n2]
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
/** Holds if specification component `c` parses as argument `n`. */
|
||||
predicate parseArg(string c, int n) {
|
||||
specSplit(_, c, _) and
|
||||
(
|
||||
c.regexpCapture("Argument\\[([-0-9]+)\\]", 1).toInt() = n
|
||||
or
|
||||
exists(int n1, int n2 |
|
||||
c.regexpCapture("Argument\\[([-0-9]+)\\.\\.([0-9]+)\\]", 1).toInt() = n1 and
|
||||
c.regexpCapture("Argument\\[([-0-9]+)\\.\\.([0-9]+)\\]", 2).toInt() = n2 and
|
||||
n = [n1 .. n2]
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
private SummaryComponent interpretComponent(string c) {
|
||||
specSplit(_, c, _) and
|
||||
(
|
||||
exists(int pos | parseArg(c, pos) and result = SummaryComponent::argument(pos))
|
||||
or
|
||||
exists(int pos | parseParam(c, pos) and result = SummaryComponent::parameter(pos))
|
||||
or
|
||||
c = "ReturnValue" and result = SummaryComponent::return(getReturnValueKind())
|
||||
or
|
||||
result = interpretComponentSpecific(c)
|
||||
)
|
||||
}
|
||||
|
||||
private predicate interpretSpec(string spec, int idx, SummaryComponentStack stack) {
|
||||
exists(string c |
|
||||
relevantSpec(spec) and
|
||||
specLength(spec, idx + 1) and
|
||||
specSplit(spec, c, idx) and
|
||||
stack = SummaryComponentStack::singleton(interpretComponent(c))
|
||||
)
|
||||
or
|
||||
exists(SummaryComponent head, SummaryComponentStack tail |
|
||||
interpretSpec(spec, idx, head, tail) and
|
||||
stack = SummaryComponentStack::push(head, tail)
|
||||
)
|
||||
}
|
||||
|
||||
private predicate interpretSpec(
|
||||
string output, int idx, SummaryComponent head, SummaryComponentStack tail
|
||||
) {
|
||||
exists(string c |
|
||||
interpretSpec(output, idx + 1, tail) and
|
||||
specSplit(output, c, idx) and
|
||||
head = interpretComponent(c)
|
||||
)
|
||||
}
|
||||
|
||||
private class MkStack extends RequiredSummaryComponentStack {
|
||||
MkStack() { interpretSpec(_, _, _, this) }
|
||||
|
||||
override predicate required(SummaryComponent c) { interpretSpec(_, _, c, this) }
|
||||
}
|
||||
|
||||
private class SummarizedCallableExternal extends SummarizedCallable {
|
||||
SummarizedCallableExternal() { summaryElement(this, _, _, _) }
|
||||
|
||||
override predicate propagatesFlow(
|
||||
SummaryComponentStack input, SummaryComponentStack output, boolean preservesValue
|
||||
) {
|
||||
exists(string inSpec, string outSpec, string kind |
|
||||
summaryElement(this, inSpec, outSpec, kind) and
|
||||
interpretSpec(inSpec, 0, input) and
|
||||
interpretSpec(outSpec, 0, output)
|
||||
|
|
||||
kind = "value" and preservesValue = true
|
||||
or
|
||||
kind = "taint" and preservesValue = false
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/** Holds if component `c` of specification `spec` cannot be parsed. */
|
||||
predicate invalidSpecComponent(string spec, string c) {
|
||||
specSplit(spec, c, _) and
|
||||
not exists(interpretComponent(c))
|
||||
}
|
||||
|
||||
private predicate inputNeedsReference(string c) {
|
||||
c = "Argument" or
|
||||
parseArg(c, _)
|
||||
}
|
||||
|
||||
private predicate outputNeedsReference(string c) {
|
||||
c = "Argument" or
|
||||
parseArg(c, _) or
|
||||
c = "ReturnValue"
|
||||
}
|
||||
|
||||
private predicate sourceElementRef(InterpretNode ref, string output, string kind) {
|
||||
exists(SourceOrSinkElement e |
|
||||
sourceElement(e, output, kind) and
|
||||
if outputNeedsReference(specLast(output))
|
||||
then e = ref.getCallTarget()
|
||||
else e = ref.asElement()
|
||||
)
|
||||
}
|
||||
|
||||
private predicate sinkElementRef(InterpretNode ref, string input, string kind) {
|
||||
exists(SourceOrSinkElement e |
|
||||
sinkElement(e, input, kind) and
|
||||
if inputNeedsReference(specLast(input))
|
||||
then e = ref.getCallTarget()
|
||||
else e = ref.asElement()
|
||||
)
|
||||
}
|
||||
|
||||
private predicate interpretOutput(string output, int idx, InterpretNode ref, InterpretNode node) {
|
||||
sourceElementRef(ref, output, _) and
|
||||
specLength(output, idx) and
|
||||
node = ref
|
||||
or
|
||||
exists(InterpretNode mid, string c |
|
||||
interpretOutput(output, idx + 1, ref, mid) and
|
||||
specSplit(output, c, idx)
|
||||
|
|
||||
exists(int pos |
|
||||
node.asNode()
|
||||
.(PostUpdateNode)
|
||||
.getPreUpdateNode()
|
||||
.(ArgumentNode)
|
||||
.argumentOf(mid.asCall(), pos)
|
||||
|
|
||||
c = "Argument" or parseArg(c, pos)
|
||||
)
|
||||
or
|
||||
exists(int pos | node.asNode().(ParameterNode).isParameterOf(mid.asCallable(), pos) |
|
||||
c = "Parameter" or parseParam(c, pos)
|
||||
)
|
||||
or
|
||||
c = "ReturnValue" and
|
||||
node.asNode() = getAnOutNode(mid.asCall(), getReturnValueKind())
|
||||
or
|
||||
interpretOutputSpecific(c, mid, node)
|
||||
)
|
||||
}
|
||||
|
||||
private predicate interpretInput(string input, int idx, InterpretNode ref, InterpretNode node) {
|
||||
sinkElementRef(ref, input, _) and
|
||||
specLength(input, idx) and
|
||||
node = ref
|
||||
or
|
||||
exists(InterpretNode mid, string c |
|
||||
interpretInput(input, idx + 1, ref, mid) and
|
||||
specSplit(input, c, idx)
|
||||
|
|
||||
exists(int pos | node.asNode().(ArgumentNode).argumentOf(mid.asCall(), pos) |
|
||||
c = "Argument" or parseArg(c, pos)
|
||||
)
|
||||
or
|
||||
exists(ReturnNode ret |
|
||||
c = "ReturnValue" and
|
||||
ret = node.asNode() and
|
||||
ret.getKind() = getReturnValueKind() and
|
||||
mid.asCallable() = DataFlowImplCommon::getNodeEnclosingCallable(ret)
|
||||
)
|
||||
or
|
||||
interpretInputSpecific(c, mid, node)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `node` is specified as a source with the given kind in a CSV flow
|
||||
* model.
|
||||
*/
|
||||
predicate isSourceNode(InterpretNode node, string kind) {
|
||||
exists(InterpretNode ref, string output |
|
||||
sourceElementRef(ref, output, kind) and
|
||||
interpretOutput(output, 0, ref, node)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `node` is specified as a sink with the given kind in a CSV flow
|
||||
* model.
|
||||
*/
|
||||
predicate isSinkNode(InterpretNode node, string kind) {
|
||||
exists(InterpretNode ref, string input |
|
||||
sinkElementRef(ref, input, kind) and
|
||||
interpretInput(input, 0, ref, node)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/** Provides a query predicate for outputting a set of relevant flow summaries. */
|
||||
module TestOutput {
|
||||
/** A flow summary to include in the `summary/3` query predicate. */
|
||||
abstract class RelevantSummarizedCallable extends SummarizedCallable {
|
||||
/** Gets the string representation of this callable used by `summary/3`. */
|
||||
string getFullString() { result = this.toString() }
|
||||
}
|
||||
|
||||
/** A query predicate for outputting flow summaries in QL tests. */
|
||||
query predicate summary(string callable, string flow, boolean preservesValue) {
|
||||
exists(
|
||||
RelevantSummarizedCallable c, SummaryComponentStack input, SummaryComponentStack output
|
||||
|
|
||||
callable = c.getFullString() and
|
||||
c.propagatesFlow(input, output, preservesValue) and
|
||||
flow = input + " -> " + output
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,175 @@
|
||||
/**
|
||||
* Provides Java specific classes and predicates for definining flow summaries.
|
||||
*/
|
||||
|
||||
private import java
|
||||
private import DataFlowPrivate
|
||||
private import DataFlowUtil
|
||||
private import FlowSummaryImpl::Private
|
||||
private import FlowSummaryImpl::Public
|
||||
private import semmle.code.java.dataflow.ExternalFlow
|
||||
|
||||
private module FlowSummaries {
|
||||
private import semmle.code.java.dataflow.FlowSummary as F
|
||||
}
|
||||
|
||||
/** Holds is `i` is a valid parameter position. */
|
||||
predicate parameterPosition(int i) { i in [-1 .. any(Parameter p).getPosition()] }
|
||||
|
||||
/** Gets the synthesized summary data-flow node for the given values. */
|
||||
Node summaryNode(SummarizedCallable c, SummaryNodeState state) { result = getSummaryNode(c, state) }
|
||||
|
||||
/** Gets the synthesized data-flow call for `receiver`. */
|
||||
DataFlowCall summaryDataFlowCall(Node receiver) { none() }
|
||||
|
||||
/** Gets the type of content `c`. */
|
||||
DataFlowType getContentType(Content c) {
|
||||
result = getErasedRepr(c.(FieldContent).getField().getType())
|
||||
or
|
||||
c instanceof CollectionContent and
|
||||
result instanceof TypeObject
|
||||
or
|
||||
c instanceof ArrayContent and
|
||||
result instanceof TypeObject
|
||||
or
|
||||
c instanceof MapKeyContent and
|
||||
result instanceof TypeObject
|
||||
or
|
||||
c instanceof MapValueContent and
|
||||
result instanceof TypeObject
|
||||
}
|
||||
|
||||
/** Gets the return type of kind `rk` for callable `c`. */
|
||||
DataFlowType getReturnType(SummarizedCallable c, ReturnKind rk) {
|
||||
result = getErasedRepr(c.getReturnType()) and
|
||||
exists(rk)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the type of the `i`th parameter in a synthesized call that targets a
|
||||
* callback of type `t`.
|
||||
*/
|
||||
DataFlowType getCallbackParameterType(DataFlowType t, int i) { none() }
|
||||
|
||||
/**
|
||||
* Gets the return type of kind `rk` in a synthesized call that targets a
|
||||
* callback of type `t`.
|
||||
*/
|
||||
DataFlowType getCallbackReturnType(DataFlowType t, ReturnKind rk) { none() }
|
||||
|
||||
/**
|
||||
* Holds if an external flow summary exists for `c` with input specification
|
||||
* `input`, output specification `output`, and kind `kind`.
|
||||
*/
|
||||
predicate summaryElement(DataFlowCallable c, string input, string output, string kind) {
|
||||
exists(
|
||||
string namespace, string type, boolean subtypes, string name, string signature, string ext
|
||||
|
|
||||
summaryModel(namespace, type, subtypes, name, signature, ext, input, output, kind) and
|
||||
c = interpretElement(namespace, type, subtypes, name, signature, ext)
|
||||
)
|
||||
}
|
||||
|
||||
/** Gets the summary component for specification component `c`, if any. */
|
||||
bindingset[c]
|
||||
SummaryComponent interpretComponentSpecific(string c) {
|
||||
c = "ArrayElement" and result = SummaryComponent::content(any(ArrayContent c0))
|
||||
or
|
||||
c = "Element" and result = SummaryComponent::content(any(CollectionContent c0))
|
||||
or
|
||||
c = "MapKey" and result = SummaryComponent::content(any(MapKeyContent c0))
|
||||
or
|
||||
c = "MapValue" and result = SummaryComponent::content(any(MapValueContent c0))
|
||||
}
|
||||
|
||||
class SourceOrSinkElement = Top;
|
||||
|
||||
/**
|
||||
* Holds if an external source specification exists for `e` with output specification
|
||||
* `output` and kind `kind`.
|
||||
*/
|
||||
predicate sourceElement(SourceOrSinkElement e, string output, string kind) {
|
||||
exists(
|
||||
string namespace, string type, boolean subtypes, string name, string signature, string ext
|
||||
|
|
||||
sourceModel(namespace, type, subtypes, name, signature, ext, output, kind) and
|
||||
e = interpretElement(namespace, type, subtypes, name, signature, ext)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if an external sink specification exists for `e` with input specification
|
||||
* `input` and kind `kind`.
|
||||
*/
|
||||
predicate sinkElement(SourceOrSinkElement e, string input, string kind) {
|
||||
exists(
|
||||
string namespace, string type, boolean subtypes, string name, string signature, string ext
|
||||
|
|
||||
sinkModel(namespace, type, subtypes, name, signature, ext, input, kind) and
|
||||
e = interpretElement(namespace, type, subtypes, name, signature, ext)
|
||||
)
|
||||
}
|
||||
|
||||
/** Gets the return kind corresponding to specification `"ReturnValue"`. */
|
||||
ReturnKind getReturnValueKind() { any() }
|
||||
|
||||
private newtype TInterpretNode =
|
||||
TElement(SourceOrSinkElement n) or
|
||||
TNode(Node n)
|
||||
|
||||
/** An entity used to interpret a source/sink specification. */
|
||||
class InterpretNode extends TInterpretNode {
|
||||
/** Gets the element that this node corresponds to, if any. */
|
||||
SourceOrSinkElement asElement() { this = TElement(result) }
|
||||
|
||||
/** Gets the data-flow node that this node corresponds to, if any. */
|
||||
Node asNode() { this = TNode(result) }
|
||||
|
||||
/** Gets the call that this node corresponds to, if any. */
|
||||
DataFlowCall asCall() { result = this.asElement() }
|
||||
|
||||
/** Gets the callable that this node corresponds to, if any. */
|
||||
DataFlowCallable asCallable() { result = this.asElement() }
|
||||
|
||||
/** Gets the target of this call, if any. */
|
||||
Callable getCallTarget() { result = this.asCall().getCallee().getSourceDeclaration() }
|
||||
|
||||
/** Gets a textual representation of this node. */
|
||||
string toString() {
|
||||
result = this.asElement().toString()
|
||||
or
|
||||
result = this.asNode().toString()
|
||||
}
|
||||
|
||||
/** Gets the location of this node. */
|
||||
Location getLocation() {
|
||||
result = this.asElement().getLocation()
|
||||
or
|
||||
result = this.asNode().getLocation()
|
||||
}
|
||||
}
|
||||
|
||||
/** Provides additional sink specification logic required for annotations. */
|
||||
pragma[inline]
|
||||
predicate interpretOutputSpecific(string c, InterpretNode mid, InterpretNode node) {
|
||||
exists(Node n, Top ast |
|
||||
n = node.asNode() and
|
||||
ast = mid.asElement()
|
||||
|
|
||||
(c = "Parameter" or c = "") and
|
||||
node.asNode().asParameter() = mid.asElement()
|
||||
or
|
||||
c = "" and
|
||||
n.asExpr().(FieldRead).getField() = ast
|
||||
)
|
||||
}
|
||||
|
||||
/** Provides additional source specification logic required for annotations. */
|
||||
pragma[inline]
|
||||
predicate interpretInputSpecific(string c, InterpretNode mid, InterpretNode n) {
|
||||
exists(FieldWrite fw |
|
||||
c = "" and
|
||||
fw.getField() = mid.asElement() and
|
||||
n.asNode().asExpr() = fw.getRHS()
|
||||
)
|
||||
}
|
||||
@@ -13,6 +13,8 @@ private import semmle.code.java.frameworks.Networking
|
||||
private import semmle.code.java.dataflow.ExternalFlow
|
||||
private import semmle.code.java.dataflow.internal.DataFlowPrivate
|
||||
import semmle.code.java.dataflow.FlowSteps
|
||||
private import FlowSummaryImpl as FlowSummaryImpl
|
||||
private import semmle.code.java.frameworks.JaxWS
|
||||
|
||||
/**
|
||||
* Holds if taint can flow from `src` to `sink` in zero or more
|
||||
@@ -28,78 +30,160 @@ predicate localExprTaint(Expr src, Expr sink) {
|
||||
localTaint(DataFlow::exprNode(src), DataFlow::exprNode(sink))
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if taint can flow in one local step from `src` to `sink`.
|
||||
*/
|
||||
predicate localTaintStep(DataFlow::Node src, DataFlow::Node sink) {
|
||||
DataFlow::localFlowStep(src, sink) or
|
||||
localAdditionalTaintStep(src, sink)
|
||||
cached
|
||||
private module Cached {
|
||||
private import DataFlowImplCommon as DataFlowImplCommon
|
||||
|
||||
cached
|
||||
predicate forceCachingInSameStage() { DataFlowImplCommon::forceCachingInSameStage() }
|
||||
|
||||
/**
|
||||
* Holds if taint can flow in one local step from `src` to `sink`.
|
||||
*/
|
||||
cached
|
||||
predicate localTaintStep(DataFlow::Node src, DataFlow::Node sink) {
|
||||
DataFlow::localFlowStep(src, sink) or
|
||||
localAdditionalTaintStep(src, sink) or
|
||||
// Simple flow through library code is included in the exposed local
|
||||
// step relation, even though flow is technically inter-procedural
|
||||
FlowSummaryImpl::Private::Steps::summaryThroughStep(src, sink, false)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if taint can flow in one local step from `src` to `sink` excluding
|
||||
* local data flow steps. That is, `src` and `sink` are likely to represent
|
||||
* different objects.
|
||||
*/
|
||||
cached
|
||||
predicate localAdditionalTaintStep(DataFlow::Node src, DataFlow::Node sink) {
|
||||
localAdditionalTaintExprStep(src.asExpr(), sink.asExpr())
|
||||
or
|
||||
localAdditionalTaintUpdateStep(src.asExpr(),
|
||||
sink.(DataFlow::PostUpdateNode).getPreUpdateNode().asExpr())
|
||||
or
|
||||
exists(Content f |
|
||||
readStep(src, f, sink) and
|
||||
not sink.getTypeBound() instanceof PrimitiveType and
|
||||
not sink.getTypeBound() instanceof BoxedType and
|
||||
not sink.getTypeBound() instanceof NumberType
|
||||
|
|
||||
f instanceof ArrayContent or
|
||||
f instanceof CollectionContent or
|
||||
f instanceof MapKeyContent or
|
||||
f instanceof MapValueContent
|
||||
)
|
||||
or
|
||||
FlowSummaryImpl::Private::Steps::summaryLocalStep(src, sink, false)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if the additional step from `src` to `sink` should be included in all
|
||||
* global taint flow configurations.
|
||||
*/
|
||||
cached
|
||||
predicate defaultAdditionalTaintStep(DataFlow::Node src, DataFlow::Node sink) {
|
||||
localAdditionalTaintStep(src, sink) or
|
||||
any(AdditionalTaintStep a).step(src, sink)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `node` should be a sanitizer in all global taint flow configurations
|
||||
* but not in local taint.
|
||||
*/
|
||||
cached
|
||||
predicate defaultTaintSanitizer(DataFlow::Node node) {
|
||||
// Ignore paths through test code.
|
||||
node.getEnclosingCallable().getDeclaringType() instanceof NonSecurityTestClass or
|
||||
node.asExpr() instanceof ValidatedVariableAccess
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if taint can flow in one local step from `src` to `sink` excluding
|
||||
* local data flow steps. That is, `src` and `sink` are likely to represent
|
||||
* different objects.
|
||||
*/
|
||||
predicate localAdditionalTaintStep(DataFlow::Node src, DataFlow::Node sink) {
|
||||
localAdditionalBasicTaintStep(src, sink)
|
||||
or
|
||||
composedValueAndTaintModelStep(src, sink)
|
||||
}
|
||||
|
||||
private predicate localAdditionalBasicTaintStep(DataFlow::Node src, DataFlow::Node sink) {
|
||||
localAdditionalTaintExprStep(src.asExpr(), sink.asExpr())
|
||||
or
|
||||
localAdditionalTaintUpdateStep(src.asExpr(),
|
||||
sink.(DataFlow::PostUpdateNode).getPreUpdateNode().asExpr())
|
||||
or
|
||||
summaryStep(src, sink, "taint") and
|
||||
not summaryStep(src, sink, "value")
|
||||
or
|
||||
exists(Argument arg |
|
||||
src.asExpr() = arg and
|
||||
arg.isVararg() and
|
||||
sink.(DataFlow::ImplicitVarargsArray).getCall() = arg.getCall()
|
||||
)
|
||||
}
|
||||
import Cached
|
||||
|
||||
/**
|
||||
* Holds if an additional step from `src` to `sink` through a call can be inferred from the
|
||||
* combination of a value-preserving step providing an alias between an input and the output
|
||||
* and a taint step from `src` to one the aliased nodes. For example, if we know that `f(a, b)` returns
|
||||
* the exact value of `a` and also propagates taint from `b` to `a`, then we also know that
|
||||
* the return value is tainted after `f` completes.
|
||||
* These configurations add a number of configuration-dependent additional taint
|
||||
* steps to all taint configurations. For each sink or additional step provided
|
||||
* by a given configuration the types are inspected to find those implicit
|
||||
* collection or array read steps that might be required at the sink or step
|
||||
* input. The corresponding store steps are then added as additional taint steps
|
||||
* to provide backwards-compatible taint flow to such sinks and steps.
|
||||
*
|
||||
* This is a temporary measure until support is added for such sinks that
|
||||
* require implicit read steps.
|
||||
*/
|
||||
private predicate composedValueAndTaintModelStep(ArgumentNode src, DataFlow::Node sink) {
|
||||
exists(Call call, ArgumentNode valueSource, DataFlow::PostUpdateNode valueSourcePost |
|
||||
src.argumentOf(call, _) and
|
||||
valueSource.argumentOf(call, _) and
|
||||
src != valueSource and
|
||||
valueSourcePost.getPreUpdateNode() = valueSource and
|
||||
// in-x -value-> out-y and in-z -taint-> in-x ==> in-z -taint-> out-y
|
||||
localAdditionalBasicTaintStep(src, valueSourcePost) and
|
||||
DataFlow::localFlowStep(valueSource, DataFlow::exprNode(call)) and
|
||||
sink = DataFlow::exprNode(call)
|
||||
)
|
||||
}
|
||||
private module StoreTaintSteps {
|
||||
private import semmle.code.java.dataflow.TaintTracking
|
||||
private import semmle.code.java.dataflow.TaintTracking2
|
||||
|
||||
/**
|
||||
* Holds if the additional step from `src` to `sink` should be included in all
|
||||
* global taint flow configurations.
|
||||
*/
|
||||
predicate defaultAdditionalTaintStep(DataFlow::Node src, DataFlow::Node sink) {
|
||||
localAdditionalTaintStep(src, sink) or
|
||||
any(AdditionalTaintStep a).step(src, sink)
|
||||
}
|
||||
private class StoreTaintConfig extends TaintTracking::Configuration {
|
||||
StoreTaintConfig() { this instanceof TaintTracking::Configuration or none() }
|
||||
|
||||
/**
|
||||
* Holds if `node` should be a sanitizer in all global taint flow configurations
|
||||
* but not in local taint.
|
||||
*/
|
||||
predicate defaultTaintSanitizer(DataFlow::Node node) {
|
||||
// Ignore paths through test code.
|
||||
node.getEnclosingCallable().getDeclaringType() instanceof NonSecurityTestClass or
|
||||
node.asExpr() instanceof ValidatedVariableAccess
|
||||
override predicate isSource(DataFlow::Node n) { none() }
|
||||
|
||||
override predicate isSink(DataFlow::Node n) { none() }
|
||||
|
||||
private predicate needsTaintStore(RefType container, Type elem, Content f) {
|
||||
exists(DataFlow::Node arg |
|
||||
(isSink(arg) or isAdditionalTaintStep(arg, _)) and
|
||||
(arg.asExpr() instanceof Argument or arg instanceof ArgumentNode) and
|
||||
arg.getType() = container
|
||||
or
|
||||
needsTaintStore(_, container, _)
|
||||
|
|
||||
container.(Array).getComponentType() = elem and
|
||||
f instanceof ArrayContent
|
||||
or
|
||||
container.(CollectionType).getElementType() = elem and
|
||||
f instanceof CollectionContent
|
||||
or
|
||||
container.(MapType).getValueType() = elem and
|
||||
f instanceof MapValueContent
|
||||
)
|
||||
}
|
||||
|
||||
override predicate isAdditionalTaintStep(DataFlow::Node node1, DataFlow::Node node2) {
|
||||
exists(Content f, Type elem |
|
||||
storeStep(node1, f, node2) and
|
||||
needsTaintStore(_, elem, f) and
|
||||
not exists(Type srctyp | srctyp = node1.getTypeBound() | not compatibleTypes(srctyp, elem))
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private class StoreTaintConfig2 extends TaintTracking2::Configuration {
|
||||
StoreTaintConfig2() { this instanceof TaintTracking2::Configuration or none() }
|
||||
|
||||
override predicate isSource(DataFlow::Node n) { none() }
|
||||
|
||||
override predicate isSink(DataFlow::Node n) { none() }
|
||||
|
||||
private predicate needsTaintStore(RefType container, Type elem, Content f) {
|
||||
exists(DataFlow::Node arg |
|
||||
(isSink(arg) or isAdditionalTaintStep(arg, _)) and
|
||||
(arg.asExpr() instanceof Argument or arg instanceof ArgumentNode) and
|
||||
arg.getType() = container
|
||||
or
|
||||
needsTaintStore(_, container, _)
|
||||
|
|
||||
container.(Array).getComponentType() = elem and
|
||||
f instanceof ArrayContent
|
||||
or
|
||||
container.(CollectionType).getElementType() = elem and
|
||||
f instanceof CollectionContent
|
||||
or
|
||||
container.(MapType).getValueType() = elem and
|
||||
f instanceof MapValueContent
|
||||
)
|
||||
}
|
||||
|
||||
override predicate isAdditionalTaintStep(DataFlow::Node node1, DataFlow::Node node2) {
|
||||
exists(Content f, Type elem |
|
||||
storeStep(node1, f, node2) and
|
||||
needsTaintStore(_, elem, f) and
|
||||
not exists(Type srctyp | srctyp = node1.getTypeBound() | not compatibleTypes(srctyp, elem))
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -112,22 +196,8 @@ private predicate localAdditionalTaintExprStep(Expr src, Expr sink) {
|
||||
or
|
||||
sink.(AssignAddExpr).getSource() = src and sink.getType() instanceof TypeString
|
||||
or
|
||||
sink.(ArrayCreationExpr).getInit() = src
|
||||
or
|
||||
sink.(ArrayInit).getAnInit() = src
|
||||
or
|
||||
sink.(ArrayAccess).getArray() = src
|
||||
or
|
||||
sink.(LogicExpr).getAnOperand() = src
|
||||
or
|
||||
exists(EnhancedForStmt for, SsaExplicitUpdate v |
|
||||
for.getExpr() = src and
|
||||
v.getDefiningExpr() = for.getVariable() and
|
||||
v.getAFirstUse() = sink
|
||||
)
|
||||
or
|
||||
containerReturnValueStep(src, sink)
|
||||
or
|
||||
constructorStep(src, sink)
|
||||
or
|
||||
qualifierToMethodStep(src, sink)
|
||||
@@ -150,12 +220,6 @@ private predicate localAdditionalTaintExprStep(Expr src, Expr sink) {
|
||||
* This is restricted to cases where the step updates the value of `sink`.
|
||||
*/
|
||||
private predicate localAdditionalTaintUpdateStep(Expr src, Expr sink) {
|
||||
exists(Assignment assign | assign.getSource() = src |
|
||||
sink = assign.getDest().(ArrayAccess).getArray()
|
||||
)
|
||||
or
|
||||
containerUpdateStep(src, sink)
|
||||
or
|
||||
qualifierToArgumentStep(src, sink)
|
||||
or
|
||||
argToArgStep(src, sink)
|
||||
@@ -286,6 +350,10 @@ private predicate taintPreservingQualifierToMethod(Method m) {
|
||||
)
|
||||
or
|
||||
m.(TaintPreservingCallable).returnsTaintFrom(-1)
|
||||
or
|
||||
exists(JaxRsResourceMethod resourceMethod |
|
||||
m.(GetterMethod).getDeclaringType() = resourceMethod.getAParameter().getType()
|
||||
)
|
||||
}
|
||||
|
||||
private class StringReplaceMethod extends TaintPreservingCallable {
|
||||
|
||||
@@ -11,6 +11,7 @@ private import VirtualDispatch
|
||||
private import semmle.code.java.dataflow.internal.BaseSSA
|
||||
private import semmle.code.java.dataflow.internal.DataFlowUtil
|
||||
private import semmle.code.java.dataflow.internal.DataFlowPrivate
|
||||
private import semmle.code.java.dataflow.InstanceAccess
|
||||
private import semmle.code.java.Collections
|
||||
private import semmle.code.java.Maps
|
||||
|
||||
|
||||
@@ -14,6 +14,7 @@ private import semmle.code.java.dataflow.internal.BaseSSA
|
||||
private import semmle.code.java.dataflow.internal.DataFlowUtil
|
||||
private import semmle.code.java.dataflow.internal.DataFlowPrivate
|
||||
private import semmle.code.java.dataflow.internal.ContainerFlow
|
||||
private import semmle.code.java.dataflow.InstanceAccess
|
||||
|
||||
/**
|
||||
* Gets a viable dispatch target for `ma`. This is the input dispatch relation.
|
||||
@@ -194,7 +195,7 @@ private predicate source(RefType t, ObjNode n) {
|
||||
private predicate sink(ObjNode n) {
|
||||
exists(MethodAccess toString |
|
||||
toString.getQualifier() = n.asExpr() and
|
||||
toString.getMethod().hasName("toString")
|
||||
toString.getMethod() instanceof ToStringMethod
|
||||
) and
|
||||
n.getTypeBound().getErasure() instanceof TypeObject
|
||||
}
|
||||
|
||||
@@ -39,9 +39,12 @@ Callable viableCallable(Call c) {
|
||||
c instanceof ConstructorCall and result = c.getCallee().getSourceDeclaration()
|
||||
}
|
||||
|
||||
/** A method that is the target of a call. */
|
||||
class CalledMethod extends Method {
|
||||
CalledMethod() { exists(MethodAccess ma | ma.getMethod() = this) }
|
||||
/** The source declaration of a method that is the target of a virtual call. */
|
||||
class VirtCalledSrcMethod extends SrcMethod {
|
||||
pragma[nomagic]
|
||||
VirtCalledSrcMethod() {
|
||||
exists(VirtualMethodAccess ma | ma.getMethod().getSourceDeclaration() = this)
|
||||
}
|
||||
}
|
||||
|
||||
cached
|
||||
@@ -73,7 +76,7 @@ private module Dispatch {
|
||||
(
|
||||
exists(Method def, RefType t, boolean exact |
|
||||
qualType(ma, t, exact) and
|
||||
def = ma.getMethod()
|
||||
def = ma.getMethod().getSourceDeclaration()
|
||||
|
|
||||
exact = true and result = exactMethodImpl(def, t.getSourceDeclaration())
|
||||
or
|
||||
@@ -185,8 +188,8 @@ private module Dispatch {
|
||||
not result.isAbstract() and
|
||||
if source instanceof VirtualMethodAccess
|
||||
then
|
||||
exists(CalledMethod def, RefType t, boolean exact |
|
||||
source.getMethod() = def and
|
||||
exists(VirtCalledSrcMethod def, RefType t, boolean exact |
|
||||
source.getMethod().getSourceDeclaration() = def and
|
||||
hasQualifierType(source, t, exact)
|
||||
|
|
||||
exact = true and result = exactMethodImpl(def, t.getSourceDeclaration())
|
||||
@@ -301,14 +304,14 @@ private module Dispatch {
|
||||
|
||||
/** Gets the implementation of `top` present on a value of precisely type `t`. */
|
||||
cached
|
||||
Method exactMethodImpl(CalledMethod top, SrcRefType t) {
|
||||
Method exactMethodImpl(VirtCalledSrcMethod top, SrcRefType t) {
|
||||
hasSrcMethod(t, result) and
|
||||
top.getAPossibleImplementation() = result
|
||||
top.getAPossibleImplementationOfSrcMethod() = result
|
||||
}
|
||||
|
||||
/** Gets the implementations of `top` present on viable subtypes of `t`. */
|
||||
cached
|
||||
Method viableMethodImpl(CalledMethod top, SrcRefType tsrc, RefType t) {
|
||||
Method viableMethodImpl(VirtCalledSrcMethod top, SrcRefType tsrc, RefType t) {
|
||||
exists(SrcRefType sub |
|
||||
result = exactMethodImpl(top, sub) and
|
||||
tsrc = t.getSourceDeclaration() and
|
||||
|
||||
@@ -149,9 +149,8 @@ private class ApacheHttpFlowStep extends SummaryModelCsv {
|
||||
"org.apache.hc.core5.http.message;RequestLine;true;getMethod;();;Argument[-1];ReturnValue;taint",
|
||||
"org.apache.hc.core5.http.message;RequestLine;true;getUri;();;Argument[-1];ReturnValue;taint",
|
||||
"org.apache.hc.core5.http.message;RequestLine;true;toString;();;Argument[-1];ReturnValue;taint",
|
||||
"org.apache.hc.core5.http.message;RequestLine;true;RequestLine;(HttpRequest);;Argument[0];ReturnValue;taint",
|
||||
"org.apache.hc.core5.http.message;RequestLine;true;RequestLine;(String,String,ProtocolVersion);;Argument[1];ReturnValue;taint",
|
||||
"org.apache.hc.core5.http.message;RequestLine;true;RequestLine;(String,String,ProtocolVersion);;Argument[1];ReturnValue;taint",
|
||||
"org.apache.hc.core5.http.message;RequestLine;true;RequestLine;(HttpRequest);;Argument[0];Argument[-1];taint",
|
||||
"org.apache.hc.core5.http.message;RequestLine;true;RequestLine;(String,String,ProtocolVersion);;Argument[1];Argument[-1];taint",
|
||||
"org.apache.hc.core5.function;Supplier;true;get;();;Argument[-1];ReturnValue;taint",
|
||||
"org.apache.hc.core5.net;URIAuthority;true;getHostName;();;Argument[-1];ReturnValue;taint",
|
||||
"org.apache.hc.core5.net;URIAuthority;true;toString;();;Argument[-1];ReturnValue;taint",
|
||||
@@ -166,14 +165,17 @@ private class ApacheHttpFlowStep extends SummaryModelCsv {
|
||||
"org.apache.http.util;EncodingUtils;true;getAsciiString;;;Argument[0];ReturnValue;taint",
|
||||
"org.apache.http.util;EncodingUtils;true;getBytes;(String,String);;Argument[0];ReturnValue;taint",
|
||||
"org.apache.http.util;EncodingUtils;true;getString;;;Argument[0];ReturnValue;taint",
|
||||
"org.apache.http.util;Args;true;containsNoBlanks;(T,String);;Argument[0];ReturnValue;value",
|
||||
"org.apache.http.util;Args;true;notNull;(T,String);;Argument[0];ReturnValue;value",
|
||||
"org.apache.http.util;Args;true;notEmpty;(T,String);;Argument[0];ReturnValue;value",
|
||||
"org.apache.http.util;Args;true;notBlank;(T,String);;Argument[0];ReturnValue;value",
|
||||
"org.apache.hc.core5.util;Args;true;containsNoBlanks;(T,String);;Argument[0];ReturnValue;value",
|
||||
"org.apache.hc.core5.util;Args;true;notNull;(T,String);;Argument[0];ReturnValue;value",
|
||||
"org.apache.hc.core5.util;Args;true;notEmpty;(T,String);;Argument[0];ReturnValue;value",
|
||||
"org.apache.hc.core5.util;Args;true;notBlank;(T,String);;Argument[0];ReturnValue;value",
|
||||
"org.apache.http.util;Args;true;containsNoBlanks;(CharSequence,String);;Argument[0];ReturnValue;value",
|
||||
"org.apache.http.util;Args;true;notNull;(Object,String);;Argument[0];ReturnValue;value",
|
||||
"org.apache.http.util;Args;true;notEmpty;(CharSequence,String);;Argument[0];ReturnValue;value",
|
||||
"org.apache.http.util;Args;true;notEmpty;(Collection,String);;Argument[0];ReturnValue;value",
|
||||
"org.apache.http.util;Args;true;notBlank;(CharSequence,String);;Argument[0];ReturnValue;value",
|
||||
"org.apache.hc.core5.util;Args;true;containsNoBlanks;(CharSequence,String);;Argument[0];ReturnValue;value",
|
||||
"org.apache.hc.core5.util;Args;true;notNull;(Object,String);;Argument[0];ReturnValue;value",
|
||||
"org.apache.hc.core5.util;Args;true;notEmpty;(Collection,String);;Argument[0];ReturnValue;value",
|
||||
"org.apache.hc.core5.util;Args;true;notEmpty;(CharSequence,String);;Argument[0];ReturnValue;value",
|
||||
"org.apache.hc.core5.util;Args;true;notEmpty;(Object,String);;Argument[0];ReturnValue;value",
|
||||
"org.apache.hc.core5.util;Args;true;notBlank;(CharSequence,String);;Argument[0];ReturnValue;value",
|
||||
"org.apache.hc.core5.http.io.entity;HttpEntities;true;create;;;Argument[0];ReturnValue;taint",
|
||||
"org.apache.hc.core5.http.io.entity;HttpEntities;true;createGzipped;;;Argument[0];ReturnValue;taint",
|
||||
"org.apache.hc.core5.http.io.entity;HttpEntities;true;createUrlEncoded;;;Argument[0];ReturnValue;taint",
|
||||
@@ -181,16 +183,16 @@ private class ApacheHttpFlowStep extends SummaryModelCsv {
|
||||
"org.apache.hc.core5.http.io.entity;HttpEntities;true;withTrailers;;;Argument[0];ReturnValue;taint",
|
||||
"org.apache.http.entity;BasicHttpEntity;true;setContent;(InputStream);;Argument[0];Argument[-1];taint",
|
||||
"org.apache.http.entity;BufferedHttpEntity;true;BufferedHttpEntity;(HttpEntity);;Argument[0];ReturnValue;taint",
|
||||
"org.apache.http.entity;ByteArrayEntity;true;ByteArrayEntity;;;Argument[0];ReturnValue;taint",
|
||||
"org.apache.http.entity;ByteArrayEntity;true;ByteArrayEntity;;;Argument[0];Argument[-1];taint",
|
||||
"org.apache.http.entity;HttpEntityWrapper;true;HttpEntityWrapper;(HttpEntity);;Argument[0];ReturnValue;taint",
|
||||
"org.apache.http.entity;InputStreamEntity;true;InputStreamEntity;;;Argument[0];ReturnValue;taint",
|
||||
"org.apache.http.entity;StringEntity;true;StringEntity;;;Argument[0];ReturnValue;taint",
|
||||
"org.apache.http.entity;StringEntity;true;StringEntity;;;Argument[0];Argument[-1];taint",
|
||||
"org.apache.hc.core5.http.io.entity;BasicHttpEntity;true;BasicHttpEntity;;;Argument[0];ReturnValue;taint",
|
||||
"org.apache.hc.core5.http.io.entity;BufferedHttpEntity;true;BufferedHttpEntity;(HttpEntity);;Argument[0];ReturnValue;taint",
|
||||
"org.apache.hc.core5.http.io.entity;ByteArrayEntity;true;ByteArrayEntity;;;Argument[0];ReturnValue;taint",
|
||||
"org.apache.hc.core5.http.io.entity;ByteArrayEntity;true;ByteArrayEntity;;;Argument[0];Argument[-1];taint",
|
||||
"org.apache.hc.core5.http.io.entity;HttpEntityWrapper;true;HttpEntityWrapper;(HttpEntity);;Argument[0];ReturnValue;taint",
|
||||
"org.apache.hc.core5.http.io.entity;InputStreamEntity;true;InputStreamEntity;;;Argument[0];ReturnValue;taint",
|
||||
"org.apache.hc.core5.http.io.entity;StringEntity;true;StringEntity;;;Argument[0];ReturnValue;taint",
|
||||
"org.apache.hc.core5.http.io.entity;StringEntity;true;StringEntity;;;Argument[0];Argument[-1];taint",
|
||||
"org.apache.http.util;ByteArrayBuffer;true;append;(byte[],int,int);;Argument[0];Argument[-1];taint",
|
||||
"org.apache.http.util;ByteArrayBuffer;true;append;(char[],int,int);;Argument[0];Argument[-1];taint",
|
||||
"org.apache.http.util;ByteArrayBuffer;true;append;(CharArrayBuffer,int,int);;Argument[0];Argument[-1];taint",
|
||||
|
||||
@@ -3,19 +3,60 @@
|
||||
*/
|
||||
|
||||
import java
|
||||
private import semmle.code.java.dataflow.DataFlow
|
||||
private import semmle.code.java.dataflow.FlowSteps
|
||||
|
||||
/**
|
||||
* The type `com.esotericsoftware.kryo.Kryo`.
|
||||
*/
|
||||
class Kryo extends RefType {
|
||||
Kryo() { this.hasQualifiedName("com.esotericsoftware.kryo", "Kryo") }
|
||||
Kryo() {
|
||||
hasQualifiedName("com.esotericsoftware.kryo", "Kryo") or
|
||||
hasQualifiedName("com.esotericsoftware.kryo5", "Kryo")
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A Kryo input stream.
|
||||
*/
|
||||
class KryoInput extends RefType {
|
||||
KryoInput() { this.hasQualifiedName("com.esotericsoftware.kryo.io", "Input") }
|
||||
KryoInput() {
|
||||
hasQualifiedName("com.esotericsoftware.kryo.io", "Input") or
|
||||
hasQualifiedName("com.esotericsoftware.kryo5.io", "Input")
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A Kryo pool.
|
||||
*/
|
||||
class KryoPool extends RefType {
|
||||
KryoPool() {
|
||||
hasQualifiedName("com.esotericsoftware.kryo.pool", "KryoPool") or
|
||||
hasQualifiedName("com.esotericsoftware.kryo5.pool", "KryoPool")
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A Kryo pool builder.
|
||||
*/
|
||||
class KryoPoolBuilder extends RefType {
|
||||
KryoPoolBuilder() {
|
||||
hasQualifiedName("com.esotericsoftware.kryo.pool", "KryoPool$Builder") or
|
||||
hasQualifiedName("com.esotericsoftware.kryo5.pool", "KryoPool$Builder")
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A Kryo pool builder method used in a fluent API call chain.
|
||||
*/
|
||||
class KryoPoolBuilderMethod extends Method {
|
||||
KryoPoolBuilderMethod() {
|
||||
getDeclaringType() instanceof KryoPoolBuilder and
|
||||
(
|
||||
getReturnType() instanceof KryoPoolBuilder or
|
||||
getReturnType() instanceof KryoPool
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -45,3 +86,13 @@ class KryoEnableWhiteListing extends MethodAccess {
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A KryoPool method that uses a Kryo instance.
|
||||
*/
|
||||
class KryoPoolRunMethod extends Method {
|
||||
KryoPoolRunMethod() {
|
||||
getDeclaringType() instanceof KryoPool and
|
||||
hasName("run")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,20 +1,28 @@
|
||||
/* Definitions related to the Apache Commons Exec library. */
|
||||
import semmle.code.java.Type
|
||||
import semmle.code.java.security.ExternalProcess
|
||||
|
||||
library class TypeCommandLine extends Class {
|
||||
/** The class `org.apache.commons.exec.CommandLine`. */
|
||||
private class TypeCommandLine extends Class {
|
||||
TypeCommandLine() { hasQualifiedName("org.apache.commons.exec", "CommandLine") }
|
||||
}
|
||||
|
||||
library class MethodCommandLineParse extends Method {
|
||||
/** The `parse()` method of the class `org.apache.commons.exec.CommandLine`. */
|
||||
private class MethodCommandLineParse extends Method, ExecCallable {
|
||||
MethodCommandLineParse() {
|
||||
getDeclaringType() instanceof TypeCommandLine and
|
||||
hasName("parse")
|
||||
}
|
||||
|
||||
override int getAnExecutedArgument() { result = 0 }
|
||||
}
|
||||
|
||||
library class MethodCommandLineAddArguments extends Method {
|
||||
/** The `addArguments()` method of the class `org.apache.commons.exec.CommandLine`. */
|
||||
private class MethodCommandLineAddArguments extends Method, ExecCallable {
|
||||
MethodCommandLineAddArguments() {
|
||||
getDeclaringType() instanceof TypeCommandLine and
|
||||
hasName("addArguments")
|
||||
}
|
||||
|
||||
override int getAnExecutedArgument() { result = 0 }
|
||||
}
|
||||
|
||||
@@ -94,29 +94,33 @@ private class ApacheStringUtilsModel extends SummaryModelCsv {
|
||||
"org.apache.commons.lang3;StringUtils;false;defaultString;;;Argument[0..1];ReturnValue;taint",
|
||||
"org.apache.commons.lang3;StringUtils;false;deleteWhitespace;;;Argument[0];ReturnValue;taint",
|
||||
"org.apache.commons.lang3;StringUtils;false;difference;;;Argument[0..1];ReturnValue;taint",
|
||||
"org.apache.commons.lang3;StringUtils;false;firstNonBlank;;;Argument[0];ReturnValue;taint",
|
||||
"org.apache.commons.lang3;StringUtils;false;firstNonEmpty;;;Argument[0];ReturnValue;taint",
|
||||
"org.apache.commons.lang3;StringUtils;false;firstNonBlank;;;ArrayElement of Argument[0];ReturnValue;value",
|
||||
"org.apache.commons.lang3;StringUtils;false;firstNonEmpty;;;ArrayElement of Argument[0];ReturnValue;value",
|
||||
"org.apache.commons.lang3;StringUtils;false;getBytes;;;Argument[0];ReturnValue;taint",
|
||||
"org.apache.commons.lang3;StringUtils;false;getCommonPrefix;;;Argument[0];ReturnValue;taint",
|
||||
"org.apache.commons.lang3;StringUtils;false;getCommonPrefix;;;ArrayElement of Argument[0];ReturnValue;taint",
|
||||
"org.apache.commons.lang3;StringUtils;false;getDigits;;;Argument[0];ReturnValue;taint",
|
||||
"org.apache.commons.lang3;StringUtils;false;getIfBlank;;;Argument[0..1];ReturnValue;taint",
|
||||
"org.apache.commons.lang3;StringUtils;false;getIfEmpty;;;Argument[0..1];ReturnValue;taint",
|
||||
"org.apache.commons.lang3;StringUtils;false;join;(char[],char);;Argument[0];ReturnValue;taint",
|
||||
"org.apache.commons.lang3;StringUtils;false;join;(char[],char,int,int);;Argument[0];ReturnValue;taint",
|
||||
"org.apache.commons.lang3;StringUtils;false;join;(java.lang.Iterable,char);;Argument[0];ReturnValue;taint",
|
||||
"org.apache.commons.lang3;StringUtils;false;join;(java.lang.Iterable,java.lang.String);;Argument[0..1];ReturnValue;taint",
|
||||
"org.apache.commons.lang3;StringUtils;false;join;(java.lang.Object[]);;Argument[0];ReturnValue;taint",
|
||||
"org.apache.commons.lang3;StringUtils;false;join;(java.lang.Object[],char);;Argument[0];ReturnValue;taint",
|
||||
"org.apache.commons.lang3;StringUtils;false;join;(java.lang.Object[],char,int,int);;Argument[0];ReturnValue;taint",
|
||||
"org.apache.commons.lang3;StringUtils;false;join;(java.lang.Object[],java.lang.String);;Argument[0..1];ReturnValue;taint",
|
||||
"org.apache.commons.lang3;StringUtils;false;join;(java.lang.Object[],java.lang.String,int,int);;Argument[0];ReturnValue;taint",
|
||||
"org.apache.commons.lang3;StringUtils;false;join;(java.lang.Iterable,char);;Element of Argument[0];ReturnValue;taint",
|
||||
"org.apache.commons.lang3;StringUtils;false;join;(java.lang.Iterable,java.lang.String);;Element of Argument[0];ReturnValue;taint",
|
||||
"org.apache.commons.lang3;StringUtils;false;join;(java.lang.Iterable,java.lang.String);;Argument[1];ReturnValue;taint",
|
||||
"org.apache.commons.lang3;StringUtils;false;join;(java.lang.Object[]);;ArrayElement of Argument[0];ReturnValue;taint",
|
||||
"org.apache.commons.lang3;StringUtils;false;join;(java.lang.Object[],char);;ArrayElement of Argument[0];ReturnValue;taint",
|
||||
"org.apache.commons.lang3;StringUtils;false;join;(java.lang.Object[],char,int,int);;ArrayElement of Argument[0];ReturnValue;taint",
|
||||
"org.apache.commons.lang3;StringUtils;false;join;(java.lang.Object[],java.lang.String);;ArrayElement of Argument[0];ReturnValue;taint",
|
||||
"org.apache.commons.lang3;StringUtils;false;join;(java.lang.Object[],java.lang.String);;Argument[1];ReturnValue;taint",
|
||||
"org.apache.commons.lang3;StringUtils;false;join;(java.lang.Object[],java.lang.String,int,int);;ArrayElement of Argument[0];ReturnValue;taint",
|
||||
"org.apache.commons.lang3;StringUtils;false;join;(java.lang.Object[],java.lang.String,int,int);;Argument[1];ReturnValue;taint",
|
||||
"org.apache.commons.lang3;StringUtils;false;join;(java.util.Iterator,char);;Argument[0];ReturnValue;taint",
|
||||
"org.apache.commons.lang3;StringUtils;false;join;(java.util.Iterator,java.lang.String);;Argument[0..1];ReturnValue;taint",
|
||||
"org.apache.commons.lang3;StringUtils;false;join;(java.util.List,char,int,int);;Argument[0];ReturnValue;taint",
|
||||
"org.apache.commons.lang3;StringUtils;false;join;(java.util.List,java.lang.String,int,int);;Argument[0];ReturnValue;taint",
|
||||
"org.apache.commons.lang3;StringUtils;false;join;(java.util.Iterator,char);;Element of Argument[0];ReturnValue;taint",
|
||||
"org.apache.commons.lang3;StringUtils;false;join;(java.util.Iterator,java.lang.String);;Element of Argument[0];ReturnValue;taint",
|
||||
"org.apache.commons.lang3;StringUtils;false;join;(java.util.Iterator,java.lang.String);;Argument[1];ReturnValue;taint",
|
||||
"org.apache.commons.lang3;StringUtils;false;join;(java.util.List,char,int,int);;Element of Argument[0];ReturnValue;taint",
|
||||
"org.apache.commons.lang3;StringUtils;false;join;(java.util.List,java.lang.String,int,int);;Element of Argument[0];ReturnValue;taint",
|
||||
"org.apache.commons.lang3;StringUtils;false;join;(java.util.List,java.lang.String,int,int);;Argument[1];ReturnValue;taint",
|
||||
"org.apache.commons.lang3;StringUtils;false;joinWith;;;Argument[0..1];ReturnValue;taint",
|
||||
"org.apache.commons.lang3;StringUtils;false;joinWith;;;Argument[0];ReturnValue;taint",
|
||||
"org.apache.commons.lang3;StringUtils;false;joinWith;;;ArrayElement of Argument[1];ReturnValue;taint",
|
||||
"org.apache.commons.lang3;StringUtils;false;left;;;Argument[0];ReturnValue;taint",
|
||||
"org.apache.commons.lang3;StringUtils;false;leftPad;(java.lang.String,int,java.lang.String);;Argument[2];ReturnValue;taint",
|
||||
"org.apache.commons.lang3;StringUtils;false;leftPad;;;Argument[0];ReturnValue;taint",
|
||||
@@ -148,9 +152,9 @@ private class ApacheStringUtilsModel extends SummaryModelCsv {
|
||||
"org.apache.commons.lang3;StringUtils;false;replaceChars;(java.lang.String,java.lang.String,java.lang.String);;Argument[2];ReturnValue;taint",
|
||||
"org.apache.commons.lang3;StringUtils;false;replaceChars;;;Argument[0];ReturnValue;taint",
|
||||
"org.apache.commons.lang3;StringUtils;false;replaceEach;;;Argument[0];ReturnValue;taint",
|
||||
"org.apache.commons.lang3;StringUtils;false;replaceEach;;;Argument[2];ReturnValue;taint",
|
||||
"org.apache.commons.lang3;StringUtils;false;replaceEach;;;ArrayElement of Argument[2];ReturnValue;taint",
|
||||
"org.apache.commons.lang3;StringUtils;false;replaceEachRepeatedly;;;Argument[0];ReturnValue;taint",
|
||||
"org.apache.commons.lang3;StringUtils;false;replaceEachRepeatedly;;;Argument[2];ReturnValue;taint",
|
||||
"org.apache.commons.lang3;StringUtils;false;replaceEachRepeatedly;;;ArrayElement of Argument[2];ReturnValue;taint",
|
||||
"org.apache.commons.lang3;StringUtils;false;replaceFirst;;;Argument[0];ReturnValue;taint",
|
||||
"org.apache.commons.lang3;StringUtils;false;replaceFirst;;;Argument[2];ReturnValue;taint",
|
||||
"org.apache.commons.lang3;StringUtils;false;replaceIgnoreCase;;;Argument[0];ReturnValue;taint",
|
||||
@@ -182,7 +186,7 @@ private class ApacheStringUtilsModel extends SummaryModelCsv {
|
||||
"org.apache.commons.lang3;StringUtils;false;strip;(java.lang.String);;Argument[0];ReturnValue;taint",
|
||||
"org.apache.commons.lang3;StringUtils;false;strip;(java.lang.String,java.lang.String);;Argument[0];ReturnValue;taint",
|
||||
"org.apache.commons.lang3;StringUtils;false;stripAccents;;;Argument[0];ReturnValue;taint",
|
||||
"org.apache.commons.lang3;StringUtils;false;stripAll;;;Argument[0];ReturnValue;taint",
|
||||
"org.apache.commons.lang3;StringUtils;false;stripAll;;;ArrayElement of Argument[0];ArrayElement of ReturnValue;taint",
|
||||
"org.apache.commons.lang3;StringUtils;false;stripEnd;;;Argument[0];ReturnValue;taint",
|
||||
"org.apache.commons.lang3;StringUtils;false;stripStart;;;Argument[0];ReturnValue;taint",
|
||||
"org.apache.commons.lang3;StringUtils;false;stripToEmpty;;;Argument[0];ReturnValue;taint",
|
||||
@@ -229,7 +233,8 @@ private class ApacheStrBuilderModel extends SummaryModelCsv {
|
||||
"org.apache.commons.lang3.text;StrBuilder;false;append;(java.lang.Object);;Argument[0];Argument[-1];taint",
|
||||
"org.apache.commons.lang3.text;StrBuilder;false;append;(java.lang.String);;Argument[0];Argument[-1];taint",
|
||||
"org.apache.commons.lang3.text;StrBuilder;false;append;(java.lang.String,int,int);;Argument[0];Argument[-1];taint",
|
||||
"org.apache.commons.lang3.text;StrBuilder;false;append;(java.lang.String,java.lang.Object[]);;Argument[0..1];Argument[-1];taint",
|
||||
"org.apache.commons.lang3.text;StrBuilder;false;append;(java.lang.String,java.lang.Object[]);;Argument[0];Argument[-1];taint",
|
||||
"org.apache.commons.lang3.text;StrBuilder;false;append;(java.lang.String,java.lang.Object[]);;ArrayElement of Argument[1];Argument[-1];taint",
|
||||
"org.apache.commons.lang3.text;StrBuilder;false;append;(java.lang.StringBuffer);;Argument[0];Argument[-1];taint",
|
||||
"org.apache.commons.lang3.text;StrBuilder;false;append;(java.lang.StringBuffer,int,int);;Argument[0];Argument[-1];taint",
|
||||
"org.apache.commons.lang3.text;StrBuilder;false;append;(java.lang.StringBuilder);;Argument[0];Argument[-1];taint",
|
||||
@@ -238,7 +243,9 @@ private class ApacheStrBuilderModel extends SummaryModelCsv {
|
||||
"org.apache.commons.lang3.text;StrBuilder;false;append;(java.nio.CharBuffer,int,int);;Argument[0];Argument[-1];taint",
|
||||
"org.apache.commons.lang3.text;StrBuilder;false;append;(org.apache.commons.lang3.text.StrBuilder);;Argument[0];Argument[-1];taint",
|
||||
"org.apache.commons.lang3.text;StrBuilder;false;append;;;Argument[-1];ReturnValue;taint",
|
||||
"org.apache.commons.lang3.text;StrBuilder;false;appendAll;;;Argument[0];Argument[-1];taint",
|
||||
"org.apache.commons.lang3.text;StrBuilder;false;appendAll;(Iterable);;Element of Argument[0];Argument[-1];taint",
|
||||
"org.apache.commons.lang3.text;StrBuilder;false;appendAll;(Iterator);;Element of Argument[0];Argument[-1];taint",
|
||||
"org.apache.commons.lang3.text;StrBuilder;false;appendAll;(Object[]);;ArrayElement of Argument[0];Argument[-1];taint",
|
||||
"org.apache.commons.lang3.text;StrBuilder;false;appendAll;;;Argument[-1];ReturnValue;taint",
|
||||
"org.apache.commons.lang3.text;StrBuilder;false;appendFixedWidthPadLeft;;;Argument[-1];ReturnValue;taint",
|
||||
"org.apache.commons.lang3.text;StrBuilder;false;appendFixedWidthPadLeft;;;Argument[0];Argument[-1];taint",
|
||||
@@ -249,14 +256,18 @@ private class ApacheStrBuilderModel extends SummaryModelCsv {
|
||||
"org.apache.commons.lang3.text;StrBuilder;false;appendSeparator;(java.lang.String,java.lang.String);;Argument[0..1];Argument[-1];taint",
|
||||
"org.apache.commons.lang3.text;StrBuilder;false;appendSeparator;;;Argument[-1];ReturnValue;taint",
|
||||
"org.apache.commons.lang3.text;StrBuilder;false;appendTo;;;Argument[-1];Argument[0];taint",
|
||||
"org.apache.commons.lang3.text;StrBuilder;false;appendWithSeparators;;;Argument[0..1];Argument[-1];taint",
|
||||
"org.apache.commons.lang3.text;StrBuilder;false;appendWithSeparators;(Iterable,String);;Element of Argument[0];Argument[-1];taint",
|
||||
"org.apache.commons.lang3.text;StrBuilder;false;appendWithSeparators;(Iterator,String);;Element of Argument[0];Argument[-1];taint",
|
||||
"org.apache.commons.lang3.text;StrBuilder;false;appendWithSeparators;(Object[],String);;ArrayElement of Argument[0];Argument[-1];taint",
|
||||
"org.apache.commons.lang3.text;StrBuilder;false;appendWithSeparators;;;Argument[1];Argument[-1];taint",
|
||||
"org.apache.commons.lang3.text;StrBuilder;false;appendWithSeparators;;;Argument[-1];ReturnValue;taint",
|
||||
"org.apache.commons.lang3.text;StrBuilder;false;appendln;(char[]);;Argument[0];Argument[-1];taint",
|
||||
"org.apache.commons.lang3.text;StrBuilder;false;appendln;(char[],int,int);;Argument[0];Argument[-1];taint",
|
||||
"org.apache.commons.lang3.text;StrBuilder;false;appendln;(java.lang.Object);;Argument[0];Argument[-1];taint",
|
||||
"org.apache.commons.lang3.text;StrBuilder;false;appendln;(java.lang.String);;Argument[0];Argument[-1];taint",
|
||||
"org.apache.commons.lang3.text;StrBuilder;false;appendln;(java.lang.String,int,int);;Argument[0];Argument[-1];taint",
|
||||
"org.apache.commons.lang3.text;StrBuilder;false;appendln;(java.lang.String,java.lang.Object[]);;Argument[0..1];Argument[-1];taint",
|
||||
"org.apache.commons.lang3.text;StrBuilder;false;appendln;(java.lang.String,java.lang.Object[]);;Argument[0];Argument[-1];taint",
|
||||
"org.apache.commons.lang3.text;StrBuilder;false;appendln;(java.lang.String,java.lang.Object[]);;ArrayElement of Argument[1];Argument[-1];taint",
|
||||
"org.apache.commons.lang3.text;StrBuilder;false;appendln;(java.lang.StringBuffer);;Argument[0];Argument[-1];taint",
|
||||
"org.apache.commons.lang3.text;StrBuilder;false;appendln;(java.lang.StringBuffer,int,int);;Argument[0];Argument[-1];taint",
|
||||
"org.apache.commons.lang3.text;StrBuilder;false;appendln;(java.lang.StringBuilder);;Argument[0];Argument[-1];taint",
|
||||
@@ -296,7 +307,8 @@ private class ApacheStrBuilderModel extends SummaryModelCsv {
|
||||
"org.apache.commons.text;StrBuilder;false;append;(java.lang.Object);;Argument[0];Argument[-1];taint",
|
||||
"org.apache.commons.text;StrBuilder;false;append;(java.lang.String);;Argument[0];Argument[-1];taint",
|
||||
"org.apache.commons.text;StrBuilder;false;append;(java.lang.String,int,int);;Argument[0];Argument[-1];taint",
|
||||
"org.apache.commons.text;StrBuilder;false;append;(java.lang.String,java.lang.Object[]);;Argument[0..1];Argument[-1];taint",
|
||||
"org.apache.commons.text;StrBuilder;false;append;(java.lang.String,java.lang.Object[]);;Argument[0];Argument[-1];taint",
|
||||
"org.apache.commons.text;StrBuilder;false;append;(java.lang.String,java.lang.Object[]);;ArrayElement of Argument[1];Argument[-1];taint",
|
||||
"org.apache.commons.text;StrBuilder;false;append;(java.lang.StringBuffer);;Argument[0];Argument[-1];taint",
|
||||
"org.apache.commons.text;StrBuilder;false;append;(java.lang.StringBuffer,int,int);;Argument[0];Argument[-1];taint",
|
||||
"org.apache.commons.text;StrBuilder;false;append;(java.lang.StringBuilder);;Argument[0];Argument[-1];taint",
|
||||
@@ -305,7 +317,9 @@ private class ApacheStrBuilderModel extends SummaryModelCsv {
|
||||
"org.apache.commons.text;StrBuilder;false;append;(java.nio.CharBuffer,int,int);;Argument[0];Argument[-1];taint",
|
||||
"org.apache.commons.text;StrBuilder;false;append;(org.apache.commons.text.StrBuilder);;Argument[0];Argument[-1];taint",
|
||||
"org.apache.commons.text;StrBuilder;false;append;;;Argument[-1];ReturnValue;taint",
|
||||
"org.apache.commons.text;StrBuilder;false;appendAll;;;Argument[0];Argument[-1];taint",
|
||||
"org.apache.commons.text;StrBuilder;false;appendAll;(Iterable);;Element of Argument[0];Argument[-1];taint",
|
||||
"org.apache.commons.text;StrBuilder;false;appendAll;(Iterator);;Element of Argument[0];Argument[-1];taint",
|
||||
"org.apache.commons.text;StrBuilder;false;appendAll;(Object[]);;ArrayElement of Argument[0];Argument[-1];taint",
|
||||
"org.apache.commons.text;StrBuilder;false;appendAll;;;Argument[-1];ReturnValue;taint",
|
||||
"org.apache.commons.text;StrBuilder;false;appendFixedWidthPadLeft;;;Argument[-1];ReturnValue;taint",
|
||||
"org.apache.commons.text;StrBuilder;false;appendFixedWidthPadLeft;;;Argument[0];Argument[-1];taint",
|
||||
@@ -316,14 +330,18 @@ private class ApacheStrBuilderModel extends SummaryModelCsv {
|
||||
"org.apache.commons.text;StrBuilder;false;appendSeparator;(java.lang.String,java.lang.String);;Argument[0..1];Argument[-1];taint",
|
||||
"org.apache.commons.text;StrBuilder;false;appendSeparator;;;Argument[-1];ReturnValue;taint",
|
||||
"org.apache.commons.text;StrBuilder;false;appendTo;;;Argument[-1];Argument[0];taint",
|
||||
"org.apache.commons.text;StrBuilder;false;appendWithSeparators;;;Argument[0..1];Argument[-1];taint",
|
||||
"org.apache.commons.text;StrBuilder;false;appendWithSeparators;(Iterable,String);;Element of Argument[0];Argument[-1];taint",
|
||||
"org.apache.commons.text;StrBuilder;false;appendWithSeparators;(Iterator,String);;Element of Argument[0];Argument[-1];taint",
|
||||
"org.apache.commons.text;StrBuilder;false;appendWithSeparators;(Object[],String);;ArrayElement of Argument[0];Argument[-1];taint",
|
||||
"org.apache.commons.text;StrBuilder;false;appendWithSeparators;;;Argument[1];Argument[-1];taint",
|
||||
"org.apache.commons.text;StrBuilder;false;appendWithSeparators;;;Argument[-1];ReturnValue;taint",
|
||||
"org.apache.commons.text;StrBuilder;false;appendln;(char[]);;Argument[0];Argument[-1];taint",
|
||||
"org.apache.commons.text;StrBuilder;false;appendln;(char[],int,int);;Argument[0];Argument[-1];taint",
|
||||
"org.apache.commons.text;StrBuilder;false;appendln;(java.lang.Object);;Argument[0];Argument[-1];taint",
|
||||
"org.apache.commons.text;StrBuilder;false;appendln;(java.lang.String);;Argument[0];Argument[-1];taint",
|
||||
"org.apache.commons.text;StrBuilder;false;appendln;(java.lang.String,int,int);;Argument[0];Argument[-1];taint",
|
||||
"org.apache.commons.text;StrBuilder;false;appendln;(java.lang.String,java.lang.Object[]);;Argument[0..1];Argument[-1];taint",
|
||||
"org.apache.commons.text;StrBuilder;false;appendln;(java.lang.String,java.lang.Object[]);;Argument[0];Argument[-1];taint",
|
||||
"org.apache.commons.text;StrBuilder;false;appendln;(java.lang.String,java.lang.Object[]);;ArrayElement of Argument[1];Argument[-1];taint",
|
||||
"org.apache.commons.text;StrBuilder;false;appendln;(java.lang.StringBuffer);;Argument[0];Argument[-1];taint",
|
||||
"org.apache.commons.text;StrBuilder;false;appendln;(java.lang.StringBuffer,int,int);;Argument[0];Argument[-1];taint",
|
||||
"org.apache.commons.text;StrBuilder;false;appendln;(java.lang.StringBuilder);;Argument[0];Argument[-1];taint",
|
||||
@@ -364,7 +382,8 @@ private class ApacheStrBuilderModel extends SummaryModelCsv {
|
||||
"org.apache.commons.text;TextStringBuilder;false;append;(java.lang.Object);;Argument[0];Argument[-1];taint",
|
||||
"org.apache.commons.text;TextStringBuilder;false;append;(java.lang.String);;Argument[0];Argument[-1];taint",
|
||||
"org.apache.commons.text;TextStringBuilder;false;append;(java.lang.String,int,int);;Argument[0];Argument[-1];taint",
|
||||
"org.apache.commons.text;TextStringBuilder;false;append;(java.lang.String,java.lang.Object[]);;Argument[0..1];Argument[-1];taint",
|
||||
"org.apache.commons.text;TextStringBuilder;false;append;(java.lang.String,java.lang.Object[]);;Argument[0];Argument[-1];taint",
|
||||
"org.apache.commons.text;TextStringBuilder;false;append;(java.lang.String,java.lang.Object[]);;ArrayElement of Argument[1];Argument[-1];taint",
|
||||
"org.apache.commons.text;TextStringBuilder;false;append;(java.lang.StringBuffer);;Argument[0];Argument[-1];taint",
|
||||
"org.apache.commons.text;TextStringBuilder;false;append;(java.lang.StringBuffer,int,int);;Argument[0];Argument[-1];taint",
|
||||
"org.apache.commons.text;TextStringBuilder;false;append;(java.lang.StringBuilder);;Argument[0];Argument[-1];taint",
|
||||
@@ -373,7 +392,9 @@ private class ApacheStrBuilderModel extends SummaryModelCsv {
|
||||
"org.apache.commons.text;TextStringBuilder;false;append;(java.nio.CharBuffer,int,int);;Argument[0];Argument[-1];taint",
|
||||
"org.apache.commons.text;TextStringBuilder;false;append;(org.apache.commons.text.TextStringBuilder);;Argument[0];Argument[-1];taint",
|
||||
"org.apache.commons.text;TextStringBuilder;false;append;;;Argument[-1];ReturnValue;taint",
|
||||
"org.apache.commons.text;TextStringBuilder;false;appendAll;;;Argument[0];Argument[-1];taint",
|
||||
"org.apache.commons.text;TextStringBuilder;false;appendAll;(Iterable);;Element of Argument[0];Argument[-1];taint",
|
||||
"org.apache.commons.text;TextStringBuilder;false;appendAll;(Iterator);;Element of Argument[0];Argument[-1];taint",
|
||||
"org.apache.commons.text;TextStringBuilder;false;appendAll;(Object[]);;ArrayElement of Argument[0];Argument[-1];taint",
|
||||
"org.apache.commons.text;TextStringBuilder;false;appendAll;;;Argument[-1];ReturnValue;taint",
|
||||
"org.apache.commons.text;TextStringBuilder;false;appendFixedWidthPadLeft;;;Argument[-1];ReturnValue;taint",
|
||||
"org.apache.commons.text;TextStringBuilder;false;appendFixedWidthPadLeft;;;Argument[0];Argument[-1];taint",
|
||||
@@ -384,14 +405,18 @@ private class ApacheStrBuilderModel extends SummaryModelCsv {
|
||||
"org.apache.commons.text;TextStringBuilder;false;appendSeparator;(java.lang.String,java.lang.String);;Argument[0..1];Argument[-1];taint",
|
||||
"org.apache.commons.text;TextStringBuilder;false;appendSeparator;;;Argument[-1];ReturnValue;taint",
|
||||
"org.apache.commons.text;TextStringBuilder;false;appendTo;;;Argument[-1];Argument[0];taint",
|
||||
"org.apache.commons.text;TextStringBuilder;false;appendWithSeparators;;;Argument[0..1];Argument[-1];taint",
|
||||
"org.apache.commons.text;TextStringBuilder;false;appendWithSeparators;(Iterable,String);;Element of Argument[0];Argument[-1];taint",
|
||||
"org.apache.commons.text;TextStringBuilder;false;appendWithSeparators;(Iterator,String);;Element of Argument[0];Argument[-1];taint",
|
||||
"org.apache.commons.text;TextStringBuilder;false;appendWithSeparators;(Object[],String);;ArrayElement of Argument[0];Argument[-1];taint",
|
||||
"org.apache.commons.text;TextStringBuilder;false;appendWithSeparators;;;Argument[1];Argument[-1];taint",
|
||||
"org.apache.commons.text;TextStringBuilder;false;appendWithSeparators;;;Argument[-1];ReturnValue;taint",
|
||||
"org.apache.commons.text;TextStringBuilder;false;appendln;(char[]);;Argument[0];Argument[-1];taint",
|
||||
"org.apache.commons.text;TextStringBuilder;false;appendln;(char[],int,int);;Argument[0];Argument[-1];taint",
|
||||
"org.apache.commons.text;TextStringBuilder;false;appendln;(java.lang.Object);;Argument[0];Argument[-1];taint",
|
||||
"org.apache.commons.text;TextStringBuilder;false;appendln;(java.lang.String);;Argument[0];Argument[-1];taint",
|
||||
"org.apache.commons.text;TextStringBuilder;false;appendln;(java.lang.String,int,int);;Argument[0];Argument[-1];taint",
|
||||
"org.apache.commons.text;TextStringBuilder;false;appendln;(java.lang.String,java.lang.Object[]);;Argument[0..1];Argument[-1];taint",
|
||||
"org.apache.commons.text;TextStringBuilder;false;appendln;(java.lang.String,java.lang.Object[]);;Argument[0];Argument[-1];taint",
|
||||
"org.apache.commons.text;TextStringBuilder;false;appendln;(java.lang.String,java.lang.Object[]);;ArrayElement of Argument[1];Argument[-1];taint",
|
||||
"org.apache.commons.text;TextStringBuilder;false;appendln;(java.lang.StringBuffer);;Argument[0];Argument[-1];taint",
|
||||
"org.apache.commons.text;TextStringBuilder;false;appendln;(java.lang.StringBuffer,int,int);;Argument[0];Argument[-1];taint",
|
||||
"org.apache.commons.text;TextStringBuilder;false;appendln;(java.lang.StringBuilder);;Argument[0];Argument[-1];taint",
|
||||
@@ -427,6 +452,101 @@ private class ApacheStrBuilderModel extends SummaryModelCsv {
|
||||
}
|
||||
}
|
||||
|
||||
private class ApacheStrBuilderFluentMethodsModel extends SummaryModelCsv {
|
||||
override predicate row(string row) {
|
||||
row =
|
||||
[
|
||||
"org.apache.commons.lang3.text;StrBuilder;false;append;;;Argument[-1];ReturnValue;value",
|
||||
"org.apache.commons.lang3.text;StrBuilder;false;appendAll;;;Argument[-1];ReturnValue;value",
|
||||
"org.apache.commons.lang3.text;StrBuilder;false;appendFixedWidthPadLeft;;;Argument[-1];ReturnValue;value",
|
||||
"org.apache.commons.lang3.text;StrBuilder;false;appendFixedWidthPadRight;;;Argument[-1];ReturnValue;value",
|
||||
"org.apache.commons.lang3.text;StrBuilder;false;appendln;;;Argument[-1];ReturnValue;value",
|
||||
"org.apache.commons.lang3.text;StrBuilder;false;appendNewLine;;;Argument[-1];ReturnValue;value",
|
||||
"org.apache.commons.lang3.text;StrBuilder;false;appendNull;;;Argument[-1];ReturnValue;value",
|
||||
"org.apache.commons.lang3.text;StrBuilder;false;appendPadding;;;Argument[-1];ReturnValue;value",
|
||||
"org.apache.commons.lang3.text;StrBuilder;false;appendSeparator;;;Argument[-1];ReturnValue;value",
|
||||
"org.apache.commons.lang3.text;StrBuilder;false;appendWithSeparators;;;Argument[-1];ReturnValue;value",
|
||||
"org.apache.commons.lang3.text;StrBuilder;false;delete;;;Argument[-1];ReturnValue;value",
|
||||
"org.apache.commons.lang3.text;StrBuilder;false;deleteAll;;;Argument[-1];ReturnValue;value",
|
||||
"org.apache.commons.lang3.text;StrBuilder;false;deleteCharAt;;;Argument[-1];ReturnValue;value",
|
||||
"org.apache.commons.lang3.text;StrBuilder;false;deleteFirst;;;Argument[-1];ReturnValue;value",
|
||||
"org.apache.commons.lang3.text;StrBuilder;false;ensureCapacity;;;Argument[-1];ReturnValue;value",
|
||||
"org.apache.commons.lang3.text;StrBuilder;false;insert;;;Argument[-1];ReturnValue;value",
|
||||
"org.apache.commons.lang3.text;StrBuilder;false;minimizeCapacity;;;Argument[-1];ReturnValue;value",
|
||||
"org.apache.commons.lang3.text;StrBuilder;false;replace;;;Argument[-1];ReturnValue;value",
|
||||
"org.apache.commons.lang3.text;StrBuilder;false;replaceAll;;;Argument[-1];ReturnValue;value",
|
||||
"org.apache.commons.lang3.text;StrBuilder;false;replaceFirst;;;Argument[-1];ReturnValue;value",
|
||||
"org.apache.commons.lang3.text;StrBuilder;false;reverse;;;Argument[-1];ReturnValue;value",
|
||||
"org.apache.commons.lang3.text;StrBuilder;false;setCharAt;;;Argument[-1];ReturnValue;value",
|
||||
"org.apache.commons.lang3.text;StrBuilder;false;setLength;;;Argument[-1];ReturnValue;value",
|
||||
"org.apache.commons.lang3.text;StrBuilder;false;setNewLineText;;;Argument[-1];ReturnValue;value",
|
||||
"org.apache.commons.lang3.text;StrBuilder;false;setNullText;;;Argument[-1];ReturnValue;value",
|
||||
"org.apache.commons.lang3.text;StrBuilder;false;trim;;;Argument[-1];ReturnValue;value",
|
||||
"org.apache.commons.text;StrBuilder;false;append;;;Argument[-1];ReturnValue;value",
|
||||
"org.apache.commons.text;StrBuilder;false;appendAll;;;Argument[-1];ReturnValue;value",
|
||||
"org.apache.commons.text;StrBuilder;false;appendFixedWidthPadLeft;;;Argument[-1];ReturnValue;value",
|
||||
"org.apache.commons.text;StrBuilder;false;appendFixedWidthPadRight;;;Argument[-1];ReturnValue;value",
|
||||
"org.apache.commons.text;StrBuilder;false;appendln;;;Argument[-1];ReturnValue;value",
|
||||
"org.apache.commons.text;StrBuilder;false;appendNewLine;;;Argument[-1];ReturnValue;value",
|
||||
"org.apache.commons.text;StrBuilder;false;appendNull;;;Argument[-1];ReturnValue;value",
|
||||
"org.apache.commons.text;StrBuilder;false;appendPadding;;;Argument[-1];ReturnValue;value",
|
||||
"org.apache.commons.text;StrBuilder;false;appendSeparator;;;Argument[-1];ReturnValue;value",
|
||||
"org.apache.commons.text;StrBuilder;false;appendWithSeparators;;;Argument[-1];ReturnValue;value",
|
||||
"org.apache.commons.text;StrBuilder;false;delete;;;Argument[-1];ReturnValue;value",
|
||||
"org.apache.commons.text;StrBuilder;false;deleteAll;;;Argument[-1];ReturnValue;value",
|
||||
"org.apache.commons.text;StrBuilder;false;deleteCharAt;;;Argument[-1];ReturnValue;value",
|
||||
"org.apache.commons.text;StrBuilder;false;deleteFirst;;;Argument[-1];ReturnValue;value",
|
||||
"org.apache.commons.text;StrBuilder;false;ensureCapacity;;;Argument[-1];ReturnValue;value",
|
||||
"org.apache.commons.text;StrBuilder;false;insert;;;Argument[-1];ReturnValue;value",
|
||||
"org.apache.commons.text;StrBuilder;false;minimizeCapacity;;;Argument[-1];ReturnValue;value",
|
||||
"org.apache.commons.text;StrBuilder;false;replace;;;Argument[-1];ReturnValue;value",
|
||||
"org.apache.commons.text;StrBuilder;false;replaceAll;;;Argument[-1];ReturnValue;value",
|
||||
"org.apache.commons.text;StrBuilder;false;replaceFirst;;;Argument[-1];ReturnValue;value",
|
||||
"org.apache.commons.text;StrBuilder;false;reverse;;;Argument[-1];ReturnValue;value",
|
||||
"org.apache.commons.text;StrBuilder;false;setCharAt;;;Argument[-1];ReturnValue;value",
|
||||
"org.apache.commons.text;StrBuilder;false;setLength;;;Argument[-1];ReturnValue;value",
|
||||
"org.apache.commons.text;StrBuilder;false;setNewLineText;;;Argument[-1];ReturnValue;value",
|
||||
"org.apache.commons.text;StrBuilder;false;setNullText;;;Argument[-1];ReturnValue;value",
|
||||
"org.apache.commons.text;StrBuilder;false;trim;;;Argument[-1];ReturnValue;value",
|
||||
"org.apache.commons.text;TextStringBuilder;false;append;;;Argument[-1];ReturnValue;value",
|
||||
"org.apache.commons.text;TextStringBuilder;false;appendAll;;;Argument[-1];ReturnValue;value",
|
||||
"org.apache.commons.text;TextStringBuilder;false;appendFixedWidthPadLeft;;;Argument[-1];ReturnValue;value",
|
||||
"org.apache.commons.text;TextStringBuilder;false;appendFixedWidthPadRight;;;Argument[-1];ReturnValue;value",
|
||||
"org.apache.commons.text;TextStringBuilder;false;appendln;;;Argument[-1];ReturnValue;value",
|
||||
"org.apache.commons.text;TextStringBuilder;false;appendNewLine;;;Argument[-1];ReturnValue;value",
|
||||
"org.apache.commons.text;TextStringBuilder;false;appendNull;;;Argument[-1];ReturnValue;value",
|
||||
"org.apache.commons.text;TextStringBuilder;false;appendPadding;;;Argument[-1];ReturnValue;value",
|
||||
"org.apache.commons.text;TextStringBuilder;false;appendSeparator;;;Argument[-1];ReturnValue;value",
|
||||
"org.apache.commons.text;TextStringBuilder;false;appendWithSeparators;;;Argument[-1];ReturnValue;value",
|
||||
"org.apache.commons.text;TextStringBuilder;false;delete;;;Argument[-1];ReturnValue;value",
|
||||
"org.apache.commons.text;TextStringBuilder;false;deleteAll;;;Argument[-1];ReturnValue;value",
|
||||
"org.apache.commons.text;TextStringBuilder;false;deleteCharAt;;;Argument[-1];ReturnValue;value",
|
||||
"org.apache.commons.text;TextStringBuilder;false;deleteFirst;;;Argument[-1];ReturnValue;value",
|
||||
"org.apache.commons.text;TextStringBuilder;false;ensureCapacity;;;Argument[-1];ReturnValue;value",
|
||||
"org.apache.commons.text;TextStringBuilder;false;insert;;;Argument[-1];ReturnValue;value",
|
||||
"org.apache.commons.text;TextStringBuilder;false;minimizeCapacity;;;Argument[-1];ReturnValue;value",
|
||||
"org.apache.commons.text;TextStringBuilder;false;replace;;;Argument[-1];ReturnValue;value",
|
||||
"org.apache.commons.text;TextStringBuilder;false;replaceAll;;;Argument[-1];ReturnValue;value",
|
||||
"org.apache.commons.text;TextStringBuilder;false;replaceFirst;;;Argument[-1];ReturnValue;value",
|
||||
"org.apache.commons.text;TextStringBuilder;false;reverse;;;Argument[-1];ReturnValue;value",
|
||||
"org.apache.commons.text;TextStringBuilder;false;setCharAt;;;Argument[-1];ReturnValue;value",
|
||||
"org.apache.commons.text;TextStringBuilder;false;setLength;;;Argument[-1];ReturnValue;value",
|
||||
"org.apache.commons.text;TextStringBuilder;false;setNewLineText;;;Argument[-1];ReturnValue;value",
|
||||
"org.apache.commons.text;TextStringBuilder;false;setNullText;;;Argument[-1];ReturnValue;value",
|
||||
"org.apache.commons.text;TextStringBuilder;false;trim;;;Argument[-1];ReturnValue;value"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* An Apache Commons-Lang StrBuilder method that returns `this`.
|
||||
*/
|
||||
private class ApacheStrBuilderFluentMethod extends FluentMethod {
|
||||
ApacheStrBuilderFluentMethod() {
|
||||
this.getReturnType().(RefType).hasQualifiedName("org.apache.commons.lang3.text", "StrBuilder")
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Taint-propagating models for `WordUtils`.
|
||||
*/
|
||||
@@ -525,9 +645,9 @@ private class ApacheStrLookupModel extends SummaryModelCsv {
|
||||
row =
|
||||
[
|
||||
"org.apache.commons.lang3.text;StrLookup;false;lookup;;;Argument[-1];ReturnValue;taint",
|
||||
"org.apache.commons.lang3.text;StrLookup;false;mapLookup;;;Argument[0];ReturnValue;taint",
|
||||
"org.apache.commons.lang3.text;StrLookup;false;mapLookup;;;MapValue of Argument[0];ReturnValue;taint",
|
||||
"org.apache.commons.text.lookup;StringLookup;true;lookup;;;Argument[-1];ReturnValue;taint",
|
||||
"org.apache.commons.text.lookup;StringLookupFactory;false;mapStringLookup;;;Argument[0];ReturnValue;taint"
|
||||
"org.apache.commons.text.lookup;StringLookupFactory;false;mapStringLookup;;;MapValue of Argument[0];ReturnValue;taint"
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -540,6 +660,7 @@ private class ApacheStrSubstitutorModel extends SummaryModelCsv {
|
||||
row =
|
||||
[
|
||||
"org.apache.commons.lang3.text;StrSubstitutor;false;StrSubstitutor;;;Argument[0];Argument[-1];taint",
|
||||
"org.apache.commons.lang3.text;StrSubstitutor;false;StrSubstitutor;;;MapValue of Argument[0];Argument[-1];taint",
|
||||
"org.apache.commons.lang3.text;StrSubstitutor;false;replace;;;Argument[-1];ReturnValue;taint",
|
||||
"org.apache.commons.lang3.text;StrSubstitutor;false;replace;(java.lang.Object);;Argument[0];ReturnValue;taint",
|
||||
"org.apache.commons.lang3.text;StrSubstitutor;false;replace;(char[]);;Argument[0];ReturnValue;taint",
|
||||
@@ -552,10 +673,12 @@ private class ApacheStrSubstitutorModel extends SummaryModelCsv {
|
||||
"org.apache.commons.lang3.text;StrSubstitutor;false;replace;(java.lang.StringBuffer,int,int);;Argument[0];ReturnValue;taint",
|
||||
"org.apache.commons.lang3.text;StrSubstitutor;false;replace;(java.lang.String,int,int);;Argument[0];ReturnValue;taint",
|
||||
"org.apache.commons.lang3.text;StrSubstitutor;false;replace;(org.apache.commons.lang3.text.StrBuilder,int,int);;Argument[0];ReturnValue;taint",
|
||||
"org.apache.commons.lang3.text;StrSubstitutor;false;replace;(java.lang.Object,java.util.Map);;Argument[0..1];ReturnValue;taint",
|
||||
"org.apache.commons.lang3.text;StrSubstitutor;false;replace;(java.lang.Object,java.util.Map);;Argument[0];ReturnValue;taint",
|
||||
"org.apache.commons.lang3.text;StrSubstitutor;false;replace;(java.lang.Object,java.util.Map);;MapValue of Argument[1];ReturnValue;taint",
|
||||
"org.apache.commons.lang3.text;StrSubstitutor;false;replace;(java.lang.Object,java.util.Map,java.lang.String,java.lang.String);;Argument[0];ReturnValue;taint",
|
||||
"org.apache.commons.lang3.text;StrSubstitutor;false;replace;(java.lang.Object,java.util.Map,java.lang.String,java.lang.String);;Argument[1];ReturnValue;taint",
|
||||
"org.apache.commons.lang3.text;StrSubstitutor;false;replace;(java.lang.Object,java.util.Properties);;Argument[0..1];ReturnValue;taint",
|
||||
"org.apache.commons.lang3.text;StrSubstitutor;false;replace;(java.lang.Object,java.util.Map,java.lang.String,java.lang.String);;MapValue of Argument[1];ReturnValue;taint",
|
||||
"org.apache.commons.lang3.text;StrSubstitutor;false;replace;(java.lang.Object,java.util.Properties);;Argument[0];ReturnValue;taint",
|
||||
"org.apache.commons.lang3.text;StrSubstitutor;false;replace;(java.lang.Object,java.util.Properties);;MapValue of Argument[1];ReturnValue;taint",
|
||||
"org.apache.commons.lang3.text;StrSubstitutor;false;setVariableResolver;;;Argument[0];Argument[-1];taint",
|
||||
"org.apache.commons.lang3.text;StrSubstitutor;false;replaceIn;(org.apache.commons.lang3.text.StrBuilder);;Argument[-1];Argument[0];taint",
|
||||
"org.apache.commons.lang3.text;StrSubstitutor;false;replaceIn;(java.lang.StringBuffer);;Argument[-1];Argument[0];taint",
|
||||
@@ -564,6 +687,7 @@ private class ApacheStrSubstitutorModel extends SummaryModelCsv {
|
||||
"org.apache.commons.lang3.text;StrSubstitutor;false;replaceIn;(java.lang.StringBuilder,int,int);;Argument[-1];Argument[0];taint",
|
||||
"org.apache.commons.lang3.text;StrSubstitutor;false;replaceIn;(org.apache.commons.lang3.text.StrBuilder,int,int);;Argument[-1];Argument[0];taint",
|
||||
"org.apache.commons.text;StringSubstitutor;false;StringSubstitutor;;;Argument[0];Argument[-1];taint",
|
||||
"org.apache.commons.text;StringSubstitutor;false;StringSubstitutor;;;MapValue of Argument[0];Argument[-1];taint",
|
||||
"org.apache.commons.text;StringSubstitutor;false;replace;;;Argument[-1];ReturnValue;taint",
|
||||
"org.apache.commons.text;StringSubstitutor;false;replace;(java.lang.Object);;Argument[0];ReturnValue;taint",
|
||||
"org.apache.commons.text;StringSubstitutor;false;replace;(char[]);;Argument[0];ReturnValue;taint",
|
||||
@@ -574,10 +698,12 @@ private class ApacheStrSubstitutorModel extends SummaryModelCsv {
|
||||
"org.apache.commons.text;StringSubstitutor;false;replace;(java.lang.StringBuffer);;Argument[0];ReturnValue;taint",
|
||||
"org.apache.commons.text;StringSubstitutor;false;replace;(java.lang.StringBuffer,int,int);;Argument[0];ReturnValue;taint",
|
||||
"org.apache.commons.text;StringSubstitutor;false;replace;(java.lang.String,int,int);;Argument[0];ReturnValue;taint",
|
||||
"org.apache.commons.text;StringSubstitutor;false;replace;(java.lang.Object,java.util.Map);;Argument[0..1];ReturnValue;taint",
|
||||
"org.apache.commons.text;StringSubstitutor;false;replace;(java.lang.Object,java.util.Map);;Argument[0];ReturnValue;taint",
|
||||
"org.apache.commons.text;StringSubstitutor;false;replace;(java.lang.Object,java.util.Map);;MapValue of Argument[1];ReturnValue;taint",
|
||||
"org.apache.commons.text;StringSubstitutor;false;replace;(java.lang.Object,java.util.Map,java.lang.String,java.lang.String);;Argument[0];ReturnValue;taint",
|
||||
"org.apache.commons.text;StringSubstitutor;false;replace;(java.lang.Object,java.util.Map,java.lang.String,java.lang.String);;Argument[1];ReturnValue;taint",
|
||||
"org.apache.commons.text;StringSubstitutor;false;replace;(java.lang.Object,java.util.Properties);;Argument[0..1];ReturnValue;taint",
|
||||
"org.apache.commons.text;StringSubstitutor;false;replace;(java.lang.Object,java.util.Map,java.lang.String,java.lang.String);;MapValue of Argument[1];ReturnValue;taint",
|
||||
"org.apache.commons.text;StringSubstitutor;false;replace;(java.lang.Object,java.util.Properties);;Argument[0];ReturnValue;taint",
|
||||
"org.apache.commons.text;StringSubstitutor;false;replace;(java.lang.Object,java.util.Properties);;MapValue of Argument[1];ReturnValue;taint",
|
||||
"org.apache.commons.text;StringSubstitutor;false;replace;(org.apache.commons.text.TextStringBuilder);;Argument[0];ReturnValue;taint",
|
||||
"org.apache.commons.text;StringSubstitutor;false;replace;(org.apache.commons.text.TextStringBuilder,int,int);;Argument[0];ReturnValue;taint",
|
||||
"org.apache.commons.text;StringSubstitutor;false;setVariableResolver;;;Argument[0];Argument[-1];taint",
|
||||
@@ -626,14 +752,40 @@ private class ApacheObjectUtilsModel extends SummaryModelCsv {
|
||||
"org.apache.commons.lang3;ObjectUtils;false;CONST_BYTE;;;Argument[0];ReturnValue;value",
|
||||
"org.apache.commons.lang3;ObjectUtils;false;CONST_SHORT;;;Argument[0];ReturnValue;value",
|
||||
"org.apache.commons.lang3;ObjectUtils;false;defaultIfNull;;;Argument[0..1];ReturnValue;value",
|
||||
"org.apache.commons.lang3;ObjectUtils;false;firstNonNull;;;Argument[0];ReturnValue;taint",
|
||||
"org.apache.commons.lang3;ObjectUtils;false;firstNonNull;;;ArrayElement of Argument[0];ReturnValue;value",
|
||||
"org.apache.commons.lang3;ObjectUtils;false;getIfNull;;;Argument[0];ReturnValue;value",
|
||||
"org.apache.commons.lang3;ObjectUtils;false;max;;;Argument[0];ReturnValue;taint",
|
||||
"org.apache.commons.lang3;ObjectUtils;false;median;;;Argument[0];ReturnValue;taint",
|
||||
"org.apache.commons.lang3;ObjectUtils;false;min;;;Argument[0];ReturnValue;taint",
|
||||
"org.apache.commons.lang3;ObjectUtils;false;mode;;;Argument[0];ReturnValue;taint",
|
||||
"org.apache.commons.lang3;ObjectUtils;false;max;;;ArrayElement of Argument[0];ReturnValue;value",
|
||||
"org.apache.commons.lang3;ObjectUtils;false;median;;;ArrayElement of Argument[0];ReturnValue;value",
|
||||
"org.apache.commons.lang3;ObjectUtils;false;min;;;ArrayElement of Argument[0];ReturnValue;value",
|
||||
"org.apache.commons.lang3;ObjectUtils;false;mode;;;ArrayElement of Argument[0];ReturnValue;value",
|
||||
"org.apache.commons.lang3;ObjectUtils;false;requireNonEmpty;;;Argument[0];ReturnValue;value",
|
||||
"org.apache.commons.lang3;ObjectUtils;false;toString;(Object,String);;Argument[1];ReturnValue;value"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
private class ApacheToStringBuilderModel extends SummaryModelCsv {
|
||||
override predicate row(string row) {
|
||||
row =
|
||||
[
|
||||
"org.apache.commons.lang3.builder;ToStringBuilder;false;toString;;;Argument[-1];ReturnValue;taint",
|
||||
"org.apache.commons.lang3.builder;ToStringBuilder;false;append;(java.lang.Object);;Argument[0];Argument[-1];taint",
|
||||
"org.apache.commons.lang3.builder;ToStringBuilder;false;append;(java.lang.Object[]);;ArrayElement of Argument[0];Argument[-1];taint",
|
||||
"org.apache.commons.lang3.builder;ToStringBuilder;false;append;(java.lang.String,java.lang.Object[]);;Argument[0];Argument[-1];taint",
|
||||
"org.apache.commons.lang3.builder;ToStringBuilder;false;append;(java.lang.String,java.lang.Object[]);;ArrayElement of Argument[1];Argument[-1];taint",
|
||||
"org.apache.commons.lang3.builder;ToStringBuilder;false;append;(java.lang.String,boolean);;Argument[0];Argument[-1];taint",
|
||||
"org.apache.commons.lang3.builder;ToStringBuilder;false;append;(java.lang.String,java.lang.Object);;Argument[0..1];Argument[-1];taint",
|
||||
"org.apache.commons.lang3.builder;ToStringBuilder;false;append;(java.lang.String,java.lang.Object[],boolean);;Argument[0];Argument[-1];taint",
|
||||
"org.apache.commons.lang3.builder;ToStringBuilder;false;append;(java.lang.String,java.lang.Object[],boolean);;ArrayElement of Argument[1];Argument[-1];taint",
|
||||
"org.apache.commons.lang3.builder;ToStringBuilder;false;build;;;Argument[-1];ReturnValue;taint",
|
||||
"org.apache.commons.lang3.builder;ToStringBuilder;false;getStringBuffer;;;Argument[-1];ReturnValue;taint",
|
||||
"org.apache.commons.lang3.builder;ToStringBuilder;false;appendToString;;;Argument[0];Argument[-1];taint",
|
||||
"org.apache.commons.lang3.builder;ToStringBuilder;false;appendSuper;;;Argument[0];Argument[-1];taint",
|
||||
// The following are value-preserving steps for fluent methods:
|
||||
"org.apache.commons.lang3.builder;ToStringBuilder;false;append;;;Argument[-1];ReturnValue;value",
|
||||
"org.apache.commons.lang3.builder;ToStringBuilder;false;appendAsObjectToString;;;Argument[-1];ReturnValue;value",
|
||||
"org.apache.commons.lang3.builder;ToStringBuilder;false;appendSuper;;;Argument[-1];ReturnValue;value",
|
||||
"org.apache.commons.lang3.builder;ToStringBuilder;false;appendToString;;;Argument[-1];ReturnValue;value"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,7 +13,8 @@ private class GuavaBaseCsv extends SummaryModelCsv {
|
||||
"com.google.common.base;Strings;false;padStart;(String,int,char);;Argument[0];ReturnValue;taint",
|
||||
"com.google.common.base;Strings;false;padEnd;(String,int,char);;Argument[0];ReturnValue;taint",
|
||||
"com.google.common.base;Strings;false;repeat;(String,int);;Argument[0];ReturnValue;taint",
|
||||
"com.google.common.base;Strings;false;lenientFormat;(String,Object[]);;Argument;ReturnValue;taint",
|
||||
"com.google.common.base;Strings;false;lenientFormat;(String,Object[]);;Argument[0];ReturnValue;taint",
|
||||
"com.google.common.base;Strings;false;lenientFormat;(String,Object[]);;ArrayElement of Argument[1];ReturnValue;taint",
|
||||
"com.google.common.base;Joiner;false;on;(String);;Argument[0];ReturnValue;taint",
|
||||
"com.google.common.base;Joiner;false;skipNulls;();;Argument[-1];ReturnValue;taint",
|
||||
"com.google.common.base;Joiner;false;useForNull;(String);;Argument[-1];ReturnValue;taint",
|
||||
@@ -21,20 +22,77 @@ private class GuavaBaseCsv extends SummaryModelCsv {
|
||||
"com.google.common.base;Joiner;false;withKeyValueSeparator;(String);;Argument[0];ReturnValue;taint",
|
||||
"com.google.common.base;Joiner;false;withKeyValueSeparator;(String);;Argument[-1];ReturnValue;taint",
|
||||
"com.google.common.base;Joiner;false;withKeyValueSeparator;(char);;Argument[-1];ReturnValue;taint",
|
||||
// Note: The signatures of some of the appendTo methods involve collection flow
|
||||
"com.google.common.base;Joiner;false;appendTo;;;Argument;Argument[0];taint",
|
||||
"com.google.common.base;Joiner;false;appendTo;(Appendable,Object,Object,Object[]);;Argument[1..2];Argument[0];taint",
|
||||
"com.google.common.base;Joiner;false;appendTo;(Appendable,Object,Object,Object[]);;ArrayElement of Argument[3];Argument[0];taint",
|
||||
"com.google.common.base;Joiner;false;appendTo;(Appendable,Iterable);;Element of Argument[1];Argument[-1];taint",
|
||||
"com.google.common.base;Joiner;false;appendTo;(Appendable,Object[]);;ArrayElement of Argument[1];Argument[-1];taint",
|
||||
"com.google.common.base;Joiner;false;appendTo;(Appendable,Iterator);;Element of Argument[1];Argument[-1];taint",
|
||||
"com.google.common.base;Joiner;false;appendTo;(StringBuilder,Object,Object,Object[]);;Argument[1..2];Argument[0];taint",
|
||||
"com.google.common.base;Joiner;false;appendTo;(StringBuilder,Object,Object,Object[]);;ArrayElement of Argument[3];Argument[0];taint",
|
||||
"com.google.common.base;Joiner;false;appendTo;(StringBuilder,Iterable);;Element of Argument[1];Argument[-1];taint",
|
||||
"com.google.common.base;Joiner;false;appendTo;(StringBuilder,Object[]);;ArrayElement of Argument[1];Argument[-1];taint",
|
||||
"com.google.common.base;Joiner;false;appendTo;(StringBuilder,Iterator);;Element of Argument[1];Argument[-1];taint",
|
||||
"com.google.common.base;Joiner;false;appendTo;;;Argument[-1];Argument[0];taint",
|
||||
"com.google.common.base;Joiner;false;appendTo;;;Argument[0];ReturnValue;value",
|
||||
"com.google.common.base;Joiner;false;join;;;Argument;ReturnValue;taint",
|
||||
"com.google.common.base;Joiner;false;join;;;Argument[-1..2];ReturnValue;taint",
|
||||
"com.google.common.base;Joiner$MapJoiner;false;useForNull;(String);;Argument[0];ReturnValue;taint",
|
||||
"com.google.common.base;Joiner$MapJoiner;false;useForNull;(String);;Argument[-1];ReturnValue;taint",
|
||||
"com.google.common.base;Joiner$MapJoiner;false;appendTo;;;Argument;Argument[0];taint",
|
||||
"com.google.common.base;Joiner$MapJoiner;false;appendTo;;;Argument[1];Argument[0];taint",
|
||||
"com.google.common.base;Joiner$MapJoiner;false;appendTo;;;Argument[0];ReturnValue;value",
|
||||
"com.google.common.base;Joiner$MapJoiner;false;join;;;Argument;ReturnValue;taint",
|
||||
"com.google.common.base;Joiner$MapJoiner;false;join;;;Argument[-1];ReturnValue;taint",
|
||||
"com.google.common.base;Joiner$MapJoiner;false;join;(Iterable);;MapKey of Element of Argument[0];ReturnValue;taint",
|
||||
"com.google.common.base;Joiner$MapJoiner;false;join;(Iterable);;MapValue of Element of Argument[0];ReturnValue;taint",
|
||||
"com.google.common.base;Joiner$MapJoiner;false;join;(Iterator);;MapKey of Element of Argument[0];ReturnValue;taint",
|
||||
"com.google.common.base;Joiner$MapJoiner;false;join;(Iterator);;MapValue of Element of Argument[0];ReturnValue;taint",
|
||||
"com.google.common.base;Joiner$MapJoiner;false;join;(Map);;MapKey of Argument[0];ReturnValue;taint",
|
||||
"com.google.common.base;Joiner$MapJoiner;false;join;(Map);;MapValue of Argument[0];ReturnValue;taint",
|
||||
"com.google.common.base;Splitter;false;split;(CharSequence);;Argument[0];ReturnValue;taint",
|
||||
"com.google.common.base;Splitter;false;splitToList;(CharSequence);;Argument[0];ReturnValue;taint",
|
||||
"com.google.common.base;Splitter;false;splitToStream;(CharSequence);;Argument[0];ReturnValue;taint",
|
||||
"com.google.common.base;Splitter$MapSplitter;false;split;(CharSequence);;Argument[0];ReturnValue;taint",
|
||||
"com.google.common.base;Preconditions;false;checkNotNull;;;Argument[0];ReturnValue;value"
|
||||
"com.google.common.base;Preconditions;false;checkNotNull;;;Argument[0];ReturnValue;value",
|
||||
"com.google.common.base;Verify;false;verifyNotNull;;;Argument[0];ReturnValue;value",
|
||||
"com.google.common.base;Ascii;false;toLowerCase;(CharSequence);;Argument[0];ReturnValue;taint",
|
||||
"com.google.common.base;Ascii;false;toLowerCase;(String);;Argument[0];ReturnValue;taint",
|
||||
"com.google.common.base;Ascii;false;toUpperCase;(CharSequence);;Argument[0];ReturnValue;taint",
|
||||
"com.google.common.base;Ascii;false;toUpperCase;(String);;Argument[0];ReturnValue;taint",
|
||||
"com.google.common.base;Ascii;false;truncate;(CharSequence,int,String);;Argument[0];ReturnValue;taint",
|
||||
"com.google.common.base;Ascii;false;truncate;(CharSequence,int,String);;Argument[2];ReturnValue;taint",
|
||||
"com.google.common.base;CaseFormat;true;to;(CaseFormat,String);;Argument[1];ReturnValue;taint",
|
||||
"com.google.common.base;Converter;true;apply;(Object);;Argument[0];ReturnValue;taint",
|
||||
"com.google.common.base;Converter;true;convert;(Object);;Argument[0];ReturnValue;taint",
|
||||
"com.google.common.base;Converter;true;convertAll;(Iterable);;Element of Argument[0];Element of ReturnValue;taint",
|
||||
"com.google.common.base;Supplier;true;get;();;Argument[0];ReturnValue;taint",
|
||||
"com.google.common.base;Suppliers;false;ofInstance;(Object);;Argument[0];ReturnValue;taint",
|
||||
"com.google.common.base;Suppliers;false;memoize;(Supplier);;Argument[0];ReturnValue;taint",
|
||||
"com.google.common.base;Suppliers;false;memoizeWithExpiration;(Supplier,long,TimeUnit);;Argument[0];ReturnValue;taint",
|
||||
"com.google.common.base;Suppliers;false;synchronizedSupplier;(Supplier);;Argument[0];ReturnValue;taint",
|
||||
"com.google.common.base;Optional;true;fromJavaUtil;(Optional);;Element of Argument[0];Element of ReturnValue;value",
|
||||
"com.google.common.base;Optional;true;fromNullable;(Object);;Argument[0];Element of ReturnValue;value",
|
||||
"com.google.common.base;Optional;true;get;();;Element of Argument[-1];ReturnValue;value",
|
||||
"com.google.common.base;Optional;true;asSet;();;Element of Argument[-1];Element of ReturnValue;value",
|
||||
"com.google.common.base;Optional;true;of;(Object);;Argument[0];Element of ReturnValue;value",
|
||||
"com.google.common.base;Optional;true;or;(Optional);;Element of Argument[-1..0];Element of ReturnValue;value",
|
||||
"com.google.common.base;Optional;true;or;(Supplier);;Element of Argument[-1];ReturnValue;value",
|
||||
"com.google.common.base;Optional;true;or;(Supplier);;Argument[0];ReturnValue;taint",
|
||||
"com.google.common.base;Optional;true;or;(Object);;Element of Argument[-1];ReturnValue;value",
|
||||
"com.google.common.base;Optional;true;or;(Object);;Argument[0];ReturnValue;value",
|
||||
"com.google.common.base;Optional;true;orNull;();;Element of Argument[-1];ReturnValue;value",
|
||||
"com.google.common.base;Optional;true;presentInstances;(Iterable);;Element of Element of Argument[0];Element of ReturnValue;value",
|
||||
"com.google.common.base;Optional;true;toJavaUtil;();;Element of Argument[-1];Element of ReturnValue;value",
|
||||
"com.google.common.base;Optional;true;toJavaUtil;(Optional);;Element of Argument[0];Element of ReturnValue;value",
|
||||
"com.google.common.base;MoreObjects;false;firstNonNull;(Object,Object);;Argument[0..1];ReturnValue;value",
|
||||
"com.google.common.base;MoreObjects;false;toStringHelper;(String);;Argument[0];ReturnValue;taint",
|
||||
"com.google.common.base;MoreObjects$ToStringHelper;false;add;;;Argument[0];ReturnValue;taint",
|
||||
"com.google.common.base;MoreObjects$ToStringHelper;false;add;;;Argument[0];Argument[-1];taint",
|
||||
"com.google.common.base;MoreObjects$ToStringHelper;false;add;;;Argument[-1];ReturnValue;value",
|
||||
"com.google.common.base;MoreObjects$ToStringHelper;false;add;(String,Object);;Argument[1];ReturnValue;taint",
|
||||
"com.google.common.base;MoreObjects$ToStringHelper;false;add;(String,Object);;Argument[1];Argument[-1];taint",
|
||||
"com.google.common.base;MoreObjects$ToStringHelper;false;addValue;;;Argument[-1];ReturnValue;value",
|
||||
"com.google.common.base;MoreObjects$ToStringHelper;false;addValue;(Object);;Argument[0];ReturnValue;taint",
|
||||
"com.google.common.base;MoreObjects$ToStringHelper;false;addValue;(Object);;Argument[0];Argument[-1];taint",
|
||||
"com.google.common.base;MoreObjects$ToStringHelper;false;omitNullValues;();;Argument[-1];ReturnValue;value",
|
||||
"com.google.common.base;MoreObjects$ToStringHelper;false;toString;();;Argument[-1];ReturnValue;taint"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
@@ -24,7 +24,9 @@ private class GuavaIoCsv extends SummaryModelCsv {
|
||||
"com.google.common.io;BaseEncoding;true;omitPadding;();;Argument[-1];ReturnValue;taint",
|
||||
"com.google.common.io;BaseEncoding;true;encode;(byte[],int,int);;Argument[-1];ReturnValue;taint",
|
||||
"com.google.common.io;ByteSource;true;asCharSource;(Charset);;Argument[-1];ReturnValue;taint",
|
||||
"com.google.common.io;ByteSource;true;concat;;;Argument[0];ReturnValue;taint",
|
||||
"com.google.common.io;ByteSource;true;concat;(ByteSource[]);;ArrayElement of Argument[0];ReturnValue;taint",
|
||||
"com.google.common.io;ByteSource;true;concat;(Iterable);;Element of Argument[0];ReturnValue;taint",
|
||||
"com.google.common.io;ByteSource;true;concat;(Iterator);;Element of Argument[0];ReturnValue;taint",
|
||||
"com.google.common.io;ByteSource;true;copyTo;(OutputStream);;Argument[-1];Argument[0];taint",
|
||||
"com.google.common.io;ByteSource;true;openStream;();;Argument[-1];ReturnValue;taint",
|
||||
"com.google.common.io;ByteSource;true;openBufferedStream;();;Argument[-1];ReturnValue;taint",
|
||||
@@ -43,7 +45,9 @@ private class GuavaIoCsv extends SummaryModelCsv {
|
||||
"com.google.common.io;ByteStreams;false;readFully;(InputStream,byte[],int,int);;Argument[0];Argument[1];taint",
|
||||
"com.google.common.io;ByteStreams;false;toByteArray;(InputStream);;Argument[0];ReturnValue;taint",
|
||||
"com.google.common.io;CharSource;true;asByteSource;(Charset);;Argument[-1];ReturnValue;taint",
|
||||
"com.google.common.io;CharSource;true;concat;;;Argument[0];ReturnValue;taint",
|
||||
"com.google.common.io;CharSource;true;concat;(CharSource[]);;ArrayElement of Argument[0];ReturnValue;taint",
|
||||
"com.google.common.io;CharSource;true;concat;(Iterable);;Element of Argument[0];ReturnValue;taint",
|
||||
"com.google.common.io;CharSource;true;concat;(Iterator);;Element of Argument[0];ReturnValue;taint",
|
||||
"com.google.common.io;CharSource;true;copyTo;(Appendable);;Argument[-1];Argument[0];taint",
|
||||
"com.google.common.io;CharSource;true;openStream;();;Argument[-1];ReturnValue;taint",
|
||||
"com.google.common.io;CharSource;true;openBufferedStream;();;Argument[-1];ReturnValue;taint",
|
||||
@@ -61,7 +65,7 @@ private class GuavaIoCsv extends SummaryModelCsv {
|
||||
"com.google.common.io;Files;false;simplifyPath;(String);;Argument[0];ReturnValue;taint",
|
||||
"com.google.common.io;MoreFiles;false;getFileExtension;(Path);;Argument[0];ReturnValue;taint",
|
||||
"com.google.common.io;MoreFiles;false;getNameWithoutExtension;(Path);;Argument[0];ReturnValue;taint",
|
||||
"com.google.common.io;LineReader;false;LineReader;(Readable);;Argument[0];ReturnValue;taint",
|
||||
"com.google.common.io;LineReader;false;LineReader;(Readable);;Argument[0];Argument[-1];taint",
|
||||
"com.google.common.io;LineReader;true;readLine;();;Argument[-1];ReturnValue;taint",
|
||||
"com.google.common.io;ByteArrayDataOutput;true;toByteArray;();;Argument[-1];ReturnValue;taint",
|
||||
"com.google.common.io;ByteArrayDataOutput;true;write;(byte[]);;Argument[0];Argument[-1];taint",
|
||||
|
||||
@@ -9,6 +9,7 @@ import semmle.code.java.Reflection
|
||||
import semmle.code.java.dataflow.DataFlow
|
||||
import semmle.code.java.dataflow.DataFlow5
|
||||
import semmle.code.java.dataflow.FlowSteps
|
||||
private import semmle.code.java.dataflow.ExternalFlow
|
||||
|
||||
/**
|
||||
* A `@com.fasterxml.jackson.annotation.JsonIgnore` annoation.
|
||||
@@ -28,7 +29,7 @@ abstract class JacksonSerializableType extends Type { }
|
||||
* A method used for serializing objects using Jackson. The final parameter is the object to be
|
||||
* serialized.
|
||||
*/
|
||||
library class JacksonWriteValueMethod extends Method, TaintPreservingCallable {
|
||||
private class JacksonWriteValueMethod extends Method, TaintPreservingCallable {
|
||||
JacksonWriteValueMethod() {
|
||||
(
|
||||
getDeclaringType().hasQualifiedName("com.fasterxml.jackson.databind", "ObjectWriter") or
|
||||
@@ -50,8 +51,20 @@ library class JacksonWriteValueMethod extends Method, TaintPreservingCallable {
|
||||
}
|
||||
}
|
||||
|
||||
private class JacksonReadValueMethod extends Method, TaintPreservingCallable {
|
||||
JacksonReadValueMethod() {
|
||||
(
|
||||
getDeclaringType().hasQualifiedName("com.fasterxml.jackson.databind", "ObjectReader") or
|
||||
getDeclaringType().hasQualifiedName("com.fasterxml.jackson.databind", "ObjectMapper")
|
||||
) and
|
||||
hasName(["readValue", "readValues"])
|
||||
}
|
||||
|
||||
override predicate returnsTaintFrom(int arg) { arg = 0 }
|
||||
}
|
||||
|
||||
/** A type whose values are explicitly serialized in a call to a Jackson method. */
|
||||
library class ExplicitlyWrittenJacksonSerializableType extends JacksonSerializableType {
|
||||
private class ExplicitlyWrittenJacksonSerializableType extends JacksonSerializableType {
|
||||
ExplicitlyWrittenJacksonSerializableType() {
|
||||
exists(MethodAccess ma |
|
||||
// A call to a Jackson write method...
|
||||
@@ -63,7 +76,7 @@ library class ExplicitlyWrittenJacksonSerializableType extends JacksonSerializab
|
||||
}
|
||||
|
||||
/** A type used in a `JacksonSerializableField` declaration. */
|
||||
library class FieldReferencedJacksonSerializableType extends JacksonSerializableType {
|
||||
private class FieldReferencedJacksonSerializableType extends JacksonSerializableType {
|
||||
FieldReferencedJacksonSerializableType() {
|
||||
exists(JacksonSerializableField f | usesType(f.getType(), this))
|
||||
}
|
||||
@@ -96,17 +109,24 @@ private class TypeLiteralToJacksonDatabindFlowConfiguration extends DataFlow5::C
|
||||
}
|
||||
|
||||
/** A type whose values are explicitly deserialized in a call to a Jackson method. */
|
||||
library class ExplicitlyReadJacksonDeserializableType extends JacksonDeserializableType {
|
||||
private class ExplicitlyReadJacksonDeserializableType extends JacksonDeserializableType {
|
||||
ExplicitlyReadJacksonDeserializableType() {
|
||||
exists(TypeLiteralToJacksonDatabindFlowConfiguration conf |
|
||||
usesType(conf.getSourceWithFlowToJacksonDatabind().getTypeName().getType(), this)
|
||||
)
|
||||
or
|
||||
exists(MethodAccess ma |
|
||||
// A call to a Jackson read method...
|
||||
ma.getMethod() instanceof JacksonReadValueMethod and
|
||||
// ...where `this` is used in the final argument, indicating that this type will be deserialized.
|
||||
usesType(ma.getArgument(ma.getNumArgument() - 1).getType(), this)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/** A type used in a `JacksonDeserializableField` declaration. */
|
||||
library class FieldReferencedJacksonDeSerializableType extends JacksonDeserializableType {
|
||||
FieldReferencedJacksonDeSerializableType() {
|
||||
private class FieldReferencedJacksonDeserializableType extends JacksonDeserializableType {
|
||||
FieldReferencedJacksonDeserializableType() {
|
||||
exists(JacksonDeserializableField f | usesType(f.getType(), this))
|
||||
}
|
||||
}
|
||||
@@ -135,6 +155,21 @@ class JacksonDeserializableField extends DeserializableField {
|
||||
}
|
||||
}
|
||||
|
||||
/** A call to a field that may be deserialized using the Jackson JSON framework. */
|
||||
private class JacksonDeserializableFieldAccess extends FieldAccess {
|
||||
JacksonDeserializableFieldAccess() { getField() instanceof JacksonDeserializableField }
|
||||
}
|
||||
|
||||
/**
|
||||
* When an object is deserialized by the Jackson JSON framework using a tainted input source,
|
||||
* the fields that the framework deserialized are themselves tainted input data.
|
||||
*/
|
||||
private class JacksonDeserializedTaintStep extends AdditionalTaintStep {
|
||||
override predicate step(DataFlow::Node node1, DataFlow::Node node2) {
|
||||
DataFlow::getFieldQualifier(node2.asExpr().(JacksonDeserializableFieldAccess)) = node1
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A call to the `addMixInAnnotations` or `addMixIn` Jackson method.
|
||||
*
|
||||
@@ -239,3 +274,14 @@ class JacksonMixedInCallable extends Callable {
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private class JacksonModel extends SummaryModelCsv {
|
||||
override predicate row(string row) {
|
||||
row =
|
||||
[
|
||||
"com.fasterxml.jackson.databind;ObjectMapper;true;valueToTree;;;Argument[0];ReturnValue;taint",
|
||||
"com.fasterxml.jackson.databind;ObjectMapper;true;valueToTree;;;MapValue of Argument[0];ReturnValue;taint",
|
||||
"com.fasterxml.jackson.databind;ObjectMapper;true;convertValue;;;Argument[0];ReturnValue;taint"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
@@ -44,14 +44,8 @@ class PlayAddCsrfTokenAnnotation extends Annotation {
|
||||
/**
|
||||
* The type `play.libs.F.Promise<Result>`.
|
||||
*/
|
||||
class PlayAsyncResultPromise extends Member {
|
||||
PlayAsyncResultPromise() {
|
||||
exists(Class c |
|
||||
c.hasQualifiedName("play.libs", "F") and
|
||||
this = c.getAMember() and
|
||||
this.getQualifiedName() = "F.Promise<Result>"
|
||||
)
|
||||
}
|
||||
class PlayAsyncResultPromise extends MemberType {
|
||||
PlayAsyncResultPromise() { hasQualifiedName("play.libs", "F$Promise<Result>") }
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -69,11 +69,19 @@ class SpringProperty extends SpringXMLElement {
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a setter method declared on this property's enclosing bean that sets this property.
|
||||
*/
|
||||
Method getSetterMethod() {
|
||||
this.getEnclosingBean().getClass().hasMethod(result, _) and
|
||||
result.getName().toLowerCase() = "set" + this.getPropertyName().toLowerCase()
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a setter method declared on bean `context` that sets this property.
|
||||
*
|
||||
* This property must be declared on a bean that is an ancestor of `context`.
|
||||
*/
|
||||
Method getSetterMethod(SpringBean context) {
|
||||
this.getEnclosingBean() = context.getBeanParent*() and
|
||||
context.getClass().hasMethod(result, _) and
|
||||
|
||||
@@ -8,7 +8,8 @@ import semmle.code.java.Expr
|
||||
import semmle.code.java.security.Validation
|
||||
|
||||
/**
|
||||
* Holds if `method` is a `toString()` method on a boxed type. These never return special characters.
|
||||
* Holds if `method` is a `toString()` method on a boxed type, with or without parameters.
|
||||
* These never return special characters.
|
||||
*/
|
||||
private predicate boxedToString(Method method) {
|
||||
method.getDeclaringType() instanceof BoxedType and
|
||||
@@ -44,11 +45,9 @@ private predicate controlledStringProp(Expr src, Expr dest) {
|
||||
exists(AddExpr concatOp | concatOp = dest | src = concatOp.getAnOperand())
|
||||
or
|
||||
// `toString()` on a safe string is safe.
|
||||
exists(MethodAccess toStringCall, Method toString |
|
||||
exists(MethodAccess toStringCall |
|
||||
src = toStringCall.getQualifier() and
|
||||
toString = toStringCall.getMethod() and
|
||||
toString.hasName("toString") and
|
||||
toString.getNumberOfParameters() = 0 and
|
||||
toStringCall.getMethod() instanceof ToStringMethod and
|
||||
dest = toStringCall
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1,7 +1,20 @@
|
||||
/* Definitions related to external processes. */
|
||||
import semmle.code.java.Member
|
||||
import semmle.code.java.JDK
|
||||
import semmle.code.java.frameworks.apache.Exec
|
||||
|
||||
private module Instances {
|
||||
private import semmle.code.java.JDK
|
||||
private import semmle.code.java.frameworks.apache.Exec
|
||||
}
|
||||
|
||||
/**
|
||||
* A callable that executes a command.
|
||||
*/
|
||||
abstract class ExecCallable extends Callable {
|
||||
/**
|
||||
* Gets the index of an argument that will be part of the command that is executed.
|
||||
*/
|
||||
abstract int getAnExecutedArgument();
|
||||
}
|
||||
|
||||
/**
|
||||
* An expression used as an argument to a call that executes an external command. For calls to
|
||||
@@ -10,21 +23,10 @@ import semmle.code.java.frameworks.apache.Exec
|
||||
*/
|
||||
class ArgumentToExec extends Expr {
|
||||
ArgumentToExec() {
|
||||
exists(MethodAccess execCall, Method method |
|
||||
execCall.getArgument(0) = this and
|
||||
method = execCall.getMethod() and
|
||||
(
|
||||
method instanceof MethodRuntimeExec or
|
||||
method instanceof MethodProcessBuilderCommand or
|
||||
method instanceof MethodCommandLineParse or
|
||||
method instanceof MethodCommandLineAddArguments
|
||||
)
|
||||
)
|
||||
or
|
||||
exists(ConstructorCall expr, Constructor cons |
|
||||
expr.getConstructor() = cons and
|
||||
cons.getDeclaringType().hasQualifiedName("java.lang", "ProcessBuilder") and
|
||||
expr.getArgument(0) = this
|
||||
exists(Call execCall, ExecCallable execCallable, int i |
|
||||
execCall.getArgument(pragma[only_bind_into](i)) = this and
|
||||
execCallable = execCall.getCallee() and
|
||||
i = execCallable.getAnExecutedArgument()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
27
java/ql/src/semmle/code/java/security/InformationLeak.qll
Normal file
27
java/ql/src/semmle/code/java/security/InformationLeak.qll
Normal file
@@ -0,0 +1,27 @@
|
||||
/** Provides classes to reason about System Information Leak vulnerabilities. */
|
||||
|
||||
import java
|
||||
import semmle.code.java.dataflow.DataFlow
|
||||
import semmle.code.java.dataflow.ExternalFlow
|
||||
import semmle.code.java.security.XSS
|
||||
|
||||
/** CSV sink models representing methods not susceptible to XSS but outputing to an HTTP response body. */
|
||||
private class DefaultInformationLeakSinkModel extends SinkModelCsv {
|
||||
override predicate row(string row) {
|
||||
row =
|
||||
[
|
||||
"javax.servlet.http;HttpServletResponse;false;sendError;(int,String);;Argument[1];information-leak"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
/** A sink that represent a method that outputs data to an HTTP response. */
|
||||
abstract class InformationLeakSink extends DataFlow::Node { }
|
||||
|
||||
/** A default sink representing methods outputing data to an HTTP response. */
|
||||
private class DefaultInformationLeakSink extends InformationLeakSink {
|
||||
DefaultInformationLeakSink() {
|
||||
sinkNode(this, "information-leak") or
|
||||
this instanceof XssSink
|
||||
}
|
||||
}
|
||||
247
java/ql/src/semmle/code/java/security/JexlInjection.qll
Normal file
247
java/ql/src/semmle/code/java/security/JexlInjection.qll
Normal file
@@ -0,0 +1,247 @@
|
||||
/** Provides classes to reason about Expression Langauge (JEXL) injection vulnerabilities. */
|
||||
|
||||
import java
|
||||
import semmle.code.java.dataflow.TaintTracking
|
||||
private import semmle.code.java.dataflow.ExternalFlow
|
||||
|
||||
/**
|
||||
* A sink for Expresssion Language injection vulnerabilities via Jexl,
|
||||
* that is, method calls that run evaluation of a JEXL expression.
|
||||
*/
|
||||
abstract class JexlEvaluationSink extends DataFlow::ExprNode { }
|
||||
|
||||
/** Default sink for JXEL injection vulnerabilities. */
|
||||
private class DefaultJexlEvaluationSink extends JexlEvaluationSink {
|
||||
DefaultJexlEvaluationSink() { sinkNode(this, "jexl") }
|
||||
}
|
||||
|
||||
private class DefaultJexlInjectionSinkModel extends SinkModelCsv {
|
||||
override predicate row(string row) {
|
||||
row =
|
||||
[
|
||||
// JEXL2
|
||||
"org.apache.commons.jexl2;JexlEngine;false;getProperty;(JexlContext,Object,String);;Argument[2];jexl",
|
||||
"org.apache.commons.jexl2;JexlEngine;false;getProperty;(Object,String);;Argument[1];jexl",
|
||||
"org.apache.commons.jexl2;JexlEngine;false;setProperty;(JexlContext,Object,String,Object);;Argument[2];jexl",
|
||||
"org.apache.commons.jexl2;JexlEngine;false;setProperty;(Object,String,Object);;Argument[1];jexl",
|
||||
"org.apache.commons.jexl2;Expression;false;evaluate;;;Argument[-1];jexl",
|
||||
"org.apache.commons.jexl2;Expression;false;callable;;;Argument[-1];jexl",
|
||||
"org.apache.commons.jexl2;JexlExpression;false;evaluate;;;Argument[-1];jexl",
|
||||
"org.apache.commons.jexl2;JexlExpression;false;callable;;;Argument[-1];jexl",
|
||||
"org.apache.commons.jexl2;Script;false;execute;;;Argument[-1];jexl",
|
||||
"org.apache.commons.jexl2;Script;false;callable;;;Argument[-1];jexl",
|
||||
"org.apache.commons.jexl2;JexlScript;false;execute;;;Argument[-1];jexl",
|
||||
"org.apache.commons.jexl2;JexlScript;false;callable;;;Argument[-1];jexl",
|
||||
"org.apache.commons.jexl2;UnifiedJEXL$Expression;false;evaluate;;;Argument[-1];jexl",
|
||||
"org.apache.commons.jexl2;UnifiedJEXL$Expression;false;prepare;;;Argument[-1];jexl",
|
||||
"org.apache.commons.jexl2;UnifiedJEXL$Template;false;evaluate;;;Argument[-1];jexl",
|
||||
// JEXL3
|
||||
"org.apache.commons.jexl3;JexlEngine;false;getProperty;(JexlContext,Object,String);;Argument[2];jexl",
|
||||
"org.apache.commons.jexl3;JexlEngine;false;getProperty;(Object,String);;Argument[1];jexl",
|
||||
"org.apache.commons.jexl3;JexlEngine;false;setProperty;(JexlContext,Object,String);;Argument[2];jexl",
|
||||
"org.apache.commons.jexl3;JexlEngine;false;setProperty;(Object,String,Object);;Argument[1];jexl",
|
||||
"org.apache.commons.jexl3;Expression;false;evaluate;;;Argument[-1];jexl",
|
||||
"org.apache.commons.jexl3;Expression;false;callable;;;Argument[-1];jexl",
|
||||
"org.apache.commons.jexl3;JexlExpression;false;evaluate;;;Argument[-1];jexl",
|
||||
"org.apache.commons.jexl3;JexlExpression;false;callable;;;Argument[-1];jexl",
|
||||
"org.apache.commons.jexl3;Script;false;execute;;;Argument[-1];jexl",
|
||||
"org.apache.commons.jexl3;Script;false;callable;;;Argument[-1];jexl",
|
||||
"org.apache.commons.jexl3;JexlScript;false;execute;;;Argument[-1];jexl",
|
||||
"org.apache.commons.jexl3;JexlScript;false;callable;;;Argument[-1];jexl",
|
||||
"org.apache.commons.jexl3;JxltEngine$Expression;false;evaluate;;;Argument[-1];jexl",
|
||||
"org.apache.commons.jexl3;JxltEngine$Expression;false;prepare;;;Argument[-1];jexl",
|
||||
"org.apache.commons.jexl3;JxltEngine$Template;false;evaluate;;;Argument[-1];jexl"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A unit class for adding additional taint steps.
|
||||
*
|
||||
* Extend this class to add additional taint steps that should apply to the `JexlInjectionFlowConfig`.
|
||||
*/
|
||||
class JexlInjectionAdditionalTaintStep extends Unit {
|
||||
/**
|
||||
* Holds if the step from `node1` to `node2` should be considered a taint
|
||||
* step for the `JexlInjectionConfig` configuration.
|
||||
*/
|
||||
abstract predicate step(DataFlow::Node node1, DataFlow::Node node2);
|
||||
}
|
||||
|
||||
/** A set of additional taint steps to consider when taint tracking JXEL related data flows. */
|
||||
private class DefaultJexlInjectionAdditionalTaintStep extends JexlInjectionAdditionalTaintStep {
|
||||
override predicate step(DataFlow::Node node1, DataFlow::Node node2) {
|
||||
createJexlScriptStep(node1, node2) or
|
||||
createJexlExpressionStep(node1, node2) or
|
||||
createJexlTemplateStep(node1, node2)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `n1` to `n2` is a dataflow step that creates a JEXL script using an unsafe engine
|
||||
* by calling `tainted.createScript(jexlExpr)`.
|
||||
*/
|
||||
private predicate createJexlScriptStep(DataFlow::Node n1, DataFlow::Node n2) {
|
||||
exists(MethodAccess ma, Method m | m = ma.getMethod() and n2.asExpr() = ma |
|
||||
not isSafeEngine(ma.getQualifier()) and
|
||||
m instanceof CreateJexlScriptMethod and
|
||||
n1.asExpr() = ma.getArgument(0) and
|
||||
n1.asExpr().getType() instanceof TypeString
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `n1` to `n2` is a dataflow step that creates a JEXL expression using an unsafe engine
|
||||
* by calling `tainted.createExpression(jexlExpr)`.
|
||||
*/
|
||||
private predicate createJexlExpressionStep(DataFlow::Node n1, DataFlow::Node n2) {
|
||||
exists(MethodAccess ma, Method m | m = ma.getMethod() and n2.asExpr() = ma |
|
||||
not isSafeEngine(ma.getQualifier()) and
|
||||
m instanceof CreateJexlExpressionMethod and
|
||||
n1.asExpr() = ma.getAnArgument() and
|
||||
n1.asExpr().getType() instanceof TypeString
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `n1` to `n2` is a dataflow step that creates a JEXL template using an unsafe engine
|
||||
* by calling `tainted.createTemplate(jexlExpr)`.
|
||||
*/
|
||||
private predicate createJexlTemplateStep(DataFlow::Node n1, DataFlow::Node n2) {
|
||||
exists(MethodAccess ma, Method m, RefType taintType |
|
||||
m = ma.getMethod() and n2.asExpr() = ma and taintType = n1.asExpr().getType()
|
||||
|
|
||||
not isSafeEngine(ma.getQualifier()) and
|
||||
m instanceof CreateJexlTemplateMethod and
|
||||
n1.asExpr() = ma.getArgument([0, 1]) and
|
||||
(taintType instanceof TypeString or taintType instanceof Reader)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `expr` is a JEXL engine that is configured with a sandbox.
|
||||
*/
|
||||
private predicate isSafeEngine(Expr expr) {
|
||||
exists(SandboxedJexlFlowConfig config | config.hasFlowTo(DataFlow::exprNode(expr)))
|
||||
}
|
||||
|
||||
/**
|
||||
* A configuration for tracking sandboxed JEXL engines.
|
||||
*/
|
||||
private class SandboxedJexlFlowConfig extends DataFlow2::Configuration {
|
||||
SandboxedJexlFlowConfig() { this = "JexlInjection::SandboxedJexlFlowConfig" }
|
||||
|
||||
override predicate isSource(DataFlow::Node node) { node instanceof SandboxedJexlSource }
|
||||
|
||||
override predicate isSink(DataFlow::Node node) {
|
||||
exists(MethodAccess ma, Method m |
|
||||
m instanceof CreateJexlScriptMethod or
|
||||
m instanceof CreateJexlExpressionMethod or
|
||||
m instanceof CreateJexlTemplateMethod
|
||||
|
|
||||
ma.getMethod() = m and ma.getQualifier() = node.asExpr()
|
||||
)
|
||||
}
|
||||
|
||||
override predicate isAdditionalFlowStep(DataFlow::Node fromNode, DataFlow::Node toNode) {
|
||||
createJexlEngineStep(fromNode, toNode)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Defines a data flow source for JEXL engines configured with a sandbox.
|
||||
*/
|
||||
private class SandboxedJexlSource extends DataFlow::ExprNode {
|
||||
SandboxedJexlSource() {
|
||||
exists(MethodAccess ma, Method m | m = ma.getMethod() |
|
||||
m.getDeclaringType() instanceof JexlBuilder and
|
||||
m.hasName(["uberspect", "sandbox"]) and
|
||||
m.getReturnType() instanceof JexlBuilder and
|
||||
this.asExpr() = [ma, ma.getQualifier()]
|
||||
)
|
||||
or
|
||||
exists(ConstructorCall cc |
|
||||
cc.getConstructedType() instanceof JexlEngine and
|
||||
cc.getArgument(0).getType() instanceof JexlUberspect and
|
||||
cc = this.asExpr()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `fromNode` to `toNode` is a dataflow step that creates one of the JEXL engines.
|
||||
*/
|
||||
private predicate createJexlEngineStep(DataFlow::Node fromNode, DataFlow::Node toNode) {
|
||||
exists(MethodAccess ma, Method m | m = ma.getMethod() |
|
||||
(m.getDeclaringType() instanceof JexlBuilder or m.getDeclaringType() instanceof JexlEngine) and
|
||||
m.hasName(["create", "createJxltEngine"]) and
|
||||
ma.getQualifier() = fromNode.asExpr() and
|
||||
ma = toNode.asExpr()
|
||||
)
|
||||
or
|
||||
exists(ConstructorCall cc |
|
||||
cc.getConstructedType() instanceof UnifiedJexl and
|
||||
cc.getArgument(0) = fromNode.asExpr() and
|
||||
cc = toNode.asExpr()
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* A method that creates a JEXL script.
|
||||
*/
|
||||
private class CreateJexlScriptMethod extends Method {
|
||||
CreateJexlScriptMethod() { getDeclaringType() instanceof JexlEngine and hasName("createScript") }
|
||||
}
|
||||
|
||||
/**
|
||||
* A method that creates a JEXL template.
|
||||
*/
|
||||
private class CreateJexlTemplateMethod extends Method {
|
||||
CreateJexlTemplateMethod() {
|
||||
(getDeclaringType() instanceof JxltEngine or getDeclaringType() instanceof UnifiedJexl) and
|
||||
hasName("createTemplate")
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A method that creates a JEXL expression.
|
||||
*/
|
||||
private class CreateJexlExpressionMethod extends Method {
|
||||
CreateJexlExpressionMethod() {
|
||||
(getDeclaringType() instanceof JexlEngine or getDeclaringType() instanceof JxltEngine) and
|
||||
hasName("createExpression")
|
||||
or
|
||||
getDeclaringType() instanceof UnifiedJexl and hasName("parse")
|
||||
}
|
||||
}
|
||||
|
||||
private class JexlRefType extends RefType {
|
||||
JexlRefType() { getPackage().hasName(["org.apache.commons.jexl2", "org.apache.commons.jexl3"]) }
|
||||
}
|
||||
|
||||
private class JexlBuilder extends JexlRefType {
|
||||
JexlBuilder() { hasName("JexlBuilder") }
|
||||
}
|
||||
|
||||
private class JexlEngine extends JexlRefType {
|
||||
JexlEngine() { hasName("JexlEngine") }
|
||||
}
|
||||
|
||||
private class JxltEngine extends JexlRefType {
|
||||
JxltEngine() { hasName("JxltEngine") }
|
||||
}
|
||||
|
||||
private class UnifiedJexl extends JexlRefType {
|
||||
UnifiedJexl() { hasName("UnifiedJEXL") }
|
||||
}
|
||||
|
||||
private class JexlUberspect extends Interface {
|
||||
JexlUberspect() {
|
||||
hasQualifiedName("org.apache.commons.jexl2.introspection", "Uberspect") or
|
||||
hasQualifiedName("org.apache.commons.jexl3.introspection", "JexlUberspect")
|
||||
}
|
||||
}
|
||||
|
||||
private class Reader extends RefType {
|
||||
Reader() { hasQualifiedName("java.io", "Reader") }
|
||||
}
|
||||
@@ -6,6 +6,7 @@ import semmle.code.java.frameworks.Jndi
|
||||
import semmle.code.java.frameworks.UnboundId
|
||||
import semmle.code.java.frameworks.SpringLdap
|
||||
import semmle.code.java.frameworks.ApacheLdap
|
||||
private import semmle.code.java.dataflow.ExternalFlow
|
||||
|
||||
/** A data flow sink for unvalidated user input that is used to construct LDAP queries. */
|
||||
abstract class LdapInjectionSink extends DataFlow::Node { }
|
||||
@@ -28,70 +29,56 @@ class LdapInjectionAdditionalTaintStep extends Unit {
|
||||
|
||||
/** Default sink for LDAP injection vulnerabilities. */
|
||||
private class DefaultLdapInjectionSink extends LdapInjectionSink {
|
||||
DefaultLdapInjectionSink() {
|
||||
exists(MethodAccess ma, Method m, int index |
|
||||
ma.getMethod() = m and
|
||||
ma.getArgument(index) = this.asExpr() and
|
||||
ldapInjectionSinkMethod(m, index)
|
||||
)
|
||||
DefaultLdapInjectionSink() { sinkNode(this, "ldap") }
|
||||
}
|
||||
|
||||
private class DefaultLdapInjectionSinkModel extends SinkModelCsv {
|
||||
override predicate row(string row) {
|
||||
row =
|
||||
[
|
||||
// jndi
|
||||
"javax.naming.directory;DirContext;true;search;;;Argument[0..1];ldap",
|
||||
// apache
|
||||
"org.apache.directory.ldap.client.api;LdapConnection;true;search;;;Argument[0..2];ldap",
|
||||
// UnboundID: search
|
||||
"com.unboundid.ldap.sdk;LDAPConnection;false;search;(ReadOnlySearchRequest);;Argument[0];ldap",
|
||||
"com.unboundid.ldap.sdk;LDAPConnection;false;search;(SearchRequest);;Argument[0];ldap",
|
||||
"com.unboundid.ldap.sdk;LDAPConnection;false;search;(SearchResultListener,String,SearchScope,DereferencePolicy,int,int,boolean,Filter,String[]);;Argument[0..7];ldap",
|
||||
"com.unboundid.ldap.sdk;LDAPConnection;false;search;(SearchResultListener,String,SearchScope,DereferencePolicy,int,int,boolean,String,String[]);;Argument[0..7];ldap",
|
||||
"com.unboundid.ldap.sdk;LDAPConnection;false;search;(SearchResultListener,String,SearchScope,Filter,String[]);;Argument[0..3];ldap",
|
||||
"com.unboundid.ldap.sdk;LDAPConnection;false;search;(SearchResultListener,String,SearchScope,String,String[]);;Argument[0..3];ldap",
|
||||
"com.unboundid.ldap.sdk;LDAPConnection;false;search;(String,SearchScope,DereferencePolicy,int,int,boolean,Filter,String[]);;Argument[0..6];ldap",
|
||||
"com.unboundid.ldap.sdk;LDAPConnection;false;search;(String,SearchScope,DereferencePolicy,int,int,boolean,String,String[]);;Argument[0..6];ldap",
|
||||
"com.unboundid.ldap.sdk;LDAPConnection;false;search;(String,SearchScope,Filter,String[]);;Argument[0..2];ldap",
|
||||
"com.unboundid.ldap.sdk;LDAPConnection;false;search;(String,SearchScope,String,String[]);;Argument[0..2];ldap",
|
||||
// UnboundID: searchForEntry
|
||||
"com.unboundid.ldap.sdk;LDAPConnection;false;searchForEntry;(ReadOnlySearchRequest);;Argument[0];ldap",
|
||||
"com.unboundid.ldap.sdk;LDAPConnection;false;searchForEntry;(SearchRequest);;Argument[0];ldap",
|
||||
"com.unboundid.ldap.sdk;LDAPConnection;false;searchForEntry;(String,SearchScope,DereferencePolicy,int,boolean,Filter,String[]);;Argument[0..5];ldap",
|
||||
"com.unboundid.ldap.sdk;LDAPConnection;false;searchForEntry;(String,SearchScope,DereferencePolicy,int,boolean,String,String[]);;Argument[0..5];ldap",
|
||||
"com.unboundid.ldap.sdk;LDAPConnection;false;searchForEntry;(String,SearchScope,Filter,String[]);;Argument[0..2];ldap",
|
||||
"com.unboundid.ldap.sdk;LDAPConnection;false;searchForEntry;(String,SearchScope,String,String[]);;Argument[0..2];ldap",
|
||||
// UnboundID: asyncSearch
|
||||
"com.unboundid.ldap.sdk;LDAPConnection;false;asyncSearch;;;Argument[0];ldap",
|
||||
// Spring
|
||||
"org.springframework.ldap.core;LdapTemplate;false;find;;;Argument[0..1];ldap",
|
||||
"org.springframework.ldap.core;LdapTemplate;false;findOne;;;Argument[0..1];ldap",
|
||||
"org.springframework.ldap.core;LdapTemplate;false;search;;;Argument[0..1];ldap",
|
||||
"org.springframework.ldap.core;LdapTemplate;false;searchForContext;;;Argument[0..1];ldap",
|
||||
"org.springframework.ldap.core;LdapTemplate;false;searchForObject;;;Argument[0..1];ldap",
|
||||
"org.springframework.ldap.core;LdapTemplate;false;authenticate;(LdapQuery,String);;Argument[0];ldap",
|
||||
"org.springframework.ldap.core;LdapTemplate;false;authenticate;(Name,String,String);;Argument[0..1];ldap",
|
||||
"org.springframework.ldap.core;LdapTemplate;false;authenticate;(Name,String,String,AuthenticatedLdapEntryContextCallback);;Argument[0..1];ldap",
|
||||
"org.springframework.ldap.core;LdapTemplate;false;authenticate;(Name,String,String,AuthenticatedLdapEntryContextCallback,AuthenticationErrorCallback);;Argument[0..1];ldap",
|
||||
"org.springframework.ldap.core;LdapTemplate;false;authenticate;(Name,String,String,AuthenticationErrorCallback);;Argument[0..1];ldap",
|
||||
"org.springframework.ldap.core;LdapTemplate;false;authenticate;(String,String,String);;Argument[0..1];ldap",
|
||||
"org.springframework.ldap.core;LdapTemplate;false;authenticate;(String,String,String,AuthenticatedLdapEntryContextCallback);;Argument[0..1];ldap",
|
||||
"org.springframework.ldap.core;LdapTemplate;false;authenticate;(String,String,String,AuthenticatedLdapEntryContextCallback,AuthenticationErrorCallback);;Argument[0..1];ldap",
|
||||
"org.springframework.ldap.core;LdapTemplate;false;authenticate;(String,String,String,AuthenticationErrorCallback);;Argument[0..1];ldap"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
/** Holds if the method parameter at `index` is susceptible to an LDAP injection attack. */
|
||||
private predicate ldapInjectionSinkMethod(Method m, int index) {
|
||||
jndiLdapInjectionSinkMethod(m, index) or
|
||||
unboundIdLdapInjectionSinkMethod(m, index) or
|
||||
springLdapInjectionSinkMethod(m, index) or
|
||||
apacheLdapInjectionSinkMethod(m, index)
|
||||
}
|
||||
|
||||
/** Holds if the JNDI method parameter at `index` is susceptible to an LDAP injection attack. */
|
||||
private predicate jndiLdapInjectionSinkMethod(Method m, int index) {
|
||||
m.getDeclaringType().getAnAncestor() instanceof TypeDirContext and
|
||||
m.hasName("search") and
|
||||
index in [0 .. 1]
|
||||
}
|
||||
|
||||
/** Holds if the UnboundID method parameter at `index` is susceptible to an LDAP injection attack. */
|
||||
private predicate unboundIdLdapInjectionSinkMethod(Method m, int index) {
|
||||
exists(Parameter param | m.getParameter(index) = param and not param.isVarargs() |
|
||||
m instanceof MethodUnboundIdLDAPConnectionSearch or
|
||||
m instanceof MethodUnboundIdLDAPConnectionAsyncSearch or
|
||||
m instanceof MethodUnboundIdLDAPConnectionSearchForEntry
|
||||
)
|
||||
}
|
||||
|
||||
/** Holds if the Spring method parameter at `index` is susceptible to an LDAP injection attack. */
|
||||
private predicate springLdapInjectionSinkMethod(Method m, int index) {
|
||||
// LdapTemplate.authenticate, LdapTemplate.find* or LdapTemplate.search* method
|
||||
(
|
||||
m instanceof MethodSpringLdapTemplateAuthenticate or
|
||||
m instanceof MethodSpringLdapTemplateFind or
|
||||
m instanceof MethodSpringLdapTemplateFindOne or
|
||||
m instanceof MethodSpringLdapTemplateSearch or
|
||||
m instanceof MethodSpringLdapTemplateSearchForContext or
|
||||
m instanceof MethodSpringLdapTemplateSearchForObject
|
||||
) and
|
||||
(
|
||||
// Parameter index is 1 (DN or query) or 2 (filter) if method is not authenticate
|
||||
index in [0 .. 1] and
|
||||
not m instanceof MethodSpringLdapTemplateAuthenticate
|
||||
or
|
||||
// But it's not the last parameter in case of authenticate method (last param is password)
|
||||
index in [0 .. 1] and
|
||||
index < m.getNumberOfParameters() - 1 and
|
||||
m instanceof MethodSpringLdapTemplateAuthenticate
|
||||
)
|
||||
}
|
||||
|
||||
/** Holds if the Apache LDAP API method parameter at `index` is susceptible to an LDAP injection attack. */
|
||||
private predicate apacheLdapInjectionSinkMethod(Method m, int index) {
|
||||
exists(Parameter param | m.getParameter(index) = param and not param.isVarargs() |
|
||||
m.getDeclaringType().getAnAncestor() instanceof TypeApacheLdapConnection and
|
||||
m.hasName("search")
|
||||
)
|
||||
}
|
||||
|
||||
/** A sanitizer that clears the taint on (boxed) primitive types. */
|
||||
private class DefaultLdapSanitizer extends LdapInjectionSanitizer {
|
||||
DefaultLdapSanitizer() {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import java
|
||||
import semmle.code.java.dataflow.DefUse
|
||||
import semmle.code.java.dataflow.DataFlow
|
||||
import semmle.code.java.dataflow.DataFlow6
|
||||
|
||||
/**
|
||||
* The `java.security.SecureRandom` class.
|
||||
@@ -166,16 +166,16 @@ private predicate isSeeded(RValue use) {
|
||||
)
|
||||
}
|
||||
|
||||
private class PredictableSeedFlowConfiguration extends DataFlow::Configuration {
|
||||
private class PredictableSeedFlowConfiguration extends DataFlow6::Configuration {
|
||||
PredictableSeedFlowConfiguration() { this = "Random::PredictableSeedFlowConfiguration" }
|
||||
|
||||
override predicate isSource(DataFlow::Node source) {
|
||||
override predicate isSource(DataFlow6::Node source) {
|
||||
source.asExpr() instanceof PredictableSeedExpr
|
||||
}
|
||||
|
||||
override predicate isSink(DataFlow::Node sink) { isSeeding(sink.asExpr(), _) }
|
||||
override predicate isSink(DataFlow6::Node sink) { isSeeding(sink.asExpr(), _) }
|
||||
|
||||
override predicate isAdditionalFlowStep(DataFlow::Node node1, DataFlow::Node node2) {
|
||||
override predicate isAdditionalFlowStep(DataFlow6::Node node1, DataFlow6::Node node2) {
|
||||
predictableCalcStep(node1.asExpr(), node2.asExpr())
|
||||
}
|
||||
}
|
||||
@@ -252,7 +252,7 @@ private predicate isSeeding(Expr arg, RValue use) {
|
||||
private predicate isSeedingSource(Expr arg, RValue use, Expr source) {
|
||||
isSeeding(arg, use) and
|
||||
exists(PredictableSeedFlowConfiguration conf |
|
||||
conf.hasFlow(DataFlow::exprNode(source), DataFlow::exprNode(arg))
|
||||
conf.hasFlow(DataFlow6::exprNode(source), DataFlow6::exprNode(arg))
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -5,41 +5,32 @@ import semmle.code.java.dataflow.DataFlow
|
||||
import semmle.code.java.dataflow.FlowSources
|
||||
import semmle.code.java.frameworks.Servlets
|
||||
import semmle.code.java.frameworks.JaxWS
|
||||
private import semmle.code.java.dataflow.ExternalFlow
|
||||
|
||||
/** A sink that is vulnerable to an HTTP header splitting attack. */
|
||||
abstract class HeaderSplittingSink extends DataFlow::Node { }
|
||||
|
||||
private class DefaultHeaderSplittingSink extends HeaderSplittingSink {
|
||||
DefaultHeaderSplittingSink() { sinkNode(this, "header-splitting") }
|
||||
}
|
||||
|
||||
private class HeaderSplittingSinkModel extends SinkModelCsv {
|
||||
override predicate row(string row) {
|
||||
row =
|
||||
[
|
||||
"javax.servlet.http;HttpServletResponse;false;addCookie;;;Argument[0];header-splitting",
|
||||
"javax.servlet.http;HttpServletResponse;false;addHeader;;;Argument[0..1];header-splitting",
|
||||
"javax.servlet.http;HttpServletResponse;false;setHeader;;;Argument[0..1];header-splitting",
|
||||
"javax.ws.rs.core;ResponseBuilder;false;header;;;Argument[1];header-splitting"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
/** A source that introduces data considered safe to use by a header splitting source. */
|
||||
abstract class SafeHeaderSplittingSource extends DataFlow::Node {
|
||||
SafeHeaderSplittingSource() { this instanceof RemoteFlowSource }
|
||||
}
|
||||
|
||||
/** A sink that identifies a Java Servlet or JaxWs method that is vulnerable to an HTTP header splitting attack. */
|
||||
private class ServletHeaderSplittingSink extends HeaderSplittingSink {
|
||||
ServletHeaderSplittingSink() {
|
||||
exists(ResponseAddCookieMethod m, MethodAccess ma |
|
||||
ma.getMethod() = m and
|
||||
this.asExpr() = ma.getArgument(0)
|
||||
)
|
||||
or
|
||||
exists(ResponseAddHeaderMethod m, MethodAccess ma |
|
||||
ma.getMethod() = m and
|
||||
this.asExpr() = ma.getAnArgument()
|
||||
)
|
||||
or
|
||||
exists(ResponseSetHeaderMethod m, MethodAccess ma |
|
||||
ma.getMethod() = m and
|
||||
this.asExpr() = ma.getAnArgument()
|
||||
)
|
||||
or
|
||||
exists(JaxRsResponseBuilder builder, Method m |
|
||||
m = builder.getAMethod() and m.getName() = "header"
|
||||
|
|
||||
this.asExpr() = m.getAReference().getArgument(1)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/** A default source that introduces data considered safe to use by a header splitting source. */
|
||||
private class DefaultSafeHeaderSplittingSource extends SafeHeaderSplittingSource {
|
||||
DefaultSafeHeaderSplittingSource() {
|
||||
|
||||
@@ -53,6 +53,65 @@ class SafeKryo extends DataFlow2::Configuration {
|
||||
ma.getMethod() instanceof KryoReadObjectMethod
|
||||
)
|
||||
}
|
||||
|
||||
override predicate isAdditionalFlowStep(DataFlow::Node node1, DataFlow::Node node2) {
|
||||
stepKryoPoolBuilderFactoryArgToConstructor(node1, node2) or
|
||||
stepKryoPoolRunMethodAccessQualifierToFunctionalArgument(node1, node2) or
|
||||
stepKryoPoolBuilderChainMethod(node1, node2) or
|
||||
stepKryoPoolBorrowMethod(node1, node2)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds when a functional expression is used to create a `KryoPool.Builder`.
|
||||
* Eg. `new KryoPool.Builder(() -> new Kryo())`
|
||||
*/
|
||||
private predicate stepKryoPoolBuilderFactoryArgToConstructor(
|
||||
DataFlow::Node node1, DataFlow::Node node2
|
||||
) {
|
||||
exists(ConstructorCall cc, FunctionalExpr fe |
|
||||
cc.getConstructedType() instanceof KryoPoolBuilder and
|
||||
fe.asMethod().getBody().getAStmt().(ReturnStmt).getResult() = node1.asExpr() and
|
||||
node2.asExpr() = cc and
|
||||
cc.getArgument(0) = fe
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds when a `KryoPool.run` is called to use a `Kryo` instance.
|
||||
* Eg. `pool.run(kryo -> ...)`
|
||||
*/
|
||||
private predicate stepKryoPoolRunMethodAccessQualifierToFunctionalArgument(
|
||||
DataFlow::Node node1, DataFlow::Node node2
|
||||
) {
|
||||
exists(MethodAccess ma |
|
||||
ma.getMethod() instanceof KryoPoolRunMethod and
|
||||
node1.asExpr() = ma.getQualifier() and
|
||||
ma.getArgument(0).(FunctionalExpr).asMethod().getParameter(0) = node2.asParameter()
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds when a `KryoPool.Builder` method is called fluently.
|
||||
*/
|
||||
private predicate stepKryoPoolBuilderChainMethod(DataFlow::Node node1, DataFlow::Node node2) {
|
||||
exists(MethodAccess ma |
|
||||
ma.getMethod() instanceof KryoPoolBuilderMethod and
|
||||
ma = node2.asExpr() and
|
||||
ma.getQualifier() = node1.asExpr()
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds when a `KryoPool.borrow` method is called.
|
||||
*/
|
||||
private predicate stepKryoPoolBorrowMethod(DataFlow::Node node1, DataFlow::Node node2) {
|
||||
exists(MethodAccess ma |
|
||||
ma.getMethod() =
|
||||
any(Method m | m.getDeclaringType() instanceof KryoPool and m.hasName("borrow")) and
|
||||
node1.asExpr() = ma.getQualifier() and
|
||||
node2.asExpr() = ma
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
predicate unsafeDeserialization(MethodAccess ma, Expr sink) {
|
||||
|
||||
58
java/ql/src/semmle/code/java/security/XPath.qll
Normal file
58
java/ql/src/semmle/code/java/security/XPath.qll
Normal file
@@ -0,0 +1,58 @@
|
||||
/** Provides classes to reason about XPath vulnerabilities. */
|
||||
|
||||
import java
|
||||
import semmle.code.java.dataflow.DataFlow
|
||||
import semmle.code.java.dataflow.ExternalFlow
|
||||
|
||||
/**
|
||||
* A sink that represents a method that interprets XPath expressions.
|
||||
* Extend this class to add your own XPath Injection sinks.
|
||||
*/
|
||||
abstract class XPathInjectionSink extends DataFlow::Node { }
|
||||
|
||||
/** CSV sink models representing methods susceptible to XPath Injection attacks. */
|
||||
private class DefaultXPathInjectionSinkModel extends SinkModelCsv {
|
||||
override predicate row(string row) {
|
||||
row =
|
||||
[
|
||||
"javax.xml.xpath;XPath;true;evaluate;;;Argument[0];xpath",
|
||||
"javax.xml.xpath;XPath;true;evaluateExpression;;;Argument[0];xpath",
|
||||
"javax.xml.xpath;XPath;true;compile;;;Argument[0];xpath",
|
||||
"org.dom4j;Node;true;selectObject;;;Argument[0];xpath",
|
||||
"org.dom4j;Node;true;selectNodes;;;Argument[0..1];xpath",
|
||||
"org.dom4j;Node;true;selectSingleNode;;;Argument[0];xpath",
|
||||
"org.dom4j;Node;true;numberValueOf;;;Argument[0];xpath",
|
||||
"org.dom4j;Node;true;valueOf;;;Argument[0];xpath",
|
||||
"org.dom4j;Node;true;matches;;;Argument[0];xpath",
|
||||
"org.dom4j;Node;true;createXPath;;;Argument[0];xpath",
|
||||
"org.dom4j;DocumentFactory;true;createPattern;;;Argument[0];xpath",
|
||||
"org.dom4j;DocumentFactory;true;createXPath;;;Argument[0];xpath",
|
||||
"org.dom4j;DocumentFactory;true;createXPathFilter;;;Argument[0];xpath",
|
||||
"org.dom4j;DocumentHelper;false;createPattern;;;Argument[0];xpath",
|
||||
"org.dom4j;DocumentHelper;false;createXPath;;;Argument[0];xpath",
|
||||
"org.dom4j;DocumentHelper;false;createXPathFilter;;;Argument[0];xpath",
|
||||
"org.dom4j;DocumentHelper;false;selectNodes;;;Argument[0];xpath",
|
||||
"org.dom4j;DocumentHelper;false;sort;;;Argument[1];xpath",
|
||||
"org.dom4j.tree;AbstractNode;true;createXPathFilter;;;Argument[0];xpath",
|
||||
"org.dom4j.tree;AbstractNode;true;createPattern;;;Argument[0];xpath",
|
||||
"org.dom4j.util;ProxyDocumentFactory;true;createPattern;;;Argument[0];xpath",
|
||||
"org.dom4j.util;ProxyDocumentFactory;true;createXPath;;;Argument[0];xpath",
|
||||
"org.dom4j.util;ProxyDocumentFactory;true;createXPathFilter;;;Argument[0];xpath"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
/** A default sink representing methods susceptible to XPath Injection attacks. */
|
||||
private class DefaultXPathInjectionSink extends XPathInjectionSink {
|
||||
DefaultXPathInjectionSink() {
|
||||
sinkNode(this, "xpath")
|
||||
or
|
||||
exists(ClassInstanceExpr constructor |
|
||||
constructor.getConstructedType().getASourceSupertype*().hasQualifiedName("org.dom4j", "XPath")
|
||||
or
|
||||
constructor.getConstructedType().hasQualifiedName("org.dom4j.xpath", "XPathPattern")
|
||||
|
|
||||
this.asExpr() = constructor.getArgument(0)
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -29,33 +29,29 @@ class XssAdditionalTaintStep extends Unit {
|
||||
abstract predicate step(DataFlow::Node node1, DataFlow::Node node2);
|
||||
}
|
||||
|
||||
/** CSV sink models representing methods susceptible to XSS attacks. */
|
||||
private class DefaultXssSinkModel extends SinkModelCsv {
|
||||
override predicate row(string row) {
|
||||
row =
|
||||
[
|
||||
"android.webkit;WebView;false;loadData;;;Argument[0];xss",
|
||||
"android.webkit;WebView;false;loadUrl;;;Argument[0];xss",
|
||||
"android.webkit;WebView;false;loadDataWithBaseURL;;;Argument[1];xss"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
/** A default sink representing methods susceptible to XSS attacks. */
|
||||
private class DefaultXssSink extends XssSink {
|
||||
DefaultXssSink() {
|
||||
sinkNode(this, "xss")
|
||||
or
|
||||
exists(HttpServletResponseSendErrorMethod m, MethodAccess ma |
|
||||
ma.getMethod() = m and
|
||||
this.asExpr() = ma.getArgument(1)
|
||||
)
|
||||
or
|
||||
exists(ServletWriterSourceToWritingMethodFlowConfig writer, MethodAccess ma |
|
||||
ma.getMethod() instanceof WritingMethod and
|
||||
writer.hasFlowToExpr(ma.getQualifier()) and
|
||||
this.asExpr() = ma.getArgument(_)
|
||||
)
|
||||
or
|
||||
exists(Method m |
|
||||
m.getDeclaringType() instanceof TypeWebView and
|
||||
(
|
||||
m.getAReference().getArgument(0) = this.asExpr() and m.getName() = "loadData"
|
||||
or
|
||||
m.getAReference().getArgument(0) = this.asExpr() and m.getName() = "loadUrl"
|
||||
or
|
||||
m.getAReference().getArgument(1) = this.asExpr() and m.getName() = "loadDataWithBaseURL"
|
||||
)
|
||||
)
|
||||
or
|
||||
exists(SpringRequestMappingMethod requestMappingMethod, ReturnStmt rs |
|
||||
requestMappingMethod = rs.getEnclosingCallable() and
|
||||
this.asExpr() = rs.getResult() and
|
||||
|
||||
@@ -36,7 +36,10 @@ abstract class ParserConfig extends MethodAccess {
|
||||
*/
|
||||
predicate disables(Expr e) {
|
||||
this.getArgument(0) = e and
|
||||
this.getArgument(1).(BooleanLiteral).getBooleanValue() = false
|
||||
(
|
||||
this.getArgument(1).(BooleanLiteral).getBooleanValue() = false or
|
||||
this.getArgument(1).(FieldAccess).getField().hasQualifiedName("java.lang", "Boolean", "FALSE")
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -44,7 +47,10 @@ abstract class ParserConfig extends MethodAccess {
|
||||
*/
|
||||
predicate enables(Expr e) {
|
||||
this.getArgument(0) = e and
|
||||
this.getArgument(1).(BooleanLiteral).getBooleanValue() = true
|
||||
(
|
||||
this.getArgument(1).(BooleanLiteral).getBooleanValue() = true or
|
||||
this.getArgument(1).(FieldAccess).getField().hasQualifiedName("java.lang", "Boolean", "TRUE")
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user