Make the lexer and parser safe
Some checks failed
Build / audit (push) Has been cancelled
Build / test (push) Has been cancelled
Build / doc (push) Has been cancelled

This commit is contained in:
Eugen Wissner 2025-01-18 16:33:13 +01:00
parent 324a4c55ff
commit 663e4f3521
Signed by: belka
GPG Key ID: A27FDC1E8EE902C0
11 changed files with 22 additions and 23 deletions

View File

@ -11,7 +11,7 @@ author: Danny Navarro <j@dannynavarro.net>,
Matthías Páll Gissurarson <mpg@mpg.is>, Matthías Páll Gissurarson <mpg@mpg.is>,
Sólrún Halla Einarsdóttir <she@mpg.is> Sólrún Halla Einarsdóttir <she@mpg.is>
maintainer: belka@caraus.de maintainer: belka@caraus.de
copyright: (c) 2019-2024 Eugen Wissner, copyright: (c) 2019-2025 Eugen Wissner,
(c) 2015-2017 J. Daniel Navarro (c) 2015-2017 J. Daniel Navarro
license: MPL-2.0 AND BSD-3-Clause license: MPL-2.0 AND BSD-3-Clause
license-files: LICENSE, license-files: LICENSE,
@ -21,7 +21,7 @@ extra-source-files:
CHANGELOG.md CHANGELOG.md
README.md README.md
tested-with: tested-with:
GHC == 9.8.2 GHC == 9.10.1
source-repository head source-repository head
type: git type: git

View File

