Skip to content

Commit b443ef6

Browse files
tools: add reserve command
Add the `hsec-tools reserve` command, which reserves an HSEC ID. The `--id-mode` option specifies whether to use a placeholder ID, or allocate the next available ID. This option is currently required, but we might change this in the future as we enhance our tooling and PR automation. The command operates in the context of the current working directory. That is, determines that it is running within an hsec-tools Git clone, and then creates the file in the appropriate place. The user can override the repo location via an optional positional argument. Also create the directory in the repo (via the `.gitkeep` idiom).
1 parent 2e89aa5 commit b443ef6

File tree

4 files changed

+79
-1
lines changed

4 files changed

+79
-1
lines changed

advisories/reserved/.gitkeep

Whitespace-only changes.
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
{-# LANGUAGE LambdaCase #-}
2+
3+
module Command.Reserve where
4+
5+
import Control.Monad (unless)
6+
import Data.Maybe (fromMaybe)
7+
import System.Exit (die)
8+
import System.FilePath ((</>), (<.>))
9+
10+
import Security.Advisories.Git (getRepoRoot)
11+
import Security.Advisories.HsecId
12+
( placeholder
13+
, printHsecId
14+
, getNextHsecId
15+
)
16+
import Security.Advisories.Filesystem
17+
( dirNameAdvisories
18+
, dirNameReserved
19+
, isSecurityAdvisoriesRepo
20+
, getGreatestId
21+
)
22+
23+
-- | How to choose IDs when creating advisories or
24+
-- reservations.
25+
data IdMode
26+
= IdModePlaceholder
27+
-- ^ Create a placeholder ID (e.g. HSEC-0000-0000). Real IDs
28+
-- will be assigned later.
29+
| IdModeAuto
30+
-- ^ Use the next available ID. This option is more likely to
31+
-- result in conflicts when submitting advisories or reservations.
32+
33+
runReserveCommand :: Maybe FilePath -> IdMode -> IO ()
34+
runReserveCommand mPath idMode = do
35+
let
36+
path = fromMaybe "." mPath
37+
repoPath <- getRepoRoot path >>= \case
38+
Left _ -> die "Not a git repo"
39+
Right a -> pure a
40+
isRepo <- isSecurityAdvisoriesRepo repoPath
41+
unless isRepo $
42+
die "Not a security-advisories repo"
43+
44+
hsid <- case idMode of
45+
IdModePlaceholder -> pure placeholder
46+
IdModeAuto -> do
47+
curMax <- getGreatestId repoPath
48+
getNextHsecId curMax
49+
50+
let
51+
advisoriesPath = repoPath </> dirNameAdvisories
52+
fileName = printHsecId hsid <.> "md"
53+
filePath = advisoriesPath </> dirNameReserved </> fileName
54+
writeFile filePath "" -- write empty file

code/hsec-tools/app/Main.hs

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ import Control.Monad (join, void, when)
77
import qualified Data.ByteString.Lazy as L
88
import Data.Foldable (for_)
99
import Data.Functor ((<&>))
10-
import Data.List (isPrefixOf)
10+
import Data.List (intercalate, isPrefixOf)
1111
import qualified Data.Text.IO as T
1212
import Options.Applicative
1313
import System.Exit (die, exitFailure, exitSuccess)
@@ -21,6 +21,8 @@ import qualified Security.Advisories.Convert.OSV as OSV
2121
import Security.Advisories.Git
2222
import Security.Advisories.Generate.HTML
2323

24+
import qualified Command.Reserve
25+
2426
main :: IO ()
2527
main = join $ execParser cliOpts
2628

@@ -31,12 +33,32 @@ cliOpts = info (commandsParser <**> helper) (fullDesc <> header "Haskell Advisor
3133
commandsParser =
3234
subparser
3335
( command "check" (info commandCheck (progDesc "Syntax check a single advisory"))
36+
<> command "reserve" (info commandReserve (progDesc "Reserve an HSEC ID"))
3437
<> command "osv" (info commandOsv (progDesc "Convert a single advisory to OSV"))
3538
<> command "render" (info commandRender (progDesc "Render a single advisory as HTML"))
3639
<> command "generate-index" (info commandGenerateIndex (progDesc "Generate an HTML index"))
3740
<> command "help" (info commandHelp (progDesc "Show command help"))
3841
)
3942

43+
-- | Create an option with a fixed set of values
44+
multiOption :: [(String, a)] -> Mod OptionFields a -> Parser a
45+
multiOption kvs m = option rdr (m <> metavar choices)
46+
where
47+
choices = "{" <> intercalate "|" (fmap fst kvs) <> "}"
48+
errMsg = "must be one of " <> choices
49+
rdr = eitherReader (maybe (Left errMsg) Right . flip lookup kvs)
50+
51+
commandReserve :: Parser (IO ())
52+
commandReserve =
53+
Command.Reserve.runReserveCommand
54+
<$> optional (argument str (metavar "REPO"))
55+
<*> multiOption
56+
[ ("placeholder", Command.Reserve.IdModePlaceholder)
57+
, ("auto", Command.Reserve.IdModeAuto)
58+
]
59+
( long "id-mode" <> help "How to assign IDs" )
60+
<**> helper
61+
4062
commandCheck :: Parser (IO ())
4163
commandCheck =
4264
withAdvisory go

code/hsec-tools/hsec-tools.cabal

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,8 @@ library
6868

6969
executable hsec-tools
7070
main-is: Main.hs
71+
other-modules:
72+
Command.Reserve
7173

7274
-- Modules included in this executable, other than Main.
7375
-- other-modules:

0 commit comments

Comments
 (0)