Skip to content

Commit

Permalink
Fix string comparison with differing timestamps (#2739)
Browse files Browse the repository at this point in the history
  • Loading branch information
msbw2 committed Jul 22, 2024
1 parent 1c3e895 commit e67b25b
Show file tree
Hide file tree
Showing 2 changed files with 43 additions and 4 deletions.
37 changes: 33 additions & 4 deletions test/Microsoft.IdentityModel.TestUtils/IdentityComparer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
using System.Security.Cryptography;
using System.Security.Cryptography.X509Certificates;
using System.Text.Json;
using System.Text.RegularExpressions;
using Microsoft.IdentityModel.JsonWebTokens;
using Microsoft.IdentityModel.Protocols;
using Microsoft.IdentityModel.Protocols.OpenIdConnect;
Expand Down Expand Up @@ -1601,6 +1602,8 @@ public static bool AreStringsEqual(object object1, object object2, CompareContex
return AreStringsEqual(object1, object2, "str1", "str2", context);
}

private static readonly Regex timestampRegex = new Regex(@"\d{1,2}:\d{2}:\d{2}", RegexOptions.Compiled);

public static bool AreStringsEqual(object object1, object object2, string name1, string name2, CompareContext context)
{
var localContext = new CompareContext(context);
Expand All @@ -1624,10 +1627,36 @@ public static bool AreStringsEqual(object object1, object object2, string name1,

if (!string.Equals(str1, str2, context.StringComparison))
{
localContext.Diffs.Add($"'{name1}' != '{name2}', StringComparison: '{context.StringComparison}'");
localContext.Diffs.Add($"'{str1}'");
localContext.Diffs.Add($"!=");
localContext.Diffs.Add($"'{str2}'");
// Try to find timestamps in the strings which might differ due to skew and compare them with epsilon.
MatchCollection match1 = timestampRegex.Matches(str1);
MatchCollection match2 = timestampRegex.Matches(str2);
int matched = 0;
// Check that at least one timestamp matched and both strings matched the same number of timestamps.
if (match1.Count != 0 && match1.Count == match2.Count)
{
for (int i = 0; i < match1.Count; i++)
{
string time1 = match1[i].Value;
string time2 = match2[i].Value;
// If the matches match string-wise exactly or are parseable DateTimes and are equal within epsilon count as matched.
if (time1 == time2 ||
(DateTime.TryParse(time1, out DateTime datetime1) &&
DateTime.TryParse(time2, out DateTime datetime2) &&
AreDatesEqualWithEpsilon(datetime1, datetime2, 1)))
{
matched++;
}
}
}

// If no fancy timestamp comparison happened or some timestamps didn't match add the diff.
if (matched == 0 || matched != match1.Count)
{
localContext.Diffs.Add($"'{name1}' != '{name2}', StringComparison: '{context.StringComparison}'");
localContext.Diffs.Add($"'{str1}'");
localContext.Diffs.Add($"!=");
localContext.Diffs.Add($"'{str2}'");
}
}

return context.Merge(localContext);
Expand Down
10 changes: 10 additions & 0 deletions test/Microsoft.IdentityModel.Tokens.Tests/IdentityComparerTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -580,6 +580,16 @@ public void CompareStrings()
Assert.True(context.Diffs[3] == $"'{string2}'");
}

[Fact]
public void CompareStringsWithTimestamps()
{
TestUtilities.WriteHeader($"{this}.{nameof(CompareStringsWithTimestamps)}", true);
var context = new CompareContext($"{this}.{nameof(CompareStringsWithTimestamps)}");
DateTime now = DateTime.UtcNow;
IdentityComparer.AreEqual($"{now:HH:mm:ss} {now.AddSeconds(1):HH:mm:ss}", $"{now.AddSeconds(1):HH:mm:ss} {now:HH:mm:ss}", context);
Assert.Empty(context.Diffs);
}

[Fact]
public void CompareSymmetricSecurityKeys()
{
Expand Down

0 comments on commit e67b25b

Please sign in to comment.