From fa4660a0f78af5521776f77cbfed6a51669d91b0 Mon Sep 17 00:00:00 2001 From: Kevin Seidel Date: Tue, 19 Apr 2022 12:25:20 +0200 Subject: [PATCH 01/21] added draft for Grype plugin, vul report parsing is tested --- pkg/plugin/grype/doc.go | 2 + pkg/plugin/grype/model.go | 55 + pkg/plugin/grype/plugin.go | 611 +++++ pkg/plugin/grype/plugin_test.go | 3708 +++++++++++++++++++++++++++++++ 4 files changed, 4376 insertions(+) create mode 100644 pkg/plugin/grype/doc.go create mode 100644 pkg/plugin/grype/model.go create mode 100644 pkg/plugin/grype/plugin.go create mode 100644 pkg/plugin/grype/plugin_test.go diff --git a/pkg/plugin/grype/doc.go b/pkg/plugin/grype/doc.go new file mode 100644 index 000000000..980b6af17 --- /dev/null +++ b/pkg/plugin/grype/doc.go @@ -0,0 +1,2 @@ +// Package grype provides primitives for working with Trivy. +package grype diff --git a/pkg/plugin/grype/model.go b/pkg/plugin/grype/model.go new file mode 100644 index 000000000..d2f7d17fa --- /dev/null +++ b/pkg/plugin/grype/model.go @@ -0,0 +1,55 @@ +package grype + +type ScanReport struct { + Matches []Match `json:"matches"` + Source Source `json:"source"` + Descriptor Descriptor `json:"descriptor"` +} + +type Match struct { + Vulnerability Vulnerability `json:"vulnerability"` + Artifact Artifact `json:"artifact"` +} + +type Vulnerability struct { + Id string `json:"id"` + DataSource string `json:"dataSource"` + Severity string `json:"severity"` + URLs []string `json:"urls"` + Description string `json:"description"` + CVSs []CVS `json:"cvss"` + Fix Fix `json:"fix"` +} + +type Artifact struct { + Name string `json:"name"` + Version string `json:"version"` +} + +type Fix struct { + Versions []string `json:"versions"` + State string `json:"state"` +} + +type CVS struct { + Version string `json:"version"` + Metrics CVSMetrics `json:"metrics"` +} + +type CVSMetrics struct { + BaseScore *float64 `json:"baseScore"` +} + +type Source struct { + Target Target `json:"target"` +} + +type Target struct { + UserInput string `json:"userInput"` + ManifestDigest string `json:"manifestDigest"` +} + +type Descriptor struct { + Name string `json:"name"` + Version string `json:"version"` +} diff --git a/pkg/plugin/grype/plugin.go b/pkg/plugin/grype/plugin.go new file mode 100644 index 000000000..66f40c8f1 --- /dev/null +++ b/pkg/plugin/grype/plugin.go @@ -0,0 +1,611 @@ +package grype + +import ( + "encoding/json" + "fmt" + "io" + "regexp" + "strings" + + "github.com/aquasecurity/starboard/pkg/apis/aquasecurity/v1alpha1" + "github.com/aquasecurity/starboard/pkg/docker" + "github.com/aquasecurity/starboard/pkg/ext" + "github.com/aquasecurity/starboard/pkg/kube" + "github.com/aquasecurity/starboard/pkg/starboard" + "github.com/aquasecurity/starboard/pkg/vulnerabilityreport" + "github.com/google/go-containerregistry/pkg/name" + corev1 "k8s.io/api/core/v1" + "k8s.io/apimachinery/pkg/api/resource" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/utils/pointer" + "sigs.k8s.io/controller-runtime/pkg/client" +) + +const ( + // Plugin the name of this plugin. + Plugin = "Grype" +) + +const ( + keyGrypeImageRef = "grype.imageRef" + keyGrypeScheme = "grype.scheme" + keyGrypePath = "grype.path" + keyGrypeOnlyFixed = "grype.onlyFixed" + keyGrypeExcludePaths = "grype.exclude" + keyGrypeHTTPProxy = "grype.httpProxy" + keyGrypeHTTPSProxy = "grype.httpsProxy" + keyGrypeNoProxy = "grype.noProxy" + keyGrypeUpdateURL = "grype.updateURL" + keyGrypeAddMissingCPEs = "grype.addMissingCPEs" + keyGrypeRegAuthority = "grype.regAuthority" + + keyResourcesRequestsCPU = "grype.resources.requests.cpu" + keyResourcesRequestsMemory = "grype.resources.requests.memory" + keyResourcesLimitsCPU = "grype.resources.limits.cpu" + keyResourcesLimitsMemory = "grype.resources.limits.memory" +) + +const defaultUpdateURL = "https://toolbox-data.anchore.io/grype/databases/listing.json" + +// Config defines configuration params for this plugin. +type Config struct { + starboard.PluginConfig +} + +// GetResourceRequirements creates ResourceRequirements from the Config. +func (c Config) GetResourceRequirements() (corev1.ResourceRequirements, error) { + requirements := corev1.ResourceRequirements{ + Requests: corev1.ResourceList{}, + Limits: corev1.ResourceList{}, + } + + err := c.setResourceLimit(keyResourcesRequestsCPU, &requirements.Requests, corev1.ResourceCPU) + if err != nil { + return requirements, err + } + + err = c.setResourceLimit(keyResourcesRequestsMemory, &requirements.Requests, corev1.ResourceMemory) + if err != nil { + return requirements, err + } + + err = c.setResourceLimit(keyResourcesLimitsCPU, &requirements.Limits, corev1.ResourceCPU) + if err != nil { + return requirements, err + } + + err = c.setResourceLimit(keyResourcesLimitsMemory, &requirements.Limits, corev1.ResourceMemory) + if err != nil { + return requirements, err + } + + return requirements, nil +} + +func (c Config) setResourceLimit(configKey string, k8sResourceList *corev1.ResourceList, k8sResourceName corev1.ResourceName) error { + if value, found := c.Data[configKey]; found { + quantity, err := resource.ParseQuantity(value) + if err != nil { + return fmt.Errorf("parsing resource definition %s: %s %w", configKey, value, err) + } + + (*k8sResourceList)[k8sResourceName] = quantity + } + return nil +} + +type plugin struct { + clock ext.Clock + idGenerator ext.IDGenerator + objectResolver *kube.ObjectResolver +} + +// NewPlugin constructs a new vulnerabilityreport.Plugin, which is using an +// upstream Grype container image to scan Kubernetes workloads. +// +// The plugin supports Image and Filesystem commands. The Filesystem command may +// be used to scan workload images cached on cluster nodes by scheduling +// scan jobs on a particular node. +// +// The Image command supports both Standalone and ClientServer modes depending +// on the settings returned by Config.GetMode. The ClientServer mode is usually +// more performant, however it requires a Grype server accessible at the +// configurable Config.GetServerURL. +func NewPlugin(clock ext.Clock, idGenerator ext.IDGenerator, client client.Client) vulnerabilityreport.Plugin { + return &plugin{ + clock: clock, + idGenerator: idGenerator, + objectResolver: &kube.ObjectResolver{Client: client}, + } +} + +// Init ensures the default Config required by this plugin. +func (p *plugin) Init(ctx starboard.PluginContext) error { + return ctx.EnsureConfig(starboard.PluginConfig{ + Data: map[string]string{ + keyGrypeImageRef: "anchore/grype:0.34.7", + keyGrypeUpdateURL: defaultUpdateURL, + + keyResourcesRequestsCPU: "100m", + keyResourcesRequestsMemory: "100M", + keyResourcesLimitsCPU: "500m", + keyResourcesLimitsMemory: "500M", + }, + }) +} + +func (p *plugin) GetScanJobSpec(ctx starboard.PluginContext, workload client.Object, credentials map[string]docker.Auth) (corev1.PodSpec, []*corev1.Secret, error) { + config, err := p.newConfigFrom(ctx) + if err != nil { + return corev1.PodSpec{}, nil, err + } + + return p.getPodSpec(ctx, config, workload, credentials) +} + +func (p *plugin) newSecretWithAggregateImagePullCredentials(obj client.Object, spec corev1.PodSpec, credentials map[string]docker.Auth) *corev1.Secret { + containerImages := kube.GetContainerImagesFromPodSpec(spec) + secretData := kube.AggregateImagePullSecretsData(containerImages, credentials) + + return &corev1.Secret{ + ObjectMeta: metav1.ObjectMeta{ + Name: vulnerabilityreport.RegistryCredentialsSecretName(obj), + }, + Data: secretData, + } +} + +const ( + tmpVolumeName = "tmp" + ignoreFileVolumeName = "ignorefile" + FsSharedVolumeName = "starboard" + grypeDBLocation = "/tmp/grypedb" +) + +// In the Standalone mode there is the init container responsible for +// downloading the latest Grype DB file from GitHub and storing it to the +// emptyDir volume shared with main containers. In other words, the init +// container runs the following Grype command: +// +// grype --cache-dir /tmp/grype/.cache image --download-db-only +// +// The number of main containers correspond to the number of containers +// defined for the scanned workload. Each container runs the Grype image scan +// command and skips the database download: +// +// grype --cache-dir /tmp/grype/.cache image --skip-update \ +// --format json +func (p *plugin) getPodSpec(ctx starboard.PluginContext, config Config, workload client.Object, credentials map[string]docker.Auth) (corev1.PodSpec, []*corev1.Secret, error) { + var secret *corev1.Secret + var secrets []*corev1.Secret + + spec, err := kube.GetPodSpec(workload) + if err != nil { + return corev1.PodSpec{}, nil, err + } + + if len(credentials) > 0 { + secret = p.newSecretWithAggregateImagePullCredentials(workload, spec, credentials) + secrets = append(secrets, secret) + } + + grypeImageRef, err := config.GetRequiredData(keyGrypeImageRef) + if err != nil { + return corev1.PodSpec{}, nil, err + } + + grypeConfigName := starboard.GetPluginConfigMapName(Plugin) + + requirements, err := config.GetResourceRequirements() + if err != nil { + return corev1.PodSpec{}, nil, err + } + + volumeMounts := []corev1.VolumeMount{ + { + Name: tmpVolumeName, + ReadOnly: false, + MountPath: grypeDBLocation, + }, + } + volumes := []corev1.Volume{ + { + Name: tmpVolumeName, + VolumeSource: corev1.VolumeSource{ + EmptyDir: &corev1.EmptyDirVolumeSource{ + Medium: corev1.StorageMediumDefault, + }, + }, + }, + } + + commonEnv := []corev1.EnvVar{ + { + Name: "HTTP_PROXY", + ValueFrom: &corev1.EnvVarSource{ + ConfigMapKeyRef: &corev1.ConfigMapKeySelector{ + LocalObjectReference: corev1.LocalObjectReference{ + Name: grypeConfigName, + }, + Key: keyGrypeHTTPProxy, + Optional: pointer.BoolPtr(true), + }, + }, + }, + { + Name: "HTTPS_PROXY", + ValueFrom: &corev1.EnvVarSource{ + ConfigMapKeyRef: &corev1.ConfigMapKeySelector{ + LocalObjectReference: corev1.LocalObjectReference{ + Name: grypeConfigName, + }, + Key: keyGrypeHTTPSProxy, + Optional: pointer.BoolPtr(true), + }, + }, + }, + { + Name: "NO_PROXY", + ValueFrom: &corev1.EnvVarSource{ + ConfigMapKeyRef: &corev1.ConfigMapKeySelector{ + LocalObjectReference: corev1.LocalObjectReference{ + Name: grypeConfigName, + }, + Key: keyGrypeNoProxy, + Optional: pointer.BoolPtr(true), + }, + }, + }, + { + Name: "GRYPE_DB_UPDATE_URL", + ValueFrom: &corev1.EnvVarSource{ + ConfigMapKeyRef: &corev1.ConfigMapKeySelector{ + LocalObjectReference: corev1.LocalObjectReference{ + Name: grypeConfigName, + }, + Key: keyGrypeUpdateURL, + Optional: pointer.BoolPtr(false), + }, + }, + }, + { + Name: "GRYPE_DB_CACHE_DIR", + Value: grypeDBLocation, + }, + } + + initContainer := corev1.Container{ + Name: p.idGenerator.GenerateID(), + Image: grypeImageRef, + ImagePullPolicy: corev1.PullIfNotPresent, + TerminationMessagePolicy: corev1.TerminationMessageFallbackToLogsOnError, + Env: commonEnv, + Command: []string{ + "grype", + }, + Args: []string{ + "db", + "update", + }, + Resources: requirements, + VolumeMounts: volumeMounts, + } + + var containers []corev1.Container + + for _, c := range spec.Containers { + + env := append(commonEnv, + corev1.EnvVar{ + Name: "GRYPE_DB_UPDATE_URL", + ValueFrom: &corev1.EnvVarSource{ + ConfigMapKeyRef: &corev1.ConfigMapKeySelector{ + LocalObjectReference: corev1.LocalObjectReference{ + Name: grypeConfigName, + }, + Key: keyGrypeUpdateURL, + Optional: pointer.BoolPtr(false), + }, + }, + }, + corev1.EnvVar{ + Name: "GRYPE_REGISTRY_AUTH_AUTHORITY", + ValueFrom: &corev1.EnvVarSource{ + ConfigMapKeyRef: &corev1.ConfigMapKeySelector{ + LocalObjectReference: corev1.LocalObjectReference{ + Name: grypeConfigName, + }, + Key: keyGrypeRegAuthority, + Optional: pointer.BoolPtr(true), + }, + }, + }, + corev1.EnvVar{ + Name: "GRYPE_EXCLUDE", + ValueFrom: &corev1.EnvVarSource{ + ConfigMapKeyRef: &corev1.ConfigMapKeySelector{ + LocalObjectReference: corev1.LocalObjectReference{ + Name: grypeConfigName, + }, + Key: keyGrypeExcludePaths, + Optional: pointer.BoolPtr(true), + }, + }, + }, + corev1.EnvVar{ + Name: "GRYPE_DB_AUTO_UPDATE", + Value: "false", + }, + ) + + if _, ok := credentials[c.Name]; ok && secret != nil { + registryUsernameKey := fmt.Sprintf("%s.username", c.Name) + registryPasswordKey := fmt.Sprintf("%s.password", c.Name) + + env = append(env, corev1.EnvVar{ + Name: "GRYPE_REGISTRY_AUTH_USERNAME", + ValueFrom: &corev1.EnvVarSource{ + SecretKeyRef: &corev1.SecretKeySelector{ + LocalObjectReference: corev1.LocalObjectReference{ + Name: secret.Name, + }, + Key: registryUsernameKey, + }, + }, + }, corev1.EnvVar{ + Name: "GRYPE_REGISTRY_AUTH_PASSWORD", + ValueFrom: &corev1.EnvVarSource{ + SecretKeyRef: &corev1.SecretKeySelector{ + LocalObjectReference: corev1.LocalObjectReference{ + Name: secret.Name, + }, + Key: registryPasswordKey, + }, + }, + }) + } + + // env, err = p.appendGrypeInsecureEnv(config, c.Image, env) + // if err != nil { + // return corev1.PodSpec{}, nil, err + // } + + // env, err = p.appendGrypeNonSSLEnv(config, c.Image, env) + // if err != nil { + // return corev1.PodSpec{}, nil, err + // } + + args := []string{ + c.Image, + "--skip-update", + "--quiet", + "--output", + "json", + } + + if args, err = p.appendGrypeOptionalArg(config, args, "--add-cpes-if-none", keyGrypeAddMissingCPEs); err != nil { + return corev1.PodSpec{}, nil, err + } + if args, err = p.appendGrypeOptionalArg(config, args, "--only-fixed", keyGrypeOnlyFixed); err != nil { + return corev1.PodSpec{}, nil, err + } + + containers = append(containers, corev1.Container{ + Name: c.Name, + Image: grypeImageRef, + ImagePullPolicy: corev1.PullIfNotPresent, + TerminationMessagePolicy: corev1.TerminationMessageFallbackToLogsOnError, + Env: env, + Command: []string{ + "grype", + }, + Args: args, + Resources: requirements, + VolumeMounts: volumeMounts, + SecurityContext: &corev1.SecurityContext{ + Privileged: pointer.BoolPtr(false), + AllowPrivilegeEscalation: pointer.BoolPtr(false), + Capabilities: &corev1.Capabilities{ + Drop: []corev1.Capability{"all"}, + }, + ReadOnlyRootFilesystem: pointer.BoolPtr(true), + }, + }) + } + + return corev1.PodSpec{ + Affinity: starboard.LinuxNodeAffinity(), + RestartPolicy: corev1.RestartPolicyNever, + ServiceAccountName: ctx.GetServiceAccountName(), + AutomountServiceAccountToken: pointer.BoolPtr(false), + Volumes: volumes, + InitContainers: []corev1.Container{initContainer}, + Containers: containers, + SecurityContext: &corev1.PodSecurityContext{}, + }, secrets, nil +} + +func (p *plugin) appendGrypeOptionalArg(config Config, args []string, arg string, key string) ([]string, error) { + if val, ok := config.Data[key]; ok && val == "true" { + return append(args, arg), nil + } else if !ok { + return args, fmt.Errorf("Invalid config key provided: %s", key) + } else { + return args, nil + } +} + +// func (p *plugin) appendGrypeInsecureEnv(config Config, image string, env []corev1.EnvVar) ([]corev1.EnvVar, error) { +// ref, err := name.ParseReference(image) +// if err != nil { +// return nil, err +// } + +// insecureRegistries := config.GetInsecureRegistries() +// if insecureRegistries[ref.Context().RegistryStr()] { +// env = append(env, corev1.EnvVar{ +// Name: "GRYPE_REGISTRY_INSECURE_SKIP_TLS_VERIFY", +// Value: "true", +// }) +// } + +// return env, nil +// } + +// func (p *plugin) appendGrypeNonSSLEnv(config Config, image string, env []corev1.EnvVar) ([]corev1.EnvVar, error) { +// ref, err := name.ParseReference(image) +// if err != nil { +// return nil, err +// } + +// nonSSLRegistries := config.GetNonSSLRegistries() +// if nonSSLRegistries[ref.Context().RegistryStr()] { +// env = append(env, corev1.EnvVar{ +// Name: "GRYPE_REGISTRY_INSECURE_USE_HTTP", +// Value: "true", +// }) +// } + +// return env, nil +// } + +func (p *plugin) ParseVulnerabilityReportData(ctx starboard.PluginContext, imageRef string, logsReader io.ReadCloser) (v1alpha1.VulnerabilityReportData, error) { + config, err := p.newConfigFrom(ctx) + if err != nil { + return v1alpha1.VulnerabilityReportData{}, err + } + var report ScanReport + err = json.NewDecoder(logsReader).Decode(&report) + if err != nil { + return v1alpha1.VulnerabilityReportData{}, err + } + vulnerabilities := make([]v1alpha1.Vulnerability, 0) + + for _, match := range report.Matches { + vul := match.Vulnerability + artifact := match.Artifact + + fixVersion := "" + for _, version := range match.Vulnerability.Fix.Versions { + fixVersion += version + fixVersion += ", " + } + fixVersion = strings.TrimSuffix(fixVersion, ", ") + + var score *float64 = pointer.Float64Ptr(0) + for _, cvs := range vul.CVSs { + if matched, err := regexp.MatchString("3\\..*", cvs.Version); matched && err == nil { + score = cvs.Metrics.BaseScore + } + } + + var severity v1alpha1.Severity + if severity, err = v1alpha1.StringToSeverity(vul.Severity); err != nil { + severity = v1alpha1.Severity("UNKNOWN") + } + + vulnerabilities = append(vulnerabilities, v1alpha1.Vulnerability{ + VulnerabilityID: vul.Id, + Resource: artifact.Name, + InstalledVersion: artifact.Version, + FixedVersion: fixVersion, + Severity: severity, + Title: artifact.Name, + PrimaryLink: vul.DataSource, + Description: vul.Description, + Links: vul.URLs, + Score: score, + }) + } + + registry, artifact, err := p.parseImageRef(imageRef) + if err != nil { + return v1alpha1.VulnerabilityReportData{}, err + } + + grypeImageRef, err := config.GetRequiredData(keyGrypeImageRef) + if err != nil { + return v1alpha1.VulnerabilityReportData{}, err + } + + version, err := starboard.GetVersionFromImageRef(grypeImageRef) + if err != nil { + return v1alpha1.VulnerabilityReportData{}, err + } + + return v1alpha1.VulnerabilityReportData{ + UpdateTimestamp: metav1.NewTime(p.clock.Now()), + Scanner: v1alpha1.Scanner{ + Name: "Grype", + Vendor: "Anchore Inc.", + Version: version, + }, + Registry: registry, + Artifact: artifact, + Summary: p.toSummary(vulnerabilities), + Vulnerabilities: vulnerabilities, + }, nil +} + +func (p *plugin) newConfigFrom(ctx starboard.PluginContext) (Config, error) { + pluginConfig, err := ctx.GetConfig() + if err != nil { + return Config{}, err + } + return Config{PluginConfig: pluginConfig}, nil +} + +func (p *plugin) toSummary(vulnerabilities []v1alpha1.Vulnerability) v1alpha1.VulnerabilitySummary { + var vs v1alpha1.VulnerabilitySummary + for _, v := range vulnerabilities { + switch v.Severity { + case v1alpha1.SeverityCritical: + vs.CriticalCount++ + case v1alpha1.SeverityHigh: + vs.HighCount++ + case v1alpha1.SeverityMedium: + vs.MediumCount++ + case v1alpha1.SeverityLow: + vs.LowCount++ + default: + vs.UnknownCount++ + } + } + return vs +} + +func (p *plugin) parseImageRef(imageRef string) (v1alpha1.Registry, v1alpha1.Artifact, error) { + ref, err := name.ParseReference(imageRef) + if err != nil { + return v1alpha1.Registry{}, v1alpha1.Artifact{}, err + } + registry := v1alpha1.Registry{ + Server: ref.Context().RegistryStr(), + } + artifact := v1alpha1.Artifact{ + Repository: ref.Context().RepositoryStr(), + } + switch t := ref.(type) { + case name.Tag: + artifact.Tag = t.TagStr() + case name.Digest: + artifact.Digest = t.DigestStr() + } + return registry, artifact, nil +} + +func constructEnvVarSourceFromConfigMap(envName, configName, configKey string) (res corev1.EnvVar) { + res = corev1.EnvVar{ + Name: envName, + ValueFrom: &corev1.EnvVarSource{ + ConfigMapKeyRef: &corev1.ConfigMapKeySelector{ + LocalObjectReference: corev1.LocalObjectReference{ + Name: configName, + }, + Key: configKey, + Optional: pointer.BoolPtr(true), + }, + }, + } + return +} diff --git a/pkg/plugin/grype/plugin_test.go b/pkg/plugin/grype/plugin_test.go new file mode 100644 index 000000000..b16477198 --- /dev/null +++ b/pkg/plugin/grype/plugin_test.go @@ -0,0 +1,3708 @@ +package grype_test + +import ( + "context" + "errors" + "io" + "strings" + "testing" + "time" + + "github.com/aquasecurity/starboard/pkg/apis/aquasecurity/v1alpha1" + "github.com/aquasecurity/starboard/pkg/ext" + "github.com/aquasecurity/starboard/pkg/plugin/grype" + "github.com/aquasecurity/starboard/pkg/plugin/trivy" + "github.com/aquasecurity/starboard/pkg/starboard" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + appsv1 "k8s.io/api/apps/v1" + corev1 "k8s.io/api/core/v1" + "k8s.io/apimachinery/pkg/api/resource" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/types" + "k8s.io/utils/pointer" + "sigs.k8s.io/controller-runtime/pkg/client" + "sigs.k8s.io/controller-runtime/pkg/client/fake" +) + +var ( + fixedTime = time.Now() + fixedClock = ext.NewFixedClock(fixedTime) +) + +const defaultUpdateURL = "https://toolbox-data.anchore.io/grype/databases/listing.json" + +func TestConfig_GetImageRef(t *testing.T) { + testCases := []struct { + name string + configData trivy.Config + expectedError string + expectedImageRef string + }{ + { + name: "Should return error", + configData: trivy.Config{PluginConfig: starboard.PluginConfig{}}, + expectedError: "property trivy.imageRef not set", + }, + { + name: "Should return image reference from config data", + configData: trivy.Config{PluginConfig: starboard.PluginConfig{ + Data: map[string]string{ + "trivy.imageRef": "gcr.io/aquasecurity/trivy:0.8.0", + }, + }}, + expectedImageRef: "gcr.io/aquasecurity/trivy:0.8.0", + }, + } + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + imageRef, err := tc.configData.GetImageRef() + if tc.expectedError != "" { + require.EqualError(t, err, tc.expectedError) + } else { + require.NoError(t, err) + assert.Equal(t, tc.expectedImageRef, imageRef) + } + }) + } +} + +func TestConfig_GetResourceRequirements(t *testing.T) { + testCases := []struct { + name string + config trivy.Config + expectedError string + expectedRequirements corev1.ResourceRequirements + }{ + { + name: "Should return empty requirements by default", + config: trivy.Config{ + PluginConfig: starboard.PluginConfig{}, + }, + expectedError: "", + expectedRequirements: corev1.ResourceRequirements{ + Requests: corev1.ResourceList{}, + Limits: corev1.ResourceList{}, + }, + }, + { + name: "Should return configured resource requirement", + config: trivy.Config{ + PluginConfig: starboard.PluginConfig{ + Data: map[string]string{ + "trivy.dbRepository": defaultUpdateURL, + "trivy.resources.requests.cpu": "800m", + "trivy.resources.requests.memory": "200M", + "trivy.resources.limits.cpu": "600m", + "trivy.resources.limits.memory": "700M", + }, + }, + }, + expectedError: "", + expectedRequirements: corev1.ResourceRequirements{ + Requests: corev1.ResourceList{ + corev1.ResourceCPU: resource.MustParse("800m"), + corev1.ResourceMemory: resource.MustParse("200M"), + }, + Limits: corev1.ResourceList{ + corev1.ResourceCPU: resource.MustParse("600m"), + corev1.ResourceMemory: resource.MustParse("700M"), + }, + }, + }, + { + name: "Should return error if resource is not parseable", + config: trivy.Config{ + PluginConfig: starboard.PluginConfig{ + Data: map[string]string{ + "trivy.resources.requests.cpu": "roughly 100", + }, + }, + }, + expectedError: "parsing resource definition trivy.resources.requests.cpu: roughly 100 quantities must match the regular expression '^([+-]?[0-9.]+)([eEinumkKMGTP]*[-+]?[0-9]*)$'", + }, + } + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + resourceRequirement, err := tc.config.GetResourceRequirements() + if tc.expectedError != "" { + require.EqualError(t, err, tc.expectedError) + } else { + require.NoError(t, err) + assert.Equal(t, tc.expectedRequirements, resourceRequirement, tc.name) + } + }) + } +} + +func TestConfig_IgnoreUnfixed(t *testing.T) { + testCases := []struct { + name string + configData trivy.Config + expectedOutput bool + }{ + { + name: "Should return false", + configData: trivy.Config{PluginConfig: starboard.PluginConfig{ + Data: map[string]string{ + "foo": "bar", + }, + }}, + expectedOutput: false, + }, + { + name: "Should return true", + configData: trivy.Config{PluginConfig: starboard.PluginConfig{ + Data: map[string]string{ + "foo": "bar", + "trivy.ignoreUnfixed": "true", + }, + }}, + expectedOutput: true, + }, + { + name: "Should return false when set it as false", + configData: trivy.Config{PluginConfig: starboard.PluginConfig{ + Data: map[string]string{ + "foo": "bar", + "trivy.ignoreUnfixed": "false", + }, + }}, + expectedOutput: true, + }, + } + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + exists := tc.configData.IgnoreUnfixed() + assert.Equal(t, tc.expectedOutput, exists) + }) + } +} + +func TestPlugin_Init(t *testing.T) { + + t.Run("Should create the default config", func(t *testing.T) { + client := fake.NewClientBuilder().WithObjects().Build() + + instance := trivy.NewPlugin(fixedClock, ext.NewSimpleIDGenerator(), client) + + pluginContext := starboard.NewPluginContext(). + WithName(trivy.Plugin). + WithNamespace("starboard-ns"). + WithServiceAccountName("starboard-sa"). + WithClient(client). + Get() + err := instance.Init(pluginContext) + require.NoError(t, err) + + var cm corev1.ConfigMap + err = client.Get(context.Background(), types.NamespacedName{ + Namespace: "starboard-ns", + Name: "starboard-trivy-config", + }, &cm) + require.NoError(t, err) + assert.Equal(t, corev1.ConfigMap{ + TypeMeta: metav1.TypeMeta{ + APIVersion: "v1", + Kind: "ConfigMap", + }, + ObjectMeta: metav1.ObjectMeta{ + Name: "starboard-trivy-config", + Namespace: "starboard-ns", + Labels: map[string]string{ + "app.kubernetes.io/managed-by": "starboard", + }, + ResourceVersion: "1", + }, + Data: map[string]string{ + "trivy.imageRef": "docker.io/aquasec/trivy:0.25.2", + "trivy.severity": "UNKNOWN,LOW,MEDIUM,HIGH,CRITICAL", + "trivy.mode": "Standalone", + "trivy.timeout": "5m0s", + "trivy.dbRepository": defaultUpdateURL, + + "trivy.resources.requests.cpu": "100m", + "trivy.resources.requests.memory": "100M", + "trivy.resources.limits.cpu": "500m", + "trivy.resources.limits.memory": "500M", + }, + }, cm) + }) + + t.Run("Should not overwrite existing config", func(t *testing.T) { + client := fake.NewClientBuilder().WithObjects( + &corev1.ConfigMap{ + TypeMeta: metav1.TypeMeta{ + APIVersion: "v1", + Kind: "ConfigMap", + }, + ObjectMeta: metav1.ObjectMeta{ + Name: "starboard-trivy-config", + Namespace: "starboard-ns", + ResourceVersion: "1", + }, + Data: map[string]string{ + "trivy.imageRef": "docker.io/aquasec/trivy:0.25.2", + "trivy.severity": "UNKNOWN,LOW,MEDIUM,HIGH,CRITICAL", + "trivy.mode": "Standalone", + }, + }).Build() + + instance := trivy.NewPlugin(fixedClock, ext.NewSimpleIDGenerator(), client) + + pluginContext := starboard.NewPluginContext(). + WithName(trivy.Plugin). + WithNamespace("starboard-ns"). + WithServiceAccountName("starboard-sa"). + WithClient(client). + Get() + err := instance.Init(pluginContext) + require.NoError(t, err) + + var cm corev1.ConfigMap + err = client.Get(context.Background(), types.NamespacedName{ + Namespace: "starboard-ns", + Name: "starboard-trivy-config", + }, &cm) + require.NoError(t, err) + assert.Equal(t, corev1.ConfigMap{ + TypeMeta: metav1.TypeMeta{ + APIVersion: "v1", + Kind: "ConfigMap", + }, + ObjectMeta: metav1.ObjectMeta{ + Name: "starboard-trivy-config", + Namespace: "starboard-ns", + ResourceVersion: "1", + }, + Data: map[string]string{ + "trivy.imageRef": "docker.io/aquasec/trivy:0.25.2", + "trivy.severity": "UNKNOWN,LOW,MEDIUM,HIGH,CRITICAL", + "trivy.mode": "Standalone", + }, + }, cm) + }) +} + +const ( + grypeDBLocation = "/tmp/grypedb" + keyGrypeImageRef = "grype.imageRef" + keyGrypeScheme = "grype.scheme" + keyGrypePath = "grype.path" + keyGrypeOnlyFixed = "grype.onlyFixed" + keyGrypeExcludePaths = "grype.exclude" + keyGrypeHTTPProxy = "grype.httpProxy" + keyGrypeHTTPSProxy = "grype.httpsProxy" + keyGrypeNoProxy = "grype.noProxy" + keyGrypeUpdateURL = "grype.updateURL" + keyGrypeAddMissingCPEs = "grype.addMissingCPEs" + keyGrypeRegAuthority = "grype.regAuthority" + + keyResourcesRequestsCPU = "grype.resources.requests.cpu" + keyResourcesRequestsMemory = "grype.resources.requests.memory" + keyResourcesLimitsCPU = "grype.resources.limits.cpu" + keyResourcesLimitsMemory = "grype.resources.limits.memory" +) + +func TestPlugin_GetScanJobSpec(t *testing.T) { + + commonEnv := []corev1.EnvVar{ + { + Name: "HTTP_PROXY", + ValueFrom: &corev1.EnvVarSource{ + ConfigMapKeyRef: &corev1.ConfigMapKeySelector{ + LocalObjectReference: corev1.LocalObjectReference{ + Name: grype.Plugin, + }, + Key: keyGrypeHTTPProxy, + Optional: pointer.BoolPtr(true), + }, + }, + }, + { + Name: "HTTPS_PROXY", + ValueFrom: &corev1.EnvVarSource{ + ConfigMapKeyRef: &corev1.ConfigMapKeySelector{ + LocalObjectReference: corev1.LocalObjectReference{ + Name: grype.Plugin, + }, + Key: keyGrypeHTTPSProxy, + Optional: pointer.BoolPtr(true), + }, + }, + }, + { + Name: "NO_PROXY", + ValueFrom: &corev1.EnvVarSource{ + ConfigMapKeyRef: &corev1.ConfigMapKeySelector{ + LocalObjectReference: corev1.LocalObjectReference{ + Name: grype.Plugin, + }, + Key: keyGrypeNoProxy, + Optional: pointer.BoolPtr(true), + }, + }, + }, + { + Name: "GRYPE_DB_UPDATE_URL", + ValueFrom: &corev1.EnvVarSource{ + ConfigMapKeyRef: &corev1.ConfigMapKeySelector{ + LocalObjectReference: corev1.LocalObjectReference{ + Name: grype.Plugin, + }, + Key: keyGrypeUpdateURL, + Optional: pointer.BoolPtr(false), + }, + }, + }, + { + Name: "GRYPE_DB_CACHE_DIR", + Value: grypeDBLocation, + }, + } + + tmpVolume := corev1.Volume{ + Name: "tmp", + VolumeSource: corev1.VolumeSource{ + EmptyDir: &corev1.EmptyDirVolumeSource{ + Medium: corev1.StorageMediumDefault, + }, + }, + } + + tmpVolumeMount := corev1.VolumeMount{ + Name: "tmp", + MountPath: grypeDBLocation, + ReadOnly: false, + } + + testCases := []struct { + name string + + config map[string]string + workloadSpec client.Object + + expectedSecrets []corev1.Secret + expectedJobSpec corev1.PodSpec + }{ + { + name: "grype", + config: map[string]string{ + "grype.imageRef": "anchore/grype:v0.34.7", + "grype.updateURL": defaultUpdateURL, + "grype.resources.requests.cpu": "100m", + "grype.resources.requests.memory": "100M", + "grype.resources.limits.cpu": "500m", + "grype.resources.limits.memory": "500M", + }, + workloadSpec: &appsv1.ReplicaSet{ + TypeMeta: metav1.TypeMeta{ + Kind: "ReplicaSet", + APIVersion: "apps/v1", + }, + ObjectMeta: metav1.ObjectMeta{ + Name: "nginx-6799fc88d8", + Namespace: "prod-ns", + }, + Spec: appsv1.ReplicaSetSpec{ + Template: corev1.PodTemplateSpec{ + Spec: corev1.PodSpec{ + Containers: []corev1.Container{ + { + Name: "nginx", + Image: "nginx:1.16", + }, + }, + }, + }, + }, + }, + expectedJobSpec: corev1.PodSpec{ + Affinity: starboard.LinuxNodeAffinity(), + RestartPolicy: corev1.RestartPolicyNever, + ServiceAccountName: "starboard-sa", + AutomountServiceAccountToken: pointer.BoolPtr(false), + Volumes: []corev1.Volume{ + tmpVolume, + }, + InitContainers: []corev1.Container{ + { + Name: "00000000-0000-0000-0000-000000000001", + Image: "anchore/grype:v0.34.7", + ImagePullPolicy: corev1.PullIfNotPresent, + TerminationMessagePolicy: corev1.TerminationMessageFallbackToLogsOnError, + Env: commonEnv, + Command: []string{ + "grype", + }, + Args: []string{ + "db", + "update", + }, + Resources: corev1.ResourceRequirements{ + Requests: corev1.ResourceList{ + corev1.ResourceCPU: resource.MustParse("100m"), + corev1.ResourceMemory: resource.MustParse("100M"), + }, + Limits: corev1.ResourceList{ + corev1.ResourceCPU: resource.MustParse("500m"), + corev1.ResourceMemory: resource.MustParse("500M"), + }, + }, + VolumeMounts: []corev1.VolumeMount{ + tmpVolumeMount, + }, + }, + }, + Containers: []corev1.Container{ + { + Name: "nginx", + Image: "docker.io/aquasec/trivy:0.14.0", + ImagePullPolicy: corev1.PullIfNotPresent, + TerminationMessagePolicy: corev1.TerminationMessageFallbackToLogsOnError, + Env: []corev1.EnvVar{ + { + Name: "TRIVY_SEVERITY", + ValueFrom: &corev1.EnvVarSource{ + ConfigMapKeyRef: &corev1.ConfigMapKeySelector{ + LocalObjectReference: corev1.LocalObjectReference{ + Name: "starboard-trivy-config", + }, + Key: "trivy.severity", + Optional: pointer.BoolPtr(true), + }, + }, + }, + { + Name: "TRIVY_IGNORE_UNFIXED", + ValueFrom: &corev1.EnvVarSource{ + ConfigMapKeyRef: &corev1.ConfigMapKeySelector{ + LocalObjectReference: corev1.LocalObjectReference{ + Name: "starboard-trivy-config", + }, + Key: "trivy.ignoreUnfixed", + Optional: pointer.BoolPtr(true), + }, + }, + }, + + { + Name: "TRIVY_SKIP_FILES", + ValueFrom: &corev1.EnvVarSource{ + ConfigMapKeyRef: &corev1.ConfigMapKeySelector{ + LocalObjectReference: corev1.LocalObjectReference{ + Name: "starboard-trivy-config", + }, + Key: "trivy.skipFiles", + Optional: pointer.BoolPtr(true), + }, + }, + }, + { + Name: "TRIVY_SKIP_DIRS", + ValueFrom: &corev1.EnvVarSource{ + ConfigMapKeyRef: &corev1.ConfigMapKeySelector{ + LocalObjectReference: corev1.LocalObjectReference{ + Name: "starboard-trivy-config", + }, + Key: "trivy.skipDirs", + Optional: pointer.BoolPtr(true), + }, + }, + }, + { + Name: "HTTP_PROXY", + ValueFrom: &corev1.EnvVarSource{ + ConfigMapKeyRef: &corev1.ConfigMapKeySelector{ + LocalObjectReference: corev1.LocalObjectReference{ + Name: "starboard-trivy-config", + }, + Key: "trivy.httpProxy", + Optional: pointer.BoolPtr(true), + }, + }, + }, + { + Name: "HTTPS_PROXY", + ValueFrom: &corev1.EnvVarSource{ + ConfigMapKeyRef: &corev1.ConfigMapKeySelector{ + LocalObjectReference: corev1.LocalObjectReference{ + Name: "starboard-trivy-config", + }, + Key: "trivy.httpsProxy", + Optional: pointer.BoolPtr(true), + }, + }, + }, + { + Name: "NO_PROXY", + ValueFrom: &corev1.EnvVarSource{ + ConfigMapKeyRef: &corev1.ConfigMapKeySelector{ + LocalObjectReference: corev1.LocalObjectReference{ + Name: "starboard-trivy-config", + }, + Key: "trivy.noProxy", + Optional: pointer.BoolPtr(true), + }, + }, + }, + }, + Command: []string{ + "trivy", + }, + Args: []string{ + "--cache-dir", "/tmp/trivy/.cache", + "--quiet", + "image", + "--skip-update", + "--format", "json", + "nginx:1.16", + }, + Resources: corev1.ResourceRequirements{ + Requests: corev1.ResourceList{ + corev1.ResourceCPU: resource.MustParse("100m"), + corev1.ResourceMemory: resource.MustParse("100M"), + }, + Limits: corev1.ResourceList{ + corev1.ResourceCPU: resource.MustParse("500m"), + corev1.ResourceMemory: resource.MustParse("500M"), + }, + }, + VolumeMounts: []corev1.VolumeMount{ + tmpVolumeMount, + }, + SecurityContext: &corev1.SecurityContext{ + Privileged: pointer.BoolPtr(false), + AllowPrivilegeEscalation: pointer.BoolPtr(false), + Capabilities: &corev1.Capabilities{ + Drop: []corev1.Capability{"all"}, + }, + ReadOnlyRootFilesystem: pointer.BoolPtr(true), + }, + }, + }, + SecurityContext: &corev1.PodSecurityContext{}, + }, + }, + { + name: "Standalone mode with insecure registry", + config: map[string]string{ + "trivy.imageRef": "docker.io/aquasec/trivy:0.14.0", + "trivy.mode": string(trivy.Standalone), + "trivy.insecureRegistry.pocRegistry": "poc.myregistry.harbor.com.pl", + "trivy.dbRepository": defaultUpdateURL, + + "trivy.resources.requests.cpu": "100m", + "trivy.resources.requests.memory": "100M", + "trivy.resources.limits.cpu": "500m", + "trivy.resources.limits.memory": "500M", + }, + workloadSpec: &corev1.Pod{ + TypeMeta: metav1.TypeMeta{ + Kind: "Pod", + APIVersion: "v1", + }, + ObjectMeta: metav1.ObjectMeta{ + Name: "nginx", + Namespace: "prod-ns", + }, + Spec: corev1.PodSpec{ + Containers: []corev1.Container{ + { + Name: "nginx", + Image: "poc.myregistry.harbor.com.pl/nginx:1.16", + }, + }, + }}, + expectedJobSpec: corev1.PodSpec{ + Affinity: starboard.LinuxNodeAffinity(), + RestartPolicy: corev1.RestartPolicyNever, + ServiceAccountName: "starboard-sa", + AutomountServiceAccountToken: pointer.BoolPtr(false), + Volumes: []corev1.Volume{ + tmpVolume, + }, + InitContainers: []corev1.Container{ + { + Name: "00000000-0000-0000-0000-000000000001", + Image: "docker.io/aquasec/trivy:0.14.0", + ImagePullPolicy: corev1.PullIfNotPresent, + TerminationMessagePolicy: corev1.TerminationMessageFallbackToLogsOnError, + Env: []corev1.EnvVar{ + { + Name: "HTTP_PROXY", + ValueFrom: &corev1.EnvVarSource{ + ConfigMapKeyRef: &corev1.ConfigMapKeySelector{ + LocalObjectReference: corev1.LocalObjectReference{ + Name: "starboard-trivy-config", + }, + Key: "trivy.httpProxy", + Optional: pointer.BoolPtr(true), + }, + }, + }, + { + Name: "HTTPS_PROXY", + ValueFrom: &corev1.EnvVarSource{ + ConfigMapKeyRef: &corev1.ConfigMapKeySelector{ + LocalObjectReference: corev1.LocalObjectReference{ + Name: "starboard-trivy-config", + }, + Key: "trivy.httpsProxy", + Optional: pointer.BoolPtr(true), + }, + }, + }, + { + Name: "NO_PROXY", + ValueFrom: &corev1.EnvVarSource{ + ConfigMapKeyRef: &corev1.ConfigMapKeySelector{ + LocalObjectReference: corev1.LocalObjectReference{ + Name: "starboard-trivy-config", + }, + Key: "trivy.noProxy", + Optional: pointer.BoolPtr(true), + }, + }, + }, + { + Name: "GITHUB_TOKEN", + ValueFrom: &corev1.EnvVarSource{ + SecretKeyRef: &corev1.SecretKeySelector{ + LocalObjectReference: corev1.LocalObjectReference{ + Name: "starboard-trivy-config", + }, + Key: "trivy.githubToken", + Optional: pointer.BoolPtr(true), + }, + }, + }, + }, + Command: []string{ + "trivy", + }, + Args: []string{ + "--cache-dir", "/tmp/trivy/.cache", + "image", + "--download-db-only", + "--db-repository", defaultUpdateURL, + }, + Resources: corev1.ResourceRequirements{ + Requests: corev1.ResourceList{ + corev1.ResourceCPU: resource.MustParse("100m"), + corev1.ResourceMemory: resource.MustParse("100M"), + }, + Limits: corev1.ResourceList{ + corev1.ResourceCPU: resource.MustParse("500m"), + corev1.ResourceMemory: resource.MustParse("500M"), + }, + }, + VolumeMounts: []corev1.VolumeMount{ + tmpVolumeMount, + }, + }, + }, + Containers: []corev1.Container{ + { + Name: "nginx", + Image: "docker.io/aquasec/trivy:0.14.0", + ImagePullPolicy: corev1.PullIfNotPresent, + TerminationMessagePolicy: corev1.TerminationMessageFallbackToLogsOnError, + Env: []corev1.EnvVar{ + { + Name: "TRIVY_SEVERITY", + ValueFrom: &corev1.EnvVarSource{ + ConfigMapKeyRef: &corev1.ConfigMapKeySelector{ + LocalObjectReference: corev1.LocalObjectReference{ + Name: "starboard-trivy-config", + }, + Key: "trivy.severity", + Optional: pointer.BoolPtr(true), + }, + }, + }, + { + Name: "TRIVY_IGNORE_UNFIXED", + ValueFrom: &corev1.EnvVarSource{ + ConfigMapKeyRef: &corev1.ConfigMapKeySelector{ + LocalObjectReference: corev1.LocalObjectReference{ + Name: "starboard-trivy-config", + }, + Key: "trivy.ignoreUnfixed", + Optional: pointer.BoolPtr(true), + }, + }, + }, + + { + Name: "TRIVY_SKIP_FILES", + ValueFrom: &corev1.EnvVarSource{ + ConfigMapKeyRef: &corev1.ConfigMapKeySelector{ + LocalObjectReference: corev1.LocalObjectReference{ + Name: "starboard-trivy-config", + }, + Key: "trivy.skipFiles", + Optional: pointer.BoolPtr(true), + }, + }, + }, + { + Name: "TRIVY_SKIP_DIRS", + ValueFrom: &corev1.EnvVarSource{ + ConfigMapKeyRef: &corev1.ConfigMapKeySelector{ + LocalObjectReference: corev1.LocalObjectReference{ + Name: "starboard-trivy-config", + }, + Key: "trivy.skipDirs", + Optional: pointer.BoolPtr(true), + }, + }, + }, + { + Name: "HTTP_PROXY", + ValueFrom: &corev1.EnvVarSource{ + ConfigMapKeyRef: &corev1.ConfigMapKeySelector{ + LocalObjectReference: corev1.LocalObjectReference{ + Name: "starboard-trivy-config", + }, + Key: "trivy.httpProxy", + Optional: pointer.BoolPtr(true), + }, + }, + }, + { + Name: "HTTPS_PROXY", + ValueFrom: &corev1.EnvVarSource{ + ConfigMapKeyRef: &corev1.ConfigMapKeySelector{ + LocalObjectReference: corev1.LocalObjectReference{ + Name: "starboard-trivy-config", + }, + Key: "trivy.httpsProxy", + Optional: pointer.BoolPtr(true), + }, + }, + }, + { + Name: "NO_PROXY", + ValueFrom: &corev1.EnvVarSource{ + ConfigMapKeyRef: &corev1.ConfigMapKeySelector{ + LocalObjectReference: corev1.LocalObjectReference{ + Name: "starboard-trivy-config", + }, + Key: "trivy.noProxy", + Optional: pointer.BoolPtr(true), + }, + }, + }, + { + Name: "TRIVY_INSECURE", + Value: "true", + }, + }, + Command: []string{ + "trivy", + }, + Args: []string{ + "--cache-dir", "/tmp/trivy/.cache", + "--quiet", + "image", + "--skip-update", + "--format", "json", + "poc.myregistry.harbor.com.pl/nginx:1.16", + }, + Resources: corev1.ResourceRequirements{ + Requests: corev1.ResourceList{ + corev1.ResourceCPU: resource.MustParse("100m"), + corev1.ResourceMemory: resource.MustParse("100M"), + }, + Limits: corev1.ResourceList{ + corev1.ResourceCPU: resource.MustParse("500m"), + corev1.ResourceMemory: resource.MustParse("500M"), + }, + }, + VolumeMounts: []corev1.VolumeMount{ + tmpVolumeMount, + }, + SecurityContext: &corev1.SecurityContext{ + Privileged: pointer.BoolPtr(false), + AllowPrivilegeEscalation: pointer.BoolPtr(false), + Capabilities: &corev1.Capabilities{ + Drop: []corev1.Capability{"all"}, + }, + ReadOnlyRootFilesystem: pointer.BoolPtr(true), + }, + }, + }, + SecurityContext: &corev1.PodSecurityContext{}, + }, + }, + { + name: "Standalone mode with non-SSL registry", + config: map[string]string{ + "trivy.imageRef": "docker.io/aquasec/trivy:0.14.0", + "trivy.mode": string(trivy.Standalone), + "trivy.nonSslRegistry.pocRegistry": "poc.myregistry.harbor.com.pl", + "trivy.dbRepository": defaultUpdateURL, + "trivy.resources.requests.cpu": "100m", + "trivy.resources.requests.memory": "100M", + "trivy.resources.limits.cpu": "500m", + "trivy.resources.limits.memory": "500M", + }, + workloadSpec: &corev1.Pod{ + TypeMeta: metav1.TypeMeta{ + Kind: "Pod", + APIVersion: "v1", + }, + ObjectMeta: metav1.ObjectMeta{ + Name: "nginx", + Namespace: "prod-ns", + }, + Spec: corev1.PodSpec{ + Containers: []corev1.Container{ + { + Name: "nginx", + Image: "poc.myregistry.harbor.com.pl/nginx:1.16", + }, + }, + }, + }, + expectedJobSpec: corev1.PodSpec{ + Affinity: starboard.LinuxNodeAffinity(), + RestartPolicy: corev1.RestartPolicyNever, + ServiceAccountName: "starboard-sa", + AutomountServiceAccountToken: pointer.BoolPtr(false), + Volumes: []corev1.Volume{ + tmpVolume, + }, + InitContainers: []corev1.Container{ + { + Name: "00000000-0000-0000-0000-000000000001", + Image: "docker.io/aquasec/trivy:0.14.0", + ImagePullPolicy: corev1.PullIfNotPresent, + TerminationMessagePolicy: corev1.TerminationMessageFallbackToLogsOnError, + Env: []corev1.EnvVar{ + { + Name: "HTTP_PROXY", + ValueFrom: &corev1.EnvVarSource{ + ConfigMapKeyRef: &corev1.ConfigMapKeySelector{ + LocalObjectReference: corev1.LocalObjectReference{ + Name: "starboard-trivy-config", + }, + Key: "trivy.httpProxy", + Optional: pointer.BoolPtr(true), + }, + }, + }, + { + Name: "HTTPS_PROXY", + ValueFrom: &corev1.EnvVarSource{ + ConfigMapKeyRef: &corev1.ConfigMapKeySelector{ + LocalObjectReference: corev1.LocalObjectReference{ + Name: "starboard-trivy-config", + }, + Key: "trivy.httpsProxy", + Optional: pointer.BoolPtr(true), + }, + }, + }, + { + Name: "NO_PROXY", + ValueFrom: &corev1.EnvVarSource{ + ConfigMapKeyRef: &corev1.ConfigMapKeySelector{ + LocalObjectReference: corev1.LocalObjectReference{ + Name: "starboard-trivy-config", + }, + Key: "trivy.noProxy", + Optional: pointer.BoolPtr(true), + }, + }, + }, + { + Name: "GITHUB_TOKEN", + ValueFrom: &corev1.EnvVarSource{ + SecretKeyRef: &corev1.SecretKeySelector{ + LocalObjectReference: corev1.LocalObjectReference{ + Name: "starboard-trivy-config", + }, + Key: "trivy.githubToken", + Optional: pointer.BoolPtr(true), + }, + }, + }, + }, + Command: []string{ + "trivy", + }, + Args: []string{ + "--cache-dir", "/tmp/trivy/.cache", + "image", + "--download-db-only", + "--db-repository", defaultUpdateURL, + }, + Resources: corev1.ResourceRequirements{ + Requests: corev1.ResourceList{ + corev1.ResourceCPU: resource.MustParse("100m"), + corev1.ResourceMemory: resource.MustParse("100M"), + }, + Limits: corev1.ResourceList{ + corev1.ResourceCPU: resource.MustParse("500m"), + corev1.ResourceMemory: resource.MustParse("500M"), + }, + }, + VolumeMounts: []corev1.VolumeMount{ + tmpVolumeMount, + }, + }, + }, + Containers: []corev1.Container{ + { + Name: "nginx", + Image: "docker.io/aquasec/trivy:0.14.0", + ImagePullPolicy: corev1.PullIfNotPresent, + TerminationMessagePolicy: corev1.TerminationMessageFallbackToLogsOnError, + Env: []corev1.EnvVar{ + { + Name: "TRIVY_SEVERITY", + ValueFrom: &corev1.EnvVarSource{ + ConfigMapKeyRef: &corev1.ConfigMapKeySelector{ + LocalObjectReference: corev1.LocalObjectReference{ + Name: "starboard-trivy-config", + }, + Key: "trivy.severity", + Optional: pointer.BoolPtr(true), + }, + }, + }, + { + Name: "TRIVY_IGNORE_UNFIXED", + ValueFrom: &corev1.EnvVarSource{ + ConfigMapKeyRef: &corev1.ConfigMapKeySelector{ + LocalObjectReference: corev1.LocalObjectReference{ + Name: "starboard-trivy-config", + }, + Key: "trivy.ignoreUnfixed", + Optional: pointer.BoolPtr(true), + }, + }, + }, + + { + Name: "TRIVY_SKIP_FILES", + ValueFrom: &corev1.EnvVarSource{ + ConfigMapKeyRef: &corev1.ConfigMapKeySelector{ + LocalObjectReference: corev1.LocalObjectReference{ + Name: "starboard-trivy-config", + }, + Key: "trivy.skipFiles", + Optional: pointer.BoolPtr(true), + }, + }, + }, + { + Name: "TRIVY_SKIP_DIRS", + ValueFrom: &corev1.EnvVarSource{ + ConfigMapKeyRef: &corev1.ConfigMapKeySelector{ + LocalObjectReference: corev1.LocalObjectReference{ + Name: "starboard-trivy-config", + }, + Key: "trivy.skipDirs", + Optional: pointer.BoolPtr(true), + }, + }, + }, + { + Name: "HTTP_PROXY", + ValueFrom: &corev1.EnvVarSource{ + ConfigMapKeyRef: &corev1.ConfigMapKeySelector{ + LocalObjectReference: corev1.LocalObjectReference{ + Name: "starboard-trivy-config", + }, + Key: "trivy.httpProxy", + Optional: pointer.BoolPtr(true), + }, + }, + }, + { + Name: "HTTPS_PROXY", + ValueFrom: &corev1.EnvVarSource{ + ConfigMapKeyRef: &corev1.ConfigMapKeySelector{ + LocalObjectReference: corev1.LocalObjectReference{ + Name: "starboard-trivy-config", + }, + Key: "trivy.httpsProxy", + Optional: pointer.BoolPtr(true), + }, + }, + }, + { + Name: "NO_PROXY", + ValueFrom: &corev1.EnvVarSource{ + ConfigMapKeyRef: &corev1.ConfigMapKeySelector{ + LocalObjectReference: corev1.LocalObjectReference{ + Name: "starboard-trivy-config", + }, + Key: "trivy.noProxy", + Optional: pointer.BoolPtr(true), + }, + }, + }, + { + Name: "TRIVY_NON_SSL", + Value: "true", + }, + }, + Command: []string{ + "trivy", + }, + Args: []string{ + "--cache-dir", "/tmp/trivy/.cache", + "--quiet", + "image", + "--skip-update", + "--format", "json", + "poc.myregistry.harbor.com.pl/nginx:1.16", + }, + Resources: corev1.ResourceRequirements{ + Requests: corev1.ResourceList{ + corev1.ResourceCPU: resource.MustParse("100m"), + corev1.ResourceMemory: resource.MustParse("100M"), + }, + Limits: corev1.ResourceList{ + corev1.ResourceCPU: resource.MustParse("500m"), + corev1.ResourceMemory: resource.MustParse("500M"), + }, + }, + VolumeMounts: []corev1.VolumeMount{ + tmpVolumeMount, + }, + SecurityContext: &corev1.SecurityContext{ + Privileged: pointer.BoolPtr(false), + AllowPrivilegeEscalation: pointer.BoolPtr(false), + Capabilities: &corev1.Capabilities{ + Drop: []corev1.Capability{"all"}, + }, + ReadOnlyRootFilesystem: pointer.BoolPtr(true), + }, + }, + }, + SecurityContext: &corev1.PodSecurityContext{}, + }, + }, + { + name: "Standalone mode with trivyignore file", + config: map[string]string{ + "trivy.imageRef": "docker.io/aquasec/trivy:0.14.0", + "trivy.mode": string(trivy.Standalone), + "trivy.ignoreFile": `# Accept the risk +CVE-2018-14618 + +# No impact in our settings +CVE-2019-1543`, + "trivy.dbRepository": defaultUpdateURL, + "trivy.resources.requests.cpu": "100m", + "trivy.resources.requests.memory": "100M", + "trivy.resources.limits.cpu": "500m", + "trivy.resources.limits.memory": "500M", + }, + workloadSpec: &corev1.Pod{ + TypeMeta: metav1.TypeMeta{ + Kind: "Pod", + APIVersion: "v1", + }, + ObjectMeta: metav1.ObjectMeta{ + Name: "nginx", + Namespace: "prod-ns", + }, + Spec: corev1.PodSpec{ + Containers: []corev1.Container{ + { + Name: "nginx", + Image: "nginx:1.16", + }, + }, + }, + }, + expectedJobSpec: corev1.PodSpec{ + Affinity: starboard.LinuxNodeAffinity(), + RestartPolicy: corev1.RestartPolicyNever, + ServiceAccountName: "starboard-sa", + AutomountServiceAccountToken: pointer.BoolPtr(false), + Volumes: []corev1.Volume{ + tmpVolume, + { + Name: "ignorefile", + VolumeSource: corev1.VolumeSource{ + ConfigMap: &corev1.ConfigMapVolumeSource{ + LocalObjectReference: corev1.LocalObjectReference{ + Name: "starboard-trivy-config", + }, + Items: []corev1.KeyToPath{ + { + Key: "trivy.ignoreFile", + Path: ".trivyignore", + }, + }, + }, + }, + }, + }, + InitContainers: []corev1.Container{ + { + Name: "00000000-0000-0000-0000-000000000001", + Image: "docker.io/aquasec/trivy:0.14.0", + ImagePullPolicy: corev1.PullIfNotPresent, + TerminationMessagePolicy: corev1.TerminationMessageFallbackToLogsOnError, + Env: []corev1.EnvVar{ + { + Name: "HTTP_PROXY", + ValueFrom: &corev1.EnvVarSource{ + ConfigMapKeyRef: &corev1.ConfigMapKeySelector{ + LocalObjectReference: corev1.LocalObjectReference{ + Name: "starboard-trivy-config", + }, + Key: "trivy.httpProxy", + Optional: pointer.BoolPtr(true), + }, + }, + }, + { + Name: "HTTPS_PROXY", + ValueFrom: &corev1.EnvVarSource{ + ConfigMapKeyRef: &corev1.ConfigMapKeySelector{ + LocalObjectReference: corev1.LocalObjectReference{ + Name: "starboard-trivy-config", + }, + Key: "trivy.httpsProxy", + Optional: pointer.BoolPtr(true), + }, + }, + }, + { + Name: "NO_PROXY", + ValueFrom: &corev1.EnvVarSource{ + ConfigMapKeyRef: &corev1.ConfigMapKeySelector{ + LocalObjectReference: corev1.LocalObjectReference{ + Name: "starboard-trivy-config", + }, + Key: "trivy.noProxy", + Optional: pointer.BoolPtr(true), + }, + }, + }, + { + Name: "GITHUB_TOKEN", + ValueFrom: &corev1.EnvVarSource{ + SecretKeyRef: &corev1.SecretKeySelector{ + LocalObjectReference: corev1.LocalObjectReference{ + Name: "starboard-trivy-config", + }, + Key: "trivy.githubToken", + Optional: pointer.BoolPtr(true), + }, + }, + }, + }, + Command: []string{ + "trivy", + }, + Args: []string{ + "--cache-dir", "/tmp/trivy/.cache", + "image", + "--download-db-only", + "--db-repository", defaultUpdateURL, + }, + Resources: corev1.ResourceRequirements{ + Requests: corev1.ResourceList{ + corev1.ResourceCPU: resource.MustParse("100m"), + corev1.ResourceMemory: resource.MustParse("100M"), + }, + Limits: corev1.ResourceList{ + corev1.ResourceCPU: resource.MustParse("500m"), + corev1.ResourceMemory: resource.MustParse("500M"), + }, + }, + VolumeMounts: []corev1.VolumeMount{ + tmpVolumeMount, + }, + }, + }, + Containers: []corev1.Container{ + { + Name: "nginx", + Image: "docker.io/aquasec/trivy:0.14.0", + ImagePullPolicy: corev1.PullIfNotPresent, + TerminationMessagePolicy: corev1.TerminationMessageFallbackToLogsOnError, + Env: []corev1.EnvVar{ + { + Name: "TRIVY_SEVERITY", + ValueFrom: &corev1.EnvVarSource{ + ConfigMapKeyRef: &corev1.ConfigMapKeySelector{ + LocalObjectReference: corev1.LocalObjectReference{ + Name: "starboard-trivy-config", + }, + Key: "trivy.severity", + Optional: pointer.BoolPtr(true), + }, + }, + }, + { + Name: "TRIVY_IGNORE_UNFIXED", + ValueFrom: &corev1.EnvVarSource{ + ConfigMapKeyRef: &corev1.ConfigMapKeySelector{ + LocalObjectReference: corev1.LocalObjectReference{ + Name: "starboard-trivy-config", + }, + Key: "trivy.ignoreUnfixed", + Optional: pointer.BoolPtr(true), + }, + }, + }, + + { + Name: "TRIVY_SKIP_FILES", + ValueFrom: &corev1.EnvVarSource{ + ConfigMapKeyRef: &corev1.ConfigMapKeySelector{ + LocalObjectReference: corev1.LocalObjectReference{ + Name: "starboard-trivy-config", + }, + Key: "trivy.skipFiles", + Optional: pointer.BoolPtr(true), + }, + }, + }, + { + Name: "TRIVY_SKIP_DIRS", + ValueFrom: &corev1.EnvVarSource{ + ConfigMapKeyRef: &corev1.ConfigMapKeySelector{ + LocalObjectReference: corev1.LocalObjectReference{ + Name: "starboard-trivy-config", + }, + Key: "trivy.skipDirs", + Optional: pointer.BoolPtr(true), + }, + }, + }, + { + Name: "HTTP_PROXY", + ValueFrom: &corev1.EnvVarSource{ + ConfigMapKeyRef: &corev1.ConfigMapKeySelector{ + LocalObjectReference: corev1.LocalObjectReference{ + Name: "starboard-trivy-config", + }, + Key: "trivy.httpProxy", + Optional: pointer.BoolPtr(true), + }, + }, + }, + { + Name: "HTTPS_PROXY", + ValueFrom: &corev1.EnvVarSource{ + ConfigMapKeyRef: &corev1.ConfigMapKeySelector{ + LocalObjectReference: corev1.LocalObjectReference{ + Name: "starboard-trivy-config", + }, + Key: "trivy.httpsProxy", + Optional: pointer.BoolPtr(true), + }, + }, + }, + { + Name: "NO_PROXY", + ValueFrom: &corev1.EnvVarSource{ + ConfigMapKeyRef: &corev1.ConfigMapKeySelector{ + LocalObjectReference: corev1.LocalObjectReference{ + Name: "starboard-trivy-config", + }, + Key: "trivy.noProxy", + Optional: pointer.BoolPtr(true), + }, + }, + }, + { + Name: "TRIVY_IGNOREFILE", + Value: "/etc/trivy/.trivyignore", + }, + }, + Command: []string{ + "trivy", + }, + Args: []string{ + "--cache-dir", "/tmp/trivy/.cache", + "--quiet", + "image", + "--skip-update", + "--format", "json", + "nginx:1.16", + }, + Resources: corev1.ResourceRequirements{ + Requests: corev1.ResourceList{ + corev1.ResourceCPU: resource.MustParse("100m"), + corev1.ResourceMemory: resource.MustParse("100M"), + }, + Limits: corev1.ResourceList{ + corev1.ResourceCPU: resource.MustParse("500m"), + corev1.ResourceMemory: resource.MustParse("500M"), + }, + }, + VolumeMounts: []corev1.VolumeMount{ + tmpVolumeMount, + { + Name: "ignorefile", + MountPath: "/etc/trivy/.trivyignore", + SubPath: ".trivyignore", + }, + }, + SecurityContext: &corev1.SecurityContext{ + Privileged: pointer.BoolPtr(false), + AllowPrivilegeEscalation: pointer.BoolPtr(false), + Capabilities: &corev1.Capabilities{ + Drop: []corev1.Capability{"all"}, + }, + ReadOnlyRootFilesystem: pointer.BoolPtr(true), + }, + }, + }, + SecurityContext: &corev1.PodSecurityContext{}, + }, + }, + { + name: "Standalone mode with mirror", + config: map[string]string{ + "trivy.imageRef": "docker.io/aquasec/trivy:0.14.0", + "trivy.mode": string(trivy.Standalone), + + "trivy.dbRepository": defaultUpdateURL, + "trivy.resources.requests.cpu": "100m", + "trivy.resources.requests.memory": "100M", + "trivy.resources.limits.cpu": "500m", + "trivy.resources.limits.memory": "500M", + + "trivy.registry.mirror.index.docker.io": "mirror.io", + }, + workloadSpec: &corev1.Pod{ + TypeMeta: metav1.TypeMeta{ + Kind: "Pod", + APIVersion: "v1", + }, + ObjectMeta: metav1.ObjectMeta{ + Name: "nginx", + Namespace: "prod-ns", + }, + Spec: corev1.PodSpec{ + Containers: []corev1.Container{ + { + Name: "nginx", + Image: "nginx:1.16", + }, + }, + }, + }, + expectedJobSpec: corev1.PodSpec{ + Affinity: starboard.LinuxNodeAffinity(), + RestartPolicy: corev1.RestartPolicyNever, + ServiceAccountName: "starboard-sa", + AutomountServiceAccountToken: pointer.BoolPtr(false), + Volumes: []corev1.Volume{ + tmpVolume, + }, + InitContainers: []corev1.Container{ + { + Name: "00000000-0000-0000-0000-000000000001", + Image: "docker.io/aquasec/trivy:0.14.0", + ImagePullPolicy: corev1.PullIfNotPresent, + TerminationMessagePolicy: corev1.TerminationMessageFallbackToLogsOnError, + Env: []corev1.EnvVar{ + { + Name: "HTTP_PROXY", + ValueFrom: &corev1.EnvVarSource{ + ConfigMapKeyRef: &corev1.ConfigMapKeySelector{ + LocalObjectReference: corev1.LocalObjectReference{ + Name: "starboard-trivy-config", + }, + Key: "trivy.httpProxy", + Optional: pointer.BoolPtr(true), + }, + }, + }, + { + Name: "HTTPS_PROXY", + ValueFrom: &corev1.EnvVarSource{ + ConfigMapKeyRef: &corev1.ConfigMapKeySelector{ + LocalObjectReference: corev1.LocalObjectReference{ + Name: "starboard-trivy-config", + }, + Key: "trivy.httpsProxy", + Optional: pointer.BoolPtr(true), + }, + }, + }, + { + Name: "NO_PROXY", + ValueFrom: &corev1.EnvVarSource{ + ConfigMapKeyRef: &corev1.ConfigMapKeySelector{ + LocalObjectReference: corev1.LocalObjectReference{ + Name: "starboard-trivy-config", + }, + Key: "trivy.noProxy", + Optional: pointer.BoolPtr(true), + }, + }, + }, + + { + Name: "GITHUB_TOKEN", + ValueFrom: &corev1.EnvVarSource{ + SecretKeyRef: &corev1.SecretKeySelector{ + LocalObjectReference: corev1.LocalObjectReference{ + Name: "starboard-trivy-config", + }, + Key: "trivy.githubToken", + Optional: pointer.BoolPtr(true), + }, + }, + }, + }, + Command: []string{ + "trivy", + }, + Args: []string{ + "--cache-dir", "/tmp/trivy/.cache", + "image", + "--download-db-only", + "--db-repository", defaultUpdateURL, + }, + Resources: corev1.ResourceRequirements{ + Requests: corev1.ResourceList{ + corev1.ResourceCPU: resource.MustParse("100m"), + corev1.ResourceMemory: resource.MustParse("100M"), + }, + Limits: corev1.ResourceList{ + corev1.ResourceCPU: resource.MustParse("500m"), + corev1.ResourceMemory: resource.MustParse("500M"), + }, + }, + VolumeMounts: []corev1.VolumeMount{ + tmpVolumeMount, + }, + }, + }, + Containers: []corev1.Container{ + { + Name: "nginx", + Image: "docker.io/aquasec/trivy:0.14.0", + ImagePullPolicy: corev1.PullIfNotPresent, + TerminationMessagePolicy: corev1.TerminationMessageFallbackToLogsOnError, + Env: []corev1.EnvVar{ + { + Name: "TRIVY_SEVERITY", + ValueFrom: &corev1.EnvVarSource{ + ConfigMapKeyRef: &corev1.ConfigMapKeySelector{ + LocalObjectReference: corev1.LocalObjectReference{ + Name: "starboard-trivy-config", + }, + Key: "trivy.severity", + Optional: pointer.BoolPtr(true), + }, + }, + }, + { + Name: "TRIVY_IGNORE_UNFIXED", + ValueFrom: &corev1.EnvVarSource{ + ConfigMapKeyRef: &corev1.ConfigMapKeySelector{ + LocalObjectReference: corev1.LocalObjectReference{ + Name: "starboard-trivy-config", + }, + Key: "trivy.ignoreUnfixed", + Optional: pointer.BoolPtr(true), + }, + }, + }, + + { + Name: "TRIVY_SKIP_FILES", + ValueFrom: &corev1.EnvVarSource{ + ConfigMapKeyRef: &corev1.ConfigMapKeySelector{ + LocalObjectReference: corev1.LocalObjectReference{ + Name: "starboard-trivy-config", + }, + Key: "trivy.skipFiles", + Optional: pointer.BoolPtr(true), + }, + }, + }, + { + Name: "TRIVY_SKIP_DIRS", + ValueFrom: &corev1.EnvVarSource{ + ConfigMapKeyRef: &corev1.ConfigMapKeySelector{ + LocalObjectReference: corev1.LocalObjectReference{ + Name: "starboard-trivy-config", + }, + Key: "trivy.skipDirs", + Optional: pointer.BoolPtr(true), + }, + }, + }, + { + Name: "HTTP_PROXY", + ValueFrom: &corev1.EnvVarSource{ + ConfigMapKeyRef: &corev1.ConfigMapKeySelector{ + LocalObjectReference: corev1.LocalObjectReference{ + Name: "starboard-trivy-config", + }, + Key: "trivy.httpProxy", + Optional: pointer.BoolPtr(true), + }, + }, + }, + { + Name: "HTTPS_PROXY", + ValueFrom: &corev1.EnvVarSource{ + ConfigMapKeyRef: &corev1.ConfigMapKeySelector{ + LocalObjectReference: corev1.LocalObjectReference{ + Name: "starboard-trivy-config", + }, + Key: "trivy.httpsProxy", + Optional: pointer.BoolPtr(true), + }, + }, + }, + { + Name: "NO_PROXY", + ValueFrom: &corev1.EnvVarSource{ + ConfigMapKeyRef: &corev1.ConfigMapKeySelector{ + LocalObjectReference: corev1.LocalObjectReference{ + Name: "starboard-trivy-config", + }, + Key: "trivy.noProxy", + Optional: pointer.BoolPtr(true), + }, + }, + }, + }, + Command: []string{ + "trivy", + }, + Args: []string{ + "--cache-dir", "/tmp/trivy/.cache", + "--quiet", + "image", + "--skip-update", + "--format", "json", + "mirror.io/library/nginx:1.16", + }, + Resources: corev1.ResourceRequirements{ + Requests: corev1.ResourceList{ + corev1.ResourceCPU: resource.MustParse("100m"), + corev1.ResourceMemory: resource.MustParse("100M"), + }, + Limits: corev1.ResourceList{ + corev1.ResourceCPU: resource.MustParse("500m"), + corev1.ResourceMemory: resource.MustParse("500M"), + }, + }, + VolumeMounts: []corev1.VolumeMount{ + tmpVolumeMount, + }, + SecurityContext: &corev1.SecurityContext{ + Privileged: pointer.BoolPtr(false), + AllowPrivilegeEscalation: pointer.BoolPtr(false), + Capabilities: &corev1.Capabilities{ + Drop: []corev1.Capability{"all"}, + }, + ReadOnlyRootFilesystem: pointer.BoolPtr(true), + }, + }, + }, + SecurityContext: &corev1.PodSecurityContext{}, + }, + }, + { + name: "ClientServer mode without insecure registry", + config: map[string]string{ + "trivy.imageRef": "docker.io/aquasec/trivy:0.14.0", + "trivy.mode": string(trivy.ClientServer), + "trivy.serverURL": "http://trivy.trivy:4954", + "trivy.dbRepository": defaultUpdateURL, + "trivy.resources.requests.cpu": "100m", + "trivy.resources.requests.memory": "100M", + "trivy.resources.limits.cpu": "500m", + "trivy.resources.limits.memory": "500M", + }, + workloadSpec: &corev1.Pod{ + TypeMeta: metav1.TypeMeta{ + Kind: "Pod", + APIVersion: "v1", + }, + ObjectMeta: metav1.ObjectMeta{ + Name: "nginx", + Namespace: "prod-ns", + }, + Spec: corev1.PodSpec{ + Containers: []corev1.Container{ + { + Name: "nginx", + Image: "nginx:1.16", + }, + }, + }, + }, + expectedJobSpec: corev1.PodSpec{ + Affinity: starboard.LinuxNodeAffinity(), + RestartPolicy: corev1.RestartPolicyNever, + ServiceAccountName: "starboard-sa", + AutomountServiceAccountToken: pointer.BoolPtr(false), + Containers: []corev1.Container{ + { + Name: "nginx", + Image: "docker.io/aquasec/trivy:0.14.0", + ImagePullPolicy: corev1.PullIfNotPresent, + TerminationMessagePolicy: corev1.TerminationMessageFallbackToLogsOnError, + Env: []corev1.EnvVar{ + { + Name: "HTTP_PROXY", + ValueFrom: &corev1.EnvVarSource{ + ConfigMapKeyRef: &corev1.ConfigMapKeySelector{ + LocalObjectReference: corev1.LocalObjectReference{ + Name: "starboard-trivy-config", + }, + Key: "trivy.httpProxy", + Optional: pointer.BoolPtr(true), + }, + }, + }, + { + Name: "HTTPS_PROXY", + ValueFrom: &corev1.EnvVarSource{ + ConfigMapKeyRef: &corev1.ConfigMapKeySelector{ + LocalObjectReference: corev1.LocalObjectReference{ + Name: "starboard-trivy-config", + }, + Key: "trivy.httpsProxy", + Optional: pointer.BoolPtr(true), + }, + }, + }, + { + Name: "NO_PROXY", + ValueFrom: &corev1.EnvVarSource{ + ConfigMapKeyRef: &corev1.ConfigMapKeySelector{ + LocalObjectReference: corev1.LocalObjectReference{ + Name: "starboard-trivy-config", + }, + Key: "trivy.noProxy", + Optional: pointer.BoolPtr(true), + }, + }, + }, + { + Name: "TRIVY_SEVERITY", + ValueFrom: &corev1.EnvVarSource{ + ConfigMapKeyRef: &corev1.ConfigMapKeySelector{ + LocalObjectReference: corev1.LocalObjectReference{ + Name: "starboard-trivy-config", + }, + Key: "trivy.severity", + Optional: pointer.BoolPtr(true), + }, + }, + }, + { + Name: "TRIVY_IGNORE_UNFIXED", + ValueFrom: &corev1.EnvVarSource{ + ConfigMapKeyRef: &corev1.ConfigMapKeySelector{ + LocalObjectReference: corev1.LocalObjectReference{ + Name: "starboard-trivy-config", + }, + Key: "trivy.ignoreUnfixed", + Optional: pointer.BoolPtr(true), + }, + }, + }, + + { + Name: "TRIVY_SKIP_FILES", + ValueFrom: &corev1.EnvVarSource{ + ConfigMapKeyRef: &corev1.ConfigMapKeySelector{ + LocalObjectReference: corev1.LocalObjectReference{ + Name: "starboard-trivy-config", + }, + Key: "trivy.skipFiles", + Optional: pointer.BoolPtr(true), + }, + }, + }, + { + Name: "TRIVY_SKIP_DIRS", + ValueFrom: &corev1.EnvVarSource{ + ConfigMapKeyRef: &corev1.ConfigMapKeySelector{ + LocalObjectReference: corev1.LocalObjectReference{ + Name: "starboard-trivy-config", + }, + Key: "trivy.skipDirs", + Optional: pointer.BoolPtr(true), + }, + }, + }, + { + Name: "TRIVY_TOKEN_HEADER", + ValueFrom: &corev1.EnvVarSource{ + ConfigMapKeyRef: &corev1.ConfigMapKeySelector{ + LocalObjectReference: corev1.LocalObjectReference{ + Name: "starboard-trivy-config", + }, + Key: "trivy.serverTokenHeader", + Optional: pointer.BoolPtr(true), + }, + }, + }, + { + Name: "TRIVY_TOKEN", + ValueFrom: &corev1.EnvVarSource{ + SecretKeyRef: &corev1.SecretKeySelector{ + LocalObjectReference: corev1.LocalObjectReference{ + Name: "starboard-trivy-config", + }, + Key: "trivy.serverToken", + Optional: pointer.BoolPtr(true), + }, + }, + }, + { + Name: "TRIVY_CUSTOM_HEADERS", + ValueFrom: &corev1.EnvVarSource{ + SecretKeyRef: &corev1.SecretKeySelector{ + LocalObjectReference: corev1.LocalObjectReference{ + Name: "starboard-trivy-config", + }, + Key: "trivy.serverCustomHeaders", + Optional: pointer.BoolPtr(true), + }, + }, + }, + }, + Command: []string{ + "trivy", + }, + Args: []string{ + "--quiet", + "client", + "--format", + "json", + "--remote", + "http://trivy.trivy:4954", + "nginx:1.16", + }, + Resources: corev1.ResourceRequirements{ + Requests: corev1.ResourceList{ + corev1.ResourceCPU: resource.MustParse("100m"), + corev1.ResourceMemory: resource.MustParse("100M"), + }, + Limits: corev1.ResourceList{ + corev1.ResourceCPU: resource.MustParse("500m"), + corev1.ResourceMemory: resource.MustParse("500M"), + }, + }, + }, + }, + }, + }, + { + name: "ClientServer mode without insecure registry", + config: map[string]string{ + "trivy.imageRef": "docker.io/aquasec/trivy:0.14.0", + "trivy.mode": string(trivy.ClientServer), + "trivy.serverURL": "http://trivy.trivy:4954", + "trivy.dbRepository": defaultUpdateURL, + "trivy.resources.requests.cpu": "100m", + "trivy.resources.requests.memory": "100M", + "trivy.resources.limits.cpu": "500m", + "trivy.resources.limits.memory": "500M", + }, + workloadSpec: &corev1.Pod{ + TypeMeta: metav1.TypeMeta{ + Kind: "Pod", + APIVersion: "v1", + }, + ObjectMeta: metav1.ObjectMeta{ + Name: "nginx", + Namespace: "prod-ns", + }, + Spec: corev1.PodSpec{ + Containers: []corev1.Container{ + { + Name: "nginx", + Image: "nginx:1.16", + }, + }, + }, + }, + expectedJobSpec: corev1.PodSpec{ + Affinity: starboard.LinuxNodeAffinity(), + RestartPolicy: corev1.RestartPolicyNever, + ServiceAccountName: "starboard-sa", + AutomountServiceAccountToken: pointer.BoolPtr(false), + Containers: []corev1.Container{ + { + Name: "nginx", + Image: "docker.io/aquasec/trivy:0.14.0", + ImagePullPolicy: corev1.PullIfNotPresent, + TerminationMessagePolicy: corev1.TerminationMessageFallbackToLogsOnError, + Env: []corev1.EnvVar{ + { + Name: "HTTP_PROXY", + ValueFrom: &corev1.EnvVarSource{ + ConfigMapKeyRef: &corev1.ConfigMapKeySelector{ + LocalObjectReference: corev1.LocalObjectReference{ + Name: "starboard-trivy-config", + }, + Key: "trivy.httpProxy", + Optional: pointer.BoolPtr(true), + }, + }, + }, + { + Name: "HTTPS_PROXY", + ValueFrom: &corev1.EnvVarSource{ + ConfigMapKeyRef: &corev1.ConfigMapKeySelector{ + LocalObjectReference: corev1.LocalObjectReference{ + Name: "starboard-trivy-config", + }, + Key: "trivy.httpsProxy", + Optional: pointer.BoolPtr(true), + }, + }, + }, + { + Name: "NO_PROXY", + ValueFrom: &corev1.EnvVarSource{ + ConfigMapKeyRef: &corev1.ConfigMapKeySelector{ + LocalObjectReference: corev1.LocalObjectReference{ + Name: "starboard-trivy-config", + }, + Key: "trivy.noProxy", + Optional: pointer.BoolPtr(true), + }, + }, + }, + { + Name: "TRIVY_SEVERITY", + ValueFrom: &corev1.EnvVarSource{ + ConfigMapKeyRef: &corev1.ConfigMapKeySelector{ + LocalObjectReference: corev1.LocalObjectReference{ + Name: "starboard-trivy-config", + }, + Key: "trivy.severity", + Optional: pointer.BoolPtr(true), + }, + }, + }, + { + Name: "TRIVY_IGNORE_UNFIXED", + ValueFrom: &corev1.EnvVarSource{ + ConfigMapKeyRef: &corev1.ConfigMapKeySelector{ + LocalObjectReference: corev1.LocalObjectReference{ + Name: "starboard-trivy-config", + }, + Key: "trivy.ignoreUnfixed", + Optional: pointer.BoolPtr(true), + }, + }, + }, + + { + Name: "TRIVY_SKIP_FILES", + ValueFrom: &corev1.EnvVarSource{ + ConfigMapKeyRef: &corev1.ConfigMapKeySelector{ + LocalObjectReference: corev1.LocalObjectReference{ + Name: "starboard-trivy-config", + }, + Key: "trivy.skipFiles", + Optional: pointer.BoolPtr(true), + }, + }, + }, + { + Name: "TRIVY_SKIP_DIRS", + ValueFrom: &corev1.EnvVarSource{ + ConfigMapKeyRef: &corev1.ConfigMapKeySelector{ + LocalObjectReference: corev1.LocalObjectReference{ + Name: "starboard-trivy-config", + }, + Key: "trivy.skipDirs", + Optional: pointer.BoolPtr(true), + }, + }, + }, + { + Name: "TRIVY_TOKEN_HEADER", + ValueFrom: &corev1.EnvVarSource{ + ConfigMapKeyRef: &corev1.ConfigMapKeySelector{ + LocalObjectReference: corev1.LocalObjectReference{ + Name: "starboard-trivy-config", + }, + Key: "trivy.serverTokenHeader", + Optional: pointer.BoolPtr(true), + }, + }, + }, + { + Name: "TRIVY_TOKEN", + ValueFrom: &corev1.EnvVarSource{ + SecretKeyRef: &corev1.SecretKeySelector{ + LocalObjectReference: corev1.LocalObjectReference{ + Name: "starboard-trivy-config", + }, + Key: "trivy.serverToken", + Optional: pointer.BoolPtr(true), + }, + }, + }, + { + Name: "TRIVY_CUSTOM_HEADERS", + ValueFrom: &corev1.EnvVarSource{ + SecretKeyRef: &corev1.SecretKeySelector{ + LocalObjectReference: corev1.LocalObjectReference{ + Name: "starboard-trivy-config", + }, + Key: "trivy.serverCustomHeaders", + Optional: pointer.BoolPtr(true), + }, + }, + }, + }, + Command: []string{ + "trivy", + }, + Args: []string{ + "--quiet", + "client", + "--format", + "json", + "--remote", + "http://trivy.trivy:4954", + "nginx:1.16", + }, + Resources: corev1.ResourceRequirements{ + Requests: corev1.ResourceList{ + corev1.ResourceCPU: resource.MustParse("100m"), + corev1.ResourceMemory: resource.MustParse("100M"), + }, + Limits: corev1.ResourceList{ + corev1.ResourceCPU: resource.MustParse("500m"), + corev1.ResourceMemory: resource.MustParse("500M"), + }, + }, + }, + }, + }, + }, + { + name: "ClientServer mode with insecure server", + config: map[string]string{ + "trivy.imageRef": "docker.io/aquasec/trivy:0.14.0", + "trivy.mode": string(trivy.ClientServer), + "trivy.serverURL": "https://trivy.trivy:4954", + "trivy.serverInsecure": "true", + "trivy.dbRepository": defaultUpdateURL, + "trivy.resources.requests.cpu": "100m", + "trivy.resources.requests.memory": "100M", + "trivy.resources.limits.cpu": "500m", + "trivy.resources.limits.memory": "500M", + }, + workloadSpec: &corev1.Pod{ + TypeMeta: metav1.TypeMeta{ + Kind: "Pod", + APIVersion: "v1", + }, + ObjectMeta: metav1.ObjectMeta{ + Name: "nginx", + Namespace: "prod-ns", + }, + Spec: corev1.PodSpec{ + Containers: []corev1.Container{ + { + Name: "nginx", + Image: "poc.myregistry.harbor.com.pl/nginx:1.16", + }, + }, + }, + }, + expectedJobSpec: corev1.PodSpec{ + Affinity: starboard.LinuxNodeAffinity(), + RestartPolicy: corev1.RestartPolicyNever, + ServiceAccountName: "starboard-sa", + AutomountServiceAccountToken: pointer.BoolPtr(false), + Containers: []corev1.Container{ + { + Name: "nginx", + Image: "docker.io/aquasec/trivy:0.14.0", + ImagePullPolicy: corev1.PullIfNotPresent, + TerminationMessagePolicy: corev1.TerminationMessageFallbackToLogsOnError, + Env: []corev1.EnvVar{ + { + Name: "HTTP_PROXY", + ValueFrom: &corev1.EnvVarSource{ + ConfigMapKeyRef: &corev1.ConfigMapKeySelector{ + LocalObjectReference: corev1.LocalObjectReference{ + Name: "starboard-trivy-config", + }, + Key: "trivy.httpProxy", + Optional: pointer.BoolPtr(true), + }, + }, + }, + { + Name: "HTTPS_PROXY", + ValueFrom: &corev1.EnvVarSource{ + ConfigMapKeyRef: &corev1.ConfigMapKeySelector{ + LocalObjectReference: corev1.LocalObjectReference{ + Name: "starboard-trivy-config", + }, + Key: "trivy.httpsProxy", + Optional: pointer.BoolPtr(true), + }, + }, + }, + { + Name: "NO_PROXY", + ValueFrom: &corev1.EnvVarSource{ + ConfigMapKeyRef: &corev1.ConfigMapKeySelector{ + LocalObjectReference: corev1.LocalObjectReference{ + Name: "starboard-trivy-config", + }, + Key: "trivy.noProxy", + Optional: pointer.BoolPtr(true), + }, + }, + }, + { + Name: "TRIVY_SEVERITY", + ValueFrom: &corev1.EnvVarSource{ + ConfigMapKeyRef: &corev1.ConfigMapKeySelector{ + LocalObjectReference: corev1.LocalObjectReference{ + Name: "starboard-trivy-config", + }, + Key: "trivy.severity", + Optional: pointer.BoolPtr(true), + }, + }, + }, + { + Name: "TRIVY_IGNORE_UNFIXED", + ValueFrom: &corev1.EnvVarSource{ + ConfigMapKeyRef: &corev1.ConfigMapKeySelector{ + LocalObjectReference: corev1.LocalObjectReference{ + Name: "starboard-trivy-config", + }, + Key: "trivy.ignoreUnfixed", + Optional: pointer.BoolPtr(true), + }, + }, + }, + + { + Name: "TRIVY_SKIP_FILES", + ValueFrom: &corev1.EnvVarSource{ + ConfigMapKeyRef: &corev1.ConfigMapKeySelector{ + LocalObjectReference: corev1.LocalObjectReference{ + Name: "starboard-trivy-config", + }, + Key: "trivy.skipFiles", + Optional: pointer.BoolPtr(true), + }, + }, + }, + { + Name: "TRIVY_SKIP_DIRS", + ValueFrom: &corev1.EnvVarSource{ + ConfigMapKeyRef: &corev1.ConfigMapKeySelector{ + LocalObjectReference: corev1.LocalObjectReference{ + Name: "starboard-trivy-config", + }, + Key: "trivy.skipDirs", + Optional: pointer.BoolPtr(true), + }, + }, + }, + { + Name: "TRIVY_TOKEN_HEADER", + ValueFrom: &corev1.EnvVarSource{ + ConfigMapKeyRef: &corev1.ConfigMapKeySelector{ + LocalObjectReference: corev1.LocalObjectReference{ + Name: "starboard-trivy-config", + }, + Key: "trivy.serverTokenHeader", + Optional: pointer.BoolPtr(true), + }, + }, + }, + { + Name: "TRIVY_TOKEN", + ValueFrom: &corev1.EnvVarSource{ + SecretKeyRef: &corev1.SecretKeySelector{ + LocalObjectReference: corev1.LocalObjectReference{ + Name: "starboard-trivy-config", + }, + Key: "trivy.serverToken", + Optional: pointer.BoolPtr(true), + }, + }, + }, + { + Name: "TRIVY_CUSTOM_HEADERS", + ValueFrom: &corev1.EnvVarSource{ + SecretKeyRef: &corev1.SecretKeySelector{ + LocalObjectReference: corev1.LocalObjectReference{ + Name: "starboard-trivy-config", + }, + Key: "trivy.serverCustomHeaders", + Optional: pointer.BoolPtr(true), + }, + }, + }, + { + Name: "TRIVY_INSECURE", + Value: "true", + }, + }, + Command: []string{ + "trivy", + }, + Args: []string{ + "--quiet", + "client", + "--format", + "json", + "--remote", + "https://trivy.trivy:4954", + "poc.myregistry.harbor.com.pl/nginx:1.16", + }, + Resources: corev1.ResourceRequirements{ + Requests: corev1.ResourceList{ + corev1.ResourceCPU: resource.MustParse("100m"), + corev1.ResourceMemory: resource.MustParse("100M"), + }, + Limits: corev1.ResourceList{ + corev1.ResourceCPU: resource.MustParse("500m"), + corev1.ResourceMemory: resource.MustParse("500M"), + }, + }, + }, + }, + }, + }, + { + name: "ClientServer mode with non-SSL registry", + config: map[string]string{ + "trivy.imageRef": "docker.io/aquasec/trivy:0.14.0", + "trivy.mode": string(trivy.ClientServer), + "trivy.serverURL": "http://trivy.trivy:4954", + "trivy.nonSslRegistry.pocRegistry": "poc.myregistry.harbor.com.pl", + "trivy.dbRepository": defaultUpdateURL, + "trivy.resources.requests.cpu": "100m", + "trivy.resources.requests.memory": "100M", + "trivy.resources.limits.cpu": "500m", + "trivy.resources.limits.memory": "500M", + }, + workloadSpec: &corev1.Pod{ + TypeMeta: metav1.TypeMeta{ + Kind: "Pod", + APIVersion: "v1", + }, + ObjectMeta: metav1.ObjectMeta{ + Name: "nginx", + Namespace: "prod-ns", + }, + Spec: corev1.PodSpec{ + Containers: []corev1.Container{ + { + Name: "nginx", + Image: "poc.myregistry.harbor.com.pl/nginx:1.16", + }, + }, + }, + }, + expectedJobSpec: corev1.PodSpec{ + Affinity: starboard.LinuxNodeAffinity(), + RestartPolicy: corev1.RestartPolicyNever, + ServiceAccountName: "starboard-sa", + AutomountServiceAccountToken: pointer.BoolPtr(false), + Containers: []corev1.Container{ + { + Name: "nginx", + Image: "docker.io/aquasec/trivy:0.14.0", + ImagePullPolicy: corev1.PullIfNotPresent, + TerminationMessagePolicy: corev1.TerminationMessageFallbackToLogsOnError, + Env: []corev1.EnvVar{ + { + Name: "HTTP_PROXY", + ValueFrom: &corev1.EnvVarSource{ + ConfigMapKeyRef: &corev1.ConfigMapKeySelector{ + LocalObjectReference: corev1.LocalObjectReference{ + Name: "starboard-trivy-config", + }, + Key: "trivy.httpProxy", + Optional: pointer.BoolPtr(true), + }, + }, + }, + { + Name: "HTTPS_PROXY", + ValueFrom: &corev1.EnvVarSource{ + ConfigMapKeyRef: &corev1.ConfigMapKeySelector{ + LocalObjectReference: corev1.LocalObjectReference{ + Name: "starboard-trivy-config", + }, + Key: "trivy.httpsProxy", + Optional: pointer.BoolPtr(true), + }, + }, + }, + { + Name: "NO_PROXY", + ValueFrom: &corev1.EnvVarSource{ + ConfigMapKeyRef: &corev1.ConfigMapKeySelector{ + LocalObjectReference: corev1.LocalObjectReference{ + Name: "starboard-trivy-config", + }, + Key: "trivy.noProxy", + Optional: pointer.BoolPtr(true), + }, + }, + }, + { + Name: "TRIVY_SEVERITY", + ValueFrom: &corev1.EnvVarSource{ + ConfigMapKeyRef: &corev1.ConfigMapKeySelector{ + LocalObjectReference: corev1.LocalObjectReference{ + Name: "starboard-trivy-config", + }, + Key: "trivy.severity", + Optional: pointer.BoolPtr(true), + }, + }, + }, + { + Name: "TRIVY_IGNORE_UNFIXED", + ValueFrom: &corev1.EnvVarSource{ + ConfigMapKeyRef: &corev1.ConfigMapKeySelector{ + LocalObjectReference: corev1.LocalObjectReference{ + Name: "starboard-trivy-config", + }, + Key: "trivy.ignoreUnfixed", + Optional: pointer.BoolPtr(true), + }, + }, + }, + + { + Name: "TRIVY_SKIP_FILES", + ValueFrom: &corev1.EnvVarSource{ + ConfigMapKeyRef: &corev1.ConfigMapKeySelector{ + LocalObjectReference: corev1.LocalObjectReference{ + Name: "starboard-trivy-config", + }, + Key: "trivy.skipFiles", + Optional: pointer.BoolPtr(true), + }, + }, + }, + { + Name: "TRIVY_SKIP_DIRS", + ValueFrom: &corev1.EnvVarSource{ + ConfigMapKeyRef: &corev1.ConfigMapKeySelector{ + LocalObjectReference: corev1.LocalObjectReference{ + Name: "starboard-trivy-config", + }, + Key: "trivy.skipDirs", + Optional: pointer.BoolPtr(true), + }, + }, + }, + { + Name: "TRIVY_TOKEN_HEADER", + ValueFrom: &corev1.EnvVarSource{ + ConfigMapKeyRef: &corev1.ConfigMapKeySelector{ + LocalObjectReference: corev1.LocalObjectReference{ + Name: "starboard-trivy-config", + }, + Key: "trivy.serverTokenHeader", + Optional: pointer.BoolPtr(true), + }, + }, + }, + { + Name: "TRIVY_TOKEN", + ValueFrom: &corev1.EnvVarSource{ + SecretKeyRef: &corev1.SecretKeySelector{ + LocalObjectReference: corev1.LocalObjectReference{ + Name: "starboard-trivy-config", + }, + Key: "trivy.serverToken", + Optional: pointer.BoolPtr(true), + }, + }, + }, + { + Name: "TRIVY_CUSTOM_HEADERS", + ValueFrom: &corev1.EnvVarSource{ + SecretKeyRef: &corev1.SecretKeySelector{ + LocalObjectReference: corev1.LocalObjectReference{ + Name: "starboard-trivy-config", + }, + Key: "trivy.serverCustomHeaders", + Optional: pointer.BoolPtr(true), + }, + }, + }, + { + Name: "TRIVY_NON_SSL", + Value: "true", + }, + }, + Command: []string{ + "trivy", + }, + Args: []string{ + "--quiet", + "client", + "--format", + "json", + "--remote", + "http://trivy.trivy:4954", + "poc.myregistry.harbor.com.pl/nginx:1.16", + }, + Resources: corev1.ResourceRequirements{ + Requests: corev1.ResourceList{ + corev1.ResourceCPU: resource.MustParse("100m"), + corev1.ResourceMemory: resource.MustParse("100M"), + }, + Limits: corev1.ResourceList{ + corev1.ResourceCPU: resource.MustParse("500m"), + corev1.ResourceMemory: resource.MustParse("500M"), + }, + }, + }, + }, + }, + }, + { + name: "ClientServer mode with trivyignore file", + config: map[string]string{ + "trivy.imageRef": "docker.io/aquasec/trivy:0.14.0", + "trivy.mode": string(trivy.ClientServer), + "trivy.serverURL": "http://trivy.trivy:4954", + "trivy.ignoreFile": `# Accept the risk +CVE-2018-14618 + +# No impact in our settings +CVE-2019-1543`, + "trivy.dbRepository": defaultUpdateURL, + "trivy.resources.requests.cpu": "100m", + "trivy.resources.requests.memory": "100M", + "trivy.resources.limits.cpu": "500m", + "trivy.resources.limits.memory": "500M", + }, + workloadSpec: &corev1.Pod{ + TypeMeta: metav1.TypeMeta{ + Kind: "Pod", + APIVersion: "v1", + }, + ObjectMeta: metav1.ObjectMeta{ + Name: "nginx", + Namespace: "prod-ns", + }, + Spec: corev1.PodSpec{ + Containers: []corev1.Container{ + { + Name: "nginx", + Image: "nginx:1.16", + }, + }, + }, + }, + expectedJobSpec: corev1.PodSpec{ + Affinity: starboard.LinuxNodeAffinity(), + RestartPolicy: corev1.RestartPolicyNever, + ServiceAccountName: "starboard-sa", + AutomountServiceAccountToken: pointer.BoolPtr(false), + Volumes: []corev1.Volume{ + { + Name: "ignorefile", + VolumeSource: corev1.VolumeSource{ + ConfigMap: &corev1.ConfigMapVolumeSource{ + LocalObjectReference: corev1.LocalObjectReference{ + Name: "starboard-trivy-config", + }, + Items: []corev1.KeyToPath{ + { + Key: "trivy.ignoreFile", + Path: ".trivyignore", + }, + }, + }, + }, + }, + }, + Containers: []corev1.Container{ + { + Name: "nginx", + Image: "docker.io/aquasec/trivy:0.14.0", + ImagePullPolicy: corev1.PullIfNotPresent, + TerminationMessagePolicy: corev1.TerminationMessageFallbackToLogsOnError, + Env: []corev1.EnvVar{ + { + Name: "HTTP_PROXY", + ValueFrom: &corev1.EnvVarSource{ + ConfigMapKeyRef: &corev1.ConfigMapKeySelector{ + LocalObjectReference: corev1.LocalObjectReference{ + Name: "starboard-trivy-config", + }, + Key: "trivy.httpProxy", + Optional: pointer.BoolPtr(true), + }, + }, + }, + { + Name: "HTTPS_PROXY", + ValueFrom: &corev1.EnvVarSource{ + ConfigMapKeyRef: &corev1.ConfigMapKeySelector{ + LocalObjectReference: corev1.LocalObjectReference{ + Name: "starboard-trivy-config", + }, + Key: "trivy.httpsProxy", + Optional: pointer.BoolPtr(true), + }, + }, + }, + { + Name: "NO_PROXY", + ValueFrom: &corev1.EnvVarSource{ + ConfigMapKeyRef: &corev1.ConfigMapKeySelector{ + LocalObjectReference: corev1.LocalObjectReference{ + Name: "starboard-trivy-config", + }, + Key: "trivy.noProxy", + Optional: pointer.BoolPtr(true), + }, + }, + }, + { + Name: "TRIVY_SEVERITY", + ValueFrom: &corev1.EnvVarSource{ + ConfigMapKeyRef: &corev1.ConfigMapKeySelector{ + LocalObjectReference: corev1.LocalObjectReference{ + Name: "starboard-trivy-config", + }, + Key: "trivy.severity", + Optional: pointer.BoolPtr(true), + }, + }, + }, + { + Name: "TRIVY_IGNORE_UNFIXED", + ValueFrom: &corev1.EnvVarSource{ + ConfigMapKeyRef: &corev1.ConfigMapKeySelector{ + LocalObjectReference: corev1.LocalObjectReference{ + Name: "starboard-trivy-config", + }, + Key: "trivy.ignoreUnfixed", + Optional: pointer.BoolPtr(true), + }, + }, + }, + + { + Name: "TRIVY_SKIP_FILES", + ValueFrom: &corev1.EnvVarSource{ + ConfigMapKeyRef: &corev1.ConfigMapKeySelector{ + LocalObjectReference: corev1.LocalObjectReference{ + Name: "starboard-trivy-config", + }, + Key: "trivy.skipFiles", + Optional: pointer.BoolPtr(true), + }, + }, + }, + { + Name: "TRIVY_SKIP_DIRS", + ValueFrom: &corev1.EnvVarSource{ + ConfigMapKeyRef: &corev1.ConfigMapKeySelector{ + LocalObjectReference: corev1.LocalObjectReference{ + Name: "starboard-trivy-config", + }, + Key: "trivy.skipDirs", + Optional: pointer.BoolPtr(true), + }, + }, + }, + { + Name: "TRIVY_TOKEN_HEADER", + ValueFrom: &corev1.EnvVarSource{ + ConfigMapKeyRef: &corev1.ConfigMapKeySelector{ + LocalObjectReference: corev1.LocalObjectReference{ + Name: "starboard-trivy-config", + }, + Key: "trivy.serverTokenHeader", + Optional: pointer.BoolPtr(true), + }, + }, + }, + { + Name: "TRIVY_TOKEN", + ValueFrom: &corev1.EnvVarSource{ + SecretKeyRef: &corev1.SecretKeySelector{ + LocalObjectReference: corev1.LocalObjectReference{ + Name: "starboard-trivy-config", + }, + Key: "trivy.serverToken", + Optional: pointer.BoolPtr(true), + }, + }, + }, + { + Name: "TRIVY_CUSTOM_HEADERS", + ValueFrom: &corev1.EnvVarSource{ + SecretKeyRef: &corev1.SecretKeySelector{ + LocalObjectReference: corev1.LocalObjectReference{ + Name: "starboard-trivy-config", + }, + Key: "trivy.serverCustomHeaders", + Optional: pointer.BoolPtr(true), + }, + }, + }, + { + Name: "TRIVY_IGNOREFILE", + Value: "/etc/trivy/.trivyignore", + }, + }, + Command: []string{ + "trivy", + }, + Args: []string{ + "--quiet", + "client", + "--format", + "json", + "--remote", + "http://trivy.trivy:4954", + "nginx:1.16", + }, + Resources: corev1.ResourceRequirements{ + Requests: corev1.ResourceList{ + corev1.ResourceCPU: resource.MustParse("100m"), + corev1.ResourceMemory: resource.MustParse("100M"), + }, + Limits: corev1.ResourceList{ + corev1.ResourceCPU: resource.MustParse("500m"), + corev1.ResourceMemory: resource.MustParse("500M"), + }, + }, + VolumeMounts: []corev1.VolumeMount{ + { + Name: "ignorefile", + MountPath: "/etc/trivy/.trivyignore", + SubPath: ".trivyignore", + }, + }, + }, + }, + }, + }, + { + name: "Trivy fs scan command in Standalone mode", + config: map[string]string{ + "trivy.imageRef": "docker.io/aquasec/trivy:0.25.2", + "trivy.mode": string(trivy.Standalone), + "trivy.command": string(trivy.Filesystem), + "trivy.dbRepository": defaultUpdateURL, + "trivy.resources.requests.cpu": "100m", + "trivy.resources.requests.memory": "100M", + "trivy.resources.limits.cpu": "500m", + "trivy.resources.limits.memory": "500M", + }, + workloadSpec: &corev1.Pod{ + TypeMeta: metav1.TypeMeta{ + Kind: "Pod", + APIVersion: "v1", + }, + ObjectMeta: metav1.ObjectMeta{ + Name: "nginx", + Namespace: "prod-ns", + }, + Spec: corev1.PodSpec{ + Containers: []corev1.Container{ + { + Name: "nginx", + Image: "nginx:1.9.1", + }, + }, + NodeName: "kind-control-pane", + }}, + expectedJobSpec: corev1.PodSpec{ + Affinity: starboard.LinuxNodeAffinity(), + RestartPolicy: corev1.RestartPolicyNever, + ServiceAccountName: "starboard-sa", + AutomountServiceAccountToken: pointer.BoolPtr(false), + Volumes: []corev1.Volume{ + { + Name: trivy.FsSharedVolumeName, + VolumeSource: corev1.VolumeSource{ + EmptyDir: &corev1.EmptyDirVolumeSource{ + Medium: corev1.StorageMediumDefault, + }, + }, + }, + }, + InitContainers: []corev1.Container{ + { + Name: "00000000-0000-0000-0000-000000000001", + Image: "docker.io/aquasec/trivy:0.25.2", + ImagePullPolicy: corev1.PullIfNotPresent, + TerminationMessagePolicy: corev1.TerminationMessageFallbackToLogsOnError, + Command: []string{ + "cp", + "-v", + "/usr/local/bin/trivy", + trivy.SharedVolumeLocationOfTrivy, + }, + Resources: corev1.ResourceRequirements{ + Requests: corev1.ResourceList{ + corev1.ResourceCPU: resource.MustParse("100m"), + corev1.ResourceMemory: resource.MustParse("100M"), + }, + Limits: corev1.ResourceList{ + corev1.ResourceCPU: resource.MustParse("500m"), + corev1.ResourceMemory: resource.MustParse("500M"), + }, + }, + VolumeMounts: []corev1.VolumeMount{ + { + Name: trivy.FsSharedVolumeName, + ReadOnly: false, + MountPath: "/var/starboard", + }, + }, + }, + { + Name: "00000000-0000-0000-0000-000000000002", + Image: "docker.io/aquasec/trivy:0.25.2", + ImagePullPolicy: corev1.PullIfNotPresent, + TerminationMessagePolicy: corev1.TerminationMessageFallbackToLogsOnError, + Env: []corev1.EnvVar{ + { + Name: "HTTP_PROXY", + ValueFrom: &corev1.EnvVarSource{ + ConfigMapKeyRef: &corev1.ConfigMapKeySelector{ + LocalObjectReference: corev1.LocalObjectReference{ + Name: "starboard-trivy-config", + }, + Key: "trivy.httpProxy", + Optional: pointer.BoolPtr(true), + }, + }, + }, + { + Name: "HTTPS_PROXY", + ValueFrom: &corev1.EnvVarSource{ + ConfigMapKeyRef: &corev1.ConfigMapKeySelector{ + LocalObjectReference: corev1.LocalObjectReference{ + Name: "starboard-trivy-config", + }, + Key: "trivy.httpsProxy", + Optional: pointer.BoolPtr(true), + }, + }, + }, + { + Name: "NO_PROXY", + ValueFrom: &corev1.EnvVarSource{ + ConfigMapKeyRef: &corev1.ConfigMapKeySelector{ + LocalObjectReference: corev1.LocalObjectReference{ + Name: "starboard-trivy-config", + }, + Key: "trivy.noProxy", + Optional: pointer.BoolPtr(true), + }, + }, + }, + + { + Name: "GITHUB_TOKEN", + ValueFrom: &corev1.EnvVarSource{ + SecretKeyRef: &corev1.SecretKeySelector{ + LocalObjectReference: corev1.LocalObjectReference{ + Name: "starboard-trivy-config", + }, + Key: "trivy.githubToken", + Optional: pointer.BoolPtr(true), + }, + }, + }, + }, + Command: []string{ + "trivy", + }, + Args: []string{ + "--download-db-only", + "--cache-dir", + "/var/starboard/trivy-db", + "--db-repository", defaultUpdateURL, + }, + Resources: corev1.ResourceRequirements{ + Requests: corev1.ResourceList{ + corev1.ResourceCPU: resource.MustParse("100m"), + corev1.ResourceMemory: resource.MustParse("100M"), + }, + Limits: corev1.ResourceList{ + corev1.ResourceCPU: resource.MustParse("500m"), + corev1.ResourceMemory: resource.MustParse("500M"), + }, + }, + VolumeMounts: []corev1.VolumeMount{ + { + Name: trivy.FsSharedVolumeName, + ReadOnly: false, + MountPath: "/var/starboard", + }, + }, + }, + }, + Containers: []corev1.Container{ + { + Name: "nginx", + Image: "nginx:1.9.1", + ImagePullPolicy: corev1.PullNever, + TerminationMessagePolicy: corev1.TerminationMessageFallbackToLogsOnError, + Env: []corev1.EnvVar{ + { + Name: "TRIVY_SEVERITY", + ValueFrom: &corev1.EnvVarSource{ + ConfigMapKeyRef: &corev1.ConfigMapKeySelector{ + LocalObjectReference: corev1.LocalObjectReference{ + Name: "starboard-trivy-config", + }, + Key: "trivy.severity", + Optional: pointer.BoolPtr(true), + }, + }, + }, + { + Name: "TRIVY_SKIP_FILES", + ValueFrom: &corev1.EnvVarSource{ + ConfigMapKeyRef: &corev1.ConfigMapKeySelector{ + LocalObjectReference: corev1.LocalObjectReference{ + Name: "starboard-trivy-config", + }, + Key: "trivy.skipFiles", + Optional: pointer.BoolPtr(true), + }, + }, + }, + { + Name: "TRIVY_SKIP_DIRS", + ValueFrom: &corev1.EnvVarSource{ + ConfigMapKeyRef: &corev1.ConfigMapKeySelector{ + LocalObjectReference: corev1.LocalObjectReference{ + Name: "starboard-trivy-config", + }, + Key: "trivy.skipDirs", + Optional: pointer.BoolPtr(true), + }, + }, + }, + { + Name: "HTTP_PROXY", + ValueFrom: &corev1.EnvVarSource{ + ConfigMapKeyRef: &corev1.ConfigMapKeySelector{ + LocalObjectReference: corev1.LocalObjectReference{ + Name: "starboard-trivy-config", + }, + Key: "trivy.httpProxy", + Optional: pointer.BoolPtr(true), + }, + }, + }, + { + Name: "HTTPS_PROXY", + ValueFrom: &corev1.EnvVarSource{ + ConfigMapKeyRef: &corev1.ConfigMapKeySelector{ + LocalObjectReference: corev1.LocalObjectReference{ + Name: "starboard-trivy-config", + }, + Key: "trivy.httpsProxy", + Optional: pointer.BoolPtr(true), + }, + }, + }, + { + Name: "NO_PROXY", + ValueFrom: &corev1.EnvVarSource{ + ConfigMapKeyRef: &corev1.ConfigMapKeySelector{ + LocalObjectReference: corev1.LocalObjectReference{ + Name: "starboard-trivy-config", + }, + Key: "trivy.noProxy", + Optional: pointer.BoolPtr(true), + }, + }, + }, + }, + Command: []string{ + trivy.SharedVolumeLocationOfTrivy, + }, + Args: []string{ + "--skip-update", + "--cache-dir", + "/var/starboard/trivy-db", + "--quiet", + "fs", + "--format", + "json", + "/", + }, + Resources: corev1.ResourceRequirements{ + Requests: corev1.ResourceList{ + corev1.ResourceCPU: resource.MustParse("100m"), + corev1.ResourceMemory: resource.MustParse("100M"), + }, + Limits: corev1.ResourceList{ + corev1.ResourceCPU: resource.MustParse("500m"), + corev1.ResourceMemory: resource.MustParse("500M"), + }, + }, + VolumeMounts: []corev1.VolumeMount{ + { + Name: trivy.FsSharedVolumeName, + ReadOnly: false, + MountPath: "/var/starboard", + }, + }, + SecurityContext: &corev1.SecurityContext{ + Privileged: pointer.BoolPtr(false), + AllowPrivilegeEscalation: pointer.BoolPtr(false), + Capabilities: &corev1.Capabilities{ + Drop: []corev1.Capability{"all"}, + }, + ReadOnlyRootFilesystem: pointer.BoolPtr(true), + RunAsUser: pointer.Int64(0), + }, + }, + }, + SecurityContext: &corev1.PodSecurityContext{}, + NodeName: "kind-control-pane", + }, + }, + } + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + fakeclient := fake.NewClientBuilder().WithObjects( + &corev1.ConfigMap{ + ObjectMeta: metav1.ObjectMeta{ + Name: "starboard-trivy-config", + Namespace: "starboard-ns", + }, + Data: tc.config, + }, + ).Build() + pluginContext := starboard.NewPluginContext(). + WithName(grype.Plugin). + WithNamespace("starboard-ns"). + WithServiceAccountName("starboard-sa"). + WithClient(fakeclient). + Get() + instance := trivy.NewPlugin(fixedClock, ext.NewSimpleIDGenerator(), fakeclient) + jobSpec, secrets, err := instance.GetScanJobSpec(pluginContext, tc.workloadSpec, nil) + require.NoError(t, err) + assert.Empty(t, secrets) + assert.Equal(t, tc.expectedJobSpec, jobSpec) + }) + } + + testCases = []struct { + name string + config map[string]string + workloadSpec client.Object + expectedSecrets []corev1.Secret + expectedJobSpec corev1.PodSpec + }{{ + name: "Trivy fs scan command in Standalone mode", + config: map[string]string{ + "trivy.imageRef": "docker.io/aquasec/trivy:0.22.0", + "trivy.mode": string(trivy.Standalone), + "trivy.command": string(trivy.Filesystem), + "trivy.dbRepository": defaultUpdateURL, + "trivy.resources.requests.cpu": "100m", + "trivy.resources.requests.memory": "100M", + "trivy.resources.limits.cpu": "500m", + "trivy.resources.limits.memory": "500M", + }, + workloadSpec: &corev1.Pod{ + TypeMeta: metav1.TypeMeta{ + Kind: "Pod", + APIVersion: "v1", + }, + ObjectMeta: metav1.ObjectMeta{ + Name: "nginx", + Namespace: "prod-ns", + }, + Spec: corev1.PodSpec{ + Containers: []corev1.Container{ + { + Name: "nginx", + Image: "nginx:1.9.1", + }, + }, + NodeName: "kind-control-pane", + ServiceAccountName: "nginx-sa", + }}, + expectedJobSpec: corev1.PodSpec{ + Affinity: starboard.LinuxNodeAffinity(), + RestartPolicy: corev1.RestartPolicyNever, + ServiceAccountName: "starboard-sa", + AutomountServiceAccountToken: pointer.BoolPtr(false), + Volumes: []corev1.Volume{ + { + Name: trivy.FsSharedVolumeName, + VolumeSource: corev1.VolumeSource{ + EmptyDir: &corev1.EmptyDirVolumeSource{ + Medium: corev1.StorageMediumDefault, + }, + }, + }, + }, + InitContainers: []corev1.Container{ + { + Name: "00000000-0000-0000-0000-000000000001", + Image: "docker.io/aquasec/trivy:0.22.0", + ImagePullPolicy: corev1.PullIfNotPresent, + TerminationMessagePolicy: corev1.TerminationMessageFallbackToLogsOnError, + Command: []string{ + "cp", + "-v", + "/usr/local/bin/trivy", + trivy.SharedVolumeLocationOfTrivy, + }, + Resources: corev1.ResourceRequirements{ + Requests: corev1.ResourceList{ + corev1.ResourceCPU: resource.MustParse("100m"), + corev1.ResourceMemory: resource.MustParse("100M"), + }, + Limits: corev1.ResourceList{ + corev1.ResourceCPU: resource.MustParse("500m"), + corev1.ResourceMemory: resource.MustParse("500M"), + }, + }, + VolumeMounts: []corev1.VolumeMount{ + { + Name: trivy.FsSharedVolumeName, + ReadOnly: false, + MountPath: "/var/starboard", + }, + }, + }, + { + Name: "00000000-0000-0000-0000-000000000002", + Image: "docker.io/aquasec/trivy:0.22.0", + ImagePullPolicy: corev1.PullIfNotPresent, + TerminationMessagePolicy: corev1.TerminationMessageFallbackToLogsOnError, + Env: []corev1.EnvVar{ + { + Name: "HTTP_PROXY", + ValueFrom: &corev1.EnvVarSource{ + ConfigMapKeyRef: &corev1.ConfigMapKeySelector{ + LocalObjectReference: corev1.LocalObjectReference{ + Name: "starboard-trivy-config", + }, + Key: "trivy.httpProxy", + Optional: pointer.BoolPtr(true), + }, + }, + }, + { + Name: "HTTPS_PROXY", + ValueFrom: &corev1.EnvVarSource{ + ConfigMapKeyRef: &corev1.ConfigMapKeySelector{ + LocalObjectReference: corev1.LocalObjectReference{ + Name: "starboard-trivy-config", + }, + Key: "trivy.httpsProxy", + Optional: pointer.BoolPtr(true), + }, + }, + }, + { + Name: "NO_PROXY", + ValueFrom: &corev1.EnvVarSource{ + ConfigMapKeyRef: &corev1.ConfigMapKeySelector{ + LocalObjectReference: corev1.LocalObjectReference{ + Name: "starboard-trivy-config", + }, + Key: "trivy.noProxy", + Optional: pointer.BoolPtr(true), + }, + }, + }, + + { + Name: "GITHUB_TOKEN", + ValueFrom: &corev1.EnvVarSource{ + SecretKeyRef: &corev1.SecretKeySelector{ + LocalObjectReference: corev1.LocalObjectReference{ + Name: "starboard-trivy-config", + }, + Key: "trivy.githubToken", + Optional: pointer.BoolPtr(true), + }, + }, + }, + }, + Command: []string{ + "trivy", + }, + Args: []string{ + "--download-db-only", + "--cache-dir", + "/var/starboard/trivy-db", + "--db-repository", defaultUpdateURL, + }, + Resources: corev1.ResourceRequirements{ + Requests: corev1.ResourceList{ + corev1.ResourceCPU: resource.MustParse("100m"), + corev1.ResourceMemory: resource.MustParse("100M"), + }, + Limits: corev1.ResourceList{ + corev1.ResourceCPU: resource.MustParse("500m"), + corev1.ResourceMemory: resource.MustParse("500M"), + }, + }, + VolumeMounts: []corev1.VolumeMount{ + { + Name: trivy.FsSharedVolumeName, + ReadOnly: false, + MountPath: "/var/starboard", + }, + }, + }, + }, + Containers: []corev1.Container{ + { + Name: "nginx", + Image: "nginx:1.9.1", + ImagePullPolicy: corev1.PullIfNotPresent, + TerminationMessagePolicy: corev1.TerminationMessageFallbackToLogsOnError, + Env: []corev1.EnvVar{ + { + Name: "TRIVY_SEVERITY", + ValueFrom: &corev1.EnvVarSource{ + ConfigMapKeyRef: &corev1.ConfigMapKeySelector{ + LocalObjectReference: corev1.LocalObjectReference{ + Name: "starboard-trivy-config", + }, + Key: "trivy.severity", + Optional: pointer.BoolPtr(true), + }, + }, + }, + { + Name: "TRIVY_SKIP_FILES", + ValueFrom: &corev1.EnvVarSource{ + ConfigMapKeyRef: &corev1.ConfigMapKeySelector{ + LocalObjectReference: corev1.LocalObjectReference{ + Name: "starboard-trivy-config", + }, + Key: "trivy.skipFiles", + Optional: pointer.BoolPtr(true), + }, + }, + }, + { + Name: "TRIVY_SKIP_DIRS", + ValueFrom: &corev1.EnvVarSource{ + ConfigMapKeyRef: &corev1.ConfigMapKeySelector{ + LocalObjectReference: corev1.LocalObjectReference{ + Name: "starboard-trivy-config", + }, + Key: "trivy.skipDirs", + Optional: pointer.BoolPtr(true), + }, + }, + }, + { + Name: "HTTP_PROXY", + ValueFrom: &corev1.EnvVarSource{ + ConfigMapKeyRef: &corev1.ConfigMapKeySelector{ + LocalObjectReference: corev1.LocalObjectReference{ + Name: "starboard-trivy-config", + }, + Key: "trivy.httpProxy", + Optional: pointer.BoolPtr(true), + }, + }, + }, + { + Name: "HTTPS_PROXY", + ValueFrom: &corev1.EnvVarSource{ + ConfigMapKeyRef: &corev1.ConfigMapKeySelector{ + LocalObjectReference: corev1.LocalObjectReference{ + Name: "starboard-trivy-config", + }, + Key: "trivy.httpsProxy", + Optional: pointer.BoolPtr(true), + }, + }, + }, + { + Name: "NO_PROXY", + ValueFrom: &corev1.EnvVarSource{ + ConfigMapKeyRef: &corev1.ConfigMapKeySelector{ + LocalObjectReference: corev1.LocalObjectReference{ + Name: "starboard-trivy-config", + }, + Key: "trivy.noProxy", + Optional: pointer.BoolPtr(true), + }, + }, + }, + }, + Command: []string{ + trivy.SharedVolumeLocationOfTrivy, + }, + Args: []string{ + "--skip-update", + "--cache-dir", + "/var/starboard/trivy-db", + "--quiet", + "fs", + "--format", + "json", + "/", + }, + Resources: corev1.ResourceRequirements{ + Requests: corev1.ResourceList{ + corev1.ResourceCPU: resource.MustParse("100m"), + corev1.ResourceMemory: resource.MustParse("100M"), + }, + Limits: corev1.ResourceList{ + corev1.ResourceCPU: resource.MustParse("500m"), + corev1.ResourceMemory: resource.MustParse("500M"), + }, + }, + VolumeMounts: []corev1.VolumeMount{ + { + Name: trivy.FsSharedVolumeName, + ReadOnly: false, + MountPath: "/var/starboard", + }, + }, + SecurityContext: &corev1.SecurityContext{ + Privileged: pointer.BoolPtr(false), + AllowPrivilegeEscalation: pointer.BoolPtr(false), + Capabilities: &corev1.Capabilities{ + Drop: []corev1.Capability{"all"}, + }, + ReadOnlyRootFilesystem: pointer.BoolPtr(true), + RunAsUser: pointer.Int64(0), + }, + }, + }, + SecurityContext: &corev1.PodSecurityContext{}, + }, + }} + // Test cases when starboard is enabled with option to run job in the namespace of workload + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + fakeclient := fake.NewClientBuilder().WithObjects( + &corev1.ConfigMap{ + ObjectMeta: metav1.ObjectMeta{ + Name: "starboard-trivy-config", + Namespace: "starboard-ns", + }, + Data: tc.config, + }, + ).Build() + pluginContext := starboard.NewPluginContext(). + WithName(trivy.Plugin). + WithNamespace("starboard-ns"). + WithServiceAccountName("starboard-sa"). + WithClient(fakeclient). + WithStarboardConfig(map[string]string{starboard.KeyVulnerabilityScansInSameNamespace: "true"}). + Get() + instance := trivy.NewPlugin(fixedClock, ext.NewSimpleIDGenerator(), fakeclient) + jobSpec, secrets, err := instance.GetScanJobSpec(pluginContext, tc.workloadSpec, nil) + require.NoError(t, err) + assert.Empty(t, secrets) + assert.Equal(t, tc.expectedJobSpec, jobSpec) + }) + } +} + +var ( + sampleReportAsString = `{ + "matches": [ + { + "vulnerability": { + "id": "CVE-2015-5237", + "dataSource": "https://nvd.nist.gov/vuln/detail/CVE-2015-5237", + "namespace": "nvd", + "severity": "High", + "urls": [ + "https://github.com/google/protobuf/issues/760", + "https://bugzilla.redhat.com/show_bug.cgi?id=1256426", + "http://www.openwall.com/lists/oss-security/2015/08/27/2", + "https://lists.apache.org/thread.html/b0656d359c7d40ec9f39c8cc61bca66802ef9a2a12ee199f5b0c1442@%3Cdev.drill.apache.org%3E", + "https://lists.apache.org/thread.html/519eb0fd45642dcecd9ff74cb3e71c20a4753f7d82e2f07864b5108f@%3Cdev.drill.apache.org%3E", + "https://lists.apache.org/thread.html/ra28fed69eef3a71e5fe5daea001d0456b05b102044237330ec5c7c82@%3Ccommits.pulsar.apache.org%3E", + "https://lists.apache.org/thread.html/f9bc3e55f4e28d1dcd1a69aae6d53e609a758e34d2869b4d798e13cc@%3Cissues.drill.apache.org%3E", + "https://lists.apache.org/thread.html/r17dc6f394429f6bffb5e4c66555d93c2e9923cbbdc5a93db9a56c1c7@%3Ccommits.pulsar.apache.org%3E", + "https://lists.apache.org/thread.html/r42e47994734cd1980ef3e204a40555336e10cc80096927aca2f37d90@%3Ccommits.pulsar.apache.org%3E", + "https://lists.apache.org/thread.html/re6d04a214424a97ea59c62190d79316edf311a0a6346524dfef3b940@%3Ccommits.pulsar.apache.org%3E", + "https://lists.apache.org/thread.html/r1263fa5b51e4ec3cb8f09ff40e4747428c71198e9bee93349ec96a3c@%3Ccommits.pulsar.apache.org%3E", + "https://lists.apache.org/thread.html/r42ef6acfb0d86a2df0c2390702ecbe97d2104a331560f2790d17ca69@%3Ccommits.pulsar.apache.org%3E", + "https://lists.apache.org/thread.html/rb71dac1d9dd4e8a8ae3dbc033aeae514eda9be1263c1df3b42a530a2@%3Ccommits.pulsar.apache.org%3E", + "https://lists.apache.org/thread.html/r320dc858da88846ba00bb077bcca2cdf75b7dde0f6eb3a3d60dba6a1@%3Ccommits.pulsar.apache.org%3E", + "https://lists.apache.org/thread.html/r85c9a764b573c786224688cc906c27e28343e18f5b33387f94cae90f@%3Ccommits.pulsar.apache.org%3E", + "https://lists.apache.org/thread.html/r02e39d7beb32eebcdbb4b516e95f67d71c90d5d462b26f4078d21eeb@%3Cuser.flink.apache.org%3E", + "https://lists.apache.org/thread.html/r02e39d7beb32eebcdbb4b516e95f67d71c90d5d462b26f4078d21eeb@%3Cdev.flink.apache.org%3E", + "https://lists.apache.org/thread.html/r5e52caf41dc49df55b4ee80758356fe1ff2a88179ff24c685de7c28d@%3Ccommits.pulsar.apache.org%3E", + "https://lists.apache.org/thread.html/rf7539287c90be979bac94af9aaba34118fbf968864944b4871af48dd@%3Ccommits.pulsar.apache.org%3E", + "https://lists.apache.org/thread.html/r1d274d647b3c2060df9be21eade4ce56d3a59998cf19ac72662dd994@%3Ccommits.pulsar.apache.org%3E", + "https://lists.apache.org/thread.html/r4886108206d4c535db9b20c813fe4723d4fe6a91b9278382af8b9d08@%3Cissues.spark.apache.org%3E", + "https://lists.apache.org/thread.html/rb40dc9d63a5331bce8e80865b7fa3af9dd31e16555affd697b6f3526@%3Cissues.spark.apache.org%3E", + "https://lists.apache.org/thread.html/r5741f4dbdd129dbb9885f5fb170dc1b24a06b9313bedef5e67fded94@%3Cissues.spark.apache.org%3E", + "https://lists.apache.org/thread.html/r14fa8d38d5757254f1a2e112270c996711d514de2e3b01c93d397ab4@%3Cissues.spark.apache.org%3E", + "https://lists.apache.org/thread.html/r2ea33ce5591a9cb9ed52750b6ab42ab658f529a7028c3166ba93c7d5@%3Ccommon-issues.hadoop.apache.org%3E", + "https://lists.apache.org/thread.html/r00d9ab1fc0f1daf14cd4386564dd84f7889404438d81462c86dfa836@%3Ccommon-dev.hadoop.apache.org%3E", + "https://lists.apache.org/thread.html/r764fc66435ee4d185d359c28c0887d3e5866d7292a8d5598d9e7cbc4@%3Ccommon-issues.hadoop.apache.org%3E", + "https://lists.apache.org/thread.html/r0ca83171c4898dc92b86fa6f484a7be1dc96206765f4d01dce0f1b28@%3Ccommon-issues.hadoop.apache.org%3E", + "https://lists.apache.org/thread.html/r00097d0b5b6164ea428554007121d5dc1f88ba2af7b9e977a10572cd@%3Cdev.hbase.apache.org%3E", + "https://lists.apache.org/thread.html/rd64381fb8f92d640c1975dc50dcdf1b8512e02a2a7b20292d3565cae@%3Cissues.hbase.apache.org%3E", + "https://lists.apache.org/thread.html/r4ef574a5621b0e670a3ce641e9922543e34f22bf4c9ee9584aa67fcf@%3Cissues.hbase.apache.org%3E", + "https://lists.apache.org/thread.html/r7fed8dd9bee494094e7011cf3c2ab75bd8754ea314c6734688c42932@%3Ccommon-issues.hadoop.apache.org%3E" + ], + "description": "protobuf allows remote authenticated attackers to cause a heap-based buffer overflow.", + "cvss": [ + { + "version": "2.0", + "vector": "AV:N/AC:L/Au:S/C:P/I:P/A:P", + "metrics": { + "baseScore": 6.5, + "exploitabilityScore": 8, + "impactScore": 6.4 + }, + "vendorMetadata": {} + }, + { + "version": "3.1", + "vector": "CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:H", + "metrics": { + "baseScore": 8.8, + "exploitabilityScore": 2.8, + "impactScore": 5.9 + }, + "vendorMetadata": {} + } + ], + "fix": { + "versions": [], + "state": "unknown" + }, + "advisories": [] + }, + "relatedVulnerabilities": [], + "matchDetails": [ + { + "type": "cpe-match", + "matcher": "stock-matcher", + "searchedBy": { + "namespace": "nvd", + "cpes": [ + "cpe:2.3:a:google:protobuf:v1.27.1:*:*:*:*:*:*:*" + ] + }, + "found": { + "versionConstraint": "<= 3.1.0 (unknown)", + "cpes": [ + "cpe:2.3:a:google:protobuf:*:*:*:*:*:*:*:*" + ] + } + } + ], + "artifact": { + "name": "google.golang.org/protobuf", + "version": "v1.27.1", + "type": "go-module", + "locations": [ + { + "path": "/grype", + "layerID": "sha256:65ea08da2c3941d8571059e12f7c48821150a89ba662c9751acf5d3664df3f86" + } + ], + "language": "go", + "licenses": [], + "cpes": [ + "cpe:2.3:a:google:protobuf:v1.27.1:*:*:*:*:*:*:*" + ], + "purl": "pkg:golang/google.golang.org/protobuf@v1.27.1", + "upstreams": [] + } + }, + { + "vulnerability": { + "id": "CVE-2021-22570", + "dataSource": "https://nvd.nist.gov/vuln/detail/CVE-2021-22570", + "namespace": "nvd", + "severity": "High", + "urls": [ + "https://github.com/protocolbuffers/protobuf/releases/tag/v3.15.0", + "https://lists.fedoraproject.org/archives/list/package-announce@lists.fedoraproject.org/message/IFX6KPNOFHYD6L4XES5PCM3QNSKZBOTQ/", + "https://lists.fedoraproject.org/archives/list/package-announce@lists.fedoraproject.org/message/3DVUZPALAQ34TQP6KFNLM4IZS6B32XSA/", + "https://lists.fedoraproject.org/archives/list/package-announce@lists.fedoraproject.org/message/BTRGBRC5KGCA4SK5MUNLPYJRAGXMBIYY/", + "https://lists.fedoraproject.org/archives/list/package-announce@lists.fedoraproject.org/message/NVTWVQRB5OCCTMKEQFY5MYED3DXDVSLP/", + "https://lists.fedoraproject.org/archives/list/package-announce@lists.fedoraproject.org/message/5PAGL5M2KGYPN3VEQCRJJE6NA7D5YG5X/", + "https://lists.fedoraproject.org/archives/list/package-announce@lists.fedoraproject.org/message/KQJB6ZPRLKV6WCMX2PRRRQBFAOXFBK6B/", + "https://lists.fedoraproject.org/archives/list/package-announce@lists.fedoraproject.org/message/MRWRAXAFR3JR7XCFWTHC2KALSZKWACCE/" + ], + "description": "Nullptr dereference when a null char is present in a proto symbol. The symbol is parsed incorrectly, leading to an unchecked call into the proto file's name during generation of the resulting error message. Since the symbol is incorrectly parsed, the file is nullptr. We recommend upgrading to version 3.15.0 or greater.", + "cvss": [ + { + "version": "2.0", + "vector": "AV:N/AC:L/Au:N/C:N/I:N/A:P", + "metrics": { + "baseScore": 5, + "exploitabilityScore": 10, + "impactScore": 2.9 + }, + "vendorMetadata": {} + }, + { + "version": "3.1", + "vector": "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H", + "metrics": { + "baseScore": 7.5, + "exploitabilityScore": 3.9, + "impactScore": 3.6 + }, + "vendorMetadata": {} + } + ], + "fix": { + "versions": [], + "state": "unknown" + }, + "advisories": [] + }, + "relatedVulnerabilities": [], + "matchDetails": [ + { + "type": "cpe-match", + "matcher": "stock-matcher", + "searchedBy": { + "namespace": "nvd", + "cpes": [ + "cpe:2.3:a:google:protobuf:v1.27.1:*:*:*:*:*:*:*" + ] + }, + "found": { + "versionConstraint": "< 3.15.0 (unknown)", + "cpes": [ + "cpe:2.3:a:google:protobuf:*:*:*:*:*:*:*:*" + ] + } + } + ], + "artifact": { + "name": "google.golang.org/protobuf", + "version": "v1.27.1", + "type": "go-module", + "locations": [ + { + "path": "/grype", + "layerID": "sha256:65ea08da2c3941d8571059e12f7c48821150a89ba662c9751acf5d3664df3f86" + } + ], + "language": "go", + "licenses": [], + "cpes": [ + "cpe:2.3:a:google:protobuf:v1.27.1:*:*:*:*:*:*:*" + ], + "purl": "pkg:golang/google.golang.org/protobuf@v1.27.1", + "upstreams": [] + } + } + ], + "source": { + "type": "image", + "target": { + "userInput": "anchore/grype", + "imageID": "sha256:a28988cab42062d638840c4e5d5e0c46ba6dbea526a6a3858d461c50554b2795", + "manifestDigest": "sha256:e3f50502292a86fc8f188336153c2e0ff9216bdf2df63c4e0323d6e163210454", + "mediaType": "application/vnd.docker.distribution.manifest.v2+json", + "tags": [], + "imageSize": 29665751, + "layers": [ + { + "mediaType": "application/vnd.docker.image.rootfs.diff.tar.gzip", + "digest": "sha256:b7574c4c31a87c8530c59277dfef86aac806cf6833f862833959df6b04e996d1", + "size": 203223 + }, + { + "mediaType": "application/vnd.docker.image.rootfs.diff.tar.gzip", + "digest": "sha256:61857988458c05660c02dc985b7dfdff0e8083d44829a546f2a0b411354ab631", + "size": 0 + }, + { + "mediaType": "application/vnd.docker.image.rootfs.diff.tar.gzip", + "digest": "sha256:65ea08da2c3941d8571059e12f7c48821150a89ba662c9751acf5d3664df3f86", + "size": 29462528 + } + ], + "manifest": "", + "config": "", + "repoDigests": [ + "index.docker.io/anchore/grype@sha256:c33bd4cbedcc4ef190abe2f24c4f70ad6ddece0ec40451415efa1dea99f3a421" + ], + "architecture": "", + "os": "" + } + }, + "distro": { + "name": "", + "version": "", + "idLike": null + }, + "descriptor": { + "name": "grype", + "version": "0.34.7", + "configuration": { + "configPath": "", + "output": "json", + "file": "", + "distro": "", + "add-cpes-if-none": false, + "output-template-file": "", + "quiet": false, + "check-for-app-update": true, + "only-fixed": false, + "platform": "", + "search": { + "scope": "Squashed", + "unindexed-archives": false, + "indexed-archives": true + }, + "ignore": null, + "exclude": [], + "db": { + "cache-dir": "/.cache/grype/db", + "update-url": "https://toolbox-data.anchore.io/grype/databases/listing.json", + "ca-cert": "", + "auto-update": true, + "validate-by-hash-on-start": false + }, + "dev": { + "profile-cpu": false, + "profile-mem": false + }, + "fail-on-severity": "", + "registry": { + "insecure-skip-tls-verify": false, + "insecure-use-http": false, + "auth": [] + }, + "log": { + "structured": false, + "level": "", + "file": "" + } + }, + "db": { + "built": "2022-04-11T08:15:09Z", + "schemaVersion": 3, + "location": "/.cache/grype/db/3", + "checksum": "sha256:7a2e9977d84257c4aa0010ab1e256ea31e5125cb9c567926440703e65cf463b9", + "error": null + } + } + } ` + + sampleReport = v1alpha1.VulnerabilityReportData{ + UpdateTimestamp: metav1.NewTime(fixedTime), + Scanner: v1alpha1.Scanner{ + Name: "Grype", + Vendor: "Anchore Inc.", + Version: "0.34.7", + }, + Registry: v1alpha1.Registry{ + Server: "index.docker.io", + }, + Artifact: v1alpha1.Artifact{ + Repository: "anchore/grype", + Tag: "latest", + }, + Summary: v1alpha1.VulnerabilitySummary{ + CriticalCount: 0, + HighCount: 2, + MediumCount: 0, + LowCount: 0, + NoneCount: 0, + UnknownCount: 0, + }, + Vulnerabilities: []v1alpha1.Vulnerability{ + { + VulnerabilityID: "CVE-2015-5237", + Resource: "google.golang.org/protobuf", + InstalledVersion: "v1.27.1", + FixedVersion: "", + Severity: v1alpha1.SeverityHigh, + Title: "google.golang.org/protobuf", + Description: "protobuf allows remote authenticated attackers to cause a heap-based buffer overflow.", + PrimaryLink: "https://nvd.nist.gov/vuln/detail/CVE-2015-5237", + Score: pointer.Float64(8.8), + Links: []string{ + "https://github.com/google/protobuf/issues/760", + "https://bugzilla.redhat.com/show_bug.cgi?id=1256426", + "http://www.openwall.com/lists/oss-security/2015/08/27/2", + "https://lists.apache.org/thread.html/b0656d359c7d40ec9f39c8cc61bca66802ef9a2a12ee199f5b0c1442@%3Cdev.drill.apache.org%3E", + "https://lists.apache.org/thread.html/519eb0fd45642dcecd9ff74cb3e71c20a4753f7d82e2f07864b5108f@%3Cdev.drill.apache.org%3E", + "https://lists.apache.org/thread.html/ra28fed69eef3a71e5fe5daea001d0456b05b102044237330ec5c7c82@%3Ccommits.pulsar.apache.org%3E", + "https://lists.apache.org/thread.html/f9bc3e55f4e28d1dcd1a69aae6d53e609a758e34d2869b4d798e13cc@%3Cissues.drill.apache.org%3E", + "https://lists.apache.org/thread.html/r17dc6f394429f6bffb5e4c66555d93c2e9923cbbdc5a93db9a56c1c7@%3Ccommits.pulsar.apache.org%3E", + "https://lists.apache.org/thread.html/r42e47994734cd1980ef3e204a40555336e10cc80096927aca2f37d90@%3Ccommits.pulsar.apache.org%3E", + "https://lists.apache.org/thread.html/re6d04a214424a97ea59c62190d79316edf311a0a6346524dfef3b940@%3Ccommits.pulsar.apache.org%3E", + "https://lists.apache.org/thread.html/r1263fa5b51e4ec3cb8f09ff40e4747428c71198e9bee93349ec96a3c@%3Ccommits.pulsar.apache.org%3E", + "https://lists.apache.org/thread.html/r42ef6acfb0d86a2df0c2390702ecbe97d2104a331560f2790d17ca69@%3Ccommits.pulsar.apache.org%3E", + "https://lists.apache.org/thread.html/rb71dac1d9dd4e8a8ae3dbc033aeae514eda9be1263c1df3b42a530a2@%3Ccommits.pulsar.apache.org%3E", + "https://lists.apache.org/thread.html/r320dc858da88846ba00bb077bcca2cdf75b7dde0f6eb3a3d60dba6a1@%3Ccommits.pulsar.apache.org%3E", + "https://lists.apache.org/thread.html/r85c9a764b573c786224688cc906c27e28343e18f5b33387f94cae90f@%3Ccommits.pulsar.apache.org%3E", + "https://lists.apache.org/thread.html/r02e39d7beb32eebcdbb4b516e95f67d71c90d5d462b26f4078d21eeb@%3Cuser.flink.apache.org%3E", + "https://lists.apache.org/thread.html/r02e39d7beb32eebcdbb4b516e95f67d71c90d5d462b26f4078d21eeb@%3Cdev.flink.apache.org%3E", + "https://lists.apache.org/thread.html/r5e52caf41dc49df55b4ee80758356fe1ff2a88179ff24c685de7c28d@%3Ccommits.pulsar.apache.org%3E", + "https://lists.apache.org/thread.html/rf7539287c90be979bac94af9aaba34118fbf968864944b4871af48dd@%3Ccommits.pulsar.apache.org%3E", + "https://lists.apache.org/thread.html/r1d274d647b3c2060df9be21eade4ce56d3a59998cf19ac72662dd994@%3Ccommits.pulsar.apache.org%3E", + "https://lists.apache.org/thread.html/r4886108206d4c535db9b20c813fe4723d4fe6a91b9278382af8b9d08@%3Cissues.spark.apache.org%3E", + "https://lists.apache.org/thread.html/rb40dc9d63a5331bce8e80865b7fa3af9dd31e16555affd697b6f3526@%3Cissues.spark.apache.org%3E", + "https://lists.apache.org/thread.html/r5741f4dbdd129dbb9885f5fb170dc1b24a06b9313bedef5e67fded94@%3Cissues.spark.apache.org%3E", + "https://lists.apache.org/thread.html/r14fa8d38d5757254f1a2e112270c996711d514de2e3b01c93d397ab4@%3Cissues.spark.apache.org%3E", + "https://lists.apache.org/thread.html/r2ea33ce5591a9cb9ed52750b6ab42ab658f529a7028c3166ba93c7d5@%3Ccommon-issues.hadoop.apache.org%3E", + "https://lists.apache.org/thread.html/r00d9ab1fc0f1daf14cd4386564dd84f7889404438d81462c86dfa836@%3Ccommon-dev.hadoop.apache.org%3E", + "https://lists.apache.org/thread.html/r764fc66435ee4d185d359c28c0887d3e5866d7292a8d5598d9e7cbc4@%3Ccommon-issues.hadoop.apache.org%3E", + "https://lists.apache.org/thread.html/r0ca83171c4898dc92b86fa6f484a7be1dc96206765f4d01dce0f1b28@%3Ccommon-issues.hadoop.apache.org%3E", + "https://lists.apache.org/thread.html/r00097d0b5b6164ea428554007121d5dc1f88ba2af7b9e977a10572cd@%3Cdev.hbase.apache.org%3E", + "https://lists.apache.org/thread.html/rd64381fb8f92d640c1975dc50dcdf1b8512e02a2a7b20292d3565cae@%3Cissues.hbase.apache.org%3E", + "https://lists.apache.org/thread.html/r4ef574a5621b0e670a3ce641e9922543e34f22bf4c9ee9584aa67fcf@%3Cissues.hbase.apache.org%3E", + "https://lists.apache.org/thread.html/r7fed8dd9bee494094e7011cf3c2ab75bd8754ea314c6734688c42932@%3Ccommon-issues.hadoop.apache.org%3E", + }, + }, + { + VulnerabilityID: "CVE-2021-22570", + Resource: "google.golang.org/protobuf", + InstalledVersion: "v1.27.1", + FixedVersion: "", + Severity: v1alpha1.SeverityHigh, + Title: "google.golang.org/protobuf", + Description: "Nullptr dereference when a null char is present in a proto symbol. The symbol is parsed incorrectly, leading to an unchecked call into the proto file's name during generation of the resulting error message. Since the symbol is incorrectly parsed, the file is nullptr. We recommend upgrading to version 3.15.0 or greater.", + PrimaryLink: "https://nvd.nist.gov/vuln/detail/CVE-2021-22570", + Score: pointer.Float64(7.5), + Links: []string{ + "https://github.com/protocolbuffers/protobuf/releases/tag/v3.15.0", + "https://lists.fedoraproject.org/archives/list/package-announce@lists.fedoraproject.org/message/IFX6KPNOFHYD6L4XES5PCM3QNSKZBOTQ/", + "https://lists.fedoraproject.org/archives/list/package-announce@lists.fedoraproject.org/message/3DVUZPALAQ34TQP6KFNLM4IZS6B32XSA/", + "https://lists.fedoraproject.org/archives/list/package-announce@lists.fedoraproject.org/message/BTRGBRC5KGCA4SK5MUNLPYJRAGXMBIYY/", + "https://lists.fedoraproject.org/archives/list/package-announce@lists.fedoraproject.org/message/NVTWVQRB5OCCTMKEQFY5MYED3DXDVSLP/", + "https://lists.fedoraproject.org/archives/list/package-announce@lists.fedoraproject.org/message/5PAGL5M2KGYPN3VEQCRJJE6NA7D5YG5X/", + "https://lists.fedoraproject.org/archives/list/package-announce@lists.fedoraproject.org/message/KQJB6ZPRLKV6WCMX2PRRRQBFAOXFBK6B/", + "https://lists.fedoraproject.org/archives/list/package-announce@lists.fedoraproject.org/message/MRWRAXAFR3JR7XCFWTHC2KALSZKWACCE/", + }, + }, + }, + } +) + +func TestPlugin_ParseVulnerabilityReportData(t *testing.T) { + config := &corev1.ConfigMap{ + ObjectMeta: metav1.ObjectMeta{ + Name: "starboard-grype-config", + Namespace: "starboard-ns", + }, + Data: map[string]string{ + "grype.imageRef": "anchore/grype:0.34.7", + }, + } + + testCases := []struct { + name string + imageRef string + input string + expectedError error + expectedReport v1alpha1.VulnerabilityReportData + }{ + { + name: "Should convert vulnerability report in JSON format when input is quiet", + imageRef: "anchore/grype", + input: sampleReportAsString, + expectedError: nil, + expectedReport: sampleReport, + }, + { + name: "Should convert vulnerability report in JSON format when OS is not detected", + imageRef: "core.harbor.domain/library/nginx@sha256:d20aa6d1cae56fd17cd458f4807e0de462caf2336f0b70b5eeb69fcaaf30dd9c", + input: `null`, + expectedError: nil, + expectedReport: v1alpha1.VulnerabilityReportData{ + UpdateTimestamp: metav1.NewTime(fixedTime), + Scanner: v1alpha1.Scanner{ + Name: "Grype", + Vendor: "Anchore Inc.", + Version: "0.34.7", + }, + Registry: v1alpha1.Registry{ + Server: "core.harbor.domain", + }, + Artifact: v1alpha1.Artifact{ + Repository: "library/nginx", + Digest: "sha256:d20aa6d1cae56fd17cd458f4807e0de462caf2336f0b70b5eeb69fcaaf30dd9c", + }, + Summary: v1alpha1.VulnerabilitySummary{ + CriticalCount: 0, + HighCount: 0, + MediumCount: 0, + LowCount: 0, + NoneCount: 0, + UnknownCount: 0, + }, + Vulnerabilities: []v1alpha1.Vulnerability{}, + }, + }, + { + name: "Should return error when image reference cannot be parsed", + imageRef: ":", + input: "null", + expectedError: errors.New("could not parse reference: :"), + }, + } + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + fakeClient := fake.NewClientBuilder().WithObjects(config).Build() + ctx := starboard.NewPluginContext(). + WithName("Grype"). + WithNamespace("starboard-ns"). + WithServiceAccountName("starboard-sa"). + WithClient(fakeClient). + Get() + instance := grype.NewPlugin(fixedClock, ext.NewSimpleIDGenerator(), fakeClient) + report, err := instance.ParseVulnerabilityReportData(ctx, tc.imageRef, io.NopCloser(strings.NewReader(tc.input))) + switch { + case tc.expectedError == nil: + require.NoError(t, err) + assert.Equal(t, tc.expectedReport, report) + default: + assert.EqualError(t, err, tc.expectedError.Error()) + } + }) + } + +} From cc3babfa2106ff7b3c94aa920b42eca029c6495c Mon Sep 17 00:00:00 2001 From: Kevin Seidel Date: Wed, 20 Apr 2022 10:58:53 +0200 Subject: [PATCH 02/21] basic grype plugin with essential features unit tested --- pkg/plugin/grype/doc.go | 2 +- pkg/plugin/grype/model_test.go | 1 + pkg/plugin/grype/plugin.go | 41 +- pkg/plugin/grype/plugin_test.go | 3163 ++----------------------------- 4 files changed, 229 insertions(+), 2978 deletions(-) create mode 100644 pkg/plugin/grype/model_test.go diff --git a/pkg/plugin/grype/doc.go b/pkg/plugin/grype/doc.go index 980b6af17..3c6f6f09a 100644 --- a/pkg/plugin/grype/doc.go +++ b/pkg/plugin/grype/doc.go @@ -1,2 +1,2 @@ -// Package grype provides primitives for working with Trivy. +// Package grype provides primitives for working with Grype. package grype diff --git a/pkg/plugin/grype/model_test.go b/pkg/plugin/grype/model_test.go new file mode 100644 index 000000000..eb96e9fbb --- /dev/null +++ b/pkg/plugin/grype/model_test.go @@ -0,0 +1 @@ +package grype_test diff --git a/pkg/plugin/grype/plugin.go b/pkg/plugin/grype/plugin.go index 66f40c8f1..0953b697b 100644 --- a/pkg/plugin/grype/plugin.go +++ b/pkg/plugin/grype/plugin.go @@ -94,6 +94,10 @@ func (c Config) setResourceLimit(configKey string, k8sResourceList *corev1.Resou return nil } +func (c *Config) GetImageRef() (string, error) { + return c.GetRequiredData(keyGrypeImageRef) +} + type plugin struct { clock ext.Clock idGenerator ext.IDGenerator @@ -162,19 +166,17 @@ const ( grypeDBLocation = "/tmp/grypedb" ) -// In the Standalone mode there is the init container responsible for -// downloading the latest Grype DB file from GitHub and storing it to the -// emptyDir volume shared with main containers. In other words, the init -// container runs the following Grype command: +// There is an init container to cache the Grype DB, which will be stored in an +// emptyDir volume and shared across the scanning containers. Most configuration +// is done via the environment of the scanning containers // -// grype --cache-dir /tmp/grype/.cache image --download-db-only +// grype db update // // The number of main containers correspond to the number of containers // defined for the scanned workload. Each container runs the Grype image scan // command and skips the database download: // -// grype --cache-dir /tmp/grype/.cache image --skip-update \ -// --format json +// grype --skip-update --quiet --output json func (p *plugin) getPodSpec(ctx starboard.PluginContext, config Config, workload client.Object, credentials map[string]docker.Auth) (corev1.PodSpec, []*corev1.Secret, error) { var secret *corev1.Secret var secrets []*corev1.Secret @@ -295,19 +297,15 @@ func (p *plugin) getPodSpec(ctx starboard.PluginContext, config Config, workload for _, c := range spec.Containers { + //optionally add schema + scanImage := "" + if val, ok := config.Data[keyGrypeScheme]; ok { + scanImage = val + ":" + c.Image + } else { + scanImage = c.Image + } + env := append(commonEnv, - corev1.EnvVar{ - Name: "GRYPE_DB_UPDATE_URL", - ValueFrom: &corev1.EnvVarSource{ - ConfigMapKeyRef: &corev1.ConfigMapKeySelector{ - LocalObjectReference: corev1.LocalObjectReference{ - Name: grypeConfigName, - }, - Key: keyGrypeUpdateURL, - Optional: pointer.BoolPtr(false), - }, - }, - }, corev1.EnvVar{ Name: "GRYPE_REGISTRY_AUTH_AUTHORITY", ValueFrom: &corev1.EnvVarSource{ @@ -376,7 +374,7 @@ func (p *plugin) getPodSpec(ctx starboard.PluginContext, config Config, workload // } args := []string{ - c.Image, + scanImage, "--skip-update", "--quiet", "--output", @@ -429,7 +427,8 @@ func (p *plugin) appendGrypeOptionalArg(config Config, args []string, arg string if val, ok := config.Data[key]; ok && val == "true" { return append(args, arg), nil } else if !ok { - return args, fmt.Errorf("Invalid config key provided: %s", key) + //ignore if optional key is not in config.data + return args, nil } else { return args, nil } diff --git a/pkg/plugin/grype/plugin_test.go b/pkg/plugin/grype/plugin_test.go index b16477198..f5b4d2331 100644 --- a/pkg/plugin/grype/plugin_test.go +++ b/pkg/plugin/grype/plugin_test.go @@ -11,11 +11,9 @@ import ( "github.com/aquasecurity/starboard/pkg/apis/aquasecurity/v1alpha1" "github.com/aquasecurity/starboard/pkg/ext" "github.com/aquasecurity/starboard/pkg/plugin/grype" - "github.com/aquasecurity/starboard/pkg/plugin/trivy" "github.com/aquasecurity/starboard/pkg/starboard" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" - appsv1 "k8s.io/api/apps/v1" corev1 "k8s.io/api/core/v1" "k8s.io/apimachinery/pkg/api/resource" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" @@ -35,23 +33,23 @@ const defaultUpdateURL = "https://toolbox-data.anchore.io/grype/databases/listin func TestConfig_GetImageRef(t *testing.T) { testCases := []struct { name string - configData trivy.Config + configData grype.Config expectedError string expectedImageRef string }{ { name: "Should return error", - configData: trivy.Config{PluginConfig: starboard.PluginConfig{}}, - expectedError: "property trivy.imageRef not set", + configData: grype.Config{PluginConfig: starboard.PluginConfig{}}, + expectedError: "property grype.imageRef not set", }, { name: "Should return image reference from config data", - configData: trivy.Config{PluginConfig: starboard.PluginConfig{ + configData: grype.Config{PluginConfig: starboard.PluginConfig{ Data: map[string]string{ - "trivy.imageRef": "gcr.io/aquasecurity/trivy:0.8.0", + "grype.imageRef": "anchore/grype:0.34.7", }, }}, - expectedImageRef: "gcr.io/aquasecurity/trivy:0.8.0", + expectedImageRef: "anchore/grype:0.34.7", }, } @@ -71,13 +69,13 @@ func TestConfig_GetImageRef(t *testing.T) { func TestConfig_GetResourceRequirements(t *testing.T) { testCases := []struct { name string - config trivy.Config + config grype.Config expectedError string expectedRequirements corev1.ResourceRequirements }{ { name: "Should return empty requirements by default", - config: trivy.Config{ + config: grype.Config{ PluginConfig: starboard.PluginConfig{}, }, expectedError: "", @@ -88,14 +86,14 @@ func TestConfig_GetResourceRequirements(t *testing.T) { }, { name: "Should return configured resource requirement", - config: trivy.Config{ + config: grype.Config{ PluginConfig: starboard.PluginConfig{ Data: map[string]string{ - "trivy.dbRepository": defaultUpdateURL, - "trivy.resources.requests.cpu": "800m", - "trivy.resources.requests.memory": "200M", - "trivy.resources.limits.cpu": "600m", - "trivy.resources.limits.memory": "700M", + "grype.dbRepository": defaultUpdateURL, + "grype.resources.requests.cpu": "800m", + "grype.resources.requests.memory": "200M", + "grype.resources.limits.cpu": "600m", + "grype.resources.limits.memory": "700M", }, }, }, @@ -113,14 +111,14 @@ func TestConfig_GetResourceRequirements(t *testing.T) { }, { name: "Should return error if resource is not parseable", - config: trivy.Config{ + config: grype.Config{ PluginConfig: starboard.PluginConfig{ Data: map[string]string{ - "trivy.resources.requests.cpu": "roughly 100", + "grype.resources.requests.cpu": "roughly 100", }, }, }, - expectedError: "parsing resource definition trivy.resources.requests.cpu: roughly 100 quantities must match the regular expression '^([+-]?[0-9.]+)([eEinumkKMGTP]*[-+]?[0-9]*)$'", + expectedError: "parsing resource definition grype.resources.requests.cpu: roughly 100 quantities must match the regular expression '^([+-]?[0-9.]+)([eEinumkKMGTP]*[-+]?[0-9]*)$'", }, } for _, tc := range testCases { @@ -136,59 +134,15 @@ func TestConfig_GetResourceRequirements(t *testing.T) { } } -func TestConfig_IgnoreUnfixed(t *testing.T) { - testCases := []struct { - name string - configData trivy.Config - expectedOutput bool - }{ - { - name: "Should return false", - configData: trivy.Config{PluginConfig: starboard.PluginConfig{ - Data: map[string]string{ - "foo": "bar", - }, - }}, - expectedOutput: false, - }, - { - name: "Should return true", - configData: trivy.Config{PluginConfig: starboard.PluginConfig{ - Data: map[string]string{ - "foo": "bar", - "trivy.ignoreUnfixed": "true", - }, - }}, - expectedOutput: true, - }, - { - name: "Should return false when set it as false", - configData: trivy.Config{PluginConfig: starboard.PluginConfig{ - Data: map[string]string{ - "foo": "bar", - "trivy.ignoreUnfixed": "false", - }, - }}, - expectedOutput: true, - }, - } - for _, tc := range testCases { - t.Run(tc.name, func(t *testing.T) { - exists := tc.configData.IgnoreUnfixed() - assert.Equal(t, tc.expectedOutput, exists) - }) - } -} - func TestPlugin_Init(t *testing.T) { t.Run("Should create the default config", func(t *testing.T) { client := fake.NewClientBuilder().WithObjects().Build() - instance := trivy.NewPlugin(fixedClock, ext.NewSimpleIDGenerator(), client) + instance := grype.NewPlugin(fixedClock, ext.NewSimpleIDGenerator(), client) pluginContext := starboard.NewPluginContext(). - WithName(trivy.Plugin). + WithName(grype.Plugin). WithNamespace("starboard-ns"). WithServiceAccountName("starboard-sa"). WithClient(client). @@ -199,7 +153,7 @@ func TestPlugin_Init(t *testing.T) { var cm corev1.ConfigMap err = client.Get(context.Background(), types.NamespacedName{ Namespace: "starboard-ns", - Name: "starboard-trivy-config", + Name: "starboard-grype-config", }, &cm) require.NoError(t, err) assert.Equal(t, corev1.ConfigMap{ @@ -208,7 +162,7 @@ func TestPlugin_Init(t *testing.T) { Kind: "ConfigMap", }, ObjectMeta: metav1.ObjectMeta{ - Name: "starboard-trivy-config", + Name: "starboard-grype-config", Namespace: "starboard-ns", Labels: map[string]string{ "app.kubernetes.io/managed-by": "starboard", @@ -216,70 +170,13 @@ func TestPlugin_Init(t *testing.T) { ResourceVersion: "1", }, Data: map[string]string{ - "trivy.imageRef": "docker.io/aquasec/trivy:0.25.2", - "trivy.severity": "UNKNOWN,LOW,MEDIUM,HIGH,CRITICAL", - "trivy.mode": "Standalone", - "trivy.timeout": "5m0s", - "trivy.dbRepository": defaultUpdateURL, - - "trivy.resources.requests.cpu": "100m", - "trivy.resources.requests.memory": "100M", - "trivy.resources.limits.cpu": "500m", - "trivy.resources.limits.memory": "500M", - }, - }, cm) - }) - - t.Run("Should not overwrite existing config", func(t *testing.T) { - client := fake.NewClientBuilder().WithObjects( - &corev1.ConfigMap{ - TypeMeta: metav1.TypeMeta{ - APIVersion: "v1", - Kind: "ConfigMap", - }, - ObjectMeta: metav1.ObjectMeta{ - Name: "starboard-trivy-config", - Namespace: "starboard-ns", - ResourceVersion: "1", - }, - Data: map[string]string{ - "trivy.imageRef": "docker.io/aquasec/trivy:0.25.2", - "trivy.severity": "UNKNOWN,LOW,MEDIUM,HIGH,CRITICAL", - "trivy.mode": "Standalone", - }, - }).Build() - - instance := trivy.NewPlugin(fixedClock, ext.NewSimpleIDGenerator(), client) - - pluginContext := starboard.NewPluginContext(). - WithName(trivy.Plugin). - WithNamespace("starboard-ns"). - WithServiceAccountName("starboard-sa"). - WithClient(client). - Get() - err := instance.Init(pluginContext) - require.NoError(t, err) + "grype.imageRef": "anchore/grype:0.34.7", + "grype.updateURL": defaultUpdateURL, - var cm corev1.ConfigMap - err = client.Get(context.Background(), types.NamespacedName{ - Namespace: "starboard-ns", - Name: "starboard-trivy-config", - }, &cm) - require.NoError(t, err) - assert.Equal(t, corev1.ConfigMap{ - TypeMeta: metav1.TypeMeta{ - APIVersion: "v1", - Kind: "ConfigMap", - }, - ObjectMeta: metav1.ObjectMeta{ - Name: "starboard-trivy-config", - Namespace: "starboard-ns", - ResourceVersion: "1", - }, - Data: map[string]string{ - "trivy.imageRef": "docker.io/aquasec/trivy:0.25.2", - "trivy.severity": "UNKNOWN,LOW,MEDIUM,HIGH,CRITICAL", - "trivy.mode": "Standalone", + "grype.resources.requests.cpu": "100m", + "grype.resources.requests.memory": "100M", + "grype.resources.limits.cpu": "500m", + "grype.resources.limits.memory": "500M", }, }, cm) }) @@ -305,7 +202,22 @@ const ( keyResourcesLimitsMemory = "grype.resources.limits.memory" ) -func TestPlugin_GetScanJobSpec(t *testing.T) { +type JobSpecTestCase struct { + Name string + + Config map[string]string + ExpectedConfigName string + WorkloadSpec client.Object + + // ExpectedSecrets []corev1.Secret + ExpectedJobSpec corev1.PodSpec +} + +//returns default test case, since most of them share a lot +func NewJobSpecTestCase(Name string) JobSpecTestCase { + j := new(JobSpecTestCase) + j.Name = Name + j.ExpectedConfigName = "starboard-grype-config" commonEnv := []corev1.EnvVar{ { @@ -313,7 +225,7 @@ func TestPlugin_GetScanJobSpec(t *testing.T) { ValueFrom: &corev1.EnvVarSource{ ConfigMapKeyRef: &corev1.ConfigMapKeySelector{ LocalObjectReference: corev1.LocalObjectReference{ - Name: grype.Plugin, + Name: j.ExpectedConfigName, }, Key: keyGrypeHTTPProxy, Optional: pointer.BoolPtr(true), @@ -325,7 +237,7 @@ func TestPlugin_GetScanJobSpec(t *testing.T) { ValueFrom: &corev1.EnvVarSource{ ConfigMapKeyRef: &corev1.ConfigMapKeySelector{ LocalObjectReference: corev1.LocalObjectReference{ - Name: grype.Plugin, + Name: j.ExpectedConfigName, }, Key: keyGrypeHTTPSProxy, Optional: pointer.BoolPtr(true), @@ -337,7 +249,7 @@ func TestPlugin_GetScanJobSpec(t *testing.T) { ValueFrom: &corev1.EnvVarSource{ ConfigMapKeyRef: &corev1.ConfigMapKeySelector{ LocalObjectReference: corev1.LocalObjectReference{ - Name: grype.Plugin, + Name: j.ExpectedConfigName, }, Key: keyGrypeNoProxy, Optional: pointer.BoolPtr(true), @@ -349,7 +261,7 @@ func TestPlugin_GetScanJobSpec(t *testing.T) { ValueFrom: &corev1.EnvVarSource{ ConfigMapKeyRef: &corev1.ConfigMapKeySelector{ LocalObjectReference: corev1.LocalObjectReference{ - Name: grype.Plugin, + Name: j.ExpectedConfigName, }, Key: keyGrypeUpdateURL, Optional: pointer.BoolPtr(false), @@ -362,6 +274,34 @@ func TestPlugin_GetScanJobSpec(t *testing.T) { }, } + j.Config = map[string]string{ + "grype.imageRef": "anchore/grype:v0.34.7", + "grype.updateURL": defaultUpdateURL, + "grype.resources.requests.cpu": "100m", + "grype.resources.requests.memory": "100M", + "grype.resources.limits.cpu": "500m", + "grype.resources.limits.memory": "500M", + } + + j.WorkloadSpec = &corev1.Pod{ + TypeMeta: metav1.TypeMeta{ + Kind: "Pod", + APIVersion: "v1", + }, + ObjectMeta: metav1.ObjectMeta{ + Name: "nginx", + Namespace: "prod-ns", + }, + Spec: corev1.PodSpec{ + Containers: []corev1.Container{ + { + Name: "nginx", + Image: "nginx:1.16", + }, + }, + }, + } + tmpVolume := corev1.Volume{ Name: "tmp", VolumeSource: corev1.VolumeSource{ @@ -377,2535 +317,162 @@ func TestPlugin_GetScanJobSpec(t *testing.T) { ReadOnly: false, } - testCases := []struct { - name string - - config map[string]string - workloadSpec client.Object - - expectedSecrets []corev1.Secret - expectedJobSpec corev1.PodSpec - }{ - { - name: "grype", - config: map[string]string{ - "grype.imageRef": "anchore/grype:v0.34.7", - "grype.updateURL": defaultUpdateURL, - "grype.resources.requests.cpu": "100m", - "grype.resources.requests.memory": "100M", - "grype.resources.limits.cpu": "500m", - "grype.resources.limits.memory": "500M", - }, - workloadSpec: &appsv1.ReplicaSet{ - TypeMeta: metav1.TypeMeta{ - Kind: "ReplicaSet", - APIVersion: "apps/v1", + //expected base podSpec, will be adjusted for the individual TCs + j.ExpectedJobSpec = corev1.PodSpec{ + Affinity: starboard.LinuxNodeAffinity(), + RestartPolicy: corev1.RestartPolicyNever, + ServiceAccountName: "starboard-sa", + AutomountServiceAccountToken: pointer.BoolPtr(false), + Volumes: []corev1.Volume{ + tmpVolume, + }, + InitContainers: []corev1.Container{ + { + Name: "00000000-0000-0000-0000-000000000001", + Image: "anchore/grype:v0.34.7", + ImagePullPolicy: corev1.PullIfNotPresent, + TerminationMessagePolicy: corev1.TerminationMessageFallbackToLogsOnError, + Env: commonEnv, + Command: []string{ + "grype", }, - ObjectMeta: metav1.ObjectMeta{ - Name: "nginx-6799fc88d8", - Namespace: "prod-ns", + Args: []string{ + "db", + "update", }, - Spec: appsv1.ReplicaSetSpec{ - Template: corev1.PodTemplateSpec{ - Spec: corev1.PodSpec{ - Containers: []corev1.Container{ - { - Name: "nginx", - Image: "nginx:1.16", - }, - }, - }, + Resources: corev1.ResourceRequirements{ + Requests: corev1.ResourceList{ + corev1.ResourceCPU: resource.MustParse("100m"), + corev1.ResourceMemory: resource.MustParse("100M"), }, - }, - }, - expectedJobSpec: corev1.PodSpec{ - Affinity: starboard.LinuxNodeAffinity(), - RestartPolicy: corev1.RestartPolicyNever, - ServiceAccountName: "starboard-sa", - AutomountServiceAccountToken: pointer.BoolPtr(false), - Volumes: []corev1.Volume{ - tmpVolume, - }, - InitContainers: []corev1.Container{ - { - Name: "00000000-0000-0000-0000-000000000001", - Image: "anchore/grype:v0.34.7", - ImagePullPolicy: corev1.PullIfNotPresent, - TerminationMessagePolicy: corev1.TerminationMessageFallbackToLogsOnError, - Env: commonEnv, - Command: []string{ - "grype", - }, - Args: []string{ - "db", - "update", - }, - Resources: corev1.ResourceRequirements{ - Requests: corev1.ResourceList{ - corev1.ResourceCPU: resource.MustParse("100m"), - corev1.ResourceMemory: resource.MustParse("100M"), - }, - Limits: corev1.ResourceList{ - corev1.ResourceCPU: resource.MustParse("500m"), - corev1.ResourceMemory: resource.MustParse("500M"), - }, - }, - VolumeMounts: []corev1.VolumeMount{ - tmpVolumeMount, - }, + Limits: corev1.ResourceList{ + corev1.ResourceCPU: resource.MustParse("500m"), + corev1.ResourceMemory: resource.MustParse("500M"), }, }, - Containers: []corev1.Container{ - { - Name: "nginx", - Image: "docker.io/aquasec/trivy:0.14.0", - ImagePullPolicy: corev1.PullIfNotPresent, - TerminationMessagePolicy: corev1.TerminationMessageFallbackToLogsOnError, - Env: []corev1.EnvVar{ - { - Name: "TRIVY_SEVERITY", - ValueFrom: &corev1.EnvVarSource{ - ConfigMapKeyRef: &corev1.ConfigMapKeySelector{ - LocalObjectReference: corev1.LocalObjectReference{ - Name: "starboard-trivy-config", - }, - Key: "trivy.severity", - Optional: pointer.BoolPtr(true), - }, - }, - }, - { - Name: "TRIVY_IGNORE_UNFIXED", - ValueFrom: &corev1.EnvVarSource{ - ConfigMapKeyRef: &corev1.ConfigMapKeySelector{ - LocalObjectReference: corev1.LocalObjectReference{ - Name: "starboard-trivy-config", - }, - Key: "trivy.ignoreUnfixed", - Optional: pointer.BoolPtr(true), - }, - }, - }, - - { - Name: "TRIVY_SKIP_FILES", - ValueFrom: &corev1.EnvVarSource{ - ConfigMapKeyRef: &corev1.ConfigMapKeySelector{ - LocalObjectReference: corev1.LocalObjectReference{ - Name: "starboard-trivy-config", - }, - Key: "trivy.skipFiles", - Optional: pointer.BoolPtr(true), - }, - }, - }, - { - Name: "TRIVY_SKIP_DIRS", - ValueFrom: &corev1.EnvVarSource{ - ConfigMapKeyRef: &corev1.ConfigMapKeySelector{ - LocalObjectReference: corev1.LocalObjectReference{ - Name: "starboard-trivy-config", - }, - Key: "trivy.skipDirs", - Optional: pointer.BoolPtr(true), - }, - }, - }, - { - Name: "HTTP_PROXY", - ValueFrom: &corev1.EnvVarSource{ - ConfigMapKeyRef: &corev1.ConfigMapKeySelector{ - LocalObjectReference: corev1.LocalObjectReference{ - Name: "starboard-trivy-config", - }, - Key: "trivy.httpProxy", - Optional: pointer.BoolPtr(true), - }, - }, - }, - { - Name: "HTTPS_PROXY", - ValueFrom: &corev1.EnvVarSource{ - ConfigMapKeyRef: &corev1.ConfigMapKeySelector{ - LocalObjectReference: corev1.LocalObjectReference{ - Name: "starboard-trivy-config", - }, - Key: "trivy.httpsProxy", - Optional: pointer.BoolPtr(true), - }, - }, - }, - { - Name: "NO_PROXY", - ValueFrom: &corev1.EnvVarSource{ - ConfigMapKeyRef: &corev1.ConfigMapKeySelector{ - LocalObjectReference: corev1.LocalObjectReference{ - Name: "starboard-trivy-config", - }, - Key: "trivy.noProxy", - Optional: pointer.BoolPtr(true), - }, - }, - }, - }, - Command: []string{ - "trivy", - }, - Args: []string{ - "--cache-dir", "/tmp/trivy/.cache", - "--quiet", - "image", - "--skip-update", - "--format", "json", - "nginx:1.16", - }, - Resources: corev1.ResourceRequirements{ - Requests: corev1.ResourceList{ - corev1.ResourceCPU: resource.MustParse("100m"), - corev1.ResourceMemory: resource.MustParse("100M"), - }, - Limits: corev1.ResourceList{ - corev1.ResourceCPU: resource.MustParse("500m"), - corev1.ResourceMemory: resource.MustParse("500M"), - }, - }, - VolumeMounts: []corev1.VolumeMount{ - tmpVolumeMount, - }, - SecurityContext: &corev1.SecurityContext{ - Privileged: pointer.BoolPtr(false), - AllowPrivilegeEscalation: pointer.BoolPtr(false), - Capabilities: &corev1.Capabilities{ - Drop: []corev1.Capability{"all"}, - }, - ReadOnlyRootFilesystem: pointer.BoolPtr(true), - }, - }, + VolumeMounts: []corev1.VolumeMount{ + tmpVolumeMount, }, - SecurityContext: &corev1.PodSecurityContext{}, }, }, - { - name: "Standalone mode with insecure registry", - config: map[string]string{ - "trivy.imageRef": "docker.io/aquasec/trivy:0.14.0", - "trivy.mode": string(trivy.Standalone), - "trivy.insecureRegistry.pocRegistry": "poc.myregistry.harbor.com.pl", - "trivy.dbRepository": defaultUpdateURL, - - "trivy.resources.requests.cpu": "100m", - "trivy.resources.requests.memory": "100M", - "trivy.resources.limits.cpu": "500m", - "trivy.resources.limits.memory": "500M", - }, - workloadSpec: &corev1.Pod{ - TypeMeta: metav1.TypeMeta{ - Kind: "Pod", - APIVersion: "v1", - }, - ObjectMeta: metav1.ObjectMeta{ - Name: "nginx", - Namespace: "prod-ns", - }, - Spec: corev1.PodSpec{ - Containers: []corev1.Container{ - { - Name: "nginx", - Image: "poc.myregistry.harbor.com.pl/nginx:1.16", - }, - }, - }}, - expectedJobSpec: corev1.PodSpec{ - Affinity: starboard.LinuxNodeAffinity(), - RestartPolicy: corev1.RestartPolicyNever, - ServiceAccountName: "starboard-sa", - AutomountServiceAccountToken: pointer.BoolPtr(false), - Volumes: []corev1.Volume{ - tmpVolume, - }, - InitContainers: []corev1.Container{ - { - Name: "00000000-0000-0000-0000-000000000001", - Image: "docker.io/aquasec/trivy:0.14.0", - ImagePullPolicy: corev1.PullIfNotPresent, - TerminationMessagePolicy: corev1.TerminationMessageFallbackToLogsOnError, - Env: []corev1.EnvVar{ - { - Name: "HTTP_PROXY", - ValueFrom: &corev1.EnvVarSource{ - ConfigMapKeyRef: &corev1.ConfigMapKeySelector{ - LocalObjectReference: corev1.LocalObjectReference{ - Name: "starboard-trivy-config", - }, - Key: "trivy.httpProxy", - Optional: pointer.BoolPtr(true), - }, - }, - }, - { - Name: "HTTPS_PROXY", - ValueFrom: &corev1.EnvVarSource{ - ConfigMapKeyRef: &corev1.ConfigMapKeySelector{ - LocalObjectReference: corev1.LocalObjectReference{ - Name: "starboard-trivy-config", - }, - Key: "trivy.httpsProxy", - Optional: pointer.BoolPtr(true), - }, - }, - }, - { - Name: "NO_PROXY", - ValueFrom: &corev1.EnvVarSource{ - ConfigMapKeyRef: &corev1.ConfigMapKeySelector{ - LocalObjectReference: corev1.LocalObjectReference{ - Name: "starboard-trivy-config", - }, - Key: "trivy.noProxy", - Optional: pointer.BoolPtr(true), - }, - }, - }, - { - Name: "GITHUB_TOKEN", - ValueFrom: &corev1.EnvVarSource{ - SecretKeyRef: &corev1.SecretKeySelector{ - LocalObjectReference: corev1.LocalObjectReference{ - Name: "starboard-trivy-config", - }, - Key: "trivy.githubToken", - Optional: pointer.BoolPtr(true), - }, + Containers: []corev1.Container{ + { + Name: "nginx", + Image: "anchore/grype:v0.34.7", + ImagePullPolicy: corev1.PullIfNotPresent, + TerminationMessagePolicy: corev1.TerminationMessageFallbackToLogsOnError, + Env: append(commonEnv, + corev1.EnvVar{ + Name: "GRYPE_REGISTRY_AUTH_AUTHORITY", + ValueFrom: &corev1.EnvVarSource{ + ConfigMapKeyRef: &corev1.ConfigMapKeySelector{ + LocalObjectReference: corev1.LocalObjectReference{ + Name: j.ExpectedConfigName, }, + Key: keyGrypeRegAuthority, + Optional: pointer.BoolPtr(true), }, }, - Command: []string{ - "trivy", - }, - Args: []string{ - "--cache-dir", "/tmp/trivy/.cache", - "image", - "--download-db-only", - "--db-repository", defaultUpdateURL, - }, - Resources: corev1.ResourceRequirements{ - Requests: corev1.ResourceList{ - corev1.ResourceCPU: resource.MustParse("100m"), - corev1.ResourceMemory: resource.MustParse("100M"), - }, - Limits: corev1.ResourceList{ - corev1.ResourceCPU: resource.MustParse("500m"), - corev1.ResourceMemory: resource.MustParse("500M"), - }, - }, - VolumeMounts: []corev1.VolumeMount{ - tmpVolumeMount, - }, }, - }, - Containers: []corev1.Container{ - { - Name: "nginx", - Image: "docker.io/aquasec/trivy:0.14.0", - ImagePullPolicy: corev1.PullIfNotPresent, - TerminationMessagePolicy: corev1.TerminationMessageFallbackToLogsOnError, - Env: []corev1.EnvVar{ - { - Name: "TRIVY_SEVERITY", - ValueFrom: &corev1.EnvVarSource{ - ConfigMapKeyRef: &corev1.ConfigMapKeySelector{ - LocalObjectReference: corev1.LocalObjectReference{ - Name: "starboard-trivy-config", - }, - Key: "trivy.severity", - Optional: pointer.BoolPtr(true), - }, - }, - }, - { - Name: "TRIVY_IGNORE_UNFIXED", - ValueFrom: &corev1.EnvVarSource{ - ConfigMapKeyRef: &corev1.ConfigMapKeySelector{ - LocalObjectReference: corev1.LocalObjectReference{ - Name: "starboard-trivy-config", - }, - Key: "trivy.ignoreUnfixed", - Optional: pointer.BoolPtr(true), - }, - }, - }, - - { - Name: "TRIVY_SKIP_FILES", - ValueFrom: &corev1.EnvVarSource{ - ConfigMapKeyRef: &corev1.ConfigMapKeySelector{ - LocalObjectReference: corev1.LocalObjectReference{ - Name: "starboard-trivy-config", - }, - Key: "trivy.skipFiles", - Optional: pointer.BoolPtr(true), - }, - }, - }, - { - Name: "TRIVY_SKIP_DIRS", - ValueFrom: &corev1.EnvVarSource{ - ConfigMapKeyRef: &corev1.ConfigMapKeySelector{ - LocalObjectReference: corev1.LocalObjectReference{ - Name: "starboard-trivy-config", - }, - Key: "trivy.skipDirs", - Optional: pointer.BoolPtr(true), - }, - }, - }, - { - Name: "HTTP_PROXY", - ValueFrom: &corev1.EnvVarSource{ - ConfigMapKeyRef: &corev1.ConfigMapKeySelector{ - LocalObjectReference: corev1.LocalObjectReference{ - Name: "starboard-trivy-config", - }, - Key: "trivy.httpProxy", - Optional: pointer.BoolPtr(true), - }, - }, - }, - { - Name: "HTTPS_PROXY", - ValueFrom: &corev1.EnvVarSource{ - ConfigMapKeyRef: &corev1.ConfigMapKeySelector{ - LocalObjectReference: corev1.LocalObjectReference{ - Name: "starboard-trivy-config", - }, - Key: "trivy.httpsProxy", - Optional: pointer.BoolPtr(true), - }, - }, - }, - { - Name: "NO_PROXY", - ValueFrom: &corev1.EnvVarSource{ - ConfigMapKeyRef: &corev1.ConfigMapKeySelector{ - LocalObjectReference: corev1.LocalObjectReference{ - Name: "starboard-trivy-config", - }, - Key: "trivy.noProxy", - Optional: pointer.BoolPtr(true), - }, + corev1.EnvVar{ + Name: "GRYPE_EXCLUDE", + ValueFrom: &corev1.EnvVarSource{ + ConfigMapKeyRef: &corev1.ConfigMapKeySelector{ + LocalObjectReference: corev1.LocalObjectReference{ + Name: j.ExpectedConfigName, }, + Key: keyGrypeExcludePaths, + Optional: pointer.BoolPtr(true), }, - { - Name: "TRIVY_INSECURE", - Value: "true", - }, - }, - Command: []string{ - "trivy", - }, - Args: []string{ - "--cache-dir", "/tmp/trivy/.cache", - "--quiet", - "image", - "--skip-update", - "--format", "json", - "poc.myregistry.harbor.com.pl/nginx:1.16", - }, - Resources: corev1.ResourceRequirements{ - Requests: corev1.ResourceList{ - corev1.ResourceCPU: resource.MustParse("100m"), - corev1.ResourceMemory: resource.MustParse("100M"), - }, - Limits: corev1.ResourceList{ - corev1.ResourceCPU: resource.MustParse("500m"), - corev1.ResourceMemory: resource.MustParse("500M"), - }, - }, - VolumeMounts: []corev1.VolumeMount{ - tmpVolumeMount, - }, - SecurityContext: &corev1.SecurityContext{ - Privileged: pointer.BoolPtr(false), - AllowPrivilegeEscalation: pointer.BoolPtr(false), - Capabilities: &corev1.Capabilities{ - Drop: []corev1.Capability{"all"}, - }, - ReadOnlyRootFilesystem: pointer.BoolPtr(true), }, }, - }, - SecurityContext: &corev1.PodSecurityContext{}, - }, - }, - { - name: "Standalone mode with non-SSL registry", - config: map[string]string{ - "trivy.imageRef": "docker.io/aquasec/trivy:0.14.0", - "trivy.mode": string(trivy.Standalone), - "trivy.nonSslRegistry.pocRegistry": "poc.myregistry.harbor.com.pl", - "trivy.dbRepository": defaultUpdateURL, - "trivy.resources.requests.cpu": "100m", - "trivy.resources.requests.memory": "100M", - "trivy.resources.limits.cpu": "500m", - "trivy.resources.limits.memory": "500M", - }, - workloadSpec: &corev1.Pod{ - TypeMeta: metav1.TypeMeta{ - Kind: "Pod", - APIVersion: "v1", - }, - ObjectMeta: metav1.ObjectMeta{ - Name: "nginx", - Namespace: "prod-ns", - }, - Spec: corev1.PodSpec{ - Containers: []corev1.Container{ - { - Name: "nginx", - Image: "poc.myregistry.harbor.com.pl/nginx:1.16", - }, + corev1.EnvVar{ + Name: "GRYPE_DB_AUTO_UPDATE", + Value: "false", }, + ), + Command: []string{ + "grype", }, - }, - expectedJobSpec: corev1.PodSpec{ - Affinity: starboard.LinuxNodeAffinity(), - RestartPolicy: corev1.RestartPolicyNever, - ServiceAccountName: "starboard-sa", - AutomountServiceAccountToken: pointer.BoolPtr(false), - Volumes: []corev1.Volume{ - tmpVolume, + Args: []string{ + "nginx:1.16", + "--skip-update", + "--quiet", + "--output", + "json", }, - InitContainers: []corev1.Container{ - { - Name: "00000000-0000-0000-0000-000000000001", - Image: "docker.io/aquasec/trivy:0.14.0", - ImagePullPolicy: corev1.PullIfNotPresent, - TerminationMessagePolicy: corev1.TerminationMessageFallbackToLogsOnError, - Env: []corev1.EnvVar{ - { - Name: "HTTP_PROXY", - ValueFrom: &corev1.EnvVarSource{ - ConfigMapKeyRef: &corev1.ConfigMapKeySelector{ - LocalObjectReference: corev1.LocalObjectReference{ - Name: "starboard-trivy-config", - }, - Key: "trivy.httpProxy", - Optional: pointer.BoolPtr(true), - }, - }, - }, - { - Name: "HTTPS_PROXY", - ValueFrom: &corev1.EnvVarSource{ - ConfigMapKeyRef: &corev1.ConfigMapKeySelector{ - LocalObjectReference: corev1.LocalObjectReference{ - Name: "starboard-trivy-config", - }, - Key: "trivy.httpsProxy", - Optional: pointer.BoolPtr(true), - }, - }, - }, - { - Name: "NO_PROXY", - ValueFrom: &corev1.EnvVarSource{ - ConfigMapKeyRef: &corev1.ConfigMapKeySelector{ - LocalObjectReference: corev1.LocalObjectReference{ - Name: "starboard-trivy-config", - }, - Key: "trivy.noProxy", - Optional: pointer.BoolPtr(true), - }, - }, - }, - { - Name: "GITHUB_TOKEN", - ValueFrom: &corev1.EnvVarSource{ - SecretKeyRef: &corev1.SecretKeySelector{ - LocalObjectReference: corev1.LocalObjectReference{ - Name: "starboard-trivy-config", - }, - Key: "trivy.githubToken", - Optional: pointer.BoolPtr(true), - }, - }, - }, - }, - Command: []string{ - "trivy", - }, - Args: []string{ - "--cache-dir", "/tmp/trivy/.cache", - "image", - "--download-db-only", - "--db-repository", defaultUpdateURL, - }, - Resources: corev1.ResourceRequirements{ - Requests: corev1.ResourceList{ - corev1.ResourceCPU: resource.MustParse("100m"), - corev1.ResourceMemory: resource.MustParse("100M"), - }, - Limits: corev1.ResourceList{ - corev1.ResourceCPU: resource.MustParse("500m"), - corev1.ResourceMemory: resource.MustParse("500M"), - }, - }, - VolumeMounts: []corev1.VolumeMount{ - tmpVolumeMount, - }, + Resources: corev1.ResourceRequirements{ + Requests: corev1.ResourceList{ + corev1.ResourceCPU: resource.MustParse("100m"), + corev1.ResourceMemory: resource.MustParse("100M"), }, - }, - Containers: []corev1.Container{ - { - Name: "nginx", - Image: "docker.io/aquasec/trivy:0.14.0", - ImagePullPolicy: corev1.PullIfNotPresent, - TerminationMessagePolicy: corev1.TerminationMessageFallbackToLogsOnError, - Env: []corev1.EnvVar{ - { - Name: "TRIVY_SEVERITY", - ValueFrom: &corev1.EnvVarSource{ - ConfigMapKeyRef: &corev1.ConfigMapKeySelector{ - LocalObjectReference: corev1.LocalObjectReference{ - Name: "starboard-trivy-config", - }, - Key: "trivy.severity", - Optional: pointer.BoolPtr(true), - }, - }, - }, - { - Name: "TRIVY_IGNORE_UNFIXED", - ValueFrom: &corev1.EnvVarSource{ - ConfigMapKeyRef: &corev1.ConfigMapKeySelector{ - LocalObjectReference: corev1.LocalObjectReference{ - Name: "starboard-trivy-config", - }, - Key: "trivy.ignoreUnfixed", - Optional: pointer.BoolPtr(true), - }, - }, - }, - - { - Name: "TRIVY_SKIP_FILES", - ValueFrom: &corev1.EnvVarSource{ - ConfigMapKeyRef: &corev1.ConfigMapKeySelector{ - LocalObjectReference: corev1.LocalObjectReference{ - Name: "starboard-trivy-config", - }, - Key: "trivy.skipFiles", - Optional: pointer.BoolPtr(true), - }, - }, - }, - { - Name: "TRIVY_SKIP_DIRS", - ValueFrom: &corev1.EnvVarSource{ - ConfigMapKeyRef: &corev1.ConfigMapKeySelector{ - LocalObjectReference: corev1.LocalObjectReference{ - Name: "starboard-trivy-config", - }, - Key: "trivy.skipDirs", - Optional: pointer.BoolPtr(true), - }, - }, - }, - { - Name: "HTTP_PROXY", - ValueFrom: &corev1.EnvVarSource{ - ConfigMapKeyRef: &corev1.ConfigMapKeySelector{ - LocalObjectReference: corev1.LocalObjectReference{ - Name: "starboard-trivy-config", - }, - Key: "trivy.httpProxy", - Optional: pointer.BoolPtr(true), - }, - }, - }, - { - Name: "HTTPS_PROXY", - ValueFrom: &corev1.EnvVarSource{ - ConfigMapKeyRef: &corev1.ConfigMapKeySelector{ - LocalObjectReference: corev1.LocalObjectReference{ - Name: "starboard-trivy-config", - }, - Key: "trivy.httpsProxy", - Optional: pointer.BoolPtr(true), - }, - }, - }, - { - Name: "NO_PROXY", - ValueFrom: &corev1.EnvVarSource{ - ConfigMapKeyRef: &corev1.ConfigMapKeySelector{ - LocalObjectReference: corev1.LocalObjectReference{ - Name: "starboard-trivy-config", - }, - Key: "trivy.noProxy", - Optional: pointer.BoolPtr(true), - }, - }, - }, - { - Name: "TRIVY_NON_SSL", - Value: "true", - }, - }, - Command: []string{ - "trivy", - }, - Args: []string{ - "--cache-dir", "/tmp/trivy/.cache", - "--quiet", - "image", - "--skip-update", - "--format", "json", - "poc.myregistry.harbor.com.pl/nginx:1.16", - }, - Resources: corev1.ResourceRequirements{ - Requests: corev1.ResourceList{ - corev1.ResourceCPU: resource.MustParse("100m"), - corev1.ResourceMemory: resource.MustParse("100M"), - }, - Limits: corev1.ResourceList{ - corev1.ResourceCPU: resource.MustParse("500m"), - corev1.ResourceMemory: resource.MustParse("500M"), - }, - }, - VolumeMounts: []corev1.VolumeMount{ - tmpVolumeMount, - }, - SecurityContext: &corev1.SecurityContext{ - Privileged: pointer.BoolPtr(false), - AllowPrivilegeEscalation: pointer.BoolPtr(false), - Capabilities: &corev1.Capabilities{ - Drop: []corev1.Capability{"all"}, - }, - ReadOnlyRootFilesystem: pointer.BoolPtr(true), - }, + Limits: corev1.ResourceList{ + corev1.ResourceCPU: resource.MustParse("500m"), + corev1.ResourceMemory: resource.MustParse("500M"), }, }, - SecurityContext: &corev1.PodSecurityContext{}, - }, - }, - { - name: "Standalone mode with trivyignore file", - config: map[string]string{ - "trivy.imageRef": "docker.io/aquasec/trivy:0.14.0", - "trivy.mode": string(trivy.Standalone), - "trivy.ignoreFile": `# Accept the risk -CVE-2018-14618 - -# No impact in our settings -CVE-2019-1543`, - "trivy.dbRepository": defaultUpdateURL, - "trivy.resources.requests.cpu": "100m", - "trivy.resources.requests.memory": "100M", - "trivy.resources.limits.cpu": "500m", - "trivy.resources.limits.memory": "500M", - }, - workloadSpec: &corev1.Pod{ - TypeMeta: metav1.TypeMeta{ - Kind: "Pod", - APIVersion: "v1", - }, - ObjectMeta: metav1.ObjectMeta{ - Name: "nginx", - Namespace: "prod-ns", + VolumeMounts: []corev1.VolumeMount{ + tmpVolumeMount, }, - Spec: corev1.PodSpec{ - Containers: []corev1.Container{ - { - Name: "nginx", - Image: "nginx:1.16", - }, + SecurityContext: &corev1.SecurityContext{ + Privileged: pointer.BoolPtr(false), + AllowPrivilegeEscalation: pointer.BoolPtr(false), + Capabilities: &corev1.Capabilities{ + Drop: []corev1.Capability{"all"}, }, + ReadOnlyRootFilesystem: pointer.BoolPtr(true), }, }, - expectedJobSpec: corev1.PodSpec{ - Affinity: starboard.LinuxNodeAffinity(), - RestartPolicy: corev1.RestartPolicyNever, - ServiceAccountName: "starboard-sa", - AutomountServiceAccountToken: pointer.BoolPtr(false), - Volumes: []corev1.Volume{ - tmpVolume, - { - Name: "ignorefile", - VolumeSource: corev1.VolumeSource{ - ConfigMap: &corev1.ConfigMapVolumeSource{ - LocalObjectReference: corev1.LocalObjectReference{ - Name: "starboard-trivy-config", - }, - Items: []corev1.KeyToPath{ - { - Key: "trivy.ignoreFile", - Path: ".trivyignore", - }, - }, - }, - }, + }, + SecurityContext: &corev1.PodSecurityContext{}, + } + return *j +} + +func TestPlugin_GetScanJobSpec(t *testing.T) { + testCases := []JobSpecTestCase{} + + //default config + testCases = append(testCases, NewJobSpecTestCase("Default single nginx image scan")) + + //ignore unfixed + ignoreUnfixed := NewJobSpecTestCase("Ignore unfixed") + ignoreUnfixed.Config["grype.onlyFixed"] = "true" + ignoreUnfixed.ExpectedJobSpec.Containers[0].Args = + append(ignoreUnfixed.ExpectedJobSpec.Containers[0].Args, "--only-fixed") + testCases = append(testCases, ignoreUnfixed) + + //add CPEs + addCPEs := NewJobSpecTestCase("Add missing CPEs") + addCPEs.Config["grype.addMissingCPEs"] = "true" + addCPEs.ExpectedJobSpec.Containers[0].Args = + append(addCPEs.ExpectedJobSpec.Containers[0].Args, "--add-cpes-if-none") + testCases = append(testCases, addCPEs) + + //both optional args + bothOptArgs := NewJobSpecTestCase("Both optional args") + bothOptArgs.Config["grype.addMissingCPEs"] = "true" + bothOptArgs.Config["grype.onlyFixed"] = "true" + bothOptArgs.ExpectedJobSpec.Containers[0].Args = + append(bothOptArgs.ExpectedJobSpec.Containers[0].Args, "--add-cpes-if-none", "--only-fixed") + testCases = append(testCases, bothOptArgs) + + //add scheme + scheme := NewJobSpecTestCase("Scan image with specified scheme") + scheme.Config["grype.scheme"] = "podman" + scheme.ExpectedJobSpec.Containers[0].Args[0] = "podman:nginx:1.16" + testCases = append(testCases, scheme) + + // Test cases when starboard is enabled with option to run job in the namespace of workload + for _, tc := range testCases { + t.Run(tc.Name, func(t *testing.T) { + fakeclient := fake.NewClientBuilder().WithObjects( + &corev1.ConfigMap{ + ObjectMeta: metav1.ObjectMeta{ + Name: "starboard-grype-config", + Namespace: "starboard-ns", }, - }, - InitContainers: []corev1.Container{ - { - Name: "00000000-0000-0000-0000-000000000001", - Image: "docker.io/aquasec/trivy:0.14.0", - ImagePullPolicy: corev1.PullIfNotPresent, - TerminationMessagePolicy: corev1.TerminationMessageFallbackToLogsOnError, - Env: []corev1.EnvVar{ - { - Name: "HTTP_PROXY", - ValueFrom: &corev1.EnvVarSource{ - ConfigMapKeyRef: &corev1.ConfigMapKeySelector{ - LocalObjectReference: corev1.LocalObjectReference{ - Name: "starboard-trivy-config", - }, - Key: "trivy.httpProxy", - Optional: pointer.BoolPtr(true), - }, - }, - }, - { - Name: "HTTPS_PROXY", - ValueFrom: &corev1.EnvVarSource{ - ConfigMapKeyRef: &corev1.ConfigMapKeySelector{ - LocalObjectReference: corev1.LocalObjectReference{ - Name: "starboard-trivy-config", - }, - Key: "trivy.httpsProxy", - Optional: pointer.BoolPtr(true), - }, - }, - }, - { - Name: "NO_PROXY", - ValueFrom: &corev1.EnvVarSource{ - ConfigMapKeyRef: &corev1.ConfigMapKeySelector{ - LocalObjectReference: corev1.LocalObjectReference{ - Name: "starboard-trivy-config", - }, - Key: "trivy.noProxy", - Optional: pointer.BoolPtr(true), - }, - }, - }, - { - Name: "GITHUB_TOKEN", - ValueFrom: &corev1.EnvVarSource{ - SecretKeyRef: &corev1.SecretKeySelector{ - LocalObjectReference: corev1.LocalObjectReference{ - Name: "starboard-trivy-config", - }, - Key: "trivy.githubToken", - Optional: pointer.BoolPtr(true), - }, - }, - }, - }, - Command: []string{ - "trivy", - }, - Args: []string{ - "--cache-dir", "/tmp/trivy/.cache", - "image", - "--download-db-only", - "--db-repository", defaultUpdateURL, - }, - Resources: corev1.ResourceRequirements{ - Requests: corev1.ResourceList{ - corev1.ResourceCPU: resource.MustParse("100m"), - corev1.ResourceMemory: resource.MustParse("100M"), - }, - Limits: corev1.ResourceList{ - corev1.ResourceCPU: resource.MustParse("500m"), - corev1.ResourceMemory: resource.MustParse("500M"), - }, - }, - VolumeMounts: []corev1.VolumeMount{ - tmpVolumeMount, - }, - }, - }, - Containers: []corev1.Container{ - { - Name: "nginx", - Image: "docker.io/aquasec/trivy:0.14.0", - ImagePullPolicy: corev1.PullIfNotPresent, - TerminationMessagePolicy: corev1.TerminationMessageFallbackToLogsOnError, - Env: []corev1.EnvVar{ - { - Name: "TRIVY_SEVERITY", - ValueFrom: &corev1.EnvVarSource{ - ConfigMapKeyRef: &corev1.ConfigMapKeySelector{ - LocalObjectReference: corev1.LocalObjectReference{ - Name: "starboard-trivy-config", - }, - Key: "trivy.severity", - Optional: pointer.BoolPtr(true), - }, - }, - }, - { - Name: "TRIVY_IGNORE_UNFIXED", - ValueFrom: &corev1.EnvVarSource{ - ConfigMapKeyRef: &corev1.ConfigMapKeySelector{ - LocalObjectReference: corev1.LocalObjectReference{ - Name: "starboard-trivy-config", - }, - Key: "trivy.ignoreUnfixed", - Optional: pointer.BoolPtr(true), - }, - }, - }, - - { - Name: "TRIVY_SKIP_FILES", - ValueFrom: &corev1.EnvVarSource{ - ConfigMapKeyRef: &corev1.ConfigMapKeySelector{ - LocalObjectReference: corev1.LocalObjectReference{ - Name: "starboard-trivy-config", - }, - Key: "trivy.skipFiles", - Optional: pointer.BoolPtr(true), - }, - }, - }, - { - Name: "TRIVY_SKIP_DIRS", - ValueFrom: &corev1.EnvVarSource{ - ConfigMapKeyRef: &corev1.ConfigMapKeySelector{ - LocalObjectReference: corev1.LocalObjectReference{ - Name: "starboard-trivy-config", - }, - Key: "trivy.skipDirs", - Optional: pointer.BoolPtr(true), - }, - }, - }, - { - Name: "HTTP_PROXY", - ValueFrom: &corev1.EnvVarSource{ - ConfigMapKeyRef: &corev1.ConfigMapKeySelector{ - LocalObjectReference: corev1.LocalObjectReference{ - Name: "starboard-trivy-config", - }, - Key: "trivy.httpProxy", - Optional: pointer.BoolPtr(true), - }, - }, - }, - { - Name: "HTTPS_PROXY", - ValueFrom: &corev1.EnvVarSource{ - ConfigMapKeyRef: &corev1.ConfigMapKeySelector{ - LocalObjectReference: corev1.LocalObjectReference{ - Name: "starboard-trivy-config", - }, - Key: "trivy.httpsProxy", - Optional: pointer.BoolPtr(true), - }, - }, - }, - { - Name: "NO_PROXY", - ValueFrom: &corev1.EnvVarSource{ - ConfigMapKeyRef: &corev1.ConfigMapKeySelector{ - LocalObjectReference: corev1.LocalObjectReference{ - Name: "starboard-trivy-config", - }, - Key: "trivy.noProxy", - Optional: pointer.BoolPtr(true), - }, - }, - }, - { - Name: "TRIVY_IGNOREFILE", - Value: "/etc/trivy/.trivyignore", - }, - }, - Command: []string{ - "trivy", - }, - Args: []string{ - "--cache-dir", "/tmp/trivy/.cache", - "--quiet", - "image", - "--skip-update", - "--format", "json", - "nginx:1.16", - }, - Resources: corev1.ResourceRequirements{ - Requests: corev1.ResourceList{ - corev1.ResourceCPU: resource.MustParse("100m"), - corev1.ResourceMemory: resource.MustParse("100M"), - }, - Limits: corev1.ResourceList{ - corev1.ResourceCPU: resource.MustParse("500m"), - corev1.ResourceMemory: resource.MustParse("500M"), - }, - }, - VolumeMounts: []corev1.VolumeMount{ - tmpVolumeMount, - { - Name: "ignorefile", - MountPath: "/etc/trivy/.trivyignore", - SubPath: ".trivyignore", - }, - }, - SecurityContext: &corev1.SecurityContext{ - Privileged: pointer.BoolPtr(false), - AllowPrivilegeEscalation: pointer.BoolPtr(false), - Capabilities: &corev1.Capabilities{ - Drop: []corev1.Capability{"all"}, - }, - ReadOnlyRootFilesystem: pointer.BoolPtr(true), - }, - }, - }, - SecurityContext: &corev1.PodSecurityContext{}, - }, - }, - { - name: "Standalone mode with mirror", - config: map[string]string{ - "trivy.imageRef": "docker.io/aquasec/trivy:0.14.0", - "trivy.mode": string(trivy.Standalone), - - "trivy.dbRepository": defaultUpdateURL, - "trivy.resources.requests.cpu": "100m", - "trivy.resources.requests.memory": "100M", - "trivy.resources.limits.cpu": "500m", - "trivy.resources.limits.memory": "500M", - - "trivy.registry.mirror.index.docker.io": "mirror.io", - }, - workloadSpec: &corev1.Pod{ - TypeMeta: metav1.TypeMeta{ - Kind: "Pod", - APIVersion: "v1", - }, - ObjectMeta: metav1.ObjectMeta{ - Name: "nginx", - Namespace: "prod-ns", - }, - Spec: corev1.PodSpec{ - Containers: []corev1.Container{ - { - Name: "nginx", - Image: "nginx:1.16", - }, - }, - }, - }, - expectedJobSpec: corev1.PodSpec{ - Affinity: starboard.LinuxNodeAffinity(), - RestartPolicy: corev1.RestartPolicyNever, - ServiceAccountName: "starboard-sa", - AutomountServiceAccountToken: pointer.BoolPtr(false), - Volumes: []corev1.Volume{ - tmpVolume, - }, - InitContainers: []corev1.Container{ - { - Name: "00000000-0000-0000-0000-000000000001", - Image: "docker.io/aquasec/trivy:0.14.0", - ImagePullPolicy: corev1.PullIfNotPresent, - TerminationMessagePolicy: corev1.TerminationMessageFallbackToLogsOnError, - Env: []corev1.EnvVar{ - { - Name: "HTTP_PROXY", - ValueFrom: &corev1.EnvVarSource{ - ConfigMapKeyRef: &corev1.ConfigMapKeySelector{ - LocalObjectReference: corev1.LocalObjectReference{ - Name: "starboard-trivy-config", - }, - Key: "trivy.httpProxy", - Optional: pointer.BoolPtr(true), - }, - }, - }, - { - Name: "HTTPS_PROXY", - ValueFrom: &corev1.EnvVarSource{ - ConfigMapKeyRef: &corev1.ConfigMapKeySelector{ - LocalObjectReference: corev1.LocalObjectReference{ - Name: "starboard-trivy-config", - }, - Key: "trivy.httpsProxy", - Optional: pointer.BoolPtr(true), - }, - }, - }, - { - Name: "NO_PROXY", - ValueFrom: &corev1.EnvVarSource{ - ConfigMapKeyRef: &corev1.ConfigMapKeySelector{ - LocalObjectReference: corev1.LocalObjectReference{ - Name: "starboard-trivy-config", - }, - Key: "trivy.noProxy", - Optional: pointer.BoolPtr(true), - }, - }, - }, - - { - Name: "GITHUB_TOKEN", - ValueFrom: &corev1.EnvVarSource{ - SecretKeyRef: &corev1.SecretKeySelector{ - LocalObjectReference: corev1.LocalObjectReference{ - Name: "starboard-trivy-config", - }, - Key: "trivy.githubToken", - Optional: pointer.BoolPtr(true), - }, - }, - }, - }, - Command: []string{ - "trivy", - }, - Args: []string{ - "--cache-dir", "/tmp/trivy/.cache", - "image", - "--download-db-only", - "--db-repository", defaultUpdateURL, - }, - Resources: corev1.ResourceRequirements{ - Requests: corev1.ResourceList{ - corev1.ResourceCPU: resource.MustParse("100m"), - corev1.ResourceMemory: resource.MustParse("100M"), - }, - Limits: corev1.ResourceList{ - corev1.ResourceCPU: resource.MustParse("500m"), - corev1.ResourceMemory: resource.MustParse("500M"), - }, - }, - VolumeMounts: []corev1.VolumeMount{ - tmpVolumeMount, - }, - }, - }, - Containers: []corev1.Container{ - { - Name: "nginx", - Image: "docker.io/aquasec/trivy:0.14.0", - ImagePullPolicy: corev1.PullIfNotPresent, - TerminationMessagePolicy: corev1.TerminationMessageFallbackToLogsOnError, - Env: []corev1.EnvVar{ - { - Name: "TRIVY_SEVERITY", - ValueFrom: &corev1.EnvVarSource{ - ConfigMapKeyRef: &corev1.ConfigMapKeySelector{ - LocalObjectReference: corev1.LocalObjectReference{ - Name: "starboard-trivy-config", - }, - Key: "trivy.severity", - Optional: pointer.BoolPtr(true), - }, - }, - }, - { - Name: "TRIVY_IGNORE_UNFIXED", - ValueFrom: &corev1.EnvVarSource{ - ConfigMapKeyRef: &corev1.ConfigMapKeySelector{ - LocalObjectReference: corev1.LocalObjectReference{ - Name: "starboard-trivy-config", - }, - Key: "trivy.ignoreUnfixed", - Optional: pointer.BoolPtr(true), - }, - }, - }, - - { - Name: "TRIVY_SKIP_FILES", - ValueFrom: &corev1.EnvVarSource{ - ConfigMapKeyRef: &corev1.ConfigMapKeySelector{ - LocalObjectReference: corev1.LocalObjectReference{ - Name: "starboard-trivy-config", - }, - Key: "trivy.skipFiles", - Optional: pointer.BoolPtr(true), - }, - }, - }, - { - Name: "TRIVY_SKIP_DIRS", - ValueFrom: &corev1.EnvVarSource{ - ConfigMapKeyRef: &corev1.ConfigMapKeySelector{ - LocalObjectReference: corev1.LocalObjectReference{ - Name: "starboard-trivy-config", - }, - Key: "trivy.skipDirs", - Optional: pointer.BoolPtr(true), - }, - }, - }, - { - Name: "HTTP_PROXY", - ValueFrom: &corev1.EnvVarSource{ - ConfigMapKeyRef: &corev1.ConfigMapKeySelector{ - LocalObjectReference: corev1.LocalObjectReference{ - Name: "starboard-trivy-config", - }, - Key: "trivy.httpProxy", - Optional: pointer.BoolPtr(true), - }, - }, - }, - { - Name: "HTTPS_PROXY", - ValueFrom: &corev1.EnvVarSource{ - ConfigMapKeyRef: &corev1.ConfigMapKeySelector{ - LocalObjectReference: corev1.LocalObjectReference{ - Name: "starboard-trivy-config", - }, - Key: "trivy.httpsProxy", - Optional: pointer.BoolPtr(true), - }, - }, - }, - { - Name: "NO_PROXY", - ValueFrom: &corev1.EnvVarSource{ - ConfigMapKeyRef: &corev1.ConfigMapKeySelector{ - LocalObjectReference: corev1.LocalObjectReference{ - Name: "starboard-trivy-config", - }, - Key: "trivy.noProxy", - Optional: pointer.BoolPtr(true), - }, - }, - }, - }, - Command: []string{ - "trivy", - }, - Args: []string{ - "--cache-dir", "/tmp/trivy/.cache", - "--quiet", - "image", - "--skip-update", - "--format", "json", - "mirror.io/library/nginx:1.16", - }, - Resources: corev1.ResourceRequirements{ - Requests: corev1.ResourceList{ - corev1.ResourceCPU: resource.MustParse("100m"), - corev1.ResourceMemory: resource.MustParse("100M"), - }, - Limits: corev1.ResourceList{ - corev1.ResourceCPU: resource.MustParse("500m"), - corev1.ResourceMemory: resource.MustParse("500M"), - }, - }, - VolumeMounts: []corev1.VolumeMount{ - tmpVolumeMount, - }, - SecurityContext: &corev1.SecurityContext{ - Privileged: pointer.BoolPtr(false), - AllowPrivilegeEscalation: pointer.BoolPtr(false), - Capabilities: &corev1.Capabilities{ - Drop: []corev1.Capability{"all"}, - }, - ReadOnlyRootFilesystem: pointer.BoolPtr(true), - }, - }, - }, - SecurityContext: &corev1.PodSecurityContext{}, - }, - }, - { - name: "ClientServer mode without insecure registry", - config: map[string]string{ - "trivy.imageRef": "docker.io/aquasec/trivy:0.14.0", - "trivy.mode": string(trivy.ClientServer), - "trivy.serverURL": "http://trivy.trivy:4954", - "trivy.dbRepository": defaultUpdateURL, - "trivy.resources.requests.cpu": "100m", - "trivy.resources.requests.memory": "100M", - "trivy.resources.limits.cpu": "500m", - "trivy.resources.limits.memory": "500M", - }, - workloadSpec: &corev1.Pod{ - TypeMeta: metav1.TypeMeta{ - Kind: "Pod", - APIVersion: "v1", - }, - ObjectMeta: metav1.ObjectMeta{ - Name: "nginx", - Namespace: "prod-ns", - }, - Spec: corev1.PodSpec{ - Containers: []corev1.Container{ - { - Name: "nginx", - Image: "nginx:1.16", - }, - }, - }, - }, - expectedJobSpec: corev1.PodSpec{ - Affinity: starboard.LinuxNodeAffinity(), - RestartPolicy: corev1.RestartPolicyNever, - ServiceAccountName: "starboard-sa", - AutomountServiceAccountToken: pointer.BoolPtr(false), - Containers: []corev1.Container{ - { - Name: "nginx", - Image: "docker.io/aquasec/trivy:0.14.0", - ImagePullPolicy: corev1.PullIfNotPresent, - TerminationMessagePolicy: corev1.TerminationMessageFallbackToLogsOnError, - Env: []corev1.EnvVar{ - { - Name: "HTTP_PROXY", - ValueFrom: &corev1.EnvVarSource{ - ConfigMapKeyRef: &corev1.ConfigMapKeySelector{ - LocalObjectReference: corev1.LocalObjectReference{ - Name: "starboard-trivy-config", - }, - Key: "trivy.httpProxy", - Optional: pointer.BoolPtr(true), - }, - }, - }, - { - Name: "HTTPS_PROXY", - ValueFrom: &corev1.EnvVarSource{ - ConfigMapKeyRef: &corev1.ConfigMapKeySelector{ - LocalObjectReference: corev1.LocalObjectReference{ - Name: "starboard-trivy-config", - }, - Key: "trivy.httpsProxy", - Optional: pointer.BoolPtr(true), - }, - }, - }, - { - Name: "NO_PROXY", - ValueFrom: &corev1.EnvVarSource{ - ConfigMapKeyRef: &corev1.ConfigMapKeySelector{ - LocalObjectReference: corev1.LocalObjectReference{ - Name: "starboard-trivy-config", - }, - Key: "trivy.noProxy", - Optional: pointer.BoolPtr(true), - }, - }, - }, - { - Name: "TRIVY_SEVERITY", - ValueFrom: &corev1.EnvVarSource{ - ConfigMapKeyRef: &corev1.ConfigMapKeySelector{ - LocalObjectReference: corev1.LocalObjectReference{ - Name: "starboard-trivy-config", - }, - Key: "trivy.severity", - Optional: pointer.BoolPtr(true), - }, - }, - }, - { - Name: "TRIVY_IGNORE_UNFIXED", - ValueFrom: &corev1.EnvVarSource{ - ConfigMapKeyRef: &corev1.ConfigMapKeySelector{ - LocalObjectReference: corev1.LocalObjectReference{ - Name: "starboard-trivy-config", - }, - Key: "trivy.ignoreUnfixed", - Optional: pointer.BoolPtr(true), - }, - }, - }, - - { - Name: "TRIVY_SKIP_FILES", - ValueFrom: &corev1.EnvVarSource{ - ConfigMapKeyRef: &corev1.ConfigMapKeySelector{ - LocalObjectReference: corev1.LocalObjectReference{ - Name: "starboard-trivy-config", - }, - Key: "trivy.skipFiles", - Optional: pointer.BoolPtr(true), - }, - }, - }, - { - Name: "TRIVY_SKIP_DIRS", - ValueFrom: &corev1.EnvVarSource{ - ConfigMapKeyRef: &corev1.ConfigMapKeySelector{ - LocalObjectReference: corev1.LocalObjectReference{ - Name: "starboard-trivy-config", - }, - Key: "trivy.skipDirs", - Optional: pointer.BoolPtr(true), - }, - }, - }, - { - Name: "TRIVY_TOKEN_HEADER", - ValueFrom: &corev1.EnvVarSource{ - ConfigMapKeyRef: &corev1.ConfigMapKeySelector{ - LocalObjectReference: corev1.LocalObjectReference{ - Name: "starboard-trivy-config", - }, - Key: "trivy.serverTokenHeader", - Optional: pointer.BoolPtr(true), - }, - }, - }, - { - Name: "TRIVY_TOKEN", - ValueFrom: &corev1.EnvVarSource{ - SecretKeyRef: &corev1.SecretKeySelector{ - LocalObjectReference: corev1.LocalObjectReference{ - Name: "starboard-trivy-config", - }, - Key: "trivy.serverToken", - Optional: pointer.BoolPtr(true), - }, - }, - }, - { - Name: "TRIVY_CUSTOM_HEADERS", - ValueFrom: &corev1.EnvVarSource{ - SecretKeyRef: &corev1.SecretKeySelector{ - LocalObjectReference: corev1.LocalObjectReference{ - Name: "starboard-trivy-config", - }, - Key: "trivy.serverCustomHeaders", - Optional: pointer.BoolPtr(true), - }, - }, - }, - }, - Command: []string{ - "trivy", - }, - Args: []string{ - "--quiet", - "client", - "--format", - "json", - "--remote", - "http://trivy.trivy:4954", - "nginx:1.16", - }, - Resources: corev1.ResourceRequirements{ - Requests: corev1.ResourceList{ - corev1.ResourceCPU: resource.MustParse("100m"), - corev1.ResourceMemory: resource.MustParse("100M"), - }, - Limits: corev1.ResourceList{ - corev1.ResourceCPU: resource.MustParse("500m"), - corev1.ResourceMemory: resource.MustParse("500M"), - }, - }, - }, - }, - }, - }, - { - name: "ClientServer mode without insecure registry", - config: map[string]string{ - "trivy.imageRef": "docker.io/aquasec/trivy:0.14.0", - "trivy.mode": string(trivy.ClientServer), - "trivy.serverURL": "http://trivy.trivy:4954", - "trivy.dbRepository": defaultUpdateURL, - "trivy.resources.requests.cpu": "100m", - "trivy.resources.requests.memory": "100M", - "trivy.resources.limits.cpu": "500m", - "trivy.resources.limits.memory": "500M", - }, - workloadSpec: &corev1.Pod{ - TypeMeta: metav1.TypeMeta{ - Kind: "Pod", - APIVersion: "v1", - }, - ObjectMeta: metav1.ObjectMeta{ - Name: "nginx", - Namespace: "prod-ns", - }, - Spec: corev1.PodSpec{ - Containers: []corev1.Container{ - { - Name: "nginx", - Image: "nginx:1.16", - }, - }, - }, - }, - expectedJobSpec: corev1.PodSpec{ - Affinity: starboard.LinuxNodeAffinity(), - RestartPolicy: corev1.RestartPolicyNever, - ServiceAccountName: "starboard-sa", - AutomountServiceAccountToken: pointer.BoolPtr(false), - Containers: []corev1.Container{ - { - Name: "nginx", - Image: "docker.io/aquasec/trivy:0.14.0", - ImagePullPolicy: corev1.PullIfNotPresent, - TerminationMessagePolicy: corev1.TerminationMessageFallbackToLogsOnError, - Env: []corev1.EnvVar{ - { - Name: "HTTP_PROXY", - ValueFrom: &corev1.EnvVarSource{ - ConfigMapKeyRef: &corev1.ConfigMapKeySelector{ - LocalObjectReference: corev1.LocalObjectReference{ - Name: "starboard-trivy-config", - }, - Key: "trivy.httpProxy", - Optional: pointer.BoolPtr(true), - }, - }, - }, - { - Name: "HTTPS_PROXY", - ValueFrom: &corev1.EnvVarSource{ - ConfigMapKeyRef: &corev1.ConfigMapKeySelector{ - LocalObjectReference: corev1.LocalObjectReference{ - Name: "starboard-trivy-config", - }, - Key: "trivy.httpsProxy", - Optional: pointer.BoolPtr(true), - }, - }, - }, - { - Name: "NO_PROXY", - ValueFrom: &corev1.EnvVarSource{ - ConfigMapKeyRef: &corev1.ConfigMapKeySelector{ - LocalObjectReference: corev1.LocalObjectReference{ - Name: "starboard-trivy-config", - }, - Key: "trivy.noProxy", - Optional: pointer.BoolPtr(true), - }, - }, - }, - { - Name: "TRIVY_SEVERITY", - ValueFrom: &corev1.EnvVarSource{ - ConfigMapKeyRef: &corev1.ConfigMapKeySelector{ - LocalObjectReference: corev1.LocalObjectReference{ - Name: "starboard-trivy-config", - }, - Key: "trivy.severity", - Optional: pointer.BoolPtr(true), - }, - }, - }, - { - Name: "TRIVY_IGNORE_UNFIXED", - ValueFrom: &corev1.EnvVarSource{ - ConfigMapKeyRef: &corev1.ConfigMapKeySelector{ - LocalObjectReference: corev1.LocalObjectReference{ - Name: "starboard-trivy-config", - }, - Key: "trivy.ignoreUnfixed", - Optional: pointer.BoolPtr(true), - }, - }, - }, - - { - Name: "TRIVY_SKIP_FILES", - ValueFrom: &corev1.EnvVarSource{ - ConfigMapKeyRef: &corev1.ConfigMapKeySelector{ - LocalObjectReference: corev1.LocalObjectReference{ - Name: "starboard-trivy-config", - }, - Key: "trivy.skipFiles", - Optional: pointer.BoolPtr(true), - }, - }, - }, - { - Name: "TRIVY_SKIP_DIRS", - ValueFrom: &corev1.EnvVarSource{ - ConfigMapKeyRef: &corev1.ConfigMapKeySelector{ - LocalObjectReference: corev1.LocalObjectReference{ - Name: "starboard-trivy-config", - }, - Key: "trivy.skipDirs", - Optional: pointer.BoolPtr(true), - }, - }, - }, - { - Name: "TRIVY_TOKEN_HEADER", - ValueFrom: &corev1.EnvVarSource{ - ConfigMapKeyRef: &corev1.ConfigMapKeySelector{ - LocalObjectReference: corev1.LocalObjectReference{ - Name: "starboard-trivy-config", - }, - Key: "trivy.serverTokenHeader", - Optional: pointer.BoolPtr(true), - }, - }, - }, - { - Name: "TRIVY_TOKEN", - ValueFrom: &corev1.EnvVarSource{ - SecretKeyRef: &corev1.SecretKeySelector{ - LocalObjectReference: corev1.LocalObjectReference{ - Name: "starboard-trivy-config", - }, - Key: "trivy.serverToken", - Optional: pointer.BoolPtr(true), - }, - }, - }, - { - Name: "TRIVY_CUSTOM_HEADERS", - ValueFrom: &corev1.EnvVarSource{ - SecretKeyRef: &corev1.SecretKeySelector{ - LocalObjectReference: corev1.LocalObjectReference{ - Name: "starboard-trivy-config", - }, - Key: "trivy.serverCustomHeaders", - Optional: pointer.BoolPtr(true), - }, - }, - }, - }, - Command: []string{ - "trivy", - }, - Args: []string{ - "--quiet", - "client", - "--format", - "json", - "--remote", - "http://trivy.trivy:4954", - "nginx:1.16", - }, - Resources: corev1.ResourceRequirements{ - Requests: corev1.ResourceList{ - corev1.ResourceCPU: resource.MustParse("100m"), - corev1.ResourceMemory: resource.MustParse("100M"), - }, - Limits: corev1.ResourceList{ - corev1.ResourceCPU: resource.MustParse("500m"), - corev1.ResourceMemory: resource.MustParse("500M"), - }, - }, - }, - }, - }, - }, - { - name: "ClientServer mode with insecure server", - config: map[string]string{ - "trivy.imageRef": "docker.io/aquasec/trivy:0.14.0", - "trivy.mode": string(trivy.ClientServer), - "trivy.serverURL": "https://trivy.trivy:4954", - "trivy.serverInsecure": "true", - "trivy.dbRepository": defaultUpdateURL, - "trivy.resources.requests.cpu": "100m", - "trivy.resources.requests.memory": "100M", - "trivy.resources.limits.cpu": "500m", - "trivy.resources.limits.memory": "500M", - }, - workloadSpec: &corev1.Pod{ - TypeMeta: metav1.TypeMeta{ - Kind: "Pod", - APIVersion: "v1", - }, - ObjectMeta: metav1.ObjectMeta{ - Name: "nginx", - Namespace: "prod-ns", - }, - Spec: corev1.PodSpec{ - Containers: []corev1.Container{ - { - Name: "nginx", - Image: "poc.myregistry.harbor.com.pl/nginx:1.16", - }, - }, - }, - }, - expectedJobSpec: corev1.PodSpec{ - Affinity: starboard.LinuxNodeAffinity(), - RestartPolicy: corev1.RestartPolicyNever, - ServiceAccountName: "starboard-sa", - AutomountServiceAccountToken: pointer.BoolPtr(false), - Containers: []corev1.Container{ - { - Name: "nginx", - Image: "docker.io/aquasec/trivy:0.14.0", - ImagePullPolicy: corev1.PullIfNotPresent, - TerminationMessagePolicy: corev1.TerminationMessageFallbackToLogsOnError, - Env: []corev1.EnvVar{ - { - Name: "HTTP_PROXY", - ValueFrom: &corev1.EnvVarSource{ - ConfigMapKeyRef: &corev1.ConfigMapKeySelector{ - LocalObjectReference: corev1.LocalObjectReference{ - Name: "starboard-trivy-config", - }, - Key: "trivy.httpProxy", - Optional: pointer.BoolPtr(true), - }, - }, - }, - { - Name: "HTTPS_PROXY", - ValueFrom: &corev1.EnvVarSource{ - ConfigMapKeyRef: &corev1.ConfigMapKeySelector{ - LocalObjectReference: corev1.LocalObjectReference{ - Name: "starboard-trivy-config", - }, - Key: "trivy.httpsProxy", - Optional: pointer.BoolPtr(true), - }, - }, - }, - { - Name: "NO_PROXY", - ValueFrom: &corev1.EnvVarSource{ - ConfigMapKeyRef: &corev1.ConfigMapKeySelector{ - LocalObjectReference: corev1.LocalObjectReference{ - Name: "starboard-trivy-config", - }, - Key: "trivy.noProxy", - Optional: pointer.BoolPtr(true), - }, - }, - }, - { - Name: "TRIVY_SEVERITY", - ValueFrom: &corev1.EnvVarSource{ - ConfigMapKeyRef: &corev1.ConfigMapKeySelector{ - LocalObjectReference: corev1.LocalObjectReference{ - Name: "starboard-trivy-config", - }, - Key: "trivy.severity", - Optional: pointer.BoolPtr(true), - }, - }, - }, - { - Name: "TRIVY_IGNORE_UNFIXED", - ValueFrom: &corev1.EnvVarSource{ - ConfigMapKeyRef: &corev1.ConfigMapKeySelector{ - LocalObjectReference: corev1.LocalObjectReference{ - Name: "starboard-trivy-config", - }, - Key: "trivy.ignoreUnfixed", - Optional: pointer.BoolPtr(true), - }, - }, - }, - - { - Name: "TRIVY_SKIP_FILES", - ValueFrom: &corev1.EnvVarSource{ - ConfigMapKeyRef: &corev1.ConfigMapKeySelector{ - LocalObjectReference: corev1.LocalObjectReference{ - Name: "starboard-trivy-config", - }, - Key: "trivy.skipFiles", - Optional: pointer.BoolPtr(true), - }, - }, - }, - { - Name: "TRIVY_SKIP_DIRS", - ValueFrom: &corev1.EnvVarSource{ - ConfigMapKeyRef: &corev1.ConfigMapKeySelector{ - LocalObjectReference: corev1.LocalObjectReference{ - Name: "starboard-trivy-config", - }, - Key: "trivy.skipDirs", - Optional: pointer.BoolPtr(true), - }, - }, - }, - { - Name: "TRIVY_TOKEN_HEADER", - ValueFrom: &corev1.EnvVarSource{ - ConfigMapKeyRef: &corev1.ConfigMapKeySelector{ - LocalObjectReference: corev1.LocalObjectReference{ - Name: "starboard-trivy-config", - }, - Key: "trivy.serverTokenHeader", - Optional: pointer.BoolPtr(true), - }, - }, - }, - { - Name: "TRIVY_TOKEN", - ValueFrom: &corev1.EnvVarSource{ - SecretKeyRef: &corev1.SecretKeySelector{ - LocalObjectReference: corev1.LocalObjectReference{ - Name: "starboard-trivy-config", - }, - Key: "trivy.serverToken", - Optional: pointer.BoolPtr(true), - }, - }, - }, - { - Name: "TRIVY_CUSTOM_HEADERS", - ValueFrom: &corev1.EnvVarSource{ - SecretKeyRef: &corev1.SecretKeySelector{ - LocalObjectReference: corev1.LocalObjectReference{ - Name: "starboard-trivy-config", - }, - Key: "trivy.serverCustomHeaders", - Optional: pointer.BoolPtr(true), - }, - }, - }, - { - Name: "TRIVY_INSECURE", - Value: "true", - }, - }, - Command: []string{ - "trivy", - }, - Args: []string{ - "--quiet", - "client", - "--format", - "json", - "--remote", - "https://trivy.trivy:4954", - "poc.myregistry.harbor.com.pl/nginx:1.16", - }, - Resources: corev1.ResourceRequirements{ - Requests: corev1.ResourceList{ - corev1.ResourceCPU: resource.MustParse("100m"), - corev1.ResourceMemory: resource.MustParse("100M"), - }, - Limits: corev1.ResourceList{ - corev1.ResourceCPU: resource.MustParse("500m"), - corev1.ResourceMemory: resource.MustParse("500M"), - }, - }, - }, - }, - }, - }, - { - name: "ClientServer mode with non-SSL registry", - config: map[string]string{ - "trivy.imageRef": "docker.io/aquasec/trivy:0.14.0", - "trivy.mode": string(trivy.ClientServer), - "trivy.serverURL": "http://trivy.trivy:4954", - "trivy.nonSslRegistry.pocRegistry": "poc.myregistry.harbor.com.pl", - "trivy.dbRepository": defaultUpdateURL, - "trivy.resources.requests.cpu": "100m", - "trivy.resources.requests.memory": "100M", - "trivy.resources.limits.cpu": "500m", - "trivy.resources.limits.memory": "500M", - }, - workloadSpec: &corev1.Pod{ - TypeMeta: metav1.TypeMeta{ - Kind: "Pod", - APIVersion: "v1", - }, - ObjectMeta: metav1.ObjectMeta{ - Name: "nginx", - Namespace: "prod-ns", - }, - Spec: corev1.PodSpec{ - Containers: []corev1.Container{ - { - Name: "nginx", - Image: "poc.myregistry.harbor.com.pl/nginx:1.16", - }, - }, - }, - }, - expectedJobSpec: corev1.PodSpec{ - Affinity: starboard.LinuxNodeAffinity(), - RestartPolicy: corev1.RestartPolicyNever, - ServiceAccountName: "starboard-sa", - AutomountServiceAccountToken: pointer.BoolPtr(false), - Containers: []corev1.Container{ - { - Name: "nginx", - Image: "docker.io/aquasec/trivy:0.14.0", - ImagePullPolicy: corev1.PullIfNotPresent, - TerminationMessagePolicy: corev1.TerminationMessageFallbackToLogsOnError, - Env: []corev1.EnvVar{ - { - Name: "HTTP_PROXY", - ValueFrom: &corev1.EnvVarSource{ - ConfigMapKeyRef: &corev1.ConfigMapKeySelector{ - LocalObjectReference: corev1.LocalObjectReference{ - Name: "starboard-trivy-config", - }, - Key: "trivy.httpProxy", - Optional: pointer.BoolPtr(true), - }, - }, - }, - { - Name: "HTTPS_PROXY", - ValueFrom: &corev1.EnvVarSource{ - ConfigMapKeyRef: &corev1.ConfigMapKeySelector{ - LocalObjectReference: corev1.LocalObjectReference{ - Name: "starboard-trivy-config", - }, - Key: "trivy.httpsProxy", - Optional: pointer.BoolPtr(true), - }, - }, - }, - { - Name: "NO_PROXY", - ValueFrom: &corev1.EnvVarSource{ - ConfigMapKeyRef: &corev1.ConfigMapKeySelector{ - LocalObjectReference: corev1.LocalObjectReference{ - Name: "starboard-trivy-config", - }, - Key: "trivy.noProxy", - Optional: pointer.BoolPtr(true), - }, - }, - }, - { - Name: "TRIVY_SEVERITY", - ValueFrom: &corev1.EnvVarSource{ - ConfigMapKeyRef: &corev1.ConfigMapKeySelector{ - LocalObjectReference: corev1.LocalObjectReference{ - Name: "starboard-trivy-config", - }, - Key: "trivy.severity", - Optional: pointer.BoolPtr(true), - }, - }, - }, - { - Name: "TRIVY_IGNORE_UNFIXED", - ValueFrom: &corev1.EnvVarSource{ - ConfigMapKeyRef: &corev1.ConfigMapKeySelector{ - LocalObjectReference: corev1.LocalObjectReference{ - Name: "starboard-trivy-config", - }, - Key: "trivy.ignoreUnfixed", - Optional: pointer.BoolPtr(true), - }, - }, - }, - - { - Name: "TRIVY_SKIP_FILES", - ValueFrom: &corev1.EnvVarSource{ - ConfigMapKeyRef: &corev1.ConfigMapKeySelector{ - LocalObjectReference: corev1.LocalObjectReference{ - Name: "starboard-trivy-config", - }, - Key: "trivy.skipFiles", - Optional: pointer.BoolPtr(true), - }, - }, - }, - { - Name: "TRIVY_SKIP_DIRS", - ValueFrom: &corev1.EnvVarSource{ - ConfigMapKeyRef: &corev1.ConfigMapKeySelector{ - LocalObjectReference: corev1.LocalObjectReference{ - Name: "starboard-trivy-config", - }, - Key: "trivy.skipDirs", - Optional: pointer.BoolPtr(true), - }, - }, - }, - { - Name: "TRIVY_TOKEN_HEADER", - ValueFrom: &corev1.EnvVarSource{ - ConfigMapKeyRef: &corev1.ConfigMapKeySelector{ - LocalObjectReference: corev1.LocalObjectReference{ - Name: "starboard-trivy-config", - }, - Key: "trivy.serverTokenHeader", - Optional: pointer.BoolPtr(true), - }, - }, - }, - { - Name: "TRIVY_TOKEN", - ValueFrom: &corev1.EnvVarSource{ - SecretKeyRef: &corev1.SecretKeySelector{ - LocalObjectReference: corev1.LocalObjectReference{ - Name: "starboard-trivy-config", - }, - Key: "trivy.serverToken", - Optional: pointer.BoolPtr(true), - }, - }, - }, - { - Name: "TRIVY_CUSTOM_HEADERS", - ValueFrom: &corev1.EnvVarSource{ - SecretKeyRef: &corev1.SecretKeySelector{ - LocalObjectReference: corev1.LocalObjectReference{ - Name: "starboard-trivy-config", - }, - Key: "trivy.serverCustomHeaders", - Optional: pointer.BoolPtr(true), - }, - }, - }, - { - Name: "TRIVY_NON_SSL", - Value: "true", - }, - }, - Command: []string{ - "trivy", - }, - Args: []string{ - "--quiet", - "client", - "--format", - "json", - "--remote", - "http://trivy.trivy:4954", - "poc.myregistry.harbor.com.pl/nginx:1.16", - }, - Resources: corev1.ResourceRequirements{ - Requests: corev1.ResourceList{ - corev1.ResourceCPU: resource.MustParse("100m"), - corev1.ResourceMemory: resource.MustParse("100M"), - }, - Limits: corev1.ResourceList{ - corev1.ResourceCPU: resource.MustParse("500m"), - corev1.ResourceMemory: resource.MustParse("500M"), - }, - }, - }, - }, - }, - }, - { - name: "ClientServer mode with trivyignore file", - config: map[string]string{ - "trivy.imageRef": "docker.io/aquasec/trivy:0.14.0", - "trivy.mode": string(trivy.ClientServer), - "trivy.serverURL": "http://trivy.trivy:4954", - "trivy.ignoreFile": `# Accept the risk -CVE-2018-14618 - -# No impact in our settings -CVE-2019-1543`, - "trivy.dbRepository": defaultUpdateURL, - "trivy.resources.requests.cpu": "100m", - "trivy.resources.requests.memory": "100M", - "trivy.resources.limits.cpu": "500m", - "trivy.resources.limits.memory": "500M", - }, - workloadSpec: &corev1.Pod{ - TypeMeta: metav1.TypeMeta{ - Kind: "Pod", - APIVersion: "v1", - }, - ObjectMeta: metav1.ObjectMeta{ - Name: "nginx", - Namespace: "prod-ns", - }, - Spec: corev1.PodSpec{ - Containers: []corev1.Container{ - { - Name: "nginx", - Image: "nginx:1.16", - }, - }, - }, - }, - expectedJobSpec: corev1.PodSpec{ - Affinity: starboard.LinuxNodeAffinity(), - RestartPolicy: corev1.RestartPolicyNever, - ServiceAccountName: "starboard-sa", - AutomountServiceAccountToken: pointer.BoolPtr(false), - Volumes: []corev1.Volume{ - { - Name: "ignorefile", - VolumeSource: corev1.VolumeSource{ - ConfigMap: &corev1.ConfigMapVolumeSource{ - LocalObjectReference: corev1.LocalObjectReference{ - Name: "starboard-trivy-config", - }, - Items: []corev1.KeyToPath{ - { - Key: "trivy.ignoreFile", - Path: ".trivyignore", - }, - }, - }, - }, - }, - }, - Containers: []corev1.Container{ - { - Name: "nginx", - Image: "docker.io/aquasec/trivy:0.14.0", - ImagePullPolicy: corev1.PullIfNotPresent, - TerminationMessagePolicy: corev1.TerminationMessageFallbackToLogsOnError, - Env: []corev1.EnvVar{ - { - Name: "HTTP_PROXY", - ValueFrom: &corev1.EnvVarSource{ - ConfigMapKeyRef: &corev1.ConfigMapKeySelector{ - LocalObjectReference: corev1.LocalObjectReference{ - Name: "starboard-trivy-config", - }, - Key: "trivy.httpProxy", - Optional: pointer.BoolPtr(true), - }, - }, - }, - { - Name: "HTTPS_PROXY", - ValueFrom: &corev1.EnvVarSource{ - ConfigMapKeyRef: &corev1.ConfigMapKeySelector{ - LocalObjectReference: corev1.LocalObjectReference{ - Name: "starboard-trivy-config", - }, - Key: "trivy.httpsProxy", - Optional: pointer.BoolPtr(true), - }, - }, - }, - { - Name: "NO_PROXY", - ValueFrom: &corev1.EnvVarSource{ - ConfigMapKeyRef: &corev1.ConfigMapKeySelector{ - LocalObjectReference: corev1.LocalObjectReference{ - Name: "starboard-trivy-config", - }, - Key: "trivy.noProxy", - Optional: pointer.BoolPtr(true), - }, - }, - }, - { - Name: "TRIVY_SEVERITY", - ValueFrom: &corev1.EnvVarSource{ - ConfigMapKeyRef: &corev1.ConfigMapKeySelector{ - LocalObjectReference: corev1.LocalObjectReference{ - Name: "starboard-trivy-config", - }, - Key: "trivy.severity", - Optional: pointer.BoolPtr(true), - }, - }, - }, - { - Name: "TRIVY_IGNORE_UNFIXED", - ValueFrom: &corev1.EnvVarSource{ - ConfigMapKeyRef: &corev1.ConfigMapKeySelector{ - LocalObjectReference: corev1.LocalObjectReference{ - Name: "starboard-trivy-config", - }, - Key: "trivy.ignoreUnfixed", - Optional: pointer.BoolPtr(true), - }, - }, - }, - - { - Name: "TRIVY_SKIP_FILES", - ValueFrom: &corev1.EnvVarSource{ - ConfigMapKeyRef: &corev1.ConfigMapKeySelector{ - LocalObjectReference: corev1.LocalObjectReference{ - Name: "starboard-trivy-config", - }, - Key: "trivy.skipFiles", - Optional: pointer.BoolPtr(true), - }, - }, - }, - { - Name: "TRIVY_SKIP_DIRS", - ValueFrom: &corev1.EnvVarSource{ - ConfigMapKeyRef: &corev1.ConfigMapKeySelector{ - LocalObjectReference: corev1.LocalObjectReference{ - Name: "starboard-trivy-config", - }, - Key: "trivy.skipDirs", - Optional: pointer.BoolPtr(true), - }, - }, - }, - { - Name: "TRIVY_TOKEN_HEADER", - ValueFrom: &corev1.EnvVarSource{ - ConfigMapKeyRef: &corev1.ConfigMapKeySelector{ - LocalObjectReference: corev1.LocalObjectReference{ - Name: "starboard-trivy-config", - }, - Key: "trivy.serverTokenHeader", - Optional: pointer.BoolPtr(true), - }, - }, - }, - { - Name: "TRIVY_TOKEN", - ValueFrom: &corev1.EnvVarSource{ - SecretKeyRef: &corev1.SecretKeySelector{ - LocalObjectReference: corev1.LocalObjectReference{ - Name: "starboard-trivy-config", - }, - Key: "trivy.serverToken", - Optional: pointer.BoolPtr(true), - }, - }, - }, - { - Name: "TRIVY_CUSTOM_HEADERS", - ValueFrom: &corev1.EnvVarSource{ - SecretKeyRef: &corev1.SecretKeySelector{ - LocalObjectReference: corev1.LocalObjectReference{ - Name: "starboard-trivy-config", - }, - Key: "trivy.serverCustomHeaders", - Optional: pointer.BoolPtr(true), - }, - }, - }, - { - Name: "TRIVY_IGNOREFILE", - Value: "/etc/trivy/.trivyignore", - }, - }, - Command: []string{ - "trivy", - }, - Args: []string{ - "--quiet", - "client", - "--format", - "json", - "--remote", - "http://trivy.trivy:4954", - "nginx:1.16", - }, - Resources: corev1.ResourceRequirements{ - Requests: corev1.ResourceList{ - corev1.ResourceCPU: resource.MustParse("100m"), - corev1.ResourceMemory: resource.MustParse("100M"), - }, - Limits: corev1.ResourceList{ - corev1.ResourceCPU: resource.MustParse("500m"), - corev1.ResourceMemory: resource.MustParse("500M"), - }, - }, - VolumeMounts: []corev1.VolumeMount{ - { - Name: "ignorefile", - MountPath: "/etc/trivy/.trivyignore", - SubPath: ".trivyignore", - }, - }, - }, - }, - }, - }, - { - name: "Trivy fs scan command in Standalone mode", - config: map[string]string{ - "trivy.imageRef": "docker.io/aquasec/trivy:0.25.2", - "trivy.mode": string(trivy.Standalone), - "trivy.command": string(trivy.Filesystem), - "trivy.dbRepository": defaultUpdateURL, - "trivy.resources.requests.cpu": "100m", - "trivy.resources.requests.memory": "100M", - "trivy.resources.limits.cpu": "500m", - "trivy.resources.limits.memory": "500M", - }, - workloadSpec: &corev1.Pod{ - TypeMeta: metav1.TypeMeta{ - Kind: "Pod", - APIVersion: "v1", - }, - ObjectMeta: metav1.ObjectMeta{ - Name: "nginx", - Namespace: "prod-ns", - }, - Spec: corev1.PodSpec{ - Containers: []corev1.Container{ - { - Name: "nginx", - Image: "nginx:1.9.1", - }, - }, - NodeName: "kind-control-pane", - }}, - expectedJobSpec: corev1.PodSpec{ - Affinity: starboard.LinuxNodeAffinity(), - RestartPolicy: corev1.RestartPolicyNever, - ServiceAccountName: "starboard-sa", - AutomountServiceAccountToken: pointer.BoolPtr(false), - Volumes: []corev1.Volume{ - { - Name: trivy.FsSharedVolumeName, - VolumeSource: corev1.VolumeSource{ - EmptyDir: &corev1.EmptyDirVolumeSource{ - Medium: corev1.StorageMediumDefault, - }, - }, - }, - }, - InitContainers: []corev1.Container{ - { - Name: "00000000-0000-0000-0000-000000000001", - Image: "docker.io/aquasec/trivy:0.25.2", - ImagePullPolicy: corev1.PullIfNotPresent, - TerminationMessagePolicy: corev1.TerminationMessageFallbackToLogsOnError, - Command: []string{ - "cp", - "-v", - "/usr/local/bin/trivy", - trivy.SharedVolumeLocationOfTrivy, - }, - Resources: corev1.ResourceRequirements{ - Requests: corev1.ResourceList{ - corev1.ResourceCPU: resource.MustParse("100m"), - corev1.ResourceMemory: resource.MustParse("100M"), - }, - Limits: corev1.ResourceList{ - corev1.ResourceCPU: resource.MustParse("500m"), - corev1.ResourceMemory: resource.MustParse("500M"), - }, - }, - VolumeMounts: []corev1.VolumeMount{ - { - Name: trivy.FsSharedVolumeName, - ReadOnly: false, - MountPath: "/var/starboard", - }, - }, - }, - { - Name: "00000000-0000-0000-0000-000000000002", - Image: "docker.io/aquasec/trivy:0.25.2", - ImagePullPolicy: corev1.PullIfNotPresent, - TerminationMessagePolicy: corev1.TerminationMessageFallbackToLogsOnError, - Env: []corev1.EnvVar{ - { - Name: "HTTP_PROXY", - ValueFrom: &corev1.EnvVarSource{ - ConfigMapKeyRef: &corev1.ConfigMapKeySelector{ - LocalObjectReference: corev1.LocalObjectReference{ - Name: "starboard-trivy-config", - }, - Key: "trivy.httpProxy", - Optional: pointer.BoolPtr(true), - }, - }, - }, - { - Name: "HTTPS_PROXY", - ValueFrom: &corev1.EnvVarSource{ - ConfigMapKeyRef: &corev1.ConfigMapKeySelector{ - LocalObjectReference: corev1.LocalObjectReference{ - Name: "starboard-trivy-config", - }, - Key: "trivy.httpsProxy", - Optional: pointer.BoolPtr(true), - }, - }, - }, - { - Name: "NO_PROXY", - ValueFrom: &corev1.EnvVarSource{ - ConfigMapKeyRef: &corev1.ConfigMapKeySelector{ - LocalObjectReference: corev1.LocalObjectReference{ - Name: "starboard-trivy-config", - }, - Key: "trivy.noProxy", - Optional: pointer.BoolPtr(true), - }, - }, - }, - - { - Name: "GITHUB_TOKEN", - ValueFrom: &corev1.EnvVarSource{ - SecretKeyRef: &corev1.SecretKeySelector{ - LocalObjectReference: corev1.LocalObjectReference{ - Name: "starboard-trivy-config", - }, - Key: "trivy.githubToken", - Optional: pointer.BoolPtr(true), - }, - }, - }, - }, - Command: []string{ - "trivy", - }, - Args: []string{ - "--download-db-only", - "--cache-dir", - "/var/starboard/trivy-db", - "--db-repository", defaultUpdateURL, - }, - Resources: corev1.ResourceRequirements{ - Requests: corev1.ResourceList{ - corev1.ResourceCPU: resource.MustParse("100m"), - corev1.ResourceMemory: resource.MustParse("100M"), - }, - Limits: corev1.ResourceList{ - corev1.ResourceCPU: resource.MustParse("500m"), - corev1.ResourceMemory: resource.MustParse("500M"), - }, - }, - VolumeMounts: []corev1.VolumeMount{ - { - Name: trivy.FsSharedVolumeName, - ReadOnly: false, - MountPath: "/var/starboard", - }, - }, - }, - }, - Containers: []corev1.Container{ - { - Name: "nginx", - Image: "nginx:1.9.1", - ImagePullPolicy: corev1.PullNever, - TerminationMessagePolicy: corev1.TerminationMessageFallbackToLogsOnError, - Env: []corev1.EnvVar{ - { - Name: "TRIVY_SEVERITY", - ValueFrom: &corev1.EnvVarSource{ - ConfigMapKeyRef: &corev1.ConfigMapKeySelector{ - LocalObjectReference: corev1.LocalObjectReference{ - Name: "starboard-trivy-config", - }, - Key: "trivy.severity", - Optional: pointer.BoolPtr(true), - }, - }, - }, - { - Name: "TRIVY_SKIP_FILES", - ValueFrom: &corev1.EnvVarSource{ - ConfigMapKeyRef: &corev1.ConfigMapKeySelector{ - LocalObjectReference: corev1.LocalObjectReference{ - Name: "starboard-trivy-config", - }, - Key: "trivy.skipFiles", - Optional: pointer.BoolPtr(true), - }, - }, - }, - { - Name: "TRIVY_SKIP_DIRS", - ValueFrom: &corev1.EnvVarSource{ - ConfigMapKeyRef: &corev1.ConfigMapKeySelector{ - LocalObjectReference: corev1.LocalObjectReference{ - Name: "starboard-trivy-config", - }, - Key: "trivy.skipDirs", - Optional: pointer.BoolPtr(true), - }, - }, - }, - { - Name: "HTTP_PROXY", - ValueFrom: &corev1.EnvVarSource{ - ConfigMapKeyRef: &corev1.ConfigMapKeySelector{ - LocalObjectReference: corev1.LocalObjectReference{ - Name: "starboard-trivy-config", - }, - Key: "trivy.httpProxy", - Optional: pointer.BoolPtr(true), - }, - }, - }, - { - Name: "HTTPS_PROXY", - ValueFrom: &corev1.EnvVarSource{ - ConfigMapKeyRef: &corev1.ConfigMapKeySelector{ - LocalObjectReference: corev1.LocalObjectReference{ - Name: "starboard-trivy-config", - }, - Key: "trivy.httpsProxy", - Optional: pointer.BoolPtr(true), - }, - }, - }, - { - Name: "NO_PROXY", - ValueFrom: &corev1.EnvVarSource{ - ConfigMapKeyRef: &corev1.ConfigMapKeySelector{ - LocalObjectReference: corev1.LocalObjectReference{ - Name: "starboard-trivy-config", - }, - Key: "trivy.noProxy", - Optional: pointer.BoolPtr(true), - }, - }, - }, - }, - Command: []string{ - trivy.SharedVolumeLocationOfTrivy, - }, - Args: []string{ - "--skip-update", - "--cache-dir", - "/var/starboard/trivy-db", - "--quiet", - "fs", - "--format", - "json", - "/", - }, - Resources: corev1.ResourceRequirements{ - Requests: corev1.ResourceList{ - corev1.ResourceCPU: resource.MustParse("100m"), - corev1.ResourceMemory: resource.MustParse("100M"), - }, - Limits: corev1.ResourceList{ - corev1.ResourceCPU: resource.MustParse("500m"), - corev1.ResourceMemory: resource.MustParse("500M"), - }, - }, - VolumeMounts: []corev1.VolumeMount{ - { - Name: trivy.FsSharedVolumeName, - ReadOnly: false, - MountPath: "/var/starboard", - }, - }, - SecurityContext: &corev1.SecurityContext{ - Privileged: pointer.BoolPtr(false), - AllowPrivilegeEscalation: pointer.BoolPtr(false), - Capabilities: &corev1.Capabilities{ - Drop: []corev1.Capability{"all"}, - }, - ReadOnlyRootFilesystem: pointer.BoolPtr(true), - RunAsUser: pointer.Int64(0), - }, - }, - }, - SecurityContext: &corev1.PodSecurityContext{}, - NodeName: "kind-control-pane", - }, - }, - } - - for _, tc := range testCases { - t.Run(tc.name, func(t *testing.T) { - fakeclient := fake.NewClientBuilder().WithObjects( - &corev1.ConfigMap{ - ObjectMeta: metav1.ObjectMeta{ - Name: "starboard-trivy-config", - Namespace: "starboard-ns", - }, - Data: tc.config, + Data: tc.Config, }, ).Build() pluginContext := starboard.NewPluginContext(). @@ -2913,329 +480,13 @@ CVE-2019-1543`, WithNamespace("starboard-ns"). WithServiceAccountName("starboard-sa"). WithClient(fakeclient). - Get() - instance := trivy.NewPlugin(fixedClock, ext.NewSimpleIDGenerator(), fakeclient) - jobSpec, secrets, err := instance.GetScanJobSpec(pluginContext, tc.workloadSpec, nil) - require.NoError(t, err) - assert.Empty(t, secrets) - assert.Equal(t, tc.expectedJobSpec, jobSpec) - }) - } - - testCases = []struct { - name string - config map[string]string - workloadSpec client.Object - expectedSecrets []corev1.Secret - expectedJobSpec corev1.PodSpec - }{{ - name: "Trivy fs scan command in Standalone mode", - config: map[string]string{ - "trivy.imageRef": "docker.io/aquasec/trivy:0.22.0", - "trivy.mode": string(trivy.Standalone), - "trivy.command": string(trivy.Filesystem), - "trivy.dbRepository": defaultUpdateURL, - "trivy.resources.requests.cpu": "100m", - "trivy.resources.requests.memory": "100M", - "trivy.resources.limits.cpu": "500m", - "trivy.resources.limits.memory": "500M", - }, - workloadSpec: &corev1.Pod{ - TypeMeta: metav1.TypeMeta{ - Kind: "Pod", - APIVersion: "v1", - }, - ObjectMeta: metav1.ObjectMeta{ - Name: "nginx", - Namespace: "prod-ns", - }, - Spec: corev1.PodSpec{ - Containers: []corev1.Container{ - { - Name: "nginx", - Image: "nginx:1.9.1", - }, - }, - NodeName: "kind-control-pane", - ServiceAccountName: "nginx-sa", - }}, - expectedJobSpec: corev1.PodSpec{ - Affinity: starboard.LinuxNodeAffinity(), - RestartPolicy: corev1.RestartPolicyNever, - ServiceAccountName: "starboard-sa", - AutomountServiceAccountToken: pointer.BoolPtr(false), - Volumes: []corev1.Volume{ - { - Name: trivy.FsSharedVolumeName, - VolumeSource: corev1.VolumeSource{ - EmptyDir: &corev1.EmptyDirVolumeSource{ - Medium: corev1.StorageMediumDefault, - }, - }, - }, - }, - InitContainers: []corev1.Container{ - { - Name: "00000000-0000-0000-0000-000000000001", - Image: "docker.io/aquasec/trivy:0.22.0", - ImagePullPolicy: corev1.PullIfNotPresent, - TerminationMessagePolicy: corev1.TerminationMessageFallbackToLogsOnError, - Command: []string{ - "cp", - "-v", - "/usr/local/bin/trivy", - trivy.SharedVolumeLocationOfTrivy, - }, - Resources: corev1.ResourceRequirements{ - Requests: corev1.ResourceList{ - corev1.ResourceCPU: resource.MustParse("100m"), - corev1.ResourceMemory: resource.MustParse("100M"), - }, - Limits: corev1.ResourceList{ - corev1.ResourceCPU: resource.MustParse("500m"), - corev1.ResourceMemory: resource.MustParse("500M"), - }, - }, - VolumeMounts: []corev1.VolumeMount{ - { - Name: trivy.FsSharedVolumeName, - ReadOnly: false, - MountPath: "/var/starboard", - }, - }, - }, - { - Name: "00000000-0000-0000-0000-000000000002", - Image: "docker.io/aquasec/trivy:0.22.0", - ImagePullPolicy: corev1.PullIfNotPresent, - TerminationMessagePolicy: corev1.TerminationMessageFallbackToLogsOnError, - Env: []corev1.EnvVar{ - { - Name: "HTTP_PROXY", - ValueFrom: &corev1.EnvVarSource{ - ConfigMapKeyRef: &corev1.ConfigMapKeySelector{ - LocalObjectReference: corev1.LocalObjectReference{ - Name: "starboard-trivy-config", - }, - Key: "trivy.httpProxy", - Optional: pointer.BoolPtr(true), - }, - }, - }, - { - Name: "HTTPS_PROXY", - ValueFrom: &corev1.EnvVarSource{ - ConfigMapKeyRef: &corev1.ConfigMapKeySelector{ - LocalObjectReference: corev1.LocalObjectReference{ - Name: "starboard-trivy-config", - }, - Key: "trivy.httpsProxy", - Optional: pointer.BoolPtr(true), - }, - }, - }, - { - Name: "NO_PROXY", - ValueFrom: &corev1.EnvVarSource{ - ConfigMapKeyRef: &corev1.ConfigMapKeySelector{ - LocalObjectReference: corev1.LocalObjectReference{ - Name: "starboard-trivy-config", - }, - Key: "trivy.noProxy", - Optional: pointer.BoolPtr(true), - }, - }, - }, - - { - Name: "GITHUB_TOKEN", - ValueFrom: &corev1.EnvVarSource{ - SecretKeyRef: &corev1.SecretKeySelector{ - LocalObjectReference: corev1.LocalObjectReference{ - Name: "starboard-trivy-config", - }, - Key: "trivy.githubToken", - Optional: pointer.BoolPtr(true), - }, - }, - }, - }, - Command: []string{ - "trivy", - }, - Args: []string{ - "--download-db-only", - "--cache-dir", - "/var/starboard/trivy-db", - "--db-repository", defaultUpdateURL, - }, - Resources: corev1.ResourceRequirements{ - Requests: corev1.ResourceList{ - corev1.ResourceCPU: resource.MustParse("100m"), - corev1.ResourceMemory: resource.MustParse("100M"), - }, - Limits: corev1.ResourceList{ - corev1.ResourceCPU: resource.MustParse("500m"), - corev1.ResourceMemory: resource.MustParse("500M"), - }, - }, - VolumeMounts: []corev1.VolumeMount{ - { - Name: trivy.FsSharedVolumeName, - ReadOnly: false, - MountPath: "/var/starboard", - }, - }, - }, - }, - Containers: []corev1.Container{ - { - Name: "nginx", - Image: "nginx:1.9.1", - ImagePullPolicy: corev1.PullIfNotPresent, - TerminationMessagePolicy: corev1.TerminationMessageFallbackToLogsOnError, - Env: []corev1.EnvVar{ - { - Name: "TRIVY_SEVERITY", - ValueFrom: &corev1.EnvVarSource{ - ConfigMapKeyRef: &corev1.ConfigMapKeySelector{ - LocalObjectReference: corev1.LocalObjectReference{ - Name: "starboard-trivy-config", - }, - Key: "trivy.severity", - Optional: pointer.BoolPtr(true), - }, - }, - }, - { - Name: "TRIVY_SKIP_FILES", - ValueFrom: &corev1.EnvVarSource{ - ConfigMapKeyRef: &corev1.ConfigMapKeySelector{ - LocalObjectReference: corev1.LocalObjectReference{ - Name: "starboard-trivy-config", - }, - Key: "trivy.skipFiles", - Optional: pointer.BoolPtr(true), - }, - }, - }, - { - Name: "TRIVY_SKIP_DIRS", - ValueFrom: &corev1.EnvVarSource{ - ConfigMapKeyRef: &corev1.ConfigMapKeySelector{ - LocalObjectReference: corev1.LocalObjectReference{ - Name: "starboard-trivy-config", - }, - Key: "trivy.skipDirs", - Optional: pointer.BoolPtr(true), - }, - }, - }, - { - Name: "HTTP_PROXY", - ValueFrom: &corev1.EnvVarSource{ - ConfigMapKeyRef: &corev1.ConfigMapKeySelector{ - LocalObjectReference: corev1.LocalObjectReference{ - Name: "starboard-trivy-config", - }, - Key: "trivy.httpProxy", - Optional: pointer.BoolPtr(true), - }, - }, - }, - { - Name: "HTTPS_PROXY", - ValueFrom: &corev1.EnvVarSource{ - ConfigMapKeyRef: &corev1.ConfigMapKeySelector{ - LocalObjectReference: corev1.LocalObjectReference{ - Name: "starboard-trivy-config", - }, - Key: "trivy.httpsProxy", - Optional: pointer.BoolPtr(true), - }, - }, - }, - { - Name: "NO_PROXY", - ValueFrom: &corev1.EnvVarSource{ - ConfigMapKeyRef: &corev1.ConfigMapKeySelector{ - LocalObjectReference: corev1.LocalObjectReference{ - Name: "starboard-trivy-config", - }, - Key: "trivy.noProxy", - Optional: pointer.BoolPtr(true), - }, - }, - }, - }, - Command: []string{ - trivy.SharedVolumeLocationOfTrivy, - }, - Args: []string{ - "--skip-update", - "--cache-dir", - "/var/starboard/trivy-db", - "--quiet", - "fs", - "--format", - "json", - "/", - }, - Resources: corev1.ResourceRequirements{ - Requests: corev1.ResourceList{ - corev1.ResourceCPU: resource.MustParse("100m"), - corev1.ResourceMemory: resource.MustParse("100M"), - }, - Limits: corev1.ResourceList{ - corev1.ResourceCPU: resource.MustParse("500m"), - corev1.ResourceMemory: resource.MustParse("500M"), - }, - }, - VolumeMounts: []corev1.VolumeMount{ - { - Name: trivy.FsSharedVolumeName, - ReadOnly: false, - MountPath: "/var/starboard", - }, - }, - SecurityContext: &corev1.SecurityContext{ - Privileged: pointer.BoolPtr(false), - AllowPrivilegeEscalation: pointer.BoolPtr(false), - Capabilities: &corev1.Capabilities{ - Drop: []corev1.Capability{"all"}, - }, - ReadOnlyRootFilesystem: pointer.BoolPtr(true), - RunAsUser: pointer.Int64(0), - }, - }, - }, - SecurityContext: &corev1.PodSecurityContext{}, - }, - }} - // Test cases when starboard is enabled with option to run job in the namespace of workload - for _, tc := range testCases { - t.Run(tc.name, func(t *testing.T) { - fakeclient := fake.NewClientBuilder().WithObjects( - &corev1.ConfigMap{ - ObjectMeta: metav1.ObjectMeta{ - Name: "starboard-trivy-config", - Namespace: "starboard-ns", - }, - Data: tc.config, - }, - ).Build() - pluginContext := starboard.NewPluginContext(). - WithName(trivy.Plugin). - WithNamespace("starboard-ns"). - WithServiceAccountName("starboard-sa"). - WithClient(fakeclient). WithStarboardConfig(map[string]string{starboard.KeyVulnerabilityScansInSameNamespace: "true"}). Get() - instance := trivy.NewPlugin(fixedClock, ext.NewSimpleIDGenerator(), fakeclient) - jobSpec, secrets, err := instance.GetScanJobSpec(pluginContext, tc.workloadSpec, nil) + instance := grype.NewPlugin(fixedClock, ext.NewSimpleIDGenerator(), fakeclient) + jobSpec, secrets, err := instance.GetScanJobSpec(pluginContext, tc.WorkloadSpec, nil) require.NoError(t, err) assert.Empty(t, secrets) - assert.Equal(t, tc.expectedJobSpec, jobSpec) + assert.Equal(t, tc.ExpectedJobSpec, jobSpec) }) } } From a5539992a895ed5ecf4f161274aff6843efb7632 Mon Sep 17 00:00:00 2001 From: Kevin Seidel Date: Wed, 20 Apr 2022 12:07:49 +0200 Subject: [PATCH 03/21] added insecure config for grype --- pkg/plugin/grype/README.md | 15 ++++++++++ pkg/plugin/grype/plugin.go | 52 ++++++++++++++++++++++++++------- pkg/plugin/grype/plugin_test.go | 42 ++++++++++++++++++++++++-- 3 files changed, 96 insertions(+), 13 deletions(-) create mode 100644 pkg/plugin/grype/README.md diff --git a/pkg/plugin/grype/README.md b/pkg/plugin/grype/README.md new file mode 100644 index 000000000..357802d4d --- /dev/null +++ b/pkg/plugin/grype/README.md @@ -0,0 +1,15 @@ +# grype plugin for Starboard + +This plugin is derived from the Trivy plugin. + +* [grype](https://github.com/anchore/grype) +* [grype config](https://github.com/anchore/grype#configuration) + +# Notes on configuration +Most of the settings correspond directly to the settings mentioned in grype documentation. +There are two settings that are specific to this plugin: + +key | description +--- | --- +`grype.insecureRegistryPrefixes` | comma separated list of prefixes of registries where TLS verification will be skipped +`grype.nonSSLRegistyPrefixes` | comma separated list of prefixes of registries that use http diff --git a/pkg/plugin/grype/plugin.go b/pkg/plugin/grype/plugin.go index 0953b697b..294f3c688 100644 --- a/pkg/plugin/grype/plugin.go +++ b/pkg/plugin/grype/plugin.go @@ -27,17 +27,19 @@ const ( ) const ( - keyGrypeImageRef = "grype.imageRef" - keyGrypeScheme = "grype.scheme" - keyGrypePath = "grype.path" - keyGrypeOnlyFixed = "grype.onlyFixed" - keyGrypeExcludePaths = "grype.exclude" - keyGrypeHTTPProxy = "grype.httpProxy" - keyGrypeHTTPSProxy = "grype.httpsProxy" - keyGrypeNoProxy = "grype.noProxy" - keyGrypeUpdateURL = "grype.updateURL" - keyGrypeAddMissingCPEs = "grype.addMissingCPEs" - keyGrypeRegAuthority = "grype.regAuthority" + keyGrypeImageRef = "grype.imageRef" + keyGrypeScheme = "grype.scheme" + keyGrypePath = "grype.path" + keyGrypeOnlyFixed = "grype.onlyFixed" + keyGrypeExcludePaths = "grype.exclude" + keyGrypeHTTPProxy = "grype.httpProxy" + keyGrypeHTTPSProxy = "grype.httpsProxy" + keyGrypeNoProxy = "grype.noProxy" + keyGrypeUpdateURL = "grype.updateURL" + keyGrypeAddMissingCPEs = "grype.addMissingCPEs" + keyGrypeRegAuthority = "grype.regAuthority" + keyGrypeInsecureRegistryPrefixes = "grype.insecureRegistryPrefixes" + keyGrypeNonSSLRegistryPrefixes = "grype.nonSSLRegistyPrefixes" keyResourcesRequestsCPU = "grype.resources.requests.cpu" keyResourcesRequestsMemory = "grype.resources.requests.memory" @@ -336,6 +338,34 @@ func (p *plugin) getPodSpec(ctx starboard.PluginContext, config Config, workload }, ) + //check if we have to set insecure settings + if val, ok := config.Data[keyGrypeInsecureRegistryPrefixes]; ok { + for _, prefix := range strings.Split(val, ",") { + if strings.HasPrefix(c.Image, strings.Trim(prefix, " ")) { + env = append(env, + corev1.EnvVar{ + Name: "GRYPE_REGISTRY_INSECURE_SKIP_TLS_VERIFY", + Value: "true", + }, + ) + break + } + } + } + if val, ok := config.Data[keyGrypeNonSSLRegistryPrefixes]; ok { + for _, prefix := range strings.Split(val, ",") { + if strings.HasPrefix(c.Image, strings.Trim(prefix, " ")) { + env = append(env, + corev1.EnvVar{ + Name: "GRYPE_REGISTRY_INSECURE_USE_HTTP", + Value: "true", + }, + ) + break + } + } + } + if _, ok := credentials[c.Name]; ok && secret != nil { registryUsernameKey := fmt.Sprintf("%s.username", c.Name) registryPasswordKey := fmt.Sprintf("%s.password", c.Name) diff --git a/pkg/plugin/grype/plugin_test.go b/pkg/plugin/grype/plugin_test.go index f5b4d2331..e4b7eb8d0 100644 --- a/pkg/plugin/grype/plugin_test.go +++ b/pkg/plugin/grype/plugin_test.go @@ -19,7 +19,6 @@ import ( metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/types" "k8s.io/utils/pointer" - "sigs.k8s.io/controller-runtime/pkg/client" "sigs.k8s.io/controller-runtime/pkg/client/fake" ) @@ -207,7 +206,7 @@ type JobSpecTestCase struct { Config map[string]string ExpectedConfigName string - WorkloadSpec client.Object + WorkloadSpec *corev1.Pod // ExpectedSecrets []corev1.Secret ExpectedJobSpec corev1.PodSpec @@ -463,6 +462,45 @@ func TestPlugin_GetScanJobSpec(t *testing.T) { scheme.ExpectedJobSpec.Containers[0].Args[0] = "podman:nginx:1.16" testCases = append(testCases, scheme) + //test insecure settings + insecure := NewJobSpecTestCase("Should set insecure env var") + insecure.Config["grype.insecureRegistryPrefixes"] = "foo, bar, ba" + insecure.WorkloadSpec.Spec.Containers[0].Image = "bar/nginx:1.16" + insecure.ExpectedJobSpec.Containers[0].Args[0] = "bar/nginx:1.16" + insecure.ExpectedJobSpec.Containers[0].Env = append(insecure.ExpectedJobSpec.Containers[0].Env, + corev1.EnvVar{ + Name: "GRYPE_REGISTRY_INSECURE_SKIP_TLS_VERIFY", + Value: "true", + }, + ) + testCases = append(testCases, insecure) + + //not insecure + notInsecure := NewJobSpecTestCase("Should NOT set insecure env var") + notInsecure.Config["grype.insecureRegistryPrefixes"] = "notFoo, bar" + notInsecure.WorkloadSpec.Spec.Containers[0].Image = "foobar/nginx:1.16" + notInsecure.ExpectedJobSpec.Containers[0].Args[0] = "foobar/nginx:1.16" + testCases = append(testCases, notInsecure) + + //no SSL + noSSL := NewJobSpecTestCase("Should set no SSL env var") + noSSL.Config["grype.nonSSLRegistyPrefixes"] = "foo, http://bar, http://ba" + noSSL.WorkloadSpec.Spec.Containers[0].Image = "http://bar/nginx:1.16" + noSSL.ExpectedJobSpec.Containers[0].Args[0] = "http://bar/nginx:1.16" + noSSL.ExpectedJobSpec.Containers[0].Env = append(noSSL.ExpectedJobSpec.Containers[0].Env, + corev1.EnvVar{ + Name: "GRYPE_REGISTRY_INSECURE_USE_HTTP", + Value: "true", + }, + ) + testCases = append(testCases, noSSL) + + noNoSSL := NewJobSpecTestCase("Should NOT set no SSL env var") + noNoSSL.Config["grype.nonSSLRegistyPrefixes"] = "foo, http://notBar" + noNoSSL.WorkloadSpec.Spec.Containers[0].Image = "http://bar/nginx:1.16" + noNoSSL.ExpectedJobSpec.Containers[0].Args[0] = "http://bar/nginx:1.16" + testCases = append(testCases, noNoSSL) + // Test cases when starboard is enabled with option to run job in the namespace of workload for _, tc := range testCases { t.Run(tc.Name, func(t *testing.T) { From cc468302b476bd4c33437876652075ad3e00efc3 Mon Sep 17 00:00:00 2001 From: Kevin Seidel Date: Wed, 20 Apr 2022 12:26:12 +0200 Subject: [PATCH 04/21] clarification in readme --- pkg/plugin/grype/README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/pkg/plugin/grype/README.md b/pkg/plugin/grype/README.md index 357802d4d..b8d7c8139 100644 --- a/pkg/plugin/grype/README.md +++ b/pkg/plugin/grype/README.md @@ -7,6 +7,7 @@ This plugin is derived from the Trivy plugin. # Notes on configuration Most of the settings correspond directly to the settings mentioned in grype documentation. +In order to set optional parameters, e.g. `grype.onlyFixed`, they have to be set to `true` There are two settings that are specific to this plugin: key | description From 2b3592eb53a6c05f0fd54f043a28570f33479dac Mon Sep 17 00:00:00 2001 From: Kevin Seidel Date: Wed, 20 Apr 2022 12:39:02 +0200 Subject: [PATCH 05/21] cleanup - some warnings fixed --- pkg/plugin/grype/plugin.go | 19 ++----------------- pkg/plugin/grype/plugin_test.go | 5 ----- 2 files changed, 2 insertions(+), 22 deletions(-) diff --git a/pkg/plugin/grype/plugin.go b/pkg/plugin/grype/plugin.go index 294f3c688..aad6cc228 100644 --- a/pkg/plugin/grype/plugin.go +++ b/pkg/plugin/grype/plugin.go @@ -522,8 +522,9 @@ func (p *plugin) ParseVulnerabilityReportData(ctx starboard.PluginContext, image fixVersion = strings.TrimSuffix(fixVersion, ", ") var score *float64 = pointer.Float64Ptr(0) + vulRegex, _ := regexp.Compile(`3\..*`) for _, cvs := range vul.CVSs { - if matched, err := regexp.MatchString("3\\..*", cvs.Version); matched && err == nil { + if vulRegex.Match([]byte(cvs.Version)) { score = cvs.Metrics.BaseScore } } @@ -622,19 +623,3 @@ func (p *plugin) parseImageRef(imageRef string) (v1alpha1.Registry, v1alpha1.Art } return registry, artifact, nil } - -func constructEnvVarSourceFromConfigMap(envName, configName, configKey string) (res corev1.EnvVar) { - res = corev1.EnvVar{ - Name: envName, - ValueFrom: &corev1.EnvVarSource{ - ConfigMapKeyRef: &corev1.ConfigMapKeySelector{ - LocalObjectReference: corev1.LocalObjectReference{ - Name: configName, - }, - Key: configKey, - Optional: pointer.BoolPtr(true), - }, - }, - } - return -} diff --git a/pkg/plugin/grype/plugin_test.go b/pkg/plugin/grype/plugin_test.go index e4b7eb8d0..2c1fb63d5 100644 --- a/pkg/plugin/grype/plugin_test.go +++ b/pkg/plugin/grype/plugin_test.go @@ -194,11 +194,6 @@ const ( keyGrypeUpdateURL = "grype.updateURL" keyGrypeAddMissingCPEs = "grype.addMissingCPEs" keyGrypeRegAuthority = "grype.regAuthority" - - keyResourcesRequestsCPU = "grype.resources.requests.cpu" - keyResourcesRequestsMemory = "grype.resources.requests.memory" - keyResourcesLimitsCPU = "grype.resources.limits.cpu" - keyResourcesLimitsMemory = "grype.resources.limits.memory" ) type JobSpecTestCase struct { From deb8a73bb6c604f21e1adf2280b947cced6b2cd1 Mon Sep 17 00:00:00 2001 From: Kevin Seidel Date: Wed, 20 Apr 2022 14:12:20 +0200 Subject: [PATCH 06/21] feat: read secret registry auth info from secret --- pkg/plugin/grype/plugin.go | 41 +++++++++++++++++++++++++++++++++++++- 1 file changed, 40 insertions(+), 1 deletion(-) diff --git a/pkg/plugin/grype/plugin.go b/pkg/plugin/grype/plugin.go index aad6cc228..6eeb95ba0 100644 --- a/pkg/plugin/grype/plugin.go +++ b/pkg/plugin/grype/plugin.go @@ -38,6 +38,9 @@ const ( keyGrypeUpdateURL = "grype.updateURL" keyGrypeAddMissingCPEs = "grype.addMissingCPEs" keyGrypeRegAuthority = "grype.regAuthority" + keyGrypeRegUsername = "grype.regUsername" + keyGrypeRegPassword = "grype.regPassword" + keyGrypeRegToken = "grype.regToken" keyGrypeInsecureRegistryPrefixes = "grype.insecureRegistryPrefixes" keyGrypeNonSSLRegistryPrefixes = "grype.nonSSLRegistyPrefixes" @@ -311,7 +314,7 @@ func (p *plugin) getPodSpec(ctx starboard.PluginContext, config Config, workload corev1.EnvVar{ Name: "GRYPE_REGISTRY_AUTH_AUTHORITY", ValueFrom: &corev1.EnvVarSource{ - ConfigMapKeyRef: &corev1.ConfigMapKeySelector{ + SecretKeyRef: &corev1.SecretKeySelector{ LocalObjectReference: corev1.LocalObjectReference{ Name: grypeConfigName, }, @@ -320,6 +323,42 @@ func (p *plugin) getPodSpec(ctx starboard.PluginContext, config Config, workload }, }, }, + corev1.EnvVar{ + Name: "GRYPE_REGISTRY_AUTH_USERNAME", + ValueFrom: &corev1.EnvVarSource{ + SecretKeyRef: &corev1.SecretKeySelector{ + LocalObjectReference: corev1.LocalObjectReference{ + Name: grypeConfigName, + }, + Key: keyGrypeRegUsername, + Optional: pointer.BoolPtr(true), + }, + }, + }, + corev1.EnvVar{ + Name: "GRYPE_REGISTRY_AUTH_PASSWORD", + ValueFrom: &corev1.EnvVarSource{ + SecretKeyRef: &corev1.SecretKeySelector{ + LocalObjectReference: corev1.LocalObjectReference{ + Name: grypeConfigName, + }, + Key: keyGrypeRegPassword, + Optional: pointer.BoolPtr(true), + }, + }, + }, + corev1.EnvVar{ + Name: "GRYPE_REGISTRY_AUTH_TOKEN", + ValueFrom: &corev1.EnvVarSource{ + SecretKeyRef: &corev1.SecretKeySelector{ + LocalObjectReference: corev1.LocalObjectReference{ + Name: grypeConfigName, + }, + Key: keyGrypeRegToken, + Optional: pointer.BoolPtr(true), + }, + }, + }, corev1.EnvVar{ Name: "GRYPE_EXCLUDE", ValueFrom: &corev1.EnvVarSource{ From e9a3af0c7768e6702921f9bc53a5999b82a804de Mon Sep 17 00:00:00 2001 From: Kevin Seidel Date: Wed, 20 Apr 2022 14:16:14 +0200 Subject: [PATCH 07/21] feat: added config for grype to helm deployment --- deploy/helm/templates/config.yaml | 92 +++++++++++++++++++++++++++++++ 1 file changed, 92 insertions(+) diff --git a/deploy/helm/templates/config.yaml b/deploy/helm/templates/config.yaml index 39ffe6507..034485daa 100644 --- a/deploy/helm/templates/config.yaml +++ b/deploy/helm/templates/config.yaml @@ -117,6 +117,98 @@ data: {{- end }} {{- end }} {{- end }} + + +{{- if eq .Values.starboard.vulnerabilityReportsPlugin "Grype" }} +{{- with .Values.grype}} +{{- if .createConfig }} +--- +apiVersion: v1 +kind: ConfigMap +metadata: + name: starboard-grype-config + labels: + {{- include "starboard-operator.labels" $ | nindent 4 }} +data: + grype.imageRef: {{ required ".Values.grype.imageRef is required" .imageRef | quote }} + {{- if .scheme }} + grype.scheme: {{ .scheme | quote }} + {{- end }} + {{- if .path }} + grype.path: {{ .path | quote }} + {{- end }} + {{- if .onlyFixed }} + grype.onlyFixed: {{ .onlyFixed | quote }} + {{- end }} + {{- if .excludePaths }} + grype.excludePaths: {{ .excludePaths | quote }} + {{- end }} + {{- if .httpProxy }} + grype.httpProxy: {{ .httpProxy | quote }} + {{- end }} + {{- if .httpsProxy }} + grype.httpsProxy: {{ .httpsProxy | quote }} + {{- end }} + {{- if .noProxy }} + grype.noProxy: {{ .noProxy | quote }} + {{- end }} + {{- if .updateURL }} + grype.updateURL: {{ .updateURL | quote }} + {{- end }} + {{- if .addMissingCPEs }} + grype.addMissingCPEs: {{ .addMissingCPEs | quote }} + {{- end }} + {{- if .updateURL }} + grype.updateURL: {{ .updateURL | quote }} + {{- end }} + {{- if .insecureRegistryPrefixes }} + grype.insecureRegistryPrefixes: {{ .insecureRegistryPrefixes | quote }} + {{- end }} + {{- if .nonSSLRegistriesPrefixes }} + grype.nonSSLRegistriesPrefixes: {{ .nonSSLRegistriesPrefixes | quote }} + {{- end }} + {{- with .resources }} + {{- with .requests }} + {{- if .cpu }} + grype.resources.requests.cpu: {{ .cpu }} + {{- end }} + {{- if hasKey . "memory" }} + grype.resources.requests.memory: {{ .memory }} + {{- end }} + {{- end }} + {{- with .limits }} + {{- if .cpu }} + grype.resources.limits.cpu: {{ .cpu }} + {{- end }} + {{- if .memory }} + grype.resources.limits.memory: {{ .memory }} + {{- end }} + {{- end }} + {{- end }} +--- +apiVersion: v1 +kind: Secret +metadata: + name: starboard-grype-config + labels: + {{- include "starboard-operator.labels" $ | nindent 4 }} +data: + {{- if .regAuthority }} + grype.regAuthority: {{ .regAuthority | b64enc | quote }} + {{- end }} + {{- if .regUsername }} + grype.regUsername: {{ .regUsername | b64enc | quote }} + {{- end }} + {{- if .regPassword }} + grype.regPassword: {{ .regPassword | b64enc | quote }} + {{- end }} + {{- if .regToken }} + grype.regToken: {{ .regToken | b64enc | quote }} + {{- end }} +{{- end }} +{{- end }} +{{- end }} + {{- if eq .Values.starboard.configAuditReportsPlugin "Conftest" }} {{- with .Values.conftest }} {{- if .createConfig }} From 44636b15ccb582421574a3551ba6513daab772fb Mon Sep 17 00:00:00 2001 From: Kevin Seidel Date: Wed, 20 Apr 2022 14:27:51 +0200 Subject: [PATCH 08/21] fix: added missing grype entries helm chart values file --- .gitignore | 2 ++ deploy/helm/values.yaml | 12 ++++++++++++ 2 files changed, 14 insertions(+) diff --git a/.gitignore b/.gitignore index 8a04933d4..49ab338d0 100644 --- a/.gitignore +++ b/.gitignore @@ -17,5 +17,7 @@ site/ ## helm chart output/* .cr-release-packages/* +deploy/helm/*.tgz + ## idea files *.iml \ No newline at end of file diff --git a/deploy/helm/values.yaml b/deploy/helm/values.yaml index 819ae65e7..a638526d6 100644 --- a/deploy/helm/values.yaml +++ b/deploy/helm/values.yaml @@ -179,6 +179,18 @@ trivy: dbRepository: "ghcr.io/aquasecurity/trivy-db" +grype: + createConfig: true + imageRef: "anchore/grype:0.34.7" + updateURL: "https://toolbox-data.anchore.io/grype/databases/listing.json" + resources: + requests: + cpu: 100m + memory: 100M + limits: + cpu: 500m + memory: 500M + compliance: # failEntriesLimit the flag to limit the number of fail entries per control check in the cluster compliance detail report failEntriesLimit: 10 From c8405300a2523efb420d64b6551aed02b6c1ce49 Mon Sep 17 00:00:00 2001 From: Kevin Seidel Date: Tue, 19 Apr 2022 12:25:20 +0200 Subject: [PATCH 09/21] added draft for Grype plugin, vul report parsing is tested --- pkg/plugin/grype/doc.go | 2 + pkg/plugin/grype/model.go | 55 + pkg/plugin/grype/plugin.go | 611 +++++ pkg/plugin/grype/plugin_test.go | 3708 +++++++++++++++++++++++++++++++ 4 files changed, 4376 insertions(+) create mode 100644 pkg/plugin/grype/doc.go create mode 100644 pkg/plugin/grype/model.go create mode 100644 pkg/plugin/grype/plugin.go create mode 100644 pkg/plugin/grype/plugin_test.go diff --git a/pkg/plugin/grype/doc.go b/pkg/plugin/grype/doc.go new file mode 100644 index 000000000..980b6af17 --- /dev/null +++ b/pkg/plugin/grype/doc.go @@ -0,0 +1,2 @@ +// Package grype provides primitives for working with Trivy. +package grype diff --git a/pkg/plugin/grype/model.go b/pkg/plugin/grype/model.go new file mode 100644 index 000000000..d2f7d17fa --- /dev/null +++ b/pkg/plugin/grype/model.go @@ -0,0 +1,55 @@ +package grype + +type ScanReport struct { + Matches []Match `json:"matches"` + Source Source `json:"source"` + Descriptor Descriptor `json:"descriptor"` +} + +type Match struct { + Vulnerability Vulnerability `json:"vulnerability"` + Artifact Artifact `json:"artifact"` +} + +type Vulnerability struct { + Id string `json:"id"` + DataSource string `json:"dataSource"` + Severity string `json:"severity"` + URLs []string `json:"urls"` + Description string `json:"description"` + CVSs []CVS `json:"cvss"` + Fix Fix `json:"fix"` +} + +type Artifact struct { + Name string `json:"name"` + Version string `json:"version"` +} + +type Fix struct { + Versions []string `json:"versions"` + State string `json:"state"` +} + +type CVS struct { + Version string `json:"version"` + Metrics CVSMetrics `json:"metrics"` +} + +type CVSMetrics struct { + BaseScore *float64 `json:"baseScore"` +} + +type Source struct { + Target Target `json:"target"` +} + +type Target struct { + UserInput string `json:"userInput"` + ManifestDigest string `json:"manifestDigest"` +} + +type Descriptor struct { + Name string `json:"name"` + Version string `json:"version"` +} diff --git a/pkg/plugin/grype/plugin.go b/pkg/plugin/grype/plugin.go new file mode 100644 index 000000000..66f40c8f1 --- /dev/null +++ b/pkg/plugin/grype/plugin.go @@ -0,0 +1,611 @@ +package grype + +import ( + "encoding/json" + "fmt" + "io" + "regexp" + "strings" + + "github.com/aquasecurity/starboard/pkg/apis/aquasecurity/v1alpha1" + "github.com/aquasecurity/starboard/pkg/docker" + "github.com/aquasecurity/starboard/pkg/ext" + "github.com/aquasecurity/starboard/pkg/kube" + "github.com/aquasecurity/starboard/pkg/starboard" + "github.com/aquasecurity/starboard/pkg/vulnerabilityreport" + "github.com/google/go-containerregistry/pkg/name" + corev1 "k8s.io/api/core/v1" + "k8s.io/apimachinery/pkg/api/resource" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/utils/pointer" + "sigs.k8s.io/controller-runtime/pkg/client" +) + +const ( + // Plugin the name of this plugin. + Plugin = "Grype" +) + +const ( + keyGrypeImageRef = "grype.imageRef" + keyGrypeScheme = "grype.scheme" + keyGrypePath = "grype.path" + keyGrypeOnlyFixed = "grype.onlyFixed" + keyGrypeExcludePaths = "grype.exclude" + keyGrypeHTTPProxy = "grype.httpProxy" + keyGrypeHTTPSProxy = "grype.httpsProxy" + keyGrypeNoProxy = "grype.noProxy" + keyGrypeUpdateURL = "grype.updateURL" + keyGrypeAddMissingCPEs = "grype.addMissingCPEs" + keyGrypeRegAuthority = "grype.regAuthority" + + keyResourcesRequestsCPU = "grype.resources.requests.cpu" + keyResourcesRequestsMemory = "grype.resources.requests.memory" + keyResourcesLimitsCPU = "grype.resources.limits.cpu" + keyResourcesLimitsMemory = "grype.resources.limits.memory" +) + +const defaultUpdateURL = "https://toolbox-data.anchore.io/grype/databases/listing.json" + +// Config defines configuration params for this plugin. +type Config struct { + starboard.PluginConfig +} + +// GetResourceRequirements creates ResourceRequirements from the Config. +func (c Config) GetResourceRequirements() (corev1.ResourceRequirements, error) { + requirements := corev1.ResourceRequirements{ + Requests: corev1.ResourceList{}, + Limits: corev1.ResourceList{}, + } + + err := c.setResourceLimit(keyResourcesRequestsCPU, &requirements.Requests, corev1.ResourceCPU) + if err != nil { + return requirements, err + } + + err = c.setResourceLimit(keyResourcesRequestsMemory, &requirements.Requests, corev1.ResourceMemory) + if err != nil { + return requirements, err + } + + err = c.setResourceLimit(keyResourcesLimitsCPU, &requirements.Limits, corev1.ResourceCPU) + if err != nil { + return requirements, err + } + + err = c.setResourceLimit(keyResourcesLimitsMemory, &requirements.Limits, corev1.ResourceMemory) + if err != nil { + return requirements, err + } + + return requirements, nil +} + +func (c Config) setResourceLimit(configKey string, k8sResourceList *corev1.ResourceList, k8sResourceName corev1.ResourceName) error { + if value, found := c.Data[configKey]; found { + quantity, err := resource.ParseQuantity(value) + if err != nil { + return fmt.Errorf("parsing resource definition %s: %s %w", configKey, value, err) + } + + (*k8sResourceList)[k8sResourceName] = quantity + } + return nil +} + +type plugin struct { + clock ext.Clock + idGenerator ext.IDGenerator + objectResolver *kube.ObjectResolver +} + +// NewPlugin constructs a new vulnerabilityreport.Plugin, which is using an +// upstream Grype container image to scan Kubernetes workloads. +// +// The plugin supports Image and Filesystem commands. The Filesystem command may +// be used to scan workload images cached on cluster nodes by scheduling +// scan jobs on a particular node. +// +// The Image command supports both Standalone and ClientServer modes depending +// on the settings returned by Config.GetMode. The ClientServer mode is usually +// more performant, however it requires a Grype server accessible at the +// configurable Config.GetServerURL. +func NewPlugin(clock ext.Clock, idGenerator ext.IDGenerator, client client.Client) vulnerabilityreport.Plugin { + return &plugin{ + clock: clock, + idGenerator: idGenerator, + objectResolver: &kube.ObjectResolver{Client: client}, + } +} + +// Init ensures the default Config required by this plugin. +func (p *plugin) Init(ctx starboard.PluginContext) error { + return ctx.EnsureConfig(starboard.PluginConfig{ + Data: map[string]string{ + keyGrypeImageRef: "anchore/grype:0.34.7", + keyGrypeUpdateURL: defaultUpdateURL, + + keyResourcesRequestsCPU: "100m", + keyResourcesRequestsMemory: "100M", + keyResourcesLimitsCPU: "500m", + keyResourcesLimitsMemory: "500M", + }, + }) +} + +func (p *plugin) GetScanJobSpec(ctx starboard.PluginContext, workload client.Object, credentials map[string]docker.Auth) (corev1.PodSpec, []*corev1.Secret, error) { + config, err := p.newConfigFrom(ctx) + if err != nil { + return corev1.PodSpec{}, nil, err + } + + return p.getPodSpec(ctx, config, workload, credentials) +} + +func (p *plugin) newSecretWithAggregateImagePullCredentials(obj client.Object, spec corev1.PodSpec, credentials map[string]docker.Auth) *corev1.Secret { + containerImages := kube.GetContainerImagesFromPodSpec(spec) + secretData := kube.AggregateImagePullSecretsData(containerImages, credentials) + + return &corev1.Secret{ + ObjectMeta: metav1.ObjectMeta{ + Name: vulnerabilityreport.RegistryCredentialsSecretName(obj), + }, + Data: secretData, + } +} + +const ( + tmpVolumeName = "tmp" + ignoreFileVolumeName = "ignorefile" + FsSharedVolumeName = "starboard" + grypeDBLocation = "/tmp/grypedb" +) + +// In the Standalone mode there is the init container responsible for +// downloading the latest Grype DB file from GitHub and storing it to the +// emptyDir volume shared with main containers. In other words, the init +// container runs the following Grype command: +// +// grype --cache-dir /tmp/grype/.cache image --download-db-only +// +// The number of main containers correspond to the number of containers +// defined for the scanned workload. Each container runs the Grype image scan +// command and skips the database download: +// +// grype --cache-dir /tmp/grype/.cache image --skip-update \ +// --format json +func (p *plugin) getPodSpec(ctx starboard.PluginContext, config Config, workload client.Object, credentials map[string]docker.Auth) (corev1.PodSpec, []*corev1.Secret, error) { + var secret *corev1.Secret + var secrets []*corev1.Secret + + spec, err := kube.GetPodSpec(workload) + if err != nil { + return corev1.PodSpec{}, nil, err + } + + if len(credentials) > 0 { + secret = p.newSecretWithAggregateImagePullCredentials(workload, spec, credentials) + secrets = append(secrets, secret) + } + + grypeImageRef, err := config.GetRequiredData(keyGrypeImageRef) + if err != nil { + return corev1.PodSpec{}, nil, err + } + + grypeConfigName := starboard.GetPluginConfigMapName(Plugin) + + requirements, err := config.GetResourceRequirements() + if err != nil { + return corev1.PodSpec{}, nil, err + } + + volumeMounts := []corev1.VolumeMount{ + { + Name: tmpVolumeName, + ReadOnly: false, + MountPath: grypeDBLocation, + }, + } + volumes := []corev1.Volume{ + { + Name: tmpVolumeName, + VolumeSource: corev1.VolumeSource{ + EmptyDir: &corev1.EmptyDirVolumeSource{ + Medium: corev1.StorageMediumDefault, + }, + }, + }, + } + + commonEnv := []corev1.EnvVar{ + { + Name: "HTTP_PROXY", + ValueFrom: &corev1.EnvVarSource{ + ConfigMapKeyRef: &corev1.ConfigMapKeySelector{ + LocalObjectReference: corev1.LocalObjectReference{ + Name: grypeConfigName, + }, + Key: keyGrypeHTTPProxy, + Optional: pointer.BoolPtr(true), + }, + }, + }, + { + Name: "HTTPS_PROXY", + ValueFrom: &corev1.EnvVarSource{ + ConfigMapKeyRef: &corev1.ConfigMapKeySelector{ + LocalObjectReference: corev1.LocalObjectReference{ + Name: grypeConfigName, + }, + Key: keyGrypeHTTPSProxy, + Optional: pointer.BoolPtr(true), + }, + }, + }, + { + Name: "NO_PROXY", + ValueFrom: &corev1.EnvVarSource{ + ConfigMapKeyRef: &corev1.ConfigMapKeySelector{ + LocalObjectReference: corev1.LocalObjectReference{ + Name: grypeConfigName, + }, + Key: keyGrypeNoProxy, + Optional: pointer.BoolPtr(true), + }, + }, + }, + { + Name: "GRYPE_DB_UPDATE_URL", + ValueFrom: &corev1.EnvVarSource{ + ConfigMapKeyRef: &corev1.ConfigMapKeySelector{ + LocalObjectReference: corev1.LocalObjectReference{ + Name: grypeConfigName, + }, + Key: keyGrypeUpdateURL, + Optional: pointer.BoolPtr(false), + }, + }, + }, + { + Name: "GRYPE_DB_CACHE_DIR", + Value: grypeDBLocation, + }, + } + + initContainer := corev1.Container{ + Name: p.idGenerator.GenerateID(), + Image: grypeImageRef, + ImagePullPolicy: corev1.PullIfNotPresent, + TerminationMessagePolicy: corev1.TerminationMessageFallbackToLogsOnError, + Env: commonEnv, + Command: []string{ + "grype", + }, + Args: []string{ + "db", + "update", + }, + Resources: requirements, + VolumeMounts: volumeMounts, + } + + var containers []corev1.Container + + for _, c := range spec.Containers { + + env := append(commonEnv, + corev1.EnvVar{ + Name: "GRYPE_DB_UPDATE_URL", + ValueFrom: &corev1.EnvVarSource{ + ConfigMapKeyRef: &corev1.ConfigMapKeySelector{ + LocalObjectReference: corev1.LocalObjectReference{ + Name: grypeConfigName, + }, + Key: keyGrypeUpdateURL, + Optional: pointer.BoolPtr(false), + }, + }, + }, + corev1.EnvVar{ + Name: "GRYPE_REGISTRY_AUTH_AUTHORITY", + ValueFrom: &corev1.EnvVarSource{ + ConfigMapKeyRef: &corev1.ConfigMapKeySelector{ + LocalObjectReference: corev1.LocalObjectReference{ + Name: grypeConfigName, + }, + Key: keyGrypeRegAuthority, + Optional: pointer.BoolPtr(true), + }, + }, + }, + corev1.EnvVar{ + Name: "GRYPE_EXCLUDE", + ValueFrom: &corev1.EnvVarSource{ + ConfigMapKeyRef: &corev1.ConfigMapKeySelector{ + LocalObjectReference: corev1.LocalObjectReference{ + Name: grypeConfigName, + }, + Key: keyGrypeExcludePaths, + Optional: pointer.BoolPtr(true), + }, + }, + }, + corev1.EnvVar{ + Name: "GRYPE_DB_AUTO_UPDATE", + Value: "false", + }, + ) + + if _, ok := credentials[c.Name]; ok && secret != nil { + registryUsernameKey := fmt.Sprintf("%s.username", c.Name) + registryPasswordKey := fmt.Sprintf("%s.password", c.Name) + + env = append(env, corev1.EnvVar{ + Name: "GRYPE_REGISTRY_AUTH_USERNAME", + ValueFrom: &corev1.EnvVarSource{ + SecretKeyRef: &corev1.SecretKeySelector{ + LocalObjectReference: corev1.LocalObjectReference{ + Name: secret.Name, + }, + Key: registryUsernameKey, + }, + }, + }, corev1.EnvVar{ + Name: "GRYPE_REGISTRY_AUTH_PASSWORD", + ValueFrom: &corev1.EnvVarSource{ + SecretKeyRef: &corev1.SecretKeySelector{ + LocalObjectReference: corev1.LocalObjectReference{ + Name: secret.Name, + }, + Key: registryPasswordKey, + }, + }, + }) + } + + // env, err = p.appendGrypeInsecureEnv(config, c.Image, env) + // if err != nil { + // return corev1.PodSpec{}, nil, err + // } + + // env, err = p.appendGrypeNonSSLEnv(config, c.Image, env) + // if err != nil { + // return corev1.PodSpec{}, nil, err + // } + + args := []string{ + c.Image, + "--skip-update", + "--quiet", + "--output", + "json", + } + + if args, err = p.appendGrypeOptionalArg(config, args, "--add-cpes-if-none", keyGrypeAddMissingCPEs); err != nil { + return corev1.PodSpec{}, nil, err + } + if args, err = p.appendGrypeOptionalArg(config, args, "--only-fixed", keyGrypeOnlyFixed); err != nil { + return corev1.PodSpec{}, nil, err + } + + containers = append(containers, corev1.Container{ + Name: c.Name, + Image: grypeImageRef, + ImagePullPolicy: corev1.PullIfNotPresent, + TerminationMessagePolicy: corev1.TerminationMessageFallbackToLogsOnError, + Env: env, + Command: []string{ + "grype", + }, + Args: args, + Resources: requirements, + VolumeMounts: volumeMounts, + SecurityContext: &corev1.SecurityContext{ + Privileged: pointer.BoolPtr(false), + AllowPrivilegeEscalation: pointer.BoolPtr(false), + Capabilities: &corev1.Capabilities{ + Drop: []corev1.Capability{"all"}, + }, + ReadOnlyRootFilesystem: pointer.BoolPtr(true), + }, + }) + } + + return corev1.PodSpec{ + Affinity: starboard.LinuxNodeAffinity(), + RestartPolicy: corev1.RestartPolicyNever, + ServiceAccountName: ctx.GetServiceAccountName(), + AutomountServiceAccountToken: pointer.BoolPtr(false), + Volumes: volumes, + InitContainers: []corev1.Container{initContainer}, + Containers: containers, + SecurityContext: &corev1.PodSecurityContext{}, + }, secrets, nil +} + +func (p *plugin) appendGrypeOptionalArg(config Config, args []string, arg string, key string) ([]string, error) { + if val, ok := config.Data[key]; ok && val == "true" { + return append(args, arg), nil + } else if !ok { + return args, fmt.Errorf("Invalid config key provided: %s", key) + } else { + return args, nil + } +} + +// func (p *plugin) appendGrypeInsecureEnv(config Config, image string, env []corev1.EnvVar) ([]corev1.EnvVar, error) { +// ref, err := name.ParseReference(image) +// if err != nil { +// return nil, err +// } + +// insecureRegistries := config.GetInsecureRegistries() +// if insecureRegistries[ref.Context().RegistryStr()] { +// env = append(env, corev1.EnvVar{ +// Name: "GRYPE_REGISTRY_INSECURE_SKIP_TLS_VERIFY", +// Value: "true", +// }) +// } + +// return env, nil +// } + +// func (p *plugin) appendGrypeNonSSLEnv(config Config, image string, env []corev1.EnvVar) ([]corev1.EnvVar, error) { +// ref, err := name.ParseReference(image) +// if err != nil { +// return nil, err +// } + +// nonSSLRegistries := config.GetNonSSLRegistries() +// if nonSSLRegistries[ref.Context().RegistryStr()] { +// env = append(env, corev1.EnvVar{ +// Name: "GRYPE_REGISTRY_INSECURE_USE_HTTP", +// Value: "true", +// }) +// } + +// return env, nil +// } + +func (p *plugin) ParseVulnerabilityReportData(ctx starboard.PluginContext, imageRef string, logsReader io.ReadCloser) (v1alpha1.VulnerabilityReportData, error) { + config, err := p.newConfigFrom(ctx) + if err != nil { + return v1alpha1.VulnerabilityReportData{}, err + } + var report ScanReport + err = json.NewDecoder(logsReader).Decode(&report) + if err != nil { + return v1alpha1.VulnerabilityReportData{}, err + } + vulnerabilities := make([]v1alpha1.Vulnerability, 0) + + for _, match := range report.Matches { + vul := match.Vulnerability + artifact := match.Artifact + + fixVersion := "" + for _, version := range match.Vulnerability.Fix.Versions { + fixVersion += version + fixVersion += ", " + } + fixVersion = strings.TrimSuffix(fixVersion, ", ") + + var score *float64 = pointer.Float64Ptr(0) + for _, cvs := range vul.CVSs { + if matched, err := regexp.MatchString("3\\..*", cvs.Version); matched && err == nil { + score = cvs.Metrics.BaseScore + } + } + + var severity v1alpha1.Severity + if severity, err = v1alpha1.StringToSeverity(vul.Severity); err != nil { + severity = v1alpha1.Severity("UNKNOWN") + } + + vulnerabilities = append(vulnerabilities, v1alpha1.Vulnerability{ + VulnerabilityID: vul.Id, + Resource: artifact.Name, + InstalledVersion: artifact.Version, + FixedVersion: fixVersion, + Severity: severity, + Title: artifact.Name, + PrimaryLink: vul.DataSource, + Description: vul.Description, + Links: vul.URLs, + Score: score, + }) + } + + registry, artifact, err := p.parseImageRef(imageRef) + if err != nil { + return v1alpha1.VulnerabilityReportData{}, err + } + + grypeImageRef, err := config.GetRequiredData(keyGrypeImageRef) + if err != nil { + return v1alpha1.VulnerabilityReportData{}, err + } + + version, err := starboard.GetVersionFromImageRef(grypeImageRef) + if err != nil { + return v1alpha1.VulnerabilityReportData{}, err + } + + return v1alpha1.VulnerabilityReportData{ + UpdateTimestamp: metav1.NewTime(p.clock.Now()), + Scanner: v1alpha1.Scanner{ + Name: "Grype", + Vendor: "Anchore Inc.", + Version: version, + }, + Registry: registry, + Artifact: artifact, + Summary: p.toSummary(vulnerabilities), + Vulnerabilities: vulnerabilities, + }, nil +} + +func (p *plugin) newConfigFrom(ctx starboard.PluginContext) (Config, error) { + pluginConfig, err := ctx.GetConfig() + if err != nil { + return Config{}, err + } + return Config{PluginConfig: pluginConfig}, nil +} + +func (p *plugin) toSummary(vulnerabilities []v1alpha1.Vulnerability) v1alpha1.VulnerabilitySummary { + var vs v1alpha1.VulnerabilitySummary + for _, v := range vulnerabilities { + switch v.Severity { + case v1alpha1.SeverityCritical: + vs.CriticalCount++ + case v1alpha1.SeverityHigh: + vs.HighCount++ + case v1alpha1.SeverityMedium: + vs.MediumCount++ + case v1alpha1.SeverityLow: + vs.LowCount++ + default: + vs.UnknownCount++ + } + } + return vs +} + +func (p *plugin) parseImageRef(imageRef string) (v1alpha1.Registry, v1alpha1.Artifact, error) { + ref, err := name.ParseReference(imageRef) + if err != nil { + return v1alpha1.Registry{}, v1alpha1.Artifact{}, err + } + registry := v1alpha1.Registry{ + Server: ref.Context().RegistryStr(), + } + artifact := v1alpha1.Artifact{ + Repository: ref.Context().RepositoryStr(), + } + switch t := ref.(type) { + case name.Tag: + artifact.Tag = t.TagStr() + case name.Digest: + artifact.Digest = t.DigestStr() + } + return registry, artifact, nil +} + +func constructEnvVarSourceFromConfigMap(envName, configName, configKey string) (res corev1.EnvVar) { + res = corev1.EnvVar{ + Name: envName, + ValueFrom: &corev1.EnvVarSource{ + ConfigMapKeyRef: &corev1.ConfigMapKeySelector{ + LocalObjectReference: corev1.LocalObjectReference{ + Name: configName, + }, + Key: configKey, + Optional: pointer.BoolPtr(true), + }, + }, + } + return +} diff --git a/pkg/plugin/grype/plugin_test.go b/pkg/plugin/grype/plugin_test.go new file mode 100644 index 000000000..b16477198 --- /dev/null +++ b/pkg/plugin/grype/plugin_test.go @@ -0,0 +1,3708 @@ +package grype_test + +import ( + "context" + "errors" + "io" + "strings" + "testing" + "time" + + "github.com/aquasecurity/starboard/pkg/apis/aquasecurity/v1alpha1" + "github.com/aquasecurity/starboard/pkg/ext" + "github.com/aquasecurity/starboard/pkg/plugin/grype" + "github.com/aquasecurity/starboard/pkg/plugin/trivy" + "github.com/aquasecurity/starboard/pkg/starboard" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + appsv1 "k8s.io/api/apps/v1" + corev1 "k8s.io/api/core/v1" + "k8s.io/apimachinery/pkg/api/resource" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/types" + "k8s.io/utils/pointer" + "sigs.k8s.io/controller-runtime/pkg/client" + "sigs.k8s.io/controller-runtime/pkg/client/fake" +) + +var ( + fixedTime = time.Now() + fixedClock = ext.NewFixedClock(fixedTime) +) + +const defaultUpdateURL = "https://toolbox-data.anchore.io/grype/databases/listing.json" + +func TestConfig_GetImageRef(t *testing.T) { + testCases := []struct { + name string + configData trivy.Config + expectedError string + expectedImageRef string + }{ + { + name: "Should return error", + configData: trivy.Config{PluginConfig: starboard.PluginConfig{}}, + expectedError: "property trivy.imageRef not set", + }, + { + name: "Should return image reference from config data", + configData: trivy.Config{PluginConfig: starboard.PluginConfig{ + Data: map[string]string{ + "trivy.imageRef": "gcr.io/aquasecurity/trivy:0.8.0", + }, + }}, + expectedImageRef: "gcr.io/aquasecurity/trivy:0.8.0", + }, + } + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + imageRef, err := tc.configData.GetImageRef() + if tc.expectedError != "" { + require.EqualError(t, err, tc.expectedError) + } else { + require.NoError(t, err) + assert.Equal(t, tc.expectedImageRef, imageRef) + } + }) + } +} + +func TestConfig_GetResourceRequirements(t *testing.T) { + testCases := []struct { + name string + config trivy.Config + expectedError string + expectedRequirements corev1.ResourceRequirements + }{ + { + name: "Should return empty requirements by default", + config: trivy.Config{ + PluginConfig: starboard.PluginConfig{}, + }, + expectedError: "", + expectedRequirements: corev1.ResourceRequirements{ + Requests: corev1.ResourceList{}, + Limits: corev1.ResourceList{}, + }, + }, + { + name: "Should return configured resource requirement", + config: trivy.Config{ + PluginConfig: starboard.PluginConfig{ + Data: map[string]string{ + "trivy.dbRepository": defaultUpdateURL, + "trivy.resources.requests.cpu": "800m", + "trivy.resources.requests.memory": "200M", + "trivy.resources.limits.cpu": "600m", + "trivy.resources.limits.memory": "700M", + }, + }, + }, + expectedError: "", + expectedRequirements: corev1.ResourceRequirements{ + Requests: corev1.ResourceList{ + corev1.ResourceCPU: resource.MustParse("800m"), + corev1.ResourceMemory: resource.MustParse("200M"), + }, + Limits: corev1.ResourceList{ + corev1.ResourceCPU: resource.MustParse("600m"), + corev1.ResourceMemory: resource.MustParse("700M"), + }, + }, + }, + { + name: "Should return error if resource is not parseable", + config: trivy.Config{ + PluginConfig: starboard.PluginConfig{ + Data: map[string]string{ + "trivy.resources.requests.cpu": "roughly 100", + }, + }, + }, + expectedError: "parsing resource definition trivy.resources.requests.cpu: roughly 100 quantities must match the regular expression '^([+-]?[0-9.]+)([eEinumkKMGTP]*[-+]?[0-9]*)$'", + }, + } + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + resourceRequirement, err := tc.config.GetResourceRequirements() + if tc.expectedError != "" { + require.EqualError(t, err, tc.expectedError) + } else { + require.NoError(t, err) + assert.Equal(t, tc.expectedRequirements, resourceRequirement, tc.name) + } + }) + } +} + +func TestConfig_IgnoreUnfixed(t *testing.T) { + testCases := []struct { + name string + configData trivy.Config + expectedOutput bool + }{ + { + name: "Should return false", + configData: trivy.Config{PluginConfig: starboard.PluginConfig{ + Data: map[string]string{ + "foo": "bar", + }, + }}, + expectedOutput: false, + }, + { + name: "Should return true", + configData: trivy.Config{PluginConfig: starboard.PluginConfig{ + Data: map[string]string{ + "foo": "bar", + "trivy.ignoreUnfixed": "true", + }, + }}, + expectedOutput: true, + }, + { + name: "Should return false when set it as false", + configData: trivy.Config{PluginConfig: starboard.PluginConfig{ + Data: map[string]string{ + "foo": "bar", + "trivy.ignoreUnfixed": "false", + }, + }}, + expectedOutput: true, + }, + } + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + exists := tc.configData.IgnoreUnfixed() + assert.Equal(t, tc.expectedOutput, exists) + }) + } +} + +func TestPlugin_Init(t *testing.T) { + + t.Run("Should create the default config", func(t *testing.T) { + client := fake.NewClientBuilder().WithObjects().Build() + + instance := trivy.NewPlugin(fixedClock, ext.NewSimpleIDGenerator(), client) + + pluginContext := starboard.NewPluginContext(). + WithName(trivy.Plugin). + WithNamespace("starboard-ns"). + WithServiceAccountName("starboard-sa"). + WithClient(client). + Get() + err := instance.Init(pluginContext) + require.NoError(t, err) + + var cm corev1.ConfigMap + err = client.Get(context.Background(), types.NamespacedName{ + Namespace: "starboard-ns", + Name: "starboard-trivy-config", + }, &cm) + require.NoError(t, err) + assert.Equal(t, corev1.ConfigMap{ + TypeMeta: metav1.TypeMeta{ + APIVersion: "v1", + Kind: "ConfigMap", + }, + ObjectMeta: metav1.ObjectMeta{ + Name: "starboard-trivy-config", + Namespace: "starboard-ns", + Labels: map[string]string{ + "app.kubernetes.io/managed-by": "starboard", + }, + ResourceVersion: "1", + }, + Data: map[string]string{ + "trivy.imageRef": "docker.io/aquasec/trivy:0.25.2", + "trivy.severity": "UNKNOWN,LOW,MEDIUM,HIGH,CRITICAL", + "trivy.mode": "Standalone", + "trivy.timeout": "5m0s", + "trivy.dbRepository": defaultUpdateURL, + + "trivy.resources.requests.cpu": "100m", + "trivy.resources.requests.memory": "100M", + "trivy.resources.limits.cpu": "500m", + "trivy.resources.limits.memory": "500M", + }, + }, cm) + }) + + t.Run("Should not overwrite existing config", func(t *testing.T) { + client := fake.NewClientBuilder().WithObjects( + &corev1.ConfigMap{ + TypeMeta: metav1.TypeMeta{ + APIVersion: "v1", + Kind: "ConfigMap", + }, + ObjectMeta: metav1.ObjectMeta{ + Name: "starboard-trivy-config", + Namespace: "starboard-ns", + ResourceVersion: "1", + }, + Data: map[string]string{ + "trivy.imageRef": "docker.io/aquasec/trivy:0.25.2", + "trivy.severity": "UNKNOWN,LOW,MEDIUM,HIGH,CRITICAL", + "trivy.mode": "Standalone", + }, + }).Build() + + instance := trivy.NewPlugin(fixedClock, ext.NewSimpleIDGenerator(), client) + + pluginContext := starboard.NewPluginContext(). + WithName(trivy.Plugin). + WithNamespace("starboard-ns"). + WithServiceAccountName("starboard-sa"). + WithClient(client). + Get() + err := instance.Init(pluginContext) + require.NoError(t, err) + + var cm corev1.ConfigMap + err = client.Get(context.Background(), types.NamespacedName{ + Namespace: "starboard-ns", + Name: "starboard-trivy-config", + }, &cm) + require.NoError(t, err) + assert.Equal(t, corev1.ConfigMap{ + TypeMeta: metav1.TypeMeta{ + APIVersion: "v1", + Kind: "ConfigMap", + }, + ObjectMeta: metav1.ObjectMeta{ + Name: "starboard-trivy-config", + Namespace: "starboard-ns", + ResourceVersion: "1", + }, + Data: map[string]string{ + "trivy.imageRef": "docker.io/aquasec/trivy:0.25.2", + "trivy.severity": "UNKNOWN,LOW,MEDIUM,HIGH,CRITICAL", + "trivy.mode": "Standalone", + }, + }, cm) + }) +} + +const ( + grypeDBLocation = "/tmp/grypedb" + keyGrypeImageRef = "grype.imageRef" + keyGrypeScheme = "grype.scheme" + keyGrypePath = "grype.path" + keyGrypeOnlyFixed = "grype.onlyFixed" + keyGrypeExcludePaths = "grype.exclude" + keyGrypeHTTPProxy = "grype.httpProxy" + keyGrypeHTTPSProxy = "grype.httpsProxy" + keyGrypeNoProxy = "grype.noProxy" + keyGrypeUpdateURL = "grype.updateURL" + keyGrypeAddMissingCPEs = "grype.addMissingCPEs" + keyGrypeRegAuthority = "grype.regAuthority" + + keyResourcesRequestsCPU = "grype.resources.requests.cpu" + keyResourcesRequestsMemory = "grype.resources.requests.memory" + keyResourcesLimitsCPU = "grype.resources.limits.cpu" + keyResourcesLimitsMemory = "grype.resources.limits.memory" +) + +func TestPlugin_GetScanJobSpec(t *testing.T) { + + commonEnv := []corev1.EnvVar{ + { + Name: "HTTP_PROXY", + ValueFrom: &corev1.EnvVarSource{ + ConfigMapKeyRef: &corev1.ConfigMapKeySelector{ + LocalObjectReference: corev1.LocalObjectReference{ + Name: grype.Plugin, + }, + Key: keyGrypeHTTPProxy, + Optional: pointer.BoolPtr(true), + }, + }, + }, + { + Name: "HTTPS_PROXY", + ValueFrom: &corev1.EnvVarSource{ + ConfigMapKeyRef: &corev1.ConfigMapKeySelector{ + LocalObjectReference: corev1.LocalObjectReference{ + Name: grype.Plugin, + }, + Key: keyGrypeHTTPSProxy, + Optional: pointer.BoolPtr(true), + }, + }, + }, + { + Name: "NO_PROXY", + ValueFrom: &corev1.EnvVarSource{ + ConfigMapKeyRef: &corev1.ConfigMapKeySelector{ + LocalObjectReference: corev1.LocalObjectReference{ + Name: grype.Plugin, + }, + Key: keyGrypeNoProxy, + Optional: pointer.BoolPtr(true), + }, + }, + }, + { + Name: "GRYPE_DB_UPDATE_URL", + ValueFrom: &corev1.EnvVarSource{ + ConfigMapKeyRef: &corev1.ConfigMapKeySelector{ + LocalObjectReference: corev1.LocalObjectReference{ + Name: grype.Plugin, + }, + Key: keyGrypeUpdateURL, + Optional: pointer.BoolPtr(false), + }, + }, + }, + { + Name: "GRYPE_DB_CACHE_DIR", + Value: grypeDBLocation, + }, + } + + tmpVolume := corev1.Volume{ + Name: "tmp", + VolumeSource: corev1.VolumeSource{ + EmptyDir: &corev1.EmptyDirVolumeSource{ + Medium: corev1.StorageMediumDefault, + }, + }, + } + + tmpVolumeMount := corev1.VolumeMount{ + Name: "tmp", + MountPath: grypeDBLocation, + ReadOnly: false, + } + + testCases := []struct { + name string + + config map[string]string + workloadSpec client.Object + + expectedSecrets []corev1.Secret + expectedJobSpec corev1.PodSpec + }{ + { + name: "grype", + config: map[string]string{ + "grype.imageRef": "anchore/grype:v0.34.7", + "grype.updateURL": defaultUpdateURL, + "grype.resources.requests.cpu": "100m", + "grype.resources.requests.memory": "100M", + "grype.resources.limits.cpu": "500m", + "grype.resources.limits.memory": "500M", + }, + workloadSpec: &appsv1.ReplicaSet{ + TypeMeta: metav1.TypeMeta{ + Kind: "ReplicaSet", + APIVersion: "apps/v1", + }, + ObjectMeta: metav1.ObjectMeta{ + Name: "nginx-6799fc88d8", + Namespace: "prod-ns", + }, + Spec: appsv1.ReplicaSetSpec{ + Template: corev1.PodTemplateSpec{ + Spec: corev1.PodSpec{ + Containers: []corev1.Container{ + { + Name: "nginx", + Image: "nginx:1.16", + }, + }, + }, + }, + }, + }, + expectedJobSpec: corev1.PodSpec{ + Affinity: starboard.LinuxNodeAffinity(), + RestartPolicy: corev1.RestartPolicyNever, + ServiceAccountName: "starboard-sa", + AutomountServiceAccountToken: pointer.BoolPtr(false), + Volumes: []corev1.Volume{ + tmpVolume, + }, + InitContainers: []corev1.Container{ + { + Name: "00000000-0000-0000-0000-000000000001", + Image: "anchore/grype:v0.34.7", + ImagePullPolicy: corev1.PullIfNotPresent, + TerminationMessagePolicy: corev1.TerminationMessageFallbackToLogsOnError, + Env: commonEnv, + Command: []string{ + "grype", + }, + Args: []string{ + "db", + "update", + }, + Resources: corev1.ResourceRequirements{ + Requests: corev1.ResourceList{ + corev1.ResourceCPU: resource.MustParse("100m"), + corev1.ResourceMemory: resource.MustParse("100M"), + }, + Limits: corev1.ResourceList{ + corev1.ResourceCPU: resource.MustParse("500m"), + corev1.ResourceMemory: resource.MustParse("500M"), + }, + }, + VolumeMounts: []corev1.VolumeMount{ + tmpVolumeMount, + }, + }, + }, + Containers: []corev1.Container{ + { + Name: "nginx", + Image: "docker.io/aquasec/trivy:0.14.0", + ImagePullPolicy: corev1.PullIfNotPresent, + TerminationMessagePolicy: corev1.TerminationMessageFallbackToLogsOnError, + Env: []corev1.EnvVar{ + { + Name: "TRIVY_SEVERITY", + ValueFrom: &corev1.EnvVarSource{ + ConfigMapKeyRef: &corev1.ConfigMapKeySelector{ + LocalObjectReference: corev1.LocalObjectReference{ + Name: "starboard-trivy-config", + }, + Key: "trivy.severity", + Optional: pointer.BoolPtr(true), + }, + }, + }, + { + Name: "TRIVY_IGNORE_UNFIXED", + ValueFrom: &corev1.EnvVarSource{ + ConfigMapKeyRef: &corev1.ConfigMapKeySelector{ + LocalObjectReference: corev1.LocalObjectReference{ + Name: "starboard-trivy-config", + }, + Key: "trivy.ignoreUnfixed", + Optional: pointer.BoolPtr(true), + }, + }, + }, + + { + Name: "TRIVY_SKIP_FILES", + ValueFrom: &corev1.EnvVarSource{ + ConfigMapKeyRef: &corev1.ConfigMapKeySelector{ + LocalObjectReference: corev1.LocalObjectReference{ + Name: "starboard-trivy-config", + }, + Key: "trivy.skipFiles", + Optional: pointer.BoolPtr(true), + }, + }, + }, + { + Name: "TRIVY_SKIP_DIRS", + ValueFrom: &corev1.EnvVarSource{ + ConfigMapKeyRef: &corev1.ConfigMapKeySelector{ + LocalObjectReference: corev1.LocalObjectReference{ + Name: "starboard-trivy-config", + }, + Key: "trivy.skipDirs", + Optional: pointer.BoolPtr(true), + }, + }, + }, + { + Name: "HTTP_PROXY", + ValueFrom: &corev1.EnvVarSource{ + ConfigMapKeyRef: &corev1.ConfigMapKeySelector{ + LocalObjectReference: corev1.LocalObjectReference{ + Name: "starboard-trivy-config", + }, + Key: "trivy.httpProxy", + Optional: pointer.BoolPtr(true), + }, + }, + }, + { + Name: "HTTPS_PROXY", + ValueFrom: &corev1.EnvVarSource{ + ConfigMapKeyRef: &corev1.ConfigMapKeySelector{ + LocalObjectReference: corev1.LocalObjectReference{ + Name: "starboard-trivy-config", + }, + Key: "trivy.httpsProxy", + Optional: pointer.BoolPtr(true), + }, + }, + }, + { + Name: "NO_PROXY", + ValueFrom: &corev1.EnvVarSource{ + ConfigMapKeyRef: &corev1.ConfigMapKeySelector{ + LocalObjectReference: corev1.LocalObjectReference{ + Name: "starboard-trivy-config", + }, + Key: "trivy.noProxy", + Optional: pointer.BoolPtr(true), + }, + }, + }, + }, + Command: []string{ + "trivy", + }, + Args: []string{ + "--cache-dir", "/tmp/trivy/.cache", + "--quiet", + "image", + "--skip-update", + "--format", "json", + "nginx:1.16", + }, + Resources: corev1.ResourceRequirements{ + Requests: corev1.ResourceList{ + corev1.ResourceCPU: resource.MustParse("100m"), + corev1.ResourceMemory: resource.MustParse("100M"), + }, + Limits: corev1.ResourceList{ + corev1.ResourceCPU: resource.MustParse("500m"), + corev1.ResourceMemory: resource.MustParse("500M"), + }, + }, + VolumeMounts: []corev1.VolumeMount{ + tmpVolumeMount, + }, + SecurityContext: &corev1.SecurityContext{ + Privileged: pointer.BoolPtr(false), + AllowPrivilegeEscalation: pointer.BoolPtr(false), + Capabilities: &corev1.Capabilities{ + Drop: []corev1.Capability{"all"}, + }, + ReadOnlyRootFilesystem: pointer.BoolPtr(true), + }, + }, + }, + SecurityContext: &corev1.PodSecurityContext{}, + }, + }, + { + name: "Standalone mode with insecure registry", + config: map[string]string{ + "trivy.imageRef": "docker.io/aquasec/trivy:0.14.0", + "trivy.mode": string(trivy.Standalone), + "trivy.insecureRegistry.pocRegistry": "poc.myregistry.harbor.com.pl", + "trivy.dbRepository": defaultUpdateURL, + + "trivy.resources.requests.cpu": "100m", + "trivy.resources.requests.memory": "100M", + "trivy.resources.limits.cpu": "500m", + "trivy.resources.limits.memory": "500M", + }, + workloadSpec: &corev1.Pod{ + TypeMeta: metav1.TypeMeta{ + Kind: "Pod", + APIVersion: "v1", + }, + ObjectMeta: metav1.ObjectMeta{ + Name: "nginx", + Namespace: "prod-ns", + }, + Spec: corev1.PodSpec{ + Containers: []corev1.Container{ + { + Name: "nginx", + Image: "poc.myregistry.harbor.com.pl/nginx:1.16", + }, + }, + }}, + expectedJobSpec: corev1.PodSpec{ + Affinity: starboard.LinuxNodeAffinity(), + RestartPolicy: corev1.RestartPolicyNever, + ServiceAccountName: "starboard-sa", + AutomountServiceAccountToken: pointer.BoolPtr(false), + Volumes: []corev1.Volume{ + tmpVolume, + }, + InitContainers: []corev1.Container{ + { + Name: "00000000-0000-0000-0000-000000000001", + Image: "docker.io/aquasec/trivy:0.14.0", + ImagePullPolicy: corev1.PullIfNotPresent, + TerminationMessagePolicy: corev1.TerminationMessageFallbackToLogsOnError, + Env: []corev1.EnvVar{ + { + Name: "HTTP_PROXY", + ValueFrom: &corev1.EnvVarSource{ + ConfigMapKeyRef: &corev1.ConfigMapKeySelector{ + LocalObjectReference: corev1.LocalObjectReference{ + Name: "starboard-trivy-config", + }, + Key: "trivy.httpProxy", + Optional: pointer.BoolPtr(true), + }, + }, + }, + { + Name: "HTTPS_PROXY", + ValueFrom: &corev1.EnvVarSource{ + ConfigMapKeyRef: &corev1.ConfigMapKeySelector{ + LocalObjectReference: corev1.LocalObjectReference{ + Name: "starboard-trivy-config", + }, + Key: "trivy.httpsProxy", + Optional: pointer.BoolPtr(true), + }, + }, + }, + { + Name: "NO_PROXY", + ValueFrom: &corev1.EnvVarSource{ + ConfigMapKeyRef: &corev1.ConfigMapKeySelector{ + LocalObjectReference: corev1.LocalObjectReference{ + Name: "starboard-trivy-config", + }, + Key: "trivy.noProxy", + Optional: pointer.BoolPtr(true), + }, + }, + }, + { + Name: "GITHUB_TOKEN", + ValueFrom: &corev1.EnvVarSource{ + SecretKeyRef: &corev1.SecretKeySelector{ + LocalObjectReference: corev1.LocalObjectReference{ + Name: "starboard-trivy-config", + }, + Key: "trivy.githubToken", + Optional: pointer.BoolPtr(true), + }, + }, + }, + }, + Command: []string{ + "trivy", + }, + Args: []string{ + "--cache-dir", "/tmp/trivy/.cache", + "image", + "--download-db-only", + "--db-repository", defaultUpdateURL, + }, + Resources: corev1.ResourceRequirements{ + Requests: corev1.ResourceList{ + corev1.ResourceCPU: resource.MustParse("100m"), + corev1.ResourceMemory: resource.MustParse("100M"), + }, + Limits: corev1.ResourceList{ + corev1.ResourceCPU: resource.MustParse("500m"), + corev1.ResourceMemory: resource.MustParse("500M"), + }, + }, + VolumeMounts: []corev1.VolumeMount{ + tmpVolumeMount, + }, + }, + }, + Containers: []corev1.Container{ + { + Name: "nginx", + Image: "docker.io/aquasec/trivy:0.14.0", + ImagePullPolicy: corev1.PullIfNotPresent, + TerminationMessagePolicy: corev1.TerminationMessageFallbackToLogsOnError, + Env: []corev1.EnvVar{ + { + Name: "TRIVY_SEVERITY", + ValueFrom: &corev1.EnvVarSource{ + ConfigMapKeyRef: &corev1.ConfigMapKeySelector{ + LocalObjectReference: corev1.LocalObjectReference{ + Name: "starboard-trivy-config", + }, + Key: "trivy.severity", + Optional: pointer.BoolPtr(true), + }, + }, + }, + { + Name: "TRIVY_IGNORE_UNFIXED", + ValueFrom: &corev1.EnvVarSource{ + ConfigMapKeyRef: &corev1.ConfigMapKeySelector{ + LocalObjectReference: corev1.LocalObjectReference{ + Name: "starboard-trivy-config", + }, + Key: "trivy.ignoreUnfixed", + Optional: pointer.BoolPtr(true), + }, + }, + }, + + { + Name: "TRIVY_SKIP_FILES", + ValueFrom: &corev1.EnvVarSource{ + ConfigMapKeyRef: &corev1.ConfigMapKeySelector{ + LocalObjectReference: corev1.LocalObjectReference{ + Name: "starboard-trivy-config", + }, + Key: "trivy.skipFiles", + Optional: pointer.BoolPtr(true), + }, + }, + }, + { + Name: "TRIVY_SKIP_DIRS", + ValueFrom: &corev1.EnvVarSource{ + ConfigMapKeyRef: &corev1.ConfigMapKeySelector{ + LocalObjectReference: corev1.LocalObjectReference{ + Name: "starboard-trivy-config", + }, + Key: "trivy.skipDirs", + Optional: pointer.BoolPtr(true), + }, + }, + }, + { + Name: "HTTP_PROXY", + ValueFrom: &corev1.EnvVarSource{ + ConfigMapKeyRef: &corev1.ConfigMapKeySelector{ + LocalObjectReference: corev1.LocalObjectReference{ + Name: "starboard-trivy-config", + }, + Key: "trivy.httpProxy", + Optional: pointer.BoolPtr(true), + }, + }, + }, + { + Name: "HTTPS_PROXY", + ValueFrom: &corev1.EnvVarSource{ + ConfigMapKeyRef: &corev1.ConfigMapKeySelector{ + LocalObjectReference: corev1.LocalObjectReference{ + Name: "starboard-trivy-config", + }, + Key: "trivy.httpsProxy", + Optional: pointer.BoolPtr(true), + }, + }, + }, + { + Name: "NO_PROXY", + ValueFrom: &corev1.EnvVarSource{ + ConfigMapKeyRef: &corev1.ConfigMapKeySelector{ + LocalObjectReference: corev1.LocalObjectReference{ + Name: "starboard-trivy-config", + }, + Key: "trivy.noProxy", + Optional: pointer.BoolPtr(true), + }, + }, + }, + { + Name: "TRIVY_INSECURE", + Value: "true", + }, + }, + Command: []string{ + "trivy", + }, + Args: []string{ + "--cache-dir", "/tmp/trivy/.cache", + "--quiet", + "image", + "--skip-update", + "--format", "json", + "poc.myregistry.harbor.com.pl/nginx:1.16", + }, + Resources: corev1.ResourceRequirements{ + Requests: corev1.ResourceList{ + corev1.ResourceCPU: resource.MustParse("100m"), + corev1.ResourceMemory: resource.MustParse("100M"), + }, + Limits: corev1.ResourceList{ + corev1.ResourceCPU: resource.MustParse("500m"), + corev1.ResourceMemory: resource.MustParse("500M"), + }, + }, + VolumeMounts: []corev1.VolumeMount{ + tmpVolumeMount, + }, + SecurityContext: &corev1.SecurityContext{ + Privileged: pointer.BoolPtr(false), + AllowPrivilegeEscalation: pointer.BoolPtr(false), + Capabilities: &corev1.Capabilities{ + Drop: []corev1.Capability{"all"}, + }, + ReadOnlyRootFilesystem: pointer.BoolPtr(true), + }, + }, + }, + SecurityContext: &corev1.PodSecurityContext{}, + }, + }, + { + name: "Standalone mode with non-SSL registry", + config: map[string]string{ + "trivy.imageRef": "docker.io/aquasec/trivy:0.14.0", + "trivy.mode": string(trivy.Standalone), + "trivy.nonSslRegistry.pocRegistry": "poc.myregistry.harbor.com.pl", + "trivy.dbRepository": defaultUpdateURL, + "trivy.resources.requests.cpu": "100m", + "trivy.resources.requests.memory": "100M", + "trivy.resources.limits.cpu": "500m", + "trivy.resources.limits.memory": "500M", + }, + workloadSpec: &corev1.Pod{ + TypeMeta: metav1.TypeMeta{ + Kind: "Pod", + APIVersion: "v1", + }, + ObjectMeta: metav1.ObjectMeta{ + Name: "nginx", + Namespace: "prod-ns", + }, + Spec: corev1.PodSpec{ + Containers: []corev1.Container{ + { + Name: "nginx", + Image: "poc.myregistry.harbor.com.pl/nginx:1.16", + }, + }, + }, + }, + expectedJobSpec: corev1.PodSpec{ + Affinity: starboard.LinuxNodeAffinity(), + RestartPolicy: corev1.RestartPolicyNever, + ServiceAccountName: "starboard-sa", + AutomountServiceAccountToken: pointer.BoolPtr(false), + Volumes: []corev1.Volume{ + tmpVolume, + }, + InitContainers: []corev1.Container{ + { + Name: "00000000-0000-0000-0000-000000000001", + Image: "docker.io/aquasec/trivy:0.14.0", + ImagePullPolicy: corev1.PullIfNotPresent, + TerminationMessagePolicy: corev1.TerminationMessageFallbackToLogsOnError, + Env: []corev1.EnvVar{ + { + Name: "HTTP_PROXY", + ValueFrom: &corev1.EnvVarSource{ + ConfigMapKeyRef: &corev1.ConfigMapKeySelector{ + LocalObjectReference: corev1.LocalObjectReference{ + Name: "starboard-trivy-config", + }, + Key: "trivy.httpProxy", + Optional: pointer.BoolPtr(true), + }, + }, + }, + { + Name: "HTTPS_PROXY", + ValueFrom: &corev1.EnvVarSource{ + ConfigMapKeyRef: &corev1.ConfigMapKeySelector{ + LocalObjectReference: corev1.LocalObjectReference{ + Name: "starboard-trivy-config", + }, + Key: "trivy.httpsProxy", + Optional: pointer.BoolPtr(true), + }, + }, + }, + { + Name: "NO_PROXY", + ValueFrom: &corev1.EnvVarSource{ + ConfigMapKeyRef: &corev1.ConfigMapKeySelector{ + LocalObjectReference: corev1.LocalObjectReference{ + Name: "starboard-trivy-config", + }, + Key: "trivy.noProxy", + Optional: pointer.BoolPtr(true), + }, + }, + }, + { + Name: "GITHUB_TOKEN", + ValueFrom: &corev1.EnvVarSource{ + SecretKeyRef: &corev1.SecretKeySelector{ + LocalObjectReference: corev1.LocalObjectReference{ + Name: "starboard-trivy-config", + }, + Key: "trivy.githubToken", + Optional: pointer.BoolPtr(true), + }, + }, + }, + }, + Command: []string{ + "trivy", + }, + Args: []string{ + "--cache-dir", "/tmp/trivy/.cache", + "image", + "--download-db-only", + "--db-repository", defaultUpdateURL, + }, + Resources: corev1.ResourceRequirements{ + Requests: corev1.ResourceList{ + corev1.ResourceCPU: resource.MustParse("100m"), + corev1.ResourceMemory: resource.MustParse("100M"), + }, + Limits: corev1.ResourceList{ + corev1.ResourceCPU: resource.MustParse("500m"), + corev1.ResourceMemory: resource.MustParse("500M"), + }, + }, + VolumeMounts: []corev1.VolumeMount{ + tmpVolumeMount, + }, + }, + }, + Containers: []corev1.Container{ + { + Name: "nginx", + Image: "docker.io/aquasec/trivy:0.14.0", + ImagePullPolicy: corev1.PullIfNotPresent, + TerminationMessagePolicy: corev1.TerminationMessageFallbackToLogsOnError, + Env: []corev1.EnvVar{ + { + Name: "TRIVY_SEVERITY", + ValueFrom: &corev1.EnvVarSource{ + ConfigMapKeyRef: &corev1.ConfigMapKeySelector{ + LocalObjectReference: corev1.LocalObjectReference{ + Name: "starboard-trivy-config", + }, + Key: "trivy.severity", + Optional: pointer.BoolPtr(true), + }, + }, + }, + { + Name: "TRIVY_IGNORE_UNFIXED", + ValueFrom: &corev1.EnvVarSource{ + ConfigMapKeyRef: &corev1.ConfigMapKeySelector{ + LocalObjectReference: corev1.LocalObjectReference{ + Name: "starboard-trivy-config", + }, + Key: "trivy.ignoreUnfixed", + Optional: pointer.BoolPtr(true), + }, + }, + }, + + { + Name: "TRIVY_SKIP_FILES", + ValueFrom: &corev1.EnvVarSource{ + ConfigMapKeyRef: &corev1.ConfigMapKeySelector{ + LocalObjectReference: corev1.LocalObjectReference{ + Name: "starboard-trivy-config", + }, + Key: "trivy.skipFiles", + Optional: pointer.BoolPtr(true), + }, + }, + }, + { + Name: "TRIVY_SKIP_DIRS", + ValueFrom: &corev1.EnvVarSource{ + ConfigMapKeyRef: &corev1.ConfigMapKeySelector{ + LocalObjectReference: corev1.LocalObjectReference{ + Name: "starboard-trivy-config", + }, + Key: "trivy.skipDirs", + Optional: pointer.BoolPtr(true), + }, + }, + }, + { + Name: "HTTP_PROXY", + ValueFrom: &corev1.EnvVarSource{ + ConfigMapKeyRef: &corev1.ConfigMapKeySelector{ + LocalObjectReference: corev1.LocalObjectReference{ + Name: "starboard-trivy-config", + }, + Key: "trivy.httpProxy", + Optional: pointer.BoolPtr(true), + }, + }, + }, + { + Name: "HTTPS_PROXY", + ValueFrom: &corev1.EnvVarSource{ + ConfigMapKeyRef: &corev1.ConfigMapKeySelector{ + LocalObjectReference: corev1.LocalObjectReference{ + Name: "starboard-trivy-config", + }, + Key: "trivy.httpsProxy", + Optional: pointer.BoolPtr(true), + }, + }, + }, + { + Name: "NO_PROXY", + ValueFrom: &corev1.EnvVarSource{ + ConfigMapKeyRef: &corev1.ConfigMapKeySelector{ + LocalObjectReference: corev1.LocalObjectReference{ + Name: "starboard-trivy-config", + }, + Key: "trivy.noProxy", + Optional: pointer.BoolPtr(true), + }, + }, + }, + { + Name: "TRIVY_NON_SSL", + Value: "true", + }, + }, + Command: []string{ + "trivy", + }, + Args: []string{ + "--cache-dir", "/tmp/trivy/.cache", + "--quiet", + "image", + "--skip-update", + "--format", "json", + "poc.myregistry.harbor.com.pl/nginx:1.16", + }, + Resources: corev1.ResourceRequirements{ + Requests: corev1.ResourceList{ + corev1.ResourceCPU: resource.MustParse("100m"), + corev1.ResourceMemory: resource.MustParse("100M"), + }, + Limits: corev1.ResourceList{ + corev1.ResourceCPU: resource.MustParse("500m"), + corev1.ResourceMemory: resource.MustParse("500M"), + }, + }, + VolumeMounts: []corev1.VolumeMount{ + tmpVolumeMount, + }, + SecurityContext: &corev1.SecurityContext{ + Privileged: pointer.BoolPtr(false), + AllowPrivilegeEscalation: pointer.BoolPtr(false), + Capabilities: &corev1.Capabilities{ + Drop: []corev1.Capability{"all"}, + }, + ReadOnlyRootFilesystem: pointer.BoolPtr(true), + }, + }, + }, + SecurityContext: &corev1.PodSecurityContext{}, + }, + }, + { + name: "Standalone mode with trivyignore file", + config: map[string]string{ + "trivy.imageRef": "docker.io/aquasec/trivy:0.14.0", + "trivy.mode": string(trivy.Standalone), + "trivy.ignoreFile": `# Accept the risk +CVE-2018-14618 + +# No impact in our settings +CVE-2019-1543`, + "trivy.dbRepository": defaultUpdateURL, + "trivy.resources.requests.cpu": "100m", + "trivy.resources.requests.memory": "100M", + "trivy.resources.limits.cpu": "500m", + "trivy.resources.limits.memory": "500M", + }, + workloadSpec: &corev1.Pod{ + TypeMeta: metav1.TypeMeta{ + Kind: "Pod", + APIVersion: "v1", + }, + ObjectMeta: metav1.ObjectMeta{ + Name: "nginx", + Namespace: "prod-ns", + }, + Spec: corev1.PodSpec{ + Containers: []corev1.Container{ + { + Name: "nginx", + Image: "nginx:1.16", + }, + }, + }, + }, + expectedJobSpec: corev1.PodSpec{ + Affinity: starboard.LinuxNodeAffinity(), + RestartPolicy: corev1.RestartPolicyNever, + ServiceAccountName: "starboard-sa", + AutomountServiceAccountToken: pointer.BoolPtr(false), + Volumes: []corev1.Volume{ + tmpVolume, + { + Name: "ignorefile", + VolumeSource: corev1.VolumeSource{ + ConfigMap: &corev1.ConfigMapVolumeSource{ + LocalObjectReference: corev1.LocalObjectReference{ + Name: "starboard-trivy-config", + }, + Items: []corev1.KeyToPath{ + { + Key: "trivy.ignoreFile", + Path: ".trivyignore", + }, + }, + }, + }, + }, + }, + InitContainers: []corev1.Container{ + { + Name: "00000000-0000-0000-0000-000000000001", + Image: "docker.io/aquasec/trivy:0.14.0", + ImagePullPolicy: corev1.PullIfNotPresent, + TerminationMessagePolicy: corev1.TerminationMessageFallbackToLogsOnError, + Env: []corev1.EnvVar{ + { + Name: "HTTP_PROXY", + ValueFrom: &corev1.EnvVarSource{ + ConfigMapKeyRef: &corev1.ConfigMapKeySelector{ + LocalObjectReference: corev1.LocalObjectReference{ + Name: "starboard-trivy-config", + }, + Key: "trivy.httpProxy", + Optional: pointer.BoolPtr(true), + }, + }, + }, + { + Name: "HTTPS_PROXY", + ValueFrom: &corev1.EnvVarSource{ + ConfigMapKeyRef: &corev1.ConfigMapKeySelector{ + LocalObjectReference: corev1.LocalObjectReference{ + Name: "starboard-trivy-config", + }, + Key: "trivy.httpsProxy", + Optional: pointer.BoolPtr(true), + }, + }, + }, + { + Name: "NO_PROXY", + ValueFrom: &corev1.EnvVarSource{ + ConfigMapKeyRef: &corev1.ConfigMapKeySelector{ + LocalObjectReference: corev1.LocalObjectReference{ + Name: "starboard-trivy-config", + }, + Key: "trivy.noProxy", + Optional: pointer.BoolPtr(true), + }, + }, + }, + { + Name: "GITHUB_TOKEN", + ValueFrom: &corev1.EnvVarSource{ + SecretKeyRef: &corev1.SecretKeySelector{ + LocalObjectReference: corev1.LocalObjectReference{ + Name: "starboard-trivy-config", + }, + Key: "trivy.githubToken", + Optional: pointer.BoolPtr(true), + }, + }, + }, + }, + Command: []string{ + "trivy", + }, + Args: []string{ + "--cache-dir", "/tmp/trivy/.cache", + "image", + "--download-db-only", + "--db-repository", defaultUpdateURL, + }, + Resources: corev1.ResourceRequirements{ + Requests: corev1.ResourceList{ + corev1.ResourceCPU: resource.MustParse("100m"), + corev1.ResourceMemory: resource.MustParse("100M"), + }, + Limits: corev1.ResourceList{ + corev1.ResourceCPU: resource.MustParse("500m"), + corev1.ResourceMemory: resource.MustParse("500M"), + }, + }, + VolumeMounts: []corev1.VolumeMount{ + tmpVolumeMount, + }, + }, + }, + Containers: []corev1.Container{ + { + Name: "nginx", + Image: "docker.io/aquasec/trivy:0.14.0", + ImagePullPolicy: corev1.PullIfNotPresent, + TerminationMessagePolicy: corev1.TerminationMessageFallbackToLogsOnError, + Env: []corev1.EnvVar{ + { + Name: "TRIVY_SEVERITY", + ValueFrom: &corev1.EnvVarSource{ + ConfigMapKeyRef: &corev1.ConfigMapKeySelector{ + LocalObjectReference: corev1.LocalObjectReference{ + Name: "starboard-trivy-config", + }, + Key: "trivy.severity", + Optional: pointer.BoolPtr(true), + }, + }, + }, + { + Name: "TRIVY_IGNORE_UNFIXED", + ValueFrom: &corev1.EnvVarSource{ + ConfigMapKeyRef: &corev1.ConfigMapKeySelector{ + LocalObjectReference: corev1.LocalObjectReference{ + Name: "starboard-trivy-config", + }, + Key: "trivy.ignoreUnfixed", + Optional: pointer.BoolPtr(true), + }, + }, + }, + + { + Name: "TRIVY_SKIP_FILES", + ValueFrom: &corev1.EnvVarSource{ + ConfigMapKeyRef: &corev1.ConfigMapKeySelector{ + LocalObjectReference: corev1.LocalObjectReference{ + Name: "starboard-trivy-config", + }, + Key: "trivy.skipFiles", + Optional: pointer.BoolPtr(true), + }, + }, + }, + { + Name: "TRIVY_SKIP_DIRS", + ValueFrom: &corev1.EnvVarSource{ + ConfigMapKeyRef: &corev1.ConfigMapKeySelector{ + LocalObjectReference: corev1.LocalObjectReference{ + Name: "starboard-trivy-config", + }, + Key: "trivy.skipDirs", + Optional: pointer.BoolPtr(true), + }, + }, + }, + { + Name: "HTTP_PROXY", + ValueFrom: &corev1.EnvVarSource{ + ConfigMapKeyRef: &corev1.ConfigMapKeySelector{ + LocalObjectReference: corev1.LocalObjectReference{ + Name: "starboard-trivy-config", + }, + Key: "trivy.httpProxy", + Optional: pointer.BoolPtr(true), + }, + }, + }, + { + Name: "HTTPS_PROXY", + ValueFrom: &corev1.EnvVarSource{ + ConfigMapKeyRef: &corev1.ConfigMapKeySelector{ + LocalObjectReference: corev1.LocalObjectReference{ + Name: "starboard-trivy-config", + }, + Key: "trivy.httpsProxy", + Optional: pointer.BoolPtr(true), + }, + }, + }, + { + Name: "NO_PROXY", + ValueFrom: &corev1.EnvVarSource{ + ConfigMapKeyRef: &corev1.ConfigMapKeySelector{ + LocalObjectReference: corev1.LocalObjectReference{ + Name: "starboard-trivy-config", + }, + Key: "trivy.noProxy", + Optional: pointer.BoolPtr(true), + }, + }, + }, + { + Name: "TRIVY_IGNOREFILE", + Value: "/etc/trivy/.trivyignore", + }, + }, + Command: []string{ + "trivy", + }, + Args: []string{ + "--cache-dir", "/tmp/trivy/.cache", + "--quiet", + "image", + "--skip-update", + "--format", "json", + "nginx:1.16", + }, + Resources: corev1.ResourceRequirements{ + Requests: corev1.ResourceList{ + corev1.ResourceCPU: resource.MustParse("100m"), + corev1.ResourceMemory: resource.MustParse("100M"), + }, + Limits: corev1.ResourceList{ + corev1.ResourceCPU: resource.MustParse("500m"), + corev1.ResourceMemory: resource.MustParse("500M"), + }, + }, + VolumeMounts: []corev1.VolumeMount{ + tmpVolumeMount, + { + Name: "ignorefile", + MountPath: "/etc/trivy/.trivyignore", + SubPath: ".trivyignore", + }, + }, + SecurityContext: &corev1.SecurityContext{ + Privileged: pointer.BoolPtr(false), + AllowPrivilegeEscalation: pointer.BoolPtr(false), + Capabilities: &corev1.Capabilities{ + Drop: []corev1.Capability{"all"}, + }, + ReadOnlyRootFilesystem: pointer.BoolPtr(true), + }, + }, + }, + SecurityContext: &corev1.PodSecurityContext{}, + }, + }, + { + name: "Standalone mode with mirror", + config: map[string]string{ + "trivy.imageRef": "docker.io/aquasec/trivy:0.14.0", + "trivy.mode": string(trivy.Standalone), + + "trivy.dbRepository": defaultUpdateURL, + "trivy.resources.requests.cpu": "100m", + "trivy.resources.requests.memory": "100M", + "trivy.resources.limits.cpu": "500m", + "trivy.resources.limits.memory": "500M", + + "trivy.registry.mirror.index.docker.io": "mirror.io", + }, + workloadSpec: &corev1.Pod{ + TypeMeta: metav1.TypeMeta{ + Kind: "Pod", + APIVersion: "v1", + }, + ObjectMeta: metav1.ObjectMeta{ + Name: "nginx", + Namespace: "prod-ns", + }, + Spec: corev1.PodSpec{ + Containers: []corev1.Container{ + { + Name: "nginx", + Image: "nginx:1.16", + }, + }, + }, + }, + expectedJobSpec: corev1.PodSpec{ + Affinity: starboard.LinuxNodeAffinity(), + RestartPolicy: corev1.RestartPolicyNever, + ServiceAccountName: "starboard-sa", + AutomountServiceAccountToken: pointer.BoolPtr(false), + Volumes: []corev1.Volume{ + tmpVolume, + }, + InitContainers: []corev1.Container{ + { + Name: "00000000-0000-0000-0000-000000000001", + Image: "docker.io/aquasec/trivy:0.14.0", + ImagePullPolicy: corev1.PullIfNotPresent, + TerminationMessagePolicy: corev1.TerminationMessageFallbackToLogsOnError, + Env: []corev1.EnvVar{ + { + Name: "HTTP_PROXY", + ValueFrom: &corev1.EnvVarSource{ + ConfigMapKeyRef: &corev1.ConfigMapKeySelector{ + LocalObjectReference: corev1.LocalObjectReference{ + Name: "starboard-trivy-config", + }, + Key: "trivy.httpProxy", + Optional: pointer.BoolPtr(true), + }, + }, + }, + { + Name: "HTTPS_PROXY", + ValueFrom: &corev1.EnvVarSource{ + ConfigMapKeyRef: &corev1.ConfigMapKeySelector{ + LocalObjectReference: corev1.LocalObjectReference{ + Name: "starboard-trivy-config", + }, + Key: "trivy.httpsProxy", + Optional: pointer.BoolPtr(true), + }, + }, + }, + { + Name: "NO_PROXY", + ValueFrom: &corev1.EnvVarSource{ + ConfigMapKeyRef: &corev1.ConfigMapKeySelector{ + LocalObjectReference: corev1.LocalObjectReference{ + Name: "starboard-trivy-config", + }, + Key: "trivy.noProxy", + Optional: pointer.BoolPtr(true), + }, + }, + }, + + { + Name: "GITHUB_TOKEN", + ValueFrom: &corev1.EnvVarSource{ + SecretKeyRef: &corev1.SecretKeySelector{ + LocalObjectReference: corev1.LocalObjectReference{ + Name: "starboard-trivy-config", + }, + Key: "trivy.githubToken", + Optional: pointer.BoolPtr(true), + }, + }, + }, + }, + Command: []string{ + "trivy", + }, + Args: []string{ + "--cache-dir", "/tmp/trivy/.cache", + "image", + "--download-db-only", + "--db-repository", defaultUpdateURL, + }, + Resources: corev1.ResourceRequirements{ + Requests: corev1.ResourceList{ + corev1.ResourceCPU: resource.MustParse("100m"), + corev1.ResourceMemory: resource.MustParse("100M"), + }, + Limits: corev1.ResourceList{ + corev1.ResourceCPU: resource.MustParse("500m"), + corev1.ResourceMemory: resource.MustParse("500M"), + }, + }, + VolumeMounts: []corev1.VolumeMount{ + tmpVolumeMount, + }, + }, + }, + Containers: []corev1.Container{ + { + Name: "nginx", + Image: "docker.io/aquasec/trivy:0.14.0", + ImagePullPolicy: corev1.PullIfNotPresent, + TerminationMessagePolicy: corev1.TerminationMessageFallbackToLogsOnError, + Env: []corev1.EnvVar{ + { + Name: "TRIVY_SEVERITY", + ValueFrom: &corev1.EnvVarSource{ + ConfigMapKeyRef: &corev1.ConfigMapKeySelector{ + LocalObjectReference: corev1.LocalObjectReference{ + Name: "starboard-trivy-config", + }, + Key: "trivy.severity", + Optional: pointer.BoolPtr(true), + }, + }, + }, + { + Name: "TRIVY_IGNORE_UNFIXED", + ValueFrom: &corev1.EnvVarSource{ + ConfigMapKeyRef: &corev1.ConfigMapKeySelector{ + LocalObjectReference: corev1.LocalObjectReference{ + Name: "starboard-trivy-config", + }, + Key: "trivy.ignoreUnfixed", + Optional: pointer.BoolPtr(true), + }, + }, + }, + + { + Name: "TRIVY_SKIP_FILES", + ValueFrom: &corev1.EnvVarSource{ + ConfigMapKeyRef: &corev1.ConfigMapKeySelector{ + LocalObjectReference: corev1.LocalObjectReference{ + Name: "starboard-trivy-config", + }, + Key: "trivy.skipFiles", + Optional: pointer.BoolPtr(true), + }, + }, + }, + { + Name: "TRIVY_SKIP_DIRS", + ValueFrom: &corev1.EnvVarSource{ + ConfigMapKeyRef: &corev1.ConfigMapKeySelector{ + LocalObjectReference: corev1.LocalObjectReference{ + Name: "starboard-trivy-config", + }, + Key: "trivy.skipDirs", + Optional: pointer.BoolPtr(true), + }, + }, + }, + { + Name: "HTTP_PROXY", + ValueFrom: &corev1.EnvVarSource{ + ConfigMapKeyRef: &corev1.ConfigMapKeySelector{ + LocalObjectReference: corev1.LocalObjectReference{ + Name: "starboard-trivy-config", + }, + Key: "trivy.httpProxy", + Optional: pointer.BoolPtr(true), + }, + }, + }, + { + Name: "HTTPS_PROXY", + ValueFrom: &corev1.EnvVarSource{ + ConfigMapKeyRef: &corev1.ConfigMapKeySelector{ + LocalObjectReference: corev1.LocalObjectReference{ + Name: "starboard-trivy-config", + }, + Key: "trivy.httpsProxy", + Optional: pointer.BoolPtr(true), + }, + }, + }, + { + Name: "NO_PROXY", + ValueFrom: &corev1.EnvVarSource{ + ConfigMapKeyRef: &corev1.ConfigMapKeySelector{ + LocalObjectReference: corev1.LocalObjectReference{ + Name: "starboard-trivy-config", + }, + Key: "trivy.noProxy", + Optional: pointer.BoolPtr(true), + }, + }, + }, + }, + Command: []string{ + "trivy", + }, + Args: []string{ + "--cache-dir", "/tmp/trivy/.cache", + "--quiet", + "image", + "--skip-update", + "--format", "json", + "mirror.io/library/nginx:1.16", + }, + Resources: corev1.ResourceRequirements{ + Requests: corev1.ResourceList{ + corev1.ResourceCPU: resource.MustParse("100m"), + corev1.ResourceMemory: resource.MustParse("100M"), + }, + Limits: corev1.ResourceList{ + corev1.ResourceCPU: resource.MustParse("500m"), + corev1.ResourceMemory: resource.MustParse("500M"), + }, + }, + VolumeMounts: []corev1.VolumeMount{ + tmpVolumeMount, + }, + SecurityContext: &corev1.SecurityContext{ + Privileged: pointer.BoolPtr(false), + AllowPrivilegeEscalation: pointer.BoolPtr(false), + Capabilities: &corev1.Capabilities{ + Drop: []corev1.Capability{"all"}, + }, + ReadOnlyRootFilesystem: pointer.BoolPtr(true), + }, + }, + }, + SecurityContext: &corev1.PodSecurityContext{}, + }, + }, + { + name: "ClientServer mode without insecure registry", + config: map[string]string{ + "trivy.imageRef": "docker.io/aquasec/trivy:0.14.0", + "trivy.mode": string(trivy.ClientServer), + "trivy.serverURL": "http://trivy.trivy:4954", + "trivy.dbRepository": defaultUpdateURL, + "trivy.resources.requests.cpu": "100m", + "trivy.resources.requests.memory": "100M", + "trivy.resources.limits.cpu": "500m", + "trivy.resources.limits.memory": "500M", + }, + workloadSpec: &corev1.Pod{ + TypeMeta: metav1.TypeMeta{ + Kind: "Pod", + APIVersion: "v1", + }, + ObjectMeta: metav1.ObjectMeta{ + Name: "nginx", + Namespace: "prod-ns", + }, + Spec: corev1.PodSpec{ + Containers: []corev1.Container{ + { + Name: "nginx", + Image: "nginx:1.16", + }, + }, + }, + }, + expectedJobSpec: corev1.PodSpec{ + Affinity: starboard.LinuxNodeAffinity(), + RestartPolicy: corev1.RestartPolicyNever, + ServiceAccountName: "starboard-sa", + AutomountServiceAccountToken: pointer.BoolPtr(false), + Containers: []corev1.Container{ + { + Name: "nginx", + Image: "docker.io/aquasec/trivy:0.14.0", + ImagePullPolicy: corev1.PullIfNotPresent, + TerminationMessagePolicy: corev1.TerminationMessageFallbackToLogsOnError, + Env: []corev1.EnvVar{ + { + Name: "HTTP_PROXY", + ValueFrom: &corev1.EnvVarSource{ + ConfigMapKeyRef: &corev1.ConfigMapKeySelector{ + LocalObjectReference: corev1.LocalObjectReference{ + Name: "starboard-trivy-config", + }, + Key: "trivy.httpProxy", + Optional: pointer.BoolPtr(true), + }, + }, + }, + { + Name: "HTTPS_PROXY", + ValueFrom: &corev1.EnvVarSource{ + ConfigMapKeyRef: &corev1.ConfigMapKeySelector{ + LocalObjectReference: corev1.LocalObjectReference{ + Name: "starboard-trivy-config", + }, + Key: "trivy.httpsProxy", + Optional: pointer.BoolPtr(true), + }, + }, + }, + { + Name: "NO_PROXY", + ValueFrom: &corev1.EnvVarSource{ + ConfigMapKeyRef: &corev1.ConfigMapKeySelector{ + LocalObjectReference: corev1.LocalObjectReference{ + Name: "starboard-trivy-config", + }, + Key: "trivy.noProxy", + Optional: pointer.BoolPtr(true), + }, + }, + }, + { + Name: "TRIVY_SEVERITY", + ValueFrom: &corev1.EnvVarSource{ + ConfigMapKeyRef: &corev1.ConfigMapKeySelector{ + LocalObjectReference: corev1.LocalObjectReference{ + Name: "starboard-trivy-config", + }, + Key: "trivy.severity", + Optional: pointer.BoolPtr(true), + }, + }, + }, + { + Name: "TRIVY_IGNORE_UNFIXED", + ValueFrom: &corev1.EnvVarSource{ + ConfigMapKeyRef: &corev1.ConfigMapKeySelector{ + LocalObjectReference: corev1.LocalObjectReference{ + Name: "starboard-trivy-config", + }, + Key: "trivy.ignoreUnfixed", + Optional: pointer.BoolPtr(true), + }, + }, + }, + + { + Name: "TRIVY_SKIP_FILES", + ValueFrom: &corev1.EnvVarSource{ + ConfigMapKeyRef: &corev1.ConfigMapKeySelector{ + LocalObjectReference: corev1.LocalObjectReference{ + Name: "starboard-trivy-config", + }, + Key: "trivy.skipFiles", + Optional: pointer.BoolPtr(true), + }, + }, + }, + { + Name: "TRIVY_SKIP_DIRS", + ValueFrom: &corev1.EnvVarSource{ + ConfigMapKeyRef: &corev1.ConfigMapKeySelector{ + LocalObjectReference: corev1.LocalObjectReference{ + Name: "starboard-trivy-config", + }, + Key: "trivy.skipDirs", + Optional: pointer.BoolPtr(true), + }, + }, + }, + { + Name: "TRIVY_TOKEN_HEADER", + ValueFrom: &corev1.EnvVarSource{ + ConfigMapKeyRef: &corev1.ConfigMapKeySelector{ + LocalObjectReference: corev1.LocalObjectReference{ + Name: "starboard-trivy-config", + }, + Key: "trivy.serverTokenHeader", + Optional: pointer.BoolPtr(true), + }, + }, + }, + { + Name: "TRIVY_TOKEN", + ValueFrom: &corev1.EnvVarSource{ + SecretKeyRef: &corev1.SecretKeySelector{ + LocalObjectReference: corev1.LocalObjectReference{ + Name: "starboard-trivy-config", + }, + Key: "trivy.serverToken", + Optional: pointer.BoolPtr(true), + }, + }, + }, + { + Name: "TRIVY_CUSTOM_HEADERS", + ValueFrom: &corev1.EnvVarSource{ + SecretKeyRef: &corev1.SecretKeySelector{ + LocalObjectReference: corev1.LocalObjectReference{ + Name: "starboard-trivy-config", + }, + Key: "trivy.serverCustomHeaders", + Optional: pointer.BoolPtr(true), + }, + }, + }, + }, + Command: []string{ + "trivy", + }, + Args: []string{ + "--quiet", + "client", + "--format", + "json", + "--remote", + "http://trivy.trivy:4954", + "nginx:1.16", + }, + Resources: corev1.ResourceRequirements{ + Requests: corev1.ResourceList{ + corev1.ResourceCPU: resource.MustParse("100m"), + corev1.ResourceMemory: resource.MustParse("100M"), + }, + Limits: corev1.ResourceList{ + corev1.ResourceCPU: resource.MustParse("500m"), + corev1.ResourceMemory: resource.MustParse("500M"), + }, + }, + }, + }, + }, + }, + { + name: "ClientServer mode without insecure registry", + config: map[string]string{ + "trivy.imageRef": "docker.io/aquasec/trivy:0.14.0", + "trivy.mode": string(trivy.ClientServer), + "trivy.serverURL": "http://trivy.trivy:4954", + "trivy.dbRepository": defaultUpdateURL, + "trivy.resources.requests.cpu": "100m", + "trivy.resources.requests.memory": "100M", + "trivy.resources.limits.cpu": "500m", + "trivy.resources.limits.memory": "500M", + }, + workloadSpec: &corev1.Pod{ + TypeMeta: metav1.TypeMeta{ + Kind: "Pod", + APIVersion: "v1", + }, + ObjectMeta: metav1.ObjectMeta{ + Name: "nginx", + Namespace: "prod-ns", + }, + Spec: corev1.PodSpec{ + Containers: []corev1.Container{ + { + Name: "nginx", + Image: "nginx:1.16", + }, + }, + }, + }, + expectedJobSpec: corev1.PodSpec{ + Affinity: starboard.LinuxNodeAffinity(), + RestartPolicy: corev1.RestartPolicyNever, + ServiceAccountName: "starboard-sa", + AutomountServiceAccountToken: pointer.BoolPtr(false), + Containers: []corev1.Container{ + { + Name: "nginx", + Image: "docker.io/aquasec/trivy:0.14.0", + ImagePullPolicy: corev1.PullIfNotPresent, + TerminationMessagePolicy: corev1.TerminationMessageFallbackToLogsOnError, + Env: []corev1.EnvVar{ + { + Name: "HTTP_PROXY", + ValueFrom: &corev1.EnvVarSource{ + ConfigMapKeyRef: &corev1.ConfigMapKeySelector{ + LocalObjectReference: corev1.LocalObjectReference{ + Name: "starboard-trivy-config", + }, + Key: "trivy.httpProxy", + Optional: pointer.BoolPtr(true), + }, + }, + }, + { + Name: "HTTPS_PROXY", + ValueFrom: &corev1.EnvVarSource{ + ConfigMapKeyRef: &corev1.ConfigMapKeySelector{ + LocalObjectReference: corev1.LocalObjectReference{ + Name: "starboard-trivy-config", + }, + Key: "trivy.httpsProxy", + Optional: pointer.BoolPtr(true), + }, + }, + }, + { + Name: "NO_PROXY", + ValueFrom: &corev1.EnvVarSource{ + ConfigMapKeyRef: &corev1.ConfigMapKeySelector{ + LocalObjectReference: corev1.LocalObjectReference{ + Name: "starboard-trivy-config", + }, + Key: "trivy.noProxy", + Optional: pointer.BoolPtr(true), + }, + }, + }, + { + Name: "TRIVY_SEVERITY", + ValueFrom: &corev1.EnvVarSource{ + ConfigMapKeyRef: &corev1.ConfigMapKeySelector{ + LocalObjectReference: corev1.LocalObjectReference{ + Name: "starboard-trivy-config", + }, + Key: "trivy.severity", + Optional: pointer.BoolPtr(true), + }, + }, + }, + { + Name: "TRIVY_IGNORE_UNFIXED", + ValueFrom: &corev1.EnvVarSource{ + ConfigMapKeyRef: &corev1.ConfigMapKeySelector{ + LocalObjectReference: corev1.LocalObjectReference{ + Name: "starboard-trivy-config", + }, + Key: "trivy.ignoreUnfixed", + Optional: pointer.BoolPtr(true), + }, + }, + }, + + { + Name: "TRIVY_SKIP_FILES", + ValueFrom: &corev1.EnvVarSource{ + ConfigMapKeyRef: &corev1.ConfigMapKeySelector{ + LocalObjectReference: corev1.LocalObjectReference{ + Name: "starboard-trivy-config", + }, + Key: "trivy.skipFiles", + Optional: pointer.BoolPtr(true), + }, + }, + }, + { + Name: "TRIVY_SKIP_DIRS", + ValueFrom: &corev1.EnvVarSource{ + ConfigMapKeyRef: &corev1.ConfigMapKeySelector{ + LocalObjectReference: corev1.LocalObjectReference{ + Name: "starboard-trivy-config", + }, + Key: "trivy.skipDirs", + Optional: pointer.BoolPtr(true), + }, + }, + }, + { + Name: "TRIVY_TOKEN_HEADER", + ValueFrom: &corev1.EnvVarSource{ + ConfigMapKeyRef: &corev1.ConfigMapKeySelector{ + LocalObjectReference: corev1.LocalObjectReference{ + Name: "starboard-trivy-config", + }, + Key: "trivy.serverTokenHeader", + Optional: pointer.BoolPtr(true), + }, + }, + }, + { + Name: "TRIVY_TOKEN", + ValueFrom: &corev1.EnvVarSource{ + SecretKeyRef: &corev1.SecretKeySelector{ + LocalObjectReference: corev1.LocalObjectReference{ + Name: "starboard-trivy-config", + }, + Key: "trivy.serverToken", + Optional: pointer.BoolPtr(true), + }, + }, + }, + { + Name: "TRIVY_CUSTOM_HEADERS", + ValueFrom: &corev1.EnvVarSource{ + SecretKeyRef: &corev1.SecretKeySelector{ + LocalObjectReference: corev1.LocalObjectReference{ + Name: "starboard-trivy-config", + }, + Key: "trivy.serverCustomHeaders", + Optional: pointer.BoolPtr(true), + }, + }, + }, + }, + Command: []string{ + "trivy", + }, + Args: []string{ + "--quiet", + "client", + "--format", + "json", + "--remote", + "http://trivy.trivy:4954", + "nginx:1.16", + }, + Resources: corev1.ResourceRequirements{ + Requests: corev1.ResourceList{ + corev1.ResourceCPU: resource.MustParse("100m"), + corev1.ResourceMemory: resource.MustParse("100M"), + }, + Limits: corev1.ResourceList{ + corev1.ResourceCPU: resource.MustParse("500m"), + corev1.ResourceMemory: resource.MustParse("500M"), + }, + }, + }, + }, + }, + }, + { + name: "ClientServer mode with insecure server", + config: map[string]string{ + "trivy.imageRef": "docker.io/aquasec/trivy:0.14.0", + "trivy.mode": string(trivy.ClientServer), + "trivy.serverURL": "https://trivy.trivy:4954", + "trivy.serverInsecure": "true", + "trivy.dbRepository": defaultUpdateURL, + "trivy.resources.requests.cpu": "100m", + "trivy.resources.requests.memory": "100M", + "trivy.resources.limits.cpu": "500m", + "trivy.resources.limits.memory": "500M", + }, + workloadSpec: &corev1.Pod{ + TypeMeta: metav1.TypeMeta{ + Kind: "Pod", + APIVersion: "v1", + }, + ObjectMeta: metav1.ObjectMeta{ + Name: "nginx", + Namespace: "prod-ns", + }, + Spec: corev1.PodSpec{ + Containers: []corev1.Container{ + { + Name: "nginx", + Image: "poc.myregistry.harbor.com.pl/nginx:1.16", + }, + }, + }, + }, + expectedJobSpec: corev1.PodSpec{ + Affinity: starboard.LinuxNodeAffinity(), + RestartPolicy: corev1.RestartPolicyNever, + ServiceAccountName: "starboard-sa", + AutomountServiceAccountToken: pointer.BoolPtr(false), + Containers: []corev1.Container{ + { + Name: "nginx", + Image: "docker.io/aquasec/trivy:0.14.0", + ImagePullPolicy: corev1.PullIfNotPresent, + TerminationMessagePolicy: corev1.TerminationMessageFallbackToLogsOnError, + Env: []corev1.EnvVar{ + { + Name: "HTTP_PROXY", + ValueFrom: &corev1.EnvVarSource{ + ConfigMapKeyRef: &corev1.ConfigMapKeySelector{ + LocalObjectReference: corev1.LocalObjectReference{ + Name: "starboard-trivy-config", + }, + Key: "trivy.httpProxy", + Optional: pointer.BoolPtr(true), + }, + }, + }, + { + Name: "HTTPS_PROXY", + ValueFrom: &corev1.EnvVarSource{ + ConfigMapKeyRef: &corev1.ConfigMapKeySelector{ + LocalObjectReference: corev1.LocalObjectReference{ + Name: "starboard-trivy-config", + }, + Key: "trivy.httpsProxy", + Optional: pointer.BoolPtr(true), + }, + }, + }, + { + Name: "NO_PROXY", + ValueFrom: &corev1.EnvVarSource{ + ConfigMapKeyRef: &corev1.ConfigMapKeySelector{ + LocalObjectReference: corev1.LocalObjectReference{ + Name: "starboard-trivy-config", + }, + Key: "trivy.noProxy", + Optional: pointer.BoolPtr(true), + }, + }, + }, + { + Name: "TRIVY_SEVERITY", + ValueFrom: &corev1.EnvVarSource{ + ConfigMapKeyRef: &corev1.ConfigMapKeySelector{ + LocalObjectReference: corev1.LocalObjectReference{ + Name: "starboard-trivy-config", + }, + Key: "trivy.severity", + Optional: pointer.BoolPtr(true), + }, + }, + }, + { + Name: "TRIVY_IGNORE_UNFIXED", + ValueFrom: &corev1.EnvVarSource{ + ConfigMapKeyRef: &corev1.ConfigMapKeySelector{ + LocalObjectReference: corev1.LocalObjectReference{ + Name: "starboard-trivy-config", + }, + Key: "trivy.ignoreUnfixed", + Optional: pointer.BoolPtr(true), + }, + }, + }, + + { + Name: "TRIVY_SKIP_FILES", + ValueFrom: &corev1.EnvVarSource{ + ConfigMapKeyRef: &corev1.ConfigMapKeySelector{ + LocalObjectReference: corev1.LocalObjectReference{ + Name: "starboard-trivy-config", + }, + Key: "trivy.skipFiles", + Optional: pointer.BoolPtr(true), + }, + }, + }, + { + Name: "TRIVY_SKIP_DIRS", + ValueFrom: &corev1.EnvVarSource{ + ConfigMapKeyRef: &corev1.ConfigMapKeySelector{ + LocalObjectReference: corev1.LocalObjectReference{ + Name: "starboard-trivy-config", + }, + Key: "trivy.skipDirs", + Optional: pointer.BoolPtr(true), + }, + }, + }, + { + Name: "TRIVY_TOKEN_HEADER", + ValueFrom: &corev1.EnvVarSource{ + ConfigMapKeyRef: &corev1.ConfigMapKeySelector{ + LocalObjectReference: corev1.LocalObjectReference{ + Name: "starboard-trivy-config", + }, + Key: "trivy.serverTokenHeader", + Optional: pointer.BoolPtr(true), + }, + }, + }, + { + Name: "TRIVY_TOKEN", + ValueFrom: &corev1.EnvVarSource{ + SecretKeyRef: &corev1.SecretKeySelector{ + LocalObjectReference: corev1.LocalObjectReference{ + Name: "starboard-trivy-config", + }, + Key: "trivy.serverToken", + Optional: pointer.BoolPtr(true), + }, + }, + }, + { + Name: "TRIVY_CUSTOM_HEADERS", + ValueFrom: &corev1.EnvVarSource{ + SecretKeyRef: &corev1.SecretKeySelector{ + LocalObjectReference: corev1.LocalObjectReference{ + Name: "starboard-trivy-config", + }, + Key: "trivy.serverCustomHeaders", + Optional: pointer.BoolPtr(true), + }, + }, + }, + { + Name: "TRIVY_INSECURE", + Value: "true", + }, + }, + Command: []string{ + "trivy", + }, + Args: []string{ + "--quiet", + "client", + "--format", + "json", + "--remote", + "https://trivy.trivy:4954", + "poc.myregistry.harbor.com.pl/nginx:1.16", + }, + Resources: corev1.ResourceRequirements{ + Requests: corev1.ResourceList{ + corev1.ResourceCPU: resource.MustParse("100m"), + corev1.ResourceMemory: resource.MustParse("100M"), + }, + Limits: corev1.ResourceList{ + corev1.ResourceCPU: resource.MustParse("500m"), + corev1.ResourceMemory: resource.MustParse("500M"), + }, + }, + }, + }, + }, + }, + { + name: "ClientServer mode with non-SSL registry", + config: map[string]string{ + "trivy.imageRef": "docker.io/aquasec/trivy:0.14.0", + "trivy.mode": string(trivy.ClientServer), + "trivy.serverURL": "http://trivy.trivy:4954", + "trivy.nonSslRegistry.pocRegistry": "poc.myregistry.harbor.com.pl", + "trivy.dbRepository": defaultUpdateURL, + "trivy.resources.requests.cpu": "100m", + "trivy.resources.requests.memory": "100M", + "trivy.resources.limits.cpu": "500m", + "trivy.resources.limits.memory": "500M", + }, + workloadSpec: &corev1.Pod{ + TypeMeta: metav1.TypeMeta{ + Kind: "Pod", + APIVersion: "v1", + }, + ObjectMeta: metav1.ObjectMeta{ + Name: "nginx", + Namespace: "prod-ns", + }, + Spec: corev1.PodSpec{ + Containers: []corev1.Container{ + { + Name: "nginx", + Image: "poc.myregistry.harbor.com.pl/nginx:1.16", + }, + }, + }, + }, + expectedJobSpec: corev1.PodSpec{ + Affinity: starboard.LinuxNodeAffinity(), + RestartPolicy: corev1.RestartPolicyNever, + ServiceAccountName: "starboard-sa", + AutomountServiceAccountToken: pointer.BoolPtr(false), + Containers: []corev1.Container{ + { + Name: "nginx", + Image: "docker.io/aquasec/trivy:0.14.0", + ImagePullPolicy: corev1.PullIfNotPresent, + TerminationMessagePolicy: corev1.TerminationMessageFallbackToLogsOnError, + Env: []corev1.EnvVar{ + { + Name: "HTTP_PROXY", + ValueFrom: &corev1.EnvVarSource{ + ConfigMapKeyRef: &corev1.ConfigMapKeySelector{ + LocalObjectReference: corev1.LocalObjectReference{ + Name: "starboard-trivy-config", + }, + Key: "trivy.httpProxy", + Optional: pointer.BoolPtr(true), + }, + }, + }, + { + Name: "HTTPS_PROXY", + ValueFrom: &corev1.EnvVarSource{ + ConfigMapKeyRef: &corev1.ConfigMapKeySelector{ + LocalObjectReference: corev1.LocalObjectReference{ + Name: "starboard-trivy-config", + }, + Key: "trivy.httpsProxy", + Optional: pointer.BoolPtr(true), + }, + }, + }, + { + Name: "NO_PROXY", + ValueFrom: &corev1.EnvVarSource{ + ConfigMapKeyRef: &corev1.ConfigMapKeySelector{ + LocalObjectReference: corev1.LocalObjectReference{ + Name: "starboard-trivy-config", + }, + Key: "trivy.noProxy", + Optional: pointer.BoolPtr(true), + }, + }, + }, + { + Name: "TRIVY_SEVERITY", + ValueFrom: &corev1.EnvVarSource{ + ConfigMapKeyRef: &corev1.ConfigMapKeySelector{ + LocalObjectReference: corev1.LocalObjectReference{ + Name: "starboard-trivy-config", + }, + Key: "trivy.severity", + Optional: pointer.BoolPtr(true), + }, + }, + }, + { + Name: "TRIVY_IGNORE_UNFIXED", + ValueFrom: &corev1.EnvVarSource{ + ConfigMapKeyRef: &corev1.ConfigMapKeySelector{ + LocalObjectReference: corev1.LocalObjectReference{ + Name: "starboard-trivy-config", + }, + Key: "trivy.ignoreUnfixed", + Optional: pointer.BoolPtr(true), + }, + }, + }, + + { + Name: "TRIVY_SKIP_FILES", + ValueFrom: &corev1.EnvVarSource{ + ConfigMapKeyRef: &corev1.ConfigMapKeySelector{ + LocalObjectReference: corev1.LocalObjectReference{ + Name: "starboard-trivy-config", + }, + Key: "trivy.skipFiles", + Optional: pointer.BoolPtr(true), + }, + }, + }, + { + Name: "TRIVY_SKIP_DIRS", + ValueFrom: &corev1.EnvVarSource{ + ConfigMapKeyRef: &corev1.ConfigMapKeySelector{ + LocalObjectReference: corev1.LocalObjectReference{ + Name: "starboard-trivy-config", + }, + Key: "trivy.skipDirs", + Optional: pointer.BoolPtr(true), + }, + }, + }, + { + Name: "TRIVY_TOKEN_HEADER", + ValueFrom: &corev1.EnvVarSource{ + ConfigMapKeyRef: &corev1.ConfigMapKeySelector{ + LocalObjectReference: corev1.LocalObjectReference{ + Name: "starboard-trivy-config", + }, + Key: "trivy.serverTokenHeader", + Optional: pointer.BoolPtr(true), + }, + }, + }, + { + Name: "TRIVY_TOKEN", + ValueFrom: &corev1.EnvVarSource{ + SecretKeyRef: &corev1.SecretKeySelector{ + LocalObjectReference: corev1.LocalObjectReference{ + Name: "starboard-trivy-config", + }, + Key: "trivy.serverToken", + Optional: pointer.BoolPtr(true), + }, + }, + }, + { + Name: "TRIVY_CUSTOM_HEADERS", + ValueFrom: &corev1.EnvVarSource{ + SecretKeyRef: &corev1.SecretKeySelector{ + LocalObjectReference: corev1.LocalObjectReference{ + Name: "starboard-trivy-config", + }, + Key: "trivy.serverCustomHeaders", + Optional: pointer.BoolPtr(true), + }, + }, + }, + { + Name: "TRIVY_NON_SSL", + Value: "true", + }, + }, + Command: []string{ + "trivy", + }, + Args: []string{ + "--quiet", + "client", + "--format", + "json", + "--remote", + "http://trivy.trivy:4954", + "poc.myregistry.harbor.com.pl/nginx:1.16", + }, + Resources: corev1.ResourceRequirements{ + Requests: corev1.ResourceList{ + corev1.ResourceCPU: resource.MustParse("100m"), + corev1.ResourceMemory: resource.MustParse("100M"), + }, + Limits: corev1.ResourceList{ + corev1.ResourceCPU: resource.MustParse("500m"), + corev1.ResourceMemory: resource.MustParse("500M"), + }, + }, + }, + }, + }, + }, + { + name: "ClientServer mode with trivyignore file", + config: map[string]string{ + "trivy.imageRef": "docker.io/aquasec/trivy:0.14.0", + "trivy.mode": string(trivy.ClientServer), + "trivy.serverURL": "http://trivy.trivy:4954", + "trivy.ignoreFile": `# Accept the risk +CVE-2018-14618 + +# No impact in our settings +CVE-2019-1543`, + "trivy.dbRepository": defaultUpdateURL, + "trivy.resources.requests.cpu": "100m", + "trivy.resources.requests.memory": "100M", + "trivy.resources.limits.cpu": "500m", + "trivy.resources.limits.memory": "500M", + }, + workloadSpec: &corev1.Pod{ + TypeMeta: metav1.TypeMeta{ + Kind: "Pod", + APIVersion: "v1", + }, + ObjectMeta: metav1.ObjectMeta{ + Name: "nginx", + Namespace: "prod-ns", + }, + Spec: corev1.PodSpec{ + Containers: []corev1.Container{ + { + Name: "nginx", + Image: "nginx:1.16", + }, + }, + }, + }, + expectedJobSpec: corev1.PodSpec{ + Affinity: starboard.LinuxNodeAffinity(), + RestartPolicy: corev1.RestartPolicyNever, + ServiceAccountName: "starboard-sa", + AutomountServiceAccountToken: pointer.BoolPtr(false), + Volumes: []corev1.Volume{ + { + Name: "ignorefile", + VolumeSource: corev1.VolumeSource{ + ConfigMap: &corev1.ConfigMapVolumeSource{ + LocalObjectReference: corev1.LocalObjectReference{ + Name: "starboard-trivy-config", + }, + Items: []corev1.KeyToPath{ + { + Key: "trivy.ignoreFile", + Path: ".trivyignore", + }, + }, + }, + }, + }, + }, + Containers: []corev1.Container{ + { + Name: "nginx", + Image: "docker.io/aquasec/trivy:0.14.0", + ImagePullPolicy: corev1.PullIfNotPresent, + TerminationMessagePolicy: corev1.TerminationMessageFallbackToLogsOnError, + Env: []corev1.EnvVar{ + { + Name: "HTTP_PROXY", + ValueFrom: &corev1.EnvVarSource{ + ConfigMapKeyRef: &corev1.ConfigMapKeySelector{ + LocalObjectReference: corev1.LocalObjectReference{ + Name: "starboard-trivy-config", + }, + Key: "trivy.httpProxy", + Optional: pointer.BoolPtr(true), + }, + }, + }, + { + Name: "HTTPS_PROXY", + ValueFrom: &corev1.EnvVarSource{ + ConfigMapKeyRef: &corev1.ConfigMapKeySelector{ + LocalObjectReference: corev1.LocalObjectReference{ + Name: "starboard-trivy-config", + }, + Key: "trivy.httpsProxy", + Optional: pointer.BoolPtr(true), + }, + }, + }, + { + Name: "NO_PROXY", + ValueFrom: &corev1.EnvVarSource{ + ConfigMapKeyRef: &corev1.ConfigMapKeySelector{ + LocalObjectReference: corev1.LocalObjectReference{ + Name: "starboard-trivy-config", + }, + Key: "trivy.noProxy", + Optional: pointer.BoolPtr(true), + }, + }, + }, + { + Name: "TRIVY_SEVERITY", + ValueFrom: &corev1.EnvVarSource{ + ConfigMapKeyRef: &corev1.ConfigMapKeySelector{ + LocalObjectReference: corev1.LocalObjectReference{ + Name: "starboard-trivy-config", + }, + Key: "trivy.severity", + Optional: pointer.BoolPtr(true), + }, + }, + }, + { + Name: "TRIVY_IGNORE_UNFIXED", + ValueFrom: &corev1.EnvVarSource{ + ConfigMapKeyRef: &corev1.ConfigMapKeySelector{ + LocalObjectReference: corev1.LocalObjectReference{ + Name: "starboard-trivy-config", + }, + Key: "trivy.ignoreUnfixed", + Optional: pointer.BoolPtr(true), + }, + }, + }, + + { + Name: "TRIVY_SKIP_FILES", + ValueFrom: &corev1.EnvVarSource{ + ConfigMapKeyRef: &corev1.ConfigMapKeySelector{ + LocalObjectReference: corev1.LocalObjectReference{ + Name: "starboard-trivy-config", + }, + Key: "trivy.skipFiles", + Optional: pointer.BoolPtr(true), + }, + }, + }, + { + Name: "TRIVY_SKIP_DIRS", + ValueFrom: &corev1.EnvVarSource{ + ConfigMapKeyRef: &corev1.ConfigMapKeySelector{ + LocalObjectReference: corev1.LocalObjectReference{ + Name: "starboard-trivy-config", + }, + Key: "trivy.skipDirs", + Optional: pointer.BoolPtr(true), + }, + }, + }, + { + Name: "TRIVY_TOKEN_HEADER", + ValueFrom: &corev1.EnvVarSource{ + ConfigMapKeyRef: &corev1.ConfigMapKeySelector{ + LocalObjectReference: corev1.LocalObjectReference{ + Name: "starboard-trivy-config", + }, + Key: "trivy.serverTokenHeader", + Optional: pointer.BoolPtr(true), + }, + }, + }, + { + Name: "TRIVY_TOKEN", + ValueFrom: &corev1.EnvVarSource{ + SecretKeyRef: &corev1.SecretKeySelector{ + LocalObjectReference: corev1.LocalObjectReference{ + Name: "starboard-trivy-config", + }, + Key: "trivy.serverToken", + Optional: pointer.BoolPtr(true), + }, + }, + }, + { + Name: "TRIVY_CUSTOM_HEADERS", + ValueFrom: &corev1.EnvVarSource{ + SecretKeyRef: &corev1.SecretKeySelector{ + LocalObjectReference: corev1.LocalObjectReference{ + Name: "starboard-trivy-config", + }, + Key: "trivy.serverCustomHeaders", + Optional: pointer.BoolPtr(true), + }, + }, + }, + { + Name: "TRIVY_IGNOREFILE", + Value: "/etc/trivy/.trivyignore", + }, + }, + Command: []string{ + "trivy", + }, + Args: []string{ + "--quiet", + "client", + "--format", + "json", + "--remote", + "http://trivy.trivy:4954", + "nginx:1.16", + }, + Resources: corev1.ResourceRequirements{ + Requests: corev1.ResourceList{ + corev1.ResourceCPU: resource.MustParse("100m"), + corev1.ResourceMemory: resource.MustParse("100M"), + }, + Limits: corev1.ResourceList{ + corev1.ResourceCPU: resource.MustParse("500m"), + corev1.ResourceMemory: resource.MustParse("500M"), + }, + }, + VolumeMounts: []corev1.VolumeMount{ + { + Name: "ignorefile", + MountPath: "/etc/trivy/.trivyignore", + SubPath: ".trivyignore", + }, + }, + }, + }, + }, + }, + { + name: "Trivy fs scan command in Standalone mode", + config: map[string]string{ + "trivy.imageRef": "docker.io/aquasec/trivy:0.25.2", + "trivy.mode": string(trivy.Standalone), + "trivy.command": string(trivy.Filesystem), + "trivy.dbRepository": defaultUpdateURL, + "trivy.resources.requests.cpu": "100m", + "trivy.resources.requests.memory": "100M", + "trivy.resources.limits.cpu": "500m", + "trivy.resources.limits.memory": "500M", + }, + workloadSpec: &corev1.Pod{ + TypeMeta: metav1.TypeMeta{ + Kind: "Pod", + APIVersion: "v1", + }, + ObjectMeta: metav1.ObjectMeta{ + Name: "nginx", + Namespace: "prod-ns", + }, + Spec: corev1.PodSpec{ + Containers: []corev1.Container{ + { + Name: "nginx", + Image: "nginx:1.9.1", + }, + }, + NodeName: "kind-control-pane", + }}, + expectedJobSpec: corev1.PodSpec{ + Affinity: starboard.LinuxNodeAffinity(), + RestartPolicy: corev1.RestartPolicyNever, + ServiceAccountName: "starboard-sa", + AutomountServiceAccountToken: pointer.BoolPtr(false), + Volumes: []corev1.Volume{ + { + Name: trivy.FsSharedVolumeName, + VolumeSource: corev1.VolumeSource{ + EmptyDir: &corev1.EmptyDirVolumeSource{ + Medium: corev1.StorageMediumDefault, + }, + }, + }, + }, + InitContainers: []corev1.Container{ + { + Name: "00000000-0000-0000-0000-000000000001", + Image: "docker.io/aquasec/trivy:0.25.2", + ImagePullPolicy: corev1.PullIfNotPresent, + TerminationMessagePolicy: corev1.TerminationMessageFallbackToLogsOnError, + Command: []string{ + "cp", + "-v", + "/usr/local/bin/trivy", + trivy.SharedVolumeLocationOfTrivy, + }, + Resources: corev1.ResourceRequirements{ + Requests: corev1.ResourceList{ + corev1.ResourceCPU: resource.MustParse("100m"), + corev1.ResourceMemory: resource.MustParse("100M"), + }, + Limits: corev1.ResourceList{ + corev1.ResourceCPU: resource.MustParse("500m"), + corev1.ResourceMemory: resource.MustParse("500M"), + }, + }, + VolumeMounts: []corev1.VolumeMount{ + { + Name: trivy.FsSharedVolumeName, + ReadOnly: false, + MountPath: "/var/starboard", + }, + }, + }, + { + Name: "00000000-0000-0000-0000-000000000002", + Image: "docker.io/aquasec/trivy:0.25.2", + ImagePullPolicy: corev1.PullIfNotPresent, + TerminationMessagePolicy: corev1.TerminationMessageFallbackToLogsOnError, + Env: []corev1.EnvVar{ + { + Name: "HTTP_PROXY", + ValueFrom: &corev1.EnvVarSource{ + ConfigMapKeyRef: &corev1.ConfigMapKeySelector{ + LocalObjectReference: corev1.LocalObjectReference{ + Name: "starboard-trivy-config", + }, + Key: "trivy.httpProxy", + Optional: pointer.BoolPtr(true), + }, + }, + }, + { + Name: "HTTPS_PROXY", + ValueFrom: &corev1.EnvVarSource{ + ConfigMapKeyRef: &corev1.ConfigMapKeySelector{ + LocalObjectReference: corev1.LocalObjectReference{ + Name: "starboard-trivy-config", + }, + Key: "trivy.httpsProxy", + Optional: pointer.BoolPtr(true), + }, + }, + }, + { + Name: "NO_PROXY", + ValueFrom: &corev1.EnvVarSource{ + ConfigMapKeyRef: &corev1.ConfigMapKeySelector{ + LocalObjectReference: corev1.LocalObjectReference{ + Name: "starboard-trivy-config", + }, + Key: "trivy.noProxy", + Optional: pointer.BoolPtr(true), + }, + }, + }, + + { + Name: "GITHUB_TOKEN", + ValueFrom: &corev1.EnvVarSource{ + SecretKeyRef: &corev1.SecretKeySelector{ + LocalObjectReference: corev1.LocalObjectReference{ + Name: "starboard-trivy-config", + }, + Key: "trivy.githubToken", + Optional: pointer.BoolPtr(true), + }, + }, + }, + }, + Command: []string{ + "trivy", + }, + Args: []string{ + "--download-db-only", + "--cache-dir", + "/var/starboard/trivy-db", + "--db-repository", defaultUpdateURL, + }, + Resources: corev1.ResourceRequirements{ + Requests: corev1.ResourceList{ + corev1.ResourceCPU: resource.MustParse("100m"), + corev1.ResourceMemory: resource.MustParse("100M"), + }, + Limits: corev1.ResourceList{ + corev1.ResourceCPU: resource.MustParse("500m"), + corev1.ResourceMemory: resource.MustParse("500M"), + }, + }, + VolumeMounts: []corev1.VolumeMount{ + { + Name: trivy.FsSharedVolumeName, + ReadOnly: false, + MountPath: "/var/starboard", + }, + }, + }, + }, + Containers: []corev1.Container{ + { + Name: "nginx", + Image: "nginx:1.9.1", + ImagePullPolicy: corev1.PullNever, + TerminationMessagePolicy: corev1.TerminationMessageFallbackToLogsOnError, + Env: []corev1.EnvVar{ + { + Name: "TRIVY_SEVERITY", + ValueFrom: &corev1.EnvVarSource{ + ConfigMapKeyRef: &corev1.ConfigMapKeySelector{ + LocalObjectReference: corev1.LocalObjectReference{ + Name: "starboard-trivy-config", + }, + Key: "trivy.severity", + Optional: pointer.BoolPtr(true), + }, + }, + }, + { + Name: "TRIVY_SKIP_FILES", + ValueFrom: &corev1.EnvVarSource{ + ConfigMapKeyRef: &corev1.ConfigMapKeySelector{ + LocalObjectReference: corev1.LocalObjectReference{ + Name: "starboard-trivy-config", + }, + Key: "trivy.skipFiles", + Optional: pointer.BoolPtr(true), + }, + }, + }, + { + Name: "TRIVY_SKIP_DIRS", + ValueFrom: &corev1.EnvVarSource{ + ConfigMapKeyRef: &corev1.ConfigMapKeySelector{ + LocalObjectReference: corev1.LocalObjectReference{ + Name: "starboard-trivy-config", + }, + Key: "trivy.skipDirs", + Optional: pointer.BoolPtr(true), + }, + }, + }, + { + Name: "HTTP_PROXY", + ValueFrom: &corev1.EnvVarSource{ + ConfigMapKeyRef: &corev1.ConfigMapKeySelector{ + LocalObjectReference: corev1.LocalObjectReference{ + Name: "starboard-trivy-config", + }, + Key: "trivy.httpProxy", + Optional: pointer.BoolPtr(true), + }, + }, + }, + { + Name: "HTTPS_PROXY", + ValueFrom: &corev1.EnvVarSource{ + ConfigMapKeyRef: &corev1.ConfigMapKeySelector{ + LocalObjectReference: corev1.LocalObjectReference{ + Name: "starboard-trivy-config", + }, + Key: "trivy.httpsProxy", + Optional: pointer.BoolPtr(true), + }, + }, + }, + { + Name: "NO_PROXY", + ValueFrom: &corev1.EnvVarSource{ + ConfigMapKeyRef: &corev1.ConfigMapKeySelector{ + LocalObjectReference: corev1.LocalObjectReference{ + Name: "starboard-trivy-config", + }, + Key: "trivy.noProxy", + Optional: pointer.BoolPtr(true), + }, + }, + }, + }, + Command: []string{ + trivy.SharedVolumeLocationOfTrivy, + }, + Args: []string{ + "--skip-update", + "--cache-dir", + "/var/starboard/trivy-db", + "--quiet", + "fs", + "--format", + "json", + "/", + }, + Resources: corev1.ResourceRequirements{ + Requests: corev1.ResourceList{ + corev1.ResourceCPU: resource.MustParse("100m"), + corev1.ResourceMemory: resource.MustParse("100M"), + }, + Limits: corev1.ResourceList{ + corev1.ResourceCPU: resource.MustParse("500m"), + corev1.ResourceMemory: resource.MustParse("500M"), + }, + }, + VolumeMounts: []corev1.VolumeMount{ + { + Name: trivy.FsSharedVolumeName, + ReadOnly: false, + MountPath: "/var/starboard", + }, + }, + SecurityContext: &corev1.SecurityContext{ + Privileged: pointer.BoolPtr(false), + AllowPrivilegeEscalation: pointer.BoolPtr(false), + Capabilities: &corev1.Capabilities{ + Drop: []corev1.Capability{"all"}, + }, + ReadOnlyRootFilesystem: pointer.BoolPtr(true), + RunAsUser: pointer.Int64(0), + }, + }, + }, + SecurityContext: &corev1.PodSecurityContext{}, + NodeName: "kind-control-pane", + }, + }, + } + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + fakeclient := fake.NewClientBuilder().WithObjects( + &corev1.ConfigMap{ + ObjectMeta: metav1.ObjectMeta{ + Name: "starboard-trivy-config", + Namespace: "starboard-ns", + }, + Data: tc.config, + }, + ).Build() + pluginContext := starboard.NewPluginContext(). + WithName(grype.Plugin). + WithNamespace("starboard-ns"). + WithServiceAccountName("starboard-sa"). + WithClient(fakeclient). + Get() + instance := trivy.NewPlugin(fixedClock, ext.NewSimpleIDGenerator(), fakeclient) + jobSpec, secrets, err := instance.GetScanJobSpec(pluginContext, tc.workloadSpec, nil) + require.NoError(t, err) + assert.Empty(t, secrets) + assert.Equal(t, tc.expectedJobSpec, jobSpec) + }) + } + + testCases = []struct { + name string + config map[string]string + workloadSpec client.Object + expectedSecrets []corev1.Secret + expectedJobSpec corev1.PodSpec + }{{ + name: "Trivy fs scan command in Standalone mode", + config: map[string]string{ + "trivy.imageRef": "docker.io/aquasec/trivy:0.22.0", + "trivy.mode": string(trivy.Standalone), + "trivy.command": string(trivy.Filesystem), + "trivy.dbRepository": defaultUpdateURL, + "trivy.resources.requests.cpu": "100m", + "trivy.resources.requests.memory": "100M", + "trivy.resources.limits.cpu": "500m", + "trivy.resources.limits.memory": "500M", + }, + workloadSpec: &corev1.Pod{ + TypeMeta: metav1.TypeMeta{ + Kind: "Pod", + APIVersion: "v1", + }, + ObjectMeta: metav1.ObjectMeta{ + Name: "nginx", + Namespace: "prod-ns", + }, + Spec: corev1.PodSpec{ + Containers: []corev1.Container{ + { + Name: "nginx", + Image: "nginx:1.9.1", + }, + }, + NodeName: "kind-control-pane", + ServiceAccountName: "nginx-sa", + }}, + expectedJobSpec: corev1.PodSpec{ + Affinity: starboard.LinuxNodeAffinity(), + RestartPolicy: corev1.RestartPolicyNever, + ServiceAccountName: "starboard-sa", + AutomountServiceAccountToken: pointer.BoolPtr(false), + Volumes: []corev1.Volume{ + { + Name: trivy.FsSharedVolumeName, + VolumeSource: corev1.VolumeSource{ + EmptyDir: &corev1.EmptyDirVolumeSource{ + Medium: corev1.StorageMediumDefault, + }, + }, + }, + }, + InitContainers: []corev1.Container{ + { + Name: "00000000-0000-0000-0000-000000000001", + Image: "docker.io/aquasec/trivy:0.22.0", + ImagePullPolicy: corev1.PullIfNotPresent, + TerminationMessagePolicy: corev1.TerminationMessageFallbackToLogsOnError, + Command: []string{ + "cp", + "-v", + "/usr/local/bin/trivy", + trivy.SharedVolumeLocationOfTrivy, + }, + Resources: corev1.ResourceRequirements{ + Requests: corev1.ResourceList{ + corev1.ResourceCPU: resource.MustParse("100m"), + corev1.ResourceMemory: resource.MustParse("100M"), + }, + Limits: corev1.ResourceList{ + corev1.ResourceCPU: resource.MustParse("500m"), + corev1.ResourceMemory: resource.MustParse("500M"), + }, + }, + VolumeMounts: []corev1.VolumeMount{ + { + Name: trivy.FsSharedVolumeName, + ReadOnly: false, + MountPath: "/var/starboard", + }, + }, + }, + { + Name: "00000000-0000-0000-0000-000000000002", + Image: "docker.io/aquasec/trivy:0.22.0", + ImagePullPolicy: corev1.PullIfNotPresent, + TerminationMessagePolicy: corev1.TerminationMessageFallbackToLogsOnError, + Env: []corev1.EnvVar{ + { + Name: "HTTP_PROXY", + ValueFrom: &corev1.EnvVarSource{ + ConfigMapKeyRef: &corev1.ConfigMapKeySelector{ + LocalObjectReference: corev1.LocalObjectReference{ + Name: "starboard-trivy-config", + }, + Key: "trivy.httpProxy", + Optional: pointer.BoolPtr(true), + }, + }, + }, + { + Name: "HTTPS_PROXY", + ValueFrom: &corev1.EnvVarSource{ + ConfigMapKeyRef: &corev1.ConfigMapKeySelector{ + LocalObjectReference: corev1.LocalObjectReference{ + Name: "starboard-trivy-config", + }, + Key: "trivy.httpsProxy", + Optional: pointer.BoolPtr(true), + }, + }, + }, + { + Name: "NO_PROXY", + ValueFrom: &corev1.EnvVarSource{ + ConfigMapKeyRef: &corev1.ConfigMapKeySelector{ + LocalObjectReference: corev1.LocalObjectReference{ + Name: "starboard-trivy-config", + }, + Key: "trivy.noProxy", + Optional: pointer.BoolPtr(true), + }, + }, + }, + + { + Name: "GITHUB_TOKEN", + ValueFrom: &corev1.EnvVarSource{ + SecretKeyRef: &corev1.SecretKeySelector{ + LocalObjectReference: corev1.LocalObjectReference{ + Name: "starboard-trivy-config", + }, + Key: "trivy.githubToken", + Optional: pointer.BoolPtr(true), + }, + }, + }, + }, + Command: []string{ + "trivy", + }, + Args: []string{ + "--download-db-only", + "--cache-dir", + "/var/starboard/trivy-db", + "--db-repository", defaultUpdateURL, + }, + Resources: corev1.ResourceRequirements{ + Requests: corev1.ResourceList{ + corev1.ResourceCPU: resource.MustParse("100m"), + corev1.ResourceMemory: resource.MustParse("100M"), + }, + Limits: corev1.ResourceList{ + corev1.ResourceCPU: resource.MustParse("500m"), + corev1.ResourceMemory: resource.MustParse("500M"), + }, + }, + VolumeMounts: []corev1.VolumeMount{ + { + Name: trivy.FsSharedVolumeName, + ReadOnly: false, + MountPath: "/var/starboard", + }, + }, + }, + }, + Containers: []corev1.Container{ + { + Name: "nginx", + Image: "nginx:1.9.1", + ImagePullPolicy: corev1.PullIfNotPresent, + TerminationMessagePolicy: corev1.TerminationMessageFallbackToLogsOnError, + Env: []corev1.EnvVar{ + { + Name: "TRIVY_SEVERITY", + ValueFrom: &corev1.EnvVarSource{ + ConfigMapKeyRef: &corev1.ConfigMapKeySelector{ + LocalObjectReference: corev1.LocalObjectReference{ + Name: "starboard-trivy-config", + }, + Key: "trivy.severity", + Optional: pointer.BoolPtr(true), + }, + }, + }, + { + Name: "TRIVY_SKIP_FILES", + ValueFrom: &corev1.EnvVarSource{ + ConfigMapKeyRef: &corev1.ConfigMapKeySelector{ + LocalObjectReference: corev1.LocalObjectReference{ + Name: "starboard-trivy-config", + }, + Key: "trivy.skipFiles", + Optional: pointer.BoolPtr(true), + }, + }, + }, + { + Name: "TRIVY_SKIP_DIRS", + ValueFrom: &corev1.EnvVarSource{ + ConfigMapKeyRef: &corev1.ConfigMapKeySelector{ + LocalObjectReference: corev1.LocalObjectReference{ + Name: "starboard-trivy-config", + }, + Key: "trivy.skipDirs", + Optional: pointer.BoolPtr(true), + }, + }, + }, + { + Name: "HTTP_PROXY", + ValueFrom: &corev1.EnvVarSource{ + ConfigMapKeyRef: &corev1.ConfigMapKeySelector{ + LocalObjectReference: corev1.LocalObjectReference{ + Name: "starboard-trivy-config", + }, + Key: "trivy.httpProxy", + Optional: pointer.BoolPtr(true), + }, + }, + }, + { + Name: "HTTPS_PROXY", + ValueFrom: &corev1.EnvVarSource{ + ConfigMapKeyRef: &corev1.ConfigMapKeySelector{ + LocalObjectReference: corev1.LocalObjectReference{ + Name: "starboard-trivy-config", + }, + Key: "trivy.httpsProxy", + Optional: pointer.BoolPtr(true), + }, + }, + }, + { + Name: "NO_PROXY", + ValueFrom: &corev1.EnvVarSource{ + ConfigMapKeyRef: &corev1.ConfigMapKeySelector{ + LocalObjectReference: corev1.LocalObjectReference{ + Name: "starboard-trivy-config", + }, + Key: "trivy.noProxy", + Optional: pointer.BoolPtr(true), + }, + }, + }, + }, + Command: []string{ + trivy.SharedVolumeLocationOfTrivy, + }, + Args: []string{ + "--skip-update", + "--cache-dir", + "/var/starboard/trivy-db", + "--quiet", + "fs", + "--format", + "json", + "/", + }, + Resources: corev1.ResourceRequirements{ + Requests: corev1.ResourceList{ + corev1.ResourceCPU: resource.MustParse("100m"), + corev1.ResourceMemory: resource.MustParse("100M"), + }, + Limits: corev1.ResourceList{ + corev1.ResourceCPU: resource.MustParse("500m"), + corev1.ResourceMemory: resource.MustParse("500M"), + }, + }, + VolumeMounts: []corev1.VolumeMount{ + { + Name: trivy.FsSharedVolumeName, + ReadOnly: false, + MountPath: "/var/starboard", + }, + }, + SecurityContext: &corev1.SecurityContext{ + Privileged: pointer.BoolPtr(false), + AllowPrivilegeEscalation: pointer.BoolPtr(false), + Capabilities: &corev1.Capabilities{ + Drop: []corev1.Capability{"all"}, + }, + ReadOnlyRootFilesystem: pointer.BoolPtr(true), + RunAsUser: pointer.Int64(0), + }, + }, + }, + SecurityContext: &corev1.PodSecurityContext{}, + }, + }} + // Test cases when starboard is enabled with option to run job in the namespace of workload + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + fakeclient := fake.NewClientBuilder().WithObjects( + &corev1.ConfigMap{ + ObjectMeta: metav1.ObjectMeta{ + Name: "starboard-trivy-config", + Namespace: "starboard-ns", + }, + Data: tc.config, + }, + ).Build() + pluginContext := starboard.NewPluginContext(). + WithName(trivy.Plugin). + WithNamespace("starboard-ns"). + WithServiceAccountName("starboard-sa"). + WithClient(fakeclient). + WithStarboardConfig(map[string]string{starboard.KeyVulnerabilityScansInSameNamespace: "true"}). + Get() + instance := trivy.NewPlugin(fixedClock, ext.NewSimpleIDGenerator(), fakeclient) + jobSpec, secrets, err := instance.GetScanJobSpec(pluginContext, tc.workloadSpec, nil) + require.NoError(t, err) + assert.Empty(t, secrets) + assert.Equal(t, tc.expectedJobSpec, jobSpec) + }) + } +} + +var ( + sampleReportAsString = `{ + "matches": [ + { + "vulnerability": { + "id": "CVE-2015-5237", + "dataSource": "https://nvd.nist.gov/vuln/detail/CVE-2015-5237", + "namespace": "nvd", + "severity": "High", + "urls": [ + "https://github.com/google/protobuf/issues/760", + "https://bugzilla.redhat.com/show_bug.cgi?id=1256426", + "http://www.openwall.com/lists/oss-security/2015/08/27/2", + "https://lists.apache.org/thread.html/b0656d359c7d40ec9f39c8cc61bca66802ef9a2a12ee199f5b0c1442@%3Cdev.drill.apache.org%3E", + "https://lists.apache.org/thread.html/519eb0fd45642dcecd9ff74cb3e71c20a4753f7d82e2f07864b5108f@%3Cdev.drill.apache.org%3E", + "https://lists.apache.org/thread.html/ra28fed69eef3a71e5fe5daea001d0456b05b102044237330ec5c7c82@%3Ccommits.pulsar.apache.org%3E", + "https://lists.apache.org/thread.html/f9bc3e55f4e28d1dcd1a69aae6d53e609a758e34d2869b4d798e13cc@%3Cissues.drill.apache.org%3E", + "https://lists.apache.org/thread.html/r17dc6f394429f6bffb5e4c66555d93c2e9923cbbdc5a93db9a56c1c7@%3Ccommits.pulsar.apache.org%3E", + "https://lists.apache.org/thread.html/r42e47994734cd1980ef3e204a40555336e10cc80096927aca2f37d90@%3Ccommits.pulsar.apache.org%3E", + "https://lists.apache.org/thread.html/re6d04a214424a97ea59c62190d79316edf311a0a6346524dfef3b940@%3Ccommits.pulsar.apache.org%3E", + "https://lists.apache.org/thread.html/r1263fa5b51e4ec3cb8f09ff40e4747428c71198e9bee93349ec96a3c@%3Ccommits.pulsar.apache.org%3E", + "https://lists.apache.org/thread.html/r42ef6acfb0d86a2df0c2390702ecbe97d2104a331560f2790d17ca69@%3Ccommits.pulsar.apache.org%3E", + "https://lists.apache.org/thread.html/rb71dac1d9dd4e8a8ae3dbc033aeae514eda9be1263c1df3b42a530a2@%3Ccommits.pulsar.apache.org%3E", + "https://lists.apache.org/thread.html/r320dc858da88846ba00bb077bcca2cdf75b7dde0f6eb3a3d60dba6a1@%3Ccommits.pulsar.apache.org%3E", + "https://lists.apache.org/thread.html/r85c9a764b573c786224688cc906c27e28343e18f5b33387f94cae90f@%3Ccommits.pulsar.apache.org%3E", + "https://lists.apache.org/thread.html/r02e39d7beb32eebcdbb4b516e95f67d71c90d5d462b26f4078d21eeb@%3Cuser.flink.apache.org%3E", + "https://lists.apache.org/thread.html/r02e39d7beb32eebcdbb4b516e95f67d71c90d5d462b26f4078d21eeb@%3Cdev.flink.apache.org%3E", + "https://lists.apache.org/thread.html/r5e52caf41dc49df55b4ee80758356fe1ff2a88179ff24c685de7c28d@%3Ccommits.pulsar.apache.org%3E", + "https://lists.apache.org/thread.html/rf7539287c90be979bac94af9aaba34118fbf968864944b4871af48dd@%3Ccommits.pulsar.apache.org%3E", + "https://lists.apache.org/thread.html/r1d274d647b3c2060df9be21eade4ce56d3a59998cf19ac72662dd994@%3Ccommits.pulsar.apache.org%3E", + "https://lists.apache.org/thread.html/r4886108206d4c535db9b20c813fe4723d4fe6a91b9278382af8b9d08@%3Cissues.spark.apache.org%3E", + "https://lists.apache.org/thread.html/rb40dc9d63a5331bce8e80865b7fa3af9dd31e16555affd697b6f3526@%3Cissues.spark.apache.org%3E", + "https://lists.apache.org/thread.html/r5741f4dbdd129dbb9885f5fb170dc1b24a06b9313bedef5e67fded94@%3Cissues.spark.apache.org%3E", + "https://lists.apache.org/thread.html/r14fa8d38d5757254f1a2e112270c996711d514de2e3b01c93d397ab4@%3Cissues.spark.apache.org%3E", + "https://lists.apache.org/thread.html/r2ea33ce5591a9cb9ed52750b6ab42ab658f529a7028c3166ba93c7d5@%3Ccommon-issues.hadoop.apache.org%3E", + "https://lists.apache.org/thread.html/r00d9ab1fc0f1daf14cd4386564dd84f7889404438d81462c86dfa836@%3Ccommon-dev.hadoop.apache.org%3E", + "https://lists.apache.org/thread.html/r764fc66435ee4d185d359c28c0887d3e5866d7292a8d5598d9e7cbc4@%3Ccommon-issues.hadoop.apache.org%3E", + "https://lists.apache.org/thread.html/r0ca83171c4898dc92b86fa6f484a7be1dc96206765f4d01dce0f1b28@%3Ccommon-issues.hadoop.apache.org%3E", + "https://lists.apache.org/thread.html/r00097d0b5b6164ea428554007121d5dc1f88ba2af7b9e977a10572cd@%3Cdev.hbase.apache.org%3E", + "https://lists.apache.org/thread.html/rd64381fb8f92d640c1975dc50dcdf1b8512e02a2a7b20292d3565cae@%3Cissues.hbase.apache.org%3E", + "https://lists.apache.org/thread.html/r4ef574a5621b0e670a3ce641e9922543e34f22bf4c9ee9584aa67fcf@%3Cissues.hbase.apache.org%3E", + "https://lists.apache.org/thread.html/r7fed8dd9bee494094e7011cf3c2ab75bd8754ea314c6734688c42932@%3Ccommon-issues.hadoop.apache.org%3E" + ], + "description": "protobuf allows remote authenticated attackers to cause a heap-based buffer overflow.", + "cvss": [ + { + "version": "2.0", + "vector": "AV:N/AC:L/Au:S/C:P/I:P/A:P", + "metrics": { + "baseScore": 6.5, + "exploitabilityScore": 8, + "impactScore": 6.4 + }, + "vendorMetadata": {} + }, + { + "version": "3.1", + "vector": "CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:H", + "metrics": { + "baseScore": 8.8, + "exploitabilityScore": 2.8, + "impactScore": 5.9 + }, + "vendorMetadata": {} + } + ], + "fix": { + "versions": [], + "state": "unknown" + }, + "advisories": [] + }, + "relatedVulnerabilities": [], + "matchDetails": [ + { + "type": "cpe-match", + "matcher": "stock-matcher", + "searchedBy": { + "namespace": "nvd", + "cpes": [ + "cpe:2.3:a:google:protobuf:v1.27.1:*:*:*:*:*:*:*" + ] + }, + "found": { + "versionConstraint": "<= 3.1.0 (unknown)", + "cpes": [ + "cpe:2.3:a:google:protobuf:*:*:*:*:*:*:*:*" + ] + } + } + ], + "artifact": { + "name": "google.golang.org/protobuf", + "version": "v1.27.1", + "type": "go-module", + "locations": [ + { + "path": "/grype", + "layerID": "sha256:65ea08da2c3941d8571059e12f7c48821150a89ba662c9751acf5d3664df3f86" + } + ], + "language": "go", + "licenses": [], + "cpes": [ + "cpe:2.3:a:google:protobuf:v1.27.1:*:*:*:*:*:*:*" + ], + "purl": "pkg:golang/google.golang.org/protobuf@v1.27.1", + "upstreams": [] + } + }, + { + "vulnerability": { + "id": "CVE-2021-22570", + "dataSource": "https://nvd.nist.gov/vuln/detail/CVE-2021-22570", + "namespace": "nvd", + "severity": "High", + "urls": [ + "https://github.com/protocolbuffers/protobuf/releases/tag/v3.15.0", + "https://lists.fedoraproject.org/archives/list/package-announce@lists.fedoraproject.org/message/IFX6KPNOFHYD6L4XES5PCM3QNSKZBOTQ/", + "https://lists.fedoraproject.org/archives/list/package-announce@lists.fedoraproject.org/message/3DVUZPALAQ34TQP6KFNLM4IZS6B32XSA/", + "https://lists.fedoraproject.org/archives/list/package-announce@lists.fedoraproject.org/message/BTRGBRC5KGCA4SK5MUNLPYJRAGXMBIYY/", + "https://lists.fedoraproject.org/archives/list/package-announce@lists.fedoraproject.org/message/NVTWVQRB5OCCTMKEQFY5MYED3DXDVSLP/", + "https://lists.fedoraproject.org/archives/list/package-announce@lists.fedoraproject.org/message/5PAGL5M2KGYPN3VEQCRJJE6NA7D5YG5X/", + "https://lists.fedoraproject.org/archives/list/package-announce@lists.fedoraproject.org/message/KQJB6ZPRLKV6WCMX2PRRRQBFAOXFBK6B/", + "https://lists.fedoraproject.org/archives/list/package-announce@lists.fedoraproject.org/message/MRWRAXAFR3JR7XCFWTHC2KALSZKWACCE/" + ], + "description": "Nullptr dereference when a null char is present in a proto symbol. The symbol is parsed incorrectly, leading to an unchecked call into the proto file's name during generation of the resulting error message. Since the symbol is incorrectly parsed, the file is nullptr. We recommend upgrading to version 3.15.0 or greater.", + "cvss": [ + { + "version": "2.0", + "vector": "AV:N/AC:L/Au:N/C:N/I:N/A:P", + "metrics": { + "baseScore": 5, + "exploitabilityScore": 10, + "impactScore": 2.9 + }, + "vendorMetadata": {} + }, + { + "version": "3.1", + "vector": "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H", + "metrics": { + "baseScore": 7.5, + "exploitabilityScore": 3.9, + "impactScore": 3.6 + }, + "vendorMetadata": {} + } + ], + "fix": { + "versions": [], + "state": "unknown" + }, + "advisories": [] + }, + "relatedVulnerabilities": [], + "matchDetails": [ + { + "type": "cpe-match", + "matcher": "stock-matcher", + "searchedBy": { + "namespace": "nvd", + "cpes": [ + "cpe:2.3:a:google:protobuf:v1.27.1:*:*:*:*:*:*:*" + ] + }, + "found": { + "versionConstraint": "< 3.15.0 (unknown)", + "cpes": [ + "cpe:2.3:a:google:protobuf:*:*:*:*:*:*:*:*" + ] + } + } + ], + "artifact": { + "name": "google.golang.org/protobuf", + "version": "v1.27.1", + "type": "go-module", + "locations": [ + { + "path": "/grype", + "layerID": "sha256:65ea08da2c3941d8571059e12f7c48821150a89ba662c9751acf5d3664df3f86" + } + ], + "language": "go", + "licenses": [], + "cpes": [ + "cpe:2.3:a:google:protobuf:v1.27.1:*:*:*:*:*:*:*" + ], + "purl": "pkg:golang/google.golang.org/protobuf@v1.27.1", + "upstreams": [] + } + } + ], + "source": { + "type": "image", + "target": { + "userInput": "anchore/grype", + "imageID": "sha256:a28988cab42062d638840c4e5d5e0c46ba6dbea526a6a3858d461c50554b2795", + "manifestDigest": "sha256:e3f50502292a86fc8f188336153c2e0ff9216bdf2df63c4e0323d6e163210454", + "mediaType": "application/vnd.docker.distribution.manifest.v2+json", + "tags": [], + "imageSize": 29665751, + "layers": [ + { + "mediaType": "application/vnd.docker.image.rootfs.diff.tar.gzip", + "digest": "sha256:b7574c4c31a87c8530c59277dfef86aac806cf6833f862833959df6b04e996d1", + "size": 203223 + }, + { + "mediaType": "application/vnd.docker.image.rootfs.diff.tar.gzip", + "digest": "sha256:61857988458c05660c02dc985b7dfdff0e8083d44829a546f2a0b411354ab631", + "size": 0 + }, + { + "mediaType": "application/vnd.docker.image.rootfs.diff.tar.gzip", + "digest": "sha256:65ea08da2c3941d8571059e12f7c48821150a89ba662c9751acf5d3664df3f86", + "size": 29462528 + } + ], + "manifest": "", + "config": "", + "repoDigests": [ + "index.docker.io/anchore/grype@sha256:c33bd4cbedcc4ef190abe2f24c4f70ad6ddece0ec40451415efa1dea99f3a421" + ], + "architecture": "", + "os": "" + } + }, + "distro": { + "name": "", + "version": "", + "idLike": null + }, + "descriptor": { + "name": "grype", + "version": "0.34.7", + "configuration": { + "configPath": "", + "output": "json", + "file": "", + "distro": "", + "add-cpes-if-none": false, + "output-template-file": "", + "quiet": false, + "check-for-app-update": true, + "only-fixed": false, + "platform": "", + "search": { + "scope": "Squashed", + "unindexed-archives": false, + "indexed-archives": true + }, + "ignore": null, + "exclude": [], + "db": { + "cache-dir": "/.cache/grype/db", + "update-url": "https://toolbox-data.anchore.io/grype/databases/listing.json", + "ca-cert": "", + "auto-update": true, + "validate-by-hash-on-start": false + }, + "dev": { + "profile-cpu": false, + "profile-mem": false + }, + "fail-on-severity": "", + "registry": { + "insecure-skip-tls-verify": false, + "insecure-use-http": false, + "auth": [] + }, + "log": { + "structured": false, + "level": "", + "file": "" + } + }, + "db": { + "built": "2022-04-11T08:15:09Z", + "schemaVersion": 3, + "location": "/.cache/grype/db/3", + "checksum": "sha256:7a2e9977d84257c4aa0010ab1e256ea31e5125cb9c567926440703e65cf463b9", + "error": null + } + } + } ` + + sampleReport = v1alpha1.VulnerabilityReportData{ + UpdateTimestamp: metav1.NewTime(fixedTime), + Scanner: v1alpha1.Scanner{ + Name: "Grype", + Vendor: "Anchore Inc.", + Version: "0.34.7", + }, + Registry: v1alpha1.Registry{ + Server: "index.docker.io", + }, + Artifact: v1alpha1.Artifact{ + Repository: "anchore/grype", + Tag: "latest", + }, + Summary: v1alpha1.VulnerabilitySummary{ + CriticalCount: 0, + HighCount: 2, + MediumCount: 0, + LowCount: 0, + NoneCount: 0, + UnknownCount: 0, + }, + Vulnerabilities: []v1alpha1.Vulnerability{ + { + VulnerabilityID: "CVE-2015-5237", + Resource: "google.golang.org/protobuf", + InstalledVersion: "v1.27.1", + FixedVersion: "", + Severity: v1alpha1.SeverityHigh, + Title: "google.golang.org/protobuf", + Description: "protobuf allows remote authenticated attackers to cause a heap-based buffer overflow.", + PrimaryLink: "https://nvd.nist.gov/vuln/detail/CVE-2015-5237", + Score: pointer.Float64(8.8), + Links: []string{ + "https://github.com/google/protobuf/issues/760", + "https://bugzilla.redhat.com/show_bug.cgi?id=1256426", + "http://www.openwall.com/lists/oss-security/2015/08/27/2", + "https://lists.apache.org/thread.html/b0656d359c7d40ec9f39c8cc61bca66802ef9a2a12ee199f5b0c1442@%3Cdev.drill.apache.org%3E", + "https://lists.apache.org/thread.html/519eb0fd45642dcecd9ff74cb3e71c20a4753f7d82e2f07864b5108f@%3Cdev.drill.apache.org%3E", + "https://lists.apache.org/thread.html/ra28fed69eef3a71e5fe5daea001d0456b05b102044237330ec5c7c82@%3Ccommits.pulsar.apache.org%3E", + "https://lists.apache.org/thread.html/f9bc3e55f4e28d1dcd1a69aae6d53e609a758e34d2869b4d798e13cc@%3Cissues.drill.apache.org%3E", + "https://lists.apache.org/thread.html/r17dc6f394429f6bffb5e4c66555d93c2e9923cbbdc5a93db9a56c1c7@%3Ccommits.pulsar.apache.org%3E", + "https://lists.apache.org/thread.html/r42e47994734cd1980ef3e204a40555336e10cc80096927aca2f37d90@%3Ccommits.pulsar.apache.org%3E", + "https://lists.apache.org/thread.html/re6d04a214424a97ea59c62190d79316edf311a0a6346524dfef3b940@%3Ccommits.pulsar.apache.org%3E", + "https://lists.apache.org/thread.html/r1263fa5b51e4ec3cb8f09ff40e4747428c71198e9bee93349ec96a3c@%3Ccommits.pulsar.apache.org%3E", + "https://lists.apache.org/thread.html/r42ef6acfb0d86a2df0c2390702ecbe97d2104a331560f2790d17ca69@%3Ccommits.pulsar.apache.org%3E", + "https://lists.apache.org/thread.html/rb71dac1d9dd4e8a8ae3dbc033aeae514eda9be1263c1df3b42a530a2@%3Ccommits.pulsar.apache.org%3E", + "https://lists.apache.org/thread.html/r320dc858da88846ba00bb077bcca2cdf75b7dde0f6eb3a3d60dba6a1@%3Ccommits.pulsar.apache.org%3E", + "https://lists.apache.org/thread.html/r85c9a764b573c786224688cc906c27e28343e18f5b33387f94cae90f@%3Ccommits.pulsar.apache.org%3E", + "https://lists.apache.org/thread.html/r02e39d7beb32eebcdbb4b516e95f67d71c90d5d462b26f4078d21eeb@%3Cuser.flink.apache.org%3E", + "https://lists.apache.org/thread.html/r02e39d7beb32eebcdbb4b516e95f67d71c90d5d462b26f4078d21eeb@%3Cdev.flink.apache.org%3E", + "https://lists.apache.org/thread.html/r5e52caf41dc49df55b4ee80758356fe1ff2a88179ff24c685de7c28d@%3Ccommits.pulsar.apache.org%3E", + "https://lists.apache.org/thread.html/rf7539287c90be979bac94af9aaba34118fbf968864944b4871af48dd@%3Ccommits.pulsar.apache.org%3E", + "https://lists.apache.org/thread.html/r1d274d647b3c2060df9be21eade4ce56d3a59998cf19ac72662dd994@%3Ccommits.pulsar.apache.org%3E", + "https://lists.apache.org/thread.html/r4886108206d4c535db9b20c813fe4723d4fe6a91b9278382af8b9d08@%3Cissues.spark.apache.org%3E", + "https://lists.apache.org/thread.html/rb40dc9d63a5331bce8e80865b7fa3af9dd31e16555affd697b6f3526@%3Cissues.spark.apache.org%3E", + "https://lists.apache.org/thread.html/r5741f4dbdd129dbb9885f5fb170dc1b24a06b9313bedef5e67fded94@%3Cissues.spark.apache.org%3E", + "https://lists.apache.org/thread.html/r14fa8d38d5757254f1a2e112270c996711d514de2e3b01c93d397ab4@%3Cissues.spark.apache.org%3E", + "https://lists.apache.org/thread.html/r2ea33ce5591a9cb9ed52750b6ab42ab658f529a7028c3166ba93c7d5@%3Ccommon-issues.hadoop.apache.org%3E", + "https://lists.apache.org/thread.html/r00d9ab1fc0f1daf14cd4386564dd84f7889404438d81462c86dfa836@%3Ccommon-dev.hadoop.apache.org%3E", + "https://lists.apache.org/thread.html/r764fc66435ee4d185d359c28c0887d3e5866d7292a8d5598d9e7cbc4@%3Ccommon-issues.hadoop.apache.org%3E", + "https://lists.apache.org/thread.html/r0ca83171c4898dc92b86fa6f484a7be1dc96206765f4d01dce0f1b28@%3Ccommon-issues.hadoop.apache.org%3E", + "https://lists.apache.org/thread.html/r00097d0b5b6164ea428554007121d5dc1f88ba2af7b9e977a10572cd@%3Cdev.hbase.apache.org%3E", + "https://lists.apache.org/thread.html/rd64381fb8f92d640c1975dc50dcdf1b8512e02a2a7b20292d3565cae@%3Cissues.hbase.apache.org%3E", + "https://lists.apache.org/thread.html/r4ef574a5621b0e670a3ce641e9922543e34f22bf4c9ee9584aa67fcf@%3Cissues.hbase.apache.org%3E", + "https://lists.apache.org/thread.html/r7fed8dd9bee494094e7011cf3c2ab75bd8754ea314c6734688c42932@%3Ccommon-issues.hadoop.apache.org%3E", + }, + }, + { + VulnerabilityID: "CVE-2021-22570", + Resource: "google.golang.org/protobuf", + InstalledVersion: "v1.27.1", + FixedVersion: "", + Severity: v1alpha1.SeverityHigh, + Title: "google.golang.org/protobuf", + Description: "Nullptr dereference when a null char is present in a proto symbol. The symbol is parsed incorrectly, leading to an unchecked call into the proto file's name during generation of the resulting error message. Since the symbol is incorrectly parsed, the file is nullptr. We recommend upgrading to version 3.15.0 or greater.", + PrimaryLink: "https://nvd.nist.gov/vuln/detail/CVE-2021-22570", + Score: pointer.Float64(7.5), + Links: []string{ + "https://github.com/protocolbuffers/protobuf/releases/tag/v3.15.0", + "https://lists.fedoraproject.org/archives/list/package-announce@lists.fedoraproject.org/message/IFX6KPNOFHYD6L4XES5PCM3QNSKZBOTQ/", + "https://lists.fedoraproject.org/archives/list/package-announce@lists.fedoraproject.org/message/3DVUZPALAQ34TQP6KFNLM4IZS6B32XSA/", + "https://lists.fedoraproject.org/archives/list/package-announce@lists.fedoraproject.org/message/BTRGBRC5KGCA4SK5MUNLPYJRAGXMBIYY/", + "https://lists.fedoraproject.org/archives/list/package-announce@lists.fedoraproject.org/message/NVTWVQRB5OCCTMKEQFY5MYED3DXDVSLP/", + "https://lists.fedoraproject.org/archives/list/package-announce@lists.fedoraproject.org/message/5PAGL5M2KGYPN3VEQCRJJE6NA7D5YG5X/", + "https://lists.fedoraproject.org/archives/list/package-announce@lists.fedoraproject.org/message/KQJB6ZPRLKV6WCMX2PRRRQBFAOXFBK6B/", + "https://lists.fedoraproject.org/archives/list/package-announce@lists.fedoraproject.org/message/MRWRAXAFR3JR7XCFWTHC2KALSZKWACCE/", + }, + }, + }, + } +) + +func TestPlugin_ParseVulnerabilityReportData(t *testing.T) { + config := &corev1.ConfigMap{ + ObjectMeta: metav1.ObjectMeta{ + Name: "starboard-grype-config", + Namespace: "starboard-ns", + }, + Data: map[string]string{ + "grype.imageRef": "anchore/grype:0.34.7", + }, + } + + testCases := []struct { + name string + imageRef string + input string + expectedError error + expectedReport v1alpha1.VulnerabilityReportData + }{ + { + name: "Should convert vulnerability report in JSON format when input is quiet", + imageRef: "anchore/grype", + input: sampleReportAsString, + expectedError: nil, + expectedReport: sampleReport, + }, + { + name: "Should convert vulnerability report in JSON format when OS is not detected", + imageRef: "core.harbor.domain/library/nginx@sha256:d20aa6d1cae56fd17cd458f4807e0de462caf2336f0b70b5eeb69fcaaf30dd9c", + input: `null`, + expectedError: nil, + expectedReport: v1alpha1.VulnerabilityReportData{ + UpdateTimestamp: metav1.NewTime(fixedTime), + Scanner: v1alpha1.Scanner{ + Name: "Grype", + Vendor: "Anchore Inc.", + Version: "0.34.7", + }, + Registry: v1alpha1.Registry{ + Server: "core.harbor.domain", + }, + Artifact: v1alpha1.Artifact{ + Repository: "library/nginx", + Digest: "sha256:d20aa6d1cae56fd17cd458f4807e0de462caf2336f0b70b5eeb69fcaaf30dd9c", + }, + Summary: v1alpha1.VulnerabilitySummary{ + CriticalCount: 0, + HighCount: 0, + MediumCount: 0, + LowCount: 0, + NoneCount: 0, + UnknownCount: 0, + }, + Vulnerabilities: []v1alpha1.Vulnerability{}, + }, + }, + { + name: "Should return error when image reference cannot be parsed", + imageRef: ":", + input: "null", + expectedError: errors.New("could not parse reference: :"), + }, + } + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + fakeClient := fake.NewClientBuilder().WithObjects(config).Build() + ctx := starboard.NewPluginContext(). + WithName("Grype"). + WithNamespace("starboard-ns"). + WithServiceAccountName("starboard-sa"). + WithClient(fakeClient). + Get() + instance := grype.NewPlugin(fixedClock, ext.NewSimpleIDGenerator(), fakeClient) + report, err := instance.ParseVulnerabilityReportData(ctx, tc.imageRef, io.NopCloser(strings.NewReader(tc.input))) + switch { + case tc.expectedError == nil: + require.NoError(t, err) + assert.Equal(t, tc.expectedReport, report) + default: + assert.EqualError(t, err, tc.expectedError.Error()) + } + }) + } + +} From b7083a1fac3b3ad7885616e37232b980b857d5f2 Mon Sep 17 00:00:00 2001 From: Kevin Seidel Date: Wed, 20 Apr 2022 10:58:53 +0200 Subject: [PATCH 10/21] basic grype plugin with essential features unit tested --- pkg/plugin/grype/doc.go | 2 +- pkg/plugin/grype/model_test.go | 1 + pkg/plugin/grype/plugin.go | 41 +- pkg/plugin/grype/plugin_test.go | 3163 ++----------------------------- 4 files changed, 229 insertions(+), 2978 deletions(-) create mode 100644 pkg/plugin/grype/model_test.go diff --git a/pkg/plugin/grype/doc.go b/pkg/plugin/grype/doc.go index 980b6af17..3c6f6f09a 100644 --- a/pkg/plugin/grype/doc.go +++ b/pkg/plugin/grype/doc.go @@ -1,2 +1,2 @@ -// Package grype provides primitives for working with Trivy. +// Package grype provides primitives for working with Grype. package grype diff --git a/pkg/plugin/grype/model_test.go b/pkg/plugin/grype/model_test.go new file mode 100644 index 000000000..eb96e9fbb --- /dev/null +++ b/pkg/plugin/grype/model_test.go @@ -0,0 +1 @@ +package grype_test diff --git a/pkg/plugin/grype/plugin.go b/pkg/plugin/grype/plugin.go index 66f40c8f1..0953b697b 100644 --- a/pkg/plugin/grype/plugin.go +++ b/pkg/plugin/grype/plugin.go @@ -94,6 +94,10 @@ func (c Config) setResourceLimit(configKey string, k8sResourceList *corev1.Resou return nil } +func (c *Config) GetImageRef() (string, error) { + return c.GetRequiredData(keyGrypeImageRef) +} + type plugin struct { clock ext.Clock idGenerator ext.IDGenerator @@ -162,19 +166,17 @@ const ( grypeDBLocation = "/tmp/grypedb" ) -// In the Standalone mode there is the init container responsible for -// downloading the latest Grype DB file from GitHub and storing it to the -// emptyDir volume shared with main containers. In other words, the init -// container runs the following Grype command: +// There is an init container to cache the Grype DB, which will be stored in an +// emptyDir volume and shared across the scanning containers. Most configuration +// is done via the environment of the scanning containers // -// grype --cache-dir /tmp/grype/.cache image --download-db-only +// grype db update // // The number of main containers correspond to the number of containers // defined for the scanned workload. Each container runs the Grype image scan // command and skips the database download: // -// grype --cache-dir /tmp/grype/.cache image --skip-update \ -// --format json +// grype --skip-update --quiet --output json func (p *plugin) getPodSpec(ctx starboard.PluginContext, config Config, workload client.Object, credentials map[string]docker.Auth) (corev1.PodSpec, []*corev1.Secret, error) { var secret *corev1.Secret var secrets []*corev1.Secret @@ -295,19 +297,15 @@ func (p *plugin) getPodSpec(ctx starboard.PluginContext, config Config, workload for _, c := range spec.Containers { + //optionally add schema + scanImage := "" + if val, ok := config.Data[keyGrypeScheme]; ok { + scanImage = val + ":" + c.Image + } else { + scanImage = c.Image + } + env := append(commonEnv, - corev1.EnvVar{ - Name: "GRYPE_DB_UPDATE_URL", - ValueFrom: &corev1.EnvVarSource{ - ConfigMapKeyRef: &corev1.ConfigMapKeySelector{ - LocalObjectReference: corev1.LocalObjectReference{ - Name: grypeConfigName, - }, - Key: keyGrypeUpdateURL, - Optional: pointer.BoolPtr(false), - }, - }, - }, corev1.EnvVar{ Name: "GRYPE_REGISTRY_AUTH_AUTHORITY", ValueFrom: &corev1.EnvVarSource{ @@ -376,7 +374,7 @@ func (p *plugin) getPodSpec(ctx starboard.PluginContext, config Config, workload // } args := []string{ - c.Image, + scanImage, "--skip-update", "--quiet", "--output", @@ -429,7 +427,8 @@ func (p *plugin) appendGrypeOptionalArg(config Config, args []string, arg string if val, ok := config.Data[key]; ok && val == "true" { return append(args, arg), nil } else if !ok { - return args, fmt.Errorf("Invalid config key provided: %s", key) + //ignore if optional key is not in config.data + return args, nil } else { return args, nil } diff --git a/pkg/plugin/grype/plugin_test.go b/pkg/plugin/grype/plugin_test.go index b16477198..f5b4d2331 100644 --- a/pkg/plugin/grype/plugin_test.go +++ b/pkg/plugin/grype/plugin_test.go @@ -11,11 +11,9 @@ import ( "github.com/aquasecurity/starboard/pkg/apis/aquasecurity/v1alpha1" "github.com/aquasecurity/starboard/pkg/ext" "github.com/aquasecurity/starboard/pkg/plugin/grype" - "github.com/aquasecurity/starboard/pkg/plugin/trivy" "github.com/aquasecurity/starboard/pkg/starboard" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" - appsv1 "k8s.io/api/apps/v1" corev1 "k8s.io/api/core/v1" "k8s.io/apimachinery/pkg/api/resource" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" @@ -35,23 +33,23 @@ const defaultUpdateURL = "https://toolbox-data.anchore.io/grype/databases/listin func TestConfig_GetImageRef(t *testing.T) { testCases := []struct { name string - configData trivy.Config + configData grype.Config expectedError string expectedImageRef string }{ { name: "Should return error", - configData: trivy.Config{PluginConfig: starboard.PluginConfig{}}, - expectedError: "property trivy.imageRef not set", + configData: grype.Config{PluginConfig: starboard.PluginConfig{}}, + expectedError: "property grype.imageRef not set", }, { name: "Should return image reference from config data", - configData: trivy.Config{PluginConfig: starboard.PluginConfig{ + configData: grype.Config{PluginConfig: starboard.PluginConfig{ Data: map[string]string{ - "trivy.imageRef": "gcr.io/aquasecurity/trivy:0.8.0", + "grype.imageRef": "anchore/grype:0.34.7", }, }}, - expectedImageRef: "gcr.io/aquasecurity/trivy:0.8.0", + expectedImageRef: "anchore/grype:0.34.7", }, } @@ -71,13 +69,13 @@ func TestConfig_GetImageRef(t *testing.T) { func TestConfig_GetResourceRequirements(t *testing.T) { testCases := []struct { name string - config trivy.Config + config grype.Config expectedError string expectedRequirements corev1.ResourceRequirements }{ { name: "Should return empty requirements by default", - config: trivy.Config{ + config: grype.Config{ PluginConfig: starboard.PluginConfig{}, }, expectedError: "", @@ -88,14 +86,14 @@ func TestConfig_GetResourceRequirements(t *testing.T) { }, { name: "Should return configured resource requirement", - config: trivy.Config{ + config: grype.Config{ PluginConfig: starboard.PluginConfig{ Data: map[string]string{ - "trivy.dbRepository": defaultUpdateURL, - "trivy.resources.requests.cpu": "800m", - "trivy.resources.requests.memory": "200M", - "trivy.resources.limits.cpu": "600m", - "trivy.resources.limits.memory": "700M", + "grype.dbRepository": defaultUpdateURL, + "grype.resources.requests.cpu": "800m", + "grype.resources.requests.memory": "200M", + "grype.resources.limits.cpu": "600m", + "grype.resources.limits.memory": "700M", }, }, }, @@ -113,14 +111,14 @@ func TestConfig_GetResourceRequirements(t *testing.T) { }, { name: "Should return error if resource is not parseable", - config: trivy.Config{ + config: grype.Config{ PluginConfig: starboard.PluginConfig{ Data: map[string]string{ - "trivy.resources.requests.cpu": "roughly 100", + "grype.resources.requests.cpu": "roughly 100", }, }, }, - expectedError: "parsing resource definition trivy.resources.requests.cpu: roughly 100 quantities must match the regular expression '^([+-]?[0-9.]+)([eEinumkKMGTP]*[-+]?[0-9]*)$'", + expectedError: "parsing resource definition grype.resources.requests.cpu: roughly 100 quantities must match the regular expression '^([+-]?[0-9.]+)([eEinumkKMGTP]*[-+]?[0-9]*)$'", }, } for _, tc := range testCases { @@ -136,59 +134,15 @@ func TestConfig_GetResourceRequirements(t *testing.T) { } } -func TestConfig_IgnoreUnfixed(t *testing.T) { - testCases := []struct { - name string - configData trivy.Config - expectedOutput bool - }{ - { - name: "Should return false", - configData: trivy.Config{PluginConfig: starboard.PluginConfig{ - Data: map[string]string{ - "foo": "bar", - }, - }}, - expectedOutput: false, - }, - { - name: "Should return true", - configData: trivy.Config{PluginConfig: starboard.PluginConfig{ - Data: map[string]string{ - "foo": "bar", - "trivy.ignoreUnfixed": "true", - }, - }}, - expectedOutput: true, - }, - { - name: "Should return false when set it as false", - configData: trivy.Config{PluginConfig: starboard.PluginConfig{ - Data: map[string]string{ - "foo": "bar", - "trivy.ignoreUnfixed": "false", - }, - }}, - expectedOutput: true, - }, - } - for _, tc := range testCases { - t.Run(tc.name, func(t *testing.T) { - exists := tc.configData.IgnoreUnfixed() - assert.Equal(t, tc.expectedOutput, exists) - }) - } -} - func TestPlugin_Init(t *testing.T) { t.Run("Should create the default config", func(t *testing.T) { client := fake.NewClientBuilder().WithObjects().Build() - instance := trivy.NewPlugin(fixedClock, ext.NewSimpleIDGenerator(), client) + instance := grype.NewPlugin(fixedClock, ext.NewSimpleIDGenerator(), client) pluginContext := starboard.NewPluginContext(). - WithName(trivy.Plugin). + WithName(grype.Plugin). WithNamespace("starboard-ns"). WithServiceAccountName("starboard-sa"). WithClient(client). @@ -199,7 +153,7 @@ func TestPlugin_Init(t *testing.T) { var cm corev1.ConfigMap err = client.Get(context.Background(), types.NamespacedName{ Namespace: "starboard-ns", - Name: "starboard-trivy-config", + Name: "starboard-grype-config", }, &cm) require.NoError(t, err) assert.Equal(t, corev1.ConfigMap{ @@ -208,7 +162,7 @@ func TestPlugin_Init(t *testing.T) { Kind: "ConfigMap", }, ObjectMeta: metav1.ObjectMeta{ - Name: "starboard-trivy-config", + Name: "starboard-grype-config", Namespace: "starboard-ns", Labels: map[string]string{ "app.kubernetes.io/managed-by": "starboard", @@ -216,70 +170,13 @@ func TestPlugin_Init(t *testing.T) { ResourceVersion: "1", }, Data: map[string]string{ - "trivy.imageRef": "docker.io/aquasec/trivy:0.25.2", - "trivy.severity": "UNKNOWN,LOW,MEDIUM,HIGH,CRITICAL", - "trivy.mode": "Standalone", - "trivy.timeout": "5m0s", - "trivy.dbRepository": defaultUpdateURL, - - "trivy.resources.requests.cpu": "100m", - "trivy.resources.requests.memory": "100M", - "trivy.resources.limits.cpu": "500m", - "trivy.resources.limits.memory": "500M", - }, - }, cm) - }) - - t.Run("Should not overwrite existing config", func(t *testing.T) { - client := fake.NewClientBuilder().WithObjects( - &corev1.ConfigMap{ - TypeMeta: metav1.TypeMeta{ - APIVersion: "v1", - Kind: "ConfigMap", - }, - ObjectMeta: metav1.ObjectMeta{ - Name: "starboard-trivy-config", - Namespace: "starboard-ns", - ResourceVersion: "1", - }, - Data: map[string]string{ - "trivy.imageRef": "docker.io/aquasec/trivy:0.25.2", - "trivy.severity": "UNKNOWN,LOW,MEDIUM,HIGH,CRITICAL", - "trivy.mode": "Standalone", - }, - }).Build() - - instance := trivy.NewPlugin(fixedClock, ext.NewSimpleIDGenerator(), client) - - pluginContext := starboard.NewPluginContext(). - WithName(trivy.Plugin). - WithNamespace("starboard-ns"). - WithServiceAccountName("starboard-sa"). - WithClient(client). - Get() - err := instance.Init(pluginContext) - require.NoError(t, err) + "grype.imageRef": "anchore/grype:0.34.7", + "grype.updateURL": defaultUpdateURL, - var cm corev1.ConfigMap - err = client.Get(context.Background(), types.NamespacedName{ - Namespace: "starboard-ns", - Name: "starboard-trivy-config", - }, &cm) - require.NoError(t, err) - assert.Equal(t, corev1.ConfigMap{ - TypeMeta: metav1.TypeMeta{ - APIVersion: "v1", - Kind: "ConfigMap", - }, - ObjectMeta: metav1.ObjectMeta{ - Name: "starboard-trivy-config", - Namespace: "starboard-ns", - ResourceVersion: "1", - }, - Data: map[string]string{ - "trivy.imageRef": "docker.io/aquasec/trivy:0.25.2", - "trivy.severity": "UNKNOWN,LOW,MEDIUM,HIGH,CRITICAL", - "trivy.mode": "Standalone", + "grype.resources.requests.cpu": "100m", + "grype.resources.requests.memory": "100M", + "grype.resources.limits.cpu": "500m", + "grype.resources.limits.memory": "500M", }, }, cm) }) @@ -305,7 +202,22 @@ const ( keyResourcesLimitsMemory = "grype.resources.limits.memory" ) -func TestPlugin_GetScanJobSpec(t *testing.T) { +type JobSpecTestCase struct { + Name string + + Config map[string]string + ExpectedConfigName string + WorkloadSpec client.Object + + // ExpectedSecrets []corev1.Secret + ExpectedJobSpec corev1.PodSpec +} + +//returns default test case, since most of them share a lot +func NewJobSpecTestCase(Name string) JobSpecTestCase { + j := new(JobSpecTestCase) + j.Name = Name + j.ExpectedConfigName = "starboard-grype-config" commonEnv := []corev1.EnvVar{ { @@ -313,7 +225,7 @@ func TestPlugin_GetScanJobSpec(t *testing.T) { ValueFrom: &corev1.EnvVarSource{ ConfigMapKeyRef: &corev1.ConfigMapKeySelector{ LocalObjectReference: corev1.LocalObjectReference{ - Name: grype.Plugin, + Name: j.ExpectedConfigName, }, Key: keyGrypeHTTPProxy, Optional: pointer.BoolPtr(true), @@ -325,7 +237,7 @@ func TestPlugin_GetScanJobSpec(t *testing.T) { ValueFrom: &corev1.EnvVarSource{ ConfigMapKeyRef: &corev1.ConfigMapKeySelector{ LocalObjectReference: corev1.LocalObjectReference{ - Name: grype.Plugin, + Name: j.ExpectedConfigName, }, Key: keyGrypeHTTPSProxy, Optional: pointer.BoolPtr(true), @@ -337,7 +249,7 @@ func TestPlugin_GetScanJobSpec(t *testing.T) { ValueFrom: &corev1.EnvVarSource{ ConfigMapKeyRef: &corev1.ConfigMapKeySelector{ LocalObjectReference: corev1.LocalObjectReference{ - Name: grype.Plugin, + Name: j.ExpectedConfigName, }, Key: keyGrypeNoProxy, Optional: pointer.BoolPtr(true), @@ -349,7 +261,7 @@ func TestPlugin_GetScanJobSpec(t *testing.T) { ValueFrom: &corev1.EnvVarSource{ ConfigMapKeyRef: &corev1.ConfigMapKeySelector{ LocalObjectReference: corev1.LocalObjectReference{ - Name: grype.Plugin, + Name: j.ExpectedConfigName, }, Key: keyGrypeUpdateURL, Optional: pointer.BoolPtr(false), @@ -362,6 +274,34 @@ func TestPlugin_GetScanJobSpec(t *testing.T) { }, } + j.Config = map[string]string{ + "grype.imageRef": "anchore/grype:v0.34.7", + "grype.updateURL": defaultUpdateURL, + "grype.resources.requests.cpu": "100m", + "grype.resources.requests.memory": "100M", + "grype.resources.limits.cpu": "500m", + "grype.resources.limits.memory": "500M", + } + + j.WorkloadSpec = &corev1.Pod{ + TypeMeta: metav1.TypeMeta{ + Kind: "Pod", + APIVersion: "v1", + }, + ObjectMeta: metav1.ObjectMeta{ + Name: "nginx", + Namespace: "prod-ns", + }, + Spec: corev1.PodSpec{ + Containers: []corev1.Container{ + { + Name: "nginx", + Image: "nginx:1.16", + }, + }, + }, + } + tmpVolume := corev1.Volume{ Name: "tmp", VolumeSource: corev1.VolumeSource{ @@ -377,2535 +317,162 @@ func TestPlugin_GetScanJobSpec(t *testing.T) { ReadOnly: false, } - testCases := []struct { - name string - - config map[string]string - workloadSpec client.Object - - expectedSecrets []corev1.Secret - expectedJobSpec corev1.PodSpec - }{ - { - name: "grype", - config: map[string]string{ - "grype.imageRef": "anchore/grype:v0.34.7", - "grype.updateURL": defaultUpdateURL, - "grype.resources.requests.cpu": "100m", - "grype.resources.requests.memory": "100M", - "grype.resources.limits.cpu": "500m", - "grype.resources.limits.memory": "500M", - }, - workloadSpec: &appsv1.ReplicaSet{ - TypeMeta: metav1.TypeMeta{ - Kind: "ReplicaSet", - APIVersion: "apps/v1", + //expected base podSpec, will be adjusted for the individual TCs + j.ExpectedJobSpec = corev1.PodSpec{ + Affinity: starboard.LinuxNodeAffinity(), + RestartPolicy: corev1.RestartPolicyNever, + ServiceAccountName: "starboard-sa", + AutomountServiceAccountToken: pointer.BoolPtr(false), + Volumes: []corev1.Volume{ + tmpVolume, + }, + InitContainers: []corev1.Container{ + { + Name: "00000000-0000-0000-0000-000000000001", + Image: "anchore/grype:v0.34.7", + ImagePullPolicy: corev1.PullIfNotPresent, + TerminationMessagePolicy: corev1.TerminationMessageFallbackToLogsOnError, + Env: commonEnv, + Command: []string{ + "grype", }, - ObjectMeta: metav1.ObjectMeta{ - Name: "nginx-6799fc88d8", - Namespace: "prod-ns", + Args: []string{ + "db", + "update", }, - Spec: appsv1.ReplicaSetSpec{ - Template: corev1.PodTemplateSpec{ - Spec: corev1.PodSpec{ - Containers: []corev1.Container{ - { - Name: "nginx", - Image: "nginx:1.16", - }, - }, - }, + Resources: corev1.ResourceRequirements{ + Requests: corev1.ResourceList{ + corev1.ResourceCPU: resource.MustParse("100m"), + corev1.ResourceMemory: resource.MustParse("100M"), }, - }, - }, - expectedJobSpec: corev1.PodSpec{ - Affinity: starboard.LinuxNodeAffinity(), - RestartPolicy: corev1.RestartPolicyNever, - ServiceAccountName: "starboard-sa", - AutomountServiceAccountToken: pointer.BoolPtr(false), - Volumes: []corev1.Volume{ - tmpVolume, - }, - InitContainers: []corev1.Container{ - { - Name: "00000000-0000-0000-0000-000000000001", - Image: "anchore/grype:v0.34.7", - ImagePullPolicy: corev1.PullIfNotPresent, - TerminationMessagePolicy: corev1.TerminationMessageFallbackToLogsOnError, - Env: commonEnv, - Command: []string{ - "grype", - }, - Args: []string{ - "db", - "update", - }, - Resources: corev1.ResourceRequirements{ - Requests: corev1.ResourceList{ - corev1.ResourceCPU: resource.MustParse("100m"), - corev1.ResourceMemory: resource.MustParse("100M"), - }, - Limits: corev1.ResourceList{ - corev1.ResourceCPU: resource.MustParse("500m"), - corev1.ResourceMemory: resource.MustParse("500M"), - }, - }, - VolumeMounts: []corev1.VolumeMount{ - tmpVolumeMount, - }, + Limits: corev1.ResourceList{ + corev1.ResourceCPU: resource.MustParse("500m"), + corev1.ResourceMemory: resource.MustParse("500M"), }, }, - Containers: []corev1.Container{ - { - Name: "nginx", - Image: "docker.io/aquasec/trivy:0.14.0", - ImagePullPolicy: corev1.PullIfNotPresent, - TerminationMessagePolicy: corev1.TerminationMessageFallbackToLogsOnError, - Env: []corev1.EnvVar{ - { - Name: "TRIVY_SEVERITY", - ValueFrom: &corev1.EnvVarSource{ - ConfigMapKeyRef: &corev1.ConfigMapKeySelector{ - LocalObjectReference: corev1.LocalObjectReference{ - Name: "starboard-trivy-config", - }, - Key: "trivy.severity", - Optional: pointer.BoolPtr(true), - }, - }, - }, - { - Name: "TRIVY_IGNORE_UNFIXED", - ValueFrom: &corev1.EnvVarSource{ - ConfigMapKeyRef: &corev1.ConfigMapKeySelector{ - LocalObjectReference: corev1.LocalObjectReference{ - Name: "starboard-trivy-config", - }, - Key: "trivy.ignoreUnfixed", - Optional: pointer.BoolPtr(true), - }, - }, - }, - - { - Name: "TRIVY_SKIP_FILES", - ValueFrom: &corev1.EnvVarSource{ - ConfigMapKeyRef: &corev1.ConfigMapKeySelector{ - LocalObjectReference: corev1.LocalObjectReference{ - Name: "starboard-trivy-config", - }, - Key: "trivy.skipFiles", - Optional: pointer.BoolPtr(true), - }, - }, - }, - { - Name: "TRIVY_SKIP_DIRS", - ValueFrom: &corev1.EnvVarSource{ - ConfigMapKeyRef: &corev1.ConfigMapKeySelector{ - LocalObjectReference: corev1.LocalObjectReference{ - Name: "starboard-trivy-config", - }, - Key: "trivy.skipDirs", - Optional: pointer.BoolPtr(true), - }, - }, - }, - { - Name: "HTTP_PROXY", - ValueFrom: &corev1.EnvVarSource{ - ConfigMapKeyRef: &corev1.ConfigMapKeySelector{ - LocalObjectReference: corev1.LocalObjectReference{ - Name: "starboard-trivy-config", - }, - Key: "trivy.httpProxy", - Optional: pointer.BoolPtr(true), - }, - }, - }, - { - Name: "HTTPS_PROXY", - ValueFrom: &corev1.EnvVarSource{ - ConfigMapKeyRef: &corev1.ConfigMapKeySelector{ - LocalObjectReference: corev1.LocalObjectReference{ - Name: "starboard-trivy-config", - }, - Key: "trivy.httpsProxy", - Optional: pointer.BoolPtr(true), - }, - }, - }, - { - Name: "NO_PROXY", - ValueFrom: &corev1.EnvVarSource{ - ConfigMapKeyRef: &corev1.ConfigMapKeySelector{ - LocalObjectReference: corev1.LocalObjectReference{ - Name: "starboard-trivy-config", - }, - Key: "trivy.noProxy", - Optional: pointer.BoolPtr(true), - }, - }, - }, - }, - Command: []string{ - "trivy", - }, - Args: []string{ - "--cache-dir", "/tmp/trivy/.cache", - "--quiet", - "image", - "--skip-update", - "--format", "json", - "nginx:1.16", - }, - Resources: corev1.ResourceRequirements{ - Requests: corev1.ResourceList{ - corev1.ResourceCPU: resource.MustParse("100m"), - corev1.ResourceMemory: resource.MustParse("100M"), - }, - Limits: corev1.ResourceList{ - corev1.ResourceCPU: resource.MustParse("500m"), - corev1.ResourceMemory: resource.MustParse("500M"), - }, - }, - VolumeMounts: []corev1.VolumeMount{ - tmpVolumeMount, - }, - SecurityContext: &corev1.SecurityContext{ - Privileged: pointer.BoolPtr(false), - AllowPrivilegeEscalation: pointer.BoolPtr(false), - Capabilities: &corev1.Capabilities{ - Drop: []corev1.Capability{"all"}, - }, - ReadOnlyRootFilesystem: pointer.BoolPtr(true), - }, - }, + VolumeMounts: []corev1.VolumeMount{ + tmpVolumeMount, }, - SecurityContext: &corev1.PodSecurityContext{}, }, }, - { - name: "Standalone mode with insecure registry", - config: map[string]string{ - "trivy.imageRef": "docker.io/aquasec/trivy:0.14.0", - "trivy.mode": string(trivy.Standalone), - "trivy.insecureRegistry.pocRegistry": "poc.myregistry.harbor.com.pl", - "trivy.dbRepository": defaultUpdateURL, - - "trivy.resources.requests.cpu": "100m", - "trivy.resources.requests.memory": "100M", - "trivy.resources.limits.cpu": "500m", - "trivy.resources.limits.memory": "500M", - }, - workloadSpec: &corev1.Pod{ - TypeMeta: metav1.TypeMeta{ - Kind: "Pod", - APIVersion: "v1", - }, - ObjectMeta: metav1.ObjectMeta{ - Name: "nginx", - Namespace: "prod-ns", - }, - Spec: corev1.PodSpec{ - Containers: []corev1.Container{ - { - Name: "nginx", - Image: "poc.myregistry.harbor.com.pl/nginx:1.16", - }, - }, - }}, - expectedJobSpec: corev1.PodSpec{ - Affinity: starboard.LinuxNodeAffinity(), - RestartPolicy: corev1.RestartPolicyNever, - ServiceAccountName: "starboard-sa", - AutomountServiceAccountToken: pointer.BoolPtr(false), - Volumes: []corev1.Volume{ - tmpVolume, - }, - InitContainers: []corev1.Container{ - { - Name: "00000000-0000-0000-0000-000000000001", - Image: "docker.io/aquasec/trivy:0.14.0", - ImagePullPolicy: corev1.PullIfNotPresent, - TerminationMessagePolicy: corev1.TerminationMessageFallbackToLogsOnError, - Env: []corev1.EnvVar{ - { - Name: "HTTP_PROXY", - ValueFrom: &corev1.EnvVarSource{ - ConfigMapKeyRef: &corev1.ConfigMapKeySelector{ - LocalObjectReference: corev1.LocalObjectReference{ - Name: "starboard-trivy-config", - }, - Key: "trivy.httpProxy", - Optional: pointer.BoolPtr(true), - }, - }, - }, - { - Name: "HTTPS_PROXY", - ValueFrom: &corev1.EnvVarSource{ - ConfigMapKeyRef: &corev1.ConfigMapKeySelector{ - LocalObjectReference: corev1.LocalObjectReference{ - Name: "starboard-trivy-config", - }, - Key: "trivy.httpsProxy", - Optional: pointer.BoolPtr(true), - }, - }, - }, - { - Name: "NO_PROXY", - ValueFrom: &corev1.EnvVarSource{ - ConfigMapKeyRef: &corev1.ConfigMapKeySelector{ - LocalObjectReference: corev1.LocalObjectReference{ - Name: "starboard-trivy-config", - }, - Key: "trivy.noProxy", - Optional: pointer.BoolPtr(true), - }, - }, - }, - { - Name: "GITHUB_TOKEN", - ValueFrom: &corev1.EnvVarSource{ - SecretKeyRef: &corev1.SecretKeySelector{ - LocalObjectReference: corev1.LocalObjectReference{ - Name: "starboard-trivy-config", - }, - Key: "trivy.githubToken", - Optional: pointer.BoolPtr(true), - }, + Containers: []corev1.Container{ + { + Name: "nginx", + Image: "anchore/grype:v0.34.7", + ImagePullPolicy: corev1.PullIfNotPresent, + TerminationMessagePolicy: corev1.TerminationMessageFallbackToLogsOnError, + Env: append(commonEnv, + corev1.EnvVar{ + Name: "GRYPE_REGISTRY_AUTH_AUTHORITY", + ValueFrom: &corev1.EnvVarSource{ + ConfigMapKeyRef: &corev1.ConfigMapKeySelector{ + LocalObjectReference: corev1.LocalObjectReference{ + Name: j.ExpectedConfigName, }, + Key: keyGrypeRegAuthority, + Optional: pointer.BoolPtr(true), }, }, - Command: []string{ - "trivy", - }, - Args: []string{ - "--cache-dir", "/tmp/trivy/.cache", - "image", - "--download-db-only", - "--db-repository", defaultUpdateURL, - }, - Resources: corev1.ResourceRequirements{ - Requests: corev1.ResourceList{ - corev1.ResourceCPU: resource.MustParse("100m"), - corev1.ResourceMemory: resource.MustParse("100M"), - }, - Limits: corev1.ResourceList{ - corev1.ResourceCPU: resource.MustParse("500m"), - corev1.ResourceMemory: resource.MustParse("500M"), - }, - }, - VolumeMounts: []corev1.VolumeMount{ - tmpVolumeMount, - }, }, - }, - Containers: []corev1.Container{ - { - Name: "nginx", - Image: "docker.io/aquasec/trivy:0.14.0", - ImagePullPolicy: corev1.PullIfNotPresent, - TerminationMessagePolicy: corev1.TerminationMessageFallbackToLogsOnError, - Env: []corev1.EnvVar{ - { - Name: "TRIVY_SEVERITY", - ValueFrom: &corev1.EnvVarSource{ - ConfigMapKeyRef: &corev1.ConfigMapKeySelector{ - LocalObjectReference: corev1.LocalObjectReference{ - Name: "starboard-trivy-config", - }, - Key: "trivy.severity", - Optional: pointer.BoolPtr(true), - }, - }, - }, - { - Name: "TRIVY_IGNORE_UNFIXED", - ValueFrom: &corev1.EnvVarSource{ - ConfigMapKeyRef: &corev1.ConfigMapKeySelector{ - LocalObjectReference: corev1.LocalObjectReference{ - Name: "starboard-trivy-config", - }, - Key: "trivy.ignoreUnfixed", - Optional: pointer.BoolPtr(true), - }, - }, - }, - - { - Name: "TRIVY_SKIP_FILES", - ValueFrom: &corev1.EnvVarSource{ - ConfigMapKeyRef: &corev1.ConfigMapKeySelector{ - LocalObjectReference: corev1.LocalObjectReference{ - Name: "starboard-trivy-config", - }, - Key: "trivy.skipFiles", - Optional: pointer.BoolPtr(true), - }, - }, - }, - { - Name: "TRIVY_SKIP_DIRS", - ValueFrom: &corev1.EnvVarSource{ - ConfigMapKeyRef: &corev1.ConfigMapKeySelector{ - LocalObjectReference: corev1.LocalObjectReference{ - Name: "starboard-trivy-config", - }, - Key: "trivy.skipDirs", - Optional: pointer.BoolPtr(true), - }, - }, - }, - { - Name: "HTTP_PROXY", - ValueFrom: &corev1.EnvVarSource{ - ConfigMapKeyRef: &corev1.ConfigMapKeySelector{ - LocalObjectReference: corev1.LocalObjectReference{ - Name: "starboard-trivy-config", - }, - Key: "trivy.httpProxy", - Optional: pointer.BoolPtr(true), - }, - }, - }, - { - Name: "HTTPS_PROXY", - ValueFrom: &corev1.EnvVarSource{ - ConfigMapKeyRef: &corev1.ConfigMapKeySelector{ - LocalObjectReference: corev1.LocalObjectReference{ - Name: "starboard-trivy-config", - }, - Key: "trivy.httpsProxy", - Optional: pointer.BoolPtr(true), - }, - }, - }, - { - Name: "NO_PROXY", - ValueFrom: &corev1.EnvVarSource{ - ConfigMapKeyRef: &corev1.ConfigMapKeySelector{ - LocalObjectReference: corev1.LocalObjectReference{ - Name: "starboard-trivy-config", - }, - Key: "trivy.noProxy", - Optional: pointer.BoolPtr(true), - }, + corev1.EnvVar{ + Name: "GRYPE_EXCLUDE", + ValueFrom: &corev1.EnvVarSource{ + ConfigMapKeyRef: &corev1.ConfigMapKeySelector{ + LocalObjectReference: corev1.LocalObjectReference{ + Name: j.ExpectedConfigName, }, + Key: keyGrypeExcludePaths, + Optional: pointer.BoolPtr(true), }, - { - Name: "TRIVY_INSECURE", - Value: "true", - }, - }, - Command: []string{ - "trivy", - }, - Args: []string{ - "--cache-dir", "/tmp/trivy/.cache", - "--quiet", - "image", - "--skip-update", - "--format", "json", - "poc.myregistry.harbor.com.pl/nginx:1.16", - }, - Resources: corev1.ResourceRequirements{ - Requests: corev1.ResourceList{ - corev1.ResourceCPU: resource.MustParse("100m"), - corev1.ResourceMemory: resource.MustParse("100M"), - }, - Limits: corev1.ResourceList{ - corev1.ResourceCPU: resource.MustParse("500m"), - corev1.ResourceMemory: resource.MustParse("500M"), - }, - }, - VolumeMounts: []corev1.VolumeMount{ - tmpVolumeMount, - }, - SecurityContext: &corev1.SecurityContext{ - Privileged: pointer.BoolPtr(false), - AllowPrivilegeEscalation: pointer.BoolPtr(false), - Capabilities: &corev1.Capabilities{ - Drop: []corev1.Capability{"all"}, - }, - ReadOnlyRootFilesystem: pointer.BoolPtr(true), }, }, - }, - SecurityContext: &corev1.PodSecurityContext{}, - }, - }, - { - name: "Standalone mode with non-SSL registry", - config: map[string]string{ - "trivy.imageRef": "docker.io/aquasec/trivy:0.14.0", - "trivy.mode": string(trivy.Standalone), - "trivy.nonSslRegistry.pocRegistry": "poc.myregistry.harbor.com.pl", - "trivy.dbRepository": defaultUpdateURL, - "trivy.resources.requests.cpu": "100m", - "trivy.resources.requests.memory": "100M", - "trivy.resources.limits.cpu": "500m", - "trivy.resources.limits.memory": "500M", - }, - workloadSpec: &corev1.Pod{ - TypeMeta: metav1.TypeMeta{ - Kind: "Pod", - APIVersion: "v1", - }, - ObjectMeta: metav1.ObjectMeta{ - Name: "nginx", - Namespace: "prod-ns", - }, - Spec: corev1.PodSpec{ - Containers: []corev1.Container{ - { - Name: "nginx", - Image: "poc.myregistry.harbor.com.pl/nginx:1.16", - }, + corev1.EnvVar{ + Name: "GRYPE_DB_AUTO_UPDATE", + Value: "false", }, + ), + Command: []string{ + "grype", }, - }, - expectedJobSpec: corev1.PodSpec{ - Affinity: starboard.LinuxNodeAffinity(), - RestartPolicy: corev1.RestartPolicyNever, - ServiceAccountName: "starboard-sa", - AutomountServiceAccountToken: pointer.BoolPtr(false), - Volumes: []corev1.Volume{ - tmpVolume, + Args: []string{ + "nginx:1.16", + "--skip-update", + "--quiet", + "--output", + "json", }, - InitContainers: []corev1.Container{ - { - Name: "00000000-0000-0000-0000-000000000001", - Image: "docker.io/aquasec/trivy:0.14.0", - ImagePullPolicy: corev1.PullIfNotPresent, - TerminationMessagePolicy: corev1.TerminationMessageFallbackToLogsOnError, - Env: []corev1.EnvVar{ - { - Name: "HTTP_PROXY", - ValueFrom: &corev1.EnvVarSource{ - ConfigMapKeyRef: &corev1.ConfigMapKeySelector{ - LocalObjectReference: corev1.LocalObjectReference{ - Name: "starboard-trivy-config", - }, - Key: "trivy.httpProxy", - Optional: pointer.BoolPtr(true), - }, - }, - }, - { - Name: "HTTPS_PROXY", - ValueFrom: &corev1.EnvVarSource{ - ConfigMapKeyRef: &corev1.ConfigMapKeySelector{ - LocalObjectReference: corev1.LocalObjectReference{ - Name: "starboard-trivy-config", - }, - Key: "trivy.httpsProxy", - Optional: pointer.BoolPtr(true), - }, - }, - }, - { - Name: "NO_PROXY", - ValueFrom: &corev1.EnvVarSource{ - ConfigMapKeyRef: &corev1.ConfigMapKeySelector{ - LocalObjectReference: corev1.LocalObjectReference{ - Name: "starboard-trivy-config", - }, - Key: "trivy.noProxy", - Optional: pointer.BoolPtr(true), - }, - }, - }, - { - Name: "GITHUB_TOKEN", - ValueFrom: &corev1.EnvVarSource{ - SecretKeyRef: &corev1.SecretKeySelector{ - LocalObjectReference: corev1.LocalObjectReference{ - Name: "starboard-trivy-config", - }, - Key: "trivy.githubToken", - Optional: pointer.BoolPtr(true), - }, - }, - }, - }, - Command: []string{ - "trivy", - }, - Args: []string{ - "--cache-dir", "/tmp/trivy/.cache", - "image", - "--download-db-only", - "--db-repository", defaultUpdateURL, - }, - Resources: corev1.ResourceRequirements{ - Requests: corev1.ResourceList{ - corev1.ResourceCPU: resource.MustParse("100m"), - corev1.ResourceMemory: resource.MustParse("100M"), - }, - Limits: corev1.ResourceList{ - corev1.ResourceCPU: resource.MustParse("500m"), - corev1.ResourceMemory: resource.MustParse("500M"), - }, - }, - VolumeMounts: []corev1.VolumeMount{ - tmpVolumeMount, - }, + Resources: corev1.ResourceRequirements{ + Requests: corev1.ResourceList{ + corev1.ResourceCPU: resource.MustParse("100m"), + corev1.ResourceMemory: resource.MustParse("100M"), }, - }, - Containers: []corev1.Container{ - { - Name: "nginx", - Image: "docker.io/aquasec/trivy:0.14.0", - ImagePullPolicy: corev1.PullIfNotPresent, - TerminationMessagePolicy: corev1.TerminationMessageFallbackToLogsOnError, - Env: []corev1.EnvVar{ - { - Name: "TRIVY_SEVERITY", - ValueFrom: &corev1.EnvVarSource{ - ConfigMapKeyRef: &corev1.ConfigMapKeySelector{ - LocalObjectReference: corev1.LocalObjectReference{ - Name: "starboard-trivy-config", - }, - Key: "trivy.severity", - Optional: pointer.BoolPtr(true), - }, - }, - }, - { - Name: "TRIVY_IGNORE_UNFIXED", - ValueFrom: &corev1.EnvVarSource{ - ConfigMapKeyRef: &corev1.ConfigMapKeySelector{ - LocalObjectReference: corev1.LocalObjectReference{ - Name: "starboard-trivy-config", - }, - Key: "trivy.ignoreUnfixed", - Optional: pointer.BoolPtr(true), - }, - }, - }, - - { - Name: "TRIVY_SKIP_FILES", - ValueFrom: &corev1.EnvVarSource{ - ConfigMapKeyRef: &corev1.ConfigMapKeySelector{ - LocalObjectReference: corev1.LocalObjectReference{ - Name: "starboard-trivy-config", - }, - Key: "trivy.skipFiles", - Optional: pointer.BoolPtr(true), - }, - }, - }, - { - Name: "TRIVY_SKIP_DIRS", - ValueFrom: &corev1.EnvVarSource{ - ConfigMapKeyRef: &corev1.ConfigMapKeySelector{ - LocalObjectReference: corev1.LocalObjectReference{ - Name: "starboard-trivy-config", - }, - Key: "trivy.skipDirs", - Optional: pointer.BoolPtr(true), - }, - }, - }, - { - Name: "HTTP_PROXY", - ValueFrom: &corev1.EnvVarSource{ - ConfigMapKeyRef: &corev1.ConfigMapKeySelector{ - LocalObjectReference: corev1.LocalObjectReference{ - Name: "starboard-trivy-config", - }, - Key: "trivy.httpProxy", - Optional: pointer.BoolPtr(true), - }, - }, - }, - { - Name: "HTTPS_PROXY", - ValueFrom: &corev1.EnvVarSource{ - ConfigMapKeyRef: &corev1.ConfigMapKeySelector{ - LocalObjectReference: corev1.LocalObjectReference{ - Name: "starboard-trivy-config", - }, - Key: "trivy.httpsProxy", - Optional: pointer.BoolPtr(true), - }, - }, - }, - { - Name: "NO_PROXY", - ValueFrom: &corev1.EnvVarSource{ - ConfigMapKeyRef: &corev1.ConfigMapKeySelector{ - LocalObjectReference: corev1.LocalObjectReference{ - Name: "starboard-trivy-config", - }, - Key: "trivy.noProxy", - Optional: pointer.BoolPtr(true), - }, - }, - }, - { - Name: "TRIVY_NON_SSL", - Value: "true", - }, - }, - Command: []string{ - "trivy", - }, - Args: []string{ - "--cache-dir", "/tmp/trivy/.cache", - "--quiet", - "image", - "--skip-update", - "--format", "json", - "poc.myregistry.harbor.com.pl/nginx:1.16", - }, - Resources: corev1.ResourceRequirements{ - Requests: corev1.ResourceList{ - corev1.ResourceCPU: resource.MustParse("100m"), - corev1.ResourceMemory: resource.MustParse("100M"), - }, - Limits: corev1.ResourceList{ - corev1.ResourceCPU: resource.MustParse("500m"), - corev1.ResourceMemory: resource.MustParse("500M"), - }, - }, - VolumeMounts: []corev1.VolumeMount{ - tmpVolumeMount, - }, - SecurityContext: &corev1.SecurityContext{ - Privileged: pointer.BoolPtr(false), - AllowPrivilegeEscalation: pointer.BoolPtr(false), - Capabilities: &corev1.Capabilities{ - Drop: []corev1.Capability{"all"}, - }, - ReadOnlyRootFilesystem: pointer.BoolPtr(true), - }, + Limits: corev1.ResourceList{ + corev1.ResourceCPU: resource.MustParse("500m"), + corev1.ResourceMemory: resource.MustParse("500M"), }, }, - SecurityContext: &corev1.PodSecurityContext{}, - }, - }, - { - name: "Standalone mode with trivyignore file", - config: map[string]string{ - "trivy.imageRef": "docker.io/aquasec/trivy:0.14.0", - "trivy.mode": string(trivy.Standalone), - "trivy.ignoreFile": `# Accept the risk -CVE-2018-14618 - -# No impact in our settings -CVE-2019-1543`, - "trivy.dbRepository": defaultUpdateURL, - "trivy.resources.requests.cpu": "100m", - "trivy.resources.requests.memory": "100M", - "trivy.resources.limits.cpu": "500m", - "trivy.resources.limits.memory": "500M", - }, - workloadSpec: &corev1.Pod{ - TypeMeta: metav1.TypeMeta{ - Kind: "Pod", - APIVersion: "v1", - }, - ObjectMeta: metav1.ObjectMeta{ - Name: "nginx", - Namespace: "prod-ns", + VolumeMounts: []corev1.VolumeMount{ + tmpVolumeMount, }, - Spec: corev1.PodSpec{ - Containers: []corev1.Container{ - { - Name: "nginx", - Image: "nginx:1.16", - }, + SecurityContext: &corev1.SecurityContext{ + Privileged: pointer.BoolPtr(false), + AllowPrivilegeEscalation: pointer.BoolPtr(false), + Capabilities: &corev1.Capabilities{ + Drop: []corev1.Capability{"all"}, }, + ReadOnlyRootFilesystem: pointer.BoolPtr(true), }, }, - expectedJobSpec: corev1.PodSpec{ - Affinity: starboard.LinuxNodeAffinity(), - RestartPolicy: corev1.RestartPolicyNever, - ServiceAccountName: "starboard-sa", - AutomountServiceAccountToken: pointer.BoolPtr(false), - Volumes: []corev1.Volume{ - tmpVolume, - { - Name: "ignorefile", - VolumeSource: corev1.VolumeSource{ - ConfigMap: &corev1.ConfigMapVolumeSource{ - LocalObjectReference: corev1.LocalObjectReference{ - Name: "starboard-trivy-config", - }, - Items: []corev1.KeyToPath{ - { - Key: "trivy.ignoreFile", - Path: ".trivyignore", - }, - }, - }, - }, + }, + SecurityContext: &corev1.PodSecurityContext{}, + } + return *j +} + +func TestPlugin_GetScanJobSpec(t *testing.T) { + testCases := []JobSpecTestCase{} + + //default config + testCases = append(testCases, NewJobSpecTestCase("Default single nginx image scan")) + + //ignore unfixed + ignoreUnfixed := NewJobSpecTestCase("Ignore unfixed") + ignoreUnfixed.Config["grype.onlyFixed"] = "true" + ignoreUnfixed.ExpectedJobSpec.Containers[0].Args = + append(ignoreUnfixed.ExpectedJobSpec.Containers[0].Args, "--only-fixed") + testCases = append(testCases, ignoreUnfixed) + + //add CPEs + addCPEs := NewJobSpecTestCase("Add missing CPEs") + addCPEs.Config["grype.addMissingCPEs"] = "true" + addCPEs.ExpectedJobSpec.Containers[0].Args = + append(addCPEs.ExpectedJobSpec.Containers[0].Args, "--add-cpes-if-none") + testCases = append(testCases, addCPEs) + + //both optional args + bothOptArgs := NewJobSpecTestCase("Both optional args") + bothOptArgs.Config["grype.addMissingCPEs"] = "true" + bothOptArgs.Config["grype.onlyFixed"] = "true" + bothOptArgs.ExpectedJobSpec.Containers[0].Args = + append(bothOptArgs.ExpectedJobSpec.Containers[0].Args, "--add-cpes-if-none", "--only-fixed") + testCases = append(testCases, bothOptArgs) + + //add scheme + scheme := NewJobSpecTestCase("Scan image with specified scheme") + scheme.Config["grype.scheme"] = "podman" + scheme.ExpectedJobSpec.Containers[0].Args[0] = "podman:nginx:1.16" + testCases = append(testCases, scheme) + + // Test cases when starboard is enabled with option to run job in the namespace of workload + for _, tc := range testCases { + t.Run(tc.Name, func(t *testing.T) { + fakeclient := fake.NewClientBuilder().WithObjects( + &corev1.ConfigMap{ + ObjectMeta: metav1.ObjectMeta{ + Name: "starboard-grype-config", + Namespace: "starboard-ns", }, - }, - InitContainers: []corev1.Container{ - { - Name: "00000000-0000-0000-0000-000000000001", - Image: "docker.io/aquasec/trivy:0.14.0", - ImagePullPolicy: corev1.PullIfNotPresent, - TerminationMessagePolicy: corev1.TerminationMessageFallbackToLogsOnError, - Env: []corev1.EnvVar{ - { - Name: "HTTP_PROXY", - ValueFrom: &corev1.EnvVarSource{ - ConfigMapKeyRef: &corev1.ConfigMapKeySelector{ - LocalObjectReference: corev1.LocalObjectReference{ - Name: "starboard-trivy-config", - }, - Key: "trivy.httpProxy", - Optional: pointer.BoolPtr(true), - }, - }, - }, - { - Name: "HTTPS_PROXY", - ValueFrom: &corev1.EnvVarSource{ - ConfigMapKeyRef: &corev1.ConfigMapKeySelector{ - LocalObjectReference: corev1.LocalObjectReference{ - Name: "starboard-trivy-config", - }, - Key: "trivy.httpsProxy", - Optional: pointer.BoolPtr(true), - }, - }, - }, - { - Name: "NO_PROXY", - ValueFrom: &corev1.EnvVarSource{ - ConfigMapKeyRef: &corev1.ConfigMapKeySelector{ - LocalObjectReference: corev1.LocalObjectReference{ - Name: "starboard-trivy-config", - }, - Key: "trivy.noProxy", - Optional: pointer.BoolPtr(true), - }, - }, - }, - { - Name: "GITHUB_TOKEN", - ValueFrom: &corev1.EnvVarSource{ - SecretKeyRef: &corev1.SecretKeySelector{ - LocalObjectReference: corev1.LocalObjectReference{ - Name: "starboard-trivy-config", - }, - Key: "trivy.githubToken", - Optional: pointer.BoolPtr(true), - }, - }, - }, - }, - Command: []string{ - "trivy", - }, - Args: []string{ - "--cache-dir", "/tmp/trivy/.cache", - "image", - "--download-db-only", - "--db-repository", defaultUpdateURL, - }, - Resources: corev1.ResourceRequirements{ - Requests: corev1.ResourceList{ - corev1.ResourceCPU: resource.MustParse("100m"), - corev1.ResourceMemory: resource.MustParse("100M"), - }, - Limits: corev1.ResourceList{ - corev1.ResourceCPU: resource.MustParse("500m"), - corev1.ResourceMemory: resource.MustParse("500M"), - }, - }, - VolumeMounts: []corev1.VolumeMount{ - tmpVolumeMount, - }, - }, - }, - Containers: []corev1.Container{ - { - Name: "nginx", - Image: "docker.io/aquasec/trivy:0.14.0", - ImagePullPolicy: corev1.PullIfNotPresent, - TerminationMessagePolicy: corev1.TerminationMessageFallbackToLogsOnError, - Env: []corev1.EnvVar{ - { - Name: "TRIVY_SEVERITY", - ValueFrom: &corev1.EnvVarSource{ - ConfigMapKeyRef: &corev1.ConfigMapKeySelector{ - LocalObjectReference: corev1.LocalObjectReference{ - Name: "starboard-trivy-config", - }, - Key: "trivy.severity", - Optional: pointer.BoolPtr(true), - }, - }, - }, - { - Name: "TRIVY_IGNORE_UNFIXED", - ValueFrom: &corev1.EnvVarSource{ - ConfigMapKeyRef: &corev1.ConfigMapKeySelector{ - LocalObjectReference: corev1.LocalObjectReference{ - Name: "starboard-trivy-config", - }, - Key: "trivy.ignoreUnfixed", - Optional: pointer.BoolPtr(true), - }, - }, - }, - - { - Name: "TRIVY_SKIP_FILES", - ValueFrom: &corev1.EnvVarSource{ - ConfigMapKeyRef: &corev1.ConfigMapKeySelector{ - LocalObjectReference: corev1.LocalObjectReference{ - Name: "starboard-trivy-config", - }, - Key: "trivy.skipFiles", - Optional: pointer.BoolPtr(true), - }, - }, - }, - { - Name: "TRIVY_SKIP_DIRS", - ValueFrom: &corev1.EnvVarSource{ - ConfigMapKeyRef: &corev1.ConfigMapKeySelector{ - LocalObjectReference: corev1.LocalObjectReference{ - Name: "starboard-trivy-config", - }, - Key: "trivy.skipDirs", - Optional: pointer.BoolPtr(true), - }, - }, - }, - { - Name: "HTTP_PROXY", - ValueFrom: &corev1.EnvVarSource{ - ConfigMapKeyRef: &corev1.ConfigMapKeySelector{ - LocalObjectReference: corev1.LocalObjectReference{ - Name: "starboard-trivy-config", - }, - Key: "trivy.httpProxy", - Optional: pointer.BoolPtr(true), - }, - }, - }, - { - Name: "HTTPS_PROXY", - ValueFrom: &corev1.EnvVarSource{ - ConfigMapKeyRef: &corev1.ConfigMapKeySelector{ - LocalObjectReference: corev1.LocalObjectReference{ - Name: "starboard-trivy-config", - }, - Key: "trivy.httpsProxy", - Optional: pointer.BoolPtr(true), - }, - }, - }, - { - Name: "NO_PROXY", - ValueFrom: &corev1.EnvVarSource{ - ConfigMapKeyRef: &corev1.ConfigMapKeySelector{ - LocalObjectReference: corev1.LocalObjectReference{ - Name: "starboard-trivy-config", - }, - Key: "trivy.noProxy", - Optional: pointer.BoolPtr(true), - }, - }, - }, - { - Name: "TRIVY_IGNOREFILE", - Value: "/etc/trivy/.trivyignore", - }, - }, - Command: []string{ - "trivy", - }, - Args: []string{ - "--cache-dir", "/tmp/trivy/.cache", - "--quiet", - "image", - "--skip-update", - "--format", "json", - "nginx:1.16", - }, - Resources: corev1.ResourceRequirements{ - Requests: corev1.ResourceList{ - corev1.ResourceCPU: resource.MustParse("100m"), - corev1.ResourceMemory: resource.MustParse("100M"), - }, - Limits: corev1.ResourceList{ - corev1.ResourceCPU: resource.MustParse("500m"), - corev1.ResourceMemory: resource.MustParse("500M"), - }, - }, - VolumeMounts: []corev1.VolumeMount{ - tmpVolumeMount, - { - Name: "ignorefile", - MountPath: "/etc/trivy/.trivyignore", - SubPath: ".trivyignore", - }, - }, - SecurityContext: &corev1.SecurityContext{ - Privileged: pointer.BoolPtr(false), - AllowPrivilegeEscalation: pointer.BoolPtr(false), - Capabilities: &corev1.Capabilities{ - Drop: []corev1.Capability{"all"}, - }, - ReadOnlyRootFilesystem: pointer.BoolPtr(true), - }, - }, - }, - SecurityContext: &corev1.PodSecurityContext{}, - }, - }, - { - name: "Standalone mode with mirror", - config: map[string]string{ - "trivy.imageRef": "docker.io/aquasec/trivy:0.14.0", - "trivy.mode": string(trivy.Standalone), - - "trivy.dbRepository": defaultUpdateURL, - "trivy.resources.requests.cpu": "100m", - "trivy.resources.requests.memory": "100M", - "trivy.resources.limits.cpu": "500m", - "trivy.resources.limits.memory": "500M", - - "trivy.registry.mirror.index.docker.io": "mirror.io", - }, - workloadSpec: &corev1.Pod{ - TypeMeta: metav1.TypeMeta{ - Kind: "Pod", - APIVersion: "v1", - }, - ObjectMeta: metav1.ObjectMeta{ - Name: "nginx", - Namespace: "prod-ns", - }, - Spec: corev1.PodSpec{ - Containers: []corev1.Container{ - { - Name: "nginx", - Image: "nginx:1.16", - }, - }, - }, - }, - expectedJobSpec: corev1.PodSpec{ - Affinity: starboard.LinuxNodeAffinity(), - RestartPolicy: corev1.RestartPolicyNever, - ServiceAccountName: "starboard-sa", - AutomountServiceAccountToken: pointer.BoolPtr(false), - Volumes: []corev1.Volume{ - tmpVolume, - }, - InitContainers: []corev1.Container{ - { - Name: "00000000-0000-0000-0000-000000000001", - Image: "docker.io/aquasec/trivy:0.14.0", - ImagePullPolicy: corev1.PullIfNotPresent, - TerminationMessagePolicy: corev1.TerminationMessageFallbackToLogsOnError, - Env: []corev1.EnvVar{ - { - Name: "HTTP_PROXY", - ValueFrom: &corev1.EnvVarSource{ - ConfigMapKeyRef: &corev1.ConfigMapKeySelector{ - LocalObjectReference: corev1.LocalObjectReference{ - Name: "starboard-trivy-config", - }, - Key: "trivy.httpProxy", - Optional: pointer.BoolPtr(true), - }, - }, - }, - { - Name: "HTTPS_PROXY", - ValueFrom: &corev1.EnvVarSource{ - ConfigMapKeyRef: &corev1.ConfigMapKeySelector{ - LocalObjectReference: corev1.LocalObjectReference{ - Name: "starboard-trivy-config", - }, - Key: "trivy.httpsProxy", - Optional: pointer.BoolPtr(true), - }, - }, - }, - { - Name: "NO_PROXY", - ValueFrom: &corev1.EnvVarSource{ - ConfigMapKeyRef: &corev1.ConfigMapKeySelector{ - LocalObjectReference: corev1.LocalObjectReference{ - Name: "starboard-trivy-config", - }, - Key: "trivy.noProxy", - Optional: pointer.BoolPtr(true), - }, - }, - }, - - { - Name: "GITHUB_TOKEN", - ValueFrom: &corev1.EnvVarSource{ - SecretKeyRef: &corev1.SecretKeySelector{ - LocalObjectReference: corev1.LocalObjectReference{ - Name: "starboard-trivy-config", - }, - Key: "trivy.githubToken", - Optional: pointer.BoolPtr(true), - }, - }, - }, - }, - Command: []string{ - "trivy", - }, - Args: []string{ - "--cache-dir", "/tmp/trivy/.cache", - "image", - "--download-db-only", - "--db-repository", defaultUpdateURL, - }, - Resources: corev1.ResourceRequirements{ - Requests: corev1.ResourceList{ - corev1.ResourceCPU: resource.MustParse("100m"), - corev1.ResourceMemory: resource.MustParse("100M"), - }, - Limits: corev1.ResourceList{ - corev1.ResourceCPU: resource.MustParse("500m"), - corev1.ResourceMemory: resource.MustParse("500M"), - }, - }, - VolumeMounts: []corev1.VolumeMount{ - tmpVolumeMount, - }, - }, - }, - Containers: []corev1.Container{ - { - Name: "nginx", - Image: "docker.io/aquasec/trivy:0.14.0", - ImagePullPolicy: corev1.PullIfNotPresent, - TerminationMessagePolicy: corev1.TerminationMessageFallbackToLogsOnError, - Env: []corev1.EnvVar{ - { - Name: "TRIVY_SEVERITY", - ValueFrom: &corev1.EnvVarSource{ - ConfigMapKeyRef: &corev1.ConfigMapKeySelector{ - LocalObjectReference: corev1.LocalObjectReference{ - Name: "starboard-trivy-config", - }, - Key: "trivy.severity", - Optional: pointer.BoolPtr(true), - }, - }, - }, - { - Name: "TRIVY_IGNORE_UNFIXED", - ValueFrom: &corev1.EnvVarSource{ - ConfigMapKeyRef: &corev1.ConfigMapKeySelector{ - LocalObjectReference: corev1.LocalObjectReference{ - Name: "starboard-trivy-config", - }, - Key: "trivy.ignoreUnfixed", - Optional: pointer.BoolPtr(true), - }, - }, - }, - - { - Name: "TRIVY_SKIP_FILES", - ValueFrom: &corev1.EnvVarSource{ - ConfigMapKeyRef: &corev1.ConfigMapKeySelector{ - LocalObjectReference: corev1.LocalObjectReference{ - Name: "starboard-trivy-config", - }, - Key: "trivy.skipFiles", - Optional: pointer.BoolPtr(true), - }, - }, - }, - { - Name: "TRIVY_SKIP_DIRS", - ValueFrom: &corev1.EnvVarSource{ - ConfigMapKeyRef: &corev1.ConfigMapKeySelector{ - LocalObjectReference: corev1.LocalObjectReference{ - Name: "starboard-trivy-config", - }, - Key: "trivy.skipDirs", - Optional: pointer.BoolPtr(true), - }, - }, - }, - { - Name: "HTTP_PROXY", - ValueFrom: &corev1.EnvVarSource{ - ConfigMapKeyRef: &corev1.ConfigMapKeySelector{ - LocalObjectReference: corev1.LocalObjectReference{ - Name: "starboard-trivy-config", - }, - Key: "trivy.httpProxy", - Optional: pointer.BoolPtr(true), - }, - }, - }, - { - Name: "HTTPS_PROXY", - ValueFrom: &corev1.EnvVarSource{ - ConfigMapKeyRef: &corev1.ConfigMapKeySelector{ - LocalObjectReference: corev1.LocalObjectReference{ - Name: "starboard-trivy-config", - }, - Key: "trivy.httpsProxy", - Optional: pointer.BoolPtr(true), - }, - }, - }, - { - Name: "NO_PROXY", - ValueFrom: &corev1.EnvVarSource{ - ConfigMapKeyRef: &corev1.ConfigMapKeySelector{ - LocalObjectReference: corev1.LocalObjectReference{ - Name: "starboard-trivy-config", - }, - Key: "trivy.noProxy", - Optional: pointer.BoolPtr(true), - }, - }, - }, - }, - Command: []string{ - "trivy", - }, - Args: []string{ - "--cache-dir", "/tmp/trivy/.cache", - "--quiet", - "image", - "--skip-update", - "--format", "json", - "mirror.io/library/nginx:1.16", - }, - Resources: corev1.ResourceRequirements{ - Requests: corev1.ResourceList{ - corev1.ResourceCPU: resource.MustParse("100m"), - corev1.ResourceMemory: resource.MustParse("100M"), - }, - Limits: corev1.ResourceList{ - corev1.ResourceCPU: resource.MustParse("500m"), - corev1.ResourceMemory: resource.MustParse("500M"), - }, - }, - VolumeMounts: []corev1.VolumeMount{ - tmpVolumeMount, - }, - SecurityContext: &corev1.SecurityContext{ - Privileged: pointer.BoolPtr(false), - AllowPrivilegeEscalation: pointer.BoolPtr(false), - Capabilities: &corev1.Capabilities{ - Drop: []corev1.Capability{"all"}, - }, - ReadOnlyRootFilesystem: pointer.BoolPtr(true), - }, - }, - }, - SecurityContext: &corev1.PodSecurityContext{}, - }, - }, - { - name: "ClientServer mode without insecure registry", - config: map[string]string{ - "trivy.imageRef": "docker.io/aquasec/trivy:0.14.0", - "trivy.mode": string(trivy.ClientServer), - "trivy.serverURL": "http://trivy.trivy:4954", - "trivy.dbRepository": defaultUpdateURL, - "trivy.resources.requests.cpu": "100m", - "trivy.resources.requests.memory": "100M", - "trivy.resources.limits.cpu": "500m", - "trivy.resources.limits.memory": "500M", - }, - workloadSpec: &corev1.Pod{ - TypeMeta: metav1.TypeMeta{ - Kind: "Pod", - APIVersion: "v1", - }, - ObjectMeta: metav1.ObjectMeta{ - Name: "nginx", - Namespace: "prod-ns", - }, - Spec: corev1.PodSpec{ - Containers: []corev1.Container{ - { - Name: "nginx", - Image: "nginx:1.16", - }, - }, - }, - }, - expectedJobSpec: corev1.PodSpec{ - Affinity: starboard.LinuxNodeAffinity(), - RestartPolicy: corev1.RestartPolicyNever, - ServiceAccountName: "starboard-sa", - AutomountServiceAccountToken: pointer.BoolPtr(false), - Containers: []corev1.Container{ - { - Name: "nginx", - Image: "docker.io/aquasec/trivy:0.14.0", - ImagePullPolicy: corev1.PullIfNotPresent, - TerminationMessagePolicy: corev1.TerminationMessageFallbackToLogsOnError, - Env: []corev1.EnvVar{ - { - Name: "HTTP_PROXY", - ValueFrom: &corev1.EnvVarSource{ - ConfigMapKeyRef: &corev1.ConfigMapKeySelector{ - LocalObjectReference: corev1.LocalObjectReference{ - Name: "starboard-trivy-config", - }, - Key: "trivy.httpProxy", - Optional: pointer.BoolPtr(true), - }, - }, - }, - { - Name: "HTTPS_PROXY", - ValueFrom: &corev1.EnvVarSource{ - ConfigMapKeyRef: &corev1.ConfigMapKeySelector{ - LocalObjectReference: corev1.LocalObjectReference{ - Name: "starboard-trivy-config", - }, - Key: "trivy.httpsProxy", - Optional: pointer.BoolPtr(true), - }, - }, - }, - { - Name: "NO_PROXY", - ValueFrom: &corev1.EnvVarSource{ - ConfigMapKeyRef: &corev1.ConfigMapKeySelector{ - LocalObjectReference: corev1.LocalObjectReference{ - Name: "starboard-trivy-config", - }, - Key: "trivy.noProxy", - Optional: pointer.BoolPtr(true), - }, - }, - }, - { - Name: "TRIVY_SEVERITY", - ValueFrom: &corev1.EnvVarSource{ - ConfigMapKeyRef: &corev1.ConfigMapKeySelector{ - LocalObjectReference: corev1.LocalObjectReference{ - Name: "starboard-trivy-config", - }, - Key: "trivy.severity", - Optional: pointer.BoolPtr(true), - }, - }, - }, - { - Name: "TRIVY_IGNORE_UNFIXED", - ValueFrom: &corev1.EnvVarSource{ - ConfigMapKeyRef: &corev1.ConfigMapKeySelector{ - LocalObjectReference: corev1.LocalObjectReference{ - Name: "starboard-trivy-config", - }, - Key: "trivy.ignoreUnfixed", - Optional: pointer.BoolPtr(true), - }, - }, - }, - - { - Name: "TRIVY_SKIP_FILES", - ValueFrom: &corev1.EnvVarSource{ - ConfigMapKeyRef: &corev1.ConfigMapKeySelector{ - LocalObjectReference: corev1.LocalObjectReference{ - Name: "starboard-trivy-config", - }, - Key: "trivy.skipFiles", - Optional: pointer.BoolPtr(true), - }, - }, - }, - { - Name: "TRIVY_SKIP_DIRS", - ValueFrom: &corev1.EnvVarSource{ - ConfigMapKeyRef: &corev1.ConfigMapKeySelector{ - LocalObjectReference: corev1.LocalObjectReference{ - Name: "starboard-trivy-config", - }, - Key: "trivy.skipDirs", - Optional: pointer.BoolPtr(true), - }, - }, - }, - { - Name: "TRIVY_TOKEN_HEADER", - ValueFrom: &corev1.EnvVarSource{ - ConfigMapKeyRef: &corev1.ConfigMapKeySelector{ - LocalObjectReference: corev1.LocalObjectReference{ - Name: "starboard-trivy-config", - }, - Key: "trivy.serverTokenHeader", - Optional: pointer.BoolPtr(true), - }, - }, - }, - { - Name: "TRIVY_TOKEN", - ValueFrom: &corev1.EnvVarSource{ - SecretKeyRef: &corev1.SecretKeySelector{ - LocalObjectReference: corev1.LocalObjectReference{ - Name: "starboard-trivy-config", - }, - Key: "trivy.serverToken", - Optional: pointer.BoolPtr(true), - }, - }, - }, - { - Name: "TRIVY_CUSTOM_HEADERS", - ValueFrom: &corev1.EnvVarSource{ - SecretKeyRef: &corev1.SecretKeySelector{ - LocalObjectReference: corev1.LocalObjectReference{ - Name: "starboard-trivy-config", - }, - Key: "trivy.serverCustomHeaders", - Optional: pointer.BoolPtr(true), - }, - }, - }, - }, - Command: []string{ - "trivy", - }, - Args: []string{ - "--quiet", - "client", - "--format", - "json", - "--remote", - "http://trivy.trivy:4954", - "nginx:1.16", - }, - Resources: corev1.ResourceRequirements{ - Requests: corev1.ResourceList{ - corev1.ResourceCPU: resource.MustParse("100m"), - corev1.ResourceMemory: resource.MustParse("100M"), - }, - Limits: corev1.ResourceList{ - corev1.ResourceCPU: resource.MustParse("500m"), - corev1.ResourceMemory: resource.MustParse("500M"), - }, - }, - }, - }, - }, - }, - { - name: "ClientServer mode without insecure registry", - config: map[string]string{ - "trivy.imageRef": "docker.io/aquasec/trivy:0.14.0", - "trivy.mode": string(trivy.ClientServer), - "trivy.serverURL": "http://trivy.trivy:4954", - "trivy.dbRepository": defaultUpdateURL, - "trivy.resources.requests.cpu": "100m", - "trivy.resources.requests.memory": "100M", - "trivy.resources.limits.cpu": "500m", - "trivy.resources.limits.memory": "500M", - }, - workloadSpec: &corev1.Pod{ - TypeMeta: metav1.TypeMeta{ - Kind: "Pod", - APIVersion: "v1", - }, - ObjectMeta: metav1.ObjectMeta{ - Name: "nginx", - Namespace: "prod-ns", - }, - Spec: corev1.PodSpec{ - Containers: []corev1.Container{ - { - Name: "nginx", - Image: "nginx:1.16", - }, - }, - }, - }, - expectedJobSpec: corev1.PodSpec{ - Affinity: starboard.LinuxNodeAffinity(), - RestartPolicy: corev1.RestartPolicyNever, - ServiceAccountName: "starboard-sa", - AutomountServiceAccountToken: pointer.BoolPtr(false), - Containers: []corev1.Container{ - { - Name: "nginx", - Image: "docker.io/aquasec/trivy:0.14.0", - ImagePullPolicy: corev1.PullIfNotPresent, - TerminationMessagePolicy: corev1.TerminationMessageFallbackToLogsOnError, - Env: []corev1.EnvVar{ - { - Name: "HTTP_PROXY", - ValueFrom: &corev1.EnvVarSource{ - ConfigMapKeyRef: &corev1.ConfigMapKeySelector{ - LocalObjectReference: corev1.LocalObjectReference{ - Name: "starboard-trivy-config", - }, - Key: "trivy.httpProxy", - Optional: pointer.BoolPtr(true), - }, - }, - }, - { - Name: "HTTPS_PROXY", - ValueFrom: &corev1.EnvVarSource{ - ConfigMapKeyRef: &corev1.ConfigMapKeySelector{ - LocalObjectReference: corev1.LocalObjectReference{ - Name: "starboard-trivy-config", - }, - Key: "trivy.httpsProxy", - Optional: pointer.BoolPtr(true), - }, - }, - }, - { - Name: "NO_PROXY", - ValueFrom: &corev1.EnvVarSource{ - ConfigMapKeyRef: &corev1.ConfigMapKeySelector{ - LocalObjectReference: corev1.LocalObjectReference{ - Name: "starboard-trivy-config", - }, - Key: "trivy.noProxy", - Optional: pointer.BoolPtr(true), - }, - }, - }, - { - Name: "TRIVY_SEVERITY", - ValueFrom: &corev1.EnvVarSource{ - ConfigMapKeyRef: &corev1.ConfigMapKeySelector{ - LocalObjectReference: corev1.LocalObjectReference{ - Name: "starboard-trivy-config", - }, - Key: "trivy.severity", - Optional: pointer.BoolPtr(true), - }, - }, - }, - { - Name: "TRIVY_IGNORE_UNFIXED", - ValueFrom: &corev1.EnvVarSource{ - ConfigMapKeyRef: &corev1.ConfigMapKeySelector{ - LocalObjectReference: corev1.LocalObjectReference{ - Name: "starboard-trivy-config", - }, - Key: "trivy.ignoreUnfixed", - Optional: pointer.BoolPtr(true), - }, - }, - }, - - { - Name: "TRIVY_SKIP_FILES", - ValueFrom: &corev1.EnvVarSource{ - ConfigMapKeyRef: &corev1.ConfigMapKeySelector{ - LocalObjectReference: corev1.LocalObjectReference{ - Name: "starboard-trivy-config", - }, - Key: "trivy.skipFiles", - Optional: pointer.BoolPtr(true), - }, - }, - }, - { - Name: "TRIVY_SKIP_DIRS", - ValueFrom: &corev1.EnvVarSource{ - ConfigMapKeyRef: &corev1.ConfigMapKeySelector{ - LocalObjectReference: corev1.LocalObjectReference{ - Name: "starboard-trivy-config", - }, - Key: "trivy.skipDirs", - Optional: pointer.BoolPtr(true), - }, - }, - }, - { - Name: "TRIVY_TOKEN_HEADER", - ValueFrom: &corev1.EnvVarSource{ - ConfigMapKeyRef: &corev1.ConfigMapKeySelector{ - LocalObjectReference: corev1.LocalObjectReference{ - Name: "starboard-trivy-config", - }, - Key: "trivy.serverTokenHeader", - Optional: pointer.BoolPtr(true), - }, - }, - }, - { - Name: "TRIVY_TOKEN", - ValueFrom: &corev1.EnvVarSource{ - SecretKeyRef: &corev1.SecretKeySelector{ - LocalObjectReference: corev1.LocalObjectReference{ - Name: "starboard-trivy-config", - }, - Key: "trivy.serverToken", - Optional: pointer.BoolPtr(true), - }, - }, - }, - { - Name: "TRIVY_CUSTOM_HEADERS", - ValueFrom: &corev1.EnvVarSource{ - SecretKeyRef: &corev1.SecretKeySelector{ - LocalObjectReference: corev1.LocalObjectReference{ - Name: "starboard-trivy-config", - }, - Key: "trivy.serverCustomHeaders", - Optional: pointer.BoolPtr(true), - }, - }, - }, - }, - Command: []string{ - "trivy", - }, - Args: []string{ - "--quiet", - "client", - "--format", - "json", - "--remote", - "http://trivy.trivy:4954", - "nginx:1.16", - }, - Resources: corev1.ResourceRequirements{ - Requests: corev1.ResourceList{ - corev1.ResourceCPU: resource.MustParse("100m"), - corev1.ResourceMemory: resource.MustParse("100M"), - }, - Limits: corev1.ResourceList{ - corev1.ResourceCPU: resource.MustParse("500m"), - corev1.ResourceMemory: resource.MustParse("500M"), - }, - }, - }, - }, - }, - }, - { - name: "ClientServer mode with insecure server", - config: map[string]string{ - "trivy.imageRef": "docker.io/aquasec/trivy:0.14.0", - "trivy.mode": string(trivy.ClientServer), - "trivy.serverURL": "https://trivy.trivy:4954", - "trivy.serverInsecure": "true", - "trivy.dbRepository": defaultUpdateURL, - "trivy.resources.requests.cpu": "100m", - "trivy.resources.requests.memory": "100M", - "trivy.resources.limits.cpu": "500m", - "trivy.resources.limits.memory": "500M", - }, - workloadSpec: &corev1.Pod{ - TypeMeta: metav1.TypeMeta{ - Kind: "Pod", - APIVersion: "v1", - }, - ObjectMeta: metav1.ObjectMeta{ - Name: "nginx", - Namespace: "prod-ns", - }, - Spec: corev1.PodSpec{ - Containers: []corev1.Container{ - { - Name: "nginx", - Image: "poc.myregistry.harbor.com.pl/nginx:1.16", - }, - }, - }, - }, - expectedJobSpec: corev1.PodSpec{ - Affinity: starboard.LinuxNodeAffinity(), - RestartPolicy: corev1.RestartPolicyNever, - ServiceAccountName: "starboard-sa", - AutomountServiceAccountToken: pointer.BoolPtr(false), - Containers: []corev1.Container{ - { - Name: "nginx", - Image: "docker.io/aquasec/trivy:0.14.0", - ImagePullPolicy: corev1.PullIfNotPresent, - TerminationMessagePolicy: corev1.TerminationMessageFallbackToLogsOnError, - Env: []corev1.EnvVar{ - { - Name: "HTTP_PROXY", - ValueFrom: &corev1.EnvVarSource{ - ConfigMapKeyRef: &corev1.ConfigMapKeySelector{ - LocalObjectReference: corev1.LocalObjectReference{ - Name: "starboard-trivy-config", - }, - Key: "trivy.httpProxy", - Optional: pointer.BoolPtr(true), - }, - }, - }, - { - Name: "HTTPS_PROXY", - ValueFrom: &corev1.EnvVarSource{ - ConfigMapKeyRef: &corev1.ConfigMapKeySelector{ - LocalObjectReference: corev1.LocalObjectReference{ - Name: "starboard-trivy-config", - }, - Key: "trivy.httpsProxy", - Optional: pointer.BoolPtr(true), - }, - }, - }, - { - Name: "NO_PROXY", - ValueFrom: &corev1.EnvVarSource{ - ConfigMapKeyRef: &corev1.ConfigMapKeySelector{ - LocalObjectReference: corev1.LocalObjectReference{ - Name: "starboard-trivy-config", - }, - Key: "trivy.noProxy", - Optional: pointer.BoolPtr(true), - }, - }, - }, - { - Name: "TRIVY_SEVERITY", - ValueFrom: &corev1.EnvVarSource{ - ConfigMapKeyRef: &corev1.ConfigMapKeySelector{ - LocalObjectReference: corev1.LocalObjectReference{ - Name: "starboard-trivy-config", - }, - Key: "trivy.severity", - Optional: pointer.BoolPtr(true), - }, - }, - }, - { - Name: "TRIVY_IGNORE_UNFIXED", - ValueFrom: &corev1.EnvVarSource{ - ConfigMapKeyRef: &corev1.ConfigMapKeySelector{ - LocalObjectReference: corev1.LocalObjectReference{ - Name: "starboard-trivy-config", - }, - Key: "trivy.ignoreUnfixed", - Optional: pointer.BoolPtr(true), - }, - }, - }, - - { - Name: "TRIVY_SKIP_FILES", - ValueFrom: &corev1.EnvVarSource{ - ConfigMapKeyRef: &corev1.ConfigMapKeySelector{ - LocalObjectReference: corev1.LocalObjectReference{ - Name: "starboard-trivy-config", - }, - Key: "trivy.skipFiles", - Optional: pointer.BoolPtr(true), - }, - }, - }, - { - Name: "TRIVY_SKIP_DIRS", - ValueFrom: &corev1.EnvVarSource{ - ConfigMapKeyRef: &corev1.ConfigMapKeySelector{ - LocalObjectReference: corev1.LocalObjectReference{ - Name: "starboard-trivy-config", - }, - Key: "trivy.skipDirs", - Optional: pointer.BoolPtr(true), - }, - }, - }, - { - Name: "TRIVY_TOKEN_HEADER", - ValueFrom: &corev1.EnvVarSource{ - ConfigMapKeyRef: &corev1.ConfigMapKeySelector{ - LocalObjectReference: corev1.LocalObjectReference{ - Name: "starboard-trivy-config", - }, - Key: "trivy.serverTokenHeader", - Optional: pointer.BoolPtr(true), - }, - }, - }, - { - Name: "TRIVY_TOKEN", - ValueFrom: &corev1.EnvVarSource{ - SecretKeyRef: &corev1.SecretKeySelector{ - LocalObjectReference: corev1.LocalObjectReference{ - Name: "starboard-trivy-config", - }, - Key: "trivy.serverToken", - Optional: pointer.BoolPtr(true), - }, - }, - }, - { - Name: "TRIVY_CUSTOM_HEADERS", - ValueFrom: &corev1.EnvVarSource{ - SecretKeyRef: &corev1.SecretKeySelector{ - LocalObjectReference: corev1.LocalObjectReference{ - Name: "starboard-trivy-config", - }, - Key: "trivy.serverCustomHeaders", - Optional: pointer.BoolPtr(true), - }, - }, - }, - { - Name: "TRIVY_INSECURE", - Value: "true", - }, - }, - Command: []string{ - "trivy", - }, - Args: []string{ - "--quiet", - "client", - "--format", - "json", - "--remote", - "https://trivy.trivy:4954", - "poc.myregistry.harbor.com.pl/nginx:1.16", - }, - Resources: corev1.ResourceRequirements{ - Requests: corev1.ResourceList{ - corev1.ResourceCPU: resource.MustParse("100m"), - corev1.ResourceMemory: resource.MustParse("100M"), - }, - Limits: corev1.ResourceList{ - corev1.ResourceCPU: resource.MustParse("500m"), - corev1.ResourceMemory: resource.MustParse("500M"), - }, - }, - }, - }, - }, - }, - { - name: "ClientServer mode with non-SSL registry", - config: map[string]string{ - "trivy.imageRef": "docker.io/aquasec/trivy:0.14.0", - "trivy.mode": string(trivy.ClientServer), - "trivy.serverURL": "http://trivy.trivy:4954", - "trivy.nonSslRegistry.pocRegistry": "poc.myregistry.harbor.com.pl", - "trivy.dbRepository": defaultUpdateURL, - "trivy.resources.requests.cpu": "100m", - "trivy.resources.requests.memory": "100M", - "trivy.resources.limits.cpu": "500m", - "trivy.resources.limits.memory": "500M", - }, - workloadSpec: &corev1.Pod{ - TypeMeta: metav1.TypeMeta{ - Kind: "Pod", - APIVersion: "v1", - }, - ObjectMeta: metav1.ObjectMeta{ - Name: "nginx", - Namespace: "prod-ns", - }, - Spec: corev1.PodSpec{ - Containers: []corev1.Container{ - { - Name: "nginx", - Image: "poc.myregistry.harbor.com.pl/nginx:1.16", - }, - }, - }, - }, - expectedJobSpec: corev1.PodSpec{ - Affinity: starboard.LinuxNodeAffinity(), - RestartPolicy: corev1.RestartPolicyNever, - ServiceAccountName: "starboard-sa", - AutomountServiceAccountToken: pointer.BoolPtr(false), - Containers: []corev1.Container{ - { - Name: "nginx", - Image: "docker.io/aquasec/trivy:0.14.0", - ImagePullPolicy: corev1.PullIfNotPresent, - TerminationMessagePolicy: corev1.TerminationMessageFallbackToLogsOnError, - Env: []corev1.EnvVar{ - { - Name: "HTTP_PROXY", - ValueFrom: &corev1.EnvVarSource{ - ConfigMapKeyRef: &corev1.ConfigMapKeySelector{ - LocalObjectReference: corev1.LocalObjectReference{ - Name: "starboard-trivy-config", - }, - Key: "trivy.httpProxy", - Optional: pointer.BoolPtr(true), - }, - }, - }, - { - Name: "HTTPS_PROXY", - ValueFrom: &corev1.EnvVarSource{ - ConfigMapKeyRef: &corev1.ConfigMapKeySelector{ - LocalObjectReference: corev1.LocalObjectReference{ - Name: "starboard-trivy-config", - }, - Key: "trivy.httpsProxy", - Optional: pointer.BoolPtr(true), - }, - }, - }, - { - Name: "NO_PROXY", - ValueFrom: &corev1.EnvVarSource{ - ConfigMapKeyRef: &corev1.ConfigMapKeySelector{ - LocalObjectReference: corev1.LocalObjectReference{ - Name: "starboard-trivy-config", - }, - Key: "trivy.noProxy", - Optional: pointer.BoolPtr(true), - }, - }, - }, - { - Name: "TRIVY_SEVERITY", - ValueFrom: &corev1.EnvVarSource{ - ConfigMapKeyRef: &corev1.ConfigMapKeySelector{ - LocalObjectReference: corev1.LocalObjectReference{ - Name: "starboard-trivy-config", - }, - Key: "trivy.severity", - Optional: pointer.BoolPtr(true), - }, - }, - }, - { - Name: "TRIVY_IGNORE_UNFIXED", - ValueFrom: &corev1.EnvVarSource{ - ConfigMapKeyRef: &corev1.ConfigMapKeySelector{ - LocalObjectReference: corev1.LocalObjectReference{ - Name: "starboard-trivy-config", - }, - Key: "trivy.ignoreUnfixed", - Optional: pointer.BoolPtr(true), - }, - }, - }, - - { - Name: "TRIVY_SKIP_FILES", - ValueFrom: &corev1.EnvVarSource{ - ConfigMapKeyRef: &corev1.ConfigMapKeySelector{ - LocalObjectReference: corev1.LocalObjectReference{ - Name: "starboard-trivy-config", - }, - Key: "trivy.skipFiles", - Optional: pointer.BoolPtr(true), - }, - }, - }, - { - Name: "TRIVY_SKIP_DIRS", - ValueFrom: &corev1.EnvVarSource{ - ConfigMapKeyRef: &corev1.ConfigMapKeySelector{ - LocalObjectReference: corev1.LocalObjectReference{ - Name: "starboard-trivy-config", - }, - Key: "trivy.skipDirs", - Optional: pointer.BoolPtr(true), - }, - }, - }, - { - Name: "TRIVY_TOKEN_HEADER", - ValueFrom: &corev1.EnvVarSource{ - ConfigMapKeyRef: &corev1.ConfigMapKeySelector{ - LocalObjectReference: corev1.LocalObjectReference{ - Name: "starboard-trivy-config", - }, - Key: "trivy.serverTokenHeader", - Optional: pointer.BoolPtr(true), - }, - }, - }, - { - Name: "TRIVY_TOKEN", - ValueFrom: &corev1.EnvVarSource{ - SecretKeyRef: &corev1.SecretKeySelector{ - LocalObjectReference: corev1.LocalObjectReference{ - Name: "starboard-trivy-config", - }, - Key: "trivy.serverToken", - Optional: pointer.BoolPtr(true), - }, - }, - }, - { - Name: "TRIVY_CUSTOM_HEADERS", - ValueFrom: &corev1.EnvVarSource{ - SecretKeyRef: &corev1.SecretKeySelector{ - LocalObjectReference: corev1.LocalObjectReference{ - Name: "starboard-trivy-config", - }, - Key: "trivy.serverCustomHeaders", - Optional: pointer.BoolPtr(true), - }, - }, - }, - { - Name: "TRIVY_NON_SSL", - Value: "true", - }, - }, - Command: []string{ - "trivy", - }, - Args: []string{ - "--quiet", - "client", - "--format", - "json", - "--remote", - "http://trivy.trivy:4954", - "poc.myregistry.harbor.com.pl/nginx:1.16", - }, - Resources: corev1.ResourceRequirements{ - Requests: corev1.ResourceList{ - corev1.ResourceCPU: resource.MustParse("100m"), - corev1.ResourceMemory: resource.MustParse("100M"), - }, - Limits: corev1.ResourceList{ - corev1.ResourceCPU: resource.MustParse("500m"), - corev1.ResourceMemory: resource.MustParse("500M"), - }, - }, - }, - }, - }, - }, - { - name: "ClientServer mode with trivyignore file", - config: map[string]string{ - "trivy.imageRef": "docker.io/aquasec/trivy:0.14.0", - "trivy.mode": string(trivy.ClientServer), - "trivy.serverURL": "http://trivy.trivy:4954", - "trivy.ignoreFile": `# Accept the risk -CVE-2018-14618 - -# No impact in our settings -CVE-2019-1543`, - "trivy.dbRepository": defaultUpdateURL, - "trivy.resources.requests.cpu": "100m", - "trivy.resources.requests.memory": "100M", - "trivy.resources.limits.cpu": "500m", - "trivy.resources.limits.memory": "500M", - }, - workloadSpec: &corev1.Pod{ - TypeMeta: metav1.TypeMeta{ - Kind: "Pod", - APIVersion: "v1", - }, - ObjectMeta: metav1.ObjectMeta{ - Name: "nginx", - Namespace: "prod-ns", - }, - Spec: corev1.PodSpec{ - Containers: []corev1.Container{ - { - Name: "nginx", - Image: "nginx:1.16", - }, - }, - }, - }, - expectedJobSpec: corev1.PodSpec{ - Affinity: starboard.LinuxNodeAffinity(), - RestartPolicy: corev1.RestartPolicyNever, - ServiceAccountName: "starboard-sa", - AutomountServiceAccountToken: pointer.BoolPtr(false), - Volumes: []corev1.Volume{ - { - Name: "ignorefile", - VolumeSource: corev1.VolumeSource{ - ConfigMap: &corev1.ConfigMapVolumeSource{ - LocalObjectReference: corev1.LocalObjectReference{ - Name: "starboard-trivy-config", - }, - Items: []corev1.KeyToPath{ - { - Key: "trivy.ignoreFile", - Path: ".trivyignore", - }, - }, - }, - }, - }, - }, - Containers: []corev1.Container{ - { - Name: "nginx", - Image: "docker.io/aquasec/trivy:0.14.0", - ImagePullPolicy: corev1.PullIfNotPresent, - TerminationMessagePolicy: corev1.TerminationMessageFallbackToLogsOnError, - Env: []corev1.EnvVar{ - { - Name: "HTTP_PROXY", - ValueFrom: &corev1.EnvVarSource{ - ConfigMapKeyRef: &corev1.ConfigMapKeySelector{ - LocalObjectReference: corev1.LocalObjectReference{ - Name: "starboard-trivy-config", - }, - Key: "trivy.httpProxy", - Optional: pointer.BoolPtr(true), - }, - }, - }, - { - Name: "HTTPS_PROXY", - ValueFrom: &corev1.EnvVarSource{ - ConfigMapKeyRef: &corev1.ConfigMapKeySelector{ - LocalObjectReference: corev1.LocalObjectReference{ - Name: "starboard-trivy-config", - }, - Key: "trivy.httpsProxy", - Optional: pointer.BoolPtr(true), - }, - }, - }, - { - Name: "NO_PROXY", - ValueFrom: &corev1.EnvVarSource{ - ConfigMapKeyRef: &corev1.ConfigMapKeySelector{ - LocalObjectReference: corev1.LocalObjectReference{ - Name: "starboard-trivy-config", - }, - Key: "trivy.noProxy", - Optional: pointer.BoolPtr(true), - }, - }, - }, - { - Name: "TRIVY_SEVERITY", - ValueFrom: &corev1.EnvVarSource{ - ConfigMapKeyRef: &corev1.ConfigMapKeySelector{ - LocalObjectReference: corev1.LocalObjectReference{ - Name: "starboard-trivy-config", - }, - Key: "trivy.severity", - Optional: pointer.BoolPtr(true), - }, - }, - }, - { - Name: "TRIVY_IGNORE_UNFIXED", - ValueFrom: &corev1.EnvVarSource{ - ConfigMapKeyRef: &corev1.ConfigMapKeySelector{ - LocalObjectReference: corev1.LocalObjectReference{ - Name: "starboard-trivy-config", - }, - Key: "trivy.ignoreUnfixed", - Optional: pointer.BoolPtr(true), - }, - }, - }, - - { - Name: "TRIVY_SKIP_FILES", - ValueFrom: &corev1.EnvVarSource{ - ConfigMapKeyRef: &corev1.ConfigMapKeySelector{ - LocalObjectReference: corev1.LocalObjectReference{ - Name: "starboard-trivy-config", - }, - Key: "trivy.skipFiles", - Optional: pointer.BoolPtr(true), - }, - }, - }, - { - Name: "TRIVY_SKIP_DIRS", - ValueFrom: &corev1.EnvVarSource{ - ConfigMapKeyRef: &corev1.ConfigMapKeySelector{ - LocalObjectReference: corev1.LocalObjectReference{ - Name: "starboard-trivy-config", - }, - Key: "trivy.skipDirs", - Optional: pointer.BoolPtr(true), - }, - }, - }, - { - Name: "TRIVY_TOKEN_HEADER", - ValueFrom: &corev1.EnvVarSource{ - ConfigMapKeyRef: &corev1.ConfigMapKeySelector{ - LocalObjectReference: corev1.LocalObjectReference{ - Name: "starboard-trivy-config", - }, - Key: "trivy.serverTokenHeader", - Optional: pointer.BoolPtr(true), - }, - }, - }, - { - Name: "TRIVY_TOKEN", - ValueFrom: &corev1.EnvVarSource{ - SecretKeyRef: &corev1.SecretKeySelector{ - LocalObjectReference: corev1.LocalObjectReference{ - Name: "starboard-trivy-config", - }, - Key: "trivy.serverToken", - Optional: pointer.BoolPtr(true), - }, - }, - }, - { - Name: "TRIVY_CUSTOM_HEADERS", - ValueFrom: &corev1.EnvVarSource{ - SecretKeyRef: &corev1.SecretKeySelector{ - LocalObjectReference: corev1.LocalObjectReference{ - Name: "starboard-trivy-config", - }, - Key: "trivy.serverCustomHeaders", - Optional: pointer.BoolPtr(true), - }, - }, - }, - { - Name: "TRIVY_IGNOREFILE", - Value: "/etc/trivy/.trivyignore", - }, - }, - Command: []string{ - "trivy", - }, - Args: []string{ - "--quiet", - "client", - "--format", - "json", - "--remote", - "http://trivy.trivy:4954", - "nginx:1.16", - }, - Resources: corev1.ResourceRequirements{ - Requests: corev1.ResourceList{ - corev1.ResourceCPU: resource.MustParse("100m"), - corev1.ResourceMemory: resource.MustParse("100M"), - }, - Limits: corev1.ResourceList{ - corev1.ResourceCPU: resource.MustParse("500m"), - corev1.ResourceMemory: resource.MustParse("500M"), - }, - }, - VolumeMounts: []corev1.VolumeMount{ - { - Name: "ignorefile", - MountPath: "/etc/trivy/.trivyignore", - SubPath: ".trivyignore", - }, - }, - }, - }, - }, - }, - { - name: "Trivy fs scan command in Standalone mode", - config: map[string]string{ - "trivy.imageRef": "docker.io/aquasec/trivy:0.25.2", - "trivy.mode": string(trivy.Standalone), - "trivy.command": string(trivy.Filesystem), - "trivy.dbRepository": defaultUpdateURL, - "trivy.resources.requests.cpu": "100m", - "trivy.resources.requests.memory": "100M", - "trivy.resources.limits.cpu": "500m", - "trivy.resources.limits.memory": "500M", - }, - workloadSpec: &corev1.Pod{ - TypeMeta: metav1.TypeMeta{ - Kind: "Pod", - APIVersion: "v1", - }, - ObjectMeta: metav1.ObjectMeta{ - Name: "nginx", - Namespace: "prod-ns", - }, - Spec: corev1.PodSpec{ - Containers: []corev1.Container{ - { - Name: "nginx", - Image: "nginx:1.9.1", - }, - }, - NodeName: "kind-control-pane", - }}, - expectedJobSpec: corev1.PodSpec{ - Affinity: starboard.LinuxNodeAffinity(), - RestartPolicy: corev1.RestartPolicyNever, - ServiceAccountName: "starboard-sa", - AutomountServiceAccountToken: pointer.BoolPtr(false), - Volumes: []corev1.Volume{ - { - Name: trivy.FsSharedVolumeName, - VolumeSource: corev1.VolumeSource{ - EmptyDir: &corev1.EmptyDirVolumeSource{ - Medium: corev1.StorageMediumDefault, - }, - }, - }, - }, - InitContainers: []corev1.Container{ - { - Name: "00000000-0000-0000-0000-000000000001", - Image: "docker.io/aquasec/trivy:0.25.2", - ImagePullPolicy: corev1.PullIfNotPresent, - TerminationMessagePolicy: corev1.TerminationMessageFallbackToLogsOnError, - Command: []string{ - "cp", - "-v", - "/usr/local/bin/trivy", - trivy.SharedVolumeLocationOfTrivy, - }, - Resources: corev1.ResourceRequirements{ - Requests: corev1.ResourceList{ - corev1.ResourceCPU: resource.MustParse("100m"), - corev1.ResourceMemory: resource.MustParse("100M"), - }, - Limits: corev1.ResourceList{ - corev1.ResourceCPU: resource.MustParse("500m"), - corev1.ResourceMemory: resource.MustParse("500M"), - }, - }, - VolumeMounts: []corev1.VolumeMount{ - { - Name: trivy.FsSharedVolumeName, - ReadOnly: false, - MountPath: "/var/starboard", - }, - }, - }, - { - Name: "00000000-0000-0000-0000-000000000002", - Image: "docker.io/aquasec/trivy:0.25.2", - ImagePullPolicy: corev1.PullIfNotPresent, - TerminationMessagePolicy: corev1.TerminationMessageFallbackToLogsOnError, - Env: []corev1.EnvVar{ - { - Name: "HTTP_PROXY", - ValueFrom: &corev1.EnvVarSource{ - ConfigMapKeyRef: &corev1.ConfigMapKeySelector{ - LocalObjectReference: corev1.LocalObjectReference{ - Name: "starboard-trivy-config", - }, - Key: "trivy.httpProxy", - Optional: pointer.BoolPtr(true), - }, - }, - }, - { - Name: "HTTPS_PROXY", - ValueFrom: &corev1.EnvVarSource{ - ConfigMapKeyRef: &corev1.ConfigMapKeySelector{ - LocalObjectReference: corev1.LocalObjectReference{ - Name: "starboard-trivy-config", - }, - Key: "trivy.httpsProxy", - Optional: pointer.BoolPtr(true), - }, - }, - }, - { - Name: "NO_PROXY", - ValueFrom: &corev1.EnvVarSource{ - ConfigMapKeyRef: &corev1.ConfigMapKeySelector{ - LocalObjectReference: corev1.LocalObjectReference{ - Name: "starboard-trivy-config", - }, - Key: "trivy.noProxy", - Optional: pointer.BoolPtr(true), - }, - }, - }, - - { - Name: "GITHUB_TOKEN", - ValueFrom: &corev1.EnvVarSource{ - SecretKeyRef: &corev1.SecretKeySelector{ - LocalObjectReference: corev1.LocalObjectReference{ - Name: "starboard-trivy-config", - }, - Key: "trivy.githubToken", - Optional: pointer.BoolPtr(true), - }, - }, - }, - }, - Command: []string{ - "trivy", - }, - Args: []string{ - "--download-db-only", - "--cache-dir", - "/var/starboard/trivy-db", - "--db-repository", defaultUpdateURL, - }, - Resources: corev1.ResourceRequirements{ - Requests: corev1.ResourceList{ - corev1.ResourceCPU: resource.MustParse("100m"), - corev1.ResourceMemory: resource.MustParse("100M"), - }, - Limits: corev1.ResourceList{ - corev1.ResourceCPU: resource.MustParse("500m"), - corev1.ResourceMemory: resource.MustParse("500M"), - }, - }, - VolumeMounts: []corev1.VolumeMount{ - { - Name: trivy.FsSharedVolumeName, - ReadOnly: false, - MountPath: "/var/starboard", - }, - }, - }, - }, - Containers: []corev1.Container{ - { - Name: "nginx", - Image: "nginx:1.9.1", - ImagePullPolicy: corev1.PullNever, - TerminationMessagePolicy: corev1.TerminationMessageFallbackToLogsOnError, - Env: []corev1.EnvVar{ - { - Name: "TRIVY_SEVERITY", - ValueFrom: &corev1.EnvVarSource{ - ConfigMapKeyRef: &corev1.ConfigMapKeySelector{ - LocalObjectReference: corev1.LocalObjectReference{ - Name: "starboard-trivy-config", - }, - Key: "trivy.severity", - Optional: pointer.BoolPtr(true), - }, - }, - }, - { - Name: "TRIVY_SKIP_FILES", - ValueFrom: &corev1.EnvVarSource{ - ConfigMapKeyRef: &corev1.ConfigMapKeySelector{ - LocalObjectReference: corev1.LocalObjectReference{ - Name: "starboard-trivy-config", - }, - Key: "trivy.skipFiles", - Optional: pointer.BoolPtr(true), - }, - }, - }, - { - Name: "TRIVY_SKIP_DIRS", - ValueFrom: &corev1.EnvVarSource{ - ConfigMapKeyRef: &corev1.ConfigMapKeySelector{ - LocalObjectReference: corev1.LocalObjectReference{ - Name: "starboard-trivy-config", - }, - Key: "trivy.skipDirs", - Optional: pointer.BoolPtr(true), - }, - }, - }, - { - Name: "HTTP_PROXY", - ValueFrom: &corev1.EnvVarSource{ - ConfigMapKeyRef: &corev1.ConfigMapKeySelector{ - LocalObjectReference: corev1.LocalObjectReference{ - Name: "starboard-trivy-config", - }, - Key: "trivy.httpProxy", - Optional: pointer.BoolPtr(true), - }, - }, - }, - { - Name: "HTTPS_PROXY", - ValueFrom: &corev1.EnvVarSource{ - ConfigMapKeyRef: &corev1.ConfigMapKeySelector{ - LocalObjectReference: corev1.LocalObjectReference{ - Name: "starboard-trivy-config", - }, - Key: "trivy.httpsProxy", - Optional: pointer.BoolPtr(true), - }, - }, - }, - { - Name: "NO_PROXY", - ValueFrom: &corev1.EnvVarSource{ - ConfigMapKeyRef: &corev1.ConfigMapKeySelector{ - LocalObjectReference: corev1.LocalObjectReference{ - Name: "starboard-trivy-config", - }, - Key: "trivy.noProxy", - Optional: pointer.BoolPtr(true), - }, - }, - }, - }, - Command: []string{ - trivy.SharedVolumeLocationOfTrivy, - }, - Args: []string{ - "--skip-update", - "--cache-dir", - "/var/starboard/trivy-db", - "--quiet", - "fs", - "--format", - "json", - "/", - }, - Resources: corev1.ResourceRequirements{ - Requests: corev1.ResourceList{ - corev1.ResourceCPU: resource.MustParse("100m"), - corev1.ResourceMemory: resource.MustParse("100M"), - }, - Limits: corev1.ResourceList{ - corev1.ResourceCPU: resource.MustParse("500m"), - corev1.ResourceMemory: resource.MustParse("500M"), - }, - }, - VolumeMounts: []corev1.VolumeMount{ - { - Name: trivy.FsSharedVolumeName, - ReadOnly: false, - MountPath: "/var/starboard", - }, - }, - SecurityContext: &corev1.SecurityContext{ - Privileged: pointer.BoolPtr(false), - AllowPrivilegeEscalation: pointer.BoolPtr(false), - Capabilities: &corev1.Capabilities{ - Drop: []corev1.Capability{"all"}, - }, - ReadOnlyRootFilesystem: pointer.BoolPtr(true), - RunAsUser: pointer.Int64(0), - }, - }, - }, - SecurityContext: &corev1.PodSecurityContext{}, - NodeName: "kind-control-pane", - }, - }, - } - - for _, tc := range testCases { - t.Run(tc.name, func(t *testing.T) { - fakeclient := fake.NewClientBuilder().WithObjects( - &corev1.ConfigMap{ - ObjectMeta: metav1.ObjectMeta{ - Name: "starboard-trivy-config", - Namespace: "starboard-ns", - }, - Data: tc.config, + Data: tc.Config, }, ).Build() pluginContext := starboard.NewPluginContext(). @@ -2913,329 +480,13 @@ CVE-2019-1543`, WithNamespace("starboard-ns"). WithServiceAccountName("starboard-sa"). WithClient(fakeclient). - Get() - instance := trivy.NewPlugin(fixedClock, ext.NewSimpleIDGenerator(), fakeclient) - jobSpec, secrets, err := instance.GetScanJobSpec(pluginContext, tc.workloadSpec, nil) - require.NoError(t, err) - assert.Empty(t, secrets) - assert.Equal(t, tc.expectedJobSpec, jobSpec) - }) - } - - testCases = []struct { - name string - config map[string]string - workloadSpec client.Object - expectedSecrets []corev1.Secret - expectedJobSpec corev1.PodSpec - }{{ - name: "Trivy fs scan command in Standalone mode", - config: map[string]string{ - "trivy.imageRef": "docker.io/aquasec/trivy:0.22.0", - "trivy.mode": string(trivy.Standalone), - "trivy.command": string(trivy.Filesystem), - "trivy.dbRepository": defaultUpdateURL, - "trivy.resources.requests.cpu": "100m", - "trivy.resources.requests.memory": "100M", - "trivy.resources.limits.cpu": "500m", - "trivy.resources.limits.memory": "500M", - }, - workloadSpec: &corev1.Pod{ - TypeMeta: metav1.TypeMeta{ - Kind: "Pod", - APIVersion: "v1", - }, - ObjectMeta: metav1.ObjectMeta{ - Name: "nginx", - Namespace: "prod-ns", - }, - Spec: corev1.PodSpec{ - Containers: []corev1.Container{ - { - Name: "nginx", - Image: "nginx:1.9.1", - }, - }, - NodeName: "kind-control-pane", - ServiceAccountName: "nginx-sa", - }}, - expectedJobSpec: corev1.PodSpec{ - Affinity: starboard.LinuxNodeAffinity(), - RestartPolicy: corev1.RestartPolicyNever, - ServiceAccountName: "starboard-sa", - AutomountServiceAccountToken: pointer.BoolPtr(false), - Volumes: []corev1.Volume{ - { - Name: trivy.FsSharedVolumeName, - VolumeSource: corev1.VolumeSource{ - EmptyDir: &corev1.EmptyDirVolumeSource{ - Medium: corev1.StorageMediumDefault, - }, - }, - }, - }, - InitContainers: []corev1.Container{ - { - Name: "00000000-0000-0000-0000-000000000001", - Image: "docker.io/aquasec/trivy:0.22.0", - ImagePullPolicy: corev1.PullIfNotPresent, - TerminationMessagePolicy: corev1.TerminationMessageFallbackToLogsOnError, - Command: []string{ - "cp", - "-v", - "/usr/local/bin/trivy", - trivy.SharedVolumeLocationOfTrivy, - }, - Resources: corev1.ResourceRequirements{ - Requests: corev1.ResourceList{ - corev1.ResourceCPU: resource.MustParse("100m"), - corev1.ResourceMemory: resource.MustParse("100M"), - }, - Limits: corev1.ResourceList{ - corev1.ResourceCPU: resource.MustParse("500m"), - corev1.ResourceMemory: resource.MustParse("500M"), - }, - }, - VolumeMounts: []corev1.VolumeMount{ - { - Name: trivy.FsSharedVolumeName, - ReadOnly: false, - MountPath: "/var/starboard", - }, - }, - }, - { - Name: "00000000-0000-0000-0000-000000000002", - Image: "docker.io/aquasec/trivy:0.22.0", - ImagePullPolicy: corev1.PullIfNotPresent, - TerminationMessagePolicy: corev1.TerminationMessageFallbackToLogsOnError, - Env: []corev1.EnvVar{ - { - Name: "HTTP_PROXY", - ValueFrom: &corev1.EnvVarSource{ - ConfigMapKeyRef: &corev1.ConfigMapKeySelector{ - LocalObjectReference: corev1.LocalObjectReference{ - Name: "starboard-trivy-config", - }, - Key: "trivy.httpProxy", - Optional: pointer.BoolPtr(true), - }, - }, - }, - { - Name: "HTTPS_PROXY", - ValueFrom: &corev1.EnvVarSource{ - ConfigMapKeyRef: &corev1.ConfigMapKeySelector{ - LocalObjectReference: corev1.LocalObjectReference{ - Name: "starboard-trivy-config", - }, - Key: "trivy.httpsProxy", - Optional: pointer.BoolPtr(true), - }, - }, - }, - { - Name: "NO_PROXY", - ValueFrom: &corev1.EnvVarSource{ - ConfigMapKeyRef: &corev1.ConfigMapKeySelector{ - LocalObjectReference: corev1.LocalObjectReference{ - Name: "starboard-trivy-config", - }, - Key: "trivy.noProxy", - Optional: pointer.BoolPtr(true), - }, - }, - }, - - { - Name: "GITHUB_TOKEN", - ValueFrom: &corev1.EnvVarSource{ - SecretKeyRef: &corev1.SecretKeySelector{ - LocalObjectReference: corev1.LocalObjectReference{ - Name: "starboard-trivy-config", - }, - Key: "trivy.githubToken", - Optional: pointer.BoolPtr(true), - }, - }, - }, - }, - Command: []string{ - "trivy", - }, - Args: []string{ - "--download-db-only", - "--cache-dir", - "/var/starboard/trivy-db", - "--db-repository", defaultUpdateURL, - }, - Resources: corev1.ResourceRequirements{ - Requests: corev1.ResourceList{ - corev1.ResourceCPU: resource.MustParse("100m"), - corev1.ResourceMemory: resource.MustParse("100M"), - }, - Limits: corev1.ResourceList{ - corev1.ResourceCPU: resource.MustParse("500m"), - corev1.ResourceMemory: resource.MustParse("500M"), - }, - }, - VolumeMounts: []corev1.VolumeMount{ - { - Name: trivy.FsSharedVolumeName, - ReadOnly: false, - MountPath: "/var/starboard", - }, - }, - }, - }, - Containers: []corev1.Container{ - { - Name: "nginx", - Image: "nginx:1.9.1", - ImagePullPolicy: corev1.PullIfNotPresent, - TerminationMessagePolicy: corev1.TerminationMessageFallbackToLogsOnError, - Env: []corev1.EnvVar{ - { - Name: "TRIVY_SEVERITY", - ValueFrom: &corev1.EnvVarSource{ - ConfigMapKeyRef: &corev1.ConfigMapKeySelector{ - LocalObjectReference: corev1.LocalObjectReference{ - Name: "starboard-trivy-config", - }, - Key: "trivy.severity", - Optional: pointer.BoolPtr(true), - }, - }, - }, - { - Name: "TRIVY_SKIP_FILES", - ValueFrom: &corev1.EnvVarSource{ - ConfigMapKeyRef: &corev1.ConfigMapKeySelector{ - LocalObjectReference: corev1.LocalObjectReference{ - Name: "starboard-trivy-config", - }, - Key: "trivy.skipFiles", - Optional: pointer.BoolPtr(true), - }, - }, - }, - { - Name: "TRIVY_SKIP_DIRS", - ValueFrom: &corev1.EnvVarSource{ - ConfigMapKeyRef: &corev1.ConfigMapKeySelector{ - LocalObjectReference: corev1.LocalObjectReference{ - Name: "starboard-trivy-config", - }, - Key: "trivy.skipDirs", - Optional: pointer.BoolPtr(true), - }, - }, - }, - { - Name: "HTTP_PROXY", - ValueFrom: &corev1.EnvVarSource{ - ConfigMapKeyRef: &corev1.ConfigMapKeySelector{ - LocalObjectReference: corev1.LocalObjectReference{ - Name: "starboard-trivy-config", - }, - Key: "trivy.httpProxy", - Optional: pointer.BoolPtr(true), - }, - }, - }, - { - Name: "HTTPS_PROXY", - ValueFrom: &corev1.EnvVarSource{ - ConfigMapKeyRef: &corev1.ConfigMapKeySelector{ - LocalObjectReference: corev1.LocalObjectReference{ - Name: "starboard-trivy-config", - }, - Key: "trivy.httpsProxy", - Optional: pointer.BoolPtr(true), - }, - }, - }, - { - Name: "NO_PROXY", - ValueFrom: &corev1.EnvVarSource{ - ConfigMapKeyRef: &corev1.ConfigMapKeySelector{ - LocalObjectReference: corev1.LocalObjectReference{ - Name: "starboard-trivy-config", - }, - Key: "trivy.noProxy", - Optional: pointer.BoolPtr(true), - }, - }, - }, - }, - Command: []string{ - trivy.SharedVolumeLocationOfTrivy, - }, - Args: []string{ - "--skip-update", - "--cache-dir", - "/var/starboard/trivy-db", - "--quiet", - "fs", - "--format", - "json", - "/", - }, - Resources: corev1.ResourceRequirements{ - Requests: corev1.ResourceList{ - corev1.ResourceCPU: resource.MustParse("100m"), - corev1.ResourceMemory: resource.MustParse("100M"), - }, - Limits: corev1.ResourceList{ - corev1.ResourceCPU: resource.MustParse("500m"), - corev1.ResourceMemory: resource.MustParse("500M"), - }, - }, - VolumeMounts: []corev1.VolumeMount{ - { - Name: trivy.FsSharedVolumeName, - ReadOnly: false, - MountPath: "/var/starboard", - }, - }, - SecurityContext: &corev1.SecurityContext{ - Privileged: pointer.BoolPtr(false), - AllowPrivilegeEscalation: pointer.BoolPtr(false), - Capabilities: &corev1.Capabilities{ - Drop: []corev1.Capability{"all"}, - }, - ReadOnlyRootFilesystem: pointer.BoolPtr(true), - RunAsUser: pointer.Int64(0), - }, - }, - }, - SecurityContext: &corev1.PodSecurityContext{}, - }, - }} - // Test cases when starboard is enabled with option to run job in the namespace of workload - for _, tc := range testCases { - t.Run(tc.name, func(t *testing.T) { - fakeclient := fake.NewClientBuilder().WithObjects( - &corev1.ConfigMap{ - ObjectMeta: metav1.ObjectMeta{ - Name: "starboard-trivy-config", - Namespace: "starboard-ns", - }, - Data: tc.config, - }, - ).Build() - pluginContext := starboard.NewPluginContext(). - WithName(trivy.Plugin). - WithNamespace("starboard-ns"). - WithServiceAccountName("starboard-sa"). - WithClient(fakeclient). WithStarboardConfig(map[string]string{starboard.KeyVulnerabilityScansInSameNamespace: "true"}). Get() - instance := trivy.NewPlugin(fixedClock, ext.NewSimpleIDGenerator(), fakeclient) - jobSpec, secrets, err := instance.GetScanJobSpec(pluginContext, tc.workloadSpec, nil) + instance := grype.NewPlugin(fixedClock, ext.NewSimpleIDGenerator(), fakeclient) + jobSpec, secrets, err := instance.GetScanJobSpec(pluginContext, tc.WorkloadSpec, nil) require.NoError(t, err) assert.Empty(t, secrets) - assert.Equal(t, tc.expectedJobSpec, jobSpec) + assert.Equal(t, tc.ExpectedJobSpec, jobSpec) }) } } From 98eb81b12600290216b24fdd0bb2e598cc4baf24 Mon Sep 17 00:00:00 2001 From: Kevin Seidel Date: Wed, 20 Apr 2022 12:07:49 +0200 Subject: [PATCH 11/21] added insecure config for grype --- pkg/plugin/grype/README.md | 15 ++++++++++ pkg/plugin/grype/plugin.go | 52 ++++++++++++++++++++++++++------- pkg/plugin/grype/plugin_test.go | 42 ++++++++++++++++++++++++-- 3 files changed, 96 insertions(+), 13 deletions(-) create mode 100644 pkg/plugin/grype/README.md diff --git a/pkg/plugin/grype/README.md b/pkg/plugin/grype/README.md new file mode 100644 index 000000000..357802d4d --- /dev/null +++ b/pkg/plugin/grype/README.md @@ -0,0 +1,15 @@ +# grype plugin for Starboard + +This plugin is derived from the Trivy plugin. + +* [grype](https://github.com/anchore/grype) +* [grype config](https://github.com/anchore/grype#configuration) + +# Notes on configuration +Most of the settings correspond directly to the settings mentioned in grype documentation. +There are two settings that are specific to this plugin: + +key | description +--- | --- +`grype.insecureRegistryPrefixes` | comma separated list of prefixes of registries where TLS verification will be skipped +`grype.nonSSLRegistyPrefixes` | comma separated list of prefixes of registries that use http diff --git a/pkg/plugin/grype/plugin.go b/pkg/plugin/grype/plugin.go index 0953b697b..294f3c688 100644 --- a/pkg/plugin/grype/plugin.go +++ b/pkg/plugin/grype/plugin.go @@ -27,17 +27,19 @@ const ( ) const ( - keyGrypeImageRef = "grype.imageRef" - keyGrypeScheme = "grype.scheme" - keyGrypePath = "grype.path" - keyGrypeOnlyFixed = "grype.onlyFixed" - keyGrypeExcludePaths = "grype.exclude" - keyGrypeHTTPProxy = "grype.httpProxy" - keyGrypeHTTPSProxy = "grype.httpsProxy" - keyGrypeNoProxy = "grype.noProxy" - keyGrypeUpdateURL = "grype.updateURL" - keyGrypeAddMissingCPEs = "grype.addMissingCPEs" - keyGrypeRegAuthority = "grype.regAuthority" + keyGrypeImageRef = "grype.imageRef" + keyGrypeScheme = "grype.scheme" + keyGrypePath = "grype.path" + keyGrypeOnlyFixed = "grype.onlyFixed" + keyGrypeExcludePaths = "grype.exclude" + keyGrypeHTTPProxy = "grype.httpProxy" + keyGrypeHTTPSProxy = "grype.httpsProxy" + keyGrypeNoProxy = "grype.noProxy" + keyGrypeUpdateURL = "grype.updateURL" + keyGrypeAddMissingCPEs = "grype.addMissingCPEs" + keyGrypeRegAuthority = "grype.regAuthority" + keyGrypeInsecureRegistryPrefixes = "grype.insecureRegistryPrefixes" + keyGrypeNonSSLRegistryPrefixes = "grype.nonSSLRegistyPrefixes" keyResourcesRequestsCPU = "grype.resources.requests.cpu" keyResourcesRequestsMemory = "grype.resources.requests.memory" @@ -336,6 +338,34 @@ func (p *plugin) getPodSpec(ctx starboard.PluginContext, config Config, workload }, ) + //check if we have to set insecure settings + if val, ok := config.Data[keyGrypeInsecureRegistryPrefixes]; ok { + for _, prefix := range strings.Split(val, ",") { + if strings.HasPrefix(c.Image, strings.Trim(prefix, " ")) { + env = append(env, + corev1.EnvVar{ + Name: "GRYPE_REGISTRY_INSECURE_SKIP_TLS_VERIFY", + Value: "true", + }, + ) + break + } + } + } + if val, ok := config.Data[keyGrypeNonSSLRegistryPrefixes]; ok { + for _, prefix := range strings.Split(val, ",") { + if strings.HasPrefix(c.Image, strings.Trim(prefix, " ")) { + env = append(env, + corev1.EnvVar{ + Name: "GRYPE_REGISTRY_INSECURE_USE_HTTP", + Value: "true", + }, + ) + break + } + } + } + if _, ok := credentials[c.Name]; ok && secret != nil { registryUsernameKey := fmt.Sprintf("%s.username", c.Name) registryPasswordKey := fmt.Sprintf("%s.password", c.Name) diff --git a/pkg/plugin/grype/plugin_test.go b/pkg/plugin/grype/plugin_test.go index f5b4d2331..e4b7eb8d0 100644 --- a/pkg/plugin/grype/plugin_test.go +++ b/pkg/plugin/grype/plugin_test.go @@ -19,7 +19,6 @@ import ( metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/types" "k8s.io/utils/pointer" - "sigs.k8s.io/controller-runtime/pkg/client" "sigs.k8s.io/controller-runtime/pkg/client/fake" ) @@ -207,7 +206,7 @@ type JobSpecTestCase struct { Config map[string]string ExpectedConfigName string - WorkloadSpec client.Object + WorkloadSpec *corev1.Pod // ExpectedSecrets []corev1.Secret ExpectedJobSpec corev1.PodSpec @@ -463,6 +462,45 @@ func TestPlugin_GetScanJobSpec(t *testing.T) { scheme.ExpectedJobSpec.Containers[0].Args[0] = "podman:nginx:1.16" testCases = append(testCases, scheme) + //test insecure settings + insecure := NewJobSpecTestCase("Should set insecure env var") + insecure.Config["grype.insecureRegistryPrefixes"] = "foo, bar, ba" + insecure.WorkloadSpec.Spec.Containers[0].Image = "bar/nginx:1.16" + insecure.ExpectedJobSpec.Containers[0].Args[0] = "bar/nginx:1.16" + insecure.ExpectedJobSpec.Containers[0].Env = append(insecure.ExpectedJobSpec.Containers[0].Env, + corev1.EnvVar{ + Name: "GRYPE_REGISTRY_INSECURE_SKIP_TLS_VERIFY", + Value: "true", + }, + ) + testCases = append(testCases, insecure) + + //not insecure + notInsecure := NewJobSpecTestCase("Should NOT set insecure env var") + notInsecure.Config["grype.insecureRegistryPrefixes"] = "notFoo, bar" + notInsecure.WorkloadSpec.Spec.Containers[0].Image = "foobar/nginx:1.16" + notInsecure.ExpectedJobSpec.Containers[0].Args[0] = "foobar/nginx:1.16" + testCases = append(testCases, notInsecure) + + //no SSL + noSSL := NewJobSpecTestCase("Should set no SSL env var") + noSSL.Config["grype.nonSSLRegistyPrefixes"] = "foo, http://bar, http://ba" + noSSL.WorkloadSpec.Spec.Containers[0].Image = "http://bar/nginx:1.16" + noSSL.ExpectedJobSpec.Containers[0].Args[0] = "http://bar/nginx:1.16" + noSSL.ExpectedJobSpec.Containers[0].Env = append(noSSL.ExpectedJobSpec.Containers[0].Env, + corev1.EnvVar{ + Name: "GRYPE_REGISTRY_INSECURE_USE_HTTP", + Value: "true", + }, + ) + testCases = append(testCases, noSSL) + + noNoSSL := NewJobSpecTestCase("Should NOT set no SSL env var") + noNoSSL.Config["grype.nonSSLRegistyPrefixes"] = "foo, http://notBar" + noNoSSL.WorkloadSpec.Spec.Containers[0].Image = "http://bar/nginx:1.16" + noNoSSL.ExpectedJobSpec.Containers[0].Args[0] = "http://bar/nginx:1.16" + testCases = append(testCases, noNoSSL) + // Test cases when starboard is enabled with option to run job in the namespace of workload for _, tc := range testCases { t.Run(tc.Name, func(t *testing.T) { From c9f370ffb658de57625441788a8d67af7303c3e1 Mon Sep 17 00:00:00 2001 From: Kevin Seidel Date: Wed, 20 Apr 2022 12:26:12 +0200 Subject: [PATCH 12/21] clarification in readme --- pkg/plugin/grype/README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/pkg/plugin/grype/README.md b/pkg/plugin/grype/README.md index 357802d4d..b8d7c8139 100644 --- a/pkg/plugin/grype/README.md +++ b/pkg/plugin/grype/README.md @@ -7,6 +7,7 @@ This plugin is derived from the Trivy plugin. # Notes on configuration Most of the settings correspond directly to the settings mentioned in grype documentation. +In order to set optional parameters, e.g. `grype.onlyFixed`, they have to be set to `true` There are two settings that are specific to this plugin: key | description From 7bc70b2afc84ebd2fb825dab7f5cc008410a2667 Mon Sep 17 00:00:00 2001 From: Kevin Seidel Date: Wed, 20 Apr 2022 12:39:02 +0200 Subject: [PATCH 13/21] cleanup - some warnings fixed --- pkg/plugin/grype/plugin.go | 19 ++----------------- pkg/plugin/grype/plugin_test.go | 5 ----- 2 files changed, 2 insertions(+), 22 deletions(-) diff --git a/pkg/plugin/grype/plugin.go b/pkg/plugin/grype/plugin.go index 294f3c688..aad6cc228 100644 --- a/pkg/plugin/grype/plugin.go +++ b/pkg/plugin/grype/plugin.go @@ -522,8 +522,9 @@ func (p *plugin) ParseVulnerabilityReportData(ctx starboard.PluginContext, image fixVersion = strings.TrimSuffix(fixVersion, ", ") var score *float64 = pointer.Float64Ptr(0) + vulRegex, _ := regexp.Compile(`3\..*`) for _, cvs := range vul.CVSs { - if matched, err := regexp.MatchString("3\\..*", cvs.Version); matched && err == nil { + if vulRegex.Match([]byte(cvs.Version)) { score = cvs.Metrics.BaseScore } } @@ -622,19 +623,3 @@ func (p *plugin) parseImageRef(imageRef string) (v1alpha1.Registry, v1alpha1.Art } return registry, artifact, nil } - -func constructEnvVarSourceFromConfigMap(envName, configName, configKey string) (res corev1.EnvVar) { - res = corev1.EnvVar{ - Name: envName, - ValueFrom: &corev1.EnvVarSource{ - ConfigMapKeyRef: &corev1.ConfigMapKeySelector{ - LocalObjectReference: corev1.LocalObjectReference{ - Name: configName, - }, - Key: configKey, - Optional: pointer.BoolPtr(true), - }, - }, - } - return -} diff --git a/pkg/plugin/grype/plugin_test.go b/pkg/plugin/grype/plugin_test.go index e4b7eb8d0..2c1fb63d5 100644 --- a/pkg/plugin/grype/plugin_test.go +++ b/pkg/plugin/grype/plugin_test.go @@ -194,11 +194,6 @@ const ( keyGrypeUpdateURL = "grype.updateURL" keyGrypeAddMissingCPEs = "grype.addMissingCPEs" keyGrypeRegAuthority = "grype.regAuthority" - - keyResourcesRequestsCPU = "grype.resources.requests.cpu" - keyResourcesRequestsMemory = "grype.resources.requests.memory" - keyResourcesLimitsCPU = "grype.resources.limits.cpu" - keyResourcesLimitsMemory = "grype.resources.limits.memory" ) type JobSpecTestCase struct { From ce0e9130e4963d1a3a4371fae5128c0ce621dec7 Mon Sep 17 00:00:00 2001 From: Kevin Seidel Date: Wed, 20 Apr 2022 14:12:20 +0200 Subject: [PATCH 14/21] feat: read secret registry auth info from secret --- pkg/plugin/grype/plugin.go | 41 +++++++++++++++++++++++++++++++++++++- 1 file changed, 40 insertions(+), 1 deletion(-) diff --git a/pkg/plugin/grype/plugin.go b/pkg/plugin/grype/plugin.go index aad6cc228..6eeb95ba0 100644 --- a/pkg/plugin/grype/plugin.go +++ b/pkg/plugin/grype/plugin.go @@ -38,6 +38,9 @@ const ( keyGrypeUpdateURL = "grype.updateURL" keyGrypeAddMissingCPEs = "grype.addMissingCPEs" keyGrypeRegAuthority = "grype.regAuthority" + keyGrypeRegUsername = "grype.regUsername" + keyGrypeRegPassword = "grype.regPassword" + keyGrypeRegToken = "grype.regToken" keyGrypeInsecureRegistryPrefixes = "grype.insecureRegistryPrefixes" keyGrypeNonSSLRegistryPrefixes = "grype.nonSSLRegistyPrefixes" @@ -311,7 +314,7 @@ func (p *plugin) getPodSpec(ctx starboard.PluginContext, config Config, workload corev1.EnvVar{ Name: "GRYPE_REGISTRY_AUTH_AUTHORITY", ValueFrom: &corev1.EnvVarSource{ - ConfigMapKeyRef: &corev1.ConfigMapKeySelector{ + SecretKeyRef: &corev1.SecretKeySelector{ LocalObjectReference: corev1.LocalObjectReference{ Name: grypeConfigName, }, @@ -320,6 +323,42 @@ func (p *plugin) getPodSpec(ctx starboard.PluginContext, config Config, workload }, }, }, + corev1.EnvVar{ + Name: "GRYPE_REGISTRY_AUTH_USERNAME", + ValueFrom: &corev1.EnvVarSource{ + SecretKeyRef: &corev1.SecretKeySelector{ + LocalObjectReference: corev1.LocalObjectReference{ + Name: grypeConfigName, + }, + Key: keyGrypeRegUsername, + Optional: pointer.BoolPtr(true), + }, + }, + }, + corev1.EnvVar{ + Name: "GRYPE_REGISTRY_AUTH_PASSWORD", + ValueFrom: &corev1.EnvVarSource{ + SecretKeyRef: &corev1.SecretKeySelector{ + LocalObjectReference: corev1.LocalObjectReference{ + Name: grypeConfigName, + }, + Key: keyGrypeRegPassword, + Optional: pointer.BoolPtr(true), + }, + }, + }, + corev1.EnvVar{ + Name: "GRYPE_REGISTRY_AUTH_TOKEN", + ValueFrom: &corev1.EnvVarSource{ + SecretKeyRef: &corev1.SecretKeySelector{ + LocalObjectReference: corev1.LocalObjectReference{ + Name: grypeConfigName, + }, + Key: keyGrypeRegToken, + Optional: pointer.BoolPtr(true), + }, + }, + }, corev1.EnvVar{ Name: "GRYPE_EXCLUDE", ValueFrom: &corev1.EnvVarSource{ From 06cf6768e448bf28d2308e729b2106618f8b3a3e Mon Sep 17 00:00:00 2001 From: Kevin Seidel Date: Wed, 20 Apr 2022 14:16:14 +0200 Subject: [PATCH 15/21] feat: added config for grype to helm deployment --- deploy/helm/templates/config.yaml | 92 +++++++++++++++++++++++++++++++ 1 file changed, 92 insertions(+) diff --git a/deploy/helm/templates/config.yaml b/deploy/helm/templates/config.yaml index 39ffe6507..034485daa 100644 --- a/deploy/helm/templates/config.yaml +++ b/deploy/helm/templates/config.yaml @@ -117,6 +117,98 @@ data: {{- end }} {{- end }} {{- end }} + + +{{- if eq .Values.starboard.vulnerabilityReportsPlugin "Grype" }} +{{- with .Values.grype}} +{{- if .createConfig }} +--- +apiVersion: v1 +kind: ConfigMap +metadata: + name: starboard-grype-config + labels: + {{- include "starboard-operator.labels" $ | nindent 4 }} +data: + grype.imageRef: {{ required ".Values.grype.imageRef is required" .imageRef | quote }} + {{- if .scheme }} + grype.scheme: {{ .scheme | quote }} + {{- end }} + {{- if .path }} + grype.path: {{ .path | quote }} + {{- end }} + {{- if .onlyFixed }} + grype.onlyFixed: {{ .onlyFixed | quote }} + {{- end }} + {{- if .excludePaths }} + grype.excludePaths: {{ .excludePaths | quote }} + {{- end }} + {{- if .httpProxy }} + grype.httpProxy: {{ .httpProxy | quote }} + {{- end }} + {{- if .httpsProxy }} + grype.httpsProxy: {{ .httpsProxy | quote }} + {{- end }} + {{- if .noProxy }} + grype.noProxy: {{ .noProxy | quote }} + {{- end }} + {{- if .updateURL }} + grype.updateURL: {{ .updateURL | quote }} + {{- end }} + {{- if .addMissingCPEs }} + grype.addMissingCPEs: {{ .addMissingCPEs | quote }} + {{- end }} + {{- if .updateURL }} + grype.updateURL: {{ .updateURL | quote }} + {{- end }} + {{- if .insecureRegistryPrefixes }} + grype.insecureRegistryPrefixes: {{ .insecureRegistryPrefixes | quote }} + {{- end }} + {{- if .nonSSLRegistriesPrefixes }} + grype.nonSSLRegistriesPrefixes: {{ .nonSSLRegistriesPrefixes | quote }} + {{- end }} + {{- with .resources }} + {{- with .requests }} + {{- if .cpu }} + grype.resources.requests.cpu: {{ .cpu }} + {{- end }} + {{- if hasKey . "memory" }} + grype.resources.requests.memory: {{ .memory }} + {{- end }} + {{- end }} + {{- with .limits }} + {{- if .cpu }} + grype.resources.limits.cpu: {{ .cpu }} + {{- end }} + {{- if .memory }} + grype.resources.limits.memory: {{ .memory }} + {{- end }} + {{- end }} + {{- end }} +--- +apiVersion: v1 +kind: Secret +metadata: + name: starboard-grype-config + labels: + {{- include "starboard-operator.labels" $ | nindent 4 }} +data: + {{- if .regAuthority }} + grype.regAuthority: {{ .regAuthority | b64enc | quote }} + {{- end }} + {{- if .regUsername }} + grype.regUsername: {{ .regUsername | b64enc | quote }} + {{- end }} + {{- if .regPassword }} + grype.regPassword: {{ .regPassword | b64enc | quote }} + {{- end }} + {{- if .regToken }} + grype.regToken: {{ .regToken | b64enc | quote }} + {{- end }} +{{- end }} +{{- end }} +{{- end }} + {{- if eq .Values.starboard.configAuditReportsPlugin "Conftest" }} {{- with .Values.conftest }} {{- if .createConfig }} From 6bd4a5f9285f882a4c64fce46938c71828c4d6b8 Mon Sep 17 00:00:00 2001 From: Kevin Seidel Date: Wed, 20 Apr 2022 14:27:51 +0200 Subject: [PATCH 16/21] fix: added missing grype entries helm chart values file --- .gitignore | 2 ++ deploy/helm/values.yaml | 12 ++++++++++++ 2 files changed, 14 insertions(+) diff --git a/.gitignore b/.gitignore index 8a04933d4..49ab338d0 100644 --- a/.gitignore +++ b/.gitignore @@ -17,5 +17,7 @@ site/ ## helm chart output/* .cr-release-packages/* +deploy/helm/*.tgz + ## idea files *.iml \ No newline at end of file diff --git a/deploy/helm/values.yaml b/deploy/helm/values.yaml index 819ae65e7..a638526d6 100644 --- a/deploy/helm/values.yaml +++ b/deploy/helm/values.yaml @@ -179,6 +179,18 @@ trivy: dbRepository: "ghcr.io/aquasecurity/trivy-db" +grype: + createConfig: true + imageRef: "anchore/grype:0.34.7" + updateURL: "https://toolbox-data.anchore.io/grype/databases/listing.json" + resources: + requests: + cpu: 100m + memory: 100M + limits: + cpu: 500m + memory: 500M + compliance: # failEntriesLimit the flag to limit the number of fail entries per control check in the cluster compliance detail report failEntriesLimit: 10 From dbff4c77766ff7d80fee620bc0e22aa794fa187b Mon Sep 17 00:00:00 2001 From: Kevin Seidel Date: Wed, 20 Apr 2022 17:08:44 +0200 Subject: [PATCH 17/21] fix: fixed plugin factory to accept grype as plugin --- pkg/plugin/factory.go | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/pkg/plugin/factory.go b/pkg/plugin/factory.go index ca8c8e9b1..ddb92ad22 100644 --- a/pkg/plugin/factory.go +++ b/pkg/plugin/factory.go @@ -7,6 +7,7 @@ import ( "github.com/aquasecurity/starboard/pkg/ext" "github.com/aquasecurity/starboard/pkg/plugin/aqua" "github.com/aquasecurity/starboard/pkg/plugin/conftest" + "github.com/aquasecurity/starboard/pkg/plugin/grype" "github.com/aquasecurity/starboard/pkg/plugin/polaris" "github.com/aquasecurity/starboard/pkg/plugin/trivy" "github.com/aquasecurity/starboard/pkg/starboard" @@ -16,6 +17,7 @@ import ( const ( Trivy starboard.Scanner = "Trivy" + Grype starboard.Scanner = "Grype" Aqua starboard.Scanner = "Aqua" Polaris starboard.Scanner = "Polaris" Conftest starboard.Scanner = "Conftest" @@ -81,6 +83,8 @@ func (r *Resolver) GetVulnerabilityPlugin() (vulnerabilityreport.Plugin, starboa switch scanner { case Trivy: return trivy.NewPlugin(ext.NewSystemClock(), ext.NewGoogleUUIDGenerator(), r.client), pluginContext, nil + case Grype: + return grype.NewPlugin(ext.NewSystemClock(), ext.NewGoogleUUIDGenerator(), r.client), pluginContext, nil case Aqua: return aqua.NewPlugin(ext.NewGoogleUUIDGenerator(), r.buildInfo), pluginContext, nil } From 186e22b19586a2e90be855255b122e963a445cb3 Mon Sep 17 00:00:00 2001 From: Kevin Seidel Date: Thu, 21 Apr 2022 10:43:02 +0200 Subject: [PATCH 18/21] remove command from podspec --- pkg/plugin/grype/plugin.go | 16 ++++++---------- 1 file changed, 6 insertions(+), 10 deletions(-) diff --git a/pkg/plugin/grype/plugin.go b/pkg/plugin/grype/plugin.go index 6eeb95ba0..6609ff502 100644 --- a/pkg/plugin/grype/plugin.go +++ b/pkg/plugin/grype/plugin.go @@ -132,7 +132,7 @@ func NewPlugin(clock ext.Clock, idGenerator ext.IDGenerator, client client.Clien func (p *plugin) Init(ctx starboard.PluginContext) error { return ctx.EnsureConfig(starboard.PluginConfig{ Data: map[string]string{ - keyGrypeImageRef: "anchore/grype:0.34.7", + keyGrypeImageRef: "anchore/grype:v0.35.0", keyGrypeUpdateURL: defaultUpdateURL, keyResourcesRequestsCPU: "100m", @@ -287,9 +287,6 @@ func (p *plugin) getPodSpec(ctx starboard.PluginContext, config Config, workload ImagePullPolicy: corev1.PullIfNotPresent, TerminationMessagePolicy: corev1.TerminationMessageFallbackToLogsOnError, Env: commonEnv, - Command: []string{ - "grype", - }, Args: []string{ "db", "update", @@ -463,12 +460,9 @@ func (p *plugin) getPodSpec(ctx starboard.PluginContext, config Config, workload ImagePullPolicy: corev1.PullIfNotPresent, TerminationMessagePolicy: corev1.TerminationMessageFallbackToLogsOnError, Env: env, - Command: []string{ - "grype", - }, - Args: args, - Resources: requirements, - VolumeMounts: volumeMounts, + Args: args, + Resources: requirements, + VolumeMounts: volumeMounts, SecurityContext: &corev1.SecurityContext{ Privileged: pointer.BoolPtr(false), AllowPrivilegeEscalation: pointer.BoolPtr(false), @@ -480,6 +474,8 @@ func (p *plugin) getPodSpec(ctx starboard.PluginContext, config Config, workload }) } + fmt.Println(initContainer) + return corev1.PodSpec{ Affinity: starboard.LinuxNodeAffinity(), RestartPolicy: corev1.RestartPolicyNever, From e5bd347cf905007e1aee3048a5874bc44ab46755 Mon Sep 17 00:00:00 2001 From: Kevin Seidel Date: Fri, 22 Apr 2022 11:41:45 +0200 Subject: [PATCH 19/21] fix: tidy up env vars and added docu for grype helm vars --- deploy/helm/templates/config.yaml | 6 +-- deploy/helm/values.yaml | 40 +++++++++++++++- pkg/plugin/grype/plugin.go | 63 ++++++------------------- pkg/plugin/grype/plugin_test.go | 77 ++++++++++++++++++++++++------- 4 files changed, 116 insertions(+), 70 deletions(-) diff --git a/deploy/helm/templates/config.yaml b/deploy/helm/templates/config.yaml index 034485daa..4811d0696 100644 --- a/deploy/helm/templates/config.yaml +++ b/deploy/helm/templates/config.yaml @@ -134,12 +134,12 @@ data: {{- if .scheme }} grype.scheme: {{ .scheme | quote }} {{- end }} - {{- if .path }} - grype.path: {{ .path | quote }} - {{- end }} {{- if .onlyFixed }} grype.onlyFixed: {{ .onlyFixed | quote }} {{- end }} + {{- if .platform }} + grype.platform: {{ .platform | quote }} + {{- end }} {{- if .excludePaths }} grype.excludePaths: {{ .excludePaths | quote }} {{- end }} diff --git a/deploy/helm/values.yaml b/deploy/helm/values.yaml index a638526d6..04245e376 100644 --- a/deploy/helm/values.yaml +++ b/deploy/helm/values.yaml @@ -73,7 +73,7 @@ service: prometheus.io/path: /metrics starboard: - # vulnerabilityReportsPlugin the name of the plugin that generates vulnerability reports. Either `Trivy` or `Aqua`. + # vulnerabilityReportsPlugin the name of the plugin that generates vulnerability reports. Either `Trivy`, `Aqua` or `Grype`. vulnerabilityReportsPlugin: "Trivy" # configAuditReportsPlugin the name of the plugin that generates config audit reports. Either `Polaris` or `Conftest`. configAuditReportsPlugin: "Polaris" @@ -180,9 +180,45 @@ trivy: dbRepository: "ghcr.io/aquasecurity/trivy-db" grype: + # create Grype config (true|false) createConfig: true - imageRef: "anchore/grype:0.34.7" + + # image ref to be used in the grype scan jobs + imageRef: "anchore/grype:v0.35.0" + + # updateURL, where the vulnerability databases should be pulled from, refer to grype documentation for this updateURL: "https://toolbox-data.anchore.io/grype/databases/listing.json" + + # if a specific scheme is required for scanning, provide it here + # scheme: + + # if you need to specify the platform of the scanned images + # platform + + # glob pattern of paths to exlude + # excludePaths: + + # proxy settings + # httpProx: + # httpsPorxy: + # noProxy: + + # add CPEs, if they are missing + # addMissingCPEs: "true" + + # INSECURE: comma separated list of prefixes for registries where tls verification should be skipped + # insecureRegistryPrefixes: + + # INSECURE: comaa separated list of prefixes for registries that support HTTP only + # nonSSLRegistriesPrefixes: + + # secret values for registry authentication + # regAuthority: *** + # regUsername: *** + # regPassword: *** + # regToken: *** + + # resources resources: requests: cpu: 100m diff --git a/pkg/plugin/grype/plugin.go b/pkg/plugin/grype/plugin.go index 6609ff502..c34d8374a 100644 --- a/pkg/plugin/grype/plugin.go +++ b/pkg/plugin/grype/plugin.go @@ -29,8 +29,8 @@ const ( const ( keyGrypeImageRef = "grype.imageRef" keyGrypeScheme = "grype.scheme" - keyGrypePath = "grype.path" keyGrypeOnlyFixed = "grype.onlyFixed" + keyGrypePlatform = "grype.platform" keyGrypeExcludePaths = "grype.exclude" keyGrypeHTTPProxy = "grype.httpProxy" keyGrypeHTTPSProxy = "grype.httpsProxy" @@ -168,7 +168,7 @@ const ( tmpVolumeName = "tmp" ignoreFileVolumeName = "ignorefile" FsSharedVolumeName = "starboard" - grypeDBLocation = "/tmp/grypedb" + grypeDBLocation = "/tmp" ) // There is an init container to cache the Grype DB, which will be stored in an @@ -356,6 +356,18 @@ func (p *plugin) getPodSpec(ctx starboard.PluginContext, config Config, workload }, }, }, + corev1.EnvVar{ + Name: "GRYPE_PLATFORM", + ValueFrom: &corev1.EnvVarSource{ + ConfigMapKeyRef: &corev1.ConfigMapKeySelector{ + LocalObjectReference: corev1.LocalObjectReference{ + Name: grypeConfigName, + }, + Key: keyGrypePlatform, + Optional: pointer.BoolPtr(true), + }, + }, + }, corev1.EnvVar{ Name: "GRYPE_EXCLUDE", ValueFrom: &corev1.EnvVarSource{ @@ -429,19 +441,8 @@ func (p *plugin) getPodSpec(ctx starboard.PluginContext, config Config, workload }) } - // env, err = p.appendGrypeInsecureEnv(config, c.Image, env) - // if err != nil { - // return corev1.PodSpec{}, nil, err - // } - - // env, err = p.appendGrypeNonSSLEnv(config, c.Image, env) - // if err != nil { - // return corev1.PodSpec{}, nil, err - // } - args := []string{ scanImage, - "--skip-update", "--quiet", "--output", "json", @@ -474,8 +475,6 @@ func (p *plugin) getPodSpec(ctx starboard.PluginContext, config Config, workload }) } - fmt.Println(initContainer) - return corev1.PodSpec{ Affinity: starboard.LinuxNodeAffinity(), RestartPolicy: corev1.RestartPolicyNever, @@ -499,40 +498,6 @@ func (p *plugin) appendGrypeOptionalArg(config Config, args []string, arg string } } -// func (p *plugin) appendGrypeInsecureEnv(config Config, image string, env []corev1.EnvVar) ([]corev1.EnvVar, error) { -// ref, err := name.ParseReference(image) -// if err != nil { -// return nil, err -// } - -// insecureRegistries := config.GetInsecureRegistries() -// if insecureRegistries[ref.Context().RegistryStr()] { -// env = append(env, corev1.EnvVar{ -// Name: "GRYPE_REGISTRY_INSECURE_SKIP_TLS_VERIFY", -// Value: "true", -// }) -// } - -// return env, nil -// } - -// func (p *plugin) appendGrypeNonSSLEnv(config Config, image string, env []corev1.EnvVar) ([]corev1.EnvVar, error) { -// ref, err := name.ParseReference(image) -// if err != nil { -// return nil, err -// } - -// nonSSLRegistries := config.GetNonSSLRegistries() -// if nonSSLRegistries[ref.Context().RegistryStr()] { -// env = append(env, corev1.EnvVar{ -// Name: "GRYPE_REGISTRY_INSECURE_USE_HTTP", -// Value: "true", -// }) -// } - -// return env, nil -// } - func (p *plugin) ParseVulnerabilityReportData(ctx starboard.PluginContext, imageRef string, logsReader io.ReadCloser) (v1alpha1.VulnerabilityReportData, error) { config, err := p.newConfigFrom(ctx) if err != nil { diff --git a/pkg/plugin/grype/plugin_test.go b/pkg/plugin/grype/plugin_test.go index 2c1fb63d5..14d4960a0 100644 --- a/pkg/plugin/grype/plugin_test.go +++ b/pkg/plugin/grype/plugin_test.go @@ -45,10 +45,10 @@ func TestConfig_GetImageRef(t *testing.T) { name: "Should return image reference from config data", configData: grype.Config{PluginConfig: starboard.PluginConfig{ Data: map[string]string{ - "grype.imageRef": "anchore/grype:0.34.7", + "grype.imageRef": "anchore/grype:v0.35.0", }, }}, - expectedImageRef: "anchore/grype:0.34.7", + expectedImageRef: "anchore/grype:v0.35.0", }, } @@ -169,7 +169,7 @@ func TestPlugin_Init(t *testing.T) { ResourceVersion: "1", }, Data: map[string]string{ - "grype.imageRef": "anchore/grype:0.34.7", + "grype.imageRef": "anchore/grype:v0.35.0", "grype.updateURL": defaultUpdateURL, "grype.resources.requests.cpu": "100m", @@ -182,11 +182,12 @@ func TestPlugin_Init(t *testing.T) { } const ( - grypeDBLocation = "/tmp/grypedb" + grypeDBLocation = "/tmp" keyGrypeImageRef = "grype.imageRef" keyGrypeScheme = "grype.scheme" keyGrypePath = "grype.path" keyGrypeOnlyFixed = "grype.onlyFixed" + keyGrypePlatform = "grype.platform" keyGrypeExcludePaths = "grype.exclude" keyGrypeHTTPProxy = "grype.httpProxy" keyGrypeHTTPSProxy = "grype.httpsProxy" @@ -194,6 +195,9 @@ const ( keyGrypeUpdateURL = "grype.updateURL" keyGrypeAddMissingCPEs = "grype.addMissingCPEs" keyGrypeRegAuthority = "grype.regAuthority" + keyGrypeRegUsername = "grype.regUsername" + keyGrypeRegPassword = "grype.regPassword" + keyGrypeRegToken = "grype.regToken" ) type JobSpecTestCase struct { @@ -327,9 +331,6 @@ func NewJobSpecTestCase(Name string) JobSpecTestCase { ImagePullPolicy: corev1.PullIfNotPresent, TerminationMessagePolicy: corev1.TerminationMessageFallbackToLogsOnError, Env: commonEnv, - Command: []string{ - "grype", - }, Args: []string{ "db", "update", @@ -359,7 +360,7 @@ func NewJobSpecTestCase(Name string) JobSpecTestCase { corev1.EnvVar{ Name: "GRYPE_REGISTRY_AUTH_AUTHORITY", ValueFrom: &corev1.EnvVarSource{ - ConfigMapKeyRef: &corev1.ConfigMapKeySelector{ + SecretKeyRef: &corev1.SecretKeySelector{ LocalObjectReference: corev1.LocalObjectReference{ Name: j.ExpectedConfigName, }, @@ -368,6 +369,54 @@ func NewJobSpecTestCase(Name string) JobSpecTestCase { }, }, }, + corev1.EnvVar{ + Name: "GRYPE_REGISTRY_AUTH_USERNAME", + ValueFrom: &corev1.EnvVarSource{ + SecretKeyRef: &corev1.SecretKeySelector{ + LocalObjectReference: corev1.LocalObjectReference{ + Name: j.ExpectedConfigName, + }, + Key: keyGrypeRegUsername, + Optional: pointer.BoolPtr(true), + }, + }, + }, + corev1.EnvVar{ + Name: "GRYPE_REGISTRY_AUTH_PASSWORD", + ValueFrom: &corev1.EnvVarSource{ + SecretKeyRef: &corev1.SecretKeySelector{ + LocalObjectReference: corev1.LocalObjectReference{ + Name: j.ExpectedConfigName, + }, + Key: keyGrypeRegPassword, + Optional: pointer.BoolPtr(true), + }, + }, + }, + corev1.EnvVar{ + Name: "GRYPE_REGISTRY_AUTH_TOKEN", + ValueFrom: &corev1.EnvVarSource{ + SecretKeyRef: &corev1.SecretKeySelector{ + LocalObjectReference: corev1.LocalObjectReference{ + Name: j.ExpectedConfigName, + }, + Key: keyGrypeRegToken, + Optional: pointer.BoolPtr(true), + }, + }, + }, + corev1.EnvVar{ + Name: "GRYPE_PLATFORM", + ValueFrom: &corev1.EnvVarSource{ + ConfigMapKeyRef: &corev1.ConfigMapKeySelector{ + LocalObjectReference: corev1.LocalObjectReference{ + Name: j.ExpectedConfigName, + }, + Key: keyGrypePlatform, + Optional: pointer.BoolPtr(true), + }, + }, + }, corev1.EnvVar{ Name: "GRYPE_EXCLUDE", ValueFrom: &corev1.EnvVarSource{ @@ -385,12 +434,8 @@ func NewJobSpecTestCase(Name string) JobSpecTestCase { Value: "false", }, ), - Command: []string{ - "grype", - }, Args: []string{ "nginx:1.16", - "--skip-update", "--quiet", "--output", "json", @@ -760,7 +805,7 @@ var ( }, "descriptor": { "name": "grype", - "version": "0.34.7", + "version": "v0.35.0", "configuration": { "configPath": "", "output": "json", @@ -817,7 +862,7 @@ var ( Scanner: v1alpha1.Scanner{ Name: "Grype", Vendor: "Anchore Inc.", - Version: "0.34.7", + Version: "v0.35.0", }, Registry: v1alpha1.Registry{ Server: "index.docker.io", @@ -912,7 +957,7 @@ func TestPlugin_ParseVulnerabilityReportData(t *testing.T) { Namespace: "starboard-ns", }, Data: map[string]string{ - "grype.imageRef": "anchore/grype:0.34.7", + "grype.imageRef": "anchore/grype:v0.35.0", }, } @@ -940,7 +985,7 @@ func TestPlugin_ParseVulnerabilityReportData(t *testing.T) { Scanner: v1alpha1.Scanner{ Name: "Grype", Vendor: "Anchore Inc.", - Version: "0.34.7", + Version: "v0.35.0", }, Registry: v1alpha1.Registry{ Server: "core.harbor.domain", From 2c880a3d0ab60ffd0c92b7ff6ee9b1af16472af6 Mon Sep 17 00:00:00 2001 From: Kevin Seidel Date: Fri, 22 Apr 2022 12:07:45 +0200 Subject: [PATCH 20/21] fix: added docs for grype --- docs/vulnerability-scanning/grype.md | 33 ++++++++++++++++++++++++++++ docs/vulnerability-scanning/index.md | 1 + 2 files changed, 34 insertions(+) create mode 100644 docs/vulnerability-scanning/grype.md diff --git a/docs/vulnerability-scanning/grype.md b/docs/vulnerability-scanning/grype.md new file mode 100644 index 000000000..b89b48adb --- /dev/null +++ b/docs/vulnerability-scanning/grype.md @@ -0,0 +1,33 @@ +# Grype + +The Grype plugin can be enabled by setting `vulnerabilityReports.scanner` to `Grype`, there is no further setup necessary. However there are additional settings available. A look at the [grype GitHub repo](https://github.com/anchore/grype) will help. + +## Configuration + +### Options + +key | default value | description +--- | --- | --- +`grype.imageRef` | `anchore/grype:v0.35.0` | the grype image used for scanning +`grype.scheme` | none | if there is a specific scheme required for scanning, set it here, e.g. `podman` +`grype.onlyFixed` | `false` | set this to true to ignore vulnerabilities without fix +`grype.platform` | none | if you have to set a specific platform for scanning, e.g. `s390x` +`grype.exclude` | none | glob pattern of paths to exclude from scanning +`grype.httpProxy` | none | http proxy +`grype.httpsProxy` | none | https proxy +`grype.noProxy` | none | targets where no proxy should be used +`grype.updateURL` | `https://toolbox-data.anchore.io/grype/databases/listing.json` | location of the vulnerability DB config, see grype docu for that +`grype.addMissingCPEs` | `false` | add CPEs if they are missing +`grype.insecureRegistryPrefixes` | none | comma separated list of registry prefixes where tls verification should be skipped +`grype.nonSSLRegistryPrefixes` | none | comma separated list of registry prefixes that support HTTP only +`grype.resources.requests.cpu` | `100m` | resource setting for the scan jobs +`grype.resources.requests.memory` | `100M` | resource setting for the scan jobs +`grype.resources.limits.cpu` | `500m` | resource setting for the scan jobs +`grype.resources.limits.memory` | `500M` | resource setting for the scan jobs + +### Secrets +The following secret keys can be provided for authentication against a protected registry: +* `grype.regUsername` +* `grype.regPassword` +* `grype.regToken` + diff --git a/docs/vulnerability-scanning/index.md b/docs/vulnerability-scanning/index.md index 4d710b9ab..4e82639d2 100644 --- a/docs/vulnerability-scanning/index.md +++ b/docs/vulnerability-scanning/index.md @@ -20,6 +20,7 @@ Starboard may scan Kubernetes workloads that run images from [Private Registries [VulnerabilityReport]: ./../crds/vulnerability-report.md [Trivy]: ./trivy.md +[Grpye]: ./grype.md [Aqua Enterprise]: ./aqua-enterprise.md [Private Registries]: ./private-registries.md [Managed Registries]: ./managed-registries.md From ee52fca9c43396702b52dea3922503853c8aca4a Mon Sep 17 00:00:00 2001 From: Kevin Seidel Date: Tue, 26 Apr 2022 12:37:10 +0200 Subject: [PATCH 21/21] fix: empty CVSs issue #1 --- pkg/plugin/grype/model.go | 5 +- pkg/plugin/grype/plugin.go | 13 ++ pkg/plugin/grype/plugin_test.go | 241 ++++++++++++++++++++++++++++++++ 3 files changed, 257 insertions(+), 2 deletions(-) diff --git a/pkg/plugin/grype/model.go b/pkg/plugin/grype/model.go index d2f7d17fa..6787b23db 100644 --- a/pkg/plugin/grype/model.go +++ b/pkg/plugin/grype/model.go @@ -7,8 +7,9 @@ type ScanReport struct { } type Match struct { - Vulnerability Vulnerability `json:"vulnerability"` - Artifact Artifact `json:"artifact"` + Vulnerability Vulnerability `json:"vulnerability"` + Artifact Artifact `json:"artifact"` + RelatedVulnerabilities []Vulnerability `json:"relatedVulnerabilities"` } type Vulnerability struct { diff --git a/pkg/plugin/grype/plugin.go b/pkg/plugin/grype/plugin.go index c34d8374a..7d329eb37 100644 --- a/pkg/plugin/grype/plugin.go +++ b/pkg/plugin/grype/plugin.go @@ -512,8 +512,21 @@ func (p *plugin) ParseVulnerabilityReportData(ctx starboard.PluginContext, image for _, match := range report.Matches { vul := match.Vulnerability + relVuls := match.RelatedVulnerabilities artifact := match.Artifact + // take behavior of related vulnerabilities into account: https://github.com/anchore/grype/issues/734 + // this avoids having empty CVSs in root vulnerability + if len(vul.CVSs) <= 0 && len(relVuls) > 0 { + for _, relVul := range relVuls { + if relVul.Id == vul.Id { + vul.CVSs = relVul.CVSs + vul.URLs = relVul.URLs + vul.Description = relVul.Description + } + } + } + fixVersion := "" for _, version := range match.Vulnerability.Fix.Versions { fixVersion += version diff --git a/pkg/plugin/grype/plugin_test.go b/pkg/plugin/grype/plugin_test.go index 14d4960a0..231c1a443 100644 --- a/pkg/plugin/grype/plugin_test.go +++ b/pkg/plugin/grype/plugin_test.go @@ -948,6 +948,240 @@ var ( }, }, } + + sampleRelVulReportAsString = `{ + "matches": [ + { + "vulnerability": { + "id": "CVE-2021-28831", + "dataSource": "http://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2021-28831", + "namespace": "alpine:3.10", + "severity": "High", + "urls": [ + "http://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2021-28831" + ], + "cvss": [], + "fix": { + "versions": [ + "1.30.1-r5" + ], + "state": "fixed" + }, + "advisories": [] + }, + "relatedVulnerabilities": [ + { + "id": "CVE-2021-28831", + "dataSource": "https://nvd.nist.gov/vuln/detail/CVE-2021-28831", + "namespace": "nvd", + "severity": "High", + "urls": [ + "https://git.busybox.net/busybox/commit/?id=f25d254dfd4243698c31a4f3153d4ac72aa9e9bd", + "https://lists.fedoraproject.org/archives/list/package-announce@lists.fedoraproject.org/message/ZASBW7QRRLY5V2R44MQ4QQM4CZIDHM2U/", + "https://lists.fedoraproject.org/archives/list/package-announce@lists.fedoraproject.org/message/Z7ZIFKPRR32ZYA3WAA2NXFA3QHHOU6FJ/", + "https://lists.fedoraproject.org/archives/list/package-announce@lists.fedoraproject.org/message/3UDQGJRECXFS5EZVDH2OI45FMO436AC4/", + "https://lists.debian.org/debian-lts-announce/2021/04/msg00001.html", + "https://security.gentoo.org/glsa/202105-09" + ], + "description": "decompress_gunzip.c in BusyBox through 1.32.1 mishandles the error bit on the huft_build result pointer, with a resultant invalid free or segmentation fault, via malformed gzip data.", + "cvss": [ + { + "version": "2.0", + "vector": "AV:N/AC:L/Au:N/C:N/I:N/A:P", + "metrics": { + "baseScore": 5, + "exploitabilityScore": 10, + "impactScore": 2.9 + }, + "vendorMetadata": {} + }, + { + "version": "3.1", + "vector": "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H", + "metrics": { + "baseScore": 7.5, + "exploitabilityScore": 3.9, + "impactScore": 3.6 + }, + "vendorMetadata": {} + } + ] + } + ], + "matchDetails": [ + { + "type": "exact-direct-match", + "matcher": "apk-matcher", + "searchedBy": { + "distro": { + "type": "alpine", + "version": "3.10.7" + }, + "namespace": "alpine:3.10", + "package": { + "name": "busybox", + "version": "1.30.1-r4" + } + }, + "found": { + "versionConstraint": "< 1.30.1-r5 (apk)" + } + } + ], + "artifact": { + "name": "busybox", + "version": "1.30.1-r4", + "type": "apk", + "locations": [ + { + "path": "/lib/apk/db/installed", + "layerID": "sha256:3cb2494d9fa711bd96908ce5bcf4f7e82e26df6c9ddc28c3809f7e54905dc1f2" + } + ], + "language": "", + "licenses": [ + "GPL-2.0" + ], + "cpes": [ + "cpe:2.3:a:busybox:busybox:1.30.1-r4:*:*:*:*:*:*:*" + ], + "purl": "pkg:alpine/busybox@1.30.1-r4?arch=x86_64&upstream=busybox&distro=alpine-3.10.7", + "upstreams": [ + { + "name": "busybox" + } + ] + } + } + ], + "source": { + "type": "image", + "target": { + "userInput": "alpine:3.10.7", + "imageID": "sha256:d1bb6234ef26de7a1976176b36eb0f518b3d3d9e6a46e2da2ae6eb0b4d99d87d", + "manifestDigest": "sha256:668ff9bbf5a284e630019bd3dcb80340ac69e71c1b38182e95583022a00b067d", + "mediaType": "application/vnd.docker.distribution.manifest.v2+json", + "tags": [], + "imageSize": 5572037, + "layers": [ + { + "mediaType": "application/vnd.docker.image.rootfs.diff.tar.gzip", + "digest": "sha256:3cb2494d9fa711bd96908ce5bcf4f7e82e26df6c9ddc28c3809f7e54905dc1f2", + "size": 5572037 + } + ], + "manifest": "ewogICAic2NoZW1hVmVyc2lvbiI6IDIsCiAgICJtZWRpYVR5cGUiOiAiYXBwbGljYXRpb24vdm5kLmRvY2tlci5kaXN0cmlidXRpb24ubWFuaWZlc3QudjIranNvbiIsCiAgICJjb25maWciOiB7CiAgICAgICJtZWRpYVR5cGUiOiAiYXBwbGljYXRpb24vdm5kLmRvY2tlci5jb250YWluZXIuaW1hZ2UudjEranNvbiIsCiAgICAgICJzaXplIjogMTQ3MiwKICAgICAgImRpZ2VzdCI6ICJzaGEyNTY6ZDFiYjYyMzRlZjI2ZGU3YTE5NzYxNzZiMzZlYjBmNTE4YjNkM2Q5ZTZhNDZlMmRhMmFlNmViMGI0ZDk5ZDg3ZCIKICAgfSwKICAgImxheWVycyI6IFsKICAgICAgewogICAgICAgICAibWVkaWFUeXBlIjogImFwcGxpY2F0aW9uL3ZuZC5kb2NrZXIuaW1hZ2Uucm9vdGZzLmRpZmYudGFyLmd6aXAiLAogICAgICAgICAic2l6ZSI6IDI3OTcxNzksCiAgICAgICAgICJkaWdlc3QiOiAic2hhMjU2OmM4YmM2NmUyNjM2ZjgxMzNkZjQzNGUxYmNlNzQxYjZiN2ZiMjE1MTVlN2M4YTU1NGE4MDViNzNmNWZkYWUyZGUiCiAgICAgIH0KICAgXQp9", + "config": "eyJhcmNoaXRlY3R1cmUiOiJhbWQ2NCIsImNvbmZpZyI6eyJIb3N0bmFtZSI6IiIsIkRvbWFpbm5hbWUiOiIiLCJVc2VyIjoiIiwiQXR0YWNoU3RkaW4iOmZhbHNlLCJBdHRhY2hTdGRvdXQiOmZhbHNlLCJBdHRhY2hTdGRlcnIiOmZhbHNlLCJUdHkiOmZhbHNlLCJPcGVuU3RkaW4iOmZhbHNlLCJTdGRpbk9uY2UiOmZhbHNlLCJFbnYiOlsiUEFUSD0vdXNyL2xvY2FsL3NiaW46L3Vzci9sb2NhbC9iaW46L3Vzci9zYmluOi91c3IvYmluOi9zYmluOi9iaW4iXSwiQ21kIjpbIi9iaW4vc2giXSwiSW1hZ2UiOiJzaGEyNTY6YjNkODQ2YWM4ZWFhNGE1ZjQwZDcyOWZjODIzMWM2ZDY4ZDQ5MjNkZGM4NTM1MGQxYWIxMTQ3OWRlM2NhZDllZiIsIlZvbHVtZXMiOm51bGwsIldvcmtpbmdEaXIiOiIiLCJFbnRyeXBvaW50IjpudWxsLCJPbkJ1aWxkIjpudWxsLCJMYWJlbHMiOm51bGx9LCJjb250YWluZXIiOiI5OWI0ZDFjZDgzZTg1MTNhNThkMDU5YjdkMWU4N2Q0NDdiZWFlM2QyYmQ2ZGI3NmEwZTJiZjQ0YzJmNzQxYzZlIiwiY29udGFpbmVyX2NvbmZpZyI6eyJIb3N0bmFtZSI6Ijk5YjRkMWNkODNlOCIsIkRvbWFpbm5hbWUiOiIiLCJVc2VyIjoiIiwiQXR0YWNoU3RkaW4iOmZhbHNlLCJBdHRhY2hTdGRvdXQiOmZhbHNlLCJBdHRhY2hTdGRlcnIiOmZhbHNlLCJUdHkiOmZhbHNlLCJPcGVuU3RkaW4iOmZhbHNlLCJTdGRpbk9uY2UiOmZhbHNlLCJFbnYiOlsiUEFUSD0vdXNyL2xvY2FsL3NiaW46L3Vzci9sb2NhbC9iaW46L3Vzci9zYmluOi91c3IvYmluOi9zYmluOi9iaW4iXSwiQ21kIjpbIi9iaW4vc2giLCItYyIsIiMobm9wKSAiLCJDTUQgW1wiL2Jpbi9zaFwiXSJdLCJJbWFnZSI6InNoYTI1NjpiM2Q4NDZhYzhlYWE0YTVmNDBkNzI5ZmM4MjMxYzZkNjhkNDkyM2RkYzg1MzUwZDFhYjExNDc5ZGUzY2FkOWVmIiwiVm9sdW1lcyI6bnVsbCwiV29ya2luZ0RpciI6IiIsIkVudHJ5cG9pbnQiOm51bGwsIk9uQnVpbGQiOm51bGwsIkxhYmVscyI6e319LCJjcmVhdGVkIjoiMjAyMS0wMy0yNVQyMjoxOTo0OC44MDM1NDY3NTZaIiwiZG9ja2VyX3ZlcnNpb24iOiIxOS4wMy4xMiIsImhpc3RvcnkiOlt7ImNyZWF0ZWQiOiIyMDIxLTAzLTI1VDIyOjE5OjQ4LjYyNTI4NjE0MVoiLCJjcmVhdGVkX2J5IjoiL2Jpbi9zaCAtYyAjKG5vcCkgQUREIGZpbGU6NDE2ODU5NTBiNjA3ZmU5MGUwODg2Yzg1N2U0ZWZkYWZhYjFlNDNhMDlkZWYxNzRhNGVhOTdmOGVjNjI0MzcwYiBpbiAvICJ9LHsiY3JlYXRlZCI6IjIwMjEtMDMtMjVUMjI6MTk6NDguODAzNTQ2NzU2WiIsImNyZWF0ZWRfYnkiOiIvYmluL3NoIC1jICMobm9wKSAgQ01EIFtcIi9iaW4vc2hcIl0iLCJlbXB0eV9sYXllciI6dHJ1ZX1dLCJvcyI6ImxpbnV4Iiwicm9vdGZzIjp7InR5cGUiOiJsYXllcnMiLCJkaWZmX2lkcyI6WyJzaGEyNTY6M2NiMjQ5NGQ5ZmE3MTFiZDk2OTA4Y2U1YmNmNGY3ZTgyZTI2ZGY2YzlkZGMyOGMzODA5ZjdlNTQ5MDVkYzFmMiJdfX0=", + "repoDigests": [ + "index.docker.io/library/alpine@sha256:f2fa517acf6123318bc893c411f34570cea193367b33bd3be1d90c7fbefe72a5" + ], + "architecture": "", + "os": "" + } + }, + "distro": { + "name": "alpine", + "version": "3.10.7", + "idLike": [] + }, + "descriptor": { + "name": "grype", + "version": "0.34.7", + "configuration": { + "configPath": "", + "output": "json", + "file": "", + "distro": "", + "add-cpes-if-none": false, + "output-template-file": "", + "quiet": true, + "check-for-app-update": true, + "only-fixed": false, + "platform": "", + "search": { + "scope": "Squashed", + "unindexed-archives": false, + "indexed-archives": true + }, + "ignore": null, + "exclude": [], + "db": { + "cache-dir": "/.cache/grype/db", + "update-url": "https://toolbox-data.anchore.io/grype/databases/listing.json", + "ca-cert": "", + "auto-update": true, + "validate-by-hash-on-start": false + }, + "dev": { + "profile-cpu": false, + "profile-mem": false + }, + "fail-on-severity": "", + "registry": { + "insecure-skip-tls-verify": false, + "insecure-use-http": false, + "auth": [] + }, + "log": { + "structured": false, + "level": "", + "file": "" + } + }, + "db": { + "built": "2022-04-25T08:19:48Z", + "schemaVersion": 3, + "location": "/.cache/grype/db/3", + "checksum": "sha256:0780fcce7a17641f6486a7d1554161201793bf4621c1351848164a17478215eb", + "error": null + } + } + }` + + sampleRelVulReport = v1alpha1.VulnerabilityReportData{ + UpdateTimestamp: metav1.NewTime(fixedTime), + Scanner: v1alpha1.Scanner{ + Name: "Grype", + Vendor: "Anchore Inc.", + Version: "v0.35.0", + }, + Registry: v1alpha1.Registry{ + Server: "index.docker.io", + }, + Artifact: v1alpha1.Artifact{ + Repository: "library/alpine", + Tag: "3.10.7", + }, + Summary: v1alpha1.VulnerabilitySummary{ + CriticalCount: 0, + HighCount: 1, + MediumCount: 0, + LowCount: 0, + NoneCount: 0, + UnknownCount: 0, + }, + Vulnerabilities: []v1alpha1.Vulnerability{ + { + VulnerabilityID: "CVE-2021-28831", + Resource: "busybox", + InstalledVersion: "1.30.1-r4", + FixedVersion: "1.30.1-r5", + Severity: v1alpha1.SeverityHigh, + Title: "busybox", + Description: "decompress_gunzip.c in BusyBox through 1.32.1 mishandles the error bit on the huft_build result pointer, with a resultant invalid free or segmentation fault, via malformed gzip data.", + PrimaryLink: "http://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2021-28831", + Score: pointer.Float64(7.5), + Links: []string{ + "https://git.busybox.net/busybox/commit/?id=f25d254dfd4243698c31a4f3153d4ac72aa9e9bd", + "https://lists.fedoraproject.org/archives/list/package-announce@lists.fedoraproject.org/message/ZASBW7QRRLY5V2R44MQ4QQM4CZIDHM2U/", + "https://lists.fedoraproject.org/archives/list/package-announce@lists.fedoraproject.org/message/Z7ZIFKPRR32ZYA3WAA2NXFA3QHHOU6FJ/", + "https://lists.fedoraproject.org/archives/list/package-announce@lists.fedoraproject.org/message/3UDQGJRECXFS5EZVDH2OI45FMO436AC4/", + "https://lists.debian.org/debian-lts-announce/2021/04/msg00001.html", + "https://security.gentoo.org/glsa/202105-09", + }, + }, + }, + } ) func TestPlugin_ParseVulnerabilityReportData(t *testing.T) { @@ -975,6 +1209,13 @@ func TestPlugin_ParseVulnerabilityReportData(t *testing.T) { expectedError: nil, expectedReport: sampleReport, }, + { + name: "Should read info from related vulnerabilities", + imageRef: "alpine:3.10.7", + input: sampleRelVulReportAsString, + expectedError: nil, + expectedReport: sampleRelVulReport, + }, { name: "Should convert vulnerability report in JSON format when OS is not detected", imageRef: "core.harbor.domain/library/nginx@sha256:d20aa6d1cae56fd17cd458f4807e0de462caf2336f0b70b5eeb69fcaaf30dd9c",