mirror of
https://github.com/hashicorp/packer.git
synced 2026-09-19 06:21:41 -04:00
Add PR suggestions
This commit is contained in:
@@ -26,7 +26,7 @@ require (
|
||||
github.com/hashicorp/packer-plugin-amazon v1.2.1
|
||||
github.com/hashicorp/packer-plugin-sdk v0.5.4
|
||||
github.com/jehiah/go-strftime v0.0.0-20171201141054-1d33003b3869
|
||||
github.com/klauspost/compress v1.13.6 // indirect
|
||||
github.com/klauspost/compress v1.13.6
|
||||
github.com/klauspost/pgzip v1.2.5
|
||||
github.com/masterzen/winrm v0.0.0-20210623064412-3b76017826b0
|
||||
github.com/mattn/go-runewidth v0.0.13 // indirect
|
||||
@@ -40,7 +40,7 @@ require (
|
||||
github.com/packer-community/winrmcp v0.0.0-20180921211025-c76d91c1e7db // indirect
|
||||
github.com/pkg/sftp v1.13.2 // indirect
|
||||
github.com/posener/complete v1.2.3
|
||||
github.com/stretchr/testify v1.8.4
|
||||
github.com/stretchr/testify v1.9.0
|
||||
github.com/ulikunitz/xz v0.5.10
|
||||
github.com/zclconf/go-cty v1.13.3
|
||||
github.com/zclconf/go-cty-yaml v1.0.1
|
||||
@@ -58,6 +58,7 @@ require (
|
||||
)
|
||||
|
||||
require (
|
||||
github.com/CycloneDX/cyclonedx-go v0.9.1
|
||||
github.com/go-openapi/strfmt v0.21.10
|
||||
github.com/oklog/ulid v1.3.1
|
||||
github.com/pierrec/lz4/v4 v4.1.18
|
||||
|
||||
@@ -20,6 +20,8 @@ github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym
|
||||
github.com/ChrisTrenkamp/goxpath v0.0.0-20170922090931-c385f95c6022/go.mod h1:nuWgzSkT5PnyOd+272uUmV0dnAnAn42Mk7PiQC5VzN4=
|
||||
github.com/ChrisTrenkamp/goxpath v0.0.0-20210404020558-97928f7e12b6 h1:w0E0fgc1YafGEh5cROhlROMWXiNoZqApk2PDN0M1+Ns=
|
||||
github.com/ChrisTrenkamp/goxpath v0.0.0-20210404020558-97928f7e12b6/go.mod h1:nuWgzSkT5PnyOd+272uUmV0dnAnAn42Mk7PiQC5VzN4=
|
||||
github.com/CycloneDX/cyclonedx-go v0.9.1 h1:yffaWOZsv77oTJa/SdVZYdgAgFioCeycBUKkqS2qzQM=
|
||||
github.com/CycloneDX/cyclonedx-go v0.9.1/go.mod h1:NE/EWvzELOFlG6+ljX/QeMlVt9VKcTwu8u0ccsACEsw=
|
||||
github.com/DataDog/datadog-go v3.2.0+incompatible/go.mod h1:LButxg5PwREeZtORoXG3tL4fMGNddJ+vMq1mwgfaqoQ=
|
||||
github.com/Masterminds/goutils v1.1.1 h1:5nUrii3FMTL5diU80unEVvNevw1nH4+ZV4DSLVJLSYI=
|
||||
github.com/Masterminds/goutils v1.1.1/go.mod h1:8cTjp+g8YejhMuvIA5y2vz3BpJxksy863GQaJW2MFNU=
|
||||
@@ -78,6 +80,8 @@ github.com/biogo/hts v1.4.3 h1:vir2yUTiRkPvtp6ZTpzh9lWTKQJZXJKZ563rpAQAsRM=
|
||||
github.com/biogo/hts v1.4.3/go.mod h1:eW40HJ1l2ExK9C+yvvoRSftInqWsf3ue+zAEjzCGWjA=
|
||||
github.com/bmatcuk/doublestar v1.1.5 h1:2bNwBOmhyFEFcoB3tGvTD5xanq+4kyOZlB8wFYbMjkk=
|
||||
github.com/bmatcuk/doublestar v1.1.5/go.mod h1:wiQtGV+rzVYxB7WIlirSN++5HPtPlXEo9MEoZQC/PmE=
|
||||
github.com/bradleyjkemp/cupaloy/v2 v2.8.0 h1:any4BmKE+jGIaMpnU8YgH/I2LPiLBufr6oMMlVBbn9M=
|
||||
github.com/bradleyjkemp/cupaloy/v2 v2.8.0/go.mod h1:bm7JXdkRd4BHJk9HpwqAI8BoAY1lps46Enkdqw6aRX0=
|
||||
github.com/bwesterb/go-ristretto v1.2.3/go.mod h1:fUIoIZaG73pV5biE2Blr2xEzDoMj7NFEuV9ekS419A0=
|
||||
github.com/cenkalti/backoff/v3 v3.2.2 h1:cfUAAO3yvKMYKPrvhDuHSwQnhZNk/RMHKdZqKTxfm6M=
|
||||
github.com/cenkalti/backoff/v3 v3.2.2/go.mod h1:cIeZDE3IrqwwJl6VUwCN6trj1oXrTS4rc0ij+ULvLYs=
|
||||
@@ -502,8 +506,9 @@ github.com/spf13/cast v1.3.1/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkU
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
|
||||
github.com/stretchr/objx v0.5.0 h1:1zr/of2m5FGMsad5YfcqgdqdWrIhu+EBEJRhR1U7z/c=
|
||||
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
|
||||
github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY=
|
||||
github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA=
|
||||
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
|
||||
@@ -515,8 +520,10 @@ github.com/stretchr/testify v1.7.2/go.mod h1:R6va5+xMeoiuVRoj+gSkQ7d3FALtqAAGI1F
|
||||
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
|
||||
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
|
||||
github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
|
||||
github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=
|
||||
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
|
||||
github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
|
||||
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
|
||||
github.com/terminalstatic/go-xsd-validate v0.1.5 h1:RqpJnf6HGE2CB/lZB1A8BYguk8uRtcvYAPLCF15qguo=
|
||||
github.com/terminalstatic/go-xsd-validate v0.1.5/go.mod h1:18lsvYFofBflqCrvo1umpABZ99+GneNTw2kEEc8UPJw=
|
||||
github.com/tklauser/go-sysconf v0.3.11 h1:89WgdJhk5SNwJfu+GKyYveZ4IaJ7xAkecBo+KdJV0CM=
|
||||
github.com/tklauser/go-sysconf v0.3.11/go.mod h1:GqXfhXY3kiPa0nAXPDIQIWzJbMCB7AmcWpGR8lSZfqI=
|
||||
github.com/tklauser/numcpus v0.6.0 h1:kebhY2Qt+3U6RNK7UqpYNA+tJ23IBEGKkB7JQBfDYms=
|
||||
@@ -537,6 +544,10 @@ github.com/xdg-go/scram v1.1.2/go.mod h1:RT/sEzTbU5y00aCK8UOx6R7YryM0iF1N2MOmC3k
|
||||
github.com/xdg-go/stringprep v1.0.4/go.mod h1:mPGuuIYwz7CmR2bT9j4GbQqutWS1zV24gijq1dTyGkM=
|
||||
github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb h1:zGWFAtiMcyryUHoUjUJX0/lt1H2+i2Ka2n+D3DImSNo=
|
||||
github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU=
|
||||
github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 h1:EzJWgHovont7NscjpAxXsDA8S8BMYve8Y5+7cuRE7R0=
|
||||
github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415/go.mod h1:GwrjFmJcFw6At/Gs6z4yjiIwzuJ1/+UwLxMQDVQXShQ=
|
||||
github.com/xeipuuv/gojsonschema v1.2.0 h1:LhYJRs+L4fBtjZUfuSZIKGeVu0QRy8e5Xi7D17UxZ74=
|
||||
github.com/xeipuuv/gojsonschema v1.2.0/go.mod h1:anYRn/JVcOK2ZgGU+IjEV4nwlhoK5sQluxsYJ78Id3Y=
|
||||
github.com/youmark/pkcs8 v0.0.0-20181117223130-1be2e3e5546d/go.mod h1:rHwXgn7JulP+udvsHwJoVG1YGAP6VLg4y9I5dyZdqmA=
|
||||
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
|
||||
github.com/yusufpapurcu/wmi v1.2.2 h1:KBNDSne4vP5mbSWnJbO+51IMOXJB67QiYCSBrubbPRg=
|
||||
|
||||
+3
-6
@@ -304,12 +304,9 @@ func (b *CoreBuild) Run(ctx context.Context, originalUi packersdk.Ui) ([]packers
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if len(b.Provisioners) > 0 {
|
||||
for _, p := range b.Provisioners {
|
||||
sbomInternalProvisioner, ok := p.Provisioner.(*SBOMInternalProvisioner)
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
for _, p := range b.Provisioners {
|
||||
sbomInternalProvisioner, ok := p.Provisioner.(*SBOMInternalProvisioner)
|
||||
if ok {
|
||||
b.SBOMFilesCompressed = append(b.SBOMFilesCompressed, sbomInternalProvisioner.CompressedData)
|
||||
}
|
||||
}
|
||||
|
||||
+14
-18
@@ -6,7 +6,6 @@ package packer
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"io"
|
||||
"log"
|
||||
"os"
|
||||
|
||||
@@ -244,7 +243,6 @@ func (p *DebuggedProvisioner) Provision(ctx context.Context, ui packersdk.Ui, co
|
||||
// press before the provisioner is actually run.
|
||||
type SBOMInternalProvisioner struct {
|
||||
Provisioner packersdk.Provisioner
|
||||
TempFileLoc string
|
||||
CompressedData []byte
|
||||
}
|
||||
|
||||
@@ -269,23 +267,28 @@ func (p *SBOMInternalProvisioner) Provision(
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to create internal temporary file for Packer SBOM: %s", err)
|
||||
}
|
||||
defer tmpFile.Close()
|
||||
|
||||
// Close the file handle before passing the name to the underlying provisioner
|
||||
tmpFileName := tmpFile.Name()
|
||||
if err = tmpFile.Close(); err != nil {
|
||||
return fmt.Errorf("failed to close temporary file for Packer SBOM %s: %s", tmpFileName, err)
|
||||
}
|
||||
|
||||
defer func(name string) {
|
||||
fileRemoveErr := os.Remove(name)
|
||||
if fileRemoveErr != nil {
|
||||
log.Printf("Error removing SBOM temporary file %s: %s", name, fileRemoveErr)
|
||||
}
|
||||
}(p.TempFileLoc)
|
||||
}(tmpFile.Name())
|
||||
|
||||
generatedData["dst"] = tmpFile.Name()
|
||||
p.TempFileLoc = tmpFile.Name()
|
||||
|
||||
err = p.Provisioner.Provision(ctx, ui, comm, generatedData)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
compressedData, err := p.compressFile(p.TempFileLoc)
|
||||
compressedData, err := p.compressFile(tmpFile.Name())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -294,25 +297,18 @@ func (p *SBOMInternalProvisioner) Provision(
|
||||
}
|
||||
|
||||
func (p *SBOMInternalProvisioner) compressFile(filePath string) ([]byte, error) {
|
||||
sourceFile, err := os.Open(filePath)
|
||||
data, err := os.ReadFile(filePath)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer sourceFile.Close()
|
||||
|
||||
data, err := io.ReadAll(sourceFile)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, fmt.Errorf("failed to read file %s: %w", filePath, err)
|
||||
}
|
||||
|
||||
encoder, err := zstd.NewWriter(nil)
|
||||
encoder, err := zstd.NewWriter(nil, zstd.WithEncoderLevel(zstd.SpeedBestCompression))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, fmt.Errorf("failed to create zstd encoder: %w", err)
|
||||
}
|
||||
defer encoder.Close()
|
||||
|
||||
compressedData := encoder.EncodeAll(data, nil)
|
||||
|
||||
fmt.Printf(fmt.Sprintf("SBOM file compressed successfully. Size: %d bytes", len(compressedData)))
|
||||
log.Printf("SBOM file compressed successfully. Size: %d bytes\n", len(compressedData))
|
||||
return compressedData, nil
|
||||
}
|
||||
|
||||
@@ -8,26 +8,40 @@ package hcp_sbom
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"fmt"
|
||||
"log"
|
||||
"os"
|
||||
|
||||
"github.com/CycloneDX/cyclonedx-go"
|
||||
"github.com/hashicorp/hcl/v2/hcldec"
|
||||
"github.com/hashicorp/packer-plugin-sdk/common"
|
||||
packersdk "github.com/hashicorp/packer-plugin-sdk/packer"
|
||||
"github.com/hashicorp/packer-plugin-sdk/template/config"
|
||||
"github.com/hashicorp/packer-plugin-sdk/template/interpolate"
|
||||
|
||||
"path/filepath"
|
||||
)
|
||||
|
||||
type Config struct {
|
||||
common.PackerConfig `mapstructure:",squash"`
|
||||
Source string `mapstructure:"source" required:"true"`
|
||||
Destination string `mapstructure:"destination"`
|
||||
ctx interpolate.Context
|
||||
|
||||
// Source is a required field that specifies the path to the SBOM file that
|
||||
// needs to be downloaded.
|
||||
// It can be a file path or a URL.
|
||||
Source string `mapstructure:"source" required:"true"`
|
||||
// Destination is an optional field that specifies the path where the SBOM
|
||||
// file will be downloaded to for the user.
|
||||
// The 'Destination' must be a writable location. If the destination is a file,
|
||||
// the SBOM will be saved or overwritten at that path. If the destination is
|
||||
// a directory, a file will be created within the directory to store the SBOM.
|
||||
// Any parent directories for the destination must already exist and be
|
||||
// writable by the provisioning user (generally not root), otherwise,
|
||||
// a "Permission Denied" error will occur. If the source path is a file,
|
||||
// it is recommended that the destination path be a file as well.
|
||||
Destination string `mapstructure:"destination"`
|
||||
ctx interpolate.Context
|
||||
}
|
||||
|
||||
type Provisioner struct {
|
||||
@@ -67,7 +81,7 @@ func (p *Provisioner) Provision(
|
||||
ctx context.Context, ui packersdk.Ui, comm packersdk.Communicator,
|
||||
generatedData map[string]interface{},
|
||||
) error {
|
||||
ui.Say(
|
||||
log.Printf(
|
||||
fmt.Sprintf("Starting to provision with hcp-sbom using source: %s",
|
||||
p.config.Source,
|
||||
),
|
||||
@@ -81,14 +95,17 @@ func (p *Provisioner) Provision(
|
||||
// Download the file for Packer
|
||||
destPath, downloadErr := p.downloadSBOMForPacker(ui, comm, generatedData)
|
||||
if downloadErr != nil {
|
||||
return fmt.Errorf("failed to download file: %w", downloadErr)
|
||||
return fmt.Errorf("failed to download Packer SBOM file: %w", downloadErr)
|
||||
}
|
||||
|
||||
// Download the file for user
|
||||
p.downloadSBOMForUser(ui, comm)
|
||||
downloadErr = p.downloadSBOMForUser(ui, comm)
|
||||
if downloadErr != nil {
|
||||
return fmt.Errorf("failed to download User SBOM file: %w", downloadErr)
|
||||
}
|
||||
|
||||
// Validate the file
|
||||
ui.Say(fmt.Sprintf("Validating SBOM file %s", destPath))
|
||||
log.Printf(fmt.Sprintf("Validating SBOM file: %s\n", destPath))
|
||||
validationErr := p.validateSBOM(ui, destPath)
|
||||
if validationErr != nil {
|
||||
return fmt.Errorf("failed to validate SBOM file: %w", validationErr)
|
||||
@@ -108,40 +125,27 @@ func (p *Provisioner) downloadSBOMForPacker(
|
||||
return p.config.Destination, fmt.Errorf("error interpolating source: %s", err)
|
||||
}
|
||||
|
||||
// FIXME:: Do we really need this?
|
||||
// Check if the source is a JSON file
|
||||
if filepath.Ext(src) != ".json" {
|
||||
return p.config.Destination, fmt.Errorf(
|
||||
"packer SBOM source file is not a JSON file: %s", src,
|
||||
)
|
||||
}
|
||||
|
||||
// Download the file for Packer
|
||||
desti, ok := generatedData["dst"] // this has been set by HCPSBOMInternalProvisioner.Provision
|
||||
if !ok {
|
||||
return "", fmt.Errorf("failed to find location for Packer SBOM file")
|
||||
dst, ok := generatedData["dst"].(string) // this has been set by HCPSBOMInternalProvisioner.Provision
|
||||
if !ok || dst == "" {
|
||||
return "", fmt.Errorf("destination path for Packer SBOM file is not valid")
|
||||
}
|
||||
|
||||
dst := fmt.Sprintf("%v", desti)
|
||||
// Ensure the destination directory exists
|
||||
dir := filepath.Dir(dst)
|
||||
if err := os.MkdirAll(dir, os.FileMode(0755)); err != nil {
|
||||
return dst, fmt.Errorf("failed to create destination directory for Packer SBOM: %s", err)
|
||||
if err := os.MkdirAll(filepath.Dir(dst), 0755); err != nil {
|
||||
return dst, fmt.Errorf("failed to create destination directory for Packer SBOM: %w", err)
|
||||
}
|
||||
|
||||
// Open the destination file
|
||||
f, err := os.OpenFile(dst, os.O_CREATE|os.O_WRONLY|os.O_TRUNC, 0644)
|
||||
if err != nil {
|
||||
return dst, fmt.Errorf("failed to open destination file: %s", err)
|
||||
return dst, fmt.Errorf("failed to open destination file for Packer SBOM: %s", err)
|
||||
}
|
||||
defer f.Close()
|
||||
|
||||
// Create MultiWriter for the current progress
|
||||
pf := io.MultiWriter(f)
|
||||
|
||||
// Download the file
|
||||
ui.Say(fmt.Sprintf("Downloading SBOM file %s for Packer => %s", src, dst))
|
||||
if err = comm.Download(src, pf); err != nil {
|
||||
if err = comm.Download(src, f); err != nil {
|
||||
ui.Error(fmt.Sprintf("download failed for Packer SBOM file: %s", err))
|
||||
return dst, err
|
||||
}
|
||||
@@ -149,101 +153,84 @@ func (p *Provisioner) downloadSBOMForPacker(
|
||||
return dst, nil
|
||||
}
|
||||
|
||||
// downloadSBOMForUser downloads a SBOM from a specified source to a local
|
||||
// destination given by user. It works with all communicators from packersdk.
|
||||
// downloadSBOMForUser downloads a Software Bill of Materials (SBOM) file from a specified source
|
||||
// to a local destination path on the machine.
|
||||
func (p *Provisioner) downloadSBOMForUser(
|
||||
ui packersdk.Ui, comm packersdk.Communicator,
|
||||
) {
|
||||
src, err := interpolate.Render(p.config.Source, &p.config.ctx)
|
||||
if err != nil {
|
||||
ui.Say(fmt.Sprintf("error interpolating source: %s", err))
|
||||
return
|
||||
}
|
||||
|
||||
// Determine the destination path
|
||||
) error {
|
||||
dst := p.config.Destination
|
||||
if dst == "" {
|
||||
ui.Say("skipped downloading SBOM file for user because 'Destination' is not provided")
|
||||
return
|
||||
log.Println("skipped downloading user SBOM file because 'Destination' is not provided")
|
||||
return nil
|
||||
}
|
||||
|
||||
dst, err = interpolate.Render(dst, &p.config.ctx)
|
||||
dst, err := interpolate.Render(dst, &p.config.ctx)
|
||||
if err != nil {
|
||||
ui.Say(fmt.Sprintf("error interpolating SBOM file destination: %s", err))
|
||||
return
|
||||
return fmt.Errorf("error interpolating SBOM file destination from user: %s\n", err)
|
||||
}
|
||||
|
||||
if strings.HasSuffix(dst, "/") {
|
||||
info, err := os.Stat(dst)
|
||||
if err != nil {
|
||||
ui.Say(fmt.Sprintf("failed to stat destination for SBOM: %s", err))
|
||||
return
|
||||
}
|
||||
src, err := interpolate.Render(p.config.Source, &p.config.ctx)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error interpolating source: %s", err)
|
||||
}
|
||||
|
||||
if info.IsDir() {
|
||||
tmpFile, err := os.CreateTemp(dst, "packer-user-sbom-*.json")
|
||||
if err != nil {
|
||||
ui.Say(fmt.Sprintf("failed to create file for Packer SBOM: %s", err))
|
||||
return
|
||||
// Check if the destination exists and determine its type
|
||||
info, err := os.Stat(dst)
|
||||
if err != nil {
|
||||
if os.IsNotExist(err) {
|
||||
// If destination doesn't exist, assume it's a file path and ensure parent directories are created
|
||||
dir := filepath.Dir(dst)
|
||||
if err := os.MkdirAll(dir, 0755); err != nil {
|
||||
return fmt.Errorf("failed to create destination directory for user SBOM: %s\n", err)
|
||||
}
|
||||
dst = tmpFile.Name()
|
||||
tmpFile.Close()
|
||||
} else {
|
||||
return fmt.Errorf("failed to stat destination for user SBOM: %s\n", err)
|
||||
}
|
||||
}
|
||||
|
||||
// Ensure the destination directory exists
|
||||
dir := filepath.Dir(dst)
|
||||
if err := os.MkdirAll(dir, os.FileMode(0755)); err != nil {
|
||||
ui.Say(fmt.Sprintf("failed to create destination directory for Packer SBOM: %s", err))
|
||||
return
|
||||
} else if info.IsDir() {
|
||||
// If the destination is a directory, create a temporary file inside it
|
||||
tmpFile, err := os.CreateTemp(dst, "packer-user-sbom-*.json")
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to create temporary file in user SBOM directory %s: %s", dst, err)
|
||||
}
|
||||
dst = tmpFile.Name()
|
||||
tmpFile.Close()
|
||||
}
|
||||
|
||||
// Open the destination file
|
||||
f, err := os.OpenFile(dst, os.O_CREATE|os.O_WRONLY|os.O_TRUNC, 0644)
|
||||
if err != nil {
|
||||
ui.Say(fmt.Sprintf("failed to open destination file: %s", err))
|
||||
return
|
||||
return fmt.Errorf("failed to open destination file for user SBOM: %s", err)
|
||||
}
|
||||
defer f.Close()
|
||||
|
||||
// Create MultiWriter for the current progress
|
||||
pf := io.MultiWriter(f)
|
||||
|
||||
// Download the file
|
||||
ui.Say(fmt.Sprintf("Downloading SBOM file for user %s => %s", src, dst))
|
||||
if err = comm.Download(src, pf); err != nil {
|
||||
ui.Error(fmt.Sprintf("download failed for user SBOM file: %s", err))
|
||||
return
|
||||
if err = comm.Download(src, f); err != nil {
|
||||
return fmt.Errorf("download failed for user SBOM file: %s", err)
|
||||
}
|
||||
|
||||
ui.Say(fmt.Sprintf("User SBOM file successfully downloaded to: %s\n", dst))
|
||||
return nil
|
||||
}
|
||||
|
||||
type SBOM struct {
|
||||
BomFormat string `json:"bomFormat"`
|
||||
SpecVersion string `json:"specVersion"`
|
||||
}
|
||||
|
||||
// validateSBOM validates CycloneDX SBOM files
|
||||
func (p *Provisioner) validateSBOM(ui packersdk.Ui, filePath string) error {
|
||||
sourceFile, err := os.Open(filePath)
|
||||
if err != nil {
|
||||
return err
|
||||
return fmt.Errorf("failed to open file %s: %w", filePath, err)
|
||||
}
|
||||
defer sourceFile.Close()
|
||||
|
||||
data, err := io.ReadAll(sourceFile)
|
||||
if err != nil {
|
||||
return err
|
||||
decoder := cyclonedx.NewBOMDecoder(sourceFile, cyclonedx.BOMFileFormatJSON)
|
||||
bom := new(cyclonedx.BOM)
|
||||
if err := decoder.Decode(bom); err != nil {
|
||||
return fmt.Errorf("failed to decode CycloneDX SBOM: %w", err)
|
||||
}
|
||||
|
||||
var sbom SBOM
|
||||
if err := json.Unmarshal(data, &sbom); err != nil {
|
||||
return fmt.Errorf("failed to unmarshal JSON: %w", err)
|
||||
if bom.BOMFormat != "CycloneDX" {
|
||||
return fmt.Errorf("invalid bomFormat: %s, expected CycloneDX", bom.BOMFormat)
|
||||
}
|
||||
|
||||
if sbom.BomFormat != "CycloneDX" {
|
||||
return fmt.Errorf("invalid bomFormat: %s", sbom.BomFormat)
|
||||
}
|
||||
|
||||
if sbom.SpecVersion == "" {
|
||||
if bom.SpecVersion.String() == "" {
|
||||
return fmt.Errorf("specVersion is required")
|
||||
}
|
||||
|
||||
|
||||
@@ -136,35 +136,35 @@ func TestValidateSBOM(t *testing.T) {
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
sbom SBOM
|
||||
sbom map[string]interface{}
|
||||
expectError bool
|
||||
errorMsg string
|
||||
}{
|
||||
{
|
||||
name: "Valid SBOM",
|
||||
sbom: SBOM{
|
||||
BomFormat: "CycloneDX",
|
||||
SpecVersion: "1.0",
|
||||
sbom: map[string]interface{}{
|
||||
"bomFormat": "CycloneDX",
|
||||
"specVersion": "1.0",
|
||||
},
|
||||
expectError: false,
|
||||
},
|
||||
{
|
||||
name: "Invalid BomFormat",
|
||||
sbom: SBOM{
|
||||
BomFormat: "InvalidFormat",
|
||||
SpecVersion: "1.0",
|
||||
sbom: map[string]interface{}{
|
||||
"bomFormat": "InvalidFormat",
|
||||
"specVersion": "1.0",
|
||||
},
|
||||
expectError: true,
|
||||
errorMsg: "invalid bomFormat: InvalidFormat",
|
||||
errorMsg: "invalid bomFormat: InvalidFormat, expected CycloneDX",
|
||||
},
|
||||
{
|
||||
name: "Empty SpecVersion",
|
||||
sbom: SBOM{
|
||||
BomFormat: "CycloneDX",
|
||||
SpecVersion: "",
|
||||
sbom: map[string]interface{}{
|
||||
"bomFormat": "CycloneDX",
|
||||
"specVersion": "",
|
||||
},
|
||||
expectError: true,
|
||||
errorMsg: "specVersion is required",
|
||||
errorMsg: "failed to decode CycloneDX SBOM: invalid specification version",
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user