Conversation
Implements Google Search structured data for /threads/[id] per discussion-forum guidelines: WebPage with mainEntity DiscussionForumPosting (author, datePublished, text, headline, url) and Comment entities for replies. Co-authored-by: Cursor <cursoragent@cursor.com>
Modified the MessageCard component to show the TL;DR section only when it is available and not empty, enhancing the user experience by providing relevant information when applicable.
Appwrite WebsiteProject ID: Website (appwrite/website)Project ID: Tip Custom domains work with both CNAME for subdomains and NS records for apex domains |
Greptile SummaryThis PR adds
Confidence Score: 3/5Not safe to merge until the JSON-LD payload is HTML-escaped to prevent A P1 security finding (XSS via unescaped
|
| Filename | Overview |
|---|---|
| src/lib/utils/metadata.ts | Adds createDiscussionForumPageSchema for JSON-LD generation; user-supplied content is not HTML-escaped before injection into a <script> tag, creating a XSS vector via </script> in thread/message data. |
| src/routes/threads/[id]/+page.server.ts | Server load function correctly constructs the canonical URL and maps thread/message fields into the schema helper; no issues beyond what the helper itself exposes. |
| src/routes/threads/[id]/+page.svelte | Injects the JSON-LD block via {@html} inside <svelte:head>; also fixes TL;DR guard to skip empty strings. Safe as long as the payload from metadata.ts is properly escaped. |
Reviews (1): Last reviewed commit: "format" | Re-trigger Greptile
| mainEntity | ||
| }; | ||
|
|
||
| return JSON.stringify(graph); |
There was a problem hiding this comment.
XSS via unescaped
</script> in user-generated JSON-LD
JSON.stringify does not HTML-encode < or >, so if any user-supplied field (thread title, message text, or author name) contains the literal string </script>, it will appear verbatim in the serialized JSON and prematurely close the surrounding <script type="application/ld+json"> tag. An attacker on Discord could craft a message body to inject arbitrary script into every visitor's browser. The standard fix is to Unicode-escape the three dangerous characters before returning:
return JSON.stringify(graph)
.replace(/</g, '\\u003c')
.replace(/>/g, '\\u003e')
.replace(/&/g, '\\u0026');| function nonEmptyText(value: string, fallback: string): string { | ||
| const t = value.trim(); | ||
| return t.length > 0 ? value : fallback; | ||
| } |
There was a problem hiding this comment.
nonEmptyText returns untrimmed value, inconsistently
The helper trims value to decide whether it is empty, but then returns the original (untrimmed) value when non-empty. Returning t (the already-trimmed copy) is more consistent and avoids passing leading/trailing whitespace into JSON-LD fields.
| function nonEmptyText(value: string, fallback: string): string { | |
| const t = value.trim(); | |
| return t.length > 0 ? value : fallback; | |
| } | |
| function nonEmptyText(value: string, fallback: string): string { | |
| const t = value.trim(); | |
| return t.length > 0 ? t : fallback; | |
| } |
| function toIso8601DateTime(value: string): string { | ||
| const d = new Date(value); | ||
| return Number.isNaN(d.getTime()) ? new Date().toISOString() : d.toISOString(); | ||
| } |
There was a problem hiding this comment.
Silent date fallback hides bad data
toIso8601DateTime silently substitutes the current server render time when given an unparseable date string, producing a plausible-looking ISO timestamp that masks a data quality issue upstream and can mislead Google Search Console.
| function toIso8601DateTime(value: string): string { | |
| const d = new Date(value); | |
| return Number.isNaN(d.getTime()) ? new Date().toISOString() : d.toISOString(); | |
| } | |
| function toIso8601DateTime(value: string): string { | |
| const d = new Date(value); | |
| if (Number.isNaN(d.getTime())) { | |
| console.warn(`[metadata] Invalid date string: "${value}"`); | |
| return ''; | |
| } | |
| return d.toISOString(); | |
| } |


What does this PR do?
(Provide a description of what this PR does.)
Test Plan
(Write your test plan here. If you changed any code, please provide us with clear instructions on how you verified your changes work.)
Related PRs and Issues
(If this PR is related to any other PR or resolves any issue or related to any issue link all related PR and issues here.)
Have you read the Contributing Guidelines on issues?
(Write your answer here.)