1414import graphql .parser .antlr .GraphqlParser ;
1515import graphql .parser .exceptions .ParseCancelledException ;
1616import graphql .parser .exceptions .ParseCancelledTooDeepException ;
17+ import graphql .parser .exceptions .ParseCancelledTooManyCharsException ;
1718import org .antlr .v4 .runtime .BaseErrorListener ;
1819import org .antlr .v4 .runtime .CharStreams ;
1920import org .antlr .v4 .runtime .CodePointCharStream ;
2526import org .antlr .v4 .runtime .atn .PredictionMode ;
2627import org .antlr .v4 .runtime .tree .ParseTreeListener ;
2728import org .antlr .v4 .runtime .tree .TerminalNode ;
29+ import org .jetbrains .annotations .NotNull ;
2830
2931import java .io .IOException ;
3032import java .io .Reader ;
3335import java .util .Optional ;
3436import java .util .function .BiConsumer ;
3537import java .util .function .BiFunction ;
38+ import java .util .function .Consumer ;
3639
3740/**
3841 * This can parse graphql syntax, both Query syntax and Schema Definition Language (SDL) syntax, into an
@@ -259,6 +262,57 @@ private Node<?> parseImpl(ParserEnvironment environment, BiFunction<GraphqlParse
259262 ParserOptions parserOptions = environment .getParserOptions ();
260263 parserOptions = Optional .ofNullable (parserOptions ).orElse (ParserOptions .getDefaultParserOptions ());
261264
265+ MultiSourceReader multiSourceReader = setupMultiSourceReader (environment , parserOptions );
266+
267+ SafeTokenReader safeTokenReader = setupSafeTokenReader (environment , parserOptions , multiSourceReader );
268+
269+ CodePointCharStream charStream = setupCharStream (safeTokenReader );
270+
271+ GraphqlLexer lexer = setupGraphqlLexer (environment , multiSourceReader , charStream );
272+
273+ // this lexer wrapper allows us to stop lexing when too many tokens are in place. This prevents DOS attacks.
274+ SafeTokenSource safeTokenSource = getSafeTokenSource (environment , parserOptions , multiSourceReader , lexer );
275+
276+ CommonTokenStream tokens = new CommonTokenStream (safeTokenSource );
277+
278+ GraphqlParser parser = new GraphqlParser (tokens );
279+ parser .removeErrorListeners ();
280+ parser .getInterpreter ().setPredictionMode (PredictionMode .SLL );
281+
282+ ExtendedBailStrategy bailStrategy = new ExtendedBailStrategy (multiSourceReader , environment );
283+ parser .setErrorHandler (bailStrategy );
284+
285+ // preserve old protected call semantics - remove at some point
286+ GraphqlAntlrToLanguage toLanguage = getAntlrToLanguage (tokens , multiSourceReader , environment );
287+
288+ setupParserListener (environment , multiSourceReader , parser , toLanguage );
289+
290+
291+ //
292+ // parsing starts ...... now!
293+ //
294+ Object [] contextAndNode = nodeFunction .apply (parser , toLanguage );
295+ ParserRuleContext parserRuleContext = (ParserRuleContext ) contextAndNode [0 ];
296+ Node <?> node = (Node <?>) contextAndNode [1 ];
297+
298+ Token stop = parserRuleContext .getStop ();
299+ List <Token > allTokens = tokens .getTokens ();
300+ if (stop != null && allTokens != null && !allTokens .isEmpty ()) {
301+ Token last = allTokens .get (allTokens .size () - 1 );
302+ //
303+ // do we have more tokens in the stream than we consumed in the parse?
304+ // if yes then it's invalid. We make sure it's the same channel
305+ boolean notEOF = last .getType () != Token .EOF ;
306+ boolean lastGreaterThanDocument = last .getTokenIndex () > stop .getTokenIndex ();
307+ boolean sameChannel = last .getChannel () == stop .getChannel ();
308+ if (notEOF && lastGreaterThanDocument && sameChannel ) {
309+ throw bailStrategy .mkMoreTokensException (last );
310+ }
311+ }
312+ return node ;
313+ }
314+
315+ private static MultiSourceReader setupMultiSourceReader (ParserEnvironment environment , ParserOptions parserOptions ) {
262316 MultiSourceReader multiSourceReader ;
263317 Reader reader = environment .getDocument ();
264318 if (reader instanceof MultiSourceReader ) {
@@ -269,13 +323,31 @@ private Node<?> parseImpl(ParserEnvironment environment, BiFunction<GraphqlParse
269323 .trackData (parserOptions .isReaderTrackData ())
270324 .build ();
271325 }
326+ return multiSourceReader ;
327+ }
328+
329+ @ NotNull
330+ private static SafeTokenReader setupSafeTokenReader (ParserEnvironment environment , ParserOptions parserOptions , MultiSourceReader multiSourceReader ) {
331+ int maxCharacters = parserOptions .getMaxCharacters ();
332+ Consumer <Integer > onTooManyCharacters = it -> {
333+ throw new ParseCancelledTooManyCharsException (environment .getI18N (), maxCharacters );
334+ };
335+ return new SafeTokenReader (multiSourceReader , maxCharacters , onTooManyCharacters );
336+ }
337+
338+ @ NotNull
339+ private static CodePointCharStream setupCharStream (SafeTokenReader safeTokenReader ) {
272340 CodePointCharStream charStream ;
273341 try {
274- charStream = CharStreams .fromReader (multiSourceReader );
342+ charStream = CharStreams .fromReader (safeTokenReader );
275343 } catch (IOException e ) {
276344 throw new UncheckedIOException (e );
277345 }
346+ return charStream ;
347+ }
278348
349+ @ NotNull
350+ private static GraphqlLexer setupGraphqlLexer (ParserEnvironment environment , MultiSourceReader multiSourceReader , CodePointCharStream charStream ) {
279351 GraphqlLexer lexer = new GraphqlLexer (charStream );
280352 lexer .removeErrorListeners ();
281353 lexer .addErrorListener (new BaseErrorListener () {
@@ -296,8 +368,11 @@ public void syntaxError(Recognizer<?, ?> recognizer, Object offendingSymbol, int
296368 throw new InvalidSyntaxException (msg , sourceLocation , null , preview , null );
297369 }
298370 });
371+ return lexer ;
372+ }
299373
300- // this lexer wrapper allows us to stop lexing when too many tokens are in place. This prevents DOS attacks.
374+ @ NotNull
375+ private SafeTokenSource getSafeTokenSource (ParserEnvironment environment , ParserOptions parserOptions , MultiSourceReader multiSourceReader , GraphqlLexer lexer ) {
301376 int maxTokens = parserOptions .getMaxTokens ();
302377 int maxWhitespaceTokens = parserOptions .getMaxWhitespaceTokens ();
303378 BiConsumer <Integer , Token > onTooManyTokens = (maxTokenCount , token ) -> throwIfTokenProblems (
@@ -306,45 +381,7 @@ public void syntaxError(Recognizer<?, ?> recognizer, Object offendingSymbol, int
306381 maxTokenCount ,
307382 multiSourceReader ,
308383 ParseCancelledException .class );
309- SafeTokenSource safeTokenSource = new SafeTokenSource (lexer , maxTokens , maxWhitespaceTokens , onTooManyTokens );
310-
311- CommonTokenStream tokens = new CommonTokenStream (safeTokenSource );
312-
313- GraphqlParser parser = new GraphqlParser (tokens );
314- parser .removeErrorListeners ();
315- parser .getInterpreter ().setPredictionMode (PredictionMode .SLL );
316-
317- ExtendedBailStrategy bailStrategy = new ExtendedBailStrategy (multiSourceReader , environment );
318- parser .setErrorHandler (bailStrategy );
319-
320- // preserve old protected call semantics - remove at some point
321- GraphqlAntlrToLanguage toLanguage = getAntlrToLanguage (tokens , multiSourceReader , environment );
322-
323- setupParserListener (environment , multiSourceReader , parser , toLanguage );
324-
325-
326- //
327- // parsing starts ...... now!
328- //
329- Object [] contextAndNode = nodeFunction .apply (parser , toLanguage );
330- ParserRuleContext parserRuleContext = (ParserRuleContext ) contextAndNode [0 ];
331- Node <?> node = (Node <?>) contextAndNode [1 ];
332-
333- Token stop = parserRuleContext .getStop ();
334- List <Token > allTokens = tokens .getTokens ();
335- if (stop != null && allTokens != null && !allTokens .isEmpty ()) {
336- Token last = allTokens .get (allTokens .size () - 1 );
337- //
338- // do we have more tokens in the stream than we consumed in the parse?
339- // if yes then it's invalid. We make sure it's the same channel
340- boolean notEOF = last .getType () != Token .EOF ;
341- boolean lastGreaterThanDocument = last .getTokenIndex () > stop .getTokenIndex ();
342- boolean sameChannel = last .getChannel () == stop .getChannel ();
343- if (notEOF && lastGreaterThanDocument && sameChannel ) {
344- throw bailStrategy .mkMoreTokensException (last );
345- }
346- }
347- return node ;
384+ return new SafeTokenSource (lexer , maxTokens , maxWhitespaceTokens , onTooManyTokens );
348385 }
349386
350387 private void setupParserListener (ParserEnvironment environment , MultiSourceReader multiSourceReader , GraphqlParser parser , GraphqlAntlrToLanguage toLanguage ) {
0 commit comments