Skip to content

Commit

Permalink
feat: support storing file paths relative to module root (#412)
Browse files Browse the repository at this point in the history
* Add -paths option for storing absolute file paths when -files is enabled

Signed-off-by: Alexey Vishnyakov <sweetvishnya@yandex-team.ru>

* Concertize file:path property

Signed-off-by: Alexey Vishnyakov <sweetvishnya@yandex-team.ru>

* Compute relative paths to module root

Signed-off-by: Alexey Vishnyakov <sweetvishnya@yandex-team.ru>

---------

Signed-off-by: Alexey Vishnyakov <sweetvishnya@yandex-team.ru>
  • Loading branch information
SweetVishnya committed Jan 30, 2024
1 parent 3a793cd commit 6b624c3
Show file tree
Hide file tree
Showing 7 changed files with 51 additions and 9 deletions.
11 changes: 7 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -113,9 +113,11 @@ In order to not only include modules, but also the packages within them,
the -packages flag can be used. Packages are represented as subcomponents of modules.
By passing -files, all files that would be included in a binary will be attached
as subcomponents of their respective package. File versions follow the v0.0.0-SHORTHASH pattern,
as subcomponents of their respective package. File versions follow the v0.0.0-SHORTHASH pattern,
where SHORTHASH is the first 12 characters of the file's SHA1 hash.
Because files are subcomponents of packages, -files can only be used in conjunction with -packages.
When -paths option is additionally enabled, each file would have a property with
a file path relative to its module root.
Licenses detected via -licenses flag will, per default, be reported as evidence.
This is because it can not be guaranteed that the detected licenses are in fact correct.
Expand All @@ -127,19 +129,20 @@ For documentation on the respective fields of the CycloneDX specification, refer
Examples:
$ GOARCH=arm64 GOOS=linux GOFLAGS="-tags=foo,bar" cyclonedx-gomod app -output linux-arm64.bom.xml
$ cyclonedx-gomod app -json -output acme-app.bom.json -files -licenses -main cmd/acme-app /usr/src/acme-module
$ cyclonedx-gomod app -json -output acme-app.bom.json -packages -files -licenses -main cmd/acme-app /usr/src/acme-module
FLAGS
-assert-licenses=false Assert detected licenses
-files=false Include files
-json=false Output in JSON
-licenses=false Perform license detection
-main ... Path to the application's main package, relative to MODULE_PATH
-main string Path to the application's main package, relative to MODULE_PATH
-noserial=false Omit serial number
-output - Output file path (or - for STDOUT)
-output-version 1.4 Output spec verson (1.4, 1.3, 1.2, 1.1, 1.0)
-packages=false Include packages
-serial ... Serial number
-paths=false Include file paths relative to their module root
-serial string Serial number
-std=false Include Go standard library as component and dependency of the module
-verbose=false Enable verbose output
```
Expand Down
5 changes: 4 additions & 1 deletion internal/cli/cmd/app/app.go
Original file line number Diff line number Diff line change
Expand Up @@ -68,9 +68,11 @@ In order to not only include modules, but also the packages within them,
the -packages flag can be used. Packages are represented as subcomponents of modules.
By passing -files, all files that would be included in a binary will be attached
as subcomponents of their respective package. File versions follow the v0.0.0-SHORTHASH pattern,
as subcomponents of their respective package. File versions follow the v0.0.0-SHORTHASH pattern,
where SHORTHASH is the first 12 characters of the file's SHA1 hash.
Because files are subcomponents of packages, -files can only be used in conjunction with -packages.
When -paths option is additionally enabled, each file would have a property with
a file path relative to its module root.
Licenses detected via -licenses flag will, per default, be reported as evidence.
This is because it can not be guaranteed that the detected licenses are in fact correct.
Expand Down Expand Up @@ -116,6 +118,7 @@ func Exec(options Options) error {
app.WithLogger(logger),
app.WithIncludeFiles(options.IncludeFiles),
app.WithIncludePackages(options.IncludePackages),
app.WithIncludePaths(options.IncludePaths),
app.WithIncludeStdlib(options.IncludeStd),
app.WithLicenseDetector(licenseDetector),
app.WithMainDir(options.Main))
Expand Down
6 changes: 6 additions & 0 deletions internal/cli/cmd/app/options.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ type Options struct {

IncludeFiles bool
IncludePackages bool
IncludePaths bool
Main string
ModuleDir string
}
Expand All @@ -49,6 +50,7 @@ func (o *Options) RegisterFlags(fs *flag.FlagSet) {

fs.BoolVar(&o.IncludeFiles, "files", false, "Include files")
fs.BoolVar(&o.IncludePackages, "packages", false, "Include packages")
fs.BoolVar(&o.IncludePaths, "paths", false, "Include file paths relative to their module root")
fs.StringVar(&o.Main, "main", "", "Path to the application's main package, relative to MODULE_PATH")
}

Expand Down Expand Up @@ -76,6 +78,10 @@ func (o Options) Validate() error {
errs = append(errs, fmt.Errorf("including files without including packages is not supported"))
}

if o.IncludePaths && !o.IncludeFiles {
errs = append(errs, fmt.Errorf("including paths without including files is not supported"))
}

err := o.validateMain(o.Main, &errs)
if err != nil {
return err
Expand Down
20 changes: 19 additions & 1 deletion internal/sbom/convert/file/file.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,10 +19,13 @@ package file

import (
"fmt"
"os"
"strings"

cdx "github.com/CycloneDX/cyclonedx-go"
"github.com/rs/zerolog"

"github.com/CycloneDX/cyclonedx-gomod/internal/gomod"
"github.com/CycloneDX/cyclonedx-gomod/internal/sbom"
)

Expand All @@ -40,7 +43,7 @@ func WithHashes(algos ...cdx.HashAlgorithm) Option {
}
}

func ToComponent(logger zerolog.Logger, absFilePath, relFilePath string, options ...Option) (*cdx.Component, error) {
func ToComponent(logger zerolog.Logger, absFilePath, relFilePath string, pathEnabled bool, module gomod.Module, options ...Option) (*cdx.Component, error) {
logger.Debug().
Str("file", absFilePath).
Msg("converting file to component")
Expand All @@ -57,6 +60,21 @@ func ToComponent(logger zerolog.Logger, absFilePath, relFilePath string, options
}
component.Version = fmt.Sprintf("v0.0.0-%s", hashes[0].Value[:12])

if pathEnabled {
if component.Properties == nil {
component.Properties = &[]cdx.Property{}
}
trimmedPath := strings.TrimPrefix(absFilePath, module.Dir)
if trimmedPath == absFilePath {
workDir, err := os.Getwd()
if err != nil {
return nil, err
}
trimmedPath = strings.TrimPrefix(absFilePath, workDir)
}
*component.Properties = append(*component.Properties, sbom.NewProperty("file:path", trimmedPath))
}

for _, option := range options {
err = option(logger, absFilePath, relFilePath, &component)
if err != nil {
Expand Down
4 changes: 3 additions & 1 deletion internal/sbom/convert/pkg/package.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ import (

type Option func(zerolog.Logger, gomod.Package, gomod.Module, *cdx.Component) error

func WithFiles(enabled bool) Option {
func WithFiles(enabled bool, pathEnabled bool) Option {
return func(logger zerolog.Logger, pkg gomod.Package, module gomod.Module, component *cdx.Component) error {
if !enabled {
return nil
Expand All @@ -57,6 +57,8 @@ func WithFiles(enabled bool) Option {
logger,
filepath.Join(pkg.Dir, file),
file,
pathEnabled,
module,
fileConv.WithHashes(
cdx.HashAlgoMD5,
cdx.HashAlgoSHA1,
Expand Down
5 changes: 3 additions & 2 deletions pkg/generate/app/generator.go
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ type generator struct {

includeFiles bool
includePackages bool
includePaths bool
includeStdlib bool
licenseDetector licensedetect.Detector
mainDir string
Expand Down Expand Up @@ -98,7 +99,7 @@ func (g generator) Generate() (*cdx.BOM, error) {
modConv.WithComponentType(cdx.ComponentTypeApplication),
modConv.WithLicenses(g.licenseDetector),
modConv.WithPackages(g.includePackages,
pkgConv.WithFiles(g.includeFiles)),
pkgConv.WithFiles(g.includeFiles, g.includePaths)),
)
if err != nil {
return nil, fmt.Errorf("failed to convert main module: %w", err)
Expand All @@ -118,7 +119,7 @@ func (g generator) Generate() (*cdx.BOM, error) {
modConv.WithLicenses(g.licenseDetector),
modConv.WithModuleHashes(),
modConv.WithPackages(g.includePackages,
pkgConv.WithFiles(g.includeFiles)),
pkgConv.WithFiles(g.includeFiles, g.includePaths)),
)
if err != nil {
return nil, fmt.Errorf("failed to convert modules: %w", err)
Expand Down
9 changes: 9 additions & 0 deletions pkg/generate/app/options.go
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,15 @@ func WithIncludePackages(enable bool) Option {
}
}

// WithIncludePaths toggles the inclusion of file paths.
// Has no effect when files are not included as well.
func WithIncludePaths(enable bool) Option {
return func(g *generator) error {
g.includePaths = enable
return nil
}
}

// WithIncludeStdlib toggles the inclusion of a std component
// representing the Go standard library in the generated BOM.
//
Expand Down

0 comments on commit 6b624c3

Please sign in to comment.