Repository: graphql/graphql-js (ts)

Issue:
Parsing SDL allows `true`, `false`, and `null` for enum value names
There is a schema validator that forbids `true`, `false`, and `null` as enum value names sinde https://github.com/graphql/graphql-js/pull/812/files. The `parse` function together with the `specifiedSDLRules` rules however allow these strings to be used as enum value names/values (they're the same there). This means you can parse and successfully validate an SDL which generates a validation error when creating the schema.

I'm not quite sure whether this is a bug in the parser or a missing SDL validator. `parseEnumValueDefinition` uses `parseName` and states that the `EnumValue` non-terminal is equivalent to the `name` non-terminal:

```javascript

  /**
   * EnumValueDefinition : Description? EnumValue Directives[Const]?
   *
   * EnumValue : Name
   */
  ;

  _proto.parseEnumValueDefinition = function parseEnumValueDefinition() {
    var start = this._lexer.token;
    var description = this.parseDescription();
    var name = this.parseName();
    var directives = this.parseDirectives(true);
    return {
      kind: Kind.ENUM_VALUE_DEFINITION,
      description: description,
      name: name,
      directives: directives,
      loc: this.loc(start)
    };
  }
```

There also is an `EnumValue` non-terminal which is defined as follows:

> EnumValue : Name but not `true`, `false` or `null`

So an enum declaration either should switch from `EnumName` to `EnumValue`, or there should be an SDL validator that rejects the three forbidden words when used as enum value names/values.

Pull request description:
Disallow "true", "false", and "null" as enum values
The specification disallows these in the SDL grammar definition, and using one of these enum values would result in a schema that would throw validation errors on creation.

Fixes #3221.

I also changed the type of `EnumValueDefinitionNode.name` from `NameNode` to `EnumValueNode`, so this is definitely breaking. We might even want to rename the `name` property to `value`, but that would be even more breaking.

Patch:
diff --git a/src/language/parser.ts b/src/language/parser.ts
index 7095cad1..9ecd0456 100644
--- a/src/language/parser.ts
+++ b/src/language/parser.ts
@@ -1036,13 +1036,11 @@ export class Parser {
 
   /**
    * EnumValueDefinition : Description? EnumValue Directives[Const]?
-   *
-   * EnumValue : Name
    */
   parseEnumValueDefinition(): EnumValueDefinitionNode {
     const start = this._lexer.token;
     const description = this.parseDescription();
-    const name = this.parseName();
+    const name = this.parseEnumValueName();
     const directives = this.parseConstDirectives();
     return this.node<EnumValueDefinitionNode>(start, {
       kind: Kind.ENUM_VALUE_DEFINITION,
@@ -1052,6 +1050,26 @@ export class Parser {
     });
   }
 
+  /**
+   * EnumValue : Name but not `true`, `false` or `null`
+   */
+  parseEnumValueName(): NameNode {
+    if (
+      this._lexer.token.value === 'true' ||
+      this._lexer.token.value === 'false' ||
+      this._lexer.token.value === 'null'
+    ) {
+      throw syntaxError(
+        this._lexer.source,
+        this._lexer.token.start,
+        `${getTokenDesc(
+          this._lexer.token,
+        )} is reserved and cannot be used for an enum value.`,
+      );
+    }
+    return this.parseName();
+  }
+
   /**
    * InputObjectTypeDefinition :
    *   - Description? input Name Directives[Const]? InputFieldsDefinition?


Test patch:
diff --git a/src/language/__tests__/parser-test.ts b/src/language/__tests__/parser-test.ts
index ffdfe11b..8cbfe16c 100644
--- a/src/language/__tests__/parser-test.ts
+++ b/src/language/__tests__/parser-test.ts
@@ -120,6 +120,26 @@ describe('Parser', () => {
     });
   });
 
+  it('does not allow "true", "false", or "null" as enum value', () => {
+    expectSyntaxError('enum Test { VALID, true }').to.deep.equal({
+      message:
+        'Syntax Error: Name "true" is reserved and cannot be used for an enum value.',
+      locations: [{ line: 1, column: 20 }],
+    });
+
+    expectSyntaxError('enum Test { VALID, false }').to.deep.equal({
+      message:
+        'Syntax Error: Name "false" is reserved and cannot be used for an enum value.',
+      locations: [{ line: 1, column: 20 }],
+    });
+
+    expectSyntaxError('enum Test { VALID, null }').to.deep.equal({
+      message:
+        'Syntax Error: Name "null" is reserved and cannot be used for an enum value.',
+      locations: [{ line: 1, column: 20 }],
+    });
+  });
+
   it('parses multi-byte characters', () => {
     // Note: \u0A0A could be naively interpreted as two line-feed chars.
     const ast = parse(`