Ctrl K
ComunidadGuías…

SECURITY.md

Rama main · réplica de lectura del repositorio original.

Archivo original ↗Preguntar a KiBot
# Security Policy

## Reporting a Vulnerability

If you discover a security vulnerability in LobbyForge, **please do not
open a public GitHub issue**. Instead, report it privately:

1. **GitHub Security Advisories** (preferred): go to the
   [Security tab](../../security/advisories/new) of this repo and create
   a private security advisory. This is monitored and reaches the
   maintainers directly.
2. **GitHub Confidential Vulnerability Reporting**: use the
   "Report a vulnerability" button on the Security tab.

Please include:
- A description of the vulnerability and its impact.
- Steps to reproduce (proof of concept).
- The affected version/commit.

We acknowledge reports within **48 hours** and aim to publish a fix or
mitigation within **30 days** for critical issues.

## Supported Versions

There are no tagged releases yet (experimental alpha); report vulnerabilities against the current `main` branch. Self-hosted instances
should keep up to date via the built-in update system (Admin → Updates).

## Security Measures

LobbyForge implements defense-in-depth:

- **Session cookies**: HMAC-SHA256 signed, HttpOnly, SameSite=Lax, Secure in production.
  web and the realtime gateway refuse to start with a missing, short or
  published placeholder session secret (only the local dev stack, with
  `LOBBYFORGE_DEV_STACK=true`, may use one); the dev stack binds `127.0.0.1` only.
- **Image uploads**: content-sniffed format and dimensions (every GIF frame
  inside the image), a per-user stored quota, a per-address daily byte
  budget, and no uploads for guest accounts.
- **Desktop app**: the window only shows the connected instance and its own
  connect screen; other pages open in the system browser.
- **Guest IDs**: 128-bit cryptographic randomness (`crypto.randomBytes`).
- **Admin auth**: owner session or constant-time emergency token (≥32 chars).
- **CSRF protection**: Fetch Metadata headers + Origin validation on all mutations.
- **CSP**: strict `script-src 'self'`, `object-src 'none'`, `frame-ancestors 'none'`.
  `frame-src` allows exactly one origin, `https://www.youtube-nocookie.com` —
  the Watch Party player (framing only, sandboxed; see `docs/WATCH_PARTY.md`).
- **Request body limits**: per-route caps (default 1 MiB).
- **Rate limiting**: Redis-backed (fails closed), per-route limits.
- **Update signatures**: Ed25519-signed manifests required for self-host updates.
- **Input validation**: Zod schemas on every API route.
- **TURN relay credentials**: per-user, time-limited (coturn REST auth) —
  no permanent shared TURN secret exists.

## Marketplace plugins — trust model (ADR-001)

**Current model: reviewed-only.** The marketplace accepts curated,
admin-reviewed plugins. The plugin-worker executes them in a hardened
container (child process, strict IPC, process-group kill, memory/pids
caps, read-only fs, internal network). This is adequate for
**reviewed code where the review process is the trust gate**.

**NOT a hostile-code sandbox.** Arbitrary untrusted JavaScript
requires per-plugin containers with separate UIDs, PID namespaces and
cgroups. Until that exists, the marketplace requires human review
before approval, and dynamic execution stays opt-in.

See docs/ARCHITECTURE_DECISIONS.md for the full ADR.

## Marketplace plugins — isolated worker runtime (LF-SEC-010)

Third-party plugin bundles execute in the **plugin-worker container**,
never in the web process:

- separate container, internal-only network (not behind nginx at all);
- NO host secrets in its environment — only the RPC token and the
  storage-capability token;
- read-only root filesystem + read-only plugin bundle mount;
- `mem_limit 256m`, `pids_limit 64`, `cap_drop ALL`,
  `no-new-privileges`, healthcheck-gated restarts;
- the web app calls it with hard timeouts — a hung plugin can at worst
  restart the worker, never the web app;
- `ctx.storage.*` executes back on the host via the internal
  `/api/internal/plugin-storage` endpoint (404 at the public edge),
  hard-scoped to the plugin's own (serverId, pluginId) keyspace.

Dynamic execution STILL defaults to off. Enabling requires BOTH
`LOBBYFORGE_DYNAMIC_PLUGINS_ENABLED=true` AND
`LOBBYFORGE_PLUGIN_WORKER_URL` — the flag alone loads nothing (fail
closed), and the old in-process import path for third-party code has
been removed from the loader entirely. Workspace plugins (hushle,
quiz, …) remain trusted first-party code in the static registry.

## Marketplace plugins — dynamic execution is DISABLED (SEC-008)

Third-party plugin bundles (`plugins/installed/…`) are NOT executed by
default. Two independent gates must BOTH agree before any dynamic
plugin code runs, and both default to closed:

1. `POST /api/marketplace/install` refuses (503) unless
   `LOBBYFORGE_DYNAMIC_PLUGINS_ENABLED=true`.
2. The boot-time loader (`lib/plugin-loader.ts`) skips the
   `plugins/installed/` walk entirely without the same flag.

(Historical note — superseded by the isolated worker runtime above.)
The original in-process execution model is gone; the remaining gate
below is defense-in-depth while the feature stays opt-in:

## Self-Host Security Checklist

When deploying LobbyForge on your own server:

- [ ] Set `LOBBYFORGE_SESSION_SECRET` to ≥32 chars of random hex.
- [ ] Set `LOBBYFORGE_ADMIN_TOKEN` to ≥32 chars (different from session secret).
- [ ] Set `LOBBYFORGE_SETUP_TOKEN` and remove it after first-run setup.
- [ ] Use HTTPS (certbot/Let's Encrypt) — WebRTC requires it.
- [ ] Keep `registrationMode` on `invite_only` (the default).
- [ ] Run the Doctor health check after deployment.
- [ ] Configure backups: `node scripts/lfctl.mjs backup create --out backups --database-url ...` (see docs/BACKUP_DRILL.md — includes a destructive restore drill you should rehearse before trusting backups).