Skip to content

fix: validate the three values that reach SQL as text - #375

Open
somethingwithproof wants to merge 1 commit into
Cacti:developfrom
somethingwithproof:fix/sql-identifier-validation
Open

fix: validate the three values that reach SQL as text#375
somethingwithproof wants to merge 1 commit into
Cacti:developfrom
somethingwithproof:fix/sql-identifier-validation

Conversation

@somethingwithproof

Copy link
Copy Markdown
Member

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.php interpolated a column name into ALTER TABLE ... DROP COLUMN unquoted. The name comes from SHOW COLUMNS, but the columns themselves are created from panel ids via api_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 the compat = 1.2.17 this plugin declares (added in Cacti/cacti#7235), so it is called through function_exists() with an inline fallback. The plugin already uses that pattern for auth_augment_roles().

panellib/analyze.php put read_config_option('intropage_analyse_db_level') straight into check table ... <level>. It is a drop_array over five constants in include/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, with CHANGED as the fallback.

include/functions.php derived $dashboard_id from a $_POST key via str_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:

ident 'panel_system'                accepted
ident 'a`b'                         rejected
ident 'x; DROP TABLE y'             rejected
level 'EXTENDED'                 -> EXTENDED
level 'EXTENDED; DROP TABLE host'-> CHANGED (fallback)
dashboard '3'                       processed
dashboard '1 OR 1=1'                skipped

php -l clean 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.php change is a blank-line removal.

Copilot AI lite review requested due to automatic review settings August 20, 2026 06:21

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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 TABLE level in panellib/analyze.php to a fixed allowed set with a safe fallback.
  • Skip non-numeric dashboard IDs derived from POST keys in include/functions.php before writing to the dashboard table.
  • Validate and safely quote column identifiers before issuing ALTER TABLE ... DROP COLUMN during upgrades in include/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 thread include/functions.php
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
somethingwithproof force-pushed the fix/sql-identifier-validation branch from ba61877 to 1a527e9 Compare August 21, 2026 19:58
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants