diff --git a/taglibs/spring-security-taglibs.gradle b/taglibs/spring-security-taglibs.gradle index 93471a698f..8727474344 100644 --- a/taglibs/spring-security-taglibs.gradle +++ b/taglibs/spring-security-taglibs.gradle @@ -1,3 +1,7 @@ +plugins { + id 'apply-nullability' +} + apply plugin: 'io.spring.convention.spring-module' dependencies { diff --git a/taglibs/src/main/java/org/springframework/security/taglibs/authz/AbstractAuthorizeTag.java b/taglibs/src/main/java/org/springframework/security/taglibs/authz/AbstractAuthorizeTag.java index b7aa5e6f22..b41038828f 100644 --- a/taglibs/src/main/java/org/springframework/security/taglibs/authz/AbstractAuthorizeTag.java +++ b/taglibs/src/main/java/org/springframework/security/taglibs/authz/AbstractAuthorizeTag.java @@ -24,6 +24,8 @@ import jakarta.servlet.ServletContext; import jakarta.servlet.ServletRequest; import jakarta.servlet.ServletResponse; import jakarta.servlet.http.HttpServletRequest; +import org.jspecify.annotations.NullUnmarked; +import org.jspecify.annotations.Nullable; import org.springframework.context.ApplicationContext; import org.springframework.core.GenericTypeResolver; @@ -40,6 +42,7 @@ import org.springframework.security.web.FilterInvocation; import org.springframework.security.web.WebAttributes; import org.springframework.security.web.access.WebInvocationPrivilegeEvaluator; import org.springframework.security.web.context.support.SecurityWebApplicationContextUtils; +import org.springframework.util.Assert; import org.springframework.util.StringUtils; /** @@ -60,11 +63,13 @@ import org.springframework.util.StringUtils; */ public abstract class AbstractAuthorizeTag { - private String access; + @SuppressWarnings("NullAway.Init") + private @Nullable String access; - private String url; + @SuppressWarnings("NullAway.Init") + private @Nullable String url; - private String method = "GET"; + private @Nullable String method = "GET"; /** * This method allows subclasses to provide a way to access the ServletRequest @@ -112,14 +117,17 @@ public abstract class AbstractAuthorizeTag { * @return the result of the authorization decision * @throws IOException */ + @SuppressWarnings("NullAway") // Dataflow analysis limitation public boolean authorizeUsingAccessExpression() throws IOException { if (getContext().getAuthentication() == null) { return false; } + String access = getAccess(); + Assert.notNull(access, "access cannot be null"); SecurityExpressionHandler handler = getExpressionHandler(); Expression accessExpression; try { - accessExpression = handler.getExpressionParser().parseExpression(getAccess()); + accessExpression = handler.getExpressionParser().parseExpression(access); } catch (ParseException ex) { throw new IOException(ex); @@ -143,13 +151,16 @@ public abstract class AbstractAuthorizeTag { * @return the result of the authorization decision * @throws IOException */ + @SuppressWarnings("NullAway") // Dataflow analysis limitation public boolean authorizeUsingUrlCheck() throws IOException { + String url = getUrl(); + Assert.notNull(url, "url cannot be null"); String contextPath = ((HttpServletRequest) getRequest()).getContextPath(); Authentication currentUser = getContext().getAuthentication(); - return getPrivilegeEvaluator().isAllowed(contextPath, getUrl(), getMethod(), currentUser); + return getPrivilegeEvaluator().isAllowed(contextPath, url, getMethod(), currentUser); } - public String getAccess() { + public @Nullable String getAccess() { return this.access; } @@ -157,7 +168,7 @@ public abstract class AbstractAuthorizeTag { this.access = access; } - public String getUrl() { + public @Nullable String getUrl() { return this.url; } @@ -165,10 +176,11 @@ public abstract class AbstractAuthorizeTag { this.url = url; } - public String getMethod() { + public @Nullable String getMethod() { return this.method; } + @NullUnmarked public void setMethod(String method) { this.method = (method != null) ? method.toUpperCase(Locale.ENGLISH) : null; } diff --git a/taglibs/src/main/java/org/springframework/security/taglibs/authz/AccessControlListTag.java b/taglibs/src/main/java/org/springframework/security/taglibs/authz/AccessControlListTag.java index bfe26d073f..5ff233a84a 100644 --- a/taglibs/src/main/java/org/springframework/security/taglibs/authz/AccessControlListTag.java +++ b/taglibs/src/main/java/org/springframework/security/taglibs/authz/AccessControlListTag.java @@ -19,6 +19,7 @@ package org.springframework.security.taglibs.authz; import java.util.ArrayList; import java.util.List; import java.util.Map; +import java.util.Objects; import jakarta.servlet.ServletContext; import jakarta.servlet.jsp.JspException; @@ -27,6 +28,7 @@ import jakarta.servlet.jsp.tagext.Tag; import jakarta.servlet.jsp.tagext.TagSupport; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; +import org.jspecify.annotations.Nullable; import org.springframework.context.ApplicationContext; import org.springframework.security.access.PermissionEvaluator; @@ -60,14 +62,18 @@ public class AccessControlListTag extends TagSupport { private SecurityContextHolderStrategy securityContextHolderStrategy = SecurityContextHolder .getContextHolderStrategy(); + @SuppressWarnings("NullAway.Init") private ApplicationContext applicationContext; + @SuppressWarnings("NullAway.Init") private Object domainObject; + @SuppressWarnings("NullAway.Init") private PermissionEvaluator permissionEvaluator; private String hasPermission = ""; + @SuppressWarnings("NullAway.Init") private String var; @Override @@ -148,7 +154,8 @@ public class AccessControlListTag extends TagSupport { return; } this.applicationContext = getContext(this.pageContext); - this.permissionEvaluator = getBeanOfType(PermissionEvaluator.class); + this.permissionEvaluator = Objects.requireNonNull(getBeanOfType(PermissionEvaluator.class), + "PermissionEvaluator Bean is required"); String[] names = this.applicationContext.getBeanNamesForType(SecurityContextHolderStrategy.class); if (names.length == 1) { SecurityContextHolderStrategy strategy = this.applicationContext @@ -157,7 +164,7 @@ public class AccessControlListTag extends TagSupport { } } - private T getBeanOfType(Class type) throws JspException { + private @Nullable T getBeanOfType(Class type) throws JspException { Map map = this.applicationContext.getBeansOfType(type); for (ApplicationContext context = this.applicationContext.getParent(); context != null; context = context .getParent()) { diff --git a/taglibs/src/main/java/org/springframework/security/taglibs/authz/AuthenticationTag.java b/taglibs/src/main/java/org/springframework/security/taglibs/authz/AuthenticationTag.java index 07939ca169..784e9aabb8 100644 --- a/taglibs/src/main/java/org/springframework/security/taglibs/authz/AuthenticationTag.java +++ b/taglibs/src/main/java/org/springframework/security/taglibs/authz/AuthenticationTag.java @@ -23,6 +23,7 @@ import jakarta.servlet.jsp.JspException; import jakarta.servlet.jsp.PageContext; import jakarta.servlet.jsp.tagext.Tag; import jakarta.servlet.jsp.tagext.TagSupport; +import org.jspecify.annotations.Nullable; import org.springframework.beans.BeanWrapperImpl; import org.springframework.beans.BeansException; @@ -49,9 +50,9 @@ public class AuthenticationTag extends TagSupport { private SecurityContextHolderStrategy securityContextHolderStrategy = SecurityContextHolder .getContextHolderStrategy(); - private String var; + private @Nullable String var; - private String property; + private @Nullable String property; private int scope; diff --git a/taglibs/src/main/java/org/springframework/security/taglibs/authz/JspAuthorizeTag.java b/taglibs/src/main/java/org/springframework/security/taglibs/authz/JspAuthorizeTag.java index 4696f5051a..48c2798043 100644 --- a/taglibs/src/main/java/org/springframework/security/taglibs/authz/JspAuthorizeTag.java +++ b/taglibs/src/main/java/org/springframework/security/taglibs/authz/JspAuthorizeTag.java @@ -25,6 +25,7 @@ import jakarta.servlet.ServletResponse; import jakarta.servlet.jsp.JspException; import jakarta.servlet.jsp.PageContext; import jakarta.servlet.jsp.tagext.Tag; +import org.jspecify.annotations.Nullable; import org.springframework.expression.BeanResolver; import org.springframework.expression.ConstructorResolver; @@ -49,13 +50,16 @@ import org.springframework.security.web.FilterInvocation; */ public class JspAuthorizeTag extends AbstractAuthorizeTag implements Tag { - private Tag parent; + @SuppressWarnings("NullAway.Init") + private @Nullable Tag parent; + @SuppressWarnings("NullAway.Init") protected PageContext pageContext; - protected String id; + protected @Nullable String id; - private String var; + @SuppressWarnings("NullAway.Init") + private @Nullable String var; private boolean authorized; @@ -104,7 +108,7 @@ public class JspAuthorizeTag extends AbstractAuthorizeTag implements Tag { return EVAL_PAGE; } - public String getId() { + public @Nullable String getId() { return this.id; } @@ -113,7 +117,7 @@ public class JspAuthorizeTag extends AbstractAuthorizeTag implements Tag { } @Override - public Tag getParent() { + public @Nullable Tag getParent() { return this.parent; } @@ -122,7 +126,7 @@ public class JspAuthorizeTag extends AbstractAuthorizeTag implements Tag { this.parent = parent; } - public String getVar() { + public @Nullable String getVar() { return this.var; } @@ -205,12 +209,12 @@ public class JspAuthorizeTag extends AbstractAuthorizeTag implements Tag { } @Override - public BeanResolver getBeanResolver() { + public @Nullable BeanResolver getBeanResolver() { return this.delegate.getBeanResolver(); } @Override - public void setVariable(String name, Object value) { + public void setVariable(String name, @Nullable Object value) { this.delegate.setVariable(name, value); } diff --git a/taglibs/src/main/java/org/springframework/security/taglibs/authz/package-info.java b/taglibs/src/main/java/org/springframework/security/taglibs/authz/package-info.java index 87283fb515..d00aaeaf46 100644 --- a/taglibs/src/main/java/org/springframework/security/taglibs/authz/package-info.java +++ b/taglibs/src/main/java/org/springframework/security/taglibs/authz/package-info.java @@ -17,4 +17,7 @@ /** * JSP Security tag library implementation. */ +@NullMarked package org.springframework.security.taglibs.authz; + +import org.jspecify.annotations.NullMarked; diff --git a/taglibs/src/main/java/org/springframework/security/taglibs/csrf/package-info.java b/taglibs/src/main/java/org/springframework/security/taglibs/csrf/package-info.java new file mode 100644 index 0000000000..2b8d65d55a --- /dev/null +++ b/taglibs/src/main/java/org/springframework/security/taglibs/csrf/package-info.java @@ -0,0 +1,23 @@ +/* + * Copyright 2004-present the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * JSP Security tag library integration with CSRF protection. + */ +@NullMarked +package org.springframework.security.taglibs.csrf; + +import org.jspecify.annotations.NullMarked; diff --git a/taglibs/src/main/java/org/springframework/security/taglibs/package-info.java b/taglibs/src/main/java/org/springframework/security/taglibs/package-info.java index ef9deb5d2a..52ed828f33 100644 --- a/taglibs/src/main/java/org/springframework/security/taglibs/package-info.java +++ b/taglibs/src/main/java/org/springframework/security/taglibs/package-info.java @@ -17,4 +17,7 @@ /** * Security related tag libraries that can be used in JSPs and templates. */ +@NullMarked package org.springframework.security.taglibs; + +import org.jspecify.annotations.NullMarked;