# Security and privacy ## Trust boundary Imported media, filenames, container metadata, tags, chapters, subtitles, attachments, codec logs, project JSON and Toolbox context are untrusted. FFmpeg parsing occurs in a worker but still consumes memory/CPU in the user's browser. The application cannot make a malicious codec safe; it limits exposure and keeps cleanup/recovery explicit. The supported product has: - no upload or processing backend; - no telemetry, analytics or third-party runtime script; - no remote-media protocol or automatic external fetch; - no shell/native command execution; - no arbitrary FFmpeg command input; - no persistent source copy in the current application. Browser extension, operating-system, reverse-proxy and compromised dependency behavior remain outside this boundary. ## Paths and command construction User names are display labels only. Virtual filesystem paths are generated from job ids plus sanitized, length-bounded basenames; separators, traversal, control characters, reserved names and collisions are handled centrally. Outputs must be declared in a typed plan before execution. Commands are argument arrays assembled from discriminated operation/preset types. No shell parses them. Filters, metadata, concat lists, chapter metadata and subtitle values use syntax-specific escaping; one escaping routine is not reused across incompatible grammars. Capability requirements are checked before execution. Logs are line/byte bounded and local path-like names are redacted in exportable diagnostics. ## Rendering and external URLs Metadata is rendered as React text, never `dangerouslySetInnerHTML`. Subtitle text is parsed and rendered or sent through a generated local file; it is not inserted as HTML. Extracted URLs are inert text. The application does not open links from media automatically. No remote font is fetched. Contact-sheet labels mount only the same-origin, size/SHA-256-verified DejaVu asset and fail closed without `drawtext`; ASS burn-in uses packaged/system-generic font configuration only when the required local capability is available. Preview/download URLs are `blob:` values made from local bytes. They are revoked on replacement, project close and unmount. Browser support for a container is not evidence that its contents are safe or fully decodable. ## Resource controls The administrator-adjustable defaults are product limits, not FFmpeg limits: | Limit | Default | | --------------------------------- | ----------------: | | soft input warning | 256 MiB | | hard single input | 1536 MiB | | hard total input | 1536 MiB | | soft output estimate | 512 MiB | | hard output estimate | 1024 MiB | | concurrent FFmpeg jobs | 1 | | scheduled operations in memory | 8 | | timeline clips | 100 | | streams per input | 64 | | image dimension / pixel count | 8192 / 67,108,864 | | generated outputs / contact cells | 255 / 500 | | job runtime | 4 hours | Before allocation, the application estimates source/output bytes, duration, resolution, frame/pixel work, passes, retained results and output count. Estimates are labelled as estimates. Hard-limit violations do not start; soft violations explain resolution/duration/output-count alternatives. The 255-output limit keeps a whole managed batch saveable in the 256-entry ZIP limit with an optional report. Memory-constrained devices receive stronger warnings only when a reliable browser signal exists. WASM itself has a documented 2 GiB input ceiling; practical browser limits can be much lower, especially when input and output coexist. Archive/export and cache operations also enforce entry, total-size and path limits. ## Workers and cancellation Wrapper, core, pthread and waveform workers are same-origin or short-lived `blob:` workers created by trusted application code. OPFS caching currently uses asynchronous browser file handles rather than a separate storage worker. MT requires shared memory and cross-origin isolation. ST loads when isolation is absent. Cancelling FFmpeg terminates and discards its worker/core; a new instance is required before another job. Stale generations cannot commit a result. ## Content Security Policy Tested deployment policy target: ```text default-src 'self'; script-src 'self' 'wasm-unsafe-eval'; worker-src 'self' blob:; connect-src 'self'; img-src 'self' blob: data:; media-src 'self' blob:; font-src 'self'; style-src 'self'; object-src 'none'; frame-src 'none'; frame-ancestors 'none'; base-uri 'none'; form-action 'none'; ``` Do not replace `'wasm-unsafe-eval'` with broad `'unsafe-eval'`. All core assets are same-origin. `media-src blob:` is required for result/source previews. The exact headers, immutable Portal reference gap and active-worktree local patch are in `docs/PORTAL_REQUIREMENTS.md`. ## Cross-origin isolation MT needs secure HTTPS (localhost during development is acceptable), `SharedArrayBuffer`, and: ```text Cross-Origin-Opener-Policy: same-origin Cross-Origin-Embedder-Policy: require-corp Cross-Origin-Resource-Policy: same-origin ``` Isolation affects every resource in the page. Do not weaken COEP or allow cross-origin dependencies merely to make MT load. The safe fallback is ST. ## Dependency and vulnerability response Exact runtime versions and core digests are recorded in `SOURCE.md`. Run `npm audit`, tests, real core smoke tests and legal review before an update. Core upgrades change a large native attack surface and require new capability snapshots, fixtures, checksums and browser tests. Report vulnerabilities privately to the repository owner through the Gitea instance when possible. Do not attach sensitive media, credentials or an unredacted diagnostic log to a public issue. Include the app/core version, browser, mode, smallest synthetic reproduction, impact and cleanup result.