Repository navigation
Harden the collector, cache refresh and WMI pages - #44
Open
somethingwithproof wants to merge 26 commits into
Open
somethingwithproof wants to merge 26 commits into
somethingwithproof wants to merge 26 commits into
Conversation
… wait 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>
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>
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>
Signed-off-by: Thomas Vincent <thomasvincent@gmail.com>
Signed-off-by: Thomas Vincent <thomasvincent@gmail.com>
…log is not writable Signed-off-by: Thomas Vincent <thomasvincent@gmail.com>
Signed-off-by: Thomas Vincent <thomasvincent@gmail.com>
Signed-off-by: Thomas Vincent <thomasvincent@gmail.com>
…parent 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>
…scripts 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>
Signed-off-by: Thomas Vincent <thomasvincent@gmail.com>
|
2 of 6 tasks
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.



Description
Fixes found in a review of the plugin, with end-to-end coverage for every changed path. The branch contains security-relevant changes: the Query Tool no longer stores or redisplays the password it is given and now runs its query only for POST requests, and account saving validates its input before writing. No fix here is a pre-authentication issue; each needs a logged-in user or access to the Cacti server.
The commits are independent and can be reviewed one at a time. Each fix has its own regression test and CHANGELOG line.
Related Issue
No existing issue covers these. Open issue #15 (Windows hosts) is not touched.
Motivation and Context
Per-finding summary:
wmi_processesrow behind, so the master poller waited forever and every later run exited with "still running". Rows older than one hour are now deleted at start, and the master stops waiting for its children after one hour.present = 0on every poll, but the delete that follows only ran when the host did not exist. Rows for instances that disappeared were never removed, and a failed query left every row flagged. The flag, insert and delete now happen only after a successful refresh.N/Aand only the last row survived. The index is now built from every listed column.root\\CIMV2is single-quoted for wmic and arrived with two backslashes. Doubled backslashes are collapsed to one.wmi_tools.php?action=queryran for a GET request, so credentials could travel in a URL, an access log or the browser history, and the CSRF token did not apply. The query action now runs only for POST; any other method gets the empty form and runs nothing. The Run button already submits by POST, so the form works as before.sql_save. The input is validated first.=in&id=.last_startedwas written from PHP's clock and compared with the database'sNOW(). When PHP and the database session use different time zones, devices were polled again on every cycle (or delayed by the offset). The start time now comes from the database clock. The CI job runs withAmerica/New_Yorkfor PHP and a UTC database, which exposed it.wmi_processessetting was empty the master used its default only for the stored setting, not for its launch loop, and waited forever. It now uses the default.Not changed, and why:
--password=): visible to local users inps. The repository gives no confirmed wmic option to pass credentials another way, so this needs a decision (an authentication file if the installed wmic supports one, or documenting the exposure). The PowerShell transport already avoids it.wmi_script.php,script/wmi-script.php, the Exchange resource XML, thepath_wmisetting): the intended install location of the scripts is not clear from the repository, so the paths and the setting are left for the maintainers to decide.compatandversion: the right floor and release number are not settled by the README, CHANGELOG and tests.Nonestill shares one index. Both need a product decision.How Has This Been Tested?
Environment: PHP 8.4.27 with Xdebug in Docker, MariaDB 11.8.9 in a throwaway container, Cacti 1.2.x (1.2.32) and Cacti develop (1.3.0) checkouts with
composer installas the workflow does it. The install state is simulated where noted below.Test layers:
tests/E2E, which need an installed Cacti and its database (the CI workflow provides both and setsWMI_E2E=1on the Pest step; elsewhere they skip with a reason):CollectorFlowTestruns the cache refresh and the realpoller_wmi.phpmaster and child against the database with a fake wmic: first refresh, dropped instances, failures (partial output, empty output, silent non-zero exit, header only), single, duplicate,None, composite and unknown primary keys, namespace forms, missing account, query or host, a live lock, a stale lock, an empty or missing concurrency setting, a child that cannot start (the master is killed by a timeout, the lock survives, and a later run recovers once the lock is old), down and disabled devices, a disabled plugin, a child run alone, and bad command lines.PagesFlowTestsends real HTTP requests to PHP's built-in server serving Cacti, logged in as real users: anonymous and no-WMI-realm refusals, account create, edit and reject cases (mismatch, empty password, empty name or username, over-long password, 64-character boundary), no CSRF token, GET, pager totals with filters across pages, bulk delete, query create and edit, the Query Tool (rows, escaped output, missing fields, failed host, namespace forms, password submitted twice never stored or echoed, no CSRF, no realm, GET refused), and the Device Template hook.EntryScriptsTestchecks that every helper undertests/E2Edoes nothing over the web.tests/E2E/coverage_prepend.phprecords executed lines whenWMI_E2E_COVnames a directory, andtests/bin/e2e-coverage-report.phpmerges that with the unit Clover report and lists each changed line with the suites that ran it. Every changed executable line ran in at least one suite.Executed locally:
php -lon every changed PHP file,tests/bin/validate-manifest.php,tests/bin/check-i18n-pot.php(no string changes): pass.WMI_E2E=1: 157 passed, 608 assertions passed.composer installas the workflow does it, the pinned Pest harness for 1.2.x,install_cacti,plugin_manage --install --enableon a bare Cacti with MariaDB and no other plugin, the log-writable step, then Pest as a non-root user withWMI_E2E=1, then the workflow's log grep): 157 passed, 1 warning (the existing PruneFilesTest unlink warning), 609 assertions passed on both Cacti 1.2.x and develop, including with PHP set toAmerica/New_York,Asia/KolkataandUTCand with eight busy containers competing for the CPU. The log grep found nothing.php tests/bin/patch-coverage.php <clover> origin/develop 100: 100.00 percent (32 of 32 changed measured lines);tests/bin/e2e-coverage-report.phpreports every changed executable line in the four unmeasured entry points as executed.The install check also covers the "plain Cacti" requirement: INFO, the install hooks and the schema are unchanged, and nothing here adds a
requiresentry or a dependency.Whole-file line coverage of the changed files, before (origin/develop, unit tests only) and after (unit plus end-to-end):
Not run: the GitHub workflow itself, PHP 8.2 and 8.3 locally, the Cacti installer to completion (the image lacks extensions it checks for, so the install state was finished by hand after
plugin_manageran for real), PHPStan and the coding-standards jobs beyond what origin/develop already reports, Sonar duplication, a wmic that hangs, a real Windows host or wmic binary, and the pages in a browser. The one-hour lock deadline is covered by unit tests only.Performance
Fixture: a fake wmic printing 3,000 and 10,000 rows of six columns (18,000 and 60,000 cache rows), one host, one query. Environment: Docker on a laptop, PHP 8.4 with Xdebug loaded, MariaDB 11.8.9 in a second container. Median of 5 runs per case, same fixture for origin/develop and this branch. Query counts are the difference in the session
Questionscounter.Cache refresh, unchanged data (steady state), 10,000 rows (two runs of each):
The refresh is within run-to-run noise. The branch issues one more query (the delete now runs on every successful refresh). At 3,000 rows: 525 ms vs 520 ms, 94 vs 95 queries.
Cache refresh when wmic fails, 10,000 rows: develop 35 ms and 4 queries, leaving 60,000 of 60,000 rows flagged
present = 0; branch 1 ms and 3 queries, leaving 0 flagged. This is a correctness change; the time saved is the skipped update of the whole table.List pager: the Queries page gains one
COUNT(*)with the search filter. On 20,000 rows with a filter matching 10 percent it took about 2.0 ms against about 2.7 ms for the page query itself. The Accounts page replaces its unfiltered count with a filtered one at the same cost.Poller start time: one extra
SELECT NOW()per device run. Collector wait loop and stale-lock delete: not benchmarked. By reading, the master issues one extraDELETEper run, and the loop issues the same oneSELECT COUNT(*)per two-second iteration as before. Not measured: web request timings of the three pages, memory of the full master run, the cost under many devices, or cachegrind profiles.Types of changes
Checklist:
Rollback: each commit reverts cleanly on its own. No schema change. The collector lock timeout is a function default (3600 seconds) in
includes/functions.php. Operators who relied onwmi_processesrows lasting longer than one hour will see them expire. A script that calledwmi_tools.php?action=queryby GET will now get the form instead; the Run button is unaffected.