# Security policy

Plotvera by Anaphase. Made by Anaphase Ltd (company number 17537023), 71-75 Shelton Street, London WC2H 9JQ.
Policy version 0.3, effective 23 Oct 2026.

Plotvera handles research data locally and treats imported project files and
share links as untrusted input.

## Reporting a vulnerability

Email contact@anaphasescience.com with:

- what you found and where (URL, version, file or endpoint)
- steps to reproduce, and what an attacker could do with it
- whether you have shared it with anyone else

Please do not include real research data, patient information or other
people's personal information. A synthetic example is enough. Do not publish
exploit details in a public issue.

We will acknowledge your report within 5 working days, keep you updated, and
tell you when it is fixed. We will credit you in the release notes if you want
to be credited.

Machine-readable contact: https://plotvera.app/.well-known/security.txt

### Scope

In scope: the web app at plotvera.app, its API routes (`/api/ai`,
`/api/share`, `/api/share-status`) and the MCP connector relay at
mcp.plotvera.app.

Out of scope: denial-of-service and volume testing, social engineering,
physical attacks, findings in third-party services that we do not control
(report those to the provider), and reports from automated scanners without a
demonstrated impact.

### Safe harbour

If you act in good faith, stay within this scope, do not access or change
other users' data beyond what is needed to show the problem, and give us
reasonable time to fix it before disclosure, we will not pursue or support
legal action against you for your research.

This is a research beta run by a small team. We do not claim any
certification.

## Supported versions

Only the latest deployed research-beta build receives security fixes. A formal
support window will be published with the first archived release.

## Security boundaries

- Project JSON and share payloads must pass the central schema validator.
- SVG values are allowlisted/sanitised before `dangerouslySetInnerHTML`.
- Online AI and cloud sharing require explicit user action and disclosure.
- Hosted AI rule, dated 22 Oct 2026: the AI edge proxy relays a user's own
  key by default and spends no deployment-owned key. A hosted path exists in
  code but is off unless both `ANAPHASE_AI_HOSTED=1` and a server key
  variable are set; with either missing, hosted requests return 503
  `hosted-unfunded` and the user's own key or the offline guidance is used.
  Production keeps those variables unset until durable metering and billing
  exist (see `docs/LAUNCH_GATES.md`). Anything shaped like a provider key is
  scrubbed from upstream error text.
  User keys are page-session scoped in `sessionStorage`; current builds purge
  the persistent `localStorage` key used by earlier releases.
- Cloud-share creation fails closed unless Vercel supplies its trusted
  `x-vercel-forwarded-for` value, the edge function can HMAC it with a
  server-only secret, and the service-role-only Supabase RPC atomically grants
  the durable quota. The database stores no raw client IP.
- Cloud shares expire after 30 days and an hourly database job deletes expired
  live rows. Reads remain possession-of-link access and do not require the
  creation quota.
- Core analysis is client-side; local storage is not encrypted and does not
  protect data from another user or malicious extension on the same browser profile.
- Self-contained share URLs contain the project in their fragment. Possession of
  the URL grants access to its data.

## Dependency audit exceptions

CI runs a production dependency audit through `test/security_audit.mjs`. Two
exact `pptxgenjs>image-size@2.0.2` advisory paths are allowlisted there because
`pptxgenjs` has not yet published a dependency range that accepts a fixed
upstream release. The workspace-level pnpm ignore list is deliberately empty,
so the wrapper can verify the advisory identifier, dependency path, installed
version, locked patch, and executable regression before permitting either
finding. It also rejects a high/critical metadata count that is not represented
in the advisory payload, preventing a package-manager-version mismatch from
silently suppressing findings.

Plotvera pins `image-size` 2.0.2 and applies a locked patch that rejects
zero-length ICNS, HEIF, and JXL boxes; the malicious samples from both
advisories are executed as regression tests in
`src/lib/dependencySecurity.test.ts`. The browser bundle does not expose these
parsers and Plotvera supplies only its own generated SVG/PNG content to PPTX
export. Remove the exceptions as soon as a patched upstream dependency is
available.

- `GHSA-w3rx-r6r6-pgpr`
- `GHSA-5p2g-fcmc-qvqq`

GitHub Actions used by the release workflow are pinned to immutable 40-character
commits. Version comments remain beside those pins so upgrades can be reviewed
deliberately without executing a newly moved major-version tag first.
