# 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.
