Merge branch 'main' into atorralba/promote-ognl-injection

This commit is contained in:
Tony Torralba
2021-08-02 14:06:45 +02:00
158 changed files with 4115 additions and 1840 deletions

View File

@@ -0,0 +1,3 @@
lgtm,codescanning
* The "Deserialization of user-controlled data" (`java/unsafe-deserialization`) query
now recognizes `Jackson` deserialization.

View File

@@ -0,0 +1,2 @@
lgtm,codescanning
* Added additional taint steps modeling constructors for collections in `java.util`.

View File

@@ -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,,,,,,,,
1 package sink source summary sink:bean-validation sink:create-file sink:header-splitting sink:information-leak sink:jexl sink:ldap sink:open-url sink:set-hostname-verifier sink:sql sink:url-open-stream sink:url-redirect sink:xpath sink:xss source:remote summary:taint summary:value
18 java.net 10 3 6 10 3 6
19 java.nio 10 2 10 2
20 java.sql 7 7
21 java.util 295 332 15 280 317
22 javax.json 123 100 23
23 javax.naming.directory 1 1
24 javax.net.ssl 2 2

View File

@@ -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

View File

@@ -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.",

View File

@@ -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,

View File

@@ -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()
)
}

View File

@@ -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".

View File

@@ -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()
)
}

View File

@@ -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`. */

View File

@@ -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")
}
/**

View File

@@ -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: Dont 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>

View File

@@ -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,

View File

@@ -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

View File

@@ -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>

View File

@@ -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)){

View File

@@ -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>

View File

@@ -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)
}
}

View File

@@ -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")
)
}

View File

@@ -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

View File

@@ -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)
)
)
}

View File

@@ -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)
)
)
}

View File

@@ -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" }

View File

@@ -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()
}
}

View File

@@ -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()
)
}
}

View File

@@ -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()
}

View File

@@ -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

View File

@@ -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

View File

@@ -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"
]
}
}

View File

@@ -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
)
}

View File

@@ -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() }
}

View 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()
)
}

View File

@@ -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()
)
}

View File

@@ -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 |

View File

@@ -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()
)
}
}

View File

@@ -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()
)
}
}

View File

@@ -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()) }
}

View File

@@ -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()
)
}
}

View File

@@ -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)

View File

@@ -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)
|

View File

@@ -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 |

View File

@@ -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);
}
}

View File

@@ -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, _)

View File

@@ -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
}
}
}

View File

@@ -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
}
}

View File

@@ -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

View File

@@ -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
}
}

View File

@@ -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
}
}

View File

@@ -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")

View 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);
});
}
}

View File

@@ -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();
}

View File

@@ -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 |

View File

@@ -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 = ""
)
}
}

View File

@@ -1 +0,0 @@
Security/CWE/CWE-502/UnsafeDeserialization.ql

View File

@@ -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

View File

@@ -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; }
}
}

View File

@@ -0,0 +1,3 @@
package com.fasterxml.jackson.core;
public abstract class JsonParser {}

View File

@@ -0,0 +1,3 @@
package com.fasterxml.jackson.core;
public interface TreeNode {}

View File

@@ -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 {}

View File

@@ -25,4 +25,8 @@ public class MappingIterator<T> implements Iterator<T>, Closeable {
public void close() throws IOException {
}
public List<T> readAll() {
return null;
}
}

View File

@@ -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; }
}

View File

@@ -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> {}
}

View File

@@ -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; }
}
}

View File

@@ -0,0 +1,3 @@
package com.fasterxml.jackson.databind.jsontype;
public abstract class PolymorphicTypeValidator {}

View File

@@ -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){}

View File

@@ -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 {};
}

View File

@@ -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">
* &#064;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 {};
}

View File

@@ -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 "";
}

View File

@@ -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>"&#42;"</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>&#42;</code> or not.
*/
public boolean isWildcardType() {
return false;
}
/**
* Indicates whether the {@linkplain #getSubtype() subtype} is the wildcard
* character <code>&#42;</code> or the wildcard character followed by a suffix
* (e.g. <code>&#42;+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>&#42;</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;
}
}
}