Files
codeql/java/ql/src/semmle/code/java/dataflow/FlowSources.qll
2020-06-27 22:56:00 +00:00

306 lines
9.9 KiB
Plaintext

/**
* Provides classes representing various flow sources for taint tracking.
*/
import java
import semmle.code.java.dataflow.DataFlow
import semmle.code.java.dataflow.TaintTracking
import semmle.code.java.dataflow.DefUse
import semmle.code.java.frameworks.Jdbc
import semmle.code.java.frameworks.Networking
import semmle.code.java.frameworks.Properties
import semmle.code.java.frameworks.Rmi
import semmle.code.java.frameworks.Servlets
import semmle.code.java.frameworks.ApacheHttp
import semmle.code.java.frameworks.android.XmlParsing
import semmle.code.java.frameworks.android.WebView
import semmle.code.java.frameworks.JaxWS
import semmle.code.java.frameworks.android.Android
import semmle.code.java.frameworks.android.Intent
import semmle.code.java.frameworks.SpringWeb
import semmle.code.java.frameworks.Guice
import semmle.code.java.frameworks.struts.StrutsActions
import semmle.code.java.frameworks.Thrift
/** A data flow source of remote user input. */
abstract class RemoteFlowSource extends DataFlow::Node {
/** Gets a string that describes the type of this remote flow source. */
abstract string getSourceType();
}
private class RemoteTaintedMethodAccessSource extends RemoteFlowSource {
RemoteTaintedMethodAccessSource() {
this.asExpr().(MethodAccess).getMethod() instanceof RemoteTaintedMethod
}
override string getSourceType() { result = "network data source" }
}
private class RmiMethodParameterSource extends RemoteFlowSource {
RmiMethodParameterSource() {
exists(RemoteCallableMethod method |
method.getAParameter() = this.asParameter() and
(
getType() instanceof PrimitiveType or
getType() instanceof TypeString
)
)
}
override string getSourceType() { result = "RMI method parameter" }
}
private class JaxWsMethodParameterSource extends RemoteFlowSource {
JaxWsMethodParameterSource() {
exists(JaxWsEndpoint endpoint |
endpoint.getARemoteMethod().getAParameter() = this.asParameter()
)
}
override string getSourceType() { result = "Jax WS method parameter" }
}
private class JaxRsMethodParameterSource extends RemoteFlowSource {
JaxRsMethodParameterSource() {
exists(JaxRsResourceClass service |
service.getAnInjectableCallable().getAParameter() = this.asParameter() or
service.getAnInjectableField().getAnAccess() = this.asExpr()
)
}
override string getSourceType() { result = "Jax Rs method parameter" }
}
private predicate variableStep(Expr tracked, VarAccess sink) {
exists(VariableAssign def |
def.getSource() = tracked and
defUsePair(def, sink)
)
}
private class ReverseDnsSource extends RemoteFlowSource {
ReverseDnsSource() {
// Try not to trigger on `localhost`.
exists(MethodAccess m | m = this.asExpr() |
m.getMethod() instanceof ReverseDNSMethod and
not exists(MethodAccess l |
(variableStep(l, m.getQualifier()) or l = m.getQualifier()) and
l.getMethod().getName() = "getLocalHost"
)
)
}
override string getSourceType() { result = "reverse DNS lookup" }
}
private class MessageBodyReaderParameterSource extends RemoteFlowSource {
MessageBodyReaderParameterSource() {
exists(MessageBodyReaderRead m |
m.getParameter(4) = this.asParameter() or
m.getParameter(5) = this.asParameter()
)
}
override string getSourceType() { result = "MessageBodyReader parameter" }
}
private class SpringMultipartFileSource extends RemoteFlowSource {
SpringMultipartFileSource() {
exists(MethodAccess ma, Method m |
ma = this.asExpr() and
m = ma.getMethod() and
m.getDeclaringType().hasQualifiedName("org.springframework.web.multipart", "MultipartFile") and
m.getName().matches("get%")
)
}
override string getSourceType() { result = "Spring MultipartFile getter" }
}
private class SpringServletInputParameterSource extends RemoteFlowSource {
SpringServletInputParameterSource() {
this.asParameter().getAnAnnotation() instanceof SpringServletInputAnnotation
}
override string getSourceType() { result = "Spring servlet input parameter" }
}
private class GuiceRequestParameterSource extends RemoteFlowSource {
GuiceRequestParameterSource() {
exists(GuiceRequestParametersAnnotation a |
a = this.asParameter().getAnAnnotation() or
a = this.asExpr().(FieldRead).getField().getAnAnnotation()
)
}
override string getSourceType() { result = "Guice request parameter" }
}
private class Struts2ActionSupportClassFieldReadSource extends RemoteFlowSource {
Struts2ActionSupportClassFieldReadSource() {
exists(Struts2ActionSupportClass c |
c.getASetterMethod().getField() = this.asExpr().(FieldRead).getField()
)
}
override string getSourceType() { result = "Struts2 ActionSupport field" }
}
private class ThriftIfaceParameterSource extends RemoteFlowSource {
ThriftIfaceParameterSource() {
exists(ThriftIface i | i.getAnImplementingMethod().getAParameter() = this.asParameter())
}
override string getSourceType() { result = "Thrift Iface parameter" }
}
/** Class for `tainted` user input. */
abstract class UserInput extends DataFlow::Node { }
/**
* DEPRECATED: Use `RemoteFlowSource` instead.
*
* Input that may be controlled by a remote user.
*/
deprecated class RemoteUserInput extends UserInput {
RemoteUserInput() { this instanceof RemoteFlowSource }
}
/** A node with input that may be controlled by a local user. */
abstract class LocalUserInput extends UserInput { }
/**
* A node with input from the local environment, such as files, standard in,
* environment variables, and main method parameters.
*/
class EnvInput extends LocalUserInput {
EnvInput() {
// Parameters to a main method.
exists(MainMethod main | this.asParameter() = main.getParameter(0))
or
// Args4j arguments.
exists(Field f | this.asExpr() = f.getAnAccess() |
f.getAnAnnotation().getType().getQualifiedName() = "org.kohsuke.args4j.Argument"
)
or
// Results from various specific methods.
this.asExpr().(MethodAccess).getMethod() instanceof EnvTaintedMethod
or
// Access to `System.in`.
exists(Field f | this.asExpr() = f.getAnAccess() | f instanceof SystemIn)
or
// Access to files.
this
.asExpr()
.(ConstructorCall)
.getConstructedType()
.hasQualifiedName("java.io", "FileInputStream")
}
}
/** A node with input from a database. */
class DatabaseInput extends LocalUserInput {
DatabaseInput() { this.asExpr().(MethodAccess).getMethod() instanceof ResultSetGetStringMethod }
}
private class RemoteTaintedMethod extends Method {
RemoteTaintedMethod() {
this instanceof ServletRequestGetParameterMethod or
this instanceof ServletRequestGetParameterMapMethod or
this instanceof ServletRequestGetParameterNamesMethod or
this instanceof HttpServletRequestGetQueryStringMethod or
this instanceof HttpServletRequestGetHeaderMethod or
this instanceof HttpServletRequestGetPathMethod or
this instanceof HttpServletRequestGetHeadersMethod or
this instanceof HttpServletRequestGetHeaderNamesMethod or
this instanceof HttpServletRequestGetRequestURIMethod or
this instanceof HttpServletRequestGetRequestURLMethod or
this instanceof HttpServletRequestGetRemoteUserMethod or
this instanceof ServletRequestGetBodyMethod or
this instanceof CookieGetValueMethod or
this instanceof CookieGetNameMethod or
this instanceof CookieGetCommentMethod or
this instanceof URLConnectionGetInputStreamMethod or
this instanceof SocketGetInputStreamMethod or
this instanceof ApacheHttpGetParams or
this instanceof ApacheHttpEntityGetContent or
// In the setting of Android we assume that XML has been transmitted over
// the network, so may be tainted.
this instanceof XmlPullGetMethod or
this instanceof XmlAttrSetGetMethod or
// The current URL in a browser may be untrusted or uncontrolled.
this instanceof WebViewGetUrlMethod
}
}
private class EnvTaintedMethod extends Method {
EnvTaintedMethod() {
this instanceof MethodSystemGetenv or
this instanceof PropertiesGetPropertyMethod or
this instanceof MethodSystemGetProperty
}
}
/** The type `java.net.InetAddress`. */
class TypeInetAddr extends RefType {
TypeInetAddr() { this.getQualifiedName() = "java.net.InetAddress" }
}
/** A reverse DNS method. */
class ReverseDNSMethod extends Method {
ReverseDNSMethod() {
this.getDeclaringType() instanceof TypeInetAddr and
(
this.getName() = "getHostName" or
this.getName() = "getCanonicalHostName"
)
}
}
/** Android `Intent` that may have come from a hostile application. */
class AndroidIntentInput extends DataFlow::Node {
AndroidIntentInput() {
exists(MethodAccess ma, AndroidGetIntentMethod m |
ma.getMethod().overrides*(m) and
this.asExpr() = ma
)
or
exists(Method m, AndroidReceiveIntentMethod rI |
m.overrides*(rI) and
this.asParameter() = m.getParameter(1)
)
}
}
/** Method access to external inputs of `android.content.Intent` object. */
class IntentGetExtraMethodAccess extends MethodAccess {
IntentGetExtraMethodAccess() {
exists(AndroidComponent ac |
this.getEnclosingCallable().getDeclaringType() = ac and ac.isExported()
) and
(
this.getMethod().getName().regexpMatch("get\\w+Extra") and
this.getMethod().getDeclaringType() instanceof TypeIntent
or
this.getMethod().getName().regexpMatch("get\\w+") and
this.getQualifier().(MethodAccess).getMethod().hasName("getExtras") and
this.getQualifier().(MethodAccess).getMethod().getDeclaringType() instanceof TypeIntent
)
}
}
/** Android intent extra source. */
private class AndroidIntentExtraSource extends RemoteFlowSource {
AndroidIntentExtraSource() {
exists(MethodAccess ma |
ma instanceof IntentGetExtraMethodAccess and
(
this.asExpr().(VarAccess).getVariable().getAnAssignedValue() = ma or
ma.getQualifier() = this.asExpr()
)
)
}
override string getSourceType() { result = "Android intent extra" }
}