Skip to content

Commit 079719e

Browse files
vaadin-botcaalador
andauthored
fix: Do not serve reserved folders (#22998) (#23033)
Do not run indexhtmlrequesthandler for requests to vaadin reserved folders. Co-authored-by: caalador <mikael.grankvist@vaadin.com>
1 parent 78bbd5d commit 079719e

File tree

9 files changed

+76
-11
lines changed

9 files changed

+76
-11
lines changed

flow-server/src/main/java/com/vaadin/flow/server/BootstrapHandler.java

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -516,7 +516,7 @@ protected boolean canHandleRequest(VaadinRequest request) {
516516
}
517517

518518
if (isVaadinStaticFileRequest(request)) {
519-
// Do not allow routes inside /VAADIN/
519+
// Do not allow routes inside /VAADIN/ or reserved static folders
520520
return false;
521521
}
522522

@@ -557,7 +557,8 @@ public static boolean isFrameworkInternalRequest(VaadinRequest request) {
557557
}
558558

559559
/**
560-
* Checks whether the request is a request for /VAADIN/*.
560+
* Checks whether the request is a request for /VAADIN/* or other reserved
561+
* static folder. (/themes, /assets, /aura, /lumo)
561562
* <p>
562563
* Warning: This assumes that the VaadinRequest is targeted for a
563564
* VaadinServlet and does no further checks to validate this.
@@ -572,8 +573,9 @@ public static boolean isFrameworkInternalRequest(VaadinRequest request) {
572573
* otherwise
573574
*/
574575
public static boolean isVaadinStaticFileRequest(VaadinRequest request) {
575-
return request.getPathInfo() != null
576-
&& request.getPathInfo().startsWith("/" + VAADIN_MAPPING);
576+
return request.getPathInfo() != null && HandlerHelper
577+
.getPublicInternalFolderPaths().stream()
578+
.anyMatch(path -> request.getPathInfo().startsWith(path));
577579
}
578580

579581
/**

flow-server/src/main/java/com/vaadin/flow/server/HandlerHelper.java

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@
3232
import java.util.function.BiConsumer;
3333
import java.util.regex.Pattern;
3434
import java.util.stream.Collectors;
35+
import java.util.stream.Stream;
3536

3637
import com.vaadin.flow.component.UI;
3738
import com.vaadin.flow.server.communication.PwaHandler;
@@ -147,6 +148,8 @@ public String getIdentifier() {
147148

148149
private static final String[] publicResourcesRoot;
149150
private static final String[] publicResources;
151+
private static final List<String> publicInternalFolderPaths;
152+
150153
static {
151154
List<String> resources = new ArrayList<>();
152155
resources.add("/" + PwaConfiguration.DEFAULT_PATH);
@@ -168,6 +171,18 @@ public String getIdentifier() {
168171
rootResources.add("/favicon.ico");
169172
publicResourcesRoot = rootResources
170173
.toArray(new String[rootResources.size()]);
174+
175+
List<String> resourcesPath = new ArrayList<>();
176+
Stream.concat(resources.stream(), rootResources.stream())
177+
.filter(resource -> resource.endsWith("/**"))
178+
.map(resource -> resource.replace("/**", ""))
179+
.forEach(resourcesPath::add);
180+
for (String resource : getPublicResourcesRequiringSecurityContext()) {
181+
if (resource.endsWith("/**")) {
182+
resourcesPath.add(resource.replace("/**", ""));
183+
}
184+
}
185+
publicInternalFolderPaths = resourcesPath;
171186
}
172187

173188
private HandlerHelper() {
@@ -544,6 +559,10 @@ public static String[] getPublicResourcesRoot() {
544559
return publicResourcesRoot;
545560
}
546561

562+
public static List<String> getPublicInternalFolderPaths() {
563+
return publicInternalFolderPaths;
564+
}
565+
547566
/**
548567
* Gets the paths of the PWA icon variants for the given base icon.
549568
*

flow-server/src/main/java/com/vaadin/flow/server/StaticFileServer.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -256,6 +256,9 @@ public boolean serveStaticResource(HttpServletRequest request,
256256
// servletContext.getResource will return a URL for them, at
257257
// least with Jetty
258258
return false;
259+
} else if (HandlerHelper.getPublicInternalFolderPaths().stream()
260+
.anyMatch(path -> filenameWithPath.equals(path))) {
261+
return false;
259262
}
260263

261264
if (HandlerHelper.isPathUnsafe(filenameWithPath)) {

flow-server/src/test/java/com/vaadin/flow/server/StaticFileServerTest.java

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828
import java.io.Serializable;
2929
import java.lang.reflect.Field;
3030
import java.net.MalformedURLException;
31+
import java.net.URI;
3132
import java.net.URISyntaxException;
3233
import java.net.URL;
3334
import java.net.URLConnection;
@@ -1252,6 +1253,30 @@ public void serveStaticResource_themeResourceRequest_productionMode_notServeFrom
12521253
Assert.assertFalse(fileServer.serveStaticResource(request, response));
12531254
}
12541255

1256+
@Test
1257+
public void frameworkStaticFolder_withoutEndingSlash_doesNotServeStaticResource()
1258+
throws IOException {
1259+
// Test framework static folders without / at the end,
1260+
// that they do not return a result
1261+
for (String publicInternalFolderPath : HandlerHelper
1262+
.getPublicInternalFolderPaths()) {
1263+
Assert.assertTrue(publicInternalFolderPath.startsWith("/"));
1264+
Assert.assertFalse(publicInternalFolderPath.endsWith("/**"));
1265+
1266+
setupRequestURI("", "", publicInternalFolderPath);
1267+
Mockito.when(
1268+
servletService.getStaticResource(publicInternalFolderPath))
1269+
.thenReturn(URI
1270+
.create("file:///" + publicInternalFolderPath + "/")
1271+
.toURL());
1272+
1273+
Assert.assertFalse(
1274+
publicInternalFolderPath
1275+
+ " should not be a static resource.",
1276+
fileServer.serveStaticResource(request, response));
1277+
}
1278+
}
1279+
12551280
private static class CapturingServletOutputStream
12561281
extends ServletOutputStream {
12571282
ByteArrayOutputStream baos = new ByteArrayOutputStream();

flow-server/src/test/java/com/vaadin/flow/server/communication/IndexHtmlRequestHandlerTest.java

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,7 @@
6161
import com.vaadin.flow.server.AppShellRegistry;
6262
import com.vaadin.flow.server.BootstrapHandler;
6363
import com.vaadin.flow.server.Constants;
64+
import com.vaadin.flow.server.HandlerHelper;
6465
import com.vaadin.flow.server.MockServletServiceSessionSetup;
6566
import com.vaadin.flow.server.VaadinContext;
6667
import com.vaadin.flow.server.VaadinRequest;
@@ -313,6 +314,18 @@ public void canHandleRequest_doNotHandle_vaadinStaticResources() {
313314
createRequestWithDestination("/VAADIN/foo.js", null, null)));
314315
}
315316

317+
@Test
318+
public void canHandleRequest_doNotHandle_vaadinReservedFolders() {
319+
for (String reservedFolder : HandlerHelper
320+
.getPublicInternalFolderPaths()) {
321+
assertFalse(reservedFolder
322+
+ " was handled even though it should not init index handler",
323+
indexHtmlRequestHandler.canHandleRequest(
324+
createRequestWithDestination(reservedFolder, null,
325+
null)));
326+
}
327+
}
328+
316329
@Test
317330
public void canHandleRequest_handle_serviceWorkerDocumentRequest() {
318331
Assert.assertTrue(indexHtmlRequestHandler.canHandleRequest(

flow-tests/test-application-theme/test-theme-reusable-vite/src/test/java/com/vaadin/flow/uitest/ui/theme/ReusableThemeIT.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@ public void secondTheme_staticFilesNotCopied() {
5252
getDriver().get(getRootURL() + "/path/themes/no-copy/no-copy.txt");
5353
String source = driver.getPageSource();
5454
Matcher m = Pattern.compile(
55-
".*Could not navigate to.*themes/no-copy/no-copy.txt.*",
55+
".*HTTP ERROR 404 Request was not handled by any registered handler.*",
5656
Pattern.DOTALL).matcher(source);
5757
Assert.assertTrue("no-copy theme should not be handled", m.matches());
5858
}

flow-tests/test-custom-frontend-directory/test-themes-custom-frontend-directory/src/test/java/com/vaadin/flow/uitest/ui/theme/ThemeIT.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -100,7 +100,7 @@ public void secondTheme_staticFilesNotCopied() {
100100
waitForDevServer();
101101
String source = driver.getPageSource();
102102
Matcher m = Pattern.compile(
103-
".*Could not navigate to.*themes/no-copy/no-copy.txt.*",
103+
".*HTTP ERROR 404 Request was not handled by any registered handler.*",
104104
Pattern.DOTALL).matcher(source);
105105
Assert.assertTrue("no-copy theme should not be handled", m.matches());
106106
}

flow-tests/test-frontend/vite-pwa/src/test/java/com/vaadin/viteapp/MainIT.java

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,9 @@
2424
import com.vaadin.flow.testutil.ChromeDeviceTest;
2525

2626
public class MainIT extends ChromeDeviceTest {
27-
final String VITE_PING_PATH = "/VAADIN";
27+
28+
// Ping actual existing file and not no route page, which now returns 404
29+
final String VITE_PING_PATH = "/VAADIN/generated/";
2830

2931
@Before
3032
public void init() {
@@ -48,12 +50,13 @@ public void openHomePage_setOffline_vitePingRequestIsRejected() {
4850
openPage("/");
4951

5052
Assert.assertTrue("Should allow Vite ping requests when online",
51-
sendVitePingRequest());
53+
sendVitePingRequest(VITE_PING_PATH + "vaadin.ts"));
5254

5355
getDevTools().setOfflineEnabled(true);
5456

57+
// Different file to not get cached.
5558
Assert.assertFalse("Should reject Vite ping requests when offline",
56-
sendVitePingRequest());
59+
sendVitePingRequest(VITE_PING_PATH + "theme.js"));
5760
}
5861

5962
@Test
@@ -103,7 +106,7 @@ private Boolean isAppThemeLoaded() {
103106
"return getComputedStyle(document.body).getPropertyValue('--theme-my-theme-loaded') === '1'");
104107
}
105108

106-
private Boolean sendVitePingRequest() {
109+
private Boolean sendVitePingRequest(String VITE_PING_PATH) {
107110
return (Boolean) ((JavascriptExecutor) getDriver())
108111
.executeAsyncScript(
109112
"const done = arguments[arguments.length - 1];"

flow-tests/test-themes/src/test/java/com/vaadin/flow/uitest/ui/theme/ThemeIT.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -102,7 +102,7 @@ public void secondTheme_staticFilesNotCopied() {
102102
getDriver().get(getRootURL() + "/path/themes/no-copy/no-copy.txt");
103103
String source = driver.getPageSource();
104104
Matcher m = Pattern.compile(
105-
".*Could not navigate to.*themes/no-copy/no-copy.txt.*",
105+
".*HTTP ERROR 404 Request was not handled by any registered handler.*",
106106
Pattern.DOTALL).matcher(source);
107107
Assert.assertTrue("no-copy theme should not be handled", m.matches());
108108
}

0 commit comments

Comments
 (0)