mirror of
https://github.com/github/codeql.git
synced 2026-05-05 13:45:19 +02:00
Merge branch 'main' into xslt-injection
This commit is contained in:
@@ -151,33 +151,6 @@ class Container extends @container, Top {
|
||||
* This is the absolute path of the container.
|
||||
*/
|
||||
override string toString() { result = getAbsolutePath() }
|
||||
|
||||
/**
|
||||
* DEPRECATED: use `getAbsolutePath()`, `getBaseName()` or `getStem()` instead.
|
||||
*
|
||||
* Gets the name of this container.
|
||||
*/
|
||||
deprecated string getName() { result = getAbsolutePath() }
|
||||
|
||||
/**
|
||||
* DEPRECATED: use `getBaseName()` or `getStem()` instead.
|
||||
*
|
||||
* The short name of this container, excluding its path and (for files) extension.
|
||||
*
|
||||
* For folders, the short name includes the extension (if any), so the short name
|
||||
* of the folder with absolute path `/home/user/.m2` is `.m2`.
|
||||
*/
|
||||
deprecated string getShortName() {
|
||||
folders(this, _, result) or
|
||||
files(this, _, result, _, _)
|
||||
}
|
||||
|
||||
/**
|
||||
* DEPRECATED: use `getAbsolutePath()` instead.
|
||||
*
|
||||
* Gets the full name of this container, including its path and extension (if any).
|
||||
*/
|
||||
deprecated string getFullName() { result = getAbsolutePath() }
|
||||
}
|
||||
|
||||
/** A folder. */
|
||||
@@ -198,13 +171,6 @@ class File extends Container, @file {
|
||||
|
||||
/** Gets the URL of this file. */
|
||||
override string getURL() { result = "file://" + this.getAbsolutePath() + ":0:0:0:0" }
|
||||
|
||||
/**
|
||||
* DEPRECATED: use `getAbsolutePath()`, `getBaseName()` or `getStem()` instead.
|
||||
*
|
||||
* Holds if this file has the specified `name`.
|
||||
*/
|
||||
deprecated predicate hasName(string name) { name = this.getAbsolutePath() }
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -90,16 +90,16 @@ class Top extends @top {
|
||||
|
||||
/** A location maps language elements to positions in source files. */
|
||||
class Location extends @location {
|
||||
/** Gets the line number where this location starts. */
|
||||
/** Gets the 1-based line number (inclusive) where this location starts. */
|
||||
int getStartLine() { locations_default(this, _, result, _, _, _) }
|
||||
|
||||
/** Gets the column number where this location starts. */
|
||||
/** Gets the 1-based column number (inclusive) where this location starts. */
|
||||
int getStartColumn() { locations_default(this, _, _, result, _, _) }
|
||||
|
||||
/** Gets the line number where this location ends. */
|
||||
/** Gets the 1-based line number (inclusive) where this location ends. */
|
||||
int getEndLine() { locations_default(this, _, _, _, result, _) }
|
||||
|
||||
/** Gets the column number where this location ends. */
|
||||
/** Gets the 1-based column number (inclusive) where this location ends. */
|
||||
int getEndColumn() { locations_default(this, _, _, _, _, result) }
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,3 +1,8 @@
|
||||
/**
|
||||
* Provides classes and predicates for reasoning about instances of
|
||||
* `java.util.Collection` and their methods.
|
||||
*/
|
||||
|
||||
import java
|
||||
|
||||
/**
|
||||
@@ -77,6 +82,7 @@ class CollectionMutator extends CollectionMethod {
|
||||
class CollectionMutation extends MethodAccess {
|
||||
CollectionMutation() { this.getMethod() instanceof CollectionMutator }
|
||||
|
||||
/** Holds if the result of this call is not immediately discarded. */
|
||||
predicate resultIsChecked() { not this.getParent() instanceof ExprStmt }
|
||||
}
|
||||
|
||||
|
||||
@@ -68,21 +68,27 @@ newtype Completion =
|
||||
*/
|
||||
ThrowCompletion(ThrowableType tt)
|
||||
|
||||
/** A completion that is either a `NormalCompletion` or a `BooleanCompletion`. */
|
||||
class NormalOrBooleanCompletion extends Completion {
|
||||
NormalOrBooleanCompletion() {
|
||||
this instanceof NormalCompletion or this instanceof BooleanCompletion
|
||||
}
|
||||
|
||||
/** Gets a textual representation of this completion. */
|
||||
string toString() { result = "completion" }
|
||||
}
|
||||
|
||||
/** Gets the completion `ContinueCompletion(NoLabel())`. */
|
||||
ContinueCompletion anonymousContinueCompletion() { result = ContinueCompletion(NoLabel()) }
|
||||
|
||||
/** Gets the completion `ContinueCompletion(JustLabel(l))`. */
|
||||
ContinueCompletion labelledContinueCompletion(Label l) { result = ContinueCompletion(JustLabel(l)) }
|
||||
|
||||
/** Gets the completion `BreakCompletion(NoLabel())`. */
|
||||
BreakCompletion anonymousBreakCompletion() { result = BreakCompletion(NoLabel()) }
|
||||
|
||||
/** Gets the completion `BreakCompletion(JustLabel(l))`. */
|
||||
BreakCompletion labelledBreakCompletion(Label l) { result = BreakCompletion(JustLabel(l)) }
|
||||
|
||||
/** Gets the completion `booleanCompletion(value, value)`. */
|
||||
/** Gets the completion `BooleanCompletion(value, value)`. */
|
||||
Completion basicBooleanCompletion(boolean value) { result = BooleanCompletion(value, value) }
|
||||
|
||||
@@ -113,6 +113,7 @@ class ControlFlowNode extends Top, @exprparent {
|
||||
result = succ(this, NormalCompletion())
|
||||
}
|
||||
|
||||
/** Gets the basic block that contains this node. */
|
||||
BasicBlock getBasicBlock() { result.getANode() = this }
|
||||
}
|
||||
|
||||
@@ -404,7 +405,7 @@ private module ControlFlowGraphImpl {
|
||||
* Expressions and statements with CFG edges in post-order AST traversal.
|
||||
*
|
||||
* This includes most expressions, except those that initiate or propagate branching control
|
||||
* flow (`LogicExpr`, `ConditionalExpr`), and parentheses, which aren't in the CFG.
|
||||
* flow (`LogicExpr`, `ConditionalExpr`).
|
||||
* Only a few statements are included; those with specific side-effects
|
||||
* occurring after the evaluation of their children, that is, `Call`, `ReturnStmt`,
|
||||
* and `ThrowStmt`. CFG nodes without child nodes in the CFG that may complete
|
||||
@@ -428,9 +429,10 @@ private module ControlFlowGraphImpl {
|
||||
or
|
||||
this instanceof CastExpr
|
||||
or
|
||||
this instanceof InstanceOfExpr
|
||||
this instanceof InstanceOfExpr and not this.(InstanceOfExpr).isPattern()
|
||||
or
|
||||
this instanceof LocalVariableDeclExpr
|
||||
this instanceof LocalVariableDeclExpr and
|
||||
not this = any(InstanceOfExpr ioe).getLocalVariableDeclExpr()
|
||||
or
|
||||
this instanceof RValue
|
||||
or
|
||||
@@ -572,12 +574,16 @@ private module ControlFlowGraphImpl {
|
||||
or
|
||||
result = first(n.(PostOrderNode).firstChild())
|
||||
or
|
||||
result = first(n.(InstanceOfExpr).getExpr())
|
||||
or
|
||||
result = first(n.(SynchronizedStmt).getExpr())
|
||||
or
|
||||
result = n and
|
||||
n instanceof Stmt and
|
||||
not n instanceof PostOrderNode and
|
||||
not n instanceof SynchronizedStmt
|
||||
or
|
||||
result = n and n instanceof SwitchExpr
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -704,6 +710,12 @@ private module ControlFlowGraphImpl {
|
||||
last(condexpr.getTrueExpr(), last, completion)
|
||||
)
|
||||
or
|
||||
exists(InstanceOfExpr ioe | ioe.isPattern() and ioe = n |
|
||||
last = n and completion = basicBooleanCompletion(false)
|
||||
or
|
||||
last = ioe.getLocalVariableDeclExpr() and completion = basicBooleanCompletion(true)
|
||||
)
|
||||
or
|
||||
// The last node of a node executed in post-order is the node itself.
|
||||
n.(PostOrderNode).mayCompleteNormally() and last = n and completion = NormalCompletion()
|
||||
or
|
||||
@@ -913,6 +925,14 @@ private module ControlFlowGraphImpl {
|
||||
result = first(e.getFalseExpr())
|
||||
)
|
||||
or
|
||||
exists(InstanceOfExpr ioe | ioe.isPattern() |
|
||||
last(ioe.getExpr(), n, completion) and completion = NormalCompletion() and result = ioe
|
||||
or
|
||||
n = ioe and
|
||||
result = ioe.getLocalVariableDeclExpr() and
|
||||
completion = basicBooleanCompletion(true)
|
||||
)
|
||||
or
|
||||
// In other expressions control flows from left to right and ends in the node itself.
|
||||
exists(PostOrderNode p, int i |
|
||||
last(p.getChildNode(i), n, completion) and completion = NormalCompletion()
|
||||
|
||||
@@ -60,6 +60,12 @@ class Expr extends ExprParent, @expr {
|
||||
/** Gets the statement containing this expression, if any. */
|
||||
Stmt getEnclosingStmt() { statementEnclosingExpr(this, result) }
|
||||
|
||||
/**
|
||||
* Gets a statement that directly or transitively contains this expression, if any.
|
||||
* This is equivalent to `this.getEnclosingStmt().getEnclosingStmt*()`.
|
||||
*/
|
||||
Stmt getAnEnclosingStmt() { result = this.getEnclosingStmt().getEnclosingStmt*() }
|
||||
|
||||
/** Gets a child of this expression. */
|
||||
Expr getAChildExpr() { exprs(result, _, _, this, _) }
|
||||
|
||||
@@ -305,10 +311,6 @@ class CompileTimeConstantExpr extends Expr {
|
||||
/**
|
||||
* Gets the integer value of this expression, where possible.
|
||||
*
|
||||
* All computations are performed on QL 32-bit `int`s, so no
|
||||
* truncation is performed in the case of overflow within `byte` or `short`:
|
||||
* `((byte)127)+((byte)1)` evaluates to 128 rather than to -128.
|
||||
*
|
||||
* Note that this does not handle the following cases:
|
||||
*
|
||||
* - values of type `long`,
|
||||
@@ -332,7 +334,10 @@ class CompileTimeConstantExpr extends Expr {
|
||||
else
|
||||
if cast.getType().hasName("short")
|
||||
then result = (val + 32768).bitAnd(65535) - 32768
|
||||
else result = val
|
||||
else
|
||||
if cast.getType().hasName("char")
|
||||
then result = val.bitAnd(65535)
|
||||
else result = val
|
||||
)
|
||||
or
|
||||
result = this.(PlusExpr).getExpr().(CompileTimeConstantExpr).getIntValue()
|
||||
@@ -413,7 +418,7 @@ class ArrayAccess extends Expr, @arrayaccess {
|
||||
/**
|
||||
* An array creation expression.
|
||||
*
|
||||
* For example, an expression such as `new String[3][2]` or
|
||||
* For example, an expression such as `new String[2][3]` or
|
||||
* `new String[][] { { "a", "b", "c" } , { "d", "e", "f" } }`.
|
||||
*
|
||||
* In both examples, `String` is the type name. In the first
|
||||
@@ -844,6 +849,7 @@ class EqualityTest extends BinaryExpr {
|
||||
this instanceof NEExpr
|
||||
}
|
||||
|
||||
/** Gets a boolean indicating whether this is `==` (true) or `!=` (false). */
|
||||
boolean polarity() {
|
||||
result = true and this instanceof EQExpr
|
||||
or
|
||||
@@ -1050,6 +1056,18 @@ class MemberRefExpr extends FunctionalExpr, @memberref {
|
||||
override string toString() { result = "...::..." }
|
||||
}
|
||||
|
||||
/** A conditional expression or a `switch` expression. */
|
||||
class ChooseExpr extends Expr {
|
||||
ChooseExpr() { this instanceof ConditionalExpr or this instanceof SwitchExpr }
|
||||
|
||||
/** Gets a result expression of this `switch` or conditional expression. */
|
||||
Expr getAResultExpr() {
|
||||
result = this.(ConditionalExpr).getTrueExpr() or
|
||||
result = this.(ConditionalExpr).getFalseExpr() or
|
||||
result = this.(SwitchExpr).getAResult()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A conditional expression of the form `a ? b : c`, where `a` is the condition,
|
||||
* `b` is the expression that is evaluated if the condition evaluates to `true`,
|
||||
@@ -1225,7 +1243,7 @@ class VariableAssign extends VariableUpdate {
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the source of this assignment, if any.
|
||||
* Gets the source (right-hand side) of this assignment, if any.
|
||||
*
|
||||
* An initialization in a `CatchClause` or `EnhancedForStmt` is implicit and
|
||||
* does not have a source.
|
||||
@@ -1326,10 +1344,7 @@ class VarAccess extends Expr, @varaccess {
|
||||
*/
|
||||
predicate isLValue() {
|
||||
exists(Assignment a | a.getDest() = this) or
|
||||
exists(PreIncExpr e | e.getExpr() = this) or
|
||||
exists(PreDecExpr e | e.getExpr() = this) or
|
||||
exists(PostIncExpr e | e.getExpr() = this) or
|
||||
exists(PostDecExpr e | e.getExpr() = this)
|
||||
exists(UnaryAssignExpr e | e.getExpr() = this)
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -194,7 +194,16 @@ class TypeVariable extends BoundedType, @typevariable {
|
||||
* and the second wildcard has a lower bound of `Float`.
|
||||
*/
|
||||
class Wildcard extends BoundedType, @wildcard {
|
||||
/** Holds if this wildcard has an upper bound. */
|
||||
/**
|
||||
* Holds if this wildcard is either unconstrained (i.e. `?`) or
|
||||
* has a type bound.
|
||||
*/
|
||||
override predicate hasTypeBound() { BoundedType.super.hasTypeBound() }
|
||||
|
||||
/**
|
||||
* Holds if this wildcard is either unconstrained (i.e. `?`) or
|
||||
* has an upper bound.
|
||||
*/
|
||||
predicate hasUpperBound() { wildcards(this, _, 1) }
|
||||
|
||||
/** Holds if this wildcard has a lower bound. */
|
||||
|
||||
@@ -79,7 +79,7 @@ abstract class JavadocElement extends @javadocElement, Top {
|
||||
abstract string getText();
|
||||
}
|
||||
|
||||
/** A Javadoc tag. */
|
||||
/** A Javadoc block tag. This does not include inline tags. */
|
||||
class JavadocTag extends JavadocElement, JavadocParent, @javadocTag {
|
||||
/** Gets the name of this Javadoc tag. */
|
||||
string getTagName() { javadocTag(this, result, _, _) }
|
||||
|
||||
@@ -1,3 +1,8 @@
|
||||
/**
|
||||
* Provides classes and predicates for reasoning about instances of
|
||||
* `java.util.Map` and their methods.
|
||||
*/
|
||||
|
||||
import java
|
||||
import Collections
|
||||
|
||||
@@ -47,6 +52,7 @@ class MapSizeMethod extends MapMethod {
|
||||
class MapMutation extends MethodAccess {
|
||||
MapMutation() { this.getMethod() instanceof MapMutator }
|
||||
|
||||
/** Holds if the result of this call is not immediately discarded. */
|
||||
predicate resultIsChecked() { not this.getParent() instanceof ExprStmt }
|
||||
}
|
||||
|
||||
@@ -72,7 +78,9 @@ class FreshMap extends ClassInstanceExpr {
|
||||
class MapPutCall extends MethodAccess {
|
||||
MapPutCall() { getCallee().(MapMethod).hasName("put") }
|
||||
|
||||
/** Gets the key argument of this call. */
|
||||
Expr getKey() { result = getArgument(0) }
|
||||
|
||||
/** Gets the value argument of this call. */
|
||||
Expr getValue() { result = getArgument(1) }
|
||||
}
|
||||
|
||||
@@ -361,18 +361,23 @@ class Method extends Callable, @method {
|
||||
override MethodAccess getAReference() { result = Callable.super.getAReference() }
|
||||
|
||||
override predicate isPublic() {
|
||||
Callable.super.isPublic() or
|
||||
// JLS 9.4: Every method declaration in the body of an interface is implicitly public.
|
||||
getDeclaringType() instanceof Interface or
|
||||
Callable.super.isPublic()
|
||||
or
|
||||
// JLS 9.4: Every method declaration in the body of an interface without an
|
||||
// access modifier is implicitly public.
|
||||
getDeclaringType() instanceof Interface and
|
||||
not this.isPrivate()
|
||||
or
|
||||
exists(FunctionalExpr func | func.asMethod() = this)
|
||||
}
|
||||
|
||||
override predicate isAbstract() {
|
||||
Callable.super.isAbstract()
|
||||
or
|
||||
// JLS 9.4: An interface method lacking a `default` modifier or a `static` modifier
|
||||
// JLS 9.4: An interface method lacking a `private`, `default`, or `static` modifier
|
||||
// is implicitly abstract.
|
||||
this.getDeclaringType() instanceof Interface and
|
||||
not this.isPrivate() and
|
||||
not this.isDefault() and
|
||||
not this.isStatic()
|
||||
}
|
||||
|
||||
1005
java/ql/src/semmle/code/java/PrintAst.qll
Normal file
1005
java/ql/src/semmle/code/java/PrintAst.qll
Normal file
File diff suppressed because it is too large
Load Diff
@@ -7,12 +7,14 @@ import JDKAnnotations
|
||||
import Serializability
|
||||
import semmle.code.java.dataflow.DefUse
|
||||
|
||||
/** Holds if `f` is a field that may be read by reflection. */
|
||||
predicate reflectivelyRead(Field f) {
|
||||
f instanceof SerializableField or
|
||||
f.getAnAnnotation() instanceof ReflectiveAccessAnnotation or
|
||||
referencedInXmlFile(f)
|
||||
}
|
||||
|
||||
/** Holds if `f` is a field that may be written by reflection. */
|
||||
predicate reflectivelyWritten(Field f) {
|
||||
f instanceof DeserializableField or
|
||||
f.getAnAnnotation() instanceof ReflectiveAccessAnnotation or
|
||||
@@ -360,6 +362,7 @@ class ReflectiveFieldAccess extends ClassMethodAccess {
|
||||
this.getCallee().hasName("getDeclaredField")
|
||||
}
|
||||
|
||||
/** Gets the field accessed by this call. */
|
||||
Field inferAccessedField() {
|
||||
(
|
||||
if this.getCallee().hasName("getDeclaredField")
|
||||
|
||||
@@ -117,7 +117,7 @@ class IfStmt extends ConditionalStmt, @ifstmt {
|
||||
* Gets the statement that is executed whenever the condition
|
||||
* of this branch statement evaluates to `true`.
|
||||
*/
|
||||
override Stmt getTrueSuccessor() { result = getThen() }
|
||||
deprecated override Stmt getTrueSuccessor() { result = getThen() }
|
||||
|
||||
/** Gets the `else` branch of this `if` statement. */
|
||||
Stmt getElse() { result.isNthChildOf(this, 2) }
|
||||
@@ -168,7 +168,7 @@ class ForStmt extends ConditionalStmt, @forstmt {
|
||||
* Gets the statement that is executed whenever the condition
|
||||
* of this branch statement evaluates to true.
|
||||
*/
|
||||
override Stmt getTrueSuccessor() { result = getStmt() }
|
||||
deprecated override Stmt getTrueSuccessor() { result = getStmt() }
|
||||
|
||||
/**
|
||||
* Gets a variable that is used as an iteration variable: it is defined,
|
||||
@@ -228,7 +228,7 @@ class WhileStmt extends ConditionalStmt, @whilestmt {
|
||||
* Gets the statement that is executed whenever the condition
|
||||
* of this branch statement evaluates to true.
|
||||
*/
|
||||
override Stmt getTrueSuccessor() { result = getStmt() }
|
||||
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() }
|
||||
@@ -249,7 +249,7 @@ class DoStmt extends ConditionalStmt, @dostmt {
|
||||
* Gets the statement that is executed whenever the condition
|
||||
* of this branch statement evaluates to `true`.
|
||||
*/
|
||||
override Stmt getTrueSuccessor() { result = getStmt() }
|
||||
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 (...)" }
|
||||
@@ -522,8 +522,8 @@ class ThrowStmt extends Stmt, @throwstmt {
|
||||
|
||||
/**
|
||||
* Gets the `catch` clause that catches the exception
|
||||
* thrown by this `throws` statement and occurs
|
||||
* in the same method as this `throws` statement,
|
||||
* thrown by this `throw` statement and occurs
|
||||
* in the same method as this `throw` statement,
|
||||
* provided such a `catch` exists.
|
||||
*/
|
||||
CatchClause getLexicalCatchIfAny() {
|
||||
|
||||
@@ -80,6 +80,7 @@ private newtype TFmtSyntax =
|
||||
|
||||
/** A syntax for format strings. */
|
||||
class FmtSyntax extends TFmtSyntax {
|
||||
/** Gets a textual representation of this format string syntax. */
|
||||
string toString() {
|
||||
result = "printf (%) syntax" and this = TFmtPrintf()
|
||||
or
|
||||
@@ -130,6 +131,7 @@ class FormattingCall extends Call {
|
||||
formatWrapper(this.getCallee(), result, _)
|
||||
}
|
||||
|
||||
/** Gets the format string syntax used by this call. */
|
||||
FmtSyntax getSyntax() {
|
||||
this.getCallee() instanceof StringFormatMethod and result = TFmtPrintf()
|
||||
or
|
||||
@@ -146,6 +148,7 @@ class FormattingCall extends Call {
|
||||
)
|
||||
}
|
||||
|
||||
/** Holds if this uses the "logger ({})" format syntax and the last argument is a `Throwable`. */
|
||||
predicate hasTrailingThrowableArgument() {
|
||||
getSyntax() = TFmtLogger() and
|
||||
getLastArg().getType().(RefType).getASourceSupertype*() instanceof TypeThrowable
|
||||
@@ -245,8 +248,7 @@ private predicate formatStringFragment(Expr fmt) {
|
||||
e.(VarAccess).getVariable().getAnAssignedValue() = fmt or
|
||||
e.(AddExpr).getLeftOperand() = fmt or
|
||||
e.(AddExpr).getRightOperand() = fmt or
|
||||
e.(ConditionalExpr).getTrueExpr() = fmt or
|
||||
e.(ConditionalExpr).getFalseExpr() = fmt
|
||||
e.(ChooseExpr).getAResultExpr() = fmt
|
||||
)
|
||||
}
|
||||
|
||||
@@ -290,9 +292,7 @@ private predicate formatStringValue(Expr e, string fmtvalue) {
|
||||
fmtvalue = left + right
|
||||
)
|
||||
or
|
||||
formatStringValue(e.(ConditionalExpr).getTrueExpr(), fmtvalue)
|
||||
or
|
||||
formatStringValue(e.(ConditionalExpr).getFalseExpr(), fmtvalue)
|
||||
formatStringValue(e.(ChooseExpr).getAResultExpr(), fmtvalue)
|
||||
or
|
||||
exists(Method getprop, MethodAccess ma, string prop |
|
||||
e = ma and
|
||||
|
||||
@@ -637,10 +637,12 @@ class IntersectionType extends RefType, @class {
|
||||
|
||||
private RefType superInterface() { implInterface(this, result) }
|
||||
|
||||
/** Gets a textual representation of this type that includes all the intersected types. */
|
||||
string getLongName() {
|
||||
result = superType().toString() + concat(" & " + superInterface().toString())
|
||||
}
|
||||
|
||||
/** Gets the first bound of this intersection type. */
|
||||
RefType getFirstBound() { extendsReftype(this, result) }
|
||||
}
|
||||
|
||||
|
||||
@@ -1,3 +1,8 @@
|
||||
/**
|
||||
* Provides classes and predicates for reasoning about guards and the control
|
||||
* flow elements controlled by those guards.
|
||||
*/
|
||||
|
||||
import java
|
||||
private import semmle.code.java.controlflow.Dominance
|
||||
private import semmle.code.java.controlflow.internal.GuardsLogic
|
||||
|
||||
@@ -213,7 +213,7 @@ private predicate hasPossibleUnknownValue(SsaVariable v) {
|
||||
|
||||
/**
|
||||
* Gets a sub-expression of `e` whose value can flow to `e` through
|
||||
* `ConditionalExpr`s. Parentheses are also removed.
|
||||
* `ConditionalExpr`s.
|
||||
*/
|
||||
private Expr possibleValue(Expr e) {
|
||||
result = possibleValue(e.(ConditionalExpr).getTrueExpr())
|
||||
|
||||
@@ -1,3 +1,7 @@
|
||||
/**
|
||||
* Provides classes for representing abstract bounds for use in, for example, range analysis.
|
||||
*/
|
||||
|
||||
import java
|
||||
private import SSA
|
||||
private import RangeUtils
|
||||
@@ -14,6 +18,7 @@ private newtype TBound =
|
||||
* A bound that may be inferred for an expression plus/minus an integer delta.
|
||||
*/
|
||||
abstract class Bound extends TBound {
|
||||
/** Gets a textual representation of this bound. */
|
||||
abstract string toString();
|
||||
|
||||
/** Gets an expression that equals this bound plus `delta`. */
|
||||
@@ -22,6 +27,13 @@ abstract class Bound extends TBound {
|
||||
/** Gets an expression that equals this bound. */
|
||||
Expr getExpr() { result = getExpr(0) }
|
||||
|
||||
/**
|
||||
* Holds if this element is at the specified location.
|
||||
* The location spans column `sc` of line `sl` to
|
||||
* column `ec` of line `el` in file `path`.
|
||||
* For more information, see
|
||||
* [Locations](https://help.semmle.com/QL/learn-ql/ql/locations.html).
|
||||
*/
|
||||
predicate hasLocationInfo(string path, int sl, int sc, int el, int ec) {
|
||||
path = "" and sl = 0 and sc = 0 and el = 0 and ec = 0
|
||||
}
|
||||
|
||||
@@ -16,7 +16,9 @@ import semmle.code.java.frameworks.android.XmlParsing
|
||||
import semmle.code.java.frameworks.android.WebView
|
||||
import semmle.code.java.frameworks.JaxWS
|
||||
import semmle.code.java.frameworks.android.Intent
|
||||
import semmle.code.java.frameworks.SpringWeb
|
||||
import semmle.code.java.frameworks.spring.SpringWeb
|
||||
import semmle.code.java.frameworks.spring.SpringController
|
||||
import semmle.code.java.frameworks.spring.SpringWebClient
|
||||
import semmle.code.java.frameworks.Guice
|
||||
import semmle.code.java.frameworks.struts.StrutsActions
|
||||
import semmle.code.java.frameworks.Thrift
|
||||
@@ -103,9 +105,22 @@ private class MessageBodyReaderParameterSource extends RemoteFlowSource {
|
||||
override string getSourceType() { result = "MessageBodyReader parameter" }
|
||||
}
|
||||
|
||||
private class SpringMultipartFileSource extends RemoteFlowSource {
|
||||
SpringMultipartFileSource() {
|
||||
exists(MethodAccess ma, Method m |
|
||||
ma = this.asExpr() and
|
||||
m = ma.getMethod() and
|
||||
m.getDeclaringType().hasQualifiedName("org.springframework.web.multipart", "MultipartFile") and
|
||||
m.getName().matches("get%")
|
||||
)
|
||||
}
|
||||
|
||||
override string getSourceType() { result = "Spring MultipartFile getter" }
|
||||
}
|
||||
|
||||
private class SpringServletInputParameterSource extends RemoteFlowSource {
|
||||
SpringServletInputParameterSource() {
|
||||
this.asParameter().getAnAnnotation() instanceof SpringServletInputAnnotation
|
||||
this.asParameter() = any(SpringRequestMappingParameter srmp | srmp.isTaintedInput())
|
||||
}
|
||||
|
||||
override string getSourceType() { result = "Spring servlet input parameter" }
|
||||
@@ -152,9 +167,13 @@ deprecated class RemoteUserInput extends UserInput {
|
||||
RemoteUserInput() { this instanceof RemoteFlowSource }
|
||||
}
|
||||
|
||||
/** Input that may be controlled by a local user. */
|
||||
/** A node with input that may be controlled by a local user. */
|
||||
abstract class LocalUserInput extends UserInput { }
|
||||
|
||||
/**
|
||||
* A node with input from the local environment, such as files, standard in,
|
||||
* environment variables, and main method parameters.
|
||||
*/
|
||||
class EnvInput extends LocalUserInput {
|
||||
EnvInput() {
|
||||
// Parameters to a main method.
|
||||
@@ -180,6 +199,7 @@ class EnvInput extends LocalUserInput {
|
||||
}
|
||||
}
|
||||
|
||||
/** A node with input from a database. */
|
||||
class DatabaseInput extends LocalUserInput {
|
||||
DatabaseInput() { this.asExpr().(MethodAccess).getMethod() instanceof ResultSetGetStringMethod }
|
||||
}
|
||||
@@ -197,6 +217,8 @@ private class RemoteTaintedMethod extends Method {
|
||||
this instanceof HttpServletRequestGetRequestURIMethod or
|
||||
this instanceof HttpServletRequestGetRequestURLMethod or
|
||||
this instanceof HttpServletRequestGetRemoteUserMethod or
|
||||
this instanceof SpringWebRequestGetMethod or
|
||||
this instanceof SpringRestTemplateResponseEntityMethod or
|
||||
this instanceof ServletRequestGetBodyMethod or
|
||||
this instanceof CookieGetValueMethod or
|
||||
this instanceof CookieGetNameMethod or
|
||||
@@ -214,6 +236,22 @@ private class RemoteTaintedMethod extends Method {
|
||||
}
|
||||
}
|
||||
|
||||
private class SpringWebRequestGetMethod extends Method {
|
||||
SpringWebRequestGetMethod() {
|
||||
exists(SpringWebRequest swr | this = swr.getAMethod() |
|
||||
this.hasName("getDescription") or
|
||||
this.hasName("getHeader") or
|
||||
this.hasName("getHeaderNames") or
|
||||
this.hasName("getHeaderValues") or
|
||||
this.hasName("getParameter") or
|
||||
this.hasName("getParameterMap") or
|
||||
this.hasName("getParameterNames") or
|
||||
this.hasName("getParameterValues")
|
||||
// TODO consider getRemoteUser
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private class EnvTaintedMethod extends Method {
|
||||
EnvTaintedMethod() {
|
||||
this instanceof MethodSystemGetenv or
|
||||
@@ -222,10 +260,12 @@ private class EnvTaintedMethod extends Method {
|
||||
}
|
||||
}
|
||||
|
||||
/** The type `java.net.InetAddress`. */
|
||||
class TypeInetAddr extends RefType {
|
||||
TypeInetAddr() { this.getQualifiedName() = "java.net.InetAddress" }
|
||||
}
|
||||
|
||||
/** A reverse DNS method. */
|
||||
class ReverseDNSMethod extends Method {
|
||||
ReverseDNSMethod() {
|
||||
this.getDeclaringType() instanceof TypeInetAddr and
|
||||
|
||||
@@ -10,8 +10,7 @@ private import RangeAnalysis
|
||||
/** Gets an expression that might have the value `i`. */
|
||||
private Expr exprWithIntValue(int i) {
|
||||
result.(ConstantIntegerExpr).getIntValue() = i or
|
||||
result.(ConditionalExpr).getTrueExpr() = exprWithIntValue(i) or
|
||||
result.(ConditionalExpr).getFalseExpr() = exprWithIntValue(i)
|
||||
result.(ChooseExpr).getAResultExpr() = exprWithIntValue(i)
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -45,8 +45,7 @@ private import semmle.code.java.frameworks.Assertions
|
||||
/** Gets an expression that may be `null`. */
|
||||
Expr nullExpr() {
|
||||
result instanceof NullLiteral or
|
||||
result.(ConditionalExpr).getTrueExpr() = nullExpr() or
|
||||
result.(ConditionalExpr).getFalseExpr() = nullExpr() or
|
||||
result.(ChooseExpr).getAResultExpr() = nullExpr() or
|
||||
result.(AssignExpr).getSource() = nullExpr() or
|
||||
result.(CastExpr).getExpr() = nullExpr()
|
||||
}
|
||||
@@ -81,9 +80,7 @@ private predicate unboxed(Expr e) {
|
||||
or
|
||||
exists(UnaryExpr un | un.getExpr() = e)
|
||||
or
|
||||
exists(ConditionalExpr cond | cond.getType() instanceof PrimitiveType |
|
||||
cond.getTrueExpr() = e or cond.getFalseExpr() = e
|
||||
)
|
||||
exists(ChooseExpr cond | cond.getType() instanceof PrimitiveType | cond.getAResultExpr() = e)
|
||||
or
|
||||
exists(ConditionNode cond | cond.getCondition() = e)
|
||||
or
|
||||
@@ -579,7 +576,7 @@ private predicate varMaybeNullInBlock_corrCond(
|
||||
* - int: A means a specific integer value and B means any other value.
|
||||
*/
|
||||
|
||||
newtype TrackVarKind =
|
||||
private newtype TrackVarKind =
|
||||
TrackVarKindNull() or
|
||||
TrackVarKindBool() or
|
||||
TrackVarKindEnum() or
|
||||
@@ -701,7 +698,7 @@ private predicate isReset(
|
||||
}
|
||||
|
||||
/** The abstract value of the tracked variable. */
|
||||
newtype TrackedValue =
|
||||
private newtype TrackedValue =
|
||||
TrackedValueA() or
|
||||
TrackedValueB() or
|
||||
TrackedValueUnknown()
|
||||
|
||||
@@ -264,14 +264,21 @@ private newtype TReason =
|
||||
* without going through a bounding condition.
|
||||
*/
|
||||
abstract class Reason extends TReason {
|
||||
/** Gets a textual representation of this reason. */
|
||||
abstract string toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* A reason for an inferred bound that indicates that the bound is inferred
|
||||
* without going through a bounding condition.
|
||||
*/
|
||||
class NoReason extends Reason, TNoReason {
|
||||
override string toString() { result = "NoReason" }
|
||||
}
|
||||
|
||||
/** A reason for an inferred bound pointing to a condition. */
|
||||
class CondReason extends Reason, TCondReason {
|
||||
/** Gets the condition that is the reason for the bound. */
|
||||
Guard getCond() { this = TCondReason(result) }
|
||||
|
||||
override string toString() { result = getCond().toString() }
|
||||
|
||||
@@ -142,6 +142,7 @@ class SsaReadPosition extends TSsaReadPosition {
|
||||
/** Holds if `v` is read at this position. */
|
||||
abstract predicate hasReadOfVar(SsaVariable v);
|
||||
|
||||
/** Gets a textual representation of this SSA read position. */
|
||||
abstract string toString();
|
||||
}
|
||||
|
||||
|
||||
@@ -89,6 +89,7 @@ class SsaSourceVariable extends TSsaSourceVariable {
|
||||
this = TQualifiedField(result, _, _)
|
||||
}
|
||||
|
||||
/** Gets a textual representation of this `SsaSourceVariable`. */
|
||||
string toString() {
|
||||
exists(LocalScopeVariable v, Callable c | this = TLocalVar(c, v) |
|
||||
if c = v.getCallable()
|
||||
@@ -112,6 +113,7 @@ class SsaSourceVariable extends TSsaSourceVariable {
|
||||
)
|
||||
}
|
||||
|
||||
/** Gets the source location for this element. */
|
||||
Location getLocation() {
|
||||
exists(LocalScopeVariable v | this = TLocalVar(_, v) and result = v.getLocation())
|
||||
or
|
||||
@@ -935,8 +937,10 @@ class SsaVariable extends TSsaVariable {
|
||||
this = TSsaUntracked(_, result)
|
||||
}
|
||||
|
||||
/** Gets a textual representation of this SSA variable. */
|
||||
string toString() { none() }
|
||||
|
||||
/** Gets the source location for this element. */
|
||||
Location getLocation() { result = getCFGNode().getLocation() }
|
||||
|
||||
/** Gets the `BasicBlock` in which this SSA variable is defined. */
|
||||
@@ -1113,7 +1117,7 @@ class SsaPhiNode extends SsaVariable, TSsaPhiNode {
|
||||
}
|
||||
}
|
||||
|
||||
library class RefTypeCastExpr extends CastExpr {
|
||||
private class RefTypeCastExpr extends CastExpr {
|
||||
RefTypeCastExpr() { this.getType() instanceof RefType }
|
||||
}
|
||||
|
||||
|
||||
@@ -552,9 +552,7 @@ private Sign exprSign(Expr e) {
|
||||
result = s1.urshift(s2)
|
||||
)
|
||||
or
|
||||
result = exprSign(e.(ConditionalExpr).getTrueExpr())
|
||||
or
|
||||
result = exprSign(e.(ConditionalExpr).getFalseExpr())
|
||||
result = exprSign(e.(ChooseExpr).getAResultExpr())
|
||||
or
|
||||
result = exprSign(e.(CastExpr).getExpr())
|
||||
)
|
||||
|
||||
@@ -72,9 +72,7 @@ private predicate privateParamArg(Parameter p, Argument arg) {
|
||||
* necessarily functionally determined by `n2`.
|
||||
*/
|
||||
private predicate joinStep0(TypeFlowNode n1, TypeFlowNode n2) {
|
||||
n2.asExpr().(ConditionalExpr).getTrueExpr() = n1.asExpr()
|
||||
or
|
||||
n2.asExpr().(ConditionalExpr).getFalseExpr() = n1.asExpr()
|
||||
n2.asExpr().(ChooseExpr).getAResultExpr() = n1.asExpr()
|
||||
or
|
||||
exists(Field f, Expr e |
|
||||
f = n2.asField() and
|
||||
@@ -226,9 +224,8 @@ private predicate upcastCand(TypeFlowNode n, RefType t, RefType t1, RefType t2)
|
||||
or
|
||||
exists(Parameter p | privateParamArg(p, n.asExpr()) and t2 = p.getType().getErasure())
|
||||
or
|
||||
exists(ConditionalExpr cond |
|
||||
cond.getTrueExpr() = n.asExpr() or cond.getFalseExpr() = n.asExpr()
|
||||
|
|
||||
exists(ChooseExpr cond |
|
||||
cond.getAResultExpr() = n.asExpr() and
|
||||
t2 = cond.getType().getErasure()
|
||||
)
|
||||
)
|
||||
@@ -308,6 +305,21 @@ private predicate instanceOfGuarded(VarAccess va, RefType t) {
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `aa` is an access to a value that is guarded by `instanceof t`.
|
||||
*/
|
||||
predicate arrayInstanceOfGuarded(ArrayAccess aa, RefType t) {
|
||||
exists(InstanceOfExpr ioe, BaseSsaVariable v1, BaseSsaVariable v2, ArrayAccess aa1 |
|
||||
ioe.getExpr() = aa1 and
|
||||
t = ioe.getTypeName().getType() and
|
||||
aa1.getArray() = v1.getAUse() and
|
||||
aa1.getIndexExpr() = v2.getAUse() and
|
||||
aa.getArray() = v1.getAUse() and
|
||||
aa.getIndexExpr() = v2.getAUse() and
|
||||
guardControls_v1(ioe, aa.getBasicBlock(), true)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `n` has type `t` and this information is discarded, such that `t`
|
||||
* might be a better type bound for nodes where `n` flows to.
|
||||
@@ -318,6 +330,7 @@ private predicate typeFlowBase(TypeFlowNode n, RefType t) {
|
||||
upcastEnhancedForStmt(n.asSsa(), srctype) or
|
||||
downcastSuccessor(n.asExpr(), srctype) or
|
||||
instanceOfGuarded(n.asExpr(), srctype) or
|
||||
arrayInstanceOfGuarded(n.asExpr(), srctype) or
|
||||
n.asExpr().(FunctionalExpr).getConstructedType() = srctype
|
||||
|
|
||||
t = srctype.(BoundedType).getAnUltimateUpperBoundType()
|
||||
@@ -365,12 +378,12 @@ private predicate typeFlow(TypeFlowNode n, RefType t) {
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
predicate erasedTypeBound(RefType t) {
|
||||
private predicate erasedTypeBound(RefType t) {
|
||||
exists(RefType t0 | typeFlow(_, t0) and t = t0.getErasure())
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
predicate typeBound(RefType t) { typeFlow(_, t) }
|
||||
private predicate typeBound(RefType t) { typeFlow(_, t) }
|
||||
|
||||
/**
|
||||
* Holds if we have a bound for `n` that is better than `t`, taking only erased
|
||||
|
||||
@@ -89,45 +89,96 @@ class ContainerType extends RefType {
|
||||
}
|
||||
|
||||
private predicate taintPreservingQualifierToMethod(Method m) {
|
||||
// java.util.Map.Entry
|
||||
m.getDeclaringType() instanceof EntryType and
|
||||
m.hasName("getValue")
|
||||
m.hasName(["getValue", "setValue"])
|
||||
or
|
||||
// java.util.Iterable
|
||||
m.getDeclaringType() instanceof IterableType and
|
||||
m.hasName("iterator")
|
||||
m.hasName(["iterator", "spliterator"])
|
||||
or
|
||||
// java.util.Iterator
|
||||
m.getDeclaringType() instanceof IteratorType and
|
||||
m.hasName("next")
|
||||
or
|
||||
// java.util.ListIterator
|
||||
m.getDeclaringType() instanceof IteratorType and
|
||||
m.hasName("previous")
|
||||
or
|
||||
// java.util.Enumeration
|
||||
m.getDeclaringType() instanceof EnumerationType and
|
||||
m.hasName("nextElement")
|
||||
m.hasName(["asIterator", "nextElement"])
|
||||
or
|
||||
m.(MapMethod).hasName("entrySet")
|
||||
// java.util.Map
|
||||
m
|
||||
.(MapMethod)
|
||||
.hasName(["computeIfAbsent", "entrySet", "get", "getOrDefault", "put", "putIfAbsent",
|
||||
"remove", "replace", "values"])
|
||||
or
|
||||
m.(MapMethod).hasName("get")
|
||||
// java.util.Collection
|
||||
m.(CollectionMethod).hasName(["parallelStream", "stream", "toArray"])
|
||||
or
|
||||
m.(MapMethod).hasName("remove")
|
||||
or
|
||||
m.(MapMethod).hasName("values")
|
||||
or
|
||||
m.(CollectionMethod).hasName("toArray")
|
||||
or
|
||||
m.(CollectionMethod).hasName("get")
|
||||
// java.util.List
|
||||
m.(CollectionMethod).hasName(["get", "listIterator", "set", "subList"])
|
||||
or
|
||||
m.(CollectionMethod).hasName("remove") and m.getParameterType(0).(PrimitiveType).hasName("int")
|
||||
or
|
||||
// java.util.Vector
|
||||
m.(CollectionMethod).hasName(["elementAt", "elements", "firstElement", "lastElement"])
|
||||
or
|
||||
// java.util.Stack
|
||||
m.(CollectionMethod).hasName(["peek", "pop"])
|
||||
or
|
||||
// java.util.Queue
|
||||
// covered by Stack: peek()
|
||||
m.(CollectionMethod).hasName(["element", "poll"])
|
||||
or
|
||||
m.(CollectionMethod).hasName("remove") and m.getNumberOfParameters() = 0
|
||||
or
|
||||
m.(CollectionMethod).hasName("subList")
|
||||
// java.util.Deque
|
||||
m
|
||||
.(CollectionMethod)
|
||||
.hasName(["getFirst", "getLast", "peekFirst", "peekLast", "pollFirst", "pollLast",
|
||||
"removeFirst", "removeLast"])
|
||||
or
|
||||
m.(CollectionMethod).hasName("firstElement")
|
||||
// java.util.concurrent.BlockingQueue
|
||||
// covered by Queue: poll(long, TimeUnit)
|
||||
m.(CollectionMethod).hasName("take")
|
||||
or
|
||||
m.(CollectionMethod).hasName("lastElement")
|
||||
// java.util.concurrent.BlockingDeque
|
||||
// covered by Deque: pollFirst(long, TimeUnit), pollLast(long, TimeUnit)
|
||||
m.(CollectionMethod).hasName(["takeFirst", "takeLast"])
|
||||
or
|
||||
m.(CollectionMethod).hasName("poll")
|
||||
// java.util.SortedSet
|
||||
m.(CollectionMethod).hasName(["first", "headSet", "last", "subSet", "tailSet"])
|
||||
or
|
||||
m.(CollectionMethod).hasName("peek")
|
||||
// java.util.NavigableSet
|
||||
// covered by Deque: pollFirst(), pollLast()
|
||||
// covered by SortedSet: headSet(E, boolean), subSet(E, boolean, E, boolean) and tailSet(E, boolean)
|
||||
m
|
||||
.(CollectionMethod)
|
||||
.hasName(["ceiling", "descendingIterator", "descendingSet", "floor", "higher", "lower"])
|
||||
or
|
||||
m.(CollectionMethod).hasName("element")
|
||||
// java.util.SortedMap
|
||||
m.(MapMethod).hasName(["headMap", "subMap", "tailMap"])
|
||||
or
|
||||
// java.util.NavigableMap
|
||||
// covered by SortedMap: headMap(K, boolean), subMap(K, boolean, K, boolean), tailMap(K, boolean)
|
||||
m
|
||||
.(MapMethod)
|
||||
.hasName(["ceilingEntry", "descendingMap", "firstEntry", "floorEntry", "higherEntry",
|
||||
"lastEntry", "lowerEntry", "pollFirstEntry", "pollLastEntry"])
|
||||
or
|
||||
// java.util.Dictionary
|
||||
m
|
||||
.getDeclaringType()
|
||||
.getSourceDeclaration()
|
||||
.getASourceSupertype*()
|
||||
.hasQualifiedName("java.util", "Dictionary") and
|
||||
m.hasName(["elements", "get", "put", "remove"])
|
||||
or
|
||||
// java.util.concurrent.ConcurrentHashMap
|
||||
m.(MapMethod).hasName(["elements", "search", "searchEntries", "searchValues"])
|
||||
}
|
||||
|
||||
private predicate qualifierToMethodStep(Expr tracked, MethodAccess sink) {
|
||||
@@ -135,28 +186,162 @@ private predicate qualifierToMethodStep(Expr tracked, MethodAccess sink) {
|
||||
tracked = sink.getQualifier()
|
||||
}
|
||||
|
||||
private predicate qualifierToArgumentStep(Expr tracked, RValue sink) {
|
||||
exists(MethodAccess ma |
|
||||
ma.getMethod().(CollectionMethod).hasName("toArray") and
|
||||
private predicate qualifierToArgumentStep(Expr tracked, Expr sink) {
|
||||
exists(MethodAccess ma, CollectionMethod method |
|
||||
method = ma.getMethod() and
|
||||
(
|
||||
// java.util.Vector
|
||||
method.hasName("copyInto")
|
||||
or
|
||||
// java.util.concurrent.BlockingQueue
|
||||
method.hasName("drainTo")
|
||||
or
|
||||
// java.util.Collection
|
||||
method.hasName("toArray") and method.getParameter(0).getType() instanceof Array
|
||||
) and
|
||||
tracked = ma.getQualifier() and
|
||||
sink = ma.getArgument(1)
|
||||
sink = ma.getArgument(0)
|
||||
)
|
||||
}
|
||||
|
||||
private predicate taintPreservingArgumentToQualifier(Method method, int arg) {
|
||||
method.(MapMethod).hasName("put") and arg = 1
|
||||
// java.util.Map.Entry
|
||||
method.getDeclaringType() instanceof EntryType and
|
||||
method.hasName("setValue") and
|
||||
arg = 0
|
||||
or
|
||||
// java.util.Map
|
||||
method.(MapMethod).hasName(["merge", "put", "putIfAbsent"]) and arg = 1
|
||||
or
|
||||
method.(MapMethod).hasName("replace") and arg = method.getNumberOfParameters() - 1
|
||||
or
|
||||
method.(MapMethod).hasName("putAll") and arg = 0
|
||||
or
|
||||
method.(CollectionMethod).hasName("add") and arg = method.getNumberOfParameters() - 1
|
||||
// java.util.ListIterator
|
||||
method.getDeclaringType() instanceof IteratorType and
|
||||
method.hasName(["add", "set"]) and
|
||||
arg = 0
|
||||
or
|
||||
method.(CollectionMethod).hasName("addAll") and arg = method.getNumberOfParameters() - 1
|
||||
or
|
||||
method.(CollectionMethod).hasName("addElement") and arg = 0
|
||||
// java.util.Collection
|
||||
method.(CollectionMethod).hasName(["add", "addAll"]) and
|
||||
// Refer to the last parameter to also cover List::add(int, E) and List::addAll(int, Collection)
|
||||
arg = method.getNumberOfParameters() - 1
|
||||
or
|
||||
// java.util.List
|
||||
// covered by Collection: add(int, E), addAll(int, Collection<? extends E>)
|
||||
method.(CollectionMethod).hasName("set") and arg = 1
|
||||
or
|
||||
// java.util.Vector
|
||||
method.(CollectionMethod).hasName(["addElement", "insertElementAt", "setElementAt"]) and arg = 0
|
||||
or
|
||||
// java.util.Stack
|
||||
method.(CollectionMethod).hasName("push") and arg = 0
|
||||
or
|
||||
// java.util.Queue
|
||||
method.(CollectionMethod).hasName("offer") and arg = 0
|
||||
or
|
||||
// java.util.Deque
|
||||
// covered by Stack: push(E)
|
||||
method.(CollectionMethod).hasName(["addFirst", "addLast", "offerFirst", "offerLast"]) and arg = 0
|
||||
or
|
||||
// java.util.concurrent.BlockingQueue
|
||||
// covered by Queue: offer(E, long, TimeUnit)
|
||||
method.(CollectionMethod).hasName("put") and arg = 0
|
||||
or
|
||||
// java.util.concurrent.TransferQueue
|
||||
method.(CollectionMethod).hasName(["transfer", "tryTransfer"]) and arg = 0
|
||||
or
|
||||
// java.util.concurrent.BlockingDeque
|
||||
// covered by Deque: offerFirst(E, long, TimeUnit), offerLast(E, long, TimeUnit)
|
||||
method.(CollectionMethod).hasName(["putFirst", "putLast"]) and arg = 0
|
||||
or
|
||||
// java.util.Dictionary
|
||||
method
|
||||
.getDeclaringType()
|
||||
.getSourceDeclaration()
|
||||
.getASourceSupertype*()
|
||||
.hasQualifiedName("java.util", "Dictionary") and
|
||||
method.hasName("put") and
|
||||
arg = 1
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `method` is a library method that returns tainted data if its
|
||||
* `arg`th argument is tainted.
|
||||
*/
|
||||
private predicate taintPreservingArgumentToMethod(Method method, int arg) {
|
||||
method.getDeclaringType().hasQualifiedName("java.util", "Collections") and
|
||||
(
|
||||
method
|
||||
.hasName(["checkedCollection", "checkedList", "checkedMap", "checkedNavigableMap",
|
||||
"checkedNavigableSet", "checkedSet", "checkedSortedMap", "checkedSortedSet",
|
||||
"enumeration", "list", "max", "min", "singleton", "singletonList",
|
||||
"synchronizedCollection", "synchronizedList", "synchronizedMap",
|
||||
"synchronizedNavigableMap", "synchronizedNavigableSet", "synchronizedSet",
|
||||
"synchronizedSortedMap", "synchronizedSortedSet", "unmodifiableCollection",
|
||||
"unmodifiableList", "unmodifiableMap", "unmodifiableNavigableMap",
|
||||
"unmodifiableNavigableSet", "unmodifiableSet", "unmodifiableSortedMap",
|
||||
"unmodifiableSortedSet"]) and
|
||||
arg = 0
|
||||
or
|
||||
method.hasName(["nCopies", "singletonMap"]) and arg = 1
|
||||
)
|
||||
or
|
||||
method
|
||||
.getDeclaringType()
|
||||
.getSourceDeclaration()
|
||||
.hasQualifiedName("java.util", ["List", "Map", "Set"]) and
|
||||
method.hasName("copyOf") and
|
||||
arg = 0
|
||||
or
|
||||
method.getDeclaringType().getSourceDeclaration().hasQualifiedName("java.util", "Map") and
|
||||
(
|
||||
method.hasName("of") and
|
||||
arg = any(int i | i in [1 .. 10] | 2 * i - 1)
|
||||
or
|
||||
method.hasName("entry") and
|
||||
arg = 1
|
||||
)
|
||||
or
|
||||
method.getDeclaringType().hasQualifiedName("java.util", "Arrays") and
|
||||
(
|
||||
method.hasName(["copyOf", "copyOfRange", "spliterator", "stream"]) and
|
||||
arg = 0
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `method` is a library method that returns tainted data if any
|
||||
* of its arguments are tainted.
|
||||
*/
|
||||
private predicate taintPreservingArgumentToMethod(Method method) {
|
||||
method.getDeclaringType().getSourceDeclaration().hasQualifiedName("java.util", ["Set", "List"]) and
|
||||
method.hasName("of")
|
||||
or
|
||||
method.getDeclaringType().getSourceDeclaration().hasQualifiedName("java.util", "Map") and
|
||||
method.hasName("ofEntries")
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `method` is a library method that writes tainted data to the
|
||||
* `output`th argument if the `input`th argument is tainted.
|
||||
*/
|
||||
private predicate taintPreservingArgToArg(Method method, int input, int output) {
|
||||
method.getDeclaringType().hasQualifiedName("java.util", "Collections") and
|
||||
(
|
||||
method.hasName(["copy", "fill"]) and
|
||||
input = 1 and
|
||||
output = 0
|
||||
or
|
||||
method.hasName("replaceAll") and input = 2 and output = 0
|
||||
)
|
||||
or
|
||||
method.getDeclaringType().hasQualifiedName("java.util", "Arrays") and
|
||||
(
|
||||
method.hasName("fill") and
|
||||
output = 0 and
|
||||
input = method.getNumberOfParameters() - 1
|
||||
)
|
||||
}
|
||||
|
||||
private predicate argToQualifierStep(Expr tracked, Expr sink) {
|
||||
@@ -168,13 +353,55 @@ private predicate argToQualifierStep(Expr tracked, Expr sink) {
|
||||
)
|
||||
}
|
||||
|
||||
/** Access to a method that passes taint from an argument. */
|
||||
private predicate argToMethodStep(Expr tracked, MethodAccess sink) {
|
||||
exists(Method m |
|
||||
m = sink.getMethod() and
|
||||
(
|
||||
exists(int i |
|
||||
taintPreservingArgumentToMethod(m, i) and
|
||||
tracked = sink.getArgument(i)
|
||||
)
|
||||
or
|
||||
m.getDeclaringType().hasQualifiedName("java.util", "Arrays") and
|
||||
m.hasName("asList") and
|
||||
tracked = sink.getAnArgument()
|
||||
)
|
||||
)
|
||||
or
|
||||
taintPreservingArgumentToMethod(sink.getMethod()) and
|
||||
tracked = sink.getAnArgument()
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `tracked` and `sink` are arguments to a method that transfers taint
|
||||
* between arguments.
|
||||
*/
|
||||
private predicate argToArgStep(Expr tracked, Expr sink) {
|
||||
exists(MethodAccess ma, Method method, int input, int output |
|
||||
ma.getMethod() = method and
|
||||
ma.getArgument(input) = tracked and
|
||||
ma.getArgument(output) = sink and
|
||||
(
|
||||
taintPreservingArgToArg(method, input, output)
|
||||
or
|
||||
method.getDeclaringType().hasQualifiedName("java.util", "Collections") and
|
||||
method.hasName("addAll") and
|
||||
input >= 1 and
|
||||
output = 0
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if the step from `n1` to `n2` is either extracting a value from a
|
||||
* container, inserting a value into a container, or transforming one container
|
||||
* to another. This is restricted to cases where `n2` is the returned value of
|
||||
* a call.
|
||||
*/
|
||||
predicate containerReturnValueStep(Expr n1, Expr n2) { qualifierToMethodStep(n1, n2) }
|
||||
predicate containerReturnValueStep(Expr n1, Expr n2) {
|
||||
qualifierToMethodStep(n1, n2) or argToMethodStep(n1, n2)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if the step from `n1` to `n2` is either extracting a value from a
|
||||
@@ -183,7 +410,8 @@ predicate containerReturnValueStep(Expr n1, Expr n2) { qualifierToMethodStep(n1,
|
||||
*/
|
||||
predicate containerUpdateStep(Expr n1, Expr n2) {
|
||||
qualifierToArgumentStep(n1, n2) or
|
||||
argToQualifierStep(n1, n2)
|
||||
argToQualifierStep(n1, n2) or
|
||||
argToArgStep(n1, n2)
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -2,14 +2,13 @@ private import java
|
||||
private import DataFlowPrivate
|
||||
import semmle.code.java.dispatch.VirtualDispatch
|
||||
|
||||
cached
|
||||
private module DispatchImpl {
|
||||
/**
|
||||
* 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
|
||||
* qualifier is the `i`th parameter of the enclosing callable `c`.
|
||||
*/
|
||||
private predicate benefitsFromCallContext(MethodAccess ma, Callable c, int i) {
|
||||
private predicate mayBenefitFromCallContext(MethodAccess ma, Callable c, int i) {
|
||||
exists(Parameter p |
|
||||
2 <= strictcount(viableImpl(ma)) and
|
||||
ma.getQualifier().(VarAccess).getVariable() = p and
|
||||
@@ -28,7 +27,7 @@ private module DispatchImpl {
|
||||
pragma[nomagic]
|
||||
private predicate relevantContext(Call ctx, int i) {
|
||||
exists(Callable c |
|
||||
benefitsFromCallContext(_, c, i) and
|
||||
mayBenefitFromCallContext(_, c, i) and
|
||||
c = viableCallable(ctx)
|
||||
)
|
||||
}
|
||||
@@ -53,14 +52,23 @@ private module DispatchImpl {
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
* qualifier is a parameter of the enclosing callable `c`.
|
||||
*/
|
||||
predicate mayBenefitFromCallContext(MethodAccess ma, Callable c) {
|
||||
mayBenefitFromCallContext(ma, c, _)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a viable dispatch target of `ma` in the context `ctx`. This is
|
||||
* restricted to those `ma`s for which a context might make a difference.
|
||||
*/
|
||||
private Method viableImplInCallContext(MethodAccess ma, Call ctx) {
|
||||
Method viableImplInCallContext(MethodAccess ma, Call ctx) {
|
||||
result = viableImpl(ma) and
|
||||
exists(int i, Callable c, Method def, RefType t, boolean exact |
|
||||
benefitsFromCallContext(ma, c, i) and
|
||||
mayBenefitFromCallContext(ma, c, i) and
|
||||
c = viableCallable(ctx) and
|
||||
contextArgHasType(ctx, i, t, exact) and
|
||||
ma.getMethod() = def
|
||||
@@ -136,57 +144,6 @@ private module DispatchImpl {
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if the call context `ctx` reduces the set of viable dispatch
|
||||
* targets of `ma` in `c`.
|
||||
*/
|
||||
cached
|
||||
predicate reducedViableImplInCallContext(MethodAccess ma, Callable c, Call ctx) {
|
||||
exists(int tgts, int ctxtgts |
|
||||
benefitsFromCallContext(ma, c, _) and
|
||||
c = viableCallable(ctx) and
|
||||
ctxtgts = count(viableImplInCallContext(ma, ctx)) and
|
||||
tgts = strictcount(viableImpl(ma)) and
|
||||
ctxtgts < tgts
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a viable dispatch target of `ma` in the context `ctx`. This is
|
||||
* restricted to those `ma`s for which the context makes a difference.
|
||||
*/
|
||||
cached
|
||||
Method prunedViableImplInCallContext(MethodAccess ma, Call ctx) {
|
||||
result = viableImplInCallContext(ma, ctx) and
|
||||
reducedViableImplInCallContext(ma, _, ctx)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if flow returning from `m` to `ma` might return further and if
|
||||
* this path restricts the set of call sites that can be returned to.
|
||||
*/
|
||||
cached
|
||||
predicate reducedViableImplInReturn(Method m, MethodAccess ma) {
|
||||
exists(int tgts, int ctxtgts |
|
||||
benefitsFromCallContext(ma, _, _) and
|
||||
m = viableImpl(ma) and
|
||||
ctxtgts = count(Call ctx | m = viableImplInCallContext(ma, ctx)) and
|
||||
tgts = strictcount(Call ctx | viableCallable(ctx) = ma.getEnclosingCallable()) and
|
||||
ctxtgts < tgts
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a viable dispatch target of `ma` in the context `ctx`. This is
|
||||
* restricted to those `ma`s and results for which the return flow from the
|
||||
* result to `ma` restricts the possible context `ctx`.
|
||||
*/
|
||||
cached
|
||||
Method prunedViableImplInCallContextReverse(MethodAccess ma, Call ctx) {
|
||||
result = viableImplInCallContext(ma, ctx) and
|
||||
reducedViableImplInReturn(result, ma)
|
||||
}
|
||||
}
|
||||
|
||||
import DispatchImpl
|
||||
|
||||
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
@@ -22,17 +22,34 @@ private module Cached {
|
||||
exists(int i |
|
||||
viableParam(call, i, p) and
|
||||
arg.argumentOf(call, i) and
|
||||
compatibleTypes(getErasedNodeTypeBound(arg), getErasedNodeTypeBound(p))
|
||||
compatibleTypes(getNodeType(arg), getNodeType(p))
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private ReturnPosition viableReturnPos(DataFlowCall call, ReturnKindExt kind) {
|
||||
viableCallable(call) = result.getCallable() and
|
||||
kind = result.getKind()
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if a value at return position `pos` can be returned to `out` via `call`,
|
||||
* taking virtual dispatch into account.
|
||||
*/
|
||||
cached
|
||||
predicate viableReturnPosOut(DataFlowCall call, ReturnPosition pos, Node out) {
|
||||
exists(ReturnKindExt kind |
|
||||
pos = viableReturnPos(call, kind) and
|
||||
out = kind.getAnOutNode(call)
|
||||
)
|
||||
}
|
||||
|
||||
/** Provides predicates for calculating flow-through summaries. */
|
||||
cached
|
||||
private module FlowThrough {
|
||||
/**
|
||||
* The first flow-through approximation:
|
||||
*
|
||||
* - Input/output access paths are abstracted with a Boolean parameter
|
||||
* - Input access paths are abstracted with a Boolean parameter
|
||||
* that indicates (non-)emptiness.
|
||||
*/
|
||||
private module Cand {
|
||||
@@ -40,83 +57,47 @@ private module Cached {
|
||||
* Holds if `p` can flow to `node` in the same callable using only
|
||||
* value-preserving steps.
|
||||
*
|
||||
* `read` indicates whether it is contents of `p` that can flow to `node`,
|
||||
* and `stored` indicates whether it flows to contents of `node`.
|
||||
* `read` indicates whether it is contents of `p` that can flow to `node`.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
private predicate parameterValueFlowCand(
|
||||
ParameterNode p, Node node, boolean read, boolean stored
|
||||
) {
|
||||
private predicate parameterValueFlowCand(ParameterNode p, Node node, boolean read) {
|
||||
p = node and
|
||||
read = false and
|
||||
stored = false
|
||||
read = false
|
||||
or
|
||||
// local flow
|
||||
exists(Node mid |
|
||||
parameterValueFlowCand(p, mid, read, stored) and
|
||||
parameterValueFlowCand(p, mid, read) and
|
||||
simpleLocalFlowStep(mid, node)
|
||||
)
|
||||
or
|
||||
// read
|
||||
exists(Node mid, boolean readMid, boolean storedMid |
|
||||
parameterValueFlowCand(p, mid, readMid, storedMid) and
|
||||
readStep(mid, _, node) and
|
||||
stored = false
|
||||
|
|
||||
// value neither read nor stored prior to read
|
||||
readMid = false and
|
||||
storedMid = false and
|
||||
read = true
|
||||
or
|
||||
// value (possibly read and then) stored prior to read (same content)
|
||||
read = readMid and
|
||||
storedMid = true
|
||||
)
|
||||
or
|
||||
// store
|
||||
exists(Node mid |
|
||||
parameterValueFlowCand(p, mid, read, false) and
|
||||
storeStep(mid, _, node) and
|
||||
stored = true
|
||||
parameterValueFlowCand(p, mid, false) and
|
||||
readStep(mid, _, node) and
|
||||
read = true
|
||||
)
|
||||
or
|
||||
// flow through: no prior read or store
|
||||
// flow through: no prior read
|
||||
exists(ArgumentNode arg |
|
||||
parameterValueFlowArgCand(p, arg, false, false) and
|
||||
argumentValueFlowsThroughCand(arg, node, read, stored)
|
||||
parameterValueFlowArgCand(p, arg, false) and
|
||||
argumentValueFlowsThroughCand(arg, node, read)
|
||||
)
|
||||
or
|
||||
// flow through: no read or store inside method
|
||||
// flow through: no read inside method
|
||||
exists(ArgumentNode arg |
|
||||
parameterValueFlowArgCand(p, arg, read, stored) and
|
||||
argumentValueFlowsThroughCand(arg, node, false, false)
|
||||
)
|
||||
or
|
||||
// flow through: possible prior read and prior store with compatible
|
||||
// flow-through method
|
||||
exists(ArgumentNode arg, boolean mid |
|
||||
parameterValueFlowArgCand(p, arg, read, mid) and
|
||||
argumentValueFlowsThroughCand(arg, node, mid, stored)
|
||||
parameterValueFlowArgCand(p, arg, read) and
|
||||
argumentValueFlowsThroughCand(arg, node, false)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate parameterValueFlowArgCand(
|
||||
ParameterNode p, ArgumentNode arg, boolean read, boolean stored
|
||||
) {
|
||||
parameterValueFlowCand(p, arg, read, stored)
|
||||
private predicate parameterValueFlowArgCand(ParameterNode p, ArgumentNode arg, boolean read) {
|
||||
parameterValueFlowCand(p, arg, read)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
predicate parameterValueFlowsToPreUpdateCand(ParameterNode p, PostUpdateNode n) {
|
||||
parameterValueFlowCand(p, n.getPreUpdateNode(), false, false)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate parameterValueFlowsToPostUpdateCand(
|
||||
ParameterNode p, PostUpdateNode n, boolean read
|
||||
) {
|
||||
parameterValueFlowCand(p, n, read, true)
|
||||
parameterValueFlowCand(p, n.getPreUpdateNode(), false)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -125,33 +106,21 @@ private module Cached {
|
||||
* into account.
|
||||
*
|
||||
* `read` indicates whether it is contents of `p` that can flow to the return
|
||||
* node, and `stored` indicates whether it flows to contents of the return
|
||||
* node.
|
||||
*/
|
||||
predicate parameterValueFlowReturnCand(
|
||||
ParameterNode p, ReturnKindExt kind, boolean read, boolean stored
|
||||
) {
|
||||
predicate parameterValueFlowReturnCand(ParameterNode p, ReturnKind kind, boolean read) {
|
||||
exists(ReturnNode ret |
|
||||
parameterValueFlowCand(p, ret, read, stored) and
|
||||
kind = TValueReturn(ret.getKind())
|
||||
)
|
||||
or
|
||||
exists(ParameterNode p2, int pos2, PostUpdateNode n |
|
||||
parameterValueFlowsToPostUpdateCand(p, n, read) and
|
||||
parameterValueFlowsToPreUpdateCand(p2, n) and
|
||||
p2.isParameterOf(_, pos2) and
|
||||
kind = TParamUpdate(pos2) and
|
||||
p != p2 and
|
||||
stored = true
|
||||
parameterValueFlowCand(p, ret, read) and
|
||||
kind = ret.getKind()
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate argumentValueFlowsThroughCand0(
|
||||
DataFlowCall call, ArgumentNode arg, ReturnKindExt kind, boolean read, boolean stored
|
||||
DataFlowCall call, ArgumentNode arg, ReturnKind kind, boolean read
|
||||
) {
|
||||
exists(ParameterNode param | viableParamArg(call, param, arg) |
|
||||
parameterValueFlowReturnCand(param, kind, read, stored)
|
||||
parameterValueFlowReturnCand(param, kind, read)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -159,308 +128,257 @@ private module Cached {
|
||||
* Holds if `arg` flows to `out` through a call using only value-preserving steps,
|
||||
* not taking call contexts into account.
|
||||
*
|
||||
* `read` indicates whether it is contents of `arg` that can flow to `out`, and
|
||||
* `stored` indicates whether it flows to contents of `out`.
|
||||
* `read` indicates whether it is contents of `arg` that can flow to `out`.
|
||||
*/
|
||||
predicate argumentValueFlowsThroughCand(
|
||||
ArgumentNode arg, Node out, boolean read, boolean stored
|
||||
) {
|
||||
exists(DataFlowCall call, ReturnKindExt kind |
|
||||
argumentValueFlowsThroughCand0(call, arg, kind, read, stored) and
|
||||
out = kind.getAnOutNode(call)
|
||||
predicate argumentValueFlowsThroughCand(ArgumentNode 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) {
|
||||
parameterValueFlowCand(p, n, _, _) and
|
||||
parameterValueFlowCand(p, n, _) and
|
||||
(
|
||||
parameterValueFlowReturnCand(p, _, _, _)
|
||||
parameterValueFlowReturnCand(p, _, _)
|
||||
or
|
||||
parameterValueFlowsToPreUpdateCand(p, _)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private module LocalFlowBigStep {
|
||||
private predicate localFlowEntry(Node n) {
|
||||
Cand::cand(_, n) and
|
||||
(
|
||||
n instanceof ParameterNode or
|
||||
n instanceof OutNode or
|
||||
n instanceof PostUpdateNode or
|
||||
readStep(_, _, n) or
|
||||
n instanceof CastNode
|
||||
)
|
||||
}
|
||||
|
||||
private predicate localFlowExit(Node n) {
|
||||
Cand::cand(_, n) and
|
||||
(
|
||||
n instanceof ArgumentNode
|
||||
or
|
||||
n instanceof ReturnNode
|
||||
or
|
||||
Cand::parameterValueFlowsToPreUpdateCand(_, n)
|
||||
or
|
||||
storeStep(n, _, _)
|
||||
or
|
||||
readStep(n, _, _)
|
||||
or
|
||||
n instanceof CastNode
|
||||
or
|
||||
n =
|
||||
any(PostUpdateNode pun | Cand::parameterValueFlowsToPreUpdateCand(_, pun))
|
||||
.getPreUpdateNode()
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate localFlowStepPlus(Node node1, Node node2) {
|
||||
localFlowEntry(node1) and
|
||||
simpleLocalFlowStep(node1, node2) and
|
||||
node1 != node2
|
||||
or
|
||||
exists(Node mid |
|
||||
localFlowStepPlus(node1, mid) and
|
||||
simpleLocalFlowStep(mid, node2) and
|
||||
not mid instanceof CastNode
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
predicate localFlowBigStep(Node node1, Node node2) {
|
||||
localFlowStepPlus(node1, node2) and
|
||||
localFlowExit(node2)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The final flow-through calculation:
|
||||
*
|
||||
* - Input/output access paths are abstracted with a `ContentOption` parameter
|
||||
* that represents the head of the access path. `TContentNone()` means that
|
||||
* the access path is unrestricted.
|
||||
* - Calculated flow is either value-preserving (`read = TReadStepTypesNone()`)
|
||||
* or summarized as a single read step with before and after types recorded
|
||||
* in the `ReadStepTypesOption` parameter.
|
||||
* - Types are checked using the `compatibleTypes()` relation.
|
||||
*/
|
||||
cached
|
||||
private module Final {
|
||||
/**
|
||||
* Holds if `p` can flow to `node` in the same callable using only
|
||||
* value-preserving steps, not taking call contexts into account.
|
||||
* value-preserving steps and possibly a single read step, not taking
|
||||
* call contexts into account.
|
||||
*
|
||||
* `contentIn` describes the content of `p` that can flow to `node`
|
||||
* (if any), and `contentOut` describes the content of `node` that
|
||||
* it flows to (if any).
|
||||
* If a read step was taken, then `read` captures the `Content`, the
|
||||
* container type, and the content type.
|
||||
*/
|
||||
private predicate parameterValueFlow(
|
||||
ParameterNode p, Node node, ContentOption contentIn, ContentOption contentOut
|
||||
) {
|
||||
parameterValueFlow0(p, node, contentIn, contentOut) and
|
||||
predicate parameterValueFlow(ParameterNode p, Node node, ReadStepTypesOption read) {
|
||||
parameterValueFlow0(p, node, read) and
|
||||
if node instanceof CastingNode
|
||||
then
|
||||
// normal flow through
|
||||
contentIn = TContentNone() and
|
||||
contentOut = TContentNone() and
|
||||
compatibleTypes(getErasedNodeTypeBound(p), getErasedNodeTypeBound(node))
|
||||
read = TReadStepTypesNone() and
|
||||
compatibleTypes(getNodeType(p), getNodeType(node))
|
||||
or
|
||||
// getter
|
||||
exists(Content fIn |
|
||||
contentIn.getContent() = fIn and
|
||||
contentOut = TContentNone() and
|
||||
compatibleTypes(fIn.getType(), getErasedNodeTypeBound(node))
|
||||
)
|
||||
or
|
||||
// (getter+)setter
|
||||
exists(Content fOut |
|
||||
contentOut.getContent() = fOut and
|
||||
compatibleTypes(fOut.getContainerType(), getErasedNodeTypeBound(node))
|
||||
)
|
||||
compatibleTypes(read.getContentType(), getNodeType(node))
|
||||
else any()
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate parameterValueFlow0(
|
||||
ParameterNode p, Node node, ContentOption contentIn, ContentOption contentOut
|
||||
) {
|
||||
private predicate parameterValueFlow0(ParameterNode p, Node node, ReadStepTypesOption read) {
|
||||
p = node and
|
||||
Cand::cand(p, _) and
|
||||
contentIn = TContentNone() and
|
||||
contentOut = TContentNone()
|
||||
read = TReadStepTypesNone()
|
||||
or
|
||||
// local flow
|
||||
exists(Node mid |
|
||||
parameterValueFlow(p, mid, contentIn, contentOut) and
|
||||
LocalFlowBigStep::localFlowBigStep(mid, node)
|
||||
parameterValueFlow(p, mid, read) and
|
||||
simpleLocalFlowStep(mid, node)
|
||||
)
|
||||
or
|
||||
// read
|
||||
exists(Node mid, Content f, ContentOption contentInMid, ContentOption contentOutMid |
|
||||
parameterValueFlow(p, mid, contentInMid, contentOutMid) and
|
||||
readStep(mid, f, node)
|
||||
|
|
||||
// value neither read nor stored prior to read
|
||||
contentInMid = TContentNone() and
|
||||
contentOutMid = TContentNone() and
|
||||
contentIn.getContent() = f and
|
||||
contentOut = TContentNone() and
|
||||
Cand::parameterValueFlowReturnCand(p, _, true, _) and
|
||||
compatibleTypes(getErasedNodeTypeBound(p), f.getContainerType())
|
||||
or
|
||||
// value (possibly read and then) stored prior to read (same content)
|
||||
contentIn = contentInMid and
|
||||
contentOutMid.getContent() = f and
|
||||
contentOut = TContentNone()
|
||||
exists(Node mid |
|
||||
parameterValueFlow(p, mid, TReadStepTypesNone()) and
|
||||
readStepWithTypes(mid, read.getContainerType(), read.getContent(), node,
|
||||
read.getContentType()) and
|
||||
Cand::parameterValueFlowReturnCand(p, _, true) and
|
||||
compatibleTypes(getNodeType(p), read.getContainerType())
|
||||
)
|
||||
or
|
||||
// store
|
||||
exists(Node mid, Content f |
|
||||
parameterValueFlow(p, mid, contentIn, TContentNone()) and
|
||||
storeStep(mid, f, node) and
|
||||
contentOut.getContent() = f
|
||||
|
|
||||
contentIn = TContentNone() and
|
||||
compatibleTypes(getErasedNodeTypeBound(p), f.getType())
|
||||
or
|
||||
compatibleTypes(contentIn.getContent().getType(), f.getType())
|
||||
)
|
||||
or
|
||||
// flow through: no prior read or store
|
||||
parameterValueFlow0_0(TReadStepTypesNone(), p, node, read)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate parameterValueFlow0_0(
|
||||
ReadStepTypesOption mustBeNone, ParameterNode p, Node node, ReadStepTypesOption read
|
||||
) {
|
||||
// flow through: no prior read
|
||||
exists(ArgumentNode arg |
|
||||
parameterValueFlowArg(p, arg, TContentNone(), TContentNone()) and
|
||||
argumentValueFlowsThrough(_, arg, contentIn, contentOut, node)
|
||||
parameterValueFlowArg(p, arg, mustBeNone) and
|
||||
argumentValueFlowsThrough(arg, read, node)
|
||||
)
|
||||
or
|
||||
// flow through: no read or store inside method
|
||||
// flow through: no read inside method
|
||||
exists(ArgumentNode arg |
|
||||
parameterValueFlowArg(p, arg, contentIn, contentOut) and
|
||||
argumentValueFlowsThrough(_, arg, TContentNone(), TContentNone(), node)
|
||||
)
|
||||
or
|
||||
// flow through: possible prior read and prior store with compatible
|
||||
// flow-through method
|
||||
exists(ArgumentNode arg, ContentOption contentMid |
|
||||
parameterValueFlowArg(p, arg, contentIn, contentMid) and
|
||||
argumentValueFlowsThrough(_, arg, contentMid, contentOut, node)
|
||||
parameterValueFlowArg(p, arg, read) and
|
||||
argumentValueFlowsThrough(arg, mustBeNone, node)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate parameterValueFlowArg(
|
||||
ParameterNode p, ArgumentNode arg, ContentOption contentIn, ContentOption contentOut
|
||||
ParameterNode p, ArgumentNode arg, ReadStepTypesOption read
|
||||
) {
|
||||
parameterValueFlow(p, arg, contentIn, contentOut) and
|
||||
Cand::argumentValueFlowsThroughCand(arg, _, _, _)
|
||||
parameterValueFlow(p, arg, read) and
|
||||
Cand::argumentValueFlowsThroughCand(arg, _, _)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate argumentValueFlowsThrough0(
|
||||
DataFlowCall call, ArgumentNode arg, ReturnKindExt kind, ContentOption contentIn,
|
||||
ContentOption contentOut
|
||||
DataFlowCall call, ArgumentNode arg, ReturnKind kind, ReadStepTypesOption read
|
||||
) {
|
||||
exists(ParameterNode param | viableParamArg(call, param, arg) |
|
||||
parameterValueFlowReturn(param, _, kind, contentIn, contentOut)
|
||||
parameterValueFlowReturn(param, kind, read)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `arg` flows to `out` through `call` using only value-preserving steps,
|
||||
* not taking call contexts into account.
|
||||
* Holds if `arg` flows to `out` through a call using only
|
||||
* value-preserving steps and possibly a single read step, not taking
|
||||
* call contexts into account.
|
||||
*
|
||||
* `contentIn` describes the content of `arg` that can flow to `out` (if any), and
|
||||
* `contentOut` describes the content of `out` that it flows to (if any).
|
||||
* If a read step was taken, then `read` captures the `Content`, the
|
||||
* container type, and the content type.
|
||||
*/
|
||||
cached
|
||||
predicate argumentValueFlowsThrough(
|
||||
DataFlowCall call, ArgumentNode arg, ContentOption contentIn, ContentOption contentOut,
|
||||
Node out
|
||||
) {
|
||||
exists(ReturnKindExt kind |
|
||||
argumentValueFlowsThrough0(call, arg, kind, contentIn, contentOut) and
|
||||
out = kind.getAnOutNode(call)
|
||||
pragma[nomagic]
|
||||
predicate argumentValueFlowsThrough(ArgumentNode arg, ReadStepTypesOption read, Node out) {
|
||||
exists(DataFlowCall call, ReturnKind kind |
|
||||
argumentValueFlowsThrough0(call, arg, kind, read) and
|
||||
out = getAnOutNode(call, kind)
|
||||
|
|
||||
// normal flow through
|
||||
contentIn = TContentNone() and
|
||||
contentOut = TContentNone() and
|
||||
compatibleTypes(getErasedNodeTypeBound(arg), getErasedNodeTypeBound(out))
|
||||
read = TReadStepTypesNone() and
|
||||
compatibleTypes(getNodeType(arg), getNodeType(out))
|
||||
or
|
||||
// getter
|
||||
exists(Content fIn |
|
||||
contentIn.getContent() = fIn and
|
||||
contentOut = TContentNone() and
|
||||
compatibleTypes(getErasedNodeTypeBound(arg), fIn.getContainerType()) and
|
||||
compatibleTypes(fIn.getType(), getErasedNodeTypeBound(out))
|
||||
)
|
||||
or
|
||||
// setter
|
||||
exists(Content fOut |
|
||||
contentIn = TContentNone() and
|
||||
contentOut.getContent() = fOut and
|
||||
compatibleTypes(getErasedNodeTypeBound(arg), fOut.getType()) and
|
||||
compatibleTypes(fOut.getContainerType(), getErasedNodeTypeBound(out))
|
||||
)
|
||||
or
|
||||
// getter+setter
|
||||
exists(Content fIn, Content fOut |
|
||||
contentIn.getContent() = fIn and
|
||||
contentOut.getContent() = fOut and
|
||||
compatibleTypes(getErasedNodeTypeBound(arg), fIn.getContainerType()) and
|
||||
compatibleTypes(fOut.getContainerType(), getErasedNodeTypeBound(out))
|
||||
)
|
||||
compatibleTypes(getNodeType(arg), read.getContainerType()) and
|
||||
compatibleTypes(read.getContentType(), getNodeType(out))
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.
|
||||
* Holds if `arg` flows to `out` through a call using only
|
||||
* value-preserving steps and a single read step, not taking call
|
||||
* contexts into account, thus representing a getter-step.
|
||||
*/
|
||||
cached
|
||||
predicate parameterValueFlowsToPreUpdate(ParameterNode p, PostUpdateNode n) {
|
||||
parameterValueFlow(p, n.getPreUpdateNode(), TContentNone(), TContentNone())
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate parameterValueFlowsToPostUpdate(
|
||||
ParameterNode p, PostUpdateNode n, ContentOption contentIn, ContentOption contentOut
|
||||
) {
|
||||
parameterValueFlow(p, n, contentIn, contentOut) and
|
||||
contentOut.hasContent()
|
||||
predicate getterStep(ArgumentNode arg, Content c, Node out) {
|
||||
argumentValueFlowsThrough(arg, TReadStepTypesSome(_, c, _), out)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `p` can flow to a return node of kind `kind` in the same
|
||||
* callable using only value-preserving steps.
|
||||
* callable using only value-preserving steps and possibly a single read
|
||||
* step.
|
||||
*
|
||||
* `contentIn` describes the content of `p` that can flow to the return
|
||||
* node (if any), and `contentOut` describes the content of the return
|
||||
* node that it flows to (if any).
|
||||
* If a read step was taken, then `read` captures the `Content`, the
|
||||
* container type, and the content type.
|
||||
*/
|
||||
cached
|
||||
predicate parameterValueFlowReturn(
|
||||
ParameterNode p, Node ret, ReturnKindExt kind, ContentOption contentIn,
|
||||
ContentOption contentOut
|
||||
private predicate parameterValueFlowReturn(
|
||||
ParameterNode p, ReturnKind kind, ReadStepTypesOption read
|
||||
) {
|
||||
ret =
|
||||
any(ReturnNode n |
|
||||
parameterValueFlow(p, n, contentIn, contentOut) and
|
||||
kind = TValueReturn(n.getKind())
|
||||
)
|
||||
or
|
||||
ret =
|
||||
any(PostUpdateNode n |
|
||||
exists(ParameterNode p2, int pos2 |
|
||||
parameterValueFlowsToPostUpdate(p, n, contentIn, contentOut) and
|
||||
parameterValueFlowsToPreUpdate(p2, n) and
|
||||
p2.isParameterOf(_, pos2) and
|
||||
kind = TParamUpdate(pos2) and
|
||||
p != p2
|
||||
)
|
||||
)
|
||||
exists(ReturnNode ret |
|
||||
parameterValueFlow(p, ret, read) and
|
||||
kind = ret.getKind()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
import Final
|
||||
}
|
||||
|
||||
import FlowThrough
|
||||
|
||||
cached
|
||||
private module DispatchWithCallContext {
|
||||
/**
|
||||
* Holds if the call context `ctx` reduces the set of viable run-time
|
||||
* dispatch targets of call `call` in `c`.
|
||||
*/
|
||||
cached
|
||||
predicate reducedViableImplInCallContext(DataFlowCall call, DataFlowCallable c, DataFlowCall ctx) {
|
||||
exists(int tgts, int ctxtgts |
|
||||
mayBenefitFromCallContext(call, c) and
|
||||
c = viableCallable(ctx) and
|
||||
ctxtgts = count(viableImplInCallContext(call, ctx)) and
|
||||
tgts = strictcount(viableCallable(call)) and
|
||||
ctxtgts < tgts
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a viable run-time dispatch target for the call `call` in the
|
||||
* context `ctx`. This is restricted to those calls for which a context
|
||||
* makes a difference.
|
||||
*/
|
||||
cached
|
||||
DataFlowCallable prunedViableImplInCallContext(DataFlowCall call, DataFlowCall ctx) {
|
||||
result = viableImplInCallContext(call, ctx) and
|
||||
reducedViableImplInCallContext(call, _, ctx)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if flow returning from callable `c` to call `call` might return
|
||||
* further and if this path restricts the set of call sites that can be
|
||||
* returned to.
|
||||
*/
|
||||
cached
|
||||
predicate reducedViableImplInReturn(DataFlowCallable c, DataFlowCall call) {
|
||||
exists(int tgts, int ctxtgts |
|
||||
mayBenefitFromCallContext(call, _) and
|
||||
c = viableCallable(call) and
|
||||
ctxtgts = count(DataFlowCall ctx | c = viableImplInCallContext(call, ctx)) and
|
||||
tgts = strictcount(DataFlowCall ctx | viableCallable(ctx) = call.getEnclosingCallable()) and
|
||||
ctxtgts < tgts
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a viable run-time dispatch target for the call `call` in the
|
||||
* context `ctx`. This is restricted to those calls and results for which
|
||||
* the return flow from the result to `call` restricts the possible context
|
||||
* `ctx`.
|
||||
*/
|
||||
cached
|
||||
DataFlowCallable prunedViableImplInCallContextReverse(DataFlowCall call, DataFlowCall ctx) {
|
||||
result = viableImplInCallContext(call, ctx) and
|
||||
reducedViableImplInReturn(result, call)
|
||||
}
|
||||
}
|
||||
|
||||
import DispatchWithCallContext
|
||||
|
||||
/**
|
||||
* 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) {
|
||||
parameterValueFlow(p, n.getPreUpdateNode(), TReadStepTypesNone())
|
||||
}
|
||||
|
||||
private predicate store(
|
||||
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)
|
||||
or
|
||||
exists(Node n1, Node n2 |
|
||||
n1 = node1.(PostUpdateNode).getPreUpdateNode() and
|
||||
n2 = node2.(PostUpdateNode).getPreUpdateNode()
|
||||
|
|
||||
argumentValueFlowsThrough(n2, TReadStepTypesSome(containerType, c, contentType), n1)
|
||||
or
|
||||
readStep(n2, c, n1) and
|
||||
contentType = getNodeType(n1) and
|
||||
containerType = getNodeType(n2)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if data can flow from `node1` to `node2` via a direct assignment to
|
||||
* `f`.
|
||||
@@ -469,21 +387,10 @@ private module Cached {
|
||||
* been stored into, in order to handle cases like `x.f1.f2 = y`.
|
||||
*/
|
||||
cached
|
||||
predicate storeDirect(Node node1, Content f, Node node2) {
|
||||
storeStep(node1, f, node2) and readStep(_, f, _)
|
||||
or
|
||||
exists(Node n1, Node n2 |
|
||||
n1 = node1.(PostUpdateNode).getPreUpdateNode() and
|
||||
n2 = node2.(PostUpdateNode).getPreUpdateNode()
|
||||
|
|
||||
argumentValueFlowsThrough(_, n2, TContentSome(f), TContentNone(), n1)
|
||||
or
|
||||
readStep(n2, f, n1)
|
||||
)
|
||||
predicate store(Node node1, TypedContent tc, Node node2, DataFlowType contentType) {
|
||||
store(node1, tc.getContent(), node2, contentType, tc.getContainerType())
|
||||
}
|
||||
|
||||
import FlowThrough
|
||||
|
||||
/**
|
||||
* Holds if the call context `call` either improves virtual dispatch in
|
||||
* `callable` or if it allows us to prune unreachable nodes in `callable`.
|
||||
@@ -520,6 +427,24 @@ private module Cached {
|
||||
newtype TReturnKindExt =
|
||||
TValueReturn(ReturnKind kind) or
|
||||
TParamUpdate(int pos) { exists(ParameterNode p | p.isParameterOf(_, pos)) }
|
||||
|
||||
cached
|
||||
newtype TBooleanOption =
|
||||
TBooleanNone() or
|
||||
TBooleanSome(boolean b) { b = true or b = false }
|
||||
|
||||
cached
|
||||
newtype TTypedContent = MkTypedContent(Content c, DataFlowType t) { store(_, c, _, _, t) }
|
||||
|
||||
cached
|
||||
newtype TAccessPathFront =
|
||||
TFrontNil(DataFlowType t) or
|
||||
TFrontHead(TypedContent tc)
|
||||
|
||||
cached
|
||||
newtype TAccessPathFrontOption =
|
||||
TAccessPathFrontNone() or
|
||||
TAccessPathFrontSome(AccessPathFront apf)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -529,26 +454,38 @@ class CastingNode extends Node {
|
||||
CastingNode() {
|
||||
this instanceof ParameterNode or
|
||||
this instanceof CastNode or
|
||||
this instanceof OutNode or
|
||||
this.(PostUpdateNode).getPreUpdateNode() instanceof ArgumentNode
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
||||
newtype TContentOption =
|
||||
TContentNone() or
|
||||
TContentSome(Content f)
|
||||
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)
|
||||
}
|
||||
|
||||
class ContentOption extends TContentOption {
|
||||
Content getContent() { this = TContentSome(result) }
|
||||
|
||||
predicate hasContent() { exists(this.getContent()) }
|
||||
|
||||
string toString() {
|
||||
result = this.getContent().toString()
|
||||
or
|
||||
not this.hasContent() and
|
||||
result = "<none>"
|
||||
private newtype TReadStepTypesOption =
|
||||
TReadStepTypesNone() or
|
||||
TReadStepTypesSome(DataFlowType container, Content c, DataFlowType content) {
|
||||
readStepWithTypes(_, container, c, _, content)
|
||||
}
|
||||
|
||||
private class ReadStepTypesOption extends TReadStepTypesOption {
|
||||
predicate isSome() { this instanceof TReadStepTypesSome }
|
||||
|
||||
DataFlowType getContainerType() { this = TReadStepTypesSome(result, _, _) }
|
||||
|
||||
Content getContent() { this = TReadStepTypesSome(_, result, _) }
|
||||
|
||||
DataFlowType getContentType() { this = TReadStepTypesSome(_, _, result) }
|
||||
|
||||
string toString() { if this.isSome() then result = "Some(..)" else result = "None()" }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -575,13 +512,19 @@ abstract class CallContext extends TCallContext {
|
||||
abstract predicate relevantFor(DataFlowCallable callable);
|
||||
}
|
||||
|
||||
class CallContextAny extends CallContext, TAnyCallContext {
|
||||
abstract class CallContextNoCall extends CallContext { }
|
||||
|
||||
class CallContextAny extends CallContextNoCall, TAnyCallContext {
|
||||
override string toString() { result = "CcAny" }
|
||||
|
||||
override predicate relevantFor(DataFlowCallable callable) { any() }
|
||||
}
|
||||
|
||||
abstract class CallContextCall extends CallContext { }
|
||||
abstract class CallContextCall extends CallContext {
|
||||
/** Holds if this call context may be `call`. */
|
||||
bindingset[call]
|
||||
abstract predicate matchesCall(DataFlowCall call);
|
||||
}
|
||||
|
||||
class CallContextSpecificCall extends CallContextCall, TSpecificCall {
|
||||
override string toString() {
|
||||
@@ -592,6 +535,8 @@ class CallContextSpecificCall extends CallContextCall, TSpecificCall {
|
||||
recordDataFlowCallSite(getCall(), callable)
|
||||
}
|
||||
|
||||
override predicate matchesCall(DataFlowCall call) { call = this.getCall() }
|
||||
|
||||
DataFlowCall getCall() { this = TSpecificCall(result) }
|
||||
}
|
||||
|
||||
@@ -601,9 +546,11 @@ class CallContextSomeCall extends CallContextCall, TSomeCall {
|
||||
override predicate relevantFor(DataFlowCallable callable) {
|
||||
exists(ParameterNode p | p.getEnclosingCallable() = callable)
|
||||
}
|
||||
|
||||
override predicate matchesCall(DataFlowCall call) { any() }
|
||||
}
|
||||
|
||||
class CallContextReturn extends CallContext, TReturn {
|
||||
class CallContextReturn extends CallContextNoCall, TReturn {
|
||||
override string toString() {
|
||||
exists(DataFlowCall call | this = TReturn(_, call) | result = "CcReturn(" + call + ")")
|
||||
}
|
||||
@@ -678,6 +625,18 @@ class ReturnNodeExt extends Node {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A node to which data can flow from a call. Either an ordinary out 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
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* An extended return kind. A return kind describes how data can be returned
|
||||
* from a callable. This can either be through a returned value or an updated
|
||||
@@ -688,7 +647,7 @@ abstract class ReturnKindExt extends TReturnKindExt {
|
||||
abstract string toString();
|
||||
|
||||
/** Gets a node corresponding to data flow out of `call`. */
|
||||
abstract Node getAnOutNode(DataFlowCall call);
|
||||
abstract OutNodeExt getAnOutNode(DataFlowCall call);
|
||||
}
|
||||
|
||||
class ValueReturnKind extends ReturnKindExt, TValueReturn {
|
||||
@@ -700,7 +659,9 @@ class ValueReturnKind extends ReturnKindExt, TValueReturn {
|
||||
|
||||
override string toString() { result = kind.toString() }
|
||||
|
||||
override Node getAnOutNode(DataFlowCall call) { result = getAnOutNode(call, this.getKind()) }
|
||||
override OutNodeExt getAnOutNode(DataFlowCall call) {
|
||||
result = getAnOutNode(call, this.getKind())
|
||||
}
|
||||
}
|
||||
|
||||
class ParamUpdateReturnKind extends ReturnKindExt, TParamUpdate {
|
||||
@@ -712,9 +673,9 @@ class ParamUpdateReturnKind extends ReturnKindExt, TParamUpdate {
|
||||
|
||||
override string toString() { result = "param update " + pos }
|
||||
|
||||
override PostUpdateNode getAnOutNode(DataFlowCall call) {
|
||||
override OutNodeExt getAnOutNode(DataFlowCall call) {
|
||||
exists(ArgumentNode arg |
|
||||
result.getPreUpdateNode() = arg and
|
||||
result.(PostUpdateNode).getPreUpdateNode() = arg and
|
||||
arg.argumentOf(call, this.getPosition())
|
||||
)
|
||||
}
|
||||
@@ -779,77 +740,83 @@ DataFlowCallable resolveCall(DataFlowCall call, CallContext cc) {
|
||||
result = viableCallable(call) and cc instanceof CallContextReturn
|
||||
}
|
||||
|
||||
newtype TSummary =
|
||||
TSummaryVal() or
|
||||
TSummaryTaint() or
|
||||
TSummaryReadVal(Content f) or
|
||||
TSummaryReadTaint(Content f) or
|
||||
TSummaryTaintStore(Content f)
|
||||
predicate read = readStep/3;
|
||||
|
||||
/**
|
||||
* A summary of flow through a callable. This can either be value-preserving
|
||||
* if no additional steps are used, taint-flow if at least one additional step
|
||||
* is used, or any one of those combined with a store or a read. Summaries
|
||||
* recorded at a return node are restricted to include at least one additional
|
||||
* step, as the value-based summaries are calculated independent of the
|
||||
* configuration.
|
||||
*/
|
||||
class Summary extends TSummary {
|
||||
/** An optional Boolean value. */
|
||||
class BooleanOption extends TBooleanOption {
|
||||
string toString() {
|
||||
result = "Val" and this = TSummaryVal()
|
||||
this = TBooleanNone() and result = "<none>"
|
||||
or
|
||||
result = "Taint" and this = TSummaryTaint()
|
||||
or
|
||||
exists(Content f |
|
||||
result = "ReadVal " + f.toString() and this = TSummaryReadVal(f)
|
||||
or
|
||||
result = "ReadTaint " + f.toString() and this = TSummaryReadTaint(f)
|
||||
or
|
||||
result = "TaintStore " + f.toString() and this = TSummaryTaintStore(f)
|
||||
)
|
||||
}
|
||||
|
||||
/** Gets the summary that results from extending this with an additional step. */
|
||||
Summary additionalStep() {
|
||||
this = TSummaryVal() and result = TSummaryTaint()
|
||||
or
|
||||
this = TSummaryTaint() and result = TSummaryTaint()
|
||||
or
|
||||
exists(Content f | this = TSummaryReadVal(f) and result = TSummaryReadTaint(f))
|
||||
or
|
||||
exists(Content f | this = TSummaryReadTaint(f) and result = TSummaryReadTaint(f))
|
||||
}
|
||||
|
||||
/** Gets the summary that results from extending this with a read. */
|
||||
Summary readStep(Content f) { this = TSummaryVal() and result = TSummaryReadVal(f) }
|
||||
|
||||
/** Gets the summary that results from extending this with a store. */
|
||||
Summary storeStep(Content f) { this = TSummaryTaint() and result = TSummaryTaintStore(f) }
|
||||
|
||||
/** Gets the summary that results from extending this with `step`. */
|
||||
bindingset[this, step]
|
||||
Summary compose(Summary step) {
|
||||
this = TSummaryVal() and result = step
|
||||
or
|
||||
this = TSummaryTaint() and
|
||||
(step = TSummaryTaint() or step = TSummaryTaintStore(_)) and
|
||||
result = step
|
||||
or
|
||||
exists(Content f |
|
||||
this = TSummaryReadVal(f) and step = TSummaryTaint() and result = TSummaryReadTaint(f)
|
||||
)
|
||||
or
|
||||
this = TSummaryReadTaint(_) and step = TSummaryTaint() and result = this
|
||||
}
|
||||
|
||||
/** Holds if this summary does not include any taint steps. */
|
||||
predicate isPartial() {
|
||||
this = TSummaryVal() or
|
||||
this = TSummaryReadVal(_)
|
||||
this = TBooleanSome(any(boolean b | result = b.toString()))
|
||||
}
|
||||
}
|
||||
|
||||
pragma[noinline]
|
||||
DataFlowType getErasedNodeTypeBound(Node n) { result = getErasedRepr(n.getTypeBound()) }
|
||||
/** Content tagged with the type of a containing object. */
|
||||
class TypedContent extends MkTypedContent {
|
||||
private Content c;
|
||||
private DataFlowType t;
|
||||
|
||||
predicate readDirect = readStep/3;
|
||||
TypedContent() { this = MkTypedContent(c, t) }
|
||||
|
||||
/** Gets the content. */
|
||||
Content getContent() { result = c }
|
||||
|
||||
/** Gets the container type. */
|
||||
DataFlowType getContainerType() { result = t }
|
||||
|
||||
/** Gets a textual representation of this content. */
|
||||
string toString() { result = c.toString() }
|
||||
}
|
||||
|
||||
/**
|
||||
* The front of an access path. This is either a head or a nil.
|
||||
*/
|
||||
abstract class AccessPathFront extends TAccessPathFront {
|
||||
abstract string toString();
|
||||
|
||||
abstract DataFlowType getType();
|
||||
|
||||
abstract boolean toBoolNonEmpty();
|
||||
|
||||
predicate headUsesContent(TypedContent tc) { this = TFrontHead(tc) }
|
||||
|
||||
predicate isClearedAt(Node n) {
|
||||
exists(TypedContent tc |
|
||||
this.headUsesContent(tc) and
|
||||
clearsContent(n, tc.getContent())
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
class AccessPathFrontNil extends AccessPathFront, TFrontNil {
|
||||
private DataFlowType t;
|
||||
|
||||
AccessPathFrontNil() { this = TFrontNil(t) }
|
||||
|
||||
override string toString() { result = ppReprType(t) }
|
||||
|
||||
override DataFlowType getType() { result = t }
|
||||
|
||||
override boolean toBoolNonEmpty() { result = false }
|
||||
}
|
||||
|
||||
class AccessPathFrontHead extends AccessPathFront, TFrontHead {
|
||||
private TypedContent tc;
|
||||
|
||||
AccessPathFrontHead() { this = TFrontHead(tc) }
|
||||
|
||||
override string toString() { result = tc.toString() }
|
||||
|
||||
override DataFlowType getType() { result = tc.getContainerType() }
|
||||
|
||||
override boolean toBoolNonEmpty() { result = true }
|
||||
}
|
||||
|
||||
/** An optional access path front. */
|
||||
class AccessPathFrontOption extends TAccessPathFrontOption {
|
||||
string toString() {
|
||||
this = TAccessPathFrontNone() and result = "<none>"
|
||||
or
|
||||
this = TAccessPathFrontSome(any(AccessPathFront apf | result = apf.toString()))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -37,21 +37,12 @@ module Consistency {
|
||||
)
|
||||
}
|
||||
|
||||
query predicate uniqueTypeBound(Node n, string msg) {
|
||||
query predicate uniqueType(Node n, string msg) {
|
||||
exists(int c |
|
||||
n instanceof RelevantNode and
|
||||
c = count(n.getTypeBound()) and
|
||||
c = count(getNodeType(n)) and
|
||||
c != 1 and
|
||||
msg = "Node should have one type bound but has " + c + "."
|
||||
)
|
||||
}
|
||||
|
||||
query predicate uniqueTypeRepr(Node n, string msg) {
|
||||
exists(int c |
|
||||
n instanceof RelevantNode and
|
||||
c = count(getErasedRepr(n.getTypeBound())) and
|
||||
c != 1 and
|
||||
msg = "Node should have one type representation but has " + c + "."
|
||||
msg = "Node should have one type but has " + c + "."
|
||||
)
|
||||
}
|
||||
|
||||
@@ -104,7 +95,7 @@ module Consistency {
|
||||
msg = "Local flow step does not preserve enclosing callable."
|
||||
}
|
||||
|
||||
private DataFlowType typeRepr() { result = getErasedRepr(any(Node n).getTypeBound()) }
|
||||
private DataFlowType typeRepr() { result = getNodeType(_) }
|
||||
|
||||
query predicate compatibleTypesReflexive(DataFlowType t, string msg) {
|
||||
t = typeRepr() and
|
||||
|
||||
@@ -129,15 +129,6 @@ private predicate instanceFieldAssign(Expr src, FieldAccess fa) {
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets an upper bound on the type of `f`.
|
||||
*/
|
||||
private Type getFieldTypeBound(Field f) {
|
||||
fieldTypeFlow(f, result, _)
|
||||
or
|
||||
not fieldTypeFlow(f, _, _) and result = f.getType()
|
||||
}
|
||||
|
||||
private newtype TContent =
|
||||
TFieldContent(InstanceField f) or
|
||||
TCollectionContent() or
|
||||
@@ -154,12 +145,6 @@ class Content extends TContent {
|
||||
predicate hasLocationInfo(string path, int sl, int sc, int el, int ec) {
|
||||
path = "" and sl = 0 and sc = 0 and el = 0 and ec = 0
|
||||
}
|
||||
|
||||
/** Gets the erased type of the object containing this content. */
|
||||
abstract DataFlowType getContainerType();
|
||||
|
||||
/** Gets the erased type of this content. */
|
||||
abstract DataFlowType getType();
|
||||
}
|
||||
|
||||
private class FieldContent extends Content, TFieldContent {
|
||||
@@ -174,26 +159,14 @@ private class FieldContent extends Content, TFieldContent {
|
||||
override predicate hasLocationInfo(string path, int sl, int sc, int el, int ec) {
|
||||
f.getLocation().hasLocationInfo(path, sl, sc, el, ec)
|
||||
}
|
||||
|
||||
override DataFlowType getContainerType() { result = getErasedRepr(f.getDeclaringType()) }
|
||||
|
||||
override DataFlowType getType() { result = getErasedRepr(getFieldTypeBound(f)) }
|
||||
}
|
||||
|
||||
private class CollectionContent extends Content, TCollectionContent {
|
||||
override string toString() { result = "collection" }
|
||||
|
||||
override DataFlowType getContainerType() { none() }
|
||||
|
||||
override DataFlowType getType() { none() }
|
||||
}
|
||||
|
||||
private class ArrayContent extends Content, TArrayContent {
|
||||
override string toString() { result = "array" }
|
||||
|
||||
override DataFlowType getContainerType() { none() }
|
||||
|
||||
override DataFlowType getType() { none() }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -222,12 +195,21 @@ predicate readStep(Node node1, Content f, Node node2) {
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if values stored inside content `c` are cleared at node `n`. For example,
|
||||
* any value stored inside `f` is cleared at the pre-update node associated with `x`
|
||||
* in `x.f = newValue`.
|
||||
*/
|
||||
predicate clearsContent(Node n, Content c) {
|
||||
n = any(PostUpdateNode pun | storeStep(_, c, pun)).getPreUpdateNode()
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a representative (boxed) type for `t` for the purpose of pruning
|
||||
* possible flow. A single type is used for all numeric types to account for
|
||||
* numeric conversions, and otherwise the erasure is used.
|
||||
*/
|
||||
DataFlowType getErasedRepr(Type t) {
|
||||
private DataFlowType getErasedRepr(Type t) {
|
||||
exists(Type e | e = t.getErasure() |
|
||||
if e instanceof NumericOrCharType
|
||||
then result.(BoxedType).getPrimitiveType().getName() = "double"
|
||||
@@ -240,6 +222,9 @@ DataFlowType getErasedRepr(Type t) {
|
||||
t instanceof NullType and result instanceof TypeObject
|
||||
}
|
||||
|
||||
pragma[noinline]
|
||||
DataFlowType getNodeType(Node n) { result = getErasedRepr(n.getTypeBound()) }
|
||||
|
||||
/** Gets a string representation of a type returned by `getErasedRepr`. */
|
||||
string ppReprType(Type t) {
|
||||
if t.(BoxedType).getPrimitiveType().getName() = "double"
|
||||
@@ -338,3 +323,6 @@ int accessPathLimit() { result = 5 }
|
||||
predicate isImmutableOrUnobservable(Node n) {
|
||||
n.getType() instanceof ImmutableType or n instanceof ImplicitVarargsArray
|
||||
}
|
||||
|
||||
/** Holds if `n` should be hidden from path explanations. */
|
||||
predicate nodeIsHidden(Node n) { none() }
|
||||
|
||||
@@ -78,15 +78,19 @@ class Node extends TNode {
|
||||
result = this.(ImplicitPostUpdateNode).getPreUpdateNode().getType()
|
||||
}
|
||||
|
||||
/** Gets the callable in which this node occurs. */
|
||||
Callable getEnclosingCallable() {
|
||||
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().getEnclosingCallable()
|
||||
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() {
|
||||
@@ -397,11 +401,34 @@ predicate simpleLocalFlowStep(Node node1, Node node2) {
|
||||
or
|
||||
node2.asExpr().(CastExpr).getExpr() = node1.asExpr()
|
||||
or
|
||||
node2.asExpr().(ConditionalExpr).getTrueExpr() = node1.asExpr()
|
||||
or
|
||||
node2.asExpr().(ConditionalExpr).getFalseExpr() = node1.asExpr()
|
||||
node2.asExpr().(ChooseExpr).getAResultExpr() = node1.asExpr()
|
||||
or
|
||||
node2.asExpr().(AssignExpr).getSource() = node1.asExpr()
|
||||
or
|
||||
exists(MethodAccess ma, Method m |
|
||||
ma = node2.asExpr() and
|
||||
m = ma.getMethod() and
|
||||
m.getDeclaringType().hasQualifiedName("java.util", "Objects") and
|
||||
(
|
||||
m.hasName(["requireNonNull", "requireNonNullElseGet"]) and node1.asExpr() = ma.getArgument(0)
|
||||
or
|
||||
m.hasName("requireNonNullElse") and node1.asExpr() = ma.getAnArgument()
|
||||
or
|
||||
m.hasName("toString") and node1.asExpr() = ma.getArgument(1)
|
||||
)
|
||||
)
|
||||
or
|
||||
exists(MethodAccess ma, Method m |
|
||||
ma = node2.asExpr() and
|
||||
m = ma.getMethod() and
|
||||
m
|
||||
.getDeclaringType()
|
||||
.getSourceDeclaration()
|
||||
.getASourceSupertype*()
|
||||
.hasQualifiedName("java.util", "Stack") and
|
||||
m.hasName("push") and
|
||||
node1.asExpr() = ma.getArgument(0)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -8,6 +8,8 @@ private import semmle.code.java.security.Validation
|
||||
private import semmle.code.java.frameworks.android.Intent
|
||||
private import semmle.code.java.frameworks.Guice
|
||||
private import semmle.code.java.frameworks.Protobuf
|
||||
private import semmle.code.java.frameworks.spring.SpringController
|
||||
private import semmle.code.java.frameworks.spring.SpringHttp
|
||||
private import semmle.code.java.Maps
|
||||
private import semmle.code.java.dataflow.internal.ContainerFlow
|
||||
private import semmle.code.java.frameworks.jackson.JacksonSerializability
|
||||
@@ -252,6 +254,22 @@ private predicate constructorStep(Expr tracked, ConstructorCall sink) {
|
||||
or
|
||||
// a custom InputStream that wraps a tainted data source is tainted
|
||||
inputStreamWrapper(sink.getConstructor(), argi)
|
||||
or
|
||||
// A SpringHttpEntity is a wrapper around a body and some headers
|
||||
// Track flow through iff body is a String
|
||||
exists(SpringHttpEntity she |
|
||||
sink.getConstructor() = she.getAConstructor() and
|
||||
argi = 0 and
|
||||
tracked.getType() instanceof TypeString
|
||||
)
|
||||
or
|
||||
// A SpringRequestEntity is a wrapper around a body and some headers
|
||||
// Track flow through iff body is a String
|
||||
exists(SpringResponseEntity sre |
|
||||
sink.getConstructor() = sre.getAConstructor() and
|
||||
argi = 0 and
|
||||
tracked.getType() instanceof TypeString
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -292,10 +310,13 @@ private predicate qualifierToMethodStep(Expr tracked, MethodAccess sink) {
|
||||
* Methods that return tainted data when called on tainted data.
|
||||
*/
|
||||
private predicate taintPreservingQualifierToMethod(Method m) {
|
||||
m instanceof CloneMethod
|
||||
or
|
||||
m.getDeclaringType() instanceof TypeString and
|
||||
(
|
||||
m.getName() = "concat" or
|
||||
m.getName() = "endsWith" or
|
||||
m.getName() = "formatted" or
|
||||
m.getName() = "getBytes" or
|
||||
m.getName() = "split" or
|
||||
m.getName() = "substring" or
|
||||
@@ -321,7 +342,11 @@ private predicate taintPreservingQualifierToMethod(Method m) {
|
||||
)
|
||||
or
|
||||
m.getDeclaringType().getQualifiedName().matches("%StringWriter") and
|
||||
m.getName() = "toString"
|
||||
(
|
||||
m.getName() = "getBuffer"
|
||||
or
|
||||
m.getName() = "toString"
|
||||
)
|
||||
or
|
||||
m.getDeclaringType().hasQualifiedName("java.util", "StringTokenizer") and
|
||||
m.getName().matches("next%")
|
||||
@@ -334,7 +359,8 @@ private predicate taintPreservingQualifierToMethod(Method m) {
|
||||
or
|
||||
(
|
||||
m.getDeclaringType().hasQualifiedName("java.lang", "StringBuilder") or
|
||||
m.getDeclaringType().hasQualifiedName("java.lang", "StringBuffer")
|
||||
m.getDeclaringType().hasQualifiedName("java.lang", "StringBuffer") or
|
||||
m.getDeclaringType().hasQualifiedName("java.io", "StringWriter")
|
||||
) and
|
||||
(m.getName() = "toString" or m.getName() = "append")
|
||||
or
|
||||
@@ -352,6 +378,21 @@ private predicate taintPreservingQualifierToMethod(Method m) {
|
||||
m = any(GuiceProvider gp).getAnOverridingGetMethod()
|
||||
or
|
||||
m = any(ProtobufMessageLite p).getAGetterMethod()
|
||||
or
|
||||
m instanceof GetterMethod and m.getDeclaringType() instanceof SpringUntrustedDataType
|
||||
or
|
||||
m.getDeclaringType() instanceof SpringHttpEntity and
|
||||
m.getName().regexpMatch("getBody|getHeaders")
|
||||
or
|
||||
exists(SpringHttpHeaders headers | m = headers.getAMethod() |
|
||||
m.getReturnType() instanceof TypeString
|
||||
or
|
||||
exists(ParameterizedType stringlist |
|
||||
m.getReturnType().(RefType).getASupertype*() = stringlist and
|
||||
stringlist.getSourceDeclaration().hasQualifiedName("java.util", "List") and
|
||||
stringlist.getTypeArgument(0) instanceof TypeString
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
private class StringReplaceMethod extends Method {
|
||||
@@ -377,9 +418,9 @@ private predicate unsafeEscape(MethodAccess ma) {
|
||||
/** Access to a method that passes taint from an argument. */
|
||||
private predicate argToMethodStep(Expr tracked, MethodAccess sink) {
|
||||
exists(Method m, int i |
|
||||
m = sink.(MethodAccess).getMethod() and
|
||||
m = sink.getMethod() and
|
||||
taintPreservingArgumentToMethod(m, i) and
|
||||
tracked = sink.(MethodAccess).getArgument(i)
|
||||
tracked = sink.getArgument(i)
|
||||
)
|
||||
or
|
||||
exists(MethodAccess ma |
|
||||
@@ -387,6 +428,22 @@ private predicate argToMethodStep(Expr tracked, MethodAccess sink) {
|
||||
tracked = ma.getAnArgument() and
|
||||
sink = ma
|
||||
)
|
||||
or
|
||||
exists(Method springResponseEntityOfOk |
|
||||
sink.getMethod() = springResponseEntityOfOk and
|
||||
springResponseEntityOfOk.getDeclaringType() instanceof SpringResponseEntity and
|
||||
springResponseEntityOfOk.getName().regexpMatch("ok|of") and
|
||||
tracked = sink.getArgument(0) and
|
||||
tracked.getType() instanceof TypeString
|
||||
)
|
||||
or
|
||||
exists(Method springResponseEntityBody |
|
||||
sink.getMethod() = springResponseEntityBody and
|
||||
springResponseEntityBody.getDeclaringType() instanceof SpringResponseEntityBodyBuilder and
|
||||
springResponseEntityBody.getName().regexpMatch("body") and
|
||||
tracked = sink.getArgument(0) and
|
||||
tracked.getType() instanceof TypeString
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -395,7 +452,7 @@ private predicate argToMethodStep(Expr tracked, MethodAccess sink) {
|
||||
*/
|
||||
private predicate taintPreservingArgumentToMethod(Method method) {
|
||||
method.getDeclaringType() instanceof TypeString and
|
||||
(method.hasName("format") or method.hasName("join"))
|
||||
(method.hasName("format") or method.hasName("formatted") or method.hasName("join"))
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -434,7 +491,15 @@ private predicate taintPreservingArgumentToMethod(Method method, int arg) {
|
||||
or
|
||||
(
|
||||
method.getDeclaringType().hasQualifiedName("java.util", "Base64$Encoder") or
|
||||
method.getDeclaringType().hasQualifiedName("java.util", "Base64$Decoder")
|
||||
method.getDeclaringType().hasQualifiedName("java.util", "Base64$Decoder") or
|
||||
method
|
||||
.getDeclaringType()
|
||||
.getASupertype*()
|
||||
.hasQualifiedName("org.apache.commons.codec", "Encoder") or
|
||||
method
|
||||
.getDeclaringType()
|
||||
.getASupertype*()
|
||||
.hasQualifiedName("org.apache.commons.codec", "Decoder")
|
||||
) and
|
||||
(
|
||||
method.getName() = "encode" and arg = 0 and method.getNumberOfParameters() = 1
|
||||
@@ -497,6 +562,10 @@ private predicate taintPreservingArgumentToMethod(Method method, int arg) {
|
||||
method instanceof JacksonWriteValueMethod and
|
||||
method.getNumberOfParameters() = 1 and
|
||||
arg = 0
|
||||
or
|
||||
method.getDeclaringType().hasQualifiedName("java.io", "StringWriter") and
|
||||
method.hasName("append") and
|
||||
arg = 0
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -571,9 +640,20 @@ private predicate argToQualifierStep(Expr tracked, Expr sink) {
|
||||
private predicate taintPreservingArgumentToQualifier(Method method, int arg) {
|
||||
exists(Method write |
|
||||
method.overrides*(write) and
|
||||
write.getDeclaringType().hasQualifiedName("java.io", "OutputStream") and
|
||||
write.hasName("write") and
|
||||
arg = 0
|
||||
arg = 0 and
|
||||
(
|
||||
write.getDeclaringType().hasQualifiedName("java.io", "OutputStream")
|
||||
or
|
||||
write.getDeclaringType().hasQualifiedName("java.io", "StringWriter")
|
||||
)
|
||||
)
|
||||
or
|
||||
exists(Method append |
|
||||
method.overrides*(append) and
|
||||
append.hasName("append") and
|
||||
arg = 0 and
|
||||
append.getDeclaringType().hasQualifiedName("java.io", "StringWriter")
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -26,7 +26,7 @@ private import TaintTrackingParameter::Private
|
||||
* To create a configuration, extend this class with a subclass whose
|
||||
* characteristic predicate is a unique singleton string. For example, write
|
||||
*
|
||||
* ```
|
||||
* ```ql
|
||||
* class MyAnalysisConfiguration extends TaintTracking::Configuration {
|
||||
* MyAnalysisConfiguration() { this = "MyAnalysisConfiguration" }
|
||||
* // Override `isSource` and `isSink`.
|
||||
@@ -41,7 +41,7 @@ private import TaintTrackingParameter::Private
|
||||
* Then, to query whether there is flow between some `source` and `sink`,
|
||||
* write
|
||||
*
|
||||
* ```
|
||||
* ```ql
|
||||
* exists(MyAnalysisConfiguration cfg | cfg.hasFlow(source, sink))
|
||||
* ```
|
||||
*
|
||||
@@ -79,13 +79,6 @@ abstract class Configuration extends DataFlow::Configuration {
|
||||
defaultTaintBarrier(node)
|
||||
}
|
||||
|
||||
/** DEPRECATED: override `isSanitizerIn` and `isSanitizerOut` instead. */
|
||||
deprecated predicate isSanitizerEdge(DataFlow::Node node1, DataFlow::Node node2) { none() }
|
||||
|
||||
deprecated final override predicate isBarrierEdge(DataFlow::Node node1, DataFlow::Node node2) {
|
||||
isSanitizerEdge(node1, node2)
|
||||
}
|
||||
|
||||
/** Holds if data flow into `node` is prohibited. */
|
||||
predicate isSanitizerIn(DataFlow::Node node) { none() }
|
||||
|
||||
|
||||
@@ -26,7 +26,7 @@ private import TaintTrackingParameter::Private
|
||||
* To create a configuration, extend this class with a subclass whose
|
||||
* characteristic predicate is a unique singleton string. For example, write
|
||||
*
|
||||
* ```
|
||||
* ```ql
|
||||
* class MyAnalysisConfiguration extends TaintTracking::Configuration {
|
||||
* MyAnalysisConfiguration() { this = "MyAnalysisConfiguration" }
|
||||
* // Override `isSource` and `isSink`.
|
||||
@@ -41,7 +41,7 @@ private import TaintTrackingParameter::Private
|
||||
* Then, to query whether there is flow between some `source` and `sink`,
|
||||
* write
|
||||
*
|
||||
* ```
|
||||
* ```ql
|
||||
* exists(MyAnalysisConfiguration cfg | cfg.hasFlow(source, sink))
|
||||
* ```
|
||||
*
|
||||
@@ -79,13 +79,6 @@ abstract class Configuration extends DataFlow::Configuration {
|
||||
defaultTaintBarrier(node)
|
||||
}
|
||||
|
||||
/** DEPRECATED: override `isSanitizerIn` and `isSanitizerOut` instead. */
|
||||
deprecated predicate isSanitizerEdge(DataFlow::Node node1, DataFlow::Node node2) { none() }
|
||||
|
||||
deprecated final override predicate isBarrierEdge(DataFlow::Node node1, DataFlow::Node node2) {
|
||||
isSanitizerEdge(node1, node2)
|
||||
}
|
||||
|
||||
/** Holds if data flow into `node` is prohibited. */
|
||||
predicate isSanitizerIn(DataFlow::Node node) { none() }
|
||||
|
||||
|
||||
@@ -1,3 +1,11 @@
|
||||
/**
|
||||
* INTERNAL: This is part of the virtual dispatch computation.
|
||||
*
|
||||
* Provides a strengthening of the virtual dispatch relation using a dedicated
|
||||
* data flow check for lambdas, anonymous classes, and other sufficiently
|
||||
* private classes where all object instantiations are accounted for.
|
||||
*/
|
||||
|
||||
import java
|
||||
private import VirtualDispatch
|
||||
private import semmle.code.java.dataflow.internal.BaseSSA
|
||||
@@ -194,9 +202,7 @@ private predicate flowStep(RelevantNode n1, RelevantNode n2) {
|
||||
or
|
||||
n2.asExpr().(CastExpr).getExpr() = n1.asExpr()
|
||||
or
|
||||
n2.asExpr().(ConditionalExpr).getTrueExpr() = n1.asExpr()
|
||||
or
|
||||
n2.asExpr().(ConditionalExpr).getFalseExpr() = n1.asExpr()
|
||||
n2.asExpr().(ChooseExpr).getAResultExpr() = n1.asExpr()
|
||||
or
|
||||
n2.asExpr().(AssignExpr).getSource() = n1.asExpr()
|
||||
or
|
||||
|
||||
@@ -100,9 +100,7 @@ private predicate step(Node n1, Node n2) {
|
||||
or
|
||||
n2.asExpr().(CastExpr).getExpr() = n1.asExpr()
|
||||
or
|
||||
n2.asExpr().(ConditionalExpr).getTrueExpr() = n1.asExpr()
|
||||
or
|
||||
n2.asExpr().(ConditionalExpr).getFalseExpr() = n1.asExpr()
|
||||
n2.asExpr().(ChooseExpr).getAResultExpr() = n1.asExpr()
|
||||
or
|
||||
n2.asExpr().(AssignExpr).getSource() = n1.asExpr()
|
||||
or
|
||||
|
||||
@@ -1,3 +1,8 @@
|
||||
/**
|
||||
* Provides predicates for reasoning about runtime call targets through virtual
|
||||
* dispatch.
|
||||
*/
|
||||
|
||||
import java
|
||||
import semmle.code.java.dataflow.TypeFlow
|
||||
private import DispatchFlow as DispatchFlow
|
||||
@@ -27,6 +32,7 @@ Callable exactCallable(Call c) {
|
||||
|
||||
private predicate implCount(MethodAccess m, int c) { strictcount(viableImpl(m)) = c }
|
||||
|
||||
/** Gets a viable implementation of the target of the given `Call`. */
|
||||
Callable viableCallable(Call c) {
|
||||
result = viableImpl(c)
|
||||
or
|
||||
|
||||
@@ -1,3 +1,8 @@
|
||||
/**
|
||||
* Provides classes and predicates for reasoning about calls that may invoke one
|
||||
* of their arguments.
|
||||
*/
|
||||
|
||||
import java
|
||||
import VirtualDispatch
|
||||
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
|
||||
import java
|
||||
|
||||
newtype AssertKind =
|
||||
private newtype AssertKind =
|
||||
AssertKindTrue() or
|
||||
AssertKindFalse() or
|
||||
AssertKindNotNull() or
|
||||
@@ -50,6 +50,7 @@ private predicate assertionMethod(Method m, AssertKind kind) {
|
||||
)
|
||||
}
|
||||
|
||||
/** An assertion method. */
|
||||
class AssertionMethod extends Method {
|
||||
AssertionMethod() { assertionMethod(this, _) }
|
||||
|
||||
|
||||
@@ -1,31 +1,50 @@
|
||||
/**
|
||||
* Provides classes and predicates for working with annotations in the `javax` package.
|
||||
*/
|
||||
|
||||
import java
|
||||
|
||||
/*
|
||||
* javax.annotation annotations
|
||||
* Annotations in the package `javax.annotation`.
|
||||
*/
|
||||
|
||||
/**
|
||||
* A `@javax.annotation.Generated` annotation.
|
||||
*/
|
||||
class GeneratedAnnotation extends Annotation {
|
||||
GeneratedAnnotation() { this.getType().hasQualifiedName("javax.annotation", "Generated") }
|
||||
}
|
||||
|
||||
/**
|
||||
* A `@javax.annotation.PostConstruct` annotation.
|
||||
*/
|
||||
class PostConstructAnnotation extends Annotation {
|
||||
PostConstructAnnotation() { this.getType().hasQualifiedName("javax.annotation", "PostConstruct") }
|
||||
}
|
||||
|
||||
/**
|
||||
* A `@javax.annotation.PreDestroy` annotation.
|
||||
*/
|
||||
class PreDestroyAnnotation extends Annotation {
|
||||
PreDestroyAnnotation() { this.getType().hasQualifiedName("javax.annotation", "PreDestroy") }
|
||||
}
|
||||
|
||||
/**
|
||||
* A `@javax.annotation.Resource` annotation.
|
||||
*/
|
||||
class ResourceAnnotation extends Annotation {
|
||||
ResourceAnnotation() { this.getType().hasQualifiedName("javax.annotation", "Resource") }
|
||||
}
|
||||
|
||||
/**
|
||||
* A `@javax.annotation.Resources` annotation.
|
||||
*/
|
||||
class ResourcesAnnotation extends Annotation {
|
||||
ResourcesAnnotation() { this.getType().hasQualifiedName("javax.annotation", "Resources") }
|
||||
}
|
||||
|
||||
/**
|
||||
* A javax.annotation.ManagedBean annotation.
|
||||
* A `@javax.annotation.ManagedBean` annotation.
|
||||
*/
|
||||
class JavaxManagedBeanAnnotation extends Annotation {
|
||||
JavaxManagedBeanAnnotation() {
|
||||
@@ -34,71 +53,104 @@ class JavaxManagedBeanAnnotation extends Annotation {
|
||||
}
|
||||
|
||||
/*
|
||||
* javax.annotation.security annotations
|
||||
* Annotations in the package `javax.annotation.security`.
|
||||
*/
|
||||
|
||||
/**
|
||||
* A `@javax.annotation.security.DeclareRoles` annotation.
|
||||
*/
|
||||
class DeclareRolesAnnotation extends Annotation {
|
||||
DeclareRolesAnnotation() {
|
||||
this.getType().hasQualifiedName("javax.annotation.security", "DeclareRoles")
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A `@javax.annotation.security.DenyAll` annotation.
|
||||
*/
|
||||
class DenyAllAnnotation extends Annotation {
|
||||
DenyAllAnnotation() { this.getType().hasQualifiedName("javax.annotation.security", "DenyAll") }
|
||||
}
|
||||
|
||||
/**
|
||||
* A `@javax.annotation.security.PermitAll` annotation.
|
||||
*/
|
||||
class PermitAllAnnotation extends Annotation {
|
||||
PermitAllAnnotation() {
|
||||
this.getType().hasQualifiedName("javax.annotation.security", "PermitAll")
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A `@javax.annotation.security.RolesAllowed` annotation.
|
||||
*/
|
||||
class RolesAllowedAnnotation extends Annotation {
|
||||
RolesAllowedAnnotation() {
|
||||
this.getType().hasQualifiedName("javax.annotation.security", "RolesAllowed")
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A `@javax.annotation.security.RunAs` annotation.
|
||||
*/
|
||||
class RunAsAnnotation extends Annotation {
|
||||
RunAsAnnotation() { this.getType().hasQualifiedName("javax.annotation.security", "RunAs") }
|
||||
}
|
||||
|
||||
/*
|
||||
* javax.interceptor annotations
|
||||
* Annotations in the package `javax.interceptor`.
|
||||
*/
|
||||
|
||||
/**
|
||||
* A `@javax.interceptor.AroundInvoke` annotation.
|
||||
*/
|
||||
class AroundInvokeAnnotation extends Annotation {
|
||||
AroundInvokeAnnotation() { this.getType().hasQualifiedName("javax.interceptor", "AroundInvoke") }
|
||||
}
|
||||
|
||||
/**
|
||||
* A `@javax.interceptor.ExcludeClassInterceptors` annotation.
|
||||
*/
|
||||
class ExcludeClassInterceptorsAnnotation extends Annotation {
|
||||
ExcludeClassInterceptorsAnnotation() {
|
||||
this.getType().hasQualifiedName("javax.interceptor", "ExcludeClassInterceptors")
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A `@javax.interceptor.ExcludeDefaultInterceptors` annotation.
|
||||
*/
|
||||
class ExcludeDefaultInterceptorsAnnotation extends Annotation {
|
||||
ExcludeDefaultInterceptorsAnnotation() {
|
||||
this.getType().hasQualifiedName("javax.interceptor", "ExcludeDefaultInterceptors")
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A `@javax.interceptor.Interceptors` annotation.
|
||||
*/
|
||||
class InterceptorsAnnotation extends Annotation {
|
||||
InterceptorsAnnotation() { this.getType().hasQualifiedName("javax.interceptor", "Interceptors") }
|
||||
}
|
||||
|
||||
/*
|
||||
* javax.jws annotations
|
||||
* Annotations in the package `javax.jws`.
|
||||
*/
|
||||
|
||||
/**
|
||||
* A `@javax.jws.WebService` annotation.
|
||||
*/
|
||||
class WebServiceAnnotation extends Annotation {
|
||||
WebServiceAnnotation() { this.getType().hasQualifiedName("javax.jws", "WebService") }
|
||||
}
|
||||
|
||||
/*
|
||||
* javax.xml.ws annotations
|
||||
* Annotations in the package `javax.xml.ws`.
|
||||
*/
|
||||
|
||||
/**
|
||||
* A `@javax.xml.ws.WebServiceRef` annotation.
|
||||
*/
|
||||
class WebServiceRefAnnotation extends Annotation {
|
||||
WebServiceRefAnnotation() { this.getType().hasQualifiedName("javax.xml.ws", "WebServiceRef") }
|
||||
}
|
||||
|
||||
@@ -1,21 +1,25 @@
|
||||
/*
|
||||
/**
|
||||
* Definitions related to `java.net.*`.
|
||||
*/
|
||||
|
||||
import semmle.code.java.Type
|
||||
|
||||
/** The type `java.net.URLConnection`. */
|
||||
class TypeUrlConnection extends RefType {
|
||||
TypeUrlConnection() { hasQualifiedName("java.net", "URLConnection") }
|
||||
}
|
||||
|
||||
/** The type `java.net.Socket`. */
|
||||
class TypeSocket extends RefType {
|
||||
TypeSocket() { hasQualifiedName("java.net", "Socket") }
|
||||
}
|
||||
|
||||
/** The type `java.net.URL`. */
|
||||
class TypeUrl extends RefType {
|
||||
TypeUrl() { hasQualifiedName("java.net", "URL") }
|
||||
}
|
||||
|
||||
/** The method `java.net.URLConnection::getInputStream`. */
|
||||
class URLConnectionGetInputStreamMethod extends Method {
|
||||
URLConnectionGetInputStreamMethod() {
|
||||
getDeclaringType() instanceof TypeUrlConnection and
|
||||
@@ -24,6 +28,7 @@ class URLConnectionGetInputStreamMethod extends Method {
|
||||
}
|
||||
}
|
||||
|
||||
/** The method `java.net.Socket::getInputStream`. */
|
||||
class SocketGetInputStreamMethod extends Method {
|
||||
SocketGetInputStreamMethod() {
|
||||
getDeclaringType() instanceof TypeSocket and
|
||||
|
||||
@@ -1,19 +1,3 @@
|
||||
import java
|
||||
|
||||
/** A Spring framework annotation indicating remote user input from servlets. */
|
||||
class SpringServletInputAnnotation extends Annotation {
|
||||
SpringServletInputAnnotation() {
|
||||
exists(AnnotationType a |
|
||||
a = this.getType() and
|
||||
a.getPackage().getName() = "org.springframework.web.bind.annotation"
|
||||
|
|
||||
a.hasName("MatrixVariable") or
|
||||
a.hasName("RequestParam") or
|
||||
a.hasName("RequestHeader") or
|
||||
a.hasName("CookieValue") or
|
||||
a.hasName("RequestPart") or
|
||||
a.hasName("PathVariable") or
|
||||
a.hasName("RequestBody")
|
||||
)
|
||||
}
|
||||
}
|
||||
import spring.SpringController
|
||||
import spring.SpringWeb
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
/** Provides classes and predicates for working with the GWT framework. */
|
||||
|
||||
import java
|
||||
import GwtXml
|
||||
import GwtUiBinder
|
||||
|
||||
@@ -8,26 +8,44 @@
|
||||
import java
|
||||
import GwtUiBinderXml
|
||||
|
||||
/**
|
||||
* An annotation in the package `com.google.gwt.uibinder.client`.
|
||||
*/
|
||||
class GwtUiBinderClientAnnotation extends Annotation {
|
||||
GwtUiBinderClientAnnotation() { getType().getPackage().hasName("com.google.gwt.uibinder.client") }
|
||||
}
|
||||
|
||||
/**
|
||||
* A `@com.google.gwt.uibinder.client.UiHandler` annotation.
|
||||
*/
|
||||
class GwtUiHandlerAnnotation extends GwtUiBinderClientAnnotation {
|
||||
GwtUiHandlerAnnotation() { getType().hasName("UiHandler") }
|
||||
}
|
||||
|
||||
/**
|
||||
* A `@com.google.gwt.uibinder.client.UiField` annotation.
|
||||
*/
|
||||
class GwtUiFieldAnnotation extends GwtUiBinderClientAnnotation {
|
||||
GwtUiFieldAnnotation() { getType().hasName("UiField") }
|
||||
}
|
||||
|
||||
/**
|
||||
* A `@com.google.gwt.uibinder.client.UiTemplate` annotation.
|
||||
*/
|
||||
class GwtUiTemplateAnnotation extends GwtUiBinderClientAnnotation {
|
||||
GwtUiTemplateAnnotation() { getType().hasName("UiTemplate") }
|
||||
}
|
||||
|
||||
/**
|
||||
* A `@com.google.gwt.uibinder.client.UiFactory` annotation.
|
||||
*/
|
||||
class GwtUiFactoryAnnotation extends GwtUiBinderClientAnnotation {
|
||||
GwtUiFactoryAnnotation() { getType().hasName("UiFactory") }
|
||||
}
|
||||
|
||||
/**
|
||||
* A `@com.google.gwt.uibinder.client.UiConstructor` annotation.
|
||||
*/
|
||||
class GwtUiConstructorAnnotation extends GwtUiBinderClientAnnotation {
|
||||
GwtUiConstructorAnnotation() { getType().hasName("UiConstructor") }
|
||||
}
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
/** Provides classes and predicates for working with `*.gwt.xml` files. */
|
||||
|
||||
import semmle.code.xml.XML
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,3 +1,7 @@
|
||||
/**
|
||||
* Provides classes and predicates for working with OCNI (Objective-C Native Interface).
|
||||
*/
|
||||
|
||||
import java
|
||||
|
||||
/**
|
||||
|
||||
23
java/ql/src/semmle/code/java/frameworks/jOOQ.qll
Normal file
23
java/ql/src/semmle/code/java/frameworks/jOOQ.qll
Normal file
@@ -0,0 +1,23 @@
|
||||
/**
|
||||
* Provides classes and predicates for working with the jOOQ framework.
|
||||
*/
|
||||
|
||||
import java
|
||||
|
||||
/**
|
||||
* Methods annotated with this allow for generation of "plain SQL"
|
||||
* and is prone to SQL injection.
|
||||
* https://www.jooq.org/doc/current/manual/sql-building/plain-sql/
|
||||
*/
|
||||
private class PlainSQLType extends Annotation {
|
||||
PlainSQLType() { this.getType().hasQualifiedName("org.jooq", "PlainSQL") }
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `m` is a jOOQ SQL method taking an SQL string as its
|
||||
* first argument.
|
||||
*/
|
||||
predicate jOOQSqlMethod(Method m) {
|
||||
m.getAnAnnotation() instanceof PlainSQLType and
|
||||
m.getParameterType(0) instanceof TypeString
|
||||
}
|
||||
@@ -9,6 +9,9 @@ import semmle.code.java.Reflection
|
||||
import semmle.code.java.dataflow.DataFlow
|
||||
import semmle.code.java.dataflow.DataFlow5
|
||||
|
||||
/**
|
||||
* A `@com.fasterxml.jackson.annotation.JsonIgnore` annoation.
|
||||
*/
|
||||
class JacksonJSONIgnoreAnnotation extends NonReflectiveAnnotation {
|
||||
JacksonJSONIgnoreAnnotation() {
|
||||
exists(AnnotationType anntp | anntp = this.getType() |
|
||||
@@ -17,6 +20,7 @@ class JacksonJSONIgnoreAnnotation extends NonReflectiveAnnotation {
|
||||
}
|
||||
}
|
||||
|
||||
/** A type whose values may be serialized using the Jackson JSON framework. */
|
||||
abstract class JacksonSerializableType extends Type { }
|
||||
|
||||
/**
|
||||
@@ -34,6 +38,7 @@ library class JacksonWriteValueMethod extends Method {
|
||||
}
|
||||
}
|
||||
|
||||
/** A type whose values are explicitly serialized in a call to a Jackson method. */
|
||||
library class ExplicitlyWrittenJacksonSerializableType extends JacksonSerializableType {
|
||||
ExplicitlyWrittenJacksonSerializableType() {
|
||||
exists(MethodAccess ma |
|
||||
@@ -45,12 +50,14 @@ library class ExplicitlyWrittenJacksonSerializableType extends JacksonSerializab
|
||||
}
|
||||
}
|
||||
|
||||
/** A type used in a `JacksonSerializableField` declaration. */
|
||||
library class FieldReferencedJacksonSerializableType extends JacksonSerializableType {
|
||||
FieldReferencedJacksonSerializableType() {
|
||||
exists(JacksonSerializableField f | usesType(f.getType(), this))
|
||||
}
|
||||
}
|
||||
|
||||
/** A type whose values may be deserialized by the Jackson JSON framework. */
|
||||
abstract class JacksonDeserializableType extends Type { }
|
||||
|
||||
private class TypeLiteralToJacksonDatabindFlowConfiguration extends DataFlow5::Configuration {
|
||||
@@ -76,6 +83,7 @@ private class TypeLiteralToJacksonDatabindFlowConfiguration extends DataFlow5::C
|
||||
TypeLiteral getSourceWithFlowToJacksonDatabind() { hasFlow(DataFlow::exprNode(result), _) }
|
||||
}
|
||||
|
||||
/** A type whose values are explicitly deserialized in a call to a Jackson method. */
|
||||
library class ExplicitlyReadJacksonDeserializableType extends JacksonDeserializableType {
|
||||
ExplicitlyReadJacksonDeserializableType() {
|
||||
exists(TypeLiteralToJacksonDatabindFlowConfiguration conf |
|
||||
@@ -84,12 +92,14 @@ library class ExplicitlyReadJacksonDeserializableType extends JacksonDeserializa
|
||||
}
|
||||
}
|
||||
|
||||
/** A type used in a `JacksonDeserializableField` declaration. */
|
||||
library class FieldReferencedJacksonDeSerializableType extends JacksonDeserializableType {
|
||||
FieldReferencedJacksonDeSerializableType() {
|
||||
exists(JacksonDeserializableField f | usesType(f.getType(), this))
|
||||
}
|
||||
}
|
||||
|
||||
/** A field that may be serialized using the Jackson JSON framework. */
|
||||
class JacksonSerializableField extends SerializableField {
|
||||
JacksonSerializableField() {
|
||||
exists(JacksonSerializableType superType |
|
||||
@@ -101,6 +111,7 @@ class JacksonSerializableField extends SerializableField {
|
||||
}
|
||||
}
|
||||
|
||||
/** A field that may be deserialized using the Jackson JSON framework. */
|
||||
class JacksonDeserializableField extends DeserializableField {
|
||||
JacksonDeserializableField() {
|
||||
exists(JacksonDeserializableType superType |
|
||||
@@ -183,6 +194,7 @@ class JacksonMixinType extends ClassOrInterface {
|
||||
}
|
||||
}
|
||||
|
||||
/** A callable used as a Jackson mixin callable. */
|
||||
class JacksonMixedInCallable extends Callable {
|
||||
JacksonMixedInCallable() {
|
||||
exists(JacksonMixinType mixinType | this = mixinType.getAMixedInCallable())
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
/** Provides classes and predicates for working with Java Server Faces. */
|
||||
|
||||
import default
|
||||
import semmle.code.java.frameworks.javaee.jsf.JSFAnnotations
|
||||
import semmle.code.java.frameworks.javaee.jsf.JSFFacesContextXML
|
||||
@@ -42,6 +44,7 @@ class FacesAccessibleType extends RefType {
|
||||
)
|
||||
}
|
||||
|
||||
/** Gets a method declared on this type that is visible to JSF. */
|
||||
FacesVisibleMethod getAnAccessibleMethod() { result = getAMethod() }
|
||||
}
|
||||
|
||||
|
||||
@@ -1,3 +1,7 @@
|
||||
/**
|
||||
* Provides classes and predicates for working with the JavaEE Persistence API.
|
||||
*/
|
||||
|
||||
import java
|
||||
|
||||
/**
|
||||
@@ -29,7 +33,7 @@ class PersistentEntity extends RefType {
|
||||
}
|
||||
|
||||
/**
|
||||
* If there is an annotation on this class defining the access type, then this is the type.
|
||||
* Gets the access type for this entity as defined by a `@javax.persistence.Access` annotation, if any.
|
||||
*/
|
||||
string getAccessTypeFromAnnotation() {
|
||||
exists(AccessAnnotation accessType | accessType = getAnAnnotation() |
|
||||
@@ -43,376 +47,607 @@ class PersistentEntity extends RefType {
|
||||
* Annotations in the `javax.persistence` package.
|
||||
*/
|
||||
|
||||
/**
|
||||
* A `@javax.persistence.Access` annotation.
|
||||
*/
|
||||
class AccessAnnotation extends Annotation {
|
||||
AccessAnnotation() { this.getType().hasQualifiedName("javax.persistence", "Access") }
|
||||
}
|
||||
|
||||
/**
|
||||
* A `@javax.persistence.AccessType` annotation.
|
||||
*/
|
||||
class AccessTypeAnnotation extends Annotation {
|
||||
AccessTypeAnnotation() { this.getType().hasQualifiedName("javax.persistence", "AccessType") }
|
||||
}
|
||||
|
||||
/**
|
||||
* A `@javax.persistence.AssociationOverride` annotation.
|
||||
*/
|
||||
class AssociationOverrideAnnotation extends Annotation {
|
||||
AssociationOverrideAnnotation() {
|
||||
this.getType().hasQualifiedName("javax.persistence", "AssociationOverride")
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A `@javax.persistence.AssociationOverrides` annotation.
|
||||
*/
|
||||
class AssociationOverridesAnnotation extends Annotation {
|
||||
AssociationOverridesAnnotation() {
|
||||
this.getType().hasQualifiedName("javax.persistence", "AssociationOverrides")
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A `@javax.persistence.AttributeOverride` annotation.
|
||||
*/
|
||||
class AttributeOverrideAnnotation extends Annotation {
|
||||
AttributeOverrideAnnotation() {
|
||||
this.getType().hasQualifiedName("javax.persistence", "AttributeOverride")
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A `@javax.persistence.AttributeOverrides` annotation.
|
||||
*/
|
||||
class AttributeOverridesAnnotation extends Annotation {
|
||||
AttributeOverridesAnnotation() {
|
||||
this.getType().hasQualifiedName("javax.persistence", "AttributeOverrides")
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A `@javax.persistence.Basic` annotation.
|
||||
*/
|
||||
class BasicAnnotation extends Annotation {
|
||||
BasicAnnotation() { this.getType().hasQualifiedName("javax.persistence", "Basic") }
|
||||
}
|
||||
|
||||
/**
|
||||
* A `@javax.persistence.Cacheable` annotation.
|
||||
*/
|
||||
class CacheableAnnotation extends Annotation {
|
||||
CacheableAnnotation() { this.getType().hasQualifiedName("javax.persistence", "Cacheable") }
|
||||
}
|
||||
|
||||
/**
|
||||
* A `@javax.persistence.CollectionTable` annotation.
|
||||
*/
|
||||
class CollectionTableAnnotation extends Annotation {
|
||||
CollectionTableAnnotation() {
|
||||
this.getType().hasQualifiedName("javax.persistence", "CollectionTable")
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A `@javax.persistence.Column` annotation.
|
||||
*/
|
||||
class ColumnAnnotation extends Annotation {
|
||||
ColumnAnnotation() { this.getType().hasQualifiedName("javax.persistence", "Column") }
|
||||
}
|
||||
|
||||
/**
|
||||
* A `@javax.persistence.ColumnResult` annotation.
|
||||
*/
|
||||
class ColumnResultAnnotation extends Annotation {
|
||||
ColumnResultAnnotation() { this.getType().hasQualifiedName("javax.persistence", "ColumnResult") }
|
||||
}
|
||||
|
||||
/**
|
||||
* A `@javax.persistence.DiscriminatorColumn` annotation.
|
||||
*/
|
||||
class DiscriminatorColumnAnnotation extends Annotation {
|
||||
DiscriminatorColumnAnnotation() {
|
||||
this.getType().hasQualifiedName("javax.persistence", "DiscriminatorColumn")
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A `@javax.persistence.DiscriminatorValue` annotation.
|
||||
*/
|
||||
class DiscriminatorValueAnnotation extends Annotation {
|
||||
DiscriminatorValueAnnotation() {
|
||||
this.getType().hasQualifiedName("javax.persistence", "DiscriminatorValue")
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A `@javax.persistence.ElementCollection` annotation.
|
||||
*/
|
||||
class ElementCollectionAnnotation extends Annotation {
|
||||
ElementCollectionAnnotation() {
|
||||
this.getType().hasQualifiedName("javax.persistence", "ElementCollection")
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A `@javax.persistence.Embeddable` annotation.
|
||||
*/
|
||||
class EmbeddableAnnotation extends Annotation {
|
||||
EmbeddableAnnotation() { this.getType().hasQualifiedName("javax.persistence", "Embeddable") }
|
||||
}
|
||||
|
||||
/**
|
||||
* A `@javax.persistence.Embedded` annotation.
|
||||
*/
|
||||
class EmbeddedAnnotation extends Annotation {
|
||||
EmbeddedAnnotation() { this.getType().hasQualifiedName("javax.persistence", "Embedded") }
|
||||
}
|
||||
|
||||
/**
|
||||
* A `@javax.persistence.EmbeddedId` annotation.
|
||||
*/
|
||||
class EmbeddedIdAnnotation extends Annotation {
|
||||
EmbeddedIdAnnotation() { this.getType().hasQualifiedName("javax.persistence", "EmbeddedId") }
|
||||
}
|
||||
|
||||
/**
|
||||
* A `@javax.persistence.Entity` annotation.
|
||||
*/
|
||||
class EntityAnnotation extends Annotation {
|
||||
EntityAnnotation() { this.getType().hasQualifiedName("javax.persistence", "Entity") }
|
||||
}
|
||||
|
||||
/**
|
||||
* A `@javax.persistence.EntityListeners` annotation.
|
||||
*/
|
||||
class EntityListenersAnnotation extends Annotation {
|
||||
EntityListenersAnnotation() {
|
||||
this.getType().hasQualifiedName("javax.persistence", "EntityListeners")
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A `@javax.persistence.EntityResult` annotation.
|
||||
*/
|
||||
class EntityResultAnnotation extends Annotation {
|
||||
EntityResultAnnotation() { this.getType().hasQualifiedName("javax.persistence", "EntityResult") }
|
||||
}
|
||||
|
||||
/**
|
||||
* A `@javax.persistence.Enumerated` annotation.
|
||||
*/
|
||||
class EnumeratedAnnotation extends Annotation {
|
||||
EnumeratedAnnotation() { this.getType().hasQualifiedName("javax.persistence", "Enumerated") }
|
||||
}
|
||||
|
||||
/**
|
||||
* A `@javax.persistence.ExcludeDefaultListeners` annotation.
|
||||
*/
|
||||
class ExcludeDefaultListenersAnnotation extends Annotation {
|
||||
ExcludeDefaultListenersAnnotation() {
|
||||
this.getType().hasQualifiedName("javax.persistence", "ExcludeDefaultListeners")
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A `@javax.persistence.ExcludeSuperclassListeners` annotation.
|
||||
*/
|
||||
class ExcludeSuperclassListenersAnnotation extends Annotation {
|
||||
ExcludeSuperclassListenersAnnotation() {
|
||||
this.getType().hasQualifiedName("javax.persistence", "ExcludeSuperclassListeners")
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A `@javax.persistence.FieldResult` annotation.
|
||||
*/
|
||||
class FieldResultAnnotation extends Annotation {
|
||||
FieldResultAnnotation() { this.getType().hasQualifiedName("javax.persistence", "FieldResult") }
|
||||
}
|
||||
|
||||
/**
|
||||
* A `@javax.persistence.GeneratedValue` annotation.
|
||||
*/
|
||||
class GeneratedValueAnnotation extends Annotation {
|
||||
GeneratedValueAnnotation() {
|
||||
this.getType().hasQualifiedName("javax.persistence", "GeneratedValue")
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A `@javax.persistence.Id` annotation.
|
||||
*/
|
||||
class IdAnnotation extends Annotation {
|
||||
IdAnnotation() { this.getType().hasQualifiedName("javax.persistence", "Id") }
|
||||
}
|
||||
|
||||
/**
|
||||
* A `@javax.persistence.IdClass` annotation.
|
||||
*/
|
||||
class IdClassAnnotation extends Annotation {
|
||||
IdClassAnnotation() { this.getType().hasQualifiedName("javax.persistence", "IdClass") }
|
||||
}
|
||||
|
||||
/**
|
||||
* A `@javax.persistence.Inheritance` annotation.
|
||||
*/
|
||||
class InheritanceAnnotation extends Annotation {
|
||||
InheritanceAnnotation() { this.getType().hasQualifiedName("javax.persistence", "Inheritance") }
|
||||
}
|
||||
|
||||
/**
|
||||
* A `@javax.persistence.JoinColumn` annotation.
|
||||
*/
|
||||
class JoinColumnAnnotation extends Annotation {
|
||||
JoinColumnAnnotation() { this.getType().hasQualifiedName("javax.persistence", "JoinColumn") }
|
||||
}
|
||||
|
||||
/**
|
||||
* A `@javax.persistence.JoinColumns` annotation.
|
||||
*/
|
||||
class JoinColumnsAnnotation extends Annotation {
|
||||
JoinColumnsAnnotation() { this.getType().hasQualifiedName("javax.persistence", "JoinColumns") }
|
||||
}
|
||||
|
||||
/**
|
||||
* A `@javax.persistence.JoinTable` annotation.
|
||||
*/
|
||||
class JoinTableAnnotation extends Annotation {
|
||||
JoinTableAnnotation() { this.getType().hasQualifiedName("javax.persistence", "JoinTable") }
|
||||
}
|
||||
|
||||
/**
|
||||
* A `@javax.persistence.Lob` annotation.
|
||||
*/
|
||||
class LobAnnotation extends Annotation {
|
||||
LobAnnotation() { this.getType().hasQualifiedName("javax.persistence", "Lob") }
|
||||
}
|
||||
|
||||
/**
|
||||
* A `@javax.persistence.ManyToMany` annotation.
|
||||
*/
|
||||
class ManyToManyAnnotation extends Annotation {
|
||||
ManyToManyAnnotation() { this.getType().hasQualifiedName("javax.persistence", "ManyToMany") }
|
||||
}
|
||||
|
||||
/**
|
||||
* A `@javax.persistence.ManyToOne` annotation.
|
||||
*/
|
||||
class ManyToOneAnnotation extends Annotation {
|
||||
ManyToOneAnnotation() { this.getType().hasQualifiedName("javax.persistence", "ManyToOne") }
|
||||
}
|
||||
|
||||
/**
|
||||
* A `@javax.persistence.MapKey` annotation.
|
||||
*/
|
||||
class MapKeyAnnotation extends Annotation {
|
||||
MapKeyAnnotation() { this.getType().hasQualifiedName("javax.persistence", "MapKey") }
|
||||
}
|
||||
|
||||
/**
|
||||
* A `@javax.persistence.MapKeyClass` annotation.
|
||||
*/
|
||||
class MapKeyClassAnnotation extends Annotation {
|
||||
MapKeyClassAnnotation() { this.getType().hasQualifiedName("javax.persistence", "MapKeyClass") }
|
||||
}
|
||||
|
||||
/**
|
||||
* A `@javax.persistence.MapKeyColumn` annotation.
|
||||
*/
|
||||
class MapKeyColumnAnnotation extends Annotation {
|
||||
MapKeyColumnAnnotation() { this.getType().hasQualifiedName("javax.persistence", "MapKeyColumn") }
|
||||
}
|
||||
|
||||
/**
|
||||
* A `@javax.persistence.MapKeyEnumerated` annotation.
|
||||
*/
|
||||
class MapKeyEnumeratedAnnotation extends Annotation {
|
||||
MapKeyEnumeratedAnnotation() {
|
||||
this.getType().hasQualifiedName("javax.persistence", "MapKeyEnumerated")
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A `@javax.persistence.MapKeyJoinColumn` annotation.
|
||||
*/
|
||||
class MapKeyJoinColumnAnnotation extends Annotation {
|
||||
MapKeyJoinColumnAnnotation() {
|
||||
this.getType().hasQualifiedName("javax.persistence", "MapKeyJoinColumn")
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A `@javax.persistence.MapKeyJoinColumns` annotation.
|
||||
*/
|
||||
class MapKeyJoinColumnsAnnotation extends Annotation {
|
||||
MapKeyJoinColumnsAnnotation() {
|
||||
this.getType().hasQualifiedName("javax.persistence", "MapKeyJoinColumns")
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A `@javax.persistence.MapKeyTemporal` annotation.
|
||||
*/
|
||||
class MapKeyTemporalAnnotation extends Annotation {
|
||||
MapKeyTemporalAnnotation() {
|
||||
this.getType().hasQualifiedName("javax.persistence", "MapKeyTemporal")
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A `@javax.persistence.MappedSuperclass` annotation.
|
||||
*/
|
||||
class MappedSuperclassAnnotation extends Annotation {
|
||||
MappedSuperclassAnnotation() {
|
||||
this.getType().hasQualifiedName("javax.persistence", "MappedSuperclass")
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A `@javax.persistence.MapsId` annotation.
|
||||
*/
|
||||
class MapsIdAnnotation extends Annotation {
|
||||
MapsIdAnnotation() { this.getType().hasQualifiedName("javax.persistence", "MapsId") }
|
||||
}
|
||||
|
||||
/**
|
||||
* A `@javax.persistence.NamedNativeQueries` annotation.
|
||||
*/
|
||||
class NamedNativeQueriesAnnotation extends Annotation {
|
||||
NamedNativeQueriesAnnotation() {
|
||||
this.getType().hasQualifiedName("javax.persistence", "NamedNativeQueries")
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A `@javax.persistence.NamedNativeQuery` annotation.
|
||||
*/
|
||||
class NamedNativeQueryAnnotation extends Annotation {
|
||||
NamedNativeQueryAnnotation() {
|
||||
this.getType().hasQualifiedName("javax.persistence", "NamedNativeQuery")
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A `@javax.persistence.NamedQueries` annotation.
|
||||
*/
|
||||
class NamedQueriesAnnotation extends Annotation {
|
||||
NamedQueriesAnnotation() { this.getType().hasQualifiedName("javax.persistence", "NamedQueries") }
|
||||
}
|
||||
|
||||
/**
|
||||
* A `@javax.persistence.NamedQuery` annotation.
|
||||
*/
|
||||
class NamedQueryAnnotation extends Annotation {
|
||||
NamedQueryAnnotation() { this.getType().hasQualifiedName("javax.persistence", "NamedQuery") }
|
||||
}
|
||||
|
||||
/**
|
||||
* A `@javax.persistence.OneToMany` annotation.
|
||||
*/
|
||||
class OneToManyAnnotation extends Annotation {
|
||||
OneToManyAnnotation() { this.getType().hasQualifiedName("javax.persistence", "OneToMany") }
|
||||
}
|
||||
|
||||
/**
|
||||
* A `@javax.persistence.OneToOne` annotation.
|
||||
*/
|
||||
class OneToOneAnnotation extends Annotation {
|
||||
OneToOneAnnotation() { this.getType().hasQualifiedName("javax.persistence", "OneToOne") }
|
||||
}
|
||||
|
||||
/**
|
||||
* A `@javax.persistence.OrderBy` annotation.
|
||||
*/
|
||||
class OrderByAnnotation extends Annotation {
|
||||
OrderByAnnotation() { this.getType().hasQualifiedName("javax.persistence", "OrderBy") }
|
||||
}
|
||||
|
||||
/**
|
||||
* A `@javax.persistence.OrderColumn` annotation.
|
||||
*/
|
||||
class OrderColumnAnnotation extends Annotation {
|
||||
OrderColumnAnnotation() { this.getType().hasQualifiedName("javax.persistence", "OrderColumn") }
|
||||
}
|
||||
|
||||
/**
|
||||
* A `@javax.persistence.PersistenceContext` annotation.
|
||||
*/
|
||||
class PersistenceContextAnnotation extends Annotation {
|
||||
PersistenceContextAnnotation() {
|
||||
this.getType().hasQualifiedName("javax.persistence", "PersistenceContext")
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A `@javax.persistence.PersistenceContexts` annotation.
|
||||
*/
|
||||
class PersistenceContextsAnnotation extends Annotation {
|
||||
PersistenceContextsAnnotation() {
|
||||
this.getType().hasQualifiedName("javax.persistence", "PersistenceContexts")
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A `@javax.persistence.PersistenceProperty` annotation.
|
||||
*/
|
||||
class PersistencePropertyAnnotation extends Annotation {
|
||||
PersistencePropertyAnnotation() {
|
||||
this.getType().hasQualifiedName("javax.persistence", "PersistenceProperty")
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A `@javax.persistence.PersistenceUnit` annotation.
|
||||
*/
|
||||
class PersistenceUnitAnnotation extends Annotation {
|
||||
PersistenceUnitAnnotation() {
|
||||
this.getType().hasQualifiedName("javax.persistence", "PersistenceUnit")
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A `@javax.persistence.PersistenceUnits` annotation.
|
||||
*/
|
||||
class PersistenceUnitsAnnotation extends Annotation {
|
||||
PersistenceUnitsAnnotation() {
|
||||
this.getType().hasQualifiedName("javax.persistence", "PersistenceUnits")
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A `@javax.persistence.PostLoad` annotation.
|
||||
*/
|
||||
class PostLoadAnnotation extends Annotation {
|
||||
PostLoadAnnotation() { this.getType().hasQualifiedName("javax.persistence", "PostLoad") }
|
||||
}
|
||||
|
||||
/**
|
||||
* A `@javax.persistence.PostPersist` annotation.
|
||||
*/
|
||||
class PostPersistAnnotation extends Annotation {
|
||||
PostPersistAnnotation() { this.getType().hasQualifiedName("javax.persistence", "PostPersist") }
|
||||
}
|
||||
|
||||
/**
|
||||
* A `@javax.persistence.PostRemove` annotation.
|
||||
*/
|
||||
class PostRemoveAnnotation extends Annotation {
|
||||
PostRemoveAnnotation() { this.getType().hasQualifiedName("javax.persistence", "PostRemove") }
|
||||
}
|
||||
|
||||
/**
|
||||
* A `@javax.persistence.PostUpdate` annotation.
|
||||
*/
|
||||
class PostUpdateAnnotation extends Annotation {
|
||||
PostUpdateAnnotation() { this.getType().hasQualifiedName("javax.persistence", "PostUpdate") }
|
||||
}
|
||||
|
||||
/**
|
||||
* A `@javax.persistence.PrePersist` annotation.
|
||||
*/
|
||||
class PrePersistAnnotation extends Annotation {
|
||||
PrePersistAnnotation() { this.getType().hasQualifiedName("javax.persistence", "PrePersist") }
|
||||
}
|
||||
|
||||
/**
|
||||
* A `@javax.persistence.PreRemove` annotation.
|
||||
*/
|
||||
class PreRemoveAnnotation extends Annotation {
|
||||
PreRemoveAnnotation() { this.getType().hasQualifiedName("javax.persistence", "PreRemove") }
|
||||
}
|
||||
|
||||
/**
|
||||
* A `@javax.persistence.PreUpdate` annotation.
|
||||
*/
|
||||
class PreUpdateAnnotation extends Annotation {
|
||||
PreUpdateAnnotation() { this.getType().hasQualifiedName("javax.persistence", "PreUpdate") }
|
||||
}
|
||||
|
||||
/**
|
||||
* A `@javax.persistence.PrimaryKeyJoinColumn` annotation.
|
||||
*/
|
||||
class PrimaryKeyJoinColumnAnnotation extends Annotation {
|
||||
PrimaryKeyJoinColumnAnnotation() {
|
||||
this.getType().hasQualifiedName("javax.persistence", "PrimaryKeyJoinColumn")
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A `@javax.persistence.PrimaryKeyJoinColumns` annotation.
|
||||
*/
|
||||
class PrimaryKeyJoinColumnsAnnotation extends Annotation {
|
||||
PrimaryKeyJoinColumnsAnnotation() {
|
||||
this.getType().hasQualifiedName("javax.persistence", "PrimaryKeyJoinColumns")
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A `@javax.persistence.QueryHint` annotation.
|
||||
*/
|
||||
class QueryHintAnnotation extends Annotation {
|
||||
QueryHintAnnotation() { this.getType().hasQualifiedName("javax.persistence", "QueryHint") }
|
||||
}
|
||||
|
||||
/**
|
||||
* A `@javax.persistence.SecondaryTable` annotation.
|
||||
*/
|
||||
class SecondaryTableAnnotation extends Annotation {
|
||||
SecondaryTableAnnotation() {
|
||||
this.getType().hasQualifiedName("javax.persistence", "SecondaryTable")
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A `@javax.persistence.SecondaryTables` annotation.
|
||||
*/
|
||||
class SecondaryTablesAnnotation extends Annotation {
|
||||
SecondaryTablesAnnotation() {
|
||||
this.getType().hasQualifiedName("javax.persistence", "SecondaryTables")
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A `@javax.persistence.SequenceGenerator` annotation.
|
||||
*/
|
||||
class SequenceGeneratorAnnotation extends Annotation {
|
||||
SequenceGeneratorAnnotation() {
|
||||
this.getType().hasQualifiedName("javax.persistence", "SequenceGenerator")
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A `@javax.persistence.SqlResultSetMapping` annotation.
|
||||
*/
|
||||
class SqlResultSetMappingAnnotation extends Annotation {
|
||||
SqlResultSetMappingAnnotation() {
|
||||
this.getType().hasQualifiedName("javax.persistence", "SqlResultSetMapping")
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A `@javax.persistence.SqlResultSetMappings` annotation.
|
||||
*/
|
||||
class SqlResultSetMappingsAnnotation extends Annotation {
|
||||
SqlResultSetMappingsAnnotation() {
|
||||
this.getType().hasQualifiedName("javax.persistence", "SqlResultSetMappings")
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A `@javax.persistence.Table` annotation.
|
||||
*/
|
||||
class TableAnnotation extends Annotation {
|
||||
TableAnnotation() { this.getType().hasQualifiedName("javax.persistence", "Table") }
|
||||
}
|
||||
|
||||
/**
|
||||
* A `@javax.persistence.TableGenerator` annotation.
|
||||
*/
|
||||
class TableGeneratorAnnotation extends Annotation {
|
||||
TableGeneratorAnnotation() {
|
||||
this.getType().hasQualifiedName("javax.persistence", "TableGenerator")
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A `@javax.persistence.Temporal` annotation.
|
||||
*/
|
||||
class TemporalAnnotation extends Annotation {
|
||||
TemporalAnnotation() { this.getType().hasQualifiedName("javax.persistence", "Temporal") }
|
||||
}
|
||||
|
||||
/**
|
||||
* A `@javax.persistence.Transient` annotation.
|
||||
*/
|
||||
class TransientAnnotation extends Annotation {
|
||||
TransientAnnotation() { this.getType().hasQualifiedName("javax.persistence", "Transient") }
|
||||
}
|
||||
|
||||
/**
|
||||
* A `@javax.persistence.UniqueConstraint` annotation.
|
||||
*/
|
||||
class UniqueConstraintAnnotation extends Annotation {
|
||||
UniqueConstraintAnnotation() {
|
||||
this.getType().hasQualifiedName("javax.persistence", "UniqueConstraint")
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A `@javax.persistence.Version` annotation.
|
||||
*/
|
||||
class VersionAnnotation extends Annotation {
|
||||
VersionAnnotation() { this.getType().hasQualifiedName("javax.persistence", "Version") }
|
||||
}
|
||||
|
||||
@@ -1,3 +1,8 @@
|
||||
/**
|
||||
* Provides classes and predicates for working with JavaEE
|
||||
* persistence configuration XML files (`persistence.xml`).
|
||||
*/
|
||||
|
||||
import java
|
||||
|
||||
/**
|
||||
@@ -6,66 +11,94 @@ import java
|
||||
class PersistenceXMLFile extends XMLFile {
|
||||
PersistenceXMLFile() { this.getStem() = "persistence" }
|
||||
|
||||
/** Gets the root XML element in this `persistence.xml` file. */
|
||||
PersistenceXmlRoot getRoot() { result = this.getAChild() }
|
||||
|
||||
// convenience methods
|
||||
/** Gets a `shared-cache-mode` XML element nested within this `persistence.xml` file. */
|
||||
SharedCacheModeElement getASharedCacheModeElement() {
|
||||
result = this.getRoot().getAPersistenceUnitElement().getASharedCacheModeElement()
|
||||
}
|
||||
|
||||
/** Gets a `property` XML element nested within this `persistence.xml` file. */
|
||||
PersistencePropertyElement getAPropertyElement() {
|
||||
result =
|
||||
this.getRoot().getAPersistenceUnitElement().getAPropertiesElement().getAPropertyElement()
|
||||
}
|
||||
}
|
||||
|
||||
/** The root `persistence` XML element in a `persistence.xml` file. */
|
||||
class PersistenceXmlRoot extends XMLElement {
|
||||
PersistenceXmlRoot() {
|
||||
this.getParent() instanceof PersistenceXMLFile and
|
||||
this.getName() = "persistence"
|
||||
}
|
||||
|
||||
/** Gets a `persistence-unit` child XML element of this `persistence` XML element. */
|
||||
PersistenceUnitElement getAPersistenceUnitElement() { result = this.getAChild() }
|
||||
}
|
||||
|
||||
/**
|
||||
* A `persistence-unit` child XML element of the root
|
||||
* `persistence` XML element in a `persistence.xml` file.
|
||||
*/
|
||||
class PersistenceUnitElement extends XMLElement {
|
||||
PersistenceUnitElement() {
|
||||
this.getParent() instanceof PersistenceXmlRoot and
|
||||
this.getName() = "persistence-unit"
|
||||
}
|
||||
|
||||
/** Gets a `shared-cache-mode` child XML element of this `persistence-unit` XML element. */
|
||||
SharedCacheModeElement getASharedCacheModeElement() { result = this.getAChild() }
|
||||
|
||||
/** Gets a `properties` child XML element of this `persistence-unit` XML element. */
|
||||
PersistencePropertiesElement getAPropertiesElement() { result = this.getAChild() }
|
||||
}
|
||||
|
||||
/**
|
||||
* A `shared-cache-mode` child XML element of a `persistence-unit`
|
||||
* XML element in a `persistence.xml` file.
|
||||
*/
|
||||
class SharedCacheModeElement extends XMLElement {
|
||||
SharedCacheModeElement() {
|
||||
this.getParent() instanceof PersistenceUnitElement and
|
||||
this.getName() = "shared-cache-mode"
|
||||
}
|
||||
|
||||
/** Gets the value of this `shared-cache-mode` XML element. */
|
||||
string getValue() { result = this.getACharactersSet().getCharacters() }
|
||||
|
||||
/** Holds if this `shared-cache-mode` XML element has the value "NONE". */
|
||||
predicate isDisabled() { this.getValue() = "NONE" }
|
||||
}
|
||||
|
||||
/**
|
||||
* A `properties` child XML element of a `persistence-unit`
|
||||
* XML element in a `persistence.xml` file.
|
||||
*/
|
||||
class PersistencePropertiesElement extends XMLElement {
|
||||
PersistencePropertiesElement() {
|
||||
this.getParent() instanceof PersistenceUnitElement and
|
||||
this.getName() = "properties"
|
||||
}
|
||||
|
||||
/** Gets a `property` child XML element of this `properties` XML element. */
|
||||
PersistencePropertyElement getAPropertyElement() { result = this.getAChild() }
|
||||
}
|
||||
|
||||
/**
|
||||
* A `property` child XML element of a `properties`
|
||||
* XML element in a `persistence.xml` file.
|
||||
*/
|
||||
class PersistencePropertyElement extends XMLElement {
|
||||
PersistencePropertyElement() {
|
||||
this.getParent() instanceof PersistencePropertiesElement and
|
||||
this.getName() = "property"
|
||||
}
|
||||
|
||||
/** see http://wiki.eclipse.org/EclipseLink/Examples/JPA/Caching */
|
||||
/**
|
||||
* Holds if this `property` XML element of a `persistence.xml` file
|
||||
* disables the EclipseLink shared cache.
|
||||
*/
|
||||
predicate disablesEclipseLinkSharedCache() {
|
||||
getAttribute("name").getValue() = "eclipselink.cache.shared.default" and
|
||||
getAttribute("value").getValue() = "false"
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
/** Provides classes and predicates for working with Enterprise Java Beans. */
|
||||
|
||||
import java
|
||||
import EJBJarXML
|
||||
|
||||
@@ -322,10 +324,17 @@ class LocalAnnotatedBusinessInterface extends AnnotatedBusinessInterface {
|
||||
* Init and create methods for session beans.
|
||||
*/
|
||||
|
||||
/**
|
||||
* A `@javax.ejb.Init` annotation.
|
||||
*/
|
||||
class InitAnnotation extends Annotation {
|
||||
InitAnnotation() { this.getType().hasQualifiedName("javax.ejb", "Init") }
|
||||
}
|
||||
|
||||
/**
|
||||
* A method annotated with a `@javax.ejb.Init` annotation
|
||||
* that is declared in or inherited by a session EJB.
|
||||
*/
|
||||
class EjbAnnotatedInitMethod extends Method {
|
||||
EjbAnnotatedInitMethod() {
|
||||
this.getAnAnnotation() instanceof InitAnnotation and
|
||||
@@ -333,21 +342,31 @@ class EjbAnnotatedInitMethod extends Method {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A method whose name starts with `ejbCreate` that is
|
||||
* declared in or inherited by a session EJB.
|
||||
*/
|
||||
class EjbCreateMethod extends Method {
|
||||
EjbCreateMethod() {
|
||||
this.getName().matches("ejbCreate%") and
|
||||
exists(SessionEJB ejb | ejb.inherits(this))
|
||||
}
|
||||
|
||||
/** Gets the suffix of the method name without the `ejbCreate` prefix. */
|
||||
string getMethodSuffix() { result = this.getName().substring(9, this.getName().length()) }
|
||||
}
|
||||
|
||||
/**
|
||||
* A method whose name starts with `create` that is
|
||||
* declared in or inherited by a legacy EJB home interface.
|
||||
*/
|
||||
class EjbInterfaceCreateMethod extends Method {
|
||||
EjbInterfaceCreateMethod() {
|
||||
this.getName().matches("create%") and
|
||||
exists(LegacyEjbHomeInterface i | i.inherits(this))
|
||||
}
|
||||
|
||||
/** Gets the suffix of the method name without the `create` prefix. */
|
||||
string getMethodSuffix() { result = this.getName().substring(6, this.getName().length()) }
|
||||
}
|
||||
|
||||
@@ -398,6 +417,10 @@ class XmlSpecifiedRemoteInterface extends LegacyEjbRemoteInterface {
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a session EJB specified in the XML deployment descriptor
|
||||
* for this legacy EJB remote interface.
|
||||
*/
|
||||
SessionEJB getAnEJB() {
|
||||
exists(EjbJarXMLFile f, EjbJarSessionElement se |
|
||||
se = f.getASessionElement() and
|
||||
@@ -423,6 +446,7 @@ class AnnotatedRemoteHomeInterface extends LegacyEjbRemoteHomeInterface {
|
||||
/** Gets an EJB to which this interface belongs. */
|
||||
SessionEJB getAnEJB() { result.getAnAnnotation().(RemoteHomeAnnotation).getANamedType() = this }
|
||||
|
||||
/** Gets a remote interface associated with this legacy remote home interface. */
|
||||
Interface getAnAssociatedRemoteInterface() { result = getACreateMethod().getReturnType() }
|
||||
}
|
||||
|
||||
@@ -486,6 +510,7 @@ class AnnotatedLocalHomeInterface extends LegacyEjbLocalHomeInterface {
|
||||
/** Gets an EJB to which this interface belongs. */
|
||||
SessionEJB getAnEJB() { result.getAnAnnotation().(LocalHomeAnnotation).getANamedType() = this }
|
||||
|
||||
/** Gets a local interface associated with this legacy local home interface. */
|
||||
Interface getAnAssociatedLocalInterface() { result = getACreateMethod().getReturnType() }
|
||||
}
|
||||
|
||||
@@ -535,6 +560,7 @@ class RemoteInterface extends Interface {
|
||||
*/
|
||||
Method getARemoteMethod() { this.inherits(result) }
|
||||
|
||||
/** Gets a remote method implementation for this remote interface. */
|
||||
Method getARemoteMethodImplementation() {
|
||||
result = getARemoteMethodImplementationChecked() or
|
||||
result = getARemoteMethodImplementationUnchecked()
|
||||
@@ -716,127 +742,209 @@ Type inheritsMatchingCreateMethodExceptThrows(StatefulSessionEJB ejb, EjbInterfa
|
||||
* Annotations in the `javax.ejb package`.
|
||||
*/
|
||||
|
||||
/**
|
||||
* A `@javax.ejb.AccessTimeout` annotation.
|
||||
*/
|
||||
class AccessTimeoutAnnotation extends Annotation {
|
||||
AccessTimeoutAnnotation() { this.getType().hasQualifiedName("javax.ejb", "AccessTimeout") }
|
||||
}
|
||||
|
||||
/**
|
||||
* A `@javax.ejb.ActivationConfigProperty` annotation.
|
||||
*/
|
||||
class ActivationConfigPropertyAnnotation extends Annotation {
|
||||
ActivationConfigPropertyAnnotation() {
|
||||
this.getType().hasQualifiedName("javax.ejb", "ActivationConfigProperty")
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A `@javax.ejb.AfterBegin` annotation.
|
||||
*/
|
||||
class AfterBeginAnnotation extends Annotation {
|
||||
AfterBeginAnnotation() { this.getType().hasQualifiedName("javax.ejb", "AfterBegin") }
|
||||
}
|
||||
|
||||
/**
|
||||
* A `@javax.ejb.AfterCompletion` annotation.
|
||||
*/
|
||||
class AfterCompletionAnnotation extends Annotation {
|
||||
AfterCompletionAnnotation() { this.getType().hasQualifiedName("javax.ejb", "AfterCompletion") }
|
||||
}
|
||||
|
||||
/**
|
||||
* A `@javax.ejb.ApplicationException` annotation.
|
||||
*/
|
||||
class ApplicationExceptionAnnotation extends Annotation {
|
||||
ApplicationExceptionAnnotation() {
|
||||
this.getType().hasQualifiedName("javax.ejb", "ApplicationException")
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A `@javax.ejb.Asynchronous` annotation.
|
||||
*/
|
||||
class AsynchronousAnnotation extends Annotation {
|
||||
AsynchronousAnnotation() { this.getType().hasQualifiedName("javax.ejb", "Asynchronous") }
|
||||
}
|
||||
|
||||
/**
|
||||
* A `@javax.ejb.BeforeCompletion` annotation.
|
||||
*/
|
||||
class BeforeCompletionAnnotation extends Annotation {
|
||||
BeforeCompletionAnnotation() { this.getType().hasQualifiedName("javax.ejb", "BeforeCompletion") }
|
||||
}
|
||||
|
||||
/**
|
||||
* A `@javax.ejb.ConcurrencyManagement` annotation.
|
||||
*/
|
||||
class ConcurrencyManagementAnnotation extends Annotation {
|
||||
ConcurrencyManagementAnnotation() {
|
||||
this.getType().hasQualifiedName("javax.ejb", "ConcurrencyManagement")
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A `@javax.ejb.DependsOn` annotation.
|
||||
*/
|
||||
class DependsOnAnnotation extends Annotation {
|
||||
DependsOnAnnotation() { this.getType().hasQualifiedName("javax.ejb", "DependsOn") }
|
||||
}
|
||||
|
||||
/**
|
||||
* A `@javax.ejb.EJB` annotation.
|
||||
*/
|
||||
class EJBAnnotation extends Annotation {
|
||||
EJBAnnotation() { this.getType().hasQualifiedName("javax.ejb", "EJB") }
|
||||
}
|
||||
|
||||
/**
|
||||
* A `@javax.ejb.EJBs` annotation.
|
||||
*/
|
||||
class EJBsAnnotation extends Annotation {
|
||||
EJBsAnnotation() { this.getType().hasQualifiedName("javax.ejb", "EJBs") }
|
||||
}
|
||||
|
||||
// See above for `@Init`, `@Local`.
|
||||
/**
|
||||
* A `@javax.ejb.LocalBean` annotation.
|
||||
*/
|
||||
class LocalBeanAnnotation extends Annotation {
|
||||
LocalBeanAnnotation() { this.getType().hasQualifiedName("javax.ejb", "LocalBean") }
|
||||
}
|
||||
|
||||
// See above for `@LocalHome`.
|
||||
/**
|
||||
* A `@javax.ejb.Lock` annotation.
|
||||
*/
|
||||
class LockAnnotation extends Annotation {
|
||||
LockAnnotation() { this.getType().hasQualifiedName("javax.ejb", "Lock") }
|
||||
}
|
||||
|
||||
/**
|
||||
* A `@javax.ejb.MessageDriven` annotation.
|
||||
*/
|
||||
class MessageDrivenAnnotation extends Annotation {
|
||||
MessageDrivenAnnotation() { this.getType().hasQualifiedName("javax.ejb", "MessageDriven") }
|
||||
}
|
||||
|
||||
/**
|
||||
* A `@javax.ejb.PostActivate` annotation.
|
||||
*/
|
||||
class PostActivateAnnotation extends Annotation {
|
||||
PostActivateAnnotation() { this.getType().hasQualifiedName("javax.ejb", "PostActivate") }
|
||||
}
|
||||
|
||||
/**
|
||||
* A `@javax.ejb.PrePassivate` annotation.
|
||||
*/
|
||||
class PrePassivateAnnotation extends Annotation {
|
||||
PrePassivateAnnotation() { this.getType().hasQualifiedName("javax.ejb", "PrePassivate") }
|
||||
}
|
||||
|
||||
// See above for `@Remote`, `@RemoteHome`.
|
||||
/**
|
||||
* A `@javax.ejb.Remove` annotation.
|
||||
*/
|
||||
class RemoveAnnotation extends Annotation {
|
||||
RemoveAnnotation() { this.getType().hasQualifiedName("javax.ejb", "Remove") }
|
||||
}
|
||||
|
||||
/**
|
||||
* A `@javax.ejb.Schedule` annotation.
|
||||
*/
|
||||
class ScheduleAnnotation extends Annotation {
|
||||
ScheduleAnnotation() { this.getType().hasQualifiedName("javax.ejb", "Schedule") }
|
||||
}
|
||||
|
||||
/**
|
||||
* A `@javax.ejb.Schedules` annotation.
|
||||
*/
|
||||
class SchedulesAnnotation extends Annotation {
|
||||
SchedulesAnnotation() { this.getType().hasQualifiedName("javax.ejb", "Schedules") }
|
||||
}
|
||||
|
||||
/**
|
||||
* A `@javax.ejb.Singleton` annotation.
|
||||
*/
|
||||
class SingletonAnnotation extends Annotation {
|
||||
SingletonAnnotation() { this.getType().hasQualifiedName("javax.ejb", "Singleton") }
|
||||
}
|
||||
|
||||
/**
|
||||
* A `@javax.ejb.Startup` annotation.
|
||||
*/
|
||||
class StartupAnnotation extends Annotation {
|
||||
StartupAnnotation() { this.getType().hasQualifiedName("javax.ejb", "Startup") }
|
||||
}
|
||||
|
||||
/**
|
||||
* A `@javax.ejb.Stateful` annotation.
|
||||
*/
|
||||
class StatefulAnnotation extends Annotation {
|
||||
StatefulAnnotation() { this.getType().hasQualifiedName("javax.ejb", "Stateful") }
|
||||
}
|
||||
|
||||
/**
|
||||
* A `@javax.ejb.StatefulTimeout` annotation.
|
||||
*/
|
||||
class StatefulTimeoutAnnotation extends Annotation {
|
||||
StatefulTimeoutAnnotation() { this.getType().hasQualifiedName("javax.ejb", "StatefulTimeout") }
|
||||
}
|
||||
|
||||
/**
|
||||
* A `@javax.ejb.Stateless` annotation.
|
||||
*/
|
||||
class StatelessAnnotation extends Annotation {
|
||||
StatelessAnnotation() { this.getType().hasQualifiedName("javax.ejb", "Stateless") }
|
||||
}
|
||||
|
||||
/**
|
||||
* A `@javax.ejb.Timeout` annotation.
|
||||
*/
|
||||
class TimeoutAnnotation extends Annotation {
|
||||
TimeoutAnnotation() { this.getType().hasQualifiedName("javax.ejb", "Timeout") }
|
||||
}
|
||||
|
||||
/**
|
||||
* A `@javax.ejb.TransactionAttribute` annotation.
|
||||
*/
|
||||
class TransactionAttributeAnnotation extends Annotation {
|
||||
TransactionAttributeAnnotation() {
|
||||
this.getType().hasQualifiedName("javax.ejb", "TransactionAttribute")
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A `@javax.ejb.TransactionManagement` annotation.
|
||||
*/
|
||||
class TransactionManagementAnnotation extends Annotation {
|
||||
TransactionManagementAnnotation() {
|
||||
this.getType().hasQualifiedName("javax.ejb", "TransactionManagement")
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A `@javax.ejb.TransactionAttribute` annotation with the
|
||||
* transaction attribute type set to `REQUIRED`.
|
||||
*/
|
||||
class RequiredTransactionAttributeAnnotation extends TransactionAttributeAnnotation {
|
||||
RequiredTransactionAttributeAnnotation() {
|
||||
exists(FieldRead fr |
|
||||
@@ -847,6 +955,10 @@ class RequiredTransactionAttributeAnnotation extends TransactionAttributeAnnotat
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A `@javax.ejb.TransactionAttribute` annotation with the
|
||||
* transaction attribute type set to `REQUIRES_NEW`.
|
||||
*/
|
||||
class RequiresNewTransactionAttributeAnnotation extends TransactionAttributeAnnotation {
|
||||
RequiresNewTransactionAttributeAnnotation() {
|
||||
exists(FieldRead fr |
|
||||
@@ -861,6 +973,9 @@ class RequiresNewTransactionAttributeAnnotation extends TransactionAttributeAnno
|
||||
* Convenience methods.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Gets the innermost `@javax.ejb.TransactionAttribute` annotation for method `m`.
|
||||
*/
|
||||
TransactionAttributeAnnotation getInnermostTransactionAttributeAnnotation(Method m) {
|
||||
// A `TransactionAttribute` annotation can either be on the method itself,
|
||||
// in which case it supersedes any such annotation on the declaring class...
|
||||
@@ -876,6 +991,10 @@ TransactionAttributeAnnotation getInnermostTransactionAttributeAnnotation(Method
|
||||
* Methods in the `javax.ejb package`.
|
||||
*/
|
||||
|
||||
/**
|
||||
* A method named `setRollbackOnly` declared on the
|
||||
* interface `javax.ejb.EJBContext` or a subtype thereof.
|
||||
*/
|
||||
class SetRollbackOnlyMethod extends Method {
|
||||
SetRollbackOnlyMethod() {
|
||||
this.getDeclaringType().getASupertype*().hasQualifiedName("javax.ejb", "EJBContext") and
|
||||
|
||||
@@ -1,3 +1,8 @@
|
||||
/**
|
||||
* Provides classes and predicates for working with
|
||||
* EJB deployment descriptor XML files (`ejb-jar.xml`).
|
||||
*/
|
||||
|
||||
import java
|
||||
|
||||
/**
|
||||
@@ -6,161 +11,222 @@ import java
|
||||
class EjbJarXMLFile extends XMLFile {
|
||||
EjbJarXMLFile() { this.getStem() = "ejb-jar" }
|
||||
|
||||
/** Gets the root `ejb-jar` XML element of this `ejb-jar.xml` file. */
|
||||
EjbJarRootElement getRoot() { result = this.getAChild() }
|
||||
|
||||
// Convenience methods.
|
||||
/** Gets an `enterprise-beans` XML element nested within this `ejb-jar.xml` file. */
|
||||
EjbJarEnterpriseBeansElement getAnEnterpriseBeansElement() {
|
||||
result = this.getRoot().getAnEnterpriseBeansElement()
|
||||
}
|
||||
|
||||
/** Gets a `session` XML element nested within this `ejb-jar.xml` file. */
|
||||
EjbJarSessionElement getASessionElement() {
|
||||
result = this.getAnEnterpriseBeansElement().getASessionElement()
|
||||
}
|
||||
|
||||
/** Gets a `message-driven` XML element nested within this `ejb-jar.xml` file. */
|
||||
EjbJarMessageDrivenElement getAMessageDrivenElement() {
|
||||
result = this.getAnEnterpriseBeansElement().getAMessageDrivenElement()
|
||||
}
|
||||
|
||||
/** Gets an `entity` XML element nested within this `ejb-jar.xml` file. */
|
||||
EjbJarEntityElement getAnEntityElement() {
|
||||
result = this.getAnEnterpriseBeansElement().getAnEntityElement()
|
||||
}
|
||||
}
|
||||
|
||||
/** The root `ejb-jar` XML element in an `ejb-jar.xml` file. */
|
||||
class EjbJarRootElement extends XMLElement {
|
||||
EjbJarRootElement() {
|
||||
this.getParent() instanceof EjbJarXMLFile and
|
||||
this.getName() = "ejb-jar"
|
||||
}
|
||||
|
||||
/** Gets an `enterprise-beans` child XML element of this root `ejb-jar` XML element. */
|
||||
EjbJarEnterpriseBeansElement getAnEnterpriseBeansElement() { result = this.getAChild() }
|
||||
}
|
||||
|
||||
/**
|
||||
* An `enterprise-beans` child XML element of the root
|
||||
* `ejb-jar` XML element in an `ejb-jar.xml` file.
|
||||
*/
|
||||
class EjbJarEnterpriseBeansElement extends XMLElement {
|
||||
EjbJarEnterpriseBeansElement() {
|
||||
this.getParent() instanceof EjbJarRootElement and
|
||||
this.getName() = "enterprise-beans"
|
||||
}
|
||||
|
||||
/** Gets a `session` child XML element of this `enterprise-beans` XML element. */
|
||||
EjbJarSessionElement getASessionElement() {
|
||||
result = this.getAChild() and
|
||||
result.getName() = "session"
|
||||
}
|
||||
|
||||
/** Gets a `message-driven` child XML element of this `enterprise-beans` XML element. */
|
||||
EjbJarMessageDrivenElement getAMessageDrivenElement() {
|
||||
result = this.getAChild() and
|
||||
result.getName() = "message-driven"
|
||||
}
|
||||
|
||||
/** Gets an `entity` child XML element of this `enterprise-beans` XML element. */
|
||||
EjbJarEntityElement getAnEntityElement() {
|
||||
result = this.getAChild() and
|
||||
result.getName() = "entity"
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A child XML element of an `enterprise-beans` XML element within an `ejb-jar.xml` file.
|
||||
*
|
||||
* This is either a `message-driven` element, a `session` element, or an `entity` element.
|
||||
*/
|
||||
abstract class EjbJarBeanTypeElement extends XMLElement {
|
||||
EjbJarBeanTypeElement() { this.getParent() instanceof EjbJarEnterpriseBeansElement }
|
||||
|
||||
/** Gets an `ejb-class` child XML element of this bean type element. */
|
||||
XMLElement getAnEjbClassElement() {
|
||||
result = this.getAChild() and
|
||||
result.getName() = "ejb-class"
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A `session` child XML element of a bean type element in an `ejb-jar.xml` file.
|
||||
*/
|
||||
class EjbJarSessionElement extends EjbJarBeanTypeElement {
|
||||
EjbJarSessionElement() { this.getName() = "session" }
|
||||
|
||||
/** Gets a `business-local` child XML element of this `session` XML element. */
|
||||
XMLElement getABusinessLocalElement() {
|
||||
result = this.getAChild() and
|
||||
result.getName() = "business-local"
|
||||
}
|
||||
|
||||
/** Gets a `business-remote` child XML element of this `session` XML element. */
|
||||
XMLElement getABusinessRemoteElement() {
|
||||
result = this.getAChild() and
|
||||
result.getName() = "business-remote"
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a business child XML element of this `session` XML element.
|
||||
*
|
||||
* This is either a `business-local` or `business-remote` element.
|
||||
*/
|
||||
XMLElement getABusinessElement() {
|
||||
result = getABusinessLocalElement() or
|
||||
result = getABusinessRemoteElement()
|
||||
}
|
||||
|
||||
/** Gets a `remote` child XML element of this `session` XML element. */
|
||||
XMLElement getARemoteElement() {
|
||||
result = this.getAChild() and
|
||||
result.getName() = "remote"
|
||||
}
|
||||
|
||||
/** Gets a `home` child XML element of this `session` XML element. */
|
||||
XMLElement getARemoteHomeElement() {
|
||||
result = this.getAChild() and
|
||||
result.getName() = "home"
|
||||
}
|
||||
|
||||
/** Gets a `local` child XML element of this `session` XML element. */
|
||||
XMLElement getALocalElement() {
|
||||
result = this.getAChild() and
|
||||
result.getName() = "local"
|
||||
}
|
||||
|
||||
/** Gets a `local-home` child XML element of this `session` XML element. */
|
||||
XMLElement getALocalHomeElement() {
|
||||
result = this.getAChild() and
|
||||
result.getName() = "local-home"
|
||||
}
|
||||
|
||||
/** Gets a `session-type` child XML element of this `session` XML element. */
|
||||
EjbJarSessionTypeElement getASessionTypeElement() { result = this.getAChild() }
|
||||
|
||||
/** Gets an `init-method` child XML element of this `session` XML element. */
|
||||
EjbJarInitMethodElement getAnInitMethodElement() { result = this.getAChild() }
|
||||
|
||||
// Convenience methods.
|
||||
/**
|
||||
* Gets a `method-name` child XML element of a `create-method`
|
||||
* XML element nested within this `session` XML element.
|
||||
*/
|
||||
XMLElement getACreateMethodNameElement() {
|
||||
result = getAnInitMethodElement().getACreateMethodElement().getAMethodNameElement()
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a `method-name` child XML element of a `bean-method`
|
||||
* XML element nested within this `session` XML element.
|
||||
*/
|
||||
XMLElement getABeanMethodNameElement() {
|
||||
result = getAnInitMethodElement().getABeanMethodElement().getAMethodNameElement()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A `message-driven` child XML element of a bean type element in an `ejb-jar.xml` file.
|
||||
*/
|
||||
class EjbJarMessageDrivenElement extends EjbJarBeanTypeElement {
|
||||
EjbJarMessageDrivenElement() { this.getName() = "message-driven" }
|
||||
}
|
||||
|
||||
/**
|
||||
* An `entity` child XML element of a bean type element in an `ejb-jar.xml` file.
|
||||
*/
|
||||
class EjbJarEntityElement extends EjbJarBeanTypeElement {
|
||||
EjbJarEntityElement() { this.getName() = "entity" }
|
||||
}
|
||||
|
||||
/** A `session-type` child XML element of a `session` element in an `ejb-jar.xml` file. */
|
||||
class EjbJarSessionTypeElement extends XMLElement {
|
||||
EjbJarSessionTypeElement() {
|
||||
this.getParent() instanceof EjbJarSessionElement and
|
||||
this.getName() = "session-type"
|
||||
}
|
||||
|
||||
/** Holds if the value of this `session-type` XML element is "Stateful". */
|
||||
predicate isStateful() { this.getACharactersSet().getCharacters() = "Stateful" }
|
||||
|
||||
/** Holds if the value of this `session-type` XML element is "Stateless". */
|
||||
predicate isStateless() { this.getACharactersSet().getCharacters() = "Stateless" }
|
||||
}
|
||||
|
||||
/** An `init-method` child XML element of a `session` element in an `ejb-jar.xml` file. */
|
||||
class EjbJarInitMethodElement extends XMLElement {
|
||||
EjbJarInitMethodElement() {
|
||||
this.getParent() instanceof EjbJarSessionElement and
|
||||
this.getName() = "init-method"
|
||||
}
|
||||
|
||||
/** Gets a `create-method` child XML element of this `init-method` XML element. */
|
||||
EjbJarCreateMethodElement getACreateMethodElement() {
|
||||
result = this.getAChild() and
|
||||
result.getName() = "create-method"
|
||||
}
|
||||
|
||||
/** Gets a `bean-method` child XML element of this `init-method` XML element. */
|
||||
EjbJarBeanMethodElement getABeanMethodElement() {
|
||||
result = this.getAChild() and
|
||||
result.getName() = "bean-method"
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A child XML element of an `init-method` element in an `ejb-jar.xml` file.
|
||||
*
|
||||
* This is either a `create-method` element, or a `bean-method` element.
|
||||
*/
|
||||
abstract class EjbJarInitMethodChildElement extends XMLElement {
|
||||
/** Gets a `method-name` child XML element of this `create-method` or `bean-method` XML element. */
|
||||
XMLElement getAMethodNameElement() {
|
||||
result = this.getAChild() and
|
||||
result.getName() = "method-name"
|
||||
}
|
||||
}
|
||||
|
||||
/** A `create-method` child XML element of an `init-method` element in an `ejb-jar.xml` file. */
|
||||
class EjbJarCreateMethodElement extends EjbJarInitMethodChildElement {
|
||||
EjbJarCreateMethodElement() {
|
||||
this.getParent() instanceof EjbJarInitMethodElement and
|
||||
@@ -168,6 +234,7 @@ class EjbJarCreateMethodElement extends EjbJarInitMethodChildElement {
|
||||
}
|
||||
}
|
||||
|
||||
/** A `bean-method` child XML element of an `init-method` element in an `ejb-jar.xml` file. */
|
||||
class EjbJarBeanMethodElement extends EjbJarInitMethodChildElement {
|
||||
EjbJarBeanMethodElement() {
|
||||
this.getParent() instanceof EjbJarInitMethodElement and
|
||||
|
||||
@@ -1,10 +1,12 @@
|
||||
import java
|
||||
import EJB
|
||||
|
||||
/*
|
||||
/**
|
||||
* Provides classes and predicates for modeling
|
||||
* EJB Programming Restrictions (see EJB 3.0 specification, section 21.1.2).
|
||||
*/
|
||||
|
||||
import java
|
||||
import EJB
|
||||
|
||||
/** A method or constructor that may not be called from an EJB. */
|
||||
abstract class ForbiddenCallable extends Callable { }
|
||||
|
||||
/**
|
||||
@@ -47,6 +49,7 @@ predicate ejbCalls(Callable origin, ForbiddenCallable target, Call call) {
|
||||
* Specification of "forbidden callables".
|
||||
*/
|
||||
|
||||
/** A method or constructor that may not be called by an EJB due to container interference. */
|
||||
class ForbiddenContainerInterferenceCallable extends ForbiddenCallable {
|
||||
ForbiddenContainerInterferenceCallable() {
|
||||
this.getDeclaringType().getASupertype*().getSourceDeclaration() instanceof ClassLoaderClass or
|
||||
@@ -55,18 +58,21 @@ class ForbiddenContainerInterferenceCallable extends ForbiddenCallable {
|
||||
}
|
||||
}
|
||||
|
||||
/** A method or constructor involving file input or output that may not be called by an EJB. */
|
||||
class ForbiddenFileCallable extends ForbiddenCallable {
|
||||
ForbiddenFileCallable() {
|
||||
this.getDeclaringType().getASupertype*().getSourceDeclaration() instanceof FileInputOutputClass
|
||||
}
|
||||
}
|
||||
|
||||
/** A method or constructor involving graphics operations that may not be called by an EJB. */
|
||||
class ForbiddenGraphicsCallable extends ForbiddenCallable {
|
||||
ForbiddenGraphicsCallable() {
|
||||
this.getDeclaringType().getASupertype*().getPackage() instanceof GraphicsPackage
|
||||
}
|
||||
}
|
||||
|
||||
/** A method or constructor involving native code that may not be called by an EJB. */
|
||||
class ForbiddenNativeCallable extends ForbiddenCallable {
|
||||
ForbiddenNativeCallable() {
|
||||
this.isNative() or
|
||||
@@ -74,32 +80,38 @@ class ForbiddenNativeCallable extends ForbiddenCallable {
|
||||
}
|
||||
}
|
||||
|
||||
/** A method or constructor involving reflection that may not be called by and EJB. */
|
||||
class ForbiddenReflectionCallable extends ForbiddenCallable {
|
||||
ForbiddenReflectionCallable() {
|
||||
this.getDeclaringType().getASupertype*().getPackage() instanceof ReflectionPackage
|
||||
}
|
||||
}
|
||||
|
||||
/** A method or constructor involving security configuration that may not be called by an EJB. */
|
||||
class ForbiddenSecurityConfigurationCallable extends ForbiddenCallable {
|
||||
ForbiddenSecurityConfigurationCallable() {
|
||||
this.getDeclaringType().getASupertype*().getSourceDeclaration() instanceof SecurityConfigClass
|
||||
}
|
||||
}
|
||||
|
||||
/** A method or constructor involving serialization that may not be called by an EJB. */
|
||||
class ForbiddenSerializationCallable extends ForbiddenCallable {
|
||||
ForbiddenSerializationCallable() { this instanceof ForbiddenSerializationMethod }
|
||||
}
|
||||
|
||||
/** A method or constructor involving network factory operations that may not be called by an EJB. */
|
||||
class ForbiddenSetFactoryCallable extends ForbiddenCallable {
|
||||
ForbiddenSetFactoryCallable() { this instanceof ForbiddenSetFactoryMethod }
|
||||
}
|
||||
|
||||
/** A method or constructor involving server socket operations that may not be called by an EJB. */
|
||||
class ForbiddenServerSocketCallable extends ForbiddenCallable {
|
||||
ForbiddenServerSocketCallable() {
|
||||
this.getDeclaringType().getASupertype*().getSourceDeclaration() instanceof ServerSocketsClass
|
||||
}
|
||||
}
|
||||
|
||||
/** A method or constructor involving synchronization that may not be called by an EJB. */
|
||||
class ForbiddenSynchronizationCallable extends ForbiddenCallable {
|
||||
ForbiddenSynchronizationCallable() {
|
||||
this.isSynchronized()
|
||||
@@ -112,26 +124,37 @@ class ForbiddenSynchronizationCallable extends ForbiddenCallable {
|
||||
}
|
||||
}
|
||||
|
||||
/** A method or constructor involving static field access that may not be called by an EJB. */
|
||||
class ForbiddenStaticFieldCallable extends ForbiddenCallable {
|
||||
ForbiddenStaticFieldCallable() { exists(forbiddenStaticFieldUse(this)) }
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets an access to a non-final static field in callable `c`
|
||||
* that is disallowed by the EJB specification.
|
||||
*/
|
||||
FieldAccess forbiddenStaticFieldUse(Callable c) {
|
||||
result.getEnclosingCallable() = c and
|
||||
result.getField().isStatic() and
|
||||
not result.getField().isFinal()
|
||||
}
|
||||
|
||||
/** A method or constructor involving thread operations that may not be called by an EJB. */
|
||||
class ForbiddenThreadingCallable extends ForbiddenCallable {
|
||||
ForbiddenThreadingCallable() {
|
||||
this.getDeclaringType().getASupertype*().getSourceDeclaration() instanceof ThreadingClass
|
||||
}
|
||||
}
|
||||
|
||||
/** A method or constructor referencing `this` that may not be called by an EJB. */
|
||||
class ForbiddenThisCallable extends ForbiddenCallable {
|
||||
ForbiddenThisCallable() { exists(forbiddenThisUse(this)) }
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets an access to `this` in callable `c`
|
||||
* that is disallowed by the EJB specification.
|
||||
*/
|
||||
ThisAccess forbiddenThisUse(Callable c) {
|
||||
result.getEnclosingCallable() = c and
|
||||
(
|
||||
@@ -144,6 +167,7 @@ ThisAccess forbiddenThisUse(Callable c) {
|
||||
* Specification of "forbidden packages".
|
||||
*/
|
||||
|
||||
/** The package `java.lang.reflect` or a subpackage thereof. */
|
||||
class ReflectionPackage extends Package {
|
||||
ReflectionPackage() {
|
||||
this.getName() = "java.lang.reflect" or
|
||||
@@ -151,6 +175,7 @@ class ReflectionPackage extends Package {
|
||||
}
|
||||
}
|
||||
|
||||
/** The package `java.awt` or `javax.swing` or a subpackage thereof. */
|
||||
class GraphicsPackage extends Package {
|
||||
GraphicsPackage() {
|
||||
this.getName() = "java.awt" or
|
||||
@@ -160,6 +185,7 @@ class GraphicsPackage extends Package {
|
||||
}
|
||||
}
|
||||
|
||||
/** The package `java.util.concurrent` or a subpackage thereof. */
|
||||
class ConcurrentPackage extends Package {
|
||||
ConcurrentPackage() {
|
||||
this.getName() = "java.util.concurrent" or
|
||||
@@ -171,6 +197,7 @@ class ConcurrentPackage extends Package {
|
||||
* Specification of "forbidden classes".
|
||||
*/
|
||||
|
||||
/** The class `java.lang.Thread` or `java.lang.ThreadGroup`. */
|
||||
class ThreadingClass extends Class {
|
||||
ThreadingClass() {
|
||||
this.hasQualifiedName("java.lang", "Thread") or
|
||||
@@ -178,6 +205,10 @@ class ThreadingClass extends Class {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The class `java.net.ServerSocket`, `java.net.MulticastSocket`
|
||||
* or `java.nio.channels.ServerSocketChannel`.
|
||||
*/
|
||||
class ServerSocketsClass extends Class {
|
||||
ServerSocketsClass() {
|
||||
this.hasQualifiedName("java.net", "ServerSocket") or
|
||||
@@ -186,6 +217,10 @@ class ServerSocketsClass extends Class {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A class in the package `java.security` named `Policy`,
|
||||
* `Security`, `Provider`, `Signer` or `Identity`.
|
||||
*/
|
||||
class SecurityConfigClass extends Class {
|
||||
SecurityConfigClass() {
|
||||
this.hasQualifiedName("java.security", "Policy") or
|
||||
@@ -196,14 +231,17 @@ class SecurityConfigClass extends Class {
|
||||
}
|
||||
}
|
||||
|
||||
/** The class `java.lang.ClassLoader`. */
|
||||
class ClassLoaderClass extends Class {
|
||||
ClassLoaderClass() { this.hasQualifiedName("java.lang", "ClassLoader") }
|
||||
}
|
||||
|
||||
/** The class `java.lang.SecurityManager`. */
|
||||
class SecurityManagerClass extends Class {
|
||||
SecurityManagerClass() { this.hasQualifiedName("java.lang", "SecurityManager") }
|
||||
}
|
||||
|
||||
/** A class involving file input or output. */
|
||||
class FileInputOutputClass extends Class {
|
||||
FileInputOutputClass() {
|
||||
this.hasQualifiedName("java.io", "File") or
|
||||
@@ -222,7 +260,7 @@ class FileInputOutputClass extends Class {
|
||||
* Specification of "forbidden methods".
|
||||
*/
|
||||
|
||||
// Forbidden container interference.
|
||||
/** A method that may cause EJB container interference. */
|
||||
class ForbiddenContainerInterferenceMethod extends Method {
|
||||
ForbiddenContainerInterferenceMethod() {
|
||||
this instanceof SystemExitMethod or
|
||||
@@ -236,6 +274,10 @@ class ForbiddenContainerInterferenceMethod extends Method {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A method named `exit` declared in
|
||||
* the class `java.lang.System`.
|
||||
*/
|
||||
class SystemExitMethod extends Method {
|
||||
SystemExitMethod() {
|
||||
this.hasName("exit") and
|
||||
@@ -249,6 +291,10 @@ class SystemExitMethod extends Method {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A method named `exit` or `halt` declared in
|
||||
* the class `java.lang.Runtime` or a subclass thereof.
|
||||
*/
|
||||
class RuntimeExitOrHaltMethod extends Method {
|
||||
RuntimeExitOrHaltMethod() {
|
||||
(this.hasName("exit") or this.hasName("halt")) and
|
||||
@@ -262,6 +308,10 @@ class RuntimeExitOrHaltMethod extends Method {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A method named `addShutdownHook` or `removeShutdownHook` declared in
|
||||
* the class `java.lang.Runtime` or a subclass thereof.
|
||||
*/
|
||||
class RuntimeAddOrRemoveShutdownHookMethod extends Method {
|
||||
RuntimeAddOrRemoveShutdownHookMethod() {
|
||||
(this.hasName("addShutdownHook") or this.hasName("removeShutdownHook")) and
|
||||
@@ -275,6 +325,10 @@ class RuntimeAddOrRemoveShutdownHookMethod extends Method {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A method named `setErr` or `setOut` declared in
|
||||
* the class `java.lang.System`.
|
||||
*/
|
||||
class SystemSetPrintStreamMethod extends Method {
|
||||
SystemSetPrintStreamMethod() {
|
||||
(this.hasName("setErr") or this.hasName("setOut")) and
|
||||
@@ -288,6 +342,10 @@ class SystemSetPrintStreamMethod extends Method {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A method named `setIn` declared in
|
||||
* the class `java.lang.System`.
|
||||
*/
|
||||
class SystemSetInputStreamMethod extends Method {
|
||||
SystemSetInputStreamMethod() {
|
||||
this.hasName("setIn") and
|
||||
@@ -301,6 +359,10 @@ class SystemSetInputStreamMethod extends Method {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A method named `getSecurityManager` declared in
|
||||
* the class `java.lang.System`.
|
||||
*/
|
||||
class SystemGetSecurityManagerMethod extends Method {
|
||||
SystemGetSecurityManagerMethod() {
|
||||
this.hasName("getSecurityManager") and
|
||||
@@ -313,6 +375,10 @@ class SystemGetSecurityManagerMethod extends Method {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A method named `setSecurityManager` declared in
|
||||
* the class `java.lang.System`.
|
||||
*/
|
||||
class SystemSetSecurityManagerMethod extends Method {
|
||||
SystemSetSecurityManagerMethod() {
|
||||
this.hasName("setSecurityManager") and
|
||||
@@ -326,6 +392,10 @@ class SystemSetSecurityManagerMethod extends Method {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A method named `inheritedChannel` declared in
|
||||
* the class `java.lang.System`.
|
||||
*/
|
||||
class SystemInheritedChannelMethod extends Method {
|
||||
SystemInheritedChannelMethod() {
|
||||
this.hasName("inheritedChannel") and
|
||||
@@ -338,7 +408,7 @@ class SystemInheritedChannelMethod extends Method {
|
||||
}
|
||||
}
|
||||
|
||||
// Forbidden serialization.
|
||||
/** A method involving serialization that may not be called from an EJB. */
|
||||
class ForbiddenSerializationMethod extends Method {
|
||||
ForbiddenSerializationMethod() {
|
||||
this instanceof EnableReplaceObjectMethod or
|
||||
@@ -350,6 +420,10 @@ class ForbiddenSerializationMethod extends Method {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A method named `enableReplaceObject` declared in
|
||||
* the class `java.io.ObjectInputStream` or a subclass thereof.
|
||||
*/
|
||||
class EnableReplaceObjectMethod extends Method {
|
||||
EnableReplaceObjectMethod() {
|
||||
this.hasName("enableReplaceObject") and
|
||||
@@ -363,6 +437,10 @@ class EnableReplaceObjectMethod extends Method {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A method named `replaceObject` declared in
|
||||
* the class `java.io.ObjectInputStream` or a subclass thereof.
|
||||
*/
|
||||
class ReplaceObjectMethod extends Method {
|
||||
ReplaceObjectMethod() {
|
||||
this.hasName("replaceObject") and
|
||||
@@ -376,6 +454,10 @@ class ReplaceObjectMethod extends Method {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A method named `enableResolveObject` declared in
|
||||
* the class `java.io.ObjectInputStream` or a subclass thereof.
|
||||
*/
|
||||
class EnableResolveObjectMethod extends Method {
|
||||
EnableResolveObjectMethod() {
|
||||
this.hasName("enableResolveObject") and
|
||||
@@ -389,6 +471,10 @@ class EnableResolveObjectMethod extends Method {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A method named `resolveObject` declared in
|
||||
* the class `java.io.ObjectInputStream` or a subclass thereof.
|
||||
*/
|
||||
class ResolveObjectMethod extends Method {
|
||||
ResolveObjectMethod() {
|
||||
this.hasName("resolveObject") and
|
||||
@@ -402,6 +488,10 @@ class ResolveObjectMethod extends Method {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A method named `resolveClass` declared in
|
||||
* the class `java.io.ObjectInputStream` or a subclass thereof.
|
||||
*/
|
||||
class ResolveClassMethod extends Method {
|
||||
ResolveClassMethod() {
|
||||
this.hasName("resolveClass") and
|
||||
@@ -415,6 +505,10 @@ class ResolveClassMethod extends Method {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A method named `resolveProxyClass` declared in
|
||||
* the class `java.io.ObjectInputStream` or a subclass thereof.
|
||||
*/
|
||||
class ResolveProxyClassMethod extends Method {
|
||||
ResolveProxyClassMethod() {
|
||||
this.hasName("resolveProxyClass") and
|
||||
@@ -434,7 +528,7 @@ class ResolveProxyClassMethod extends Method {
|
||||
}
|
||||
}
|
||||
|
||||
// Forbidden "set factory" methods.
|
||||
/** A method involving network factory operations that may not be called from an EJB. */
|
||||
class ForbiddenSetFactoryMethod extends Method {
|
||||
ForbiddenSetFactoryMethod() {
|
||||
this instanceof SetSocketFactoryMethod or
|
||||
@@ -443,6 +537,10 @@ class ForbiddenSetFactoryMethod extends Method {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A method named `setSocketFactory` declared in
|
||||
* the class `java.net.ServerSocket` or a subclass thereof.
|
||||
*/
|
||||
class SetSocketFactoryMethod extends Method {
|
||||
SetSocketFactoryMethod() {
|
||||
this.hasName("setSocketFactory") and
|
||||
@@ -461,6 +559,10 @@ class SetSocketFactoryMethod extends Method {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A method named `setSocketImplFactory` declared in
|
||||
* the class `java.net.Socket` or a subclass thereof.
|
||||
*/
|
||||
class SetSocketImplFactoryMethod extends Method {
|
||||
SetSocketImplFactoryMethod() {
|
||||
this.hasName("setSocketImplFactory") and
|
||||
@@ -479,6 +581,10 @@ class SetSocketImplFactoryMethod extends Method {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A method named `setURLStreamHandlerFactory` declared in
|
||||
* the class `java.net.URL` or a subclass thereof.
|
||||
*/
|
||||
class SetUrlStreamHandlerFactoryMethod extends Method {
|
||||
SetUrlStreamHandlerFactoryMethod() {
|
||||
this.hasName("setURLStreamHandlerFactory") and
|
||||
@@ -497,7 +603,7 @@ class SetUrlStreamHandlerFactoryMethod extends Method {
|
||||
}
|
||||
}
|
||||
|
||||
// Forbidden native code methods.
|
||||
/** A method involving native code that may not be called by an EJB. */
|
||||
class ForbiddenNativeCodeMethod extends Method {
|
||||
ForbiddenNativeCodeMethod() {
|
||||
this instanceof SystemOrRuntimeLoadLibraryMethod or
|
||||
@@ -505,6 +611,10 @@ class ForbiddenNativeCodeMethod extends Method {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A method named `load` or `loadLibrary` declared in the class
|
||||
* `java.lang.System` or `java.lang.Runtime` or a subclass thereof.
|
||||
*/
|
||||
class SystemOrRuntimeLoadLibraryMethod extends Method {
|
||||
SystemOrRuntimeLoadLibraryMethod() {
|
||||
(this.hasName("load") or this.hasName("loadLibrary")) and
|
||||
@@ -525,6 +635,10 @@ class SystemOrRuntimeLoadLibraryMethod extends Method {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A method named `exec` declared in the class
|
||||
* `java.lang.Runtime` or in a subclass thereof.
|
||||
*/
|
||||
class RuntimeExecMethod extends Method {
|
||||
RuntimeExecMethod() {
|
||||
this.hasName("exec") and
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
/** Provides classes and predicates for working with Java Server Faces annotations. */
|
||||
|
||||
import default
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,7 +1,10 @@
|
||||
import java
|
||||
import semmle.code.java.Maps
|
||||
import SpringWeb
|
||||
import SpringWebClient
|
||||
|
||||
/**
|
||||
* An annotation type that identifies Spring components.
|
||||
* An annotation type that identifies Spring controllers.
|
||||
*/
|
||||
class SpringControllerAnnotation extends AnnotationType {
|
||||
SpringControllerAnnotation() {
|
||||
@@ -13,6 +16,15 @@ class SpringControllerAnnotation extends AnnotationType {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* An annotation type that identifies Spring rest controllers.
|
||||
*
|
||||
* Rest controllers are the same as controllers, but imply the `@ResponseBody` annotation.
|
||||
*/
|
||||
class SpringRestControllerAnnotation extends SpringControllerAnnotation {
|
||||
SpringRestControllerAnnotation() { hasName("RestController") }
|
||||
}
|
||||
|
||||
/**
|
||||
* A class annotated, directly or indirectly, as a Spring `Controller`.
|
||||
*/
|
||||
@@ -20,6 +32,13 @@ class SpringController extends Class {
|
||||
SpringController() { getAnAnnotation().getType() instanceof SpringControllerAnnotation }
|
||||
}
|
||||
|
||||
/**
|
||||
* A class annotated, directly or indirectly, as a Spring `RestController`.
|
||||
*/
|
||||
class SpringRestController extends SpringController {
|
||||
SpringRestController() { getAnAnnotation().getType() instanceof SpringRestControllerAnnotation }
|
||||
}
|
||||
|
||||
/**
|
||||
* A method on a Spring controller which is accessed by the Spring MVC framework.
|
||||
*/
|
||||
@@ -38,7 +57,7 @@ class SpringModelAttributeMethod extends SpringControllerMethod {
|
||||
// not declared with @Inherited.
|
||||
exists(Method superMethod |
|
||||
this.overrides*(superMethod) and
|
||||
superMethod.hasAnnotation("org.springframework.web.bind.annotation", "ModelAttribute")
|
||||
superMethod.getAnAnnotation() instanceof SpringModelAttributeAnnotation
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -58,19 +77,180 @@ class SpringInitBinderMethod extends SpringControllerMethod {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* An `AnnotationType` that is used to indicate a `RequestMapping`.
|
||||
*/
|
||||
class SpringRequestMappingAnnotationType extends AnnotationType {
|
||||
SpringRequestMappingAnnotationType() {
|
||||
// `@RequestMapping` used directly as an annotation.
|
||||
hasQualifiedName("org.springframework.web.bind.annotation", "RequestMapping")
|
||||
or
|
||||
// `@RequestMapping` can be used as a meta-annotation on other annotation types, e.g. GetMapping, PostMapping etc.
|
||||
getAnAnnotation().getType() instanceof SpringRequestMappingAnnotationType
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* An `AnnotationType` that is used to indicate a `ResponseBody`.
|
||||
*/
|
||||
class SpringResponseBodyAnnotationType extends AnnotationType {
|
||||
SpringResponseBodyAnnotationType() {
|
||||
// `@ResponseBody` used directly as an annotation.
|
||||
hasQualifiedName("org.springframework.web.bind.annotation", "ResponseBody")
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A method on a Spring controller that is executed in response to a web request.
|
||||
*/
|
||||
class SpringRequestMappingMethod extends SpringControllerMethod {
|
||||
Annotation requestMappingAnnotation;
|
||||
|
||||
SpringRequestMappingMethod() {
|
||||
// Any method that declares the @RequestMapping annotation, or overrides a method that declares
|
||||
// the annotation. We have to do this explicit check because the @RequestMapping annotation is
|
||||
// not declared with @Inherited.
|
||||
exists(Method superMethod |
|
||||
this.overrides*(superMethod) and
|
||||
superMethod.hasAnnotation("org.springframework.web.bind.annotation", "RequestMapping")
|
||||
requestMappingAnnotation = superMethod.getAnAnnotation() and
|
||||
requestMappingAnnotation.getType() instanceof SpringRequestMappingAnnotationType
|
||||
)
|
||||
}
|
||||
|
||||
/** Gets a request mapping parameter. */
|
||||
SpringRequestMappingParameter getARequestParameter() { result = getAParameter() }
|
||||
|
||||
/** Gets the "produces" @RequestMapping annotation value, if present. */
|
||||
string getProduces() {
|
||||
result =
|
||||
requestMappingAnnotation.getValue("produces").(CompileTimeConstantExpr).getStringValue()
|
||||
}
|
||||
|
||||
/** Holds if this is considered an `@ResponseBody` method. */
|
||||
predicate isResponseBody() {
|
||||
getAnAnnotation().getType() instanceof SpringResponseBodyAnnotationType or
|
||||
getDeclaringType().getAnAnnotation().getType() instanceof SpringResponseBodyAnnotationType or
|
||||
getDeclaringType() instanceof SpringRestController
|
||||
}
|
||||
}
|
||||
|
||||
/** A Spring framework annotation indicating remote user input from servlets. */
|
||||
class SpringServletInputAnnotation extends Annotation {
|
||||
SpringServletInputAnnotation() {
|
||||
exists(AnnotationType a |
|
||||
a = this.getType() and
|
||||
a.getPackage().getName() = "org.springframework.web.bind.annotation"
|
||||
|
|
||||
a.hasName("MatrixVariable") or
|
||||
a.hasName("RequestParam") or
|
||||
a.hasName("RequestHeader") or
|
||||
a.hasName("CookieValue") or
|
||||
a.hasName("RequestPart") or
|
||||
a.hasName("PathVariable") or
|
||||
a.hasName("RequestBody")
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/** An annotation of the type `org.springframework.web.bind.annotation.ModelAttribute`. */
|
||||
class SpringModelAttributeAnnotation extends Annotation {
|
||||
SpringModelAttributeAnnotation() {
|
||||
getType().hasQualifiedName("org.springframework.web.bind.annotation", "ModelAttribute")
|
||||
}
|
||||
}
|
||||
|
||||
/** A parameter of a `SpringRequestMappingMethod`. */
|
||||
class SpringRequestMappingParameter extends Parameter {
|
||||
SpringRequestMappingParameter() { getCallable() instanceof SpringRequestMappingMethod }
|
||||
|
||||
/** Holds if the parameter should not be consider a direct source of taint. */
|
||||
predicate isNotDirectlyTaintedInput() {
|
||||
getType().(RefType).getAnAncestor() instanceof SpringWebRequest or
|
||||
getType().(RefType).getAnAncestor() instanceof SpringNativeWebRequest or
|
||||
getType().(RefType).getAnAncestor().hasQualifiedName("javax.servlet", "ServletRequest") or
|
||||
getType().(RefType).getAnAncestor().hasQualifiedName("javax.servlet", "ServletResponse") or
|
||||
getType().(RefType).getAnAncestor().hasQualifiedName("javax.servlet.http", "HttpSession") or
|
||||
getType().(RefType).getAnAncestor().hasQualifiedName("javax.servlet.http", "PushBuilder") or
|
||||
getType().(RefType).getAnAncestor().hasQualifiedName("java.security", "Principal") or
|
||||
getType().(RefType).getAnAncestor().hasQualifiedName("org.springframework.http", "HttpMethod") or
|
||||
getType().(RefType).getAnAncestor().hasQualifiedName("java.util", "Locale") or
|
||||
getType().(RefType).getAnAncestor().hasQualifiedName("java.util", "TimeZone") or
|
||||
getType().(RefType).getAnAncestor().hasQualifiedName("java.time", "ZoneId") or
|
||||
getType().(RefType).getAnAncestor().hasQualifiedName("java.io", "OutputStream") or
|
||||
getType().(RefType).getAnAncestor().hasQualifiedName("java.io", "Writer") or
|
||||
getType()
|
||||
.(RefType)
|
||||
.getAnAncestor()
|
||||
.hasQualifiedName("org.springframework.web.servlet.mvc.support", "RedirectAttributes") or
|
||||
// Also covers BindingResult. Note, you can access the field value through this interface, which should be considered tainted
|
||||
getType().(RefType).getAnAncestor().hasQualifiedName("org.springframework.validation", "Errors") or
|
||||
getType()
|
||||
.(RefType)
|
||||
.getAnAncestor()
|
||||
.hasQualifiedName("org.springframework.web.bind.support", "SessionStatus") or
|
||||
getType()
|
||||
.(RefType)
|
||||
.getAnAncestor()
|
||||
.hasQualifiedName("org.springframework.web.util", "UriComponentsBuilder") or
|
||||
getType()
|
||||
.(RefType)
|
||||
.getAnAncestor()
|
||||
.hasQualifiedName("org.springframework.data.domain", "Pageable") or
|
||||
this instanceof SpringModel
|
||||
}
|
||||
|
||||
private predicate isExplicitlyTaintedInput() {
|
||||
// InputStream or Reader parameters allow access to the body of a request
|
||||
getType().(RefType).getAnAncestor().hasQualifiedName("java.io", "InputStream") or
|
||||
getType().(RefType).getAnAncestor().hasQualifiedName("java.io", "Reader") or
|
||||
// The SpringServletInputAnnotations allow access to the URI, request parameters, cookie values and the body of the request
|
||||
this.getAnAnnotation() instanceof SpringServletInputAnnotation or
|
||||
// HttpEntity is like @RequestBody, but with a wrapper including the headers
|
||||
// TODO model unwrapping aspects
|
||||
getType().(RefType).getASourceSupertype*() instanceof SpringHttpEntity or
|
||||
this
|
||||
.getAnAnnotation()
|
||||
.getType()
|
||||
.hasQualifiedName("org.springframework.web.bind.annotation", "RequestAttribute") or
|
||||
this
|
||||
.getAnAnnotation()
|
||||
.getType()
|
||||
.hasQualifiedName("org.springframework.web.bind.annotation", "SessionAttribute")
|
||||
}
|
||||
|
||||
private predicate isImplicitRequestParam() {
|
||||
// Any parameter which is not explicitly handled, is consider to be an `@RequestParam`, if
|
||||
// it is a simple bean property
|
||||
not isNotDirectlyTaintedInput() and
|
||||
not isExplicitlyTaintedInput() and
|
||||
(
|
||||
getType() instanceof PrimitiveType or
|
||||
getType() instanceof TypeString
|
||||
)
|
||||
}
|
||||
|
||||
private predicate isImplicitModelAttribute() {
|
||||
// Any parameter which is not explicitly handled, is consider to be an `@ModelAttribute`, if
|
||||
// it is not an implicit request param
|
||||
not isNotDirectlyTaintedInput() and
|
||||
not isExplicitlyTaintedInput() and
|
||||
not isImplicitRequestParam()
|
||||
}
|
||||
|
||||
/** Holds if this is an explicit or implicit `@ModelAttribute` parameter. */
|
||||
predicate isModelAttribute() {
|
||||
isImplicitModelAttribute() or
|
||||
getAnAnnotation() instanceof SpringModelAttributeAnnotation
|
||||
}
|
||||
|
||||
/** Holds if the input is tainted. */
|
||||
predicate isTaintedInput() {
|
||||
isExplicitlyTaintedInput()
|
||||
or
|
||||
// Any parameter which is not explicitly identified, is consider to be an `@RequestParam`, if
|
||||
// it is a simple bean property) or a @ModelAttribute if not
|
||||
not isNotDirectlyTaintedInput()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -90,7 +270,7 @@ abstract class SpringModel extends Parameter {
|
||||
* A `java.util.Map` can be accepted as the model parameter for a Spring `RequestMapping` method.
|
||||
*/
|
||||
class SpringModelPlainMap extends SpringModel {
|
||||
SpringModelPlainMap() { getType().(RefType).hasQualifiedName("java.util", "Map") }
|
||||
SpringModelPlainMap() { getType() instanceof MapType }
|
||||
|
||||
override RefType getATypeInModel() {
|
||||
exists(MethodAccess methodCall |
|
||||
@@ -133,3 +313,39 @@ class SpringModelResponseType extends RefType {
|
||||
exists(SpringModel model | usesType(model.getATypeInModel(), this))
|
||||
}
|
||||
}
|
||||
|
||||
/** Strips wrapper types. */
|
||||
private RefType stripType(Type t) {
|
||||
result = t or
|
||||
result = stripType(t.(Array).getComponentType()) or
|
||||
result = stripType(t.(ParameterizedType).getATypeArgument())
|
||||
}
|
||||
|
||||
/**
|
||||
* A user data type that may be populated from an HTTP request.
|
||||
*
|
||||
* This includes types directly referred to as either `@ModelAttribute` or `@RequestBody` parameters,
|
||||
* or types that are referred to by those types.
|
||||
*/
|
||||
class SpringUntrustedDataType extends RefType {
|
||||
SpringUntrustedDataType() {
|
||||
exists(SpringRequestMappingParameter p |
|
||||
p.isModelAttribute()
|
||||
or
|
||||
p.getAnAnnotation().(SpringServletInputAnnotation).getType().hasName("RequestBody")
|
||||
|
|
||||
this.fromSource() and
|
||||
this = stripType(p.getType())
|
||||
)
|
||||
or
|
||||
exists(SpringRestTemplateResponseEntityMethod rm |
|
||||
this = stripType(rm.getAReference().getType().(ParameterizedType).getTypeArgument(0)) and
|
||||
this.fromSource()
|
||||
)
|
||||
or
|
||||
exists(SpringUntrustedDataType mt |
|
||||
this = stripType(mt.getAField().getType()) and
|
||||
this.fromSource()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,40 @@
|
||||
/**
|
||||
* Provides classes for working with Spring classes and interfaces from
|
||||
* `org.springframework.http`.
|
||||
*/
|
||||
|
||||
import java
|
||||
|
||||
/** The class `org.springframework.http.HttpEntity` or an instantiation of it. */
|
||||
class SpringHttpEntity extends Class {
|
||||
SpringHttpEntity() {
|
||||
this.getSourceDeclaration().hasQualifiedName("org.springframework.http", "HttpEntity")
|
||||
}
|
||||
}
|
||||
|
||||
/** The class `org.springframework.http.RequestEntity` or an instantiation of it. */
|
||||
class SpringRequestEntity extends Class {
|
||||
SpringRequestEntity() {
|
||||
this.getSourceDeclaration().hasQualifiedName("org.springframework.http", "RequestEntity")
|
||||
}
|
||||
}
|
||||
|
||||
/** The class `org.springframework.http.ResponseEntity` or an instantiation of it. */
|
||||
class SpringResponseEntity extends Class {
|
||||
SpringResponseEntity() {
|
||||
this.getSourceDeclaration().hasQualifiedName("org.springframework.http", "ResponseEntity")
|
||||
}
|
||||
}
|
||||
|
||||
/** The nested class `BodyBuilder` in `org.springframework.http.ResponseEntity`. */
|
||||
class SpringResponseEntityBodyBuilder extends Interface {
|
||||
SpringResponseEntityBodyBuilder() {
|
||||
this.getSourceDeclaration().getEnclosingType() instanceof SpringResponseEntity and
|
||||
this.hasName("BodyBuilder")
|
||||
}
|
||||
}
|
||||
|
||||
/** The class `org.springframework.http.HttpHeaders`. */
|
||||
class SpringHttpHeaders extends Class {
|
||||
SpringHttpHeaders() { this.hasQualifiedName("org.springframework.http", "HttpHeaders") }
|
||||
}
|
||||
19
java/ql/src/semmle/code/java/frameworks/spring/SpringWeb.qll
Normal file
19
java/ql/src/semmle/code/java/frameworks/spring/SpringWeb.qll
Normal file
@@ -0,0 +1,19 @@
|
||||
/**
|
||||
* Provides classes for working with Spring web requests.
|
||||
*/
|
||||
|
||||
import java
|
||||
|
||||
/** An interface for web requests in the Spring framework. */
|
||||
class SpringWebRequest extends Class {
|
||||
SpringWebRequest() {
|
||||
this.hasQualifiedName("org.springframework.web.context.request", "WebRequest")
|
||||
}
|
||||
}
|
||||
|
||||
/** An interface for web requests in the Spring framework. */
|
||||
class SpringNativeWebRequest extends Class {
|
||||
SpringNativeWebRequest() {
|
||||
this.hasQualifiedName("org.springframework.web.context.request", "NativeWebRequest")
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
/**
|
||||
* Provides classes for working with Spring web clients.
|
||||
*/
|
||||
|
||||
import java
|
||||
import SpringHttp
|
||||
|
||||
/** The class `org.springframework.web.client.RestTemplate`. */
|
||||
class SpringRestTemplate extends Class {
|
||||
SpringRestTemplate() { this.hasQualifiedName("org.springframework.web.client", "RestTemplate") }
|
||||
}
|
||||
|
||||
/**
|
||||
* A method declared in `org.springframework.web.client.RestTemplate` that
|
||||
* returns a `SpringResponseEntity`.
|
||||
*/
|
||||
class SpringRestTemplateResponseEntityMethod extends Method {
|
||||
SpringRestTemplateResponseEntityMethod() {
|
||||
this.getDeclaringType() instanceof SpringRestTemplate and
|
||||
this.getReturnType() instanceof SpringResponseEntity
|
||||
}
|
||||
}
|
||||
|
||||
/** The interface `org.springframework.web.reactive.function.client.WebClient`. */
|
||||
class SpringWebClient extends Interface {
|
||||
SpringWebClient() {
|
||||
this.hasQualifiedName("org.springframework.web.reactive.function.client", "WebClient")
|
||||
}
|
||||
}
|
||||
@@ -1,3 +1,7 @@
|
||||
/**
|
||||
* Provides predicates and classes relating to encryption in Java.
|
||||
*/
|
||||
|
||||
import java
|
||||
|
||||
class SSLClass extends RefType {
|
||||
@@ -85,8 +89,10 @@ private string algorithmRegex(string algorithmString) {
|
||||
"((^|.*[A-Z]{2}|.*[^a-zA-Z])(" + algorithmString.toLowerCase() + ")([^a-z].*|$))"
|
||||
}
|
||||
|
||||
/** Gets a blacklist of algorithms that are known to be insecure. */
|
||||
private string algorithmBlacklist() {
|
||||
/**
|
||||
* Gets the name of an algorithm that is known to be insecure.
|
||||
*/
|
||||
string getAnInsecureAlgorithmName() {
|
||||
result = "DES" or
|
||||
result = "RC2" or
|
||||
result = "RC4" or
|
||||
@@ -94,32 +100,40 @@ private string algorithmBlacklist() {
|
||||
result = "ARCFOUR" // a variant of RC4
|
||||
}
|
||||
|
||||
// These are only bad if they're being used for encryption.
|
||||
private string hashAlgorithmBlacklist() {
|
||||
/**
|
||||
* Gets the name of a hash algorithm that is insecure if it is being used for
|
||||
* encryption.
|
||||
*/
|
||||
string getAnInsecureHashAlgorithmName() {
|
||||
result = "SHA1" or
|
||||
result = "MD5"
|
||||
}
|
||||
|
||||
private string rankedAlgorithmBlacklist(int i) {
|
||||
private string rankedInsecureAlgorithm(int i) {
|
||||
// In this case we know these are being used for encryption, so we want to match
|
||||
// weak hash algorithms too.
|
||||
result = rank[i](string s | s = algorithmBlacklist() or s = hashAlgorithmBlacklist())
|
||||
}
|
||||
|
||||
private string algorithmBlacklistString(int i) {
|
||||
i = 1 and result = rankedAlgorithmBlacklist(i)
|
||||
or
|
||||
result = rankedAlgorithmBlacklist(i) + "|" + algorithmBlacklistString(i - 1)
|
||||
}
|
||||
|
||||
/** Gets a regex for matching strings that look like they contain a blacklisted algorithm. */
|
||||
string algorithmBlacklistRegex() {
|
||||
result =
|
||||
algorithmRegex(algorithmBlacklistString(max(int i | exists(rankedAlgorithmBlacklist(i)))))
|
||||
rank[i](string s | s = getAnInsecureAlgorithmName() or s = getAnInsecureHashAlgorithmName())
|
||||
}
|
||||
|
||||
/** Gets a whitelist of algorithms that are known to be secure. */
|
||||
private string algorithmWhitelist() {
|
||||
private string insecureAlgorithmString(int i) {
|
||||
i = 1 and result = rankedInsecureAlgorithm(i)
|
||||
or
|
||||
result = rankedInsecureAlgorithm(i) + "|" + insecureAlgorithmString(i - 1)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the regular expression used for matching strings that look like they
|
||||
* contain an algorithm that is known to be insecure.
|
||||
*/
|
||||
string getInsecureAlgorithmRegex() {
|
||||
result = algorithmRegex(insecureAlgorithmString(max(int i | exists(rankedInsecureAlgorithm(i)))))
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the name of an algorithm that is known to be secure.
|
||||
*/
|
||||
string getASecureAlgorithmName() {
|
||||
result = "RSA" or
|
||||
result = "SHA256" or
|
||||
result = "SHA512" or
|
||||
@@ -130,20 +144,50 @@ private string algorithmWhitelist() {
|
||||
result = "ECIES"
|
||||
}
|
||||
|
||||
private string rankedAlgorithmWhitelist(int i) { result = rank[i](algorithmWhitelist()) }
|
||||
private string rankedSecureAlgorithm(int i) { result = rank[i](getASecureAlgorithmName()) }
|
||||
|
||||
private string algorithmWhitelistString(int i) {
|
||||
i = 1 and result = rankedAlgorithmWhitelist(i)
|
||||
private string secureAlgorithmString(int i) {
|
||||
i = 1 and result = rankedSecureAlgorithm(i)
|
||||
or
|
||||
result = rankedAlgorithmWhitelist(i) + "|" + algorithmWhitelistString(i - 1)
|
||||
result = rankedSecureAlgorithm(i) + "|" + secureAlgorithmString(i - 1)
|
||||
}
|
||||
|
||||
/** Gets a regex for matching strings that look like they contain a whitelisted algorithm. */
|
||||
string algorithmWhitelistRegex() {
|
||||
result =
|
||||
algorithmRegex(algorithmWhitelistString(max(int i | exists(rankedAlgorithmWhitelist(i)))))
|
||||
/**
|
||||
* Gets a regular expression for matching strings that look like they
|
||||
* contain an algorithm that is known to be secure.
|
||||
*/
|
||||
string getSecureAlgorithmRegex() {
|
||||
result = algorithmRegex(secureAlgorithmString(max(int i | exists(rankedSecureAlgorithm(i)))))
|
||||
}
|
||||
|
||||
/**
|
||||
* DEPRECATED: Terminology has been updated. Use `getAnInsecureAlgorithmName()`
|
||||
* instead.
|
||||
*/
|
||||
deprecated string algorithmBlacklist() { result = getAnInsecureAlgorithmName() }
|
||||
|
||||
/**
|
||||
* DEPRECATED: Terminology has been updated. Use
|
||||
* `getAnInsecureHashAlgorithmName()` instead.
|
||||
*/
|
||||
deprecated string hashAlgorithmBlacklist() { result = getAnInsecureHashAlgorithmName() }
|
||||
|
||||
/**
|
||||
* DEPRECATED: Terminology has been updated. Use `getInsecureAlgorithmRegex()` instead.
|
||||
*/
|
||||
deprecated string algorithmBlacklistRegex() { result = getInsecureAlgorithmRegex() }
|
||||
|
||||
/**
|
||||
* DEPRECATED: Terminology has been updated. Use `getASecureAlgorithmName()`
|
||||
* instead.
|
||||
*/
|
||||
deprecated string algorithmWhitelist() { result = getASecureAlgorithmName() }
|
||||
|
||||
/**
|
||||
* DEPRECATED: Terminology has been updated. Use `getSecureAlgorithmRegex()` instead.
|
||||
*/
|
||||
deprecated string algorithmWhitelistRegex() { result = getSecureAlgorithmRegex() }
|
||||
|
||||
/**
|
||||
* Any use of a cryptographic element that specifies an encryption
|
||||
* algorithm. For example, methods returning ciphers, decryption methods,
|
||||
@@ -220,11 +264,16 @@ abstract class JavaSecurityAlgoSpec extends CryptoAlgoSpec { }
|
||||
class JavaSecurityMessageDigest extends JavaSecurityAlgoSpec {
|
||||
JavaSecurityMessageDigest() {
|
||||
exists(Constructor c | c.getAReference() = this |
|
||||
c.getDeclaringType().getQualifiedName() = "java.security.MessageDigest"
|
||||
c.getDeclaringType().hasQualifiedName("java.security", "MessageDigest")
|
||||
)
|
||||
or
|
||||
exists(Method m | m.getAReference() = this |
|
||||
m.getDeclaringType().hasQualifiedName("java.security", "MessageDigest") and
|
||||
m.getName() = "getInstance"
|
||||
)
|
||||
}
|
||||
|
||||
override Expr getAlgoSpec() { result = this.(ConstructorCall).getArgument(0) }
|
||||
override Expr getAlgoSpec() { result = this.(Call).getArgument(0) }
|
||||
}
|
||||
|
||||
class JavaSecuritySignature extends JavaSecurityAlgoSpec {
|
||||
|
||||
146
java/ql/src/semmle/code/java/security/ExternalAPIs.qll
Normal file
146
java/ql/src/semmle/code/java/security/ExternalAPIs.qll
Normal file
@@ -0,0 +1,146 @@
|
||||
/**
|
||||
* Definitions for reasoning about untrusted data used in APIs defined outside the
|
||||
* database.
|
||||
*/
|
||||
|
||||
import java
|
||||
import semmle.code.java.dataflow.FlowSources
|
||||
import semmle.code.java.dataflow.TaintTracking
|
||||
|
||||
/**
|
||||
* A `Method` that is considered a "safe" external API from a security perspective.
|
||||
*/
|
||||
abstract class SafeExternalAPIMethod extends Method { }
|
||||
|
||||
/** The default set of "safe" external APIs. */
|
||||
private class DefaultSafeExternalAPIMethod extends SafeExternalAPIMethod {
|
||||
DefaultSafeExternalAPIMethod() {
|
||||
this instanceof EqualsMethod
|
||||
or
|
||||
getName().regexpMatch("size|length|compareTo|getClass|lastIndexOf")
|
||||
or
|
||||
this.getDeclaringType().hasQualifiedName("org.apache.commons.lang3", "Validate")
|
||||
or
|
||||
getQualifiedName() = "Objects.equals"
|
||||
or
|
||||
getDeclaringType() instanceof TypeString and getName() = "equals"
|
||||
or
|
||||
getDeclaringType().hasQualifiedName("com.google.common.base", "Preconditions")
|
||||
or
|
||||
getDeclaringType().getPackage().getName().matches("org.junit%")
|
||||
or
|
||||
getDeclaringType().hasQualifiedName("com.google.common.base", "Strings") and
|
||||
getName() = "isNullOrEmpty"
|
||||
or
|
||||
getDeclaringType().hasQualifiedName("org.apache.commons.lang3", "StringUtils") and
|
||||
getName() = "isNotEmpty"
|
||||
or
|
||||
getDeclaringType().hasQualifiedName("java.lang", "Character") and
|
||||
getName() = "isDigit"
|
||||
or
|
||||
getDeclaringType().hasQualifiedName("java.lang", "String") and
|
||||
getName().regexpMatch("equalsIgnoreCase|regionMatches")
|
||||
or
|
||||
getDeclaringType().hasQualifiedName("java.lang", "Boolean") and
|
||||
getName() = "parseBoolean"
|
||||
or
|
||||
getDeclaringType().hasQualifiedName("org.apache.commons.io", "IOUtils") and
|
||||
getName() = "closeQuietly"
|
||||
or
|
||||
getDeclaringType().hasQualifiedName("org.springframework.util", "StringUtils") and
|
||||
getName().regexpMatch("hasText|isEmpty")
|
||||
}
|
||||
}
|
||||
|
||||
/** A node representing data being passed to an external API. */
|
||||
class ExternalAPIDataNode extends DataFlow::Node {
|
||||
Call call;
|
||||
int i;
|
||||
|
||||
ExternalAPIDataNode() {
|
||||
(
|
||||
// Argument to call to a method
|
||||
this.asExpr() = call.getArgument(i)
|
||||
or
|
||||
// Qualifier to call to a method which returns non trivial value
|
||||
this.asExpr() = call.getQualifier() and
|
||||
i = -1 and
|
||||
not call.getCallee().getReturnType() instanceof VoidType and
|
||||
not call.getCallee().getReturnType() instanceof BooleanType
|
||||
) and
|
||||
// Defined outside the source archive
|
||||
not call.getCallee().fromSource() and
|
||||
// Not a call to an method which is overridden in source
|
||||
not exists(Method m |
|
||||
m.getASourceOverriddenMethod() = call.getCallee().getSourceDeclaration() and
|
||||
m.fromSource()
|
||||
) and
|
||||
// Not already modeled as a taint step
|
||||
not exists(DataFlow::Node next | TaintTracking::localTaintStep(this, next)) and
|
||||
// Not a call to a known safe external API
|
||||
not call.getCallee() instanceof SafeExternalAPIMethod
|
||||
}
|
||||
|
||||
/** Gets the called API `Method`. */
|
||||
Method getMethod() { result = call.getCallee() }
|
||||
|
||||
/** Gets the index which is passed untrusted data (where -1 indicates the qualifier). */
|
||||
int getIndex() { result = i }
|
||||
|
||||
/** Gets the description of the method being called. */
|
||||
string getMethodDescription() {
|
||||
result = getMethod().getDeclaringType().getPackage() + "." + getMethod().getQualifiedName()
|
||||
}
|
||||
}
|
||||
|
||||
/** A configuration for tracking flow from `RemoteFlowSource`s to `ExternalAPIDataNode`s. */
|
||||
class UntrustedDataToExternalAPIConfig extends TaintTracking::Configuration {
|
||||
UntrustedDataToExternalAPIConfig() { this = "UntrustedDataToExternalAPIConfig" }
|
||||
|
||||
override predicate isSource(DataFlow::Node source) { source instanceof RemoteFlowSource }
|
||||
|
||||
override predicate isSink(DataFlow::Node sink) { sink instanceof ExternalAPIDataNode }
|
||||
}
|
||||
|
||||
/** A node representing untrusted data being passed to an external API. */
|
||||
class UntrustedExternalAPIDataNode extends ExternalAPIDataNode {
|
||||
UntrustedExternalAPIDataNode() { any(UntrustedDataToExternalAPIConfig c).hasFlow(_, this) }
|
||||
|
||||
/** Gets a source of untrusted data which is passed to this external API data node. */
|
||||
DataFlow::Node getAnUntrustedSource() {
|
||||
any(UntrustedDataToExternalAPIConfig c).hasFlow(result, this)
|
||||
}
|
||||
}
|
||||
|
||||
private newtype TExternalAPI =
|
||||
TExternalAPIParameter(Method m, int index) {
|
||||
exists(UntrustedExternalAPIDataNode n |
|
||||
m = n.getMethod() and
|
||||
index = n.getIndex()
|
||||
)
|
||||
}
|
||||
|
||||
/** An external API which is used with untrusted data. */
|
||||
class ExternalAPIUsedWithUntrustedData extends TExternalAPI {
|
||||
/** Gets a possibly untrusted use of this external API. */
|
||||
UntrustedExternalAPIDataNode getUntrustedDataNode() {
|
||||
this = TExternalAPIParameter(result.getMethod(), result.getIndex())
|
||||
}
|
||||
|
||||
/** Gets the number of untrusted sources used with this external API. */
|
||||
int getNumberOfUntrustedSources() {
|
||||
result = count(getUntrustedDataNode().getAnUntrustedSource())
|
||||
}
|
||||
|
||||
/** Gets a textual representation of this element. */
|
||||
string toString() {
|
||||
exists(Method m, int index, string indexString |
|
||||
if index = -1 then indexString = "qualifier" else indexString = "param " + index
|
||||
|
|
||||
this = TExternalAPIParameter(m, index) and
|
||||
result =
|
||||
m.getDeclaringType().(RefType).getQualifiedName() + "." + m.getSignature() + " [" +
|
||||
indexString + "]"
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -9,9 +9,9 @@ private predicate fileRead(VarAccess fileAccess, Expr fileReadingExpr) {
|
||||
cie = fileReadingExpr and
|
||||
cie.getArgument(0) = fileAccess
|
||||
|
|
||||
cie.getConstructedType().hasQualifiedName("java.io", "RandomAccessFile") or
|
||||
cie.getConstructedType().hasQualifiedName("java.io", "FileReader") or
|
||||
cie.getConstructedType().hasQualifiedName("java.io", "FileInputStream")
|
||||
cie
|
||||
.getConstructedType()
|
||||
.hasQualifiedName("java.io", ["RandomAccessFile", "FileReader", "FileInputStream"])
|
||||
)
|
||||
or
|
||||
exists(MethodAccess ma, Method filesMethod |
|
||||
@@ -22,13 +22,9 @@ private predicate fileRead(VarAccess fileAccess, Expr fileReadingExpr) {
|
||||
// represented by the first argument.
|
||||
filesMethod.getDeclaringType().hasQualifiedName("java.nio.file", "Files") and
|
||||
fileAccess = ma.getArgument(0) and
|
||||
(
|
||||
filesMethod.hasName("readAllBytes") or
|
||||
filesMethod.hasName("readAllLines") or
|
||||
filesMethod.hasName("newBufferedReader") or
|
||||
filesMethod.hasName("newInputReader") or
|
||||
filesMethod.hasName("newByteChannel")
|
||||
)
|
||||
filesMethod
|
||||
.hasName(["readAllBytes", "readAllLines", "readString", "lines", "newBufferedReader",
|
||||
"newInputStream", "newByteChannel"])
|
||||
)
|
||||
)
|
||||
or
|
||||
|
||||
413
java/ql/src/semmle/code/java/security/LdapInjection.qll
Normal file
413
java/ql/src/semmle/code/java/security/LdapInjection.qll
Normal file
@@ -0,0 +1,413 @@
|
||||
/** Provides classes to reason about LDAP injection attacks. */
|
||||
|
||||
import java
|
||||
import semmle.code.java.dataflow.TaintTracking
|
||||
import semmle.code.java.frameworks.Jndi
|
||||
import semmle.code.java.frameworks.UnboundId
|
||||
import semmle.code.java.frameworks.SpringLdap
|
||||
import semmle.code.java.frameworks.ApacheLdap
|
||||
|
||||
/** A data flow sink for unvalidated user input that is used to construct LDAP queries. */
|
||||
abstract class LdapInjectionSink extends DataFlow::Node { }
|
||||
|
||||
/** A sanitizer that prevents LDAP injection attacks. */
|
||||
abstract class LdapInjectionSanitizer extends DataFlow::Node { }
|
||||
|
||||
/**
|
||||
* A unit class for adding additional taint steps.
|
||||
*
|
||||
* Extend this class to add additional taint steps that should apply to the `LdapInjectionFlowConfig`.
|
||||
*/
|
||||
class LdapInjectionAdditionalTaintStep extends TaintTracking::Unit {
|
||||
/**
|
||||
* Holds if the step from `node1` to `node2` should be considered a taint
|
||||
* step for the `LdapInjectionFlowConfig` configuration.
|
||||
*/
|
||||
abstract predicate step(DataFlow::Node node1, DataFlow::Node node2);
|
||||
}
|
||||
|
||||
/** 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)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/** 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() {
|
||||
this.getType() instanceof PrimitiveType or
|
||||
this.getType() instanceof BoxedType
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `n1` to `n2` is a dataflow step that converts between `String` and `LdapName`,
|
||||
* i.e. `new LdapName(tainted)`.
|
||||
*/
|
||||
private predicate ldapNameStep(DataFlow::ExprNode n1, DataFlow::ExprNode n2) {
|
||||
exists(ConstructorCall cc | cc.getConstructedType() instanceof TypeLdapName |
|
||||
n1.asExpr() = cc.getAnArgument() and
|
||||
n2.asExpr() = cc
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `n1` to `n2` is a dataflow step that converts between `List<Rdn>` and `LdapName`,
|
||||
* i.e. `new LdapName().addAll(tainted)`.
|
||||
*/
|
||||
private predicate ldapNameAddAllStep(DataFlow::ExprNode n1, DataFlow::ExprNode n2) {
|
||||
exists(MethodAccess ma |
|
||||
n1.asExpr() = ma.getAnArgument() and
|
||||
(n2.asExpr() = ma or n2.asExpr() = ma.getQualifier())
|
||||
|
|
||||
ma.getMethod() instanceof MethodLdapNameAddAll
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `n1` to `n2` is a dataflow step that converts between `LdapName` and `LdapName` or
|
||||
* `String`, i.e. `taintedLdapName.clone()`, `taintedLdapName.getAll()`,
|
||||
* `taintedLdapName.getRdns()` or `taintedLdapName.toString()`.
|
||||
*/
|
||||
private predicate ldapNameGetCloneStep(DataFlow::ExprNode n1, DataFlow::ExprNode n2) {
|
||||
exists(MethodAccess ma, Method m |
|
||||
n1.asExpr() = ma.getQualifier() and
|
||||
n2.asExpr() = ma and
|
||||
ma.getMethod() = m
|
||||
|
|
||||
m instanceof MethodLdapNameClone or
|
||||
m instanceof MethodLdapNameGetAll or
|
||||
m instanceof MethodLdapNameGetRdns or
|
||||
m instanceof MethodLdapNameToString
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `n1` to `n2` is a dataflow step that converts between `String` and UnboundID `Filter`,
|
||||
* i.e. `Filter.create*(tainted)`.
|
||||
*/
|
||||
private predicate filterStep(DataFlow::ExprNode n1, DataFlow::ExprNode n2) {
|
||||
exists(MethodAccess ma, Method m |
|
||||
n1.asExpr() = ma.getAnArgument() and
|
||||
n2.asExpr() = ma and
|
||||
ma.getMethod() = m
|
||||
|
|
||||
m instanceof MethodUnboundIdFilterCreate or
|
||||
m instanceof MethodUnboundIdFilterCreateANDFilter or
|
||||
m instanceof MethodUnboundIdFilterCreateNOTFilter or
|
||||
m instanceof MethodUnboundIdFilterCreateORFilter or
|
||||
m instanceof MethodUnboundIdFilterSimplifyFilter
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `n1` to `n2` is a dataflow step that converts between UnboundID `Filter` and `String`,
|
||||
* i.e. `taintedFilter.toString()` or `taintedFilter.toString(buffer)`.
|
||||
*/
|
||||
private predicate filterToStringStep(DataFlow::ExprNode n1, DataFlow::ExprNode n2) {
|
||||
exists(MethodAccess ma, Method m |
|
||||
n1.asExpr() = ma.getQualifier() and
|
||||
(n2.asExpr() = ma or n2.asExpr() = ma.getAnArgument())
|
||||
|
|
||||
ma.getMethod() = m and
|
||||
m.getDeclaringType() instanceof TypeUnboundIdLdapFilter and
|
||||
(m.hasName("toString") or m.hasName("toNormalizedString"))
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `n1` to `n2` is a dataflow step that converts between `String` and UnboundID
|
||||
* `SearchRequest`, i.e. `new SearchRequest(tainted)`.
|
||||
*/
|
||||
private predicate unboundIdSearchRequestStep(DataFlow::ExprNode n1, DataFlow::ExprNode n2) {
|
||||
exists(ConstructorCall cc, int index, Parameter param |
|
||||
cc.getConstructedType() instanceof TypeUnboundIdSearchRequest
|
||||
|
|
||||
n1.asExpr() = cc.getArgument(index) and
|
||||
n2.asExpr() = cc and
|
||||
cc.getConstructor().getParameter(index) = param and
|
||||
not param.isVarargs()
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `n1` to `n2` is a dataflow step that converts between UnboundID `SearchRequest`
|
||||
* and UnboundID `SearchRequest`, i.e. `taintedSearchRequest.duplicate()`.
|
||||
*/
|
||||
private predicate unboundIdSearchRequestDuplicateStep(DataFlow::ExprNode n1, DataFlow::ExprNode n2) {
|
||||
exists(MethodAccess ma, Method m | n1.asExpr() = ma.getQualifier() and n2.asExpr() = ma |
|
||||
ma.getMethod() = m and
|
||||
m.getDeclaringType().getAnAncestor() instanceof TypeUnboundIdReadOnlySearchRequest and
|
||||
m.hasName("duplicate")
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `n1` to `n2` is a dataflow step that converts between DN or filter and UnboundID
|
||||
* `SearchRequest`, i.e. `searchRequest.setBaseDN(tainted)` or `searchRequest.setFilter(tainted)`.
|
||||
*/
|
||||
private predicate unboundIdSearchRequestSetStep(DataFlow::ExprNode n1, DataFlow::ExprNode n2) {
|
||||
exists(MethodAccess ma, Method m |
|
||||
n1.asExpr() = ma.getAnArgument() and
|
||||
n2.asExpr() = ma.getQualifier() and
|
||||
ma.getMethod() = m
|
||||
|
|
||||
m instanceof MethodUnboundIdSearchRequestSetBaseDN or
|
||||
m instanceof MethodUnboundIdSearchRequestSetFilter
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `n1` to `n2` is a dataflow step that converts between `String` and Spring `LdapQuery`,
|
||||
* i.e. `LdapQueryBuilder.query().filter(tainted)` or `LdapQueryBuilder.query().base(tainted)`.
|
||||
*/
|
||||
private predicate ldapQueryStep(DataFlow::ExprNode n1, DataFlow::ExprNode n2) {
|
||||
exists(MethodAccess ma, Method m, int index |
|
||||
n1.asExpr() = ma.getArgument(index) and
|
||||
n2.asExpr() = ma and
|
||||
ma.getMethod() = m and
|
||||
index = 0
|
||||
|
|
||||
m instanceof MethodSpringLdapQueryBuilderFilter or
|
||||
m instanceof MethodSpringLdapQueryBuilderBase
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `n1` to `n2` is a dataflow step that converts between Spring `LdapQueryBuilder` and
|
||||
* `Name`, i.e. `taintedLdapQueryBuilder.base()`.
|
||||
*/
|
||||
private predicate ldapQueryBaseStep(DataFlow::ExprNode n1, DataFlow::ExprNode n2) {
|
||||
exists(MethodAccess ma, Method m |
|
||||
n1.asExpr() = ma.getQualifier() and
|
||||
n2.asExpr() = ma and
|
||||
ma.getMethod() = m
|
||||
|
|
||||
m instanceof MethodSpringLdapQueryBuilderBase and
|
||||
m.getNumberOfParameters() = 0
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `n1` to `n2` is a dataflow step that converts between Spring `LdapQueryBuilder`,
|
||||
* `ConditionCriteria` or `ContainerCriteria`, i.e. when the query is built, for example
|
||||
* `query().base(tainted).where("objectclass").is("person")`.
|
||||
*/
|
||||
private predicate ldapQueryBuilderStep(DataFlow::ExprNode n1, DataFlow::ExprNode n2) {
|
||||
exists(MethodAccess ma, Method m |
|
||||
n1.asExpr() = ma.getQualifier() and
|
||||
n2.asExpr() = ma and
|
||||
ma.getMethod() = m
|
||||
|
|
||||
(
|
||||
m.getDeclaringType() instanceof TypeSpringLdapQueryBuilder or
|
||||
m.getDeclaringType() instanceof TypeSpringConditionCriteria or
|
||||
m.getDeclaringType() instanceof TypeSpringContainerCriteria
|
||||
) and
|
||||
(
|
||||
m.getReturnType() instanceof TypeSpringLdapQueryBuilder or
|
||||
m.getReturnType() instanceof TypeSpringConditionCriteria or
|
||||
m.getReturnType() instanceof TypeSpringContainerCriteria
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `n1` to `n2` is a dataflow step that converts between `String` and Spring
|
||||
* `HardcodedFilter`, i.e. `new HardcodedFilter(tainted)`.
|
||||
*/
|
||||
private predicate hardcodedFilterStep(DataFlow::ExprNode n1, DataFlow::ExprNode n2) {
|
||||
exists(ConstructorCall cc | cc.getConstructedType() instanceof TypeSpringHardcodedFilter |
|
||||
n1.asExpr() = cc.getAnArgument() and
|
||||
n2.asExpr() = cc
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `n1` to `n2` is a dataflow step that converts between Spring `Filter` and
|
||||
* `String`, i.e. `taintedFilter.toString()`, `taintedFilter.encode()` or
|
||||
* `taintedFilter.encode(buffer)`.
|
||||
*/
|
||||
private predicate springLdapFilterToStringStep(DataFlow::ExprNode n1, DataFlow::ExprNode n2) {
|
||||
exists(MethodAccess ma, Method m |
|
||||
n1.asExpr() = ma.getQualifier() and
|
||||
(n2.asExpr() = ma or n2.asExpr() = ma.getAnArgument()) and
|
||||
ma.getMethod() = m
|
||||
|
|
||||
m.getDeclaringType().getAnAncestor() instanceof TypeSpringLdapFilter and
|
||||
(m.hasName("encode") or m.hasName("toString"))
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `n1` to `n2` is a dataflow step that converts between `String` and Spring
|
||||
* `LdapNameBuilder`, i.e. `LdapNameBuilder.newInstance(tainted)` or
|
||||
* `LdapNameBuilder.newInstance().add(tainted)`.
|
||||
*/
|
||||
private predicate ldapNameBuilderStep(DataFlow::ExprNode n1, DataFlow::ExprNode n2) {
|
||||
exists(MethodAccess ma, Method m |
|
||||
n1.asExpr() = ma.getAnArgument() and
|
||||
(n2.asExpr() = ma or n2.asExpr() = ma.getQualifier()) and
|
||||
ma.getMethod() = m and
|
||||
m.getNumberOfParameters() = 1
|
||||
|
|
||||
m instanceof MethodSpringLdapNameBuilderNewInstance or
|
||||
m instanceof MethodSpringLdapNameBuilderAdd
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `n1` to `n2` is a dataflow step that converts between tainted Spring `LdapNameBuilder`
|
||||
* and `LdapName`, `LdapNameBuilder.build()`.
|
||||
*/
|
||||
private predicate ldapNameBuilderBuildStep(DataFlow::ExprNode n1, DataFlow::ExprNode n2) {
|
||||
exists(MethodAccess ma | n1.asExpr() = ma.getQualifier() and n2.asExpr() = ma |
|
||||
ma.getMethod() instanceof MethodSpringLdapNameBuilderBuild
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `n1` to `n2` is a dataflow step that converts between `String` and `LdapName` via
|
||||
* Spring `LdapUtils.newLdapName`, i.e. `LdapUtils.newLdapName(tainted)`.
|
||||
*/
|
||||
private predicate ldapUtilsStep(DataFlow::ExprNode n1, DataFlow::ExprNode n2) {
|
||||
exists(MethodAccess ma | n1.asExpr() = ma.getAnArgument() and n2.asExpr() = ma |
|
||||
ma.getMethod() instanceof MethodSpringLdapUtilsNewLdapName
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `n1` to `n2` is a dataflow step that converts between `String` and Apache LDAP API
|
||||
* `SearchRequest`, i.e. `searchRequest.setFilter(tainted)` or `searchRequest.setBase(tainted)`.
|
||||
*/
|
||||
private predicate apacheSearchRequestStep(DataFlow::ExprNode n1, DataFlow::ExprNode n2) {
|
||||
exists(MethodAccess ma, Method m |
|
||||
n1.asExpr() = ma.getAnArgument() and
|
||||
n2.asExpr() = ma.getQualifier()
|
||||
|
|
||||
ma.getMethod() = m and
|
||||
m.getDeclaringType().getAnAncestor() instanceof TypeApacheSearchRequest and
|
||||
(m.hasName("setFilter") or m.hasName("setBase"))
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `n1` to `n2` is a dataflow step that converts between Apache LDAP API `SearchRequest`
|
||||
* and filter or DN i.e. `tainterSearchRequest.getFilter()` or `taintedSearchRequest.getBase()`.
|
||||
*/
|
||||
private predicate apacheSearchRequestGetStep(DataFlow::ExprNode n1, DataFlow::ExprNode n2) {
|
||||
exists(MethodAccess ma, Method m | n1.asExpr() = ma.getQualifier() and n2.asExpr() = ma |
|
||||
ma.getMethod() = m and
|
||||
m.getDeclaringType().getAnAncestor() instanceof TypeApacheSearchRequest and
|
||||
(m.hasName("getFilter") or m.hasName("getBase"))
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `n1` to `n2` is a dataflow step that converts between `String` and Apache LDAP API
|
||||
* `Dn`, i.e. `new Dn(tainted)`.
|
||||
*/
|
||||
private predicate apacheLdapDnStep(DataFlow::ExprNode n1, DataFlow::ExprNode n2) {
|
||||
exists(ConstructorCall cc | cc.getConstructedType() instanceof TypeApacheDn |
|
||||
n1.asExpr() = cc.getAnArgument() and
|
||||
n2.asExpr() = cc
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `n1` to `n2` is a dataflow step that converts between Apache LDAP API `Dn`
|
||||
* and `String` i.e. `taintedDn.getName()`, `taintedDn.getNormName()` or `taintedDn.toString()`.
|
||||
*/
|
||||
private predicate apacheLdapDnGetStep(DataFlow::ExprNode n1, DataFlow::ExprNode n2) {
|
||||
exists(MethodAccess ma, Method m | n1.asExpr() = ma.getQualifier() and n2.asExpr() = ma |
|
||||
ma.getMethod() = m and
|
||||
m.getDeclaringType().getAnAncestor() instanceof TypeApacheDn and
|
||||
(m.hasName("getName") or m.hasName("getNormName") or m.hasName("toString"))
|
||||
)
|
||||
}
|
||||
|
||||
/** A set of additional taint steps to consider when taint tracking LDAP related data flows. */
|
||||
private class DefaultLdapInjectionAdditionalTaintStep extends LdapInjectionAdditionalTaintStep {
|
||||
override predicate step(DataFlow::Node node1, DataFlow::Node node2) {
|
||||
ldapNameStep(node1, node2) or
|
||||
ldapNameAddAllStep(node1, node2) or
|
||||
ldapNameGetCloneStep(node1, node2) or
|
||||
filterStep(node1, node2) or
|
||||
filterToStringStep(node1, node2) or
|
||||
unboundIdSearchRequestStep(node1, node2) or
|
||||
unboundIdSearchRequestDuplicateStep(node1, node2) or
|
||||
unboundIdSearchRequestSetStep(node1, node2) or
|
||||
ldapQueryStep(node1, node2) or
|
||||
ldapQueryBaseStep(node1, node2) or
|
||||
ldapQueryBuilderStep(node1, node2) or
|
||||
hardcodedFilterStep(node1, node2) or
|
||||
springLdapFilterToStringStep(node1, node2) or
|
||||
ldapNameBuilderStep(node1, node2) or
|
||||
ldapNameBuilderBuildStep(node1, node2) or
|
||||
ldapUtilsStep(node1, node2) or
|
||||
apacheSearchRequestStep(node1, node2) or
|
||||
apacheSearchRequestGetStep(node1, node2) or
|
||||
apacheLdapDnStep(node1, node2) or
|
||||
apacheLdapDnGetStep(node1, node2)
|
||||
}
|
||||
}
|
||||
141
java/ql/src/semmle/code/java/security/PathCreation.qll
Normal file
141
java/ql/src/semmle/code/java/security/PathCreation.qll
Normal file
@@ -0,0 +1,141 @@
|
||||
/**
|
||||
* Models the different ways to create paths. Either by using `java.io.File`-related APIs or `java.nio.file.Path`-related APIs.
|
||||
*/
|
||||
|
||||
import java
|
||||
|
||||
/** Models the creation of a path. */
|
||||
abstract class PathCreation extends Expr {
|
||||
/**
|
||||
* Gets an input that is used in the creation of this path.
|
||||
* This excludes inputs of type `File` and `Path`.
|
||||
*/
|
||||
abstract Expr getAnInput();
|
||||
}
|
||||
|
||||
/** Models the `java.nio.file.Paths.get` method. */
|
||||
private class PathsGet extends PathCreation, MethodAccess {
|
||||
PathsGet() {
|
||||
exists(Method m | m = this.getMethod() |
|
||||
m.getDeclaringType() instanceof TypePaths and
|
||||
m.getName() = "get"
|
||||
)
|
||||
}
|
||||
|
||||
override Expr getAnInput() { result = this.getAnArgument() }
|
||||
}
|
||||
|
||||
/** Models the `java.nio.file.FileSystem.getPath` method. */
|
||||
private class FileSystemGetPath extends PathCreation, MethodAccess {
|
||||
FileSystemGetPath() {
|
||||
exists(Method m | m = this.getMethod() |
|
||||
m.getDeclaringType() instanceof TypeFileSystem and
|
||||
m.getName() = "getPath"
|
||||
)
|
||||
}
|
||||
|
||||
override Expr getAnInput() { result = this.getAnArgument() }
|
||||
}
|
||||
|
||||
/** Models the `new java.io.File(...)` constructor. */
|
||||
private class FileCreation extends PathCreation, ClassInstanceExpr {
|
||||
FileCreation() { this.getConstructedType() instanceof TypeFile }
|
||||
|
||||
override Expr getAnInput() {
|
||||
result = this.getAnArgument() and
|
||||
// Relevant arguments include those that are not a `File`.
|
||||
not result.getType() instanceof TypeFile
|
||||
}
|
||||
}
|
||||
|
||||
/** Models the `java.nio.file.Path.resolveSibling` method. */
|
||||
private class PathResolveSiblingCreation extends PathCreation, MethodAccess {
|
||||
PathResolveSiblingCreation() {
|
||||
exists(Method m | m = this.getMethod() |
|
||||
m.getDeclaringType() instanceof TypePath and
|
||||
m.getName() = "resolveSibling"
|
||||
)
|
||||
}
|
||||
|
||||
override Expr getAnInput() {
|
||||
result = this.getAnArgument() and
|
||||
// Relevant arguments are those of type `String`.
|
||||
result.getType() instanceof TypeString
|
||||
}
|
||||
}
|
||||
|
||||
/** Models the `java.nio.file.Path.resolve` method. */
|
||||
private class PathResolveCreation extends PathCreation, MethodAccess {
|
||||
PathResolveCreation() {
|
||||
exists(Method m | m = this.getMethod() |
|
||||
m.getDeclaringType() instanceof TypePath and
|
||||
m.getName() = "resolve"
|
||||
)
|
||||
}
|
||||
|
||||
override Expr getAnInput() {
|
||||
result = this.getAnArgument() and
|
||||
// Relevant arguments are those of type `String`.
|
||||
result.getType() instanceof TypeString
|
||||
}
|
||||
}
|
||||
|
||||
/** Models the `java.nio.file.Path.of` method. */
|
||||
private class PathOfCreation extends PathCreation, MethodAccess {
|
||||
PathOfCreation() {
|
||||
exists(Method m | m = this.getMethod() |
|
||||
m.getDeclaringType() instanceof TypePath and
|
||||
m.getName() = "of"
|
||||
)
|
||||
}
|
||||
|
||||
override Expr getAnInput() { result = this.getAnArgument() }
|
||||
}
|
||||
|
||||
/** Models the `new java.io.FileWriter(...)` constructor. */
|
||||
private class FileWriterCreation extends PathCreation, ClassInstanceExpr {
|
||||
FileWriterCreation() { this.getConstructedType().hasQualifiedName("java.io", "FileWriter") }
|
||||
|
||||
override Expr getAnInput() {
|
||||
result = this.getAnArgument() and
|
||||
// Relevant arguments are those of type `String`.
|
||||
result.getType() instanceof TypeString
|
||||
}
|
||||
}
|
||||
|
||||
/** Models the `new java.io.FileReader(...)` constructor. */
|
||||
private class FileReaderCreation extends PathCreation, ClassInstanceExpr {
|
||||
FileReaderCreation() { this.getConstructedType().hasQualifiedName("java.io", "FileReader") }
|
||||
|
||||
override Expr getAnInput() {
|
||||
result = this.getAnArgument() and
|
||||
// Relevant arguments are those of type `String`.
|
||||
result.getType() instanceof TypeString
|
||||
}
|
||||
}
|
||||
|
||||
/** Models the `new java.io.FileInputStream(...)` constructor. */
|
||||
private class FileInputStreamCreation extends PathCreation, ClassInstanceExpr {
|
||||
FileInputStreamCreation() {
|
||||
this.getConstructedType().hasQualifiedName("java.io", "FileInputStream")
|
||||
}
|
||||
|
||||
override Expr getAnInput() {
|
||||
result = this.getAnArgument() and
|
||||
// Relevant arguments are those of type `String`.
|
||||
result.getType() instanceof TypeString
|
||||
}
|
||||
}
|
||||
|
||||
/** Models the `new java.io.FileOutputStream(...)` constructor. */
|
||||
private class FileOutputStreamCreation extends PathCreation, ClassInstanceExpr {
|
||||
FileOutputStreamCreation() {
|
||||
this.getConstructedType().hasQualifiedName("java.io", "FileOutputStream")
|
||||
}
|
||||
|
||||
override Expr getAnInput() {
|
||||
result = this.getAnArgument() and
|
||||
// Relevant arguments are those of type `String`.
|
||||
result.getType() instanceof TypeString
|
||||
}
|
||||
}
|
||||
51
java/ql/src/semmle/code/java/security/QueryInjection.qll
Normal file
51
java/ql/src/semmle/code/java/security/QueryInjection.qll
Normal file
@@ -0,0 +1,51 @@
|
||||
/** Provides classes to reason about database query language injection vulnerabilities. */
|
||||
|
||||
import java
|
||||
import semmle.code.java.dataflow.DataFlow
|
||||
import semmle.code.java.frameworks.Jdbc
|
||||
import semmle.code.java.frameworks.jOOQ
|
||||
import semmle.code.java.frameworks.android.SQLite
|
||||
import semmle.code.java.frameworks.javaee.Persistence
|
||||
import semmle.code.java.frameworks.SpringJdbc
|
||||
import semmle.code.java.frameworks.MyBatis
|
||||
import semmle.code.java.frameworks.Hibernate
|
||||
|
||||
/** A sink for database query language injection vulnerabilities. */
|
||||
abstract class QueryInjectionSink extends DataFlow::Node { }
|
||||
|
||||
/** A sink for SQL injection vulnerabilities. */
|
||||
private class SqlInjectionSink extends QueryInjectionSink {
|
||||
SqlInjectionSink() {
|
||||
this.asExpr() instanceof SqlExpr
|
||||
or
|
||||
exists(MethodAccess ma, Method m, int index |
|
||||
ma.getMethod() = m and
|
||||
ma.getArgument(index) = this.asExpr()
|
||||
|
|
||||
index = m.(SQLiteRunner).sqlIndex()
|
||||
or
|
||||
m instanceof BatchUpdateVarargsMethod
|
||||
or
|
||||
index = 0 and jdbcSqlMethod(m)
|
||||
or
|
||||
index = 0 and mybatisSqlMethod(m)
|
||||
or
|
||||
index = 0 and hibernateSqlMethod(m)
|
||||
or
|
||||
index = 0 and jOOQSqlMethod(m)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/** A sink for Java Persistence Query Language injection vulnerabilities. */
|
||||
private class PersistenceQueryInjectionSink extends QueryInjectionSink {
|
||||
PersistenceQueryInjectionSink() {
|
||||
// the query (first) argument to a `createQuery` or `createNativeQuery` method on `EntityManager`
|
||||
exists(MethodAccess call, TypeEntityManager em | call.getArgument(0) = this.asExpr() |
|
||||
call.getMethod() = em.getACreateQueryMethod() or
|
||||
call.getMethod() = em.getACreateNativeQueryMethod()
|
||||
// note: `createNamedQuery` is safe, as it takes only the query name,
|
||||
// and named queries can only be constructed using constants as the query text
|
||||
)
|
||||
}
|
||||
}
|
||||
49
java/ql/src/semmle/code/java/security/ResponseSplitting.qll
Normal file
49
java/ql/src/semmle/code/java/security/ResponseSplitting.qll
Normal file
@@ -0,0 +1,49 @@
|
||||
/** Provides classes to reason about header splitting attacks. */
|
||||
|
||||
import java
|
||||
import semmle.code.java.dataflow.DataFlow
|
||||
import semmle.code.java.dataflow.FlowSources
|
||||
import semmle.code.java.frameworks.Servlets
|
||||
import semmle.code.java.frameworks.JaxWS
|
||||
|
||||
/** A sink that is vulnerable to an HTTP header splitting attack. */
|
||||
abstract class HeaderSplittingSink extends DataFlow::Node { }
|
||||
|
||||
/** 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() {
|
||||
this.asExpr().(MethodAccess).getMethod() instanceof HttpServletRequestGetHeaderMethod or
|
||||
this.asExpr().(MethodAccess).getMethod() instanceof CookieGetNameMethod
|
||||
}
|
||||
}
|
||||
@@ -51,7 +51,14 @@ class SafeKryo extends DataFlow2::Configuration {
|
||||
predicate unsafeDeserialization(MethodAccess ma, Expr sink) {
|
||||
exists(Method m | m = ma.getMethod() |
|
||||
m instanceof ObjectInputStreamReadObjectMethod and
|
||||
sink = ma.getQualifier()
|
||||
sink = ma.getQualifier() and
|
||||
not exists(DataFlow::ExprNode node |
|
||||
node.getExpr() = sink and
|
||||
node
|
||||
.getTypeBound()
|
||||
.(RefType)
|
||||
.hasQualifiedName("org.apache.commons.io.serialization", "ValidatingObjectInputStream")
|
||||
)
|
||||
or
|
||||
m instanceof XMLDecoderReadObjectMethod and
|
||||
sink = ma.getQualifier()
|
||||
|
||||
26
java/ql/src/semmle/code/java/security/UrlRedirect.qll
Normal file
26
java/ql/src/semmle/code/java/security/UrlRedirect.qll
Normal file
@@ -0,0 +1,26 @@
|
||||
/** Provides classes to reason about URL redirect attacks. */
|
||||
|
||||
import java
|
||||
import semmle.code.java.dataflow.DataFlow
|
||||
import semmle.code.java.frameworks.Servlets
|
||||
|
||||
/** A URL redirection sink */
|
||||
abstract class UrlRedirectSink extends DataFlow::Node { }
|
||||
|
||||
/** A Servlet URL redirection sink. */
|
||||
private class ServletUrlRedirectSink extends UrlRedirectSink {
|
||||
ServletUrlRedirectSink() {
|
||||
exists(MethodAccess ma |
|
||||
ma.getMethod() instanceof HttpServletResponseSendRedirectMethod and
|
||||
this.asExpr() = ma.getArgument(0)
|
||||
)
|
||||
or
|
||||
exists(MethodAccess ma |
|
||||
ma.getMethod() instanceof ResponseSetHeaderMethod or
|
||||
ma.getMethod() instanceof ResponseAddHeaderMethod
|
||||
|
|
||||
ma.getArgument(0).(CompileTimeConstantExpr).getStringValue() = "Location" and
|
||||
this.asExpr() = ma.getArgument(1)
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,8 @@
|
||||
import java
|
||||
import semmle.code.java.frameworks.Servlets
|
||||
import semmle.code.java.frameworks.android.WebView
|
||||
import semmle.code.java.frameworks.spring.SpringController
|
||||
import semmle.code.java.frameworks.spring.SpringHttp
|
||||
import semmle.code.java.dataflow.TaintTracking
|
||||
|
||||
/*
|
||||
@@ -30,6 +32,48 @@ class XssSink extends DataFlow::ExprNode {
|
||||
m.getAReference().getArgument(1) = this.getExpr() and m.getName() = "loadDataWithBaseURL"
|
||||
)
|
||||
)
|
||||
or
|
||||
exists(SpringRequestMappingMethod requestMappingMethod, ReturnStmt rs |
|
||||
requestMappingMethod = rs.getEnclosingCallable() and
|
||||
this.asExpr() = rs.getResult() and
|
||||
(
|
||||
not exists(requestMappingMethod.getProduces()) or
|
||||
requestMappingMethod.getProduces().matches("text/%")
|
||||
)
|
||||
|
|
||||
// If a Spring request mapping method is either annotated with @ResponseBody (or equivalent),
|
||||
// or returns a HttpEntity or sub-type, then the return value of the method is converted into
|
||||
// a HTTP reponse using a HttpMessageConverter implementation. The implementation is chosen
|
||||
// based on the return type of the method, and the Accept header of the request.
|
||||
//
|
||||
// By default, the only message converter which produces a response which is vulnerable to
|
||||
// XSS is the StringHttpMessageConverter, which "Accept"s all text/* content types, including
|
||||
// text/html. Therefore, if a browser request includes "text/html" in the "Accept" header,
|
||||
// any String returned will be converted into a text/html response.
|
||||
requestMappingMethod.isResponseBody() and
|
||||
requestMappingMethod.getReturnType() instanceof TypeString
|
||||
or
|
||||
exists(Type returnType |
|
||||
// A return type of HttpEntity<T> or ResponseEntity<T> represents an HTTP response with both
|
||||
// a body and a set of headers. The body is subject to the same HttpMessageConverter
|
||||
// process as above.
|
||||
returnType = requestMappingMethod.getReturnType() and
|
||||
(
|
||||
returnType instanceof SpringHttpEntity
|
||||
or
|
||||
returnType instanceof SpringResponseEntity
|
||||
)
|
||||
|
|
||||
// The type argument, representing the type of the body, is type String
|
||||
returnType.(ParameterizedClass).getTypeArgument(0) instanceof TypeString
|
||||
or
|
||||
// Return type is a Raw class, which means no static type information on the body. In this
|
||||
// case we will still treat this as an XSS sink, but rely on our taint flow steps for
|
||||
// HttpEntity/ResponseEntity to only pass taint into those instances if the body type was
|
||||
// String.
|
||||
returnType instanceof RawClass
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -53,6 +97,7 @@ class WritingMethod extends Method {
|
||||
(
|
||||
this.getName().matches("print%") or
|
||||
this.getName() = "append" or
|
||||
this.getName() = "format" or
|
||||
this.getName() = "write"
|
||||
)
|
||||
}
|
||||
|
||||
@@ -116,7 +116,7 @@ class XMLFile extends XMLParent, File {
|
||||
XMLFile() { xmlEncoding(this, _) }
|
||||
|
||||
/** Gets a printable representation of this XML file. */
|
||||
override string toString() { result = XMLParent.super.toString() }
|
||||
override string toString() { result = getName() }
|
||||
|
||||
/** Gets the name of this XML file. */
|
||||
override string getName() { result = File.super.getAbsolutePath() }
|
||||
@@ -236,7 +236,7 @@ class XMLElement extends @xmlelement, XMLParent, XMLLocatable {
|
||||
string getAttributeValue(string name) { result = this.getAttribute(name).getValue() }
|
||||
|
||||
/** Gets a printable representation of this XML element. */
|
||||
override string toString() { result = XMLParent.super.toString() }
|
||||
override string toString() { result = getName() }
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
Reference in New Issue
Block a user