Commit Graph
94 Commits
Author SHA1 Message Date
Nic Boet 35a8daf99e perf: emulate prepares for ZoneMinder's own queries on PHP 8.1+
dbConnect() turns emulated prepares off, so every query of the web UI, the
ajax calls and the includes the API runs (config, servers, users,
permissions) is a server-side prepare: prepare, execute and close, three
round trips to the database where one would do. From PHP 8.1, pdo_mysql
returns integers and floats as PHP ints and floats with emulated prepares
too, so values keep the types they have now. Emulate prepares there and keep
server-side prepares on older PHP, where every value would become a string.

Emulated prepares quote parameters bound as strings, which breaks LIMIT ?.
dbQuery() now binds integer values as integers (everything else as strings,
as execute() did), and the log and watch ajax calls cast their validated
offset and limit to int. With emulation, PDO would also run several
statements given in one string; MYSQL_ATTR_MULTI_STATEMENTS is turned off so
that is refused as before. CakePHP's own connection is not changed: it
already uses PDO's default, emulated prepares.

tests/php/test_database_emulated_prepares.php compares every column of every
table read through both kinds of prepare (types and values), checks
aggregates, LIMIT with positional and named integers, string and null
parameters, and that two statements in one string are refused.
2026-10-14 22:41:42 -05:00
IgorA100 c6b77d44d7 Preserve non-ASCII characters in log messages (#5188) 2026-10-06 08:56:35 -04:00
Isaac ConnorandClaude Opus 5 1be3378faa refactor: extract the log toolbar and table into a reusable panel refs #5147
The Log view was the only way to read a log, and its toolbar, its
bootstrap-table and the script behind them were written into the view, so
a second view could only have a log by copying all three.

Move the markup to skins/classic/includes/logpanel.php as
getLogPanelHTML(), and the behaviour to skins/classic/js/logpanel.js,
which picks up every .logPanel in the page and drives it. The script
keeps its request state, its selection and its in-flight XHR in a
closure, so two panels on one page no longer share them; the table gets
its ajax function from that closure instead of the global that
data-ajax="ajaxRequest" resolved to. Options cover what the call sites
differ on: the element id prefix, a fixed set of components, the page
size and whether the Back and Refresh page buttons are shown.

A panel locked to components sends embedded=1, and ajax/log.php then
leaves zmLogComponent and zmLogFilterLevel alone, so an embedded panel
cannot overwrite what the user chose in the Log view. Such a panel also
drops the component selector and column, and the unfiltered total, which
counts every log row and means nothing once the panel is scoped.

The level colours and the table's column rules move from the per-view
log.css files to the matching skin.css, since they now apply wherever a
panel is embedded.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-18 19:04:04 -04:00
Isaac Connor 56030724ed fix: stop the js log endpoint 500ing on a Firefox line:column location
Firefox reports an error location as "line:column", for example 1500:28.
ajax/log.php passed that through validInt(), which is

  preg_replace('/[^\-\d]/', '', $input)

so it only removes the colon and splices the two numbers into 150028.
Logs.Line is smallint(5) unsigned, max 65535, so the insert died with

  PDOException SQLSTATE[22003]: Out of range value for column 'Line'
    includes/logger.php:438 <- ajax/log.php:81

and the request returned 500.  The endpoint failed on the very error it
was sent to report, so a js error surfaced as "AJAX execution error:
Internal Server Error [500]" instead of a log entry.  Any Firefox error
past roughly line 655 hits this, on any view.

Parse the leading line number instead and clamp it to what the column
holds.  validInt() is left alone; it is used widely and its callers rely
on the strip-everything behaviour, the defect is handing it a line:column
string.

Checked with an assert script over 1500:28, 1789:8, a plain 1500, an
over-range 70000 and 99999:1, empty, non-numeric, and array input; the
first three give the line, the next two clamp to 65535, the rest stay NULL.
2026-08-27 19:54:42 -04:00
Isaac ConnorandClaude Opus 4.8 66299ae7cf feat: make log Level and Component filters multi-select
Convert the Level and Component filter dropdowns on the system log view
into Chosen multi-selects so several values can be filtered at once. An
empty selection means "All" (shown via the placeholder), replacing the
former explicit All option.

- log.php: emit filterLevel[]/filterComponent[] with multiple + placeholder,
  read the remembered selection as an array (tolerating the legacy scalar),
  and drop the leftover ZM\Debug dumps of the component list
- js/log.js: send level/Component as arrays when non-empty; drop the
  redundant filterComponent change binding now that data-on-change wires it
- ajax/log.php: match with Level IN (...) / Component IN (...) using
  parameterized placeholders, and persist the selections as arrays in the
  session

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-01 11:50:32 -04:00
SteveGilvarryandClaude Opus 4.8 630f778c3f fix: sanitize all client log fields and drop raw POST dump
Address review feedback on the log create endpoint hardening:

- Coerce every client field to a scalar before sanitizing, so non-scalar
  input (e.g. message[]=x) can no longer make preg_replace return an array
  and trip a TypeError in logPrint().
- Strip control characters from level too, and log the sanitized value in
  the "Unexpected logger level" message instead of the raw field, closing
  the same control-character injection via the level field. Also stop
  mutating $_POST; use a local variable.
- Replace the invalid-request print_r($_POST, true) dump, which emitted the
  raw POST (with embedded newlines) into the log, with a fixed message.

refs #2466

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-13 19:08:25 +10:00
SteveGilvarryandClaude Opus 4.8 a7958bbdfb fix: strip control characters from client-supplied web_js log fields
The log create endpoint (request=log&task=create) passed the client
supplied message and file straight to logPrint. logPrint only strips
trailing newlines, so embedded newlines survived into the flat text log
file and could forge what looked like additional log lines.

Strip control characters (newlines, carriage returns, tabs, etc.) from
message and file in createRequest before logging, matching the apparent
original intent (logPrint carries a comment expecting newlines to already
be replaced). A log message is a single line and the Log view stores it
as one Message column, so this has no effect on legitimate use.

This is defense-in-depth: the endpoint already requires an authenticated
System:Edit user (ZM_LOG_INJECT defaults off), the component is hardcoded
to web_js, and the Log view escapes angle brackets so the field cannot
inject markup. Relates to the long-standing log injection report.

refs #2466

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-13 15:11:40 +10:00
IgorA100 46fdd66c18 More correctly retrieves Component from $_REQUEST and writes it to the session (log.php) 2026-05-22 17:59:22 +03:00
IgorA100 0f44886ba1 More correct parsing of $_REQUEST['level'] before assigning its value to $L (log.php) 2026-05-21 18:46:07 +03:00
Isaac ConnorandCopilot Autofix powered by AI baa66facf9 Potential fix for pull request finding
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
2026-05-21 07:15:51 -04:00
IgorA100 805ebcdf3f Don't use the $newSessionValue array and cast $_REQUEST['level'] to a scalar string (log.php) 2026-05-21 11:38:25 +03:00
IgorA100 0ae7e74177 Don't use the $newSessionValue array and cast $_REQUEST['level'] to a scalar string (log.php) 2026-05-21 11:35:08 +03:00
IgorA100 c1850820fe Open and close the session only once per page refresh. (log.php)
Open and close the session only once per page refresh.
First, save all changes in the $newSessionValue array, then open the session, set the new session values, and close the session.
Only write the correct $_REQUEST['level'] values ​​to "zmLogFilterLevel" (if they are in ZM\Logger::$codes).
2026-05-17 21:25:02 +03:00
IgorA100 ae84fbde72 Save the selected filter level in the session (log.php) 2026-05-17 00:21:40 +03:00
Isaac Connor a2e4c84750 Merge branch 'master' into patch-89533 2026-05-14 17:30:19 -04:00
Isaac Connor 2b1627e245 Merge pull request #4818 from IgorA100/patch-750745
Fix: Correct calculation of the total number of pages for pagination with filters on Log page
2026-05-14 07:47:48 -04:00
Isaac Connor 644716b4ea fix: sanitize reflected user input in ajax error messages
Earlier commits in this branch dropped reflected user input from
ajaxError() messages entirely to close Snyk XSS findings, but that
lost useful diagnostic information ("Unrecognised action <name>",
"Insufficient permissions for user <user>"). Use validHtmlStr()
(htmlspecialchars with ENT_QUOTES) so the values still appear in
the message and Snyk recognises the sanitization. Affects
add_monitors.php, device.php, event.php, events.php, log.php.
2026-05-13 22:54:02 -04:00
Isaac Connor 65ea580e59 fix: remove reflected user input from log ajax XSS sinks
Four sites in log.php concatenated user-controlled data into the
$message string that flows to ajaxError() / exit(). Three permission
messages reflected \$user->Username(); the catch-all task message
reflected \$_REQUEST['task']. Drop the reflected tokens; still log
the unrecognised task server-side via ZM\\Warning.
2026-05-13 16:57:24 -04:00
IgorA100 74a61da6fa Merge branch 'master' into patch-89533 2026-05-13 12:54:55 +03:00
IgorA100andCopilot Autofix powered by AI ce0a128146 Potential fix for pull request finding
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
2026-05-13 10:06:30 +03:00
IgorA100 0333a86359 Server -> ServerId mapping for sorting in a SQL table (log.php) 2026-05-12 20:39:05 +03:00
IgorA100 c5adf25717 Merge branch 'master' into patch-750745 2026-05-12 19:53:47 +03:00
IgorA100andCopilot Autofix powered by AI 94a97dcd99 Potential fix for pull request finding
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
2026-05-12 19:51:50 +03:00
IgorA100 3c94068f04 Update log.php 2026-05-12 12:17:28 +03:00
IgorA100 8dcb424f9f Fix: Save the selected "All" value in the "zmLogComponent" session (log.php)
Previously, there was an issue where selecting "All" didn't change the session value because "All"=""
2026-05-11 23:33:27 +03:00
IgorA100 02210240a8 Fix: Correct calculation of the total number of pages for pagination with filters (log.php)
We don't need an additional check to include the WHERE clause in the query string, since $where will only be populated if filters are specified.

Previously, there was an issue with counting the total number of pages when using a filter by components or levels.
2026-05-11 12:32:38 +03:00
IgorA100 8a396dec39 Update log.php 2026-05-10 22:43:06 +03:00
IgorA100 9d3a86f7ac Fix: Search (log.php) 2026-05-10 22:17:07 +03:00
IgorA100 91aa946888 Feat: Optimizing slow SQL queries for large tables (log.php)
Using a JOIN subquery can speed up the main query by 10-20 times.
Also, you should select by the 'Level' column, not 'Code', since 'Level' is indexed.
2026-05-10 19:29:28 +03:00
Simpler1 07d328a164 feat(log): Add selection to logs and delete for selected logs. 2026-03-06 14:50:24 -05:00
Simpler1 3f0ede73a6 feat(logs): New button to clear logs 2026-03-06 12:04:57 -05:00
Isaac Connor f9d8ba5281 Store log Component in session 2025-09-06 11:26:20 -04:00
Isaac Connor 97b7ae9c5f Use htmlspecialchars on Message to prevent Stored Cross-Site Scripting. Fixes GHSA-rqxv-447h-g7jx 2024-09-30 06:23:22 -04:00
Isaac Connor 839ef9ce67 Add a Component filter to logs 2024-01-16 16:35:52 -05:00
Isaac Connor 969baa3891 Convert user from an array to a User object 2023-04-23 12:57:29 -04:00
Isaac Connor 2447f6e0db Fix wrong number of parameters when using text search 2023-04-13 13:27:20 -04:00
Isaac Connor 86768d108a Fix values not being used in log query causing errors when level filter isused 2023-04-01 09:41:37 -04:00
Isaac Connor 0462efdef0 Don't strip tags from log Message. It removes anything involving > OR < 2023-02-07 10:54:25 -05:00
Isaac Connor bfc4b5aa67 Implement filtering by ServerId 2022-10-25 13:36:02 -04:00
Isaac Connor 4f0f2224b2 Restructure code around build where part of sql, add specific filters for level, StartDateTime and EndDateTime. 2022-10-25 12:49:08 -04:00
Isaac Connor de2866f957 Check value of System:Edit permission and ZM_LOG_INJECT to disable ajax log injection. 2022-10-06 14:51:29 -04:00
Isaac Connor d289eb4860 File must be escaped as well to prevent XSS 2022-09-30 09:57:35 -04:00
Isaac Connor 852b67bf21 Don't call format on an empty string and only pass the integer part of it 2022-08-10 17:58:11 -04:00
Isaac Connor c18a5e422a Rework code to be smaller. Don't require SystemView permission to post a log entry. Default to ERR if invalid err code provided instead of PANIC so that the log entry still gets saved 2022-08-10 09:18:39 -04:00
Isaac Connor d135be17ba Must pass time() to format() 2022-06-03 14:25:19 -04:00
Isaac Connor b4d5029884 Use dateTimeFOrmatter to format the date/time on logs 2022-06-01 15:42:44 -04:00
Isaac Connor 7743445323 Handle when there isn't a server Id 2021-03-22 21:30:56 -04:00
Isaac Connor f306febb5f use find_one when loading Server which can use caching 2021-03-22 17:49:59 -04:00
Andrew Bauer c166b0de0f fix return too soon when no log results 2021-01-02 16:33:56 -06:00
Isaac Connor 52c7cc5869 reorganize code. Remove Server caching as it is done in Object.php. 2020-11-11 11:49:44 -05:00