Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added SkipIntrospectionFields option on MaxDepthAnalyzer #4649

Merged
merged 6 commits into from
Jan 15, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -137,10 +137,42 @@ public static IRequestExecutorBuilder AddValidationRule<T>(
return ConfigureValidation(builder, b => b.TryAddValidationRule(factory));
}

/// <summary>
/// Adds a validation rule that inspects if a GraphQL query document
/// exceeds the maximum allowed operation depth.
/// </summary>
public static IRequestExecutorBuilder AddMaxExecutionDepthRule(
this IRequestExecutorBuilder builder,
int maxAllowedExecutionDepth) =>
int maxAllowedExecutionDepth)
{
if (builder is null)
{
throw new ArgumentNullException(nameof(builder));
}

ConfigureValidation(builder, b => b.AddMaxExecutionDepthRule(maxAllowedExecutionDepth));
return builder;
}

/// <summary>
/// Adds a validation rule that inspects if a GraphQL query document
/// exceeds the maximum allowed operation depth.
/// </summary>
public static IRequestExecutorBuilder AddMaxExecutionDepthRule(
this IRequestExecutorBuilder builder,
int maxAllowedExecutionDepth,
bool skipIntrospectionFields)
{
if (builder is null)
{
throw new ArgumentNullException(nameof(builder));
}

ConfigureValidation(
builder,
b => b.AddMaxExecutionDepthRule(maxAllowedExecutionDepth, skipIntrospectionFields));
return builder;
}

/// <summary>
/// Adds a validation rule that only allows requests to use `__schema` or `__type`
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
using System;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Linq;
using HotChocolate.Language;
using HotChocolate.Types;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
using System;
using HotChocolate.Language;

namespace HotChocolate.Execution.Pipeline.Complexity;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -303,10 +303,20 @@ public static IValidationBuilder AddOperationRules(
public static IValidationBuilder AddMaxExecutionDepthRule(
this IValidationBuilder builder,
int maxAllowedExecutionDepth)
=> AddMaxExecutionDepthRule(builder, maxAllowedExecutionDepth, false);

public static IValidationBuilder AddMaxExecutionDepthRule(
this IValidationBuilder builder,
int maxAllowedExecutionDepth,
bool skipIntrospectionFields)
{
return builder
.TryAddValidationVisitor((_, o) => new MaxExecutionDepthVisitor(o))
.SetAllowedExecutionDepth(maxAllowedExecutionDepth);
.ModifyValidationOptions(o =>
{
o.MaxAllowedExecutionDepth = maxAllowedExecutionDepth;
o.SkipIntrospectionFields = skipIntrospectionFields;
});
}

/// <summary>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -85,26 +85,10 @@ public static IValidationBuilder ConfigureValidation(
return builder;
}

/// <summary>
/// Sets the maximum allowed depth of a query. The default
/// value is <see langword="null"/>. The minimum allowed value is
/// <c>1</c>.
/// </summary>
internal static IValidationBuilder SetAllowedExecutionDepth(
internal static IValidationBuilder ModifyValidationOptions(
this IValidationBuilder builder,
int allowedExecutionDepth)
{
if (allowedExecutionDepth < 1)
{
throw new ArgumentOutOfRangeException(
nameof(allowedExecutionDepth),
allowedExecutionDepth,
Resources.HotChocolateValidationBuilderExtensions_MinimumAllowedValue);
}

return builder.ConfigureValidation(m =>
m.Modifiers.Add(o => o.MaxAllowedExecutionDepth = allowedExecutionDepth));
}
Action<ValidationOptions> configure)
=> builder.ConfigureValidation(m => m.Modifiers.Add(configure));

