Merge branch 'main' into starcke/automodel-pack

This commit is contained in:
Anders Starcke Henriksen
2023-08-30 09:25:28 +02:00
1326 changed files with 56700 additions and 39820 deletions

View File

@@ -33,6 +33,7 @@ class Member extends Element, Annotatable, Modifiable, @member {
* Holds if this member has the specified name and is declared in the
* specified package and type.
*/
pragma[nomagic]
predicate hasQualifiedName(string package, string type, string name) {
this.getDeclaringType().hasQualifiedName(package, type) and this.hasName(name)
}

View File

@@ -345,6 +345,29 @@ private predicate elementSpec(
neutralModel(package, type, name, signature, _, _) and ext = "" and subtypes = false
}
private string getNestedName(Type t) {
not t instanceof RefType and result = t.toString()
or
not t.(Array).getElementType() instanceof NestedType and result = t.(RefType).nestedName()
or
result =
t.(Array).getElementType().(NestedType).getEnclosingType().nestedName() + "$" + t.getName()
}
private string getQualifiedName(Type t) {
not t instanceof RefType and result = t.toString()
or
result = t.(RefType).getQualifiedName()
or
exists(Array a, Type c | a = t and c = a.getElementType() |
not c instanceof RefType and result = t.toString()
or
exists(string pkgName | pkgName = c.(RefType).getPackage().getName() |
if pkgName = "" then result = getNestedName(a) else result = pkgName + "." + getNestedName(a)
)
)
}
/**
* Gets a parenthesized string containing all parameter types of this callable, separated by a comma.
*
@@ -354,31 +377,39 @@ private predicate elementSpec(
cached
string paramsString(Callable c) {
result =
"(" + concat(int i | | c.getParameterType(i).getErasure().toString(), "," order by i) + ")"
"(" + concat(int i | | getNestedName(c.getParameterType(i).getErasure()), "," order by i) + ")"
}
private string paramsStringQualified(Callable c) {
result =
"(" + concat(int i | | getQualifiedName(c.getParameterType(i).getErasure()), "," order by i) +
")"
}
private Element interpretElement0(
string package, string type, boolean subtypes, string name, string signature
) {
elementSpec(package, type, subtypes, name, signature, _) and
exists(RefType t | t.hasQualifiedName(package, type) |
(
exists(Member m |
(
result = m
or
subtypes = true and result.(SrcMethod).overridesOrInstantiates+(m)
) and
m.getDeclaringType() = t and
m.hasName(name)
m.hasQualifiedName(package, type, name)
|
signature = "" or
m.(Callable).getSignature() = any(string nameprefix) + signature or
paramsStringQualified(m) = signature or
paramsString(m) = signature
)
or
(if subtypes = true then result.(SrcRefType).getASourceSupertype*() = t else result = t) and
name = "" and
signature = ""
exists(RefType t |
t.hasQualifiedName(package, type) and
(if subtypes = true then result.(SrcRefType).getASourceSupertype*() = t else result = t) and
name = "" and
signature = ""
)
)
}

View File

@@ -175,8 +175,6 @@ class Provenance = Impl::Public::Provenance;
class SummarizedCallable = Impl::Public::SummarizedCallable;
class NeutralCallable = Impl::Public::NeutralCallable;
/**
* An adapter class to add the flow summaries specified on `SyntheticCallable`
* to `SummarizedCallable`.

View File

@@ -257,11 +257,22 @@ private Guard boundFlowCond(SsaVariable v, Expr e, int delta, boolean upper, boo
or
// guard that tests whether `v2` is bounded by `e + delta + d1 - d2` and
// exists a guard `guardEq` such that `v = v2 - d1 + d2`.
exists(SsaVariable v2, Guard guardEq, boolean eqIsTrue, int d1, int d2 |
guardEq = eqFlowCond(v, ssaRead(v2, d1), d2, true, eqIsTrue) and
result = boundFlowCond(v2, e, delta + d1 - d2, upper, testIsTrue) and
// guardEq needs to control guard
guardEq.directlyControls(result.getBasicBlock(), eqIsTrue)
exists(SsaVariable v2, int d |
// equality needs to control guard
result.getBasicBlock() = eqSsaCondDirectlyControls(v, v2, d) and
result = boundFlowCond(v2, e, delta - d, upper, testIsTrue)
)
}
/**
* Gets a basic block in which `v1` equals `v2 + delta`.
*/
pragma[nomagic]
private BasicBlock eqSsaCondDirectlyControls(SsaVariable v1, SsaVariable v2, int delta) {
exists(Guard guardEq, int d1, int d2, boolean eqIsTrue |
guardEq = eqFlowCond(v1, ssaRead(v2, d1), d2, true, eqIsTrue) and
delta = d2 - d1 and
guardEq.directlyControls(result, eqIsTrue)
)
}

View File

@@ -8,6 +8,10 @@ import semmle.code.java.dataflow.DataFlow2
import semmle.code.java.dataflow.internal.TaintTrackingUtil::StringBuilderVarModule
module TaintTracking {
import semmle.code.java.dataflow.internal.tainttracking1.TaintTracking
import semmle.code.java.dataflow.internal.tainttracking1.TaintTrackingParameter::Public
private import semmle.code.java.dataflow.internal.DataFlowImplSpecific
private import semmle.code.java.dataflow.internal.TaintTrackingImplSpecific
private import codeql.dataflow.TaintTracking
import TaintFlowMake<JavaDataFlow, JavaTaintTracking>
import semmle.code.java.dataflow.internal.tainttracking1.TaintTrackingImpl
}

View File

@@ -2,16 +2,16 @@ private import java
private import DataFlowPrivate
private import DataFlowUtil
private import semmle.code.java.dataflow.InstanceAccess
private import semmle.code.java.dataflow.FlowSummary
private import semmle.code.java.dataflow.internal.FlowSummaryImpl as Impl
private import semmle.code.java.dispatch.VirtualDispatch as VirtualDispatch
private import semmle.code.java.dataflow.TypeFlow
private import semmle.code.java.dispatch.internal.Unification
private module DispatchImpl {
private predicate hasHighConfidenceTarget(Call c) {
exists(SummarizedCallable sc | sc.getACall() = c and not sc.applyGeneratedModel())
exists(Impl::Public::SummarizedCallable sc | sc.getACall() = c and not sc.applyGeneratedModel())
or
exists(NeutralCallable nc | nc.getACall() = c and nc.hasManualModel())
exists(Impl::Public::NeutralSummaryCallable nc | nc.getACall() = c and nc.hasManualModel())
or
exists(Callable srcTgt |
srcTgt = VirtualDispatch::viableCallable(c) and

View File

@@ -296,11 +296,21 @@ module Public {
predicate hasProvenance(Provenance provenance) { provenance = "manual" }
}
/** A callable where there is no flow via the callable. */
class NeutralCallable extends SummarizedCallableBase {
/**
* A callable where there is no flow via the callable.
*/
class NeutralSummaryCallable extends NeutralCallable {
NeutralSummaryCallable() { this.getKind() = "summary" }
}
/**
* A callable that has a neutral model.
*/
class NeutralCallable extends NeutralCallableBase {
private string kind;
private Provenance provenance;
NeutralCallable() { neutralSummaryElement(this, provenance) }
NeutralCallable() { neutralElement(this, kind, provenance) }
/**
* Holds if the neutral is auto generated.
@@ -316,6 +326,11 @@ module Public {
* Holds if the neutral has provenance `p`.
*/
predicate hasProvenance(Provenance p) { p = provenance }
/**
* Gets the kind of the neutral.
*/
string getKind() { result = kind }
}
}
@@ -1318,6 +1333,11 @@ module Private {
/** Gets the string representation of this callable used by `neutral/1`. */
abstract string getCallableCsv();
/**
* Gets the kind of the neutral.
*/
string getKind() { result = super.getKind() }
string toString() { result = super.toString() }
}
@@ -1358,12 +1378,13 @@ module Private {
/**
* Holds if a neutral model `csv` exists (semi-colon separated format). Used for testing purposes.
* The syntax is: "namespace;type;name;signature;provenance"",
* The syntax is: "namespace;type;name;signature;kind;provenance"",
*/
query predicate neutral(string csv) {
exists(RelevantNeutralCallable c |
csv =
c.getCallableCsv() // Callable information
+ c.getKind() + ";" // kind
+ renderProvenanceNeutral(c) // provenance
)
}

View File

@@ -14,6 +14,16 @@ private import semmle.code.java.dataflow.internal.AccessPathSyntax as AccessPath
class SummarizedCallableBase = FlowSummary::SummarizedCallableBase;
/**
* A class of callables that are candidates for neutral modeling.
*/
class NeutralCallableBase extends Callable {
NeutralCallableBase() { this.isSourceDeclaration() }
/** Gets a call that targets this neutral. */
Call getACall() { result.getCallee().getSourceDeclaration() = this }
}
/**
* A module for importing frameworks that define synthetic globals.
*/
@@ -156,13 +166,13 @@ predicate summaryElement(
}
/**
* Holds if a neutral summary model exists for `c` with provenance `provenance`,
* which means that there is no flow through `c`.
* Holds if a neutral model exists for `c` of kind `kind`
* and with provenance `provenance`.
*/
predicate neutralSummaryElement(SummarizedCallableBase c, string provenance) {
predicate neutralElement(NeutralCallableBase c, string kind, string provenance) {
exists(string namespace, string type, string name, string signature |
neutralModel(namespace, type, name, signature, "summary", provenance) and
c.asCallable() = interpretElement(namespace, type, false, name, signature, "")
neutralModel(namespace, type, name, signature, kind, provenance) and
c = interpretElement(namespace, type, false, name, signature, "")
)
}

View File

@@ -0,0 +1,10 @@
/**
* Provides Java-specific definitions for use in the taint tracking library.
*/
private import codeql.dataflow.TaintTracking
private import DataFlowImplSpecific
module JavaTaintTracking implements InputSig<JavaDataFlow> {
import TaintTrackingUtil
}

View File

@@ -177,7 +177,7 @@ private RefType getElementType(RefType container) {
* of `c` at sinks and inputs to additional taint steps.
*/
bindingset[node]
predicate defaultImplicitTaintRead(DataFlow::Node node, DataFlow::Content c) {
predicate defaultImplicitTaintRead(DataFlow::Node node, DataFlow::ContentSet c) {
exists(RefType container |
(node.asExpr() instanceof Argument or node instanceof ArgumentNode) and
getElementType*(node.getType()) = container

View File

@@ -1,75 +0,0 @@
/**
* Provides classes for performing local (intra-procedural) and
* global (inter-procedural) taint-tracking analyses.
*/
import TaintTrackingParameter::Public
private import TaintTrackingParameter::Private
private module AddTaintDefaults<DataFlowInternal::FullStateConfigSig Config> implements
DataFlowInternal::FullStateConfigSig
{
import Config
predicate isBarrier(DataFlow::Node node) {
Config::isBarrier(node) or defaultTaintSanitizer(node)
}
predicate isAdditionalFlowStep(DataFlow::Node node1, DataFlow::Node node2) {
Config::isAdditionalFlowStep(node1, node2) or
defaultAdditionalTaintStep(node1, node2)
}
predicate allowImplicitRead(DataFlow::Node node, DataFlow::ContentSet c) {
Config::allowImplicitRead(node, c)
or
(
Config::isSink(node) or
Config::isSink(node, _) or
Config::isAdditionalFlowStep(node, _) or
Config::isAdditionalFlowStep(node, _, _, _)
) and
defaultImplicitTaintRead(node, c)
}
}
/**
* Constructs a global taint tracking computation.
*/
module Global<DataFlow::ConfigSig Config> implements DataFlow::GlobalFlowSig {
private module Config0 implements DataFlowInternal::FullStateConfigSig {
import DataFlowInternal::DefaultState<Config>
import Config
}
private module C implements DataFlowInternal::FullStateConfigSig {
import AddTaintDefaults<Config0>
}
import DataFlowInternal::Impl<C>
}
/** DEPRECATED: Use `Global` instead. */
deprecated module Make<DataFlow::ConfigSig Config> implements DataFlow::GlobalFlowSig {
import Global<Config>
}
/**
* Constructs a global taint tracking computation using flow state.
*/
module GlobalWithState<DataFlow::StateConfigSig Config> implements DataFlow::GlobalFlowSig {
private module Config0 implements DataFlowInternal::FullStateConfigSig {
import Config
}
private module C implements DataFlowInternal::FullStateConfigSig {
import AddTaintDefaults<Config0>
}
import DataFlowInternal::Impl<C>
}
/** DEPRECATED: Use `GlobalWithState` instead. */
deprecated module MakeWithState<DataFlow::StateConfigSig Config> implements DataFlow::GlobalFlowSig {
import GlobalWithState<Config>
}

View File

@@ -4,6 +4,8 @@
*/
import java
private import semmle.code.java.frameworks.Networking
private import semmle.code.java.frameworks.Rmi
private import semmle.code.java.security.XSS
/**
@@ -23,16 +25,112 @@ string getAJaxRsPackage(string subpackage) { result = getAJaxRsPackage() + "." +
*/
class JaxWsEndpoint extends Class {
JaxWsEndpoint() {
exists(AnnotationType a | a = this.getAnAnnotation().getType() |
exists(AnnotationType a | a = this.getAnAncestor().getAnAnnotation().getType() |
a.hasName(["WebService", "WebServiceProvider", "WebServiceClient"])
)
}
/** Gets a method annotated with `@WebMethod` or `@WebEndpoint`. */
Callable getARemoteMethod() {
/**
* Gets a method of this class that is not an excluded `@WebMethod`,
* and the parameters and return value of which are either of an acceptable type,
* or are annotated with `@XmlJavaTypeAdapter`.
*/
Method getARemoteMethod() {
result = this.getACallable() and
exists(AnnotationType a | a = result.getAnAnnotation().getType() |
a.hasName(["WebMethod", "WebEndpoint"])
result.isPublic() and
not result instanceof InitializerMethod and
not exists(Annotation a | a = result.getAnAnnotation() |
a.getType().hasQualifiedName(["javax", "jakarta"] + ".jws", "WebMethod") and
a.getValue("exclude").(BooleanLiteral).getBooleanValue() = true
) and
forex(ParamOrReturn paramOrRet | paramOrRet = result.getAParameter() or paramOrRet = result |
exists(Type t | t = paramOrRet.getType() |
t instanceof JaxAcceptableType
or
t.(Annotatable).getAnAnnotation().getType() instanceof XmlJavaTypeAdapter
or
t instanceof VoidType
)
or
paramOrRet.getInheritedAnnotation().getType() instanceof XmlJavaTypeAdapter
)
}
}
/** The annotation type `@XmlJavaTypeAdapter`. */
class XmlJavaTypeAdapter extends AnnotationType {
XmlJavaTypeAdapter() {
this.hasQualifiedName(["javax", "jakarta"] + ".xml.bind.annotation.adapters",
"XmlJavaTypeAdapter")
}
}
private class ParamOrReturn extends Annotatable {
ParamOrReturn() { this instanceof Parameter or this instanceof Method }
Type getType() {
result = this.(Parameter).getType()
or
result = this.(Method).getReturnType()
}
Annotation getInheritedAnnotation() {
result = this.getAnAnnotation()
or
result = this.(Method).getAnOverride*().getAnAnnotation()
or
result =
this.(Parameter)
.getCallable()
.(Method)
.getAnOverride*()
.getParameter(this.(Parameter).getPosition())
.getAnAnnotation()
}
}
// JAX-RPC 1.1, section 5
private class JaxAcceptableType extends Type {
JaxAcceptableType() {
// JAX-RPC 1.1, section 5.1.1
this instanceof PrimitiveType
or
// JAX-RPC 1.1, section 5.1.2
this.(Array).getElementType() instanceof JaxAcceptableType
or
// JAX-RPC 1.1, section 5.1.3
this instanceof JaxAcceptableStandardClass
or
// JAX-RPC 1.1, section 5.1.4
this instanceof JaxValueType
}
}
private class JaxAcceptableStandardClass extends RefType {
JaxAcceptableStandardClass() {
this instanceof TypeString or
this.hasQualifiedName("java.util", "Date") or
this.hasQualifiedName("java.util", "Calendar") or
this.hasQualifiedName("java.math", "BigInteger") or
this.hasQualifiedName("java.math", "BigDecimal") or
this.hasQualifiedName("javax.xml.namespace", "QName") or
this instanceof TypeUri
}
}
// JAX-RPC 1.1, section 5.4
private class JaxValueType extends RefType {
JaxValueType() {
not this instanceof Wildcard and
// Mutually exclusive with other `JaxAcceptableType`s
not this instanceof Array and
not this instanceof JaxAcceptableStandardClass and
not this.getPackage().getName().matches("java.%") and
// Must not implement (directly or indirectly) the java.rmi.Remote interface.
not this.getAnAncestor() instanceof TypeRemote and
// The Java type of a public field must be a supported JAX-RPC type as specified in the section 5.1.
forall(Field f | this.getAMember() = f and f.isPublic() |
f.getType() instanceof JaxAcceptableType
)
}
}

View File

@@ -397,3 +397,8 @@ class GetServletResourceAsStreamMethod extends Method {
this.hasName("getResourceAsStream")
}
}
/** The interface `javax.servlet.http.HttpSession` */
class HttpServletSession extends RefType {
HttpServletSession() { this.hasQualifiedName("javax.servlet.http", "HttpSession") }
}

View File

@@ -0,0 +1,22 @@
/** Provides definitions related to XML parsing in Model-Driven Health Tools. */
import java
private import semmle.code.java.security.XmlParsers
/** A call to `CDAUtil.load` or `CDAUtil.loadAs`. */
private class CdaUtilLoad extends XmlParserCall {
CdaUtilLoad() {
this.getMethod()
.hasQualifiedName("org.openhealthtools.mdht.uml.cda.util", "CDAUtil", ["load", "loadAs"])
}
override Expr getSink() {
result = this.getAnArgument() and
exists(RefType t | result.getType().(RefType).getASourceSupertype*() = t |
t instanceof TypeInputStream or
t instanceof InputSource
)
}
override predicate isSafe() { none() }
}

View File

@@ -0,0 +1,40 @@
/** Classes and predicates for reasoning about the `owasp.easpi` package. */
import java
/**
* The `org.owasp.esapi.Validator` interface.
*/
class EsapiValidator extends RefType {
EsapiValidator() { this.hasQualifiedName("org.owasp.esapi", "Validator") }
}
/**
* The methods of `org.owasp.esapi.Validator` which validate data.
*/
class EsapiIsValidMethod extends Method {
EsapiIsValidMethod() {
this.getDeclaringType() instanceof EsapiValidator and
this.hasName([
"isValidCreditCard", "isValidDate", "isValidDirectoryPath", "isValidDouble",
"isValidFileContent", "isValidFileName", "isValidInput", "isValidInteger",
"isValidListItem", "isValidNumber", "isValidPrintable", "isValidRedirectLocation",
"isValidSafeHTML", "isValidURI"
])
}
}
/**
* The methods of `org.owasp.esapi.Validator` which return validated data.
*/
class EsapiGetValidMethod extends Method {
EsapiGetValidMethod() {
this.getDeclaringType() instanceof EsapiValidator and
this.hasName([
"getValidCreditCard", "getValidDate", "getValidDirectoryPath", "getValidDouble",
"getValidFileContent", "getValidFileName", "getValidInput", "getValidInteger",
"getValidListItem", "getValidNumber", "getValidPrintable", "getValidRedirectLocation",
"getValidSafeHTML", "getValidURI"
])
}
}

View File

@@ -148,6 +148,8 @@ private module RegexFlowConfig implements DataFlow::ConfigSig {
predicate isBarrier(DataFlow::Node node) {
node.getEnclosingCallable().getDeclaringType() instanceof NonSecurityTestClass
}
int fieldFlowBranchLimit() { result = 1 }
}
private module RegexFlow = DataFlow::Global<RegexFlowConfig>;

View File

@@ -42,6 +42,8 @@ private class DefaultCommandInjectionSanitizer extends CommandInjectionSanitizer
or
this.getType() instanceof BoxedType
or
this.getType() instanceof NumberType
or
isSafeCommandArgument(this.asExpr())
}
}

View File

@@ -270,7 +270,7 @@ string getInsecureAlgorithmRegex() {
string getASecureAlgorithmName() {
result =
[
"RSA", "SHA256", "SHA512", "CCM", "GCM", "AES(?![^a-zA-Z](ECB|CBC/PKCS[57]Padding))",
"RSA", "SHA-?256", "SHA-?512", "CCM", "GCM", "AES(?![^a-zA-Z](ECB|CBC/PKCS[57]Padding))",
"Blowfish", "ECIES"
]
}

View File

@@ -0,0 +1,70 @@
/** Provides classes and predicates to reason about trust boundary violations */
import java
private import semmle.code.java.dataflow.DataFlow
private import semmle.code.java.controlflow.Guards
private import semmle.code.java.dataflow.ExternalFlow
private import semmle.code.java.dataflow.FlowSources
private import semmle.code.java.frameworks.owasp.Esapi
/**
* A source of data that crosses a trust boundary.
*/
abstract class TrustBoundaryViolationSource extends DataFlow::Node { }
private class RemoteSource extends TrustBoundaryViolationSource instanceof RemoteFlowSource { }
/**
* A sink for data that crosses a trust boundary.
*/
class TrustBoundaryViolationSink extends DataFlow::Node {
TrustBoundaryViolationSink() { sinkNode(this, "trust-boundary-violation") }
}
/**
* A sanitizer for data that crosses a trust boundary.
*/
abstract class TrustBoundaryValidationSanitizer extends DataFlow::Node { }
/**
* A node validated by an OWASP ESAPI validation method.
*/
private class EsapiValidatedInputSanitizer extends TrustBoundaryValidationSanitizer {
EsapiValidatedInputSanitizer() {
this = DataFlow::BarrierGuard<esapiIsValidData/3>::getABarrierNode() or
this.asExpr().(MethodAccess).getMethod() instanceof EsapiGetValidMethod
}
}
/**
* Holds if `g` is a guard that checks that `e` is valid data according to an OWASP ESAPI validation method.
*/
private predicate esapiIsValidData(Guard g, Expr e, boolean branch) {
branch = true and
exists(MethodAccess ma | ma.getMethod() instanceof EsapiIsValidMethod |
g = ma and
e = ma.getArgument(1)
)
}
/**
* Taint tracking for data that crosses a trust boundary.
*/
module TrustBoundaryConfig implements DataFlow::ConfigSig {
predicate isSource(DataFlow::Node source) { source instanceof TrustBoundaryViolationSource }
predicate isBarrier(DataFlow::Node node) {
node instanceof TrustBoundaryValidationSanitizer or
node.getType() instanceof HttpServletSession or
node.getType() instanceof NumberType or
node.getType() instanceof PrimitiveType or
node.getType() instanceof BoxedType
}
predicate isSink(DataFlow::Node sink) { sink instanceof TrustBoundaryViolationSink }
}
/**
* Taint-tracking flow for values which cross a trust boundary.
*/
module TrustBoundaryFlow = TaintTracking::Global<TrustBoundaryConfig>;

View File

@@ -9,6 +9,7 @@ private module Frameworks {
private import semmle.code.java.frameworks.apache.CommonsXml
private import semmle.code.java.frameworks.javaee.Xml
private import semmle.code.java.frameworks.javase.Beans
private import semmle.code.java.frameworks.mdht.MdhtXml
private import semmle.code.java.frameworks.rundeck.RundeckXml
}