<!-- OpenSmartRoute: External review pack. Source https://github.com/isathish/OpenSmartRoute/blob/main/docs/SECURITY_REVIEW.md; HTML https://opensmartroute.ai/docs/SECURITY_REVIEW -->
# External security review pack

Everything an independent reviewer needs to assess the OpenSmartRoute control plane against the threat
model in [SECURITY.md](https://opensmartroute.ai/docs/SECURITY.md): scope, trust boundaries, the evidence the project already produces,
how to reproduce it, the questions we want answered and how a report is published. It exists so that the
review named in the [roadmap](https://opensmartroute.ai/docs/ROADMAP.md#v09-security-hardening-of-the-control-plane-complete) can be run
by a third party without a briefing call.

## 1. Engagement

| | |
|---|---|
| Subject | `opensmartroute` 1.0.0 (`pip install opensmartroute`), the `osr serve` image, the hosted platform at `https://api.opensmartroute.ai` / `https://opensmartroute.ai` |
| Type | White-box: full source, tests, CI and deployment code are public in this repository |
| Rules of engagement | Test against your own `osr serve` / `python -m osr_platform.app` instance or a free-plan workspace you created; no load testing of the hosted platform; no access to other workspaces' data |
| Contact | `security@opensmartroute.org` or GitHub private vulnerability reporting ([../SECURITY.md](https://opensmartroute.ai/docs/security-policy.md)); acknowledgement within 72 h |
| Publication | Findings fixed or accepted first; then the report is linked in section 7 and in the roadmap |

## 2. System under review

```
caller ── HTTPS ──> platform/web (Next.js, proxies /api,/v1,/mcp) ──> platform/api (FastAPI, osr_platform)
                                                                        │  API keys, sessions, plans, metering,
                                                                        │  workspaces, audit, admin
                                                                        └──> opensmartroute (SDK: signals,
                                                                             policy, strategies, learners)
                                                                                └──> targets (Azure OpenAI,
                                                                                     MCP tools, agents, humans)
self-hosted: caller ──> osr serve (src/opensmartroute/server.py) ──> opensmartroute ──> targets
library:     your code ──> Router / RouterBuilder ──> targets
```

Trust boundaries, from the outside in:

1. **Internet -> web / API.** Anonymous: landing, docs, `/api/v1/models`, `/rankings`, `/stats/public`,
   `/status`, `/estimate` (rate limited), `/signup` (5 per IP per day). Everything else needs an API key
   (`osr_live_`, hashed at rest) or a browser session (`osr_sess_`, 30 days, SSO via PKCE with a signed
   state). Admin routes need `X-Admin-Token`. Cross-workspace reads are 404 by construction
   (`db.usage_by_request(account, request_id)` scopes every trace / event lookup).
2. **`osr serve`.** Optional bearer / `X-API-Key` middleware (`serve --token`, `--require-auth`,
   `OSR_SERVER_AUTH_TOKENS`); `OPEN_PATHS` are the probes, `/metrics`, `/whoami` and the OpenAPI UI. The
   Helm chart and the Docker image are documented as "front it with your gateway".
3. **Router -> request text.** `InputGuard` (size, gadget and steering scrub, optional PII redaction) runs
   before signals; `Policy` runs before scoring and cannot be outweighed; the LLM judge sees a fenced,
   sanitised prompt and its output is parsed item by item.
4. **Router -> catalogue.** Imported MCP tool descriptions, A2A cards and `SKILL.md` files pass the
   injection lexicon and the learned gadget score; MCP manifests can be signed (HMAC-SHA256 / Ed25519).
5. **Router -> execution.** `OriginPolicy` (parameter provenance), `ResourceLimiter` (steps, calls, depth,
   tokens, cost, wall-clock), breakers and budgets sit between a decision and a handler call.
6. **Router -> state.** JSON only, atomic writes, hashed keys, versioned schema, quarantine of corrupt
   state, optional AES-256-GCM at rest, hash-chained audit with `verify()`.

## 3. Threat model

[SECURITY.md, "Threat model & controls"](https://opensmartroute.ai/docs/SECURITY.md#threat-model--controls) lists T1-T16 with the OWASP
Top 10 / OWASP LLM Top 10 mapping and the implementing code; ["Threat-model delta: 0.3 -> 0.4"](https://opensmartroute.ai/docs/SECURITY.md#threat-model-delta-03---04)
lists the residual risk we already know about per threat. The reviewer is asked to confirm, refute or
extend that table rather than start from a blank page.

## 4. Evidence the project produces

| Evidence | Where | How to reproduce |
|---|---|---|
| Static analysis on every push: `ruff` (incl. the `S` bandit rules), `bandit`, `mypy` | [.github/workflows/ci.yml](https://github.com/isathish/OpenSmartRoute/blob/main/.github/workflows/ci.yml) | `python -m ruff check src tests scripts; python -m bandit -q -r src -c pyproject.toml; python -m mypy src` |
| CodeQL `security-and-quality` for Python and GitHub Actions, on push and weekly | [.github/workflows/codeql.yml](https://github.com/isathish/OpenSmartRoute/blob/main/.github/workflows/codeql.yml) | Security tab of the repository |
| Unit tests for the controls (guard, gadget detector, sanitiser, provenance, limits, signed manifests, audit chain, encrypted store) | [tests/test_security.py](https://github.com/isathish/OpenSmartRoute/blob/main/tests/test_security.py) (49 tests), [tests/test_enterprise_ops_stores.py](https://github.com/isathish/OpenSmartRoute/blob/main/tests/test_enterprise_ops_stores.py) | `python -m pytest tests/test_security.py -q` |
| Safety-routing red-team suite (paraphrase, gadget and steering attacks against safety routing) gates CI and every release | `security.safety.run_safety_suite`, [ci.yml](https://github.com/isathish/OpenSmartRoute/blob/main/.github/workflows/ci.yml) | `osr -t examples/targets.yaml safety --learned-guard` |
| Platform authorisation tests (API keys, sessions, workspaces, SSO state, device flow, admin token, trace scoping) | [platform/api/tests](https://github.com/isathish/OpenSmartRoute/blob/main/platform/api/tests) (108 tests) | `PYTHONPATH=src:platform/api python -m pytest platform/api/tests -q` |
| Zero runtime dependencies in the SDK core; optional extras imported lazily | [pyproject.toml](https://github.com/isathish/OpenSmartRoute/blob/main/pyproject.toml) | `pip install opensmartroute && pip show opensmartroute` (Requires: none) |
| Signed releases: SLSA build provenance and PEP 740 attestations, trusted publishing (OIDC, no long-lived PyPI token) | [.github/workflows/release.yml](https://github.com/isathish/OpenSmartRoute/blob/main/.github/workflows/release.yml) | `gh attestation verify dist/opensmartroute-*.whl --owner isathish` |
| Container hardening: non-root image, read-only root filesystem, dropped capabilities, NetworkPolicy, PDB in the Helm chart | [deploy/Dockerfile](https://github.com/isathish/OpenSmartRoute/blob/main/deploy/Dockerfile), [deploy/helm/opensmartroute](https://github.com/isathish/OpenSmartRoute/blob/main/deploy/helm/opensmartroute) | `helm template deploy/helm/opensmartroute` |
| Content-free logs and traces (ids, digests and lengths only) | `observability.text_digest`, `LoggingTelemetry` | grep the test suite for `text_digest`; run `osr serve` with a `FileSink` and inspect |
| Hash-chained audit log with tamper detection | `enterprise.FileAuditSink.verify` | edit one line of an audit file and run `verify()` |

## 5. Questions for the reviewer

Ordered by the impact a positive finding would have.

1. **Rerouting (T1, T2).** Can a request author steer the decision to a chosen target through the text
   alone - gadgets the learned detector misses, steering the scrub does not remove, or judge-prompt
   escapes past the sanitiser? Success = a reproducible prompt that moves a target's propensity by more
   than the ensemble's stated confidence margin on `examples/targets.yaml`.
2. **Policy bypass (T3).** Does any strategy, plan slot, fallback, cascade or `exclude`/`candidates`
   path reach a target that `Policy.check` rejected (PII to a `pii_allowed: false` target, region or
   tenant constraint)? The platform's `/api/v1/policy` overlay is in scope.
3. **Cross-workspace access (platform).** Any read or write that crosses `account_id`: traces, events,
   activity, savings, API keys, invites, SSO connections, marketplace listings, admin routes without
   the admin token, IDOR on `request_id`.
4. **Authentication (platform).** API-key hashing and comparison, session fixation / expiry, SSO PKCE
   and state signature, the RFC 8628 device flow (`slow_down`, code entropy, approval binding), signup
   rate limiting, the OpenAI-compatible `/v1` proxy's key handling.
5. **Catalogue poisoning (T13, T14).** An MCP tool description, A2A card or `SKILL.md` that passes the
   import filter and changes routing or smuggles a parameter past `OriginPolicy`.
6. **Learner poisoning (T5).** Feedback (`/feedback`, `Outcome`) sequences that move a target's learned
   quality faster than the documented prior / confidence ramp allows, or state files that load and
   are trusted when they should be quarantined.
7. **Resource amplification (T6, T15).** Inputs or plans that escape `InputGuard` limits, `ResourceLimiter`
   caps or the knapsack meters; unbounded growth in any in-memory map or state store.
8. **Data at rest and in logs (T4, T16).** Any code path that writes request text to logs, traces, cache
   keys, feedback, audit or metrics; weaknesses in `EncryptedStateStore` (nonce reuse, key rotation).
9. **Supply chain (T11).** Release workflow, attestation coverage, Docker base image, the `install.sh`
   / `install.ps1` bootstrap scripts served from the web app.

## 6. Known gaps (do not re-report)

- The SDK does not authenticate callers; `osr serve` is unauthenticated unless `--require-auth` or
  tokens are configured, and the Helm chart says so.
- The gadget detector ships trained on synthetic gadgets; the injection lexicon is English-centric.
- A compromised LLM judge can still bias ranks among legitimate candidate ids.
- A valid-looking poisoned learner snapshot is indistinguishable from real learning; write access to the
  state store is the boundary.
- Audit verification is only meaningful if the log file is write-once at the OS level.
- Encryption at rest is opt-in; key management is the operator's (`load_secret`).

## 7. Review log

| Date | Reviewer | Scope | Report | Findings (open / fixed) |
|---|---|---|---|---|
| - | none yet | - | - | - |

A completed review adds a row here, a "Threat-model delta" row in [SECURITY.md](https://opensmartroute.ai/docs/SECURITY.md) for anything
that changed, and a CHANGELOG entry for every fix. Until a row exists, the roadmap lists the independent
report as an open adoption item under v1.0, not as a shipped control: `python scripts/release.py readiness`
reads this table and reports the row as open while the only entry is the placeholder above.
