179 lines
11 KiB
Markdown
179 lines
11 KiB
Markdown
# 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.
|
|
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`/`Matcher` entry 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 uses `no 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 one `eval(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#sub` block 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's
|
|
`RubyVM.wrap`/`toJS` JavaScript-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 with `Match.Result` at 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:
|
|
|
|
```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 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.
|