Compare commits

...

45 Commits

Author SHA1 Message Date
Daniel Grunberger
b3f664822c fixes 2023-08-01 10:40:57 +03:00
Daniel Grunberger
e829af7205 changes 2023-07-31 21:15:23 +03:00
Daniel Grunberger
e4bafad67b fixes 2023-07-31 11:26:47 +03:00
Daniel Grunberger
dd8f624f0c changes 2023-07-31 09:46:38 +03:00
Daniel Grunberger
e794ab8b45 changes 2023-07-30 22:20:04 +03:00
Daniel Grunberger
05cb1abec5 Merge remote-tracking branch 'other/scan-workload' into new-output 2023-07-30 10:08:42 +03:00
Amir Malka
e972df933a removed designators (unused) field from PolicyIdentifier, and designators argument from GetResources function
Signed-off-by: Amir Malka <amirm@armosec.io>
2023-07-27 19:32:31 +03:00
Amir Malka
d740ba3ed2 remove namespace argument from pullSingleResource, using field selector instead
Signed-off-by: Amir Malka <amirm@armosec.io>
2023-07-27 18:25:37 +03:00
Daniel Grunberger
2121b20076 Merge remote-tracking branch 'other/scan-workload' into new-output 2023-07-27 15:32:33 +03:00
Daniel Grunberger
27482a9067 Merge remote-tracking branch 'other/scan-workload' into new-output 2023-07-27 15:31:59 +03:00
Amir Malka
5b61611789 fix GetWorkloadParentKind
Signed-off-by: Amir Malka <amirm@armosec.io>
2023-07-27 15:28:56 +03:00
Amir Malka
6cefd56559 add scanned workload reference to opasessionobj
Signed-off-by: Amir Malka <amirm@armosec.io>
2023-07-27 14:09:20 +03:00
Amir Malka
c98b696a9c more refactoring
Signed-off-by: Amir Malka <amirm@armosec.io>
2023-07-27 12:03:14 +03:00
Amir Malka
facbc4749a added unit tests
Signed-off-by: Amir Malka <amirm@armosec.io>
2023-07-26 10:18:04 +03:00
Daniel Grunberger
e57d65541c integrate img scan 2023-07-26 08:55:31 +03:00
Daniel Grunberger
6679ac54ea Merge branch 'feat-image-scan-svc' into new-output 2023-07-25 17:01:43 +03:00
Daniel Grunberger
dd3b5bf5cf Merge branch 'feat-image-scan-svc' of github.com:kubescape/kubescape into feat-image-scan-svc 2023-07-25 17:00:12 +03:00
Daniel Grunberger
811914a0ff more 2023-07-25 16:55:01 +03:00
Daniel Grunberger
6341947142 merge 2023-07-25 16:54:44 +03:00
Amir Malka
aab9cd5ff9 added unit tests
Signed-off-by: Amir Malka <amirm@armosec.io>
2023-07-25 15:24:09 +03:00
Amir Malka
0df62cb8d3 Merge branch 'master' of github.com:amirmalka/kubescape into scan-workload
Signed-off-by: Amir Malka <amirm@armosec.io>
2023-07-25 14:55:19 +03:00
Vlad Klokun
3c320035ad chore(imagescan): add dependencies to httphandler
Signed-off-by: Vlad Klokun <vklokun@protonmail.ch>
2023-07-25 12:50:15 +03:00
Vlad Klokun
3177ab47f0 chore(imagescan): include dependencies
This commit adds the dependencies necessary for image scanning.

Signed-off-by: Vlad Klokun <vklokun@protonmail.ch>
2023-07-25 12:31:47 +03:00
Amir Malka
755d8c3bb0 Merge branch 'master' of github.com:amirmalka/kubescape into scan-workload
Signed-off-by: Amir Malka <amirm@armosec.io>
2023-07-25 11:54:34 +03:00
Vlad Klokun
e67fcae4aa feat(imagescan): add an image scanning command
This commit adds a CLI command and an associated package that scan
images for vulnerabilities.

Signed-off-by: Vlad Klokun <vklokun@protonmail.ch>

feat(imagescan): fail on exceeding the severity threshold

Signed-off-by: Vlad Klokun <vklokun@protonmail.ch>
2023-07-25 11:46:50 +03:00
Daniel Grunberger
03b69bfacc go mod 2023-07-25 11:39:44 +03:00
Daniel Grunberger
219b32d874 identifiers 2023-07-25 11:39:31 +03:00
Daniel Grunberger
315fe799e7 Merge branch 'master' into new-output 2023-07-25 11:11:14 +03:00
Daniel Grunberger
5c6e66d9b7 Merge branch 'scan-workload' of github.com:amirmalka/kubescape into new-output 2023-07-25 11:10:10 +03:00
Amir Malka
7f812f2c9a fix conflict
Signed-off-by: Amir Malka <amirm@armosec.io>
2023-07-20 20:30:25 +03:00
Amir Malka
eab88f0184 Merge branch 'scan-workload' of github.com:amirmalka/kubescape into scan-workload
Signed-off-by: Amir Malka <amirm@armosec.io>
2023-07-20 20:26:21 +03:00
Amir Malka
557c063f69 support single workload scan
Signed-off-by: Amir Malka <amirm@armosec.io>
2023-07-20 20:20:14 +03:00
Amir Malka
e1f7e06d45 Merge branch 'master' of github.com:armosec/kubescape into scan-workload 2023-07-20 19:39:17 +03:00
Daniel Grunberger
ab634debe4 add cmd
Signed-off-by: Daniel Grunberger <danielgrunberger@armosec.io>
2023-07-17 15:28:41 +03:00
Daniel Grunberger
ad0103c50a continue
Signed-off-by: Daniel Grunberger <danielgrunberger@armosec.io>
2023-07-17 14:19:23 +03:00
Daniel Grunberger
46e1cea203 Merge branch 'feat-image-scan-svc' into new-output
Signed-off-by: Daniel Grunberger <danielgrunberger@armosec.io>
2023-07-11 13:49:52 +03:00
Daniel Grunberger
57522160a0 touches
Signed-off-by: Daniel Grunberger <danielgrunberger@armosec.io>
2023-07-11 13:44:24 +03:00
Daniel Grunberger
4ecd366115 use iface
Signed-off-by: Daniel Grunberger <danielgrunberger@armosec.io>
2023-07-10 19:09:42 +03:00
Vlad Klokun
4253005485 wip: feat: use scanning service in CLI
Signed-off-by: Vlad Klokun <vklokun@protonmail.ch>
2023-07-10 15:48:29 +03:00
Vlad Klokun
dcfcc1dbea wip: adjust image scanning service
Signed-off-by: Vlad Klokun <vklokun@protonmail.ch>
2023-07-10 15:48:29 +03:00
Vlad Klokun
5eb9c29905 chore: include dependencies
Signed-off-by: Vlad Klokun <vklokun@protonmail.ch>
2023-07-10 15:48:29 +03:00
Vlad Klokun
614c5ea17f wip: feat: add image scanning service
Signed-off-by: Vlad Klokun <vklokun@protonmail.ch>
2023-07-10 15:48:23 +03:00
Vlad Klokun
6b643f36d8 wip: feat(cli): add an image scanning command
Add a CLI command that launches an image scan. Does not scan images yet.

Signed-off-by: Vlad Klokun <vklokun@protonmail.ch>
2023-07-10 15:24:17 +03:00
Daniel Grunberger
f54bfabacc factory
Signed-off-by: Daniel Grunberger <danielgrunberger@armosec.io>
2023-07-10 09:10:37 +03:00
Daniel Grunberger
fac2f7bec1 phase-1
Signed-off-by: Daniel Grunberger <danielgrunberger@armosec.io>
2023-07-06 08:54:01 +03:00
97 changed files with 7187 additions and 1138 deletions

View File

@@ -80,15 +80,18 @@ func getFrameworkCmd(ks meta.IKubescape, scanInfo *cautils.ScanInfo) *cobra.Comm
if len(args) == 0 { // scan all frameworks
scanInfo.ScanAll = true
scanInfo.SetScanType(cautils.ScanTypeFramework)
} else {
// Read frameworks from input args
frameworks = strings.Split(args[0], ",")
if cautils.StringInSlice(frameworks, "all") != cautils.ValueNotFound {
scanInfo.ScanAll = true
frameworks = getter.NativeFrameworks
}
if len(args) > 1 {
if len(args[1:]) == 0 || args[1] != "-" {
scanInfo.SetScanType(cautils.ScanTypeRepo)
scanInfo.InputPatterns = args[1:]
logger.L().Debug("List of input files", helpers.Interface("patterns", scanInfo.InputPatterns))
} else { // store stdin to file - do NOT move to separate function !!
@@ -102,11 +105,16 @@ func getFrameworkCmd(ks meta.IKubescape, scanInfo *cautils.ScanInfo) *cobra.Comm
return err
}
scanInfo.InputPatterns = []string{tempFile.Name()}
scanInfo.SetScanType(cautils.ScanTypeFramework)
}
}
}
scanInfo.FrameworkScan = true
if (scanInfo.IsNewOutputFormat) && (scanInfo.ScanType == cautils.ScanTypeRepo || scanInfo.ScanType == cautils.ScanTypeCluster) {
frameworks = append(frameworks, "clusterscan")
}
scanInfo.SetPolicyIdentifiers(frameworks, apisv1.KindFramework)
ctx := context.TODO()
@@ -118,7 +126,8 @@ func getFrameworkCmd(ks meta.IKubescape, scanInfo *cautils.ScanInfo) *cobra.Comm
if err = results.HandleResults(ctx); err != nil {
logger.L().Fatal(err.Error())
}
if !scanInfo.VerboseMode {
if !scanInfo.VerboseMode && scanInfo.ScanType == cautils.ScanTypeFramework {
logger.L().Info("Run with '--verbose'/'-v' flag for detailed resources view\n")
}
if results.GetRiskScore() > float32(scanInfo.FailThreshold) {

106
cmd/scan/image.go Normal file
View File

@@ -0,0 +1,106 @@
package scan
import (
"context"
"fmt"
logger "github.com/kubescape/go-logger"
"github.com/kubescape/go-logger/helpers"
"github.com/kubescape/kubescape/v2/core/cautils"
"github.com/kubescape/kubescape/v2/core/core"
"github.com/kubescape/kubescape/v2/core/meta"
"github.com/kubescape/kubescape/v2/core/pkg/resultshandling"
"github.com/kubescape/kubescape/v2/pkg/imagescan"
"github.com/spf13/cobra"
)
// TODO(vladklokun): document image scanning on the Kubescape Docs Hub?
var (
imageExample = fmt.Sprintf(`
# Scan the 'nginx' image
%[1]s scan image "nginx"
# Image scan documentation:
# https://hub.armosec.io/docs/images
`, cautils.ExecName())
)
// imageCmd represents the image command
func getImageCmd(ks meta.IKubescape, scanInfo *cautils.ScanInfo) *cobra.Command {
cmd := &cobra.Command{
Use: "image <IMAGE_NAME>",
Short: "Scans an image for vulnerabilities",
Example: imageExample,
Args: func(cmd *cobra.Command, args []string) error {
if len(args) != 1 {
return fmt.Errorf("The command takes exactly one image.")
}
return nil
},
RunE: func(cmd *cobra.Command, args []string) error {
if err := validateImageScanInfo(scanInfo); err != nil {
return err
}
failOnSeverity := imagescan.ParseSeverity(scanInfo.FailThresholdSeverity)
ctx := context.Background()
dbCfg, _ := imagescan.NewDefaultDBConfig()
svc := imagescan.NewScanService(dbCfg)
creds := imagescan.RegistryCredentials{
Username: scanInfo.ImageScanInfo.Username,
Password: scanInfo.ImageScanInfo.Password,
}
userInput := args[0]
logger.L().Info(fmt.Sprintf("Scanning image: %s", userInput))
scanResults, err := svc.Scan(ctx, userInput, creds)
if err != nil {
logger.L().Error("Image scan failed", helpers.Error(err))
return err
}
logger.L().Success("Image scan completed successfully")
scanInfo.IsNewOutputFormat = true
scanInfo.SetScanType(cautils.ScanTypeImage)
outputPrinters := core.GetOutputPrinters(scanInfo, ctx)
uiPrinter := core.GetUIPrinter(ctx, scanInfo.VerboseMode, scanInfo.FormatVersion, scanInfo.PrintAttackTree, cautils.ViewTypes(scanInfo.View), scanInfo.ScanType, scanInfo.InputPatterns)
resultsHandler := resultshandling.NewResultsHandler(nil, outputPrinters, uiPrinter, scanResults)
resultsHandler.ImageScanData = []cautils.ImageScanData{
{
PresenterConfig: scanResults,
Image: userInput,
},
}
resultsHandler.HandleResults(ctx)
if imagescan.ExceedsSeverityThreshold(scanResults, failOnSeverity) {
terminateOnExceedingSeverity(scanInfo, logger.L())
}
return err
},
}
cmd.PersistentFlags().StringVarP(&scanInfo.ImageScanInfo.Username, "username", "u", "", "Username for registry login")
cmd.PersistentFlags().StringVarP(&scanInfo.ImageScanInfo.Password, "password", "p", "", "Password for registry login")
return cmd
}
// validateImageScanInfo validates the ScanInfo struct for the `image` command
func validateImageScanInfo(scanInfo *cautils.ScanInfo) error {
severity := scanInfo.FailThresholdSeverity
if err := validateSeverity(severity); severity != "" && err != nil {
return err
}
return nil
}

View File

@@ -51,6 +51,7 @@ func GetScanCommand(ks meta.IKubescape) *cobra.Command {
RunE: func(cmd *cobra.Command, args []string) error {
if len(args) == 0 {
scanInfo.SetScanType(cautils.ScanTypeCluster)
return getFrameworkCmd(ks, &scanInfo).RunE(cmd, []string{strings.Join(getter.NativeFrameworks, ",")})
}
return nil
@@ -71,6 +72,8 @@ func GetScanCommand(ks meta.IKubescape) *cobra.Command {
scanCmd.PersistentFlags().StringVar(&scanInfo.UseExceptions, "exceptions", "", "Path to an exceptions obj. If not set will download exceptions from ARMO management portal")
scanCmd.PersistentFlags().StringVar(&scanInfo.UseArtifactsFrom, "use-artifacts-from", "", "Load artifacts from local directory. If not used will download them")
scanCmd.PersistentFlags().StringVarP(&scanInfo.ExcludedNamespaces, "exclude-namespaces", "e", "", "Namespaces to exclude from scanning. e.g: --exclude-namespaces ns-a,ns-b. Notice, when running with `exclude-namespace` kubescape does not scan cluster-scoped objects.")
scanCmd.PersistentFlags().StringVarP(&scanInfo.ChartPath, "chart", "c", "", "Path to a Helm chart. If not set will download Helm chart from ARMO management portal")
scanCmd.PersistentFlags().StringVarP(&scanInfo.FilePath, "file", "i", "", "Path to a YAML file or a directory containing YAML files. If not set will download YAML files from ARMO management portal")
scanCmd.PersistentFlags().Float32VarP(&scanInfo.FailThreshold, "fail-threshold", "t", 100, "Failure threshold is the percent above which the command fails and returns exit code 1")
scanCmd.PersistentFlags().Float32VarP(&scanInfo.ComplianceThreshold, "compliance-threshold", "", 0, "Compliance threshold is the percent below which the command fails and returns exit code 1")
@@ -83,13 +86,15 @@ func GetScanCommand(ks meta.IKubescape) *cobra.Command {
scanCmd.PersistentFlags().BoolVarP(&scanInfo.VerboseMode, "verbose", "v", false, "Display all of the input resources and not only failed resources")
scanCmd.PersistentFlags().StringVar(&scanInfo.View, "view", string(cautils.ResourceViewType), fmt.Sprintf("View results based on the %s/%s. default is --view=%s", cautils.ResourceViewType, cautils.ControlViewType, cautils.ResourceViewType))
scanCmd.PersistentFlags().BoolVar(&scanInfo.UseDefault, "use-default", false, "Load local policy object from default path. If not used will download latest")
scanCmd.PersistentFlags().StringSliceVar(&scanInfo.UseFrom, "use-from", nil, "Load local policy object from specified path. If not used will download latest")
scanCmd.PersistentFlags().StringSliceVar(&scanInfo.UseFrom, "use-from", []string{"/Users/danielgrunberger/armo/merge/release/clusterscan.json", "/Users/danielgrunberger/armo/merge/release/mitre.json", "/Users/danielgrunberger/armo/merge/release/nsa.json"}, "Load local policy object from specified path. If not used will download latest")
scanCmd.PersistentFlags().StringVar(&scanInfo.HostSensorYamlPath, "host-scan-yaml", "", "Override default host scanner DaemonSet. Use this flag cautiously")
scanCmd.PersistentFlags().StringVar(&scanInfo.FormatVersion, "format-version", "v2", "Output object can be different between versions, this is for maintaining backward and forward compatibility. Supported:'v1'/'v2'")
scanCmd.PersistentFlags().StringVar(&scanInfo.CustomClusterName, "cluster-name", "", "Set the custom name of the cluster. Not same as the kube-context flag")
scanCmd.PersistentFlags().BoolVarP(&scanInfo.Submit, "submit", "", false, "Submit the scan results to Kubescape SaaS where you can see the results in a user-friendly UI, choose your preferred compliance framework, check risk results history and trends, manage exceptions, get remediation recommendations and much more. By default the results are not submitted")
scanCmd.PersistentFlags().BoolVarP(&scanInfo.OmitRawResources, "omit-raw-resources", "", false, "Omit raw resources from the output. By default the raw resources are included in the output")
scanCmd.PersistentFlags().BoolVarP(&scanInfo.PrintAttackTree, "print-attack-tree", "", false, "Print attack tree")
scanCmd.PersistentFlags().BoolVarP(&scanInfo.ScanImages, "scan-images", "", false, "Scan images")
scanCmd.PersistentFlags().BoolVarP(&scanInfo.IsNewOutputFormat, "new-output", "", false, "Show new output")
scanCmd.PersistentFlags().MarkDeprecated("silent", "use '--logger' flag instead. Flag will be removed at 1.May.2022")
scanCmd.PersistentFlags().MarkDeprecated("fail-threshold", "use '--compliance-threshold' flag instead. Flag will be removed at 1.Dec.2023")
@@ -117,6 +122,9 @@ func GetScanCommand(ks meta.IKubescape) *cobra.Command {
scanCmd.AddCommand(getControlCmd(ks, &scanInfo))
scanCmd.AddCommand(getFrameworkCmd(ks, &scanInfo))
scanCmd.AddCommand(getWorkloadCmd(ks, &scanInfo))
scanCmd.AddCommand(getImageCmd(ks, &scanInfo))
return scanCmd
}

110
cmd/scan/workload.go Normal file
View File

@@ -0,0 +1,110 @@
package scan
import (
"context"
"fmt"
"strings"
logger "github.com/kubescape/go-logger"
"github.com/kubescape/kubescape/v2/core/cautils"
"github.com/kubescape/kubescape/v2/core/meta"
v1 "github.com/kubescape/opa-utils/httpserver/apis/v1"
"github.com/spf13/cobra"
)
var (
workloadExample = fmt.Sprintf(`
# Scan an workload
%[1]s scan workload <kind>/<name>
# Scan an workload in a specific namespace
%[1]s scan workload <kind>/<name> --namespace <namespace>
# Scan an workload from a file path
%[1]s scan workload <kind>/<name> --file-path <file path>
# Scan an workload from a helm-chart template
%[1]s scan workload <kind>/<name> --chart-path <chart path>
`, cautils.ExecName())
)
var namespace string
// controlCmd represents the control command
func getWorkloadCmd(ks meta.IKubescape, scanInfo *cautils.ScanInfo) *cobra.Command {
workloadCmd := &cobra.Command{
Use: "workload <kind>/<name> [`<glob pattern>`/`-`] [flags]",
Short: fmt.Sprint("The workload you wish to scan"),
Example: workloadExample,
Args: func(cmd *cobra.Command, args []string) error {
if len(args) != 1 {
return fmt.Errorf("usage: <kind>/<name>")
}
if scanInfo.ChartPath != "" && scanInfo.FilePath == "" {
return fmt.Errorf("usage: --chart-path <chart path> --file-path <file path>")
}
wlIdentifier := strings.Split(args[0], "/")
if len(wlIdentifier) != 2 || wlIdentifier[0] == "" || wlIdentifier[1] == "" {
return fmt.Errorf("usage: <kind>/<name>")
}
return nil
},
RunE: func(cmd *cobra.Command, args []string) error {
var wlIdentifier string
wlIdentifier += args[0]
kind, name, err := parseWorkloadIdentifierString(wlIdentifier)
if err != nil {
logger.L().Fatal(err.Error())
}
scanInfo.WorkloadIdentifier = &cautils.WorkloadIdentifier{
Namespace: namespace,
Kind: kind,
Name: name,
}
scanInfo.ScanAll = true
scanInfo.IsNewOutputFormat = true
scanInfo.SetScanType(cautils.ScanTypeWorkload)
scanInfo.ScanImages = true
scanInfo.UseFrom = []string{"/Users/danielgrunberger/armo/merge/release/workloadscan.json"}
scanInfo.SetPolicyIdentifiers([]string{"workloadscan"}, v1.KindFramework)
ctx := context.TODO()
results, err := ks.Scan(ctx, scanInfo)
if err != nil {
logger.L().Fatal(err.Error())
}
if err = results.HandleResults(ctx); err != nil {
logger.L().Fatal(err.Error())
}
return nil
},
}
workloadCmd.PersistentFlags().StringVarP(&namespace, "namespace", "n", "", "Namespace of the workload. Default will be empty.")
workloadCmd.PersistentFlags().StringVar(&scanInfo.FilePath, "file-path", "", "Path to the workload file.")
workloadCmd.PersistentFlags().StringVar(&scanInfo.ChartPath, "chart-path", "", "Path to the helm chart.")
return workloadCmd
}
func parseWorkloadIdentifierString(workloadIdentifier string) (kind, name string, err error) {
// workloadIdentifier is in the form of namespace/kind/name
// example: default/Deployment/nginx-deployment
x := strings.Split(workloadIdentifier, "/")
if len(x) != 2 {
return "", "", fmt.Errorf("invalid workload identifier")
}
return x[0], x[1], nil
}

View File

@@ -2,13 +2,16 @@ package cautils
import (
"context"
"sort"
"github.com/anchore/grype/grype/presenter/models"
"github.com/armosec/armoapi-go/armotypes"
"github.com/kubescape/k8s-interface/workloadinterface"
"github.com/kubescape/opa-utils/reporthandling"
apis "github.com/kubescape/opa-utils/reporthandling/apis"
"github.com/kubescape/opa-utils/reporthandling/attacktrack/v1alpha1"
"github.com/kubescape/opa-utils/reporthandling/results/v1/prioritization"
"github.com/kubescape/opa-utils/reporthandling/results/v1/reportsummary"
"github.com/kubescape/opa-utils/reporthandling/results/v1/resourcesresults"
reporthandlingv2 "github.com/kubescape/opa-utils/reporthandling/v2"
)
@@ -17,10 +20,27 @@ import (
type K8SResources map[string][]string
type KSResources map[string][]string
type ImageScanData struct {
PresenterConfig *models.PresenterConfig
Image string
}
type ScanTypes string
const (
TopWorkloadsNumber = 5
ScanTypeCluster ScanTypes = "cluster"
ScanTypeRepo ScanTypes = "repo"
ScanTypeImage ScanTypes = "image"
ScanTypeWorkload ScanTypes = "workload"
ScanTypeFramework ScanTypes = "framework"
)
type OPASessionObj struct {
K8SResources *K8SResources // input k8s objects
ArmoResource *KSResources // input ARMO objects
K8SResources K8SResources // input k8s objects
KubescapeResource KSResources // input Kubescape objects
AllPolicies *Policies // list of all frameworks
ExcludedRules map[string]bool // rules to exclude map[rule name>]X
AllResources map[string]workloadinterface.IMetadata // all scanned resources, map[<resource ID>]<resource>
ResourcesResult map[string]resourcesresults.Result // resources scan results, map[<resource ID>]<resource result>
ResourceSource map[string]reporthandling.Source // resources sources, map[<resource ID>]<resource result>
@@ -36,9 +56,11 @@ type OPASessionObj struct {
Policies []reporthandling.Framework // list of frameworks to scan
Exceptions []armotypes.PostureExceptionPolicy // list of exceptions to apply on scan results
OmitRawResources bool // omit raw resources from output
ScanType ScanTypes // scan type
ScannedWorkload workloadinterface.IWorkload // single workload scan
}
func NewOPASessionObj(ctx context.Context, frameworks []reporthandling.Framework, k8sResources *K8SResources, scanInfo *ScanInfo) *OPASessionObj {
func NewOPASessionObj(ctx context.Context, frameworks []reporthandling.Framework, k8sResources K8SResources, scanInfo *ScanInfo) *OPASessionObj {
return &OPASessionObj{
Report: &reporthandlingv2.PostureReport{},
Policies: frameworks,
@@ -55,6 +77,38 @@ func NewOPASessionObj(ctx context.Context, frameworks []reporthandling.Framework
}
}
// SetTopWorkloads sets the top workloads by score
func (sessionObj *OPASessionObj) SetTopWorkloads() {
count := 0
topWorkloadsSorted := make([]prioritization.PrioritizedResource, 0)
// create list in order to sort
for _, wl := range sessionObj.ResourcesPrioritized {
topWorkloadsSorted = append(topWorkloadsSorted, wl)
}
sort.Slice(topWorkloadsSorted, func(i, j int) bool {
if topWorkloadsSorted[i].Score == topWorkloadsSorted[j].Score {
return topWorkloadsSorted[i].ResourceID < topWorkloadsSorted[j].ResourceID
}
return topWorkloadsSorted[i].Score > topWorkloadsSorted[j].Score
})
// set top workloads according to number of top workloads
for i := 0; i < TopWorkloadsNumber; i++ {
if i >= len(topWorkloadsSorted) {
break
}
wlObj := reportsummary.TopWorkload{
Workload: sessionObj.AllResources[topWorkloadsSorted[i].ResourceID],
ResourceSource: sessionObj.ResourceSource[topWorkloadsSorted[i].ResourceID],
}
sessionObj.Report.SummaryDetails.TopWorkloadsByScore = append(sessionObj.Report.SummaryDetails.TopWorkloadsByScore, wlObj)
count++
}
}
func (sessionObj *OPASessionObj) SetMapNamespaceToNumberOfResources(mapNamespaceToNumberOfResources map[string]int) {
if sessionObj.Metadata.ContextMetadata.ClusterContextMetadata == nil {
sessionObj.Metadata.ContextMetadata.ClusterContextMetadata = &reporthandlingv2.ClusterMetadata{}

View File

@@ -0,0 +1,48 @@
package cautils
// func TestSetTopWorkloads(t *testing.T) {
// tests := []struct {
// name string
// resourcesPrioritized map[string]prioritization.PrioritizedResource
// allResources map[string]workloadinterface.IMetadata
// resourcesSource map[string]reporthandling.Source
// want []reportsummary.TopWorkload
// }{{
// name: "Test 1",
// resourcesPrioritized: map[string]prioritization.PrioritizedResource{
// "1": {
// Score: 1,
// },
// },
// allResources: map[string]workloadinterface.IMetadata{
// "1": &workloadinterface.BaseObject{},
// },
// }}
// for _, tt := range tests {
// opaSessionObj := OPASessionObj{
// ResourcesPrioritized: tt.resourcesPrioritized,
// }
// t.Run(tt.name, func(t *testing.T) {
// opaSessionObj.SetTopWorkloads()
// if len(opaSessionObj.Report.SummaryDetails.TopWorkloadsByScore) != len(tt.want) {
// t.Errorf("got %d, want %d", len(opaSessionObj.Report.SummaryDetails.TopWorkloadsByScore), len(tt.want))
// }
// for i := 0; i < len(opaSessionObj.Report.SummaryDetails.TopWorkloadsByScore); i++ {
// if opaSessionObj.Report.SummaryDetails.TopWorkloadsByScore[i].Workload.GetKind() != tt.want[i].Workload.GetKind() {
// t.Errorf("got %s, want %s", opaSessionObj.Report.SummaryDetails.TopWorkloadsByScore[i].Workload.GetKind(), tt.want[i].Workload.GetKind())
// }
// if opaSessionObj.Report.SummaryDetails.TopWorkloadsByScore[i].Workload.GetName() != tt.want[i].Workload.GetName() {
// t.Errorf("got %s, want %s", opaSessionObj.Report.SummaryDetails.TopWorkloadsByScore[i].Workload.GetName(), tt.want[i].Workload.GetName())
// }
// if opaSessionObj.Report.SummaryDetails.TopWorkloadsByScore[i].Workload.GetNamespace() != tt.want[i].Workload.GetNamespace() {
// t.Errorf("got %s, want %s", opaSessionObj.Report.SummaryDetails.TopWorkloadsByScore[i].Workload.GetNamespace(), tt.want[i].Workload.GetNamespace())
// }
// if opaSessionObj.Report.SummaryDetails.TopWorkloadsByScore[i].ResourceSource != tt.want[i].ResourceSource {
// t.Errorf("got %s, want %s", opaSessionObj.Report.SummaryDetails.TopWorkloadsByScore[i].ResourceSource, tt.want[i].ResourceSource)
// }
// }
// })
// }
// }

View File

@@ -15,7 +15,7 @@ func NewPolicies() *Policies {
}
}
func (policies *Policies) Set(frameworks []reporthandling.Framework, version string) {
func (policies *Policies) Set(frameworks []reporthandling.Framework, version string, excludedRules map[string]bool) {
for i := range frameworks {
if frameworks[i].Name != "" && len(frameworks[i].Controls) > 0 {
policies.Frameworks = append(policies.Frameworks, frameworks[i].Name)
@@ -23,6 +23,13 @@ func (policies *Policies) Set(frameworks []reporthandling.Framework, version str
for j := range frameworks[i].Controls {
compatibleRules := []reporthandling.PolicyRule{}
for r := range frameworks[i].Controls[j].Rules {
if excludedRules != nil {
ruleName := frameworks[i].Controls[j].Rules[r].Name
if _, exclude := excludedRules[ruleName]; exclude {
continue
}
}
if !ruleWithKSOpaDependency(frameworks[i].Controls[j].Rules[r].Attributes) && isRuleKubescapeVersionCompatible(frameworks[i].Controls[j].Rules[r].Attributes, version) {
compatibleRules = append(compatibleRules, frameworks[i].Controls[j].Rules[r])
}

View File

@@ -24,10 +24,6 @@ var (
JSON_PREFIX = []string{"json"}
)
var (
ErrNoFilesToScan string = "no files found to scan, input %s"
)
type FileFormat string
const (
@@ -35,8 +31,13 @@ const (
JSON_FILE_FORMAT FileFormat = "json"
)
type Chart struct {
Name string
Path string
}
// LoadResourcesFromHelmCharts scans a given path (recursively) for helm charts, renders the templates and returns a map of workloads and a map of chart names
func LoadResourcesFromHelmCharts(ctx context.Context, basePath string) (map[string][]workloadinterface.IMetadata, map[string]string) {
func LoadResourcesFromHelmCharts(ctx context.Context, basePath string) (map[string][]workloadinterface.IMetadata, map[string]Chart) {
directories, _ := listDirs(basePath)
helmDirectories := make([]string, 0)
for _, dir := range directories {
@@ -46,7 +47,7 @@ func LoadResourcesFromHelmCharts(ctx context.Context, basePath string) (map[stri
}
sourceToWorkloads := map[string][]workloadinterface.IMetadata{}
sourceToChartName := map[string]string{}
sourceToChart := make(map[string]Chart, 0)
for _, helmDir := range helmDirectories {
chart, err := NewHelmChart(helmDir)
if err == nil {
@@ -59,11 +60,14 @@ func LoadResourcesFromHelmCharts(ctx context.Context, basePath string) (map[stri
chartName := chart.GetName()
for k, v := range wls {
sourceToWorkloads[k] = v
sourceToChartName[k] = chartName
sourceToChart[k] = Chart{
Name: chartName,
Path: helmDir,
}
}
}
}
return sourceToWorkloads, sourceToChartName
return sourceToWorkloads, sourceToChart
}
// If the contents at given path is a Kustomize Directory, LoadResourcesFromKustomizeDirectory will
@@ -101,14 +105,14 @@ func LoadResourcesFromKustomizeDirectory(ctx context.Context, basePath string) (
return sourceToWorkloads, kustomizeDirectoryName
}
func LoadResourcesFromFiles(ctx context.Context, input, rootPath string) (map[string][]workloadinterface.IMetadata, error) {
func LoadResourcesFromFiles(ctx context.Context, input, rootPath string) map[string][]workloadinterface.IMetadata {
files, errs := listFiles(input)
if len(errs) > 0 {
logger.L().Ctx(ctx).Warning(fmt.Sprintf("%v", errs))
}
if len(files) == 0 {
logger.L().Ctx(ctx).Error("no files found to scan", helpers.String("input", input))
return nil, fmt.Errorf(ErrNoFilesToScan, input)
return nil
}
workloads, errs := loadFiles(rootPath, files)
@@ -116,7 +120,7 @@ func LoadResourcesFromFiles(ctx context.Context, input, rootPath string) (map[st
logger.L().Ctx(ctx).Warning(fmt.Sprintf("%v", errs))
}
return workloads, nil
return workloads
}
func loadFiles(rootPath string, filePaths []string) (map[string][]workloadinterface.IMetadata, []error) {

View File

@@ -2,7 +2,6 @@ package cautils
import (
"context"
"fmt"
"os"
"path/filepath"
"strings"
@@ -32,8 +31,7 @@ func TestListFiles(t *testing.T) {
}
func TestLoadResourcesFromFiles(t *testing.T) {
workloads, err := LoadResourcesFromFiles(context.TODO(), onlineBoutiquePath(), "")
assert.Equal(t, err, nil)
workloads := LoadResourcesFromFiles(context.TODO(), onlineBoutiquePath(), "")
assert.Equal(t, 12, len(workloads))
for i, w := range workloads {
@@ -44,13 +42,6 @@ func TestLoadResourcesFromFiles(t *testing.T) {
assert.Equal(t, "/v1//Service/adservice", getRelativePath(w[1].GetID()))
}
}
// check case for empty directory
emptyDirectoryPath := filepath.Join("testdata", "emptyDirectory")
expectedError := fmt.Sprintf(ErrNoFilesToScan, emptyDirectoryPath)
_, err = LoadResourcesFromFiles(context.TODO(), emptyDirectoryPath, "")
assert.NotEqual(t, err, nil)
assert.Equal(t, err.Error(), expectedError)
}
func TestLoadResourcesFromHelmCharts(t *testing.T) {

View File

@@ -8,7 +8,9 @@ import (
)
// NativeFrameworks identifies all pre-built, native frameworks.
var NativeFrameworks = []string{"allcontrols", "nsa", "mitre"}
var NativeFrameworks = []string{"mitre", "nsa"}
// var NativeFrameworks = []string{"clusterscan"}
type (
// TenantResponse holds the credentials for a tenant.

View File

@@ -8,7 +8,6 @@ import (
"path/filepath"
"strings"
"github.com/armosec/armoapi-go/identifiers"
giturl "github.com/kubescape/go-git-url"
"github.com/kubescape/go-logger"
"github.com/kubescape/go-logger/helpers"
@@ -94,45 +93,63 @@ const (
)
type PolicyIdentifier struct {
Identifier string // policy Identifier e.g. c-0012 for control, nsa,mitre for frameworks
Kind apisv1.NotificationPolicyKind // policy kind e.g. Framework,Control,Rule
Designators identifiers.PortalDesignator
Identifier string // policy Identifier e.g. c-0012 for control, nsa,mitre for frameworks
Kind apisv1.NotificationPolicyKind // policy kind e.g. Framework,Control,Rule
}
type WorkloadIdentifier struct {
Namespace string
ApiVersion string
Kind string
Name string
}
type ImageScanInfo struct {
Username string
Password string
}
type ScanInfo struct {
Getters // TODO - remove from object
PolicyIdentifier []PolicyIdentifier // TODO - remove from object
UseExceptions string // Load file with exceptions configuration
ControlsInputs string // Load file with inputs for controls
AttackTracks string // Load file with attack tracks
UseFrom []string // Load framework from local file (instead of download). Use when running offline
UseDefault bool // Load framework from cached file (instead of download). Use when running offline
UseArtifactsFrom string // Load artifacts from local path. Use when running offline
VerboseMode bool // Display all of the input resources and not only failed resources
View string // Display all of the input resources and not only failed resources
Format string // Format results (table, json, junit ...)
Output string // Store results in an output file, Output file name
FormatVersion string // Output object can be different between versions, this is for testing and backward compatibility
CustomClusterName string // Set the custom name of the cluster
ExcludedNamespaces string // used for host scanner namespace
IncludeNamespaces string //
InputPatterns []string // Yaml files input patterns
Silent bool // Silent mode - Do not print progress logs
FailThreshold float32 // DEPRECATED - Failure score threshold
ComplianceThreshold float32 // Compliance score threshold
FailThresholdSeverity string // Severity at and above which the command should fail
Submit bool // Submit results to Kubescape Cloud BE
CreateAccount bool // Create account in Kubescape Cloud BE if no account found in local cache
ScanID string // Report id of the current scan
HostSensorEnabled BoolPtrFlag // Deploy Kubescape K8s host scanner to collect data from certain controls
HostSensorYamlPath string // Path to hostsensor file
Local bool // Do not submit results
Credentials Credentials // account ID
KubeContext string // context name
FrameworkScan bool // false if scanning control
ScanAll bool // true if scan all frameworks
OmitRawResources bool // true if omit raw resources from the output
PrintAttackTree bool // true if print attack tree
Getters // TODO - remove from object
PolicyIdentifier []PolicyIdentifier // TODO - remove from object
UseExceptions string // Load file with exceptions configuration
ControlsInputs string // Load file with inputs for controls
AttackTracks string // Load file with attack tracks
UseFrom []string // Load framework from local file (instead of download). Use when running offline
UseDefault bool // Load framework from cached file (instead of download). Use when running offline
UseArtifactsFrom string // Load artifacts from local path. Use when running offline
VerboseMode bool // Display all of the input resources and not only failed resources
View string // Display all of the input resources and not only failed resources
Format string // Format results (table, json, junit ...)
Output string // Store results in an output file, Output file name
FormatVersion string // Output object can be different between versions, this is for testing and backward compatibility
CustomClusterName string // Set the custom name of the cluster
ExcludedNamespaces string // used for host scanner namespace
IncludeNamespaces string //
InputPatterns []string // Yaml files input patterns
Silent bool // Silent mode - Do not print progress logs
FailThreshold float32 // DEPRECATED - Failure score threshold
ComplianceThreshold float32 // Compliance score threshold
FailThresholdSeverity string // Severity at and above which the command should fail
Submit bool // Submit results to Kubescape Cloud BE
CreateAccount bool // Create account in Kubescape Cloud BE if no account found in local cache
ScanID string // Report id of the current scan
HostSensorEnabled BoolPtrFlag // Deploy Kubescape K8s host scanner to collect data from certain controls
HostSensorYamlPath string // Path to hostsensor file
Local bool // Do not submit results
Credentials Credentials // account ID
KubeContext string // context name
FrameworkScan bool // false if scanning control
ScanAll bool // true if scan all frameworks
OmitRawResources bool // true if omit raw resources from the output
PrintAttackTree bool // true if print attack tree
WorkloadIdentifier *WorkloadIdentifier // workload identifier for workload scan
ScanType ScanTypes
ScanImages bool
ChartPath string
FilePath string
ImageScanInfo ImageScanInfo
IsNewOutputFormat bool
}
type Getters struct {
@@ -204,6 +221,13 @@ func (scanInfo *ScanInfo) Formats() []string {
}
}
func (scanInfo *ScanInfo) SetScanType(scanType ScanTypes) {
if !scanInfo.IsNewOutputFormat {
return
}
scanInfo.ScanType = scanType
}
func (scanInfo *ScanInfo) SetPolicyIdentifiers(policies []string, kind apisv1.NotificationPolicyKind) {
for _, policy := range policies {
if !scanInfo.contains(policy) {
@@ -452,3 +476,14 @@ func ScanningContextToScanningScope(scanningContext ScanningContext) string {
}
return ScopeYAML
}
func (wi *WorkloadIdentifier) String() string {
s := fmt.Sprintf("%s/%s", wi.Kind, wi.Name)
if wi.ApiVersion == "" {
s = fmt.Sprintf("%s/%s", wi.ApiVersion, s)
}
if wi.Namespace == "" {
s = fmt.Sprintf("%s/%s", wi.Namespace, s)
}
return s
}

View File

@@ -32,9 +32,9 @@ var (
}
)
func MapKSResource(ksResourceMap *KSResources, resources []string) []string {
func MapKSResource(ksResourceMap KSResources, resources []string) []string {
var hostResources []string
for k := range *ksResourceMap {
for k := range ksResourceMap {
for _, resource := range resources {
if strings.Contains(k, resource) {
hostResources = append(hostResources, k)
@@ -44,15 +44,15 @@ func MapKSResource(ksResourceMap *KSResources, resources []string) []string {
return hostResources
}
func MapHostResources(ksResourceMap *KSResources) []string {
func MapHostResources(ksResourceMap KSResources) []string {
return MapKSResource(ksResourceMap, HostSensorResources)
}
func MapImageVulnResources(ksResourceMap *KSResources) []string {
func MapImageVulnResources(ksResourceMap KSResources) []string {
return MapKSResource(ksResourceMap, ImageVulnResources)
}
func MapCloudResources(ksResourceMap *KSResources) []string {
func MapCloudResources(ksResourceMap KSResources) []string {
return MapKSResource(ksResourceMap, CloudResources)
}

View File

@@ -65,13 +65,13 @@ func getRBACHandler(tenantConfig cautils.ITenantConfig, k8s *k8sinterface.Kubern
return nil
}
func getReporter(ctx context.Context, tenantConfig cautils.ITenantConfig, reportID string, submit, fwScan bool, scanningContext cautils.ScanningContext) reporter.IReport {
func getReporter(ctx context.Context, tenantConfig cautils.ITenantConfig, reportID string, submit, fwScan bool, scanInfo cautils.ScanInfo) reporter.IReport {
_, span := otel.Tracer("").Start(ctx, "getReporter")
defer span.End()
if submit {
submitData := reporterv2.SubmitContextScan
if scanningContext != cautils.ContextCluster {
if scanInfo.GetScanningContext() != cautils.ContextCluster {
submitData = reporterv2.SubmitContextRepository
}
return reporterv2.NewReportEventReceiver(tenantConfig.GetConfigObj(), reportID, submitData)
@@ -81,7 +81,8 @@ func getReporter(ctx context.Context, tenantConfig cautils.ITenantConfig, report
return reporterv2.NewReportMock("", "")
}
var message string
if !fwScan {
if !fwScan && scanInfo.ScanType != cautils.ScanTypeWorkload {
message = "Kubescape does not submit scan results when scanning controls"
}
@@ -94,11 +95,16 @@ func getResourceHandler(ctx context.Context, scanInfo *cautils.ScanInfo, tenantC
if len(scanInfo.InputPatterns) > 0 || k8s == nil {
// scanInfo.HostSensor.SetBool(false)
return resourcehandler.NewFileResourceHandler(ctx, scanInfo.InputPatterns)
return resourcehandler.NewFileResourceHandler(ctx, scanInfo.InputPatterns, scanInfo.WorkloadIdentifier)
}
if scanInfo.WorkloadIdentifier != nil && scanInfo.FilePath != "" {
return resourcehandler.NewFileResourceHandler(ctx, []string{scanInfo.FilePath}, scanInfo.WorkloadIdentifier)
}
getter.GetKSCloudAPIConnector()
rbacObjects := getRBACHandler(tenantConfig, k8s, scanInfo.Submit)
return resourcehandler.NewK8sResourceHandler(k8s, getFieldSelector(scanInfo), hostSensorHandler, rbacObjects, registryAdaptors)
return resourcehandler.NewK8sResourceHandler(k8s, getFieldSelector(scanInfo), hostSensorHandler, rbacObjects, registryAdaptors, scanInfo.WorkloadIdentifier)
}
// getHostSensorHandler yields a IHostSensor that knows how to collect a host's scanned resources.
@@ -188,6 +194,12 @@ func setSubmitBehavior(scanInfo *cautils.ScanInfo, tenantConfig cautils.ITenantC
return
}
// do not submit workload scanning
if scanInfo.WorkloadIdentifier != nil {
scanInfo.Submit = false
return
}
// If There is no account, or if the account is not legal, do not submit
if _, err := uuid.Parse(tenantConfig.GetAccountID()); err != nil {
scanInfo.Submit = false
@@ -280,12 +292,12 @@ func getAttackTracksGetter(ctx context.Context, attackTracks, accountID string,
}
// getUIPrinter returns a printer that will be used to print to the programs UI (terminal)
func getUIPrinter(ctx context.Context, verboseMode bool, formatVersion string, attackTree bool, viewType cautils.ViewTypes) printer.IPrinter {
func GetUIPrinter(ctx context.Context, verboseMode bool, formatVersion string, attackTree bool, viewType cautils.ViewTypes, scanType cautils.ScanTypes, inputPatterns []string) printer.IPrinter {
var p printer.IPrinter
if helpers.ToLevel(logger.L().GetLevel()) >= helpers.WarningLevel {
p = &printerv2.SilentPrinter{}
} else {
p = printerv2.NewPrettyPrinter(verboseMode, formatVersion, attackTree, viewType)
p = printerv2.NewPrettyPrinter(verboseMode, formatVersion, attackTree, viewType, scanType, inputPatterns)
// Since the UI of the program is a CLI (Stdout), it means that it should always print to Stdout
p.SetWriter(ctx, os.Stdout.Name())

View File

@@ -7,6 +7,7 @@ import (
"github.com/kubescape/go-logger"
"github.com/kubescape/go-logger/helpers"
"github.com/kubescape/k8s-interface/k8sinterface"
"github.com/kubescape/k8s-interface/workloadinterface"
"github.com/kubescape/kubescape/v2/core/cautils"
"github.com/kubescape/kubescape/v2/core/cautils/getter"
"github.com/kubescape/kubescape/v2/core/pkg/hostsensorutils"
@@ -17,8 +18,10 @@ import (
"github.com/kubescape/kubescape/v2/core/pkg/resultshandling"
"github.com/kubescape/kubescape/v2/core/pkg/resultshandling/printer"
"github.com/kubescape/kubescape/v2/core/pkg/resultshandling/reporter"
"github.com/kubescape/kubescape/v2/pkg/imagescan"
apisv1 "github.com/kubescape/opa-utils/httpserver/apis/v1"
"go.opentelemetry.io/otel"
"golang.org/x/exp/slices"
"github.com/kubescape/opa-utils/resources"
)
@@ -66,6 +69,7 @@ func getInterfaces(ctx context.Context, scanInfo *cautils.ScanInfo) componentInt
// ================== version testing ======================================
// move this
v := cautils.NewIVersionCheckHandler(ctx)
v.CheckLatestVersion(ctx, cautils.NewVersionCheckRequest(cautils.BuildNumber, policyIdentifierIdentities(scanInfo.PolicyIdentifier), "", cautils.ScanningContextToScanningScope(scanInfo.GetScanningContext())))
@@ -79,7 +83,11 @@ func getInterfaces(ctx context.Context, scanInfo *cautils.ScanInfo) componentInt
spanHostScanner.End()
// ================== setup registry adaptors ======================================
registryAdaptors, _ := resourcehandler.NewRegistryAdaptors()
registryAdaptors, err := resourcehandler.NewRegistryAdaptors()
if err != nil {
logger.L().Ctx(ctx).Error("failed to initialize registry adaptors", helpers.Error(err))
}
// ================== setup resource collector object ======================================
@@ -88,19 +96,12 @@ func getInterfaces(ctx context.Context, scanInfo *cautils.ScanInfo) componentInt
// ================== setup reporter & printer objects ======================================
// reporting behavior - setup reporter
reportHandler := getReporter(ctx, tenantConfig, scanInfo.ScanID, scanInfo.Submit, scanInfo.FrameworkScan, scanInfo.GetScanningContext())
reportHandler := getReporter(ctx, tenantConfig, scanInfo.ScanID, scanInfo.Submit, scanInfo.FrameworkScan, *scanInfo)
// setup printers
formats := scanInfo.Formats()
outputPrinters := GetOutputPrinters(scanInfo, ctx)
outputPrinters := make([]printer.IPrinter, 0)
for _, format := range formats {
printerHandler := resultshandling.NewPrinter(ctx, format, scanInfo.FormatVersion, scanInfo.PrintAttackTree, scanInfo.VerboseMode, cautils.ViewTypes(scanInfo.View))
printerHandler.SetWriter(ctx, scanInfo.Output)
outputPrinters = append(outputPrinters, printerHandler)
}
uiPrinter := getUIPrinter(ctx, scanInfo.VerboseMode, scanInfo.FormatVersion, scanInfo.PrintAttackTree, cautils.ViewTypes(scanInfo.View))
uiPrinter := GetUIPrinter(ctx, scanInfo.VerboseMode, scanInfo.FormatVersion, scanInfo.PrintAttackTree, cautils.ViewTypes(scanInfo.View), scanInfo.ScanType, scanInfo.InputPatterns)
// ================== return interface ======================================
@@ -114,6 +115,18 @@ func getInterfaces(ctx context.Context, scanInfo *cautils.ScanInfo) componentInt
}
}
func GetOutputPrinters(scanInfo *cautils.ScanInfo, ctx context.Context) []printer.IPrinter {
formats := scanInfo.Formats()
outputPrinters := make([]printer.IPrinter, 0)
for _, format := range formats {
printerHandler := resultshandling.NewPrinter(ctx, format, scanInfo.FormatVersion, scanInfo.PrintAttackTree, scanInfo.VerboseMode, cautils.ViewTypes(scanInfo.View))
printerHandler.SetWriter(ctx, scanInfo.Output)
outputPrinters = append(outputPrinters, printerHandler)
}
return outputPrinters
}
func (ks *Kubescape) Scan(ctx context.Context, scanInfo *cautils.ScanInfo) (*resultshandling.ResultsHandler, error) {
ctxInit, spanInit := otel.Tracer("").Start(ctx, "initialization")
logger.L().Info("Kubescape scanner starting")
@@ -148,7 +161,7 @@ func (ks *Kubescape) Scan(ctx context.Context, scanInfo *cautils.ScanInfo) (*res
}
}()
resultsHandling := resultshandling.NewResultsHandler(interfaces.report, interfaces.outputPrinters, interfaces.uiPrinter)
resultsHandling := resultshandling.NewResultsHandler(interfaces.report, interfaces.outputPrinters, interfaces.uiPrinter, nil)
// ===================== policies =====================
ctxPolicies, spanPolicies := otel.Tracer("").Start(ctxInit, "policies")
@@ -162,7 +175,7 @@ func (ks *Kubescape) Scan(ctx context.Context, scanInfo *cautils.ScanInfo) (*res
// ===================== resources =====================
ctxResources, spanResources := otel.Tracer("").Start(ctxInit, "resources")
err = resourcehandler.CollectResources(ctxResources, interfaces.resourceHandler, scanInfo.PolicyIdentifier, scanData, cautils.NewProgressHandler(""))
err = resourcehandler.CollectResources(ctxResources, interfaces.resourceHandler, scanInfo.PolicyIdentifier, scanData, cautils.NewProgressHandler(""), *scanInfo)
if err != nil {
spanInit.End()
return resultsHandling, err
@@ -182,16 +195,22 @@ func (ks *Kubescape) Scan(ctx context.Context, scanInfo *cautils.ScanInfo) (*res
}
// ======================== prioritization ===================
if scanInfo.PrintAttackTree {
if scanInfo.PrintAttackTree || isPrioritizationScanType(scanInfo.ScanType) {
_, spanPrioritization := otel.Tracer("").Start(ctxOpa, "prioritization")
if priotizationHandler, err := resourcesprioritization.NewResourcesPrioritizationHandler(ctxOpa, scanInfo.Getters.AttackTracksGetter, scanInfo.PrintAttackTree); err != nil {
logger.L().Ctx(ctx).Warning("failed to get attack tracks, this may affect the scanning results", helpers.Error(err))
} else if err := priotizationHandler.PrioritizeResources(scanData); err != nil {
return resultsHandling, fmt.Errorf("%w", err)
}
if err == nil && isPrioritizationScanType(scanInfo.ScanType) {
scanData.SetTopWorkloads()
}
spanPrioritization.End()
}
if scanInfo.ScanImages {
scanImages(scanInfo, scanData, ctx, resultsHandling)
}
// ========================= results handling =====================
resultsHandling.SetData(scanData)
@@ -201,3 +220,59 @@ func (ks *Kubescape) Scan(ctx context.Context, scanInfo *cautils.ScanInfo) (*res
return resultsHandling, nil
}
func scanImages(scanInfo *cautils.ScanInfo, scanData *cautils.OPASessionObj, ctx context.Context, resultsHandling *resultshandling.ResultsHandler) {
imagesToScan := []string{}
if scanInfo.ScanType == cautils.ScanTypeWorkload {
containers, _ := workloadinterface.NewWorkloadObj(scanData.ScannedWorkload.GetObject()).GetContainers()
for _, container := range containers {
if !slices.Contains(imagesToScan, container.Image) {
imagesToScan = append(imagesToScan, container.Image)
}
}
} else {
for _, workload := range scanData.AllResources {
containers, _ := workloadinterface.NewWorkloadObj(workload.GetObject()).GetContainers()
for _, container := range containers {
if !slices.Contains(imagesToScan, container.Image) {
imagesToScan = append(imagesToScan, container.Image)
}
}
}
}
logger.L().Info("Scanning images")
dbCfg, _ := imagescan.NewDefaultDBConfig()
svc := imagescan.NewScanService(dbCfg)
for _, img := range imagesToScan {
scanSingleImage(ctx, img, svc, resultsHandling, *scanInfo)
}
logger.L().Success("Finished scanning images")
}
func scanSingleImage(ctx context.Context, img string, svc imagescan.Service, resultsHandling *resultshandling.ResultsHandler, scanInfo cautils.ScanInfo) {
logger.L().Ctx(ctx).Debug(fmt.Sprintf("Scanning image: %s", img))
registryCredentials := imagescan.RegistryCredentials{
Username: scanInfo.ImageScanInfo.Username,
Password: scanInfo.ImageScanInfo.Password,
}
scanResults, err := svc.Scan(ctx, img, registryCredentials)
if err != nil {
logger.L().Ctx(ctx).Error(fmt.Sprintf("failed to scan image: %s", img), helpers.Error(err))
return
}
resultsHandling.ImageScanData = append(resultsHandling.ImageScanData, cautils.ImageScanData{
Image: img,
PresenterConfig: scanResults,
})
}
func isPrioritizationScanType(scanType cautils.ScanTypes) bool {
return scanType == cautils.ScanTypeCluster || scanType == cautils.ScanTypeRepo
}

View File

@@ -59,7 +59,7 @@ func NewOPAProcessor(sessionObj *cautils.OPASessionObj, regoDependenciesData *re
}
func (opap *OPAProcessor) ProcessRulesListener(ctx context.Context, progressListener IJobProgressNotificationClient) error {
opap.OPASessionObj.AllPolicies = ConvertFrameworksToPolicies(opap.Policies, cautils.BuildNumber)
opap.OPASessionObj.AllPolicies = ConvertFrameworksToPolicies(opap.Policies, cautils.BuildNumber, opap.ExcludedRules)
ConvertFrameworksToSummaryDetails(&opap.Report.SummaryDetails, opap.Policies, opap.OPASessionObj.AllPolicies)
@@ -271,7 +271,7 @@ func (opap *OPAProcessor) processRule(ctx context.Context, rule *reporthandling.
inputResources, err := reporthandling.RegoResourcesAggregator(
rule,
getAllSupportedObjects(opap.K8SResources, opap.ArmoResource, opap.AllResources, rule), // NOTE: this uses the initial snapshot of AllResources
getAllSupportedObjects(opap.K8SResources, opap.KubescapeResource, opap.AllResources, rule), // NOTE: this uses the initial snapshot of AllResources
)
if err != nil {
return nil, nil, fmt.Errorf("error getting aggregated k8sObjects: %w", err)

View File

@@ -185,10 +185,10 @@ func TestProcessResourcesResult(t *testing.T) {
opaSessionObj := cautils.NewOPASessionObjMock()
opaSessionObj.Policies = frameworks
policies := ConvertFrameworksToPolicies(opaSessionObj.Policies, "")
policies := ConvertFrameworksToPolicies(opaSessionObj.Policies, "", nil)
ConvertFrameworksToSummaryDetails(&opaSessionObj.Report.SummaryDetails, opaSessionObj.Policies, policies)
opaSessionObj.K8SResources = &k8sResources
opaSessionObj.K8SResources = k8sResources
opaSessionObj.AllResources[deployment.GetID()] = deployment
opap := NewOPAProcessor(opaSessionObj, resources.NewRegoDependenciesDataMock())

View File

@@ -87,14 +87,14 @@ func isEmptyResources(counters reportsummary.ICounters) bool {
return counters.Failed() == 0 && counters.Skipped() == 0 && counters.Passed() == 0
}
func getAllSupportedObjects(k8sResources *cautils.K8SResources, ksResources *cautils.KSResources, allResources map[string]workloadinterface.IMetadata, rule *reporthandling.PolicyRule) []workloadinterface.IMetadata {
func getAllSupportedObjects(k8sResources cautils.K8SResources, ksResources cautils.KSResources, allResources map[string]workloadinterface.IMetadata, rule *reporthandling.PolicyRule) []workloadinterface.IMetadata {
k8sObjects := []workloadinterface.IMetadata{}
k8sObjects = append(k8sObjects, getKubernetesObjects(k8sResources, allResources, rule.Match)...)
k8sObjects = append(k8sObjects, getKSObjects(ksResources, allResources, rule.DynamicMatch)...)
return k8sObjects
}
func getKSObjects(k8sResources *cautils.KSResources, allResources map[string]workloadinterface.IMetadata, match []reporthandling.RuleMatchObjects) []workloadinterface.IMetadata {
func getKSObjects(k8sResources cautils.KSResources, allResources map[string]workloadinterface.IMetadata, match []reporthandling.RuleMatchObjects) []workloadinterface.IMetadata {
k8sObjects := []workloadinterface.IMetadata{}
for m := range match {
@@ -103,7 +103,7 @@ func getKSObjects(k8sResources *cautils.KSResources, allResources map[string]wor
for _, resource := range match[m].Resources {
groupResources := k8sinterface.ResourceGroupToString(groups, version, resource)
for _, groupResource := range groupResources {
if k8sObj, ok := (*k8sResources)[groupResource]; ok {
if k8sObj, ok := k8sResources[groupResource]; ok {
for i := range k8sObj {
k8sObjects = append(k8sObjects, allResources[k8sObj[i]])
}
@@ -117,7 +117,7 @@ func getKSObjects(k8sResources *cautils.KSResources, allResources map[string]wor
return filterOutChildResources(k8sObjects, match)
}
func getKubernetesObjects(k8sResources *cautils.K8SResources, allResources map[string]workloadinterface.IMetadata, match []reporthandling.RuleMatchObjects) []workloadinterface.IMetadata {
func getKubernetesObjects(k8sResources cautils.K8SResources, allResources map[string]workloadinterface.IMetadata, match []reporthandling.RuleMatchObjects) []workloadinterface.IMetadata {
k8sObjects := []workloadinterface.IMetadata{}
for m := range match {
@@ -126,7 +126,7 @@ func getKubernetesObjects(k8sResources *cautils.K8SResources, allResources map[s
for _, resource := range match[m].Resources {
groupResources := k8sinterface.ResourceGroupToString(groups, version, resource)
for _, groupResource := range groupResources {
if k8sObj, ok := (*k8sResources)[groupResource]; ok {
if k8sObj, ok := k8sResources[groupResource]; ok {
/*
if k8sObj == nil {
// logger.L().Debug("skipping", helpers.String("resource", groupResource))

File diff suppressed because one or more lines are too long

View File

@@ -16,9 +16,9 @@ import (
)
// ConvertFrameworksToPolicies convert list of frameworks to list of policies
func ConvertFrameworksToPolicies(frameworks []reporthandling.Framework, version string) *cautils.Policies {
func ConvertFrameworksToPolicies(frameworks []reporthandling.Framework, version string, excludedRules map[string]bool) *cautils.Policies {
policies := cautils.NewPolicies()
policies.Set(frameworks, version)
policies.Set(frameworks, version, excludedRules)
return policies
}
@@ -38,6 +38,7 @@ func ConvertFrameworksToSummaryDetails(summaryDetails *reportsummary.SummaryDeta
ScoreFactor: frameworks[i].Controls[j].BaseScore,
Description: frameworks[i].Controls[j].Description,
Remediation: frameworks[i].Controls[j].Remediation,
Category: frameworks[i].Controls[j].Category,
}
if frameworks[i].Controls[j].GetActionRequiredAttribute() == string(apis.SubStatusManualReview) {
c.Status = apis.StatusSkipped

View File

@@ -13,9 +13,20 @@ import (
func TestConvertFrameworksToPolicies(t *testing.T) {
fw0 := mocks.MockFramework_0006_0013()
fw1 := mocks.MockFramework_0044()
policies := ConvertFrameworksToPolicies([]reporthandling.Framework{*fw0, *fw1}, "")
policies := ConvertFrameworksToPolicies([]reporthandling.Framework{*fw0, *fw1}, "", nil)
assert.Equal(t, 2, len(policies.Frameworks))
assert.Equal(t, 3, len(policies.Controls))
// with excluded rules map
excludedRulesMap := map[string]bool{
"alert-rw-hostpath": true,
}
fw0 = mocks.MockFramework_0006_0013()
fw1 = mocks.MockFramework_0044()
policies = ConvertFrameworksToPolicies([]reporthandling.Framework{*fw0, *fw1}, "", excludedRulesMap)
assert.Equal(t, 2, len(policies.Frameworks))
assert.Equal(t, 2, len(policies.Controls))
}
func TestInitializeSummaryDetails(t *testing.T) {
fw0 := mocks.MockFramework_0006_0013()
@@ -23,7 +34,7 @@ func TestInitializeSummaryDetails(t *testing.T) {
summaryDetails := reportsummary.SummaryDetails{}
frameworks := []reporthandling.Framework{*fw0, *fw1}
policies := ConvertFrameworksToPolicies([]reporthandling.Framework{*fw0, *fw1}, "")
policies := ConvertFrameworksToPolicies([]reporthandling.Framework{*fw0, *fw1}, "", nil)
ConvertFrameworksToSummaryDetails(&summaryDetails, frameworks, policies)
assert.Equal(t, 2, len(summaryDetails.Frameworks))
assert.Equal(t, 3, len(summaryDetails.Controls))

View File

@@ -2,6 +2,7 @@ package policyhandler
import (
"context"
"encoding/json"
"fmt"
"strings"
@@ -104,7 +105,7 @@ func (policyHandler *PolicyHandler) getScanPolicies(ctx context.Context, policyI
// check if the cached policies are the same as the requested policies, otherwise download the policies
if cachedIdentifiers, identifiersExist := policyHandler.cachedPolicyIdentifiers.Get(); identifiersExist && cautils.StringSlicesAreEqual(cachedIdentifiers, policyIdentifiersSlice) {
logger.L().Info("Using cached policies")
return cachedPolicies, nil
return deepCopyPolicies(cachedPolicies)
}
logger.L().Debug("Cached policies are not the same as the requested policies")
@@ -121,6 +122,20 @@ func (policyHandler *PolicyHandler) getScanPolicies(ctx context.Context, policyI
return policies, err
}
func deepCopyPolicies(src []reporthandling.Framework) ([]reporthandling.Framework, error) {
data, err := json.Marshal(src)
if err != nil {
return nil, err
}
var dst []reporthandling.Framework
err = json.Unmarshal(data, &dst)
if err != nil {
return nil, err
}
return dst, nil
}
func (policyHandler *PolicyHandler) downloadScanPolicies(ctx context.Context, policyIdentifier []cautils.PolicyIdentifier) ([]reporthandling.Framework, error) {
frameworks := []reporthandling.Framework{}

View File

@@ -54,7 +54,7 @@ func (es *ExcludeSelector) GetNamespacesSelectors(resource *schema.GroupVersionR
fieldSelectors := ""
for _, n := range strings.Split(es.namespace, ",") {
if n != "" {
fieldSelectors += getNamespacesSelector(resource, n, "!=") + ","
fieldSelectors = combineFieldSelectors(fieldSelectors, getNamespacesSelector(resource.Resource, n, "!="))
}
}
return []string{fieldSelectors}
@@ -65,21 +65,42 @@ func (is *IncludeSelector) GetNamespacesSelectors(resource *schema.GroupVersionR
fieldSelectors := []string{}
for _, n := range strings.Split(is.namespace, ",") {
if n != "" {
fieldSelectors = append(fieldSelectors, getNamespacesSelector(resource, n, "=="))
fieldSelectors = append(fieldSelectors, getNamespacesSelector(resource.Resource, n, "=="))
}
}
return fieldSelectors
}
func getNamespacesSelector(resource *schema.GroupVersionResource, ns, operator string) string {
fieldSelector := "metadata."
if resource.Resource == "namespaces" {
fieldSelector += "name"
} else if k8sinterface.IsResourceInNamespaceScope(resource.Resource) {
fieldSelector += "namespace"
} else {
func getNamespacesSelector(kind, ns, operator string) string {
if ns == "" {
return ""
}
return fmt.Sprintf("%s%s%s", fieldSelector, operator, ns)
if kind == "namespaces" || kind == "Namespace" {
return getNameFieldSelectorString(ns, operator)
}
if k8sinterface.IsResourceInNamespaceScope(kind) {
return getNamespaceFieldSelectorString(ns, operator)
}
return ""
}
func getNameFieldSelectorString(resourceName, operator string) string {
return fmt.Sprintf("metadata.name%s%s", operator, resourceName)
}
func getNamespaceFieldSelectorString(namespace, operator string) string {
return fmt.Sprintf("metadata.namespace%s%s", operator, namespace)
}
func combineFieldSelectors(selectors ...string) string {
var nonEmptyStrings []string
for i := range selectors {
if selectors[i] != "" {
nonEmptyStrings = append(nonEmptyStrings, selectors[i])
}
}
return strings.Join(nonEmptyStrings, ",")
}

View File

@@ -10,8 +10,13 @@ import (
func TestGetNamespacesSelector(t *testing.T) {
k8sinterface.InitializeMapResourcesMock()
assert.Equal(t, "metadata.namespace==default", getNamespacesSelector(&schema.GroupVersionResource{Version: "v1", Resource: "pods"}, "default", "=="))
assert.Equal(t, "", getNamespacesSelector(&schema.GroupVersionResource{Version: "v1", Resource: "nodes"}, "default", "=="))
assert.Equal(t, "", getNamespacesSelector("pods", "", "=="))
assert.Equal(t, "metadata.namespace==default", getNamespacesSelector("pods", "default", "=="))
assert.Equal(t, "metadata.namespace==default", getNamespacesSelector("Pod", "default", "=="))
assert.Equal(t, "", getNamespacesSelector("nodes", "default", "=="))
assert.Equal(t, "", getNamespacesSelector("Node", "default", "=="))
assert.Equal(t, "metadata.name==kube-system", getNamespacesSelector("namespaces", "kube-system", "=="))
assert.Equal(t, "metadata.name==kube-system", getNamespacesSelector("Namespace", "kube-system", "=="))
}
func TestExcludedNamespacesSelectors(t *testing.T) {
@@ -20,11 +25,11 @@ func TestExcludedNamespacesSelectors(t *testing.T) {
es := NewExcludeSelector("default,ingress")
selectors := es.GetNamespacesSelectors(&schema.GroupVersionResource{Resource: "pods"})
assert.Equal(t, 1, len(selectors))
assert.Equal(t, "metadata.namespace!=default,metadata.namespace!=ingress,", selectors[0])
assert.Equal(t, "metadata.namespace!=default,metadata.namespace!=ingress", selectors[0])
selectors2 := es.GetNamespacesSelectors(&schema.GroupVersionResource{Resource: "namespaces"})
assert.Equal(t, 1, len(selectors2))
assert.Equal(t, "metadata.name!=default,metadata.name!=ingress,", selectors2[0])
assert.Equal(t, "metadata.name!=default,metadata.name!=ingress", selectors2[0])
}
func TestIncludeNamespacesSelectors(t *testing.T) {

View File

@@ -6,7 +6,6 @@ import (
"os"
"path/filepath"
"github.com/armosec/armoapi-go/identifiers"
"github.com/kubescape/k8s-interface/workloadinterface"
"github.com/kubescape/opa-utils/reporthandling"
"k8s.io/apimachinery/pkg/version"
@@ -20,36 +19,43 @@ import (
// FileResourceHandler handle resources from files and URLs
type FileResourceHandler struct {
inputPatterns []string
workloadIdentifier *cautils.WorkloadIdentifier
inputPatterns []string
}
func NewFileResourceHandler(_ context.Context, inputPatterns []string) *FileResourceHandler {
func NewFileResourceHandler(_ context.Context, inputPatterns []string, workloadIdentifier *cautils.WorkloadIdentifier) *FileResourceHandler {
k8sinterface.InitializeMapResourcesMock() // initialize the resource map
return &FileResourceHandler{
inputPatterns: inputPatterns,
inputPatterns: inputPatterns,
workloadIdentifier: workloadIdentifier,
}
}
func (fileHandler *FileResourceHandler) GetResources(ctx context.Context, sessionObj *cautils.OPASessionObj, _ *identifiers.PortalDesignator, progressListener opaprocessor.IJobProgressNotificationClient) (*cautils.K8SResources, map[string]workloadinterface.IMetadata, *cautils.KSResources, error) {
//
// build resources map
// map resources based on framework required resources: map["/group/version/kind"][]<k8s workloads ids>
k8sResources := setK8sResourceMap(sessionObj.Policies)
func (fileHandler *FileResourceHandler) GetResources(ctx context.Context, sessionObj *cautils.OPASessionObj, progressListener opaprocessor.IJobProgressNotificationClient, scanInfo cautils.ScanInfo) (cautils.K8SResources, map[string]workloadinterface.IMetadata, cautils.KSResources, map[string]bool, error) {
allResources := map[string]workloadinterface.IMetadata{}
ksResources := &cautils.KSResources{}
ksResources := cautils.KSResources{}
if len(fileHandler.inputPatterns) == 0 {
return nil, nil, nil, fmt.Errorf("missing input")
return nil, nil, nil, nil, fmt.Errorf("missing input")
}
logger.L().Info("Accessing local objects")
cautils.StartSpinner()
// load resources from all input paths
mappedResources := map[string][]workloadinterface.IMetadata{}
for path := range fileHandler.inputPatterns {
workloadIDToSource, workloads, err := getResourcesFromPath(ctx, fileHandler.inputPatterns[path])
if err != nil {
return nil, allResources, nil, err
var workloadIDToSource map[string]reporthandling.Source
var workloads []workloadinterface.IMetadata
var err error
if scanInfo.ChartPath != "" && scanInfo.FilePath != "" {
workloadIDToSource, workloads, err = getWorkloadFromHelmChart(ctx, scanInfo.ChartPath, scanInfo.FilePath)
} else {
workloadIDToSource, workloads, err = getResourcesFromPath(ctx, fileHandler.inputPatterns[path])
if err != nil {
return nil, allResources, nil, nil, err
}
}
if len(workloads) == 0 {
logger.L().Debug("path ignored because contains only a non-kubernetes file", helpers.String("path", fileHandler.inputPatterns[path]))
@@ -60,26 +66,96 @@ func (fileHandler *FileResourceHandler) GetResources(ctx context.Context, sessio
}
// map all resources: map["/apiVersion/version/kind"][]<k8s workloads>
mappedResources := mapResources(workloads)
// save only relevant resources
for i := range mappedResources {
if _, ok := (*k8sResources)[i]; ok {
ids := []string{}
for j := range mappedResources[i] {
ids = append(ids, mappedResources[i][j].GetID())
allResources[mappedResources[i][j].GetID()] = mappedResources[i][j]
}
(*k8sResources)[i] = append((*k8sResources)[i], ids...)
}
}
addWorkloadsToResourcesMap(mappedResources, workloads)
}
// locate input workload in the mapped resources - if not found or not a valid workload, return error
inputWorkload, err := findWorkloadToScan(mappedResources, fileHandler.workloadIdentifier)
if err != nil {
return nil, nil, nil, nil, err
}
sessionObj.ScannedWorkload = inputWorkload
// build a resources map, based on the policies
// map resources based on framework required resources: map["/group/version/kind"][]<k8s workloads ids>
resourceToQuery, excludedRulesMap := getQueryableResourceMapFromPolicies(fileHandler, sessionObj.Policies, inputWorkload)
k8sResources := resourceToQuery.ToK8sResourceMap()
// save only relevant resources
for i := range mappedResources {
if _, ok := k8sResources[i]; ok {
ids := []string{}
for j := range mappedResources[i] {
ids = append(ids, mappedResources[i][j].GetID())
allResources[mappedResources[i][j].GetID()] = mappedResources[i][j]
}
k8sResources[i] = append(k8sResources[i], ids...)
}
}
// save input workload in resource maps
addWorkloadToResourceMaps(k8sResources, allResources, inputWorkload)
cautils.StopSpinner()
logger.L().Success("Done accessing local objects")
return k8sResources, allResources, ksResources, nil
return k8sResources, allResources, ksResources, excludedRulesMap, nil
}
func getWorkloadFromHelmChart(ctx context.Context, helmPath, workloadPath string) (map[string]reporthandling.Source, []workloadinterface.IMetadata, error) {
clonedRepo, err := cloneGitRepo(&helmPath)
if err != nil {
return nil, nil, err
}
if clonedRepo != "" {
defer os.RemoveAll(clonedRepo)
}
// Get repo root
repoRoot, gitRepo := extractGitRepo(helmPath)
helmSourceToWorkloads, helmSourceToChart := cautils.LoadResourcesFromHelmCharts(ctx, helmPath)
wlSource, _ := helmSourceToWorkloads[workloadPath]
helmChart := helmSourceToChart[workloadPath]
relSource, err := filepath.Rel(repoRoot, helmPath)
if err == nil {
helmPath = relSource
}
var lastCommit reporthandling.LastCommit
if gitRepo != nil {
commitInfo, _ := gitRepo.GetFileLastCommit(helmPath)
if commitInfo != nil {
lastCommit = reporthandling.LastCommit{
Hash: commitInfo.SHA,
Date: commitInfo.Author.Date,
CommitterName: commitInfo.Author.Name,
CommitterEmail: commitInfo.Author.Email,
Message: commitInfo.Message,
}
}
}
workloadSource := reporthandling.Source{
Path: repoRoot,
HelmPath: helmChart.Path,
RelativePath: helmPath,
FileType: reporthandling.SourceTypeHelmChart,
HelmChartName: helmChart.Name,
LastCommit: lastCommit,
}
workloadIDToSource := make(map[string]reporthandling.Source, 0)
workloadIDToSource[wlSource[0].GetID()] = workloadSource
workloads := []workloadinterface.IMetadata{}
workloads = append(workloads, wlSource...)
return workloadIDToSource, workloads, nil
}
func getResourcesFromPath(ctx context.Context, path string) (map[string]reporthandling.Source, []workloadinterface.IMetadata, error) {
@@ -95,13 +171,7 @@ func getResourcesFromPath(ctx context.Context, path string) (map[string]reportha
}
// Get repo root
repoRoot := ""
gitRepo, err := cautils.NewLocalGitRepository(path)
if err == nil && gitRepo != nil {
repoRoot, _ = gitRepo.GetRootDir()
} else {
repoRoot, _ = filepath.Abs(path)
}
repoRoot, gitRepo := extractGitRepo(path)
// when scanning a single file, we consider the repository root to be
// the directory of the scanned file
@@ -110,10 +180,7 @@ func getResourcesFromPath(ctx context.Context, path string) (map[string]reportha
}
// load resource from local file system
sourceToWorkloads, err := cautils.LoadResourcesFromFiles(ctx, path, repoRoot)
if err != nil {
return nil, nil, err
}
sourceToWorkloads := cautils.LoadResourcesFromFiles(ctx, path, repoRoot)
// update workloads and workloadIDToSource
var warnIssued bool
@@ -156,10 +223,21 @@ func getResourcesFromPath(ctx context.Context, path string) (map[string]reportha
}
}
workloadSource := reporthandling.Source{
RelativePath: relSource,
FileType: filetype,
LastCommit: lastCommit,
var workloadSource reporthandling.Source
if clonedRepo != "" {
workloadSource = reporthandling.Source{
Path: "",
RelativePath: relSource,
FileType: filetype,
LastCommit: lastCommit,
}
} else {
workloadSource = reporthandling.Source{
Path: repoRoot,
RelativePath: relSource,
FileType: filetype,
LastCommit: lastCommit,
}
}
for i := range ws {
@@ -172,10 +250,10 @@ func getResourcesFromPath(ctx context.Context, path string) (map[string]reportha
}
// load resources from helm charts
helmSourceToWorkloads, helmSourceToChartName := cautils.LoadResourcesFromHelmCharts(ctx, path)
helmSourceToWorkloads, helmSourceToChart := cautils.LoadResourcesFromHelmCharts(ctx, path)
for source, ws := range helmSourceToWorkloads {
workloads = append(workloads, ws...)
helmChartName := helmSourceToChartName[source]
helmChart := helmSourceToChart[source]
relSource, err := filepath.Rel(repoRoot, source)
if err == nil {
@@ -197,9 +275,11 @@ func getResourcesFromPath(ctx context.Context, path string) (map[string]reportha
}
workloadSource := reporthandling.Source{
Path: repoRoot,
HelmPath: helmChart.Path,
RelativePath: source,
FileType: reporthandling.SourceTypeHelmChart,
HelmChartName: helmChartName,
HelmChartName: helmChart.Name,
LastCommit: lastCommit,
}
@@ -253,6 +333,21 @@ func getResourcesFromPath(ctx context.Context, path string) (map[string]reportha
return workloadIDToSource, workloads, nil
}
func extractGitRepo(path string) (string, *cautils.LocalGitRepository) {
repoRoot := ""
gitRepo, err := cautils.NewLocalGitRepository(path)
if err == nil && gitRepo != nil {
repoRoot, _ = gitRepo.GetRootDir()
} else {
repoRoot, _ = filepath.Abs(path)
}
return repoRoot, gitRepo
}
func (fileHandler *FileResourceHandler) GetClusterAPIServerInfo(_ context.Context) *version.Info {
return nil
}
func (fileHandler *FileResourceHandler) GetWorkloadParentKind(resource workloadinterface.IWorkload) string {
return ""
}

View File

@@ -33,10 +33,7 @@ func cloneGitRepo(path *string) (string, error) {
return clonedDir, nil
}
// build resources map
func mapResources(workloads []workloadinterface.IMetadata) map[string][]workloadinterface.IMetadata {
allResources := map[string][]workloadinterface.IMetadata{}
func addWorkloadsToResourcesMap(allResources map[string][]workloadinterface.IMetadata, workloads []workloadinterface.IMetadata) {
for i := range workloads {
groupVersionResource, err := k8sinterface.GetGroupVersionResource(workloads[i].GetKind())
if err != nil {
@@ -58,8 +55,6 @@ func mapResources(workloads []workloadinterface.IMetadata) map[string][]workload
allResources[resourceTriplets] = []workloadinterface.IMetadata{workloads[i]}
}
}
return allResources
}
/* unused for now
@@ -85,3 +80,36 @@ func addCommitData(input string, workloadIDToSource map[string]reporthandling.So
}
}
*/
func findWorkloadToScan(mappedResources map[string][]workloadinterface.IMetadata, workloadIdentifier *cautils.WorkloadIdentifier) (workloadinterface.IWorkload, error) {
if workloadIdentifier == nil {
return nil, nil
}
wls := []workloadinterface.IWorkload{}
for _, resources := range mappedResources {
for _, r := range resources {
if r.GetKind() == workloadIdentifier.Kind && r.GetName() == workloadIdentifier.Name {
if workloadIdentifier.Namespace != "" && workloadIdentifier.Namespace != r.GetNamespace() {
continue
}
if workloadIdentifier.ApiVersion != "" && workloadIdentifier.ApiVersion != r.GetApiVersion() {
continue
}
if k8sinterface.IsTypeWorkload(r.GetObject()) {
wl := workloadinterface.NewWorkloadObj(r.GetObject())
wls = append(wls, wl)
}
}
}
}
if len(wls) == 0 {
return nil, fmt.Errorf("workload '%s' not found", workloadIdentifier.String())
} else if len(wls) > 1 {
return nil, fmt.Errorf("more than one workload found for '%s'", workloadIdentifier.String())
}
return wls[0], nil
}

View File

@@ -0,0 +1,100 @@
package resourcehandler
import (
"testing"
"github.com/kubescape/k8s-interface/workloadinterface"
"github.com/kubescape/kubescape/v2/core/cautils"
"github.com/kubescape/opa-utils/reporthandling"
"github.com/stretchr/testify/assert"
)
func mockWorkloadWithSource(apiVersion, kind, namespace, name, ownerReferenceKind, source string) workloadinterface.IMetadata {
wl := mockWorkload(apiVersion, kind, namespace, name, ownerReferenceKind)
resource := reporthandling.NewResourceIMetadata(wl)
resource.SetSource(&reporthandling.Source{
Path: source,
RelativePath: source,
})
return resource
}
func TestFindWorkloadToScan(t *testing.T) {
mappedResources := map[string][]workloadinterface.IMetadata{
"/v1/pods": {
mockWorkloadWithSource("v1", "Pod", "default", "nginx", "", "/fileA.yaml"),
mockWorkloadWithSource("v1", "Pod", "default", "nginx", "", "/fileB.yaml"),
mockWorkloadWithSource("v1", "Pod", "", "mariadb", "", "/fileB.yaml"),
},
}
tt := []struct {
name string
workloadIdentifier *cautils.WorkloadIdentifier
expectedResourceName string
expectErr bool
expectedErrorString string
}{
{
name: "workload identifier is nil",
workloadIdentifier: nil,
expectedResourceName: "",
expectErr: false,
},
{
name: "multiple workloads match",
workloadIdentifier: &cautils.WorkloadIdentifier{
Namespace: "default",
Kind: "Pod",
Name: "nginx",
ApiVersion: "v1",
},
expectedResourceName: "",
expectErr: true,
expectedErrorString: "more than one workload found for 'Pod/nginx'",
},
{
name: "single workload match",
workloadIdentifier: &cautils.WorkloadIdentifier{
Namespace: "",
Kind: "Pod",
Name: "mariadb",
ApiVersion: "v1",
},
expectedResourceName: "mariadb",
expectErr: false,
expectedErrorString: "",
},
{
name: "no workload match",
workloadIdentifier: &cautils.WorkloadIdentifier{
Namespace: "",
Kind: "Deployment",
Name: "notfound",
ApiVersion: "apps/v1",
},
expectedResourceName: "",
expectErr: true,
expectedErrorString: "not found",
},
}
for _, tc := range tt {
t.Run(tc.name, func(t *testing.T) {
resource, err := findWorkloadToScan(mappedResources, tc.workloadIdentifier)
if (err != nil) != tc.expectErr {
t.Errorf("findWorkloadToScan() error = %v, expectErr %v", err, tc.expectErr)
return
}
if tc.expectErr {
assert.ErrorContains(t, err, tc.expectedErrorString)
}
if tc.expectedResourceName != "" {
assert.Equal(t, tc.expectedResourceName, resource.GetName())
}
})
}
}

View File

@@ -4,10 +4,6 @@ import (
"context"
_ "embed"
"encoding/json"
"errors"
"fmt"
"os"
"path/filepath"
"testing"
"github.com/kubescape/k8s-interface/k8sinterface"
@@ -25,11 +21,6 @@ import (
clientcmdapi "k8s.io/client-go/tools/clientcmd/api"
)
func emptyDirectory() string {
o, _ := os.Getwd()
return filepath.Join(filepath.Dir(o), ".", "cautils", "testdata", "emptyDirectory")
}
var (
//go:embed testdata/kubeconfig_mock.json
kubeConfigMock string
@@ -241,7 +232,7 @@ func getResourceHandlerMock() *K8sResourceHandler {
Context: context.Background(),
}
return NewK8sResourceHandler(k8s, &EmptySelector{}, nil, nil, nil)
return NewK8sResourceHandler(k8s, &EmptySelector{}, nil, nil, nil, nil)
}
func Test_CollectResources(t *testing.T) {
resourceHandler := getResourceHandlerMock()
@@ -267,10 +258,3 @@ func Test_CollectResources(t *testing.T) {
}, "Cluster named .*eks.* without a cloud config panics on non-cluster scan !")
}
func Test_getResourcesFromPath(t *testing.T) {
expectedError := errors.New(fmt.Sprintf(cautils.ErrNoFilesToScan, emptyDirectory())).Error()
_, _, err := getResourcesFromPath(context.TODO(), emptyDirectory())
assert.NotEqual(t, err, nil)
assert.Contains(t, err.Error(), expectedError)
}

View File

@@ -19,8 +19,7 @@ import (
clientcmdapi "k8s.io/client-go/tools/clientcmd/api"
)
// CollectResources uses the provided resource handler to collect resources and returns an updated OPASessionObj
func CollectResources(ctx context.Context, rsrcHandler IResourceHandler, policyIdentifier []cautils.PolicyIdentifier, opaSessionObj *cautils.OPASessionObj, progressListener opaprocessor.IJobProgressNotificationClient) error {
func CollectResources(ctx context.Context, rsrcHandler IResourceHandler, policyIdentifier []cautils.PolicyIdentifier, opaSessionObj *cautils.OPASessionObj, progressListener opaprocessor.IJobProgressNotificationClient, scanInfo cautils.ScanInfo) error {
ctx, span := otel.Tracer("").Start(ctx, "resourcehandler.CollectResources")
defer span.End()
opaSessionObj.Report.ClusterAPIServerInfo = rsrcHandler.GetClusterAPIServerInfo(ctx)
@@ -30,16 +29,17 @@ func CollectResources(ctx context.Context, rsrcHandler IResourceHandler, policyI
setCloudMetadata(opaSessionObj)
}
resourcesMap, allResources, ksResources, err := rsrcHandler.GetResources(ctx, opaSessionObj, &policyIdentifier[0].Designators, progressListener)
resourcesMap, allResources, ksResources, excludedRulesMap, err := rsrcHandler.GetResources(ctx, opaSessionObj, progressListener, scanInfo)
if err != nil {
return err
}
opaSessionObj.K8SResources = resourcesMap
opaSessionObj.AllResources = allResources
opaSessionObj.ArmoResource = ksResources
opaSessionObj.KubescapeResource = ksResources
opaSessionObj.ExcludedRules = excludedRulesMap
if (opaSessionObj.K8SResources == nil || len(*opaSessionObj.K8SResources) == 0) && (opaSessionObj.ArmoResource == nil || len(*opaSessionObj.ArmoResource) == 0) {
if (opaSessionObj.K8SResources == nil || len(opaSessionObj.K8SResources) == 0) && (opaSessionObj.KubescapeResource == nil || len(opaSessionObj.KubescapeResource) == 0) {
return fmt.Errorf("empty list of resources")
}

View File

@@ -3,7 +3,6 @@ package resourcehandler
import (
"context"
"github.com/armosec/armoapi-go/identifiers"
"github.com/kubescape/k8s-interface/workloadinterface"
"github.com/kubescape/kubescape/v2/core/cautils"
"github.com/kubescape/kubescape/v2/core/pkg/opaprocessor"
@@ -11,6 +10,7 @@ import (
)
type IResourceHandler interface {
GetResources(context.Context, *cautils.OPASessionObj, *identifiers.PortalDesignator, opaprocessor.IJobProgressNotificationClient) (*cautils.K8SResources, map[string]workloadinterface.IMetadata, *cautils.KSResources, error)
GetResources(context.Context, *cautils.OPASessionObj, opaprocessor.IJobProgressNotificationClient, cautils.ScanInfo) (cautils.K8SResources, map[string]workloadinterface.IMetadata, cautils.KSResources, map[string]bool, error)
GetClusterAPIServerInfo(ctx context.Context) *version.Info
GetWorkloadParentKind(workloadinterface.IWorkload) string
}

View File

@@ -20,15 +20,12 @@ import (
"github.com/kubescape/k8s-interface/k8sinterface"
"github.com/kubescape/k8s-interface/workloadinterface"
"github.com/armosec/armoapi-go/identifiers"
v1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
k8slabels "k8s.io/apimachinery/pkg/labels"
"k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/apimachinery/pkg/version"
"k8s.io/client-go/dynamic"
)
type cloudResourceGetter func(string, string) (workloadinterface.IMetadata, error)
@@ -41,49 +38,56 @@ var cloudResourceGetterMapping = map[string]cloudResourceGetter{
}
type K8sResourceHandler struct {
k8s *k8sinterface.KubernetesApi
hostSensorHandler hostsensorutils.IHostSensor
fieldSelector IFieldSelector
rbacObjectsAPI *cautils.RBACObjects
registryAdaptors *RegistryAdaptors
k8s *k8sinterface.KubernetesApi
hostSensorHandler hostsensorutils.IHostSensor
fieldSelector IFieldSelector
rbacObjectsAPI *cautils.RBACObjects
registryAdaptors *RegistryAdaptors
workloadIdentifier *cautils.WorkloadIdentifier
}
func NewK8sResourceHandler(k8s *k8sinterface.KubernetesApi, fieldSelector IFieldSelector, hostSensorHandler hostsensorutils.IHostSensor, rbacObjects *cautils.RBACObjects, registryAdaptors *RegistryAdaptors) *K8sResourceHandler {
func NewK8sResourceHandler(k8s *k8sinterface.KubernetesApi, fieldSelector IFieldSelector, hostSensorHandler hostsensorutils.IHostSensor, rbacObjects *cautils.RBACObjects, registryAdaptors *RegistryAdaptors, workloadIdentifier *cautils.WorkloadIdentifier) *K8sResourceHandler {
return &K8sResourceHandler{
k8s: k8s,
fieldSelector: fieldSelector,
hostSensorHandler: hostSensorHandler,
rbacObjectsAPI: rbacObjects,
registryAdaptors: registryAdaptors,
k8s: k8s,
fieldSelector: fieldSelector,
hostSensorHandler: hostSensorHandler,
rbacObjectsAPI: rbacObjects,
registryAdaptors: registryAdaptors,
workloadIdentifier: workloadIdentifier,
}
}
func (k8sHandler *K8sResourceHandler) GetResources(ctx context.Context, sessionObj *cautils.OPASessionObj, designator *identifiers.PortalDesignator, progressListener opaprocessor.IJobProgressNotificationClient) (*cautils.K8SResources, map[string]workloadinterface.IMetadata, *cautils.KSResources, error) {
allResources := map[string]workloadinterface.IMetadata{}
func (k8sHandler *K8sResourceHandler) GetResources(ctx context.Context, sessionObj *cautils.OPASessionObj, progressListener opaprocessor.IJobProgressNotificationClient, scanInfo cautils.ScanInfo) (cautils.K8SResources, map[string]workloadinterface.IMetadata, cautils.KSResources, map[string]bool, error) {
// get k8s resources
logger.L().Info("Accessing Kubernetes objects")
cautils.StartSpinner()
workload, err := k8sHandler.findWorkloadToScan(k8sHandler.workloadIdentifier)
if err != nil {
return nil, nil, nil, nil, err
}
sessionObj.ScannedWorkload = workload
resourceToControl := make(map[string][]string)
// build resources map
// map resources based on framework required resources: map["/group/version/kind"][]<k8s workloads ids>
k8sResourcesMap := setK8sResourceMap(sessionObj.Policies)
// get namespace and labels from designator (ignore cluster labels)
_, namespace, labels := identifiers.DigestPortalDesignator(designator)
// pull k8s recourses
queryableResources, excludedRulesMap := getQueryableResourceMapFromPolicies(k8sHandler, sessionObj.Policies, workload)
ksResourceMap := setKSResourceMap(sessionObj.Policies, resourceToControl)
// map of Kubescape resources to control_ids
sessionObj.ResourceToControlsMap = resourceToControl
if err := k8sHandler.pullResources(k8sResourcesMap, allResources, namespace, labels); err != nil {
// pull k8s resources
k8sResourcesMap, allResources, err := k8sHandler.pullResources(queryableResources)
if err != nil {
cautils.StopSpinner()
return k8sResourcesMap, allResources, ksResourceMap, err
return k8sResourcesMap, allResources, ksResourceMap, nil, err
}
// add workload to k8s resources map (for single workload scan)
addWorkloadToResourceMaps(k8sResourcesMap, allResources, workload)
metrics.UpdateKubernetesResourcesCount(ctx, int64(len(allResources)))
numberOfWorkerNodes, err := k8sHandler.pullWorkerNodesNumber()
@@ -153,10 +157,53 @@ func (k8sHandler *K8sResourceHandler) GetResources(ctx context.Context, sessionO
}
}
return k8sResourcesMap, allResources, ksResourceMap, nil
return k8sResourcesMap, allResources, ksResourceMap, excludedRulesMap, nil
}
func (k8sHandler *K8sResourceHandler) collectCloudResources(ctx context.Context, sessionObj *cautils.OPASessionObj, allResources map[string]workloadinterface.IMetadata, ksResourceMap *cautils.KSResources, cloudResources []string, progressListener opaprocessor.IJobProgressNotificationClient) error {
func (k8sHandler *K8sResourceHandler) findWorkloadToScan(workloadIdentifier *cautils.WorkloadIdentifier) (workloadinterface.IWorkload, error) {
if workloadIdentifier == nil {
return nil, nil
}
var wlIdentifierString string
if workloadIdentifier.ApiVersion != "" {
wlIdentifierString = fmt.Sprintf("%s/%s", workloadIdentifier.ApiVersion, workloadIdentifier.Kind)
} else {
wlIdentifierString = workloadIdentifier.Kind
}
gvr, err := k8sinterface.GetGroupVersionResource(wlIdentifierString)
if err != nil {
return nil, err
}
fieldSelectors := getNameFieldSelectorString(workloadIdentifier.Name, "=")
if workloadIdentifier.Namespace != "" && k8sinterface.IsNamespaceScope(&gvr) {
fieldSelectors = combineFieldSelectors(fieldSelectors, getNamespaceFieldSelectorString(workloadIdentifier.Namespace, "="))
}
result, err := k8sHandler.pullSingleResource(&gvr, nil, fieldSelectors)
if err != nil {
return nil, fmt.Errorf("failed to get resource %s, reason: %v", workloadIdentifier.String(), err)
}
if len(result) == 0 {
return nil, fmt.Errorf("%s was not found", workloadIdentifier.String())
}
if len(result) > 1 {
return nil, fmt.Errorf("more than one resource found for %s", workloadIdentifier.String())
}
metaObjs := ConvertMapListToMeta(k8sinterface.ConvertUnstructuredSliceToMap(result))
if !k8sinterface.IsTypeWorkload(metaObjs[0].GetObject()) {
return nil, fmt.Errorf("%s is not a valid Kubernetes workload", workloadIdentifier.String())
}
wl := workloadinterface.NewWorkloadObj(metaObjs[0].GetObject())
return wl, nil
}
func (k8sHandler *K8sResourceHandler) collectCloudResources(ctx context.Context, sessionObj *cautils.OPASessionObj, allResources map[string]workloadinterface.IMetadata, ksResourceMap cautils.KSResources, cloudResources []string, progressListener opaprocessor.IJobProgressNotificationClient) error {
clusterName := cautils.ClusterName
provider := cloudsupport.GetCloudProvider(clusterName)
if provider == "" {
@@ -197,7 +244,7 @@ func (k8sHandler *K8sResourceHandler) collectCloudResources(ctx context.Context,
}
allResources[wl.GetID()] = wl
(*ksResourceMap)[fmt.Sprintf("%s/%s", wl.GetApiVersion(), wl.GetKind())] = []string{wl.GetID()}
ksResourceMap[fmt.Sprintf("%s/%s", wl.GetApiVersion(), wl.GetKind())] = []string{wl.GetID()}
}
logger.L().Success("Downloaded cloud resources")
@@ -222,14 +269,14 @@ func cloudResourceRequired(cloudResources []string, resource string) bool {
return false
}
func (k8sHandler *K8sResourceHandler) collectAPIServerInfoResource(allResources map[string]workloadinterface.IMetadata, ksResourceMap *cautils.KSResources) error {
func (k8sHandler *K8sResourceHandler) collectAPIServerInfoResource(allResources map[string]workloadinterface.IMetadata, ksResourceMap cautils.KSResources) error {
clusterAPIServerInfo, err := k8sHandler.k8s.DiscoveryClient.ServerVersion()
if err != nil {
return err
}
resource := cloudsupport.NewApiServerVersionInfo(clusterAPIServerInfo)
allResources[resource.GetID()] = resource
(*ksResourceMap)[fmt.Sprintf("%s/%s", resource.GetApiVersion(), resource.GetKind())] = []string{resource.GetID()}
ksResourceMap[fmt.Sprintf("%s/%s", resource.GetApiVersion(), resource.GetKind())] = []string{resource.GetID()}
return nil
}
@@ -267,13 +314,15 @@ func setMapNamespaceToNumOfResources(ctx context.Context, allResources map[strin
sessionObj.SetMapNamespaceToNumberOfResources(mapNamespaceToNumberOfResources)
}
func (k8sHandler *K8sResourceHandler) pullResources(k8sResources *cautils.K8SResources, allResources map[string]workloadinterface.IMetadata, namespace string, labels map[string]string) error {
func (k8sHandler *K8sResourceHandler) pullResources(queryableResources QueryableResources) (cautils.K8SResources, map[string]workloadinterface.IMetadata, error) {
k8sResources := queryableResources.ToK8sResourceMap()
allResources := map[string]workloadinterface.IMetadata{}
var errs error
for groupResource := range *k8sResources {
apiGroup, apiVersion, resource := k8sinterface.StringToResourceGroup(groupResource)
for _, qr := range queryableResources {
apiGroup, apiVersion, resource := k8sinterface.StringToResourceGroup(qr.GroupVersionResourceTriplet)
gvr := schema.GroupVersionResource{Group: apiGroup, Version: apiVersion, Resource: resource}
result, err := k8sHandler.pullSingleResource(&gvr, namespace, labels)
result, err := k8sHandler.pullSingleResource(&gvr, nil, qr.FieldSelectors)
if err != nil {
if !strings.Contains(err.Error(), "the server could not find the requested resource") {
// handle error
@@ -290,19 +339,44 @@ func (k8sHandler *K8sResourceHandler) pullResources(k8sResources *cautils.K8SRes
for i := range metaObjs {
allResources[metaObjs[i].GetID()] = metaObjs[i]
}
(*k8sResources)[groupResource] = workloadinterface.ListMetaIDs(metaObjs)
key := qr.GroupVersionResourceTriplet
if _, ok := k8sResources[key]; !ok {
k8sResources[key] = workloadinterface.ListMetaIDs(metaObjs)
} else {
k8sResources[key] = append(k8sResources[key], workloadinterface.ListMetaIDs(metaObjs)...)
}
}
return errs
return k8sResources, allResources, errs
// return errs
}
func (k8sHandler *K8sResourceHandler) pullSingleResource(resource *schema.GroupVersionResource, namespace string, labels map[string]string) ([]unstructured.Unstructured, error) {
func (k8sHandler *K8sResourceHandler) GetWorkloadParentKind(workload workloadinterface.IWorkload) string {
if workload == nil {
return ""
}
// CalculateWorkloadParentRecursive return the kind of the parent workload
// In case the given workload has no parent, it will return the kind and name of the workload itself
// We return the parent kind only if it is different from the workload kind
if kind, name, _ := k8sHandler.k8s.CalculateWorkloadParentRecursive(workload); kind != workload.GetKind() && name != workload.GetName() {
return kind
}
return ""
}
func (k8sHandler *K8sResourceHandler) pullSingleResource(resource *schema.GroupVersionResource, labels map[string]string, fields string) ([]unstructured.Unstructured, error) {
resourceList := []unstructured.Unstructured{}
// set labels
listOptions := metav1.ListOptions{}
fieldSelectors := k8sHandler.fieldSelector.GetNamespacesSelectors(resource)
for i := range fieldSelectors {
listOptions.FieldSelector = fieldSelectors[i]
if fieldSelectors[i] != "" {
listOptions.FieldSelector = combineFieldSelectors(fieldSelectors[i], fields)
} else if fields != "" {
listOptions.FieldSelector = fields
}
if len(labels) > 0 {
set := k8slabels.Set(labels)
@@ -310,21 +384,12 @@ func (k8sHandler *K8sResourceHandler) pullSingleResource(resource *schema.GroupV
}
// set dynamic object
var clientResource dynamic.ResourceInterface
if namespace != "" {
clientResource = k8sHandler.k8s.DynamicClient.Resource(*resource)
} else if k8sinterface.IsNamespaceScope(resource) {
clientResource = k8sHandler.k8s.DynamicClient.Resource(*resource).Namespace(namespace)
} else if k8sHandler.fieldSelector.GetClusterScope(resource) {
clientResource = k8sHandler.k8s.DynamicClient.Resource(*resource)
} else {
continue
}
clientResource := k8sHandler.k8s.DynamicClient.Resource(*resource)
// list resources
result, err := clientResource.List(context.Background(), listOptions)
if err != nil || result == nil {
return nil, fmt.Errorf("failed to get resource: %v, namespace: %s, labelSelector: %v, reason: %v", resource, namespace, listOptions.LabelSelector, err)
return nil, fmt.Errorf("failed to get resource: %v, labelSelector: %v, fieldSelector: %v, reason: %v", resource, listOptions.LabelSelector, listOptions.FieldSelector, err)
}
resourceList = append(resourceList, result.Items...)
@@ -344,7 +409,7 @@ func ConvertMapListToMeta(resourceMap []map[string]interface{}) []workloadinterf
return workloads
}
func (k8sHandler *K8sResourceHandler) collectHostResources(ctx context.Context, allResources map[string]workloadinterface.IMetadata, ksResourceMap *cautils.KSResources) (map[string]apis.StatusInfo, error) {
func (k8sHandler *K8sResourceHandler) collectHostResources(ctx context.Context, allResources map[string]workloadinterface.IMetadata, ksResourceMap cautils.KSResources) (map[string]apis.StatusInfo, error) {
logger.L().Debug("Collecting host scanner resources")
hostResources, infoMap, err := k8sHandler.hostSensorHandler.CollectResources(ctx)
if err != nil {
@@ -356,11 +421,11 @@ func (k8sHandler *K8sResourceHandler) collectHostResources(ctx context.Context,
groupResource := k8sinterface.JoinResourceTriplets(group, version, hostResources[rscIdx].GetKind())
allResources[hostResources[rscIdx].GetID()] = &hostResources[rscIdx]
grpResourceList, ok := (*ksResourceMap)[groupResource]
grpResourceList, ok := ksResourceMap[groupResource]
if !ok {
grpResourceList = make([]string, 0)
}
(*ksResourceMap)[groupResource] = append(grpResourceList, hostResources[rscIdx].GetID())
ksResourceMap[groupResource] = append(grpResourceList, hostResources[rscIdx].GetID())
}
return infoMap, nil
}

View File

@@ -52,7 +52,7 @@ var (
)
func isEmptyImgVulns(ksResourcesMap cautils.KSResources) bool {
imgVulnResources := cautils.MapImageVulnResources(&ksResourcesMap)
imgVulnResources := cautils.MapImageVulnResources(ksResourcesMap)
for _, resource := range imgVulnResources {
if val, ok := ksResourcesMap[resource]; ok {
if len(val) > 0 {
@@ -63,23 +63,7 @@ func isEmptyImgVulns(ksResourcesMap cautils.KSResources) bool {
return true
}
func setK8sResourceMap(frameworks []reporthandling.Framework) *cautils.K8SResources {
k8sResources := make(cautils.K8SResources)
complexMap := setComplexK8sResourceMap(frameworks)
for group := range complexMap {
for version := range complexMap[group] {
for resource := range complexMap[group][version] {
groupResources := k8sinterface.ResourceGroupToString(group, version, resource)
for _, groupResource := range groupResources {
k8sResources[groupResource] = nil
}
}
}
}
return &k8sResources
}
func setKSResourceMap(frameworks []reporthandling.Framework, resourceToControl map[string][]string) *cautils.KSResources {
func setKSResourceMap(frameworks []reporthandling.Framework, resourceToControl map[string][]string) cautils.KSResources {
ksResources := make(cautils.KSResources)
complexMap := setComplexKSResourceMap(frameworks, resourceToControl)
for group := range complexMap {
@@ -92,21 +76,7 @@ func setKSResourceMap(frameworks []reporthandling.Framework, resourceToControl m
}
}
}
return &ksResources
}
func setComplexK8sResourceMap(frameworks []reporthandling.Framework) map[string]map[string]map[string]interface{} {
k8sResources := make(map[string]map[string]map[string]interface{})
for _, framework := range frameworks {
for _, control := range framework.Controls {
for _, rule := range control.Rules {
for _, match := range rule.Match {
insertResources(k8sResources, match)
}
}
}
}
return k8sResources
return ksResources
}
// [group][versionn][resource]
@@ -152,24 +122,6 @@ func insertControls(resource string, resourceToControl map[string][]string, cont
}
}
func insertResources(k8sResources map[string]map[string]map[string]interface{}, match reporthandling.RuleMatchObjects) {
for _, apiGroup := range match.APIGroups {
if v, ok := k8sResources[apiGroup]; !ok || v == nil {
k8sResources[apiGroup] = make(map[string]map[string]interface{})
}
for _, apiVersions := range match.APIVersions {
if v, ok := k8sResources[apiGroup][apiVersions]; !ok || v == nil {
k8sResources[apiGroup][apiVersions] = make(map[string]interface{})
}
for _, resource := range match.Resources {
if _, ok := k8sResources[apiGroup][apiVersions][resource]; !ok {
k8sResources[apiGroup][apiVersions][resource] = nil
}
}
}
}
}
func insertKSResourcesAndControls(k8sResources map[string]map[string]map[string]interface{}, match reporthandling.RuleMatchObjects, resourceToControl map[string][]string, control reporthandling.Control) {
for _, apiGroup := range match.APIGroups {
if v, ok := k8sResources[apiGroup]; !ok || v == nil {

View File

@@ -1,31 +1,12 @@
package resourcehandler
import (
"github.com/kubescape/k8s-interface/k8sinterface"
"github.com/kubescape/kubescape/v2/core/cautils"
"github.com/kubescape/opa-utils/reporthandling"
"github.com/stretchr/testify/assert"
"testing"
)
func TestGetK8sResources(t *testing.T) {
// getK8sResources
}
func TestSetResourceMap(t *testing.T) {
k8sinterface.InitializeMapResourcesMock()
framework := reporthandling.MockFrameworkA()
k8sResources := setK8sResourceMap([]reporthandling.Framework{*framework})
resources := k8sinterface.ResourceGroupToString("*", "v1", "Pod")
if len(resources) == 0 {
t.Error("expected resources")
}
_, ok := (*k8sResources)[resources[0]]
if !ok {
t.Errorf("missing: 'apps'. k8sResources: %v", k8sResources)
}
}
func TestSsEmptyImgVulns(t *testing.T) {
ksResourcesMap := make(cautils.KSResources, 0)
ksResourcesMap["container.googleapis.com/v1"] = []string{"fsdfds"}
@@ -38,59 +19,3 @@ func TestSsEmptyImgVulns(t *testing.T) {
ksResourcesMap["bla"] = []string{"blu"}
assert.Equal(t, true, isEmptyImgVulns(ksResourcesMap))
}
func TestInsertK8sResources(t *testing.T) {
// insertK8sResources
k8sResources := make(map[string]map[string]map[string]interface{})
match1 := reporthandling.RuleMatchObjects{
APIGroups: []string{"apps"},
APIVersions: []string{"v1", "v1beta"},
Resources: []string{"pods"},
}
match2 := reporthandling.RuleMatchObjects{
APIGroups: []string{"apps"},
APIVersions: []string{"v1"},
Resources: []string{"deployments"},
}
match3 := reporthandling.RuleMatchObjects{
APIGroups: []string{"core"},
APIVersions: []string{"v1"},
Resources: []string{"secrets"},
}
insertResources(k8sResources, match1)
insertResources(k8sResources, match2)
insertResources(k8sResources, match3)
apiGroup1, ok := k8sResources["apps"]
if !ok {
t.Errorf("missing: 'apps'. k8sResources: %v", k8sResources)
return
}
apiVersion1, ok := apiGroup1["v1"]
if !ok {
t.Errorf("missing: 'v1'. k8sResources: %v", k8sResources)
return
}
_, ok = apiVersion1["pods"]
if !ok {
t.Errorf("missing: 'pods'. k8sResources: %v", k8sResources)
}
_, ok = apiVersion1["deployments"]
if !ok {
t.Errorf("missing: 'deployments'. k8sResources: %v", k8sResources)
}
apiVersion2, ok := apiGroup1["v1beta"]
if !ok {
t.Errorf("missing: 'v1beta'. k8sResources: %v", k8sResources)
return
}
_, ok = apiVersion2["pods"]
if !ok {
t.Errorf("missing: 'pods'. k8sResources: %v", k8sResources)
}
_, ok = k8sResources["core"]
if !ok {
t.Errorf("missing: 'core'. k8sResources: %v", k8sResources)
return
}
}

View File

@@ -0,0 +1,56 @@
package resourcehandler
import (
"fmt"
"github.com/kubescape/kubescape/v2/core/cautils"
)
type QueryableResources map[string]QueryableResource
// QueryableResource is a struct that holds a representation of a resource we would like to query (from the K8S API, or from other sources)
type QueryableResource struct {
// <api group/api version/resource>
GroupVersionResourceTriplet string
// metadata.name==<resource name>, metadata.namespace==<resource namespace> etc.
FieldSelectors string
}
func (qr *QueryableResource) String() string {
if qr.FieldSelectors == "" {
return qr.GroupVersionResourceTriplet
}
return fmt.Sprintf("%s/%s", qr.GroupVersionResourceTriplet, qr.FieldSelectors)
}
func (qr *QueryableResource) Copy() QueryableResource {
return QueryableResource{
GroupVersionResourceTriplet: qr.GroupVersionResourceTriplet,
FieldSelectors: qr.FieldSelectors,
}
}
func (qr *QueryableResource) AddFieldSelector(fieldSelector string) {
if fieldSelector == "" {
return
}
if qr.FieldSelectors == "" {
qr.FieldSelectors = fieldSelector
return
}
qr.FieldSelectors = combineFieldSelectors(qr.FieldSelectors, fieldSelector)
}
func (qrm QueryableResources) ToK8sResourceMap() cautils.K8SResources {
resources := make(cautils.K8SResources)
for _, qr := range qrm {
resources[qr.GroupVersionResourceTriplet] = nil
}
return resources
}
func (qrm QueryableResources) Add(qr QueryableResource) {
qrm[qr.String()] = qr
}

View File

@@ -0,0 +1,126 @@
package resourcehandler
import (
"fmt"
"reflect"
"testing"
"github.com/kubescape/kubescape/v2/core/cautils"
)
func TestString(t *testing.T) {
tt := []struct {
name string
input QueryableResource
output string
}{
{
name: "Empty field selectors",
input: QueryableResource{GroupVersionResourceTriplet: "/v1/pods", FieldSelectors: ""},
output: "/v1/pods",
},
{
name: "Non-empty field selectors",
input: QueryableResource{GroupVersionResourceTriplet: "/v1/pods", FieldSelectors: "fs1"},
output: "/v1/pods/fs1",
},
}
for _, tc := range tt {
t.Run(tc.name, func(t *testing.T) {
result := tc.input.String()
if result != tc.output {
t.Errorf("Expected: %s, got: %s", tc.output, result)
}
})
}
}
func TestCopy(t *testing.T) {
rsrc := &QueryableResource{GroupVersionResourceTriplet: "gvr1", FieldSelectors: "fs1"}
copy := rsrc.Copy()
if copy != *rsrc {
t.Errorf("Expected: %v, got: %v", *rsrc, copy)
}
if fmt.Sprintf("%p", rsrc) == fmt.Sprintf("%p", &copy) {
t.Errorf("pointers of original object and copy should not be same. object: %p, copy: %p", rsrc, &copy)
}
}
func TestAddFieldSelector(t *testing.T) {
tt := []struct {
name string
initial QueryableResource
fieldSelector string
expected QueryableResource
}{
{
name: "Add to empty FieldSelectors",
initial: QueryableResource{GroupVersionResourceTriplet: "gvr1", FieldSelectors: ""},
fieldSelector: "fs1",
expected: QueryableResource{GroupVersionResourceTriplet: "gvr1", FieldSelectors: "fs1"},
},
{
name: "Add to non-empty FieldSelectors",
initial: QueryableResource{GroupVersionResourceTriplet: "gvr1", FieldSelectors: "fs1"},
fieldSelector: "fs2",
expected: QueryableResource{GroupVersionResourceTriplet: "gvr1", FieldSelectors: "fs1,fs2"},
},
{
name: "Add empty FieldSelector to non-empty FieldSelectors",
initial: QueryableResource{GroupVersionResourceTriplet: "gvr1", FieldSelectors: "fs1"},
fieldSelector: "",
expected: QueryableResource{GroupVersionResourceTriplet: "gvr1", FieldSelectors: "fs1"},
},
}
for _, tc := range tt {
t.Run(tc.name, func(t *testing.T) {
tc.initial.AddFieldSelector(tc.fieldSelector)
if tc.initial != tc.expected {
t.Errorf("Expected: %v, got: %v", tc.expected, tc.initial)
}
})
}
}
func TestToK8sResourceMap(t *testing.T) {
qrm := make(QueryableResources)
qrm.Add(QueryableResource{GroupVersionResourceTriplet: "/v1/pods", FieldSelectors: "metadata.namespace=kube-system"})
qrm.Add(QueryableResource{GroupVersionResourceTriplet: "/v1/pods", FieldSelectors: "metadata.namespace=default"})
qrm.Add(QueryableResource{GroupVersionResourceTriplet: "/v1/nodes", FieldSelectors: ""})
qrm.Add(QueryableResource{GroupVersionResourceTriplet: "batch/v1/jobs", FieldSelectors: ""})
expectedResult := cautils.K8SResources{
"/v1/pods": nil,
"/v1/nodes": nil,
"batch/v1/jobs": nil,
}
result := qrm.ToK8sResourceMap()
if len(result) != len(expectedResult) {
t.Fatalf("Expected: %v, got: %v", expectedResult, result)
}
for k, v := range result {
if _, ok := expectedResult[k]; !ok || v != nil {
t.Fatalf("Expected: %v, got: %v", expectedResult, result)
}
}
}
func TestAdd(t *testing.T) {
qrMap := make(QueryableResources)
qr := QueryableResource{GroupVersionResourceTriplet: "/v1/pods", FieldSelectors: "metadata.namespace=default"}
qrMap.Add(qr)
if resource, ok := qrMap["/v1/pods/metadata.namespace=default"]; !ok {
t.Fatalf("Expected resource was not added to the map")
} else if !reflect.DeepEqual(resource, qr) {
t.Fatalf("Expected: %v, got: %v", qr, resource)
}
}

View File

@@ -40,7 +40,7 @@ func NewRegistryAdaptors() (*RegistryAdaptors, error) {
return registryAdaptors, nil
}
func (registryAdaptors *RegistryAdaptors) collectImagesVulnerabilities(k8sResourcesMap *cautils.K8SResources, allResources map[string]workloadinterface.IMetadata, ksResourceMap *cautils.KSResources) error {
func (registryAdaptors *RegistryAdaptors) collectImagesVulnerabilities(k8sResourcesMap cautils.K8SResources, allResources map[string]workloadinterface.IMetadata, ksResourceMap cautils.KSResources) error {
logger.L().Debug("Collecting images vulnerabilities")
if len(registryAdaptors.adaptors) == 0 {
@@ -80,7 +80,7 @@ func (registryAdaptors *RegistryAdaptors) collectImagesVulnerabilities(k8sResour
for i := range metaObjs {
allResources[metaObjs[i].GetID()] = metaObjs[i]
}
(*ksResourceMap)[k8sinterface.JoinResourceTriplets(ImagevulnerabilitiesObjectGroup, ImagevulnerabilitiesObjectVersion, ImagevulnerabilitiesObjectKind)] = workloadinterface.ListMetaIDs(metaObjs)
ksResourceMap[k8sinterface.JoinResourceTriplets(ImagevulnerabilitiesObjectGroup, ImagevulnerabilitiesObjectVersion, ImagevulnerabilitiesObjectKind)] = workloadinterface.ListMetaIDs(metaObjs)
return nil
}
@@ -106,9 +106,9 @@ func vulnerabilityToIMetadata(imageTag string, vulnerabilities []registryvulnera
}
// list all images tags
func listImagesTags(k8sResourcesMap *cautils.K8SResources, allResources map[string]workloadinterface.IMetadata) []string {
func listImagesTags(k8sResourcesMap cautils.K8SResources, allResources map[string]workloadinterface.IMetadata) []string {
images := []string{}
for _, resources := range *k8sResourcesMap {
for _, resources := range k8sResourcesMap {
for j := range resources {
if resource, ok := allResources[resources[j]]; ok {
if resource.GetObjectType() == workloadinterface.TypeWorkloadObject {

View File

@@ -0,0 +1,172 @@
package resourcehandler
import (
"github.com/kubescape/k8s-interface/k8sinterface"
"github.com/kubescape/k8s-interface/workloadinterface"
"github.com/kubescape/kubescape/v2/core/cautils"
"github.com/kubescape/opa-utils/reporthandling"
)
// utils which are common to all resource handlers
func addWorkloadToResourceMaps(k8sResources cautils.K8SResources, allResources map[string]workloadinterface.IMetadata, wl workloadinterface.IWorkload) {
if wl == nil {
return
}
allResources[wl.GetID()] = wl
resourceGroup := k8sinterface.ResourceGroupToSlice(wl.GetGroup(), wl.GetVersion(), wl.GetKind())[0]
k8sResources[resourceGroup] = append(k8sResources[resourceGroup], wl.GetID())
}
func getQueryableResourceMapFromPolicies(handler IResourceHandler, frameworks []reporthandling.Framework, workload workloadinterface.IWorkload) (QueryableResources, map[string]bool) {
queryableResources := make(QueryableResources)
excludedRulesMap := make(map[string]bool)
parentKind := handler.GetWorkloadParentKind(workload)
namespace := getScannedWorkloadNamespace(workload)
for _, framework := range frameworks {
for _, control := range framework.Controls {
for _, rule := range control.Rules {
var resourcesFilterMap map[string]bool = nil
// for workload scan, we need to filter the resources according to the given workload and its owner reference
if workload != nil {
if resourcesFilterMap = filterRuleMatchesForWorkload(workload.GetKind(), parentKind, rule.Match); resourcesFilterMap == nil {
// rule does not apply to this workload
excludedRulesMap[rule.Name] = false
continue
}
}
for _, match := range rule.Match {
updateQueryableResourcesMapFromRuleMatchObject(&match, resourcesFilterMap, queryableResources, namespace)
}
}
}
}
return queryableResources, excludedRulesMap
}
// getScannedWorkloadNamespace returns the namespace of the scanned workload.
// If workload is nil (e.g. cluster scan), returns an empty string
// If the workload is a namespaced or the Namespace iself, returns the namespace name
// In all other cases, returns an empty string
func getScannedWorkloadNamespace(workload workloadinterface.IWorkload) string {
if workload == nil {
return ""
}
if workload.GetKind() == "Namespace" {
return workload.GetName()
}
if k8sinterface.IsResourceInNamespaceScope(workload.GetKind()) {
return workload.GetNamespace()
}
return ""
}
// filterRuleMatches returns a map, of which resources should be queried for a given workload and its owner reference
// The map is of the form: map[<resource>]bool (The bool value indicates whether the resource should be queried or not)
// The function will return a nil map if the rule does not apply to the given workload
func filterRuleMatchesForWorkload(workloadKind, ownerReferenceKind string, matchObjects []reporthandling.RuleMatchObjects) map[string]bool {
resourceMap := make(map[string]bool)
for _, match := range matchObjects {
for _, resource := range match.Resources {
resourceMap[resource] = false
}
}
// rule does not apply to this workload
if _, exists := resourceMap[workloadKind]; !exists {
return nil
}
workloadKinds := map[string]bool{
"Pod": false,
"DaemonSet": false,
"Deployment": false,
"ReplicaSet": false,
"StatefulSet": false,
"CronJob": false,
"Job": false,
}
_, isInputResourceWorkload := workloadKinds[workloadKind]
// has owner reference
if isInputResourceWorkload && ownerReferenceKind != "" {
// owner reference kind exists in the matches - that means the rule does not apply to the given workload
if _, exist := resourceMap[ownerReferenceKind]; exist {
return nil
}
}
for r := range resourceMap {
// we don't need to query the same resource
if r == workloadKind {
continue
}
_, isCurrentResourceWorkload := workloadKinds[r]
resourceMap[r] = !isCurrentResourceWorkload || !isInputResourceWorkload
}
return resourceMap
}
// getOwnerReferenceKind returns the kind of the first owner reference of the given object
// If the object has no owner references or not a valid workload, returns an empty string
func getOwnerReferenceKind(object workloadinterface.IMetadata) string {
if !k8sinterface.IsTypeWorkload(object.GetObject()) {
return ""
}
wl := workloadinterface.NewWorkloadObj(object.GetObject())
ownerReferences, err := wl.GetOwnerReferences()
if err != nil || len(ownerReferences) == 0 {
return ""
}
return ownerReferences[0].Kind
}
// updateQueryableResourcesMapFromMatch updates the queryableResources map with the relevant resources from the match object.
// if namespace is not empty, the namespace filter is added to the queryable resources (which are namespaced)
// if resourcesFilterMap is not nil, only the resources with value 'true' will be added to the queryable resources
func updateQueryableResourcesMapFromRuleMatchObject(match *reporthandling.RuleMatchObjects, resourcesFilterMap map[string]bool, queryableResources QueryableResources, namespace string) {
for _, apiGroup := range match.APIGroups {
for _, apiVersions := range match.APIVersions {
for _, resource := range match.Resources {
if resourcesFilterMap != nil {
if relevant := resourcesFilterMap[resource]; !relevant {
continue
}
}
groupResources := k8sinterface.ResourceGroupToString(apiGroup, apiVersions, resource)
// if namespace filter is set, we are scanning a workload in a specific namespace
// calling the getNamespacesSelector will add the namespace field selector (or name for Namespace resource)
globalFieldSelector := getNamespacesSelector(resource, namespace, "=")
for _, groupResource := range groupResources {
queryableResource := QueryableResource{
GroupVersionResourceTriplet: groupResource,
}
queryableResource.AddFieldSelector(globalFieldSelector)
if match.FieldSelector == nil || len(match.FieldSelector) == 0 {
queryableResources.Add(queryableResource)
continue
}
for _, fieldSelector := range match.FieldSelector {
qrCopy := queryableResource.Copy()
qrCopy.AddFieldSelector(fieldSelector)
queryableResources.Add(qrCopy)
}
}
}
}
}
}

View File

@@ -0,0 +1,591 @@
package resourcehandler
import (
"context"
"fmt"
"reflect"
"testing"
"github.com/armosec/armoapi-go/armotypes"
"github.com/kubescape/k8s-interface/k8sinterface"
"github.com/kubescape/k8s-interface/workloadinterface"
"github.com/kubescape/kubescape/v2/core/cautils"
"github.com/kubescape/kubescape/v2/core/pkg/opaprocessor"
"github.com/kubescape/opa-utils/reporthandling"
"github.com/stretchr/testify/assert"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/version"
)
func mockMatch(i int) reporthandling.RuleMatchObjects {
switch i {
case 1:
return reporthandling.RuleMatchObjects{
APIGroups: []string{"apps"},
APIVersions: []string{"v1", "v1beta"},
Resources: []string{"Pod"},
}
case 2:
return reporthandling.RuleMatchObjects{
APIGroups: []string{"apps"},
APIVersions: []string{"v1"},
Resources: []string{"Deployment", "ReplicaSet"},
}
case 3:
return reporthandling.RuleMatchObjects{
APIGroups: []string{"core"},
APIVersions: []string{"v1"},
Resources: []string{"Secret"},
}
case 4:
return reporthandling.RuleMatchObjects{
APIGroups: []string{"core"},
APIVersions: []string{"v1"},
Resources: []string{"Secret"},
FieldSelector: []string{"metadata.name=secret1", "metadata.name=secret2,metadata.namespace=default"},
}
case 5:
return reporthandling.RuleMatchObjects{
APIGroups: []string{"rbac.authorization.k8s.io"},
APIVersions: []string{"v1"},
Resources: []string{"ClusterRoleBinding", "RoleBinding"},
FieldSelector: []string{"metadata.name=test123"},
}
case 6:
return reporthandling.RuleMatchObjects{
APIGroups: []string{""},
APIVersions: []string{"v1"},
Resources: []string{"Namespace"},
FieldSelector: []string{},
}
case 7:
return reporthandling.RuleMatchObjects{
APIGroups: []string{""},
APIVersions: []string{"v1"},
Resources: []string{"Node"},
FieldSelector: []string{},
}
default:
panic("invalid index")
}
}
func mockRule(ruleName string, matches []reporthandling.RuleMatchObjects, ruleRego string) reporthandling.PolicyRule {
rule := reporthandling.PolicyRule{
PortalBase: *armotypes.MockPortalBase("aaaaaaaa-bbbb-cccc-dddd-000000000001", ruleName, nil),
RuleLanguage: reporthandling.RegoLanguage,
Match: matches,
RuleDependencies: []reporthandling.RuleDependency{
{
PackageName: "kubernetes.api.client",
},
},
}
if ruleRego != "" {
rule.Rule = ruleRego
} else {
rule.Rule = reporthandling.MockRegoPrivilegedPods()
}
return rule
}
func mockControl(controlName string, rules []reporthandling.PolicyRule) reporthandling.Control {
return reporthandling.Control{
PortalBase: *armotypes.MockPortalBase("aaaaaaaa-bbbb-cccc-dddd-000000000001", controlName, nil),
Rules: rules,
}
}
func mockFramework(frameworkName string, controls []reporthandling.Control) *reporthandling.Framework {
return &reporthandling.Framework{
PortalBase: *armotypes.MockPortalBase("aaaaaaaa-bbbb-cccc-dddd-000000000001", frameworkName, nil),
CreationTime: "",
Description: "mock framework description",
Controls: controls,
}
}
func mockWorkload(apiVersion, kind, namespace, name, ownerReferenceKind string) workloadinterface.IWorkload {
mock := workloadinterface.NewWorkloadMock(nil)
mock.SetKind(kind)
mock.SetApiVersion(apiVersion)
mock.SetName(name)
mock.SetNamespace(namespace)
if ownerReferenceKind != "" {
ownerreferences := []metav1.OwnerReference{
{
Kind: ownerReferenceKind,
},
}
workloadinterface.SetInMap(mock.GetWorkload(), []string{"metadata"}, "ownerReferences", ownerreferences)
}
if ok := k8sinterface.IsTypeWorkload(mock.GetObject()); !ok {
panic("mocked object is not a valid workload")
}
return mock
}
type ResourceHandlerMock struct {
}
func (mock *ResourceHandlerMock) GetResources(context.Context, *cautils.OPASessionObj, opaprocessor.IJobProgressNotificationClient) (cautils.K8SResources, map[string]workloadinterface.IMetadata, cautils.KSResources, map[string]bool, error) {
return nil, nil, nil, nil, nil
}
func (mock *ResourceHandlerMock) GetClusterAPIServerInfo(ctx context.Context) *version.Info {
return nil
}
func (mock *ResourceHandlerMock) GetWorkloadParentKind(wl workloadinterface.IWorkload) string {
if wl == nil {
return ""
}
if owners, err := wl.GetOwnerReferences(); err == nil && len(owners) > 0 {
return owners[0].Kind
}
return ""
}
func TestGetQueryableResourceMapFromPolicies(t *testing.T) {
k8sinterface.InitializeMapResourcesMock()
testCases := []struct {
name string
workload workloadinterface.IWorkload
controls []reporthandling.Control
expectedResourceGroups []string
expectedExcludedRules []string
}{
{
name: "no workload - all resources groups are queryable",
workload: nil,
controls: []reporthandling.Control{
mockControl("1", []reporthandling.PolicyRule{
mockRule("rule-a", []reporthandling.RuleMatchObjects{
mockMatch(1), mockMatch(2), mockMatch(3), mockMatch(4),
}, ""),
mockRule("rule-b", []reporthandling.RuleMatchObjects{
mockMatch(6),
}, ""),
}),
},
expectedExcludedRules: []string{},
expectedResourceGroups: []string{
"/v1/namespaces",
"apps/v1/deployments",
"apps/v1/pods",
"apps/v1/replicasets",
"apps/v1beta/pods",
"core/v1/secrets",
"core/v1/secrets/metadata.name=secret1",
"core/v1/secrets/metadata.name=secret2,metadata.namespace=default",
},
},
{
name: "workload - pod with parent deployment",
workload: mockWorkload("apps/v1", "Pod", "default", "nginx", "Deployment"),
controls: []reporthandling.Control{
mockControl("1", []reporthandling.PolicyRule{
mockRule("rule-a", []reporthandling.RuleMatchObjects{
mockMatch(1), mockMatch(2), mockMatch(3), mockMatch(4),
}, ""),
mockRule("rule-b", []reporthandling.RuleMatchObjects{
mockMatch(6),
}, ""),
}),
},
expectedExcludedRules: []string{
"rule-b",
"rule-a",
},
expectedResourceGroups: []string{},
},
{
name: "workload - Namespace",
workload: mockWorkload("v1", "Namespace", "", "ns1", ""),
controls: []reporthandling.Control{
mockControl("1", []reporthandling.PolicyRule{
mockRule("rule-a", []reporthandling.RuleMatchObjects{
mockMatch(1), mockMatch(2), mockMatch(3), mockMatch(4),
}, ""),
mockRule("rule-b", []reporthandling.RuleMatchObjects{
mockMatch(6), mockMatch(3), mockMatch(2), mockMatch(7),
}, ""),
}),
},
expectedExcludedRules: []string{
"rule-a",
},
expectedResourceGroups: []string{
"/v1/nodes",
"core/v1/secrets/metadata.namespace=ns1",
"apps/v1/deployments/metadata.namespace=ns1",
"apps/v1/replicasets/metadata.namespace=ns1",
},
},
{
name: "workload - Deployment",
workload: mockWorkload("apps/v1", "Deployment", "ns1", "deploy1", ""),
controls: []reporthandling.Control{
mockControl("1", []reporthandling.PolicyRule{
mockRule("rule-b", []reporthandling.RuleMatchObjects{
mockMatch(6), mockMatch(3), mockMatch(2), mockMatch(7),
}, ""),
}),
},
expectedExcludedRules: []string{},
expectedResourceGroups: []string{
"core/v1/secrets/metadata.namespace=ns1",
"/v1/namespaces/metadata.name=ns1",
"/v1/nodes",
},
},
{
name: "workload - Node",
workload: mockWorkload("v1", "Node", "", "node1", ""),
controls: []reporthandling.Control{
mockControl("1", []reporthandling.PolicyRule{
mockRule("rule-b", []reporthandling.RuleMatchObjects{
mockMatch(6), mockMatch(3), mockMatch(2), mockMatch(7),
}, ""),
}),
},
expectedExcludedRules: []string{},
expectedResourceGroups: []string{
"core/v1/secrets",
"/v1/namespaces",
"apps/v1/deployments",
"apps/v1/replicasets",
},
},
}
for _, testCase := range testCases {
t.Run(testCase.name, func(t *testing.T) {
resourceGroups, excludedRulesMap := getQueryableResourceMapFromPolicies(&ResourceHandlerMock{}, []reporthandling.Framework{*mockFramework("test", testCase.controls)}, testCase.workload) // TODO check second param
assert.Equalf(t, len(testCase.expectedExcludedRules), len(excludedRulesMap), "excludedRulesMap length is not as expected")
for _, expectedExcludedRuleName := range testCase.expectedExcludedRules {
assert.Contains(t, excludedRulesMap, expectedExcludedRuleName, "excludedRulesMap does not contain expected rule name")
}
assert.Equalf(t, len(testCase.expectedResourceGroups), len(resourceGroups), "queryableResourceMap length is not as expected")
for _, expected := range testCase.expectedResourceGroups {
assert.Contains(t, resourceGroups, expected, "queryableResourceMap does not contain expected resource group")
}
})
}
}
func TestUpdateQueryableResourcesMapFromRuleMatchObject(t *testing.T) {
testCases := []struct {
name string
matches []reporthandling.RuleMatchObjects
resourcesFilterMap map[string]bool
namespace string
expectedQueryableResourceGroups []string
expectedK8SResourceGroups []string
}{
{
name: "filter map is nil - query all",
matches: []reporthandling.RuleMatchObjects{
mockMatch(1), mockMatch(2), mockMatch(3), mockMatch(4),
},
resourcesFilterMap: nil,
namespace: "",
expectedQueryableResourceGroups: []string{
"apps/v1/pods",
"apps/v1beta/pods",
"apps/v1/deployments",
"apps/v1/replicasets",
"core/v1/secrets",
"core/v1/secrets/metadata.name=secret1",
"core/v1/secrets/metadata.name=secret2,metadata.namespace=default",
},
expectedK8SResourceGroups: []string{
"apps/v1/pods",
"apps/v1beta/pods",
"apps/v1/deployments",
"apps/v1/replicasets",
"core/v1/secrets",
},
},
{
name: "filter map not nil - query only secrets and pods",
matches: []reporthandling.RuleMatchObjects{
mockMatch(1), mockMatch(2), mockMatch(3), mockMatch(4),
},
namespace: "",
resourcesFilterMap: map[string]bool{
"Secret": true,
"Pod": true,
"ReplicaSet": false,
"Deployment": false,
},
expectedQueryableResourceGroups: []string{
"apps/v1/pods",
"apps/v1beta/pods",
"core/v1/secrets",
"core/v1/secrets/metadata.name=secret1",
"core/v1/secrets/metadata.name=secret2,metadata.namespace=default",
},
expectedK8SResourceGroups: []string{
"apps/v1/pods",
"apps/v1beta/pods",
"core/v1/secrets",
},
},
{
name: "namespace field selector for namespaced resources",
matches: []reporthandling.RuleMatchObjects{
mockMatch(5),
},
namespace: "ns1",
resourcesFilterMap: map[string]bool{
"RoleBinding": true,
"ClusterRoleBinding": true,
},
expectedQueryableResourceGroups: []string{
"rbac.authorization.k8s.io/v1/clusterrolebindings/metadata.name=test123",
"rbac.authorization.k8s.io/v1/rolebindings/metadata.namespace=ns1,metadata.name=test123",
},
expectedK8SResourceGroups: []string{
"rbac.authorization.k8s.io/v1/clusterrolebindings",
"rbac.authorization.k8s.io/v1/rolebindings",
},
},
{
name: "name field selector for Namespace resource",
matches: []reporthandling.RuleMatchObjects{
mockMatch(2), mockMatch(6),
},
namespace: "ns1",
resourcesFilterMap: map[string]bool{
"Deployment": true,
"ReplicaSet": false,
"Namespace": true,
},
expectedQueryableResourceGroups: []string{
"apps/v1/deployments/metadata.namespace=ns1",
"/v1/namespaces/metadata.name=ns1",
},
expectedK8SResourceGroups: []string{
"apps/v1/deployments",
"/v1/namespaces",
},
},
}
for _, testCase := range testCases {
t.Run(testCase.name, func(t *testing.T) {
queryableResources := make(QueryableResources)
for i := range testCase.matches {
updateQueryableResourcesMapFromRuleMatchObject(&testCase.matches[i], testCase.resourcesFilterMap, queryableResources, testCase.namespace)
}
assert.Equal(t, len(testCase.expectedQueryableResourceGroups), len(queryableResources))
for _, resourceGroup := range testCase.expectedQueryableResourceGroups {
assert.Contains(t, queryableResources, resourceGroup)
}
k8sResources := queryableResources.ToK8sResourceMap()
assert.Equal(t, len(testCase.expectedK8SResourceGroups), len(k8sResources))
for _, resourceGroup := range testCase.expectedK8SResourceGroups {
assert.Contains(t, k8sResources, resourceGroup)
}
})
}
}
func TestFilterRuleMatchesForWorkload(t *testing.T) {
testCases := []struct {
workloadKind string
ownerReferenceKind string
matchResources []string
expectedMap map[string]bool
}{
{
workloadKind: "Pod",
ownerReferenceKind: "",
matchResources: []string{
"Node", "Pod", "DaemonSet", "Deployment", "ReplicaSet", "StatefulSet", "CronJob", "Job", "PodSecurityPolicy",
},
expectedMap: map[string]bool{
"Node": true,
"PodSecurityPolicy": true,
"Pod": false,
"DaemonSet": false,
"Deployment": false,
"ReplicaSet": false,
"StatefulSet": false,
"CronJob": false,
"Job": false,
},
},
{
workloadKind: "Deployment",
ownerReferenceKind: "",
matchResources: []string{
"Node", "Pod", "DaemonSet", "Deployment", "ReplicaSet", "StatefulSet", "CronJob", "Job", "PodSecurityPolicy",
},
expectedMap: map[string]bool{
"Node": true,
"PodSecurityPolicy": true,
"Pod": false,
"DaemonSet": false,
"Deployment": false,
"ReplicaSet": false,
"StatefulSet": false,
"CronJob": false,
"Job": false,
},
},
{
workloadKind: "Deployment",
ownerReferenceKind: "",
matchResources: []string{
"Deployment", "ReplicaSet",
},
expectedMap: map[string]bool{
"Deployment": false,
"ReplicaSet": false,
},
},
{
workloadKind: "Pod",
ownerReferenceKind: "Deployment",
matchResources: []string{
"Node", "Pod", "DaemonSet", "Deployment", "ReplicaSet", "StatefulSet", "CronJob", "Job", "PodSecurityPolicy",
},
expectedMap: nil, // rule does not apply to workload
},
{
workloadKind: "ReplicaSet",
ownerReferenceKind: "Deployment",
matchResources: []string{
"Node", "Pod", "DaemonSet", "Deployment", "ReplicaSet", "StatefulSet", "CronJob", "Job", "PodSecurityPolicy",
},
expectedMap: nil, // rule does not apply to workload
},
{
workloadKind: "ReplicaSet",
ownerReferenceKind: "",
matchResources: []string{
"Node", "Pod", "DaemonSet", "Deployment", "ReplicaSet", "StatefulSet", "CronJob", "Job", "PodSecurityPolicy",
},
expectedMap: map[string]bool{
"Node": true,
"PodSecurityPolicy": true,
"Pod": false,
"DaemonSet": false,
"Deployment": false,
"ReplicaSet": false,
"StatefulSet": false,
"CronJob": false,
"Job": false,
},
},
{
workloadKind: "ClusterRole",
ownerReferenceKind: "",
matchResources: []string{
"Node", "Pod", "DaemonSet", "Deployment", "ReplicaSet", "StatefulSet", "CronJob", "Job", "PodSecurityPolicy",
},
expectedMap: nil, // rule does not apply to workload
},
{
workloadKind: "Node",
ownerReferenceKind: "",
matchResources: []string{
"Node", "Pod", "DaemonSet", "Deployment", "ReplicaSet", "StatefulSet", "CronJob", "Job", "PodSecurityPolicy",
},
expectedMap: map[string]bool{
"Node": false,
"PodSecurityPolicy": true,
"Pod": true,
"DaemonSet": true,
"Deployment": true,
"ReplicaSet": true,
"StatefulSet": true,
"CronJob": true,
"Job": true,
},
},
{
workloadKind: "Pod",
ownerReferenceKind: "",
matchResources: []string{
"PodSecurityPolicy", "Pod",
},
expectedMap: map[string]bool{
"PodSecurityPolicy": true,
"Pod": false,
},
},
{
workloadKind: "Pod",
ownerReferenceKind: "Deployment",
matchResources: []string{
"PodSecurityPolicy", "Pod",
},
expectedMap: map[string]bool{
"PodSecurityPolicy": true,
"Pod": false,
},
},
{
workloadKind: "Pod",
ownerReferenceKind: "Deployment",
matchResources: []string{
"PodSecurityPolicy", "Pod", "ReplicaSet",
},
expectedMap: map[string]bool{
"PodSecurityPolicy": true,
"Pod": false,
"ReplicaSet": false,
},
},
{
workloadKind: "Deployment",
ownerReferenceKind: "",
matchResources: []string{
"PodSecurityPolicy", "Pod",
},
expectedMap: nil, // rule does not apply to workload
},
{
workloadKind: "PodSecurityPolicy",
ownerReferenceKind: "",
matchResources: []string{
"PodSecurityPolicy", "Pod",
},
expectedMap: map[string]bool{
"PodSecurityPolicy": false,
"Pod": true,
},
},
}
for i, testCase := range testCases {
t.Run(fmt.Sprintf("%v", i), func(t *testing.T) {
matches := []reporthandling.RuleMatchObjects{
{
Resources: testCase.matchResources,
},
}
result := filterRuleMatchesForWorkload(testCase.workloadKind, testCase.ownerReferenceKind, matches)
if testCase.expectedMap == nil {
assert.Nil(t, result, "expected nil (rule does not apply to workload)")
return
}
if !reflect.DeepEqual(result, testCase.expectedMap) {
t.Errorf("expected %v, got %v", testCase.expectedMap, result)
}
})
}
}

View File

@@ -24,7 +24,8 @@ const (
)
type IPrinter interface {
ActionPrint(ctx context.Context, opaSessionObj *cautils.OPASessionObj)
PrintNextSteps()
ActionPrint(ctx context.Context, opaSessionObj *cautils.OPASessionObj, imageScanData []cautils.ImageScanData)
SetWriter(ctx context.Context, outputFile string)
Score(score float32)
}

View File

@@ -8,6 +8,7 @@ import (
"path/filepath"
"strings"
"github.com/anchore/grype/grype/presenter/models"
logger "github.com/kubescape/go-logger"
"github.com/kubescape/kubescape/v2/core/cautils"
"github.com/kubescape/kubescape/v2/core/pkg/resultshandling/printer"
@@ -18,6 +19,8 @@ const (
jsonOutputExt = ".json"
)
var _ printer.IPrinter = &JsonPrinter{}
type JsonPrinter struct {
writer *os.File
}
@@ -40,7 +43,14 @@ func (jsonPrinter *JsonPrinter) Score(score float32) {
fmt.Fprintf(os.Stderr, "\nOverall compliance-score (100- Excellent, 0- All failed): %d\n", cautils.Float32ToInt(score))
}
func (jsonPrinter *JsonPrinter) ActionPrint(ctx context.Context, opaSessionObj *cautils.OPASessionObj) {
func (jsonPrinter *JsonPrinter) PrintImageScan(context.Context, *models.PresenterConfig) {
}
func (jsonPrinter *JsonPrinter) PrintNextSteps() {
}
func (jsonPrinter *JsonPrinter) ActionPrint(ctx context.Context, opaSessionObj *cautils.OPASessionObj, _ []cautils.ImageScanData) {
report := cautils.ReportV2ToV1(opaSessionObj)
var postureReportStr []byte

View File

@@ -9,6 +9,7 @@ import (
"sort"
"strings"
"github.com/anchore/grype/grype/presenter/models"
logger "github.com/kubescape/go-logger"
"github.com/kubescape/go-logger/helpers"
"github.com/kubescape/kubescape/v2/core/cautils"
@@ -51,7 +52,14 @@ func (hp *HtmlPrinter) SetWriter(ctx context.Context, outputFile string) {
hp.writer = printer.GetWriter(ctx, outputFile)
}
func (hp *HtmlPrinter) ActionPrint(ctx context.Context, opaSessionObj *cautils.OPASessionObj) {
func (hp *HtmlPrinter) PrintImageScan(context.Context, *models.PresenterConfig) {
}
func (hp *HtmlPrinter) PrintNextSteps() {
}
func (hp *HtmlPrinter) ActionPrint(ctx context.Context, opaSessionObj *cautils.OPASessionObj, imageScanData []cautils.ImageScanData) {
tplFuncMap := template.FuncMap{
"sum": func(nums ...int) int {
total := 0

View File

@@ -8,6 +8,8 @@ import (
"path/filepath"
"strings"
"github.com/anchore/grype/grype/presenter"
"github.com/anchore/grype/grype/presenter/models"
logger "github.com/kubescape/go-logger"
"github.com/kubescape/go-logger/helpers"
"github.com/kubescape/kubescape/v2/core/cautils"
@@ -41,17 +43,43 @@ func (jp *JsonPrinter) SetWriter(ctx context.Context, outputFile string) {
func (jp *JsonPrinter) Score(score float32) {
fmt.Fprintf(os.Stderr, "\nOverall compliance-score (100- Excellent, 0- All failed): %d\n", cautils.Float32ToInt(score))
}
func (jp *JsonPrinter) ActionPrint(ctx context.Context, opaSessionObj *cautils.OPASessionObj) {
r, err := json.Marshal(FinalizeResults(opaSessionObj))
if err != nil {
logger.L().Ctx(ctx).Fatal("failed to Marshal posture report object")
func (jp *JsonPrinter) ActionPrint(ctx context.Context, opaSessionObj *cautils.OPASessionObj, imageScanData []cautils.ImageScanData) {
var err error
if opaSessionObj != nil {
err = printConfigurationsScanning(opaSessionObj, ctx, jp)
} else if imageScanData != nil {
err = jp.PrintImageScan(ctx, imageScanData[0].PresenterConfig)
} else {
err = fmt.Errorf("failed to print results, no data provided")
}
if _, err := jp.writer.Write(r); err != nil {
logger.L().Ctx(ctx).Error("failed to write results", helpers.Error(err))
if err != nil {
logger.L().Ctx(ctx).Error("failed to print results", helpers.Error(err))
return
}
printer.LogOutputFile(jp.writer.Name())
}
func printConfigurationsScanning(opaSessionObj *cautils.OPASessionObj, ctx context.Context, jp *JsonPrinter) error {
r, err := json.Marshal(FinalizeResults(opaSessionObj))
if err != nil {
return err
}
_, err = jp.writer.Write(r)
return err
}
func (jp *JsonPrinter) PrintImageScan(ctx context.Context, scanResults *models.PresenterConfig) error {
pres := presenter.GetPresenter("json", jp.writer.Name(), false, *scanResults)
return pres.Present(jp.writer)
}
func (jp *JsonPrinter) PrintNextSteps() {
}

View File

@@ -9,6 +9,7 @@ import (
"sort"
"strings"
"github.com/anchore/grype/grype/presenter/models"
logger "github.com/kubescape/go-logger"
"github.com/kubescape/go-logger/helpers"
"github.com/kubescape/k8s-interface/workloadinterface"
@@ -112,7 +113,14 @@ func (jp *JunitPrinter) Score(score float32) {
fmt.Fprintf(os.Stderr, "\nOverall compliance-score (100- Excellent, 0- All failed): %d\n", cautils.Float32ToInt(score))
}
func (jp *JunitPrinter) ActionPrint(ctx context.Context, opaSessionObj *cautils.OPASessionObj) {
func (jp *JunitPrinter) PrintImageScan(context.Context, *models.PresenterConfig) {
}
func (jp *JunitPrinter) PrintNextSteps() {
}
func (jp *JunitPrinter) ActionPrint(ctx context.Context, opaSessionObj *cautils.OPASessionObj, imageScanData []cautils.ImageScanData) {
junitResult := testsSuites(opaSessionObj)
postureReportStr, err := xml.Marshal(junitResult)
if err != nil {

View File

@@ -10,6 +10,7 @@ import (
"strings"
"time"
"github.com/anchore/grype/grype/presenter/models"
logger "github.com/kubescape/go-logger"
"github.com/kubescape/go-logger/helpers"
"github.com/kubescape/kubescape/v2/core/cautils"
@@ -63,10 +64,10 @@ func (pp *PdfPrinter) printInfo(m pdf.Maroto, summaryDetails *reportsummary.Summ
if infoMap[i].info != "" {
m.Row(5, func() {
m.Col(12, func() {
m.Text(fmt.Sprintf("%v %v", infoMap[i].stars, infoMap[i].info),props.Text{
Style: consts.Bold,
Align: consts.Left,
Size: 8,
m.Text(fmt.Sprintf("%v %v", infoMap[i].stars, infoMap[i].info), props.Text{
Style: consts.Bold,
Align: consts.Left,
Size: 8,
Extrapolate: false,
Color: color.Color{
Red: 0,
@@ -85,7 +86,14 @@ func (pp *PdfPrinter) printInfo(m pdf.Maroto, summaryDetails *reportsummary.Summ
}
func (pp *PdfPrinter) ActionPrint(ctx context.Context, opaSessionObj *cautils.OPASessionObj) {
func (pp *PdfPrinter) PrintImageScan(context.Context, *models.PresenterConfig) {
}
func (pp *PdfPrinter) PrintNextSteps() {
}
func (pp *PdfPrinter) ActionPrint(ctx context.Context, opaSessionObj *cautils.OPASessionObj, imageScanData []cautils.ImageScanData) {
sortedControlIDs := getSortedControlsIDs(opaSessionObj.Report.SummaryDetails.Controls)
infoToPrintInfo := mapInfoToPrintInfo(opaSessionObj.Report.SummaryDetails.Controls)

View File

@@ -8,15 +8,19 @@ import (
"sort"
"strings"
"github.com/anchore/grype/grype/presenter/models"
"github.com/enescakir/emoji"
logger "github.com/kubescape/go-logger"
"github.com/kubescape/go-logger/helpers"
"github.com/kubescape/k8s-interface/workloadinterface"
"github.com/kubescape/kubescape/v2/core/cautils"
"github.com/kubescape/kubescape/v2/core/pkg/resultshandling/printer"
"github.com/kubescape/kubescape/v2/core/pkg/resultshandling/printer/v2/prettyprinter"
"github.com/kubescape/kubescape/v2/core/pkg/resultshandling/printer/v2/prettyprinter/tableprinter/imageprinter"
"github.com/kubescape/opa-utils/objectsenvelopes"
"github.com/kubescape/opa-utils/reporthandling/apis"
helpersv1 "github.com/kubescape/opa-utils/reporthandling/helpers/v1"
"github.com/kubescape/opa-utils/reporthandling/results/v1/reportsummary"
"github.com/olekukonko/tablewriter"
"k8s.io/utils/strings/slices"
)
const (
@@ -32,40 +36,130 @@ type PrettyPrinter struct {
viewType cautils.ViewTypes
verboseMode bool
printAttackTree bool
scanType cautils.ScanTypes
inputPatterns []string
mainPrinter prettyprinter.MainPrinter
}
func NewPrettyPrinter(verboseMode bool, formatVersion string, attackTree bool, viewType cautils.ViewTypes) *PrettyPrinter {
return &PrettyPrinter{
func NewPrettyPrinter(verboseMode bool, formatVersion string, attackTree bool, viewType cautils.ViewTypes, scanType cautils.ScanTypes, inputPatterns []string) *PrettyPrinter {
prettyPrinter := &PrettyPrinter{
verboseMode: verboseMode,
formatVersion: formatVersion,
viewType: viewType,
printAttackTree: attackTree,
scanType: scanType,
inputPatterns: inputPatterns,
}
return prettyPrinter
}
func (pp *PrettyPrinter) SetMainPrinter() {
switch pp.scanType {
case cautils.ScanTypeCluster:
pp.mainPrinter = prettyprinter.NewClusterPrinter(pp.writer)
case cautils.ScanTypeRepo:
pp.mainPrinter = prettyprinter.NewRepoPrinter(pp.writer, pp.inputPatterns)
case cautils.ScanTypeImage:
pp.mainPrinter = prettyprinter.NewImagePrinter(pp.writer, pp.verboseMode)
case cautils.ScanTypeWorkload:
pp.mainPrinter = prettyprinter.NewWorkloadPrinter(pp.writer)
default:
pp.mainPrinter = prettyprinter.NewSummaryPrinter(pp.writer, pp.verboseMode)
}
}
func (pp *PrettyPrinter) ActionPrint(_ context.Context, opaSessionObj *cautils.OPASessionObj) {
fmt.Fprintf(pp.writer, "\n"+getSeparator("^")+"\n")
func (pp *PrettyPrinter) PrintNextSteps() {
pp.mainPrinter.PrintNextSteps()
}
sortedControlIDs := getSortedControlsIDs(opaSessionObj.Report.SummaryDetails.Controls) // ListControls().All())
// convertToImageScanSummary takes a list of image scan data and converts it to a single image scan summary
func (pp *PrettyPrinter) convertToImageScanSummary(imageScanData []cautils.ImageScanData) (*imageprinter.ImageScanSummary, error) {
imageScanSummary := imageprinter.ImageScanSummary{
CVEs: []imageprinter.CVE{},
PackageScores: map[string]*imageprinter.PackageScore{},
MapsSeverityToSummary: map[string]*imageprinter.SeveritySummary{},
}
switch pp.viewType {
case cautils.ControlViewType:
pp.printResults(&opaSessionObj.Report.SummaryDetails.Controls, opaSessionObj.AllResources, sortedControlIDs)
case cautils.ResourceViewType:
if pp.verboseMode {
pp.resourceTable(opaSessionObj)
for _, imageScan := range imageScanData {
if !slices.Contains(imageScanSummary.Images, imageScan.Image) {
imageScanSummary.Images = append(imageScanSummary.Images, imageScan.Image)
}
presenterConfig := imageScan.PresenterConfig
doc, err := models.NewDocument(presenterConfig.Packages, presenterConfig.Context, presenterConfig.Matches, presenterConfig.IgnoredMatches, presenterConfig.MetadataProvider, nil, presenterConfig.DBStatus)
if err != nil {
logger.L().Error(fmt.Sprintf("failed to create document for image: %v", imageScan.Image), helpers.Error(err))
continue
}
imageScanSummary.CVEs = append(imageScanSummary.CVEs, extractCVEs(doc.Matches)...)
extractPkgNameToScoreMap(doc.Matches, imageScanSummary.PackageScores)
extractSeverityToSummaryMap(imageScanSummary.CVEs, imageScanSummary.MapsSeverityToSummary)
}
return &imageScanSummary, nil
}
func (pp *PrettyPrinter) PrintImageScan(imageScanData []cautils.ImageScanData) {
imageScanSummary, err := pp.convertToImageScanSummary(imageScanData)
if err != nil {
logger.L().Error("failed to convert to image scan summary", helpers.Error(err))
return
}
pp.mainPrinter.PrintImageScanning(imageScanSummary)
}
func (pp *PrettyPrinter) ActionPrint(_ context.Context, opaSessionObj *cautils.OPASessionObj, imageScanData []cautils.ImageScanData) {
if opaSessionObj != nil {
fmt.Fprintf(pp.writer, "\n"+getSeparator("^")+"\n")
sortedControlIDs := getSortedControlsIDs(opaSessionObj.Report.SummaryDetails.Controls) // ListControls().All())
switch pp.viewType {
case cautils.ControlViewType:
pp.printResults(&opaSessionObj.Report.SummaryDetails.Controls, opaSessionObj.AllResources, sortedControlIDs)
case cautils.ResourceViewType:
if pp.verboseMode {
pp.resourceTable(opaSessionObj)
}
}
pp.printOverview(opaSessionObj, pp.verboseMode)
pp.mainPrinter.PrintConfigurationsScanning(&opaSessionObj.Report.SummaryDetails, sortedControlIDs)
// When writing to Stdout, we arent really writing to an output file,
// so no need to print that we are
if pp.writer.Name() != os.Stdout.Name() {
printer.LogOutputFile(pp.writer.Name())
}
pp.printAttackTracks(opaSessionObj)
}
if len(imageScanData) > 0 {
pp.PrintImageScan(imageScanData)
}
}
func (pp *PrettyPrinter) printOverview(opaSessionObj *cautils.OPASessionObj, printExtraLine bool) {
if printExtraLine {
fmt.Fprintf(pp.writer, "\n")
}
if pp.scanType == cautils.ScanTypeCluster || pp.scanType == cautils.ScanTypeRepo {
cautils.InfoDisplay(pp.writer, "\nSecurity Overview\n\n")
} else if pp.scanType == cautils.ScanTypeWorkload {
ns := opaSessionObj.ScannedWorkload.GetNamespace()
if ns == "" {
cautils.InfoDisplay(pp.writer, "Workload - Kind: %s, Name: %s\n\n", opaSessionObj.ScannedWorkload.GetKind(), opaSessionObj.ScannedWorkload.GetName())
} else {
cautils.InfoDisplay(pp.writer, "Workload - Namespace: %s, Kind: %s, Name: %s\n\n", opaSessionObj.ScannedWorkload.GetNamespace(), opaSessionObj.ScannedWorkload.GetKind(), opaSessionObj.ScannedWorkload.GetName())
}
}
pp.printSummaryTable(&opaSessionObj.Report.SummaryDetails, sortedControlIDs)
// When writing to Stdout, we arent really writing to an output file,
// so no need to print that we are
if pp.writer.Name() != os.Stdout.Name() {
printer.LogOutputFile(pp.writer.Name())
}
pp.printAttackTracks(opaSessionObj)
}
func (pp *PrettyPrinter) SetWriter(ctx context.Context, outputFile string) {
@@ -74,6 +168,7 @@ func (pp *PrettyPrinter) SetWriter(ctx context.Context, outputFile string) {
// otherwise
if outputFile == os.Stdout.Name() {
pp.writer = printer.GetWriter(ctx, "")
pp.SetMainPrinter()
return
}
@@ -85,6 +180,8 @@ func (pp *PrettyPrinter) SetWriter(ctx context.Context, outputFile string) {
}
pp.writer = printer.GetWriter(ctx, outputFile)
pp.SetMainPrinter()
}
func (pp *PrettyPrinter) Score(score float32) {
@@ -113,6 +210,7 @@ func (prettyPrinter *PrettyPrinter) printSummary(controlName string, controlSumm
cautils.DescriptionDisplay(prettyPrinter.writer, "\n")
}
func (prettyPrinter *PrettyPrinter) printTitle(controlSummary reportsummary.IControlSummary) {
cautils.InfoDisplay(prettyPrinter.writer, "[control: %s - %s] ", controlSummary.GetName(), cautils.GetControlLink(controlSummary.GetID()))
statusDetails := ""
@@ -132,6 +230,7 @@ func (prettyPrinter *PrettyPrinter) printTitle(controlSummary reportsummary.ICon
cautils.WarningDisplay(prettyPrinter.writer, "Reason: %v\n", controlSummary.GetStatus().Info())
}
}
func (pp *PrettyPrinter) printResources(controlSummary reportsummary.IControlSummary, allResources map[string]workloadinterface.IMetadata) {
workloadsSummary := listResultSummary(controlSummary, allResources)
@@ -199,67 +298,6 @@ func generateRelatedObjectsStr(workload WorkloadSummary) string {
}
return relatedStr
}
func generateFooter(summaryDetails *reportsummary.SummaryDetails) []string {
// Severity | Control name | failed resources | all resources | % success
row := make([]string, _rowLen)
row[columnName] = "Resource Summary"
row[columnCounterFailed] = fmt.Sprintf("%d", summaryDetails.NumberOfResources().Failed())
row[columnCounterAll] = fmt.Sprintf("%d", summaryDetails.NumberOfResources().All())
row[columnSeverity] = " "
row[columnComplianceScore] = fmt.Sprintf("%.2f%s", summaryDetails.ComplianceScore, "%")
return row
}
func (pp *PrettyPrinter) printSummaryTable(summaryDetails *reportsummary.SummaryDetails, sortedControlIDs [][]string) {
if summaryDetails.NumberOfControls().All() == 0 {
fmt.Fprintf(pp.writer, "\nKubescape did not scan any of the resources, make sure you are scanning valid kubernetes manifests (Deployments, Pods, etc.)\n")
return
}
cautils.InfoTextDisplay(pp.writer, "\n"+controlCountersForSummary(summaryDetails.NumberOfControls())+"\n")
cautils.InfoTextDisplay(pp.writer, renderSeverityCountersSummary(summaryDetails.GetResourcesSeverityCounters())+"\n\n")
// cautils.InfoTextDisplay(prettyPrinter.writer, "\n"+"Severities: SOME OTHER"+"\n\n")
summaryTable := tablewriter.NewWriter(pp.writer)
summaryTable.SetAutoWrapText(false)
summaryTable.SetHeader(getControlTableHeaders())
summaryTable.SetHeaderLine(true)
summaryTable.SetColumnAlignment(getColumnsAlignments())
printAll := pp.verboseMode
if summaryDetails.NumberOfResources().Failed() == 0 {
// if there are no failed controls, print the resource table and detailed information
printAll = true
}
infoToPrintInfo := mapInfoToPrintInfo(summaryDetails.Controls)
for i := len(sortedControlIDs) - 1; i >= 0; i-- {
for _, c := range sortedControlIDs[i] {
row := generateRow(summaryDetails.Controls.GetControl(reportsummary.EControlCriteriaID, c), infoToPrintInfo, printAll)
if len(row) > 0 {
summaryTable.Append(row)
}
}
}
summaryTable.SetFooter(generateFooter(summaryDetails))
summaryTable.Render()
// When scanning controls the framework list will be empty
cautils.InfoTextDisplay(pp.writer, frameworksScoresToString(summaryDetails.ListFrameworks()))
pp.printInfo(infoToPrintInfo)
}
func (pp *PrettyPrinter) printInfo(infoToPrintInfo []infoStars) {
fmt.Println()
for i := range infoToPrintInfo {
cautils.InfoDisplay(pp.writer, fmt.Sprintf("%s %s\n", infoToPrintInfo[i].stars, infoToPrintInfo[i].info))
}
}
func frameworksScoresToString(frameworks []reportsummary.IFrameworkSummary) string {
if len(frameworks) == 1 {
@@ -279,26 +317,6 @@ func frameworksScoresToString(frameworks []reportsummary.IFrameworkSummary) stri
return ""
}
// renderSeverityCountersSummary renders the string that reports severity counters summary
func renderSeverityCountersSummary(counters reportsummary.ISeverityCounters) string {
critical := counters.NumberOfCriticalSeverity()
high := counters.NumberOfHighSeverity()
medium := counters.NumberOfMediumSeverity()
low := counters.NumberOfLowSeverity()
return fmt.Sprintf(
"Failed Resources by Severity: Critical — %d, High — %d, Medium — %d, Low — %d",
critical, high, medium, low,
)
}
func controlCountersForSummary(counters reportsummary.ICounters) string {
return fmt.Sprintf("Controls: %d (Failed: %d, Passed: %d, Action Required: %d)", counters.All(), counters.Failed(), counters.Passed(), counters.Skipped())
}
func controlCountersForResource(l *helpersv1.AllLists) string {
return fmt.Sprintf("Controls: %d (Failed: %d, action required: %d)", l.Len(), l.Failed(), l.Skipped())
}
func getSeparator(sep string) string {
s := ""
for i := 0; i < 80; i++ {

View File

@@ -0,0 +1,73 @@
package prettyprinter
import (
"fmt"
"os"
"github.com/kubescape/kubescape/v2/core/cautils"
"github.com/kubescape/kubescape/v2/core/pkg/resultshandling/printer/v2/prettyprinter/tableprinter/configurationprinter"
"github.com/kubescape/kubescape/v2/core/pkg/resultshandling/printer/v2/prettyprinter/tableprinter/imageprinter"
"github.com/kubescape/opa-utils/reporthandling/results/v1/reportsummary"
)
type ClusterPrinter struct {
writer *os.File
categoriesTablePrinter configurationprinter.TablePrinter
}
func NewClusterPrinter(writer *os.File) *ClusterPrinter {
return &ClusterPrinter{
writer: writer,
categoriesTablePrinter: configurationprinter.NewClusterPrinter(),
}
}
var _ MainPrinter = &ClusterPrinter{}
func (cp *ClusterPrinter) PrintImageScanning(summary *imageprinter.ImageScanSummary) {
printImageScanningSummary(cp.writer, *summary, false)
printImagesCommands(cp.writer, *summary)
}
func (cp *ClusterPrinter) PrintConfigurationsScanning(summaryDetails *reportsummary.SummaryDetails, sortedControlIDs [][]string) {
cp.categoriesTablePrinter.PrintCategoriesTables(cp.writer, summaryDetails, sortedControlIDs)
printComplianceScore(cp.writer, filterComplianceFrameworks(summaryDetails.ListFrameworks()))
if len(summaryDetails.TopWorkloadsByScore) > 0 {
cp.printTopWorkloads(summaryDetails)
}
}
func (cp *ClusterPrinter) PrintNextSteps() {
printNextSteps(cp.writer, cp.getNextSteps(), false)
}
func (cp *ClusterPrinter) getNextSteps() []string {
return []string{
configScanVerboseRunText,
installHelmText,
CICDSetupText,
}
}
func (cp *ClusterPrinter) printTopWorkloads(summaryDetails *reportsummary.SummaryDetails) {
cautils.InfoTextDisplay(cp.writer, getTopWorkloadsTitle(len(summaryDetails.TopWorkloadsByScore)))
for i, wl := range summaryDetails.TopWorkloadsByScore {
ns := wl.Workload.GetNamespace()
name := wl.Workload.GetName()
kind := wl.Workload.GetKind()
cautils.SimpleDisplay(cp.writer, fmt.Sprintf("%d. namespace: %s, name: %s, kind: %s - '%s'\n", i+1, ns, name, kind, getCallToActionString(cp.getWorkloadScanCommand(ns, kind, name))))
}
cautils.SimpleDisplay(cp.writer, "Read more about the most risky workloads here: https://docs.io/most-risky-workloads\n")
cautils.InfoTextDisplay(cp.writer, "\n")
}
func (cp *ClusterPrinter) getWorkloadScanCommand(namespace, kind, name string) string {
return fmt.Sprintf("$ kubescape scan workload %s/%s --namespace %s", kind, name, namespace)
}

View File

@@ -0,0 +1,42 @@
package prettyprinter
import (
"os"
"github.com/kubescape/kubescape/v2/core/pkg/resultshandling/printer/v2/prettyprinter/tableprinter/configurationprinter"
"github.com/kubescape/kubescape/v2/core/pkg/resultshandling/printer/v2/prettyprinter/tableprinter/imageprinter"
"github.com/kubescape/opa-utils/reporthandling/results/v1/reportsummary"
)
var _ MainPrinter = &SummaryPrinter{}
type SummaryPrinter struct {
writer *os.File
verboseMode bool
summaryTablePrinter configurationprinter.TablePrinter
}
func NewSummaryPrinter(writer *os.File, verboseMode bool) *SummaryPrinter {
return &SummaryPrinter{
writer: writer,
verboseMode: verboseMode,
summaryTablePrinter: configurationprinter.NewFrameworkPrinter(verboseMode),
}
}
var _ MainPrinter = &RepoPrinter{}
func (sp *SummaryPrinter) PrintImageScanning(*imageprinter.ImageScanSummary) {
}
func (sp *SummaryPrinter) PrintNextSteps() {
}
func (sp *SummaryPrinter) getVerboseMode() bool {
return sp.verboseMode
}
func (sp *SummaryPrinter) PrintConfigurationsScanning(summaryDetails *reportsummary.SummaryDetails, sortedControlIDs [][]string) {
sp.summaryTablePrinter.PrintSummaryTable(sp.writer, summaryDetails, sortedControlIDs)
}

View File

@@ -0,0 +1,56 @@
package prettyprinter
import (
"os"
"github.com/kubescape/kubescape/v2/core/cautils"
"github.com/kubescape/kubescape/v2/core/pkg/resultshandling/printer/v2/prettyprinter/tableprinter/imageprinter"
"github.com/kubescape/opa-utils/reporthandling/results/v1/reportsummary"
)
const (
TopPackagesNumber = 5
)
type ImagePrinter struct {
writer *os.File
imageTablePrinter imageprinter.TablePrinter
verboseMode bool
}
func NewImagePrinter(writer *os.File, verboseMode bool) *ImagePrinter {
return &ImagePrinter{
writer: writer,
verboseMode: verboseMode,
imageTablePrinter: imageprinter.NewTableWriter(),
}
}
var _ MainPrinter = &ImagePrinter{}
func (ip *ImagePrinter) PrintImageScanning(summary *imageprinter.ImageScanSummary) {
ip.PrintImageScanningTable(*summary)
printImageScanningSummary(ip.writer, *summary, ip.verboseMode)
printTopVulnerabilities(ip.writer, *summary)
}
func (ip *ImagePrinter) PrintImageScanningTable(summary imageprinter.ImageScanSummary) {
if !ip.verboseMode {
// filter out vulnerabilities with severity lower than High
summary.CVEs = filterCVEsBySeverities(summary.CVEs, []string{"Critical", "High"})
}
ip.imageTablePrinter.PrintImageScanningTable(ip.writer, summary)
cautils.InfoTextDisplay(ip.writer, "\n")
}
func (ip *ImagePrinter) PrintConfigurationsScanning(summaryDetails *reportsummary.SummaryDetails, sortedControlIDs [][]string) {
}
func (ip *ImagePrinter) PrintNextSteps() {
if ip.verboseMode {
printNextSteps(ip.writer, []string{CICDSetupText, installHelmText}, true)
return
}
printNextSteps(ip.writer, []string{imageScanVerboseRunText, CICDSetupText, installHelmText}, true)
}

View File

@@ -0,0 +1,12 @@
package prettyprinter
import (
"github.com/kubescape/kubescape/v2/core/pkg/resultshandling/printer/v2/prettyprinter/tableprinter/imageprinter"
"github.com/kubescape/opa-utils/reporthandling/results/v1/reportsummary"
)
type MainPrinter interface {
PrintConfigurationsScanning(summaryDetails *reportsummary.SummaryDetails, sortedControls [][]string)
PrintImageScanning(imageScanSummary *imageprinter.ImageScanSummary)
PrintNextSteps()
}

View File

@@ -0,0 +1,83 @@
package prettyprinter
import (
"fmt"
"os"
"github.com/kubescape/kubescape/v2/core/cautils"
"github.com/kubescape/kubescape/v2/core/pkg/resultshandling/printer/v2/prettyprinter/tableprinter/configurationprinter"
"github.com/kubescape/kubescape/v2/core/pkg/resultshandling/printer/v2/prettyprinter/tableprinter/imageprinter"
"github.com/kubescape/opa-utils/reporthandling"
"github.com/kubescape/opa-utils/reporthandling/results/v1/reportsummary"
)
type RepoPrinter struct {
writer *os.File
inputPatterns []string
categoriesTablePrinter configurationprinter.TablePrinter
}
func NewRepoPrinter(writer *os.File, inputPatterns []string) *RepoPrinter {
return &RepoPrinter{
writer: writer,
categoriesTablePrinter: configurationprinter.NewRepoPrinter(inputPatterns),
}
}
var _ MainPrinter = &RepoPrinter{}
func (rp *RepoPrinter) PrintImageScanning(summary *imageprinter.ImageScanSummary) {
printImageScanningSummary(rp.writer, *summary, false)
printImagesCommands(rp.writer, *summary)
printTopVulnerabilities(rp.writer, *summary)
}
func (rp *RepoPrinter) PrintConfigurationsScanning(summaryDetails *reportsummary.SummaryDetails, sortedControlIDs [][]string) {
rp.categoriesTablePrinter.PrintCategoriesTables(rp.writer, summaryDetails, sortedControlIDs)
if len(summaryDetails.TopWorkloadsByScore) > 1 {
rp.printTopWorkloads(summaryDetails)
}
}
func (rp *RepoPrinter) PrintNextSteps() {
printNextSteps(rp.writer, rp.getNextSteps(), false)
}
func (rp *RepoPrinter) getNextSteps() []string {
return []string{
configScanVerboseRunText,
clusterScanRunText,
CICDSetupText,
installHelmText,
}
}
func (rp *RepoPrinter) printTopWorkloads(summaryDetails *reportsummary.SummaryDetails) {
cautils.InfoTextDisplay(rp.writer, getTopWorkloadsTitle(len(summaryDetails.TopWorkloadsByScore)))
for i, wl := range summaryDetails.TopWorkloadsByScore {
ns := wl.Workload.GetNamespace()
name := wl.Workload.GetName()
kind := wl.Workload.GetKind()
cmdPrefix := getWorkloadPrefixForCmd(ns, kind, name)
cautils.SimpleDisplay(rp.writer, fmt.Sprintf("%d. %s - '%s'\n", i+1, cmdPrefix, getCallToActionString(rp.getWorkloadScanCommand(ns, kind, name, wl.ResourceSource))))
}
cautils.InfoTextDisplay(rp.writer, "\n")
}
func (rp *RepoPrinter) getWorkloadScanCommand(ns, kind, name string, source reporthandling.Source) string {
cmd := fmt.Sprintf("$ kubescape scan workload %s/%s --namespace %s", kind, name, ns)
if ns == "" {
cmd = fmt.Sprintf("$ kubescape scan workload %s/%s", kind, name)
}
if source.FileType == reporthandling.SourceTypeHelmChart {
return fmt.Sprintf("%s --chart-path=%s --file-path=%s", cmd, source.HelmPath, fmt.Sprintf("%s/%s", source.Path, source.RelativePath))
} else {
return fmt.Sprintf("%s --file-path=%s", cmd, fmt.Sprintf("%s/%s", source.Path, source.RelativePath))
}
}

View File

@@ -0,0 +1,115 @@
package configurationprinter
import (
"fmt"
"io"
"github.com/fatih/color"
"github.com/kubescape/kubescape/v2/core/cautils"
"github.com/kubescape/kubescape/v2/core/pkg/resultshandling/printer/v2/prettyprinter/tableprinter/utils"
"github.com/kubescape/opa-utils/reporthandling/apis"
"github.com/kubescape/opa-utils/reporthandling/results/v1/reportsummary"
"github.com/olekukonko/tablewriter"
)
const (
docsPrefix = "https://hub.armosec.io/docs"
scanControlPrefix = "$ kubescape scan control"
controlNameHeader = "CONTROL NAME"
statusHeader = "STATUS"
docsHeader = "DOCS"
resourcesHeader = "RESOURCES"
runHeader = "RUN"
)
// initializes the table headers and column alignments based on the category type
func initCategoryTableData(categoryType CategoryType) ([]string, []int) {
if categoryType == TypeCounting {
return getCategoryCountingTypeHeaders(), getCountingTypeAlignments()
}
return getCategoryStatusTypeHeaders(), getStatusTypeAlignments()
}
func getCategoryStatusTypeHeaders() []string {
headers := make([]string, 3)
headers[0] = controlNameHeader
headers[1] = statusHeader
headers[2] = docsHeader
return headers
}
func getCategoryCountingTypeHeaders() []string {
headers := make([]string, 3)
headers[0] = controlNameHeader
headers[1] = resourcesHeader
headers[2] = runHeader
return headers
}
func getStatusTypeAlignments() []int {
return []int{tablewriter.ALIGN_LEFT, tablewriter.ALIGN_CENTER, tablewriter.ALIGN_CENTER}
}
func getCountingTypeAlignments() []int {
return []int{tablewriter.ALIGN_LEFT, tablewriter.ALIGN_CENTER, tablewriter.ALIGN_LEFT}
}
// returns a row for status type table based on the control summary
func generateCategoryStatusRow(controlSummary reportsummary.IControlSummary, infoToPrintInfo []utils.InfoStars) []string {
// show only passed, failed and action required controls
status := controlSummary.GetStatus()
if !status.IsFailed() && !status.IsSkipped() && !status.IsPassed() {
return nil
}
rows := make([]string, 3)
rows[0] = controlSummary.GetName()
if len(controlSummary.GetName()) > 50 {
rows[0] = controlSummary.GetName()[:50] + "..."
} else {
rows[0] = controlSummary.GetName()
}
rows[1] = color.New(color.Bold, utils.GetStatusColor(controlSummary.GetStatus().Status())).SprintFunc()(getStatus(status, controlSummary, infoToPrintInfo))
rows[2] = getDocsForControl(controlSummary)
return rows
}
func getStatus(status apis.IStatus, controlSummary reportsummary.IControlSummary, infoToPrintInfo []utils.InfoStars) string {
// skipped is shown as action required
if status.IsSkipped() {
return fmt.Sprintf("%s %s", "action required", GetInfoColumn(controlSummary, infoToPrintInfo))
}
return string(controlSummary.GetStatus().Status())
}
func getCategoryTableWriter(writer io.Writer, headers []string, columnAligments []int) *tablewriter.Table {
table := tablewriter.NewWriter(writer)
table.SetHeader(headers)
table.SetHeaderLine(true)
table.SetColumnAlignment(columnAligments)
table.SetAutoWrapText(false)
return table
}
func renderSingleCategory(writer io.Writer, categoryName string, table *tablewriter.Table, rows [][]string, infoToPrintInfo []utils.InfoStars) {
cautils.InfoTextDisplay(writer, categoryName+"\n")
table.ClearRows()
table.AppendBulk(rows)
table.Render()
if len(infoToPrintInfo) > 0 {
printCategoryInfo(writer, infoToPrintInfo)
}
cautils.SimpleDisplay(writer, "\n")
}

View File

@@ -0,0 +1,109 @@
package configurationprinter
import (
"reflect"
"testing"
"github.com/kubescape/kubescape/v2/core/pkg/resultshandling/printer/v2/prettyprinter/tableprinter/utils"
"github.com/kubescape/opa-utils/reporthandling/apis"
"github.com/kubescape/opa-utils/reporthandling/results/v1/reportsummary"
"github.com/olekukonko/tablewriter"
"github.com/stretchr/testify/assert"
)
func TestInitCategoryTableData(t *testing.T) {
tests := []struct {
name string
categoryType CategoryType
expectedHeaders []string
expectedAlignments []int
}{
{
name: "Test1",
categoryType: TypeCounting,
expectedHeaders: []string{"CONTROL NAME", "RESOURCES", "RUN"},
expectedAlignments: []int{tablewriter.ALIGN_LEFT, tablewriter.ALIGN_CENTER, tablewriter.ALIGN_LEFT},
},
{
name: "Test2",
categoryType: TypeStatus,
expectedHeaders: []string{"CONTROL NAME", "STATUS", "DOCS"},
expectedAlignments: []int{tablewriter.ALIGN_LEFT, tablewriter.ALIGN_CENTER, tablewriter.ALIGN_CENTER},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
headers, alignments := initCategoryTableData(tt.categoryType)
if len(headers) != len(tt.expectedHeaders) {
t.Errorf("initCategoryTableData() headers = %v, want %v", headers, tt.expectedHeaders)
}
if len(alignments) != len(tt.expectedAlignments) {
t.Errorf("initCategoryTableData() alignments = %v, want %v", alignments, tt.expectedAlignments)
}
assert.True(t, reflect.DeepEqual(headers, tt.expectedHeaders))
assert.True(t, reflect.DeepEqual(alignments, tt.expectedAlignments))
})
}
}
func TestGenerateCategoryStatusRow(t *testing.T) {
tests := []struct {
name string
controlSummary reportsummary.IControlSummary
infoToPrintInfo []utils.InfoStars
expectedRows []string
}{
{
name: "failed control",
controlSummary: &reportsummary.ControlSummary{
Name: "test",
Status: apis.StatusFailed,
ControlID: "ctrlID",
},
expectedRows: []string{"test", "failed", "https://hub.armosec.io/docs/ctrlid"},
},
{
name: "skipped control",
controlSummary: &reportsummary.ControlSummary{
Name: "test",
Status: apis.StatusSkipped,
StatusInfo: apis.StatusInfo{
InnerInfo: "testInfo",
},
ControlID: "ctrlID",
},
expectedRows: []string{"test", "action required *", "https://hub.armosec.io/docs/ctrlid"},
infoToPrintInfo: []utils.InfoStars{
{
Info: "testInfo",
Stars: "*",
},
},
},
{
name: "passed control",
controlSummary: &reportsummary.ControlSummary{
Name: "test",
Status: apis.StatusPassed,
ControlID: "ctrlID",
},
expectedRows: []string{"test", "passed", "https://hub.armosec.io/docs/ctrlid"},
},
{
name: "big name",
controlSummary: &reportsummary.ControlSummary{
Name: "testtesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttest",
Status: apis.StatusFailed,
ControlID: "ctrlID",
},
expectedRows: []string{"testtesttesttesttesttesttesttesttesttesttesttestte...", "failed", "https://hub.armosec.io/docs/ctrlid"},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
row := generateCategoryStatusRow(tt.controlSummary, tt.infoToPrintInfo)
assert.True(t, reflect.DeepEqual(row, tt.expectedRows))
})
}
}

View File

@@ -0,0 +1,90 @@
package configurationprinter
import (
"fmt"
"io"
"github.com/fatih/color"
"github.com/kubescape/kubescape/v2/core/pkg/resultshandling/printer/v2/prettyprinter/tableprinter/utils"
"github.com/kubescape/opa-utils/reporthandling/results/v1/reportsummary"
)
type ClusterPrinter struct{}
func NewClusterPrinter() *ClusterPrinter {
return &ClusterPrinter{}
}
var _ TablePrinter = &ClusterPrinter{}
func (cp *ClusterPrinter) PrintSummaryTable(writer io.Writer, summaryDetails *reportsummary.SummaryDetails, sortedControlIDs [][]string) {
}
func (cp *ClusterPrinter) PrintCategoriesTables(writer io.Writer, summaryDetails *reportsummary.SummaryDetails, sortedControlIDs [][]string) {
categoriesToCategoryControls := mapCategoryToSummary(summaryDetails.ListControls(), mapClusterControlsToCategories)
for _, id := range clusterCategoriesDisplayOrder {
categoryControl, ok := categoriesToCategoryControls[id]
if !ok {
continue
}
cp.renderSingleCategoryTable(categoryControl.CategoryName, mapCategoryToType[id], writer, categoryControl.controlSummaries, utils.MapInfoToPrintInfoFromIface(categoryControl.controlSummaries))
}
}
func (cp *ClusterPrinter) renderSingleCategoryTable(categoryName string, categoryType CategoryType, writer io.Writer, controlSummaries []reportsummary.IControlSummary, infoToPrintInfo []utils.InfoStars) {
sortControlSummaries(controlSummaries)
headers, columnAligments := initCategoryTableData(categoryType)
table := getCategoryTableWriter(writer, headers, columnAligments)
var rows [][]string
for _, ctrls := range controlSummaries {
var row []string
if categoryType == TypeCounting {
row = cp.generateCountingCategoryRow(ctrls)
} else {
row = generateCategoryStatusRow(ctrls, infoToPrintInfo)
}
if len(row) > 0 {
rows = append(rows, row)
}
}
if len(rows) == 0 {
return
}
renderSingleCategory(writer, categoryName, table, rows, infoToPrintInfo)
}
func (cp *ClusterPrinter) generateCountingCategoryRow(controlSummary reportsummary.IControlSummary) []string {
row := make([]string, 3)
row[0] = controlSummary.GetName()
failedResources := controlSummary.NumberOfResources().Failed()
if failedResources > 0 {
row[1] = string(color.New(color.FgYellow, color.Bold).SprintFunc()(fmt.Sprintf("%d", failedResources)))
} else {
row[1] = fmt.Sprintf("%d", failedResources)
}
row[2] = cp.generateTableNextSteps(controlSummary)
return row
}
func (cp *ClusterPrinter) generateTableNextSteps(controlSummary reportsummary.IControlSummary) string {
return fmt.Sprintf("%s %s -v", scanControlPrefix, controlSummary.GetID())
}
func (cp *ClusterPrinter) generateNextSteps(controlSummary reportsummary.IControlSummary) string {
return fmt.Sprintf("$ kubescape scan control %s", controlSummary.GetID())
}

View File

@@ -0,0 +1,90 @@
package configurationprinter
import (
"testing"
"github.com/kubescape/opa-utils/reporthandling/results/v1/reportsummary"
)
func TestClusterScan_GenerateCountingCategoryRow(t *testing.T) {
tests := []struct {
name string
controlSummary reportsummary.IControlSummary
expectedRow []string
}{
{
name: "failed resources",
controlSummary: &reportsummary.ControlSummary{
ControlID: "ctrl1",
Name: "ctrl1",
StatusCounters: reportsummary.StatusCounters{
FailedResources: 5,
PassedResources: 3,
SkippedResources: 2,
},
},
expectedRow: []string{"ctrl1", "5", "$ kubescape scan control ctrl1 -v"},
},
{
name: "passed resources",
controlSummary: &reportsummary.ControlSummary{
ControlID: "ctrl2",
Name: "ctrl2",
StatusCounters: reportsummary.StatusCounters{
PassedResources: 3,
},
},
expectedRow: []string{"ctrl2", "0", "$ kubescape scan control ctrl2 -v"},
},
}
clusterPrinter := NewClusterPrinter()
for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
row := clusterPrinter.generateCountingCategoryRow(test.controlSummary)
if len(row) != len(test.expectedRow) {
t.Errorf("expected row length %d, got %d", len(test.expectedRow), len(row))
}
for i := range row {
if row[i] != test.expectedRow[i] {
t.Errorf("expected row %v, got %v", test.expectedRow, row)
}
}
})
}
}
func TestClusterScan_GenerateTableNextSteps(t *testing.T) {
tests := []struct {
name string
controlSummary reportsummary.IControlSummary
expectedNextSteps string
}{
{
name: "with id",
controlSummary: &reportsummary.ControlSummary{
ControlID: "ctrl1",
},
expectedNextSteps: "$ kubescape scan control ctrl1 -v",
}, {
name: "empty id",
controlSummary: &reportsummary.ControlSummary{},
expectedNextSteps: "$ kubescape scan control -v",
},
}
clusterPrinter := NewClusterPrinter()
for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
nextSteps := clusterPrinter.generateTableNextSteps(test.controlSummary)
if nextSteps != test.expectedNextSteps {
t.Errorf("expected next steps %s, got %s", test.expectedNextSteps, nextSteps)
}
})
}
}

View File

@@ -0,0 +1,148 @@
package configurationprinter
import (
"github.com/kubescape/opa-utils/reporthandling/apis"
"github.com/kubescape/opa-utils/reporthandling/results/v1/reportsummary"
)
type CategoryControls struct {
CategoryName string
controlSummaries []reportsummary.IControlSummary
Status apis.ScanningStatus
}
type CategoryType string
const (
TypeCounting CategoryType = "COUNTING"
TypeStatus CategoryType = "STATUS"
// Categories to show are hardcoded by ID, so their names are not important. We also want full control over the categories and their order, so a new release of the security checks will not affect the output
// cluster scan categories
controlPlaneCategoryID = "Cat-1"
accessControlCategoryID = "Cat-2"
secretsCategoryID = "Cat-3"
networkCategoryID = "Cat-4"
workloadsCategoryID = "Cat-5"
// workload scan categories
supplyChainCategoryID = "Cat-6"
resourceManagementCategoryID = "Cat-7"
storageCategoryID = "Cat-8"
nodeEscapeCategoryID = "Cat-9"
)
var clusterCategoriesDisplayOrder = []string{
controlPlaneCategoryID,
accessControlCategoryID,
secretsCategoryID,
networkCategoryID,
workloadsCategoryID,
}
var repoCategoriesDisplayOrder = []string{
workloadsCategoryID,
accessControlCategoryID,
secretsCategoryID,
networkCategoryID,
}
var workloadCategoriesDisplayOrder = []string{
supplyChainCategoryID,
resourceManagementCategoryID,
storageCategoryID,
secretsCategoryID,
networkCategoryID,
nodeEscapeCategoryID,
}
// map categories to table type. Each table type has a different display
var mapCategoryToType = map[string]CategoryType{
controlPlaneCategoryID: TypeStatus,
accessControlCategoryID: TypeCounting,
secretsCategoryID: TypeCounting,
networkCategoryID: TypeCounting,
workloadsCategoryID: TypeCounting,
}
var mapClusterControlsToCategories = map[string]string{
"C-0066": controlPlaneCategoryID,
"C-0088": controlPlaneCategoryID,
"C-0067": controlPlaneCategoryID,
"C-0005": controlPlaneCategoryID,
"C-0262": controlPlaneCategoryID,
"C-0015": accessControlCategoryID,
"C-0002": accessControlCategoryID,
"C-0007": accessControlCategoryID,
"C-0063": accessControlCategoryID,
"C-0036": accessControlCategoryID,
"C-0039": accessControlCategoryID,
"C-0035": accessControlCategoryID,
"C-0188": accessControlCategoryID,
"C-0187": accessControlCategoryID,
"C-0012": secretsCategoryID,
"C-0260": networkCategoryID,
"C-0256": networkCategoryID,
"C-0038": workloadsCategoryID,
"C-0041": workloadsCategoryID,
"C-0048": workloadsCategoryID,
"C-0057": workloadsCategoryID,
"C-0013": workloadsCategoryID,
}
var mapWorkloadControlsToCategories = map[string]string{
"C-0078": supplyChainCategoryID,
"C-0236": supplyChainCategoryID,
"C-0237": supplyChainCategoryID,
"C-0004": resourceManagementCategoryID,
"C-0050": resourceManagementCategoryID,
"C-0045": storageCategoryID,
"C-0048": storageCategoryID,
"C-0257": storageCategoryID,
"C-0207": secretsCategoryID,
"C-0034": secretsCategoryID,
"C-0012": secretsCategoryID,
"C-0041": networkCategoryID,
"C-0260": networkCategoryID,
"C-0044": networkCategoryID,
"C-0038": nodeEscapeCategoryID,
"C-0046": nodeEscapeCategoryID,
"C-0013": nodeEscapeCategoryID,
"C-0016": nodeEscapeCategoryID,
"C-0017": nodeEscapeCategoryID,
"C-0055": nodeEscapeCategoryID,
"C-0057": nodeEscapeCategoryID,
}
var mapRepoControlsToCategories = map[string]string{
"C-0015": accessControlCategoryID,
"C-0002": accessControlCategoryID,
"C-0007": accessControlCategoryID,
"C-0063": accessControlCategoryID,
"C-0036": accessControlCategoryID,
"C-0039": accessControlCategoryID,
"C-0035": accessControlCategoryID,
"C-0188": accessControlCategoryID,
"C-0187": accessControlCategoryID,
"C-0012": secretsCategoryID,
"C-0260": networkCategoryID,
"C-0256": networkCategoryID,
"C-0038": workloadsCategoryID,
"C-0041": workloadsCategoryID,
"C-0048": workloadsCategoryID,
"C-0057": workloadsCategoryID,
"C-0013": workloadsCategoryID,
}

View File

@@ -0,0 +1,83 @@
package configurationprinter
import (
"fmt"
"io"
"github.com/kubescape/kubescape/v2/core/cautils"
"github.com/kubescape/kubescape/v2/core/pkg/resultshandling/printer/v2/prettyprinter/tableprinter/utils"
"github.com/kubescape/opa-utils/reporthandling/results/v1/reportsummary"
"github.com/olekukonko/tablewriter"
)
type FrameworkPrinter struct {
verboseMode bool
}
func NewFrameworkPrinter(verboseMode bool) *FrameworkPrinter {
return &FrameworkPrinter{
verboseMode: verboseMode,
}
}
var _ TablePrinter = &FrameworkPrinter{}
func (fp *FrameworkPrinter) getVerboseMode() bool {
return fp.verboseMode
}
func (fp *FrameworkPrinter) PrintSummaryTable(writer io.Writer, summaryDetails *reportsummary.SummaryDetails, sortedControlIDs [][]string) {
if summaryDetails.NumberOfControls().All() == 0 {
fmt.Fprintf(writer, "\nKubescape did not scan any of the resources, make sure you are scanning valid kubernetes manifests (Deployments, Pods, etc.)\n")
return
}
cautils.InfoTextDisplay(writer, "\n"+ControlCountersForSummary(summaryDetails.NumberOfControls())+"\n")
cautils.InfoTextDisplay(writer, renderSeverityCountersSummary(summaryDetails.GetResourcesSeverityCounters())+"\n\n")
summaryTable := tablewriter.NewWriter(writer)
summaryTable.SetAutoWrapText(false)
summaryTable.SetHeader(GetControlTableHeaders())
summaryTable.SetHeaderLine(true)
summaryTable.SetColumnAlignment(GetColumnsAlignments())
printAll := fp.getVerboseMode()
if summaryDetails.NumberOfResources().Failed() == 0 {
// if there are no failed controls, print the resource table and detailed information
printAll = true
}
infoToPrintInfo := utils.MapInfoToPrintInfo(summaryDetails.Controls)
for i := len(sortedControlIDs) - 1; i >= 0; i-- {
for _, c := range sortedControlIDs[i] {
row := GenerateRow(summaryDetails.Controls.GetControl(reportsummary.EControlCriteriaID, c), infoToPrintInfo, printAll)
if len(row) > 0 {
summaryTable.Append(row)
}
}
}
summaryTable.SetFooter(GenerateFooter(summaryDetails))
summaryTable.Render()
// When scanning controls the framework list will be empty
cautils.InfoTextDisplay(writer, utils.FrameworksScoresToString(summaryDetails.ListFrameworks()))
utils.PrintInfo(writer, infoToPrintInfo)
}
func (fp *FrameworkPrinter) PrintCategoriesTables(writer io.Writer, summaryDetails *reportsummary.SummaryDetails, sortedControlIDs [][]string) {
}
func renderSeverityCountersSummary(counters reportsummary.ISeverityCounters) string {
critical := counters.NumberOfCriticalSeverity()
high := counters.NumberOfHighSeverity()
medium := counters.NumberOfMediumSeverity()
low := counters.NumberOfLowSeverity()
return fmt.Sprintf(
"Failed Resources by Severity: Critical — %d, High — %d, Medium — %d, Low — %d",
critical, high, medium, low,
)
}

View File

@@ -0,0 +1,12 @@
package configurationprinter
import (
"io"
"github.com/kubescape/opa-utils/reporthandling/results/v1/reportsummary"
)
type TablePrinter interface {
PrintCategoriesTables(writer io.Writer, summaryDetails *reportsummary.SummaryDetails, sortedControlIDs [][]string)
PrintSummaryTable(writer io.Writer, summaryDetails *reportsummary.SummaryDetails, sortedControlIDs [][]string)
}

View File

@@ -0,0 +1,113 @@
package configurationprinter
import (
"fmt"
"io"
"strings"
"github.com/fatih/color"
"github.com/kubescape/kubescape/v2/core/pkg/resultshandling/printer/v2/prettyprinter/tableprinter/utils"
"github.com/kubescape/opa-utils/reporthandling"
"github.com/kubescape/opa-utils/reporthandling/apis"
"github.com/kubescape/opa-utils/reporthandling/results/v1/reportsummary"
)
type RepoPrinter struct {
inputPatterns []string
}
func NewRepoPrinter(inputPatterns []string) *RepoPrinter {
return &RepoPrinter{
inputPatterns: inputPatterns,
}
}
var _ TablePrinter = &RepoPrinter{}
func (rp *RepoPrinter) PrintSummaryTable(writer io.Writer, summaryDetails *reportsummary.SummaryDetails, sortedControlIDs [][]string) {
}
func (rp *RepoPrinter) PrintCategoriesTables(writer io.Writer, summaryDetails *reportsummary.SummaryDetails, sortedControlIDs [][]string) {
categoriesToCategoryControls := mapCategoryToSummary(summaryDetails.ListControls(), mapRepoControlsToCategories)
for _, id := range repoCategoriesDisplayOrder {
categoryControl, ok := categoriesToCategoryControls[id]
if !ok {
continue
}
if categoryControl.Status != apis.StatusFailed {
continue
}
rp.renderSingleCategoryTable(categoryControl.CategoryName, mapCategoryToType[id], writer, categoryControl.controlSummaries, utils.MapInfoToPrintInfoFromIface(categoryControl.controlSummaries))
}
}
func (rp *RepoPrinter) renderSingleCategoryTable(categoryName string, categoryType CategoryType, writer io.Writer, controlSummaries []reportsummary.IControlSummary, infoToPrintInfo []utils.InfoStars) {
sortControlSummaries(controlSummaries)
headers, columnAligments := initCategoryTableData(categoryType)
table := getCategoryTableWriter(writer, headers, columnAligments)
var rows [][]string
for _, ctrls := range controlSummaries {
if ctrls.NumberOfResources().Failed() == 0 {
continue
}
var row []string
if categoryType == TypeCounting {
row = rp.generateCountingCategoryRow(ctrls, rp.inputPatterns)
} else {
row = generateCategoryStatusRow(ctrls, infoToPrintInfo)
}
if len(row) > 0 {
rows = append(rows, row)
}
}
if len(rows) == 0 {
return
}
renderSingleCategory(writer, categoryName, table, rows, infoToPrintInfo)
}
func (rp *RepoPrinter) generateCountingCategoryRow(controlSummary reportsummary.IControlSummary, inputPatterns []string) []string {
rows := make([]string, 3)
rows[0] = controlSummary.GetName()
failedResources := controlSummary.NumberOfResources().Failed()
if failedResources > 0 {
rows[1] = string(color.New(color.FgYellow, color.Bold).SprintFunc()(fmt.Sprintf("%d", failedResources)))
} else {
rows[1] = fmt.Sprintf("%d", failedResources)
}
rows[2] = rp.generateTableNextSteps(controlSummary, inputPatterns)
return rows
}
func (rp *RepoPrinter) getWorkloadScanCommand(ns, kind, name string, source reporthandling.Source) string {
cmd := fmt.Sprintf("$ kubescape scan workload %s/%s/%s", ns, kind, name)
if ns == "" {
cmd = fmt.Sprintf("$ kubescape scan workload %s/%s", kind, name)
}
if source.FileType == "Helm" {
return fmt.Sprintf("%s --chart-path=%s", cmd, source.RelativePath)
} else {
return fmt.Sprintf("%s --file-path=%s", cmd, source.RelativePath)
}
}
func (rp *RepoPrinter) generateTableNextSteps(controlSummary reportsummary.IControlSummary, inputPatterns []string) string {
return fmt.Sprintf("$ kubescape scan control %s %s -v", controlSummary.GetID(), strings.Join(inputPatterns, ","))
}

View File

@@ -0,0 +1,102 @@
package configurationprinter
import (
"testing"
"github.com/kubescape/opa-utils/reporthandling/results/v1/reportsummary"
)
func TestRepoScan_GenerateCountingCategoryRow(t *testing.T) {
tests := []struct {
name string
controlSummary reportsummary.ControlSummary
expectedRow []string
inputPatterns []string
}{
{
name: "multiple files",
controlSummary: reportsummary.ControlSummary{
ControlID: "ctrl1",
Name: "ctrl1",
StatusCounters: reportsummary.StatusCounters{
FailedResources: 5,
PassedResources: 3,
SkippedResources: 2,
},
},
inputPatterns: []string{"file.yaml", "file2.yaml"},
expectedRow: []string{"ctrl1", "5", "$ kubescape scan control ctrl1 file.yaml,file2.yaml"},
},
{
name: "one file",
controlSummary: reportsummary.ControlSummary{
ControlID: "ctrl1",
Name: "ctrl1",
StatusCounters: reportsummary.StatusCounters{
FailedResources: 5,
PassedResources: 3,
SkippedResources: 2,
},
},
inputPatterns: []string{"file.yaml"},
expectedRow: []string{"ctrl1", "5", "$ kubescape scan control ctrl1 file.yaml"},
},
}
repoPrinter := NewRepoPrinter(nil)
for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
row := repoPrinter.generateCountingCategoryRow(&test.controlSummary, test.inputPatterns)
if len(row) != len(test.expectedRow) {
t.Errorf("expected row length %d, got %d", len(test.expectedRow), len(row))
}
for i := range row {
if row[i] != test.expectedRow[i] {
t.Errorf("expected row %v, got %v", test.expectedRow, row)
}
}
})
}
}
func TestRepoScan_GenerateTableNextSteps(t *testing.T) {
tests := []struct {
name string
controlSummary reportsummary.ControlSummary
expectedNextSteps string
inputPatterns []string
}{
{
name: "single file",
controlSummary: reportsummary.ControlSummary{
ControlID: "ctrl1",
},
inputPatterns: []string{"file.yaml"},
expectedNextSteps: "$ kubescape scan control ctrl1 file.yaml",
},
{
name: "multiple files",
controlSummary: reportsummary.ControlSummary{
ControlID: "ctrl1",
},
inputPatterns: []string{"file.yaml", "file2.yaml"},
expectedNextSteps: "$ kubescape scan control ctrl1 file.yaml,file2.yaml",
},
}
repoPrinter := NewRepoPrinter(nil)
for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
nextSteps := repoPrinter.generateTableNextSteps(&test.controlSummary, test.inputPatterns)
if nextSteps != test.expectedNextSteps {
t.Errorf("expected next steps %s, got %s", test.expectedNextSteps, nextSteps)
}
})
}
}

View File

@@ -0,0 +1,98 @@
package configurationprinter
import (
"fmt"
"github.com/fatih/color"
"github.com/kubescape/kubescape/v2/core/cautils"
"github.com/kubescape/kubescape/v2/core/pkg/resultshandling/printer/v2/prettyprinter/tableprinter/utils"
"github.com/kubescape/opa-utils/reporthandling/apis"
"github.com/kubescape/opa-utils/reporthandling/results/v1/reportsummary"
"github.com/olekukonko/tablewriter"
)
const (
summaryColumnSeverity = iota
summaryColumnName = iota
summaryColumnCounterFailed = iota
summaryColumnCounterAll = iota
summaryColumnComplianceScore = iota
_summaryRowLen = iota
)
func ControlCountersForSummary(counters reportsummary.ICounters) string {
return fmt.Sprintf("Controls: %d (Failed: %d, Passed: %d, Action Required: %d)", counters.All(), counters.Failed(), counters.Passed(), counters.Skipped())
}
func GetSeverityColumn(controlSummary reportsummary.IControlSummary) string {
return color.New(utils.GetColor(apis.ControlSeverityToInt(controlSummary.GetScoreFactor())), color.Bold).SprintFunc()(apis.ControlSeverityToString(controlSummary.GetScoreFactor()))
}
func GetControlTableHeaders() []string {
headers := make([]string, _summaryRowLen)
headers[summaryColumnName] = "CONTROL NAME"
headers[summaryColumnCounterFailed] = "FAILED RESOURCES"
headers[summaryColumnCounterAll] = "ALL RESOURCES"
headers[summaryColumnSeverity] = "SEVERITY"
headers[summaryColumnComplianceScore] = "% COMPLIANCE-SCORE"
return headers
}
func GetColumnsAlignments() []int {
alignments := make([]int, _summaryRowLen)
alignments[summaryColumnName] = tablewriter.ALIGN_LEFT
alignments[summaryColumnCounterFailed] = tablewriter.ALIGN_CENTER
alignments[summaryColumnCounterAll] = tablewriter.ALIGN_CENTER
alignments[summaryColumnSeverity] = tablewriter.ALIGN_LEFT
alignments[summaryColumnComplianceScore] = tablewriter.ALIGN_CENTER
return alignments
}
func GenerateRow(controlSummary reportsummary.IControlSummary, infoToPrintInfo []utils.InfoStars, verbose bool) []string {
row := make([]string, _summaryRowLen)
// ignore passed results
if !verbose && (controlSummary.GetStatus().IsPassed()) {
return []string{}
}
row[summaryColumnSeverity] = GetSeverityColumn(controlSummary)
if len(controlSummary.GetName()) > 50 {
row[summaryColumnName] = controlSummary.GetName()[:50] + "..."
} else {
row[summaryColumnName] = controlSummary.GetName()
}
row[summaryColumnCounterFailed] = fmt.Sprintf("%d", controlSummary.NumberOfResources().Failed())
row[summaryColumnCounterAll] = fmt.Sprintf("%d", controlSummary.NumberOfResources().All())
row[summaryColumnComplianceScore] = GetComplianceScoreColumn(controlSummary, infoToPrintInfo)
return row
}
func GetComplianceScoreColumn(controlSummary reportsummary.IControlSummary, infoToPrintInfo []utils.InfoStars) string {
if controlSummary.GetStatus().IsSkipped() {
return fmt.Sprintf("%s %s", "Action Required", GetInfoColumn(controlSummary, infoToPrintInfo))
}
return fmt.Sprintf("%d", cautils.Float32ToInt(controlSummary.GetComplianceScore())) + "%"
}
func GetInfoColumn(controlSummary reportsummary.IControlSummary, infoToPrintInfo []utils.InfoStars) string {
for i := range infoToPrintInfo {
if infoToPrintInfo[i].Info == controlSummary.GetStatus().Info() {
return infoToPrintInfo[i].Stars
}
}
return ""
}
func GenerateFooter(summaryDetails *reportsummary.SummaryDetails) []string {
// Severity | Control name | failed resources | all resources | % success
row := make([]string, _summaryRowLen)
row[summaryColumnName] = "Resource Summary"
row[summaryColumnCounterFailed] = fmt.Sprintf("%d", summaryDetails.NumberOfResources().Failed())
row[summaryColumnCounterAll] = fmt.Sprintf("%d", summaryDetails.NumberOfResources().All())
row[summaryColumnSeverity] = " "
row[summaryColumnComplianceScore] = fmt.Sprintf("%.2f%s", summaryDetails.ComplianceScore, "%")
return row
}

View File

@@ -0,0 +1,98 @@
package configurationprinter
import (
"fmt"
"io"
"sort"
"strings"
"github.com/kubescape/kubescape/v2/core/cautils"
"github.com/kubescape/kubescape/v2/core/pkg/resultshandling/printer/v2/prettyprinter/tableprinter/utils"
"github.com/kubescape/opa-utils/reporthandling/apis"
"github.com/kubescape/opa-utils/reporthandling/results/v1/reportsummary"
)
// returns map of category ID to category controls (name and controls)
// controls will be on the map only if the are in the mapClusterControlsToCategories map
func mapCategoryToSummary(controlSummaries []reportsummary.IControlSummary, mapDisplayCtrlIDToCategory map[string]string) map[string]CategoryControls {
mapCategoriesToCtrlSummary := map[string][]reportsummary.IControlSummary{}
// helper map to get the category name
mapCategoryIDToName := make(map[string]string)
for i := range controlSummaries {
// check if we need to print this control
category, ok := mapDisplayCtrlIDToCategory[controlSummaries[i].GetID()]
if !ok {
continue
}
// the category on the map can be either category or subcategory, so we need to check both
if controlSummaries[i].GetCategory().ID == category {
if _, ok := mapCategoriesToCtrlSummary[controlSummaries[i].GetCategory().ID]; !ok {
mapCategoryIDToName[controlSummaries[i].GetCategory().ID] = controlSummaries[i].GetCategory().Name // set category name
mapCategoriesToCtrlSummary[controlSummaries[i].GetCategory().ID] = []reportsummary.IControlSummary{}
}
mapCategoriesToCtrlSummary[controlSummaries[i].GetCategory().ID] = append(mapCategoriesToCtrlSummary[controlSummaries[i].GetCategory().ID], controlSummaries[i])
continue
}
if controlSummaries[i].GetCategory().SubCategory.ID == category {
if _, ok := mapCategoriesToCtrlSummary[controlSummaries[i].GetCategory().SubCategory.ID]; !ok {
mapCategoryIDToName[controlSummaries[i].GetCategory().SubCategory.ID] = controlSummaries[i].GetCategory().SubCategory.Name // set category name
mapCategoriesToCtrlSummary[controlSummaries[i].GetCategory().SubCategory.ID] = []reportsummary.IControlSummary{}
}
mapCategoriesToCtrlSummary[controlSummaries[i].GetCategory().SubCategory.ID] = append(mapCategoriesToCtrlSummary[controlSummaries[i].GetCategory().SubCategory.ID], controlSummaries[i])
continue
}
}
mapCategoryToControls := buildCategoryToControlsMap(mapCategoriesToCtrlSummary, mapCategoryIDToName)
return mapCategoryToControls
}
// returns map of category ID to category controls (name and controls)
func buildCategoryToControlsMap(mapCategoriesToCtrlSummary map[string][]reportsummary.IControlSummary, mapCategoryIDToName map[string]string) map[string]CategoryControls {
mapCategoryToControls := make(map[string]CategoryControls)
for categoryID, ctrls := range mapCategoriesToCtrlSummary {
status := apis.StatusPassed
for _, ctrl := range ctrls {
if ctrl.GetStatus().Status() == apis.StatusFailed {
status = apis.StatusFailed
break
}
}
categoryName := mapCategoryIDToName[categoryID]
mapCategoryToControls[categoryID] = CategoryControls{
CategoryName: categoryName,
controlSummaries: ctrls,
Status: status,
}
}
return mapCategoryToControls
}
// returns doc link for control
func getDocsForControl(controlSummary reportsummary.IControlSummary) string {
return fmt.Sprintf("%s/%s", docsPrefix, strings.ToLower(controlSummary.GetID()))
}
// returns run command with verbose for control
func getRunCommandForControl(controlSummary reportsummary.IControlSummary) string {
return fmt.Sprintf("%s %s -v", scanControlPrefix, controlSummary.GetID())
}
func sortControlSummaries(controlSummaries []reportsummary.IControlSummary) {
sort.Slice(controlSummaries, func(i, j int) bool {
return controlSummaries[i].GetName() < controlSummaries[j].GetName()
})
}
func printCategoryInfo(writer io.Writer, infoToPrintInfo []utils.InfoStars) {
for i := range infoToPrintInfo {
cautils.InfoDisplay(writer, fmt.Sprintf("%s %s\n", infoToPrintInfo[i].Stars, infoToPrintInfo[i].Info))
}
}

View File

@@ -0,0 +1,570 @@
package configurationprinter
import (
"testing"
"github.com/kubescape/opa-utils/reporthandling"
"github.com/kubescape/opa-utils/reporthandling/apis"
"github.com/kubescape/opa-utils/reporthandling/results/v1/reportsummary"
)
func TestMapCategoryToSummary(t *testing.T) {
tests := []struct {
name string
ctrlSummaries map[string]reportsummary.ControlSummary
mapDisplayCtrlIDToCategory map[string]string
expected map[string]CategoryControls
}{
{
name: "controls mapped to right categories",
ctrlSummaries: map[string]reportsummary.ControlSummary{
"controlName1": {
ControlID: "ctrlID1",
Status: apis.StatusFailed,
Category: reporthandling.Category{
Name: "category1",
ID: "catID1",
},
},
"controlName2": {
ControlID: "ctrlID2",
Status: apis.StatusFailed,
Category: reporthandling.Category{
Name: "category1",
ID: "catID1",
},
},
"controlName3": {
ControlID: "ctrlID3",
Status: apis.StatusFailed,
Category: reporthandling.Category{
Name: "category2",
ID: "catID2",
},
},
},
mapDisplayCtrlIDToCategory: map[string]string{
"ctrlID1": "catID1",
"ctrlID2": "catID1",
"ctrlID3": "catID2",
},
expected: map[string]CategoryControls{
"catID1": {
CategoryName: "category1",
controlSummaries: []reportsummary.IControlSummary{
&reportsummary.ControlSummary{
ControlID: "ctrlID1",
},
&reportsummary.ControlSummary{
ControlID: "ctrlID2",
},
},
},
"catID2": {
CategoryName: "category2",
controlSummaries: []reportsummary.IControlSummary{
&reportsummary.ControlSummary{
ControlID: "ctrlID3",
},
},
},
},
},
{
name: "empty display map",
ctrlSummaries: map[string]reportsummary.ControlSummary{
"controlName1": {
ControlID: "ctrlID1",
Status: apis.StatusFailed,
Category: reporthandling.Category{
Name: "category1",
ID: "catID1",
},
},
"controlName2": {
ControlID: "ctrlID2",
Status: apis.StatusFailed,
Category: reporthandling.Category{
Name: "category1",
ID: "catID1",
},
},
"controlName3": {
ControlID: "ctrlID3",
Status: apis.StatusFailed,
Category: reporthandling.Category{
Name: "category2",
ID: "catID2",
},
},
},
mapDisplayCtrlIDToCategory: map[string]string{},
expected: map[string]CategoryControls{},
},
{
name: "controls not in map are not mapped",
ctrlSummaries: map[string]reportsummary.ControlSummary{
"controlName1": {
ControlID: "ctrlID1",
Status: apis.StatusFailed,
Category: reporthandling.Category{
Name: "category1",
ID: "catID1",
},
},
"controlName2": {
ControlID: "ctrlID2",
Status: apis.StatusFailed,
Category: reporthandling.Category{
Name: "category1",
ID: "catID1",
},
},
"controlName3": {
ControlID: "ctrlID3",
Status: apis.StatusFailed,
Category: reporthandling.Category{
Name: "category2",
ID: "catID2",
},
},
},
mapDisplayCtrlIDToCategory: map[string]string{
"ctrlID3": "catID2",
},
expected: map[string]CategoryControls{
"catID2": {
CategoryName: "category2",
controlSummaries: []reportsummary.IControlSummary{
&reportsummary.ControlSummary{
ControlID: "ctrlID3",
},
},
},
},
},
{
name: "controls mapped to right sub-categories",
ctrlSummaries: map[string]reportsummary.ControlSummary{
"controlName1": {
ControlID: "ctrlID1",
Status: apis.StatusFailed,
Category: reporthandling.Category{
Name: "category1",
ID: "catID1",
SubCategory: reporthandling.SubCategory{
Name: "subCategory1",
ID: "subCatID1",
},
},
},
"controlName2": {
ControlID: "ctrlID2",
Status: apis.StatusFailed,
Category: reporthandling.Category{
Name: "category1",
ID: "catID1",
SubCategory: reporthandling.SubCategory{
Name: "subCategory1",
ID: "subCatID1",
},
},
},
"controlName3": {
ControlID: "ctrlID3",
Status: apis.StatusFailed,
Category: reporthandling.Category{
Name: "category2",
ID: "catID2",
SubCategory: reporthandling.SubCategory{
Name: "subCategory2",
ID: "subCatID2",
},
},
},
},
mapDisplayCtrlIDToCategory: map[string]string{
"ctrlID1": "subCatID1",
"ctrlID2": "subCatID1",
"ctrlID3": "subCatID2",
},
expected: map[string]CategoryControls{
"subCatID1": {
CategoryName: "subCategory1",
controlSummaries: []reportsummary.IControlSummary{
&reportsummary.ControlSummary{
ControlID: "ctrlID1",
},
&reportsummary.ControlSummary{
ControlID: "ctrlID2",
},
},
},
"subCatID2": {
CategoryName: "subCategory2",
controlSummaries: []reportsummary.IControlSummary{
&reportsummary.ControlSummary{
ControlID: "ctrlID3",
},
},
},
},
},
{
name: "controls mapped to categories and sub-categories",
ctrlSummaries: map[string]reportsummary.ControlSummary{
"controlName1": {
ControlID: "ctrlID1",
Status: apis.StatusFailed,
Category: reporthandling.Category{
Name: "category1",
ID: "catID1",
SubCategory: reporthandling.SubCategory{
Name: "subCategory1",
ID: "subCatID1",
},
},
},
"controlName2": {
ControlID: "ctrlID2",
Status: apis.StatusFailed,
Category: reporthandling.Category{
Name: "category1",
ID: "catID1",
SubCategory: reporthandling.SubCategory{
Name: "subCategory1",
ID: "subCatID1",
},
},
},
"controlName3": {
ControlID: "ctrlID3",
Status: apis.StatusFailed,
Category: reporthandling.Category{
Name: "category2",
ID: "catID2",
SubCategory: reporthandling.SubCategory{
Name: "subCategory2",
ID: "subCatID2",
},
},
},
},
mapDisplayCtrlIDToCategory: map[string]string{
"ctrlID1": "catID1",
"ctrlID2": "subCatID1",
"ctrlID3": "subCatID2",
},
expected: map[string]CategoryControls{
"catID1": {
CategoryName: "category1",
controlSummaries: []reportsummary.IControlSummary{
&reportsummary.ControlSummary{
ControlID: "ctrlID1",
},
},
},
"subCatID1": {
CategoryName: "subCategory1",
controlSummaries: []reportsummary.IControlSummary{
&reportsummary.ControlSummary{
ControlID: "ctrlID2",
},
},
},
"subCatID2": {
CategoryName: "subCategory2",
controlSummaries: []reportsummary.IControlSummary{
&reportsummary.ControlSummary{
ControlID: "ctrlID3",
},
},
},
},
},
}
for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
summaryDetails := reportsummary.SummaryDetails{
Controls: test.ctrlSummaries,
}
actual := mapCategoryToSummary(summaryDetails.ListControls(), test.mapDisplayCtrlIDToCategory)
if len(actual) != len(test.expected) {
t.Errorf("expected %d categories, got %d", len(test.expected), len(actual))
}
for categoryID, category := range actual {
expectedCategory, ok := test.expected[categoryID]
if !ok {
t.Errorf("unexpected category %s", categoryID)
}
if category.CategoryName != expectedCategory.CategoryName {
t.Errorf("expected category name %s, got %s", test.expected[category.CategoryName].CategoryName, category.CategoryName)
}
if len(category.controlSummaries) != len(expectedCategory.controlSummaries) {
t.Errorf("expected %d controls, got %d", len(test.expected[category.CategoryName].controlSummaries), len(category.controlSummaries))
}
for i := range category.controlSummaries {
found := false
for j := range expectedCategory.controlSummaries {
if category.controlSummaries[i].GetID() == expectedCategory.controlSummaries[j].GetID() {
found = true
break
}
}
if !found {
t.Errorf("unexpected control %s", category.controlSummaries[i].GetID())
}
}
}
})
}
}
func TestBuildCategoryToControlsMap(t *testing.T) {
tests := []struct {
name string
mapCategoriesToCtrlSummary map[string][]reportsummary.ControlSummary
mapCategoryIDToName map[string]string
expected map[string]CategoryControls
}{
{
name: "build map of categories to controls",
mapCategoriesToCtrlSummary: map[string][]reportsummary.ControlSummary{
"catID1": {
{
ControlID: "ctrlID1",
},
},
"catID2": {
{
ControlID: "ctrlID2",
},
},
"catID3": {
{
ControlID: "ctrlID3",
},
{
ControlID: "ctrlID4",
},
},
},
mapCategoryIDToName: map[string]string{
"catID1": "category1",
"catID2": "category2",
"catID3": "category3",
},
expected: map[string]CategoryControls{
"catID1": {
CategoryName: "category1",
controlSummaries: []reportsummary.IControlSummary{
&reportsummary.ControlSummary{
ControlID: "ctrlID1",
},
},
},
"catID2": {
CategoryName: "category2",
controlSummaries: []reportsummary.IControlSummary{
&reportsummary.ControlSummary{
ControlID: "ctrlID2",
},
},
},
"catID3": {
CategoryName: "category3",
controlSummaries: []reportsummary.IControlSummary{
&reportsummary.ControlSummary{
ControlID: "ctrlID3",
},
&reportsummary.ControlSummary{
ControlID: "ctrlID4",
},
},
},
},
},
{
name: "build map of categories to controls with empty map",
mapCategoriesToCtrlSummary: map[string][]reportsummary.ControlSummary{},
mapCategoryIDToName: map[string]string{},
expected: map[string]CategoryControls{},
},
{
name: "two categories with same name",
mapCategoriesToCtrlSummary: map[string][]reportsummary.ControlSummary{
"catID1": {
{
ControlID: "ctrlID1",
},
},
"catID2": {
{
ControlID: "ctrlID2",
},
},
"catID3": {
{
ControlID: "ctrlID3",
},
},
},
mapCategoryIDToName: map[string]string{
"catID1": "category1",
"catID2": "category1",
"catID3": "category2",
},
expected: map[string]CategoryControls{
"catID1": {
CategoryName: "category1",
controlSummaries: []reportsummary.IControlSummary{
&reportsummary.ControlSummary{
ControlID: "ctrlID1",
},
},
},
"catID2": {
CategoryName: "category1",
controlSummaries: []reportsummary.IControlSummary{
&reportsummary.ControlSummary{
ControlID: "ctrlID2",
},
},
},
"catID3": {
CategoryName: "category2",
controlSummaries: []reportsummary.IControlSummary{
&reportsummary.ControlSummary{
ControlID: "ctrlID3",
},
},
},
},
},
}
for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
ctrlSummaries := make(map[string][]reportsummary.IControlSummary, 0)
for id, summaries := range test.mapCategoriesToCtrlSummary {
for _, summary := range summaries {
if _, ok := ctrlSummaries[id]; !ok {
ctrlSummaries[id] = []reportsummary.IControlSummary{}
}
ctrlSummaries[id] = append(ctrlSummaries[id], &summary)
}
}
actual := buildCategoryToControlsMap(ctrlSummaries, test.mapCategoryIDToName)
if len(actual) != len(test.expected) {
t.Errorf("expected %d categories, got %d", len(test.expected), len(actual))
}
for categoryID, category := range actual {
expectedCategory, ok := test.expected[categoryID]
if !ok {
t.Errorf("unexpected category %s", categoryID)
}
if category.CategoryName != expectedCategory.CategoryName {
t.Errorf("expected category name %s, got %s", test.expected[category.CategoryName].CategoryName, category.CategoryName)
}
if len(category.controlSummaries) != len(expectedCategory.controlSummaries) {
t.Errorf("expected %d controls, got %d", len(test.expected[category.CategoryName].controlSummaries), len(category.controlSummaries))
}
for i := range category.controlSummaries {
found := false
for j := range expectedCategory.controlSummaries {
if category.controlSummaries[i].GetID() == expectedCategory.controlSummaries[j].GetID() {
found = true
break
}
}
if !found {
t.Errorf("unexpected control %s", category.controlSummaries[i].GetID())
}
}
}
})
}
}
func TestGetDocsForControl(t *testing.T) {
tests := []struct {
name string
controlSummary reportsummary.IControlSummary
expectedDocsLink string
}{
{
name: "control with uppercase ID",
controlSummary: &reportsummary.ControlSummary{
ControlID: "ctrlID1",
},
expectedDocsLink: "https://hub.armosec.io/docs/ctrlid1",
},
{
name: "control with lowercase ID",
controlSummary: &reportsummary.ControlSummary{
ControlID: "ctrlid1",
},
expectedDocsLink: "https://hub.armosec.io/docs/ctrlid1",
},
}
for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
actual := getDocsForControl(test.controlSummary)
if actual != test.expectedDocsLink {
t.Errorf("expected %s, got %s", test.expectedDocsLink, actual)
}
})
}
}
func TestGetRunCommandForControl(t *testing.T) {
tests := []struct {
name string
controlSummary reportsummary.IControlSummary
expectedRunLink string
}{
{
name: "control with uppercase ID",
controlSummary: &reportsummary.ControlSummary{
ControlID: "ctrlID1",
},
expectedRunLink: "$ kubescape scan control ctrlID1 -v",
},
{
name: "control with lowercase ID",
controlSummary: &reportsummary.ControlSummary{
ControlID: "ctrlid1",
},
expectedRunLink: "$ kubescape scan control ctrlid1 -v",
},
}
for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
actualLink := getRunCommandForControl(test.controlSummary)
if actualLink != test.expectedRunLink {
t.Errorf("expected %s, got %s", test.expectedRunLink, actualLink)
}
})
}
}

View File

@@ -0,0 +1,101 @@
package configurationprinter
import (
"fmt"
"io"
"github.com/kubescape/kubescape/v2/core/pkg/resultshandling/printer/v2/prettyprinter/tableprinter/utils"
"github.com/kubescape/opa-utils/reporthandling/results/v1/reportsummary"
"github.com/olekukonko/tablewriter"
)
type WorkloadPrinter struct {
}
var _ TablePrinter = &WorkloadPrinter{}
func NewWorkloadPrinter() *WorkloadPrinter {
return &WorkloadPrinter{}
}
func (wp *WorkloadPrinter) PrintSummaryTable(writer io.Writer, summaryDetails *reportsummary.SummaryDetails, sortedControlIDs [][]string) {
}
func (wp *WorkloadPrinter) PrintCategoriesTables(writer io.Writer, summaryDetails *reportsummary.SummaryDetails, sortedControlIDs [][]string) {
categoriesToCategoryControls := mapCategoryToSummary(summaryDetails.ListControls(), mapWorkloadControlsToCategories)
for _, id := range workloadCategoriesDisplayOrder {
categoryControl, ok := categoriesToCategoryControls[id]
if !ok {
continue
}
wp.renderSingleCategoryTable(categoryControl.CategoryName, mapCategoryToType[id], writer, categoryControl.controlSummaries, utils.MapInfoToPrintInfoFromIface(categoryControl.controlSummaries))
}
}
func (wp *WorkloadPrinter) renderSingleCategoryTable(categoryName string, categoryType CategoryType, writer io.Writer, controlSummaries []reportsummary.IControlSummary, infoToPrintInfo []utils.InfoStars) {
sortControlSummaries(controlSummaries)
headers, columnAligments := wp.initCategoryTableData(categoryType)
table := getCategoryTableWriter(writer, headers, columnAligments)
var rows [][]string
for _, ctrls := range controlSummaries {
var row []string
row = generateCategoryStatusRow(ctrls, infoToPrintInfo)
if len(row) > 0 {
rows = append(rows, row)
}
}
if len(rows) == 0 {
return
}
renderSingleCategory(writer, categoryName, table, rows, infoToPrintInfo)
}
func (wp *WorkloadPrinter) initCategoryTableData(categoryType CategoryType) ([]string, []int) {
if categoryType == TypeCounting {
return wp.getCategoryCountingTypeHeaders(), wp.getCountingTypeAlignments()
}
return getCategoryStatusTypeHeaders(), getStatusTypeAlignments()
}
func (wp *WorkloadPrinter) generateCountingCategoryRow(controlSummary reportsummary.IControlSummary, infoToPrintInfo []utils.InfoStars) []string {
row := make([]string, 3)
row[0] = controlSummary.GetName()
row[1] = getStatus(controlSummary.GetStatus(), controlSummary, infoToPrintInfo)
row[2] = getDocsForControl(controlSummary)
return row
}
func (wp *WorkloadPrinter) getCategoriesColumnsAlignments() []int {
return getCountingTypeAlignments()
}
func (wp *WorkloadPrinter) generateNextSteps(controlSummary reportsummary.IControlSummary) string {
return fmt.Sprintf("$ kubescape scan wokrload <ns>/<kind>/<name> %s", controlSummary.GetID())
}
func (wp *WorkloadPrinter) getCategoryCountingTypeHeaders() []string {
headers := make([]string, 3)
headers[0] = controlNameHeader
headers[1] = statusHeader
headers[2] = docsHeader
return headers
}
func (wp *WorkloadPrinter) getCountingTypeAlignments() []int {
return []int{tablewriter.ALIGN_LEFT, tablewriter.ALIGN_CENTER, tablewriter.ALIGN_LEFT}
}

View File

@@ -0,0 +1,116 @@
package configurationprinter
import (
"reflect"
"testing"
"github.com/kubescape/opa-utils/reporthandling/results/v1/reportsummary"
"github.com/olekukonko/tablewriter"
"github.com/stretchr/testify/assert"
)
func TestWorkloadScan_InitCategoryTableData(t *testing.T) {
tests := []struct {
name string
categoryType CategoryType
expectedHeaders []string
expectedAlignments []int
}{
{
name: "Test1",
categoryType: TypeCounting,
expectedHeaders: []string{"CONTROL NAME", "RESOURCES"},
expectedAlignments: []int{tablewriter.ALIGN_LEFT, tablewriter.ALIGN_CENTER},
},
{
name: "Test2",
categoryType: TypeStatus,
expectedHeaders: []string{"CONTROL NAME", "STATUS", "DOCS"},
expectedAlignments: []int{tablewriter.ALIGN_LEFT, tablewriter.ALIGN_CENTER, tablewriter.ALIGN_CENTER},
},
}
workloadPrinter := NewWorkloadPrinter()
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
headers, alignments := workloadPrinter.initCategoryTableData(tt.categoryType)
if len(headers) != len(tt.expectedHeaders) {
t.Errorf("initCategoryTableData() headers = %v, want %v", headers, tt.expectedHeaders)
}
if len(alignments) != len(tt.expectedAlignments) {
t.Errorf("initCategoryTableData() alignments = %v, want %v", alignments, tt.expectedAlignments)
}
assert.True(t, reflect.DeepEqual(headers, tt.expectedHeaders))
assert.True(t, reflect.DeepEqual(alignments, tt.expectedAlignments))
})
}
}
func TestWorkloadScan_GenerateCountingCategoryRow(t *testing.T) {
tests := []struct {
name string
controlSummary reportsummary.IControlSummary
expectedRows []string
}{
{
name: "1 failed control",
controlSummary: &reportsummary.ControlSummary{
Name: "ctrl1",
StatusCounters: reportsummary.StatusCounters{
FailedResources: 1,
},
},
expectedRows: []string{"ctrl1", "1"},
},
{
name: "multiple failed controls",
controlSummary: &reportsummary.ControlSummary{
Name: "ctrl1",
StatusCounters: reportsummary.StatusCounters{
FailedResources: 5,
},
},
expectedRows: []string{"ctrl1", "5"},
},
{
name: "no failed controls",
controlSummary: &reportsummary.ControlSummary{
Name: "ctrl1",
StatusCounters: reportsummary.StatusCounters{
FailedResources: 0,
},
},
expectedRows: []string{"ctrl1", "0"},
},
}
workloadPrinter := NewWorkloadPrinter()
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
row := workloadPrinter.generateCountingCategoryRow(tt.controlSummary)
assert.True(t, reflect.DeepEqual(row, tt.expectedRows))
})
}
}
func TestWorkloadScan_GetCategoryCountingTypeHeaders(t *testing.T) {
workloadPrinter := NewWorkloadPrinter()
headers := workloadPrinter.getCategoryCountingTypeHeaders()
assert.True(t, reflect.DeepEqual(headers, []string{"CONTROL NAME", "RESOURCES"}))
}
func TestWorkloadScan_GetCountingTypeAlignments(t *testing.T) {
workloadPrinter := NewWorkloadPrinter()
alignments := workloadPrinter.getCountingTypeAlignments()
assert.True(t, reflect.DeepEqual(alignments, []int{tablewriter.ALIGN_LEFT, tablewriter.ALIGN_CENTER}))
}

View File

@@ -0,0 +1,29 @@
package imageprinter
type ImageScanSummary struct {
MapsSeverityToSummary map[string]*SeveritySummary
CVEs []CVE
PackageScores map[string]*PackageScore // map of package name to package score
Images []string
}
type SeveritySummary struct {
NumberOfCVEs int
NumberOfFixableCVEs int
}
type CVE struct {
Severity string
ID string
Package string
Version string
FixVersions []string
FixedState string
}
type PackageScore struct {
Name string
Version string
Score int
MapSeverityToCVEsNumber map[string]int
}

View File

@@ -0,0 +1,7 @@
package imageprinter
import "io"
type TablePrinter interface {
PrintImageScanningTable(io.Writer, ImageScanSummary)
}

View File

@@ -0,0 +1,31 @@
package imageprinter
import (
"io"
)
const (
imageColumnSeverity = iota
imageColumnName = iota
imageColumnComponent = iota
imageColumnVersion = iota
imageColumnFixedIn = iota
)
type TableWriter struct {
}
func NewTableWriter() *TableWriter {
return &TableWriter{}
}
var _ TablePrinter = &TableWriter{}
func (tw *TableWriter) PrintImageScanningTable(writer io.Writer, summary ImageScanSummary) {
rows := generateRows(summary)
if len(rows) == 0 {
return
}
renderTable(writer, getImageScanningHeaders(), getImageScanningColumnsAlignments(), rows)
}

View File

@@ -0,0 +1,70 @@
package imageprinter
import (
"io"
"sort"
"strings"
v5 "github.com/anchore/grype/grype/db/v5"
"github.com/fatih/color"
"github.com/kubescape/kubescape/v2/core/pkg/resultshandling/printer/v2/prettyprinter/tableprinter/utils"
"github.com/olekukonko/tablewriter"
)
func renderTable(writer io.Writer, headers []string, columnAlignments []int, rows [][]string) {
table := tablewriter.NewWriter(writer)
table.SetHeader(headers)
table.SetHeaderLine(true)
table.SetColumnAlignment(columnAlignments)
table.AppendBulk(rows)
table.Render()
}
func generateRows(summary ImageScanSummary) [][]string {
rows := make([][]string, 0, len(summary.CVEs))
// sort CVEs by severity
sort.Slice(summary.CVEs, func(i, j int) bool {
return utils.ImageSeverityToInt(summary.CVEs[i].Severity) > utils.ImageSeverityToInt(summary.CVEs[j].Severity)
})
for _, cve := range summary.CVEs {
rows = append(rows, generateRow(cve))
}
return rows
}
func generateRow(cve CVE) []string {
row := make([]string, 5)
row[imageColumnSeverity] = color.New(utils.GetColor(utils.ImageSeverityToInt(cve.Severity)), color.Bold).Sprint(cve.Severity)
row[imageColumnName] = cve.ID
row[imageColumnComponent] = cve.Package
row[imageColumnVersion] = cve.Version
// if the CVE is fixed, show all the versions that fix it
if cve.FixedState == string(v5.FixedState) {
row[imageColumnFixedIn] = strings.Join(cve.FixVersions, ",")
// if the CVE is not fixed, show the state
} else if cve.FixedState == string(v5.WontFixState) {
row[imageColumnFixedIn] = cve.FixedState
}
return row
}
func getImageScanningHeaders() []string {
headers := make([]string, 5)
headers[imageColumnSeverity] = "SEVERITY"
headers[imageColumnName] = "NAME"
headers[imageColumnComponent] = "COMPONENT"
headers[imageColumnVersion] = "VERSION"
headers[imageColumnFixedIn] = "FIXED IN"
return headers
}
func getImageScanningColumnsAlignments() []int {
return []int{tablewriter.ALIGN_CENTER, tablewriter.ALIGN_LEFT, tablewriter.ALIGN_LEFT, tablewriter.ALIGN_LEFT, tablewriter.ALIGN_LEFT}
}

View File

@@ -0,0 +1,149 @@
package imageprinter
import (
"testing"
v5 "github.com/anchore/grype/grype/db/v5"
)
func TestGenerateRows(t *testing.T) {
test := []struct {
name string
summary ImageScanSummary
expectedRows [][]string
}{
{
name: "check CVEs are sorted by severity",
summary: ImageScanSummary{
CVEs: []CVE{
{
ID: "CVE-2020-0001",
Severity: "Low",
Package: "package1",
Version: "1.0.0",
FixedState: string(v5.NotFixedState),
},
{
ID: "CVE-2020-0002",
Severity: "High",
Package: "package2",
Version: "1.0.0",
FixedState: string(v5.NotFixedState),
},
{
ID: "CVE-2020-0003",
Severity: "Medium",
Package: "package3",
Version: "1.0.0",
FixedState: string(v5.NotFixedState),
},
},
},
expectedRows: [][]string{
{"High", "CVE-2020-0002", "package2", "1.0.0", ""},
{"Medium", "CVE-2020-0003", "package3", "1.0.0", ""},
{"Low", "CVE-2020-0001", "package1", "1.0.0", ""},
},
},
{
name: "check fixed CVEs show versions",
summary: ImageScanSummary{
CVEs: []CVE{
{
ID: "CVE-2020-0001",
Severity: "Low",
Package: "package1",
Version: "1.0.0",
FixedState: string(v5.NotFixedState),
},
{
ID: "CVE-2020-0002",
Severity: "High",
Package: "package2",
Version: "1.0.0",
FixVersions: []string{"v1", "v2"},
FixedState: string(v5.FixedState),
},
},
},
expectedRows: [][]string{
{"High", "CVE-2020-0002", "package2", "1.0.0", "v1,v2"},
{"Low", "CVE-2020-0001", "package1", "1.0.0", ""},
},
},
}
for _, tt := range test {
t.Run(tt.name, func(t *testing.T) {
actualRows := generateRows(tt.summary)
if len(actualRows) != len(tt.expectedRows) {
t.Errorf("expected %d rows, got %d", len(tt.expectedRows), len(actualRows))
}
for i := range actualRows {
for j := range actualRows[i] {
if actualRows[i][j] != tt.expectedRows[i][j] {
t.Errorf("expected %s, got %s", tt.expectedRows[i][j], actualRows[i][j])
}
}
}
})
}
}
func TestGenerateRow(t *testing.T) {
tests := []struct {
name string
cve CVE
want []string
}{
{
name: "check row with fixed version",
cve: CVE{
Severity: "High",
ID: "CVE-2020-0001",
Package: "package1",
Version: "1.0.0",
FixVersions: []string{"v1", "v2"},
FixedState: string(v5.FixedState),
},
want: []string{"High", "CVE-2020-0001", "package1", "1.0.0", "v1,v2"},
},
{
name: "check row with not fixed version",
cve: CVE{
Severity: "High",
ID: "CVE-2020-0001",
Package: "package1",
Version: "1.0.0",
FixedState: string(v5.NotFixedState),
},
want: []string{"High", "CVE-2020-0001", "package1", "1.0.0", ""},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
actualRow := generateRow(tt.cve)
for i := range actualRow {
if actualRow[i] != tt.want[i] {
t.Errorf("expected %s, got %s", tt.want[i], actualRow[i])
}
}
})
}
}
func TestGetImageScanningHeaders(t *testing.T) {
headers := getImageScanningHeaders()
expectedHeaders := []string{"SEVERITY", "NAME", "COMPONENT", "VERSION", "FIXED IN"}
for i := range headers {
if headers[i] != expectedHeaders[i] {
t.Errorf("expected %s, got %s", expectedHeaders[i], headers[i])
}
}
}

View File

@@ -0,0 +1,141 @@
package utils
import (
"fmt"
"io"
"strings"
"github.com/fatih/color"
"github.com/kubescape/kubescape/v2/core/cautils"
"github.com/kubescape/opa-utils/reporthandling/apis"
"github.com/kubescape/opa-utils/reporthandling/results/v1/reportsummary"
)
type InfoStars struct {
Stars string
Info string
}
func MapInfoToPrintInfoFromIface(ctrls []reportsummary.IControlSummary) []InfoStars {
infoToPrintInfo := []InfoStars{}
infoToPrintInfoMap := map[string]interface{}{}
starCount := "*"
for _, ctrl := range ctrls {
if ctrl.GetStatus().IsSkipped() && ctrl.GetStatus().Info() != "" {
if _, ok := infoToPrintInfoMap[ctrl.GetStatus().Info()]; !ok {
infoToPrintInfo = append(infoToPrintInfo, InfoStars{
Info: ctrl.GetStatus().Info(),
Stars: starCount,
})
starCount += "*"
infoToPrintInfoMap[ctrl.GetStatus().Info()] = nil
}
}
}
return infoToPrintInfo
}
func MapInfoToPrintInfo(controls reportsummary.ControlSummaries) []InfoStars {
infoToPrintInfo := []InfoStars{}
infoToPrintInfoMap := map[string]interface{}{}
starCount := "*"
for _, control := range controls {
if control.GetStatus().IsSkipped() && control.GetStatus().Info() != "" {
if _, ok := infoToPrintInfoMap[control.GetStatus().Info()]; !ok {
infoToPrintInfo = append(infoToPrintInfo, InfoStars{
Info: control.GetStatus().Info(),
Stars: starCount,
})
starCount += "*"
infoToPrintInfoMap[control.GetStatus().Info()] = nil
}
}
}
return infoToPrintInfo
}
func GetColor(severity int) color.Attribute {
switch severity {
case apis.SeverityCritical:
return color.FgRed
case apis.SeverityHigh:
return color.FgYellow
case apis.SeverityMedium:
return color.FgCyan
case apis.SeverityLow:
return color.FgWhite
default:
return color.FgWhite
}
}
func ImageSeverityToInt(severity string) int {
severity = strings.ToLower(severity)
switch severity {
case "critical":
return 5
case "high":
return 4
case "medium":
return 3
case "low":
return 2
case "negligible":
return 1
default:
return 0
}
}
func FrameworksScoresToString(frameworks []reportsummary.IFrameworkSummary) string {
if len(frameworks) == 1 {
if frameworks[0].GetName() != "" {
return fmt.Sprintf("FRAMEWORK %s\n", frameworks[0].GetName())
// cautils.InfoTextDisplay(prettyPrinter.writer, ))
}
} else if len(frameworks) > 1 {
p := "FRAMEWORKS: "
i := 0
for ; i < len(frameworks)-1; i++ {
p += fmt.Sprintf("%s (compliance: %.2f), ", frameworks[i].GetName(), frameworks[i].GetComplianceScore())
}
p += fmt.Sprintf("%s (compliance: %.2f)\n", frameworks[i].GetName(), frameworks[i].GetComplianceScore())
return p
}
return ""
}
func PrintInfo(writer io.Writer, infoToPrintInfo []InfoStars) {
fmt.Println()
for i := range infoToPrintInfo {
cautils.InfoDisplay(writer, fmt.Sprintf("%s %s\n", infoToPrintInfo[i].Stars, infoToPrintInfo[i].Info))
}
}
func GetStatusColor(status apis.ScanningStatus) color.Attribute {
switch status {
case apis.StatusPassed:
return color.FgGreen
case apis.StatusFailed:
return color.FgRed
case apis.StatusSkipped:
return color.FgCyan
default:
return color.FgWhite
}
}
func getColor(controlSeverity int) color.Attribute {
switch controlSeverity {
case apis.SeverityCritical:
return color.FgRed
case apis.SeverityHigh:
return color.FgYellow
case apis.SeverityMedium:
return color.FgCyan
case apis.SeverityLow:
return color.FgWhite
default:
return color.FgWhite
}
}

View File

@@ -0,0 +1,11 @@
package utils
import "testing"
func TestGetColor(t *testing.T) {
}
func TestImageSeverityToInt(t *testing.T) {
}

View File

@@ -0,0 +1,219 @@
package prettyprinter
import (
"fmt"
"os"
"sort"
"strings"
"github.com/fatih/color"
"github.com/kubescape/kubescape/v2/core/cautils"
"github.com/kubescape/kubescape/v2/core/pkg/resultshandling/printer/v2/prettyprinter/tableprinter/imageprinter"
"github.com/kubescape/kubescape/v2/core/pkg/resultshandling/printer/v2/prettyprinter/tableprinter/utils"
helpersv1 "github.com/kubescape/opa-utils/reporthandling/helpers/v1"
"github.com/kubescape/opa-utils/reporthandling/results/v1/reportsummary"
"k8s.io/utils/strings/slices"
)
const (
linkToHelm = "https://github.com/kubescape/helm-charts"
linkToCICDSetup = "https://hub.armosec.io/docs/integrations"
configScanVerboseRunText = "Run with '--verbose'/'-v' flag for detailed resources view"
imageScanVerboseRunText = "Run with '--verbose'/'-v' flag for detailed vulnerabilities view"
)
var (
clusterScanRunText = fmt.Sprintf("Run a cluster scan: %s", getCallToActionString("'$ kubescape scan'"))
installHelmText = fmt.Sprintf("Install helm for continuos monitoring: %s", linkToHelm)
CICDSetupText = fmt.Sprintf("Add Kubescape to CICD: %s", linkToCICDSetup)
complianceFrameworks = []string{"nsa", "mitre"}
cveSeverities = []string{"Critical", "High", "Medium", "Low", "Negligible", "Unknown"}
)
func filterComplianceFrameworks(frameworks []reportsummary.IFrameworkSummary) []reportsummary.IFrameworkSummary {
complianceFws := []reportsummary.IFrameworkSummary{}
for _, fw := range frameworks {
if slices.Contains(complianceFrameworks, strings.ToLower(fw.GetName())) {
complianceFws = append(complianceFws, fw)
}
}
return complianceFws
}
func ControlCountersForResource(l *helpersv1.AllLists) string {
return fmt.Sprintf("Controls: %d (Failed: %d, action required: %d)", l.Len(), l.Failed(), l.Skipped())
}
func getWorkloadPrefixForCmd(namespace, kind, name string) string {
if namespace == "" {
return fmt.Sprintf("name: %s, kind: %s", name, kind)
}
return fmt.Sprintf("namespace: %s, name: %s, kind: %s", namespace, name, kind)
}
func getTopWorkloadsTitle(topWLsLen int) string {
if topWLsLen > 1 {
return "Your most risky workloads:\n"
}
if topWLsLen > 0 {
return "Your most risky workload:\n"
}
return ""
}
// getSeverityToSummaryMap returns a map of severity to summary, if shouldMerge is true, it will merge Low, Negligible and Unknown to Other
func getSeverityToSummaryMap(summary imageprinter.ImageScanSummary, verboseMode bool) map[string]*imageprinter.SeveritySummary {
tempMap := map[string]*imageprinter.SeveritySummary{}
for severity, severitySummary := range summary.MapsSeverityToSummary {
if !verboseMode {
if severity == "Low" || severity == "Negligible" || severity == "Unknown" {
severity = "Other"
}
}
if _, ok := tempMap[severity]; !ok {
tempMap[severity] = &imageprinter.SeveritySummary{}
}
tempMap[severity].NumberOfCVEs += severitySummary.NumberOfCVEs
tempMap[severity].NumberOfFixableCVEs += severitySummary.NumberOfFixableCVEs
}
addEmptySeverities(tempMap, verboseMode)
return tempMap
}
func addEmptySeverities(mapSeverityTSummary map[string]*imageprinter.SeveritySummary, verboseMode bool) {
if verboseMode {
for _, severity := range cveSeverities {
if _, ok := mapSeverityTSummary[severity]; !ok {
mapSeverityTSummary[severity] = &imageprinter.SeveritySummary{}
}
}
} else {
for _, severity := range []string{"Critical", "High", "Other"} {
if _, ok := mapSeverityTSummary[severity]; !ok {
mapSeverityTSummary[severity] = &imageprinter.SeveritySummary{}
}
}
}
}
// filterCVEsBySeverities returns a list of CVEs only with the severities that are in the severities list
func filterCVEsBySeverities(cves []imageprinter.CVE, severities []string) []imageprinter.CVE {
var filteredCVEs []imageprinter.CVE
for _, cve := range cves {
for _, severity := range severities {
if cve.Severity == severity {
filteredCVEs = append(filteredCVEs, cve)
}
}
}
return filteredCVEs
}
func printTopVulnerabilities(writer *os.File, summary imageprinter.ImageScanSummary) {
if len(summary.PackageScores) == 0 {
return
}
cautils.InfoTextDisplay(writer, "\nMost vulnerable components:\n")
var ss []string
for k := range summary.PackageScores {
ss = append(ss, k)
}
sort.Slice(ss, func(i, j int) bool {
if summary.PackageScores[ss[i]].Score == summary.PackageScores[ss[j]].Score {
return summary.PackageScores[ss[i]].Name < summary.PackageScores[ss[j]].Name
}
return summary.PackageScores[ss[i]].Score > summary.PackageScores[ss[j]].Score
})
for i := 0; i < len(ss) && i < TopPackagesNumber; i++ {
topPkg := summary.PackageScores[ss[i]]
output := fmt.Sprintf(" * %s (%s) -", topPkg.Name, topPkg.Version)
for severity, numberOfCVEs := range topPkg.MapSeverityToCVEsNumber {
output += fmt.Sprintf(" %d %s,", numberOfCVEs, severity)
}
output = output[:len(output)-1]
cautils.SimpleDisplay(writer, output+"\n")
}
cautils.SimpleDisplay(writer, "\n")
return
}
func printImageScanningSummary(writer *os.File, summary imageprinter.ImageScanSummary, verboseMode bool) {
mapSeverityTSummary := getSeverityToSummaryMap(summary, verboseMode)
// sort keys by severity
keys := make([]string, 0, len(mapSeverityTSummary))
for k := range mapSeverityTSummary {
keys = append(keys, k)
}
sort.Slice(keys, func(i, j int) bool {
return utils.ImageSeverityToInt(keys[i]) > utils.ImageSeverityToInt(keys[j])
})
if len(summary.CVEs) == 0 {
cautils.InfoTextDisplay(writer, "Vulnerability summary - no vulnerabilities were found!\n\n")
return
}
cautils.InfoTextDisplay(writer, "Vulnerability summary - %d vulnerabilities found:\n", len(summary.CVEs))
if len(summary.Images) == 1 {
cautils.SimpleDisplay(writer, "Image: %s\n", summary.Images[0])
} else {
cautils.SimpleDisplay(writer, "Images: %s\n", strings.Join(summary.Images, ", "))
}
for _, k := range keys {
if k == "Other" {
cautils.SimpleDisplay(writer, " * %d %s \n", mapSeverityTSummary[k].NumberOfCVEs, k)
} else {
cautils.SimpleDisplay(writer, " * %d %s\n", mapSeverityTSummary[k].NumberOfCVEs, k)
}
}
}
func printImagesCommands(writer *os.File, summary imageprinter.ImageScanSummary) {
for _, img := range summary.Images {
imgWithoutTag := strings.Split(img, ":")[0]
cautils.SimpleDisplay(writer, fmt.Sprintf("Receive full report for %s image by running: %s\n", imgWithoutTag, getCallToActionString(fmt.Sprintf("'$ kubescape scan image %s'", img))))
}
cautils.InfoTextDisplay(writer, "\n")
}
func printNextSteps(writer *os.File, nextSteps []string, addLine bool) {
cautils.InfoTextDisplay(writer, "Follow-up steps:\n")
for _, ns := range nextSteps {
cautils.SimpleDisplay(writer, "- "+ns+"\n")
}
if addLine {
cautils.SimpleDisplay(writer, "\n")
}
}
func printComplianceScore(writer *os.File, frameworks []reportsummary.IFrameworkSummary) {
cautils.InfoTextDisplay(writer, "Compliance Score:\n")
for _, fw := range frameworks {
cautils.SimpleDisplay(writer, "* %s: %.2f%%\n", fw.GetName(), fw.GetComplianceScore())
}
cautils.SimpleDisplay(writer, fmt.Sprintf("View full compliance report by running: %s\n", getCallToActionString("'$ kubescape scan framework nsa,mitre'")))
cautils.InfoTextDisplay(writer, "\n")
}
func getCallToActionString(action string) string {
return color.New(color.Bold, color.FgHiBlue).SprintFunc()(action)
}

View File

@@ -0,0 +1,368 @@
package prettyprinter
import (
"reflect"
"testing"
"github.com/kubescape/kubescape/v2/core/pkg/resultshandling/printer/v2/prettyprinter/tableprinter/imageprinter"
"github.com/kubescape/opa-utils/reporthandling/results/v1/reportsummary"
"github.com/stretchr/testify/assert"
)
func Test_filterComplianceFrameworks(t *testing.T) {
tests := []struct {
name string
summaryDetails *reportsummary.SummaryDetails
expectedSummaryDetails *reportsummary.SummaryDetails
}{
{
name: "check compliance frameworks are filtered",
summaryDetails: &reportsummary.SummaryDetails{
Frameworks: []reportsummary.FrameworkSummary{
{
Name: "CIS Kubernetes Benchmark",
},
{
Name: "nsa",
},
{
Name: "mitre",
},
},
},
expectedSummaryDetails: &reportsummary.SummaryDetails{
Frameworks: []reportsummary.FrameworkSummary{
{
Name: "nsa",
},
{
Name: "mitre",
},
},
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
complianceFws := filterComplianceFrameworks(tt.summaryDetails.ListFrameworks())
assert.True(t, reflect.DeepEqual(complianceFws, tt.expectedSummaryDetails.ListFrameworks()))
})
}
}
func Test_getWorkloadPrefixForCmd(t *testing.T) {
tests := []struct {
name string
namespace string
kind string
name1 string
want string
}{
{
name: "non-empty namespace",
namespace: "default",
kind: "pod",
name1: "test",
want: "namespace: default, name: test, kind: pod",
},
{
name: "empty namespace",
namespace: "",
kind: "pod",
name1: "test",
want: "name: test, kind: pod",
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if got := getWorkloadPrefixForCmd(tt.namespace, tt.kind, tt.name1); got != tt.want {
t.Errorf("getWorkloadPrefixForCmd() = %v, want %v", got, tt.want)
}
})
}
}
func Test_getTopWorkloadsTitle(t *testing.T) {
title := getTopWorkloadsTitle(0)
assert.Equal(t, "", title)
title = getTopWorkloadsTitle(1)
assert.Equal(t, "Your most risky workload:\n", title)
title = getTopWorkloadsTitle(2)
assert.Equal(t, "Your most risky workloads:\n", title)
title = getTopWorkloadsTitle(10)
assert.Equal(t, "Your most risky workloads:\n", title)
}
func Test_getSeverityToSummaryMap(t *testing.T) {
tests := []struct {
name string
summaryDetails imageprinter.ImageScanSummary
expected map[string]imageprinter.SeveritySummary
shouldMerge bool
}{
{
name: "without merging",
summaryDetails: imageprinter.ImageScanSummary{
MapsSeverityToSummary: map[string]*imageprinter.SeveritySummary{
"High": {
NumberOfCVEs: 10,
NumberOfFixableCVEs: 2,
},
"Low": {
NumberOfCVEs: 5,
NumberOfFixableCVEs: 1,
},
"Negligible": {
NumberOfCVEs: 3,
NumberOfFixableCVEs: 0,
},
},
},
shouldMerge: false,
expected: map[string]imageprinter.SeveritySummary{
"High": {
NumberOfCVEs: 10,
NumberOfFixableCVEs: 2,
},
"Low": {
NumberOfCVEs: 5,
NumberOfFixableCVEs: 1,
},
"Negligible": {
NumberOfCVEs: 3,
NumberOfFixableCVEs: 0,
},
},
},
{
name: "with merging",
summaryDetails: imageprinter.ImageScanSummary{
MapsSeverityToSummary: map[string]*imageprinter.SeveritySummary{
"Critical": {
NumberOfCVEs: 15,
NumberOfFixableCVEs: 2,
},
"High": {
NumberOfCVEs: 10,
NumberOfFixableCVEs: 2,
},
"Medium": {
NumberOfCVEs: 5,
NumberOfFixableCVEs: 1,
},
"Low": {
NumberOfCVEs: 5,
NumberOfFixableCVEs: 1,
},
"Negligible": {
NumberOfCVEs: 3,
NumberOfFixableCVEs: 0,
},
},
},
shouldMerge: true,
expected: map[string]imageprinter.SeveritySummary{
"Critical": {
NumberOfCVEs: 15,
NumberOfFixableCVEs: 2,
},
"High": {
NumberOfCVEs: 10,
NumberOfFixableCVEs: 2,
},
"Medium": {
NumberOfCVEs: 5,
NumberOfFixableCVEs: 1,
},
"Other": {
NumberOfCVEs: 8,
NumberOfFixableCVEs: 1,
},
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
sevToSummaryMap := getSeverityToSummaryMap(tt.summaryDetails, tt.shouldMerge)
for k, v := range sevToSummaryMap {
if v.NumberOfCVEs != tt.expected[k].NumberOfCVEs || v.NumberOfFixableCVEs != tt.expected[k].NumberOfFixableCVEs {
t.Errorf("in test: %v, error for key %v, want: %v, have :%v", tt.name, k, tt.expected[k], v)
}
}
})
}
}
func Test_filterCVEsBySeverities(t *testing.T) {
test := []struct {
name string
cves []imageprinter.CVE
severities []string
expectedCVEs []imageprinter.CVE
}{
{
name: "empty severities list",
cves: []imageprinter.CVE{
{
Severity: "High",
ID: "CVE-2020-1234",
},
},
severities: []string{},
expectedCVEs: []imageprinter.CVE{},
},
{
name: "one severity",
cves: []imageprinter.CVE{
{
Severity: "High",
ID: "CVE-2020-1234",
},
{
Severity: "Medium",
ID: "CVE-2020-1235",
},
},
severities: []string{"High"},
expectedCVEs: []imageprinter.CVE{
{
Severity: "High",
ID: "CVE-2020-1234",
},
},
},
{
name: "multiple severities",
cves: []imageprinter.CVE{
{
Severity: "High",
ID: "CVE-2020-1234",
},
{
Severity: "Medium",
ID: "CVE-2020-1235",
},
{
Severity: "Low",
ID: "CVE-2020-1236",
},
},
severities: []string{"High", "Low"},
expectedCVEs: []imageprinter.CVE{
{
Severity: "High",
ID: "CVE-2020-1234",
},
{
Severity: "Low",
ID: "CVE-2020-1236",
},
},
},
}
for _, tt := range test {
t.Run(tt.name, func(t *testing.T) {
filteredCVEs := filterCVEsBySeverities(tt.cves, tt.severities)
for i := range filteredCVEs {
if filteredCVEs[i].Severity != tt.expectedCVEs[i].Severity || filteredCVEs[i].ID != tt.expectedCVEs[i].ID {
t.Errorf("filterCVEsBySeverities() = %v, want %v", filteredCVEs, tt.expectedCVEs)
}
}
})
}
}
func Test_sortTopVulnerablePackages(t *testing.T) {
tests := []struct {
name string
pkgScores map[string]*imageprinter.PackageScore
expectedPkgScores map[string]*imageprinter.PackageScore
}{
{
name: "change order",
pkgScores: map[string]*imageprinter.PackageScore{
"pkg1": {
Version: "1.0.0",
Score: 10,
},
"pkg2": {
Version: "2.0.0",
Score: 20,
},
"pkg3": {
Version: "3.0.0",
Score: 15,
},
},
expectedPkgScores: map[string]*imageprinter.PackageScore{
"pkg2": {
Version: "2.0.0",
Score: 20,
},
"pkg3": {
Version: "3.0.0",
Score: 15,
},
"pkg1": {
Version: "1.0.0",
Score: 10,
},
},
},
{
name: "keep order",
pkgScores: map[string]*imageprinter.PackageScore{
"pkg1": {
Version: "1.0.0",
Score: 30,
},
"pkg2": {
Version: "2.0.0",
Score: 20,
},
"pkg3": {
Version: "3.0.0",
Score: 10,
},
},
expectedPkgScores: map[string]*imageprinter.PackageScore{
"pkg1": {
Version: "1.0.0",
Score: 30,
},
"pkg2": {
Version: "2.0.0",
Score: 20,
},
"pkg3": {
Version: "3.0.0",
Score: 10,
},
},
},
{
name: "empty",
pkgScores: map[string]*imageprinter.PackageScore{},
expectedPkgScores: map[string]*imageprinter.PackageScore{},
},
}
for _, tt := range tests {
actual := sortTopVulnerablePackages(tt.pkgScores)
for k, v := range actual {
if v.Version != tt.expectedPkgScores[k].Version || v.Score != tt.expectedPkgScores[k].Score {
t.Errorf("in test: %v, error for key %v, want: %v, have :%v", tt.name, k, tt.expectedPkgScores[k], v)
}
}
}
}

View File

@@ -0,0 +1,45 @@
package prettyprinter
import (
"os"
"github.com/kubescape/kubescape/v2/core/pkg/resultshandling/printer/v2/prettyprinter/tableprinter/configurationprinter"
"github.com/kubescape/kubescape/v2/core/pkg/resultshandling/printer/v2/prettyprinter/tableprinter/imageprinter"
"github.com/kubescape/opa-utils/reporthandling/results/v1/reportsummary"
)
type WorkloadPrinter struct {
writer *os.File
categoriesTablePrinter configurationprinter.TablePrinter
}
func NewWorkloadPrinter(writer *os.File) *WorkloadPrinter {
return &WorkloadPrinter{
writer: writer,
categoriesTablePrinter: configurationprinter.NewWorkloadPrinter(),
}
}
var _ MainPrinter = &WorkloadPrinter{}
func (wp *WorkloadPrinter) PrintImageScanning(summary *imageprinter.ImageScanSummary) {
printImageScanningSummary(wp.writer, *summary, false)
printImagesCommands(wp.writer, *summary)
}
func (wp *WorkloadPrinter) PrintNextSteps() {
printNextSteps(wp.writer, wp.getNextSteps(), true)
}
func (wp *WorkloadPrinter) getNextSteps() []string {
return []string{
configScanVerboseRunText,
installHelmText,
CICDSetupText,
}
}
func (wp *WorkloadPrinter) PrintConfigurationsScanning(summaryDetails *reportsummary.SummaryDetails, sortedControlIDs [][]string) {
wp.categoriesTablePrinter.PrintCategoriesTables(wp.writer, summaryDetails, sortedControlIDs)
}

View File

@@ -0,0 +1 @@
package printer

View File

@@ -5,6 +5,7 @@ import (
"fmt"
"os"
"github.com/anchore/grype/grype/presenter/models"
logger "github.com/kubescape/go-logger"
"github.com/kubescape/go-logger/helpers"
"github.com/kubescape/k8s-interface/workloadinterface"
@@ -27,6 +28,10 @@ func NewPrometheusPrinter(verboseMode bool) *PrometheusPrinter {
}
}
func (pp *PrometheusPrinter) PrintNextSteps() {
}
func (pp *PrometheusPrinter) SetWriter(ctx context.Context, outputFile string) {
pp.writer = printer.GetWriter(ctx, outputFile)
}
@@ -47,7 +52,10 @@ func (pp *PrometheusPrinter) generatePrometheusFormat(
return m
}
func (pp *PrometheusPrinter) ActionPrint(ctx context.Context, opaSessionObj *cautils.OPASessionObj) {
func (pp *PrometheusPrinter) PrintImageScan(context.Context, *models.PresenterConfig) {
}
func (pp *PrometheusPrinter) ActionPrint(ctx context.Context, opaSessionObj *cautils.OPASessionObj, imageScanData []cautils.ImageScanData) {
metrics := pp.generatePrometheusFormat(opaSessionObj.AllResources, opaSessionObj.ResourcesResult, &opaSessionObj.Report.SummaryDetails)

View File

@@ -6,6 +6,7 @@ import (
"strings"
"github.com/kubescape/kubescape/v2/core/cautils"
"github.com/kubescape/kubescape/v2/core/pkg/resultshandling/printer/v2/prettyprinter"
"github.com/kubescape/opa-utils/reporthandling/results/v1/reportsummary"
"github.com/kubescape/opa-utils/reporthandling/results/v1/resourcesresults"
"github.com/olekukonko/tablewriter"
@@ -40,7 +41,7 @@ func (prettyPrinter *PrettyPrinter) resourceTable(opaSessionObj *cautils.OPASess
if resource.GetNamespace() != "" {
fmt.Fprintf(prettyPrinter.writer, "Namespace: %s\n", resource.GetNamespace())
}
fmt.Fprintf(prettyPrinter.writer, "\n"+controlCountersForResource(result.ListControlsIDs(nil))+"\n\n")
fmt.Fprintf(prettyPrinter.writer, "\n"+prettyprinter.ControlCountersForResource(result.ListControlsIDs(nil))+"\n\n")
summaryTable := tablewriter.NewWriter(prettyPrinter.writer)
summaryTable.SetAutoWrapText(true)

View File

@@ -10,6 +10,7 @@ import (
"strconv"
"strings"
"github.com/anchore/grype/grype/presenter/models"
logger "github.com/kubescape/go-logger"
"github.com/kubescape/go-logger/helpers"
"github.com/kubescape/kubescape/v2/core/cautils"
@@ -109,7 +110,14 @@ func (sp *SARIFPrinter) addResult(scanRun *sarif.Run, ctl reportsummary.IControl
})
}
func (sp *SARIFPrinter) ActionPrint(ctx context.Context, opaSessionObj *cautils.OPASessionObj) {
func (sp *SARIFPrinter) PrintImageScan(context.Context, *models.PresenterConfig) {
}
func (sp *SARIFPrinter) PrintNextSteps() {
}
func (sp *SARIFPrinter) ActionPrint(ctx context.Context, opaSessionObj *cautils.OPASessionObj, imageScanData []cautils.ImageScanData) {
report, err := sarif.New(sarif.Version210)
if err != nil {
panic(err)

View File

@@ -3,6 +3,7 @@ package printer
import (
"context"
"github.com/anchore/grype/grype/presenter/models"
"github.com/kubescape/kubescape/v2/core/cautils"
"github.com/kubescape/kubescape/v2/core/pkg/resultshandling/printer"
)
@@ -13,7 +14,14 @@ var _ printer.IPrinter = &SilentPrinter{}
type SilentPrinter struct {
}
func (silentPrinter *SilentPrinter) ActionPrint(ctx context.Context, opaSessionObj *cautils.OPASessionObj) {
func (silentPrinter *SilentPrinter) PrintNextSteps() {
}
func (silentPrinter *SilentPrinter) PrintImageScan(context.Context, *models.PresenterConfig) {
}
func (silentPrinter *SilentPrinter) ActionPrint(ctx context.Context, opaSessionObj *cautils.OPASessionObj, imageScanData []cautils.ImageScanData) {
}
func (silentPrinter *SilentPrinter) SetWriter(ctx context.Context, outputFile string) {

View File

@@ -1,8 +1,12 @@
package printer
import (
v5 "github.com/anchore/grype/grype/db/v5"
"github.com/anchore/grype/grype/presenter/models"
"github.com/kubescape/k8s-interface/workloadinterface"
"github.com/kubescape/kubescape/v2/core/cautils"
"github.com/kubescape/kubescape/v2/core/pkg/resultshandling/printer/v2/prettyprinter/tableprinter/imageprinter"
"github.com/kubescape/kubescape/v2/core/pkg/resultshandling/printer/v2/prettyprinter/tableprinter/utils"
"github.com/kubescape/opa-utils/reporthandling"
"github.com/kubescape/opa-utils/reporthandling/results/v1/prioritization"
"github.com/kubescape/opa-utils/reporthandling/results/v1/reportsummary"
@@ -82,3 +86,52 @@ func finalizeResources(results []resourcesresults.Result, allResources map[strin
}
return resources
}
// returns map of severity to summary
func extractSeverityToSummaryMap(cves []imageprinter.CVE, mapSeverityToSummary map[string]*imageprinter.SeveritySummary) {
for _, cve := range cves {
if _, ok := mapSeverityToSummary[cve.Severity]; !ok {
mapSeverityToSummary[cve.Severity] = &imageprinter.SeveritySummary{}
}
mapSeverityToSummary[cve.Severity].NumberOfCVEs += 1
if cve.FixedState == string(v5.FixedState) {
mapSeverityToSummary[cve.Severity].NumberOfFixableCVEs = mapSeverityToSummary[cve.Severity].NumberOfFixableCVEs + 1
}
}
}
// returns a map of package name + version to score (we can have multiple matches for the same package with different versions)
func extractPkgNameToScoreMap(matches []models.Match, pkgScores map[string]*imageprinter.PackageScore) {
for i := range matches {
key := matches[i].Artifact.Name + matches[i].Artifact.Version
if _, ok := pkgScores[key]; !ok {
pkgScores[key] = &imageprinter.PackageScore{
Version: matches[i].Artifact.Version,
Name: matches[i].Artifact.Name,
MapSeverityToCVEsNumber: make(map[string]int, 0),
}
}
if _, ok := pkgScores[key].MapSeverityToCVEsNumber[matches[i].Vulnerability.Severity]; !ok {
pkgScores[key].MapSeverityToCVEsNumber[matches[i].Vulnerability.Severity] = 1
} else {
pkgScores[key].MapSeverityToCVEsNumber[matches[i].Vulnerability.Severity] = pkgScores[key].MapSeverityToCVEsNumber[matches[i].Vulnerability.Severity] + 1
}
pkgScores[key].Score = pkgScores[key].Score + utils.ImageSeverityToInt(matches[i].Vulnerability.Severity)
}
}
func extractCVEs(matches []models.Match) []imageprinter.CVE {
CVEs := []imageprinter.CVE{}
for i := range matches {
cve := imageprinter.CVE{
ID: matches[i].Vulnerability.ID,
Severity: matches[i].Vulnerability.Severity,
Package: matches[i].Artifact.Name,
Version: matches[i].Artifact.Version,
FixVersions: matches[i].Vulnerability.Fix.Versions,
FixedState: matches[i].Vulnerability.Fix.State,
}
CVEs = append(CVEs, cve)
}
return CVEs
}

View File

@@ -0,0 +1,480 @@
package printer
import (
"testing"
"github.com/anchore/grype/grype/presenter/models"
"github.com/kubescape/kubescape/v2/core/pkg/resultshandling/printer/v2/prettyprinter/tableprinter/imageprinter"
"github.com/stretchr/testify/assert"
)
func TestExtractCVEs(t *testing.T) {
tests := []struct {
name string
matches []models.Match
want []imageprinter.CVE
}{
{
name: "single vuln",
matches: []models.Match{
{
Artifact: models.Package{
Name: "foo",
Version: "1.2.3",
},
Vulnerability: models.Vulnerability{
VulnerabilityMetadata: models.VulnerabilityMetadata{
ID: "CVE-2020-1234",
Severity: "High",
},
Fix: models.Fix{
Versions: []string{"1.2.3"},
State: "Fixed",
},
},
},
},
want: []imageprinter.CVE{
{
ID: "CVE-2020-1234",
Severity: "High",
Package: "foo",
Version: "1.2.3",
FixVersions: []string{"1.2.3"},
FixedState: "Fixed",
},
},
},
{
name: "multiple vulns",
matches: []models.Match{
{
Artifact: models.Package{
Name: "foo",
Version: "1.2.3",
},
Vulnerability: models.Vulnerability{
VulnerabilityMetadata: models.VulnerabilityMetadata{
ID: "CVE-2020-1234",
Severity: "High",
},
Fix: models.Fix{
Versions: []string{"1.2.3"},
State: "Fixed",
},
},
},
{
Artifact: models.Package{
Name: "test",
Version: "1",
},
Vulnerability: models.Vulnerability{
VulnerabilityMetadata: models.VulnerabilityMetadata{
ID: "CVE-2020-1235",
Severity: "Critical",
},
Fix: models.Fix{
Versions: []string{"1"},
State: "Fixed",
},
},
},
{
Artifact: models.Package{
Name: "test2",
Version: "3",
},
Vulnerability: models.Vulnerability{
VulnerabilityMetadata: models.VulnerabilityMetadata{
ID: "CVE-2020-1236",
Severity: "Low",
},
Fix: models.Fix{
Versions: []string{"2", "3", "4"},
State: "Not fixed",
},
},
},
},
want: []imageprinter.CVE{
{
ID: "CVE-2020-1234",
Severity: "High",
Package: "foo",
Version: "1.2.3",
FixVersions: []string{"1.2.3"},
FixedState: "Fixed",
},
{
ID: "CVE-2020-1235",
Severity: "Critical",
Package: "test",
Version: "1",
FixVersions: []string{"1"},
FixedState: "Fixed",
},
{
ID: "CVE-2020-1236",
Severity: "Low",
Package: "test2",
Version: "3",
FixVersions: []string{"2", "3", "4"},
FixedState: "Not fixed",
},
},
},
{
name: "empty vulns",
matches: []models.Match{},
want: []imageprinter.CVE{},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
actual := extractCVEs(tt.matches)
if len(actual) != len(tt.want) {
t.Errorf("extractCVEs() = %v, want %v", actual, tt.want)
}
for i := range actual {
if actual[i].ID != tt.want[i].ID {
t.Errorf("extractCVEs() = %v, want %v", actual, tt.want)
}
if actual[i].Severity != tt.want[i].Severity {
t.Errorf("extractCVEs() = %v, want %v", actual, tt.want)
}
if actual[i].Package != tt.want[i].Package {
t.Errorf("extractCVEs() = %v, want %v", actual, tt.want)
}
if actual[i].Version != tt.want[i].Version {
t.Errorf("extractCVEs() = %v, want %v", actual, tt.want)
}
if actual[i].FixedState != tt.want[i].FixedState {
t.Errorf("extractCVEs() = %v, want %v", actual, tt.want)
}
if len(actual[i].FixVersions) != len(tt.want[i].FixVersions) {
t.Errorf("extractCVEs() = %v, want %v", actual, tt.want)
}
for j := range actual[i].FixVersions {
if actual[i].FixVersions[j] != tt.want[i].FixVersions[j] {
t.Errorf("extractCVEs() = %v, want %v", actual, tt.want)
}
}
}
})
}
}
func TestExtractPkgNameToScoreMap(t *testing.T) {
tests := []struct {
name string
matches []models.Match
want map[string]*imageprinter.PackageScore
}{
{
name: "single package",
matches: []models.Match{
{
Artifact: models.Package{
Name: "foo",
Version: "1.2.3",
},
Vulnerability: models.Vulnerability{
VulnerabilityMetadata: models.VulnerabilityMetadata{
Severity: "High",
},
},
},
},
want: map[string]*imageprinter.PackageScore{
"foo1.2.3": {
Name: "foo",
Score: 4,
Version: "1.2.3",
},
},
},
{
name: "multiple packages - different versions",
matches: []models.Match{
{
Artifact: models.Package{
Name: "pkg1",
Version: "version1",
},
Vulnerability: models.Vulnerability{
VulnerabilityMetadata: models.VulnerabilityMetadata{
Severity: "Critical",
},
},
},
{
Artifact: models.Package{
Name: "pkg2",
Version: "1.2",
},
Vulnerability: models.Vulnerability{
VulnerabilityMetadata: models.VulnerabilityMetadata{
Severity: "Low",
},
},
},
{
Artifact: models.Package{
Name: "pkg3",
Version: "1.2.3",
},
Vulnerability: models.Vulnerability{
VulnerabilityMetadata: models.VulnerabilityMetadata{
Severity: "High",
},
},
},
},
want: map[string]*imageprinter.PackageScore{
"pkg1version1": {
Name: "pkg1",
Score: 5,
Version: "version1",
},
"pkg21.2": {
Name: "pkg2",
Score: 2,
Version: "1.2",
},
"pkg31.2.3": {
Name: "pkg3",
Score: 4,
Version: "1.2.3",
},
},
},
{
name: "multiple packages - mixed versions",
matches: []models.Match{
{
Artifact: models.Package{
Name: "pkg1",
Version: "version1",
},
Vulnerability: models.Vulnerability{
VulnerabilityMetadata: models.VulnerabilityMetadata{
Severity: "High",
},
},
},
{
Artifact: models.Package{
Name: "pkg1",
Version: "version1",
},
Vulnerability: models.Vulnerability{
VulnerabilityMetadata: models.VulnerabilityMetadata{
Severity: "High",
},
},
},
{
Artifact: models.Package{
Name: "pkg1",
Version: "version2",
},
Vulnerability: models.Vulnerability{
VulnerabilityMetadata: models.VulnerabilityMetadata{
Severity: "Critical",
},
},
},
{
Artifact: models.Package{
Name: "pkg3",
Version: "1.2",
},
Vulnerability: models.Vulnerability{
VulnerabilityMetadata: models.VulnerabilityMetadata{
Severity: "Medium",
},
},
},
{
Artifact: models.Package{
Name: "pkg3",
Version: "1.2",
},
Vulnerability: models.Vulnerability{
VulnerabilityMetadata: models.VulnerabilityMetadata{
Severity: "Low",
},
},
},
{
Artifact: models.Package{
Name: "pkg4",
Version: "1.2.3",
},
Vulnerability: models.Vulnerability{
VulnerabilityMetadata: models.VulnerabilityMetadata{
Severity: "High",
},
},
},
},
want: map[string]*imageprinter.PackageScore{
"pkg1version1": {
Name: "pkg1",
Score: 8,
Version: "version1",
},
"pkg1version2": {
Name: "pkg1",
Score: 5,
Version: "version2",
},
"pkg31.2": {
Name: "pkg3",
Score: 5,
Version: "1.2",
},
"pkg41.2.3": {
Name: "pkg4",
Score: 4,
Version: "1.2.3",
},
},
},
{
name: "empty packages",
matches: []models.Match{},
want: map[string]*imageprinter.PackageScore{},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
actual := extractPkgNameToScoreMap(tt.matches)
if len(actual) == 0 {
assert.Equal(t, tt.want, actual)
return
}
if len(actual) != len(tt.want) {
t.Errorf("extractPkgNameToScore() = %v, want %v", actual, tt.want)
}
for k := range actual {
if actual[k].Score != tt.want[k].Score {
t.Errorf("extractPkgNameToScore() = %v, want %v", actual, tt.want)
}
if actual[k].Version != tt.want[k].Version {
t.Errorf("extractPkgNameToScore() = %v, want %v", actual, tt.want)
}
if actual[k].Name != tt.want[k].Name {
t.Errorf("extractPkgNameToScore() = %v, want %v", actual, tt.want)
}
}
})
}
}
// func TestExtractSeverityToSummaryMap(t *testing.T) {
// tests := []struct {
// name string
// cves []imageprinter.CVE
// want map[string]*imageprinter.SeveritySummary
// }{
// {
// name: "single cve",
// cves: []imageprinter.CVE{
// {
// ID: "CVE-2020-1234",
// Severity: "High",
// FixedState: string(v5.FixedState),
// },
// },
// want: map[string]*imageprinter.SeveritySummary{
// "High": {
// NumberOfCVEs: 1,
// NumberOfFixableCVEs: 1,
// },
// },
// },
// {
// name: "multiple cves",
// cves: []imageprinter.CVE{
// {
// ID: "CVE-2020-1234",
// Severity: "High",
// FixedState: string(v5.FixedState),
// },
// {
// ID: "CVE-2020-1235",
// Severity: "High",
// FixedState: string(v5.NotFixedState),
// },
// {
// ID: "CVE-2020-23",
// Severity: "Low",
// FixedState: string(v5.NotFixedState),
// },
// {
// ID: "CVE-2020-4321",
// Severity: "Medium",
// FixedState: string(v5.NotFixedState),
// },
// {
// ID: "CVE-2020-53152",
// Severity: "Negligible",
// FixedState: string(v5.NotFixedState),
// },
// {
// ID: "CVE-2020-531524",
// Severity: "Negligible",
// FixedState: string(v5.NotFixedState),
// },
// },
// want: map[string]*imageprinter.SeveritySummary{
// "High": {
// NumberOfCVEs: 2,
// NumberOfFixableCVEs: 1,
// },
// "Low": {
// NumberOfCVEs: 1,
// NumberOfFixableCVEs: 0,
// },
// "Medium": {
// NumberOfCVEs: 1,
// NumberOfFixableCVEs: 0,
// },
// "Negligible": {
// NumberOfCVEs: 2,
// NumberOfFixableCVEs: 0,
// },
// },
// },
// }
// for _, tt := range tests {
// t.Run(tt.name, func(t *testing.T) {
// actual := extractSeverityToSummaryMap(tt.cves)
// if len(actual) == 0 {
// assert.Equal(t, tt.want, actual)
// return
// }
// if len(actual) != len(tt.want) {
// t.Errorf("extractSeverityToSummaryMap() = %v, want %v", actual, tt.want)
// }
// for k := range actual {
// if actual[k].NumberOfCVEs != tt.want[k].NumberOfCVEs {
// t.Errorf("extractSeverityToSummaryMap() = %v, want %v", actual, tt.want)
// }
// if actual[k].NumberOfFixableCVEs != tt.want[k].NumberOfFixableCVEs {
// t.Errorf("extractSeverityToSummaryMap() = %v, want %v", actual, tt.want)
// }
// }
// })
// }
// }

View File

@@ -146,8 +146,8 @@ type (
// mockableOPASessionObj reproduces OPASessionObj with concrete types instead of interfaces.
// It may be unmarshaled from a JSON fixture.
mockableOPASessionObj struct {
K8SResources *cautils.K8SResources
ArmoResource *cautils.KSResources
K8SResources cautils.K8SResources
KubescapeResource cautils.KSResources
AllPolicies *cautils.Policies
AllResources map[string]*workloadinterface.Workload
ResourcesResult map[string]resourcesresults.Result
@@ -193,9 +193,9 @@ func mockOPASessionObj(t testing.TB) *cautils.OPASessionObj {
)
o := cautils.OPASessionObj{
K8SResources: v.K8SResources,
ArmoResource: v.ArmoResource,
AllPolicies: v.AllPolicies,
K8SResources: v.K8SResources,
KubescapeResource: v.KubescapeResource,
AllPolicies: v.AllPolicies,
//AllResources map[string]*workloadinterface.Workload // all scanned resources, map[<resource ID>]<resource>
ResourcesResult: v.ResourcesResult,
ResourceSource: v.ResourceSource,

View File

@@ -722,7 +722,7 @@
"rbac.authorization.k8s.io/v1/kubescape/Role/ks-sa-roles"
]
},
"ArmoResource": {
"KubescapeResource": {
"container.googleapis.com/v1/ClusterDescribe": [
"container.googleapis.com/v1/ClusterDescribe/cluster-mock"
],

View File

@@ -5,6 +5,7 @@ import (
"encoding/json"
"fmt"
"github.com/anchore/grype/grype/presenter/models"
logger "github.com/kubescape/go-logger"
"github.com/kubescape/go-logger/helpers"
"github.com/kubescape/kubescape/v2/core/cautils"
@@ -16,60 +17,62 @@ import (
)
type ResultsHandler struct {
reporterObj reporter.IReport
uiPrinter printer.IPrinter
scanData *cautils.OPASessionObj
printerObjs []printer.IPrinter
ReporterObj reporter.IReport
UiPrinter printer.IPrinter
ScanData *cautils.OPASessionObj
PrinterObjs []printer.IPrinter
ImageScanData []cautils.ImageScanData
}
func NewResultsHandler(reporterObj reporter.IReport, printerObjs []printer.IPrinter, uiPrinter printer.IPrinter) *ResultsHandler {
func NewResultsHandler(reporterObj reporter.IReport, printerObjs []printer.IPrinter, uiPrinter printer.IPrinter, imageScanData *models.PresenterConfig) *ResultsHandler {
return &ResultsHandler{
reporterObj: reporterObj,
printerObjs: printerObjs,
uiPrinter: uiPrinter,
ReporterObj: reporterObj,
PrinterObjs: printerObjs,
UiPrinter: uiPrinter,
ImageScanData: make([]cautils.ImageScanData, 0),
}
}
// GetRiskScore returns the results risk score
func (rh *ResultsHandler) GetRiskScore() float32 {
return rh.scanData.Report.SummaryDetails.Score
return rh.ScanData.Report.SummaryDetails.Score
}
// GetComplianceScore returns the results compliance score
func (rh *ResultsHandler) GetComplianceScore() float32 {
return rh.scanData.Report.SummaryDetails.ComplianceScore
return rh.ScanData.Report.SummaryDetails.ComplianceScore
}
// GetData returns scan/action related data (policies, resources, results, etc.)
//
// Call the ToJson() method if you want the JSON representation of the data
func (rh *ResultsHandler) GetData() *cautils.OPASessionObj {
return rh.scanData
return rh.ScanData
}
// SetData sets the scan/action related data
func (rh *ResultsHandler) SetData(data *cautils.OPASessionObj) {
rh.scanData = data
rh.ScanData = data
}
// GetPrinter returns all printers
func (rh *ResultsHandler) GetPrinters() []printer.IPrinter {
return rh.printerObjs
return rh.PrinterObjs
}
// GetReporter returns the reporter object
func (rh *ResultsHandler) GetReporter() reporter.IReport {
return rh.reporterObj
return rh.ReporterObj
}
// ToJson returns the results in the JSON format
func (rh *ResultsHandler) ToJson() ([]byte, error) {
return json.Marshal(printerv2.FinalizeResults(rh.scanData))
return json.Marshal(printerv2.FinalizeResults(rh.ScanData))
}
// GetResults returns the results
func (rh *ResultsHandler) GetResults() *reporthandlingv2.PostureReport {
return printerv2.FinalizeResults(rh.scanData)
return printerv2.FinalizeResults(rh.ScanData)
}
// HandleResults handles all necessary actions for the scan results
@@ -78,21 +81,26 @@ func (rh *ResultsHandler) HandleResults(ctx context.Context) error {
// First we output the results and then the score, so the
// score - a summary of the results—can always be seen at the end
// of output
rh.uiPrinter.ActionPrint(ctx, rh.scanData)
rh.uiPrinter.Score(rh.GetComplianceScore())
rh.UiPrinter.ActionPrint(ctx, rh.ScanData, rh.ImageScanData)
rh.UiPrinter.PrintNextSteps()
// Then print to output files
for _, printer := range rh.printerObjs {
printer.ActionPrint(ctx, rh.scanData)
printer.Score(rh.GetComplianceScore())
for _, printer := range rh.PrinterObjs {
printer.ActionPrint(ctx, rh.ScanData, rh.ImageScanData)
if rh.ScanData != nil {
printer.Score(rh.GetComplianceScore())
}
}
// We should submit only after printing results, so a user can see
// results at all times, even if submission fails
if err := rh.reporterObj.Submit(ctx, rh.scanData); err != nil {
return err
if rh.ReporterObj != nil {
if err := rh.ReporterObj.Submit(ctx, rh.ScanData); err != nil {
return err
}
rh.ReporterObj.DisplayReportURL()
}
rh.reporterObj.DisplayReportURL()
return nil
}
@@ -123,6 +131,6 @@ func NewPrinter(ctx context.Context, printFormat, formatVersion string, verboseM
if printFormat != printer.PrettyFormat {
logger.L().Ctx(ctx).Warning(fmt.Sprintf("Invalid format \"%s\", default format \"pretty-printer\" is applied", printFormat))
}
return printerv2.NewPrettyPrinter(verboseMode, formatVersion, attackTree, viewType)
return printerv2.NewPrettyPrinter(verboseMode, formatVersion, attackTree, viewType, "", nil)
}
}

View File

@@ -8,7 +8,7 @@ Using ARMO Platform, you will save valuable time and make spot-on hardening deci
1. Install Kubescape
```
curl -s https://raw.githubusercontent.com/kubescape/kubescape/master/docs/providers/install.sh | /bin/bash
curl -s https://raw.githubusercontent.com/kubescape/kubescape/master/install.sh | /bin/bash
```
> Alternatively, you can [install Kubescape using package managers](../installation.md#installation)

View File

@@ -1,40 +0,0 @@
Write-Host "Installing Kubescape..." -ForegroundColor Cyan
$BASE_DIR=$env:USERPROFILE + "\.kubescape"
$packageName = "/kubescape.exe"
# Get latest release url
$config = Invoke-WebRequest "https://api.github.com/repos/kubescape/kubescape/releases/latest" | ConvertFrom-Json
$url = $config.html_url.Replace("/tag/","/download/")
$fullUrl = $url + $packageName
# Create a new directory if needed
New-Item -Path $BASE_DIR -ItemType "directory" -ErrorAction SilentlyContinue
# Download the binary
$useBitTransfer = $null -ne (Get-Module -Name BitsTransfer -ListAvailable) -and ($PSVersionTable.PSVersion.Major -le 5)
if ($useBitTransfer)
{
Write-Information -MessageData 'Using a fallback BitTransfer method since you are running Windows PowerShell'
Start-BitsTransfer -Source $fullUrl -Destination $BASE_DIR\kubescape.exe
}
else
{
Invoke-WebRequest -Uri $fullUrl -OutFile $BASE_DIR\kubescape.exe
}
# Update user PATH if needed
$currentPath = [Environment]::GetEnvironmentVariable("Path", "User")
if (-not $currentPath.Contains($BASE_DIR)) {
$confirmation = Read-Host "Add kubescape to user path? (y/n)"
if ($confirmation -eq 'y') {
$env:Path=[Environment]::GetEnvironmentVariable("Path", "User") + ";$BASE_DIR;"
[Environment]::SetEnvironmentVariable("Path", "${env:Path}", "User")
}
}
Write-Host "Finished Installation.`n" -ForegroundColor Green
Write-Host "Executing Kubescape.`n" -ForegroundColor Green
kubescape scan --create-account

View File

@@ -1,121 +0,0 @@
#!/bin/bash
set -e
while getopts v: option
do
case ${option} in
v) RELEASE="download/${OPTARG}";;
*) ;;
esac
done
if [ -z "${RELEASE}" ]; then
RELEASE="latest/download"
fi
echo -e "\033[0;36mInstalling Kubescape..."
echo
BASE_DIR=~/.kubescape
KUBESCAPE_EXEC=kubescape
osName=$(uname -s)
if [[ $osName == *"MINGW"* ]]; then
osName=windows
elif [[ $osName == *"Darwin"* ]]; then
osName=macos
else
osName=ubuntu
fi
arch=$(uname -m)
if [[ $arch == *"aarch64"* || $arch == *"arm64"* ]]; then
arch="-arm64"
else
if [[ $arch != *"x86_64"* ]]; then
echo -e "\033[33mArchitecture $arch may be unsupported, will try to install the amd64 one anyway."
fi
arch=""
fi
mkdir -p $BASE_DIR
OUTPUT=$BASE_DIR/$KUBESCAPE_EXEC
DOWNLOAD_URL="https://github.com/kubescape/kubescape/releases/${RELEASE}/kubescape${arch}-${osName}-latest"
curl --progress-bar -L $DOWNLOAD_URL -o $OUTPUT
# Find install dir
install_dir=/usr/local/bin # default if running as root
if [ "$(id -u)" -ne 0 ]; then
install_dir=$BASE_DIR/bin # if not running as root, install to user dir
export PATH=$PATH:$BASE_DIR/bin
fi
# Create install dir if it does not exist
if [ ! -d "$install_dir" ]; then
mkdir -p $install_dir
fi
chmod +x $OUTPUT 2>/dev/null
# cleaning up old install
SUDO=
if [ "$(id -u)" -ne 0 ] && [ -n "$(which sudo)" ] && [ "$KUBESCAPE_EXEC" != "" ] && [ -f /usr/local/bin/$KUBESCAPE_EXEC ]; then
SUDO=sudo
echo -e "\n\033[33mOld installation as root found, do you want to remove it? [\033[0my\033[33m/n]:"
read -n 1 -r
if [[ ! $REPLY =~ ^[Yy]$ ]] && [[ "$REPLY" != "" ]]; then
echo -e "\n\033[0mSkipping old installation as root removal."
else
echo -e "\n\033[0mWe will need the root access to uninstall the old kubescape CLI."
if $SUDO rm -f /usr/local/bin/$KUBESCAPE_EXEC 2>/dev/null; then
echo -e "\033[32mRemoved old installation as root at /usr/local/bin/$KUBESCAPE_EXEC"
else
echo -e "\033[31mFailed to remove old installation as root at /usr/local/bin/$KUBESCAPE_EXEC, please remove it manually."
fi
fi
fi
if [ "$KUBESCAPE_EXEC" != "" ]; then
if [ "${SUDO_USER:-$USER}" != "" ]; then
rm -f /home/"${SUDO_USER:-$USER}"/.kubescape/bin/$KUBESCAPE_EXEC 2>/dev/null || true
fi
if [ "$BASE_DIR" != "" ]; then
rm -f $BASE_DIR/bin/$KUBESCAPE_EXEC 2>/dev/null || true
fi
fi
# Old install location, clean all those things up
for pdir in ${PATH//:/ }; do
edir="${pdir/#\~/$HOME}"
if [[ $edir == $HOME/* ]] && [[ -f $edir/$KUBESCAPE_EXEC ]]; then
echo -e "\n\033[33mOld installation found at $edir/$KUBESCAPE_EXEC, do you want to remove it? [\033[0my\033[33m/n]:"
read -n 1 -r
if [[ ! $REPLY =~ ^[Yy]$ ]] && [[ "$REPLY" != "" ]]; then
continue
fi
if rm -f "$edir"/$KUBESCAPE_EXEC 2>/dev/null; then
echo -e "\n\033[32mRemoved old installation at $edir/$KUBESCAPE_EXEC"
else
echo -e "\n\033[31mFailed to remove old installation as root at $edir/$KUBESCAPE_EXEC, please remove it manually."
fi
fi
done
cp $OUTPUT $install_dir/$KUBESCAPE_EXEC
rm -f $OUTPUT
echo
echo -e "\033[32mFinished Installation."
if [ "$(id -u)" -ne 0 ]; then
echo -e "\nRemember to add the Kubescape CLI to your path with:"
echo -e " export PATH=\$PATH:$BASE_DIR/bin"
export PATH=\$PATH:$BASE_DIR/bin
fi
echo -e "\033[0m"
echo -e "\033[32mExecuting Kubescape."
echo
$KUBESCAPE_EXEC scan --create-account

152
go.mod
View File

@@ -4,39 +4,43 @@ go 1.20
require (
cloud.google.com/go/containeranalysis v0.9.0
github.com/armosec/armoapi-go v0.0.212
github.com/adrg/xdg v0.4.0
github.com/anchore/grype v0.64.2
github.com/anchore/stereoscope v0.0.0-20230724160817-d515761c6ca2
github.com/anchore/syft v0.85.0
github.com/armosec/armoapi-go v0.0.211
github.com/armosec/utils-go v0.0.20
github.com/armosec/utils-k8s-go v0.0.16
github.com/briandowns/spinner v1.18.1
github.com/enescakir/emoji v1.0.0
github.com/fatih/color v1.15.0
github.com/francoispqt/gojay v1.2.13
github.com/go-git/go-git/v5 v5.5.2
github.com/google/go-containerregistry v0.13.0
github.com/go-git/go-git/v5 v5.7.0
github.com/google/go-containerregistry v0.15.2
github.com/google/uuid v1.3.0
github.com/johnfercher/maroto v0.42.0
github.com/json-iterator/go v1.1.12
github.com/kubescape/go-git-url v0.0.25
github.com/kubescape/go-logger v0.0.15
github.com/kubescape/k8s-interface v0.0.135
github.com/kubescape/opa-utils v0.0.260
github.com/kubescape/opa-utils v0.0.260-0.20230731053836-95235e06963a
github.com/kubescape/rbac-utils v0.0.20
github.com/kubescape/regolibrary v1.0.286-rc.0
github.com/libgit2/git2go/v33 v33.0.9
github.com/mattn/go-isatty v0.0.17
github.com/mattn/go-isatty v0.0.18
github.com/mikefarah/yq/v4 v4.29.1
github.com/olekukonko/tablewriter v0.0.5
github.com/open-policy-agent/opa v0.55.0
github.com/owenrumney/go-sarif/v2 v2.1.2
github.com/schollz/progressbar/v3 v3.13.0
github.com/sergi/go-diff v1.2.0
github.com/sergi/go-diff v1.3.1
github.com/sigstore/cosign v1.13.1
github.com/spf13/cobra v1.7.0
github.com/stretchr/testify v1.8.4
github.com/whilp/git-urls v1.0.0
go.opentelemetry.io/otel v1.16.0
go.opentelemetry.io/otel/metric v1.16.0
golang.org/x/mod v0.11.0
golang.org/x/mod v0.12.0
golang.org/x/sync v0.3.0
google.golang.org/api v0.126.0
google.golang.org/genproto v0.0.0-20230530153820-e85fd2cbaebc
@@ -56,11 +60,13 @@ require github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 // indirect
require (
bitbucket.org/creachadair/shell v0.0.7 // indirect
cloud.google.com/go v0.110.2 // indirect
cloud.google.com/go/compute v1.20.1 // indirect
cloud.google.com/go/compute/metadata v0.2.3 // indirect
cloud.google.com/go/container v1.24.0 // indirect
cloud.google.com/go/grafeas v0.2.0 // indirect
cloud.google.com/go/iam v0.13.0 // indirect
cloud.google.com/go/storage v1.29.0 // indirect
github.com/AliyunContainerService/ack-ram-tool/pkg/credentials/alibabacloudsdkgo/helper v0.2.0 // indirect
github.com/Azure/azure-sdk-for-go v66.0.0+incompatible // indirect
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.6.0 // indirect
@@ -79,15 +85,19 @@ require (
github.com/Azure/go-autorest/tracing v0.6.0 // indirect
github.com/AzureAD/microsoft-authentication-library-for-go v1.0.0 // indirect
github.com/BurntSushi/toml v1.2.1 // indirect
github.com/CycloneDX/cyclonedx-go v0.7.1 // indirect
github.com/DataDog/zstd v1.4.5 // indirect
github.com/Masterminds/goutils v1.1.1 // indirect
github.com/Masterminds/semver v1.5.0 // indirect
github.com/Masterminds/semver/v3 v3.2.0 // indirect
github.com/Masterminds/sprig/v3 v3.2.3 // indirect
github.com/Microsoft/go-winio v0.6.0 // indirect
github.com/Microsoft/go-winio v0.6.1 // indirect
github.com/OneOfOne/xxhash v1.2.8 // indirect
github.com/ProtonMail/go-crypto v0.0.0-20221026131551-cf6655e29de4 // indirect
github.com/ProtonMail/go-crypto v0.0.0-20230518184743-7afd39499903 // indirect
github.com/ThalesIgnite/crypto11 v1.2.5 // indirect
github.com/a8m/envsubst v1.3.0 // indirect
github.com/acomagu/bufpipe v1.0.3 // indirect
github.com/acobaugh/osrelease v0.1.0 // indirect
github.com/acomagu/bufpipe v1.0.4 // indirect
github.com/agnivade/levenshtein v1.1.1 // indirect
github.com/alecthomas/participle/v2 v2.0.0-beta.5 // indirect
github.com/alibabacloud-go/alibabacloud-gateway-spi v0.0.4 // indirect
@@ -101,6 +111,13 @@ require (
github.com/alibabacloud-go/tea-utils v1.4.4 // indirect
github.com/alibabacloud-go/tea-xml v1.1.2 // indirect
github.com/aliyun/credentials-go v1.2.3 // indirect
github.com/anchore/go-logger v0.0.0-20230531193951-db5ae83e7dbe // indirect
github.com/anchore/go-macholibre v0.0.0-20220308212642-53e6d0aaf6fb // indirect
github.com/anchore/go-struct-converter v0.0.0-20221118182256-c68fdcfa2092 // indirect
github.com/anchore/go-version v1.2.2-0.20210903204242-51efa5b487c4 // indirect
github.com/anchore/packageurl-go v0.1.1-0.20230104203445-02e0a6721501 // indirect
github.com/anchore/sqlite v1.4.6-0.20220607210448-bcc6ee5c4963 // indirect
github.com/andybalholm/brotli v1.0.4 // indirect
github.com/armosec/gojay v1.2.15 // indirect
github.com/asaskevich/govalidator v0.0.0-20210307081110-f21760c49a8d // indirect
github.com/aws/aws-sdk-go v1.44.312 // indirect
@@ -120,51 +137,61 @@ require (
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.14.14 // indirect
github.com/aws/aws-sdk-go-v2/service/sts v1.20.1 // indirect
github.com/aws/smithy-go v1.13.5 // indirect
github.com/awslabs/amazon-ecr-credential-helper/ecr-login v0.0.0-20220228164355-396b2034c795 // indirect
github.com/awslabs/amazon-ecr-credential-helper/ecr-login v0.0.0-20220517224237-e6f29200ae04 // indirect
github.com/becheran/wildmatch-go v1.0.0 // indirect
github.com/benbjohnson/clock v1.1.0 // indirect
github.com/beorn7/perks v1.0.1 // indirect
github.com/bgentry/go-netrc v0.0.0-20140422174119-9fd32a8b3d3d // indirect
github.com/bgentry/speakeasy v0.1.0 // indirect
github.com/blang/semver v3.5.1+incompatible // indirect
github.com/bmatcuk/doublestar/v2 v2.0.4 // indirect
github.com/bmatcuk/doublestar/v4 v4.6.0 // indirect
github.com/boombuler/barcode v1.0.1 // indirect
github.com/cenkalti/backoff/v4 v4.2.1 // indirect
github.com/census-instrumentation/opencensus-proto v0.4.1 // indirect
github.com/cespare/xxhash/v2 v2.2.0 // indirect
github.com/chrismellard/docker-credential-acr-env v0.0.0-20220119192733-fe33c00cee21 // indirect
github.com/clbanning/mxj/v2 v2.5.6 // indirect
github.com/cloudflare/circl v1.1.0 // indirect
github.com/cloudflare/circl v1.3.3 // indirect
github.com/cncf/udpa/go v0.0.0-20220112060539-c52dc94e7fbe // indirect
github.com/cncf/xds/go v0.0.0-20230105202645-06c439db220b // indirect
github.com/common-nighthawk/go-figure v0.0.0-20210622060536-734e95fb86be // indirect
github.com/containerd/stargz-snapshotter/estargz v0.12.1 // indirect
github.com/containerd/containerd v1.7.2 // indirect
github.com/containerd/stargz-snapshotter/estargz v0.14.3 // indirect
github.com/coreos/go-oidc v2.2.1+incompatible // indirect
github.com/coreos/go-oidc/v3 v3.4.0 // indirect
github.com/coreos/go-semver v0.3.0 // indirect
github.com/coreos/go-systemd/v22 v22.4.0 // indirect
github.com/coreos/go-systemd/v22 v22.5.0 // indirect
github.com/cpuguy83/go-md2man/v2 v2.0.2 // indirect
github.com/cyberphone/json-canonicalization v0.0.0-20210823021906-dc406ceaf94b // indirect
github.com/cyphar/filepath-securejoin v0.2.3 // indirect
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/deitch/magic v0.0.0-20230404182410-1ff89d7342da // indirect
github.com/dimchansky/utfbom v1.1.1 // indirect
github.com/docker/cli v20.10.21+incompatible // indirect
github.com/docker/distribution v2.8.1+incompatible // indirect
github.com/docker/cli v23.0.5+incompatible // indirect
github.com/docker/distribution v2.8.2+incompatible // indirect
github.com/docker/docker v24.0.5+incompatible // indirect
github.com/docker/docker-credential-helpers v0.7.0 // indirect
github.com/docker/go-connections v0.4.0 // indirect
github.com/docker/go-units v0.5.0 // indirect
github.com/dustin/go-humanize v1.0.0 // indirect
github.com/dsnet/compress v0.0.2-0.20210315054119-f66993602bf5 // indirect
github.com/dustin/go-humanize v1.0.1 // indirect
github.com/elliotchance/orderedmap v1.5.0 // indirect
github.com/emicklei/go-restful/v3 v3.9.0 // indirect
github.com/emicklei/go-restful/v3 v3.10.1 // indirect
github.com/emirpasic/gods v1.18.1 // indirect
github.com/envoyproxy/go-control-plane v0.10.3 // indirect
github.com/envoyproxy/protoc-gen-validate v0.9.1 // indirect
github.com/evanphx/json-patch v5.6.0+incompatible // indirect
github.com/facebookincubator/nvdtools v0.1.5 // indirect
github.com/fsnotify/fsnotify v1.6.0 // indirect
github.com/fullstorydev/grpcurl v1.8.7 // indirect
github.com/gabriel-vasile/mimetype v1.4.2 // indirect
github.com/ghodss/yaml v1.0.0 // indirect
github.com/github/go-spdx/v2 v2.1.2 // indirect
github.com/go-chi/chi v4.1.2+incompatible // indirect
github.com/go-errors/errors v1.0.1 // indirect
github.com/go-git/gcfg v1.5.0 // indirect
github.com/go-git/go-billy/v5 v5.4.0 // indirect
github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 // indirect
github.com/go-git/go-billy/v5 v5.4.1 // indirect
github.com/go-gota/gota v0.12.0 // indirect
github.com/go-ini/ini v1.67.0 // indirect
github.com/go-logr/logr v1.2.4 // indirect
@@ -183,6 +210,8 @@ require (
github.com/go-playground/locales v0.14.0 // indirect
github.com/go-playground/universal-translator v0.18.0 // indirect
github.com/go-playground/validator/v10 v10.11.0 // indirect
github.com/go-restruct/restruct v1.2.0-alpha // indirect
github.com/go-test/deep v1.1.0 // indirect
github.com/gobwas/glob v0.2.3 // indirect
github.com/goccy/go-json v0.9.11 // indirect
github.com/goccy/go-yaml v1.9.6 // indirect
@@ -201,6 +230,7 @@ require (
github.com/google/go-github/v45 v45.2.0 // indirect
github.com/google/go-querystring v1.1.0 // indirect
github.com/google/gofuzz v1.2.0 // indirect
github.com/google/licensecheck v0.3.1 // indirect
github.com/google/s2a-go v0.1.4 // indirect
github.com/google/trillian v1.5.0 // indirect
github.com/googleapis/enterprise-certificate-proxy v0.2.3 // indirect
@@ -211,35 +241,52 @@ require (
github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0 // indirect
github.com/grpc-ecosystem/grpc-gateway v1.16.0 // indirect
github.com/grpc-ecosystem/grpc-gateway/v2 v2.15.2 // indirect
github.com/hako/durafmt v0.0.0-20210608085754-5c1018a4e16b // indirect
github.com/hashicorp/errwrap v1.1.0 // indirect
github.com/hashicorp/go-cleanhttp v0.5.2 // indirect
github.com/hashicorp/go-getter v1.7.1 // indirect
github.com/hashicorp/go-multierror v1.1.1 // indirect
github.com/hashicorp/go-retryablehttp v0.7.1 // indirect
github.com/hashicorp/go-safetemp v1.0.0 // indirect
github.com/hashicorp/go-version v1.6.0 // indirect
github.com/hashicorp/hcl v1.0.0 // indirect
github.com/huandu/xstrings v1.3.3 // indirect
github.com/imdario/mergo v0.3.13 // indirect
github.com/imdario/mergo v0.3.15 // indirect
github.com/in-toto/in-toto-golang v0.3.4-0.20220709202702-fa494aaa0add // indirect
github.com/inconshreveable/mousetrap v1.1.0 // indirect
github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 // indirect
github.com/jedisct1/go-minisign v0.0.0-20211028175153-1c139d1cc84b // indirect
github.com/jhump/protoreflect v1.13.0 // indirect
github.com/jinzhu/copier v0.3.5 // indirect
github.com/jinzhu/inflection v1.0.0 // indirect
github.com/jinzhu/now v1.1.5 // indirect
github.com/jmespath/go-jmespath v0.4.0 // indirect
github.com/jonboulle/clockwork v0.3.0 // indirect
github.com/josharian/intern v1.0.0 // indirect
github.com/jung-kurt/gofpdf v1.16.2 // indirect
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 // indirect
github.com/kevinburke/ssh_config v1.2.0 // indirect
github.com/klauspost/compress v1.16.0 // indirect
github.com/klauspost/compress v1.16.5 // indirect
github.com/klauspost/pgzip v1.2.5 // indirect
github.com/knqyf263/go-apk-version v0.0.0-20200609155635-041fdbb8563f // indirect
github.com/knqyf263/go-deb-version v0.0.0-20190517075300-09fca494f03d // indirect
github.com/knqyf263/go-rpmdb v0.0.0-20230301153543-ba94b245509b // indirect
github.com/kylelemons/godebug v1.1.0 // indirect
github.com/leodido/go-urn v1.2.1 // indirect
github.com/letsencrypt/boulder v0.0.0-20220929215747-76583552c2be // indirect
github.com/magiconair/properties v1.8.6 // indirect
github.com/magiconair/properties v1.8.7 // indirect
github.com/mailru/easyjson v0.7.7 // indirect
github.com/mattn/go-colorable v0.1.13 // indirect
github.com/mattn/go-runewidth v0.0.14 // indirect
github.com/matttproud/golang_protobuf_extensions v1.0.4 // indirect
github.com/mholt/archiver/v3 v3.5.1 // indirect
github.com/microsoft/go-rustaudit v0.0.0-20220730194248-4b17361d90a5 // indirect
github.com/miekg/pkcs11 v1.1.1 // indirect
github.com/mitchellh/colorstring v0.0.0-20190213212951-d06e56a500db // indirect
github.com/mitchellh/copystructure v1.2.0 // indirect
github.com/mitchellh/go-homedir v1.1.0 // indirect
github.com/mitchellh/go-testing-interface v1.14.1 // indirect
github.com/mitchellh/hashstructure/v2 v2.0.2 // indirect
github.com/mitchellh/mapstructure v1.5.0 // indirect
github.com/mitchellh/reflectwalk v1.0.2 // indirect
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
@@ -247,13 +294,16 @@ require (
github.com/monochromegane/go-gitignore v0.0.0-20200626010858-205db1a8cc00 // indirect
github.com/mozillazg/docker-credential-acr-helper v0.3.0 // indirect
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
github.com/nwaples/rardecode v1.1.0 // indirect
github.com/oklog/ulid v1.3.1 // indirect
github.com/opencontainers/go-digest v1.0.0 // indirect
github.com/opencontainers/image-spec v1.1.0-rc4 // indirect
github.com/opentracing/opentracing-go v1.2.0 // indirect
github.com/owenrumney/go-sarif v1.1.1 // indirect
github.com/pelletier/go-toml v1.9.5 // indirect
github.com/pelletier/go-toml/v2 v2.0.5 // indirect
github.com/pjbgf/sha1cd v0.2.3 // indirect
github.com/pelletier/go-toml/v2 v2.0.8 // indirect
github.com/pierrec/lz4/v4 v4.1.15 // indirect
github.com/pjbgf/sha1cd v0.3.0 // indirect
github.com/pkg/browser v0.0.0-20210911075715-681adbf594b8 // indirect
github.com/pkg/errors v0.9.1 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
@@ -263,10 +313,13 @@ require (
github.com/prometheus/common v0.42.0 // indirect
github.com/prometheus/procfs v0.10.1 // indirect
github.com/rcrowley/go-metrics v0.0.0-20201227073835-cf1acfcdf475 // indirect
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect
github.com/rivo/uniseg v0.4.3 // indirect
github.com/russross/blackfriday/v2 v2.1.0 // indirect
github.com/ruudk/golang-pdf417 v0.0.0-20201230142125-a7e3863a1245 // indirect
github.com/sassoftware/go-rpmutils v0.2.0 // indirect
github.com/sassoftware/relic v0.0.0-20210427151427-dfb082b79b74 // indirect
github.com/scylladb/go-set v1.0.3-0.20200225121959-cc7b2070d91e // indirect
github.com/secure-systems-lab/go-securesystemslib v0.4.0 // indirect
github.com/segmentio/ksuid v1.0.4 // indirect
github.com/shibumi/go-pathspec v1.3.0 // indirect
@@ -275,41 +328,53 @@ require (
github.com/sigstore/rekor v0.12.1-0.20220915152154-4bb6f441c1b2 // indirect
github.com/sigstore/sigstore v1.4.4 // indirect
github.com/sirupsen/logrus v1.9.3 // indirect
github.com/skeema/knownhosts v1.1.0 // indirect
github.com/skeema/knownhosts v1.1.1 // indirect
github.com/skratchdot/open-golang v0.0.0-20200116055534-eef842397966 // indirect
github.com/soheilhy/cmux v0.1.5 // indirect
github.com/spf13/afero v1.9.2 // indirect
github.com/spf13/cast v1.5.0 // indirect
github.com/spdx/tools-golang v0.5.2 // indirect
github.com/spf13/afero v1.9.5 // indirect
github.com/spf13/cast v1.5.1 // indirect
github.com/spf13/jwalterweatherman v1.1.0 // indirect
github.com/spf13/pflag v1.0.5 // indirect
github.com/spf13/viper v1.13.0 // indirect
github.com/spf13/viper v1.16.0 // indirect
github.com/spiffe/go-spiffe/v2 v2.1.1 // indirect
github.com/stripe/stripe-go/v74 v74.28.0 // indirect
github.com/subosito/gotenv v1.4.1 // indirect
github.com/subosito/gotenv v1.4.2 // indirect
github.com/sylabs/sif/v2 v2.8.1 // indirect
github.com/sylabs/squashfs v0.6.1 // indirect
github.com/syndtr/goleveldb v1.0.1-0.20220721030215-126854af5e6d // indirect
github.com/tchap/go-patricia/v2 v2.3.1 // indirect
github.com/tent/canonical-json-go v0.0.0-20130607151641-96e4ba3a7613 // indirect
github.com/thales-e-security/pool v0.0.2 // indirect
github.com/therootcompany/xz v1.0.1 // indirect
github.com/theupdateframework/go-tuf v0.5.2-0.20220930112810-3890c1e7ace4 // indirect
github.com/titanous/rocacheck v0.0.0-20171023193734-afe73141d399 // indirect
github.com/tjfoc/gmsm v1.3.2 // indirect
github.com/tmc/grpc-websocket-proxy v0.0.0-20220101234140-673ab2c3ae75 // indirect
github.com/transparency-dev/merkle v0.0.1 // indirect
github.com/ulikunitz/xz v0.5.10 // indirect
github.com/uptrace/opentelemetry-go-extra/otelutil v0.2.2 // indirect
github.com/uptrace/opentelemetry-go-extra/otelzap v0.2.2 // indirect
github.com/uptrace/uptrace-go v1.16.0 // indirect
github.com/urfave/cli v1.22.7 // indirect
github.com/vbatts/tar-split v0.11.2 // indirect
github.com/urfave/cli v1.22.12 // indirect
github.com/vbatts/go-mtree v0.5.3 // indirect
github.com/vbatts/tar-split v0.11.3 // indirect
github.com/vifraa/gopom v0.2.1 // indirect
github.com/wagoodman/go-partybus v0.0.0-20230516145632-8ccac152c651 // indirect
github.com/wagoodman/go-presenter v0.0.0-20211015174752-f9c01afc824b // indirect
github.com/wagoodman/go-progress v0.0.0-20230301185719-21920a456ad5 // indirect
github.com/xanzy/go-gitlab v0.73.1 // indirect
github.com/xanzy/ssh-agent v0.3.3 // indirect
github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb // indirect
github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 // indirect
github.com/xeipuuv/gojsonschema v1.2.0 // indirect
github.com/xi2/xz v0.0.0-20171230120015-48954b6210f8 // indirect
github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2 // indirect
github.com/xlab/treeprint v1.1.0 // indirect
github.com/yashtewari/glob-intersection v0.2.0 // indirect
github.com/zclconf/go-cty v1.10.0 // indirect
github.com/zeebo/errs v1.2.2 // indirect
go.etcd.io/bbolt v1.3.6 // indirect
go.etcd.io/bbolt v1.3.7 // indirect
go.etcd.io/etcd/api/v3 v3.6.0-alpha.0 // indirect
go.etcd.io/etcd/client/pkg/v3 v3.6.0-alpha.0 // indirect
go.etcd.io/etcd/client/v2 v2.306.0-alpha.0 // indirect
@@ -323,7 +388,7 @@ require (
go.etcd.io/etcd/v3 v3.6.0-alpha.0 // indirect
go.mongodb.org/mongo-driver v1.11.1 // indirect
go.opencensus.io v0.24.0 // indirect
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.35.0 // indirect
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.40.0 // indirect
go.opentelemetry.io/contrib/instrumentation/runtime v0.42.0 // indirect
go.opentelemetry.io/otel/exporters/otlp/internal/retry v1.16.0 // indirect
go.opentelemetry.io/otel/exporters/otlp/otlpmetric v0.39.0 // indirect
@@ -361,9 +426,20 @@ require (
gopkg.in/square/go-jose.v2 v2.6.0 // indirect
gopkg.in/warnings.v0 v0.1.2 // indirect
gopkg.in/yaml.v2 v2.4.0 // indirect
gorm.io/gorm v1.25.2 // indirect
k8s.io/apiextensions-apiserver v0.27.2 // indirect
k8s.io/klog/v2 v2.100.1 // indirect
k8s.io/kube-openapi v0.0.0-20230501164219-8b0f38b5fd1f // indirect
lukechampine.com/uint128 v1.3.0 // indirect
modernc.org/cc/v3 v3.40.0 // indirect
modernc.org/ccgo/v3 v3.16.13 // indirect
modernc.org/libc v1.22.5 // indirect
modernc.org/mathutil v1.5.0 // indirect
modernc.org/memory v1.5.0 // indirect
modernc.org/opt v0.1.3 // indirect
modernc.org/sqlite v1.24.0 // indirect
modernc.org/strutil v1.1.3 // indirect
modernc.org/token v1.1.0 // indirect
sigs.k8s.io/controller-runtime v0.15.0 // indirect
sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd // indirect
sigs.k8s.io/release-utils v0.7.3 // indirect
@@ -373,4 +449,10 @@ require (
replace github.com/libgit2/git2go/v33 => ./git2go
replace google.golang.org/grpc => google.golang.org/grpc v1.54.0
replace (
// TODO(vladklokun): Since later versions (e.g. v0.40.0) that get used without the pin introduce weird packaging issues probably due to package renames, pin to last known good.
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc => go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.35.0
google.golang.org/grpc => google.golang.org/grpc v1.54.0
gorm.io/gorm => gorm.io/gorm v1.23.10
)

432
go.sum

File diff suppressed because it is too large Load Diff

View File

@@ -5,19 +5,19 @@ go 1.20
replace github.com/kubescape/kubescape/v2 => ../
require (
github.com/armosec/utils-go v0.0.20
github.com/armosec/utils-go v0.0.14
github.com/go-openapi/runtime v0.24.2
github.com/google/uuid v1.3.0
github.com/gorilla/mux v1.8.0
github.com/gorilla/schema v1.2.0
github.com/kubescape/go-logger v0.0.15
github.com/kubescape/k8s-interface v0.0.135
github.com/kubescape/go-logger v0.0.13
github.com/kubescape/k8s-interface v0.0.134
github.com/kubescape/kubescape/v2 v2.0.0-00010101000000-000000000000
github.com/kubescape/opa-utils v0.0.260
github.com/stretchr/testify v1.8.4
github.com/kubescape/opa-utils v0.0.256-0.20230720131313-796d89cc623c
github.com/stretchr/testify v1.8.3
go.opentelemetry.io/contrib/instrumentation/github.com/gorilla/mux/otelmux v0.38.0
go.opentelemetry.io/otel v1.16.0
k8s.io/utils v0.0.0-20230726121419-3b25d923346b
k8s.io/utils v0.0.0-20230220204549-a5ecb0141aa5
)
require (
@@ -25,32 +25,32 @@ require (
go.uber.org/atomic v1.11.0 // indirect
go.uber.org/multierr v1.11.0 // indirect
go.uber.org/zap v1.24.0 // indirect
golang.org/x/crypto v0.11.0 // indirect
golang.org/x/exp v0.0.0-20230728194245-b0cb94b80691 // indirect
golang.org/x/crypto v0.10.0 // indirect
golang.org/x/exp v0.0.0-20230711023510-fffb14384f22 // indirect
golang.org/x/mod v0.11.0 // indirect
golang.org/x/net v0.12.0 // indirect
golang.org/x/oauth2 v0.10.0 // indirect
golang.org/x/net v0.11.0 // indirect
golang.org/x/oauth2 v0.7.0 // indirect
google.golang.org/appengine v1.6.7 // indirect
google.golang.org/genproto v0.0.0-20230530153820-e85fd2cbaebc // indirect
google.golang.org/grpc v1.56.2 // indirect
google.golang.org/genproto v0.0.0-20230410155749-daa745c078e1 // indirect
google.golang.org/grpc v1.55.0 // indirect
)
require (
bitbucket.org/creachadair/shell v0.0.7 // indirect
cloud.google.com/go/compute v1.20.1 // indirect
cloud.google.com/go/compute v1.19.1 // indirect
cloud.google.com/go/compute/metadata v0.2.3 // indirect
cloud.google.com/go/container v1.24.0 // indirect
cloud.google.com/go/container v1.15.0 // indirect
cloud.google.com/go/containeranalysis v0.9.0 // indirect
cloud.google.com/go/grafeas v0.2.0 // indirect
cloud.google.com/go/iam v0.13.0 // indirect
github.com/AliyunContainerService/ack-ram-tool/pkg/credentials/alibabacloudsdkgo/helper v0.2.0 // indirect
github.com/Azure/azure-sdk-for-go v66.0.0+incompatible // indirect
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.6.0 // indirect
github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.3.0 // indirect
github.com/Azure/azure-sdk-for-go/sdk/internal v1.3.0 // indirect
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.1.0 // indirect
github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.2.0 // indirect
github.com/Azure/azure-sdk-for-go/sdk/internal v1.0.0 // indirect
github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/authorization/armauthorization v1.0.0 // indirect
github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/authorization/armauthorization/v2 v2.1.1 // indirect
github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/containerservice/armcontainerservice/v2 v2.4.0 // indirect
github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/authorization/armauthorization/v2 v2.0.0 // indirect
github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/containerservice/armcontainerservice/v2 v2.2.0 // indirect
github.com/Azure/go-autorest v14.2.0+incompatible // indirect
github.com/Azure/go-autorest/autorest v0.11.28 // indirect
github.com/Azure/go-autorest/autorest/adal v0.9.21 // indirect
@@ -59,17 +59,17 @@ require (
github.com/Azure/go-autorest/autorest/date v0.3.0 // indirect
github.com/Azure/go-autorest/logger v0.2.1 // indirect
github.com/Azure/go-autorest/tracing v0.6.0 // indirect
github.com/AzureAD/microsoft-authentication-library-for-go v1.0.0 // indirect
github.com/AzureAD/microsoft-authentication-library-for-go v0.7.0 // indirect
github.com/BurntSushi/toml v1.2.1 // indirect
github.com/Masterminds/goutils v1.1.1 // indirect
github.com/Masterminds/semver/v3 v3.2.0 // indirect
github.com/Masterminds/sprig/v3 v3.2.3 // indirect
github.com/Microsoft/go-winio v0.6.0 // indirect
github.com/Microsoft/go-winio v0.6.1 // indirect
github.com/OneOfOne/xxhash v1.2.8 // indirect
github.com/ProtonMail/go-crypto v0.0.0-20221026131551-cf6655e29de4 // indirect
github.com/ProtonMail/go-crypto v0.0.0-20230518184743-7afd39499903 // indirect
github.com/ThalesIgnite/crypto11 v1.2.5 // indirect
github.com/a8m/envsubst v1.3.0 // indirect
github.com/acomagu/bufpipe v1.0.3 // indirect
github.com/acomagu/bufpipe v1.0.4 // indirect
github.com/agnivade/levenshtein v1.1.1 // indirect
github.com/alecthomas/participle/v2 v2.0.0-beta.5 // indirect
github.com/alibabacloud-go/alibabacloud-gateway-spi v0.0.4 // indirect
@@ -83,28 +83,27 @@ require (
github.com/alibabacloud-go/tea-utils v1.4.4 // indirect
github.com/alibabacloud-go/tea-xml v1.1.2 // indirect
github.com/aliyun/credentials-go v1.2.3 // indirect
github.com/armosec/armoapi-go v0.0.212 // indirect
github.com/armosec/gojay v1.2.15 // indirect
github.com/armosec/utils-k8s-go v0.0.16 // indirect
github.com/armosec/armoapi-go v0.0.202 // indirect
github.com/armosec/utils-k8s-go v0.0.13 // indirect
github.com/asaskevich/govalidator v0.0.0-20210307081110-f21760c49a8d // indirect
github.com/aws/aws-sdk-go v1.44.312 // indirect
github.com/aws/aws-sdk-go-v2 v1.19.1 // indirect
github.com/aws/aws-sdk-go-v2/config v1.18.30 // indirect
github.com/aws/aws-sdk-go-v2/credentials v1.13.29 // indirect
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.13.6 // indirect
github.com/aws/aws-sdk-go-v2/internal/configsources v1.1.36 // indirect
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.4.30 // indirect
github.com/aws/aws-sdk-go-v2/internal/ini v1.3.37 // indirect
github.com/aws/aws-sdk-go v1.44.180 // indirect
github.com/aws/aws-sdk-go-v2 v1.17.3 // indirect
github.com/aws/aws-sdk-go-v2/config v1.17.8 // indirect
github.com/aws/aws-sdk-go-v2/credentials v1.12.21 // indirect
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.12.17 // indirect
github.com/aws/aws-sdk-go-v2/internal/configsources v1.1.27 // indirect
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.4.21 // indirect
github.com/aws/aws-sdk-go-v2/internal/ini v1.3.24 // indirect
github.com/aws/aws-sdk-go-v2/service/ecr v1.18.0 // indirect
github.com/aws/aws-sdk-go-v2/service/ecrpublic v1.12.0 // indirect
github.com/aws/aws-sdk-go-v2/service/eks v1.28.1 // indirect
github.com/aws/aws-sdk-go-v2/service/eks v1.21.4 // indirect
github.com/aws/aws-sdk-go-v2/service/iam v1.19.0 // indirect
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.9.30 // indirect
github.com/aws/aws-sdk-go-v2/service/sso v1.12.14 // indirect
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.14.14 // indirect
github.com/aws/aws-sdk-go-v2/service/sts v1.20.1 // indirect
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.9.17 // indirect
github.com/aws/aws-sdk-go-v2/service/sso v1.11.23 // indirect
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.13.6 // indirect
github.com/aws/aws-sdk-go-v2/service/sts v1.16.19 // indirect
github.com/aws/smithy-go v1.13.5 // indirect
github.com/awslabs/amazon-ecr-credential-helper/ecr-login v0.0.0-20220228164355-396b2034c795 // indirect
github.com/awslabs/amazon-ecr-credential-helper/ecr-login v0.0.0-20220517224237-e6f29200ae04 // indirect
github.com/benbjohnson/clock v1.1.0 // indirect
github.com/beorn7/perks v1.0.1 // indirect
github.com/bgentry/speakeasy v0.1.0 // indirect
@@ -116,15 +115,15 @@ require (
github.com/cespare/xxhash/v2 v2.2.0 // indirect
github.com/chrismellard/docker-credential-acr-env v0.0.0-20220119192733-fe33c00cee21 // indirect
github.com/clbanning/mxj/v2 v2.5.6 // indirect
github.com/cloudflare/circl v1.1.0 // indirect
github.com/cloudflare/circl v1.3.3 // indirect
github.com/cncf/udpa/go v0.0.0-20220112060539-c52dc94e7fbe // indirect
github.com/cncf/xds/go v0.0.0-20230105202645-06c439db220b // indirect
github.com/common-nighthawk/go-figure v0.0.0-20210622060536-734e95fb86be // indirect
github.com/containerd/stargz-snapshotter/estargz v0.12.1 // indirect
github.com/containerd/stargz-snapshotter/estargz v0.14.3 // indirect
github.com/coreos/go-oidc v2.2.1+incompatible // indirect
github.com/coreos/go-oidc/v3 v3.4.0 // indirect
github.com/coreos/go-semver v0.3.0 // indirect
github.com/coreos/go-systemd/v22 v22.4.0 // indirect
github.com/coreos/go-systemd/v22 v22.5.0 // indirect
github.com/cpuguy83/go-md2man/v2 v2.0.2 // indirect
github.com/cyberphone/json-canonicalization v0.0.0-20210823021906-dc406ceaf94b // indirect
github.com/cyphar/filepath-securejoin v0.2.3 // indirect
@@ -132,13 +131,13 @@ require (
github.com/dimchansky/utfbom v1.1.1 // indirect
github.com/docker/cli v20.10.21+incompatible // indirect
github.com/docker/distribution v2.8.1+incompatible // indirect
github.com/docker/docker v24.0.5+incompatible // indirect
github.com/docker/docker v24.0.2+incompatible // indirect
github.com/docker/docker-credential-helpers v0.7.0 // indirect
github.com/docker/go-connections v0.4.0 // indirect
github.com/docker/go-units v0.5.0 // indirect
github.com/dustin/go-humanize v1.0.0 // indirect
github.com/dustin/go-humanize v1.0.1 // indirect
github.com/elliotchance/orderedmap v1.5.0 // indirect
github.com/emicklei/go-restful/v3 v3.9.0 // indirect
github.com/emicklei/go-restful/v3 v3.10.1 // indirect
github.com/emirpasic/gods v1.18.1 // indirect
github.com/enescakir/emoji v1.0.0 // indirect
github.com/envoyproxy/go-control-plane v0.10.3 // indirect
@@ -152,17 +151,16 @@ require (
github.com/ghodss/yaml v1.0.0 // indirect
github.com/go-chi/chi v4.1.2+incompatible // indirect
github.com/go-errors/errors v1.0.1 // indirect
github.com/go-git/gcfg v1.5.0 // indirect
github.com/go-git/go-billy/v5 v5.4.0 // indirect
github.com/go-git/go-git/v5 v5.5.2 // indirect
github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 // indirect
github.com/go-git/go-billy/v5 v5.4.1 // indirect
github.com/go-git/go-git/v5 v5.7.0 // indirect
github.com/go-gota/gota v0.12.0 // indirect
github.com/go-ini/ini v1.67.0 // indirect
github.com/go-logr/logr v1.2.4 // indirect
github.com/go-logr/stdr v1.2.2 // indirect
github.com/go-openapi/analysis v0.21.4 // indirect
github.com/go-openapi/errors v0.20.3 // indirect
github.com/go-openapi/jsonpointer v0.19.6 // indirect
github.com/go-openapi/jsonreference v0.20.1 // indirect
github.com/go-openapi/jsonpointer v0.19.5 // indirect
github.com/go-openapi/jsonreference v0.20.0 // indirect
github.com/go-openapi/loads v0.21.2 // indirect
github.com/go-openapi/spec v0.20.7 // indirect
github.com/go-openapi/strfmt v0.21.3 // indirect
@@ -177,7 +175,7 @@ require (
github.com/goccy/go-yaml v1.9.6 // indirect
github.com/gogo/protobuf v1.3.2 // indirect
github.com/golang-jwt/jwt v3.2.2+incompatible // indirect
github.com/golang-jwt/jwt/v4 v4.5.0 // indirect
github.com/golang-jwt/jwt/v4 v4.4.2 // indirect
github.com/golang/glog v1.1.1 // indirect
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect
github.com/golang/mock v1.6.0 // indirect
@@ -187,15 +185,15 @@ require (
github.com/google/certificate-transparency-go v1.1.3 // indirect
github.com/google/gnostic v0.5.7-v3refs // indirect
github.com/google/go-cmp v0.5.9 // indirect
github.com/google/go-containerregistry v0.13.0 // indirect
github.com/google/go-containerregistry v0.15.2 // indirect
github.com/google/go-github/v45 v45.2.0 // indirect
github.com/google/go-querystring v1.1.0 // indirect
github.com/google/gofuzz v1.2.0 // indirect
github.com/google/s2a-go v0.1.4 // indirect
github.com/google/s2a-go v0.1.3 // indirect
github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 // indirect
github.com/google/trillian v1.5.0 // indirect
github.com/googleapis/enterprise-certificate-proxy v0.2.3 // indirect
github.com/googleapis/gax-go/v2 v2.11.0 // indirect
github.com/googleapis/gax-go/v2 v2.8.0 // indirect
github.com/gorilla/websocket v1.4.2 // indirect
github.com/grpc-ecosystem/go-grpc-middleware v1.3.0 // indirect
github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0 // indirect
@@ -205,7 +203,7 @@ require (
github.com/hashicorp/go-retryablehttp v0.7.1 // indirect
github.com/hashicorp/hcl v1.0.0 // indirect
github.com/huandu/xstrings v1.3.3 // indirect
github.com/imdario/mergo v0.3.13 // indirect
github.com/imdario/mergo v0.3.15 // indirect
github.com/in-toto/in-toto-golang v0.3.4-0.20220709202702-fa494aaa0add // indirect
github.com/inconshreveable/mousetrap v1.1.0 // indirect
github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 // indirect
@@ -219,7 +217,7 @@ require (
github.com/json-iterator/go v1.1.12 // indirect
github.com/jung-kurt/gofpdf v1.16.2 // indirect
github.com/kevinburke/ssh_config v1.2.0 // indirect
github.com/klauspost/compress v1.16.0 // indirect
github.com/klauspost/compress v1.16.5 // indirect
github.com/kubescape/go-git-url v0.0.25 // indirect
github.com/kubescape/rbac-utils v0.0.20 // indirect
github.com/kubescape/regolibrary v1.0.286-rc.0 // indirect
@@ -227,10 +225,10 @@ require (
github.com/leodido/go-urn v1.2.1 // indirect
github.com/letsencrypt/boulder v0.0.0-20220929215747-76583552c2be // indirect
github.com/libgit2/git2go/v33 v33.0.9 // indirect
github.com/magiconair/properties v1.8.6 // indirect
github.com/magiconair/properties v1.8.7 // indirect
github.com/mailru/easyjson v0.7.7 // indirect
github.com/mattn/go-colorable v0.1.13 // indirect
github.com/mattn/go-isatty v0.0.17 // indirect
github.com/mattn/go-isatty v0.0.18 // indirect
github.com/mattn/go-runewidth v0.0.14 // indirect
github.com/matttproud/golang_protobuf_extensions v1.0.4 // indirect
github.com/miekg/pkcs11 v1.1.1 // indirect
@@ -247,22 +245,21 @@ require (
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
github.com/oklog/ulid v1.3.1 // indirect
github.com/olekukonko/tablewriter v0.0.5 // indirect
github.com/open-policy-agent/opa v0.55.0 // indirect
github.com/open-policy-agent/opa v0.52.0 // indirect
github.com/opencontainers/go-digest v1.0.0 // indirect
github.com/opencontainers/image-spec v1.1.0-rc4 // indirect
github.com/opencontainers/image-spec v1.1.0-rc3 // indirect
github.com/opentracing/opentracing-go v1.2.0 // indirect
github.com/owenrumney/go-sarif/v2 v2.1.2 // indirect
github.com/pelletier/go-toml v1.9.5 // indirect
github.com/pelletier/go-toml/v2 v2.0.5 // indirect
github.com/pjbgf/sha1cd v0.2.3 // indirect
github.com/pelletier/go-toml/v2 v2.0.8 // indirect
github.com/pjbgf/sha1cd v0.3.0 // indirect
github.com/pkg/browser v0.0.0-20210911075715-681adbf594b8 // indirect
github.com/pkg/errors v0.9.1 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/pquerna/cachecontrol v0.2.0 // indirect
github.com/prometheus/client_golang v1.16.0 // indirect
github.com/prometheus/client_model v0.4.0 // indirect
github.com/pquerna/cachecontrol v0.1.0 // indirect
github.com/prometheus/client_golang v1.15.0 // indirect
github.com/prometheus/client_model v0.3.0 // indirect
github.com/prometheus/common v0.42.0 // indirect
github.com/prometheus/procfs v0.10.1 // indirect
github.com/prometheus/procfs v0.9.0 // indirect
github.com/rcrowley/go-metrics v0.0.0-20201227073835-cf1acfcdf475 // indirect
github.com/rivo/uniseg v0.4.3 // indirect
github.com/russross/blackfriday/v2 v2.1.0 // indirect
@@ -271,7 +268,7 @@ require (
github.com/schollz/progressbar/v3 v3.13.0 // indirect
github.com/secure-systems-lab/go-securesystemslib v0.4.0 // indirect
github.com/segmentio/ksuid v1.0.4 // indirect
github.com/sergi/go-diff v1.2.0 // indirect
github.com/sergi/go-diff v1.3.1 // indirect
github.com/shibumi/go-pathspec v1.3.0 // indirect
github.com/shopspring/decimal v1.2.0 // indirect
github.com/sigstore/cosign v1.13.1 // indirect
@@ -279,18 +276,18 @@ require (
github.com/sigstore/rekor v0.12.1-0.20220915152154-4bb6f441c1b2 // indirect
github.com/sigstore/sigstore v1.4.4 // indirect
github.com/sirupsen/logrus v1.9.3 // indirect
github.com/skeema/knownhosts v1.1.0 // indirect
github.com/skeema/knownhosts v1.1.1 // indirect
github.com/skratchdot/open-golang v0.0.0-20200116055534-eef842397966 // indirect
github.com/soheilhy/cmux v0.1.5 // indirect
github.com/spf13/afero v1.9.2 // indirect
github.com/spf13/cast v1.5.0 // indirect
github.com/spf13/afero v1.9.5 // indirect
github.com/spf13/cast v1.5.1 // indirect
github.com/spf13/cobra v1.7.0 // indirect
github.com/spf13/jwalterweatherman v1.1.0 // indirect
github.com/spf13/pflag v1.0.5 // indirect
github.com/spf13/viper v1.13.0 // indirect
github.com/spf13/viper v1.16.0 // indirect
github.com/spiffe/go-spiffe/v2 v2.1.1 // indirect
github.com/stripe/stripe-go/v74 v74.28.0 // indirect
github.com/subosito/gotenv v1.4.1 // indirect
github.com/stripe/stripe-go/v74 v74.8.0 // indirect
github.com/subosito/gotenv v1.4.2 // indirect
github.com/syndtr/goleveldb v1.0.1-0.20220721030215-126854af5e6d // indirect
github.com/tchap/go-patricia/v2 v2.3.1 // indirect
github.com/tent/canonical-json-go v0.0.0-20130607151641-96e4ba3a7613 // indirect
@@ -298,13 +295,13 @@ require (
github.com/theupdateframework/go-tuf v0.5.2-0.20220930112810-3890c1e7ace4 // indirect
github.com/titanous/rocacheck v0.0.0-20171023193734-afe73141d399 // indirect
github.com/tjfoc/gmsm v1.3.2 // indirect
github.com/tmc/grpc-websocket-proxy v0.0.0-20220101234140-673ab2c3ae75 // indirect
github.com/tmc/grpc-websocket-proxy v0.0.0-20201229170055-e5319fda7802 // indirect
github.com/transparency-dev/merkle v0.0.1 // indirect
github.com/uptrace/opentelemetry-go-extra/otelutil v0.2.2 // indirect
github.com/uptrace/opentelemetry-go-extra/otelzap v0.2.2 // indirect
github.com/uptrace/opentelemetry-go-extra/otelutil v0.2.1 // indirect
github.com/uptrace/opentelemetry-go-extra/otelzap v0.2.1 // indirect
github.com/uptrace/uptrace-go v1.16.0 // indirect
github.com/urfave/cli v1.22.7 // indirect
github.com/vbatts/tar-split v0.11.2 // indirect
github.com/urfave/cli v1.22.12 // indirect
github.com/vbatts/tar-split v0.11.3 // indirect
github.com/whilp/git-urls v1.0.0 // indirect
github.com/xanzy/go-gitlab v0.73.1 // indirect
github.com/xanzy/ssh-agent v0.3.3 // indirect
@@ -313,9 +310,9 @@ require (
github.com/xeipuuv/gojsonschema v1.2.0 // indirect
github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2 // indirect
github.com/xlab/treeprint v1.1.0 // indirect
github.com/yashtewari/glob-intersection v0.2.0 // indirect
github.com/yashtewari/glob-intersection v0.1.0 // indirect
github.com/zeebo/errs v1.2.2 // indirect
go.etcd.io/bbolt v1.3.6 // indirect
go.etcd.io/bbolt v1.3.7 // indirect
go.etcd.io/etcd/api/v3 v3.6.0-alpha.0 // indirect
go.etcd.io/etcd/client/pkg/v3 v3.6.0-alpha.0 // indirect
go.etcd.io/etcd/client/v2 v2.306.0-alpha.0 // indirect
@@ -329,7 +326,7 @@ require (
go.etcd.io/etcd/v3 v3.6.0-alpha.0 // indirect
go.mongodb.org/mongo-driver v1.11.4 // indirect
go.opencensus.io v0.24.0 // indirect
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.35.0 // indirect
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.40.0 // indirect
go.opentelemetry.io/contrib/instrumentation/runtime v0.42.0 // indirect
go.opentelemetry.io/otel/exporters/otlp/internal/retry v1.16.0 // indirect
go.opentelemetry.io/otel/exporters/otlp/otlpmetric v0.39.0 // indirect
@@ -342,18 +339,16 @@ require (
go.opentelemetry.io/otel/sdk/metric v0.39.0 // indirect
go.opentelemetry.io/proto/otlp v0.19.0 // indirect
go.starlark.net v0.0.0-20200306205701-8dd3e2ee1dd5 // indirect
golang.org/x/sync v0.3.0 // indirect
golang.org/x/sys v0.10.0 // indirect
golang.org/x/term v0.10.0 // indirect
golang.org/x/text v0.11.0 // indirect
golang.org/x/sync v0.1.0 // indirect
golang.org/x/sys v0.9.0 // indirect
golang.org/x/term v0.9.0 // indirect
golang.org/x/text v0.10.0 // indirect
golang.org/x/time v0.3.0 // indirect
golang.org/x/tools v0.9.1 // indirect
golang.org/x/tools v0.8.0 // indirect
golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2 // indirect
gonum.org/v1/gonum v0.9.1 // indirect
google.golang.org/api v0.126.0 // indirect
google.golang.org/genproto/googleapis/api v0.0.0-20230530153820-e85fd2cbaebc // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20230530153820-e85fd2cbaebc // indirect
google.golang.org/protobuf v1.31.0 // indirect
google.golang.org/api v0.122.0 // indirect
google.golang.org/protobuf v1.30.0 // indirect
gopkg.in/cheggaaa/pb.v1 v1.0.28 // indirect
gopkg.in/inf.v0 v0.9.1 // indirect
gopkg.in/ini.v1 v1.67.0 // indirect
@@ -364,21 +359,26 @@ require (
gopkg.in/yaml.v2 v2.4.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
helm.sh/helm/v3 v3.11.1 // indirect
k8s.io/api v0.27.4 // indirect
k8s.io/apiextensions-apiserver v0.27.2 // indirect
k8s.io/apimachinery v0.27.4 // indirect
k8s.io/client-go v0.27.4 // indirect
k8s.io/klog/v2 v2.100.1 // indirect
k8s.io/kube-openapi v0.0.0-20230501164219-8b0f38b5fd1f // indirect
sigs.k8s.io/controller-runtime v0.15.0 // indirect
sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd // indirect
k8s.io/api v0.26.2 // indirect
k8s.io/apiextensions-apiserver v0.26.0 // indirect
k8s.io/apimachinery v0.26.2 // indirect
k8s.io/client-go v0.26.2 // indirect
k8s.io/klog/v2 v2.90.1 // indirect
k8s.io/kube-openapi v0.0.0-20221012153701-172d655c2280 // indirect
sigs.k8s.io/controller-runtime v0.12.3 // indirect
sigs.k8s.io/json v0.0.0-20220713155537-f223a00ba0e2 // indirect
sigs.k8s.io/kustomize/api v0.12.1 // indirect
sigs.k8s.io/kustomize/kyaml v0.13.9 // indirect
sigs.k8s.io/release-utils v0.7.3 // indirect
sigs.k8s.io/structured-merge-diff/v4 v4.3.0 // indirect
sigs.k8s.io/structured-merge-diff/v4 v4.2.3 // indirect
sigs.k8s.io/yaml v1.3.0 // indirect
)
replace github.com/libgit2/git2go/v33 => ../git2go
replace google.golang.org/grpc => google.golang.org/grpc v1.54.0
replace (
// TODO(vladklokun): Since later versions (e.g. v0.40.0) that get used without the pin introduce weird packaging issues probably due to package renames, pin to last known good.
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc => go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.35.0
google.golang.org/grpc => google.golang.org/grpc v1.54.0
)

View File

@@ -41,7 +41,7 @@ cloud.google.com/go v0.102.1/go.mod h1:XZ77E9qnTEnrgEOvr4xzfdX5TRo7fB4T2F4O6+34h
cloud.google.com/go v0.104.0/go.mod h1:OO6xxXdJyvuJPcEPBLN9BJPD+jep5G1+2U5B5gkRYtA=
cloud.google.com/go v0.105.0/go.mod h1:PrLgOJNe5nfE9UMxKxgXj4mD3voiP+YQ6gdt6KMFOKM=
cloud.google.com/go v0.107.0/go.mod h1:wpc2eNrD7hXUTy8EKS10jkxpZBjASrORK7goS+3YX2I=
cloud.google.com/go v0.110.2 h1:sdFPBr6xG9/wkBbfhmUz/JmZC7X6LavQgcrVINrKiVA=
cloud.google.com/go v0.110.0 h1:Zc8gqp3+a9/Eyph2KDmcGaPtbKRIoqq4YTlL4NMD0Ys=
cloud.google.com/go/accessapproval v1.4.0/go.mod h1:zybIuC3KpDOvotz59lFe5qxRZx6C75OtwbisN56xYB4=
cloud.google.com/go/accessapproval v1.5.0/go.mod h1:HFy3tuiGvMdcd/u+Cu5b9NkO1pEICJ46IR82PoUdplw=
cloud.google.com/go/accesscontextmanager v1.3.0/go.mod h1:TgCBehyr5gNMz7ZaH9xubp+CE8dkrszb4oK9CWyvD4o=
@@ -125,8 +125,8 @@ cloud.google.com/go/compute v1.12.1/go.mod h1:e8yNOBcBONZU1vJKCvCoDw/4JQsA0dpM4x
cloud.google.com/go/compute v1.13.0/go.mod h1:5aPTS0cUNMIc1CE546K+Th6weJUNQErARyZtRXDJ8GE=
cloud.google.com/go/compute v1.14.0/go.mod h1:YfLtxrj9sU4Yxv+sXzZkyPjEyPBZfXHUvjxega5vAdo=
cloud.google.com/go/compute v1.15.1/go.mod h1:bjjoF/NtFUrkD/urWfdHaKuOPDR5nWIs63rR+SXhcpA=
cloud.google.com/go/compute v1.20.1 h1:6aKEtlUiwEpJzM001l0yFkpXmUVXaN8W+fbkb2AZNbg=
cloud.google.com/go/compute v1.20.1/go.mod h1:4tCnrn48xsqlwSAiLf1HXMQk8CONslYbdiEZc9FEIbM=
cloud.google.com/go/compute v1.19.1 h1:am86mquDUgjGNWxiGn+5PGLbmgiWXlE/yNWpIpNvuXY=
cloud.google.com/go/compute v1.19.1/go.mod h1:6ylj3a05WF8leseCdIf77NK0g1ey+nj5IKd5/kvShxE=
cloud.google.com/go/compute/metadata v0.1.0/go.mod h1:Z1VN+bulIf6bt4P/C37K4DyZYZEXYonfTBHHFPO/4UU=
cloud.google.com/go/compute/metadata v0.2.0/go.mod h1:zFmK7XCadkQkj6TtorcaGlCW1hT1fIilQDwofLpJ20k=
cloud.google.com/go/compute/metadata v0.2.1/go.mod h1:jgHgmJd2RKBGzXqF5LR2EZMGxBkeanZ9wwa75XHJgOM=
@@ -136,8 +136,8 @@ cloud.google.com/go/contactcenterinsights v1.3.0/go.mod h1:Eu2oemoePuEFc/xKFPjbT
cloud.google.com/go/contactcenterinsights v1.4.0/go.mod h1:L2YzkGbPsv+vMQMCADxJoT9YiTTnSEd6fEvCeHTYVck=
cloud.google.com/go/container v1.6.0/go.mod h1:Xazp7GjJSeUYo688S+6J5V+n/t+G5sKBTFkKNudGRxg=
cloud.google.com/go/container v1.7.0/go.mod h1:Dp5AHtmothHGX3DwwIHPgq45Y8KmNsgN3amoYfxVkLo=
cloud.google.com/go/container v1.24.0 h1:N51t/cgQJFqDD/W7Mb+IvmAPHrf8AbPx7Bb7aF4lROE=
cloud.google.com/go/container v1.24.0/go.mod h1:lTNExE2R7f+DLbAN+rJiKTisauFCaoDq6NURZ83eVH4=
cloud.google.com/go/container v1.15.0 h1:NKlY/wCDapfVZlbVVaeuu2UZZED5Dy1z4Zx1KhEzm8c=
cloud.google.com/go/container v1.15.0/go.mod h1:ft+9S0WGjAyjDggg5S06DXj+fHJICWg8L7isCQe9pQA=
cloud.google.com/go/containeranalysis v0.5.1/go.mod h1:1D92jd8gRR/c0fGMlymRgxWD3Qw9C1ff6/T7mLgVL8I=
cloud.google.com/go/containeranalysis v0.6.0/go.mod h1:HEJoiEIu+lEXM+k7+qLCci0h33lX3ZqoYFdmPcoO7s4=
cloud.google.com/go/containeranalysis v0.9.0 h1:EQ4FFxNaEAg8PqQCO7bVQfWz9NVwZCUKaM1b3ycfx3U=
@@ -243,6 +243,7 @@ cloud.google.com/go/lifesciences v0.6.0/go.mod h1:ddj6tSX/7BOnhxCSd3ZcETvtNr8NZ6
cloud.google.com/go/logging v1.6.1/go.mod h1:5ZO0mHHbvm8gEmeEUHrmDlTDSu5imF6MUP9OfilNXBw=
cloud.google.com/go/longrunning v0.1.1/go.mod h1:UUFxuDWkv22EuY93jjmDMFT5GPQKeFVJBIF6QlTqdsE=
cloud.google.com/go/longrunning v0.3.0/go.mod h1:qth9Y41RRSUE69rDcOn6DdK3HfQfsUI0YSmW3iIlLJc=
cloud.google.com/go/longrunning v0.4.1 h1:v+yFJOfKC3yZdY6ZUI933pIYdhyhV8S3NpWrXWmg7jM=
cloud.google.com/go/managedidentities v1.3.0/go.mod h1:UzlW3cBOiPrzucO5qWkNkh0w33KFtBJU281hacNvsdE=
cloud.google.com/go/managedidentities v1.4.0/go.mod h1:NWSBYbEMgqmbZsLIyKvxrYbtqOsxY1ZrGM+9RgDqInM=
cloud.google.com/go/maps v0.1.0/go.mod h1:BQM97WGyfw9FWEmQMpZ5T6cpovXXSd1cGmFma94eubI=
@@ -436,20 +437,18 @@ github.com/Azure/azure-sdk-for-go v30.1.0+incompatible/go.mod h1:9XXNKU+eRnpl9mo
github.com/Azure/azure-sdk-for-go v46.4.0+incompatible/go.mod h1:9XXNKU+eRnpl9moKnB4QOLf1HestfXbmab5FXxiDBjc=
github.com/Azure/azure-sdk-for-go v66.0.0+incompatible h1:bmmC38SlE8/E81nNADlgmVGurPWMHDX2YNXVQMrBpEE=
github.com/Azure/azure-sdk-for-go v66.0.0+incompatible/go.mod h1:9XXNKU+eRnpl9moKnB4QOLf1HestfXbmab5FXxiDBjc=
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.6.0 h1:8kDqDngH+DmVBiCtIjCFTGa7MBnsIOkF9IccInFEbjk=
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.6.0/go.mod h1:bjGvMhVMb+EEm3VRNQawDMUyMMjo+S5ewNjflkep/0Q=
github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.3.0 h1:vcYCAze6p19qBW7MhZybIsqD8sMV8js0NyQM8JDnVtg=
github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.3.0/go.mod h1:OQeznEEkTZ9OrhHJoDD8ZDq51FHgXjqtP9z6bEwBq9U=
github.com/Azure/azure-sdk-for-go/sdk/internal v1.3.0 h1:sXr+ck84g/ZlZUOZiNELInmMgOsuGwdjjVkEIde0OtY=
github.com/Azure/azure-sdk-for-go/sdk/internal v1.3.0/go.mod h1:okt5dMMTOFjX/aovMlrjvvXoPMBVSPzk9185BT0+eZM=
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.1.0 h1:Ut0ZGdOwJDw0npYEg+TLlPls3Pq6JiZaP2/aGKir7Zw=
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.1.0/go.mod h1:uGG2W01BaETf0Ozp+QxxKJdMBNRWPdstHG0Fmdwn1/U=
github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.2.0 h1:t/W5MYAuQy81cvM8VUNfRLzhtKpXhVUAN7Cd7KVbTyc=
github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.2.0/go.mod h1:NBanQUfSWiWn3QEpWDTCU0IjBECKOYvl2R8xdRtMtiM=
github.com/Azure/azure-sdk-for-go/sdk/internal v1.0.0 h1:jp0dGvZ7ZK0mgqnTSClMxa5xuRL7NZgHameVYF6BurY=
github.com/Azure/azure-sdk-for-go/sdk/internal v1.0.0/go.mod h1:eWRD7oawr1Mu1sLCawqVc0CUiF43ia3qQMxLscsKQ9w=
github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/authorization/armauthorization v1.0.0 h1:qtRcg5Y7jNJ4jEzPq4GpWLfTspHdNe2ZK6LjwGcjgmU=
github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/authorization/armauthorization v1.0.0/go.mod h1:lPneRe3TwsoDRKY4O6YDLXHhEWrD+TIRa8XrV/3/fqw=
github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/authorization/armauthorization/v2 v2.1.1 h1:6A4M8smF+y8nM/DYsLNQz9n7n2ZGaEVqfz8ZWQirQkI=
github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/authorization/armauthorization/v2 v2.1.1/go.mod h1:WqyxV5S0VtXD2+2d6oPqOvyhGubCvzLCKSAKgQ004Uk=
github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/containerservice/armcontainerservice/v2 v2.4.0 h1:1u/K2BFv0MwkG6he8RYuUcbbeK22rkoZbg4lKa/msZU=
github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/containerservice/armcontainerservice/v2 v2.4.0/go.mod h1:U5gpsREQZE6SLk1t/cFfc1eMhYAlYpEzvaYXuDfefy8=
github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/internal v1.1.2 h1:mLY+pNLjCUeKhgnAJWAKhEUQM+RJQo2H1fuGSw1Ky1E=
github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/resources/armresources v1.0.0 h1:ECsQtyERDVz3NP3kvDOTLvbQhqWp/x9EsGKtb4ogUr8=
github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/authorization/armauthorization/v2 v2.0.0 h1:WJd2y/3vp3sgG1u1KfDaEyGiM9oC11cBa9rbmsSv5rQ=
github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/authorization/armauthorization/v2 v2.0.0/go.mod h1:XlGHa0e9Mg7RNOshDEuc0HptPdtN/SI0HCu+02rdnOA=
github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/containerservice/armcontainerservice/v2 v2.2.0 h1:3L+gX5ssCABAToH0VQ64/oNz7rr+ShW+2sB+sonzIlY=
github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/containerservice/armcontainerservice/v2 v2.2.0/go.mod h1:4gUds0dEPFIld6DwHfbo0cLBljyIyI5E5ciPb5MLi3Q=
github.com/Azure/azure-service-bus-go v0.9.1/go.mod h1:yzBx6/BUGfjfeqbRZny9AQIbIe3AcV9WZbAdpkoXOa0=
github.com/Azure/azure-storage-blob-go v0.8.0/go.mod h1:lPI3aLPpuLTeUwh1sViKXFxwl2B6teiRqI0deQUvsw0=
github.com/Azure/go-autorest v12.0.0+incompatible/go.mod h1:r+4oMnoxhatjLLJ6zxSWATqVooLgysK6ZNox3g/xq24=
@@ -483,8 +482,8 @@ github.com/Azure/go-autorest/logger v0.2.1 h1:IG7i4p/mDa2Ce4TRyAO8IHnVhAVF3RFU+Z
github.com/Azure/go-autorest/logger v0.2.1/go.mod h1:T9E3cAhj2VqvPOtCYAvby9aBXkZmbF5NWuPV8+WeEW8=
github.com/Azure/go-autorest/tracing v0.6.0 h1:TYi4+3m5t6K48TGI9AUdb+IzbnSxvnvUMfuitfgcfuo=
github.com/Azure/go-autorest/tracing v0.6.0/go.mod h1:+vhtPC754Xsa23ID7GlGsrdKBpUA79WCAKPPZVC2DeU=
github.com/AzureAD/microsoft-authentication-library-for-go v1.0.0 h1:OBhqkivkhkMqLPymWEppkm7vgPQY2XsHoEkaMQ0AdZY=
github.com/AzureAD/microsoft-authentication-library-for-go v1.0.0/go.mod h1:kgDmCTgBzIEPFElEF+FK0SdjAor06dRq2Go927dnQ6o=
github.com/AzureAD/microsoft-authentication-library-for-go v0.7.0 h1:VgSJlZH5u0k2qxSpqyghcFQKmvYckj46uymKK5XzkBM=
github.com/AzureAD/microsoft-authentication-library-for-go v0.7.0/go.mod h1:BDJ5qMFKx9DugEg3+uQSDCdbYPr5s9vBTrL9P8TpqOU=
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
github.com/BurntSushi/toml v1.2.1 h1:9F2/+DoOYIOksmaJFPw1tGFy1eDnIJXg+UHjuD8lTak=
github.com/BurntSushi/toml v1.2.1/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ=
@@ -505,13 +504,13 @@ github.com/Masterminds/sprig v2.22.0+incompatible/go.mod h1:y6hNFY5UBTIWBxnzTeuN
github.com/Masterminds/sprig/v3 v3.2.3 h1:eL2fZNezLomi0uOLqjQoN6BfsDD+fyLtgbJMAj9n6YA=
github.com/Masterminds/sprig/v3 v3.2.3/go.mod h1:rXcFaZ2zZbLRJv/xSysmlgIM1u11eBaRMhvYXJNkGuM=
github.com/Microsoft/go-winio v0.5.2/go.mod h1:WpS1mjBmmwHBEWmogvA2mj8546UReBk4v8QkMxJ6pZY=
github.com/Microsoft/go-winio v0.6.0 h1:slsWYD/zyx7lCXoZVlvQrj0hPTM1HI4+v1sIda2yDvg=
github.com/Microsoft/go-winio v0.6.0/go.mod h1:cTAf44im0RAYeL23bpB+fzCyDH2MJiz2BO69KH/soAE=
github.com/Microsoft/go-winio v0.6.1 h1:9/kr64B9VUZrLm5YYwbGtUJnMgqWVOdUAXu6Migciow=
github.com/Microsoft/go-winio v0.6.1/go.mod h1:LRdKpFKfdobln8UmuiYcKPot9D2v6svN5+sAH+4kjUM=
github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU=
github.com/OneOfOne/xxhash v1.2.8 h1:31czK/TI9sNkxIKfaUfGlU47BAxQ0ztGgd9vPyqimf8=
github.com/OneOfOne/xxhash v1.2.8/go.mod h1:eZbhyaAYD41SGSSsnmcpxVoRiQ/MPUTjUdIIOT9Um7Q=
github.com/ProtonMail/go-crypto v0.0.0-20221026131551-cf6655e29de4 h1:ra2OtmuW0AE5csawV4YXMNGNQQXvLRps3z2Z59OPO+I=
github.com/ProtonMail/go-crypto v0.0.0-20221026131551-cf6655e29de4/go.mod h1:UBYPn8k0D56RtnR8RFQMjmh4KrZzWJ5o7Z9SYjossQ8=
github.com/ProtonMail/go-crypto v0.0.0-20230518184743-7afd39499903 h1:ZK3C5DtzV2nVAQTx5S5jQvMeDqWtD1By5mOoyY/xJek=
github.com/ProtonMail/go-crypto v0.0.0-20230518184743-7afd39499903/go.mod h1:8TI4H3IbrackdNgv+92dI+rhpCaLqM0IfpgCgenFvRE=
github.com/PuerkitoBio/purell v1.1.1/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0=
github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE=
github.com/Shopify/sarama v1.19.0/go.mod h1:FVkBWblsNy7DGZRfXLU0O9RCGt5g3g3yEuWXgklEdEo=
@@ -521,8 +520,8 @@ github.com/ThalesIgnite/crypto11 v1.2.5/go.mod h1:ILDKtnCKiQ7zRoNxcp36Y1ZR8LBPmR
github.com/VividCortex/gohistogram v1.0.0/go.mod h1:Pf5mBqqDxYaXu3hDrrU+w6nw50o/4+TcAqDqk/vUH7g=
github.com/a8m/envsubst v1.3.0 h1:GmXKmVssap0YtlU3E230W98RWtWCyIZzjtf1apWWyAg=
github.com/a8m/envsubst v1.3.0/go.mod h1:MVUTQNGQ3tsjOOtKCNd+fl8RzhsXcDvvAEzkhGtlsbY=
github.com/acomagu/bufpipe v1.0.3 h1:fxAGrHZTgQ9w5QqVItgzwj235/uYZYgbXitB+dLupOk=
github.com/acomagu/bufpipe v1.0.3/go.mod h1:mxdxdup/WdsKVreO5GpW4+M/1CE2sMG4jeGJ2sYmHc4=
github.com/acomagu/bufpipe v1.0.4 h1:e3H4WUzM3npvo5uv95QuJM3cQspFNtFBzvJ2oNjKIDQ=
github.com/acomagu/bufpipe v1.0.4/go.mod h1:mxdxdup/WdsKVreO5GpW4+M/1CE2sMG4jeGJ2sYmHc4=
github.com/afex/hystrix-go v0.0.0-20180502004556-fa1af6a1f4f5/go.mod h1:SkGFH1ia65gfNATL8TAiHDNxPzPdmEL5uirI2Uyuz6c=
github.com/agnivade/levenshtein v1.1.1 h1:QY8M92nrzkmr798gCo3kmMyqXFzdQVpxLlGPRBij0P8=
github.com/agnivade/levenshtein v1.1.1/go.mod h1:veldBMzWxcCG2ZvUTKD2kJNRdCk5hVbJomOvKkmgYbo=
@@ -578,7 +577,6 @@ github.com/aliyun/credentials-go v1.2.3 h1:Vmodnr52Rz1mcbwn0kzMhLRKb6soizewuKXdf
github.com/aliyun/credentials-go v1.2.3/go.mod h1:/KowD1cfGSLrLsH28Jr8W+xwoId0ywIy5lNzDz6O1vw=
github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239/go.mod h1:2FmKhYUyUczH0OGQWaF5ceTx0UBShxjsH6f8oGKYe2c=
github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be h1:9AeTilPcZAjCFIImctFaOjnTIavg87rW78vTPkQqLI8=
github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be/go.mod h1:ySMOLuWl6zY27l47sB3qLNK6tF2fkHG55UZxx8oIVo4=
github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY=
github.com/aokoli/goutils v1.0.1/go.mod h1:SijmP0QR8LtwsmDs8Yii5Z/S4trXFGFC2oO5g9DP+DQ=
github.com/apache/beam v2.28.0+incompatible/go.mod h1:/8NX3Qi8vGstDLLaeaU7+lzVEu/ACaQhYjeefzQ0y1o=
@@ -600,14 +598,12 @@ github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj
github.com/armon/go-radix v1.0.0 h1:F4z6KzEeeQIMeLFa97iZU6vupzoecKdU5TX24SNppXI=
github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPdPJAN/hZIm0C4OItdklCFmMRWYpio=
github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs=
github.com/armosec/armoapi-go v0.0.212 h1:QdmCywiA8NRhyynzHJkIYYZBMBb+1pySUhN3jm4b3Fw=
github.com/armosec/armoapi-go v0.0.212/go.mod h1:4AEdwBrbS1YCAn/lZzV+cOOR9BPa0MTHYHiJDlR1uRQ=
github.com/armosec/gojay v1.2.15 h1:sSB2vnAvacUNkw9nzUYZKcPzhJOyk6/5LK2JCNdmoZY=
github.com/armosec/gojay v1.2.15/go.mod h1:vzVAaay2TWJAngOpxu8aqLbye9jMgoKleuAOK+xsOts=
github.com/armosec/utils-go v0.0.20 h1:bvr+TMumEYdMsGFGSsaQysST7K02nNROFvuajNuKPlw=
github.com/armosec/utils-go v0.0.20/go.mod h1:ZEFiSv8KpTFNT19jHis1IengiF/BGDvg7tHmXo+cwxs=
github.com/armosec/utils-k8s-go v0.0.16 h1:h46PoxAb4OHA2p719PzcAS03lADw4lH4TyRMaZ3ix/g=
github.com/armosec/utils-k8s-go v0.0.16/go.mod h1:QX0QAGlH7KCZq810eO9QjTYqkhjw8cvrr96TZfaUGrk=
github.com/armosec/armoapi-go v0.0.202 h1:0PM89laC4rplz4eSELJ5ueVxxDIuEfIlDwlGa/c0pkg=
github.com/armosec/armoapi-go v0.0.202/go.mod h1:Fq2xtueM2ha0VK1b/PcbtbkAzHUgjqMz4MEdabNpdwY=
github.com/armosec/utils-go v0.0.14 h1:Q6HGxOyc5aPObgUM2FQpkYGXjj7/LSrUPkppFJGTexU=
github.com/armosec/utils-go v0.0.14/go.mod h1:F/K1mI/qcj7fNuJl7xktoCeHM83azOF0Zq6eC2WuPyU=
github.com/armosec/utils-k8s-go v0.0.13 h1:MzrRotrtZjpz4Yq1VRGbxDOfd6b5qRqZupzLnpj+W1A=
github.com/armosec/utils-k8s-go v0.0.13/go.mod h1:rPHiOaHefWa9ujspwvYYAp0uEbqGGyAMiNrFa/Gpp/8=
github.com/aryann/difflib v0.0.0-20170710044230-e206f873d14a/go.mod h1:DAHtR1m6lCRdSC2Tm3DSWRPvIPr6xNKyeHdqDQSQT+A=
github.com/asaskevich/govalidator v0.0.0-20200907205600-7a23bdc65eef/go.mod h1:WaHUgvxTVq04UNunO+XhnAqY/wQc+bxr74GqbsZ/Jqw=
github.com/asaskevich/govalidator v0.0.0-20210307081110-f21760c49a8d h1:Byv0BzEl3/e6D5CLfI0j/7hiIEtvGVFPCZ7Ei2oq8iQ=
@@ -621,62 +617,67 @@ github.com/aws/aws-sdk-go v1.23.20/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpi
github.com/aws/aws-sdk-go v1.25.11/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo=
github.com/aws/aws-sdk-go v1.27.0/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo=
github.com/aws/aws-sdk-go v1.37.0/go.mod h1:hcU610XS61/+aQV88ixoOzUoG7v3b31pl2zKMmprdro=
github.com/aws/aws-sdk-go v1.44.312 h1:llrElfzeqG/YOLFFKjg1xNpZCFJ2xraIi3PqSuP+95k=
github.com/aws/aws-sdk-go v1.44.312/go.mod h1:aVsgQcEevwlmQ7qHE9I3h+dtQgpqhFB+i8Phjh7fkwI=
github.com/aws/aws-sdk-go v1.44.180 h1:VLZuAHI9fa/3WME5JjpVjcPCNfpGHVMiHx8sLHWhMgI=
github.com/aws/aws-sdk-go v1.44.180/go.mod h1:aVsgQcEevwlmQ7qHE9I3h+dtQgpqhFB+i8Phjh7fkwI=
github.com/aws/aws-sdk-go-v2 v0.18.0/go.mod h1:JWVYvqSMppoMJC0x5wdwiImzgXTI9FuZwxzkQq9wy+g=
github.com/aws/aws-sdk-go-v2 v1.7.1/go.mod h1:L5LuPC1ZgDr2xQS7AmIec/Jlc7O/Y1u2KxJyNVab250=
github.com/aws/aws-sdk-go-v2 v1.14.0/go.mod h1:ZA3Y8V0LrlWj63MQAnRHgKf/5QB//LSZCPNWlWrNGLU=
github.com/aws/aws-sdk-go-v2 v1.16.7/go.mod h1:6CpKuLXg2w7If3ABZCl/qZ6rEgwtjZTn4eAf4RcEyuw=
github.com/aws/aws-sdk-go-v2 v1.16.16/go.mod h1:SwiyXi/1zTUZ6KIAmLK5V5ll8SiURNUYOqTerZPaF9k=
github.com/aws/aws-sdk-go-v2 v1.17.3 h1:shN7NlnVzvDUgPQ+1rLMSxY8OWRNDRYtiqe0p/PgrhY=
github.com/aws/aws-sdk-go-v2 v1.17.3/go.mod h1:uzbQtefpm44goOPmdKyAlXSNcwlRgF3ePWVW6EtJvvw=
github.com/aws/aws-sdk-go-v2 v1.19.1 h1:STs0lbbpXu3byTPcnRLghs2DH0yk9qKDo27TyyJSKsM=
github.com/aws/aws-sdk-go-v2 v1.19.1/go.mod h1:uzbQtefpm44goOPmdKyAlXSNcwlRgF3ePWVW6EtJvvw=
github.com/aws/aws-sdk-go-v2/config v1.5.0/go.mod h1:RWlPOAW3E3tbtNAqTwvSW54Of/yP3oiZXMI0xfUdjyA=
github.com/aws/aws-sdk-go-v2/config v1.18.30 h1:TTAXQIn31qYFUQjkW6siVrRTX1ux+sADZDOe3jsZcMg=
github.com/aws/aws-sdk-go-v2/config v1.18.30/go.mod h1:+YogjT7e/t9JVu/sOnZZgxTge1G+bPNk8zOaI0QIQvE=
github.com/aws/aws-sdk-go-v2/config v1.17.8 h1:b9LGqNnOdg9vR4Q43tBTVWk4J6F+W774MSchvKJsqnE=
github.com/aws/aws-sdk-go-v2/config v1.17.8/go.mod h1:UkCI3kb0sCdvtjiXYiU4Zx5h07BOpgBTtkPu/49r+kA=
github.com/aws/aws-sdk-go-v2/credentials v1.3.1/go.mod h1:r0n73xwsIVagq8RsxmZbGSRQFj9As3je72C2WzUIToc=
github.com/aws/aws-sdk-go-v2/credentials v1.13.29 h1:KNgCpThGuZyCjq9EuuqoLDenKKMwO/x1Xx01ckDa7VI=
github.com/aws/aws-sdk-go-v2/credentials v1.13.29/go.mod h1:VMq1LcmSEa9qxBlOCYTjVuGJWEEzhGmgL552jQsmhss=
github.com/aws/aws-sdk-go-v2/credentials v1.12.21 h1:4tjlyCD0hRGNQivh5dN8hbP30qQhMLBE/FgQR1vHHWM=
github.com/aws/aws-sdk-go-v2/credentials v1.12.21/go.mod h1:O+4XyAt4e+oBAoIwNUYkRg3CVMscaIJdmZBOcPgJ8D8=
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.3.0/go.mod h1:2LAuqPx1I6jNfaGDucWfA2zqQCYCOMCDHiCOciALyNw=
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.13.6 h1:kortK122LvTU34CGX/F9oJpelXKkEA2j/MW48II+8+8=
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.13.6/go.mod h1:k7IPHyHNIASI0m0RwOmCjWOTtgG+J0raqwuHH8WhWJE=
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.12.17 h1:r08j4sbZu/RVi+BNxkBJwPMUYY3P8mgSDuKkZ/ZN1lE=
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.12.17/go.mod h1:yIkQcCDYNsZfXpd5UX2Cy+sWA1jPgIhGTw9cOBzfVnQ=
github.com/aws/aws-sdk-go-v2/internal/configsources v1.1.5/go.mod h1:2hXc8ooJqF2nAznsbJQIn+7h851/bu8GVC80OVTTqf8=
github.com/aws/aws-sdk-go-v2/internal/configsources v1.1.14/go.mod h1:kdjrMwHwrC3+FsKhNcCMJ7tUVj/8uSD5CZXeQ4wV6fM=
github.com/aws/aws-sdk-go-v2/internal/configsources v1.1.23/go.mod h1:2DFxAQ9pfIRy0imBCJv+vZ2X6RKxves6fbnEuSry6b4=
github.com/aws/aws-sdk-go-v2/internal/configsources v1.1.27 h1:I3cakv2Uy1vNmmhRQmFptYDxOvBnwCdNwyw63N0RaRU=
github.com/aws/aws-sdk-go-v2/internal/configsources v1.1.27/go.mod h1:a1/UpzeyBBerajpnP5nGZa9mGzsBn5cOKxm6NWQsvoI=
github.com/aws/aws-sdk-go-v2/internal/configsources v1.1.36 h1:kbk81RlPoC6e4co7cQx2FAvH9TgbzxIqCqiosAFiB+w=
github.com/aws/aws-sdk-go-v2/internal/configsources v1.1.36/go.mod h1:T8Jsn/uNL/AFOXrVYQ1YQaN1r9gN34JU1855/Lyjv+o=
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.3.0/go.mod h1:miRSv9l093jX/t/j+mBCaLqFHo9xKYzJ7DGm1BsGoJM=
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.4.8/go.mod h1:ZIV8GYoC6WLBW5KGs+o4rsc65/ozd+eQ0L31XF5VDwk=
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.4.17/go.mod h1:pRwaTYCJemADaqCbUAxltMoHKata7hmB5PjEXeu0kfg=
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.4.21 h1:5NbbMrIzmUn/TXFqAle6mgrH5m9cOvMLRGL7pnG8tRE=
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.4.21/go.mod h1:+Gxn8jYn5k9ebfHEqlhrMirFjSW0v0C9fI+KN5vk2kE=
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.4.30 h1:lMl8S5SB8jNCB+Sty2Em4lnu3IJytceHQd7qbmfqKL0=
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.4.30/go.mod h1:v3GSCnFxbHzt9dlWBqvA1K1f9lmWuf4ztupZBCAIVs4=
github.com/aws/aws-sdk-go-v2/internal/ini v1.1.1/go.mod h1:Zy8smImhTdOETZqfyn01iNOe0CNggVbPjCajyaz6Gvg=
github.com/aws/aws-sdk-go-v2/internal/ini v1.3.37 h1:BXiqvN7WuV/pMhz8CivhO8cG8icJcjnjHumif4ukQ0c=
github.com/aws/aws-sdk-go-v2/internal/ini v1.3.37/go.mod h1:d4GZ62cjnz/hjKFdAu11gAwK73bdhqaFv2O4J1gaqIs=
github.com/aws/aws-sdk-go-v2/internal/ini v1.3.24 h1:wj5Rwc05hvUSvKuOF29IYb9QrCLjU+rHAy/x/o0DK2c=
github.com/aws/aws-sdk-go-v2/internal/ini v1.3.24/go.mod h1:jULHjqqjDlbyTa7pfM7WICATnOv+iOhjletM3N0Xbu8=
github.com/aws/aws-sdk-go-v2/service/ecr v1.4.1/go.mod h1:FglZcyeiBqcbvyinl+n14aT/EWC7S1MIH+Gan2iizt0=
github.com/aws/aws-sdk-go-v2/service/ecr v1.18.0 h1:5RVanD+P+L2W9WU07/8J/A52vnQi7F3ClBdWQttgYlg=
github.com/aws/aws-sdk-go-v2/service/ecr v1.18.0/go.mod h1:9yGOFsa2OcdyePojE89xNGtdBusTyc8ocjpiuFtFc0g=
github.com/aws/aws-sdk-go-v2/service/ecrpublic v1.4.1/go.mod h1:eD5Eo4drVP2FLTw0G+SMIPWNWvQRGGTtIZR2XeAagoA=
github.com/aws/aws-sdk-go-v2/service/ecrpublic v1.12.0 h1:LsqBpyRofMG6eDs6YGud6FhdGyIyXelAasPOZ6wWLro=
github.com/aws/aws-sdk-go-v2/service/ecrpublic v1.12.0/go.mod h1:IArQ3IBR00FkuraKwudKZZU32OxJfdTdwV+W5iZh3Y4=
github.com/aws/aws-sdk-go-v2/service/eks v1.28.1 h1:SA+98Rnehl2KXewvGXc2Lw2ns3Y4t9jdMHmEY5hcNws=
github.com/aws/aws-sdk-go-v2/service/eks v1.28.1/go.mod h1:cQRkgJKg6s9AIzFZ+i4pXdm+/3Fw4MuPNqCdMvSaqns=
github.com/aws/aws-sdk-go-v2/service/eks v1.21.4 h1:qmKWieiIiYwD46GRD6nxFc1KsyR0ChGRid8emb7rDEY=
github.com/aws/aws-sdk-go-v2/service/eks v1.21.4/go.mod h1:Th2+t6mwi0bZayXUOFOTuyWR2nwRUVcadDy4WGE8C2E=
github.com/aws/aws-sdk-go-v2/service/iam v1.19.0 h1:9vCynoqC+dgxZKrsjvAniyIopsv3RZFsZ6wkQ+yxtj8=
github.com/aws/aws-sdk-go-v2/service/iam v1.19.0/go.mod h1:OyAuvpFeSVNppcSsp1hFOVQcaTRc1LE24YIR7pMbbAA=
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.2.1/go.mod h1:zceowr5Z1Nh2WVP8bf/3ikB41IZW59E4yIYbg+pC6mw=
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.9.30 h1:UcVZxLVNY4yayCmiG94Ge3l2qbc5WEB/oa4RmjoQEi0=
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.9.30/go.mod h1:wPffyJiWWtHwvpFyn23WjAjVjMnlQOQrl02+vutBh3Y=
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.9.17 h1:Jrd/oMh0PKQc6+BowB+pLEwLIgaQF29eYbe7E1Av9Ug=
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.9.17/go.mod h1:4nYOrY41Lrbk2170/BGkcJKBhws9Pfn8MG3aGqjjeFI=
github.com/aws/aws-sdk-go-v2/service/kms v1.18.11 h1:IxfVvdMedvCHXOWIuypaCjmNqGOP1uaXnaSVQzut7KE=
github.com/aws/aws-sdk-go-v2/service/sso v1.3.1/go.mod h1:J3A3RGUvuCZjvSuZEcOpHDnzZP/sKbhDWV2T1EOzFIM=
github.com/aws/aws-sdk-go-v2/service/sso v1.12.14 h1:gUjz7trfz9qBm0AlkKTvJHBXELi1wvw+2LA9GfD2AsM=
github.com/aws/aws-sdk-go-v2/service/sso v1.12.14/go.mod h1:9kfRdJgLCbnyeqZ/DpaSwcgj9ZDYLfRpe8Sze+NrYfQ=
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.14.14 h1:8bEtxV5UT9ucdWGXfZ7CM3caQhSHGjWnTHt0OeF7m7s=
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.14.14/go.mod h1:nd9BG2UnexN2sDx/mk2Jd6pf3d2E61AiA8m8Fdvdx8Y=
github.com/aws/aws-sdk-go-v2/service/sso v1.11.23 h1:pwvCchFUEnlceKIgPUouBJwK81aCkQ8UDMORfeFtW10=
github.com/aws/aws-sdk-go-v2/service/sso v1.11.23/go.mod h1:/w0eg9IhFGjGyyncHIQrXtU8wvNsTJOP0R6PPj0wf80=
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.13.6 h1:OwhhKc1P9ElfWbMKPIbMMZBV6hzJlL2JKD76wNNVzgQ=
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.13.6/go.mod h1:csZuQY65DAdFBt1oIjO5hhBR49kQqop4+lcuCjf2arA=
github.com/aws/aws-sdk-go-v2/service/sts v1.6.0/go.mod h1:q7o0j7d7HrJk/vr9uUt3BVRASvcU7gYZB9PUgPiByXg=
github.com/aws/aws-sdk-go-v2/service/sts v1.20.1 h1:U7h9CPoyMfVoN5jUglB0LglCMP10AK4vMBsbsCKM8Yw=
github.com/aws/aws-sdk-go-v2/service/sts v1.20.1/go.mod h1:BUHusg4cOA1TFGegj7x8/eoWrbdHzJfoMrXcbMQAG0k=
github.com/aws/aws-sdk-go-v2/service/sts v1.16.19 h1:9pPi0PsFNAGILFfPCk8Y0iyEBGc6lu6OQ97U7hmdesg=
github.com/aws/aws-sdk-go-v2/service/sts v1.16.19/go.mod h1:h4J3oPZQbxLhzGnk+j9dfYHi5qIOVJ5kczZd658/ydM=
github.com/aws/smithy-go v1.6.0/go.mod h1:SObp3lf9smib00L/v3U2eAKG8FyQ7iLrJnQiAmR5n+E=
github.com/aws/smithy-go v1.11.0/go.mod h1:3xHYmszWVx2c0kIwQeEVf9uSm4fYZt67FBJnwub1bgM=
github.com/aws/smithy-go v1.12.0/go.mod h1:Tg+OJXh4MB2R/uN61Ko2f6hTZwB/ZYGOtib8J3gBHzA=
github.com/aws/smithy-go v1.13.3/go.mod h1:Tg+OJXh4MB2R/uN61Ko2f6hTZwB/ZYGOtib8J3gBHzA=
github.com/aws/smithy-go v1.13.5 h1:hgz0X/DX0dGqTYpGALqXJoRKRj5oQ7150i5FdTePzO8=
github.com/aws/smithy-go v1.13.5/go.mod h1:Tg+OJXh4MB2R/uN61Ko2f6hTZwB/ZYGOtib8J3gBHzA=
github.com/awslabs/amazon-ecr-credential-helper/ecr-login v0.0.0-20220228164355-396b2034c795 h1:IWeCJzU+IYaO2rVEBlGPTBfe90cmGXFTLdhUFlzDGsY=
github.com/awslabs/amazon-ecr-credential-helper/ecr-login v0.0.0-20220228164355-396b2034c795/go.mod h1:8vJsEZ4iRqG+Vx6pKhWK6U00qcj0KC37IsfszMkY6UE=
github.com/awslabs/amazon-ecr-credential-helper/ecr-login v0.0.0-20220517224237-e6f29200ae04 h1:p2I85zYI9z5/c/3Q0LiO3RtNXcmXHTtJfml/hV16zNg=
github.com/awslabs/amazon-ecr-credential-helper/ecr-login v0.0.0-20220517224237-e6f29200ae04/go.mod h1:Z+bXnIbhKJYSvxNwsNnwde7pDKxuqlEZCbUBoTwAqf0=
github.com/aybabtme/rgbterm v0.0.0-20170906152045-cc83f3b3ce59/go.mod h1:q/89r3U2H7sSsE2t6Kca0lfwTK8JdoNGS/yzM/4iH5I=
github.com/beevik/etree v1.1.0/go.mod h1:r8Aw8JqVegEf0w2fDnATrX9VpkMcyFeM0FhwO62wh+A=
github.com/benbjohnson/clock v1.0.3/go.mod h1:bGMdMPoPVvcYyt1gHDf4J2KE153Yf9BuiUKYMaxlTDM=
@@ -734,8 +735,9 @@ github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMn
github.com/clbanning/mxj/v2 v2.5.6 h1:Jm4VaCI/+Ug5Q57IzEoZbwx4iQFA6wkXv72juUSeK+g=
github.com/clbanning/mxj/v2 v2.5.6/go.mod h1:hNiWqW14h+kc+MdF9C6/YoRfjEJoR3ou6tn/Qo+ve2s=
github.com/clbanning/x2j v0.0.0-20191024224557-825249438eec/go.mod h1:jMjuTZXRI4dUb/I5gc9Hdhagfvm9+RyrPryS/auMzxE=
github.com/cloudflare/circl v1.1.0 h1:bZgT/A+cikZnKIwn7xL2OBj012Bmvho/o6RpRvv3GKY=
github.com/cloudflare/circl v1.1.0/go.mod h1:prBCrKB9DV4poKZY1l9zBXg2QJY7mvgRvtMxxK7fi4I=
github.com/cloudflare/circl v1.3.3 h1:fE/Qz0QdIGqeWfnwq0RE0R7MI51s0M2E4Ga9kq5AEMs=
github.com/cloudflare/circl v1.3.3/go.mod h1:5XYMA4rFBvNIrhs50XuiBJ15vF2pZn4nnUKZrLbUZFA=
github.com/cncf/udpa/go v0.0.0-20210930031921-04548b0d99d4/go.mod h1:6pvJx4me5XPnfI9Z40ddWsdw2W/uZgQLFXToKeRcDiI=
github.com/cncf/udpa/go v0.0.0-20220112060539-c52dc94e7fbe h1:QQ3GSy+MqSHxm/d8nCtnAiZdYFd45cYZPs8vOOIYKfk=
github.com/cncf/udpa/go v0.0.0-20220112060539-c52dc94e7fbe/go.mod h1:6pvJx4me5XPnfI9Z40ddWsdw2W/uZgQLFXToKeRcDiI=
@@ -756,8 +758,8 @@ github.com/codahale/hdrhistogram v0.0.0-20161010025455-3a0bb77429bd/go.mod h1:sE
github.com/codahale/rfc6979 v0.0.0-20141003034818-6a90f24967eb h1:EDmT6Q9Zs+SbUoc7Ik9EfrFqcylYqgPZ9ANSbTAntnE=
github.com/common-nighthawk/go-figure v0.0.0-20210622060536-734e95fb86be h1:J5BL2kskAlV9ckgEsNQXscjIaLiOYiZ75d4e94E6dcQ=
github.com/common-nighthawk/go-figure v0.0.0-20210622060536-734e95fb86be/go.mod h1:mk5IQ+Y0ZeO87b858TlA645sVcEcbiX6YqP98kt+7+w=
github.com/containerd/stargz-snapshotter/estargz v0.12.1 h1:+7nYmHJb0tEkcRaAW+MHqoKaJYZmkikupxCqVtmPuY0=
github.com/containerd/stargz-snapshotter/estargz v0.12.1/go.mod h1:12VUuCq3qPq4y8yUW+l5w3+oXV3cx2Po3KSe/SmPGqw=
github.com/containerd/stargz-snapshotter/estargz v0.14.3 h1:OqlDCK3ZVUO6C3B/5FSkDwbkEETK84kQgEeFwDC+62k=
github.com/containerd/stargz-snapshotter/estargz v0.14.3/go.mod h1:KY//uOCIkSuNAHhJogcZtrNHdKrA99/FCCRjE3HD36o=
github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk=
github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE=
github.com/coreos/etcd v3.3.13+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE=
@@ -774,8 +776,8 @@ github.com/coreos/go-systemd v0.0.0-20181012123002-c6f51f82210d/go.mod h1:F5haX7
github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
github.com/coreos/go-systemd/v22 v22.1.0/go.mod h1:xO0FLkIi5MaZafQlIrOotqXZ90ih+1atmu1JpKERPPk=
github.com/coreos/go-systemd/v22 v22.3.2/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc=
github.com/coreos/go-systemd/v22 v22.4.0 h1:y9YHcjnjynCd/DVbg5j9L/33jQM3MxJlbj/zWskzfGU=
github.com/coreos/go-systemd/v22 v22.4.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc=
github.com/coreos/go-systemd/v22 v22.5.0 h1:RrqgGjYQKalulkV8NGVIfkXQf6YYmOyiJKk8iXXhfZs=
github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc=
github.com/coreos/pkg v0.0.0-20160727233714-3ac0863d7acf/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA=
github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA=
github.com/cpuguy83/go-md2man v1.0.10/go.mod h1:SmD6nW6nTyfqj6ABTjUi3V3JVMnlJmwcJI5acqYI6dE=
@@ -807,13 +809,17 @@ github.com/dgryski/trifles v0.0.0-20200323201526-dd97f9abfb48/go.mod h1:if7Fbed8
github.com/dimchansky/utfbom v1.1.0/go.mod h1:rO41eb7gLfo8SF1jd9F8HplJm1Fewwi4mQvIirEdv+8=
github.com/dimchansky/utfbom v1.1.1 h1:vV6w1AhK4VMnhBno/TPVCoK9U/LP0PkLCS9tbxHdi/U=
github.com/dimchansky/utfbom v1.1.1/go.mod h1:SxdoEBH5qIqFocHMyGOXVAybYJdr71b1Q/j0mACtrfE=
github.com/dnaeon/go-vcr v1.2.0 h1:zHCHvJYTMh1N7xnV7zf1m1GPBF9Ad0Jk/whtQ1663qI=
github.com/dnaeon/go-vcr v1.1.0 h1:ReYa/UBrRyQdant9B4fNHGoCNKw6qh6P0fsdGmZpR7c=
github.com/docker/cli v23.0.5+incompatible h1:ufWmAOuD3Vmr7JP2G5K3cyuNC4YZWiAsuDEvFVVDafE=
github.com/docker/cli v23.0.5+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8=
github.com/docker/distribution v2.8.2+incompatible h1:T3de5rq0dB1j30rp0sA2rER+m322EBzniBPB6ZIzuh8=
github.com/docker/distribution v2.8.2+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w=
github.com/docker/cli v20.10.21+incompatible h1:qVkgyYUnOLQ98LtXBrwd/duVqPT2X4SHndOuGsfwyhU=
github.com/docker/cli v20.10.21+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8=
github.com/docker/distribution v2.8.1+incompatible h1:Q50tZOPR6T/hjNsyc9g8/syEs6bk8XXApsHjKukMl68=
github.com/docker/distribution v2.8.1+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w=
github.com/docker/docker v24.0.5+incompatible h1:WmgcE4fxyI6EEXxBRxsHnZXrO1pQ3smi0k/jho4HLeY=
github.com/docker/docker v24.0.5+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
github.com/docker/docker v24.0.2+incompatible h1:eATx+oLz9WdNVkQrr0qjQ8HvRJ4bOOxfzEo8R+dA3cg=
github.com/docker/docker v24.0.2+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
github.com/docker/docker-credential-helpers v0.6.3/go.mod h1:WRaJzqw3CTB9bk10avuGsjVBZsD05qeibJ1/TYlvc0Y=
github.com/docker/docker-credential-helpers v0.7.0 h1:xtCHsjxogADNZcdv1pKUHXryefjlVRqWqIhk/uXJp0A=
github.com/docker/docker-credential-helpers v0.7.0/go.mod h1:rETQfLdHNT3foU5kuNkFR1R1V12OJRRO5lzt2D1b5X0=
@@ -824,16 +830,18 @@ github.com/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4
github.com/docker/go-units v0.5.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk=
github.com/docopt/docopt-go v0.0.0-20180111231733-ee0de3bc6815/go.mod h1:WwZ+bS3ebgob9U8Nd0kOddGdZWjyMGR8Wziv+TBNwSE=
github.com/dustin/go-humanize v0.0.0-20171111073723-bb3d318650d4/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk=
github.com/dustin/go-humanize v1.0.0 h1:VSnTsYCnlFHaM2/igO1h6X3HA71jcobQuxemgkq4zYo=
github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk=
github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY=
github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto=
github.com/eapache/go-resiliency v1.1.0/go.mod h1:kFI+JgMyC7bLPUVY133qvEBtVayf5mFgVsvEsIPBvNs=
github.com/eapache/go-xerial-snappy v0.0.0-20180814174437-776d5712da21/go.mod h1:+020luEh2TKB4/GOp8oxxtq0Daoen/Cii55CzbTV6DU=
github.com/eapache/queue v1.1.0/go.mod h1:6eCeP0CKFpHLu8blIFXhExK/dRa7WDZfr6jVFPTqq+I=
github.com/edsrzf/mmap-go v1.0.0/go.mod h1:YO35OhQPt3KJa3ryjFM5Bs14WD66h8eGKpfaBNrHW5M=
github.com/elazarl/goproxy v0.0.0-20221015165544-a0805db90819 h1:RIB4cRk+lBqKK3Oy0r2gRX4ui7tuhiZq2SuTtTCi0/0=
github.com/elliotchance/orderedmap v1.5.0 h1:1IsExUsjv5XNBD3ZdC7jkAAqLWOOKdbPTmkHx63OsBg=
github.com/elliotchance/orderedmap v1.5.0/go.mod h1:wsDwEaX5jEoyhbs7x93zk2H/qv0zwuhg4inXhDkYqys=
github.com/emicklei/go-restful/v3 v3.9.0 h1:XwGDlfxEnQZzuopoqxwSEllNcCOM9DhhFyhFIIGKwxE=
github.com/emicklei/go-restful/v3 v3.9.0/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc=
github.com/emicklei/go-restful/v3 v3.10.1 h1:rc42Y5YTp7Am7CS630D7JmhRjq4UlEUuEKfrDac4bSQ=
github.com/emicklei/go-restful/v3 v3.10.1/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc=
github.com/emirpasic/gods v1.12.0/go.mod h1:YfzfFFoVP/catgzJb4IKIqXjX78Ha8FMSDh3ymbK86o=
github.com/emirpasic/gods v1.18.1 h1:FXtiHYKDGKCW2KzwZKx0iC0PQmdlorYgdFG9jPXJ1Bc=
github.com/emirpasic/gods v1.18.1/go.mod h1:8tpGGwCnJ5H4r6BWwaV6OrWmMoPhUl5jm/FMNAnJvWQ=
@@ -874,7 +882,7 @@ github.com/francoispqt/gojay v1.2.13 h1:d2m3sFjloqoIUQU3TsHBgj6qg/BVGlTBeHDUmyJn
github.com/francoispqt/gojay v1.2.13/go.mod h1:ehT5mTG4ua4581f1++1WLG0vPdaA9HaiDsoyrBGkyDY=
github.com/franela/goblin v0.0.0-20200105215937-c9ffbefa60db/go.mod h1:7dvUGVsVBjqR7JHJk0brhHOZYGmfBYOrK0ZhYMEtBr4=
github.com/franela/goreq v0.0.0-20171204163338-bcd34c9993f8/go.mod h1:ZhphrRTfi2rbfLwlschooIH4+wKKDR4Pdxhh+TRoA20=
github.com/frankban/quicktest v1.14.3 h1:FJKSZTDHjyhriyC81FLQ0LY93eSai0ZyR/ZIkd3ZUKE=
github.com/frankban/quicktest v1.14.4 h1:g2rn0vABPOOXmZUj+vbmUp0lPoXEMuhTpIluN0XL9UY=
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
github.com/fsnotify/fsnotify v1.5.4/go.mod h1:OVB6XrOHzAwXMpEM7uPOzcehqUV2UqJxmVXmkdnm1bU=
@@ -892,7 +900,6 @@ github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeME
github.com/gliderlabs/ssh v0.1.1/go.mod h1:U7qILu1NlMHj9FlMhZLlkCdDnU1DBEAqr0aevW3Awn0=
github.com/gliderlabs/ssh v0.2.2/go.mod h1:U7qILu1NlMHj9FlMhZLlkCdDnU1DBEAqr0aevW3Awn0=
github.com/gliderlabs/ssh v0.3.5 h1:OcaySEmAQJgyYcArR+gGGTHCyE7nvhEMTlYY+Dp8CpY=
github.com/gliderlabs/ssh v0.3.5/go.mod h1:8XB4KraRrX39qHhT6yxPsHedjA08I/uBVwj4xC+/+z4=
github.com/go-chi/chi v4.1.2+incompatible h1:fGFk2Gmi/YKXk0OmGfBh0WgmN3XB8lVnEyNz34tQRec=
github.com/go-chi/chi v4.1.2+incompatible/go.mod h1:eB3wogJHnLi3x/kFX2A+IbTBlXxmMeXJVKy9tTv1XzQ=
github.com/go-errors/errors v1.0.1 h1:LUHzmkK3GUKUrL/1gfBUxAHzcev3apQlezX/+O7ma6w=
@@ -901,23 +908,19 @@ github.com/go-fonts/dejavu v0.1.0/go.mod h1:4Wt4I4OU2Nq9asgDCteaAaWZOV24E+0/Pwo0
github.com/go-fonts/latin-modern v0.2.0/go.mod h1:rQVLdDMK+mK1xscDwsqM5J8U2jrRa3T0ecnM9pNujks=
github.com/go-fonts/liberation v0.1.1/go.mod h1:K6qoJYypsmfVjWg8KOVDQhLc8UDgIK2HYqyqAO9z7GY=
github.com/go-fonts/stix v0.1.0/go.mod h1:w/c1f0ldAUlJmLBvlbkvVXLAD+tAMqobIIQpmnUIzUY=
github.com/go-git/gcfg v1.5.0 h1:Q5ViNfGF8zFgyJWPqYwA7qGFoMTEiBmdlkcfRmpIMa4=
github.com/go-git/gcfg v1.5.0/go.mod h1:5m20vg6GwYabIxaOonVkTdrILxQMpEShl1xiMF4ua+E=
github.com/go-git/go-billy/v5 v5.3.1/go.mod h1:pmpqyWchKfYfrkb/UVH4otLvyi/5gJlGI4Hb3ZqZ3W0=
github.com/go-git/go-billy/v5 v5.4.0 h1:Vaw7LaSTRJOUric7pe4vnzBSgyuf2KrLsu2Y4ZpQBDE=
github.com/go-git/go-billy/v5 v5.4.0/go.mod h1:vjbugF6Fz7JIflbVpl1hJsGjSHNltrSw45YK/ukIvQg=
github.com/go-git/go-git-fixtures/v4 v4.3.1 h1:y5z6dd3qi8Hl+stezc8p3JxDkoTRqMAlKnXHuzrfjTQ=
github.com/go-git/go-git-fixtures/v4 v4.3.1/go.mod h1:8LHG1a3SRW71ettAD/jW13h8c6AqjVSeL11RAdgaqpo=
github.com/go-git/go-git/v5 v5.5.2 h1:v8lgZa5k9ylUw+OR/roJHTxR4QItsNFI5nKtAXFuynw=
github.com/go-git/go-git/v5 v5.5.2/go.mod h1:BE5hUJ5yaV2YMxhmaP4l6RBQ08kMxKSPD4BlxtH7OjI=
github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 h1:+zs/tPmkDkHx3U66DAb0lQFJrpS6731Oaa12ikc+DiI=
github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376/go.mod h1:an3vInlBmSxCcxctByoQdvwPiA7DTK7jaaFDBTtu0ic=
github.com/go-git/go-billy/v5 v5.4.1 h1:Uwp5tDRkPr+l/TnbHOQzp+tmJfLceOlbVucgpTz8ix4=
github.com/go-git/go-billy/v5 v5.4.1/go.mod h1:vjbugF6Fz7JIflbVpl1hJsGjSHNltrSw45YK/ukIvQg=
github.com/go-git/go-git-fixtures/v4 v4.3.2-0.20230305113008-0c11038e723f h1:Pz0DHeFij3XFhoBRGUDPzSJ+w2UcK5/0JvF8DRI58r8=
github.com/go-git/go-git/v5 v5.7.0 h1:t9AudWVLmqzlo+4bqdf7GY+46SUuRsx59SboFxkq2aE=
github.com/go-git/go-git/v5 v5.7.0/go.mod h1:coJHKEOk5kUClpsNlXrUvPrDxY3w3gjHvhcZd8Fodw8=
github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU=
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
github.com/go-gota/gota v0.12.0 h1:T5BDg1hTf5fZ/CO+T/N0E+DDqUhvoKBl+UVckgcAAQg=
github.com/go-gota/gota v0.12.0/go.mod h1:UT+NsWpZC/FhaOyWb9Hui0jXg0Iq8e/YugZHTbyW/34=
github.com/go-ini/ini v1.25.4/go.mod h1:ByCAeIL28uOIIG0E3PJtZPDL8WnHpFKFOtgjp+3Ies8=
github.com/go-ini/ini v1.67.0 h1:z6ZrTEZqSWOTyH2FlglNbNgARyHG8oLW9gMELqKr06A=
github.com/go-ini/ini v1.67.0/go.mod h1:ByCAeIL28uOIIG0E3PJtZPDL8WnHpFKFOtgjp+3Ies8=
github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
github.com/go-kit/kit v0.10.0/go.mod h1:xUsJbQ/Fp4kEt7AFgCuvyX4a71u8h9jB8tj/ORgOZ7o=
@@ -930,11 +933,12 @@ github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG
github.com/go-logfmt/logfmt v0.5.1/go.mod h1:WYhtIu8zTZfxdn5+rREduYbwxfcBr/Vr6KEVveWlfTs=
github.com/go-logr/logr v1.2.0/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
github.com/go-logr/logr v1.2.3/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
github.com/go-logr/logr v1.2.4 h1:g01GSCwiDw2xSZfjJ2/T9M+S6pFdcNtFYsp+Y43HYDQ=
github.com/go-logr/logr v1.2.4/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag=
github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE=
github.com/go-logr/zapr v1.2.4 h1:QHVo+6stLbfJmYGkQ7uGHUCu5hnAFAj6mDe6Ea0SeOo=
github.com/go-logr/zapr v1.2.0 h1:n4JnPI1T3Qq1SFEi/F8rwLrZERp2bso19PJZDB9dayk=
github.com/go-openapi/analysis v0.21.2/go.mod h1:HZwRk4RRisyG8vx2Oe6aqeSQcoxRp47Xkp3+K6q+LdY=
github.com/go-openapi/analysis v0.21.4 h1:ZDFLvSNxpDaomuCueM0BlSXxpANBlFYiBvr+GXrvIHc=
github.com/go-openapi/analysis v0.21.4/go.mod h1:4zQ35W4neeZTqh3ol0rv/O8JBbka9QyAgQRPp9y3pfo=
@@ -944,13 +948,11 @@ github.com/go-openapi/errors v0.20.2/go.mod h1:cM//ZKUKyO06HSwqAelJ5NsEMMcpa6VpX
github.com/go-openapi/errors v0.20.3 h1:rz6kiC84sqNQoqrtulzaL/VERgkoCyB6WdEkc2ujzUc=
github.com/go-openapi/errors v0.20.3/go.mod h1:Z3FlZ4I8jEGxjUK+bugx3on2mIAk4txuAOhlsB1FSgk=
github.com/go-openapi/jsonpointer v0.19.3/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg=
github.com/go-openapi/jsonpointer v0.19.5 h1:gZr+CIYByUqjcgeLXnQu2gHYQC9o73G2XUeOFYEICuY=
github.com/go-openapi/jsonpointer v0.19.5/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg=
github.com/go-openapi/jsonpointer v0.19.6 h1:eCs3fxoIi3Wh6vtgmLTOjdhSpiqphQ+DaPn38N2ZdrE=
github.com/go-openapi/jsonpointer v0.19.6/go.mod h1:osyAmYz/mB/C3I+WsTTSgw1ONzaLJoLCyoi6/zppojs=
github.com/go-openapi/jsonreference v0.19.6/go.mod h1:diGHMEHg2IqXZGKxqyvWdfWU/aim5Dprw5bqpKkTvns=
github.com/go-openapi/jsonreference v0.20.0 h1:MYlu0sBgChmCfJxxUKZ8g1cPWFOB37YSZqewK7OKeyA=
github.com/go-openapi/jsonreference v0.20.0/go.mod h1:Ag74Ico3lPc+zR+qjn4XBUmXymS4zJbYVCZmcgkasdo=
github.com/go-openapi/jsonreference v0.20.1 h1:FBLnyygC4/IZZr893oiomc9XaghoveYTrLC1F86HID8=
github.com/go-openapi/jsonreference v0.20.1/go.mod h1:Bl1zwGIM8/wsvqjsOQLJ/SH+En5Ap4rVB5KVcIDZG2k=
github.com/go-openapi/loads v0.21.1/go.mod h1:/DtAMXXneXFjbQMGEtbamCZb+4x7eGwkvZCvBmwUG+g=
github.com/go-openapi/loads v0.21.2 h1:r2a/xFIYeZ4Qd2TnGpWDIQNcP80dIaZgf704za8enro=
github.com/go-openapi/loads v0.21.2/go.mod h1:Jq58Os6SSGz0rzh62ptiu8Z31I+OTHqmULx5e/gJbNw=
@@ -991,12 +993,11 @@ github.com/go-rod/rod v0.111.0 h1:aMNNdz10GYPYec9z1WsFqwAdRYVsuufVTOrah7whG3I=
github.com/go-sql-driver/mysql v1.4.0/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w=
github.com/go-sql-driver/mysql v1.4.1/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w=
github.com/go-sql-driver/mysql v1.6.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg=
github.com/go-sql-driver/mysql v1.7.1 h1:lUIinVbN1DY0xBg0eMOzmmtGoHwWBbvnWubQUrtU8EI=
github.com/go-sql-driver/mysql v1.7.0 h1:ueSltNNllEqE3qcWBTD0iQd3IpL/6U+mJxLkazJ7YPc=
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
github.com/go-stack/stack v1.8.1/go.mod h1:dcoOX6HbPZSZptuspn9bctJ+N/CnF5gGygcUP3XYfe4=
github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE=
github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 h1:tfuBGBXKqDEevZMzYi5KSi8KkcZtzBcTgAUUtapy0OI=
github.com/go-test/deep v1.0.8 h1:TDsG77qcSprGbC6vTN8OuXp5g+J+b5Pcguhf7Zt61VM=
github.com/go-test/deep v1.1.0 h1:WOcxcdHcvdgThNXjw0t76K42FXTU7HpNQWHpA2HHNlg=
github.com/gobuffalo/attrs v0.0.0-20190224210810-a9411de4debd/go.mod h1:4duuawTqi2wkkpB4ePgWMaai6/Kc6WEz83bhFwpHzj0=
github.com/gobuffalo/depgen v0.0.0-20190329151759-d478694a28d3/go.mod h1:3STtPUQYuzV0gBVOY3vy6CfMm/ljR4pABfrTeHNLHUY=
github.com/gobuffalo/depgen v0.1.0/go.mod h1:+ifsuy7fhi15RWncXQQKjWS9JPkdah5sZvtHc2RXGlg=
@@ -1041,8 +1042,8 @@ github.com/golang-jwt/jwt v3.2.2+incompatible h1:IfV12K8xAKAnZqdXVzCZ+TOjboZ2keL
github.com/golang-jwt/jwt v3.2.2+incompatible/go.mod h1:8pz2t5EyA70fFQQSrl6XZXzqecmYZeUEB8OUGHkxJ+I=
github.com/golang-jwt/jwt/v4 v4.0.0/go.mod h1:/xlHOz8bRuivTWchD4jCa+NbatV+wEUSzwAxVc6locg=
github.com/golang-jwt/jwt/v4 v4.2.0/go.mod h1:/xlHOz8bRuivTWchD4jCa+NbatV+wEUSzwAxVc6locg=
github.com/golang-jwt/jwt/v4 v4.5.0 h1:7cYmW1XlMY7h7ii7UhUyChSgS5wUJEnm9uZVTGqOWzg=
github.com/golang-jwt/jwt/v4 v4.5.0/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0=
github.com/golang-jwt/jwt/v4 v4.4.2 h1:rcc4lwaZgFMCZ5jxF9ABolDcIHdBytAFgqFPbSJQAYs=
github.com/golang-jwt/jwt/v4 v4.4.2/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0=
github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0/go.mod h1:E/TSTwGwJL78qG/PmXZO1EjYhfJinVAhrmmHX6Z8B9k=
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
github.com/golang/glog v1.0.0/go.mod h1:EWib/APOK0SL3dFbYqvxE3UYd8E6s1ouQ7iEp/0LWV4=
@@ -1118,8 +1119,8 @@ github.com/google/go-cmp v0.5.7/go.mod h1:n+brtR0CgQNWTVd5ZUFpTBC8YFBDLK/h/bpaJ8
github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38=
github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/google/go-containerregistry v0.13.0 h1:y1C7Z3e149OJbOPDBxLYR8ITPz8dTKqQwjErKVHJC8k=
github.com/google/go-containerregistry v0.13.0/go.mod h1:J9FQ+eSS4a1aC2GNZxvNpbWhgp0487v+cgiilB4FqDo=
github.com/google/go-containerregistry v0.15.2 h1:MMkSh+tjSdnmJZO7ljvEqV1DjfekB6VUEAZgy3a+TQE=
github.com/google/go-containerregistry v0.15.2/go.mod h1:wWK+LnOv4jXMM23IT/F1wdYftGWGr47Is8CG+pmHK1Q=
github.com/google/go-github v17.0.0+incompatible/go.mod h1:zLgOLi98H3fifZn+44m+umXrS52loVEgC2AApnigrVQ=
github.com/google/go-github/v28 v28.1.1/go.mod h1:bsqJWQX05omyWVmc00nEUql9mhQyv38lDZ8kPZcQVoM=
github.com/google/go-github/v45 v45.2.0 h1:5oRLszbrkvxDDqBCNj2hjDZMKmvexaZ1xw/FCD+K3FI=
@@ -1154,12 +1155,11 @@ github.com/google/pprof v0.0.0-20210226084205-cbba55b83ad5/go.mod h1:kpwsk12EmLe
github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
github.com/google/pprof v0.0.0-20210601050228-01bbb1931b22/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
github.com/google/pprof v0.0.0-20210609004039-a478d1d731e9/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
github.com/google/pprof v0.0.0-20210720184732-4bb14d4b1be1 h1:K6RDEckDVWvDI9JAJYCmNdQXq6neHJOYx3V6jnqNEec=
github.com/google/pprof v0.0.0-20210720184732-4bb14d4b1be1/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
github.com/google/rpmpack v0.0.0-20191226140753-aa36bfddb3a0/go.mod h1:RaTPr0KUf2K7fnZYLNDrr8rxAamWs3iNywJLtQ2AzBg=
github.com/google/s2a-go v0.1.4 h1:1kZ/sQM3srePvKs3tXAvQzo66XfcReoqFpIpIccE7Oc=
github.com/google/s2a-go v0.1.4/go.mod h1:Ej+mSEMGRnqRzjc7VtF+jdBwYG5fuJfiZ8ELkjEwM0A=
github.com/google/s2a-go v0.1.3 h1:FAgZmpLl/SXurPEZyCMPBIiiYeTbqfjlbdnCNTAkbGE=
github.com/google/s2a-go v0.1.3/go.mod h1:Ej+mSEMGRnqRzjc7VtF+jdBwYG5fuJfiZ8ELkjEwM0A=
github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 h1:El6M4kTTCOh6aBiKaUGG7oYTSPP8MxqL4YI3kZKwcP4=
github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510/go.mod h1:pupxD2MaaD3pAXIBCelhxNneeOaAeabZDe5s4K6zSpQ=
github.com/google/subcommands v1.0.1/go.mod h1:ZjhPrFU+Olkh9WazFPsl27BQ4UPiG37m3yTrtFlrHVk=
@@ -1193,8 +1193,8 @@ github.com/googleapis/gax-go/v2 v2.4.0/go.mod h1:XOTVJ59hdnfJLIP/dh8n5CGryZR2LxK
github.com/googleapis/gax-go/v2 v2.5.1/go.mod h1:h6B0KMMFNtI2ddbGJn3T3ZbwkeT6yqEF02fYlzkUCyo=
github.com/googleapis/gax-go/v2 v2.6.0/go.mod h1:1mjbznJAPHFpesgE5ucqfYEscaz5kMdcIDwU/6+DDoY=
github.com/googleapis/gax-go/v2 v2.7.0/go.mod h1:TEop28CZZQ2y+c0VxMUmu1lV+fQx57QpBWsYpwqHJx8=
github.com/googleapis/gax-go/v2 v2.11.0 h1:9V9PWXEsWnPpQhu/PeQIkS4eGzMlTLGgt80cUUI8Ki4=
github.com/googleapis/gax-go/v2 v2.11.0/go.mod h1:DxmR61SGKkGLa2xigwuZIQpkCI2S5iydzRfb3peWZJI=
github.com/googleapis/gax-go/v2 v2.8.0 h1:UBtEZqx1bjXtOQ5BVTkuYghXrr3N4V123VKJK67vJZc=
github.com/googleapis/gax-go/v2 v2.8.0/go.mod h1:4orTrqY6hXxxaUL4LHIPl6lGo8vAE38/qKbhSAKP6QI=
github.com/googleapis/go-type-adapters v1.0.0/go.mod h1:zHW75FOG2aur7gAO2B+MLby+cLsWGBF62rFAi7WjWO4=
github.com/googleapis/google-cloud-go-testing v0.0.0-20200911160855-bcd43fbb19e8/go.mod h1:dvDLG8qkwmyD9a/MJJN3XJcT3xFxOKAvTZGvuZmac9g=
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
@@ -1296,8 +1296,8 @@ github.com/imdario/mergo v0.3.4/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJ
github.com/imdario/mergo v0.3.8/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA=
github.com/imdario/mergo v0.3.9/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA=
github.com/imdario/mergo v0.3.11/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA=
github.com/imdario/mergo v0.3.13 h1:lFzP57bqS/wsqKssCGmtLAb8A0wKjLGrve2q3PPVcBk=
github.com/imdario/mergo v0.3.13/go.mod h1:4lJ1jqUDcsbIECGy0RUJAXNIhg+6ocWgb1ALK2O4oXg=
github.com/imdario/mergo v0.3.15 h1:M8XP7IuFNsqUx6VPK2P9OSmsYsI/YFaGil0uD21V3dM=
github.com/imdario/mergo v0.3.15/go.mod h1:WBLT9ZmE3lPoWsEzCh9LPo3TiwVN+ZKEjmz+hD27ysY=
github.com/in-toto/in-toto-golang v0.3.4-0.20220709202702-fa494aaa0add h1:DAh7mHiRT7wc6kKepYdCpH16ElPciMPQWJaJ7H3l/ng=
github.com/in-toto/in-toto-golang v0.3.4-0.20220709202702-fa494aaa0add/go.mod h1:DQI8vlV6h6qSY/tCOoYKtxjWrkyiNpJ3WTV/WoBllmQ=
github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
@@ -1312,7 +1312,6 @@ github.com/jedisct1/go-minisign v0.0.0-20211028175153-1c139d1cc84b/go.mod h1:hQm
github.com/jellevandenhooff/dkim v0.0.0-20150330215556-f50fe3d243e1/go.mod h1:E0B/fFc00Y+Rasa88328GlI/XbtyysCtTHZS8h7IrBU=
github.com/jellydator/ttlcache/v2 v2.11.1 h1:AZGME43Eh2Vv3giG6GeqeLeFXxwxn1/qHItqWZl6U64=
github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI=
github.com/jessevdk/go-flags v1.5.0/go.mod h1:Fw0T6WPc1dYxT4mKEZRfG5kJhaTDP9pj1c2EWnYs/m4=
github.com/jhump/gopoet v0.0.0-20190322174617-17282ff210b3/go.mod h1:me9yfT6IJSlOL3FCfrg+L6yzUEZ+5jW6WHt4Sk+UPUI=
github.com/jhump/gopoet v0.1.0/go.mod h1:me9yfT6IJSlOL3FCfrg+L6yzUEZ+5jW6WHt4Sk+UPUI=
github.com/jhump/goprotoc v0.5.0/go.mod h1:VrbvcYrQOrTi3i0Vf+m+oqQWk9l72mjkJCYo7UvLHRQ=
@@ -1372,9 +1371,8 @@ github.com/kisielk/errcheck v1.2.0/go.mod h1:/BMXB+zMLi60iA8Vv6Ksmxu/1UDYcXs4uQL
github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8=
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
github.com/klauspost/compress v1.13.6/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk=
github.com/klauspost/compress v1.15.11/go.mod h1:QPwzmACJjUTFsnSHH934V6woptycfrDDJnH7hvFVbGM=
github.com/klauspost/compress v1.16.0 h1:iULayQNOReoYUe+1qtKOqw9CwJv3aNQu8ivo7lw1HU4=
github.com/klauspost/compress v1.16.0/go.mod h1:ntbaceVETuRiXiv4DpjP66DpAtAGkEQskQzEyD//IeE=
github.com/klauspost/compress v1.16.5 h1:IFV2oUNUzZaz+XyusxpLzpzS8Pt5rh0Z16For/djlyI=
github.com/klauspost/compress v1.16.5/go.mod h1:ntbaceVETuRiXiv4DpjP66DpAtAGkEQskQzEyD//IeE=
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
@@ -1393,12 +1391,12 @@ github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
github.com/kubescape/go-git-url v0.0.25 h1:i7SSSC1+1m/Dg+4LV3erp0YklnWj1Z0cVlRxCT3Zy/0=
github.com/kubescape/go-git-url v0.0.25/go.mod h1:IbVT7Wsxlghsa+YxI5KOx4k9VQJaa3z0kTaQz5D3nKM=
github.com/kubescape/go-logger v0.0.15 h1:iCzVFlXXF7KYLd1cZumGOtuAZbltV8QiZI+kTe6TSN0=
github.com/kubescape/go-logger v0.0.15/go.mod h1:Al+yTE+vemECb/Myn2G9+2o2uFmMtphbkQmxf4OEHxE=
github.com/kubescape/k8s-interface v0.0.135 h1:DrhaJO+RbRRj3ai3Oy8rchs/0XJqvFSv7798ckaCObc=
github.com/kubescape/k8s-interface v0.0.135/go.mod h1:5sz+5Cjvo98lTbTVDiDA4MmlXxeHSVMW/wR0V3hV4K8=
github.com/kubescape/opa-utils v0.0.260 h1:VwI9vTkPYaVKAfvRATUAX+DX13c0Y+BRVEMNzw4WUOU=
github.com/kubescape/opa-utils v0.0.260/go.mod h1:0Be6E+vHqjavl/JneqgyC+oXOdfs6s+V6YnFvBkIAsA=
github.com/kubescape/go-logger v0.0.13 h1:Rio+grBhpcdExZIVT+HcBVcgbvwrU/aVSV99iKP1NNM=
github.com/kubescape/go-logger v0.0.13/go.mod h1:Tod++iNn5kofkhjpfwjUrqie2YHkLZNoH0Pq0+KldMo=
github.com/kubescape/k8s-interface v0.0.134 h1:dbXNGM0GH+xj8NadMu5pp1ZPFR90E+62DvWnyU0bdnA=
github.com/kubescape/k8s-interface v0.0.134/go.mod h1:JHRoGqyMJaALx9mVXfYhOnCDxlEAn9Aw8EIqZOFED80=
github.com/kubescape/opa-utils v0.0.256-0.20230720131313-796d89cc623c h1:ae+aMhlE8pD22Rn5b1CaXe1bTRMNemqJF9cUULezTDs=
github.com/kubescape/opa-utils v0.0.256-0.20230720131313-796d89cc623c/go.mod h1:SkNqbhUGipSYVE+oAUaHko6aggp8XVVbDChoNg48lao=
github.com/kubescape/rbac-utils v0.0.20 h1:1MMxsCsCZ3ntDi8f9ZYYcY+K7bv50bDW5ZvnGnhMhJw=
github.com/kubescape/rbac-utils v0.0.20/go.mod h1:t57AhSrjuNGQ+mpZWQM/hBzrCOeKBDHegFoVo4tbikQ=
github.com/kubescape/regolibrary v1.0.286-rc.0 h1:OzhtQEx1npAxTbgkbEpMLZvPWg6sh2CmCgQLs0j6pQ4=
@@ -1423,8 +1421,8 @@ github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czP
github.com/magiconair/properties v1.8.1/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
github.com/magiconair/properties v1.8.4/go.mod h1:y3VJvCyxH9uVvJTWEGAELF3aiYNyPKd5NZ3oSwXrF60=
github.com/magiconair/properties v1.8.5/go.mod h1:y3VJvCyxH9uVvJTWEGAELF3aiYNyPKd5NZ3oSwXrF60=
github.com/magiconair/properties v1.8.6 h1:5ibWZ6iY0NctNGWo87LalDlEZ6R41TqbbDamhfG/Qzo=
github.com/magiconair/properties v1.8.6/go.mod h1:y3VJvCyxH9uVvJTWEGAELF3aiYNyPKd5NZ3oSwXrF60=
github.com/magiconair/properties v1.8.7 h1:IeQXZAiQcpL9mgcAe1Nu6cX9LLw6ExEHKjN0VQdvPDY=
github.com/magiconair/properties v1.8.7/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0=
github.com/mailru/easyjson v0.0.0-20190312143242-1de009706dbe/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
@@ -1450,8 +1448,9 @@ github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hd
github.com/mattn/go-isatty v0.0.11/go.mod h1:PhnuNfih5lzO57/f3n+odYbM4JtupLOxQOAqxQCu2WE=
github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
github.com/mattn/go-isatty v0.0.17 h1:BTarxUcIeDqL27Mc+vyvdWYSL28zpIhv3RoTdsLMPng=
github.com/mattn/go-isatty v0.0.17/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
github.com/mattn/go-isatty v0.0.18 h1:DOKFKCQ7FNG2L1rbrmstDN4QVRdS89Nkh85u68Uwp98=
github.com/mattn/go-isatty v0.0.18/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
github.com/mattn/go-runewidth v0.0.2/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU=
github.com/mattn/go-runewidth v0.0.7/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI=
github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI=
@@ -1550,21 +1549,21 @@ github.com/onsi/ginkgo v1.16.4/go.mod h1:dX+/inL/fNMqNlz0e9LfyB9TswhZpCVdJM/Z6Vv
github.com/onsi/ginkgo v1.16.5 h1:8xi0RTUf59SOSfEtZMvwTvXYMzG4gV23XVHOZiXNtnE=
github.com/onsi/ginkgo v1.16.5/go.mod h1:+E8gABHa3K6zRBolWtd+ROzc/U5bkGt0FwiG042wbpU=
github.com/onsi/ginkgo/v2 v2.1.3/go.mod h1:vw5CSIxN1JObi/U8gcbwft7ZxR2dgaR70JSE3/PpL4c=
github.com/onsi/ginkgo/v2 v2.9.5 h1:+6Hr4uxzP4XIUyAkg61dWBw8lb/gc4/X5luuxN/EC+Q=
github.com/onsi/ginkgo/v2 v2.4.0 h1:+Ig9nvqgS5OBSACXNk15PLdp0U9XPYROt9CFzVdFGIs=
github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
github.com/onsi/gomega v1.5.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY=
github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo=
github.com/onsi/gomega v1.17.0/go.mod h1:HnhC7FXeEQY45zxNK3PPoIUhzk/80Xly9PcubAlGdZY=
github.com/onsi/gomega v1.19.0/go.mod h1:LY+I3pBVzYsTBU1AnDwOSxaYi9WoWiqgwooUqq9yPro=
github.com/onsi/gomega v1.27.7 h1:fVih9JD6ogIiHUN6ePK7HJidyEDpWGVB5mzM7cWNXoU=
github.com/onsi/gomega v1.23.0 h1:/oxKu9c2HVap+F3PfKort2Hw5DEU+HGlW8n+tguWsys=
github.com/op/go-logging v0.0.0-20160315200505-970db520ece7/go.mod h1:HzydrMdWErDVzsI23lYNej1Htcns9BCg93Dk0bBINWk=
github.com/open-policy-agent/opa v0.55.0 h1:s7Vm4ph6zDqqP/KzvUSw9fsKVsm9lhbTZhYGxxTK7mo=
github.com/open-policy-agent/opa v0.55.0/go.mod h1:2Vh8fj/bXCqSwGMbBiHGrw+O8yrho6T/fdaHt5ROmaQ=
github.com/open-policy-agent/opa v0.52.0 h1:Rv3F+VCDqsufaiYy/3S9/Iuk0yfcREK4iZmWbNsKZjA=
github.com/open-policy-agent/opa v0.52.0/go.mod h1:2n99s7WY/BXZUWUOq10JdTgK+G6XM4FYGoe7kQ5Vg0s=
github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U=
github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM=
github.com/opencontainers/image-spec v1.1.0-rc4 h1:oOxKUJWnFC4YGHCCMNql1x4YaDfYBTS5Y4x/Cgeo1E0=
github.com/opencontainers/image-spec v1.1.0-rc4/go.mod h1:X4pATf0uXsnn3g5aiGIsVnJBR4mxhKzfwmvK/B2NTm8=
github.com/opencontainers/image-spec v1.1.0-rc3 h1:fzg1mXZFj8YdPeNkRXMg+zb88BFV0Ys52cJydRwBkb8=
github.com/opencontainers/image-spec v1.1.0-rc3/go.mod h1:X4pATf0uXsnn3g5aiGIsVnJBR4mxhKzfwmvK/B2NTm8=
github.com/opentracing-contrib/go-observer v0.0.0-20170622124052-a52f23424492/go.mod h1:Ngi6UdF0k5OKD5t5wlmGhe/EDKPoUM3BXZSSfIuJbis=
github.com/opentracing/basictracer-go v1.0.0/go.mod h1:QfBfYuafItcjQuMwinw9GhYKwFXS9KnPs5lxoYwgW74=
github.com/opentracing/opentracing-go v1.0.2/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o=
@@ -1592,10 +1591,8 @@ github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/9
github.com/pelletier/go-toml v1.7.0/go.mod h1:vwGMzjaWMwyfHwgIBhI2YUM4fB6nL6lVAvS1LBMMhTE=
github.com/pelletier/go-toml v1.8.1/go.mod h1:T2/BmBdy8dvIRq1a/8aqjN41wvWlN4lrapLU/GW4pbc=
github.com/pelletier/go-toml v1.9.3/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c=
github.com/pelletier/go-toml v1.9.5 h1:4yBQzkHv+7BHq2PQUZF3Mx0IYxG7LsP222s7Agd3ve8=
github.com/pelletier/go-toml v1.9.5/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c=
github.com/pelletier/go-toml/v2 v2.0.5 h1:ipoSadvV8oGUjnUbMub59IDPPwfxF694nG/jwbMiyQg=
github.com/pelletier/go-toml/v2 v2.0.5/go.mod h1:OMHamSCAODeSsVrwwvcJOaoN0LIUIaFVNZzmWyNfXas=
github.com/pelletier/go-toml/v2 v2.0.8 h1:0ctb6s9mE31h0/lhu+J6OPmVeDxJn+kYnJc2jZR9tGQ=
github.com/pelletier/go-toml/v2 v2.0.8/go.mod h1:vuYfssBdrU2XDZ9bYydBu6t+6a6PYNcZljzZR9VXg+4=
github.com/performancecopilot/speed v3.0.0+incompatible/go.mod h1:/CLtqpZ5gBg1M9iaPbIdPPGyKcA8hKdoy6hAWba7Yac=
github.com/phpdave11/gofpdf v1.4.2/go.mod h1:zpO6xFn9yxo3YLyMvW8HcKWVdbNqgIfOOp2dXMnm1mY=
github.com/phpdave11/gofpdi v1.0.7/go.mod h1:vBmVV0Do6hSBHC8uKUQ71JGW+ZGQq74llk/7bXwjDoI=
@@ -1603,8 +1600,8 @@ github.com/phpdave11/gofpdi v1.0.12/go.mod h1:vBmVV0Do6hSBHC8uKUQ71JGW+ZGQq74llk
github.com/pierrec/lz4 v1.0.2-0.20190131084431-473cd7ce01a1/go.mod h1:3/3N9NVKO0jef7pBehbT1qWhCMrIgbYNnFAZCqQ5LRc=
github.com/pierrec/lz4 v2.0.5+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY=
github.com/pierrec/lz4 v2.6.1+incompatible h1:9UY3+iC23yxF0UfGaYrGplQ+79Rg+h/q9FV9ix19jjM=
github.com/pjbgf/sha1cd v0.2.3 h1:uKQP/7QOzNtKYH7UTohZLcjF5/55EnTw0jO/Ru4jZwI=
github.com/pjbgf/sha1cd v0.2.3/go.mod h1:HOK9QrgzdHpbc2Kzip0Q1yi3M2MFGPADtR6HjG65m5M=
github.com/pjbgf/sha1cd v0.3.0 h1:4D5XXmUUBUl/xQ6IjCkEAbqXskkq/4O7LmGn0AqMDs4=
github.com/pjbgf/sha1cd v0.3.0/go.mod h1:nZ1rrWOcGJ5uZgEEVL1VUM9iRQiZvWdbZjkKyFzPPsI=
github.com/pkg/browser v0.0.0-20210911075715-681adbf594b8 h1:KoWmjvw+nsYOo29YJK9vDA65RGE3NrOnUtO7a+RF9HU=
github.com/pkg/browser v0.0.0-20210911075715-681adbf594b8/go.mod h1:HKlIX3XHQyzLZPlr7++PzdhaXEj94dEiJgZDTsxEqUI=
github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e h1:aoZm08cpOy4WuID//EZDgcC4zIxODThtZNPirFr42+A=
@@ -1620,8 +1617,8 @@ github.com/pmezard/go-difflib v0.0.0-20151028094244-d8ed2627bdf0/go.mod h1:iKH77
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI=
github.com/pquerna/cachecontrol v0.2.0 h1:vBXSNuE5MYP9IJ5kjsdo8uq+w41jSPgvba2DEnkRx9k=
github.com/pquerna/cachecontrol v0.2.0/go.mod h1:NrUG3Z7Rdu85UNR3vm7SOsl1nFIeSiQnrHV5K9mBcUI=
github.com/pquerna/cachecontrol v0.1.0 h1:yJMy84ti9h/+OEWa752kBTKv4XC30OtVVHYv/8cTqKc=
github.com/pquerna/cachecontrol v0.1.0/go.mod h1:NrUG3Z7Rdu85UNR3vm7SOsl1nFIeSiQnrHV5K9mBcUI=
github.com/prometheus/client_golang v0.8.0/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
github.com/prometheus/client_golang v0.9.3-0.20190127221311-3c4408c8b829/go.mod h1:p2iRAGwDERtqlqzRXnrOVns+ignqQo//hLXqYxZYVNs=
@@ -1634,16 +1631,16 @@ github.com/prometheus/client_golang v1.10.0/go.mod h1:WJM3cc3yu7XKBKa/I8WeZm+V3e
github.com/prometheus/client_golang v1.11.0/go.mod h1:Z6t4BnS23TR94PD6BsDNk8yVqroYurpAkEiz0P2BEV0=
github.com/prometheus/client_golang v1.11.1/go.mod h1:Z6t4BnS23TR94PD6BsDNk8yVqroYurpAkEiz0P2BEV0=
github.com/prometheus/client_golang v1.12.1/go.mod h1:3Z9XVyYiZYEO+YQWt3RD2R3jrbd179Rt297l4aS6nDY=
github.com/prometheus/client_golang v1.16.0 h1:yk/hx9hDbrGHovbci4BY+pRMfSuuat626eFsHb7tmT8=
github.com/prometheus/client_golang v1.16.0/go.mod h1:Zsulrv/L9oM40tJ7T815tM89lFEugiJ9HzIqaAx4LKc=
github.com/prometheus/client_golang v1.15.0 h1:5fCgGYogn0hFdhyhLbw7hEsWxufKtY9klyvdNfFlFhM=
github.com/prometheus/client_golang v1.15.0/go.mod h1:e9yaBhRPU2pPNsZwE+JdQl0KEt1N9XgF6zxWmaC0xOk=
github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
github.com/prometheus/client_model v0.0.0-20190115171406-56726106282f/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
github.com/prometheus/client_model v0.1.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
github.com/prometheus/client_model v0.4.0 h1:5lQXD3cAg1OXBf4Wq03gTrXHeaV0TQvGfUooCfx1yqY=
github.com/prometheus/client_model v0.4.0/go.mod h1:oMQmHW1/JoDwqLtg57MGgP/Fb1CJEYF2imWWhWtMkYU=
github.com/prometheus/client_model v0.3.0 h1:UBgGFHqYdG/TPFD1B1ogZywDqEkwp3fBMvqdiQ7Xew4=
github.com/prometheus/client_model v0.3.0/go.mod h1:LDGWKZIo7rky3hgvBe+caln+Dr3dPggB5dvjtD7w9+w=
github.com/prometheus/common v0.0.0-20180801064454-c7de2306084e/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro=
github.com/prometheus/common v0.0.0-20181113130724-41aa239b4cce/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro=
github.com/prometheus/common v0.2.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
@@ -1668,8 +1665,8 @@ github.com/prometheus/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4O
github.com/prometheus/procfs v0.2.0/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU=
github.com/prometheus/procfs v0.6.0/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA=
github.com/prometheus/procfs v0.7.3/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA=
github.com/prometheus/procfs v0.10.1 h1:kYK1Va/YMlutzCGazswoHKo//tZVlFpKYh+PymziUAg=
github.com/prometheus/procfs v0.10.1/go.mod h1:nwNm2aOCAYw8uTR/9bWRREkZFxAUcWzPHWJq+XBB/FM=
github.com/prometheus/procfs v0.9.0 h1:wzCHvIvM5SxWqYvwgVL7yJY8Lz3PKn49KQtpgMYJfhI=
github.com/prometheus/procfs v0.9.0/go.mod h1:+pB4zwohETzFnmlpe6yd2lSc+0/46IYZRB/chUwxUZY=
github.com/prometheus/prometheus v2.5.0+incompatible/go.mod h1:oAIUtOny2rjMX0OWN5vPR5/q/twIROJvdqnQKDdil/s=
github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU=
github.com/pseudomuto/protoc-gen-doc v1.4.1/go.mod h1:exDTOVwqpp30eV/EDPFLZy3Pwr2sn6hBC1WIYH/UbIg=
@@ -1690,7 +1687,7 @@ github.com/rogpeppe/go-internal v1.2.2/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFR
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc=
github.com/rogpeppe/go-internal v1.8.0/go.mod h1:WmiCO8CzOY8rg0OYDC4/i/2WRWAB6poM+XZ2dLUbcbE=
github.com/rogpeppe/go-internal v1.11.0 h1:cWPaGQEPrBb5/AsnsZesgZZ9yb1OQ+GOISoDNXVBh4M=
github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8=
github.com/rs/cors v1.7.0/go.mod h1:gFx+x8UowdsKA9AchylcLynDq+nNFfI8FkUZdN/jGCU=
github.com/rs/cors v1.8.2/go.mod h1:XyqrcTp5zjWr1wsJ8PIRZssZ8b/WMcMf71DJnit4EMU=
github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g=
@@ -1717,8 +1714,9 @@ github.com/segmentio/ksuid v1.0.4 h1:sBo2BdShXjmcugAMwjugoGUdUV0pcxY5mW4xKRn3v4c
github.com/segmentio/ksuid v1.0.4/go.mod h1:/XUiZBD3kVx5SmUOl55voK5yeAbBNNIed+2O73XgrPE=
github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo=
github.com/sergi/go-diff v1.1.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM=
github.com/sergi/go-diff v1.2.0 h1:XU+rvMAioB0UC3q1MFrIQy4Vo5/4VsRDQQXHsEya6xQ=
github.com/sergi/go-diff v1.2.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM=
github.com/sergi/go-diff v1.3.1 h1:xkr+Oxo4BOQKmkn/B9eMK0g5Kg/983T9DqqPHwYqD+8=
github.com/sergi/go-diff v1.3.1/go.mod h1:aMJSSKb2lpPvRNec0+w3fl7LP9IOFzdc9Pa4NFbPK1I=
github.com/shibumi/go-pathspec v1.3.0 h1:QUyMZhFo0Md5B8zV8x2tesohbb5kfbpTi9rBnKh5dkI=
github.com/shibumi/go-pathspec v1.3.0/go.mod h1:Xutfslp817l2I1cZvgcfeMQJG5QnU2lh5tVaaMCl3jE=
github.com/shopspring/decimal v1.2.0 h1:abSATXmQEYyShuxI4/vyW3tV1MrKAJzCZ/0zLUXYbsQ=
@@ -1760,11 +1758,11 @@ github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMB
github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88=
github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=
github.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=
github.com/sirupsen/logrus v1.9.0/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ=
github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
github.com/skeema/knownhosts v1.1.0 h1:Wvr9V0MxhjRbl3f9nMnKnFfiWTJmtECJ9Njkea3ysW0=
github.com/skeema/knownhosts v1.1.0/go.mod h1:sKFq3RD6/TKZkSWn8boUbDC7Qkgcv+8XXijpFO6roag=
github.com/skeema/knownhosts v1.1.1 h1:MTk78x9FPgDFVFkDLTrsnnfCJl7g1C/nnKvePgrIngE=
github.com/skeema/knownhosts v1.1.1/go.mod h1:g4fPeYpque7P0xefxtGzV81ihjC8sX2IqpAoNkjxbMo=
github.com/skratchdot/open-golang v0.0.0-20200116055534-eef842397966 h1:JIAuq3EEf9cgbU6AtGPK4CTG3Zf6CKMNqf0MHTggAUA=
github.com/skratchdot/open-golang v0.0.0-20200116055534-eef842397966/go.mod h1:sUM3LWHvSMaG192sy56D9F7CNvL7jUJVXoqM1QKLnog=
github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=
@@ -1785,12 +1783,13 @@ github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B
github.com/spf13/afero v1.3.3/go.mod h1:5KUK8ByomD5Ti5Artl0RtHeI5pTF7MIDuXL3yY520V4=
github.com/spf13/afero v1.4.1/go.mod h1:Ai8FlHk4v/PARR026UzYexafAt9roJ7LcLMAmO6Z93I=
github.com/spf13/afero v1.6.0/go.mod h1:Ai8FlHk4v/PARR026UzYexafAt9roJ7LcLMAmO6Z93I=
github.com/spf13/afero v1.9.2 h1:j49Hj62F0n+DaZ1dDCvhABaPNSGNkt32oRFxI33IEMw=
github.com/spf13/afero v1.9.2/go.mod h1:iUV7ddyEEZPO5gA3zD4fJt6iStLlL+Lg4m2cihcDf8Y=
github.com/spf13/afero v1.9.5 h1:stMpOSZFs//0Lv29HduCmli3GUfpFoF3Y1Q/aXj/wVM=
github.com/spf13/afero v1.9.5/go.mod h1:UBogFpq8E9Hx+xc5CNTTEpTnuHVmXDwZcZcE1eb/UhQ=
github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=
github.com/spf13/cast v1.3.1/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=
github.com/spf13/cast v1.5.0 h1:rj3WzYc11XZaIZMPKmwP96zkFEnnAmV8s6XbB2aY32w=
github.com/spf13/cast v1.5.0/go.mod h1:SpXXQ5YoyJw6s3/6cMTQuxvgRl3PCJiyaX9p6b155UU=
github.com/spf13/cast v1.5.1 h1:R+kOtfhWQE6TVQzY+4D7wJLBgkdVasCEFxSUBYBYIlA=
github.com/spf13/cast v1.5.1/go.mod h1:b9PdjNptOpzXr7Rq1q9gJML/2cdGQAo69NKzQ10KN48=
github.com/spf13/cobra v0.0.3/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ=
github.com/spf13/cobra v0.0.5/go.mod h1:3K3wKZymM7VvHMDS9+Akkh4K60UwM26emMESw8tLCHU=
github.com/spf13/cobra v1.0.0/go.mod h1:/6GTrnGXV9HjY+aR4k0oJ5tcvakLuG6EuKReYlHNrgE=
@@ -1812,8 +1811,8 @@ github.com/spf13/viper v1.4.0/go.mod h1:PTJ7Z/lr49W6bUbkmS1V3by4uWynFiR9p7+dSq/y
github.com/spf13/viper v1.7.0/go.mod h1:8WkrPz2fc9jxqZNCJI/76HCieCp4Q8HaLFoCha5qpdg=
github.com/spf13/viper v1.7.1/go.mod h1:8WkrPz2fc9jxqZNCJI/76HCieCp4Q8HaLFoCha5qpdg=
github.com/spf13/viper v1.8.1/go.mod h1:o0Pch8wJ9BVSWGQMbra6iw0oQ5oktSIBaujf1rJH9Ns=
github.com/spf13/viper v1.13.0 h1:BWSJ/M+f+3nmdz9bxB+bWX28kkALN2ok11D0rSo8EJU=
github.com/spf13/viper v1.13.0/go.mod h1:Icm2xNL3/8uyh/wFuB1jI7TiTNKp8632Nwegu+zgdYw=
github.com/spf13/viper v1.16.0 h1:rGGH0XDZhdUOryiDWjmIvUSWpbNqisK8Wk0Vyefw8hc=
github.com/spf13/viper v1.16.0/go.mod h1:yg78JgCJcbrQOvV9YLXgkLaZqUidkY9K+Dd1FofRzQg=
github.com/spiffe/go-spiffe/v2 v2.1.1 h1:RT9kM8MZLZIsPTH+HKQEP5yaAk3yd/VBzlINaRjXs8k=
github.com/spiffe/go-spiffe/v2 v2.1.1/go.mod h1:5qg6rpqlwIub0JAiF1UK9IMD6BpPTmvG6yfSgDBs5lg=
github.com/src-d/gcfg v1.4.0/go.mod h1:p/UMsR43ujA89BJY9duynAwIpvqEujIH/jFlfL7jWoI=
@@ -1839,13 +1838,14 @@ github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/
github.com/stretchr/testify v1.7.2/go.mod h1:R6va5+xMeoiuVRoj+gSkQ7d3FALtqAAGI1FQKckRals=
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
github.com/stretchr/testify v1.8.3/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
github.com/stripe/stripe-go/v74 v74.28.0 h1:ItzPPy+cjMKbR3Oihknt/8dv6PANp3hTThUGZjhF9lc=
github.com/stripe/stripe-go/v74 v74.28.0/go.mod h1:f9L6LvaXa35ja7eyvP6GQswoaIPaBRvGAimAO+udbBw=
github.com/stripe/stripe-go/v74 v74.8.0 h1:0+3EfQSBhMg8SQ1+w+AP6Gxyko2crWbUG2uXbzYs8SU=
github.com/stripe/stripe-go/v74 v74.8.0/go.mod h1:5PoXNp30AJ3tGq57ZcFuaMylzNi8KpwlrYAFmO1fHZw=
github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw=
github.com/subosito/gotenv v1.4.1 h1:jyEFiXpy21Wm81FBN71l9VoMMV8H8jG+qIK3GCpY6Qs=
github.com/subosito/gotenv v1.4.1/go.mod h1:ayKnFf/c6rvx/2iiLrJUk1e6plDbT3edrFNGqEflhK0=
github.com/subosito/gotenv v1.4.2 h1:X1TuBLAMDFbaTAChgCBLu3DU3UPyELpnF2jjJ2cz/S8=
github.com/subosito/gotenv v1.4.2/go.mod h1:ayKnFf/c6rvx/2iiLrJUk1e6plDbT3edrFNGqEflhK0=
github.com/syndtr/goleveldb v1.0.1-0.20220721030215-126854af5e6d h1:vfofYNRScrDdvS342BElfbETmL1Aiz3i2t0zfRj16Hs=
github.com/syndtr/goleveldb v1.0.1-0.20220721030215-126854af5e6d/go.mod h1:RRCYJbIwD5jmqPI9XoAFR0OcDxqUctll6zUj/+B4S48=
github.com/tarm/serial v0.0.0-20180830185346-98f6abe2eb07/go.mod h1:kDXzergiv9cbyO7IOYJZWg1U88JhDg3PB6klq9Hg2pA=
@@ -1870,9 +1870,8 @@ github.com/tjfoc/gmsm v1.3.2/go.mod h1:HaUcFuY0auTiaHB9MHFGCPx5IaLhTUd2atbCFBQXn
github.com/tmc/grpc-websocket-proxy v0.0.0-20170815181823-89b8d40f7ca8/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=
github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=
github.com/tmc/grpc-websocket-proxy v0.0.0-20200427203606-3cfed13b9966/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=
github.com/tmc/grpc-websocket-proxy v0.0.0-20201229170055-e5319fda7802 h1:uruHq4dN7GR16kFc5fp3d1RIYzJW5onx8Ybykw2YQFA=
github.com/tmc/grpc-websocket-proxy v0.0.0-20201229170055-e5319fda7802/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=
github.com/tmc/grpc-websocket-proxy v0.0.0-20220101234140-673ab2c3ae75 h1:6fotK7otjonDflCTK0BCfls4SPy3NcCVb5dqqmbRknE=
github.com/tmc/grpc-websocket-proxy v0.0.0-20220101234140-673ab2c3ae75/go.mod h1:KO6IkyS8Y3j8OdNO85qEYBsRPuteD+YciPomcXdrMnk=
github.com/tomasen/realip v0.0.0-20180522021738-f0c99a92ddce/go.mod h1:o8v6yHRoik09Xen7gje4m9ERNah1d1PPsVq1VEx9vE4=
github.com/transparency-dev/merkle v0.0.1 h1:T9/9gYB8uZl7VOJIhdwjALeRWlxUxSfDEysjfmx+L9E=
github.com/transparency-dev/merkle v0.0.1/go.mod h1:B8FIw5LTq6DaULoHsVFRzYIUDkl8yuSwCdZnOZGKL/A=
@@ -1880,19 +1879,20 @@ github.com/ugorji/go v1.1.4/go.mod h1:uQMGLiO92mf5W77hV/PUCpI3pbzQx3CRekS0kk+RGr
github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0=
github.com/ulikunitz/xz v0.5.6/go.mod h1:2bypXElzHzzJZwzH67Y6wb67pO62Rzfn7BSiF4ABRW8=
github.com/ulikunitz/xz v0.5.7/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oWt/14=
github.com/uptrace/opentelemetry-go-extra/otelutil v0.2.2 h1:CNznWHkrbA6o1q2H/BsH4tIHf4zbKNtndeoV+AH8z0U=
github.com/uptrace/opentelemetry-go-extra/otelutil v0.2.2/go.mod h1:7YSrHCmYPHIXjTWnKSU7EGT0TFEcm3WwSeQquwCGg38=
github.com/uptrace/opentelemetry-go-extra/otelzap v0.2.2 h1:uyrW06oJi4iWvhjPLVfk4qrSP2Zm0AMozKKDmp6i4pE=
github.com/uptrace/opentelemetry-go-extra/otelzap v0.2.2/go.mod h1:PMAs2dNxP55lgt6xu0if+Jasm6s+Xpmqn6ev1NyDfnI=
github.com/uptrace/opentelemetry-go-extra/otelutil v0.2.1 h1:qjljyY//UH064+gQDHh5U7M1Jh6b+iQpJUWVAuRJ04A=
github.com/uptrace/opentelemetry-go-extra/otelutil v0.2.1/go.mod h1:7YSrHCmYPHIXjTWnKSU7EGT0TFEcm3WwSeQquwCGg38=
github.com/uptrace/opentelemetry-go-extra/otelzap v0.2.1 h1:HhKd/kmL1JuBK3zPr3gT/Ku7lvvBsnsy8NtQ+uG5rRM=
github.com/uptrace/opentelemetry-go-extra/otelzap v0.2.1/go.mod h1:GiIWZ+UVlFnAik/QCTc7TjsLA+YV1m94ls3G1Q7fKRY=
github.com/uptrace/uptrace-go v1.16.0 h1:yB9vt1hBYYoXWExNx0okubLOjd339d7lH+/5o+Lp+MY=
github.com/uptrace/uptrace-go v1.16.0/go.mod h1:Ssc5wLpoL+9V0qkT5FtrIiru9SY4xb7q1UVLjSpxpCg=
github.com/urfave/cli v1.20.0/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA=
github.com/urfave/cli v1.22.1/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0=
github.com/urfave/cli v1.22.4/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0=
github.com/urfave/cli v1.22.7 h1:aXiFAgRugfJ27UFDsGJ9DB2FvTC73hlVXFSqq5bo9eU=
github.com/urfave/cli v1.22.7/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0=
github.com/vbatts/tar-split v0.11.2 h1:Via6XqJr0hceW4wff3QRzD5gAk/tatMw/4ZA7cTlIME=
github.com/vbatts/tar-split v0.11.2/go.mod h1:vV3ZuO2yWSVsz+pfFzDG/upWH1JhjOiEaWq6kXyQ3VI=
github.com/urfave/cli v1.22.12 h1:igJgVw1JdKH+trcLWLeLwZjU9fEfPesQ+9/e4MQ44S8=
github.com/urfave/cli v1.22.12/go.mod h1:sSBEIC79qR6OvcmsD4U3KABeOTxDqQtdDnaFuUN30b8=
github.com/vbatts/tar-split v0.11.3 h1:hLFqsOLQ1SsppQNTMpkpPXClLDfC2A3Zgy9OUU+RVck=
github.com/vbatts/tar-split v0.11.3/go.mod h1:9QlHN18E+fEH7RdG+QAJJcuya3rqT7eXSTY7wGrAokY=
github.com/viant/assertly v0.4.8/go.mod h1:aGifi++jvCrUaklKEKT0BU95igDNaqkvz+49uaYMPRU=
github.com/viant/toolbox v0.24.0/go.mod h1:OxMCG57V0PXuIP2HNQrtJf2CjqdmbrOx5EkMILuUhzM=
github.com/vmihailenco/msgpack/v4 v4.3.12/go.mod h1:gborTTJjAo/GWTqqRjrLCn9pgNN+NXzzngzBKDPIqw4=
@@ -1926,8 +1926,8 @@ github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q
github.com/xlab/treeprint v1.1.0 h1:G/1DjNkPpfZCFt9CSh6b5/nY4VimlbHF3Rh4obvtzDk=
github.com/xlab/treeprint v1.1.0/go.mod h1:gj5Gd3gPdKtR1ikdDK6fnFLdmIS0X30kTTuNd/WEJu0=
github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q=
github.com/yashtewari/glob-intersection v0.2.0 h1:8iuHdN88yYuCzCdjt0gDe+6bAhUwBeEWqThExu54RFg=
github.com/yashtewari/glob-intersection v0.2.0/go.mod h1:LK7pIC3piUjovexikBbJ26Yml7g8xa5bsjfx2v1fwok=
github.com/yashtewari/glob-intersection v0.1.0 h1:6gJvMYQlTDOL3dMsPF6J0+26vwX9MB8/1q3uAdhmTrg=
github.com/yashtewari/glob-intersection v0.1.0/go.mod h1:LK7pIC3piUjovexikBbJ26Yml7g8xa5bsjfx2v1fwok=
github.com/youmark/pkcs8 v0.0.0-20181117223130-1be2e3e5546d/go.mod h1:rHwXgn7JulP+udvsHwJoVG1YGAP6VLg4y9I5dyZdqmA=
github.com/ysmood/goob v0.4.0 h1:HsxXhyLBeGzWXnqVKtmT9qM7EuVs/XOgkX7T6r1o1AQ=
github.com/ysmood/gson v0.7.2 h1:1iWUvpi5DPvd2j59W7ifRPR9DiAZ3Ga+fmMl1mJrRbM=
@@ -1947,8 +1947,9 @@ github.com/zeebo/errs v1.2.2/go.mod h1:sgbWHsvVuTPHcqJJGQ1WhI5KbWlHYz+2+2C/LSEtC
go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU=
go.etcd.io/bbolt v1.3.3/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU=
go.etcd.io/bbolt v1.3.5/go.mod h1:G5EMThwa9y8QZGBClrRx5EY+Yw9kAhnjy3bSjsnlVTQ=
go.etcd.io/bbolt v1.3.6 h1:/ecaJf0sk1l4l6V4awd65v2C3ILy7MSj+s/x1ADCIMU=
go.etcd.io/bbolt v1.3.6/go.mod h1:qXsaaIqmgQH0T+OPdb99Bf+PKfBBQVAdyD6TY9G8XM4=
go.etcd.io/bbolt v1.3.7 h1:j+zJOnnEjF/kyHlDDgGnVL/AIqIJPq8UoB2GSNfkUfQ=
go.etcd.io/bbolt v1.3.7/go.mod h1:N9Mkw9X8x5fupy0IKsmuqVtoGDyxsaDlbk4Rd05IAQw=
go.etcd.io/etcd v0.0.0-20191023171146-3cf2f69b5738/go.mod h1:dnLIgRNXwCJa5e+c6mIZCrds/GIG4ncV9HhK5PX7jPg=
go.etcd.io/etcd/api/v3 v3.5.0-alpha.0/go.mod h1:mPcW6aZJukV6Aa81LSKpBjQXTWlXB5r74ymPoSWa3Sw=
go.etcd.io/etcd/api/v3 v3.5.0/go.mod h1:cbVKeC6lCfl7j/8jBhAK6aIYO9XOjdptoxU/nLQcPvs=
@@ -2014,18 +2015,15 @@ go.opencensus.io v0.22.5/go.mod h1:5pWMHQbX5EPX2/62yrJeAkowc+lfs/XD7Uxpq3pI6kk=
go.opencensus.io v0.23.0/go.mod h1:XItmlyltB5F7CS4xOC1DcqMoFqwtC6OG2xF7mCv7P7E=
go.opencensus.io v0.24.0 h1:y73uSU6J157QMP2kn2r30vwW1A2W2WFwSCGnAVxeaD0=
go.opencensus.io v0.24.0/go.mod h1:vNK8G9p7aAivkbmorf4v+7Hgx+Zs0yY+0fOtgBfjQKo=
go.opentelemetry.io/contrib v0.20.0/go.mod h1:G/EtFaa6qaN7+LxqfIAT3GiZa7Wv5DTBUzl5H4LY0Kc=
go.opentelemetry.io/contrib v1.6.0 h1:xJawAzMuR3s4Au5p/ABHqYFychHjK2AHB9JvkBuBbTA=
go.opentelemetry.io/contrib v1.6.0/go.mod h1:FlyPNX9s4U6MCsWEc5YAK4KzKNHFDsjrDUZijJiXvy8=
go.opentelemetry.io/contrib/instrumentation/github.com/gorilla/mux/otelmux v0.38.0 h1:LD3oTSAwqtEMtZ/ati5F9UMopROCPNDqh9k10CjuWjE=
go.opentelemetry.io/contrib/instrumentation/github.com/gorilla/mux/otelmux v0.38.0/go.mod h1:iUSPEXZM7sckWSTCtzog1lU42Qaiu9U2WY6vdqwFHDI=
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.20.0/go.mod h1:oVGt1LRbBOBq1A5BQLlUg9UaU/54aiHw8cgjV3aWZ/E=
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.35.0 h1:xFSRQBbXF6VvYRf2lqMJXxoB72XI1K/azav8TekHHSw=
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.35.0/go.mod h1:h8TWwRAhQpOd0aM5nYsRD8+flnkj+526GEIVlarH7eY=
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.42.0 h1:pginetY7+onl4qN1vl0xW/V/v6OBZ0vVdH+esuJgvmM=
go.opentelemetry.io/contrib/instrumentation/runtime v0.42.0 h1:EbmAUG9hEAMXyfWEasIt2kmh/WmXUznUksChApTgBGc=
go.opentelemetry.io/contrib/instrumentation/runtime v0.42.0/go.mod h1:rD9feqRYP24P14t5kmhNMqsqm1jvKmpx2H2rKVw52V8=
go.opentelemetry.io/otel v0.20.0/go.mod h1:Y3ugLH2oa81t5QO+Lty+zXf8zC9L26ax4Nzoxm/dooo=
go.opentelemetry.io/otel v1.10.0/go.mod h1:NbvWjCthWHKBEUMpf0/v8ZRZlni86PpGFEMA9pnQSnQ=
go.opentelemetry.io/otel v1.16.0 h1:Z7GVAX/UkAXPKsy94IU+i6thsQS4nb7LviLpnaNeW8s=
go.opentelemetry.io/otel v1.16.0/go.mod h1:vl0h9NUa1D5s1nv3A5vZOYWn8av4K8Ml6JDeHrT/bx4=
go.opentelemetry.io/otel/exporters/otlp v0.20.0/go.mod h1:YIieizyaN77rtLJra0buKiNBOm9XQfkPEKBeuhoMwAM=
@@ -2053,6 +2051,7 @@ go.opentelemetry.io/otel/sdk/metric v0.20.0/go.mod h1:knxiS8Xd4E/N+ZqKmUPf3gTTZ4
go.opentelemetry.io/otel/sdk/metric v0.39.0 h1:Kun8i1eYf48kHH83RucG93ffz0zGV1sh46FAScOTuDI=
go.opentelemetry.io/otel/sdk/metric v0.39.0/go.mod h1:piDIRgjcK7u0HCL5pCA4e74qpK/jk3NiUoAHATVAmiI=
go.opentelemetry.io/otel/trace v0.20.0/go.mod h1:6GjCW8zgDjwGHGa6GkyeB8+/5vjT16gUEi0Nf1iBdgw=
go.opentelemetry.io/otel/trace v1.10.0/go.mod h1:Sij3YYczqAdz+EhmGhE6TpTxUO5/F/AzrK+kxfGqySM=
go.opentelemetry.io/otel/trace v1.16.0 h1:8JRpaObFoW0pxuVPapkgH8UhHQj+bJW8jJsCZEu5MQs=
go.opentelemetry.io/otel/trace v1.16.0/go.mod h1:Yt9vYq1SdNz3xdjZZK7wcXv1qv2pwLkqr2QVwea0ef0=
go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI=
@@ -2069,7 +2068,6 @@ go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=
go.uber.org/atomic v1.9.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=
go.uber.org/atomic v1.11.0 h1:ZvwS0R+56ePWxUNi+Atn9dWONBPp/AUETXlHW0DxSjE=
go.uber.org/atomic v1.11.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0=
go.uber.org/goleak v1.1.10/go.mod h1:8a7PlsEVH3e/a/GLqe5IIrQx6GzcnRmZEufDUTk4A7A=
go.uber.org/goleak v1.1.11/go.mod h1:cwTWslyiVhfpKIDGSZEM2HlOvcqm+tG4zioyIeLoqMQ=
go.uber.org/goleak v1.2.1 h1:NBol2c7O1ZokfZ0LEU9K6Whx/KnwvepVetCUhtKja4A=
go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0=
@@ -2123,13 +2121,12 @@ golang.org/x/crypto v0.0.0-20211215153901-e495a2d5b3d3/go.mod h1:IxCIyHEi3zRg3s0
golang.org/x/crypto v0.0.0-20220131195533-30dcbda58838/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
golang.org/x/crypto v0.0.0-20220314234659-1baeb1ce4c0b/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
golang.org/x/crypto v0.0.0-20220411220226-7b82a4e95df4/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
golang.org/x/crypto v0.0.0-20220525230936-793ad666bf5e/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
golang.org/x/crypto v0.0.0-20220826181053-bd7e27e6170d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
golang.org/x/crypto v0.3.0/go.mod h1:hebNnKkNXi2UzZN1eVRvBB7co0a+JxK6XbPiWVs/3J4=
golang.org/x/crypto v0.11.0 h1:6Ewdq3tDic1mg5xRO4milcWCfMVQhI4NkqWWvqejpuA=
golang.org/x/crypto v0.11.0/go.mod h1:xgJhtzW8F9jGdVFWZESrid1U1bjeNy4zgy5cRr/CIio=
golang.org/x/crypto v0.7.0/go.mod h1:pYwdfH91IfpZVANVyUOhSIPZaFoJGxTFbZhFTx+dXZU=
golang.org/x/crypto v0.10.0 h1:LKqV2xt9+kDzSTfOhx4FrkEBcMrAgHSYgzywV9zcGmM=
golang.org/x/crypto v0.10.0/go.mod h1:o4eNf7Ede1fv+hwOwZsTHl9EsPFO6q6ZvYR8vYfY45I=
golang.org/x/exp v0.0.0-20180321215751-8460e604b9de/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20180807140117-3d87b88a115f/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
@@ -2145,8 +2142,8 @@ golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u0
golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM=
golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU=
golang.org/x/exp v0.0.0-20200331195152-e8c3332aa8e5/go.mod h1:4M0jN8W1tt0AVLNr8HDosyJCDCDuyL9N9+3m7wDWgKw=
golang.org/x/exp v0.0.0-20230728194245-b0cb94b80691 h1:/yRP+0AN7mf5DkD3BAI6TOFnd51gEoDEb8o35jIFtgw=
golang.org/x/exp v0.0.0-20230728194245-b0cb94b80691/go.mod h1:FXUEEKJgO7OQYeo8N01OfiKP8RXMtf6e8aTskBGqWdc=
golang.org/x/exp v0.0.0-20230711023510-fffb14384f22 h1:FqrVOBQxQ8r/UwwXibI0KMolVhvFiGobSfdE33deHJM=
golang.org/x/exp v0.0.0-20230711023510-fffb14384f22/go.mod h1:FXUEEKJgO7OQYeo8N01OfiKP8RXMtf6e8aTskBGqWdc=
golang.org/x/image v0.0.0-20180708004352-c73c2afc3b81/go.mod h1:ux5Hcp/YLpHSI86hEcLt0YII63i6oz57MZXIpbrjZUs=
golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=
golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
@@ -2244,12 +2241,10 @@ golang.org/x/net v0.0.0-20210421230115-4e50805a0758/go.mod h1:72T/g9IO56b78aLF+1
golang.org/x/net v0.0.0-20210423184538-5f58ad60dda6/go.mod h1:OJAsFXCWl8Ukc7SiCT/9KSuxbyM7479/AVlXFRxuMCk=
golang.org/x/net v0.0.0-20210428140749-89ef3d95e781/go.mod h1:OJAsFXCWl8Ukc7SiCT/9KSuxbyM7479/AVlXFRxuMCk=
golang.org/x/net v0.0.0-20210503060351-7fd8e65b6420/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20210520170846-37e1c6afe023/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20210525063256-abc453219eb5/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20210813160813-60bc85c4be6d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20211015210444-4f30a5c0130f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20211123203042-d83791d6bcd9/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
golang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
golang.org/x/net v0.0.0-20220325170049-de3da57026de/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
@@ -2269,8 +2264,8 @@ golang.org/x/net v0.2.0/go.mod h1:KqCZLdyyvdV855qA2rE3GC2aiw5xGR5TEjj8smXukLY=
golang.org/x/net v0.5.0/go.mod h1:DivGGAXEgPSlEBzxGzZI+ZLohi+xUj054jfeKui00ws=
golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
golang.org/x/net v0.8.0/go.mod h1:QVkue5JL9kW//ek3r6jTKnTFis1tRmNAW2P1shuFdJc=
golang.org/x/net v0.12.0 h1:cfawfvKITfUsFCeJIHJrbSxpeu/E81khclypR0GVT50=
golang.org/x/net v0.12.0/go.mod h1:zEVYFnQC7m/vmpQFELhcD1EWkZlX69l4oqgmer6hfKA=
golang.org/x/net v0.11.0 h1:Gi2tvZIJyBtO9SDr1q9h5hEQCp/4L2RQ+ar0qjx2oNU=
golang.org/x/net v0.11.0/go.mod h1:2L/ixqYpgIVXmeoSA/4Lu7BzTG4KIyPIryS4IsOd1oQ=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/oauth2 v0.0.0-20181017192945-9dcd33a902f4/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/oauth2 v0.0.0-20181106182150-f42d05182288/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
@@ -2305,8 +2300,8 @@ golang.org/x/oauth2 v0.0.0-20220909003341-f21342109be1/go.mod h1:h4gKUeWbJ4rQPri
golang.org/x/oauth2 v0.0.0-20221006150949-b44042a4b9c1/go.mod h1:h4gKUeWbJ4rQPri7E0u6Gs4e9Ri2zaLxzw5DI5XGrYg=
golang.org/x/oauth2 v0.0.0-20221014153046-6fdb5e3db783/go.mod h1:h4gKUeWbJ4rQPri7E0u6Gs4e9Ri2zaLxzw5DI5XGrYg=
golang.org/x/oauth2 v0.4.0/go.mod h1:RznEsdpjGAINPTOF0UH/t+xJ75L18YO3Ho6Pyn+uRec=
golang.org/x/oauth2 v0.10.0 h1:zHCpF2Khkwy4mMB4bv0U37YtJdTGW8jI0glAApi0Kh8=
golang.org/x/oauth2 v0.10.0/go.mod h1:kTpgurOux7LqtuxjuyZa4Gj2gdezIt/jQtGnNFfypQI=
golang.org/x/oauth2 v0.7.0 h1:qe6s0zUXlPX80/dITx3440hWZ7GwMwgDDyrSGTPJG/g=
golang.org/x/oauth2 v0.7.0/go.mod h1:hPLQkd9LyjfXTiRohC/41GhcFqxisoUQ99sCUOHO9x4=
golang.org/x/perf v0.0.0-20180704124530-6e6d33e29852/go.mod h1:JLpeXjPJfIyPr5TlbXLkXWLhP8nz10XfvxElABhCtcw=
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
@@ -2324,9 +2319,8 @@ golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJ
golang.org/x/sync v0.0.0-20220601150217-0de741cfad7f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20220929204114-8fcdb60fdcc0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.1.0 h1:wsuoTGHzEhffawBOhz5CYhcrV4IdKZbEyZjBMuTp12o=
golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.3.0 h1:ftCYgMx6zT/asHUrPw8BLLscYtGznsLAnjq5RH9P66E=
golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y=
golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
@@ -2444,7 +2438,7 @@ golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBc
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220825204002-c680a09ffe64/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220906165534-d0df966e6959/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
@@ -2452,19 +2446,18 @@ golang.org/x/sys v0.3.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.4.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.10.0 h1:SqMFp9UcQJZa+pmYuAKjd9xq1f0j5rLcDIk0mj4qAsA=
golang.org/x/sys v0.10.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.9.0 h1:KS/R3tvhPqvJvwcKfnBHJwwthS11LRhmM5D59eEXa0s=
golang.org/x/sys v0.9.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
golang.org/x/term v0.0.0-20220722155259-a9ba230a4035/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
golang.org/x/term v0.1.0/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
golang.org/x/term v0.2.0/go.mod h1:TVmDHMZPmdnySmBfhjOoOdhjzdE1h4u1VwSiw2l1Nuc=
golang.org/x/term v0.4.0/go.mod h1:9P2UbLfCdcvo3p/nzKvsmas4TnlujnuoV9hGgYzW1lQ=
golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
golang.org/x/term v0.6.0/go.mod h1:m6U89DPEgQRMq3DNkDClhWw02AUbt2daBVO4cn4Hv9U=
golang.org/x/term v0.10.0 h1:3R7pNqamzBraeqj/Tj8qt1aQ2HpmlC+Cx/qL/7hn4/c=
golang.org/x/term v0.10.0/go.mod h1:lpqdcUyK/oCiQxvxVrppt5ggO2KCZ5QblwqPnfZ6d5o=
golang.org/x/term v0.9.0 h1:GRRCnKYhdQrD8kfRAdQ6Zcw1P0OcELxGLKJvtjVMZ28=
golang.org/x/term v0.9.0/go.mod h1:M6DEAAIenWoTxdKrOltXcmDY3rSplQUkrvaDU5FcQyo=
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
@@ -2479,8 +2472,8 @@ golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
golang.org/x/text v0.6.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
golang.org/x/text v0.8.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
golang.org/x/text v0.11.0 h1:LAntKIrcmeSKERyiOh0XMV39LXS8IE9UL2yP7+f5ij4=
golang.org/x/text v0.11.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
golang.org/x/text v0.10.0 h1:UpjohKhiEgNc0CSauXmwYftY1+LlaC75SJwh0SgCX58=
golang.org/x/text v0.10.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
@@ -2522,7 +2515,6 @@ golang.org/x/tools v0.0.0-20191010075000-0337d82405ff/go.mod h1:b+2E5dAYhXwXZwtn
golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191029041327-9cc4af7d6b2c/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191029190741-b9c20aec41a5/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191108193012-7d206e10da11/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191112195655-aa38f8e97acc/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191113191852-77e3bb0ad9e7/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191115202509-3a792d9c32b2/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
@@ -2575,8 +2567,8 @@ golang.org/x/tools v0.1.10/go.mod h1:Uh6Zz+xoGYZom868N8YTex3t7RhtHDBrE8Gzo9bV56E
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
golang.org/x/tools v0.3.0/go.mod h1:/rWhSS2+zyEVwoJf8YAX6L2f0ntZ7Kn/mGgAWcipA5k=
golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
golang.org/x/tools v0.9.1 h1:8WMNJAz3zrtPmnYC7ISf5dEn3MT0gY7jBJfw27yrrLo=
golang.org/x/tools v0.9.1/go.mod h1:owI94Op576fPu3cIGQeHs3joujW/2Oc6MtlxbF5dfNc=
golang.org/x/tools v0.8.0 h1:vSDcovVPld282ceKgDimkRSC8kpaH1dgyc9UMzlt84Y=
golang.org/x/tools v0.8.0/go.mod h1:JxBZ99ISMI5ViVkT1tr6tdNmXeTrcpVSD3vZ1RsRdN4=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
@@ -2658,8 +2650,8 @@ google.golang.org/api v0.99.0/go.mod h1:1YOf74vkVndF7pG6hIHuINsM7eWwpVTAfNMNiL91
google.golang.org/api v0.100.0/go.mod h1:ZE3Z2+ZOr87Rx7dqFsdRQkRBk36kDtp/h+QpHbB7a70=
google.golang.org/api v0.102.0/go.mod h1:3VFl6/fzoA+qNuS1N1/VfXY4LjoXN/wzeIp7TweWwGo=
google.golang.org/api v0.103.0/go.mod h1:hGtW6nK1AC+d9si/UBhw8Xli+QMOf6xyNAyJw4qU9w0=
google.golang.org/api v0.126.0 h1:q4GJq+cAdMAC7XP7njvQ4tvohGLiSlytuL4BQxbIZ+o=
google.golang.org/api v0.126.0/go.mod h1:mBwVAtz+87bEN6CbA1GtZPDOqY2R5ONPqJeIlvyo4Aw=
google.golang.org/api v0.122.0 h1:zDobeejm3E7pEG1mNHvdxvjs5XJoCMzyNH+CmwL94Es=
google.golang.org/api v0.122.0/go.mod h1:gcitW0lvnyWjSp9nKxAbdHKIZ6vF4aajGueeslZOyms=
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
google.golang.org/appengine v1.2.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
google.golang.org/appengine v1.3.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
@@ -2810,12 +2802,8 @@ google.golang.org/genproto v0.0.0-20221118155620-16455021b5e6/go.mod h1:rZS5c/ZV
google.golang.org/genproto v0.0.0-20221201164419-0e50fba7f41c/go.mod h1:rZS5c/ZVYMaOGBfO68GWtjOw/eLaZM1X6iVtgjZ+EWg=
google.golang.org/genproto v0.0.0-20221202195650-67e5cbc046fd/go.mod h1:cTsE614GARnxrLsqKREzmNYJACSWWpAWdNMwnD7c2BE=
google.golang.org/genproto v0.0.0-20230110181048-76db0878b65f/go.mod h1:RGgjbofJ8xD9Sq1VVhDM1Vok1vRONV+rg+CjzG4SZKM=
google.golang.org/genproto v0.0.0-20230530153820-e85fd2cbaebc h1:8DyZCyvI8mE1IdLy/60bS+52xfymkE72wv1asokgtao=
google.golang.org/genproto v0.0.0-20230530153820-e85fd2cbaebc/go.mod h1:xZnkP7mREFX5MORlOPEzLMr+90PPZQ2QWzrVTWfAq64=
google.golang.org/genproto/googleapis/api v0.0.0-20230530153820-e85fd2cbaebc h1:kVKPf/IiYSBWEWtkIn6wZXwWGCnLKcC8oWfZvXjsGnM=
google.golang.org/genproto/googleapis/api v0.0.0-20230530153820-e85fd2cbaebc/go.mod h1:vHYtlOoi6TsQ3Uk2yxR7NI5z8uoV+3pZtR4jmHIkRig=
google.golang.org/genproto/googleapis/rpc v0.0.0-20230530153820-e85fd2cbaebc h1:XSJ8Vk1SWuNr8S18z1NZSziL0CPIXLCCMDOEFtHBOFc=
google.golang.org/genproto/googleapis/rpc v0.0.0-20230530153820-e85fd2cbaebc/go.mod h1:66JfowdXAEgad5O9NnYcsNPLCPZJD++2L9X0PCMODrA=
google.golang.org/genproto v0.0.0-20230410155749-daa745c078e1 h1:KpwkzHKEF7B9Zxg18WzOa7djJ+Ha5DzthMyZYQfEn2A=
google.golang.org/genproto v0.0.0-20230410155749-daa745c078e1/go.mod h1:nKE/iIaLqn2bQwXBg8f1g2Ylh6r5MN5CmZvuzZCgsCU=
google.golang.org/grpc v1.54.0 h1:EhTqbhiYeixwWQtAEZAxmV9MGqcjEU2mFx52xCzNyag=
google.golang.org/grpc v1.54.0/go.mod h1:PUSEXI6iWghWaB6lXM4knEgpJNu2qUcKfDtNci3EC2g=
google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.1.0/go.mod h1:6Kw0yEErY5E/yWrBtf03jp27GLLJujG4z/JK95pnjjw=
@@ -2837,8 +2825,8 @@ google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQ
google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
google.golang.org/protobuf v1.28.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
google.golang.org/protobuf v1.28.1/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
google.golang.org/protobuf v1.31.0 h1:g0LDEJHgrBl9N9r17Ru3sqWhkIx2NB67okBHPwC7hs8=
google.golang.org/protobuf v1.31.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
google.golang.org/protobuf v1.30.0 h1:kPPoIgf3TsEvrm0PFe15JQ+570QVxYzEvvHqChK+cng=
google.golang.org/protobuf v1.30.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=
gopkg.in/alexcesaro/statsd.v2 v2.0.0 h1:FXkZSCZIH17vLCO5sO2UucTHsH9pc+17F6pl3JVCwMc=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
@@ -2890,10 +2878,9 @@ gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C
gopkg.in/yaml.v3 v3.0.0-20200605160147-a5ece683394c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.0/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gotest.tools/v3 v3.5.0 h1:Ljk6PdHdOhAb5aDMWXjDLMMhph+BpztA4v1QdqEW2eY=
gotest.tools/v3 v3.4.0 h1:ZazjZUfuVeZGLAmlKKuyv3IKP5orXcwtOwDQH6YVr6o=
grpc.go4.org v0.0.0-20170609214715-11d0a25b4919/go.mod h1:77eQGdRu53HpSqPFJFmuJdjuHRquDANNeA4x7B8WQ9o=
helm.sh/helm/v3 v3.11.1 h1:cmL9fFohOoNQf+wnp2Wa0OhNFH0KFnSzEkVxi3fcc3I=
helm.sh/helm/v3 v3.11.1/go.mod h1:z/Bu/BylToGno/6dtNGuSmjRqxKq5gaH+FU0BPO+AQ8=
@@ -2905,37 +2892,37 @@ honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt
honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k=
honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k=
honnef.co/go/tools v0.0.1-2020.1.5/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k=
k8s.io/api v0.27.4 h1:0pCo/AN9hONazBKlNUdhQymmnfLRbSZjd5H5H3f0bSs=
k8s.io/api v0.27.4/go.mod h1:O3smaaX15NfxjzILfiln1D8Z3+gEYpjEpiNA/1EVK1Y=
k8s.io/apiextensions-apiserver v0.27.2 h1:iwhyoeS4xj9Y7v8YExhUwbVuBhMr3Q4bd/laClBV6Bo=
k8s.io/apiextensions-apiserver v0.27.2/go.mod h1:Oz9UdvGguL3ULgRdY9QMUzL2RZImotgxvGjdWRq6ZXQ=
k8s.io/apimachinery v0.27.4 h1:CdxflD4AF61yewuid0fLl6bM4a3q04jWel0IlP+aYjs=
k8s.io/apimachinery v0.27.4/go.mod h1:XNfZ6xklnMCOGGFNqXG7bUrQCoR04dh/E7FprV6pb+E=
k8s.io/client-go v0.27.4 h1:vj2YTtSJ6J4KxaC88P4pMPEQECWMY8gqPqsTgUKzvjk=
k8s.io/client-go v0.27.4/go.mod h1:ragcly7lUlN0SRPk5/ZkGnDjPknzb37TICq07WhI6Xc=
k8s.io/klog/v2 v2.100.1 h1:7WCHKK6K8fNhTqfBhISHQ97KrnJNFZMcQvKp7gP/tmg=
k8s.io/klog/v2 v2.100.1/go.mod h1:y1WjHnz7Dj687irZUWR/WLkLc5N1YHtjLdmgWjndZn0=
k8s.io/kube-openapi v0.0.0-20230501164219-8b0f38b5fd1f h1:2kWPakN3i/k81b0gvD5C5FJ2kxm1WrQFanWchyKuqGg=
k8s.io/kube-openapi v0.0.0-20230501164219-8b0f38b5fd1f/go.mod h1:byini6yhqGC14c3ebc/QwanvYwhuMWF6yz2F8uwW8eg=
k8s.io/utils v0.0.0-20230726121419-3b25d923346b h1:sgn3ZU783SCgtaSJjpcVVlRqd6GSnlTLKgpAAttJvpI=
k8s.io/utils v0.0.0-20230726121419-3b25d923346b/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0=
k8s.io/api v0.26.2 h1:dM3cinp3PGB6asOySalOZxEG4CZ0IAdJsrYZXE/ovGQ=
k8s.io/api v0.26.2/go.mod h1:1kjMQsFE+QHPfskEcVNgL3+Hp88B80uj0QtSOlj8itU=
k8s.io/apiextensions-apiserver v0.26.0 h1:Gy93Xo1eg2ZIkNX/8vy5xviVSxwQulsnUdQ00nEdpDo=
k8s.io/apiextensions-apiserver v0.26.0/go.mod h1:7ez0LTiyW5nq3vADtK6C3kMESxadD51Bh6uz3JOlqWQ=
k8s.io/apimachinery v0.26.2 h1:da1u3D5wfR5u2RpLhE/ZtZS2P7QvDgLZTi9wrNZl/tQ=
k8s.io/apimachinery v0.26.2/go.mod h1:ats7nN1LExKHvJ9TmwootT00Yz05MuYqPXEXaVeOy5I=
k8s.io/client-go v0.26.2 h1:s1WkVujHX3kTp4Zn4yGNFK+dlDXy1bAAkIl+cFAiuYI=
k8s.io/client-go v0.26.2/go.mod h1:u5EjOuSyBa09yqqyY7m3abZeovO/7D/WehVVlZ2qcqU=
k8s.io/klog/v2 v2.90.1 h1:m4bYOKall2MmOiRaR1J+We67Do7vm9KiQVlT96lnHUw=
k8s.io/klog/v2 v2.90.1/go.mod h1:y1WjHnz7Dj687irZUWR/WLkLc5N1YHtjLdmgWjndZn0=
k8s.io/kube-openapi v0.0.0-20221012153701-172d655c2280 h1:+70TFaan3hfJzs+7VK2o+OGxg8HsuBr/5f6tVAjDu6E=
k8s.io/kube-openapi v0.0.0-20221012153701-172d655c2280/go.mod h1:+Axhij7bCpeqhklhUTe3xmOn6bWxolyZEeyaFpjGtl4=
k8s.io/utils v0.0.0-20230220204549-a5ecb0141aa5 h1:kmDqav+P+/5e1i9tFfHq1qcF3sOrDp+YEkVDAHu7Jwk=
k8s.io/utils v0.0.0-20230220204549-a5ecb0141aa5/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0=
pack.ag/amqp v0.11.2/go.mod h1:4/cbmt4EJXSKlG6LCfWHoqmN0uFdy5i/+YFz+fTfhV4=
rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8=
rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4=
rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0=
rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA=
sigs.k8s.io/controller-runtime v0.15.0 h1:ML+5Adt3qZnMSYxZ7gAverBLNPSMQEibtzAgp0UPojU=
sigs.k8s.io/controller-runtime v0.15.0/go.mod h1:7ngYvp1MLT+9GeZ+6lH3LOlcHkp/+tzA/fmHa4iq9kk=
sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd h1:EDPBXCAspyGV4jQlpZSudPeMmr1bNJefnuqLsRAsHZo=
sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd/go.mod h1:B8JuhiUyNFVKdsE8h686QcCxMaH6HrOAZj4vswFpcB0=
sigs.k8s.io/controller-runtime v0.12.3 h1:FCM8xeY/FI8hoAfh/V4XbbYMY20gElh9yh+A98usMio=
sigs.k8s.io/controller-runtime v0.12.3/go.mod h1:qKsk4WE6zW2Hfj0G4v10EnNB2jMG1C+NTb8h+DwCoU0=
sigs.k8s.io/json v0.0.0-20220713155537-f223a00ba0e2 h1:iXTIw73aPyC+oRdyqqvVJuloN1p0AC/kzH07hu3NE+k=
sigs.k8s.io/json v0.0.0-20220713155537-f223a00ba0e2/go.mod h1:B8JuhiUyNFVKdsE8h686QcCxMaH6HrOAZj4vswFpcB0=
sigs.k8s.io/kustomize/api v0.12.1 h1:7YM7gW3kYBwtKvoY216ZzY+8hM+lV53LUayghNRJ0vM=
sigs.k8s.io/kustomize/api v0.12.1/go.mod h1:y3JUhimkZkR6sbLNwfJHxvo1TCLwuwm14sCYnkH6S1s=
sigs.k8s.io/kustomize/kyaml v0.13.9 h1:Qz53EAaFFANyNgyOEJbT/yoIHygK40/ZcvU3rgry2Tk=
sigs.k8s.io/kustomize/kyaml v0.13.9/go.mod h1:QsRbD0/KcU+wdk0/L0fIp2KLnohkVzs6fQ85/nOXac4=
sigs.k8s.io/release-utils v0.7.3 h1:6pS8x6c5RmdUgR9qcg1LO6hjUzuE4Yo9TGZ3DemrZdM=
sigs.k8s.io/release-utils v0.7.3/go.mod h1:n0mVez/1PZYZaZUTJmxewxH3RJ/Lf7JUDh7TG1CASOE=
sigs.k8s.io/structured-merge-diff/v4 v4.3.0 h1:UZbZAZfX0wV2zr7YZorDz6GXROfDFj6LvqCRm4VUVKk=
sigs.k8s.io/structured-merge-diff/v4 v4.3.0/go.mod h1:N8hJocpFajUSSeSJ9bOZ77VzejKZaXsTtZo4/u7Io08=
sigs.k8s.io/structured-merge-diff/v4 v4.2.3 h1:PRbqxJClWWYMNV1dhaG4NsibJbArud9kFxnAMREiWFE=
sigs.k8s.io/structured-merge-diff/v4 v4.2.3/go.mod h1:qjx8mGObPmV2aSZepjQjbmb2ihdVs8cGKBraizNC69E=
sigs.k8s.io/yaml v1.1.0/go.mod h1:UJmg0vDUVViEyp3mgSv9WPwZCDxu4rQW1olrI1uml+o=
sigs.k8s.io/yaml v1.2.0/go.mod h1:yfXDCHCao9+ENCvLSE62v9VSji2MKu5jeNfTrofGhJc=
sigs.k8s.io/yaml v1.3.0 h1:a2VclLzOGrwOHDiV8EfBGhvjHvP46CtW5j6POvhYGGo=

View File

@@ -56,6 +56,16 @@ func ToScanInfo(scanRequest *utilsmetav1.PostScanRequest) *cautils.ScanInfo {
scanInfo.HostSensorEnabled = cautils.NewBoolPtr(scanRequest.HostScanner)
}
// workload scan
if scanRequest.Workload != nil {
scanInfo.WorkloadIdentifier = &cautils.WorkloadIdentifier{
Kind: scanRequest.Workload.Kind,
Name: scanRequest.Workload.Name,
Namespace: scanRequest.Workload.Namespace,
ApiVersion: scanRequest.Workload.ApiVersion,
}
}
return scanInfo
}

View File

@@ -57,6 +57,23 @@ func TestToScanInfo(t *testing.T) {
s := ToScanInfo(req)
assert.True(t, s.ScanAll)
assert.True(t, s.FrameworkScan)
assert.Nil(t, s.WorkloadIdentifier)
}
{
req := &utilsmetav1.PostScanRequest{
Workload: &apisv1.WorkloadScan{
ApiVersion: "apps/v1",
Kind: "Deployment",
Name: "nginx",
Namespace: "ns1",
},
}
s := ToScanInfo(req)
assert.NotNil(t, s.WorkloadIdentifier)
assert.Equal(t, "apps/v1", s.WorkloadIdentifier.ApiVersion)
assert.Equal(t, "Deployment", s.WorkloadIdentifier.Kind)
assert.Equal(t, "nginx", s.WorkloadIdentifier.Name)
assert.Equal(t, "ns1", s.WorkloadIdentifier.Namespace)
}
}

View File

@@ -118,7 +118,6 @@ echo -e "\033[35mUsage: $ $KUBESCAPE_EXEC scan"
if [ "$(id -u)" -ne 0 ]; then
echo -e "\nRemember to add the Kubescape CLI to your path with:"
echo -e " export PATH=\$PATH:$BASE_DIR/bin"
export PATH=\$PATH:$BASE_DIR/bin
fi
echo -e "\033[0m"

1
pkg/imagescan/doc.go Normal file
View File

@@ -0,0 +1 @@
package imagescan

176
pkg/imagescan/imagescan.go Normal file
View File

@@ -0,0 +1,176 @@
package imagescan
import (
"context"
"errors"
"fmt"
"path/filepath"
"github.com/adrg/xdg"
"github.com/anchore/grype/grype"
"github.com/anchore/grype/grype/db"
"github.com/anchore/grype/grype/grypeerr"
"github.com/anchore/grype/grype/matcher"
"github.com/anchore/grype/grype/matcher/dotnet"
"github.com/anchore/grype/grype/matcher/golang"
"github.com/anchore/grype/grype/matcher/java"
"github.com/anchore/grype/grype/matcher/javascript"
"github.com/anchore/grype/grype/matcher/python"
"github.com/anchore/grype/grype/matcher/ruby"
"github.com/anchore/grype/grype/matcher/stock"
"github.com/anchore/grype/grype/pkg"
"github.com/anchore/grype/grype/presenter/models"
"github.com/anchore/grype/grype/store"
"github.com/anchore/grype/grype/vulnerability"
"github.com/anchore/stereoscope/pkg/image"
"github.com/anchore/syft/syft/pkg/cataloger"
)
const (
defaultGrypeListingURL = "https://toolbox-data.anchore.io/grype/databases/listing.json"
defaultDBDirName = "grypedb"
)
type RegistryCredentials struct {
Username string
Password string
}
func (c RegistryCredentials) IsEmpty() bool {
return c.Username == "" || c.Password == ""
}
// ExceedsSeverityThreshold returns true if vulnerabilities in the scan results exceed the severity threshold, false otherwise.
//
// Values equal to the threshold are considered failing, too.
func ExceedsSeverityThreshold(scanResults *models.PresenterConfig, severity vulnerability.Severity) bool {
return grype.HasSeverityAtOrAbove(scanResults.MetadataProvider, severity, scanResults.Matches)
}
func NewDefaultDBConfig() (db.Config, bool) {
dir := filepath.Join(xdg.CacheHome, defaultDBDirName)
url := defaultGrypeListingURL
shouldUpdate := true
return db.Config{
DBRootDir: dir,
ListingURL: url,
}, shouldUpdate
}
func getMatchers() []matcher.Matcher {
return matcher.NewDefaultMatchers(
matcher.Config{
Java: java.MatcherConfig{
ExternalSearchConfig: java.ExternalSearchConfig{MavenBaseURL: "https://search.maven.org/solrsearch/select"},
UseCPEs: true,
},
Ruby: ruby.MatcherConfig{UseCPEs: true},
Python: python.MatcherConfig{UseCPEs: true},
Dotnet: dotnet.MatcherConfig{UseCPEs: true},
Javascript: javascript.MatcherConfig{UseCPEs: true},
Golang: golang.MatcherConfig{UseCPEs: true},
Stock: stock.MatcherConfig{UseCPEs: true},
},
)
}
func validateDBLoad(loadErr error, status *db.Status) error {
if loadErr != nil {
return fmt.Errorf("failed to load vulnerability db: %w", loadErr)
}
if status == nil {
return fmt.Errorf("unable to determine the status of the vulnerability db")
}
if status.Err != nil {
return fmt.Errorf("db could not be loaded: %w", status.Err)
}
return nil
}
func getProviderConfig(creds RegistryCredentials) pkg.ProviderConfig {
syftCreds := []image.RegistryCredentials{{Username: creds.Username, Password: creds.Password}}
regOpts := &image.RegistryOptions{
Credentials: syftCreds,
}
catOpts := cataloger.DefaultConfig()
pc := pkg.ProviderConfig{
SyftProviderConfig: pkg.SyftProviderConfig{
RegistryOptions: regOpts,
CatalogingOptions: catOpts,
// Platform: appConfig.Platform,
// Name: appConfig.Name,
// DefaultImagePullSource: appConfig.DefaultImagePullSource,
},
SynthesisConfig: pkg.SynthesisConfig{
GenerateMissingCPEs: true,
},
}
return pc
}
// Service is a facade for image scanning functionality.
//
// It performs image scanning and everything needed in between.
type Service struct {
dbCfg db.Config
}
func (s *Service) Scan(ctx context.Context, userInput string, creds RegistryCredentials) (*models.PresenterConfig, error) {
var err error
store, status, dbCloser, err := grype.LoadVulnerabilityDB(s.dbCfg, true)
if err = validateDBLoad(err, status); err != nil {
return nil, err
}
packages, pkgContext, sbom, err := pkg.Provide(userInput, getProviderConfig(creds))
if err != nil {
return nil, err
}
if dbCloser != nil {
defer dbCloser.Close()
}
// applyDistroHint(packages, &pkgContext, appConfig)
matcher := grype.VulnerabilityMatcher{
Store: *store,
Matchers: getMatchers(),
}
remainingMatches, ignoredMatches, err := matcher.FindMatches(packages, pkgContext)
if err != nil {
if !errors.Is(err, grypeerr.ErrAboveSeverityThreshold) {
return nil, err
}
}
pb := models.PresenterConfig{
Matches: *remainingMatches,
IgnoredMatches: ignoredMatches,
Packages: packages,
Context: pkgContext,
MetadataProvider: store,
SBOM: sbom,
AppConfig: nil,
DBStatus: status,
}
return &pb, nil
}
func NewVulnerabilityDB(cfg db.Config, update bool) (*store.Store, *db.Status, *db.Closer, error) {
return grype.LoadVulnerabilityDB(cfg, update)
}
func NewScanService(dbCfg db.Config) Service {
return Service{dbCfg: dbCfg}
}
// ParseSeverity returns a Grype severity given a severity string
//
// Used as a thin wrapper for ease of access from one image scan package
func ParseSeverity(severity string) vulnerability.Severity {
return vulnerability.ParseSeverity(severity)
}

View File

@@ -0,0 +1,221 @@
package imagescan
import (
"context"
"testing"
"github.com/anchore/grype/grype/db"
grypedb "github.com/anchore/grype/grype/db/v5"
"github.com/anchore/grype/grype/match"
"github.com/anchore/grype/grype/pkg"
"github.com/anchore/grype/grype/presenter/models"
"github.com/anchore/grype/grype/vulnerability"
syftPkg "github.com/anchore/syft/syft/pkg"
"github.com/google/uuid"
"github.com/stretchr/testify/assert"
)
func TestNewScanService(t *testing.T) {
dbCfg, _ := NewDefaultDBConfig()
svc := NewScanService(dbCfg)
assert.IsType(t, Service{}, svc)
}
func TestRegistryCredentials(t *testing.T) {
tt := []struct {
name string
username string
password string
want bool
}{
{
name: "Valid credentials should not be empty",
username: "user",
password: "pass",
want: false,
},
{
name: "Empty username should be considered empty credentials",
username: "",
password: "pass",
want: true,
},
{
name: "Empty password should be considered empty credentials",
username: "user",
password: "",
want: true,
},
{
name: "Empty user and password should be considered empty credentials",
username: "",
password: "",
want: true,
},
}
for _, tc := range tt {
t.Run(tc.name, func(t *testing.T) {
creds := RegistryCredentials{Username: tc.username, Password: tc.password}
got := creds.IsEmpty()
assert.Equal(t, tc.want, got)
})
}
}
func TestScan(t *testing.T) {
tt := []struct {
name string
image string
creds RegistryCredentials
}{
{
name: "Valid image name produces a non-nil scan result",
image: "nginx",
},
{
name: "Scanning a valid image with provided credentials should produce a non-nil scan result",
image: "nginx",
creds: RegistryCredentials{
Username: "test",
Password: "password",
},
},
}
for _, tc := range tt {
t.Run(tc.name, func(t *testing.T) {
ctx := context.Background()
dbCfg, _ := NewDefaultDBConfig()
svc := NewScanService(dbCfg)
creds := RegistryCredentials{}
scanResults, err := svc.Scan(ctx, tc.image, creds)
assert.NoError(t, err)
assert.IsType(t, &models.PresenterConfig{}, scanResults)
})
}
}
// fakeMetaProvider is a test double that fakes an actual MetadataProvider
type fakeMetaProvider struct {
vulnerabilities map[string]map[string][]grypedb.Vulnerability
metadata map[string]map[string]*grypedb.VulnerabilityMetadata
}
func newFakeMetaProvider() *fakeMetaProvider {
d := fakeMetaProvider{
vulnerabilities: make(map[string]map[string][]grypedb.Vulnerability),
metadata: make(map[string]map[string]*grypedb.VulnerabilityMetadata),
}
d.fillWithData()
return &d
}
func (d *fakeMetaProvider) GetAllVulnerabilityMetadata() (*[]grypedb.VulnerabilityMetadata, error) {
return nil, nil
}
func (d *fakeMetaProvider) GetVulnerabilityMatchExclusion(id string) ([]grypedb.VulnerabilityMatchExclusion, error) {
return nil, nil
}
func (d *fakeMetaProvider) GetVulnerabilityMetadata(id, namespace string) (*grypedb.VulnerabilityMetadata, error) {
return d.metadata[id][namespace], nil
}
func (d *fakeMetaProvider) fillWithData() {
d.metadata["CVE-2014-fake-1"] = map[string]*grypedb.VulnerabilityMetadata{
"debian:distro:debian:8": {
ID: "CVE-2014-fake-1",
Namespace: "debian:distro:debian:8",
Severity: "medium",
},
}
d.vulnerabilities["debian:distro:debian:8"] = map[string][]grypedb.Vulnerability{
"neutron": {
{
PackageName: "neutron",
Namespace: "debian:distro:debian:8",
VersionConstraint: "< 2014.1.3-6",
ID: "CVE-2014-fake-1",
VersionFormat: "deb",
},
},
}
}
func TestExceedsSeverityThreshold(t *testing.T) {
thePkg := pkg.Package{
ID: pkg.ID(uuid.NewString()),
Name: "the-package",
Version: "v0.1",
Type: syftPkg.RpmPkg,
}
matches := match.NewMatches()
matches.Add(match.Match{
Vulnerability: vulnerability.Vulnerability{
ID: "CVE-2014-fake-1",
Namespace: "debian:distro:debian:8",
},
Package: thePkg,
Details: match.Details{
{
Type: match.ExactDirectMatch,
},
},
})
tt := []struct {
name string
failOnSeverity string
matches match.Matches
expectedResult bool
}{
{
name: "No severity set should pass",
failOnSeverity: "",
matches: matches,
expectedResult: false,
},
{
name: "Fail severity higher than vulnerability should not fail",
failOnSeverity: "high",
matches: matches,
expectedResult: false,
},
{
name: "Fail severity equal to vulnerability should fail",
failOnSeverity: "medium",
matches: matches,
expectedResult: true,
},
{
name: "Fail severity below found vuln should fail",
failOnSeverity: "low",
matches: matches,
expectedResult: true,
},
}
metadataProvider := db.NewVulnerabilityMetadataProvider(newFakeMetaProvider())
for _, tc := range tt {
t.Run(tc.name, func(t *testing.T) {
scanResults := &models.PresenterConfig{
Matches: tc.matches,
MetadataProvider: metadataProvider,
}
inputSeverity := vulnerability.ParseSeverity(tc.failOnSeverity)
ours := ExceedsSeverityThreshold(scanResults, inputSeverity)
assert.Equal(t, tc.expectedResult, ours)
})
}
}