[pkg-nagios-changes] [Git][nagios-team/nagvis][upstream] New upstream version 1.10.7

Bas Couwenberg (@sebastic) gitlab at salsa.debian.org
Fri Oct 30 14:40:40 GMT 2026



Bas Couwenberg pushed to branch upstream at Debian Nagios Maintainer Group / nagvis


Commits:
59e23ea3 by Bas Couwenberg at 2026-10-30T15:37:00+01:00
New upstream version 1.10.7
- - - - -


10 changed files:

- ChangeLog
- package-lock.json
- share/frontend/nagvis-js/js/ElementContext.js
- share/frontend/nagvis-js/js/ElementHover.js
- share/frontend/nagvis-js/js/ElementLabel.js
- share/frontend/nagvis-js/js/frontend.js
- share/frontend/nagvis-js/js/nagvis.js
- share/server/core/defines/global.php
- share/userfiles/templates/default.context.html
- share/userfiles/templates/default.header.html


Changes:

=====================================
ChangeLog
=====================================
@@ -1,3 +1,9 @@
+1.10.7
+  * FIX: Special characters in hover menu child rows are no longer shown as HTML entities (#459)
+  * FIX: NagVis logo link now opens nagvis.org in a new tab, so a Content-Security-Policy no longer blocks it when NagVis is embedded in a frame (e.g. the Checkmk NagVis snapin)
+  * FIX: Prevent XSS via object names in context menus
+  * FIX: Prevent XSS via check output and object names in hover menus and labels (CVSS: 7.3) CVSS:4.0/AV:N/AC:L/AT:P/PR:L/UI:A/VC:H/VI:H/VA:H/SC:N/SI:L/SA:N. HTML in check output, notes or object names is now shown as text instead of being rendered.
+
 1.10.6
   * FEAT: The "editHtml" permission can now be granted to roles via the role management GUI, instead of only being available to administrators (#452)
   * FIX: Non-admin roles (e.g. Managers) can edit maps again - the editHtml permission is now only required to change HTML content, not for every map modification (#452)


=====================================
package-lock.json
=====================================
@@ -170,29 +170,43 @@
             }
         },
         "node_modules/@humanfs/core": {
-            "version": "0.19.1",
-            "resolved": "https://registry.npmjs.org/@humanfs/core/-/core-0.19.1.tgz",
-            "integrity": "sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA==",
+            "version": "0.19.2",
+            "resolved": "https://registry.npmjs.org/@humanfs/core/-/core-0.19.2.tgz",
+            "integrity": "sha512-UhXNm+CFMWcbChXywFwkmhqjs3PRCmcSa/hfBgLIb7oQ5HNb1wS0icWsGtSAUNgefHeI+eBrA8I1fxmbHsGdvA==",
             "dev": true,
             "license": "Apache-2.0",
+            "dependencies": {
+                "@humanfs/types": "^0.15.0"
+            },
             "engines": {
                 "node": ">=18.18.0"
             }
         },
         "node_modules/@humanfs/node": {
-            "version": "0.16.7",
-            "resolved": "https://registry.npmjs.org/@humanfs/node/-/node-0.16.7.tgz",
-            "integrity": "sha512-/zUx+yOsIrG4Y43Eh2peDeKCxlRt/gET6aHfaKpuq267qXdYDFViVHfMaLyygZOnl0kGWxFIgsBy8QFuTLUXEQ==",
+            "version": "0.16.8",
+            "resolved": "https://registry.npmjs.org/@humanfs/node/-/node-0.16.8.tgz",
+            "integrity": "sha512-gE1eQNZ3R++kTzFUpdGlpmy8kDZD/MLyHqDwqjkVQI0JMdI1D51sy1H958PNXYkM2rAac7e5/CnIKZrHtPh3BQ==",
             "dev": true,
             "license": "Apache-2.0",
             "dependencies": {
-                "@humanfs/core": "^0.19.1",
+                "@humanfs/core": "^0.19.2",
+                "@humanfs/types": "^0.15.0",
                 "@humanwhocodes/retry": "^0.4.0"
             },
             "engines": {
                 "node": ">=18.18.0"
             }
         },
+        "node_modules/@humanfs/types": {
+            "version": "0.15.0",
+            "resolved": "https://registry.npmjs.org/@humanfs/types/-/types-0.15.0.tgz",
+            "integrity": "sha512-ZZ1w0aoQkwuUuC7Yf+7sdeaNfqQiiLcSRbfI08oAxqLtpXQr9AIVX7Ay7HLDuiLYAaFPu8oBYNq/QIi9URHJ3Q==",
+            "dev": true,
+            "license": "Apache-2.0",
+            "engines": {
+                "node": ">=18.18.0"
+            }
+        },
         "node_modules/@humanwhocodes/module-importer": {
             "version": "1.0.1",
             "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz",
@@ -306,9 +320,9 @@
             "license": "MIT"
         },
         "node_modules/brace-expansion": {
-            "version": "1.1.13",
-            "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.13.tgz",
-            "integrity": "sha512-9ZLprWS6EENmhEOpjCYW2c8VkmOvckIJZfkr7rBW6dObmfgJ/L1GpSYW5Hpo9lDz4D1+n0Ckz8rU7FwHDQiG/w==",
+            "version": "1.1.21",
+            "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.21.tgz",
+            "integrity": "sha512-9zeA+KLZNNzglF2TPKRQEDyx6Yby7daAkuy8MiPzpXPsYDWi/DRM8jmwUDxokQjYqBpv5DgPiwD4h4ZZSy1Ujw==",
             "dev": true,
             "license": "MIT",
             "dependencies": {
@@ -769,9 +783,9 @@
             "license": "ISC"
         },
         "node_modules/js-yaml": {
-            "version": "4.3.0",
-            "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.3.0.tgz",
-            "integrity": "sha512-1td788aAnnZ5qs7V2QIRl1owjtYpbKt749Y3xauqQgwIIGF/xXWz1wMTEBx5O3LK3lXLVuqXPdPxj2BoFHaW9Q==",
+            "version": "4.3.2",
+            "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.3.2.tgz",
+            "integrity": "sha512-SFNOvSJ+Dgf/9An904Yx+CgSlIPCkIpao4qo51lpee25TIRejdH3rhR4EZMGoNx3/TP3O+wzWuiTFl4sqbltzA==",
             "dev": true,
             "funding": [
                 {


=====================================
share/frontend/nagvis-js/js/ElementContext.js
=====================================
@@ -24,6 +24,21 @@
 const g_context_templates = {};
 let g_context_open = null;
 
+// The custom macros come from the backend definitions in the main config, which
+// only admins can edit, and are meant to hold HTML or URLs
+const g_context_raw_macros = new Set(["custom_1", "custom_2", "custom_3"]);
+
+// Makes a context macro value safe for the menu HTML as well as for JS strings
+// in inline handlers and javascript: URLs. The browser decodes HTML entities
+// (and the percent encoding of javascript: URLs) before running the JS, so
+// backslashes, quotes and percent signs are JS escaped first. "%20" is kept for
+// the space encoded pnp_* macros.
+function escapeContextMacro(name, value) {
+    if (!value) return "";
+    if (g_context_raw_macros.has(name)) return value;
+    return htmlspecialchars(String(value).replace(/[\\'"]|%(?!20)/g, (c) => "\\x" + c.charCodeAt(0).toString(16)));
+}
+
 // Hides the currently open context menu
 function contextHide() {
     if (g_context_open !== null) g_context_open.hide();
@@ -369,9 +384,10 @@ const ElementContext = Element.extend({
         }
         oSectionMacros = null;
 
-        // Loop and replace all normal macros
+        // Loop and replace all normal macros. The values are object derived, so
+        // escape them to prevent XSS when the menu is inserted via innerHTML.
         this.template_html = this.template_html.replace(/\[(\w*)\]/g, function () {
-            return oMacros[arguments[1]] || "";
+            return escapeContextMacro(arguments[1], oMacros[arguments[1]]);
         });
         oMacros = null;
     }


=====================================
share/frontend/nagvis-js/js/ElementHover.js
=====================================
@@ -26,6 +26,23 @@ const g_hover_template_childs = {};
 const g_hover_urls = {};
 let g_hover_open = null;
 
+// State output macros are pre-encoded by the server and get their own escaping
+const g_hover_state_output_macros = new Set(["obj_output", "obj_summary_output", "obj_perfdata"]);
+
+// The custom macros come from the backend definitions in the main config, which
+// only admins can edit, and are meant to hold HTML or URLs
+const g_hover_raw_macros = new Set(["custom_1", "custom_2", "custom_3"]);
+
+// Escapes a hover macro value before it is inserted into the menu HTML. Every
+// macro is escaped except the language strings and the custom macros, so newly
+// added macros are safe by default.
+function escapeHoverMacro(name, value) {
+    if (value === null || value === undefined) return "";
+    if (name.startsWith("lang_") || g_hover_raw_macros.has(name)) return value;
+    if (g_hover_state_output_macros.has(name)) return escapeStateOutput(value);
+    return htmlspecialchars(value);
+}
+
 // Hides the currently open hover menu
 function hoverHide() {
     if (g_hover_open !== null) g_hover_open.hide();
@@ -500,7 +517,9 @@ const ElementHover = Element.extend({
 
         // Replace all normal macros
         template_html = template_html.replace(/\[(\w*)\]/g, function () {
-            return htmlspecialchars(oMacros[arguments[1]] || "");
+            // The child display_name is pre-encoded by the server just like the output
+            if (arguments[1] === "obj_display_name") return escapeStateOutput(oMacros.obj_display_name);
+            return escapeHoverMacro(arguments[1], oMacros[arguments[1]]);
         });
         return template_html;
     },
@@ -577,8 +596,9 @@ const ElementHover = Element.extend({
         // not be mistaken for an unknown macro and dropped. Keep the original
         // "[name]" text for tokens that are not actual macros, while still
         // replacing defined macros that legitimately hold an empty value.
+        // All values are escaped to prevent XSS via check output.
         template_html = template_html.replace(/\[(\w*)\]/g, function (match, name) {
-            return Object.prototype.hasOwnProperty.call(oMacros, name) ? oMacros[name] : match;
+            return Object.prototype.hasOwnProperty.call(oMacros, name) ? escapeHoverMacro(name, oMacros[name]) : match;
         });
         return template_html;
     },
@@ -719,7 +739,8 @@ const ElementHover = Element.extend({
 
         // Loop and replace all normal macros
         this.template_html = this.template_html.replace(/\[(\w*)\]/g, function () {
-            return oMacros[arguments[1]] || "[" + arguments[1] + "]";
+            const name = arguments[1];
+            return oMacros[name] ? escapeHoverMacro(name, oMacros[name]) : "[" + name + "]";
         });
 
         // Re-add the clean child code


=====================================
share/frontend/nagvis-js/js/ElementLabel.js
=====================================
@@ -37,13 +37,17 @@ const ElementLabel = Element.extend({
                 objName = this.obj.conf.name;
             }
 
-            this.label_text = this.label_text.replace(getRegEx("name", "\\[name\\]", "g"), objName);
-            this.label_text = this.label_text.replace(getRegEx("alias", "\\[alias\\]", "g"), this.obj.conf.alias);
+            // The label is rendered as HTML, so object derived values are escaped
+            const name = htmlspecialchars(objName);
+            const alias = htmlspecialchars(this.obj.conf.alias);
+            this.label_text = this.label_text.replace(getRegEx("name", "\\[name\\]", "g"), () => name);
+            this.label_text = this.label_text.replace(getRegEx("alias", "\\[alias\\]", "g"), () => alias);
 
             if (this.obj.conf.type == "service") {
+                const service_description = htmlspecialchars(this.obj.conf.service_description);
                 this.label_text = this.label_text.replace(
                     getRegEx("service_description", "\\[service_description\\]", "g"),
-                    this.obj.conf.service_description
+                    () => service_description
                 );
             }
         }
@@ -124,15 +128,18 @@ const ElementLabel = Element.extend({
 
         // Replace static macros in label_text when needed
         if (text && text !== "") {
-            text = text.replace(getRegEx("output", "\\[output\\]", "g"), this.obj.conf.output);
+            const output = escapeStateOutput(this.obj.conf.output);
+            text = text.replace(getRegEx("output", "\\[output\\]", "g"), () => output);
 
             if (this.obj.conf.type == "service" || this.obj.conf.type == "host") {
-                text = text.replace(getRegEx("perfdata", "\\[perfdata\\]", "g"), this.obj.conf.perfdata);
+                const perfdata = escapeStateOutput(this.obj.conf.perfdata);
+                text = text.replace(getRegEx("perfdata", "\\[perfdata\\]", "g"), () => perfdata);
             }
         }
 
+        // Drop a trailing entity or tag which has been cut in half by the truncation
         if (this.obj.conf.label_maxlen > 0 && text.length > this.obj.conf.label_maxlen)
-            text = text.substr(0, this.obj.conf.label_maxlen - 2) + "...";
+            text = text.substr(0, this.obj.conf.label_maxlen - 2).replace(/&[^;\s]*$|<[^>]*$/, "") + "...";
 
         return text;
     },


=====================================
share/frontend/nagvis-js/js/frontend.js
=====================================
@@ -285,6 +285,16 @@ function showAckDialog(map_name, objectId) {
     );
 }
 
+// Reloads the current automap with the given object as root. The object name
+// is looked up here instead of being put into the context template, because
+// HTML escaping does not protect a value inside inline JS.
+function makeRootObject(map_name, objectId) {
+    const obj = getMapObjByDomObjId(objectId);
+    if (!obj) return;
+    window.location =
+        "index.php?mod=Map&act=view&show=" + escapeUrlValues(map_name) + getViewParams({ root: obj.conf.name }, true);
+}
+
 // Handles manual map object update triggered by e.g. the context menu
 function refreshMapObject(event, objectId, only_state) {
     if (typeof only_state === "undefined") only_state = true;


=====================================
share/frontend/nagvis-js/js/nagvis.js
=====================================
@@ -787,7 +787,23 @@ function htmlspecialchars(sStr) {
         '"': """,
         "'": "'"
     };
-    return sStr.replace(/[&<>"']/g, (match) => map[match]);
+    if (sStr === null || sStr === undefined) return "";
+    return String(sStr).replace(/[&<>"']/g, (match) => map[match]);
+}
+
+/**
+ * Escapes state output (output/summary_output/perfdata) which has already been
+ * pre-encoded by the server (escapeStringForJson). Neutralises HTML tags while
+ * keeping the server generated <br /> line breaks and entities intact.
+ *
+ * "&" is not escaped, so the result is only safe in element content and in
+ * quoted attributes, not in unquoted attributes or inline JS.
+ */
+function escapeStateOutput(sStr) {
+    return (sStr || "")
+        .replace(/</g, "<")
+        .replace(/>/g, ">")
+        .replace(/<br \/>/g, "<br />");
 }
 
 /**


=====================================
share/server/core/defines/global.php
=====================================
@@ -24,7 +24,7 @@
  *****************************************************************************/
 
 // NagVis Version
-const CONST_VERSION = '1.10.6';
+const CONST_VERSION = '1.10.7';
 
 // Set PHP error handling to standard level
 // Different levels for php versions below 5.1 because PHP 5.1 reports


=====================================
share/userfiles/templates/default.context.html
=====================================
@@ -1,7 +1,7 @@
 <ul>
     <!-- BEGIN automap_not_root -->
     <!-- BEGIN host_or_shape -->
-    <li><a href="javascript:window.location='index.php?mod=Map&act=view&show=[map_name]'+getViewParams({'root':'[name]'}, true);">[lang_make_root]</a></li>
+    <li><a href="#" onClick="makeRootObject('[map_name]', '[obj_id]');contextHide();return false">[lang_make_root]</a></li>
     <!-- END host_or_shape -->
     <!-- END automap_not_root -->
 


=====================================
share/userfiles/templates/default.header.html
=====================================
@@ -6,7 +6,7 @@
 <ul class="head">
     <div id="sidetoggle" onclick="toggleSidebar(true)"><img src="{$pathImages}/internal/sidebar_open.png" /></div>
     <li class="toggle"><a href="#" onclick="toggleHeader(true)" title="Hide header menu"><</a></li>
-    <li class="logo"><a href="https://www.nagvis.org/"><img src="{$pathImages}/internal/logo_small.png" alt="NagVis" /></a></li>
+    <li class="logo"><a href="https://www.nagvis.org/" target="_blank" rel="noopener noreferrer"><img src="{$pathImages}/internal/logo_small.png" alt="NagVis" /></a></li>
     <li class="dropdown">
         <span id="views-ddheader" onclick="ddMenuToggle(event, 'views')">{$langOpen}</span>
         {template headMaps maps pathBase currentMap indent=1}



View it on GitLab: https://salsa.debian.org/nagios-team/nagvis/-/commit/59e23ea3c9315d41baff1aeb323b4f8e674e4458

-- 
View it on GitLab: https://salsa.debian.org/nagios-team/nagvis/-/commit/59e23ea3c9315d41baff1aeb323b4f8e674e4458
You're receiving this email because of your account on salsa.debian.org. Manage all notifications: https://salsa.debian.org/-/profile/notifications | Help: https://salsa.debian.org/help


-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://alioth-lists.debian.net/pipermail/pkg-nagios-changes/attachments/20261030/836af894/attachment-0001.htm>


More information about the pkg-nagios-changes mailing list