# RustProxy 0.31.0 - API, panel and HTTP cache-maintenance security review Scope: source snapshot supplied for the v0.31.0 maintenance follow-up, reviewed 2026-10-10. This is a **code/contract review with offline regressions**, not a penetration test or proof of production safety. The supplied archive has no `.git` directory; the review does not establish parity with remote `master`. ## Interfaces and trust boundaries | Surface | Exposure | Authentication and mutation rules | Assessment | |---|---|---|---| | Public administration bootstrap (`/`, login JS/CSS, `/api/auth/status`, setup/login POST) | Login/public administration listener | Host/origin validation; request marker for unsafe bootstrap actions; one-time setup capability and login throttling | Source-reviewed; live browser flow pending | | Private panel HTML, JS, documentation, OpenAPI | Administration listener | Administrator session; `Cache-Control: no-store`; CSP, frame and MIME protections | Source and anonymous-response test reviewed; runtime pending | | `/api/status`, `/api/system-info`, `/api/traffic`, `/api/cache`, logs, config, preferences, audit | Administration listener | Administrator cookie session; sensitive reads not on anonymous allowlist | Source and regression tests added; runtime pending | | `/api/events`, live-log SSE | Administration listener | Session on subscribe and repeated validity checks; bounded viewers; heartbeat/disconnect cleanup | Source-reviewed; reconnect/load pending | | State-changing admin APIs (including cache/config/preferences) | Administration listener | Valid session, Origin/Host, `X-RustProxy-Request: 1`, CSRF token; audit on cache commands | Source and regression tests added; runtime pending | | Certificate API (`/api/certificates/{id}/reload`, deploy) | Administration listener | Narrow-scoped Key ID + Bearer grant; no general read access; HTTPS required remotely; origin validation when supplied | Source-reviewed; token rotation/revocation live tests pending | | Public `BAN`/`PURGE` | Configured public vhost listener | **No admin session**: separate explicit IP/CIDR allowlist (empty by default), matched listener/vhost/ACL and optional rate limit | Hardened below; reverse/HTTP tests pending | | Separate Prometheus metrics exporter | Distinct optional listener | Configured IP CIDRs and bearer-token policy; not the admin session | Unchanged; deployment exposure audit pending | ## Findings addressed in this follow-up 1. **E0716 / compilation defect (high functional impact).** `error()` accepts `&'static str`; a reference to `format!(...)` was a temporary. Invalid selector errors now log the detailed reason server-side and return a fixed, non-sensitive message with status 422. This removes the reported lifetime violation; the Rust compiler must still verify the entire crate. 2. **Cache protocol-scope confusion (medium).** Path-only `BAN`/`PURGE` selectors could affect representations of the same host/path across both HTTP and HTTPS. Targets are now built with the trusted external scheme, validated to `http`/`https`, and the matched Host/vhost. IPv6 literal Hosts are bracketed for the URL parser. Other tenants, schemes and hosts remain outside the selector. 3. **Unbounded blocking maintenance work (medium availability risk).** Cache invalidation may scan hot/cold entries. Both public HTTP and authenticated admin cache purge/selective invalidation share two maintenance slots and execute scans with `spawn_blocking`. Bursts receive 429 instead of piling up unbounded blocking work on Tokio core threads. The permit stays with the worker after a client disconnects. This is a concurrency bound, **not** a per-client rate quota or a hard time limit for disk work. 4. **Ambiguous maintenance input (medium).** Duplicate TTL/confirmation or `Content-Length` headers, transfer-encoded bodies, nonempty bodies and upgrade requests are rejected; `Content-Length: 0` remains compatible with CLI clients. TTL is limited to 1..604800 s. Broad `BAN /` requires the explicit confirmation header; `PURGE` cannot use BAN-only controls. 5. **Audit ordering (low/medium).** Removal of an administrator-created BAN rule now records the audit entry before mutating state. Unavailable audit storage blocks the mutation rather than allowing an unaudited removal. 6. **Operator safety/usability (low).** An EN/PL help popover beside the HTTP allowlist gives exact `curl` syntax, IP-only authentication semantics and reverse-proxy identity requirements. No new global config variable is added. ## Existing protections checked in source - Admin router's exact anonymous method/path allowlist contains only the expected authentication bootstrap APIs; private docs and assets remain session-protected. Request guard rejects duplicate Host/Origin and checks browser `Sec-Fetch-Site` cross-site signals. - Mutation guard uses an Origin/Host match, per-session CSRF comparison and `X-RustProxy-Request` marker; cookie sessions are HttpOnly/SameSite=Strict and use Secure under HTTPS/trusted forwarded protocol. - Secure response policy includes CSP, `X-Frame-Options: DENY`, `X-Content-Type-Options: nosniff`, referrer restrictions and no-store for private material. A static scan checked 27 first-party browser JS files for direct unsafe HTML injection and dynamic `eval`/`Function` sinks; this scan **cannot establish that the entire DOM is XSS-free**. - IP trust comes from the transport peer or a validated forwarding chain only when the peer is configured trusted. An untrusted client-supplied XFF does not independently grant BAN/PURGE permission. The cache allowlist rejects `/0`, invalid CIDRs, duplicates and more than 32 entries. Requests are never forwarded upstream. - Certificate API tokens have explicit operation/ID grants and cannot use the general admin read API; Bearer grant operations are intentionally separate from cookie + CSRF authentication. ## HTTP maintenance CLI (allowlisted client only) ```sh # One URL, including the query string: 200 with JSON result. curl -i -X PURGE 'https://site.example/assets/app.js?v=3' # Lazily ban previously cached objects under this path: 202 with JSON result. curl -i -X BAN 'https://site.example/assets/' # Entire host: explicit opt-in confirmation header. curl -i -X BAN 'https://site.example/' -H 'X-RustProxy-Confirm-Wide: 1' # Optional BAN expiration (30 seconds). curl -i -X BAN 'https://site.example/assets/' -H 'X-RustProxy-Ban-Ttl: 30' ``` Use **Settings -> Cache -> HTTP BAN / PURGE IP allowlist** to grant the caller IP/CIDR. This HTTP interface does not accept admin cookies/tokens as an alternative to the allowlist. The domain, Host header, external scheme and listener must select the intended vhost. Behind a reverse proxy, forward the non-standard methods untouched and **replace**, rather than append untrusted, XFF/Forwarded headers at the edge. Never add the entire reverse-proxy subnet as a maintenance client merely because it is trusted for forwarding. ## Release security test plan (requires compiled RustProxy) 1. On the deployment host, run `cargo fmt --all -- --check`, `./dev.sh check`, `./dev.sh test`, and `./dev.sh release`. Address any compiler/regression errors before installation. `cargo audit` or an equivalent advisory scan requires an up-to-date advisory DB and was not run in this offline environment. 2. Start a disposable instance with a populated cache under both HTTP and HTTPS for the same vhost/URL. Confirm `PURGE` returns 200 and drops the exact query variant only; `BAN` returns 202 and invalidates only **older** entries on the URL prefix. Confirm the *other* scheme and vhost stay hot. 3. From a denied IP, expect 403 on `BAN` and `PURGE`. With an empty allowlist, all clients must be denied. Send duplicate TTL/confirmation/Content-Length, TE/body and `BAN /` without confirmation: expect 400. On a trusted-proxy hop with missing real client identity, expect 403. Test both IPv4 and IPv6. 4. From an allowed IP, send 10 concurrent broad maintenance requests; confirm that only two workers run simultaneously and excess return 429. Verify server CPU/IO and audit entries while the cold tier is populated. 5. Send GET requests without admin cookies to `/api/config`, `/api/status`, `/api/system-info`, `/api/events`, `/api/logs`, `/docs/openapi.json` and `/assets/app.js`; expect 401 and protective headers. With valid cookie but missing/bad Origin, marker or CSRF on PUT/POST/DELETE, expect 403. Test logout and password/session invalidation, live SSE reconnect/expiry. 6. Test the Certificate API with no grant, a valid certificate-specific grant, revoked token, HTTP remote peer, and misuse against `/api/config`/logs. Check errors for token disclosure and audit coverage. 7. Test actual browser startup, CSP console, output escaping and form save with malicious HTML strings; inspect reverse-proxy Host/Origin forwarding. Prefer HTTPS administration with a strict `allowed_hosts` list. ## Residual risk / non-claims - **Not executed in this environment:** Cargo format/check/tests/release, actual Rust listener requests, HTTP/2/3, fuzzing, penetration tests, session revocation under load, proxy deployment configuration, dependency CVE scanning and long-running cold-cache resource measurements. - Proxy trust configuration is security-critical. A misconfigured edge that accepts forged forwarded client addresses can let external users impersonate an allowlisted IP. Restrict direct listener reachability and sanitize headers. - A public IP allowlist is not user authentication; anyone using an allowed IP/network can issue destructive maintenance commands. Use narrow CIDRs and optionally enforce additional controls on the edge (mTLS, firewall). - The two-worker concurrency bound mitigates bursts, not all denial-of-service scenarios: configured cache size and storage latency still determine scan cost. Per-IP quotas are not provided here. - Proxy protocol path, certificate verification, arbitrary upstream code, all application content and third-party packages are **not certified safe** by this audit. Production acceptance remains open until the runtime test plan is completed.