mirror of
https://github.com/aquasecurity/trivy.git
synced 2025-12-21 23:00:42 -08:00
feat(sbom): add a dedicated sbom command (#1799)
Co-authored-by: knqyf263 <knqyf263@gmail.com>
This commit is contained in:
@@ -1,9 +1,9 @@
|
|||||||
# CycloneDX
|
# CycloneDX
|
||||||
|
|
||||||
Trivy generates JSON reports in the [CycloneDX][cyclonedx] format.
|
Trivy generates JSON reports in the [CycloneDX][cyclonedx] format.
|
||||||
Note that XML format is not supported at the moment.
|
Note that XML format is not supported at the moment.
|
||||||
|
|
||||||
|
You can use the regular subcommands (like `image`, `fs` and `rootfs`) and specify `cyclonedx` with the `--format` option.
|
||||||
You can specify `cyclonedx` with the `--format` option.
|
|
||||||
|
|
||||||
```
|
```
|
||||||
$ trivy image --format cyclonedx --output result.json alpine:3.15
|
$ trivy image --format cyclonedx --output result.json alpine:3.15
|
||||||
@@ -227,6 +227,7 @@ $ cat result.json | jq .
|
|||||||
}
|
}
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|
||||||
</details>
|
</details>
|
||||||
|
|
||||||
[cyclonedx]: https://cyclonedx.org/
|
[cyclonedx]: https://cyclonedx.org/
|
||||||
191
docs/advanced/sbom/index.md
Normal file
191
docs/advanced/sbom/index.md
Normal file
@@ -0,0 +1,191 @@
|
|||||||
|
# SBOM
|
||||||
|
Trivy currently supports the following SBOM formats.
|
||||||
|
|
||||||
|
- [CycloneDX][cyclonedx]
|
||||||
|
|
||||||
|
To generate SBOM, you can use the `--format` option for each subcommand such as `image` and `fs`.
|
||||||
|
|
||||||
|
```
|
||||||
|
$ trivy image --format cyclonedx --output result.json alpine:3.15
|
||||||
|
```
|
||||||
|
|
||||||
|
In addition, you can use the `trivy sbom` subcommand.
|
||||||
|
|
||||||
|
```
|
||||||
|
$ trivy sbom alpine:3.15
|
||||||
|
```
|
||||||
|
|
||||||
|
<details>
|
||||||
|
<summary>Result</summary>
|
||||||
|
|
||||||
|
```
|
||||||
|
{
|
||||||
|
"bomFormat": "CycloneDX",
|
||||||
|
"specVersion": "1.3",
|
||||||
|
"serialNumber": "urn:uuid:2be5773d-7cd3-4b4b-90a5-e165474ddace",
|
||||||
|
"version": 1,
|
||||||
|
"metadata": {
|
||||||
|
"timestamp": "2022-02-22T15:11:40.270597Z",
|
||||||
|
"tools": [
|
||||||
|
{
|
||||||
|
"vendor": "aquasecurity",
|
||||||
|
"name": "trivy",
|
||||||
|
"version": "dev"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"component": {
|
||||||
|
"bom-ref": "pkg:oci/alpine@sha256:21a3deaa0d32a8057914f36584b5288d2e5ecc984380bc0118285c70fa8c9300?repository_url=index.docker.io%2Flibrary%2Falpine&arch=amd64",
|
||||||
|
"type": "container",
|
||||||
|
"name": "alpine:3.15",
|
||||||
|
"version": "",
|
||||||
|
"purl": "pkg:oci/alpine@sha256:21a3deaa0d32a8057914f36584b5288d2e5ecc984380bc0118285c70fa8c9300?repository_url=index.docker.io%2Flibrary%2Falpine&arch=amd64",
|
||||||
|
"properties": [
|
||||||
|
{
|
||||||
|
"name": "aquasecurity:trivy:SchemaVersion",
|
||||||
|
"value": "2"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "aquasecurity:trivy:ImageID",
|
||||||
|
"value": "sha256:c059bfaa849c4d8e4aecaeb3a10c2d9b3d85f5165c66ad3a4d937758128c4d18"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "aquasecurity:trivy:RepoDigest",
|
||||||
|
"value": "alpine@sha256:21a3deaa0d32a8057914f36584b5288d2e5ecc984380bc0118285c70fa8c9300"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "aquasecurity:trivy:DiffID",
|
||||||
|
"value": "sha256:8d3ac3489996423f53d6087c81180006263b79f206d3fdec9e66f0e27ceb8759"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "aquasecurity:trivy:RepoTag",
|
||||||
|
"value": "alpine:3.15"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"components": [
|
||||||
|
{
|
||||||
|
"bom-ref": "pkg:apk/alpine/alpine-baselayout@3.2.0-r18?distro=3.15.0",
|
||||||
|
"type": "library",
|
||||||
|
"name": "alpine-baselayout",
|
||||||
|
"version": "3.2.0-r18",
|
||||||
|
"licenses": [
|
||||||
|
{
|
||||||
|
"expression": "GPL-2.0-only"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"purl": "pkg:apk/alpine/alpine-baselayout@3.2.0-r18?distro=3.15.0",
|
||||||
|
"properties": [
|
||||||
|
{
|
||||||
|
"name": "aquasecurity:trivy:SrcName",
|
||||||
|
"value": "alpine-baselayout"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "aquasecurity:trivy:SrcVersion",
|
||||||
|
"value": "3.2.0-r18"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "aquasecurity:trivy:LayerDigest",
|
||||||
|
"value": "sha256:59bf1c3509f33515622619af21ed55bbe26d24913cedbca106468a5fb37a50c3"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "aquasecurity:trivy:LayerDiffID",
|
||||||
|
"value": "sha256:8d3ac3489996423f53d6087c81180006263b79f206d3fdec9e66f0e27ceb8759"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
...(snip)...
|
||||||
|
{
|
||||||
|
"bom-ref": "pkg:apk/alpine/zlib@1.2.11-r3?distro=3.15.0",
|
||||||
|
"type": "library",
|
||||||
|
"name": "zlib",
|
||||||
|
"version": "1.2.11-r3",
|
||||||
|
"licenses": [
|
||||||
|
{
|
||||||
|
"expression": "Zlib"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"purl": "pkg:apk/alpine/zlib@1.2.11-r3?distro=3.15.0",
|
||||||
|
"properties": [
|
||||||
|
{
|
||||||
|
"name": "aquasecurity:trivy:SrcName",
|
||||||
|
"value": "zlib"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "aquasecurity:trivy:SrcVersion",
|
||||||
|
"value": "1.2.11-r3"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "aquasecurity:trivy:LayerDigest",
|
||||||
|
"value": "sha256:59bf1c3509f33515622619af21ed55bbe26d24913cedbca106468a5fb37a50c3"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "aquasecurity:trivy:LayerDiffID",
|
||||||
|
"value": "sha256:8d3ac3489996423f53d6087c81180006263b79f206d3fdec9e66f0e27ceb8759"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"bom-ref": "3da6a469-964d-4b4e-b67d-e94ec7c88d37",
|
||||||
|
"type": "operating-system",
|
||||||
|
"name": "alpine",
|
||||||
|
"version": "3.15.0",
|
||||||
|
"properties": [
|
||||||
|
{
|
||||||
|
"name": "aquasecurity:trivy:Type",
|
||||||
|
"value": "alpine"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "aquasecurity:trivy:Class",
|
||||||
|
"value": "os-pkgs"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"dependencies": [
|
||||||
|
{
|
||||||
|
"ref": "3da6a469-964d-4b4e-b67d-e94ec7c88d37",
|
||||||
|
"dependsOn": [
|
||||||
|
"pkg:apk/alpine/alpine-baselayout@3.2.0-r18?distro=3.15.0",
|
||||||
|
"pkg:apk/alpine/alpine-keys@2.4-r1?distro=3.15.0",
|
||||||
|
"pkg:apk/alpine/apk-tools@2.12.7-r3?distro=3.15.0",
|
||||||
|
"pkg:apk/alpine/busybox@1.34.1-r3?distro=3.15.0",
|
||||||
|
"pkg:apk/alpine/ca-certificates-bundle@20191127-r7?distro=3.15.0",
|
||||||
|
"pkg:apk/alpine/libc-utils@0.7.2-r3?distro=3.15.0",
|
||||||
|
"pkg:apk/alpine/libcrypto1.1@1.1.1l-r7?distro=3.15.0",
|
||||||
|
"pkg:apk/alpine/libretls@3.3.4-r2?distro=3.15.0",
|
||||||
|
"pkg:apk/alpine/libssl1.1@1.1.1l-r7?distro=3.15.0",
|
||||||
|
"pkg:apk/alpine/musl@1.2.2-r7?distro=3.15.0",
|
||||||
|
"pkg:apk/alpine/musl-utils@1.2.2-r7?distro=3.15.0",
|
||||||
|
"pkg:apk/alpine/scanelf@1.3.3-r0?distro=3.15.0",
|
||||||
|
"pkg:apk/alpine/ssl_client@1.34.1-r3?distro=3.15.0",
|
||||||
|
"pkg:apk/alpine/zlib@1.2.11-r3?distro=3.15.0"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"ref": "pkg:oci/alpine@sha256:21a3deaa0d32a8057914f36584b5288d2e5ecc984380bc0118285c70fa8c9300?repository_url=index.docker.io%2Flibrary%2Falpine&arch=amd64",
|
||||||
|
"dependsOn": [
|
||||||
|
"3da6a469-964d-4b4e-b67d-e94ec7c88d37"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
</details>
|
||||||
|
|
||||||
|
`fs`, `repo` and `archive` also work with `sbom` subcommand.
|
||||||
|
|
||||||
|
```
|
||||||
|
# filesystem
|
||||||
|
$ trivy sbom --artifact-type fs /path/to/project
|
||||||
|
|
||||||
|
# repository
|
||||||
|
$ trivy sbom --artifact-type repo github.com/aquasecurity/trivy-ci-test
|
||||||
|
|
||||||
|
# container image archive
|
||||||
|
$ trivy sbom --artifact-type archive alpine.tar
|
||||||
|
```
|
||||||
|
|
||||||
|
[cyclonedx]: cyclonedx.md
|
||||||
19
docs/getting-started/cli/sbom.md
Normal file
19
docs/getting-started/cli/sbom.md
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
# SBOM
|
||||||
|
|
||||||
|
```bash
|
||||||
|
NAME:
|
||||||
|
trivy sbom - generate SBOM for an artifact
|
||||||
|
|
||||||
|
USAGE:
|
||||||
|
trivy sbom [command options] ARTIFACT
|
||||||
|
|
||||||
|
OPTIONS:
|
||||||
|
--output value, -o value output file name [$TRIVY_OUTPUT]
|
||||||
|
--clear-cache, -c clear image caches without scanning (default: false) [$TRIVY_CLEAR_CACHE]
|
||||||
|
--ignorefile value specify .trivyignore file (default: ".trivyignore") [$TRIVY_IGNOREFILE]
|
||||||
|
--timeout value timeout (default: 5m0s) [$TRIVY_TIMEOUT]
|
||||||
|
--severity value, -s value severities of vulnerabilities to be displayed (comma separated) (default: "UNKNOWN,LOW,MEDIUM,HIGH,CRITICAL") [$TRIVY_SEVERITY]
|
||||||
|
--artifact-type value, --type value input artifact type (image, fs, repo, archive) (default: "image") [$TRIVY_ARTIFACT_TYPE]
|
||||||
|
--sbom-format value, --format value SBOM format (cyclonedx) (default: "cyclonedx") [$TRIVY_SBOM_FORMAT]
|
||||||
|
--help, -h show help (default: false)
|
||||||
|
```
|
||||||
@@ -25,6 +25,7 @@ nav:
|
|||||||
- Repository: getting-started/cli/repo.md
|
- Repository: getting-started/cli/repo.md
|
||||||
- Client: getting-started/cli/client.md
|
- Client: getting-started/cli/client.md
|
||||||
- Server: getting-started/cli/server.md
|
- Server: getting-started/cli/server.md
|
||||||
|
- SBOM: getting-started/cli/sbom.md
|
||||||
- Vulnerability:
|
- Vulnerability:
|
||||||
- Scanning:
|
- Scanning:
|
||||||
- Overview: vulnerability/scanning/index.md
|
- Overview: vulnerability/scanning/index.md
|
||||||
@@ -72,6 +73,7 @@ nav:
|
|||||||
- Plugins: advanced/plugins.md
|
- Plugins: advanced/plugins.md
|
||||||
- Air-Gapped Environment: advanced/air-gap.md
|
- Air-Gapped Environment: advanced/air-gap.md
|
||||||
- SBOM:
|
- SBOM:
|
||||||
|
- Overview: advanced/sbom/index.md
|
||||||
- CycloneDX: advanced/sbom/cyclonedx.md
|
- CycloneDX: advanced/sbom/cyclonedx.md
|
||||||
- Integrations:
|
- Integrations:
|
||||||
- Overview: advanced/integrations/index.md
|
- Overview: advanced/integrations/index.md
|
||||||
|
|||||||
@@ -365,6 +365,7 @@ func NewApp(version string) *cli.App {
|
|||||||
NewImageCommand(),
|
NewImageCommand(),
|
||||||
NewFilesystemCommand(),
|
NewFilesystemCommand(),
|
||||||
NewRootfsCommand(),
|
NewRootfsCommand(),
|
||||||
|
NewSbomCommand(),
|
||||||
NewRepositoryCommand(),
|
NewRepositoryCommand(),
|
||||||
NewClientCommand(),
|
NewClientCommand(),
|
||||||
NewServerCommand(),
|
NewServerCommand(),
|
||||||
@@ -735,6 +736,57 @@ func NewPluginCommand() *cli.Command {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// NewSbomCommand is the factory method to add sbom command
|
||||||
|
func NewSbomCommand() *cli.Command {
|
||||||
|
return &cli.Command{
|
||||||
|
Name: "sbom",
|
||||||
|
ArgsUsage: "ARTIFACT",
|
||||||
|
Usage: "generate SBOM for an artifact",
|
||||||
|
Description: `ARTIFACT can be a container image, file path/directory, git repository or container image archive. See examples.`,
|
||||||
|
CustomHelpTemplate: cli.CommandHelpTemplate + `EXAMPLES:
|
||||||
|
- image scanning:
|
||||||
|
$ trivy sbom alpine:3.15
|
||||||
|
|
||||||
|
- filesystem scanning:
|
||||||
|
$ trivy sbom --artifact-type fs /path/to/myapp
|
||||||
|
|
||||||
|
- git repository scanning:
|
||||||
|
$ trivy sbom --artifact-type repo github.com/aquasecurity/trivy-ci-test
|
||||||
|
|
||||||
|
- image archive scanning:
|
||||||
|
$ trivy sbom --artifact-type archive ./alpine.tar
|
||||||
|
|
||||||
|
`,
|
||||||
|
Action: artifact.SbomRun,
|
||||||
|
Flags: []cli.Flag{
|
||||||
|
&outputFlag,
|
||||||
|
&clearCacheFlag,
|
||||||
|
&ignoreFileFlag,
|
||||||
|
&timeoutFlag,
|
||||||
|
&severityFlag,
|
||||||
|
&offlineScan,
|
||||||
|
stringSliceFlag(skipFiles),
|
||||||
|
stringSliceFlag(skipDirs),
|
||||||
|
|
||||||
|
// dedicated options
|
||||||
|
&cli.StringFlag{
|
||||||
|
Name: "artifact-type",
|
||||||
|
Aliases: []string{"type"},
|
||||||
|
Value: "image",
|
||||||
|
Usage: "input artifact type (image, fs, repo, archive)",
|
||||||
|
EnvVars: []string{"TRIVY_ARTIFACT_TYPE"},
|
||||||
|
},
|
||||||
|
&cli.StringFlag{
|
||||||
|
Name: "sbom-format",
|
||||||
|
Aliases: []string{"format"},
|
||||||
|
Value: "cyclonedx",
|
||||||
|
Usage: "SBOM format (cyclonedx)",
|
||||||
|
EnvVars: []string{"TRIVY_SBOM_FORMAT"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// NewVersionCommand adds version command
|
// NewVersionCommand adds version command
|
||||||
func NewVersionCommand() *cli.Command {
|
func NewVersionCommand() *cli.Command {
|
||||||
return &cli.Command{
|
return &cli.Command{
|
||||||
|
|||||||
@@ -18,6 +18,7 @@ type Option struct {
|
|||||||
option.CacheOption
|
option.CacheOption
|
||||||
option.ConfigOption
|
option.ConfigOption
|
||||||
option.RemoteOption
|
option.RemoteOption
|
||||||
|
option.SbomOption
|
||||||
|
|
||||||
// We don't want to allow disabled analyzers to be passed by users,
|
// We don't want to allow disabled analyzers to be passed by users,
|
||||||
// but it differs depending on scanning modes.
|
// but it differs depending on scanning modes.
|
||||||
@@ -40,6 +41,7 @@ func NewOption(c *cli.Context) (Option, error) {
|
|||||||
CacheOption: option.NewCacheOption(c),
|
CacheOption: option.NewCacheOption(c),
|
||||||
ConfigOption: option.NewConfigOption(c),
|
ConfigOption: option.NewConfigOption(c),
|
||||||
RemoteOption: option.NewRemoteOption(c),
|
RemoteOption: option.NewRemoteOption(c),
|
||||||
|
SbomOption: option.NewSbomOption(c),
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -70,6 +72,9 @@ func (c *Option) initPreScanOptions() error {
|
|||||||
if err := c.CacheOption.Init(); err != nil {
|
if err := c.CacheOption.Init(); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
if err := c.SbomOption.Init(c.Context, c.Logger); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
c.RemoteOption.Init(c.Logger)
|
c.RemoteOption.Init(c.Logger)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -86,7 +86,7 @@ func TestOption_Init(t *testing.T) {
|
|||||||
name: "invalid option combination: token and token header without server",
|
name: "invalid option combination: token and token header without server",
|
||||||
args: []string{"--token", "secret", "--token-header", "X-Trivy-Token", "alpine:3.11"},
|
args: []string{"--token", "secret", "--token-header", "X-Trivy-Token", "alpine:3.11"},
|
||||||
logs: []string{
|
logs: []string{
|
||||||
"'--token', '--token-header' and 'custom-header' can be used only with '--server'",
|
`"--token" can be used only with "--server"`,
|
||||||
},
|
},
|
||||||
want: Option{
|
want: Option{
|
||||||
ReportOption: option.ReportOption{
|
ReportOption: option.ReportOption{
|
||||||
|
|||||||
62
pkg/commands/artifact/sbom.go
Normal file
62
pkg/commands/artifact/sbom.go
Normal file
@@ -0,0 +1,62 @@
|
|||||||
|
package artifact
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/urfave/cli/v2"
|
||||||
|
"golang.org/x/exp/maps"
|
||||||
|
"golang.org/x/xerrors"
|
||||||
|
|
||||||
|
"github.com/aquasecurity/fanal/analyzer"
|
||||||
|
"github.com/aquasecurity/trivy/pkg/types"
|
||||||
|
)
|
||||||
|
|
||||||
|
type ArtifactType string
|
||||||
|
|
||||||
|
const (
|
||||||
|
containerImageArtifact ArtifactType = "image"
|
||||||
|
filesystemArtifact ArtifactType = "fs"
|
||||||
|
repositoryArtifact ArtifactType = "repo"
|
||||||
|
imageArchiveArtifact ArtifactType = "archive"
|
||||||
|
)
|
||||||
|
|
||||||
|
var artifactTypes = map[ArtifactType]struct {
|
||||||
|
initializer InitializeScanner
|
||||||
|
disableAnalyzers []analyzer.Type
|
||||||
|
}{
|
||||||
|
containerImageArtifact: {
|
||||||
|
initializer: imageScanner,
|
||||||
|
disableAnalyzers: analyzer.TypeLockfiles,
|
||||||
|
},
|
||||||
|
filesystemArtifact: {
|
||||||
|
initializer: filesystemStandaloneScanner,
|
||||||
|
disableAnalyzers: analyzer.TypeIndividualPkgs,
|
||||||
|
},
|
||||||
|
repositoryArtifact: {
|
||||||
|
initializer: repositoryScanner,
|
||||||
|
disableAnalyzers: analyzer.TypeIndividualPkgs,
|
||||||
|
},
|
||||||
|
imageArchiveArtifact: {
|
||||||
|
initializer: archiveScanner,
|
||||||
|
disableAnalyzers: analyzer.TypeLockfiles,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
// SbomRun runs generates sbom for image and package artifacts
|
||||||
|
func SbomRun(ctx *cli.Context) error {
|
||||||
|
opt, err := initOption(ctx)
|
||||||
|
if err != nil {
|
||||||
|
return xerrors.Errorf("option error: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
artifactType := opt.SbomOption.ArtifactType
|
||||||
|
s, ok := artifactTypes[ArtifactType(artifactType)]
|
||||||
|
if !ok {
|
||||||
|
return xerrors.Errorf(`"--artifact-type" must be %q`, maps.Keys(artifactTypes))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Scan the relevant dependencies
|
||||||
|
opt.DisabledAnalyzers = s.disableAnalyzers
|
||||||
|
opt.ReportOption.VulnType = []string{types.VulnTypeOS, types.VulnTypeLibrary}
|
||||||
|
opt.ReportOption.SecurityChecks = []string{types.SecurityCheckVulnerability}
|
||||||
|
|
||||||
|
return Run(ctx.Context, opt, s.initializer, initCache)
|
||||||
|
}
|
||||||
@@ -50,8 +50,13 @@ func (c *RemoteOption) Init(logger *zap.SugaredLogger) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if c.RemoteAddr == "" {
|
if c.RemoteAddr == "" {
|
||||||
if len(c.customHeaders) > 0 || c.token != "" || c.tokenHeader != DefaultTokenHeader {
|
switch {
|
||||||
logger.Warn(`'--token', '--token-header' and 'custom-header' can be used only with '--server'`)
|
case len(c.customHeaders) > 0:
|
||||||
|
logger.Warn(`"--custom-header"" can be used only with "--server"`)
|
||||||
|
case c.token != "":
|
||||||
|
logger.Warn(`"--token" can be used only with "--server"`)
|
||||||
|
case c.tokenHeader != "" && c.tokenHeader != DefaultTokenHeader:
|
||||||
|
logger.Warn(`'--token-header' can be used only with "--server"`)
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|||||||
39
pkg/commands/option/sbom.go
Normal file
39
pkg/commands/option/sbom.go
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
package option
|
||||||
|
|
||||||
|
import (
|
||||||
|
"golang.org/x/exp/slices"
|
||||||
|
"golang.org/x/xerrors"
|
||||||
|
|
||||||
|
"github.com/urfave/cli/v2"
|
||||||
|
"go.uber.org/zap"
|
||||||
|
)
|
||||||
|
|
||||||
|
var supportedSbomFormats = []string{"cyclonedx"}
|
||||||
|
|
||||||
|
// SbomOption holds the options for SBOM generation
|
||||||
|
type SbomOption struct {
|
||||||
|
ArtifactType string
|
||||||
|
SbomFormat string
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewSbomOption is the factory method to return SBOM options
|
||||||
|
func NewSbomOption(c *cli.Context) SbomOption {
|
||||||
|
return SbomOption{
|
||||||
|
ArtifactType: c.String("artifact-type"),
|
||||||
|
SbomFormat: c.String("sbom-format"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Init initialize the CLI context for SBOM generation
|
||||||
|
func (c *SbomOption) Init(ctx *cli.Context, logger *zap.SugaredLogger) error {
|
||||||
|
if ctx.Command.Name != "sbom" {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if !slices.Contains(supportedSbomFormats, c.SbomFormat) {
|
||||||
|
logger.Errorf(`"--format" must be %q`, supportedSbomFormats)
|
||||||
|
return xerrors.Errorf(`"--format" must be %q`, supportedSbomFormats)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user