Skip to content

Fix: Automatic prefix stripping for mysql_variables, pgsql_variables, and admin_variables config parsing#5247

Merged
renecannao merged 5 commits intov3.0from
v3.0-issue5246
Dec 11, 2025
Merged

Fix: Automatic prefix stripping for mysql_variables, pgsql_variables, and admin_variables config parsing#5247
renecannao merged 5 commits intov3.0from
v3.0-issue5246

Conversation

@renecannao
Copy link
Contributor

Summary

Fixes #5246

When users mistakenly include module prefix (e.g., "mysql-") in configuration variable names within mysql_variables, pgsql_variables, or admin_variables sections, the prefix is automatically stripped before storing in the database.

Problem

Users often write mysql-log_unhealthy_connections=false in mysql_variables section, which results in double-prefixing (mysql-mysql-log_unhealthy_connections) and lookup failures.

Solution

Modified ProxySQL_Config::Read_Global_Variables_from_configfile() to detect and strip duplicate prefixes:

  • Check if variable name starts with "prefix-" (where prefix is "mysql", "pgsql", or "admin")
  • If duplicate prefix found, remove it before constructing SQL INSERT query
  • Preserves backward compatibility: both prefixed and non-prefixed forms work

Code Changes

  1. lib/ProxySQL_Config.cpp: Moved prefix stripping logic after value lookup but before SQL query construction
  2. test/tap/tests/test_load_from_config_prefix_stripping-t.cpp: New TAP test validating prefix stripping across all three module types
  3. include/proxysql_config.h: Added extensive Doxygen documentation for class and methods

Testing

The fix handles all three module types:

  • mysql_variables: Strips "mysql-" prefix
  • pgsql_variables: Strips "pgsql-" prefix
  • admin_variables: Strips "admin-" prefix

Example

Config file contains:

mysql_variables = {
    mysql-log_unhealthy_connections = false
    mysql-threads = 2
    poll_timeout = 1000
}

Result in global_variables table:

  • mysql-log_unhealthy_connections = "false" (single prefix)
  • mysql-threads = "2" (single prefix)
  • mysql-poll_timeout = "1000" (single prefix added)

Related Issues

Closes #5242 where users reported "Closing unhealthy client connection fix not working" due to this configuration issue.

…and admin_variables config parsing

When users mistakenly include the module prefix (e.g., mysql-log_unhealthy_connections)
in the mysql_variables section, the variable gets stored with a double prefix
(e.g., mysql-mysql-log_unhealthy_connections). This fix automatically strips
the prefix if present, ensuring variables are stored correctly.

The same logic applies to pgsql_variables (pgsql-) and admin_variables (admin-).

Fixes #5246
The previous implementation stripped the prefix before calling
group.lookupValue(), which would fail because the config file
contains the prefixed name (e.g., "mysql-log_unhealthy_connections").
The lookup must use the original name from the config file.

This commit moves the prefix stripping logic to after the value
lookup but before constructing the SQL query, ensuring both:
1. The correct value is retrieved from the config using the
   original prefixed name
2. The variable is stored in the database with a single prefix

Also includes a test to verify the fix works for mysql_variables,
pgsql_variables, and admin_variables sections.
…al_Variables_from_configfile

This commit adds detailed Doxygen documentation for:
1. The ProxySQL_Config class - describes its role in configuration management
2. The Read_Global_Variables_from_configfile() method - documents its behavior,
   parameters, return value, and the automatic prefix stripping feature

The documentation explains the automatic prefix stripping behavior that handles
cases where users mistakenly include module prefix (e.g., "mysql-") in variable
names within configuration files.
@gemini-code-assist
Copy link

Summary of Changes

Hello @renecannao, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

This pull request enhances the robustness of ProxySQL's configuration parsing by introducing an automatic mechanism to correct common user errors. It specifically targets the mysql_variables, pgsql_variables, and admin_variables sections, where users sometimes inadvertently include redundant module prefixes in variable names. By stripping these duplicate prefixes, the system ensures that configuration variables are consistently stored and correctly recognized, preventing lookup failures and improving the overall reliability of configuration management.

Highlights

  • Automatic Prefix Stripping: Implemented automatic stripping of module prefixes (e.g., 'mysql-', 'pgsql-', 'admin-') from variable names within mysql_variables, pgsql_variables, and admin_variables sections of the configuration file.
  • Problem Resolution: This fix addresses issues where users mistakenly included module prefixes in variable names, leading to double-prefixing (e.g., mysql-mysql-log_unhealthy_connections) and subsequent lookup failures in the database.
  • Backward Compatibility: The solution ensures backward compatibility, allowing both correctly prefixed and previously double-prefixed variable names in the configuration to be parsed and stored with a single, correct prefix.
  • New Test Coverage: A new TAP test has been added to thoroughly validate the prefix stripping logic across all three affected module types: MySQL, PostgreSQL, and Admin variables.
Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here.

