From 1daff361dd62e9899de7b86cf4b3d9c0ceef9fee Mon Sep 17 00:00:00 2001 From: Marcin Tojek Date: Mon, 15 Apr 2024 15:07:58 +0200 Subject: [PATCH 1/5] e2e tests for security --- site/e2e/tests/deployment/security.ts | 15 ++++++++++++ site/src/components/Badges/Badges.tsx | 7 +++++- site/src/pages/DeploySettingsPage/Option.tsx | 24 ++++++++++++++++---- 3 files changed, 41 insertions(+), 5 deletions(-) create mode 100644 site/e2e/tests/deployment/security.ts diff --git a/site/e2e/tests/deployment/security.ts b/site/e2e/tests/deployment/security.ts new file mode 100644 index 0000000000000..9e761b6adea9c --- /dev/null +++ b/site/e2e/tests/deployment/security.ts @@ -0,0 +1,15 @@ +import { expect, test } from "@playwright/test"; +import * as API from "api/api"; +import { setupApiCalls } from "../../api"; + +test("enabled security settings", async ({ page }) => { + await setupApiCalls(page); + + await page.goto("/deployment/security", { waitUntil: "domcontentloaded" }); + + // Load deployment settings + const config = await API.getDeploymentConfig(); + + // Check flags + +}); diff --git a/site/src/components/Badges/Badges.tsx b/site/src/components/Badges/Badges.tsx index 152e7735fd27f..7fc76fb879a9c 100644 --- a/site/src/components/Badges/Badges.tsx +++ b/site/src/components/Badges/Badges.tsx @@ -41,7 +41,11 @@ const styles = { } satisfies Record>; export const EnabledBadge: FC = () => { - return Enabled; + return ( + + Enabled + + ); }; export const EntitledBadge: FC = () => { @@ -95,6 +99,7 @@ export const DisabledBadge: FC = forwardRef< color: theme.experimental.l1.text, }), ]} + className="option-disabled" > Disabled diff --git a/site/src/pages/DeploySettingsPage/Option.tsx b/site/src/pages/DeploySettingsPage/Option.tsx index 9e5e9f7abdda7..513290970aff6 100644 --- a/site/src/pages/DeploySettingsPage/Option.tsx +++ b/site/src/pages/DeploySettingsPage/Option.tsx @@ -38,15 +38,27 @@ export const OptionValue: FC = (props) => { } if (typeof value === "number") { - return {value}; + return ( + + {value} + + ); } if (!value || value.length === 0) { - return Not set; + return ( + + Not set + + ); } if (typeof value === "string") { - return {value}; + return ( + + {value} + + ); } if (typeof value === "object" && !Array.isArray(value)) { @@ -104,7 +116,11 @@ export const OptionValue: FC = (props) => { ); } - return {JSON.stringify(value)}; + return ( + + {JSON.stringify(value)} + + ); }; type OptionConfigProps = HTMLAttributes & { isSource: boolean }; From 815691027b2fabfa021e27720dab45c6febd8cf7 Mon Sep 17 00:00:00 2001 From: Marcin Tojek Date: Mon, 15 Apr 2024 16:18:03 +0200 Subject: [PATCH 2/5] WIP --- site/e2e/playwright.config.ts | 2 +- site/e2e/tests/deployment/security.spec.ts | 49 ++++++++++++++++++++ site/e2e/tests/deployment/security.ts | 15 ------ site/src/pages/DeploySettingsPage/Option.tsx | 8 +++- 4 files changed, 56 insertions(+), 18 deletions(-) create mode 100644 site/e2e/tests/deployment/security.spec.ts delete mode 100644 site/e2e/tests/deployment/security.ts diff --git a/site/e2e/playwright.config.ts b/site/e2e/playwright.config.ts index 2fe84b17a2f83..c3d7e070bcbf8 100644 --- a/site/e2e/playwright.config.ts +++ b/site/e2e/playwright.config.ts @@ -32,7 +32,7 @@ export default defineConfig({ timeout: 50_000, }, ], - reporter: [["./reporter.ts"]], + //reporter: [["./reporter.ts"]], use: { baseURL: `http://localhost:${coderPort}`, video: "retain-on-failure", diff --git a/site/e2e/tests/deployment/security.spec.ts b/site/e2e/tests/deployment/security.spec.ts new file mode 100644 index 0000000000000..74597abb62519 --- /dev/null +++ b/site/e2e/tests/deployment/security.spec.ts @@ -0,0 +1,49 @@ +import { expect, test, type Page } from "@playwright/test"; +import * as API from "api/api"; +import { setupApiCalls } from "../../api"; + +test("enabled security settings", async ({ page }) => { + await setupApiCalls(page); + + await page.goto("/deployment/security", { waitUntil: "domcontentloaded" }); + + // Load deployment settings + const config = await API.getDeploymentConfig(); + + // Check flags + await expectConfigOption(page, config, "ssh-keygen-algorithm") + await expectConfigOption(page, config, "secure-auth-cookie") + await expectConfigOption(page, config, "disable-owner-workspace-access") + + await expectConfigOption(page, config, "tls-redirect-http-to-https") + await expectConfigOption(page, config, "strict-transport-security") + await expectConfigOption(page, config, "tls-address") + await expectConfigOption(page, config, "tls-allow-insecure-ciphers") +}); + +const expectConfigOption = async(page :Page, config: API.DeploymentConfig, flag: string) => { + let value = config.options.find(option => option.flag === flag)?.value; + if (value === undefined) { + throw new Error(`Option with env ${flag} has undefined value.`); + } + + let type = ""; + if (typeof value === "boolean") { + type = value ? "option-enabled" : "option-disabled"; + value = value ? "Enabled" : "Disabled"; + } else if (typeof value === "number") { + type = "option-value-number"; + value = String(value); + } else if (!value || value.length === 0) { + type = "option-value-empty"; + } else if (typeof value === "string") { + type = "option-value-string"; + } else if (typeof value === "object") { + type = "object-array"; + } else { + type = "option-value-json"; + } + + const configOption = page.locator(`div.options-table .option-${flag} .${type}`); + await expect(configOption).toHaveText(String(value)); +} diff --git a/site/e2e/tests/deployment/security.ts b/site/e2e/tests/deployment/security.ts deleted file mode 100644 index 9e761b6adea9c..0000000000000 --- a/site/e2e/tests/deployment/security.ts +++ /dev/null @@ -1,15 +0,0 @@ -import { expect, test } from "@playwright/test"; -import * as API from "api/api"; -import { setupApiCalls } from "../../api"; - -test("enabled security settings", async ({ page }) => { - await setupApiCalls(page); - - await page.goto("/deployment/security", { waitUntil: "domcontentloaded" }); - - // Load deployment settings - const config = await API.getDeploymentConfig(); - - // Check flags - -}); diff --git a/site/src/pages/DeploySettingsPage/Option.tsx b/site/src/pages/DeploySettingsPage/Option.tsx index 513290970aff6..b79aeef3ab100 100644 --- a/site/src/pages/DeploySettingsPage/Option.tsx +++ b/site/src/pages/DeploySettingsPage/Option.tsx @@ -34,7 +34,11 @@ export const OptionValue: FC = (props) => { const theme = useTheme(); if (typeof value === "boolean") { - return value ? : ; + return ( +
+ {value ? : } +
+ ); } if (typeof value === "number") { @@ -106,7 +110,7 @@ export const OptionValue: FC = (props) => { if (Array.isArray(value)) { return ( -
    +
      {value.map((item) => (
    • {item} From 5ddcb6db80b8aeb23907e54377036365f7b90204 Mon Sep 17 00:00:00 2001 From: Marcin Tojek Date: Mon, 15 Apr 2024 16:21:23 +0200 Subject: [PATCH 3/5] WIP --- site/e2e/tests/deployment/security.spec.ts | 30 +++++++++++++--------- 1 file changed, 18 insertions(+), 12 deletions(-) diff --git a/site/e2e/tests/deployment/security.spec.ts b/site/e2e/tests/deployment/security.spec.ts index 74597abb62519..c39289c81e34e 100644 --- a/site/e2e/tests/deployment/security.spec.ts +++ b/site/e2e/tests/deployment/security.spec.ts @@ -11,18 +11,22 @@ test("enabled security settings", async ({ page }) => { const config = await API.getDeploymentConfig(); // Check flags - await expectConfigOption(page, config, "ssh-keygen-algorithm") - await expectConfigOption(page, config, "secure-auth-cookie") - await expectConfigOption(page, config, "disable-owner-workspace-access") - - await expectConfigOption(page, config, "tls-redirect-http-to-https") - await expectConfigOption(page, config, "strict-transport-security") - await expectConfigOption(page, config, "tls-address") - await expectConfigOption(page, config, "tls-allow-insecure-ciphers") + await expectConfigOption(page, config, "ssh-keygen-algorithm"); + await expectConfigOption(page, config, "secure-auth-cookie"); + await expectConfigOption(page, config, "disable-owner-workspace-access"); + + await expectConfigOption(page, config, "tls-redirect-http-to-https"); + await expectConfigOption(page, config, "strict-transport-security"); + await expectConfigOption(page, config, "tls-address"); + await expectConfigOption(page, config, "tls-allow-insecure-ciphers"); }); -const expectConfigOption = async(page :Page, config: API.DeploymentConfig, flag: string) => { - let value = config.options.find(option => option.flag === flag)?.value; +const expectConfigOption = async ( + page: Page, + config: API.DeploymentConfig, + flag: string, +) => { + let value = config.options.find((option) => option.flag === flag)?.value; if (value === undefined) { throw new Error(`Option with env ${flag} has undefined value.`); } @@ -44,6 +48,8 @@ const expectConfigOption = async(page :Page, config: API.DeploymentConfig, flag: type = "option-value-json"; } - const configOption = page.locator(`div.options-table .option-${flag} .${type}`); + const configOption = page.locator( + `div.options-table .option-${flag} .${type}`, + ); await expect(configOption).toHaveText(String(value)); -} +}; From c65bb92aca7daf409fe85f5fb1ede47e52d4c790 Mon Sep 17 00:00:00 2001 From: Marcin Tojek Date: Mon, 15 Apr 2024 16:59:24 +0200 Subject: [PATCH 4/5] WIP --- site/e2e/tests/deployment/security.spec.ts | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/site/e2e/tests/deployment/security.spec.ts b/site/e2e/tests/deployment/security.spec.ts index c39289c81e34e..02cebb1a581c3 100644 --- a/site/e2e/tests/deployment/security.spec.ts +++ b/site/e2e/tests/deployment/security.spec.ts @@ -26,12 +26,13 @@ const expectConfigOption = async ( config: API.DeploymentConfig, flag: string, ) => { - let value = config.options.find((option) => option.flag === flag)?.value; - if (value === undefined) { + const opt = config.options.find((option) => option.flag === flag); + if (opt === undefined) { throw new Error(`Option with env ${flag} has undefined value.`); } - let type = ""; + let type = "", + value = opt.value; if (typeof value === "boolean") { type = value ? "option-enabled" : "option-disabled"; value = value ? "Enabled" : "Disabled"; @@ -48,6 +49,12 @@ const expectConfigOption = async ( type = "option-value-json"; } + // Special cases + if (opt.flag === "strict-transport-security" && opt.value === 0) { + type = "option-value-string"; + value = "Disabled"; + } + const configOption = page.locator( `div.options-table .option-${flag} .${type}`, ); From 3cc9a085c150cf8740bcb53ae6e964065bc5fced Mon Sep 17 00:00:00 2001 From: Marcin Tojek Date: Mon, 15 Apr 2024 18:05:08 +0200 Subject: [PATCH 5/5] Done --- site/e2e/playwright.config.ts | 2 +- site/e2e/tests/deployment/security.spec.ts | 33 ++++++++++++++-------- 2 files changed, 22 insertions(+), 13 deletions(-) diff --git a/site/e2e/playwright.config.ts b/site/e2e/playwright.config.ts index c3d7e070bcbf8..2fe84b17a2f83 100644 --- a/site/e2e/playwright.config.ts +++ b/site/e2e/playwright.config.ts @@ -32,7 +32,7 @@ export default defineConfig({ timeout: 50_000, }, ], - //reporter: [["./reporter.ts"]], + reporter: [["./reporter.ts"]], use: { baseURL: `http://localhost:${coderPort}`, video: "retain-on-failure", diff --git a/site/e2e/tests/deployment/security.spec.ts b/site/e2e/tests/deployment/security.spec.ts index 02cebb1a581c3..fe88dcfba248c 100644 --- a/site/e2e/tests/deployment/security.spec.ts +++ b/site/e2e/tests/deployment/security.spec.ts @@ -5,23 +5,30 @@ import { setupApiCalls } from "../../api"; test("enabled security settings", async ({ page }) => { await setupApiCalls(page); + const config = await API.getDeploymentConfig(); + await page.goto("/deployment/security", { waitUntil: "domcontentloaded" }); - // Load deployment settings - const config = await API.getDeploymentConfig(); + const flags = [ + "ssh-keygen-algorithm", + "secure-auth-cookie", + "disable-owner-workspace-access", - // Check flags - await expectConfigOption(page, config, "ssh-keygen-algorithm"); - await expectConfigOption(page, config, "secure-auth-cookie"); - await expectConfigOption(page, config, "disable-owner-workspace-access"); + "tls-redirect-http-to-https", + "strict-transport-security", + "tls-address", + "tls-allow-insecure-ciphers", + "tls-client-auth", + "tls-enable", + "tls-min-version", + ]; - await expectConfigOption(page, config, "tls-redirect-http-to-https"); - await expectConfigOption(page, config, "strict-transport-security"); - await expectConfigOption(page, config, "tls-address"); - await expectConfigOption(page, config, "tls-allow-insecure-ciphers"); + for (const flag of flags) { + await verifyConfigFlag(page, config, flag); + } }); -const expectConfigOption = async ( +const verifyConfigFlag = async ( page: Page, config: API.DeploymentConfig, flag: string, @@ -31,9 +38,11 @@ const expectConfigOption = async ( throw new Error(`Option with env ${flag} has undefined value.`); } + // Map option type to test class name. let type = "", value = opt.value; if (typeof value === "boolean") { + // Boolean options map to string (Enabled/Disabled). type = value ? "option-enabled" : "option-disabled"; value = value ? "Enabled" : "Disabled"; } else if (typeof value === "number") { @@ -52,7 +61,7 @@ const expectConfigOption = async ( // Special cases if (opt.flag === "strict-transport-security" && opt.value === 0) { type = "option-value-string"; - value = "Disabled"; + value = "Disabled"; // Display "Disabled" instead of zero seconds. } const configOption = page.locator(