Repository navigation
Harden panel actions and chart output, and fix review findings - #432
Open
somethingwithproof wants to merge 24 commits into
Open
somethingwithproof wants to merge 24 commits into
somethingwithproof wants to merge 24 commits into
Conversation
Labels such as device template names were pasted into the inline chart script. Signed-off-by: Thomas Vincent <thomasvincent@gmail.com>
The permission was only applied when the dashboard rendered. Signed-off-by: Thomas Vincent <thomasvincent@gmail.com>
GET requests no longer remove panels, change settings or switch login options. Signed-off-by: Thomas Vincent <thomasvincent@gmail.com>
A system panel with no data row passed NULL to get_panel() and stopped the poller. Signed-off-by: Thomas Vincent <thomasvincent@gmail.com>
The delete used a column the table does not have. Signed-off-by: Thomas Vincent <thomasvincent@gmail.com>
The favourite_graph grant was appended to the fetched rows instead of each user. Signed-off-by: Thomas Vincent <thomasvincent@gmail.com>
REPLACE reset the shared flag and created dashboards the user did not have. Signed-off-by: Thomas Vincent <thomasvincent@gmail.com>
The condition was missing parentheses, so the realm was only checked on the main poller. Signed-off-by: Thomas Vincent <thomasvincent@gmail.com>
The cleanup compared the row id with the panel name column and never matched. Signed-off-by: Thomas Vincent <thomasvincent@gmail.com>
The preference was left cleared if get_allowed_devices() threw. Signed-off-by: Thomas Vincent <thomasvincent@gmail.com>
Joins without a user condition repeated each log row once per user holding a permission. Signed-off-by: Thomas Vincent <thomasvincent@gmail.com>
Adds the POST cases next to the GET cases that are refused. Signed-off-by: Thomas Vincent <thomasvincent@gmail.com>
A panel string now also appears in the reload and detail endpoints. Signed-off-by: Thomas Vincent <thomasvincent@gmail.com>
They are skipped unless INTROPAGE_E2E_URL and INTROPAGE_E2E_DSN are set. Signed-off-by: Thomas Vincent <thomasvincent@gmail.com>
Signed-off-by: Thomas Vincent <thomasvincent@gmail.com>
…d test Signed-off-by: Thomas Vincent <thomasvincent@gmail.com>
Signed-off-by: Thomas Vincent <thomasvincent@gmail.com>
An unknown id made get_panel() log a warning and try to create a row. Signed-off-by: Thomas Vincent <thomasvincent@gmail.com>
Covers panel access, POST only actions, names, charts, poller, groups, thold events and upgrade. Signed-off-by: Thomas Vincent <thomasvincent@gmail.com>
It reads the end-to-end dumps and the unit XML coverage and lists changed lines no test reaches. Signed-off-by: Thomas Vincent <thomasvincent@gmail.com>
A step serves the installed Cacti with the built-in server and makes cacti.log writable for the test run. Signed-off-by: Thomas Vincent <thomasvincent@gmail.com>
Graph pages only call the hook for graphs with archives, so a CLI runner calls it for a seeded graph. Signed-off-by: Thomas Vincent <thomasvincent@gmail.com>
The report now skips bare closing braces and prints the source of unreached branches. Signed-off-by: Thomas Vincent <thomasvincent@gmail.com>
Signed-off-by: Thomas Vincent <thomasvincent@gmail.com>
|
2 of 6 tasks
1 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.



