diff --git a/website/src/components/anchor-modal.js b/website/src/components/anchor-modal.js index 8fdc8e4..2382ce1 100644 --- a/website/src/components/anchor-modal.js +++ b/website/src/components/anchor-modal.js @@ -106,21 +106,29 @@ 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` - ) + 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) { 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 }) => {