Skip to content

Commit bc93efe

Browse files
committed
fix: adds explicit error message for invalid json pointers
1 parent 07b525f commit bc93efe

7 files changed

Lines changed: 119 additions & 1 deletion

File tree

src/Microsoft.OpenApi/Reader/JsonNodeHelper.cs

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -172,6 +172,17 @@ public static Dictionary<string, HashSet<T>> CreateArrayMap<T>(this JsonNode? no
172172
return jsonObject.TryGetPropertyValue("$ref", out var refNode) ? refNode?.GetScalarValue() : null;
173173
}
174174

175+
public static void ValidateReferencePointerFormat(string pointer)
176+
{
177+
var hashIndex = pointer.IndexOf('#');
178+
if (hashIndex >= 0 &&
179+
pointer.Length > hashIndex + 1 &&
180+
pointer[hashIndex + 1] != '/')
181+
{
182+
throw new OpenApiException(string.Format(SRResource.ReferenceHasInvalidFormat, pointer));
183+
}
184+
}
185+
175186
/// <summary>
176187
/// Returns the value of $dynamicRef if $ref is absent. Used to create a schema reference
177188
/// for bare $dynamicRef schemas (no $ref) so they participate in reference resolution.

src/Microsoft.OpenApi/Reader/V3/OpenApiV3Deserializer.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -152,6 +152,8 @@ private static IOpenApiExtension LoadExtension(string name, JsonNode node, Parsi
152152

153153
private static (string, string?) GetReferenceIdAndExternalResource(string pointer)
154154
{
155+
JsonNodeHelper.ValidateReferencePointerFormat(pointer);
156+
155157
var refSegments = pointer.Split('/');
156158
var refId = refSegments[refSegments.Count() -1];
157159
var isExternalResource = !refSegments[0].StartsWith("#", StringComparison.OrdinalIgnoreCase);

src/Microsoft.OpenApi/Reader/V31/OpenApiV31Deserializer.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -152,6 +152,8 @@ private static IOpenApiExtension LoadExtension(string name, JsonNode node, Parsi
152152

153153
private static (string, string?) GetReferenceIdAndExternalResource(string pointer)
154154
{
155+
JsonNodeHelper.ValidateReferencePointerFormat(pointer);
156+
155157
/* Check whether the reference pointer is a URL
156158
* (id keyword allows you to supply a URL for the schema as a target for referencing)
157159
* E.g. $ref: 'https://example.com/schemas/resource.json'

src/Microsoft.OpenApi/Reader/V32/OpenApiV32Deserializer.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -152,6 +152,8 @@ private static IOpenApiExtension LoadExtension(string name, JsonNode node, Parsi
152152

153153
private static (string, string?) GetReferenceIdAndExternalResource(string pointer)
154154
{
155+
JsonNodeHelper.ValidateReferencePointerFormat(pointer);
156+
155157
/* Check whether the reference pointer is a URL
156158
* (id keyword allows you to supply a URL for the schema as a target for referencing)
157159
* E.g. $ref: 'https://example.com/schemas/resource.json'

test/Microsoft.OpenApi.Readers.Tests/V31Tests/OpenApiDocumentTests.cs

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,40 @@ public class OpenApiDocumentTests
1616
{
1717
private const string SampleFolderPath = "V31Tests/Samples/OpenApiDocument/";
1818

19+
[Fact]
20+
public void ParseDocumentWithMalformedReferenceShouldYieldExpectedDiagnostic()
21+
{
22+
var result = OpenApiDocument.Parse(
23+
"""
24+
openapi: 3.1.1
25+
info:
26+
title: test
27+
version: 1.0.0
28+
paths:
29+
/test:
30+
get:
31+
responses:
32+
'200':
33+
description: successful operation
34+
content:
35+
application/json:
36+
schema:
37+
$ref: '#components/schemas/Item'
38+
components:
39+
schemas:
40+
Item:
41+
type: object
42+
properties:
43+
id:
44+
type: integer
45+
""",
46+
OpenApiConstants.Yaml,
47+
SettingsFixture.ReaderSettings);
48+
49+
var error = Assert.Single(result.Diagnostic.Errors);
50+
Assert.Equal("The reference string '#components/schemas/Item' has invalid format.", error.Message);
51+
}
52+
1953
[Fact]
2054
public async Task ParseDocumentWithWebhooksShouldSucceed()
2155
{

test/Microsoft.OpenApi.Readers.Tests/V32Tests/OpenApiDocumentTests.cs

Lines changed: 34 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,40 @@ public class OpenApiDocumentTests
1616
{
1717
private const string SampleFolderPath = "V32Tests/Samples/OpenApiDocument/";
1818

19+
[Fact]
20+
public void ParseDocumentWithMalformedReferenceShouldYieldExpectedDiagnostic()
21+
{
22+
var result = OpenApiDocument.Parse(
23+
"""
24+
openapi: 3.2.0
25+
info:
26+
title: test
27+
version: 1.0.0
28+
paths:
29+
/test:
30+
get:
31+
responses:
32+
'200':
33+
description: successful operation
34+
content:
35+
application/json:
36+
schema:
37+
$ref: '#components/schemas/Item'
38+
components:
39+
schemas:
40+
Item:
41+
type: object
42+
properties:
43+
id:
44+
type: integer
45+
""",
46+
OpenApiConstants.Yaml,
47+
SettingsFixture.ReaderSettings);
48+
49+
var error = Assert.Single(result.Diagnostic.Errors);
50+
Assert.Equal("The reference string '#components/schemas/Item' has invalid format.", error.Message);
51+
}
52+
1953
[Fact]
2054
public async Task ParseDocumentWithWebhooksShouldSucceed()
2155
{
@@ -665,4 +699,3 @@ public void LoadDocumentWithBooleanSchemaShouldNotThrowNullReferenceException()
665699
}
666700
}
667701
}
668-

test/Microsoft.OpenApi.Readers.Tests/V3Tests/OpenApiDocumentTests.cs

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -153,6 +153,40 @@ public async Task ParseBrokenMinimalDocumentShouldYieldExpectedDiagnostic()
153153
}, result.Diagnostic);
154154
}
155155

156+
[Fact]
157+
public void ParseDocumentWithMalformedReferenceShouldYieldExpectedDiagnostic()
158+
{
159+
var result = OpenApiDocument.Parse(
160+
"""
161+
openapi: 3.0.3
162+
info:
163+
title: test
164+
version: 1.0.0
165+
paths:
166+
/test:
167+
get:
168+
responses:
169+
'200':
170+
description: successful operation
171+
content:
172+
application/json:
173+
schema:
174+
$ref: '#components/schemas/Item'
175+
components:
176+
schemas:
177+
Item:
178+
type: object
179+
properties:
180+
id:
181+
type: integer
182+
""",
183+
OpenApiConstants.Yaml,
184+
SettingsFixture.ReaderSettings);
185+
186+
var error = Assert.Single(result.Diagnostic.Errors);
187+
Assert.Equal("The reference string '#components/schemas/Item' has invalid format.", error.Message);
188+
}
189+
156190
[Fact]
157191
public async Task ParseMinimalDocumentShouldSucceed()
158192
{

0 commit comments

Comments
 (0)