-
-
Notifications
You must be signed in to change notification settings - Fork 748
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Added Introspection Cycle Detection Rule
- Loading branch information
1 parent
a190f03
commit d4c48d9
Showing
27 changed files
with
473 additions
and
38 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,27 @@ | ||
namespace HotChocolate.Validation; | ||
|
||
internal sealed class CoordinateLimit | ||
{ | ||
public ushort MaxAllowed { get; private set; } | ||
|
||
public ushort Count { get; private set; } | ||
|
||
public bool Add() | ||
{ | ||
if (Count < MaxAllowed) | ||
{ | ||
Count++; | ||
return true; | ||
} | ||
|
||
return false; | ||
} | ||
|
||
public void Remove() => Count--; | ||
|
||
public void Reset(ushort maxAllowed) | ||
{ | ||
MaxAllowed = maxAllowed; | ||
Count = 0; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
90 changes: 90 additions & 0 deletions
90
src/HotChocolate/Core/src/Validation/FieldDepthCycleTracker.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,90 @@ | ||
using System.Collections.Generic; | ||
using HotChocolate.Language; | ||
|
||
namespace HotChocolate.Validation; | ||
|
||
/// <summary> | ||
/// Allows to track field cycle depths in a GraphQL query. | ||
/// </summary> | ||
public sealed class FieldDepthCycleTracker | ||
{ | ||
private readonly Dictionary<FieldCoordinate, CoordinateLimit> _coordinates = new(); | ||
private readonly List<CoordinateLimit> _limits = new(); | ||
private ushort? _defaultMaxAllowed; | ||
|
||
/// <summary> | ||
/// Adds a field coordinate to the tracker. | ||
/// </summary> | ||
/// <param name="coordinate"> | ||
/// A field coordinate. | ||
/// </param> | ||
/// <returns> | ||
/// <c>true</c> if the field coordinate has not reached its cycle depth limit; | ||
/// otherwise, <c>false</c>. | ||
/// </returns> | ||
public bool Add(FieldCoordinate coordinate) | ||
{ | ||
if (_coordinates.TryGetValue(coordinate, out var limit)) | ||
{ | ||
return limit.Add(); | ||
} | ||
|
||
if(_defaultMaxAllowed.HasValue) | ||
{ | ||
_limits.TryPop(out limit); | ||
limit ??= new CoordinateLimit(); | ||
limit.Reset(_defaultMaxAllowed.Value); | ||
_coordinates.Add(coordinate, limit); | ||
return limit.Add(); | ||
} | ||
|
||
return true; | ||
} | ||
|
||
/// <summary> | ||
/// Removes a field coordinate from the tracker. | ||
/// </summary> | ||
/// <param name="coordinate"> | ||
/// A field coordinate. | ||
/// </param> | ||
public void Remove(FieldCoordinate coordinate) | ||
{ | ||
if (_coordinates.TryGetValue(coordinate, out var limit)) | ||
{ | ||
limit.Remove(); | ||
} | ||
} | ||
|
||
/// <summary> | ||
/// Initializes the field depth tracker with the specified limits. | ||
/// </summary> | ||
/// <param name="limits"> | ||
/// A collection of field coordinates and their cycle depth limits. | ||
/// </param> | ||
/// <param name="defaultMaxAllowed"> | ||
/// The default cycle depth limit for coordinates that were not explicitly defined. | ||
/// </param> | ||
public void Initialize( | ||
IEnumerable<(FieldCoordinate Coordinate, ushort MaxAllowed)> limits, | ||
ushort? defaultMaxAllowed = null) | ||
{ | ||
foreach (var (coordinate, maxAllowed) in limits) | ||
{ | ||
_limits.TryPop(out var limit); | ||
limit ??= new CoordinateLimit(); | ||
limit.Reset(maxAllowed); | ||
_coordinates.Add(coordinate, limit); | ||
} | ||
|
||
_defaultMaxAllowed = defaultMaxAllowed; | ||
} | ||
|
||
/// <summary> | ||
/// Resets the field depth tracker. | ||
/// </summary> | ||
public void Reset() | ||
{ | ||
_limits.AddRange(_coordinates.Values); | ||
_coordinates.Clear(); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
77 changes: 77 additions & 0 deletions
77
src/HotChocolate/Core/src/Validation/Rules/IntrospectionDepthVisitor.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,77 @@ | ||
using HotChocolate.Language; | ||
using HotChocolate.Language.Visitors; | ||
using HotChocolate.Types; | ||
using HotChocolate.Types.Introspection; | ||
using HotChocolate.Utilities; | ||
|
||
namespace HotChocolate.Validation.Rules; | ||
|
||
/// <summary> | ||
/// This rules ensures that recursive introspection fields cannot be used | ||
/// to create endless cycles. | ||
/// </summary> | ||
internal sealed class IntrospectionDepthVisitor : TypeDocumentValidatorVisitor | ||
{ | ||
private readonly (FieldCoordinate Coordinate, ushort MaxAllowed)[] _limits = | ||
[ | ||
(new FieldCoordinate("__Type", "fields"), 1), | ||
(new FieldCoordinate("__Type", "inputFields"), 1), | ||
(new FieldCoordinate("__Type", "interfaces"), 1), | ||
(new FieldCoordinate("__Type", "possibleTypes"), 1), | ||
(new FieldCoordinate("__Type", "ofType"), 8), | ||
]; | ||
|
||
protected override ISyntaxVisitorAction Enter( | ||
DocumentNode node, | ||
IDocumentValidatorContext context) | ||
{ | ||
context.FieldDepth.Initialize(_limits); | ||
return base.Enter(node, context); | ||
} | ||
|
||
protected override ISyntaxVisitorAction Enter( | ||
FieldNode node, | ||
IDocumentValidatorContext context) | ||
{ | ||
if (IntrospectionFields.TypeName.EqualsOrdinal(node.Name.Value)) | ||
{ | ||
return Skip; | ||
} | ||
|
||
if (context.Types.TryPeek(out var type) | ||
&& type.NamedType() is IComplexOutputType ot | ||
&& ot.Fields.TryGetField(node.Name.Value, out var of)) | ||
{ | ||
// we are only interested in fields if the root field is either | ||
// __type or __schema. | ||
if (context.OutputFields.Count == 0 | ||
&& !of.IsIntrospectionField) | ||
{ | ||
return Skip; | ||
} | ||
|
||
if (!context.FieldDepth.Add(of.Coordinate)) | ||
{ | ||
context.ReportMaxIntrospectionDepthOverflow(node); | ||
return Break; | ||
} | ||
|
||
context.OutputFields.Push(of); | ||
context.Types.Push(of.Type); | ||
return Continue; | ||
} | ||
|
||
context.UnexpectedErrorsDetected = true; | ||
return Skip; | ||
} | ||
|
||
protected override ISyntaxVisitorAction Leave( | ||
FieldNode node, | ||
IDocumentValidatorContext context) | ||
{ | ||
context.FieldDepth.Remove(context.OutputFields.Peek().Coordinate); | ||
context.Types.Pop(); | ||
context.OutputFields.Pop(); | ||
return Continue; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.