Skip to content

Commit b7ecfe9

Browse files
authored
fix/circular schema serialization to v2 (#3125)
* fix(serialization): potential infinite loop when inlining references * fix(serialization): additional recursive paths
1 parent 5369417 commit b7ecfe9

12 files changed

Lines changed: 378 additions & 8 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
@@ -90,7 +90,7 @@ public virtual void SerializeAsV3(IOpenApiWriter writer)
9090
SerializeInternal(writer, OpenApiSpecVersion.OpenApi3_0, (writer, element) => element.SerializeAsV3(writer));
9191
}
9292

93-
internal void SerializeInternal(IOpenApiWriter writer, OpenApiSpecVersion version,
93+
internal void SerializeInternal(IOpenApiWriter writer, OpenApiSpecVersion version,
9494
Action<IOpenApiWriter, IOpenApiSerializable> callback)
9595
{
9696
Utils.CheckArgumentNull(writer);
@@ -167,7 +167,12 @@ public virtual void SerializeAsV2(IOpenApiWriter writer)
167167
writer.WriteProperty(OpenApiConstants.AllowReserved, AllowReserved, false);
168168

169169
// schema
170-
var targetSchema = Schema switch {
170+
if (Schema is OpenApiSchemaReference)
171+
{
172+
writer.CountInlineReference();
173+
}
174+
var targetSchema = Schema switch
175+
{
171176
OpenApiSchemaReference schemaReference => schemaReference.RecursiveTarget,
172177
OpenApiSchema schema => schema,
173178
_ => null,

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

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

110-
internal void SerializeInternal(IOpenApiWriter writer, OpenApiSpecVersion version,
110+
internal void SerializeInternal(IOpenApiWriter writer, OpenApiSpecVersion version,
111111
Action<IOpenApiWriter, IOpenApiSerializable> callback)
112112
{
113113
Utils.CheckArgumentNull(writer);
@@ -202,7 +202,12 @@ internal virtual void WriteRequestBodySchemaForV2(IOpenApiWriter writer, Diction
202202
// uniqueItems
203203
// enum
204204
// multipleOf
205-
var targetSchema = Schema switch {
205+
if (Schema is OpenApiSchemaReference)
206+
{
207+
writer.CountInlineReference();
208+
}
209+
var targetSchema = Schema switch
210+
{
206211
OpenApiSchemaReference schemaReference => schemaReference.RecursiveTarget,
207212
OpenApiSchema schema => schema,
208213
_ => null,

‎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);
@@ -254,6 +254,7 @@ private protected void SerializeInternal(IOpenApiWriter writer,
254254
Action<IOpenApiWriter, U> action)
255255
{
256256
Utils.CheckArgumentNull(writer);
257+
writer.CountInlineReference();
257258
ApplyToTarget(element => action(writer, element));
258259
}
259260
}

‎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
@@ -250,8 +250,14 @@ private void SerializeAsWithoutLoops(IOpenApiWriter writer, Action<IOpenApiWrite
250250
}
251251
else
252252
{
253-
SerializeInternal(writer, (w, element) => action(w, element));
254-
writer.GetSettings().LoopDetector.PopLoop<IOpenApiSchema>();
253+
try
254+
{
255+
SerializeInternal(writer, (w, element) => action(w, element));
256+
}
257+
finally
258+
{
259+
writer.GetSettings().LoopDetector.PopLoop<IOpenApiSchema>();
260+
}
255261
}
256262

257263
}

‎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

‎src/Microsoft.OpenApi/Writers/OpenApiWriterBase.cs‎

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ public abstract class OpenApiWriterBase : IOpenApiWriter
3535
/// Number which specifies the level of indentation.
3636
/// </summary>
3737
private int _indentLevel;
38+
private int _inlineReferenceCount;
3839

3940
/// <summary>
4041
/// Initializes a new instance of the <see cref="OpenApiWriterBase"/> class.
@@ -70,6 +71,20 @@ protected OpenApiWriterBase(TextWriter textWriter, OpenApiWriterSettings? settin
7071
/// </summary>
7172
protected TextWriter Writer { get; }
7273

74+
internal void CountInlineReference()
75+
{
76+
if (_inlineReferenceCount >= Settings.MaxInlineReferenceCount)
77+
{
78+
throw new OpenApiWriterException(
79+
string.Format(
80+
CultureInfo.InvariantCulture,
81+
SRResource.MaximumInlineReferenceCountExceeded,
82+
Settings.MaxInlineReferenceCount));
83+
}
84+
85+
_inlineReferenceCount++;
86+
}
87+
7388
/// <summary>
7489
/// Write start object.
7590
/// </summary>
@@ -319,7 +334,7 @@ public virtual void WriteIndentation()
319334
Writer.Write(IndentationString);
320335
}
321336
}
322-
337+
323338
/// <summary>
324339
/// Get current scope.
325340
/// </summary>

0 commit comments

Comments
 (0)