Skip to content

Commit

Permalink
fix and improve code documentation; update changelog
Browse files Browse the repository at this point in the history
Signed-off-by: nscuro <[email protected]>
  • Loading branch information
nscuro committed Sep 23, 2021
1 parent 75d38b2 commit 9b45f4a
Show file tree
Hide file tree
Showing 11 changed files with 132 additions and 120 deletions.
11 changes: 8 additions & 3 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,12 +22,17 @@

### Breaking Changes

* The CLI now consists of multiple subcommands, thus being incompatible with the CLI in cyclonedx-gomod `v0.x`.
* Detected licenses (when using the `-licenses` flag) will now use the `components/evidence/licenses` node instead of `components/licenses`. Tools that consume SBOMs and don't support CycloneDX v1.3 yet may not recognize those licenses.
* Version normalization has been removed ([#60](https:/CycloneDX/cyclonedx-gomod/pull/60)). As a consequence, `+incompatible` suffixes and `v` prefixes (`-noprefix` flag in cyclonedx-gomod v0.x) are not trimmed anymore.
* The CLI now consists of multiple subcommands, thus being incompatible with the CLI in cyclonedx-gomod `v0.x`
* Detected licenses (when using the `-licenses` flag) will now use the `components/evidence/licenses` node instead of `components/licenses`. Tools that consume SBOMs and don't support CycloneDX v1.3 yet may not recognize those licenses
* Version normalization has been removed ([#60](https:/CycloneDX/cyclonedx-gomod/pull/60)). As a consequence, `+incompatible` suffixes and `v` prefixes (`-novprefix` flag in `v0.x`) are not trimmed anymore
* The `-reproducible` flag has been removed

### Dependency Updates

* Update `github.com/CycloneDX/cyclonedx-go` from `v0.3.0` to `v0.4.0` (via [`5bab19b`](https:/CycloneDX/cyclonedx-gomod/commit/5bab19bbed9c6de22112ebeb2f71691c4b4163f5))
* Update `golang.org/x/mod` from `v0.4.2` to `v0.5.0` (via [#57](https:/CycloneDX/cyclonedx-gomod/pull/57))
* Update `golang.org/x/crypto` from `v0.0.0-20210711020723-a769d52b0f97` to `v0.0.0-20210817164053-32db794688a5` (via [`75ae52a`](https:/CycloneDX/cyclonedx-gomod/commit/75ae52ac039d9d702a1861c9625d0a14116097ce))

### Known Issues

* When used with Go 1.17 or newer, with modules that have `go 1.17` in their `go.mod`, the dependency graph may contain too many edges ([#64](https:/CycloneDX/cyclonedx-gomod/issues/64))
66 changes: 32 additions & 34 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -66,31 +66,29 @@ Noteworthy environment variables that act as build constraints are:
A complete overview of all environment variables can be found here:
https://pkg.go.dev/cmd/go#hdr-Environment_variables
Unless the -reproducible flag is provided, build constraints will be
included as properties of the main component.
Applicable build constraints will be included as properties of the main component.
The -main flag should be used to specify the path to the application's main file.
-main must point to a go file within MODPATH. If -main is not specified, "main.go" is assumed.
By passing -files, all files that would be compiled into the binary will be included
as subcomponents of their respective module. Files versions follow the v0.0.0-SHORTHASH pattern,
By passing -files, all files that would be included in a binary will be attached
as subcomponents of their respective module. File versions follow the v0.0.0-SHORTHASH pattern,
where SHORTHASH is the first 12 characters of the file's SHA1 hash.
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/main.go /usr/src/acme-module
FLAGS
-files=false Include files
-json=false Output in JSON
-licenses=false Resolve module licenses
-main main.go Path to the application's main file, relative to MODPATH
-noserial=false Omit serial number
-output - Output file path (or - for STDOUT)
-reproducible=false Make the SBOM reproducible by omitting dynamic content
-serial ... Serial number
-std=false Include Go standard library as component and dependency of the module
-verbose=false Enable verbose output
-files=false Include files
-json=false Output in JSON
-licenses=false Resolve module licenses
-main main.go Path to the application's main file, relative to MODPATH
-noserial=false Omit serial number
-output - Output file path (or - for STDOUT)
-serial ... Serial number
-std=false Include Go standard library as component and dependency of the module
-verbose=false Enable verbose output
```

#### `bin`
Expand All @@ -103,24 +101,25 @@ Generate SBOM for a binary.
When license resolution is enabled, all modules (including the main module)
will be downloaded to the module cache using "go mod download".
For the download of the main module to work, its version has to be provided
via the -version flag.
Please note that data embedded in binaries shouldn't be trusted,
unless there's solid evidence that the binaries haven't been modified
since they've been built.
Example:
$ cyclonedx-gomod bin -json -output minikube-v1.22.0.bom.json -version v1.22.0 ./minikube
$ cyclonedx-gomod bin -json -output acme-app-v1.0.0.bom.json -version v1.0.0 ./acme-app
FLAGS
-json=false Output in JSON
-licenses=false Resolve module licenses
-noserial=false Omit serial number
-output - Output file path (or - for STDOUT)
-reproducible=false Make the SBOM reproducible by omitting dynamic content
-serial ... Serial number
-std=false Include Go standard library as component and dependency of the module
-verbose=false Enable verbose output
-version ... Version of the main component
-json=false Output in JSON
-licenses=false Resolve module licenses
-noserial=false Omit serial number
-output - Output file path (or - for STDOUT)
-serial ... Serial number
-std=false Include Go standard library as component and dependency of the module
-verbose=false Enable verbose output
-version ... Version of the main component
```

#### `mod`
Expand All @@ -136,16 +135,15 @@ Examples:
$ cyclonedx-gomod mod -reproducible -test -output bom.xml ./cyclonedx-go
FLAGS
-json=false Output in JSON
-licenses=false Resolve module licenses
-noserial=false Omit serial number
-output - Output file path (or - for STDOUT)
-reproducible=false Make the SBOM reproducible by omitting dynamic content
-serial ... Serial number
-std=false Include Go standard library as component and dependency of the module
-test=false Include test dependencies
-type application Type of the main component
-verbose=false Enable verbose output
-json=false Output in JSON
-licenses=false Resolve module licenses
-noserial=false Omit serial number
-output - Output file path (or - for STDOUT)
-serial ... Serial number
-std=false Include Go standard library as component and dependency of the module
-test=false Include test dependencies
-type application Type of the main component
-verbose=false Enable verbose output
```

### Examples
Expand Down
64 changes: 34 additions & 30 deletions internal/cli/cmd/app/app.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,14 +20,15 @@ package app
import (
"context"
"flag"
"fmt"
"strings"

cdx "github.com/CycloneDX/cyclonedx-go"
cliutil "github.com/CycloneDX/cyclonedx-gomod/internal/cli/util"
cliUtil "github.com/CycloneDX/cyclonedx-gomod/internal/cli/util"
"github.com/CycloneDX/cyclonedx-gomod/internal/gocmd"
"github.com/CycloneDX/cyclonedx-gomod/internal/gomod"
"github.com/CycloneDX/cyclonedx-gomod/internal/sbom"
modconv "github.com/CycloneDX/cyclonedx-gomod/internal/sbom/convert/module"
modConv "github.com/CycloneDX/cyclonedx-gomod/internal/sbom/convert/module"
"github.com/peterbourgon/ff/v3/ffcli"
"github.com/rs/zerolog/log"
)
Expand Down Expand Up @@ -57,14 +58,13 @@ Noteworthy environment variables that act as build constraints are:
A complete overview of all environment variables can be found here:
https://pkg.go.dev/cmd/go#hdr-Environment_variables
Unless the -reproducible flag is provided, build constraints will be
included as properties of the main component.
Applicable build constraints will be included as properties of the main component.
The -main flag should be used to specify the path to the application's main file.
-main must point to a go file within MODPATH. If -main is not specified, "main.go" is assumed.
By passing -files, all files that would be compiled into the binary will be included
as subcomponents of their respective module. Files versions follow the v0.0.0-SHORTHASH pattern,
By passing -files, all files that would be included in a binary will be attached
as subcomponents of their respective module. File versions follow the v0.0.0-SHORTHASH pattern,
where SHORTHASH is the first 12 characters of the file's SHA1 hash.
Examples:
Expand All @@ -81,7 +81,7 @@ Examples:
options.ModuleDir = args[0]
}

cliutil.ConfigureLogger(options.LogOptions)
cliUtil.ConfigureLogger(options.LogOptions)

return Exec(options)
},
Expand All @@ -96,30 +96,34 @@ func Exec(options Options) error {

modules, err := gomod.GetModulesFromPackages(options.ModuleDir, options.Main)
if err != nil {
return err
return fmt.Errorf("failed to collect modules: %w", err)
}

// Dependencies need to be applied prior to determining the main
// module's version, because `go mod graph` omits that version.
err = gomod.ApplyModuleGraph(options.ModuleDir, modules)
if err != nil {
return err
return fmt.Errorf("failed to apply module graph: %w", err)
}

// Determine version of main module
modules[0].Version, err = gomod.GetModuleVersion(modules[0].Dir)
if err != nil {
return err
return fmt.Errorf("failed to determine version of main module: %w", err)
}

mainComponent, err := modconv.ToComponent(modules[0],
modconv.WithComponentType(cdx.ComponentTypeApplication),
modconv.WithFiles(options.IncludeFiles),
modconv.WithLicenses(options.ResolveLicenses),
// Convert main module
mainComponent, err := modConv.ToComponent(modules[0],
modConv.WithComponentType(cdx.ComponentTypeApplication),
modConv.WithFiles(options.IncludeFiles),
modConv.WithLicenses(options.ResolveLicenses),
)
if err != nil {
return err
return fmt.Errorf("failed to convert main module: %w", err)
}

// Build properties (e.g. the Go version) depend on the environment
// and are thus only included when the SBOM doesn't have to be reproducible.
if !options.SBOMOptions.Reproducible {
buildProperties, err := createBuildProperties()
if err != nil {
Expand All @@ -132,43 +136,43 @@ func Exec(options Options) error {
}
}

components, err := modconv.ToComponents(modules[1:],
modconv.WithFiles(options.IncludeFiles),
modconv.WithLicenses(options.ResolveLicenses),
modconv.WithModuleHashes(),
// Convert the other modules
components, err := modConv.ToComponents(modules[1:],
modConv.WithFiles(options.IncludeFiles),
modConv.WithLicenses(options.ResolveLicenses),
modConv.WithModuleHashes(),
)
if err != nil {
return err
return fmt.Errorf("failed to convert modules: %w", err)
}

dependencies := sbom.BuildDependencyGraph(modules)

bom := cdx.NewBOM()

err = cliutil.SetSerialNumber(bom, options.SBOMOptions)
err = cliUtil.SetSerialNumber(bom, options.SBOMOptions)
if err != nil {
return err
return fmt.Errorf("failed to set serial number: %w", err)
}

// Assemble metadata
bom.Metadata = &cdx.Metadata{
Component: mainComponent,
}
err = cliutil.AddCommonMetadata(bom, options.SBOMOptions)
err = cliUtil.AddCommonMetadata(bom, options.SBOMOptions)
if err != nil {
return err
return fmt.Errorf("failed to add common metadata: %w", err)
}

bom.Components = &components
dependencies := sbom.BuildDependencyGraph(modules)
bom.Dependencies = &dependencies

if options.IncludeStd {
err = cliutil.AddStdComponent(bom)
err = cliUtil.AddStdComponent(bom)
if err != nil {
return err
return fmt.Errorf("failed to add stdlib component: %w", err)
}
}

return cliutil.WriteBOM(bom, options.OutputOptions)
return cliUtil.WriteBOM(bom, options.OutputOptions)
}

var buildEnv = []string{
Expand Down
37 changes: 20 additions & 17 deletions internal/cli/cmd/bin/bin.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,10 +25,10 @@ import (
"path/filepath"

cdx "github.com/CycloneDX/cyclonedx-go"
cliutil "github.com/CycloneDX/cyclonedx-gomod/internal/cli/util"
cliUtil "github.com/CycloneDX/cyclonedx-gomod/internal/cli/util"
"github.com/CycloneDX/cyclonedx-gomod/internal/gomod"
"github.com/CycloneDX/cyclonedx-gomod/internal/sbom"
modconv "github.com/CycloneDX/cyclonedx-gomod/internal/sbom/convert/module"
modConv "github.com/CycloneDX/cyclonedx-gomod/internal/sbom/convert/module"
"github.com/peterbourgon/ff/v3/ffcli"
"github.com/rs/zerolog/log"
)
Expand All @@ -55,16 +55,16 @@ unless there's solid evidence that the binaries haven't been modified
since they've been built.
Example:
$ cyclonedx-gomod bin -json -output minikube-v1.22.0.bom.json -version v1.22.0 ./minikube`,
$ cyclonedx-gomod bin -json -output acme-app-v1.0.0.bom.json -version v1.0.0 ./acme-app`,
FlagSet: fs,
Exec: func(_ context.Context, args []string) error {
if len(args) != 1 {
return fmt.Errorf("no binary path provided")
}
options.BinaryPath = args[0]

cliutil.ConfigureLogger(options.LogOptions)
cliUtil.ConfigureLogger(options.LogOptions)

options.BinaryPath = args[0]
return Exec(options)
},
}
Expand All @@ -87,10 +87,11 @@ func Exec(options Options) error {
modules[0].Version = options.Version
}

// If we want to resolve licenses, we have to download the modules first
if options.ResolveLicenses {
err = downloadModules(modules, hashes)
if err != nil {
return err
return fmt.Errorf("failed to download modules: %w", err)
}
}

Expand All @@ -99,20 +100,22 @@ func Exec(options Options) error {
modules[0].Dependencies = append(modules[0].Dependencies, &modules[i])
}

mainComponent, err := modconv.ToComponent(modules[0],
modconv.WithComponentType(cdx.ComponentTypeApplication),
modconv.WithLicenses(options.ResolveLicenses),
// Convert main module
mainComponent, err := modConv.ToComponent(modules[0],
modConv.WithComponentType(cdx.ComponentTypeApplication),
modConv.WithLicenses(options.ResolveLicenses),
)
if err != nil {
return err
return fmt.Errorf("failed to convert main module: %w", err)
}

components, err := modconv.ToComponents(modules[1:],
modconv.WithLicenses(options.ResolveLicenses),
// Convert the other modules
components, err := modConv.ToComponents(modules[1:],
modConv.WithLicenses(options.ResolveLicenses),
withModuleHashes(hashes),
)
if err != nil {
return err
return fmt.Errorf("failed to convert modules: %w", err)
}

dependencyGraph := sbom.BuildDependencyGraph(modules)
Expand All @@ -124,7 +127,7 @@ func Exec(options Options) error {

bom := cdx.NewBOM()

err = cliutil.SetSerialNumber(bom, options.SBOMOptions)
err = cliUtil.SetSerialNumber(bom, options.SBOMOptions)
if err != nil {
return err
}
Expand All @@ -133,7 +136,7 @@ func Exec(options Options) error {
Component: mainComponent,
Properties: &binaryProperties,
}
err = cliutil.AddCommonMetadata(bom, options.SBOMOptions)
err = cliUtil.AddCommonMetadata(bom, options.SBOMOptions)
if err != nil {
return err
}
Expand All @@ -142,10 +145,10 @@ func Exec(options Options) error {
bom.Dependencies = &dependencyGraph
bom.Compositions = createCompositions(*mainComponent, components)

return cliutil.WriteBOM(bom, options.OutputOptions)
return cliUtil.WriteBOM(bom, options.OutputOptions)
}

func withModuleHashes(hashes map[string]string) modconv.Option {
func withModuleHashes(hashes map[string]string) modConv.Option {
return func(m gomod.Module, c *cdx.Component) error {
h1, ok := hashes[m.Coordinates()]
if !ok {
Expand Down
Loading

0 comments on commit 9b45f4a

Please sign in to comment.