Skip to content

Commit

Permalink
[SECURITY PROBLEM] Implement a configuration option to disable the su…
Browse files Browse the repository at this point in the history
…ggestion feature when a GraphQL query fails (#319)

* impl: add validate option

* impl: disable field name and type name suggestion

* test: fix signature (validate option)

* fix: logic of suggestion

* test: implement test for fields_on_correct_type_rule

* test: add disable suggestion tests

* fix: use variadic arguments

* test: implement DIsableSuggestion

* test: remove ValidateOption

* remove unneccesary changes

* test: add tests for disable suggestion

* .
  • Loading branch information
tomoikey authored Sep 29, 2024
1 parent eedca08 commit 888baf8
Show file tree
Hide file tree
Showing 4 changed files with 237 additions and 143 deletions.
39 changes: 26 additions & 13 deletions validator/rules/fields_on_correct_type.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,27 +11,40 @@ import (
. "github.com/vektah/gqlparser/v2/validator"
)

var FieldsOnCorrectTypeRule = Rule{
Name: "FieldsOnCorrectType",
RuleFunc: func(observers *Events, addError AddErrFunc) {
observers.OnField(func(walker *Walker, field *ast.Field) {
if field.ObjectDefinition == nil || field.Definition != nil {
return
}
func ruleFuncFieldsOnCorrectType(observers *Events, addError AddErrFunc, disableSuggestion bool) {
observers.OnField(func(walker *Walker, field *ast.Field) {
if field.ObjectDefinition == nil || field.Definition != nil {
return
}

message := fmt.Sprintf(`Cannot query field "%s" on type "%s".`, field.Name, field.ObjectDefinition.Name)
message := fmt.Sprintf(`Cannot query field "%s" on type "%s".`, field.Name, field.ObjectDefinition.Name)

if !disableSuggestion {
if suggestedTypeNames := getSuggestedTypeNames(walker, field.ObjectDefinition, field.Name); suggestedTypeNames != nil {
message += " Did you mean to use an inline fragment on " + QuotedOrList(suggestedTypeNames...) + "?"
} else if suggestedFieldNames := getSuggestedFieldNames(field.ObjectDefinition, field.Name); suggestedFieldNames != nil {
message += " Did you mean " + QuotedOrList(suggestedFieldNames...) + "?"
}
}

addError(
Message("%s", message),
At(field.Position),
)
})
}

var FieldsOnCorrectTypeRule = Rule{
Name: "FieldsOnCorrectType",
RuleFunc: func(observers *Events, addError AddErrFunc) {
ruleFuncFieldsOnCorrectType(observers, addError, false)
},
}

addError(
Message("%s", message),
At(field.Position),
)
})
var FieldsOnCorrectTypeRuleWithoutSuggestions = Rule{
Name: "FieldsOnCorrectTypeWithoutSuggestions",
RuleFunc: func(observers *Events, addError AddErrFunc) {
ruleFuncFieldsOnCorrectType(observers, addError, true)
},
}

Expand Down
70 changes: 47 additions & 23 deletions validator/rules/known_argument_names.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,43 +7,53 @@ import (
. "github.com/vektah/gqlparser/v2/validator"
)

var KnownArgumentNamesRule = Rule{
Name: "KnownArgumentNames",
RuleFunc: func(observers *Events, addError AddErrFunc) {
// A GraphQL field is only valid if all supplied arguments are defined by that field.
observers.OnField(func(walker *Walker, field *ast.Field) {
if field.Definition == nil || field.ObjectDefinition == nil {
return
func ruleFuncKnownArgumentNames(observers *Events, addError AddErrFunc, disableSuggestion bool) {
// A GraphQL field is only valid if all supplied arguments are defined by that field.
observers.OnField(func(walker *Walker, field *ast.Field) {
if field.Definition == nil || field.ObjectDefinition == nil {
return
}
for _, arg := range field.Arguments {
def := field.Definition.Arguments.ForName(arg.Name)
if def != nil {
continue
}
for _, arg := range field.Arguments {
def := field.Definition.Arguments.ForName(arg.Name)
if def != nil {
continue
}

if disableSuggestion {
addError(
Message(`Unknown argument "%s" on field "%s.%s".`, arg.Name, field.ObjectDefinition.Name, field.Name),
At(field.Position),
)
} else {
var suggestions []string
for _, argDef := range field.Definition.Arguments {
suggestions = append(suggestions, argDef.Name)
}

addError(
Message(`Unknown argument "%s" on field "%s.%s".`, arg.Name, field.ObjectDefinition.Name, field.Name),
SuggestListQuoted("Did you mean", arg.Name, suggestions),
At(field.Position),
)
}
})
}
})

observers.OnDirective(func(walker *Walker, directive *ast.Directive) {
if directive.Definition == nil {
return
observers.OnDirective(func(walker *Walker, directive *ast.Directive) {
if directive.Definition == nil {
return
}
for _, arg := range directive.Arguments {
def := directive.Definition.Arguments.ForName(arg.Name)
if def != nil {
continue
}
for _, arg := range directive.Arguments {
def := directive.Definition.Arguments.ForName(arg.Name)
if def != nil {
continue
}

if disableSuggestion {
addError(
Message(`Unknown argument "%s" on directive "@%s".`, arg.Name, directive.Name),
At(directive.Position),
)
} else {
var suggestions []string
for _, argDef := range directive.Definition.Arguments {
suggestions = append(suggestions, argDef.Name)
Expand All @@ -55,7 +65,21 @@ var KnownArgumentNamesRule = Rule{
At(directive.Position),
)
}
})
}
})
}

var KnownArgumentNamesRule = Rule{
Name: "KnownArgumentNames",
RuleFunc: func(observers *Events, addError AddErrFunc) {
ruleFuncKnownArgumentNames(observers, addError, false)
},
}

var KnownArgumentNamesRuleWithoutSuggestions = Rule{
Name: "KnownArgumentNamesWithoutSuggestions",
RuleFunc: func(observers *Events, addError AddErrFunc) {
ruleFuncKnownArgumentNames(observers, addError, true)
},
}

Expand Down
88 changes: 53 additions & 35 deletions validator/rules/known_type_names.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,46 +7,50 @@ import (
. "github.com/vektah/gqlparser/v2/validator"
)

var KnownTypeNamesRule = Rule{
Name: "KnownTypeNames",
RuleFunc: func(observers *Events, addError AddErrFunc) {
observers.OnVariable(func(walker *Walker, variable *ast.VariableDefinition) {
typeName := variable.Type.Name()
typdef := walker.Schema.Types[typeName]
if typdef != nil {
return
}
func ruleFuncKnownTypeNames(observers *Events, addError AddErrFunc, disableSuggestion bool) {
observers.OnVariable(func(walker *Walker, variable *ast.VariableDefinition) {
typeName := variable.Type.Name()
typdef := walker.Schema.Types[typeName]
if typdef != nil {
return
}

addError(
Message(`Unknown type "%s".`, typeName),
At(variable.Position),
)
})
addError(
Message(`Unknown type "%s".`, typeName),
At(variable.Position),
)
})

observers.OnInlineFragment(func(walker *Walker, inlineFragment *ast.InlineFragment) {
typedName := inlineFragment.TypeCondition
if typedName == "" {
return
}
observers.OnInlineFragment(func(walker *Walker, inlineFragment *ast.InlineFragment) {
typedName := inlineFragment.TypeCondition
if typedName == "" {
return
}

def := walker.Schema.Types[typedName]
if def != nil {
return
}
def := walker.Schema.Types[typedName]
if def != nil {
return
}

addError(
Message(`Unknown type "%s".`, typedName),
At(inlineFragment.Position),
)
})
addError(
Message(`Unknown type "%s".`, typedName),
At(inlineFragment.Position),
)
})

observers.OnFragment(func(walker *Walker, fragment *ast.FragmentDefinition) {
typeName := fragment.TypeCondition
def := walker.Schema.Types[typeName]
if def != nil {
return
}
observers.OnFragment(func(walker *Walker, fragment *ast.FragmentDefinition) {
typeName := fragment.TypeCondition
def := walker.Schema.Types[typeName]
if def != nil {
return
}

if disableSuggestion {
addError(
Message(`Unknown type "%s".`, typeName),
At(fragment.Position),
)
} else {
var possibleTypes []string
for _, t := range walker.Schema.Types {
possibleTypes = append(possibleTypes, t.Name)
Expand All @@ -57,7 +61,21 @@ var KnownTypeNamesRule = Rule{
SuggestListQuoted("Did you mean", typeName, possibleTypes),
At(fragment.Position),
)
})
}
})
}

var KnownTypeNamesRule = Rule{
Name: "KnownTypeNames",
RuleFunc: func(observers *Events, addError AddErrFunc) {
ruleFuncKnownTypeNames(observers, addError, false)
},
}

var KnownTypeNamesRuleWithoutSuggestions = Rule{
Name: "KnownTypeNamesWithoutSuggestions",
RuleFunc: func(observers *Events, addError AddErrFunc) {
ruleFuncKnownTypeNames(observers, addError, true)
},
}

Expand Down
Loading

0 comments on commit 888baf8

Please sign in to comment.