diff --git a/Directory.Packages.props b/Directory.Packages.props
index b120fe3..68a66aa 100644
--- a/Directory.Packages.props
+++ b/Directory.Packages.props
@@ -12,5 +12,7 @@ SPDX-License-Identifier: MIT
+
+
\ No newline at end of file
diff --git a/FuzzingTests/FuzzingTests.csproj b/FuzzingTests/FuzzingTests.csproj
new file mode 100644
index 0000000..dd14e74
--- /dev/null
+++ b/FuzzingTests/FuzzingTests.csproj
@@ -0,0 +1,23 @@
+
+
+
+
+
+ $(MainTargetFramework)
+ True
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/FuzzingTests/GitVersionVariables_Tests.cs b/FuzzingTests/GitVersionVariables_Tests.cs
new file mode 100644
index 0000000..6a4a9c8
--- /dev/null
+++ b/FuzzingTests/GitVersionVariables_Tests.cs
@@ -0,0 +1,23 @@
+// SPDX-FileCopyrightText: 2026 Frans van Dorsselaer
+//
+// SPDX-License-Identifier: MIT
+
+using System.Text.Json.Nodes;
+
+namespace FuzzingTests;
+
+[TestClass]
+sealed class GitVersionVariables_Tests
+{
+ [TestMethod]
+ public void JsonIsParsedCorrectly()
+ {
+ var jsonGenerator = JsonGenerator.GenerateExactlyTwoPairs();
+
+ Prop.ForAll(Arb.From(jsonGenerator), (JsonObject json) =>
+ {
+ var result = GitVersionVariables.FromJson(json.ToJsonString());
+ return result.Count == 2 && json.All(pair => result.TryGetValue(pair.Key, out var value) && value == pair.Value!.ToString());
+ }).QuickCheckThrowOnFailure();
+ }
+}
diff --git a/FuzzingTests/GlobalSuppressions.cs b/FuzzingTests/GlobalSuppressions.cs
new file mode 100644
index 0000000..f9a7d2a
--- /dev/null
+++ b/FuzzingTests/GlobalSuppressions.cs
@@ -0,0 +1,7 @@
+// SPDX-FileCopyrightText: 2026 Frans van Dorsselaer
+//
+// SPDX-License-Identifier: MIT
+
+using System.Diagnostics.CodeAnalysis;
+
+[assembly: SuppressMessage("Performance", "CA1812:Internal class is never instantiated", Justification = "We use internal test classes")]
diff --git a/FuzzingTests/JsonGenerator.cs b/FuzzingTests/JsonGenerator.cs
new file mode 100644
index 0000000..258e6f8
--- /dev/null
+++ b/FuzzingTests/JsonGenerator.cs
@@ -0,0 +1,48 @@
+// SPDX-FileCopyrightText: 2026 Frans van Dorsselaer
+//
+// SPDX-License-Identifier: MIT
+
+using System.Text.Json.Nodes;
+
+namespace FuzzingTests;
+
+static class JsonGenerator
+{
+ static readonly char[] AllowedKeyChars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz".ToCharArray();
+
+ public static Gen GenerateExactlyTwoPairs()
+ {
+ // GitVersion keys are always strings of length 1 to 20, consisting of uppercase and lowercase letters.
+ var keyGen = Gen.Choose(1, 20).SelectMany(
+ length => Gen.ArrayOf(Gen.Elements(AllowedKeyChars), length).Select(chars => new string(chars)));
+
+ // GitVersion values can be either strings or integers, so we create a generator that can produce both types.
+ var valueGen = Gen.OneOf(
+ ArbMap.Default.GeneratorFor().Where(s => s is not null).Select(s => JsonValue.Create(s)),
+ Gen.Choose(0, int.MaxValue).Select(i => JsonValue.Create(i)));
+
+ var firstPairGen = Gen.Zip(keyGen, valueGen);
+
+ // Chain the generation to enforce uniqueness on the second key
+ return firstPairGen.SelectMany(firstPair =>
+ {
+ var key1 = firstPair.Item1;
+ var val1 = firstPair.Item2;
+
+ // CRITICAL: Key 2 must be different from Key 1 to avoid a duplicate key error
+ var uniqueSecondKeyGen = keyGen.Where(key2 => key2 != key1);
+
+ return Gen.Zip(uniqueSecondKeyGen, valueGen).Select(secondPair =>
+ {
+ var key2 = secondPair.Item1;
+ var val2 = secondPair.Item2;
+
+ return new JsonObject
+ {
+ { key1, val1 },
+ { key2, val2 }
+ };
+ });
+ });
+ }
+}
diff --git a/FuzzingTests/MSTestSettings.cs b/FuzzingTests/MSTestSettings.cs
new file mode 100644
index 0000000..399ee0b
--- /dev/null
+++ b/FuzzingTests/MSTestSettings.cs
@@ -0,0 +1,6 @@
+// SPDX-FileCopyrightText: 2026 Frans van Dorsselaer
+//
+// SPDX-License-Identifier: MIT
+
+[assembly: Parallelize(Scope = ExecutionScope.MethodLevel)]
+[assembly: DiscoverInternals]
diff --git a/FuzzingTests/Usings.cs b/FuzzingTests/Usings.cs
new file mode 100644
index 0000000..4efd69e
--- /dev/null
+++ b/FuzzingTests/Usings.cs
@@ -0,0 +1,8 @@
+// SPDX-FileCopyrightText: 2026 Frans van Dorsselaer
+//
+// SPDX-License-Identifier: MIT
+
+global using Dorssel.GitVersion.MsBuild;
+
+global using FsCheck;
+global using FsCheck.Fluent;
diff --git a/FuzzingTests/packages.lock.json b/FuzzingTests/packages.lock.json
new file mode 100644
index 0000000..6925520
--- /dev/null
+++ b/FuzzingTests/packages.lock.json
@@ -0,0 +1,146 @@
+{
+ "version": 2,
+ "dependencies": {
+ "net10.0": {
+ "FsCheck": {
+ "type": "Direct",
+ "requested": "[3.3.4, )",
+ "resolved": "3.3.4",
+ "contentHash": "Gintz/p4eTVb3EW3P8tg5wZB66sHbUBO42kHwuDZJ9BRHurOuC3+JF5YPq4rfeURMxuGs5YgzW/DDneIUsTNqA==",
+ "dependencies": {
+ "FSharp.Core": "5.0.2"
+ }
+ },
+ "GitVersion.MsBuild": {
+ "type": "Direct",
+ "requested": "[6.8.2, )",
+ "resolved": "6.8.2",
+ "contentHash": "eTd8jUiogUXpVssFyDWWdMrPr5Cdc57tWaQ3Cv/eMIhnumOHZhDkc/wcy8hYU8aH7NS4VT2w/pHjVITDSTXMGw=="
+ },
+ "JunitXml.TestLogger": {
+ "type": "Direct",
+ "requested": "[8.0.0, )",
+ "resolved": "8.0.0",
+ "contentHash": "rO2ugKcbVy8SHNKeqfX/DshUcTtolnEUwMKehVB6gh5/YoL+U07zoACCyGRvgMtzq7fr9o6GLkXOmVj9WMgx+w=="
+ },
+ "Microsoft.NET.ILLink.Tasks": {
+ "type": "Direct",
+ "requested": "[10.0.13, )",
+ "resolved": "10.0.13",
+ "contentHash": "40yrBMsLEcjixBR4G+FiUaBPvxXIPSKJ2OgSrbOZcmSDBda4Sb0k7BWrfDpIvn3IvvDzwOL/tEPa6gDQfh6aRw=="
+ },
+ "Microsoft.Testing.Extensions.CodeCoverage": {
+ "type": "Direct",
+ "requested": "[18.12.0, )",
+ "resolved": "18.12.0",
+ "contentHash": "BST/sWYpKk9OXO7Yxktcg6FWORRsHU0EdCbMwZ7d3OqJsMKlrNHIZ30g0Ib4k5T5acJYXRBmg9mNrTU/N6cy1g==",
+ "dependencies": {
+ "Microsoft.DiaSymReader": "2.2.10",
+ "Microsoft.Extensions.DependencyModel": "10.0.10",
+ "Microsoft.Testing.Platform": "2.5.0",
+ "Mono.Cecil": "0.11.6"
+ }
+ },
+ "Microsoft.Testing.Extensions.TrxReport": {
+ "type": "Direct",
+ "requested": "[2.5.1, )",
+ "resolved": "2.5.1",
+ "contentHash": "NHqpG2UAVsXoNnNZtyIjxgsfPe9N/E6eTHjHCs7uaSbdOCOsbX5I7N0d1BHyjM7vv3Q5R2nlvngKSHWT+ZCRXQ==",
+ "dependencies": {
+ "Microsoft.Testing.Extensions.TrxReport.Abstractions": "2.5.1",
+ "Microsoft.Testing.Platform": "[2.5.1, 3.0.0)"
+ }
+ },
+ "MSTest.TestAdapter": {
+ "type": "Direct",
+ "requested": "[4.5.1, )",
+ "resolved": "4.5.1",
+ "contentHash": "0q9TkkpOF3YjXGMfjcQG5bftpu/NPhVqKFL4DLUDJK6CGO4si5z+PVxVL1fM6TXEFSHnCM09E1691nhn8GUTYg==",
+ "dependencies": {
+ "MSTest.TestFramework": "4.5.1",
+ "Microsoft.TestPlatform.ObjectModel": "18.10.1",
+ "Microsoft.Testing.Extensions.Telemetry": "2.5.1",
+ "Microsoft.Testing.Extensions.TrxReport.Abstractions": "2.5.1",
+ "Microsoft.Testing.Platform.MSBuild": "2.5.1"
+ }
+ },
+ "MSTest.TestFramework": {
+ "type": "Direct",
+ "requested": "[4.5.1, )",
+ "resolved": "4.5.1",
+ "contentHash": "mdYgQYAtDGq/IixJq7oD6VvIYAz+mRniGMxyhlZ2wQOpIeFe6oFQ/RniT3i7c3ZfCTKykhI6AB0wqRYVEWZDgQ==",
+ "dependencies": {
+ "MSTest.Analyzers": "4.5.1"
+ }
+ },
+ "FSharp.Core": {
+ "type": "Transitive",
+ "resolved": "5.0.2",
+ "contentHash": "DpcajqENgb3VXaHw1FKfVS+TWwEzck1PCajqLwBAVrtd1lfxd7T8JISVZBdV1mX9+2g48+tIgpNaVJWObdMrBw=="
+ },
+ "Microsoft.ApplicationInsights": {
+ "type": "Transitive",
+ "resolved": "2.23.0",
+ "contentHash": "nWArUZTdU7iqZLycLKWe0TDms48KKGE6pONH2terYNa8REXiqixrMOkf1sk5DHGMaUTqONU2YkS4SAXBhLStgw=="
+ },
+ "Microsoft.DiaSymReader": {
+ "type": "Transitive",
+ "resolved": "2.2.10",
+ "contentHash": "zmGsm6b2y3STDa/Of7rdkkfTDV8VuGB8aCqIkLoJIQh5tL78K3zJ8OUyFKnfsaORXmGr9iOKwDkZfUjeXi+CwA=="
+ },
+ "Microsoft.Extensions.DependencyModel": {
+ "type": "Transitive",
+ "resolved": "10.0.10",
+ "contentHash": "rfZA1RjR021RPqSmIPovfz2aOd79TGqJ9BengbjnzIISOVwjLmuSDnhCMmiY/1c6iYvGolQ1iNGzkav0u11XEA=="
+ },
+ "Microsoft.Testing.Extensions.Telemetry": {
+ "type": "Transitive",
+ "resolved": "2.5.1",
+ "contentHash": "mu2iyoHovwl0q1mGeeRU0h8+LALXC7HqbxniLa1shTKYdJGl+KPoCEgjWIQ8MLzk1tlTGPOfg1JsxdH3RxhMCA==",
+ "dependencies": {
+ "Microsoft.ApplicationInsights": "2.23.0",
+ "Microsoft.Testing.Platform": "[2.5.1, 3.0.0)"
+ }
+ },
+ "Microsoft.Testing.Extensions.TrxReport.Abstractions": {
+ "type": "Transitive",
+ "resolved": "2.5.1",
+ "contentHash": "DYTZAguaHz0wNJdHDTTGYOXtnlDoLoIbTMDKSApNNcytm55DJcnwpwELfQGLk5DB5QlgaEjNhnGo1Pu5QvL5VA==",
+ "dependencies": {
+ "Microsoft.Testing.Platform": "[2.5.1, 3.0.0)"
+ }
+ },
+ "Microsoft.Testing.Platform": {
+ "type": "Transitive",
+ "resolved": "2.5.1",
+ "contentHash": "FAMaFFWnULW+hDHMb40Qzhcgz9cAU2/EtgRCBNX/DdQoKo0sPG2EiY+w7bQdl/BgHnf/DbpXLbf5Xy0o15M8NA=="
+ },
+ "Microsoft.Testing.Platform.MSBuild": {
+ "type": "Transitive",
+ "resolved": "2.5.1",
+ "contentHash": "lYawcAwY+h76rQmpma2n0HaL8zkOFbbKapso0Eb+uOAUQn7333pIByLftEuiO67csAoZufHNMwi6PqhNemWQXA==",
+ "dependencies": {
+ "Microsoft.Testing.Platform": "[2.5.1, 3.0.0)"
+ }
+ },
+ "Microsoft.TestPlatform.ObjectModel": {
+ "type": "Transitive",
+ "resolved": "18.10.1",
+ "contentHash": "o+PuLDRVp1iwiVAPBADZItXgm9Ck23XtPhE4kZtZVi0uYnMiiz1Dx9Cjh75kJ+ijfqnORL3VOTOaQVKi0f/ixg=="
+ },
+ "Mono.Cecil": {
+ "type": "Transitive",
+ "resolved": "0.11.6",
+ "contentHash": "f33RkDtZO8VlGXCtmQIviOtxgnUdym9xx/b1p9h91CRGOsJFxCFOFK1FDbVt1OCf1aWwYejUFa2MOQyFWTFjbA=="
+ },
+ "MSTest.Analyzers": {
+ "type": "Transitive",
+ "resolved": "4.5.1",
+ "contentHash": "oa0w2QC/T3U+ulUn38AmbWE/wnEMtqSFRb2z277z5SpswqNc/ySWcAVlG1qEWUxzm+NztCjkoeLAn/83gIx3ug=="
+ },
+ "Dorssel.GitVersion.MsBuild": {
+ "type": "Project"
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/FuzzingTests/packages.lock.json.license b/FuzzingTests/packages.lock.json.license
new file mode 100644
index 0000000..8e46c87
--- /dev/null
+++ b/FuzzingTests/packages.lock.json.license
@@ -0,0 +1,3 @@
+SPDX-FileCopyrightText: 2026 Frans van Dorsselaer
+
+SPDX-License-Identifier: MIT
diff --git a/GitVersion.MsBuild/AssemblySettings.cs b/GitVersion.MsBuild/AssemblySettings.cs
new file mode 100644
index 0000000..a38b04d
--- /dev/null
+++ b/GitVersion.MsBuild/AssemblySettings.cs
@@ -0,0 +1,19 @@
+// SPDX-FileCopyrightText: 2026 Frans van Dorsselaer
+//
+// SPDX-License-Identifier: MIT
+
+using System.Runtime.CompilerServices;
+
+[assembly: InternalsVisibleTo("FuzzingTests, PublicKey = "
+ + "002400000480000094010000060200000024000052534131000c000001000100f1c9097124f926" // DevSkim: ignore DS173237
+ + "60242335d835f2f0bc70ff592f63bd0fb41d3a753441884f35c53253a0eb123a01d960dfba3903" // DevSkim: ignore DS173237
+ + "7470b7272d51ade1af6fca95d260075ec71cae6559cd9e20558a6f281f53ec76108cbc4a55c5dd" // DevSkim: ignore DS173237
+ + "05ceac08699305fdbdb3a7bce263b9b920a1ea077d3ad05736a47ade8a2489c33b62a37115dc64" // DevSkim: ignore DS173237
+ + "6758f0f5d804e01d9729514e8ac719b9b861bf6ae820c34cd60b3bb71f4042f637829ac42eb105" // DevSkim: ignore DS173237
+ + "f3d55cbb10dae14ff7c84198b8a74328eca4c5815cac4734d3c2581496cbbe253cff89bdb33f8f" // DevSkim: ignore DS173237
+ + "44e447f33ef3e227eb6a4e2c9e197c2dd90fc24c054be4f90e38dc95a47878854556debd0b8540" // DevSkim: ignore DS173237
+ + "221cd2f782e7281191af6a5463ce34c1caf03a40d1b2c26ddf717db931b74633b9102b855699db" // DevSkim: ignore DS173237
+ + "299b2c8e4c1710eee22a79d360fc953e872d5722dd3851a5cccd5697c102e455491890c0ff1e40" // DevSkim: ignore DS173237
+ + "0d050a4d6efafb8e62e59df7239fda4b06856e3abb17d8b32530c43f2afffc626a94046c6d58ea" // DevSkim: ignore DS173237
+ + "6af4915f06baca85b66d157ea2655dde145054dac3a5543dd7e5" // DevSkim: ignore DS173237
+ )]
diff --git a/GitVersion.MsBuild/GetVersion.cs b/GitVersion.MsBuild/GetVersion.cs
index 4b6a4a7..d56d36b 100644
--- a/GitVersion.MsBuild/GetVersion.cs
+++ b/GitVersion.MsBuild/GetVersion.cs
@@ -96,7 +96,7 @@ public class GetVersion : GitVersionTaskBase
public override bool Execute()
{
- foreach (var keyValue in GitVersionVariables.FromFile(VersionFile!))
+ foreach (var keyValue in GitVersionVariables.FromJson(File.ReadAllText(VersionFile)))
{
GetType().GetProperty(keyValue.Key).SetValue(this, keyValue.Value);
}
diff --git a/GitVersion.MsBuild/GitVersionVariables.cs b/GitVersion.MsBuild/GitVersionVariables.cs
index 1315163..d8832d4 100644
--- a/GitVersion.MsBuild/GitVersionVariables.cs
+++ b/GitVersion.MsBuild/GitVersionVariables.cs
@@ -3,26 +3,23 @@
// SPDX-License-Identifier: MIT
using System.Collections.ObjectModel;
+using System.Runtime.Serialization.Json;
+using System.Text;
namespace Dorssel.GitVersion.MsBuild;
static class GitVersionVariables
{
- public static ReadOnlyDictionary FromFile(string versionFile)
+ public static ReadOnlyDictionary FromJson(string json)
{
- var dictionary = new Dictionary();
-
- foreach (var line in File.ReadAllLines(versionFile))
- {
- var keyValue = line.Split(':');
- if (keyValue.Length != 2)
+ var serializer = new DataContractJsonSerializer(typeof(Dictionary),
+ new DataContractJsonSerializerSettings()
{
- // opening or closing brace
- continue;
- }
- dictionary.Add(keyValue[0].Trim(' ', '"'), keyValue[1].Trim(' ', '"', ','));
- }
+ UseSimpleDictionaryFormat = true
+ });
- return new(dictionary);
+ using var stream = new MemoryStream(Encoding.UTF8.GetBytes(json));
+ var result = (Dictionary)serializer.ReadObject(stream);
+ return new(result);
}
}
diff --git a/gitversion-msbuild.slnx b/gitversion-msbuild.slnx
index 9a69cf3..af48f09 100644
--- a/gitversion-msbuild.slnx
+++ b/gitversion-msbuild.slnx
@@ -22,6 +22,7 @@ SPDX-License-Identifier: MIT
+