Jakarta Bean Validation — the annotation-driven spec better known by its original name, JSR 380 — is the validation layer sitting inside nearly every Spring Boot application built in the last decade, letting a developer write @NotNull @Size(max = 255) on a field instead of hand-rolling null checks. Its reference implementation, Hibernate Validator, ships by default with Spring Boot's spring-boot-starter-validation dependency, and the spec itself moved from javax.validation to jakarta.validation in Bean Validation 3.0, published by the Jakarta EE working group on July 4, 2020 — a pure package rename with no new constraint semantics added. But in June 2025, CVE-2025-35036 (tracked upstream as GHSA-7v6m-28jr-rg84) showed what happens when teams treat this convenience layer as a security control rather than a formatting one: Hibernate Validator's default message interpolator evaluates constraint-violation messages as Expression Language, and if attacker-controlled input ends up embedded in a custom message string, that EL gets executed — CVSS v3.1 score 7.3. This wasn't even the first time; a related EL sandbox-escape (GHSL-2020-020, fixed in Hibernate Validator 6.1.5.Final) surfaced back in 2020. This post walks through how @Valid cascading actually works, where teams misconfigure it, and how the EL injection class of bug keeps recurring.
What does @Valid actually validate, and what does it silently skip?
@Valid triggers Bean Validation at a specific integration point — a Spring @RequestBody parameter, a JPA pre-persist hook, or a manually invoked Validator.validate() call — and when placed on a field or property referencing another object, it cascades validation into that nested object's own constraints. The common misconfiguration is assuming cascading is automatic: if a User class has an Address address field with its own @NotBlank constraints, but the address field itself lacks @Valid, Hibernate Validator validates every top-level constraint on User and silently skips all of Address's constraints entirely — no error, no warning, just an unvalidated nested object flowing into your business logic or persistence layer. The same gap applies to collections: a List<Address> needs @Valid on the field for each element to be checked. Because Bean Validation only runs where something explicitly invokes it, any code path that constructs a DTO manually — outside a Spring-managed controller method or JPA lifecycle callback — bypasses validation entirely, even with every annotation correctly in place.
How is @Validated different from @Valid, and why does the distinction matter?
@Validated is a Spring-specific annotation, not part of the Jakarta Bean Validation spec at all, and it solves a different problem than @Valid: it enables Spring's MethodValidationPostProcessor to validate parameters and return values on any Spring-managed bean's methods, and it supports validation groups (@Validated(OnCreate.class)) so the same DTO can apply different constraints on create versus update. Developers who reach for @Validated expecting it to fix a nested-cascading gap are solving the wrong problem — @Validated governs whether method-level validation runs at all on a service or component bean, while @Valid on a field is what actually tells Hibernate Validator to descend into a nested object. Using @Validated on a controller class without also putting @Valid on the relevant nested fields inside the request DTO leaves the same silent-skip gap described above; the two annotations operate at different levels of the same validation pipeline and neither substitutes for the other.
How did a message-formatting feature turn into a code execution vulnerability?
Hibernate Validator's default ResourceBundleMessageInterpolator resolves a message template in two passes: first it substitutes {parameter}-style placeholders like {min} and {max} with the constraint annotation's own attribute values, then it evaluates any remaining ${...}-style Expression Language syntax in the resulting string. The first pass is normally harmless because {min} and {max} come from the annotation itself, not from user input. CVE-2025-35036 (CVSS 7.3, disclosed via GHSA-7v6m-28jr-rg84 in June 2025) applies when application code builds a custom violation message that concatenates attacker-controlled input directly into the message string — for example, constructing a ConstraintValidatorContext message dynamically from a request field — because that string is still passed through the same EL evaluator. An attacker who controls part of the message text can inject EL syntax and, depending on classpath and sandbox behavior, achieve information disclosure or code execution. This wasn't a first offense: GHSL-2020-020 found in 2020 that even Hibernate Validator's "safe" EL mode, interpolateExpressionSafely, could be bypassed, and it was fixed in Hibernate Validator 6.1.5.Final. Two disclosures five years apart against the same feature is a signal that EL-based message interpolation is a structurally risky default, not a one-off implementation bug.
What should teams actually do to close this off?
The direct fix is procedural, not annotation-level: never build a constraint violation message by concatenating untrusted input, full stop — treat message templates the same way you'd treat a SQL string, as a place attacker data must never be interpolated unescaped. Teams that need dynamic message content should pass user data as a named EL parameter via addMessageParameter() rather than string-concatenating it into the template itself, since parameters are substituted as literal values rather than re-evaluated as expressions. More conservatively, Hibernate Validator supports swapping the default interpolator for ParameterMessageInterpolator, which disables EL evaluation of messages outright and only performs simple {parameter} substitution — appropriate for applications that don't rely on EL-based conditional messages. Regardless of interpolator choice, pin Hibernate Validator to 6.2.0+ or 7.0.0+, since both the 2020 and 2025 disclosures landed their fixes in specific patched releases rather than being addressed by a config flag applied to older jars.
Where does Bean Validation stop being a security control?
Bean Validation enforces shape and format — string length, numeric range, non-null, regex pattern — at the boundary where data enters your application, and that is a genuinely useful defensive layer, but it was never designed to enforce authorization or business-logic rules, and treating it as such is the most common misconfiguration of all. A @Size(max = 10000) constraint on a comment field prevents a resource-exhaustion payload; it says nothing about whether the authenticated user is allowed to post a comment on that particular resource. Similarly, a @Pattern constraint built from a naively written regex is itself a ReDoS risk if the pattern contains nested quantifiers matched against attacker-controlled string length — the constraint meant to reject bad input becomes the vector for a denial-of-service if the validator itself runs unbounded backtracking. And because Bean Validation typically runs against a DTO at the controller boundary, any code path that persists a JPA entity directly — bypassing the DTO — skips those constraints unless the entity itself is separately annotated and validated by the persistence provider's own pre-persist hook.
How Safeguard Helps
Safeguard's software composition analysis flags outdated Hibernate Validator versions against the CVE record — including CVE-2025-35036 and its 2020 predecessor — and reachability analysis traces whether your codebase actually calls the vulnerable EL-interpolation path (custom message construction with concatenated input) rather than flagging every Hibernate Validator dependency as equally urgent. PR Guard, Safeguard's AI-driven pull request review, can flag the two misconfiguration patterns covered here in a diff: nested objects or collections missing @Valid where cascading was clearly intended, and violation messages built by string concatenation from request data. Because Safeguard treats reachable, exploitable findings differently from theoretical dependency matches, a team pinned to an older Hibernate Validator jar sees that finding prioritized only when the surrounding code pattern makes it exploitable, not just present.