From bdb918462271524cefc72c44153aee0a83672e0b Mon Sep 17 00:00:00 2001 From: Marcel Ebert Date: Mon, 12 Oct 2026 15:05:24 +0200 Subject: [PATCH 1/2] docs(repo): add a security policy --- SECURITY.md | 10 ++++++++++ 1 file changed, 10 insertions(+) create mode 100644 SECURITY.md diff --git a/SECURITY.md b/SECURITY.md new file mode 100644 index 000000000..d4894b748 --- /dev/null +++ b/SECURITY.md @@ -0,0 +1,10 @@ +# Security policy + +Please report suspected vulnerabilities privately by email to marcel@satoshipay.io. Do not +open a public issue or pull request for them. + +Include the affected component, the steps to reproduce, and the impact you expect. We will +acknowledge your report and keep you informed while we investigate and fix it. + +The security expectations for each part of Vortex are documented in +[`docs/security-spec/`](docs/security-spec/README.md). From 69f5856476eab9e4db223c7025d86c6d75f5459e Mon Sep 17 00:00:00 2001 From: Marcel Ebert Date: Mon, 12 Oct 2026 15:05:24 +0200 Subject: [PATCH 2/2] chore(repo): add the oss-scanner build image and threat model Anthropic's OSS Scanner builds this image online and audits the repository offline; the threat model points it at the security spec and states how we rate severity. --- .oss-scanner/Dockerfile | 29 +++++++++++ .oss-scanner/threat_model.md | 96 ++++++++++++++++++++++++++++++++++++ MAP.md | 1 + 3 files changed, 126 insertions(+) create mode 100644 .oss-scanner/Dockerfile create mode 100644 .oss-scanner/threat_model.md diff --git a/.oss-scanner/Dockerfile b/.oss-scanner/Dockerfile new file mode 100644 index 000000000..e39b9a1d0 --- /dev/null +++ b/.oss-scanner/Dockerfile @@ -0,0 +1,29 @@ +# Build image for Anthropic's OSS Scanner (github.com/anthropics/oss-scanner). +# It is built with network access; the audit afterwards runs offline, so everything the +# tests need is fetched here. .oss-scanner/threat_model.md says how to run them. +FROM node:22-bookworm + +COPY --from=oven/bun:1.3.1 /usr/local/bin/bun /usr/local/bin/bun +RUN ln -s bun /usr/local/bin/bunx + +# PostgreSQL for the API suite, with the credentials apps/api/src/test-utils/preload.ts expects. +RUN apt-get update && apt-get install -y --no-install-recommends postgresql \ + && rm -rf /var/lib/apt/lists/* +RUN service postgresql start \ + && su postgres -c "psql -c \"ALTER USER postgres PASSWORD 'postgres'\" -c 'CREATE DATABASE vortex_test'" \ + && service postgresql stop +ENV TEST_DB_PORT=5432 + +# Foundry for contracts/monerium-forwarder. +RUN curl -fsSL https://foundry.paradigm.xyz | bash && /root/.foundry/bin/foundryup +ENV PATH="/root/.foundry/bin:${PATH}" + +# Scanner contract: the checkout lives inside the image, at /src. +COPY . /src +WORKDIR /src + +RUN git submodule update --init --depth 1 contracts/monerium-forwarder/lib/forge-std +RUN bun install --frozen-lockfile && bun run build:shared && bun run build:sdk +# Compiling caches the solc releases both contract projects pin. +RUN cd contracts/monerium-forwarder && forge build \ + && cd ../relayer && bunx hardhat compile diff --git a/.oss-scanner/threat_model.md b/.oss-scanner/threat_model.md new file mode 100644 index 000000000..7e1a19eae --- /dev/null +++ b/.oss-scanner/threat_model.md @@ -0,0 +1,96 @@ +# Vortex threat model + +Read by Anthropic's OSS Scanner before it audits this repository. The normative security +expectations live in [`docs/security-spec/`](../docs/security-spec/README.md): start with +`00-system-overview/architecture.md`, then the module spec for the code you are reading. Code +that contradicts an invariant there is a finding. + +## What Vortex does and where untrusted input enters + +Vortex is a cross-border payment gateway. It converts fiat (BRL, EUR, USD, MXN, COP, ARS) to +stablecoins and back across EVM chains, Pendulum and AssetHub. The API orchestrates each +conversion ("ramp") as a sequence of phases. It holds operator keys that fund per-ramp +ephemeral accounts, subsidize shortfalls and distribute fees. Users and partners sign their own +transactions client-side and submit them presigned. + +Treat all of the following as attacker-controlled: + +- Every HTTP request to `apps/api`: anonymous quote requests, user sessions (Supabase email + OTP), partner API keys (`pk_`/`sk_`), and the admin token's routes. +- Presigned transactions and addresses submitted during ramp registration and update. +- Inbound webhooks from fiat payment providers. +- Responses from fiat providers, price feeds and chain RPCs. The code must validate them + rather than trust them. +- URL and embed parameters of the widget (`apps/frontend`), which partners embed in their + own sites. + +## Components that matter most / least + +Most important, roughly in order: + +1. The ramp engine in `apps/api/src/api/services/phases/` and the quote, fee, discount and + subsidy logic in `apps/api/src/api/services/quote/` and `ramp/`: anything that moves funds + or decides how much moves. +2. Authentication and authorization: `apps/api/src/api/middlewares/`, API credentials, + admin routes and admin impersonation. Ownership must be checked after a resource is loaded. +3. Validation of presigned transactions and of inbound webhooks. +4. `contracts/monerium-forwarder` (forwarder clones, factory and subsidy vault) and + `contracts/relayer` (TokenRelayer). +5. `apps/rebalancer`, which moves platform liquidity between chains. +6. `packages/shared` (signing helpers, wire types) and `packages/sdk` (ephemeral key handling + for partner integrations). + +Lower priority but in scope: `apps/frontend`, `apps/dashboard` and `apps/gold` (client-side +key storage and XSS matter there). Out of scope: `apps/demo` (an example), `scripts/`, +`supabase/` snippets, `docs/`, and test helpers. + +## How to exercise it + +The image has every dependency installed, `@vortexfi/shared` and `@vortexfi/sdk` built, and +both contract projects compiled. Start PostgreSQL before the API suite: + +```bash +service postgresql start +cd /src/apps/api && bun test --timeout 15000 +cd /src/packages/shared && bun test +cd /src/packages/sdk && bun test +cd /src/packages/kyc && bun test +cd /src/apps/rebalancer && bun test +cd /src/contracts/monerium-forwarder && forge test +cd /src/contracts/relayer && bunx hardhat test +``` + +Suites that call real providers or chains skip themselves offline (they need +`RUN_LIVE_TESTS` or `ETH_RPC_URL`). `apps/api/src/test-utils/` holds the factories and fakes +for API tests; write a proof of concept as a `*.test.ts` next to the affected code (or a +Foundry test for the forwarder contracts) so it runs offline in this image. + +## How we rate severity + +- **Critical**: an unauthenticated or ordinary user/partner can steal, redirect or + permanently lock user or platform funds; get a payout without paying; obtain server-held + keys or secrets; bypass admin authentication; or execute code on the API server. +- **High**: an attacker reads or acts on another account's quotes, ramps, recipients, + webhooks, API keys or KYC data; moves quote, fee or subsidy amounts outside the bounds the + spec allows; forges a provider webhook; or strands funds so that an operator must recover + them by hand. +- **Medium**: one client can stall ramp processing or the API; errors leak internal state + without secrets or personal data; or the issue needs the admin token or a partner's secret + key and grants nothing beyond that role. +- **Low**: hardening gaps without a demonstrated exploit path, and client-side issues that + need unusual user interaction. + +## Reports and patches + +- One report per root cause, listing every affected route or caller. +- Name the attacker's starting privileges, what they gain, and the spec invariant violated. +- Propose a minimal patch in the surrounding code's style, with a regression test. + +## Anything to leave alone + +- Exceptions accepted or deferred in `docs/security-spec/RISK-REGISTER.md`, unless you show + the accepted bound does not hold. +- Moonbeam and other dormant XCM paths: report them only if a live route reaches them. +- Vulnerabilities in dependencies, chains or providers without a reachable path in Vortex code. +- Placeholder values in `.env.example` files and credentials in old migrations, which are + revoked. Report one only if you can show it is still valid. diff --git a/MAP.md b/MAP.md index 762b98f78..3597cfe95 100644 --- a/MAP.md +++ b/MAP.md @@ -48,3 +48,4 @@ The full placement and lifecycle policy is in [`docs/README.md`](docs/README.md) | `.agents/skills` | Purpose-built, repository-specific agent workflows (review, shipping, PR feedback, cleanup rules, Vortex integration, and Sentry guidance). | | `.claude` | Shared Claude Code settings and worktree configuration. | | `.clinerules` | Pointer from Cline to the canonical `CLAUDE.md` and documentation policy. | +| `.oss-scanner` | Build image and threat model for Anthropic's OSS Scanner, which audits this repository offline. |