Skip to content

Commit f5b425d

Browse files
authored
properly escape single quotes values in PK and RK Uri paths (Azure#21650)
* properly escape single quotes values in PK and RK Uri paths
1 parent 0f550f5 commit f5b425d

11 files changed

+1522
-35
lines changed

sdk/tables/Azure.Data.Tables/src/TableClient.cs

Lines changed: 32 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -321,7 +321,7 @@ public virtual Response<TableItem> Create(CancellationToken cancellationToken =
321321
try
322322
{
323323
var response = _tableOperations.Create(
324-
new TableProperties() { TableName = Name },
324+
new TableProperties { TableName = Name },
325325
null,
326326
_defaultQueryOptions,
327327
cancellationToken);
@@ -347,7 +347,7 @@ public virtual async Task<Response<TableItem>> CreateAsync(CancellationToken can
347347
try
348348
{
349349
var response = await _tableOperations.CreateAsync(
350-
new TableProperties() { TableName = Name },
350+
new TableProperties { TableName = Name },
351351
null,
352352
_defaultQueryOptions,
353353
cancellationToken)
@@ -374,7 +374,7 @@ public virtual Response<TableItem> CreateIfNotExists(CancellationToken cancellat
374374
try
375375
{
376376
var response = _tableOperations.Create(
377-
new TableProperties() { TableName = Name },
377+
new TableProperties { TableName = Name },
378378
null,
379379
_defaultQueryOptions,
380380
cancellationToken);
@@ -404,7 +404,7 @@ public virtual async Task<Response<TableItem>> CreateIfNotExistsAsync(Cancellati
404404
try
405405
{
406406
var response = await _tableOperations.CreateAsync(
407-
new TableProperties() { TableName = Name },
407+
new TableProperties { TableName = Name },
408408
null,
409409
_defaultQueryOptions,
410410
cancellationToken)
@@ -577,7 +577,7 @@ public virtual Response<T> GetEntity<T>(string partitionKey, string rowKey, IEnu
577577
Name,
578578
partitionKey,
579579
rowKey,
580-
queryOptions: new QueryOptions() { Format = _defaultQueryOptions.Format, Select = selectArg },
580+
queryOptions: new QueryOptions { Format = _defaultQueryOptions.Format, Select = selectArg },
581581
cancellationToken: cancellationToken);
582582

583583
var result = ((Dictionary<string, object>)response.Value).ToTableEntity<T>();
@@ -620,7 +620,7 @@ public virtual async Task<Response<T>> GetEntityAsync<T>(
620620
Name,
621621
partitionKey,
622622
rowKey,
623-
queryOptions: new QueryOptions() { Format = _defaultQueryOptions.Format, Select = selectArg },
623+
queryOptions: new QueryOptions { Format = _defaultQueryOptions.Format, Select = selectArg },
624624
cancellationToken: cancellationToken)
625625
.ConfigureAwait(false);
626626

@@ -660,16 +660,16 @@ public virtual async Task<Response> UpsertEntityAsync<T>(
660660
{
661661
TableUpdateMode.Replace => await _tableOperations.UpdateEntityAsync(
662662
Name,
663-
entity!.PartitionKey,
664-
entity.RowKey,
663+
TableOdataFilter.EscapeStringValue(entity!.PartitionKey),
664+
TableOdataFilter.EscapeStringValue(entity.RowKey),
665665
tableEntityProperties: entity.ToOdataAnnotatedDictionary(),
666666
queryOptions: _defaultQueryOptions,
667667
cancellationToken: cancellationToken)
668668
.ConfigureAwait(false),
669669
TableUpdateMode.Merge => await _tableOperations.MergeEntityAsync(
670670
Name,
671-
entity!.PartitionKey,
672-
entity.RowKey,
671+
TableOdataFilter.EscapeStringValue(entity!.PartitionKey),
672+
TableOdataFilter.EscapeStringValue(entity.RowKey),
673673
tableEntityProperties: entity.ToOdataAnnotatedDictionary(),
674674
queryOptions: _defaultQueryOptions,
675675
cancellationToken: cancellationToken)
@@ -708,15 +708,15 @@ public virtual Response UpsertEntity<T>(T entity, TableUpdateMode mode = TableUp
708708
{
709709
TableUpdateMode.Replace => _tableOperations.UpdateEntity(
710710
Name,
711-
entity!.PartitionKey,
712-
entity.RowKey,
711+
TableOdataFilter.EscapeStringValue(entity!.PartitionKey),
712+
TableOdataFilter.EscapeStringValue(entity.RowKey),
713713
tableEntityProperties: entity.ToOdataAnnotatedDictionary(),
714714
queryOptions: _defaultQueryOptions,
715715
cancellationToken: cancellationToken),
716716
TableUpdateMode.Merge => _tableOperations.MergeEntity(
717717
Name,
718-
entity!.PartitionKey,
719-
entity.RowKey,
718+
TableOdataFilter.EscapeStringValue(entity!.PartitionKey),
719+
TableOdataFilter.EscapeStringValue(entity.RowKey),
720720
tableEntityProperties: entity.ToOdataAnnotatedDictionary(),
721721
queryOptions: _defaultQueryOptions,
722722
cancellationToken: cancellationToken),
@@ -770,8 +770,8 @@ public virtual async Task<Response> UpdateEntityAsync<T>(
770770
{
771771
return await _tableOperations.UpdateEntityAsync(
772772
Name,
773-
entity.PartitionKey,
774-
entity.RowKey,
773+
TableOdataFilter.EscapeStringValue(entity.PartitionKey),
774+
TableOdataFilter.EscapeStringValue(entity.RowKey),
775775
tableEntityProperties: entity.ToOdataAnnotatedDictionary(),
776776
ifMatch: ifMatch.ToString(),
777777
queryOptions: _defaultQueryOptions,
@@ -782,8 +782,8 @@ public virtual async Task<Response> UpdateEntityAsync<T>(
782782
{
783783
return await _tableOperations.MergeEntityAsync(
784784
Name,
785-
entity!.PartitionKey,
786-
entity.RowKey,
785+
TableOdataFilter.EscapeStringValue(entity!.PartitionKey),
786+
TableOdataFilter.EscapeStringValue(entity.RowKey),
787787
tableEntityProperties: entity.ToOdataAnnotatedDictionary(),
788788
ifMatch: ifMatch.ToString(),
789789
queryOptions: _defaultQueryOptions,
@@ -836,8 +836,8 @@ public virtual Response UpdateEntity<T>(T entity, ETag ifMatch, TableUpdateMode
836836
{
837837
return _tableOperations.UpdateEntity(
838838
Name,
839-
entity!.PartitionKey,
840-
entity!.RowKey,
839+
TableOdataFilter.EscapeStringValue(entity!.PartitionKey),
840+
TableOdataFilter.EscapeStringValue(entity!.RowKey),
841841
tableEntityProperties: entity.ToOdataAnnotatedDictionary(),
842842
ifMatch: ifMatch.ToString(),
843843
queryOptions: _defaultQueryOptions,
@@ -847,8 +847,8 @@ public virtual Response UpdateEntity<T>(T entity, ETag ifMatch, TableUpdateMode
847847
{
848848
return _tableOperations.MergeEntity(
849849
Name,
850-
entity.PartitionKey,
851-
entity.RowKey,
850+
TableOdataFilter.EscapeStringValue(entity.PartitionKey),
851+
TableOdataFilter.EscapeStringValue(entity.RowKey),
852852
tableEntityProperties: entity.ToOdataAnnotatedDictionary(),
853853
ifMatch: ifMatch.ToString(),
854854
queryOptions: _defaultQueryOptions,
@@ -975,7 +975,7 @@ public virtual AsyncPageable<T> QueryAsync<T>(
975975
{
976976
var response = await _tableOperations.QueryEntitiesAsync(
977977
Name,
978-
queryOptions: new QueryOptions() { Format = _defaultQueryOptions.Format, Top = pageSizeHint, Filter = filter, Select = selectArg },
978+
queryOptions: new QueryOptions { Format = _defaultQueryOptions.Format, Top = pageSizeHint, Filter = filter, Select = selectArg },
979979
cancellationToken: cancellationToken)
980980
.ConfigureAwait(false);
981981

@@ -1000,7 +1000,7 @@ public virtual AsyncPageable<T> QueryAsync<T>(
10001000

10011001
var response = await _tableOperations.QueryEntitiesAsync(
10021002
Name,
1003-
queryOptions: new QueryOptions() { Format = _defaultQueryOptions.Format, Top = pageSizeHint, Filter = filter, Select = selectArg },
1003+
queryOptions: new QueryOptions { Format = _defaultQueryOptions.Format, Top = pageSizeHint, Filter = filter, Select = selectArg },
10041004
nextPartitionKey: NextPartitionKey,
10051005
nextRowKey: NextRowKey,
10061006
cancellationToken: cancellationToken)
@@ -1054,7 +1054,7 @@ public virtual Pageable<T> Query<T>(
10541054
scope.Start();
10551055
try
10561056
{
1057-
var queryOptions = new QueryOptions() { Format = _defaultQueryOptions.Format, Top = pageSizeHint, Filter = filter, Select = selectArg };
1057+
var queryOptions = new QueryOptions { Format = _defaultQueryOptions.Format, Top = pageSizeHint, Filter = filter, Select = selectArg };
10581058

10591059
var response = _tableOperations.QueryEntities(
10601060
Name,
@@ -1080,7 +1080,7 @@ public virtual Pageable<T> Query<T>(
10801080
{
10811081
var (NextPartitionKey, NextRowKey) = ParseContinuationToken(continuationToken);
10821082

1083-
var queryOptions = new QueryOptions() { Format = _defaultQueryOptions.Format, Top = pageSizeHint, Filter = filter, Select = selectArg };
1083+
var queryOptions = new QueryOptions { Format = _defaultQueryOptions.Format, Top = pageSizeHint, Filter = filter, Select = selectArg };
10841084

10851085
var response = _tableOperations.QueryEntities(
10861086
Name,
@@ -1422,8 +1422,8 @@ private MultipartContent BuildChangeSet(
14221422
new QueryOptions { Format = _defaultQueryOptions.Format!.Value }),
14231423
TableTransactionActionType.Delete => batchOperations.CreateDeleteEntityRequest(
14241424
Name,
1425-
item.Entity.PartitionKey,
1426-
item.Entity.RowKey,
1425+
TableOdataFilter.EscapeStringValue(item.Entity.PartitionKey),
1426+
TableOdataFilter.EscapeStringValue(item.Entity.RowKey),
14271427
item.ETag == default ? ETag.All.ToString() : item.ETag.ToString(),
14281428
null,
14291429
new QueryOptions { Format = _defaultQueryOptions.Format!.Value }),
@@ -1445,16 +1445,16 @@ private HttpMessage CreateUpdateOrMergeRequest(TableRestClient batchOperations,
14451445
{
14461446
TableUpdateMode.Replace => batchOperations.CreateUpdateEntityRequest(
14471447
Name,
1448-
entity.PartitionKey,
1449-
entity.RowKey,
1448+
TableOdataFilter.EscapeStringValue(entity.PartitionKey),
1449+
TableOdataFilter.EscapeStringValue(entity.RowKey),
14501450
null,
14511451
ifMatch == default ? null : ifMatch.ToString(),
14521452
entity.ToOdataAnnotatedDictionary(),
14531453
new QueryOptions { Format = _defaultQueryOptions.Format!.Value }),
14541454
TableUpdateMode.Merge => batchOperations.CreateMergeEntityRequest(
14551455
Name,
1456-
entity.PartitionKey,
1457-
entity.RowKey,
1456+
TableOdataFilter.EscapeStringValue(entity.PartitionKey),
1457+
TableOdataFilter.EscapeStringValue(entity.RowKey),
14581458
null,
14591459
ifMatch == default ? null : ifMatch.ToString(),
14601460
entity.ToOdataAnnotatedDictionary(),

sdk/tables/Azure.Data.Tables/src/TableOdataFilter.cs

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -68,9 +68,9 @@ public static string Create(FormattableString filter)
6868
DateTime x => $"{XmlConstants.LiteralPrefixDateTime}'{XmlConvert.ToString(x.ToUniversalTime(), XmlDateTimeSerializationMode.RoundtripKind)}'",
6969

7070
// Text
71-
string x => $"'{x.Replace("'", "''")}'",
72-
char x => $"'{x.ToString().Replace("'", "''")}'",
73-
StringBuilder x => $"'{x.Replace("'", "''")}'",
71+
string x => $"'{EscapeStringValue(x)}'",
72+
char x => $"'{EscapeStringValue(x)}'",
73+
StringBuilder x => $"'{EscapeStringValue(x)}'",
7474

7575
// Everything else
7676
object x => throw new ArgumentException(
@@ -80,5 +80,15 @@ public static string Create(FormattableString filter)
8080

8181
return string.Format(CultureInfo.InvariantCulture, filter.Format, args);
8282
}
83+
84+
internal static string EscapeStringValue(string s) => s.Replace("'", "''");
85+
internal static StringBuilder EscapeStringValue(StringBuilder s) => s.Replace("'", "''");
86+
87+
internal static string EscapeStringValue(char s) =>
88+
s switch
89+
{
90+
_ when s == '\'' => "''",
91+
_ => s.ToString()
92+
};
8393
}
8494
}

0 commit comments

Comments
 (0)