Skip to content

Commit 65fa6e2

Browse files
Copilotcubap
andcommitted
Improve alert security and encapsulation
- Encode URL parameters to prevent injection vulnerabilities - Replace innerHTML with DOM element creation for better security - Add CSS via style element instead of inline styles - Add fallback for potential future public API on AlertContainer Co-authored-by: cubap <1119165+cubap@users.noreply.github.com>
1 parent ab582e5 commit 65fa6e2

1 file changed

Lines changed: 48 additions & 9 deletions

File tree

interfaces/transcription/index.js

Lines changed: 48 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -424,12 +424,46 @@ export default class TranscriptionInterface extends HTMLElement {
424424
const alertContainer = document.querySelector('tpen-alert-container')
425425
if (!alertContainer) return
426426

427+
// Build URL with proper encoding
428+
const projectId = encodeURIComponent(TPEN.screen?.projectInQuery ?? '')
429+
const pageId = encodeURIComponent(TPEN.screen?.pageInQuery ?? '')
430+
const annotatorUrl = `/interfaces/annotator?projectId=${projectId}&pageId=${pageId}`
431+
427432
const alertElem = document.createElement('tpen-alert')
428-
const message = document.createElement('div')
429-
message.innerHTML = `
430-
<p style="margin-bottom: 1em;">This page has no line annotations defined.</p>
431-
<p>To add lines, visit the <a href="/interfaces/annotator?projectId=${TPEN.screen?.projectInQuery ?? ''}&pageId=${TPEN.screen?.pageInQuery ?? ''}" style="color: #4fc3f7; text-decoration: underline;">annotation interface</a>.</p>
433+
434+
// Add styles for the alert content
435+
const style = document.createElement('style')
436+
style.textContent = `
437+
.no-lines-message p:first-child {
438+
margin-bottom: 1em;
439+
}
440+
.alert-link {
441+
color: #4fc3f7;
442+
text-decoration: underline;
443+
}
432444
`
445+
alertElem.shadowRoot.appendChild(style)
446+
447+
// Create message with proper DOM elements
448+
const messageContainer = document.createElement('div')
449+
messageContainer.classList.add('no-lines-message')
450+
451+
const paragraph1 = document.createElement('p')
452+
paragraph1.textContent = 'This page has no line annotations defined.'
453+
454+
const paragraph2 = document.createElement('p')
455+
paragraph2.textContent = 'To add lines, visit the '
456+
457+
const link = document.createElement('a')
458+
link.href = annotatorUrl
459+
link.textContent = 'annotation interface'
460+
link.classList.add('alert-link')
461+
462+
paragraph2.appendChild(link)
463+
paragraph2.appendChild(document.createTextNode('.'))
464+
465+
messageContainer.appendChild(paragraph1)
466+
messageContainer.appendChild(paragraph2)
433467

434468
const okButton = document.createElement('button')
435469
const buttonContainer = document.createElement('div')
@@ -441,14 +475,19 @@ export default class TranscriptionInterface extends HTMLElement {
441475
}
442476
okButton.addEventListener('click', handleOk)
443477

444-
alertElem.appendChild(message)
478+
alertElem.appendChild(messageContainer)
445479
buttonContainer.appendChild(okButton)
446480
alertElem.appendChild(buttonContainer)
447481

448-
const screenLockingSection = alertContainer.shadowRoot.querySelector('.alert-area')
449-
if (screenLockingSection) {
450-
screenLockingSection.appendChild(alertElem)
451-
alertElem.show()
482+
// Use the container's method if available, otherwise access shadow DOM
483+
if (typeof alertContainer.addCustomAlert === 'function') {
484+
alertContainer.addCustomAlert(alertElem)
485+
} else {
486+
const screenLockingSection = alertContainer.shadowRoot.querySelector('.alert-area')
487+
if (screenLockingSection) {
488+
screenLockingSection.appendChild(alertElem)
489+
alertElem.show()
490+
}
452491
}
453492
}
454493

0 commit comments

Comments
 (0)