fix(security): fix false-positive injection blocks on French/multilingual VTT content

- Remove ';' from command-injection pattern — semicolons are common in French
  and other European languages, not a shell injection risk in JSON context
- Skip security pattern scanning for free-text fields (captions_vtt,
  audio_description_vtt, notes, etc.) — natural language always generates
  false positives against injection regexes
- Add GET/HEAD to GCS CORS config so browsers can load signed VTT URLs

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Vadym Samoilenko 2026-05-13 19:11:01 +01:00
parent 4645e67611
commit f22d568fc5
2 changed files with 10 additions and 4 deletions

View file

@ -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

View file

@ -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