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