From 73f0474008e1bebd0733e62a22aceda9e5de6743 Mon Sep 17 00:00:00 2001 From: Douglas Palmer Date: Thu, 14 Oct 2021 15:47:57 -0700 Subject: [PATCH] [KEYCLOAK-19422] ClassLoaderTheme and ClasspathThemeResourceProviderFactory allows reading any file available as a resource to the classloader --- .../java/org/keycloak/theme/ClassLoaderTheme.java | 15 +++++++++++++-- .../ClasspathThemeResourceProviderFactory.java | 13 ++++++++++++- .../theme/ThemeResourceProviderTest.java | 12 ++++++++++++ 3 files changed, 37 insertions(+), 3 deletions(-) diff --git a/services/src/main/java/org/keycloak/theme/ClassLoaderTheme.java b/services/src/main/java/org/keycloak/theme/ClassLoaderTheme.java index 82ad64a584..7c013f7422 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 class ClassLoaderTheme implements Theme { } @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 6af4073f2d..1536afcadb 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 class ClasspathThemeResourceProviderFactory implements ThemeResourceProvi @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 24026dac0d..f77355d848 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 class ThemeResourceProviderTest extends AbstractTestRealmKeycloakTest { }); } + @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);