From 641ab292777af46cdf9d6577e90c76dea99b3a0d Mon Sep 17 00:00:00 2001 From: Peter Woolery Date: Fri, 1 May 2026 15:44:57 -0700 Subject: [PATCH] fix(server): reject inverted period_start/period_end in CameraRecord A misbehaving or clock-broken device could submit period_end <= period_start, polluting the camera_records table with zero-length or inverted windows that corrupt downstream hourly analytics. Add a Pydantic model_validator so the request is rejected at the API boundary instead of silently persisting bad ranges. Found via adversarial review (run 2026-05-01-191359, both reviewers). Co-Authored-By: Claude Opus 4.7 (1M context) --- server/camera_endpoint.py | 8 +++++++- server/test_camera_endpoint.py | 12 ++++++++++++ 2 files changed, 19 insertions(+), 1 deletion(-) diff --git a/server/camera_endpoint.py b/server/camera_endpoint.py index 8bababb..99b801d 100644 --- a/server/camera_endpoint.py +++ b/server/camera_endpoint.py @@ -11,7 +11,7 @@ import sqlite3 from typing import List from fastapi import Depends -from pydantic import BaseModel, Field +from pydantic import BaseModel, Field, model_validator class CameraRecord(BaseModel): @@ -20,6 +20,12 @@ class CameraRecord(BaseModel): entries: int = Field(ge=0) exits: int = Field(ge=0) + @model_validator(mode="after") + def _period_order(self): + if self.period_end <= self.period_start: + raise ValueError("period_end must be strictly greater than period_start") + return self + class CameraEventsRequest(BaseModel): location_id: str diff --git a/server/test_camera_endpoint.py b/server/test_camera_endpoint.py index 3917b75..27f00c9 100644 --- a/server/test_camera_endpoint.py +++ b/server/test_camera_endpoint.py @@ -98,3 +98,15 @@ def test_negative_counts_rejected(): with pytest.raises(ValidationError): CameraRecord(period_start=1712000000, period_end=1712003600, entries=-1, exits=0) + + +def test_inverted_period_rejected(): + """Pydantic should reject period_end <= period_start.""" + from pydantic import ValidationError + from server.camera_endpoint import CameraRecord + with pytest.raises(ValidationError): + CameraRecord(period_start=1712003600, period_end=1712003600, + entries=0, exits=0) + with pytest.raises(ValidationError): + CameraRecord(period_start=1712003600, period_end=1712000000, + entries=0, exits=0)