Address Micronaut modeling review feedback

This commit is contained in:
Nicolas Will
2026-07-28 21:03:58 +02:00
parent 3bf0c86ad9
commit 6ac1a72c61
30 changed files with 1303 additions and 1053 deletions

View File

@@ -0,0 +1,7 @@
extensions:
- addsTo:
pack: codeql/java-all
extensible: sourceModel
data:
- ["io.micronaut.context.annotation", "Property", True, "", "", "Annotated", "", "environment", "manual"]
- ["io.micronaut.context.annotation", "Value", True, "", "", "Annotated", "", "environment", "manual"]

View File

@@ -3,31 +3,33 @@ extensions:
pack: codeql/java-all
extensible: sinkModel
data:
# HttpClient.toBlocking() returns BlockingHttpClient; retrieve/exchange with String URL are SSRF sinks
- ["io.micronaut.http.client", "BlockingHttpClient", True, "retrieve", "(String)", "", "Argument[0]", "request-forgery", "manual"]
- ["io.micronaut.http.client", "BlockingHttpClient", True, "retrieve", "(String,Class)", "", "Argument[0]", "request-forgery", "manual"]
- ["io.micronaut.http.client", "BlockingHttpClient", True, "exchange", "(HttpRequest)", "", "Argument[0]", "request-forgery", "manual"]
- ["io.micronaut.http.client", "BlockingHttpClient", True, "exchange", "(HttpRequest,Argument)", "", "Argument[0]", "request-forgery", "manual"]
- ["io.micronaut.http.client", "BlockingHttpClient", True, "exchange", "(HttpRequest,Argument,Argument)", "", "Argument[0]", "request-forgery", "manual"]
- ["io.micronaut.http.client", "BlockingHttpClient", True, "exchange", "(HttpRequest,Class)", "", "Argument[0]", "request-forgery", "manual"]
- ["io.micronaut.http.client", "BlockingHttpClient", True, "exchange", "(String)", "", "Argument[0]", "request-forgery", "manual"]
- ["io.micronaut.http.client", "BlockingHttpClient", True, "exchange", "(String,Class)", "", "Argument[0]", "request-forgery", "manual"]
- ["io.micronaut.http.client", "BlockingHttpClient", True, "exchange", "(String,Class,Class)", "", "Argument[0]", "request-forgery", "manual"]
- ["io.micronaut.http.client", "BlockingHttpClient", True, "retrieve", "(HttpRequest)", "", "Argument[0]", "request-forgery", "manual"]
- ["io.micronaut.http.client", "BlockingHttpClient", True, "retrieve", "(HttpRequest,Argument)", "", "Argument[0]", "request-forgery", "manual"]
- ["io.micronaut.http.client", "BlockingHttpClient", True, "retrieve", "(HttpRequest,Argument,Argument)", "", "Argument[0]", "request-forgery", "manual"]
- ["io.micronaut.http.client", "BlockingHttpClient", True, "retrieve", "(HttpRequest,Class)", "", "Argument[0]", "request-forgery", "manual"]
- ["io.micronaut.http.client", "BlockingHttpClient", True, "retrieve", "(String)", "", "Argument[0]", "request-forgery", "manual"]
- ["io.micronaut.http.client", "BlockingHttpClient", True, "retrieve", "(String,Class)", "", "Argument[0]", "request-forgery", "manual"]
- ["io.micronaut.http.client", "BlockingHttpClient", True, "retrieve", "(String,Class,Class)", "", "Argument[0]", "request-forgery", "manual"]
- ["io.micronaut.http.client", "HttpClient", True, "exchange", "(HttpRequest)", "", "Argument[0]", "request-forgery", "manual"]
- ["io.micronaut.http.client", "HttpClient", True, "exchange", "(HttpRequest,Argument)", "", "Argument[0]", "request-forgery", "manual"]
- ["io.micronaut.http.client", "HttpClient", True, "exchange", "(HttpRequest,Argument,Argument)", "", "Argument[0]", "request-forgery", "manual"]
- ["io.micronaut.http.client", "HttpClient", True, "exchange", "(HttpRequest,Class)", "", "Argument[0]", "request-forgery", "manual"]
- ["io.micronaut.http.client", "HttpClient", True, "exchange", "(String)", "", "Argument[0]", "request-forgery", "manual"]
- ["io.micronaut.http.client", "HttpClient", True, "exchange", "(String,Class)", "", "Argument[0]", "request-forgery", "manual"]
- ["io.micronaut.http.client", "HttpClient", True, "retrieve", "(HttpRequest)", "", "Argument[0]", "request-forgery", "manual"]
- ["io.micronaut.http.client", "HttpClient", True, "retrieve", "(HttpRequest,Argument)", "", "Argument[0]", "request-forgery", "manual"]
- ["io.micronaut.http.client", "HttpClient", True, "retrieve", "(HttpRequest,Argument,Argument)", "", "Argument[0]", "request-forgery", "manual"]
- ["io.micronaut.http.client", "HttpClient", True, "retrieve", "(HttpRequest,Class)", "", "Argument[0]", "request-forgery", "manual"]
- ["io.micronaut.http.client", "HttpClient", True, "retrieve", "(String)", "", "Argument[0]", "request-forgery", "manual"]
- addsTo:
pack: codeql/java-all
extensible: summaryModel
data:
# HttpClient.toBlocking() taint propagation
- ["io.micronaut.http.client", "HttpClient", True, "toBlocking", "()", "", "Argument[this]", "ReturnValue", "taint", "manual"]
# HttpRequest.GET/POST/PUT/DELETE/PATCH factory methods propagate URI taint
- ["io.micronaut.http", "HttpRequest", True, "GET", "(String)", "", "Argument[0]", "ReturnValue", "taint", "manual"]
- ["io.micronaut.http", "HttpRequest", True, "POST", "(String,Object)", "", "Argument[0]", "ReturnValue", "taint", "manual"]
- ["io.micronaut.http", "HttpRequest", True, "PUT", "(String,Object)", "", "Argument[0]", "ReturnValue", "taint", "manual"]
- ["io.micronaut.http", "HttpRequest", True, "DELETE", "(String)", "", "Argument[0]", "ReturnValue", "taint", "manual"]
- ["io.micronaut.http", "HttpRequest", True, "PATCH", "(String,Object)", "", "Argument[0]", "ReturnValue", "taint", "manual"]
- ["io.micronaut.http", "HttpRequest", True, "HEAD", "(String)", "", "Argument[0]", "ReturnValue", "taint", "manual"]
- ["io.micronaut.http", "HttpRequest", True, "OPTIONS", "(String)", "", "Argument[0]", "ReturnValue", "taint", "manual"]
# UriBuilder taint propagation
- ["io.micronaut.http.uri", "UriBuilder", True, "of", "(CharSequence)", "", "Argument[0]", "ReturnValue", "taint", "manual"]
- ["io.micronaut.http.uri", "UriBuilder", True, "of", "(URI)", "", "Argument[0]", "ReturnValue", "taint", "manual"]
- ["io.micronaut.http.uri", "UriBuilder", True, "host", "(String)", "", "Argument[0]", "ReturnValue", "taint", "manual"]
- ["io.micronaut.http.uri", "UriBuilder", True, "path", "(String)", "", "Argument[0]", "ReturnValue", "taint", "manual"]
- ["io.micronaut.http.uri", "UriBuilder", True, "queryParam", "(String,Object[])", "", "Argument[0]", "ReturnValue", "taint", "manual"]
- ["io.micronaut.http.uri", "UriBuilder", True, "queryParam", "(String,Object[])", "", "Argument[1].ArrayElement", "ReturnValue", "taint", "manual"]
- ["io.micronaut.http.uri", "UriBuilder", True, "fragment", "(String)", "", "Argument[0]", "ReturnValue", "taint", "manual"]
- ["io.micronaut.http.uri", "UriBuilder", True, "build", "()", "", "Argument[this]", "ReturnValue", "taint", "manual"]

View File

