The three-regex gate could be bypassed (an attribute without a space, <svg/onload>, entity-encoded or tab-split javascript: URLs, MathML xlink:href, base, meta refresh, forms) and it blanked a whole post or category when ordinary text such as "Data: 12 maja 2024" matched. - sanitizeJournalHtml runs sanitize-html with the Journal plugin's bluemonday policy: UGC elements plus footer, class, schema.org microdata, four validated inline styles, footnote roles, dir=auto, two chat transcript data attributes; http, https, mailto and relative URLs only - gateJournalHtml and isSafeJournalHtml keep their signatures; html is always the sanitized value and is never blanked - post body, category content and both page payloads are sanitized; the blocked notice, its style and string, and the blocked props and content_blocked field are removed - toPostView moves to postView.ts so list pages and cards do not load the sanitizer - tests cover every bypass from the review, the Data:/metadata: text and an imported author footer; the SSR smoke expects the unsafe sample's safe paragraphs
22 lines
810 B
TypeScript
22 lines
810 B
TypeScript
// The post page's SSR payload: the post with its body sanitized, without the
|
|
// raw `content` source or `translations`, and with related posts as list
|
|
// rows. Kept apart from view.ts because it loads the sanitizer, which list
|
|
// pages and cards do not need.
|
|
|
|
import { sanitizeJournalHtml } from './htmlGate.ts'
|
|
import type { JournalPostDetail } from './types.ts'
|
|
import { toListPost } from './view.ts'
|
|
|
|
/** A post page's data: the post with its body sanitized and no source or translations. */
|
|
export type JournalPostView = JournalPostDetail
|
|
|
|
export function toPostView(post: JournalPostDetail): JournalPostView {
|
|
return {
|
|
...post,
|
|
content: null,
|
|
translations: null,
|
|
content_html: sanitizeJournalHtml(post.content_html),
|
|
related_posts: (post.related_posts ?? []).map(toListPost),
|
|
}
|
|
}
|