Skip to content

Fix | Reenable SqlBulkCopy in least-privilege environments#4306

Open
edwardneal wants to merge 4 commits into
dotnet:mainfrom
edwardneal:fix/issue-4293
Open

Fix | Reenable SqlBulkCopy in least-privilege environments#4306
edwardneal wants to merge 4 commits into
dotnet:mainfrom
edwardneal:fix/issue-4293

Conversation

@edwardneal
Copy link
Copy Markdown
Contributor

Description

SqlBulkCopy has traditionally only normally required SELECT and INSERT permissions over the table being copied to (src). However, recent additions to the class also required SELECT permissions over the [sys].[all_columns] metadata view. These permissions are present by default but are sometimes removed as part of SQL Server hardening.

This change to the permissions contract was unintentional, the failure of SqlBulkCopy in least-privilege environments which only grant SELECT & INSERT permissions was a bug. This PR resolves the bug by guarding all access to the metadata view behind a simple HAS_PERMS_BY_NAME check.

As a result of this change, least-privilege environments will be unable to use the newer features introduced in #3677 and #3590. I think this is unavoidable - there's no way to implement the features safely without access to the metadata view.

HAS_PERMS_BY_NAME is documented here as being supported in SQL Server, Azure SQL, managed instances and Fabric. It doesn't document support for two platforms: Azure Synapse Analytics, and APS/PDW. However, other documentation confirms that Azure Synapse Serverless pools support this function, and testing against a dedicated pool confirms that this does too. Furthermore, the APS/PDW 2016 release notes explicitly document the introduction of this function.

I've chosen to use this approach because wrapping metadata access in a TRY/CATCH block won't work - when XACT_ABORT is enabled, even a caught exception will result in the wrapping transaction being doomed, and Synapse doesn't allow this option to be temporarily turned off.

Issues

Fixes #4293.

Testing

Existing SqlBulkCopy tests continue to pass. I've added two new tests which build a low-privileged environment, then run a bulk copy inside it. They verify that the copy succeeds, but also that the newer alias-based functionality is unavailable.

As part of this, I've added ServerLogin and DatabaseUser primitives. The latter primitive highlighted the need for some form of state (the database in which we create the user) to be made available at the point of creation, so I've added this.

This widened test infrastructure and more complex testing accounts for about 3/4 of the changes in this PR.

Could someone start a CI run please?

* Add an UnescapedName property to DatabaseObject and use it in XEventsTracingTest.
* Create ServerLogin primitive.
* Create DatabaseUser primitive.
This makes DatabaseUser simpler to work with. It can store a reference to the database the user should be created in, and the class can handle switching to/from that database.
Fix bug by checking HAS_PERMS_BY_NAME over the sys.all_columns view.
This is safe on all supported platforms (including Synapse Analytics dedicated and shared pools - documentation is inconsistent.)

Regression tests create a low-privileged login and user in on-premise environments, then use those credentials to perform a SqlBulkCopy.
@edwardneal edwardneal requested a review from a team as a code owner May 24, 2026 14:16
@github-project-automation github-project-automation Bot moved this to To triage in SqlClient Board May 24, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: To triage

Development

Successfully merging this pull request may close these issues.

SqlBulkCopy v7 breaking change: now requires metadata visibility for sys.all_columns

1 participant