mirror of
https://github.com/github/codeql.git
synced 2026-03-04 14:46:48 +01:00
Merge branch 'main' into atorralba/java/promote-xxe-experimental-sinks
This commit is contained in:
@@ -31,7 +31,7 @@ class Compilation extends @compilation {
|
||||
}
|
||||
|
||||
/** Gets a file compiled during this invocation. */
|
||||
File getAFileCompiled() { result = getFileCompiled(_) }
|
||||
File getAFileCompiled() { result = this.getFileCompiled(_) }
|
||||
|
||||
/** Gets the `i`th file compiled during this invocation. */
|
||||
File getFileCompiled(int i) { compilation_compiling_files(this, i, result) }
|
||||
@@ -76,7 +76,7 @@ class Compilation extends @compilation {
|
||||
/**
|
||||
* Gets an argument passed to the extractor on this invocation.
|
||||
*/
|
||||
string getAnArgument() { result = getArgument(_) }
|
||||
string getAnArgument() { result = this.getArgument(_) }
|
||||
|
||||
/**
|
||||
* Gets the `i`th argument passed to the extractor on this invocation.
|
||||
@@ -86,7 +86,7 @@ class Compilation extends @compilation {
|
||||
/**
|
||||
* Gets an expanded argument passed to the extractor on this invocation.
|
||||
*/
|
||||
string getAnExpandedArgument() { result = getExpandedArgument(_) }
|
||||
string getAnExpandedArgument() { result = this.getExpandedArgument(_) }
|
||||
|
||||
/**
|
||||
* Gets the `i`th expanded argument passed to the extractor on this invocation.
|
||||
|
||||
@@ -26,7 +26,7 @@ class Exception extends Element, @exception {
|
||||
/** Holds if this exception has the specified `name`. */
|
||||
override predicate hasName(string name) { this.getType().hasName(name) }
|
||||
|
||||
override string toString() { result = this.getType().toString() }
|
||||
override string toString() { result = pragma[only_bind_out](this.getType()).toString() }
|
||||
|
||||
override string getAPrimaryQlClass() { result = "Exception" }
|
||||
}
|
||||
|
||||
@@ -1644,7 +1644,9 @@ class TypeLiteral extends Expr, @typeliteral {
|
||||
Type getReferencedType() { result = this.getTypeName().getType() }
|
||||
|
||||
/** Gets a printable representation of this expression. */
|
||||
override string toString() { result = this.getTypeName().toString() + ".class" }
|
||||
override string toString() {
|
||||
result = pragma[only_bind_out](this.getTypeName()).toString() + ".class"
|
||||
}
|
||||
|
||||
override string getAPrimaryQlClass() { result = "TypeLiteral" }
|
||||
}
|
||||
@@ -1752,7 +1754,7 @@ class VarAccess extends Expr, @varaccess {
|
||||
exists(Expr q | q = this.getQualifier() |
|
||||
if q.isParenthesized()
|
||||
then result = "(...)." + this.getVariable().getName()
|
||||
else result = q.toString() + "." + this.getVariable().getName()
|
||||
else result = pragma[only_bind_out](q).toString() + "." + this.getVariable().getName()
|
||||
)
|
||||
or
|
||||
not this.hasQualifier() and result = this.getVariable().getName()
|
||||
|
||||
@@ -27,7 +27,9 @@ class ImportType extends Import {
|
||||
/** Gets the imported type. */
|
||||
ClassOrInterface getImportedType() { imports(this, result, _, _) }
|
||||
|
||||
override string toString() { result = "import " + this.getImportedType().toString() }
|
||||
override string toString() {
|
||||
result = "import " + pragma[only_bind_out](this.getImportedType()).toString()
|
||||
}
|
||||
|
||||
override string getAPrimaryQlClass() { result = "ImportType" }
|
||||
}
|
||||
@@ -49,7 +51,9 @@ class ImportOnDemandFromType extends Import {
|
||||
/** Gets an imported type. */
|
||||
NestedType getAnImport() { result.getEnclosingType() = this.getTypeHoldingImport() }
|
||||
|
||||
override string toString() { result = "import " + this.getTypeHoldingImport().toString() + ".*" }
|
||||
override string toString() {
|
||||
result = "import " + pragma[only_bind_out](this.getTypeHoldingImport()).toString() + ".*"
|
||||
}
|
||||
|
||||
override string getAPrimaryQlClass() { result = "ImportOnDemandFromType" }
|
||||
}
|
||||
@@ -71,7 +75,7 @@ class ImportOnDemandFromPackage extends Import {
|
||||
|
||||
/** Gets a printable representation of this import declaration. */
|
||||
override string toString() {
|
||||
result = "import " + this.getPackageHoldingImport().toString() + ".*"
|
||||
result = "import " + pragma[only_bind_out](this.getPackageHoldingImport()).toString() + ".*"
|
||||
}
|
||||
|
||||
override string getAPrimaryQlClass() { result = "ImportOnDemandFromPackage" }
|
||||
@@ -100,7 +104,7 @@ class ImportStaticOnDemand extends Import {
|
||||
|
||||
/** Gets a printable representation of this import declaration. */
|
||||
override string toString() {
|
||||
result = "import static " + this.getTypeHoldingImport().toString() + ".*"
|
||||
result = "import static " + pragma[only_bind_out](this.getTypeHoldingImport()).toString() + ".*"
|
||||
}
|
||||
|
||||
override string getAPrimaryQlClass() { result = "ImportStaticOnDemand" }
|
||||
@@ -141,7 +145,9 @@ class ImportStaticTypeMember extends Import {
|
||||
|
||||
/** Gets a printable representation of this import declaration. */
|
||||
override string toString() {
|
||||
result = "import static " + this.getTypeHoldingImport().toString() + "." + this.getName()
|
||||
result =
|
||||
"import static " + pragma[only_bind_out](this.getTypeHoldingImport()).toString() + "." +
|
||||
this.getName()
|
||||
}
|
||||
|
||||
override string getAPrimaryQlClass() { result = "ImportStaticTypeMember" }
|
||||
|
||||
@@ -379,6 +379,19 @@ class SrcCallable extends Callable {
|
||||
this.isProtected() and not tsub.isFinal()
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if this callable is implicitly public in the sense that it can be the
|
||||
* target of virtual dispatch by a call from outside the codebase.
|
||||
*/
|
||||
predicate isImplicitlyPublic() {
|
||||
this.isEffectivelyPublic()
|
||||
or
|
||||
exists(SrcMethod m |
|
||||
m.(SrcCallable).isEffectivelyPublic() and
|
||||
m.getAPossibleImplementationOfSrcMethod() = this
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/** Gets the erasure of `t1` if it is a raw type, or `t1` itself otherwise. */
|
||||
|
||||
@@ -73,10 +73,10 @@ class RequiresDirective extends Directive, @requires {
|
||||
|
||||
override string toString() {
|
||||
exists(string transitive, string static |
|
||||
(if isTransitive() then transitive = "transitive " else transitive = "") and
|
||||
(if isStatic() then static = "static " else static = "")
|
||||
(if this.isTransitive() then transitive = "transitive " else transitive = "") and
|
||||
(if this.isStatic() then static = "static " else static = "")
|
||||
|
|
||||
result = "requires " + transitive + static + getTargetModule() + ";"
|
||||
result = "requires " + transitive + static + this.getTargetModule() + ";"
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -111,11 +111,11 @@ class ExportsDirective extends Directive, @exports {
|
||||
|
||||
override string toString() {
|
||||
exists(string toClause |
|
||||
if isQualified()
|
||||
then toClause = (" to " + concat(getATargetModule().getName(), ", "))
|
||||
if this.isQualified()
|
||||
then toClause = (" to " + concat(this.getATargetModule().getName(), ", "))
|
||||
else toClause = ""
|
||||
|
|
||||
result = "exports " + getExportedPackage() + toClause + ";"
|
||||
result = "exports " + this.getExportedPackage() + toClause + ";"
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -150,11 +150,11 @@ class OpensDirective extends Directive, @opens {
|
||||
|
||||
override string toString() {
|
||||
exists(string toClause |
|
||||
if isQualified()
|
||||
then toClause = (" to " + concat(getATargetModule().getName(), ", "))
|
||||
if this.isQualified()
|
||||
then toClause = (" to " + concat(this.getATargetModule().getName(), ", "))
|
||||
else toClause = ""
|
||||
|
|
||||
result = "opens " + getOpenedPackage() + toClause + ";"
|
||||
result = "opens " + this.getOpenedPackage() + toClause + ";"
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -170,7 +170,7 @@ class UsesDirective extends Directive, @uses {
|
||||
*/
|
||||
string getServiceInterfaceName() { uses(this, result) }
|
||||
|
||||
override string toString() { result = "uses " + getServiceInterfaceName() + ";" }
|
||||
override string toString() { result = "uses " + this.getServiceInterfaceName() + ";" }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -191,7 +191,7 @@ class ProvidesDirective extends Directive, @provides {
|
||||
|
||||
override string toString() {
|
||||
result =
|
||||
"provides " + getServiceInterfaceName() + " with " +
|
||||
concat(getServiceImplementationName(), ", ") + ";"
|
||||
"provides " + this.getServiceInterfaceName() + " with " +
|
||||
concat(this.getServiceImplementationName(), ", ") + ";"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -191,14 +191,19 @@ private predicate typePrefixContains_ext_neq(ParameterizedPrefix pps, Parameteri
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private TTypeParam parameterizedPrefixWithWildcard(ParameterizedPrefix pps0, Wildcard s) {
|
||||
result = TTypeParam(pps0, s)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate typePrefixContainsAux1(
|
||||
ParameterizedPrefix pps, ParameterizedPrefix ppt0, RefType s
|
||||
) {
|
||||
exists(ParameterizedPrefix pps0 |
|
||||
typePrefixContains(pps0, ppt0) and
|
||||
pps = TTypeParam(pps0, s) and
|
||||
s instanceof Wildcard // manual magic, implied by `typeArgumentContains(_, s, t, _)`
|
||||
// `s instanceof Wildcard` is manual magic, implied by `typeArgumentContains(_, s, t, _)`
|
||||
pps = parameterizedPrefixWithWildcard(pps0, s)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -804,7 +809,9 @@ class AnonymousClass extends NestedClass {
|
||||
// Include super.toString, i.e. the name given in the database, because for Kotlin anonymous
|
||||
// classes we can get specialisations of anonymous generic types, and this will supply the
|
||||
// trailing type arguments.
|
||||
result = "new " + this.getClassInstanceExpr().getTypeName() + "(...) { ... }" + super.toString()
|
||||
result =
|
||||
"new " + pragma[only_bind_out](this.getClassInstanceExpr().getTypeName()).toString() +
|
||||
"(...) { ... }" + super.toString()
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -23,12 +23,12 @@ class ExcludeDebuggingProfilingLogging extends ExcludedConstantField {
|
||||
"log"
|
||||
]
|
||||
|
|
||||
getName().regexpMatch(".*(?i)" + validFieldName + ".*")
|
||||
this.getName().regexpMatch(".*(?i)" + validFieldName + ".*")
|
||||
) and
|
||||
// Boolean type
|
||||
(
|
||||
getType().hasName("boolean") or
|
||||
getType().(BoxedType).hasQualifiedName("java.lang", "Boolean")
|
||||
this.getType().hasName("boolean") or
|
||||
this.getType().(BoxedType).hasQualifiedName("java.lang", "Boolean")
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -23,7 +23,7 @@ abstract class Bound extends TBound {
|
||||
abstract Expr getExpr(int delta);
|
||||
|
||||
/** Gets an expression that equals this bound. */
|
||||
Expr getExpr() { result = getExpr(0) }
|
||||
Expr getExpr() { result = this.getExpr(0) }
|
||||
|
||||
/**
|
||||
* Holds if this element is at the specified location.
|
||||
@@ -54,12 +54,12 @@ class SsaBound extends Bound, TBoundSsa {
|
||||
/** Gets the SSA variable that equals this bound. */
|
||||
SsaVariable getSsa() { this = TBoundSsa(result) }
|
||||
|
||||
override string toString() { result = getSsa().toString() }
|
||||
override string toString() { result = this.getSsa().toString() }
|
||||
|
||||
override Expr getExpr(int delta) { result = getSsa().getAUse() and delta = 0 }
|
||||
override Expr getExpr(int delta) { result = this.getSsa().getAUse() and delta = 0 }
|
||||
|
||||
override predicate hasLocationInfo(string path, int sl, int sc, int el, int ec) {
|
||||
getSsa().getLocation().hasLocationInfo(path, sl, sc, el, ec)
|
||||
this.getSsa().getLocation().hasLocationInfo(path, sl, sc, el, ec)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -68,11 +68,11 @@ class SsaBound extends Bound, TBoundSsa {
|
||||
* interesting, but isn't otherwise represented by the value of an SSA variable.
|
||||
*/
|
||||
class ExprBound extends Bound, TBoundExpr {
|
||||
override string toString() { result = getExpr().toString() }
|
||||
override string toString() { result = this.getExpr().toString() }
|
||||
|
||||
override Expr getExpr(int delta) { this = TBoundExpr(result) and delta = 0 }
|
||||
|
||||
override predicate hasLocationInfo(string path, int sl, int sc, int el, int ec) {
|
||||
getExpr().getLocation().hasLocationInfo(path, sl, sc, el, ec)
|
||||
this.getExpr().getLocation().hasLocationInfo(path, sl, sc, el, ec)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,8 +12,8 @@
|
||||
* - Summaries:
|
||||
* `package; type; subtypes; name; signature; ext; input; output; kind; provenance`
|
||||
* - Neutrals:
|
||||
* `package; type; name; signature; provenance`
|
||||
* A neutral is used to indicate that there is no flow via a callable.
|
||||
* `package; type; name; signature; kind; provenance`
|
||||
* A neutral is used to indicate that a callable is neutral with respect to flow (no summary), source (is not a source) or sink (is not a sink).
|
||||
*
|
||||
* The interpretation of a row is similar to API-graphs with a left-to-right
|
||||
* reading.
|
||||
@@ -65,7 +65,9 @@
|
||||
* which classes the interpreted elements should be added. For example, for
|
||||
* sources "remote" indicates a default remote flow source, and for summaries
|
||||
* "taint" indicates a default additional taint step and "value" indicates a
|
||||
* globally applicable value-preserving step.
|
||||
* globally applicable value-preserving step. For neutrals the kind can be `summary`,
|
||||
* `source` or `sink` to indicate that the neutral is neutral with respect to
|
||||
* flow (no summary), source (is not a source) or sink (is not a sink).
|
||||
* 9. The `provenance` column is a tag to indicate the origin and verification of a model.
|
||||
* The format is {origin}-{verification} or just "manual" where the origin describes
|
||||
* the origin of the model and verification describes how the model has been verified.
|
||||
@@ -164,8 +166,8 @@ predicate summaryModel(
|
||||
.summaryModel(package, type, subtypes, name, signature, ext, input, output, kind, provenance)
|
||||
}
|
||||
|
||||
/** Holds if a neutral model exists indicating there is no flow for the given parameters. */
|
||||
predicate neutralModel = Extensions::neutralModel/5;
|
||||
/** Holds if a neutral model exists for the given parameters. */
|
||||
predicate neutralModel = Extensions::neutralModel/6;
|
||||
|
||||
private predicate relevantPackage(string package) {
|
||||
sourceModel(package, _, _, _, _, _, _, _, _) or
|
||||
@@ -273,10 +275,10 @@ module ModelValidation {
|
||||
not kind =
|
||||
[
|
||||
"open-url", "jndi-injection", "ldap", "sql", "jdbc-url", "logging", "mvel", "xpath",
|
||||
"groovy", "xss", "ognl-injection", "intent-start", "pending-intent-sent",
|
||||
"url-open-stream", "url-redirect", "create-file", "read-file", "write-file",
|
||||
"set-hostname-verifier", "header-splitting", "information-leak", "xslt", "jexl",
|
||||
"bean-validation", "ssti", "fragment-injection", "command-injection"
|
||||
"groovy", "xss", "ognl-injection", "intent-start", "pending-intent-sent", "url-redirect",
|
||||
"create-file", "read-file", "write-file", "set-hostname-verifier", "header-splitting",
|
||||
"information-leak", "xslt", "jexl", "bean-validation", "ssti", "fragment-injection",
|
||||
"command-injection"
|
||||
] and
|
||||
not kind.matches("regex-use%") and
|
||||
not kind.matches("qltest%") and
|
||||
@@ -288,6 +290,11 @@ module ModelValidation {
|
||||
not kind.matches("qltest%") and
|
||||
result = "Invalid kind \"" + kind + "\" in source model."
|
||||
)
|
||||
or
|
||||
exists(string kind | neutralModel(_, _, _, _, kind, _) |
|
||||
not kind = ["summary", "source", "sink"] and
|
||||
result = "Invalid kind \"" + kind + "\" in neutral model."
|
||||
)
|
||||
}
|
||||
|
||||
private string getInvalidModelSignature() {
|
||||
@@ -302,7 +309,7 @@ module ModelValidation {
|
||||
summaryModel(package, type, _, name, signature, ext, _, _, _, provenance) and
|
||||
pred = "summary"
|
||||
or
|
||||
neutralModel(package, type, name, signature, provenance) and
|
||||
neutralModel(package, type, name, signature, _, provenance) and
|
||||
ext = "" and
|
||||
pred = "neutral"
|
||||
|
|
||||
@@ -346,7 +353,7 @@ private predicate elementSpec(
|
||||
or
|
||||
summaryModel(package, type, subtypes, name, signature, ext, _, _, _, _)
|
||||
or
|
||||
neutralModel(package, type, name, signature, _) and ext = "" and subtypes = false
|
||||
neutralModel(package, type, name, signature, _, _) and ext = "" and subtypes = false
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -27,10 +27,10 @@ extensible predicate summaryModel(
|
||||
);
|
||||
|
||||
/**
|
||||
* Holds if a neutral model exists indicating there is no flow for the given parameters.
|
||||
* Holds if a neutral model exists for the given parameters.
|
||||
*/
|
||||
extensible predicate neutralModel(
|
||||
string package, string type, string name, string signature, string provenance
|
||||
string package, string type, string name, string signature, string kind, string provenance
|
||||
);
|
||||
|
||||
/**
|
||||
|
||||
@@ -18,6 +18,7 @@ private module Frameworks {
|
||||
private import semmle.code.java.frameworks.ApacheHttp
|
||||
private import semmle.code.java.frameworks.guava.Guava
|
||||
private import semmle.code.java.frameworks.Guice
|
||||
private import semmle.code.java.frameworks.IoJsonWebToken
|
||||
private import semmle.code.java.frameworks.jackson.JacksonSerializability
|
||||
private import semmle.code.java.frameworks.Properties
|
||||
private import semmle.code.java.frameworks.Protobuf
|
||||
|
||||
@@ -132,28 +132,28 @@ class InstanceAccessExt extends TInstanceAccessExt {
|
||||
result = enc.getQualifier().toString() + "(" + enc.getType() + ")enclosing"
|
||||
)
|
||||
or
|
||||
isOwnInstanceAccess() and result = "this"
|
||||
this.isOwnInstanceAccess() and result = "this"
|
||||
}
|
||||
|
||||
private string ppKind() {
|
||||
isExplicit(_) and result = " <" + getAssociatedExprOrStmt().toString() + ">"
|
||||
this.isExplicit(_) and result = " <" + this.getAssociatedExprOrStmt().toString() + ">"
|
||||
or
|
||||
isImplicitFieldQualifier(_) and result = " <.field>"
|
||||
this.isImplicitFieldQualifier(_) and result = " <.field>"
|
||||
or
|
||||
isImplicitMethodQualifier(_) and result = " <.method>"
|
||||
this.isImplicitMethodQualifier(_) and result = " <.method>"
|
||||
or
|
||||
isImplicitThisConstructorArgument(_) and result = " <constr(this)>"
|
||||
this.isImplicitThisConstructorArgument(_) and result = " <constr(this)>"
|
||||
or
|
||||
isImplicitEnclosingInstanceCapture(_) and result = " <.new>"
|
||||
this.isImplicitEnclosingInstanceCapture(_) and result = " <.new>"
|
||||
or
|
||||
isImplicitEnclosingInstanceQualifier(_) and result = "."
|
||||
this.isImplicitEnclosingInstanceQualifier(_) and result = "."
|
||||
}
|
||||
|
||||
/** Gets a textual representation of this element. */
|
||||
string toString() { result = ppBase() + ppKind() }
|
||||
string toString() { result = this.ppBase() + this.ppKind() }
|
||||
|
||||
/** Gets the source location for this element. */
|
||||
Location getLocation() { result = getAssociatedExprOrStmt().getLocation() }
|
||||
Location getLocation() { result = this.getAssociatedExprOrStmt().getLocation() }
|
||||
|
||||
private ExprParent getAssociatedExprOrStmt() {
|
||||
this = TExplicitInstanceAccess(result) or
|
||||
@@ -166,8 +166,8 @@ class InstanceAccessExt extends TInstanceAccessExt {
|
||||
|
||||
/** Gets the callable in which this instance access occurs. */
|
||||
Callable getEnclosingCallable() {
|
||||
result = getAssociatedExprOrStmt().(Expr).getEnclosingCallable() or
|
||||
result = getAssociatedExprOrStmt().(Stmt).getEnclosingCallable()
|
||||
result = this.getAssociatedExprOrStmt().(Expr).getEnclosingCallable() or
|
||||
result = this.getAssociatedExprOrStmt().(Stmt).getEnclosingCallable()
|
||||
}
|
||||
|
||||
/** Holds if this is the explicit instance access `ia`. */
|
||||
@@ -206,7 +206,7 @@ class InstanceAccessExt extends TInstanceAccessExt {
|
||||
}
|
||||
|
||||
/** Holds if this is an access to an object's own instance. */
|
||||
predicate isOwnInstanceAccess() { not isEnclosingInstanceAccess(_) }
|
||||
predicate isOwnInstanceAccess() { not this.isEnclosingInstanceAccess(_) }
|
||||
|
||||
/** Holds if this is an access to an enclosing instance. */
|
||||
predicate isEnclosingInstanceAccess(RefType t) {
|
||||
@@ -221,14 +221,14 @@ class InstanceAccessExt extends TInstanceAccessExt {
|
||||
|
||||
/** Gets the type of this instance access. */
|
||||
RefType getType() {
|
||||
isEnclosingInstanceAccess(result)
|
||||
this.isEnclosingInstanceAccess(result)
|
||||
or
|
||||
isOwnInstanceAccess() and result = getEnclosingCallable().getDeclaringType()
|
||||
this.isOwnInstanceAccess() and result = this.getEnclosingCallable().getDeclaringType()
|
||||
}
|
||||
|
||||
/** Gets the control flow node associated with this instance access. */
|
||||
ControlFlowNode getCfgNode() {
|
||||
exists(ExprParent e | e = getAssociatedExprOrStmt() |
|
||||
exists(ExprParent e | e = this.getAssociatedExprOrStmt() |
|
||||
e instanceof Call and result = e
|
||||
or
|
||||
e instanceof InstanceAccess and result = e
|
||||
@@ -244,14 +244,14 @@ class InstanceAccessExt extends TInstanceAccessExt {
|
||||
* An access to an object's own instance.
|
||||
*/
|
||||
class OwnInstanceAccess extends InstanceAccessExt {
|
||||
OwnInstanceAccess() { isOwnInstanceAccess() }
|
||||
OwnInstanceAccess() { this.isOwnInstanceAccess() }
|
||||
}
|
||||
|
||||
/**
|
||||
* An access to an enclosing instance.
|
||||
*/
|
||||
class EnclosingInstanceAccess extends InstanceAccessExt {
|
||||
EnclosingInstanceAccess() { isEnclosingInstanceAccess(_) }
|
||||
EnclosingInstanceAccess() { this.isEnclosingInstanceAccess(_) }
|
||||
|
||||
/** Gets the implicit qualifier of this in the desugared representation. */
|
||||
InstanceAccessExt getQualifier() {
|
||||
|
||||
@@ -292,7 +292,7 @@ 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() }
|
||||
override string toString() { result = this.getCond().toString() }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -362,7 +362,7 @@ private predicate safeCast(Type fromtyp, Type totyp) {
|
||||
*/
|
||||
private class RangeAnalysisSafeCastingExpr extends CastingExpr {
|
||||
RangeAnalysisSafeCastingExpr() {
|
||||
safeCast(getExpr().getType(), getType()) or
|
||||
safeCast(this.getExpr().getType(), this.getType()) or
|
||||
this instanceof ImplicitCastExpr or
|
||||
this instanceof ImplicitNotNullExpr or
|
||||
this instanceof ImplicitCoercionToUnitExpr
|
||||
@@ -388,14 +388,14 @@ private predicate typeBound(Type typ, int lowerbound, int upperbound) {
|
||||
private class NarrowingCastingExpr extends CastingExpr {
|
||||
NarrowingCastingExpr() {
|
||||
not this instanceof RangeAnalysisSafeCastingExpr and
|
||||
typeBound(getType(), _, _)
|
||||
typeBound(this.getType(), _, _)
|
||||
}
|
||||
|
||||
/** Gets the lower bound of the resulting type. */
|
||||
int getLowerBound() { typeBound(getType(), result, _) }
|
||||
int getLowerBound() { typeBound(this.getType(), result, _) }
|
||||
|
||||
/** Gets the upper bound of the resulting type. */
|
||||
int getUpperBound() { typeBound(getType(), _, result) }
|
||||
int getUpperBound() { typeBound(this.getType(), _, result) }
|
||||
}
|
||||
|
||||
/** Holds if `e >= 1` as determined by sign analysis. */
|
||||
|
||||
@@ -24,17 +24,17 @@ private newtype TTypeFlowNode =
|
||||
*/
|
||||
private class TypeFlowNode extends TTypeFlowNode {
|
||||
string toString() {
|
||||
result = asField().toString() or
|
||||
result = asSsa().toString() or
|
||||
result = asExpr().toString() or
|
||||
result = asMethod().toString()
|
||||
result = this.asField().toString() or
|
||||
result = this.asSsa().toString() or
|
||||
result = this.asExpr().toString() or
|
||||
result = this.asMethod().toString()
|
||||
}
|
||||
|
||||
Location getLocation() {
|
||||
result = asField().getLocation() or
|
||||
result = asSsa().getLocation() or
|
||||
result = asExpr().getLocation() or
|
||||
result = asMethod().getLocation()
|
||||
result = this.asField().getLocation() or
|
||||
result = this.asSsa().getLocation() or
|
||||
result = this.asExpr().getLocation() or
|
||||
result = this.asMethod().getLocation()
|
||||
}
|
||||
|
||||
Field asField() { this = TField(result) }
|
||||
@@ -46,10 +46,10 @@ private class TypeFlowNode extends TTypeFlowNode {
|
||||
Method asMethod() { this = TMethod(result) }
|
||||
|
||||
RefType getType() {
|
||||
result = asField().getType() or
|
||||
result = asSsa().getSourceVariable().getType() or
|
||||
result = boxIfNeeded(asExpr().getType()) or
|
||||
result = asMethod().getReturnType()
|
||||
result = this.asField().getType() or
|
||||
result = this.asSsa().getSourceVariable().getType() or
|
||||
result = boxIfNeeded(this.asExpr().getType()) or
|
||||
result = this.asMethod().getReturnType()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -815,24 +815,20 @@ private module Cached {
|
||||
)
|
||||
}
|
||||
|
||||
private predicate store(
|
||||
Node node1, Content c, Node node2, DataFlowType contentType, DataFlowType containerType
|
||||
) {
|
||||
exists(ContentSet cs |
|
||||
c = cs.getAStoreContent() and storeSet(node1, cs, node2, contentType, containerType)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if data can flow from `node1` to `node2` via a direct assignment to
|
||||
* `f`.
|
||||
* `c`.
|
||||
*
|
||||
* This includes reverse steps through reads when the result of the read has
|
||||
* been stored into, in order to handle cases like `x.f1.f2 = y`.
|
||||
*/
|
||||
cached
|
||||
predicate store(Node node1, TypedContent tc, Node node2, DataFlowType contentType) {
|
||||
store(node1, tc.getContent(), node2, contentType, tc.getContainerType())
|
||||
predicate store(
|
||||
Node node1, Content c, Node node2, DataFlowType contentType, DataFlowType containerType
|
||||
) {
|
||||
exists(ContentSet cs |
|
||||
c = cs.getAStoreContent() and storeSet(node1, cs, node2, contentType, containerType)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -932,36 +928,15 @@ private module Cached {
|
||||
TReturnCtxNoFlowThrough() or
|
||||
TReturnCtxMaybeFlowThrough(ReturnPosition pos)
|
||||
|
||||
cached
|
||||
newtype TTypedContentApprox =
|
||||
MkTypedContentApprox(ContentApprox c, DataFlowType t) {
|
||||
exists(Content cont |
|
||||
c = getContentApprox(cont) and
|
||||
store(_, cont, _, _, t)
|
||||
)
|
||||
}
|
||||
|
||||
cached
|
||||
newtype TTypedContent = MkTypedContent(Content c, DataFlowType t) { store(_, c, _, _, t) }
|
||||
|
||||
cached
|
||||
TypedContent getATypedContent(TypedContentApprox c) {
|
||||
exists(ContentApprox cls, DataFlowType t, Content cont |
|
||||
c = MkTypedContentApprox(cls, pragma[only_bind_into](t)) and
|
||||
result = MkTypedContent(cont, pragma[only_bind_into](t)) and
|
||||
cls = getContentApprox(cont)
|
||||
)
|
||||
}
|
||||
|
||||
cached
|
||||
newtype TAccessPathFront =
|
||||
TFrontNil(DataFlowType t) or
|
||||
TFrontHead(TypedContent tc)
|
||||
TFrontNil() or
|
||||
TFrontHead(Content c)
|
||||
|
||||
cached
|
||||
newtype TApproxAccessPathFront =
|
||||
TApproxFrontNil(DataFlowType t) or
|
||||
TApproxFrontHead(TypedContentApprox tc)
|
||||
TApproxFrontNil() or
|
||||
TApproxFrontHead(ContentApprox c)
|
||||
|
||||
cached
|
||||
newtype TAccessPathFrontOption =
|
||||
@@ -986,8 +961,16 @@ predicate recordDataFlowCallSite(DataFlowCall call, DataFlowCallable callable) {
|
||||
/**
|
||||
* A `Node` at which a cast can occur such that the type should be checked.
|
||||
*/
|
||||
class CastingNode extends Node {
|
||||
class CastingNode instanceof Node {
|
||||
CastingNode() { castingNode(this) }
|
||||
|
||||
string toString() { result = super.toString() }
|
||||
|
||||
predicate hasLocationInfo(
|
||||
string filepath, int startline, int startcolumn, int endline, int endcolumn
|
||||
) {
|
||||
super.hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn)
|
||||
}
|
||||
}
|
||||
|
||||
private predicate readStepWithTypes(
|
||||
@@ -1135,9 +1118,17 @@ LocalCallContext getLocalCallContext(CallContext ctx, DataFlowCallable callable)
|
||||
* The value of a parameter at function entry, viewed as a node in a data
|
||||
* flow graph.
|
||||
*/
|
||||
class ParamNode extends Node {
|
||||
class ParamNode instanceof Node {
|
||||
ParamNode() { parameterNode(this, _, _) }
|
||||
|
||||
string toString() { result = super.toString() }
|
||||
|
||||
predicate hasLocationInfo(
|
||||
string filepath, int startline, int startcolumn, int endline, int endcolumn
|
||||
) {
|
||||
super.hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if this node is the parameter of callable `c` at the specified
|
||||
* position.
|
||||
@@ -1146,9 +1137,17 @@ class ParamNode extends Node {
|
||||
}
|
||||
|
||||
/** A data-flow node that represents a call argument. */
|
||||
class ArgNode extends Node {
|
||||
class ArgNode instanceof Node {
|
||||
ArgNode() { argumentNode(this, _, _) }
|
||||
|
||||
string toString() { result = super.toString() }
|
||||
|
||||
predicate hasLocationInfo(
|
||||
string filepath, int startline, int startcolumn, int endline, int endcolumn
|
||||
) {
|
||||
super.hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn)
|
||||
}
|
||||
|
||||
/** Holds if this argument occurs at the given position in the given call. */
|
||||
final predicate argumentOf(DataFlowCall call, ArgumentPosition pos) {
|
||||
argumentNode(this, call, pos)
|
||||
@@ -1159,9 +1158,17 @@ class ArgNode extends Node {
|
||||
* A node from which flow can return to the caller. This is either a regular
|
||||
* `ReturnNode` or a `PostUpdateNode` corresponding to the value of a parameter.
|
||||
*/
|
||||
class ReturnNodeExt extends Node {
|
||||
class ReturnNodeExt instanceof Node {
|
||||
ReturnNodeExt() { returnNodeExt(this, _) }
|
||||
|
||||
string toString() { result = super.toString() }
|
||||
|
||||
predicate hasLocationInfo(
|
||||
string filepath, int startline, int startcolumn, int endline, int endcolumn
|
||||
) {
|
||||
super.hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn)
|
||||
}
|
||||
|
||||
/** Gets the kind of this returned value. */
|
||||
ReturnKindExt getKind() { returnNodeExt(this, result) }
|
||||
}
|
||||
@@ -1170,8 +1177,16 @@ 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 {
|
||||
class OutNodeExt instanceof Node {
|
||||
OutNodeExt() { outNodeExt(this) }
|
||||
|
||||
string toString() { result = super.toString() }
|
||||
|
||||
predicate hasLocationInfo(
|
||||
string filepath, int startline, int startcolumn, int endline, int endcolumn
|
||||
) {
|
||||
super.hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1387,67 +1402,37 @@ class ReturnCtx extends TReturnCtx {
|
||||
}
|
||||
}
|
||||
|
||||
/** An approximated `Content` tagged with the type of a containing object. */
|
||||
class TypedContentApprox extends MkTypedContentApprox {
|
||||
private ContentApprox c;
|
||||
private DataFlowType t;
|
||||
|
||||
TypedContentApprox() { this = MkTypedContentApprox(c, t) }
|
||||
|
||||
/** Gets a typed content approximated by this value. */
|
||||
TypedContent getATypedContent() { result = getATypedContent(this) }
|
||||
|
||||
/** Gets the content. */
|
||||
ContentApprox getContent() { result = c }
|
||||
|
||||
/** Gets the container type. */
|
||||
DataFlowType getContainerType() { result = t }
|
||||
|
||||
/** Gets a textual representation of this approximated content. */
|
||||
string toString() { result = c.toString() }
|
||||
}
|
||||
|
||||
/**
|
||||
* The front of an approximated access path. This is either a head or a nil.
|
||||
*/
|
||||
abstract class ApproxAccessPathFront extends TApproxAccessPathFront {
|
||||
abstract string toString();
|
||||
|
||||
abstract DataFlowType getType();
|
||||
|
||||
abstract boolean toBoolNonEmpty();
|
||||
|
||||
TypedContentApprox getHead() { this = TApproxFrontHead(result) }
|
||||
ContentApprox getHead() { this = TApproxFrontHead(result) }
|
||||
|
||||
pragma[nomagic]
|
||||
TypedContent getAHead() {
|
||||
exists(TypedContentApprox cont |
|
||||
Content getAHead() {
|
||||
exists(ContentApprox cont |
|
||||
this = TApproxFrontHead(cont) and
|
||||
result = cont.getATypedContent()
|
||||
cont = getContentApprox(result)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
class ApproxAccessPathFrontNil extends ApproxAccessPathFront, TApproxFrontNil {
|
||||
private DataFlowType t;
|
||||
|
||||
ApproxAccessPathFrontNil() { this = TApproxFrontNil(t) }
|
||||
|
||||
override string toString() { result = ppReprType(t) }
|
||||
|
||||
override DataFlowType getType() { result = t }
|
||||
override string toString() { result = "nil" }
|
||||
|
||||
override boolean toBoolNonEmpty() { result = false }
|
||||
}
|
||||
|
||||
class ApproxAccessPathFrontHead extends ApproxAccessPathFront, TApproxFrontHead {
|
||||
private TypedContentApprox tc;
|
||||
private ContentApprox c;
|
||||
|
||||
ApproxAccessPathFrontHead() { this = TApproxFrontHead(tc) }
|
||||
ApproxAccessPathFrontHead() { this = TApproxFrontHead(c) }
|
||||
|
||||
override string toString() { result = tc.toString() }
|
||||
|
||||
override DataFlowType getType() { result = tc.getContainerType() }
|
||||
override string toString() { result = c.toString() }
|
||||
|
||||
override boolean toBoolNonEmpty() { result = true }
|
||||
}
|
||||
@@ -1461,65 +1446,31 @@ class ApproxAccessPathFrontOption extends TApproxAccessPathFrontOption {
|
||||
}
|
||||
}
|
||||
|
||||
/** A `Content` tagged with the type of a containing object. */
|
||||
class TypedContent extends MkTypedContent {
|
||||
private Content c;
|
||||
private DataFlowType t;
|
||||
|
||||
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() }
|
||||
|
||||
/**
|
||||
* Holds if access paths with this `TypedContent` at their head always should
|
||||
* be tracked at high precision. This disables adaptive access path precision
|
||||
* for such access paths.
|
||||
*/
|
||||
predicate forceHighPrecision() { forceHighPrecision(c) }
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 ApproxAccessPathFront toApprox();
|
||||
|
||||
TypedContent getHead() { this = TFrontHead(result) }
|
||||
Content getHead() { this = TFrontHead(result) }
|
||||
}
|
||||
|
||||
class AccessPathFrontNil extends AccessPathFront, TFrontNil {
|
||||
private DataFlowType t;
|
||||
override string toString() { result = "nil" }
|
||||
|
||||
AccessPathFrontNil() { this = TFrontNil(t) }
|
||||
|
||||
override string toString() { result = ppReprType(t) }
|
||||
|
||||
override DataFlowType getType() { result = t }
|
||||
|
||||
override ApproxAccessPathFront toApprox() { result = TApproxFrontNil(t) }
|
||||
override ApproxAccessPathFront toApprox() { result = TApproxFrontNil() }
|
||||
}
|
||||
|
||||
class AccessPathFrontHead extends AccessPathFront, TFrontHead {
|
||||
private TypedContent tc;
|
||||
private Content c;
|
||||
|
||||
AccessPathFrontHead() { this = TFrontHead(tc) }
|
||||
AccessPathFrontHead() { this = TFrontHead(c) }
|
||||
|
||||
override string toString() { result = tc.toString() }
|
||||
override string toString() { result = c.toString() }
|
||||
|
||||
override DataFlowType getType() { result = tc.getContainerType() }
|
||||
|
||||
override ApproxAccessPathFront toApprox() { result.getAHead() = tc }
|
||||
override ApproxAccessPathFront toApprox() { result.getAHead() = c }
|
||||
}
|
||||
|
||||
/** An optional access path front. */
|
||||
|
||||
@@ -58,6 +58,9 @@ module Consistency {
|
||||
predicate uniqueParameterNodePositionExclude(DataFlowCallable c, ParameterPosition pos, Node p) {
|
||||
none()
|
||||
}
|
||||
|
||||
/** Holds if `n` should be excluded from the consistency test `identityLocalStep`. */
|
||||
predicate identityLocalStepExclude(Node n) { none() }
|
||||
}
|
||||
|
||||
private class RelevantNode extends Node {
|
||||
@@ -287,4 +290,10 @@ module Consistency {
|
||||
not exists(unique(ContentApprox approx | approx = getContentApprox(c))) and
|
||||
msg = "Non-unique content approximation."
|
||||
}
|
||||
|
||||
query predicate identityLocalStep(Node n, string msg) {
|
||||
simpleLocalFlowStep(n, n) and
|
||||
not any(ConsistencyConfiguration c).identityLocalStepExclude(n) and
|
||||
msg = "Node steps to itself"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -374,7 +374,7 @@ int accessPathLimit() { result = 5 }
|
||||
* precision. This disables adaptive access path precision for such access paths.
|
||||
*/
|
||||
predicate forceHighPrecision(Content c) {
|
||||
c instanceof ArrayContent or c instanceof CollectionContent
|
||||
c instanceof ArrayContent or c instanceof CollectionContent or c instanceof MapValueContent
|
||||
}
|
||||
|
||||
/** Holds if `n` should be hidden from path explanations. */
|
||||
|
||||
@@ -335,7 +335,7 @@ module Public {
|
||||
class NeutralCallable extends SummarizedCallableBase {
|
||||
private Provenance provenance;
|
||||
|
||||
NeutralCallable() { neutralElement(this, provenance) }
|
||||
NeutralCallable() { neutralSummaryElement(this, provenance) }
|
||||
|
||||
/**
|
||||
* Holds if the neutral is auto generated.
|
||||
|
||||
@@ -154,12 +154,12 @@ predicate summaryElement(
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if a neutral model exists for `c` with provenance `provenance`,
|
||||
* Holds if a neutral summary model exists for `c` with provenance `provenance`,
|
||||
* which means that there is no flow through `c`.
|
||||
*/
|
||||
predicate neutralElement(SummarizedCallableBase c, string provenance) {
|
||||
predicate neutralSummaryElement(SummarizedCallableBase c, string provenance) {
|
||||
exists(string namespace, string type, string name, string signature |
|
||||
neutralModel(namespace, type, name, signature, provenance) and
|
||||
neutralModel(namespace, type, name, signature, "summary", provenance) and
|
||||
c.asCallable() = interpretElement(namespace, type, false, name, signature, "")
|
||||
)
|
||||
}
|
||||
|
||||
@@ -90,7 +90,7 @@ class Sign extends TSign {
|
||||
* Gets a possible sign after subtracting an expression with sign `s` from an expression
|
||||
* that has this sign.
|
||||
*/
|
||||
Sign sub(Sign s) { result = add(s.neg()) }
|
||||
Sign sub(Sign s) { result = this.add(s.neg()) }
|
||||
|
||||
/**
|
||||
* Gets a possible sign after multiplying an expression with sign `s` to an expression
|
||||
@@ -244,37 +244,37 @@ class Sign extends TSign {
|
||||
|
||||
/** Perform `op` on this sign. */
|
||||
Sign applyUnaryOp(TUnarySignOperation op) {
|
||||
op = TIncOp() and result = inc()
|
||||
op = TIncOp() and result = this.inc()
|
||||
or
|
||||
op = TDecOp() and result = dec()
|
||||
op = TDecOp() and result = this.dec()
|
||||
or
|
||||
op = TNegOp() and result = neg()
|
||||
op = TNegOp() and result = this.neg()
|
||||
or
|
||||
op = TBitNotOp() and result = bitnot()
|
||||
op = TBitNotOp() and result = this.bitnot()
|
||||
}
|
||||
|
||||
/** Perform `op` on this sign and sign `s`. */
|
||||
Sign applyBinaryOp(Sign s, TBinarySignOperation op) {
|
||||
op = TAddOp() and result = add(s)
|
||||
op = TAddOp() and result = this.add(s)
|
||||
or
|
||||
op = TSubOp() and result = sub(s)
|
||||
op = TSubOp() and result = this.sub(s)
|
||||
or
|
||||
op = TMulOp() and result = mul(s)
|
||||
op = TMulOp() and result = this.mul(s)
|
||||
or
|
||||
op = TDivOp() and result = div(s)
|
||||
op = TDivOp() and result = this.div(s)
|
||||
or
|
||||
op = TRemOp() and result = rem(s)
|
||||
op = TRemOp() and result = this.rem(s)
|
||||
or
|
||||
op = TBitAndOp() and result = bitand(s)
|
||||
op = TBitAndOp() and result = this.bitand(s)
|
||||
or
|
||||
op = TBitOrOp() and result = bitor(s)
|
||||
op = TBitOrOp() and result = this.bitor(s)
|
||||
or
|
||||
op = TBitXorOp() and result = bitxor(s)
|
||||
op = TBitXorOp() and result = this.bitxor(s)
|
||||
or
|
||||
op = TLeftShiftOp() and result = lshift(s)
|
||||
op = TLeftShiftOp() and result = this.lshift(s)
|
||||
or
|
||||
op = TRightShiftOp() and result = rshift(s)
|
||||
op = TRightShiftOp() and result = this.rshift(s)
|
||||
or
|
||||
op = TUnsignedRightShiftOp() and result = urshift(s)
|
||||
op = TUnsignedRightShiftOp() and result = this.urshift(s)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -42,8 +42,8 @@ class SpringFactoryMethod extends CallableEntryPoint {
|
||||
*/
|
||||
class SpringBeanAnnotatedMethod extends CallableEntryPoint {
|
||||
SpringBeanAnnotatedMethod() {
|
||||
hasAnnotation("org.springframework.context.annotation", "Bean") and
|
||||
getDeclaringType().(SpringComponent).isLive()
|
||||
this.hasAnnotation("org.springframework.context.annotation", "Bean") and
|
||||
this.getDeclaringType().(SpringComponent).isLive()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -59,9 +59,9 @@ class SpringControllerEntryPoint extends CallableEntryPoint instanceof SpringCon
|
||||
class SpringResponseAccessibleMethod extends CallableEntryPoint {
|
||||
SpringResponseAccessibleMethod() {
|
||||
// Must be on a type used in a Model response.
|
||||
getDeclaringType() instanceof SpringModelResponseType and
|
||||
this.getDeclaringType() instanceof SpringModelResponseType and
|
||||
// Must be public.
|
||||
isPublic()
|
||||
this.isPublic()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -72,10 +72,11 @@ class SpringResponseAccessibleMethod extends CallableEntryPoint {
|
||||
class SpringManagedResource extends CallableEntryPoint {
|
||||
SpringManagedResource() {
|
||||
(
|
||||
hasAnnotation("org.springframework.jmx.export.annotation", "ManagedAttribute") or
|
||||
hasAnnotation("org.springframework.jmx.export.annotation", "ManagedOperation")
|
||||
this.hasAnnotation("org.springframework.jmx.export.annotation", "ManagedAttribute") or
|
||||
this.hasAnnotation("org.springframework.jmx.export.annotation", "ManagedOperation")
|
||||
) and
|
||||
getDeclaringType().hasAnnotation("org.springframework.jmx.export.annotation", "ManagedResource")
|
||||
this.getDeclaringType()
|
||||
.hasAnnotation("org.springframework.jmx.export.annotation", "ManagedResource")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -84,18 +85,18 @@ class SpringManagedResource extends CallableEntryPoint {
|
||||
*/
|
||||
class SpringPersistenceConstructor extends CallableEntryPoint {
|
||||
SpringPersistenceConstructor() {
|
||||
hasAnnotation("org.springframework.data.annotation", "PersistenceConstructor") and
|
||||
getDeclaringType() instanceof PersistentEntity
|
||||
this.hasAnnotation("org.springframework.data.annotation", "PersistenceConstructor") and
|
||||
this.getDeclaringType() instanceof PersistentEntity
|
||||
}
|
||||
}
|
||||
|
||||
class SpringAspect extends CallableEntryPoint {
|
||||
SpringAspect() {
|
||||
(
|
||||
hasAnnotation("org.aspectj.lang.annotation", "Around") or
|
||||
hasAnnotation("org.aspectj.lang.annotation", "Before")
|
||||
this.hasAnnotation("org.aspectj.lang.annotation", "Around") or
|
||||
this.hasAnnotation("org.aspectj.lang.annotation", "Before")
|
||||
) and
|
||||
getDeclaringType().hasAnnotation("org.aspectj.lang.annotation", "Aspect")
|
||||
this.getDeclaringType().hasAnnotation("org.aspectj.lang.annotation", "Aspect")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -105,10 +106,10 @@ class SpringAspect extends CallableEntryPoint {
|
||||
class SpringCli extends CallableEntryPoint {
|
||||
SpringCli() {
|
||||
(
|
||||
hasAnnotation("org.springframework.shell.core.annotation", "CliCommand") or
|
||||
hasAnnotation("org.springframework.shell.core.annotation", "CliAvailabilityIndicator")
|
||||
this.hasAnnotation("org.springframework.shell.core.annotation", "CliCommand") or
|
||||
this.hasAnnotation("org.springframework.shell.core.annotation", "CliAvailabilityIndicator")
|
||||
) and
|
||||
getDeclaringType()
|
||||
this.getDeclaringType()
|
||||
.getAnAncestor()
|
||||
.hasQualifiedName("org.springframework.shell.core", "CommandMarker")
|
||||
}
|
||||
|
||||
@@ -6,14 +6,16 @@ import external.ExternalArtifact
|
||||
* A method in a FIT fixture class, typically used in the fitnesse framework.
|
||||
*/
|
||||
class FitFixtureEntryPoint extends CallableEntryPoint {
|
||||
FitFixtureEntryPoint() { getDeclaringType().getAnAncestor().hasQualifiedName("fit", "Fixture") }
|
||||
FitFixtureEntryPoint() {
|
||||
this.getDeclaringType().getAnAncestor().hasQualifiedName("fit", "Fixture")
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* FitNesse entry points externally defined.
|
||||
*/
|
||||
class FitNesseSlimEntryPointData extends ExternalData {
|
||||
FitNesseSlimEntryPointData() { getDataPath() = "fitnesse.csv" }
|
||||
FitNesseSlimEntryPointData() { this.getDataPath() = "fitnesse.csv" }
|
||||
|
||||
/**
|
||||
* Gets the class name.
|
||||
@@ -21,34 +23,34 @@ class FitNesseSlimEntryPointData extends ExternalData {
|
||||
* This may be a fully qualified name, or just the name of the class. It may also be, or
|
||||
* include, a FitNesse symbol, in which case it can be ignored.
|
||||
*/
|
||||
string getClassName() { result = getField(0) }
|
||||
string getClassName() { result = this.getField(0) }
|
||||
|
||||
/**
|
||||
* Gets a Class that either has `getClassName()` as the fully qualified name, or as the class name.
|
||||
*/
|
||||
Class getACandidateClass() {
|
||||
result.getQualifiedName().matches(getClassName()) or
|
||||
result.getName() = getClassName()
|
||||
result.getQualifiedName().matches(this.getClassName()) or
|
||||
result.getName() = this.getClassName()
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the name of the callable that will be called.
|
||||
*/
|
||||
string getCallableName() { result = getField(1) }
|
||||
string getCallableName() { result = this.getField(1) }
|
||||
|
||||
/**
|
||||
* Gets the number of parameters for the callable that will be called.
|
||||
*/
|
||||
int getNumParameters() { result = getField(2).toInt() }
|
||||
int getNumParameters() { result = this.getField(2).toInt() }
|
||||
|
||||
/**
|
||||
* Gets a callable on one of the candidate classes that matches the criteria for the method name
|
||||
* and number of arguments.
|
||||
*/
|
||||
Callable getACandidateCallable() {
|
||||
result.getDeclaringType() = getACandidateClass() and
|
||||
result.getName() = getCallableName() and
|
||||
result.getNumberOfParameters() = getNumParameters()
|
||||
result.getDeclaringType() = this.getACandidateClass() and
|
||||
result.getName() = this.getCallableName() and
|
||||
result.getNumberOfParameters() = this.getNumParameters()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -64,7 +64,7 @@ class AssertionMethod extends Method {
|
||||
|
||||
/** Gets a call to the assertion method with `checkedArg` as argument. */
|
||||
MethodAccess getACheck(Expr checkedArg) {
|
||||
result = getACheck() and checkedArg = result.getAnArgument()
|
||||
result = this.getACheck() and checkedArg = result.getAnArgument()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -27,8 +27,8 @@ deprecated class CamelToURI = CamelToUri;
|
||||
class CamelToBeanUri extends CamelToUri {
|
||||
CamelToBeanUri() {
|
||||
// A `<to>` element references a bean if the URI starts with "bean:", or there is no scheme.
|
||||
matches("bean:%") or
|
||||
not exists(indexOf(":"))
|
||||
this.matches("bean:%") or
|
||||
not exists(this.indexOf(":"))
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -38,13 +38,13 @@ class CamelToBeanUri extends CamelToUri {
|
||||
* parameter parts are optional.
|
||||
*/
|
||||
string getBeanIdentifier() {
|
||||
if not exists(indexOf(":"))
|
||||
if not exists(this.indexOf(":"))
|
||||
then result = this
|
||||
else
|
||||
exists(int start | start = indexOf(":", 0, 0) + 1 |
|
||||
if not exists(indexOf("?"))
|
||||
then result = suffix(start)
|
||||
else result = substring(start, indexOf("?", 0, 0))
|
||||
exists(int start | start = this.indexOf(":", 0, 0) + 1 |
|
||||
if not exists(this.indexOf("?"))
|
||||
then result = this.suffix(start)
|
||||
else result = this.substring(start, this.indexOf("?", 0, 0))
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -8,7 +8,7 @@ import java
|
||||
* An annotation defined in the Cucumber library.
|
||||
*/
|
||||
class CucumberAnnotation extends Annotation {
|
||||
CucumberAnnotation() { getType().getPackage().getName().matches("cucumber.api.java%") }
|
||||
CucumberAnnotation() { this.getType().getPackage().getName().matches("cucumber.api.java%") }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -16,7 +16,9 @@ class CucumberAnnotation extends Annotation {
|
||||
*/
|
||||
class CucumberJava8Language extends Interface {
|
||||
CucumberJava8Language() {
|
||||
getASupertype().getAnAncestor().hasQualifiedName("cucumber.runtime.java8", "LambdaGlueBase")
|
||||
this.getASupertype()
|
||||
.getAnAncestor()
|
||||
.hasQualifiedName("cucumber.runtime.java8", "LambdaGlueBase")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -24,12 +26,12 @@ class CucumberJava8Language extends Interface {
|
||||
* A step definition for Cucumber.
|
||||
*/
|
||||
class CucumberStepDefinition extends Method {
|
||||
CucumberStepDefinition() { getAnAnnotation() instanceof CucumberAnnotation }
|
||||
CucumberStepDefinition() { this.getAnAnnotation() instanceof CucumberAnnotation }
|
||||
}
|
||||
|
||||
/**
|
||||
* A class containing Cucumber step definitions.
|
||||
*/
|
||||
class CucumberStepDefinitionClass extends Class {
|
||||
CucumberStepDefinitionClass() { getAMember() instanceof CucumberStepDefinition }
|
||||
CucumberStepDefinitionClass() { this.getAMember() instanceof CucumberStepDefinition }
|
||||
}
|
||||
|
||||
11
java/ql/lib/semmle/code/java/frameworks/IoJsonWebToken.qll
Normal file
11
java/ql/lib/semmle/code/java/frameworks/IoJsonWebToken.qll
Normal file
@@ -0,0 +1,11 @@
|
||||
/** Predicates and classes to reason about the `io.jsonwebtoken` library. */
|
||||
|
||||
import java
|
||||
private import semmle.code.java.dataflow.DataFlow
|
||||
private import semmle.code.java.dataflow.FlowSteps
|
||||
|
||||
private class JwsHeaderFieldsInheritTaint extends DataFlow::SyntheticFieldContent,
|
||||
TaintInheritingContent
|
||||
{
|
||||
JwsHeaderFieldsInheritTaint() { this.getField().matches("io.jsonwebtoken.JwsHeader.%") }
|
||||
}
|
||||
@@ -7,30 +7,30 @@ import java
|
||||
/*--- Types ---*/
|
||||
/** The interface `java.sql.Connection`. */
|
||||
class TypeConnection extends Interface {
|
||||
TypeConnection() { hasQualifiedName("java.sql", "Connection") }
|
||||
TypeConnection() { this.hasQualifiedName("java.sql", "Connection") }
|
||||
}
|
||||
|
||||
/** The interface `java.sql.PreparedStatement`. */
|
||||
class TypePreparedStatement extends Interface {
|
||||
TypePreparedStatement() { hasQualifiedName("java.sql", "PreparedStatement") }
|
||||
TypePreparedStatement() { this.hasQualifiedName("java.sql", "PreparedStatement") }
|
||||
}
|
||||
|
||||
/** The interface `java.sql.ResultSet`. */
|
||||
class TypeResultSet extends Interface {
|
||||
TypeResultSet() { hasQualifiedName("java.sql", "ResultSet") }
|
||||
TypeResultSet() { this.hasQualifiedName("java.sql", "ResultSet") }
|
||||
}
|
||||
|
||||
/** The interface `java.sql.Statement`. */
|
||||
class TypeStatement extends Interface {
|
||||
TypeStatement() { hasQualifiedName("java.sql", "Statement") }
|
||||
TypeStatement() { this.hasQualifiedName("java.sql", "Statement") }
|
||||
}
|
||||
|
||||
/*--- Methods ---*/
|
||||
/** A method with the name `getString` declared in `java.sql.ResultSet`. */
|
||||
class ResultSetGetStringMethod extends Method {
|
||||
ResultSetGetStringMethod() {
|
||||
getDeclaringType() instanceof TypeResultSet and
|
||||
hasName("getString") and
|
||||
getReturnType() instanceof TypeString
|
||||
this.getDeclaringType() instanceof TypeResultSet and
|
||||
this.hasName("getString") and
|
||||
this.getReturnType() instanceof TypeString
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,8 +13,8 @@ import java
|
||||
*/
|
||||
class LombokAnnotation extends Annotation {
|
||||
LombokAnnotation() {
|
||||
getType().getPackage().hasName("lombok") or
|
||||
getType().getPackage().getName().matches("lombok.%")
|
||||
this.getType().getPackage().hasName("lombok") or
|
||||
this.getType().getPackage().getName().matches("lombok.%")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -22,7 +22,7 @@ class LombokAnnotation extends Annotation {
|
||||
* A Lombok `@NonNull` annotation.
|
||||
*/
|
||||
class LombokNonNullAnnotation extends LombokAnnotation {
|
||||
LombokNonNullAnnotation() { getType().hasName("NonNull") }
|
||||
LombokNonNullAnnotation() { this.getType().hasName("NonNull") }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -32,7 +32,7 @@ class LombokNonNullAnnotation extends LombokAnnotation {
|
||||
* automatically closed by Lombok in a generated try-finally block.
|
||||
*/
|
||||
class LombokCleanupAnnotation extends LombokAnnotation {
|
||||
LombokCleanupAnnotation() { getType().hasName("Cleanup") }
|
||||
LombokCleanupAnnotation() { this.getType().hasName("Cleanup") }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -47,7 +47,7 @@ class LombokCleanupAnnotation extends LombokAnnotation {
|
||||
* overridden by specifying `AccessLevel.NONE` for a field.
|
||||
*/
|
||||
class LombokGetterAnnotation extends LombokAnnotation {
|
||||
LombokGetterAnnotation() { getType().hasName("Getter") }
|
||||
LombokGetterAnnotation() { this.getType().hasName("Getter") }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -62,7 +62,7 @@ class LombokGetterAnnotation extends LombokAnnotation {
|
||||
* overridden by specifying `AccessLevel.NONE` for a field.
|
||||
*/
|
||||
class LombokSetterAnnotation extends LombokAnnotation {
|
||||
LombokSetterAnnotation() { getType().hasName("Setter") }
|
||||
LombokSetterAnnotation() { this.getType().hasName("Setter") }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -72,7 +72,7 @@ class LombokSetterAnnotation extends LombokAnnotation {
|
||||
* generates a `toString()` method.
|
||||
*/
|
||||
class LombokToStringAnnotation extends LombokAnnotation {
|
||||
LombokToStringAnnotation() { getType().hasName("ToString") }
|
||||
LombokToStringAnnotation() { this.getType().hasName("ToString") }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -82,7 +82,7 @@ class LombokToStringAnnotation extends LombokAnnotation {
|
||||
* generates suitable `equals` and `hashCode` methods.
|
||||
*/
|
||||
class LombokEqualsAndHashCodeAnnotation extends LombokAnnotation {
|
||||
LombokEqualsAndHashCodeAnnotation() { getType().hasName("EqualsAndHashCode") }
|
||||
LombokEqualsAndHashCodeAnnotation() { this.getType().hasName("EqualsAndHashCode") }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -92,7 +92,7 @@ class LombokEqualsAndHashCodeAnnotation extends LombokAnnotation {
|
||||
* generates a constructor with no parameters.
|
||||
*/
|
||||
class LombokNoArgsConstructorAnnotation extends LombokAnnotation {
|
||||
LombokNoArgsConstructorAnnotation() { getType().hasName("NoArgsConstructor") }
|
||||
LombokNoArgsConstructorAnnotation() { this.getType().hasName("NoArgsConstructor") }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -104,7 +104,7 @@ class LombokNoArgsConstructorAnnotation extends LombokAnnotation {
|
||||
* where it is declared.
|
||||
*/
|
||||
class LombokRequiredArgsConstructorAnnotation extends LombokAnnotation {
|
||||
LombokRequiredArgsConstructorAnnotation() { getType().hasName("RequiredArgsConstructor") }
|
||||
LombokRequiredArgsConstructorAnnotation() { this.getType().hasName("RequiredArgsConstructor") }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -114,7 +114,7 @@ class LombokRequiredArgsConstructorAnnotation extends LombokAnnotation {
|
||||
* generates a constructor with a parameter for each field in the class.
|
||||
*/
|
||||
class LombokAllArgsConstructorAnnotation extends LombokAnnotation {
|
||||
LombokAllArgsConstructorAnnotation() { getType().hasName("AllArgsConstructor") }
|
||||
LombokAllArgsConstructorAnnotation() { this.getType().hasName("AllArgsConstructor") }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -124,7 +124,7 @@ class LombokAllArgsConstructorAnnotation extends LombokAnnotation {
|
||||
* fields, `@Setter` on all non-final fields, and `@RequiredArgsConstructor`.
|
||||
*/
|
||||
class LombokDataAnnotation extends LombokAnnotation {
|
||||
LombokDataAnnotation() { getType().hasName("Data") }
|
||||
LombokDataAnnotation() { this.getType().hasName("Data") }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -138,7 +138,7 @@ class LombokDataAnnotation extends LombokAnnotation {
|
||||
* ```
|
||||
*/
|
||||
class LombokValueAnnotation extends LombokAnnotation {
|
||||
LombokValueAnnotation() { getType().hasName("Value") }
|
||||
LombokValueAnnotation() { this.getType().hasName("Value") }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -148,7 +148,7 @@ class LombokValueAnnotation extends LombokAnnotation {
|
||||
* generates complex builder APIs for the class.
|
||||
*/
|
||||
class LombokBuilderAnnotation extends LombokAnnotation {
|
||||
LombokBuilderAnnotation() { getType().hasName("Builder") }
|
||||
LombokBuilderAnnotation() { this.getType().hasName("Builder") }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -158,7 +158,7 @@ class LombokBuilderAnnotation extends LombokAnnotation {
|
||||
* without declaring them in a `throws` clause.
|
||||
*/
|
||||
class LombokSneakyThrowsAnnotation extends LombokAnnotation {
|
||||
LombokSneakyThrowsAnnotation() { getType().hasName("SneakyThrows") }
|
||||
LombokSneakyThrowsAnnotation() { this.getType().hasName("SneakyThrows") }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -170,7 +170,7 @@ class LombokSneakyThrowsAnnotation extends LombokAnnotation {
|
||||
* methods annotated with `@Synchronized`.
|
||||
*/
|
||||
class LombokSynchronizedAnnotation extends LombokAnnotation {
|
||||
LombokSynchronizedAnnotation() { getType().hasName("Synchronized") }
|
||||
LombokSynchronizedAnnotation() { this.getType().hasName("Synchronized") }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -180,7 +180,7 @@ class LombokSynchronizedAnnotation extends LombokAnnotation {
|
||||
* generates a logger field named `log` with a specified type.
|
||||
*/
|
||||
class LombokLogAnnotation extends LombokAnnotation {
|
||||
LombokLogAnnotation() { getType().hasName("Log") }
|
||||
LombokLogAnnotation() { this.getType().hasName("Log") }
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -196,14 +196,14 @@ class LombokLogAnnotation extends LombokAnnotation {
|
||||
*/
|
||||
class LombokGetterAnnotatedField extends Field {
|
||||
LombokGetterAnnotatedField() {
|
||||
getAnAnnotation() instanceof LombokGetterAnnotation
|
||||
this.getAnAnnotation() instanceof LombokGetterAnnotation
|
||||
or
|
||||
exists(LombokAnnotation a |
|
||||
a instanceof LombokGetterAnnotation or
|
||||
a instanceof LombokDataAnnotation or
|
||||
a instanceof LombokValueAnnotation
|
||||
|
|
||||
a = getDeclaringType().getSourceDeclaration().getAnAnnotation()
|
||||
a = this.getDeclaringType().getSourceDeclaration().getAnAnnotation()
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -217,8 +217,8 @@ class LombokGetterAnnotatedField extends Field {
|
||||
*/
|
||||
class LombokEqualsAndHashCodeGeneratedClass extends Class {
|
||||
LombokEqualsAndHashCodeGeneratedClass() {
|
||||
getAnAnnotation() instanceof LombokEqualsAndHashCodeAnnotation or
|
||||
getAnAnnotation() instanceof LombokDataAnnotation or
|
||||
getAnAnnotation() instanceof LombokValueAnnotation
|
||||
this.getAnAnnotation() instanceof LombokEqualsAndHashCodeAnnotation or
|
||||
this.getAnAnnotation() instanceof LombokDataAnnotation or
|
||||
this.getAnAnnotation() instanceof LombokValueAnnotation
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,30 +7,30 @@ private import semmle.code.java.dataflow.FlowSteps
|
||||
* The `java.util.Properties` class.
|
||||
*/
|
||||
class TypeProperty extends Class {
|
||||
TypeProperty() { hasQualifiedName("java.util", "Properties") }
|
||||
TypeProperty() { this.hasQualifiedName("java.util", "Properties") }
|
||||
}
|
||||
|
||||
/** The `getProperty` method of the class `java.util.Properties`. */
|
||||
class PropertiesGetPropertyMethod extends Method {
|
||||
PropertiesGetPropertyMethod() {
|
||||
getDeclaringType() instanceof TypeProperty and
|
||||
hasName("getProperty")
|
||||
this.getDeclaringType() instanceof TypeProperty and
|
||||
this.hasName("getProperty")
|
||||
}
|
||||
}
|
||||
|
||||
/** The `get` method of the class `java.util.Properties`. */
|
||||
class PropertiesGetMethod extends Method {
|
||||
PropertiesGetMethod() {
|
||||
getDeclaringType() instanceof TypeProperty and
|
||||
hasName("get")
|
||||
this.getDeclaringType() instanceof TypeProperty and
|
||||
this.hasName("get")
|
||||
}
|
||||
}
|
||||
|
||||
/** The `setProperty` method of the class `java.util.Properties`. */
|
||||
class PropertiesSetPropertyMethod extends Method {
|
||||
PropertiesSetPropertyMethod() {
|
||||
getDeclaringType() instanceof TypeProperty and
|
||||
hasName("setProperty")
|
||||
this.getDeclaringType() instanceof TypeProperty and
|
||||
this.hasName("setProperty")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -39,7 +39,7 @@ class PropertiesSetPropertyMethod extends Method {
|
||||
*/
|
||||
class PropertiesStoreMethod extends Method {
|
||||
PropertiesStoreMethod() {
|
||||
getDeclaringType() instanceof TypeProperty and
|
||||
(getName().matches("store%") or getName() = "save")
|
||||
this.getDeclaringType() instanceof TypeProperty and
|
||||
(this.getName().matches("store%") or this.getName() = "save")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,7 +4,7 @@ import java
|
||||
|
||||
/** The interface `java.rmi.Remote`. */
|
||||
class TypeRemote extends RefType {
|
||||
TypeRemote() { hasQualifiedName("java.rmi", "Remote") }
|
||||
TypeRemote() { this.hasQualifiedName("java.rmi", "Remote") }
|
||||
}
|
||||
|
||||
/** A method that is intended to be called via RMI. */
|
||||
|
||||
@@ -10,7 +10,7 @@ import semmle.code.java.Reflection
|
||||
* The Selenium `PageFactory` class used to create page objects
|
||||
*/
|
||||
class SeleniumPageFactory extends Class {
|
||||
SeleniumPageFactory() { hasQualifiedName("org.openqa.selenium.support", "PageFactory") }
|
||||
SeleniumPageFactory() { this.hasQualifiedName("org.openqa.selenium.support", "PageFactory") }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -18,14 +18,14 @@ class SeleniumPageFactory extends Class {
|
||||
*/
|
||||
class SeleniumInitElementsAccess extends MethodAccess {
|
||||
SeleniumInitElementsAccess() {
|
||||
getMethod().getDeclaringType() instanceof SeleniumPageFactory and
|
||||
getMethod().hasName("initElements")
|
||||
this.getMethod().getDeclaringType() instanceof SeleniumPageFactory and
|
||||
this.getMethod().hasName("initElements")
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the class that is initialized by this call..
|
||||
*/
|
||||
Class getInitClass() { result = inferClassParameterType(getArgument(1)) }
|
||||
Class getInitClass() { result = inferClassParameterType(this.getArgument(1)) }
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -5,14 +5,14 @@ import semmle.code.java.security.ExternalProcess
|
||||
|
||||
/** The class `org.apache.commons.exec.CommandLine`. */
|
||||
private class TypeCommandLine extends Class {
|
||||
TypeCommandLine() { hasQualifiedName("org.apache.commons.exec", "CommandLine") }
|
||||
TypeCommandLine() { this.hasQualifiedName("org.apache.commons.exec", "CommandLine") }
|
||||
}
|
||||
|
||||
/** The `parse()` method of the class `org.apache.commons.exec.CommandLine`. */
|
||||
private class MethodCommandLineParse extends Method, ExecCallable {
|
||||
MethodCommandLineParse() {
|
||||
getDeclaringType() instanceof TypeCommandLine and
|
||||
hasName("parse")
|
||||
this.getDeclaringType() instanceof TypeCommandLine and
|
||||
this.hasName("parse")
|
||||
}
|
||||
|
||||
override int getAnExecutedArgument() { result = 0 }
|
||||
@@ -21,8 +21,8 @@ private class MethodCommandLineParse extends Method, ExecCallable {
|
||||
/** The `addArguments()` method of the class `org.apache.commons.exec.CommandLine`. */
|
||||
private class MethodCommandLineAddArguments extends Method, ExecCallable {
|
||||
MethodCommandLineAddArguments() {
|
||||
getDeclaringType() instanceof TypeCommandLine and
|
||||
hasName("addArguments")
|
||||
this.getDeclaringType() instanceof TypeCommandLine and
|
||||
this.hasName("addArguments")
|
||||
}
|
||||
|
||||
override int getAnExecutedArgument() { result = 0 }
|
||||
|
||||
@@ -20,19 +20,19 @@ import semmle.code.java.Reflection
|
||||
import semmle.code.java.frameworks.spring.Spring
|
||||
|
||||
library class CamelAnnotation extends Annotation {
|
||||
CamelAnnotation() { getType().getPackage().hasName("org.apache.camel") }
|
||||
CamelAnnotation() { this.getType().getPackage().hasName("org.apache.camel") }
|
||||
}
|
||||
|
||||
/**
|
||||
* An annotation indicating that the annotated method is called by Apache Camel.
|
||||
*/
|
||||
class CamelConsumeAnnotation extends CamelAnnotation {
|
||||
CamelConsumeAnnotation() { getType().hasName("Consume") }
|
||||
CamelConsumeAnnotation() { this.getType().hasName("Consume") }
|
||||
}
|
||||
|
||||
/**
|
||||
* A method that may be called by Apache Camel in response to a message.
|
||||
*/
|
||||
class CamelConsumeMethod extends Method {
|
||||
CamelConsumeMethod() { getAnAnnotation() instanceof CamelConsumeAnnotation }
|
||||
CamelConsumeMethod() { this.getAnAnnotation() instanceof CamelConsumeAnnotation }
|
||||
}
|
||||
|
||||
@@ -23,7 +23,7 @@ import semmle.code.java.frameworks.spring.Spring
|
||||
*/
|
||||
library class ProcessorDefinitionElement extends MethodAccess {
|
||||
ProcessorDefinitionElement() {
|
||||
getMethod()
|
||||
this.getMethod()
|
||||
.getDeclaringType()
|
||||
.getSourceDeclaration()
|
||||
.hasQualifiedName("org.apache.camel.model", "ProcessorDefinition")
|
||||
@@ -36,15 +36,15 @@ library class ProcessorDefinitionElement extends MethodAccess {
|
||||
* This declares a "target" for this route, described by the URI given as the first argument.
|
||||
*/
|
||||
class CamelJavaDslToDecl extends ProcessorDefinitionElement {
|
||||
CamelJavaDslToDecl() { getMethod().hasName("to") }
|
||||
CamelJavaDslToDecl() { this.getMethod().hasName("to") }
|
||||
|
||||
/**
|
||||
* Gets the URI specified by this `to` declaration.
|
||||
*/
|
||||
string getUri() { result = getArgument(0).(CompileTimeConstantExpr).getStringValue() }
|
||||
string getUri() { result = this.getArgument(0).(CompileTimeConstantExpr).getStringValue() }
|
||||
|
||||
/** DEPRECATED: Alias for getUri */
|
||||
deprecated string getURI() { result = getUri() }
|
||||
deprecated string getURI() { result = this.getUri() }
|
||||
}
|
||||
|
||||
/** DEPRECATED: Alias for CamelJavaDslToDecl */
|
||||
@@ -57,20 +57,20 @@ deprecated class CamelJavaDSLToDecl = CamelJavaDslToDecl;
|
||||
* or the bean object itself.
|
||||
*/
|
||||
class CamelJavaDslBeanDecl extends ProcessorDefinitionElement {
|
||||
CamelJavaDslBeanDecl() { getMethod().hasName("bean") }
|
||||
CamelJavaDslBeanDecl() { this.getMethod().hasName("bean") }
|
||||
|
||||
/**
|
||||
* Gets a bean class that may be registered as a target by this `bean()` declaration.
|
||||
*/
|
||||
RefType getABeanClass() {
|
||||
if getArgument(0).getType() instanceof TypeClass
|
||||
if this.getArgument(0).getType() instanceof TypeClass
|
||||
then
|
||||
// In this case, we've been given a Class<?>, which implies a Spring Bean of this type
|
||||
// should be loaded. Infer the type of type parameter.
|
||||
result = inferClassParameterType(getArgument(0))
|
||||
result = inferClassParameterType(this.getArgument(0))
|
||||
else
|
||||
// In this case, the object itself is used as the target for the Apache Camel messages.
|
||||
result = getArgument(0).getType()
|
||||
result = this.getArgument(0).getType()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -85,22 +85,24 @@ deprecated class CamelJavaDSLBeanDecl = CamelJavaDslBeanDecl;
|
||||
* assumption that it either represetns a qualified name, or a Srping bean identifier.
|
||||
*/
|
||||
class CamelJavaDslBeanRefDecl extends ProcessorDefinitionElement {
|
||||
CamelJavaDslBeanRefDecl() { getMethod().hasName("beanRef") }
|
||||
CamelJavaDslBeanRefDecl() { this.getMethod().hasName("beanRef") }
|
||||
|
||||
/**
|
||||
* Gets the string describing the bean referred to.
|
||||
*/
|
||||
string getBeanRefString() { result = getArgument(0).(CompileTimeConstantExpr).getStringValue() }
|
||||
string getBeanRefString() {
|
||||
result = this.getArgument(0).(CompileTimeConstantExpr).getStringValue()
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a class that may be referred to by this bean reference.
|
||||
*/
|
||||
RefType getABeanClass() {
|
||||
exists(SpringBean bean | bean.getBeanIdentifier() = getBeanRefString() |
|
||||
exists(SpringBean bean | bean.getBeanIdentifier() = this.getBeanRefString() |
|
||||
result = bean.getClass()
|
||||
)
|
||||
or
|
||||
result.getQualifiedName() = getBeanRefString()
|
||||
result.getQualifiedName() = this.getBeanRefString()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -114,28 +116,28 @@ deprecated class CamelJavaDSLBeanRefDecl = CamelJavaDslBeanRefDecl;
|
||||
*/
|
||||
class CamelJavaDslMethodDecl extends MethodAccess {
|
||||
CamelJavaDslMethodDecl() {
|
||||
getMethod()
|
||||
this.getMethod()
|
||||
.getDeclaringType()
|
||||
.getSourceDeclaration()
|
||||
.hasQualifiedName("org.apache.camel.builder", "ExpressionClause") and
|
||||
getMethod().hasName("method")
|
||||
this.getMethod().hasName("method")
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a possible bean that this "method" expression represents.
|
||||
*/
|
||||
RefType getABean() {
|
||||
if getArgument(0).getType() instanceof TypeString
|
||||
if this.getArgument(0).getType() instanceof TypeString
|
||||
then
|
||||
exists(SpringBean bean |
|
||||
bean.getBeanIdentifier() = getArgument(0).(CompileTimeConstantExpr).getStringValue()
|
||||
bean.getBeanIdentifier() = this.getArgument(0).(CompileTimeConstantExpr).getStringValue()
|
||||
|
|
||||
result = bean.getClass()
|
||||
)
|
||||
else
|
||||
if getArgument(0).getType() instanceof TypeClass
|
||||
then result = inferClassParameterType(getArgument(0))
|
||||
else result = getArgument(0).getType()
|
||||
if this.getArgument(0).getType() instanceof TypeClass
|
||||
then result = inferClassParameterType(this.getArgument(0))
|
||||
else result = this.getArgument(0).getType()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -36,8 +36,8 @@ predicate isGigaSpacesEventMethod(Method eventMethod) {
|
||||
*/
|
||||
class GigaSpacesSpaceIdGetterMethod extends Method {
|
||||
GigaSpacesSpaceIdGetterMethod() {
|
||||
getAnAnnotation().getType().hasQualifiedName("com.gigaspaces.annotation.pojo", "SpaceId") and
|
||||
getName().matches("get%")
|
||||
this.getAnAnnotation().getType().hasQualifiedName("com.gigaspaces.annotation.pojo", "SpaceId") and
|
||||
this.getName().matches("get%")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -47,10 +47,10 @@ class GigaSpacesSpaceIdGetterMethod extends Method {
|
||||
class GigaSpacesSpaceIdSetterMethod extends Method {
|
||||
GigaSpacesSpaceIdSetterMethod() {
|
||||
exists(GigaSpacesSpaceIdGetterMethod getterMethod |
|
||||
getterMethod.getDeclaringType() = getDeclaringType() and
|
||||
getName().matches("set%")
|
||||
getterMethod.getDeclaringType() = this.getDeclaringType() and
|
||||
this.getName().matches("set%")
|
||||
|
|
||||
getterMethod.getName().suffix(3) = getName().suffix(3)
|
||||
getterMethod.getName().suffix(3) = this.getName().suffix(3)
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -61,7 +61,9 @@ class GigaSpacesSpaceIdSetterMethod extends Method {
|
||||
*/
|
||||
class GigaSpacesSpaceRoutingMethod extends Method {
|
||||
GigaSpacesSpaceRoutingMethod() {
|
||||
getAnAnnotation().getType().hasQualifiedName("com.gigaspaces.annotation.pojo", "SpaceRouting") and
|
||||
getName().matches("get%")
|
||||
this.getAnAnnotation()
|
||||
.getType()
|
||||
.hasQualifiedName("com.gigaspaces.annotation.pojo", "SpaceRouting") and
|
||||
this.getName().matches("get%")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,7 +8,7 @@ import semmle.code.java.frameworks.javaee.jsf.JSFFacesContextXML
|
||||
* A method that is visible to faces, if the instance type is visible to faces.
|
||||
*/
|
||||
library class FacesVisibleMethod extends Method {
|
||||
FacesVisibleMethod() { isPublic() and not isStatic() }
|
||||
FacesVisibleMethod() { this.isPublic() and not this.isStatic() }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -45,7 +45,7 @@ class FacesAccessibleType extends RefType {
|
||||
}
|
||||
|
||||
/** Gets a method declared on this type that is visible to JSF. */
|
||||
FacesVisibleMethod getAnAccessibleMethod() { result = getAMethod() }
|
||||
FacesVisibleMethod getAnAccessibleMethod() { result = this.getAMethod() }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -59,7 +59,7 @@ class FacesAccessibleType extends RefType {
|
||||
class FacesComponent extends Class {
|
||||
FacesComponent() {
|
||||
// Must extend UIComponent for it to be a valid component.
|
||||
getAnAncestor().hasQualifiedName("javax.faces.component", "UIComponent") and
|
||||
this.getAnAncestor().hasQualifiedName("javax.faces.component", "UIComponent") and
|
||||
(
|
||||
// Must be registered using either an annotation
|
||||
exists(FacesComponentAnnotation componentAnnotation |
|
||||
|
||||
@@ -6,12 +6,14 @@ import default
|
||||
* A Java Server Faces `ManagedBean` annotation on a class.
|
||||
*/
|
||||
class FacesManagedBeanAnnotation extends Annotation {
|
||||
FacesManagedBeanAnnotation() { getType().hasQualifiedName("javax.faces.bean", "ManagedBean") }
|
||||
FacesManagedBeanAnnotation() {
|
||||
this.getType().hasQualifiedName("javax.faces.bean", "ManagedBean")
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the `Class` of the managed bean.
|
||||
*/
|
||||
Class getManagedBeanClass() { result = getAnnotatedElement() }
|
||||
Class getManagedBeanClass() { result = this.getAnnotatedElement() }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -21,11 +23,11 @@ class FacesManagedBeanAnnotation extends Annotation {
|
||||
*/
|
||||
class FacesComponentAnnotation extends Annotation {
|
||||
FacesComponentAnnotation() {
|
||||
getType().hasQualifiedName("javax.faces.component", "FacesComponent")
|
||||
this.getType().hasQualifiedName("javax.faces.component", "FacesComponent")
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the `Class` of the FacesComponent, if this annotation is valid.
|
||||
*/
|
||||
Class getFacesComponentClass() { result = getAnnotatedElement() }
|
||||
Class getFacesComponentClass() { result = this.getAnnotatedElement() }
|
||||
}
|
||||
|
||||
@@ -10,7 +10,7 @@ import semmle.code.java.frameworks.spring.SpringBean
|
||||
* An Apache Camel element in a Spring Beans file.
|
||||
*/
|
||||
class SpringCamelXmlElement extends SpringXmlElement {
|
||||
SpringCamelXmlElement() { getNamespace().getUri() = "http://camel.apache.org/schema/spring" }
|
||||
SpringCamelXmlElement() { this.getNamespace().getUri() = "http://camel.apache.org/schema/spring" }
|
||||
}
|
||||
|
||||
/** DEPRECATED: Alias for SpringCamelXmlElement */
|
||||
@@ -22,7 +22,7 @@ deprecated class SpringCamelXMLElement = SpringCamelXmlElement;
|
||||
* All Apache Camel Spring elements are nested within a `<camelContext>` or a `<routeContext>`.
|
||||
*/
|
||||
class SpringCamelXmlContext extends SpringCamelXmlElement {
|
||||
SpringCamelXmlContext() { getName() = "camelContext" }
|
||||
SpringCamelXmlContext() { this.getName() = "camelContext" }
|
||||
}
|
||||
|
||||
/** DEPRECATED: Alias for SpringCamelXmlContext */
|
||||
@@ -35,7 +35,7 @@ deprecated class SpringCamelXMLContext = SpringCamelXmlContext;
|
||||
* `<camelContext>`.
|
||||
*/
|
||||
class SpringCamelXmlRouteContext extends SpringCamelXmlElement {
|
||||
SpringCamelXmlRouteContext() { getName() = "routeContext" }
|
||||
SpringCamelXmlRouteContext() { this.getName() = "routeContext" }
|
||||
}
|
||||
|
||||
/** DEPRECATED: Alias for SpringCamelXmlRouteContext */
|
||||
@@ -51,10 +51,10 @@ class SpringCamelXmlRoute extends SpringCamelXmlElement {
|
||||
SpringCamelXmlRoute() {
|
||||
// A route must either be in a `<routeContext>` or a `<camelContext>`.
|
||||
(
|
||||
getParent() instanceof SpringCamelXmlRouteContext or
|
||||
getParent() instanceof SpringCamelXmlContext
|
||||
this.getParent() instanceof SpringCamelXmlRouteContext or
|
||||
this.getParent() instanceof SpringCamelXmlContext
|
||||
) and
|
||||
getName() = "route"
|
||||
this.getName() = "route"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -66,8 +66,8 @@ deprecated class SpringCamelXMLRoute = SpringCamelXmlRoute;
|
||||
*/
|
||||
class SpringCamelXmlRouteElement extends SpringCamelXmlElement {
|
||||
SpringCamelXmlRouteElement() {
|
||||
getParent() instanceof SpringCamelXmlRoute or
|
||||
getParent() instanceof SpringCamelXmlRouteElement
|
||||
this.getParent() instanceof SpringCamelXmlRoute or
|
||||
this.getParent() instanceof SpringCamelXmlRouteElement
|
||||
}
|
||||
}
|
||||
|
||||
@@ -82,12 +82,12 @@ deprecated class SpringCamelXMLRouteElement = SpringCamelXmlRouteElement;
|
||||
* route.
|
||||
*/
|
||||
class SpringCamelXmlBeanRef extends SpringCamelXmlRouteElement {
|
||||
SpringCamelXmlBeanRef() { getName() = "bean" }
|
||||
SpringCamelXmlBeanRef() { this.getName() = "bean" }
|
||||
|
||||
/**
|
||||
* Gets the Spring bean that is referenced by this route bean definition, if any.
|
||||
*/
|
||||
SpringBean getRefBean() { result.getBeanIdentifier() = getAttribute("ref").getValue() }
|
||||
SpringBean getRefBean() { result.getBeanIdentifier() = this.getAttribute("ref").getValue() }
|
||||
|
||||
/**
|
||||
* Gets the RefType referred to by `beanType` attribute, if any.
|
||||
@@ -95,7 +95,7 @@ class SpringCamelXmlBeanRef extends SpringCamelXmlRouteElement {
|
||||
* This defines the bean that should be created by Apache Camel as a target of this route. In
|
||||
* this case, no pre-existing bean is required.
|
||||
*/
|
||||
RefType getBeanType() { result.getQualifiedName() = getAttribute("beanType").getValue() }
|
||||
RefType getBeanType() { result.getQualifiedName() = this.getAttribute("beanType").getValue() }
|
||||
}
|
||||
|
||||
/** DEPRECATED: Alias for SpringCamelXmlBeanRef */
|
||||
@@ -109,15 +109,15 @@ deprecated class SpringCamelXMLBeanRef = SpringCamelXmlBeanRef;
|
||||
* consists of a bean name and optional method name.
|
||||
*/
|
||||
class SpringCamelXmlToElement extends SpringCamelXmlRouteElement {
|
||||
SpringCamelXmlToElement() { getName() = "to" }
|
||||
SpringCamelXmlToElement() { this.getName() = "to" }
|
||||
|
||||
/**
|
||||
* Gets the URI attribute for this `<to>` element.
|
||||
*/
|
||||
string getUri() { result = getAttribute("uri").getValue() }
|
||||
string getUri() { result = this.getAttribute("uri").getValue() }
|
||||
|
||||
/** DEPRECATED: Alias for getUri */
|
||||
deprecated string getURI() { result = getUri() }
|
||||
deprecated string getURI() { result = this.getUri() }
|
||||
}
|
||||
|
||||
/** DEPRECATED: Alias for SpringCamelXmlToElement */
|
||||
@@ -132,20 +132,20 @@ deprecated class SpringCamelXMLToElement = SpringCamelXmlToElement;
|
||||
* (if "beanType" is used.
|
||||
*/
|
||||
class SpringCamelXmlMethodElement extends SpringCamelXmlElement {
|
||||
SpringCamelXmlMethodElement() { getName() = "method" }
|
||||
SpringCamelXmlMethodElement() { this.getName() = "method" }
|
||||
|
||||
/**
|
||||
* Gets the `SpringBean` that this method expression refers to.
|
||||
*/
|
||||
SpringBean getRefBean() {
|
||||
result.getBeanIdentifier() = getAttribute("ref").getValue() or
|
||||
result.getBeanIdentifier() = getAttribute("bean").getValue()
|
||||
result.getBeanIdentifier() = this.getAttribute("ref").getValue() or
|
||||
result.getBeanIdentifier() = this.getAttribute("bean").getValue()
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the class based on the `beanType` attribute.
|
||||
*/
|
||||
RefType getBeanType() { result.getQualifiedName() = getAttribute("beanType").getValue() }
|
||||
RefType getBeanType() { result.getQualifiedName() = this.getAttribute("beanType").getValue() }
|
||||
}
|
||||
|
||||
/** DEPRECATED: Alias for SpringCamelXmlMethodElement */
|
||||
|
||||
@@ -8,14 +8,14 @@ import java
|
||||
*/
|
||||
class InitializingBeanClass extends Class {
|
||||
InitializingBeanClass() {
|
||||
getAnAncestor().hasQualifiedName("org.springframework.beans.factory", "InitializingBean")
|
||||
this.getAnAncestor().hasQualifiedName("org.springframework.beans.factory", "InitializingBean")
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the `afterPropertiesSet()` method, which is called after the bean has been initialized.
|
||||
*/
|
||||
Method getAfterPropertiesSet() {
|
||||
inherits(result) and
|
||||
this.inherits(result) and
|
||||
result.hasName("afterPropertiesSet")
|
||||
}
|
||||
}
|
||||
|
||||
237
java/ql/lib/semmle/code/java/security/ArithmeticCommon.qll
Normal file
237
java/ql/lib/semmle/code/java/security/ArithmeticCommon.qll
Normal file
@@ -0,0 +1,237 @@
|
||||
/** Provides guards and predicates to reason about arithmetic. */
|
||||
|
||||
import semmle.code.java.arithmetic.Overflow
|
||||
import semmle.code.java.controlflow.Guards
|
||||
private import semmle.code.java.dataflow.SSA
|
||||
private import semmle.code.java.dataflow.DataFlow
|
||||
private import semmle.code.java.dataflow.RangeAnalysis
|
||||
private import semmle.code.java.dataflow.RangeUtils
|
||||
private import semmle.code.java.dataflow.SignAnalysis
|
||||
private import semmle.code.java.controlflow.internal.GuardsLogic
|
||||
|
||||
/**
|
||||
* Holds if the type of `exp` is narrower than or equal to `numType`,
|
||||
* or there is an enclosing cast to a type at least as narrow as 'numType'.
|
||||
*/
|
||||
predicate narrowerThanOrEqualTo(ArithExpr exp, NumType numType) {
|
||||
exp.getType().(NumType).widerThan(numType)
|
||||
implies
|
||||
exists(CastingExpr cast | cast.getAChildExpr() = exp | numType.widerThanOrEqualTo(cast.getType()))
|
||||
}
|
||||
|
||||
private Guard sizeGuard(SsaVariable v, boolean branch, boolean upper) {
|
||||
exists(ComparisonExpr comp | comp = result |
|
||||
comp.getLesserOperand() = ssaRead(v, 0) and
|
||||
(
|
||||
branch = true and upper = true
|
||||
or
|
||||
branch = false and upper = false
|
||||
)
|
||||
or
|
||||
comp.getGreaterOperand() = ssaRead(v, 0) and
|
||||
(
|
||||
branch = true and upper = false
|
||||
or
|
||||
branch = false and upper = true
|
||||
)
|
||||
or
|
||||
exists(MethodAccess ma |
|
||||
ma.getMethod() instanceof MethodAbs and
|
||||
ma.getArgument(0) = ssaRead(v, 0) and
|
||||
(
|
||||
comp.getLesserOperand() = ma and branch = true
|
||||
or
|
||||
comp.getGreaterOperand() = ma and branch = false
|
||||
) and
|
||||
(upper = false or upper = true)
|
||||
)
|
||||
or
|
||||
// overflow test
|
||||
exists(AddExpr add, RValue use, Expr pos |
|
||||
use = ssaRead(v, 0) and
|
||||
add.hasOperands(use, pos) and
|
||||
positive(use) and
|
||||
positive(pos) and
|
||||
upper = true
|
||||
|
|
||||
comp.getLesserOperand() = add and
|
||||
comp.getGreaterOperand().(IntegerLiteral).getIntValue() = 0 and
|
||||
branch = false
|
||||
or
|
||||
comp.getGreaterOperand() = add and
|
||||
comp.getLesserOperand().(IntegerLiteral).getIntValue() = 0 and
|
||||
branch = true
|
||||
)
|
||||
)
|
||||
or
|
||||
result.isEquality(ssaRead(v, 0), _, branch) and
|
||||
(upper = true or upper = false)
|
||||
or
|
||||
exists(MethodAccess call, Method m, int ix |
|
||||
call = result and
|
||||
call.getArgument(ix) = ssaRead(v, 0) and
|
||||
call.getMethod().getSourceDeclaration() = m and
|
||||
m = customSizeGuard(ix, branch, upper)
|
||||
)
|
||||
}
|
||||
|
||||
private Guard derivedSizeGuard(SsaVariable v, boolean branch, boolean upper) {
|
||||
result = sizeGuard(v, branch, upper) or
|
||||
exists(boolean branch0 | implies_v3(result, branch, derivedSizeGuard(v, branch0, upper), branch0))
|
||||
}
|
||||
|
||||
private Method customSizeGuard(int index, boolean retval, boolean upper) {
|
||||
exists(Parameter p, SsaImplicitInit v |
|
||||
result.getReturnType().(PrimitiveType).hasName("boolean") and
|
||||
not result.isOverridable() and
|
||||
p.getCallable() = result and
|
||||
not p.isVarargs() and
|
||||
p.getType() instanceof NumericOrCharType and
|
||||
p.getPosition() = index and
|
||||
v.isParameterDefinition(p) and
|
||||
forex(ReturnStmt ret |
|
||||
ret.getEnclosingCallable() = result and
|
||||
exists(Expr res | res = ret.getResult() |
|
||||
not res.(BooleanLiteral).getBooleanValue() = retval.booleanNot()
|
||||
)
|
||||
|
|
||||
ret.getResult() = derivedSizeGuard(v, retval, upper)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `e` is bounded in a way that is likely to prevent overflow.
|
||||
*/
|
||||
predicate guardedLessThanSomething(Expr e) {
|
||||
exists(SsaVariable v, Guard guard, boolean branch |
|
||||
e = v.getAUse() and
|
||||
guard = sizeGuard(v.getAPhiInputOrPriorDef*(), branch, true) and
|
||||
guard.controls(e.getBasicBlock(), branch)
|
||||
)
|
||||
or
|
||||
negative(e)
|
||||
or
|
||||
e.(MethodAccess).getMethod() instanceof MethodMathMin
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `e` is bounded in a way that is likely to prevent underflow.
|
||||
*/
|
||||
predicate guardedGreaterThanSomething(Expr e) {
|
||||
exists(SsaVariable v, Guard guard, boolean branch |
|
||||
e = v.getAUse() and
|
||||
guard = sizeGuard(v.getAPhiInputOrPriorDef*(), branch, false) and
|
||||
guard.controls(e.getBasicBlock(), branch)
|
||||
)
|
||||
or
|
||||
positive(e)
|
||||
or
|
||||
e.(MethodAccess).getMethod() instanceof MethodMathMax
|
||||
}
|
||||
|
||||
/** Holds if `e` occurs in a context where it will be upcast to a wider type. */
|
||||
predicate upcastToWiderType(Expr e) {
|
||||
exists(NumType t1, NumType t2 |
|
||||
t1 = e.getType() and
|
||||
(
|
||||
t2.widerThan(t1)
|
||||
or
|
||||
t1 instanceof CharacterType and t2.getWidthRank() >= 3
|
||||
)
|
||||
|
|
||||
exists(Variable v | v.getAnAssignedValue() = e and t2 = v.getType())
|
||||
or
|
||||
exists(CastingExpr c | c.getExpr() = e and t2 = c.getType())
|
||||
or
|
||||
exists(ReturnStmt ret | ret.getResult() = e and t2 = ret.getEnclosingCallable().getReturnType())
|
||||
or
|
||||
exists(Parameter p | p.getAnArgument() = e and t2 = p.getType())
|
||||
or
|
||||
exists(ConditionalExpr cond | cond.getABranchExpr() = e | t2 = cond.getType())
|
||||
)
|
||||
}
|
||||
|
||||
/** Holds if the result of `exp` has certain bits filtered by a bitwise and. */
|
||||
private predicate inBitwiseAnd(Expr exp) {
|
||||
exists(AndBitwiseExpr a | a.getAnOperand() = exp) or
|
||||
inBitwiseAnd(exp.(LeftShiftExpr).getAnOperand()) or
|
||||
inBitwiseAnd(exp.(RightShiftExpr).getAnOperand()) or
|
||||
inBitwiseAnd(exp.(UnsignedRightShiftExpr).getAnOperand())
|
||||
}
|
||||
|
||||
/** Holds if overflow/underflow is irrelevant for this expression. */
|
||||
predicate overflowIrrelevant(Expr exp) {
|
||||
inBitwiseAnd(exp) or
|
||||
exp.getEnclosingCallable() instanceof HashCodeMethod
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `n` is unlikely to be part in a path from some source containing
|
||||
* numeric data to some arithmetic expression that may overflow/underflow.
|
||||
*/
|
||||
private predicate unlikelyNode(DataFlow::Node n) {
|
||||
n.getTypeBound() instanceof TypeObject and
|
||||
not exists(CastingExpr cast |
|
||||
DataFlow::localFlow(n, DataFlow::exprNode(cast.getExpr())) and
|
||||
cast.getType() instanceof NumericOrCharType
|
||||
)
|
||||
}
|
||||
|
||||
/** Holds if `n` is likely guarded against overflow. */
|
||||
predicate overflowBarrier(DataFlow::Node n) {
|
||||
n.getType() instanceof BooleanType or
|
||||
guardedLessThanSomething(n.asExpr()) or
|
||||
unlikelyNode(n) or
|
||||
upcastToWiderType(n.asExpr()) or
|
||||
overflowIrrelevant(n.asExpr())
|
||||
}
|
||||
|
||||
/** Holds if `n` is likely guarded against underflow. */
|
||||
predicate underflowBarrier(DataFlow::Node n) {
|
||||
n.getType() instanceof BooleanType or
|
||||
guardedGreaterThanSomething(n.asExpr()) or
|
||||
unlikelyNode(n) or
|
||||
upcastToWiderType(n.asExpr()) or
|
||||
overflowIrrelevant(n.asExpr())
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `use` is an operand of `exp` that acts as a sink for
|
||||
* overflow-related dataflow.
|
||||
*/
|
||||
predicate overflowSink(ArithExpr exp, VarAccess use) {
|
||||
exp.getAnOperand() = use and
|
||||
(
|
||||
// overflow unlikely for subtraction and division
|
||||
exp instanceof AddExpr or
|
||||
exp instanceof PreIncExpr or
|
||||
exp instanceof PostIncExpr or
|
||||
exp instanceof MulExpr
|
||||
) and
|
||||
not guardedLessThanSomething(use) and
|
||||
// Exclude widening conversions of tainted values due to binary numeric promotion (JLS 5.6.2)
|
||||
// unless there is an enclosing cast down to a narrower type.
|
||||
narrowerThanOrEqualTo(exp, use.getType()) and
|
||||
not overflowIrrelevant(exp)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `use` is an operand of `exp` that acts as a sink for
|
||||
* underflow-related dataflow.
|
||||
*/
|
||||
predicate underflowSink(ArithExpr exp, VarAccess use) {
|
||||
exp.getAnOperand() = use and
|
||||
(
|
||||
// underflow unlikely for addition and division
|
||||
exp.(SubExpr).getLeftOperand() = use or
|
||||
exp instanceof PreDecExpr or
|
||||
exp instanceof PostDecExpr or
|
||||
exp instanceof MulExpr
|
||||
) and
|
||||
not guardedGreaterThanSomething(use) and
|
||||
// Exclude widening conversions of tainted values due to binary numeric promotion (JLS 5.6.2)
|
||||
// unless there is an enclosing cast down to a narrower type.
|
||||
narrowerThanOrEqualTo(exp, use.getType()) and
|
||||
not overflowIrrelevant(exp)
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
/** Provides taint-tracking configurations to reason about arithmetic using local-user-controlled data. */
|
||||
|
||||
import java
|
||||
private import semmle.code.java.dataflow.FlowSources
|
||||
private import semmle.code.java.security.ArithmeticCommon
|
||||
|
||||
/**
|
||||
* A taint-tracking configuration to reason about arithmetic overflow using local-user-controlled data.
|
||||
*/
|
||||
module ArithmeticTaintedLocalOverflowConfig implements DataFlow::ConfigSig {
|
||||
predicate isSource(DataFlow::Node source) { source instanceof LocalUserInput }
|
||||
|
||||
predicate isSink(DataFlow::Node sink) { overflowSink(_, sink.asExpr()) }
|
||||
|
||||
predicate isBarrier(DataFlow::Node n) { overflowBarrier(n) }
|
||||
}
|
||||
|
||||
/**
|
||||
* Taint-tracking flow for arithmetic overflow using local-user-controlled data.
|
||||
*/
|
||||
module ArithmeticTaintedLocalOverflowFlow =
|
||||
TaintTracking::Global<ArithmeticTaintedLocalOverflowConfig>;
|
||||
|
||||
/**
|
||||
* A taint-tracking configuration to reason about arithmetic underflow using local-user-controlled data.
|
||||
*/
|
||||
module ArithmeticTaintedLocalUnderflowConfig implements DataFlow::ConfigSig {
|
||||
predicate isSource(DataFlow::Node source) { source instanceof LocalUserInput }
|
||||
|
||||
predicate isSink(DataFlow::Node sink) { underflowSink(_, sink.asExpr()) }
|
||||
|
||||
predicate isBarrier(DataFlow::Node n) { underflowBarrier(n) }
|
||||
}
|
||||
|
||||
/**
|
||||
* Taint-tracking flow for arithmetic underflow using local-user-controlled data.
|
||||
*/
|
||||
module ArithmeticTaintedLocalUnderflowFlow =
|
||||
TaintTracking::Global<ArithmeticTaintedLocalUnderflowConfig>;
|
||||
@@ -0,0 +1,29 @@
|
||||
/** Provides taint-tracking configurations to reason about arithmetic with unvalidated user input. */
|
||||
|
||||
import java
|
||||
private import semmle.code.java.dataflow.FlowSources
|
||||
private import semmle.code.java.security.ArithmeticCommon
|
||||
|
||||
/** A taint-tracking configuration to reason about overflow from unvalidated user input. */
|
||||
module RemoteUserInputOverflowConfig implements DataFlow::ConfigSig {
|
||||
predicate isSource(DataFlow::Node source) { source instanceof RemoteFlowSource }
|
||||
|
||||
predicate isSink(DataFlow::Node sink) { overflowSink(_, sink.asExpr()) }
|
||||
|
||||
predicate isBarrier(DataFlow::Node n) { overflowBarrier(n) }
|
||||
}
|
||||
|
||||
/** A taint-tracking configuration to reason about underflow from unvalidated user input. */
|
||||
module RemoteUserInputUnderflowConfig implements DataFlow::ConfigSig {
|
||||
predicate isSource(DataFlow::Node source) { source instanceof RemoteFlowSource }
|
||||
|
||||
predicate isSink(DataFlow::Node sink) { underflowSink(_, sink.asExpr()) }
|
||||
|
||||
predicate isBarrier(DataFlow::Node n) { underflowBarrier(n) }
|
||||
}
|
||||
|
||||
/** Taint-tracking flow for overflow from unvalidated user input. */
|
||||
module RemoteUserInputOverflow = TaintTracking::Global<RemoteUserInputOverflowConfig>;
|
||||
|
||||
/** Taint-tracking flow for underflow from unvalidated user input. */
|
||||
module RemoteUserInputUnderflow = TaintTracking::Global<RemoteUserInputUnderflowConfig>;
|
||||
@@ -0,0 +1,39 @@
|
||||
/** Provides taint-tracking configuration to reason about arithmetic with uncontrolled values. */
|
||||
|
||||
import java
|
||||
private import semmle.code.java.dataflow.TaintTracking
|
||||
private import semmle.code.java.security.RandomQuery
|
||||
private import semmle.code.java.security.SecurityTests
|
||||
private import semmle.code.java.security.ArithmeticCommon
|
||||
|
||||
private class TaintSource extends DataFlow::ExprNode {
|
||||
TaintSource() {
|
||||
exists(RandomDataSource m | not m.resultMayBeBounded() | m.getOutput() = this.getExpr())
|
||||
}
|
||||
}
|
||||
|
||||
/** A taint-tracking configuration to reason about overflow from arithmetic with uncontrolled values. */
|
||||
module ArithmeticUncontrolledOverflowConfig implements DataFlow::ConfigSig {
|
||||
predicate isSource(DataFlow::Node source) { source instanceof TaintSource }
|
||||
|
||||
predicate isSink(DataFlow::Node sink) { overflowSink(_, sink.asExpr()) }
|
||||
|
||||
predicate isBarrier(DataFlow::Node n) { overflowBarrier(n) }
|
||||
}
|
||||
|
||||
/** Taint-tracking flow to reason about overflow from arithmetic with uncontrolled values. */
|
||||
module ArithmeticUncontrolledOverflowFlow =
|
||||
TaintTracking::Global<ArithmeticUncontrolledOverflowConfig>;
|
||||
|
||||
/** A taint-tracking configuration to reason about underflow from arithmetic with uncontrolled values. */
|
||||
module ArithmeticUncontrolledUnderflowConfig implements DataFlow::ConfigSig {
|
||||
predicate isSource(DataFlow::Node source) { source instanceof TaintSource }
|
||||
|
||||
predicate isSink(DataFlow::Node sink) { underflowSink(_, sink.asExpr()) }
|
||||
|
||||
predicate isBarrier(DataFlow::Node n) { underflowBarrier(n) }
|
||||
}
|
||||
|
||||
/** Taint-tracking flow to reason about underflow from arithmetic with uncontrolled values. */
|
||||
module ArithmeticUncontrolledUnderflowFlow =
|
||||
TaintTracking::Global<ArithmeticUncontrolledUnderflowConfig>;
|
||||
@@ -0,0 +1,61 @@
|
||||
/** Provides predicates and classes for reasoning about arithmetic with extreme values. */
|
||||
|
||||
import java
|
||||
private import semmle.code.java.dataflow.DataFlow
|
||||
private import semmle.code.java.security.ArithmeticCommon
|
||||
|
||||
/**
|
||||
* A field representing an extreme value.
|
||||
*
|
||||
* For example, `Integer.MAX_VALUE` or `Long.MIN_VALUE`.
|
||||
*/
|
||||
abstract class ExtremeValueField extends Field {
|
||||
ExtremeValueField() { this.getType() instanceof IntegralType }
|
||||
}
|
||||
|
||||
/** A field representing the minimum value of a primitive type. */
|
||||
private class MinValueField extends ExtremeValueField {
|
||||
MinValueField() { this.getName() = "MIN_VALUE" }
|
||||
}
|
||||
|
||||
/** A field representing the maximum value of a primitive type. */
|
||||
private class MaxValueField extends ExtremeValueField {
|
||||
MaxValueField() { this.getName() = "MAX_VALUE" }
|
||||
}
|
||||
|
||||
/** A variable access that refers to an extreme value. */
|
||||
class ExtremeSource extends VarAccess {
|
||||
ExtremeSource() { this.getVariable() instanceof ExtremeValueField }
|
||||
}
|
||||
|
||||
/** A dataflow configuration which tracks flow from maximum values to an overflow. */
|
||||
module MaxValueFlowConfig implements DataFlow::ConfigSig {
|
||||
predicate isSource(DataFlow::Node source) {
|
||||
source.asExpr().(ExtremeSource).getVariable() instanceof MaxValueField
|
||||
}
|
||||
|
||||
predicate isSink(DataFlow::Node sink) { overflowSink(_, sink.asExpr()) }
|
||||
|
||||
predicate isBarrierIn(DataFlow::Node n) { isSource(n) }
|
||||
|
||||
predicate isBarrier(DataFlow::Node n) { overflowBarrier(n) }
|
||||
}
|
||||
|
||||
/** Dataflow from maximum values to an underflow. */
|
||||
module MaxValueFlow = DataFlow::Global<MaxValueFlowConfig>;
|
||||
|
||||
/** A dataflow configuration which tracks flow from minimum values to an underflow. */
|
||||
module MinValueFlowConfig implements DataFlow::ConfigSig {
|
||||
predicate isSource(DataFlow::Node source) {
|
||||
source.asExpr().(ExtremeSource).getVariable() instanceof MinValueField
|
||||
}
|
||||
|
||||
predicate isSink(DataFlow::Node sink) { underflowSink(_, sink.asExpr()) }
|
||||
|
||||
predicate isBarrierIn(DataFlow::Node n) { isSource(n) }
|
||||
|
||||
predicate isBarrier(DataFlow::Node n) { underflowBarrier(n) }
|
||||
}
|
||||
|
||||
/** Dataflow from minimum values to an underflow. */
|
||||
module MinValueFlow = DataFlow::Global<MinValueFlowConfig>;
|
||||
@@ -0,0 +1,38 @@
|
||||
/** Provides to taint-tracking configuration to reason about the use of broken or risky cryptographic algorithms. */
|
||||
|
||||
import java
|
||||
private import semmle.code.java.security.Encryption
|
||||
private import semmle.code.java.dataflow.TaintTracking
|
||||
|
||||
private class ShortStringLiteral extends StringLiteral {
|
||||
ShortStringLiteral() { this.getValue().length() < 100 }
|
||||
}
|
||||
|
||||
/**
|
||||
* A string literal that may refer to a broken or risky cryptographic algorithm.
|
||||
*/
|
||||
class BrokenAlgoLiteral extends ShortStringLiteral {
|
||||
BrokenAlgoLiteral() {
|
||||
this.getValue().regexpMatch(getInsecureAlgorithmRegex()) and
|
||||
// Exclude German and French sentences.
|
||||
not this.getValue().regexpMatch(".*\\p{IsLowercase} des \\p{IsLetter}.*")
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A taint-tracking configuration to reason about the use of broken or risky cryptographic algorithms.
|
||||
*/
|
||||
module InsecureCryptoConfig implements DataFlow::ConfigSig {
|
||||
predicate isSource(DataFlow::Node n) { n.asExpr() instanceof BrokenAlgoLiteral }
|
||||
|
||||
predicate isSink(DataFlow::Node n) { exists(CryptoAlgoSpec c | n.asExpr() = c.getAlgoSpec()) }
|
||||
|
||||
predicate isBarrier(DataFlow::Node node) {
|
||||
node.getType() instanceof PrimitiveType or node.getType() instanceof BoxedType
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Taint-tracking flow for use of broken or risky cryptographic algorithms.
|
||||
*/
|
||||
module InsecureCryptoFlow = TaintTracking::Global<InsecureCryptoConfig>;
|
||||
@@ -155,7 +155,7 @@ private class CommandArgArrayImmutableFirst extends CommandArgumentArray {
|
||||
Expr getFirstElement() {
|
||||
result = this.getAWrite(0)
|
||||
or
|
||||
not exists(getAWrite(0)) and
|
||||
not exists(this.getAWrite(0)) and
|
||||
result = firstElementOf(this.getDefiningExpr())
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,26 @@
|
||||
/** Provides a taint-tracking configuration to reason about use of externally controlled strings for command injection vulnerabilities. */
|
||||
|
||||
import java
|
||||
private import semmle.code.java.dataflow.FlowSources
|
||||
private import semmle.code.java.security.ExternalProcess
|
||||
private import semmle.code.java.security.CommandArguments
|
||||
|
||||
/** A taint-tracking configuration to reason about use of externally controlled strings to make command line commands. */
|
||||
module ExecTaintedLocalConfig implements DataFlow::ConfigSig {
|
||||
predicate isSource(DataFlow::Node src) { src instanceof LocalUserInput }
|
||||
|
||||
predicate isSink(DataFlow::Node sink) { sink.asExpr() instanceof ArgumentToExec }
|
||||
|
||||
predicate isBarrier(DataFlow::Node node) {
|
||||
node.getType() instanceof PrimitiveType
|
||||
or
|
||||
node.getType() instanceof BoxedType
|
||||
or
|
||||
isSafeCommandArgument(node.asExpr())
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Taint-tracking flow for use of externally controlled strings to make command line commands.
|
||||
*/
|
||||
module ExecTaintedLocalFlow = TaintTracking::Global<ExecTaintedLocalConfig>;
|
||||
@@ -0,0 +1,20 @@
|
||||
/** Provides a taint-tracking configuration to reason about externally-controlled format strings from local sources. */
|
||||
|
||||
import java
|
||||
private import semmle.code.java.dataflow.FlowSources
|
||||
private import semmle.code.java.StringFormat
|
||||
|
||||
/** A taint-tracking configuration to reason about externally-controlled format strings from local sources. */
|
||||
module ExternallyControlledFormatStringLocalConfig implements DataFlow::ConfigSig {
|
||||
predicate isSource(DataFlow::Node source) { source instanceof LocalUserInput }
|
||||
|
||||
predicate isSink(DataFlow::Node sink) {
|
||||
sink.asExpr() = any(StringFormat formatCall).getFormatArgument()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Taint-tracking flow for externally-controlled format strings from local sources.
|
||||
*/
|
||||
module ExternallyControlledFormatStringLocalFlow =
|
||||
TaintTracking::Global<ExternallyControlledFormatStringLocalConfig>;
|
||||
@@ -5,8 +5,8 @@ import java
|
||||
*/
|
||||
class SetWritable extends Method {
|
||||
SetWritable() {
|
||||
getDeclaringType() instanceof TypeFile and
|
||||
hasName("setWritable")
|
||||
this.getDeclaringType() instanceof TypeFile and
|
||||
this.hasName("setWritable")
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -12,8 +12,10 @@ import SensitiveApi
|
||||
*/
|
||||
private class HardcodedByteArray extends ArrayCreationExpr {
|
||||
HardcodedByteArray() {
|
||||
getType().(Array).getElementType().(PrimitiveType).getName() = "byte" and
|
||||
forex(Expr elem | elem = getInit().getAChildExpr() | elem instanceof CompileTimeConstantExpr)
|
||||
this.getType().(Array).getElementType().(PrimitiveType).getName() = "byte" and
|
||||
forex(Expr elem | elem = this.getInit().getAChildExpr() |
|
||||
elem instanceof CompileTimeConstantExpr
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -24,8 +26,10 @@ private class HardcodedByteArray extends ArrayCreationExpr {
|
||||
*/
|
||||
private class HardcodedCharArray extends ArrayCreationExpr {
|
||||
HardcodedCharArray() {
|
||||
getType().(Array).getElementType().(PrimitiveType).getName() = "char" and
|
||||
forex(Expr elem | elem = getInit().getAChildExpr() | elem instanceof CompileTimeConstantExpr)
|
||||
this.getType().(Array).getElementType().(PrimitiveType).getName() = "char" and
|
||||
forex(Expr elem | elem = this.getInit().getAChildExpr() |
|
||||
elem instanceof CompileTimeConstantExpr
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -72,7 +76,7 @@ class CredentialsApiSink extends CredentialsSink {
|
||||
*/
|
||||
class PasswordVariable extends Variable {
|
||||
PasswordVariable() {
|
||||
getName().regexpMatch("(?i)(encrypted|old|new)?pass(wd|word|code|phrase)(chars|value)?")
|
||||
this.getName().regexpMatch("(?i)(encrypted|old|new)?pass(wd|word|code|phrase)(chars|value)?")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -80,7 +84,7 @@ class PasswordVariable extends Variable {
|
||||
* A variable whose name indicates that it may hold a user name.
|
||||
*/
|
||||
class UsernameVariable extends Variable {
|
||||
UsernameVariable() { getName().regexpMatch("(?i)(user|username)") }
|
||||
UsernameVariable() { this.getName().regexpMatch("(?i)(user|username)") }
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -9,7 +9,7 @@ import HardcodedCredentials
|
||||
* A call to a method that is or overrides `java.lang.Object.equals`.
|
||||
*/
|
||||
class EqualsAccess extends MethodAccess {
|
||||
EqualsAccess() { getMethod() instanceof EqualsMethod }
|
||||
EqualsAccess() { this.getMethod() instanceof EqualsMethod }
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -0,0 +1,25 @@
|
||||
/** Provides a dataflow configuration to reason about improper validation of code-specified size used for array construction. */
|
||||
|
||||
import java
|
||||
private import semmle.code.java.security.internal.ArraySizing
|
||||
private import semmle.code.java.dataflow.TaintTracking
|
||||
|
||||
/**
|
||||
* A dataflow configuration to reason about improper validation of code-specified size used for array construction.
|
||||
*/
|
||||
module BoundedFlowSourceConfig implements DataFlow::ConfigSig {
|
||||
predicate isSource(DataFlow::Node source) {
|
||||
source instanceof BoundedFlowSource and
|
||||
// There is not a fixed lower bound which is greater than zero.
|
||||
not source.(BoundedFlowSource).lowerBound() > 0
|
||||
}
|
||||
|
||||
predicate isSink(DataFlow::Node sink) {
|
||||
any(CheckableArrayAccess caa).canThrowOutOfBoundsDueToEmptyArray(sink.asExpr(), _)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Dataflow flow for improper validation of code-specified size used for array construction.
|
||||
*/
|
||||
module BoundedFlowSourceFlow = DataFlow::Global<BoundedFlowSourceConfig>;
|
||||
@@ -0,0 +1,22 @@
|
||||
/** Provides a taint-tracking configuration to reason about improper validation of local user-provided size used for array construction. */
|
||||
|
||||
import java
|
||||
private import semmle.code.java.security.internal.ArraySizing
|
||||
private import semmle.code.java.dataflow.FlowSources
|
||||
|
||||
/**
|
||||
* A taint-tracking configuration to reason about improper validation of local user-provided size used for array construction.
|
||||
*/
|
||||
module ImproperValidationOfArrayConstructionLocalConfig implements DataFlow::ConfigSig {
|
||||
predicate isSource(DataFlow::Node source) { source instanceof LocalUserInput }
|
||||
|
||||
predicate isSink(DataFlow::Node sink) {
|
||||
any(CheckableArrayAccess caa).canThrowOutOfBoundsDueToEmptyArray(sink.asExpr(), _)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Taint-tracking flow for improper validation of local user-provided size used for array construction.
|
||||
*/
|
||||
module ImproperValidationOfArrayConstructionLocalFlow =
|
||||
TaintTracking::Global<ImproperValidationOfArrayConstructionLocalConfig>;
|
||||
@@ -0,0 +1,22 @@
|
||||
/** Provides a taint-tracking configuration to reason about improper validation of user-provided size used for array construction. */
|
||||
|
||||
import java
|
||||
private import semmle.code.java.security.internal.ArraySizing
|
||||
private import semmle.code.java.dataflow.FlowSources
|
||||
|
||||
/**
|
||||
* A taint-tracking configuration to reason about improper validation of user-provided size used for array construction.
|
||||
*/
|
||||
module ImproperValidationOfArrayConstructionConfig implements DataFlow::ConfigSig {
|
||||
predicate isSource(DataFlow::Node source) { source instanceof RemoteFlowSource }
|
||||
|
||||
predicate isSink(DataFlow::Node sink) {
|
||||
any(CheckableArrayAccess caa).canThrowOutOfBoundsDueToEmptyArray(sink.asExpr(), _)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Taint-tracking flow for improper validation of user-provided size used for array construction.
|
||||
*/
|
||||
module ImproperValidationOfArrayConstructionFlow =
|
||||
TaintTracking::Global<ImproperValidationOfArrayConstructionConfig>;
|
||||
@@ -0,0 +1,22 @@
|
||||
/** Provides a dataflow configuration to reason about improper validation of code-specified array index. */
|
||||
|
||||
import java
|
||||
private import semmle.code.java.security.internal.ArraySizing
|
||||
private import semmle.code.java.security.internal.BoundingChecks
|
||||
private import semmle.code.java.dataflow.DataFlow
|
||||
|
||||
/**
|
||||
* A dataflow configuration to reason about improper validation of code-specified array index.
|
||||
*/
|
||||
module BoundedFlowSourceConfig implements DataFlow::ConfigSig {
|
||||
predicate isSource(DataFlow::Node source) { source instanceof BoundedFlowSource }
|
||||
|
||||
predicate isSink(DataFlow::Node sink) {
|
||||
exists(CheckableArrayAccess arrayAccess | arrayAccess.canThrowOutOfBounds(sink.asExpr()))
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Dataflow flow for improper validation of code-specified array index.
|
||||
*/
|
||||
module BoundedFlowSourceFlow = DataFlow::Global<BoundedFlowSourceConfig>;
|
||||
@@ -0,0 +1,22 @@
|
||||
/** Provides a taint-tracking configuration to reason about improper validation of local user-provided array index. */
|
||||
|
||||
import java
|
||||
private import semmle.code.java.security.internal.ArraySizing
|
||||
private import semmle.code.java.dataflow.FlowSources
|
||||
|
||||
/**
|
||||
* A taint-tracking configuration to reason about improper validation of local user-provided array index.
|
||||
*/
|
||||
module ImproperValidationOfArrayIndexLocalConfig implements DataFlow::ConfigSig {
|
||||
predicate isSource(DataFlow::Node source) { source instanceof LocalUserInput }
|
||||
|
||||
predicate isSink(DataFlow::Node sink) {
|
||||
any(CheckableArrayAccess caa).canThrowOutOfBounds(sink.asExpr())
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Taint-tracking flow for improper validation of local user-provided array index.
|
||||
*/
|
||||
module ImproperValidationOfArrayIndexLocalFlow =
|
||||
TaintTracking::Global<ImproperValidationOfArrayIndexLocalConfig>;
|
||||
@@ -0,0 +1,24 @@
|
||||
/** Provides a taint-tracking configuration to reason about improper validation of user-provided array index. */
|
||||
|
||||
import java
|
||||
private import semmle.code.java.security.internal.ArraySizing
|
||||
private import semmle.code.java.dataflow.FlowSources
|
||||
|
||||
/**
|
||||
* A taint-tracking configuration to reason about improper validation of user-provided array index.
|
||||
*/
|
||||
module ImproperValidationOfArrayIndexConfig implements DataFlow::ConfigSig {
|
||||
predicate isSource(DataFlow::Node source) { source instanceof RemoteFlowSource }
|
||||
|
||||
predicate isSink(DataFlow::Node sink) {
|
||||
any(CheckableArrayAccess caa).canThrowOutOfBounds(sink.asExpr())
|
||||
}
|
||||
|
||||
predicate isBarrier(DataFlow::Node node) { node.getType() instanceof BooleanType }
|
||||
}
|
||||
|
||||
/**
|
||||
* Taint-tracking flow for improper validation of user-provided array index.
|
||||
*/
|
||||
module ImproperValidationOfArrayIndexFlow =
|
||||
TaintTracking::Global<ImproperValidationOfArrayIndexConfig>;
|
||||
@@ -0,0 +1,41 @@
|
||||
/** Provides a dataflow configuration to reason about the failure to use secure cookies. */
|
||||
|
||||
import java
|
||||
private import semmle.code.java.dataflow.DataFlow
|
||||
private import semmle.code.java.frameworks.Servlets
|
||||
|
||||
private predicate isSafeSecureCookieSetting(Expr e) {
|
||||
e.(CompileTimeConstantExpr).getBooleanValue() = true
|
||||
or
|
||||
exists(Method isSecure |
|
||||
isSecure.hasName("isSecure") and
|
||||
isSecure.getDeclaringType().getASourceSupertype*() instanceof ServletRequest
|
||||
|
|
||||
e.(MethodAccess).getMethod() = isSecure
|
||||
)
|
||||
}
|
||||
|
||||
/** A dataflow configuration to reason about the failure to use secure cookies. */
|
||||
module SecureCookieConfig implements DataFlow::ConfigSig {
|
||||
predicate isSource(DataFlow::Node source) {
|
||||
exists(MethodAccess ma, Method m | ma.getMethod() = m |
|
||||
m.getDeclaringType() instanceof TypeCookie and
|
||||
m.getName() = "setSecure" and
|
||||
source.asExpr() = ma.getQualifier() and
|
||||
forex(DataFlow::Node argSource |
|
||||
DataFlow::localFlow(argSource, DataFlow::exprNode(ma.getArgument(0))) and
|
||||
not DataFlow::localFlowStep(_, argSource)
|
||||
|
|
||||
isSafeSecureCookieSetting(argSource.asExpr())
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
predicate isSink(DataFlow::Node sink) {
|
||||
sink.asExpr() =
|
||||
any(MethodAccess add | add.getMethod() instanceof ResponseAddCookieMethod).getArgument(0)
|
||||
}
|
||||
}
|
||||
|
||||
/** Data flow to reason about the failure to use secure cookies. */
|
||||
module SecureCookieFlow = DataFlow::Global<SecureCookieConfig>;
|
||||
@@ -0,0 +1,63 @@
|
||||
/**
|
||||
* Provides classes and a taint-tracking configuration to reason about the use of potentially insecure cryptographic algorithms.
|
||||
*/
|
||||
|
||||
import java
|
||||
private import semmle.code.java.security.Encryption
|
||||
private import semmle.code.java.dataflow.TaintTracking
|
||||
private import semmle.code.java.dispatch.VirtualDispatch
|
||||
|
||||
private class ShortStringLiteral extends StringLiteral {
|
||||
ShortStringLiteral() { this.getValue().length() < 100 }
|
||||
}
|
||||
|
||||
/**
|
||||
* A string literal that may refer to an insecure cryptographic algorithm.
|
||||
*/
|
||||
class InsecureAlgoLiteral extends ShortStringLiteral {
|
||||
InsecureAlgoLiteral() {
|
||||
// Algorithm identifiers should be at least two characters.
|
||||
this.getValue().length() > 1 and
|
||||
exists(string s | s = this.getValue() |
|
||||
not s.regexpMatch(getSecureAlgorithmRegex()) and
|
||||
// Exclude results covered by another query.
|
||||
not s.regexpMatch(getInsecureAlgorithmRegex())
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private predicate objectToString(MethodAccess ma) {
|
||||
exists(ToStringMethod m |
|
||||
m = ma.getMethod() and
|
||||
m.getDeclaringType() instanceof TypeObject and
|
||||
DataFlow::exprNode(ma.getQualifier()).getTypeBound().getErasure() instanceof TypeObject
|
||||
)
|
||||
}
|
||||
|
||||
private class StringContainer extends RefType {
|
||||
StringContainer() {
|
||||
this instanceof TypeString or
|
||||
this instanceof StringBuildingType or
|
||||
this.hasQualifiedName("java.util", "StringTokenizer") or
|
||||
this.(Array).getComponentType() instanceof StringContainer
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A taint-tracking configuration to reason about the use of potentially insecure cryptographic algorithms.
|
||||
*/
|
||||
module InsecureCryptoConfig implements DataFlow::ConfigSig {
|
||||
predicate isSource(DataFlow::Node n) { n.asExpr() instanceof InsecureAlgoLiteral }
|
||||
|
||||
predicate isSink(DataFlow::Node n) { exists(CryptoAlgoSpec c | n.asExpr() = c.getAlgoSpec()) }
|
||||
|
||||
predicate isBarrier(DataFlow::Node n) {
|
||||
objectToString(n.asExpr()) or
|
||||
not n.getType().getErasure() instanceof StringContainer
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Taint-tracking flow for use of potentially insecure cryptographic algorithms.
|
||||
*/
|
||||
module InsecureCryptoFlow = TaintTracking::Global<InsecureCryptoConfig>;
|
||||
@@ -0,0 +1,133 @@
|
||||
/** Provides classes to reason about possible truncation from casting of a user-provided value. */
|
||||
|
||||
import java
|
||||
private import semmle.code.java.arithmetic.Overflow
|
||||
private import semmle.code.java.dataflow.SSA
|
||||
private import semmle.code.java.controlflow.Guards
|
||||
private import semmle.code.java.dataflow.RangeAnalysis
|
||||
private import semmle.code.java.dataflow.FlowSources
|
||||
|
||||
/**
|
||||
* A `CastExpr` that is a narrowing cast.
|
||||
*/
|
||||
class NumericNarrowingCastExpr extends CastExpr {
|
||||
NumericNarrowingCastExpr() {
|
||||
exists(NumericType sourceType, NumericType targetType |
|
||||
sourceType = this.getExpr().getType() and targetType = this.getType()
|
||||
|
|
||||
not targetType.(NumType).widerThanOrEqualTo(sourceType)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* An expression that performs a right shift operation.
|
||||
*/
|
||||
class RightShiftOp extends Expr {
|
||||
RightShiftOp() {
|
||||
this instanceof RightShiftExpr or
|
||||
this instanceof UnsignedRightShiftExpr or
|
||||
this instanceof AssignRightShiftExpr or
|
||||
this instanceof AssignUnsignedRightShiftExpr
|
||||
}
|
||||
|
||||
private Expr getLhs() {
|
||||
this.(BinaryExpr).getLeftOperand() = result or
|
||||
this.(Assignment).getDest() = result
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the variable that is shifted.
|
||||
*/
|
||||
Variable getShiftedVariable() {
|
||||
this.getLhs() = result.getAnAccess() or
|
||||
this.getLhs().(AndBitwiseExpr).getAnOperand() = result.getAnAccess()
|
||||
}
|
||||
}
|
||||
|
||||
private predicate boundedRead(RValue read) {
|
||||
exists(SsaVariable v, ConditionBlock cb, ComparisonExpr comp, boolean testIsTrue |
|
||||
read = v.getAUse() and
|
||||
cb.controls(read.getBasicBlock(), testIsTrue) and
|
||||
cb.getCondition() = comp
|
||||
|
|
||||
comp.getLesserOperand() = v.getAUse() and testIsTrue = true
|
||||
or
|
||||
comp.getGreaterOperand() = v.getAUse() and testIsTrue = false
|
||||
)
|
||||
}
|
||||
|
||||
private predicate castCheck(RValue read) {
|
||||
exists(EqualityTest eq, CastExpr cast |
|
||||
cast.getExpr() = read and
|
||||
eq.hasOperands(cast, read.getVariable().getAnAccess())
|
||||
)
|
||||
}
|
||||
|
||||
private class SmallType extends Type {
|
||||
SmallType() {
|
||||
this instanceof BooleanType or
|
||||
this.(PrimitiveType).hasName("byte") or
|
||||
this.(BoxedType).getPrimitiveType().hasName("byte")
|
||||
}
|
||||
}
|
||||
|
||||
private predicate smallExpr(Expr e) {
|
||||
exists(int low, int high |
|
||||
bounded(e, any(ZeroBound zb), low, false, _) and
|
||||
bounded(e, any(ZeroBound zb), high, true, _) and
|
||||
high - low < 256
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* A taint-tracking configuration for reasoning about user input that is used in a
|
||||
* numeric cast.
|
||||
*/
|
||||
module NumericCastFlowConfig implements DataFlow::ConfigSig {
|
||||
predicate isSource(DataFlow::Node src) { src instanceof RemoteFlowSource }
|
||||
|
||||
predicate isSink(DataFlow::Node sink) {
|
||||
sink.asExpr() = any(NumericNarrowingCastExpr cast).getExpr() and
|
||||
sink.asExpr() instanceof VarAccess
|
||||
}
|
||||
|
||||
predicate isBarrier(DataFlow::Node node) {
|
||||
boundedRead(node.asExpr()) or
|
||||
castCheck(node.asExpr()) or
|
||||
node.getType() instanceof SmallType or
|
||||
smallExpr(node.asExpr()) or
|
||||
node.getEnclosingCallable() instanceof HashCodeMethod or
|
||||
exists(RightShiftOp e | e.getShiftedVariable().getAnAccess() = node.asExpr())
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Taint-tracking flow for user input that is used in a numeric cast.
|
||||
*/
|
||||
module NumericCastFlow = TaintTracking::Global<NumericCastFlowConfig>;
|
||||
|
||||
/**
|
||||
* A taint-tracking configuration for reasoning about local user input that is
|
||||
* used in a numeric cast.
|
||||
*/
|
||||
module NumericCastLocalFlowConfig implements DataFlow::ConfigSig {
|
||||
predicate isSource(DataFlow::Node src) { src instanceof LocalUserInput }
|
||||
|
||||
predicate isSink(DataFlow::Node sink) {
|
||||
sink.asExpr() = any(NumericNarrowingCastExpr cast).getExpr()
|
||||
}
|
||||
|
||||
predicate isBarrier(DataFlow::Node node) {
|
||||
boundedRead(node.asExpr()) or
|
||||
castCheck(node.asExpr()) or
|
||||
node.getType() instanceof SmallType or
|
||||
smallExpr(node.asExpr()) or
|
||||
node.getEnclosingCallable() instanceof HashCodeMethod
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Taint-tracking flow for local user input that is used in a numeric cast.
|
||||
*/
|
||||
module NumericCastLocalFlow = TaintTracking::Global<NumericCastLocalFlowConfig>;
|
||||
@@ -0,0 +1,24 @@
|
||||
/** Provides a taint-tracking configuration to reason about response splitting vulnerabilities from local user input. */
|
||||
|
||||
import java
|
||||
private import semmle.code.java.dataflow.FlowSources
|
||||
private import semmle.code.java.security.ResponseSplitting
|
||||
|
||||
/**
|
||||
* A taint-tracking configuration to reason about response splitting vulnerabilities from local user input.
|
||||
*/
|
||||
module ResponseSplittingLocalConfig implements DataFlow::ConfigSig {
|
||||
predicate isSource(DataFlow::Node source) { source instanceof LocalUserInput }
|
||||
|
||||
predicate isSink(DataFlow::Node sink) { sink instanceof HeaderSplittingSink }
|
||||
|
||||
predicate isBarrier(DataFlow::Node node) {
|
||||
node.getType() instanceof PrimitiveType or
|
||||
node.getType() instanceof BoxedType
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Taint-tracking flow for response splitting vulnerabilities from local user input.
|
||||
*/
|
||||
module ResponseSplittingLocalFlow = TaintTracking::Global<ResponseSplittingLocalConfig>;
|
||||
@@ -22,22 +22,22 @@ abstract class FlagKind extends string {
|
||||
|
||||
private predicate flagFlowStepTC(DataFlow::Node node1, DataFlow::Node node2) {
|
||||
node2 = node1 and
|
||||
isFlagWithName(node1)
|
||||
this.isFlagWithName(node1)
|
||||
or
|
||||
exists(DataFlow::Node nodeMid |
|
||||
flagFlowStep(nodeMid, node2) and
|
||||
flagFlowStepTC(node1, nodeMid)
|
||||
this.flagFlowStepTC(node1, nodeMid)
|
||||
)
|
||||
}
|
||||
|
||||
private predicate isFlagWithName(DataFlow::Node flag) {
|
||||
exists(VarAccess v | v.getVariable().getName() = getAFlagName() |
|
||||
exists(VarAccess v | v.getVariable().getName() = this.getAFlagName() |
|
||||
flag.asExpr() = v and v.getType() instanceof FlagType
|
||||
)
|
||||
or
|
||||
exists(StringLiteral s | s.getValue() = getAFlagName() | flag.asExpr() = s)
|
||||
exists(StringLiteral s | s.getValue() = this.getAFlagName() | flag.asExpr() = s)
|
||||
or
|
||||
exists(MethodAccess ma | ma.getMethod().getName() = getAFlagName() |
|
||||
exists(MethodAccess ma | ma.getMethod().getName() = this.getAFlagName() |
|
||||
flag.asExpr() = ma and
|
||||
ma.getType() instanceof FlagType
|
||||
)
|
||||
@@ -46,8 +46,8 @@ abstract class FlagKind extends string {
|
||||
/** Gets a node representing a (likely) security flag. */
|
||||
DataFlow::Node getAFlag() {
|
||||
exists(DataFlow::Node flag |
|
||||
isFlagWithName(flag) and
|
||||
flagFlowStepTC(flag, result)
|
||||
this.isFlagWithName(flag) and
|
||||
this.flagFlowStepTC(flag, result)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,34 @@
|
||||
/** Provides classes and modules to reason about SqlInjection vulnerabilities from string concatentation. */
|
||||
|
||||
import java
|
||||
private import semmle.code.java.dataflow.TaintTracking
|
||||
private import semmle.code.java.security.SqlConcatenatedLib
|
||||
private import semmle.code.java.security.SqlInjectionQuery
|
||||
|
||||
private class UncontrolledStringBuilderSource extends DataFlow::ExprNode {
|
||||
UncontrolledStringBuilderSource() {
|
||||
exists(StringBuilderVar sbv |
|
||||
uncontrolledStringBuilderQuery(sbv, _) and
|
||||
this.getExpr() = sbv.getToStringCall()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A taint-tracking configuration for reasoning about uncontrolled string builders.
|
||||
*/
|
||||
module UncontrolledStringBuilderSourceFlowConfig implements DataFlow::ConfigSig {
|
||||
predicate isSource(DataFlow::Node src) { src instanceof UncontrolledStringBuilderSource }
|
||||
|
||||
predicate isSink(DataFlow::Node sink) { sink instanceof QueryInjectionSink }
|
||||
|
||||
predicate isBarrier(DataFlow::Node node) {
|
||||
node.getType() instanceof PrimitiveType or node.getType() instanceof BoxedType
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Taint-tracking flow for uncontrolled string builders that are used in a SQL query.
|
||||
*/
|
||||
module UncontrolledStringBuilderSourceFlow =
|
||||
TaintTracking::Global<UncontrolledStringBuilderSourceFlowConfig>;
|
||||
@@ -0,0 +1,32 @@
|
||||
/**
|
||||
* Provides a taint-tracking configuration for reasoning about local user input
|
||||
* that is used in a SQL query.
|
||||
*/
|
||||
|
||||
import java
|
||||
private import semmle.code.java.dataflow.FlowSources
|
||||
private import semmle.code.java.security.SqlInjectionQuery
|
||||
|
||||
/**
|
||||
* A taint-tracking configuration for reasoning about local user input that is
|
||||
* used in a SQL query.
|
||||
*/
|
||||
module LocalUserInputToQueryInjectionFlowConfig implements DataFlow::ConfigSig {
|
||||
predicate isSource(DataFlow::Node src) { src instanceof LocalUserInput }
|
||||
|
||||
predicate isSink(DataFlow::Node sink) { sink instanceof QueryInjectionSink }
|
||||
|
||||
predicate isBarrier(DataFlow::Node node) {
|
||||
node.getType() instanceof PrimitiveType or node.getType() instanceof BoxedType
|
||||
}
|
||||
|
||||
predicate isAdditionalFlowStep(DataFlow::Node node1, DataFlow::Node node2) {
|
||||
any(AdditionalQueryInjectionTaintStep s).step(node1, node2)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Taint-tracking flow for local user input that is used in a SQL query.
|
||||
*/
|
||||
module LocalUserInputToQueryInjectionFlow =
|
||||
TaintTracking::Global<LocalUserInputToQueryInjectionFlowConfig>;
|
||||
@@ -0,0 +1,122 @@
|
||||
/** Provides predicates to reason about exposure of stack-traces. */
|
||||
|
||||
import java
|
||||
private import semmle.code.java.dataflow.DataFlow
|
||||
private import semmle.code.java.dataflow.TaintTracking
|
||||
private import semmle.code.java.security.InformationLeak
|
||||
|
||||
/**
|
||||
* One of the `printStackTrace()` overloads on `Throwable`.
|
||||
*/
|
||||
private class PrintStackTraceMethod extends Method {
|
||||
PrintStackTraceMethod() {
|
||||
this.getDeclaringType()
|
||||
.getSourceDeclaration()
|
||||
.getASourceSupertype*()
|
||||
.hasQualifiedName("java.lang", "Throwable") and
|
||||
this.getName() = "printStackTrace"
|
||||
}
|
||||
}
|
||||
|
||||
private module ServletWriterSourceToPrintStackTraceMethodFlowConfig implements DataFlow::ConfigSig {
|
||||
predicate isSource(DataFlow::Node src) { src.asExpr() instanceof XssVulnerableWriterSource }
|
||||
|
||||
predicate isSink(DataFlow::Node sink) {
|
||||
exists(MethodAccess ma |
|
||||
sink.asExpr() = ma.getAnArgument() and ma.getMethod() instanceof PrintStackTraceMethod
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private module ServletWriterSourceToPrintStackTraceMethodFlow =
|
||||
TaintTracking::Global<ServletWriterSourceToPrintStackTraceMethodFlowConfig>;
|
||||
|
||||
/**
|
||||
* A call that uses `Throwable.printStackTrace()` on a stream that is connected
|
||||
* to external output.
|
||||
*/
|
||||
private predicate printsStackToWriter(MethodAccess call) {
|
||||
exists(PrintStackTraceMethod printStackTrace |
|
||||
call.getMethod() = printStackTrace and
|
||||
ServletWriterSourceToPrintStackTraceMethodFlow::flowToExpr(call.getAnArgument())
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* A `PrintWriter` that wraps a given string writer. This pattern is used
|
||||
* in the most common idiom for converting a `Throwable` to a string.
|
||||
*/
|
||||
private predicate printWriterOnStringWriter(Expr printWriter, Variable stringWriterVar) {
|
||||
printWriter.getType().(Class).hasQualifiedName("java.io", "PrintWriter") and
|
||||
stringWriterVar.getType().(Class).hasQualifiedName("java.io", "StringWriter") and
|
||||
(
|
||||
printWriter.(ClassInstanceExpr).getAnArgument() = stringWriterVar.getAnAccess() or
|
||||
printWriterOnStringWriter(printWriter.(VarAccess).getVariable().getInitializer(),
|
||||
stringWriterVar)
|
||||
)
|
||||
}
|
||||
|
||||
private predicate stackTraceExpr(Expr exception, MethodAccess stackTraceString) {
|
||||
exists(Expr printWriter, Variable stringWriterVar, MethodAccess printStackCall |
|
||||
printWriterOnStringWriter(printWriter, stringWriterVar) and
|
||||
printStackCall.getMethod() instanceof PrintStackTraceMethod and
|
||||
printStackCall.getAnArgument() = printWriter and
|
||||
printStackCall.getQualifier() = exception and
|
||||
stackTraceString.getQualifier() = stringWriterVar.getAnAccess() and
|
||||
stackTraceString.getMethod() instanceof ToStringMethod
|
||||
)
|
||||
}
|
||||
|
||||
private module StackTraceStringToHttpResponseSinkFlowConfig implements DataFlow::ConfigSig {
|
||||
predicate isSource(DataFlow::Node src) { stackTraceExpr(_, src.asExpr()) }
|
||||
|
||||
predicate isSink(DataFlow::Node sink) { sink instanceof InformationLeakSink }
|
||||
}
|
||||
|
||||
private module StackTraceStringToHttpResponseSinkFlow =
|
||||
TaintTracking::Global<StackTraceStringToHttpResponseSinkFlowConfig>;
|
||||
|
||||
/**
|
||||
* Holds if `call` writes the data of `stackTrace` to an external stream.
|
||||
*/
|
||||
predicate printsStackExternally(MethodAccess call, Expr stackTrace) {
|
||||
printsStackToWriter(call) and
|
||||
call.getQualifier() = stackTrace and
|
||||
not call.getQualifier() instanceof SuperAccess
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `stackTrace` is a stringified stack trace which flows to an external sink.
|
||||
*/
|
||||
predicate stringifiedStackFlowsExternally(DataFlow::Node externalExpr, Expr stackTrace) {
|
||||
exists(MethodAccess stackTraceString |
|
||||
stackTraceExpr(stackTrace, stackTraceString) and
|
||||
StackTraceStringToHttpResponseSinkFlow::flow(DataFlow::exprNode(stackTraceString), externalExpr)
|
||||
)
|
||||
}
|
||||
|
||||
private class GetMessageFlowSource extends DataFlow::Node {
|
||||
GetMessageFlowSource() {
|
||||
exists(Method method | this.asExpr().(MethodAccess).getMethod() = method |
|
||||
method.hasName("getMessage") and
|
||||
method.hasNoParameters() and
|
||||
method.getDeclaringType().hasQualifiedName("java.lang", "Throwable")
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private module GetMessageFlowSourceToHttpResponseSinkFlowConfig implements DataFlow::ConfigSig {
|
||||
predicate isSource(DataFlow::Node src) { src instanceof GetMessageFlowSource }
|
||||
|
||||
predicate isSink(DataFlow::Node sink) { sink instanceof InformationLeakSink }
|
||||
}
|
||||
|
||||
private module GetMessageFlowSourceToHttpResponseSinkFlow =
|
||||
TaintTracking::Global<GetMessageFlowSourceToHttpResponseSinkFlowConfig>;
|
||||
|
||||
/**
|
||||
* Holds if there is a call to `getMessage()` that then flows to a servlet response.
|
||||
*/
|
||||
predicate getMessageFlowsExternally(DataFlow::Node externalExpr, GetMessageFlowSource getMessage) {
|
||||
GetMessageFlowSourceToHttpResponseSinkFlow::flow(getMessage, externalExpr)
|
||||
}
|
||||
@@ -0,0 +1,65 @@
|
||||
/** Provides classes to reason about tainted permissions check vulnerabilities. */
|
||||
|
||||
import java
|
||||
private import semmle.code.java.dataflow.FlowSources
|
||||
private import semmle.code.java.dataflow.TaintTracking
|
||||
|
||||
/**
|
||||
* The `org.apache.shiro.subject.Subject` class.
|
||||
*/
|
||||
private class TypeShiroSubject extends RefType {
|
||||
TypeShiroSubject() { this.getQualifiedName() = "org.apache.shiro.subject.Subject" }
|
||||
}
|
||||
|
||||
/**
|
||||
* The `org.apache.shiro.authz.permission.WildcardPermission` class.
|
||||
*/
|
||||
private class TypeShiroWildCardPermission extends RefType {
|
||||
TypeShiroWildCardPermission() {
|
||||
this.getQualifiedName() = "org.apache.shiro.authz.permission.WildcardPermission"
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* An expression that constructs a permission.
|
||||
*/
|
||||
abstract class PermissionsConstruction extends Top {
|
||||
/** Gets the input to this permission construction. */
|
||||
abstract Expr getInput();
|
||||
}
|
||||
|
||||
private class PermissionsCheckMethodAccess extends MethodAccess, PermissionsConstruction {
|
||||
PermissionsCheckMethodAccess() {
|
||||
exists(Method m | m = this.getMethod() |
|
||||
m.getDeclaringType() instanceof TypeShiroSubject and
|
||||
m.getName() = "isPermitted"
|
||||
or
|
||||
m.getName().toLowerCase().matches("%permitted%") and
|
||||
m.getNumberOfParameters() = 1
|
||||
)
|
||||
}
|
||||
|
||||
override Expr getInput() { result = this.getArgument(0) }
|
||||
}
|
||||
|
||||
private class WildCardPermissionConstruction extends ClassInstanceExpr, PermissionsConstruction {
|
||||
WildCardPermissionConstruction() {
|
||||
this.getConstructor().getDeclaringType() instanceof TypeShiroWildCardPermission
|
||||
}
|
||||
|
||||
override Expr getInput() { result = this.getArgument(0) }
|
||||
}
|
||||
|
||||
/**
|
||||
* A configuration for tracking flow from user input to a permissions check.
|
||||
*/
|
||||
module TaintedPermissionsCheckFlowConfig implements DataFlow::ConfigSig {
|
||||
predicate isSource(DataFlow::Node source) { source instanceof UserInput }
|
||||
|
||||
predicate isSink(DataFlow::Node sink) {
|
||||
sink.asExpr() = any(PermissionsConstruction p).getInput()
|
||||
}
|
||||
}
|
||||
|
||||
/** Tracks flow from user input to a permissions check. */
|
||||
module TaintedPermissionsCheckFlow = TaintTracking::Global<TaintedPermissionsCheckFlowConfig>;
|
||||
@@ -0,0 +1,254 @@
|
||||
/** Provides classes to reason about local information disclosure in a temporary directory. */
|
||||
|
||||
import java
|
||||
private import semmle.code.java.dataflow.TaintTracking
|
||||
private import semmle.code.java.os.OSCheck
|
||||
private import semmle.code.java.security.TempDirUtils
|
||||
|
||||
/**
|
||||
* A method which creates a file or directory in the file system.
|
||||
*/
|
||||
abstract private class MethodFileSystemFileCreation extends Method {
|
||||
MethodFileSystemFileCreation() { this.getDeclaringType() instanceof TypeFile }
|
||||
}
|
||||
|
||||
/**
|
||||
* A method which creates a directory in the file system.
|
||||
*/
|
||||
private class MethodFileDirectoryCreation extends MethodFileSystemFileCreation {
|
||||
MethodFileDirectoryCreation() { this.hasName(["mkdir", "mkdirs"]) }
|
||||
}
|
||||
|
||||
/**
|
||||
* A method which creates a file in the file system.
|
||||
*/
|
||||
private class MethodFileFileCreation extends MethodFileSystemFileCreation {
|
||||
MethodFileFileCreation() { this.hasName("createNewFile") }
|
||||
}
|
||||
|
||||
/**
|
||||
* A dataflow node that creates a file or directory in the file system.
|
||||
*/
|
||||
abstract private class FileCreationSink extends DataFlow::Node { }
|
||||
|
||||
/**
|
||||
* The qualifier of a call to one of `File`'s file-creating or directory-creating methods,
|
||||
* treated as a sink by `TempDirSystemGetPropertyToCreateConfig`.
|
||||
*/
|
||||
private class FileFileCreationSink extends FileCreationSink {
|
||||
FileFileCreationSink() {
|
||||
exists(MethodAccess ma |
|
||||
ma.getMethod() instanceof MethodFileSystemFileCreation and
|
||||
ma.getQualifier() = this.asExpr()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The argument to a call to one of `Files` file-creating or directory-creating methods,
|
||||
* treated as a sink by `TempDirSystemGetPropertyToCreateConfig`.
|
||||
*/
|
||||
private class FilesFileCreationSink extends FileCreationSink {
|
||||
FilesFileCreationSink() {
|
||||
exists(FilesVulnerableCreationMethodAccess ma | ma.getArgument(0) = this.asExpr())
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A call to a `Files` method that create files/directories without explicitly
|
||||
* setting the newly-created file or directory's permissions.
|
||||
*/
|
||||
private class FilesVulnerableCreationMethodAccess extends MethodAccess {
|
||||
FilesVulnerableCreationMethodAccess() {
|
||||
exists(Method m |
|
||||
m = this.getMethod() and
|
||||
m.getDeclaringType().hasQualifiedName("java.nio.file", "Files")
|
||||
|
|
||||
m.hasName(["write", "newBufferedWriter", "newOutputStream"])
|
||||
or
|
||||
m.hasName(["createFile", "createDirectory", "createDirectories"]) and
|
||||
this.getNumArgument() = 1
|
||||
or
|
||||
m.hasName("newByteChannel") and
|
||||
this.getNumArgument() = 2
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A call to a `File` method that create files/directories with a specific set of permissions explicitly set.
|
||||
* We can safely assume that any calls to these methods with explicit `PosixFilePermissions.asFileAttribute`
|
||||
* contains a certain level of intentionality behind it.
|
||||
*/
|
||||
private class FilesSanitizingCreationMethodAccess extends MethodAccess {
|
||||
FilesSanitizingCreationMethodAccess() {
|
||||
exists(Method m |
|
||||
m = this.getMethod() and
|
||||
m.getDeclaringType().hasQualifiedName("java.nio.file", "Files")
|
||||
|
|
||||
m.hasName(["createFile", "createDirectory", "createDirectories"]) and
|
||||
this.getNumArgument() = 2
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The temp directory argument to a call to `java.io.File::createTempFile`,
|
||||
* treated as a sink by `TempDirSystemGetPropertyToCreateConfig`.
|
||||
*/
|
||||
private class FileCreateTempFileSink extends FileCreationSink {
|
||||
FileCreateTempFileSink() {
|
||||
exists(MethodAccess ma |
|
||||
ma.getMethod() instanceof MethodFileCreateTempFile and ma.getArgument(2) = this.asExpr()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A sanitizer that holds when the program is definitely running under some version of Windows.
|
||||
*/
|
||||
abstract private class WindowsOsSanitizer extends DataFlow::Node { }
|
||||
|
||||
private class IsNotUnixSanitizer extends WindowsOsSanitizer {
|
||||
IsNotUnixSanitizer() { any(IsUnixGuard guard).controls(this.asExpr().getBasicBlock(), false) }
|
||||
}
|
||||
|
||||
private class IsWindowsSanitizer extends WindowsOsSanitizer {
|
||||
IsWindowsSanitizer() { any(IsWindowsGuard guard).controls(this.asExpr().getBasicBlock(), true) }
|
||||
}
|
||||
|
||||
private class IsSpecificWindowsSanitizer extends WindowsOsSanitizer {
|
||||
IsSpecificWindowsSanitizer() {
|
||||
any(IsSpecificWindowsVariant guard).controls(this.asExpr().getBasicBlock(), true)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A taint tracking configuration tracking the access of the system temporary directory
|
||||
* flowing to the creation of files or directories.
|
||||
*/
|
||||
module TempDirSystemGetPropertyToCreateConfig implements DataFlow::ConfigSig {
|
||||
predicate isSource(DataFlow::Node source) {
|
||||
source.asExpr() instanceof ExprSystemGetPropertyTempDirTainted
|
||||
}
|
||||
|
||||
predicate isSink(DataFlow::Node sink) {
|
||||
sink instanceof FileCreationSink and
|
||||
not TempDirSystemGetPropertyDirectlyToMkdir::flowTo(sink)
|
||||
}
|
||||
|
||||
predicate isBarrier(DataFlow::Node sanitizer) {
|
||||
exists(FilesSanitizingCreationMethodAccess sanitisingMethodAccess |
|
||||
sanitizer.asExpr() = sanitisingMethodAccess.getArgument(0)
|
||||
)
|
||||
or
|
||||
sanitizer instanceof WindowsOsSanitizer
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Taint-tracking flow which tracks the access of the system temporary directory
|
||||
* flowing to the creation of files or directories.
|
||||
*/
|
||||
module TempDirSystemGetPropertyToCreate =
|
||||
TaintTracking::Global<TempDirSystemGetPropertyToCreateConfig>;
|
||||
|
||||
/**
|
||||
* Configuration that tracks calls to to `mkdir` or `mkdirs` that are are directly on the temp directory system property.
|
||||
* Examples:
|
||||
* - `File tempDir = new File(System.getProperty("java.io.tmpdir")); tempDir.mkdir();`
|
||||
* - `File tempDir = new File(System.getProperty("java.io.tmpdir")); tempDir.mkdirs();`
|
||||
*
|
||||
* These are examples of code that is simply verifying that the temp directory exists.
|
||||
* As such, this code pattern is filtered out as an explicit vulnerability in
|
||||
* `TempDirSystemGetPropertyToCreateConfig::isSink`.
|
||||
*/
|
||||
module TempDirSystemGetPropertyDirectlyToMkdirConfig implements DataFlow::ConfigSig {
|
||||
predicate isSource(DataFlow::Node node) {
|
||||
exists(ExprSystemGetPropertyTempDirTainted propertyGetExpr, DataFlow::Node callSite |
|
||||
DataFlow::localFlow(DataFlow::exprNode(propertyGetExpr), callSite)
|
||||
|
|
||||
isFileConstructorArgument(callSite.asExpr(), node.asExpr(), 1)
|
||||
)
|
||||
}
|
||||
|
||||
predicate isSink(DataFlow::Node node) {
|
||||
exists(MethodAccess ma | ma.getMethod() instanceof MethodFileDirectoryCreation |
|
||||
ma.getQualifier() = node.asExpr()
|
||||
)
|
||||
}
|
||||
|
||||
predicate isBarrier(DataFlow::Node sanitizer) {
|
||||
isFileConstructorArgument(sanitizer.asExpr(), _, _)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Taint-tracking flow that tracks calls to to `mkdir` or `mkdirs` that are are directly on the temp directory system property.
|
||||
* Examples:
|
||||
* - `File tempDir = new File(System.getProperty("java.io.tmpdir")); tempDir.mkdir();`
|
||||
* - `File tempDir = new File(System.getProperty("java.io.tmpdir")); tempDir.mkdirs();`
|
||||
*
|
||||
* These are examples of code that is simply verifying that the temp directory exists.
|
||||
* As such, this code pattern is filtered out as an explicit vulnerability in
|
||||
* `TempDirSystemGetPropertyToCreateConfig::isSink`.
|
||||
*/
|
||||
module TempDirSystemGetPropertyDirectlyToMkdir =
|
||||
TaintTracking::Global<TempDirSystemGetPropertyDirectlyToMkdirConfig>;
|
||||
|
||||
//
|
||||
// Begin configuration for tracking single-method calls that are vulnerable.
|
||||
//
|
||||
/**
|
||||
* A `MethodAccess` against a method that creates a temporary file or directory in a shared temporary directory.
|
||||
*/
|
||||
abstract class MethodAccessInsecureFileCreation extends MethodAccess {
|
||||
/**
|
||||
* Gets the type of entity created (e.g. `file`, `directory`, ...).
|
||||
*/
|
||||
abstract string getFileSystemEntityType();
|
||||
|
||||
/**
|
||||
* Gets the dataflow node representing the file system entity created.
|
||||
*/
|
||||
DataFlow::Node getNode() { result.asExpr() = this }
|
||||
}
|
||||
|
||||
/**
|
||||
* An insecure call to `java.io.File.createTempFile`.
|
||||
*/
|
||||
class MethodAccessInsecureFileCreateTempFile extends MethodAccessInsecureFileCreation {
|
||||
MethodAccessInsecureFileCreateTempFile() {
|
||||
this.getMethod() instanceof MethodFileCreateTempFile and
|
||||
(
|
||||
// `File.createTempFile(string, string)` always uses the default temporary directory
|
||||
this.getNumArgument() = 2
|
||||
or
|
||||
// The default temporary directory is used when the last argument of `File.createTempFile(string, string, File)` is `null`
|
||||
DataFlow::localExprFlow(any(NullLiteral n), this.getArgument(2))
|
||||
)
|
||||
}
|
||||
|
||||
override string getFileSystemEntityType() { result = "file" }
|
||||
}
|
||||
|
||||
/**
|
||||
* The `com.google.common.io.Files.createTempDir` method.
|
||||
*/
|
||||
class MethodGuavaFilesCreateTempFile extends Method {
|
||||
MethodGuavaFilesCreateTempFile() {
|
||||
this.getDeclaringType().hasQualifiedName("com.google.common.io", "Files") and
|
||||
this.hasName("createTempDir")
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A call to the `com.google.common.io.Files.createTempDir` method.
|
||||
*/
|
||||
class MethodAccessInsecureGuavaFilesCreateTempFile extends MethodAccessInsecureFileCreation {
|
||||
MethodAccessInsecureGuavaFilesCreateTempFile() {
|
||||
this.getMethod() instanceof MethodGuavaFilesCreateTempFile
|
||||
}
|
||||
|
||||
override string getFileSystemEntityType() { result = "directory" }
|
||||
}
|
||||
83
java/ql/lib/semmle/code/java/security/TempDirUtils.qll
Normal file
83
java/ql/lib/semmle/code/java/security/TempDirUtils.qll
Normal file
@@ -0,0 +1,83 @@
|
||||
/**
|
||||
* Provides classes and predicates for reasoning about temporary file/directory creations.
|
||||
*/
|
||||
|
||||
import java
|
||||
private import semmle.code.java.environment.SystemProperty
|
||||
import semmle.code.java.dataflow.FlowSources
|
||||
|
||||
/**
|
||||
* A method or field access that returns a `String` or `File` that has been tainted by `System.getProperty("java.io.tmpdir")`.
|
||||
*/
|
||||
class ExprSystemGetPropertyTempDirTainted extends Expr {
|
||||
ExprSystemGetPropertyTempDirTainted() { this = getSystemProperty("java.io.tmpdir") }
|
||||
}
|
||||
|
||||
/**
|
||||
* A `java.io.File::createTempFile` method.
|
||||
*/
|
||||
class MethodFileCreateTempFile extends Method {
|
||||
MethodFileCreateTempFile() {
|
||||
this.getDeclaringType() instanceof TypeFile and
|
||||
this.hasName("createTempFile")
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `expDest` is some constructor call `new java.io.File(expSource)`, where the specific `File` constructor being used has `paramCount` parameters.
|
||||
*/
|
||||
predicate isFileConstructorArgument(Expr expSource, Expr exprDest, int paramCount) {
|
||||
exists(ConstructorCall construtorCall |
|
||||
construtorCall.getConstructedType() instanceof TypeFile and
|
||||
construtorCall.getArgument(0) = expSource and
|
||||
construtorCall = exprDest and
|
||||
construtorCall.getConstructor().getNumberOfParameters() = paramCount
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* A method call to `java.io.File::setReadable`.
|
||||
*/
|
||||
private class FileSetRedableMethodAccess extends MethodAccess {
|
||||
FileSetRedableMethodAccess() {
|
||||
exists(Method m | this.getMethod() = m |
|
||||
m.getDeclaringType() instanceof TypeFile and
|
||||
m.hasName("setReadable")
|
||||
)
|
||||
}
|
||||
|
||||
predicate isCallWithArguments(boolean arg1, boolean arg2) {
|
||||
this.isCallWithArgument(0, arg1) and this.isCallToSecondArgumentWithValue(arg2)
|
||||
}
|
||||
|
||||
private predicate isCallToSecondArgumentWithValue(boolean value) {
|
||||
this.getMethod().getNumberOfParameters() = 1 and value = true
|
||||
or
|
||||
this.isCallWithArgument(1, value)
|
||||
}
|
||||
|
||||
private predicate isCallWithArgument(int index, boolean arg) {
|
||||
DataFlow::localExprFlow(any(CompileTimeConstantExpr e | e.getBooleanValue() = arg),
|
||||
this.getArgument(index))
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Hold's if temporary directory's use is protected if there is an explicit call to
|
||||
* `setReadable(false, false)`, then `setRedabale(true, true)`.
|
||||
*/
|
||||
predicate isPermissionsProtectedTempDirUse(DataFlow::Node sink) {
|
||||
exists(FileSetRedableMethodAccess setReadable1, FileSetRedableMethodAccess setReadable2 |
|
||||
setReadable1.isCallWithArguments(false, false) and
|
||||
setReadable2.isCallWithArguments(true, true)
|
||||
|
|
||||
exists(DataFlow::Node setReadableNode1, DataFlow::Node setReadableNode2 |
|
||||
setReadableNode1.asExpr() = setReadable1.getQualifier() and
|
||||
setReadableNode2.asExpr() = setReadable2.getQualifier()
|
||||
|
|
||||
DataFlow::localFlow(sink, setReadableNode1) and // Flow from sink to setReadable(false, false)
|
||||
DataFlow::localFlow(sink, setReadableNode2) and // Flow from sink to setReadable(true, true)
|
||||
DataFlow::localFlow(setReadableNode1, setReadableNode2) // Flow from setReadable(false, false) to setReadable(true, true)
|
||||
)
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,104 @@
|
||||
/** Provides predicates and dataflow configurations for reasoning about unsafe hostname verification. */
|
||||
|
||||
import java
|
||||
private import semmle.code.java.controlflow.Guards
|
||||
private import semmle.code.java.dataflow.DataFlow
|
||||
private import semmle.code.java.dataflow.FlowSources
|
||||
private import semmle.code.java.security.Encryption
|
||||
private import semmle.code.java.security.SecurityFlag
|
||||
private import semmle.code.java.dataflow.ExternalFlow
|
||||
|
||||
/**
|
||||
* Holds if `m` always returns `true` ignoring any exceptional flow.
|
||||
*/
|
||||
private predicate alwaysReturnsTrue(HostnameVerifierVerify m) {
|
||||
forex(ReturnStmt rs | rs.getEnclosingCallable() = m |
|
||||
rs.getResult().(CompileTimeConstantExpr).getBooleanValue() = true
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* A class that overrides the `javax.net.ssl.HostnameVerifier.verify` method and **always** returns `true` (though it could also exit due to an uncaught exception), thus
|
||||
* accepting any certificate despite a hostname mismatch.
|
||||
*/
|
||||
class TrustAllHostnameVerifier extends RefType {
|
||||
TrustAllHostnameVerifier() {
|
||||
this.getAnAncestor() instanceof HostnameVerifier and
|
||||
exists(HostnameVerifierVerify m |
|
||||
m.getDeclaringType() = this and
|
||||
alwaysReturnsTrue(m)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A configuration to model the flow of a `TrustAllHostnameVerifier` to a `set(Default)HostnameVerifier` call.
|
||||
*/
|
||||
module TrustAllHostnameVerifierConfig implements DataFlow::ConfigSig {
|
||||
predicate isSource(DataFlow::Node source) {
|
||||
source.asExpr().(ClassInstanceExpr).getConstructedType() instanceof TrustAllHostnameVerifier
|
||||
}
|
||||
|
||||
predicate isSink(DataFlow::Node sink) { sink instanceof HostnameVerifierSink }
|
||||
|
||||
predicate isBarrier(DataFlow::Node barrier) {
|
||||
// ignore nodes that are in functions that intentionally disable hostname verification
|
||||
barrier
|
||||
.getEnclosingCallable()
|
||||
.getName()
|
||||
/*
|
||||
* Regex: (_)* :
|
||||
* some methods have underscores.
|
||||
* Regex: (no|ignore|disable)(strictssl|ssl|verify|verification|hostname)
|
||||
* noStrictSSL ignoreSsl
|
||||
* Regex: (set)?(accept|trust|ignore|allow)(all|every|any)
|
||||
* acceptAll trustAll ignoreAll setTrustAnyHttps
|
||||
* Regex: (use|do|enable)insecure
|
||||
* useInsecureSSL
|
||||
* Regex: (set|do|use)?no.*(check|validation|verify|verification)
|
||||
* setNoCertificateCheck
|
||||
* Regex: disable
|
||||
* disableChecks
|
||||
*/
|
||||
|
||||
.regexpMatch("^(?i)(_)*((no|ignore|disable)(strictssl|ssl|verify|verification|hostname)" +
|
||||
"|(set)?(accept|trust|ignore|allow)(all|every|any)" +
|
||||
"|(use|do|enable)insecure|(set|do|use)?no.*(check|validation|verify|verification)|disable).*$")
|
||||
}
|
||||
}
|
||||
|
||||
/** Data flow to model the flow of a `TrustAllHostnameVerifier` to a `set(Default)HostnameVerifier` call. */
|
||||
module TrustAllHostnameVerifierFlow = DataFlow::Global<TrustAllHostnameVerifierConfig>;
|
||||
|
||||
/**
|
||||
* A sink that sets the `HostnameVerifier` on `HttpsURLConnection`.
|
||||
*/
|
||||
private class HostnameVerifierSink extends DataFlow::Node {
|
||||
HostnameVerifierSink() { sinkNode(this, "set-hostname-verifier") }
|
||||
}
|
||||
|
||||
/**
|
||||
* Flags suggesting a deliberately unsafe `HostnameVerifier` usage.
|
||||
*/
|
||||
private class UnsafeHostnameVerificationFlag extends FlagKind {
|
||||
UnsafeHostnameVerificationFlag() { this = "UnsafeHostnameVerificationFlag" }
|
||||
|
||||
bindingset[result]
|
||||
override string getAFlagName() {
|
||||
result
|
||||
.regexpMatch("(?i).*(secure|disable|selfCert|selfSign|validat|verif|trust|ignore|nocertificatecheck).*") and
|
||||
result != "equalsIgnoreCase"
|
||||
}
|
||||
}
|
||||
|
||||
/** Gets a guard that represents a (likely) flag controlling an unsafe `HostnameVerifier` use. */
|
||||
private Guard getAnUnsafeHostnameVerifierFlagGuard() {
|
||||
result = any(UnsafeHostnameVerificationFlag flag).getAFlag().asExpr()
|
||||
}
|
||||
|
||||
/** Holds if `node` is guarded by a flag that suggests an intentionally insecure use. */
|
||||
predicate isNodeGuardedByFlag(DataFlow::Node node) {
|
||||
exists(Guard g | g.controls(node.asExpr().getBasicBlock(), _) |
|
||||
g = getASecurityFeatureFlagGuard() or g = getAnUnsafeHostnameVerifierFlagGuard()
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
/** Provides a taint-tracking configuration to reason about URL redirection from local sources. */
|
||||
|
||||
import java
|
||||
private import semmle.code.java.dataflow.FlowSources
|
||||
private import semmle.code.java.security.UrlRedirect
|
||||
|
||||
/**
|
||||
* A taint-tracking configuration to reason about URL redirection from local sources.
|
||||
*/
|
||||
module UrlRedirectLocalConfig implements DataFlow::ConfigSig {
|
||||
predicate isSource(DataFlow::Node source) { source instanceof LocalUserInput }
|
||||
|
||||
predicate isSink(DataFlow::Node sink) { sink instanceof UrlRedirectSink }
|
||||
}
|
||||
|
||||
/**
|
||||
* Taint-tracking flow for URL redirection from local sources.
|
||||
*/
|
||||
module UrlRedirectLocalFlow = TaintTracking::Global<UrlRedirectLocalConfig>;
|
||||
19
java/ql/lib/semmle/code/java/security/UrlRedirectQuery.qll
Normal file
19
java/ql/lib/semmle/code/java/security/UrlRedirectQuery.qll
Normal file
@@ -0,0 +1,19 @@
|
||||
/** Provides a taint-tracking configuration for reasoning about URL redirections. */
|
||||
|
||||
import java
|
||||
private import semmle.code.java.dataflow.FlowSources
|
||||
private import semmle.code.java.security.UrlRedirect
|
||||
|
||||
/**
|
||||
* A taint-tracking configuration for reasoning about URL redirections.
|
||||
*/
|
||||
module UrlRedirectConfig implements DataFlow::ConfigSig {
|
||||
predicate isSource(DataFlow::Node source) { source instanceof RemoteFlowSource }
|
||||
|
||||
predicate isSink(DataFlow::Node sink) { sink instanceof UrlRedirectSink }
|
||||
}
|
||||
|
||||
/**
|
||||
* Taint-tracking flow for URL redirections.
|
||||
*/
|
||||
module UrlRedirectFlow = TaintTracking::Global<UrlRedirectConfig>;
|
||||
@@ -0,0 +1,20 @@
|
||||
/** Provides taint-tracking flow to reason about XPath injection queries. */
|
||||
|
||||
import java
|
||||
private import semmle.code.java.dataflow.FlowSources
|
||||
private import semmle.code.java.dataflow.TaintTracking
|
||||
private import semmle.code.java.security.XPath
|
||||
|
||||
/**
|
||||
* A taint-tracking configuration for reasoning about XPath injection vulnerabilities.
|
||||
*/
|
||||
module XPathInjectionConfig implements DataFlow::ConfigSig {
|
||||
predicate isSource(DataFlow::Node source) { source instanceof RemoteFlowSource }
|
||||
|
||||
predicate isSink(DataFlow::Node sink) { sink instanceof XPathInjectionSink }
|
||||
}
|
||||
|
||||
/**
|
||||
* Taint-tracking flow for XPath injection vulnerabilities.
|
||||
*/
|
||||
module XPathInjectionFlow = TaintTracking::Global<XPathInjectionConfig>;
|
||||
@@ -635,6 +635,11 @@ class XmlReader extends RefType {
|
||||
XmlReader() { this.hasQualifiedName("org.xml.sax", "XMLReader") }
|
||||
}
|
||||
|
||||
/** The class `org.xml.sax.InputSource`. */
|
||||
class InputSource extends Class {
|
||||
InputSource() { this.hasQualifiedName("org.xml.sax", "InputSource") }
|
||||
}
|
||||
|
||||
/** DEPRECATED: Alias for XmlReader */
|
||||
deprecated class XMLReader = XmlReader;
|
||||
|
||||
@@ -1144,22 +1149,34 @@ class XmlUnmarshal extends XmlParserCall {
|
||||
}
|
||||
|
||||
/* XPathExpression: https://cheatsheetseries.owasp.org/cheatsheets/XML_External_Entity_Prevention_Cheat_Sheet.html#xpathexpression */
|
||||
/** The class `javax.xml.xpath.XPathExpression`. */
|
||||
class XPathExpression extends RefType {
|
||||
/** The interface `javax.xml.xpath.XPathExpression`. */
|
||||
class XPathExpression extends Interface {
|
||||
XPathExpression() { this.hasQualifiedName("javax.xml.xpath", "XPathExpression") }
|
||||
}
|
||||
|
||||
/** A call to `XPathExpression.evaluate`. */
|
||||
/** The interface `java.xml.xpath.XPath`. */
|
||||
class XPath extends Interface {
|
||||
XPath() { this.hasQualifiedName("javax.xml.xpath", "XPath") }
|
||||
}
|
||||
|
||||
/** A call to the method `evaluate` of the classes `XPathExpression` or `XPath`. */
|
||||
class XPathEvaluate extends XmlParserCall {
|
||||
Argument sink;
|
||||
|
||||
XPathEvaluate() {
|
||||
exists(Method m |
|
||||
this.getMethod() = m and
|
||||
m.getDeclaringType() instanceof XPathExpression and
|
||||
m.hasName("evaluate")
|
||||
|
|
||||
m.getDeclaringType().getASourceSupertype*() instanceof XPathExpression and
|
||||
sink = this.getArgument(0)
|
||||
or
|
||||
m.getDeclaringType().getASourceSupertype*() instanceof XPath and
|
||||
sink = this.getArgument(1)
|
||||
)
|
||||
}
|
||||
|
||||
override Expr getSink() { result = this.getArgument(0) }
|
||||
override Expr getSink() { result = sink }
|
||||
|
||||
override predicate isSafe() { none() }
|
||||
}
|
||||
|
||||
20
java/ql/lib/semmle/code/java/security/XssLocalQuery.qll
Normal file
20
java/ql/lib/semmle/code/java/security/XssLocalQuery.qll
Normal file
@@ -0,0 +1,20 @@
|
||||
/** Provides a taint-tracking configuration to reason about cross-site scripting from a local source. */
|
||||
|
||||
import java
|
||||
private import semmle.code.java.dataflow.FlowSources
|
||||
private import semmle.code.java.dataflow.TaintTracking
|
||||
private import semmle.code.java.security.XSS
|
||||
|
||||
/**
|
||||
* A taint-tracking configuration for reasoning about cross-site scripting vulnerabilities from a local source.
|
||||
*/
|
||||
module XssLocalConfig implements DataFlow::ConfigSig {
|
||||
predicate isSource(DataFlow::Node source) { source instanceof LocalUserInput }
|
||||
|
||||
predicate isSink(DataFlow::Node sink) { sink instanceof XssSink }
|
||||
}
|
||||
|
||||
/**
|
||||
* Taint-tracking flow for cross-site scripting vulnerabilities from a local source.
|
||||
*/
|
||||
module XssLocalFlow = TaintTracking::Global<XssLocalConfig>;
|
||||
157
java/ql/lib/semmle/code/java/security/internal/ArraySizing.qll
Normal file
157
java/ql/lib/semmle/code/java/security/internal/ArraySizing.qll
Normal file
@@ -0,0 +1,157 @@
|
||||
/** Provides predicates and classes to reason about the sizing and indexing of arrays. */
|
||||
|
||||
import java
|
||||
private import semmle.code.java.dataflow.DataFlow
|
||||
private import semmle.code.java.dataflow.DefUse
|
||||
private import semmle.code.java.security.RandomDataSource
|
||||
private import BoundingChecks
|
||||
|
||||
/**
|
||||
* If the `Array` accessed by the `ArrayAccess` is a fixed size, return the array size.
|
||||
*/
|
||||
int fixedArraySize(ArrayAccess arrayAccess) {
|
||||
exists(Variable v |
|
||||
v.getAnAccess() = arrayAccess.getArray() and
|
||||
result = v.getAnAssignedValue().(ArrayCreationExpr).getFirstDimensionSize()
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if an `ArrayIndexOutOfBoundsException` is ever caught.
|
||||
*/
|
||||
private predicate arrayIndexOutOfBoundExceptionCaught(ArrayAccess arrayAccess) {
|
||||
exists(TryStmt ts, CatchClause cc, RefType exc |
|
||||
(
|
||||
ts.getBlock().getAChild*() = arrayAccess.getEnclosingStmt() or
|
||||
ts.getAResourceDecl().getAChild*() = arrayAccess.getEnclosingStmt() or
|
||||
ts.getAResourceExpr().getAChildExpr*() = arrayAccess
|
||||
) and
|
||||
cc = ts.getACatchClause() and
|
||||
exc = cc.getVariable().getType() and
|
||||
exc.hasQualifiedName("java.lang", "ArrayIndexOutOfBoundsException")
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* A pointless loop, of the type seen frequently in Juliet tests, of the form:
|
||||
*
|
||||
* ```
|
||||
* while(true) {
|
||||
* ...
|
||||
* break;
|
||||
* }
|
||||
* ```
|
||||
*/
|
||||
class PointlessLoop extends WhileStmt {
|
||||
PointlessLoop() {
|
||||
this.getCondition().(BooleanLiteral).getBooleanValue() = true and
|
||||
// The only `break` must be the last statement.
|
||||
forall(BreakStmt break | break.getTarget() = this |
|
||||
this.getStmt().(BlockStmt).getLastStmt() = break
|
||||
) and
|
||||
// No `continue` statements.
|
||||
not exists(ContinueStmt continue | continue.getTarget() = this)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* An `ArrayAccess` for which we can determine whether the index is appropriately bound checked.
|
||||
*
|
||||
* We only consider first dimension array accesses, and we only consider indices in loops, if it's
|
||||
* obvious that the loop only executes once.
|
||||
*/
|
||||
class CheckableArrayAccess extends ArrayAccess {
|
||||
CheckableArrayAccess() {
|
||||
// We are not interested in array accesses that don't access the first dimension.
|
||||
not this.getArray() instanceof ArrayAccess and
|
||||
// Array accesses within loops can make it difficult to verify whether the index is checked
|
||||
// prior to access. Ignore "pointless" loops of the sort found in Juliet test cases.
|
||||
not exists(LoopStmt loop |
|
||||
loop.getBody().getAChild*() = this.getEnclosingStmt() and
|
||||
not loop instanceof PointlessLoop
|
||||
) and
|
||||
// The possible exception is not caught
|
||||
not arrayIndexOutOfBoundExceptionCaught(this)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if we believe this indexing expression can throw an `ArrayIndexOutOfBoundsException`.
|
||||
*/
|
||||
predicate canThrowOutOfBounds(Expr index) {
|
||||
index = this.getIndexExpr() and
|
||||
not (
|
||||
// There is a condition dominating this expression ensuring that the index is >= 0.
|
||||
lowerBound(index) >= 0 and
|
||||
// There is a condition dominating this expression that ensures the index is less than the length.
|
||||
lessthanLength(this)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if we believe this indexing expression can throw an `ArrayIndexOutOfBoundsException` due
|
||||
* to the array being initialized with `sizeExpr`, which may be zero.
|
||||
*/
|
||||
predicate canThrowOutOfBoundsDueToEmptyArray(Expr sizeExpr, ArrayCreationExpr arrayCreation) {
|
||||
// Find an `ArrayCreationExpr` for the array used in this indexing operation.
|
||||
exists(VariableAssign assign |
|
||||
assign.getSource() = arrayCreation and
|
||||
defUsePair(assign, this.getArray())
|
||||
) and
|
||||
// If the array access is protected by a conditional that verifies the index is less than the array
|
||||
// length, then the array will never be accessed if the size is zero.
|
||||
not lessthanLength(this) and
|
||||
// Verify that the size expression is never checked to be greater than 0.
|
||||
sizeExpr = arrayCreation.getDimension(0) and
|
||||
not lowerBound(sizeExpr) > 0
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A source of "flow" which has an upper or lower bound.
|
||||
*/
|
||||
abstract class BoundedFlowSource extends DataFlow::Node {
|
||||
/**
|
||||
* Return a lower bound for the input, if possible.
|
||||
*/
|
||||
abstract int lowerBound();
|
||||
|
||||
/**
|
||||
* Return an upper bound for the input, if possible.
|
||||
*/
|
||||
abstract int upperBound();
|
||||
|
||||
/**
|
||||
* Return a description for this flow source, suitable for putting in an alert message.
|
||||
*/
|
||||
abstract string getDescription();
|
||||
}
|
||||
|
||||
/**
|
||||
* Input that is constructed using a random value.
|
||||
*/
|
||||
class RandomValueFlowSource extends BoundedFlowSource {
|
||||
RandomDataSource nextAccess;
|
||||
|
||||
RandomValueFlowSource() { this.asExpr() = nextAccess }
|
||||
|
||||
override int lowerBound() { result = nextAccess.getLowerBound() }
|
||||
|
||||
override int upperBound() { result = nextAccess.getUpperBound() }
|
||||
|
||||
override string getDescription() { result = "Random value" }
|
||||
}
|
||||
|
||||
/**
|
||||
* A compile time constant expression that evaluates to a numeric type.
|
||||
*/
|
||||
class NumericLiteralFlowSource extends BoundedFlowSource {
|
||||
NumericLiteralFlowSource() { exists(this.asExpr().(CompileTimeConstantExpr).getIntValue()) }
|
||||
|
||||
override int lowerBound() { result = this.asExpr().(CompileTimeConstantExpr).getIntValue() }
|
||||
|
||||
override int upperBound() { result = this.asExpr().(CompileTimeConstantExpr).getIntValue() }
|
||||
|
||||
override string getDescription() {
|
||||
result = "Literal value " + this.asExpr().(CompileTimeConstantExpr).getIntValue()
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,65 @@
|
||||
/**
|
||||
* Provides classes and predicates for determining upper and lower bounds on a value determined by bounding checks that
|
||||
* have been made on dominant paths.
|
||||
*/
|
||||
|
||||
import java
|
||||
private import semmle.code.java.controlflow.Guards
|
||||
|
||||
/**
|
||||
* Holds if the given `ComparisonExpr` is thought to be true when `VarAccess` is accessed.
|
||||
*/
|
||||
private predicate conditionHolds(ComparisonExpr ce, VarAccess va) {
|
||||
exists(ConditionBlock cond |
|
||||
cond.getCondition() = ce and
|
||||
cond.controls(va.getBasicBlock(), true)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine an inclusive lower-bound - if possible - for the value accessed by the given `VarAccess`,
|
||||
* based upon the conditionals that hold at the point the variable is accessed.
|
||||
*/
|
||||
int lowerBound(VarAccess va) {
|
||||
exists(ComparisonExpr greaterThanValue |
|
||||
// This condition should hold when the variable is later accessed.
|
||||
conditionHolds(greaterThanValue, va)
|
||||
|
|
||||
greaterThanValue.getGreaterOperand() = va.getVariable().getAnAccess() and
|
||||
if greaterThanValue.isStrict()
|
||||
then
|
||||
// value > i, so value has a lower bound of i + 1
|
||||
result = greaterThanValue.getLesserOperand().(CompileTimeConstantExpr).getIntValue() + 1
|
||||
else
|
||||
// value >= i, so value has a lower bound of i
|
||||
result = greaterThanValue.getLesserOperand().(CompileTimeConstantExpr).getIntValue()
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if the index expression is a `VarAccess`, where the variable has been confirmed to be less
|
||||
* than the length.
|
||||
*/
|
||||
predicate lessthanLength(ArrayAccess a) {
|
||||
exists(ComparisonExpr lessThanLength, VarAccess va |
|
||||
va = a.getIndexExpr() and
|
||||
conditionHolds(lessThanLength, va)
|
||||
|
|
||||
lessThanLength.getGreaterOperand().(FieldAccess).getQualifier() = arrayReference(a) and
|
||||
lessThanLength.getGreaterOperand().(FieldAccess).getField().hasName("length") and
|
||||
lessThanLength.getLesserOperand() = va.getVariable().getAnAccess() and
|
||||
lessThanLength.isStrict()
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Return all other references to the array accessed in the `ArrayAccess`.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
private Expr arrayReference(ArrayAccess arrayAccess) {
|
||||
// Array is stored in a variable.
|
||||
result = arrayAccess.getArray().(VarAccess).getVariable().getAnAccess()
|
||||
or
|
||||
// Array is returned from a method.
|
||||
result.(MethodAccess).getMethod() = arrayAccess.getArray().(MethodAccess).getMethod()
|
||||
}
|
||||
Reference in New Issue
Block a user