mirror of
https://github.com/github/codeql.git
synced 2026-04-30 03:05:15 +02:00
Merge branch 'main' into atorralba/promote-ognl-injection
This commit is contained in:
@@ -7,6 +7,7 @@
|
||||
* Such operations could interfere with the EJB container's operation.
|
||||
* @kind problem
|
||||
* @problem.severity error
|
||||
* @security-severity 4.9
|
||||
* @precision low
|
||||
* @id java/ejb/container-interference
|
||||
* @tags reliability
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
* for enterprise components.
|
||||
* @kind problem
|
||||
* @problem.severity error
|
||||
* @security-severity 4.9
|
||||
* @precision low
|
||||
* @id java/ejb/file-io
|
||||
* @tags reliability
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
* Such use could compromise security and system stability.
|
||||
* @kind problem
|
||||
* @problem.severity error
|
||||
* @security-severity 4.9
|
||||
* @precision low
|
||||
* @id java/ejb/native-code
|
||||
* @tags reliability
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
* as this could compromise security.
|
||||
* @kind problem
|
||||
* @problem.severity error
|
||||
* @security-severity 4.9
|
||||
* @precision low
|
||||
* @id java/ejb/reflection
|
||||
* @tags external/cwe/cwe-573
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
* This functionality is reserved for the EJB container for security reasons.
|
||||
* @kind problem
|
||||
* @problem.severity error
|
||||
* @security-severity 4.9
|
||||
* @precision low
|
||||
* @id java/ejb/security-configuration-access
|
||||
* @tags external/cwe/cwe-573
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
* the Java serialization protocol, since their use could compromise security.
|
||||
* @kind problem
|
||||
* @problem.severity error
|
||||
* @security-severity 4.9
|
||||
* @precision low
|
||||
* @id java/ejb/substitution-in-serialization
|
||||
* @tags external/cwe/cwe-573
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
* compromise security or interfere with the EJB container's operation.
|
||||
* @kind problem
|
||||
* @problem.severity error
|
||||
* @security-severity 4.9
|
||||
* @precision low
|
||||
* @id java/ejb/socket-or-stream-handler-factory
|
||||
* @tags reliability
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
* numeric errors such as overflows.
|
||||
* @kind problem
|
||||
* @problem.severity warning
|
||||
* @security-severity 5.9
|
||||
* @precision very-high
|
||||
* @id java/implicit-cast-in-compound-assignment
|
||||
* @tags reliability
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
* guarantee an evenly distributed sequence of random numbers.
|
||||
* @kind problem
|
||||
* @problem.severity warning
|
||||
* @security-severity 5.9
|
||||
* @precision medium
|
||||
* @id java/random-used-once
|
||||
* @tags reliability
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
* may cause a deadlock.
|
||||
* @kind problem
|
||||
* @problem.severity error
|
||||
* @security-severity 6.9
|
||||
* @precision medium
|
||||
* @id java/unreleased-lock
|
||||
* @tags reliability
|
||||
|
||||
@@ -14,7 +14,7 @@ but not closed may cause a resource leak.
|
||||
|
||||
<p>Ensure that the resource is always closed to avoid a resource leak. Note that, because of exceptions,
|
||||
it is safest to close a resource in a <code>finally</code> block. (However, this is unnecessary for
|
||||
subclasses of <code>StringReader</code> and <code>ByteArrayInputStream</code>.)
|
||||
subclasses of <code>CharArrayReader</code>, <code>StringReader</code> and <code>ByteArrayInputStream</code>.)
|
||||
</p>
|
||||
|
||||
<p>For Java 7 or later, the recommended way to close resources that implement <code>java.lang.AutoCloseable</code>
|
||||
|
||||
@@ -17,16 +17,16 @@ import CloseType
|
||||
|
||||
predicate readerType(RefType t) {
|
||||
exists(RefType sup | sup = t.getASupertype*() |
|
||||
sup.hasName("Reader") or
|
||||
sup.hasName("InputStream") or
|
||||
sup.hasQualifiedName("java.io", ["Reader", "InputStream"]) or
|
||||
sup.hasQualifiedName("java.util.zip", "ZipFile")
|
||||
)
|
||||
}
|
||||
|
||||
predicate safeReaderType(RefType t) {
|
||||
exists(RefType sup | sup = t.getASupertype*() |
|
||||
sup.hasName("StringReader") or
|
||||
sup.hasName("ByteArrayInputStream") or
|
||||
sup.hasQualifiedName("java.io", ["CharArrayReader", "StringReader", "ByteArrayInputStream"])
|
||||
or
|
||||
// Note: It is unclear which specific class this is supposed to match
|
||||
sup.hasName("StringInputStream")
|
||||
)
|
||||
}
|
||||
|
||||
@@ -14,7 +14,7 @@ but not properly closed later may cause a resource leak.
|
||||
|
||||
<p>Ensure that the resource is always closed to avoid a resource leak. Note that, because of exceptions,
|
||||
it is safest to close a resource properly in a <code>finally</code> block. (However, this is unnecessary for
|
||||
subclasses of <code>StringWriter</code> and <code>ByteArrayOutputStream</code>.)</p>
|
||||
subclasses of <code>CharArrayWriter</code>, <code>StringWriter</code> and <code>ByteArrayOutputStream</code>.)</p>
|
||||
|
||||
<p>For Java 7 or later, the recommended way to close resources that implement <code>java.lang.AutoCloseable</code>
|
||||
is to declare them within a <code>try-with-resources</code> statement, so that they are closed implicitly.</p>
|
||||
|
||||
@@ -17,15 +17,13 @@ import CloseType
|
||||
|
||||
predicate writerType(RefType t) {
|
||||
exists(RefType sup | sup = t.getASupertype*() |
|
||||
sup.hasName("Writer") or
|
||||
sup.hasName("OutputStream")
|
||||
sup.hasQualifiedName("java.io", ["Writer", "OutputStream"])
|
||||
)
|
||||
}
|
||||
|
||||
predicate safeWriterType(RefType t) {
|
||||
exists(RefType sup | sup = t.getASupertype*() |
|
||||
sup.hasName("StringWriter") or
|
||||
sup.hasName("ByteArrayOutputStream")
|
||||
sup.hasQualifiedName("java.io", ["CharArrayWriter", "StringWriter", "ByteArrayOutputStream"])
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
* @kind path-problem
|
||||
* @precision low
|
||||
* @problem.severity error
|
||||
* @security-severity 5.9
|
||||
* @tags security external/cwe/cwe-20
|
||||
*/
|
||||
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
* @description Accessing paths influenced by users can allow an attacker to access unexpected resources.
|
||||
* @kind path-problem
|
||||
* @problem.severity error
|
||||
* @security-severity 6.4
|
||||
* @precision high
|
||||
* @id java/path-injection
|
||||
* @tags security
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
* @description Accessing paths influenced by users can allow an attacker to access unexpected resources.
|
||||
* @kind path-problem
|
||||
* @problem.severity recommendation
|
||||
* @security-severity 6.4
|
||||
* @precision medium
|
||||
* @id java/path-injection-local
|
||||
* @tags security
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
* @kind path-problem
|
||||
* @id java/zipslip
|
||||
* @problem.severity error
|
||||
* @security-severity 6.4
|
||||
* @precision high
|
||||
* @tags security
|
||||
* external/cwe/cwe-022
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
* malicious changes in the PATH environment variable.
|
||||
* @kind problem
|
||||
* @problem.severity warning
|
||||
* @security-severity 5.9
|
||||
* @precision medium
|
||||
* @id java/relative-path-command
|
||||
* @tags security
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
* changes in the strings.
|
||||
* @kind path-problem
|
||||
* @problem.severity error
|
||||
* @security-severity 5.9
|
||||
* @precision high
|
||||
* @id java/command-line-injection
|
||||
* @tags security
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
* changes in the strings.
|
||||
* @kind path-problem
|
||||
* @problem.severity recommendation
|
||||
* @security-severity 5.9
|
||||
* @precision medium
|
||||
* @id java/command-line-injection-local
|
||||
* @tags security
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
* insertion of special characters in the strings.
|
||||
* @kind problem
|
||||
* @problem.severity error
|
||||
* @security-severity 5.9
|
||||
* @precision high
|
||||
* @id java/concatenated-command-line
|
||||
* @tags security
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
* allows for a cross-site scripting vulnerability.
|
||||
* @kind path-problem
|
||||
* @problem.severity error
|
||||
* @security-severity 2.9
|
||||
* @precision high
|
||||
* @id java/xss
|
||||
* @tags security
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
* allows for a cross-site scripting vulnerability.
|
||||
* @kind path-problem
|
||||
* @problem.severity recommendation
|
||||
* @security-severity 2.9
|
||||
* @precision medium
|
||||
* @id java/xss-local
|
||||
* @tags security
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
* malicious code by the user.
|
||||
* @kind path-problem
|
||||
* @problem.severity error
|
||||
* @security-severity 6.4
|
||||
* @precision high
|
||||
* @id java/sql-injection
|
||||
* @tags security
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
* malicious code by the user.
|
||||
* @kind path-problem
|
||||
* @problem.severity recommendation
|
||||
* @security-severity 6.4
|
||||
* @precision medium
|
||||
* @id java/sql-injection-local
|
||||
* @tags security
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
* characters is vulnerable to insertion of malicious code.
|
||||
* @kind problem
|
||||
* @problem.severity error
|
||||
* @security-severity 6.4
|
||||
* @precision high
|
||||
* @id java/concatenated-sql-query
|
||||
* @tags security
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
* malicious LDAP code by the user.
|
||||
* @kind path-problem
|
||||
* @problem.severity error
|
||||
* @security-severity 5.9
|
||||
* @precision high
|
||||
* @id java/ldap-injection
|
||||
* @tags security
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
* @description User-controlled data may be evaluated as a Java EL expression, leading to arbitrary code execution.
|
||||
* @kind path-problem
|
||||
* @problem.severity error
|
||||
* @security-severity 10.0
|
||||
* @precision high
|
||||
* @id java/insecure-bean-validation
|
||||
* @tags security
|
||||
|
||||
38
java/ql/src/Security/CWE/CWE-094/JexlInjection.ql
Normal file
38
java/ql/src/Security/CWE/CWE-094/JexlInjection.ql
Normal file
@@ -0,0 +1,38 @@
|
||||
/**
|
||||
* @name Expression language injection (JEXL)
|
||||
* @description Evaluation of a user-controlled JEXL expression
|
||||
* may lead to arbitrary code execution.
|
||||
* @kind path-problem
|
||||
* @problem.severity error
|
||||
* @security-severity 10.0
|
||||
* @precision high
|
||||
* @id java/jexl-expression-injection
|
||||
* @tags security
|
||||
* external/cwe/cwe-094
|
||||
*/
|
||||
|
||||
import java
|
||||
import semmle.code.java.dataflow.FlowSources
|
||||
import semmle.code.java.security.JexlInjection
|
||||
import DataFlow::PathGraph
|
||||
|
||||
/**
|
||||
* A taint-tracking configuration for unsafe user input
|
||||
* that is used to construct and evaluate a JEXL expression.
|
||||
* It supports both JEXL 2 and 3.
|
||||
*/
|
||||
class JexlInjectionConfig extends TaintTracking::Configuration {
|
||||
JexlInjectionConfig() { this = "JexlInjectionConfig" }
|
||||
|
||||
override predicate isSource(DataFlow::Node source) { source instanceof RemoteFlowSource }
|
||||
|
||||
override predicate isSink(DataFlow::Node sink) { sink instanceof JexlEvaluationSink }
|
||||
|
||||
override predicate isAdditionalTaintStep(DataFlow::Node node1, DataFlow::Node node2) {
|
||||
any(JexlInjectionAdditionalTaintStep c).step(node1, node2)
|
||||
}
|
||||
}
|
||||
|
||||
from DataFlow::PathNode source, DataFlow::PathNode sink, JexlInjectionConfig conf
|
||||
where conf.hasFlowPath(source, sink)
|
||||
select sink.getNode(), source, sink, "JEXL injection from $@.", source.getNode(), "this user input"
|
||||
@@ -3,6 +3,7 @@
|
||||
* @description Using a deprecated artifact repository may eventually give attackers access for a supply chain attack.
|
||||
* @kind problem
|
||||
* @problem.severity error
|
||||
* @security-severity 6.5
|
||||
* @precision very-high
|
||||
* @id java/maven/dependency-upon-bintray
|
||||
* @tags security
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
* an HTTP header.
|
||||
* @kind problem
|
||||
* @problem.severity error
|
||||
* @security-severity 3.6
|
||||
* @precision high
|
||||
* @id java/netty-http-response-splitting
|
||||
* @tags security
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
* makes code vulnerable to attack by header splitting.
|
||||
* @kind path-problem
|
||||
* @problem.severity error
|
||||
* @security-severity 3.6
|
||||
* @precision high
|
||||
* @id java/http-response-splitting
|
||||
* @tags security
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
* makes code vulnerable to attack by header splitting.
|
||||
* @kind path-problem
|
||||
* @problem.severity recommendation
|
||||
* @security-severity 3.6
|
||||
* @precision medium
|
||||
* @id java/http-response-splitting-local
|
||||
* @tags security
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
* @description Using unvalidated external input as the argument to a construction of an array can lead to index out of bound exceptions.
|
||||
* @kind path-problem
|
||||
* @problem.severity warning
|
||||
* @security-severity 5.9
|
||||
* @precision medium
|
||||
* @id java/improper-validation-of-array-construction
|
||||
* @tags security
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
* a construction of an array can lead to index out of bound exceptions.
|
||||
* @kind path-problem
|
||||
* @problem.severity recommendation
|
||||
* @security-severity 5.9
|
||||
* @precision medium
|
||||
* @id java/improper-validation-of-array-construction-code-specified
|
||||
* @tags security
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
* a construction of an array can lead to index out of bound exceptions.
|
||||
* @kind path-problem
|
||||
* @problem.severity recommendation
|
||||
* @security-severity 5.9
|
||||
* @precision medium
|
||||
* @id java/improper-validation-of-array-construction-local
|
||||
* @tags security
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
* @description Using external input as an index to an array, without proper validation, can lead to index out of bound exceptions.
|
||||
* @kind path-problem
|
||||
* @problem.severity warning
|
||||
* @security-severity 5.9
|
||||
* @precision medium
|
||||
* @id java/improper-validation-of-array-index
|
||||
* @tags security
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
* proper validation, can lead to index out of bound exceptions.
|
||||
* @kind path-problem
|
||||
* @problem.severity recommendation
|
||||
* @security-severity 5.9
|
||||
* @precision medium
|
||||
* @id java/improper-validation-of-array-index-code-specified
|
||||
* @tags security
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
* proper validation, can lead to index out of bound exceptions.
|
||||
* @kind path-problem
|
||||
* @problem.severity recommendation
|
||||
* @security-severity 5.9
|
||||
* @precision medium
|
||||
* @id java/improper-validation-of-array-index-local
|
||||
* @tags security
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
* @description Using external input in format strings can lead to exceptions or information leaks.
|
||||
* @kind path-problem
|
||||
* @problem.severity error
|
||||
* @security-severity 6.9
|
||||
* @precision high
|
||||
* @id java/tainted-format-string
|
||||
* @tags security
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
* @description Using external input in format strings can lead to exceptions or information leaks.
|
||||
* @kind path-problem
|
||||
* @problem.severity recommendation
|
||||
* @security-severity 6.9
|
||||
* @precision medium
|
||||
* @id java/tainted-format-string-local
|
||||
* @tags security
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
* overflows.
|
||||
* @kind path-problem
|
||||
* @problem.severity warning
|
||||
* @security-severity 5.9
|
||||
* @precision medium
|
||||
* @id java/tainted-arithmetic
|
||||
* @tags security
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
* overflows.
|
||||
* @kind path-problem
|
||||
* @problem.severity recommendation
|
||||
* @security-severity 5.9
|
||||
* @precision medium
|
||||
* @id java/tainted-arithmetic-local
|
||||
* @tags security
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
* overflows.
|
||||
* @kind path-problem
|
||||
* @problem.severity warning
|
||||
* @security-severity 5.9
|
||||
* @precision medium
|
||||
* @id java/uncontrolled-arithmetic
|
||||
* @tags security
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
* is then used in an arithmetic expression, this may result in an overflow.
|
||||
* @kind path-problem
|
||||
* @problem.severity recommendation
|
||||
* @security-severity 5.9
|
||||
* @precision medium
|
||||
* @id java/extreme-value-arithmetic
|
||||
* @tags security
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
* to behave unexpectedly.
|
||||
* @kind problem
|
||||
* @problem.severity warning
|
||||
* @security-severity 5.9
|
||||
* @precision medium
|
||||
* @id java/comparison-with-wider-type
|
||||
* @tags reliability
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
* that are useful to an attacker for developing a subsequent exploit.
|
||||
* @kind problem
|
||||
* @problem.severity error
|
||||
* @security-severity 3.6
|
||||
* @precision high
|
||||
* @id java/stack-trace-exposure
|
||||
* @tags security
|
||||
@@ -15,7 +16,7 @@
|
||||
import java
|
||||
import semmle.code.java.dataflow.DataFlow
|
||||
import semmle.code.java.dataflow.TaintTracking
|
||||
import semmle.code.java.security.XSS
|
||||
import semmle.code.java.security.InformationLeak
|
||||
|
||||
/**
|
||||
* One of the `printStackTrace()` overloads on `Throwable`.
|
||||
@@ -83,14 +84,14 @@ predicate stackTraceExpr(Expr exception, MethodAccess stackTraceString) {
|
||||
)
|
||||
}
|
||||
|
||||
class StackTraceStringToXssSinkFlowConfig extends TaintTracking::Configuration {
|
||||
StackTraceStringToXssSinkFlowConfig() {
|
||||
this = "StackTraceExposure::StackTraceStringToXssSinkFlowConfig"
|
||||
class StackTraceStringToHttpResponseSinkFlowConfig extends TaintTracking::Configuration {
|
||||
StackTraceStringToHttpResponseSinkFlowConfig() {
|
||||
this = "StackTraceExposure::StackTraceStringToHttpResponseSinkFlowConfig"
|
||||
}
|
||||
|
||||
override predicate isSource(DataFlow::Node src) { stackTraceExpr(_, src.asExpr()) }
|
||||
|
||||
override predicate isSink(DataFlow::Node sink) { sink instanceof XssSink }
|
||||
override predicate isSink(DataFlow::Node sink) { sink instanceof InformationLeakSink }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -105,8 +106,8 @@ predicate printsStackExternally(MethodAccess call, Expr stackTrace) {
|
||||
/**
|
||||
* A stringified stack trace flows to an external sink.
|
||||
*/
|
||||
predicate stringifiedStackFlowsExternally(XssSink externalExpr, Expr stackTrace) {
|
||||
exists(MethodAccess stackTraceString, StackTraceStringToXssSinkFlowConfig conf |
|
||||
predicate stringifiedStackFlowsExternally(DataFlow::Node externalExpr, Expr stackTrace) {
|
||||
exists(MethodAccess stackTraceString, StackTraceStringToHttpResponseSinkFlowConfig conf |
|
||||
stackTraceExpr(stackTrace, stackTraceString) and
|
||||
conf.hasFlow(DataFlow::exprNode(stackTraceString), externalExpr)
|
||||
)
|
||||
@@ -123,21 +124,21 @@ class GetMessageFlowSource extends MethodAccess {
|
||||
}
|
||||
}
|
||||
|
||||
class GetMessageFlowSourceToXssSinkFlowConfig extends TaintTracking::Configuration {
|
||||
GetMessageFlowSourceToXssSinkFlowConfig() {
|
||||
this = "StackTraceExposure::GetMessageFlowSourceToXssSinkFlowConfig"
|
||||
class GetMessageFlowSourceToHttpResponseSinkFlowConfig extends TaintTracking::Configuration {
|
||||
GetMessageFlowSourceToHttpResponseSinkFlowConfig() {
|
||||
this = "StackTraceExposure::GetMessageFlowSourceToHttpResponseSinkFlowConfig"
|
||||
}
|
||||
|
||||
override predicate isSource(DataFlow::Node src) { src.asExpr() instanceof GetMessageFlowSource }
|
||||
|
||||
override predicate isSink(DataFlow::Node sink) { sink instanceof XssSink }
|
||||
override predicate isSink(DataFlow::Node sink) { sink instanceof InformationLeakSink }
|
||||
}
|
||||
|
||||
/**
|
||||
* A call to `getMessage()` that then flows to a servlet response.
|
||||
*/
|
||||
predicate getMessageFlowsExternally(XssSink externalExpr, GetMessageFlowSource getMessage) {
|
||||
any(GetMessageFlowSourceToXssSinkFlowConfig conf)
|
||||
predicate getMessageFlowsExternally(DataFlow::Node externalExpr, GetMessageFlowSource getMessage) {
|
||||
any(GetMessageFlowSourceToHttpResponseSinkFlowConfig conf)
|
||||
.hasFlow(DataFlow::exprNode(getMessage), externalExpr)
|
||||
}
|
||||
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
* @description Marking a certificate as valid for a host without checking the certificate hostname allows an attacker to perform a machine-in-the-middle attack.
|
||||
* @kind path-problem
|
||||
* @problem.severity error
|
||||
* @security-severity 4.9
|
||||
* @precision high
|
||||
* @id java/unsafe-hostname-verification
|
||||
* @tags security
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
* @description Storing sensitive information in cleartext can expose it to an attacker.
|
||||
* @kind problem
|
||||
* @problem.severity recommendation
|
||||
* @security-severity 5.9
|
||||
* @precision medium
|
||||
* @id java/cleartext-storage-in-class
|
||||
* @tags security
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
* @description Storing sensitive information in cleartext can expose it to an attacker.
|
||||
* @kind problem
|
||||
* @problem.severity error
|
||||
* @security-severity 2.9
|
||||
* @precision high
|
||||
* @id java/cleartext-storage-in-cookie
|
||||
* @tags security
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
* @description Storing sensitive information in cleartext can expose it to an attacker.
|
||||
* @kind problem
|
||||
* @problem.severity warning
|
||||
* @security-severity 6.4
|
||||
* @precision medium
|
||||
* @id java/cleartext-storage-in-properties
|
||||
* @tags security
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
* @description Non-HTTPS connections can be intercepted by third parties.
|
||||
* @kind path-problem
|
||||
* @problem.severity recommendation
|
||||
* @security-severity 5.2
|
||||
* @precision medium
|
||||
* @id java/non-https-url
|
||||
* @tags security
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
* @description Non-SSL connections can be intercepted by third parties.
|
||||
* @kind problem
|
||||
* @problem.severity recommendation
|
||||
* @security-severity 5.2
|
||||
* @precision medium
|
||||
* @id java/non-ssl-connection
|
||||
* @tags security
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
* third parties.
|
||||
* @kind problem
|
||||
* @problem.severity recommendation
|
||||
* @security-severity 5.2
|
||||
* @precision medium
|
||||
* @id java/non-ssl-socket-factory
|
||||
* @tags security
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
* @description Using broken or weak cryptographic algorithms can allow an attacker to compromise security.
|
||||
* @kind path-problem
|
||||
* @problem.severity warning
|
||||
* @security-severity 5.2
|
||||
* @precision high
|
||||
* @id java/weak-cryptographic-algorithm
|
||||
* @tags security
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
* @description Using broken or weak cryptographic algorithms can allow an attacker to compromise security.
|
||||
* @kind path-problem
|
||||
* @problem.severity warning
|
||||
* @security-severity 5.2
|
||||
* @precision medium
|
||||
* @id java/potentially-weak-cryptographic-algorithm
|
||||
* @tags security
|
||||
|
||||
@@ -3,9 +3,11 @@
|
||||
* @description Using a predictable seed in a pseudo-random number generator can lead to predictability of the numbers generated by it.
|
||||
* @kind problem
|
||||
* @problem.severity error
|
||||
* @security-severity 5.9
|
||||
* @precision high
|
||||
* @id java/predictable-seed
|
||||
* @tags security
|
||||
* external/cwe/cwe-335
|
||||
*/
|
||||
|
||||
import java
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
* @description Using a vulnerable version of JHipster to generate random numbers makes it easier for attackers to take over accounts.
|
||||
* @kind problem
|
||||
* @problem.severity error
|
||||
* @security-severity 5.9
|
||||
* @precision very-high
|
||||
* @id java/jhipster-prng
|
||||
* @tags security
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
* a Cross-Site Request Forgery (CSRF) attack.
|
||||
* @kind problem
|
||||
* @problem.severity error
|
||||
* @security-severity 6.4
|
||||
* @precision high
|
||||
* @id java/spring-disabled-csrf-protection
|
||||
* @tags security
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
* if the state may be changed between the check and use.
|
||||
* @kind problem
|
||||
* @problem.severity warning
|
||||
* @security-severity 5.9
|
||||
* @precision medium
|
||||
* @id java/toctou-race-condition
|
||||
* @tags security
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
* @description Opening a socket after authenticating via a different channel may allow an attacker to connect to the port first.
|
||||
* @kind problem
|
||||
* @problem.severity warning
|
||||
* @security-severity 10.0
|
||||
* @precision medium
|
||||
* @id java/socket-auth-race-condition
|
||||
* @tags security
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
* execute arbitrary code.
|
||||
* @kind path-problem
|
||||
* @problem.severity error
|
||||
* @security-severity 5.9
|
||||
* @precision high
|
||||
* @id java/unsafe-deserialization
|
||||
* @tags security
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
* may cause redirection to malicious web sites.
|
||||
* @kind path-problem
|
||||
* @problem.severity error
|
||||
* @security-severity 2.7
|
||||
* @precision high
|
||||
* @id java/unvalidated-url-redirection
|
||||
* @tags security
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
* may cause redirection to malicious web sites.
|
||||
* @kind path-problem
|
||||
* @problem.severity recommendation
|
||||
* @security-severity 2.7
|
||||
* @precision medium
|
||||
* @id java/unvalidated-url-redirection-local
|
||||
* @tags security
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
* references may lead to disclosure of confidential data or denial of service.
|
||||
* @kind path-problem
|
||||
* @problem.severity error
|
||||
* @security-severity 5.9
|
||||
* @precision high
|
||||
* @id java/xxe
|
||||
* @tags security
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
* interception.
|
||||
* @kind problem
|
||||
* @problem.severity error
|
||||
* @security-severity 2.9
|
||||
* @precision high
|
||||
* @id java/insecure-cookie
|
||||
* @tags security
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
* malicious code by the user.
|
||||
* @kind path-problem
|
||||
* @problem.severity error
|
||||
* @security-severity 5.9
|
||||
* @precision high
|
||||
* @id java/xml/xpath-injection
|
||||
* @tags security
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
* @description Certain standard library routines are dangerous to call.
|
||||
* @kind problem
|
||||
* @problem.severity warning
|
||||
* @security-severity 10.0
|
||||
* @precision medium
|
||||
* @id java/potentially-dangerous-function
|
||||
* @tags reliability
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
* can cause unexpected truncation.
|
||||
* @kind path-problem
|
||||
* @problem.severity error
|
||||
* @security-severity 5.9
|
||||
* @precision high
|
||||
* @id java/tainted-numeric-cast
|
||||
* @tags security
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
* can cause unexpected truncation.
|
||||
* @kind path-problem
|
||||
* @problem.severity recommendation
|
||||
* @security-severity 5.9
|
||||
* @precision medium
|
||||
* @id java/tainted-numeric-cast-local
|
||||
* @tags security
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
* the file may be modified or removed by external actors.
|
||||
* @kind problem
|
||||
* @problem.severity error
|
||||
* @security-severity 5.9
|
||||
* @precision high
|
||||
* @id java/world-writable-file-read
|
||||
* @tags security
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
* @description Using a hard-coded credential in a call to a sensitive Java API may compromise security.
|
||||
* @kind path-problem
|
||||
* @problem.severity error
|
||||
* @security-severity 5.9
|
||||
* @precision medium
|
||||
* @id java/hardcoded-credential-api-call
|
||||
* @tags security
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
* @description Comparing a parameter to a hard-coded credential may compromise security.
|
||||
* @kind problem
|
||||
* @problem.severity error
|
||||
* @security-severity 5.9
|
||||
* @precision low
|
||||
* @id java/hardcoded-credential-comparison
|
||||
* @tags security
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
* @description Using a hard-coded credential in a sensitive call may compromise security.
|
||||
* @kind path-problem
|
||||
* @problem.severity error
|
||||
* @security-severity 5.9
|
||||
* @precision low
|
||||
* @id java/hardcoded-credential-sensitive-call
|
||||
* @tags security
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
* @description Hard-coding a password string may compromise security.
|
||||
* @kind problem
|
||||
* @problem.severity error
|
||||
* @security-severity 5.9
|
||||
* @precision low
|
||||
* @id java/hardcoded-password-field
|
||||
* @tags security
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
* passing through authentication systems.
|
||||
* @kind path-problem
|
||||
* @problem.severity error
|
||||
* @security-severity 5.9
|
||||
* @precision medium
|
||||
* @id java/user-controlled-bypass
|
||||
* @tags security
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
* permissions being granted.
|
||||
* @kind path-problem
|
||||
* @problem.severity error
|
||||
* @security-severity 5.9
|
||||
* @precision high
|
||||
* @id java/tainted-permissions-check
|
||||
* @tags security
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
* @description Non-HTTPS connections can be intercepted by third parties.
|
||||
* @kind problem
|
||||
* @problem.severity error
|
||||
* @security-severity 5.9
|
||||
* @precision very-high
|
||||
* @id java/maven/non-https-url
|
||||
* @tags security
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
* @description Acquiring multiple locks in a different order may cause deadlock.
|
||||
* @kind problem
|
||||
* @problem.severity recommendation
|
||||
* @security-severity 6.9
|
||||
* @precision medium
|
||||
* @id java/lock-order-inconsistency
|
||||
* @tags security
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
* looping.
|
||||
* @kind problem
|
||||
* @problem.severity warning
|
||||
* @security-severity 3.6
|
||||
* @precision medium
|
||||
* @id java/unreachable-exit-in-loop
|
||||
* @tags security
|
||||
|
||||
@@ -1,19 +0,0 @@
|
||||
/**
|
||||
* @name Expression language injection (JEXL)
|
||||
* @description Evaluation of a user-controlled JEXL expression
|
||||
* may lead to arbitrary code execution.
|
||||
* @kind path-problem
|
||||
* @problem.severity error
|
||||
* @precision high
|
||||
* @id java/jexl-expression-injection
|
||||
* @tags security
|
||||
* external/cwe/cwe-094
|
||||
*/
|
||||
|
||||
import java
|
||||
import JexlInjectionLib
|
||||
import DataFlow::PathGraph
|
||||
|
||||
from DataFlow::PathNode source, DataFlow::PathNode sink, JexlInjectionConfig conf
|
||||
where conf.hasFlowPath(source, sink)
|
||||
select sink.getNode(), source, sink, "JEXL injection from $@.", source.getNode(), "this user input"
|
||||
@@ -1,277 +0,0 @@
|
||||
import java
|
||||
import FlowUtils
|
||||
import semmle.code.java.dataflow.FlowSources
|
||||
import semmle.code.java.dataflow.TaintTracking
|
||||
|
||||
/**
|
||||
* A taint-tracking configuration for unsafe user input
|
||||
* that is used to construct and evaluate a JEXL expression.
|
||||
* It supports both JEXL 2 and 3.
|
||||
*/
|
||||
class JexlInjectionConfig extends TaintTracking::Configuration {
|
||||
JexlInjectionConfig() { this = "JexlInjectionConfig" }
|
||||
|
||||
override predicate isSource(DataFlow::Node source) { source instanceof RemoteFlowSource }
|
||||
|
||||
override predicate isSink(DataFlow::Node sink) { sink instanceof JexlEvaluationSink }
|
||||
|
||||
override predicate isAdditionalTaintStep(DataFlow::Node fromNode, DataFlow::Node toNode) {
|
||||
any(TaintPropagatingJexlMethodCall c).taintFlow(fromNode, toNode) or
|
||||
hasGetterFlow(fromNode, toNode)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A sink for Expresssion Language injection vulnerabilities via Jexl,
|
||||
* i.e. method calls that run evaluation of a JEXL expression.
|
||||
*
|
||||
* Creating a `Callable` from a tainted JEXL expression or script is considered as a sink
|
||||
* although the tainted expression is not executed at this point.
|
||||
* Here we assume that it will get executed at some point,
|
||||
* maybe stored in an object field and then reached by a different flow.
|
||||
*/
|
||||
private class JexlEvaluationSink extends DataFlow::ExprNode {
|
||||
JexlEvaluationSink() {
|
||||
exists(MethodAccess ma, Method m, Expr taintFrom |
|
||||
ma.getMethod() = m and taintFrom = this.asExpr()
|
||||
|
|
||||
m instanceof DirectJexlEvaluationMethod and ma.getQualifier() = taintFrom
|
||||
or
|
||||
m instanceof CreateJexlCallableMethod and ma.getQualifier() = taintFrom
|
||||
or
|
||||
m instanceof JexlEngineGetSetPropertyMethod and
|
||||
taintFrom.getType() instanceof TypeString and
|
||||
ma.getAnArgument() = taintFrom
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Defines method calls that propagate tainted data via one of the methods
|
||||
* from JEXL library.
|
||||
*/
|
||||
private class TaintPropagatingJexlMethodCall extends MethodAccess {
|
||||
Expr taintFromExpr;
|
||||
|
||||
TaintPropagatingJexlMethodCall() {
|
||||
exists(Method m, RefType taintType |
|
||||
this.getMethod() = m and
|
||||
taintType = taintFromExpr.getType()
|
||||
|
|
||||
isUnsafeEngine(this.getQualifier()) and
|
||||
(
|
||||
m instanceof CreateJexlScriptMethod and
|
||||
taintFromExpr = this.getArgument(0) and
|
||||
taintType instanceof TypeString
|
||||
or
|
||||
m instanceof CreateJexlExpressionMethod and
|
||||
taintFromExpr = this.getAnArgument() and
|
||||
taintType instanceof TypeString
|
||||
or
|
||||
m instanceof CreateJexlTemplateMethod and
|
||||
(taintType instanceof TypeString or taintType instanceof Reader) and
|
||||
taintFromExpr = this.getArgument([0, 1])
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `fromNode` to `toNode` is a dataflow step that propagates
|
||||
* tainted data.
|
||||
*/
|
||||
predicate taintFlow(DataFlow::Node fromNode, DataFlow::Node toNode) {
|
||||
fromNode.asExpr() = taintFromExpr and toNode.asExpr() = this
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `expr` is a JEXL engine that is not configured with a sandbox.
|
||||
*/
|
||||
private predicate isUnsafeEngine(Expr expr) {
|
||||
not exists(SandboxedJexlFlowConfig config | config.hasFlowTo(DataFlow::exprNode(expr)))
|
||||
}
|
||||
|
||||
/**
|
||||
* A configuration for a tracking sandboxed JEXL engines.
|
||||
*/
|
||||
private class SandboxedJexlFlowConfig extends DataFlow2::Configuration {
|
||||
SandboxedJexlFlowConfig() { this = "JexlInjection::SandboxedJexlFlowConfig" }
|
||||
|
||||
override predicate isSource(DataFlow::Node node) { node instanceof SandboxedJexlSource }
|
||||
|
||||
override predicate isSink(DataFlow::Node node) {
|
||||
exists(MethodAccess ma, Method m | ma.getMethod() = m |
|
||||
(
|
||||
m instanceof CreateJexlScriptMethod or
|
||||
m instanceof CreateJexlExpressionMethod or
|
||||
m instanceof CreateJexlTemplateMethod
|
||||
) and
|
||||
ma.getQualifier() = node.asExpr()
|
||||
)
|
||||
}
|
||||
|
||||
override predicate isAdditionalFlowStep(DataFlow::Node fromNode, DataFlow::Node toNode) {
|
||||
createsJexlEngine(fromNode, toNode)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Defines a data flow source for JEXL engines configured with a sandbox.
|
||||
*/
|
||||
private class SandboxedJexlSource extends DataFlow::ExprNode {
|
||||
SandboxedJexlSource() {
|
||||
exists(MethodAccess ma, Method m | m = ma.getMethod() |
|
||||
m.getDeclaringType() instanceof JexlBuilder and
|
||||
m.hasName(["uberspect", "sandbox"]) and
|
||||
m.getReturnType() instanceof JexlBuilder and
|
||||
this.asExpr() = [ma, ma.getQualifier()]
|
||||
)
|
||||
or
|
||||
exists(ConstructorCall cc |
|
||||
cc.getConstructedType() instanceof JexlEngine and
|
||||
cc.getArgument(0).getType() instanceof JexlUberspect and
|
||||
cc = this.asExpr()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `fromNode` to `toNode` is a dataflow step that creates one of the JEXL engines.
|
||||
*/
|
||||
private predicate createsJexlEngine(DataFlow::Node fromNode, DataFlow::Node toNode) {
|
||||
exists(MethodAccess ma, Method m | m = ma.getMethod() |
|
||||
(m.getDeclaringType() instanceof JexlBuilder or m.getDeclaringType() instanceof JexlEngine) and
|
||||
m.hasName(["create", "createJxltEngine"]) and
|
||||
ma.getQualifier() = fromNode.asExpr() and
|
||||
ma = toNode.asExpr()
|
||||
)
|
||||
or
|
||||
exists(ConstructorCall cc |
|
||||
cc.getConstructedType() instanceof UnifiedJexl and
|
||||
cc.getArgument(0) = fromNode.asExpr() and
|
||||
cc = toNode.asExpr()
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* A methods in the `JexlEngine` class that gets or sets a property with a JEXL expression.
|
||||
*/
|
||||
private class JexlEngineGetSetPropertyMethod extends Method {
|
||||
JexlEngineGetSetPropertyMethod() {
|
||||
getDeclaringType() instanceof JexlEngine and
|
||||
hasName(["getProperty", "setProperty"])
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A method that triggers direct evaluation of JEXL expressions.
|
||||
*/
|
||||
private class DirectJexlEvaluationMethod extends Method {
|
||||
DirectJexlEvaluationMethod() {
|
||||
getDeclaringType() instanceof JexlExpression and hasName("evaluate")
|
||||
or
|
||||
getDeclaringType() instanceof JexlScript and hasName("execute")
|
||||
or
|
||||
getDeclaringType() instanceof JxltEngineExpression and hasName(["evaluate", "prepare"])
|
||||
or
|
||||
getDeclaringType() instanceof JxltEngineTemplate and hasName("evaluate")
|
||||
or
|
||||
getDeclaringType() instanceof UnifiedJexlExpression and hasName(["evaluate", "prepare"])
|
||||
or
|
||||
getDeclaringType() instanceof UnifiedJexlTemplate and hasName("evaluate")
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A method that creates a JEXL script.
|
||||
*/
|
||||
private class CreateJexlScriptMethod extends Method {
|
||||
CreateJexlScriptMethod() { getDeclaringType() instanceof JexlEngine and hasName("createScript") }
|
||||
}
|
||||
|
||||
/**
|
||||
* A method that creates a `Callable` for a JEXL expression or script.
|
||||
*/
|
||||
private class CreateJexlCallableMethod extends Method {
|
||||
CreateJexlCallableMethod() {
|
||||
(getDeclaringType() instanceof JexlExpression or getDeclaringType() instanceof JexlScript) and
|
||||
hasName("callable")
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A method that creates a JEXL template.
|
||||
*/
|
||||
private class CreateJexlTemplateMethod extends Method {
|
||||
CreateJexlTemplateMethod() {
|
||||
(getDeclaringType() instanceof JxltEngine or getDeclaringType() instanceof UnifiedJexl) and
|
||||
hasName("createTemplate")
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A method that creates a JEXL expression.
|
||||
*/
|
||||
private class CreateJexlExpressionMethod extends Method {
|
||||
CreateJexlExpressionMethod() {
|
||||
(getDeclaringType() instanceof JexlEngine or getDeclaringType() instanceof JxltEngine) and
|
||||
hasName("createExpression")
|
||||
or
|
||||
getDeclaringType() instanceof UnifiedJexl and hasName("parse")
|
||||
}
|
||||
}
|
||||
|
||||
private class JexlRefType extends RefType {
|
||||
JexlRefType() { getPackage().hasName(["org.apache.commons.jexl2", "org.apache.commons.jexl3"]) }
|
||||
}
|
||||
|
||||
private class JexlExpression extends JexlRefType {
|
||||
JexlExpression() { hasName(["Expression", "JexlExpression"]) }
|
||||
}
|
||||
|
||||
private class JexlScript extends JexlRefType {
|
||||
JexlScript() { hasName(["Script", "JexlScript"]) }
|
||||
}
|
||||
|
||||
private class JexlBuilder extends JexlRefType {
|
||||
JexlBuilder() { hasName("JexlBuilder") }
|
||||
}
|
||||
|
||||
private class JexlEngine extends JexlRefType {
|
||||
JexlEngine() { hasName("JexlEngine") }
|
||||
}
|
||||
|
||||
private class JxltEngine extends JexlRefType {
|
||||
JxltEngine() { hasName("JxltEngine") }
|
||||
}
|
||||
|
||||
private class UnifiedJexl extends JexlRefType {
|
||||
UnifiedJexl() { hasName("UnifiedJEXL") }
|
||||
}
|
||||
|
||||
private class JexlUberspect extends Interface {
|
||||
JexlUberspect() {
|
||||
hasQualifiedName("org.apache.commons.jexl2.introspection", "Uberspect") or
|
||||
hasQualifiedName("org.apache.commons.jexl3.introspection", "JexlUberspect")
|
||||
}
|
||||
}
|
||||
|
||||
private class JxltEngineExpression extends NestedType {
|
||||
JxltEngineExpression() { getEnclosingType() instanceof JxltEngine and hasName("Expression") }
|
||||
}
|
||||
|
||||
private class JxltEngineTemplate extends NestedType {
|
||||
JxltEngineTemplate() { getEnclosingType() instanceof JxltEngine and hasName("Template") }
|
||||
}
|
||||
|
||||
private class UnifiedJexlExpression extends NestedType {
|
||||
UnifiedJexlExpression() { getEnclosingType() instanceof UnifiedJexl and hasName("Expression") }
|
||||
}
|
||||
|
||||
private class UnifiedJexlTemplate extends NestedType {
|
||||
UnifiedJexlTemplate() { getEnclosingType() instanceof UnifiedJexl and hasName("Template") }
|
||||
}
|
||||
|
||||
private class Reader extends RefType {
|
||||
Reader() { hasQualifiedName("java.io", "Reader") }
|
||||
}
|
||||
@@ -3,6 +3,8 @@
|
||||
* @description Evaluation of a user-controlled malicious expression in Java Python
|
||||
* interpreter may lead to remote code execution.
|
||||
* @kind path-problem
|
||||
* @problem.severity error
|
||||
* @precision high
|
||||
* @id java/jython-injection
|
||||
* @tags security
|
||||
* external/cwe/cwe-094
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
public void bindRemoteObject(Registry registry, int port) throws Exception {
|
||||
ObjectInputFilter filter = info -> {
|
||||
if (info.serialClass().getCanonicalName().startsWith("com.safe.package.")) {
|
||||
return ObjectInputFilter.Status.ALLOWED;
|
||||
}
|
||||
return ObjectInputFilter.Status.REJECTED;
|
||||
};
|
||||
registry.bind("safer", UnicastRemoteObject.exportObject(new RemoteObjectImpl(), port, filter));
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
public class Server {
|
||||
public void bindRemoteObject(Registry registry) throws Exception {
|
||||
registry.bind("safe", new RemoteObjectImpl());
|
||||
}
|
||||
}
|
||||
|
||||
interface RemoteObject extends Remote {
|
||||
void calculate(int a, double b) throws RemoteException;
|
||||
void save(String s) throws RemoteException;
|
||||
}
|
||||
|
||||
class RemoteObjectImpl implements RemoteObject {
|
||||
// ...
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
public class Server {
|
||||
public void bindRemoteObject(Registry registry) throws Exception {
|
||||
registry.bind("unsafe", new RemoteObjectImpl());
|
||||
}
|
||||
}
|
||||
|
||||
interface RemoteObject extends Remote {
|
||||
void action(Object obj) throws RemoteException;
|
||||
}
|
||||
|
||||
class RemoteObjectImpl implements RemoteObject {
|
||||
// ...
|
||||
}
|
||||
@@ -0,0 +1,81 @@
|
||||
<!DOCTYPE qhelp PUBLIC "-//Semmle//qhelp//EN" "qhelp.dtd">
|
||||
<qhelp>
|
||||
|
||||
<overview>
|
||||
<p>
|
||||
Java RMI uses the default Java serialization mechanism (in other words, <code>ObjectInputStream</code>)
|
||||
to pass parameters in remote method invocations. This mechanism is known to be unsafe when deserializing
|
||||
untrusted data. If a registered remote object has a method that accepts a complex object,
|
||||
an attacker can take advantage of the unsafe deserialization mechanism.
|
||||
In the worst case, it results in remote code execution.
|
||||
</p>
|
||||
</overview>
|
||||
|
||||
<recommendation>
|
||||
<p>
|
||||
Use only strings and primitive types for parameters of remotely invokable methods.
|
||||
</p>
|
||||
<p>
|
||||
Set a filter for incoming serialized data by wrapping remote objects using either <code>UnicastRemoteObject.exportObject(Remote, int, ObjectInputFilter)</code>
|
||||
or <code>UnicastRemoteObject.exportObject(Remote, int, RMIClientSocketFactory, RMIServerSocketFactory, ObjectInputFilter)</code> methods.
|
||||
Those methods accept an <code>ObjectInputFilter</code> that decides which classes are allowed for deserialization.
|
||||
The filter should allow deserializing only safe classes.
|
||||
</p>
|
||||
<p>
|
||||
It is also possible to set a process-wide deserialization filter.
|
||||
The filter can be set by with <code>ObjectInputFilter.Config.setSerialFilter(ObjectInputFilter)</code> method,
|
||||
or by setting system or security property <code>jdk.serialFilter</code>.
|
||||
Make sure that you use the latest Java versions that include JEP 290.
|
||||
Please note that the query is not sensitive to this mitigation.
|
||||
</p>
|
||||
<p>
|
||||
If switching to the latest Java versions is not possible,
|
||||
consider using other implementations of remote procedure calls. For example, HTTP API with JSON.
|
||||
Make sure that the underlying deserialization mechanism is properly configured
|
||||
so that deserialization attacks are not possible.
|
||||
</p>
|
||||
</recommendation>
|
||||
|
||||
<example>
|
||||
<p>
|
||||
The following code registers a remote object
|
||||
with a vulnerable method that accepts a complex object:
|
||||
</p>
|
||||
<sample src="RmiUnsafeRemoteObject.java" />
|
||||
|
||||
<p>
|
||||
The next example registers a safe remote object
|
||||
whose methods use only primitive types and strings:
|
||||
</p>
|
||||
<sample src="RmiSafeRemoteObject.java" />
|
||||
|
||||
<p>
|
||||
The next example shows how to set a deserilization filter for a remote object:
|
||||
</p>
|
||||
<sample src="RmiRemoteObjectWithFilter.java" />
|
||||
|
||||
</example>
|
||||
|
||||
<references>
|
||||
<li>
|
||||
Oracle:
|
||||
<a href="https://www.oracle.com/java/technologies/javase/remote-method-invocation-home.html">Remote Method Invocation (RMI)</a>.
|
||||
</li>
|
||||
<li>
|
||||
ITNEXT:
|
||||
<a href="https://itnext.io/java-rmi-for-pentesters-part-two-reconnaissance-attack-against-non-jmx-registries-187a6561314d">Java RMI for pentesters part two - reconnaissance & attack against non-JMX registries</a>.
|
||||
</li>
|
||||
<li>
|
||||
MOGWAI LABS:
|
||||
<a href="https://mogwailabs.de/en/blog/2019/03/attacking-java-rmi-services-after-jep-290">Attacking Java RMI services after JEP 290</a>
|
||||
</li>
|
||||
<li>
|
||||
OWASP:
|
||||
<a href="https://www.owasp.org/index.php/Deserialization_of_untrusted_data">Deserialization of untrusted data</a>.
|
||||
</li>
|
||||
<li>
|
||||
OpenJDK:
|
||||
<a href="https://openjdk.java.net/jeps/290">JEP 290: Filter Incoming Serialization Data</a>
|
||||
</li>
|
||||
</references>
|
||||
</qhelp>
|
||||
@@ -0,0 +1,78 @@
|
||||
/**
|
||||
* @name Unsafe deserialization in a remotely callable method.
|
||||
* @description If a registered remote object has a method that accepts a complex object,
|
||||
* an attacker can take advantage of the unsafe deserialization mechanism
|
||||
* which is used to pass parameters in RMI.
|
||||
* In the worst case, it results in remote code execution.
|
||||
* @kind path-problem
|
||||
* @problem.severity error
|
||||
* @precision high
|
||||
* @id java/unsafe-deserialization-rmi
|
||||
* @tags security
|
||||
* external/cwe/cwe-502
|
||||
*/
|
||||
|
||||
import java
|
||||
import semmle.code.java.dataflow.TaintTracking
|
||||
import semmle.code.java.frameworks.Rmi
|
||||
import DataFlow::PathGraph
|
||||
|
||||
/**
|
||||
* A method that binds a name to a remote object.
|
||||
*/
|
||||
private class BindMethod extends Method {
|
||||
BindMethod() {
|
||||
(
|
||||
getDeclaringType().hasQualifiedName("java.rmi", "Naming") or
|
||||
getDeclaringType().hasQualifiedName("java.rmi.registry", "Registry")
|
||||
) and
|
||||
hasName(["bind", "rebind"])
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `type` has an vulnerable remote method.
|
||||
*/
|
||||
private predicate hasVulnerableMethod(RefType type) {
|
||||
exists(RemoteCallableMethod m, Type parameterType |
|
||||
m.getDeclaringType() = type and parameterType = m.getAParamType()
|
||||
|
|
||||
not parameterType instanceof PrimitiveType and
|
||||
not parameterType instanceof TypeString and
|
||||
not parameterType.(RefType).hasQualifiedName("java.io", "ObjectInputStream")
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* A taint-tracking configuration for unsafe remote objects
|
||||
* that are vulnerable to deserialization attacks.
|
||||
*/
|
||||
private class BindingUnsafeRemoteObjectConfig extends TaintTracking::Configuration {
|
||||
BindingUnsafeRemoteObjectConfig() { this = "BindingUnsafeRemoteObjectConfig" }
|
||||
|
||||
override predicate isSource(DataFlow::Node source) {
|
||||
exists(ConstructorCall cc | cc = source.asExpr() |
|
||||
hasVulnerableMethod(cc.getConstructedType().getASupertype*())
|
||||
)
|
||||
}
|
||||
|
||||
override predicate isSink(DataFlow::Node sink) {
|
||||
exists(MethodAccess ma | ma.getArgument(1) = sink.asExpr() |
|
||||
ma.getMethod() instanceof BindMethod
|
||||
)
|
||||
}
|
||||
|
||||
override predicate isAdditionalTaintStep(DataFlow::Node fromNode, DataFlow::Node toNode) {
|
||||
exists(MethodAccess ma, Method m | m = ma.getMethod() |
|
||||
m.getDeclaringType().hasQualifiedName("java.rmi.server", "UnicastRemoteObject") and
|
||||
m.hasName("exportObject") and
|
||||
not m.getParameterType([2, 4]).(RefType).hasQualifiedName("java.io", "ObjectInputFilter") and
|
||||
ma.getArgument(0) = fromNode.asExpr() and
|
||||
ma = toNode.asExpr()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
from DataFlow::PathNode source, DataFlow::PathNode sink, BindingUnsafeRemoteObjectConfig conf
|
||||
where conf.hasFlowPath(source, sink)
|
||||
select sink.getNode(), source, sink, "Unsafe deserialization in a remote object."
|
||||
@@ -0,0 +1,38 @@
|
||||
package com.example.demo;
|
||||
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.RequestParam;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
@RestController
|
||||
public class DemoApplication {
|
||||
|
||||
@GetMapping("/string1")
|
||||
public String string1(@RequestParam(value = "input", defaultValue = "test") String input,
|
||||
@RequestParam(value = "pattern", defaultValue = ".*") String pattern) {
|
||||
// BAD: Unsanitized user input is used to construct a regular expression
|
||||
if (input.matches("^" + pattern + "=.*$"))
|
||||
return "match!";
|
||||
|
||||
return "doesn't match!";
|
||||
}
|
||||
|
||||
@GetMapping("/string2")
|
||||
public String string2(@RequestParam(value = "input", defaultValue = "test") String input,
|
||||
@RequestParam(value = "pattern", defaultValue = ".*") String pattern) {
|
||||
// GOOD: User input is sanitized before constructing the regex
|
||||
if (input.matches("^" + escapeSpecialRegexChars(pattern) + "=.*$"))
|
||||
return "match!";
|
||||
|
||||
return "doesn't match!";
|
||||
}
|
||||
|
||||
Pattern SPECIAL_REGEX_CHARS = Pattern.compile("[{}()\\[\\]><-=!.+*?^$\\\\|]");
|
||||
|
||||
String escapeSpecialRegexChars(String str) {
|
||||
return SPECIAL_REGEX_CHARS.matcher(str).replaceAll("\\\\$0");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
<!DOCTYPE qhelp PUBLIC
|
||||
"-//Semmle//qhelp//EN"
|
||||
"qhelp.dtd">
|
||||
<qhelp>
|
||||
|
||||
<overview>
|
||||
<p>
|
||||
Constructing a regular expression with unsanitized user input is dangerous as a malicious user may
|
||||
be able to modify the meaning of the expression. In particular, such a user may be able to provide
|
||||
a regular expression fragment that takes exponential time in the worst case, and use that to
|
||||
perform a Denial of Service attack.
|
||||
</p>
|
||||
</overview>
|
||||
|
||||
<recommendation>
|
||||
<p>
|
||||
Before embedding user input into a regular expression, use a sanitization function
|
||||
to escape meta-characters that have special meaning.
|
||||
</p>
|
||||
</recommendation>
|
||||
|
||||
<example>
|
||||
<p>
|
||||
The following example shows a HTTP request parameter that is used to construct a regular expression:
|
||||
</p>
|
||||
<sample src="RegexInjection.java" />
|
||||
<p>
|
||||
In the first case the user-provided regex is not escaped.
|
||||
If a malicious user provides a regex that has exponential worst case performance,
|
||||
then this could lead to a Denial of Service.
|
||||
</p>
|
||||
<p>
|
||||
In the second case, the user input is escaped using <code>escapeSpecialRegexChars</code> before being included
|
||||
in the regular expression. This ensures that the user cannot insert characters which have a special
|
||||
meaning in regular expressions.
|
||||
</p>
|
||||
</example>
|
||||
|
||||
<references>
|
||||
<li>
|
||||
OWASP:
|
||||
<a href="https://www.owasp.org/index.php/Regular_expression_Denial_of_Service_-_ReDoS">Regular expression Denial of Service - ReDoS</a>.
|
||||
</li>
|
||||
<li>
|
||||
Wikipedia: <a href="https://en.wikipedia.org/wiki/ReDoS">ReDoS</a>.
|
||||
</li>
|
||||
</references>
|
||||
</qhelp>
|
||||
@@ -0,0 +1,89 @@
|
||||
/**
|
||||
* @name Regular expression injection
|
||||
* @description User input should not be used in regular expressions without first being sanitized,
|
||||
* otherwise a malicious user may be able to provide a regex that could require
|
||||
* exponential time on certain inputs.
|
||||
* @kind path-problem
|
||||
* @problem.severity error
|
||||
* @precision high
|
||||
* @id java/regex-injection
|
||||
* @tags security
|
||||
* external/cwe/cwe-730
|
||||
* external/cwe/cwe-400
|
||||
*/
|
||||
|
||||
import java
|
||||
import semmle.code.java.dataflow.FlowSources
|
||||
import semmle.code.java.dataflow.TaintTracking
|
||||
import DataFlow::PathGraph
|
||||
|
||||
/**
|
||||
* A data flow sink for untrusted user input used to construct regular expressions.
|
||||
*/
|
||||
class RegexSink extends DataFlow::ExprNode {
|
||||
RegexSink() {
|
||||
exists(MethodAccess ma, Method m | m = ma.getMethod() |
|
||||
(
|
||||
m.getDeclaringType() instanceof TypeString and
|
||||
(
|
||||
ma.getArgument(0) = this.asExpr() and
|
||||
m.hasName(["matches", "split", "replaceFirst", "replaceAll"])
|
||||
)
|
||||
or
|
||||
m.getDeclaringType().hasQualifiedName("java.util.regex", "Pattern") and
|
||||
(
|
||||
ma.getArgument(0) = this.asExpr() and
|
||||
m.hasName(["compile", "matches"])
|
||||
)
|
||||
or
|
||||
m.getDeclaringType().hasQualifiedName("org.apache.commons.lang3", "RegExUtils") and
|
||||
(
|
||||
ma.getArgument(1) = this.asExpr() and
|
||||
m.getParameterType(1) instanceof TypeString and
|
||||
m.hasName([
|
||||
"removeAll", "removeFirst", "removePattern", "replaceAll", "replaceFirst",
|
||||
"replacePattern"
|
||||
])
|
||||
)
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
abstract class Sanitizer extends DataFlow::ExprNode { }
|
||||
|
||||
/**
|
||||
* A call to a function whose name suggests that it escapes regular
|
||||
* expression meta-characters.
|
||||
*/
|
||||
class RegExpSanitizationCall extends Sanitizer {
|
||||
RegExpSanitizationCall() {
|
||||
exists(string calleeName, string sanitize, string regexp |
|
||||
calleeName = this.asExpr().(Call).getCallee().getName() and
|
||||
sanitize = "(?:escape|saniti[sz]e)" and
|
||||
regexp = "regexp?"
|
||||
|
|
||||
calleeName
|
||||
.regexpMatch("(?i)(" + sanitize + ".*" + regexp + ".*)" + "|(" + regexp + ".*" + sanitize +
|
||||
".*)")
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A taint-tracking configuration for untrusted user input used to construct regular expressions.
|
||||
*/
|
||||
class RegexInjectionConfiguration extends TaintTracking::Configuration {
|
||||
RegexInjectionConfiguration() { this = "RegexInjectionConfiguration" }
|
||||
|
||||
override predicate isSource(DataFlow::Node source) { source instanceof RemoteFlowSource }
|
||||
|
||||
override predicate isSink(DataFlow::Node sink) { sink instanceof RegexSink }
|
||||
|
||||
override predicate isSanitizer(DataFlow::Node node) { node instanceof Sanitizer }
|
||||
}
|
||||
|
||||
from DataFlow::PathNode source, DataFlow::PathNode sink, RegexInjectionConfiguration c
|
||||
where c.hasFlowPath(source, sink)
|
||||
select sink.getNode(), source, sink, "$@ is user controlled.", source.getNode(),
|
||||
"This regular expression pattern"
|
||||
@@ -73,10 +73,10 @@ class BlockStmt extends Stmt, @block {
|
||||
/** Gets the last statement in this block. */
|
||||
Stmt getLastStmt() { result = getStmt(getNumStmt() - 1) }
|
||||
|
||||
/** Gets a printable representation of this statement. May include more detail than `toString()`. */
|
||||
override string pp() { result = "{ ... }" }
|
||||
|
||||
/** This statement's Halstead ID (used to compute Halstead metrics). */
|
||||
override string toString() { result = "{ ... }" }
|
||||
|
||||
override string getHalsteadID() { result = "BlockStmt" }
|
||||
|
||||
override string getAPrimaryQlClass() { result = "BlockStmt" }
|
||||
@@ -130,14 +130,14 @@ class IfStmt extends ConditionalStmt, @ifstmt {
|
||||
/** Gets the `else` branch of this `if` statement. */
|
||||
Stmt getElse() { result.isNthChildOf(this, 2) }
|
||||
|
||||
/** Gets a printable representation of this statement. May include more detail than `toString()`. */
|
||||
override string pp() {
|
||||
result = "if (...) " + this.getThen().pp() + " else " + this.getElse().pp()
|
||||
or
|
||||
not exists(this.getElse()) and result = "if (...) " + this.getThen().pp()
|
||||
}
|
||||
|
||||
/** This statement's Halstead ID (used to compute Halstead metrics). */
|
||||
override string toString() { result = "if (...)" }
|
||||
|
||||
override string getHalsteadID() { result = "IfStmt" }
|
||||
|
||||
override string getAPrimaryQlClass() { result = "IfStmt" }
|
||||
@@ -201,10 +201,10 @@ class ForStmt extends ConditionalStmt, @forstmt {
|
||||
getCondition().getAChildExpr*() = result.getAnAccess()
|
||||
}
|
||||
|
||||
/** Gets a printable representation of this statement. May include more detail than `toString()`. */
|
||||
override string pp() { result = "for (...;...;...) " + this.getStmt().pp() }
|
||||
|
||||
/** This statement's Halstead ID (used to compute Halstead metrics). */
|
||||
override string toString() { result = "for (...;...;...)" }
|
||||
|
||||
override string getHalsteadID() { result = "ForStmt" }
|
||||
|
||||
override string getAPrimaryQlClass() { result = "ForStmt" }
|
||||
@@ -221,10 +221,10 @@ class EnhancedForStmt extends Stmt, @enhancedforstmt {
|
||||
/** Gets the body of this enhanced `for` loop. */
|
||||
Stmt getStmt() { result.getParent() = this }
|
||||
|
||||
/** Gets a printable representation of this statement. May include more detail than `toString()`. */
|
||||
override string pp() { result = "for (...) " + this.getStmt().pp() }
|
||||
override string pp() { result = "for (... : ...) " + this.getStmt().pp() }
|
||||
|
||||
override string toString() { result = "for (... : ...)" }
|
||||
|
||||
/** This statement's Halstead ID (used to compute Halstead metrics). */
|
||||
override string getHalsteadID() { result = "EnhancedForStmt" }
|
||||
|
||||
override string getAPrimaryQlClass() { result = "EnhancedForStmt" }
|
||||
@@ -244,10 +244,10 @@ class WhileStmt extends ConditionalStmt, @whilestmt {
|
||||
*/
|
||||
deprecated override Stmt getTrueSuccessor() { result = getStmt() }
|
||||
|
||||
/** Gets a printable representation of this statement. May include more detail than `toString()`. */
|
||||
override string pp() { result = "while (...) " + this.getStmt().pp() }
|
||||
|
||||
/** This statement's Halstead ID (used to compute Halstead metrics). */
|
||||
override string toString() { result = "while (...)" }
|
||||
|
||||
override string getHalsteadID() { result = "WhileStmt" }
|
||||
|
||||
override string getAPrimaryQlClass() { result = "WhileStmt" }
|
||||
@@ -267,10 +267,10 @@ class DoStmt extends ConditionalStmt, @dostmt {
|
||||
*/
|
||||
deprecated override Stmt getTrueSuccessor() { result = getStmt() }
|
||||
|
||||
/** Gets a printable representation of this statement. May include more detail than `toString()`. */
|
||||
override string pp() { result = "do " + this.getStmt().pp() + " while (...)" }
|
||||
|
||||
/** This statement's Halstead ID (used to compute Halstead metrics). */
|
||||
override string toString() { result = "do ... while (...)" }
|
||||
|
||||
override string getHalsteadID() { result = "DoStmt" }
|
||||
|
||||
override string getAPrimaryQlClass() { result = "DoStmt" }
|
||||
@@ -356,10 +356,10 @@ class TryStmt extends Stmt, @trystmt {
|
||||
result = getAResourceExpr().getVariable()
|
||||
}
|
||||
|
||||
/** Gets a printable representation of this statement. May include more detail than `toString()`. */
|
||||
override string pp() { result = "try " + this.getBlock().pp() + " catch (...)" }
|
||||
|
||||
/** This statement's Halstead ID (used to compute Halstead metrics). */
|
||||
override string toString() { result = "try ..." }
|
||||
|
||||
override string getHalsteadID() { result = "TryStmt" }
|
||||
|
||||
override string getAPrimaryQlClass() { result = "TryStmt" }
|
||||
@@ -387,10 +387,10 @@ class CatchClause extends Stmt, @catchclause {
|
||||
)
|
||||
}
|
||||
|
||||
/** Gets a printable representation of this statement. May include more detail than `toString()`. */
|
||||
override string pp() { result = "catch (...) " + this.getBlock().pp() }
|
||||
|
||||
/** This statement's Halstead ID (used to compute Halstead metrics). */
|
||||
override string toString() { result = "catch (...)" }
|
||||
|
||||
override string getHalsteadID() { result = "CatchClause" }
|
||||
|
||||
override string getAPrimaryQlClass() { result = "CatchClause" }
|
||||
@@ -422,10 +422,10 @@ class SwitchStmt extends Stmt, @switchstmt {
|
||||
/** Gets the expression of this `switch` statement. */
|
||||
Expr getExpr() { result.getParent() = this }
|
||||
|
||||
/** Gets a printable representation of this statement. May include more detail than `toString()`. */
|
||||
override string pp() { result = "switch (...)" }
|
||||
|
||||
/** This statement's Halstead ID (used to compute Halstead metrics). */
|
||||
override string toString() { result = "switch (...)" }
|
||||
|
||||
override string getHalsteadID() { result = "SwitchStmt" }
|
||||
|
||||
override string getAPrimaryQlClass() { result = "SwitchStmt" }
|
||||
@@ -485,10 +485,10 @@ class ConstCase extends SwitchCase {
|
||||
*/
|
||||
Expr getValue(int i) { result.getParent() = this and result.getIndex() = i and i >= 0 }
|
||||
|
||||
/** Gets a printable representation of this statement. May include more detail than `toString()`. */
|
||||
override string pp() { result = "case ..." }
|
||||
|
||||
/** This statement's Halstead ID (used to compute Halstead metrics). */
|
||||
override string toString() { result = "case ..." }
|
||||
|
||||
override string getHalsteadID() { result = "ConstCase" }
|
||||
|
||||
override string getAPrimaryQlClass() { result = "ConstCase" }
|
||||
@@ -498,10 +498,10 @@ class ConstCase extends SwitchCase {
|
||||
class DefaultCase extends SwitchCase {
|
||||
DefaultCase() { not exists(Expr e | e.getParent() = this | e.getIndex() >= 0) }
|
||||
|
||||
/** Gets a printable representation of this statement. May include more detail than `toString()`. */
|
||||
override string pp() { result = "default" }
|
||||
|
||||
/** This statement's Halstead ID (used to compute Halstead metrics). */
|
||||
override string toString() { result = "default" }
|
||||
|
||||
override string getHalsteadID() { result = "DefaultCase" }
|
||||
|
||||
override string getAPrimaryQlClass() { result = "DefaultCase" }
|
||||
@@ -515,10 +515,10 @@ class SynchronizedStmt extends Stmt, @synchronizedstmt {
|
||||
/** Gets the block of this `synchronized` statement. */
|
||||
Stmt getBlock() { result.getParent() = this }
|
||||
|
||||
/** Gets a printable representation of this statement. May include more detail than `toString()`. */
|
||||
override string pp() { result = "synchronized (...) " + this.getBlock().pp() }
|
||||
|
||||
/** This statement's Halstead ID (used to compute Halstead metrics). */
|
||||
override string toString() { result = "synchronized (...)" }
|
||||
|
||||
override string getHalsteadID() { result = "SynchronizedStmt" }
|
||||
|
||||
override string getAPrimaryQlClass() { result = "SynchronizedStmt" }
|
||||
@@ -529,10 +529,10 @@ class ReturnStmt extends Stmt, @returnstmt {
|
||||
/** Gets the expression returned by this `return` statement, if any. */
|
||||
Expr getResult() { result.getParent() = this }
|
||||
|
||||
/** Gets a printable representation of this statement. May include more detail than `toString()`. */
|
||||
override string pp() { result = "return ..." }
|
||||
|
||||
/** This statement's Halstead ID (used to compute Halstead metrics). */
|
||||
override string toString() { result = "return ..." }
|
||||
|
||||
override string getHalsteadID() { result = "ReturnStmt" }
|
||||
|
||||
override string getAPrimaryQlClass() { result = "ReturnStmt" }
|
||||
@@ -543,10 +543,10 @@ class ThrowStmt extends Stmt, @throwstmt {
|
||||
/** Gets the expression thrown by this `throw` statement. */
|
||||
Expr getExpr() { result.getParent() = this }
|
||||
|
||||
/** Gets a printable representation of this statement. May include more detail than `toString()`. */
|
||||
override string pp() { result = "throw ..." }
|
||||
|
||||
/** This statement's Halstead ID (used to compute Halstead metrics). */
|
||||
override string toString() { result = "throw ..." }
|
||||
|
||||
override string getHalsteadID() { result = "ThrowStmt" }
|
||||
|
||||
/** Gets the type of the expression thrown by this `throw` statement. */
|
||||
@@ -638,12 +638,12 @@ class BreakStmt extends Stmt, @breakstmt {
|
||||
/** Holds if this `break` statement has an explicit label. */
|
||||
predicate hasLabel() { exists(string s | s = this.getLabel()) }
|
||||
|
||||
/** Gets a printable representation of this statement. May include more detail than `toString()`. */
|
||||
override string pp() {
|
||||
if this.hasLabel() then result = "break " + this.getLabel() else result = "break"
|
||||
}
|
||||
|
||||
/** This statement's Halstead ID (used to compute Halstead metrics). */
|
||||
override string toString() { result = "break" }
|
||||
|
||||
override string getHalsteadID() { result = "BreakStmt" }
|
||||
|
||||
override string getAPrimaryQlClass() { result = "BreakStmt" }
|
||||
@@ -660,6 +660,8 @@ class YieldStmt extends Stmt, @yieldstmt {
|
||||
|
||||
override string pp() { result = "yield ..." }
|
||||
|
||||
override string toString() { result = "yield ..." }
|
||||
|
||||
override string getHalsteadID() { result = "YieldStmt" }
|
||||
|
||||
override string getAPrimaryQlClass() { result = "YieldStmt" }
|
||||
@@ -673,12 +675,12 @@ class ContinueStmt extends Stmt, @continuestmt {
|
||||
/** Holds if this `continue` statement has an explicit label. */
|
||||
predicate hasLabel() { exists(string s | s = this.getLabel()) }
|
||||
|
||||
/** Gets a printable representation of this statement. May include more detail than `toString()`. */
|
||||
override string pp() {
|
||||
if this.hasLabel() then result = "continue " + this.getLabel() else result = "continue"
|
||||
}
|
||||
|
||||
/** This statement's Halstead ID (used to compute Halstead metrics). */
|
||||
override string toString() { result = "continue" }
|
||||
|
||||
override string getHalsteadID() { result = "ContinueStmt" }
|
||||
|
||||
override string getAPrimaryQlClass() { result = "ContinueStmt" }
|
||||
@@ -686,10 +688,10 @@ class ContinueStmt extends Stmt, @continuestmt {
|
||||
|
||||
/** The empty statement. */
|
||||
class EmptyStmt extends Stmt, @emptystmt {
|
||||
/** Gets a printable representation of this statement. May include more detail than `toString()`. */
|
||||
override string pp() { result = ";" }
|
||||
|
||||
/** This statement's Halstead ID (used to compute Halstead metrics). */
|
||||
override string toString() { result = ";" }
|
||||
|
||||
override string getHalsteadID() { result = "EmptyStmt" }
|
||||
|
||||
override string getAPrimaryQlClass() { result = "EmptyStmt" }
|
||||
@@ -704,10 +706,10 @@ class ExprStmt extends Stmt, @exprstmt {
|
||||
/** Gets the expression of this expression statement. */
|
||||
Expr getExpr() { result.getParent() = this }
|
||||
|
||||
/** Gets a printable representation of this statement. May include more detail than `toString()`. */
|
||||
override string pp() { result = "...;" }
|
||||
override string pp() { result = "<Expr>;" }
|
||||
|
||||
override string toString() { result = "<Expr>;" }
|
||||
|
||||
/** This statement's Halstead ID (used to compute Halstead metrics). */
|
||||
override string getHalsteadID() { result = "ExprStmt" }
|
||||
|
||||
/** Holds if this statement represents a field declaration with an initializer. */
|
||||
@@ -733,13 +735,13 @@ class LabeledStmt extends Stmt, @labeledstmt {
|
||||
/** Gets the label of this labeled statement. */
|
||||
string getLabel() { namestrings(result, _, this) }
|
||||
|
||||
/** Gets a printable representation of this statement. May include more detail than `toString()`. */
|
||||
override string pp() { result = this.getLabel() + ": " + this.getStmt().pp() }
|
||||
|
||||
/** This statement's Halstead ID (used to compute Halstead metrics). */
|
||||
override string getHalsteadID() { result = this.getLabel() + ":" }
|
||||
|
||||
override string getAPrimaryQlClass() { result = "LabelStmt" }
|
||||
override string toString() { result = "<Label>: ..." }
|
||||
|
||||
override string getAPrimaryQlClass() { result = "LabeledStmt" }
|
||||
}
|
||||
|
||||
/** An `assert` statement. */
|
||||
@@ -750,12 +752,12 @@ class AssertStmt extends Stmt, @assertstmt {
|
||||
/** Gets the assertion message expression, if any. */
|
||||
Expr getMessage() { exprs(result, _, _, this, _) and result.getIndex() = 1 }
|
||||
|
||||
/** Gets a printable representation of this statement. May include more detail than `toString()`. */
|
||||
override string pp() {
|
||||
if exists(this.getMessage()) then result = "assert ... : ..." else result = "assert ..."
|
||||
}
|
||||
|
||||
/** This statement's Halstead ID (used to compute Halstead metrics). */
|
||||
override string toString() { result = "assert ..." }
|
||||
|
||||
override string getHalsteadID() { result = "AssertStmt" }
|
||||
|
||||
override string getAPrimaryQlClass() { result = "AssertStmt" }
|
||||
@@ -775,10 +777,10 @@ class LocalVariableDeclStmt extends Stmt, @localvariabledeclstmt {
|
||||
/** Gets an index of a variable declared in this local variable declaration statement. */
|
||||
int getAVariableIndex() { exists(getVariable(result)) }
|
||||
|
||||
/** Gets a printable representation of this statement. May include more detail than `toString()`. */
|
||||
override string pp() { result = "local variable declaration" }
|
||||
override string pp() { result = "var ...;" }
|
||||
|
||||
override string toString() { result = "var ...;" }
|
||||
|
||||
/** This statement's Halstead ID (used to compute Halstead metrics). */
|
||||
override string getHalsteadID() { result = "LocalVariableDeclStmt" }
|
||||
|
||||
override string getAPrimaryQlClass() { result = "LocalVariableDeclStmt" }
|
||||
@@ -789,10 +791,10 @@ class LocalClassDeclStmt extends Stmt, @localclassdeclstmt {
|
||||
/** Gets the local class declared by this statement. */
|
||||
LocalClass getLocalClass() { isLocalClass(result, this) }
|
||||
|
||||
/** Gets a printable representation of this statement. May include more detail than `toString()`. */
|
||||
override string pp() { result = "local class declaration: " + this.getLocalClass().toString() }
|
||||
override string pp() { result = "class " + this.getLocalClass().toString() }
|
||||
|
||||
override string toString() { result = "class ..." }
|
||||
|
||||
/** This statement's Halstead ID (used to compute Halstead metrics). */
|
||||
override string getHalsteadID() { result = "LocalClassDeclStmt" }
|
||||
|
||||
override string getAPrimaryQlClass() { result = "LocalClassDeclStmt" }
|
||||
@@ -829,13 +831,10 @@ class ThisConstructorInvocationStmt extends Stmt, ConstructorCall, @constructori
|
||||
/** Gets the immediately enclosing statement of this constructor invocation. */
|
||||
override Stmt getEnclosingStmt() { result = this }
|
||||
|
||||
/** Gets a printable representation of this statement. May include more detail than `toString()`. */
|
||||
override string pp() { result = "this(...)" }
|
||||
|
||||
/** Gets a printable representation of this statement. */
|
||||
override string toString() { result = pp() }
|
||||
override string toString() { result = "this(...)" }
|
||||
|
||||
/** This statement's Halstead ID (used to compute Halstead metrics). */
|
||||
override string getHalsteadID() { result = "ConstructorInvocationStmt" }
|
||||
|
||||
override string getAPrimaryQlClass() { result = "ThisConstructorInvocationStmt" }
|
||||
@@ -873,13 +872,10 @@ class SuperConstructorInvocationStmt extends Stmt, ConstructorCall, @superconstr
|
||||
/** Gets the immediately enclosing statement of this constructor invocation. */
|
||||
override Stmt getEnclosingStmt() { result = this }
|
||||
|
||||
/** Gets a printable representation of this statement. May include more detail than `toString()`. */
|
||||
override string pp() { result = "super(...)" }
|
||||
|
||||
/** Gets a printable representation of this statement. */
|
||||
override string toString() { result = pp() }
|
||||
override string toString() { result = "super(...)" }
|
||||
|
||||
/** This statement's Halstead ID (used to compute Halstead metrics). */
|
||||
override string getHalsteadID() { result = "SuperConstructorInvocationStmt" }
|
||||
|
||||
override string getAPrimaryQlClass() { result = "SuperConstructorInvocationStmt" }
|
||||
|
||||
@@ -67,6 +67,8 @@
|
||||
import java
|
||||
private import semmle.code.java.dataflow.DataFlow::DataFlow
|
||||
private import internal.DataFlowPrivate
|
||||
private import internal.FlowSummaryImpl::Private::External
|
||||
private import internal.FlowSummaryImplSpecific
|
||||
private import FlowSummary
|
||||
|
||||
/**
|
||||
@@ -74,20 +76,30 @@ private import FlowSummary
|
||||
* ensuring that they are visible to the taint tracking / data flow library.
|
||||
*/
|
||||
private module Frameworks {
|
||||
private import internal.ContainerFlow
|
||||
private import semmle.code.java.frameworks.ApacheHttp
|
||||
private import semmle.code.java.frameworks.apache.Lang
|
||||
private import semmle.code.java.frameworks.guava.Guava
|
||||
private import semmle.code.java.frameworks.jackson.JacksonSerializability
|
||||
private import semmle.code.java.security.ResponseSplitting
|
||||
private import semmle.code.java.security.InformationLeak
|
||||
private import semmle.code.java.security.XSS
|
||||
private import semmle.code.java.security.LdapInjection
|
||||
private import semmle.code.java.security.XPath
|
||||
private import semmle.code.java.security.JexlInjection
|
||||
private import semmle.code.java.security.OgnlInjection
|
||||
}
|
||||
|
||||
private predicate sourceModelCsv(string row) {
|
||||
row =
|
||||
[
|
||||
// org.springframework.security.web.savedrequest.SavedRequest
|
||||
"org.springframework.security.web.savedrequest;SavedRequest;true;getRedirectUrl;;;ReturnValue;remote",
|
||||
"org.springframework.security.web.savedrequest;SavedRequest;true;getCookies;;;ReturnValue;remote",
|
||||
"org.springframework.security.web.savedrequest;SavedRequest;true;getHeaderValues;;;ReturnValue;remote",
|
||||
"org.springframework.security.web.savedrequest;SavedRequest;true;getHeaderNames;;;ReturnValue;remote",
|
||||
"org.springframework.security.web.savedrequest;SavedRequest;true;getParameterValues;;;ReturnValue;remote",
|
||||
"org.springframework.security.web.savedrequest;SavedRequest;true;getParameterMap;;;ReturnValue;remote",
|
||||
// ServletRequestGetParameterMethod
|
||||
"javax.servlet;ServletRequest;false;getParameter;(String);;ReturnValue;remote",
|
||||
"javax.servlet;ServletRequest;false;getParameterValues;(String);;ReturnValue;remote",
|
||||
@@ -351,7 +363,8 @@ private predicate summaryModel(string row) {
|
||||
any(SummaryModelCsv s).row(row)
|
||||
}
|
||||
|
||||
private predicate sourceModel(
|
||||
/** Holds if a source model exists for the given parameters. */
|
||||
predicate sourceModel(
|
||||
string namespace, string type, boolean subtypes, string name, string signature, string ext,
|
||||
string output, string kind
|
||||
) {
|
||||
@@ -369,7 +382,8 @@ private predicate sourceModel(
|
||||
)
|
||||
}
|
||||
|
||||
private predicate sinkModel(
|
||||
/** Holds if a sink model exists for the given parameters. */
|
||||
predicate sinkModel(
|
||||
string namespace, string type, boolean subtypes, string name, string signature, string ext,
|
||||
string input, string kind
|
||||
) {
|
||||
@@ -387,7 +401,8 @@ private predicate sinkModel(
|
||||
)
|
||||
}
|
||||
|
||||
private predicate summaryModel(
|
||||
/** Holds if a summary model exists for the given parameters. */
|
||||
predicate summaryModel(
|
||||
string namespace, string type, boolean subtypes, string name, string signature, string ext,
|
||||
string input, string output, string kind
|
||||
) {
|
||||
@@ -474,7 +489,7 @@ module CsvValidation {
|
||||
not namespace.regexpMatch("[a-zA-Z0-9_\\.]+") and
|
||||
msg = "Dubious namespace \"" + namespace + "\" in " + pred + " model."
|
||||
or
|
||||
not type.regexpMatch("[a-zA-Z0-9_\\$]+") and
|
||||
not type.regexpMatch("[a-zA-Z0-9_\\$<>]+") and
|
||||
msg = "Dubious type \"" + type + "\" in " + pred + " model."
|
||||
or
|
||||
not name.regexpMatch("[a-zA-Z0-9_]*") and
|
||||
@@ -492,10 +507,15 @@ module CsvValidation {
|
||||
or
|
||||
summaryModel(_, _, _, _, _, _, input, _, _) and pred = "summary"
|
||||
|
|
||||
specSplit(input, part, _) and
|
||||
not part.regexpMatch("|ReturnValue|ArrayElement|Element|MapKey|MapValue") and
|
||||
not (part = "Argument" and pred = "sink") and
|
||||
not parseArg(part, _) and
|
||||
(
|
||||
invalidSpecComponent(input, part) and
|
||||
not part = "" and
|
||||
not (part = "Argument" and pred = "sink") and
|
||||
not parseArg(part, _)
|
||||
or
|
||||
specSplit(input, part, _) and
|
||||
parseParam(part, _)
|
||||
) and
|
||||
msg = "Unrecognized input specification \"" + part + "\" in " + pred + " model."
|
||||
)
|
||||
or
|
||||
@@ -504,11 +524,9 @@ module CsvValidation {
|
||||
or
|
||||
summaryModel(_, _, _, _, _, _, _, output, _) and pred = "summary"
|
||||
|
|
||||
specSplit(output, part, _) and
|
||||
not part.regexpMatch("|ReturnValue|ArrayElement|Element|MapKey|MapValue") and
|
||||
invalidSpecComponent(output, part) and
|
||||
not part = "" and
|
||||
not (part = ["Argument", "Parameter"] and pred = "source") and
|
||||
not parseArg(part, _) and
|
||||
not parseParam(part, _) and
|
||||
msg = "Unrecognized output specification \"" + part + "\" in " + pred + " model."
|
||||
)
|
||||
or
|
||||
@@ -555,7 +573,7 @@ private RefType interpretType(string namespace, string type, boolean subtypes) {
|
||||
private string paramsStringPart(Callable c, int i) {
|
||||
i = -1 and result = "("
|
||||
or
|
||||
exists(int n, string p | c.getParameterType(n).toString() = p |
|
||||
exists(int n, string p | c.getParameterType(n).getErasure().toString() = p |
|
||||
i = 2 * n and result = p
|
||||
or
|
||||
i = 2 * n - 1 and result = "," and n != 0
|
||||
@@ -589,7 +607,8 @@ private Element interpretElement0(
|
||||
)
|
||||
}
|
||||
|
||||
private Element interpretElement(
|
||||
/** Gets the source/sink/summary element corresponding to the supplied parameters. */
|
||||
Element interpretElement(
|
||||
string namespace, string type, boolean subtypes, string name, string signature, string ext
|
||||
) {
|
||||
elementSpec(namespace, type, subtypes, name, signature, ext) and
|
||||
@@ -600,269 +619,25 @@ private Element interpretElement(
|
||||
)
|
||||
}
|
||||
|
||||
private predicate sourceElement(Element e, string output, string kind) {
|
||||
exists(
|
||||
string namespace, string type, boolean subtypes, string name, string signature, string ext
|
||||
|
|
||||
sourceModel(namespace, type, subtypes, name, signature, ext, output, kind) and
|
||||
e = interpretElement(namespace, type, subtypes, name, signature, ext)
|
||||
)
|
||||
}
|
||||
cached
|
||||
private module Cached {
|
||||
/**
|
||||
* Holds if `node` is specified as a source with the given kind in a CSV flow
|
||||
* model.
|
||||
*/
|
||||
cached
|
||||
predicate sourceNode(Node node, string kind) {
|
||||
exists(InterpretNode n | isSourceNode(n, kind) and n.asNode() = node)
|
||||
}
|
||||
|
||||
private predicate sinkElement(Element e, string input, string kind) {
|
||||
exists(
|
||||
string namespace, string type, boolean subtypes, string name, string signature, string ext
|
||||
|
|
||||
sinkModel(namespace, type, subtypes, name, signature, ext, input, kind) and
|
||||
e = interpretElement(namespace, type, subtypes, name, signature, ext)
|
||||
)
|
||||
}
|
||||
|
||||
private predicate summaryElement(Element e, string input, string output, string kind) {
|
||||
exists(
|
||||
string namespace, string type, boolean subtypes, string name, string signature, string ext
|
||||
|
|
||||
summaryModel(namespace, type, subtypes, name, signature, ext, input, output, kind) and
|
||||
e = interpretElement(namespace, type, subtypes, name, signature, ext)
|
||||
)
|
||||
}
|
||||
|
||||
private string inOutSpec() {
|
||||
sourceModel(_, _, _, _, _, _, result, _) or
|
||||
sinkModel(_, _, _, _, _, _, result, _) or
|
||||
summaryModel(_, _, _, _, _, _, result, _, _) or
|
||||
summaryModel(_, _, _, _, _, _, _, result, _)
|
||||
}
|
||||
|
||||
private predicate specSplit(string s, string c, int n) {
|
||||
inOutSpec() = s and s.splitAt(" of ", n) = c
|
||||
}
|
||||
|
||||
private predicate len(string s, int len) { len = 1 + max(int n | specSplit(s, _, n)) }
|
||||
|
||||
private string getLast(string s) {
|
||||
exists(int len |
|
||||
len(s, len) and
|
||||
specSplit(s, result, len - 1)
|
||||
)
|
||||
}
|
||||
|
||||
private predicate parseParam(string c, int n) {
|
||||
specSplit(_, c, _) and
|
||||
(
|
||||
c.regexpCapture("Parameter\\[([-0-9]+)\\]", 1).toInt() = n
|
||||
or
|
||||
exists(int n1, int n2 |
|
||||
c.regexpCapture("Parameter\\[([-0-9]+)\\.\\.([0-9]+)\\]", 1).toInt() = n1 and
|
||||
c.regexpCapture("Parameter\\[([-0-9]+)\\.\\.([0-9]+)\\]", 2).toInt() = n2 and
|
||||
n = [n1 .. n2]
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
private predicate parseArg(string c, int n) {
|
||||
specSplit(_, c, _) and
|
||||
(
|
||||
c.regexpCapture("Argument\\[([-0-9]+)\\]", 1).toInt() = n
|
||||
or
|
||||
exists(int n1, int n2 |
|
||||
c.regexpCapture("Argument\\[([-0-9]+)\\.\\.([0-9]+)\\]", 1).toInt() = n1 and
|
||||
c.regexpCapture("Argument\\[([-0-9]+)\\.\\.([0-9]+)\\]", 2).toInt() = n2 and
|
||||
n = [n1 .. n2]
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
private predicate inputNeedsReference(string c) {
|
||||
c = "Argument" or
|
||||
parseArg(c, _)
|
||||
}
|
||||
|
||||
private predicate outputNeedsReference(string c) {
|
||||
c = "Argument" or
|
||||
parseArg(c, _) or
|
||||
c = "ReturnValue"
|
||||
}
|
||||
|
||||
private predicate sourceElementRef(Top ref, string output, string kind) {
|
||||
exists(Element e |
|
||||
sourceElement(e, output, kind) and
|
||||
if outputNeedsReference(getLast(output))
|
||||
then ref.(Call).getCallee().getSourceDeclaration() = e
|
||||
else ref = e
|
||||
)
|
||||
}
|
||||
|
||||
private predicate sinkElementRef(Top ref, string input, string kind) {
|
||||
exists(Element e |
|
||||
sinkElement(e, input, kind) and
|
||||
if inputNeedsReference(getLast(input))
|
||||
then ref.(Call).getCallee().getSourceDeclaration() = e
|
||||
else ref = e
|
||||
)
|
||||
}
|
||||
|
||||
private predicate summaryElementRef(Top ref, string input, string output, string kind) {
|
||||
exists(Element e |
|
||||
summaryElement(e, input, output, kind) and
|
||||
if inputNeedsReference(getLast(input))
|
||||
then ref.(Call).getCallee().getSourceDeclaration() = e
|
||||
else ref = e
|
||||
)
|
||||
}
|
||||
|
||||
private SummaryComponent interpretComponent(string c) {
|
||||
specSplit(_, c, _) and
|
||||
(
|
||||
exists(int pos | parseArg(c, pos) and result = SummaryComponent::argument(pos))
|
||||
or
|
||||
exists(int pos | parseParam(c, pos) and result = SummaryComponent::parameter(pos))
|
||||
or
|
||||
c = "ReturnValue" and result = SummaryComponent::return()
|
||||
or
|
||||
c = "ArrayElement" and result = SummaryComponent::content(any(ArrayContent c0))
|
||||
or
|
||||
c = "Element" and result = SummaryComponent::content(any(CollectionContent c0))
|
||||
or
|
||||
c = "MapKey" and result = SummaryComponent::content(any(MapKeyContent c0))
|
||||
or
|
||||
c = "MapValue" and result = SummaryComponent::content(any(MapValueContent c0))
|
||||
)
|
||||
}
|
||||
|
||||
private predicate interpretSpec(string spec, int idx, SummaryComponentStack stack) {
|
||||
exists(string c |
|
||||
summaryElement(_, spec, _, _) or
|
||||
summaryElement(_, _, spec, _)
|
||||
|
|
||||
len(spec, idx + 1) and
|
||||
specSplit(spec, c, idx) and
|
||||
stack = SummaryComponentStack::singleton(interpretComponent(c))
|
||||
)
|
||||
or
|
||||
exists(SummaryComponent head, SummaryComponentStack tail |
|
||||
interpretSpec(spec, idx, head, tail) and
|
||||
stack = SummaryComponentStack::push(head, tail)
|
||||
)
|
||||
}
|
||||
|
||||
private predicate interpretSpec(
|
||||
string output, int idx, SummaryComponent head, SummaryComponentStack tail
|
||||
) {
|
||||
exists(string c |
|
||||
interpretSpec(output, idx + 1, tail) and
|
||||
specSplit(output, c, idx) and
|
||||
head = interpretComponent(c)
|
||||
)
|
||||
}
|
||||
|
||||
private class MkStack extends RequiredSummaryComponentStack {
|
||||
MkStack() { interpretSpec(_, _, _, this) }
|
||||
|
||||
override predicate required(SummaryComponent c) { interpretSpec(_, _, c, this) }
|
||||
}
|
||||
|
||||
private class SummarizedCallableExternal extends SummarizedCallable {
|
||||
SummarizedCallableExternal() { summaryElement(this, _, _, _) }
|
||||
|
||||
override predicate propagatesFlow(
|
||||
SummaryComponentStack input, SummaryComponentStack output, boolean preservesValue
|
||||
) {
|
||||
exists(string inSpec, string outSpec, string kind |
|
||||
summaryElement(this, inSpec, outSpec, kind) and
|
||||
interpretSpec(inSpec, 0, input) and
|
||||
interpretSpec(outSpec, 0, output)
|
||||
|
|
||||
kind = "value" and preservesValue = true
|
||||
or
|
||||
kind = "taint" and preservesValue = false
|
||||
)
|
||||
/**
|
||||
* Holds if `node` is specified as a sink with the given kind in a CSV flow
|
||||
* model.
|
||||
*/
|
||||
cached
|
||||
predicate sinkNode(Node node, string kind) {
|
||||
exists(InterpretNode n | isSinkNode(n, kind) and n.asNode() = node)
|
||||
}
|
||||
}
|
||||
|
||||
private newtype TAstOrNode =
|
||||
TAst(Top t) or
|
||||
TNode(Node n)
|
||||
|
||||
private predicate interpretOutput(string output, int idx, Top ref, TAstOrNode node) {
|
||||
(
|
||||
sourceElementRef(ref, output, _) or
|
||||
summaryElementRef(ref, _, output, _)
|
||||
) and
|
||||
len(output, idx) and
|
||||
node = TAst(ref)
|
||||
or
|
||||
exists(Top mid, string c, Node n |
|
||||
interpretOutput(output, idx + 1, ref, TAst(mid)) and
|
||||
specSplit(output, c, idx) and
|
||||
node = TNode(n)
|
||||
|
|
||||
exists(int pos | n.(PostUpdateNode).getPreUpdateNode().(ArgumentNode).argumentOf(mid, pos) |
|
||||
c = "Argument" or parseArg(c, pos)
|
||||
)
|
||||
or
|
||||
exists(int pos | n.(ParameterNode).isParameterOf(mid, pos) |
|
||||
c = "Parameter" or parseParam(c, pos)
|
||||
)
|
||||
or
|
||||
(c = "Parameter" or c = "") and
|
||||
n.asParameter() = mid
|
||||
or
|
||||
c = "ReturnValue" and
|
||||
n.asExpr().(Call) = mid
|
||||
or
|
||||
c = "" and
|
||||
n.asExpr().(FieldRead).getField() = mid
|
||||
)
|
||||
}
|
||||
|
||||
private predicate interpretInput(string input, int idx, Top ref, TAstOrNode node) {
|
||||
(
|
||||
sinkElementRef(ref, input, _) or
|
||||
summaryElementRef(ref, input, _, _)
|
||||
) and
|
||||
len(input, idx) and
|
||||
node = TAst(ref)
|
||||
or
|
||||
exists(Top mid, string c, Node n |
|
||||
interpretInput(input, idx + 1, ref, TAst(mid)) and
|
||||
specSplit(input, c, idx) and
|
||||
node = TNode(n)
|
||||
|
|
||||
exists(int pos | n.(ArgumentNode).argumentOf(mid, pos) | c = "Argument" or parseArg(c, pos))
|
||||
or
|
||||
exists(ReturnStmt ret |
|
||||
c = "ReturnValue" and
|
||||
n.asExpr() = ret.getResult() and
|
||||
mid = ret.getEnclosingCallable()
|
||||
)
|
||||
or
|
||||
exists(FieldWrite fw |
|
||||
c = "" and
|
||||
fw.getField() = mid and
|
||||
n.asExpr() = fw.getRHS()
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `node` is specified as a source with the given kind in a CSV flow
|
||||
* model.
|
||||
*/
|
||||
predicate sourceNode(Node node, string kind) {
|
||||
exists(Top ref, string output |
|
||||
sourceElementRef(ref, output, kind) and
|
||||
interpretOutput(output, 0, ref, TNode(node))
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `node` is specified as a sink with the given kind in a CSV flow
|
||||
* model.
|
||||
*/
|
||||
predicate sinkNode(Node node, string kind) {
|
||||
exists(Top ref, string input |
|
||||
sinkElementRef(ref, input, kind) and
|
||||
interpretInput(input, 0, ref, TNode(node))
|
||||
)
|
||||
}
|
||||
import Cached
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
import java
|
||||
import semmle.code.java.Collections
|
||||
import semmle.code.java.Maps
|
||||
private import semmle.code.java.dataflow.SSA
|
||||
private import DataFlowUtil
|
||||
private import semmle.code.java.dataflow.ExternalFlow
|
||||
|
||||
private class EntryType extends RefType {
|
||||
EntryType() {
|
||||
@@ -88,6 +91,286 @@ class ContainerType extends RefType {
|
||||
}
|
||||
}
|
||||
|
||||
private class ContainerFlowSummaries extends SummaryModelCsv {
|
||||
override predicate row(string row) {
|
||||
row =
|
||||
[
|
||||
"java.util;Map<>$Entry;true;getValue;;;MapValue of Argument[-1];ReturnValue;value",
|
||||
"java.util;Map<>$Entry;true;setValue;;;MapValue of Argument[-1];ReturnValue;value",
|
||||
"java.util;Map<>$Entry;true;setValue;;;Argument[0];MapValue of Argument[-1];value",
|
||||
"java.lang;Iterable;true;iterator;();;Element of Argument[-1];Element of ReturnValue;value",
|
||||
"java.lang;Iterable;true;spliterator;();;Element of Argument[-1];Element of ReturnValue;value",
|
||||
"java.util;Iterator;true;next;;;Element of Argument[-1];ReturnValue;value",
|
||||
"java.util;ListIterator;true;previous;;;Element of Argument[-1];ReturnValue;value",
|
||||
"java.util;ListIterator;true;add;(Object);;Argument[0];Element of Argument[-1];value",
|
||||
"java.util;ListIterator;true;set;(Object);;Argument[0];Element of Argument[-1];value",
|
||||
"java.util;Enumeration;true;asIterator;;;Element of Argument[-1];Element of ReturnValue;value",
|
||||
"java.util;Enumeration;true;nextElement;;;Element of Argument[-1];ReturnValue;value",
|
||||
"java.util;Map;true;computeIfAbsent;;;MapValue of Argument[-1];ReturnValue;value",
|
||||
"java.util;Map;true;computeIfAbsent;;;ReturnValue of Argument[1];ReturnValue;value",
|
||||
"java.util;Map;true;computeIfAbsent;;;ReturnValue of Argument[1];MapValue of Argument[-1];value",
|
||||
"java.util;Map;true;entrySet;;;MapValue of Argument[-1];MapValue of Element of ReturnValue;value",
|
||||
"java.util;Map;true;entrySet;;;MapKey of Argument[-1];MapKey of Element of ReturnValue;value",
|
||||
"java.util;Map;true;get;;;MapValue of Argument[-1];ReturnValue;value",
|
||||
"java.util;Map;true;getOrDefault;;;MapValue of Argument[-1];ReturnValue;value",
|
||||
"java.util;Map;true;getOrDefault;;;Argument[1];ReturnValue;value",
|
||||
"java.util;Map;true;put;;;MapValue of Argument[-1];ReturnValue;value",
|
||||
"java.util;Map;true;put;;;Argument[0];MapKey of Argument[-1];value",
|
||||
"java.util;Map;true;put;;;Argument[1];MapValue of Argument[-1];value",
|
||||
"java.util;Map;true;putIfAbsent;;;MapValue of Argument[-1];ReturnValue;value",
|
||||
"java.util;Map;true;putIfAbsent;;;Argument[0];MapKey of Argument[-1];value",
|
||||
"java.util;Map;true;putIfAbsent;;;Argument[1];MapValue of Argument[-1];value",
|
||||
"java.util;Map;true;remove;(Object);;MapValue of Argument[-1];ReturnValue;value",
|
||||
"java.util;Map;true;replace;(Object,Object);;MapValue of Argument[-1];ReturnValue;value",
|
||||
"java.util;Map;true;replace;(Object,Object);;Argument[0];MapKey of Argument[-1];value",
|
||||
"java.util;Map;true;replace;(Object,Object);;Argument[1];MapValue of Argument[-1];value",
|
||||
"java.util;Map;true;replace;(Object,Object,Object);;Argument[0];MapKey of Argument[-1];value",
|
||||
"java.util;Map;true;replace;(Object,Object,Object);;Argument[2];MapValue of Argument[-1];value",
|
||||
"java.util;Map;true;keySet;();;MapKey of Argument[-1];Element of ReturnValue;value",
|
||||
"java.util;Map;true;values;();;MapValue of Argument[-1];Element of ReturnValue;value",
|
||||
"java.util;Map;true;merge;(Object,Object,BiFunction);;Argument[1];MapValue of Argument[-1];value",
|
||||
"java.util;Map;true;putAll;(Map);;MapKey of Argument[0];MapKey of Argument[-1];value",
|
||||
"java.util;Map;true;putAll;(Map);;MapValue of Argument[0];MapValue of Argument[-1];value",
|
||||
"java.util;Collection;true;parallelStream;();;Element of Argument[-1];Element of ReturnValue;value",
|
||||
"java.util;Collection;true;stream;();;Element of Argument[-1];Element of ReturnValue;value",
|
||||
"java.util;Collection;true;toArray;;;Element of Argument[-1];ArrayElement of ReturnValue;value",
|
||||
"java.util;Collection;true;toArray;;;Element of Argument[-1];ArrayElement of Argument[0];value",
|
||||
"java.util;Collection;true;add;;;Argument[0];Element of Argument[-1];value",
|
||||
"java.util;Collection;true;addAll;;;Element of Argument[0];Element of Argument[-1];value",
|
||||
"java.util;List;true;get;(int);;Element of Argument[-1];ReturnValue;value",
|
||||
"java.util;List;true;listIterator;;;Element of Argument[-1];Element of ReturnValue;value",
|
||||
"java.util;List;true;remove;(int);;Element of Argument[-1];ReturnValue;value",
|
||||
"java.util;List;true;set;(int,Object);;Element of Argument[-1];ReturnValue;value",
|
||||
"java.util;List;true;set;(int,Object);;Argument[1];Element of Argument[-1];value",
|
||||
"java.util;List;true;subList;;;Element of Argument[-1];Element of ReturnValue;value",
|
||||
"java.util;List;true;add;(int,Object);;Argument[1];Element of Argument[-1];value",
|
||||
"java.util;List;true;addAll;(int,Collection);;Element of Argument[1];Element of Argument[-1];value",
|
||||
"java.util;Vector;true;elementAt;(int);;Element of Argument[-1];ReturnValue;value",
|
||||
"java.util;Vector;true;elements;();;Element of Argument[-1];Element of ReturnValue;value",
|
||||
"java.util;Vector;true;firstElement;();;Element of Argument[-1];ReturnValue;value",
|
||||
"java.util;Vector;true;lastElement;();;Element of Argument[-1];ReturnValue;value",
|
||||
"java.util;Vector;true;addElement;(Object);;Argument[0];Element of Argument[-1];value",
|
||||
"java.util;Vector;true;insertElementAt;(Object,int);;Argument[0];Element of Argument[-1];value",
|
||||
"java.util;Vector;true;setElementAt;(Object,int);;Argument[0];Element of Argument[-1];value",
|
||||
"java.util;Vector;true;copyInto;(Object[]);;Element of Argument[-1];ArrayElement of Argument[0];value",
|
||||
"java.util;Stack;true;peek;();;Element of Argument[-1];ReturnValue;value",
|
||||
"java.util;Stack;true;pop;();;Element of Argument[-1];ReturnValue;value",
|
||||
"java.util;Stack;true;push;(Object);;Argument[0];Element of Argument[-1];value",
|
||||
"java.util;Queue;true;element;();;Element of Argument[-1];ReturnValue;value",
|
||||
"java.util;Queue;true;peek;();;Element of Argument[-1];ReturnValue;value",
|
||||
"java.util;Queue;true;poll;();;Element of Argument[-1];ReturnValue;value",
|
||||
"java.util;Queue;true;remove;();;Element of Argument[-1];ReturnValue;value",
|
||||
"java.util;Queue;true;offer;(Object);;Argument[0];Element of Argument[-1];value",
|
||||
"java.util;Deque;true;descendingIterator;();;Element of Argument[-1];Element of ReturnValue;value",
|
||||
"java.util;Deque;true;getFirst;();;Element of Argument[-1];ReturnValue;value",
|
||||
"java.util;Deque;true;getLast;();;Element of Argument[-1];ReturnValue;value",
|
||||
"java.util;Deque;true;peekFirst;();;Element of Argument[-1];ReturnValue;value",
|
||||
"java.util;Deque;true;peekLast;();;Element of Argument[-1];ReturnValue;value",
|
||||
"java.util;Deque;true;pollFirst;();;Element of Argument[-1];ReturnValue;value",
|
||||
"java.util;Deque;true;pollLast;();;Element of Argument[-1];ReturnValue;value",
|
||||
"java.util;Deque;true;pop;();;Element of Argument[-1];ReturnValue;value",
|
||||
"java.util;Deque;true;removeFirst;();;Element of Argument[-1];ReturnValue;value",
|
||||
"java.util;Deque;true;removeLast;();;Element of Argument[-1];ReturnValue;value",
|
||||
"java.util;Deque;true;push;(Object);;Argument[0];Element of Argument[-1];value",
|
||||
"java.util;Deque;true;offerLast;(Object);;Argument[0];Element of Argument[-1];value",
|
||||
"java.util;Deque;true;offerFirst;(Object);;Argument[0];Element of Argument[-1];value",
|
||||
"java.util;Deque;true;addLast;(Object);;Argument[0];Element of Argument[-1];value",
|
||||
"java.util;Deque;true;addFirst;(Object);;Argument[0];Element of Argument[-1];value",
|
||||
"java.util.concurrent;BlockingDeque;true;pollFirst;(long,TimeUnit);;Element of Argument[-1];ReturnValue;value",
|
||||
"java.util.concurrent;BlockingDeque;true;pollLast;(long,TimeUnit);;Element of Argument[-1];ReturnValue;value",
|
||||
"java.util.concurrent;BlockingDeque;true;takeFirst;();;Element of Argument[-1];ReturnValue;value",
|
||||
"java.util.concurrent;BlockingDeque;true;takeLast;();;Element of Argument[-1];ReturnValue;value",
|
||||
"java.util.concurrent;BlockingQueue;true;poll;(long,TimeUnit);;Element of Argument[-1];ReturnValue;value",
|
||||
"java.util.concurrent;BlockingQueue;true;take;();;Element of Argument[-1];ReturnValue;value",
|
||||
"java.util.concurrent;BlockingQueue;true;offer;(Object,long,TimeUnit);;Argument[0];Element of Argument[-1];value",
|
||||
"java.util.concurrent;BlockingQueue;true;put;(Object);;Argument[0];Element of Argument[-1];value",
|
||||
"java.util.concurrent;BlockingDeque;true;offerLast;(Object,long,TimeUnit);;Argument[0];Element of Argument[-1];value",
|
||||
"java.util.concurrent;BlockingDeque;true;offerFirst;(Object,long,TimeUnit);;Argument[0];Element of Argument[-1];value",
|
||||
"java.util.concurrent;BlockingDeque;true;putLast;(Object);;Argument[0];Element of Argument[-1];value",
|
||||
"java.util.concurrent;BlockingDeque;true;putFirst;(Object);;Argument[0];Element of Argument[-1];value",
|
||||
"java.util.concurrent;BlockingQueue;true;drainTo;(Collection,int);;Element of Argument[-1];Element of Argument[0];value",
|
||||
"java.util.concurrent;BlockingQueue;true;drainTo;(Collection);;Element of Argument[-1];Element of Argument[0];value",
|
||||
"java.util.concurrent;ConcurrentHashMap;true;elements;();;MapValue of Argument[-1];Element of ReturnValue;value",
|
||||
"java.util;Dictionary;true;elements;();;MapValue of Argument[-1];Element of ReturnValue;value",
|
||||
"java.util;Dictionary;true;get;(Object);;MapValue of Argument[-1];ReturnValue;value",
|
||||
"java.util;Dictionary;true;put;(Object,Object);;MapValue of Argument[-1];ReturnValue;value",
|
||||
"java.util;Dictionary;true;put;(Object,Object);;Argument[0];MapKey of Argument[-1];value",
|
||||
"java.util;Dictionary;true;put;(Object,Object);;Argument[1];MapValue of Argument[-1];value",
|
||||
"java.util;Dictionary;true;remove;(Object);;MapValue of Argument[-1];ReturnValue;value",
|
||||
"java.util;NavigableMap;true;ceilingEntry;(Object);;MapKey of Argument[-1];MapKey of ReturnValue;value",
|
||||
"java.util;NavigableMap;true;ceilingEntry;(Object);;MapValue of Argument[-1];MapValue of ReturnValue;value",
|
||||
"java.util;NavigableMap;true;descendingMap;();;MapKey of Argument[-1];MapKey of ReturnValue;value",
|
||||
"java.util;NavigableMap;true;descendingMap;();;MapValue of Argument[-1];MapValue of ReturnValue;value",
|
||||
"java.util;NavigableMap;true;firstEntry;();;MapKey of Argument[-1];MapKey of ReturnValue;value",
|
||||
"java.util;NavigableMap;true;firstEntry;();;MapValue of Argument[-1];MapValue of ReturnValue;value",
|
||||
"java.util;NavigableMap;true;floorEntry;(Object);;MapKey of Argument[-1];MapKey of ReturnValue;value",
|
||||
"java.util;NavigableMap;true;floorEntry;(Object);;MapValue of Argument[-1];MapValue of ReturnValue;value",
|
||||
"java.util;NavigableMap;true;headMap;(Object,boolean);;MapKey of Argument[-1];MapKey of ReturnValue;value",
|
||||
"java.util;NavigableMap;true;headMap;(Object,boolean);;MapValue of Argument[-1];MapValue of ReturnValue;value",
|
||||
"java.util;NavigableMap;true;higherEntry;(Object);;MapKey of Argument[-1];MapKey of ReturnValue;value",
|
||||
"java.util;NavigableMap;true;higherEntry;(Object);;MapValue of Argument[-1];MapValue of ReturnValue;value",
|
||||
"java.util;NavigableMap;true;lastEntry;();;MapKey of Argument[-1];MapKey of ReturnValue;value",
|
||||
"java.util;NavigableMap;true;lastEntry;();;MapValue of Argument[-1];MapValue of ReturnValue;value",
|
||||
"java.util;NavigableMap;true;lowerEntry;(Object);;MapKey of Argument[-1];MapKey of ReturnValue;value",
|
||||
"java.util;NavigableMap;true;lowerEntry;(Object);;MapValue of Argument[-1];MapValue of ReturnValue;value",
|
||||
"java.util;NavigableMap;true;pollFirstEntry;();;MapKey of Argument[-1];MapKey of ReturnValue;value",
|
||||
"java.util;NavigableMap;true;pollFirstEntry;();;MapValue of Argument[-1];MapValue of ReturnValue;value",
|
||||
"java.util;NavigableMap;true;pollLastEntry;();;MapKey of Argument[-1];MapKey of ReturnValue;value",
|
||||
"java.util;NavigableMap;true;pollLastEntry;();;MapValue of Argument[-1];MapValue of ReturnValue;value",
|
||||
"java.util;NavigableMap;true;subMap;(Object,boolean,Object,boolean);;MapKey of Argument[-1];MapKey of ReturnValue;value",
|
||||
"java.util;NavigableMap;true;subMap;(Object,boolean,Object,boolean);;MapValue of Argument[-1];MapValue of ReturnValue;value",
|
||||
"java.util;NavigableMap;true;tailMap;(Object,boolean);;MapKey of Argument[-1];MapKey of ReturnValue;value",
|
||||
"java.util;NavigableMap;true;tailMap;(Object,boolean);;MapValue of Argument[-1];MapValue of ReturnValue;value",
|
||||
"java.util;NavigableSet;true;ceiling;(Object);;Element of Argument[-1];ReturnValue;value",
|
||||
"java.util;NavigableSet;true;descendingIterator;();;Element of Argument[-1];Element of ReturnValue;value",
|
||||
"java.util;NavigableSet;true;descendingSet;();;Element of Argument[-1];Element of ReturnValue;value",
|
||||
"java.util;NavigableSet;true;floor;(Object);;Element of Argument[-1];ReturnValue;value",
|
||||
"java.util;NavigableSet;true;headSet;(Object,boolean);;Element of Argument[-1];Element of ReturnValue;value",
|
||||
"java.util;NavigableSet;true;higher;(Object);;Element of Argument[-1];ReturnValue;value",
|
||||
"java.util;NavigableSet;true;lower;(Object);;Element of Argument[-1];ReturnValue;value",
|
||||
"java.util;NavigableSet;true;pollFirst;();;Element of Argument[-1];ReturnValue;value",
|
||||
"java.util;NavigableSet;true;pollLast;();;Element of Argument[-1];ReturnValue;value",
|
||||
"java.util;NavigableSet;true;subSet;(Object,boolean,Object,boolean);;Element of Argument[-1];Element of ReturnValue;value",
|
||||
"java.util;NavigableSet;true;tailSet;(Object,boolean);;Element of Argument[-1];Element of ReturnValue;value",
|
||||
"java.util;Scanner;true;next;(Pattern);;Argument[-1];ReturnValue;taint",
|
||||
"java.util;Scanner;true;next;(String);;Argument[-1];ReturnValue;taint",
|
||||
"java.util;SortedMap;true;headMap;(Object);;MapKey of Argument[-1];MapKey of ReturnValue;value",
|
||||
"java.util;SortedMap;true;headMap;(Object);;MapValue of Argument[-1];MapValue of ReturnValue;value",
|
||||
"java.util;SortedMap;true;subMap;(Object,Object);;MapKey of Argument[-1];MapKey of ReturnValue;value",
|
||||
"java.util;SortedMap;true;subMap;(Object,Object);;MapValue of Argument[-1];MapValue of ReturnValue;value",
|
||||
"java.util;SortedMap;true;tailMap;(Object);;MapKey of Argument[-1];MapKey of ReturnValue;value",
|
||||
"java.util;SortedMap;true;tailMap;(Object);;MapValue of Argument[-1];MapValue of ReturnValue;value",
|
||||
"java.util;SortedSet;true;first;();;Element of Argument[-1];ReturnValue;value",
|
||||
"java.util;SortedSet;true;headSet;(Object);;Element of Argument[-1];Element of ReturnValue;value",
|
||||
"java.util;SortedSet;true;last;();;Element of Argument[-1];ReturnValue;value",
|
||||
"java.util;SortedSet;true;subSet;(Object,Object);;Element of Argument[-1];Element of ReturnValue;value",
|
||||
"java.util;SortedSet;true;tailSet;(Object);;Element of Argument[-1];Element of ReturnValue;value",
|
||||
"java.util.concurrent;TransferQueue;true;tryTransfer;(Object,long,TimeUnit);;Argument[0];Element of Argument[-1];value",
|
||||
"java.util.concurrent;TransferQueue;true;transfer;(Object);;Argument[0];Element of Argument[-1];value",
|
||||
"java.util.concurrent;TransferQueue;true;tryTransfer;(Object);;Argument[0];Element of Argument[-1];value",
|
||||
"java.util;List;false;copyOf;(Collection);;Element of Argument[0];Element of ReturnValue;value",
|
||||
"java.util;List;false;of;(Object[]);;ArrayElement of Argument[0];Element of ReturnValue;value",
|
||||
"java.util;List;false;of;(Object);;Argument[0];Element of ReturnValue;value",
|
||||
"java.util;List;false;of;(Object,Object);;Argument[0..1];Element of ReturnValue;value",
|
||||
"java.util;List;false;of;(Object,Object,Object);;Argument[0..2];Element of ReturnValue;value",
|
||||
"java.util;List;false;of;(Object,Object,Object,Object);;Argument[0..3];Element of ReturnValue;value",
|
||||
"java.util;List;false;of;(Object,Object,Object,Object,Object);;Argument[0..4];Element of ReturnValue;value",
|
||||
"java.util;List;false;of;(Object,Object,Object,Object,Object,Object);;Argument[0..5];Element of ReturnValue;value",
|
||||
"java.util;List;false;of;(Object,Object,Object,Object,Object,Object,Object);;Argument[0..6];Element of ReturnValue;value",
|
||||
"java.util;List;false;of;(Object,Object,Object,Object,Object,Object,Object,Object);;Argument[0..7];Element of ReturnValue;value",
|
||||
"java.util;List;false;of;(Object,Object,Object,Object,Object,Object,Object,Object,Object);;Argument[0..8];Element of ReturnValue;value",
|
||||
"java.util;List;false;of;(Object,Object,Object,Object,Object,Object,Object,Object,Object,Object);;Argument[0..9];Element of ReturnValue;value",
|
||||
"java.util;Map;false;copyOf;(Map);;MapKey of Argument[0];MapKey of ReturnValue;value",
|
||||
"java.util;Map;false;copyOf;(Map);;MapValue of Argument[0];MapValue of ReturnValue;value",
|
||||
"java.util;Map;false;entry;(Object,Object);;Argument[0];MapKey of ReturnValue;value",
|
||||
"java.util;Map;false;entry;(Object,Object);;Argument[1];MapValue of ReturnValue;value",
|
||||
"java.util;Map;false;of;;;Argument[0];MapKey of ReturnValue;value",
|
||||
"java.util;Map;false;of;;;Argument[1];MapValue of ReturnValue;value",
|
||||
"java.util;Map;false;of;;;Argument[2];MapKey of ReturnValue;value",
|
||||
"java.util;Map;false;of;;;Argument[3];MapValue of ReturnValue;value",
|
||||
"java.util;Map;false;of;;;Argument[4];MapKey of ReturnValue;value",
|
||||
"java.util;Map;false;of;;;Argument[5];MapValue of ReturnValue;value",
|
||||
"java.util;Map;false;of;;;Argument[6];MapKey of ReturnValue;value",
|
||||
"java.util;Map;false;of;;;Argument[7];MapValue of ReturnValue;value",
|
||||
"java.util;Map;false;of;;;Argument[8];MapKey of ReturnValue;value",
|
||||
"java.util;Map;false;of;;;Argument[9];MapValue of ReturnValue;value",
|
||||
"java.util;Map;false;of;;;Argument[10];MapKey of ReturnValue;value",
|
||||
"java.util;Map;false;of;;;Argument[11];MapValue of ReturnValue;value",
|
||||
"java.util;Map;false;of;;;Argument[12];MapKey of ReturnValue;value",
|
||||
"java.util;Map;false;of;;;Argument[13];MapValue of ReturnValue;value",
|
||||
"java.util;Map;false;of;;;Argument[14];MapKey of ReturnValue;value",
|
||||
"java.util;Map;false;of;;;Argument[15];MapValue of ReturnValue;value",
|
||||
"java.util;Map;false;of;;;Argument[16];MapKey of ReturnValue;value",
|
||||
"java.util;Map;false;of;;;Argument[17];MapValue of ReturnValue;value",
|
||||
"java.util;Map;false;of;;;Argument[18];MapKey of ReturnValue;value",
|
||||
"java.util;Map;false;of;;;Argument[19];MapValue of ReturnValue;value",
|
||||
"java.util;Map;false;ofEntries;;;MapKey of ArrayElement of Argument[0];MapKey of ReturnValue;value",
|
||||
"java.util;Map;false;ofEntries;;;MapValue of ArrayElement of Argument[0];MapValue of ReturnValue;value",
|
||||
"java.util;Set;false;copyOf;(Collection);;Element of Argument[0];Element of ReturnValue;value",
|
||||
"java.util;Set;false;of;(Object[]);;ArrayElement of Argument[0];Element of ReturnValue;value",
|
||||
"java.util;Set;false;of;(Object);;Argument[0];Element of ReturnValue;value",
|
||||
"java.util;Set;false;of;(Object,Object);;Argument[0..1];Element of ReturnValue;value",
|
||||
"java.util;Set;false;of;(Object,Object,Object);;Argument[0..2];Element of ReturnValue;value",
|
||||
"java.util;Set;false;of;(Object,Object,Object,Object);;Argument[0..3];Element of ReturnValue;value",
|
||||
"java.util;Set;false;of;(Object,Object,Object,Object,Object);;Argument[0..4];Element of ReturnValue;value",
|
||||
"java.util;Set;false;of;(Object,Object,Object,Object,Object,Object);;Argument[0..5];Element of ReturnValue;value",
|
||||
"java.util;Set;false;of;(Object,Object,Object,Object,Object,Object,Object);;Argument[0..6];Element of ReturnValue;value",
|
||||
"java.util;Set;false;of;(Object,Object,Object,Object,Object,Object,Object,Object);;Argument[0..7];Element of ReturnValue;value",
|
||||
"java.util;Set;false;of;(Object,Object,Object,Object,Object,Object,Object,Object,Object);;Argument[0..8];Element of ReturnValue;value",
|
||||
"java.util;Set;false;of;(Object,Object,Object,Object,Object,Object,Object,Object,Object,Object);;Argument[0..9];Element of ReturnValue;value",
|
||||
"java.util;Arrays;false;stream;;;ArrayElement of Argument[0];Element of ReturnValue;value",
|
||||
"java.util;Arrays;false;spliterator;;;ArrayElement of Argument[0];Element of ReturnValue;value",
|
||||
"java.util;Arrays;false;copyOfRange;;;ArrayElement of Argument[0];ArrayElement of ReturnValue;value",
|
||||
"java.util;Arrays;false;copyOf;;;ArrayElement of Argument[0];ArrayElement of ReturnValue;value",
|
||||
"java.util;Collections;false;list;(Enumeration);;Element of Argument[0];Element of ReturnValue;value",
|
||||
"java.util;Collections;false;enumeration;(Collection);;Element of Argument[0];Element of ReturnValue;value",
|
||||
"java.util;Collections;false;nCopies;(int,Object);;Argument[1];Element of ReturnValue;value",
|
||||
"java.util;Collections;false;singletonMap;(Object,Object);;Argument[0];MapKey of ReturnValue;value",
|
||||
"java.util;Collections;false;singletonMap;(Object,Object);;Argument[1];MapValue of ReturnValue;value",
|
||||
"java.util;Collections;false;singletonList;(Object);;Argument[0];Element of ReturnValue;value",
|
||||
"java.util;Collections;false;singleton;(Object);;Argument[0];Element of ReturnValue;value",
|
||||
"java.util;Collections;false;checkedNavigableMap;(NavigableMap,Class,Class);;MapKey of Argument[0];MapKey of ReturnValue;value",
|
||||
"java.util;Collections;false;checkedNavigableMap;(NavigableMap,Class,Class);;MapValue of Argument[0];MapValue of ReturnValue;value",
|
||||
"java.util;Collections;false;checkedSortedMap;(SortedMap,Class,Class);;MapKey of Argument[0];MapKey of ReturnValue;value",
|
||||
"java.util;Collections;false;checkedSortedMap;(SortedMap,Class,Class);;MapValue of Argument[0];MapValue of ReturnValue;value",
|
||||
"java.util;Collections;false;checkedMap;(Map,Class,Class);;MapKey of Argument[0];MapKey of ReturnValue;value",
|
||||
"java.util;Collections;false;checkedMap;(Map,Class,Class);;MapValue of Argument[0];MapValue of ReturnValue;value",
|
||||
"java.util;Collections;false;checkedList;(List,Class);;Element of Argument[0];Element of ReturnValue;value",
|
||||
"java.util;Collections;false;checkedNavigableSet;(NavigableSet,Class);;Element of Argument[0];Element of ReturnValue;value",
|
||||
"java.util;Collections;false;checkedSortedSet;(SortedSet,Class);;Element of Argument[0];Element of ReturnValue;value",
|
||||
"java.util;Collections;false;checkedSet;(Set,Class);;Element of Argument[0];Element of ReturnValue;value",
|
||||
"java.util;Collections;false;checkedCollection;(Collection,Class);;Element of Argument[0];Element of ReturnValue;value",
|
||||
"java.util;Collections;false;synchronizedNavigableMap;(NavigableMap);;MapKey of Argument[0];MapKey of ReturnValue;value",
|
||||
"java.util;Collections;false;synchronizedNavigableMap;(NavigableMap);;MapValue of Argument[0];MapValue of ReturnValue;value",
|
||||
"java.util;Collections;false;synchronizedSortedMap;(SortedMap);;MapKey of Argument[0];MapKey of ReturnValue;value",
|
||||
"java.util;Collections;false;synchronizedSortedMap;(SortedMap);;MapValue of Argument[0];MapValue of ReturnValue;value",
|
||||
"java.util;Collections;false;synchronizedMap;(Map);;MapKey of Argument[0];MapKey of ReturnValue;value",
|
||||
"java.util;Collections;false;synchronizedMap;(Map);;MapValue of Argument[0];MapValue of ReturnValue;value",
|
||||
"java.util;Collections;false;synchronizedList;(List);;Element of Argument[0];Element of ReturnValue;value",
|
||||
"java.util;Collections;false;synchronizedNavigableSet;(NavigableSet);;Element of Argument[0];Element of ReturnValue;value",
|
||||
"java.util;Collections;false;synchronizedSortedSet;(SortedSet);;Element of Argument[0];Element of ReturnValue;value",
|
||||
"java.util;Collections;false;synchronizedSet;(Set);;Element of Argument[0];Element of ReturnValue;value",
|
||||
"java.util;Collections;false;synchronizedCollection;(Collection);;Element of Argument[0];Element of ReturnValue;value",
|
||||
"java.util;Collections;false;unmodifiableNavigableMap;(NavigableMap);;MapKey of Argument[0];MapKey of ReturnValue;value",
|
||||
"java.util;Collections;false;unmodifiableNavigableMap;(NavigableMap);;MapValue of Argument[0];MapValue of ReturnValue;value",
|
||||
"java.util;Collections;false;unmodifiableSortedMap;(SortedMap);;MapKey of Argument[0];MapKey of ReturnValue;value",
|
||||
"java.util;Collections;false;unmodifiableSortedMap;(SortedMap);;MapValue of Argument[0];MapValue of ReturnValue;value",
|
||||
"java.util;Collections;false;unmodifiableMap;(Map);;MapKey of Argument[0];MapKey of ReturnValue;value",
|
||||
"java.util;Collections;false;unmodifiableMap;(Map);;MapValue of Argument[0];MapValue of ReturnValue;value",
|
||||
"java.util;Collections;false;unmodifiableList;(List);;Element of Argument[0];Element of ReturnValue;value",
|
||||
"java.util;Collections;false;unmodifiableNavigableSet;(NavigableSet);;Element of Argument[0];Element of ReturnValue;value",
|
||||
"java.util;Collections;false;unmodifiableSortedSet;(SortedSet);;Element of Argument[0];Element of ReturnValue;value",
|
||||
"java.util;Collections;false;unmodifiableSet;(Set);;Element of Argument[0];Element of ReturnValue;value",
|
||||
"java.util;Collections;false;unmodifiableCollection;(Collection);;Element of Argument[0];Element of ReturnValue;value",
|
||||
"java.util;Collections;false;max;;;Element of Argument[0];ReturnValue;value",
|
||||
"java.util;Collections;false;min;;;Element of Argument[0];ReturnValue;value",
|
||||
"java.util;Arrays;false;fill;(Object[],int,int,Object);;Argument[3];ArrayElement of Argument[0];value",
|
||||
"java.util;Arrays;false;fill;(Object[],Object);;Argument[1];ArrayElement of Argument[0];value",
|
||||
"java.util;Arrays;false;fill;(float[],int,int,float);;Argument[3];ArrayElement of Argument[0];value",
|
||||
"java.util;Arrays;false;fill;(float[],float);;Argument[1];ArrayElement of Argument[0];value",
|
||||
"java.util;Arrays;false;fill;(double[],int,int,double);;Argument[3];ArrayElement of Argument[0];value",
|
||||
"java.util;Arrays;false;fill;(double[],double);;Argument[1];ArrayElement of Argument[0];value",
|
||||
"java.util;Arrays;false;fill;(boolean[],int,int,boolean);;Argument[3];ArrayElement of Argument[0];value",
|
||||
"java.util;Arrays;false;fill;(boolean[],boolean);;Argument[1];ArrayElement of Argument[0];value",
|
||||
"java.util;Arrays;false;fill;(byte[],int,int,byte);;Argument[3];ArrayElement of Argument[0];value",
|
||||
"java.util;Arrays;false;fill;(byte[],byte);;Argument[1];ArrayElement of Argument[0];value",
|
||||
"java.util;Arrays;false;fill;(char[],int,int,char);;Argument[3];ArrayElement of Argument[0];value",
|
||||
"java.util;Arrays;false;fill;(char[],char);;Argument[1];ArrayElement of Argument[0];value",
|
||||
"java.util;Arrays;false;fill;(short[],int,int,short);;Argument[3];ArrayElement of Argument[0];value",
|
||||
"java.util;Arrays;false;fill;(short[],short);;Argument[1];ArrayElement of Argument[0];value",
|
||||
"java.util;Arrays;false;fill;(int[],int,int,int);;Argument[3];ArrayElement of Argument[0];value",
|
||||
"java.util;Arrays;false;fill;(int[],int);;Argument[1];ArrayElement of Argument[0];value",
|
||||
"java.util;Arrays;false;fill;(long[],int,int,long);;Argument[3];ArrayElement of Argument[0];value",
|
||||
"java.util;Arrays;false;fill;(long[],long);;Argument[1];ArrayElement of Argument[0];value",
|
||||
"java.util;Collections;false;replaceAll;(List,Object,Object);;Argument[2];Element of Argument[0];value",
|
||||
"java.util;Collections;false;copy;(List,List);;Element of Argument[1];Element of Argument[0];value",
|
||||
"java.util;Collections;false;fill;(List,Object);;Argument[1];Element of Argument[0];value",
|
||||
"java.util;Arrays;false;asList;;;ArrayElement of Argument[0];Element of ReturnValue;value",
|
||||
"java.util;Collections;false;addAll;(Collection,Object[]);;ArrayElement of Argument[1];Element of Argument[0];value"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
private predicate taintPreservingQualifierToMethod(Method m) {
|
||||
// java.util.Map.Entry
|
||||
m.getDeclaringType() instanceof EntryType and
|
||||
@@ -426,3 +709,58 @@ predicate containerStep(Expr n1, Expr n2) {
|
||||
containerReturnValueStep(n1, n2) or
|
||||
containerUpdateStep(n1, n2)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if the step from `node1` to `node2` stores a value in an array.
|
||||
* This covers array assignments and initializers as well as implicit array
|
||||
* creations for varargs.
|
||||
*/
|
||||
predicate arrayStoreStep(Node node1, Node node2) {
|
||||
exists(Argument arg |
|
||||
node1.asExpr() = arg and
|
||||
arg.isVararg() and
|
||||
node2.(ImplicitVarargsArray).getCall() = arg.getCall()
|
||||
)
|
||||
or
|
||||
node2.asExpr().(ArrayInit).getAnInit() = node1.asExpr()
|
||||
or
|
||||
exists(Assignment assign | assign.getSource() = node1.asExpr() |
|
||||
node2.(PostUpdateNode).getPreUpdateNode().asExpr() = assign.getDest().(ArrayAccess).getArray()
|
||||
)
|
||||
}
|
||||
|
||||
private predicate enhancedForStmtStep(Node node1, Node node2, Type containerType) {
|
||||
exists(EnhancedForStmt for, Expr e, SsaExplicitUpdate v |
|
||||
for.getExpr() = e and
|
||||
node1.asExpr() = e and
|
||||
containerType = e.getType() and
|
||||
v.getDefiningExpr() = for.getVariable() and
|
||||
v.getAFirstUse() = node2.asExpr()
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if the step from `node1` to `node2` reads a value from an array.
|
||||
* This covers ordinary array reads as well as array iteration through enhanced
|
||||
* `for` statements.
|
||||
*/
|
||||
predicate arrayReadStep(Node node1, Node node2, Type elemType) {
|
||||
exists(ArrayAccess aa |
|
||||
aa.getArray() = node1.asExpr() and
|
||||
aa.getType() = elemType and
|
||||
node2.asExpr() = aa
|
||||
)
|
||||
or
|
||||
exists(Array arr |
|
||||
enhancedForStmtStep(node1, node2, arr) and
|
||||
arr.getComponentType() = elemType
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if the step from `node1` to `node2` reads a value from a collection.
|
||||
* This only covers iteration through enhanced `for` statements.
|
||||
*/
|
||||
predicate collectionReadStep(Node node1, Node node2) {
|
||||
enhancedForStmtStep(node1, node2, any(Type t | not t instanceof Array))
|
||||
}
|
||||
|
||||
@@ -106,7 +106,7 @@ private module DispatchImpl {
|
||||
mayBenefitFromCallContext(ma, c, i) and
|
||||
c = viableCallable(ctx) and
|
||||
contextArgHasType(ctx, i, t, exact) and
|
||||
ma.getMethod() = def
|
||||
ma.getMethod().getSourceDeclaration() = def
|
||||
|
|
||||
exact = true and result = VirtualDispatch::exactMethodImpl(def, t.getSourceDeclaration())
|
||||
or
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user