fix(security): close remaining high-severity CodeQL alerts

- svg-sanitize.ts: strip each dangerous element repeatedly until stable with
  whitespace-tolerant end tags, defeating nested/overlapping tags (closes 5
  incomplete-multi-character-sanitization + 1 bad-tag-filter; the prior
  single-pass regex could leave a residual <script>/<iframe>).
- file-preview.ts: add a resolve()+containment barrier (the path-traversal
  guard CodeQL recognizes) on top of the id charset check (closes 9
  path-injection).
- metadata.ts: bound the XMP namespace:name key segments so parseXmp cannot
  backtrack polynomially (closes js/polynomial-redos).
- analytics-disabled.spec.ts: match analytics by URL host, not substring
  (closes 4 incomplete-url-substring-sanitization).

typecheck + lint green; svg (119), preview (22), metadata (164) tests pass.
This commit is contained in:
SnapOtter
2026-06-21 13:47:22 +08:00
parent 4fdd10f488
commit bdadb843d8
4 changed files with 63 additions and 30 deletions
+13 -6
View File
@@ -57,13 +57,20 @@ test.describe("Analytics disabled by server", () => {
// Intercept ALL network requests and log any that hit analytics domains
await page.route("**/*", (route) => {
const url = route.request().url();
// Match on the URL host, not a substring, so an unrelated host that merely
// contains "posthog"/"sentry" can't false-trigger (CodeQL
// js/incomplete-url-substring-sanitization).
let host = "";
try {
host = new URL(url).hostname.toLowerCase();
} catch {
// non-URL scheme (data:/blob:) -- not an analytics host
}
if (
url.includes("posthog.com") ||
url.includes("posthog") ||
url.includes("sentry.io") ||
url.includes("sentry") ||
url.includes("us.i.posthog.com") ||
url.includes("ingest.sentry.io")
host === "posthog.com" ||
host.endsWith(".posthog.com") ||
host === "sentry.io" ||
host.endsWith(".sentry.io")
) {
analyticsRequests.push(url);
}