mirror of
https://github.com/blakeblackshear/frigate.git
synced 2026-07-22 03:39:02 +03:00
Merge remote-tracking branch 'origin/master' into dev
CI / AMD64 Build (push) Has been cancelled
CI / ARM Build (push) Has been cancelled
CI / Jetson Jetpack 6 (push) Has been cancelled
CI / AMD64 Extra Build (push) Has been cancelled
CI / ARM Extra Build (push) Has been cancelled
CI / Synaptics Build (push) Has been cancelled
CI / Assemble and push default build (push) Has been cancelled
CI / AMD64 Build (push) Has been cancelled
CI / ARM Build (push) Has been cancelled
CI / Jetson Jetpack 6 (push) Has been cancelled
CI / AMD64 Extra Build (push) Has been cancelled
CI / ARM Extra Build (push) Has been cancelled
CI / Synaptics Build (push) Has been cancelled
CI / Assemble and push default build (push) Has been cancelled
Resolve conflicts in the export pipeline where dev's job-queue refactor met master's chapter-metadata and security work. - Unify chapter support under ChaptersEnum (none / recording_segments / review_items); the realtime stream-copy export selects the per-segment or per-review-item builder by the camera's configured mode. Thread chapters through ExportRecordingsBody -> _build_export_job -> ExportJob -> RecordingExporter. - Keep master's creation_time/comment export metadata and fix a video_path duplication the textual merge introduced in the preview command. - Move the chapters request field to ExportRecordingsBody (the single export endpoint) where it is actually honored. Restore security fixes the automatic merge would have reverted: - frigate/util/services.py: restore the #23493 rename to the public is_go2rtc_arbitrary_exec_allowed so create_config.py's dynamic-source exec guard imports and runs (the merge otherwise left a broken import). - Preserve the export image-path ".." traversal check inside _sanitize_existing_image, applied to single/custom/batch exports. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -15,12 +15,24 @@ from frigate.const import (
|
||||
)
|
||||
from frigate.ffmpeg_presets import parse_preset_hardware_acceleration_encode
|
||||
from frigate.util.config import find_config_file, resolve_ffmpeg_path
|
||||
from frigate.util.services import is_restricted_go2rtc_source
|
||||
from frigate.util.services import (
|
||||
is_go2rtc_arbitrary_exec_allowed,
|
||||
is_restricted_go2rtc_source,
|
||||
)
|
||||
|
||||
sys.path.remove("/opt/frigate")
|
||||
|
||||
yaml = YAML()
|
||||
|
||||
FRIGATE_ENV_VARS = {k: v for k, v in os.environ.items() if k.startswith("FRIGATE_")}
|
||||
# read docker secret files as env vars too
|
||||
if os.path.isdir("/run/secrets"):
|
||||
for secret_file in os.listdir("/run/secrets"):
|
||||
if secret_file.startswith("FRIGATE_"):
|
||||
FRIGATE_ENV_VARS[secret_file] = (
|
||||
Path(os.path.join("/run/secrets", secret_file)).read_text().strip()
|
||||
)
|
||||
|
||||
config_file = find_config_file()
|
||||
|
||||
try:
|
||||
@@ -100,7 +112,7 @@ for name in list(go2rtc_config.get("streams", {})):
|
||||
|
||||
if isinstance(stream, str):
|
||||
try:
|
||||
formatted_stream = substitute_frigate_vars(stream)
|
||||
formatted_stream = stream.format(**FRIGATE_ENV_VARS)
|
||||
if is_restricted_go2rtc_source(formatted_stream):
|
||||
print(
|
||||
f"[ERROR] Stream '{name}' uses a restricted source (echo/expr/exec) which is disabled by default for security. "
|
||||
@@ -119,7 +131,7 @@ for name in list(go2rtc_config.get("streams", {})):
|
||||
filtered_streams = []
|
||||
for i, stream_item in enumerate(stream):
|
||||
try:
|
||||
formatted_stream = substitute_frigate_vars(stream_item)
|
||||
formatted_stream = stream_item.format(**FRIGATE_ENV_VARS)
|
||||
if is_restricted_go2rtc_source(formatted_stream):
|
||||
print(
|
||||
f"[ERROR] Stream '{name}' item {i + 1} uses a restricted source (echo/expr/exec) which is disabled by default for security. "
|
||||
@@ -143,6 +155,20 @@ for name in list(go2rtc_config.get("streams", {})):
|
||||
)
|
||||
del go2rtc_config["streams"][name]
|
||||
|
||||
elif isinstance(stream, dict):
|
||||
# The map form ({"url": ...}) lets go2rtc resolve the source
|
||||
# recursively, so it is effectively a dynamic way to generate the URL
|
||||
# for a stream. That can only be backed by an exec source, so it cannot
|
||||
# be allowed unless arbitrary exec is explicitly enabled. When it is
|
||||
# enabled, leave the map untouched for go2rtc to resolve.
|
||||
if not is_go2rtc_arbitrary_exec_allowed():
|
||||
print(
|
||||
f"[ERROR] Stream '{name}' uses a dynamic source format which is disabled by default for security. "
|
||||
f"Set GO2RTC_ALLOW_ARBITRARY_EXEC=true to enable arbitrary exec sources."
|
||||
)
|
||||
del go2rtc_config["streams"][name]
|
||||
continue
|
||||
|
||||
# add birdseye restream stream if enabled
|
||||
if config.get("birdseye", {}).get("restream", False):
|
||||
birdseye: dict[str, Any] = config.get("birdseye")
|
||||
|
||||
Reference in New Issue
Block a user