This is an automated email from the ASF dual-hosted git repository.

lprimak pushed a commit to branch 3.x
in repository https://gitbox.apache.org/repos/asf/shiro.git

commit 5ed8730eaf86736922aa74eb6703969cc80da20e
Author: lprimak <[email protected]>
AuthorDate: Thu May 21 19:34:39 2026 -0500

    enh(jakarta-ee): strip out the host part of the referer header
---
 .../shiro/ee/filters/FormResubmitSupport.java      |  28 ++++-
 .../apache/shiro/ee/filters/FormSupportTest.java   | 137 +++++++++++++++++++--
 2 files changed, 150 insertions(+), 15 deletions(-)

diff --git 
a/support/jakarta-ee/src/main/java/org/apache/shiro/ee/filters/FormResubmitSupport.java
 
b/support/jakarta-ee/src/main/java/org/apache/shiro/ee/filters/FormResubmitSupport.java
index 695165a72..02c9164de 100644
--- 
a/support/jakarta-ee/src/main/java/org/apache/shiro/ee/filters/FormResubmitSupport.java
+++ 
b/support/jakarta-ee/src/main/java/org/apache/shiro/ee/filters/FormResubmitSupport.java
@@ -258,13 +258,31 @@ public class FormResubmitSupport {
 
     static String getReferer(HttpServletRequest request) {
         String referer = request.getHeader("referer");
-        if (referer != null) {
-            // do not switch to https if custom port is specified
-            if 
(!referer.matches("^http:\\/\\/[A-z|.|[0-9]]+:[0-9]+(\\/.*|$)")) {
-                referer = referer.replaceFirst("^http:", "https:");
+        if (referer == null || referer.isBlank()) {
+            return null;
+        }
+
+        try {
+            URI uri = URI.create(referer);
+
+            String contextPath = WebUtils.getContextPath(request);
+            String path = WebUtils.normalize(uri.getPath());
+
+            if (path == null) {
+                return null;
             }
+
+            if (!contextPath.isEmpty()
+                    && !path.equals(contextPath)
+                    && !path.startsWith(contextPath + "/")) {
+                return null;
+            }
+
+            String query = uri.getRawQuery();
+            return query == null ? path : path + "?" + query;
+        } catch (IllegalArgumentException e) {
+            return null;
         }
-        return referer;
     }
 
     /**
diff --git 
a/support/jakarta-ee/src/test/java/org/apache/shiro/ee/filters/FormSupportTest.java
 
b/support/jakarta-ee/src/test/java/org/apache/shiro/ee/filters/FormSupportTest.java
index 8272823d8..99dcfdb64 100644
--- 
a/support/jakarta-ee/src/test/java/org/apache/shiro/ee/filters/FormSupportTest.java
+++ 
b/support/jakarta-ee/src/test/java/org/apache/shiro/ee/filters/FormSupportTest.java
@@ -53,28 +53,145 @@ class FormSupportTest {
         assertThat(getReferer(request)).isNull();
     }
 
+    @Test
+    void blankReferer() {
+        when(request.getHeader("referer")).thenReturn("   ");
+        assertThat(getReferer(request)).isNull();
+    }
+
     @Test
     void plainStringReferer() {
         when(request.getHeader("referer")).thenReturn("hello");
-        assertThat(getReferer(request)).isEqualTo("hello");
+        when(request.getContextPath()).thenReturn("/myapp");
+        assertThat(getReferer(request)).isNull();
+    }
+
+    @Test
+    void malformedReferer() {
+        when(request.getHeader("referer")).thenReturn("http://exa mple.com");
+        assertThat(getReferer(request)).isNull();
+    }
+
+    @Test
+    void refererWithinContextPath() {
+        
when(request.getHeader("referer")).thenReturn("https://example.com/myapp/login.xhtml";);
+        when(request.getContextPath()).thenReturn("/myapp");
+
+        assertThat(getReferer(request)).isEqualTo("/myapp/login.xhtml");
+    }
+
+    @Test
+    void refererWithinContextPathWithQuery() {
+        
when(request.getHeader("referer")).thenReturn("https://example.com/myapp/login.xhtml?a=1&b=2";);
+        when(request.getContextPath()).thenReturn("/myapp");
+
+        
assertThat(getReferer(request)).isEqualTo("/myapp/login.xhtml?a=1&b=2");
     }
 
     @Test
-    void switchToHttps() {
-        when(request.getHeader("referer")).thenReturn("http://example.com";);
-        assertThat(getReferer(request)).isEqualTo("https://example.com";);
+    void refererEqualToContextPathBecomesRoot() {
+        
when(request.getHeader("referer")).thenReturn("https://example.com/myapp";);
+        when(request.getContextPath()).thenReturn("/myapp");
+
+        assertThat(getReferer(request)).isEqualTo("/myapp");
     }
 
     @Test
-    void dontSwitchToHttpsWhenCustomPort() {
-        
when(request.getHeader("referer")).thenReturn("http://example.com:8080/";);
-        assertThat(getReferer(request)).isEqualTo("http://example.com:8080/";);
+    void refererOutsideContextPathIsRejected() {
+        
when(request.getHeader("referer")).thenReturn("https://example.com/otherapp/login.xhtml";);
+        when(request.getContextPath()).thenReturn("/myapp");
+
+        assertThat(getReferer(request)).isNull();
     }
 
     @Test
-    void dontSwitchToHttpsWhenCustomPortNoTrailingSlash() {
-        
when(request.getHeader("referer")).thenReturn("http://example.com:8080";);
-        assertThat(getReferer(request)).isEqualTo("http://example.com:8080";);
+    void rootContextKeepsPath() {
+        
when(request.getHeader("referer")).thenReturn("https://example.com/login.xhtml";);
+        when(request.getContextPath()).thenReturn("");
+
+        assertThat(getReferer(request)).isEqualTo("/login.xhtml");
+    }
+
+    @Test
+    void rootContextKeepsPathWithQuery() {
+        
when(request.getHeader("referer")).thenReturn("https://example.com/login.xhtml?x=1";);
+        when(request.getContextPath()).thenReturn("");
+
+        assertThat(getReferer(request)).isEqualTo("/login.xhtml?x=1");
+    }
+
+    @Test
+    void normalizedPathWithinContextIsAccepted() {
+        
when(request.getHeader("referer")).thenReturn("https://example.com/myapp//foo/./bar.xhtml";);
+        when(request.getContextPath()).thenReturn("/myapp");
+
+        assertThat(getReferer(request)).isEqualTo("/myapp/foo/bar.xhtml");
+    }
+
+    @Test
+    void normalizedPathEscapingContextIsRejected() {
+        
when(request.getHeader("referer")).thenReturn("https://example.com/myapp/../otherapp/page.xhtml";);
+        when(request.getContextPath()).thenReturn("/myapp");
+
+        assertThat(getReferer(request)).isNull();
+    }
+
+    @Test
+    void opaqueUriRefererIsRejected() {
+        
when(request.getHeader("referer")).thenReturn("mailto:[email protected]";);
+        when(request.getContextPath()).thenReturn("/myapp");
+
+        assertThat(getReferer(request)).isNull();
+    }
+
+    @Test
+    void javascriptUriRefererIsRejected() {
+        when(request.getHeader("referer")).thenReturn("javascript:alert(1)");
+        when(request.getContextPath()).thenReturn("/myapp");
+
+        assertThat(getReferer(request)).isNull();
+    }
+
+    @Test
+    void contextPathPrefixMatchRequiresPathBoundary() {
+        
when(request.getHeader("referer")).thenReturn("https://example.com/myapplication/page.xhtml";);
+        when(request.getContextPath()).thenReturn("/myapp");
+
+        assertThat(getReferer(request)).isNull();
+    }
+
+    @Test
+    void refererWithFragmentDropsFragmentAndKeepsQueryOnly() {
+        
when(request.getHeader("referer")).thenReturn("https://example.com/myapp/page.xhtml?a=1#frag";);
+        when(request.getContextPath()).thenReturn("/myapp");
+
+        assertThat(getReferer(request)).isEqualTo("/myapp/page.xhtml?a=1");
+    }
+
+    @Test
+    void externalHostWithMatchingContextCurrentlyPasses() {
+        
when(request.getHeader("referer")).thenReturn("https://attacker.example/myapp/login.xhtml";);
+        when(request.getContextPath()).thenReturn("/myapp");
+
+        assertThat(getReferer(request)).isEqualTo("/myapp/login.xhtml");
+    }
+
+    @Test
+    void encodedPathTraversalRefererIsRejected() {
+        when(request.getHeader("referer"))
+                
.thenReturn("https://example.com/myapp/%2e%2e/otherapp/page.xhtml";);
+        when(request.getContextPath()).thenReturn("/myapp");
+
+        assertThat(getReferer(request)).isNull();
+    }
+
+    @Test
+    void encodedPathTraversalWithEncodedSlashesRefererIsRejected() {
+        when(request.getHeader("referer"))
+                
.thenReturn("https://example.com/myapp/%2e%2e%2fotherapp%2fpage.xhtml";);
+        when(request.getContextPath()).thenReturn("/myapp");
+
+        assertThat(getReferer(request)).isNull();
     }
 
     @Test

Reply via email to