11
11
module Main where
12
12
13
13
import Commonmark.Html (Html , renderHtml )
14
- import Commonmark.Parser
14
+ import Commonmark.Parser ( commonmark )
15
15
import Commonmark.Types
16
+ ( Attributes ,
17
+ Format ,
18
+ HasAttributes (.. ),
19
+ IsBlock (.. ),
20
+ IsInline (.. ),
21
+ ListSpacing ,
22
+ ListType ,
23
+ Rangeable (.. ),
24
+ SourceRange ,
25
+ )
16
26
import Control.Monad ((>=>) )
17
- import Control.Monad.Except (ExceptT (ExceptT ), MonadError , throwError )
27
+ import Control.Monad.Except
28
+ ( ExceptT (ExceptT ),
29
+ MonadError ,
30
+ catchError ,
31
+ runExceptT ,
32
+ throwError ,
33
+ )
34
+ import Control.Monad.IO.Class (liftIO )
35
+ import Data.Bifunctor
18
36
import Data.Functor ((<&>) )
19
37
import Data.Functor.Identity (Identity (Identity ))
20
38
import qualified Data.HashMap.Lazy as V
@@ -29,60 +47,81 @@ import qualified Data.Text as T
29
47
import qualified Data.Text.IO as T
30
48
import qualified Data.Text.Lazy as T (toStrict )
31
49
import Data.Time.Calendar
32
- import Data.Time.Calendar.OrdinalDate
33
50
import Data.Time.Clock (UTCTime (.. ))
34
51
import Distribution.Parsec (eitherParsec )
35
52
import Distribution.Types.VersionRange (VersionRange )
36
53
import GHC.Exts (IsList (.. ))
37
54
import System.Exit (exitFailure , exitSuccess )
38
55
import System.IO (hPrint , hPutStrLn , stderr )
39
- -- import Text.Toml (parseTomlDoc)
40
- -- import qualified Text.Toml.Types as Toml
41
-
42
56
import qualified TOML
43
57
44
58
main :: IO ()
45
59
main = do
46
60
input <- T. getContents
47
- case commonmark " stdin" input of
48
- Left err -> do
49
- hPrint stderr err
50
- exitFailure
51
- Right markdown -> do
52
- case advisoryDoc markdown of
53
- Nothing -> hPrint stderr " Does not have toml code block as first element"
54
- Just (frontMatter, text) -> do
55
- table <- case TOML. decode frontMatter of
56
- Left err -> do
57
- hPutStrLn stderr " Couldn't parse front matter as TOML:"
58
- T. hPutStrLn stderr (TOML. renderTOMLError err)
59
- exitFailure
60
- Right tbl -> do
61
- pure tbl
62
- case parseAdvisory table of
63
- Left err -> do
64
- hPrint stderr (show err)
65
- exitFailure
66
- Right f ->
67
- T. putStrLn $ renderAdvisory $ f $ T. toStrict $ renderHtml (fromBlock text :: Html () )
68
-
69
- exitSuccess
70
-
71
- newtype CWE = CWE { unCWE :: Integer }
61
+ markdown <- runH (T. pack . show ) $ commonmark " stdin" input
62
+ (frontMatter, text) <- run $ advisoryDoc markdown
63
+ table <-
64
+ runH ((" Couldn't parse front matter as TOML:\n " <> ) . TOML. renderTOMLError) $
65
+ TOML. decode frontMatter
66
+ f <- runH (T. pack . show ) $ parseAdvisory table
67
+ T. putStrLn $
68
+ renderAdvisory $
69
+ f $
70
+ T. toStrict $
71
+ renderHtml (fromBlock text :: Html () )
72
+ exitSuccess
73
+ where
74
+ panic err = T. hPutStrLn stderr err >> exitFailure
75
+ run = either panic pure
76
+ runH f = run . first f
77
+
78
+ newtype CWE = CWE { unCWE :: Integer }
72
79
deriving (Show )
73
80
74
- data Architecture = AArch64 | Alpha | Arm | HPPA | HPPA1_1 | I386 | IA64 | M68K | MIPS | MIPSEB | MIPSEL | NIOS2 | PowerPC | PowerPC64 | PowerPC64LE | RISCV32 | RISCV64 | RS6000 | S390 | S390X | SH4 | SPARC | SPARC64 | VAX | X86_64
81
+ data Architecture
82
+ = AArch64
83
+ | Alpha
84
+ | Arm
85
+ | HPPA
86
+ | HPPA1_1
87
+ | I386
88
+ | IA64
89
+ | M68K
90
+ | MIPS
91
+ | MIPSEB
92
+ | MIPSEL
93
+ | NIOS2
94
+ | PowerPC
95
+ | PowerPC64
96
+ | PowerPC64LE
97
+ | RISCV32
98
+ | RISCV64
99
+ | RS6000
100
+ | S390
101
+ | S390X
102
+ | SH4
103
+ | SPARC
104
+ | SPARC64
105
+ | VAX
106
+ | X86_64
75
107
deriving (Show )
76
108
77
- data OS = Windows | MacOS | Linux | FreeBSD | Android | NetBSD | OpenBSD
109
+ data OS
110
+ = Windows
111
+ | MacOS
112
+ | Linux
113
+ | FreeBSD
114
+ | Android
115
+ | NetBSD
116
+ | OpenBSD
78
117
deriving (Show )
79
118
80
119
data Date = Date { dateYear :: Integer , dateMonth :: Int , dateDay :: Int }
81
120
deriving (Show )
82
121
83
122
newtype Keyword = Keyword Text
84
123
deriving (Eq , Ord )
85
- deriving Show via Text
124
+ deriving ( Show ) via Text
86
125
87
126
data Advisory = Advisory
88
127
{ advisoryId :: Text ,
@@ -117,9 +156,22 @@ renderAdvisory adv =
117
156
row " Aliases" (T. intercalate " , " . advisoryAliases),
118
157
row " CVSS" (fromMaybe " " . advisoryCVSS),
119
158
row " Versions" (T. pack . show . advisoryVersions),
120
- row " Architectures" (maybe " All" ( T. intercalate " , " . map (T. pack . show )) . advisoryArchitectures),
121
- row " OS" (maybe " All" ( T. intercalate " , " . map (T. pack . show )) . advisoryOS),
122
- row " Affected exports" (T. intercalate " , " . map (\ (name, version) -> name <> " in " <> T. pack (show version)) . advisoryNames)
159
+ row
160
+ " Architectures"
161
+ ( maybe
162
+ " All"
163
+ ( T. intercalate " , "
164
+ . map (T. pack . show )
165
+ )
166
+ . advisoryArchitectures
167
+ ),
168
+ row " OS" (maybe " All" (T. intercalate " , " . map (T. pack . show )) . advisoryOS),
169
+ row
170
+ " Affected exports"
171
+ ( T. intercalate " , "
172
+ . map (\ (name, version) -> name <> " in " <> T. pack (show version))
173
+ . advisoryNames
174
+ )
123
175
],
124
176
" </table>" ,
125
177
advisoryHtml adv
@@ -139,18 +191,30 @@ parseAdvisory table = runTableParser $ do
139
191
package <- mandatory advisory " package" isString
140
192
date <- mandatory advisory " date" isDate <&> uncurry3 Date . toGregorian
141
193
url <- mandatory advisory " url" isString
142
- cats <- fromMaybe [] <$> optional advisory " cwe" (isArrayOf (fmap CWE . isInt))
143
- kwds <- fromMaybe [] <$> optional advisory " keywords" (isArrayOf (fmap Keyword . isString))
144
- aliases <- fromMaybe [] <$> optional advisory " aliases" (isArrayOf isString)
194
+ cats <-
195
+ fromMaybe []
196
+ <$> optional advisory " cwe" (isArrayOf (fmap CWE . isInt))
197
+ kwds <-
198
+ fromMaybe []
199
+ <$> optional advisory " keywords" (isArrayOf (fmap Keyword . isString))
200
+ aliases <-
201
+ fromMaybe []
202
+ <$> optional advisory " aliases" (isArrayOf isString)
145
203
cvss <- optional advisory " cvss" isString
146
204
147
205
(os, arch, decls) <-
148
206
optional table " affected" isTable >>= \ case
149
207
Nothing -> pure (Nothing , Nothing , [] )
150
208
Just tbl -> do
151
- os <- optional tbl " os" $ isArrayOf (isString >=> operatingSystem)
152
- arch <- optional tbl " os" $ isArrayOf (isString >=> architecture)
153
- decls <- maybe [] Map. toList <$> optional tbl " declarations" (isTableOf versionRange)
209
+ os <-
210
+ optional tbl " os" $
211
+ isArrayOf (isString >=> operatingSystem)
212
+ arch <-
213
+ optional tbl " os" $
214
+ isArrayOf (isString >=> architecture)
215
+ decls <-
216
+ maybe [] Map. toList
217
+ <$> optional tbl " declarations" (isTableOf versionRange)
154
218
pure (os, arch, decls)
155
219
156
220
versions <- mandatory table " versions" isTable
@@ -231,7 +295,13 @@ data TableParseErr
231
295
deriving (Show )
232
296
233
297
newtype TableParser a = TableParser { runTableParser :: Either TableParseErr a }
234
- deriving (Functor , Applicative , Monad , MonadError TableParseErr ) via ExceptT TableParseErr Identity
298
+ deriving
299
+ ( Functor ,
300
+ Applicative ,
301
+ Monad ,
302
+ MonadError TableParseErr
303
+ )
304
+ via ExceptT TableParseErr Identity
235
305
236
306
hasNoKeysBut :: [Text ] -> TOML. Table -> TableParser ()
237
307
hasNoKeysBut keys tbl =
@@ -242,14 +312,30 @@ hasNoKeysBut keys tbl =
242
312
[] -> pure ()
243
313
k : ks -> throwError (UnexpectedKeys $ k :| ks)
244
314
245
- optional :: TOML. Table -> Text -> (TOML. Value -> TableParser a ) -> TableParser (Maybe a )
246
- optional tbl k act = onKey tbl k (pure Nothing ) (fmap Just . act)
247
-
248
- mandatory :: TOML. Table -> Text -> (TOML. Value -> TableParser a ) -> TableParser a
249
- mandatory tbl k act = onKey tbl k (throwError $ MissingKey k) act
250
-
251
- onKey :: TOML. Table -> Text -> TableParser a -> (TOML. Value -> TableParser a ) -> TableParser a
252
- onKey tbl k absent present = maybe absent present $ Map. lookup k tbl
315
+ optional ::
316
+ TOML. Table ->
317
+ Text ->
318
+ (TOML. Value -> TableParser a ) ->
319
+ TableParser (Maybe a )
320
+ optional tbl k act =
321
+ onKey tbl k (pure Nothing ) (fmap Just . act)
322
+
323
+ mandatory ::
324
+ TOML. Table ->
325
+ Text ->
326
+ (TOML. Value -> TableParser a ) ->
327
+ TableParser a
328
+ mandatory tbl k act =
329
+ onKey tbl k (throwError $ MissingKey k) act
330
+
331
+ onKey ::
332
+ TOML. Table ->
333
+ Text ->
334
+ TableParser a ->
335
+ (TOML. Value -> TableParser a ) ->
336
+ TableParser a
337
+ onKey tbl k absent present =
338
+ maybe absent present $ Map. lookup k tbl
253
339
254
340
isInt :: TOML. Value -> TableParser Integer
255
341
isInt (TOML. Integer i) = pure i
@@ -263,9 +349,14 @@ isTable :: TOML.Value -> TableParser TOML.Table
263
349
isTable (TOML. Table table) = pure table
264
350
isTable other = throwError $ InvalidFormat " Table" (describeValue other)
265
351
266
- isTableOf :: (TOML. Value -> TableParser a ) -> TOML. Value -> TableParser (Map Text a )
267
- isTableOf elt (TOML. Table table) = traverse elt table
268
- isTableOf _ other = throwError $ InvalidFormat " Table" (describeValue other)
352
+ isTableOf ::
353
+ (TOML. Value -> TableParser a ) ->
354
+ TOML. Value ->
355
+ TableParser (Map Text a )
356
+ isTableOf elt (TOML. Table table) =
357
+ traverse elt table
358
+ isTableOf _ other =
359
+ throwError $ InvalidFormat " Table" (describeValue other)
269
360
270
361
isDate :: TOML. Value -> TableParser Day
271
362
isDate (TOML. LocalDate time) = pure time
@@ -275,8 +366,12 @@ isArray :: TOML.Value -> TableParser [TOML.Value]
275
366
isArray (TOML. Array arr) = pure arr
276
367
isArray other = throwError $ InvalidFormat " Array" (describeValue other)
277
368
278
- isArrayOf :: (TOML. Value -> TableParser a ) -> TOML. Value -> TableParser [a ]
279
- isArrayOf elt v = isArray v >>= traverse elt . toList
369
+ isArrayOf ::
370
+ (TOML. Value -> TableParser a ) ->
371
+ TOML. Value ->
372
+ TableParser [a ]
373
+ isArrayOf elt v =
374
+ isArray v >>= traverse elt . toList
280
375
281
376
describeValue :: TOML. Value -> Text
282
377
describeValue TOML. String {} = " string"
@@ -290,14 +385,14 @@ describeValue TOML.LocalDateTime {} = "local date/time"
290
385
describeValue TOML. LocalDate {} = " local date"
291
386
describeValue TOML. LocalTime {} = " local time"
292
387
293
- advisoryDoc :: Block -> Maybe (Text , Block )
388
+ advisoryDoc :: Block -> Either Text (Text , Block )
294
389
advisoryDoc (BSeq bs) =
295
390
case bs of
296
- [] -> Nothing
391
+ [] -> Left " Does not have toml code block as first element "
297
392
b : bs -> advisoryDoc b >>= \ (toml, code) -> pure (toml, BSeq (code : bs))
298
393
advisoryDoc (BRanged _ b) = advisoryDoc b
299
394
advisoryDoc (CodeBlock (T. unpack -> " toml" ) frontMatter) = pure (frontMatter, mempty )
300
- advisoryDoc _ = Nothing
395
+ advisoryDoc _ = Left " Does not have toml code block as first element "
301
396
302
397
data Block
303
398
= Para Inline
0 commit comments