mirror of
https://github.com/whit3rabbit/anyllm-proxy.git
synced 2026-09-22 16:00:51 +00:00
Rust workspace with two crates: - translator: pure, IO-free mapping between Anthropic Messages API and OpenAI Chat Completions - proxy: axum HTTP server with auth, streaming SSE, retry/backoff, concurrency limits 169 tests passing (unit, golden fixture, integration). Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
79 lines
2.9 KiB
Markdown
79 lines
2.9 KiB
Markdown
# Security Audit
|
|
|
|
## Authentication
|
|
|
|
### Proxy Auth Boundary
|
|
- The proxy validates that requests carry either `x-api-key` or `Authorization: Bearer` header
|
|
- The proxy does NOT verify the key value itself; it prevents accidental open proxying
|
|
- Client credentials are never forwarded to OpenAI
|
|
- OpenAI is always called with the server-configured `OPENAI_API_KEY`
|
|
|
|
### Recommendation
|
|
- For production, add key verification against an allowlist
|
|
- Consider mTLS or OAuth/JWT at the proxy edge for multi-tenant deployments
|
|
|
|
## Request Size Limits
|
|
|
|
- 32 MB body limit enforced via axum's `DefaultBodyLimit`
|
|
- Matches Anthropic's documented Messages endpoint limit
|
|
- Prevents memory exhaustion from oversized payloads
|
|
|
|
## SSRF Prevention
|
|
|
|
- The proxy only makes outbound HTTP requests to `OPENAI_BASE_URL` (configured via env)
|
|
- No user-controlled URLs are fetched:
|
|
- Image URLs in content are passed as text, not fetched
|
|
- Document blocks are converted to text notes, not processed
|
|
- No file download/upload proxying
|
|
|
|
### Recommendation
|
|
- Validate `OPENAI_BASE_URL` is not a private/internal address at startup
|
|
- Consider URL allowlist for production
|
|
|
|
## Secret Handling
|
|
|
|
- `OPENAI_API_KEY` is read from environment (never hardcoded)
|
|
- `redact_secret()` utility available for logging (shows first/last 4 chars only)
|
|
- Authorization headers are not logged
|
|
- Request bodies may contain sensitive content; `RUST_LOG` should be `info` in production
|
|
|
|
## Concurrency Protection
|
|
|
|
- Tower `ConcurrencyLimitLayer` caps at 100 concurrent requests
|
|
- Prevents self-DOS during upstream 429 incidents
|
|
- Retry logic has exponential backoff to avoid hammering upstream
|
|
|
|
## Header Filtering
|
|
|
|
- Inbound `x-api-key` and `Authorization` headers are consumed, not forwarded
|
|
- `anthropic-version` header is accepted but not forwarded
|
|
- Outbound requests only include `Authorization: Bearer` with server key
|
|
- `x-request-id` is generated/echoed for correlation
|
|
|
|
## Streaming Security
|
|
|
|
- Bounded channel (capacity 32) prevents unbounded memory growth
|
|
- Client disconnect detected and upstream connection dropped
|
|
- No content buffering; deltas translated directly
|
|
|
|
## Dependencies
|
|
|
|
- All dependencies are from crates.io (auditable)
|
|
- No `unsafe` code in project source
|
|
- `cargo audit` should be run in CI
|
|
|
|
## OWASP Top 10 Relevance
|
|
|
|
| Risk | Status | Notes |
|
|
|---|---|---|
|
|
| Injection | Mitigated | No SQL/shell; JSON parsed via serde |
|
|
| Broken Auth | Partial | Auth presence checked; value not verified |
|
|
| Sensitive Data Exposure | Mitigated | Secrets redacted in logs; env-based config |
|
|
| XML External Entities | N/A | JSON only |
|
|
| Broken Access Control | N/A | Single-purpose proxy |
|
|
| Security Misconfiguration | Mitigated | Sensible defaults; env-based config |
|
|
| XSS | N/A | API-only, no HTML |
|
|
| Insecure Deserialization | Mitigated | Typed serde with strict schemas |
|
|
| Using Components with Known Vulns | Recommendation | Run cargo audit in CI |
|
|
| Insufficient Logging | Mitigated | Structured tracing with request IDs |
|