fix: validate the three values that reach SQL as text - #375
Open
somethingwithproof wants to merge 1 commit into
Open
fix: validate the three values that reach SQL as text#375somethingwithproof wants to merge 1 commit into
somethingwithproof wants to merge 1 commit into
Conversation
There was a problem hiding this comment.
Pull request overview
This PR adds defense-in-depth validation for a small set of values that were previously interpolated into SQL as text (rather than being bound parameters), ensuring they are constrained at the point of use even if upstream assumptions change.
Changes:
- Validate and constrain the
CHECK TABLElevel inpanellib/analyze.phpto a fixed allowed set with a safe fallback. - Skip non-numeric dashboard IDs derived from POST keys in
include/functions.phpbefore writing to the dashboard table. - Validate and safely quote column identifiers before issuing
ALTER TABLE ... DROP COLUMNduring upgrades ininclude/database.php, logging and skipping unexpected names.
Reviewed changes
Copilot reviewed 3 out of 3 changed files in this pull request and generated 1 comment.
| File | Description |
|---|---|
panellib/analyze.php |
Whitelists the CHECK TABLE level string before concatenating into SQL. |
include/functions.php |
Adds validation for dashboard IDs derived from POST field names prior to DB writes. |
include/database.php |
Adds identifier validation + quoting for dynamic column drops during schema upgrade. |
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
Comment on lines
137
to
+143
| if (strpos($var, 'name_') !== false) { | ||
| $dashboard_id = str_replace('name_', '', $var); | ||
|
|
||
| if (!is_numeric($dashboard_id)) { | ||
| continue; | ||
| } | ||
|
|
None is reachable from a request today, but each is spliced into a statement rather than bound, so the guard belongs at the point of use. Signed-off-by: Thomas Vincent <thomasvincent@gmail.com>
somethingwithproof
force-pushed
the
fix/sql-identifier-validation
branch
from
August 21, 2026 19:58
ba61877 to
1a527e9
Compare
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.
A SQL review of the plugin found no injection. Every request-carried value reaches the database through a prepared statement, and the ~99 dynamic queries resolve to literals, integer primary keys, or
db_qstr().Three values reach SQL as text rather than as a bound parameter. None is request-reachable today, so this is defence in depth, not a fix for a live bug. Each guard sits at the point of use so it holds regardless of how the value got there.
include/database.phpinterpolated a column name intoALTER TABLE ... DROP COLUMNunquoted. The name comes fromSHOW COLUMNS, but the columns themselves are created from panel ids viaapi_plugin_db_add_column(), so the value is only as constrained as that path stays. It is now validated and backtick-quoted, and a rejected name is logged instead of executed.Core's
db_is_safe_identifier()is the right helper, but it postdates thecompat = 1.2.17this plugin declares (added in Cacti/cacti#7235), so it is called throughfunction_exists()with an inline fallback. The plugin already uses that pattern forauth_augment_roles().panellib/analyze.phpputread_config_option('intropage_analyse_db_level')straight intocheck table ... <level>. It is adrop_arrayover five constants ininclude/variables.php, so the form constrains it, but the value is read back as text. The allowed list is now authoritative at the point of use, withCHANGEDas the fallback.include/functions.phpderived$dashboard_idfrom a$_POSTkey viastr_replace('name_', '', $var). It was already bound, so there was no injection, but any string could become a dashboard id. Non-numeric keys are now skipped.Behaviour:
php -lclean on all three files. No behaviour change for valid input.Checked against the other open PRs before writing this: #373 and #374 touch two of the same files but not these lines, and #367's
include/database.phpchange is a blank-line removal.