1313#include < memory>
1414#include < numeric>
1515#include < optional>
16+ #include < sstream>
1617#include < tuple>
1718#include < utility>
1819
@@ -659,6 +660,56 @@ struct executable_selector<type_condition> : std::true_type
659660{
660661};
661662
663+ template <typename Rule>
664+ struct ast_action : nothing<Rule>
665+ {
666+ };
667+
668+ struct [[nodiscard]] depth_guard
669+ {
670+ explicit depth_guard (size_t & depth) noexcept
671+ : _depth (depth)
672+ {
673+ ++_depth;
674+ }
675+
676+ ~depth_guard ()
677+ {
678+ --_depth;
679+ }
680+
681+ depth_guard (depth_guard&&) noexcept = delete ;
682+ depth_guard (const depth_guard&) = delete ;
683+
684+ depth_guard& operator =(depth_guard&&) noexcept = delete ;
685+ depth_guard& operator =(const depth_guard&) = delete ;
686+
687+ private:
688+ size_t & _depth;
689+ };
690+
691+ template <>
692+ struct ast_action <selection_set> : maybe_nothing
693+ {
694+ template <typename Rule, apply_mode A, rewind_mode M, template <typename ...> class Action ,
695+ template <typename ...> class Control , typename ParseInput, typename ... States>
696+ [[nodiscard]] static bool match (ParseInput& in, States&&... st)
697+ {
698+ depth_guard guard (in.selectionSetDepth );
699+ if (in.selectionSetDepth > in.depthLimit ())
700+ {
701+ std::ostringstream oss;
702+
703+ oss << " Exceeded " << in.depthLimit ()
704+ << " nested depth limit for https://spec.graphql.org/October2021/#SelectionSet" ;
705+
706+ throw parse_error (oss.str (), in);
707+ }
708+
709+ return tao::graphqlpeg::template match<Rule, A, M, Action, Control>(in, st...);
710+ }
711+ };
712+
662713template <typename Rule>
663714struct ast_control : normal<Rule>
664715{
@@ -843,7 +894,7 @@ template <>
843894const char * ast_control<schema_document_content>::error_message =
844895 " Expected schema type https://spec.graphql.org/October2021/#Document" ;
845896
846- ast parseSchemaString (std::string_view input)
897+ ast parseSchemaString (std::string_view input, size_t depthLimit )
847898{
848899 ast result { std::make_shared<ast_input>(
849900 ast_input { std::vector<char > { input.cbegin (), input.cend () } }),
@@ -854,55 +905,55 @@ ast parseSchemaString(std::string_view input)
854905 {
855906 // Try a smaller grammar with only schema type definitions first.
856907 result.root =
857- parse_tree::parse<schema_document, ast_node, schema_selector, nothing , ast_control>(
858- memory_input<>( data.data (), data.size (), " GraphQL" ));
908+ parse_tree::parse<schema_document, ast_node, schema_selector, ast_action , ast_control>(
909+ ast_memory (depthLimit, data.data (), data.size (), " GraphQL" s ));
859910 }
860911 catch (const peg::parse_error&)
861912 {
862913 // Try again with the full document grammar so validation can handle the unexepected
863914 // executable definitions if this is a mixed document.
864915 result.root =
865- parse_tree::parse<mixed_document, ast_node, schema_selector, nothing , ast_control>(
866- memory_input<>( data.data (), data.size (), " GraphQL" ));
916+ parse_tree::parse<mixed_document, ast_node, schema_selector, ast_action , ast_control>(
917+ ast_memory (depthLimit, data.data (), data.size (), " GraphQL" s ));
867918 }
868919
869920 return result;
870921}
871922
872- ast parseSchemaFile (std::string_view filename)
923+ ast parseSchemaFile (std::string_view filename, size_t depthLimit )
873924{
874925 ast result;
875926
876927 try
877928 {
878- result.input =
879- std::make_shared< ast_input>(ast_input { std::make_unique<file_input<>>( filename) });
929+ result.input = std::make_shared<ast_input>(
930+ ast_input { std::make_unique<ast_file>(depthLimit, filename) });
880931
881- auto & in = *std::get<std::unique_ptr<file_input<> >>(result.input ->data );
932+ auto & in = *std::get<std::unique_ptr<ast_file >>(result.input ->data );
882933
883934 // Try a smaller grammar with only schema type definitions first.
884935 result.root =
885- parse_tree::parse<schema_document, ast_node, schema_selector, nothing , ast_control>(
936+ parse_tree::parse<schema_document, ast_node, schema_selector, ast_action , ast_control>(
886937 std::move (in));
887938 }
888939 catch (const peg::parse_error&)
889940 {
890- result.input =
891- std::make_shared< ast_input>(ast_input { std::make_unique<file_input<>>( filename) });
941+ result.input = std::make_shared<ast_input>(
942+ ast_input { std::make_unique<ast_file>(depthLimit, filename) });
892943
893- auto & in = *std::get<std::unique_ptr<file_input<> >>(result.input ->data );
944+ auto & in = *std::get<std::unique_ptr<ast_file >>(result.input ->data );
894945
895946 // Try again with the full document grammar so validation can handle the unexepected
896947 // executable definitions if this is a mixed document.
897948 result.root =
898- parse_tree::parse<mixed_document, ast_node, schema_selector, nothing , ast_control>(
949+ parse_tree::parse<mixed_document, ast_node, schema_selector, ast_action , ast_control>(
899950 std::move (in));
900951 }
901952
902953 return result;
903954}
904955
905- ast parseString (std::string_view input)
956+ ast parseString (std::string_view input, size_t depthLimit )
906957{
907958 ast result { std::make_shared<ast_input>(
908959 ast_input { std::vector<char > { input.cbegin (), input.cend () } }),
@@ -913,48 +964,48 @@ ast parseString(std::string_view input)
913964 {
914965 // Try a smaller grammar with only executable definitions first.
915966 result.root = parse_tree::
916- parse<executable_document, ast_node, executable_selector, nothing , ast_control>(
917- memory_input<>( data.data (), data.size (), " GraphQL" ));
967+ parse<executable_document, ast_node, executable_selector, ast_action , ast_control>(
968+ ast_memory (depthLimit, data.data (), data.size (), " GraphQL" s ));
918969 }
919970 catch (const peg::parse_error&)
920971 {
921972 // Try again with the full document grammar so validation can handle the unexepected type
922973 // definitions if this is a mixed document.
923- result.root =
924- parse_tree:: parse<mixed_document, ast_node, executable_selector, nothing , ast_control>(
925- memory_input<>( data.data (), data.size (), " GraphQL" ));
974+ result.root = parse_tree::
975+ parse<mixed_document, ast_node, executable_selector, ast_action , ast_control>(
976+ ast_memory (depthLimit, data.data (), data.size (), " GraphQL" s ));
926977 }
927978
928979 return result;
929980}
930981
931- ast parseFile (std::string_view filename)
982+ ast parseFile (std::string_view filename, size_t depthLimit )
932983{
933984 ast result;
934985
935986 try
936987 {
937- result.input =
938- std::make_shared< ast_input>(ast_input { std::make_unique<file_input<>>( filename) });
988+ result.input = std::make_shared<ast_input>(
989+ ast_input { std::make_unique<ast_file>(depthLimit, filename) });
939990
940- auto & in = *std::get<std::unique_ptr<file_input<> >>(result.input ->data );
991+ auto & in = *std::get<std::unique_ptr<ast_file >>(result.input ->data );
941992
942993 // Try a smaller grammar with only executable definitions first.
943994 result.root = parse_tree::
944- parse<executable_document, ast_node, executable_selector, nothing , ast_control>(
995+ parse<executable_document, ast_node, executable_selector, ast_action , ast_control>(
945996 std::move (in));
946997 }
947998 catch (const peg::parse_error&)
948999 {
949- result.input =
950- std::make_shared< ast_input>(ast_input { std::make_unique<file_input<>>( filename) });
1000+ result.input = std::make_shared<ast_input>(
1001+ ast_input { std::make_unique<ast_file>(depthLimit, filename) });
9511002
952- auto & in = *std::get<std::unique_ptr<file_input<> >>(result.input ->data );
1003+ auto & in = *std::get<std::unique_ptr<ast_file >>(result.input ->data );
9531004
9541005 // Try again with the full document grammar so validation can handle the unexepected type
9551006 // definitions if this is a mixed document.
956- result.root =
957- parse_tree:: parse<mixed_document, ast_node, executable_selector, nothing , ast_control>(
1007+ result.root = parse_tree::
1008+ parse<mixed_document, ast_node, executable_selector, ast_action , ast_control>(
9581009 std::move (in));
9591010 }
9601011
@@ -976,7 +1027,7 @@ peg::ast operator"" _graphql(const char* text, size_t size)
9761027 peg::ast_node,
9771028 peg::executable_selector,
9781029 peg::nothing,
979- peg::ast_control>(peg::memory_input<>(text, size, " GraphQL" ));
1030+ peg::ast_control>(peg::memory_input<>(text, size, " GraphQL" s ));
9801031 }
9811032 catch (const peg::parse_error&)
9821033 {
@@ -986,7 +1037,7 @@ peg::ast operator"" _graphql(const char* text, size_t size)
9861037 peg::ast_node,
9871038 peg::executable_selector,
9881039 peg::nothing,
989- peg::ast_control>(peg::memory_input<>(text, size, " GraphQL" ));
1040+ peg::ast_control>(peg::memory_input<>(text, size, " GraphQL" s ));
9901041 }
9911042
9921043 return result;
0 commit comments