Skip to content

zoneminder

v1.38.3 Security

This release includes 8 security fixes for security teams reviewing exposed deployments.

Published 7d Media Servers
βœ“ No known CVEs patched
Read the diff β†’ Tool health β†’ What is this tool? β†’
This release patches 8 known CVEs

Topics

cctv distributed h264 h265 scalable video
+3 more
vms zmninja zoneminder

Affected surfaces

auth rbac rce_ssrf

ReleasePort's take

Moderate signal
editorial:auto 7d

Versionβ€―1.38.3 introduces critical security hardening for the ConfigsController and FilterTerm evaluation, eliminating privilege‑escalation risks via RBAC checks and removal of eval().

Why it matters: The release adds System=Edit RBAC checks to prevent API privilege escalation (severityβ€―95) and replaces eval() with safe compare(), fixing RCE vulnerabilitiesβ€”critical for any deployment exposing the ConfigsController or FilterTerm APIs.

Summary

AI summary

Broad release touches Web Interface & API, πŸ”’ Security & Hardening, 🎯 ONVIF Improvements, and πŸ” Authentication & Permissions.

Changes in this release

Security Critical

Adds System=Edit RBAC checks to ConfigsController edit/delete preventing API privilege escalation (RCE).

Adds System=Edit RBAC checks to ConfigsController edit/delete preventing API privilege escalation (RCE).

Source: llm_adapter@2026-05-28

Confidence: high

β€”
Security Critical

Replaces eval() in FilterTerm SystemLoad/DiskPercent/DiskBlocks evaluation with safe compare(), fixing RCE (eval‑based).

Replaces eval() in FilterTerm SystemLoad/DiskPercent/DiskBlocks evaluation with safe compare(), fixing RCE (eval‑based).

Source: llm_adapter@2026-05-28

Confidence: high

β€”
Security High

Sanitizes monitor Device path to prevent command injection (GHSA-g66m-77fq-79v9).

Sanitizes monitor Device path to prevent command injection (GHSA-g66m-77fq-79v9).

Source: llm_adapter@2026-05-28

Confidence: high

β€”
Security High

Escapes URLs in camera probe wget() to prevent command injection (GHSA-745h-vg7c-73cg).

Escapes URLs in camera probe wget() to prevent command injection (GHSA-745h-vg7c-73cg).

Source: llm_adapter@2026-05-28

Confidence: high

β€”
Security High

Hardens ffmpeg path handling in Event.php to prevent command injection.

Hardens ffmpeg path handling in Event.php to prevent command injection.

Source: granite4.1:30b@2026-05-28-audit

Confidence: low

β€”
Security High

Sanitizes export filename/connkey at AJAX boundary and suppresses raw SQL error text in events ajax responses (XSS sanitization).

Sanitizes export filename/connkey at AJAX boundary and suppresses raw SQL error text in events ajax responses (XSS sanitization).

Source: granite4.1:30b@2026-05-28-audit

Confidence: low

β€”
Security High

Validates FilterTerm operators, intval's monitor IDs, restricts getFormChanges column keys, and dbEscapes MIME fields (SQL injection fixes).

Validates FilterTerm operators, intval's monitor IDs, restricts getFormChanges column keys, and dbEscapes MIME fields (SQL injection fixes).

Source: granite4.1:30b@2026-05-28-audit

Confidence: low

β€”
Breaking High

Makes C++ User class role‑aware, consulting Role_Monitors_Permissions and matching PHP visibleMonitor() logic (role‑based stream access).

Makes C++ User class role‑aware, consulting Role_Monitors_Permissions and matching PHP visibleMonitor() logic (role‑based stream access).

Source: llm_adapter@2026-05-28

Confidence: high

β€”
Feature Medium

Adds CLOSE_DURATION event close mode handling (previously silently fell back to CLOSE_IDLE).

Adds CLOSE_DURATION event close mode handling (previously silently fell back to CLOSE_IDLE).

Source: llm_adapter@2026-05-28

Confidence: high

β€”
Feature Medium

Defaults ffmpeg decoder thread_count to 2, roughly halving software 1080p H.264 decode time (performance improvement).

