Skip to content
OpenSmartRoute

External security review pack

Scope, trust boundaries, the evidence the project produces with reproduction commands, the questions for an independent reviewer and the review log.

docs/SECURITY_REVIEW.md

Everything an independent reviewer needs to assess the OpenSmartRoute control plane against the threat model in 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 can be run by a third party without a briefing call.

1. Engagement#

Subjectopensmartroute 1.0.0 (pip install opensmartroute), the osr serve image, the hosted platform at https://api.opensmartroute.ai / https://opensmartroute.ai
TypeWhite-box: full source, tests, CI and deployment code are public in this repository
Rules of engagementTest 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
Contactsecurity@opensmartroute.org or GitHub private vulnerability reporting (../SECURITY.md); acknowledgement within 72 h
PublicationFindings 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" lists T1-T16 with the OWASP Top 10 / OWASP LLM Top 10 mapping and the implementing code; "Threat-model delta: 0.3 -> 0.4" 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#

EvidenceWhereHow to reproduce
Static analysis on every push: ruff (incl. the S bandit rules), bandit, mypy.github/workflows/ci.ymlpython -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.ymlSecurity 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 (49 tests), tests/test_enterprise_ops_stores.pypython -m pytest tests/test_security.py -q
Safety-routing red-team suite (paraphrase, gadget and steering attacks against safety routing) gates CI and every releasesecurity.safety.run_safety_suite, ci.ymlosr -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 (108 tests)PYTHONPATH=src:platform/api python -m pytest platform/api/tests -q
Zero runtime dependencies in the SDK core; optional extras imported lazilypyproject.tomlpip 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.ymlgh attestation verify dist/opensmartroute-*.whl --owner isathish
Container hardening: non-root image, read-only root filesystem, dropped capabilities, NetworkPolicy, PDB in the Helm chartdeploy/Dockerfile, deploy/helm/opensmartroutehelm template deploy/helm/opensmartroute
Content-free logs and traces (ids, digests and lengths only)observability.text_digest, LoggingTelemetrygrep the test suite for text_digest; run osr serve with a FileSink and inspect
Hash-chained audit log with tamper detectionenterprise.FileAuditSink.verifyedit 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#

DateReviewerScopeReportFindings (open / fixed)
-none yet---

A completed review adds a row here, a "Threat-model delta" row in 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.