feat(security): consolidated security hardening — forward-port from 1.2.x + DOM XSS + JS fixes#7055
Conversation
Centralized shell command gateway routing all execution through cacti_exec() with argv-array input, per-argument escaping, optional timeout via proc_open + stream_select, and automatic credential redaction in logs. Migrated structure_rra_paths.php to use bounded GET_LOCK retry with exponential backoff and shutdown-function release guard. Signed-off-by: Thomas Vincent <thomasvincent@gmail.com>
Add early SAPI !== cli rejection in cli_check.php returning 404. Tighten Content-Security-Policy from default-src * to default-src 'self' with explicit per-directive sources, add HSTS on HTTPS, and add COOP/CORP headers. Add XML size-limit checks and structured parse-error logging in install/functions.php and lib/import.php. Signed-off-by: Thomas Vincent <thomasvincent@gmail.com>
Centralized outbound HTTP fetch with reserved-IP rejection via FILTER_FLAG_NO_RES_RANGE, TLS peer verification by default, redirect-following disabled, and configurable timeout. Prevents SSRF by resolving hostnames and checking all A records against RFC 1918/6598/loopback/link-local ranges before connecting. Signed-off-by: Thomas Vincent <thomasvincent@gmail.com>
Add cacti_ldap_filter() for safe LDAP filter construction using ldap_escape() with LDAP_ESCAPE_FILTER per substitution variable. Replaces raw string interpolation in search filter templates. Signed-off-by: Thomas Vincent <thomasvincent@gmail.com>
Add get_request_sort() for whitelist-only column/direction extraction, get_request_ids() for integer-only ID array parsing, and db_qstr_like() for safe SQL LIKE clause construction with percent/underscore escaping. Signed-off-by: Thomas Vincent <thomasvincent@gmail.com>
Add cacti_dispatch() declarative action table replacing ad-hoc switch/case on \$_REQUEST['action']. Enforces HTTP method, realm permission, and optional object-level ACL callback before dispatch. Logs unknown actions and method mismatches. Signed-off-by: Thomas Vincent <thomasvincent@gmail.com>
Apply html_escape() to unescaped description fields in managers.php SNMP notification tooltip and three locations in html_form.php where field_array description was rendered as raw HTML in formFieldDescription spans and display_tooltip() calls. Signed-off-by: Thomas Vincent <thomasvincent@gmail.com>
…ntion Add cacti_auth_transition() to lib/auth.php for safe privilege-level changes. Regenerates session ID, checks lockout status, rotates remember-me cookie token, and invalidates cached permission data. Intended for login, password change, and role-switch call sites. Signed-off-by: Thomas Vincent <thomasvincent@gmail.com>
Document current csrf-magic state, known gaps, and short/medium/long term migration plan toward explicit token embedding and SameSite cookie enforcement. Signed-off-by: Thomas Vincent <thomasvincent@gmail.com>
Add phpstan.neon targeting new security gateway files at level 5 with ignore rules for Cacti global functions not visible to PHPStan. Add GitHub Actions workflow running PHPStan on PHP 8.1 for push/PR to 1.2.x branch. Signed-off-by: Thomas Vincent <thomasvincent@gmail.com>
Add Pest source-scanning tests verifying all 9 security architecture items: exec gateway, HTTP SSRF protection, LDAP filter escaping, request helpers, action dispatcher, XSS escaping, auth transition, CSP/HSTS headers, and XML hardening. Signed-off-by: Thomas Vincent <thomasvincent@gmail.com>
Atomic write with realpath containment. Rejects absolute paths, traversal segments, and symlinks that escape the allowed base directories. Post-write verification via realpath. Eliminates the file-write/path-traversal vulnerability class in package imports. Signed-off-by: Thomas Vincent <thomasvincent@gmail.com>
basename() on plugin name, realpath containment check against the plugins directory. Eliminates the LFI/include-path vulnerability class across all 5 plugin include sites in lib/plugins.php. Signed-off-by: Thomas Vincent <thomasvincent@gmail.com>
All 4 include_once sites in lib/plugins.php now use cacti_plugin_path() which enforces basename + realpath containment. Rejects traversal attempts and paths that resolve outside the plugins directory. Signed-off-by: Thomas Vincent <thomasvincent@gmail.com>
Force SHA-256 for package signature verification. The legacy SHA-1 key path is commented out. Packages signed with the old key must be re-signed with the SHA-256 key to import. Signed-off-by: Thomas Vincent <thomasvincent@gmail.com>
Signed-off-by: Thomas Vincent <thomasvincent@gmail.com>
When graph.php is loaded without a valid local_graph_id the existing $exists check at line 66 can pass due to MySQL implicit type coercion (WHERE local_graph_id = '' matches id 0). The subsequent db_fetch_row at line 105 returns false, and line 114 dereferences $graph['graph_template_id'] on a non-array, producing PHP 8.x 'Undefined array key' warnings for graph_template_id, rows, steps, and id. Add a cacti_sizeof guard after the graph fetch and redirect to graph_view.php with raise_message() so the user sees a clear error instead of a stack of PHP warnings. Signed-off-by: Thomas Vincent <thomasvincent@gmail.com>
Pest source-scan tests confirming the cacti_sizeof($graph) guard exists after db_fetch_row_prepared, fires raise_message with redirect to graph_view.php, and exits before any $graph['...'] dereference. All syntax is PHP 7.4 compatible (no arrow functions, no match expressions, no named arguments). Signed-off-by: Thomas Vincent <thomasvincent@gmail.com>
…ipts Prevent null array dereferences when db_fetch_row_prepared returns an empty result for a missing or deleted record. Signed-off-by: Thomas Vincent <thomasvincent@gmail.com>
Signed-off-by: Thomas Vincent <thomasvincent@gmail.com>
…ures Per TheWitness: guard-failure redirects should return the user to where they came from, not to a hardcoded page head. Use validate_redirect_url with HTTP_REFERER and a safe fallback to the file's own list view. Applies to all 16 guard sites added in 34cdca9 plus the graph.php guard from ddc5c35. Signed-off-by: Thomas Vincent <thomasvincent@gmail.com>
The graph.php guard now redirects via validate_redirect_url with HTTP_REFERER instead of a hardcoded Location header. Update the test assertion to match. Signed-off-by: Thomas Vincent <thomasvincent@gmail.com>
Setting cache_directory to the Cacti install root itself passed the strpos prefix check because equality was not rejected. Tighten to require strict subdirectory (base + DIRECTORY_SEPARATOR prefix). Signed-off-by: Thomas Vincent <thomasvincent@gmail.com>
tempnam() files persist until removed. The import flow created a temp file but never cleaned it up, leaking one file per import operation. Signed-off-by: Thomas Vincent <thomasvincent@gmail.com>
index.php and script_server.php had identical normalization + prefix blocks for dynamic-include confinement. Replace both with the new cacti_path_is_within helper in lib/functions.php. Signed-off-by: Thomas Vincent <thomasvincent@gmail.com>
Source-scan tests for cacti_csv_safe (tab/CR in dangerous list, ltrim applied), cacti_path_is_within usage in index.php and script_server.php, db_replace redaction of snmp_auth_passphrase and rsa_private_key, and boost cache-directory equality rejection. Signed-off-by: Thomas Vincent <thomasvincent@gmail.com>
…ion/purge actions Signed-off-by: Thomas Vincent <thomasvincent@gmail.com>
Csrf-magic fallback cookie now sets Secure, HttpOnly, SameSite=Strict. Token comparisons use hash_equals() instead of raw ===. GET logout documented as risk-accepted (annoyance-only, SameSite mitigates). Test coverage for all three changes. Signed-off-by: Thomas Vincent <thomasvincent@gmail.com>
* Improve IPv6 support in RRDtool proxy and ping utilities
- Fix RRDtool proxy to dynamically detect IPv6 addresses and use
AF_INET6 sockets instead of hardcoded AF_INET (IPv4-only), enabling
connections to IPv6 RRDtool proxy servers including backup servers
- Strip brackets from IPv6 addresses before passing to socket_connect
- Properly close failed sockets before creating new ones for failover
- Replace fragile str_contains(':') IPv6 detection in ping with
filter_var(FILTER_FLAG_IPV6) for more robust address validation
https://claude.ai/code/session_01SbuDigvAkYvPKvdcougsdo
* Fix JS code quality: implicit globals, deprecated APIs, loose equality
- Add var declarations to prevent implicit globals in install.js
(element, enabled, button, buttonCheck)
- Remove console.log debug output left in production (install.js)
- Replace deprecated jQuery .unbind() with .off() (layout.js)
- Fix "depreciated" typo to "deprecated" in deprecation warnings
- Convert == / != to === / !== for null, boolean, string, typeof,
and numeric comparisons across install.js and realtime.js
https://claude.ai/code/session_01SbuDigvAkYvPKvdcougsdo
* Replace deprecated jQuery shorthand event methods in layout.js
Replace .click(), .keyup(), .keydown(), .mousedown(), .mouseenter(),
.mouseleave(), .submit(), .resize() with .on() equivalents. Replace
.focus(), .change() trigger calls with .trigger(). These shorthands
were deprecated in jQuery 3.5.
https://claude.ai/code/session_01SbuDigvAkYvPKvdcougsdo
* Replace deprecated jQuery methods in realtime.js
Replace .bind() with .on() and .change() trigger calls with
.trigger('change'). .bind() was deprecated in jQuery 3.0 and
shorthand triggers in jQuery 3.5.
https://claude.ai/code/session_01SbuDigvAkYvPKvdcougsdo
* Replace deprecated jQuery methods in install.js and fix ===/!== errors
Replace .click(), .change(), .focus() with .on()/.trigger() equivalents.
Also fix !=== and ==== operators that were incorrectly introduced by a
prior replace-all of == to === within existing !== and === expressions.
https://claude.ai/code/session_01SbuDigvAkYvPKvdcougsdo
* Restore == null where needed to catch both null and undefined
In JavaScript, == null matches both null and undefined, which is an
intentional idiom. The prior === null conversion broke cases where
values come from jQuery .val(), .data(), $.urlParam(), or object
property access that may return undefined rather than null. Revert
those specific cases while keeping === null where variables are
explicitly initialized to null.
https://claude.ai/code/session_01SbuDigvAkYvPKvdcougsdo
* Replace deprecated jQuery methods in all theme main.js files
Replace .unbind().click() with .off('click').on('click'), convert
.hover() to .on('mouseenter').on('mouseleave'), replace .change(),
.scroll(), .click() shorthands with .on() equivalents, and .blur()
with .trigger('blur') across all 10 theme files.
https://claude.ai/code/session_01SbuDigvAkYvPKvdcougsdo
* Convert string concatenation to template literals
Replace 'str' + var + 'str' patterns with ES6 template literals
in realtime.js and install.js. Improves readability especially for
URL construction and HTML building. Also replace $.parseJSON() with
native JSON.parse() in realtime.js.
https://claude.ai/code/session_01SbuDigvAkYvPKvdcougsdo
* Convert var to const for single-assignment variables
Replace var with const where the variable is assigned once and never
reassigned within its scope, in install.js and realtime.js. Keeps var
for variables that are conditionally reassigned (e.g. size, url).
https://claude.ai/code/session_01SbuDigvAkYvPKvdcougsdo
---------
Co-authored-by: Claude <noreply@anthropic.com>
… null check Signed-off-by: Thomas Vincent <thomasvincent@gmail.com>
Signed-off-by: Thomas Vincent <thomasvincent@gmail.com>
Signed-off-by: Thomas Vincent <thomasvincent@gmail.com>
Signed-off-by: Thomas Vincent <thomasvincent@gmail.com>
…, hi-IN, it-IT, ja-JP, ko-KR, lv-LV, pl-PL, ru-RU, uk-UA Signed-off-by: Thomas Vincent <thomasvincent@gmail.com>
Signed-off-by: Thomas Vincent <thomasvincent@gmail.com>
Signed-off-by: Thomas Vincent <thomasvincent@gmail.com>
…acti_validate_sort_column Signed-off-by: Thomas Vincent <thomasvincent@gmail.com>
Signed-off-by: Thomas Vincent <thomasvincent@gmail.com>
…lidation.php, remove duplicate helper Signed-off-by: Thomas Vincent <thomasvincent@gmail.com>
… duplicate Signed-off-by: Thomas Vincent <thomasvincent@gmail.com>
…ggregate and reports Signed-off-by: Thomas Vincent <thomasvincent@gmail.com>
… failures Signed-off-by: Thomas Vincent <thomasvincent@gmail.com>
…xport, query_host_cpu Local branch diverged from upstream before PR Cacti#6941 was merged. Restore the scripts from upstream/develop so DatasourceScriptErrorReturnTest passes. Signed-off-by: Thomas Vincent <thomasvincent@gmail.com>
…acti#7057) Consolidated PR now only contains its unique work, avoiding merge conflicts with the companion PRs. Files moved to their respective PRs: - lib/ping.php → Cacti#7057 - theme/nav/layout files → Cacti#7040 - test files, workflow files, security helpers → Cacti#7036 Signed-off-by: Thomas Vincent <thomasvincent@gmail.com>
This branch contains only files unique to the consolidated security work that are not already covered by: - Cacti#7036 (security hardening batch) - Cacti#7040 (tooltip pin / theme+nav changes) - Cacti#7057 (ping.php alignment) Previously shipped as feat/security-consolidated-develop with 124 files; rebased to eliminate merge conflicts by removing duplicated content. Signed-off-by: Thomas Vincent <thomasvincent@gmail.com>
8bd240e to
7523bc2
Compare
|
@somethingwithproof, This pull unwinds a lot of changes in the develop branch. It's going to take a lot of work to unwind the unwinding. |
|
OK, I will take a look |
Add .claude/, .omc/, .worktrees/, and notepad.md to .gitignore so local AI-assistant session state, oh-my-claudecode orchestration files, and scratch worktrees never leak into a PR. Add CLAUDE.md at the repo root documenting the house conventions (PHP 7.4 target on 1.2.x branches, cacti_sizeof/htmle/gfrv wrappers, db_qstr_rlike, the vendor-pollution pitfall) so future AI contributions match the review feedback pattern from TheWitness instead of repeating it on every PR. Signed-off-by: Thomas Vincent <thomasvincent@gmail.com>
|
Added |
5 local oh-my-claudecode artefacts (.omc/sessions/*.json and .omc/state/*) were staged into this branch at some point and persisted across rebases. The .gitignore entry added in the previous commit stops new ones from being added, but the already-tracked files need a separate git rm --cached to leave the index. Signed-off-by: Thomas Vincent <thomasvincent@gmail.com>
|
Closing per your feedback that this unwinds too many already-landed develop changes. The audit also flagged that the The one genuinely-unique helper (the declarative action dispatcher) has been extracted to a tight single-file PR at #7063. The existing helper files that overlap with #7054 will land there as part of the 1.2.x architectural branch; forward-porting them to develop can happen on a clean rebase once #7054 settles. |
Forward-port of #7054 (architectural security helpers) plus DOM XSS fixes and JS error corrections.
35 commits covering: cacti_exec, cacti_http_fetch, cacti_ldap_filter, cacti_safe_write, cacti_plugin_path, cacti_dispatch, cacti_auth_transition, typed request helpers, db_fetch_row guards, CSRF hardening, CSP/HSTS, DOM XSS escaping, JS null-deref fixes, SHA-256 enforcement, PHPStan CI.
Consolidates and supersedes: #7049, #7053.
Signed-off-by: Thomas Vincent thomasvincent@gmail.com