# Security and privacy

Patterns, flags, replacement templates, subjects and imported projects are
untrusted.

- Syntax and execution use separate workers.
- Regex execution never occurs on the UI thread.
- Timeout, crash and cancellation terminate the worker.
- A terminated worker is never reused.
- Pattern, subject, replacement/list template, match, capture and output sizes
  are bounded.
- PCRE2 adds native match-step, depth and heap caps. Its C ABI returns only
  caller-owned bounded records and releases code, match data and match context
  on every exit path.
- PCRE2 automatic-callout tracing is a separate ABI and worker path. It copies
  only complete fixed records and bounded mark text, stops matching at the
  50,000-event or 10 MiB serialized trace cap, and is killed on timeout,
  cancellation or crash.
- PCRE2 runs only in mandatory UTF/UCP mode. Exact UTF-8 byte boundaries are
  normalized to editor UTF-16 offsets; lone surrogates are rejected instead of
  being silently replaced by browser encoding.
- Python starts only after the worker verifies Pyodide, CPython and `re`
  identities. Pattern, subject, flags and replacement remain string data passed
  to the fixed application bridge; no user-supplied Python source is evaluated.
  Native code-point boundaries are checked before conversion to editor UTF-16.
- Java starts only after the worker verifies the TeaVM bridge ABI, engine
  identity and compiled self-test. It exposes only fixed bounded
  `Pattern`/`Matcher` entry points; no JVM, Java class loading or user-supplied
  JavaScript is available. The runtime is TeaVM, not OpenJDK.
- Every bundled engine directory has a closed file set, deterministic
  metadata/checksums and licence material verified both before installation and
  in `dist/`. Runtime imports and fetches resolve only against the same-origin
  release path.
- Replacement templates are strings; executable callbacks are unavailable.
- Replacement/list token decorations, rows, chips, diagnostics and list
  evaluation work have separate presentation/operation caps; incomplete list
  results cannot be exported.
- Per-match replacement mapping executes no user code and is capped at 200
  matches, 2,000 materialized contributions and 50,000 token evaluations.
- Imported JSON is aggregate-size-bounded before parsing, then field-validated
  and never evaluated or auto-run. Export and IndexedDB save apply the same
  32 MiB aggregate UTF-8 document bound before materialization or persistence.
- Project and standalone-suite test subjects are not exported by default, and
  unit-test subjects require a separate opt-in before IndexedDB persistence.
- Unit-test add, update, clone and append-import paths share 1,000-case and
  32 MiB aggregate state caps.
- Unit-test assertion diagnostics never stringify complete large values and
  retain at most 2 KiB UTF-8 per case. Oversized replacement output is not
  copied into an exact current-result draft.
- Corpus file input is local, recognized UTF-8 text only. It is bounded before
  decoding, rejects NUL/binary input, runs sequentially in a dedicated
  cancellable supervisor and never modifies source files.
- Corpus JSON/CSV/NDJSON summaries never contain source, capture-sample or
  applied-output content. CSV cells are formula-injection protected. Local UI
  previews are explicitly bounded. Applied files require an explicit
  content-export acknowledgement; names are normalized against archive path
  traversal and collisions. Incomplete applied output cannot be downloaded or
  included in the all-document ZIP, whose bytes have a separate hard limit.
- Corpus documents, contents, outputs and results are never placed in project
  JSON or IndexedDB. Schema-v1 imports that attempt to inject corpus payloads
  are rejected.
- ECMAScript growth analysis preflights repetition count and UTF-8 bytes before
  allocating a generated string. The analysis worker is terminated on timeout,
  cancellation or crash; replacement timing also preflights its output bound.
  Analysis settings and results are neither persisted nor uploaded.
- Generated-case synthesis runs in its own killable worker under AST, depth,
  attempt, repetition and UTF-8 byte caps. Labels come only from bounded
  requests to the selected real engine worker; a wrong outcome, timeout, crash,
  cancellation or compile rejection is never converted into a verified case.
  Settings, discarded candidates and unselected results are ephemeral and
  never uploaded.
- Imported generated-test provenance is accepted only for the exact supported
  generator id/version and must agree with the test assertion outcome. Unknown
  provenance fields and future versions are rejected; editing a case removes
  its provenance.
- ECMAScript formatting consumes only exact accepted regexpp literal-token
  ranges and never treats PCRE2 as ECMAScript. Source/candidate syntax and
  execution use separate killable workers; compile failure, truncation,
  timeout asymmetry, engine-identity drift, incomplete tests or any semantic
  difference fails closed. Preview/results are ephemeral and apply requires an
  explicit confirmation.
- Subject minimization rejects lone surrogates, caps input at 1 MiB UTF-8,
  evaluation count at 2,000 and aggregate wall time at 60 seconds. Each
  predicate check runs in the selected killable engine worker. Timeout, crash,
  cancellation and worker error remain distinct; truncated results fail
  closed. Subjects and results are ephemeral and never uploaded or persisted.
- No backend, uploads, telemetry, remote corpus URL, CDN, remote font,
  arbitrary command line or unsafe HTML rendering exists. Application code
  does not evaluate user-controlled JavaScript or Python. The generic generated
  Pyodide loader contains an Emscripten dynamic-link branch that uses
  JavaScript `eval`, but the shipped self-contained CPython/stdlib path does not
  exercise it and loads under the CSP below without broad `'unsafe-eval'`.

The engine-enabled build needs a CSP equivalent to:

```text
default-src 'self';
script-src 'self' 'wasm-unsafe-eval';
worker-src 'self' blob:;
connect-src 'self';
img-src 'self' data:;
font-src 'self';
style-src 'self' 'unsafe-inline';
object-src 'none';
frame-src 'none';
base-uri 'none';
form-action 'none';
```

The Toolbox shell currently requires inline styles. Corpus ZIP generation,
PCRE2, Pyodide and TeaVM use self-hosted modules/assets and local workers under
the existing `worker-src` and `connect-src` policy. Broad `'unsafe-eval'` is not
required; `'wasm-unsafe-eval'` remains required for the two WebAssembly
runtimes.

Report vulnerabilities privately to the repository owner before public issue
details are posted.