@ -1,3 +1,4 @@
{-# LANGUAGE NoGeneralizedNewtypeDeriving #-}
{-# LANGUAGE Safe #-} {-# LANGUAGE Safe #-}
-- | Target AST for parser. -- | Target AST for parser.

View File

@ -1,3 +1,4 @@
{-# LANGUAGE NoGeneralizedNewtypeDeriving #-}
{-# LANGUAGE Safe #-} {-# LANGUAGE Safe #-}
-- | Various parts of a GraphQL document can be annotated with directives. -- | Various parts of a GraphQL document can be annotated with directives.

View File

@ -3,6 +3,7 @@
{-# LANGUAGE NamedFieldPuns #-} {-# LANGUAGE NamedFieldPuns #-}
{-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE RecordWildCards #-} {-# LANGUAGE RecordWildCards #-}
{-# LANGUAGE NoGeneralizedNewtypeDeriving #-}
{-# LANGUAGE Safe #-} {-# LANGUAGE Safe #-}
-- | This module defines an abstract syntax tree for the @GraphQL@ language. It -- | This module defines an abstract syntax tree for the @GraphQL@ language. It

View File

@ -2,6 +2,7 @@
{-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE LambdaCase #-} {-# LANGUAGE LambdaCase #-}
{-# LANGUAGE RecordWildCards #-} {-# LANGUAGE RecordWildCards #-}
{-# LANGUAGE NoGeneralizedNewtypeDeriving #-}
{-# LANGUAGE Safe #-} {-# LANGUAGE Safe #-}
-- | This module defines a minifier and a printer for the @GraphQL@ language. -- | This module defines a minifier and a printer for the @GraphQL@ language.

View File

@ -1,5 +1,7 @@
{-# LANGUAGE ExplicitForAll #-} {-# LANGUAGE ExplicitForAll #-}
{-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE NoGeneralizedNewtypeDeriving #-}
{-# LANGUAGE Safe #-}
-- | This module defines a bunch of small parsers used to parse individual -- | This module defines a bunch of small parsers used to parse individual
-- lexemes. -- lexemes.

View File

@ -2,6 +2,8 @@
{-# LANGUAGE LambdaCase #-} {-# LANGUAGE LambdaCase #-}
{-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE RecordWildCards #-} {-# LANGUAGE RecordWildCards #-}
{-# LANGUAGE NoGeneralizedNewtypeDeriving #-}
{-# LANGUAGE Safe #-}
-- | @GraphQL@ document parser. -- | @GraphQL@ document parser.
module Language.GraphQL.AST.Parser module Language.GraphQL.AST.Parser

View File

@ -189,6 +189,8 @@ data QueryError
| CoercionError Full.VariableDefinition | CoercionError Full.VariableDefinition
| UnknownInputType Full.VariableDefinition | UnknownInputType Full.VariableDefinition
type ExecuteHandler m a e = e -> ExecutorT m a
tell :: Monad m => Seq Error -> ExecutorT m () tell :: Monad m => Seq Error -> ExecutorT m ()
tell = ExecutorT . lift . Writer.tell tell = ExecutorT . lift . Writer.tell
@ -313,8 +315,7 @@ executeQuery topSelections schema = do
pure $ Response data' errors pure $ Response data' errors
handleException :: (MonadCatch m, Serialize a) handleException :: (MonadCatch m, Serialize a)
=> FieldException => ExecuteHandler m a FieldException
-> ExecutorT m a
handleException (FieldException fieldLocation errorPath next) = handleException (FieldException fieldLocation errorPath next) =
let newError = constructError next fieldLocation errorPath let newError = constructError next fieldLocation errorPath
in tell (Seq.singleton newError) >> pure null in tell (Seq.singleton newError) >> pure null
@ -390,30 +391,28 @@ executeField objectValue fields (viewResolver -> resolverPair) errorPath =
fieldErrorPath = fieldsSegment fields : errorPath fieldErrorPath = fieldsSegment fields : errorPath
inputCoercionHandler :: (MonadCatch m, Serialize a) inputCoercionHandler :: (MonadCatch m, Serialize a)
=> Full.Location => Full.Location
-> InputCoercionException -> ExecuteHandler m a InputCoercionException
-> ExecutorT m a
inputCoercionHandler _ e@(InputCoercionException _ _ (Just valueNode)) = inputCoercionHandler _ e@(InputCoercionException _ _ (Just valueNode)) =
let argumentLocation = getField @"location" valueNode let argumentLocation = getField @"location" valueNode
in exceptionHandler argumentLocation e in exceptionHandler argumentLocation e
inputCoercionHandler fieldLocation e = exceptionHandler fieldLocation e inputCoercionHandler fieldLocation e = exceptionHandler fieldLocation e
resultHandler :: (MonadCatch m, Serialize a) resultHandler :: (MonadCatch m, Serialize a)
=> Full.Location => Full.Location
-> ResultException -> ExecuteHandler m a ResultException
-> ExecutorT m a
resultHandler = exceptionHandler resultHandler = exceptionHandler
resolverHandler :: (MonadCatch m, Serialize a) resolverHandler :: (MonadCatch m, Serialize a)
=> Full.Location => Full.Location
-> ResolverException -> ExecuteHandler m a ResolverException
-> ExecutorT m a
resolverHandler = exceptionHandler resolverHandler = exceptionHandler
nullResultHandler :: (MonadCatch m, Serialize a) nullResultHandler :: (MonadCatch m, Serialize a) => ExecuteHandler m a FieldException
=> FieldException
-> ExecutorT m a
nullResultHandler e@(FieldException fieldLocation errorPath' next) = nullResultHandler e@(FieldException fieldLocation errorPath' next) =
let newError = constructError next fieldLocation errorPath' let newError = constructError next fieldLocation errorPath'
in if Out.isNonNullType fieldType in if Out.isNonNullType fieldType
then throwM e then throwM e
else returnError newError else returnError newError
exceptionHandler :: (Exception e, MonadCatch m, Serialize a)
=> Full.Location
-> ExecuteHandler m a e
exceptionHandler errorLocation e = exceptionHandler errorLocation e =
let newError = constructError e errorLocation fieldErrorPath let newError = constructError e errorLocation fieldErrorPath
in if Out.isNonNullType fieldType in if Out.isNonNullType fieldType

View File

@ -3,6 +3,7 @@
obtain one at https://mozilla.org/MPL/2.0/. -} obtain one at https://mozilla.org/MPL/2.0/. -}
{-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE NoGeneralizedNewtypeDeriving #-}
{-# LANGUAGE Safe #-} {-# LANGUAGE Safe #-}
-- | Types that can be used as both input and output types. -- | Types that can be used as both input and output types.

View File

@ -3,6 +3,7 @@
obtain one at https://mozilla.org/MPL/2.0/. -} obtain one at https://mozilla.org/MPL/2.0/. -}
{-# LANGUAGE PatternSynonyms #-} {-# LANGUAGE PatternSynonyms #-}
{-# LANGUAGE NoGeneralizedNewtypeDeriving #-}
{-# LANGUAGE Safe #-} {-# LANGUAGE Safe #-}
{-# LANGUAGE ViewPatterns #-} {-# LANGUAGE ViewPatterns #-}

View File

@ -1067,18 +1067,12 @@ overlappingFieldsCanBeMergedRule = OperationDefinitionRule $ \case
go selectionSet selectionType = do go selectionSet selectionType = do
fieldTuples <- evalStateT (collectFields selectionType selectionSet) HashSet.empty fieldTuples <- evalStateT (collectFields selectionType selectionSet) HashSet.empty
fieldsInSetCanMerge fieldTuples fieldsInSetCanMerge fieldTuples
fieldsInSetCanMerge :: forall m
. HashMap Full.Name (NonEmpty (Full.Field, Type.CompositeType m))
-> ReaderT (Validation m) Seq Error
fieldsInSetCanMerge fieldTuples = do fieldsInSetCanMerge fieldTuples = do
validation <- ask validation <- ask
let (lonely, paired) = flattenPairs fieldTuples let (lonely, paired) = flattenPairs fieldTuples
let reader = flip runReaderT validation let reader = flip runReaderT validation
lift $ foldMap (reader . visitLonelyFields) lonely lift $ foldMap (reader . visitLonelyFields) lonely
<> foldMap (reader . forEachFieldTuple) paired <> foldMap (reader . forEachFieldTuple) paired
forEachFieldTuple :: forall m
. (FieldInfo m, FieldInfo m)
-> ReaderT (Validation m) Seq Error
forEachFieldTuple (fieldA, fieldB) = forEachFieldTuple (fieldA, fieldB) =
case (parent fieldA, parent fieldB) of case (parent fieldA, parent fieldB) of
(parentA@Type.CompositeObjectType{}, parentB@Type.CompositeObjectType{}) (parentA@Type.CompositeObjectType{}, parentB@Type.CompositeObjectType{})
@ -1105,10 +1099,6 @@ overlappingFieldsCanBeMergedRule = OperationDefinitionRule $ \case
let Full.Field _ _ _ _ subSelections _ = node let Full.Field _ _ _ _ subSelections _ = node
compositeFieldType = Type.outToComposite type' compositeFieldType = Type.outToComposite type'
in maybe (lift Seq.empty) (go subSelections) compositeFieldType in maybe (lift Seq.empty) (go subSelections) compositeFieldType
sameResponseShape :: forall m
. FieldInfo m
-> FieldInfo m
-> ReaderT (Validation m) Seq Error
sameResponseShape fieldA fieldB = sameResponseShape fieldA fieldB =
let Full.Field _ _ _ _ selectionsA _ = node fieldA let Full.Field _ _ _ _ selectionsA _ = node fieldA
Full.Field _ _ _ _ selectionsB _ = node fieldB Full.Field _ _ _ _ selectionsB _ = node fieldB