diff --git a/package.json b/package.json
index 8743467b56..74921c60f5 100644
--- a/package.json
+++ b/package.json
@@ -51,7 +51,7 @@
"player:perf": "bun run --filter @hyperframes/player perf",
"format:check": "oxfmt --check .",
"knip": "knip",
- "test:scripts": "node --import tsx --test scripts/animejs-v4-guidance.test.mjs scripts/check-tracked-artifacts.test.mjs scripts/check-registry-set-delta.test.mjs scripts/check-no-main-deletions.test.mjs scripts/check-pr-captures.test.mjs scripts/check-docs-snippet-motion.test.mjs scripts/registry-target-paths.test.mjs scripts/check-workspace-contracts.test.mjs scripts/check-media-use-copy-parity.test.mjs scripts/check-package-cycles.test.mjs scripts/check-cli-process-ownership.test.mjs scripts/check-large-files.test.mjs scripts/package-subpaths.test.mjs scripts/validate-release-channel.test.mjs scripts/publish-workflow.test.mjs scripts/install-workspace-dependencies.test.mjs scripts/draft-changelog.test.ts scripts/set-version.test.ts scripts/release-prepare.test.ts scripts/cli-options.test.ts scripts/changelog-weekly.test.ts scripts/claude-plugin-compression.test.ts scripts/catalog-payload-assets.test.ts scripts/host-registry-assets.test.ts scripts/catalog-preview-temp.test.ts scripts/catalog-hosted-files.test.ts scripts/player-cdn-pin.test.ts scripts/studio-runtime-smoke.test.mjs scripts/verify-packed-manifests.test.mjs scripts/lint-skills.test.mjs scripts/creator-editing-recipes.test.mjs packages/gcp-cloud-run/check-dockerfile-workspaces.test.mjs packages/core/scripts/writeGeneratedFile.test.ts scripts/catalog-drift.test.ts scripts/catalog-fetch-mirror.test.ts scripts/catalog-script-inlining.test.ts scripts/catalog-detail.test.ts scripts/generate-catalog-pages.test.ts scripts/verify-catalog-payloads.test.ts scripts/registry-skill-files.test.ts scripts/creator-editing-capabilities.test.mjs scripts/generate-catalog-previews.test.ts scripts/registry-primitive-payloads.test.ts registry/components/pan-stations/pan-stations.test.mjs && vitest run scripts/catalog/ scripts/contrastRatchet.test.ts scripts/generate-catalog-payloads.test.ts",
+ "test:scripts": "node --import tsx --test scripts/animejs-v4-guidance.test.mjs scripts/check-tracked-artifacts.test.mjs scripts/check-registry-set-delta.test.mjs scripts/check-no-main-deletions.test.mjs scripts/check-pr-captures.test.mjs scripts/check-docs-snippet-motion.test.mjs scripts/registry-target-paths.test.mjs scripts/check-workspace-contracts.test.mjs scripts/check-media-use-copy-parity.test.mjs scripts/check-svg-sanitize-parity.test.mjs scripts/check-package-cycles.test.mjs scripts/check-cli-process-ownership.test.mjs scripts/check-large-files.test.mjs scripts/package-subpaths.test.mjs scripts/validate-release-channel.test.mjs scripts/publish-workflow.test.mjs scripts/install-workspace-dependencies.test.mjs scripts/draft-changelog.test.ts scripts/set-version.test.ts scripts/release-prepare.test.ts scripts/cli-options.test.ts scripts/changelog-weekly.test.ts scripts/claude-plugin-compression.test.ts scripts/catalog-payload-assets.test.ts scripts/host-registry-assets.test.ts scripts/catalog-preview-temp.test.ts scripts/catalog-hosted-files.test.ts scripts/player-cdn-pin.test.ts scripts/studio-runtime-smoke.test.mjs scripts/verify-packed-manifests.test.mjs scripts/lint-skills.test.mjs scripts/creator-editing-recipes.test.mjs packages/gcp-cloud-run/check-dockerfile-workspaces.test.mjs packages/core/scripts/writeGeneratedFile.test.ts scripts/catalog-drift.test.ts scripts/catalog-fetch-mirror.test.ts scripts/catalog-script-inlining.test.ts scripts/catalog-detail.test.ts scripts/generate-catalog-pages.test.ts scripts/verify-catalog-payloads.test.ts scripts/registry-skill-files.test.ts scripts/creator-editing-capabilities.test.mjs scripts/generate-catalog-previews.test.ts scripts/registry-primitive-payloads.test.ts registry/components/pan-stations/pan-stations.test.mjs && vitest run scripts/catalog/ scripts/contrastRatchet.test.ts scripts/generate-catalog-payloads.test.ts",
"typecheck:scripts": "tsc --noEmit -p scripts/tsconfig.json",
"test:skills": "node --test 'skills/**/*.test.mjs' 'packages/cli/src/media-use/**/*.test.mjs'",
"generate:previews": "tsx scripts/generate-template-previews.ts",
diff --git a/packages/cli/src/media-use/lib/freeze.mjs b/packages/cli/src/media-use/lib/freeze.mjs
index dbfc12741d..b8ca20e50f 100644
--- a/packages/cli/src/media-use/lib/freeze.mjs
+++ b/packages/cli/src/media-use/lib/freeze.mjs
@@ -1,5 +1,5 @@
import { fetchMedia, isPublicMediaUrl } from "./media-fetch.mjs";
-import { sanitizeSvg } from "@hyperframes/core/sanitize-svg";
+import { sanitizeSvg } from "./svg-sanitize.mjs";
import { writeFileSync, copyFileSync, mkdirSync, readFileSync } from "node:fs";
import { dirname } from "node:path";
diff --git a/packages/cli/src/media-use/lib/svg-sanitize.mjs b/packages/cli/src/media-use/lib/svg-sanitize.mjs
new file mode 100644
index 0000000000..21b435e087
--- /dev/null
+++ b/packages/cli/src/media-use/lib/svg-sanitize.mjs
@@ -0,0 +1,36 @@
+// Port of packages/core/src/figma/sanitizeSvg.ts; parity enforced by scripts/check-svg-sanitize-parity.test.mjs.
+
+function replaceStable(input, pattern, replacement) {
+ let out = input;
+ let prev;
+ do {
+ prev = out;
+ out = out.replace(pattern, replacement);
+ } while (out !== prev);
+ return out;
+}
+
+function isAllowedHref(value) {
+ const v = value.trim().toLowerCase();
+ return v.startsWith("#") || v.startsWith("data:image/");
+}
+
+export function sanitizeSvg(svg) {
+ let out = svg;
+ out = replaceStable(out, /", "script element"],
+ ['', "self-closing script"],
+ ['', "style block"],
+ ['', "foreignObject subtree"],
+ ["", "self-closing foreignObject"],
+ ['', "quoted on* handler"],
+ [``, "single-quoted on* handler"],
+ ["", "unquoted on* handler"],
+ ['', "javascript: href"],
+ ['', "javascript: xlink:href"],
+ ['', "allowed data:image/ href"],
+ ['', "allowed local fragment href"],
+ ['', "data:text/html href"],
+ [
+ "",
+ "nested/overlapping script tags",
+ ],
+ [
+ "",
+ "nested foreignObject",
+ ],
+ [
+ '',
+ "nested svg element",
+ ],
+ ["", "mixed-case on* handler names"],
+ ['', "mixed-case href attribute name"],
+];
+
+describe("svg-sanitize parity: core vs media-use port", () => {
+ for (const [input, label] of HOSTILE_CORPUS) {
+ it(`matches core output for: ${label}`, () => {
+ assert.equal(mediaUseSanitizeSvg(input), coreSanitizeSvg(input));
+ });
+ }
+
+ it("both strip every hostile marker from the full corpus", () => {
+ for (const [input] of HOSTILE_CORPUS) {
+ const out = mediaUseSanitizeSvg(input);
+ assert.ok(!/