@@ -85,7 +85,6 @@ import Data.Aeson.Internal.Functions (mapKey)
85import Data.Aeson.Parser.Internal (eitherDecodeWith, jsonEOF)
85import Data.Aeson.Parser.Internal (eitherDecodeWith, jsonEOF)
86import Data.Aeson.Types.Generic
86import Data.Aeson.Types.Generic
87import Data.Aeson.Types.Internal
87import Data.Aeson.Types.Internal
88−import Data.Attoparsec.Number (Number(..))
89import Data.Bits (unsafeShiftR)
88import Data.Bits (unsafeShiftR)
90import Data.Fixed (Fixed, HasResolution)
89import Data.Fixed (Fixed, HasResolution)
91import Data.Functor.Compose (Compose(..))
90import Data.Functor.Compose (Compose(..))
@@ -99,7 +98,7 @@ import Data.Maybe (fromMaybe)
99import Data.Semigroup ((<>))
98import Data.Semigroup ((<>))
100import Data.Proxy (Proxy(..))
99import Data.Proxy (Proxy(..))
101import Data.Ratio ((%), Ratio)
100import Data.Ratio ((%), Ratio)
102−import Data.Scientific (Scientific)
101+import Data.Scientific (Scientific, base10Exponent)
103import Data.Tagged (Tagged(..))
102import Data.Tagged (Tagged(..))
104import Data.Text (Text, pack, unpack)
103import Data.Text (Text, pack, unpack)
105import Data.Time (Day, DiffTime, LocalTime, NominalDiffTime, TimeOfDay, UTCTime, ZonedTime)
104import Data.Time (Day, DiffTime, LocalTime, NominalDiffTime, TimeOfDay, UTCTime, ZonedTime)
@@ -172,15 +171,6 @@ parseIndexedJSONPair keyParser valParser idx value = p value <?> Index idx
172parseJSONElemAtIndex :: (Value -> Parser a) -> Int -> V.Vector Value -> Parser a
171parseJSONElemAtIndex :: (Value -> Parser a) -> Int -> V.Vector Value -> Parser a
173parseJSONElemAtIndex p idx ary = p (V.unsafeIndex ary idx) <?> Index idx
172parseJSONElemAtIndex p idx ary = p (V.unsafeIndex ary idx) <?> Index idx
175−scientificToNumber :: Scientific -> Number
177− | e < 0 = D $ Scientific.toRealFloat s
178− | otherwise = I $ c * 10 ^ e
180− e = Scientific.base10Exponent s
181− c = Scientific.coefficient s
182−{-# INLINE scientificToNumber #-}
184parseRealFloat :: RealFloat a => String -> Value -> Parser a
174parseRealFloat :: RealFloat a => String -> Value -> Parser a
185parseRealFloat _ (Number s) = pure $ Scientific.toRealFloat s
175parseRealFloat _ (Number s) = pure $ Scientific.toRealFloat s
186parseRealFloat _ Null = pure (0/0)
176parseRealFloat _ Null = pure (0/0)
@@ -196,7 +186,7 @@ parseIntegralFromScientific expected s =
197parseIntegral :: Integral a => String -> Value -> Parser a
187parseIntegral :: Integral a => String -> Value -> Parser a
198parseIntegral expected =
188parseIntegral expected =
199− withScientific expected $ parseIntegralFromScientific expected
189+ withBoundedScientific expected $ parseIntegralFromScientific expected
200{-# INLINE parseIntegral #-}
190{-# INLINE parseIntegral #-}
202parseBoundedIntegralFromScientific :: (Bounded a, Integral a) => String -> Scientific -> Parser a
192parseBoundedIntegralFromScientific :: (Bounded a, Integral a) => String -> Scientific -> Parser a
@@ -218,8 +208,13 @@ parseScientificText
218 . T.encodeUtf8
208 . T.encodeUtf8
220parseIntegralText :: Integral a => String -> Text -> Parser a
210parseIntegralText :: Integral a => String -> Text -> Parser a
221−parseIntegralText expected t =
211+parseIntegralText expected t
212+ = parseScientificText t
213+ >>= rejectLargeExponent
222− parseScientificText t >>= parseIntegralFromScientific expected
214+ >>= parseIntegralFromScientific expected
216+ rejectLargeExponent :: Scientific -> Parser Scientific
217+ rejectLargeExponent s = withBoundedScientific expected pure (Number s)
223{-# INLINE parseIntegralText #-}
218{-# INLINE parseIntegralText #-}
225parseBoundedIntegralText :: (Bounded a, Integral a) => String -> Text -> Parser a
220parseBoundedIntegralText :: (Bounded a, Integral a) => String -> Text -> Parser a
@@ -648,11 +643,30 @@ withArray expected _ v = typeMismatch expected v
648-- | @'withScientific' expected f value@ applies @f@ to the 'Scientific' number
643-- | @'withScientific' expected f value@ applies @f@ to the 'Scientific' number
649-- when @value@ is a 'Number' and fails using @'typeMismatch' expected@
644-- when @value@ is a 'Number' and fails using @'typeMismatch' expected@
650-- otherwise.
645-- otherwise.
647+-- /Warning/: If you are converting from a scientific to an unbounded
648+-- type such as 'Integer' you may want to add a restriction on the
649+-- size of the exponent (see 'withBoundedScientific') to prevent
650+-- malicious input from filling up the memory of the target system.
651withScientific :: String -> (Scientific -> Parser a) -> Value -> Parser a
651withScientific :: String -> (Scientific -> Parser a) -> Value -> Parser a
652withScientific _ f (Number scientific) = f scientific
652withScientific _ f (Number scientific) = f scientific
653withScientific expected _ v = typeMismatch expected v
653withScientific expected _ v = typeMismatch expected v
654{-# INLINE withScientific #-}
654{-# INLINE withScientific #-}
656+-- | @'withBoundedScientific' expected f value@ applies @f@ to the 'Scientific' number
657+-- when @value@ is a 'Number' and fails using @'typeMismatch' expected@
660+-- The conversion will also fail wyth a @'typeMismatch' if the
661+-- 'Scientific' exponent is larger than 1024.
662+withBoundedScientific :: String -> (Scientific -> Parser a) -> Value -> Parser a
663+withBoundedScientific _ f v@(Number scientific) =
664+ if base10Exponent scientific > 1024
665+ then typeMismatch "a number with exponent <= 1024" v
667+withBoundedScientific expected _ v = typeMismatch expected v
668+{-# INLINE withBoundedScientific #-}
656-- | @'withBool' expected f value@ applies @f@ to the 'Bool' when @value@ is a
670-- | @'withBool' expected f value@ applies @f@ to the 'Bool' when @value@ is a
657-- 'Bool' and fails using @'typeMismatch' expected@ otherwise.
671-- 'Bool' and fails using @'typeMismatch' expected@ otherwise.
658withBool :: String -> (Bool -> Parser a) -> Value -> Parser a
672withBool :: String -> (Bool -> Parser a) -> Value -> Parser a
@@ -1232,12 +1246,6 @@ instance FromJSONKey Double where
1232 "-Infinity" -> pure (negate 1/0)
1246 "-Infinity" -> pure (negate 1/0)
1233 _ -> Scientific.toRealFloat <$> parseScientificText t
1247 _ -> Scientific.toRealFloat <$> parseScientificText t
1235−instance FromJSON Number where
1236− parseJSON (Number s) = pure $ scientificToNumber s
1237− parseJSON Null = pure (D (0/0))
1238− parseJSON v = typeMismatch "Number" v
1239− {-# INLINE parseJSON #-}
1241instance FromJSON Float where
1249instance FromJSON Float where
1242 parseJSON = parseRealFloat "Float"
1250 parseJSON = parseRealFloat "Float"
1243 {-# INLINE parseJSON #-}
1251 {-# INLINE parseJSON #-}
@@ -1258,12 +1266,12 @@ instance (FromJSON a, Integral a) => FromJSON (Ratio a) where
1258 else pure $ numerator % denominator
1266 else pure $ numerator % denominator
1259 {-# INLINE parseJSON #-}
1267 {-# INLINE parseJSON #-}
1261−-- | /WARNING:/ Only parse fixed-precision numbers from trusted input
1269+-- | This instance includes a bounds check to prevent maliciously
1262−-- since an attacker could easily fill up the memory of the target
1270+-- large inputs to fill up the memory of the target system. You can
1263−-- system by specifying a scientific number with a big exponent like
1271+-- newtype 'Scientific' and provide your own instance using
1264−-- @1e1000000000@.
1272+-- 'withScientific' if you want to allow larger inputs.
1265instance HasResolution a => FromJSON (Fixed a) where
1273instance HasResolution a => FromJSON (Fixed a) where
1266− parseJSON = withScientific "Fixed" $ pure . realToFrac
1274+ parseJSON = withBoundedScientific "Fixed" $ pure . realToFrac
1267 {-# INLINE parseJSON #-}
1275 {-# INLINE parseJSON #-}
1269instance FromJSON Int where
1277instance FromJSON Int where
@@ -1273,10 +1281,10 @@ instance FromJSON Int where
1273instance FromJSONKey Int where
1281instance FromJSONKey Int where
1274 fromJSONKey = FromJSONKeyTextParser $ parseBoundedIntegralText "Int"
1282 fromJSONKey = FromJSONKeyTextParser $ parseBoundedIntegralText "Int"
1276−-- | /WARNING:/ Only parse Integers from trusted input since an
1284+-- | This instance includes a bounds check to prevent maliciously
1277−-- attacker could easily fill up the memory of the target system by
1285+-- large inputs to fill up the memory of the target system. You can
1278−-- specifying a scientific number with a big exponent like
1286+-- newtype 'Scientific' and provide your own instance using
1279−-- @1e1000000000@.
1287+-- 'withScientific' if you want to allow larger inputs.
1280instance FromJSON Integer where
1288instance FromJSON Integer where
1281 parseJSON = parseIntegral "Integer"
1289 parseJSON = parseIntegral "Integer"
1282 {-# INLINE parseJSON #-}
1290 {-# INLINE parseJSON #-}
@@ -1715,21 +1723,21 @@ instance FromJSONKey UTCTime where
1715 fromJSONKey = FromJSONKeyTextParser (Time.run Time.utcTime)
1723 fromJSONKey = FromJSONKeyTextParser (Time.run Time.utcTime)
1718−-- | /WARNING:/ Only parse lengths of time from trusted input
1726+-- | This instance includes a bounds check to prevent maliciously
1719−-- since an attacker could easily fill up the memory of the target
1727+-- large inputs to fill up the memory of the target system. You can
1720−-- system by specifying a scientific number with a big exponent like
1728+-- newtype 'Scientific' and provide your own instance using
1721−-- @1e1000000000@.
1729+-- 'withScientific' if you want to allow larger inputs.
1722instance FromJSON NominalDiffTime where
1730instance FromJSON NominalDiffTime where
1723− parseJSON = withScientific "NominalDiffTime" $ pure . realToFrac
1731+ parseJSON = withBoundedScientific "NominalDiffTime" $ pure . realToFrac
1724 {-# INLINE parseJSON #-}
1732 {-# INLINE parseJSON #-}
1727−-- | /WARNING:/ Only parse lengths of time from trusted input
1735+-- | This instance includes a bounds check to prevent maliciously
1728−-- since an attacker could easily fill up the memory of the target
1736+-- large inputs to fill up the memory of the target system. You can
1729−-- system by specifying a scientific number with a big exponent like
1737+-- newtype 'Scientific' and provide your own instance using
1730−-- @1e1000000000@.
1738+-- 'withScientific' if you want to allow larger inputs.
1731instance FromJSON DiffTime where
1739instance FromJSON DiffTime where
1732− parseJSON = withScientific "DiffTime" $ pure . realToFrac
1740+ parseJSON = withBoundedScientific "DiffTime" $ pure . realToFrac
1733 {-# INLINE parseJSON #-}
1741 {-# INLINE parseJSON #-}
1735-------------------------------------------------------------------------------
1743-------------------------------------------------------------------------------