[AZ-178] Implement streaming video detection endpoint

- Added `/detect/video` endpoint for true streaming video detection, allowing inference to start as upload bytes arrive.
- Introduced `run_detect_video_stream` method in the inference module to handle video processing from a file-like object.
- Updated media hashing to include a new function for computing hashes directly from files with minimal I/O.
- Enhanced documentation to reflect changes in video processing and API behavior.

Made-with: Cursor
This commit is contained in:
Oleksandr Bezdieniezhnykh
2026-04-01 03:11:43 +03:00
parent e65d8da6a3
commit be4cab4fcb
42 changed files with 2983 additions and 29 deletions
+56
View File
@@ -0,0 +1,56 @@
# Dependency Scan
**Date**: 2026-03-31
**Tool**: Manual review + WebSearch (pip-audit not installed)
**Files scanned**: `requirements.txt`, `requirements-macos.txt`, `requirements-gpu.txt`, `e2e/requirements.txt`
## Findings
### CVE-2026-28356 — python-multipart ReDoS (HIGH, CVSS 7.5)
- **Package**: python-multipart (unpinned in `requirements.txt`)
- **Vulnerability**: `parse_options_header()` uses a regex with ambiguous alternation causing exponential backtracking on malicious HTTP/multipart headers
- **Impact**: Denial of service against the FastAPI application
- **Fixed in**: 1.2.2, 1.3.1, 1.4.0-dev
- **Remediation**: Pin `python-multipart>=1.3.1`
### CVE-2025-43859 — h11 HTTP Request Smuggling via uvicorn (CRITICAL, CVSS 9.1)
- **Package**: uvicorn (unpinned in `requirements.txt`) → transitive dependency h11-0.14.0
- **Vulnerability**: Lenient parsing of line terminators in chunked-coding message bodies enables HTTP request smuggling
- **Impact**: Bypass security controls, cache poisoning, session hijacking, data leakage
- **Fixed in**: h11 update required; check for h11>=0.15.0
- **Remediation**: Pin `h11>=0.15.0` or verify uvicorn version pulls a patched h11
### Unpinned Dependencies — Supply Chain Risk (MEDIUM)
- **Packages**: `fastapi`, `uvicorn[standard]`, `python-multipart` in `requirements.txt`; `pytest`, `sseclient-py`, `flask`, `gunicorn` in `e2e/requirements.txt`
- **Risk**: Unpinned packages may resolve to different (potentially vulnerable) versions across environments. A compromised PyPI upload could be silently pulled in.
- **Remediation**: Pin all dependencies to specific versions
### opencv-python — Outdated (LOW)
- **Package**: opencv-python==4.10.0.84
- **Vulnerability**: No known CVEs for 4.10.0.84, but latest is 4.13.0.92
- **Remediation**: Consider upgrading to 4.13.0.92 when convenient
## Summary
| Severity | Count |
|----------|-------|
| Critical | 1 |
| High | 1 |
| Medium | 1 |
| Low | 1 |
## No Known Vulnerabilities
- requests==2.32.4 (patched version)
- numpy==2.3.0
- onnxruntime==1.22.0
- loguru==0.7.3
- av==14.2.0
- xxhash==3.5.0
- Cython==3.2.4
- pynvml==12.0.0
- coremltools==9.0
@@ -0,0 +1,79 @@
# Configuration & Infrastructure Review
**Date**: 2026-03-31
**Scope**: Dockerfiles, docker-compose files, .env, .gitignore
## Container Security
### Dockerfile (CPU)
| Check | Status | Detail |
|-------|--------|--------|
| Non-root user | FAIL | Runs as root (no USER directive) |
| Minimal base image | PASS | Uses `python:3.11-slim` |
| No secrets in build args | PASS | No ARG with secrets |
| apt cache cleaned | PASS | `rm -rf /var/lib/apt/lists/*` |
| No-cache pip install | PASS | `--no-cache-dir` |
| Health check | FAIL | No HEALTHCHECK directive |
### Dockerfile.gpu
| Check | Status | Detail |
|-------|--------|--------|
| Non-root user | FAIL | Runs as root (no USER directive) |
| Minimal base image | WARN | Uses `nvidia/cuda:12.2.0-runtime-ubuntu22.04` (necessary for GPU, but large) |
| No secrets in build args | PASS | No ARG with secrets |
| apt cache cleaned | PASS | `rm -rf /var/lib/apt/lists/*` |
| No-cache pip install | PASS | `--no-cache-dir` |
| Health check | FAIL | No HEALTHCHECK directive |
### Remediation
Add to both Dockerfiles:
```dockerfile
RUN adduser --disabled-password --gecos '' appuser
USER appuser
HEALTHCHECK --interval=30s --timeout=5s CMD curl -f http://localhost:8080/health || exit 1
```
## CI/CD Security
No CI/CD pipeline files found in the repository (no `.github/workflows/`, `.gitlab-ci.yml`, `azure-pipelines.yml`, or `Jenkinsfile`). CI/CD security cannot be assessed.
**Recommendation**: When CI/CD is added, include dependency scanning, SAST, secret scanning, and image scanning steps.
## Environment Configuration
| Check | Status | Detail |
|-------|--------|--------|
| .env handling | PASS | `.env` is gitignored (root level); `e2e/.env` is tracked but contains only `COMPOSE_PROFILES=cpu` (no secrets) |
| Secrets in docker-compose | PASS | No credentials in compose files; service URLs are internal Docker network names |
| Environment separation | PASS | URLs are configurable via env vars (`LOADER_URL`, `ANNOTATIONS_URL`, `VIDEOS_DIR`, `IMAGES_DIR`) |
| Secret management | N/A | No secrets required by this service (tokens come from HTTP headers) |
## Network Security
| Check | Status | Detail |
|-------|--------|--------|
| Exposed ports | WARN | Port 8080 exposed; relies on external network controls for access restriction |
| TLS configuration | FAIL | No TLS termination in the application; `CMD` runs uvicorn without `--ssl-*` flags |
| CORS | WARN | No CORSMiddleware configured — browser clients cannot make cross-origin requests (may be intentional if behind API gateway) |
| Security headers | FAIL | No security headers middleware (see SAST findings) |
## .gitignore Review
| Check | Status | Detail |
|-------|--------|--------|
| .env files excluded | PASS | `.env`, `.env.*` patterns in .gitignore |
| Credentials excluded | PASS | `.cursor/mcp.json` excluded |
| Binary files excluded | PASS | `.onnx`, media formats excluded |
| Build artifacts excluded | PASS | `build/`, `dist/`, `*.so`, `*.egg-info/` excluded |
## Summary
| Severity | Count |
|----------|-------|
| Critical | 0 |
| High | 0 |
| Medium | 3 (root containers x2, no TLS) |
| Low | 3 (no healthcheck x2, no CORS config) |
+67
View File
@@ -0,0 +1,67 @@
# OWASP Top 10 Review
**Date**: 2026-03-31
**OWASP Version**: 2025 (8th edition)
**Scope**: Full codebase
## Assessment
| # | Category | Status | Findings |
|---|----------|--------|----------|
| A01 | Broken Access Control | FAIL | 3 |
| A02 | Security Misconfiguration | FAIL | 2 |
| A03 | Software Supply Chain Failures | FAIL | 2 |
| A04 | Cryptographic Failures | FAIL | 1 |
| A05 | Injection | PASS | — |
| A06 | Insecure Design | FAIL | 2 |
| A07 | Authentication Failures | FAIL | 1 |
| A08 | Software or Data Integrity Failures | PASS | — |
| A09 | Logging & Alerting Failures | FAIL | 1 |
| A10 | Mishandling of Exceptional Conditions | FAIL | 1 |
## Category Details
### A01: Broken Access Control — FAIL
1. **No authentication required**: All endpoints are publicly accessible. Bearer tokens are optional. Any network-reachable client can trigger inference, access SSE stream, and consume resources.
2. **SSE stream leaks cross-user data**: `/detect/stream` broadcasts all detection events to all connected clients regardless of identity. Horizontal privilege escalation — any user sees every other user's results.
3. **No authorization on media operations**: `/detect/{media_id}` reads any media file that the annotations service returns a path for. No check that the requesting user owns that media.
### A02: Security Misconfiguration — FAIL
1. **Docker containers run as root**: Both `Dockerfile` and `Dockerfile.gpu` do not create or switch to a non-root user. Container compromise gives root filesystem access.
2. **No security headers**: The FastAPI app does not set standard security headers (X-Content-Type-Options, X-Frame-Options, Strict-Transport-Security, Content-Security-Policy).
### A03: Software Supply Chain Failures — FAIL
1. **Unpinned dependencies**: `fastapi`, `uvicorn[standard]`, `python-multipart` are unpinned in `requirements.txt`. A compromised PyPI upload could be silently pulled during build.
2. **Known CVEs in transitive dependencies**: h11 (via uvicorn) has CVE-2025-43859 (CRITICAL — HTTP request smuggling); python-multipart has CVE-2026-28356 (HIGH — ReDoS).
### A04: Cryptographic Failures — FAIL
1. **JWT tokens not cryptographically verified**: `TokenManager._decode_exp()` and `decode_user_id()` decode JWT payloads via base64 without signature verification. Forged tokens are accepted as valid.
### A05: Injection — PASS
No SQL, command injection, XSS, LDAP, or template injection patterns found. JSON deserialization uses `json.loads()` which is safe. User input does not reach shell commands or query constructors.
### A06: Insecure Design — FAIL
1. **No rate limiting**: Compute-intensive `/detect` endpoint has no rate limiting. With only 2 ThreadPoolExecutor workers, a small number of requests can exhaust inference capacity.
2. **No input size validation**: Uploaded files are fully read into memory (`await file.read()`) without size checks. Memory exhaustion possible with large payloads.
### A07: Authentication Failures — FAIL
1. **JWT token refresh silently fails**: `TokenManager._refresh()` catches all exceptions and passes silently. Failed refreshes go undetected, potentially allowing expired tokens to be used indefinitely (until the next decode check).
### A08: Software or Data Integrity Failures — PASS
No auto-update mechanisms, no CI/CD pipeline in the repo, no unsigned artifact consumption. Model files are loaded from a trusted internal loader service.
### A09: Logging & Alerting Failures — FAIL
1. **Security events not logged**: Authentication failures, token refresh failures, and unauthorized access attempts are silently swallowed (`except Exception: pass`). No audit trail for security-relevant events.
### A10: Mishandling of Exceptional Conditions — FAIL
1. **Silent exception swallowing**: Multiple `except Exception: pass` blocks (token refresh, annotation posting) hide failures. The system "fails open" — errors are ignored and processing continues, potentially in an inconsistent security state.
+140
View File
@@ -0,0 +1,140 @@
# Security Audit Report
**Date**: 2026-03-31
**Scope**: Azaion.Detections (full codebase)
**Verdict**: FAIL
## Summary
| Severity | Count |
|----------|-------|
| Critical | 1 |
| High | 3 |
| Medium | 5 |
| Low | 5 |
## OWASP Top 10 Assessment
| Category | Status | Findings |
|----------|--------|----------|
| A01 Broken Access Control | FAIL | 3 |
| A02 Security Misconfiguration | FAIL | 2 |
| A03 Software Supply Chain Failures | FAIL | 2 |
| A04 Cryptographic Failures | FAIL | 1 |
| A05 Injection | PASS | — |
| A06 Insecure Design | FAIL | 2 |
| A07 Authentication Failures | FAIL | 1 |
| A08 Software or Data Integrity Failures | PASS | — |
| A09 Logging & Alerting Failures | FAIL | 1 |
| A10 Mishandling of Exceptional Conditions | FAIL | 1 |
## Findings
| # | Severity | Category | Location | Title |
|---|----------|----------|----------|-------|
| 1 | Critical | A03 Supply Chain | requirements.txt (uvicorn→h11) | HTTP request smuggling via h11 CVE-2025-43859 |
| 2 | High | A04 Crypto | src/main.py:67-99 | JWT decoded without signature verification |
| 3 | High | A01 Access Control | src/main.py (all routes) | No authentication required on any endpoint |
| 4 | High | A03 Supply Chain | requirements.txt (python-multipart) | ReDoS via python-multipart CVE-2026-28356 |
| 5 | Medium | A01 Access Control | src/main.py:608-627 | SSE stream broadcasts cross-user data |
| 6 | Medium | A06 Insecure Design | src/main.py:348-469 | No rate limiting on inference endpoints |
| 7 | Medium | A02 Misconfig | Dockerfile, Dockerfile.gpu | Containers run as root |
| 8 | Medium | A03 Supply Chain | requirements.txt | Unpinned critical dependencies |
| 9 | Medium | A02 Misconfig | Dockerfile, Dockerfile.gpu | No TLS and no security headers |
| 10 | Low | A06 Insecure Design | src/main.py:357 | No request body size limit |
| 11 | Low | A10 Exceptions | src/main.py:63,490 | Silent exception swallowing |
| 12 | Low | A09 Logging | src/main.py | Security events not logged |
| 13 | Low | A01 Access Control | src/main.py:449-450 | Exception details leaked in responses |
| 14 | Low | A07 Auth | src/main.py:54-64 | Token refresh failure silently ignored |
### Finding Details
**F1: HTTP Request Smuggling via h11** (Critical / A03)
- Location: `requirements.txt` — unpinned `uvicorn[standard]` pulls `h11-0.14.0`
- Description: CVE-2025-43859 (CVSS 9.1). Lenient parsing of chunked-coding line terminators enables HTTP request smuggling.
- Impact: Bypass security controls, cache poisoning, session hijacking, data leakage
- Remediation: Pin `h11>=0.15.0` in requirements.txt
**F2: JWT Decoded Without Signature Verification** (High / A04)
- Location: `src/main.py:67-99` (`TokenManager._decode_exp`, `decode_user_id`)
- Description: JWT payloads are base64-decoded without cryptographic signature verification. Any client can forge tokens with arbitrary claims.
- Impact: Full user impersonation — attacker crafts JWT with target's user ID to access their AI settings, post annotations under their account
- Remediation: Use PyJWT with signature verification against the issuer's public key
**F3: No Authentication on Endpoints** (High / A01)
- Location: `src/main.py` — all route handlers
- Description: All endpoints are publicly accessible. Bearer tokens are optional.
- Impact: Unauthorized inference triggering, resource exhaustion, unauthorized access to SSE event stream
- Remediation: Add FastAPI dependency injection for auth middleware on `/detect`, `/detect/{media_id}`, `/detect/stream`
**F4: python-multipart ReDoS** (High / A03)
- Location: `requirements.txt` — unpinned `python-multipart`
- Description: CVE-2026-28356 (CVSS 7.5). `parse_options_header()` regex causes exponential backtracking on malicious headers.
- Impact: Denial of service
- Remediation: Pin `python-multipart>=1.3.1`
**F5: SSE Stream Cross-User Data Leak** (Medium / A01)
- Location: `src/main.py:608-627`
- Description: All detection events broadcast to all connected SSE clients without filtering.
- Impact: Any client sees all users' detection results (media IDs, coordinates, status)
- Remediation: Associate SSE queues with authenticated users; filter events by ownership
**F6: No Rate Limiting** (Medium / A06)
- Location: `src/main.py:348-469`, `src/main.py:494-605`
- Description: No rate limiting on compute-intensive inference endpoints.
- Impact: DoS via inference exhaustion (2 worker threads)
- Remediation: Add slowapi or similar rate limiting middleware
**F7: Docker Containers Run as Root** (Medium / A02)
- Location: `Dockerfile:10`, `Dockerfile.gpu:10`
- Description: No USER directive; processes run as root inside containers.
- Impact: Container escape or compromise gives root filesystem access
- Remediation: Add non-root user (`adduser --disabled-password appuser && USER appuser`)
**F8: Unpinned Critical Dependencies** (Medium / A03)
- Location: `requirements.txt`
- Description: `fastapi`, `uvicorn[standard]`, `python-multipart` are unpinned.
- Impact: Supply chain attack via compromised PyPI package; inconsistent builds across environments
- Remediation: Pin all dependencies to specific versions
**F9: No TLS / No Security Headers** (Medium / A02)
- Location: `Dockerfile` CMD, `src/main.py` (app setup)
- Description: Uvicorn runs without TLS. No security headers middleware.
- Impact: Data in transit is unencrypted; missing browser security protections
- Remediation: Terminate TLS at reverse proxy or add `--ssl-*` flags; add security headers middleware
**F10-F14**: Low severity findings (request size limits, exception handling, logging gaps) documented in `static_analysis.md` and `owasp_review.md`.
## Dependency Vulnerabilities
| Package | CVE | Severity | Fix Version |
|---------|-----|----------|-------------|
| h11 (via uvicorn) | CVE-2025-43859 | Critical | h11>=0.15.0 |
| python-multipart | CVE-2026-28356 | High | >=1.3.1 |
| opencv-python | — | Low (outdated) | 4.13.0.92 |
## Recommendations
### Immediate (Critical/High)
1. **Pin h11>=0.15.0** to fix HTTP request smuggling vulnerability
2. **Pin python-multipart>=1.3.1** to fix ReDoS vulnerability
3. **Pin all dependencies** to specific versions in requirements.txt
4. **Add JWT signature verification** using PyJWT with the issuer's public key
5. **Add authentication middleware** requiring valid tokens on /detect, /detect/{media_id}, /detect/stream
### Short-term (Medium)
6. **Filter SSE events by user** — associate queues with authenticated sessions
7. **Add rate limiting** on inference endpoints (slowapi or nginx rate limiting)
8. **Run containers as non-root** — add USER directive to Dockerfiles
9. **Add security headers** middleware (X-Content-Type-Options, X-Frame-Options, HSTS)
10. **Configure TLS** at reverse proxy level or add Dockerfile HEALTHCHECK
### Long-term (Low / Hardening)
11. **Add request body size limits** via uvicorn config or middleware
12. **Log security events** — authentication failures, token refresh failures, rate limit hits
13. **Replace silent exception handling** with proper error logging
14. **Set up CI/CD** with dependency scanning, SAST, and secret scanning
15. **Add CORS configuration** if browser clients will access the API directly
+72
View File
@@ -0,0 +1,72 @@
# 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