Skip to content

CHORE: credential hygiene in test output, workflow permissions, and scanning config - #713

Merged
Gaurav Sharma (bewithgaurav) merged 8 commits into
mainfrom
bewithgaurav/sanitize-connection-string-in-test-assertions
Aug 12, 2026
Merged

CHORE: credential hygiene in test output, workflow permissions, and scanning config#713
Gaurav Sharma (bewithgaurav) merged 8 commits into
mainfrom
bewithgaurav/sanitize-connection-string-in-test-assertions

Conversation

@bewithgaurav

@bewithgaurav Gaurav Sharma (bewithgaurav) commented Aug 11, 2026

Copy link
Copy Markdown
Collaborator

Work Item / Issue Reference

AB#46465


Summary

This pull request improves security and CI hygiene by tightening permissions in GitHub Actions workflows, preventing accidental credential leaks in test logs, and cleaning up outdated credential scanning configuration. The most significant changes are grouped below.

Security improvements in test logging:

  • The conn_str pytest fixture is now wrapped in a custom _MaskedConnectionString class that hides passwords from pytest failure output, preventing accidental exposure of credentials in CI logs. All assertions in integration tests now use sanitized connection strings for extra safety. [1] [2] [3] [4] [5]

GitHub Actions workflow hardening:

  • All workflows now use permissions: contents: read instead of broader permissions, reducing the risk if a workflow is compromised. [1] [2] [3] [4]
  • All actions/checkout steps now set persist-credentials: false, ensuring that the default GitHub token is not persisted to the workflow environment, further limiting the risk of credential leakage in forked PRs or CI jobs. [1] [2] [3] [4] [5]

Credential scanning configuration cleanup:

  • The obsolete .config/CredScanSuppressions.json file was removed, and documentation was updated to reference only the current .gdn/ scanning mechanism. [1] [2]

…permissions

The conn_str fixture returned a plain str, and pytest renders every test argument in the failure header with repr(). Any failing test that takes conn_str therefore printed the live connection string, password included, into CI logs. That is 205 tests across 16 files, and it fires on any assertion failure, not just the ones that look at connection strings. The fixture now returns a str subclass whose repr() runs the value through sanitize_connection_string(). The value itself is unchanged, so equality, str(), f-strings and concatenation all behave as before.

test_012 separately asserted on conn.connection_str in the two tests that run against a real database. Those now assert on a sanitized local, so a failure cannot surface the credential through assertion introspection.

Workflows: lint-check granted pull-requests: write at workflow level but neither job writes to pull requests, so it drops to contents: read. devskim, pr-code-coverage and forked-pr-coverage only declared permissions per job and now carry a contents: read default, with the existing job-level grants left intact. Every actions/checkout that does not push gets persist-credentials: false.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@github-actions github-actions Bot added the pr-size: small Minimal code update label Aug 11, 2026
Comment thread tests/test_012_connection_string_integration.py Fixed
Comment thread tests/test_012_connection_string_integration.py Fixed
Comment thread tests/test_012_connection_string_integration.py Fixed
@github-actions

github-actions Bot commented Aug 11, 2026

Copy link
Copy Markdown

📊 Code Coverage Report

🔥 Diff Coverage

100%


🎯 Overall Coverage

82%


📈 Total Lines Covered: 7368 out of 8963
📁 Project: mssql-python


Diff Coverage

Diff: main...HEAD, staged and unstaged changes

No lines with coverage information in this diff.


📋 Files Needing Attention

📉 Files with overall lowest coverage (click to expand)
mssql_python.pybind.logger_bridge.cpp: 59.2%
mssql_python.pybind.ddbc_bindings.h: 59.9%
mssql_python.pybind.logger_bridge.hpp: 70.8%
mssql_python.pybind.ddbc_bindings.cpp: 76.6%
mssql_python.__init__.py: 77.6%
mssql_python.row.py: 77.6%
mssql_python.ddbc_bindings.py: 79.6%
mssql_python.pybind.connection.connection_pool.cpp: 81.4%
mssql_python.pybind.connection.connection.cpp: 84.3%
mssql_python.logging.py: 85.5%

🔗 Quick Links

⚙️ Build Summary 📋 Coverage Details

View Azure DevOps Build

Browse Full Coverage Report

