Copilot commented on code in PR #16332:
URL: https://github.com/apache/grails-core/pull/16332#discussion_r3969787285
##########
grails-spring-security/plugin/src/main/groovy/grails/plugin/springsecurity/web/filter/IpAddressFilter.groovy:
##########
@@ -138,6 +134,44 @@ class IpAddressFilter extends GenericFilterBean {
false
}
+ /**
+ * Resolves the path that restriction patterns are matched against, in the
form Grails URL mapping dispatch
+ * resolves it: path parameters removed per segment before
percent-decoding (Jakarta Servlet 6.0 section 3.5.2),
+ * decoded exactly once (RFC 3986 section 2.4) and relative to the context
path, so an encoded or matrix-parameter
+ * variant of a restricted path is subject to the same restriction. For a
forwarded request the original request
+ * URI is checked, canonicalized the same way. Like dispatch, and unlike
RFC 3986 section 6.2.2.1, the context
+ * path is compared case-insensitively. A URI with an illegal percent
escape, which a Servlet 6.0 container
+ * rejects with 400 before the filter chain runs, is matched undecoded
rather than aborting the chain.
+ */
+ protected String getPathWithinApplication(HttpServletRequest request) {
+ String forwardUri =
request.getAttribute(WebUtils.FORWARD_REQUEST_URI_ATTRIBUTE) as String
+ if (!forwardUri) {
+ try {
+ return urlPathHelper.getPathWithinApplication(request)
+ } catch (IllegalArgumentException ignored) {
+ return rawUrlPathHelper.getPathWithinApplication(request)
+ }
+ }
+ String path = urlPathHelper.removeSemicolonContent(forwardUri)
+ String contextPath =
(request.getAttribute(WebUtils.FORWARD_CONTEXT_PATH_ATTRIBUTE) as String) ?:
request.contextPath
+ try {
+ path = urlPathHelper.decodeRequestString(request, path)
+ contextPath = urlPathHelper.decodeRequestString(request,
contextPath)
+ } catch (IllegalArgumentException ignored) {
+ // illegal percent escape: match the undecoded path
+ }
+ if (contextPath && contextPath != '/' && path.regionMatches(true, 0,
contextPath, 0, contextPath.length())) {
+ path = path.substring(contextPath.length())
+ }
+ path ?: '/'
+ }
+
Review Comment:
In the forwarded-URI branch, the code removes matrix params and decodes but
does not appear to apply the same path sanitization that
`UrlPathHelper.getPathWithinApplication(request)` applies (notably collapsing
duplicate slashes as referenced elsewhere in this PR’s tests/docs). This can
reintroduce a dispatch/matcher inconsistency specifically for forwarded
requests. Consider applying the same sanitization rules used by `UrlPathHelper`
(e.g., duplicate-slash collapse / path sanitization) to `forwardUri` before
matching restrictions, so forward matching stays aligned with dispatch
semantics.
##########
grails-interceptors/src/main/groovy/grails/artefact/Interceptor.groovy:
##########
@@ -89,25 +107,21 @@ trait Interceptor implements ResponseRenderer,
ResponseRedirector, RequestForwar
allMatchers << matcher
}
- HttpServletRequest req = request
- String ctxPath = req.contextPath
- String uri = req.requestURI
- String noCtxUri = uri - ctxPath
- boolean checkNoCtxUri = ctxPath && uri.startsWith(ctxPath)
-
- def matchedInfo =
request.getAttribute(UrlMappingsHandlerMapping.MATCHED_REQUEST)
-
- UrlMappingInfo grailsMappingInfo = (UrlMappingInfo) matchedInfo
+ String uri
+ try {
+ uri =
UrlPathHelper.defaultInstance.getPathWithinApplication(request)
+ } catch (IllegalArgumentException ignored) {
+ // illegal percent escape: match the undecoded path rather than
fail
+ UrlPathHelper rawPathHelper = new UrlPathHelper()
+ rawPathHelper.urlDecode = false
+ uri = rawPathHelper.getPathWithinApplication(request)
+ }
Review Comment:
The exception path allocates and configures a new `UrlPathHelper` each time
an illegal percent escape is encountered. Since this is attacker-controlled
input, repeated requests could create avoidable allocation pressure. Consider
reusing a static/shared `UrlPathHelper` configured with `urlDecode = false`
(similar to the approach used in `AntPathRequestMatcher`) to keep behavior
consistent and avoid per-request object creation.
##########
grails-spring-security/plugin/src/test/groovy/grails/plugin/springsecurity/web/filter/IpAddressFilterSpec.groovy:
##########
@@ -21,6 +21,8 @@ package grails.plugin.springsecurity.web.filter
import jakarta.servlet.FilterChain
import grails.plugin.springsecurity.AbstractUnitSpec
+import org.grails.web.util.WebUtils
+import spock.lang.Unroll
Review Comment:
This spec uses `org.grails.web.util.WebUtils` constants for forward/include
attribute keys. To avoid accidental mismatch with the production filter’s
attribute keys (which are often sourced from Spring’s
`org.springframework.web.util.WebUtils` or `jakarta.servlet.RequestDispatcher`
constants), consider importing and using the same constants source as the
production code (or `RequestDispatcher`), so the test is guaranteed to set the
exact attributes the filter reads.
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: [email protected]
For queries about this service, please contact Infrastructure at:
[email protected]