Skip to content

Commit fb69a9f

Browse files
committed
Fix #11: reject quoted timestamps instead of silently parsing as null
2 parents 1bc8218 + 1a307b9 commit fb69a9f

6 files changed

Lines changed: 55 additions & 6 deletions

File tree

.gitignore

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -28,9 +28,8 @@ cabal.project.local~
2828

2929
# End of https://www.toptal.com/developers/gitignore/api/haskell
3030

31-
_cache
32-
_keys
33-
_repo
34-
35-
# only at the root since we need to check-in testcases _sources
31+
# only at the root since testcases might include these paths
3632
./_sources
33+
./_cache
34+
./_keys
35+
./_repo

app/Foliage/Meta.hs

Lines changed: 37 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,8 @@ where
3232

3333
import Control.Applicative ((<|>))
3434
import Control.Monad (void)
35+
import Control.Monad.State (modify)
36+
import Data.HashMap.Strict qualified as HashMap
3537
import Data.List (sortOn)
3638
import Data.Maybe (fromMaybe)
3739
import Data.Ord (Down (Down))
@@ -138,7 +140,7 @@ data PackageVersionSpec = PackageVersionSpec
138140
sourceMetaCodec :: TomlCodec PackageVersionSpec
139141
sourceMetaCodec =
140142
PackageVersionSpec
141-
<$> Toml.dioptional (timeCodec "timestamp")
143+
<$> optionalTimeCodec "timestamp"
142144
.= packageVersionTimestamp
143145
<*> packageSourceCodec
144146
.= packageVersionSource
@@ -200,3 +202,37 @@ withDefault :: (Eq a) => a -> TomlCodec a -> TomlCodec a
200202
withDefault d c = (fromMaybe d <$> Toml.dioptional c) .= f
201203
where
202204
f a = if a == d then Nothing else Just a
205+
206+
-- | Codec for a maybe-missing time value.
207+
--
208+
-- Note this is different from dioptional timeCodec. With dioptional timeCodec,
209+
-- if the user writes
210+
-- timestamp = '2022-08-22T10:38:45Z'
211+
-- rather than
212+
-- timestamp = 2022-08-22T10:38:45Z
213+
-- the timestamp will parse as Nothing because it won't match the zoneTime
214+
-- type and it is not an error because it is optional.
215+
--
216+
-- We use a handrolled version of match (matchMaybe) to make it work.
217+
--
218+
-- See discussions at
219+
-- 1. https://github.com/input-output-hk/foliage/issues/11
220+
-- 2. https://github.com/input-output-hk/foliage/pull/57
221+
-- 3. https://github.com/kowainik/tomland/issues/223
222+
optionalTimeCodec :: Toml.Key -> TomlCodec (Maybe UTCTime)
223+
optionalTimeCodec key =
224+
Toml.dimap (fmap $ utcToZonedTime utc) (fmap zonedTimeToUTC) $ matchMaybe Toml._ZonedTime key
225+
226+
matchMaybe :: forall a. Toml.TomlBiMap a Toml.AnyValue -> Toml.Key -> TomlCodec (Maybe a)
227+
matchMaybe bimap key = Toml.Codec input output
228+
where
229+
input :: Toml.TomlEnv (Maybe a)
230+
input toml = case HashMap.lookup key (Toml.tomlPairs toml) of
231+
Nothing -> pure Nothing
232+
Just anyVal -> pure <$> Toml.whenLeftBiMapError key (Toml.backward bimap anyVal) pure
233+
234+
output :: Maybe a -> Toml.TomlState (Maybe a)
235+
output Nothing = pure Nothing
236+
output (Just a) = do
237+
anyVal <- Toml.eitherToTomlState $ Toml.forward bimap a
238+
Just a <$ modify (Toml.insertKeyAnyVal key anyVal)

foliage.cabal

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,7 @@ executable foliage
5757
ed25519 >=0.0.5.0 && <0.1,
5858
filepath >=1.4.2.1 && <1.6,
5959
hackage-security >=0.6.2.1 && <0.7,
60+
mtl >=2.2 && <2.4,
6061
network-uri >=2.6.4.1 && <2.8,
6162
optparse-applicative >=0.17.0.0 && <0.20,
6263
shake >=0.19.6 && <0.20,
@@ -66,6 +67,7 @@ executable foliage
6667
time >=1.9.3 && <1.16,
6768
time-compat >=1.9.6.1 && <1.10,
6869
tomland >=1.3.3.1 && <1.4,
70+
unordered-containers >=0.2 && <0.3,
6971
vector >=0.13.0.0 && <0.14,
7072
with-utf8 >=1.0.2.3 && <1.2,
7173
zlib >=0.6.2.3 && <0.8,

tests/Tests.hs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
{-# LANGUAGE LambdaCase #-}
22

33
import Codec.Archive.Tar.Entry (GenEntry (entryContent, entryTime), GenEntryContent (NormalFile))
4+
import Control.Exception (SomeException, try)
45
import Data.ByteString.Lazy qualified as BL
56
import Foliage.Tests.Tar
67
import Foliage.Tests.Utils
@@ -63,4 +64,12 @@ main = do
6364

6465
step "Running checks"
6566
doesFileExist "_repo/foliage/packages.json" @? "foliage/packages.json does not exist"
67+
, ---
68+
testCaseSteps "rejects quoted timestamp in meta.toml" $ \step ->
69+
inTemporaryDirectoryWithFixture "tests/fixtures/bad-timestamp" $ do
70+
step "Building repository (expecting failure)"
71+
result <- try @SomeException (callCommand "foliage build --no-signatures")
72+
case result of
73+
Left _ -> pure ()
74+
Right _ -> assertFailure "foliage build should have failed on a quoted timestamp"
6675
]
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
url = "file:tarballs/pkg-a-2.3.4.5.tar.gz"
2+
timestamp = '2022-03-29T06:19:50+00:00'
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
../simple/tarballs

0 commit comments

Comments
 (0)