`.config/CredScanSuppressions.json` has never been in effect. CredScan only reads a suppressions file when a pipeline passes `credscan.suppressionsFile`, and no pipeline here does, so the directory globs it lists have never suppressed anything. Guardian is what actually runs, wired in all three OneBranch pipelines through `sdl.suppression.suppressionFile` pointing at `.gdn/.gdnsuppress`, which excuses individual findings by signature rather than by directory.

Leaving the file in place is worse than not having it. It sits at the conventional path so it reads as active, and the next person to notice it is as likely to wire it up as to remove it. Wiring it up would switch credential scanning off across `tests/`, `benchmarks/`, `eng/` and `OneBranchPipelines/`.

copilot-instructions.md pointed at the file, so that reference now names `.gdn/` only.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@github-actions github-actions Bot added pr-size: medium Moderate update size and removed pr-size: small Minimal code update labels Aug 11, 2026
@bewithgaurav Gaurav Sharma (bewithgaurav) changed the title CHORE: keep db credentials out of pytest output and tighten workflow permissions CHORE: credential hygiene in test output, workflow permissions, and scanning config Aug 11, 2026
The new repr tests used a realistic-looking password literal. Committed connection strings here stick to Server=localhost with a plain dummy value, and the value now matches what the rest of tests/ already uses, so credential scanning has nothing new to flag. A new finding would not be in .gdn/.gdnsuppress and would break the build.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Comment thread tests/test_012_connection_string_integration.py Fixed
Comment thread tests/test_012_connection_string_integration.py Fixed
Copilot AI added 2 commits August 11, 2026 13:40
test_007_logging already covers sanitize_connection_string thoroughly, braced values with semicolons and escaped braces included, so testing that surface again through the wrapper added nothing. What is actually new here is that repr() routes through the sanitizer at all and that the subclass still behaves like the string it wraps, which is one test.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Deleting the wrapper breaks the import at collection time and a sanitizer regression trips the nine tests in test_007_logging, so the only thing left for this test to catch was someone editing __repr__ to stop calling the sanitizer. Not worth the conftest private-name import into a test module.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@github-actions github-actions Bot added pr-size: small Minimal code update and removed pr-size: medium Moderate update size labels Aug 11, 2026
Copilot AI and others added 2 commits August 11, 2026 14:17
First question anyone asks is why this is not just a call to sanitize_connection_string() where the value is used. pytest reads repr() off the object it holds, str.__repr__ cannot be reassigned on the builtin, and sanitizing the fixture value itself would leave the tests unable to connect.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@bewithgaurav
Gaurav Sharma (bewithgaurav) marked this pull request as ready for review August 11, 2026 08:49
Copilot AI lite review requested due to automatic review settings August 11, 2026 08:49

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

Improves credential hygiene across the test suite and CI configuration to reduce the chance of leaking live connection string secrets into logs, while also tightening default GitHub Actions permissions.

Changes:

  • Mask conn_str fixture repr() output so pytest failure introspection can’t print live passwords.
  • Ensure real-DB connection string assertions in test_012 operate on a sanitized value.
  • Reduce GitHub Actions default permissions to contents: read, disable checkout credential persistence, and remove a misleading/dead CredScan suppressions config reference/file.

Reviewed changes

Copilot reviewed 8 out of 8 changed files in this pull request and generated no comments.

Show a summary per file
File Description
tests/test_012_connection_string_integration.py Asserts on sanitized connection strings to prevent leaking credentials via assertion introspection.
tests/conftest.py Wraps conn_str in a str subclass overriding __repr__ to sanitize sensitive values in pytest output.
.github/workflows/pr-code-coverage.yml Adds default contents: read and disables persisted checkout credentials.
.github/workflows/lint-check.yml Drops unnecessary workflow-level PR write permissions and disables persisted checkout credentials.
.github/workflows/forked-pr-coverage.yml Adds default contents: read and disables persisted checkout credentials while keeping job-level PR write permissions.
.github/workflows/devskim.yml Adds default contents: read and disables persisted checkout credentials.
.github/copilot-instructions.md Removes reference to the inactive CredScan suppressions file, pointing only to .gdn/.
.config/CredScanSuppressions.json Removes an unused/misleading suppressions file to avoid accidental broad credential-scanning bypass.

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

@bewithgaurav
Gaurav Sharma (bewithgaurav) merged commit 06852dd into main Aug 12, 2026
29 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

pr-size: small Minimal code update

Projects

None yet

Development

Successfully merging this pull request may close these issues.

6 participants