Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support archives as package src, autonamespace and other improvements #4

Merged
merged 11 commits into from
Nov 29, 2021
53 changes: 27 additions & 26 deletions cmd/bom/cmd/generate.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@ package cmd

import (
"fmt"
"net/url"
"os"

"github.com/pkg/errors"
Expand Down Expand Up @@ -78,7 +77,8 @@ type generateOptions struct {
configFile string
license string
images []string
tarballs []string
imageArchives []string
archives []string
files []string
directories []string
ignorePatterns []string
Expand All @@ -89,32 +89,12 @@ func (opts *generateOptions) Validate() error {
if opts.configFile == "" &&
len(opts.images) == 0 &&
len(opts.files) == 0 &&
len(opts.tarballs) == 0 &&
len(opts.imageArchives) == 0 &&
len(opts.archives) == 0 &&
len(opts.directories) == 0 {
return errors.New("to generate a SPDX BOM you have to provide at least one image or file")
}

// A namespace URL is required
if opts.configFile == "" && opts.namespace == "" {
msg := "Error. No namespace defined\n\n"
msg += "You did not specify a namespace for your document. This is an error.\n"
msg += "To produce a valid SPDX SBOM, the document has to have an URI as its\n"
msg += "namespace.\n\nIf you are testing, you can use http://example.com/ for now but your\n"
msg += "final document must have a namespace URI pointing to the location where\n"
msg += "your SBOM will be referenced in the future.\n\n"
msg += "For more details, check the SPDX documentation here:\n"
msg += "https://spdx.github.io/spdx-spec/2-document-creation-information/#25-spdx-document-namespace\n\n"
msg += "Hint: --namespace is your friend here\n\n"
logrus.Info(msg)

return errors.New("A namespace URI must be defined to have a compliant SPDX BOM")
}

// Check namespace is a valid URL
if _, err := url.Parse(opts.namespace); err != nil {
return errors.Wrap(err, "parsing the namespace URL")
}

return nil
}

Expand All @@ -136,13 +116,33 @@ func init() {
)

generateCmd.PersistentFlags().StringSliceVarP(
&genOpts.tarballs,
&genOpts.imageArchives,
"tarball",
"t",
[]string{},
"list of docker archive tarballs to include in the manifest",
)

if err := generateCmd.PersistentFlags().MarkDeprecated(
"tarball", "tarball has been renamed to image-archive",
); err != nil {
logrus.Fatal(errors.Wrap(err, "marking flag as deprecated"))
}

generateCmd.PersistentFlags().StringSliceVar(
&genOpts.imageArchives,
"image-archive",
[]string{},
"list of docker archive tarballs to include in the manifest",
)

generateCmd.PersistentFlags().StringSliceVar(
&genOpts.archives,
"archive",
[]string{},
"list of archives to add as packages (supports tar, tar.gz)",
)

generateCmd.PersistentFlags().StringSliceVarP(
&genOpts.directories,
"dirs",
Expand Down Expand Up @@ -228,7 +228,8 @@ func generateBOM(opts *generateOptions) error {

builder := spdx.NewDocBuilder()
builderOpts := &spdx.DocGenerateOptions{
Tarballs: opts.tarballs,
Tarballs: opts.imageArchives,
Archives: opts.archives,
Files: opts.files,
Images: opts.images,
Directories: opts.directories,
Expand Down
36 changes: 29 additions & 7 deletions pkg/spdx/builder.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import (
"os"
"path/filepath"

"github.com/google/uuid"
"github.com/pkg/errors"
"github.com/sirupsen/logrus"
"gopkg.in/yaml.v2"
Expand Down Expand Up @@ -96,7 +97,8 @@ type DocGenerateOptions struct {
Namespace string // Namespace for the document (a unique URI)
CreatorPerson string // Document creator information
License string // Main license of the document
Tarballs []string // A slice of tar paths
Tarballs []string // A slice of docker archives (tar)
Archives []string // A list of archive files to add as packages
Files []string // A slice of naked files to include in the bom
Images []string // A slice of docker images
Directories []string // A slice of directories to convert into packages
Expand All @@ -105,7 +107,11 @@ type DocGenerateOptions struct {
}

func (o *DocGenerateOptions) Validate() error {
if len(o.Tarballs) == 0 && len(o.Files) == 0 && len(o.Images) == 0 && len(o.Directories) == 0 {
if len(o.Tarballs) == 0 &&
len(o.Files) == 0 &&
len(o.Images) == 0 &&
len(o.Directories) == 0 &&
len(o.Archives) == 0 {
return errors.New(
"to build a document at least an image, tarball, directory or a file has to be specified",
)
Expand Down Expand Up @@ -164,13 +170,17 @@ func (builder *defaultDocBuilderImpl) GenerateDoc(
// Create the new document
doc = NewDocument()
doc.Name = genopts.Name
doc.Namespace = genopts.Namespace
doc.Creator.Person = genopts.CreatorPerson
doc.ExternalDocRefs = genopts.ExternalDocumentRef

// If we do not have a namespace, we generate one
// under the public SPDX URL defined in the spec.
// (ref https://spdx.github.io/spdx-spec/document-creation-information/#65-spdx-document-namespace-field)
if genopts.Namespace == "" {
return nil, errors.New("unable to generate doc, namespace URI is not defined")
doc.Namespace = "https://spdx.org/spdxdocs/k8s-releng-bom-" + uuid.NewString()
} else {
doc.Namespace = genopts.Namespace
}
doc.Creator.Person = genopts.CreatorPerson
doc.ExternalDocRefs = genopts.ExternalDocumentRef

for _, i := range genopts.Directories {
logrus.Infof("Processing directory %s", i)
Expand All @@ -196,7 +206,7 @@ func (builder *defaultDocBuilderImpl) GenerateDoc(
}
}

// Porcess OCI image archives
// Process OCI image archives
for _, tb := range genopts.Tarballs {
logrus.Infof("Processing tarball %s", tb)
p, err := spdx.PackageFromImageTarball(tb)
Expand All @@ -208,6 +218,18 @@ func (builder *defaultDocBuilderImpl) GenerateDoc(
}
}

// Add archive files as packages
for _, tf := range genopts.Archives {
logrus.Infof("Adding archive file as package: %s", tf)
p, err := spdx.PackageFromArchive(tf)
if err != nil {
return nil, errors.Wrap(err, "creating spdx package from archive")
}
if err := doc.AddPackage(p); err != nil {
return nil, errors.Wrap(err, "adding package to document")
}
}

// Process single files, not part of a package
for _, f := range genopts.Files {
logrus.Infof("Processing file %s", f)
Expand Down
18 changes: 17 additions & 1 deletion pkg/spdx/file.go
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,13 @@ func (f *File) Render() (docFragment string, err error) {

// BuildID sets the file ID, optionally from a series of strings
func (f *File) BuildID(seeds ...string) {
f.Entity.BuildID(append([]string{"SPDXRef-File"}, seeds...)...)
prefix := ""
if f.Options() != nil {
if f.Options().Prefix != "" {
prefix = "-" + f.Options().Prefix
}
}
f.Entity.BuildID(append([]string{"SPDXRef-File" + prefix}, seeds...)...)
}

func (f *File) SetEntity(e *Entity) {
Expand All @@ -102,3 +108,13 @@ func (f *File) Draw(builder *strings.Builder, o *DrawingOptions, depth int, seen
}
fmt.Fprintf(builder, treeLines(o, depth, connector)+"%s (%s)\n", f.SPDXID(), f.Name)
}

func (f *File) ReadSourceFile(path string) error {
if err := f.Entity.ReadSourceFile(path); err != nil {
return err
}
if f.SPDXID() == "" {
f.BuildID()
}
return nil
}
Loading