diff --git a/Makefile b/Makefile index dbe1f612e5..a5a06e2391 100644 --- a/Makefile +++ b/Makefile @@ -61,6 +61,7 @@ full-clean: clean clean: cabal clean -rm -rf dist + -rm -f "bill-of-materials.$(HELM_SEMVER).json" .PHONY: clean-hint clean-hint: @@ -543,6 +544,12 @@ kind-restart-%: .local/kind-kubeconfig helm-template-%: clean-charts charts-integration ./hack/bin/helm-template.sh $(*) +# Ask the security team for the `DEPENDENCY_TRACK_API_KEY` (if you need it) .PHONY: upload-bombon upload-bombon: - ./hack/bin/bombon.hs -- "$@" + nix build -f nix wireServer.allLocalPackagesBom -o "bill-of-materials.$(HELM_SEMVER).json" + ./hack/bin/bombon.hs -- \ + --bom-filepath "./bill-of-materials.$(HELM_SEMVER).json" \ + --project-version $(HELM_SEMVER) \ + --api-key $(DEPENDENCY_TRACK_API_KEY) \ + --auto-create diff --git a/changelog.d/5-internal/upload-bom-to-deptrack b/changelog.d/5-internal/upload-bom-to-deptrack new file mode 100644 index 0000000000..abffca8b63 --- /dev/null +++ b/changelog.d/5-internal/upload-bom-to-deptrack @@ -0,0 +1,3 @@ +Upload bill-of-material (BOM) files directly to the Dependency Tracker via REST. +This eases the life of the security team and prevents cluttering our release +artifact page. diff --git a/hack/bin/bombon.hs b/hack/bin/bombon.hs index 36f87a7493..0c01c4cf80 100755 --- a/hack/bin/bombon.hs +++ b/hack/bin/bombon.hs @@ -1,17 +1,120 @@ -#!/usr/bin/env -S nix -Lv run github:wireapp/ghc-flakr/ecb1f45f1549e06c92d71164e305ce501eb0e36e --extra-experimental-features flakes -{-# LANGUAGE BlockArguments #-} -{-# LANGUAGE ImportQualifiedPost #-} -{-# LANGUAGE OverloadedStrings #-} +#!/usr/bin/env -S nix -Lv run github:wireapp/ghc-flakr/99fe5a331fdd37d52043f14e5c565ac29a30bcb4 +{-# LANGUAGE DataKinds #-} -import Data.Text qualified as T -import Turtle +import Data.Aeson +import qualified Data.ByteString.Base64.Lazy as Base64 +import qualified Data.ByteString.Lazy.Char8 as BL +import Data.Proxy +import Data.Text.Lazy +import Data.Text.Lazy.Encoding +import GHC.Generics +import qualified Network.HTTP.Client as HTTP +import Network.HTTP.Client.TLS (tlsManagerSettings) +import Options.Applicative +import Servant.API +import Servant.Client +data Payload = Payload + { bom :: Text, + projectName :: String, + projectVersion :: String, + autoCreate :: Bool + } + deriving (Generic, Show) + +instance ToJSON Payload + +data ApiResponse = ApiResponse + { token :: String + } + deriving (Generic, Show) + +instance FromJSON ApiResponse + +type DependenyTrackAPI = + "api" + :> "v1" + :> "bom" + :> ReqBody '[JSON] Payload + :> Header "X-Api-Key" String + :> Put '[JSON] ApiResponse + +api :: Proxy DependenyTrackAPI +api = Proxy + +putBOM :: Payload -> Maybe String -> ClientM ApiResponse +putBOM = client api + +data CliOptions = CliOptions + { opBomPath :: String, + opProjectName :: String, + opProjectVersion :: String, + opAutoCreate :: Bool, + opApiKey :: String + } + deriving (Show) + +cliParser :: Parser CliOptions +cliParser = + CliOptions + <$> ( strOption + ( long "bom-filepath" + <> short 'f' + <> metavar "FILENAME" + ) + ) + <*> ( strOption + ( long "project-name" + <> short 'p' + <> metavar "PROJECT_NAME" + <> value "wire-server-ci" + ) + ) + <*> ( strOption + ( long "project-version" + <> short 'v' + <> metavar "PROJECT_VERSION" + ) + ) + <*> ( switch + ( long "auto-create" + <> short 'c' + ) + ) + <*> ( strOption + ( long "api-key" + <> short 'k' + <> metavar "API_KEY" + ) + ) + +fullCliParser :: ParserInfo CliOptions +fullCliParser = + info + (cliParser <**> helper) + ( fullDesc + <> progDesc "Upload BOM files to deptrack" + ) + +main :: IO () main = do - (release, repo) <- options "Upload boms" do - (,) - <$> optText "release" 't' "Which release tag to upload the artifacts to" - <*> optText "repo" 'r' "Which repository to upload the artifacts to" - let bomName = "wire-server-bom-" <> release <> ".json" - ExitSuccess <- proc "nix" ["build", "-f", "nix", "wireServer.allLocalPackagesBom", "-o", bomName] mempty - printf ("uploading " % s % " to release " % s % "\n") bomName ("chart/" <> release) - proc "gh" ["-R", repo, "release", "upload", "chart/" <> release, bomName] mempty + options <- execParser fullCliParser + manager' <- HTTP.newManager tlsManagerSettings + bom <- readFile $ opBomPath options + let payload = + Payload + { bom = toBase64Text bom, + projectName = opProjectName options, + projectVersion = opProjectVersion options, + autoCreate = opAutoCreate options + } + res <- + runClientM + (putBOM payload ((Just . opApiKey) options)) + (mkClientEnv manager' (BaseUrl Https "deptrack.wire.link" 443 "")) + case res of + Left err -> print $ "Error: " ++ show err + Right res -> print res + +toBase64Text :: String -> Text +toBase64Text = decodeUtf8 . Base64.encode . BL.pack