mirror of
https://github.com/Cacti/cacti.git
synced 2026-11-02 18:17:00 +09:00
* security: add nonce CSP mode for inline JavaScript protection Reworks the content_security_policy_script setting into a three-mode Inline JavaScript Protection dropdown (HTMLX default, Nonce, None) and ports the per-request nonce, strict-dynamic policy, and CSP violation report endpoint from 1.2.x. The stored option key and its default are unchanged, so existing installs keep their current policy byte-for-byte; nonce mode is opt-in. Signed-off-by: Thomas Vincent <thomasvincent@gmail.com> * security: stage nonce CSP enforcement safely Add report-only migration, gated enforcement, nonce-aware shared emitters, Linux Docker validation, and operator documentation.\n\nRefs #7703 * security: validate CSP header sources Reject malformed alternate-source configurations at the CSP boundary, validate fallback report URIs, and remove the obsolete Psalm header-taint baseline entry.\n\nRefs #7705 * chore: drop the composer.json change from this PR Dependency and manifest changes are a maintainer call and do not belong in this one. Signed-off-by: Thomas Vincent <thomasvincent@gmail.com> --------- Signed-off-by: Thomas Vincent <thomasvincent@gmail.com>
76 lines
3.4 KiB
PHP
76 lines
3.4 KiB
PHP
<?php
|
|
/*
|
|
+-------------------------------------------------------------------------+
|
|
| Copyright (C) 2004-2026 The Cacti Group |
|
|
+-------------------------------------------------------------------------+
|
|
| Cacti: The Complete RRDtool-based Graphing Solution |
|
|
+-------------------------------------------------------------------------+
|
|
*/
|
|
|
|
/*
|
|
* Stand-alone fixture served by the built-in PHP web server
|
|
* (`php -S 127.0.0.1:<port> -t tests/integration/fixtures csp_fixture.php`).
|
|
*
|
|
* The active CSP mode comes from the CSP_TEST_MODE environment variable so
|
|
* the harness can drive every branch of the setting without a database. The
|
|
* header and <meta> lines below mirror include/global.php and lib/html.php,
|
|
* so the test proves the real wire format for each mode over HTTP.
|
|
*
|
|
* Dependency-free on purpose: only lib/headers_secure.php is loaded so a
|
|
* failure points at the header helper, not the Cacti bootstrap.
|
|
*/
|
|
|
|
require_once __DIR__ . '/../../../lib/headers_secure.php';
|
|
|
|
if (!defined('CACTI_CSP_NONCE_ENFORCE')) {
|
|
define('CACTI_CSP_NONCE_ENFORCE', getenv('CSP_TEST_ENFORCE') === '1');
|
|
}
|
|
|
|
if (!function_exists('read_config_option')) {
|
|
function read_config_option($key) {
|
|
switch ($key) {
|
|
case 'content_security_policy_script':
|
|
$v = getenv('CSP_TEST_MODE');
|
|
|
|
return $v === false ? '' : $v;
|
|
case 'content_security_alternate_sources':
|
|
$v = getenv('CSP_TEST_ALTERNATES');
|
|
|
|
return $v === false ? '' : $v;
|
|
case 'content_security_report_uri':
|
|
$v = getenv('CSP_TEST_REPORT_URI');
|
|
|
|
return $v === false ? '' : $v;
|
|
}
|
|
|
|
return '';
|
|
}
|
|
}
|
|
|
|
$alternates = CactiSecureHeaders::normalizeAlternateSources(read_config_option('content_security_alternate_sources'));
|
|
$script_src = CactiSecureHeaders::scriptSrc($alternates);
|
|
$report_uri = CactiSecureHeaders::getCspMode() === 'nonce-enforce' ?
|
|
CactiSecureHeaders::reportUriDirective() : '';
|
|
|
|
// Mirrors the HTTP header emitted by include/global.php.
|
|
header("Content-Security-Policy: default-src *; img-src 'self' https://api.qrserver.com $alternates data: blob:; style-src 'self' 'unsafe-inline' $alternates; $script_src; frame-ancestors 'self' $alternates; worker-src 'self' $alternates;$report_uri");
|
|
|
|
$report_script_src = CactiSecureHeaders::reportOnlyScriptSrc($alternates);
|
|
|
|
if ($report_script_src !== '') {
|
|
$report_uri = CactiSecureHeaders::reportUriDirective();
|
|
header("Content-Security-Policy-Report-Only: default-src *; img-src 'self' https://api.qrserver.com $alternates data: blob:; style-src 'self' 'unsafe-inline' $alternates; $report_script_src; frame-ancestors 'self' $alternates; worker-src 'self' $alternates;$report_uri");
|
|
}
|
|
|
|
$nonce_attr = CactiSecureHeaders::getNonceAttribute();
|
|
|
|
print "<!doctype html>\n<html><head>\n";
|
|
/* Mirrors the <meta> policy in lib/html.php: no frame-ancestors/report-uri,
|
|
* both of which are invalid inside a meta CSP. */
|
|
print "<meta http-equiv=\"Content-Security-Policy\" content=\"default-src *; img-src 'self' https://api.qrserver.com $alternates data: blob:; style-src 'self' 'unsafe-inline' $alternates; $script_src; worker-src 'self' $alternates;\">\n";
|
|
print "</head><body>\n";
|
|
print "<script type='text/javascript' $nonce_attr>var x = 1;</script>\n";
|
|
print "<script type='text/javascript'>window.legacyInlineRan = true;</script>\n";
|
|
print "<button type='button' onclick='window.legacyHandlerRan = true;'>Legacy handler</button>\n";
|
|
print "</body></html>\n";
|