Title
Fix permission, CSRF and chart-label handling and several correctness bugs found in a code review
Description
This PR contains the fixes from a read-through of develop at 13159fc, plus end-to-end tests that exercise them against a running Cacti. Some of the fixes are security-relevant and are called out below. Each fix is its own commit with its own regression test.
Security-relevant fixes:
Correctness fixes:
DELETE ... WHERE id = ?on a table keyed byuser_group_id.REPLACE, which reset the shared flag, and could create dashboards the user did not have. It now updates the name of an existing dashboard.intropage_get_allowed_devices()clears the user'shide_disabledsetting for the lookup and now restores it in afinallyblock.user_auth_permswithout a user condition, so each log row was repeated once per user holding a matching permission.get_panel()before checking anything; for an unknown id that logged a PHP warning into the response and tried to create a row. The row is now looked up first.Not changed:
intropage_add_panel()andintropage_remove_panel()in setup.php cannot work against the current schema.intropage_add_panel()writesfile,has_detailandrefresh_interval, whichplugin_intropage_panel_definitionno longer has, andintropage_remove_panel()usesDROP ?, which cannot bind an identifier. Nothing in the repository calls them and the panel library README describesregister_<name>()as the way to add panels. Removing a public function is a decision for the maintainers. Options: leave as is, deprecate with a log message, or remove in a later release.Found while writing the end-to-end tests and left alone, because they are outside the lines this PR changes:
favgraphsets the priority of a new favourite toMAX(priority) + 1for the user. For a user with no panel rows that is NULL, the insert fails, and the dashboard link is then written with panel id 0. The tests seed a panel first.AUTH ERROR: Invalid Cacti User ID ... is being used in a permission that does not existon every cycle. Rows of deleted users are only removed by theuser_removehook.sql_save()logsDBCALL ERROR: Column: height contains an invalid value: normalfor this plugin's panel rows. It also happens without this PR.Related Issue
No tracking issue. These come from a code review of develop. Issue #366 is a broader automated audit and is not closed by this PR.
Motivation and Context
The permission check and the POST requirement close gaps that were reachable by any authenticated user with the viewer realm. The poller fix matters because a TypeError in the poller makes Cacti disable the plugin; this was reproduced on a live Cacti. The end-to-end tests exist because most of these paths (display.php, poller_intropage.php, the hooks, the upgrade) cannot be loaded by the stub-based unit suite.
How Has This Been Tested?
Unit and regression tests (Pest, the repository's stub bootstrap):
Tests: 128 passed, 103 skipped, 1 warningwithout the end-to-end environment (the skips are the end-to-end tests and one test that was already skipped; the warning is anrmdirnotice that the baseline run also has).tests/bin/patch-coverage.phpagainst origin/develop reports 100.00 percent (89/89 changed lines); display.php and poller_intropage.php are on its existing allowlist, which is why they also have end-to-end tests.check-i18n-pot.php,validate-manifest.phpandphp -lon every PHP file pass.End-to-end tests (
tests/Integration/IntropageHttpTest.php, 102 tests): real login, real CSRF tokens, real requests to intropage.php anduser_group_admin.php, the real poller script, the realgraph_buttonshook through Cacti's plugin API, the real upgrade code against the Cacti database, and the real database. They run whenINTROPAGE_E2E_HOSTandINTROPAGE_E2E_DB_NAMEare set (plus the otherINTROPAGE_E2E_*variables described at the top of the file) and are skipped otherwise. The workflow now sets them, makescacti/logwritable for the runner, and serves the installed Cacti with PHP's built-in server as the web user before the Pest step, so they run in CI on every job. Paths covered:hide_disabledResults per Cacti core, PHP 8.3, MariaDB 11.8.9 in Docker, full Pest suite with the end-to-end environment (the order the workflow uses: install the plugin with
plugin_manage, serve Cacti, run Pest, runpoller.php --force, scan cacti.log):FATAL:|ERROR:|WARNING:in cacti.logLimits of that check: the Docker image has PHP 8.3 only (the workflow matrix is 8.2 to 8.4), the installer step was replaced by loading
cacti.sqland setting the version because rrdtool and fping are not in the image, and the tests ran as root instead of therunneruser (the log permission step is untested). Pest on 1.2.x used develop'sinclude/vendorcopied over the 1.2.x vendor directory instead of the workflow's composer install.The same end-to-end file run against the unfixed origin/develop code: 53 failed, 49 passed on both cores. The 49 that pass are the Cacti core guarantees (CSRF, login) and the paths that must keep working. A few of the 53 are knock-on failures after the poller TypeError makes Cacti disable the plugin.
Traceability:
tests/bin/e2e-coverage-report.phptakes Xdebug dumps written bytests/Integration/coverage-prepend.phpwhile the real entry points run (one directory per test) and the PHPUnit XML coverage of the unit tests, and lists for every added or modified executable line the tests that run it. For this branch every changed line is run by at least one unit or end-to-end test. Whole-file coverage of the changed files, line and Xdebug branch coverage, end-to-end run on develop:Changed-line gaps in the end-to-end run, covered by unit tests instead: the Thold event queries (the Thold plugin is not installed; the real-database test runs the panel's own SQL in the Pest process, not through the web server), the non-numeric branch of
intropage_chart_column(), and the remote poller branches of the console_after condition (the stack runs as poller 1). Not tested: the JavaScript in includes/intropage.js (no JavaScript harness in the repository; the end-to-end tests cover the server side of the same requests).Rules for the test helpers: nothing uses
eval, a weak hash or anhttp://literal, and no test builds SQL from strings (passwords usepassword_hash, template hashes use random bytes, the address comes fromINTROPAGE_E2E_SCHEMEandINTROPAGE_E2E_HOST).tests/ships inside the plugin directory on a git clone, so every runnable helper there (the end-to-end file,coverage-prepend.php,upgrade-runner.php,hook-runner.php, the report script) exits with 404, or silently under the CLI, unless started by the test process with its environment, takes every path from environment variables, writes only below the directory named there under a validated name, and never includes a path from a request.tests/Security/TestFilesInertTest.phpruns each of them without the environment and expects no output and no files. The upgrade runner refuses any tree that is not a throwaway copy below the system temp directory.Performance
Fixture: empty Cacti develop install, no devices, one user, one dashboard with one panel. Cacti and MariaDB on one Docker network; PHP's built-in server. Times are noisy at this size and are not a claim of speedup or slowdown.
Statements per request, counted with the MariaDB general log for one panel reload (origin/develop, then this branch): 131, then 133 (131 plus a group-membership query and a permission query).
Com_select/insert/update/delete/replacedeltas over 25 requests, three alternating rounds:The two extra statements on reload come from the new permission check, and the same cost applies to the detail request (which now also reads the row once before
get_panel()). Add panel has the same count because the permission result is cached for the rest of the request and the dashboard render that follows reuses it.Median wall time per request over the same rounds stayed within run-to-run noise (reload 6.9 to 9.0 ms before, 6.9 to 7.2 ms after; dashboard page 22.1 to 27.9 ms before, 22.6 to 24.6 ms after). Peak memory and server CPU per request were not measured.
Chart columns: building 20 columns with
json_encodecosts 3.2 microseconds instead of 1.6 (median of 7 runs of 20000, PHP 8.4, CPU time), with the same peak memory. This runs once per chart refresh.Not measured: poller duration with many users and panels (the change is one integer cast per row), the threshold event query plan (fewer rows are joined, but no Thold data was available), the end-to-end suite's time in CI (20 seconds locally), and Xdebug cachegrind.
Rollback
Revert the merge commit. No schema change and no data migration. The permission and POST checks change behaviour for anyone relying on GET links to dashboard actions or on reloading panels they were not granted; both were unintended. The favorite-graph button on Cacti's graph pages now posts from a small inline script (CSP nonce applied when Cacti provides one). The workflow change can be reverted on its own: it adds environment variables, a step that makes
cacti/logwritable and a step that serves Cacti on 127.0.0.1:8080.Types of changes
Checklist