mirror of
https://github.com/kubescape/kubescape.git
synced 2026-02-14 18:09:55 +00:00
Compare commits
45 Commits
master
...
new-output
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b3f664822c | ||
|
|
e829af7205 | ||
|
|
e4bafad67b | ||
|
|
dd8f624f0c | ||
|
|
e794ab8b45 | ||
|
|
05cb1abec5 | ||
|
|
e972df933a | ||
|
|
d740ba3ed2 | ||
|
|
2121b20076 | ||
|
|
27482a9067 | ||
|
|
5b61611789 | ||
|
|
6cefd56559 | ||
|
|
c98b696a9c | ||
|
|
facbc4749a | ||
|
|
e57d65541c | ||
|
|
6679ac54ea | ||
|
|
dd3b5bf5cf | ||
|
|
811914a0ff | ||
|
|
6341947142 | ||
|
|
aab9cd5ff9 | ||
|
|
0df62cb8d3 | ||
|
|
3c320035ad | ||
|
|
3177ab47f0 | ||
|
|
755d8c3bb0 | ||
|
|
e67fcae4aa | ||
|
|
03b69bfacc | ||
|
|
219b32d874 | ||
|
|
315fe799e7 | ||
|
|
5c6e66d9b7 | ||
|
|
7f812f2c9a | ||
|
|
eab88f0184 | ||
|
|
557c063f69 | ||
|
|
e1f7e06d45 | ||
|
|
ab634debe4 | ||
|
|
ad0103c50a | ||
|
|
46e1cea203 | ||
|
|
57522160a0 | ||
|
|
4ecd366115 | ||
|
|
4253005485 | ||
|
|
dcfcc1dbea | ||
|
|
5eb9c29905 | ||
|
|
614c5ea17f | ||
|
|
6b643f36d8 | ||
|
|
f54bfabacc | ||
|
|
fac2f7bec1 |
@@ -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
106
cmd/scan/image.go
Normal 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
|
||||
}
|
||||
@@ -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
110
cmd/scan/workload.go
Normal 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
|
||||
}
|
||||
@@ -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{}
|
||||
|
||||
48
core/cautils/datastructures_test.go
Normal file
48
core/cautils/datastructures_test.go
Normal 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)
|
||||
// }
|
||||
// }
|
||||
// })
|
||||
// }
|
||||
// }
|
||||
@@ -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])
|
||||
}
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
|
||||
@@ -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 program’s 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())
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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())
|
||||
|
||||
@@ -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
@@ -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
|
||||
|
||||
@@ -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))
|
||||
|
||||
@@ -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{}
|
||||
|
||||
|
||||
@@ -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, ",")
|
||||
}
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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 ""
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
100
core/pkg/resourcehandler/filesloaderutils_test.go
Normal file
100
core/pkg/resourcehandler/filesloaderutils_test.go
Normal 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())
|
||||
}
|
||||
})
|
||||
|
||||
}
|
||||
}
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
@@ -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")
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
|
||||
56
core/pkg/resourcehandler/queryableresource.go
Normal file
56
core/pkg/resourcehandler/queryableresource.go
Normal 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
|
||||
}
|
||||
126
core/pkg/resourcehandler/queryableresource_test.go
Normal file
126
core/pkg/resourcehandler/queryableresource_test.go
Normal 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", ©) {
|
||||
t.Errorf("pointers of original object and copy should not be same. object: %p, copy: %p", rsrc, ©)
|
||||
}
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
}
|
||||
@@ -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 {
|
||||
|
||||
172
core/pkg/resourcehandler/resourcehandlerutils.go
Normal file
172
core/pkg/resourcehandler/resourcehandlerutils.go
Normal 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)
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
591
core/pkg/resourcehandler/resourcehandlerutils_test.go
Normal file
591
core/pkg/resourcehandler/resourcehandlerutils_test.go
Normal 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)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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() {
|
||||
|
||||
}
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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 aren’t 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 aren’t 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++ {
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
@@ -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)
|
||||
}
|
||||
@@ -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)
|
||||
}
|
||||
@@ -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()
|
||||
}
|
||||
@@ -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))
|
||||
}
|
||||
}
|
||||
@@ -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")
|
||||
}
|
||||
@@ -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))
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -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())
|
||||
}
|
||||
@@ -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)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -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,
|
||||
}
|
||||
@@ -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,
|
||||
)
|
||||
}
|
||||
@@ -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)
|
||||
}
|
||||
@@ -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, ","))
|
||||
}
|
||||
@@ -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)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
@@ -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))
|
||||
}
|
||||
}
|
||||
@@ -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)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -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}
|
||||
}
|
||||
@@ -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}))
|
||||
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
package imageprinter
|
||||
|
||||
import "io"
|
||||
|
||||
type TablePrinter interface {
|
||||
PrintImageScanningTable(io.Writer, ImageScanSummary)
|
||||
}
|
||||
@@ -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)
|
||||
}
|
||||
@@ -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}
|
||||
}
|
||||
@@ -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])
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
package utils
|
||||
|
||||
import "testing"
|
||||
|
||||
func TestGetColor(t *testing.T) {
|
||||
|
||||
}
|
||||
|
||||
func TestImageSeverityToInt(t *testing.T) {
|
||||
|
||||
}
|
||||
219
core/pkg/resultshandling/printer/v2/prettyprinter/utils.go
Normal file
219
core/pkg/resultshandling/printer/v2/prettyprinter/utils.go
Normal 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)
|
||||
}
|
||||
368
core/pkg/resultshandling/printer/v2/prettyprinter/utils_test.go
Normal file
368
core/pkg/resultshandling/printer/v2/prettyprinter/utils_test.go
Normal 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)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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)
|
||||
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
package printer
|
||||
@@ -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)
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
480
core/pkg/resultshandling/printer/v2/utils_test.go
Normal file
480
core/pkg/resultshandling/printer/v2/utils_test.go
Normal 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)
|
||||
// }
|
||||
// }
|
||||
// })
|
||||
// }
|
||||
// }
|
||||
@@ -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,
|
||||
|
||||
@@ -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"
|
||||
],
|
||||
|
||||
@@ -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 result’s risk score
|
||||
func (rh *ResultsHandler) GetRiskScore() float32 {
|
||||
return rh.scanData.Report.SummaryDetails.Score
|
||||
return rh.ScanData.Report.SummaryDetails.Score
|
||||
}
|
||||
|
||||
// GetComplianceScore returns the result’s 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)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
|
||||
|
||||
@@ -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
|
||||
@@ -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
152
go.mod
@@ -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
|
||||
)
|
||||
|
||||
@@ -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
|
||||
)
|
||||
|
||||
@@ -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=
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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
1
pkg/imagescan/doc.go
Normal file
@@ -0,0 +1 @@
|
||||
package imagescan
|
||||
176
pkg/imagescan/imagescan.go
Normal file
176
pkg/imagescan/imagescan.go
Normal 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)
|
||||
}
|
||||
221
pkg/imagescan/imagescan_test.go
Normal file
221
pkg/imagescan/imagescan_test.go
Normal 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)
|
||||
})
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user