Files
DoGaMa-serv/tools/sbom/main.go
T

98 lines
2.9 KiB
Go

// Command sbom converts `go list -m -json all` output into a deterministic SPDX 2.3 inventory.
package main
import (
"crypto/sha256"
"encoding/base64"
"encoding/hex"
"encoding/json"
"errors"
"fmt"
"io"
"os"
"strconv"
"strings"
"time"
)
type module struct {
Path string
Version string
Sum string
}
type document struct {
SPDXVersion string `json:"spdxVersion"`
DataLicense string `json:"dataLicense"`
SPDXID string `json:"SPDXID"`
Name string `json:"name"`
DocumentNamespace string `json:"documentNamespace"`
CreationInfo creationInfo `json:"creationInfo"`
Packages []packageInfo `json:"packages"`
}
type creationInfo struct {
Created string `json:"created"`
Creators []string `json:"creators"`
}
type packageInfo struct {
Name string `json:"name"`
SPDXID string `json:"SPDXID"`
VersionInfo string `json:"versionInfo,omitempty"`
DownloadLocation string `json:"downloadLocation"`
FilesAnalyzed bool `json:"filesAnalyzed"`
Checksums []checksum `json:"checksums,omitempty"`
}
type checksum struct {
Algorithm string `json:"algorithm"`
ChecksumValue string `json:"checksumValue"`
}
func main() {
decoder := json.NewDecoder(os.Stdin)
var modules []module
for {
var value module
if err := decoder.Decode(&value); err != nil {
if errors.Is(err, io.EOF) {
break
}
fatal(err)
}
modules = append(modules, value)
}
epoch, err := strconv.ParseInt(os.Getenv("SOURCE_DATE_EPOCH"), 10, 64)
if err != nil {
fatal(fmt.Errorf("SOURCE_DATE_EPOCH: %w", err))
}
digest := sha256.Sum256([]byte(fmt.Sprint(modules)))
doc := document{
SPDXVersion: "SPDX-2.3", DataLicense: "CC0-1.0", SPDXID: "SPDXRef-DOCUMENT",
Name: "DoGaMa Go module inventory", DocumentNamespace: "https://dogama.invalid/spdx/" + hex.EncodeToString(digest[:]),
CreationInfo: creationInfo{Created: time.Unix(epoch, 0).UTC().Format(time.RFC3339), Creators: []string{"Tool: dogama-release"}},
}
for index, value := range modules {
pkg := packageInfo{Name: value.Path, SPDXID: fmt.Sprintf("SPDXRef-Package-%d", index), VersionInfo: value.Version, DownloadLocation: "https://proxy.golang.org/" + strings.ToLower(value.Path), FilesAnalyzed: false}
if strings.HasPrefix(value.Sum, "h1:") {
sum, decodeErr := base64.StdEncoding.DecodeString(strings.TrimPrefix(value.Sum, "h1:"))
if decodeErr != nil || len(sum) != sha256.Size {
fatal(fmt.Errorf("invalid module checksum for %s", value.Path))
}
pkg.Checksums = []checksum{{Algorithm: "SHA256", ChecksumValue: hex.EncodeToString(sum)}}
}
doc.Packages = append(doc.Packages, pkg)
}
encoder := json.NewEncoder(os.Stdout)
encoder.SetIndent("", " ")
if err := encoder.Encode(doc); err != nil {
fatal(err)
}
}
func fatal(err error) {
fmt.Fprintln(os.Stderr, err)
os.Exit(1)
}