From 64fbbeee29d431fd6cffeddb0df736a5ef96ca90 Mon Sep 17 00:00:00 2001 From: Islam Zaoui <98623858+IslamZaoui@users.noreply.github.com> Date: Thu, 10 Oct 2024 18:27:34 +0100 Subject: [PATCH 1/3] chore: update bun to 1.1.30 & add unit testing --- .github/workflows/lint.yaml | 8 ++-- .github/workflows/publish.yml | 4 +- README.md | 2 +- apps/demo/.gitignore | 1 + apps/demo/e2e/headers.test.ts | 25 +++++++++++ apps/demo/package.json | 4 +- apps/demo/playwright.config.ts | 10 +++++ package.json | 10 +++-- packages/securekit/package.json | 4 +- packages/securekit/test/utils.test.ts | 61 +++++++++++++++++++++++++++ turbo.json | 7 +++ 11 files changed, 123 insertions(+), 13 deletions(-) create mode 100644 apps/demo/e2e/headers.test.ts create mode 100644 apps/demo/playwright.config.ts create mode 100644 packages/securekit/test/utils.test.ts diff --git a/.github/workflows/lint.yaml b/.github/workflows/lint.yaml index 2daa24f..aac0390 100644 --- a/.github/workflows/lint.yaml +++ b/.github/workflows/lint.yaml @@ -1,4 +1,4 @@ -name: CI/CD +name: CI on: push: @@ -16,7 +16,7 @@ jobs: - name: Set up bun uses: oven-sh/setup-bun@v2 with: - bun-version: 1.1.29 + bun-version: 1.1.30 - name: Install dependencies run: bun install --frozen-lockfile @@ -24,5 +24,5 @@ jobs: - name: Run lint run: bun lint - - name: Run build - run: bun run build + - name: Run test + run: bun run test diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index 4cce327..e5044aa 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -1,7 +1,7 @@ name: Publish on: workflow_run: - workflows: [CI/CD] + workflows: [CI] branches: [main] types: [completed] @@ -22,7 +22,7 @@ jobs: - name: Set up bun uses: oven-sh/setup-bun@v2 with: - bun-version: 1.1.29 + bun-version: 1.1.30 - name: Install Dependencies run: bun install --frozen-lockfile diff --git a/README.md b/README.md index 1f90a64..15c54a7 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ Secure your [sveltekit](https://kit.svelte.dev/) app using [http response headers](https://cheatsheetseries.owasp.org/cheatsheets/HTTP_Headers_Cheat_Sheet.html) -[![CI/CD](https://github.com/IslamZaoui/securekit/actions/workflows/lint.yaml/badge.svg)](https://github.com/IslamZaoui/securekit/actions/workflows/lint.yaml) +[![CI](https://github.com/IslamZaoui/securekit/actions/workflows/lint.yaml/badge.svg)](https://github.com/IslamZaoui/securekit/actions/workflows/lint.yaml) [![NPM](https://img.shields.io/npm/v/%40islamzaoui%2Fsecurekit)](https://www.npmjs.com/package/@islamzaoui/securekit) [![Issues](https://img.shields.io/github/issues/IslamZaoui/securekit)](https://github.com/IslamZaoui/securekit/issues) [![License](https://img.shields.io/github/license/IslamZaoui/securekit)](https://github.com/IslamZaoui/securekit/blob/main/LICENSE) diff --git a/apps/demo/.gitignore b/apps/demo/.gitignore index 5040242..944ac85 100644 --- a/apps/demo/.gitignore +++ b/apps/demo/.gitignore @@ -1,3 +1,4 @@ +test-results node_modules # Output diff --git a/apps/demo/e2e/headers.test.ts b/apps/demo/e2e/headers.test.ts new file mode 100644 index 0000000..8f78ccd --- /dev/null +++ b/apps/demo/e2e/headers.test.ts @@ -0,0 +1,25 @@ +import { expect, test } from '@playwright/test'; + +test('home page has expected headers', async ({ page }) => { + const response = await page.goto('/'); + if (response) { + const headers = await response.allHeaders(); + expect(headers).toHaveProperty('access-control-allow-origin'); + expect(headers).toHaveProperty('content-security-policy'); + expect(headers).toHaveProperty('permissions-policy'); + expect(headers).toHaveProperty('x-content-type-options'); + expect(headers).toHaveProperty('x-frame-options'); + } else { + throw new Error('Failed to load the home page'); + } +}); + +test('home page have removed headers', async ({ page }) => { + const response = await page.goto('/'); + if (response) { + const headers = await response.allHeaders(); + expect(headers).not.toHaveProperty('x-sveltekit-page'); + } else { + throw new Error('Failed to load the home page'); + } +}); diff --git a/apps/demo/package.json b/apps/demo/package.json index 5267dd0..bca26a9 100644 --- a/apps/demo/package.json +++ b/apps/demo/package.json @@ -8,9 +8,11 @@ "check": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json", "check:watch": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json --watch", "lint": "prettier --check . && eslint .", - "format": "prettier --write ." + "format": "prettier --write .", + "test": "playwright test" }, "devDependencies": { + "@playwright/test": "^1.45.3", "@sveltejs/adapter-vercel": "^5.4.4", "@sveltejs/kit": "^2.0.0", "@sveltejs/vite-plugin-svelte": "^3.0.0", diff --git a/apps/demo/playwright.config.ts b/apps/demo/playwright.config.ts new file mode 100644 index 0000000..e3d5d6b --- /dev/null +++ b/apps/demo/playwright.config.ts @@ -0,0 +1,10 @@ +import { defineConfig } from '@playwright/test'; + +export default defineConfig({ + webServer: { + command: 'bun run build && bun run preview', + port: 4173 + }, + + testDir: 'e2e' +}); diff --git a/package.json b/package.json index 3334652..b100d3e 100644 --- a/package.json +++ b/package.json @@ -4,6 +4,8 @@ "scripts": { "build": "turbo run build", "dev": "turbo run dev", + "preview": "turbo run preview", + "test": "turbo run test", "release": "turbo run build && changeset publish", "lint": "prettier --check \"**/*.{json,md}\" && turbo run lint", "format": "prettier --write \"**/*.{json,md}\" && turbo run format" @@ -13,9 +15,9 @@ "apps/*" ], "devDependencies": { - "@changesets/cli": "^2.27.8", - "prettier": "^3.1.1", - "turbo": "^2.1.2" + "@changesets/cli": "^2.27.9", + "prettier": "^3.3.3", + "turbo": "^2.1.3" }, - "packageManager": "bun@1.1.29" + "packageManager": "bun@1.1.30" } diff --git a/packages/securekit/package.json b/packages/securekit/package.json index fb8a64a..17fb0aa 100644 --- a/packages/securekit/package.json +++ b/packages/securekit/package.json @@ -11,7 +11,8 @@ "lint": "tsc && prettier --check .", "format": "prettier --write .", "dev": "tsup src/index.ts --format cjs,esm --dts --watch", - "build": "tsup src/index.ts --format cjs,esm --dts" + "build": "tsup src/index.ts --format cjs,esm --dts", + "test": "bun test" }, "keywords": [ "http", @@ -25,6 +26,7 @@ "license": "MIT", "description": "Secure your sveltekit app using http response security headers", "devDependencies": { + "@types/bun": "^1.1.11", "prettier": "^3.1.1", "tsup": "^8.3.0", "typescript": "^5.0.0" diff --git a/packages/securekit/test/utils.test.ts b/packages/securekit/test/utils.test.ts new file mode 100644 index 0000000..c76ab27 --- /dev/null +++ b/packages/securekit/test/utils.test.ts @@ -0,0 +1,61 @@ +import { expect, test } from 'bun:test'; +import { combineCspHeaders } from '../src/utils/cspHelpers'; +import { + normalizeHeaderValue, + normalizeCspValue, +} from '../src/utils/headerNormalizers'; + +test('combineCspHeaders should return null for both null inputs', () => { + expect(combineCspHeaders(null, null)).toBeNull(); +}); + +test('combineCspHeaders should return newCsp when existingCsp is null', () => { + const newCsp = "default-src 'self'"; + expect(combineCspHeaders(null, newCsp)).toBe(newCsp); +}); + +test('combineCspHeaders should return existingCsp when newCsp is null', () => { + const existingCsp = "default-src 'self'"; + expect(combineCspHeaders(existingCsp, null)).toBe(existingCsp); +}); + +test('combineCspHeaders should combine two CSP headers', () => { + const existingCsp = "default-src 'self'; script-src 'unsafe-inline'"; + const newCsp = "script-src 'self'; img-src 'none'"; + const expectedCsp = + "default-src 'self'; script-src 'unsafe-inline' 'self'; img-src 'none'"; + expect(combineCspHeaders(existingCsp, newCsp)).toBe(expectedCsp); +}); + +test('combineCspHeaders should handle duplicate directives correctly', () => { + const existingCsp = "default-src 'self'; script-src 'unsafe-inline'"; + const newCsp = "script-src 'unsafe-inline' 'self'"; + const expectedCsp = "default-src 'self'; script-src 'unsafe-inline' 'self'"; + expect(combineCspHeaders(existingCsp, newCsp)).toBe(expectedCsp); +}); + +test('normalizeHeaderValue should return null for null input', () => { + expect(normalizeHeaderValue(null)).toBeNull(); +}); + +test('normalizeHeaderValue should trim whitespace from string input', () => { + expect(normalizeHeaderValue(' test ')).toBe('test'); +}); + +test('normalizeHeaderValue should join array of strings with a comma and space', () => { + expect(normalizeHeaderValue(['value1', 'value2'])).toBe('value1, value2'); +}); + +test('normalizeHeaderValue should convert number to string', () => { + expect(normalizeHeaderValue(123)).toBe('123'); +}); + +test('normalizeCspValue should trim whitespace from string input', () => { + expect(normalizeCspValue(' directive ')).toBe('directive'); +}); + +test('normalizeCspValue should join array of strings with a space', () => { + expect(normalizeCspValue(['directive1', 'directive2'])).toBe( + 'directive1 directive2', + ); +}); diff --git a/turbo.json b/turbo.json index 3a75ced..f1a568e 100644 --- a/turbo.json +++ b/turbo.json @@ -3,10 +3,17 @@ "tasks": { "format": {}, "lint": {}, + "test": { + "outputs": [".svelte-kit/**", ".vercel/output/**"] + }, "dev": { "cache": false, "persistent": true }, + "preview": { + "cache": false, + "persistent": true + }, "build": { "dependsOn": ["^build"], "outputs": [ From 6435c7724d7f22d14278f601d313fcbb98c8e7fc Mon Sep 17 00:00:00 2001 From: Islam Zaoui <98623858+IslamZaoui@users.noreply.github.com> Date: Fri, 11 Oct 2024 00:22:30 +0100 Subject: [PATCH 2/3] fix: resolve testing failure by ensuring library is built before use --- turbo.json | 1 + 1 file changed, 1 insertion(+) diff --git a/turbo.json b/turbo.json index f1a568e..306a011 100644 --- a/turbo.json +++ b/turbo.json @@ -4,6 +4,7 @@ "format": {}, "lint": {}, "test": { + "dependsOn": ["^build"], "outputs": [".svelte-kit/**", ".vercel/output/**"] }, "dev": { From 5e295577b3de28589393e9cf36f5e0f252591ecb Mon Sep 17 00:00:00 2001 From: Islam Zaoui <98623858+IslamZaoui@users.noreply.github.com> Date: Fri, 11 Oct 2024 00:25:18 +0100 Subject: [PATCH 3/3] fix: install playwright --- .github/workflows/lint.yaml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.github/workflows/lint.yaml b/.github/workflows/lint.yaml index aac0390..959ad31 100644 --- a/.github/workflows/lint.yaml +++ b/.github/workflows/lint.yaml @@ -24,5 +24,8 @@ jobs: - name: Run lint run: bun lint + - name: Install playwright + run: bunx playwright install + - name: Run test run: bun run test