Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 12 additions & 4 deletions website/src/components/anchor-modal.js
Original file line number Diff line number Diff line change
Expand Up @@ -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 = '<div class="text-red-500">Invalid anchor ID.</div>'
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) {
Expand Down
5 changes: 4 additions & 1 deletion website/src/utils/router.js
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand All @@ -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
}
Expand Down
2 changes: 1 addition & 1 deletion website/tests/e2e/website.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -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 }) => {
Expand Down
Loading