Repository navigation
Harden alert commands, upgrades and dashboards, and fix review findings - #422
somethingwithproof wants to merge 31 commits into
Conversation
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>
…ules 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>
Signed-off-by: Thomas Vincent <thomasvincent@gmail.com>
Signed-off-by: Thomas Vincent <thomasvincent@gmail.com>
…al, alerts and imports 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>
…paths Signed-off-by: Thomas Vincent <thomasvincent@gmail.com>
Signed-off-by: Thomas Vincent <thomasvincent@gmail.com>
TheWitness
left a comment
There was a problem hiding this comment.
Some questions and general comments. Since we are making all these changes, we should ensure that the ALERT variables are in the environment so that we don't have to pass to the script. It may be there already, I did not do a review of anything but the diff.
| $command = str_replace('<SEVERITY>', cacti_escapeshellarg($severities[$alert['severity']]), $command); | ||
|
|
||
| return $command; | ||
| // One pass: a substituted value that contains another token must not be |
There was a problem hiding this comment.
This stuff really belongs in the environment of the script so that we don't have to pass it in. It was a big mistake at the time. Everywhere else I have done it just puts the variables into the environment.
| syslog_ensure_share_tables(); | ||
| syslog_ensure_dashboard_tables(); | ||
| } | ||
| // Rule owners are Cacti usernames, which allow 50 characters. The marker is |
There was a problem hiding this comment.
The db_update_table should handle this automatically when it's called on upgrade. I'm wondering why you increased user to 50 chars though?
| FROM `$syslogdb_default`.`syslog` AS syslog | ||
| WHERE $predicate) | ||
| WHERE $predicate | ||
| GROUP BY bucket) |
| FROM `$syslogdb_default`.`syslog_removed` AS syslog | ||
| WHERE $predicate) | ||
| WHERE $predicate | ||
| GROUP BY bucket) |
| FROM `$syslogdb_default`.`syslog` AS syslog | ||
| WHERE $predicate) | ||
| WHERE $predicate | ||
| GROUP BY label) |
| FROM `$syslogdb_default`.`syslog_removed` AS syslog | ||
| WHERE $predicate) | ||
| WHERE $predicate | ||
| GROUP BY label) |
…422) (#424) * fix: look up A and AAAA records and accept IPv6 sender addresses Signed-off-by: Thomas Vincent <thomasvincent@gmail.com> * test: populate a valid alert rule in CI and assert that it fires Signed-off-by: Thomas Vincent <thomasvincent@gmail.com> --------- Signed-off-by: Thomas Vincent <thomasvincent@gmail.com>
|




Title
Fix alert, removal, dashboard and import defects found in a code review of develop
Description
This change fixes twelve defects found in a review of the syslog processing path, the rule pages and the install and upgrade code. It contains security-relevant fixes: the alert command token substitution (first item below) and the import validation changes. The commits are atomic, one fix per commit, each with its own tests.
Per-finding summary:
includes/functions.php,alert_replace_variables). Tokens were replaced one after another, so a substituted value that contained another token name (for example a device name sent by a remote syslog sender) was expanded a second time, inside the quotes of the first value. The command string is now built in onestrtr()pass, so substituted text is never scanned again. Affects device-level alerts whose command uses both the host name and message tokens.includes/schema.php,includes/installer.php). The default upgrade choice dropped and recreated the alert, removal and report rule tables, although the advisor says rules are kept. It now discards stored messages only. The advisor text now says so, andcacti.potis updated.includes/functions.php). Every*in the rule SQL was rewritten when the grouped query was built, which broke raw SQL rules such asmessage REGEXP 'a.*b'. Only the leadingSELECT *is rewritten now.includes/functions.php). All enabled rules are counted in one statement, so one rule that does not run made every alert count zero. If that statement fails, each rule is now counted on its own and only the failing rule is skipped.syslog_alerts.php). Imported rules are checked with the same compile and test query as the editor, and anidin the file can no longer choose the row to overwrite. A file without a hash no longer reads an undefinedid.includes/functions.php). The counter used for the limit was only incremented after the loop, so the limit never applied. Threshold alert messages now stop at the configured number of rows. The query that fetches the matches is not limited, because the breach marking needs every matching message.lib/syslog_dashboard.php). The two branches of the UNION had an aggregate and no GROUP BY. Panels showed one arbitrary label with the grand total, or one time bucket, and the queries fail underONLY_FULL_GROUP_BY. Each branch is now grouped.includes/functions.php). For host and program rules, and SQL rules that name those columns, the moving INSERT joined the reference tables and used unqualifiedhostandprogram, which MySQL and MariaDB reject as ambiguous. The WHERE clause now filters the staging rows in a derived table before the join.includes/functions.php,syslog_removal.php). "Reprocess" on one removal rule ran every enabled rule over the stored history, including delete rules.syslog_remove_items()takes an optional rule id and reprocess passes it.includes/schema.php).userwasvarchar(32)in the rule, report, template, dashboard and device rule tables, while Cacti allows 50 character user names. New installs createvarchar(50). Existing installs are altered once during the schema check; the marker rowschema_user_widthinsyslog_statusis written only after all four tables were altered, and a failed alter makes the check fail so it is retried.includes/functions.php). Lookups were unbounded, so a flood of distinct sender-chosen names could stall a processing run. Lookups stop afterSYSLOG_RESOLVE_BUDGET_SECONDS(60) per call; names that were not looked up are left as received and are never prefixedunresolved-. A warning is logged when the budget is spent.syslog_saved_searches.php). Imported templates are now parsed with the logical search parser, and the record type and grouping values are validated, as in the editor and the save action.Test tooling in this change:
tests/regression/review_fixes_database_test.phpruns the SQL-level checks against a real MariaDB (dashboard GROUP BY under both SQL modes, archiving rules, reprocess, rule isolation, truncate scope, owner column widening). It is added to the CI step that runs the other database regression checks. It exits with 404 unless run from the CLI with the test database settings, and a unit test covers both cases.eval().Related Issue
None filed. These were found in a review of develop. Issue numbers can be added to the CHANGELOG entries if maintainers want them.
Motivation and Context
Items 3 to 5, 7, 8 and 9 give wrong results or silently skip work. Item 2 deletes configuration during a documented upgrade path. Item 1 is a command-injection path reachable by a remote syslog sender when an administrator uses both tokens in a device-level alert command. Items 10 and 11 are compatibility and reliability issues.
How Has This Been Tested?
Executed locally, on the head of this branch rebased on
origin/develop(7635b1a):docker run --rm -v <cacti>:/w -w /w prof-php sh -c 'php -d xdebug.mode=coverage include/vendor/bin/pest --configuration=plugins/syslog/phpunit.xml --coverage-clover=plugins/syslog/coverage/clover.xml'on PHP 8.4.27: 531 passed, 1 failed. The failing test isQueryBuilderGuardTest("fails each filter compile closed when lib/QueryBuilder.php is missing"); it fails the same way onorigin/developin this environment (its subprocess check returns null) and is not touched by this change. It could not be confirmed here whether CI passes it.php tests/bin/patch-coverage.php coverage/clover.xml origin/develop 100reports 100.00% (50 of 50 measured changed lines).includes/installer.php,includes/schema.php,syslog_alerts.php,syslog_removal.phpandsyslog_saved_searches.phpare not in Clover and are covered by the existing allowlist; their changes are exercised by the unit tests and the database and E2E runs below.origin/developsources: 20 of the 26 new unit tests that exercise changed behavior fail there, and the rest are guards for behavior that must not change.review_fixes_database_test.phppasses with 24 checks and reports 21 failures againstorigin/develop.cli_schema_upgrade_test.php,threshold_recovery_database_test.phpandincoming_preservation_database_test.phppass;quality_must_fix_database_test.phppasses inside the Cacti container (it needs a Cacti tree).tests/E2E/playwright/run-e2e.shagainst a Cacti core1.3.0 - Devtree, PHP 8.4, MariaDB 11.8.9 and the Playwright 1.52.0 image: 16 of 16 pass. Checks 11 to 16 all fail against theorigin/developsources.requiresentries:php cli/plugin_manage.php --plugin=syslog --install --enable --allpermsinstalls and enables syslog 4.11, creates all plugin tables, andcacti.loghas no FATAL, ERROR or WARNING lines. An upgrade-style run (owner columns set back tovarchar(32), marker removed, thensyslog_process.php) widens all six tables and writes the marker. The CI-style processor run (syslog_process.php --debug) logsSYSTEM SYSLOG STATSwith no error lines.php -lon every changed PHP file with PHP 8.4, andphp tests/bin/check-i18n-pot.php origin/develop.Not run locally: the GitHub Actions workflow itself (PHP 8.2 and 8.3 legs, Cacti 1.2.x core, the SNMP and poller steps, Sonar), and anything on MySQL. The E2E run used Cacti core 1.3.0 - Dev, not 1.2.x. PHP 8.2 and 8.3 were not run; the code uses no syntax newer than PHP 8.0.
Performance
Fixture: MariaDB 11.8.9 in Docker Desktop on an Apple silicon Mac, default server settings, PHP 8.4.27 with Xdebug off. 200,000 rows in
syslog, 50,000 insyslog_removed, 100,000 staged rows insyslog_incoming, 200 hosts, 50 programs. Five runs per case on a freshly built database; the table shows medians. Statements are server-side counts fromSHOW SESSION STATUS LIKE 'Questions'. Peak PHP memory was 4 MB in every case.What the numbers say:
Not measured: hostname lookup timing (it depends on the resolver; the added check is one clock read per name), alert e-mail and command paths, the added validation statement per imported alert or template (one extra test query each, by inspection), the extra
SELECTfor the owner column marker (one statement per schema check, by inspection; the benchmark case for it returned different results on the two trees and is not reported), larger tables, remote collectors, page rendering, and a MySQL server.Risk and rollback
ALTER TABLE ... MODIFY COLUMNon four small rule tables once per install. Widening avarcharis a metadata-only change on InnoDB for these sizes; it needs the usual ALTER privilege the plugin already uses. If it fails, the schema check fails and is retried on the next request; the marker row is not written.unresolved-prefix.syslog_statuskeeps an unusedschema_user_widthrow.Screenshots (if appropriate):
Not applicable.
Types of changes
Checklist: