From 75d9457ed1b9cc64157ebd05be9b4cc05bffd9f6 Mon Sep 17 00:00:00 2001 From: AkshayK Date: Mon, 14 Sep 2026 17:26:10 -0400 Subject: [PATCH] C++: Model Comdb2 SQL execution sinks --- .../2026-09-14-comdb2-sql-injection.md | 4 +++ cpp/ql/lib/ext/Comdb2.model.yml | 11 +++++++ .../CWE-089/SqlTainted/SqlTainted.expected | 9 ++++++ .../CWE/CWE-089/SqlTainted/test_comdb2.c | 32 +++++++++++++++++++ 4 files changed, 56 insertions(+) create mode 100644 cpp/ql/lib/change-notes/2026-09-14-comdb2-sql-injection.md create mode 100644 cpp/ql/lib/ext/Comdb2.model.yml create mode 100644 cpp/ql/test/query-tests/Security/CWE/CWE-089/SqlTainted/test_comdb2.c diff --git a/cpp/ql/lib/change-notes/2026-09-14-comdb2-sql-injection.md b/cpp/ql/lib/change-notes/2026-09-14-comdb2-sql-injection.md new file mode 100644 index 000000000000..a93b74541670 --- /dev/null +++ b/cpp/ql/lib/change-notes/2026-09-14-comdb2-sql-injection.md @@ -0,0 +1,4 @@ +--- +category: minorAnalysis +--- +* Added SQL-injection sink models for the Comdb2 C API functions `cdb2_run_statement` and `cdb2_run_statement_typed`. diff --git a/cpp/ql/lib/ext/Comdb2.model.yml b/cpp/ql/lib/ext/Comdb2.model.yml new file mode 100644 index 000000000000..7edcab128ce2 --- /dev/null +++ b/cpp/ql/lib/ext/Comdb2.model.yml @@ -0,0 +1,11 @@ +# Partial model of the Comdb2 C API. +# https://bloomberg.github.io/comdb2/c_api.html +extensions: + - addsTo: + pack: codeql/cpp-all + extensible: sinkModel + data: # namespace, type, subtypes, name, signature, ext, input, kind, provenance + # Both functions interpret the second argument as SQL. The typed variant + # specifies result-column types; bound parameter values are passed separately. + - ["", "", False, "cdb2_run_statement", "", "", "Argument[*1]", "sql-injection", "manual"] + - ["", "", False, "cdb2_run_statement_typed", "", "", "Argument[*1]", "sql-injection", "manual"] diff --git a/cpp/ql/test/query-tests/Security/CWE/CWE-089/SqlTainted/SqlTainted.expected b/cpp/ql/test/query-tests/Security/CWE/CWE-089/SqlTainted/SqlTainted.expected index 1f8441edcd61..253fe0f02e14 100644 --- a/cpp/ql/test/query-tests/Security/CWE/CWE-089/SqlTainted/SqlTainted.expected +++ b/cpp/ql/test/query-tests/Security/CWE/CWE-089/SqlTainted/SqlTainted.expected @@ -6,6 +6,8 @@ | test.c:106:24:106:29 | query1 | test.c:101:8:101:16 | gets output argument | test.c:106:24:106:29 | query1 | This argument to a SQL query function is derived from $@. | test.c:101:8:101:16 | gets output argument | user input (string read by gets) | | test.c:107:28:107:33 | query1 | test.c:101:8:101:16 | gets output argument | test.c:107:28:107:33 | query1 | This argument to a SQL query function is derived from $@. | test.c:101:8:101:16 | gets output argument | user input (string read by gets) | | test.cpp:43:27:43:33 | *access to array | test.cpp:39:27:39:30 | **argv | test.cpp:43:27:43:33 | *access to array | This argument to a SQL query function is derived from $@ and then passed to pqxx::work::exec1((unnamed parameter 0)). | test.cpp:39:27:39:30 | **argv | user input (a command-line argument) | +| test_comdb2.c:22:28:22:36 | userInput | test_comdb2.c:18:8:18:16 | gets output argument | test_comdb2.c:22:28:22:36 | userInput | This argument to a SQL query function is derived from $@. | test_comdb2.c:18:8:18:16 | gets output argument | user input (string read by gets) | +| test_comdb2.c:23:34:23:42 | userInput | test_comdb2.c:18:8:18:16 | gets output argument | test_comdb2.c:23:34:23:42 | userInput | This argument to a SQL query function is derived from $@. | test_comdb2.c:18:8:18:16 | gets output argument | user input (string read by gets) | | test_libpq.c:26:16:26:24 | userInput | test_libpq.c:23:8:23:16 | gets output argument | test_libpq.c:26:16:26:24 | userInput | This argument to a SQL query function is derived from $@. | test_libpq.c:23:8:23:16 | gets output argument | user input (string read by gets) | | test_libpq.c:27:22:27:30 | userInput | test_libpq.c:23:8:23:16 | gets output argument | test_libpq.c:27:22:27:30 | userInput | This argument to a SQL query function is derived from $@. | test_libpq.c:23:8:23:16 | gets output argument | user input (string read by gets) | | test_libpq.c:28:27:28:35 | userInput | test_libpq.c:23:8:23:16 | gets output argument | test_libpq.c:28:27:28:35 | userInput | This argument to a SQL query function is derived from $@. | test_libpq.c:23:8:23:16 | gets output argument | user input (string read by gets) | @@ -26,6 +28,8 @@ edges | test.c:101:8:101:16 | gets output argument | test.c:106:24:106:29 | query1 | provenance | TaintFunction Sink:MaD:2 | | test.c:101:8:101:16 | gets output argument | test.c:107:28:107:33 | query1 | provenance | TaintFunction Sink:MaD:1 | | test.cpp:39:27:39:30 | **argv | test.cpp:43:27:43:33 | *access to array | provenance | | +| test_comdb2.c:18:8:18:16 | gets output argument | test_comdb2.c:22:28:22:36 | userInput | provenance | Sink:MaD:9 | +| test_comdb2.c:18:8:18:16 | gets output argument | test_comdb2.c:23:34:23:42 | userInput | provenance | Sink:MaD:10 | | test_libpq.c:23:8:23:16 | gets output argument | test_libpq.c:26:16:26:24 | userInput | provenance | Sink:MaD:3 | | test_libpq.c:23:8:23:16 | gets output argument | test_libpq.c:27:22:27:30 | userInput | provenance | Sink:MaD:4 | | test_libpq.c:23:8:23:16 | gets output argument | test_libpq.c:28:27:28:35 | userInput | provenance | Sink:MaD:5 | @@ -41,6 +45,8 @@ models | 6 | Sink: ; ; false; PQsendPrepare; ; ; Argument[*2]; sql-injection; manual | | 7 | Sink: ; ; false; PQsendQuery; ; ; Argument[*1]; sql-injection; manual | | 8 | Sink: ; ; false; PQsendQueryParams; ; ; Argument[*1]; sql-injection; manual | +| 9 | Sink: ; ; false; cdb2_run_statement; ; ; Argument[*1]; sql-injection; manual | +| 10 | Sink: ; ; false; cdb2_run_statement_typed; ; ; Argument[*1]; sql-injection; manual | nodes | test.c:14:27:14:30 | **argv | semmle.label | **argv | | test.c:15:20:15:26 | *access to array | semmle.label | *access to array | @@ -59,6 +65,9 @@ nodes | test.c:107:28:107:33 | query1 | semmle.label | query1 | | test.cpp:39:27:39:30 | **argv | semmle.label | **argv | | test.cpp:43:27:43:33 | *access to array | semmle.label | *access to array | +| test_comdb2.c:18:8:18:16 | gets output argument | semmle.label | gets output argument | +| test_comdb2.c:22:28:22:36 | userInput | semmle.label | userInput | +| test_comdb2.c:23:34:23:42 | userInput | semmle.label | userInput | | test_libpq.c:23:8:23:16 | gets output argument | semmle.label | gets output argument | | test_libpq.c:26:16:26:24 | userInput | semmle.label | userInput | | test_libpq.c:27:22:27:30 | userInput | semmle.label | userInput | diff --git a/cpp/ql/test/query-tests/Security/CWE/CWE-089/SqlTainted/test_comdb2.c b/cpp/ql/test/query-tests/Security/CWE/CWE-089/SqlTainted/test_comdb2.c new file mode 100644 index 000000000000..ce30e27700d6 --- /dev/null +++ b/cpp/ql/test/query-tests/Security/CWE/CWE-089/SqlTainted/test_comdb2.c @@ -0,0 +1,32 @@ +// Minimal declarations from the public Comdb2 C API: +// https://github.com/bloomberg/comdb2/blob/dfb15415b48cbe67220d76964243086051813676/cdb2api/cdb2api.h +typedef struct cdb2_hndl cdb2_hndl_tp; + +enum { CDB2_CSTRING = 3 }; + +int cdb2_run_statement(cdb2_hndl_tp *hndl, const char *sql); +int cdb2_run_statement_typed(cdb2_hndl_tp *hndl, const char *sql, int ntypes, const int *types); +int cdb2_bind_param(cdb2_hndl_tp *hndl, const char *name, int type, + const void *varaddr, int length); + +char *gets(char *s); +typedef unsigned long size_t; +size_t strlen(const char *s); + +void comdb2Tests(cdb2_hndl_tp *hndl) { + char userInput[1000]; + gets(userInput); // $ Source + int types[] = {CDB2_CSTRING}; + + // Both execution functions interpret user-controlled query text as SQL. + cdb2_run_statement(hndl, userInput); // $ Alert + cdb2_run_statement_typed(hndl, userInput, 1, types); // $ Alert + + cdb2_run_statement(hndl, "SELECT 1"); // GOOD + cdb2_run_statement_typed(hndl, "SELECT 'constant'", 1, types); // GOOD + + // Bound values are data, not SQL text, even when controlled by the user. + cdb2_bind_param(hndl, "value", CDB2_CSTRING, userInput, strlen(userInput) + 1); // GOOD + cdb2_run_statement(hndl, "SELECT @value"); // GOOD + cdb2_run_statement_typed(hndl, "SELECT @value", 1, types); // GOOD +}