Codebase-wide sweep bringing the web client into strict compliance with the AGENTS.md TypeScript and Mantine/React conventions: ~170 inline 2+-static-prop elements extracted to named .withProps() constants across ~77 files, the AGENTS.md rule wording tightened to match (static-prop boundary + Accordion/Transition/data-*/Box exemptions), a real ScreenStage Box->Flex layout regression fixed (direction: column) with a Storybook regression guard, and an App.tsx double-cast tightened. Closes #1762.
41 lines
1.3 KiB
TypeScript
41 lines
1.3 KiB
TypeScript
import { Box } from "@mantine/core";
|
|
import { useMemo } from "react";
|
|
import { wrapHtmlWithCsp } from "./contentViewerUtils";
|
|
import { useObjectUrl } from "./useObjectUrl";
|
|
|
|
/**
|
|
* Render an HTML resource inside a hardened iframe. Defense is layered:
|
|
*
|
|
* - `sandbox=""` — explicitly empty: no `allow-scripts`, `allow-forms`, or
|
|
* `allow-same-origin`, so scripts can't run and the frame is origin-isolated.
|
|
* - A `Content-Security-Policy` `<meta>` is injected (see {@link wrapHtmlWithCsp})
|
|
* as defense-in-depth — correct even if the sandbox is later loosened.
|
|
* - The document is served from a `Blob` object URL (revoked on unmount) rather
|
|
* than `srcdoc`, keeping it off the parent's origin.
|
|
*/
|
|
export interface HtmlFrameProps {
|
|
/** The raw HTML document or fragment to preview. */
|
|
html: string;
|
|
}
|
|
|
|
export function HtmlFrame({ html }: HtmlFrameProps) {
|
|
const blob = useMemo(
|
|
() => new Blob([wrapHtmlWithCsp(html)], { type: "text/html" }),
|
|
[html],
|
|
);
|
|
const url = useObjectUrl(blob);
|
|
return (
|
|
// Box+iframe is a native element (not a Mantine primitive), so the
|
|
// `.withProps()` extraction rule doesn't apply.
|
|
<Box
|
|
component="iframe"
|
|
title="HTML preview"
|
|
src={url}
|
|
sandbox=""
|
|
w="100%"
|
|
h={400}
|
|
bd={0}
|
|
display="block"
|
|
/>
|
|
);
|
|
}
|