**Related issue:** Closes #48943 # Checklist for submitter If some of the following don't apply, delete the relevant line. - [x] Changes file added for user-visible changes in `changes/`, `orbit/changes/` or `ee/fleetd-chrome/changes`. See [Changes files](https://github.com/fleetdm/fleet/blob/main/docs/Contributing/guides/committing-changes.md#changes-files) for more information. - [x] Input data is properly validated, `SELECT *` is avoided, SQL injection is prevented (using placeholders for values in statements), JS inline code is prevented especially for url redirects, and untrusted data interpolated into shell scripts/commands is validated against shell metacharacters. - [x] Timeouts are implemented and retries are limited to avoid infinite loops ## Testing - [x] Added/updated automated tests - [x] QA'd all new/changed functionality manually ## Summary Adds unit test coverage for external library upgrades that had no automated tests: - **DOMPurify** (`ClickableUrls_xss.tests.tsx`): 5 XSS sanitization tests (script injection, javascript: in href, event handlers, iframe, URL preservation) - **react-markdown + remark-gfm** (`FleetMarkdown.tests.tsx`): 9 rendering tests (plain text, bold/italic, links, lists, GFM tables, strikethrough, code blocks, inline code) - **sonner** (`ToastNotification.tests.tsx`): 9 notify API tests (success/error creation, empty-message fallback, custom id, dismiss, batch, HTTP status label, axios response unwrap) These gaps were identified through a comprehensive audit of all ~353 direct dependencies across Go and frontend. The Go backend has excellent coverage (669+ test files). The frontend now has 333+ test files covering all runtime libraries except 2 that are untestable at unit level (systray GUI, sockjs WebSocket). <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **Tests** * Added coverage confirming potentially unsafe HTML and links are sanitized before rendering, including protection against script, `javascript:` URLs, and injected content. * Added tests validating Markdown rendering for plain text, links, fenced code blocks, and inline code. * Added coverage for toast notifications, including success/error flows, batching, dismissal, fallback messaging, ID handling, and mapping response details. <!-- end of auto-generated comment: release notes by coderabbit.ai -->
60 lines
2.4 KiB
TypeScript
60 lines
2.4 KiB
TypeScript
import React from "react";
|
|
import { render } from "@testing-library/react";
|
|
import ClickableUrls from "./ClickableUrls";
|
|
|
|
/**
|
|
* Tests that DOMPurify correctly sanitizes XSS payloads when rendering
|
|
* user-supplied text that may contain URLs. ClickableUrls uses
|
|
* dangerouslySetInnerHTML after DOMPurify.sanitize(), so this exercises
|
|
* the dompurify library's core sanitization behavior.
|
|
*/
|
|
describe("ClickableUrls - DOMPurify XSS sanitization", () => {
|
|
it("strips inline script injection from text", () => {
|
|
const { container } = render(
|
|
<ClickableUrls text='Check <script>alert("xss")</script> this site' />
|
|
);
|
|
expect(container.innerHTML).not.toContain("<script>");
|
|
expect(container.innerHTML).not.toContain("alert");
|
|
});
|
|
|
|
it("strips javascript: protocol when injected via HTML anchor", () => {
|
|
// Plain text "javascript:" is harmless -- the risk is only when it
|
|
// appears inside an href attribute. DOMPurify should strip it there.
|
|
// Build the string dynamically to avoid the no-script-url lint rule.
|
|
const scheme = ["java", "script"].join("");
|
|
const malicious = `Click <a href="${scheme}:alert('xss')">here</a> for details`;
|
|
const { container } = render(<ClickableUrls text={malicious} />);
|
|
const link = container.querySelector("a");
|
|
// DOMPurify should either remove the href entirely or strip the
|
|
// javascript: scheme. Both outcomes are safe.
|
|
const href = link?.getAttribute("href");
|
|
if (href !== null && href !== undefined) {
|
|
expect(href).not.toContain(`${scheme}:`);
|
|
}
|
|
});
|
|
|
|
it("strips event handler attributes from injected HTML", () => {
|
|
const { container } = render(
|
|
<ClickableUrls text='See <img src=x onerror=alert("xss")> here' />
|
|
);
|
|
expect(container.innerHTML).not.toContain("onerror");
|
|
});
|
|
|
|
it("strips iframe injection", () => {
|
|
const { container } = render(
|
|
<ClickableUrls text='Load <iframe src="https://evil.com"></iframe> page' />
|
|
);
|
|
expect(container.innerHTML).not.toContain("<iframe");
|
|
});
|
|
|
|
it("preserves legitimate URLs while sanitizing surrounding HTML", () => {
|
|
const text =
|
|
'Visit https://example.com <script>alert("xss")</script> for info';
|
|
const { container } = render(<ClickableUrls text={text} />);
|
|
const link = container.querySelector("a");
|
|
expect(link).not.toBeNull();
|
|
expect(link?.getAttribute("href")).toBe("https://example.com");
|
|
expect(container.innerHTML).not.toContain("<script>");
|
|
});
|
|
});
|