From 1a130fca3cc7d8c87a6d3746b3b4a341e2d16239 Mon Sep 17 00:00:00 2001 From: johnycho Date: Sat, 10 Jan 2026 17:24:37 +0900 Subject: [PATCH 1/3] Improve serialVersionUID check in tests Signed-off-by: johnycho --- .../security/SpringSecurityCoreVersionSerializableTests.java | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/config/src/test/java/org/springframework/security/SpringSecurityCoreVersionSerializableTests.java b/config/src/test/java/org/springframework/security/SpringSecurityCoreVersionSerializableTests.java index 85714f4124..0306bed51a 100644 --- a/config/src/test/java/org/springframework/security/SpringSecurityCoreVersionSerializableTests.java +++ b/config/src/test/java/org/springframework/security/SpringSecurityCoreVersionSerializableTests.java @@ -36,6 +36,7 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.HashSet; import java.util.List; +import java.util.Objects; import java.util.Set; import java.util.stream.Stream; @@ -208,8 +209,8 @@ class SpringSecurityCoreVersionSerializableTests { .map(Field::getName) .anyMatch((n) -> n.equals("serialVersionUID")); SuppressWarnings suppressWarnings = clazz.getAnnotation(SuppressWarnings.class); - boolean hasSerialIgnore = suppressWarnings == null - || Arrays.asList(suppressWarnings.value()).contains("Serial"); + boolean hasSerialIgnore = Objects.nonNull(suppressWarnings) + && Arrays.asList(suppressWarnings.value()).contains("serial"); if (!hasSerialVersion && !hasSerialIgnore) { classes.add(clazz); continue; From acabacb9711c2e6896fe96cbe6db1e849f295994 Mon Sep 17 00:00:00 2001 From: Josh Cummings <3627351+jzheaux@users.noreply.github.com> Date: Mon, 30 Mar 2026 17:14:03 -0600 Subject: [PATCH 2/3] Update Test to find SuppressWarnings Signed-off-by: Josh Cummings <3627351+jzheaux@users.noreply.github.com> --- ...gSecurityCoreVersionSerializableTests.java | 60 +++++++++++++++++-- 1 file changed, 54 insertions(+), 6 deletions(-) diff --git a/config/src/test/java/org/springframework/security/SpringSecurityCoreVersionSerializableTests.java b/config/src/test/java/org/springframework/security/SpringSecurityCoreVersionSerializableTests.java index 0306bed51a..daa733675d 100644 --- a/config/src/test/java/org/springframework/security/SpringSecurityCoreVersionSerializableTests.java +++ b/config/src/test/java/org/springframework/security/SpringSecurityCoreVersionSerializableTests.java @@ -33,11 +33,10 @@ import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; import java.util.ArrayList; -import java.util.Arrays; import java.util.HashSet; import java.util.List; -import java.util.Objects; import java.util.Set; +import java.util.regex.Pattern; import java.util.stream.Stream; import org.apache.commons.lang3.ObjectUtils; @@ -208,10 +207,7 @@ class SpringSecurityCoreVersionSerializableTests { boolean hasSerialVersion = Stream.of(clazz.getDeclaredFields()) .map(Field::getName) .anyMatch((n) -> n.equals("serialVersionUID")); - SuppressWarnings suppressWarnings = clazz.getAnnotation(SuppressWarnings.class); - boolean hasSerialIgnore = Objects.nonNull(suppressWarnings) - && Arrays.asList(suppressWarnings.value()).contains("serial"); - if (!hasSerialVersion && !hasSerialIgnore) { + if (!hasSerialVersion && !hasSuppressSerialInSource(clazz)) { classes.add(clazz); continue; } @@ -250,6 +246,58 @@ class SpringSecurityCoreVersionSerializableTests { return classes.stream(); } + private static boolean hasSuppressSerialInSource(Class clazz) { + try { + Class fileClass = clazz; + while (fileClass.getEnclosingClass() != null) { + fileClass = fileClass.getEnclosingClass(); + } + var codeSource = fileClass.getProtectionDomain().getCodeSource(); + if (codeSource == null) { + return false; + } + Path sourceFile = findSourceFile(Path.of(codeSource.getLocation().toURI()), fileClass); + if (sourceFile == null) { + return false; + } + return hasSuppressSerialAnnotation(Files.readAllLines(sourceFile), clazz.getSimpleName()); + } + catch (Exception ex) { + return false; + } + } + + private static Path findSourceFile(Path start, Class clazz) { + String relativePath = clazz.getName().replace('.', '/') + ".java"; + Path dir = start; + for (int i = 0; i < 10 && dir != null; i++) { + for (String sourceRoot : List.of("src/main/java", "src/test/java")) { + Path candidate = dir.resolve(sourceRoot).resolve(relativePath); + if (Files.exists(candidate)) { + return candidate; + } + } + dir = dir.getParent(); + } + return null; + } + + private static boolean hasSuppressSerialAnnotation(List lines, String simpleClassName) { + Pattern classDeclaration = Pattern + .compile("\\b(?:class|interface|enum|record)\\s+" + Pattern.quote(simpleClassName) + "\\b"); + for (int i = 0; i < lines.size(); i++) { + if (classDeclaration.matcher(lines.get(i)).find()) { + for (int j = Math.max(0, i - 5); j < i; j++) { + String line = lines.get(j); + if (line.contains("@SuppressWarnings") && line.contains("\"serial\"")) { + return true; + } + } + } + } + return false; + } + private static String getCurrentVersion() { String version = System.getProperty("springSecurityVersion"); String[] parts = version.split("\\."); From 08fca57d129aa7b305ad38500bae9aa001c19dbd Mon Sep 17 00:00:00 2001 From: Josh Cummings <3627351+jzheaux@users.noreply.github.com> Date: Tue, 31 Mar 2026 13:56:37 -0600 Subject: [PATCH 3/3] Add Missing Serialization Support Closed gh-19012 Signed-off-by: Josh Cummings <3627351+jzheaux@users.noreply.github.com> --- .../security/SerializationSamples.java | 4 ++++ ...i.AuthenticatorAttestationResponse.serialized | Bin 0 -> 1197 bytes ...ExpressionAttributeAuthorizationDecision.java | 1 + ...OAuth2AuthorizationRequestRedirectFilter.java | 1 + .../oauth2/jwt/DPoPProofJwtDecoderFactory.java | 1 + .../authentication/Saml2AuthenticationToken.java | 4 ++++ .../OpenSamlRelyingPartyRegistration.java | 1 + .../expression/WebExpressionConfigAttribute.java | 1 + .../api/AuthenticatorAttestationResponse.java | 4 ++++ 9 files changed, 17 insertions(+) create mode 100644 config/src/test/resources/serialized/6.5.x/org.springframework.security.web.webauthn.api.AuthenticatorAttestationResponse.serialized diff --git a/config/src/test/java/org/springframework/security/SerializationSamples.java b/config/src/test/java/org/springframework/security/SerializationSamples.java index 82297189ea..0093b902b8 100644 --- a/config/src/test/java/org/springframework/security/SerializationSamples.java +++ b/config/src/test/java/org/springframework/security/SerializationSamples.java @@ -211,6 +211,7 @@ import org.springframework.security.web.webauthn.api.AuthenticationExtensionsCli import org.springframework.security.web.webauthn.api.AuthenticationExtensionsClientOutputs; import org.springframework.security.web.webauthn.api.AuthenticatorAssertionResponse; import org.springframework.security.web.webauthn.api.AuthenticatorAttachment; +import org.springframework.security.web.webauthn.api.AuthenticatorAttestationResponse; import org.springframework.security.web.webauthn.api.AuthenticatorTransport; import org.springframework.security.web.webauthn.api.Bytes; import org.springframework.security.web.webauthn.api.CredProtectAuthenticationExtensionsClientInput; @@ -225,6 +226,7 @@ import org.springframework.security.web.webauthn.api.PublicKeyCredentialRequestO import org.springframework.security.web.webauthn.api.PublicKeyCredentialType; import org.springframework.security.web.webauthn.api.PublicKeyCredentialUserEntity; import org.springframework.security.web.webauthn.api.TestAuthenticationAssertionResponses; +import org.springframework.security.web.webauthn.api.TestAuthenticatorAttestationResponses; import org.springframework.security.web.webauthn.api.TestBytes; import org.springframework.security.web.webauthn.api.TestPublicKeyCredentialRequestOptions; import org.springframework.security.web.webauthn.api.TestPublicKeyCredentialUserEntities; @@ -654,6 +656,8 @@ final class SerializationSamples { generatorByClassName.put(CredentialPropertiesOutput.class, (o) -> credentialOutput); generatorByClassName.put(ImmutableAuthenticationExtensionsClientOutputs.class, (o) -> outputs); generatorByClassName.put(AuthenticatorAssertionResponse.class, (r) -> response); + generatorByClassName.put(AuthenticatorAttestationResponse.class, + (r) -> TestAuthenticatorAttestationResponses.createAuthenticatorAttestationResponse().build()); generatorByClassName.put(RelyingPartyAuthenticationRequest.class, (r) -> authRequest); generatorByClassName.put(PublicKeyCredential.class, (r) -> credential); generatorByClassName.put(WebAuthnAuthenticationRequestToken.class, (r) -> requestToken); diff --git a/config/src/test/resources/serialized/6.5.x/org.springframework.security.web.webauthn.api.AuthenticatorAttestationResponse.serialized b/config/src/test/resources/serialized/6.5.x/org.springframework.security.web.webauthn.api.AuthenticatorAttestationResponse.serialized new file mode 100644 index 0000000000000000000000000000000000000000..5ca34eea2203a12408188c21a8213cc3c07abb89 GIT binary patch literal 1197 zcmZ4UmVvdnh`}$vC|$3(peQphJ*_A)H?=&!C|j>MHMz7Xv!qh5JT(b~6H7}n^7Il5 zGW8sRtkk@c%;dz9{36GalGNgo#FEVXyr9(Lg8aPV)R$?7CCuN%)-y3M`7j73A{6*1 zWu+#UlrWh304>xBuaKdR zfw7_h7-Ytz*#!2%rN(Xc8pjss15J)*U`+yfs)T_l+Nq+TvN?3W}}t^;0Vna|?1( z^>Xr)6LT{1i%YC5OiYYHb`<3o7yCn%rzPeTr`8sOLV&dtL~<}NFxoFpPRlJx$;;16 zO$Wwva7k{-f(%f+fdVXI%IS-%4w^(6&AH0E_0`ooEsUjp$9CymK52G~Ly)`AN8?5; z(31?iZiS{TU*#abcbaSBtWY6d1_9HFIuBo6%qe(ycnRmrO>mu&e}nug1-NY&o_;?pavH>d9ULCyGY$zrP4%)>sY zbRId>786>YS9QuySxEJf`h>hgFJzb6-Di2Gdn)6lgG0l~4hD}f4wd!WswN*f?7dj# z{(`+xWpZ=x)gRa#T=VL{yQ1mx+&5lxOyl5sF6TGZu*F{J((iM@3=Da*+g63FWAaR@ z)@`^u$+}Swn2@AEsaOw`iuD|eiV`b}RlpP|Mc-Uvv+ryN+a+*%VN5Jx5RQhV`JBYO zbbUx>vIZv}_h?X#A~y?%z;cy!{-)c}+8-?gVJVOa=s*{0n7oOTC+Xc9cPVhDVl7L| qDNQY55JK`za0#gN0s5wdfi0slsVFlAoYVwM7&tQXN>YpR5_16KQp^Vc literal 0 HcmV?d00001 diff --git a/core/src/main/java/org/springframework/security/authorization/method/ExpressionAttributeAuthorizationDecision.java b/core/src/main/java/org/springframework/security/authorization/method/ExpressionAttributeAuthorizationDecision.java index 6eff3cc8ae..5cf340520e 100644 --- a/core/src/main/java/org/springframework/security/authorization/method/ExpressionAttributeAuthorizationDecision.java +++ b/core/src/main/java/org/springframework/security/authorization/method/ExpressionAttributeAuthorizationDecision.java @@ -28,6 +28,7 @@ import org.springframework.security.authorization.AuthorizationDecision; * instead */ @Deprecated +@SuppressWarnings("serial") public class ExpressionAttributeAuthorizationDecision extends AuthorizationDecision { private final ExpressionAttribute expressionAttribute; diff --git a/oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/web/OAuth2AuthorizationRequestRedirectFilter.java b/oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/web/OAuth2AuthorizationRequestRedirectFilter.java index 0757ae4693..434e4bc3fe 100644 --- a/oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/web/OAuth2AuthorizationRequestRedirectFilter.java +++ b/oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/web/OAuth2AuthorizationRequestRedirectFilter.java @@ -269,6 +269,7 @@ public class OAuth2AuthorizationRequestRedirectFilter extends OncePerRequestFilt } + @SuppressWarnings("serial") private static final class OAuth2AuthorizationRequestException extends AuthenticationException { OAuth2AuthorizationRequestException(Throwable cause) { diff --git a/oauth2/oauth2-jose/src/main/java/org/springframework/security/oauth2/jwt/DPoPProofJwtDecoderFactory.java b/oauth2/oauth2-jose/src/main/java/org/springframework/security/oauth2/jwt/DPoPProofJwtDecoderFactory.java index be89885b7b..de88ba57ae 100644 --- a/oauth2/oauth2-jose/src/main/java/org/springframework/security/oauth2/jwt/DPoPProofJwtDecoderFactory.java +++ b/oauth2/oauth2-jose/src/main/java/org/springframework/security/oauth2/jwt/DPoPProofJwtDecoderFactory.java @@ -185,6 +185,7 @@ public final class DPoPProofJwtDecoderFactory implements JwtDecoderFactory { private static final int MAX_SIZE = 1000; diff --git a/saml2/saml2-service-provider/src/main/java/org/springframework/security/saml2/provider/service/authentication/Saml2AuthenticationToken.java b/saml2/saml2-service-provider/src/main/java/org/springframework/security/saml2/provider/service/authentication/Saml2AuthenticationToken.java index 797bc124b0..9f0f39dd43 100644 --- a/saml2/saml2-service-provider/src/main/java/org/springframework/security/saml2/provider/service/authentication/Saml2AuthenticationToken.java +++ b/saml2/saml2-service-provider/src/main/java/org/springframework/security/saml2/provider/service/authentication/Saml2AuthenticationToken.java @@ -16,6 +16,7 @@ package org.springframework.security.saml2.provider.service.authentication; +import java.io.Serial; import java.util.Collections; import org.springframework.security.authentication.AbstractAuthenticationToken; @@ -33,6 +34,9 @@ import org.springframework.util.Assert; */ public class Saml2AuthenticationToken extends AbstractAuthenticationToken { + @Serial + private static final long serialVersionUID = 5225098478444036532L; + private final RelyingPartyRegistration relyingPartyRegistration; private final String saml2Response; diff --git a/saml2/saml2-service-provider/src/main/java/org/springframework/security/saml2/provider/service/registration/OpenSamlRelyingPartyRegistration.java b/saml2/saml2-service-provider/src/main/java/org/springframework/security/saml2/provider/service/registration/OpenSamlRelyingPartyRegistration.java index 03e4a54172..448ff5340a 100644 --- a/saml2/saml2-service-provider/src/main/java/org/springframework/security/saml2/provider/service/registration/OpenSamlRelyingPartyRegistration.java +++ b/saml2/saml2-service-provider/src/main/java/org/springframework/security/saml2/provider/service/registration/OpenSamlRelyingPartyRegistration.java @@ -42,6 +42,7 @@ import org.springframework.security.saml2.core.Saml2X509Credential; * */ @Deprecated +@SuppressWarnings("serial") public final class OpenSamlRelyingPartyRegistration extends RelyingPartyRegistration { OpenSamlRelyingPartyRegistration(RelyingPartyRegistration registration) { diff --git a/web/src/main/java/org/springframework/security/web/access/expression/WebExpressionConfigAttribute.java b/web/src/main/java/org/springframework/security/web/access/expression/WebExpressionConfigAttribute.java index 9a71c98480..94e54d524f 100644 --- a/web/src/main/java/org/springframework/security/web/access/expression/WebExpressionConfigAttribute.java +++ b/web/src/main/java/org/springframework/security/web/access/expression/WebExpressionConfigAttribute.java @@ -32,6 +32,7 @@ import org.springframework.security.web.FilterInvocation; * {@link AuthorizationManager}. */ @Deprecated +@SuppressWarnings("serial") class WebExpressionConfigAttribute implements ConfigAttribute, EvaluationContextPostProcessor { private final Expression authorizeExpression; diff --git a/web/src/main/java/org/springframework/security/web/webauthn/api/AuthenticatorAttestationResponse.java b/web/src/main/java/org/springframework/security/web/webauthn/api/AuthenticatorAttestationResponse.java index 75123cb88f..50532f898c 100644 --- a/web/src/main/java/org/springframework/security/web/webauthn/api/AuthenticatorAttestationResponse.java +++ b/web/src/main/java/org/springframework/security/web/webauthn/api/AuthenticatorAttestationResponse.java @@ -16,6 +16,7 @@ package org.springframework.security.web.webauthn.api; +import java.io.Serial; import java.util.Arrays; import java.util.List; @@ -34,6 +35,9 @@ import java.util.List; */ public final class AuthenticatorAttestationResponse extends AuthenticatorResponse { + @Serial + private static final long serialVersionUID = -1628559840895428945L; + private final Bytes attestationObject; private final List transports;