Defaults ffmpeg decoder thread_count to 2, roughly halving software 1080p H.264 decode time (performance improvement).

Source: llm_adapter@2026-05-28

Confidence: low

β€”
Feature Low

Stops NotAuthorized loop by cleaning up ONVIF subscription before re‑Subscribe (subscription cleanup).

Stops NotAuthorized loop by cleaning up ONVIF subscription before re‑Subscribe (subscription cleanup).

Source: granite4.1:30b@2026-05-28-audit

Confidence: low

β€”
Feature Low

Adds credential fallback chain: ONVIF_Username/Password β†’ Monitor User/Pass when ControlAddress lacks auth.

Adds credential fallback chain: ONVIF_Username/Password β†’ Monitor User/Pass when ControlAddress lacks auth.

Source: granite4.1:30b@2026-05-28-audit

Confidence: low

β€”
Feature Low

Triggers reconnect when ffmpegCamera detects >10β€―s DTS rollback, ending warning storms after stream restarts (DTS backward jump detection).

Triggers reconnect when ffmpegCamera detects >10β€―s DTS rollback, ending warning storms after stream restarts (DTS backward jump detection).

Source: granite4.1:30b@2026-05-28-audit

Confidence: low

β€”
Feature Low

Adds missing dash separator to Content‑Range header.

Adds missing dash separator to Content‑Range header.

Source: granite4.1:30b@2026-05-28-audit

Confidence: low

β€”
Dependency Medium

Bumps `firebase/php-jwt` from 6.0.0 to 7.0.0 (runtime dependency update).

Bumps `firebase/php-jwt` from 6.0.0 to 7.0.0 (runtime dependency update).

Source: llm_adapter@2026-05-28

Confidence: high

β€”
Bugfix High

