summaryrefslogtreecommitdiffhomepage
path: root/src/features/markdown/ui
diff options
context:
space:
mode:
authorAdam Malczewski <[email protected]>2026-10-21 14:37:28 +0900
committerAdam Malczewski <[email protected]>2026-10-21 14:37:28 +0900
commit6a2a40d64737b3cb678740c8f85027c3be0000bc (patch)
tree316420c29b54a76def9c4e41885a682dcaa1e3e9 /src/features/markdown/ui
parent8eefbc5287f73414df7feba6c78dc8ca73783b1d (diff)
downloaddispatch-web-6a2a40d64737b3cb678740c8f85027c3be0000bc.tar.gz
dispatch-web-6a2a40d64737b3cb678740c8f85027c3be0000bc.zip
fix(review-followup-feb): block raw Markdown fetch vectors and guard preview
Diffstat (limited to 'src/features/markdown/ui')
-rw-r--r--src/features/markdown/ui/markdown.test.ts19
1 files changed, 19 insertions, 0 deletions
diff --git a/src/features/markdown/ui/markdown.test.ts b/src/features/markdown/ui/markdown.test.ts
index 149bff5..b86cc0a 100644
--- a/src/features/markdown/ui/markdown.test.ts
+++ b/src/features/markdown/ui/markdown.test.ts
@@ -62,6 +62,25 @@ describe("Markdown", () => {
).toBeInTheDocument();
});
+ it.each([
+ false,
+ true,
+ ])("keeps non-image HTML inert before and after image consent (streaming=%s)", async (streaming) => {
+ const { container } = render(Markdown, {
+ props: {
+ text: '<div style="background:url(https://evil.example/bg)"><video poster="https://evil.example/poster" src="https://evil.example/video"></video><svg><image href="https://evil.example/svg" /></svg><input type="image" src="https://evil.example/input"><img src="https://evil.example/allowed-after-click"></div>',
+ streaming,
+ },
+ });
+ expect(container.querySelector("video,svg,input,[style],img")).toBeNull();
+ await fireEvent.click(screen.getByRole("button", { name: "Load image from evil.example" }));
+ expect(container.querySelectorAll("img")).toHaveLength(1);
+ expect(container.querySelector("img")?.getAttribute("src")).toBe(
+ "https://evil.example/allowed-after-click",
+ );
+ expect(container.querySelector("video,svg,input,[style]")).toBeNull();
+ });
+
it("rejects an unsafe URL in a forged placeholder", async () => {
const { container } = render(Markdown, {
props: { text: '<button class="remote-image" data-src="javascript:alert(1)">Load</button>' },