84 lines
3.2 KiB
Markdown
84 lines
3.2 KiB
Markdown
# Security policy
|
|
|
|
## Reporting a vulnerability
|
|
|
|
Please **do not** open a public issue for security problems.
|
|
|
|
Instead, report it privately to a maintainer — message an op on `#orbit`
|
|
(Libera.Chat or the Tchatou network). We'll acknowledge it, work on a fix,
|
|
and credit you (if you'd like) once it's resolved.
|
|
|
|
## Supported versions
|
|
|
|
Orbit is a work in progress; only the latest `main` is supported. Fixes land
|
|
there and deploy from it.
|
|
|
|
## Scope & trust model
|
|
|
|
A few things that are intentional, not bugs:
|
|
|
|
- **Plugins run with full page privileges.** Orbit's plugin system is
|
|
**operator-controlled** — a deployment lists plugin scripts in `config.json`,
|
|
so they run with the same trust as the app itself. There is no user-uploaded
|
|
plugin mechanism. Only load plugins you trust; treat them like part of your
|
|
deployment. See the [plugin docs](https://orbit.tchatou.fr/docs/plugins/).
|
|
- **The client is static.** It holds no server-side secrets; account auth is via
|
|
the IRC server (SASL) and the connection is TLS (`wss://`).
|
|
|
|
Things we *do* want to hear about: ways a remote party (a malicious message,
|
|
channel, or server response) could run code, steal a session, or break out of
|
|
the intended sandbox in the client.
|
|
|
|
## Hardening a deployment
|
|
|
|
### Subresource Integrity for plugins
|
|
|
|
Pin any plugin served from a third-party origin with an SRI hash, so a
|
|
compromised host can't silently swap the file. A `plugins` entry may be an
|
|
object instead of a bare URL:
|
|
|
|
```json
|
|
{
|
|
"plugins": [
|
|
"/app/plugins/orbit-clock.js",
|
|
{ "url": "https://cdn.example/orbit-x.js", "integrity": "sha384-…" }
|
|
]
|
|
}
|
|
```
|
|
|
|
Generate the hash with: `openssl dgst -sha384 -binary file.js | openssl base64 -A`
|
|
(prefix the result with `sha384-`). Same-origin plugins don't strictly need it.
|
|
|
|
### Content-Security-Policy
|
|
|
|
Orbit ships no `<meta>` CSP because the right policy depends on your deployment
|
|
(IRC WebSocket host, whether you use the Turnstile challenge, where plugins are
|
|
hosted). Set it as a response header at your edge. A good starting point, with
|
|
the parts you must adjust marked:
|
|
|
|
```nginx
|
|
# Replace wss://YOUR-IRC-HOST with config.json → server.url's origin.
|
|
# Add any off-origin plugin hosts to script-src. Drop the challenges.cloudflare.com
|
|
# lines if you don't use the Turnstile challenge. frame-src 'self' is required for
|
|
# sandboxed plugins; youtube-nocookie serves the inline YouTube link cards.
|
|
add_header Content-Security-Policy "
|
|
default-src 'self';
|
|
script-src 'self' https://challenges.cloudflare.com;
|
|
style-src 'self' 'unsafe-inline' https://fonts.googleapis.com;
|
|
font-src 'self' https://fonts.gstatic.com;
|
|
img-src 'self' data: blob: https:;
|
|
connect-src 'self' wss://YOUR-IRC-HOST;
|
|
frame-src 'self' https://challenges.cloudflare.com https://www.youtube-nocookie.com;
|
|
worker-src 'self';
|
|
manifest-src 'self';
|
|
base-uri 'self';
|
|
form-action 'self';
|
|
object-src 'none';
|
|
frame-ancestors 'self';
|
|
" always;
|
|
```
|
|
|
|
Roll it out with `Content-Security-Policy-Report-Only` first and watch the
|
|
browser console / `report-to` for violations, then switch to the enforcing
|
|
header once it's clean. Self-hosting the Google Fonts CSS/woff2 files lets you
|
|
drop the `fonts.googleapis.com` / `fonts.gstatic.com` origins entirely.
|