Why SQL Injection Detection Needs Accurate Library Models

SQL injection remains a serious security risk when an application constructs database queries using untrusted input. Developers may validate an HTTP parameter, pass it through several helper functions, and eventually use it to execute a database statement. The vulnerability can be difficult to recognize when the database call is buried inside a C API or a shared database-access layer.

Static analysis tools such as CodeQL help identify these vulnerabilities by tracing data from potentially untrusted sources to dangerous operations. However, the analyzer must understand the libraries used by the application. If a database API is not modeled correctly, a query that receives attacker-controlled data may be missed.

CodeQL 2.27.2 addresses this issue for C and C++ applications using Bloomberg's Comdb2 database client. The release adds SQL injection sink models for two C API functions: cdb2_run_statement and cdb2_run_statement_typed.

The change does not introduce a new SQL injection vulnerability or a new database API. It improves CodeQL's understanding of existing APIs so that its security analysis can identify relevant data flows more accurately.

For developers maintaining C and C++ services, the practical benefit is better visibility into query-execution paths that may otherwise escape automated security checks.

What Is Comdb2?

Comdb2 is a distributed relational database developed at Bloomberg. Its C API allows applications to connect to a database, execute statements, and retrieve results.

A C or C++ service may use Comdb2 directly or wrap its database operations in a repository, service, or helper layer. In either case, SQL statements can be constructed from application data before being passed to the database client.

Consider an application that receives an account identifier through an HTTP request and uses it to construct a query. If the identifier is concatenated directly into SQL, an attacker may be able to alter the query's structure.

The vulnerability is not specific to Comdb2. It is a general problem that occurs whenever untrusted data becomes executable SQL without an appropriate parameterization strategy.

How CodeQL Detects SQL Injection

CodeQL represents a program as a database of facts about its code. Security queries use these facts to reason about relationships between expressions, function calls, and data flow.

SQL injection analysis commonly follows a source-to-sink model.

Component

Meaning

Example

Source

A value that may be controlled by an attacker

HTTP request parameter

Data-flow path

The sequence of operations that carries the value through the program

String assignment, helper calls, and query construction

Sink

An operation where the value can affect SQL execution

A database statement-execution function

Sanitizer or barrier

A transformation or validation that can prevent the dangerous flow when correctly applied

Parameter binding or a recognized safe transformation

Alert

A reported path that requires investigation

User-controlled input reaching SQL execution

A simplified flow might look like this:

HTTP request
     |
     v
Untrusted account identifier
     |
     v
Query construction
     |
     v
cdb2_run_statement(...)
     |
     v
Database execution

The sink model tells CodeQL that a particular argument to a library function represents a security-sensitive SQL execution point.

Without that knowledge, the analyzer might understand the application's string operations but fail to recognize the final database call as a sink for its SQL injection query.

What Changed in CodeQL 2.27.2?

CodeQL 2.27.2 added SQL injection sink models for these Comdb2 C API functions:

The models help CodeQL recognize query-execution operations when analyzing C and C++ code that uses these APIs.

The distinction is important: the update improves the analyzer's library knowledge. It does not automatically make the application secure, validate SQL statements, or modify database behavior.

Area

Before the model update

With the new models

Comdb2 API recognition

SQL execution may not be recognized correctly by the relevant query

The supported execution APIs are modeled as SQL injection sinks

Data-flow analysis

A path can be missed if the final API is not recognized

CodeQL can report relevant flows that reach the modeled sink

Application security

Unsafe query construction remains a code-level risk

Developers have better visibility into potential injection paths

Runtime behavior

Database execution is unchanged

Database execution is unchanged

The models are particularly relevant when user-controlled values travel through multiple functions before reaching a Comdb2 statement-execution call.

Example: An Unsafe Query Construction Path

Consider a simplified C++ service that receives an account identifier and builds a SQL statement.

The following example is illustrative and focuses on the vulnerability pattern. The request-handling function and database initialization are omitted.

#include <string>
#include <cdb2api.h>

int find_account(
    cdb2_hndl_tp* db,
    const std::string& account_id)
{
    std::string sql =
        "SELECT account_id, balance "
        "FROM accounts WHERE account_id = '" +
        account_id + "'";

    return cdb2_run_statement(
        db,
        sql.c_str());
}

The problem is that account_id is inserted directly into the SQL statement.

If the value originates from an HTTP request or another untrusted source, an attacker may be able to change the query's meaning by supplying SQL syntax as part of the value.

For example, an input containing a quote may terminate the intended string literal. Depending on the resulting statement and database behavior, the attacker could influence the query beyond its intended account lookup.

The exact impact depends on the SQL dialect, statement structure, permissions, and database configuration. The core problem is that the application treats untrusted input as part of the SQL program rather than as data.

With the Comdb2 sink model available, CodeQL can recognize the execution call and investigate whether the statement passed to it contains data derived from an untrusted source.

How Data Flow Can Become Difficult to Follow

Real applications rarely construct and execute a query inside one small function. A typical service may separate request parsing, validation, query generation, and database execution into different layers.

For example:

std::string build_account_query(
    const std::string& account_id)
{
    return "SELECT * FROM accounts WHERE account_id = '" +
           account_id + "'";
}

int execute_account_query(
    cdb2_hndl_tp* db,
    const std::string& account_id)
{
    std::string sql = build_account_query(account_id);

    return cdb2_run_statement(
        db,
        sql.c_str());
}

The data-flow path now includes an additional helper function. A reviewer examining only execute_account_query may not immediately see that the query is constructed using untrusted input.

Static analysis can connect these operations when its data-flow models recognize the relevant functions and transformations.