@@ -4,35 +4,56 @@ extensions:
extensible: sourceModel
data:
- ["io.micronaut.http", "HttpRequest", True, "getBody", "", "", "ReturnValue", "remote", "manual"]
- ["io.micronaut.http", "HttpRequest", True, "getHeaders", "", "", "ReturnValue.Element.MapValue.Element", "remote", "manual"]
- ["io.micronaut.http", "HttpRequest", True, "getParameters", "", "", "ReturnValue.Element.MapValue.Element.Element.MapValue", "remote", "manual"]
- ["io.micronaut.http", "HttpRequest", True, "getCookies", "", "", "ReturnValue", "remote", "manual"]
- ["io.micronaut.http", "HttpRequest", True, "getUri", "", "", "ReturnValue", "remote", "manual"]
- ["io.micronaut.http", "HttpRequest", True, "getPath", "", "", "ReturnValue", "remote", "manual"]
- ["io.micronaut.http", "HttpRequest", True, "getContentType", "", "", "ReturnValue", "remote", "manual"]
- ["io.micronaut.http", "HttpRequest", True, "getContentLength", "", "", "ReturnValue", "remote", "manual"]
- ["io.micronaut.http", "HttpRequest", True, "getContentType", "", "", "ReturnValue", "remote", "manual"]
- ["io.micronaut.http", "HttpRequest", True, "getCookies", "", "", "ReturnValue", "remote", "manual"]
- ["io.micronaut.http", "HttpRequest", True, "getHeaders", "", "", "ReturnValue", "remote", "manual"]
- ["io.micronaut.http", "HttpRequest", True, "getMethodName", "", "", "ReturnValue", "remote", "manual"]
- ["io.micronaut.http", "HttpRequest", True, "getParameters", "", "", "ReturnValue", "remote", "manual"]
- ["io.micronaut.http", "HttpRequest", True, "getPath", "", "", "ReturnValue", "remote", "manual"]
- ["io.micronaut.http", "HttpRequest", True, "getUri", "", "", "ReturnValue", "remote", "manual"]
- addsTo:
pack: codeql/java-all
extensible: summaryModel
data:
- ["io.micronaut.http", "HttpHeaders", True, "get", "", "", "Argument[this]", "ReturnValue", "taint", "manual"]
- ["io.micronaut.http", "HttpHeaders", True, "getAll", "", "", "Argument[this]", "ReturnValue", "taint", "manual"]
- ["io.micronaut.http", "HttpHeaders", True, "getFirst", "", "", "Argument[this]", "ReturnValue", "taint", "manual"]
- ["io.micronaut.http", "HttpHeaders", True, "values", "", "", "Argument[this]", "ReturnValue", "taint", "manual"]
- ["io.micronaut.http", "HttpParameters", True, "get", "", "", "Argument[this]", "ReturnValue", "taint", "manual"]
- ["io.micronaut.http", "HttpParameters", True, "getAll", "", "", "Argument[this]", "ReturnValue", "taint", "manual"]
- ["io.micronaut.http", "HttpParameters", True, "getFirst", "", "", "Argument[this]", "ReturnValue", "taint", "manual"]
- ["io.micronaut.core.convert.value", "ConvertibleMultiValues", True, "get", "", "", "Argument[this].Element.MapValue", "ReturnValue", "taint", "manual"]
- ["io.micronaut.core.convert.value", "ConvertibleMultiValues", True, "getAll", "", "", "Argument[this].Element.MapValue", "ReturnValue", "value", "manual"]
- ["io.micronaut.core.convert.value", "ConvertibleMultiValues", True, "getFirst", "", "", "Argument[this].Element.MapValue", "ReturnValue", "taint", "manual"]
- ["io.micronaut.core.convert.value", "ConvertibleValues", True, "asMap", "", "", "Argument[this].Element.MapValue", "ReturnValue.MapValue", "taint", "manual"]
- ["io.micronaut.core.convert.value", "ConvertibleValues", True, "asProperties", "", "", "Argument[this].Element.MapValue", "ReturnValue.MapValue", "taint", "manual"]
- ["io.micronaut.core.convert.value", "ConvertibleValues", True, "getValue", "", "", "Argument[this].Element.MapValue", "ReturnValue", "taint", "manual"]
- ["io.micronaut.core.convert.value", "ConvertibleValues", True, "subMap", "", "", "Argument[this].Element.MapValue", "ReturnValue.MapValue", "taint", "manual"]
- ["io.micronaut.core.convert.value", "ConvertibleValues", True, "values", "", "", "Argument[this].Element.MapValue", "ReturnValue.Element", "taint", "manual"]
- ["io.micronaut.http", "HttpRequest", True, "DELETE", "(String)", "", "Argument[0]", "ReturnValue", "taint", "manual"]
- ["io.micronaut.http", "HttpRequest", True, "DELETE", "(String,Object)", "", "Argument[0]", "ReturnValue", "taint", "manual"]
- ["io.micronaut.http", "HttpRequest", True, "DELETE", "(URI)", "", "Argument[0]", "ReturnValue", "taint", "manual"]
- ["io.micronaut.http", "HttpRequest", True, "DELETE", "(URI,Object)", "", "Argument[0]", "ReturnValue", "taint", "manual"]
- ["io.micronaut.http", "HttpRequest", True, "GET", "(String)", "", "Argument[0]", "ReturnValue", "taint", "manual"]
- ["io.micronaut.http", "HttpRequest", True, "GET", "(URI)", "", "Argument[0]", "ReturnValue", "taint", "manual"]
- ["io.micronaut.http", "HttpRequest", True, "HEAD", "(String)", "", "Argument[0]", "ReturnValue", "taint", "manual"]
- ["io.micronaut.http", "HttpRequest", True, "HEAD", "(URI)", "", "Argument[0]", "ReturnValue", "taint", "manual"]
- ["io.micronaut.http", "HttpRequest", True, "OPTIONS", "(String)", "", "Argument[0]", "ReturnValue", "taint", "manual"]
- ["io.micronaut.http", "HttpRequest", True, "OPTIONS", "(URI)", "", "Argument[0]", "ReturnValue", "taint", "manual"]
- ["io.micronaut.http", "HttpRequest", True, "PATCH", "(String,Object)", "", "Argument[0]", "ReturnValue", "taint", "manual"]
- ["io.micronaut.http", "HttpRequest", True, "PATCH", "(URI,Object)", "", "Argument[0]", "ReturnValue", "taint", "manual"]
- ["io.micronaut.http", "HttpRequest", True, "POST", "(String,Object)", "", "Argument[0]", "ReturnValue", "taint", "manual"]
- ["io.micronaut.http", "HttpRequest", True, "POST", "(URI,Object)", "", "Argument[0]", "ReturnValue", "taint", "manual"]
- ["io.micronaut.http", "HttpRequest", True, "PUT", "(String,Object)", "", "Argument[0]", "ReturnValue", "taint", "manual"]
- ["io.micronaut.http", "HttpRequest", True, "PUT", "(URI,Object)", "", "Argument[0]", "ReturnValue", "taint", "manual"]
- ["io.micronaut.http.cookie", "Cookie", True, "getDomain", "", "", "Argument[this]", "ReturnValue", "taint", "manual"]
- ["io.micronaut.http.cookie", "Cookie", True, "getName", "", "", "Argument[this]", "ReturnValue", "taint", "manual"]
- ["io.micronaut.http.cookie", "Cookie", True, "getPath", "", "", "Argument[this]", "ReturnValue", "taint", "manual"]
- ["io.micronaut.http.cookie", "Cookie", True, "getValue", "", "", "Argument[this]", "ReturnValue", "taint", "manual"]
- ["io.micronaut.http.cookie", "Cookies", True, "findCookie", "", "", "Argument[this]", "ReturnValue", "taint", "manual"]
- ["io.micronaut.http.cookie", "Cookies", True, "get", "", "", "Argument[this]", "ReturnValue", "taint", "manual"]
- ["io.micronaut.http.cookie", "Cookies", True, "getAll", "", "", "Argument[this]", "ReturnValue", "taint", "manual"]
- ["io.micronaut.http.cookie", "Cookies", True, "findCookie", "", "", "Argument[this]", "ReturnValue", "taint", "manual"]
- ["io.micronaut.http.cookie", "Cookie", True, "getValue", "", "", "Argument[this]", "ReturnValue", "taint", "manual"]
- ["io.micronaut.http.cookie", "Cookie", True, "getName", "", "", "Argument[this]", "ReturnValue", "taint", "manual"]
- ["io.micronaut.http.cookie", "Cookie", True, "getDomain", "", "", "Argument[this]", "ReturnValue", "taint", "manual"]
- ["io.micronaut.http.cookie", "Cookie", True, "getPath", "", "", "Argument[this]", "ReturnValue", "taint", "manual"]
- addsTo:
pack: codeql/java-all
extensible: sinkModel
data:
- ["io.micronaut.http", "MutableHttpResponse", True, "header", "(CharSequence,CharSequence)", "", "Argument[1]", "response-splitting", "manual"]
- ["io.micronaut.http", "HttpResponse", True, "permanentRedirect", "(URI)", "", "Argument[0]", "url-redirection", "manual"]
- ["io.micronaut.http", "HttpResponse", True, "redirect", "(URI)", "", "Argument[0]", "url-redirection", "manual"]
- ["io.micronaut.http", "HttpResponse", True, "seeOther", "(URI)", "", "Argument[0]", "url-redirection", "manual"]
- ["io.micronaut.http", "HttpResponse", True, "temporaryRedirect", "(URI)", "", "Argument[0]", "url-redirection", "manual"]
- ["io.micronaut.http", "MutableHttpResponse", True, "header", "(CharSequence,CharSequence)", "", "Argument[1]", "response-splitting", "manual"]
- ["io.micronaut.http", "MutableHttpResponse", True, "headers", "(Map)", "", "Argument[0]", "response-splitting", "manual"]

