From a31343b61a2a1d5178921dceb0b15b3f540f5528 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=7BAI=7Df=20D=2E=20M=C3=BCller?= Date: Fri, 20 Feb 2026 12:52:42 +0100 Subject: [PATCH 1/2] fix: Resolve CodeQL security findings - anchor-modal.js: Validate anchorId against safe pattern before use in fetch() URLs (fixes js/client-side-request-forgery x3) Also validate lang code before use in URL - router.js: Validate anchorId from URL hash before passing to showAnchorDetails (fixes js/unvalidated-dynamic-method-call) - website.spec.js: Anchor GitHub URL regex to prevent partial host matching (fixes js/regex/missing-regexp-anchor) Co-Authored-By: Claude Sonnet 4.6 --- website/src/components/anchor-modal.js | 14 ++++++++++++-- website/src/utils/router.js | 5 ++++- website/tests/e2e/website.spec.js | 2 +- 3 files changed, 17 insertions(+), 4 deletions(-) diff --git a/website/src/components/anchor-modal.js b/website/src/components/anchor-modal.js index 8fdc8e4..257456a 100644 --- a/website/src/components/anchor-modal.js +++ b/website/src/components/anchor-modal.js @@ -106,20 +106,30 @@ export function closeModal() { } } +const SAFE_ANCHOR_ID = /^[a-z0-9]+(?:-[a-z0-9]+)*$/ +const SAFE_LANG = /^[a-z]{2}$/ + export async function loadAnchorContent(anchorId) { const modal = document.getElementById('anchor-modal') const titleEl = modal.querySelector('#modal-title') const contentEl = modal.querySelector('#modal-content') + if (!SAFE_ANCHOR_ID.test(anchorId)) { + contentEl.innerHTML = '
Invalid anchor ID.
' + return + } + try { // Try language-specific file first (e.g., tdd-london-school.de.adoc for German) const currentLang = i18n.currentLang() let response - if (currentLang !== 'en') { + const safeLang = SAFE_LANG.test(currentLang) ? currentLang : 'en' + + if (safeLang !== 'en') { // Try fetching language-specific anchor file response = await fetch( - `${import.meta.env.BASE_URL}docs/anchors/${anchorId}.${currentLang}.adoc` + `${import.meta.env.BASE_URL}docs/anchors/${anchorId}.${safeLang}.adoc` ) // If language-specific file not found, fallback to English diff --git a/website/src/utils/router.js b/website/src/utils/router.js index cc0aa6a..7c750e5 100644 --- a/website/src/utils/router.js +++ b/website/src/utils/router.js @@ -51,6 +51,9 @@ function handleRoute() { // Check for anchor route (#/anchor/:id) if (path.startsWith('/anchor/')) { const anchorId = path.replace('/anchor/', '') + const safeAnchorId = /^[a-z0-9]+(?:-[a-z0-9]+)*$/.test(anchorId) ? anchorId : null + if (!safeAnchorId) return + // Navigate to home first const homeHandler = routes.get('/') if (homeHandler) { @@ -60,7 +63,7 @@ function handleRoute() { // Then open the anchor modal // Import dynamically to avoid circular dependency import('../components/anchor-modal.js').then(({ showAnchorDetails }) => { - showAnchorDetails(anchorId) + showAnchorDetails(safeAnchorId) }) return } diff --git a/website/tests/e2e/website.spec.js b/website/tests/e2e/website.spec.js index 7f333e3..8989d9d 100644 --- a/website/tests/e2e/website.spec.js +++ b/website/tests/e2e/website.spec.js @@ -142,7 +142,7 @@ test.describe('Homepage - Card Grid', () => { const editBtn = firstCard.locator('.anchor-edit-btn') await expect(editBtn).toBeVisible() - await expect(editBtn).toHaveAttribute('href', /github\.com.*edit/) + await expect(editBtn).toHaveAttribute('href', /^https:\/\/github\.com\/.+\/edit\/.+/) }) test('should display action links', async ({ page }) => { From c0c8a2145ac123f23a59b1933f5ed0420dd46ec4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=7BAI=7Df=20D=2E=20M=C3=BCller?= Date: Fri, 20 Feb 2026 12:59:11 +0100 Subject: [PATCH 2/2] fix: Apply Prettier formatting to anchor-modal.js Co-Authored-By: Claude Sonnet 4.6 --- website/src/components/anchor-modal.js | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/website/src/components/anchor-modal.js b/website/src/components/anchor-modal.js index 257456a..2382ce1 100644 --- a/website/src/components/anchor-modal.js +++ b/website/src/components/anchor-modal.js @@ -128,9 +128,7 @@ export async function loadAnchorContent(anchorId) { if (safeLang !== 'en') { // Try fetching language-specific anchor file - response = await fetch( - `${import.meta.env.BASE_URL}docs/anchors/${anchorId}.${safeLang}.adoc` - ) + response = await fetch(`${import.meta.env.BASE_URL}docs/anchors/${anchorId}.${safeLang}.adoc`) // If language-specific file not found, fallback to English if (!response.ok) {