Repository navigation
security: review hardening (password ACL, credential masking, FFI bounds) - #292
Merged
Merged
Conversation
* Make review user read status consistent with other APIs * Validate URLs for web push endpoint * Validate the role for a custom viewer, rate limit password changing * Cleanup (cherry picked from commit ad79e66) Conflict in register_notifications resolved by keeping the fork's remote-user handling and validating the subscription before storing it. Fork test test_notification_api updated to send a well-formed subscription. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…th full camera access only (blakeblackshear#24056) * Fix review summary analysis * Add ability to scope based on full camera access (cherry picked from commit fc79aea) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
clean_camera_user_pass only matched usernames made of [a-zA-Z0-9_-], so a URL such as rtsp://john.doe:secret@cam or rtsp://:secret@cam, or an http snapshot URL with user=john.doe&password=..., passed through unmasked. That function masks ffmpeg/go2rtc log lines and the camera input paths, ffmpeg_cmds and go2rtc streams returned by GET /config, which every authenticated role can read. Redaction now uses broader patterns; the narrow pattern used by escape_special_characters is unchanged so the ffmpeg command line is built exactly as before. GET /config also returned go2rtc's rtsp/api listener passwords and WebRTC ICE server credentials verbatim. They are now replaced with the redaction sentinel for non-admin roles; admins still receive them for editing. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
yuv420_crop_repack_rust passed caller-supplied src_w/src_h to Rust without checking them against the frame, so a frame smaller than claimed was read out of bounds. yuv420_to_3channel_rust and yuv420_region_to_rgb_resize_rust did not reject odd sizes, non-planar input, degenerate regions or a zero destination, where Rust returns early and the wrapper handed back its uninitialized np.empty buffer as a converted frame (e.g. an odd detect width fed heap garbage to the detector). All three now raise ValueError, which the callers already catch to fall back to the numpy/OpenCV path. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The runner allocated n*dim*4 bytes (or dim bytes for an error) straight from the response header, and the stats reader accumulated an unbounded body. A broken or hostile peer at the configured endpoint could make the embeddings process allocate gigabytes. The response must now match the request batch size with a sane embedding width, and error/stats bodies are capped at 64 KiB. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Versions match what the current frigate-ncnn-vulkan-npu image ships. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
This is a security review of
dev, covering the API auth and roles, file-serving and upload paths, SSRF, go2rtc/ffmpeg argument building, credential exposure, the WebSocket classifier, the Rust FFI wrappers and the fork's Docker build. It fixes the clear issues with small changes, each covered by tests. Anything debatable, or anything that would change behaviour, is listed under recommendations and left unchanged.Two of the commits are clean cherry-picks (
-x) of upstream security fixes thatdevhad not picked up yet. Both are already onsync/upstream-0.18, so they should be no-ops when that branch is integrated.Findings
frigate/api/auth.pyupdate_passwordviewerrole was blocked from changing another user's password. Any custom role (e.g.neighbor) could reset the admin password (privilege escalation).admincan target other accounts. The endpoint is now rate-limited. Tests intest_http_password.pyfrigate/api/notification.pyregister_notificationshttpsURL on port 443, no credentials, no private/internal host. The conflict was resolved by keeping the fork's remote-user handling.test_webpush_registration.py,test_notification_api.pyfrigate/api/review.pyDELETE /review/{id}/viewedfrigate/api/review.pyPOST /review/summarize/...require_full_camera_access. Tests intest_http_camera_access.pyfrigate/util/builtin.pyclean_camera_user_pass[a-zA-Z0-9_-].rtsp://john.doe:pw@…,rtsp://admin%40x:pw@…,rtsp://:pw@…anduser=john.doe&password=…were returned unmasked. This affected log lines and the camera paths,ffmpeg_cmdsand go2rtc streams inGET /config, which every authenticated role can read.REGEX_REDACT_*). The pattern used byescape_special_charactersis unchanged, so the ffmpeg command line is built exactly as before. Tests intest_camera_pw.pyfrigate/api/app.pyGET /configgo2rtc.rtsp.password,go2rtc.api.passwordandgo2rtc.webrtc.ice_servers[].credentialwere returned verbatim to all roles.test_http_app.pyfrigate/util/frame_rs.pyyuv420_crop_repack_rustsrc_w/src_hwere passed to Rust unchecked against the frame, so a smaller frame was read out of bounds.ValueError. The caller falls back to numpy.test_frame_rs_bounds.pyframe_rs.pyyuv420_to_3channel_rust,yuv420_region_to_rgb_resize_rustnp.emptybuffer as a frame. An odddetect.widthwould feed heap garbage to the detector.ValueErrorso the existing Python fallback runs.test_frame_rs_bounds.pyfrigate/detectors/npu_service_runner.pyn*dim*4(and error length) drove an unbounded allocation. The stats body was read without a limit.0 < dim <= 65536. Error and stats bodies are capped at 64 KiB.test_npu_service_runner.pydocker/ncnn-vulkan/Dockerfilepip install --no-deps ncnn portalocker tqdmwas unpinned.frigate/api/fastapi_app.pycheck_csrfX-CSRF-Tokenis present. This works as a custom-header CSRF defence only because browsers can't send it cross-origin without CORS. Combined with SameSite unset (Lax default), it is acceptable but brittle.Origin/Sec-Fetch-Sitefor unsafe methods.frigate/api/auth.pyjwt.decodeOctKeymakenone/RS confusion not exploitable, but it is implicit.algorithms=["HS256"](upstream code, so best done upstream).frigate/embeddings/onnx/base_embedding.py_process_imagerequests.get(image)with no timeout or size limit forhttp*strings. No user-reachable caller passes a string today (all callers pass thumbnail bytes or arrays).frigate/api/classification.py/faces/{name}/register,/faces/recognizefile.file.read()unbounded, no content-type check, decoded with OpenCV (decompression bombs). Admin-only, and nginx caps the body at 20 MB.frigate/api/export.pyGET /cases,/cases/{id}frigate/api/camera.pygo2rtc add/deleter.textfrom go2rtc, which may echo a stream source. Admin-only.clean_camera_user_pass.docker/ncnn-vulkan/DockerfileFROM ghcr.io/blakeblackshear/frigate:stableis a mutable tag. The container runs as root (upstream design).Reviewed with no issues found
require_camera_accessinline.safe_join/sanitize_path_component(faces, classification, triggers, exports), the/preview/{file}thumbnail endpoint, and the media-auth URI classifier (rejects./..)./labelscache (the access check runs before the cache lookup) and the go2rtcexec:/echo:/expr:filter (checked before and after variable substitution).notification_testblocked.0600.frame_rswrappers,motion/rust_engine.pyanddetectors/rust_yolo.pywere already bounds-checked.Testing
ruff checkandruff format --checkonfrigate migrations docker *.py: clean.python3 generate_api_auth_spec.py --check: up to date.frigate-ncnn-vulkan-npu:latestwith freshly built Rust libs mounted over/opt/frigate: Ran 1042 tests, OK (skipped=1). The known-flaky WAL test passed on this run.cargo test --release: frame-rs 33 passed, motion-rs 25 passed, yolo-rs 12 passed (no Rust changes).No changes to
test/upstream-0.18-rust-integration/ #244.🤖 Generated with Claude Code