View File

@@ -0,0 +1,28 @@
extensions:
- addsTo:
pack: codeql/java-all
extensible: summaryModel
data:
- ["io.micronaut.http.uri", "UriBuilder", True, "build", "()", "", "Argument[this]", "ReturnValue", "taint", "manual"]
- ["io.micronaut.http.uri", "UriBuilder", True, "expand", "(Map)", "", "Argument[0].MapValue", "ReturnValue", "taint", "manual"]
- ["io.micronaut.http.uri", "UriBuilder", True, "expand", "(Map)", "", "Argument[this]", "ReturnValue", "taint", "manual"]
- ["io.micronaut.http.uri", "UriBuilder", True, "fragment", "(String)", "", "Argument[0]", "Argument[this]", "taint", "manual"]
- ["io.micronaut.http.uri", "UriBuilder", True, "fragment", "(String)", "", "Argument[this]", "ReturnValue", "value", "manual"]
- ["io.micronaut.http.uri", "UriBuilder", True, "host", "(String)", "", "Argument[0]", "Argument[this]", "taint", "manual"]
- ["io.micronaut.http.uri", "UriBuilder", True, "host", "(String)", "", "Argument[this]", "ReturnValue", "value", "manual"]
- ["io.micronaut.http.uri", "UriBuilder", True, "of", "(CharSequence)", "", "Argument[0]", "ReturnValue", "taint", "manual"]
- ["io.micronaut.http.uri", "UriBuilder", True, "of", "(URI)", "", "Argument[0]", "ReturnValue", "taint", "manual"]
- ["io.micronaut.http.uri", "UriBuilder", True, "path", "(String)", "", "Argument[0]", "Argument[this]", "taint", "manual"]
- ["io.micronaut.http.uri", "UriBuilder", True, "path", "(String)", "", "Argument[this]", "ReturnValue", "value", "manual"]
- ["io.micronaut.http.uri", "UriBuilder", True, "queryParam", "(String,Object[])", "", "Argument[0]", "Argument[this]", "taint", "manual"]
- ["io.micronaut.http.uri", "UriBuilder", True, "queryParam", "(String,Object[])", "", "Argument[1].ArrayElement", "Argument[this]", "taint", "manual"]
- ["io.micronaut.http.uri", "UriBuilder", True, "queryParam", "(String,Object[])", "", "Argument[this]", "ReturnValue", "value", "manual"]
- ["io.micronaut.http.uri", "UriBuilder", True, "replacePath", "(String)", "", "Argument[0]", "Argument[this]", "taint", "manual"]
- ["io.micronaut.http.uri", "UriBuilder", True, "replacePath", "(String)", "", "Argument[this]", "ReturnValue", "value", "manual"]
- ["io.micronaut.http.uri", "UriBuilder", True, "replaceQueryParam", "(String,Object[])", "", "Argument[0]", "Argument[this]", "taint", "manual"]
- ["io.micronaut.http.uri", "UriBuilder", True, "replaceQueryParam", "(String,Object[])", "", "Argument[1].ArrayElement", "Argument[this]", "taint", "manual"]
- ["io.micronaut.http.uri", "UriBuilder", True, "replaceQueryParam", "(String,Object[])", "", "Argument[this]", "ReturnValue", "value", "manual"]
- ["io.micronaut.http.uri", "UriBuilder", True, "scheme", "(String)", "", "Argument[0]", "Argument[this]", "taint", "manual"]
- ["io.micronaut.http.uri", "UriBuilder", True, "scheme", "(String)", "", "Argument[this]", "ReturnValue", "value", "manual"]
- ["io.micronaut.http.uri", "UriBuilder", True, "userInfo", "(String)", "", "Argument[0]", "Argument[this]", "taint", "manual"]
- ["io.micronaut.http.uri", "UriBuilder", True, "userInfo", "(String)", "", "Argument[this]", "ReturnValue", "value", "manual"]

View File