public static IValidationBuilder TryAddValidationVisitor<T>(
this IValidationBuilder builder,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,10 @@
<ProjectReference Include="..\Types\HotChocolate.Types.csproj" />
</ItemGroup>

<ItemGroup>
<InternalsVisibleTo Include="HotChocolate.Validation.Tests" />
</ItemGroup>

<ItemGroup>
<PackageReference Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="3.1.4" />
<PackageReference Include="Microsoft.Extensions.Options" Version="3.1.4" />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,4 +7,10 @@ public interface IMaxExecutionDepthOptionsAccessor
/// <see langword="null"/>. The minimum allowed value is <c>1</c>.
/// </summary>
int? MaxAllowedExecutionDepth { get; }

/// <summary>
/// Specifies that the max execution depth analysis
/// shall skip introspection fields.
/// </summary>
bool SkipIntrospectionFields { get; }
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ namespace HotChocolate.Validation.Options;
/// </summary>
public class ValidationOptions : IMaxExecutionDepthOptionsAccessor
{
private int? _maxAllowedExecutionDepth;

/// <summary>
/// Gets the document rules of the validation.
/// </summary>
Expand All @@ -17,5 +19,18 @@ public class ValidationOptions : IMaxExecutionDepthOptionsAccessor
/// Gets the maximum allowed depth of a query. The default value is
/// <see langword="null"/>. The minimum allowed value is <c>1</c>.
/// </summary>
public int? MaxAllowedExecutionDepth { get; set; }
public int? MaxAllowedExecutionDepth
{
get => _maxAllowedExecutionDepth;
set
{
_maxAllowedExecutionDepth = value < 1 ? 1 : value;
}
}

/// <summary>
/// Specifies that the max execution depth analysis
/// shall skip introspection fields.
/// </summary>
public bool SkipIntrospectionFields { get; set; }
}

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
Expand Up @@ -209,9 +209,6 @@
<data name="ErrorHelper_MaxExecutionDepth" xml:space="preserve">
<value>The GraphQL document has an execution depth of {0} which exceeds the max allowed execution depth of {1}.</value>
</data>
<data name="HotChocolateValidationBuilderExtensions_MinimumAllowedValue" xml:space="preserve">
<value>The minimum allowed value is 1.</value>
</data>
<data name="ErrorHelper_DirectiveMustBeUniqueInLocation" xml:space="preserve">
<value>Only one of each directive is allowed per location.</value>
</data>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,12 @@ protected override ISyntaxVisitorAction Enter(
FieldNode node,
IDocumentValidatorContext context)
{
if (_options.SkipIntrospectionFields &&
node.Name.Value.StartsWith("__"))
{
return Skip;
}

context.Fields.Push(node);

if (context.Count < context.Fields.Count)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -765,6 +765,42 @@ ... on Droid {
});
}

[Fact]
public async Task Execution_Depth_Is_Skipped_For_Introspection()
{
Snapshot.FullName();
await ExpectValid(@"
query {
__schema {
types {
fields {
type {
kind
name
ofType {
kind
name
ofType {
kind
name
ofType {
kind
name
}
}
}
}
}
}
}
}",
configure: c =>
{
AddDefaultConfiguration(c);
c.AddMaxExecutionDepthRule(3, skipIntrospectionFields: true);
});
}

[InlineData("true")]
[InlineData("false")]
[Theory]
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
using System;
using Microsoft.Extensions.DependencyInjection;
using Xunit;

namespace HotChocolate.Validation.Extensions;

public class HotChocolateValidationBuilderExtensionsTests
{
[Fact]
public void AddMaxExecutionDepthRule1_Builder_Is_Null()
{
void Fail()
=> HotChocolateValidationBuilderExtensions.AddMaxExecutionDepthRule(null!, 5);
Assert.Throws<ArgumentNullException>(Fail);
}

[Fact]
public void AddMaxExecutionDepthRule2_Builder_Is_Null()
{
void Fail()
=> HotChocolateValidationBuilderExtensions.AddMaxExecutionDepthRule(null!, 5, true);
Assert.Throws<ArgumentNullException>(Fail);
}
}
32 changes: 31 additions & 1 deletion src/HotChocolate/Core/test/Validation.Tests/MaxDepthRuleTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ namespace HotChocolate.Validation
public class MaxDepthRuleTests : DocumentValidatorVisitorTestBase
{
public MaxDepthRuleTests()
: base(b => b.AddMaxExecutionDepthRule(3))
: base(b => b.AddMaxExecutionDepthRule(3, skipIntrospectionFields: false))
{
}

Expand Down Expand Up @@ -87,6 +87,36 @@ ... on Level3 {
");
}

[Fact]
public void MaxDepth3_IntrospectionQuery_Exceeds_Allowed_Depth_Error()
{
ExpectErrors(@"
query {
__schema {
types {
fields {
type {
kind
name
ofType {
kind
name
ofType {
kind
name
ofType {
kind
name
}
}
}
}
}
}
}
}");
}

[Fact]
public void MaxDepth3_QueryWith3Levels_Valid()
{
Expand Down
Loading