feat: introduce local-first SVG workbench
This commit is contained in:
@@ -0,0 +1,79 @@
|
||||
# Security model
|
||||
|
||||
SVG is active content. Threats include script/event execution, navigation,
|
||||
external fetches and tracking, CSS resource resolution, `foreignObject`, native
|
||||
animation triggers, namespace confusion, entity/DOCTYPE processing, parser
|
||||
differentials, oversized compressed/data content and algorithmic complexity.
|
||||
|
||||
## Source and projection
|
||||
|
||||
Canonical source is inert text and remains available even when unsafe. A
|
||||
separate projection is built from a cloned semantic document. DOMPurify's SVG
|
||||
profiles are followed by an application policy that:
|
||||
|
||||
- removes `script`, `foreignObject`, `animate`, `animateMotion`,
|
||||
`animateTransform`, `set`, foreign namespaces and all `on*` attributes;
|
||||
- disables links/navigation and executable or external URL attributes;
|
||||
- allows only unique local fragment references and bounded base64 raster image
|
||||
data (`png`, `jpeg`, `gif`, `webp`, `avif`);
|
||||
- parses style declarations/stylesheets with css-tree and rejects `@import`,
|
||||
unsafe legacy properties, parse failures, non-local URLs, `expression()` and
|
||||
resource-producing functions such as `image-set()` and `paint()`;
|
||||
- reports findings with source ranges wherever a semantic node/range exists.
|
||||
|
||||
DOCTYPE is excluded before DOM parsing; entity declarations invalidate the
|
||||
semantic projection. Sanitized export removes temporary mapping attributes.
|
||||
Sanitize is never applied to canonical source without preview and acceptance.
|
||||
|
||||
## Isolated preview
|
||||
|
||||
The projection is an iframe `srcdoc` with `sandbox="allow-scripts"` and no
|
||||
`allow-same-origin`, giving it an opaque origin. Its child CSP is:
|
||||
|
||||
```text
|
||||
default-src 'none'; connect-src 'none'; object-src 'none'; frame-src 'none';
|
||||
base-uri 'none'; form-action 'none'; img-src data: blob:;
|
||||
style-src 'unsafe-inline'; script-src <controller-origin>
|
||||
```
|
||||
|
||||
The sole script is the packaged `canvas-frame-controller.js`. It is loaded with
|
||||
anonymous CORS, accepts messages only from `parent` with a random channel,
|
||||
validates message fields and removes its script element. The parent validates
|
||||
both `event.source` and channel.
|
||||
|
||||
Because a sandbox without same-origin treats the script request as cross-origin,
|
||||
serve this one app-relative asset with:
|
||||
|
||||
```text
|
||||
Access-Control-Allow-Origin: *
|
||||
Cross-Origin-Resource-Policy: cross-origin
|
||||
```
|
||||
|
||||
Keep `Cross-Origin-Resource-Policy: same-origin` for other files. Use an Nginx
|
||||
`map`/header value, not a nested `location` that would drop inherited security
|
||||
headers. Toolbox Portal 0.10.0 implements and tests this exception.
|
||||
|
||||
## Limits and tests
|
||||
|
||||
Hard limits are defined in `src/app/limits.ts`. SVGZ uses streaming
|
||||
decompression and stops once output crosses the source limit. Workers are
|
||||
revisioned, timed out and cancelable. Tree/diagnostic rendering is capped.
|
||||
|
||||
Project-authored tests cover script, handlers, links, external image/use/filter,
|
||||
CSS imports/URLs/parser failures/resource functions, native animation,
|
||||
namespaces, duplicate IDs/cycles, malformed entities/XML, depth/attribute/path
|
||||
and data limits. Browser tests assert no script callback and no request to the
|
||||
hostile domain.
|
||||
|
||||
## Limitations and reporting
|
||||
|
||||
This policy protects this application's projections and exports; it is not a
|
||||
general guarantee for arbitrary downstream embedding. Browser SVG/CSS parsers
|
||||
and DOMPurify remain dependency/security boundaries. Bounded data URLs may still
|
||||
decode expensive images within the raster-pixel limits. CSP header regressions
|
||||
can break the fixed controller even when the content remains inert.
|
||||
|
||||
Report vulnerabilities privately through the repository owner/contact before
|
||||
opening a public issue when disclosure could expose users. Include the SVG,
|
||||
browser, deployment headers and observed network/execution behavior without
|
||||
sensitive user files.
|
||||
Reference in New Issue
Block a user