# 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
