Skip to content

Commit

Permalink
feat: add option to assert detected licenses
Browse files Browse the repository at this point in the history
Closes #96

Signed-off-by: nscuro <nscuro@protonmail.com>
  • Loading branch information
nscuro committed Nov 10, 2021
1 parent fdf4107 commit c43fe86
Show file tree
Hide file tree
Showing 13 changed files with 283 additions and 26 deletions.
53 changes: 28 additions & 25 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -111,14 +111,15 @@ Examples:
$ cyclonedx-gomod app -json -output acme-app.bom.json -files -licenses -main cmd/acme-app /usr/src/acme-module
FLAGS
-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
-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
-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
-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
```

#### `bin`
Expand All @@ -145,14 +146,15 @@ Example:
$ 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 Perform license detection
-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
-assert-licenses=false Assert detected licenses
-json=false Output in JSON
-licenses=false Perform license detection
-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 @@ -168,15 +170,16 @@ Examples:
$ cyclonedx-gomod mod -test -output bom.xml ./cyclonedx-go
FLAGS
-json=false Output in JSON
-licenses=false Perform license detection
-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
-assert-licenses=false Assert detected licenses
-json=false Output in JSON
-licenses=false Perform license detection
-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
17 changes: 17 additions & 0 deletions e2e/cmd_app_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,23 @@ func TestAppCmdSimple(t *testing.T) {
runSnapshotIT(t, &appOptions.OutputOptions, func() error { return appcmd.Exec(appOptions) })
}

func TestAppCmdSimpleAssertLicenses(t *testing.T) {
fixturePath := extractFixture(t, "./testdata/modcmd/simple.tar.gz")

appOptions := appcmd.Options{
SBOMOptions: options.SBOMOptions{
AssertLicenses: true,
Reproducible: true,
ResolveLicenses: true,
SerialNumber: zeroUUID.String(),
},
ModuleDir: fixturePath,
Main: "",
}

runSnapshotIT(t, &appOptions.OutputOptions, func() error { return appcmd.Exec(appOptions) })
}

func TestAppCmdSimpleWithFiles(t *testing.T) {
fixturePath := extractFixture(t, "./testdata/modcmd/simple.tar.gz")

Expand Down
15 changes: 15 additions & 0 deletions e2e/cmd_bin_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,3 +37,18 @@ func TestBinCmdSimple(t *testing.T) {

runSnapshotIT(t, &binOptions.OutputOptions, func() error { return bincmd.Exec(binOptions) })
}

func TestBinCmdSimpleAssertLicenses(t *testing.T) {
binOptions := bincmd.Options{
SBOMOptions: options.SBOMOptions{
AssertLicenses: true,
Reproducible: true,
ResolveLicenses: true,
SerialNumber: zeroUUID.String(),
},
BinaryPath: "./testdata/bincmd/simple",
Version: "v1.0.0",
}

runSnapshotIT(t, &binOptions.OutputOptions, func() error { return bincmd.Exec(binOptions) })
}
17 changes: 17 additions & 0 deletions e2e/cmd_mod_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,23 @@ func TestModCmdSimple(t *testing.T) {
runSnapshotIT(t, &modOptions.OutputOptions, func() error { return modcmd.Exec(modOptions) })
}

func TestModCmdSimpleAssertLicenses(t *testing.T) {
fixturePath := extractFixture(t, "./testdata/modcmd/simple.tar.gz")

modOptions := modcmd.Options{
SBOMOptions: options.SBOMOptions{
AssertLicenses: true,
Reproducible: true,
ResolveLicenses: true,
SerialNumber: zeroUUID.String(),
},
ComponentType: string(cdx.ComponentTypeLibrary),
ModuleDir: fixturePath,
}

runSnapshotIT(t, &modOptions.OutputOptions, func() error { return modcmd.Exec(modOptions) })
}

// Integration test with a module that uses replacement with a local module.
// The local dependency is not a Git repository and thus won't have a version.
func TestModCmdLocal(t *testing.T) {
Expand Down
37 changes: 37 additions & 0 deletions e2e/testdata/snapshots/TestAppCmdSimpleAssertLicenses
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
<?xml version="1.0" encoding="UTF-8"?>
<bom xmlns="http://cyclonedx.org/schema/bom/1.3" serialNumber="urn:uuid:00000000-0000-0000-0000-000000000000" version="1">
<metadata>
<component bom-ref="pkg:golang/testmod-simple@v0.0.0-20210716183230-c7ea7c975ab8" type="application">
<name>testmod-simple</name>
<version>v0.0.0-20210716183230-c7ea7c975ab8</version>
<purl>pkg:golang/testmod-simple@v0.0.0-20210716183230-c7ea7c975ab8</purl>
</component>
</metadata>
<components>
<component bom-ref="pkg:golang/github.com/google/uuid@v1.2.0" type="library">
<name>github.com/google/uuid</name>
<version>v1.2.0</version>
<scope>required</scope>
<hashes>
<hash alg="SHA-256">a8962d5e72515a6a5eee6ff75e5ca1aec2eb11446a1d1336931ce8c57ab2503b</hash>
</hashes>
<licenses>
<license>
<id>BSD-3-Clause</id>
</license>
</licenses>
<purl>pkg:golang/github.com/google/uuid@v1.2.0</purl>
<externalReferences>
<reference type="vcs">
<url>https://github.com/google/uuid</url>
</reference>
</externalReferences>
</component>
</components>
<dependencies>
<dependency ref="pkg:golang/testmod-simple@v0.0.0-20210716183230-c7ea7c975ab8">
<dependency ref="pkg:golang/github.com/google/uuid@v1.2.0"></dependency>
</dependency>
<dependency ref="pkg:golang/github.com/google/uuid@v1.2.0"></dependency>
</dependencies>
</bom>
59 changes: 59 additions & 0 deletions e2e/testdata/snapshots/TestBinCmdSimpleAssertLicenses
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
<?xml version="1.0" encoding="UTF-8"?>
<bom xmlns="http://cyclonedx.org/schema/bom/1.3" serialNumber="urn:uuid:00000000-0000-0000-0000-000000000000" version="1">
<metadata>
<component bom-ref="pkg:golang/testmod-simple@v1.0.0" type="application">
<name>testmod-simple</name>
<version>v1.0.0</version>
<purl>pkg:golang/testmod-simple@v1.0.0</purl>
</component>
<properties>
<property name="cdx:gomod:binary:name">simple</property>
<property name="cdx:gomod:binary:hash:MD5">f2bd20870a0bc20bef23facd73a1fd21</property>
<property name="cdx:gomod:binary:hash:SHA-1">eaff83601ad04f88d8f44b7acd97201932e8037e</property>
<property name="cdx:gomod:binary:hash:SHA-256">2fad71e51c9d4d892036bf253a65b4555c6b72a0a0e2a4b3a1a8c47ca5e5272a</property>
<property name="cdx:gomod:binary:hash:SHA-384">cff5f2a077c59e66f1862759212720fa74f4c2ccc81eb3c0ed93155be4b52a8659eb7d79e7ac174cc997b5fe5a5333e0</property>
<property name="cdx:gomod:binary:hash:SHA-512">e678f2af01315f382e62260a30485ae23307d33615b1d1661c86c07a0468d676398955e8ebc0efca25b17de01eb167d628780ca4b5f768588d64c0b5761773a4</property>
</properties>
</metadata>
<components>
<component bom-ref="pkg:golang/github.com/google/uuid@v1.2.0" type="library">
<name>github.com/google/uuid</name>
<version>v1.2.0</version>
<scope>required</scope>
<hashes>
<hash alg="SHA-256">a8962d5e72515a6a5eee6ff75e5ca1aec2eb11446a1d1336931ce8c57ab2503b</hash>
</hashes>
<licenses>
<license>
<id>BSD-3-Clause</id>
</license>
</licenses>
<purl>pkg:golang/github.com/google/uuid@v1.2.0</purl>
<externalReferences>
<reference type="vcs">
<url>https://github.com/google/uuid</url>
</reference>
</externalReferences>
</component>
</components>
<dependencies>
<dependency ref="pkg:golang/testmod-simple@v1.0.0">
<dependency ref="pkg:golang/github.com/google/uuid@v1.2.0"></dependency>
</dependency>
<dependency ref="pkg:golang/github.com/google/uuid@v1.2.0"></dependency>
</dependencies>
<compositions>
<composition>
<aggregate>complete</aggregate>
<dependencies>
<dependency ref="pkg:golang/testmod-simple@v1.0.0"></dependency>
</dependencies>
</composition>
<composition>
<aggregate>unknown</aggregate>
<dependencies>
<dependency ref="pkg:golang/github.com/google/uuid@v1.2.0"></dependency>
</dependencies>
</composition>
</compositions>
</bom>
37 changes: 37 additions & 0 deletions e2e/testdata/snapshots/TestModCmdSimpleAssertLicenses
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
<?xml version="1.0" encoding="UTF-8"?>
<bom xmlns="http://cyclonedx.org/schema/bom/1.3" serialNumber="urn:uuid:00000000-0000-0000-0000-000000000000" version="1">
<metadata>
<component bom-ref="pkg:golang/testmod-simple@v0.0.0-20210716183230-c7ea7c975ab8" type="library">
<name>testmod-simple</name>
<version>v0.0.0-20210716183230-c7ea7c975ab8</version>
<purl>pkg:golang/testmod-simple@v0.0.0-20210716183230-c7ea7c975ab8</purl>
</component>
</metadata>
<components>
<component bom-ref="pkg:golang/github.com/google/uuid@v1.2.0" type="library">
<name>github.com/google/uuid</name>
<version>v1.2.0</version>
<scope>required</scope>
<hashes>
<hash alg="SHA-256">a8962d5e72515a6a5eee6ff75e5ca1aec2eb11446a1d1336931ce8c57ab2503b</hash>
</hashes>
<licenses>
<license>
<id>BSD-3-Clause</id>
</license>
</licenses>
<purl>pkg:golang/github.com/google/uuid@v1.2.0</purl>
<externalReferences>
<reference type="vcs">
<url>https://github.com/google/uuid</url>
</reference>
</externalReferences>
</component>
</components>
<dependencies>
<dependency ref="pkg:golang/testmod-simple@v0.0.0-20210716183230-c7ea7c975ab8">
<dependency ref="pkg:golang/github.com/google/uuid@v1.2.0"></dependency>
</dependency>
<dependency ref="pkg:golang/github.com/google/uuid@v1.2.0"></dependency>
</dependencies>
</bom>
4 changes: 4 additions & 0 deletions internal/cli/cmd/app/app.go
Original file line number Diff line number Diff line change
Expand Up @@ -179,6 +179,10 @@ func Exec(options Options) error {
}
}

if options.AssertLicenses {
sbom.AssertLicenses(bom)
}

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

Expand Down
4 changes: 4 additions & 0 deletions internal/cli/cmd/bin/bin.go
Original file line number Diff line number Diff line change
Expand Up @@ -156,6 +156,10 @@ func Exec(options Options) error {
}
}

if options.AssertLicenses {
sbom.AssertLicenses(bom)
}

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

Expand Down
4 changes: 4 additions & 0 deletions internal/cli/cmd/mod/mod.go
Original file line number Diff line number Diff line change
Expand Up @@ -148,5 +148,9 @@ func Exec(options Options) error {
}
}

if options.AssertLicenses {
sbom.AssertLicenses(bom)
}

return cliUtil.WriteBOM(bom, options.OutputOptions)
}
9 changes: 8 additions & 1 deletion internal/cli/options/options.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,10 @@ package options
import (
"flag"
"fmt"
"os"

"github.com/rs/zerolog"
"github.com/rs/zerolog/log"
"os"

"github.com/google/uuid"
)
Expand Down Expand Up @@ -86,6 +87,7 @@ func (o OutputOptions) Validate() error {

// SBOMOptions provides options for customizing the SBOM.
type SBOMOptions struct {
AssertLicenses bool
IncludeStd bool
NoSerialNumber bool
ResolveLicenses bool
Expand All @@ -97,6 +99,7 @@ type SBOMOptions struct {
}

func (s *SBOMOptions) RegisterFlags(fs *flag.FlagSet) {
fs.BoolVar(&s.AssertLicenses, "assert-licenses", false, "Assert detected licenses")
fs.BoolVar(&s.IncludeStd, "std", false, "Include Go standard library as component and dependency of the module")
fs.BoolVar(&s.NoSerialNumber, "noserial", false, "Omit serial number")
// Reproducible is used for testing only and intentionally omitted here
Expand All @@ -107,6 +110,10 @@ func (s *SBOMOptions) RegisterFlags(fs *flag.FlagSet) {
func (s SBOMOptions) Validate() error {
errs := make([]error, 0)

if s.AssertLicenses && !s.ResolveLicenses {
errs = append(errs, fmt.Errorf("assertion of licenses has no effect without licenses detection"))
}

// Serial numbers must be valid UUIDs
if !s.NoSerialNumber && s.SerialNumber != "" {
if _, err := uuid.Parse(s.SerialNumber); err != nil {
Expand Down
15 changes: 15 additions & 0 deletions internal/cli/options/options_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,21 @@ func TestSBOMOptions_Validate(t *testing.T) {
require.NoError(t, err)
})

t.Run("AssertLicensesWithoutLicenseResolution", func(t *testing.T) {
var options SBOMOptions
options.AssertLicenses = true
options.ResolveLicenses = false

err := options.Validate()
require.Error(t, err)

var validationError *ValidationError
require.ErrorAs(t, err, &validationError)

require.Len(t, validationError.Errors, 1)
require.Contains(t, validationError.Errors[0].Error(), "has no effect")
})

t.Run("InvalidSerialNumber", func(t *testing.T) {
var options SBOMOptions
options.SerialNumber = "foobar"
Expand Down

0 comments on commit c43fe86

Please sign in to comment.