diff --git a/backend/app/middleware/validation.py b/backend/app/middleware/validation.py index 657b824..23d5ef4 100644 --- a/backend/app/middleware/validation.py +++ b/backend/app/middleware/validation.py @@ -57,8 +57,8 @@ class RequestValidator: r"%2e%2e%2f", r"%2e%2e\\", - # Command injection (removed $ to allow MongoDB operators in controlled contexts) - r"[;&|`](?!\s*$)", # Allow $ but not as command separator + # Command injection (removed $ and ; — semicolons are common in natural language) + r"[&|`](?!\s*$)", r"\b(rm|wget|curl|nc|bash|sh|cmd|powershell)\b\s+", # MongoDB injection — NoSQL operator abuse @@ -188,6 +188,9 @@ class RequestValidator: except json.JSONDecodeError as e: raise ValidationError(f"Invalid JSON: {e}") from e + # Fields that contain free-form natural language — skip injection pattern checks + _FREETEXT_FIELDS = {"captions_vtt", "audio_description_vtt", "text", "notes", "change_note", "description"} + def _validate_json_values(self, obj: Any, path: str = "root") -> None: """Recursively validate JSON values.""" if isinstance(obj, dict): @@ -196,7 +199,9 @@ class RequestValidator: for key, value in obj.items(): self.validate_string_content(key, f"{path}.key") - self._validate_json_values(value, f"{path}.{key}") + # Skip pattern scanning for free-text fields (VTT content, notes, etc.) + if key not in self._FREETEXT_FIELDS: + self._validate_json_values(value, f"{path}.{key}") elif isinstance(obj, list): if len(obj) > 1000: # Prevent large arrays diff --git a/infra/gcs-cors.json b/infra/gcs-cors.json index 6e86330..5e70428 100644 --- a/infra/gcs-cors.json +++ b/infra/gcs-cors.json @@ -6,10 +6,11 @@ "http://localhost:5173", "http://localhost:3000" ], - "method": ["PUT"], + "method": ["GET", "HEAD", "PUT"], "responseHeader": [ "Content-Type", "Content-Range", + "Content-Disposition", "X-Goog-Resumable" ], "maxAgeSeconds": 3600