Skip to content

Commit 519bbe6

Browse files
authored
Redact SAS credentials from Error.SasCredentialRequiresUriWithoutSas message (Azure#48580)
1 parent 65b4f56 commit 519bbe6

File tree

10 files changed

+178
-6
lines changed

10 files changed

+178
-6
lines changed

sdk/storage/Azure.Storage.Blobs/assets.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,5 +2,5 @@
22
"AssetsRepo": "Azure/azure-sdk-assets",
33
"AssetsRepoPrefixPath": "net",
44
"TagPrefix": "net/storage/Azure.Storage.Blobs",
5-
"Tag": "net/storage/Azure.Storage.Blobs_bf9faa9325"
5+
"Tag": "net/storage/Azure.Storage.Blobs_d1a42a73c0"
66
}

sdk/storage/Azure.Storage.Blobs/tests/BlobSasBuilderTests.cs

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -554,6 +554,48 @@ public async Task BlobSasBuilder_CorrelationId()
554554
}
555555
}
556556

557+
[RecordedTest]
558+
public async Task SasCredentialRequiresUriWithoutSasError_RedactedSasUri()
559+
{
560+
// Arrange
561+
BlobServiceClient oauthService = GetServiceClient_OAuth();
562+
string containerName = GetNewContainerName();
563+
564+
await using DisposingContainer test = await GetTestContainerAsync(service: oauthService, containerName: containerName);
565+
566+
Response<UserDelegationKey> userDelegationKey = await oauthService.GetUserDelegationKeyAsync(
567+
startsOn: Recording.UtcNow.AddHours(-1),
568+
expiresOn: Recording.UtcNow.AddHours(1));
569+
570+
BlobSasBuilder blobSasBuilder = new BlobSasBuilder
571+
{
572+
StartsOn = Recording.UtcNow.AddHours(-1),
573+
ExpiresOn = Recording.UtcNow.AddHours(1),
574+
BlobContainerName = containerName,
575+
};
576+
577+
blobSasBuilder.SetPermissions(BlobSasPermissions.All);
578+
579+
BlobUriBuilder blobUriBuilder = new BlobUriBuilder(test.Container.Uri)
580+
{
581+
Sas = blobSasBuilder.ToSasQueryParameters(userDelegationKey, test.Container.AccountName)
582+
};
583+
584+
Uri sasUri = blobUriBuilder.ToUri();
585+
586+
UriBuilder uriBuilder = new UriBuilder(sasUri);
587+
uriBuilder.Query = "[REDACTED]";
588+
string redactedUri = uriBuilder.Uri.ToString();
589+
590+
ArgumentException ex = Errors.SasCredentialRequiresUriWithoutSas<BlobUriBuilder>(sasUri);
591+
592+
// Assert
593+
Assert.IsTrue(ex.Message.Contains(redactedUri));
594+
Assert.IsFalse(ex.Message.Contains("st="));
595+
Assert.IsFalse(ex.Message.Contains("se="));
596+
Assert.IsFalse(ex.Message.Contains("sig="));
597+
}
598+
557599
private string BuildSignature(bool includeBlob, bool includeSnapshot, string containerName, string blobName, TestConstants constants)
558600
{
559601
var canonicalName = includeBlob ? $"/blob/{constants.Sas.Account}/{containerName}/{blobName}"

sdk/storage/Azure.Storage.Common/CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
### Bugs Fixed
1010
- Fixed bug where in rare cases, a `NullReferenceException` could be thrown when parsing an error response from the service.
1111
- Fixed bug to ensure that the accumulated disposables within the internal `StorageWriteStream` are disposed properly in the event that an exception is thrown during the disposal process. (#47781)
12+
- Fixed bug to Redact SAS credentials from Error.SasCredentialRequiresUriWithoutSas message.
1213

1314
### Other Changes
1415

sdk/storage/Azure.Storage.Common/src/Shared/Errors.Clients.cs

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -45,10 +45,16 @@ public static InvalidOperationException TokenCredentialsRequireHttps()
4545
=> new InvalidOperationException("Use of token credentials requires HTTPS");
4646

4747
public static ArgumentException SasCredentialRequiresUriWithoutSas<TUriBuilder>(Uri uri)
48-
=> new ArgumentException(
49-
$"You cannot use {nameof(AzureSasCredential)} when the resource URI also contains a Shared Access Signature: {uri}\n" +
48+
{
49+
UriBuilder uriBuilder = new UriBuilder(uri);
50+
uriBuilder.Query = "[REDACTED]";
51+
Uri redactedUri = uriBuilder.Uri;
52+
53+
return new ArgumentException(
54+
$"You cannot use {nameof(AzureSasCredential)} when the resource URI also contains a Shared Access Signature: {redactedUri}\n" +
5055
$"You can remove the shared access signature by creating a {typeof(TUriBuilder).Name}, setting {typeof(TUriBuilder).Name}.Sas to null," +
5156
$" and calling {typeof(TUriBuilder).Name}.ToUri.");
57+
}
5258

5359
public static InvalidOperationException SasMissingData(string paramName)
5460
=> new InvalidOperationException($"SAS is missing required parameter: {paramName}");

sdk/storage/Azure.Storage.Files.DataLake/assets.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,5 +2,5 @@
22
"AssetsRepo": "Azure/azure-sdk-assets",
33
"AssetsRepoPrefixPath": "net",
44
"TagPrefix": "net/storage/Azure.Storage.Files.DataLake",
5-
"Tag": "net/storage/Azure.Storage.Files.DataLake_d74597f1e3"
5+
"Tag": "net/storage/Azure.Storage.Files.DataLake_a37793870a"
66
}

sdk/storage/Azure.Storage.Files.DataLake/tests/DataLakeSasBuilderTests.cs

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -712,5 +712,50 @@ public async Task DataLakeSasBuilder_DirectoryDepth_CustomSas(string directoryNa
712712
// Assert
713713
Assert.AreEqual(expectedDepth, newUriBuilder.Sas.DirectoryDepth);
714714
}
715+
716+
[RecordedTest]
717+
public async Task SasCredentialRequiresUriWithoutSasError_RedactedSasUri()
718+
{
719+
// Arrange
720+
DataLakeServiceClient oauthService = GetServiceClient_OAuth();
721+
string fileSystemName = GetNewFileSystemName();
722+
string directoryName = GetNewDirectoryName();
723+
724+
await using DisposingFileSystem test = await GetNewFileSystem(service: oauthService, fileSystemName: fileSystemName);
725+
726+
DataLakeDirectoryClient directory = test.FileSystem.GetDirectoryClient(directoryName);
727+
728+
Response<UserDelegationKey> userDelegationKey = await oauthService.GetUserDelegationKeyAsync(
729+
startsOn: null,
730+
expiresOn: Recording.UtcNow.AddHours(1));
731+
732+
// Make a SAS with the DirectoryDepth/sdd
733+
DataLakeSasBuilder dataLakeSasBuilder = new(DataLakeSasPermissions.All, Recording.UtcNow.AddHours(1))
734+
{
735+
StartsOn = Recording.UtcNow.AddHours(-1),
736+
ExpiresOn = Recording.UtcNow.AddHours(1),
737+
Path = directoryName,
738+
IsDirectory = true
739+
};
740+
741+
DataLakeUriBuilder uriBuilder = new DataLakeUriBuilder(test.Container.Uri)
742+
{
743+
Sas = dataLakeSasBuilder.ToSasQueryParameters(userDelegationKey, test.Container.AccountName)
744+
};
745+
746+
Uri sasUri = uriBuilder.ToUri();
747+
748+
UriBuilder uriBuilder2 = new UriBuilder(sasUri);
749+
uriBuilder2.Query = "[REDACTED]";
750+
string redactedUri = uriBuilder2.Uri.ToString();
751+
752+
ArgumentException ex = Errors.SasCredentialRequiresUriWithoutSas<DataLakeUriBuilder>(sasUri);
753+
754+
// Assert
755+
Assert.IsTrue(ex.Message.Contains(redactedUri));
756+
Assert.IsFalse(ex.Message.Contains("st="));
757+
Assert.IsFalse(ex.Message.Contains("se="));
758+
Assert.IsFalse(ex.Message.Contains("sig="));
759+
}
715760
}
716761
}

