From cd3aaff0fd75c596ee9807d2ad2e8178eb83e1cd Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Thu, 18 Jun 2026 18:35:57 -0400 Subject: [PATCH] feat: add ZM_LOG_BROWSER_EXTENSIONS config to optionally log extension errors refs #3340 #4914 unconditionally drops Javascript errors and CSP violations sourced from browser extensions. Some operators want to know when a plugin is touching the ZoneMinder tab, so gate the suppression behind a config entry. Add ZM_LOG_BROWSER_EXTENSIONS (boolean, default no) in the logging category. It is exposed to JS by the existing non-private-config loop in skin.js.php as the string '0'/'1' (the same convention as ZM_LOG_INJECT), so logger.js only filters extension sources when it is '0'. Default behaviour is unchanged: extension noise stays out of the log. Co-Authored-By: Claude Opus 4.8 (1M context) --- scripts/ZoneMinder/lib/ZoneMinder/ConfigData.pm.in | 14 ++++++++++++++ web/js/logger.js | 13 ++++++++++--- 2 files changed, 24 insertions(+), 3 deletions(-) diff --git a/scripts/ZoneMinder/lib/ZoneMinder/ConfigData.pm.in b/scripts/ZoneMinder/lib/ZoneMinder/ConfigData.pm.in index b1f01ab72..bc82c5e8e 100644 --- a/scripts/ZoneMinder/lib/ZoneMinder/ConfigData.pm.in +++ b/scripts/ZoneMinder/lib/ZoneMinder/ConfigData.pm.in @@ -1383,6 +1383,20 @@ our @options = ( type => $types{boolean}, category => 'logging', }, + { + name => 'ZM_LOG_BROWSER_EXTENSIONS', + default => 'no', + description => 'Log Javascript errors and CSP violations from browser extensions', + help => q` + Browser extensions (ad blockers, password managers, etc.) inject scripts into + every page. Their Javascript errors and Content-Security-Policy violations are + not ZoneMinder's code and normally just spam the web_js log, so by default they + are ignored. Enable this if you want to see when a browser extension is touching + the ZoneMinder tab, at the cost of noisier logs. + `, + type => $types{boolean}, + category => 'logging', + }, { name => 'ZM_LOG_DEBUG', default => 'no', diff --git a/web/js/logger.js b/web/js/logger.js index 1c081d9e5..45b5e8cc4 100644 --- a/web/js/logger.js +++ b/web/js/logger.js @@ -36,14 +36,21 @@ if (!window.console) { // Browser extensions (ad blockers, password managers, etc.) inject scripts // into every page; their errors and CSP violations are not ZoneMinder's and -// would otherwise spam the log. Ignore anything sourced from an extension. +// would otherwise spam the log. Ignore anything sourced from an extension, +// unless ZM_LOG_BROWSER_EXTENSIONS is enabled (useful to detect a plugin +// touching the ZoneMinder tab). +// ZM_LOG_BROWSER_EXTENSIONS is emitted as the string '0'/'1' (boolean config), +// matching the ZM_LOG_INJECT convention above. +const logExtensionSources = + (typeof ZM_LOG_BROWSER_EXTENSIONS !== 'undefined') && (ZM_LOG_BROWSER_EXTENSIONS !== '0'); + function isExtensionSource(url) { return (typeof url === 'string') && /^(moz-extension|chrome-extension|safari-web-extension|safari-extension|webkit-masked-url)(:|$)/.test(url); } window.onerror = function(message, url, line, col, error) { - if (isExtensionSource(url)) return; + if (!logExtensionSources && isExtensionSource(url)) return; if (!message || message === 'Script error.') { message = 'Script error (no details available, possibly cross-origin)'; } @@ -55,7 +62,7 @@ window.onerror = function(message, url, line, col, error) { }; window.addEventListener("securitypolicyviolation", function logCSP(evt) { - if (isExtensionSource(evt.sourceFile) || isExtensionSource(evt.blockedURI)) return; + if (!logExtensionSources && (isExtensionSource(evt.sourceFile) || isExtensionSource(evt.blockedURI))) return; const level = evt.disposition == "enforce" ? "ERR" : "DBG"; let message = evt.blockedURI + " violated CSP " + evt.violatedDirective;