FilterTerm.php:
- Use intval() on AlarmedZoneId value in SQL subquery to prevent
injection via crafted filter val
report_event_audit.php, montagereview.php:
- Cast $selected_monitor_ids through array_map('intval') before
interpolating into SQL IN clause (values come from $_REQUEST)
download_functions.php:
- Replace manual single-quoting with escapeshellarg() for merged
file name in ffmpeg, tar, and zip commands (monitor names can
contain shell metacharacters including single quotes)
- Same fix for export list file path
export_functions.php:
- Use escapeshellarg() on source and destination paths in cp -as
commands during event export
functions.php:
- Validate column keys in getFormChanges() against /^[a-zA-Z0-9_]+$/
to prevent SQL injection via crafted array keys from $_REQUEST
- Use dbEscape() and intval() for image/document MIME type and size
fields instead of raw string interpolation
- Replace escapeshellcmd() with escapeshellarg() in deletePath()
rm -rf command
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The zone loader now ignores the Units DB field and detects the coordinate
format by checking for decimal points: decimal values are percentages,
integer-only values are legacy pixels. This fixes motion detection being
broken when zones had Units=Pixels but percentage coordinates (or vice
versa), which resulted in a ~99x99 pixel zone on a 2560x1440 monitor.
The PHP zone view now always forces Units=Percent when saving, since it
always works in percentage space. convertPixelPointsToPercent() now
returns bool to indicate whether conversion occurred.
Tests added for: truncation bug via atoi, correct percentage-to-pixel
conversion, auto-detect heuristic, and resolution independence.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The Device field from the Monitors table was interpolated directly into
shell commands (qx(), backticks, exec()) without sanitization, allowing
authenticated users with monitor-edit permissions to execute arbitrary
commands as www-data via the Device Path field.
Defense in depth:
- Input validation: reject Device values not matching /^\/dev\/[\w\/.\-]+$/
at save time in both web UI and REST API
- Output sanitization: use escapeshellarg() in PHP and quote validated
values in Perl at every shell execution point
Affected locations:
- scripts/ZoneMinder/lib/ZoneMinder/Monitor.pm (control, zmcControl)
- scripts/zmpkg.pl.in (system startup)
- web/includes/Monitor.php (zmcControl)
- web/includes/functions.php (zmcStatus, zmcCheck, validDevicePath)
- web/includes/actions/monitor.php (save action)
- web/api/app/Model/Monitor.php (daemonControl, validation rules)
- web/api/app/Controller/MonitorsController.php (daemonStatus)
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
When zone coordinates are stored as pixel values (e.g. from a missed DB
migration), the web layer now detects values > 100 and converts them to
percentages using the monitor's dimensions, mirroring the existing C++
detection logic in zm_zone.cpp. This prevents limitPoints() from clamping
pixel values to 0-100 and zones rendering incorrectly in SVG overlays.
- Add convertPixelPointsToPercent() helper in functions.php
- Call conversion before limitPoints() in zone.php and zones.php
- Update Zone::svg_polygon() to accept monitor dimensions and convert
- Pass ViewWidth/ViewHeight to svg_polygon() from Monitor::getStreamHTML()
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Convert zone coordinates from absolute pixel values to percentages
(0.00-100.00) so zones automatically adapt when monitor resolution
changes. This eliminates the need to manually reconfigure zones after
resolution adjustments.
Changes:
- Add DB migration (zm_update-1.37.81.sql) to convert existing pixel
coords to percentages, recalculate area, and update Units default
- Add Zone::ParsePercentagePolygon() in C++ to parse percentage coords
and convert to pixels at runtime using monitor dimensions
- Backwards compat: C++ Zone::Load() checks Units column and uses old
pixel parser for legacy 'Pixels' zones
- Update PHP coordsToPoints/mapCoords/getPolyArea for float coords,
replace scanline area algorithm with shoelace formula
- Update JS zone editor to work in percentage coordinate space with
SVG viewBox "0 0 100 100" and non-scaling-stroke for consistent
line thickness
- Position zone SVG overlay inside imageFeed container via JS to align
with image only (not status bar)
- Support array of zone IDs in Monitor::getStreamHTML zones option
- Update monitor resize handler: percentage coords don't need rescaling,
only threshold pixel counts are adjusted
- Add 8 Catch2 unit tests for ParsePercentagePolygon
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
When adding cameras via ONVIF probe, passwords containing special
characters (like parentheses, slashes, etc.) were being stored in the
database in URL-encoded form instead of plain text. This caused
authentication failures when the encoded password was used.
The issue was in extract_auth_values_from_url() which extracted
credentials from the stream URI but didn't decode them. Since the ONVIF
probe process double-encodes passwords (to survive POST encoding), and
monitor.php decodes once, the extracted password still remained
URL-encoded.
The fix adds urldecode() to both username and password after extraction,
ensuring they're stored in their original form in the database.
Example: Password "pass)word" was being stored as "pass%29word"