mirror of
https://github.com/Cacti/cacti.git
synced 2026-10-23 03:27:02 +09:00
3ab4c69fb7ddc3af029ef5ca08b5bdf7f9ada8f6
38
Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
3ab4c69fb7 |
PHPStan Level 8 - Phase 1 Conversion (#8135)
* PHPStan Level 8 - Phase 1 Conversion
Reduces the real (no-baseline) PHPStan level 8 error count from ~2,724 to 425
and regenerates phpstan-baseline.neon accordingly (8,941 -> 1,921 lines).
Core type-safety changes
- lib/database.php: narrow db_fetch_row()/db_fetch_row_prepared() to return
array (was bool|array) and db_fetch_assoc()/db_fetch_assoc_prepared() to
return array (was mixed); the _prepared workers coerce the connection-failure
path to an empty array so the declared type is honest. empty()/cacti_sizeof()
checks are unaffected.
- lib/database.php: narrow $db_conn guards from is_object() to instanceof PDO in
the transaction/quote/insert-id helpers so the PDO calls resolve.
- lib/mib_cache.php: the fluent MibCache methods mib()/object()/table()/row()
now return self instead of a bare object so chained calls type-check.
Type-safety casts and guards (mechanical, behaviour-preserving)
- Cast string|false/|null builtin results feeding string parameters
(json_decode, base64_encode, trim, htmle, preg_replace, explode, etc.).
- Cast int|string / int|false values feeding int id/pid parameters.
- Guard resource|false fopen()/popen() handles before fwrite/fclose/fread, and
wrap foreach over array|false/list|false results with `?: []`.
Genuine bugs found and fixed
- cli/add_tree.php / lib/api_tree.php: site tree nodes could not be created via
the CLI - the --site-id option was never parsed, $nodeTypes had no 'site'
entry (passing null to api_tree_item_save(int $type) -> TypeError), and
api_tree_item_save() never persisted site_id. All three fixed.
- tree.php: leaves_exist() is typed : int but returned db_fetch_assoc_prepared()
(an array of rows) for a SELECT COUNT(*); now uses db_fetch_cell_prepared()
and returns the scalar count.
- lib/mib_cache.php: $entries_per_object was a float (division / ceil) used as
an array key (PHP 8.1 implicit float-to-int key deprecation); now cast to int.
Config
- .phpstan.neon: add notIdentical.alwaysTrue and booleanAnd.alwaysFalse to the
existing always-true/false ignore family (same "PHPStan gets confused"
dynamic-constant cause already covered by identical.alwaysTrue,
notEqual.alwaysTrue, booleanAnd.leftAlwaysTrue, etc.).
- phpstan-baseline.neon: regenerated; the remaining 425 entries are the
untouched long tail for Phase 2.
* Remove redundant @phpstan-ignore comments
Follow-up cleanup for the level 8 Phase 1 work. Removes 14 inline
@phpstan-ignore comments that no longer suppress any reported error:
- cli/repair_database.php: `@phpstan-ignore booleanAnd.alwaysFalse` is now
covered by the global booleanAnd.alwaysFalse ignore added to .phpstan.neon.
- 13 others (host.php, include/global.php, include/session.php,
lib/api_automation.php x2, lib/functions.php x2, lib/html_utility.php x2,
lib/installer.php, lib/package.php, lib/reports.php, lib/rrd.php) no longer
match any reported error after the type-safety fixes (e.g. the
db_fetch_row_prepared() return-type narrowing removed the offset-assign
errors the api_automation ignores were suppressing).
Verified with reportUnmatchedIgnoredErrors enabled: 0 unmatched ignores
remain and the full level 8 run still reports 0 errors.
* ci: satisfy validator ratchet and refresh security sink baselines
- lib/api_tree.php: give the new tree-item site_id a real numeric
constraint ('^[-0-9]+$', matching html_reports.php) instead of an
empty regex, so the validator constraint ratchet passes without
bumping its baseline.
- tests/security/baselines: refresh the three sink/hotspot signatures
whose code text changed from the (string) casts added in this branch
(lib/poller.php setsid, cli/rrdresize.php json_encode, lib/import.php
simplexml_load_string).
* ci(js): allow DOMPurify 3.4.16 and update sync_js pin test
The dependabot bump (#8134) moved package.json/package-lock.json to
DOMPurify 3.4.16 on develop but left tests/js/sync_js.test.mjs pinned to
3.4.15, so the PR merge commit fails the 'DOMPurify is pinned' contract.
Bring package.json/package-lock.json to 3.4.16 (matching develop) and
update the pin test to expect 3.4.16.
* test: update sql.php null-handling assertion for the (string) cast
This branch changed scripts/sql.php from trim(\ ?? '') to
trim((string) \) (the phpstan-safe form that coerces both null and
false from shell_exec()). Update SqlScriptsTest's null-return assertion
to match the new cast instead of the removed '?? ''.
* fix: address Copilot review - ownership IDs, fopen guards, mib_cache bound
- chown()/chgrp(): pass integer UID/GID from fileowner()/filegroup()
(guarding the false lookup) instead of string-casting them, which made
them resolve account/group names like '1001' and fail. Covers
lib/api_device.php, lib/boost.php and poller_maintenance.php (x2).
- fopen() failure now short-circuits instead of reporting success:
lib/package.php returns false, lib/clog_webapi.php and utilities.php
render an error, and snmpagent_mibcachechild.php skips the cache write
and sentinel unlink so a concurrent writer's lock is preserved.
- lib/mib_cache.php: keep the float quotient as the loop bound and cast
only the array offset, so a non-even entries/columns split no longer
truncates the loop and drops a cached row.
* revert: keep db_fetch_* returning false on error; baseline Level 8 instead
Per review, restoring the intentional fail-safe: db_fetch_row[_prepared]()
return bool|array and db_fetch_assoc[_prepared]() return mixed again, so a
genuine SQL error (not just a missing connection) is signalled as false and
the === false checks in cdef_is_in_use(), boost_process_* and aggregate.php
work as before. Dropped the is_array() ? : [] coercion.
Level 8 stays green by regenerating phpstan-baseline.neon to capture the
re-introduced bool|array/mixed call-site errors (the ~1,850 the narrowing
had eliminated). Tightening those callers to drop them from the baseline is
Phase 2 work.
---------
Co-authored-by: TheWitness <copilot@users.noreply.github.com>
|
||
|
|
2d516ca0a9 |
security: consolidate additional verified GHSA fixes for develop (batch 2) (#8065)
* security: contain data source paths within the RRA directory (GHSA-9qff-xccr-rrc5)
* security: escape executable path in exec_background on non-win32 (GHSA-cv7j-p6fh-j4q7)
* security: stop disabled accounts authenticating via remember-me cookie and revoke on disable (GHSA-pqrp-v752-mc4f)
* security: prefer base_url over client Host for the force_https redirect (GHSA-3466-fp7g-c9rf)
* changelog: add fragments for GHSA-9qff-xccr-rrc5, GHSA-cv7j-p6fh-j4q7, GHSA-pqrp-v752-mc4f, GHSA-3466-fp7g-c9rf
* security: address review findings on GHSA batch 2 (develop)
- lib/dsstats.php, poller_maintenance.php: route through
get_data_source_path() instead of expanding data_source_path directly,
so the RRA containment check applies to these consumers too (GHSA-9qff)
- lib/functions.php: data_source_path_within_rra() now also validates
the realpath of each existing ancestor segment, closing a symlink
pivot that the purely lexical check missed (GHSA-9qff)
- include/global.php: fail closed instead of falling back to SERVER_NAME
when base_url is unset/invalid, removing the residual open-redirect
path (GHSA-3466)
- lib/html_utility.php: cacti_force_https_host()/
cacti_build_https_redirect_url() now parse a scheme-less base_url
(matching lib/reports.php's own normalization) and preserve a
configured non-default port instead of silently dropping it (GHSA-3466)
- tests: move DataSourcePathContainmentTest.php into
tests/Unit/Security/PathTraversal/, and switch its fixtures from a
fictional CACTI_PATH_RRA path to a real temp directory so the new
realpath()-based checks (including a symlink-pivot regression case)
can actually run
* test: fix CACTI_PATH_RRA constant collision in DataSourcePathContainmentTest
CI showed 2 failures: RrdProxyProtocolTest.php runs earlier in the same
Pest process and already defines CACTI_PATH_RRA to a fictional,
non-existent '/var/lib/cacti/rra', so our own definition guard never
ran and realpath(CACTI_PATH_RRA) returned false for every assertion,
failing the two tests that expect containment to be true.
Now ensure the directory backing CACTI_PATH_RRA actually exists on
disk regardless of who defined the constant first, and gracefully
skip only the fixture-dependent assertions if this process can't
create it (e.g. no permission on a system path).
* test: refresh sink inventory baseline for exec_background rename
|
||
|
|
a1fab6816f |
ci: refresh security/SQL-interpolation baselines (#8059)
Signed-off-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> |
||
|
|
1e5329dd89 |
security: consolidate verified GHSA-rjvj win32 cmd.exe hardening for develop (#8056)
* fix(win32): harden the cmd.exe shell sinks on develop (GHSA-rjvj-r52f-8v5q) Forward-port of the 1.2.x win32 command-injection remediation. On Windows the SNMP/ping/data-query/trap commands run through cmd.exe, which ignores the \" escape and toggles quoting on every ", so cacti_escapeshellarg cannot stop & | ^ < > ( ). - add cacti_escapeshellarg_cmd(), stripping those operators on win32 before quoting (a Unix pass-through) - snmp_escape_string(): strip the operators in the win32 branch - route the SNMP binary hostname, the data-query device values (get_script_query_path), the data-input arg builders, the Windows ping hostname, and the SNMP trap notification arguments through the strip get_script_query_path is device-reachable (a rogue monitored device supplies an SNMP index/system field that executes on the Windows poller); the rest are post-auth. Signed-off-by: Thomas Vincent <thomasvincent@gmail.com> * fix(win32): strip %VAR% from the hostname on Windows (GHSA-rjvj-r52f-8v5q) cmd.exe expands %VAR% even inside quotes, so a device hostname of %USERNAME% would expand into the SNMP/ping command line. Add an opt-in $strip_env flag to cacti_escapeshellarg_cmd() and set it for the SNMP binary and ping hostnames, which never legitimately contain a percent. Community and credential values are left untouched because they can legitimately contain %. Signed-off-by: Thomas Vincent <thomasvincent@gmail.com> * docs(changelog): note win32 cmd.exe hardening for develop * security: require Package Management permission to trust new package signers package_accept_key() (reachable via the repository-based package import flow) inserted new keys into package_public_keys, the global trust store consulted by import_validate_signature() for every future import by every user, without checking for the Package Management (realm 29) permission that gates package_keys.php/package.php/package_repos.php for the same table. An Import Templates (realm 17) user could therefore add themselves as a trusted signer for the whole install. Also add the 'accept' action to the CSRF-protected bad_actions list so the acceptance step requires POST plus a valid token like every other state-changing action. GHSA-gxgm-8rhv-r7jv * docs(changelog): note package-signer trust authorization fix * security: close tab-based open redirect bypass in CSRF timeout handler sanitize_uri()'s guard ( |
||
|
|
dd61dd7a31 |
ci: refresh security/SQL-interpolation baselines (#8001)
Signed-off-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> |
||
|
|
6e72b00ae5 |
security(cli): keep database credentials off the command line (#7909)
* security(cli): keep database credentials off the command line audit_database.php passed the database password as --password= to the mysql client. Escaping does not help there: an argument is readable from the process list by every local user for as long as the import runs, and with --debug the same string was printed to stdout. The credentials now go in a defaults file created with mode 0600 and removed afterwards. splice_rrd.php named its dump files from the RRD basename and mt_rand() in a world writable directory and created them by shell redirection, and backupRRDFile() copied into the same place. Both now use one private directory created for the run. Modern kernels block the symlink case through fs.protected_symlinks, so this is hardening rather than a fix for an exploitable path, but the names should not be guessable in the first place. float_rrdfiles.php appended its debug log to a fixed /tmp/clearer.log, which any local user could pre-create or read. It now uses the system temporary directory with a name of its own. Signed-off-by: Thomas Vincent <thomasvincent@gmail.com> * ci: refresh audit database checks * fix: quote credentials written to the MySQL option file The option file format is not byte transparent. An unquoted '#' starts a comment, a backslash escapes, and trailing whitespace is stripped, so any install whose database password contained one of those authenticated with a truncated credential and failed the schema load with no useful diagnostic. Double quote the values and escape the backslash and quote characters, verified against my_print_defaults. Refuse a credential containing a newline rather than write a file the parser would read as further options. Use --defaults-file rather than --defaults-extra-file so a ~/.my.cnf belonging to whoever runs the script cannot override the credentials Cacti supplies, and trim the 'which mysql' fallback, whose trailing newline split the command and stranded the credential argument on the second line. Signed-off-by: Thomas Vincent <thomasvincent@gmail.com> * Align CLI hardening with quality guards Signed-off-by: Thomas Vincent <thomasvincent@gmail.com> --------- Signed-off-by: Thomas Vincent <thomasvincent@gmail.com> |
||
|
|
ed5ab24474 |
fix: detect a failed SNMP walk by exit code, not by device data (#7929)
* fix: detect a failed SNMP walk by exit code, not by device data cacti_snmp_walk() discarded the entire walk when its output contained the word Timeout. That output is the walk itself, one line per OID, so a device with an ifAlias of 'Timeout Monitor' or a matching sysDescr emptied the result and data_query.php then dropped every index for the device. The check could not detect a real timeout either. net-snmp writes that to stderr and neither walk command redirects it, so the matched string was always device data. cacti_snmp_get() already reads the exit code for exactly this reason; exec_into_array() exposes it by reference. Also refuse a walk with empty credentials, matching cacti_snmp_get, get_raw and getnext. An unknown v3 protocol previously built a credential-less snmpwalk and failed with no log line. Signed-off-by: Thomas Vincent <thomasvincent@gmail.com> * test: cover the credential guard and refresh the security baselines Signed-off-by: Thomas Vincent <thomasvincent@gmail.com> * test: give the exec_into_array stub the real signature The stub dropped the by-reference exit code that lib/functions.php exposes, so any code reading it saw 0 regardless of what the command returned. That is why the walk's failure detection could regress to matching stdout without a test noticing. Signed-off-by: Thomas Vincent <thomasvincent@gmail.com> * test: expect the exit-code wording for a failed walk Signed-off-by: Thomas Vincent <thomasvincent@gmail.com> --------- Signed-off-by: Thomas Vincent <thomasvincent@gmail.com> |
||
|
|
ac0b2c9515 |
Fix confirmed poller, database, automation, and UI defects (#7861)
* issue#7024: do not delete still-running poller_time rows on cycle start The overrun path deleted every poller_time row for the poller, including the in-flight ones it had just counted, so overrunning processes lost their tracking. Restrict the delete to completed rows. Signed-off-by: Thomas Vincent <thomasvincent@gmail.com> * issue#7028: restore identifier and connection safety in the db layer _db_replace interpolated caller-supplied column keys as backtick identifiers without escaping, and db_commit_transaction checked @@in_transaction on the default connection rather than the one passed in. Both were correct on 1.2.x and regressed on develop. Signed-off-by: Thomas Vincent <thomasvincent@gmail.com> * issue#7341: size the CSV export maxrows to the requested span The export hardcoded --maxrows=10000, truncating wide date ranges regardless of the data source profile step. Signed-off-by: Thomas Vincent <thomasvincent@gmail.com> * issue#7421: offset automation_get_next_host through the 32-bit integer The hand-rolled octet walk subtracted the wrong magnitude per octet and emitted out-of-range octets for ranges wider than a /16. Signed-off-by: Thomas Vincent <thomasvincent@gmail.com> * issue#7030: skip the poller_output ENGINE=MEMORY conversion when already applied The output-table refresh ran ALTER TABLE poller_output ENGINE=MEMORY every cycle even though the swapped-in copy already inherits the engine, adding a metadata-locking DDL to the hot path. Probe the current engine and convert only when needed. Signed-off-by: Thomas Vincent <thomasvincent@gmail.com> * issue#7342: prefer snmpEngineTime only when it covers system uptime update_system_mibs preferred snmpEngineTime (x100) over sysUpTime whenever it was present, so an SNMP engine that restarts independently of the system (for example OpenBSD snmpd) made the stored uptime drop, which reads as a reboot and forces a perpetual reindex with mass 'U' results. Capture both values and use snmpEngineTime only when it is at least the system uptime, which still covers the 32-bit sysUpTime wrap. Signed-off-by: Thomas Vincent <thomasvincent@gmail.com> * issue#7403: scale lmsensors readings in the get path like the query path The get path returned the raw milli-unit SNMP counter while the query path divided voltage and temperature by 1000 (and corrected negative voltages), so a get gave values 1000x too large. Apply the same scaling before returning. Signed-off-by: Thomas Vincent <thomasvincent@gmail.com> * issue#7029: propagate the reconnected handle back to the caller db_check_reconnect() closed the dead connection, opened a new one, and returned true without giving the new handle back, so the db_execute_prepared retry ran its query against the connection that was just closed. Take $db_conn by reference and assign the fresh handle; the one caller that passed a literal false now passes a variable. Signed-off-by: Thomas Vincent <thomasvincent@gmail.com> * test: refresh sink inventory baseline Signed-off-by: Thomas Vincent <thomasvincent@gmail.com> * test: refresh architectural hotspots baseline Signed-off-by: Thomas Vincent <thomasvincent@gmail.com> * issue#7474: return valid after correcting the theme to selected_theme is_valid_theme() corrected an invalid theme to selected_theme but left $valid false, so callers saw the theme as invalid even though it had been fixed. Signed-off-by: Thomas Vincent <thomasvincent@gmail.com> * issue#7462: allow SELECT aliases as sortable automation columns site_name and host_template_name are aliases (s.name AS site_name), valid in ORDER BY but not real columns, so api_automation_column_exists() rejected them and the site-name sort silently failed. Signed-off-by: Thomas Vincent <thomasvincent@gmail.com> * issue#7456: bind the OS value in the automation OS filter The OS filter built 'os = ?' but bound the network value, so filtering by OS matched on the wrong input. Signed-off-by: Thomas Vincent <thomasvincent@gmail.com> * issue#7491: filter host_template_id against its own column html_make_device_where() compared h.location to the host_template_id value instead of h.host_template_id. Signed-off-by: Thomas Vincent <thomasvincent@gmail.com> * issue#7479: apply the hostname and description filters in get_hosts The REST get_hosts() honored host_id/poller/site/template/location but ignored the hostname and description filters its callers pass; add them as bound LIKE. Signed-off-by: Thomas Vincent <thomasvincent@gmail.com> * issue#7475: authorize a direct client-IP poller match before the hostname guard A poller configured by IP with no hostname-based pollers was rejected because the empty-hostname guard ran before the direct-match check, even though the client address exactly matched a poller's configured address. Signed-off-by: Thomas Vincent <thomasvincent@gmail.com> * Guard two divisors that PHP 8 makes fatal Signed-off-by: Thomas Vincent <thomasvincent@gmail.com> * docs: consolidate functional fix changelog * test: cover consolidated functional fixes * docs: identify consolidated functional fixes --------- Signed-off-by: Thomas Vincent <thomasvincent@gmail.com> |
||
|
|
ead882172e |
Make installer completion fail closed and add test coverage (#7626)
* Make installer completion fail closed Signed-off-by: Thomas Vincent <thomasvincent@gmail.com> * Add installer unit and Docker integration gates Signed-off-by: Thomas Vincent <thomasvincent@gmail.com> * Require installer coverage evidence Signed-off-by: Thomas Vincent <thomasvincent@gmail.com> * Stabilize installer failure scenarios Signed-off-by: Thomas Vincent <thomasvincent@gmail.com> * Address installer review feedback Signed-off-by: Thomas Vincent <thomasvincent@gmail.com> * Reject incomplete installer selection payloads Signed-off-by: Thomas Vincent <thomasvincent@gmail.com> * Match table validation to rendered controls Signed-off-by: Thomas Vincent <thomasvincent@gmail.com> * Protect long-running installer migrations Signed-off-by: Thomas Vincent <thomasvincent@gmail.com> * Load installer table metadata in one query Signed-off-by: Thomas Vincent <thomasvincent@gmail.com> * Optimize installer completion queries Signed-off-by: Thomas Vincent <thomasvincent@gmail.com> * Batch installer default settings writes Signed-off-by: Thomas Vincent <thomasvincent@gmail.com> * Batch installer color upserts Signed-off-by: Thomas Vincent <thomasvincent@gmail.com> * Batch installer template and poller lookups Signed-off-by: Thomas Vincent <thomasvincent@gmail.com> * Refresh SQL interpolation baseline after develop rebase Signed-off-by: Thomas Vincent <thomasvincent@gmail.com> * ci: use Cacti Composer test setup Signed-off-by: Thomas Vincent <thomasvincent@gmail.com> * test: resolve includes through Cacti path constants Signed-off-by: Thomas Vincent <thomasvincent@gmail.com> * ci: align installer workflows with policy Signed-off-by: Thomas Vincent <thomasvincent@gmail.com> * ci: refresh security baselines after develop rebase Signed-off-by: Thomas Vincent <thomasvincent@gmail.com> * ci: reuse TCP database credentials for installer checks Signed-off-by: Thomas Vincent <thomasvincent@gmail.com> * test(security): refresh sink inventory baseline after rebase onto develop Signed-off-by: Thomas Vincent <thomasvincent@gmail.com> * ci: run installer coverage on PHP 8.2 The 8.1 pin is below the ^8.2 composer requirement, so pest died in platform_check before any test ran. Signed-off-by: Thomas Vincent <thomasvincent@gmail.com> --------- Signed-off-by: Thomas Vincent <thomasvincent@gmail.com> |
||
|
|
964c1fb1f4 |
Harden RRDtool execution and data handoff (#7615)
* fix(rrdtool): quote RRDfile paths passed to the command line __rrd_execute() hands the assembled line to shell_exec(), and its docblock already required callers to quote each variable argument. Signed-off-by: Thomas Vincent <thomasvincent@gmail.com> * fix(tests): widen bare RRD path sweep Signed-off-by: Thomas Vincent <thomasvincent@gmail.com> * fix(rrdtool): quote binary path as one argument Signed-off-by: Thomas Vincent <thomasvincent@gmail.com> * fix(rrd): preserve caller logging context Signed-off-by: Thomas Vincent <thomasvincent@gmail.com> * Make RRD XML mutations transactional Signed-off-by: Thomas Vincent <thomasvincent@gmail.com> * Harden RRDtool proxy transport handling Signed-off-by: Thomas Vincent <thomasvincent@gmail.com> * fix(rrdproxy): reject invalid command argument lists Signed-off-by: Thomas Vincent <thomasvincent@gmail.com> * fix(rrdproxy): preserve binary graph payloads Signed-off-by: Thomas Vincent <thomasvincent@gmail.com> * Keep RRDtool proxy sessions encrypted Signed-off-by: Thomas Vincent <thomasvincent@gmail.com> * Harden local RRDtool command and output handoff Signed-off-by: Thomas Vincent <thomasvincent@gmail.com> * Eliminate graph rendering metadata lookups Signed-off-by: Thomas Vincent <thomasvincent@gmail.com> * Batch DSStats metadata and last-value reads Signed-off-by: Thomas Vincent <thomasvincent@gmail.com> * Batch interface speed metadata reads Signed-off-by: Thomas Vincent <thomasvincent@gmail.com> * Update architectural hotspot baseline for RRD execution Signed-off-by: Thomas Vincent <thomasvincent@gmail.com> * Batch RRD resolution profile lookups Signed-off-by: Thomas Vincent <thomasvincent@gmail.com> * Batch DSStats summary aggregates Signed-off-by: Thomas Vincent <thomasvincent@gmail.com> * Narrow phpseclib key types for the proxy encrypt and decrypt calls Signed-off-by: Thomas Vincent <thomasvincent@gmail.com> * test(rrd): bound the forked proxy peers so a stall cannot hang the suite The children accepted and read with no deadline while the parent used a 2s socket timeout, so any mismatch left the child blocked in read and the parent blocked in a bare pcntl_waitpid(). Note that accept() ignores SO_RCVTIMEO, so readiness has to be waited on with socket_select() rather than a socket option. Also corrects the array-command expectation, which still assumed develop's escape-everything behaviour rather than rrdtool_build_command() leaving the operation unquoted. Signed-off-by: Thomas Vincent <thomasvincent@gmail.com> * test(rrd): seed proxy settings where the real read_config_option looks UnitStubs only declares its stub when lib/functions.php has not been loaded yet, so during a full run the real database-backed function is the one in effect and $unit_config_options was being ignored. The parent then dialled an address the child was not listening on, which is what stalled the handshake. Seeding the option cache keeps the settings visible to either implementation. Signed-off-by: Thomas Vincent <thomasvincent@gmail.com> * test(rrd): assert the shell-free RRDtool launch instead of the old quoting __rrd_init now hands proc_open an argv array with bypass_shell, so escaping the binary path would put the quotes inside the filename. The assertion still expected the pre-argv line and had been failing unnoticed behind the suite hang. Signed-off-by: Thomas Vincent <thomasvincent@gmail.com> * test(dsstats): derive the expected stamps instead of assuming UTC dsstats_poller_output() formats with date(), so the hardcoded UTC strings only matched on a UTC runner. The failure was masked by the suite hang. Signed-off-by: Thomas Vincent <thomasvincent@gmail.com> * fix(rrd): keep RRDtool control codes intact in stdin commands Escaping the backslash when converting a shell-quoted argument to an RRDtool token left legends such as " IO Wait\:" ending in a dangling control code, so RRDtool refused every graph carrying an escaped colon. Verified against RRDtool 1.7.2 in pipe mode; values with no unambiguous quoted form are now refused rather than mangled. Signed-off-by: Thomas Vincent <thomasvincent@gmail.com> * test: use Cacti test bootstrap and config Signed-off-by: Thomas Vincent <thomasvincent@gmail.com> * test: resolve includes through Cacti path constants Signed-off-by: Thomas Vincent <thomasvincent@gmail.com> * test: match the escaping helpers by token, not by text lib/rrd.php documents its escaping behaviour in a comment, and the regex over raw source counted that as a call site. Signed-off-by: Thomas Vincent <thomasvincent@gmail.com> * ci: refresh sink inventory after develop rebase Signed-off-by: Thomas Vincent <thomasvincent@gmail.com> * test(rrd): prove argv isolation and concurrent pipe draining Signed-off-by: Thomas Vincent <thomasvincent@gmail.com> * test(security): refresh sink inventory baseline after rebase onto develop Signed-off-by: Thomas Vincent <thomasvincent@gmail.com> --------- Signed-off-by: Thomas Vincent <thomasvincent@gmail.com> |
||
|
|
f472548df8 |
ci: refresh security/SQL-interpolation baselines (#7838)
Signed-off-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> |
||
|
|
13adf317c0 |
chore: stop pull requests conflicting each other on shared files (#7798)
* chore: resolve concurrent CHANGELOG appends with a union merge Every PR lands on the same two anchors, so merging one reliably conflicts the others on CHANGELOG alone. Signed-off-by: Thomas Vincent <thomasvincent@gmail.com> (cherry picked from commit |
||
|
|
ff475bef6d | feat(installer): add installation domain foundation (#7291) | ||
|
|
f2898730e1 |
fix: guard process registration and cleanup against PID reuse (#7425) (#7574)
* fix: guard process registration and cleanup against PID reuse A bare posix_kill($pid, 0) only proves some process owns the pid, so a recycled one blocked new tasks or drew a SIGTERM meant for something else. Signed-off-by: Thomas Vincent <thomasvincent@gmail.com> * test: spawn probe children without a wrapping shell proc_open with a string command runs it via sh, so the child's /proc comm was sh and the identity check correctly refused to signal it on Linux. Signed-off-by: Thomas Vincent <thomasvincent@gmail.com> * docs: append the changelog entry at the end of the issue block Matches .github/copilot-instructions.md, which appends rather than inserting in numeric order. Signed-off-by: Thomas Vincent <thomasvincent@gmail.com> * fix: limit the process identity check to CLI callers The registry holds CLI tasks, so a web request comparing command names would never match and would report a running task dead. Signed-off-by: Thomas Vincent <thomasvincent@gmail.com> * test: use the full licence header on the new test files Signed-off-by: Thomas Vincent <thomasvincent@gmail.com> * test: treat CACTI_PATH_LOG as a directory and pass ctor args to the statement class Signed-off-by: Thomas Vincent <thomasvincent@gmail.com> * fix: make process identity checks self-contained * fix(batchgapfix): guard forced process termination * fix(processes): preserve live pids on permission errors * Refresh security baselines after develop rebase Signed-off-by: Thomas Vincent <thomasvincent@gmail.com> * Wait for the spawned child to exec before the identity check Signed-off-by: Thomas Vincent <thomasvincent@gmail.com> * Serve fetch() from the buffered rows in the process registry fake db_table_exists() probes with SHOW TABLES, and db_fetch_row_prepared() reads a single row with fetch(); the fake drained the cursor on execute() and answered neither, so every registry query came back empty and the tests exercised the no-processes-table path instead of the guard. Signed-off-by: Thomas Vincent <thomasvincent@gmail.com> * fix(changelog): drop entries duplicated by an earlier rebase Only the issue#7425 line belongs to this branch. Signed-off-by: Thomas Vincent <thomasvincent@gmail.com> * fix(poller): identify a registered process by its executable, not its name Children are spawned from path_php_binary, so a versioned child and a parent started as plain php reported different command names for one interpreter. Signed-off-by: Thomas Vincent <thomasvincent@gmail.com> * test(poller): skip the alias check where both paths share a command name Whether a second path to the interpreter yields a second name is the runner's business; asserting it failed where php is already the plain name. Signed-off-by: Thomas Vincent <thomasvincent@gmail.com> --------- Signed-off-by: Thomas Vincent <thomasvincent@gmail.com> Co-authored-by: TheWitness <thewitness@cacti.net> |
||
|
|
602a5e64cb |
ci: refresh security/SQL-interpolation baselines (#7737)
Signed-off-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> |
||
|
|
a6da63ac89 |
Harden remote graph transport and response validation (#7631)
* Harden remote graph transport boundaries * Tighten remote graph SVG validation * fix: PHPStan Issues with emerging code * Refresh security baselines after develop rebase Signed-off-by: Thomas Vincent <thomasvincent@gmail.com> * Match transport test to the explicit response-header default Signed-off-by: Thomas Vincent <thomasvincent@gmail.com> --------- Signed-off-by: Thomas Vincent <thomasvincent@gmail.com> Co-authored-by: TheWitness <thewitness@cacti.net> |
||
|
|
014d81489a |
ci: refresh security/SQL-interpolation baselines (#7659)
Signed-off-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> |
||
|
|
d75530f8ca |
ci: refresh security/SQL-interpolation baselines (#7654)
Signed-off-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> |
||
|
|
eaa1f022a9 |
ci: refresh security/SQL-interpolation baselines (#7653)
Signed-off-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> |
||
|
|
edafd489a5 |
fix(poller): launch cactid without a shell (#7603)
* Launch cactid poller without a shell * Handle an unset poller PHP binary * fix(poller): reject null bytes in executable paths --------- Co-authored-by: TheWitness <thewitness@cacti.net> |
||
|
|
bf45e21fd1 |
ci: refresh security/SQL-interpolation baselines (#7640)
Signed-off-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> |
||
|
|
a75bf823bb |
feat: consolidate htmx loader and renderer abstraction global.php includes (#7517)
* feat(rendering): add view renderer abstraction Signed-off-by: Thomas Vincent <thomasvincent@gmail.com> * isolate renderer traversal test file * Standardize renderer headers and endpoint guard * style(renderer): use standard unit-test header in CactiRendererTest Signed-off-by: Thomas Vincent <thomasvincent@gmail.com> * fix(renderer): reject null-byte paths before realpath Signed-off-by: Thomas Vincent <thomasvincent@gmail.com> * ci: refresh sink and hotspot baselines for line shifts Signed-off-by: Thomas Vincent <thomasvincent@gmail.com> * fix(renderer): guard root base path and template-dir redirect Signed-off-by: Thomas Vincent <thomasvincent@gmail.com> * fix(renderer): canonicalize both paths in index.php direct-access guard Signed-off-by: Thomas Vincent <thomasvincent@gmail.com> * hardening: normalize renderer base path to one separator Signed-off-by: Thomas Vincent <thomasvincent@gmail.com> * fix(renderer): reject filesystem root as template path A /-rooted renderer collapsed the containment prefix to /, which str_starts_with() matches for every absolute path, so any file was includable. Fail closed by refusing a root template path. Signed-off-by: Thomas Vincent <thomasvincent@gmail.com> * hardening: reject windows drive-root template path Signed-off-by: Thomas Vincent <thomasvincent@gmail.com> * refactor: adopt PHP 8.4 idioms in view renderer Signed-off-by: Thomas Vincent <thomasvincent@gmail.com> * test: cover renderer abstraction to full coverage Signed-off-by: Thomas Vincent <thomasvincent@gmail.com> * hardening(renderer): reject UNC template names and fix sink baselines Refuse a leading-backslash (UNC) template name the same way absolute POSIX paths are refused, skip the direct-request redirect when SCRIPT_FILENAME carries a null byte, and correct the renderer include and views redirect line numbers in the security baselines. Signed-off-by: Thomas Vincent <thomasvincent@gmail.com> * style: apply php-cs-fixer Signed-off-by: Thomas Vincent <thomasvincent@gmail.com> * fix(renderer): use PHP 8.1-compatible readonly property in CactiRenderer Signed-off-by: Thomas Vincent <thomasvincent@gmail.com> * hardening(rendering): drop template context array before include Unset the internal context array after extract so an included template only sees the extracted variables, not the full context. Signed-off-by: Thomas Vincent <thomasvincent@gmail.com> * ci: refresh security baselines for renderer line shift Signed-off-by: Thomas Vincent <thomasvincent@gmail.com> * fix(renderer): simplify containment check and drain nested output buffers The redundant real_path !== template_path comparison in validateTemplateFile() can never be false once is_file()/is_dir() have run, so drop it and keep the str_starts_with() prefix check as the actual containment guard. renderFile() also only unwound a single output buffer, so a template that opens (and leaves) its own buffer could leak an ob level or drop its content; unwind every buffer opened during rendering and reassemble them in the order they were written. Signed-off-by: Thomas Vincent <thomasvincent@gmail.com> * ci: refresh security baselines for renderer buffer/containment fix The dynamic_include line for lib/renderer.php's include shifted again after adding drainOutputBuffers(). Signed-off-by: Thomas Vincent <thomasvincent@gmail.com> * feat(htmx): vendor htmx 2.0.6 and add opt-in filter loader Signed-off-by: Thomas Vincent <thomasvincent@gmail.com> * test: cover htmx filter loader Add missing-vendored-file case for htmx_script_tag and mark the pre-bootstrap read_config_option guard as uncoverable. Signed-off-by: Thomas Vincent <thomasvincent@gmail.com> * test: harden htmx loader harness and tighten enable check Skip anything but an exact 'on' for htmx_enabled, reject 4xx/5xx fixture responses, fail fast when the fixture base path is unresolved, pin setup-php to a commit SHA, and reindent the e2e specs to tabs. Signed-off-by: Thomas Vincent <thomasvincent@gmail.com> * style: apply php-cs-fixer to htmx test files Signed-off-by: Thomas Vincent <thomasvincent@gmail.com> * test(htmx): drop duplicate script-tag src assertion The subdir-install test repeated the preceding assertion; CACTI_PATH_URL is fixed at bootstrap so it could not exercise a distinct scenario. Signed-off-by: Thomas Vincent <thomasvincent@gmail.com> * fix(e2e): drop sqlite3 from docker-php-ext-install php:8.2-apache already ships sqlite3 built in, and its ext/sqlite3 source only carries config0.m4 (not config.m4), so building it standalone always fails with "Cannot find config.m4", breaking the e2e image build before Playwright ever runs. Signed-off-by: Thomas Vincent <thomasvincent@gmail.com> * test(htmx): clear stat cache around version-file rename Earlier tests already stat the version file via file_exists()/ file_get_contents(), so PHP's stat cache can report it present right after these tests rename it away, making the assertions flaky. Signed-off-by: Thomas Vincent <thomasvincent@gmail.com> * fix(htmx): recompute script-tag cache-buster per call The md5 cache-buster was cached in a static, so FPM/script_server processes kept serving a stale ?v=... hash after an htmx.min.js upgrade. Recompute it per call, matching get_md5_include_js() and get_theme_paths() in lib/functions.php, and drop the 'v=' parameter name to match their path + '?' + hash shape. Signed-off-by: Thomas Vincent <thomasvincent@gmail.com> * test(e2e): drop obsolete MariaDB options * ci(htmx): run the htmx integration test by path instead of a testsuite filter Signed-off-by: Thomas Vincent <thomasvincent@gmail.com> * build: vendor htmx unminified Debian rejects minified sources. The file is the htmx 2.0.6 dist source; its minified sibling hashes identically to what was committed, and the pinned SRI is recomputed over the unminified file. Signed-off-by: Thomas Vincent <thomasvincent@gmail.com> * fix: address htmx review feedback * test(e2e): resolve Cacti version through PHP * test(htmx): harden portable server cleanup * ci(actions): update checkout action pins --------- Signed-off-by: Thomas Vincent <thomasvincent@gmail.com> Co-authored-by: TheWitness <thewitness@cacti.net> |
||
|
|
085bee630d |
ci: refresh security/SQL-interpolation baselines (#7608)
Signed-off-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> |
||
|
|
616da95e6d |
ci: refresh security/SQL-interpolation baselines (#7588)
Signed-off-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> |
||
|
|
f2a5ef0d51 |
fix: cast path_php_binary before passing it to string parameters (#7576) (#7577)
* fix: cast path_php_binary before passing it to string parameters The setting has no default, so it is null on a fresh install, and PHP 9 turns the resulting coercion from a deprecation into a TypeError. Signed-off-by: Thomas Vincent <thomasvincent@gmail.com> * docs: append the changelog entry at the end of the issue block Matches .github/copilot-instructions.md, which appends rather than inserting in numeric order. Signed-off-by: Thomas Vincent <thomasvincent@gmail.com> * test: use the full licence header on the new test files Signed-off-by: Thomas Vincent <thomasvincent@gmail.com> * fix: cast path_php_binary at every site that escapes it The first pass covered three of them; a repo-wide scan found 21, and the test now fails if a new one appears. Signed-off-by: Thomas Vincent <thomasvincent@gmail.com> * test: strengthen PHP binary path guard * fix: cover default-config PHP binary reads --------- Signed-off-by: Thomas Vincent <thomasvincent@gmail.com> Co-authored-by: TheWitness <thewitness@cacti.net> |
||
|
|
967da9443d |
ci: refresh security/SQL-interpolation baselines (#7565)
Signed-off-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> |
||
|
|
2f1178fccf |
ci: refresh security/SQL-interpolation baselines (#7564)
Signed-off-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> |
||
|
|
7c5e4c102a |
feat(support): consolidate diagnostics, lockout CSRF, process-table hook, and safe-triage (#7514)
* feat(support): add redaction toggle and recent-log panel Signed-off-by: Thomas Vincent <thomasvincent@gmail.com> * test: cover support safe triage Unit-test the redaction helper, severity log tail and environment table, and add a Docker probe that the redacted report masks the RSA fingerprint and hides the database host and password. Signed-off-by: Thomas Vincent <thomasvincent@gmail.com> * fix(support): escape rsa fingerprint and default unknown poller type Signed-off-by: Thomas Vincent <thomasvincent@gmail.com> * ci: refresh security baselines Signed-off-by: Thomas Vincent <thomasvincent@gmail.com> * fix(support): redact compressed IPv6 addresses Signed-off-by: Thomas Vincent <thomasvincent@gmail.com> * ci: refresh security baselines Signed-off-by: Thomas Vincent <thomasvincent@gmail.com> * ci: skip tests tree in hardening pattern guard Test fixtures assign request superglobals to exercise product code; the guard only protects product code. Signed-off-by: Thomas Vincent <thomasvincent@gmail.com> * fix(support): drop redundant isset flagged by phpstan Signed-off-by: Thomas Vincent <thomasvincent@gmail.com> * feat(support): guard snmpget path before executing Reject a null byte and require a regular executable file for path_snmpget so a misconfigured path cannot throw a ValueError or match a directory. Signed-off-by: Thomas Vincent <thomasvincent@gmail.com> * ci: refresh security baselines for snmpget path guard Signed-off-by: Thomas Vincent <thomasvincent@gmail.com> * fix(support): redact FQDNs before the node name and stop truncating tool output Masking the node name first could leave a domain suffix unmasked when the name was a prefix of an FQDN, and strip_tags() on installed SNMP output dropped legitimate angle-bracket content instead of just masking it. Also tighten the redact toggle to a strict '1' match, carry it through page-refresh URLs, and correct the diagnostics report comment to describe what actually gets masked. Signed-off-by: Thomas Vincent <thomasvincent@gmail.com> * fix(support): guard poller type lookup, log stats, and binary path checks Fall back to an "Unknown" label when poller_type doesn't match a known poller instead of an undefined-index warning, suppress warnings from filesize()/fopen() if the log file disappears between the caller's check and the open, and require is_file() plus a null-byte guard on the required-binaries version check (matches the snmpget check next to it). Signed-off-by: Thomas Vincent <thomasvincent@gmail.com> * docs(tests): align triage probe comments with actual assertions The header comments claimed the unredacted report exposes the DB host and password, but the probe only ever asserts the RSA fingerprint; DB credentials are checked for absence when present, not required to appear unmasked. Correct the wording in both files. Signed-off-by: Thomas Vincent <thomasvincent@gmail.com> * fix(support): require POST + CSRF token for maintenance lockout, compare-and-set Signed-off-by: Thomas Vincent <thomasvincent@gmail.com> * fix(support): require a valid expected state for lockout compare-and-set Signed-off-by: Thomas Vincent <thomasvincent@gmail.com> * test(security): refresh sink/hotspot baselines for upstream line shifts Signed-off-by: Thomas Vincent <thomasvincent@gmail.com> * fix(support): clamp page offset, quote SQL literals, suppress lockout URL state Signed-off-by: Thomas Vincent <thomasvincent@gmail.com> * test: cover support lockout csrf guard Exercise the POST-only guard and compare-and-set branches, and add a Docker probe that a GET or tokenless POST cannot change the lockout state. Signed-off-by: Thomas Vincent <thomasvincent@gmail.com> * test(support): guard lockout probe extraction and correct probe scope note Signed-off-by: Thomas Vincent <thomasvincent@gmail.com> * ci: refresh security baselines Signed-off-by: Thomas Vincent <thomasvincent@gmail.com> * ci: skip tests tree in hardening pattern guard Test fixtures assign request superglobals to exercise product code; the guard only protects product code. Signed-off-by: Thomas Vincent <thomasvincent@gmail.com> * fix(support): keep lockout e2e health poll alive under pipefail Tolerate grep returning non-zero before the Health field appears so the poll loop waits instead of exiting the script. Signed-off-by: Thomas Vincent <thomasvincent@gmail.com> * test(support): add integration coverage for the lockout compare-and-set The Unit test cannot reach the "currently locked" branch (forced reads return the PHP_TESTING default) and the e2e probe deliberately never locks the system, so the real read/write round trip against cacti_lockout_status has no coverage. Add a fast integration test that drives the lock, a stale-expected rejection, and the unlock against a real database, skipping gracefully when none is reachable. Signed-off-by: Thomas Vincent <thomasvincent@gmail.com> * test(support): match the page-clamp assertion whitespace-tolerantly Whitespace-exact substr_count() breaks on reformatting; the regex enforces the same guard without depending on exact spacing. Signed-off-by: Thomas Vincent <thomasvincent@gmail.com> * fix(support): harden the lockout e2e probe's auth and cookie-jar handling Assert a CSRF token was extracted and the session actually authenticated before running the lockout checks, otherwise a failed login still passes. Also handle tempnam() failure and clean up the cookie jar on every exit path via a shutdown handler. Signed-off-by: Thomas Vincent <thomasvincent@gmail.com> * feat(support): add diagnostics export and environment checks Signed-off-by: Thomas Vincent <thomasvincent@gmail.com> * test: cover support diagnostics export Unit-test the environment table and report redaction, and add a Docker probe that checks the report never leaks the database host, password or full RSA fingerprint. Signed-off-by: Thomas Vincent <thomasvincent@gmail.com> * fix(support): guard null snmp version and copy-status styling Signed-off-by: Thomas Vincent <thomasvincent@gmail.com> * ci: refresh security baselines Signed-off-by: Thomas Vincent <thomasvincent@gmail.com> * fix(support): keep diagnostics textarea visible on copy failure and report real extension load state Signed-off-by: Thomas Vincent <thomasvincent@gmail.com> * ci: refresh security baselines Signed-off-by: Thomas Vincent <thomasvincent@gmail.com> * fix(support): verify PHP extensions in both cli and web contexts show_tech_environment() only checked utility_php_verify_extensions()'s web flag, which seeds CLI-only extensions like pcntl as satisfied without checking whether they're actually loaded. Shell out to cli_check.php for the real CLI state and combine it with the web state the same way lib/installer.php does, so status reflects reality in both directions. Also guard the source-extraction helpers in SupportDiagnosticsTest and the e2e probe against a moved/unreadable support.php instead of letting strpos(false, ...)/eval() fail confusingly. Signed-off-by: Thomas Vincent <thomasvincent@gmail.com> * fix(support): require POST + CSRF token for maintenance lockout, compare-and-set Signed-off-by: Thomas Vincent <thomasvincent@gmail.com> * refactor(support): register process tables via support_process_tables hook Signed-off-by: Thomas Vincent <thomasvincent@gmail.com> * fix(support): guard non-array hook result and empty process-table set Signed-off-by: Thomas Vincent <thomasvincent@gmail.com> * fix(support): cast poller_interval to int in SQL and validate hook entry types Signed-off-by: Thomas Vincent <thomasvincent@gmail.com> * test(security): refresh sink/hotspot baselines for upstream line shifts Signed-off-by: Thomas Vincent <thomasvincent@gmail.com> * fix(support): reuse forced admin lookup and coerce expected state Signed-off-by: Thomas Vincent <thomasvincent@gmail.com> * test: cover support process-table hook Verify the hook drops malformed plugin definitions and keeps valid ones, and add a Docker probe that the hook returns well-formed tables against a real database. Signed-off-by: Thomas Vincent <thomasvincent@gmail.com> * test(support): guard hook extraction and tolerate empty health probe Signed-off-by: Thomas Vincent <thomasvincent@gmail.com> * ci: refresh security baselines Signed-off-by: Thomas Vincent <thomasvincent@gmail.com> * fix(support): escape plugin-contributed process-table cells Plugins can now supply the process-table SELECT fragments via the support_process_tables hook, so escape the rendered cell values, and install the e2e cleanup trap before backing up config.php. Signed-off-by: Thomas Vincent <thomasvincent@gmail.com> * ci: refresh security baselines for support.php line shift Signed-off-by: Thomas Vincent <thomasvincent@gmail.com> * fix(support): use form_selectable_ecell for process rows and cast pid in row id Plugin-contributed process-table values were only escaped via manual html_escape() calls, and the row id was built from an unsanitized pid; switch to the file's existing form_selectable_ecell() helper and cast pid to int before it goes into the id attribute. Signed-off-by: Thomas Vincent <thomasvincent@gmail.com> * test(support): harden hook-call rewrite and narrow validation comment Replace the exact-string str_replace() with a regex plus a match-count assertion so the probe fails loudly instead of silently no-op'ing if support_process_tables() formatting drifts, and correct the header comment's claim that malformed definitions can't corrupt the UNION -- the validation only checks types, not SQL validity. Signed-off-by: Thomas Vincent <thomasvincent@gmail.com> * test(support): make e2e hook extraction regex whitespace-tolerant The extraction pattern required exact spacing around the function signature; match flexible whitespace instead and fail loudly if preg_match doesn't return exactly one match. Signed-off-by: Thomas Vincent <thomasvincent@gmail.com> * test(support): add integration coverage for the process tables SQL path Unit tests eval() support_process_tables() with the plugin hook stubbed and never touch a database; the e2e Docker probe calls the same function directly but never builds or executes the UNION show_cacti_processes() runs. Add a fast, DB-only integration test that executes the real query against a live schema, skipping gracefully when no database is reachable. Signed-off-by: Thomas Vincent <thomasvincent@gmail.com> * fix(support): harden process table hook contracts * docs: add changelog entry * style: satisfy support hook fixer rules * fix(support): reconcile escaping contract and diagnostics test after merge tech_env_status() now escapes its $text argument (safe-triage's design, tested by SupportSafeTriageTest); strip the now-redundant html_escape() calls at its call sites in show_tech_environment() to avoid double encoding. Update SupportDiagnosticsTest's source-extraction marker and eval() inputs to match the merged, redact-toggle-aware report block from safe-triage. Refresh the sink inventory baseline for the resulting line shift. * fix(support): escape the PHP uname value on both redact paths Hostnames and kernel strings can carry markup, and support_redact() itself substitutes <host>, so redact first and escape the result either way. Signed-off-by: Thomas Vincent <thomasvincent@gmail.com> * test(support): locate the diagnostics report block by regex, not literal The $snmp_line assignment became a conditional and the literal start marker stopped matching, aborting the probe before it built a report. Signed-off-by: Thomas Vincent <thomasvincent@gmail.com> * docs: append the process tables entry to the feature block Signed-off-by: Thomas Vincent <thomasvincent@gmail.com> --------- Signed-off-by: Thomas Vincent <thomasvincent@gmail.com> Co-authored-by: TheWitness <thewitness@cacti.net> |
||
|
|
87c976445e |
ci: refresh security/SQL-interpolation baselines (#7562)
Signed-off-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> |
||
|
|
d9634955e1 |
ci: pin GitHub Actions to commit SHAs (#7399)
* ci: pin GitHub Actions to commit SHAs Signed-off-by: Thomas Vincent <thomasvincent@gmail.com> * test(security): refresh sink/hotspot baselines for database/rrd line shifts Signed-off-by: Thomas Vincent <thomasvincent@gmail.com> --------- Signed-off-by: Thomas Vincent <thomasvincent@gmail.com> |
||
|
|
c3df72d11e |
fix(db): map empty value to NULL for nullable numeric columns in sql_save (#7337)
* fix(db): map empty value to NULL for nullable numeric columns in sql_save Nullable numeric columns previously coerced an empty incoming value to 0 instead of SQL NULL; the NOT-NULL/no-default branch is unchanged since it has no safe NULL fallback. Signed-off-by: Thomas Vincent <thomasvincent@gmail.com> * fix(db): preserve numeric zero when mapping empty to NULL in sql_save Signed-off-by: Thomas Vincent <thomasvincent@gmail.com> * test(security): refresh sink/hotspot baselines for upstream line shifts Signed-off-by: Thomas Vincent <thomasvincent@gmail.com> --------- Signed-off-by: Thomas Vincent <thomasvincent@gmail.com> Co-authored-by: TheWitness <thewitness@cacti.net> |
||
|
|
59bf392e41 |
fix(rrd): drop duplicate --slope-mode emission (#7336)
* fix(rrd): drop duplicate --slope-mode emission - forward-ports the #7183 hunk from 1.2.x; the switch case already emits it Signed-off-by: Thomas Vincent <thomasvincent@gmail.com> * test(rrd): guard file_get_contents result before substr_count Signed-off-by: Thomas Vincent <thomasvincent@gmail.com> * test(security): refresh sink/hotspot baselines for upstream line shifts Signed-off-by: Thomas Vincent <thomasvincent@gmail.com> --------- Signed-off-by: Thomas Vincent <thomasvincent@gmail.com> Co-authored-by: TheWitness <thewitness@cacti.net> |
||
|
|
04f0088427 |
fix: memoize db_get_table_column_types to cut redundant SHOW COLUMNS (#7381)
* fix: memoize db_get_table_column_types to cut redundant SHOW COLUMNS sql_save() calls db_get_table_column_types() for every write, and it ran SHOW COLUMNS with no caching, so bulk operations re-query the same tables hundreds of times (creating 30 graphs issued ~540 identical lookups). Cache the result per request and clear it in the column-mutating DDL helpers so the installer/upgrade path stays correct. Signed-off-by: Thomas Vincent <thomasvincent@gmail.com> * test: cover db_get_table_column_types memoization and invalidation Prove the cache is transparent (identical output cached vs fresh), can be cleared, keys per connection, and that every column-mutating DDL helper clears it so sql_save() sees the new schema after an ALTER. Signed-off-by: Thomas Vincent <thomasvincent@gmail.com> * test(security): refresh sink/hotspot baselines for upstream line shifts Signed-off-by: Thomas Vincent <thomasvincent@gmail.com> --------- Signed-off-by: Thomas Vincent <thomasvincent@gmail.com> Co-authored-by: TheWitness <thewitness@cacti.net> |
||
|
|
bc12e4492e |
Fix TypeError in boost_graph_set_file when sending reports (#7268)
* Fix TypeError in boost_graph_set_file when sending reports reports.php passes '' for rra_id; normalize to int|null in rrdtool_function_graph before it reaches boost. Ref #7214 Signed-off-by: Thomas Vincent <thomasvincent@gmail.com> * Restrict rra_id normalization to whole numbers Signed-off-by: Thomas Vincent <thomasvincent@gmail.com> * ci: bound page crawler network retries * test: match rra_id source anchors to ctype_digit or filter_var Signed-off-by: Thomas Vincent <thomasvincent@gmail.com> * test: accept ?int spelling of the rra_id contract Signed-off-by: Thomas Vincent <thomasvincent@gmail.com> * ci: refresh sink and hotspot baselines for line shifts Signed-off-by: Thomas Vincent <thomasvincent@gmail.com> --------- Signed-off-by: Thomas Vincent <thomasvincent@gmail.com> Co-authored-by: TheWitness <thewitness@cacti.net> |
||
|
|
58fe3db379 |
test(security): refresh sink/hotspot baselines for auth/rrd line shifts (#7367)
Signed-off-by: Thomas Vincent <thomasvincent@gmail.com> |
||
|
|
ccfe04a20c |
Harden remote-agent FCRDNS and basic auth identity (#7324)
* Harden remote agent and basic auth identity handling * fix(auth): gate forwarded identity headers behind trusted proxies Signed-off-by: Thomas Vincent <thomasvincent@gmail.com> * chore: Update CHANGELOG for new feature, and reorder per rules * chore: Add the issue number to CHANGELOG * chore: Update hotspots --------- Signed-off-by: Thomas Vincent <thomasvincent@gmail.com> Co-authored-by: TheWitness <thewitness@cacti.net> |
||
|
|
805f48aa48 | chore: Missed one PHPStan Issue (#7348) | ||
|
|
d00906f459 | security: add sink-inventory + architectural-hotspot CI gate (develop) (#7243) |