@@ -13,6 +13,7 @@ module Security.Advisories.Filesystem
13
13
(
14
14
dirNameAdvisories
15
15
, dirNameReserved
16
+ , dirNamePublished
16
17
, isSecurityAdvisoriesRepo
17
18
, getReservedIds
18
19
, getAdvisoryIds
@@ -36,12 +37,10 @@ import Data.Semigroup (Max(Max, getMax))
36
37
import Data.Traversable (for )
37
38
38
39
import Control.Monad.IO.Class (MonadIO , liftIO )
39
- import Control.Monad.Writer.Strict (execWriterT , tell )
40
40
import qualified Data.Text as T
41
41
import qualified Data.Text.IO as T
42
- import System.FilePath ((</>) , takeBaseName , splitDirectories )
43
- import System.Directory (doesDirectoryExist , pathIsSymbolicLink )
44
- import System.Directory.PathWalk
42
+ import System.FilePath ((</>) , splitDirectories )
43
+ import System.Directory (doesDirectoryExist , listDirectory )
45
44
import Validation (Validation (.. ))
46
45
47
46
import Security.Advisories (Advisory , AttributeOverridePolicy (NoOverrides ), OutOfBandAttributes (.. ), ParseAdvisoryError , parseAdvisory , ComponentIdentifier (.. ))
@@ -58,6 +57,9 @@ dirNameAdvisories = "advisories"
58
57
dirNameReserved :: FilePath
59
58
dirNameReserved = " reserved"
60
59
60
+ dirNamePublished :: FilePath
61
+ dirNamePublished = " published"
62
+
61
63
-- | Check whether the directory appears to be the root of a
62
64
-- /security-advisories/ filesystem. Only checks that the
63
65
-- @advisories@ subdirectory exists.
@@ -109,7 +111,7 @@ forReserved
109
111
:: (MonadIO m , Monoid r )
110
112
=> FilePath -> (FilePath -> HsecId -> m r ) -> m r
111
113
forReserved root =
112
- _forFiles (root </> dirNameAdvisories </> dirNameReserved)
114
+ _forFilesByYear (root </> dirNameAdvisories </> dirNameReserved)
113
115
114
116
-- | Invoke a callback for each HSEC ID under each of the advisory
115
117
-- subdirectories, excluding the @reserved@ directory. The results
@@ -121,23 +123,17 @@ forReserved root =
121
123
forAdvisory
122
124
:: (MonadIO m , Monoid r )
123
125
=> FilePath -> (FilePath -> HsecId -> m r ) -> m r
124
- forAdvisory root go = do
125
- let dir = root </> dirNameAdvisories
126
- subdirs <- filter (/= dirNameReserved) <$> _getSubdirs dir
127
- fmap fold $ for subdirs $ \ subdir -> _forFiles (dir </> subdir) go
126
+ forAdvisory root =
127
+ _forFilesByYear (root </> dirNameAdvisories </> dirNamePublished)
128
128
129
- -- | List deduplicated parsed Advisories
129
+ -- | List parsed Advisories
130
130
listAdvisories
131
131
:: (MonadIO m )
132
132
=> FilePath -> m (Validation [(FilePath , ParseAdvisoryError )] [Advisory ])
133
133
listAdvisories root =
134
- forAdvisory root $ \ advisoryPath _advisoryId -> do
135
- isSym <- liftIO $ pathIsSymbolicLink advisoryPath
136
- if isSym
137
- then return $ pure []
138
- else
139
- bimap (\ err -> [(advisoryPath, err)]) pure
140
- <$> advisoryFromFile advisoryPath
134
+ forAdvisory root $ \ advisoryPath _advisoryId ->
135
+ bimap (\ err -> [(advisoryPath, err)]) pure
136
+ <$> advisoryFromFile advisoryPath
141
137
142
138
-- | Parse an advisory from a file system path
143
139
advisoryFromFile
@@ -158,28 +154,20 @@ advisoryFromFile advisoryPath = do
158
154
$ either Failure Success
159
155
$ parseAdvisory NoOverrides oob fileContent
160
156
161
- -- | Get names (not paths) of subdirectories of the given directory
162
- -- (one level). There's no monoidal, interruptible variant of
163
- -- @pathWalk@ so we use @WriterT@ to smuggle the result out.
164
- --
165
- _getSubdirs :: (MonadIO m ) => FilePath -> m [FilePath ]
166
- _getSubdirs root =
167
- execWriterT $
168
- pathWalkInterruptible root $ \ _ subdirs _ -> do
169
- tell subdirs
170
- pure Stop
171
-
172
- _forFiles
157
+ _forFilesByYear
173
158
:: (MonadIO m , Monoid r )
174
159
=> FilePath -- ^ (sub)directory name
175
160
-> (FilePath -> HsecId -> m r )
176
161
-> m r
177
- _forFiles root go =
178
- pathWalkAccumulate root $ \ dir _ files ->
179
- fmap fold $ for files $ \ file ->
180
- case parseHsecId (takeBaseName file) of
181
- Nothing -> pure mempty
182
- Just hsid -> go (dir </> file) hsid
162
+ _forFilesByYear root go = do
163
+ yearsFile <- liftIO $ listDirectory root
164
+ fmap (foldMap fold) $
165
+ for yearsFile $ \ year -> do
166
+ files <- liftIO $ listDirectory (root </> year)
167
+ for files $ \ file ->
168
+ case parseHsecId (" HSEC-" <> year <> " -" <> file) of
169
+ Nothing -> pure mempty
170
+ Just hsid -> go (root </> year </> file) hsid
183
171
184
172
parseComponentIdentifier :: Monad m => FilePath -> ExceptT OOBError m (Maybe ComponentIdentifier )
185
173
parseComponentIdentifier fp = ExceptT . pure $ case drop 1 $ reverse $ splitDirectories fp of
0 commit comments