From 399b9c15254b4dfaaddecee8e85d066fc013c01a Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sat, 1 Apr 2023 11:19:39 +0300 Subject: [PATCH] Bump react-router-dom from 6.7.0 to 6.10.0 in /Plan/react/dashboard (#2953) * Bump react-router-dom from 6.7.0 to 6.10.0 in /Plan/react/dashboard Bumps [react-router-dom](https://github.com/remix-run/react-router/tree/HEAD/packages/react-router-dom) from 6.7.0 to 6.10.0. - [Release notes](https://github.com/remix-run/react-router/releases) - [Changelog](https://github.com/remix-run/react-router/blob/main/packages/react-router-dom/CHANGELOG.md) - [Commits](https://github.com/remix-run/react-router/commits/react-router-dom@6.10.0/packages/react-router-dom) --- updated-dependencies: - dependency-name: react-router-dom dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] * Fix Open URI redirect with login redirect Affects issues: - Fixed #2747 * Tested open redirection and fixed addresses that bypassed the protection --------- Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Aurora Lahtela <24460436+AuroraLS3@users.noreply.github.com> --- .../delivery/export/ExportTestUtilities.java | 12 +- .../delivery/webserver/HttpsServerTest.java | 30 ++- .../webserver/OpenRedirectFuzzTest.java | 173 ++++++++++++ .../java/extension/SeleniumExtension.java | 1 + .../fuzzing/Open-Redirect-payloads.txt | 246 ++++++++++++++++++ .../components/navigation/MainPageRedirect.js | 8 +- .../dashboard/src/views/layout/LoginPage.js | 20 +- .../dashboard/src/views/layout/NetworkPage.js | 4 +- .../dashboard/src/views/layout/PlayerPage.js | 4 + .../dashboard/src/views/layout/PlayersPage.js | 4 + .../dashboard/src/views/layout/QueryPage.js | 4 + Plan/react/dashboard/yarn.lock | 28 +- 12 files changed, 506 insertions(+), 28 deletions(-) create mode 100644 Plan/common/src/test/java/com/djrapitops/plan/delivery/webserver/OpenRedirectFuzzTest.java create mode 100644 Plan/common/src/test/resources/fuzzing/Open-Redirect-payloads.txt diff --git a/Plan/common/src/test/java/com/djrapitops/plan/delivery/export/ExportTestUtilities.java b/Plan/common/src/test/java/com/djrapitops/plan/delivery/export/ExportTestUtilities.java index cd584f16e..d7fb361e2 100644 --- a/Plan/common/src/test/java/com/djrapitops/plan/delivery/export/ExportTestUtilities.java +++ b/Plan/common/src/test/java/com/djrapitops/plan/delivery/export/ExportTestUtilities.java @@ -78,7 +78,7 @@ public class ExportTestUtilities { assertTrue(loggedLines.isEmpty(), () -> "Browser console included " + loggedLines.size() + " logs: " + loggedLines); } - static Optional getElement(ChromeDriver driver) { + public static Optional getMainPageElement(ChromeDriver driver) { try { return Optional.of(driver.findElement(By.className("load-in"))); } catch (NoSuchElementException e) { @@ -86,6 +86,14 @@ public class ExportTestUtilities { } } + public static Optional getElementById(ChromeDriver driver, String id) { + try { + return Optional.of(driver.findElement(By.id(id))); + } catch (NoSuchElementException e) { + return Optional.empty(); + } + } + public static List getLogsAfterRequestToAddress(ChromeDriver driver, String address) { System.out.println("GET: " + address); driver.get(address); @@ -97,7 +105,7 @@ public class ExportTestUtilities { Awaitility.await() .atMost(Duration.of(10, ChronoUnit.SECONDS)) - .until(() -> getElement(driver).map(WebElement::isDisplayed).orElse(false)); + .until(() -> getMainPageElement(driver).map(WebElement::isDisplayed).orElse(false)); List logs = new ArrayList<>(); logs.addAll(driver.manage().logs().get(LogType.CLIENT).getAll()); diff --git a/Plan/common/src/test/java/com/djrapitops/plan/delivery/webserver/HttpsServerTest.java b/Plan/common/src/test/java/com/djrapitops/plan/delivery/webserver/HttpsServerTest.java index 1609bad3f..0b7676ece 100644 --- a/Plan/common/src/test/java/com/djrapitops/plan/delivery/webserver/HttpsServerTest.java +++ b/Plan/common/src/test/java/com/djrapitops/plan/delivery/webserver/HttpsServerTest.java @@ -27,7 +27,7 @@ import java.net.HttpURLConnection; import java.security.KeyManagementException; import java.security.NoSuchAlgorithmException; -import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.junit.jupiter.api.Assertions.*; interface HttpsServerTest { @@ -78,21 +78,35 @@ interface HttpsServerTest { } default String login(String address) throws IOException, KeyManagementException, NoSuchAlgorithmException { - HttpURLConnection loginConnection = null; + HttpURLConnection connection = null; String cookie = ""; try { - loginConnection = connector.getConnection("POST", address + "/auth/login"); - loginConnection.setDoOutput(true); - loginConnection.getOutputStream().write("user=test&password=testPass".getBytes()); - try (InputStream in = loginConnection.getInputStream()) { + connection = connector.getConnection("POST", address + "/auth/login"); + connection.setDoOutput(true); + connection.getOutputStream().write("user=test&password=testPass".getBytes()); + try (InputStream in = connection.getInputStream()) { String responseBody = new String(IOUtils.toByteArray(in)); assertTrue(responseBody.contains("\"success\":true"), () -> "Not successful: " + responseBody); - cookie = loginConnection.getHeaderField("Set-Cookie").split(";")[0]; + cookie = connection.getHeaderField("Set-Cookie").split(";")[0]; System.out.println("Got cookie: " + cookie); } } finally { - loginConnection.disconnect(); + assertNotNull(connection); + connection.disconnect(); } return cookie; } + + default void logout(String address, String cookie) throws IOException, KeyManagementException, NoSuchAlgorithmException { + HttpURLConnection connection = null; + try { + connection = connector.getConnection("POST", address + "/auth/logout"); + connection.setRequestProperty("Cookie", cookie); + int responseCode = connection.getResponseCode(); + assertEquals(302, responseCode, () -> "Logout not redirecting, got response code " + responseCode); + } finally { + assertNotNull(connection); + connection.disconnect(); + } + } } \ No newline at end of file diff --git a/Plan/common/src/test/java/com/djrapitops/plan/delivery/webserver/OpenRedirectFuzzTest.java b/Plan/common/src/test/java/com/djrapitops/plan/delivery/webserver/OpenRedirectFuzzTest.java new file mode 100644 index 000000000..13edeb8ce --- /dev/null +++ b/Plan/common/src/test/java/com/djrapitops/plan/delivery/webserver/OpenRedirectFuzzTest.java @@ -0,0 +1,173 @@ +/* + * This file is part of Player Analytics (Plan). + * + * Plan is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License v3 as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Plan is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Plan. If not, see . + */ +package com.djrapitops.plan.delivery.webserver; + +import com.djrapitops.plan.PlanSystem; +import com.djrapitops.plan.delivery.domain.auth.User; +import com.djrapitops.plan.delivery.webserver.http.WebServer; +import com.djrapitops.plan.settings.config.PlanConfig; +import com.djrapitops.plan.settings.config.paths.WebserverSettings; +import com.djrapitops.plan.storage.database.transactions.commands.StoreWebUserTransaction; +import com.djrapitops.plan.utilities.PassEncryptUtil; +import extension.SeleniumExtension; +import org.junit.jupiter.api.*; +import org.junit.jupiter.api.extension.ExtendWith; +import org.junit.jupiter.api.io.TempDir; +import org.openqa.selenium.By; +import org.openqa.selenium.JavascriptExecutor; +import org.openqa.selenium.WebDriver; +import org.openqa.selenium.WebElement; +import org.openqa.selenium.chrome.ChromeDriver; +import org.openqa.selenium.support.ui.WebDriverWait; +import org.testcontainers.shaded.org.awaitility.Awaitility; +import org.testcontainers.shaded.org.awaitility.core.ConditionTimeoutException; +import utilities.RandomData; +import utilities.TestResources; +import utilities.mocks.PluginMockComponent; + +import java.io.File; +import java.net.URLEncoder; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.StandardCopyOption; +import java.time.Duration; +import java.time.temporal.ChronoUnit; +import java.util.Collection; +import java.util.Collections; +import java.util.List; +import java.util.stream.Stream; + +import static com.djrapitops.plan.delivery.export.ExportTestUtilities.getElementById; +import static com.djrapitops.plan.delivery.export.ExportTestUtilities.getMainPageElement; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; + +@ExtendWith(SeleniumExtension.class) +@Disabled("This test can take 10 minutes to run so it's not enabled on the CI") +class OpenRedirectFuzzTest implements HttpsServerTest { + + private static final int TEST_PORT_NUMBER = RandomData.randomInt(9005, 9500); + + private static PlanSystem system; + private static List payloads; + + @BeforeAll + static void setUpClass(@TempDir Path tempDir) throws Exception { + File file = tempDir.resolve("TestCert.p12").toFile(); + File testCert = TestResources.getTestResourceFile("TestCert.p12", OpenRedirectFuzzTest.class); + Files.copy(testCert.toPath(), file.toPath(), StandardCopyOption.REPLACE_EXISTING); + + String absolutePath = file.getAbsolutePath(); + + PluginMockComponent component = new PluginMockComponent(tempDir); + system = component.getPlanSystem(); + + PlanConfig config = system.getConfigSystem().getConfig(); + + config.set(WebserverSettings.CERTIFICATE_PATH, absolutePath); + config.set(WebserverSettings.CERTIFICATE_KEYPASS, "test"); + config.set(WebserverSettings.CERTIFICATE_STOREPASS, "test"); + config.set(WebserverSettings.CERTIFICATE_ALIAS, "test"); + + config.set(WebserverSettings.PORT, TEST_PORT_NUMBER); + + system.enable(); + + User user = new User("test", "console", null, PassEncryptUtil.createHash("testPass"), 0, Collections.emptyList()); + system.getDatabaseSystem().getDatabase().executeTransaction(new StoreWebUserTransaction(user)); + + loadPayloads(); + } + + private static void loadPayloads() throws Exception { + File payloads = TestResources.getTestResourceFile("fuzzing/Open-Redirect-payloads.txt", OpenRedirectFuzzTest.class); + try (Stream lines = Files.lines(payloads.toPath())) { + OpenRedirectFuzzTest.payloads = lines.toList(); + } + } + + private static void waitForLoginPageToLoad(ChromeDriver driver, String address) { + new WebDriverWait(driver, Duration.of(1, ChronoUnit.SECONDS)).until( + webDriver -> ((JavascriptExecutor) webDriver).executeScript("return document.readyState").equals("complete")); + Awaitility.await() + .alias("Login page didn't open using payload " + address) + .atMost(Duration.of(1, ChronoUnit.SECONDS)) + .until(() -> getElementById(driver, "inputUser").map(WebElement::isDisplayed).orElse(false)); + } + + @AfterAll + static void tearDownClass() { + if (system != null) { + system.disable(); + } + } + + @AfterEach + void clearBrowserConsole(WebDriver driver) { + SeleniumExtension.newTab(driver); + } + + @TestFactory + Collection openRedirectFuzzTest(ChromeDriver driver) { + assertFalse(payloads.isEmpty()); + + int testPortNumber = testPortNumber(); + return payloads.stream() + .map(payload -> payload.replace("$PORT", Integer.toString(testPortNumber))) + .flatMap(payload -> Stream.of(payload, URLEncoder.encode(payload, StandardCharsets.UTF_8))) + .map(payload -> DynamicTest.dynamicTest("Login has no open redirect vulnerability '" + payload + "'", () -> { + String address = "https://localhost:" + testPortNumber; + try { + String loginPageAddress = address + "/login?from=" + payload; + + driver.get(loginPageAddress); + + waitForLoginPageToLoad(driver, loginPageAddress); + + driver.findElement(By.id("inputUser")).sendKeys("test"); + driver.findElement(By.id("inputPassword")).sendKeys("testPass"); + driver.findElement(By.id("login-button")).click(); + + try { + Awaitility.await() + .atMost(Duration.of(1, ChronoUnit.SECONDS)) + .until(() -> getMainPageElement(driver).map(WebElement::isDisplayed).orElse(false)); + } catch (ConditionTimeoutException e) { + String currentUrl = driver.getCurrentUrl(); + assertTrue(currentUrl.startsWith(address), () -> payload + " redirected to " + currentUrl + " which should not have happened!"); + } + + String currentUrl = driver.getCurrentUrl(); + assertTrue(currentUrl.startsWith(address), () -> payload + " redirected to " + currentUrl + " which should not have happened!"); + } finally { + driver.get(address + "/auth/logout"); + } + })) + .toList(); + } + + @Override + public WebServer getWebServer() { + return system.getWebServerSystem().getWebServer(); + } + + @Override + public int testPortNumber() { + return TEST_PORT_NUMBER; + } +} \ No newline at end of file diff --git a/Plan/common/src/test/java/extension/SeleniumExtension.java b/Plan/common/src/test/java/extension/SeleniumExtension.java index 953895078..110297f18 100644 --- a/Plan/common/src/test/java/extension/SeleniumExtension.java +++ b/Plan/common/src/test/java/extension/SeleniumExtension.java @@ -72,6 +72,7 @@ public class SeleniumExtension implements ParameterResolver, BeforeAllCallback, private ChromeDriver getChromeWebDriver() { ChromeOptions chromeOptions = new ChromeOptions(); chromeOptions.addArguments("--enable-javascript"); + chromeOptions.addArguments("--ignore-certificate-errors"); chromeOptions.addArguments("--remote-allow-origins=*"); chromeOptions.setCapability(ChromeOptions.LOGGING_PREFS, getLoggingPreferences()); diff --git a/Plan/common/src/test/resources/fuzzing/Open-Redirect-payloads.txt b/Plan/common/src/test/resources/fuzzing/Open-Redirect-payloads.txt new file mode 100644 index 000000000..841d31a59 --- /dev/null +++ b/Plan/common/src/test/resources/fuzzing/Open-Redirect-payloads.txt @@ -0,0 +1,246 @@ +//google.com/%2f.. +//localhost:$PORT@google.com/%2f.. +///google.com/%2f.. +///localhost:$PORT@google.com/%2f.. +////google.com/%2f.. +////localhost:$PORT@google.com/%2f.. +https://google.com/%2f.. +https://localhost:$PORT@google.com/%2f.. +/https://google.com/%2f.. +/https://localhost:$PORT@google.com/%2f.. +//www.google.com/%2f%2e%2e +//localhost:$PORT@www.google.com/%2f%2e%2e +///www.google.com/%2f%2e%2e +///localhost:$PORT@www.google.com/%2f%2e%2e +////www.google.com/%2f%2e%2e +////localhost:$PORT@www.google.com/%2f%2e%2e +https://www.google.com/%2f%2e%2e +https://localhost:$PORT@www.google.com/%2f%2e%2e +/https://www.google.com/%2f%2e%2e +/https://localhost:$PORT@www.google.com/%2f%2e%2e +//google.com/ +//localhost:$PORT@google.com/ +///google.com/ +///localhost:$PORT@google.com/ +////google.com/ +////localhost:$PORT@google.com/ +https://google.com/ +https://localhost:$PORT@google.com/ +/https://google.com/ +/https://localhost:$PORT@google.com/ +//google.com// +//localhost:$PORT@google.com// +///google.com// +///localhost:$PORT@google.com// +////google.com// +////localhost:$PORT@google.com// +https://google.com// +https://localhost:$PORT@google.com// +//https://google.com +//https://google.com// +https://https://google.com// +//https://https://google.com// +//https://localhost:$PORT@google.com +//https://localhost:$PORT@google.com// +https://https://localhost:$PORT@google.com// +//https://https://localhost:$PORT@google.com// +//www.google.com/%2e%2e%2f +//localhost:$PORT@www.google.com/%2e%2e%2f +///www.google.com/%2e%2e%2f +///localhost:$PORT@www.google.com/%2e%2e%2f +////www.google.com/%2e%2e%2f +////localhost:$PORT@www.google.com/%2e%2e%2f +https://www.google.com/%2e%2e%2f +https://localhost:$PORT@www.google.com/%2e%2e%2f +//https://www.google.com/%2e%2e%2f +//https://localhost:$PORT@www.google.com/%2e%2e%2f +///www.google.com/%2e%2e +///localhost:$PORT@www.google.com/%2e%2e +////www.google.com/%2e%2e +////localhost:$PORT@www.google.com/%2e%2e +https:///www.google.com/%2e%2e +https:///localhost:$PORT@www.google.com/%2e%2e +//https:///www.google.com/%2e%2e +//localhost:$PORT@https:///www.google.com/%2e%2e +/https://www.google.com/%2e%2e +/https://localhost:$PORT@www.google.com/%2e%2e +///www.google.com/%2f%2e%2e +///localhost:$PORT@www.google.com/%2f%2e%2e +////www.google.com/%2f%2e%2e +////localhost:$PORT@www.google.com/%2f%2e%2e +https:///www.google.com/%2f%2e%2e +https:///localhost:$PORT@www.google.com/%2f%2e%2e +/https://www.google.com/%2f%2e%2e +/https://localhost:$PORT@www.google.com/%2f%2e%2e +/https:///www.google.com/%2f%2e%2e +/https:///localhost:$PORT@www.google.com/%2f%2e%2e +/%09/google.com +/%09/localhost:$PORT@google.com +//%09/google.com +//%09/localhost:$PORT@google.com +///%09/google.com +///%09/localhost:$PORT@google.com +////%09/google.com +////%09/localhost:$PORT@google.com +https://%09/google.com +https://%09/localhost:$PORT@google.com +/%5cgoogle.com +/%5clocalhost:$PORT@google.com +//%5cgoogle.com +//%5clocalhost:$PORT@google.com +///%5cgoogle.com +///%5clocalhost:$PORT@google.com +////%5cgoogle.com +////%5clocalhost:$PORT@google.com +https://%5cgoogle.com +https://%5clocalhost:$PORT@google.com +/https://%5cgoogle.com +/https://%5clocalhost:$PORT@google.com +https://google.com +https://localhost:$PORT@google.com +javascript:alert(1); +javascript:alert(1) +//javascript:alert(1); +/javascript:alert(1); +//javascript:alert(1) +/javascript:alert(1) +/%5cjavascript:alert(1); +/%5cjavascript:alert(1) +//%5cjavascript:alert(1); +//%5cjavascript:alert(1) +/%09/javascript:alert(1); +/%09/javascript:alert(1) +java%0d%0ascript%0d%0a:alert(0) +//google.com +https:google.com +//google%E3%80%82com +\/\/google.com/ +/\/google.com/ +//google%00.com +https://localhost:$PORT/https://www.google.com/ +";alert(0);// +javascript://localhost:$PORT?%a0alert%281%29 +http://0xd8.0x3a.0xd6.0xce +http://localhost:$PORT@0xd8.0x3a.0xd6.0xce +http://3H6k7lIAiqjfNeN@0xd8.0x3a.0xd6.0xce +http://XY>.7d8T\205pZM@0xd8.0x3a.0xd6.0xce +http://0xd83ad6ce +http://localhost:$PORT@0xd83ad6ce +http://3H6k7lIAiqjfNeN@0xd83ad6ce +http://XY>.7d8T\205pZM@0xd83ad6ce +http://3627734734 +http://localhost:$PORT@3627734734 +http://3H6k7lIAiqjfNeN@3627734734 +http://XY>.7d8T\205pZM@3627734734 +http://472.314.470.462 +http://localhost:$PORT@472.314.470.462 +http://3H6k7lIAiqjfNeN@472.314.470.462 +http://XY>.7d8T\205pZM@472.314.470.462 +http://0330.072.0326.0316 +http://localhost:$PORT@0330.072.0326.0316 +http://3H6k7lIAiqjfNeN@0330.072.0326.0316 +http://XY>.7d8T\205pZM@0330.072.0326.0316 +http://00330.00072.0000326.00000316 +http://localhost:$PORT@00330.00072.0000326.00000316 +http://3H6k7lIAiqjfNeN@00330.00072.0000326.00000316 +http://XY>.7d8T\205pZM@00330.00072.0000326.00000316 +http://[::216.58.214.206] +http://localhost:$PORT@[::216.58.214.206] +http://3H6k7lIAiqjfNeN@[::216.58.214.206] +http://XY>.7d8T\205pZM@[::216.58.214.206] +http://[::ffff:216.58.214.206] +http://localhost:$PORT@[::ffff:216.58.214.206] +http://3H6k7lIAiqjfNeN@[::ffff:216.58.214.206] +http://XY>.7d8T\205pZM@[::ffff:216.58.214.206] +http://0xd8.072.54990 +http://localhost:$PORT@0xd8.072.54990 +http://3H6k7lIAiqjfNeN@0xd8.072.54990 +http://XY>.7d8T\205pZM@0xd8.072.54990 +http://0xd8.3856078 +http://localhost:$PORT@0xd8.3856078 +http://3H6k7lIAiqjfNeN@0xd8.3856078 +http://XY>.7d8T\205pZM@0xd8.3856078 +http://00330.3856078 +http://localhost:$PORT@00330.3856078 +http://3H6k7lIAiqjfNeN@00330.3856078 +http://XY>.7d8T\205pZM@00330.3856078 +http://00330.0x3a.54990 +http://localhost:$PORT@00330.0x3a.54990 +http://3H6k7lIAiqjfNeN@00330.0x3a.54990 +http://XY>.7d8T\205pZM@00330.0x3a.54990 +http:0xd8.0x3a.0xd6.0xce +http:localhost:$PORT@0xd8.0x3a.0xd6.0xce +http:3H6k7lIAiqjfNeN@0xd8.0x3a.0xd6.0xce +http:XY>.7d8T\205pZM@0xd8.0x3a.0xd6.0xce +http:0xd83ad6ce +http:localhost:$PORT@0xd83ad6ce +http:3H6k7lIAiqjfNeN@0xd83ad6ce +http:XY>.7d8T\205pZM@0xd83ad6ce +http:3627734734 +http:localhost:$PORT@3627734734 +http:3H6k7lIAiqjfNeN@3627734734 +http:XY>.7d8T\205pZM@3627734734 +http:472.314.470.462 +http:localhost:$PORT@472.314.470.462 +http:3H6k7lIAiqjfNeN@472.314.470.462 +http:XY>.7d8T\205pZM@472.314.470.462 +http:0330.072.0326.0316 +http:localhost:$PORT@0330.072.0326.0316 +http:3H6k7lIAiqjfNeN@0330.072.0326.0316 +http:XY>.7d8T\205pZM@0330.072.0326.0316 +http:00330.00072.0000326.00000316 +http:localhost:$PORT@00330.00072.0000326.00000316 +http:3H6k7lIAiqjfNeN@00330.00072.0000326.00000316 +http:XY>.7d8T\205pZM@00330.00072.0000326.00000316 +http:[::216.58.214.206] +http:localhost:$PORT@[::216.58.214.206] +http:3H6k7lIAiqjfNeN@[::216.58.214.206] +http:XY>.7d8T\205pZM@[::216.58.214.206] +http:[::ffff:216.58.214.206] +http:localhost:$PORT@[::ffff:216.58.214.206] +http:3H6k7lIAiqjfNeN@[::ffff:216.58.214.206] +http:XY>.7d8T\205pZM@[::ffff:216.58.214.206] +http:0xd8.072.54990 +http:localhost:$PORT@0xd8.072.54990 +http:3H6k7lIAiqjfNeN@0xd8.072.54990 +http:XY>.7d8T\205pZM@0xd8.072.54990 +http:0xd8.3856078 +http:localhost:$PORT@0xd8.3856078 +http:3H6k7lIAiqjfNeN@0xd8.3856078 +http:XY>.7d8T\205pZM@0xd8.3856078 +http:00330.3856078 +http:localhost:$PORT@00330.3856078 +http:3H6k7lIAiqjfNeN@00330.3856078 +http:XY>.7d8T\205pZM@00330.3856078 +http:00330.0x3a.54990 +http:localhost:$PORT@00330.0x3a.54990 +http:3H6k7lIAiqjfNeN@00330.0x3a.54990 +http:XY>.7d8T\205pZM@00330.0x3a.54990 +〱google.com +〵google.com +ゝgoogle.com +ーgoogle.com +ーgoogle.com +/〱google.com +/〵google.com +/ゝgoogle.com +/ーgoogle.com +/ーgoogle.com +%68%74%74%70%3a%2f%2f%67%6f%6f%67%6c%65%2e%63%6f%6d +http://%67%6f%6f%67%6c%65%2e%63%6f%6d +<>javascript:alert(1); +<>//google.com +//google.com\@localhost:$PORT +https://:@google.com\@localhost:$PORT +\x6A\x61\x76\x61\x73\x63\x72\x69\x70\x74\x3aalert(1) +\u006A\u0061\u0076\u0061\u0073\u0063\u0072\u0069\u0070\u0074\u003aalert(1) +ja\nva\tscript\r:alert(1) +\j\av\a\s\cr\i\pt\:\a\l\ert\(1\) +\152\141\166\141\163\143\162\151\160\164\072alert(1) +http://google.com:80#@localhost:$PORT/ +http://google.com:80?@localhost:$PORT/ +http://google.com\localhost:$PORT +http://google.com&localhost:$PORT +http:///////////google.com +\\google.com +http://localhost:$PORT.google.com diff --git a/Plan/react/dashboard/src/components/navigation/MainPageRedirect.js b/Plan/react/dashboard/src/components/navigation/MainPageRedirect.js index 7cd975f80..516e17b9c 100644 --- a/Plan/react/dashboard/src/components/navigation/MainPageRedirect.js +++ b/Plan/react/dashboard/src/components/navigation/MainPageRedirect.js @@ -80,7 +80,13 @@ const MainPageRedirect = () => { }; if (authRequired && !loggedIn) { - return () + if (!window.location.pathname.startsWith("/login")) { + return () + } else { + return () + } } else if (authRequired && loggedIn) { return redirectBasedOnPermissions(); } else { diff --git a/Plan/react/dashboard/src/views/layout/LoginPage.js b/Plan/react/dashboard/src/views/layout/LoginPage.js index 10dec63f6..8370bc00d 100644 --- a/Plan/react/dashboard/src/views/layout/LoginPage.js +++ b/Plan/react/dashboard/src/views/layout/LoginPage.js @@ -169,7 +169,25 @@ const LoginPage = () => { } else if (data && data.success) { await updateLoginDetails(); if (redirectTo && !redirectTo.startsWith('http') && !redirectTo.startsWith('file') && !redirectTo.startsWith('javascript')) { - navigate(redirectTo.substring(redirectTo.indexOf('/')) + (window.location.hash ? window.location.hash : '')); + // Normalize the URL so that it can't redirect to different domain. + try { + const redirectUrl = new URL( + redirectTo.substring(redirectTo.indexOf('/')) + (window.location.hash ? window.location.hash : ''), + window.location.protocol + '//' + window.location.host + ); + if (redirectUrl.pathname.includes("//")) { + // Invalid redirect URL, something fishy might be going on, redirect to / + navigate('/'); + } else { + navigate( + redirectUrl.pathname + redirectUrl.search + redirectUrl.hash + ); + } + } catch (e) { + console.warn(e); + // Invalid redirect URL, something fishy might be going on, redirect to / + navigate('/'); + } } else { navigate('/'); } diff --git a/Plan/react/dashboard/src/views/layout/NetworkPage.js b/Plan/react/dashboard/src/views/layout/NetworkPage.js index 2729fc37f..d291e16ae 100644 --- a/Plan/react/dashboard/src/views/layout/NetworkPage.js +++ b/Plan/react/dashboard/src/views/layout/NetworkPage.js @@ -117,7 +117,7 @@ const NetworkSidebar = () => { ) } -const ServerPage = () => { +const NetworkPage = () => { const {networkName, serverUUID} = useMetadata(); const {currentTab} = useNavigation(); @@ -150,4 +150,4 @@ const ServerPage = () => { ) } -export default ServerPage; \ No newline at end of file +export default NetworkPage; \ No newline at end of file diff --git a/Plan/react/dashboard/src/views/layout/PlayerPage.js b/Plan/react/dashboard/src/views/layout/PlayerPage.js index 6cd640b6b..1991f9672 100644 --- a/Plan/react/dashboard/src/views/layout/PlayerPage.js +++ b/Plan/react/dashboard/src/views/layout/PlayerPage.js @@ -10,6 +10,8 @@ import {useTranslation} from "react-i18next"; import {faCalendarCheck} from "@fortawesome/free-regular-svg-icons"; import {useDataRequest} from "../../hooks/dataFetchHook"; import ErrorPage from "./ErrorPage"; +import {useAuth} from "../../hooks/authenticationHook"; +import MainPageRedirect from "../../components/navigation/MainPageRedirect"; const HelpModal = React.lazy(() => import("../../components/modal/HelpModal")); @@ -47,6 +49,8 @@ const PlayerPage = () => { finishUpdate(player.timestamp, player.timestamp_f); }, [player, t, i18n, finishUpdate, setSidebarItems]) + const {authRequired, loggedIn} = useAuth(); + if (authRequired && !loggedIn) return ; if (loadingError) return ; return player ? ( diff --git a/Plan/react/dashboard/src/views/layout/PlayersPage.js b/Plan/react/dashboard/src/views/layout/PlayersPage.js index 1c3a41dbf..89dae90be 100644 --- a/Plan/react/dashboard/src/views/layout/PlayersPage.js +++ b/Plan/react/dashboard/src/views/layout/PlayersPage.js @@ -9,6 +9,8 @@ import ColorSelectorModal from "../../components/modal/ColorSelectorModal"; import {useMetadata} from "../../hooks/metadataHook"; import ErrorPage from "./ErrorPage"; import {staticSite} from "../../service/backendConfiguration"; +import {useAuth} from "../../hooks/authenticationHook"; +import MainPageRedirect from "../../components/navigation/MainPageRedirect"; const HelpModal = React.lazy(() => import("../../components/modal/HelpModal")); @@ -30,6 +32,8 @@ const PlayersPage = () => { setCurrentTab('html.label.players') }, [t, i18n, setCurrentTab, setSidebarItems]) + const {authRequired, loggedIn} = useAuth(); + if (authRequired && !loggedIn) return ; if (error) return ; const displayedServerName = isProxy ? networkName : (serverName && serverName.startsWith('Server') ? "Plan" : serverName); diff --git a/Plan/react/dashboard/src/views/layout/QueryPage.js b/Plan/react/dashboard/src/views/layout/QueryPage.js index 2bb85799a..de5c90955 100644 --- a/Plan/react/dashboard/src/views/layout/QueryPage.js +++ b/Plan/react/dashboard/src/views/layout/QueryPage.js @@ -9,6 +9,8 @@ import ColorSelectorModal from "../../components/modal/ColorSelectorModal"; import {useMetadata} from "../../hooks/metadataHook"; import ErrorPage from "./ErrorPage"; import {QueryResultContextProvider} from "../../hooks/queryResultContext"; +import {useAuth} from "../../hooks/authenticationHook"; +import MainPageRedirect from "../../components/navigation/MainPageRedirect"; const QueryPage = () => { const {t, i18n} = useTranslation(); @@ -30,6 +32,8 @@ const QueryPage = () => { setCurrentTab('html.query.title.text'); }, [t, i18n, setCurrentTab, setSidebarItems]) + const {authRequired, loggedIn} = useAuth(); + if (authRequired && !loggedIn) return ; if (error) return ; const displayedServerName = isProxy ? networkName : (serverName && serverName.startsWith('Server') ? "Plan" : serverName); diff --git a/Plan/react/dashboard/yarn.lock b/Plan/react/dashboard/yarn.lock index ee848242e..291415e73 100644 --- a/Plan/react/dashboard/yarn.lock +++ b/Plan/react/dashboard/yarn.lock @@ -1537,10 +1537,10 @@ dependencies: "@swc/helpers" "^0.4.14" -"@remix-run/router@1.3.0": - version "1.3.0" - resolved "https://registry.yarnpkg.com/@remix-run/router/-/router-1.3.0.tgz#b6ee542c7f087b73b3d8215b9bf799f648be71cb" - integrity sha512-nwQoYb3m4DDpHTeOwpJEuDt8lWVcujhYYSFGLluC+9es2PyLjm+jjq3IeRBQbwBtPLJE/lkuHuGHr8uQLgmJRA== +"@remix-run/router@1.5.0": + version "1.5.0" + resolved "https://registry.yarnpkg.com/@remix-run/router/-/router-1.5.0.tgz#57618e57942a5f0131374a9fdb0167e25a117fdc" + integrity sha512-bkUDCp8o1MvFO+qxkODcbhSqRa6P2GXgrGZVpt0dCXNW2HCSCqYI0ZoAqEOSAjRWmmlKcYgFvN4B4S+zo/f8kg== "@restart/hooks@^0.4.6", "@restart/hooks@^0.4.7": version "0.4.8" @@ -8343,19 +8343,19 @@ react-refresh@^0.11.0: integrity sha512-F27qZr8uUqwhWZboondsPx8tnC3Ct3SxZA3V5WyEvujRyyNv0VYPhoBg1gZ8/MV5tubQp76Trw8lTv9hzRBa+A== react-router-dom@6: - version "6.7.0" - resolved "https://registry.yarnpkg.com/react-router-dom/-/react-router-dom-6.7.0.tgz#0249f4ca4eb704562b8b0ff29caeb928c3a6ed38" - integrity sha512-jQtXUJyhso3kFw430+0SPCbmCmY1/kJv8iRffGHwHy3CkoomGxeYzMkmeSPYo6Egzh3FKJZRAL22yg5p2tXtfg== + version "6.10.0" + resolved "https://registry.yarnpkg.com/react-router-dom/-/react-router-dom-6.10.0.tgz#090ddc5c84dc41b583ce08468c4007c84245f61f" + integrity sha512-E5dfxRPuXKJqzwSe/qGcqdwa18QiWC6f3H3cWXM24qj4N0/beCIf/CWTipop2xm7mR0RCS99NnaqPNjHtrAzCg== dependencies: - "@remix-run/router" "1.3.0" - react-router "6.7.0" + "@remix-run/router" "1.5.0" + react-router "6.10.0" -react-router@6.7.0: - version "6.7.0" - resolved "https://registry.yarnpkg.com/react-router/-/react-router-6.7.0.tgz#db262684c13b5c2970694084ae9e8531718a0681" - integrity sha512-KNWlG622ddq29MAM159uUsNMdbX8USruoKnwMMQcs/QWZgFUayICSn2oB7reHce1zPj6CG18kfkZIunSSRyGHg== +react-router@6.10.0: + version "6.10.0" + resolved "https://registry.yarnpkg.com/react-router/-/react-router-6.10.0.tgz#230f824fde9dd0270781b5cb497912de32c0a971" + integrity sha512-Nrg0BWpQqrC3ZFFkyewrflCud9dio9ME3ojHCF/WLsprJVzkq3q3UeEhMCAW1dobjeGbWgjNn/PVF6m46ANxXQ== dependencies: - "@remix-run/router" "1.3.0" + "@remix-run/router" "1.5.0" react-scripts@5.0.1: version "5.0.1"