From 4b3164a76b316951cb043aebf950663a17e51c90 Mon Sep 17 00:00:00 2001 From: Vincent Biret Date: Fri, 20 Mar 2026 09:25:05 -0400 Subject: [PATCH 1/3] fix: a bug where path parameter validation would fail if they contained forbidden JSON pointer characters Signed-off-by: Vincent Biret --- .../Services/OpenApiVisitorBase.cs | 13 ++++++- .../Rules/OpenApiParameterRules.cs | 2 +- .../OpenApiParameterValidationTests.cs | 37 ++++++++++++++++++- 3 files changed, 48 insertions(+), 4 deletions(-) diff --git a/src/Microsoft.OpenApi/Services/OpenApiVisitorBase.cs b/src/Microsoft.OpenApi/Services/OpenApiVisitorBase.cs index 45a85d3ab..8e0699ebc 100644 --- a/src/Microsoft.OpenApi/Services/OpenApiVisitorBase.cs +++ b/src/Microsoft.OpenApi/Services/OpenApiVisitorBase.cs @@ -33,10 +33,19 @@ public virtual void Enter(string segment) this._path.Push(string.Empty); return; } + this._path.Push(EncodeJsonPointerSegment(segment)); + } + + internal static string EncodeJsonPointerSegment(string? segment) + { + if (string.IsNullOrEmpty(segment)) + { + return string.Empty; + } #if NETSTANDARD2_1_OR_GREATER || NETCOREAPP1_0_OR_GREATER - this._path.Push(segment.Replace("~", "~0", StringComparison.Ordinal).Replace("/", "~1", StringComparison.OrdinalIgnoreCase)); + return segment.Replace("~", "~0", StringComparison.Ordinal).Replace("/", "~1", StringComparison.OrdinalIgnoreCase); #else - this._path.Push(segment.Replace("~", "~0").Replace("/", "~1")); + return segment!.Replace("~", "~0").Replace("/", "~1"); #endif } diff --git a/src/Microsoft.OpenApi/Validations/Rules/OpenApiParameterRules.cs b/src/Microsoft.OpenApi/Validations/Rules/OpenApiParameterRules.cs index a84ea3255..c00e1dcce 100644 --- a/src/Microsoft.OpenApi/Validations/Rules/OpenApiParameterRules.cs +++ b/src/Microsoft.OpenApi/Validations/Rules/OpenApiParameterRules.cs @@ -61,7 +61,7 @@ public static class OpenApiParameterRules (context, parameter) => { if (parameter.In == ParameterLocation.Path && - !(context.PathString.Contains("{" + parameter.Name + "}") || context.PathString.Contains("#/components"))) + !(context.PathString.Contains("{" + OpenApiVisitorBase.EncodeJsonPointerSegment(parameter.Name) + "}") || context.PathString.Contains("#/components"))) { context.Enter("in"); context.CreateError( diff --git a/test/Microsoft.OpenApi.Tests/Validations/OpenApiParameterValidationTests.cs b/test/Microsoft.OpenApi.Tests/Validations/OpenApiParameterValidationTests.cs index 207db74b6..e12216c05 100644 --- a/test/Microsoft.OpenApi.Tests/Validations/OpenApiParameterValidationTests.cs +++ b/test/Microsoft.OpenApi.Tests/Validations/OpenApiParameterValidationTests.cs @@ -203,7 +203,42 @@ public void PathParameterInThePathShouldBeOk() validator.Enter("1"); var walker = new OpenApiWalker(validator); - walker.Walk(parameter); + walker.Walk((IOpenApiParameter)parameter); + + errors = validator.Errors; + var result = errors.Any(); + + // Assert + Assert.False(result); + } + + [Fact] + public void PathParameterInThePathShouldBeOkWithSlashInParameterName() + { + // Arrange + IEnumerable errors; + + var parameter = new OpenApiParameter + { + Name = "parameter/1", + In = ParameterLocation.Path, + Required = true, + Schema = new OpenApiSchema() + { + Type = JsonSchemaType.String, + } + }; + + // Act + var validator = new OpenApiValidator(ValidationRuleSet.GetDefaultRuleSet()); + validator.Enter("paths"); + validator.Enter("/{parameter/1}"); + validator.Enter("get"); + validator.Enter("parameters"); + validator.Enter("1"); + + var walker = new OpenApiWalker(validator); + walker.Walk((IOpenApiParameter)parameter); errors = validator.Errors; var result = errors.Any(); From 83719d784a1eaf1cc0c559555b011bcbca35a438 Mon Sep 17 00:00:00 2001 From: Vincent Biret Date: Fri, 20 Mar 2026 09:43:25 -0400 Subject: [PATCH 2/3] tests: increases coverage by testing for null parameter names Signed-off-by: Vincent Biret --- .../OpenApiParameterValidationTests.cs | 32 ++++++++++++++++++- 1 file changed, 31 insertions(+), 1 deletion(-) diff --git a/test/Microsoft.OpenApi.Tests/Validations/OpenApiParameterValidationTests.cs b/test/Microsoft.OpenApi.Tests/Validations/OpenApiParameterValidationTests.cs index e12216c05..1d2882033 100644 --- a/test/Microsoft.OpenApi.Tests/Validations/OpenApiParameterValidationTests.cs +++ b/test/Microsoft.OpenApi.Tests/Validations/OpenApiParameterValidationTests.cs @@ -75,7 +75,7 @@ public void ValidateExampleShouldNotHaveDataTypeMismatchForSimpleSchema() var validator = new OpenApiValidator(ValidationRuleSet.GetDefaultRuleSet()); validator.Enter("{parameter1}"); var walker = new OpenApiWalker(validator); - walker.Walk(parameter); + walker.Walk((IOpenApiParameter)parameter); warnings = validator.Warnings; var result = !warnings.Any(); @@ -246,5 +246,35 @@ public void PathParameterInThePathShouldBeOkWithSlashInParameterName() // Assert Assert.False(result); } + + [Fact] + public void PathParameterValidationShouldNotThrowWithEmptyParameterName() + { + // Arrange + var parameter = new OpenApiParameter + { + Name = string.Empty, + In = ParameterLocation.Path, + Required = true, + Schema = new OpenApiSchema() + { + Type = JsonSchemaType.String, + } + }; + + // Act + var validator = new OpenApiValidator(ValidationRuleSet.GetDefaultRuleSet()); + validator.Enter("paths"); + validator.Enter("/{}"); + validator.Enter("get"); + validator.Enter("parameters"); + validator.Enter("1"); + + var walker = new OpenApiWalker(validator); + var exception = Record.Exception(() => walker.Walk((IOpenApiParameter)parameter)); + + // Assert + Assert.Null(exception); + } } } From 7d1de9ed2b7c97d518258452afd378be11aa070c Mon Sep 17 00:00:00 2001 From: Vincent Biret Date: Fri, 20 Mar 2026 09:44:29 -0400 Subject: [PATCH 3/3] tests: adds missing cast to ensure the right walker method is being called Signed-off-by: Vincent Biret --- .../Validations/OpenApiHeaderValidationTests.cs | 2 +- .../Validations/OpenApiSchemaValidationTests.cs | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/test/Microsoft.OpenApi.Tests/Validations/OpenApiHeaderValidationTests.cs b/test/Microsoft.OpenApi.Tests/Validations/OpenApiHeaderValidationTests.cs index a7a0cba0a..98e59928c 100644 --- a/test/Microsoft.OpenApi.Tests/Validations/OpenApiHeaderValidationTests.cs +++ b/test/Microsoft.OpenApi.Tests/Validations/OpenApiHeaderValidationTests.cs @@ -86,7 +86,7 @@ public void ValidateExamplesShouldNotHaveDataTypeMismatchForSimpleSchema() // Act var validator = new OpenApiValidator(ValidationRuleSet.GetDefaultRuleSet()); var walker = new OpenApiWalker(validator); - walker.Walk(header); + walker.Walk((IOpenApiHeader)header); warnings = validator.Warnings; var result = !warnings.Any(); diff --git a/test/Microsoft.OpenApi.Tests/Validations/OpenApiSchemaValidationTests.cs b/test/Microsoft.OpenApi.Tests/Validations/OpenApiSchemaValidationTests.cs index fdf36e0b4..0cb229534 100644 --- a/test/Microsoft.OpenApi.Tests/Validations/OpenApiSchemaValidationTests.cs +++ b/test/Microsoft.OpenApi.Tests/Validations/OpenApiSchemaValidationTests.cs @@ -26,7 +26,7 @@ public void ValidateDefaultShouldNotHaveDataTypeMismatchForSimpleSchema() // Act var validator = new OpenApiValidator(ValidationRuleSet.GetDefaultRuleSet()); var walker = new OpenApiWalker(validator); - walker.Walk(schema); + walker.Walk((IOpenApiSchema)schema); warnings = validator.Warnings; var result = !warnings.Any(); @@ -50,7 +50,7 @@ public void ValidateExampleAndDefaultShouldNotHaveDataTypeMismatchForSimpleSchem // Act var validator = new OpenApiValidator(ValidationRuleSet.GetDefaultRuleSet()); var walker = new OpenApiWalker(validator); - walker.Walk(schema); + walker.Walk((IOpenApiSchema)schema); warnings = validator.Warnings; bool result = !warnings.Any(); @@ -92,7 +92,7 @@ public void ValidateEnumShouldNotHaveDataTypeMismatchForSimpleSchema() // Act var validator = new OpenApiValidator(ValidationRuleSet.GetDefaultRuleSet()); var walker = new OpenApiWalker(validator); - walker.Walk(schema); + walker.Walk((IOpenApiSchema)schema); warnings = validator.Warnings; var result = !warnings.Any();