Files
cacti/include/js/cacti-rendering.js
Thomas VincentandTheWitness 97e67c6777 security: harden browser rendering and review CodeQL boundaries (#8298)
* security: harden browser history and dependency option merging

Signed-off-by: Thomas Vincent <thomasvincent@gmail.com>

* docs: record CodeQL triage decisions and follow-up scope

Signed-off-by: Thomas Vincent <thomasvincent@gmail.com>

* test: wait for history reload before inspecting browser storage

Signed-off-by: Thomas Vincent <thomasvincent@gmail.com>

* security: isolate HTMX history from the legacy navigation handler

Signed-off-by: Thomas Vincent <thomasvincent@gmail.com>

* ci: build CodeQL assets through controlled dependency installs

Signed-off-by: Thomas Vincent <thomasvincent@gmail.com>

* security: render form validation messages as plain text

Signed-off-by: Thomas Vincent <thomasvincent@gmail.com>

* test: verify drag rows and color input security boundaries

Signed-off-by: Thomas Vincent <thomasvincent@gmail.com>

* test: verify tree navigation rename and datetime input boundaries

Signed-off-by: Thomas Vincent <thomasvincent@gmail.com>

* security: harden menu rendering and complete CodeQL caller review

Signed-off-by: Thomas Vincent <thomasvincent@gmail.com>

* security: centralize menu rendering and stabilize certificate CI

Signed-off-by: Thomas Vincent <thomasvincent@gmail.com>

* test: align certificate expectation with parallel web PR

Signed-off-by: Thomas Vincent <thomasvincent@gmail.com>

---------

Signed-off-by: Thomas Vincent <thomasvincent@gmail.com>
Co-authored-by: TheWitness <thewitness@cacti.net>
2026-10-11 21:26:38 -04:00

41 lines
1.4 KiB
JavaScript

/* Copyright (C) 2026 The Cacti Group. SPDX-License-Identifier: GPL-2.0-or-later */
// One synchronous interface for legacy widgets that intentionally accept menu
// markup. Labels use text APIs instead. This policy is not a general-purpose
// template renderer: application attributes, IDs, styles and embeds are removed.
(() => {
'use strict';
function menuFragment(value) {
const content = typeof value === 'string' ? value : '';
if (!window.DOMPurify || !window.DOMPurify.isSupported) {
return document.createTextNode(content);
}
const fragment = window.DOMPurify.sanitize(content, {
ALLOWED_TAGS: ['h6', 'div', 'a', 'span', 'p', 'br', 'ul', 'ol', 'li', 'strong', 'em', 'b', 'i'],
ALLOWED_ATTR: ['href', 'title'],
ALLOW_DATA_ATTR: false,
ALLOW_ARIA_ATTR: false,
RETURN_DOM_FRAGMENT: true
});
fragment.querySelectorAll('a[href]').forEach(link => {
try {
const url = new URL(link.getAttribute('href'), document.baseURI);
if (!['http:', 'https:', 'mailto:', 'tel:'].includes(url.protocol)) {
link.removeAttribute('href');
}
} catch (error) {
link.removeAttribute('href');
}
});
return fragment;
}
// Legacy classic-script loaders need a shared synchronous interface. An own,
// immutable value also prevents HTML named properties from replacing it.
Object.defineProperty(window, 'CactiRenderer', {
value: Object.freeze({ menuFragment }),
configurable: true
});
})();