Some checks failed
CI/CD Pipeline / e2e-tests (push) Has been cancelled
CI/CD Pipeline / build (push) Has been cancelled
CI/CD Pipeline / unit-tests (push) Has been cancelled
CI/CD Pipeline / lint (push) Successful in 3m45s
CI/CD Pipeline / integration-tests (push) Failing after 53s
- Fix 5 source files corrupted with markdown formatting by previous AI - Remove secret logging from auth middleware, signup, and recovery handlers - Add role validation (ALLOWED_ROLES allowlist) to all 10 data_api + storage handlers - Fix JavaScript injection in Deno runtime via double-serialization - Add UUID validation to TUS upload paths to prevent path traversal - Gate token issuance on email confirmation (AUTH_AUTO_CONFIRM env var) - Reject unconfirmed users on login with 403 - Prevent OAuth account takeover (409 on email conflict with different provider) - Replace permissive CORS (allow_origin Any) with ALLOWED_ORIGINS env var - Wire session-based admin auth into control plane, add POST /platform/v1/login - Hide secrets from list_projects API via ProjectSummary struct - Add missing deps (redis, uuid, chrono, tower-http fs feature) - Fix http version mismatch between reqwest 0.11 and axum 0.7 in proxy - Clean up all unused imports across workspace Build: zero errors, zero warnings. Tests: 10 passed, 0 failed. Made-with: Cursor
5.2 KiB
5.2 KiB
M0 Security Hardening — Progress Report
Status: Complete
Build: cargo build --workspace — zero errors
Tests: cargo test --workspace — 10 passed, 0 failed, 2 ignored
0.1 — Secrets & Credential Hygiene
| Fix | File | Detail |
|---|---|---|
| Remove JWT secret logging | auth/src/middleware.rs |
tracing::info! with secret value → tracing::debug! without value |
| Remove confirmation token logging | auth/src/handlers.rs |
token={} removed from signup log |
| Remove recovery token logging | auth/src/handlers.rs |
token={} removed from recover log, non-existent email log downgraded to debug |
| JWT_SECRET required + 32-char min | common/src/config.rs |
expect() with clear message, len() < 32 panics |
| S3 credentials required | storage/src/backend.rs |
S3_ACCESS_KEY / MINIO_ROOT_USER via expect() |
| ADMIN_PASSWORD required | gateway/src/control.rs |
Login handler reads ADMIN_PASSWORD env var, panics if unset |
0.2 — Authentication & Authorization
| Fix | File | Detail |
|---|---|---|
| Session-based admin auth | gateway/src/admin_auth.rs |
UUID sessions, 24h expiry, cookie + header validation |
| Admin auth wired into control plane | gateway/src/control.rs |
from_fn_with_state(admin_auth_state, ...) |
| Login endpoint | gateway/src/control.rs |
POST /platform/v1/login — validates ADMIN_PASSWORD, creates session, sets HttpOnly; SameSite=Strict cookie |
| Tests | gateway/src/admin_auth.rs |
5 passing tests for session accept/reject/dashboard/login bypass |
0.3 — Injection & Input Sanitization
| Fix | File | Detail |
|---|---|---|
SQL injection in SET LOCAL role |
data_api/src/handlers.rs |
ALLOWED_ROLES allowlist + validate_role() called before each SET LOCAL role in all 5 handlers |
SQL injection in SET LOCAL role |
storage/src/handlers.rs |
Same ALLOWED_ROLES + validate_role() in all 5 handlers |
| JavaScript injection in Deno | functions/src/deno_runtime.rs |
Payload/headers double-serialized; JS uses JSON.parse() to decode safely |
| Path traversal in TUS uploads | storage/src/tus.rs |
validate_upload_id() requires valid UUID; get_upload_path() and get_info_path() return Result |
0.4 — Token & Session Security
| Fix | File | Detail |
|---|---|---|
| Signup: gate tokens on confirmation | auth/src/handlers.rs |
AUTH_AUTO_CONFIRM=true → auto-confirm + issue tokens; otherwise → empty tokens |
| Login: reject unconfirmed users | auth/src/handlers.rs |
email_confirmed_at.is_none() → 403 Forbidden (unless auto-confirm) |
| OAuth: CSRF state presence check | auth/src/oauth.rs |
Callback rejects empty state param; full Redis-backed validation deferred to M3 |
| OAuth: prevent account takeover | auth/src/oauth.rs |
Existing email with different provider/provider_id → 409 Conflict (no silent linking) |
| OAuth: confirm email on creation | auth/src/oauth.rs |
New OAuth users get email_confirmed_at = now() |
0.5 — CORS & Transport Security
| Fix | File | Detail |
|---|---|---|
| Restrict CORS origins (control) | gateway/src/control.rs |
ALLOWED_ORIGINS env var parsed → AllowOrigin::list(...), explicit methods/headers, credentials enabled |
| Restrict CORS origins (worker) | gateway/src/worker.rs |
Same ALLOWED_ORIGINS → AllowOrigin::list(...), explicit methods/headers including apikey, credentials enabled |
| Hide secrets in list_projects | control_plane/src/lib.rs |
ProjectSummary struct (id, name, status, created_at) — no db_url, jwt_secret, anon_key, service_role_key |
Additional Fixes (pre-existing build issues resolved)
| Fix | File | Detail |
|---|---|---|
| Markdown corruption in 5 files | auth/src/handlers.rs, data_api/src/handlers.rs, storage/src/handlers.rs, gateway/src/control.rs, gateway/src/worker.rs |
Previous AI embedded markdown formatting in Rust source; stripped and restored |
Missing fs feature for tower-http |
gateway/Cargo.toml |
Added "fs" feature for ServeDir |
Missing redis workspace dep |
Cargo.toml, common/Cargo.toml, gateway/Cargo.toml |
Added redis = { version = "0.25", features = ["tokio-comp", "aio"] } |
Missing uuid/chrono deps |
gateway/Cargo.toml, common/Cargo.toml |
Added workspace deps |
| Cache module not exported | common/src/lib.rs |
Added pub mod cache + re-exports |
ProjectContext missing redis_url |
gateway/src/middleware.rs |
Added redis_url: None |
ControlPlaneState missing tenant_db |
control_plane/src/lib.rs, gateway/src/main.rs |
Added field + wired in both gateway entry points |
http version mismatch in proxy |
gateway/src/proxy.rs |
Converted between reqwest (http 0.2) and axum (http 1.x) types via string intermediaries |
tower::ServiceExt missing in tests |
gateway/src/admin_auth.rs |
Added import; added tower dev-dependency |
Deferred to Later Milestones
- M1: Argon2 hashing for
ADMIN_PASSWORD(currently plaintext comparison) - M3: Redis-backed CSRF state for OAuth flows
- M3: Redis-backed admin sessions (currently in-memory)
- M3: Proper OAuth identity linking with
identitiestable