Related: #39 (SPR-CONFIG-004 targets a namespace that does not exist; the two are best
fixed together).
Config that is missed
management:
endpoints:
web:
cors:
allowed-origins: "*"
allow-credentials: true
$ sprig scan ./realprops
Checked 0 Java file(s), 1 config file(s) in 28 ms. Found 0 finding(s).
$ echo $?
0
Reproduced identically across two runs.
The namespace is real, unlike the one SPR-CONFIG-004 targets (#39)
Property counts from META-INF/spring-configuration-metadata.json in
spring-boot-actuator-autoconfigure:
| Boot |
management.endpoints.web.cors.* |
legacy endpoints.cors.* |
| 2.0.9 |
6 |
6 |
| 2.1.18 |
6 |
6 |
| 2.2.13 |
6 |
6 |
| 2.3.12 |
6 |
0 |
| 2.4.13 |
6 |
0 |
| 2.5.15 |
7 |
0 |
| 2.6.15 |
7 |
0 |
| 2.7.18 |
7 |
0 |
| 3.0.13 |
7 |
0 |
| 3.2.12 |
7 |
0 |
| 3.5.16 |
7 |
0 |
Present in every Boot 2.x and 3.x release checked. The 7th property is
allowed-origin-patterns, added in 2.5. (spring-boot-actuator-autoconfigure does not
exist as a separate artifact before 2.0, so 1.5 is not comparable.)
spring.graphql.cors.* is the second real surface, with the same 7 properties. Verified
cutoff: 0 in 2.5.15, 0 in 2.6.15, 7 from 2.7.18 onward.
Note for whoever implements this: the legacy endpoints.cors.* prefix exists through 2.2
and is gone from 2.3. Probably not worth supporting, but worth a deliberate decision rather
than an accidental omission.
Why it is exploitable
Two distinct cases, both verified against a running app whose only CORS configuration
comes from these properties (no @CrossOrigin anywhere in the source).
Case 1: wildcard with credentials, Spring Framework below 5.3
Boot 2.3.12 (ships spring-web 5.2.15), attacker origin against an exposed health endpoint:
$ curl -i -H "Origin: https://evil.example" http://localhost:8088/actuator/health
HTTP/1.1 200
Access-Control-Allow-Origin: https://evil.example
Access-Control-Allow-Credentials: true
{"status":"UP"}
The origin is reflected and credentials are permitted, so any site can read Actuator
responses with the victim's session attached.
Case 2: wildcard without credentials, current Spring
From Framework 5.3 onward validateAllowCredentials() rejects wildcard-plus-credentials at
startup, so that combination cannot survive in a running Boot 3 app; it is loud and
self-announcing. A wildcard without credentials, however, starts cleanly and still leaks:
$ curl -i -H "Origin: https://evil.example" http://localhost:8087/actuator/health
HTTP/1.1 200
Access-Control-Allow-Origin: *
{"status":"UP"}
Boot 3.5.16 (ships spring-web 6.2.19). Any origin can read Actuator endpoint bodies
cross-origin. With a broader
management.endpoints.web.exposure.include, that reaches /env, /configprops,
/beans, /mappings and similar: the same exposure surface SPR-CONFIG-001 already treats
as sensitive, reached through a different door. In my judgement this is the case worth
prioritising, since it is the only one of the two that can describe a running Boot 3 app.
Negative control
Same app and Boot version, wildcard replaced by a concrete allowlist plus credentials. The
attacker origin is rejected outright, on both the preflight and the plain GET:
$ curl -i -X OPTIONS -H "Origin: https://evil.example" \
-H "Access-Control-Request-Method: GET" http://localhost:8094/actuator/health
HTTP/1.1 403
$ curl -i -H "Origin: https://evil.example" http://localhost:8094/actuator/health
HTTP/1.1 403
while the allowed origin is served normally:
$ curl -i -X OPTIONS -H "Origin: https://app.example.com" \
-H "Access-Control-Request-Method: GET" http://localhost:8094/actuator/health
HTTP/1.1 200
Access-Control-Allow-Origin: https://app.example.com
Access-Control-Allow-Methods: GET
Access-Control-Allow-Credentials: true
Access-Control-Max-Age: 1800
So the leaks above are caused by the wildcard, not by an artifact of the test setup.
Suggested detection
| Condition |
Severity |
allowed-origins contains * and allow-credentials: true |
HIGH |
allowed-origins contains * and credentials unset or false |
MEDIUM |
Across management.endpoints.web.cors.* and spring.graphql.cors.*.
allowed-origin-patterns needs a deliberate decision. Note that SPR-CORS-001 is not a
straight precedent here: it lets originPatterns suppress only the implicit wildcard
(origins unset), while an explicit origins = "*" still fires even when originPatterns
is present (CorsWildcardCredentialsRule.java:71-76).
The MEDIUM tier could reasonably be argued upward when
management.endpoints.web.exposure.include is itself broad, since the value of what leaks
rises sharply. Worth deciding before implementation rather than after.
References
- Spring Boot Actuator:
management.endpoints.web.cors.*, documented under "CORS support"
in the Actuator reference.
CorsConfiguration.validateAllowCredentials(), Spring Framework 5.3+, rejects
allowedOrigins = ["*"] combined with allowCredentials = true.
- CWE-942: Permissive Cross-domain Policy with Untrusted Domains.
Environment
- sprig version:
sprig 0.1.0
- Java version:
openjdk version "17.0.20" 2026-07-21 (Homebrew, build 17.0.20+0)
- Probes: Boot 2.3.12.RELEASE and Boot 3.5.16,
spring-boot-starter-web +
spring-boot-starter-actuator, no CORS annotations in source.
Related: #39 (SPR-CONFIG-004 targets a namespace that does not exist; the two are best
fixed together).
Config that is missed
Reproduced identically across two runs.
The namespace is real, unlike the one SPR-CONFIG-004 targets (#39)
Property counts from
META-INF/spring-configuration-metadata.jsoninspring-boot-actuator-autoconfigure:management.endpoints.web.cors.*endpoints.cors.*Present in every Boot 2.x and 3.x release checked. The 7th property is
allowed-origin-patterns, added in 2.5. (spring-boot-actuator-autoconfiguredoes notexist as a separate artifact before 2.0, so 1.5 is not comparable.)
spring.graphql.cors.*is the second real surface, with the same 7 properties. Verifiedcutoff: 0 in 2.5.15, 0 in 2.6.15, 7 from 2.7.18 onward.
Note for whoever implements this: the legacy
endpoints.cors.*prefix exists through 2.2and is gone from 2.3. Probably not worth supporting, but worth a deliberate decision rather
than an accidental omission.
Why it is exploitable
Two distinct cases, both verified against a running app whose only CORS configuration
comes from these properties (no
@CrossOriginanywhere in the source).Case 1: wildcard with credentials, Spring Framework below 5.3
Boot 2.3.12 (ships spring-web 5.2.15), attacker origin against an exposed health endpoint:
The origin is reflected and credentials are permitted, so any site can read Actuator
responses with the victim's session attached.
Case 2: wildcard without credentials, current Spring
From Framework 5.3 onward
validateAllowCredentials()rejects wildcard-plus-credentials atstartup, so that combination cannot survive in a running Boot 3 app; it is loud and
self-announcing. A wildcard without credentials, however, starts cleanly and still leaks:
Boot 3.5.16 (ships spring-web 6.2.19). Any origin can read Actuator endpoint bodies
cross-origin. With a broader
management.endpoints.web.exposure.include, that reaches/env,/configprops,/beans,/mappingsand similar: the same exposure surface SPR-CONFIG-001 already treatsas sensitive, reached through a different door. In my judgement this is the case worth
prioritising, since it is the only one of the two that can describe a running Boot 3 app.
Negative control
Same app and Boot version, wildcard replaced by a concrete allowlist plus credentials. The
attacker origin is rejected outright, on both the preflight and the plain GET:
while the allowed origin is served normally:
So the leaks above are caused by the wildcard, not by an artifact of the test setup.
Suggested detection
allowed-originscontains*andallow-credentials: trueallowed-originscontains*and credentials unset or falseAcross
management.endpoints.web.cors.*andspring.graphql.cors.*.allowed-origin-patternsneeds a deliberate decision. Note that SPR-CORS-001 is not astraight precedent here: it lets
originPatternssuppress only the implicit wildcard(
originsunset), while an explicitorigins = "*"still fires even whenoriginPatternsis present (
CorsWildcardCredentialsRule.java:71-76).The MEDIUM tier could reasonably be argued upward when
management.endpoints.web.exposure.includeis itself broad, since the value of what leaksrises sharply. Worth deciding before implementation rather than after.
References
management.endpoints.web.cors.*, documented under "CORS support"in the Actuator reference.
CorsConfiguration.validateAllowCredentials(), Spring Framework 5.3+, rejectsallowedOrigins = ["*"]combined withallowCredentials = true.Environment
sprig 0.1.0openjdk version "17.0.20" 2026-07-21(Homebrew, build 17.0.20+0)spring-boot-starter-web+spring-boot-starter-actuator, no CORS annotations in source.