Skip to content

Commit c6ef130

Browse files
authored
fix(serialization): references inlining (#3124)
fix(serialization): potential infinite loop when inlining references * fix(serialization): additional recursive paths
1 parent 00b5675 commit c6ef130

13 files changed

Lines changed: 414 additions & 10 deletions

‎src/Microsoft.OpenApi/Extensions/OpenAPIWriterExtensions.cs‎

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,5 +15,13 @@ internal static OpenApiWriterSettings GetSettings(this IOpenApiWriter openApiWri
1515
}
1616
return new();
1717
}
18+
19+
internal static void CountInlineReference(this IOpenApiWriter openApiWriter)
20+
{
21+
if (openApiWriter is OpenApiWriterBase @base)
22+
{
23+
@base.CountInlineReference();
24+
}
25+
}
1826
}
1927
}

‎src/Microsoft.OpenApi/Models/OpenApiHeader.cs‎

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -98,7 +98,7 @@ public virtual void SerializeAsV3(IOpenApiWriter writer)
9898
SerializeInternal(writer, OpenApiSpecVersion.OpenApi3_0, (writer, element) => element.SerializeAsV3(writer));
9999
}
100100

101-
internal void SerializeInternal(IOpenApiWriter writer, OpenApiSpecVersion version,
101+
internal void SerializeInternal(IOpenApiWriter writer, OpenApiSpecVersion version,
102102
Action<IOpenApiWriter, IOpenApiSerializable> callback)
103103
{
104104
Utils.CheckArgumentNull(writer);
@@ -175,7 +175,12 @@ public virtual void SerializeAsV2(IOpenApiWriter writer)
175175
writer.WriteProperty(OpenApiConstants.AllowReserved, AllowReserved, false);
176176

177177
// schema
178-
var targetSchema = Schema switch {
178+
if (Schema is OpenApiSchemaReference)
179+
{
180+
writer.CountInlineReference();
181+
}
182+
var targetSchema = Schema switch
183+
{
179184
OpenApiSchemaReference schemaReference => schemaReference.RecursiveTarget,
180185
OpenApiSchema schema => schema,
181186
_ => null,

‎src/Microsoft.OpenApi/Models/OpenApiParameter.cs‎

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -113,11 +113,11 @@ public virtual void SerializeAsV3(IOpenApiWriter writer)
113113
SerializeInternal(writer, OpenApiSpecVersion.OpenApi3_0, (writer, element) => element.SerializeAsV3(writer));
114114
}
115115

116-
internal void SerializeInternal(IOpenApiWriter writer, OpenApiSpecVersion version,
116+
internal void SerializeInternal(IOpenApiWriter writer, OpenApiSpecVersion version,
117117
Action<IOpenApiWriter, IOpenApiSerializable> callback)
118118
{
119119
Utils.CheckArgumentNull(writer);
120-
120+
121121
// Validate that Cookie style is only used in OpenAPI 3.2 and later
122122
if (Style == ParameterStyle.Cookie && version < OpenApiSpecVersion.OpenApi3_2)
123123
{
@@ -228,7 +228,12 @@ internal virtual void WriteRequestBodySchemaForV2(IOpenApiWriter writer, Diction
228228
// uniqueItems
229229
// enum
230230
// multipleOf
231-
var targetSchema = Schema switch {
231+
if (Schema is OpenApiSchemaReference)
232+
{
233+
writer.CountInlineReference();
234+
}
235+
var targetSchema = Schema switch
236+
{
232237
OpenApiSchemaReference schemaReference => schemaReference.RecursiveTarget,
233238
OpenApiSchema schema => schema,
234239
_ => null,
@@ -273,7 +278,7 @@ internal virtual void WriteRequestBodySchemaForV2(IOpenApiWriter writer, Diction
273278
public virtual void SerializeAsV2(IOpenApiWriter writer)
274279
{
275280
Utils.CheckArgumentNull(writer);
276-
281+
277282
// Validate that Cookie style is only used in OpenAPI 3.2 and later
278283
if (Style == ParameterStyle.Cookie)
279284
{

‎src/Microsoft.OpenApi/Models/References/BaseOpenApiReferenceHolder.cs‎

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -207,7 +207,7 @@ protected BaseOpenApiReferenceHolder(string referenceId, OpenApiDocument? hostDo
207207
/// <inheritdoc/>
208208
public virtual void SerializeAsV3(IOpenApiWriter writer)
209209
{
210-
if (!writer.GetSettings().ShouldInlineReference(Reference)
210+
if (!writer.GetSettings().ShouldInlineReference(Reference)
211211
|| Reference.Type == ReferenceType.Tag) // tags are held as references need to drop in.
212212
{
213213
Reference.SerializeAsV3(writer);
@@ -267,6 +267,7 @@ private protected void SerializeInternal(IOpenApiWriter writer,
267267
Action<IOpenApiWriter, U> action)
268268
{
269269
Utils.CheckArgumentNull(writer);
270+
writer.CountInlineReference();
270271
ApplyToTarget(element => action(writer, element));
271272
}
272273
}

‎src/Microsoft.OpenApi/Models/References/OpenApiMediaTypeReference.cs‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,11 +77,13 @@ protected override BaseOpenApiReference CopyReference(BaseOpenApiReference sourc
7777
/// <inheritdoc/>
7878
public override void SerializeAsV31(IOpenApiWriter writer)
7979
{
80+
writer.CountInlineReference();
8081
CopyReferenceAsTargetElementWithOverrides(new OpenApiMediaType()).SerializeAsV31(writer);
8182
}
8283
/// <inheritdoc/>
8384
public override void SerializeAsV3(IOpenApiWriter writer)
8485
{
86+
writer.CountInlineReference();
8587
CopyReferenceAsTargetElementWithOverrides(new OpenApiMediaType()).SerializeAsV3(writer);
8688
}
8789
}

‎src/Microsoft.OpenApi/Models/References/OpenApiRequestBodyReference.cs‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,7 @@ public override void SerializeAsV2(IOpenApiWriter writer)
6464
{
6565
if (writer.GetSettings().ShouldInlineReference(Reference))
6666
{
67+
writer.CountInlineReference();
6768
return GetFromTarget(target => target.ConvertToBodyParameter(writer));
6869
}
6970

@@ -74,6 +75,7 @@ public override void SerializeAsV2(IOpenApiWriter writer)
7475
{
7576
if (writer.GetSettings().ShouldInlineReference(Reference))
7677
{
78+
writer.CountInlineReference();
7779
return GetFromTarget(target => target.ConvertToFormDataParameters(writer));
7880
}
7981

‎src/Microsoft.OpenApi/Models/References/OpenApiSchemaReference.cs‎

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -256,8 +256,14 @@ private void SerializeAsWithoutLoops(IOpenApiWriter writer, Action<IOpenApiWrite
256256
}
257257
else
258258
{
259-
SerializeInternal(writer, (w, element) => action(w, element));
260-
writer.GetSettings().LoopDetector.PopLoop<IOpenApiSchema>();
259+
try
260+
{
261+
SerializeInternal(writer, (w, element) => action(w, element));
262+
}
263+
finally
264+
{
265+
writer.GetSettings().LoopDetector.PopLoop<IOpenApiSchema>();
266+
}
261267
}
262268

263269
}

‎src/Microsoft.OpenApi/Properties/SRResource.Designer.cs‎

Lines changed: 9 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

‎src/Microsoft.OpenApi/Properties/SRResource.resx‎

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -132,6 +132,9 @@
132132
<data name="IndentationLevelInvalid" xml:space="preserve">
133133
<value>Indentation level cannot be lower than 0.</value>
134134
</data>
135+
<data name="MaximumInlineReferenceCountExceeded" xml:space="preserve">
136+
<value>The maximum number of inline references ({0}) has been exceeded.</value>
137+
</data>
135138
<data name="InputItemShouldBeType" xml:space="preserve">
136139
<value>The input item should be in type of '{0}'.</value>
137140
</data>
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,4 @@
11
#nullable enable
22
Microsoft.OpenApi.OpenApiDocument.GetOperationById(string! operationId) -> Microsoft.OpenApi.OpenApiOperation?
3+
Microsoft.OpenApi.OpenApiWriterSettings.MaxInlineReferenceCount.get -> int
4+
Microsoft.OpenApi.OpenApiWriterSettings.MaxInlineReferenceCount.set -> void

0 commit comments

Comments
 (0)