Repository navigation
FIX: prevent native log format-string injection - #791
Conversation
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
There was a problem hiding this comment.
🟢 Approval recommended
No unresolved blocking issues were identified.
Pull request overview
Prevents native printf-style log injection from server-controlled metadata and adds compile-time validation.
Changes:
- Converts dynamic log calls to literal formats.
- Fixes format mismatches.
- Adds compiler checks and regression tests.
File summaries
| File | Description |
|---|---|
tests/test_039_native_logging_format_security.py |
Adds logging format-security regression tests. |
mssql_python/pybind/logger_bridge.hpp |
Adds printf-format annotations. |
mssql_python/pybind/ddbc_bindings.cpp |
Secures and corrects native log calls. |
mssql_python/pybind/CMakeLists.txt |
Enables format diagnostics. |
Review details
- Files reviewed: 4/4 changed files
- Comments generated: 0
- Review effort level: Lite
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
📊 Code Coverage Report
Diff CoverageDiff: main...HEAD, staged and unstaged changes
Summary
mssql_python/pybind/ddbc_bindings.cppLines 2730-2740 2730 py::bytes b = element.cast<py::bytes>();
2731 if (PyBytes_GET_SIZE(b.ptr()) != 16) {
2732 LOG("BindParameterArray: GUID bytes wrong "
2733 "length - param_index=%d, row=%zu, "
! 2734 "length=%lld",
2735 paramIndex, i,
! 2736 static_cast<long long>(PyBytes_GET_SIZE(b.ptr())));
2737 ThrowStdException("UUID binary data must be "
2738 "exactly 16 bytes long.");
2739 }
2740 std::memcpy(uuid_bytes.data(), PyBytes_AS_STRING(b.ptr()), 16);Lines 2759-2768 2759 std::memcpy(guidArray[i].Data4, uuid_bytes.data() + 8, 8);
2760 strLenOrIndArray[i] = sizeof(SQLGUID);
2761 }
2762 LOG("BindParameterArray: SQL_C_GUID bound - "
! 2763 "param_index=%d, count=%zu",
! 2764 paramIndex, paramSetSize);
2765 dataPtr = guidArray;
2766 bufferLength = sizeof(SQLGUID);
2767 break;
2768 }📋 Files Needing Attention📉 Files with overall lowest coverage (click to expand)mssql_python.pybind.performance_counter.hpp: 0.7%
mssql_python.pybind.logger_bridge.cpp: 57.9%
mssql_python.pybind.ddbc_bindings.h: 64.1%
mssql_python.pybind.logger_bridge.hpp: 70.8%
mssql_python.pybind.ddbc_bindings.cpp: 78.4%
mssql_python.pybind.connection.connection_pool.cpp: 82.3%
mssql_python.pybind.connection.connection.cpp: 82.5%
mssql_python.logging.py: 86.2%
mssql_python.pooling.py: 90.1%
mssql_python.pybind.fetch_temporal.hpp: 92.1%🔗 Quick Links
|
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
PR Performance ReportNo consistent slowdowns detected across all 2 environments. Coverage: 2 of 2 environments completed. Advisory result; does not block merging.
Affected phases and call countsPhase times are inclusive diagnostics and must not be added together. They identify where measured time changed, not why it changed. No affected phases or call-count changes were recorded. All database tasks and timingsUnix / SQL Server 2022
Unix / SQL Server 2025
Build, commits and measurement detailsPR head:
A consistent change requires more than 20% median paired movement, at least 1 ms between the median runtimes, and at least 80% of pairs exceeding the relative threshold in the same direction. A slowdown without enough pair agreement is reported as inconsistent. The displayed change is the median of paired before-and-after ratios. It is not recalculated from the two displayed median runtimes. Both revisions use profiling-enabled builds on the same agent and database, with alternating order and discarded warmups. Results are diagnostic and do not represent production-wheel latency. Raw samples and logs are attached to the ADO run as |
Gaurav Sharma (bewithgaurav)
left a comment
There was a problem hiding this comment.
this addresses the reported format-string issue and keeps the existing error behavior intact. one optional suggestion to strengthen regression coverage. approving.
There was a problem hiding this comment.
🔵 Needs a closer look
The source-contract test can miss multiline dynamic log format expressions.
Review details
Suppressed comments (1)
tests/test_039_native_logging_format_security.py:32
- This regex does not match a call whose format expression starts on the next line, because
.does not match newlines by default. For example,LOG_ERROR(\n dynamic_format, ...)is skipped, so the new source-contract test can pass while a dynamic native format remains. Match the first non-whitespace character across lines (or enable DOTALL) so multiline calls are checked too.
pattern = re.compile(r"\bLOG(?:_INFO|_WARNING|_ERROR)?\s*\(\s*(.)")
- Files reviewed: 4/4 changed files
- Comments generated: 0 new
- Review effort level: Lite
Work Item / Issue Reference
Summary
Prevents server-controlled Arrow column metadata from being interpreted as a native printf-style format string.
Converts all dynamic native LOG calls to literal formats, fixes existing format/argument mismatches exposed by compiler checking, and enables compile-time printf validation for GCC, Clang, and AppleClang.
Adds a source-contract regression test that rejects nonliteral native LOG formats across first-party C++ sources and headers.
Validation