Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions java/ql/lib/change-notes/2026-02-27-micronaut.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
---
category: minorAnalysis
---
* Added modeling for the Micronaut framework, including HTTP controllers, WebSocket endpoints, configuration injection, data access, security annotations, and HTTP client sinks.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Shipwright · LOW

The change note claims modeling for 'data access' and 'security annotations', but the added MicronautData.qll and MicronautSecurity.qll only define annotation/class recognition wit

Impact: The change note claims modeling for 'data access' and 'security annotations', but the added MicronautData.qll and MicronautSecurity.qll only define annotation/class recognition with no dataflow integration or query behavior. A maintainer reading the change note will expect functional modeling that does not exist yet.

Suggested fix: Review the cited evidence, fix the risk if confirmed, and rerun Shipwright.

7 changes: 7 additions & 0 deletions java/ql/lib/ext/io.micronaut.context.annotation.model.yml
Original file line number Diff line number Diff line change
@@ -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"]
13 changes: 13 additions & 0 deletions java/ql/lib/ext/io.micronaut.core.convert.value.model.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
extensions:
- addsTo:
pack: codeql/java-all
extensible: summaryModel
data:
- ["io.micronaut.core.convert.value", "ConvertibleMultiValues", True, "get", "", "", "Argument[this].MapValue", "ReturnValue", "taint", "manual"]
- ["io.micronaut.core.convert.value", "ConvertibleMultiValues", True, "getAll", "", "", "Argument[this].MapValue", "ReturnValue.Element", "value", "manual"]
- ["io.micronaut.core.convert.value", "ConvertibleMultiValues", True, "getFirst", "", "", "Argument[this].MapValue", "ReturnValue.Element", "taint", "manual"]
- ["io.micronaut.core.convert.value", "ConvertibleValues", True, "asMap", "", "", "Argument[this]", "ReturnValue", "taint", "manual"]
- ["io.micronaut.core.convert.value", "ConvertibleValues", True, "asProperties", "", "", "Argument[this]", "ReturnValue", "taint", "manual"]
- ["io.micronaut.core.convert.value", "ConvertibleValues", True, "getValue", "", "", "Argument[this].MapValue", "ReturnValue", "taint", "manual"]
- ["io.micronaut.core.convert.value", "ConvertibleValues", True, "subMap", "", "", "Argument[this].MapValue", "ReturnValue.MapValue", "taint", "manual"]
- ["io.micronaut.core.convert.value", "ConvertibleValues", True, "values", "", "", "Argument[this].MapValue", "ReturnValue.Element", "taint", "manual"]
35 changes: 35 additions & 0 deletions java/ql/lib/ext/io.micronaut.http.client.model.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
extensions:
- addsTo:
pack: codeql/java-all
extensible: sinkModel
data:
- ["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"]

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Shipwright · HIGH

The HTTP client sink model marks only Argument[0] of exchange/retrieve as request-forgery.

Impact: The HTTP client sink model marks only Argument[0] of exchange/retrieve as request-forgery. For overloads taking a String URI, the URI is the request target and should be modeled, but the sink kind request-forgery on a String argument may not match the expected taint source for URL manipulation queries, potentially missing SSRF-like flows through user-controlled URI strings.

Suggested fix: Review the cited evidence, fix the risk if confirmed, and rerun Shipwright.

- ["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:
- ["io.micronaut.http.client", "HttpClient", True, "toBlocking", "()", "", "Argument[this]", "ReturnValue", "taint", "manual"]
12 changes: 12 additions & 0 deletions java/ql/lib/ext/io.micronaut.http.cookie.model.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
extensions:
- addsTo:
pack: codeql/java-all
extensible: summaryModel
data:
- ["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"]
44 changes: 44 additions & 0 deletions java/ql/lib/ext/io.micronaut.http.model.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
extensions:
- addsTo:
pack: codeql/java-all
extensible: sourceModel
data:
- ["io.micronaut.http", "HttpRequest", True, "getBody", "", "", "ReturnValue", "remote", "manual"]

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Shipwright · HIGH

The source model marks HttpRequest.getContentLength() and getMethodName() as remote taint sources.

Impact: The source model marks HttpRequest.getContentLength() and getMethodName() as remote taint sources. These return a long and a String derived from request metadata, not attacker-controlled content. Treating them as tainted will create false flows into numeric and string sinks and dilute query precision.

Suggested fix: Review the cited evidence, fix the risk if confirmed, and rerun Shipwright.

- ["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", "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"]
- addsTo:
pack: codeql/java-all
extensible: sinkModel
data:
- ["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"]
10 changes: 10 additions & 0 deletions java/ql/lib/ext/io.micronaut.http.multipart.model.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
extensions:
- addsTo:
pack: codeql/java-all
extensible: sourceModel
data:
- ["io.micronaut.http.multipart", "CompletedFileUpload", True, "getBytes", "", "", "ReturnValue", "remote", "manual"]
- ["io.micronaut.http.multipart", "CompletedFileUpload", True, "getInputStream", "", "", "ReturnValue", "remote", "manual"]
- ["io.micronaut.http.multipart", "CompletedFileUpload", True, "getFilename", "", "", "ReturnValue", "remote", "manual"]
- ["io.micronaut.http.multipart", "CompletedFileUpload", True, "getContentType", "", "", "ReturnValue", "remote", "manual"]
- ["io.micronaut.http.multipart", "CompletedFileUpload", True, "getSize", "", "", "ReturnValue", "remote", "manual"]
28 changes: 28 additions & 0 deletions java/ql/lib/ext/io.micronaut.http.uri.model.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
extensions:
- addsTo:
pack: codeql/java-all
extensible: summaryModel
data:

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Shipwright · HIGH

The summary model for UriBuilder.expand(Map) propagates taint from Argument[0].MapValue to ReturnValue, but the stub and real Micronaut API use Map<String, ?

Impact: The summary model for UriBuilder.expand(Map) propagates taint from Argument[0].MapValue to ReturnValue, but the stub and real Micronaut API use Map<String, ? super Object>. If the CodeQL model does not resolve MapValue for wildcard-typed maps, the taint propagation will silently fail, causing missed flows through URI template expansion.

Suggested fix: Review the cited evidence, fix the risk if confirmed, and rerun Shipwright.

- ["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"]
24 changes: 24 additions & 0 deletions java/ql/lib/semmle/code/java/dataflow/FlowSources.qll
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,8 @@ import semmle.code.java.frameworks.Guice
import semmle.code.java.frameworks.struts.StrutsActions
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
private import semmle.code.java.dataflow.ExternalFlow
private import codeql.threatmodels.ThreatModels

Expand Down Expand Up @@ -179,6 +181,28 @@ private class AndroidExternalStorageSource extends RemoteFlowSource {
override string getSourceType() { result = "Android external storage" }
}

private class MicronautHttpInputParameterSource extends RemoteFlowSource {
MicronautHttpInputParameterSource() {
this.asParameter() = any(MicronautRequestMappingParameter mrmp | mrmp.isTaintedInput())
}

override string getSourceType() { result = "Micronaut HTTP input parameter" }
}

private class MicronautWebSocketParameterSource extends RemoteFlowSource {
MicronautWebSocketParameterSource() { this.asParameter() instanceof MicronautWebSocketParameter }

override string getSourceType() { result = "Micronaut WebSocket parameter" }
}

private class MicronautErrorHandlerSource extends RemoteFlowSource {
MicronautErrorHandlerSource() {
this.asParameter() = any(MicronautErrorHandler h).getARemoteParameter()
}

override string getSourceType() { result = "Micronaut error handler parameter" }
}

/** Class for `tainted` user input. */
abstract class UserInput extends SourceNode { }

Expand Down
Loading