80 lines
3.6 KiB
Markdown
80 lines
3.6 KiB
Markdown
# 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.
|