feat(vuln): add --pkg-relationships (#7237)

Signed-off-by: knqyf263 <knqyf263@gmail.com>
This commit is contained in:
Teppei Fukuda
2024-07-29 10:18:59 +04:00
committed by GitHub
parent f35f4a5e83
commit 5c37361600
27 changed files with 580 additions and 278 deletions

View File

@@ -63,7 +63,8 @@ trivy filesystem [flags] PATH
--output-plugin-arg string [EXPERIMENTAL] output plugin arguments --output-plugin-arg string [EXPERIMENTAL] output plugin arguments
--parallel int number of goroutines enabled for parallel scanning, set 0 to auto-detect parallelism (default 5) --parallel int number of goroutines enabled for parallel scanning, set 0 to auto-detect parallelism (default 5)
--password strings password. Comma-separated passwords allowed. TRIVY_PASSWORD should be used for security reasons. --password strings password. Comma-separated passwords allowed. TRIVY_PASSWORD should be used for security reasons.
--pkg-types strings comma-separated list of package types (os,library) (default [os,library]) --pkg-relationships strings list of package relationships (unknown,root,direct,indirect) (default [unknown,root,direct,indirect])
--pkg-types strings list of package types (os,library) (default [os,library])
--redis-ca string redis ca file location, if using redis as cache backend --redis-ca string redis ca file location, if using redis as cache backend
--redis-cert string redis certificate file location, if using redis as cache backend --redis-cert string redis certificate file location, if using redis as cache backend
--redis-key string redis key file location, if using redis as cache backend --redis-key string redis key file location, if using redis as cache backend

View File

@@ -81,7 +81,8 @@ trivy image [flags] IMAGE_NAME
--output-plugin-arg string [EXPERIMENTAL] output plugin arguments --output-plugin-arg string [EXPERIMENTAL] output plugin arguments
--parallel int number of goroutines enabled for parallel scanning, set 0 to auto-detect parallelism (default 5) --parallel int number of goroutines enabled for parallel scanning, set 0 to auto-detect parallelism (default 5)
--password strings password. Comma-separated passwords allowed. TRIVY_PASSWORD should be used for security reasons. --password strings password. Comma-separated passwords allowed. TRIVY_PASSWORD should be used for security reasons.
--pkg-types strings comma-separated list of package types (os,library) (default [os,library]) --pkg-relationships strings list of package relationships (unknown,root,direct,indirect) (default [unknown,root,direct,indirect])
--pkg-types strings list of package types (os,library) (default [os,library])
--platform string set platform in the form os/arch if image is multi-platform capable --platform string set platform in the form os/arch if image is multi-platform capable
--podman-host string unix podman socket path to use for podman scanning --podman-host string unix podman socket path to use for podman scanning
--redis-ca string redis ca file location, if using redis as cache backend --redis-ca string redis ca file location, if using redis as cache backend

View File

@@ -78,7 +78,8 @@ trivy kubernetes [flags] [CONTEXT]
--output-plugin-arg string [EXPERIMENTAL] output plugin arguments --output-plugin-arg string [EXPERIMENTAL] output plugin arguments
--parallel int number of goroutines enabled for parallel scanning, set 0 to auto-detect parallelism (default 5) --parallel int number of goroutines enabled for parallel scanning, set 0 to auto-detect parallelism (default 5)
--password strings password. Comma-separated passwords allowed. TRIVY_PASSWORD should be used for security reasons. --password strings password. Comma-separated passwords allowed. TRIVY_PASSWORD should be used for security reasons.
--pkg-types strings comma-separated list of package types (os,library) (default [os,library]) --pkg-relationships strings list of package relationships (unknown,root,direct,indirect) (default [unknown,root,direct,indirect])
--pkg-types strings list of package types (os,library) (default [os,library])
--qps float specify the maximum QPS to the master from this client (default 5) --qps float specify the maximum QPS to the master from this client (default 5)
--redis-ca string redis ca file location, if using redis as cache backend --redis-ca string redis ca file location, if using redis as cache backend
--redis-cert string redis certificate file location, if using redis as cache backend --redis-cert string redis certificate file location, if using redis as cache backend

View File

@@ -63,7 +63,8 @@ trivy repository [flags] (REPO_PATH | REPO_URL)
--output-plugin-arg string [EXPERIMENTAL] output plugin arguments --output-plugin-arg string [EXPERIMENTAL] output plugin arguments
--parallel int number of goroutines enabled for parallel scanning, set 0 to auto-detect parallelism (default 5) --parallel int number of goroutines enabled for parallel scanning, set 0 to auto-detect parallelism (default 5)
--password strings password. Comma-separated passwords allowed. TRIVY_PASSWORD should be used for security reasons. --password strings password. Comma-separated passwords allowed. TRIVY_PASSWORD should be used for security reasons.
--pkg-types strings comma-separated list of package types (os,library) (default [os,library]) --pkg-relationships strings list of package relationships (unknown,root,direct,indirect) (default [unknown,root,direct,indirect])
--pkg-types strings list of package types (os,library) (default [os,library])
--redis-ca string redis ca file location, if using redis as cache backend --redis-ca string redis ca file location, if using redis as cache backend
--redis-cert string redis certificate file location, if using redis as cache backend --redis-cert string redis certificate file location, if using redis as cache backend
--redis-key string redis key file location, if using redis as cache backend --redis-key string redis key file location, if using redis as cache backend

View File

@@ -65,7 +65,8 @@ trivy rootfs [flags] ROOTDIR
--output-plugin-arg string [EXPERIMENTAL] output plugin arguments --output-plugin-arg string [EXPERIMENTAL] output plugin arguments
--parallel int number of goroutines enabled for parallel scanning, set 0 to auto-detect parallelism (default 5) --parallel int number of goroutines enabled for parallel scanning, set 0 to auto-detect parallelism (default 5)
--password strings password. Comma-separated passwords allowed. TRIVY_PASSWORD should be used for security reasons. --password strings password. Comma-separated passwords allowed. TRIVY_PASSWORD should be used for security reasons.
--pkg-types strings comma-separated list of package types (os,library) (default [os,library]) --pkg-relationships strings list of package relationships (unknown,root,direct,indirect) (default [unknown,root,direct,indirect])
--pkg-types strings list of package types (os,library) (default [os,library])
--redis-ca string redis ca file location, if using redis as cache backend --redis-ca string redis ca file location, if using redis as cache backend
--redis-cert string redis certificate file location, if using redis as cache backend --redis-cert string redis certificate file location, if using redis as cache backend
--redis-key string redis key file location, if using redis as cache backend --redis-key string redis key file location, if using redis as cache backend

View File

@@ -43,7 +43,8 @@ trivy sbom [flags] SBOM_PATH
--offline-scan do not issue API requests to identify dependencies --offline-scan do not issue API requests to identify dependencies
-o, --output string output file name -o, --output string output file name
--output-plugin-arg string [EXPERIMENTAL] output plugin arguments --output-plugin-arg string [EXPERIMENTAL] output plugin arguments
--pkg-types strings comma-separated list of package types (os,library) (default [os,library]) --pkg-relationships strings list of package relationships (unknown,root,direct,indirect) (default [unknown,root,direct,indirect])
--pkg-types strings list of package types (os,library) (default [os,library])
--redis-ca string redis ca file location, if using redis as cache backend --redis-ca string redis ca file location, if using redis as cache backend
--redis-cert string redis certificate file location, if using redis as cache backend --redis-cert string redis certificate file location, if using redis as cache backend
--redis-key string redis key file location, if using redis as cache backend --redis-key string redis key file location, if using redis as cache backend

View File

@@ -56,7 +56,8 @@ trivy vm [flags] VM_IMAGE
-o, --output string output file name -o, --output string output file name
--output-plugin-arg string [EXPERIMENTAL] output plugin arguments --output-plugin-arg string [EXPERIMENTAL] output plugin arguments
--parallel int number of goroutines enabled for parallel scanning, set 0 to auto-detect parallelism (default 5) --parallel int number of goroutines enabled for parallel scanning, set 0 to auto-detect parallelism (default 5)
--pkg-types strings comma-separated list of package types (os,library) (default [os,library]) --pkg-relationships strings list of package relationships (unknown,root,direct,indirect) (default [unknown,root,direct,indirect])
--pkg-types strings list of package types (os,library) (default [os,library])
--redis-ca string redis ca file location, if using redis as cache backend --redis-ca string redis ca file location, if using redis as cache backend
--redis-cert string redis certificate file location, if using redis as cache backend --redis-cert string redis certificate file location, if using redis as cache backend
--redis-key string redis key file location, if using redis as cache backend --redis-key string redis key file location, if using redis as cache backend

View File

@@ -202,7 +202,8 @@ Currently, specifying a username and password is not supported.
This section describes vulnerability-specific configuration. This section describes vulnerability-specific configuration.
Other common options are documented [here](../configuration/index.md). Other common options are documented [here](../configuration/index.md).
### Enabling a subset of package types ### Enabling a Subset of Package Types
It's possible to only enable certain package types if you prefer. It's possible to only enable certain package types if you prefer.
You can do so by passing the `--pkg-types` option. You can do so by passing the `--pkg-types` option.
This flag takes a comma-separated list of package types. This flag takes a comma-separated list of package types.
@@ -268,6 +269,45 @@ Total: 7 (UNKNOWN: 0, LOW: 1, MEDIUM: 1, HIGH: 3, CRITICAL: 2)
</details> </details>
!!! info
This flag filters the packages themselves, so it also affects the `--list-all-pkgs` option and SBOM generation.
### Filtering by Package Relationships
Trivy supports filtering vulnerabilities based on the relationship of packages within a project.
This is achieved through the `--pkg-relationships` flag.
This feature allows you to focus on vulnerabilities in specific types of dependencies, such as only those in direct dependencies.
In Trivy, there are four types of package relationships:
1. `root`: The root package being scanned
2. `direct`: Direct dependencies of the root package
3. `indirect`: Transitive dependencies
4. `unknown`: Packages whose relationship cannot be determined
The available relationships may vary depending on the ecosystem.
To see which relationships are supported for a particular project, you can use the JSON output format and check the `Relationship` field:
```
$ trivy repo -f json --list-all-pkgs /path/to/project
```
To scan only the root package and its direct dependencies, you can use the flag as follows:
```
$ trivy repo --pkg-relationships root,direct /path/to/project
```
By default, all relationships are included in the scan.
!!! info
This flag filters the packages themselves, so it also affects the `--list-all-pkgs` option and SBOM generation.
!!! warning
As it may not provide a complete package list, `--pkg-relationships` cannot be used with `--dependency-tree`, `--vex` or SBOM generation.
[^1]: https://github.com/GoogleContainerTools/distroless [^1]: https://github.com/GoogleContainerTools/distroless
[nvd-CVE-2023-0464]: https://nvd.nist.gov/vuln/detail/CVE-2023-0464 [nvd-CVE-2023-0464]: https://nvd.nist.gov/vuln/detail/CVE-2023-0464

View File

@@ -243,9 +243,6 @@ func NewRootCommand(globalFlags *flag.GlobalFlagGroup) *cobra.Command {
} }
func NewImageCommand(globalFlags *flag.GlobalFlagGroup) *cobra.Command { func NewImageCommand(globalFlags *flag.GlobalFlagGroup) *cobra.Command {
scanFlagGroup := flag.NewScanFlagGroup()
scanFlagGroup.IncludeDevDeps = nil // disable '--include-dev-deps'
reportFlagGroup := flag.NewReportFlagGroup() reportFlagGroup := flag.NewReportFlagGroup()
report := flag.ReportFormatFlag.Clone() report := flag.ReportFormatFlag.Clone()
report.Default = "summary" // override the default value as the summary is preferred for the compliance report report.Default = "summary" // override the default value as the summary is preferred for the compliance report
@@ -256,27 +253,28 @@ func NewImageCommand(globalFlags *flag.GlobalFlagGroup) *cobra.Command {
compliance.Values = []string{types.ComplianceDockerCIS160} compliance.Values = []string{types.ComplianceDockerCIS160}
reportFlagGroup.Compliance = compliance // override usage as the accepted values differ for each subcommand. reportFlagGroup.Compliance = compliance // override usage as the accepted values differ for each subcommand.
misconfFlagGroup := flag.NewMisconfFlagGroup()
misconfFlagGroup.CloudformationParamVars = nil // disable '--cf-params'
misconfFlagGroup.TerraformTFVars = nil // disable '--tf-vars'
imageFlags := &flag.Flags{ imageFlags := &flag.Flags{
GlobalFlagGroup: globalFlags, GlobalFlagGroup: globalFlags,
CacheFlagGroup: flag.NewCacheFlagGroup(), CacheFlagGroup: flag.NewCacheFlagGroup(),
DBFlagGroup: flag.NewDBFlagGroup(), DBFlagGroup: flag.NewDBFlagGroup(),
ImageFlagGroup: flag.NewImageFlagGroup(), // container image specific ImageFlagGroup: flag.NewImageFlagGroup(), // container image specific
LicenseFlagGroup: flag.NewLicenseFlagGroup(), LicenseFlagGroup: flag.NewLicenseFlagGroup(),
MisconfFlagGroup: misconfFlagGroup, MisconfFlagGroup: flag.NewMisconfFlagGroup(),
ModuleFlagGroup: flag.NewModuleFlagGroup(), ModuleFlagGroup: flag.NewModuleFlagGroup(),
PackageFlagGroup: flag.NewPackageFlagGroup(),
RemoteFlagGroup: flag.NewClientFlags(), // for client/server mode RemoteFlagGroup: flag.NewClientFlags(), // for client/server mode
RegistryFlagGroup: flag.NewRegistryFlagGroup(), RegistryFlagGroup: flag.NewRegistryFlagGroup(),
RegoFlagGroup: flag.NewRegoFlagGroup(), RegoFlagGroup: flag.NewRegoFlagGroup(),
ReportFlagGroup: reportFlagGroup, ReportFlagGroup: reportFlagGroup,
ScanFlagGroup: scanFlagGroup, ScanFlagGroup: flag.NewScanFlagGroup(),
SecretFlagGroup: flag.NewSecretFlagGroup(), SecretFlagGroup: flag.NewSecretFlagGroup(),
VulnerabilityFlagGroup: flag.NewVulnerabilityFlagGroup(), VulnerabilityFlagGroup: flag.NewVulnerabilityFlagGroup(),
} }
imageFlags.PackageFlagGroup.IncludeDevDeps = nil // disable '--include-dev-deps'
imageFlags.MisconfFlagGroup.CloudformationParamVars = nil // disable '--cf-params'
imageFlags.MisconfFlagGroup.TerraformTFVars = nil // disable '--tf-vars'
cmd := &cobra.Command{ cmd := &cobra.Command{
Use: "image [flags] IMAGE_NAME", Use: "image [flags] IMAGE_NAME",
Aliases: []string{"i"}, Aliases: []string{"i"},
@@ -342,6 +340,7 @@ func NewFilesystemCommand(globalFlags *flag.GlobalFlagGroup) *cobra.Command {
LicenseFlagGroup: flag.NewLicenseFlagGroup(), LicenseFlagGroup: flag.NewLicenseFlagGroup(),
MisconfFlagGroup: flag.NewMisconfFlagGroup(), MisconfFlagGroup: flag.NewMisconfFlagGroup(),
ModuleFlagGroup: flag.NewModuleFlagGroup(), ModuleFlagGroup: flag.NewModuleFlagGroup(),
PackageFlagGroup: flag.NewPackageFlagGroup(),
RemoteFlagGroup: flag.NewClientFlags(), // for client/server mode RemoteFlagGroup: flag.NewClientFlags(), // for client/server mode
RegistryFlagGroup: flag.NewRegistryFlagGroup(), RegistryFlagGroup: flag.NewRegistryFlagGroup(),
RegoFlagGroup: flag.NewRegoFlagGroup(), RegoFlagGroup: flag.NewRegoFlagGroup(),
@@ -400,6 +399,7 @@ func NewRootfsCommand(globalFlags *flag.GlobalFlagGroup) *cobra.Command {
LicenseFlagGroup: flag.NewLicenseFlagGroup(), LicenseFlagGroup: flag.NewLicenseFlagGroup(),
MisconfFlagGroup: flag.NewMisconfFlagGroup(), MisconfFlagGroup: flag.NewMisconfFlagGroup(),
ModuleFlagGroup: flag.NewModuleFlagGroup(), ModuleFlagGroup: flag.NewModuleFlagGroup(),
PackageFlagGroup: flag.NewPackageFlagGroup(),
RemoteFlagGroup: flag.NewClientFlags(), // for client/server mode RemoteFlagGroup: flag.NewClientFlags(), // for client/server mode
RegistryFlagGroup: flag.NewRegistryFlagGroup(), RegistryFlagGroup: flag.NewRegistryFlagGroup(),
RegoFlagGroup: flag.NewRegoFlagGroup(), RegoFlagGroup: flag.NewRegoFlagGroup(),
@@ -411,7 +411,7 @@ func NewRootfsCommand(globalFlags *flag.GlobalFlagGroup) *cobra.Command {
rootfsFlags.ReportFlagGroup.ReportFormat = nil // TODO: support --report summary rootfsFlags.ReportFlagGroup.ReportFormat = nil // TODO: support --report summary
rootfsFlags.ReportFlagGroup.Compliance = nil // disable '--compliance' rootfsFlags.ReportFlagGroup.Compliance = nil // disable '--compliance'
rootfsFlags.ReportFlagGroup.ReportFormat = nil // disable '--report' rootfsFlags.ReportFlagGroup.ReportFormat = nil // disable '--report'
rootfsFlags.ScanFlagGroup.IncludeDevDeps = nil // disable '--include-dev-deps' rootfsFlags.PackageFlagGroup.IncludeDevDeps = nil // disable '--include-dev-deps'
rootfsFlags.CacheFlagGroup.CacheBackend.Default = string(cache.TypeMemory) // Use memory cache by default rootfsFlags.CacheFlagGroup.CacheBackend.Default = string(cache.TypeMemory) // Use memory cache by default
cmd := &cobra.Command{ cmd := &cobra.Command{
@@ -460,6 +460,7 @@ func NewRepositoryCommand(globalFlags *flag.GlobalFlagGroup) *cobra.Command {
LicenseFlagGroup: flag.NewLicenseFlagGroup(), LicenseFlagGroup: flag.NewLicenseFlagGroup(),
MisconfFlagGroup: flag.NewMisconfFlagGroup(), MisconfFlagGroup: flag.NewMisconfFlagGroup(),
ModuleFlagGroup: flag.NewModuleFlagGroup(), ModuleFlagGroup: flag.NewModuleFlagGroup(),
PackageFlagGroup: flag.NewPackageFlagGroup(),
RegistryFlagGroup: flag.NewRegistryFlagGroup(), RegistryFlagGroup: flag.NewRegistryFlagGroup(),
RegoFlagGroup: flag.NewRegoFlagGroup(), RegoFlagGroup: flag.NewRegoFlagGroup(),
RemoteFlagGroup: flag.NewClientFlags(), // for client/server mode RemoteFlagGroup: flag.NewClientFlags(), // for client/server mode
@@ -516,7 +517,6 @@ func NewConvertCommand(globalFlags *flag.GlobalFlagGroup) *cobra.Command {
ScanFlagGroup: &flag.ScanFlagGroup{}, ScanFlagGroup: &flag.ScanFlagGroup{},
ReportFlagGroup: flag.NewReportFlagGroup(), ReportFlagGroup: flag.NewReportFlagGroup(),
} }
convertFlags.ReportFlagGroup.PkgTypes = nil // disable '--pkg-types'
cmd := &cobra.Command{ cmd := &cobra.Command{
Use: "convert [flags] RESULT_JSON", Use: "convert [flags] RESULT_JSON",
@@ -685,7 +685,6 @@ func NewConfigCommand(globalFlags *flag.GlobalFlagGroup) *cobra.Command {
configFlags.ReportFlagGroup.ListAllPkgs = nil // disable '--list-all-pkgs' configFlags.ReportFlagGroup.ListAllPkgs = nil // disable '--list-all-pkgs'
configFlags.ReportFlagGroup.ExitOnEOL = nil // disable '--exit-on-eol' configFlags.ReportFlagGroup.ExitOnEOL = nil // disable '--exit-on-eol'
configFlags.ReportFlagGroup.ShowSuppressed = nil // disable '--show-suppressed' configFlags.ReportFlagGroup.ShowSuppressed = nil // disable '--show-suppressed'
configFlags.ReportFlagGroup.PkgTypes = nil // disable '--pkg-types'
configFlags.ReportFlagGroup.ReportFormat.Usage = "specify a compliance report format for the output" // @TODO: support --report summary for non compliance reports configFlags.ReportFlagGroup.ReportFormat.Usage = "specify a compliance report format for the output" // @TODO: support --report summary for non compliance reports
configFlags.CacheFlagGroup.CacheBackend.Default = string(cache.TypeMemory) configFlags.CacheFlagGroup.CacheBackend.Default = string(cache.TypeMemory)
@@ -960,7 +959,6 @@ func NewKubernetesCommand(globalFlags *flag.GlobalFlagGroup) *cobra.Command {
}) })
scanners.Default = scanners.Values scanners.Default = scanners.Values
scanFlags.Scanners = scanners scanFlags.Scanners = scanners
scanFlags.IncludeDevDeps = nil // disable '--include-dev-deps'
// required only SourceFlag // required only SourceFlag
imageFlags := &flag.ImageFlagGroup{ImageSources: flag.SourceFlag.Clone()} imageFlags := &flag.ImageFlagGroup{ImageSources: flag.SourceFlag.Clone()}
@@ -997,6 +995,7 @@ func NewKubernetesCommand(globalFlags *flag.GlobalFlagGroup) *cobra.Command {
ImageFlagGroup: imageFlags, ImageFlagGroup: imageFlags,
K8sFlagGroup: flag.NewK8sFlagGroup(), // kubernetes-specific flags K8sFlagGroup: flag.NewK8sFlagGroup(), // kubernetes-specific flags
MisconfFlagGroup: misconfFlagGroup, MisconfFlagGroup: misconfFlagGroup,
PackageFlagGroup: flag.NewPackageFlagGroup(),
RegoFlagGroup: flag.NewRegoFlagGroup(), RegoFlagGroup: flag.NewRegoFlagGroup(),
ReportFlagGroup: reportFlagGroup, ReportFlagGroup: reportFlagGroup,
ScanFlagGroup: scanFlags, ScanFlagGroup: scanFlags,
@@ -1004,6 +1003,8 @@ func NewKubernetesCommand(globalFlags *flag.GlobalFlagGroup) *cobra.Command {
RegistryFlagGroup: flag.NewRegistryFlagGroup(), RegistryFlagGroup: flag.NewRegistryFlagGroup(),
VulnerabilityFlagGroup: flag.NewVulnerabilityFlagGroup(), VulnerabilityFlagGroup: flag.NewVulnerabilityFlagGroup(),
} }
k8sFlags.PackageFlagGroup.IncludeDevDeps = nil // disable '--include-dev-deps'
cmd := &cobra.Command{ cmd := &cobra.Command{
Use: "kubernetes [flags] [CONTEXT]", Use: "kubernetes [flags] [CONTEXT]",
Aliases: []string{"k8s"}, Aliases: []string{"k8s"},
@@ -1055,6 +1056,7 @@ func NewVMCommand(globalFlags *flag.GlobalFlagGroup) *cobra.Command {
DBFlagGroup: flag.NewDBFlagGroup(), DBFlagGroup: flag.NewDBFlagGroup(),
MisconfFlagGroup: flag.NewMisconfFlagGroup(), MisconfFlagGroup: flag.NewMisconfFlagGroup(),
ModuleFlagGroup: flag.NewModuleFlagGroup(), ModuleFlagGroup: flag.NewModuleFlagGroup(),
PackageFlagGroup: flag.NewPackageFlagGroup(),
RemoteFlagGroup: flag.NewClientFlags(), // for client/server mode RemoteFlagGroup: flag.NewClientFlags(), // for client/server mode
ReportFlagGroup: flag.NewReportFlagGroup(), ReportFlagGroup: flag.NewReportFlagGroup(),
ScanFlagGroup: flag.NewScanFlagGroup(), ScanFlagGroup: flag.NewScanFlagGroup(),
@@ -1069,7 +1071,7 @@ func NewVMCommand(globalFlags *flag.GlobalFlagGroup) *cobra.Command {
}, },
} }
vmFlags.ReportFlagGroup.ReportFormat = nil // disable '--report' vmFlags.ReportFlagGroup.ReportFormat = nil // disable '--report'
vmFlags.ScanFlagGroup.IncludeDevDeps = nil // disable '--include-dev-deps' vmFlags.PackageFlagGroup.IncludeDevDeps = nil // disable '--include-dev-deps'
vmFlags.MisconfFlagGroup.CloudformationParamVars = nil // disable '--cf-params' vmFlags.MisconfFlagGroup.CloudformationParamVars = nil // disable '--cf-params'
vmFlags.MisconfFlagGroup.TerraformTFVars = nil // disable '--tf-vars' vmFlags.MisconfFlagGroup.TerraformTFVars = nil // disable '--tf-vars'
@@ -1128,9 +1130,8 @@ func NewSBOMCommand(globalFlags *flag.GlobalFlagGroup) *cobra.Command {
types.VulnerabilityScanner, types.VulnerabilityScanner,
}) })
scanFlagGroup := flag.NewScanFlagGroup() scanFlagGroup := flag.NewScanFlagGroup()
scanFlagGroup.Scanners = scanners // allow only 'vuln' and 'license' options for '--scanners' scanFlagGroup.Scanners = scanners // allow only 'vuln' and 'license' options for '--scanners'
scanFlagGroup.IncludeDevDeps = nil // disable '--include-dev-deps' scanFlagGroup.Parallel = nil // disable '--parallel'
scanFlagGroup.Parallel = nil // disable '--parallel'
licenseFlagGroup := flag.NewLicenseFlagGroup() licenseFlagGroup := flag.NewLicenseFlagGroup()
// License full-scan and confidence-level are for file content only // License full-scan and confidence-level are for file content only
@@ -1141,6 +1142,7 @@ func NewSBOMCommand(globalFlags *flag.GlobalFlagGroup) *cobra.Command {
GlobalFlagGroup: globalFlags, GlobalFlagGroup: globalFlags,
CacheFlagGroup: flag.NewCacheFlagGroup(), CacheFlagGroup: flag.NewCacheFlagGroup(),
DBFlagGroup: flag.NewDBFlagGroup(), DBFlagGroup: flag.NewDBFlagGroup(),
PackageFlagGroup: flag.NewPackageFlagGroup(),
RemoteFlagGroup: flag.NewClientFlags(), // for client/server mode RemoteFlagGroup: flag.NewClientFlags(), // for client/server mode
ReportFlagGroup: reportFlagGroup, ReportFlagGroup: reportFlagGroup,
ScanFlagGroup: scanFlagGroup, ScanFlagGroup: scanFlagGroup,
@@ -1150,6 +1152,7 @@ func NewSBOMCommand(globalFlags *flag.GlobalFlagGroup) *cobra.Command {
} }
sbomFlags.CacheFlagGroup.CacheBackend.Default = string(cache.TypeMemory) // Use memory cache by default sbomFlags.CacheFlagGroup.CacheBackend.Default = string(cache.TypeMemory) // Use memory cache by default
sbomFlags.PackageFlagGroup.IncludeDevDeps = nil // disable '--include-dev-deps'
cmd := &cobra.Command{ cmd := &cobra.Command{
Use: "sbom [flags] SBOM_PATH", Use: "sbom [flags] SBOM_PATH",

View File

@@ -479,6 +479,7 @@ func (r *runner) initScannerConfig(opts flag.Options) (ScannerConfig, types.Scan
scanOptions := types.ScanOptions{ scanOptions := types.ScanOptions{
PkgTypes: opts.PkgTypes, PkgTypes: opts.PkgTypes,
PkgRelationships: opts.PkgRelationships,
Scanners: opts.Scanners, Scanners: opts.Scanners,
ImageConfigScanners: opts.ImageConfigScanners, // this is valid only for 'image' subcommand ImageConfigScanners: opts.ImageConfigScanners, // this is valid only for 'image' subcommand
ScanRemovedPackages: opts.ScanRemovedPkgs, // this is valid only for 'image' subcommand ScanRemovedPackages: opts.ScanRemovedPkgs, // this is valid only for 'image' subcommand
@@ -488,18 +489,24 @@ func (r *runner) initScannerConfig(opts flag.Options) (ScannerConfig, types.Scan
} }
if len(opts.ImageConfigScanners) != 0 { if len(opts.ImageConfigScanners) != 0 {
log.Info("Container image config scanners", log.Any("scanners", opts.ImageConfigScanners)) log.WithPrefix(log.PrefixContainerImage).Info("Container image config scanners", log.Any("scanners", opts.ImageConfigScanners))
}
if opts.Scanners.Enabled(types.SBOMScanner) {
logger := log.WithPrefix(log.PrefixPackage)
logger.Debug("Package types", log.Any("types", scanOptions.PkgTypes))
logger.Debug("Package relationships", log.Any("relationships", scanOptions.PkgRelationships))
} }
if opts.Scanners.Enabled(types.VulnerabilityScanner) { if opts.Scanners.Enabled(types.VulnerabilityScanner) {
log.Info("Vulnerability scanning is enabled") log.WithPrefix(log.PrefixVulnerability).Info("Vulnerability scanning is enabled")
log.Debug("Package types", log.Any("types", scanOptions.PkgTypes))
} }
// ScannerOption is filled only when config scanning is enabled. // ScannerOption is filled only when config scanning is enabled.
var configScannerOptions misconf.ScannerOption var configScannerOptions misconf.ScannerOption
if opts.Scanners.Enabled(types.MisconfigScanner) || opts.ImageConfigScanners.Enabled(types.MisconfigScanner) { if opts.Scanners.Enabled(types.MisconfigScanner) || opts.ImageConfigScanners.Enabled(types.MisconfigScanner) {
log.Info("Misconfiguration scanning is enabled") logger := log.WithPrefix(log.PrefixMisconfiguration)
logger.Info("Misconfiguration scanning is enabled")
var downloadedPolicyPaths []string var downloadedPolicyPaths []string
var disableEmbedded bool var disableEmbedded bool
@@ -507,10 +514,10 @@ func (r *runner) initScannerConfig(opts flag.Options) (ScannerConfig, types.Scan
downloadedPolicyPaths, err := operation.InitBuiltinPolicies(context.Background(), opts.CacheDir, opts.Quiet, opts.SkipCheckUpdate, opts.MisconfOptions.ChecksBundleRepository, opts.RegistryOpts()) downloadedPolicyPaths, err := operation.InitBuiltinPolicies(context.Background(), opts.CacheDir, opts.Quiet, opts.SkipCheckUpdate, opts.MisconfOptions.ChecksBundleRepository, opts.RegistryOpts())
if err != nil { if err != nil {
if !opts.SkipCheckUpdate { if !opts.SkipCheckUpdate {
log.Error("Falling back to embedded checks", log.Err(err)) logger.Error("Falling back to embedded checks", log.Err(err))
} }
} else { } else {
log.Debug("Policies successfully loaded from disk") logger.Debug("Policies successfully loaded from disk")
disableEmbedded = true disableEmbedded = true
} }
configScannerOptions = misconf.ScannerOption{ configScannerOptions = misconf.ScannerOption{
@@ -537,19 +544,21 @@ func (r *runner) initScannerConfig(opts flag.Options) (ScannerConfig, types.Scan
// Do not load config file for secret scanning // Do not load config file for secret scanning
if opts.Scanners.Enabled(types.SecretScanner) { if opts.Scanners.Enabled(types.SecretScanner) {
log.Info("Secret scanning is enabled") logger := log.WithPrefix(log.PrefixSecret)
log.Info("If your scanning is slow, please try '--scanners vuln' to disable secret scanning") logger.Info("Secret scanning is enabled")
logger.Info("If your scanning is slow, please try '--scanners vuln' to disable secret scanning")
// e.g. https://aquasecurity.github.io/trivy/latest/docs/scanner/secret/#recommendation // e.g. https://aquasecurity.github.io/trivy/latest/docs/scanner/secret/#recommendation
log.Infof("Please see also %s for faster secret detection", doc.URL("/docs/scanner/secret/", "recommendation")) logger.Info(fmt.Sprintf("Please see also %s for faster secret detection", doc.URL("/docs/scanner/secret/", "recommendation")))
} else { } else {
opts.SecretConfigPath = "" opts.SecretConfigPath = ""
} }
if opts.Scanners.Enabled(types.LicenseScanner) { if opts.Scanners.Enabled(types.LicenseScanner) {
logger := log.WithPrefix(log.PrefixLicense)
if opts.LicenseFull { if opts.LicenseFull {
log.Info("Full license scanning is enabled") logger.Info("Full license scanning is enabled")
} else { } else {
log.Info("License scanning is enabled") logger.Info("License scanning is enabled")
} }
} }

View File

@@ -20,11 +20,29 @@ const (
RelationshipIndirect RelationshipIndirect
) )
var relationshipNames = [...]string{ var (
"unknown", Relationships = []Relationship{
"root", RelationshipUnknown,
"direct", RelationshipRoot,
"indirect", RelationshipDirect,
RelationshipIndirect,
}
relationshipNames = [...]string{
"unknown",
"root",
"direct",
"indirect",
}
)
func NewRelationship(s string) (Relationship, error) {
for i, name := range relationshipNames {
if s == name {
return Relationship(i), nil
}
}
return RelationshipUnknown, xerrors.Errorf("invalid relationship (%s)", s)
} }
func (r Relationship) String() string { func (r Relationship) String() string {

View File

@@ -320,6 +320,7 @@ type Flags struct {
LicenseFlagGroup *LicenseFlagGroup LicenseFlagGroup *LicenseFlagGroup
MisconfFlagGroup *MisconfFlagGroup MisconfFlagGroup *MisconfFlagGroup
ModuleFlagGroup *ModuleFlagGroup ModuleFlagGroup *ModuleFlagGroup
PackageFlagGroup *PackageFlagGroup
RemoteFlagGroup *RemoteFlagGroup RemoteFlagGroup *RemoteFlagGroup
RegistryFlagGroup *RegistryFlagGroup RegistryFlagGroup *RegistryFlagGroup
RegoFlagGroup *RegoFlagGroup RegoFlagGroup *RegoFlagGroup
@@ -343,6 +344,7 @@ type Options struct {
LicenseOptions LicenseOptions
MisconfOptions MisconfOptions
ModuleOptions ModuleOptions
PackageOptions
RegistryOptions RegistryOptions
RegoOptions RegoOptions
RemoteOptions RemoteOptions
@@ -370,6 +372,12 @@ func (o *Options) Align(f *Flags) error {
o.enableSBOM() o.enableSBOM()
} }
if f.PackageFlagGroup != nil && f.PackageFlagGroup.PkgRelationships != nil &&
slices.Compare(o.PkgRelationships, ftypes.Relationships) != 0 &&
(o.DependencyTree || slices.Contains(types.SupportedSBOMFormats, o.Format) || len(o.VEXSources) != 0) {
return xerrors.Errorf("'--pkg-relationships' cannot be used with '--dependency-tree', '--vex' or SBOM formats")
}
if o.Compliance.Spec.ID != "" { if o.Compliance.Spec.ID != "" {
if viper.IsSet(ScannersFlag.ConfigName) { if viper.IsSet(ScannersFlag.ConfigName) {
log.Info(`The option to change scanners is disabled for scanning with the "--compliance" flag. Default scanners used.`) log.Info(`The option to change scanners is disabled for scanning with the "--compliance" flag. Default scanners used.`)
@@ -568,6 +576,9 @@ func (f *Flags) groups() []FlagGroup {
if f.K8sFlagGroup != nil { if f.K8sFlagGroup != nil {
groups = append(groups, f.K8sFlagGroup) groups = append(groups, f.K8sFlagGroup)
} }
if f.PackageFlagGroup != nil {
groups = append(groups, f.PackageFlagGroup)
}
if f.RemoteFlagGroup != nil { if f.RemoteFlagGroup != nil {
groups = append(groups, f.RemoteFlagGroup) groups = append(groups, f.RemoteFlagGroup)
} }
@@ -707,6 +718,13 @@ func (f *Flags) ToOptions(args []string) (Options, error) {
} }
} }
if f.PackageFlagGroup != nil {
opts.PackageOptions, err = f.PackageFlagGroup.ToOptions()
if err != nil {
return Options{}, xerrors.Errorf("package flag error: %w", err)
}
}
if f.RegoFlagGroup != nil { if f.RegoFlagGroup != nil {
opts.RegoOptions, err = f.RegoFlagGroup.ToOptions() opts.RegoOptions, err = f.RegoFlagGroup.ToOptions()
if err != nil { if err != nil {

91
pkg/flag/package_flags.go Normal file
View File

@@ -0,0 +1,91 @@
package flag
import (
ftypes "github.com/aquasecurity/trivy/pkg/fanal/types"
"github.com/aquasecurity/trivy/pkg/types"
xstrings "github.com/aquasecurity/trivy/pkg/x/strings"
)
var (
IncludeDevDepsFlag = Flag[bool]{
Name: "include-dev-deps",
ConfigName: "pkg.include-dev-deps",
Usage: "include development dependencies in the report (supported: npm, yarn)",
}
PkgTypesFlag = Flag[[]string]{
Name: "pkg-types",
ConfigName: "pkg.types",
Default: types.PkgTypes,
Values: types.PkgTypes,
Usage: "list of package types",
Aliases: []Alias{
{
Name: "vuln-type",
ConfigName: "vulnerability.type",
Deprecated: true, // --vuln-type was renamed to --pkg-types
},
},
}
PkgRelationshipsFlag = Flag[[]string]{
Name: "pkg-relationships",
ConfigName: "pkg.relationships",
Default: xstrings.ToStringSlice(ftypes.Relationships),
Values: xstrings.ToStringSlice(ftypes.Relationships),
Usage: "list of package relationships",
}
)
// PackageFlagGroup composes common package flag structs.
// These flags affect both SBOM and vulnerability scanning.
type PackageFlagGroup struct {
IncludeDevDeps *Flag[bool]
PkgTypes *Flag[[]string]
PkgRelationships *Flag[[]string]
}
type PackageOptions struct {
IncludeDevDeps bool
PkgTypes []string
PkgRelationships []ftypes.Relationship
}
func NewPackageFlagGroup() *PackageFlagGroup {
return &PackageFlagGroup{
IncludeDevDeps: IncludeDevDepsFlag.Clone(),
PkgTypes: PkgTypesFlag.Clone(),
PkgRelationships: PkgRelationshipsFlag.Clone(),
}
}
func (f *PackageFlagGroup) Name() string {
return "Package"
}
func (f *PackageFlagGroup) Flags() []Flagger {
return []Flagger{
f.IncludeDevDeps,
f.PkgTypes,
f.PkgRelationships,
}
}
func (f *PackageFlagGroup) ToOptions() (PackageOptions, error) {
if err := parseFlags(f); err != nil {
return PackageOptions{}, err
}
var relationships []ftypes.Relationship
for _, r := range f.PkgRelationships.Value() {
relationship, err := ftypes.NewRelationship(r)
if err != nil {
return PackageOptions{}, err
}
relationships = append(relationships, relationship)
}
return PackageOptions{
IncludeDevDeps: f.IncludeDevDeps.Value(),
PkgTypes: f.PkgTypes.Value(),
PkgRelationships: relationships,
}, nil
}

View File

@@ -0,0 +1,84 @@
package flag_test
import (
"testing"
"github.com/spf13/viper"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
ftypes "github.com/aquasecurity/trivy/pkg/fanal/types"
"github.com/aquasecurity/trivy/pkg/flag"
"github.com/aquasecurity/trivy/pkg/types"
)
func TestPackageFlagGroup_ToOptions(t *testing.T) {
type fields struct {
pkgTypes string
pkgRelationships string
}
tests := []struct {
name string
fields fields
want flag.PackageOptions
wantLogs []string
}{
{
name: "happy default (without flags)",
fields: fields{},
want: flag.PackageOptions{},
},
{
name: "happy path for OS packages",
fields: fields{
pkgTypes: "os",
},
want: flag.PackageOptions{
PkgTypes: []string{
types.PkgTypeOS,
},
},
},
{
name: "happy path for library packages",
fields: fields{
pkgTypes: "library",
},
want: flag.PackageOptions{
PkgTypes: []string{
types.PkgTypeLibrary,
},
},
},
{
name: "root and indirect relationships",
fields: fields{
pkgRelationships: "root,indirect",
},
want: flag.PackageOptions{
PkgRelationships: []ftypes.Relationship{
ftypes.RelationshipRoot,
ftypes.RelationshipIndirect,
},
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
t.Cleanup(viper.Reset)
setValue(flag.PkgTypesFlag.ConfigName, tt.fields.pkgTypes)
setValue(flag.PkgRelationshipsFlag.ConfigName, tt.fields.pkgRelationships)
// Assert options
f := &flag.PackageFlagGroup{
PkgTypes: flag.PkgTypesFlag.Clone(),
PkgRelationships: flag.PkgRelationshipsFlag.Clone(),
}
got, err := f.ToOptions()
require.NoError(t, err)
assert.EqualExportedValuesf(t, tt.want, got, "PackageFlagGroup")
})
}
}

View File

@@ -106,20 +106,6 @@ var (
ConfigName: "scan.show-suppressed", ConfigName: "scan.show-suppressed",
Usage: "[EXPERIMENTAL] show suppressed vulnerabilities", Usage: "[EXPERIMENTAL] show suppressed vulnerabilities",
} }
PkgTypesFlag = Flag[[]string]{
Name: "pkg-types",
ConfigName: "pkg-types",
Default: types.PkgTypes,
Values: types.PkgTypes,
Usage: "comma-separated list of package types",
Aliases: []Alias{
{
Name: "vuln-type",
ConfigName: "vulnerability.type",
Deprecated: true, // --vuln-type was renamed to --pkg-types
},
},
}
) )
// ReportFlagGroup composes common printer flag structs // ReportFlagGroup composes common printer flag structs
@@ -139,7 +125,6 @@ type ReportFlagGroup struct {
Severity *Flag[[]string] Severity *Flag[[]string]
Compliance *Flag[string] Compliance *Flag[string]
ShowSuppressed *Flag[bool] ShowSuppressed *Flag[bool]
PkgTypes *Flag[[]string]
} }
type ReportOptions struct { type ReportOptions struct {
@@ -157,7 +142,6 @@ type ReportOptions struct {
Severities []dbTypes.Severity Severities []dbTypes.Severity
Compliance spec.ComplianceSpec Compliance spec.ComplianceSpec
ShowSuppressed bool ShowSuppressed bool
PkgTypes []string
} }
func NewReportFlagGroup() *ReportFlagGroup { func NewReportFlagGroup() *ReportFlagGroup {
@@ -176,7 +160,6 @@ func NewReportFlagGroup() *ReportFlagGroup {
Severity: SeverityFlag.Clone(), Severity: SeverityFlag.Clone(),
Compliance: ComplianceFlag.Clone(), Compliance: ComplianceFlag.Clone(),
ShowSuppressed: ShowSuppressedFlag.Clone(), ShowSuppressed: ShowSuppressedFlag.Clone(),
PkgTypes: PkgTypesFlag.Clone(),
} }
} }
@@ -200,7 +183,6 @@ func (f *ReportFlagGroup) Flags() []Flagger {
f.Severity, f.Severity,
f.Compliance, f.Compliance,
f.ShowSuppressed, f.ShowSuppressed,
f.PkgTypes,
} }
} }
@@ -270,7 +252,6 @@ func (f *ReportFlagGroup) ToOptions() (ReportOptions, error) {
Severities: toSeverity(f.Severity.Value()), Severities: toSeverity(f.Severity.Value()),
Compliance: cs, Compliance: cs,
ShowSuppressed: f.ShowSuppressed.Value(), ShowSuppressed: f.ShowSuppressed.Value(),
PkgTypes: f.PkgTypes.Value(),
}, nil }, nil
} }

View File

@@ -160,28 +160,6 @@ func TestReportFlagGroup_ToOptions(t *testing.T) {
Severities: []dbTypes.Severity{dbTypes.SeverityLow}, Severities: []dbTypes.Severity{dbTypes.SeverityLow},
}, },
}, },
{
name: "happy path for OS packages",
fields: fields{
pkgTypes: "os",
},
want: flag.ReportOptions{
PkgTypes: []string{
types.PkgTypeOS,
},
},
},
{
name: "happy path for library packages",
fields: fields{
pkgTypes: "library",
},
want: flag.ReportOptions{
PkgTypes: []string{
types.PkgTypeLibrary,
},
},
},
} }
for _, tt := range tests { for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) { t.Run(tt.name, func(t *testing.T) {
@@ -206,7 +184,6 @@ func TestReportFlagGroup_ToOptions(t *testing.T) {
setValue(flag.OutputPluginArgFlag.ConfigName, tt.fields.outputPluginArgs) setValue(flag.OutputPluginArgFlag.ConfigName, tt.fields.outputPluginArgs)
setValue(flag.SeverityFlag.ConfigName, tt.fields.severities) setValue(flag.SeverityFlag.ConfigName, tt.fields.severities)
setValue(flag.ComplianceFlag.ConfigName, tt.fields.compliance) setValue(flag.ComplianceFlag.ConfigName, tt.fields.compliance)
setValue(flag.PkgTypesFlag.ConfigName, tt.fields.pkgTypes)
// Assert options // Assert options
f := &flag.ReportFlagGroup{ f := &flag.ReportFlagGroup{
@@ -222,7 +199,6 @@ func TestReportFlagGroup_ToOptions(t *testing.T) {
OutputPluginArg: flag.OutputPluginArgFlag.Clone(), OutputPluginArg: flag.OutputPluginArgFlag.Clone(),
Severity: flag.SeverityFlag.Clone(), Severity: flag.SeverityFlag.Clone(),
Compliance: flag.ComplianceFlag.Clone(), Compliance: flag.ComplianceFlag.Clone(),
PkgTypes: flag.PkgTypesFlag.Clone(),
} }
got, err := f.ToOptions() got, err := f.ToOptions()

View File

@@ -96,51 +96,43 @@ var (
Default: "https://rekor.sigstore.dev", Default: "https://rekor.sigstore.dev",
Usage: "[EXPERIMENTAL] address of rekor STL server", Usage: "[EXPERIMENTAL] address of rekor STL server",
} }
IncludeDevDepsFlag = Flag[bool]{
Name: "include-dev-deps",
ConfigName: "scan.include-dev-deps",
Usage: "include development dependencies in the report (supported: npm, yarn)",
}
) )
type ScanFlagGroup struct { type ScanFlagGroup struct {
SkipDirs *Flag[[]string] SkipDirs *Flag[[]string]
SkipFiles *Flag[[]string] SkipFiles *Flag[[]string]
OfflineScan *Flag[bool] OfflineScan *Flag[bool]
Scanners *Flag[[]string] Scanners *Flag[[]string]
FilePatterns *Flag[[]string] FilePatterns *Flag[[]string]
Slow *Flag[bool] // deprecated Slow *Flag[bool] // deprecated
Parallel *Flag[int] Parallel *Flag[int]
SBOMSources *Flag[[]string] SBOMSources *Flag[[]string]
RekorURL *Flag[string] RekorURL *Flag[string]
IncludeDevDeps *Flag[bool]
} }
type ScanOptions struct { type ScanOptions struct {
Target string Target string
SkipDirs []string SkipDirs []string
SkipFiles []string SkipFiles []string
OfflineScan bool OfflineScan bool
Scanners types.Scanners Scanners types.Scanners
FilePatterns []string FilePatterns []string
Parallel int Parallel int
SBOMSources []string SBOMSources []string
RekorURL string RekorURL string
IncludeDevDeps bool
} }
func NewScanFlagGroup() *ScanFlagGroup { func NewScanFlagGroup() *ScanFlagGroup {
return &ScanFlagGroup{ return &ScanFlagGroup{
SkipDirs: SkipDirsFlag.Clone(), SkipDirs: SkipDirsFlag.Clone(),
SkipFiles: SkipFilesFlag.Clone(), SkipFiles: SkipFilesFlag.Clone(),
OfflineScan: OfflineScanFlag.Clone(), OfflineScan: OfflineScanFlag.Clone(),
Scanners: ScannersFlag.Clone(), Scanners: ScannersFlag.Clone(),
FilePatterns: FilePatternsFlag.Clone(), FilePatterns: FilePatternsFlag.Clone(),
Parallel: ParallelFlag.Clone(), Parallel: ParallelFlag.Clone(),
SBOMSources: SBOMSourcesFlag.Clone(), SBOMSources: SBOMSourcesFlag.Clone(),
RekorURL: RekorURLFlag.Clone(), RekorURL: RekorURLFlag.Clone(),
IncludeDevDeps: IncludeDevDepsFlag.Clone(), Slow: SlowFlag.Clone(),
Slow: SlowFlag.Clone(),
} }
} }
@@ -159,7 +151,6 @@ func (f *ScanFlagGroup) Flags() []Flagger {
f.Parallel, f.Parallel,
f.SBOMSources, f.SBOMSources,
f.RekorURL, f.RekorURL,
f.IncludeDevDeps,
} }
} }
@@ -180,15 +171,14 @@ func (f *ScanFlagGroup) ToOptions(args []string) (ScanOptions, error) {
} }
return ScanOptions{ return ScanOptions{
Target: target, Target: target,
SkipDirs: f.SkipDirs.Value(), SkipDirs: f.SkipDirs.Value(),
SkipFiles: f.SkipFiles.Value(), SkipFiles: f.SkipFiles.Value(),
OfflineScan: f.OfflineScan.Value(), OfflineScan: f.OfflineScan.Value(),
Scanners: xstrings.ToTSlice[types.Scanner](f.Scanners.Value()), Scanners: xstrings.ToTSlice[types.Scanner](f.Scanners.Value()),
FilePatterns: f.FilePatterns.Value(), FilePatterns: f.FilePatterns.Value(),
Parallel: parallel, Parallel: parallel,
SBOMSources: f.SBOMSources.Value(), SBOMSources: f.SBOMSources.Value(),
RekorURL: f.RekorURL.Value(), RekorURL: f.RekorURL.Value(),
IncludeDevDeps: f.IncludeDevDeps.Value(),
}, nil }, nil
} }

View File

@@ -17,6 +17,13 @@ const (
LevelWarn = slog.LevelWarn LevelWarn = slog.LevelWarn
LevelError = slog.LevelError LevelError = slog.LevelError
LevelFatal = slog.Level(12) LevelFatal = slog.Level(12)
PrefixContainerImage = "image"
PrefixPackage = "pkg"
PrefixVulnerability = "vuln"
PrefixMisconfiguration = "misconfig"
PrefixSecret = "secret"
PrefixLicense = "license"
) )
// Logger is an alias of slog.Logger // Logger is an alias of slog.Logger

View File

@@ -83,6 +83,7 @@ func (s Scanner) Scan(ctx context.Context, target, artifactKey string, blobKeys
BlobIds: blobKeys, BlobIds: blobKeys,
Options: &rpc.ScanOptions{ Options: &rpc.ScanOptions{
PkgTypes: opts.PkgTypes, PkgTypes: opts.PkgTypes,
PkgRelationships: xstrings.ToStringSlice(opts.PkgRelationships),
Scanners: xstrings.ToStringSlice(opts.Scanners), Scanners: xstrings.ToStringSlice(opts.Scanners),
LicenseCategories: licenseCategories, LicenseCategories: licenseCategories,
IncludeDevDeps: opts.IncludeDevDeps, IncludeDevDeps: opts.IncludeDevDeps,

View File

@@ -44,21 +44,7 @@ func teeError(err error) error {
// Scan scans and return response // Scan scans and return response
func (s *ScanServer) Scan(ctx context.Context, in *rpcScanner.ScanRequest) (*rpcScanner.ScanResponse, error) { func (s *ScanServer) Scan(ctx context.Context, in *rpcScanner.ScanRequest) (*rpcScanner.ScanResponse, error) {
scanners := lo.Map(in.Options.Scanners, func(s string, index int) types.Scanner { options := s.ToOptions(in.Options)
return types.Scanner(s)
})
licenseCategories := lo.MapEntries(in.Options.LicenseCategories,
func(k string, v *rpcScanner.Licenses) (ftypes.LicenseCategory, []string) {
return ftypes.LicenseCategory(k), v.Names
})
options := types.ScanOptions{
PkgTypes: in.Options.PkgTypes,
Scanners: scanners,
IncludeDevDeps: in.Options.IncludeDevDeps,
LicenseCategories: licenseCategories,
}
results, os, err := s.localScanner.Scan(ctx, in.Target, in.ArtifactId, in.BlobIds, options) results, os, err := s.localScanner.Scan(ctx, in.Target, in.ArtifactId, in.BlobIds, options)
if err != nil { if err != nil {
return nil, teeError(xerrors.Errorf("failed scan, %s: %w", in.Target, err)) return nil, teeError(xerrors.Errorf("failed scan, %s: %w", in.Target, err))
@@ -67,6 +53,37 @@ func (s *ScanServer) Scan(ctx context.Context, in *rpcScanner.ScanRequest) (*rpc
return rpc.ConvertToRPCScanResponse(results, os), nil return rpc.ConvertToRPCScanResponse(results, os), nil
} }
func (s *ScanServer) ToOptions(in *rpcScanner.ScanOptions) types.ScanOptions {
pkgRelationships := lo.FilterMap(in.PkgRelationships, func(r string, index int) (ftypes.Relationship, bool) {
rel, err := ftypes.NewRelationship(r)
if err != nil {
log.Warnf("Invalid relationship: %s", r)
return ftypes.RelationshipUnknown, false
}
return rel, true
})
if len(pkgRelationships) == 0 {
pkgRelationships = ftypes.Relationships // For backward compatibility
}
scanners := lo.Map(in.Scanners, func(s string, index int) types.Scanner {
return types.Scanner(s)
})
licenseCategories := lo.MapEntries(in.LicenseCategories,
func(k string, v *rpcScanner.Licenses) (ftypes.LicenseCategory, []string) {
return ftypes.LicenseCategory(k), v.Names
})
return types.ScanOptions{
PkgTypes: in.PkgTypes,
PkgRelationships: pkgRelationships,
Scanners: scanners,
IncludeDevDeps: in.IncludeDevDeps,
LicenseCategories: licenseCategories,
}
}
// CacheServer implements the cache // CacheServer implements the cache
type CacheServer struct { type CacheServer struct {
cache cache.Cache cache cache.Cache

View File

@@ -108,9 +108,8 @@ func (s Scanner) Scan(ctx context.Context, targetName, artifactKey string, blobK
func (s Scanner) ScanTarget(ctx context.Context, target types.ScanTarget, options types.ScanOptions) (types.Results, ftypes.OS, error) { func (s Scanner) ScanTarget(ctx context.Context, target types.ScanTarget, options types.ScanOptions) (types.Results, ftypes.OS, error) {
var results types.Results var results types.Results
// By default, we need to remove dev dependencies from the result // Filter packages according to the options
// IncludeDevDeps option allows you not to remove them excludePackages(&target, options)
excludeDevDeps(target.Applications, options.IncludeDevDeps)
// Add packages if needed and scan packages for vulnerabilities // Add packages if needed and scan packages for vulnerabilities
vulnResults, eosl, err := s.scanVulnerabilities(ctx, target, options) vulnResults, eosl, err := s.scanVulnerabilities(ctx, target, options)
@@ -395,6 +394,32 @@ func ShouldScanMisconfigOrRbac(scanners types.Scanners) bool {
return scanners.AnyEnabled(types.MisconfigScanner, types.RBACScanner) return scanners.AnyEnabled(types.MisconfigScanner, types.RBACScanner)
} }
func excludePackages(target *types.ScanTarget, options types.ScanOptions) {
// Filter packages by relationship
filterPkgByRelationship(target, options)
// By default, development packages are removed from the result
// '--include-dev-deps' option allows including them
excludeDevDeps(target.Applications, options.IncludeDevDeps)
}
func filterPkgByRelationship(target *types.ScanTarget, options types.ScanOptions) {
if slices.Compare(options.PkgRelationships, ftypes.Relationships) == 0 {
return // No need to filter
}
filter := func(pkgs []ftypes.Package) []ftypes.Package {
return lo.Filter(pkgs, func(pkg ftypes.Package, index int) bool {
return slices.Contains(options.PkgRelationships, pkg.Relationship)
})
}
target.Packages = filter(target.Packages)
for i, app := range target.Applications {
target.Applications[i].Packages = filter(app.Packages)
}
}
// excludeDevDeps removes development dependencies from the list of applications // excludeDevDeps removes development dependencies from the list of applications
func excludeDevDeps(apps []ftypes.Application, include bool) { func excludeDevDeps(apps []ftypes.Application, include bool) {
if include { if include {

View File

@@ -65,8 +65,17 @@ var (
Licenses: []string{"MIT"}, Licenses: []string{"MIT"},
} }
laravelPkg = ftypes.Package{ laravelPkg = ftypes.Package{
Name: "laravel/framework", Name: "laravel/framework",
Version: "6.0.0", Version: "6.0.0",
Relationship: ftypes.RelationshipDirect,
Layer: ftypes.Layer{
DiffID: "sha256:0ea33a93585cf1917ba522b2304634c3073654062d5282c1346322967790ef33",
},
}
guzzlePkg = ftypes.Package{
Name: "guzzlehttp/guzzle",
Version: "7.9.2",
Relationship: ftypes.RelationshipIndirect,
Layer: ftypes.Layer{ Layer: ftypes.Layer{
DiffID: "sha256:0ea33a93585cf1917ba522b2304634c3073654062d5282c1346322967790ef33", DiffID: "sha256:0ea33a93585cf1917ba522b2304634c3073654062d5282c1346322967790ef33",
}, },
@@ -98,7 +107,8 @@ func TestScanner_Scan(t *testing.T) {
types.PkgTypeOS, types.PkgTypeOS,
types.PkgTypeLibrary, types.PkgTypeLibrary,
}, },
Scanners: types.Scanners{types.VulnerabilityScanner}, PkgRelationships: ftypes.Relationships,
Scanners: types.Scanners{types.VulnerabilityScanner},
}, },
}, },
fixtures: []string{"testdata/fixtures/happy.yaml"}, fixtures: []string{"testdata/fixtures/happy.yaml"},
@@ -198,7 +208,8 @@ func TestScanner_Scan(t *testing.T) {
target: "alpine:latest", target: "alpine:latest",
layerIDs: []string{"sha256:5216338b40a7b96416b8b9858974bbe4acc3096ee60acbc4dfb1ee02aecceb10"}, layerIDs: []string{"sha256:5216338b40a7b96416b8b9858974bbe4acc3096ee60acbc4dfb1ee02aecceb10"},
options: types.ScanOptions{ options: types.ScanOptions{
Scanners: types.Scanners{types.LicenseScanner}, PkgRelationships: ftypes.Relationships,
Scanners: types.Scanners{types.LicenseScanner},
}, },
}, },
fixtures: []string{"testdata/fixtures/happy.yaml"}, fixtures: []string{"testdata/fixtures/happy.yaml"},
@@ -298,7 +309,8 @@ func TestScanner_Scan(t *testing.T) {
types.PkgTypeOS, types.PkgTypeOS,
types.PkgTypeLibrary, types.PkgTypeLibrary,
}, },
Scanners: types.Scanners{types.VulnerabilityScanner}, PkgRelationships: ftypes.Relationships,
Scanners: types.Scanners{types.VulnerabilityScanner},
}, },
}, },
fixtures: []string{"testdata/fixtures/happy.yaml"}, fixtures: []string{"testdata/fixtures/happy.yaml"},
@@ -377,8 +389,9 @@ func TestScanner_Scan(t *testing.T) {
target: "./result.cdx", target: "./result.cdx",
layerIDs: []string{"sha256:5216338b40a7b96416b8b9858974bbe4acc3096ee60acbc4dfb1ee02aecceb10"}, layerIDs: []string{"sha256:5216338b40a7b96416b8b9858974bbe4acc3096ee60acbc4dfb1ee02aecceb10"},
options: types.ScanOptions{ options: types.ScanOptions{
PkgTypes: []string{types.PkgTypeLibrary}, PkgTypes: []string{types.PkgTypeLibrary},
Scanners: types.Scanners{types.VulnerabilityScanner}, PkgRelationships: ftypes.Relationships,
Scanners: types.Scanners{types.VulnerabilityScanner},
}, },
}, },
fixtures: []string{"testdata/fixtures/happy.yaml"}, fixtures: []string{"testdata/fixtures/happy.yaml"},
@@ -477,7 +490,8 @@ func TestScanner_Scan(t *testing.T) {
types.PkgTypeOS, types.PkgTypeOS,
types.PkgTypeLibrary, types.PkgTypeLibrary,
}, },
Scanners: types.Scanners{types.VulnerabilityScanner}, PkgRelationships: ftypes.Relationships,
Scanners: types.Scanners{types.VulnerabilityScanner},
}, },
}, },
fixtures: []string{"testdata/fixtures/happy.yaml"}, fixtures: []string{"testdata/fixtures/happy.yaml"},
@@ -558,7 +572,8 @@ func TestScanner_Scan(t *testing.T) {
types.PkgTypeOS, types.PkgTypeOS,
types.PkgTypeLibrary, types.PkgTypeLibrary,
}, },
Scanners: types.Scanners{types.VulnerabilityScanner}, PkgRelationships: ftypes.Relationships,
Scanners: types.Scanners{types.VulnerabilityScanner},
}, },
}, },
fixtures: []string{"testdata/fixtures/happy.yaml"}, fixtures: []string{"testdata/fixtures/happy.yaml"},
@@ -630,7 +645,8 @@ func TestScanner_Scan(t *testing.T) {
types.PkgTypeOS, types.PkgTypeOS,
types.PkgTypeLibrary, types.PkgTypeLibrary,
}, },
Scanners: types.Scanners{types.VulnerabilityScanner}, PkgRelationships: ftypes.Relationships,
Scanners: types.Scanners{types.VulnerabilityScanner},
}, },
}, },
fixtures: []string{"testdata/fixtures/happy.yaml"}, fixtures: []string{"testdata/fixtures/happy.yaml"},
@@ -645,12 +661,17 @@ func TestScanner_Scan(t *testing.T) {
wantResults: nil, wantResults: nil,
}, },
{ {
name: "happy path with only language-specific package detection", name: "happy path with only language-specific package detection, excluding direct packages",
args: args{ args: args{
target: "alpine:latest", target: "alpine:latest",
layerIDs: []string{"sha256:0ea33a93585cf1917ba522b2304634c3073654062d5282c1346322967790ef33"}, layerIDs: []string{"sha256:0ea33a93585cf1917ba522b2304634c3073654062d5282c1346322967790ef33"},
options: types.ScanOptions{ options: types.ScanOptions{
PkgTypes: []string{types.PkgTypeLibrary}, PkgTypes: []string{types.PkgTypeLibrary},
PkgRelationships: []ftypes.Relationship{
ftypes.RelationshipUnknown,
ftypes.RelationshipRoot,
ftypes.RelationshipIndirect,
},
Scanners: types.Scanners{types.VulnerabilityScanner}, Scanners: types.Scanners{types.VulnerabilityScanner},
}, },
}, },
@@ -680,7 +701,8 @@ func TestScanner_Scan(t *testing.T) {
Type: "composer", Type: "composer",
FilePath: "/app/composer-lock.json", FilePath: "/app/composer-lock.json",
Packages: []ftypes.Package{ Packages: []ftypes.Package{
laravelPkg, laravelPkg, // will be excluded
guzzlePkg,
}, },
}, },
}, },
@@ -721,19 +743,7 @@ func TestScanner_Scan(t *testing.T) {
Target: "/app/composer-lock.json", Target: "/app/composer-lock.json",
Class: types.ClassLangPkg, Class: types.ClassLangPkg,
Type: ftypes.Composer, Type: ftypes.Composer,
Packages: ftypes.Packages{laravelPkg}, Packages: ftypes.Packages{guzzlePkg},
Vulnerabilities: []types.DetectedVulnerability{
{
VulnerabilityID: "CVE-2021-21263",
PkgName: laravelPkg.Name,
InstalledVersion: laravelPkg.Version,
FixedVersion: "8.22.1, 7.30.3, 6.20.12",
Status: dbTypes.StatusFixed,
Layer: ftypes.Layer{
DiffID: "sha256:0ea33a93585cf1917ba522b2304634c3073654062d5282c1346322967790ef33",
},
},
},
}, },
}, },
wantOS: ftypes.OS{ wantOS: ftypes.OS{
@@ -900,7 +910,8 @@ func TestScanner_Scan(t *testing.T) {
types.PkgTypeOS, types.PkgTypeOS,
types.PkgTypeLibrary, types.PkgTypeLibrary,
}, },
Scanners: types.Scanners{types.VulnerabilityScanner}, PkgRelationships: ftypes.Relationships,
Scanners: types.Scanners{types.VulnerabilityScanner},
}, },
}, },
fixtures: []string{"testdata/fixtures/happy.yaml"}, fixtures: []string{"testdata/fixtures/happy.yaml"},
@@ -920,8 +931,9 @@ func TestScanner_Scan(t *testing.T) {
target: "alpine:latest", target: "alpine:latest",
layerIDs: []string{"sha256:5216338b40a7b96416b8b9858974bbe4acc3096ee60acbc4dfb1ee02aecceb10"}, layerIDs: []string{"sha256:5216338b40a7b96416b8b9858974bbe4acc3096ee60acbc4dfb1ee02aecceb10"},
options: types.ScanOptions{ options: types.ScanOptions{
PkgTypes: []string{types.PkgTypeLibrary}, PkgTypes: []string{types.PkgTypeLibrary},
Scanners: types.Scanners{types.VulnerabilityScanner}, PkgRelationships: ftypes.Relationships,
Scanners: types.Scanners{types.VulnerabilityScanner},
}, },
}, },
fixtures: []string{"testdata/fixtures/sad.yaml"}, fixtures: []string{"testdata/fixtures/sad.yaml"},
@@ -1103,8 +1115,7 @@ func TestScanner_Scan(t *testing.T) {
s := NewScanner(applier, ospkg.NewScanner(), langpkg.NewScanner(), vulnerability.NewClient(db.Config{})) s := NewScanner(applier, ospkg.NewScanner(), langpkg.NewScanner(), vulnerability.NewClient(db.Config{}))
gotResults, gotOS, err := s.Scan(context.Background(), tt.args.target, "", tt.args.layerIDs, tt.args.options) gotResults, gotOS, err := s.Scan(context.Background(), tt.args.target, "", tt.args.layerIDs, tt.args.options)
if tt.wantErr != "" { if tt.wantErr != "" {
require.Error(t, err, tt.name) require.ErrorContains(t, err, tt.wantErr, tt.name)
require.Contains(t, err.Error(), tt.wantErr, tt.name)
return return
} }

View File

@@ -23,6 +23,7 @@ type ScanTarget struct {
// ScanOptions holds the attributes for scanning vulnerabilities // ScanOptions holds the attributes for scanning vulnerabilities
type ScanOptions struct { type ScanOptions struct {
PkgTypes []string PkgTypes []string
PkgRelationships []types.Relationship
Scanners Scanners Scanners Scanners
ImageConfigScanners Scanners // Scanners for container image configuration ImageConfigScanners Scanners // Scanners for container image configuration
ScanRemovedPackages bool ScanRemovedPackages bool

View File

@@ -1,17 +1,28 @@
package strings package strings
import "github.com/samber/lo" import (
"fmt"
"github.com/samber/lo"
)
type String interface { type String interface {
~string ~string
} }
func ToStringSlice[T String](ss []T) []string { func ToStringSlice[T any](ss []T) []string {
if ss == nil { if len(ss) == 0 {
return nil return nil
} }
return lo.Map(ss, func(s T, _ int) string { return lo.Map(ss, func(s T, _ int) string {
return string(s) switch v := any(s).(type) {
case string:
return v
case fmt.Stringer:
return v.String()
default:
return fmt.Sprint(v)
}
}) })
} }

View File

@@ -150,6 +150,7 @@ type ScanOptions struct {
Scanners []string `protobuf:"bytes,2,rep,name=scanners,proto3" json:"scanners,omitempty"` Scanners []string `protobuf:"bytes,2,rep,name=scanners,proto3" json:"scanners,omitempty"`
LicenseCategories map[string]*Licenses `protobuf:"bytes,4,rep,name=license_categories,json=licenseCategories,proto3" json:"license_categories,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` LicenseCategories map[string]*Licenses `protobuf:"bytes,4,rep,name=license_categories,json=licenseCategories,proto3" json:"license_categories,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"`
IncludeDevDeps bool `protobuf:"varint,5,opt,name=include_dev_deps,json=includeDevDeps,proto3" json:"include_dev_deps,omitempty"` IncludeDevDeps bool `protobuf:"varint,5,opt,name=include_dev_deps,json=includeDevDeps,proto3" json:"include_dev_deps,omitempty"`
PkgRelationships []string `protobuf:"bytes,6,rep,name=pkg_relationships,json=pkgRelationships,proto3" json:"pkg_relationships,omitempty"`
} }
func (x *ScanOptions) Reset() { func (x *ScanOptions) Reset() {
@@ -212,6 +213,13 @@ func (x *ScanOptions) GetIncludeDevDeps() bool {
return false return false
} }
func (x *ScanOptions) GetPkgRelationships() []string {
if x != nil {
return x.PkgRelationships
}
return nil
}
type ScanResponse struct { type ScanResponse struct {
state protoimpl.MessageState state protoimpl.MessageState
sizeCache protoimpl.SizeCache sizeCache protoimpl.SizeCache
@@ -398,7 +406,7 @@ var file_rpc_scanner_service_proto_rawDesc = []byte{
0x53, 0x63, 0x61, 0x6e, 0x4f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x07, 0x6f, 0x70, 0x74, 0x53, 0x63, 0x61, 0x6e, 0x4f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x07, 0x6f, 0x70, 0x74,
0x69, 0x6f, 0x6e, 0x73, 0x22, 0x20, 0x0a, 0x08, 0x4c, 0x69, 0x63, 0x65, 0x6e, 0x73, 0x65, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x22, 0x20, 0x0a, 0x08, 0x4c, 0x69, 0x63, 0x65, 0x6e, 0x73, 0x65, 0x73,
0x12, 0x14, 0x0a, 0x05, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x09, 0x52, 0x12, 0x14, 0x0a, 0x05, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x09, 0x52,
0x05, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x22, 0xbd, 0x02, 0x0a, 0x0b, 0x53, 0x63, 0x61, 0x6e, 0x4f, 0x05, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x22, 0xea, 0x02, 0x0a, 0x0b, 0x53, 0x63, 0x61, 0x6e, 0x4f,
0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x1b, 0x0a, 0x09, 0x70, 0x6b, 0x67, 0x5f, 0x74, 0x79, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x1b, 0x0a, 0x09, 0x70, 0x6b, 0x67, 0x5f, 0x74, 0x79,
0x70, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x09, 0x52, 0x08, 0x70, 0x6b, 0x67, 0x54, 0x79, 0x70, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x09, 0x52, 0x08, 0x70, 0x6b, 0x67, 0x54, 0x79,
0x70, 0x65, 0x73, 0x12, 0x1a, 0x0a, 0x08, 0x73, 0x63, 0x61, 0x6e, 0x6e, 0x65, 0x72, 0x73, 0x18, 0x70, 0x65, 0x73, 0x12, 0x1a, 0x0a, 0x08, 0x73, 0x63, 0x61, 0x6e, 0x6e, 0x65, 0x72, 0x73, 0x18,
@@ -411,59 +419,61 @@ var file_rpc_scanner_service_proto_rawDesc = []byte{
0x79, 0x52, 0x11, 0x6c, 0x69, 0x63, 0x65, 0x6e, 0x73, 0x65, 0x43, 0x61, 0x74, 0x65, 0x67, 0x6f, 0x79, 0x52, 0x11, 0x6c, 0x69, 0x63, 0x65, 0x6e, 0x73, 0x65, 0x43, 0x61, 0x74, 0x65, 0x67, 0x6f,
0x72, 0x69, 0x65, 0x73, 0x12, 0x28, 0x0a, 0x10, 0x69, 0x6e, 0x63, 0x6c, 0x75, 0x64, 0x65, 0x5f, 0x72, 0x69, 0x65, 0x73, 0x12, 0x28, 0x0a, 0x10, 0x69, 0x6e, 0x63, 0x6c, 0x75, 0x64, 0x65, 0x5f,
0x64, 0x65, 0x76, 0x5f, 0x64, 0x65, 0x70, 0x73, 0x18, 0x05, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0e, 0x64, 0x65, 0x76, 0x5f, 0x64, 0x65, 0x70, 0x73, 0x18, 0x05, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0e,
0x69, 0x6e, 0x63, 0x6c, 0x75, 0x64, 0x65, 0x44, 0x65, 0x76, 0x44, 0x65, 0x70, 0x73, 0x1a, 0x60, 0x69, 0x6e, 0x63, 0x6c, 0x75, 0x64, 0x65, 0x44, 0x65, 0x76, 0x44, 0x65, 0x70, 0x73, 0x12, 0x2b,
0x0a, 0x16, 0x4c, 0x69, 0x63, 0x65, 0x6e, 0x73, 0x65, 0x43, 0x61, 0x74, 0x65, 0x67, 0x6f, 0x72, 0x0a, 0x11, 0x70, 0x6b, 0x67, 0x5f, 0x72, 0x65, 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x68,
0x69, 0x65, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x69, 0x70, 0x73, 0x18, 0x06, 0x20, 0x03, 0x28, 0x09, 0x52, 0x10, 0x70, 0x6b, 0x67, 0x52, 0x65,
0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x30, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x68, 0x69, 0x70, 0x73, 0x1a, 0x60, 0x0a, 0x16, 0x4c,
0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x74, 0x72, 0x69, 0x76, 0x69, 0x63, 0x65, 0x6e, 0x73, 0x65, 0x43, 0x61, 0x74, 0x65, 0x67, 0x6f, 0x72, 0x69, 0x65, 0x73,
0x79, 0x2e, 0x73, 0x63, 0x61, 0x6e, 0x6e, 0x65, 0x72, 0x2e, 0x76, 0x31, 0x2e, 0x4c, 0x69, 0x63, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01,
0x65, 0x6e, 0x73, 0x65, 0x73, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x30, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65,
0x4a, 0x04, 0x08, 0x03, 0x10, 0x04, 0x22, 0x64, 0x0a, 0x0c, 0x53, 0x63, 0x61, 0x6e, 0x52, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x74, 0x72, 0x69, 0x76, 0x79, 0x2e, 0x73,
0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x20, 0x0a, 0x02, 0x6f, 0x73, 0x18, 0x01, 0x20, 0x01, 0x63, 0x61, 0x6e, 0x6e, 0x65, 0x72, 0x2e, 0x76, 0x31, 0x2e, 0x4c, 0x69, 0x63, 0x65, 0x6e, 0x73,
0x28, 0x0b, 0x32, 0x10, 0x2e, 0x74, 0x72, 0x69, 0x76, 0x79, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x65, 0x73, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x4a, 0x04, 0x08,
0x6e, 0x2e, 0x4f, 0x53, 0x52, 0x02, 0x6f, 0x73, 0x12, 0x32, 0x0a, 0x07, 0x72, 0x65, 0x73, 0x75, 0x03, 0x10, 0x04, 0x22, 0x64, 0x0a, 0x0c, 0x53, 0x63, 0x61, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f,
0x6c, 0x74, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x18, 0x2e, 0x74, 0x72, 0x69, 0x76, 0x6e, 0x73, 0x65, 0x12, 0x20, 0x0a, 0x02, 0x6f, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32,
0x79, 0x2e, 0x73, 0x63, 0x61, 0x6e, 0x6e, 0x65, 0x72, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, 0x73, 0x10, 0x2e, 0x74, 0x72, 0x69, 0x76, 0x79, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x4f,
0x75, 0x6c, 0x74, 0x52, 0x07, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x73, 0x22, 0xd5, 0x03, 0x0a, 0x53, 0x52, 0x02, 0x6f, 0x73, 0x12, 0x32, 0x0a, 0x07, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x73,
0x06, 0x52, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x12, 0x16, 0x0a, 0x06, 0x74, 0x61, 0x72, 0x67, 0x65, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x18, 0x2e, 0x74, 0x72, 0x69, 0x76, 0x79, 0x2e, 0x73,
0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x12, 0x63, 0x61, 0x6e, 0x6e, 0x65, 0x72, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, 0x73, 0x75, 0x6c, 0x74,
0x45, 0x0a, 0x0f, 0x76, 0x75, 0x6c, 0x6e, 0x65, 0x72, 0x61, 0x62, 0x69, 0x6c, 0x69, 0x74, 0x69, 0x52, 0x07, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x73, 0x22, 0xd5, 0x03, 0x0a, 0x06, 0x52, 0x65,
0x65, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x74, 0x72, 0x69, 0x76, 0x79, 0x73, 0x75, 0x6c, 0x74, 0x12, 0x16, 0x0a, 0x06, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x18, 0x01,
0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x56, 0x75, 0x6c, 0x6e, 0x65, 0x72, 0x61, 0x62, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x12, 0x45, 0x0a, 0x0f,
0x69, 0x6c, 0x69, 0x74, 0x79, 0x52, 0x0f, 0x76, 0x75, 0x6c, 0x6e, 0x65, 0x72, 0x61, 0x62, 0x69, 0x76, 0x75, 0x6c, 0x6e, 0x65, 0x72, 0x61, 0x62, 0x69, 0x6c, 0x69, 0x74, 0x69, 0x65, 0x73, 0x18,
0x6c, 0x69, 0x74, 0x69, 0x65, 0x73, 0x12, 0x54, 0x0a, 0x11, 0x6d, 0x69, 0x73, 0x63, 0x6f, 0x6e, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x74, 0x72, 0x69, 0x76, 0x79, 0x2e, 0x63, 0x6f,
0x66, 0x69, 0x67, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x04, 0x20, 0x03, 0x28, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x56, 0x75, 0x6c, 0x6e, 0x65, 0x72, 0x61, 0x62, 0x69, 0x6c, 0x69,
0x0b, 0x32, 0x26, 0x2e, 0x74, 0x72, 0x69, 0x76, 0x79, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x74, 0x79, 0x52, 0x0f, 0x76, 0x75, 0x6c, 0x6e, 0x65, 0x72, 0x61, 0x62, 0x69, 0x6c, 0x69, 0x74,
0x2e, 0x44, 0x65, 0x74, 0x65, 0x63, 0x74, 0x65, 0x64, 0x4d, 0x69, 0x73, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x65, 0x73, 0x12, 0x54, 0x0a, 0x11, 0x6d, 0x69, 0x73, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67,
0x69, 0x67, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x11, 0x6d, 0x69, 0x73, 0x63, 0x6f, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x04, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x26,
0x6e, 0x66, 0x69, 0x67, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x14, 0x0a, 0x05, 0x2e, 0x74, 0x72, 0x69, 0x76, 0x79, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x44, 0x65,
0x63, 0x6c, 0x61, 0x73, 0x73, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x63, 0x6c, 0x61, 0x74, 0x65, 0x63, 0x74, 0x65, 0x64, 0x4d, 0x69, 0x73, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x75,
0x73, 0x73, 0x12, 0x12, 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x11, 0x6d, 0x69, 0x73, 0x63, 0x6f, 0x6e, 0x66, 0x69,
0x52, 0x04, 0x74, 0x79, 0x70, 0x65, 0x12, 0x31, 0x0a, 0x08, 0x70, 0x61, 0x63, 0x6b, 0x61, 0x67, 0x67, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x14, 0x0a, 0x05, 0x63, 0x6c, 0x61,
0x65, 0x73, 0x18, 0x05, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x74, 0x72, 0x69, 0x76, 0x79, 0x73, 0x73, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x63, 0x6c, 0x61, 0x73, 0x73, 0x12,
0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x50, 0x61, 0x63, 0x6b, 0x61, 0x67, 0x65, 0x52, 0x12, 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x74,
0x08, 0x70, 0x61, 0x63, 0x6b, 0x61, 0x67, 0x65, 0x73, 0x12, 0x47, 0x0a, 0x10, 0x63, 0x75, 0x73, 0x79, 0x70, 0x65, 0x12, 0x31, 0x0a, 0x08, 0x70, 0x61, 0x63, 0x6b, 0x61, 0x67, 0x65, 0x73, 0x18,
0x74, 0x6f, 0x6d, 0x5f, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x18, 0x07, 0x20, 0x05, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x74, 0x72, 0x69, 0x76, 0x79, 0x2e, 0x63, 0x6f,
0x03, 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x74, 0x72, 0x69, 0x76, 0x79, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x50, 0x61, 0x63, 0x6b, 0x61, 0x67, 0x65, 0x52, 0x08, 0x70, 0x61,
0x6f, 0x6e, 0x2e, 0x43, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x63, 0x6b, 0x61, 0x67, 0x65, 0x73, 0x12, 0x47, 0x0a, 0x10, 0x63, 0x75, 0x73, 0x74, 0x6f, 0x6d,
0x65, 0x52, 0x0f, 0x63, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x5f, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x18, 0x07, 0x20, 0x03, 0x28, 0x0b,
0x65, 0x73, 0x12, 0x35, 0x0a, 0x07, 0x73, 0x65, 0x63, 0x72, 0x65, 0x74, 0x73, 0x18, 0x08, 0x20, 0x32, 0x1c, 0x2e, 0x74, 0x72, 0x69, 0x76, 0x79, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e,
0x03, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x74, 0x72, 0x69, 0x76, 0x79, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x43, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x52, 0x0f,
0x6f, 0x6e, 0x2e, 0x53, 0x65, 0x63, 0x72, 0x65, 0x74, 0x46, 0x69, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x63, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x12,
0x52, 0x07, 0x73, 0x65, 0x63, 0x72, 0x65, 0x74, 0x73, 0x12, 0x39, 0x0a, 0x08, 0x6c, 0x69, 0x63, 0x35, 0x0a, 0x07, 0x73, 0x65, 0x63, 0x72, 0x65, 0x74, 0x73, 0x18, 0x08, 0x20, 0x03, 0x28, 0x0b,
0x65, 0x6e, 0x73, 0x65, 0x73, 0x18, 0x09, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1d, 0x2e, 0x74, 0x72, 0x32, 0x1b, 0x2e, 0x74, 0x72, 0x69, 0x76, 0x79, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e,
0x69, 0x76, 0x79, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x44, 0x65, 0x74, 0x65, 0x63, 0x53, 0x65, 0x63, 0x72, 0x65, 0x74, 0x46, 0x69, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x52, 0x07, 0x73,
0x74, 0x65, 0x64, 0x4c, 0x69, 0x63, 0x65, 0x6e, 0x73, 0x65, 0x52, 0x08, 0x6c, 0x69, 0x63, 0x65, 0x65, 0x63, 0x72, 0x65, 0x74, 0x73, 0x12, 0x39, 0x0a, 0x08, 0x6c, 0x69, 0x63, 0x65, 0x6e, 0x73,
0x6e, 0x73, 0x65, 0x73, 0x32, 0x50, 0x0a, 0x07, 0x53, 0x63, 0x61, 0x6e, 0x6e, 0x65, 0x72, 0x12, 0x65, 0x73, 0x18, 0x09, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1d, 0x2e, 0x74, 0x72, 0x69, 0x76, 0x79,
0x45, 0x0a, 0x04, 0x53, 0x63, 0x61, 0x6e, 0x12, 0x1d, 0x2e, 0x74, 0x72, 0x69, 0x76, 0x79, 0x2e, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x44, 0x65, 0x74, 0x65, 0x63, 0x74, 0x65, 0x64,
0x73, 0x63, 0x61, 0x6e, 0x6e, 0x65, 0x72, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x63, 0x61, 0x6e, 0x52, 0x4c, 0x69, 0x63, 0x65, 0x6e, 0x73, 0x65, 0x52, 0x08, 0x6c, 0x69, 0x63, 0x65, 0x6e, 0x73, 0x65,
0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1e, 0x2e, 0x74, 0x72, 0x69, 0x76, 0x79, 0x2e, 0x73, 0x73, 0x32, 0x50, 0x0a, 0x07, 0x53, 0x63, 0x61, 0x6e, 0x6e, 0x65, 0x72, 0x12, 0x45, 0x0a, 0x04,
0x63, 0x61, 0x6e, 0x6e, 0x65, 0x72, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x63, 0x61, 0x6e, 0x52, 0x65, 0x53, 0x63, 0x61, 0x6e, 0x12, 0x1d, 0x2e, 0x74, 0x72, 0x69, 0x76, 0x79, 0x2e, 0x73, 0x63, 0x61,
0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x42, 0x33, 0x5a, 0x31, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x6e, 0x6e, 0x65, 0x72, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x63, 0x61, 0x6e, 0x52, 0x65, 0x71, 0x75,
0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x61, 0x71, 0x75, 0x61, 0x73, 0x65, 0x63, 0x75, 0x72, 0x69, 0x74, 0x65, 0x73, 0x74, 0x1a, 0x1e, 0x2e, 0x74, 0x72, 0x69, 0x76, 0x79, 0x2e, 0x73, 0x63, 0x61, 0x6e,
0x79, 0x2f, 0x74, 0x72, 0x69, 0x76, 0x79, 0x2f, 0x72, 0x70, 0x63, 0x2f, 0x73, 0x63, 0x61, 0x6e, 0x6e, 0x65, 0x72, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x63, 0x61, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f,
0x6e, 0x65, 0x72, 0x3b, 0x73, 0x63, 0x61, 0x6e, 0x6e, 0x65, 0x72, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x6e, 0x73, 0x65, 0x42, 0x33, 0x5a, 0x31, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f,
0x74, 0x6f, 0x33, 0x6d, 0x2f, 0x61, 0x71, 0x75, 0x61, 0x73, 0x65, 0x63, 0x75, 0x72, 0x69, 0x74, 0x79, 0x2f, 0x74,
0x72, 0x69, 0x76, 0x79, 0x2f, 0x72, 0x70, 0x63, 0x2f, 0x73, 0x63, 0x61, 0x6e, 0x6e, 0x65, 0x72,
0x3b, 0x73, 0x63, 0x61, 0x6e, 0x6e, 0x65, 0x72, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
} }
var ( var (

View File

@@ -27,6 +27,7 @@ message ScanOptions {
repeated string scanners = 2; repeated string scanners = 2;
map<string, Licenses> license_categories = 4; map<string, Licenses> license_categories = 4;
bool include_dev_deps = 5; bool include_dev_deps = 5;
repeated string pkg_relationships = 6;
reserved 3; // deleted 'list_all_packages' reserved 3; // deleted 'list_all_packages'
} }

View File

@@ -1094,46 +1094,47 @@ func callClientError(ctx context.Context, h *twirp.ClientHooks, err twirp.Error)
} }
var twirpFileDescriptor0 = []byte{ var twirpFileDescriptor0 = []byte{
// 650 bytes of a gzipped FileDescriptorProto // 671 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x7c, 0x54, 0xcd, 0x6e, 0xdb, 0x38, 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x7c, 0x54, 0xdd, 0x6e, 0xd3, 0x4c,
0x10, 0x86, 0x7f, 0x62, 0xcb, 0xe3, 0xc5, 0xc6, 0x21, 0x76, 0x03, 0xc5, 0xd9, 0x6c, 0x0d, 0x1f, 0x10, 0x55, 0x7e, 0x9a, 0x38, 0x93, 0x4f, 0x5f, 0xd3, 0x15, 0x54, 0x6e, 0x4a, 0x21, 0xca, 0x05,
0x0a, 0x9f, 0xec, 0xc6, 0x69, 0xd1, 0xbf, 0x5b, 0x93, 0xb4, 0x48, 0xd1, 0x22, 0x01, 0x1d, 0xf4, 0x8a, 0x84, 0x94, 0xd0, 0x14, 0xc4, 0xdf, 0x1d, 0x6d, 0x41, 0x45, 0xa0, 0x56, 0x9b, 0x8a, 0x0b,
0xd0, 0x8b, 0x4b, 0x53, 0x13, 0x95, 0xb0, 0x2c, 0x29, 0x1c, 0xca, 0x80, 0x5f, 0xa5, 0xef, 0xd2, 0x6e, 0xc2, 0x66, 0x3d, 0x75, 0x57, 0x71, 0x6c, 0x77, 0x67, 0x1d, 0x29, 0xaf, 0xc2, 0x7b, 0xf1,
0xc7, 0xe8, 0xfb, 0x14, 0x22, 0x25, 0x23, 0x76, 0x92, 0x9e, 0xc4, 0x99, 0xf9, 0xe6, 0x9b, 0x0f, 0x12, 0x3c, 0x05, 0xda, 0xb5, 0x13, 0x35, 0x69, 0xcb, 0x95, 0x77, 0x66, 0xce, 0xcc, 0x39, 0xde,
0x9c, 0x4f, 0x84, 0x03, 0x9d, 0xca, 0x11, 0x49, 0x11, 0xc7, 0xa8, 0x47, 0x84, 0x7a, 0xa9, 0x24, 0x39, 0x5a, 0xd8, 0xd3, 0xa9, 0x1c, 0x90, 0x14, 0x71, 0x8c, 0x7a, 0x40, 0xa8, 0xe7, 0x4a, 0x62,
0x0e, 0x53, 0x9d, 0x98, 0x84, 0x75, 0x8c, 0x56, 0xcb, 0xd5, 0xb0, 0x28, 0x0e, 0x97, 0xc7, 0x5d, 0x3f, 0xd5, 0x89, 0x49, 0x58, 0xcb, 0x68, 0x35, 0x5f, 0xf4, 0x8b, 0x62, 0x7f, 0x7e, 0xd8, 0xf6,
0x3f, 0x07, 0xcb, 0x64, 0xb1, 0x48, 0xe2, 0x4d, 0x6c, 0xff, 0x47, 0x05, 0xda, 0x13, 0x29, 0x62, 0x2d, 0x58, 0x26, 0xb3, 0x59, 0x12, 0xaf, 0x63, 0xbb, 0xbf, 0x4a, 0xd0, 0x1c, 0x49, 0x11, 0x73,
0x8e, 0xb7, 0x19, 0x92, 0x61, 0xfb, 0xd0, 0x30, 0x42, 0x87, 0x68, 0xfc, 0x4a, 0xaf, 0x32, 0x68, 0xbc, 0xc9, 0x90, 0x0c, 0xdb, 0x85, 0x9a, 0x11, 0x3a, 0x44, 0xe3, 0x97, 0x3a, 0xa5, 0x5e, 0x83,
0xf1, 0x22, 0x62, 0x4f, 0xa0, 0x2d, 0xb4, 0x51, 0x37, 0x42, 0x9a, 0xa9, 0x0a, 0xfc, 0xaa, 0x2d, 0x17, 0x11, 0x7b, 0x06, 0x4d, 0xa1, 0x8d, 0xba, 0x12, 0xd2, 0x8c, 0x55, 0xe0, 0x97, 0x5d, 0x11,
0x42, 0x99, 0xba, 0x08, 0xd8, 0x01, 0x78, 0xb3, 0x28, 0x99, 0x4d, 0x55, 0x40, 0x7e, 0xad, 0x57, 0x96, 0xa9, 0xb3, 0x80, 0xed, 0x81, 0x37, 0x89, 0x92, 0xc9, 0x58, 0x05, 0xe4, 0x57, 0x3a, 0x95,
0x1b, 0xb4, 0x78, 0x33, 0x8f, 0x2f, 0x02, 0x62, 0x2f, 0xa1, 0x99, 0xa4, 0x46, 0x25, 0x31, 0xf9, 0x5e, 0x83, 0xd7, 0x6d, 0x7c, 0x16, 0x10, 0x7b, 0x03, 0xf5, 0x24, 0x35, 0x2a, 0x89, 0xc9, 0xaf,
0xf5, 0x5e, 0x65, 0xd0, 0x1e, 0x1f, 0x0d, 0xb7, 0x15, 0x0e, 0x73, 0x0d, 0x97, 0x0e, 0xc4, 0x4b, 0x76, 0x4a, 0xbd, 0xe6, 0xf0, 0xa0, 0xbf, 0xa9, 0xb0, 0x6f, 0x35, 0x9c, 0xe7, 0x20, 0xbe, 0x44,
0x74, 0xbf, 0x07, 0xde, 0x27, 0x25, 0x31, 0x26, 0x24, 0xf6, 0x0f, 0xec, 0xc4, 0x62, 0x81, 0xe4, 0x77, 0x3b, 0xe0, 0x7d, 0x55, 0x12, 0x63, 0x42, 0x62, 0x8f, 0x60, 0x2b, 0x16, 0x33, 0x24, 0xbf,
0x57, 0x2c, 0xb9, 0x0b, 0xfa, 0x3f, 0xab, 0x4e, 0x7e, 0xd1, 0xca, 0x0e, 0xa1, 0x95, 0xce, 0xc3, 0xe4, 0x86, 0xe7, 0x41, 0xf7, 0x4f, 0x39, 0x97, 0x5f, 0xb4, 0xb2, 0x7d, 0x68, 0xa4, 0xd3, 0x70,
0xa9, 0x59, 0xa5, 0x6b, 0xa4, 0x97, 0xce, 0xc3, 0xeb, 0x3c, 0x66, 0x5d, 0xf0, 0x8a, 0x89, 0xe4, 0x6c, 0x16, 0xe9, 0x0a, 0xe9, 0xa5, 0xd3, 0xf0, 0xd2, 0xc6, 0xac, 0x0d, 0x5e, 0xc1, 0x48, 0x7e,
0x57, 0x5d, 0xad, 0x8c, 0x99, 0x04, 0x16, 0xb9, 0x51, 0x53, 0x29, 0x0c, 0x86, 0x89, 0x56, 0x98, 0x39, 0xaf, 0x2d, 0x63, 0x26, 0x81, 0x45, 0x39, 0xd5, 0x58, 0x0a, 0x83, 0x61, 0xa2, 0x15, 0x5a,
0xcb, 0xad, 0x0d, 0xda, 0xe3, 0xe7, 0x7f, 0x94, 0x3b, 0x2c, 0x24, 0x9e, 0xae, 0xdb, 0xce, 0x63, 0xb9, 0x95, 0x5e, 0x73, 0xf8, 0xea, 0x9f, 0x72, 0xfb, 0x85, 0xc4, 0xe3, 0x55, 0xdb, 0x69, 0x6c,
0xa3, 0x57, 0x7c, 0x2f, 0xda, 0xce, 0xb3, 0x01, 0x74, 0x54, 0x2c, 0xa3, 0x2c, 0xc0, 0x69, 0x80, 0xf4, 0x82, 0xef, 0x44, 0x9b, 0x79, 0xd6, 0x83, 0x96, 0x8a, 0x65, 0x94, 0x05, 0x38, 0x0e, 0x70,
0xcb, 0x69, 0x80, 0x29, 0xf9, 0x3b, 0xbd, 0xca, 0xc0, 0xe3, 0x7f, 0x17, 0xf9, 0x33, 0x5c, 0x9e, 0x3e, 0x0e, 0x30, 0x25, 0x7f, 0xab, 0x53, 0xea, 0x79, 0xfc, 0xff, 0x22, 0x7f, 0x82, 0xf3, 0x13,
0x61, 0x4a, 0xdd, 0x6f, 0xb0, 0xff, 0x30, 0x2d, 0xeb, 0x40, 0x6d, 0x8e, 0xab, 0x62, 0x3b, 0xf9, 0x4c, 0x89, 0xbd, 0x80, 0x1d, 0xfb, 0x1f, 0x1a, 0x23, 0xe1, 0x48, 0xae, 0x55, 0x4a, 0x7e, 0xcd,
0x91, 0x3d, 0x83, 0x9d, 0xa5, 0x88, 0x32, 0xb4, 0x4b, 0x69, 0x8f, 0xbb, 0xf7, 0xd5, 0x96, 0x97, 0x69, 0x6e, 0xa5, 0xd3, 0x90, 0xdf, 0xce, 0xb7, 0x7f, 0xc2, 0xee, 0xfd, 0x1a, 0x58, 0x0b, 0x2a,
0xc8, 0x1d, 0xf0, 0x4d, 0xf5, 0x55, 0xe5, 0x63, 0xdd, 0xab, 0x75, 0xea, 0xfd, 0x00, 0xfe, 0x72, 0x53, 0x5c, 0x14, 0xab, 0xb4, 0x47, 0xf6, 0x12, 0xb6, 0xe6, 0x22, 0xca, 0xd0, 0x6d, 0xb0, 0x39,
0xdb, 0xa7, 0x34, 0x89, 0x09, 0x59, 0x0f, 0xaa, 0x09, 0x59, 0xf2, 0xf6, 0xb8, 0x53, 0x10, 0x39, 0x6c, 0xdf, 0xfd, 0xb5, 0xe5, 0x8d, 0xf3, 0x1c, 0xf8, 0xbe, 0xfc, 0xb6, 0xf4, 0xa5, 0xea, 0x55,
0xdf, 0x0c, 0x2f, 0x27, 0xbc, 0x9a, 0x10, 0x1b, 0x43, 0x53, 0x23, 0x65, 0x91, 0x71, 0x6b, 0x6e, 0x5a, 0xd5, 0x6e, 0x00, 0xff, 0xe5, 0x56, 0xa1, 0x34, 0x89, 0x09, 0x59, 0x07, 0xca, 0x09, 0xb9,
0x8f, 0xfd, 0xfb, 0xf3, 0xb8, 0x05, 0xf0, 0x12, 0xd8, 0xff, 0x55, 0x83, 0x86, 0xcb, 0x3d, 0xea, 0xe1, 0xcd, 0x61, 0xab, 0x18, 0x94, 0x9b, 0xac, 0x7f, 0x3e, 0xe2, 0xe5, 0x84, 0xd8, 0x10, 0xea,
0xaf, 0x73, 0xd8, 0x5d, 0x66, 0x51, 0x8c, 0x5a, 0xcc, 0x54, 0xa4, 0x4c, 0x7e, 0xf9, 0x55, 0x4b, 0x1a, 0x29, 0x8b, 0x4c, 0xee, 0x89, 0xe6, 0xd0, 0xbf, 0xcb, 0xc7, 0x1d, 0x80, 0x2f, 0x81, 0xdd,
0x7f, 0xb8, 0xa9, 0xe2, 0xcb, 0x1d, 0xd0, 0x8a, 0x6f, 0xf7, 0xb0, 0x6b, 0xd8, 0x5b, 0x28, 0x92, 0xdf, 0x15, 0xa8, 0xe5, 0xb9, 0x07, 0xcd, 0x78, 0x0a, 0xdb, 0xf3, 0x2c, 0x8a, 0x51, 0x8b, 0x89,
0x49, 0x7c, 0xa3, 0xc2, 0x4c, 0x8b, 0xd2, 0x74, 0x39, 0xd1, 0xd3, 0x4d, 0xa2, 0x33, 0x34, 0x28, 0x8a, 0x94, 0xb1, 0x9b, 0x2a, 0xbb, 0xf1, 0xfb, 0xeb, 0x2a, 0xbe, 0xdf, 0x02, 0x2d, 0xf8, 0x66,
0x0d, 0x06, 0x9f, 0xb7, 0xe0, 0xfc, 0x3e, 0x41, 0xee, 0x3d, 0x19, 0x09, 0x22, 0xbf, 0x61, 0x35, 0x0f, 0xbb, 0x84, 0x9d, 0x99, 0x22, 0x99, 0xc4, 0x57, 0x2a, 0xcc, 0xb4, 0x58, 0x3a, 0xd4, 0x0e,
0xbb, 0x80, 0x31, 0xa8, 0xe7, 0x3e, 0xf3, 0x6b, 0x36, 0x69, 0xcf, 0xec, 0x18, 0xbc, 0x54, 0xc8, 0x7a, 0xbe, 0x3e, 0xe8, 0x04, 0x0d, 0x4a, 0x83, 0xc1, 0xb7, 0x0d, 0x38, 0xbf, 0x3b, 0xc0, 0x1a,
0xb9, 0x08, 0x31, 0xdf, 0x6c, 0x3e, 0xf6, 0xdf, 0xcd, 0xb1, 0x57, 0xae, 0xca, 0xd7, 0x30, 0xf6, 0x55, 0x46, 0x82, 0xec, 0xba, 0xac, 0xe6, 0x3c, 0x60, 0x0c, 0xaa, 0xd6, 0x94, 0x7e, 0xc5, 0x25,
0x01, 0x3a, 0x32, 0x23, 0x93, 0x2c, 0xa6, 0x1a, 0x29, 0xc9, 0xb4, 0x44, 0xf2, 0x9b, 0xb6, 0xf5, 0xdd, 0x99, 0x1d, 0x82, 0x97, 0x0a, 0x39, 0x15, 0x21, 0x5a, 0x1b, 0x58, 0xda, 0xc7, 0xeb, 0xb4,
0xbf, 0xcd, 0xd6, 0x53, 0x8b, 0xe2, 0x05, 0x88, 0xef, 0xca, 0x8d, 0x98, 0xd8, 0x0b, 0x68, 0x12, 0x17, 0x79, 0x95, 0xaf, 0x60, 0xec, 0x33, 0xb4, 0x64, 0x46, 0x26, 0x99, 0x8d, 0x35, 0x52, 0x92,
0x4a, 0x8d, 0x86, 0x7c, 0xef, 0xa1, 0xab, 0x9b, 0xd8, 0xe2, 0x7b, 0x15, 0x07, 0x2a, 0x0e, 0x79, 0x69, 0x89, 0xe4, 0xd7, 0x5d, 0xeb, 0x93, 0xf5, 0xd6, 0x63, 0x87, 0xe2, 0x05, 0x88, 0x6f, 0xcb,
0x89, 0x65, 0xaf, 0xc1, 0x2b, 0x9c, 0x4a, 0x7e, 0xcb, 0xf6, 0x1d, 0x3d, 0x7c, 0x53, 0x85, 0x8b, 0xb5, 0x98, 0xd8, 0x6b, 0xa8, 0x13, 0x4a, 0x8d, 0x86, 0x7c, 0xef, 0xbe, 0xab, 0x1b, 0xb9, 0xe2,
0xf8, 0x1a, 0x3e, 0xbe, 0x82, 0xe6, 0xc4, 0x6d, 0x9d, 0x9d, 0x43, 0x3d, 0x3f, 0xb2, 0x47, 0x7e, 0x27, 0x15, 0x07, 0x2a, 0x0e, 0xf9, 0x12, 0xcb, 0xde, 0x81, 0x57, 0xd8, 0x9a, 0xfc, 0x86, 0xeb,
0xed, 0xe2, 0x79, 0xe9, 0xfe, 0xff, 0x58, 0xd9, 0xf9, 0xef, 0xdd, 0xc9, 0xd7, 0xe3, 0x50, 0x99, 0x3b, 0xb8, 0xff, 0xa6, 0x0a, 0x17, 0xf1, 0x15, 0x7c, 0x78, 0x01, 0xf5, 0x51, 0xbe, 0x75, 0x76,
0xef, 0xd9, 0x2c, 0x1f, 0x3e, 0x12, 0xb7, 0x99, 0x20, 0x94, 0x99, 0x56, 0x66, 0x35, 0xb2, 0x8d, 0x0a, 0x55, 0x7b, 0x64, 0x0f, 0xbc, 0x03, 0xc5, 0x5b, 0xd4, 0x7e, 0xfa, 0x50, 0x39, 0xf7, 0xdf,
0xa3, 0x3b, 0xaf, 0xde, 0xdb, 0xe2, 0x3b, 0x6b, 0xd8, 0xa7, 0xec, 0xe4, 0x77, 0x00, 0x00, 0x00, 0xc7, 0xa3, 0x1f, 0x87, 0xa1, 0x32, 0xd7, 0xd9, 0xc4, 0x92, 0x0f, 0xc4, 0x4d, 0x26, 0x08, 0x65,
0xff, 0xff, 0x50, 0x58, 0x22, 0xc7, 0x13, 0x05, 0x00, 0x00, 0xa6, 0x95, 0x59, 0x0c, 0x5c, 0xe3, 0xe0, 0xd6, 0x13, 0xf9, 0xa1, 0xf8, 0x4e, 0x6a, 0xee, 0xdd,
0x3b, 0xfa, 0x1b, 0x00, 0x00, 0xff, 0xff, 0xf7, 0x66, 0x86, 0x78, 0x40, 0x05, 0x00, 0x00,
} }