Skip to content

Security: magne/vitni

SECURITY.md

Security policy

Vitni loads WebAssembly components, grants them capabilities, and makes network requests on their behalf. That is a real attack surface, so vulnerability reports are welcome and will be handled seriously.

Reporting a vulnerability

Do not open a public issue.

GitHub's private vulnerability reporting (SecurityReport a vulnerability) is the preferred route. Email magne.rasmussen@gmail.com with [vitni security] in the subject also works.

Useful in a report:

  • what an attacker can do, and what they need to start (a crafted import file, a plugin bundle, a workspace on disk, a config.toml value, network position);
  • the commit or release you tested, the platform, and whether it was a debug or release build;
  • reproduction steps, ideally a minimal input file or bundle. Redact real genealogy data — synthetic names are fine and preferred.

Expectations. This is a single-maintainer project, not a company with a rota: acknowledgement within 7 days, an assessment within 14, and a fix schedule agreed with you from there. Fixes land in public commits with the vulnerability described in the commit message and release notes, and you get credit under whatever name you prefer, or none. There is no bug bounty. Please give the fix a chance to ship before disclosing publicly; if a report goes unanswered for 30 days, disclose.

What is in scope

Anything that breaks a boundary the program claims to hold:

  • Plugin sandbox escape — a wasm32-wasip2 component reaching the filesystem, the network, the process, or host memory other than through the vitni:host-api WIT world (ADR 0007, ADR 0011).
  • Capability bypass — an operation performed without the grant it is gated on. The grants are query, commands, log, progress, import-source, export-sink, net, media-store, ai and present; deny-by-default is the invariant, and a plugin doing something its plugin.toml never declared is a bug in this class. So is escaping the net policy's host allow-list, or writing outside the media library through media-store.
  • Signature and trust-tier bypass — making a bundle classify as Sanctioned or UserTrusted without a signature from a trusted key, or getting a tier to widen what a plugin may do (ADR 0014 §3: a tier says who signed a bundle, never what it may do).
  • Resource exhaustion that the fuel and memory limits are supposed to bound — a plugin that hangs or OOMs the host despite them.
  • Malicious input — path traversal, decompression bombs, XXE-style entity abuse or memory exhaustion reachable from a GEDCOM, Gramps XML or Digitalarkivet file, or from a media file.
  • Integrity of the record — anything that silently drops, rewrites or reorders committed events, or that lets an operator's identity in an EventContext be forged. The event log is the evidence layer; a correction must be a new assertion, never an edit.
  • Credential leaks — an API key from config.toml or the environment ([map], [ai]) reaching a plugin, a log line, a crash report, or an export.

What is not in scope

  • signing::DEV_PUBLIC_KEY / dev_signing_key() being in the source tree (crates/vitni-plugin-host/src/trust.rs). It is deliberate: debug and CI builds trust it so the first-party plugin fleet classifies as sanctioned during testing, and a release build never trusts it — the release key arrives at packaging time through the compile-time VITNI_PROJECT_PUBLIC_KEY. A release binary that does trust the dev key would be a genuine finding.
  • A plugin abusing a capability you granted it. The grant is the security boundary; the program will not second-guess it.
  • Anything requiring an attacker to already have write access to your workspace directory, your ~/.config/vitni/, or the binary itself.
  • Missing hardening with no demonstrated impact (a compiler flag, a header, a dependency version) — useful, but file it as a normal issue.
  • Reports generated by a scanner with no analysis attached.

Supported versions

Pre-1.0 and pre-release: there is no supported-version matrix yet. Fixes go to main, and to the newest release once releases exist. Older commits and tags get nothing backported.

Cryptography and trust, in one paragraph

Plugin bundles are authenticated with detached ed25519 signatures over a canonical digest of plugin.toml + plugin.wasm. Verification fails closed: a bundle carrying a signature that matches no trusted key is a hard error, while an unsigned bundle stays loadable but untrusted and is never auto-granted anything. Trust roots are the sanctioned key compiled into the binary plus publisher keys the user pinned. ADR 0014 is the full account.

There aren't any published security advisories