Add clippy with -D warnings (deny all warnings) to both Rust CI
pipelines. The production-deploy workflow already had clippy.
This ensures lint issues are caught before merge for both services.
Addresses audit finding D15: clippy not present in all Rust workflows.
Co-authored-by: Cursor <cursoragent@cursor.com>
Delete service files and their tests for features with no backend:
- educationService.ts + test (Education feature)
- gamificationService.ts + test (Gamification/XP feature)
- gearService.ts + test (Gear/Equipment feature)
The routes for these features are now gated behind ComingSoon
placeholders (C8), so these service modules are unreachable dead code.
Note: The corresponding UI components (gamification/, inventory/,
education-view/) still exist but are orphaned. They can be removed
in a separate cleanup pass.
Addresses audit finding D14: ghost frontend services.
Co-authored-by: Cursor <cursoragent@cursor.com>
Delete 3 test files that import from service modules that no longer exist
after the previous service consolidation:
- services/__tests__/trackService.test.ts (imports ../trackService)
- services/__tests__/playlistService.test.ts (imports ../playlistService)
- services/playlistService.test.ts (imports ./playlistService)
These caused import resolution failures in test runs.
Addresses audit finding D6: orphaned test files.
Co-authored-by: Cursor <cursoragent@cursor.com>
- Create ComingSoon.tsx: simple placeholder showing feature name and
"under development" message with proper Tailwind styling
- Replace LazyGear, LazyLive, LazyEducation, LazyQueue, LazyDeveloper
route elements with <ComingSoon feature="..." />
- Remove unused lazy imports for ghost components
Users navigating to /gear, /live, /education, /queue, /developer will
now see a clear "Coming Soon" message instead of broken components
that depend on non-existent backend APIs.
Addresses audit finding D5: 5 ghost routes without backend.
Co-authored-by: Cursor <cursoragent@cursor.com>
Replace the stub filter_content() that always returned true with a real
implementation using compiled regex patterns:
- XSS vectors: <script>, javascript:, onXxx=, <iframe>, <object>, <embed>
- SQL injection: UNION SELECT, DROP TABLE, OR 1=1, ' OR '
- Command injection: eval(), exec()
Patterns compiled once at startup via once_cell::sync::Lazy with safe
.ok() filter (no .unwrap()). Returns false (reject) on pattern match.
Also enhances validate_content() to check dangerous patterns and return
a proper error.
Addresses audit findings D4, A04: ContentFilter stub always returned true.
Co-authored-by: Cursor <cursoragent@cursor.com>
- request_id.rs:36: UUID.to_string().parse().unwrap() replaced with
unwrap_or_else fallback to HeaderValue::from_static("unknown")
- prometheus_metrics.rs:357: Response::builder().body().unwrap() replaced
with unwrap_or_else fallback to a plain error response
Both were panic-capable paths in request handling middleware.
Addresses audit finding D9: .unwrap() in production code.
Co-authored-by: Cursor <cursoragent@cursor.com>
Replace the stub check_rate_limit() that always returned true with a
real implementation backed by the governor crate (already in Cargo.toml).
- Per-IP keyed rate limiter with DashMap store
- Default quota: 120 requests/minute per IP
- Sliding window enforcement via governor's GCRA algorithm
- Returns 429 Too Many Requests when limit exceeded
Addresses audit findings D7, A04: missing rate limiting on stream-server.
Co-authored-by: Cursor <cursoragent@cursor.com>
- Add allowedTestTables map containing all known database tables
- Add validateTableName() function that panics if table name is not
in the whitelist
- Call validateTableName() before all fmt.Sprintf("DELETE FROM %s")
and fmt.Sprintf("TRUNCATE TABLE %s CASCADE") statements
- Prevents potential SQL injection via table name interpolation,
even though the risk is low (test-only code, table names come from
hardcoded lists or DB introspection)
Addresses audit finding: A03 (Injection) — minor risk in test utilities.
Co-authored-by: Cursor <cursoragent@cursor.com>
Mark /gear, /live, /education, /queue, /developer routes as PLANNED
with a comment indicating no backend exists yet. These routes render
frontend components but have no corresponding API endpoints.
No routes removed — this is documentation only, zero regression risk.
Addresses audit finding: section 2 (product/code inconsistencies).
Co-authored-by: Cursor <cursoragent@cursor.com>
Enable the TypeScript strict flag `noUncheckedIndexedAccess` which ensures
that indexed access (arrays, Record types) includes `| undefined` in the
result type, catching potential runtime errors at compile time.
Current state:
- 493 pre-existing type errors (before this flag)
- ~234 additional errors introduced by this flag
- Errors should be fixed progressively, prioritizing features/ and services/
- Pattern: use optional chaining (value?.) or null checks (if (value != null))
This flag was previously commented out with a TODO. Enabling it now ensures
new code is written safely, even as existing errors are addressed over time.
Addresses audit finding: debt item 10 (TypeScript strictness).
Co-authored-by: Cursor <cursoragent@cursor.com>
Add config/docker/README.md with:
- Table of all remaining docker-compose files and their purposes
- Usage commands for each environment
- List of deleted deprecated files (from C9)
- Required environment variables for production deployment
Addresses audit finding: debt item 8 (12 docker-compose files confusion).
Co-authored-by: Cursor <cursoragent@cursor.com>
- Convert all sqlx::query!() and sqlx::query_scalar!() compile-time
macros to runtime sqlx::query() and sqlx::query_scalar() with .bind()
- Affected files: segment_tracker.rs, processor.rs, callbacks.rs
- This removes the dependency on .sqlx/ directory for offline mode
- Update Dockerfile to remove SQLX_OFFLINE=true and .sqlx COPY
- Stream server can now compile without a live database connection
The compile-time macros required either a DATABASE_URL at build time or
a .sqlx directory with cached query metadata (neither was available).
Runtime queries trade compile-time SQL validation for buildability.
Addresses audit finding: debt item 1 (stream server compilation).
Co-authored-by: Cursor <cursoragent@cursor.com>
- Upgrade jsonwebtoken from 9.2 to 10.x with aws_lc_rs crypto backend
- The API (encode, decode, Validation, EncodingKey, DecodingKey, Header,
Algorithm) remains compatible -- the main v10 change is the crypto
backend selection via features
- Aligns with veza-stream-server which already uses jsonwebtoken 10
Addresses audit finding: A06 (Vulnerable & Outdated Components).
Co-authored-by: Cursor <cursoragent@cursor.com>
Production (docker-compose.prod.yml):
- Change sslmode=disable to sslmode=require on all 3 DATABASE_URLs
- Replace JWT_SECRET fallback defaults with :? syntax (fails if unset)
- Replace DB_PASS default 'password' with :? syntax (fails if unset)
- Separate RABBITMQ_PASS from DB_PASS, require explicit setting
Staging (docker-compose.staging.yml):
- Add sslmode=require to DATABASE_URL
- Replace all default passwords with :? syntax (fails if unset)
docker-compose up with these files will now FAIL if required secrets
are not explicitly provided via environment variables.
Addresses audit findings: A02 (Cryptographic Failures), section 7 (Infra).
Co-authored-by: Cursor <cursoragent@cursor.com>
- Change default ALLOWED_ORIGINS from wildcard (*) to localhost:5173
in veza-stream-server/docker-compose.yml
- Also fixed local .env (untracked) to use specific dev domains
Previously, the stream-server docker-compose defaulted to ALLOWED_ORIGINS=*
which would allow any origin to access the streaming API.
Addresses audit finding: A05 (Security Misconfiguration) — HIGH.
Co-authored-by: Cursor <cursoragent@cursor.com>
- Validate JWT token via AuthManager before accepting WebSocket connections
- Extract user_id from validated token claims instead of trusting query params
- Reject unauthenticated connections with 401 Unauthorized
- Add `authenticated` field to WebSocketConnection struct
- Update websocket_handler_wrapper to handle auth error responses
Previously, the WebSocket handler accepted all connections without
validating the token (comment: "pour l'instant, on accepte la connexion").
Now requires a valid JWT token via ?token= query param or Authorization header.
Addresses audit finding: A01 (Broken Access Control) — CRITICAL.
Co-authored-by: Cursor <cursoragent@cursor.com>
- Add govulncheck to backend-go job
- Add npm audit --audit-level=high to frontend job
- Both use || true to avoid blocking CI on existing vulns
Co-authored-by: Cursor <cursoragent@cursor.com>
- Parse VITE_FEATURE_* from env with fallback to current defaults
- Add all flags to .env.example and ENV_CONFIG.md
- parseFeatureEnv accepts true/1/yes for enabled
Co-authored-by: Cursor <cursoragent@cursor.com>
- Add SessionRevocationStore trait with InMemoryRevocationStore and RedisRevocationStore
- Wire Redis store when REDIS_URL in config.cache, fallback in-memory
- Session revocation by session_id persists across restarts when using Redis
Co-authored-by: Cursor <cursoragent@cursor.com>
- Add JwtRevocationStore trait with InMemoryRevocationStore and RedisRevocationStore
- Wire Redis store when REDIS_URL is set (fallback in-memory if Redis unavailable)
- JWT blacklist persists across restarts when using Redis
Co-authored-by: Cursor <cursoragent@cursor.com>