You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

Copy link

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

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

Code Review

This pull request correctly implements automatic prefix stripping for configuration variables and adds corresponding documentation and tests. The core logic is sound. However, the implementation is adjacent to a pre-existing critical SQL injection vulnerability in the way database queries are constructed. My review includes a detailed comment on this vulnerability with a recommendation to use prepared statements to mitigate it.

Comment on lines 121 to 126
// Automatic prefix stripping: if variable already starts with "prefix-", remove it
if (strncmp(n, prefix, prefix_len) == 0 && n[prefix_len] == '-') {
n += prefix_len + 1; // Skip "prefix-"
}
char *query=(char *)malloc(strlen(q)+strlen(prefix)+strlen(n)+strlen(value_string.c_str()));
sprintf(query,q, prefix, n, value_string.c_str());

Choose a reason for hiding this comment

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

security-critical critical

While the logic to strip prefixes is correct, the subsequent construction of the SQL query using sprintf on line 126 is vulnerable to SQL Injection. Variable values from the configuration file are inserted directly into the query string without proper escaping. A malicious value containing a double quote could be used to execute arbitrary SQL commands.

To fix this, you should use prepared statements with parameter binding. This is the standard, safe way to execute queries with dynamic data.

Here is an example of how you could refactor the loop to use prepared statements:

// Before the loop
const char *sql = "INSERT OR REPLACE INTO global_variables VALUES (?, ?)";
auto prep_result = admindb->prepare_v2(sql);
int rc = prep_result.first;
stmt_unique_ptr stmt = std::move(prep_result.second);

if (rc != SQLITE_OK) {
    // Handle error, e.g., log and return
    return 0;
}

for (i=0; i< count; i++) {
    const Setting &sett = group[i];
    const char *n = sett.getName();
    // ... logic to get value_string ...

    // Automatic prefix stripping
    if (strncmp(n, prefix, prefix_len) == 0 && n[prefix_len] == '-') {
        n += prefix_len + 1; // Skip "prefix-"
    }

    std::string var_name = std::string(prefix) + "-" + n;

    (*proxy_sqlite3_bind_text)(stmt.get(), 1, var_name.c_str(), -1, SQLITE_TRANSIENT);
    (*proxy_sqlite3_bind_text)(stmt.get(), 2, value_string.c_str(), -1, SQLITE_TRANSIENT);

    int step_rc;
    do {
        step_rc = (*proxy_sqlite3_step)(stmt.get());
        if (step_rc == SQLITE_BUSY || step_rc == SQLITE_LOCKED) {
            usleep(100);
        }
    } while (step_rc == SQLITE_BUSY || step_rc == SQLITE_LOCKED);

    (*proxy_sqlite3_reset)(stmt.get());
}
// After the loop, stmt is automatically finalized by stmt_unique_ptr destructor

This refactoring prepares the statement once before the loop and safely binds values for each iteration, preventing SQL injection.

Adds the new prefix stripping validation test to the default-g4 test group
along with mysql-auto_increment_delay_multiplex=0-g4, mysql-multiplexing=false-g4,
mysql-query_digests=0-g4, and mysql-query_digests_keep_comment=1-g4 groups.

This ensures the test is run as part of the regular test suite.
Replace sprintf-based SQL query construction with prepared statements using
bound parameters to prevent SQL injection attacks. This addresses the security
issue identified in PR #5247 review.

Changes:
- Use SQLite prepared statement with placeholders ?1, ?2
- Bind variable names and values securely using proxy_sqlite3_bind_text
- Use ASSERT_SQLITE_OK for error handling as per ProxySQL conventions
- Remove malloc/sprintf vulnerable code pattern
- Add necessary includes for SQLite functions and ASSERT_SQLITE_OK macro

Security: SQL injection could have occurred if configuration variable names
or values contained malicious quotes. Prepared statements eliminate this risk.
@sonarqubecloud
Copy link

Quality Gate Failed Quality Gate failed

Failed conditions
2 Security Hotspots

See analysis details on SonarQube Cloud

@renecannao renecannao merged commit 8cf3e59 into v3.0 Dec 11, 2025
13 of 14 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

1 participant