Correct Micronaut cookie and map coverage

This commit is contained in:
Nicolas Will
2026-07-29 02:57:23 +02:00
parent 6ac1a72c61
commit 26b37ad6bc
5 changed files with 54 additions and 14 deletions

View File

@@ -1,6 +1,7 @@
import io.micronaut.http.annotation.*;
import io.micronaut.http.*;
import io.micronaut.http.cookie.Cookies;
import io.micronaut.http.cookie.Cookie;
import java.io.InputStream;
import java.io.Reader;
@@ -72,6 +73,10 @@ class MicronautControllerTest {
@Get("/cookies")
void testCookies(Cookies cookies) {
sink(cookies); // $hasTaintFlow
sink(cookies.getValue("session")); // $hasTaintFlow
for (java.util.Map.Entry<String, Cookie> entry : cookies) {
sink(entry.getValue()); // $hasTaintFlow
}
}
@Post("/stream")

View File

@@ -47,6 +47,12 @@ class MicronautHttpRequestTest {
sink(cookie.getPath()); // $hasTaintFlow
sink(request.getCookies().getAll()); // $hasTaintFlow
sink(request.getCookies().findCookie("session")); // $hasTaintFlow
sink(request.getCookies().getValue("session")); // $hasTaintFlow
sink(request.getCookies().values().iterator().next()); // $hasTaintFlow
sink(request.getCookies().asMap().get("session")); // $hasTaintFlow
for (Map.Entry<String, Cookie> entry : request.getCookies()) {
sink(entry.getValue()); // $hasTaintFlow
}
}
@Get("/uri")

View File

@@ -4,6 +4,8 @@ import io.micronaut.http.annotation.QueryValue;
import io.micronaut.http.HttpResponse;
import io.micronaut.http.MutableHttpResponse;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
@Controller("/headers")
public class MicronautResponseSplitting {
@@ -26,4 +28,22 @@ public class MicronautResponseSplitting {
// BAD: user-controlled header value
return HttpResponse.ok().headers(Collections.singletonMap("X-Custom", headerValue));
}
@Get("/bad-map-mutation")
public MutableHttpResponse<?> badMapMutation(@QueryValue String headerValue) {
Map<CharSequence, CharSequence> headers = new HashMap<>();
headers.put("X-Custom", headerValue);
return HttpResponse.ok().headers(headers);
}
@Get("/good-map")
public MutableHttpResponse<?> goodMap() {
return HttpResponse.ok().headers(Collections.singletonMap("X-Custom", "safe"));
}
@Get("/good-map-sanitized")
public MutableHttpResponse<?> goodMapSanitized(@QueryValue String headerValue) {
String sanitized = headerValue.replace('\n', ' ').replace('\r', ' ');
return HttpResponse.ok().headers(Collections.singletonMap("X-Custom", sanitized));
}
}

View File

@@ -1,16 +1,20 @@
#select
| 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 |
| MicronautResponseSplitting.java:16:53:16:63 | headerValue | MicronautResponseSplitting.java:14:39:14:68 | headerValue : String | MicronautResponseSplitting.java:16:53:16:63 | headerValue | This header depends on a $@, which may cause a response-splitting vulnerability. | MicronautResponseSplitting.java:14:39:14:68 | headerValue | user-provided value |
| MicronautResponseSplitting.java:29:42:29:90 | singletonMap(...) | MicronautResponseSplitting.java:27:42:27:71 | headerValue : String | MicronautResponseSplitting.java:29:42:29:90 | singletonMap(...) | This header depends on a $@, which may cause a response-splitting vulnerability. | MicronautResponseSplitting.java:27:42:27:71 | headerValue | user-provided value |
| MicronautResponseSplitting.java:36:42:36:48 | headers | MicronautResponseSplitting.java:33:50:33:79 | headerValue : String | MicronautResponseSplitting.java:36:42:36:48 | headers | This header depends on a $@, which may cause a response-splitting vulnerability. | MicronautResponseSplitting.java:33:50:33:79 | 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: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 |
| MicronautResponseSplitting.java:14:39:14:68 | headerValue : String | MicronautResponseSplitting.java:16:53:16:63 | headerValue | provenance | Sink:MaD:1 |
| MicronautResponseSplitting.java:27:42:27:71 | headerValue : String | MicronautResponseSplitting.java:29:79:29:89 | headerValue : String | provenance | |
| MicronautResponseSplitting.java:29:79:29:89 | headerValue : String | MicronautResponseSplitting.java:29:42:29:90 | singletonMap(...) | provenance | MaD:7 Sink:MaD:2 |
| MicronautResponseSplitting.java:33:50:33:79 | headerValue : String | MicronautResponseSplitting.java:35:33:35:43 | headerValue : String | provenance | |
| MicronautResponseSplitting.java:35:9:35:15 | headers [post update] : HashMap [<map.value>] : String | MicronautResponseSplitting.java:36:42:36:48 | headers | provenance | Sink:MaD:2 |
| MicronautResponseSplitting.java:35:33:35:43 | headerValue : String | MicronautResponseSplitting.java:35:9:35:15 | headers [post update] : HashMap [<map.value>] : String | provenance | MaD:8 |
| 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:22:39:22:66 | getParameter(...) : String | ResponseSplitting.java:22:20:22:67 | new Cookie(...) : Cookie | provenance | Src:MaD:5 MaD:9 |
| 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
@@ -21,13 +25,18 @@ models
| 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 |
| 8 | Summary: java.util; Map; true; put; (Object,Object); ; Argument[1]; Argument[this].MapValue; value; manual |
| 9 | Summary: javax.servlet.http; Cookie; false; Cookie; ; ; Argument[1]; Argument[this]; taint; manual |
nodes
| 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 |
| MicronautResponseSplitting.java:14:39:14:68 | headerValue : String | semmle.label | headerValue : String |
| MicronautResponseSplitting.java:16:53:16:63 | headerValue | semmle.label | headerValue |
| MicronautResponseSplitting.java:27:42:27:71 | headerValue : String | semmle.label | headerValue : String |
| MicronautResponseSplitting.java:29:42:29:90 | singletonMap(...) | semmle.label | singletonMap(...) |
| MicronautResponseSplitting.java:29:79:29:89 | headerValue : String | semmle.label | headerValue : String |
| MicronautResponseSplitting.java:33:50:33:79 | headerValue : String | semmle.label | headerValue : String |
| MicronautResponseSplitting.java:35:9:35:15 | headers [post update] : HashMap [<map.value>] : String | semmle.label | headers [post update] : HashMap [<map.value>] : String |
| MicronautResponseSplitting.java:35:33:35:43 | headerValue : String | semmle.label | headerValue : String |
| MicronautResponseSplitting.java:36:42:36:48 | headers | semmle.label | headers |
| 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

@@ -1,10 +1,10 @@
package io.micronaut.http.cookie;
import java.util.Collection;
import io.micronaut.core.convert.value.ConvertibleValues;
import java.util.Optional;
import java.util.Set;
public interface Cookies {
public interface Cookies extends ConvertibleValues<Cookie> {
Set<Cookie> getAll();
Optional<Cookie> findCookie(CharSequence name);
Cookie get(CharSequence name);