submissions(#23): format submission bodies (safe markdown-lite)

Submission cards showed the raw form markdown ("### Bug Report", "- npub: …").
Added a tiny, SAFE renderer: `#…###` headings → bold lines, leading `- `/`* `
bullet markers stripped, `**bold**` honored inline — built entirely with
textContent/createTextNode + <strong>, so no HTML is ever parsed from the
content (submissions are untrusted public events; rendering raw markdown/HTML
would be an XSS hole). Lint (innerHTML check) + build clean; tests 26/26.

Co-Authored-By: Claude Opus 4.8 <[email protected]>
This commit is contained in:
thePR0M3TH3ANandClaude Opus 4.8 committed 2026-07-07 09:21:26 -04:00
1 parent ba287a62be
commit 36cb71b676
2 files changed
+56 -3

No files matched your search

+13 -1
View File
@@ -3210,10 +3210,22 @@
}
.submission-card__body {
display: flex;
flex-direction: column;
gap: var(--space-3xs);
}
.submission-card__heading {
margin: 0;
font-size: var(--font-size-sm);
font-weight: 600;
color: var(--color-text-strong);
}
.submission-card__line {
margin: 0;
font-size: var(--font-size-sm);
color: var(--color-text);
white-space: pre-wrap;
word-break: break-word;
}
+43 -2
View File
@@ -69,6 +69,47 @@ export function renderSubmissions(controller) {
}
}
// Render `**bold**` segments as <strong>; everything else as plain text — never
// parse HTML from the content (submissions are untrusted public events).
function appendInline(el, text) {
const parts = String(text).split("**");
parts.forEach((part, i) => {
if (!part) {
return;
}
if (i % 2 === 1) {
const strong = document.createElement("strong");
strong.textContent = part;
el.appendChild(strong);
} else {
el.appendChild(document.createTextNode(part));
}
});
}
// Tiny, SAFE markdown-lite renderer for the form-generated body: `#…###`
// headings become bold lines, leading `- `/`* ` bullet markers are stripped,
// `**bold**` is honored inline. No HTML is ever parsed from the content.
function renderSubmissionBody(container, content) {
for (const raw of String(content).split("\n")) {
const line = raw.replace(/\s+$/, "");
if (!line.trim()) {
continue;
}
if (/^#{1,6}\s/.test(line)) {
const heading = document.createElement("p");
heading.className = "submission-card__heading";
appendInline(heading, line.replace(/^#{1,6}\s+/, ""));
container.appendChild(heading);
} else {
const p = document.createElement("p");
p.className = "submission-card__line";
appendInline(p, line.replace(/^[-*]\s+/, ""));
container.appendChild(p);
}
}
}
function renderSubmissionRow(submission) {
const li = document.createElement("li");
li.className = "submission-card";
@@ -90,9 +131,9 @@ function renderSubmissionRow(submission) {
li.appendChild(head);
if (submission.content) {
const body = document.createElement("p");
const body = document.createElement("div");
body.className = "submission-card__body";
body.textContent = submission.content;
renderSubmissionBody(body, submission.content);
li.appendChild(body);
}