CVE-2024-32874: Insufficient Information (NVD-CWE-noinfo)
value: Frigate is a network video recorder (NVR) with realtime local object detection for IP cameras. Below 0.13.2 Release, when uploading a file or retrieving the filename, a user may intentionally use a large Unicode filename which would lead to a application-level denial of service. This is due to no limitation set on the length of the filename and the costy use of the Unicode normalization with the form NFKD under the hood of `secure_filename()`.
CWE description: There is insufficient information about the issue to classify it; details are unkown or unspecified.
Published: 2024-05-14T15:37Z
Repository: https://github.com/blakeblackshear/frigate
Language: Python
Files changed: frigate/api/media.py
Commit date: 2024-04-27 11:27:23 -0500
Commit message: Security improvement: ensure user provided file names don't exceed max length (#11129)

* Security fixes

* formatting
Diff: diff --git a/frigate/api/media.py b/frigate/api/media.py
index 51946764..be4ea08d 100644
--- a/frigate/api/media.py
+++ b/frigate/api/media.py
@@ -390,11 +390,21 @@ def recording_clip(camera_name, start_ts, end_ts):
             playlist_lines.append(f"inpoint {int(start_ts - clip.start_time)}")
         # if this is the ending clip, add an outpoint
         if clip.end_time > end_ts:
             playlist_lines.append(f"outpoint {int(end_ts - clip.start_time)}")
 
-    file_name = secure_filename(f"clip_{camera_name}_{start_ts}-{end_ts}.mp4")
+    file_name = f"clip_{camera_name}_{start_ts}-{end_ts}.mp4"
+
+    if len(file_name) > 1000:
+        return make_response(
+            jsonify(
+                {"success": False, "message": "Filename exceeded max length of 1000"}
+            ),
+            403,
+        )
+
+    file_name = secure_filename(file_name)
     path = os.path.join(CACHE_DIR, file_name)
 
     if not os.path.exists(path):
         ffmpeg_cmd = [
             "ffmpeg",
@@ -1165,11 +1175,24 @@ def preview_gif(camera_name: str, start_ts, end_ts, max_cache_age=2592000):
 
 
 @MediaBp.route("/<camera_name>/start/<int:start_ts>/end/<int:end_ts>/preview.mp4")
 @MediaBp.route("/<camera_name>/start/<float:start_ts>/end/<float:end_ts>/preview.mp4")
 def preview_mp4(camera_name: str, start_ts, end_ts):
-    file_name = secure_filename(f"clip_{camera_name}_{start_ts}-{end_ts}.mp4")
+    file_name = f"clip_{camera_name}_{start_ts}-{end_ts}.mp4"
+
+    if len(file_name) > 1000:
+        return make_response(
+            jsonify(
+                {
+                    "success": False,
+                    "message": "Filename exceeded max length of 1000 characters.",
+                }
+            ),
+            403,
+        )
+
+    file_name = secure_filename(file_name)
     path = os.path.join(CACHE_DIR, file_name)
 
     if datetime.fromtimestamp(start_ts) < datetime.now().replace(minute=0, second=0):
         # has preview mp4
         try:
@@ -1335,10 +1358,18 @@ def review_preview(id: str):
 
 @MediaBp.route("/preview/<file_name>/thumbnail.jpg")
 @MediaBp.route("/preview/<file_name>/thumbnail.webp")
 def preview_thumbnail(file_name: str):
     """Get a thumbnail from the cached preview frames."""
+    if len(file_name) > 1000:
+        return make_response(
+            jsonify(
+                {"success": False, "message": "Filename exceeded max length of 1000"}
+            ),
+            403,
+        )
+
     safe_file_name_current = secure_filename(file_name)
     preview_dir = os.path.join(CACHE_DIR, "preview_frames")
 
     try:
         with open(

Vulnerable code:     file_name = secure_filename(f"clip_{camera_name}_{start_ts}-{end_ts}.mp4")
    file_name = secure_filename(f"clip_{camera_name}_{start_ts}-{end_ts}.mp4")
Fixed code:     file_name = f"clip_{camera_name}_{start_ts}-{end_ts}.mp4"

    if len(file_name) > 1000:
        return make_response(
            jsonify(
                {"success": False, "message": "Filename exceeded max length of 1000"}
            ),
            403,
        )

    file_name = secure_filename(file_name)
    file_name = f"clip_{camera_name}_{start_ts}-{end_ts}.mp4"

    if len(file_name) > 1000:
        return make_response(
            jsonify(
                {
                    "success": False,
                    "message": "Filename exceeded max length of 1000 characters.",
                }
            ),
            403,
        )

    file_name = secure_filename(file_name)
    if len(file_name) > 1000:
        return make_response(
            jsonify(
                {"success": False, "message": "Filename exceeded max length of 1000"}
            ),
            403,
        )