sdk/storage/Azure.Storage.Files.Shares/assets.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,5 +2,5 @@
22
"AssetsRepo": "Azure/azure-sdk-assets",
33
"AssetsRepoPrefixPath": "net",
44
"TagPrefix": "net/storage/Azure.Storage.Files.Shares",
5-
"Tag": "net/storage/Azure.Storage.Files.Shares_92c08b30c4"
5+
"Tag": "net/storage/Azure.Storage.Files.Shares_cd8d6a7947"
66
}

sdk/storage/Azure.Storage.Files.Shares/tests/FileSasBuilderTests.cs

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -243,6 +243,45 @@ public void ShareUriBuilder_CustomUri_AccountShareFileTest()
243243
Assert.AreEqual(originalUri, newUri);
244244
}
245245

246+
[RecordedTest]
247+
public async Task SasCredentialRequiresUriWithoutSasError_RedactedSasUri()
248+
{
249+
// Arrange
250+
await using DisposingShare test = await GetTestShareAsync();
251+
252+
ShareSasBuilder fileSasBuilder = new ShareSasBuilder
253+
{
254+
StartsOn = Recording.UtcNow.AddHours(-1),
255+
ExpiresOn = Recording.UtcNow.AddHours(1),
256+
ShareName = test.Share.Name
257+
};
258+
259+
fileSasBuilder.SetPermissions(
260+
rawPermissions: "LDWCR",
261+
normalize: true);
262+
263+
StorageSharedKeyCredential sharedKeyCredential = new StorageSharedKeyCredential(TestConfigDefault.AccountName, TestConfigDefault.AccountKey);
264+
265+
ShareUriBuilder fileUriBuilder = new ShareUriBuilder(test.Share.Uri)
266+
{
267+
Sas = fileSasBuilder.ToSasQueryParameters(sharedKeyCredential)
268+
};
269+
270+
Uri sasUri = fileUriBuilder.ToUri();
271+
272+
UriBuilder uriBuilder = new UriBuilder(sasUri);
273+
uriBuilder.Query = "[REDACTED]";
274+
string redactedUri = uriBuilder.Uri.ToString();
275+
276+
ArgumentException ex = Errors.SasCredentialRequiresUriWithoutSas<ShareUriBuilder>(sasUri);
277+
278+
// Assert
279+
Assert.IsTrue(ex.Message.Contains(redactedUri));
280+
Assert.IsFalse(ex.Message.Contains("st="));
281+
Assert.IsFalse(ex.Message.Contains("se="));
282+
Assert.IsFalse(ex.Message.Contains("sig="));
283+
}
284+
246285
private ShareSasBuilder BuildFileSasBuilder(bool includeFilePath, TestConstants constants, string shareName, string filePath)
247286
{
248287
var fileSasBuilder = new ShareSasBuilder

sdk/storage/Azure.Storage.Queues/assets.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,5 +2,5 @@
22
"AssetsRepo": "Azure/azure-sdk-assets",
33
"AssetsRepoPrefixPath": "net",
44
"TagPrefix": "net/storage/Azure.Storage.Queues",
5-
"Tag": "net/storage/Azure.Storage.Queues_ef3164e0b2"
5+
"Tag": "net/storage/Azure.Storage.Queues_ef5347e834"
66
}

