mirror of
https://github.com/layer-systems/website.git
synced 2026-09-12 05:33:12 +02:00
Address review: drop script/style entirely, guard IndexedDB persistence, fix indentation
Co-authored-by: mroxso <24775431+mroxso@users.noreply.github.com>
This commit is contained in:
committed by
GitHub
parent
e76ef0b5df
commit
4498a70626
@@ -468,7 +468,7 @@ export function markdownToDoc(markdown: string): JSONContent {
|
||||
// Table: header row, | --- | separator, then body rows.
|
||||
if (/^\|.*\|\s*$/.test(line) && i + 1 < lines.length && /^\|[\s:|-]+\|\s*$/.test(lines[i + 1])) {
|
||||
flushParagraph();
|
||||
const parseRow = (row: string, header: boolean): JSONContent => ({
|
||||
const parseRow = (row: string, header: boolean): JSONContent => ({
|
||||
type: 'tableRow',
|
||||
content: row
|
||||
.replace(/^\|/, '')
|
||||
|
||||
@@ -42,6 +42,13 @@ const ALLOWED_ELEMENTS = new Set([
|
||||
/** class="task-list" / "task-item" and data-checked carry checklist state. */
|
||||
const ALLOWED_CLASSES = new Set(['task-list', 'task-item']);
|
||||
|
||||
/**
|
||||
* Elements whose entire subtree must be dropped rather than unwrapped: their
|
||||
* text content is not safe to surface (e.g. `<script>alert(1)</script>` would
|
||||
* otherwise become a literal "alert(1)" text node).
|
||||
*/
|
||||
const REMOVE_ENTIRELY = new Set(['script', 'style']);
|
||||
|
||||
const SAFE_PROTOCOLS = new Set(['https:', 'http:', 'mailto:', 'nostr:']);
|
||||
|
||||
function sanitizeHref(value: string | null): string | null {
|
||||
@@ -94,12 +101,19 @@ function sanitizeAttributes(element: Element): void {
|
||||
}
|
||||
|
||||
function sanitizeElement(element: Element): void {
|
||||
const tag = element.tagName.toLowerCase();
|
||||
if (REMOVE_ENTIRELY.has(tag)) {
|
||||
// Drop the element and all of its content — unwrapping would surface
|
||||
// script/style text as a literal text node.
|
||||
element.remove();
|
||||
return;
|
||||
}
|
||||
|
||||
// Children first: a disallowed child is unwrapped before we look up again.
|
||||
for (const child of [...element.children]) {
|
||||
sanitizeElement(child);
|
||||
}
|
||||
|
||||
const tag = element.tagName.toLowerCase();
|
||||
if (!ALLOWED_ELEMENTS.has(tag)) {
|
||||
// Unwrap: keep the (already sanitized) children, drop the wrapper. This
|
||||
// keeps the text of a <div> or <span> instead of deleting it.
|
||||
|
||||
@@ -28,6 +28,21 @@ function indexedDbAvailable(): boolean {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructing `IndexeddbPersistence` can throw even when IndexedDB exists
|
||||
* (e.g. blocked/denied storage in private browsing or locked-down
|
||||
* environments). Treat persistence as best-effort and degrade to `null`
|
||||
* (in-memory) rather than crashing the caller.
|
||||
*/
|
||||
function createPersistence(documentId: string, doc: Y.Doc): IndexeddbPersistence | null {
|
||||
if (!indexedDbAvailable()) return null;
|
||||
try {
|
||||
return new IndexeddbPersistence(`${DB_PREFIX}${documentId}`, doc);
|
||||
} catch {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Opens the Yjs document for `documentId` with offline persistence behind it.
|
||||
*
|
||||
@@ -49,9 +64,7 @@ export function openDocumentSession(
|
||||
events: DocumentSessionEvents,
|
||||
): DocumentSession {
|
||||
const doc = new Y.Doc();
|
||||
const persistence = indexedDbAvailable()
|
||||
? new IndexeddbPersistence(`${DB_PREFIX}${documentId}`, doc)
|
||||
: null;
|
||||
const persistence = createPersistence(documentId, doc);
|
||||
|
||||
const onUpdate = () => events.onUpdate();
|
||||
doc.on('update', onUpdate);
|
||||
@@ -91,13 +104,16 @@ export function openDocumentSession(
|
||||
|
||||
/** Removes the persisted body of a document that was deleted from the index. */
|
||||
export async function deletePersistedDocument(documentId: string): Promise<void> {
|
||||
if (!indexedDbAvailable()) return;
|
||||
const doc = new Y.Doc();
|
||||
const persistence = new IndexeddbPersistence(`${DB_PREFIX}${documentId}`, doc);
|
||||
try {
|
||||
await persistence.clearData();
|
||||
const persistence = createPersistence(documentId, doc);
|
||||
if (!persistence) return;
|
||||
try {
|
||||
await persistence.clearData();
|
||||
} finally {
|
||||
persistence.destroy();
|
||||
}
|
||||
} finally {
|
||||
persistence.destroy();
|
||||
doc.destroy();
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user