CVE-2026-59298
This Vulnerability has been fixed in the Never-Ending Support (NES) version offered by HeroDevs.
Overview
Spring Cloud Function is the Spring project that lets developers express business logic as plain Java functions and then run that same logic in very different places, from a standalone Spring Boot application to a message listener or a serverless platform. Its web adapter is the piece that puts those functions on HTTP: adding the adapter to a servlet or reactive application publishes every registered function at a URL, converts the incoming request into a message for the function, and turns the value the function returns back into an HTTP response.
An Incorrectly Configured Access Control vulnerability (CVE-2026-59298) has been identified in the web adapter of Spring Cloud Function, which allows a caller to choose the headers that come back on a function endpoint's response. The adapter assembles the response header set out of the caller's own request headers and screens that set against a deny list holding only three names, so security-relevant headers that arrive on the request, among them Access-Control-Allow-Origin, Access-Control-Allow-Credentials, Content-Security-Policy, Set-Cookie, Cache-Control and Authorization, are reflected back verbatim.
MITRE's CWE-942, Permissive Cross-domain Policy with Untrusted Domains, describes this weakness as one where: The product uses a web-client protection mechanism such as a Content Security Policy (CSP) or cross-domain policy file, but the policy includes untrusted domains with which the web client is allowed to communicate. Here the policy is not merely permissive, it is supplied by the requester, because the origin the response says it trusts and the content security policy a browser is then asked to enforce are whatever the caller placed on the request.
This issue affects applications that expose functions over HTTP through the web adapter of Spring Cloud Function.
Details
Module Info
- Product: Spring Cloud Function
- Affected packages:
org.springframework.cloud:spring-cloud-function-web - Affected versions: >=2.0.0 <=3.2.16, >=4.0.0 <=4.1.6, >=4.2.0 <=4.2.7, >=4.3.0 <=4.3.4, >=5.0.0 <5.0.4
- GitHub repository: https://github.com/spring-cloud/spring-cloud-function
- Published packages: https://central.sonatype.com/artifact/org.springframework.cloud/spring-cloud-function-web
- Package manager: Maven
- Fixed in:
- NES for Spring Cloud Function nes-v3.1.11, nes-v3.2.18, nes-v4.1.10, nes-v4.2.8, and nes-v4.3.6 (released September 1, 2026)
- Spring Cloud Function 5.0.4 (OSS)
Vulnerability Info
This Low-severity vulnerability is found in the org.springframework.cloud:spring-cloud-function-web package in the HTTP request handling of Spring Cloud Function. The web adapter is auto-configured as soon as the module is on the classpath of a servlet or reactive web application. No property enables it, and no property is consulted on the path described below, so the behavior is the default one for any application that publishes functions over HTTP.
Every request that reaches a function endpoint is processed by a single helper, which copies the inbound request headers into the message handed to the function and then constructs the response on that same header set:
HttpHeaders headers = wrapper.getHeaders();
...
inputMessage = builder.copyHeaders(headers.toSingleValueMap()).build();
...
BodyBuilder responseOkBuilder = ResponseEntity.ok().headers(HeaderUtils.sanitize(headers, ignoredHeaders, requestOnlyHeaders));
headers is the caller's own HttpHeaders, so the response header set starts life as a copy of the request header set. What decides which of those headers survive into the response is a pair of static collections in HeaderUtils:
private static HttpHeaders IGNORED = new HttpHeaders();
private static HttpHeaders REQUEST_ONLY = new HttpHeaders();
static {
IGNORED.add(MessageHeaders.ID, "");
IGNORED.add(HttpHeaders.CONTENT_LENGTH, "0");
IGNORED.add(HttpHeaders.TRANSFER_ENCODING, "*");
// Headers that would typically be added by a downstream client
REQUEST_ONLY.add(HttpHeaders.ACCEPT, "");
REQUEST_ONLY.add(HttpHeaders.CONTENT_LENGTH, "");
REQUEST_ONLY.add(HttpHeaders.CONTENT_TYPE, "");
REQUEST_ONLY.add(HttpHeaders.HOST, "");
}
sanitize copies a header across unless one of those two collections names it, and the application-supplied lists it also consults are empty unless the application has configured them:
public static HttpHeaders sanitize(HttpHeaders request, List<String> ignoredHeders, List<String> requestOnlyHeaders) {
HttpHeaders result = new HttpHeaders();
for (String name : request.keySet()) {
List<String> value = request.get(name);
name = name.toLowerCase(Locale.ROOT);
if (!IGNORED.containsKey(name) && !REQUEST_ONLY.containsKey(name) && !ignoredHeders.contains(name) && !requestOnlyHeaders.contains(name)) {
result.put(name, value);
}
}
return result;
}
Between them the two collections name a message identifier, the content length, the transfer encoding, the accept header, the content type and the host. Nothing else is withheld. A request carrying Access-Control-Allow-Origin: https://attacker.example, Access-Control-Allow-Credentials: true, Content-Security-Policy: script-src *, Set-Cookie: sid=attacker; Path=/, Cache-Control: public, max-age=31536000 or Authorization: Bearer ... therefore produces a response carrying exactly those headers with exactly those values.
The same collection governs the second way headers reach the response. Because the helper has already copied the request headers into the function's input message, any function that returns a Message whose headers were carried over emits them again through the companion method:
public static HttpHeaders fromMessage(MessageHeaders headers, List<String> ignoredHeders) {
HttpHeaders result = new HttpHeaders();
for (String name : headers.keySet()) {
Object value = headers.get(name);
name = name.toLowerCase(Locale.ROOT);
if (!IGNORED.containsKey(name) && !ignoredHeders.contains(name)) {
...
}
}
return result;
}
Both legs are reachable without authentication, because the function endpoints the adapter publishes are not themselves access controlled, and both operate on values the requester supplies. The consequences follow the header chosen. Reflected Access-Control-Allow-Origin and Access-Control-Allow-Credentials values tell a browser that a hostile origin may read the endpoint's responses with credentials attached, which is the cross-origin policy relaxation CWE-942 describes. A reflected Content-Security-Policy replaces whatever policy the deployment intended with one the caller wrote. A reflected Set-Cookie writes a cookie of the attacker's choosing into the victim's browser under the application's own domain, which is the classic setup for session fixation. A reflected Cache-Control changes how long an intermediary retains the response, and where a shared cache sits in front of the endpoint, a poisoned entry produced by one request is served to later, unrelated clients. Authorization, Cookie and the proxy authentication headers are echoed back to the caller who sent them rather than disclosed to a third party, but they are written into a response that intermediaries and logging pipelines may retain.
The remediated releases extend the ignored set so that Set-Cookie, Set-Cookie2, Content-Security-Policy, the six Access-Control-* names, Authorization, Cache-Control, Cookie, Proxy-Authenticate and Proxy-Authorization are always dropped from the response on both paths, without any configuration change.
Mitigation
Only recent versions of Spring Cloud Function receive community support. The 3.1.x, 3.2.x, 4.1.x, 4.2.x and 4.3.x lines are End-of-Life and will not receive public updates to address this issue.
Users of the affected components should apply one of the following mitigations:
- Upgrade to a currently supported version of Spring Cloud Function. The open-source fix ships in 5.0.4 on the 5.0.x line.
- As an interim measure on affected 4.x versions, name the sensitive headers explicitly in the adapter's own ignore list,
spring.cloud.function.http.ignored-headers, which is consulted alongside the built-in one and defaults to empty. This is partial hardening rather than a fix: it only withholds the headers an operator thinks to enumerate, and the property was introduced in 4.0.4, so the 3.1.x and 3.2.x lines have no equivalent setting. - Leverage a commercial support partner like HeroDevs for post-EOL security support.
Credits
- No public finder credit is listed in the advisory sources checked for this entry.