sdk/storage/Azure.Storage.Queues/tests/QueueSasBuilderTests.cs

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -235,6 +235,45 @@ public void QueueUriBuilder_CustomUri_AccountShareFileTest()
235235
Assert.AreEqual(originalUri, newUri);
236236
}
237237

238+
[RecordedTest]
239+
public async Task SasCredentialRequiresUriWithoutSasError_RedactedSasUri()
240+
{
241+
// Arrange
242+
await using DisposingQueue test = await GetTestQueueAsync();
243+
244+
QueueSasBuilder queueSasBuilder = new QueueSasBuilder
245+
{
246+
StartsOn = Recording.UtcNow.AddHours(-1),
247+
ExpiresOn = Recording.UtcNow.AddHours(1),
248+
QueueName = test.Queue.Name
249+
};
250+
251+
queueSasBuilder.SetPermissions(
252+
rawPermissions: "raup",
253+
normalize: true);
254+
255+
StorageSharedKeyCredential sharedKeyCredential = new StorageSharedKeyCredential(TestConfigDefault.AccountName, TestConfigDefault.AccountKey);
256+
257+
QueueUriBuilder queueUriBuilder = new QueueUriBuilder(test.Queue.Uri)
258+
{
259+
Sas = queueSasBuilder.ToSasQueryParameters(sharedKeyCredential)
260+
};
261+
262+
Uri sasUri = queueUriBuilder.ToUri();
263+
264+
UriBuilder uriBuilder = new UriBuilder(sasUri);
265+
uriBuilder.Query = "[REDACTED]";
266+
string redactedUri = uriBuilder.Uri.ToString();
267+
268+
ArgumentException ex = Errors.SasCredentialRequiresUriWithoutSas<QueueUriBuilder>(sasUri);
269+
270+
// Assert
271+
Assert.IsTrue(ex.Message.Contains(redactedUri));
272+
Assert.IsFalse(ex.Message.Contains("st="));
273+
Assert.IsFalse(ex.Message.Contains("se="));
274+
Assert.IsFalse(ex.Message.Contains("sig="));
275+
}
276+
238277
private QueueSasBuilder BuildQueueSasBuilder(TestConstants constants, string queueName)
239278
{
240279
var queueSasBuilder = new QueueSasBuilder

0 commit comments

Comments
 (0)