mirror of
https://github.com/multica-ai/multica.git
synced 2026-07-31 00:40:46 +02:00
* fix: sanitize markdown rendering in comments and shared renderers Add rehype-sanitize to both ReadonlyContent and Markdown components so that raw HTML parsed by rehype-raw is sanitized against a strict allowlist before reaching the DOM. On the backend, add a bluemonday sanitization pass when creating and updating comments to strip dangerous tags as defense-in-depth. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: add mention:// protocol to sanitize allowlist and validate file card URLs - Add mention:// to rehype-sanitize protocols.href in both ReadonlyContent and Markdown so @mention links survive sanitization - Validate data-href on file cards to only allow http(s) URLs, blocking javascript: and data: schemes in both frontend click handler and backend bluemonday policy - Narrow class attribute allowlist to specific elements (code, div, span, pre) Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
94 lines
2.4 KiB
Go
94 lines
2.4 KiB
Go
package sanitize
|
|
|
|
import (
|
|
"testing"
|
|
)
|
|
|
|
func TestHTML(t *testing.T) {
|
|
tests := []struct {
|
|
name string
|
|
input string
|
|
want string
|
|
}{
|
|
{
|
|
name: "plain text",
|
|
input: "hello world",
|
|
want: "hello world",
|
|
},
|
|
{
|
|
name: "safe markdown",
|
|
input: "**bold** and *italic*",
|
|
want: "**bold** and *italic*",
|
|
},
|
|
{
|
|
name: "script tag stripped",
|
|
input: `<script>alert(1)</script>`,
|
|
want: "",
|
|
},
|
|
{
|
|
name: "iframe stripped",
|
|
input: `<iframe srcdoc="<script>parent.__xss=1</script>"></iframe>`,
|
|
want: "",
|
|
},
|
|
{
|
|
name: "img with onerror stripped",
|
|
input: `<img src=x onerror="alert(1)">`,
|
|
want: `<img src="x">`,
|
|
},
|
|
{
|
|
name: "safe link preserved",
|
|
input: `<a href="https://example.com">link</a>`,
|
|
want: `<a href="https://example.com" rel="nofollow">link</a>`,
|
|
},
|
|
{
|
|
name: "file card div preserved",
|
|
input: `<div data-type="fileCard" data-href="https://cdn.example.com/file.pdf" data-filename="report.pdf"></div>`,
|
|
want: `<div data-type="fileCard" data-href="https://cdn.example.com/file.pdf" data-filename="report.pdf"></div>`,
|
|
},
|
|
{
|
|
name: "object tag stripped",
|
|
input: `<object data="evil.swf"></object>`,
|
|
want: "",
|
|
},
|
|
{
|
|
name: "embed tag stripped",
|
|
input: `<embed src="evil.swf">`,
|
|
want: "",
|
|
},
|
|
{
|
|
name: "style tag stripped",
|
|
input: `<style>body{display:none}</style>`,
|
|
want: "",
|
|
},
|
|
{
|
|
name: "mention link preserved",
|
|
input: `[@User](mention://member/abc-123)`,
|
|
want: `[@User](mention://member/abc-123)`,
|
|
},
|
|
{
|
|
name: "file card with javascript href stripped",
|
|
input: `<div data-type="fileCard" data-href="javascript:alert(1)" data-filename="evil.pdf"></div>`,
|
|
want: `<div data-type="fileCard" data-filename="evil.pdf"></div>`,
|
|
},
|
|
{
|
|
name: "file card with data URI stripped",
|
|
input: `<div data-type="fileCard" data-href="data:text/html,<script>alert(1)</script>" data-filename="x.html"></div>`,
|
|
want: `<div data-type="fileCard" data-filename="x.html"></div>`,
|
|
},
|
|
{
|
|
name: "file card with http href preserved",
|
|
input: `<div data-type="fileCard" data-href="http://example.com/file.pdf" data-filename="file.pdf"></div>`,
|
|
want: `<div data-type="fileCard" data-href="http://example.com/file.pdf" data-filename="file.pdf"></div>`,
|
|
},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
got := HTML(tt.input)
|
|
if got != tt.want {
|
|
t.Errorf("HTML(%q) = %q, want %q", tt.input, got, tt.want)
|
|
}
|
|
})
|
|
}
|
|
}
|