1313public  class  ParserOptions  {
1414
1515    /** 
16-      * An  graphql hacking vector is to send nonsensical queries that burn lots of parsing CPU time and burn 
17-      * memory representing a document that wont  ever execute.  To prevent this for most users, graphql-java 
16+      * A  graphql hacking vector is to send nonsensical queries that burn lots of parsing CPU time and burn 
17+      * memory representing a document that won't  ever execute.  To prevent this for most users, graphql-java 
1818     * set this value to 15000.  ANTLR parsing time is linear to the number of tokens presented.  The more you 
1919     * allow the longer it takes. 
2020     * 
2121     * If you want to allow more, then {@link #setDefaultParserOptions(ParserOptions)} allows you to change this 
2222     * JVM wide. 
2323     */ 
24-     public  static  final  int  MAX_QUERY_TOKENS  = 15000 ;
24+     public  static  final  int  MAX_QUERY_TOKENS  = 15_000 ;
25+     /** 
26+      * Another graphql hacking vector is to send large amounts of whitespace in operations that burn lots of parsing CPU time and burn 
27+      * memory representing a document.  Whitespace token processing in ANTLR is 2 orders of magnitude faster than grammar token processing 
28+      * however it still takes some time to happen. 
29+      * 
30+      * If you want to allow more, then {@link #setDefaultParserOptions(ParserOptions)} allows you to change this 
31+      * JVM wide. 
32+      */ 
33+     public  static  final  int  MAX_WHITESPACE_TOKENS  = 200_000 ;
2534
2635    private  static  ParserOptions  defaultJvmParserOptions  = newParserOptions ()
2736            .captureIgnoredChars (false )
2837            .captureSourceLocation (true )
2938            .captureLineComments (true )
3039            .maxTokens (MAX_QUERY_TOKENS ) // to prevent a billion laughs style attacks, we set a default for graphql-java 
40+             .maxWhitespaceTokens (MAX_WHITESPACE_TOKENS )
41+             .build ();
42+ 
43+     private  static  ParserOptions  defaultJvmOperationParserOptions  = newParserOptions ()
44+             .captureIgnoredChars (false )
45+             .captureSourceLocation (true )
46+             .captureLineComments (false ) // #comments are not useful in query parsing 
47+             .maxTokens (MAX_QUERY_TOKENS ) // to prevent a billion laughs style attacks, we set a default for graphql-java 
48+             .maxWhitespaceTokens (MAX_WHITESPACE_TOKENS )
49+             .build ();
3150
51+     private  static  ParserOptions  defaultJvmSdlParserOptions  = newParserOptions ()
52+             .captureIgnoredChars (false )
53+             .captureSourceLocation (true )
54+             .captureLineComments (true ) // #comments are useful in SDL parsing 
55+             .maxTokens (Integer .MAX_VALUE ) // we are less worried about a billion laughs with SDL parsing since the call path is not facing attackers 
56+             .maxWhitespaceTokens (Integer .MAX_VALUE )
3257            .build ();
3358
3459    /** 
35-      * By default the Parser will not capture ignored characters.  A static holds this default 
60+      * By default,  the Parser will not capture ignored characters.  A static holds this default 
3661     * value in a JVM wide basis options object. 
3762     * 
3863     * Significant memory savings can be made if we do NOT capture ignored characters, 
3964     * especially in SDL parsing. 
4065     * 
41-      * @return the static default value on whether to capture ignored chars  
66+      * @return the static default JVM value  
4267     * 
4368     * @see graphql.language.IgnoredChar 
4469     * @see graphql.language.SourceLocation 
@@ -48,7 +73,7 @@ public static ParserOptions getDefaultParserOptions() {
4873    }
4974
5075    /** 
51-      * By default the Parser will not capture ignored characters.  A static holds this default 
76+      * By default,  the Parser will not capture ignored characters.  A static holds this default 
5277     * value in a JVM wide basis options object. 
5378     * 
5479     * Significant memory savings can be made if we do NOT capture ignored characters, 
@@ -65,17 +90,80 @@ public static void setDefaultParserOptions(ParserOptions options) {
6590        defaultJvmParserOptions  = assertNotNull (options );
6691    }
6792
93+ 
94+     /** 
95+      * By default, for operation parsing, the Parser will not capture ignored characters, and it will not capture line comments into AST 
96+      * elements .  A static holds this default value for operation parsing in a JVM wide basis options object. 
97+      * 
98+      * @return the static default JVM value for operation parsing 
99+      * 
100+      * @see graphql.language.IgnoredChar 
101+      * @see graphql.language.SourceLocation 
102+      */ 
103+     public  static  ParserOptions  getDefaultOperationParserOptions () {
104+         return  defaultJvmOperationParserOptions ;
105+     }
106+ 
107+     /** 
108+      * By default, the Parser will not capture ignored characters or line comments.  A static holds this default 
109+      * value in a JVM wide basis options object for operation parsing. 
110+      * 
111+      * This static can be set to true to allow the behavior of version 16.x or before. 
112+      * 
113+      * @param options - the new default JVM parser options for operation parsing 
114+      * 
115+      * @see graphql.language.IgnoredChar 
116+      * @see graphql.language.SourceLocation 
117+      */ 
118+     public  static  void  setDefaultOperationParserOptions (ParserOptions  options ) {
119+         defaultJvmOperationParserOptions  = assertNotNull (options );
120+     }
121+ 
122+     /** 
123+      * By default, for SDL parsing, the Parser will not capture ignored characters, but it will capture line comments into AST 
124+      * elements.  The SDL default options allow unlimited tokens and whitespace, since a DOS attack vector is 
125+      * not commonly available via schema SDL parsing. 
126+      * 
127+      * A static holds this default value for SDL parsing in a JVM wide basis options object. 
128+      * 
129+      * @return the static default JVM value for SDL parsing 
130+      * 
131+      * @see graphql.language.IgnoredChar 
132+      * @see graphql.language.SourceLocation 
133+      * @see graphql.schema.idl.SchemaParser 
134+      */ 
135+     public  static  ParserOptions  getDefaultSdlParserOptions () {
136+         return  defaultJvmSdlParserOptions ;
137+     }
138+ 
139+     /** 
140+      * By default, for SDL parsing, the Parser will not capture ignored characters, but it will capture line comments into AST 
141+      * elements .  A static holds this default value for operation parsing in a JVM wide basis options object. 
142+      * 
143+      * This static can be set to true to allow the behavior of version 16.x or before. 
144+      * 
145+      * @param options - the new default JVM parser options for operation parsing 
146+      * 
147+      * @see graphql.language.IgnoredChar 
148+      * @see graphql.language.SourceLocation 
149+      */ 
150+     public  static  void  setDefaultSdlParserOptions (ParserOptions  options ) {
151+         defaultJvmSdlParserOptions  = assertNotNull (options );
152+     }
153+ 
68154    private  final  boolean  captureIgnoredChars ;
69155    private  final  boolean  captureSourceLocation ;
70156    private  final  boolean  captureLineComments ;
71157    private  final  int  maxTokens ;
158+     private  final  int  maxWhitespaceTokens ;
72159    private  final  ParsingListener  parsingListener ;
73160
74161    private  ParserOptions (Builder  builder ) {
75162        this .captureIgnoredChars  = builder .captureIgnoredChars ;
76163        this .captureSourceLocation  = builder .captureSourceLocation ;
77164        this .captureLineComments  = builder .captureLineComments ;
78165        this .maxTokens  = builder .maxTokens ;
166+         this .maxWhitespaceTokens  = builder .maxWhitespaceTokens ;
79167        this .parsingListener  = builder .parsingListener ;
80168    }
81169
@@ -117,7 +205,7 @@ public boolean isCaptureLineComments() {
117205    }
118206
119207    /** 
120-      * A graphql hacking vector is to send nonsensical queries that burn lots of parsing CPU time and burn  
208+      * A graphql hacking vector is to send nonsensical queries that burn lots of parsing CPU time and burns  
121209     * memory representing a document that won't ever execute.  To prevent this you can set a maximum number of parse 
122210     * tokens that will be accepted before an exception is thrown and the parsing is stopped. 
123211     * 
@@ -127,6 +215,17 @@ public int getMaxTokens() {
127215        return  maxTokens ;
128216    }
129217
218+     /** 
219+      * A graphql hacking vector is to send larges amounts of whitespace that burn lots of parsing CPU time and burn 
220+      * memory representing a document.  To prevent this you can set a maximum number of whitespace parse 
221+      * tokens that will be accepted before an exception is thrown and the parsing is stopped. 
222+      * 
223+      * @return the maximum number of raw whitespace tokens the parser will accept, after which an exception will be thrown. 
224+      */ 
225+     public  int  getMaxWhitespaceTokens () {
226+         return  maxWhitespaceTokens ;
227+     }
228+ 
130229    public  ParsingListener  getParsingListener () {
131230        return  parsingListener ;
132231    }
@@ -148,6 +247,7 @@ public static class Builder {
148247        private  boolean  captureLineComments  = true ;
149248        private  int  maxTokens  = MAX_QUERY_TOKENS ;
150249        private  ParsingListener  parsingListener  = ParsingListener .NOOP ;
250+         private  int  maxWhitespaceTokens  = MAX_WHITESPACE_TOKENS ;
151251
152252        Builder () {
153253        }
@@ -157,6 +257,7 @@ public static class Builder {
157257            this .captureSourceLocation  = parserOptions .captureSourceLocation ;
158258            this .captureLineComments  = parserOptions .captureLineComments ;
159259            this .maxTokens  = parserOptions .maxTokens ;
260+             this .maxWhitespaceTokens  = parserOptions .maxWhitespaceTokens ;
160261            this .parsingListener  = parserOptions .parsingListener ;
161262        }
162263
@@ -180,6 +281,11 @@ public Builder maxTokens(int maxTokens) {
180281            return  this ;
181282        }
182283
284+         public  Builder  maxWhitespaceTokens (int  maxWhitespaceTokens ) {
285+             this .maxWhitespaceTokens  = maxWhitespaceTokens ;
286+             return  this ;
287+         }
288+ 
183289        public  Builder  parsingListener (ParsingListener  parsingListener ) {
184290            this .parsingListener  = assertNotNull (parsingListener );
185291            return  this ;
0 commit comments