mirror of
https://github.com/azaion/detections.git
synced 2026-04-22 08:56:32 +00:00
[AZ-178] Fix Critical/High security findings: auth, CVEs, non-root containers, per-job SSE
- Pin all deps; h11==0.16.0 (CVE-2025-43859), python-multipart>=1.3.1 (CVE-2026-28356), PyJWT==2.12.1
- Add HMAC JWT verification (require_auth FastAPI dependency, JWT_SECRET-gated)
- Fix TokenManager._refresh() to use ADMIN_API_URL instead of ANNOTATIONS_URL
- Rename POST /detect → POST /detect/image (image-only, rejects video files)
- Replace global SSE stream with per-job SSE: GET /detect/{media_id} with event replay buffer
- Apply require_auth to all 4 protected endpoints
- Fix on_annotation/on_status closure to use mutable current_id for correct post-upload event routing
- Add non-root appuser to Dockerfile and Dockerfile.gpu
- Add JWT_SECRET to e2e/docker-compose.test.yml and run-tests.sh
- Update all e2e tests and unit tests for new endpoints and HMAC token signing
- 64/64 tests pass
Made-with: Cursor
This commit is contained in:
@@ -81,10 +81,11 @@ def _weather_label_ok(label, base_names):
|
||||
|
||||
|
||||
@pytest.mark.slow
|
||||
def test_ft_p_03_detection_response_structure_ac1(http_client, image_small, warm_engine):
|
||||
def test_ft_p_03_detection_response_structure_ac1(http_client, image_small, warm_engine, auth_headers):
|
||||
r = http_client.post(
|
||||
"/detect",
|
||||
"/detect/image",
|
||||
files={"file": ("img.jpg", image_small, "image/jpeg")},
|
||||
headers=auth_headers,
|
||||
)
|
||||
assert r.status_code == 200
|
||||
body = r.json()
|
||||
@@ -105,12 +106,13 @@ def test_ft_p_03_detection_response_structure_ac1(http_client, image_small, warm
|
||||
|
||||
|
||||
@pytest.mark.slow
|
||||
def test_ft_p_05_confidence_filtering_ac2(http_client, image_small, warm_engine):
|
||||
def test_ft_p_05_confidence_filtering_ac2(http_client, image_small, warm_engine, auth_headers):
|
||||
cfg_hi = json.dumps({"probability_threshold": 0.8})
|
||||
r_hi = http_client.post(
|
||||
"/detect",
|
||||
"/detect/image",
|
||||
files={"file": ("img.jpg", image_small, "image/jpeg")},
|
||||
data={"config": cfg_hi},
|
||||
headers=auth_headers,
|
||||
)
|
||||
assert r_hi.status_code == 200
|
||||
hi = r_hi.json()
|
||||
@@ -119,9 +121,10 @@ def test_ft_p_05_confidence_filtering_ac2(http_client, image_small, warm_engine)
|
||||
assert float(d["confidence"]) + _EPS >= 0.8
|
||||
cfg_lo = json.dumps({"probability_threshold": 0.1})
|
||||
r_lo = http_client.post(
|
||||
"/detect",
|
||||
"/detect/image",
|
||||
files={"file": ("img.jpg", image_small, "image/jpeg")},
|
||||
data={"config": cfg_lo},
|
||||
headers=auth_headers,
|
||||
)
|
||||
assert r_lo.status_code == 200
|
||||
lo = r_lo.json()
|
||||
@@ -130,12 +133,13 @@ def test_ft_p_05_confidence_filtering_ac2(http_client, image_small, warm_engine)
|
||||
|
||||
|
||||
@pytest.mark.slow
|
||||
def test_ft_p_06_overlap_deduplication_ac3(http_client, image_dense, warm_engine):
|
||||
def test_ft_p_06_overlap_deduplication_ac3(http_client, image_dense, warm_engine, auth_headers):
|
||||
cfg_loose = json.dumps({"tracking_intersection_threshold": 0.6})
|
||||
r1 = http_client.post(
|
||||
"/detect",
|
||||
"/detect/image",
|
||||
files={"file": ("img.jpg", image_dense, "image/jpeg")},
|
||||
data={"config": cfg_loose},
|
||||
headers=auth_headers,
|
||||
timeout=_DETECT_SLOW_TIMEOUT,
|
||||
)
|
||||
assert r1.status_code == 200
|
||||
@@ -151,9 +155,10 @@ def test_ft_p_06_overlap_deduplication_ac3(http_client, image_dense, warm_engine
|
||||
assert ratio <= 0.6 + _EPS, (label, ratio)
|
||||
cfg_strict = json.dumps({"tracking_intersection_threshold": 0.01})
|
||||
r2 = http_client.post(
|
||||
"/detect",
|
||||
"/detect/image",
|
||||
files={"file": ("img.jpg", image_dense, "image/jpeg")},
|
||||
data={"config": cfg_strict},
|
||||
headers=auth_headers,
|
||||
timeout=_DETECT_SLOW_TIMEOUT,
|
||||
)
|
||||
assert r2.status_code == 200
|
||||
@@ -163,7 +168,7 @@ def test_ft_p_06_overlap_deduplication_ac3(http_client, image_dense, warm_engine
|
||||
|
||||
|
||||
@pytest.mark.slow
|
||||
def test_ft_p_07_physical_size_filtering_ac4(http_client, image_small, warm_engine):
|
||||
def test_ft_p_07_physical_size_filtering_ac4(http_client, image_small, warm_engine, auth_headers):
|
||||
by_id, _ = _load_classes_media()
|
||||
wh = _image_width_height(image_small)
|
||||
assert wh is not None
|
||||
@@ -180,9 +185,10 @@ def test_ft_p_07_physical_size_filtering_ac4(http_client, image_small, warm_engi
|
||||
}
|
||||
)
|
||||
r = http_client.post(
|
||||
"/detect",
|
||||
"/detect/image",
|
||||
files={"file": ("img.jpg", image_small, "image/jpeg")},
|
||||
data={"config": cfg},
|
||||
headers=auth_headers,
|
||||
timeout=_DETECT_SLOW_TIMEOUT,
|
||||
)
|
||||
assert r.status_code == 200
|
||||
@@ -197,12 +203,13 @@ def test_ft_p_07_physical_size_filtering_ac4(http_client, image_small, warm_engi
|
||||
|
||||
@pytest.mark.slow
|
||||
def test_ft_p_13_weather_mode_class_variants_ac5(
|
||||
http_client, image_different_types, warm_engine
|
||||
http_client, image_different_types, warm_engine, auth_headers
|
||||
):
|
||||
_, base_names = _load_classes_media()
|
||||
r = http_client.post(
|
||||
"/detect",
|
||||
"/detect/image",
|
||||
files={"file": ("img.jpg", image_different_types, "image/jpeg")},
|
||||
headers=auth_headers,
|
||||
timeout=_DETECT_SLOW_TIMEOUT,
|
||||
)
|
||||
assert r.status_code == 200
|
||||
|
||||
Reference in New Issue
Block a user