Skip to content

Commit d802af6

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

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
@@ -517,7 +517,7 @@ protected boolean canHandleRequest(VaadinRequest request) {
517517
}
518518

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

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

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

580582
/**

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
@@ -30,6 +30,7 @@
3030
import java.util.function.BiConsumer;
3131
import java.util.regex.Pattern;
3232
import java.util.stream.Collectors;
33+
import java.util.stream.Stream;
3334

3435
import jakarta.servlet.http.HttpServletRequest;
3536

@@ -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);
@@ -164,6 +167,18 @@ public String getIdentifier() {
164167
rootResources.add("/favicon.ico");
165168
publicResourcesRoot = rootResources
166169
.toArray(new String[rootResources.size()]);
170+
171+
List<String> resourcesPath = new ArrayList<>();
172+
Stream.concat(resources.stream(), rootResources.stream())
173+
.filter(resource -> resource.endsWith("/**"))
174+
.map(resource -> resource.replace("/**", ""))
175+
.forEach(resourcesPath::add);
176+
for (String resource : getPublicResourcesRequiringSecurityContext()) {
177+
if (resource.endsWith("/**")) {
178+
resourcesPath.add(resource.replace("/**", ""));
179+
}
180+
}
181+
publicInternalFolderPaths = resourcesPath;
167182
}
168183

169184
private HandlerHelper() {
@@ -518,6 +533,10 @@ public static String[] getPublicResourcesRoot() {
518533
return publicResourcesRoot;
519534
}
520535

536+
public static List<String> getPublicInternalFolderPaths() {
537+
return publicInternalFolderPaths;
538+
}
539+
521540
/**
522541
* Gets the paths of the PWA icon variants for the given base icon.
523542
*

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
@@ -252,6 +252,9 @@ public boolean serveStaticResource(HttpServletRequest request,
252252
// servletContext.getResource will return a URL for them, at
253253
// least with Jetty
254254
return false;
255+
} else if (HandlerHelper.getPublicInternalFolderPaths().stream()
256+
.anyMatch(path -> filenameWithPath.equals(path))) {
257+
return false;
255258
}
256259

257260
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;
@@ -1257,6 +1258,30 @@ public void serveStaticResource_themeResourceRequest_productionMode_notServeFrom
12571258
Assert.assertFalse(fileServer.serveStaticResource(request, response));
12581259
}
12591260

1261+
@Test
1262+
public void frameworkStaticFolder_withoutEndingSlash_doesNotServeStaticResource()
1263+
throws IOException {
1264+
// Test framework static folders without / at the end,
1265+
// that they do not return a result
1266+
for (String publicInternalFolderPath : HandlerHelper
1267+
.getPublicInternalFolderPaths()) {
1268+
Assert.assertTrue(publicInternalFolderPath.startsWith("/"));
1269+
Assert.assertFalse(publicInternalFolderPath.endsWith("/**"));
1270+
1271+
setupRequestURI("", "", publicInternalFolderPath);
1272+
Mockito.when(
1273+
servletService.getStaticResource(publicInternalFolderPath))
1274+
.thenReturn(URI
1275+
.create("file:///" + publicInternalFolderPath + "/")
1276+
.toURL());
1277+
1278+
Assert.assertFalse(
1279+
publicInternalFolderPath
1280+
+ " should not be a static resource.",
1281+
fileServer.serveStaticResource(request, response));
1282+
}
1283+
}
1284+
12601285
private static class CapturingServletOutputStream
12611286
extends ServletOutputStream {
12621287
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
@@ -58,6 +58,7 @@
5858
import com.vaadin.flow.server.AppShellRegistry;
5959
import com.vaadin.flow.server.BootstrapHandler;
6060
import com.vaadin.flow.server.Constants;
61+
import com.vaadin.flow.server.HandlerHelper;
6162
import com.vaadin.flow.server.MockServletServiceSessionSetup;
6263
import com.vaadin.flow.server.VaadinContext;
6364
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)