Commit Graph
12 Commits
Author SHA1 Message Date
Isaac ConnorandClaude Opus 5.5 c3d2e542bc fix: never build an object from an id the request sent as an array
ZM_Object's constructor treats an array as the object's row, and validInt()
and validCardinal() returned an array as they got it (preg_replace() does).
So wherever a request id reached `new ZM\X(...)`, raw or through those
validators, a crafted array built an object whose Id, UserId or MonitorId
the sender chose. The permission check then looked at those, and the delete
or update that followed used the Id. A user with Events=View deleted another
user's filter by posting Id[Id]=<filter>&Id[UserId]=<self> to the filter
delete action; the same shape reached events (MonitorId), users, reports,
snapshots and storage.

validInt() and validCardinal() now give '' for anything that isn't a single
value. The places that built an object from a raw $_REQUEST value (the
control and event ajax handlers, the event, filter, report, snapshot and
storage actions, view_hls, and the download and event detail modals) take
the id through validCardinal().

tests/php/test_request_ids_scalar.php covers the validators with arrays and
fails if web code builds an object straight from $_REQUEST, $_GET or $_POST.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-11 23:07:08 -04:00
IgorA100 afb81fe720 Write "is not" instead of "has not" (view_hls.php) 2026-06-05 12:13:03 +03:00
IgorA100 c73463ad63 Check the response of file_get_contents() for "false" (view_hls.php) 2026-06-04 14:44:02 +03:00
IgorA100 cb242008b4 Merge branch 'master' into patch-180448 2026-06-03 23:37:58 +03:00
Isaac ConnorandClaude Opus 4.7 ad1e9c23a6 fix: enforce per-event ACL on direct media endpoints (GHSA-vj5r-pc2v-gfwv)
image.php, view_video.php and view_hls.php previously checked only the
coarse canView('Events') / canView('Snapshots') role before streaming
media for a user-supplied event id. An authenticated user denied access
to a monitor could still fetch event snapshots, captured frames,
recorded MP4s and HLS manifests for events belonging to that monitor by
calling the direct endpoints with the event id.

Call Event->canView() after loading the event and return 404 on denial
so the event id cannot be enumerated. view_video also validates
Event->Id() so unknown ids return 404 instead of an empty 200 body.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-06-03 08:45:20 -04:00
IgorA100 964ea784fc - Do not check the M3u8 file if currentView === 'frames'
- When executing Length() and Duration() , do not write the result to the database.
- When assigning a value to the "data-video-duration-secs" attribute, first check the Length in $row['Length'] and, if it is 0, only then check the file's Duration.
- If the M3u8 file is missing or invalid, do not cache the server response.
2026-05-26 19:23:59 +03:00
IgorA100andCopilot Autofix powered by AI 0ff920ec07 Apply suggestions from code review
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
2026-05-26 16:58:19 +03:00
IgorA100 fa1da1b912 If the m3u8 manifest doesn't contain fragments, then instead of outputting to the page body, save the message in the logs as Warning, since we're passing status code 204. (view_hls.php)
If the manifest is m3u8, then we don't write anything in the logs; we simply pass status code 204.
2026-05-22 12:57:26 +03:00
IgorA100 af6011880d If the "index.m3u8" manifest file is missing or if the manifest is invalid, the return status code is 204 instead of 404. (view_hls.php)
This will avoid errors in the browser console and PHP logs.
2026-05-21 18:53:09 +03:00
Isaac ConnorandClaude Opus 4.7 275b675ac8 fix: stop view_hls.php emitting /zm/index.php?index.php?view=… refs #4757
The segment-URL rewrite captured "index.php?…" in $1 and then prepended
$base_url . '?', producing a double-prefixed URL that drops the view=
routing parameter. Players fetched the manifest itself instead of the
mp4 byte range, so Firefox showed a spinner and Chrome silently failed.

Capture only the query string (everything after "index.php?") for both
the bare segment URLs and the EXT-X-MAP URI rewrite.

Same fix that landed (unmerged) in PR #4803 and PR #4806; pulled in here
so the full HLS path can be reviewed and merged together.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-16 11:21:21 -04:00
Isaac ConnorandClaude Opus 4.6 3f8bc81760 fix: HLS fragment tracking, live playback, and fallback handling
Fragment tracking:
- Rewrite detection to use avio_flush()+avio_tell() before each video
  keyframe write, giving exact fragment boundaries. Fixes duplicate
  entries and missing first fragment in the m3u8 manifest.
- Remove unused pending_fragment_dts_ and last_flush_pos_ members.

Live event playback:
- Set DefaultVideo to index.m3u8 at event INSERT time (no DB update
  needed after videoStore opens)
- Rename incomplete file to include codec (incomplete.h264.mp4) so
  canPlayCodec() works during recording
- Rewrite final m3u8 after video file rename to reference final name
- Add live retry handler in event.php: retries all error codes with
  backoff (3s then 5s), max 30 retries, resets on successful playback
- Update progress bar duration from video element for live events

Fallback handling:
- view_video.php: when DefaultVideo is m3u8 and no file param given,
  search event dir for actual mp4 (final name then incomplete)
- view_hls.php: reject m3u8 with no EXTINF entries (event just started)
- event.php: require m3u8 file to exist on disk before offering HLS,
  fall back to direct MP4 otherwise

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-14 20:57:46 -04:00
Isaac ConnorandClaude Opus 4.6 e15d2a6427 feat: add HLS byte-range playback for event video
Write a single continuous fragmented MP4 per event and generate an HLS
m3u8 manifest with byte-range references into that file. This enables
seamless browser playback via video.js's built-in http-streaming (VHS)
without needing separate segment files.

C++ changes:
- VideoStore tracks fragment boundaries (moof+mdat byte offsets and
  durations) as packets are written, by monitoring avio_tell() around
  keyframe writes in write_packet()
- Add writeM3U8() method that generates EXT-X-VERSION:7 byte-range
  manifests with EXT-X-MAP for the init segment
- Event writes a live m3u8 (no EXT-X-ENDLIST) on each new fragment
  for in-progress viewing, and a final VOD manifest at event close
- Change movflags to frag_keyframe+empty_moov+default_base_moof
  (default_base_moof required by HLS fMP4 spec, faststart removed
  as it's meaningless with empty_moov)

PHP/web changes:
- New view_hls.php endpoint serves pre-built m3u8 with auth tokens
- event.php detects index.m3u8 and uses HLS as primary source with
  direct MP4 as fallback
- CSRF exemption for view_hls in index.php

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-12 15:40:12 -04:00