54 lines
1.9 KiB
React
54 lines
1.9 KiB
React
// @vitest-environment jsdom
|
|
|
|
import { render, screen } from "@testing-library/react";
|
|
import { describe, expect, it } from "vitest";
|
|
|
|
import { SafeText } from "../../src/presentation/security/safe-text.jsx";
|
|
import { assertSafeConfigNames } from "../../src/contracts/env.js";
|
|
import { defineStorageKey } from "../../src/contracts/storage-keys.js";
|
|
import { projectTelemetryEvent } from "../../src/contracts/telemetry.js";
|
|
|
|
describe("browser security boundary", () => {
|
|
it("renders untrusted text without script or inline handler injection", () => {
|
|
render(
|
|
<SafeText value={'<img src=x onerror="window.compromised=true"><script>x</script>'} />,
|
|
);
|
|
expect(screen.getByText(/<img/)).toBeVisible();
|
|
expect(document.querySelector("script")).toBeNull();
|
|
expect(document.querySelector("[onerror]")).toBeNull();
|
|
});
|
|
|
|
it("rejects secret-like client configuration names", () => {
|
|
expect(() => assertSafeConfigNames({ PRIVATE_KEY: "not-public" })).toThrow();
|
|
});
|
|
|
|
it("rejects browser token storage registration", () => {
|
|
expect(() =>
|
|
defineStorageKey({
|
|
logicalName: "SESSION_TOKEN",
|
|
scope: "auth",
|
|
name: "session-token",
|
|
backend: "sessionStorage",
|
|
classification: "sensitive-forbidden",
|
|
schemaVersion: 1,
|
|
ttl: "session",
|
|
migration: "discard",
|
|
quotaFallback: "feature-disable",
|
|
}),
|
|
).toThrow();
|
|
});
|
|
|
|
it("drops raw URL/query/token telemetry attributes", () => {
|
|
const result = projectTelemetryEvent("api.request.failed", {
|
|
error_kind: "SERVER_FAILURE",
|
|
http_status_group: "5xx",
|
|
attempt_count_bucket: "1",
|
|
route_id: "APP_HOME",
|
|
raw_url: "https://api.test?token=private",
|
|
query_string: "token=private",
|
|
});
|
|
expect(result.success).toBe(true);
|
|
expect(JSON.stringify(result)).not.toMatch(/raw_url|query_string|private/);
|
|
});
|
|
});
|