From 87eee80decdea1346acbd3c5c2670de49a658818 Mon Sep 17 00:00:00 2001 From: Simon Cropp Date: Fri, 31 Jul 2026 23:43:52 +1000 Subject: [PATCH 1/2] Add GuidFormats option to ScrubInlineGuids ScrubInlineGuids matches both D and N format guids. Since any 32 character hex sequence (an MD5 hash for example) is a valid N format guid, that content is scrubbed too. Add a GuidFormats flags enum (Dashed, Undashed, All) and an optional formats parameter (defaulting to All) to all ScrubInlineGuids overloads, so scrubbing can be limited to specific formats. Fixes #1822 --- docs/guids.md | 31 +++++++++++++++++-- docs/mdsource/guids.source.md | 11 +++++++ ...ts.ScrubInlineGuidsDashedOnly.verified.txt | 1 + ....ScrubInlineGuidsUndashedOnly.verified.txt | 1 + src/Verify.Tests/GuidScrubberTests.cs | 16 ++++++++++ .../Serialization/Scrubbers/GuidFormats.cs | 26 ++++++++++++++++ .../Serialization/Scrubbers/GuidMatcher.cs | 14 +++++++++ ...Settings_ExtensionMappedGlobalScrubbers.cs | 10 ++++-- .../VerifierSettings_GlobalScrubbers.cs | 9 ++++-- ...ttings_ExtensionMappedInstanceScrubbers.cs | 10 ++++-- .../VerifySettings_InstanceScrubbers.cs | 9 ++++-- src/Verify/SettingsTask_Scrubbing.cs | 12 +++---- 12 files changed, 130 insertions(+), 20 deletions(-) create mode 100644 src/Verify.Tests/GuidScrubberTests.ScrubInlineGuidsDashedOnly.verified.txt create mode 100644 src/Verify.Tests/GuidScrubberTests.ScrubInlineGuidsUndashedOnly.verified.txt create mode 100644 src/Verify/Serialization/Scrubbers/GuidFormats.cs diff --git a/docs/guids.md b/docs/guids.md index 503f9cde1..55adea984 100644 --- a/docs/guids.md +++ b/docs/guids.md @@ -139,6 +139,33 @@ public static class ModuleInitializer +### Limiting to specific formats + +By default both the `D` and `N` formats are scrubbed. To restrict scrubbing to specific formats, pass a `GuidFormats` value. This is useful for avoiding the scrubbing of 32 character hex content (an MD5 hash for example) that would otherwise match the `N` format: + + + +```cs +// Only the "D" format is scrubbed. The 32 char hex hash is left untouched. +[Fact] +public Task ScrubInlineGuidsDashedOnly() => + Verify("guid: 173535ae-995b-4cc6-a74e-8cd4be57039c hash: 5d41402abc4b2a76b9719d911017c592") + .ScrubInlineGuids(GuidFormats.Dashed); +``` +snippet source | anchor + + +Results in the following, where the `D` format Guid is scrubbed but the hash is left untouched: + + + +```txt +guid: Guid_1 hash: 5d41402abc4b2a76b9719d911017c592 +``` +snippet source | anchor + + + ## Named Guid Specific Guids can be named. When any of those Guids are found, it will be replaced with the supplied name. @@ -184,7 +211,7 @@ public Task NamedGuidFluent() .AddNamedGuid(guid, "instanceNamed"); } ``` -snippet source | anchor +snippet source | anchor @@ -219,7 +246,7 @@ public Task InferredNamedGuidFluent() .AddNamedGuid(namedGuid); } ``` -snippet source | anchor +snippet source | anchor Result: diff --git a/docs/mdsource/guids.source.md b/docs/mdsource/guids.source.md index a8d6127b4..b3a781a27 100644 --- a/docs/mdsource/guids.source.md +++ b/docs/mdsource/guids.source.md @@ -51,6 +51,17 @@ snippet: ScrubInlineGuidsFluent snippet: ScrubInlineGuidsGlobal +### Limiting to specific formats + +By default both the `D` and `N` formats are scrubbed. To restrict scrubbing to specific formats, pass a `GuidFormats` value. This is useful for avoiding the scrubbing of 32 character hex content (an MD5 hash for example) that would otherwise match the `N` format: + +snippet: ScrubInlineGuidsDashedOnly + +Results in the following, where the `D` format Guid is scrubbed but the hash is left untouched: + +snippet: GuidScrubberTests.ScrubInlineGuidsDashedOnly.verified.txt + + ## Named Guid Specific Guids can be named. When any of those Guids are found, it will be replaced with the supplied name. diff --git a/src/Verify.Tests/GuidScrubberTests.ScrubInlineGuidsDashedOnly.verified.txt b/src/Verify.Tests/GuidScrubberTests.ScrubInlineGuidsDashedOnly.verified.txt new file mode 100644 index 000000000..9e53577b9 --- /dev/null +++ b/src/Verify.Tests/GuidScrubberTests.ScrubInlineGuidsDashedOnly.verified.txt @@ -0,0 +1 @@ +guid: Guid_1 hash: 5d41402abc4b2a76b9719d911017c592 \ No newline at end of file diff --git a/src/Verify.Tests/GuidScrubberTests.ScrubInlineGuidsUndashedOnly.verified.txt b/src/Verify.Tests/GuidScrubberTests.ScrubInlineGuidsUndashedOnly.verified.txt new file mode 100644 index 000000000..a8e071fcd --- /dev/null +++ b/src/Verify.Tests/GuidScrubberTests.ScrubInlineGuidsUndashedOnly.verified.txt @@ -0,0 +1 @@ +guid: 173535ae-995b-4cc6-a74e-8cd4be57039c hash: Guid_1 \ No newline at end of file diff --git a/src/Verify.Tests/GuidScrubberTests.cs b/src/Verify.Tests/GuidScrubberTests.cs index ac83d2b95..e18ba9b05 100644 --- a/src/Verify.Tests/GuidScrubberTests.cs +++ b/src/Verify.Tests/GuidScrubberTests.cs @@ -123,6 +123,22 @@ public Task InlineNamedGuidNFormat() => Verify("value: c8eeaf99d5c4434185434597c3fd40c9") .ScrubInlineGuids(); + #region ScrubInlineGuidsDashedOnly + + // Only the "D" format is scrubbed. The 32 char hex hash is left untouched. + [Fact] + public Task ScrubInlineGuidsDashedOnly() => + Verify("guid: 173535ae-995b-4cc6-a74e-8cd4be57039c hash: 5d41402abc4b2a76b9719d911017c592") + .ScrubInlineGuids(GuidFormats.Dashed); + + #endregion + + // Only the "N" format is scrubbed. The "D" format guid is left untouched. + [Fact] + public Task ScrubInlineGuidsUndashedOnly() => + Verify("guid: 173535ae-995b-4cc6-a74e-8cd4be57039c hash: 5d41402abc4b2a76b9719d911017c592") + .ScrubInlineGuids(GuidFormats.Undashed); + #region NamedGuidFluent [Fact] diff --git a/src/Verify/Serialization/Scrubbers/GuidFormats.cs b/src/Verify/Serialization/Scrubbers/GuidFormats.cs new file mode 100644 index 000000000..e497f7fcf --- /dev/null +++ b/src/Verify/Serialization/Scrubbers/GuidFormats.cs @@ -0,0 +1,26 @@ +namespace VerifyTests; + +/// +/// The formats that matches. +/// +[Flags] +public enum GuidFormats +{ + /// + /// The "D" format: 32 hex digits separated by hyphens (e.g. 00000000-0000-0000-0000-000000000000). + /// The "B" and "P" formats are covered by this since they wrap the "D" format in delimiters. + /// + Dashed = 1, + + /// + /// The "N" format: 32 hex digits with no separators (e.g. 00000000000000000000000000000000). + /// Note that any 32 character hex sequence (an MD5 hash for example) is a valid "N" format Guid, so + /// content of that exact length will also be scrubbed. + /// + Undashed = 2, + + /// + /// Both and . + /// + All = Dashed | Undashed +} diff --git a/src/Verify/Serialization/Scrubbers/GuidMatcher.cs b/src/Verify/Serialization/Scrubbers/GuidMatcher.cs index 68853261a..8ec9c7e35 100644 --- a/src/Verify/Serialization/Scrubbers/GuidMatcher.cs +++ b/src/Verify/Serialization/Scrubbers/GuidMatcher.cs @@ -26,6 +26,20 @@ static class GuidMatcher static counter => counter.ScrubGuids, requireWordBoundary: true); + // The scrubbers for the requested formats, in the order they should be applied. + public static IEnumerable ForFormats(GuidFormats formats) + { + if ((formats & GuidFormats.Dashed) != 0) + { + yield return Instance; + } + + if ((formats & GuidFormats.Undashed) != 0) + { + yield return NInstance; + } + } + static string? Match(CharSpan window, Counter counter, IReadOnlyDictionary context) { // Cheap prefilter: the "D" format has dashes at fixed offsets diff --git a/src/Verify/Serialization/Scrubbers/VerifierSettings_ExtensionMappedGlobalScrubbers.cs b/src/Verify/Serialization/Scrubbers/VerifierSettings_ExtensionMappedGlobalScrubbers.cs index 36edbd50b..15be27a08 100644 --- a/src/Verify/Serialization/Scrubbers/VerifierSettings_ExtensionMappedGlobalScrubbers.cs +++ b/src/Verify/Serialization/Scrubbers/VerifierSettings_ExtensionMappedGlobalScrubbers.cs @@ -90,10 +90,14 @@ public static void ScrubEmptyLines(string extension) => /// /// Replace inline s with a placeholder. /// - public static void ScrubInlineGuids(string extension) + /// The file extension to apply the scrubber to. + /// The formats to match. Defaults to . + public static void ScrubInlineGuids(string extension, GuidFormats formats = GuidFormats.All) { - AddScrubber(extension, GuidMatcher.Instance); - AddScrubber(extension, GuidMatcher.NInstance); + foreach (var scrubber in GuidMatcher.ForFormats(formats)) + { + AddScrubber(extension, scrubber); + } } /// diff --git a/src/Verify/Serialization/Scrubbers/VerifierSettings_GlobalScrubbers.cs b/src/Verify/Serialization/Scrubbers/VerifierSettings_GlobalScrubbers.cs index c26a5339a..f1dc767e3 100644 --- a/src/Verify/Serialization/Scrubbers/VerifierSettings_GlobalScrubbers.cs +++ b/src/Verify/Serialization/Scrubbers/VerifierSettings_GlobalScrubbers.cs @@ -127,10 +127,13 @@ public static void ScrubInlineDates( /// /// Replace inline s with a placeholder. /// - public static void ScrubInlineGuids() + /// The formats to match. Defaults to . + public static void ScrubInlineGuids(GuidFormats formats = GuidFormats.All) { - AddScrubber(GuidMatcher.Instance); - AddScrubber(GuidMatcher.NInstance); + foreach (var scrubber in GuidMatcher.ForFormats(formats)) + { + AddScrubber(scrubber); + } } /// diff --git a/src/Verify/Serialization/Scrubbers/VerifySettings_ExtensionMappedInstanceScrubbers.cs b/src/Verify/Serialization/Scrubbers/VerifySettings_ExtensionMappedInstanceScrubbers.cs index d31d9cb3a..a3c1c048e 100644 --- a/src/Verify/Serialization/Scrubbers/VerifySettings_ExtensionMappedInstanceScrubbers.cs +++ b/src/Verify/Serialization/Scrubbers/VerifySettings_ExtensionMappedInstanceScrubbers.cs @@ -78,10 +78,14 @@ public void ScrubLinesContaining(string extension, StringComparison comparison, /// /// Replace inline s with a placeholder. /// - public void ScrubInlineGuids(string extension) + /// The file extension to apply the scrubber to. + /// The formats to match. Defaults to . + public void ScrubInlineGuids(string extension, GuidFormats formats = GuidFormats.All) { - AddScrubber(extension, GuidMatcher.Instance); - AddScrubber(extension, GuidMatcher.NInstance); + foreach (var scrubber in GuidMatcher.ForFormats(formats)) + { + AddScrubber(extension, scrubber); + } } /// diff --git a/src/Verify/Serialization/Scrubbers/VerifySettings_InstanceScrubbers.cs b/src/Verify/Serialization/Scrubbers/VerifySettings_InstanceScrubbers.cs index 04cebe482..a7b710b1a 100644 --- a/src/Verify/Serialization/Scrubbers/VerifySettings_InstanceScrubbers.cs +++ b/src/Verify/Serialization/Scrubbers/VerifySettings_InstanceScrubbers.cs @@ -85,15 +85,18 @@ public void ScrubLinesContaining(StringComparison comparison, params string[] st /// /// Replace inline s with a placeholder. /// - public void ScrubInlineGuids() + /// The formats to match. Defaults to . + public void ScrubInlineGuids(GuidFormats formats = GuidFormats.All) { if (serialization.ScrubGuids == false) { throw new("ScrubGuids is disabled. Call .ScrubGuids() before calling .ScrubInlineGuids()."); } - AddScrubber(GuidMatcher.Instance); - AddScrubber(GuidMatcher.NInstance); + foreach (var scrubber in GuidMatcher.ForFormats(formats)) + { + AddScrubber(scrubber); + } } /// diff --git a/src/Verify/SettingsTask_Scrubbing.cs b/src/Verify/SettingsTask_Scrubbing.cs index 6de2afef6..caa82b2d9 100644 --- a/src/Verify/SettingsTask_Scrubbing.cs +++ b/src/Verify/SettingsTask_Scrubbing.cs @@ -42,11 +42,11 @@ public SettingsTask AddScrubber(string extension, Action scrubber return this; } - /// + /// [Pure] - public SettingsTask ScrubInlineGuids() + public SettingsTask ScrubInlineGuids(GuidFormats formats = GuidFormats.All) { - CurrentSettings.ScrubInlineGuids(); + CurrentSettings.ScrubInlineGuids(formats); return this; } @@ -66,11 +66,11 @@ public SettingsTask ScrubNumericIds() return this; } - /// + /// [Pure] - public SettingsTask ScrubInlineGuids(string extension) + public SettingsTask ScrubInlineGuids(string extension, GuidFormats formats = GuidFormats.All) { - CurrentSettings.ScrubInlineGuids(extension); + CurrentSettings.ScrubInlineGuids(extension, formats); return this; } From 8d761e2daf386720a282baf4ab9af031d41cbb27 Mon Sep 17 00:00:00 2001 From: Simon Cropp Date: Fri, 31 Jul 2026 23:51:54 +1000 Subject: [PATCH 2/2] Preserve binary compatibility with overloads Adding an optional parameter to the existing ScrubInlineGuids methods changed their signatures, which is a binary breaking change: an assembly compiled against the old parameterless signature would fail with MissingMethodException at runtime, since optional arguments are baked in at the call site. Keep every original signature untouched and add sibling overloads that take GuidFormats. --- ...Settings_ExtensionMappedGlobalScrubbers.cs | 11 ++++++++-- .../VerifierSettings_GlobalScrubbers.cs | 10 ++++++++-- ...ttings_ExtensionMappedInstanceScrubbers.cs | 11 ++++++++-- .../VerifySettings_InstanceScrubbers.cs | 10 ++++++++-- src/Verify/SettingsTask_Scrubbing.cs | 20 +++++++++++++++++-- 5 files changed, 52 insertions(+), 10 deletions(-) diff --git a/src/Verify/Serialization/Scrubbers/VerifierSettings_ExtensionMappedGlobalScrubbers.cs b/src/Verify/Serialization/Scrubbers/VerifierSettings_ExtensionMappedGlobalScrubbers.cs index 15be27a08..a3a6a9799 100644 --- a/src/Verify/Serialization/Scrubbers/VerifierSettings_ExtensionMappedGlobalScrubbers.cs +++ b/src/Verify/Serialization/Scrubbers/VerifierSettings_ExtensionMappedGlobalScrubbers.cs @@ -91,8 +91,15 @@ public static void ScrubEmptyLines(string extension) => /// Replace inline s with a placeholder. /// /// The file extension to apply the scrubber to. - /// The formats to match. Defaults to . - public static void ScrubInlineGuids(string extension, GuidFormats formats = GuidFormats.All) + public static void ScrubInlineGuids(string extension) => + ScrubInlineGuids(extension, GuidFormats.All); + + /// + /// Replace inline s with a placeholder. + /// + /// The file extension to apply the scrubber to. + /// The formats to match. + public static void ScrubInlineGuids(string extension, GuidFormats formats) { foreach (var scrubber in GuidMatcher.ForFormats(formats)) { diff --git a/src/Verify/Serialization/Scrubbers/VerifierSettings_GlobalScrubbers.cs b/src/Verify/Serialization/Scrubbers/VerifierSettings_GlobalScrubbers.cs index f1dc767e3..b21b1d910 100644 --- a/src/Verify/Serialization/Scrubbers/VerifierSettings_GlobalScrubbers.cs +++ b/src/Verify/Serialization/Scrubbers/VerifierSettings_GlobalScrubbers.cs @@ -127,8 +127,14 @@ public static void ScrubInlineDates( /// /// Replace inline s with a placeholder. /// - /// The formats to match. Defaults to . - public static void ScrubInlineGuids(GuidFormats formats = GuidFormats.All) + public static void ScrubInlineGuids() => + ScrubInlineGuids(GuidFormats.All); + + /// + /// Replace inline s with a placeholder. + /// + /// The formats to match. + public static void ScrubInlineGuids(GuidFormats formats) { foreach (var scrubber in GuidMatcher.ForFormats(formats)) { diff --git a/src/Verify/Serialization/Scrubbers/VerifySettings_ExtensionMappedInstanceScrubbers.cs b/src/Verify/Serialization/Scrubbers/VerifySettings_ExtensionMappedInstanceScrubbers.cs index a3c1c048e..5675b4718 100644 --- a/src/Verify/Serialization/Scrubbers/VerifySettings_ExtensionMappedInstanceScrubbers.cs +++ b/src/Verify/Serialization/Scrubbers/VerifySettings_ExtensionMappedInstanceScrubbers.cs @@ -79,8 +79,15 @@ public void ScrubLinesContaining(string extension, StringComparison comparison, /// Replace inline s with a placeholder. /// /// The file extension to apply the scrubber to. - /// The formats to match. Defaults to . - public void ScrubInlineGuids(string extension, GuidFormats formats = GuidFormats.All) + public void ScrubInlineGuids(string extension) => + ScrubInlineGuids(extension, GuidFormats.All); + + /// + /// Replace inline s with a placeholder. + /// + /// The file extension to apply the scrubber to. + /// The formats to match. + public void ScrubInlineGuids(string extension, GuidFormats formats) { foreach (var scrubber in GuidMatcher.ForFormats(formats)) { diff --git a/src/Verify/Serialization/Scrubbers/VerifySettings_InstanceScrubbers.cs b/src/Verify/Serialization/Scrubbers/VerifySettings_InstanceScrubbers.cs index a7b710b1a..f74b97d7b 100644 --- a/src/Verify/Serialization/Scrubbers/VerifySettings_InstanceScrubbers.cs +++ b/src/Verify/Serialization/Scrubbers/VerifySettings_InstanceScrubbers.cs @@ -85,8 +85,14 @@ public void ScrubLinesContaining(StringComparison comparison, params string[] st /// /// Replace inline s with a placeholder. /// - /// The formats to match. Defaults to . - public void ScrubInlineGuids(GuidFormats formats = GuidFormats.All) + public void ScrubInlineGuids() => + ScrubInlineGuids(GuidFormats.All); + + /// + /// Replace inline s with a placeholder. + /// + /// The formats to match. + public void ScrubInlineGuids(GuidFormats formats) { if (serialization.ScrubGuids == false) { diff --git a/src/Verify/SettingsTask_Scrubbing.cs b/src/Verify/SettingsTask_Scrubbing.cs index caa82b2d9..a69dd1fbb 100644 --- a/src/Verify/SettingsTask_Scrubbing.cs +++ b/src/Verify/SettingsTask_Scrubbing.cs @@ -42,9 +42,17 @@ public SettingsTask AddScrubber(string extension, Action scrubber return this; } + /// + [Pure] + public SettingsTask ScrubInlineGuids() + { + CurrentSettings.ScrubInlineGuids(); + return this; + } + /// [Pure] - public SettingsTask ScrubInlineGuids(GuidFormats formats = GuidFormats.All) + public SettingsTask ScrubInlineGuids(GuidFormats formats) { CurrentSettings.ScrubInlineGuids(formats); return this; @@ -66,9 +74,17 @@ public SettingsTask ScrubNumericIds() return this; } + /// + [Pure] + public SettingsTask ScrubInlineGuids(string extension) + { + CurrentSettings.ScrubInlineGuids(extension); + return this; + } + /// [Pure] - public SettingsTask ScrubInlineGuids(string extension, GuidFormats formats = GuidFormats.All) + public SettingsTask ScrubInlineGuids(string extension, GuidFormats formats) { CurrentSettings.ScrubInlineGuids(extension, formats); return this;