11 KiB
11 KiB
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
reidentities. 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. Bounded replacement bytes use canonical base64 rather than content-dependent JSON escaping; declared length, request cap and canonical UTF-8 are validated before the output is exposed. - Java starts only after the worker verifies the TeaVM bridge ABI, engine
identity and compiled self-test. It exposes only fixed bounded
Pattern/Matcherentry points; no JVM, Java class loading or user-supplied JavaScript is available. The runtime is TeaVM, not OpenJDK. - PHP starts only after the worker verifies PHP 8.5.8, PCRE2 10.44 and the
fixed bridge ABI. User values are JSON data written to a private virtual
filesystem; the bridge chooses a safe delimiter, allowlists modifiers and
invokes only fixed
preg_*operations. - The legacy Perl worker writes exact-key, byte-bounded JSON to a fixed MEMFS
request path and invokes only the constant
RegexTools::run_request()entry point. The bridge usesno re 'eval', rejects(?{...})and(??{...})outside escaped literals/classes, pre-tokenizes its non-executable replacement grammar and streams bounded UTF-8 chunks from native match spans. Successful output uses a separate fixed binary file, never the JSON response; the worker verifies exact bytes, limit and canonical UTF-8 before decoding it. Replacement stops at the match/capture-row bound and leaves the unprocessed subject tail unchanged. WebPerl 0.09-beta's unmodified generated loader contains oneeval(code)site in an optional JavaScript-interoperability import; the fixed bridge never loads or calls it. The checksummed asset passes the browser CSP smoke without broad'unsafe-eval'. - Ruby evaluates only the fixed application bridge during worker startup.
Pattern, subject, flags, limits and replacement travel in one bounded,
length-prefixed file in the worker-local WASI memory filesystem. Native
String#gsub/String#subblock iteration cannot materialize expanded user output: a fixed CRuby 4.0.0-compatible expander stops at the byte limit and writes that prefix to a separate bounded file. The inert JSON response contains metadata only and is assembled without per-code-point arrays. No user value is interpolated into source or sent through ruby.wasm'sRubyVM.wrap/toJSJavaScript-eval conversion path. Unpaired browser surrogates are rejected before UTF-8 encoding. Ruby cold startup passes the strict-CSP release browser gate. - C++ accepts only structured fixed Embind calls and is compiled with
Emscripten
-sDYNAMIC_EXECUTION=0; the generated module is checked for dynamic-source construction. Its libc++-compatible replacement expander appends directly through the UTF-8 byte cap and is parity-checked at load. Embind returns a structured JavaScript string rather than JSON, so there is no escape amplification; the 64 MiB native buffer and its bounded UTF-16 conversion are the explicit peak-output copies. - Go and Rust accept JSON data only through fixed exports. Each validates its request and enforces pattern, subject, result and output caps inside WebAssembly. Adapters measure exact encoded request bytes; the native request cap covers worst-case sixfold JSON escaping for every accepted string field, so control-heavy values retain the documented decoded field limits. Their native-compatible replacement token streams stop at the output cap without allocating a whole capture expansion. Bounded UTF-8 output crosses the JSON bridge as fixed 4:3 base64 rather than content-dependent escaped text. Rust additionally caps compiled-regex and DFA storage.
- .NET accepts bounded JSON only through fixed
[JSExport]methods and never evaluates user input as managed or JavaScript source. A native regex timeout complements the supervisor timeout. Its fixed replacement parser streams native-compatible token fragments through the UTF-8 byte cap, verifies parity withMatch.Resultat load and transports output as canonical base64 to avoid JSON escaping amplification. The shipped .NET host contains runtime debugging machinery, but normal production startup disables debugging and is verified under the documented CSP. - Scala compatibility shares the TeaVM Java bridge. It introduces no Scala runtime, dynamic compiler or additional execution surface.
- 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 source in any language. The generic
generated Pyodide loader contains an Emscripten dynamic-link branch that
uses JavaScript
eval, while the legacy WebPerl loader contains the optional interop site described above. Neither shipped fixed execution path exercises those branches, and both load under the CSP below without broad'unsafe-eval'.
Every enabled profile, including a cold Ruby start, passes the release CSP equivalent to:
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 and
every engine pack use self-hosted modules/assets and local workers under the
existing worker-src and connect-src policy. Broad 'unsafe-eval' must not
be added to accommodate an engine. The release gate blocks any future profile
that fails this strict-CSP browser matrix. 'wasm-unsafe-eval' remains required
for the WebAssembly runtimes.
Report vulnerabilities privately to the repository owner before public issue details are posted.