@@ -32,7 +32,6 @@ import semmle.code.java.frameworks.Thrift
import semmle.code.java.frameworks.javaee.jsf.JSFRenderer
import semmle.code.java.frameworks.micronaut.MicronautController
import semmle.code.java.frameworks.micronaut.MicronautWebSocket
import semmle.code.java.frameworks.micronaut.MicronautConfig
private import semmle.code.java.dataflow.ExternalFlow
private import codeql.threatmodels.ThreatModels
@@ -204,16 +203,6 @@ private class MicronautWebSocketParameterSource extends RemoteFlowSource {
override string getSourceType() { result = "Micronaut WebSocket parameter" }
}
private class MicronautConfigSource extends LocalUserInput {
MicronautConfigSource() {
this.asExpr() = any(MicronautConfigField f).getAnAccess()
or
this.asParameter() instanceof MicronautConfigParameter
}
override string getThreatModel() { result = "environment" }
}
private class MicronautErrorHandlerSource extends RemoteFlowSource {
MicronautErrorHandlerSource() {
this.asParameter() = any(MicronautErrorHandler h).getARemoteParameter()

View File

@@ -1,41 +0,0 @@
/** Provides classes for identifying Micronaut configuration injection sources. */
overlay[local?]
module;
import java
/** The annotation type `@Value` from `io.micronaut.context.annotation`. */
class MicronautValueAnnotation extends AnnotationType {
MicronautValueAnnotation() { this.hasQualifiedName("io.micronaut.context.annotation", "Value") }
}
/** The annotation type `@Property` from `io.micronaut.context.annotation`. */
class MicronautPropertyAnnotation extends AnnotationType {
MicronautPropertyAnnotation() {
this.hasQualifiedName("io.micronaut.context.annotation", "Property")
}
}
/**
* A field annotated with Micronaut's `@Value` or `@Property` annotation,
* representing an injected configuration value.
*/
class MicronautConfigField extends Field {
MicronautConfigField() {
this.getAnAnnotation().getType() instanceof MicronautValueAnnotation
or
this.getAnAnnotation().getType() instanceof MicronautPropertyAnnotation
}
}
/**
* A parameter annotated with Micronaut's `@Value` or `@Property` annotation,
* representing an injected configuration value.
*/
class MicronautConfigParameter extends Parameter {
MicronautConfigParameter() {
this.getAnAnnotation().getType() instanceof MicronautValueAnnotation
or
this.getAnAnnotation().getType() instanceof MicronautPropertyAnnotation
}
}

View File

@@ -59,6 +59,25 @@ class MicronautHttpInputAnnotation extends Annotation {
}
}
/** Holds if `parameter` is explicitly bound to remote HTTP request data. */
private predicate isExplicitlyTaintedInput(Parameter parameter) {
parameter.getAnAnnotation() instanceof MicronautHttpInputAnnotation
or
parameter.getAnAnnotation().getType() instanceof MicronautRequestBeanAnnotation
or
parameter
.getType()
.(RefType)
.getASourceSupertype*()
.hasQualifiedName("io.micronaut.http", ["HttpRequest", "HttpHeaders", "HttpParameters"])
or
parameter
.getType()
.(RefType)
.getASourceSupertype*()
.hasQualifiedName("io.micronaut.http.cookie", "Cookies")
}
/** A parameter of a `MicronautRequestMappingMethod`. */
class MicronautRequestMappingParameter extends Parameter {
MicronautRequestMappingParameter() { this.getCallable() instanceof MicronautRequestMappingMethod }
@@ -67,11 +86,6 @@ class MicronautRequestMappingParameter extends Parameter {
predicate isNotDirectlyTaintedInput() {
this.getType().(RefType).getAnAncestor().hasQualifiedName("io.micronaut.http", "HttpResponse")
or
this.getType()
.(RefType)
.getAnAncestor()
.hasQualifiedName("io.micronaut.http", "MutableHttpResponse")
or
this.getType().(RefType).getAnAncestor().hasQualifiedName("java.security", "Principal")
or
this.getType().(RefType).getAnAncestor().hasQualifiedName("java.util", "Locale")
@@ -80,35 +94,19 @@ class MicronautRequestMappingParameter extends Parameter {
or
this.getType().(RefType).getAnAncestor().hasQualifiedName("java.time", "ZoneId")
or
this.getType().(RefType).getAnAncestor() instanceof TypeInputStream
or
this.getType().(RefType).getAnAncestor().hasQualifiedName("java.io", "Reader")
or
// @Value/@Property parameters are configuration injection, not HTTP input
this.getAnAnnotation()
.getType()
.hasQualifiedName("io.micronaut.context.annotation", ["Value", "Property"])
}
private predicate isExplicitlyTaintedInput() {
// The MicronautHttpInputAnnotation allows access to the URI path,
// request parameters, cookie values, headers, and the body of the request.
this.getAnAnnotation() instanceof MicronautHttpInputAnnotation
or
// A @RequestBean parameter binds multiple request attributes into a POJO
this.getAnAnnotation().getType() instanceof MicronautRequestBeanAnnotation
or
// An HttpRequest parameter provides access to request data
this.getType()
.(RefType)
.getASourceSupertype*()
.hasQualifiedName("io.micronaut.http", "HttpRequest")
or
// InputStream or Reader parameters allow access to the body of a request
this.getType().(RefType).getAnAncestor() instanceof TypeInputStream
or
this.getType().(RefType).getAnAncestor().hasQualifiedName("java.io", "Reader")
}
/** Holds if the input is tainted (i.e. comes from user-controlled input). */
predicate isTaintedInput() {
this.isExplicitlyTaintedInput()
isExplicitlyTaintedInput(this)
or
not this.isNotDirectlyTaintedInput()
}
@@ -126,11 +124,7 @@ class MicronautErrorHandler extends Method {
/** Gets a parameter that carries user-controlled request data. */
Parameter getARemoteParameter() {
result = this.getAParameter() and
result
.getType()
.(RefType)
.getASourceSupertype*()
.hasQualifiedName("io.micronaut.http", "HttpRequest")
isExplicitlyTaintedInput(result)
}
}

View File

@@ -26,7 +26,7 @@ class MicronautServerWebSocketClass extends Class {
class MicronautWebSocketHandlerAnnotation extends AnnotationType {
MicronautWebSocketHandlerAnnotation() {
this.getPackage().hasName("io.micronaut.websocket.annotation") and
this.hasName(["OnMessage", "OnOpen"])
this.hasName(["OnClose", "OnMessage", "OnOpen"])
}
}
@@ -44,10 +44,10 @@ class MicronautWebSocketMessageHandler extends Method {
class MicronautWebSocketParameter extends Parameter {
MicronautWebSocketParameter() {
this.getCallable() instanceof MicronautWebSocketMessageHandler and
// Exclude WebSocketSession parameters
// Exclude framework-provided WebSocket lifecycle parameters
not this.getType()
.(RefType)
.getAnAncestor()
.hasQualifiedName("io.micronaut.websocket", "WebSocketSession")
.hasQualifiedName("io.micronaut.websocket", ["CloseReason", "WebSocketSession"])
}
}

View File

@@ -1,30 +0,0 @@
import io.micronaut.context.annotation.Value;
import io.micronaut.context.annotation.Property;
import io.micronaut.http.annotation.*;
@Controller("/config")
class MicronautConfigTest {
void sink(Object o) {}
@Value("${app.secret}")
String secretValue;
@Property(name = "app.api-key")
String apiKey;
@Get("/secret")
void testValueField() {
sink(secretValue); // $hasTaintFlow
}
@Get("/key")
void testPropertyField() {
sink(apiKey); // $hasTaintFlow
}
@Get("/param")
void testValueParam(@Value("${app.name}") String appName) {
sink(appName); // $hasTaintFlow
}
}

View File

@@ -1,5 +1,6 @@
import io.micronaut.http.annotation.*;
import io.micronaut.http.*;
import io.micronaut.http.cookie.Cookies;
import java.io.InputStream;
import java.io.Reader;
@@ -58,14 +59,29 @@ class MicronautControllerTest {
sink(request); // $hasTaintFlow
}
@Get("/headers")
void testHttpHeaders(HttpHeaders headers) {
sink(headers); // $hasTaintFlow
}
@Get("/parameters")
void testHttpParameters(HttpParameters parameters) {
sink(parameters); // $hasTaintFlow
}
@Get("/cookies")
void testCookies(Cookies cookies) {
sink(cookies); // $hasTaintFlow
}
@Post("/stream")
void testInputStream(InputStream stream) {
void testInputStream(@Body InputStream stream) {
sink(stream); // $hasTaintFlow
}
@Post("/reader")
void testReader(Reader reader) {
sink(reader); // $hasTaintFlow
sink(reader);
}
@Post("/post")
@@ -92,4 +108,17 @@ class MicronautControllerTest {
void testErrorHandler(HttpRequest<?> request) {
sink(request); // $hasTaintFlow
}
@io.micronaut.http.annotation.Error
void testAnnotatedErrorHandler(@Header String header, @RequestBean Object bean) {
sink(header); // $hasTaintFlow
sink(bean); // $hasTaintFlow
}
@io.micronaut.http.annotation.Error
void testErrorHandlerCarriers(HttpHeaders headers, HttpParameters parameters, Cookies cookies) {
sink(headers); // $hasTaintFlow
sink(parameters); // $hasTaintFlow
sink(cookies); // $hasTaintFlow
}
}

View File

@@ -2,7 +2,9 @@ import io.micronaut.http.*;
import io.micronaut.http.client.HttpClient;
import io.micronaut.http.client.BlockingHttpClient;
import io.micronaut.http.uri.UriBuilder;
import io.micronaut.core.type.Argument;
import java.net.URI;
import java.util.Collections;
class MicronautHttpClientTest {
@@ -34,6 +36,39 @@ class MicronautHttpClientTest {
client.toBlocking().exchange(url, String.class); // $hasTaintFlow
}
void testRetrieveStringClassClass() {
client.toBlocking().retrieve(taint(), String.class, Object.class); // $hasTaintFlow
}
void testExchangeStringClassClass() {
client.toBlocking().exchange(taint(), String.class, Object.class); // $hasTaintFlow
}
void testBlockingRequest() {
client.toBlocking().retrieve(HttpRequest.GET(taint())); // $hasTaintFlow
client.toBlocking().retrieve(HttpRequest.GET(taint()), String.class); // $hasTaintFlow
client.toBlocking().retrieve(HttpRequest.GET(taint()), new Argument<String>()); // $hasTaintFlow
client.toBlocking().retrieve(HttpRequest.GET(taint()), new Argument<String>(), new Argument<Object>()); // $hasTaintFlow
client.toBlocking().exchange(HttpRequest.GET(taint())); // $hasTaintFlow
client.toBlocking().exchange(HttpRequest.GET(taint()), String.class); // $hasTaintFlow
client.toBlocking().exchange(HttpRequest.GET(taint()), new Argument<String>()); // $hasTaintFlow
client.toBlocking().exchange(HttpRequest.GET(taint()), new Argument<String>(), new Argument<Object>()); // $hasTaintFlow
}
void testNonBlockingClient() {
client.retrieve(taint()); // $hasTaintFlow
client.exchange(taint()); // $hasTaintFlow
client.exchange(taint(), String.class); // $hasTaintFlow
client.retrieve(HttpRequest.GET(taint())); // $hasTaintFlow
client.retrieve(HttpRequest.GET(taint()), String.class); // $hasTaintFlow
client.retrieve(HttpRequest.GET(taint()), new Argument<String>()); // $hasTaintFlow
client.retrieve(HttpRequest.GET(taint()), new Argument<String>(), new Argument<Object>()); // $hasTaintFlow
client.exchange(HttpRequest.GET(taint())); // $hasTaintFlow
client.exchange(HttpRequest.GET(taint()), String.class); // $hasTaintFlow
client.exchange(HttpRequest.GET(taint()), new Argument<String>()); // $hasTaintFlow
client.exchange(HttpRequest.GET(taint()), new Argument<String>(), new Argument<Object>()); // $hasTaintFlow
}
void testGetFactory() {
HttpRequest<?> req = HttpRequest.GET(taint());
sink(req); // $hasTaintFlow
@@ -69,6 +104,21 @@ class MicronautHttpClientTest {
sink(req); // $hasTaintFlow
}
void testUriFactories() {
sink(HttpRequest.GET(URI.create(taint()))); // $hasTaintFlow
sink(HttpRequest.POST(URI.create(taint()), "body")); // $hasTaintFlow
sink(HttpRequest.PUT(URI.create(taint()), "body")); // $hasTaintFlow
sink(HttpRequest.DELETE(URI.create(taint()))); // $hasTaintFlow
sink(HttpRequest.DELETE(URI.create(taint()), "body")); // $hasTaintFlow
sink(HttpRequest.PATCH(URI.create(taint()), "body")); // $hasTaintFlow
sink(HttpRequest.HEAD(URI.create(taint()))); // $hasTaintFlow
sink(HttpRequest.OPTIONS(URI.create(taint()))); // $hasTaintFlow
}
void testDeleteFactoryWithBody() {
sink(HttpRequest.DELETE(taint(), "body")); // $hasTaintFlow
}
void testUriBuilderOfCharSequence() {
URI uri = UriBuilder.of(taint()).build();
sink(uri); // $hasTaintFlow
@@ -103,4 +153,32 @@ class MicronautHttpClientTest {
URI uri = UriBuilder.of("http://example.com").fragment(taint()).build();
sink(uri); // $hasTaintFlow
}
void testUriBuilderMutation() {
UriBuilder builder = UriBuilder.of("http://example.com");
builder.host(taint());
sink(builder.build()); // $hasTaintFlow
}
void testUriBuilderReplaceQueryParam() {
URI uri = UriBuilder.of("http://example.com").replaceQueryParam("key", taint()).build();
sink(uri); // $hasTaintFlow
}
void testUriBuilderAdditionalFluentMethods() {
sink(UriBuilder.of("http://example.com").scheme(taint()).build()); // $hasTaintFlow
sink(UriBuilder.of("http://example.com").userInfo(taint()).build()); // $hasTaintFlow
sink(UriBuilder.of("http://example.com").replacePath(taint()).build()); // $hasTaintFlow
}
void testUriBuilderExpandBuilder() {
URI uri = UriBuilder.of(taint()).expand(Collections.singletonMap("id", "value"));
sink(uri); // $hasTaintFlow
}
void testUriBuilderExpandValue() {
URI uri = UriBuilder.of("http://example.com/{id}")
.expand(Collections.singletonMap("id", taint()));
sink(uri); // $hasTaintFlow
}
}

View File

@@ -1,6 +1,8 @@
import io.micronaut.http.annotation.*;
import io.micronaut.http.*;
import io.micronaut.http.cookie.*;
import java.util.List;
import java.util.Map;
@Controller("/http")
class MicronautHttpRequestTest {
@@ -11,17 +13,27 @@ class MicronautHttpRequestTest {
void testHeaders(HttpRequest<?> request) {
sink(request.getHeaders()); // $hasTaintFlow
sink(request.getHeaders().get("X-Custom")); // $hasTaintFlow
sink(request.getHeaders().getAll("X-Custom")); // $hasTaintFlow
sink(request.getHeaders().getFirst("X-Custom")); // $hasTaintFlow
sink(request.getHeaders().values()); // $hasTaintFlow
sink(request.getHeaders().getAll("X-Custom").get(0)); // $hasTaintFlow
sink(request.getHeaders().getFirst("X-Custom").get()); // $hasTaintFlow
sink(request.getHeaders().getValue("X-Custom").get(0)); // $hasTaintFlow
sink(request.getHeaders().values().iterator().next().get(0)); // $hasTaintFlow
sink(request.getHeaders().asMap().get("X-Custom").get(0)); // $hasTaintFlow
sink(request.getHeaders().asProperties().get("X-Custom")); // $hasTaintFlow
sink(request.getHeaders().subMap("X", List.class).get("Custom").get(0)); // $hasTaintFlow
for (Map.Entry<String, List<String>> header : request.getHeaders()) {
sink(header.getValue().get(0)); // $hasTaintFlow
}
}
@Get("/params")
void testParameters(HttpRequest<?> request) {
sink(request.getParameters()); // $hasTaintFlow
sink(request.getParameters().get("q")); // $hasTaintFlow
sink(request.getParameters().getAll("q")); // $hasTaintFlow
sink(request.getParameters().getFirst("q")); // $hasTaintFlow
sink(request.getParameters().getAll("q").get(0)); // $hasTaintFlow
sink(request.getParameters().getFirst("q").get()); // $hasTaintFlow
sink(request.getParameters().getValue("q").get(0)); // $hasTaintFlow
sink(request.getParameters().values().iterator().next().get(0)); // $hasTaintFlow
sink(request.getParameters().asMap().get("q").get(0)); // $hasTaintFlow
}
@Get("/cookies")

View File

@@ -1,4 +1,5 @@
import io.micronaut.websocket.annotation.*;
import io.micronaut.websocket.CloseReason;
import io.micronaut.websocket.WebSocketSession;
@ServerWebSocket("/chat/{room}")
@@ -15,4 +16,11 @@ class MicronautWebSocketTest {
void onOpen(String room, WebSocketSession session) {
sink(room); // $hasTaintFlow
}
@OnClose
void onClose(String room, CloseReason reason, WebSocketSession session) {
sink(room); // $hasTaintFlow
sink(reason);
sink(session);
}
}

View File

@@ -3,6 +3,7 @@ import io.micronaut.http.annotation.Get;
import io.micronaut.http.annotation.QueryValue;
import io.micronaut.http.HttpResponse;
import io.micronaut.http.MutableHttpResponse;
import java.util.Collections;
@Controller("/headers")
public class MicronautResponseSplitting {
@@ -19,4 +20,10 @@ public class MicronautResponseSplitting {
String sanitized = headerValue.replace('\n', ' ').replace('\r', ' ');
return HttpResponse.ok().header("X-Custom", sanitized);
}
@Get("/bad-map")
public MutableHttpResponse<?> badMap(@QueryValue String headerValue) {
// BAD: user-controlled header value
return HttpResponse.ok().headers(Collections.singletonMap("X-Custom", headerValue));
}
}

View File

@@ -1,25 +1,33 @@
#select
| MicronautResponseSplitting.java:13:53:13:63 | headerValue | MicronautResponseSplitting.java:11:39:11:68 | headerValue : String | MicronautResponseSplitting.java:13:53:13:63 | headerValue | This header depends on a $@, which may cause a response-splitting vulnerability. | MicronautResponseSplitting.java:11:39:11:68 | headerValue | user-provided value |
| MicronautResponseSplitting.java:14:53:14:63 | headerValue | MicronautResponseSplitting.java:12:39:12:68 | headerValue : String | MicronautResponseSplitting.java:14:53:14:63 | headerValue | This header depends on a $@, which may cause a response-splitting vulnerability. | MicronautResponseSplitting.java:12:39:12:68 | headerValue | user-provided value |
| MicronautResponseSplitting.java:27:42:27:90 | singletonMap(...) | MicronautResponseSplitting.java:25:42:25:71 | headerValue : String | MicronautResponseSplitting.java:27:42:27:90 | singletonMap(...) | This header depends on a $@, which may cause a response-splitting vulnerability. | MicronautResponseSplitting.java:25:42:25:71 | headerValue | user-provided value |
| ResponseSplitting.java:23:23:23:28 | cookie | ResponseSplitting.java:22:39:22:66 | getParameter(...) : String | ResponseSplitting.java:23:23:23:28 | cookie | This header depends on a $@, which may cause a response-splitting vulnerability. | ResponseSplitting.java:22:39:22:66 | getParameter(...) | user-provided value |
| ResponseSplitting.java:28:38:28:72 | getParameter(...) | ResponseSplitting.java:28:38:28:72 | getParameter(...) | ResponseSplitting.java:28:38:28:72 | getParameter(...) | This header depends on a $@, which may cause a response-splitting vulnerability. | ResponseSplitting.java:28:38:28:72 | getParameter(...) | user-provided value |
| ResponseSplitting.java:29:38:29:72 | getParameter(...) | ResponseSplitting.java:29:38:29:72 | getParameter(...) | ResponseSplitting.java:29:38:29:72 | getParameter(...) | This header depends on a $@, which may cause a response-splitting vulnerability. | ResponseSplitting.java:29:38:29:72 | getParameter(...) | user-provided value |
| ResponseSplitting.java:59:27:59:57 | replaceFirst(...) | ResponseSplitting.java:53:14:53:48 | getParameter(...) : String | ResponseSplitting.java:59:27:59:57 | replaceFirst(...) | This header depends on a $@, which may cause a response-splitting vulnerability. | ResponseSplitting.java:53:14:53:48 | getParameter(...) | user-provided value |
edges
| MicronautResponseSplitting.java:11:39:11:68 | headerValue : String | MicronautResponseSplitting.java:13:53:13:63 | headerValue | provenance | Sink:MaD:1 |
| ResponseSplitting.java:22:20:22:67 | new Cookie(...) : Cookie | ResponseSplitting.java:23:23:23:28 | cookie | provenance | Sink:MaD:2 |
| ResponseSplitting.java:22:39:22:66 | getParameter(...) : String | ResponseSplitting.java:22:20:22:67 | new Cookie(...) : Cookie | provenance | Src:MaD:4 MaD:6 |
| ResponseSplitting.java:53:14:53:48 | getParameter(...) : String | ResponseSplitting.java:59:27:59:27 | t : String | provenance | Src:MaD:4 |
| ResponseSplitting.java:59:27:59:27 | t : String | ResponseSplitting.java:59:27:59:57 | replaceFirst(...) | provenance | MaD:5 Sink:MaD:3 |
| MicronautResponseSplitting.java:12:39:12:68 | headerValue : String | MicronautResponseSplitting.java:14:53:14:63 | headerValue | provenance | Sink:MaD:1 |
| MicronautResponseSplitting.java:25:42:25:71 | headerValue : String | MicronautResponseSplitting.java:27:79:27:89 | headerValue : String | provenance | |
| MicronautResponseSplitting.java:27:79:27:89 | headerValue : String | MicronautResponseSplitting.java:27:42:27:90 | singletonMap(...) | provenance | MaD:7 Sink:MaD:2 |
| ResponseSplitting.java:22:20:22:67 | new Cookie(...) : Cookie | ResponseSplitting.java:23:23:23:28 | cookie | provenance | Sink:MaD:3 |
| ResponseSplitting.java:22:39:22:66 | getParameter(...) : String | ResponseSplitting.java:22:20:22:67 | new Cookie(...) : Cookie | provenance | Src:MaD:5 MaD:8 |
| ResponseSplitting.java:53:14:53:48 | getParameter(...) : String | ResponseSplitting.java:59:27:59:27 | t : String | provenance | Src:MaD:5 |
| ResponseSplitting.java:59:27:59:27 | t : String | ResponseSplitting.java:59:27:59:57 | replaceFirst(...) | provenance | MaD:6 Sink:MaD:4 |
models
| 1 | Sink: io.micronaut.http; MutableHttpResponse; true; header; (CharSequence,CharSequence); ; Argument[1]; response-splitting; manual |
| 2 | Sink: javax.servlet.http; HttpServletResponse; false; addCookie; ; ; Argument[0]; response-splitting; manual |
| 3 | Sink: javax.servlet.http; HttpServletResponse; false; setHeader; ; ; Argument[0..1]; response-splitting; manual |
| 4 | Source: javax.servlet; ServletRequest; false; getParameter; (String); ; ReturnValue; remote; manual |
| 5 | Summary: java.lang; String; false; replaceFirst; ; ; Argument[this]; ReturnValue; taint; manual |
| 6 | Summary: javax.servlet.http; Cookie; false; Cookie; ; ; Argument[1]; Argument[this]; taint; manual |
| 2 | Sink: io.micronaut.http; MutableHttpResponse; true; headers; (Map); ; Argument[0]; response-splitting; manual |
| 3 | Sink: javax.servlet.http; HttpServletResponse; false; addCookie; ; ; Argument[0]; response-splitting; manual |
| 4 | Sink: javax.servlet.http; HttpServletResponse; false; setHeader; ; ; Argument[0..1]; response-splitting; manual |
| 5 | Source: javax.servlet; ServletRequest; false; getParameter; (String); ; ReturnValue; remote; manual |
| 6 | Summary: java.lang; String; false; replaceFirst; ; ; Argument[this]; ReturnValue; taint; manual |
| 7 | Summary: java.util; Collections; false; singletonMap; (Object,Object); ; Argument[1]; ReturnValue.MapValue; value; manual |
| 8 | Summary: javax.servlet.http; Cookie; false; Cookie; ; ; Argument[1]; Argument[this]; taint; manual |
nodes
| MicronautResponseSplitting.java:11:39:11:68 | headerValue : String | semmle.label | headerValue : String |
| MicronautResponseSplitting.java:13:53:13:63 | headerValue | semmle.label | headerValue |
| MicronautResponseSplitting.java:12:39:12:68 | headerValue : String | semmle.label | headerValue : String |
| MicronautResponseSplitting.java:14:53:14:63 | headerValue | semmle.label | headerValue |
| MicronautResponseSplitting.java:25:42:25:71 | headerValue : String | semmle.label | headerValue : String |
| MicronautResponseSplitting.java:27:42:27:90 | singletonMap(...) | semmle.label | singletonMap(...) |
| MicronautResponseSplitting.java:27:79:27:89 | headerValue : String | semmle.label | headerValue : String |
| ResponseSplitting.java:22:20:22:67 | new Cookie(...) : Cookie | semmle.label | new Cookie(...) : Cookie |
| ResponseSplitting.java:22:39:22:66 | getParameter(...) : String | semmle.label | getParameter(...) : String |
| ResponseSplitting.java:23:23:23:28 | cookie | semmle.label | cookie |

View File

@@ -22,4 +22,19 @@ public class MicronautUrlRedirect {
}
return HttpResponse.ok();
}
@Get("/permanent")
public MutableHttpResponse<?> permanent(@QueryValue String target) {
return HttpResponse.permanentRedirect(URI.create(target));
}
@Get("/see-other")
public MutableHttpResponse<?> seeOther(@QueryValue String target) {
return HttpResponse.seeOther(URI.create(target));
}
@Get("/temporary")
public MutableHttpResponse<?> temporary(@QueryValue String target) {
return HttpResponse.temporaryRedirect(URI.create(target));
}
}

View File

@@ -1,5 +1,8 @@
#select
| MicronautUrlRedirect.java:14:38:14:55 | create(...) | MicronautUrlRedirect.java:12:39:12:63 | target : String | MicronautUrlRedirect.java:14:38:14:55 | create(...) | Untrusted URL redirection depends on a $@. | MicronautUrlRedirect.java:12:39:12:63 | target | user-provided value |
| MicronautUrlRedirect.java:28:47:28:64 | create(...) | MicronautUrlRedirect.java:27:45:27:69 | target : String | MicronautUrlRedirect.java:28:47:28:64 | create(...) | Untrusted URL redirection depends on a $@. | MicronautUrlRedirect.java:27:45:27:69 | target | user-provided value |
| MicronautUrlRedirect.java:33:38:33:55 | create(...) | MicronautUrlRedirect.java:32:44:32:68 | target : String | MicronautUrlRedirect.java:33:38:33:55 | create(...) | Untrusted URL redirection depends on a $@. | MicronautUrlRedirect.java:32:44:32:68 | target | user-provided value |
| MicronautUrlRedirect.java:38:47:38:64 | create(...) | MicronautUrlRedirect.java:37:45:37:69 | target : String | MicronautUrlRedirect.java:38:47:38:64 | create(...) | Untrusted URL redirection depends on a $@. | MicronautUrlRedirect.java:37:45:37:69 | target | user-provided value |
| UrlRedirect2.java:27:25:27:54 | getParameter(...) | UrlRedirect2.java:27:25:27:54 | getParameter(...) | UrlRedirect2.java:27:25:27:54 | getParameter(...) | Untrusted URL redirection depends on a $@. | UrlRedirect2.java:27:25:27:54 | getParameter(...) | user-provided value |
| UrlRedirect.java:23:25:23:54 | getParameter(...) | UrlRedirect.java:23:25:23:54 | getParameter(...) | UrlRedirect.java:23:25:23:54 | getParameter(...) | Untrusted URL redirection depends on a $@. | UrlRedirect.java:23:25:23:54 | getParameter(...) | user-provided value |
| UrlRedirect.java:32:25:32:67 | weakCleanup(...) | UrlRedirect.java:32:37:32:66 | getParameter(...) : String | UrlRedirect.java:32:25:32:67 | weakCleanup(...) | Untrusted URL redirection depends on a $@. | UrlRedirect.java:32:37:32:66 | getParameter(...) | user-provided value |
@@ -8,23 +11,41 @@
| mad/Test.java:14:22:14:38 | (...)... | mad/Test.java:9:16:9:41 | getParameter(...) : String | mad/Test.java:14:22:14:38 | (...)... | Untrusted URL redirection depends on a $@. | mad/Test.java:9:16:9:41 | getParameter(...) | user-provided value |
edges
| MicronautUrlRedirect.java:12:39:12:63 | target : String | MicronautUrlRedirect.java:14:49:14:54 | target : String | provenance | |
| MicronautUrlRedirect.java:14:49:14:54 | target : String | MicronautUrlRedirect.java:14:38:14:55 | create(...) | provenance | MaD:5 Sink:MaD:1 |
| UrlRedirect.java:32:37:32:66 | getParameter(...) : String | UrlRedirect.java:32:25:32:67 | weakCleanup(...) | provenance | Src:MaD:3 MaD:4 |
| UrlRedirect.java:32:37:32:66 | getParameter(...) : String | UrlRedirect.java:45:28:45:39 | input : String | provenance | Src:MaD:3 |
| MicronautUrlRedirect.java:14:49:14:54 | target : String | MicronautUrlRedirect.java:14:38:14:55 | create(...) | provenance | MaD:8 Sink:MaD:2 |
| MicronautUrlRedirect.java:27:45:27:69 | target : String | MicronautUrlRedirect.java:28:58:28:63 | target : String | provenance | |
| MicronautUrlRedirect.java:28:58:28:63 | target : String | MicronautUrlRedirect.java:28:47:28:64 | create(...) | provenance | MaD:8 Sink:MaD:1 |
| MicronautUrlRedirect.java:32:44:32:68 | target : String | MicronautUrlRedirect.java:33:49:33:54 | target : String | provenance | |
| MicronautUrlRedirect.java:33:49:33:54 | target : String | MicronautUrlRedirect.java:33:38:33:55 | create(...) | provenance | MaD:8 Sink:MaD:3 |
| MicronautUrlRedirect.java:37:45:37:69 | target : String | MicronautUrlRedirect.java:38:58:38:63 | target : String | provenance | |
| MicronautUrlRedirect.java:38:58:38:63 | target : String | MicronautUrlRedirect.java:38:47:38:64 | create(...) | provenance | MaD:8 Sink:MaD:4 |
| UrlRedirect.java:32:37:32:66 | getParameter(...) : String | UrlRedirect.java:32:25:32:67 | weakCleanup(...) | provenance | Src:MaD:6 MaD:7 |
| UrlRedirect.java:32:37:32:66 | getParameter(...) : String | UrlRedirect.java:45:28:45:39 | input : String | provenance | Src:MaD:6 |
| UrlRedirect.java:45:28:45:39 | input : String | UrlRedirect.java:46:10:46:14 | input : String | provenance | |
| UrlRedirect.java:46:10:46:14 | input : String | UrlRedirect.java:46:10:46:40 | replaceAll(...) : String | provenance | MaD:4 |
| mad/Test.java:9:16:9:41 | getParameter(...) : String | mad/Test.java:14:31:14:38 | source(...) : String | provenance | Src:MaD:3 |
| mad/Test.java:14:31:14:38 | source(...) : String | mad/Test.java:14:22:14:38 | (...)... | provenance | Sink:MaD:2 |
| UrlRedirect.java:46:10:46:14 | input : String | UrlRedirect.java:46:10:46:40 | replaceAll(...) : String | provenance | MaD:7 |
| mad/Test.java:9:16:9:41 | getParameter(...) : String | mad/Test.java:14:31:14:38 | source(...) : String | provenance | Src:MaD:6 |
| mad/Test.java:14:31:14:38 | source(...) : String | mad/Test.java:14:22:14:38 | (...)... | provenance | Sink:MaD:5 |
models
| 1 | Sink: io.micronaut.http; HttpResponse; true; redirect; (URI); ; Argument[0]; url-redirection; manual |
| 2 | Sink: org.kohsuke.stapler; HttpResponses; true; redirectTo; (String); ; Argument[0]; url-redirection; ai-manual |
| 3 | Source: javax.servlet; ServletRequest; false; getParameter; (String); ; ReturnValue; remote; manual |
| 4 | Summary: java.lang; String; false; replaceAll; ; ; Argument[this]; ReturnValue; taint; manual |
| 5 | Summary: java.net; URI; false; create; ; ; Argument[0]; ReturnValue; taint; manual |
| 1 | Sink: io.micronaut.http; HttpResponse; true; permanentRedirect; (URI); ; Argument[0]; url-redirection; manual |
| 2 | Sink: io.micronaut.http; HttpResponse; true; redirect; (URI); ; Argument[0]; url-redirection; manual |
| 3 | Sink: io.micronaut.http; HttpResponse; true; seeOther; (URI); ; Argument[0]; url-redirection; manual |
| 4 | Sink: io.micronaut.http; HttpResponse; true; temporaryRedirect; (URI); ; Argument[0]; url-redirection; manual |
| 5 | Sink: org.kohsuke.stapler; HttpResponses; true; redirectTo; (String); ; Argument[0]; url-redirection; ai-manual |
| 6 | Source: javax.servlet; ServletRequest; false; getParameter; (String); ; ReturnValue; remote; manual |
| 7 | Summary: java.lang; String; false; replaceAll; ; ; Argument[this]; ReturnValue; taint; manual |
| 8 | Summary: java.net; URI; false; create; ; ; Argument[0]; ReturnValue; taint; manual |
nodes
| MicronautUrlRedirect.java:12:39:12:63 | target : String | semmle.label | target : String |
| MicronautUrlRedirect.java:14:38:14:55 | create(...) | semmle.label | create(...) |
| MicronautUrlRedirect.java:14:49:14:54 | target : String | semmle.label | target : String |
| MicronautUrlRedirect.java:27:45:27:69 | target : String | semmle.label | target : String |
| MicronautUrlRedirect.java:28:47:28:64 | create(...) | semmle.label | create(...) |
| MicronautUrlRedirect.java:28:58:28:63 | target : String | semmle.label | target : String |
| MicronautUrlRedirect.java:32:44:32:68 | target : String | semmle.label | target : String |
| MicronautUrlRedirect.java:33:38:33:55 | create(...) | semmle.label | create(...) |
| MicronautUrlRedirect.java:33:49:33:54 | target : String | semmle.label | target : String |
| MicronautUrlRedirect.java:37:45:37:69 | target : String | semmle.label | target : String |
| MicronautUrlRedirect.java:38:47:38:64 | create(...) | semmle.label | create(...) |
| MicronautUrlRedirect.java:38:58:38:63 | target : String | semmle.label | target : String |
| UrlRedirect2.java:27:25:27:54 | getParameter(...) | semmle.label | getParameter(...) |
| UrlRedirect.java:23:25:23:54 | getParameter(...) | semmle.label | getParameter(...) |
| UrlRedirect.java:32:25:32:67 | weakCleanup(...) | semmle.label | weakCleanup(...) |

View File

@@ -2,6 +2,7 @@ import io.micronaut.http.annotation.Controller;
import io.micronaut.http.annotation.Get;
import io.micronaut.http.annotation.QueryValue;
import io.micronaut.http.client.HttpClient;
import io.micronaut.http.HttpRequest;
import io.micronaut.http.uri.UriBuilder;
import java.net.URI;
@@ -30,4 +31,19 @@ public class MicronautSSRF {
URI uri = UriBuilder.of("http://example.com").host(host).build();
return client.toBlocking().retrieve(uri.toString()); // $ Alert
}
@Get("/blocking-request")
public Object testBlockingRequest(@QueryValue String url) { // $ Source
return client.toBlocking().exchange(HttpRequest.GET(url)); // $ Alert
}
@Get("/non-blocking")
public Object testNonBlocking(@QueryValue String url) { // $ Source
return client.exchange(url); // $ Alert
}
@Get("/non-blocking-request")
public Object testNonBlockingRequest(@QueryValue String url) { // $ Source
return client.exchange(HttpRequest.GET(url)); // $ Alert
}
}

View File

@@ -0,0 +1,10 @@
package io.micronaut.core.convert.value;
import java.util.List;
import java.util.Optional;
public interface ConvertibleMultiValues<V> extends ConvertibleValues<List<V>> {
V get(CharSequence name);
List<V> getAll(CharSequence name);
Optional<V> getFirst(CharSequence name);
}

View File

@@ -0,0 +1,15 @@
package io.micronaut.core.convert.value;
import java.util.Collection;
import java.util.Iterator;
import java.util.Map;
import java.util.Properties;
public interface ConvertibleValues<V> extends Iterable<Map.Entry<String, V>> {
Map<String, V> asMap();
Properties asProperties();
V getValue(CharSequence name);
Iterator<Map.Entry<String, V>> iterator();
Map<String, V> subMap(String prefix, Class<?> valueType);
Collection<V> values();
}

View File

@@ -0,0 +1,4 @@
package io.micronaut.core.type;
public class Argument<T> {
}

View File

@@ -1,11 +1,6 @@
package io.micronaut.http;
import java.util.List;
import java.util.Optional;
import io.micronaut.core.convert.value.ConvertibleMultiValues;
public interface HttpHeaders {
String get(CharSequence name);
List<String> getAll(CharSequence name);
Optional<String> getFirst(CharSequence name);
java.util.Collection<List<String>> values();
public interface HttpHeaders extends ConvertibleMultiValues<String> {
}

View File

@@ -1,10 +1,6 @@
package io.micronaut.http;
import java.util.List;
import java.util.Optional;
import io.micronaut.core.convert.value.ConvertibleMultiValues;
public interface HttpParameters {
String get(CharSequence name);
List<String> getAll(CharSequence name);
Optional<String> getFirst(CharSequence name);
public interface HttpParameters extends ConvertibleMultiValues<String> {
}

View File

@@ -17,10 +17,19 @@ public interface HttpRequest<B> {
String getMethodName();
static <T> HttpRequest<T> GET(String uri) { return null; }
static <T> HttpRequest<T> GET(URI uri) { return null; }
static <T> HttpRequest<T> POST(String uri, T body) { return null; }
static <T> HttpRequest<T> POST(URI uri, T body) { return null; }
static <T> HttpRequest<T> PUT(String uri, T body) { return null; }
static <T> HttpRequest<T> PUT(URI uri, T body) { return null; }
static HttpRequest<?> DELETE(String uri) { return null; }
static <T> HttpRequest<T> DELETE(String uri, T body) { return null; }
static HttpRequest<?> DELETE(URI uri) { return null; }
static <T> HttpRequest<T> DELETE(URI uri, T body) { return null; }
static <T> HttpRequest<T> PATCH(String uri, T body) { return null; }
static <T> HttpRequest<T> PATCH(URI uri, T body) { return null; }
static HttpRequest<?> HEAD(String uri) { return null; }
static HttpRequest<?> HEAD(URI uri) { return null; }
static HttpRequest<?> OPTIONS(String uri) { return null; }
static HttpRequest<?> OPTIONS(URI uri) { return null; }
}

View File

@@ -12,6 +12,9 @@ public interface HttpResponse<B> {
static <T> MutableHttpResponse<T> ok() { return null; }
static <T> MutableHttpResponse<T> ok(T body) { return null; }
static MutableHttpResponse<?> redirect(URI location) { return null; }
static MutableHttpResponse<?> permanentRedirect(URI location) { return null; }
static MutableHttpResponse<?> seeOther(URI location) { return null; }
static MutableHttpResponse<?> temporaryRedirect(URI location) { return null; }
static MutableHttpResponse<?> notFound() { return null; }
static MutableHttpResponse<?> badRequest() { return null; }
static MutableHttpResponse<?> serverError() { return null; }

View File

@@ -2,12 +2,21 @@ package io.micronaut.http.client;
import io.micronaut.http.HttpRequest;
import io.micronaut.http.HttpResponse;
import io.micronaut.core.type.Argument;
public interface BlockingHttpClient {
<I, O> HttpResponse<O> exchange(HttpRequest<I> request, Class<O> bodyType);
<I, O> HttpResponse<O> exchange(HttpRequest<I> request, Argument<O> bodyType);
<I, O, E> HttpResponse<O> exchange(HttpRequest<I> request, Argument<O> bodyType, Argument<E> errorType);
<I, O> HttpResponse<O> exchange(HttpRequest<I> request);
<I> String retrieve(HttpRequest<I> request);
<I, O> O retrieve(HttpRequest<I> request, Class<O> bodyType);
<I, O> O retrieve(HttpRequest<I> request, Argument<O> bodyType);
<I, O, E> O retrieve(HttpRequest<I> request, Argument<O> bodyType, Argument<E> errorType);
String retrieve(String uri);
<O> O retrieve(String uri, Class<O> bodyType);
<O, E> O retrieve(String uri, Class<O> bodyType, Class<E> errorType);
String exchange(String uri);
<O> HttpResponse<O> exchange(String uri, Class<O> bodyType);
<O, E> HttpResponse<O> exchange(String uri, Class<O> bodyType, Class<E> errorType);
}

View File

@@ -1,5 +1,19 @@
package io.micronaut.http.client;
import io.micronaut.http.HttpRequest;
import io.micronaut.core.type.Argument;
public interface HttpClient {
BlockingHttpClient toBlocking();
Object exchange(HttpRequest<?> request);
<O> Object exchange(HttpRequest<?> request, Class<O> bodyType);
<O> Object exchange(HttpRequest<?> request, Argument<O> bodyType);
<O, E> Object exchange(HttpRequest<?> request, Argument<O> bodyType, Argument<E> errorType);
Object exchange(String uri);
<O> Object exchange(String uri, Class<O> bodyType);
Object retrieve(HttpRequest<?> request);
<O> Object retrieve(HttpRequest<?> request, Class<O> bodyType);
<O> Object retrieve(HttpRequest<?> request, Argument<O> bodyType);
<O, E> Object retrieve(HttpRequest<?> request, Argument<O> bodyType, Argument<E> errorType);
Object retrieve(String uri);
}

View File

@@ -1,6 +1,7 @@
package io.micronaut.http.uri;
import java.net.URI;
import java.util.Map;
public interface UriBuilder {
static UriBuilder of(CharSequence uri) { return null; }
@@ -8,6 +9,11 @@ public interface UriBuilder {
UriBuilder host(String host);
UriBuilder path(String path);
UriBuilder queryParam(String name, Object... values);
UriBuilder replacePath(String path);
UriBuilder replaceQueryParam(String name, Object... values);
UriBuilder scheme(String scheme);
UriBuilder userInfo(String userInfo);
UriBuilder fragment(String fragment);
URI build();
URI expand(Map<String, ? super Object> values);
}

View File

@@ -0,0 +1,4 @@
package io.micronaut.websocket;
public class CloseReason {
}