This is where accurate sink modeling becomes useful. The analyzer can identify the database execution point and determine whether the query reaching it depends on potentially untrusted data.

However, a sink model alone is not a complete guarantee of detection. CodeQL must also recognize the source, understand the intervening data flow, and apply the relevant query logic. Custom wrappers, indirect calls, unusual string operations, and unsupported source patterns can affect results.

How to Fix the Vulnerability

The preferred solution is to use parameterized queries or prepared statements supported by the database API. Parameter binding separates the SQL structure from the values supplied by the application.

A query should define the intended operation independently of the account identifier. The identifier is then supplied through the database client's supported parameter mechanism.

The following is a conceptual example rather than a verified Comdb2 API recipe:

// Conceptual pattern only:
//
// 1. Prepare or define SQL with a parameter placeholder.
// 2. Bind account_id using the supported Comdb2 API.
// 3. Execute the statement through the supported client interface.
//
// Do not concatenate account_id into the SQL text.

Consult the Comdb2 client documentation for the parameter-binding API supported by the installed client version. Do not assume that a placeholder syntax or binding function from another database driver is compatible with Comdb2.

If the relevant API does not provide a suitable parameter-binding mechanism for a particular operation, use a documented safe alternative rather than inventing a placeholder convention.

Avoid Treating Escaping as a Universal Solution

Manually escaping quote characters is not a reliable replacement for parameterized queries in every context.

Correct escaping depends on the database's parsing rules, character encoding, SQL context, and the exact function being used. A transformation that is safe for one string-literal context may not be sufficient for another.

Input validation is still useful. For example, an account identifier may be required to follow a restricted format. However, validation should complement safe query construction rather than replace it.

Investigating a CodeQL Alert

When CodeQL reports a possible SQL injection path involving a Comdb2 call, investigate the complete path instead of changing the sink or suppressing the alert immediately.

A practical review process includes these steps:

  1. Identify the source. Determine whether the value originates from an HTTP request, message payload, command-line argument, configuration value, or another potentially untrusted source.

  2. Trace the transformations. Follow the value through assignments, string operations, helper functions, and query-building utilities.

  3. Inspect the sink. Confirm how the statement reaches cdb2_run_statement or cdb2_run_statement_typed.

  4. Check query construction. Determine whether untrusted data changes the SQL structure or is handled as a value through a supported parameter-binding mechanism.

  5. Verify any sanitization. Confirm that the transformation is appropriate for the actual database API and SQL context.

  6. Test the fix. Add regression tests for normal values, quotes, unexpected delimiters, and other relevant input cases.

  7. Run CodeQL again. Confirm that the original path is no longer reported and inspect other related paths that may remain vulnerable.

A static-analysis alert identifies a potential security problem. The developer must still evaluate the application's actual data flow and execution context.

Enable the Updated Analysis

CodeQL 2.27.2 includes the Comdb2 modeling improvement. GitHub code scanning on GitHub.com automatically receives new CodeQL versions as they become available, while other environments may require a manual upgrade according to their supported release process.

A typical GitHub Actions workflow uses the CodeQL Action to initialize analysis, build or autobuild the project as appropriate, and run the security queries.

A simplified workflow structure looks like this:

name: CodeQL Security Analysis

on:
  push:
    branches: [main]
  pull_request:
    branches: [main]

jobs:
  analyze:
    runs-on: ubuntu-latest
    permissions:
      contents: read
      security-events: write

    steps:
      - name: Checkout repository
        uses: actions/checkout@v4

      - name: Initialize CodeQL
        uses: github/codeql-action/init@v3
        with:
          languages: cpp

      - name: Build project
        run: |
          # Add the commands required to build your C/C++ project.
          echo "Configure the actual project build here"

      - name: Perform CodeQL analysis
        uses: github/codeql-action/analyze@v3

This workflow illustrates the major stages, not a complete production build configuration. Replace the placeholder build command with the project's real build steps, and follow GitHub's current CodeQL Action guidance for version pinning, permissions, and build configuration.

For compiled C/C++ projects, a successful CodeQL build or supported autobuild process is important because the extractor needs information about the code being analyzed.

After analysis completes, review the security results and confirm that the relevant database code was included in the analysis. Merely adding a workflow file does not guarantee that every source file or execution path has been analyzed successfully.

Common Mistakes When Investigating SQL Injection

Assuming the new model fixes application code. The model improves detection; developers must still correct unsafe query construction.

Checking only the final database function. The vulnerability may originate several functions earlier, where a string is constructed from untrusted input.

Trusting validation without reviewing query construction. A restricted input format can reduce risk, but query parameters remain the preferred defense when supported.

Suppressing alerts without tracing the path. A flow may appear safe at the final call while an earlier helper introduces unsafe concatenation.

Assuming every database driver behaves identically. Parameter placeholders and binding APIs differ. Use the documented Comdb2 client interface.

Ignoring build and extraction failures. Incomplete C/C++ analysis can prevent the tool from examining the intended code paths.

Best Practices for C and C++ Database Code

Conclusion

CodeQL 2.27.2 improves SQL injection analysis for C and C++ applications using Comdb2 by adding sink models for cdb2_run_statement and cdb2_run_statement_typed.

The change helps the analyzer recognize database execution points and trace potentially untrusted data into them. That is particularly valuable in applications where query construction and execution are separated across multiple layers.

The new models do not change Comdb2 itself or eliminate SQL injection automatically. Developers still need to trace the reported data flow, verify the query construction, and use safe parameter-binding techniques supported by the client API.

For teams maintaining C and C++ database services, the update is a useful reason to rerun security analysis, review previously unmodeled database paths, and strengthen regression tests around query execution.