diff --git a/services/src/main/java/org/keycloak/theme/ClassLoaderTheme.java b/services/src/main/java/org/keycloak/theme/ClassLoaderTheme.java index 82ad64a5842..7c013f74222 100644 --- a/services/src/main/java/org/keycloak/theme/ClassLoaderTheme.java +++ b/services/src/main/java/org/keycloak/theme/ClassLoaderTheme.java @@ -105,8 +105,19 @@ public URL getTemplate(String name) { } @Override - public InputStream getResourceAsStream(String path) { - return classLoader.getResourceAsStream(resourceRoot + path); + public InputStream getResourceAsStream(String path) throws IOException { + final URL rootResourceURL = classLoader.getResource(resourceRoot); + if (rootResourceURL == null) { + return null; + } + final String rootPath = rootResourceURL.getPath(); + final URL resourceURL = classLoader.getResource(resourceRoot + path); + if(resourceURL == null || !resourceURL.getPath().startsWith(rootPath)) { + return null; + } + else { + return resourceURL.openConnection().getInputStream(); + } } @Override diff --git a/services/src/main/java/org/keycloak/theme/ClasspathThemeResourceProviderFactory.java b/services/src/main/java/org/keycloak/theme/ClasspathThemeResourceProviderFactory.java index 6af4073f2d1..1536afcadbe 100644 --- a/services/src/main/java/org/keycloak/theme/ClasspathThemeResourceProviderFactory.java +++ b/services/src/main/java/org/keycloak/theme/ClasspathThemeResourceProviderFactory.java @@ -39,7 +39,18 @@ public URL getTemplate(String name) throws IOException { @Override public InputStream getResourceAsStream(String path) throws IOException { - return classLoader.getResourceAsStream(THEME_RESOURCES_RESOURCES + path); + final URL rootResourceURL = classLoader.getResource(THEME_RESOURCES_RESOURCES); + if (rootResourceURL == null) { + return null; + } + final String rootPath = rootResourceURL.getPath(); + final URL resourceURL = classLoader.getResource(THEME_RESOURCES_RESOURCES + path); + if(resourceURL == null || !resourceURL.getPath().startsWith(rootPath)) { + return null; + } + else { + return resourceURL.openConnection().getInputStream(); + } } @Override diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/theme/ThemeResourceProviderTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/theme/ThemeResourceProviderTest.java index 24026dac0d6..f77355d848d 100644 --- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/theme/ThemeResourceProviderTest.java +++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/theme/ThemeResourceProviderTest.java @@ -73,6 +73,18 @@ public void getMessages() { }); } + @Test + public void getResourceIllegalTraversal() { + testingClient.server().run(session -> { + try { + Theme theme = session.theme().getTheme("base", Theme.Type.LOGIN); + Assert.assertNull(theme.getResourceAsStream("../templates/test.ftl")); + } catch (IOException e) { + Assert.fail(e.getMessage()); + } + }); + } + @Test public void gzipEncoding() throws IOException { final String resourcesVersion = testingClient.server().fetch(session -> Version.RESOURCES_VERSION, String.class);