mirror of
https://github.com/github/codeql.git
synced 2026-05-02 04:05:14 +02:00
Merge branch 'main' into atorralba/promote-ognl-injection
This commit is contained in:
@@ -0,0 +1,3 @@
|
||||
lgtm,codescanning
|
||||
* The "Deserialization of user-controlled data" (`java/unsafe-deserialization`) query
|
||||
now recognizes `Jackson` deserialization.
|
||||
@@ -0,0 +1,2 @@
|
||||
lgtm,codescanning
|
||||
* Added additional taint steps modeling constructors for collections in `java.util`.
|
||||
@@ -18,7 +18,7 @@ java.lang,,,3,,,,,,,,,,,,,,,1,2
|
||||
java.net,10,3,6,,,,,,,10,,,,,,,3,6,
|
||||
java.nio,10,,2,,10,,,,,,,,,,,,,2,
|
||||
java.sql,7,,,,,,,,,,,7,,,,,,,
|
||||
java.util,,,295,,,,,,,,,,,,,,,15,280
|
||||
java.util,,,332,,,,,,,,,,,,,,,15,317
|
||||
javax.json,,,123,,,,,,,,,,,,,,,100,23
|
||||
javax.naming.directory,1,,,,,,,,1,,,,,,,,,,
|
||||
javax.net.ssl,2,,,,,,,,,,2,,,,,,,,
|
||||
|
||||
|
@@ -14,9 +14,9 @@ Java framework & library support
|
||||
`Apache Commons Text <https://commons.apache.org/proper/commons-text/>`_,``org.apache.commons.text``,,272,,,,,,,,
|
||||
`Apache HttpComponents <https://hc.apache.org/>`_,"``org.apache.hc.core5.*``, ``org.apache.http``",5,136,28,,,3,,,,25
|
||||
`Google Guava <https://guava.dev/>`_,``com.google.common.*``,,158,6,,6,,,,,
|
||||
Java Standard Library,``java.*``,3,327,30,13,,,7,,,10
|
||||
Java Standard Library,``java.*``,3,364,30,13,,,7,,,10
|
||||
Java extensions,"``javax.*``, ``jakarta.*``",22,540,18,,,,,1,1,2
|
||||
`Spring <https://spring.io/>`_,``org.springframework.*``,29,306,62,,,,19,14,,29
|
||||
Others,"``com.esotericsoftware.kryo.io``, ``com.esotericsoftware.kryo5.io``, ``com.fasterxml.jackson.databind``, ``com.unboundid.ldap.sdk``, ``org.apache.commons.codec``, ``org.apache.commons.jexl2``, ``org.apache.commons.jexl3``, ``org.apache.directory.ldap.client.api``, ``org.apache.ibatis.jdbc``, ``org.dom4j``, ``org.hibernate``, ``org.jooq``, ``org.xml.sax``, ``org.xmlpull.v1``, ``play.mvc``",7,12,82,,,,14,18,,
|
||||
Totals,,84,2428,296,13,6,6,107,33,1,66
|
||||
Totals,,84,2465,296,13,6,6,107,33,1,66
|
||||
|
||||
|
||||
@@ -17,7 +17,7 @@ from InstanceOfExpr ioe, RefType t, RefType ct
|
||||
where
|
||||
ioe.getExpr() instanceof ThisAccess and
|
||||
t = ioe.getExpr().getType() and
|
||||
ct = ioe.getTypeName().getType() and
|
||||
ct = ioe.getCheckedType() and
|
||||
ct.getASupertype*() = t
|
||||
select ioe,
|
||||
"Testing whether 'this' is an instance of $@ in $@ introduces a dependency cycle between the two types.",
|
||||
|
||||
@@ -15,7 +15,7 @@ import java
|
||||
from InstanceOfExpr ioe, RefType t, RefType ct
|
||||
where
|
||||
t = ioe.getExpr().getType() and
|
||||
ct = ioe.getTypeName().getType() and
|
||||
ct = ioe.getCheckedType() and
|
||||
ct = t.getASupertype+()
|
||||
select ioe,
|
||||
"There is no need to test whether an instance of $@ is also an instance of $@ - it always is.", t,
|
||||
|
||||
@@ -17,8 +17,8 @@ predicate instanceofInEquals(EqualsMethod m, InstanceOfExpr e) {
|
||||
m.fromSource() and
|
||||
e.getEnclosingCallable() = m and
|
||||
e.getExpr().(VarAccess).getVariable() = m.getParameter() and
|
||||
exists(Class instanceofType |
|
||||
instanceofType = e.getTypeName().getType() and
|
||||
exists(RefType instanceofType |
|
||||
instanceofType = e.getCheckedType() and
|
||||
not instanceofType.isFinal()
|
||||
)
|
||||
}
|
||||
|
||||
@@ -51,7 +51,7 @@ class LockObjectField extends Field {
|
||||
class ValidSynchStmt extends Stmt {
|
||||
ValidSynchStmt() {
|
||||
// It's OK to lock the enclosing class.
|
||||
this.(SynchronizedStmt).getExpr().(TypeLiteral).getTypeName().getType() =
|
||||
this.(SynchronizedStmt).getExpr().(TypeLiteral).getReferencedType() =
|
||||
this.getEnclosingCallable().getDeclaringType()
|
||||
or
|
||||
// It's OK to lock on a "lock object field".
|
||||
|
||||
@@ -24,7 +24,7 @@ import java
|
||||
predicate isSynchronizedByBlock(Method m) {
|
||||
exists(SynchronizedStmt sync, Expr on | sync = m.getBody().getAChild*() and on = sync.getExpr() |
|
||||
if m.isStatic()
|
||||
then on.(TypeLiteral).getTypeName().getType() = m.getDeclaringType()
|
||||
then on.(TypeLiteral).getReferencedType() = m.getDeclaringType()
|
||||
else on.(ThisAccess).getType().(RefType).getSourceDeclaration() = m.getDeclaringType()
|
||||
)
|
||||
}
|
||||
|
||||
@@ -18,7 +18,7 @@ import semmle.code.java.dataflow.SSA
|
||||
/** `ioe` is of the form `va instanceof t`. */
|
||||
predicate instanceOfCheck(InstanceOfExpr ioe, VarAccess va, RefType t) {
|
||||
ioe.getExpr() = va and
|
||||
ioe.getTypeName().getType().(RefType).getSourceDeclaration() = t
|
||||
ioe.getCheckedType().getSourceDeclaration() = t
|
||||
}
|
||||
|
||||
/** Expression `e` assumes that `va` could be of type `t`. */
|
||||
|
||||
@@ -127,7 +127,9 @@ predicate potentiallyStatic(InnerClass c) {
|
||||
forall(InnerClass superOfNested | superOfNested = nested.getASourceSupertype+() |
|
||||
potentiallyStatic(superOfNested)
|
||||
)
|
||||
)
|
||||
) and
|
||||
// JUnit Nested test classes are required to be non-static.
|
||||
not c.hasAnnotation("org.junit.jupiter.api", "Nested")
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -14,8 +14,8 @@ may have unforeseen effects, such as the execution of arbitrary code.
|
||||
</p>
|
||||
<p>
|
||||
There are many different serialization frameworks. This query currently
|
||||
supports Kryo, XmlDecoder, XStream, SnakeYaml, JYaml, JsonIO, YAMLBeans, HessianBurlap, Castor, Burlap
|
||||
and Java IO serialization through <code>ObjectInputStream</code>/<code>ObjectOutputStream</code>.
|
||||
supports Kryo, XmlDecoder, XStream, SnakeYaml, JYaml, JsonIO, YAMLBeans, HessianBurlap, Castor, Burlap,
|
||||
Jackson and Java IO serialization through <code>ObjectInputStream</code>/<code>ObjectOutputStream</code>.
|
||||
</p>
|
||||
</overview>
|
||||
|
||||
@@ -91,6 +91,15 @@ Remote code execution in JYaml library:
|
||||
JsonIO deserialization vulnerabilities:
|
||||
<a href="https://klezvirus.github.io/Advanced-Web-Hacking/Serialisation/">JsonIO deserialization</a>.
|
||||
</li>
|
||||
<li>
|
||||
Research by Moritz Bechler:
|
||||
<a href="https://www.github.com/mbechler/marshalsec/blob/master/marshalsec.pdf?raw=true">Java Unmarshaller Security - Turning your data into code execution</a>
|
||||
</li>
|
||||
<li>
|
||||
Blog posts by the developer of Jackson libraries:
|
||||
<a href="https://cowtowncoder.medium.com/on-jackson-cves-dont-panic-here-is-what-you-need-to-know-54cd0d6e8062">On Jackson CVEs: Don’t Panic — Here is what you need to know</a>
|
||||
<a href="https://cowtowncoder.medium.com/jackson-2-10-safe-default-typing-2d018f0ce2ba">Jackson 2.10: Safe Default Typing</a>
|
||||
</li>
|
||||
</references>
|
||||
|
||||
</qhelp>
|
||||
|
||||
@@ -12,51 +12,9 @@
|
||||
*/
|
||||
|
||||
import java
|
||||
import semmle.code.java.dataflow.FlowSources
|
||||
import semmle.code.java.security.UnsafeDeserialization
|
||||
import semmle.code.java.security.UnsafeDeserializationQuery
|
||||
import DataFlow::PathGraph
|
||||
|
||||
class UnsafeDeserializationConfig extends TaintTracking::Configuration {
|
||||
UnsafeDeserializationConfig() { this = "UnsafeDeserializationConfig" }
|
||||
|
||||
override predicate isSource(DataFlow::Node source) { source instanceof RemoteFlowSource }
|
||||
|
||||
override predicate isSink(DataFlow::Node sink) { sink instanceof UnsafeDeserializationSink }
|
||||
|
||||
override predicate isAdditionalTaintStep(DataFlow::Node pred, DataFlow::Node succ) {
|
||||
exists(ClassInstanceExpr cie |
|
||||
cie.getArgument(0) = pred.asExpr() and
|
||||
cie = succ.asExpr() and
|
||||
(
|
||||
cie.getConstructor().getDeclaringType() instanceof JsonIoJsonReader or
|
||||
cie.getConstructor().getDeclaringType() instanceof YamlBeansReader or
|
||||
cie.getConstructor().getDeclaringType().getASupertype*() instanceof UnsafeHessianInput or
|
||||
cie.getConstructor().getDeclaringType() instanceof BurlapInput
|
||||
)
|
||||
)
|
||||
or
|
||||
exists(MethodAccess ma |
|
||||
ma.getMethod() instanceof BurlapInputInitMethod and
|
||||
ma.getArgument(0) = pred.asExpr() and
|
||||
ma.getQualifier() = succ.asExpr()
|
||||
)
|
||||
}
|
||||
|
||||
override predicate isSanitizer(DataFlow::Node node) {
|
||||
exists(ClassInstanceExpr cie |
|
||||
cie.getConstructor().getDeclaringType() instanceof JsonIoJsonReader and
|
||||
cie = node.asExpr() and
|
||||
exists(SafeJsonIoConfig sji | sji.hasFlowToExpr(cie.getArgument(1)))
|
||||
)
|
||||
or
|
||||
exists(MethodAccess ma |
|
||||
ma.getMethod() instanceof JsonIoJsonToJavaMethod and
|
||||
ma.getArgument(0) = node.asExpr() and
|
||||
exists(SafeJsonIoConfig sji | sji.hasFlowToExpr(ma.getArgument(1)))
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
from DataFlow::PathNode source, DataFlow::PathNode sink, UnsafeDeserializationConfig conf
|
||||
where conf.hasFlowPath(source, sink)
|
||||
select sink.getNode().(UnsafeDeserializationSink).getMethodAccess(), source, sink,
|
||||
|
||||
@@ -16,9 +16,9 @@ import java
|
||||
import semmle.code.java.Collections
|
||||
|
||||
predicate guardedByInstanceOf(VarAccess e, RefType t) {
|
||||
exists(IfStmt s, InstanceOfExpr instanceCheck, Type checkType |
|
||||
exists(IfStmt s, InstanceOfExpr instanceCheck, RefType checkType |
|
||||
s.getCondition() = instanceCheck and
|
||||
instanceCheck.getTypeName().getType() = checkType and
|
||||
instanceCheck.getCheckedType() = checkType and
|
||||
// The same variable appears as the subject of the `instanceof`.
|
||||
instanceCheck.getExpr() = e.getVariable().getAnAccess() and
|
||||
// The checked type is either the type itself, or a raw version. For example, it is usually
|
||||
|
||||
@@ -26,7 +26,7 @@ all certificate errors are ignored. In the 'GOOD' case, certificate errors are r
|
||||
|
||||
<references>
|
||||
<li>Teamdev:
|
||||
<a href="https://jxbrowser.support.teamdev.com/support/discussions/topics/9000051708">
|
||||
<a href="https://jxbrowser-support.teamdev.com/release-notes/2019/v6-24.html">
|
||||
Changelog of JxBrowser 6.24</a>.</li>
|
||||
</references>
|
||||
</qhelp>
|
||||
|
||||
@@ -34,6 +34,29 @@ public class SpringUrlRedirect {
|
||||
}
|
||||
|
||||
@GetMapping("url5")
|
||||
public ResponseEntity<Void> bad5(String redirectUrl) {
|
||||
return ResponseEntity.status(HttpStatus.FOUND)
|
||||
.location(URI.create(redirectUrl))
|
||||
.build();
|
||||
}
|
||||
|
||||
@GetMapping("url6")
|
||||
public ResponseEntity<Void> bad6(String redirectUrl) {
|
||||
HttpHeaders httpHeaders = new HttpHeaders();
|
||||
httpHeaders.setLocation(URI.create(redirectUrl));
|
||||
|
||||
return new ResponseEntity<>(httpHeaders, HttpStatus.SEE_OTHER);
|
||||
}
|
||||
|
||||
@GetMapping("url7")
|
||||
public ResponseEntity<Void> bad7(String redirectUrl) {
|
||||
HttpHeaders httpHeaders = new HttpHeaders();
|
||||
httpHeaders.add("Location", redirectUrl);
|
||||
|
||||
return ResponseEntity.status(HttpStatus.SEE_OTHER).headers(httpHeaders).build();
|
||||
}
|
||||
|
||||
@GetMapping("url8")
|
||||
public RedirectView good1(String redirectUrl) {
|
||||
RedirectView rv = new RedirectView();
|
||||
if (redirectUrl.startsWith(VALID_REDIRECT)){
|
||||
|
||||
@@ -21,10 +21,10 @@ redirects on the server; then choose from that list based on the user input prov
|
||||
<example>
|
||||
|
||||
<p>The following examples show the bad case and the good case respectively.
|
||||
In <code>bad1</code> method and <code>bad2</code> method and <code>bad3</code> method and
|
||||
<code>bad4</code> method, shows an HTTP request parameter being used directly in a URL redirect
|
||||
without validating the input, which facilitates phishing attacks. In <code>good1</code> method,
|
||||
shows how to solve this problem by verifying whether the user input is a known fixed string beginning.
|
||||
The <code>bad</code> methods show an HTTP request parameter being used directly
|
||||
in a URL redirect without validating the input, which facilitates phishing attacks.
|
||||
In the <code>good1</code> method, it is shown how to solve this problem by verifying whether
|
||||
the user input is a known fixed string beginning.
|
||||
</p>
|
||||
|
||||
<sample src="SpringUrlRedirect.java" />
|
||||
@@ -33,5 +33,6 @@ shows how to solve this problem by verifying whether the user input is a known f
|
||||
<references>
|
||||
<li>A Guide To Spring Redirects: <a href="https://www.baeldung.com/spring-redirect-and-forward">Spring Redirects</a>.</li>
|
||||
<li>Url redirection - attack and defense: <a href="https://www.virtuesecurity.com/kb/url-redirection-attack-and-defense/">Url Redirection</a>.</li>
|
||||
<li>How to redirect to an external URL from Spring Boot REST Controller (Post/Redirect/Get pattern)?: <a href="https://fullstackdeveloper.guru/2021/03/12/how-to-redirect-to-an-external-url-from-spring-boot-rest-controller/">ResponseEntity Redirection</a>.</li>
|
||||
</references>
|
||||
</qhelp>
|
||||
|
||||
@@ -34,6 +34,10 @@ class SpringUrlRedirectFlowConfig extends TaintTracking::Configuration {
|
||||
|
||||
override predicate isSink(DataFlow::Node sink) { sink instanceof SpringUrlRedirectSink }
|
||||
|
||||
override predicate isAdditionalTaintStep(DataFlow::Node fromNode, DataFlow::Node toNode) {
|
||||
springUrlRedirectTaintStep(fromNode, toNode)
|
||||
}
|
||||
|
||||
override predicate isSanitizerGuard(DataFlow::BarrierGuard guard) {
|
||||
guard instanceof StartsWithSanitizer
|
||||
}
|
||||
@@ -57,6 +61,8 @@ class SpringUrlRedirectFlowConfig extends TaintTracking::Configuration {
|
||||
not ma.getArgument(0).(CompileTimeConstantExpr).getStringValue().regexpMatch("^%s.*")
|
||||
)
|
||||
)
|
||||
or
|
||||
nonLocationHeaderSanitizer(node)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -35,8 +35,13 @@ class RedirectAppendCall extends MethodAccess {
|
||||
}
|
||||
|
||||
/** A URL redirection sink from spring controller method. */
|
||||
class SpringUrlRedirectSink extends DataFlow::Node {
|
||||
SpringUrlRedirectSink() {
|
||||
abstract class SpringUrlRedirectSink extends DataFlow::Node { }
|
||||
|
||||
/**
|
||||
* A sink for URL Redirection via the Spring View classes.
|
||||
*/
|
||||
private class SpringViewUrlRedirectSink extends SpringUrlRedirectSink {
|
||||
SpringViewUrlRedirectSink() {
|
||||
exists(RedirectBuilderExpr rbe |
|
||||
rbe.getRightOperand() = this.asExpr() and
|
||||
any(SpringRequestMappingMethod sqmm).polyCalls*(this.getEnclosingCallable())
|
||||
@@ -71,3 +76,64 @@ class SpringUrlRedirectSink extends DataFlow::Node {
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A sink for URL Redirection via the `ResponseEntity` class.
|
||||
*/
|
||||
private class SpringResponseEntityUrlRedirectSink extends SpringUrlRedirectSink {
|
||||
SpringResponseEntityUrlRedirectSink() {
|
||||
// Find `new ResponseEntity(httpHeaders, ...)` or
|
||||
// `new ResponseEntity(..., httpHeaders, ...)` sinks
|
||||
exists(ClassInstanceExpr cie, Argument argument |
|
||||
cie.getConstructedType() instanceof SpringResponseEntity and
|
||||
argument.getType() instanceof SpringHttpHeaders and
|
||||
argument = cie.getArgument([0, 1]) and
|
||||
this.asExpr() = argument
|
||||
)
|
||||
or
|
||||
// Find `ResponseEntity.status(...).headers(taintHeaders).build()` or
|
||||
// `ResponseEntity.status(...).location(URI.create(taintURL)).build()` sinks
|
||||
exists(MethodAccess ma |
|
||||
ma.getMethod()
|
||||
.getDeclaringType()
|
||||
.hasQualifiedName("org.springframework.http", "ResponseEntity$HeadersBuilder<BodyBuilder>") and
|
||||
ma.getMethod().getName() in ["headers", "location"] and
|
||||
this.asExpr() = ma.getArgument(0)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private class HttpHeadersMethodAccess extends MethodAccess {
|
||||
HttpHeadersMethodAccess() { this.getMethod().getDeclaringType() instanceof SpringHttpHeaders }
|
||||
}
|
||||
|
||||
private class HttpHeadersAddSetMethodAccess extends HttpHeadersMethodAccess {
|
||||
HttpHeadersAddSetMethodAccess() { this.getMethod().getName() in ["add", "set"] }
|
||||
}
|
||||
|
||||
private class HttpHeadersSetLocationMethodAccess extends HttpHeadersMethodAccess {
|
||||
HttpHeadersSetLocationMethodAccess() { this.getMethod().hasName("setLocation") }
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `fromNode` to `toNode` is a dataflow step from a tainted argument to
|
||||
* a `HttpHeaders` instance qualifier, i.e. `httpHeaders.setLocation(tainted)`.
|
||||
*/
|
||||
predicate springUrlRedirectTaintStep(DataFlow::Node fromNode, DataFlow::Node toNode) {
|
||||
exists(HttpHeadersSetLocationMethodAccess ma |
|
||||
fromNode.asExpr() = ma.getArgument(0) and
|
||||
toNode.asExpr() = ma.getQualifier()
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* A sanitizer to exclude the cases where the `HttpHeaders.add` or `HttpHeaders.set`
|
||||
* methods are called with a HTTP header other than "Location".
|
||||
* E.g: `httpHeaders.add("X-Some-Header", taintedUrlString)`
|
||||
*/
|
||||
predicate nonLocationHeaderSanitizer(DataFlow::Node node) {
|
||||
exists(HttpHeadersAddSetMethodAccess ma, Argument firstArg | node.asExpr() = ma.getArgument(1) |
|
||||
firstArg = ma.getArgument(0) and
|
||||
not firstArg.(CompileTimeConstantExpr).getStringValue().matches("Location")
|
||||
)
|
||||
}
|
||||
|
||||
@@ -35,6 +35,7 @@ predicate instantiates(RefType t, GenericType g, int i, RefType arg) {
|
||||
* - a class `MyIntMap<V> extends HashMap<Integer, V>` instantiates `Map` (among others)
|
||||
* with the `0`-th type parameter being `Integer` and the `1`-th type parameter being `V`.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
predicate indirectlyInstantiates(RefType t, GenericType g, int i, RefType arg) {
|
||||
instantiates(t, g, i, arg)
|
||||
or
|
||||
|
||||
@@ -62,7 +62,7 @@ predicate depends(RefType t, RefType dep) {
|
||||
or
|
||||
// the type of a type literal accessed in `t`,
|
||||
exists(TypeLiteral l | l.getEnclosingCallable().getDeclaringType() = t |
|
||||
usesType(l.getTypeName().getType(), dep)
|
||||
usesType(l.getReferencedType(), dep)
|
||||
)
|
||||
or
|
||||
// the type of an annotation (or one of its element values) that annotates `t` or one of its members,
|
||||
@@ -76,7 +76,7 @@ predicate depends(RefType t, RefType dep) {
|
||||
or
|
||||
// the type accessed in an `instanceof` expression in `t`.
|
||||
exists(InstanceOfExpr ioe | t = ioe.getEnclosingCallable().getDeclaringType() |
|
||||
usesType(ioe.getTypeName().getType(), dep)
|
||||
usesType(ioe.getCheckedType(), dep)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -80,7 +80,7 @@ predicate numDepends(RefType t, RefType dep, int value) {
|
||||
elem = l and
|
||||
l.getEnclosingCallable().getDeclaringType() = t
|
||||
|
|
||||
usesType(l.getTypeName().getType(), dep)
|
||||
usesType(l.getReferencedType(), dep)
|
||||
)
|
||||
or
|
||||
// the type of an annotation (or one of its element values) that annotates `t` or one of its members,
|
||||
@@ -100,7 +100,7 @@ predicate numDepends(RefType t, RefType dep, int value) {
|
||||
elem = ioe and
|
||||
t = ioe.getEnclosingCallable().getDeclaringType()
|
||||
|
|
||||
usesType(ioe.getTypeName().getType(), dep)
|
||||
usesType(ioe.getCheckedType(), dep)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -46,17 +46,6 @@ class Expr extends ExprParent, @expr {
|
||||
*/
|
||||
int getKind() { exprs(this, result, _, _, _) }
|
||||
|
||||
/**
|
||||
* DEPRECATED: This is no longer necessary. See `Expr.isParenthesized()`.
|
||||
*
|
||||
* Gets this expression with any surrounding parentheses removed.
|
||||
*/
|
||||
deprecated Expr getProperExpr() {
|
||||
result = this.(ParExpr).getExpr().getProperExpr()
|
||||
or
|
||||
result = this and not this instanceof ParExpr
|
||||
}
|
||||
|
||||
/** Gets the statement containing this expression, if any. */
|
||||
Stmt getEnclosingStmt() { statementEnclosingExpr(this, result) }
|
||||
|
||||
@@ -1318,19 +1307,6 @@ class SwitchExpr extends Expr, StmtParent, @switchexpr {
|
||||
override string getAPrimaryQlClass() { result = "SwitchExpr" }
|
||||
}
|
||||
|
||||
/**
|
||||
* DEPRECATED: Use `Expr.isParenthesized()` instead.
|
||||
*
|
||||
* A parenthesised expression.
|
||||
*/
|
||||
deprecated class ParExpr extends Expr, @parexpr {
|
||||
/** Gets the expression inside the parentheses. */
|
||||
deprecated Expr getExpr() { result.getParent() = this }
|
||||
|
||||
/** Gets a printable representation of this expression. */
|
||||
override string toString() { result = "(...)" }
|
||||
}
|
||||
|
||||
/** An `instanceof` expression. */
|
||||
class InstanceOfExpr extends Expr, @instanceofexpr {
|
||||
/** Gets the expression on the left-hand side of the `instanceof` operator. */
|
||||
@@ -1357,6 +1333,9 @@ class InstanceOfExpr extends Expr, @instanceofexpr {
|
||||
/** Gets the access to the type on the right-hand side of the `instanceof` operator. */
|
||||
Expr getTypeName() { result.isNthChildOf(this, 1) }
|
||||
|
||||
/** Gets the type this `instanceof` expression checks for. */
|
||||
RefType getCheckedType() { result = getTypeName().getType() }
|
||||
|
||||
/** Gets a printable representation of this expression. */
|
||||
override string toString() { result = "...instanceof..." }
|
||||
|
||||
@@ -1449,6 +1428,12 @@ class TypeLiteral extends Expr, @typeliteral {
|
||||
/** Gets the access to the type whose class is accessed. */
|
||||
Expr getTypeName() { result.getParent() = this }
|
||||
|
||||
/**
|
||||
* Gets the type this type literal refers to. For example for `String.class` the
|
||||
* result is the type representing `String`.
|
||||
*/
|
||||
Type getReferencedType() { result = getTypeName().getType() }
|
||||
|
||||
/** Gets a printable representation of this expression. */
|
||||
override string toString() { result = this.getTypeName().toString() + ".class" }
|
||||
|
||||
|
||||
@@ -52,7 +52,7 @@ abstract private class ReflectiveClassIdentifier extends Expr {
|
||||
|
||||
private class ReflectiveClassIdentifierLiteral extends ReflectiveClassIdentifier, TypeLiteral {
|
||||
override RefType getReflectivelyIdentifiedClass() {
|
||||
result = getTypeName().getType().(RefType).getSourceDeclaration()
|
||||
result = getReferencedType().(RefType).getSourceDeclaration()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -168,7 +168,7 @@ class TestNGTestMethod extends Method {
|
||||
or
|
||||
// Or the data provider class should be declared
|
||||
result.getDeclaringType() =
|
||||
testAnnotation.getValue("dataProviderClass").(TypeLiteral).getTypeName().getType()
|
||||
testAnnotation.getValue("dataProviderClass").(TypeLiteral).getReferencedType()
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -227,7 +227,7 @@ class TestNGListenersAnnotation extends TestNGAnnotation {
|
||||
* Gets a listener defined in this annotation.
|
||||
*/
|
||||
TestNGListenerImpl getAListener() {
|
||||
result = getAValue("value").(TypeLiteral).getTypeName().getType()
|
||||
result = getAValue("value").(TypeLiteral).getReferencedType()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -303,7 +303,7 @@ class JUnitCategoryAnnotation extends Annotation {
|
||||
literal = value.(ArrayCreationExpr).getInit().getAnInit()
|
||||
)
|
||||
|
|
||||
result = literal.getTypeName().getType()
|
||||
result = literal.getReferencedType()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -25,8 +25,8 @@ Expr enumConstEquality(Expr e, boolean polarity, EnumConstant c) {
|
||||
}
|
||||
|
||||
/** Gets an instanceof expression of `v` with type `type` */
|
||||
InstanceOfExpr instanceofExpr(SsaVariable v, Type type) {
|
||||
result.getTypeName().getType() = type and
|
||||
InstanceOfExpr instanceofExpr(SsaVariable v, RefType type) {
|
||||
result.getCheckedType() = type and
|
||||
result.getExpr() = v.getAUse()
|
||||
}
|
||||
|
||||
|
||||
@@ -504,7 +504,7 @@ private predicate correlatedConditions(
|
||||
inverted = pol1.booleanXor(pol2)
|
||||
)
|
||||
or
|
||||
exists(SsaVariable v, Type type |
|
||||
exists(SsaVariable v, RefType type |
|
||||
cond1.getCondition() = instanceofExpr(v, type) and
|
||||
cond2.getCondition() = instanceofExpr(v, type) and
|
||||
inverted = false
|
||||
|
||||
@@ -299,7 +299,7 @@ private predicate downcastSuccessor(VarAccess va, RefType t) {
|
||||
private predicate instanceOfGuarded(VarAccess va, RefType t) {
|
||||
exists(InstanceOfExpr ioe, BaseSsaVariable v |
|
||||
ioe.getExpr() = v.getAUse() and
|
||||
t = ioe.getTypeName().getType() and
|
||||
t = ioe.getCheckedType() and
|
||||
va = v.getAUse() and
|
||||
guardControls_v1(ioe, va.getBasicBlock(), true)
|
||||
)
|
||||
@@ -311,7 +311,7 @@ private predicate instanceOfGuarded(VarAccess va, RefType t) {
|
||||
predicate arrayInstanceOfGuarded(ArrayAccess aa, RefType t) {
|
||||
exists(InstanceOfExpr ioe, BaseSsaVariable v1, BaseSsaVariable v2, ArrayAccess aa1 |
|
||||
ioe.getExpr() = aa1 and
|
||||
t = ioe.getTypeName().getType() and
|
||||
t = ioe.getCheckedType() and
|
||||
aa1.getArray() = v1.getAUse() and
|
||||
aa1.getIndexExpr() = v2.getAUse() and
|
||||
aa.getArray() = v1.getAUse() and
|
||||
|
||||
@@ -368,7 +368,44 @@ private class ContainerFlowSummaries extends SummaryModelCsv {
|
||||
"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"
|
||||
"java.util;Collections;false;addAll;(Collection,Object[]);;ArrayElement of Argument[1];Element of Argument[0];value",
|
||||
"java.util;AbstractMap$SimpleEntry;false;SimpleEntry;(Object,Object);;Argument[0];MapKey of Argument[-1];value",
|
||||
"java.util;AbstractMap$SimpleEntry;false;SimpleEntry;(Object,Object);;Argument[1];MapValue of Argument[-1];value",
|
||||
"java.util;AbstractMap$SimpleEntry;false;SimpleEntry;(Entry);;MapKey of Argument[0];MapKey of Argument[-1];value",
|
||||
"java.util;AbstractMap$SimpleEntry;false;SimpleEntry;(Entry);;MapValue of Argument[0];MapValue of Argument[-1];value",
|
||||
"java.util;AbstractMap$SimpleImmutableEntry;false;SimpleImmutableEntry;(Object,Object);;Argument[0];MapKey of Argument[-1];value",
|
||||
"java.util;AbstractMap$SimpleImmutableEntry;false;SimpleImmutableEntry;(Object,Object);;Argument[1];MapValue of Argument[-1];value",
|
||||
"java.util;AbstractMap$SimpleImmutableEntry;false;SimpleImmutableEntry;(Entry);;MapKey of Argument[0];MapKey of Argument[-1];value",
|
||||
"java.util;AbstractMap$SimpleImmutableEntry;false;SimpleImmutableEntry;(Entry);;MapValue of Argument[0];MapValue of Argument[-1];value",
|
||||
"java.util;ArrayDeque;false;ArrayDeque;(Collection);;Element of Argument[0];Element of Argument[-1];value",
|
||||
"java.util;ArrayList;false;ArrayList;(Collection);;Element of Argument[0];Element of Argument[-1];value",
|
||||
"java.util;EnumMap;false;EnumMap;(Map);;MapKey of Argument[0];MapKey of Argument[-1];value",
|
||||
"java.util;EnumMap;false;EnumMap;(Map);;MapValue of Argument[0];MapValue of Argument[-1];value",
|
||||
"java.util;EnumMap;false;EnumMap;(EnumMap);;MapKey of Argument[0];MapKey of Argument[-1];value",
|
||||
"java.util;EnumMap;false;EnumMap;(EnumMap);;MapValue of Argument[0];MapValue of Argument[-1];value",
|
||||
"java.util;HashMap;false;HashMap;(Map);;MapKey of Argument[0];MapKey of Argument[-1];value",
|
||||
"java.util;HashMap;false;HashMap;(Map);;MapValue of Argument[0];MapValue of Argument[-1];value",
|
||||
"java.util;HashSet;false;HashSet;(Collection);;Element of Argument[0];Element of Argument[-1];value",
|
||||
"java.util;Hashtable;false;Hashtable;(Map);;MapKey of Argument[0];MapKey of Argument[-1];value",
|
||||
"java.util;Hashtable;false;Hashtable;(Map);;MapValue of Argument[0];MapValue of Argument[-1];value",
|
||||
"java.util;IdentityHashMap;false;IdentityHashMap;(Map);;MapKey of Argument[0];MapKey of Argument[-1];value",
|
||||
"java.util;IdentityHashMap;false;IdentityHashMap;(Map);;MapValue of Argument[0];MapValue of Argument[-1];value",
|
||||
"java.util;LinkedHashMap;false;LinkedHashMap;(Map);;MapKey of Argument[0];MapKey of Argument[-1];value",
|
||||
"java.util;LinkedHashMap;false;LinkedHashMap;(Map);;MapValue of Argument[0];MapValue of Argument[-1];value",
|
||||
"java.util;LinkedHashSet;false;LinkedHashSet;(Collection);;Element of Argument[0];Element of Argument[-1];value",
|
||||
"java.util;LinkedList;false;LinkedList;(Collection);;Element of Argument[0];Element of Argument[-1];value",
|
||||
"java.util;PriorityQueue;false;PriorityQueue;(Collection);;Element of Argument[0];Element of Argument[-1];value",
|
||||
"java.util;PriorityQueue;false;PriorityQueue;(PriorityQueue);;Element of Argument[0];Element of Argument[-1];value",
|
||||
"java.util;PriorityQueue;false;PriorityQueue;(SortedSet);;Element of Argument[0];Element of Argument[-1];value",
|
||||
"java.util;TreeMap;false;TreeMap;(Map);;MapKey of Argument[0];MapKey of Argument[-1];value",
|
||||
"java.util;TreeMap;false;TreeMap;(Map);;MapValue of Argument[0];MapValue of Argument[-1];value",
|
||||
"java.util;TreeMap;false;TreeMap;(SortedMap);;MapKey of Argument[0];MapKey of Argument[-1];value",
|
||||
"java.util;TreeMap;false;TreeMap;(SortedMap);;MapValue of Argument[0];MapValue of Argument[-1];value",
|
||||
"java.util;TreeSet;false;TreeSet;(Collection);;Element of Argument[0];Element of Argument[-1];value",
|
||||
"java.util;TreeSet;false;TreeSet;(SortedSet);;Element of Argument[0];Element of Argument[-1];value",
|
||||
"java.util;Vector;false;Vector;(Collection);;Element of Argument[0];Element of Argument[-1];value",
|
||||
"java.util;WeakHashMap;false;WeakHashMap;(Map);;MapKey of Argument[0];MapKey of Argument[-1];value",
|
||||
"java.util;WeakHashMap;false;WeakHashMap;(Map);;MapValue of Argument[0];MapValue of Argument[-1];value"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
@@ -176,7 +176,7 @@ private module Dispatch {
|
||||
v.getAUse() = q and
|
||||
guardControls_v1(ioe, q.getBasicBlock(), false) and
|
||||
ioe.getExpr() = v.getAUse() and
|
||||
ioe.getTypeName().getType().getErasure() = t and
|
||||
ioe.getCheckedType().getErasure() = t and
|
||||
tgt.getDeclaringType().getSourceDeclaration().getASourceSupertype*() = t
|
||||
)
|
||||
}
|
||||
|
||||
@@ -64,5 +64,5 @@ class RunWithAnnotation extends Annotation {
|
||||
/**
|
||||
* Gets the runner that will be used.
|
||||
*/
|
||||
Type getRunner() { result = getValue("value").(TypeLiteral).getTypeName().getType() }
|
||||
Type getRunner() { result = getValue("value").(TypeLiteral).getReferencedType() }
|
||||
}
|
||||
|
||||
174
java/ql/src/semmle/code/java/frameworks/Jackson.qll
Normal file
174
java/ql/src/semmle/code/java/frameworks/Jackson.qll
Normal file
@@ -0,0 +1,174 @@
|
||||
/**
|
||||
* Provides classes and predicates for working with the Jackson serialization framework.
|
||||
*/
|
||||
|
||||
import java
|
||||
private import semmle.code.java.Reflection
|
||||
private import semmle.code.java.dataflow.DataFlow
|
||||
|
||||
private class ObjectMapper extends RefType {
|
||||
ObjectMapper() {
|
||||
getASupertype*().hasQualifiedName("com.fasterxml.jackson.databind", "ObjectMapper")
|
||||
}
|
||||
}
|
||||
|
||||
/** A builder for building Jackson's `JsonMapper`. */
|
||||
class MapperBuilder extends RefType {
|
||||
MapperBuilder() {
|
||||
hasQualifiedName("com.fasterxml.jackson.databind.cfg", "MapperBuilder<JsonMapper,Builder>")
|
||||
}
|
||||
}
|
||||
|
||||
private class JsonFactory extends RefType {
|
||||
JsonFactory() { hasQualifiedName("com.fasterxml.jackson.core", "JsonFactory") }
|
||||
}
|
||||
|
||||
private class JsonParser extends RefType {
|
||||
JsonParser() { hasQualifiedName("com.fasterxml.jackson.core", "JsonParser") }
|
||||
}
|
||||
|
||||
/** A type descriptor in Jackson libraries. For example, `java.lang.Class`. */
|
||||
class JacksonTypeDescriptorType extends RefType {
|
||||
JacksonTypeDescriptorType() {
|
||||
this instanceof TypeClass or
|
||||
hasQualifiedName("com.fasterxml.jackson.databind", "JavaType") or
|
||||
hasQualifiedName("com.fasterxml.jackson.core.type", "TypeReference")
|
||||
}
|
||||
}
|
||||
|
||||
/** A method in `ObjectMapper` that deserialize data. */
|
||||
class ObjectMapperReadMethod extends Method {
|
||||
ObjectMapperReadMethod() {
|
||||
this.getDeclaringType() instanceof ObjectMapper and
|
||||
this.hasName(["readValue", "readValues", "treeToValue"])
|
||||
}
|
||||
}
|
||||
|
||||
/** A call that enables the default typing in `ObjectMapper`. */
|
||||
class EnableJacksonDefaultTyping extends MethodAccess {
|
||||
EnableJacksonDefaultTyping() {
|
||||
this.getMethod().getDeclaringType() instanceof ObjectMapper and
|
||||
this.getMethod().hasName("enableDefaultTyping")
|
||||
}
|
||||
}
|
||||
|
||||
/** A qualifier of a call to one of the methods in `ObjectMapper` that deserialize data. */
|
||||
class ObjectMapperReadQualifier extends DataFlow::ExprNode {
|
||||
ObjectMapperReadQualifier() {
|
||||
exists(MethodAccess ma | ma.getQualifier() = this.asExpr() |
|
||||
ma.getMethod() instanceof ObjectMapperReadMethod
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/** A source that sets a type validator. */
|
||||
class SetPolymorphicTypeValidatorSource extends DataFlow::ExprNode {
|
||||
SetPolymorphicTypeValidatorSource() {
|
||||
exists(MethodAccess ma, Method m | m = ma.getMethod() |
|
||||
(
|
||||
m.getDeclaringType() instanceof ObjectMapper and
|
||||
m.hasName("setPolymorphicTypeValidator")
|
||||
or
|
||||
m.getDeclaringType() instanceof MapperBuilder and
|
||||
m.hasName("polymorphicTypeValidator")
|
||||
) and
|
||||
this.asExpr() = ma.getQualifier()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/** Holds if `fromNode` to `toNode` is a dataflow step that resolves a class. */
|
||||
predicate resolveClassStep(DataFlow::Node fromNode, DataFlow::Node toNode) {
|
||||
exists(ReflectiveClassIdentifierMethodAccess ma |
|
||||
ma.getArgument(0) = fromNode.asExpr() and
|
||||
ma = toNode.asExpr()
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `fromNode` to `toNode` is a dataflow step that creates a Jackson parser.
|
||||
*
|
||||
* For example, a `createParser(userString)` call yields a `JsonParser`, which becomes dangerous
|
||||
* if passed to an unsafely-configured `ObjectMapper`'s `readValue` method.
|
||||
*/
|
||||
predicate createJacksonJsonParserStep(DataFlow::Node fromNode, DataFlow::Node toNode) {
|
||||
exists(MethodAccess ma, Method m | m = ma.getMethod() |
|
||||
(m.getDeclaringType() instanceof ObjectMapper or m.getDeclaringType() instanceof JsonFactory) and
|
||||
m.hasName("createParser") and
|
||||
ma.getArgument(0) = fromNode.asExpr() and
|
||||
ma = toNode.asExpr()
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `fromNode` to `toNode` is a dataflow step that creates a Jackson `TreeNode`.
|
||||
*
|
||||
* These are parse trees of user-supplied JSON, which may lead to arbitrary code execution
|
||||
* if passed to an unsafely-configured `ObjectMapper`'s `treeToValue` method.
|
||||
*/
|
||||
predicate createJacksonTreeNodeStep(DataFlow::Node fromNode, DataFlow::Node toNode) {
|
||||
exists(MethodAccess ma, Method m | m = ma.getMethod() |
|
||||
m.getDeclaringType() instanceof ObjectMapper and
|
||||
m.hasName("readTree") and
|
||||
ma.getArgument(0) = fromNode.asExpr() and
|
||||
ma = toNode.asExpr()
|
||||
)
|
||||
or
|
||||
exists(MethodAccess ma, Method m | m = ma.getMethod() |
|
||||
m.getDeclaringType() instanceof JsonParser and
|
||||
m.hasName("readValueAsTree") and
|
||||
ma.getQualifier() = fromNode.asExpr() and
|
||||
ma = toNode.asExpr()
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `type` or one of its supertypes has a field with `JsonTypeInfo` annotation
|
||||
* that enables polymorphic type handling.
|
||||
*/
|
||||
private predicate hasJsonTypeInfoAnnotation(RefType type) {
|
||||
hasFieldWithJsonTypeAnnotation(type.getASupertype*()) or
|
||||
hasJsonTypeInfoAnnotation(type.getAField().getType())
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `type` has a field with `JsonTypeInfo` annotation
|
||||
* that enables polymorphic type handling.
|
||||
*/
|
||||
private predicate hasFieldWithJsonTypeAnnotation(RefType type) {
|
||||
exists(Annotation a |
|
||||
type.getAField().getAnAnnotation() = a and
|
||||
a.getType().hasQualifiedName("com.fasterxml.jackson.annotation", "JsonTypeInfo") and
|
||||
a.getValue("use").(VarAccess).getVariable().hasName(["CLASS", "MINIMAL_CLASS"])
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `call` is a method call to a Jackson deserialization method such as `ObjectMapper.readValue(String, Class)`,
|
||||
* and the target deserialized class has a field with a `JsonTypeInfo` annotation that enables polymorphic typing.
|
||||
*/
|
||||
predicate hasArgumentWithUnsafeJacksonAnnotation(MethodAccess call) {
|
||||
call.getMethod() instanceof ObjectMapperReadMethod and
|
||||
exists(RefType argType, int i | i > 0 and argType = call.getArgument(i).getType() |
|
||||
hasJsonTypeInfoAnnotation(argType.(ParameterizedType).getATypeArgument())
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `fromNode` to `toNode` is a dataflow step that looks like resolving a class.
|
||||
* A method probably resolves a class if it takes a string, returns a type descriptor,
|
||||
* and its name contains "resolve", "load", etc.
|
||||
*
|
||||
* Any method call that satisfies the rule above is assumed to propagate taint from its string arguments,
|
||||
* so methods that accept user-controlled data but sanitize it or use it for some
|
||||
* completely different purpose before returning a type descriptor could result in false positives.
|
||||
*/
|
||||
predicate looksLikeResolveClassStep(DataFlow::Node fromNode, DataFlow::Node toNode) {
|
||||
exists(MethodAccess ma, Method m, Expr arg | m = ma.getMethod() and arg = ma.getAnArgument() |
|
||||
m.getReturnType() instanceof JacksonTypeDescriptorType and
|
||||
m.getName().toLowerCase().regexpMatch("(.*)(resolve|load|class|type)(.*)") and
|
||||
arg.getType() instanceof TypeString and
|
||||
arg = fromNode.asExpr() and
|
||||
ma = toNode.asExpr()
|
||||
)
|
||||
}
|
||||
@@ -33,7 +33,7 @@ class HttpResponseParseAsDeserializableField extends DeserializableField {
|
||||
HttpResponseParseAsDeserializableField() {
|
||||
exists(RefType decltype, TypeLiteralToParseAsFlowConfiguration conf |
|
||||
decltype = getDeclaringType() and
|
||||
conf.getSourceWithFlowToParseAs().getTypeName().getType() = decltype and
|
||||
conf.getSourceWithFlowToParseAs().getReferencedType() = decltype and
|
||||
decltype.fromSource()
|
||||
)
|
||||
}
|
||||
|
||||
@@ -116,7 +116,7 @@ private class TypeLiteralToJacksonDatabindFlowConfiguration extends DataFlow5::C
|
||||
private class ExplicitlyReadJacksonDeserializableType extends JacksonDeserializableType {
|
||||
ExplicitlyReadJacksonDeserializableType() {
|
||||
exists(TypeLiteralToJacksonDatabindFlowConfiguration conf |
|
||||
usesType(conf.getSourceWithFlowToJacksonDatabind().getTypeName().getType(), this)
|
||||
usesType(conf.getSourceWithFlowToJacksonDatabind().getReferencedType(), this)
|
||||
)
|
||||
or
|
||||
exists(MethodAccess ma |
|
||||
|
||||
@@ -199,7 +199,7 @@ abstract class EjbInterfaceAnnotation extends Annotation {
|
||||
// within the "value" element of this annotation.
|
||||
// Uses `getAChildExpr*()` since the "value" element can have type `Class` or `Class[]`.
|
||||
exists(TypeLiteral tl | tl = getValue("value").getAChildExpr*() |
|
||||
exists(TypeAccess ta | ta = tl.getTypeName() | result = ta.getType())
|
||||
result = tl.getReferencedType()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -45,7 +45,7 @@ class SpringComponentScan extends Annotation {
|
||||
// Base package classes are type literals whose package should be considered a base package.
|
||||
typeLiteral = getAValue("basePackageClasses")
|
||||
|
|
||||
result = typeLiteral.getTypeName().getType().(RefType).getPackage().getName()
|
||||
result = typeLiteral.getReferencedType().(RefType).getPackage().getName()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,172 +0,0 @@
|
||||
import semmle.code.java.frameworks.Kryo
|
||||
import semmle.code.java.frameworks.XStream
|
||||
import semmle.code.java.frameworks.SnakeYaml
|
||||
import semmle.code.java.frameworks.FastJson
|
||||
import semmle.code.java.frameworks.JYaml
|
||||
import semmle.code.java.frameworks.JsonIo
|
||||
import semmle.code.java.frameworks.YamlBeans
|
||||
import semmle.code.java.frameworks.HessianBurlap
|
||||
import semmle.code.java.frameworks.Castor
|
||||
import semmle.code.java.frameworks.apache.Lang
|
||||
|
||||
class ObjectInputStreamReadObjectMethod extends Method {
|
||||
ObjectInputStreamReadObjectMethod() {
|
||||
this.getDeclaringType().getASourceSupertype*().hasQualifiedName("java.io", "ObjectInputStream") and
|
||||
(this.hasName("readObject") or this.hasName("readUnshared"))
|
||||
}
|
||||
}
|
||||
|
||||
class XMLDecoderReadObjectMethod extends Method {
|
||||
XMLDecoderReadObjectMethod() {
|
||||
this.getDeclaringType().hasQualifiedName("java.beans", "XMLDecoder") and
|
||||
this.hasName("readObject")
|
||||
}
|
||||
}
|
||||
|
||||
class SafeXStream extends DataFlow2::Configuration {
|
||||
SafeXStream() { this = "UnsafeDeserialization::SafeXStream" }
|
||||
|
||||
override predicate isSource(DataFlow::Node src) {
|
||||
any(XStreamEnableWhiteListing ma).getQualifier().(VarAccess).getVariable().getAnAccess() =
|
||||
src.asExpr()
|
||||
}
|
||||
|
||||
override predicate isSink(DataFlow::Node sink) {
|
||||
exists(MethodAccess ma |
|
||||
sink.asExpr() = ma.getQualifier() and
|
||||
ma.getMethod() instanceof XStreamReadObjectMethod
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
class SafeKryo extends DataFlow2::Configuration {
|
||||
SafeKryo() { this = "UnsafeDeserialization::SafeKryo" }
|
||||
|
||||
override predicate isSource(DataFlow::Node src) {
|
||||
any(KryoEnableWhiteListing ma).getQualifier().(VarAccess).getVariable().getAnAccess() =
|
||||
src.asExpr()
|
||||
}
|
||||
|
||||
override predicate isSink(DataFlow::Node sink) {
|
||||
exists(MethodAccess ma |
|
||||
sink.asExpr() = ma.getQualifier() and
|
||||
ma.getMethod() instanceof KryoReadObjectMethod
|
||||
)
|
||||
}
|
||||
|
||||
override predicate isAdditionalFlowStep(DataFlow::Node node1, DataFlow::Node node2) {
|
||||
stepKryoPoolBuilderFactoryArgToConstructor(node1, node2) or
|
||||
stepKryoPoolRunMethodAccessQualifierToFunctionalArgument(node1, node2) or
|
||||
stepKryoPoolBuilderChainMethod(node1, node2) or
|
||||
stepKryoPoolBorrowMethod(node1, node2)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds when a functional expression is used to create a `KryoPool.Builder`.
|
||||
* Eg. `new KryoPool.Builder(() -> new Kryo())`
|
||||
*/
|
||||
private predicate stepKryoPoolBuilderFactoryArgToConstructor(
|
||||
DataFlow::Node node1, DataFlow::Node node2
|
||||
) {
|
||||
exists(ConstructorCall cc, FunctionalExpr fe |
|
||||
cc.getConstructedType() instanceof KryoPoolBuilder and
|
||||
fe.asMethod().getBody().getAStmt().(ReturnStmt).getResult() = node1.asExpr() and
|
||||
node2.asExpr() = cc and
|
||||
cc.getArgument(0) = fe
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds when a `KryoPool.run` is called to use a `Kryo` instance.
|
||||
* Eg. `pool.run(kryo -> ...)`
|
||||
*/
|
||||
private predicate stepKryoPoolRunMethodAccessQualifierToFunctionalArgument(
|
||||
DataFlow::Node node1, DataFlow::Node node2
|
||||
) {
|
||||
exists(MethodAccess ma |
|
||||
ma.getMethod() instanceof KryoPoolRunMethod and
|
||||
node1.asExpr() = ma.getQualifier() and
|
||||
ma.getArgument(0).(FunctionalExpr).asMethod().getParameter(0) = node2.asParameter()
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds when a `KryoPool.Builder` method is called fluently.
|
||||
*/
|
||||
private predicate stepKryoPoolBuilderChainMethod(DataFlow::Node node1, DataFlow::Node node2) {
|
||||
exists(MethodAccess ma |
|
||||
ma.getMethod() instanceof KryoPoolBuilderMethod and
|
||||
ma = node2.asExpr() and
|
||||
ma.getQualifier() = node1.asExpr()
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds when a `KryoPool.borrow` method is called.
|
||||
*/
|
||||
private predicate stepKryoPoolBorrowMethod(DataFlow::Node node1, DataFlow::Node node2) {
|
||||
exists(MethodAccess ma |
|
||||
ma.getMethod() =
|
||||
any(Method m | m.getDeclaringType() instanceof KryoPool and m.hasName("borrow")) and
|
||||
node1.asExpr() = ma.getQualifier() and
|
||||
node2.asExpr() = ma
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
predicate unsafeDeserialization(MethodAccess ma, Expr sink) {
|
||||
exists(Method m | m = ma.getMethod() |
|
||||
m instanceof ObjectInputStreamReadObjectMethod and
|
||||
sink = ma.getQualifier() and
|
||||
not exists(DataFlow::ExprNode node |
|
||||
node.getExpr() = sink and
|
||||
node.getTypeBound()
|
||||
.(RefType)
|
||||
.hasQualifiedName("org.apache.commons.io.serialization", "ValidatingObjectInputStream")
|
||||
)
|
||||
or
|
||||
m instanceof XMLDecoderReadObjectMethod and
|
||||
sink = ma.getQualifier()
|
||||
or
|
||||
m instanceof XStreamReadObjectMethod and
|
||||
sink = ma.getAnArgument() and
|
||||
not exists(SafeXStream sxs | sxs.hasFlowToExpr(ma.getQualifier()))
|
||||
or
|
||||
m instanceof KryoReadObjectMethod and
|
||||
sink = ma.getAnArgument() and
|
||||
not exists(SafeKryo sk | sk.hasFlowToExpr(ma.getQualifier()))
|
||||
or
|
||||
m instanceof MethodApacheSerializationUtilsDeserialize and
|
||||
sink = ma.getArgument(0)
|
||||
or
|
||||
ma instanceof UnsafeSnakeYamlParse and
|
||||
sink = ma.getArgument(0)
|
||||
or
|
||||
ma.getMethod() instanceof FastJsonParseMethod and
|
||||
not fastJsonLooksSafe() and
|
||||
sink = ma.getArgument(0)
|
||||
or
|
||||
ma.getMethod() instanceof JYamlLoaderUnsafeLoadMethod and
|
||||
sink = ma.getArgument(0)
|
||||
or
|
||||
ma.getMethod() instanceof JsonIoJsonToJavaMethod and
|
||||
sink = ma.getArgument(0)
|
||||
or
|
||||
ma.getMethod() instanceof JsonIoReadObjectMethod and
|
||||
sink = ma.getQualifier()
|
||||
or
|
||||
ma.getMethod() instanceof YamlBeansReaderReadMethod and sink = ma.getQualifier()
|
||||
or
|
||||
ma.getMethod() instanceof UnsafeHessianInputReadObjectMethod and sink = ma.getQualifier()
|
||||
or
|
||||
ma.getMethod() instanceof CastorUnmarshalMethod and sink = ma.getAnArgument()
|
||||
or
|
||||
ma.getMethod() instanceof BurlapInputReadObjectMethod and sink = ma.getQualifier()
|
||||
)
|
||||
}
|
||||
|
||||
class UnsafeDeserializationSink extends DataFlow::ExprNode {
|
||||
UnsafeDeserializationSink() { unsafeDeserialization(_, this.getExpr()) }
|
||||
|
||||
MethodAccess getMethodAccess() { unsafeDeserialization(result, this.getExpr()) }
|
||||
}
|
||||
@@ -0,0 +1,318 @@
|
||||
/**
|
||||
* Provides classes and predicates for finding deserialization vulnerabilities.
|
||||
*/
|
||||
|
||||
import semmle.code.java.dataflow.FlowSources
|
||||
private import semmle.code.java.dataflow.TaintTracking2
|
||||
private import semmle.code.java.frameworks.Kryo
|
||||
private import semmle.code.java.frameworks.XStream
|
||||
private import semmle.code.java.frameworks.SnakeYaml
|
||||
private import semmle.code.java.frameworks.FastJson
|
||||
private import semmle.code.java.frameworks.JYaml
|
||||
private import semmle.code.java.frameworks.JsonIo
|
||||
private import semmle.code.java.frameworks.YamlBeans
|
||||
private import semmle.code.java.frameworks.HessianBurlap
|
||||
private import semmle.code.java.frameworks.Castor
|
||||
private import semmle.code.java.frameworks.Jackson
|
||||
private import semmle.code.java.frameworks.apache.Lang
|
||||
private import semmle.code.java.Reflection
|
||||
|
||||
private class ObjectInputStreamReadObjectMethod extends Method {
|
||||
ObjectInputStreamReadObjectMethod() {
|
||||
this.getDeclaringType().getASourceSupertype*().hasQualifiedName("java.io", "ObjectInputStream") and
|
||||
(this.hasName("readObject") or this.hasName("readUnshared"))
|
||||
}
|
||||
}
|
||||
|
||||
private class XMLDecoderReadObjectMethod extends Method {
|
||||
XMLDecoderReadObjectMethod() {
|
||||
this.getDeclaringType().hasQualifiedName("java.beans", "XMLDecoder") and
|
||||
this.hasName("readObject")
|
||||
}
|
||||
}
|
||||
|
||||
private class SafeXStream extends DataFlow2::Configuration {
|
||||
SafeXStream() { this = "UnsafeDeserialization::SafeXStream" }
|
||||
|
||||
override predicate isSource(DataFlow::Node src) {
|
||||
any(XStreamEnableWhiteListing ma).getQualifier().(VarAccess).getVariable().getAnAccess() =
|
||||
src.asExpr()
|
||||
}
|
||||
|
||||
override predicate isSink(DataFlow::Node sink) {
|
||||
exists(MethodAccess ma |
|
||||
sink.asExpr() = ma.getQualifier() and
|
||||
ma.getMethod() instanceof XStreamReadObjectMethod
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private class SafeKryo extends DataFlow2::Configuration {
|
||||
SafeKryo() { this = "UnsafeDeserialization::SafeKryo" }
|
||||
|
||||
override predicate isSource(DataFlow::Node src) {
|
||||
any(KryoEnableWhiteListing ma).getQualifier().(VarAccess).getVariable().getAnAccess() =
|
||||
src.asExpr()
|
||||
}
|
||||
|
||||
override predicate isSink(DataFlow::Node sink) {
|
||||
exists(MethodAccess ma |
|
||||
sink.asExpr() = ma.getQualifier() and
|
||||
ma.getMethod() instanceof KryoReadObjectMethod
|
||||
)
|
||||
}
|
||||
|
||||
override predicate isAdditionalFlowStep(DataFlow::Node node1, DataFlow::Node node2) {
|
||||
stepKryoPoolBuilderFactoryArgToConstructor(node1, node2) or
|
||||
stepKryoPoolRunMethodAccessQualifierToFunctionalArgument(node1, node2) or
|
||||
stepKryoPoolBuilderChainMethod(node1, node2) or
|
||||
stepKryoPoolBorrowMethod(node1, node2)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds when a functional expression is used to create a `KryoPool.Builder`.
|
||||
* Eg. `new KryoPool.Builder(() -> new Kryo())`
|
||||
*/
|
||||
private predicate stepKryoPoolBuilderFactoryArgToConstructor(
|
||||
DataFlow::Node node1, DataFlow::Node node2
|
||||
) {
|
||||
exists(ConstructorCall cc, FunctionalExpr fe |
|
||||
cc.getConstructedType() instanceof KryoPoolBuilder and
|
||||
fe.asMethod().getBody().getAStmt().(ReturnStmt).getResult() = node1.asExpr() and
|
||||
node2.asExpr() = cc and
|
||||
cc.getArgument(0) = fe
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds when a `KryoPool.run` is called to use a `Kryo` instance.
|
||||
* Eg. `pool.run(kryo -> ...)`
|
||||
*/
|
||||
private predicate stepKryoPoolRunMethodAccessQualifierToFunctionalArgument(
|
||||
DataFlow::Node node1, DataFlow::Node node2
|
||||
) {
|
||||
exists(MethodAccess ma |
|
||||
ma.getMethod() instanceof KryoPoolRunMethod and
|
||||
node1.asExpr() = ma.getQualifier() and
|
||||
ma.getArgument(0).(FunctionalExpr).asMethod().getParameter(0) = node2.asParameter()
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds when a `KryoPool.Builder` method is called fluently.
|
||||
*/
|
||||
private predicate stepKryoPoolBuilderChainMethod(DataFlow::Node node1, DataFlow::Node node2) {
|
||||
exists(MethodAccess ma |
|
||||
ma.getMethod() instanceof KryoPoolBuilderMethod and
|
||||
ma = node2.asExpr() and
|
||||
ma.getQualifier() = node1.asExpr()
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds when a `KryoPool.borrow` method is called.
|
||||
*/
|
||||
private predicate stepKryoPoolBorrowMethod(DataFlow::Node node1, DataFlow::Node node2) {
|
||||
exists(MethodAccess ma |
|
||||
ma.getMethod() =
|
||||
any(Method m | m.getDeclaringType() instanceof KryoPool and m.hasName("borrow")) and
|
||||
node1.asExpr() = ma.getQualifier() and
|
||||
node2.asExpr() = ma
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `ma` is a call that deserializes data from `sink`.
|
||||
*/
|
||||
predicate unsafeDeserialization(MethodAccess ma, Expr sink) {
|
||||
exists(Method m | m = ma.getMethod() |
|
||||
m instanceof ObjectInputStreamReadObjectMethod and
|
||||
sink = ma.getQualifier() and
|
||||
not exists(DataFlow::ExprNode node |
|
||||
node.getExpr() = sink and
|
||||
node.getTypeBound()
|
||||
.(RefType)
|
||||
.hasQualifiedName("org.apache.commons.io.serialization", "ValidatingObjectInputStream")
|
||||
)
|
||||
or
|
||||
m instanceof XMLDecoderReadObjectMethod and
|
||||
sink = ma.getQualifier()
|
||||
or
|
||||
m instanceof XStreamReadObjectMethod and
|
||||
sink = ma.getAnArgument() and
|
||||
not exists(SafeXStream sxs | sxs.hasFlowToExpr(ma.getQualifier()))
|
||||
or
|
||||
m instanceof KryoReadObjectMethod and
|
||||
sink = ma.getAnArgument() and
|
||||
not exists(SafeKryo sk | sk.hasFlowToExpr(ma.getQualifier()))
|
||||
or
|
||||
m instanceof MethodApacheSerializationUtilsDeserialize and
|
||||
sink = ma.getArgument(0)
|
||||
or
|
||||
ma instanceof UnsafeSnakeYamlParse and
|
||||
sink = ma.getArgument(0)
|
||||
or
|
||||
ma.getMethod() instanceof FastJsonParseMethod and
|
||||
not fastJsonLooksSafe() and
|
||||
sink = ma.getArgument(0)
|
||||
or
|
||||
ma.getMethod() instanceof JYamlLoaderUnsafeLoadMethod and
|
||||
sink = ma.getArgument(0)
|
||||
or
|
||||
ma.getMethod() instanceof JsonIoJsonToJavaMethod and
|
||||
sink = ma.getArgument(0)
|
||||
or
|
||||
ma.getMethod() instanceof JsonIoReadObjectMethod and
|
||||
sink = ma.getQualifier()
|
||||
or
|
||||
ma.getMethod() instanceof YamlBeansReaderReadMethod and sink = ma.getQualifier()
|
||||
or
|
||||
ma.getMethod() instanceof UnsafeHessianInputReadObjectMethod and sink = ma.getQualifier()
|
||||
or
|
||||
ma.getMethod() instanceof CastorUnmarshalMethod and sink = ma.getAnArgument()
|
||||
or
|
||||
ma.getMethod() instanceof BurlapInputReadObjectMethod and sink = ma.getQualifier()
|
||||
or
|
||||
ma.getMethod() instanceof ObjectMapperReadMethod and
|
||||
sink = ma.getArgument(0) and
|
||||
(
|
||||
exists(UnsafeTypeConfig config | config.hasFlowToExpr(ma.getAnArgument()))
|
||||
or
|
||||
exists(EnableJacksonDefaultTypingConfig config | config.hasFlowToExpr(ma.getQualifier()))
|
||||
or
|
||||
hasArgumentWithUnsafeJacksonAnnotation(ma)
|
||||
) and
|
||||
not exists(SafeObjectMapperConfig config | config.hasFlowToExpr(ma.getQualifier()))
|
||||
)
|
||||
}
|
||||
|
||||
/** A sink for unsafe deserialization. */
|
||||
class UnsafeDeserializationSink extends DataFlow::ExprNode {
|
||||
UnsafeDeserializationSink() { unsafeDeserialization(_, this.getExpr()) }
|
||||
|
||||
/** Gets a call that triggers unsafe deserialization. */
|
||||
MethodAccess getMethodAccess() { unsafeDeserialization(result, this.getExpr()) }
|
||||
}
|
||||
|
||||
/**
|
||||
* Tracks flows from remote user input to a deserialization sink.
|
||||
*/
|
||||
class UnsafeDeserializationConfig extends TaintTracking::Configuration {
|
||||
UnsafeDeserializationConfig() { this = "UnsafeDeserializationConfig" }
|
||||
|
||||
override predicate isSource(DataFlow::Node source) { source instanceof RemoteFlowSource }
|
||||
|
||||
override predicate isSink(DataFlow::Node sink) { sink instanceof UnsafeDeserializationSink }
|
||||
|
||||
override predicate isAdditionalTaintStep(DataFlow::Node pred, DataFlow::Node succ) {
|
||||
exists(ClassInstanceExpr cie |
|
||||
cie.getArgument(0) = pred.asExpr() and
|
||||
cie = succ.asExpr() and
|
||||
(
|
||||
cie.getConstructor().getDeclaringType() instanceof JsonIoJsonReader or
|
||||
cie.getConstructor().getDeclaringType() instanceof YamlBeansReader or
|
||||
cie.getConstructor().getDeclaringType().getASupertype*() instanceof UnsafeHessianInput or
|
||||
cie.getConstructor().getDeclaringType() instanceof BurlapInput
|
||||
)
|
||||
)
|
||||
or
|
||||
exists(MethodAccess ma |
|
||||
ma.getMethod() instanceof BurlapInputInitMethod and
|
||||
ma.getArgument(0) = pred.asExpr() and
|
||||
ma.getQualifier() = succ.asExpr()
|
||||
)
|
||||
or
|
||||
createJacksonJsonParserStep(pred, succ)
|
||||
or
|
||||
createJacksonTreeNodeStep(pred, succ)
|
||||
}
|
||||
|
||||
override predicate isSanitizer(DataFlow::Node node) {
|
||||
exists(ClassInstanceExpr cie |
|
||||
cie.getConstructor().getDeclaringType() instanceof JsonIoJsonReader and
|
||||
cie = node.asExpr() and
|
||||
exists(SafeJsonIoConfig sji | sji.hasFlowToExpr(cie.getArgument(1)))
|
||||
)
|
||||
or
|
||||
exists(MethodAccess ma |
|
||||
ma.getMethod() instanceof JsonIoJsonToJavaMethod and
|
||||
ma.getArgument(0) = node.asExpr() and
|
||||
exists(SafeJsonIoConfig sji | sji.hasFlowToExpr(ma.getArgument(1)))
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Tracks flow from a remote source to a type descriptor (e.g. a `java.lang.Class` instance)
|
||||
* passed to a Jackson deserialization method.
|
||||
*
|
||||
* If this is user-controlled, arbitrary code could be executed while instantiating the user-specified type.
|
||||
*/
|
||||
class UnsafeTypeConfig extends TaintTracking2::Configuration {
|
||||
UnsafeTypeConfig() { this = "UnsafeTypeConfig" }
|
||||
|
||||
override predicate isSource(DataFlow::Node src) { src instanceof RemoteFlowSource }
|
||||
|
||||
override predicate isSink(DataFlow::Node sink) {
|
||||
exists(MethodAccess ma, int i, Expr arg | i > 0 and ma.getArgument(i) = arg |
|
||||
ma.getMethod() instanceof ObjectMapperReadMethod and
|
||||
arg.getType() instanceof JacksonTypeDescriptorType and
|
||||
arg = sink.asExpr()
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `fromNode` to `toNode` is a dataflow step that resolves a class
|
||||
* or at least looks like resolving a class.
|
||||
*/
|
||||
override predicate isAdditionalTaintStep(DataFlow::Node fromNode, DataFlow::Node toNode) {
|
||||
resolveClassStep(fromNode, toNode) or
|
||||
looksLikeResolveClassStep(fromNode, toNode)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Tracks flow from `enableDefaultTyping` calls to a subsequent Jackson deserialization method call.
|
||||
*/
|
||||
class EnableJacksonDefaultTypingConfig extends DataFlow2::Configuration {
|
||||
EnableJacksonDefaultTypingConfig() { this = "EnableJacksonDefaultTypingConfig" }
|
||||
|
||||
override predicate isSource(DataFlow::Node src) {
|
||||
any(EnableJacksonDefaultTyping ma).getQualifier() = src.asExpr()
|
||||
}
|
||||
|
||||
override predicate isSink(DataFlow::Node sink) { sink instanceof ObjectMapperReadQualifier }
|
||||
}
|
||||
|
||||
/**
|
||||
* Tracks flow from calls that set a type validator to a subsequent Jackson deserialization method call,
|
||||
* including across builder method calls.
|
||||
*
|
||||
* Such a Jackson deserialization method call is safe because validation will likely prevent instantiating unexpected types.
|
||||
*/
|
||||
class SafeObjectMapperConfig extends DataFlow2::Configuration {
|
||||
SafeObjectMapperConfig() { this = "SafeObjectMapperConfig" }
|
||||
|
||||
override predicate isSource(DataFlow::Node src) {
|
||||
src instanceof SetPolymorphicTypeValidatorSource
|
||||
}
|
||||
|
||||
override predicate isSink(DataFlow::Node sink) { sink instanceof ObjectMapperReadQualifier }
|
||||
|
||||
/**
|
||||
* Holds if `fromNode` to `toNode` is a dataflow step
|
||||
* that configures or creates an `ObjectMapper` via a builder.
|
||||
*/
|
||||
override predicate isAdditionalFlowStep(DataFlow::Node fromNode, DataFlow::Node toNode) {
|
||||
exists(MethodAccess ma, Method m | m = ma.getMethod() |
|
||||
m.getDeclaringType() instanceof MapperBuilder and
|
||||
m.getReturnType()
|
||||
.(RefType)
|
||||
.hasQualifiedName("com.fasterxml.jackson.databind.json",
|
||||
["JsonMapper$Builder", "JsonMapper"]) and
|
||||
fromNode.asExpr() = ma.getQualifier() and
|
||||
ma = toNode.asExpr()
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -12,8 +12,8 @@ import sys
|
||||
import tempfile
|
||||
|
||||
if any(s == "--help" for s in sys.argv):
|
||||
print("""Usage:
|
||||
GenerateFlowTestCase.py specsToTest.csv projectPom.xml outdir
|
||||
print("""Usage:
|
||||
GenerateFlowTestCase.py specsToTest.csv projectPom.xml outdir [--force]
|
||||
|
||||
This generates test cases exercising function model specifications found in specsToTest.csv
|
||||
producing files Test.java, test.ql and test.expected in outdir.
|
||||
@@ -22,32 +22,41 @@ projectPom.xml should be a Maven pom sufficient to resolve the classes named in
|
||||
Typically this means supplying a skeleton POM <dependencies> section that retrieves whatever jars
|
||||
contain the needed classes.
|
||||
|
||||
If --force is present, existing files may be overwritten.
|
||||
|
||||
Requirements: `mvn` and `codeql` should both appear on your path.
|
||||
|
||||
After test generation completes, any lines in specsToTest.csv that didn't produce tests are output.
|
||||
If this happens, check the spelling of class and method names, and the syntax of input and output specifications.
|
||||
""")
|
||||
sys.exit(0)
|
||||
sys.exit(0)
|
||||
|
||||
force = False
|
||||
if "--force" in sys.argv:
|
||||
sys.argv.remove("--force")
|
||||
force = True
|
||||
|
||||
if len(sys.argv) != 4:
|
||||
print("Usage: GenerateFlowTestCase.py specsToTest.csv projectPom.xml outdir", file=sys.stderr)
|
||||
print("specsToTest.csv should contain CSV rows describing method taint-propagation specifications to test", file=sys.stderr)
|
||||
print("projectPom.xml should import dependencies sufficient to resolve the types used in specsToTest.csv", file=sys.stderr)
|
||||
sys.exit(1)
|
||||
print(
|
||||
"Usage: GenerateFlowTestCase.py specsToTest.csv projectPom.xml outdir [--force]", file=sys.stderr)
|
||||
print("specsToTest.csv should contain CSV rows describing method taint-propagation specifications to test", file=sys.stderr)
|
||||
print("projectPom.xml should import dependencies sufficient to resolve the types used in specsToTest.csv", file=sys.stderr)
|
||||
sys.exit(1)
|
||||
|
||||
try:
|
||||
os.makedirs(sys.argv[3])
|
||||
os.makedirs(sys.argv[3])
|
||||
except Exception as e:
|
||||
if e.errno != errno.EEXIST:
|
||||
print("Failed to create output directory %s: %s" % (sys.argv[3], e))
|
||||
sys.exit(1)
|
||||
if e.errno != errno.EEXIST:
|
||||
print("Failed to create output directory %s: %s" % (sys.argv[3], e))
|
||||
sys.exit(1)
|
||||
|
||||
resultJava = os.path.join(sys.argv[3], "Test.java")
|
||||
resultQl = os.path.join(sys.argv[3], "test.ql")
|
||||
|
||||
if os.path.exists(resultJava) or os.path.exists(resultQl):
|
||||
print("Won't overwrite existing files '%s' or '%s'" % (resultJava, resultQl), file = sys.stderr)
|
||||
sys.exit(1)
|
||||
if not force and (os.path.exists(resultJava) or os.path.exists(resultQl)):
|
||||
print("Won't overwrite existing files '%s' or '%s'" %
|
||||
(resultJava, resultQl), file=sys.stderr)
|
||||
sys.exit(1)
|
||||
|
||||
workDir = tempfile.mkdtemp()
|
||||
|
||||
@@ -57,129 +66,159 @@ projectDir = os.path.join(workDir, "mavenProject")
|
||||
os.makedirs(projectDir)
|
||||
|
||||
try:
|
||||
shutil.copyfile(sys.argv[2], os.path.join(projectDir, "pom.xml"))
|
||||
shutil.copyfile(sys.argv[2], os.path.join(projectDir, "pom.xml"))
|
||||
except Exception as e:
|
||||
print("Failed to read project POM %s: %s" % (sys.argv[2], e), file = sys.stderr)
|
||||
sys.exit(1)
|
||||
print("Failed to read project POM %s: %s" %
|
||||
(sys.argv[2], e), file=sys.stderr)
|
||||
sys.exit(1)
|
||||
|
||||
commentRegex = re.compile("^\s*(//|#)")
|
||||
|
||||
|
||||
def isComment(s):
|
||||
return commentRegex.match(s) is not None
|
||||
return commentRegex.match(s) is not None
|
||||
|
||||
|
||||
try:
|
||||
with open(sys.argv[1], "r") as f:
|
||||
specs = [l for l in f if not isComment(l)]
|
||||
with open(sys.argv[1], "r") as f:
|
||||
specs = [l for l in f if not isComment(l)]
|
||||
except Exception as e:
|
||||
print("Failed to open %s: %s\n" % (sys.argv[1], e))
|
||||
sys.exit(1)
|
||||
print("Failed to open %s: %s\n" % (sys.argv[1], e))
|
||||
sys.exit(1)
|
||||
|
||||
projectTestPkgDir = os.path.join(projectDir, "src", "main", "java", "test")
|
||||
projectTestFile = os.path.join(projectTestPkgDir, "Test.java")
|
||||
|
||||
os.makedirs(projectTestPkgDir)
|
||||
|
||||
|
||||
def qualifiedOuterNameFromCsvRow(row):
|
||||
cells = row.split(";")
|
||||
if len(cells) < 2:
|
||||
return None
|
||||
return cells[0] + "." + cells[1].replace("$", ".")
|
||||
cells = row.split(";")
|
||||
if len(cells) < 2:
|
||||
return None
|
||||
return cells[0] + "." + cells[1].replace("$", ".")
|
||||
|
||||
|
||||
with open(projectTestFile, "w") as testJava:
|
||||
testJava.write("package test;\n\npublic class Test {\n\n")
|
||||
testJava.write("package test;\n\npublic class Test {\n\n")
|
||||
|
||||
for i, spec in enumerate(specs):
|
||||
outerName = qualifiedOuterNameFromCsvRow(spec)
|
||||
if outerName is None:
|
||||
print("A taint specification has the wrong format: should be 'package;classname;methodname....'", file = sys.stderr)
|
||||
print("Mis-formatted row: " + spec, file = sys.stderr)
|
||||
sys.exit(1)
|
||||
testJava.write("\t%s obj%d = null;\n" % (outerName, i))
|
||||
for i, spec in enumerate(specs):
|
||||
outerName = qualifiedOuterNameFromCsvRow(spec)
|
||||
if outerName is None:
|
||||
print("A taint specification has the wrong format: should be 'package;classname;methodname....'", file=sys.stderr)
|
||||
print("Mis-formatted row: " + spec, file=sys.stderr)
|
||||
sys.exit(1)
|
||||
testJava.write("\t%s obj%d = null;\n" % (outerName, i))
|
||||
|
||||
testJava.write("}")
|
||||
testJava.write("}")
|
||||
|
||||
print("Creating project database")
|
||||
cmd = ["codeql", "database", "create", "--language=java", "db"]
|
||||
ret = subprocess.call(cmd, cwd = projectDir)
|
||||
ret = subprocess.call(cmd, cwd=projectDir)
|
||||
if ret != 0:
|
||||
print("Failed to create project database. Check that '%s' is a valid POM that pulls in all necessary dependencies, and '%s' specifies valid classes and methods." % (sys.argv[2], sys.argv[1]), file = sys.stderr)
|
||||
print("Failed command was: %s (cwd: %s)" % (shlex.join(cmd), projectDir), file = sys.stderr)
|
||||
sys.exit(1)
|
||||
print("Failed to create project database. Check that '%s' is a valid POM that pulls in all necessary dependencies, and '%s' specifies valid classes and methods." % (
|
||||
sys.argv[2], sys.argv[1]), file=sys.stderr)
|
||||
print("Failed command was: %s (cwd: %s)" %
|
||||
(shlex.join(cmd), projectDir), file=sys.stderr)
|
||||
sys.exit(1)
|
||||
|
||||
print("Creating test-generation query")
|
||||
queryDir = os.path.join(workDir, "query")
|
||||
os.makedirs(queryDir)
|
||||
qlFile = os.path.join(queryDir, "gen.ql")
|
||||
with open(os.path.join(queryDir, "qlpack.yml"), "w") as f:
|
||||
f.write("name: test-generation-query\nversion: 0.0.0\nlibraryPathDependencies: codeql-java")
|
||||
f.write("name: test-generation-query\nversion: 0.0.0\nlibraryPathDependencies: codeql-java")
|
||||
with open(qlFile, "w") as f:
|
||||
f.write("import java\nimport utils.GenerateFlowTestCase\n\nclass GenRow extends TargetSummaryModelCsv {\n\n\toverride predicate row(string r) {\n\t\tr = [\n")
|
||||
f.write(",\n".join('\t\t\t"%s"' % spec.strip() for spec in specs))
|
||||
f.write("\n\t\t]\n\t}\n}\n")
|
||||
f.write(
|
||||
"import java\nimport utils.GenerateFlowTestCase\n\nclass GenRow extends TargetSummaryModelCsv {\n\n\toverride predicate row(string r) {\n\t\tr = [\n")
|
||||
f.write(",\n".join('\t\t\t"%s"' % spec.strip() for spec in specs))
|
||||
f.write("\n\t\t]\n\t}\n}\n")
|
||||
|
||||
print("Generating tests")
|
||||
generatedBqrs = os.path.join(queryDir, "out.bqrs")
|
||||
cmd = ['codeql', 'query', 'run', qlFile, '--database', os.path.join(projectDir, "db"), '--output', generatedBqrs]
|
||||
cmd = ['codeql', 'query', 'run', qlFile, '--database',
|
||||
os.path.join(projectDir, "db"), '--output', generatedBqrs]
|
||||
ret = subprocess.call(cmd)
|
||||
if ret != 0:
|
||||
print("Failed to generate tests. Failed command was: " + shlex.join(cmd))
|
||||
sys.exit(1)
|
||||
print("Failed to generate tests. Failed command was: " + shlex.join(cmd))
|
||||
sys.exit(1)
|
||||
|
||||
generatedJson = os.path.join(queryDir, "out.json")
|
||||
cmd = ['codeql', 'bqrs', 'decode', generatedBqrs, '--format=json', '--output', generatedJson]
|
||||
cmd = ['codeql', 'bqrs', 'decode', generatedBqrs,
|
||||
'--format=json', '--output', generatedJson]
|
||||
ret = subprocess.call(cmd)
|
||||
if ret != 0:
|
||||
print("Failed to decode BQRS. Failed command was: " + shlex.join(cmd))
|
||||
sys.exit(1)
|
||||
print("Failed to decode BQRS. Failed command was: " + shlex.join(cmd))
|
||||
sys.exit(1)
|
||||
|
||||
|
||||
def getTuples(queryName, jsonResult, fname):
|
||||
if queryName not in jsonResult or "tuples" not in jsonResult[queryName]:
|
||||
print("Failed to read generated tests: expected key '%s' with a 'tuples' subkey in file '%s'" % (queryName, fname), file = sys.stderr)
|
||||
sys.exit(1)
|
||||
return jsonResult[queryName]["tuples"]
|
||||
if queryName not in jsonResult or "tuples" not in jsonResult[queryName]:
|
||||
print("Failed to read generated tests: expected key '%s' with a 'tuples' subkey in file '%s'" % (
|
||||
queryName, fname), file=sys.stderr)
|
||||
sys.exit(1)
|
||||
return jsonResult[queryName]["tuples"]
|
||||
|
||||
|
||||
with open(generatedJson, "r") as f:
|
||||
generateOutput = json.load(f)
|
||||
expectedTables = ("getTestCase", "getASupportMethodModel", "missingSummaryModelCsv", "getAParseFailure")
|
||||
generateOutput = json.load(f)
|
||||
expectedTables = ("getTestCase", "getASupportMethodModel",
|
||||
"missingSummaryModelCsv", "getAParseFailure", "noTestCaseGenerated")
|
||||
|
||||
testCaseRows, supportModelRows, missingSummaryModelCsvRows, parseFailureRows = \
|
||||
tuple([getTuples(k, generateOutput, generatedJson) for k in expectedTables])
|
||||
testCaseRows, supportModelRows, missingSummaryModelCsvRows, parseFailureRows, noTestCaseGeneratedRows = \
|
||||
tuple([getTuples(k, generateOutput, generatedJson)
|
||||
for k in expectedTables])
|
||||
|
||||
if len(testCaseRows) != 1 or len(testCaseRows[0]) != 1:
|
||||
print("Expected exactly one getTestCase result with one column (got: %s)" % json.dumps(testCaseRows), file = sys.stderr)
|
||||
if any(len(row) != 1 for row in supportModelRows):
|
||||
print("Expected exactly one column in getASupportMethodModel relation (got: %s)" % json.dumps(supportModelRows), file = sys.stderr)
|
||||
if any(len(row) != 2 for row in parseFailureRows):
|
||||
print("Expected exactly two columns in parseFailureRows relation (got: %s)" % json.dumps(parseFailureRows), file = sys.stderr)
|
||||
if len(testCaseRows) != 1 or len(testCaseRows[0]) != 1:
|
||||
print("Expected exactly one getTestCase result with one column (got: %s)" %
|
||||
json.dumps(testCaseRows), file=sys.stderr)
|
||||
if any(len(row) != 1 for row in supportModelRows):
|
||||
print("Expected exactly one column in getASupportMethodModel relation (got: %s)" %
|
||||
json.dumps(supportModelRows), file=sys.stderr)
|
||||
if any(len(row) != 2 for row in parseFailureRows):
|
||||
print("Expected exactly two columns in parseFailureRows relation (got: %s)" %
|
||||
json.dumps(parseFailureRows), file=sys.stderr)
|
||||
if any(len(row) != 1 for row in noTestCaseGeneratedRows):
|
||||
print("Expected exactly one column in noTestCaseGenerated relation (got: %s)" %
|
||||
json.dumps(noTestCaseGeneratedRows), file=sys.stderr)
|
||||
|
||||
if len(missingSummaryModelCsvRows) != 0:
|
||||
print("Tests for some CSV rows were requested that were not in scope (SummaryModelCsv.row does not hold):\n" + "\n".join(r[0] for r in missingSummaryModelCsvRows))
|
||||
sys.exit(1)
|
||||
if len(parseFailureRows) != 0:
|
||||
print("The following rows failed to generate any test case. Check package, class and method name spelling, and argument and result specifications:\n%s" % "\n".join(r[0] + ": " + r[1] for r in parseFailureRows), file = sys.stderr)
|
||||
sys.exit(1)
|
||||
if len(missingSummaryModelCsvRows) != 0:
|
||||
print("Tests for some CSV rows were requested that were not in scope (SummaryModelCsv.row does not hold):\n" +
|
||||
"\n".join(r[0] for r in missingSummaryModelCsvRows))
|
||||
sys.exit(1)
|
||||
if len(parseFailureRows) != 0:
|
||||
print("The following rows failed to generate any test case. Check package, class and method name spelling, and argument and result specifications:\n%s" %
|
||||
"\n".join(r[0] + ": " + r[1] for r in parseFailureRows), file=sys.stderr)
|
||||
sys.exit(1)
|
||||
if len(noTestCaseGeneratedRows) != 0:
|
||||
print("The following CSV rows failed to generate any test case due to a limitation of the query. Other test cases will still be generated:\n" +
|
||||
"\n".join(r[0] for r in noTestCaseGeneratedRows))
|
||||
|
||||
with open(resultJava, "w") as f:
|
||||
f.write(generateOutput["getTestCase"]["tuples"][0][0])
|
||||
f.write(generateOutput["getTestCase"]["tuples"][0][0])
|
||||
|
||||
scriptPath = os.path.dirname(sys.argv[0])
|
||||
|
||||
|
||||
def copyfile(fromName, toFileHandle):
|
||||
with open(os.path.join(scriptPath, fromName), "r") as fromFileHandle:
|
||||
shutil.copyfileobj(fromFileHandle, toFileHandle)
|
||||
with open(os.path.join(scriptPath, fromName), "r") as fromFileHandle:
|
||||
shutil.copyfileobj(fromFileHandle, toFileHandle)
|
||||
|
||||
|
||||
with open(resultQl, "w") as f:
|
||||
copyfile("testHeader.qlfrag", f)
|
||||
if len(supportModelRows) != 0:
|
||||
copyfile("testModelsHeader.qlfrag", f)
|
||||
f.write(", ".join('"%s"' % modelSpecRow[0].strip() for modelSpecRow in supportModelRows))
|
||||
copyfile("testModelsFooter.qlfrag", f)
|
||||
copyfile("testFooter.qlfrag", f)
|
||||
copyfile("testHeader.qlfrag", f)
|
||||
if len(supportModelRows) != 0:
|
||||
copyfile("testModelsHeader.qlfrag", f)
|
||||
f.write(", ".join('"%s"' %
|
||||
modelSpecRow[0].strip() for modelSpecRow in supportModelRows))
|
||||
copyfile("testModelsFooter.qlfrag", f)
|
||||
copyfile("testFooter.qlfrag", f)
|
||||
|
||||
# Make an empty .expected file, since this is an inline-exectations test
|
||||
with open(os.path.join(sys.argv[3], "test.expected"), "w"):
|
||||
pass
|
||||
pass
|
||||
|
||||
cmd = ['codeql', 'query', 'format', '-qq', '-i', resultQl]
|
||||
subprocess.call(cmd)
|
||||
|
||||
shutil.rmtree(workDir)
|
||||
shutil.rmtree(workDir)
|
||||
|
||||
@@ -37,6 +37,9 @@ query string getAParseFailure(string reason) {
|
||||
any(TargetSummaryModelCsv target).row(result) and
|
||||
any(SummaryModelCsv model).row(result) and
|
||||
(
|
||||
not summaryModel(_, _, _, _, _, _, _, _, _, result) and
|
||||
reason = "row could not be parsed"
|
||||
or
|
||||
exists(
|
||||
string namespace, string type, boolean subtypes, string name, string signature, string ext
|
||||
|
|
||||
@@ -59,13 +62,26 @@ query string getAParseFailure(string reason) {
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a CSV row for which a test was requested and was correctly parsed,
|
||||
* but for which no test case could be generated due to a limitation of the query.
|
||||
*/
|
||||
query string noTestCaseGenerated() {
|
||||
any(TargetSummaryModelCsv target).row(result) and
|
||||
any(SummaryModelCsv model).row(result) and
|
||||
not exists(getAParseFailure(_)) and
|
||||
not exists(any(TestCase tc).getATestSnippetForRow(result))
|
||||
}
|
||||
|
||||
private class CallableToTest extends Callable {
|
||||
CallableToTest() {
|
||||
exists(
|
||||
string namespace, string type, boolean subtypes, string name, string signature, string ext
|
||||
|
|
||||
summaryModel(namespace, type, subtypes, name, signature, ext, _, _, _) and
|
||||
this = interpretElement(namespace, type, subtypes, name, signature, ext)
|
||||
this = interpretElement(namespace, type, subtypes, name, signature, ext) and
|
||||
this.isPublic() and
|
||||
getRootType(this.getDeclaringType()).isPublic()
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -124,6 +140,21 @@ string getFieldToken(FieldContent fc) {
|
||||
fc.getField().getName()
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a valid Java token naming the synthetic field `fc`,
|
||||
* assuming that the name of that field consists only of characters valid in a Java identifier and `.`.
|
||||
*/
|
||||
string getSyntheticFieldToken(SyntheticFieldContent fc) {
|
||||
exists(string name, int parts |
|
||||
name = fc.getField() and
|
||||
parts = count(name.splitAt("."))
|
||||
|
|
||||
if parts = 1
|
||||
then result = name
|
||||
else result = name.splitAt(".", parts - 2) + "_" + name.splitAt(".", parts - 1)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a token suitable for incorporation into a Java method name describing content `c`.
|
||||
*/
|
||||
@@ -137,6 +168,8 @@ string contentToken(Content c) {
|
||||
c instanceof MapValueContent and result = "MapValue"
|
||||
or
|
||||
result = getFieldToken(c)
|
||||
or
|
||||
result = getSyntheticFieldToken(c)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -145,7 +178,10 @@ string contentToken(Content c) {
|
||||
RefType getRootType(RefType t) {
|
||||
if t instanceof NestedType
|
||||
then result = getRootType(t.(NestedType).getEnclosingType())
|
||||
else result = t
|
||||
else
|
||||
if t instanceof Array
|
||||
then result = getRootType(t.(Array).getElementType())
|
||||
else result = t
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -406,6 +442,8 @@ class TestCase extends TTestCase {
|
||||
content instanceof CollectionContent and result = "Element"
|
||||
or
|
||||
result = "Field[" + content.(FieldContent).getField().getQualifiedName() + "]"
|
||||
or
|
||||
result = "SyntheticField[" + content.(SyntheticFieldContent).getField() + "]"
|
||||
)
|
||||
)
|
||||
}
|
||||
@@ -482,18 +520,22 @@ predicate isImportable(Type t) {
|
||||
* if we cannot import it due to a name clash.
|
||||
*/
|
||||
string getShortNameIfPossible(Type t) {
|
||||
getRootSourceDeclaration(t) = any(TestCase tc).getADesiredImport() and
|
||||
if t instanceof RefType
|
||||
then
|
||||
exists(RefType replaced, string nestedName |
|
||||
replaced = replaceTypeVariable(t).getSourceDeclaration() and
|
||||
nestedName = replaced.nestedName().replaceAll("$", ".")
|
||||
|
|
||||
if isImportable(getRootSourceDeclaration(t))
|
||||
then result = nestedName
|
||||
else result = replaced.getPackage().getName() + "." + nestedName
|
||||
)
|
||||
else result = t.getName()
|
||||
if t instanceof Array
|
||||
then result = getShortNameIfPossible(t.(Array).getComponentType()) + "[]"
|
||||
else (
|
||||
getRootSourceDeclaration(t) = any(TestCase tc).getADesiredImport() and
|
||||
if t instanceof RefType
|
||||
then
|
||||
exists(RefType replaced, string nestedName |
|
||||
replaced = replaceTypeVariable(t).getSourceDeclaration() and
|
||||
nestedName = replaced.nestedName().replaceAll("$", ".")
|
||||
|
|
||||
if isImportable(getRootSourceDeclaration(t))
|
||||
then result = nestedName
|
||||
else result = replaced.getPackage().getName() + "." + nestedName
|
||||
)
|
||||
else result = t.getName()
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -530,7 +572,7 @@ query string getTestCase() {
|
||||
result =
|
||||
"package generatedtest;\n\n" + concat(getAnImportStatement() + "\n") +
|
||||
"\n// Test case generated by GenerateFlowTestCase.ql\npublic class Test {\n\n" +
|
||||
concat("\t" + getASupportMethod() + "\n") + "\n\tpublic void test() {\n\n" +
|
||||
concat("\t" + getASupportMethod() + "\n") + "\n\tpublic void test() throws Exception {\n\n" +
|
||||
concat(string row, string snippet |
|
||||
snippet = any(TestCase tc).getATestSnippetForRow(row)
|
||||
|
|
||||
|
||||
@@ -1,35 +1,90 @@
|
||||
edges
|
||||
| SpringUrlRedirect.java:13:30:13:47 | redirectUrl : String | SpringUrlRedirect.java:15:19:15:29 | redirectUrl |
|
||||
| SpringUrlRedirect.java:20:24:20:41 | redirectUrl : String | SpringUrlRedirect.java:21:36:21:46 | redirectUrl |
|
||||
| SpringUrlRedirect.java:26:30:26:47 | redirectUrl : String | SpringUrlRedirect.java:27:44:27:54 | redirectUrl |
|
||||
| SpringUrlRedirect.java:32:30:32:47 | redirectUrl : String | SpringUrlRedirect.java:33:47:33:57 | redirectUrl |
|
||||
| SpringUrlRedirect.java:37:24:37:41 | redirectUrl : String | SpringUrlRedirect.java:40:29:40:39 | redirectUrl |
|
||||
| SpringUrlRedirect.java:45:24:45:41 | redirectUrl : String | SpringUrlRedirect.java:48:30:48:40 | redirectUrl |
|
||||
| SpringUrlRedirect.java:53:24:53:41 | redirectUrl : String | SpringUrlRedirect.java:54:30:54:66 | format(...) |
|
||||
| SpringUrlRedirect.java:58:24:58:41 | redirectUrl : String | SpringUrlRedirect.java:59:30:59:76 | format(...) |
|
||||
| SpringUrlRedirect.java:17:30:17:47 | redirectUrl : String | SpringUrlRedirect.java:19:19:19:29 | redirectUrl |
|
||||
| SpringUrlRedirect.java:24:24:24:41 | redirectUrl : String | SpringUrlRedirect.java:25:36:25:46 | redirectUrl |
|
||||
| SpringUrlRedirect.java:30:30:30:47 | redirectUrl : String | SpringUrlRedirect.java:31:44:31:54 | redirectUrl |
|
||||
| SpringUrlRedirect.java:36:30:36:47 | redirectUrl : String | SpringUrlRedirect.java:37:47:37:57 | redirectUrl |
|
||||
| SpringUrlRedirect.java:41:24:41:41 | redirectUrl : String | SpringUrlRedirect.java:44:29:44:39 | redirectUrl |
|
||||
| SpringUrlRedirect.java:49:24:49:41 | redirectUrl : String | SpringUrlRedirect.java:52:30:52:40 | redirectUrl |
|
||||
| SpringUrlRedirect.java:57:24:57:41 | redirectUrl : String | SpringUrlRedirect.java:58:30:58:66 | format(...) |
|
||||
| SpringUrlRedirect.java:62:24:62:41 | redirectUrl : String | SpringUrlRedirect.java:63:30:63:76 | format(...) |
|
||||
| SpringUrlRedirect.java:89:38:89:55 | redirectUrl : String | SpringUrlRedirect.java:91:38:91:48 | redirectUrl : String |
|
||||
| SpringUrlRedirect.java:91:38:91:48 | redirectUrl : String | SpringUrlRedirect.java:91:27:91:49 | create(...) |
|
||||
| SpringUrlRedirect.java:96:39:96:56 | redirectUrl : String | SpringUrlRedirect.java:98:44:98:54 | redirectUrl : String |
|
||||
| SpringUrlRedirect.java:98:33:98:55 | create(...) : URI | SpringUrlRedirect.java:100:37:100:47 | httpHeaders |
|
||||
| SpringUrlRedirect.java:98:44:98:54 | redirectUrl : String | SpringUrlRedirect.java:98:33:98:55 | create(...) : URI |
|
||||
| SpringUrlRedirect.java:104:39:104:56 | redirectUrl : String | SpringUrlRedirect.java:106:37:106:47 | redirectUrl : String |
|
||||
| SpringUrlRedirect.java:106:9:106:19 | httpHeaders [post update] : HttpHeaders | SpringUrlRedirect.java:108:68:108:78 | httpHeaders |
|
||||
| SpringUrlRedirect.java:106:9:106:19 | httpHeaders [post update] [<map.value>, <element>] : String | SpringUrlRedirect.java:108:68:108:78 | httpHeaders |
|
||||
| SpringUrlRedirect.java:106:37:106:47 | redirectUrl : String | SpringUrlRedirect.java:106:9:106:19 | httpHeaders [post update] : HttpHeaders |
|
||||
| SpringUrlRedirect.java:106:37:106:47 | redirectUrl : String | SpringUrlRedirect.java:106:9:106:19 | httpHeaders [post update] [<map.value>, <element>] : String |
|
||||
| SpringUrlRedirect.java:112:39:112:56 | redirectUrl : String | SpringUrlRedirect.java:114:37:114:47 | redirectUrl : String |
|
||||
| SpringUrlRedirect.java:114:9:114:19 | httpHeaders [post update] : HttpHeaders | SpringUrlRedirect.java:116:37:116:47 | httpHeaders |
|
||||
| SpringUrlRedirect.java:114:9:114:19 | httpHeaders [post update] [<map.value>, <element>] : String | SpringUrlRedirect.java:116:37:116:47 | httpHeaders |
|
||||
| SpringUrlRedirect.java:114:37:114:47 | redirectUrl : String | SpringUrlRedirect.java:114:9:114:19 | httpHeaders [post update] : HttpHeaders |
|
||||
| SpringUrlRedirect.java:114:37:114:47 | redirectUrl : String | SpringUrlRedirect.java:114:9:114:19 | httpHeaders [post update] [<map.value>, <element>] : String |
|
||||
| SpringUrlRedirect.java:120:33:120:50 | redirectUrl : String | SpringUrlRedirect.java:122:37:122:47 | redirectUrl : String |
|
||||
| SpringUrlRedirect.java:122:9:122:19 | httpHeaders [post update] : HttpHeaders | SpringUrlRedirect.java:124:49:124:59 | httpHeaders |
|
||||
| SpringUrlRedirect.java:122:9:122:19 | httpHeaders [post update] [<map.value>, <element>] : String | SpringUrlRedirect.java:124:49:124:59 | httpHeaders |
|
||||
| SpringUrlRedirect.java:122:37:122:47 | redirectUrl : String | SpringUrlRedirect.java:122:9:122:19 | httpHeaders [post update] : HttpHeaders |
|
||||
| SpringUrlRedirect.java:122:37:122:47 | redirectUrl : String | SpringUrlRedirect.java:122:9:122:19 | httpHeaders [post update] [<map.value>, <element>] : String |
|
||||
| SpringUrlRedirect.java:128:33:128:50 | redirectUrl : String | SpringUrlRedirect.java:130:44:130:54 | redirectUrl : String |
|
||||
| SpringUrlRedirect.java:130:33:130:55 | create(...) : URI | SpringUrlRedirect.java:132:49:132:59 | httpHeaders |
|
||||
| SpringUrlRedirect.java:130:44:130:54 | redirectUrl : String | SpringUrlRedirect.java:130:33:130:55 | create(...) : URI |
|
||||
nodes
|
||||
| SpringUrlRedirect.java:13:30:13:47 | redirectUrl : String | semmle.label | redirectUrl : String |
|
||||
| SpringUrlRedirect.java:15:19:15:29 | redirectUrl | semmle.label | redirectUrl |
|
||||
| SpringUrlRedirect.java:20:24:20:41 | redirectUrl : String | semmle.label | redirectUrl : String |
|
||||
| SpringUrlRedirect.java:21:36:21:46 | redirectUrl | semmle.label | redirectUrl |
|
||||
| SpringUrlRedirect.java:26:30:26:47 | redirectUrl : String | semmle.label | redirectUrl : String |
|
||||
| SpringUrlRedirect.java:27:44:27:54 | redirectUrl | semmle.label | redirectUrl |
|
||||
| SpringUrlRedirect.java:32:30:32:47 | redirectUrl : String | semmle.label | redirectUrl : String |
|
||||
| SpringUrlRedirect.java:33:47:33:57 | redirectUrl | semmle.label | redirectUrl |
|
||||
| SpringUrlRedirect.java:37:24:37:41 | redirectUrl : String | semmle.label | redirectUrl : String |
|
||||
| SpringUrlRedirect.java:40:29:40:39 | redirectUrl | semmle.label | redirectUrl |
|
||||
| SpringUrlRedirect.java:45:24:45:41 | redirectUrl : String | semmle.label | redirectUrl : String |
|
||||
| SpringUrlRedirect.java:48:30:48:40 | redirectUrl | semmle.label | redirectUrl |
|
||||
| SpringUrlRedirect.java:53:24:53:41 | redirectUrl : String | semmle.label | redirectUrl : String |
|
||||
| SpringUrlRedirect.java:54:30:54:66 | format(...) | semmle.label | format(...) |
|
||||
| SpringUrlRedirect.java:58:24:58:41 | redirectUrl : String | semmle.label | redirectUrl : String |
|
||||
| SpringUrlRedirect.java:59:30:59:76 | format(...) | semmle.label | format(...) |
|
||||
| SpringUrlRedirect.java:17:30:17:47 | redirectUrl : String | semmle.label | redirectUrl : String |
|
||||
| SpringUrlRedirect.java:19:19:19:29 | redirectUrl | semmle.label | redirectUrl |
|
||||
| SpringUrlRedirect.java:24:24:24:41 | redirectUrl : String | semmle.label | redirectUrl : String |
|
||||
| SpringUrlRedirect.java:25:36:25:46 | redirectUrl | semmle.label | redirectUrl |
|
||||
| SpringUrlRedirect.java:30:30:30:47 | redirectUrl : String | semmle.label | redirectUrl : String |
|
||||
| SpringUrlRedirect.java:31:44:31:54 | redirectUrl | semmle.label | redirectUrl |
|
||||
| SpringUrlRedirect.java:36:30:36:47 | redirectUrl : String | semmle.label | redirectUrl : String |
|
||||
| SpringUrlRedirect.java:37:47:37:57 | redirectUrl | semmle.label | redirectUrl |
|
||||
| SpringUrlRedirect.java:41:24:41:41 | redirectUrl : String | semmle.label | redirectUrl : String |
|
||||
| SpringUrlRedirect.java:44:29:44:39 | redirectUrl | semmle.label | redirectUrl |
|
||||
| SpringUrlRedirect.java:49:24:49:41 | redirectUrl : String | semmle.label | redirectUrl : String |
|
||||
| SpringUrlRedirect.java:52:30:52:40 | redirectUrl | semmle.label | redirectUrl |
|
||||
| SpringUrlRedirect.java:57:24:57:41 | redirectUrl : String | semmle.label | redirectUrl : String |
|
||||
| SpringUrlRedirect.java:58:30:58:66 | format(...) | semmle.label | format(...) |
|
||||
| SpringUrlRedirect.java:62:24:62:41 | redirectUrl : String | semmle.label | redirectUrl : String |
|
||||
| SpringUrlRedirect.java:63:30:63:76 | format(...) | semmle.label | format(...) |
|
||||
| SpringUrlRedirect.java:89:38:89:55 | redirectUrl : String | semmle.label | redirectUrl : String |
|
||||
| SpringUrlRedirect.java:91:27:91:49 | create(...) | semmle.label | create(...) |
|
||||
| SpringUrlRedirect.java:91:38:91:48 | redirectUrl : String | semmle.label | redirectUrl : String |
|
||||
| SpringUrlRedirect.java:96:39:96:56 | redirectUrl : String | semmle.label | redirectUrl : String |
|
||||
| SpringUrlRedirect.java:98:33:98:55 | create(...) : URI | semmle.label | create(...) : URI |
|
||||
| SpringUrlRedirect.java:98:44:98:54 | redirectUrl : String | semmle.label | redirectUrl : String |
|
||||
| SpringUrlRedirect.java:100:37:100:47 | httpHeaders | semmle.label | httpHeaders |
|
||||
| SpringUrlRedirect.java:104:39:104:56 | redirectUrl : String | semmle.label | redirectUrl : String |
|
||||
| SpringUrlRedirect.java:106:9:106:19 | httpHeaders [post update] : HttpHeaders | semmle.label | httpHeaders [post update] : HttpHeaders |
|
||||
| SpringUrlRedirect.java:106:9:106:19 | httpHeaders [post update] [<map.value>, <element>] : String | semmle.label | httpHeaders [post update] [<map.value>, <element>] : String |
|
||||
| SpringUrlRedirect.java:106:37:106:47 | redirectUrl : String | semmle.label | redirectUrl : String |
|
||||
| SpringUrlRedirect.java:108:68:108:78 | httpHeaders | semmle.label | httpHeaders |
|
||||
| SpringUrlRedirect.java:112:39:112:56 | redirectUrl : String | semmle.label | redirectUrl : String |
|
||||
| SpringUrlRedirect.java:114:9:114:19 | httpHeaders [post update] : HttpHeaders | semmle.label | httpHeaders [post update] : HttpHeaders |
|
||||
| SpringUrlRedirect.java:114:9:114:19 | httpHeaders [post update] [<map.value>, <element>] : String | semmle.label | httpHeaders [post update] [<map.value>, <element>] : String |
|
||||
| SpringUrlRedirect.java:114:37:114:47 | redirectUrl : String | semmle.label | redirectUrl : String |
|
||||
| SpringUrlRedirect.java:116:37:116:47 | httpHeaders | semmle.label | httpHeaders |
|
||||
| SpringUrlRedirect.java:120:33:120:50 | redirectUrl : String | semmle.label | redirectUrl : String |
|
||||
| SpringUrlRedirect.java:122:9:122:19 | httpHeaders [post update] : HttpHeaders | semmle.label | httpHeaders [post update] : HttpHeaders |
|
||||
| SpringUrlRedirect.java:122:9:122:19 | httpHeaders [post update] [<map.value>, <element>] : String | semmle.label | httpHeaders [post update] [<map.value>, <element>] : String |
|
||||
| SpringUrlRedirect.java:122:37:122:47 | redirectUrl : String | semmle.label | redirectUrl : String |
|
||||
| SpringUrlRedirect.java:124:49:124:59 | httpHeaders | semmle.label | httpHeaders |
|
||||
| SpringUrlRedirect.java:128:33:128:50 | redirectUrl : String | semmle.label | redirectUrl : String |
|
||||
| SpringUrlRedirect.java:130:33:130:55 | create(...) : URI | semmle.label | create(...) : URI |
|
||||
| SpringUrlRedirect.java:130:44:130:54 | redirectUrl : String | semmle.label | redirectUrl : String |
|
||||
| SpringUrlRedirect.java:132:49:132:59 | httpHeaders | semmle.label | httpHeaders |
|
||||
#select
|
||||
| SpringUrlRedirect.java:15:19:15:29 | redirectUrl | SpringUrlRedirect.java:13:30:13:47 | redirectUrl : String | SpringUrlRedirect.java:15:19:15:29 | redirectUrl | Potentially untrusted URL redirection due to $@. | SpringUrlRedirect.java:13:30:13:47 | redirectUrl | user-provided value |
|
||||
| SpringUrlRedirect.java:21:36:21:46 | redirectUrl | SpringUrlRedirect.java:20:24:20:41 | redirectUrl : String | SpringUrlRedirect.java:21:36:21:46 | redirectUrl | Potentially untrusted URL redirection due to $@. | SpringUrlRedirect.java:20:24:20:41 | redirectUrl | user-provided value |
|
||||
| SpringUrlRedirect.java:27:44:27:54 | redirectUrl | SpringUrlRedirect.java:26:30:26:47 | redirectUrl : String | SpringUrlRedirect.java:27:44:27:54 | redirectUrl | Potentially untrusted URL redirection due to $@. | SpringUrlRedirect.java:26:30:26:47 | redirectUrl | user-provided value |
|
||||
| SpringUrlRedirect.java:33:47:33:57 | redirectUrl | SpringUrlRedirect.java:32:30:32:47 | redirectUrl : String | SpringUrlRedirect.java:33:47:33:57 | redirectUrl | Potentially untrusted URL redirection due to $@. | SpringUrlRedirect.java:32:30:32:47 | redirectUrl | user-provided value |
|
||||
| SpringUrlRedirect.java:40:29:40:39 | redirectUrl | SpringUrlRedirect.java:37:24:37:41 | redirectUrl : String | SpringUrlRedirect.java:40:29:40:39 | redirectUrl | Potentially untrusted URL redirection due to $@. | SpringUrlRedirect.java:37:24:37:41 | redirectUrl | user-provided value |
|
||||
| SpringUrlRedirect.java:48:30:48:40 | redirectUrl | SpringUrlRedirect.java:45:24:45:41 | redirectUrl : String | SpringUrlRedirect.java:48:30:48:40 | redirectUrl | Potentially untrusted URL redirection due to $@. | SpringUrlRedirect.java:45:24:45:41 | redirectUrl | user-provided value |
|
||||
| SpringUrlRedirect.java:54:30:54:66 | format(...) | SpringUrlRedirect.java:53:24:53:41 | redirectUrl : String | SpringUrlRedirect.java:54:30:54:66 | format(...) | Potentially untrusted URL redirection due to $@. | SpringUrlRedirect.java:53:24:53:41 | redirectUrl | user-provided value |
|
||||
| SpringUrlRedirect.java:59:30:59:76 | format(...) | SpringUrlRedirect.java:58:24:58:41 | redirectUrl : String | SpringUrlRedirect.java:59:30:59:76 | format(...) | Potentially untrusted URL redirection due to $@. | SpringUrlRedirect.java:58:24:58:41 | redirectUrl | user-provided value |
|
||||
| SpringUrlRedirect.java:19:19:19:29 | redirectUrl | SpringUrlRedirect.java:17:30:17:47 | redirectUrl : String | SpringUrlRedirect.java:19:19:19:29 | redirectUrl | Potentially untrusted URL redirection due to $@. | SpringUrlRedirect.java:17:30:17:47 | redirectUrl | user-provided value |
|
||||
| SpringUrlRedirect.java:25:36:25:46 | redirectUrl | SpringUrlRedirect.java:24:24:24:41 | redirectUrl : String | SpringUrlRedirect.java:25:36:25:46 | redirectUrl | Potentially untrusted URL redirection due to $@. | SpringUrlRedirect.java:24:24:24:41 | redirectUrl | user-provided value |
|
||||
| SpringUrlRedirect.java:31:44:31:54 | redirectUrl | SpringUrlRedirect.java:30:30:30:47 | redirectUrl : String | SpringUrlRedirect.java:31:44:31:54 | redirectUrl | Potentially untrusted URL redirection due to $@. | SpringUrlRedirect.java:30:30:30:47 | redirectUrl | user-provided value |
|
||||
| SpringUrlRedirect.java:37:47:37:57 | redirectUrl | SpringUrlRedirect.java:36:30:36:47 | redirectUrl : String | SpringUrlRedirect.java:37:47:37:57 | redirectUrl | Potentially untrusted URL redirection due to $@. | SpringUrlRedirect.java:36:30:36:47 | redirectUrl | user-provided value |
|
||||
| SpringUrlRedirect.java:44:29:44:39 | redirectUrl | SpringUrlRedirect.java:41:24:41:41 | redirectUrl : String | SpringUrlRedirect.java:44:29:44:39 | redirectUrl | Potentially untrusted URL redirection due to $@. | SpringUrlRedirect.java:41:24:41:41 | redirectUrl | user-provided value |
|
||||
| SpringUrlRedirect.java:52:30:52:40 | redirectUrl | SpringUrlRedirect.java:49:24:49:41 | redirectUrl : String | SpringUrlRedirect.java:52:30:52:40 | redirectUrl | Potentially untrusted URL redirection due to $@. | SpringUrlRedirect.java:49:24:49:41 | redirectUrl | user-provided value |
|
||||
| SpringUrlRedirect.java:58:30:58:66 | format(...) | SpringUrlRedirect.java:57:24:57:41 | redirectUrl : String | SpringUrlRedirect.java:58:30:58:66 | format(...) | Potentially untrusted URL redirection due to $@. | SpringUrlRedirect.java:57:24:57:41 | redirectUrl | user-provided value |
|
||||
| SpringUrlRedirect.java:63:30:63:76 | format(...) | SpringUrlRedirect.java:62:24:62:41 | redirectUrl : String | SpringUrlRedirect.java:63:30:63:76 | format(...) | Potentially untrusted URL redirection due to $@. | SpringUrlRedirect.java:62:24:62:41 | redirectUrl | user-provided value |
|
||||
| SpringUrlRedirect.java:91:27:91:49 | create(...) | SpringUrlRedirect.java:89:38:89:55 | redirectUrl : String | SpringUrlRedirect.java:91:27:91:49 | create(...) | Potentially untrusted URL redirection due to $@. | SpringUrlRedirect.java:89:38:89:55 | redirectUrl | user-provided value |
|
||||
| SpringUrlRedirect.java:100:37:100:47 | httpHeaders | SpringUrlRedirect.java:96:39:96:56 | redirectUrl : String | SpringUrlRedirect.java:100:37:100:47 | httpHeaders | Potentially untrusted URL redirection due to $@. | SpringUrlRedirect.java:96:39:96:56 | redirectUrl | user-provided value |
|
||||
| SpringUrlRedirect.java:108:68:108:78 | httpHeaders | SpringUrlRedirect.java:104:39:104:56 | redirectUrl : String | SpringUrlRedirect.java:108:68:108:78 | httpHeaders | Potentially untrusted URL redirection due to $@. | SpringUrlRedirect.java:104:39:104:56 | redirectUrl | user-provided value |
|
||||
| SpringUrlRedirect.java:116:37:116:47 | httpHeaders | SpringUrlRedirect.java:112:39:112:56 | redirectUrl : String | SpringUrlRedirect.java:116:37:116:47 | httpHeaders | Potentially untrusted URL redirection due to $@. | SpringUrlRedirect.java:112:39:112:56 | redirectUrl | user-provided value |
|
||||
| SpringUrlRedirect.java:124:49:124:59 | httpHeaders | SpringUrlRedirect.java:120:33:120:50 | redirectUrl : String | SpringUrlRedirect.java:124:49:124:59 | httpHeaders | Potentially untrusted URL redirection due to $@. | SpringUrlRedirect.java:120:33:120:50 | redirectUrl | user-provided value |
|
||||
| SpringUrlRedirect.java:132:49:132:59 | httpHeaders | SpringUrlRedirect.java:128:33:128:50 | redirectUrl : String | SpringUrlRedirect.java:132:49:132:59 | httpHeaders | Potentially untrusted URL redirection due to $@. | SpringUrlRedirect.java:128:33:128:50 | redirectUrl | user-provided value |
|
||||
|
||||
@@ -3,6 +3,10 @@ import org.springframework.stereotype.Controller;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.servlet.ModelAndView;
|
||||
import org.springframework.web.servlet.view.RedirectView;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import java.net.URI;
|
||||
|
||||
@Controller
|
||||
public class SpringUrlRedirect {
|
||||
@@ -80,4 +84,51 @@ public class SpringUrlRedirect {
|
||||
public String good3(String status) {
|
||||
return "redirect:" + String.format("/stories/search/criteria?status=%s", status);
|
||||
}
|
||||
|
||||
@GetMapping("url12")
|
||||
public ResponseEntity<Void> bad9(String redirectUrl) {
|
||||
return ResponseEntity.status(HttpStatus.FOUND)
|
||||
.location(URI.create(redirectUrl))
|
||||
.build();
|
||||
}
|
||||
|
||||
@GetMapping("url13")
|
||||
public ResponseEntity<Void> bad10(String redirectUrl) {
|
||||
HttpHeaders httpHeaders = new HttpHeaders();
|
||||
httpHeaders.setLocation(URI.create(redirectUrl));
|
||||
|
||||
return new ResponseEntity<>(httpHeaders, HttpStatus.SEE_OTHER);
|
||||
}
|
||||
|
||||
@GetMapping("url14")
|
||||
public ResponseEntity<Void> bad11(String redirectUrl) {
|
||||
HttpHeaders httpHeaders = new HttpHeaders();
|
||||
httpHeaders.add("Location", redirectUrl);
|
||||
|
||||
return ResponseEntity.status(HttpStatus.SEE_OTHER).headers(httpHeaders).build();
|
||||
}
|
||||
|
||||
@GetMapping("url15")
|
||||
public ResponseEntity<Void> bad12(String redirectUrl) {
|
||||
HttpHeaders httpHeaders = new HttpHeaders();
|
||||
httpHeaders.add("Location", redirectUrl);
|
||||
|
||||
return new ResponseEntity<>(httpHeaders, HttpStatus.SEE_OTHER);
|
||||
}
|
||||
|
||||
@GetMapping("url16")
|
||||
public ResponseEntity bad13(String redirectUrl) {
|
||||
HttpHeaders httpHeaders = new HttpHeaders();
|
||||
httpHeaders.add("Location", redirectUrl);
|
||||
|
||||
return new ResponseEntity<>("TestBody", httpHeaders, HttpStatus.SEE_OTHER);
|
||||
}
|
||||
|
||||
@GetMapping("url17")
|
||||
public ResponseEntity bad14(String redirectUrl) {
|
||||
HttpHeaders httpHeaders = new HttpHeaders();
|
||||
httpHeaders.setLocation(URI.create(redirectUrl));
|
||||
|
||||
return new ResponseEntity<>("TestBody", httpHeaders, HttpStatus.SEE_OTHER);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import default
|
||||
import semmle.code.java.security.UnsafeDeserialization
|
||||
import semmle.code.java.security.UnsafeDeserializationQuery
|
||||
|
||||
from Method m, MethodAccess ma
|
||||
where ma.getMethod() = m and unsafeDeserialization(ma, _)
|
||||
|
||||
@@ -0,0 +1,286 @@
|
||||
package generatedtest;
|
||||
|
||||
import java.util.AbstractMap;
|
||||
import java.util.ArrayDeque;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.EnumMap;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.Hashtable;
|
||||
import java.util.IdentityHashMap;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.PriorityQueue;
|
||||
import java.util.SortedMap;
|
||||
import java.util.SortedSet;
|
||||
import java.util.TreeMap;
|
||||
import java.util.TreeSet;
|
||||
import java.util.Vector;
|
||||
import java.util.WeakHashMap;
|
||||
|
||||
// Test case generated by GenerateFlowTestCase.ql
|
||||
public class Constructors {
|
||||
|
||||
Object getElement(Collection container) { return container.iterator().next(); }
|
||||
Object getMapKey(Map container) { return container.keySet().iterator().next(); }
|
||||
Object getMapValue(Map container) { return container.get(null); }
|
||||
Object getMapKey(Map.Entry container) { return container.getKey(); }
|
||||
Object getMapValue(Map.Entry container) { return container.getValue(); }
|
||||
Object source() { return null; }
|
||||
void sink(Object o) { }
|
||||
|
||||
public void test() {
|
||||
|
||||
{
|
||||
// "java.util;AbstractMap$SimpleEntry;false;SimpleEntry;(Entry);;MapKey of Argument[0];MapKey of Argument[-1];value"
|
||||
AbstractMap.SimpleEntry out = null;
|
||||
Map.Entry in = new AbstractMap.SimpleEntry(source(), null);
|
||||
out = new AbstractMap.SimpleEntry(in);
|
||||
sink(getMapKey(out)); // $ hasValueFlow
|
||||
}
|
||||
{
|
||||
// "java.util;AbstractMap$SimpleEntry;false;SimpleEntry;(Entry);;MapValue of Argument[0];MapValue of Argument[-1];value"
|
||||
AbstractMap.SimpleEntry out = null;
|
||||
Map.Entry in = new AbstractMap.SimpleEntry(null, source());;
|
||||
out = new AbstractMap.SimpleEntry(in);
|
||||
sink(getMapValue(out)); // $ hasValueFlow
|
||||
}
|
||||
{
|
||||
// "java.util;AbstractMap$SimpleEntry;false;SimpleEntry;(Object,Object);;Argument[0];MapKey of Argument[-1];value"
|
||||
AbstractMap.SimpleEntry out = null;
|
||||
Object in = source();
|
||||
out = new AbstractMap.SimpleEntry(in, null);
|
||||
sink(getMapKey(out)); // $ hasValueFlow
|
||||
}
|
||||
{
|
||||
// "java.util;AbstractMap$SimpleEntry;false;SimpleEntry;(Object,Object);;Argument[1];MapValue of Argument[-1];value"
|
||||
AbstractMap.SimpleEntry out = null;
|
||||
Object in = source();
|
||||
out = new AbstractMap.SimpleEntry(null, in);
|
||||
sink(getMapValue(out)); // $ hasValueFlow
|
||||
}
|
||||
{
|
||||
// "java.util;AbstractMap$SimpleImmutableEntry;false;SimpleImmutableEntry;(Entry);;MapKey of Argument[0];MapKey of Argument[-1];value"
|
||||
AbstractMap.SimpleImmutableEntry out = null;
|
||||
Map.Entry in = new AbstractMap.SimpleEntry(source(), null);
|
||||
out = new AbstractMap.SimpleImmutableEntry(in);
|
||||
sink(getMapKey(out)); // $ hasValueFlow
|
||||
}
|
||||
{
|
||||
// "java.util;AbstractMap$SimpleImmutableEntry;false;SimpleImmutableEntry;(Entry);;MapValue of Argument[0];MapValue of Argument[-1];value"
|
||||
AbstractMap.SimpleImmutableEntry out = null;
|
||||
Map.Entry in = new AbstractMap.SimpleEntry(null, source());
|
||||
out = new AbstractMap.SimpleImmutableEntry(in);
|
||||
sink(getMapValue(out)); // $ hasValueFlow
|
||||
}
|
||||
{
|
||||
// "java.util;ArrayDeque;false;ArrayDeque;(Collection);;Element of Argument[0];Element of Argument[-1];value"
|
||||
ArrayDeque out = null;
|
||||
Collection in = List.of(source());
|
||||
out = new ArrayDeque(in);
|
||||
sink(getElement(out)); // $ hasValueFlow
|
||||
}
|
||||
{
|
||||
// "java.util;ArrayList;false;ArrayList;(Collection);;Element of Argument[0];Element of Argument[-1];value"
|
||||
ArrayList out = null;
|
||||
Collection in = List.of(source());
|
||||
out = new ArrayList(in);
|
||||
sink(getElement(out)); // $ hasValueFlow
|
||||
}
|
||||
{
|
||||
// "java.util;EnumMap;false;EnumMap;(EnumMap);;MapKey of Argument[0];MapKey of Argument[-1];value"
|
||||
EnumMap out = null;
|
||||
EnumMap in = new EnumMap(Map.of(source(), null));
|
||||
out = new EnumMap(in);
|
||||
sink(getMapKey(out)); // $ hasValueFlow
|
||||
}
|
||||
{
|
||||
// "java.util;EnumMap;false;EnumMap;(EnumMap);;MapValue of Argument[0];MapValue of Argument[-1];value"
|
||||
EnumMap out = null;
|
||||
EnumMap in = new EnumMap(Map.of(null, source()));
|
||||
out = new EnumMap(in);
|
||||
sink(getMapValue(out)); // $ hasValueFlow
|
||||
}
|
||||
{
|
||||
// "java.util;EnumMap;false;EnumMap;(Map);;MapKey of Argument[0];MapKey of Argument[-1];value"
|
||||
EnumMap out = null;
|
||||
Map in = Map.of(source(), null);
|
||||
out = new EnumMap(in);
|
||||
sink(getMapKey(out)); // $ hasValueFlow
|
||||
}
|
||||
{
|
||||
// "java.util;EnumMap;false;EnumMap;(Map);;MapValue of Argument[0];MapValue of Argument[-1];value"
|
||||
EnumMap out = null;
|
||||
Map in = Map.of(null, source());
|
||||
out = new EnumMap(in);
|
||||
sink(getMapValue(out)); // $ hasValueFlow
|
||||
}
|
||||
{
|
||||
// "java.util;HashMap;false;HashMap;(Map);;MapKey of Argument[0];MapKey of Argument[-1];value"
|
||||
HashMap out = null;
|
||||
Map in = Map.of(source(), null);
|
||||
out = new HashMap(in);
|
||||
sink(getMapKey(out)); // $ hasValueFlow
|
||||
}
|
||||
{
|
||||
// "java.util;HashMap;false;HashMap;(Map);;MapValue of Argument[0];MapValue of Argument[-1];value"
|
||||
HashMap out = null;
|
||||
Map in = Map.of(null, source());
|
||||
out = new HashMap(in);
|
||||
sink(getMapValue(out)); // $ hasValueFlow
|
||||
}
|
||||
{
|
||||
// "java.util;HashSet;false;HashSet;(Collection);;Element of Argument[0];Element of Argument[-1];value"
|
||||
HashSet out = null;
|
||||
Collection in = List.of(source());
|
||||
out = new HashSet(in);
|
||||
sink(getElement(out)); // $ hasValueFlow
|
||||
}
|
||||
{
|
||||
// "java.util;Hashtable;false;Hashtable;(Map);;MapKey of Argument[0];MapKey of Argument[-1];value"
|
||||
Hashtable out = null;
|
||||
Map in = Map.of(source(), null);
|
||||
out = new Hashtable(in);
|
||||
sink(getMapKey(out)); // $ hasValueFlow
|
||||
}
|
||||
{
|
||||
// "java.util;Hashtable;false;Hashtable;(Map);;MapValue of Argument[0];MapValue of Argument[-1];value"
|
||||
Hashtable out = null;
|
||||
Map in = Map.of(null, source());
|
||||
out = new Hashtable(in);
|
||||
sink(getMapValue(out)); // $ hasValueFlow
|
||||
}
|
||||
{
|
||||
// "java.util;IdentityHashMap;false;IdentityHashMap;(Map);;MapKey of Argument[0];MapKey of Argument[-1];value"
|
||||
IdentityHashMap out = null;
|
||||
Map in = Map.of(source(), null);
|
||||
out = new IdentityHashMap(in);
|
||||
sink(getMapKey(out)); // $ hasValueFlow
|
||||
}
|
||||
{
|
||||
// "java.util;IdentityHashMap;false;IdentityHashMap;(Map);;MapValue of Argument[0];MapValue of Argument[-1];value"
|
||||
IdentityHashMap out = null;
|
||||
Map in = Map.of(null, source());
|
||||
out = new IdentityHashMap(in);
|
||||
sink(getMapValue(out)); // $ hasValueFlow
|
||||
}
|
||||
{
|
||||
// "java.util;LinkedHashMap;false;LinkedHashMap;(Map);;MapKey of Argument[0];MapKey of Argument[-1];value"
|
||||
LinkedHashMap out = null;
|
||||
Map in = Map.of(source(), null);
|
||||
out = new LinkedHashMap(in);
|
||||
sink(getMapKey(out)); // $ hasValueFlow
|
||||
}
|
||||
{
|
||||
// "java.util;LinkedHashMap;false;LinkedHashMap;(Map);;MapValue of Argument[0];MapValue of Argument[-1];value"
|
||||
LinkedHashMap out = null;
|
||||
Map in = Map.of(null, source());
|
||||
out = new LinkedHashMap(in);
|
||||
sink(getMapValue(out)); // $ hasValueFlow
|
||||
}
|
||||
{
|
||||
// "java.util;LinkedHashSet;false;LinkedHashSet;(Collection);;Element of Argument[0];Element of Argument[-1];value"
|
||||
LinkedHashSet out = null;
|
||||
Collection in = List.of(source());
|
||||
out = new LinkedHashSet(in);
|
||||
sink(getElement(out)); // $ hasValueFlow
|
||||
}
|
||||
{
|
||||
// "java.util;LinkedList;false;LinkedList;(Collection);;Element of Argument[0];Element of Argument[-1];value"
|
||||
LinkedList out = null;
|
||||
Collection in = List.of(source());
|
||||
out = new LinkedList(in);
|
||||
sink(getElement(out)); // $ hasValueFlow
|
||||
}
|
||||
{
|
||||
// "java.util;PriorityQueue;false;PriorityQueue;(Collection);;Element of Argument[0];Element of Argument[-1];value"
|
||||
PriorityQueue out = null;
|
||||
Collection in = List.of(source());
|
||||
out = new PriorityQueue(in);
|
||||
sink(getElement(out)); // $ hasValueFlow
|
||||
}
|
||||
{
|
||||
// "java.util;PriorityQueue;false;PriorityQueue;(PriorityQueue);;Element of Argument[0];Element of Argument[-1];value"
|
||||
PriorityQueue out = null;
|
||||
PriorityQueue in = new PriorityQueue(List.of(source()));
|
||||
out = new PriorityQueue(in);
|
||||
sink(getElement(out)); // $ hasValueFlow
|
||||
}
|
||||
{
|
||||
// "java.util;PriorityQueue;false;PriorityQueue;(SortedSet);;Element of Argument[0];Element of Argument[-1];value"
|
||||
PriorityQueue out = null;
|
||||
SortedSet in = new TreeSet(List.of(source()));
|
||||
out = new PriorityQueue(in);
|
||||
sink(getElement(out)); // $ hasValueFlow
|
||||
}
|
||||
{
|
||||
// "java.util;TreeMap;false;TreeMap;(Map);;MapKey of Argument[0];MapKey of Argument[-1];value"
|
||||
TreeMap out = null;
|
||||
Map in = Map.of(source(), null);
|
||||
out = new TreeMap(in);
|
||||
sink(getMapKey(out)); // $ hasValueFlow
|
||||
}
|
||||
{
|
||||
// "java.util;TreeMap;false;TreeMap;(Map);;MapValue of Argument[0];MapValue of Argument[-1];value"
|
||||
TreeMap out = null;
|
||||
Map in = Map.of(null, source());
|
||||
out = new TreeMap(in);
|
||||
sink(getMapValue(out)); // $ hasValueFlow
|
||||
}
|
||||
{
|
||||
// "java.util;TreeMap;false;TreeMap;(SortedMap);;MapKey of Argument[0];MapKey of Argument[-1];value"
|
||||
TreeMap out = null;
|
||||
SortedMap in = new TreeMap(Map.of(source(), null));
|
||||
out = new TreeMap(in);
|
||||
sink(getMapKey(out)); // $ hasValueFlow
|
||||
}
|
||||
{
|
||||
// "java.util;TreeMap;false;TreeMap;(SortedMap);;MapValue of Argument[0];MapValue of Argument[-1];value"
|
||||
TreeMap out = null;
|
||||
SortedMap in = new TreeMap(Map.of(null, source()));
|
||||
out = new TreeMap(in);
|
||||
sink(getMapValue(out)); // $ hasValueFlow
|
||||
}
|
||||
{
|
||||
// "java.util;TreeSet;false;TreeSet;(Collection);;Element of Argument[0];Element of Argument[-1];value"
|
||||
TreeSet out = null;
|
||||
Collection in = List.of(source());
|
||||
out = new TreeSet(in);
|
||||
sink(getElement(out)); // $ hasValueFlow
|
||||
}
|
||||
{
|
||||
// "java.util;TreeSet;false;TreeSet;(SortedSet);;Element of Argument[0];Element of Argument[-1];value"
|
||||
TreeSet out = null;
|
||||
SortedSet in = new TreeSet(List.of(source()));
|
||||
out = new TreeSet(in);
|
||||
sink(getElement(out)); // $ hasValueFlow
|
||||
}
|
||||
{
|
||||
// "java.util;Vector;false;Vector;(Collection);;Element of Argument[0];Element of Argument[-1];value"
|
||||
Vector out = null;
|
||||
Collection in = List.of(source());
|
||||
out = new Vector(in);
|
||||
sink(getElement(out)); // $ hasValueFlow
|
||||
}
|
||||
{
|
||||
// "java.util;WeakHashMap;false;WeakHashMap;(Map);;MapKey of Argument[0];MapKey of Argument[-1];value"
|
||||
WeakHashMap out = null;
|
||||
Map in = Map.of(source(), null);
|
||||
out = new WeakHashMap(in);
|
||||
sink(getMapKey(out)); // $ hasValueFlow
|
||||
}
|
||||
{
|
||||
// "java.util;WeakHashMap;false;WeakHashMap;(Map);;MapValue of Argument[0];MapValue of Argument[-1];value"
|
||||
WeakHashMap out = null;
|
||||
Map in = Map.of(null, source());
|
||||
out = new WeakHashMap(in);
|
||||
sink(getMapValue(out)); // $ hasValueFlow
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,160 @@
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
import java.util.Optional;
|
||||
|
||||
@RestController
|
||||
public class SpringXSS {
|
||||
|
||||
@GetMapping
|
||||
public static ResponseEntity<String> specificContentType(boolean safeContentType, boolean chainDirectly, String userControlled) {
|
||||
|
||||
ResponseEntity.BodyBuilder builder = ResponseEntity.ok();
|
||||
|
||||
if(safeContentType) {
|
||||
if(chainDirectly) {
|
||||
return builder.contentType(MediaType.TEXT_HTML).body(userControlled); // $xss
|
||||
}
|
||||
else {
|
||||
ResponseEntity.BodyBuilder builder2 = builder.contentType(MediaType.TEXT_HTML);
|
||||
return builder2.body(userControlled); // $xss
|
||||
}
|
||||
}
|
||||
else {
|
||||
if(chainDirectly) {
|
||||
return builder.contentType(MediaType.APPLICATION_JSON).body(userControlled); // $SPURIOUS: xss
|
||||
}
|
||||
else {
|
||||
ResponseEntity.BodyBuilder builder2 = builder.contentType(MediaType.APPLICATION_JSON);
|
||||
return builder2.body(userControlled); // $SPURIOUS: xss
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@GetMapping(value = "/xyz", produces = MediaType.APPLICATION_JSON_VALUE)
|
||||
public static ResponseEntity<String> methodContentTypeSafe(String userControlled) {
|
||||
return ResponseEntity.ok(userControlled);
|
||||
}
|
||||
|
||||
@PostMapping(value = "/xyz", produces = MediaType.APPLICATION_JSON_VALUE)
|
||||
public static ResponseEntity<String> methodContentTypeSafePost(String userControlled) {
|
||||
return ResponseEntity.ok(userControlled);
|
||||
}
|
||||
|
||||
@RequestMapping(value = "/xyz", produces = MediaType.APPLICATION_JSON_VALUE)
|
||||
public static ResponseEntity<String> methodContentTypeSafeRequest(String userControlled) {
|
||||
return ResponseEntity.ok(userControlled);
|
||||
}
|
||||
|
||||
@GetMapping(value = "/xyz", produces = "application/json")
|
||||
public static ResponseEntity<String> methodContentTypeSafeStringLiteral(String userControlled) {
|
||||
return ResponseEntity.ok(userControlled);
|
||||
}
|
||||
|
||||
@GetMapping(value = "/xyz", produces = MediaType.TEXT_HTML_VALUE)
|
||||
public static ResponseEntity<String> methodContentTypeUnsafe(String userControlled) {
|
||||
return ResponseEntity.ok(userControlled); // $MISSING: xss
|
||||
}
|
||||
|
||||
@GetMapping(value = "/xyz", produces = "text/html")
|
||||
public static ResponseEntity<String> methodContentTypeUnsafeStringLiteral(String userControlled) {
|
||||
return ResponseEntity.ok(userControlled); // $xss
|
||||
}
|
||||
|
||||
@GetMapping(value = "/xyz", produces = {MediaType.TEXT_HTML_VALUE, MediaType.APPLICATION_JSON_VALUE})
|
||||
public static ResponseEntity<String> methodContentTypeMaybeSafe(String userControlled) {
|
||||
return ResponseEntity.ok(userControlled); // $xss
|
||||
}
|
||||
|
||||
@GetMapping(value = "/xyz", produces = MediaType.APPLICATION_JSON_VALUE)
|
||||
public static ResponseEntity<String> methodContentTypeSafeOverriddenWithUnsafe(String userControlled) {
|
||||
return ResponseEntity.ok().contentType(MediaType.TEXT_HTML).body(userControlled); // $MISSING: xss
|
||||
}
|
||||
|
||||
@GetMapping(value = "/xyz", produces = MediaType.TEXT_HTML_VALUE)
|
||||
public static ResponseEntity<String> methodContentTypeUnsafeOverriddenWithSafe(String userControlled) {
|
||||
return ResponseEntity.ok().contentType(MediaType.APPLICATION_JSON).body(userControlled);
|
||||
}
|
||||
|
||||
@GetMapping(value = "/xyz", produces = {"text/html", "application/json"})
|
||||
public static ResponseEntity<String> methodContentTypeMaybeSafeStringLiterals(String userControlled, int constructionMethod) {
|
||||
// Also try out some alternative constructors for the ResponseEntity:
|
||||
switch(constructionMethod) {
|
||||
case 0:
|
||||
return ResponseEntity.ok(userControlled); // $xss
|
||||
case 1:
|
||||
return ResponseEntity.of(Optional.of(userControlled)); // $xss
|
||||
case 2:
|
||||
return ResponseEntity.ok().body(userControlled); // $xss
|
||||
case 3:
|
||||
return new ResponseEntity<String>(userControlled, HttpStatus.OK); // $xss
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@RestController
|
||||
@RequestMapping(produces = {"application/json"})
|
||||
private static class ClassContentTypeSafe {
|
||||
@GetMapping(value = "/abc")
|
||||
public ResponseEntity<String> test(String userControlled) {
|
||||
return ResponseEntity.ok(userControlled); // $SPURIOUS: xss
|
||||
}
|
||||
|
||||
@GetMapping(value = "/abc")
|
||||
public String testDirectReturn(String userControlled) {
|
||||
return userControlled; // $SPURIOUS: xss
|
||||
}
|
||||
|
||||
@GetMapping(value = "/xyz", produces = {"text/html"})
|
||||
public ResponseEntity<String> overridesWithUnsafe(String userControlled) {
|
||||
return ResponseEntity.ok(userControlled); // $xss
|
||||
}
|
||||
|
||||
@GetMapping(value = "/abc")
|
||||
public ResponseEntity<String> overridesWithUnsafe2(String userControlled) {
|
||||
return ResponseEntity.ok().contentType(MediaType.TEXT_HTML).body(userControlled); // $xss
|
||||
}
|
||||
}
|
||||
|
||||
@RestController
|
||||
@RequestMapping(produces = {"text/html"})
|
||||
private static class ClassContentTypeUnsafe {
|
||||
@GetMapping(value = "/abc")
|
||||
public ResponseEntity<String> test(String userControlled) {
|
||||
return ResponseEntity.ok(userControlled); // $xss
|
||||
}
|
||||
|
||||
@GetMapping(value = "/abc")
|
||||
public String testDirectReturn(String userControlled) {
|
||||
return userControlled; // $xss
|
||||
}
|
||||
|
||||
@GetMapping(value = "/xyz", produces = {"application/json"})
|
||||
public ResponseEntity<String> overridesWithSafe(String userControlled) {
|
||||
return ResponseEntity.ok(userControlled); // $SPURIOUS: xss
|
||||
}
|
||||
|
||||
@GetMapping(value = "/abc")
|
||||
public ResponseEntity<String> overridesWithSafe2(String userControlled) {
|
||||
return ResponseEntity.ok().contentType(MediaType.APPLICATION_JSON).body(userControlled); // $SPURIOUS: xss
|
||||
}
|
||||
}
|
||||
|
||||
@GetMapping(value = "/abc")
|
||||
public static ResponseEntity<String> entityWithNoMediaType(String userControlled) {
|
||||
return ResponseEntity.ok(userControlled); // $xss
|
||||
}
|
||||
|
||||
@GetMapping(value = "/abc")
|
||||
public static String stringWithNoMediaType(String userControlled) {
|
||||
return userControlled; // $xss
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1 +1 @@
|
||||
//semmle-extractor-options: --javac-args -cp ${testdir}/../../../../../stubs/servlet-api-2.4:${testdir}/../../../../../stubs/javax-ws-rs-api-2.1.1/
|
||||
//semmle-extractor-options: --javac-args -cp ${testdir}/../../../../../stubs/servlet-api-2.4:${testdir}/../../../../../stubs/javax-ws-rs-api-2.1.1/:${testdir}/../../../../../stubs/springframework-5.3.8
|
||||
|
||||
@@ -12,34 +12,34 @@ public class A {
|
||||
public Object deserialize1(Socket sock) throws java.io.IOException, ClassNotFoundException {
|
||||
InputStream inputStream = sock.getInputStream();
|
||||
ObjectInputStream in = new ObjectInputStream(inputStream);
|
||||
return in.readObject(); // unsafe
|
||||
return in.readObject(); // $unsafeDeserialization
|
||||
}
|
||||
|
||||
public Object deserialize2(Socket sock) throws java.io.IOException, ClassNotFoundException {
|
||||
InputStream inputStream = sock.getInputStream();
|
||||
ObjectInputStream in = new ObjectInputStream(inputStream);
|
||||
return in.readUnshared(); // unsafe
|
||||
return in.readUnshared(); // $unsafeDeserialization
|
||||
}
|
||||
|
||||
public Object deserialize3(Socket sock) throws java.io.IOException {
|
||||
InputStream inputStream = sock.getInputStream();
|
||||
XMLDecoder d = new XMLDecoder(inputStream);
|
||||
return d.readObject(); // unsafe
|
||||
return d.readObject(); // $unsafeDeserialization
|
||||
}
|
||||
|
||||
public Object deserialize4(Socket sock) throws java.io.IOException {
|
||||
XStream xs = new XStream();
|
||||
InputStream inputStream = sock.getInputStream();
|
||||
Reader reader = new InputStreamReader(inputStream);
|
||||
return xs.fromXML(reader); // unsafe
|
||||
return xs.fromXML(reader); // $unsafeDeserialization
|
||||
}
|
||||
|
||||
public void deserialize5(Socket sock) throws java.io.IOException {
|
||||
Kryo kryo = new Kryo();
|
||||
Input input = new Input(sock.getInputStream());
|
||||
A a1 = kryo.readObject(input, A.class); // unsafe
|
||||
A a2 = kryo.readObjectOrNull(input, A.class); // unsafe
|
||||
Object o = kryo.readClassAndObject(input); // unsafe
|
||||
A a1 = kryo.readObject(input, A.class); // $unsafeDeserialization
|
||||
A a2 = kryo.readObjectOrNull(input, A.class); // $unsafeDeserialization
|
||||
Object o = kryo.readClassAndObject(input); // $unsafeDeserialization
|
||||
}
|
||||
|
||||
private Kryo getSafeKryo() throws java.io.IOException {
|
||||
@@ -58,21 +58,21 @@ public class A {
|
||||
public void deserializeSnakeYaml(Socket sock) throws java.io.IOException {
|
||||
Yaml yaml = new Yaml();
|
||||
InputStream input = sock.getInputStream();
|
||||
Object o = yaml.load(input); //unsafe
|
||||
Object o2 = yaml.loadAll(input); //unsafe
|
||||
Object o3 = yaml.parse(new InputStreamReader(input)); //unsafe
|
||||
A o4 = yaml.loadAs(input, A.class); //unsafe
|
||||
A o5 = yaml.loadAs(new InputStreamReader(input), A.class); //unsafe
|
||||
Object o = yaml.load(input); // $unsafeDeserialization
|
||||
Object o2 = yaml.loadAll(input); // $unsafeDeserialization
|
||||
Object o3 = yaml.parse(new InputStreamReader(input)); // $unsafeDeserialization
|
||||
A o4 = yaml.loadAs(input, A.class); // $unsafeDeserialization
|
||||
A o5 = yaml.loadAs(new InputStreamReader(input), A.class); // $unsafeDeserialization
|
||||
}
|
||||
|
||||
public void deserializeSnakeYaml2(Socket sock) throws java.io.IOException {
|
||||
Yaml yaml = new Yaml(new Constructor());
|
||||
InputStream input = sock.getInputStream();
|
||||
Object o = yaml.load(input); //unsafe
|
||||
Object o2 = yaml.loadAll(input); //unsafe
|
||||
Object o3 = yaml.parse(new InputStreamReader(input)); //unsafe
|
||||
A o4 = yaml.loadAs(input, A.class); //unsafe
|
||||
A o5 = yaml.loadAs(new InputStreamReader(input), A.class); //unsafe
|
||||
Object o = yaml.load(input); // $unsafeDeserialization
|
||||
Object o2 = yaml.loadAll(input); // $unsafeDeserialization
|
||||
Object o3 = yaml.parse(new InputStreamReader(input)); // $unsafeDeserialization
|
||||
A o4 = yaml.loadAs(input, A.class); // $unsafeDeserialization
|
||||
A o5 = yaml.loadAs(new InputStreamReader(input), A.class); // $unsafeDeserialization
|
||||
}
|
||||
|
||||
public void deserializeSnakeYaml3(Socket sock) throws java.io.IOException {
|
||||
@@ -88,10 +88,10 @@ public class A {
|
||||
public void deserializeSnakeYaml4(Socket sock) throws java.io.IOException {
|
||||
Yaml yaml = new Yaml(new Constructor(A.class));
|
||||
InputStream input = sock.getInputStream();
|
||||
Object o = yaml.load(input); //unsafe
|
||||
Object o2 = yaml.loadAll(input); //unsafe
|
||||
Object o3 = yaml.parse(new InputStreamReader(input)); //unsafe
|
||||
A o4 = yaml.loadAs(input, A.class); //unsafe
|
||||
A o5 = yaml.loadAs(new InputStreamReader(input), A.class); //unsafe
|
||||
Object o = yaml.load(input); // $unsafeDeserialization
|
||||
Object o2 = yaml.loadAll(input); // $unsafeDeserialization
|
||||
Object o3 = yaml.parse(new InputStreamReader(input)); // $unsafeDeserialization
|
||||
A o4 = yaml.loadAs(input, A.class); // $unsafeDeserialization
|
||||
A o5 = yaml.loadAs(new InputStreamReader(input), A.class); // $unsafeDeserialization
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,14 +5,14 @@ import com.alibaba.fastjson.JSON;
|
||||
public class B {
|
||||
public Object deserializeJson1(Socket sock) throws java.io.IOException {
|
||||
InputStream inputStream = sock.getInputStream();
|
||||
return JSON.parseObject(inputStream, null); // unsafe
|
||||
return JSON.parseObject(inputStream, null); // $unsafeDeserialization
|
||||
}
|
||||
|
||||
public Object deserializeJson2(Socket sock) throws java.io.IOException {
|
||||
InputStream inputStream = sock.getInputStream();
|
||||
byte[] bytes = new byte[100];
|
||||
inputStream.read(bytes);
|
||||
return JSON.parse(bytes); // unsafe
|
||||
return JSON.parse(bytes); // $unsafeDeserialization
|
||||
}
|
||||
|
||||
public Object deserializeJson3(Socket sock) throws java.io.IOException {
|
||||
@@ -20,7 +20,7 @@ public class B {
|
||||
byte[] bytes = new byte[100];
|
||||
inputStream.read(bytes);
|
||||
String s = new String(bytes);
|
||||
return JSON.parseObject(s); // unsafe
|
||||
return JSON.parseObject(s); // $unsafeDeserialization
|
||||
}
|
||||
|
||||
public Object deserializeJson4(Socket sock) throws java.io.IOException {
|
||||
@@ -28,6 +28,6 @@ public class B {
|
||||
byte[] bytes = new byte[100];
|
||||
inputStream.read(bytes);
|
||||
String s = new String(bytes);
|
||||
return JSON.parse(s); // unsafe
|
||||
return JSON.parse(s); // $unsafeDeserialization
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,16 +21,16 @@ public class C {
|
||||
@GetMapping(value = "jyaml")
|
||||
public void bad1(HttpServletRequest request) throws Exception {
|
||||
String data = request.getParameter("data");
|
||||
Yaml.load(data); //bad
|
||||
Yaml.loadStream(data); //bad
|
||||
Yaml.loadStreamOfType(data, Object.class); //bad
|
||||
Yaml.loadType(data, Object.class); //bad
|
||||
Yaml.load(data); // $unsafeDeserialization
|
||||
Yaml.loadStream(data); // $unsafeDeserialization
|
||||
Yaml.loadStreamOfType(data, Object.class); // $unsafeDeserialization
|
||||
Yaml.loadType(data, Object.class); // $unsafeDeserialization
|
||||
|
||||
org.ho.yaml.YamlConfig yamlConfig = new YamlConfig();
|
||||
yamlConfig.load(data); //bad
|
||||
yamlConfig.loadStream(data); //bad
|
||||
yamlConfig.loadStreamOfType(data, Object.class); //bad
|
||||
yamlConfig.loadType(data, Object.class); //bad
|
||||
yamlConfig.load(data); // $unsafeDeserialization
|
||||
yamlConfig.loadStream(data); // $unsafeDeserialization
|
||||
yamlConfig.loadStreamOfType(data, Object.class); // $unsafeDeserialization
|
||||
yamlConfig.loadType(data, Object.class); // $unsafeDeserialization
|
||||
}
|
||||
|
||||
@GetMapping(value = "jsonio")
|
||||
@@ -40,19 +40,19 @@ public class C {
|
||||
HashMap hashMap = new HashMap();
|
||||
hashMap.put("USE_MAPS", true);
|
||||
|
||||
JsonReader.jsonToJava(data); //bad
|
||||
JsonReader.jsonToJava(data); // $unsafeDeserialization
|
||||
|
||||
JsonReader jr = new JsonReader(data, null); //bad
|
||||
jr.readObject();
|
||||
JsonReader jr = new JsonReader(data, null);
|
||||
jr.readObject(); // $unsafeDeserialization
|
||||
}
|
||||
|
||||
@GetMapping(value = "yamlbeans")
|
||||
public void bad3(HttpServletRequest request) throws Exception {
|
||||
String data = request.getParameter("data");
|
||||
YamlReader r = new YamlReader(data);
|
||||
r.read(); //bad
|
||||
r.read(Object.class); //bad
|
||||
r.read(Object.class, Object.class); //bad
|
||||
r.read(); // $unsafeDeserialization
|
||||
r.read(Object.class); // $unsafeDeserialization
|
||||
r.read(Object.class, Object.class); // $unsafeDeserialization
|
||||
}
|
||||
|
||||
@GetMapping(value = "hessian")
|
||||
@@ -60,8 +60,8 @@ public class C {
|
||||
byte[] bytes = request.getParameter("data").getBytes();
|
||||
ByteArrayInputStream bis = new ByteArrayInputStream(bytes);
|
||||
HessianInput hessianInput = new HessianInput(bis);
|
||||
hessianInput.readObject(); //bad
|
||||
hessianInput.readObject(Object.class); //bad
|
||||
hessianInput.readObject(); // $unsafeDeserialization
|
||||
hessianInput.readObject(Object.class); // $unsafeDeserialization
|
||||
}
|
||||
|
||||
@GetMapping(value = "hessian2")
|
||||
@@ -69,14 +69,14 @@ public class C {
|
||||
byte[] bytes = request.getParameter("data").getBytes();
|
||||
ByteArrayInputStream bis = new ByteArrayInputStream(bytes);
|
||||
Hessian2Input hessianInput = new Hessian2Input(bis);
|
||||
hessianInput.readObject(); //bad
|
||||
hessianInput.readObject(Object.class); //bad
|
||||
hessianInput.readObject(); // $unsafeDeserialization
|
||||
hessianInput.readObject(Object.class); // $unsafeDeserialization
|
||||
}
|
||||
|
||||
@GetMapping(value = "castor")
|
||||
public void bad6(HttpServletRequest request) throws Exception {
|
||||
Unmarshaller unmarshaller = new Unmarshaller();
|
||||
unmarshaller.unmarshal(new StringReader(request.getParameter("data"))); //bad
|
||||
unmarshaller.unmarshal(new StringReader(request.getParameter("data"))); // $unsafeDeserialization
|
||||
}
|
||||
|
||||
@GetMapping(value = "burlap")
|
||||
@@ -84,11 +84,11 @@ public class C {
|
||||
byte[] serializedData = request.getParameter("data").getBytes();
|
||||
ByteArrayInputStream is = new ByteArrayInputStream(serializedData);
|
||||
BurlapInput burlapInput = new BurlapInput(is);
|
||||
burlapInput.readObject(); //bad
|
||||
burlapInput.readObject(); // $unsafeDeserialization
|
||||
|
||||
BurlapInput burlapInput1 = new BurlapInput();
|
||||
burlapInput1.init(is);
|
||||
burlapInput1.readObject(); //bad
|
||||
burlapInput1.readObject(); // $unsafeDeserialization
|
||||
}
|
||||
|
||||
@GetMapping(value = "jsonio1")
|
||||
|
||||
209
java/ql/test/query-tests/security/CWE-502/JacksonTest.java
Normal file
209
java/ql/test/query-tests/security/CWE-502/JacksonTest.java
Normal file
@@ -0,0 +1,209 @@
|
||||
import com.fasterxml.jackson.annotation.JsonTypeInfo;
|
||||
import com.fasterxml.jackson.core.JsonFactory;
|
||||
import com.fasterxml.jackson.databind.JavaType;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import com.fasterxml.jackson.databind.json.JsonMapper;
|
||||
import com.fasterxml.jackson.databind.jsontype.BasicPolymorphicTypeValidator;
|
||||
import com.fasterxml.jackson.databind.jsontype.PolymorphicTypeValidator;
|
||||
import java.io.IOException;
|
||||
import java.io.Serializable;
|
||||
import java.net.ServerSocket;
|
||||
import java.net.Socket;
|
||||
import java.util.List;
|
||||
|
||||
public class JacksonTest {
|
||||
|
||||
public static void withSocket(Action<String> action) throws Exception {
|
||||
try (ServerSocket serverSocket = new ServerSocket(0)) {
|
||||
try (Socket socket = serverSocket.accept()) {
|
||||
byte[] bytes = new byte[1024];
|
||||
int n = socket.getInputStream().read(bytes);
|
||||
String jexlExpr = new String(bytes, 0, n);
|
||||
action.run(jexlExpr);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
interface Action<T> {
|
||||
void run(T object) throws Exception;
|
||||
}
|
||||
|
||||
abstract class PhoneNumber implements Serializable {
|
||||
public int areaCode;
|
||||
public int local;
|
||||
}
|
||||
|
||||
class DomesticNumber extends PhoneNumber {
|
||||
}
|
||||
|
||||
class InternationalNumber extends PhoneNumber {
|
||||
public int countryCode;
|
||||
}
|
||||
|
||||
class Employee extends Person {
|
||||
}
|
||||
|
||||
class Person {
|
||||
public String name;
|
||||
public int age;
|
||||
|
||||
// this annotation enables polymorphic type handling
|
||||
@JsonTypeInfo(use = JsonTypeInfo.Id.CLASS)
|
||||
public Object phone;
|
||||
}
|
||||
|
||||
class Task {
|
||||
public Person assignee;
|
||||
}
|
||||
|
||||
class Tag implements Serializable {
|
||||
public String title;
|
||||
}
|
||||
|
||||
class Cat {
|
||||
public String name;
|
||||
public Serializable tag;
|
||||
}
|
||||
|
||||
class UnsafePersonDeserialization {
|
||||
|
||||
// BAD: Person has a field with an annotation that enables polymorphic type
|
||||
// handling
|
||||
private static void testUnsafeDeserialization() throws Exception {
|
||||
JacksonTest.withSocket(string -> {
|
||||
ObjectMapper mapper = new ObjectMapper();
|
||||
mapper.readValue(string, Person.class); // $unsafeDeserialization
|
||||
});
|
||||
}
|
||||
|
||||
// BAD: Employee extends Person that has a field with an annotation that enables
|
||||
// polymorphic type handling
|
||||
private static void testUnsafeDeserializationWithExtendedClass() throws Exception {
|
||||
JacksonTest.withSocket(string -> {
|
||||
ObjectMapper mapper = new ObjectMapper();
|
||||
mapper.readValue(string, Employee.class); // $unsafeDeserialization
|
||||
});
|
||||
}
|
||||
|
||||
// BAD: Task has a Person field that has a field with an annotation that enables
|
||||
// polymorphic type handling
|
||||
private static void testUnsafeDeserializationWithWrapper() throws Exception {
|
||||
JacksonTest.withSocket(string -> {
|
||||
ObjectMapper mapper = new ObjectMapper();
|
||||
mapper.readValue(string, Task.class); // $unsafeDeserialization
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
class SaferPersonDeserialization {
|
||||
|
||||
// GOOD: Despite enabled polymorphic type handling, this is safe because ObjectMapper
|
||||
// has a validator
|
||||
private static void testSafeDeserializationWithValidator() throws Exception {
|
||||
JacksonTest.withSocket(string -> {
|
||||
PolymorphicTypeValidator ptv =
|
||||
BasicPolymorphicTypeValidator.builder()
|
||||
.allowIfSubType("only.allowed.package")
|
||||
.build();
|
||||
|
||||
ObjectMapper mapper = new ObjectMapper();
|
||||
mapper.setPolymorphicTypeValidator(ptv);
|
||||
|
||||
mapper.readValue(string, Person.class);
|
||||
});
|
||||
}
|
||||
|
||||
// GOOD: Despite enabled polymorphic type handling, this is safe because ObjectMapper
|
||||
// has a validator
|
||||
private static void testSafeDeserializationWithValidatorAndBuilder() throws Exception {
|
||||
JacksonTest.withSocket(string -> {
|
||||
PolymorphicTypeValidator ptv =
|
||||
BasicPolymorphicTypeValidator.builder()
|
||||
.allowIfSubType("only.allowed.package")
|
||||
.build();
|
||||
|
||||
ObjectMapper mapper = JsonMapper.builder()
|
||||
.polymorphicTypeValidator(ptv)
|
||||
.build();
|
||||
|
||||
mapper.readValue(string, Person.class);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
class UnsafeCatDeserialization {
|
||||
|
||||
// BAD: deserializing untrusted input while polymorphic type handling is on
|
||||
private static void testUnsafeDeserialization() throws Exception {
|
||||
JacksonTest.withSocket(string -> {
|
||||
ObjectMapper mapper = new ObjectMapper();
|
||||
mapper.enableDefaultTyping(); // this enables polymorphic type handling
|
||||
mapper.readValue(string, Cat.class); // $unsafeDeserialization
|
||||
});
|
||||
}
|
||||
|
||||
// BAD: deserializing untrusted input while polymorphic type handling is on
|
||||
private static void testUnsafeDeserializationWithObjectMapperReadValues() throws Exception {
|
||||
JacksonTest.withSocket(string -> {
|
||||
ObjectMapper mapper = new ObjectMapper();
|
||||
mapper.enableDefaultTyping();
|
||||
mapper.readValues(new JsonFactory().createParser(string), Cat.class).readAll(); // $unsafeDeserialization
|
||||
});
|
||||
}
|
||||
|
||||
// BAD: deserializing untrusted input while polymorphic type handling is on
|
||||
private static void testUnsafeDeserializationWithObjectMapperTreeToValue() throws Exception {
|
||||
JacksonTest.withSocket(string -> {
|
||||
ObjectMapper mapper = new ObjectMapper();
|
||||
mapper.enableDefaultTyping();
|
||||
mapper.treeToValue(mapper.readTree(string), Cat.class); // $unsafeDeserialization
|
||||
});
|
||||
}
|
||||
|
||||
// BAD: an attacker can control both data and type of deserialized object
|
||||
private static void testUnsafeDeserializationWithUnsafeClass() throws Exception {
|
||||
JacksonTest.withSocket(input -> {
|
||||
String[] parts = input.split(";");
|
||||
String data = parts[0];
|
||||
String type = parts[1];
|
||||
Class clazz = Class.forName(type);
|
||||
ObjectMapper mapper = new ObjectMapper();
|
||||
mapper.readValue(data, clazz); // $unsafeDeserialization
|
||||
});
|
||||
}
|
||||
|
||||
// BAD: an attacker can control both data and type of deserialized object
|
||||
private static void testUnsafeDeserializationWithUnsafeClassAndCustomTypeResolver() throws Exception {
|
||||
JacksonTest.withSocket(input -> {
|
||||
String[] parts = input.split(";");
|
||||
String data = parts[0];
|
||||
String type = parts[1];
|
||||
ObjectMapper mapper = new ObjectMapper();
|
||||
mapper.readValue(data, resolveImpl(type, mapper)); // $unsafeDeserialization
|
||||
});
|
||||
}
|
||||
|
||||
private static JavaType resolveImpl(String type, ObjectMapper mapper) throws Exception {
|
||||
return mapper.constructType(Class.forName(type));
|
||||
}
|
||||
}
|
||||
|
||||
class SaferCatDeserialization {
|
||||
|
||||
// GOOD: Despite enabled polymorphic type handling, this is safe because ObjectMapper
|
||||
// has a validator
|
||||
private static void testUnsafeDeserialization() throws Exception {
|
||||
JacksonTest.withSocket(string -> {
|
||||
PolymorphicTypeValidator ptv =
|
||||
BasicPolymorphicTypeValidator.builder()
|
||||
.allowIfSubType("only.allowed.pachage")
|
||||
.build();
|
||||
|
||||
ObjectMapper mapper = JsonMapper.builder().polymorphicTypeValidator(ptv).build();
|
||||
mapper.enableDefaultTyping(); // this enables polymorphic type handling
|
||||
|
||||
mapper.readValue(string, Cat.class);
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -19,7 +19,7 @@ public class TestMessageBodyReader implements MessageBodyReader<Object> {
|
||||
public Object readFrom(Class<Object> type, Type genericType, Annotation[] annotations, MediaType mediaType,
|
||||
MultivaluedMap<String, String> httpHeaders, InputStream entityStream) throws IOException {
|
||||
try {
|
||||
return new ObjectInputStream(entityStream).readObject();
|
||||
return new ObjectInputStream(entityStream).readObject(); // $unsafeDeserialization
|
||||
} catch (ClassNotFoundException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
@@ -1,229 +0,0 @@
|
||||
edges
|
||||
| A.java:13:31:13:51 | getInputStream(...) : InputStream | A.java:14:50:14:60 | inputStream : InputStream |
|
||||
| A.java:13:31:13:51 | getInputStream(...) : InputStream | A.java:15:12:15:13 | in |
|
||||
| A.java:14:28:14:61 | new ObjectInputStream(...) : ObjectInputStream | A.java:15:12:15:13 | in |
|
||||
| A.java:14:50:14:60 | inputStream : InputStream | A.java:14:28:14:61 | new ObjectInputStream(...) : ObjectInputStream |
|
||||
| A.java:19:31:19:51 | getInputStream(...) : InputStream | A.java:20:50:20:60 | inputStream : InputStream |
|
||||
| A.java:19:31:19:51 | getInputStream(...) : InputStream | A.java:21:12:21:13 | in |
|
||||
| A.java:20:28:20:61 | new ObjectInputStream(...) : ObjectInputStream | A.java:21:12:21:13 | in |
|
||||
| A.java:20:50:20:60 | inputStream : InputStream | A.java:20:28:20:61 | new ObjectInputStream(...) : ObjectInputStream |
|
||||
| A.java:25:31:25:51 | getInputStream(...) : InputStream | A.java:26:35:26:45 | inputStream : InputStream |
|
||||
| A.java:26:20:26:46 | new XMLDecoder(...) : XMLDecoder | A.java:27:12:27:12 | d |
|
||||
| A.java:26:35:26:45 | inputStream : InputStream | A.java:26:20:26:46 | new XMLDecoder(...) : XMLDecoder |
|
||||
| A.java:32:31:32:51 | getInputStream(...) : InputStream | A.java:33:43:33:53 | inputStream : InputStream |
|
||||
| A.java:33:21:33:54 | new InputStreamReader(...) : InputStreamReader | A.java:34:23:34:28 | reader |
|
||||
| A.java:33:43:33:53 | inputStream : InputStream | A.java:33:21:33:54 | new InputStreamReader(...) : InputStreamReader |
|
||||
| A.java:39:19:39:50 | new Input(...) : Input | A.java:40:28:40:32 | input |
|
||||
| A.java:39:19:39:50 | new Input(...) : Input | A.java:41:34:41:38 | input |
|
||||
| A.java:39:19:39:50 | new Input(...) : Input | A.java:42:40:42:44 | input |
|
||||
| A.java:39:29:39:49 | getInputStream(...) : InputStream | A.java:39:19:39:50 | new Input(...) : Input |
|
||||
| A.java:60:25:60:45 | getInputStream(...) : InputStream | A.java:61:26:61:30 | input |
|
||||
| A.java:60:25:60:45 | getInputStream(...) : InputStream | A.java:62:30:62:34 | input |
|
||||
| A.java:60:25:60:45 | getInputStream(...) : InputStream | A.java:63:50:63:54 | input : InputStream |
|
||||
| A.java:60:25:60:45 | getInputStream(...) : InputStream | A.java:64:24:64:28 | input |
|
||||
| A.java:60:25:60:45 | getInputStream(...) : InputStream | A.java:65:46:65:50 | input : InputStream |
|
||||
| A.java:63:50:63:54 | input : InputStream | A.java:63:28:63:55 | new InputStreamReader(...) |
|
||||
| A.java:65:46:65:50 | input : InputStream | A.java:65:24:65:51 | new InputStreamReader(...) |
|
||||
| A.java:70:25:70:45 | getInputStream(...) : InputStream | A.java:71:26:71:30 | input |
|
||||
| A.java:70:25:70:45 | getInputStream(...) : InputStream | A.java:72:30:72:34 | input |
|
||||
| A.java:70:25:70:45 | getInputStream(...) : InputStream | A.java:73:50:73:54 | input : InputStream |
|
||||
| A.java:70:25:70:45 | getInputStream(...) : InputStream | A.java:74:24:74:28 | input |
|
||||
| A.java:70:25:70:45 | getInputStream(...) : InputStream | A.java:75:46:75:50 | input : InputStream |
|
||||
| A.java:73:50:73:54 | input : InputStream | A.java:73:28:73:55 | new InputStreamReader(...) |
|
||||
| A.java:75:46:75:50 | input : InputStream | A.java:75:24:75:51 | new InputStreamReader(...) |
|
||||
| A.java:90:25:90:45 | getInputStream(...) : InputStream | A.java:91:26:91:30 | input |
|
||||
| A.java:90:25:90:45 | getInputStream(...) : InputStream | A.java:92:30:92:34 | input |
|
||||
| A.java:90:25:90:45 | getInputStream(...) : InputStream | A.java:93:50:93:54 | input : InputStream |
|
||||
| A.java:90:25:90:45 | getInputStream(...) : InputStream | A.java:94:24:94:28 | input |
|
||||
| A.java:90:25:90:45 | getInputStream(...) : InputStream | A.java:95:46:95:50 | input : InputStream |
|
||||
| A.java:93:50:93:54 | input : InputStream | A.java:93:28:93:55 | new InputStreamReader(...) |
|
||||
| A.java:95:46:95:50 | input : InputStream | A.java:95:24:95:51 | new InputStreamReader(...) |
|
||||
| B.java:7:31:7:51 | getInputStream(...) : InputStream | B.java:8:29:8:39 | inputStream |
|
||||
| B.java:12:31:12:51 | getInputStream(...) : InputStream | B.java:14:5:14:15 | inputStream : InputStream |
|
||||
| B.java:14:5:14:15 | inputStream : InputStream | B.java:14:22:14:26 | bytes [post update] : byte[] |
|
||||
| B.java:14:22:14:26 | bytes [post update] : byte[] | B.java:15:23:15:27 | bytes |
|
||||
| B.java:19:31:19:51 | getInputStream(...) : InputStream | B.java:21:5:21:15 | inputStream : InputStream |
|
||||
| B.java:21:5:21:15 | inputStream : InputStream | B.java:21:22:21:26 | bytes [post update] : byte[] |
|
||||
| B.java:21:22:21:26 | bytes [post update] : byte[] | B.java:23:29:23:29 | s |
|
||||
| B.java:27:31:27:51 | getInputStream(...) : InputStream | B.java:29:5:29:15 | inputStream : InputStream |
|
||||
| B.java:29:5:29:15 | inputStream : InputStream | B.java:29:22:29:26 | bytes [post update] : byte[] |
|
||||
| B.java:29:22:29:26 | bytes [post update] : byte[] | B.java:31:23:31:23 | s |
|
||||
| C.java:23:17:23:44 | getParameter(...) : String | C.java:24:13:24:16 | data |
|
||||
| C.java:23:17:23:44 | getParameter(...) : String | C.java:25:19:25:22 | data |
|
||||
| C.java:23:17:23:44 | getParameter(...) : String | C.java:26:25:26:28 | data |
|
||||
| C.java:23:17:23:44 | getParameter(...) : String | C.java:27:17:27:20 | data |
|
||||
| C.java:23:17:23:44 | getParameter(...) : String | C.java:30:19:30:22 | data |
|
||||
| C.java:23:17:23:44 | getParameter(...) : String | C.java:31:25:31:28 | data |
|
||||
| C.java:23:17:23:44 | getParameter(...) : String | C.java:32:31:32:34 | data |
|
||||
| C.java:23:17:23:44 | getParameter(...) : String | C.java:33:23:33:26 | data |
|
||||
| C.java:38:17:38:44 | getParameter(...) : String | C.java:43:25:43:28 | data |
|
||||
| C.java:38:17:38:44 | getParameter(...) : String | C.java:46:3:46:4 | jr |
|
||||
| C.java:51:17:51:44 | getParameter(...) : String | C.java:53:3:53:3 | r |
|
||||
| C.java:51:17:51:44 | getParameter(...) : String | C.java:54:3:54:3 | r |
|
||||
| C.java:51:17:51:44 | getParameter(...) : String | C.java:55:3:55:3 | r |
|
||||
| C.java:60:18:60:45 | getParameter(...) : String | C.java:61:55:61:59 | bytes : byte[] |
|
||||
| C.java:60:18:60:45 | getParameter(...) : String | C.java:63:3:63:14 | hessianInput |
|
||||
| C.java:60:18:60:45 | getParameter(...) : String | C.java:64:3:64:14 | hessianInput |
|
||||
| C.java:61:30:61:60 | new ByteArrayInputStream(...) : ByteArrayInputStream | C.java:63:3:63:14 | hessianInput |
|
||||
| C.java:61:30:61:60 | new ByteArrayInputStream(...) : ByteArrayInputStream | C.java:64:3:64:14 | hessianInput |
|
||||
| C.java:61:55:61:59 | bytes : byte[] | C.java:61:30:61:60 | new ByteArrayInputStream(...) : ByteArrayInputStream |
|
||||
| C.java:69:18:69:45 | getParameter(...) : String | C.java:70:55:70:59 | bytes : byte[] |
|
||||
| C.java:69:18:69:45 | getParameter(...) : String | C.java:72:3:72:14 | hessianInput |
|
||||
| C.java:69:18:69:45 | getParameter(...) : String | C.java:73:3:73:14 | hessianInput |
|
||||
| C.java:70:30:70:60 | new ByteArrayInputStream(...) : ByteArrayInputStream | C.java:72:3:72:14 | hessianInput |
|
||||
| C.java:70:30:70:60 | new ByteArrayInputStream(...) : ByteArrayInputStream | C.java:73:3:73:14 | hessianInput |
|
||||
| C.java:70:55:70:59 | bytes : byte[] | C.java:70:30:70:60 | new ByteArrayInputStream(...) : ByteArrayInputStream |
|
||||
| C.java:79:43:79:70 | getParameter(...) : String | C.java:79:26:79:71 | new StringReader(...) |
|
||||
| C.java:84:27:84:54 | getParameter(...) : String | C.java:85:54:85:67 | serializedData : byte[] |
|
||||
| C.java:84:27:84:54 | getParameter(...) : String | C.java:87:3:87:13 | burlapInput |
|
||||
| C.java:84:27:84:54 | getParameter(...) : String | C.java:91:3:91:14 | burlapInput1 |
|
||||
| C.java:85:29:85:68 | new ByteArrayInputStream(...) : ByteArrayInputStream | C.java:87:3:87:13 | burlapInput |
|
||||
| C.java:85:29:85:68 | new ByteArrayInputStream(...) : ByteArrayInputStream | C.java:91:3:91:14 | burlapInput1 |
|
||||
| C.java:85:54:85:67 | serializedData : byte[] | C.java:85:29:85:68 | new ByteArrayInputStream(...) : ByteArrayInputStream |
|
||||
| TestMessageBodyReader.java:20:55:20:78 | entityStream : InputStream | TestMessageBodyReader.java:22:18:22:52 | new ObjectInputStream(...) |
|
||||
| TestMessageBodyReader.java:20:55:20:78 | entityStream : InputStream | TestMessageBodyReader.java:22:40:22:51 | entityStream : InputStream |
|
||||
| TestMessageBodyReader.java:22:40:22:51 | entityStream : InputStream | TestMessageBodyReader.java:22:18:22:52 | new ObjectInputStream(...) |
|
||||
nodes
|
||||
| A.java:13:31:13:51 | getInputStream(...) : InputStream | semmle.label | getInputStream(...) : InputStream |
|
||||
| A.java:14:28:14:61 | new ObjectInputStream(...) : ObjectInputStream | semmle.label | new ObjectInputStream(...) : ObjectInputStream |
|
||||
| A.java:14:50:14:60 | inputStream : InputStream | semmle.label | inputStream : InputStream |
|
||||
| A.java:15:12:15:13 | in | semmle.label | in |
|
||||
| A.java:19:31:19:51 | getInputStream(...) : InputStream | semmle.label | getInputStream(...) : InputStream |
|
||||
| A.java:20:28:20:61 | new ObjectInputStream(...) : ObjectInputStream | semmle.label | new ObjectInputStream(...) : ObjectInputStream |
|
||||
| A.java:20:50:20:60 | inputStream : InputStream | semmle.label | inputStream : InputStream |
|
||||
| A.java:21:12:21:13 | in | semmle.label | in |
|
||||
| A.java:25:31:25:51 | getInputStream(...) : InputStream | semmle.label | getInputStream(...) : InputStream |
|
||||
| A.java:26:20:26:46 | new XMLDecoder(...) : XMLDecoder | semmle.label | new XMLDecoder(...) : XMLDecoder |
|
||||
| A.java:26:35:26:45 | inputStream : InputStream | semmle.label | inputStream : InputStream |
|
||||
| A.java:27:12:27:12 | d | semmle.label | d |
|
||||
| A.java:32:31:32:51 | getInputStream(...) : InputStream | semmle.label | getInputStream(...) : InputStream |
|
||||
| A.java:33:21:33:54 | new InputStreamReader(...) : InputStreamReader | semmle.label | new InputStreamReader(...) : InputStreamReader |
|
||||
| A.java:33:43:33:53 | inputStream : InputStream | semmle.label | inputStream : InputStream |
|
||||
| A.java:34:23:34:28 | reader | semmle.label | reader |
|
||||
| A.java:39:19:39:50 | new Input(...) : Input | semmle.label | new Input(...) : Input |
|
||||
| A.java:39:29:39:49 | getInputStream(...) : InputStream | semmle.label | getInputStream(...) : InputStream |
|
||||
| A.java:40:28:40:32 | input | semmle.label | input |
|
||||
| A.java:41:34:41:38 | input | semmle.label | input |
|
||||
| A.java:42:40:42:44 | input | semmle.label | input |
|
||||
| A.java:60:25:60:45 | getInputStream(...) : InputStream | semmle.label | getInputStream(...) : InputStream |
|
||||
| A.java:61:26:61:30 | input | semmle.label | input |
|
||||
| A.java:62:30:62:34 | input | semmle.label | input |
|
||||
| A.java:63:28:63:55 | new InputStreamReader(...) | semmle.label | new InputStreamReader(...) |
|
||||
| A.java:63:50:63:54 | input : InputStream | semmle.label | input : InputStream |
|
||||
| A.java:64:24:64:28 | input | semmle.label | input |
|
||||
| A.java:65:24:65:51 | new InputStreamReader(...) | semmle.label | new InputStreamReader(...) |
|
||||
| A.java:65:46:65:50 | input : InputStream | semmle.label | input : InputStream |
|
||||
| A.java:70:25:70:45 | getInputStream(...) : InputStream | semmle.label | getInputStream(...) : InputStream |
|
||||
| A.java:71:26:71:30 | input | semmle.label | input |
|
||||
| A.java:72:30:72:34 | input | semmle.label | input |
|
||||
| A.java:73:28:73:55 | new InputStreamReader(...) | semmle.label | new InputStreamReader(...) |
|
||||
| A.java:73:50:73:54 | input : InputStream | semmle.label | input : InputStream |
|
||||
| A.java:74:24:74:28 | input | semmle.label | input |
|
||||
| A.java:75:24:75:51 | new InputStreamReader(...) | semmle.label | new InputStreamReader(...) |
|
||||
| A.java:75:46:75:50 | input : InputStream | semmle.label | input : InputStream |
|
||||
| A.java:90:25:90:45 | getInputStream(...) : InputStream | semmle.label | getInputStream(...) : InputStream |
|
||||
| A.java:91:26:91:30 | input | semmle.label | input |
|
||||
| A.java:92:30:92:34 | input | semmle.label | input |
|
||||
| A.java:93:28:93:55 | new InputStreamReader(...) | semmle.label | new InputStreamReader(...) |
|
||||
| A.java:93:50:93:54 | input : InputStream | semmle.label | input : InputStream |
|
||||
| A.java:94:24:94:28 | input | semmle.label | input |
|
||||
| A.java:95:24:95:51 | new InputStreamReader(...) | semmle.label | new InputStreamReader(...) |
|
||||
| A.java:95:46:95:50 | input : InputStream | semmle.label | input : InputStream |
|
||||
| B.java:7:31:7:51 | getInputStream(...) : InputStream | semmle.label | getInputStream(...) : InputStream |
|
||||
| B.java:8:29:8:39 | inputStream | semmle.label | inputStream |
|
||||
| B.java:12:31:12:51 | getInputStream(...) : InputStream | semmle.label | getInputStream(...) : InputStream |
|
||||
| B.java:14:5:14:15 | inputStream : InputStream | semmle.label | inputStream : InputStream |
|
||||
| B.java:14:22:14:26 | bytes [post update] : byte[] | semmle.label | bytes [post update] : byte[] |
|
||||
| B.java:15:23:15:27 | bytes | semmle.label | bytes |
|
||||
| B.java:19:31:19:51 | getInputStream(...) : InputStream | semmle.label | getInputStream(...) : InputStream |
|
||||
| B.java:21:5:21:15 | inputStream : InputStream | semmle.label | inputStream : InputStream |
|
||||
| B.java:21:22:21:26 | bytes [post update] : byte[] | semmle.label | bytes [post update] : byte[] |
|
||||
| B.java:23:29:23:29 | s | semmle.label | s |
|
||||
| B.java:27:31:27:51 | getInputStream(...) : InputStream | semmle.label | getInputStream(...) : InputStream |
|
||||
| B.java:29:5:29:15 | inputStream : InputStream | semmle.label | inputStream : InputStream |
|
||||
| B.java:29:22:29:26 | bytes [post update] : byte[] | semmle.label | bytes [post update] : byte[] |
|
||||
| B.java:31:23:31:23 | s | semmle.label | s |
|
||||
| C.java:23:17:23:44 | getParameter(...) : String | semmle.label | getParameter(...) : String |
|
||||
| C.java:24:13:24:16 | data | semmle.label | data |
|
||||
| C.java:25:19:25:22 | data | semmle.label | data |
|
||||
| C.java:26:25:26:28 | data | semmle.label | data |
|
||||
| C.java:27:17:27:20 | data | semmle.label | data |
|
||||
| C.java:30:19:30:22 | data | semmle.label | data |
|
||||
| C.java:31:25:31:28 | data | semmle.label | data |
|
||||
| C.java:32:31:32:34 | data | semmle.label | data |
|
||||
| C.java:33:23:33:26 | data | semmle.label | data |
|
||||
| C.java:38:17:38:44 | getParameter(...) : String | semmle.label | getParameter(...) : String |
|
||||
| C.java:43:25:43:28 | data | semmle.label | data |
|
||||
| C.java:46:3:46:4 | jr | semmle.label | jr |
|
||||
| C.java:51:17:51:44 | getParameter(...) : String | semmle.label | getParameter(...) : String |
|
||||
| C.java:53:3:53:3 | r | semmle.label | r |
|
||||
| C.java:54:3:54:3 | r | semmle.label | r |
|
||||
| C.java:55:3:55:3 | r | semmle.label | r |
|
||||
| C.java:60:18:60:45 | getParameter(...) : String | semmle.label | getParameter(...) : String |
|
||||
| C.java:61:30:61:60 | new ByteArrayInputStream(...) : ByteArrayInputStream | semmle.label | new ByteArrayInputStream(...) : ByteArrayInputStream |
|
||||
| C.java:61:55:61:59 | bytes : byte[] | semmle.label | bytes : byte[] |
|
||||
| C.java:63:3:63:14 | hessianInput | semmle.label | hessianInput |
|
||||
| C.java:64:3:64:14 | hessianInput | semmle.label | hessianInput |
|
||||
| C.java:69:18:69:45 | getParameter(...) : String | semmle.label | getParameter(...) : String |
|
||||
| C.java:70:30:70:60 | new ByteArrayInputStream(...) : ByteArrayInputStream | semmle.label | new ByteArrayInputStream(...) : ByteArrayInputStream |
|
||||
| C.java:70:55:70:59 | bytes : byte[] | semmle.label | bytes : byte[] |
|
||||
| C.java:72:3:72:14 | hessianInput | semmle.label | hessianInput |
|
||||
| C.java:73:3:73:14 | hessianInput | semmle.label | hessianInput |
|
||||
| C.java:79:26:79:71 | new StringReader(...) | semmle.label | new StringReader(...) |
|
||||
| C.java:79:43:79:70 | getParameter(...) : String | semmle.label | getParameter(...) : String |
|
||||
| C.java:84:27:84:54 | getParameter(...) : String | semmle.label | getParameter(...) : String |
|
||||
| C.java:85:29:85:68 | new ByteArrayInputStream(...) : ByteArrayInputStream | semmle.label | new ByteArrayInputStream(...) : ByteArrayInputStream |
|
||||
| C.java:85:54:85:67 | serializedData : byte[] | semmle.label | serializedData : byte[] |
|
||||
| C.java:87:3:87:13 | burlapInput | semmle.label | burlapInput |
|
||||
| C.java:91:3:91:14 | burlapInput1 | semmle.label | burlapInput1 |
|
||||
| TestMessageBodyReader.java:20:55:20:78 | entityStream : InputStream | semmle.label | entityStream : InputStream |
|
||||
| TestMessageBodyReader.java:22:18:22:52 | new ObjectInputStream(...) | semmle.label | new ObjectInputStream(...) |
|
||||
| TestMessageBodyReader.java:22:40:22:51 | entityStream : InputStream | semmle.label | entityStream : InputStream |
|
||||
#select
|
||||
| A.java:15:12:15:26 | readObject(...) | A.java:13:31:13:51 | getInputStream(...) : InputStream | A.java:15:12:15:13 | in | Unsafe deserialization of $@. | A.java:13:31:13:51 | getInputStream(...) | user input |
|
||||
| A.java:21:12:21:28 | readUnshared(...) | A.java:19:31:19:51 | getInputStream(...) : InputStream | A.java:21:12:21:13 | in | Unsafe deserialization of $@. | A.java:19:31:19:51 | getInputStream(...) | user input |
|
||||
| A.java:27:12:27:25 | readObject(...) | A.java:25:31:25:51 | getInputStream(...) : InputStream | A.java:27:12:27:12 | d | Unsafe deserialization of $@. | A.java:25:31:25:51 | getInputStream(...) | user input |
|
||||
| A.java:34:12:34:29 | fromXML(...) | A.java:32:31:32:51 | getInputStream(...) : InputStream | A.java:34:23:34:28 | reader | Unsafe deserialization of $@. | A.java:32:31:32:51 | getInputStream(...) | user input |
|
||||
| A.java:40:12:40:42 | readObject(...) | A.java:39:29:39:49 | getInputStream(...) : InputStream | A.java:40:28:40:32 | input | Unsafe deserialization of $@. | A.java:39:29:39:49 | getInputStream(...) | user input |
|
||||
| A.java:41:12:41:48 | readObjectOrNull(...) | A.java:39:29:39:49 | getInputStream(...) : InputStream | A.java:41:34:41:38 | input | Unsafe deserialization of $@. | A.java:39:29:39:49 | getInputStream(...) | user input |
|
||||
| A.java:42:16:42:45 | readClassAndObject(...) | A.java:39:29:39:49 | getInputStream(...) : InputStream | A.java:42:40:42:44 | input | Unsafe deserialization of $@. | A.java:39:29:39:49 | getInputStream(...) | user input |
|
||||
| A.java:61:16:61:31 | load(...) | A.java:60:25:60:45 | getInputStream(...) : InputStream | A.java:61:26:61:30 | input | Unsafe deserialization of $@. | A.java:60:25:60:45 | getInputStream(...) | user input |
|
||||
| A.java:62:17:62:35 | loadAll(...) | A.java:60:25:60:45 | getInputStream(...) : InputStream | A.java:62:30:62:34 | input | Unsafe deserialization of $@. | A.java:60:25:60:45 | getInputStream(...) | user input |
|
||||
| A.java:63:17:63:56 | parse(...) | A.java:60:25:60:45 | getInputStream(...) : InputStream | A.java:63:28:63:55 | new InputStreamReader(...) | Unsafe deserialization of $@. | A.java:60:25:60:45 | getInputStream(...) | user input |
|
||||
| A.java:64:12:64:38 | loadAs(...) | A.java:60:25:60:45 | getInputStream(...) : InputStream | A.java:64:24:64:28 | input | Unsafe deserialization of $@. | A.java:60:25:60:45 | getInputStream(...) | user input |
|
||||
| A.java:65:12:65:61 | loadAs(...) | A.java:60:25:60:45 | getInputStream(...) : InputStream | A.java:65:24:65:51 | new InputStreamReader(...) | Unsafe deserialization of $@. | A.java:60:25:60:45 | getInputStream(...) | user input |
|
||||
| A.java:71:16:71:31 | load(...) | A.java:70:25:70:45 | getInputStream(...) : InputStream | A.java:71:26:71:30 | input | Unsafe deserialization of $@. | A.java:70:25:70:45 | getInputStream(...) | user input |
|
||||
| A.java:72:17:72:35 | loadAll(...) | A.java:70:25:70:45 | getInputStream(...) : InputStream | A.java:72:30:72:34 | input | Unsafe deserialization of $@. | A.java:70:25:70:45 | getInputStream(...) | user input |
|
||||
| A.java:73:17:73:56 | parse(...) | A.java:70:25:70:45 | getInputStream(...) : InputStream | A.java:73:28:73:55 | new InputStreamReader(...) | Unsafe deserialization of $@. | A.java:70:25:70:45 | getInputStream(...) | user input |
|
||||
| A.java:74:12:74:38 | loadAs(...) | A.java:70:25:70:45 | getInputStream(...) : InputStream | A.java:74:24:74:28 | input | Unsafe deserialization of $@. | A.java:70:25:70:45 | getInputStream(...) | user input |
|
||||
| A.java:75:12:75:61 | loadAs(...) | A.java:70:25:70:45 | getInputStream(...) : InputStream | A.java:75:24:75:51 | new InputStreamReader(...) | Unsafe deserialization of $@. | A.java:70:25:70:45 | getInputStream(...) | user input |
|
||||
| A.java:91:16:91:31 | load(...) | A.java:90:25:90:45 | getInputStream(...) : InputStream | A.java:91:26:91:30 | input | Unsafe deserialization of $@. | A.java:90:25:90:45 | getInputStream(...) | user input |
|
||||
| A.java:92:17:92:35 | loadAll(...) | A.java:90:25:90:45 | getInputStream(...) : InputStream | A.java:92:30:92:34 | input | Unsafe deserialization of $@. | A.java:90:25:90:45 | getInputStream(...) | user input |
|
||||
| A.java:93:17:93:56 | parse(...) | A.java:90:25:90:45 | getInputStream(...) : InputStream | A.java:93:28:93:55 | new InputStreamReader(...) | Unsafe deserialization of $@. | A.java:90:25:90:45 | getInputStream(...) | user input |
|
||||
| A.java:94:12:94:38 | loadAs(...) | A.java:90:25:90:45 | getInputStream(...) : InputStream | A.java:94:24:94:28 | input | Unsafe deserialization of $@. | A.java:90:25:90:45 | getInputStream(...) | user input |
|
||||
| A.java:95:12:95:61 | loadAs(...) | A.java:90:25:90:45 | getInputStream(...) : InputStream | A.java:95:24:95:51 | new InputStreamReader(...) | Unsafe deserialization of $@. | A.java:90:25:90:45 | getInputStream(...) | user input |
|
||||
| B.java:8:12:8:46 | parseObject(...) | B.java:7:31:7:51 | getInputStream(...) : InputStream | B.java:8:29:8:39 | inputStream | Unsafe deserialization of $@. | B.java:7:31:7:51 | getInputStream(...) | user input |
|
||||
| B.java:15:12:15:28 | parse(...) | B.java:12:31:12:51 | getInputStream(...) : InputStream | B.java:15:23:15:27 | bytes | Unsafe deserialization of $@. | B.java:12:31:12:51 | getInputStream(...) | user input |
|
||||
| B.java:23:12:23:30 | parseObject(...) | B.java:19:31:19:51 | getInputStream(...) : InputStream | B.java:23:29:23:29 | s | Unsafe deserialization of $@. | B.java:19:31:19:51 | getInputStream(...) | user input |
|
||||
| B.java:31:12:31:24 | parse(...) | B.java:27:31:27:51 | getInputStream(...) : InputStream | B.java:31:23:31:23 | s | Unsafe deserialization of $@. | B.java:27:31:27:51 | getInputStream(...) | user input |
|
||||
| C.java:24:3:24:17 | load(...) | C.java:23:17:23:44 | getParameter(...) : String | C.java:24:13:24:16 | data | Unsafe deserialization of $@. | C.java:23:17:23:44 | getParameter(...) | user input |
|
||||
| C.java:25:3:25:23 | loadStream(...) | C.java:23:17:23:44 | getParameter(...) : String | C.java:25:19:25:22 | data | Unsafe deserialization of $@. | C.java:23:17:23:44 | getParameter(...) | user input |
|
||||
| C.java:26:3:26:43 | loadStreamOfType(...) | C.java:23:17:23:44 | getParameter(...) : String | C.java:26:25:26:28 | data | Unsafe deserialization of $@. | C.java:23:17:23:44 | getParameter(...) | user input |
|
||||
| C.java:27:3:27:35 | loadType(...) | C.java:23:17:23:44 | getParameter(...) : String | C.java:27:17:27:20 | data | Unsafe deserialization of $@. | C.java:23:17:23:44 | getParameter(...) | user input |
|
||||
| C.java:30:3:30:23 | load(...) | C.java:23:17:23:44 | getParameter(...) : String | C.java:30:19:30:22 | data | Unsafe deserialization of $@. | C.java:23:17:23:44 | getParameter(...) | user input |
|
||||
| C.java:31:3:31:29 | loadStream(...) | C.java:23:17:23:44 | getParameter(...) : String | C.java:31:25:31:28 | data | Unsafe deserialization of $@. | C.java:23:17:23:44 | getParameter(...) | user input |
|
||||
| C.java:32:3:32:49 | loadStreamOfType(...) | C.java:23:17:23:44 | getParameter(...) : String | C.java:32:31:32:34 | data | Unsafe deserialization of $@. | C.java:23:17:23:44 | getParameter(...) | user input |
|
||||
| C.java:33:3:33:41 | loadType(...) | C.java:23:17:23:44 | getParameter(...) : String | C.java:33:23:33:26 | data | Unsafe deserialization of $@. | C.java:23:17:23:44 | getParameter(...) | user input |
|
||||
| C.java:43:3:43:29 | jsonToJava(...) | C.java:38:17:38:44 | getParameter(...) : String | C.java:43:25:43:28 | data | Unsafe deserialization of $@. | C.java:38:17:38:44 | getParameter(...) | user input |
|
||||
| C.java:46:3:46:17 | readObject(...) | C.java:38:17:38:44 | getParameter(...) : String | C.java:46:3:46:4 | jr | Unsafe deserialization of $@. | C.java:38:17:38:44 | getParameter(...) | user input |
|
||||
| C.java:53:3:53:10 | read(...) | C.java:51:17:51:44 | getParameter(...) : String | C.java:53:3:53:3 | r | Unsafe deserialization of $@. | C.java:51:17:51:44 | getParameter(...) | user input |
|
||||
| C.java:54:3:54:22 | read(...) | C.java:51:17:51:44 | getParameter(...) : String | C.java:54:3:54:3 | r | Unsafe deserialization of $@. | C.java:51:17:51:44 | getParameter(...) | user input |
|
||||
| C.java:55:3:55:36 | read(...) | C.java:51:17:51:44 | getParameter(...) : String | C.java:55:3:55:3 | r | Unsafe deserialization of $@. | C.java:51:17:51:44 | getParameter(...) | user input |
|
||||
| C.java:63:3:63:27 | readObject(...) | C.java:60:18:60:45 | getParameter(...) : String | C.java:63:3:63:14 | hessianInput | Unsafe deserialization of $@. | C.java:60:18:60:45 | getParameter(...) | user input |
|
||||
| C.java:64:3:64:39 | readObject(...) | C.java:60:18:60:45 | getParameter(...) : String | C.java:64:3:64:14 | hessianInput | Unsafe deserialization of $@. | C.java:60:18:60:45 | getParameter(...) | user input |
|
||||
| C.java:72:3:72:27 | readObject(...) | C.java:69:18:69:45 | getParameter(...) : String | C.java:72:3:72:14 | hessianInput | Unsafe deserialization of $@. | C.java:69:18:69:45 | getParameter(...) | user input |
|
||||
| C.java:73:3:73:39 | readObject(...) | C.java:69:18:69:45 | getParameter(...) : String | C.java:73:3:73:14 | hessianInput | Unsafe deserialization of $@. | C.java:69:18:69:45 | getParameter(...) | user input |
|
||||
| C.java:79:3:79:72 | unmarshal(...) | C.java:79:43:79:70 | getParameter(...) : String | C.java:79:26:79:71 | new StringReader(...) | Unsafe deserialization of $@. | C.java:79:43:79:70 | getParameter(...) | user input |
|
||||
| C.java:87:3:87:26 | readObject(...) | C.java:84:27:84:54 | getParameter(...) : String | C.java:87:3:87:13 | burlapInput | Unsafe deserialization of $@. | C.java:84:27:84:54 | getParameter(...) | user input |
|
||||
| C.java:91:3:91:27 | readObject(...) | C.java:84:27:84:54 | getParameter(...) : String | C.java:91:3:91:14 | burlapInput1 | Unsafe deserialization of $@. | C.java:84:27:84:54 | getParameter(...) | user input |
|
||||
| TestMessageBodyReader.java:22:18:22:65 | readObject(...) | TestMessageBodyReader.java:20:55:20:78 | entityStream : InputStream | TestMessageBodyReader.java:22:18:22:52 | new ObjectInputStream(...) | Unsafe deserialization of $@. | TestMessageBodyReader.java:20:55:20:78 | entityStream | user input |
|
||||
|
||||
@@ -0,0 +1,18 @@
|
||||
import java
|
||||
import semmle.code.java.security.UnsafeDeserializationQuery
|
||||
import TestUtilities.InlineExpectationsTest
|
||||
|
||||
class UnsafeDeserializationTest extends InlineExpectationsTest {
|
||||
UnsafeDeserializationTest() { this = "UnsafeDeserializationTest" }
|
||||
|
||||
override string getARelevantTag() { result = "unsafeDeserialization" }
|
||||
|
||||
override predicate hasActualResult(Location location, string element, string tag, string value) {
|
||||
tag = "unsafeDeserialization" and
|
||||
exists(DataFlow::Node sink, UnsafeDeserializationConfig conf | conf.hasFlowTo(sink) |
|
||||
sink.getLocation() = location and
|
||||
element = sink.toString() and
|
||||
value = ""
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -1 +0,0 @@
|
||||
Security/CWE/CWE-502/UnsafeDeserialization.ql
|
||||
@@ -1 +1 @@
|
||||
//semmle-extractor-options: --javac-args -cp ${testdir}/../../../stubs/snakeyaml-1.21:${testdir}/../../../stubs/xstream-1.4.10:${testdir}/../../../stubs/kryo-4.0.2:${testdir}/../../../stubs/jsr311-api-1.1.1:${testdir}/../../../stubs/fastjson-1.2.74:${testdir}/../../../stubs/springframework-5.3.8:${testdir}/../../../stubs/servlet-api-2.4:${testdir}/../../../stubs/jyaml-1.3:${testdir}/../../../stubs/json-io-4.10.0:${testdir}/../../../stubs/yamlbeans-1.09:${testdir}/../../../stubs/hessian-4.0.38:${testdir}/../../../stubs/castor-1.4.1
|
||||
//semmle-extractor-options: --javac-args -cp ${testdir}/../../../stubs/snakeyaml-1.21:${testdir}/../../../stubs/xstream-1.4.10:${testdir}/../../../stubs/kryo-4.0.2:${testdir}/../../../stubs/jsr311-api-1.1.1:${testdir}/../../../stubs/fastjson-1.2.74:${testdir}/../../../stubs/springframework-5.3.8:${testdir}/../../../stubs/servlet-api-2.4:${testdir}/../../../stubs/jyaml-1.3:${testdir}/../../../stubs/json-io-4.10.0:${testdir}/../../../stubs/yamlbeans-1.09:${testdir}/../../../stubs/hessian-4.0.38:${testdir}/../../../stubs/castor-1.4.1:${testdir}/../../../stubs/jackson-databind-2.10
|
||||
|
||||
@@ -0,0 +1,21 @@
|
||||
package com.fasterxml.jackson.annotation;
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
@Target({ElementType.ANNOTATION_TYPE, ElementType.TYPE, ElementType.FIELD, ElementType.METHOD, ElementType.PARAMETER})
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
public @interface JsonTypeInfo {
|
||||
JsonTypeInfo.Id use();
|
||||
|
||||
public static enum Id {
|
||||
CLASS("@class"),
|
||||
MINIMAL_CLASS("@c");
|
||||
|
||||
private Id(String defProp) { }
|
||||
|
||||
public String getDefaultPropertyName() { return null; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
package com.fasterxml.jackson.core;
|
||||
|
||||
public abstract class JsonParser {}
|
||||
@@ -0,0 +1,3 @@
|
||||
package com.fasterxml.jackson.core;
|
||||
|
||||
public interface TreeNode {}
|
||||
@@ -1,213 +1,3 @@
|
||||
package com.fasterxml.jackson.databind;
|
||||
|
||||
import java.util.List;
|
||||
import com.fasterxml.jackson.core.type.ResolvedType;
|
||||
|
||||
public abstract class JavaType extends ResolvedType implements java.io.Serializable, // 2.1
|
||||
java.lang.reflect.Type // 2.2
|
||||
{
|
||||
public abstract JavaType withTypeHandler(Object h);
|
||||
|
||||
public abstract JavaType withContentTypeHandler(Object h);
|
||||
|
||||
public abstract JavaType withValueHandler(Object h);
|
||||
|
||||
public abstract JavaType withContentValueHandler(Object h);
|
||||
|
||||
public JavaType withHandlersFrom(JavaType src) {
|
||||
return null;
|
||||
}
|
||||
|
||||
public abstract JavaType withContentType(JavaType contentType);
|
||||
|
||||
public abstract JavaType withStaticTyping();
|
||||
|
||||
public JavaType forcedNarrowBy(Class<?> subclass) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public final Class<?> getRawClass() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public final boolean hasRawClass(Class<?> clz) {
|
||||
return false;
|
||||
}
|
||||
|
||||
public boolean hasContentType() {
|
||||
return false;
|
||||
}
|
||||
|
||||
public final boolean isTypeOrSubTypeOf(Class<?> clz) {
|
||||
return false;
|
||||
}
|
||||
|
||||
public final boolean isTypeOrSuperTypeOf(Class<?> clz) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isAbstract() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isConcrete() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isThrowable() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isArrayType() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public final boolean isEnumType() {
|
||||
return false;
|
||||
}
|
||||
|
||||
public final boolean isEnumImplType() {
|
||||
return false;
|
||||
}
|
||||
|
||||
public final boolean isRecordType() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public final boolean isInterface() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public final boolean isPrimitive() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public final boolean isFinal() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public abstract boolean isContainerType();
|
||||
|
||||
@Override
|
||||
public boolean isCollectionLikeType() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isMapLikeType() {
|
||||
return false;
|
||||
}
|
||||
|
||||
public final boolean isJavaLangObject() {
|
||||
return false;
|
||||
}
|
||||
|
||||
public final boolean useStaticType() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasGenericTypes() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public JavaType getKeyType() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public JavaType getContentType() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public JavaType getReferencedType() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public abstract int containedTypeCount();
|
||||
|
||||
@Override
|
||||
public abstract JavaType containedType(int index);
|
||||
|
||||
@Override
|
||||
public abstract String containedTypeName(int index);
|
||||
|
||||
@Override
|
||||
public Class<?> getParameterSource() {
|
||||
return null;
|
||||
}
|
||||
|
||||
public JavaType containedTypeOrUnknown(int index) {
|
||||
return null;
|
||||
}
|
||||
|
||||
public abstract JavaType findSuperType(Class<?> erasedTarget);
|
||||
|
||||
public abstract JavaType getSuperClass();
|
||||
|
||||
public abstract List<JavaType> getInterfaces();
|
||||
|
||||
public abstract JavaType[] findTypeParameters(Class<?> expType);
|
||||
|
||||
public <T> T getValueHandler() {
|
||||
return null;
|
||||
}
|
||||
|
||||
public <T> T getTypeHandler() {
|
||||
return null;
|
||||
}
|
||||
|
||||
public Object getContentValueHandler() {
|
||||
return false;
|
||||
}
|
||||
|
||||
public Object getContentTypeHandler() {
|
||||
return false;
|
||||
}
|
||||
|
||||
public boolean hasValueHandler() {
|
||||
return false;
|
||||
}
|
||||
|
||||
public boolean hasHandlers() {
|
||||
return false;
|
||||
}
|
||||
|
||||
public String getGenericSignature() {
|
||||
return null;
|
||||
}
|
||||
|
||||
public abstract StringBuilder getGenericSignature(StringBuilder sb);
|
||||
|
||||
public String getErasedSignature() {
|
||||
return null;
|
||||
}
|
||||
|
||||
public abstract StringBuilder getErasedSignature(StringBuilder sb);
|
||||
|
||||
@Override
|
||||
public abstract String toString();
|
||||
|
||||
@Override
|
||||
public abstract boolean equals(Object o);
|
||||
|
||||
@Override
|
||||
public final int hashCode() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
}
|
||||
public class JavaType {}
|
||||
@@ -25,4 +25,8 @@ public class MappingIterator<T> implements Iterator<T>, Closeable {
|
||||
public void close() throws IOException {
|
||||
|
||||
}
|
||||
|
||||
public List<T> readAll() {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
package com.fasterxml.jackson.databind.cfg;
|
||||
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import com.fasterxml.jackson.databind.jsontype.PolymorphicTypeValidator;
|
||||
|
||||
public abstract class MapperBuilder<M extends ObjectMapper, B extends MapperBuilder<M, B>> {
|
||||
public M build() { return null; }
|
||||
public B polymorphicTypeValidator(PolymorphicTypeValidator ptv) { return null; }
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
package com.fasterxml.jackson.databind.json;
|
||||
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import com.fasterxml.jackson.databind.cfg.MapperBuilder;
|
||||
|
||||
public class JsonMapper extends ObjectMapper {
|
||||
public static JsonMapper.Builder builder() { return null; }
|
||||
public static class Builder extends MapperBuilder<JsonMapper, JsonMapper.Builder> {}
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
package com.fasterxml.jackson.databind.jsontype;
|
||||
|
||||
public class BasicPolymorphicTypeValidator extends PolymorphicTypeValidator {
|
||||
public static BasicPolymorphicTypeValidator.Builder builder() { return null; }
|
||||
|
||||
public static class Builder {
|
||||
public BasicPolymorphicTypeValidator.Builder allowIfSubType(final String prefixForSubType) { return null; }
|
||||
public BasicPolymorphicTypeValidator build() { return null; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
package com.fasterxml.jackson.databind.jsontype;
|
||||
|
||||
public abstract class PolymorphicTypeValidator {}
|
||||
@@ -62,33 +62,33 @@ public class MediaType extends MimeType implements Serializable
|
||||
public static MediaType asMediaType(MimeType p0){ return null; }
|
||||
public static MediaType parseMediaType(String p0){ return null; }
|
||||
public static MediaType valueOf(String p0){ return null; }
|
||||
public static String ALL_VALUE = null;
|
||||
public static String APPLICATION_ATOM_XML_VALUE = null;
|
||||
public static String APPLICATION_CBOR_VALUE = null;
|
||||
public static String APPLICATION_FORM_URLENCODED_VALUE = null;
|
||||
public static String APPLICATION_JSON_UTF8_VALUE = null;
|
||||
public static String APPLICATION_JSON_VALUE = null;
|
||||
public static String APPLICATION_NDJSON_VALUE = null;
|
||||
public static String APPLICATION_OCTET_STREAM_VALUE = null;
|
||||
public static String APPLICATION_PDF_VALUE = null;
|
||||
public static String APPLICATION_PROBLEM_JSON_UTF8_VALUE = null;
|
||||
public static String APPLICATION_PROBLEM_JSON_VALUE = null;
|
||||
public static String APPLICATION_PROBLEM_XML_VALUE = null;
|
||||
public static String APPLICATION_RSS_XML_VALUE = null;
|
||||
public static String APPLICATION_STREAM_JSON_VALUE = null;
|
||||
public static String APPLICATION_XHTML_XML_VALUE = null;
|
||||
public static String APPLICATION_XML_VALUE = null;
|
||||
public static String IMAGE_GIF_VALUE = null;
|
||||
public static String IMAGE_JPEG_VALUE = null;
|
||||
public static String IMAGE_PNG_VALUE = null;
|
||||
public static String MULTIPART_FORM_DATA_VALUE = null;
|
||||
public static String MULTIPART_MIXED_VALUE = null;
|
||||
public static String MULTIPART_RELATED_VALUE = null;
|
||||
public static String TEXT_EVENT_STREAM_VALUE = null;
|
||||
public static String TEXT_HTML_VALUE = null;
|
||||
public static String TEXT_MARKDOWN_VALUE = null;
|
||||
public static String TEXT_PLAIN_VALUE = null;
|
||||
public static String TEXT_XML_VALUE = null;
|
||||
public static final String ALL_VALUE = "";
|
||||
public static final String APPLICATION_ATOM_XML_VALUE = "";
|
||||
public static final String APPLICATION_CBOR_VALUE = "";
|
||||
public static final String APPLICATION_FORM_URLENCODED_VALUE = "";
|
||||
public static final String APPLICATION_JSON_UTF8_VALUE = "";
|
||||
public static final String APPLICATION_JSON_VALUE = "";
|
||||
public static final String APPLICATION_NDJSON_VALUE = "";
|
||||
public static final String APPLICATION_OCTET_STREAM_VALUE = "";
|
||||
public static final String APPLICATION_PDF_VALUE = "";
|
||||
public static final String APPLICATION_PROBLEM_JSON_UTF8_VALUE = "";
|
||||
public static final String APPLICATION_PROBLEM_JSON_VALUE = "";
|
||||
public static final String APPLICATION_PROBLEM_XML_VALUE = "";
|
||||
public static final String APPLICATION_RSS_XML_VALUE = "";
|
||||
public static final String APPLICATION_STREAM_JSON_VALUE = "";
|
||||
public static final String APPLICATION_XHTML_XML_VALUE = "";
|
||||
public static final String APPLICATION_XML_VALUE = "";
|
||||
public static final String IMAGE_GIF_VALUE = "";
|
||||
public static final String IMAGE_JPEG_VALUE = "";
|
||||
public static final String IMAGE_PNG_VALUE = "";
|
||||
public static final String MULTIPART_FORM_DATA_VALUE = "";
|
||||
public static final String MULTIPART_MIXED_VALUE = "";
|
||||
public static final String MULTIPART_RELATED_VALUE = "";
|
||||
public static final String TEXT_EVENT_STREAM_VALUE = "";
|
||||
public static final String TEXT_HTML_VALUE = "";
|
||||
public static final String TEXT_MARKDOWN_VALUE = "";
|
||||
public static final String TEXT_PLAIN_VALUE = "";
|
||||
public static final String TEXT_XML_VALUE = "";
|
||||
public static String toString(Collection<MediaType> p0){ return null; }
|
||||
public static void sortByQualityValue(List<MediaType> p0){}
|
||||
public static void sortBySpecificity(List<MediaType> p0){}
|
||||
|
||||
@@ -1,3 +1,19 @@
|
||||
/*
|
||||
* Copyright 2002-2016 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.web.bind.annotation;
|
||||
|
||||
import java.lang.annotation.Documented;
|
||||
@@ -6,11 +22,69 @@ import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
import org.springframework.core.annotation.AliasFor;
|
||||
|
||||
/**
|
||||
* Annotation for mapping HTTP {@code POST} requests onto specific handler
|
||||
* methods.
|
||||
*
|
||||
* <p>Specifically, {@code @PostMapping} is a <em>composed annotation</em> that
|
||||
* acts as a shortcut for {@code @RequestMapping(method = RequestMethod.POST)}.
|
||||
*
|
||||
* @author Sam Brannen
|
||||
* @since 4.3
|
||||
* @see GetMapping
|
||||
* @see PutMapping
|
||||
* @see DeleteMapping
|
||||
* @see PatchMapping
|
||||
* @see RequestMapping
|
||||
*/
|
||||
@Target(ElementType.METHOD)
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Documented
|
||||
@RequestMapping(method = RequestMethod.POST)
|
||||
public @interface PostMapping {
|
||||
|
||||
String[] value() default {};
|
||||
}
|
||||
/**
|
||||
* Alias for {@link RequestMapping#name}.
|
||||
*/
|
||||
@AliasFor(annotation = RequestMapping.class)
|
||||
String name() default "";
|
||||
|
||||
/**
|
||||
* Alias for {@link RequestMapping#value}.
|
||||
*/
|
||||
@AliasFor(annotation = RequestMapping.class)
|
||||
String[] value() default {};
|
||||
|
||||
/**
|
||||
* Alias for {@link RequestMapping#path}.
|
||||
*/
|
||||
@AliasFor(annotation = RequestMapping.class)
|
||||
String[] path() default {};
|
||||
|
||||
/**
|
||||
* Alias for {@link RequestMapping#params}.
|
||||
*/
|
||||
@AliasFor(annotation = RequestMapping.class)
|
||||
String[] params() default {};
|
||||
|
||||
/**
|
||||
* Alias for {@link RequestMapping#headers}.
|
||||
*/
|
||||
@AliasFor(annotation = RequestMapping.class)
|
||||
String[] headers() default {};
|
||||
|
||||
/**
|
||||
* Alias for {@link RequestMapping#consumes}.
|
||||
*/
|
||||
@AliasFor(annotation = RequestMapping.class)
|
||||
String[] consumes() default {};
|
||||
|
||||
/**
|
||||
* Alias for {@link RequestMapping#produces}.
|
||||
*/
|
||||
@AliasFor(annotation = RequestMapping.class)
|
||||
String[] produces() default {};
|
||||
|
||||
}
|
||||
|
||||
@@ -1,3 +1,19 @@
|
||||
/*
|
||||
* Copyright 2002-2020 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.web.bind.annotation;
|
||||
|
||||
import java.lang.annotation.Documented;
|
||||
@@ -5,28 +21,189 @@ import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
import org.springframework.core.annotation.AliasFor;
|
||||
|
||||
@Target(value={ElementType.METHOD,ElementType.TYPE})
|
||||
@Retention(value=RetentionPolicy.RUNTIME)
|
||||
/**
|
||||
* Annotation for mapping web requests onto methods in request-handling classes
|
||||
* with flexible method signatures.
|
||||
*
|
||||
* <p>Both Spring MVC and Spring WebFlux support this annotation through a
|
||||
* {@code RequestMappingHandlerMapping} and {@code RequestMappingHandlerAdapter}
|
||||
* in their respective modules and package structure. For the exact list of
|
||||
* supported handler method arguments and return types in each, please use the
|
||||
* reference documentation links below:
|
||||
* <ul>
|
||||
* <li>Spring MVC
|
||||
* <a href="https://docs.spring.io/spring/docs/current/spring-framework-reference/web.html#mvc-ann-arguments">Method Arguments</a>
|
||||
* and
|
||||
* <a href="https://docs.spring.io/spring/docs/current/spring-framework-reference/web.html#mvc-ann-return-types">Return Values</a>
|
||||
* </li>
|
||||
* <li>Spring WebFlux
|
||||
* <a href="https://docs.spring.io/spring/docs/current/spring-framework-reference/web-reactive.html#webflux-ann-arguments">Method Arguments</a>
|
||||
* and
|
||||
* <a href="https://docs.spring.io/spring/docs/current/spring-framework-reference/web-reactive.html#webflux-ann-return-types">Return Values</a>
|
||||
* </li>
|
||||
* </ul>
|
||||
*
|
||||
* <p><strong>Note:</strong> This annotation can be used both at the class and
|
||||
* at the method level. In most cases, at the method level applications will
|
||||
* prefer to use one of the HTTP method specific variants
|
||||
* {@link GetMapping @GetMapping}, {@link PostMapping @PostMapping},
|
||||
* {@link PutMapping @PutMapping}, {@link DeleteMapping @DeleteMapping}, or
|
||||
* {@link PatchMapping @PatchMapping}.</p>
|
||||
*
|
||||
* <p><b>NOTE:</b> When using controller interfaces (e.g. for AOP proxying),
|
||||
* make sure to consistently put <i>all</i> your mapping annotations - such as
|
||||
* {@code @RequestMapping} and {@code @SessionAttributes} - on
|
||||
* the controller <i>interface</i> rather than on the implementation class.
|
||||
*
|
||||
* @author Juergen Hoeller
|
||||
* @author Arjen Poutsma
|
||||
* @author Sam Brannen
|
||||
* @since 2.5
|
||||
* @see GetMapping
|
||||
* @see PostMapping
|
||||
* @see PutMapping
|
||||
* @see DeleteMapping
|
||||
* @see PatchMapping
|
||||
*/
|
||||
@Target({ElementType.TYPE, ElementType.METHOD})
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Documented
|
||||
@Mapping
|
||||
public @interface RequestMapping {
|
||||
String name() default "";
|
||||
|
||||
@AliasFor("path")
|
||||
String[] value() default {};
|
||||
/**
|
||||
* Assign a name to this mapping.
|
||||
* <p><b>Supported at the type level as well as at the method level!</b>
|
||||
* When used on both levels, a combined name is derived by concatenation
|
||||
* with "#" as separator.
|
||||
* @see org.springframework.web.servlet.mvc.method.annotation.MvcUriComponentsBuilder
|
||||
* @see org.springframework.web.servlet.handler.HandlerMethodMappingNamingStrategy
|
||||
*/
|
||||
String name() default "";
|
||||
|
||||
@AliasFor("value")
|
||||
String[] path() default {};
|
||||
/**
|
||||
* The primary mapping expressed by this annotation.
|
||||
* <p>This is an alias for {@link #path}. For example,
|
||||
* {@code @RequestMapping("/foo")} is equivalent to
|
||||
* {@code @RequestMapping(path="/foo")}.
|
||||
* <p><b>Supported at the type level as well as at the method level!</b>
|
||||
* When used at the type level, all method-level mappings inherit
|
||||
* this primary mapping, narrowing it for a specific handler method.
|
||||
* <p><strong>NOTE</strong>: A handler method that is not mapped to any path
|
||||
* explicitly is effectively mapped to an empty path.
|
||||
*/
|
||||
@AliasFor("path")
|
||||
String[] value() default {};
|
||||
|
||||
RequestMethod[] method() default {};
|
||||
/**
|
||||
* The path mapping URIs (e.g. {@code "/profile"}).
|
||||
* <p>Ant-style path patterns are also supported (e.g. {@code "/profile/**"}).
|
||||
* At the method level, relative paths (e.g. {@code "edit"}) are supported
|
||||
* within the primary mapping expressed at the type level.
|
||||
* Path mapping URIs may contain placeholders (e.g. <code>"/${profile_path}"</code>).
|
||||
* <p><b>Supported at the type level as well as at the method level!</b>
|
||||
* When used at the type level, all method-level mappings inherit
|
||||
* this primary mapping, narrowing it for a specific handler method.
|
||||
* <p><strong>NOTE</strong>: A handler method that is not mapped to any path
|
||||
* explicitly is effectively mapped to an empty path.
|
||||
* @since 4.2
|
||||
*/
|
||||
@AliasFor("value")
|
||||
String[] path() default {};
|
||||
|
||||
String[] params() default {};
|
||||
/**
|
||||
* The HTTP request methods to map to, narrowing the primary mapping:
|
||||
* GET, POST, HEAD, OPTIONS, PUT, PATCH, DELETE, TRACE.
|
||||
* <p><b>Supported at the type level as well as at the method level!</b>
|
||||
* When used at the type level, all method-level mappings inherit this
|
||||
* HTTP method restriction.
|
||||
*/
|
||||
RequestMethod[] method() default {};
|
||||
|
||||
String[] headers() default {};
|
||||
/**
|
||||
* The parameters of the mapped request, narrowing the primary mapping.
|
||||
* <p>Same format for any environment: a sequence of "myParam=myValue" style
|
||||
* expressions, with a request only mapped if each such parameter is found
|
||||
* to have the given value. Expressions can be negated by using the "!=" operator,
|
||||
* as in "myParam!=myValue". "myParam" style expressions are also supported,
|
||||
* with such parameters having to be present in the request (allowed to have
|
||||
* any value). Finally, "!myParam" style expressions indicate that the
|
||||
* specified parameter is <i>not</i> supposed to be present in the request.
|
||||
* <p><b>Supported at the type level as well as at the method level!</b>
|
||||
* When used at the type level, all method-level mappings inherit this
|
||||
* parameter restriction.
|
||||
*/
|
||||
String[] params() default {};
|
||||
|
||||
String[] consumes() default {};
|
||||
/**
|
||||
* The headers of the mapped request, narrowing the primary mapping.
|
||||
* <p>Same format for any environment: a sequence of "My-Header=myValue" style
|
||||
* expressions, with a request only mapped if each such header is found
|
||||
* to have the given value. Expressions can be negated by using the "!=" operator,
|
||||
* as in "My-Header!=myValue". "My-Header" style expressions are also supported,
|
||||
* with such headers having to be present in the request (allowed to have
|
||||
* any value). Finally, "!My-Header" style expressions indicate that the
|
||||
* specified header is <i>not</i> supposed to be present in the request.
|
||||
* <p>Also supports media type wildcards (*), for headers such as Accept
|
||||
* and Content-Type. For instance,
|
||||
* <pre class="code">
|
||||
* @RequestMapping(value = "/something", headers = "content-type=text/*")
|
||||
* </pre>
|
||||
* will match requests with a Content-Type of "text/html", "text/plain", etc.
|
||||
* <p><b>Supported at the type level as well as at the method level!</b>
|
||||
* When used at the type level, all method-level mappings inherit this
|
||||
* header restriction.
|
||||
* @see org.springframework.http.MediaType
|
||||
*/
|
||||
String[] headers() default {};
|
||||
|
||||
/**
|
||||
* Narrows the primary mapping by media types that can be consumed by the
|
||||
* mapped handler. Consists of one or more media types one of which must
|
||||
* match to the request {@code Content-Type} header. Examples:
|
||||
* <pre class="code">
|
||||
* consumes = "text/plain"
|
||||
* consumes = {"text/plain", "application/*"}
|
||||
* consumes = MediaType.TEXT_PLAIN_VALUE
|
||||
* </pre>
|
||||
* Expressions can be negated by using the "!" operator, as in
|
||||
* "!text/plain", which matches all requests with a {@code Content-Type}
|
||||
* other than "text/plain".
|
||||
* <p><b>Supported at the type level as well as at the method level!</b>
|
||||
* If specified at both levels, the method level consumes condition overrides
|
||||
* the type level condition.
|
||||
* @see org.springframework.http.MediaType
|
||||
* @see javax.servlet.http.HttpServletRequest#getContentType()
|
||||
*/
|
||||
String[] consumes() default {};
|
||||
|
||||
/**
|
||||
* Narrows the primary mapping by media types that can be produced by the
|
||||
* mapped handler. Consists of one or more media types one of which must
|
||||
* be chosen via content negotiation against the "acceptable" media types
|
||||
* of the request. Typically those are extracted from the {@code "Accept"}
|
||||
* header but may be derived from query parameters, or other. Examples:
|
||||
* <pre class="code">
|
||||
* produces = "text/plain"
|
||||
* produces = {"text/plain", "application/*"}
|
||||
* produces = MediaType.TEXT_PLAIN_VALUE
|
||||
* produces = "text/plain;charset=UTF-8"
|
||||
* </pre>
|
||||
* <p>If a declared media type contains a parameter (e.g. "charset=UTF-8",
|
||||
* "type=feed", "type=entry") and if a compatible media type from the request
|
||||
* has that parameter too, then the parameter values must match. Otherwise
|
||||
* if the media type from the request does not contain the parameter, it is
|
||||
* assumed the client accepts any value.
|
||||
* <p>Expressions can be negated by using the "!" operator, as in "!text/plain",
|
||||
* which matches all requests with a {@code Accept} other than "text/plain".
|
||||
* <p><b>Supported at the type level as well as at the method level!</b>
|
||||
* If specified at both levels, the method level produces condition overrides
|
||||
* the type level condition.
|
||||
* @see org.springframework.http.MediaType
|
||||
*/
|
||||
String[] produces() default {};
|
||||
|
||||
String[] produces() default {};
|
||||
}
|
||||
|
||||
@@ -0,0 +1,61 @@
|
||||
/*
|
||||
* Copyright 2002-2017 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.web.bind.annotation;
|
||||
|
||||
import java.lang.annotation.Documented;
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
import org.springframework.core.annotation.AliasFor;
|
||||
import org.springframework.stereotype.Controller;
|
||||
|
||||
/**
|
||||
* A convenience annotation that is itself annotated with
|
||||
* {@link Controller @Controller} and {@link ResponseBody @ResponseBody}.
|
||||
* <p>
|
||||
* Types that carry this annotation are treated as controllers where
|
||||
* {@link RequestMapping @RequestMapping} methods assume
|
||||
* {@link ResponseBody @ResponseBody} semantics by default.
|
||||
*
|
||||
* <p><b>NOTE:</b> {@code @RestController} is processed if an appropriate
|
||||
* {@code HandlerMapping}-{@code HandlerAdapter} pair is configured such as the
|
||||
* {@code RequestMappingHandlerMapping}-{@code RequestMappingHandlerAdapter}
|
||||
* pair which are the default in the MVC Java config and the MVC namespace.
|
||||
*
|
||||
* @author Rossen Stoyanchev
|
||||
* @author Sam Brannen
|
||||
* @since 4.0
|
||||
*/
|
||||
@Target(ElementType.TYPE)
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Documented
|
||||
@Controller
|
||||
@ResponseBody
|
||||
public @interface RestController {
|
||||
|
||||
/**
|
||||
* The value may indicate a suggestion for a logical component name,
|
||||
* to be turned into a Spring bean in case of an autodetected component.
|
||||
* @return the suggested component name, if any (or empty String otherwise)
|
||||
* @since 4.0.1
|
||||
*/
|
||||
@AliasFor(annotation = Controller.class)
|
||||
String value() default "";
|
||||
|
||||
}
|
||||
@@ -0,0 +1,329 @@
|
||||
/*
|
||||
* Copyright 2002-2020 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.util;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.ObjectInputStream;
|
||||
import java.io.Serializable;
|
||||
import java.nio.charset.Charset;
|
||||
import java.util.BitSet;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.Iterator;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import java.util.TreeSet;
|
||||
|
||||
import org.springframework.lang.Nullable;
|
||||
|
||||
/**
|
||||
* Represents a MIME Type, as originally defined in RFC 2046 and subsequently
|
||||
* used in other Internet protocols including HTTP.
|
||||
*
|
||||
* <p>This class, however, does not contain support for the q-parameters used
|
||||
* in HTTP content negotiation. Those can be found in the subclass
|
||||
* {@code org.springframework.http.MediaType} in the {@code spring-web} module.
|
||||
*
|
||||
* <p>Consists of a {@linkplain #getType() type} and a {@linkplain #getSubtype() subtype}.
|
||||
* Also has functionality to parse MIME Type values from a {@code String} using
|
||||
* {@link #valueOf(String)}. For more parsing options see {@link MimeTypeUtils}.
|
||||
*
|
||||
* @author Arjen Poutsma
|
||||
* @author Juergen Hoeller
|
||||
* @author Rossen Stoyanchev
|
||||
* @author Sam Brannen
|
||||
* @since 4.0
|
||||
* @see MimeTypeUtils
|
||||
*/
|
||||
public class MimeType implements Comparable<MimeType>, Serializable {
|
||||
|
||||
protected static final String WILDCARD_TYPE = "";
|
||||
|
||||
/**
|
||||
* Create a new {@code MimeType} for the given primary type.
|
||||
* <p>The {@linkplain #getSubtype() subtype} is set to <code>"*"</code>,
|
||||
* and the parameters are empty.
|
||||
* @param type the primary type
|
||||
* @throws IllegalArgumentException if any of the parameters contains illegal characters
|
||||
*/
|
||||
public MimeType(String type) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new {@code MimeType} for the given primary type and subtype.
|
||||
* <p>The parameters are empty.
|
||||
* @param type the primary type
|
||||
* @param subtype the subtype
|
||||
* @throws IllegalArgumentException if any of the parameters contains illegal characters
|
||||
*/
|
||||
public MimeType(String type, String subtype) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new {@code MimeType} for the given type, subtype, and character set.
|
||||
* @param type the primary type
|
||||
* @param subtype the subtype
|
||||
* @param charset the character set
|
||||
* @throws IllegalArgumentException if any of the parameters contains illegal characters
|
||||
*/
|
||||
public MimeType(String type, String subtype, Charset charset) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Copy-constructor that copies the type, subtype, parameters of the given {@code MimeType},
|
||||
* and allows to set the specified character set.
|
||||
* @param other the other MimeType
|
||||
* @param charset the character set
|
||||
* @throws IllegalArgumentException if any of the parameters contains illegal characters
|
||||
* @since 4.3
|
||||
*/
|
||||
public MimeType(MimeType other, Charset charset) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Copy-constructor that copies the type and subtype of the given {@code MimeType},
|
||||
* and allows for different parameter.
|
||||
* @param other the other MimeType
|
||||
* @param parameters the parameters (may be {@code null})
|
||||
* @throws IllegalArgumentException if any of the parameters contains illegal characters
|
||||
*/
|
||||
public MimeType(MimeType other, @Nullable Map<String, String> parameters) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new {@code MimeType} for the given type, subtype, and parameters.
|
||||
* @param type the primary type
|
||||
* @param subtype the subtype
|
||||
* @param parameters the parameters (may be {@code null})
|
||||
* @throws IllegalArgumentException if any of the parameters contains illegal characters
|
||||
*/
|
||||
public MimeType(String type, String subtype, @Nullable Map<String, String> parameters) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Copy-constructor that copies the type, subtype and parameters of the given {@code MimeType},
|
||||
* skipping checks performed in other constructors.
|
||||
* @param other the other MimeType
|
||||
* @since 5.3
|
||||
*/
|
||||
protected MimeType(MimeType other) {
|
||||
}
|
||||
|
||||
protected void checkParameters(String parameter, String value) {
|
||||
}
|
||||
|
||||
protected String unquote(String s) {
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicates whether the {@linkplain #getType() type} is the wildcard character
|
||||
* <code>*</code> or not.
|
||||
*/
|
||||
public boolean isWildcardType() {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicates whether the {@linkplain #getSubtype() subtype} is the wildcard
|
||||
* character <code>*</code> or the wildcard character followed by a suffix
|
||||
* (e.g. <code>*+xml</code>).
|
||||
* @return whether the subtype is a wildcard
|
||||
*/
|
||||
public boolean isWildcardSubtype() {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicates whether this MIME Type is concrete, i.e. whether neither the type
|
||||
* nor the subtype is a wildcard character <code>*</code>.
|
||||
* @return whether this MIME Type is concrete
|
||||
*/
|
||||
public boolean isConcrete() {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the primary type.
|
||||
*/
|
||||
public String getType() {
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the subtype.
|
||||
*/
|
||||
public String getSubtype() {
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the subtype suffix as defined in RFC 6839.
|
||||
* @since 5.3
|
||||
*/
|
||||
@Nullable
|
||||
public String getSubtypeSuffix() {
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the character set, as indicated by a {@code charset} parameter, if any.
|
||||
* @return the character set, or {@code null} if not available
|
||||
* @since 4.3
|
||||
*/
|
||||
@Nullable
|
||||
public Charset getCharset() {
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a generic parameter value, given a parameter name.
|
||||
* @param name the parameter name
|
||||
* @return the parameter value, or {@code null} if not present
|
||||
*/
|
||||
@Nullable
|
||||
public String getParameter(String name) {
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return all generic parameter values.
|
||||
* @return a read-only map (possibly empty, never {@code null})
|
||||
*/
|
||||
public Map<String, String> getParameters() {
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicate whether this MIME Type includes the given MIME Type.
|
||||
* <p>For instance, {@code text/*} includes {@code text/plain} and {@code text/html},
|
||||
* and {@code application/*+xml} includes {@code application/soap+xml}, etc.
|
||||
* This method is <b>not</b> symmetric.
|
||||
* @param other the reference MIME Type with which to compare
|
||||
* @return {@code true} if this MIME Type includes the given MIME Type;
|
||||
* {@code false} otherwise
|
||||
*/
|
||||
public boolean includes(@Nullable MimeType other) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicate whether this MIME Type is compatible with the given MIME Type.
|
||||
* <p>For instance, {@code text/*} is compatible with {@code text/plain},
|
||||
* {@code text/html}, and vice versa. In effect, this method is similar to
|
||||
* {@link #includes}, except that it <b>is</b> symmetric.
|
||||
* @param other the reference MIME Type with which to compare
|
||||
* @return {@code true} if this MIME Type is compatible with the given MIME Type;
|
||||
* {@code false} otherwise
|
||||
*/
|
||||
public boolean isCompatibleWith(@Nullable MimeType other) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Similar to {@link #equals(Object)} but based on the type and subtype
|
||||
* only, i.e. ignoring parameters.
|
||||
* @param other the other mime type to compare to
|
||||
* @return whether the two mime types have the same type and subtype
|
||||
* @since 5.1.4
|
||||
*/
|
||||
public boolean equalsTypeAndSubtype(@Nullable MimeType other) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Unlike {@link Collection#contains(Object)} which relies on
|
||||
* {@link MimeType#equals(Object)}, this method only checks the type and the
|
||||
* subtype, but otherwise ignores parameters.
|
||||
* @param mimeTypes the list of mime types to perform the check against
|
||||
* @return whether the list contains the given mime type
|
||||
* @since 5.1.4
|
||||
*/
|
||||
public boolean isPresentIn(Collection<? extends MimeType> mimeTypes) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public boolean equals(@Nullable Object other) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if the parameters in this {@code MimeType} and the supplied
|
||||
* {@code MimeType} are equal, performing case-insensitive comparisons
|
||||
* for {@link Charset Charsets}.
|
||||
* @since 4.2
|
||||
*/
|
||||
private boolean parametersAreEqual(MimeType other) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return null;
|
||||
}
|
||||
|
||||
protected void appendTo(StringBuilder builder) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Compares this MIME Type to another alphabetically.
|
||||
* @param other the MIME Type to compare to
|
||||
* @see MimeTypeUtils#sortBySpecificity(List)
|
||||
*/
|
||||
@Override
|
||||
public int compareTo(MimeType other) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse the given String value into a {@code MimeType} object,
|
||||
* with this method name following the 'valueOf' naming convention
|
||||
* (as supported by {@link org.springframework.core.convert.ConversionService}.
|
||||
* @see MimeTypeUtils#parseMimeType(String)
|
||||
*/
|
||||
public static MimeType valueOf(String value) {
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Comparator to sort {@link MimeType MimeTypes} in order of specificity.
|
||||
*
|
||||
* @param <T> the type of mime types that may be compared by this comparator
|
||||
*/
|
||||
public static class SpecificityComparator<T extends MimeType> implements Comparator<T> {
|
||||
|
||||
@Override
|
||||
public int compare(T mimeType1, T mimeType2) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
protected int compareParameters(T mimeType1, T mimeType2) {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user