ODS Platform · Security Audit
8 of 8 SEC-FIX-2 findings resolved. Status improved from CRITICAL to CONCERNS. 5 low-severity items remain for future sprints.
OWASP Top 10 — Category Review
set_config('app.tenant_id', $1, true)Command::new() calls with user-controlled input found in codebaseJWT_ALLOW_HS256=true opt-inexp, iss, aud claims validated; HS256 secret minimum 32 chars enforced at startupextractors.rs:221 — format!("Invalid token: {e}") passes jsonwebtoken error text verbatim into 401 response. Leaks rejection reason (e.g., "InvalidSignature", "InvalidIssuer") to callers.error.rs:34 — FK violations create PdfError::Validation(format!("Foreign key violation: {db_err}")). The Validation variant is returned verbatim to clients, exposing internal table/column relationship names. Missed by SEC-01 sanitization..gitignore covers .env, *.pem, *.keylopdf does not involve XML entity parsing.tenant_id = current_setting('app.tenant_id')::UUID at DB layer — migration 006 (SEC-02)find_by_iddocs/adr/001-defer-rbac-enforcement.md, deferred post-P1 pending OID role definitions.CORS_ALLOWED_ORIGINS unset; Docker runs as non-root app:app; no debug mode in production code pathsproducer.rs — RedpandaProducer has no TLS/SASL; Kafka traffic unencrypted and unauthenticated. Required before production deployment.main.rs — Custom migration runner silently ignores SQL errors on startup; schema inconsistency risk on failed migration mid-run..unwrap() on serde ops are inside #[cfg(test)] onlytemplates.rs — serde_json::Value for schema and input_data fields has no recursion depth limit; deeply nested JSON could exhaust stack before size checks fire.cargo-audit not installed — automated CVE scan not performed. Run: cargo install cargo-audit && cargo auditrdkafka 0.36 wraps librdkafka C library (native code surface); lopdf 0.34 parses untrusted PDF bytes with reduced maintenance activity. No known CVEs at review date.reqwest and validator removed (SEC-05)X-Correlation-Id header propagated from incoming requests to CloudEvents correlationid field (SEC-07)Remaining Findings — All Low Severity
| ID | Category | Severity | Location | Description |
|---|---|---|---|---|
R-01 |
A02 | LOW | extractors.rs:221 |
JWT error text from jsonwebtoken crate passed verbatim in Unauthorized response. Reveals token rejection reason to callers (e.g., "InvalidSignature", "ExpiredSignature"). Fix: return generic "Token validation failed"; log detail at WARN server-side. |
R-02 |
A03 | LOW | error.rs:34 |
FK violations create PdfError::Validation(format!("Foreign key violation: {db_err}")). Validation variant returned verbatim — exposes constraint names and table relationships.
Fix: sanitize FK path in error_response() like Conflict/Internal variants.
|
R-03 |
A05 | LOW · ACCEPTED | ADR-001 |
No RBAC enforcement — any authenticated tenant user has full read/write access regardless of role. Risk accepted. Cross-tenant isolation enforced by RLS. RBAC deferred post-P1 per docs/adr/001-defer-rbac-enforcement.md. |
R-04 |
A06 | LOW | producer.rs |
RedpandaProducer connects without TLS or SASL authentication. Kafka traffic unencrypted. Must configure TLS and SASL before production deployment. |
R-05 |
A06 | LOW | main.rs |
Custom migration runner silently ignores SQL errors; re-applies all migrations on every startup. Failed migration mid-run leaves schema in inconsistent state. Consider sqlx migrate or tracking applied migrations in a dedicated table. |
R-06 |
A08 | LOW | templates.rs |
serde_json::Value accepts arbitrarily nested JSON with no recursion depth limit.
1MB body limit provides partial protection; consider explicit depth validation in domain layer.
|
R-07 |
A09 | WARN | Cargo.toml |
cargo-audit not installed — automated CVE scanning not possible.
Action: cargo install cargo-audit and add cargo audit as a required CI step.
|
| 7 findings total | 5 LOW · 1 ACCEPTED · 1 WARN (tooling gap) | |||
Dependency Review
cargo install cargo-audit && cargo audit. Add to CI/CD pipeline as a required gate before merge.
| Crate | Version | Risk | Notes |
|---|---|---|---|
rdkafka |
0.36 | LOW | Wraps librdkafka C library — introduces native code surface outside Rust ownership model. No known CVEs.
Monitor for updates. C library memory bugs can bypass Rust safety guarantees.
|
lopdf |
0.34 | LOW | Parses untrusted PDF bytes in split, merge, and rotate operations. Reduced maintenance activity. No known CVEs at time of review. Monitor for updates or consider actively-maintained alternatives. |
reqwest |
0.12 | RESOLVED | Removed as dead dependency (SEC-05). Was unused — removal reduces attack surface. |
validator |
0.19 | RESOLVED | Removed as dead dependency (SEC-05). Was unused — removal reduces attack surface. |
Secrets Scan & Unsafe Code
.gitignore covers .env, .env.*, *.pem, *.key — test fixtures explicitly allowedtests/fixtures/test_rsa_private.pem is tracked — test-only, never used in production.env.example:5 contains dev credential ods-dev-2026 — intentional documentation, must not be used in staging/prodTEST_SECRET constant in extractors.rs inside #[cfg(test)] — never compiled into production binaryunsafe blocks found in production source code.unwrap() calls on serde operations are inside #[cfg(test)] blocks onlystd::mem::transmute or raw pointer dereferences outside of tests.env, .env.*, *.log, *.pem, *.key. Correct exceptions for !.env.example and !tests/fixtures/*.pem. No production credentials at risk of accidental commit.