# Static Analysis (SAST) **Date**: 2026-03-31 **Scope**: `src/`, `e2e/`, root config files **Method**: Manual code review with pattern matching ## Findings ### F1: JWT Tokens Decoded Without Signature Verification (HIGH) - **Location**: `src/main.py:67-99` (`TokenManager._decode_exp`, `TokenManager.decode_user_id`) - **Description**: JWT payloads are base64-decoded and parsed directly without cryptographic signature verification. Any attacker can forge a JWT with arbitrary claims (user ID, expiration). - **Impact**: An attacker can impersonate any user by crafting a JWT with a target's `sub`/`userId` claim, then use that to read their AI settings or post annotations under their account. - **Remediation**: Verify JWT signatures using the issuer's public key or shared secret before trusting claims. Use a library like `PyJWT` with `algorithms` and `verify=True`. ### F2: No Authentication Required on Any Endpoint (HIGH) - **Location**: `src/main.py:325-627` (all route handlers) - **Description**: All endpoints (`/detect`, `/detect/{media_id}`, `/detect/stream`, `/health`) are publicly accessible. Bearer tokens are optional — requests without tokens proceed normally with reduced functionality. - **Impact**: Any network-reachable client can trigger AI inference (expensive compute), read the SSE event stream (all detection events for all users), and consume server resources. - **Remediation**: Add authentication middleware requiring valid tokens on `/detect`, `/detect/{media_id}`, and `/detect/stream`. Keep `/health` public. ### F3: SSE Event Stream Broadcasts to All Clients (MEDIUM) - **Location**: `src/main.py:608-627` (`detect_stream`) - **Description**: The `/detect/stream` SSE endpoint broadcasts all detection events (including `mediaId` and annotations) to every connected client via a shared `_event_queues` list. There is no per-user filtering or authentication. - **Impact**: Any connected client receives detection results for all users, leaking media IDs, detection coordinates, and processing status of other users' media. - **Remediation**: Associate each SSE queue with an authenticated user and filter events by user ownership. ### F4: No Rate Limiting on Compute-Intensive Endpoints (MEDIUM) - **Location**: `src/main.py:348-469` (`/detect`), `src/main.py:494-605` (`/detect/{media_id}`) - **Description**: No rate limiting or throttling on endpoints that trigger AI inference. The ThreadPoolExecutor has only 2 workers, making it easy to exhaust. - **Impact**: An attacker can repeatedly call `/detect` with valid images to consume all inference capacity, causing denial of service for legitimate users. - **Remediation**: Add rate limiting middleware (e.g., `slowapi`) on inference endpoints. ### F5: Exception Details Leaked in HTTP Responses (LOW) - **Location**: `src/main.py:449-450` - **Description**: `RuntimeError` and `ValueError` messages are passed directly to `HTTPException(detail=str(e))`. Internal error messages could reveal implementation details. - **Impact**: Information disclosure of internal error messages to external clients. - **Remediation**: Return generic error messages to clients; log detailed errors server-side only. ### F6: Silent Exception Swallowing (LOW) - **Location**: `src/main.py:63-64` (`_refresh`), `src/main.py:490-491` (`_post_annotation_to_service`) - **Description**: Multiple `except Exception: pass` blocks silently ignore errors, including potential security-relevant failures like token refresh failures or annotation posting failures. - **Impact**: Security-relevant events (failed auth refresh, failed API calls) go undetected, making incident investigation difficult. - **Remediation**: Log exceptions at WARNING level instead of silently swallowing. ### F7: No Request Body Size Limit (LOW) - **Location**: `src/main.py:348-469` (`/detect` upload endpoint) - **Description**: No explicit limit on uploaded file size. Large uploads could exhaust memory (entire file is read into memory with `await file.read()`). - **Impact**: Memory exhaustion DoS via oversized uploads. - **Remediation**: Configure uvicorn/nginx `--limit-request-body` or check `Content-Length` before reading. ## Summary | Severity | Count | |----------|-------| | Critical | 0 | | High | 2 | | Medium | 2 | | Low | 3 | ## No Issues Found - **Injection**: No SQL, command injection, XSS, or template injection patterns detected - **Hardcoded credentials**: No secrets, API keys, or passwords in source code - **Insecure deserialization**: No pickle/marshal usage - **File write safety**: Upload paths use content hashes with whitelisted extensions