Fixes VideoStore use‑after‑free by holding filename by value and adding null guard (refs #4757).

Fixes VideoStore use‑after‑free by holding filename by value and adding null guard (refs #4757).

Source: llm_adapter@2026-05-28

Confidence: high

β€”
Bugfix Medium

Derives Event end time from StartDateTime + Length when EndDateTime is missing, preventing montage review crashes.

Derives Event end time from StartDateTime + Length when EndDateTime is missing, preventing montage review crashes.

Source: llm_adapter@2026-05-28

Confidence: high

β€”
Bugfix Medium

Restricts image.php proxy URL scheme to http/https only, preventing SSRF.

Restricts image.php proxy URL scheme to http/https only, preventing SSRF.

Source: granite4.1:30b@2026-05-28-audit

Confidence: low

β€”
Bugfix Medium

Hardens onvifprobe.php, zmvideo.pl invocation, MonitorsController zmu calls, HostController du, event export, and download/export pipelines with escapeshellarg() and input sanitization (command injection fixes).

Hardens onvifprobe.php, zmvideo.pl invocation, MonitorsController zmu calls, HostController du, event export, and download/export pipelines with escapeshellarg() and input sanitization (command injection fixes).

Source: granite4.1:30b@2026-05-28-audit

Confidence: low

β€”
Bugfix Medium

Adds monitor mutex to block shared‑memory access during loadMonitor() remap; bails early when monitor fails to load, preventing MonitorStream segfault.

Adds monitor mutex to block shared‑memory access during loadMonitor() remap; bails early when monitor fails to load, preventing MonitorStream segfault.

Source: granite4.1:30b@2026-05-28-audit

Confidence: low

β€”
Bugfix Medium

Fixes MonitorLink file‑descriptor leak by calling disconnect() before connect(), avoiding "Too many open files" errors.

Fixes MonitorLink file‑descriptor leak by calling disconnect() before connect(), avoiding "Too many open files" errors.

Source: granite4.1:30b@2026-05-28-audit

Confidence: low

β€”
Bugfix Medium

Redirects stdin/stdout/stderr to /dev/null during daemonization instead of closing them, avoiding memory corruption from libx264 writing into reused FDs.

Redirects stdin/stdout/stderr to /dev/null during daemonization instead of closing them, avoiding memory corruption from libx264 writing into reused FDs.

Source: granite4.1:30b@2026-05-28-audit

Confidence: low

β€”
Bugfix Medium

Removes write‑index guard that caused rapid Pause/Play cycling and ~2 empty events per second in ONDEMAND mode.

Removes write‑index guard that caused rapid Pause/Play cycling and ~2 empty events per second in ONDEMAND mode.

Source: granite4.1:30b@2026-05-28-audit

Confidence: low

β€”
Bugfix Medium

Replaces SOAP_STRINGS[] array with a switch covering known gsoap codes, fixing OOB read on negative codes (GHSA-4842).

Replaces SOAP_STRINGS[] array with a switch covering known gsoap codes, fixing OOB read on negative codes (GHSA-4842).

Source: granite4.1:30b@2026-05-28-audit

Confidence: low

β€”
Bugfix Medium

Synthesizes monotonic DTS for AV_NOPTS_VALUE in VideoStore, preventing reorder queue and audio first‑DTS comparison issues (AV_NOPTS_VALUE handling).

Synthesizes monotonic DTS for AV_NOPTS_VALUE in VideoStore, preventing reorder queue and audio first‑DTS comparison issues (AV_NOPTS_VALUE handling).

Source: granite4.1:30b@2026-05-28-audit

Confidence: low

β€”
Bugfix Medium

Loads incomplete.mp4 files in C++ handler, view_video.php, and image.php; fixes mp4 export end‑timestamp for multi‑event downloads (closes #4767, #4774).

Loads incomplete.mp4 files in C++ handler, view_video.php, and image.php; fixes mp4 export end‑timestamp for multi‑event downloads (closes #4767, #4774).

Source: granite4.1:30b@2026-05-28-audit

Confidence: low

β€”
Bugfix Low

Prevents segfault when MPEG stream codec fails to open.

Prevents segfault when MPEG stream codec fails to open.

Source: granite4.1:30b@2026-05-28-audit

Confidence: low

β€”
Bugfix Low

Cleans up video_out_ctx, codec, and hw_device contexts on setup_hwaccel failure; skips flush_codecs when no frames were sent (memory‑leak fixes).

Cleans up video_out_ctx, codec, and hw_device contexts on setup_hwaccel failure; skips flush_codecs when no frames were sent (memory‑leak fixes).

Source: granite4.1:30b@2026-05-28-audit

Confidence: low

β€”
Bugfix Low

Adds null‑pointer guards for StorageId in Monitor::Load, ModelId in Monitor::Model(), evtStream element, monitor div in video-stream.js.

Adds null‑pointer guards for StorageId in Monitor::Load, ModelId in Monitor::Model(), evtStream element, monitor div in video-stream.js.

Source: granite4.1:30b@2026-05-28-audit

Confidence: low

β€”
Bugfix Low

Correctly parses HTTP Range header forms `bytes=start-end`, `bytes=start-`, and `bytes=-suffix`, fixing ERR_CONTENT_LENGTH_MISMATCH on HEVC playback (Range header parsing).

Correctly parses HTTP Range header forms `bytes=start-end`, `bytes=start-`, and `bytes=-suffix`, fixing ERR_CONTENT_LENGTH_MISMATCH on HEVC playback (Range header parsing).

Source: granite4.1:30b@2026-05-28-audit

Confidence: low

β€”
Full changelog

ZoneMinder 1.38.3 Release Notes

This is a maintenance release that includes security fixes, crash and memory-safety fixes, and a wide range of bug fixes for ZoneMinder 1.38.

Note: 1.38.3 supersedes 1.38.2. The 1.38.2 tag was created but its
Debian package builds hit a cross-distro orig.tar.gz upload conflict
(each distro built its own non-deterministic upstream tarball; mini-dinstall
marked most distros as "incomplete"). Switching the Debian source format
to 3.0 (native) for 1.38.3 eliminates the shared-orig requirement entirely.
Apart from the source format and a few CI fixes, the content is identical
to what 1.38.2 would have shipped.

Key Highlights

πŸ”’ Security & Hardening

  • API privilege escalation (RCE) - Added System=Edit RBAC checks to ConfigsController edit/delete and hardened ffmpeg path handling in Event.php
  • GHSA-g66m-77fq-79v9 - Sanitized monitor Device path to prevent command injection
  • GHSA-745h-vg7c-73cg - Escaped URLs in camera probe wget() to prevent command injection
  • eval()-based RCE in filters - Replaced eval() in FilterTerm SystemLoad/DiskPercent/DiskBlocks evaluation with a safe compare() method and operator allowlist
  • Command injection - Hardened onvifprobe.php, zmvideo.pl invocation, MonitorsController zmu calls, HostController du, event export, and download/export pipelines with escapeshellarg() and input sanitization
  • SQL injection - Validated FilterTerm operators and collate, intval'd AlarmedZoneId/monitor IDs, restricted getFormChanges column keys, dbEscape'd MIME fields
  • SSRF - Restricted image.php proxy URL scheme to http/https only
  • XSS sanitization series - Removed reflected user input from add_monitors, device, event, events, log, and filterdebug AJAX sinks; sanitized export filename/connkey at the AJAX boundary; suppressed raw SQL error text in events ajax responses
  • Auth hash bypass with reverse proxy - HTTP_X_FORWARDED_FOR is now used consistently in both PHP and C++ auth-hash validation when AUTH_HASH_IPS is enabled
  • Reverse-proxy username handling - Auth relay now carries the username so zms validates against the indexed Username column instead of brute-iterating users
  • Bumped firebase/php-jwt from 6.0.0 to 7.0.0

πŸ› Crash & Memory Safety

  • VideoStore use-after-free - filename is now held by value; destructor guards against null oc when open() bailed early (refs #4757)
  • MonitorStream segfault - Added monitor mutex to block shared-memory access during loadMonitor() remap; bail early when monitor failed to load
  • MPEG stream codec failure - Prevented segfault when the codec fails to open
  • MonitorLink fd leak - connect() now calls disconnect() first, avoiding "Too many open files" under repeated reconnect attempts
  • VideoStore fd/codec leaks - Free video_out_ctx after failed avcodec_open2; clean up codec/hw_device contexts on setup_hwaccel failure; skip flush_codecs when no frames were sent
  • Daemon FD reuse memory corruption - Daemonization now redirects stdin/stdout/stderr to /dev/null instead of closing them, avoiding libx264 writing into a reused stderr FD
  • Null pointer guards - StorageId in Monitor::Load, ModelId in Monitor::Model(), evtStream element, monitor div in video-stream.js
  • Empty events in ONDEMAND mode - Removed write-index guard that caused rapid Pause/Play cycling and ~2 empty events per second

🎯 ONVIF Improvements

  • Subscription cleanup before re-Subscribe - Stops the NotAuthorized loop caused by leaked pull-point slots on Hikvision/Reolink cameras
  • OOB read on negative gsoap codes - Replaced SOAP_STRINGS[] array with a switch covering the codes seen in practice (SOAP_EOF, network/SSL block, SOAP_STOP) (fixes #4842)
  • Credential fallback chain - Falls back to Monitor ONVIF_Username/Password then Monitor User/Pass when ControlAddress lacks auth
  • 'Use ONVIF' badge - Only displayed on the console when the listener is actually enabled

πŸŽ₯ Recording & Playback Correctness

  • AV_NOPTS_VALUE handling in VideoStore - Synthesizes monotonic DTS for video passthrough; reorder queue and audio first-DTS no longer compare AV_NOPTS_VALUE sentinels
  • DTS backward jump detection - FfmpegCamera now triggers reconnect on >10s DTS rollback, ending the minutes-long warning storm after stream restarts
  • Event end time - Derived from StartDateTime + Length when EndDateTime is missing (crashed/killed zmc), so montage review no longer paints a bar across hours of down-time
  • incomplete.mp4 playback - C++ handler, view_video.php, and image.php now load incomplete event files; mp4 export end-timestamp fixed for multi-event downloads (closes #4767, #4774)
  • HTTP Range header parsing - Correctly handles bytes=start-end, bytes=start-, and bytes=-suffix; fixes ERR_CONTENT_LENGTH_MISMATCH on HEVC mp4 playback in Chrome (fixes #4777)
  • Content-Range header - Added missing dash separator
  • Event::delete deadlock retry - Wraps the delete transaction in READ COMMITTED with rollback + retry on errno 1213 instead of committing through the deadlock
  • Polygon fill - Scan-line fill now steps active edges by pairs, fixing non-convex zones whose concave gaps were incorrectly marked inside the zone
  • Polygon clamp - Percentage polygons clamp to width-1/height-1, stopping spurious "polygon hi_x >= image width" warnings
  • Zone stats percentage - Convert Area from percentage coordinate space (0-10000) to pixel area before calculating percentages
  • alarm_frame_count in ready_count - Stops "Hit end of packetqueue before satisfying pre_event_count" warnings when pre_event_count is 0 but alarm_frame_count is set

πŸ” Authentication & Permissions

  • Role-based stream access - The C++ User class is now role-aware and consults Role_Monitors_Permissions, Role_Groups_Permissions, and User_Roles base permissions, matching the PHP visibleMonitor() logic
  • Stale auth hash on long-running montage - MonitorStream.js refreshes the auth hash before restarting streams on tab visibility change and after the idle modal
  • Auth relay - Username included in relay; empty auth_relay no longer produces double && in zms URLs; warns on user mismatch instead of silently falling back
  • Trigger reliability - zmTriggerEventOn now writes trigger_state last, fixing a ~1/3 trigger acceptance rate

πŸ—οΈ Build & PHP 8

  • Debian source format β†’ 3.0 (native) - The 1.38 line now ships native source packages instead of 3.0 (quilt). Eliminates the cross-distro orig.tar.gz hash conflict on the build farm; each distro produces a self-contained zoneminder_<VERSION>-<DISTRO>.tar.gz. Invisible to apt/dpkg consumers β€” affects only the source package layout in the repository.
  • a2enmod rewrite - Debian/Ubuntu postinst now enables mod_rewrite (was incorrectly calling a2enmod cgi), fixing install failures
  • PHP 7 polyfill - str_starts_with/str_ends_with/str_contains polyfilled at the top of functions.php so PHP 7 installs don't fatal on event.php
  • PHP 8 GD handling - imagedestroy() now called conditionally; GDImage memory release forced on PHP >= 8.0
  • www-data video group - Debian/Ubuntu postinst adds www-data to video and dialout groups so zmc can open /dev/video* on fresh installs (refs #4642)
  • FreeBSD support - Top command parsing fixed for FreeBSD 13.5; /proc/meminfo presence check; removed unnecessary kFreeBSD arch checks; FreeBSD arm build fixes
  • ZM_NO_CURL=ON build - Added HAVE_LIBCURL preprocessor guards throughout zm_monitor.h/cpp and zmc.cpp

Detailed Changes

Core Engine

  • Default ffmpeg decoder thread_count to 2 (roughly halves software 1080p H.264 decode time)
  • Flush decoder_queue on decoder thread exit to avoid stale latency offset across reconnect
  • Reset last_write_index in Pause() to restore DECODING_ONDEMAND bootstrap
  • Added CLOSE_DURATION event close mode handling (was silently falling back to CLOSE_IDLE)
  • Event-id latch on linked monitor score detection so brief alarm cycles between analysis ticks are no longer missed
  • Reduced linked monitor reconnect throttle from 60s to 1s
  • Used +1 instead of +last_duration for equal-DTS fixup

Camera Support

  • FFmpeg: Use CxxUrl for credential injection (replaces fragile substr-based string manipulation)
  • Reolink: Handle HTTP-to-HTTPS 302 redirect during login (LWP::UserAgent does not follow redirects on POST)
  • V4L2: Treat ENOTTY like EINVAL when querying JPEG compression options (some drivers return ENOTTY)
  • Added SSL certificate verification fallback to the base Control class β€” retries with verification disabled on SSL errors, applies to get/put/post

Web Interface & API

  • pushState URL state management on the events page so filter state is shareable and browser back/forward restores it
  • Mobile layout fixes for monitor config and watch views (CSS specificity on :first-child)
  • Don't blank the screen between events when animations are off
  • Alarm/idle border applied only to .imageFeed, not nested img/video elements β€” fixes extra borders and streams jumping around
  • Stream URL state: include port in Server URL methods for port-forwarded setups (fixes #4675); honor explicit port argument in urlToApi single-server case; urlToApi falls back to location.host
  • Thumbnail overlay scale calculated from actual monitor dimensions instead of hardcoded scale=75
  • Montage review playback smoothness, fractional-seconds preserved through mmove/setSpeed, video seek overshoot fix on initial AVSEEK_FLAG_FRAME
  • montagereview cookie stores speed value instead of index; changeFilters guards against NaN from invalid date input
  • getTracksFromStream moved to skin.js so it can also be used on recorded events; added Go2RTC variant; restart MSE thread on any appendMseBuffer error (not just QuotaExceededError)
  • VolumeSlider: noUiSlider properly destroyed when switching player or monitor; #volumeControls now always includes the monitor ID
  • Event page: VID vs MJPEG playClicked/pauseClicked separation; toggleZones listener assigned after Event page loads
  • MSE addSourceBuffer guarded against detached MediaSource when WebRTC wins the race in video-rtc.js
  • RTSP2Web RTC errors restart the stream instead of killing it
  • z-index ordering for zones SVG overlay corrected (cannot exceed .zonePoint index)
  • Sort events by Tags column alias rather than T.Name (which is out of scope in the aggregated query)
  • navbar_type now saved in cookies
  • Move tag-related commands to canView(Events) instead of canEdit(Events); add Create to canEdit
  • Filter debug modal: strip SKIP LOCKED from EXPLAIN so MySQL accepts the query
  • Don't add postLoginQuery to the URL when empty
  • Fixed image.php .$file concatenation error
  • Silenced noisy isJSON() parse-failure console.warn β€” every getCookie() call for a plain-string cookie ('visible', 'hidden', etc.) was logging an "Unexpected token" SyntaxError to the browser console

Database & Filters

  • Filter::Sql() now clears accumulated state (PostSQLConditions, HasDiskPercent, HasDiskBlocks, HasSystemLoad) before rebuilding β€” previously grew unboundedly
  • Filter::Execute uses prepare instead of prepare_cached since the SQL changes every cycle (the cache never hit; entries leaked one per distinct substituted value)
  • zmfilter uses the minimum per-filter delay instead of the last filter's delay, so no filter oversleeps its ExecuteInterval; overdue warning now uses the unclamped delay and includes the filter name and ExecuteInterval value
  • Fixed Filter <name> is taking N seconds longer than execute interval warning printing huge bogus numbers (~56 years) on a filter's first iteration β€” elapsed was computed against epoch 0 instead of now
  • Handle PostSQLConditions being an empty array; don't Fatal on SQL prepare errors
  • zmDbDo error logging substitutes all SQL placeholders (was only substituting the first)
  • Log bind params correctly when SQL contains literal % characters

Configuration & Logging

  • Increased potential config line size β€” HTML snippets can easily exceed 256 bytes
  • Enriched zms auth-failure warning with diagnostic fields to distinguish stale hash, missing auth, disabled user, and IP mismatch
  • Removed Warn() in favour of Warning() (fixes #4724)
  • Log commands used when updating the database

Scripts & Tools

  • zmcontrol/Control.pm: Base class SSL fallback applied across get/put/post
  • zmtelemetry/zmu: Daniel Caujolle-Bert refactors using ZoneMinder::Config, ZM_PATH_UNAME, lc()/chomp()/qx, eq instead of ==
  • a2enmod: Postinst fix from cgi to rewrite

Email

  • QP-encode plain-text email body so URLs with %EP%/%EPS%/%EPI% substitution tags survive transit (mail clients had been QP-decoding =NN digit pairs)

Miscellaneous

  • Advanced RtspServer pin to a0715995 (correct RTP marker bit from frame.last; cmake_minimum_required to 3.10)
  • Don't pass null as the first parameter to strtotime() (PHP 8.1 deprecation)
  • Added ZoneMinder.spec from the OBS project

Platform-Specific Changes

FreeBSD

  • Fixed top command parsing (tested on FreeBSD 13.5)
  • Check for /proc/meminfo before reading (does not exist on FreeBSD)
  • Removed unnecessary kFreeBSD arch checks (was amd64/i386 only)
  • FreeBSD arm build fixes

CI/CD

  • Switched Debian source format to 3.0 (native) (eliminates cross-distro orig.tar.gz upload conflicts)
  • Tag-push workflows now derive the rsync deploy target from the X.Y minor version (so 1.38.3 lands in the same release-1.38/ dist as future point releases)
  • Fixed bash-specific [[ ]] syntax under container sh -e shells; use the locally-checked-out do_debian_package.sh instead of curl from refs/heads/<tag> (404s on tag refs)
  • Updated deb and aarch64 deb package workflows for the release-1.38 branch
  • Renamed proposed rsync target to proposed-1.38
  • Use -r=<tag> for release builds instead of -s=CURRENT
  • Dynamic branch and deploy targets in deb package workflows
  • RPM workflows fire on release-1.38 and derive deploy target from ref
  • Package workflows now also fire on tag pushes so stable repo deploys actually run
  • ESLint workflow updated to v9 for flat config support
  • ESLint config synced to ESLint 9 flat config format
  • Bumped GitHub Actions: download-artifact v8, upload-artifact v7, crazy-max/ghaction-import-gpg v7

Upgrade Notes

  1. Reverse-proxy users with AUTH_HASH_IPS enabled: Authentication now consults HTTP_X_FORWARDED_FOR (falling back to REMOTE_ADDR), so the per-monitor auth hash matches across PHP and C++ when ZoneMinder sits behind a proxy. If you carried custom ZoneMinder-side workarounds for this (e.g. disabling AUTH_HASH_IPS, custom auth_relay code), those can now be removed. Apache/nginx-side RemoteIPHeader / proxy_set_header configuration should stay as-is.
  2. Role-based permissions: Users who receive camera permissions via Roles will now correctly get live stream access through zms β€” previously the C++ User class only checked direct user permissions.
  3. PHP 8 users: GDImage memory handling is now PHP 8-aware. No action needed, but resource usage should be lower.
  4. Debian/Ubuntu fresh installs: postinst now adds www-data to the video group and runs a2enmod rewrite (not cgi). Existing installs may want to verify these manually.
  5. ONVIF subscription leaks: If cameras were previously stuck in NotAuthorized loops after a few hours, this should now self-recover without a zmc restart.

Contributors

This release includes contributions from the ZoneMinder development team and community members who reported bugs, tested fixes, and provided feedback.


Full Changelog: https://github.com/ZoneMinder/zoneminder/compare/1.38.1...1.38.3

Security Fixes

  • GHSA-g66m-77fq-79v9 – Sanitized monitor Device path to prevent command injection
  • GHSA-745h-vg7c-73cg – Escaped URLs in camera probe wget() to prevent command injection
  • API privilege escalation (RCE) – Added System=Edit RBAC checks to ConfigsController edit/delete and hardened ffmpeg path handling in Event.php
  • eval()-based RCE in filters – Replaced eval() with safe compare() method and operator allowlist
  • Multiple command‑injection mitigations across onvifprobe.php, zmvideo.pl, MonitorsController zmu calls, HostController du, event export/download pipelines using escapeshellarg() and sanitization
  • SQL injection fixes – Validated FilterTerm operators/collate, intval'd IDs, restricted getFormChanges keys, dbEscape'd MIME fields
  • SSRF mitigation – Restricted image.php proxy URL scheme to http/https only
  • XSS sanitization series – Removed reflected user input from several AJAX sinks and sanitized export filename/connkey at the boundary

Weekly OSS security release digest.

The CVE patches and breaking changes that affected production tools this week. One email, every Sunday.

No spam, unsubscribe anytime.

Share this release

Track zoneminder

Get notified when new releases ship.

Sign up free

About zoneminder

ZoneMinder is a free, open source Closed-circuit television software application developed for Linux which supports IP, USB and Analog cameras.

All releases β†’

Related context

Earlier breaking changes

  • v1.38.2 Makes the C++ User class role‑aware, consulting Role_Monitors_Permissions and related tables for live‑stream access.

Beta — feedback welcome: [email protected]