From 331d843f68c89b5945238f8fa9466470e959f488 Mon Sep 17 00:00:00 2001 From: lew Date: Thu, 26 Mar 2026 22:01:17 +0000 Subject: [PATCH] feat: escapes html in guestbook, and adds a missing label --- www/src/lib/format.ts | 9 +++++++++ www/src/pages/index.astro | 17 +++++++++++------ www/src/scripts/guestbook-sign.ts | 3 ++- www/src/styles/global.css | 12 ++++++++++++ 4 files changed, 34 insertions(+), 7 deletions(-) diff --git a/www/src/lib/format.ts b/www/src/lib/format.ts index e82cca5..a011492 100644 --- a/www/src/lib/format.ts +++ b/www/src/lib/format.ts @@ -1,3 +1,12 @@ +export function escapeHtml(str: string): string { + return str + .replace(/&/g, '&') + .replace(//g, '>') + .replace(/"/g, '"') + .replace(/'/g, '''); +} + export function formatDate(date: Date): string { const d = String(date.getDate()).padStart(2, '0'); const m = String(date.getMonth() + 1).padStart(2, '0'); diff --git a/www/src/pages/index.astro b/www/src/pages/index.astro index b78acb2..0896843 100644 --- a/www/src/pages/index.astro +++ b/www/src/pages/index.astro @@ -2,7 +2,7 @@ import { getCollection } from 'astro:content'; import Layout from '../layouts/Layout.astro'; import { getApprovedEntries, type GuestbookEntry } from '../lib/db'; -import { formatDate, formatListItem } from '../lib/format'; +import { formatDate, formatListItem, escapeHtml } from '../lib/format'; import { organizePostsByCategory, getSlug, enrichPostsWithDates } from '../lib/md'; import { getTxtFiles } from '../lib/txt'; import { DEFAULT_CATEGORY, SECTIONS, SUBDOMAINS } from '../lib/consts'; @@ -64,13 +64,18 @@ const urls = [
 {
   const prefix = i === 0 ? labelPrefix(SECTIONS.guestbook, `?just=${SECTIONS.guestbook}`) : blankPrefix;
-  const nameHtml = e.url ? `${e.name}` : `${e.name}`;
-  return `${prefix}${formatDate(e.createdAt)}  ${nameHtml} ${e.message.replace(/\n/g, ' ')}`;
+  const safeName = escapeHtml(e.name);
+  const safeMessage = escapeHtml(e.message.replace(/\n/g, ' '));
+  const nameHtml = e.url ? `${safeName}` : `${safeName}`;
+  return `${prefix}${formatDate(e.createdAt)}  ${nameHtml} ${safeMessage}`;
 }).join('')} />
   
-
-
-
+ +
+ +
+ +
diff --git a/www/src/scripts/guestbook-sign.ts b/www/src/scripts/guestbook-sign.ts index b04befb..7bbb995 100644 --- a/www/src/scripts/guestbook-sign.ts +++ b/www/src/scripts/guestbook-sign.ts @@ -31,7 +31,8 @@ export function initGuestbookForm() { } else if (res.status === 429) { status.textContent = ' too many requests, try later.'; } else { - status.textContent = ' error'; + const body = await res.json().catch(() => null); + status.textContent = body?.error ? ` ${body.error}` : ' error'; } } catch { status.textContent = ' failed'; diff --git a/www/src/styles/global.css b/www/src/styles/global.css index 3b9574d..7a1054d 100644 --- a/www/src/styles/global.css +++ b/www/src/styles/global.css @@ -125,3 +125,15 @@ html[data-compact] .guestbook-entry { html[data-compact] .guestbook-form { margin-left: 0 !important; } + +.sr-only { + position: absolute; + width: 1px; + height: 1px; + padding: 0; + margin: -1px; + overflow: hidden; + clip: rect(0, 0, 0, 0); + white-space: nowrap; + border: 0; +}