Fix CoerceArgumentValues() hasValue #1056
Open
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Fixes a bug discovered whilst carefully evaluating
CoerceArgumentValues()
that leads to "undefined value leakage" and potential null pointer exception if strictly following the spec. GraphQL.js does not suffer this, so this is a spec bug rather than an implementation bug.Consider the following schema:
And the following GraphQL query:
Imagine that we send an empty object (
{}
) as the variable values.Coercing the variableValues according to https://spec.graphql.org/draft/#CoerceVariableValues() we get an empty object (
{}
).Fast-forward to https://spec.graphql.org/draft/#CoerceArgumentValues():
coercedValues = {}
argumentValues = { arg: $var }
fieldName = 'field'
field named {fieldName}.
argumentDefinitions = { arg: String! = "defaultValue" }
argumentName = 'arg'
argumentType = String!
defaultValue = 'defaultValue'
{argumentName}. 🐛 !!!BUG!!!
hasValue = true
becauseargumentValues
does provide the variable$var
as the value for the argument 'arg'{argumentName}.
argumentValue = $var
Yes, $var is a variable
variableName = 'var'
{variableName}. 🐛 !!!BUG!!! This does not fire, but
hasValue
is already {true} by the above.{variableName}. 🐛 !!!BUG!!!
value = undefined
NOT TRIGGERED
NOT TRIGGERED
since hasValue is true{defaultValue}.
not {true} or {value} is {null}, raise a field error.
NOT TRIGGERED
becausehasValue
is {true} and value is not {null} (it is undefined!)Yes, it is
It is not, it is undefined
{null}.
It is!
{value}.
coercedValues[argumentName] = undefined
(sincevalue
is undefined){argumentType}, raise a field error.
input coercion rules of {argumentType}.
{coercedValue}.
Expectation:
coercedValues = { arg: "defaultValue" }
Actual result:
coercedValues = { arg: undefined }
arg
is non-null string -> NPE! 💥Essentially the phrase "Let {hasValue} be {true} if {argumentValues} provides a value for the name {argumentName}" is at best ambiguous and at worst plain wrong, since the next two lines get the "value" for {argumentName} and then check to see if this {value} is a variable.
This PR fixes this issue by only setting
hasValue
totrue
when the value is explicitly resolved via the two branches: variable and non-variable.There is no need for a GraphQL.js PR for this since GraphQL.js already follows the expected behavior; reproduction: