Compare commits

..

1 Commits

Author SHA1 Message Date
Matthias Bertschy
7ae2d3646b override default worker pool size with KUBESCAPE_WORKERS
Signed-off-by: Matthias Bertschy <matthias.bertschy@gmail.com>
2025-03-07 18:13:29 +01:00
99 changed files with 3670 additions and 6176 deletions

View File

@@ -1,7 +1,6 @@
name: 00-pr_scanner
permissions: read-all
on:
workflow_dispatch: {}
pull_request:
types: [opened, reopened, synchronize, ready_for_review]
paths-ignore:
@@ -27,7 +26,6 @@ jobs:
deployments: read
id-token: write
issues: read
models: read
discussions: read
packages: read
pages: read
@@ -55,7 +53,6 @@ jobs:
discussions: read
id-token: write
issues: read
models: read
packages: write
pages: read
pull-requests: read
@@ -68,7 +65,7 @@ jobs:
COMPONENT_NAME: kubescape
CGO_ENABLED: 0
GO111MODULE: ""
GO_VERSION: "1.24"
GO_VERSION: "1.23"
RELEASE: "latest"
CLIENT: test
secrets: inherit

View File

@@ -8,7 +8,7 @@ jobs:
retag:
outputs:
NEW_TAG: ${{ steps.tag-calculator.outputs.NEW_TAG }}
runs-on: ubuntu-large
runs-on: ubuntu22-core4-mem16-ssd150
steps:
- uses: actions/checkout@v4
- id: tag-calculator
@@ -23,7 +23,6 @@ jobs:
discussions: read
id-token: write
issues: read
models: read
packages: write
pages: read
pull-requests: read
@@ -38,7 +37,7 @@ jobs:
COMPONENT_NAME: kubescape
CGO_ENABLED: 0
GO111MODULE: ""
GO_VERSION: "1.24"
GO_VERSION: "1.23"
RELEASE: ${{ needs.retag.outputs.NEW_TAG }}
CLIENT: release
secrets: inherit
@@ -51,7 +50,6 @@ jobs:
discussions: read
id-token: write
issues: read
models: read
packages: read
pages: read
pull-requests: read
@@ -74,7 +72,6 @@ jobs:
discussions: read
id-token: write
issues: read
models: read
packages: write
pages: read
pull-requests: read
@@ -100,7 +97,6 @@ jobs:
discussions: read
id-token: write
issues: read
models: read
packages: write
pages: read
pull-requests: read
@@ -110,7 +106,7 @@ jobs:
attestations: read
contents: write
uses: ./.github/workflows/e-post-release.yaml
needs: [retag, publish-image]
needs: [publish-image]
with:
TAG: ${{ needs.retag.outputs.NEW_TAG }}
secrets: inherit

View File

@@ -27,7 +27,7 @@ jobs:
name: Create cross-platform build
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
runs-on: ubuntu-large
runs-on: ubuntu22-core4-mem16-ssd150
steps:
- uses: actions/checkout@v4
@@ -48,10 +48,10 @@ jobs:
run: ${{ env.DOCKER_CMD }} sh -c 'cd httphandler && go test -v ./...'
if: startsWith(github.ref, 'refs/tags')
- uses: anchore/sbom-action/download-syft@v0
- uses: anchore/sbom-action/download-syft@v0.15.2
name: Setup Syft
- uses: goreleaser/goreleaser-action@v6
- uses: goreleaser/goreleaser-action@v5
name: Build
with:
distribution: goreleaser
@@ -70,18 +70,20 @@ jobs:
- name: golangci-lint
continue-on-error: false
uses: golangci/golangci-lint-action@v8
uses: golangci/golangci-lint-action@v3
with:
version: v2.1
version: latest
args: --timeout 10m
only-new-issues: true
skip-pkg-cache: true
skip-build-cache: true
scanners:
env:
GITGUARDIAN_API_KEY: ${{ secrets.GITGUARDIAN_API_KEY }}
SNYK_TOKEN: ${{ secrets.SNYK_TOKEN }}
name: PR Scanner
runs-on: ubuntu-large
runs-on: ubuntu22-core4-mem16-ssd150
steps:
- uses: actions/checkout@v4
with:
@@ -90,7 +92,7 @@ jobs:
- uses: actions/setup-go@v4
name: Installing go
with:
go-version: "1.24"
go-version: "1.23"
- name: Scanning - Forbidden Licenses (go-licenses)
id: licenses-scan
continue-on-error: true

View File

@@ -18,7 +18,7 @@ on:
GO_VERSION:
required: false
type: string
default: "1.24"
default: "1.23"
GO111MODULE:
required: false
type: string
@@ -70,7 +70,7 @@ on:
type: string
GO_VERSION:
type: string
default: "1.24"
default: "1.23"
GO111MODULE:
required: true
type: string
@@ -187,7 +187,7 @@ jobs:
- name: (debug) Step 6 - Check disk space before goreleaser
run: df -h
- uses: goreleaser/goreleaser-action@v6
- uses: goreleaser/goreleaser-action@v5
name: Build
with:
distribution: goreleaser
@@ -227,7 +227,7 @@ jobs:
name: Upload artifacts
with:
name: kubescape
path: dist/*
path: dist/kubescape*
if-no-files-found: error
- name: (debug) Step 10 - Check disk space after uploading artifacts
@@ -248,7 +248,7 @@ jobs:
CGO_ENABLED: 0
GO111MODULE: "on"
BUILD_PLATFORM: linux/amd64,linux/arm64
GO_VERSION: "1.24"
GO_VERSION: "1.23"
REQUIRED_TESTS: '[
"ks_microservice_create_2_cronjob_mitre_and_nsa_proxy",
"ks_microservice_triggering_with_cron_job",
@@ -308,7 +308,7 @@ jobs:
- uses: actions/setup-python@v4
with:
python-version: '3.9'
python-version: '3.8.13'
cache: 'pip'
- name: create env

View File

@@ -33,7 +33,7 @@ jobs:
CGO_ENABLED: 0
GO111MODULE: "on"
BUILD_PLATFORM: ${{ inputs.PLATFORMS && 'linux/amd64,linux/arm64' || 'linux/amd64' }}
GO_VERSION: "1.24"
GO_VERSION: "1.23"
REQUIRED_TESTS: '[]'
COSIGN: ${{ inputs.CO_SIGN }}
HELM_E2E_TEST: false

View File

@@ -61,26 +61,32 @@ jobs:
prerelease: false
fail_on_unmatched_files: true
files: |
./checksums.sha256
./kubescape-${{ env.MAC_OS }}
./kubescape-${{ env.MAC_OS }}.sbom
./kubescape-${{ env.MAC_OS }}.sha256
./kubescape-${{ env.MAC_OS }}.tar.gz
./kubescape-${{ env.UBUNTU_OS }}
./kubescape-${{ env.UBUNTU_OS }}.sbom
./kubescape-${{ env.UBUNTU_OS }}.sha256
./kubescape-${{ env.UBUNTU_OS }}.tar.gz
./kubescape-${{ env.WINDOWS_OS }}.exe
./kubescape-${{ env.WINDOWS_OS }}.exe.sbom
./kubescape-${{ env.WINDOWS_OS }}.exe.sha256
./kubescape-${{ env.WINDOWS_OS }}.tar.gz
./kubescape-arm64-${{ env.MAC_OS }}
./kubescape-arm64-${{ env.MAC_OS }}.sbom
./kubescape-arm64-${{ env.MAC_OS }}.sha256
./kubescape-arm64-${{ env.MAC_OS }}.tar.gz
./kubescape-arm64-${{ env.UBUNTU_OS }}
./kubescape-arm64-${{ env.UBUNTU_OS }}.sbom
./kubescape-arm64-${{ env.UBUNTU_OS }}.sha256
./kubescape-arm64-${{ env.UBUNTU_OS }}.tar.gz
./kubescape-arm64-${{ env.WINDOWS_OS }}.exe
./kubescape-arm64-${{ env.WINDOWS_OS }}.exe.sbom
./kubescape-arm64-${{ env.WINDOWS_OS }}.exe.sha256
./kubescape-arm64-${{ env.WINDOWS_OS }}.tar.gz
./kubescape-riscv64-${{ env.UBUNTU_OS }}
./kubescape-riscv64-${{ env.UBUNTU_OS }}.sbom
./kubescape-riscv64-${{ env.UBUNTU_OS }}.sha256
./kubescape-riscv64-${{ env.UBUNTU_OS }}.tar.gz
./kubescape.exe

View File

@@ -7,7 +7,6 @@ permissions:
discussions: read
id-token: write
issues: read
models: read
packages: read
pages: read
pull-requests: read

View File

@@ -19,7 +19,7 @@ jobs:
uses: rajatjindal/krew-release-bot@v0.0.47
if: github.repository_owner == 'kubescape'
env:
GITHUB_REF: refs/tags/${{ inputs.TAG }}
GITHUB_REF: ${{ inputs.TAG }}
- name: Invoke workflow to update packaging
uses: benc-uk/workflow-dispatch@v1
if: github.repository_owner == 'kubescape'

View File

@@ -1,57 +1,51 @@
version: "2"
linters-settings:
govet:
shadow: true
dupl:
threshold: 200
goconst:
min-len: 3
min-occurrences: 2
gocognit:
min-complexity: 65
linters:
enable:
- bodyclose
- gosec
- staticcheck
- nolintlint
- gofmt
- unused
- govet
- bodyclose
- typecheck
- goimports
- ineffassign
- gosimple
disable:
- dupl
# temporarily disabled
- errcheck
- gochecknoglobals
- gochecknoinits
- gocognit
- dupl
- gocritic
- lll
- gocognit
- nakedret
- revive
- stylecheck
- unconvert
- unparam
settings:
dupl:
threshold: 200
gocognit:
min-complexity: 65
goconst:
min-len: 3
min-occurrences: 2
exclusions:
generated: lax
presets:
- comments
- common-false-positives
- legacy
- std-error-handling
rules:
- linters:
- revive
text: var-naming
- linters:
- revive
text: type name will be used as (.+?) by other packages, and that stutters
- linters:
- staticcheck
text: ST1003
paths:
- third_party$
- builtin$
- examples$
formatters:
enable:
- gofmt
- goimports
exclusions:
generated: lax
paths:
- third_party$
- builtin$
- examples$
#- forbidigo # <- see later
# should remain disabled
- lll
- gochecknoinits
- gochecknoglobals
issues:
exclude-rules:
- linters:
- revive
text: "var-naming"
- linters:
- revive
text: "type name will be used as (.+?) by other packages, and that stutters"
- linters:
- stylecheck
text: "ST1003"

View File

@@ -1,29 +1,16 @@
# This is an example .goreleaser.yml file with some sensible defaults.
# Make sure to check the documentation at https://goreleaser.com
# The lines below are called `modelines`. See `:help modeline`
# The lines bellow are called `modelines`. See `:help modeline`
# Feel free to remove those if you don't want/need to use them.
# yaml-language-server: $schema=https://goreleaser.com/static/schema.json
# vim: set ts=2 sw=2 tw=0 fo=cnqoj
version: 2
before:
hooks:
# You may remove this if you don't use go modules.
- go mod tidy
archives:
- id: binaries
formats:
- binary
name_template: >-
{{ .Binary }}
- id: default
formats:
- tar.gz
name_template: >-
{{ .Binary }}
builds:
- goos:
- linux
@@ -46,6 +33,15 @@ builds:
{{- else }}{{ .Os }}{{ end }}-latest
no_unique_dist_dir: true
archives:
- format: binary
id: binaries
name_template: >-
{{ .Binary }}
- format: tar.gz
name_template: >-
{{ .Binary }}
changelog:
sort: asc
filters:
@@ -54,7 +50,9 @@ changelog:
- "^test:"
checksum:
name_template: "checksums.sha256"
ids:
- binaries
split: true
sboms:
- artifacts: binary

View File

@@ -112,7 +112,7 @@ Kubescape changes are tracked on the [release](https://github.com/kubescape/kube
## License
Copyright 2021-2025, the Kubescape Authors. All rights reserved. Kubescape is released under the Apache 2.0 license. See the [LICENSE](LICENSE) file for details.
Copyright 2021-2024, the Kubescape Authors. All rights reserved. Kubescape is released under the Apache 2.0 license. See the [LICENSE](LICENSE) file for details.
Kubescape is a [Cloud Native Computing Foundation (CNCF) incubating project](https://www.cncf.io/projects/kubescape/) and was contributed by [ARMO](https://www.armosec.io/?utm_source=github&utm_medium=repository).

View File

@@ -1,4 +1,4 @@
FROM --platform=$BUILDPLATFORM golang:1.24-bookworm AS builder
FROM --platform=$BUILDPLATFORM golang:1.23-bookworm AS builder
ENV GO111MODULE=on CGO_ENABLED=0
WORKDIR /work

View File

@@ -26,7 +26,7 @@ var (
%[1]s list controls
Control documentation:
https://kubescape.io/docs/controls/
https://hub.armosec.io/docs/controls
`, cautils.ExecName())
)

View File

@@ -1,466 +0,0 @@
package mcpserver
import (
"context"
"encoding/json"
"fmt"
"log"
"strings"
"time"
"github.com/kubescape/go-logger"
helpersv1 "github.com/kubescape/k8s-interface/instanceidhandler/v1/helpers"
"github.com/kubescape/storage/pkg/apis/softwarecomposition/v1beta1"
spdxv1beta1 "github.com/kubescape/storage/pkg/generated/clientset/versioned/typed/softwarecomposition/v1beta1"
"github.com/mark3labs/mcp-go/mcp"
"github.com/mark3labs/mcp-go/server"
"github.com/spf13/cobra"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)
type KubescapeMcpserver struct {
s *server.MCPServer
ksClient spdxv1beta1.SpdxV1beta1Interface
}
func createVulnerabilityToolsAndResources(ksServer *KubescapeMcpserver) {
// Tool to list vulnerability manifests
listManifestsTool := mcp.NewTool(
"list_vulnerability_manifests",
mcp.WithDescription("Discover available vulnerability manifests at image and workload levels"),
mcp.WithString("namespace",
mcp.Description("Filter by namespace (optional)"),
),
mcp.WithString("level",
mcp.Description("Type of vulnerability manifests to list"),
mcp.Enum("image", "workload", "both"),
),
)
ksServer.s.AddTool(listManifestsTool, func(ctx context.Context, request mcp.CallToolRequest) (*mcp.CallToolResult, error) {
return ksServer.CallTool("list_vulnerability_manifests", request.Params.Arguments.(map[string]interface{}))
})
listVulnerabilitiesTool := mcp.NewTool(
"list_vulnerabilities_in_manifest",
mcp.WithDescription("List all vulnerabilities in a given manifest"),
mcp.WithString("namespace",
mcp.Description("Filter by namespace (optional)"),
),
mcp.WithString("manifest_name",
mcp.Required(),
mcp.Description("Name of the manifest to list vulnerabilities from"),
),
)
ksServer.s.AddTool(listVulnerabilitiesTool, func(ctx context.Context, request mcp.CallToolRequest) (*mcp.CallToolResult, error) {
return ksServer.CallTool("list_vulnerabilities_in_manifest", request.Params.Arguments.(map[string]interface{}))
})
listVulnerabilityMatchesForCVE := mcp.NewTool(
"list_vulnerability_matches_for_cve",
mcp.WithDescription("List all vulnerability matches for a given CVE in a given manifest"),
mcp.WithString("namespace",
mcp.Description("Filter by namespace (optional)"),
),
mcp.WithString("manifest_name",
mcp.Required(),
mcp.Description("Name of the manifest to list vulnerabilities from"),
),
mcp.WithString("cve_id",
mcp.Required(),
mcp.Description("ID of the CVE to list matches for"),
),
)
ksServer.s.AddTool(listVulnerabilityMatchesForCVE, func(ctx context.Context, request mcp.CallToolRequest) (*mcp.CallToolResult, error) {
return ksServer.CallTool("list_vulnerability_matches_for_cve", request.Params.Arguments.(map[string]interface{}))
})
vulnerabilityManifestTemplate := mcp.NewResourceTemplate(
"kubescape://vulnerability-manifests/{namespace}/{manifest_name}",
"Vulnerability Manifest",
mcp.WithTemplateDescription("Complete vulnerability manifest either for a specific workload or image. Use 'list_vulnerability_manifests' tool to discover available manifests."),
mcp.WithTemplateMIMEType("application/json"),
)
ksServer.s.AddResourceTemplate(vulnerabilityManifestTemplate, ksServer.ReadResource)
}
func createConfigurationsToolsAndResources(ksServer *KubescapeMcpserver) {
// Tool to list configuration manifests
listConfigsTool := mcp.NewTool(
"list_configuration_security_scan_manifests",
mcp.WithDescription("Discover available security configuration scan results at workload level (this returns a list of manifests, not the scan results themselves, to get the scan results, use the get_configuration_security_scan_manifest tool)"),
mcp.WithString("namespace",
mcp.Description("Filter by namespace (optional)"),
),
)
ksServer.s.AddTool(listConfigsTool, func(ctx context.Context, request mcp.CallToolRequest) (*mcp.CallToolResult, error) {
return ksServer.CallTool("list_configuration_security_scan_manifests", request.Params.Arguments.(map[string]interface{}))
})
getConfigDetailsTool := mcp.NewTool(
"get_configuration_security_scan_manifest",
mcp.WithDescription("Get details of a specific security configuration scan result"),
mcp.WithString("namespace",
mcp.Description("Namespace of the manifest (optional, defaults to 'kubescape')"),
),
mcp.WithString("manifest_name",
mcp.Required(),
mcp.Description("Name of the configuration manifest to get details for (get this from the list_configuration_security_scan_manifests tool)"),
),
)
ksServer.s.AddTool(getConfigDetailsTool, func(ctx context.Context, request mcp.CallToolRequest) (*mcp.CallToolResult, error) {
return ksServer.CallTool("get_configuration_security_scan_manifest", request.Params.Arguments.(map[string]interface{}))
})
configManifestTemplate := mcp.NewResourceTemplate(
"kubescape://configuration-manifests/{namespace}/{manifest_name}",
"Configuration Security Scan Manifest",
mcp.WithTemplateDescription("Complete configuration scan manifest for a specific workload. Use 'list_configuration_security_scan_manifests' tool to discover available manifests."),
mcp.WithTemplateMIMEType("application/json"),
)
ksServer.s.AddResourceTemplate(configManifestTemplate, ksServer.ReadConfigurationResource)
}
func (ksServer *KubescapeMcpserver) ReadResource(ctx context.Context, request mcp.ReadResourceRequest) ([]mcp.ResourceContents, error) {
uri := request.Params.URI
// Validate the URI and check if it starts with kubescape://vulnerability-manifests/
if !strings.HasPrefix(uri, "kubescape://vulnerability-manifests/") {
return nil, fmt.Errorf("invalid URI: %s", uri)
}
// Verify that the URI is either the CVE list or CVE details
if !strings.HasSuffix(uri, "/cve_list") && !strings.Contains(uri, "/cve_details/") {
return nil, fmt.Errorf("invalid URI: %s", uri)
}
// Split the URI into namespace and manifest name
parts := strings.Split(uri, "/")
if len(parts) != 4 && len(parts) != 5 {
return nil, fmt.Errorf("invalid URI: %s", uri)
}
namespace := parts[1]
manifestName := parts[2]
cveID := ""
if len(parts) == 5 {
cveID = parts[3]
}
// Get the vulnerability manifest
manifest, err := ksServer.ksClient.VulnerabilityManifests(namespace).Get(ctx, manifestName, metav1.GetOptions{})
if err != nil {
return nil, fmt.Errorf("failed to get vulnerability manifest: %s", err)
}
var responseJson []byte
if cveID == "" {
// CVE list
var cveList []v1beta1.Vulnerability
for _, match := range manifest.Spec.Payload.Matches {
cveList = append(cveList, match.Vulnerability)
}
responseJson, err = json.Marshal(cveList)
if err != nil {
return nil, fmt.Errorf("failed to marshal cve list: %s", err)
}
} else {
// CVE details
var match []v1beta1.Match
for _, m := range manifest.Spec.Payload.Matches {
if m.Vulnerability.ID == cveID {
match = append(match, m)
}
}
responseJson, err = json.Marshal(match)
if err != nil {
return nil, fmt.Errorf("failed to marshal cve details: %s", err)
}
}
return []mcp.ResourceContents{mcp.TextResourceContents{
URI: uri,
Text: string(responseJson),
}}, nil
}
func (ksServer *KubescapeMcpserver) ReadConfigurationResource(ctx context.Context, request mcp.ReadResourceRequest) ([]mcp.ResourceContents, error) {
uri := request.Params.URI
if !strings.HasPrefix(uri, "kubescape://configuration-manifests/") {
return nil, fmt.Errorf("invalid URI: %s", uri)
}
parts := strings.Split(uri[len("kubescape://configuration-manifests/"):], "/")
if len(parts) != 2 {
return nil, fmt.Errorf("invalid URI: %s", uri)
}
namespace := parts[0]
manifestName := parts[1]
manifest, err := ksServer.ksClient.WorkloadConfigurationScans(namespace).Get(ctx, manifestName, metav1.GetOptions{})
if err != nil {
return nil, fmt.Errorf("failed to get configuration manifest: %s", err)
}
responseJson, err := json.Marshal(manifest)
if err != nil {
return nil, fmt.Errorf("failed to marshal configuration manifest: %s", err)
}
return []mcp.ResourceContents{mcp.TextResourceContents{
URI: uri,
Text: string(responseJson),
}}, nil
}
func (ksServer *KubescapeMcpserver) CallTool(name string, arguments map[string]interface{}) (*mcp.CallToolResult, error) {
switch name {
case "list_vulnerability_manifests":
//namespace, ok := arguments["namespace"]
//if !ok {
// namespace = ""
//}
level, ok := arguments["level"]
if !ok {
level = "both"
}
result := map[string]interface{}{
"vulnerability_manifests": map[string]interface{}{},
}
// Get workload-level manifests
labelSelector := ""
if level == "workload" {
labelSelector = "kubescape.io/context=filtered"
} else if level == "image" {
labelSelector = "kubescape.io/context=non-filtered"
}
var manifests *v1beta1.VulnerabilityManifestList
var err error
if labelSelector == "" {
manifests, err = ksServer.ksClient.VulnerabilityManifests(metav1.NamespaceAll).List(context.Background(), metav1.ListOptions{})
} else {
manifests, err = ksServer.ksClient.VulnerabilityManifests(metav1.NamespaceAll).List(context.Background(), metav1.ListOptions{
LabelSelector: labelSelector,
})
}
if err != nil {
return nil, err
}
log.Printf("Found %d manifests", len(manifests.Items))
vulnerabilityManifests := []map[string]interface{}{}
for _, manifest := range manifests.Items {
isImageLevel := manifest.Annotations[helpersv1.WlidMetadataKey] == ""
manifestMap := map[string]interface{}{
"type": "workload",
"namespace": manifest.Namespace,
"manifest_name": manifest.Name,
"image-level": isImageLevel,
"workload-level": !isImageLevel,
"image-id": manifest.Annotations[helpersv1.ImageIDMetadataKey],
"image-tag": manifest.Annotations[helpersv1.ImageTagMetadataKey],
"workload-id": manifest.Annotations[helpersv1.WlidMetadataKey],
"workload-container-name": manifest.Annotations[helpersv1.ContainerNameMetadataKey],
"resource_uri": fmt.Sprintf("kubescape://vulnerability-manifests/%s/%s",
manifest.Namespace, manifest.Name),
}
vulnerabilityManifests = append(vulnerabilityManifests, manifestMap)
}
result["vulnerability_manifests"].(map[string]interface{})["manifests"] = vulnerabilityManifests
// Add template information
result["available_templates"] = map[string]string{
"vulnerability_manifest_cve_list": "kubescape://vulnerability-manifests/{namespace}/{manifest_name}/cve_list",
"vulnerability_manifest_cve_details": "kubescape://vulnerability-manifests/{namespace}/{manifest_name}/cve_details/{cve_id}",
}
content, _ := json.Marshal(result)
return &mcp.CallToolResult{
Content: []mcp.Content{
mcp.TextContent{
Type: "text",
Text: string(content),
},
},
}, nil
case "list_vulnerabilities_in_manifest":
namespace, ok := arguments["namespace"]
if !ok {
namespace = "kubescape"
}
manifestName, ok := arguments["manifest_name"]
if !ok {
return nil, fmt.Errorf("manifest_name is required")
}
manifest, err := ksServer.ksClient.VulnerabilityManifests(namespace.(string)).Get(context.Background(), manifestName.(string), metav1.GetOptions{})
if err != nil {
return nil, fmt.Errorf("failed to get vulnerability manifest: %s", err)
}
var cveList []v1beta1.Vulnerability
for _, match := range manifest.Spec.Payload.Matches {
cveList = append(cveList, match.Vulnerability)
}
responseJson, err := json.Marshal(cveList)
if err != nil {
return nil, fmt.Errorf("failed to marshal cve list: %s", err)
}
return &mcp.CallToolResult{
Content: []mcp.Content{
mcp.TextContent{
Type: "text",
Text: string(responseJson),
},
},
}, nil
case "list_vulnerability_matches_for_cve":
namespace, ok := arguments["namespace"]
if !ok {
namespace = "kubescape"
}
manifestName, ok := arguments["manifest_name"]
if !ok {
return nil, fmt.Errorf("manifest_name is required")
}
cveID, ok := arguments["cve_id"]
if !ok {
return nil, fmt.Errorf("cve_id is required")
}
manifest, err := ksServer.ksClient.VulnerabilityManifests(namespace.(string)).Get(context.Background(), manifestName.(string), metav1.GetOptions{})
if err != nil {
return nil, fmt.Errorf("failed to get vulnerability manifest: %s", err)
}
var match []v1beta1.Match
for _, m := range manifest.Spec.Payload.Matches {
if m.Vulnerability.ID == cveID.(string) {
match = append(match, m)
}
}
responseJson, err := json.Marshal(match)
if err != nil {
return nil, fmt.Errorf("failed to marshal cve details: %s", err)
}
return &mcp.CallToolResult{
Content: []mcp.Content{
mcp.TextContent{
Type: "text",
Text: string(responseJson),
},
},
}, nil
case "list_configuration_security_scan_manifests":
namespace, ok := arguments["namespace"]
if !ok {
namespace = "kubescape"
}
manifests, err := ksServer.ksClient.WorkloadConfigurationScans(namespace.(string)).List(context.Background(), metav1.ListOptions{})
if err != nil {
return nil, err
}
log.Printf("Found %d configuration manifests", len(manifests.Items))
configManifests := []map[string]interface{}{}
for _, manifest := range manifests.Items {
item := map[string]interface{}{
"namespace": manifest.Namespace,
"manifest_name": manifest.Name,
"resource_uri": fmt.Sprintf("kubescape://configuration-manifests/%s/%s", manifest.Namespace, manifest.Name),
}
configManifests = append(configManifests, item)
}
result := map[string]interface{}{
"configuration_manifests": map[string]interface{}{
"manifests": configManifests,
},
"available_templates": map[string]string{
"configuration_manifest_details": "kubescape://configuration-manifests/{namespace}/{manifest_name}",
},
}
content, _ := json.Marshal(result)
return &mcp.CallToolResult{
Content: []mcp.Content{
mcp.TextContent{
Type: "text",
Text: string(content),
},
},
}, nil
case "get_configuration_security_scan_manifest":
namespace, ok := arguments["namespace"]
if !ok {
namespace = "kubescape"
}
manifestName, ok := arguments["manifest_name"]
if !ok {
return nil, fmt.Errorf("manifest_name is required")
}
manifest, err := ksServer.ksClient.WorkloadConfigurationScans(namespace.(string)).Get(context.Background(), manifestName.(string), metav1.GetOptions{})
if err != nil {
return nil, fmt.Errorf("failed to get configuration manifest: %s", err)
}
responseJson, err := json.Marshal(manifest)
if err != nil {
return nil, fmt.Errorf("failed to marshal configuration manifest: %s", err)
}
return &mcp.CallToolResult{
Content: []mcp.Content{
mcp.TextContent{
Type: "text",
Text: string(responseJson),
},
},
}, nil
default:
return nil, fmt.Errorf("unknown tool: %s", name)
}
}
func mcpServerEntrypoint() error {
logger.L().Info("Starting MCP server...")
// Create a kubernetes client and verify it's working
client, err := CreateKsObjectConnection("default", 10*time.Second)
if err != nil {
return fmt.Errorf("failed to create kubernetes client: %v", err)
}
// Create a new MCP server
s := server.NewMCPServer(
"Kubescape MCP Server",
"0.0.1",
server.WithToolCapabilities(false),
server.WithRecovery(),
)
ksServer := &KubescapeMcpserver{
s: s,
ksClient: client,
}
// Creating Kubescape tools and resources
createVulnerabilityToolsAndResources(ksServer)
createConfigurationsToolsAndResources(ksServer)
// Start the server
if err := server.ServeStdio(s); err != nil {
return fmt.Errorf("Server error: %v\n", err)
}
return nil
}
func GetMCPServerCmd() *cobra.Command {
cmd := &cobra.Command{
Use: "mcpserver",
Short: "Start the Kubescape MCP server",
Long: `Start the Kubescape MCP server`,
RunE: func(cmd *cobra.Command, args []string) error {
return mcpServerEntrypoint()
},
}
return cmd
}

View File

@@ -1,14 +0,0 @@
package mcpserver
import (
"time"
"github.com/kubescape/kubescape/v3/pkg/ksinit"
spdxv1beta1 "github.com/kubescape/storage/pkg/generated/clientset/versioned/typed/softwarecomposition/v1beta1"
)
// CreateKsObjectConnection delegates to the shared ksinit package
func CreateKsObjectConnection(namespace string, maxElapsedTime time.Duration) (spdxv1beta1.SpdxV1beta1Interface, error) {
return ksinit.CreateKsObjectConnection(namespace, maxElapsedTime)
}

View File

@@ -6,12 +6,13 @@ import (
"strings"
"time"
"github.com/distribution/reference"
"github.com/docker/distribution/reference"
"github.com/kubescape/go-logger"
"github.com/kubescape/kubescape/v3/cmd/shared"
"github.com/kubescape/kubescape/v3/core/cautils"
"github.com/kubescape/kubescape/v3/core/meta"
metav1 "github.com/kubescape/kubescape/v3/core/meta/datastructures/v1"
"github.com/kubescape/kubescape/v3/pkg/imagescan"
"github.com/spf13/cobra"
)
@@ -27,7 +28,6 @@ var patchCmdExamples = fmt.Sprintf(`
func GetPatchCmd(ks meta.IKubescape) *cobra.Command {
var patchInfo metav1.PatchInfo
var scanInfo cautils.ScanInfo
var useDefaultMatchers bool
patchCmd := &cobra.Command{
Use: "patch --image <image>:<tag> [flags]",
@@ -49,15 +49,12 @@ func GetPatchCmd(ks meta.IKubescape) *cobra.Command {
return err
}
// Set the UseDefaultMatchers field in scanInfo
scanInfo.UseDefaultMatchers = useDefaultMatchers
exceedsSeverityThreshold, err := ks.Patch(&patchInfo, &scanInfo)
results, err := ks.Patch(&patchInfo, &scanInfo)
if err != nil {
return err
}
if exceedsSeverityThreshold {
if imagescan.ExceedsSeverityThreshold(results, imagescan.ParseSeverity(scanInfo.FailThresholdSeverity)) {
shared.TerminateOnExceedingSeverity(&scanInfo, logger.L())
}
@@ -79,7 +76,6 @@ func GetPatchCmd(ks meta.IKubescape) *cobra.Command {
patchCmd.PersistentFlags().BoolVarP(&scanInfo.VerboseMode, "verbose", "v", false, "Display full report. Default to false")
patchCmd.PersistentFlags().StringVarP(&scanInfo.FailThresholdSeverity, "severity-threshold", "s", "", "Severity threshold is the severity of a vulnerability at which the command fails and returns exit code 1")
patchCmd.PersistentFlags().BoolVarP(&useDefaultMatchers, "use-default-matchers", "", true, "Use default matchers (true) or CPE matchers (false) for image scanning")
return patchCmd
}

View File

@@ -3,8 +3,6 @@ package patch
import (
"testing"
metav1 "github.com/kubescape/kubescape/v3/core/meta/datastructures/v1"
"github.com/kubescape/kubescape/v3/core/mocks"
"github.com/spf13/cobra"
"github.com/stretchr/testify/assert"
@@ -52,18 +50,3 @@ func TestGetPatchCmdWithNonExistentImage(t *testing.T) {
assert.Error(t, err)
assert.Equal(t, expectedErrorMessage, err.Error())
}
func Test_validateImagePatchInfo_EmptyImage(t *testing.T) {
patchInfo := &metav1.PatchInfo{}
err := validateImagePatchInfo(patchInfo)
assert.NotNil(t, err)
assert.Equal(t, "image tag is required", err.Error())
}
func Test_validateImagePatchInfo_Image(t *testing.T) {
patchInfo := &metav1.PatchInfo{
Image: "testing",
}
err := validateImagePatchInfo(patchInfo)
assert.Nil(t, err)
}

View File

@@ -13,14 +13,12 @@ import (
)
func GetPreReqCmd(ks meta.IKubescape) *cobra.Command {
var kubeconfigPath *string
// preReqCmd represents the prerequisites command
preReqCmd := &cobra.Command{
Use: "prerequisites",
Short: "Check prerequisites for installing Kubescape Operator",
Run: func(cmd *cobra.Command, args []string) {
clientSet, inCluster := common.BuildKubeClient(*kubeconfigPath)
clientSet, inCluster := common.BuildKubeClient()
if clientSet == nil {
logger.L().Fatal("Could not create kube client. Exiting.")
}
@@ -44,8 +42,5 @@ func GetPreReqCmd(ks meta.IKubescape) *cobra.Command {
common.GenerateOutput(finalReport, inCluster)
},
}
kubeconfigPath = preReqCmd.PersistentFlags().String("kubeconfig", "", "Path to the kubeconfig file. If not set, in-cluster config is used or $HOME/.kube/config if outside a cluster.")
return preReqCmd
}

View File

@@ -13,7 +13,6 @@ import (
"github.com/kubescape/kubescape/v3/cmd/download"
"github.com/kubescape/kubescape/v3/cmd/fix"
"github.com/kubescape/kubescape/v3/cmd/list"
"github.com/kubescape/kubescape/v3/cmd/mcpserver"
"github.com/kubescape/kubescape/v3/cmd/operator"
"github.com/kubescape/kubescape/v3/cmd/patch"
"github.com/kubescape/kubescape/v3/cmd/prerequisites"
@@ -53,7 +52,7 @@ func getRootCmd(ks meta.IKubescape) *cobra.Command {
rootCmd := &cobra.Command{
Use: "kubescape",
Short: "Kubescape is a tool for testing Kubernetes security posture. Docs: https://kubescape.io/docs/",
Short: "Kubescape is a tool for testing Kubernetes security posture. Docs: https://hub.armosec.io/docs",
Example: ksExamples,
PersistentPreRun: func(cmd *cobra.Command, args []string) {
k8sinterface.SetClusterContextName(rootInfo.KubeContext)
@@ -101,7 +100,6 @@ func getRootCmd(ks meta.IKubescape) *cobra.Command {
rootCmd.AddCommand(vap.GetVapHelperCmd())
rootCmd.AddCommand(operator.GetOperatorCmd(ks))
rootCmd.AddCommand(prerequisites.GetPreReqCmd(ks))
rootCmd.AddCommand(mcpserver.GetMCPServerCmd())
// deprecated commands
rootCmd.AddCommand(&cobra.Command{

View File

@@ -1,24 +0,0 @@
package cmd
import (
"context"
"testing"
"github.com/stretchr/testify/assert"
)
func TestNewDefaultKubescapeCommand(t *testing.T) {
t.Run("NewDefaultKubescapeCommand", func(t *testing.T) {
cmd := NewDefaultKubescapeCommand(context.Background())
assert.NotNil(t, cmd)
})
}
func TestExecute(t *testing.T) {
t.Run("Execute", func(t *testing.T) {
err := Execute(context.Background())
if err != nil {
assert.EqualErrorf(t, err, "unknown command \"^\\\\QTestExecute\\\\E$\" for \"kubescape\"", err.Error())
}
})
}

View File

@@ -29,7 +29,7 @@ var (
Run '%[1]s list controls' for the list of supported controls
Control documentation:
https://kubescape.io/docs/controls/
https://hub.armosec.io/docs/controls
`, cautils.ExecName())
)
@@ -99,7 +99,7 @@ func getControlCmd(ks meta.IKubescape, scanInfo *cautils.ScanInfo) *cobra.Comman
if err != nil {
logger.L().Fatal(err.Error())
}
if err := results.HandleResults(ks.Context(), scanInfo); err != nil {
if err := results.HandleResults(ks.Context()); err != nil {
logger.L().Fatal(err.Error())
}
if !scanInfo.VerboseMode {

View File

@@ -117,7 +117,7 @@ func getFrameworkCmd(ks meta.IKubescape, scanInfo *cautils.ScanInfo) *cobra.Comm
logger.L().Fatal(err.Error())
}
if err = results.HandleResults(ks.Context(), scanInfo); err != nil {
if err = results.HandleResults(ks.Context()); err != nil {
logger.L().Fatal(err.Error())
}

View File

@@ -8,6 +8,7 @@ import (
"github.com/kubescape/kubescape/v3/core/cautils"
"github.com/kubescape/kubescape/v3/core/meta"
metav1 "github.com/kubescape/kubescape/v3/core/meta/datastructures/v1"
"github.com/kubescape/kubescape/v3/pkg/imagescan"
"github.com/spf13/cobra"
)
@@ -32,7 +33,6 @@ var (
func getImageCmd(ks meta.IKubescape, scanInfo *cautils.ScanInfo) *cobra.Command {
var imgCredentials shared.ImageCredentials
var exceptions string
var useDefaultMatchers bool
cmd := &cobra.Command{
Use: "image <image>:<tag> [flags]",
@@ -54,19 +54,18 @@ func getImageCmd(ks meta.IKubescape, scanInfo *cautils.ScanInfo) *cobra.Command
}
imgScanInfo := &metav1.ImageScanInfo{
Image: args[0],
Username: imgCredentials.Username,
Password: imgCredentials.Password,
Exceptions: exceptions,
UseDefaultMatchers: useDefaultMatchers,
Image: args[0],
Username: imgCredentials.Username,
Password: imgCredentials.Password,
Exceptions: exceptions,
}
exceedsSeverityThreshold, err := ks.ScanImage(imgScanInfo, scanInfo)
results, err := ks.ScanImage(imgScanInfo, scanInfo)
if err != nil {
return err
}
if exceedsSeverityThreshold {
if imagescan.ExceedsSeverityThreshold(results, imagescan.ParseSeverity(scanInfo.FailThresholdSeverity)) {
shared.TerminateOnExceedingSeverity(scanInfo, logger.L())
}
@@ -78,7 +77,6 @@ func getImageCmd(ks meta.IKubescape, scanInfo *cautils.ScanInfo) *cobra.Command
cmd.PersistentFlags().StringVarP(&exceptions, "exceptions", "", "", "Path to the exceptions file")
cmd.PersistentFlags().StringVarP(&imgCredentials.Username, "username", "u", "", "Username for registry login")
cmd.PersistentFlags().StringVarP(&imgCredentials.Password, "password", "p", "", "Password for registry login")
cmd.PersistentFlags().BoolVarP(&useDefaultMatchers, "use-default-matchers", "", true, "Use default matchers (true) or CPE matchers (false)")
return cmd
}

View File

@@ -92,7 +92,6 @@ func GetScanCommand(ks meta.IKubescape) *cobra.Command {
scanCmd.PersistentFlags().BoolVarP(&scanInfo.PrintAttackTree, "print-attack-tree", "", false, "Print attack tree")
scanCmd.PersistentFlags().BoolVarP(&scanInfo.EnableRegoPrint, "enable-rego-prints", "", false, "Enable sending to rego prints to the logs (use with debug log level: -l debug)")
scanCmd.PersistentFlags().BoolVarP(&scanInfo.ScanImages, "scan-images", "", false, "Scan resources images")
scanCmd.PersistentFlags().BoolVarP(&scanInfo.UseDefaultMatchers, "use-default-matchers", "", true, "Use default matchers (true) or CPE matchers (false) for image scanning")
scanCmd.PersistentFlags().MarkDeprecated("fail-threshold", "use '--compliance-threshold' flag instead. Flag will be removed at 1.Dec.2023")
scanCmd.PersistentFlags().MarkDeprecated("create-account", "Create account is no longer supported. In case of a missing Account ID and a configured backend server, a new account id will be generated automatically by Kubescape. Feel free to contact the Kubescape maintainers for more information.")
@@ -140,7 +139,7 @@ func securityScan(scanInfo cautils.ScanInfo, ks meta.IKubescape) error {
return err
}
if err = results.HandleResults(ks.Context(), &scanInfo); err != nil {
if err = results.HandleResults(ks.Context()); err != nil {
return err
}

View File

@@ -5,7 +5,6 @@ import (
"os"
"reflect"
"testing"
"time"
"github.com/kubescape/go-logger/helpers"
"github.com/kubescape/kubescape/v3/cmd/shared"
@@ -187,23 +186,20 @@ type spyLogger struct {
setItems []spyLogMessage
}
var _ helpers.ILogger = &spyLogger{}
func (l *spyLogger) Error(msg string, details ...helpers.IDetails) {}
func (l *spyLogger) Success(msg string, details ...helpers.IDetails) {}
func (l *spyLogger) Warning(msg string, details ...helpers.IDetails) {}
func (l *spyLogger) Info(msg string, details ...helpers.IDetails) {}
func (l *spyLogger) Debug(msg string, details ...helpers.IDetails) {}
func (l *spyLogger) SetLevel(level string) error { return nil }
func (l *spyLogger) GetLevel() string { return "" }
func (l *spyLogger) SetWriter(w *os.File) {}
func (l *spyLogger) GetWriter() *os.File { return &os.File{} }
func (l *spyLogger) LoggerName() string { return "" }
func (l *spyLogger) Ctx(_ context.Context) helpers.ILogger { return l }
func (l *spyLogger) Start(msg string, details ...helpers.IDetails) {}
func (l *spyLogger) StopSuccess(msg string, details ...helpers.IDetails) {}
func (l *spyLogger) StopError(msg string, details ...helpers.IDetails) {}
func (l *spyLogger) TimedWrapper(funcName string, timeout time.Duration, task func()) {}
func (l *spyLogger) Error(msg string, details ...helpers.IDetails) {}
func (l *spyLogger) Success(msg string, details ...helpers.IDetails) {}
func (l *spyLogger) Warning(msg string, details ...helpers.IDetails) {}
func (l *spyLogger) Info(msg string, details ...helpers.IDetails) {}
func (l *spyLogger) Debug(msg string, details ...helpers.IDetails) {}
func (l *spyLogger) SetLevel(level string) error { return nil }
func (l *spyLogger) GetLevel() string { return "" }
func (l *spyLogger) SetWriter(w *os.File) {}
func (l *spyLogger) GetWriter() *os.File { return &os.File{} }
func (l *spyLogger) LoggerName() string { return "" }
func (l *spyLogger) Ctx(_ context.Context) helpers.ILogger { return l }
func (l *spyLogger) Start(msg string, details ...helpers.IDetails) {}
func (l *spyLogger) StopSuccess(msg string, details ...helpers.IDetails) {}
func (l *spyLogger) StopError(msg string, details ...helpers.IDetails) {}
func (l *spyLogger) Fatal(msg string, details ...helpers.IDetails) {
firstDetail := details[0]

View File

@@ -70,7 +70,7 @@ func getWorkloadCmd(ks meta.IKubescape, scanInfo *cautils.ScanInfo) *cobra.Comma
logger.L().Fatal(err.Error())
}
if err = results.HandleResults(ks.Context(), scanInfo); err != nil {
if err = results.HandleResults(ks.Context()); err != nil {
logger.L().Fatal(err.Error())
}

View File

@@ -94,17 +94,3 @@ func TestGetWorkloadCmd_ChartPathAndFilePathEmpty(t *testing.T) {
expectedErrorMessage = "invalid workload identifier"
assert.Equal(t, expectedErrorMessage, err.Error())
}
func Test_parseWorkloadIdentifierString_Empty(t *testing.T) {
t.Run("empty identifier", func(t *testing.T) {
_, _, err := parseWorkloadIdentifierString("")
assert.Error(t, err)
})
}
func Test_parseWorkloadIdentifierString_NoError(t *testing.T) {
t.Run("valid identifier", func(t *testing.T) {
_, _, err := parseWorkloadIdentifierString("default/Deployment")
assert.NoError(t, err)
})
}

View File

@@ -5,7 +5,6 @@ import (
"os"
"reflect"
"testing"
"time"
"github.com/kubescape/go-logger/helpers"
"github.com/kubescape/kubescape/v3/core/cautils"
@@ -21,23 +20,20 @@ type spyLogger struct {
setItems []spyLogMessage
}
var _ helpers.ILogger = &spyLogger{}
func (l *spyLogger) Error(msg string, details ...helpers.IDetails) {}
func (l *spyLogger) Success(msg string, details ...helpers.IDetails) {}
func (l *spyLogger) Warning(msg string, details ...helpers.IDetails) {}
func (l *spyLogger) Info(msg string, details ...helpers.IDetails) {}
func (l *spyLogger) Debug(msg string, details ...helpers.IDetails) {}
func (l *spyLogger) SetLevel(level string) error { return nil }
func (l *spyLogger) GetLevel() string { return "" }
func (l *spyLogger) SetWriter(w *os.File) {}
func (l *spyLogger) GetWriter() *os.File { return &os.File{} }
func (l *spyLogger) LoggerName() string { return "" }
func (l *spyLogger) Ctx(_ context.Context) helpers.ILogger { return l }
func (l *spyLogger) Start(msg string, details ...helpers.IDetails) {}
func (l *spyLogger) StopSuccess(msg string, details ...helpers.IDetails) {}
func (l *spyLogger) StopError(msg string, details ...helpers.IDetails) {}
func (l *spyLogger) TimedWrapper(funcName string, timeout time.Duration, task func()) {}
func (l *spyLogger) Error(msg string, details ...helpers.IDetails) {}
func (l *spyLogger) Success(msg string, details ...helpers.IDetails) {}
func (l *spyLogger) Warning(msg string, details ...helpers.IDetails) {}
func (l *spyLogger) Info(msg string, details ...helpers.IDetails) {}
func (l *spyLogger) Debug(msg string, details ...helpers.IDetails) {}
func (l *spyLogger) SetLevel(level string) error { return nil }
func (l *spyLogger) GetLevel() string { return "" }
func (l *spyLogger) SetWriter(w *os.File) {}
func (l *spyLogger) GetWriter() *os.File { return &os.File{} }
func (l *spyLogger) LoggerName() string { return "" }
func (l *spyLogger) Ctx(_ context.Context) helpers.ILogger { return l }
func (l *spyLogger) Start(msg string, details ...helpers.IDetails) {}
func (l *spyLogger) StopSuccess(msg string, details ...helpers.IDetails) {}
func (l *spyLogger) StopError(msg string, details ...helpers.IDetails) {}
func (l *spyLogger) Fatal(msg string, details ...helpers.IDetails) {
firstDetail := details[0]

View File

@@ -1,18 +0,0 @@
package update
import (
"context"
"testing"
"github.com/kubescape/kubescape/v3/core/core"
"github.com/stretchr/testify/assert"
)
func TestGetUpdateCmd(t *testing.T) {
ks := core.NewKubescape(context.TODO())
cmd := GetUpdateCmd(ks)
assert.NotNil(t, cmd)
err := cmd.RunE(cmd, []string{})
assert.Nil(t, err)
}

View File

@@ -220,11 +220,9 @@ func createPolicyBinding(bindingName string, policyName string, action string, p
}
policyBinding.Spec.ValidationActions = []admissionv1.ValidationAction{admissionv1.ValidationAction(action)}
paramAction := admissionv1.DenyAction
if paramRefName != "" {
policyBinding.Spec.ParamRef = &admissionv1.ParamRef{
Name: paramRefName,
ParameterNotFoundAction: &paramAction,
Name: paramRefName,
}
}
// Marshal the policy binding to YAML

View File

@@ -16,11 +16,14 @@ func GetVersionCmd(ks meta.IKubescape) *cobra.Command {
Long: ``,
RunE: func(cmd *cobra.Command, args []string) error {
v := versioncheck.NewIVersionCheckHandler(ks.Context())
_ = v.CheckLatestVersion(ks.Context(), versioncheck.NewVersionCheckRequest("", versioncheck.BuildNumber, "", "", "version", nil))
versionCheckRequest := versioncheck.NewVersionCheckRequest("", versioncheck.BuildNumber, "", "", "version", nil)
if err := v.CheckLatestVersion(ks.Context(), versionCheckRequest); err != nil {
return err
}
_, _ = fmt.Fprintf(cmd.OutOrStdout(),
fmt.Fprintf(cmd.OutOrStdout(),
"Your current version is: %s\n",
versioncheck.BuildNumber,
versionCheckRequest.ClientVersion,
)
return nil
},

View File

@@ -20,7 +20,7 @@ func TestGetVersionCmd(t *testing.T) {
}{
{
name: "Undefined Build Number",
buildNumber: "unknown",
buildNumber: "",
want: "Your current version is: unknown\n",
},
{

View File

@@ -24,7 +24,8 @@ const (
configFileName string = "config"
kubescapeNamespace string = "kubescape"
kubescapeConfigMapName string = "kubescape-config" // deprecated - for backward compatibility
kubescapeConfigMapName string = "kubescape-config" // deprecated - for backward compatibility
kubescapeCloudConfigMapName string = "ks-cloud-config" // deprecated - for backward compatibility
cloudConfigMapLabelSelector string = "kubescape.io/infra=config"
credsLabelSelectors string = "kubescape.io/infra=credentials" //nolint:gosec
@@ -206,8 +207,6 @@ func NewClusterConfig(k8s *k8sinterface.KubernetesApi, accountID, accessKey, clu
loadConfigFromFile(c.configObj)
}
loadUrlsFromFile(c.configObj)
// second, load urls from config map
c.updateConfigEmptyFieldsFromKubescapeConfigMap()
@@ -271,12 +270,15 @@ func (c *ClusterConfig) updateConfigEmptyFieldsFromKubescapeConfigMap() error {
return err
}
var ksConfigMap *corev1.ConfigMap
var urlsConfigMap *corev1.ConfigMap
if len(configMaps.Items) == 0 {
// try to find configmaps by name (for backward compatibility)
ksConfigMap, _ = c.k8s.KubernetesClient.CoreV1().ConfigMaps(c.configMapNamespace).Get(context.Background(), kubescapeConfigMapName, metav1.GetOptions{})
urlsConfigMap, _ = c.k8s.KubernetesClient.CoreV1().ConfigMaps(c.configMapNamespace).Get(context.Background(), kubescapeCloudConfigMapName, metav1.GetOptions{})
} else {
// use the first configmap with the label
ksConfigMap = &configMaps.Items[0]
urlsConfigMap = &configMaps.Items[0]
}
if ksConfigMap != nil {
@@ -289,6 +291,30 @@ func (c *ClusterConfig) updateConfigEmptyFieldsFromKubescapeConfigMap() error {
}
}
if urlsConfigMap != nil {
if jsonConf, ok := urlsConfigMap.Data["services"]; ok {
services, err := servicediscovery.GetServices(
servicediscoveryv2.NewServiceDiscoveryStreamV2([]byte(jsonConf)),
)
if err != nil {
// try to parse as v1
services, err = servicediscovery.GetServices(
servicediscoveryv1.NewServiceDiscoveryStreamV1([]byte(jsonConf)),
)
if err != nil {
return err
}
}
if services.GetApiServerUrl() != "" {
c.configObj.CloudAPIURL = services.GetApiServerUrl()
}
if services.GetReportReceiverHttpUrl() != "" {
c.configObj.CloudReportURL = services.GetReportReceiverHttpUrl()
}
}
}
return err
}
@@ -371,7 +397,7 @@ func (c *ClusterConfig) updateConfigData(configMap *corev1.ConfigMap) {
func loadConfigFromFile(configObj *ConfigObj) error {
dat, err := os.ReadFile(ConfigFileFullPath())
if err != nil {
return nil // no config file
return err
}
return readConfig(dat, configObj)
}
@@ -387,32 +413,6 @@ func readConfig(dat []byte, configObj *ConfigObj) error {
return nil
}
func loadUrlsFromFile(obj *ConfigObj) error {
dat, err := os.ReadFile("/etc/config/services.json")
if err != nil {
return nil // no config file
}
services, err := servicediscovery.GetServices(
servicediscoveryv2.NewServiceDiscoveryStreamV2(dat),
)
if err != nil {
// try to parse as v1
services, err = servicediscovery.GetServices(
servicediscoveryv1.NewServiceDiscoveryStreamV1(dat),
)
if err != nil {
return err
}
}
if services.GetApiServerUrl() != "" {
obj.CloudAPIURL = services.GetApiServerUrl()
}
if services.GetReportReceiverHttpUrl() != "" {
obj.CloudReportURL = services.GetReportReceiverHttpUrl()
}
return nil
}
func DeleteConfigFile() error {
return os.Remove(ConfigFileFullPath())
}

View File

@@ -4,10 +4,7 @@ import (
"context"
"sort"
"github.com/anchore/grype/grype/match"
"github.com/anchore/grype/grype/pkg"
"github.com/anchore/grype/grype/vulnerability"
"github.com/anchore/syft/syft/sbom"
"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"
@@ -23,14 +20,8 @@ type K8SResources map[string][]string
type ExternalResources map[string][]string
type ImageScanData struct {
Context pkg.Context
IgnoredMatches []match.IgnoredMatch
Image string
Matches match.Matches
Packages []pkg.Package
RemainingMatches *match.Matches
SBOM *sbom.SBOM
VulnerabilityProvider vulnerability.Provider
PresenterConfig *models.PresenterConfig
Image string
}
type ScanTypes string

View File

@@ -1,9 +1,6 @@
package cautils
import (
"fmt"
"io"
"os"
"path/filepath"
"strconv"
"strings"
@@ -15,12 +12,7 @@ import (
helmchart "helm.sh/helm/v3/pkg/chart"
helmloader "helm.sh/helm/v3/pkg/chart/loader"
helmchartutil "helm.sh/helm/v3/pkg/chartutil"
"helm.sh/helm/v3/pkg/cli"
helmdownloader "helm.sh/helm/v3/pkg/downloader"
helmengine "helm.sh/helm/v3/pkg/engine"
helmgetter "helm.sh/helm/v3/pkg/getter"
helmregistry "helm.sh/helm/v3/pkg/registry"
"k8s.io/client-go/util/homedir"
)
type HelmChart struct {
@@ -32,51 +24,7 @@ func IsHelmDirectory(path string) (bool, error) {
return helmchartutil.IsChartDir(path)
}
// newRegistryClient creates a Helm registry client for chart authentication
func newRegistryClient(certFile, keyFile, caFile string, insecureSkipTLS, plainHTTP bool, username, password string) (*helmregistry.Client, error) {
// Basic client options with debug disabled
opts := []helmregistry.ClientOption{
helmregistry.ClientOptDebug(false),
helmregistry.ClientOptWriter(io.Discard),
}
// Add TLS certificates if provided
if certFile != "" && keyFile != "" {
opts = append(opts, helmregistry.ClientOptCredentialsFile(certFile))
}
// Add CA certificate if provided
if caFile != "" {
opts = append(opts, helmregistry.ClientOptCredentialsFile(caFile))
}
// Enable plain HTTP if needed
if insecureSkipTLS {
opts = append(opts, helmregistry.ClientOptPlainHTTP())
}
registryClient, err := helmregistry.NewClient(opts...)
if err != nil {
return nil, err
}
return registryClient, nil
}
// defaultKeyring returns the default GPG keyring path for chart verification
func defaultKeyring() string {
if v, ok := os.LookupEnv("GNUPGHOME"); ok {
return filepath.Join(v, "pubring.gpg")
}
return filepath.Join(homedir.HomeDir(), ".gnupg", "pubring.gpg")
}
func NewHelmChart(path string) (*HelmChart, error) {
// Build chart dependencies before loading if Chart.lock exists
if err := buildDependencies(path); err != nil {
logger.L().Warning("Failed to build chart dependencies", helpers.String("path", path), helpers.Error(err))
}
chart, err := helmloader.Load(path)
if err != nil {
return nil, err
@@ -88,35 +36,6 @@ func NewHelmChart(path string) (*HelmChart, error) {
}, nil
}
// buildDependencies builds chart dependencies using the downloader manager
func buildDependencies(chartPath string) error {
// Create registry client for authentication
registryClient, err := newRegistryClient("", "", "", false, false, "", "")
if err != nil {
return fmt.Errorf("failed to create registry client: %w", err)
}
// Create downloader manager with required configuration
settings := cli.New()
manager := &helmdownloader.Manager{
Out: io.Discard, // Suppress output during scanning
ChartPath: chartPath,
Keyring: defaultKeyring(),
SkipUpdate: false, // Allow updates to get latest dependencies
Getters: helmgetter.All(settings),
RegistryClient: registryClient,
Debug: false,
}
// Build dependencies from Chart.lock file
err = manager.Build()
if e, ok := err.(helmdownloader.ErrRepoNotFound); ok {
return fmt.Errorf("%s. Please add missing repos via 'helm repo add'", e.Error())
}
return err
}
func (hc *HelmChart) GetName() string {
return hc.chart.Name()
}

View File

@@ -137,7 +137,6 @@ type ScanInfo struct {
TriggeredByCLI bool // indicates whether the scan was triggered by the CLI
ScanType ScanTypes
ScanImages bool
UseDefaultMatchers bool
ChartPath string
FilePath string
scanningContext *ScanningContext

View File

@@ -33,11 +33,10 @@ func TestUserConfirmed(t *testing.T) {
for _, tt := range tests {
t.Run(string(tt.input), func(t *testing.T) {
originalStdin := os.Stdin
r, w, _ := os.Pipe()
os.Stdin = r
defer func() {
os.Stdin = originalStdin
os.Stdin = os.Stdin
}()
go func() {

View File

@@ -7,6 +7,7 @@ import (
"regexp"
"strings"
"github.com/anchore/grype/grype/presenter/models"
"github.com/kubescape/go-logger"
"github.com/kubescape/kubescape/v3/core/cautils"
ksmetav1 "github.com/kubescape/kubescape/v3/core/meta/datastructures/v1"
@@ -110,9 +111,7 @@ func regexStringMatch(pattern, target string) bool {
// exception policy.
func isTargetImage(targets []Target, attributes Attributes) bool {
for _, target := range targets {
if regexStringMatch(target.Attributes.Registry, attributes.Registry) && regexStringMatch(target.Attributes.Organization, attributes.Organization) && regexStringMatch(target.Attributes.ImageName, attributes.ImageName) && regexStringMatch(target.Attributes.ImageTag, attributes.ImageTag) {
return true
}
return regexStringMatch(target.Attributes.Registry, attributes.Registry) && regexStringMatch(target.Attributes.Organization, attributes.Organization) && regexStringMatch(target.Attributes.ImageName, attributes.ImageName) && regexStringMatch(target.Attributes.ImageTag, attributes.ImageTag)
}
return false
@@ -162,16 +161,11 @@ func getUniqueVulnerabilitiesAndSeverities(policies []VulnerabilitiesIgnorePolic
return uniqueVulnsList, uniqueSeversList
}
func (ks *Kubescape) ScanImage(imgScanInfo *ksmetav1.ImageScanInfo, scanInfo *cautils.ScanInfo) (bool, error) {
func (ks *Kubescape) ScanImage(imgScanInfo *ksmetav1.ImageScanInfo, scanInfo *cautils.ScanInfo) (*models.PresenterConfig, error) {
logger.L().Start(fmt.Sprintf("Scanning image %s...", imgScanInfo.Image))
distCfg, installCfg, _ := imagescan.NewDefaultDBConfig()
svc, err := imagescan.NewScanServiceWithMatchers(distCfg, installCfg, imgScanInfo.UseDefaultMatchers)
if err != nil {
logger.L().StopError(fmt.Sprintf("Failed to initialize image scanner: %s", err))
return false, err
}
defer svc.Close()
dbCfg, _ := imagescan.NewDefaultDBConfig()
svc := imagescan.NewScanService(dbCfg)
creds := imagescan.RegistryCredentials{
Username: imgScanInfo.Username,
@@ -184,16 +178,16 @@ func (ks *Kubescape) ScanImage(imgScanInfo *ksmetav1.ImageScanInfo, scanInfo *ca
exceptionPolicies, err := GetImageExceptionsFromFile(imgScanInfo.Exceptions)
if err != nil {
logger.L().StopError(fmt.Sprintf("Failed to load exceptions from file: %s", imgScanInfo.Exceptions))
return false, err
return nil, err
}
vulnerabilityExceptions, severityExceptions = getUniqueVulnerabilitiesAndSeverities(exceptionPolicies, imgScanInfo.Image)
}
imageScanData, err := svc.Scan(ks.Context(), imgScanInfo.Image, creds, vulnerabilityExceptions, severityExceptions)
scanResults, err := svc.Scan(ks.Context(), imgScanInfo.Image, creds, vulnerabilityExceptions, severityExceptions)
if err != nil {
logger.L().StopError(fmt.Sprintf("Failed to scan image: %s", imgScanInfo.Image))
return false, err
return nil, err
}
logger.L().StopSuccess(fmt.Sprintf("Successfully scanned image: %s", imgScanInfo.Image))
@@ -206,7 +200,12 @@ func (ks *Kubescape) ScanImage(imgScanInfo *ksmetav1.ImageScanInfo, scanInfo *ca
resultsHandler := resultshandling.NewResultsHandler(nil, outputPrinters, uiPrinter)
resultsHandler.ImageScanData = []cautils.ImageScanData{*imageScanData}
resultsHandler.ImageScanData = []cautils.ImageScanData{
{
PresenterConfig: scanResults,
Image: imgScanInfo.Image,
},
}
return svc.ExceedsSeverityThreshold(imagescan.ParseSeverity(scanInfo.FailThresholdSeverity), imageScanData.Matches), resultsHandler.HandleResults(ks.Context(), scanInfo)
return scanResults, resultsHandler.HandleResults(ks.Context())
}

View File

@@ -241,33 +241,6 @@ func TestIsTargetImage(t *testing.T) {
},
expected: true,
},
{
targets: []Target{
{
Attributes: Attributes{
Registry: "quay.io",
Organization: "kubescape",
ImageName: "kubescape*",
ImageTag: "",
},
},
{
Attributes: Attributes{
Registry: "docker.io",
Organization: "library",
ImageName: "alpine",
ImageTag: "",
},
},
},
attributes: Attributes{
Registry: "docker.io",
Organization: "library",
ImageName: "alpine",
ImageTag: "latest",
},
expected: true,
},
}
for _, tt := range tests {

View File

@@ -7,13 +7,14 @@ import (
"sort"
"strings"
"github.com/jedib0t/go-pretty/v6/table"
"github.com/jedib0t/go-pretty/v6/text"
"github.com/jwalton/gchalk"
"github.com/kubescape/kubescape/v3/core/cautils"
metav1 "github.com/kubescape/kubescape/v3/core/meta/datastructures/v1"
"github.com/kubescape/kubescape/v3/core/pkg/resultshandling/printer"
v2 "github.com/kubescape/kubescape/v3/core/pkg/resultshandling/printer/v2"
"github.com/kubescape/kubescape/v3/core/pkg/resultshandling/printer/v2/prettyprinter/tableprinter/utils"
"github.com/maruel/natural"
"github.com/olekukonko/tablewriter"
)
var listFunc = map[string]func(context.Context, *metav1.ListPolicies) ([]string, error){
@@ -99,19 +100,30 @@ func prettyPrintListFormat(ctx context.Context, targetPolicy string, policies []
return
}
policyTable := table.NewWriter()
policyTable.SetOutputMirror(printer.GetWriter(ctx, ""))
policyTable := tablewriter.NewWriter(printer.GetWriter(ctx, ""))
policyTable.SetAutoWrapText(true)
header := fmt.Sprintf("Supported %s", targetPolicy)
policyTable.AppendHeader(table.Row{header})
policyTable.Style().Options.SeparateHeader = true
policyTable.Style().Options.SeparateRows = true
policyTable.Style().Format.HeaderAlign = text.AlignLeft
policyTable.Style().Format.Header = text.FormatDefault
policyTable.Style().Format.RowAlign = text.AlignCenter
policyTable.Style().Box = table.StyleBoxRounded
policyTable.SetHeader([]string{header})
policyTable.SetHeaderLine(true)
policyTable.SetRowLine(true)
policyTable.SetHeaderAlignment(tablewriter.ALIGN_LEFT)
policyTable.SetAutoFormatHeaders(false)
policyTable.SetAlignment(tablewriter.ALIGN_CENTER)
policyTable.SetUnicodeHVC(tablewriter.Regular, tablewriter.Regular, gchalk.Ansi256(238))
data := v2.Matrix{}
policyTable.AppendRows(generatePolicyRows(policies))
controlRows := generatePolicyRows(policies)
var headerColors []tablewriter.Colors
for range controlRows[0] {
headerColors = append(headerColors, tablewriter.Colors{tablewriter.Bold, tablewriter.FgHiYellowColor})
}
policyTable.SetHeaderColor(headerColors...)
data = append(data, controlRows...)
policyTable.AppendBulk(data)
policyTable.Render()
}
@@ -122,32 +134,40 @@ func jsonListFormat(_ context.Context, _ string, policies []string) {
}
func prettyPrintControls(ctx context.Context, policies []string) {
controlsTable := table.NewWriter()
controlsTable.SetOutputMirror(printer.GetWriter(ctx, ""))
controlsTable := tablewriter.NewWriter(printer.GetWriter(ctx, ""))
controlsTable.Style().Options.SeparateHeader = true
controlsTable.Style().Options.SeparateRows = true
controlsTable.Style().Format.HeaderAlign = text.AlignLeft
controlsTable.Style().Format.Header = text.FormatDefault
controlsTable.Style().Box = table.StyleBoxRounded
controlsTable.SetColumnConfigs([]table.ColumnConfig{{Number: 1, Align: text.AlignRight}})
controlsTable.SetAutoWrapText(false)
controlsTable.SetHeaderLine(true)
controlsTable.SetRowLine(true)
controlsTable.SetHeaderAlignment(tablewriter.ALIGN_LEFT)
controlsTable.SetAutoFormatHeaders(false)
controlsTable.SetUnicodeHVC(tablewriter.Regular, tablewriter.Regular, gchalk.Ansi256(238))
controlRows := generateControlRows(policies)
short := utils.CheckShortTerminalWidth(controlRows, table.Row{"Control ID", "Control name", "Docs", "Frameworks"})
short := utils.CheckShortTerminalWidth(controlRows, []string{"Control ID", "Control name", "Docs", "Frameworks"})
if short {
controlsTable.AppendHeader(table.Row{"Controls"})
controlsTable.SetAutoWrapText(false)
controlsTable.SetHeader([]string{"Controls"})
controlRows = shortFormatControlRows(controlRows)
} else {
controlsTable.AppendHeader(table.Row{"Control ID", "Control name", "Docs", "Frameworks"})
controlsTable.SetHeader([]string{"Control ID", "Control name", "Docs", "Frameworks"})
}
var headerColors []tablewriter.Colors
for range controlRows[0] {
headerColors = append(headerColors, tablewriter.Colors{tablewriter.Bold, tablewriter.FgHiYellowColor})
}
controlsTable.SetHeaderColor(headerColors...)
controlsTable.AppendRows(controlRows)
data := v2.Matrix{}
data = append(data, controlRows...)
controlsTable.AppendBulk(data)
controlsTable.Render()
}
func generateControlRows(policies []string) []table.Row {
rows := make([]table.Row, 0, len(policies))
func generateControlRows(policies []string) [][]string {
rows := [][]string{}
for _, control := range policies {
@@ -168,7 +188,7 @@ func generateControlRows(policies []string) []table.Row {
docs := cautils.GetControlLink(id)
currentRow := table.Row{id, control, docs, strings.Replace(framework, " ", "\n", -1)}
currentRow := []string{id, control, docs, strings.Replace(framework, " ", "\n", -1)}
rows = append(rows, currentRow)
}
@@ -176,19 +196,20 @@ func generateControlRows(policies []string) []table.Row {
return rows
}
func generatePolicyRows(policies []string) []table.Row {
rows := make([]table.Row, 0, len(policies))
func generatePolicyRows(policies []string) [][]string {
rows := [][]string{}
for _, policy := range policies {
rows = append(rows, table.Row{policy})
currentRow := []string{policy}
rows = append(rows, currentRow)
}
return rows
}
func shortFormatControlRows(controlRows []table.Row) []table.Row {
rows := make([]table.Row, 0, len(controlRows))
func shortFormatControlRows(controlRows [][]string) [][]string {
rows := [][]string{}
for _, controlRow := range controlRows {
rows = append(rows, table.Row{fmt.Sprintf("Control ID"+strings.Repeat(" ", 3)+": %+v\nControl Name"+strings.Repeat(" ", 1)+": %+v\nDocs"+strings.Repeat(" ", 9)+": %+v\nFrameworks"+strings.Repeat(" ", 3)+": %+v", controlRow[0], controlRow[1], controlRow[2], strings.Replace(controlRow[3].(string), "\n", " ", -1))})
rows = append(rows, []string{fmt.Sprintf("Control ID"+strings.Repeat(" ", 3)+": %+v\nControl Name"+strings.Repeat(" ", 1)+": %+v\nDocs"+strings.Repeat(" ", 9)+": %+v\nFrameworks"+strings.Repeat(" ", 3)+": %+v", controlRow[0], controlRow[1], controlRow[2], strings.Replace(controlRow[3], "\n", " ", -1))})
}
return rows
}

View File

@@ -9,7 +9,6 @@ import (
"sort"
"testing"
"github.com/jedib0t/go-pretty/v6/table"
metav1 "github.com/kubescape/kubescape/v3/core/meta/datastructures/v1"
"github.com/stretchr/testify/assert"
)
@@ -106,7 +105,7 @@ func TestGeneratePolicyRows_NonEmptyPolicyList(t *testing.T) {
result := generatePolicyRows(policies)
// Assert
assert.Equal(t, []table.Row{{"policy1"}, {"policy2"}, {"policy3"}}, result)
assert.Equal(t, [][]string{{"policy1"}, {"policy2"}, {"policy3"}}, result)
}
// Returns an empty 2D slice for an empty list of policies.
@@ -123,12 +122,12 @@ func TestGeneratePolicyRows_EmptyPolicyList(t *testing.T) {
// The function returns a list of rows, each containing a formatted string with control ID, control name, docs, and frameworks.
func TestShortFormatControlRows_ReturnsListOfRowsWithFormattedString(t *testing.T) {
controlRows := []table.Row{
controlRows := [][]string{
{"ID1", "Control 1", "Docs 1", "Framework 1"},
{"ID2", "Control 2", "Docs 2", "Framework 2"},
}
want := []table.Row{
want := [][]string{
{"Control ID : ID1\nControl Name : Control 1\nDocs : Docs 1\nFrameworks : Framework 1"},
{"Control ID : ID2\nControl Name : Control 2\nDocs : Docs 2\nFrameworks : Framework 2"},
}
@@ -140,12 +139,12 @@ func TestShortFormatControlRows_ReturnsListOfRowsWithFormattedString(t *testing.
// The function formats the control rows correctly, replacing newlines in the frameworks column with line breaks.
func TestShortFormatControlRows_FormatsControlRowsCorrectly(t *testing.T) {
controlRows := []table.Row{
controlRows := [][]string{
{"ID1", "Control 1", "Docs 1", "Framework\n1"},
{"ID2", "Control 2", "Docs 2", "Framework\n2"},
}
want := []table.Row{
want := [][]string{
{"Control ID : ID1\nControl Name : Control 1\nDocs : Docs 1\nFrameworks : Framework 1"},
{"Control ID : ID2\nControl Name : Control 2\nDocs : Docs 2\nFrameworks : Framework 2"},
}
@@ -157,11 +156,11 @@ func TestShortFormatControlRows_FormatsControlRowsCorrectly(t *testing.T) {
// The function handles a control row with an empty control ID.
func TestShortFormatControlRows_HandlesControlRowWithEmptyControlID(t *testing.T) {
controlRows := []table.Row{
controlRows := [][]string{
{"", "Control 1", "Docs 1", "Framework 1"},
}
want := []table.Row{
want := [][]string{
{"Control ID : \nControl Name : Control 1\nDocs : Docs 1\nFrameworks : Framework 1"},
}
@@ -172,11 +171,11 @@ func TestShortFormatControlRows_HandlesControlRowWithEmptyControlID(t *testing.T
// The function handles a control row with an empty control name.
func TestShortFormatControlRows_HandlesControlRowWithEmptyControlName(t *testing.T) {
controlRows := []table.Row{
controlRows := [][]string{
{"ID1", "", "Docs 1", "Framework 1"},
}
want := []table.Row{
want := [][]string{
{"Control ID : ID1\nControl Name : \nDocs : Docs 1\nFrameworks : Framework 1"},
}
@@ -193,7 +192,7 @@ func TestGenerateControlRowsWithAllFields(t *testing.T) {
"3|Control 3|Framework 3",
}
want := []table.Row{
want := [][]string{
{"1", "Control 1", "https://hub.armosec.io/docs/1", "Framework\n1"},
{"2", "Control 2", "https://hub.armosec.io/docs/2", "Framework\n2"},
{"3", "Control 3", "https://hub.armosec.io/docs/3", "Framework\n3"},
@@ -216,7 +215,7 @@ func TestGenerateControlRowsHandlesPoliciesWithEmptyStringOrNoPipesOrOnePipeMiss
"5|Control 5||Extra 5",
}
expectedRows := []table.Row{
expectedRows := [][]string{
{"", "", "https://hub.armosec.io/docs/", ""},
{"1", "", "https://hub.armosec.io/docs/1", ""},
{"2", "Control 2", "https://hub.armosec.io/docs/2", "Framework\n2"},
@@ -253,18 +252,18 @@ func TestGenerateTableWithCorrectHeadersAndRows(t *testing.T) {
os.Stdout = rescueStdout
// got := buf.String()
want := `────────────┬──────────────┬───────────────────────────────┬────────────
want := `────────────┬──────────────┬───────────────────────────────┬────────────
│ Control ID │ Control name │ Docs │ Frameworks │
├────────────┼──────────────┼───────────────────────────────┼────────────┤
│ 1 │ Control 1 │ https://hub.armosec.io/docs/1 │ Framework │
│ │ │ │ 1
│ │ │ │ 1
├────────────┼──────────────┼───────────────────────────────┼────────────┤
│ 2 │ Control 2 │ https://hub.armosec.io/docs/2 │ Framework │
│ │ │ │ 2
│ │ │ │ 2
├────────────┼──────────────┼───────────────────────────────┼────────────┤
│ 3 │ Control 3 │ https://hub.armosec.io/docs/3 │ Framework │
│ │ │ │ 3
────────────┴──────────────┴───────────────────────────────┴────────────
│ │ │ │ 3
────────────┴──────────────┴───────────────────────────────┴────────────
`
assert.Equal(t, want, string(got))
@@ -295,7 +294,7 @@ func TestGenerateTableWithMalformedPoliciesAndPrettyPrintHeadersAndRows(t *testi
os.Stdout = rescueStdout
want := `────────────┬──────────────┬───────────────────────────────┬────────────
want := `────────────┬──────────────┬───────────────────────────────┬────────────
│ Control ID │ Control name │ Docs │ Frameworks │
├────────────┼──────────────┼───────────────────────────────┼────────────┤
│ │ │ https://hub.armosec.io/docs/ │ │
@@ -303,18 +302,18 @@ func TestGenerateTableWithMalformedPoliciesAndPrettyPrintHeadersAndRows(t *testi
│ 1 │ │ https://hub.armosec.io/docs/1 │ │
├────────────┼──────────────┼───────────────────────────────┼────────────┤
│ 2 │ Control 2 │ https://hub.armosec.io/docs/2 │ Framework │
│ │ │ │ 2
│ │ │ │ 2
├────────────┼──────────────┼───────────────────────────────┼────────────┤
│ 3 │ Control 3 │ https://hub.armosec.io/docs/3 │ Framework │
│ │ │ │ 3
│ │ │ │ 3
├────────────┼──────────────┼───────────────────────────────┼────────────┤
│ 4 │ │ https://hub.armosec.io/docs/4 │ Framework │
│ │ │ │ 4
│ │ │ │ 4
├────────────┼──────────────┼───────────────────────────────┼────────────┤
│ │ │ https://hub.armosec.io/docs/ │ │
├────────────┼──────────────┼───────────────────────────────┼────────────┤
│ 5 │ Control 5 │ https://hub.armosec.io/docs/5 │ │
────────────┴──────────────┴───────────────────────────────┴────────────
────────────┴──────────────┴───────────────────────────────┴────────────
`
assert.Equal(t, want, string(got))

View File

@@ -1,22 +1,15 @@
package core
import (
"bytes"
"context"
"errors"
"fmt"
"os"
"slices"
"strings"
"time"
"github.com/anchore/clio"
grypejson "github.com/anchore/grype/grype/presenter/json"
"github.com/anchore/grype/grype/presenter"
"github.com/anchore/grype/grype/presenter/models"
copaGrype "github.com/anubhav06/copa-grype/grype"
"github.com/containerd/platforms"
"github.com/docker/buildx/build"
"github.com/docker/cli/cli/config"
"github.com/kubescape/go-logger"
"github.com/kubescape/go-logger/helpers"
"github.com/kubescape/kubescape/v3/core/cautils"
@@ -24,37 +17,21 @@ import (
"github.com/kubescape/kubescape/v3/core/pkg/resultshandling"
"github.com/kubescape/kubescape/v3/core/pkg/resultshandling/printer"
"github.com/kubescape/kubescape/v3/pkg/imagescan"
"github.com/moby/buildkit/client"
"github.com/moby/buildkit/client/llb"
"github.com/moby/buildkit/exporter/containerimage/exptypes"
gwclient "github.com/moby/buildkit/frontend/gateway/client"
"github.com/moby/buildkit/session"
"github.com/moby/buildkit/session/auth/authprovider"
"github.com/project-copacetic/copacetic/pkg/buildkit"
"github.com/project-copacetic/copacetic/pkg/pkgmgr"
"github.com/project-copacetic/copacetic/pkg/types/unversioned"
"github.com/project-copacetic/copacetic/pkg/utils"
"github.com/quay/claircore/osrelease"
log "github.com/sirupsen/logrus"
)
const (
copaProduct = "copa"
)
func (ks *Kubescape) Patch(patchInfo *ksmetav1.PatchInfo, scanInfo *cautils.ScanInfo) (bool, error) {
func (ks *Kubescape) Patch(patchInfo *ksmetav1.PatchInfo, scanInfo *cautils.ScanInfo) (*models.PresenterConfig, error) {
// ===================== Scan the image =====================
logger.L().Start(fmt.Sprintf("Scanning image: %s", patchInfo.Image))
// Setup the scan service
distCfg, installCfg, _ := imagescan.NewDefaultDBConfig()
svc, err := imagescan.NewScanServiceWithMatchers(distCfg, installCfg, scanInfo.UseDefaultMatchers)
if err != nil {
logger.L().StopError(fmt.Sprintf("Failed to initialize image scanner: %s", err))
return false, err
}
defer svc.Close()
dbCfg, _ := imagescan.NewDefaultDBConfig()
svc := imagescan.NewScanService(dbCfg)
creds := imagescan.RegistryCredentials{
Username: patchInfo.Username,
Password: patchInfo.Password,
@@ -62,21 +39,15 @@ func (ks *Kubescape) Patch(patchInfo *ksmetav1.PatchInfo, scanInfo *cautils.Scan
// Scan the image
scanResults, err := svc.Scan(ks.Context(), patchInfo.Image, creds, nil, nil)
if err != nil {
return false, err
}
model, err := models.NewDocument(clio.Identification{}, scanResults.Packages, scanResults.Context,
*scanResults.RemainingMatches, scanResults.IgnoredMatches, scanResults.VulnerabilityProvider, nil, nil, models.DefaultSortStrategy, false)
if err != nil {
return false, fmt.Errorf("failed to create document: %w", err)
return nil, err
}
// If the scan results ID is empty, set it to "grype"
if model.Descriptor.Name == "" {
model.Descriptor.Name = "grype"
if scanResults.ID.Name == "" {
scanResults.ID.Name = "grype"
}
// Save the scan results to a file in json format
pres := grypejson.NewPresenter(models.PresenterConfig{Document: model, SBOM: scanResults.SBOM})
pres := presenter.GetPresenter("json", "", false, *scanResults)
fileName := fmt.Sprintf("%s:%s.json", patchInfo.ImageName, patchInfo.ImageTag)
fileName = strings.ReplaceAll(fileName, "/", "-")
@@ -84,7 +55,7 @@ func (ks *Kubescape) Patch(patchInfo *ksmetav1.PatchInfo, scanInfo *cautils.Scan
writer := printer.GetWriter(ks.Context(), fileName)
if err = pres.Present(writer); err != nil {
return false, err
return nil, err
}
logger.L().StopSuccess(fmt.Sprintf("Successfully scanned image: %s", patchInfo.Image))
@@ -98,7 +69,7 @@ func (ks *Kubescape) Patch(patchInfo *ksmetav1.PatchInfo, scanInfo *cautils.Scan
}
if err = copaPatch(ks.Context(), patchInfo.Timeout, patchInfo.BuildkitAddress, patchInfo.Image, fileName, patchedImageName, "", patchInfo.IgnoreError, patchInfo.BuildKitOpts); err != nil {
return false, err
return nil, err
}
// Restore the output streams
@@ -112,7 +83,7 @@ func (ks *Kubescape) Patch(patchInfo *ksmetav1.PatchInfo, scanInfo *cautils.Scan
scanResultsPatched, err := svc.Scan(ks.Context(), patchedImageName, creds, nil, nil)
if err != nil {
return false, err
return nil, err
}
logger.L().StopSuccess(fmt.Sprintf("Successfully re-scanned image: %s", patchedImageName))
@@ -128,9 +99,14 @@ func (ks *Kubescape) Patch(patchInfo *ksmetav1.PatchInfo, scanInfo *cautils.Scan
outputPrinters := GetOutputPrinters(scanInfo, ks.Context(), "")
uiPrinter := GetUIPrinter(ks.Context(), scanInfo, "")
resultsHandler := resultshandling.NewResultsHandler(nil, outputPrinters, uiPrinter)
resultsHandler.ImageScanData = []cautils.ImageScanData{*scanResultsPatched}
resultsHandler.ImageScanData = []cautils.ImageScanData{
{
PresenterConfig: scanResultsPatched,
Image: patchedImageName,
},
}
return svc.ExceedsSeverityThreshold(imagescan.ParseSeverity(scanInfo.FailThresholdSeverity), scanResultsPatched.Matches), resultsHandler.HandleResults(ks.Context(), scanInfo)
return scanResultsPatched, resultsHandler.HandleResults(ks.Context())
}
func disableCopaLogger() {
@@ -184,185 +160,43 @@ func patchWithContext(ctx context.Context, buildkitAddr, image, reportFile, patc
}
}
var updates *unversioned.UpdateManifest
// Parse report for update packages
updates, err := tryParseScanReport(reportFile)
if err != nil {
return err
}
bkClient, err := buildkit.NewClient(ctx, bkOpts)
client, err := buildkit.NewClient(ctx, bkOpts)
if err != nil {
return fmt.Errorf("copa: error creating buildkit client :: %w", err)
return err
}
defer bkClient.Close()
defer client.Close()
dockerConfig := config.LoadDefaultConfigFile(os.Stderr)
cfg := authprovider.DockerAuthProviderConfig{ConfigFile: dockerConfig}
attachable := []session.Attachable{authprovider.NewDockerAuthProvider(cfg)}
solveOpt := client.SolveOpt{
Exports: []client.ExportEntry{
{
Type: client.ExporterImage,
Attrs: map[string]string{
"name": patchedImageName,
"push": "true",
},
},
},
Frontend: "", // i.e. we are passing in the llb.Definition directly
Session: attachable, // used for authprovider, sshagentprovider and secretprovider
}
solveOpt.SourcePolicy, err = build.ReadSourcePolicy()
// Configure buildctl/client for use by package manager
config, err := buildkit.InitializeBuildkitConfig(ctx, client, image, updates)
if err != nil {
return fmt.Errorf("copa: error reading source policy :: %w", err)
return err
}
buildChannel := make(chan *client.SolveStatus)
_, err = bkClient.Build(ctx, solveOpt, copaProduct, func(ctx context.Context, c gwclient.Client) (*gwclient.Result, error) {
// Configure buildctl/client for use by package manager
config, err := buildkit.InitializeBuildkitConfig(ctx, c, image)
if err != nil {
return nil, fmt.Errorf("copa: error initializing buildkit config for image %s :: %w", image, err)
}
// Create package manager helper
pkgmgr, err := pkgmgr.GetPackageManager(updates.Metadata.OS.Type, config, workingFolder)
if err != nil {
return err
}
// Create package manager helper
var manager pkgmgr.PackageManager
if reportFile == "" {
// determine OS family
fileBytes, err := buildkit.ExtractFileFromState(ctx, c, &config.ImageState, "/etc/os-release")
if err != nil {
return nil, fmt.Errorf("unable to extract /etc/os-release file from state %w", err)
}
// Export the patched image state to Docker
patchedImageState, _, err := pkgmgr.InstallUpdates(ctx, updates, ignoreError)
if err != nil {
return err
}
osType, err := getOSType(ctx, fileBytes)
if err != nil {
return nil, fmt.Errorf("copa: error getting os type :: %w", err)
}
osVersion, err := getOSVersion(ctx, fileBytes)
if err != nil {
return nil, fmt.Errorf("copa: error getting os version :: %w", err)
}
// get package manager based on os family type
manager, err = pkgmgr.GetPackageManager(osType, osVersion, config, workingFolder)
if err != nil {
return nil, fmt.Errorf("copa: error getting package manager for ostype=%s, version=%s :: %w", osType, osVersion, err)
}
// do not specify updates, will update all
updates = nil
} else {
// get package manager based on os family type
manager, err = pkgmgr.GetPackageManager(updates.Metadata.OS.Type, updates.Metadata.OS.Version, config, workingFolder)
if err != nil {
return nil, fmt.Errorf("copa: error getting package manager by family type: ostype=%s, osversion=%s :: %w", updates.Metadata.OS.Type, updates.Metadata.OS.Version, err)
}
}
// Export the patched image state to Docker
// TODO: Add support for other output modes as buildctl does.
log.Infof("Patching %d vulnerabilities", len(updates.Updates))
patchedImageState, errPkgs, err := manager.InstallUpdates(ctx, updates, ignoreError)
log.Infof("Error is: %v", err)
if err != nil {
return nil, nil
}
platform := platforms.Normalize(platforms.DefaultSpec())
if platform.OS != "linux" {
platform.OS = "linux"
}
def, err := patchedImageState.Marshal(ctx, llb.Platform(platform))
if err != nil {
return nil, err
}
res, err := c.Solve(ctx, gwclient.SolveRequest{
Definition: def.ToPB(),
Evaluate: true,
})
if err != nil {
return nil, err
}
res.AddMeta(exptypes.ExporterImageConfigKey, config.ConfigData)
// Currently can only validate updates if updating via scanner
if reportFile != "" {
// create a new manifest with the successfully patched packages
validatedManifest := &unversioned.UpdateManifest{
Metadata: unversioned.Metadata{
OS: unversioned.OS{
Type: updates.Metadata.OS.Type,
Version: updates.Metadata.OS.Version,
},
Config: unversioned.Config{
Arch: updates.Metadata.Config.Arch,
},
},
Updates: []unversioned.UpdatePackage{},
}
for _, update := range updates.Updates {
if !slices.Contains(errPkgs, update.Name) {
validatedManifest.Updates = append(validatedManifest.Updates, update)
}
}
}
return res, nil
}, buildChannel)
if err = buildkit.SolveToDocker(ctx, config.Client, patchedImageState, config.ConfigData, patchedImageName); err != nil {
return err
}
return nil
}
func getOSType(ctx context.Context, osreleaseBytes []byte) (string, error) {
r := bytes.NewReader(osreleaseBytes)
osData, err := osrelease.Parse(ctx, r)
if err != nil {
return "", fmt.Errorf("unable to parse os-release data %w", err)
}
osType := strings.ToLower(osData["NAME"])
switch {
case strings.Contains(osType, "alpine"):
return "alpine", nil
case strings.Contains(osType, "debian"):
return "debian", nil
case strings.Contains(osType, "ubuntu"):
return "ubuntu", nil
case strings.Contains(osType, "amazon"):
return "amazon", nil
case strings.Contains(osType, "centos"):
return "centos", nil
case strings.Contains(osType, "mariner"):
return "cbl-mariner", nil
case strings.Contains(osType, "azure linux"):
return "azurelinux", nil
case strings.Contains(osType, "red hat"):
return "redhat", nil
case strings.Contains(osType, "rocky"):
return "rocky", nil
case strings.Contains(osType, "oracle"):
return "oracle", nil
case strings.Contains(osType, "alma"):
return "alma", nil
default:
log.Error("unsupported osType ", osType)
return "", errors.ErrUnsupported
}
}
func getOSVersion(ctx context.Context, osreleaseBytes []byte) (string, error) {
r := bytes.NewReader(osreleaseBytes)
osData, err := osrelease.Parse(ctx, r)
if err != nil {
return "", fmt.Errorf("unable to parse os-release data %w", err)
}
return osData["VERSION_ID"], nil
}
// This function adds support to copa for patching Kubescape produced results
func tryParseScanReport(file string) (*unversioned.UpdateManifest, error) {

View File

@@ -202,7 +202,7 @@ func (ks *Kubescape) Scan(scanInfo *cautils.ScanInfo) (*resultshandling.ResultsH
}
if scanInfo.ScanImages {
scanImages(scanInfo.ScanType, scanData, ks.Context(), resultsHandling, scanInfo)
scanImages(scanInfo.ScanType, scanData, ks.Context(), resultsHandling)
}
// ========================= results handling =====================
resultsHandling.SetData(scanData)
@@ -214,7 +214,7 @@ func (ks *Kubescape) Scan(scanInfo *cautils.ScanInfo) (*resultshandling.ResultsH
return resultsHandling, nil
}
func scanImages(scanType cautils.ScanTypes, scanData *cautils.OPASessionObj, ctx context.Context, resultsHandling *resultshandling.ResultsHandler, scanInfo *cautils.ScanInfo) {
func scanImages(scanType cautils.ScanTypes, scanData *cautils.OPASessionObj, ctx context.Context, resultsHandling *resultshandling.ResultsHandler) {
var imagesToScan []string
if scanType == cautils.ScanTypeWorkload {
@@ -243,13 +243,8 @@ func scanImages(scanType cautils.ScanTypes, scanData *cautils.OPASessionObj, ctx
}
}
distCfg, installCfg, _ := imagescan.NewDefaultDBConfig()
svc, err := imagescan.NewScanServiceWithMatchers(distCfg, installCfg, scanInfo.UseDefaultMatchers)
if err != nil {
logger.L().StopError(fmt.Sprintf("Failed to initialize image scanner: %s", err))
return
}
defer svc.Close()
dbCfg, _ := imagescan.NewDefaultDBConfig()
svc := imagescan.NewScanService(dbCfg)
for _, img := range imagesToScan {
logger.L().Start("Scanning", helpers.String("image", img))
@@ -260,14 +255,17 @@ func scanImages(scanType cautils.ScanTypes, scanData *cautils.OPASessionObj, ctx
}
}
func scanSingleImage(ctx context.Context, img string, svc *imagescan.Service, resultsHandling *resultshandling.ResultsHandler) error {
func scanSingleImage(ctx context.Context, img string, svc imagescan.Service, resultsHandling *resultshandling.ResultsHandler) error {
scanResults, err := svc.Scan(ctx, img, imagescan.RegistryCredentials{}, nil, nil)
if err != nil {
return err
}
resultsHandling.ImageScanData = append(resultsHandling.ImageScanData, *scanResults)
resultsHandling.ImageScanData = append(resultsHandling.ImageScanData, cautils.ImageScanData{
Image: img,
PresenterConfig: scanResults,
})
return nil
}

View File

@@ -1,9 +1,8 @@
package v1
type ImageScanInfo struct {
Username string
Password string
Image string
Exceptions string
UseDefaultMatchers bool
Username string
Password string
Image string
Exceptions string
}

View File

@@ -3,6 +3,7 @@ package meta
import (
"context"
"github.com/anchore/grype/grype/presenter/models"
"github.com/kubescape/kubescape/v3/core/cautils"
metav1 "github.com/kubescape/kubescape/v3/core/meta/datastructures/v1"
"github.com/kubescape/kubescape/v3/core/pkg/resultshandling"
@@ -26,8 +27,8 @@ type IKubescape interface {
Fix(fixInfo *metav1.FixInfo) error
// patch
Patch(patchInfo *metav1.PatchInfo, scanInfo *cautils.ScanInfo) (bool, error)
Patch(patchInfo *metav1.PatchInfo, scanInfo *cautils.ScanInfo) (*models.PresenterConfig, error)
// scan image
ScanImage(imgScanInfo *metav1.ImageScanInfo, scanInfo *cautils.ScanInfo) (bool, error)
ScanImage(imgScanInfo *metav1.ImageScanInfo, scanInfo *cautils.ScanInfo) (*models.PresenterConfig, error)
}

View File

@@ -3,6 +3,7 @@ package mocks
import (
"context"
"github.com/anchore/grype/grype/presenter/models"
"github.com/kubescape/kubescape/v3/core/cautils"
metav1 "github.com/kubescape/kubescape/v3/core/meta/datastructures/v1"
"github.com/kubescape/kubescape/v3/core/pkg/resultshandling"
@@ -14,38 +15,38 @@ func (m *MockIKubescape) Context() context.Context {
return context.TODO()
}
func (m *MockIKubescape) Scan(_ *cautils.ScanInfo) (*resultshandling.ResultsHandler, error) {
func (m *MockIKubescape) Scan(scanInfo *cautils.ScanInfo) (*resultshandling.ResultsHandler, error) {
return nil, nil
}
func (m *MockIKubescape) List(_ *metav1.ListPolicies) error {
func (m *MockIKubescape) List(listPolicies *metav1.ListPolicies) error {
return nil
}
func (m *MockIKubescape) Download(_ *metav1.DownloadInfo) error {
func (m *MockIKubescape) Download(downloadInfo *metav1.DownloadInfo) error {
return nil
}
func (m *MockIKubescape) SetCachedConfig(_ *metav1.SetConfig) error {
func (m *MockIKubescape) SetCachedConfig(setConfig *metav1.SetConfig) error {
return nil
}
func (m *MockIKubescape) ViewCachedConfig(_ *metav1.ViewConfig) error {
func (m *MockIKubescape) ViewCachedConfig(viewConfig *metav1.ViewConfig) error {
return nil
}
func (m *MockIKubescape) DeleteCachedConfig(_ *metav1.DeleteConfig) error {
func (m *MockIKubescape) DeleteCachedConfig(deleteConfig *metav1.DeleteConfig) error {
return nil
}
func (m *MockIKubescape) Fix(_ *metav1.FixInfo) error {
func (m *MockIKubescape) Fix(fixInfo *metav1.FixInfo) error {
return nil
}
func (m *MockIKubescape) Patch(_ *metav1.PatchInfo, _ *cautils.ScanInfo) (bool, error) {
return false, nil
func (m *MockIKubescape) Patch(patchInfo *metav1.PatchInfo, scanInfo *cautils.ScanInfo) (*models.PresenterConfig, error) {
return nil, nil
}
func (m *MockIKubescape) ScanImage(_ *metav1.ImageScanInfo, _ *cautils.ScanInfo) (bool, error) {
return false, nil
func (m *MockIKubescape) ScanImage(imgScanInfo *metav1.ImageScanInfo, scanInfo *cautils.ScanInfo) (*models.PresenterConfig, error) {
return nil, nil
}

View File

@@ -96,7 +96,7 @@ func (hsh *HostSensorHandler) Init(ctx context.Context) error {
hsh.populatePodNamesToNodeNames(ctx, log)
if err := hsh.checkPodForEachNode(); err != nil {
return fmt.Errorf("%s: %v", failedToValidateHostSensorPodStatus, err)
logger.L().Ctx(ctx).Warning(failedToValidateHostSensorPodStatus, helpers.Error(err))
}
return nil

View File

@@ -6,6 +6,7 @@ import (
"github.com/kubescape/go-logger"
"github.com/kubescape/go-logger/helpers"
"github.com/kubescape/kubescape/v3/core/cautils"
"github.com/kubescape/opa-utils/objectsenvelopes/hostsensor"
)
@@ -27,18 +28,19 @@ type workerPool struct {
func newWorkerPool() workerPool {
wp := workerPool{}
wp.noOfWorkers = noOfWorkers
wp.noOfWorkers, _ = cautils.ParseIntEnvVar("KUBESCAPE_WORKERS", noOfWorkers)
wp.init()
return wp
}
func (wp *workerPool) init(noOfPods ...int) {
if len(noOfPods) > 0 && noOfPods[0] < noOfWorkers {
if len(noOfPods) > 0 && noOfPods[0] < wp.noOfWorkers {
wp.noOfWorkers = noOfPods[0]
}
logger.L().Debug("Initializing worker pool", helpers.Int("noOfWorkers", wp.noOfWorkers))
// init the channels
wp.jobs = make(chan job, noOfWorkers)
wp.results = make(chan hostsensor.HostSensorDataEnvelope, noOfWorkers)
wp.jobs = make(chan job, wp.noOfWorkers)
wp.results = make(chan hostsensor.HostSensorDataEnvelope, wp.noOfWorkers)
wp.done = make(chan bool)
}
@@ -57,7 +59,7 @@ func (wp *workerPool) hostSensorWorker(ctx context.Context, hsh *HostSensorHandl
}
func (wp *workerPool) createWorkerPool(ctx context.Context, hsh *HostSensorHandler, wg *sync.WaitGroup, log *LogsMap) {
for i := 0; i < noOfWorkers; i++ {
for i := 0; i < wp.noOfWorkers; i++ {
wg.Add(1)
go wp.hostSensorWorker(ctx, hsh, wg, log)
}

View File

@@ -19,10 +19,10 @@ import (
"github.com/kubescape/opa-utils/reporthandling/results/v1/resourcesresults"
reporthandlingv2 "github.com/kubescape/opa-utils/reporthandling/v2"
"github.com/kubescape/opa-utils/resources"
"github.com/open-policy-agent/opa/v1/ast"
"github.com/open-policy-agent/opa/v1/rego"
"github.com/open-policy-agent/opa/v1/storage"
opaprint "github.com/open-policy-agent/opa/v1/topdown/print"
"github.com/open-policy-agent/opa/ast"
"github.com/open-policy-agent/opa/rego"
"github.com/open-policy-agent/opa/storage"
opaprint "github.com/open-policy-agent/opa/topdown/print"
"go.opentelemetry.io/otel"
)
@@ -324,7 +324,6 @@ func (opap *OPAProcessor) runRegoOnK8s(ctx context.Context, rule *reporthandling
// NOTE: OPA module compilation is the most resource-intensive operation.
compiled, err := ast.CompileModulesWithOpt(modules, ast.CompileOpts{
EnablePrintStatements: opap.printEnabled,
ParserOptions: ast.ParserOptions{RegoVersion: ast.RegoV0},
})
if err != nil {
return nil, fmt.Errorf("in 'runRegoOnK8s', failed to compile rule, name: %s, reason: %w", rule.Name, err)
@@ -352,7 +351,6 @@ func (opap *OPAProcessor) Print(ctx opaprint.Context, str string) error {
func (opap *OPAProcessor) regoEval(ctx context.Context, inputObj []map[string]interface{}, compiledRego *ast.Compiler, store *storage.Store) ([]reporthandling.RuleResponse, error) {
rego := rego.New(
rego.SetRegoVersion(ast.RegoV0),
rego.Query("data.armo_builtins"), // get package name from rule
rego.Compiler(compiledRego),
rego.Input(inputObj),

View File

@@ -11,10 +11,10 @@ import (
"github.com/kubescape/opa-utils/reporthandling"
"github.com/kubescape/opa-utils/reporthandling/apis"
"github.com/kubescape/opa-utils/reporthandling/results/v1/reportsummary"
"github.com/open-policy-agent/opa/v1/ast"
"github.com/open-policy-agent/opa/v1/rego"
"github.com/open-policy-agent/opa/v1/topdown/builtins"
"github.com/open-policy-agent/opa/v1/types"
"github.com/open-policy-agent/opa/ast"
"github.com/open-policy-agent/opa/rego"
"github.com/open-policy-agent/opa/topdown/builtins"
"github.com/open-policy-agent/opa/types"
)
// convertFrameworksToPolicies convert list of frameworks to list of policies

View File

@@ -232,7 +232,7 @@ func (k8sHandler *K8sResourceHandler) collectCloudResources(ctx context.Context,
if !strings.Contains(err.Error(), cloudv1.NotSupportedMsg) {
// Return error with useful info on how to configure credentials for getting cloud provider info
logger.L().Debug("failed to get cloud data", helpers.String("resourceKind", resourceKind), helpers.Error(err))
err = fmt.Errorf("failed to get %s descriptive information. Read more: https://kubescape.io/docs/integrations/kubescape-integration-with-cloud-providers/", strings.ToUpper(k8sHandler.cloudProvider))
err = fmt.Errorf("failed to get %s descriptive information. Read more: https://hub.armosec.io/docs/kubescape-integration-with-cloud-providers", strings.ToUpper(k8sHandler.cloudProvider))
cautils.SetInfoMapForResources(err.Error(), cloudResources, sessionObj.InfoMap)
}
@@ -478,15 +478,8 @@ func (k8sHandler *K8sResourceHandler) setCloudProvider() error {
// NoSchedule taint with empty value is usually applied to controlplane
func isMasterNodeTaints(taints []v1.Taint) bool {
for _, taint := range taints {
if taint.Effect == v1.TaintEffectNoSchedule {
// NoSchedule taint with empty value is usually applied to controlplane
if taint.Value == "" {
return true
}
if taint.Key == "node-role.kubernetes.io/control-plane" && taint.Value == "true" {
return true
}
if taint.Effect == v1.TaintEffectNoSchedule && taint.Value == "" {
return true
}
}
return false

View File

@@ -14,264 +14,264 @@ import (
)
func TestIsMasterNodeTaints(t *testing.T) {
noTaintNodeJson := `
noTaintNode := `
{
"apiVersion": "v1",
"kind": "Node",
"metadata": {
"annotations": {
"kubeadm.alpha.kubernetes.io/cri-socket": "/var/run/dockershim.sock",
"node.alpha.kubernetes.io/ttl": "0",
"volumes.kubernetes.io/controller-managed-attach-detach": "true"
},
"creationTimestamp": "2022-05-16T10:52:32Z",
"labels": {
"beta.kubernetes.io/arch": "amd64",
"beta.kubernetes.io/os": "linux",
"kubernetes.io/arch": "amd64",
"kubernetes.io/hostname": "danielg-minikube",
"kubernetes.io/os": "linux",
"minikube.k8s.io/commit": "3e64b11ed75e56e4898ea85f96b2e4af0301f43d",
"minikube.k8s.io/name": "danielg-minikube",
"minikube.k8s.io/updated_at": "2022_05_16T13_52_35_0700",
"minikube.k8s.io/version": "v1.25.1",
"node-role.kubernetes.io/control-plane": "",
"node-role.kubernetes.io/master": "",
"node.kubernetes.io/exclude-from-external-load-balancers": ""
},
"name": "danielg-minikube",
"resourceVersion": "9432",
"uid": "fc4afcb6-4ca4-4038-ba54-5e16065a614a"
},
"spec": {
"podCIDR": "10.244.0.0/24",
"podCIDRs": [
"10.244.0.0/24"
]
},
"status": {
"addresses": [
{
"apiVersion": "v1",
"kind": "Node",
"metadata": {
"annotations": {
"kubeadm.alpha.kubernetes.io/cri-socket": "/var/run/dockershim.sock",
"node.alpha.kubernetes.io/ttl": "0",
"volumes.kubernetes.io/controller-managed-attach-detach": "true"
},
"creationTimestamp": "2022-05-16T10:52:32Z",
"labels": {
"beta.kubernetes.io/arch": "amd64",
"beta.kubernetes.io/os": "linux",
"kubernetes.io/arch": "amd64",
"kubernetes.io/hostname": "danielg-minikube",
"kubernetes.io/os": "linux",
"minikube.k8s.io/commit": "3e64b11ed75e56e4898ea85f96b2e4af0301f43d",
"minikube.k8s.io/name": "danielg-minikube",
"minikube.k8s.io/updated_at": "2022_05_16T13_52_35_0700",
"minikube.k8s.io/version": "v1.25.1",
"node-role.kubernetes.io/control-plane": "",
"node-role.kubernetes.io/master": "",
"node.kubernetes.io/exclude-from-external-load-balancers": ""
},
"name": "danielg-minikube",
"resourceVersion": "9432",
"uid": "fc4afcb6-4ca4-4038-ba54-5e16065a614a"
"address": "192.168.49.2",
"type": "InternalIP"
},
"spec": {
"podCIDR": "10.244.0.0/24",
"podCIDRs": [
"10.244.0.0/24"
]
{
"address": "danielg-minikube",
"type": "Hostname"
}
],
"allocatable": {
"cpu": "4",
"ephemeral-storage": "94850516Ki",
"hugepages-2Mi": "0",
"memory": "10432976Ki",
"pods": "110"
},
"capacity": {
"cpu": "4",
"ephemeral-storage": "94850516Ki",
"hugepages-2Mi": "0",
"memory": "10432976Ki",
"pods": "110"
},
"conditions": [
{
"lastHeartbeatTime": "2022-05-16T14:14:31Z",
"lastTransitionTime": "2022-05-16T10:52:29Z",
"message": "kubelet has sufficient memory available",
"reason": "KubeletHasSufficientMemory",
"status": "False",
"type": "MemoryPressure"
},
"status": {
"addresses": [
{
"address": "192.168.49.2",
"type": "InternalIP"
},
{
"address": "danielg-minikube",
"type": "Hostname"
}
],
"allocatable": {
"cpu": "4",
"ephemeral-storage": "94850516Ki",
"hugepages-2Mi": "0",
"memory": "10432976Ki",
"pods": "110"
},
"capacity": {
"cpu": "4",
"ephemeral-storage": "94850516Ki",
"hugepages-2Mi": "0",
"memory": "10432976Ki",
"pods": "110"
},
"conditions": [
{
"lastHeartbeatTime": "2022-05-16T14:14:31Z",
"lastTransitionTime": "2022-05-16T10:52:29Z",
"message": "kubelet has sufficient memory available",
"reason": "KubeletHasSufficientMemory",
"status": "False",
"type": "MemoryPressure"
},
{
"lastHeartbeatTime": "2022-05-16T14:14:31Z",
"lastTransitionTime": "2022-05-16T10:52:29Z",
"message": "kubelet has no disk pressure",
"reason": "KubeletHasNoDiskPressure",
"status": "False",
"type": "DiskPressure"
},
{
"lastHeartbeatTime": "2022-05-16T14:14:31Z",
"lastTransitionTime": "2022-05-16T10:52:29Z",
"message": "kubelet has sufficient PID available",
"reason": "KubeletHasSufficientPID",
"status": "False",
"type": "PIDPressure"
},
{
"lastHeartbeatTime": "2022-05-16T14:14:31Z",
"lastTransitionTime": "2022-05-16T10:52:45Z",
"message": "kubelet is posting ready status",
"reason": "KubeletReady",
"status": "True",
"type": "Ready"
}
],
"daemonEndpoints": {
"kubeletEndpoint": {
"Port": 10250
}
},
"images": [
{
"names": [
"requarks/wiki@sha256:dd83fff15e77843ff934b25c28c865ac000edf7653e5d11adad1dd51df87439d"
],
"sizeBytes": 441083858
},
{
"names": [
"mariadb@sha256:821d0411208eaa88f9e1f0daccd1d534f88d19baf724eb9a2777cbedb10b6c66"
],
"sizeBytes": 400782682
},
{
"names": [
"k8s.gcr.io/etcd@sha256:64b9ea357325d5db9f8a723dcf503b5a449177b17ac87d69481e126bb724c263",
"k8s.gcr.io/etcd:3.5.1-0"
],
"sizeBytes": 292558922
},
{
"names": [
"kubernetesui/dashboard@sha256:ec27f462cf1946220f5a9ace416a84a57c18f98c777876a8054405d1428cc92e",
"kubernetesui/dashboard:v2.3.1"
],
"sizeBytes": 220033604
},
{
"names": [
"k8s.gcr.io/kube-apiserver@sha256:f54681a71cce62cbc1b13ebb3dbf1d880f849112789811f98b6aebd2caa2f255",
"k8s.gcr.io/kube-apiserver:v1.23.1"
],
"sizeBytes": 135162256
},
{
"names": [
"k8s.gcr.io/kube-controller-manager@sha256:a7ed87380108a2d811f0d392a3fe87546c85bc366e0d1e024dfa74eb14468604",
"k8s.gcr.io/kube-controller-manager:v1.23.1"
],
"sizeBytes": 124971684
},
{
"names": [
"k8s.gcr.io/kube-proxy@sha256:e40f3a28721588affcf187f3f246d1e078157dabe274003eaa2957a83f7170c8",
"k8s.gcr.io/kube-proxy:v1.23.1"
],
"sizeBytes": 112327826
},
{
"names": [
"quay.io/kubescape/kubescape@sha256:6196f766be50d94b45d903a911f5ee95ac99bc392a1324c3e063bec41efd98ba",
"quay.io/kubescape/kubescape:v2.0.153"
],
"sizeBytes": 110345054
},
{
"names": [
"nginx@sha256:f7988fb6c02e0ce69257d9bd9cf37ae20a60f1df7563c3a2a6abe24160306b8d"
],
"sizeBytes": 109129446
},
{
"names": [
"quay.io/armosec/action-trigger@sha256:b93707d10ff86aac8dfa42ad37192d6bcf9aceeb4321b21756e438389c26e07c",
"quay.io/armosec/action-trigger:v0.0.5"
],
"sizeBytes": 65127067
},
{
"names": [
"quay.io/armosec/images-vulnerabilities-scan@sha256:a5f9ddc04a7fdce6d52ef85a21f0de567d8e04d418c2bc5bf5d72b151c997625",
"quay.io/armosec/images-vulnerabilities-scan:v0.0.7"
],
"sizeBytes": 61446712
},
{
"names": [
"quay.io/armosec/images-vulnerabilities-scan@sha256:2f879858da89f6542e3223fb18d6d793810cc2ad6e398b66776475e4218b6af5",
"quay.io/armosec/images-vulnerabilities-scan:v0.0.8"
],
"sizeBytes": 61446528
},
{
"names": [
"quay.io/armosec/cluster-collector@sha256:2c4f733d09f7f4090ace04585230bdfacbbc29a3ade38a2e1233d2c0f730d9b6",
"quay.io/armosec/cluster-collector:v0.0.9"
],
"sizeBytes": 53699576
},
{
"names": [
"k8s.gcr.io/kube-scheduler@sha256:8be4eb1593cf9ff2d91b44596633b7815a3753696031a1eb4273d1b39427fa8c",
"k8s.gcr.io/kube-scheduler:v1.23.1"
],
"sizeBytes": 53488305
},
{
"names": [
"k8s.gcr.io/coredns/coredns@sha256:5b6ec0d6de9baaf3e92d0f66cd96a25b9edbce8716f5f15dcd1a616b3abd590e",
"k8s.gcr.io/coredns/coredns:v1.8.6"
],
"sizeBytes": 46829283
},
{
"names": [
"kubernetesui/metrics-scraper@sha256:36d5b3f60e1a144cc5ada820910535074bdf5cf73fb70d1ff1681537eef4e172",
"kubernetesui/metrics-scraper:v1.0.7"
],
"sizeBytes": 34446077
},
{
"names": [
"gcr.io/k8s-minikube/storage-provisioner@sha256:18eb69d1418e854ad5a19e399310e52808a8321e4c441c1dddad8977a0d7a944",
"gcr.io/k8s-minikube/storage-provisioner:v5"
],
"sizeBytes": 31465472
},
{
"names": [
"quay.io/armosec/notification-server@sha256:b6e9b296cd53bd3b2b42c516d8ab43db998acff1124a57aff8d66b3dd7881979",
"quay.io/armosec/notification-server:v0.0.3"
],
"sizeBytes": 20209940
},
{
"names": [
"quay.io/kubescape/host-scanner@sha256:82139d2561039726be060df2878ef023c59df7c536fbd7f6d766af5a99569fee",
"quay.io/kubescape/host-scanner:latest"
],
"sizeBytes": 11796788
},
{
"names": [
"k8s.gcr.io/pause@sha256:3d380ca8864549e74af4b29c10f9cb0956236dfb01c40ca076fb6c37253234db",
"k8s.gcr.io/pause:3.6"
],
"sizeBytes": 682696
}
],
"nodeInfo": {
"architecture": "amd64",
"bootID": "828cbe73-120b-43cf-aae0-9e2d15b8c873",
"containerRuntimeVersion": "docker://20.10.12",
"kernelVersion": "5.13.0-40-generic",
"kubeProxyVersion": "v1.23.1",
"kubeletVersion": "v1.23.1",
"machineID": "8de776e053e140d6a14c2d2def3d6bb8",
"operatingSystem": "linux",
"osImage": "Ubuntu 20.04.2 LTS",
"systemUUID": "da12dc19-10bf-4033-a440-2d9aa33d6fe3"
}
{
"lastHeartbeatTime": "2022-05-16T14:14:31Z",
"lastTransitionTime": "2022-05-16T10:52:29Z",
"message": "kubelet has no disk pressure",
"reason": "KubeletHasNoDiskPressure",
"status": "False",
"type": "DiskPressure"
},
{
"lastHeartbeatTime": "2022-05-16T14:14:31Z",
"lastTransitionTime": "2022-05-16T10:52:29Z",
"message": "kubelet has sufficient PID available",
"reason": "KubeletHasSufficientPID",
"status": "False",
"type": "PIDPressure"
},
{
"lastHeartbeatTime": "2022-05-16T14:14:31Z",
"lastTransitionTime": "2022-05-16T10:52:45Z",
"message": "kubelet is posting ready status",
"reason": "KubeletReady",
"status": "True",
"type": "Ready"
}
],
"daemonEndpoints": {
"kubeletEndpoint": {
"Port": 10250
}
`
var noTaintNode v1.Node
_ = json.Unmarshal([]byte(noTaintNodeJson), &noTaintNode)
assert.False(t, isMasterNodeTaints(noTaintNode.Spec.Taints))
},
"images": [
{
"names": [
"requarks/wiki@sha256:dd83fff15e77843ff934b25c28c865ac000edf7653e5d11adad1dd51df87439d"
],
"sizeBytes": 441083858
},
{
"names": [
"mariadb@sha256:821d0411208eaa88f9e1f0daccd1d534f88d19baf724eb9a2777cbedb10b6c66"
],
"sizeBytes": 400782682
},
{
"names": [
"k8s.gcr.io/etcd@sha256:64b9ea357325d5db9f8a723dcf503b5a449177b17ac87d69481e126bb724c263",
"k8s.gcr.io/etcd:3.5.1-0"
],
"sizeBytes": 292558922
},
{
"names": [
"kubernetesui/dashboard@sha256:ec27f462cf1946220f5a9ace416a84a57c18f98c777876a8054405d1428cc92e",
"kubernetesui/dashboard:v2.3.1"
],
"sizeBytes": 220033604
},
{
"names": [
"k8s.gcr.io/kube-apiserver@sha256:f54681a71cce62cbc1b13ebb3dbf1d880f849112789811f98b6aebd2caa2f255",
"k8s.gcr.io/kube-apiserver:v1.23.1"
],
"sizeBytes": 135162256
},
{
"names": [
"k8s.gcr.io/kube-controller-manager@sha256:a7ed87380108a2d811f0d392a3fe87546c85bc366e0d1e024dfa74eb14468604",
"k8s.gcr.io/kube-controller-manager:v1.23.1"
],
"sizeBytes": 124971684
},
{
"names": [
"k8s.gcr.io/kube-proxy@sha256:e40f3a28721588affcf187f3f246d1e078157dabe274003eaa2957a83f7170c8",
"k8s.gcr.io/kube-proxy:v1.23.1"
],
"sizeBytes": 112327826
},
{
"names": [
"quay.io/kubescape/kubescape@sha256:6196f766be50d94b45d903a911f5ee95ac99bc392a1324c3e063bec41efd98ba",
"quay.io/kubescape/kubescape:v2.0.153"
],
"sizeBytes": 110345054
},
{
"names": [
"nginx@sha256:f7988fb6c02e0ce69257d9bd9cf37ae20a60f1df7563c3a2a6abe24160306b8d"
],
"sizeBytes": 109129446
},
{
"names": [
"quay.io/armosec/action-trigger@sha256:b93707d10ff86aac8dfa42ad37192d6bcf9aceeb4321b21756e438389c26e07c",
"quay.io/armosec/action-trigger:v0.0.5"
],
"sizeBytes": 65127067
},
{
"names": [
"quay.io/armosec/images-vulnerabilities-scan@sha256:a5f9ddc04a7fdce6d52ef85a21f0de567d8e04d418c2bc5bf5d72b151c997625",
"quay.io/armosec/images-vulnerabilities-scan:v0.0.7"
],
"sizeBytes": 61446712
},
{
"names": [
"quay.io/armosec/images-vulnerabilities-scan@sha256:2f879858da89f6542e3223fb18d6d793810cc2ad6e398b66776475e4218b6af5",
"quay.io/armosec/images-vulnerabilities-scan:v0.0.8"
],
"sizeBytes": 61446528
},
{
"names": [
"quay.io/armosec/cluster-collector@sha256:2c4f733d09f7f4090ace04585230bdfacbbc29a3ade38a2e1233d2c0f730d9b6",
"quay.io/armosec/cluster-collector:v0.0.9"
],
"sizeBytes": 53699576
},
{
"names": [
"k8s.gcr.io/kube-scheduler@sha256:8be4eb1593cf9ff2d91b44596633b7815a3753696031a1eb4273d1b39427fa8c",
"k8s.gcr.io/kube-scheduler:v1.23.1"
],
"sizeBytes": 53488305
},
{
"names": [
"k8s.gcr.io/coredns/coredns@sha256:5b6ec0d6de9baaf3e92d0f66cd96a25b9edbce8716f5f15dcd1a616b3abd590e",
"k8s.gcr.io/coredns/coredns:v1.8.6"
],
"sizeBytes": 46829283
},
{
"names": [
"kubernetesui/metrics-scraper@sha256:36d5b3f60e1a144cc5ada820910535074bdf5cf73fb70d1ff1681537eef4e172",
"kubernetesui/metrics-scraper:v1.0.7"
],
"sizeBytes": 34446077
},
{
"names": [
"gcr.io/k8s-minikube/storage-provisioner@sha256:18eb69d1418e854ad5a19e399310e52808a8321e4c441c1dddad8977a0d7a944",
"gcr.io/k8s-minikube/storage-provisioner:v5"
],
"sizeBytes": 31465472
},
{
"names": [
"quay.io/armosec/notification-server@sha256:b6e9b296cd53bd3b2b42c516d8ab43db998acff1124a57aff8d66b3dd7881979",
"quay.io/armosec/notification-server:v0.0.3"
],
"sizeBytes": 20209940
},
{
"names": [
"quay.io/kubescape/host-scanner@sha256:82139d2561039726be060df2878ef023c59df7c536fbd7f6d766af5a99569fee",
"quay.io/kubescape/host-scanner:latest"
],
"sizeBytes": 11796788
},
{
"names": [
"k8s.gcr.io/pause@sha256:3d380ca8864549e74af4b29c10f9cb0956236dfb01c40ca076fb6c37253234db",
"k8s.gcr.io/pause:3.6"
],
"sizeBytes": 682696
}
],
"nodeInfo": {
"architecture": "amd64",
"bootID": "828cbe73-120b-43cf-aae0-9e2d15b8c873",
"containerRuntimeVersion": "docker://20.10.12",
"kernelVersion": "5.13.0-40-generic",
"kubeProxyVersion": "v1.23.1",
"kubeletVersion": "v1.23.1",
"machineID": "8de776e053e140d6a14c2d2def3d6bb8",
"operatingSystem": "linux",
"osImage": "Ubuntu 20.04.2 LTS",
"systemUUID": "da12dc19-10bf-4033-a440-2d9aa33d6fe3"
}
}
}
`
var l v1.Node
_ = json.Unmarshal([]byte(noTaintNode), &l)
assert.False(t, isMasterNodeTaints(l.Spec.Taints))
taintNodeJson :=
taintNode :=
`
{
"apiVersion": "v1",
@@ -532,60 +532,8 @@ func TestIsMasterNodeTaints(t *testing.T) {
}
}
`
var taintNode v1.Node
_ = json.Unmarshal([]byte(taintNodeJson), &taintNode)
assert.True(t, isMasterNodeTaints(taintNode.Spec.Taints))
taintNodeJson1 :=
`
{
"apiVersion": "v1",
"kind": "Node",
"metadata": {
"annotations": {
"kubeadm.alpha.kubernetes.io/cri-socket": "/var/run/dockershim.sock",
"node.alpha.kubernetes.io/ttl": "0",
"volumes.kubernetes.io/controller-managed-attach-detach": "true"
},
"creationTimestamp": "2022-05-16T10:52:32Z",
"labels": {
"beta.kubernetes.io/arch": "amd64",
"beta.kubernetes.io/os": "linux",
"kubernetes.io/arch": "amd64",
"kubernetes.io/hostname": "danielg-minikube",
"kubernetes.io/os": "linux",
"minikube.k8s.io/commit": "3e64b11ed75e56e4898ea85f96b2e4af0301f43d",
"minikube.k8s.io/name": "danielg-minikube",
"minikube.k8s.io/updated_at": "2022_05_16T13_52_35_0700",
"minikube.k8s.io/version": "v1.25.1",
"node-role.kubernetes.io/control-plane": "",
"node-role.kubernetes.io/master": "",
"node.kubernetes.io/exclude-from-external-load-balancers": ""
},
"name": "danielg-minikube",
"resourceVersion": "9871",
"uid": "fc4afcb6-4ca4-4038-ba54-5e16065a614a"
},
"spec": {
"podCIDR": "10.244.0.0/24",
"podCIDRs": [
"10.244.0.0/24"
],
"taints": [
{
"effect": "NoSchedule",
"key": "node-role.kubernetes.io/control-plane",
"value": "true"
}
]
},
"status": {}
}
`
var taintNode1 v1.Node
_ = json.Unmarshal([]byte(taintNodeJson1), &taintNode1)
assert.True(t, isMasterNodeTaints(taintNode1.Spec.Taints))
_ = json.Unmarshal([]byte(taintNode), &l)
assert.True(t, isMasterNodeTaints(l.Spec.Taints))
}
func TestSetMapNamespaceToNumOfResources(t *testing.T) {

View File

@@ -9,7 +9,7 @@ const (
emptySpace = " "
middleItem = "├── "
continueItem = "│ "
lastItem = "── "
lastItem = "── "
)
type (
@@ -66,7 +66,7 @@ func (t *tree) Items() []Tree {
return t.items
}
// Print returns a visual representation of the tree
// Print returns an visual representation of the tree
func (t *tree) Print() string {
return newPrinter().Print(t)
}

View File

@@ -31,7 +31,7 @@ func TestTreePrint(t *testing.T) {
tree: SimpleTreeMock(),
want: "root\n" +
"├── child1\n" +
"── child2\n",
"── child2\n",
},
{
name: "SimpleTreeWithLinesMock",
@@ -42,36 +42,36 @@ func TestTreePrint(t *testing.T) {
"├── child3\n" +
"│ Line2\n" +
"│ Line3\n" +
"── child4\n",
"── child4\n",
},
{
name: "SubTreeMock1",
tree: SubTreeMock1(),
want: "root\n" +
"── child1\n" +
" ── child1.1\n",
"── child1\n" +
" ── child1.1\n",
},
{
name: "SubTreeMock2",
tree: SubTreeMock2(),
want: "root\n" +
"├── child1\n" +
"│ ── child1.1\n" +
"│ ── child1.1\n" +
"├── child2\n" +
"── child3\n" +
" ── child3.1\n",
"── child3\n" +
" ── child3.1\n",
},
{
name: "SubTreeWithLinesMock",
tree: SubTreeWithLinesMock(),
want: "root\n" +
"├── child1\n" +
"│ ── child1.1\n" +
"│ ── child1.1\n" +
"│ Line2\n" +
"│ Line3\n" +
"├── child2\n" +
"── child3\n" +
" ── child3.1\n" +
"── child3\n" +
" ── child3.1\n" +
" Line2\n" +
" Line3\n",
},
@@ -85,8 +85,8 @@ func TestTreePrint(t *testing.T) {
}
func TestPrintText_LastTree(t *testing.T) {
inputText := "Root\n├── Child1\n── Child2"
expectedOutput := "── Root\n ├── Child1\n ── Child2\n"
inputText := "Root\n├── Child1\n── Child2"
expectedOutput := "── Root\n ├── Child1\n ── Child2\n"
result := p.printText(inputText, []bool{}, true)
@@ -94,8 +94,8 @@ func TestPrintText_LastTree(t *testing.T) {
}
func TestPrintText_NotLastTree(t *testing.T) {
inputText := "Root\n├── Child1\n── Child2"
expectedOutput := "├── Root\n│ ├── Child1\n│ ── Child2\n"
inputText := "Root\n├── Child1\n── Child2"
expectedOutput := "├── Root\n│ ├── Child1\n│ ── Child2\n"
result := p.printText(inputText, []bool{}, false)
@@ -122,7 +122,7 @@ func Test_printer_printItems(t *testing.T) {
name: "SimpleTreeMock",
tree: SimpleTreeMock(),
want: "├── child1\n" +
"── child2\n",
"── child2\n",
},
{
name: "SimpleTreeWithLinesMock",
@@ -132,33 +132,33 @@ func Test_printer_printItems(t *testing.T) {
"├── child3\n" +
"│ Line2\n" +
"│ Line3\n" +
"── child4\n",
"── child4\n",
},
{
name: "SubTreeMock1",
tree: SubTreeMock1(),
want: "── child1\n" +
" ── child1.1\n",
want: "── child1\n" +
" ── child1.1\n",
},
{
name: "SubTreeMock2",
tree: SubTreeMock2(),
want: "├── child1\n" +
"│ ── child1.1\n" +
"│ ── child1.1\n" +
"├── child2\n" +
"── child3\n" +
" ── child3.1\n",
"── child3\n" +
" ── child3.1\n",
},
{
name: "SubTreeWithLinesMock",
tree: SubTreeWithLinesMock(),
want: "├── child1\n" +
"│ ── child1.1\n" +
"│ ── child1.1\n" +
"│ Line2\n" +
"│ Line3\n" +
"├── child2\n" +
"── child3\n" +
" ── child3.1\n" +
"── child3\n" +
" ── child3.1\n" +
" Line2\n" +
" Line3\n",
},

View File

@@ -95,7 +95,7 @@ func (prettyPrinter *PrettyPrinter) printAttackTracks(opaSessionObj *cautils.OPA
})
for i := 0; i < topResourceCount && i < len(resources); i++ {
fmt.Fprintf(prettyPrinter.writer, "\n%s\n", getSeparator("^"))
fmt.Fprintf(prettyPrinter.writer, "\n"+getSeparator("^")+"\n")
resource := resources[i]
resourceObj := opaSessionObj.AllResources[resource.ResourceID]

View File

@@ -6,11 +6,10 @@ import (
"fmt"
"os"
"path/filepath"
"slices"
"strings"
"github.com/anchore/clio"
grypejson "github.com/anchore/grype/grype/presenter/json"
"github.com/anchore/grype/grype/presenter"
"github.com/anchore/grype/grype/presenter/models"
"github.com/kubescape/go-logger"
"github.com/kubescape/go-logger/helpers"
@@ -18,6 +17,7 @@ import (
"github.com/kubescape/kubescape/v3/core/pkg/resultshandling/printer"
"github.com/kubescape/kubescape/v3/core/pkg/resultshandling/printer/v2/prettyprinter/tableprinter/imageprinter"
"github.com/kubescape/opa-utils/reporthandling/results/v1/reportsummary"
"k8s.io/utils/strings/slices"
)
const (
@@ -70,10 +70,17 @@ func (jp *JsonPrinter) convertToImageScanSummary(imageScanData []cautils.ImageSc
imageScanSummary.Images = append(imageScanSummary.Images, imageScanData[i].Image)
}
CVEs := extractCVEs(imageScanData[i].Matches)
presenterConfig := imageScanData[i].PresenterConfig
doc, err := models.NewDocument(clio.Identification{}, 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", imageScanData[i].Image), helpers.Error(err))
continue
}
CVEs := extractCVEs(doc.Matches)
imageScanSummary.CVEs = append(imageScanSummary.CVEs, CVEs...)
setPkgNameToScoreMap(imageScanData[i].Matches, imageScanSummary.PackageScores)
setPkgNameToScoreMap(doc.Matches, imageScanSummary.PackageScores)
setSeverityToSummaryMap(CVEs, imageScanSummary.MapsSeverityToSummary)
}
@@ -85,15 +92,9 @@ func (jp *JsonPrinter) ActionPrint(ctx context.Context, opaSessionObj *cautils.O
var err error
if opaSessionObj != nil {
err = printConfigurationsScanning(opaSessionObj, imageScanData, jp)
err = printConfigurationsScanning(opaSessionObj, ctx, imageScanData, jp)
} else if imageScanData != nil {
model, err2 := models.NewDocument(clio.Identification{}, imageScanData[0].Packages, imageScanData[0].Context,
*imageScanData[0].RemainingMatches, imageScanData[0].IgnoredMatches, imageScanData[0].VulnerabilityProvider, nil, nil, models.DefaultSortStrategy, false)
if err2 != nil {
logger.L().Ctx(ctx).Error("failed to create document: %w", helpers.Error(err))
return
}
err = grypejson.NewPresenter(models.PresenterConfig{Document: model, SBOM: imageScanData[0].SBOM}).Present(jp.writer)
err = jp.PrintImageScan(ctx, imageScanData[0].PresenterConfig)
} else {
err = fmt.Errorf("no data provided")
}
@@ -106,7 +107,7 @@ func (jp *JsonPrinter) ActionPrint(ctx context.Context, opaSessionObj *cautils.O
printer.LogOutputFile(jp.writer.Name())
}
func printConfigurationsScanning(opaSessionObj *cautils.OPASessionObj, imageScanData []cautils.ImageScanData, jp *JsonPrinter) error {
func printConfigurationsScanning(opaSessionObj *cautils.OPASessionObj, ctx context.Context, imageScanData []cautils.ImageScanData, jp *JsonPrinter) error {
if imageScanData != nil {
imageScanSummary, err := jp.convertToImageScanSummary(imageScanData)
@@ -167,6 +168,14 @@ func convertToReportSummary(input map[string]*imageprinter.SeveritySummary) map[
return output
}
func (jp *JsonPrinter) PrintImageScan(ctx context.Context, scanResults *models.PresenterConfig) error {
if scanResults == nil {
return fmt.Errorf("no image vulnerability data provided")
}
pres := presenter.GetPresenter("json", "", false, *scanResults)
return pres.Present(jp.writer)
}
func (jp *JsonPrinter) PrintNextSteps() {
}

View File

@@ -7,9 +7,9 @@ import (
"sort"
"strings"
"github.com/anchore/clio"
"github.com/anchore/grype/grype/presenter/models"
"github.com/enescakir/emoji"
"github.com/jedib0t/go-pretty/v6/table"
"github.com/jedib0t/go-pretty/v6/text"
"github.com/jwalton/gchalk"
"github.com/kubescape/go-logger"
"github.com/kubescape/go-logger/helpers"
@@ -21,6 +21,7 @@ import (
"github.com/kubescape/opa-utils/objectsenvelopes"
"github.com/kubescape/opa-utils/reporthandling/apis"
"github.com/kubescape/opa-utils/reporthandling/results/v1/reportsummary"
"github.com/olekukonko/tablewriter"
"k8s.io/utils/strings/slices"
)
@@ -89,10 +90,17 @@ func (pp *PrettyPrinter) convertToImageScanSummary(imageScanData []cautils.Image
imageScanSummary.Images = append(imageScanSummary.Images, imageScanData[i].Image)
}
CVEs := extractCVEs(imageScanData[i].Matches)
presenterConfig := imageScanData[i].PresenterConfig
doc, err := models.NewDocument(clio.Identification{}, 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", imageScanData[i].Image), helpers.Error(err))
continue
}
CVEs := extractCVEs(doc.Matches)
imageScanSummary.CVEs = append(imageScanSummary.CVEs, CVEs...)
setPkgNameToScoreMap(imageScanData[i].Matches, imageScanSummary.PackageScores)
setPkgNameToScoreMap(doc.Matches, imageScanSummary.PackageScores)
setSeverityToSummaryMap(CVEs, imageScanSummary.MapsSeverityToSummary)
}
@@ -113,8 +121,9 @@ func (pp *PrettyPrinter) ActionPrint(_ context.Context, opaSessionObj *cautils.O
if opaSessionObj != nil {
// TODO line is currently printed on framework scan only
if isPrintSeparatorType(pp.scanType) {
fmt.Fprintf(pp.writer, "\n%s\n\n",
gchalk.WithAnsi256(238).Bold(strings.Repeat("─", 50)))
fmt.Fprintf(pp.writer, "\n"+
gchalk.WithAnsi256(238).Bold(fmt.Sprintf("%s\n", strings.Repeat("─", 50)))+
"\n")
} else {
fmt.Fprintf(pp.writer, "\n")
}
@@ -165,20 +174,20 @@ func (pp *PrettyPrinter) printHeader(opaSessionObj *cautils.OPASessionObj) {
} else if pp.scanType == cautils.ScanTypeWorkload {
cautils.InfoDisplay(pp.writer, "Workload security posture overview for:\n")
ns := opaSessionObj.SingleResourceScan.GetNamespace()
var rows []table.Row
rows := [][]string{}
if ns != "" {
rows = append(rows, table.Row{"Namespace", gchalk.WithBrightWhite().Bold(opaSessionObj.SingleResourceScan.GetNamespace())})
rows = append(rows, []string{"Namespace", gchalk.WithBrightWhite().Bold(opaSessionObj.SingleResourceScan.GetNamespace())})
}
rows = append(rows, table.Row{"Kind", gchalk.WithBrightWhite().Bold(opaSessionObj.SingleResourceScan.GetKind())})
rows = append(rows, table.Row{"Name", gchalk.WithBrightWhite().Bold(opaSessionObj.SingleResourceScan.GetName())})
rows = append(rows, []string{"Kind", gchalk.WithBrightWhite().Bold(opaSessionObj.SingleResourceScan.GetKind())})
rows = append(rows, []string{"Name", gchalk.WithBrightWhite().Bold(opaSessionObj.SingleResourceScan.GetName())})
tableWriter := table.NewWriter()
tableWriter.SetOutputMirror(pp.writer)
table := tablewriter.NewWriter(pp.writer)
tableWriter.SetColumnConfigs([]table.ColumnConfig{{Number: 1, Align: text.AlignRight}, {Number: 2, Align: text.AlignLeft}})
tableWriter.AppendRows(rows)
table.SetColumnAlignment([]int{tablewriter.ALIGN_RIGHT, tablewriter.ALIGN_LEFT})
table.SetUnicodeHVC(tablewriter.Regular, tablewriter.Regular, gchalk.Ansi256(238))
table.AppendBulk(rows)
tableWriter.Render()
table.Render()
cautils.SimpleDisplay(pp.writer, "\nIn this overview, Kubescape shows you a summary of the security posture of a workload, including key controls that apply to its configuration, and the vulnerability status of the container image.\n\n\n")
}
@@ -200,7 +209,7 @@ func (pp *PrettyPrinter) SetWriter(ctx context.Context, outputFile string) {
pp.SetMainPrinter()
}
func (pp *PrettyPrinter) Score(_ float32) {
func (pp *PrettyPrinter) Score(score float32) {
}
func (pp *PrettyPrinter) printResults(controls *reportsummary.ControlSummaries, allResources map[string]workloadinterface.IMetadata, sortedControlIDs [][]string) {
@@ -209,12 +218,12 @@ func (pp *PrettyPrinter) printResults(controls *reportsummary.ControlSummaries,
controlSummary := controls.GetControl(reportsummary.EControlCriteriaID, c) // summaryDetails.Controls ListControls().All() Controls.GetControl(ca)
pp.printTitle(controlSummary)
pp.printResources(controlSummary, allResources)
pp.printSummary(controlSummary)
pp.printSummary(c, controlSummary)
}
}
}
func (prettyPrinter *PrettyPrinter) printSummary(controlSummary reportsummary.IControlSummary) {
func (prettyPrinter *PrettyPrinter) printSummary(controlName string, controlSummary reportsummary.IControlSummary) {
cautils.SimpleDisplay(prettyPrinter.writer, "Summary - ")
cautils.SuccessDisplay(prettyPrinter.writer, "Passed:%v ", controlSummary.NumberOfResources().Passed())
cautils.WarningDisplay(prettyPrinter.writer, "Action Required:%v ", controlSummary.NumberOfResources().Skipped())

View File

@@ -3,15 +3,15 @@ package configurationprinter
import (
"io"
"github.com/jedib0t/go-pretty/v6/table"
"github.com/jedib0t/go-pretty/v6/text"
"github.com/jwalton/gchalk"
"github.com/kubescape/kubescape/v3/core/cautils"
"github.com/kubescape/kubescape/v3/core/pkg/resultshandling/printer/v2/prettyprinter/tableprinter/utils"
"github.com/kubescape/opa-utils/reporthandling/results/v1/reportsummary"
"github.com/olekukonko/tablewriter"
)
const (
docsPrefix = "https://kubescape.io/docs/controls"
docsPrefix = "https://hub.armosec.io/docs"
scanControlPrefix = "$ kubescape scan control"
controlNameHeader = "Control name"
statusHeader = ""
@@ -21,15 +21,15 @@ const (
)
// initializes the table headers and column alignments based on the category type
func initCategoryTableData(categoryType CategoryType) (table.Row, []table.ColumnConfig) {
func initCategoryTableData(categoryType CategoryType) ([]string, []int) {
if categoryType == TypeCounting {
return getCategoryCountingTypeHeaders(), getCountingTypeAlignments()
}
return getCategoryStatusTypeHeaders(), getStatusTypeAlignments()
}
func getCategoryStatusTypeHeaders() table.Row {
headers := make(table.Row, 3)
func getCategoryStatusTypeHeaders() []string {
headers := make([]string, 3)
headers[0] = statusHeader
headers[1] = controlNameHeader
headers[2] = docsHeader
@@ -37,8 +37,8 @@ func getCategoryStatusTypeHeaders() table.Row {
return headers
}
func getCategoryCountingTypeHeaders() table.Row {
headers := make(table.Row, 3)
func getCategoryCountingTypeHeaders() []string {
headers := make([]string, 3)
headers[0] = controlNameHeader
headers[1] = resourcesHeader
headers[2] = runHeader
@@ -46,16 +46,16 @@ func getCategoryCountingTypeHeaders() table.Row {
return headers
}
func getStatusTypeAlignments() []table.ColumnConfig {
return []table.ColumnConfig{{Number: 1, Align: text.AlignCenter}, {Number: 2, Align: text.AlignLeft}, {Number: 3, Align: text.AlignCenter}}
func getStatusTypeAlignments() []int {
return []int{tablewriter.ALIGN_CENTER, tablewriter.ALIGN_LEFT, tablewriter.ALIGN_CENTER}
}
func getCountingTypeAlignments() []table.ColumnConfig {
return []table.ColumnConfig{{Number: 1, Align: text.AlignLeft}, {Number: 2, Align: text.AlignCenter}, {Number: 3, Align: text.AlignLeft}}
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) table.Row {
func generateCategoryStatusRow(controlSummary reportsummary.IControlSummary, infoToPrintInfo []utils.InfoStars) []string {
// show only passed, failed and action required controls
status := controlSummary.GetStatus()
@@ -63,7 +63,7 @@ func generateCategoryStatusRow(controlSummary reportsummary.IControlSummary) tab
return nil
}
rows := make(table.Row, 3)
rows := make([]string, 3)
rows[0] = utils.GetStatusIcon(controlSummary.GetStatus().Status())
@@ -80,26 +80,31 @@ func generateCategoryStatusRow(controlSummary reportsummary.IControlSummary) tab
}
func getCategoryTableWriter(writer io.Writer, headers table.Row, columnAlignments []table.ColumnConfig) table.Writer {
tableWriter := table.NewWriter()
tableWriter.SetOutputMirror(writer)
tableWriter.AppendHeader(headers)
tableWriter.Style().Options.SeparateHeader = true
tableWriter.Style().Format.HeaderAlign = text.AlignLeft
tableWriter.Style().Format.Header = text.FormatDefault
tableWriter.SetColumnConfigs(columnAlignments)
tableWriter.Style().Box = table.StyleBoxRounded
return tableWriter
func getCategoryTableWriter(writer io.Writer, headers []string, columnAligments []int) *tablewriter.Table {
table := tablewriter.NewWriter(writer)
table.SetHeader(headers)
table.SetHeaderLine(true)
table.SetHeaderAlignment(tablewriter.ALIGN_LEFT)
table.SetAutoFormatHeaders(false)
table.SetColumnAlignment(columnAligments)
table.SetAutoWrapText(false)
table.SetUnicodeHVC(tablewriter.Regular, tablewriter.Regular, gchalk.Ansi256(238))
var headerColors []tablewriter.Colors
for range headers {
headerColors = append(headerColors, tablewriter.Colors{tablewriter.FgHiYellowColor})
}
table.SetHeaderColor(headerColors...)
return table
}
func renderSingleCategory(writer io.Writer, categoryName string, tableWriter table.Writer, rows []table.Row, infoToPrintInfo []utils.InfoStars) {
func renderSingleCategory(writer io.Writer, categoryName string, table *tablewriter.Table, rows [][]string, infoToPrintInfo []utils.InfoStars) {
cautils.InfoDisplay(writer, categoryName+"\n")
tableWriter.ResetRows()
tableWriter.AppendRows(rows)
table.ClearRows()
table.AppendBulk(rows)
tableWriter.Render()
table.Render()
if len(infoToPrintInfo) > 0 {
printCategoryInfo(writer, infoToPrintInfo)

View File

@@ -3,13 +3,13 @@ package configurationprinter
import (
"io"
"os"
"reflect"
"testing"
"github.com/jedib0t/go-pretty/v6/table"
"github.com/jedib0t/go-pretty/v6/text"
"github.com/kubescape/kubescape/v3/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"
)
@@ -17,20 +17,20 @@ func TestInitCategoryTableData(t *testing.T) {
tests := []struct {
name string
categoryType CategoryType
expectedHeaders table.Row
expectedAlignments []table.ColumnConfig
expectedHeaders []string
expectedAlignments []int
}{
{
name: "Test1",
categoryType: TypeCounting,
expectedHeaders: table.Row{"Control name", "Resources", "View details"},
expectedAlignments: []table.ColumnConfig{{Number: 1, Align: text.AlignLeft}, {Number: 2, Align: text.AlignCenter}, {Number: 3, Align: text.AlignLeft}},
expectedHeaders: []string{"Control name", "Resources", "View details"},
expectedAlignments: []int{tablewriter.ALIGN_LEFT, tablewriter.ALIGN_CENTER, tablewriter.ALIGN_LEFT},
},
{
name: "Test2",
categoryType: TypeStatus,
expectedHeaders: table.Row{"", "Control name", "Docs"},
expectedAlignments: []table.ColumnConfig{{Number: 1, Align: text.AlignCenter}, {Number: 2, Align: text.AlignLeft}, {Number: 3, Align: text.AlignCenter}},
expectedHeaders: []string{"", "Control name", "Docs"},
expectedAlignments: []int{tablewriter.ALIGN_CENTER, tablewriter.ALIGN_LEFT, tablewriter.ALIGN_CENTER},
},
}
for _, tt := range tests {
@@ -42,8 +42,8 @@ func TestInitCategoryTableData(t *testing.T) {
if len(alignments) != len(tt.expectedAlignments) {
t.Errorf("initCategoryTableData() alignments = %v, want %v", alignments, tt.expectedAlignments)
}
assert.Equal(t, headers, tt.expectedHeaders)
assert.Equal(t, alignments, tt.expectedAlignments)
assert.True(t, reflect.DeepEqual(headers, tt.expectedHeaders))
assert.True(t, reflect.DeepEqual(alignments, tt.expectedAlignments))
})
}
}
@@ -88,12 +88,52 @@ func TestGetCategoryCountingTypeHeaders(t *testing.T) {
}
}
func TestGetStatusTypeAlignments(t *testing.T) {
alignments := getStatusTypeAlignments()
if len(alignments) != 3 {
t.Errorf("Expected 3 alignments, got %d", len(alignments))
}
if alignments[0] != tablewriter.ALIGN_CENTER {
t.Errorf("Expected %d, got %d", tablewriter.ALIGN_CENTER, alignments[0])
}
if alignments[1] != tablewriter.ALIGN_LEFT {
t.Errorf("Expected %d, got %d", tablewriter.ALIGN_LEFT, alignments[1])
}
if alignments[2] != tablewriter.ALIGN_CENTER {
t.Errorf("Expected %d, got %d", tablewriter.ALIGN_CENTER, alignments[2])
}
}
func TestGetCountingTypeAlignments(t *testing.T) {
alignments := getCountingTypeAlignments()
if len(alignments) != 3 {
t.Errorf("Expected 3 alignments, got %d", len(alignments))
}
if alignments[0] != tablewriter.ALIGN_LEFT {
t.Errorf("Expected %d, got %d", tablewriter.ALIGN_LEFT, alignments[0])
}
if alignments[1] != tablewriter.ALIGN_CENTER {
t.Errorf("Expected %d, got %d", tablewriter.ALIGN_CENTER, alignments[1])
}
if alignments[2] != tablewriter.ALIGN_LEFT {
t.Errorf("Expected %d, got %d", tablewriter.ALIGN_LEFT, alignments[2])
}
}
func TestGenerateCategoryStatusRow(t *testing.T) {
tests := []struct {
name string
controlSummary reportsummary.IControlSummary
infoToPrintInfo []utils.InfoStars
expectedRows table.Row
expectedRows []string
}{
{
name: "failed control",
@@ -102,7 +142,7 @@ func TestGenerateCategoryStatusRow(t *testing.T) {
Status: apis.StatusFailed,
ControlID: "ctrlID",
},
expectedRows: table.Row{"❌", "test", "https://kubescape.io/docs/controls/ctrlid"},
expectedRows: []string{"❌", "test", "https://hub.armosec.io/docs/ctrlid"},
},
{
name: "skipped control",
@@ -114,7 +154,7 @@ func TestGenerateCategoryStatusRow(t *testing.T) {
},
ControlID: "ctrlID",
},
expectedRows: table.Row{"⚠️", "test", "https://kubescape.io/docs/controls/ctrlid"},
expectedRows: []string{"⚠️", "test", "https://hub.armosec.io/docs/ctrlid"},
infoToPrintInfo: []utils.InfoStars{
{
Info: "testInfo",
@@ -129,7 +169,7 @@ func TestGenerateCategoryStatusRow(t *testing.T) {
Status: apis.StatusPassed,
ControlID: "ctrlID",
},
expectedRows: table.Row{"✅", "test", "https://kubescape.io/docs/controls/ctrlid"},
expectedRows: []string{"✅", "test", "https://hub.armosec.io/docs/ctrlid"},
},
{
name: "big name",
@@ -138,36 +178,36 @@ func TestGenerateCategoryStatusRow(t *testing.T) {
Status: apis.StatusFailed,
ControlID: "ctrlID",
},
expectedRows: table.Row{"❌", "testtesttesttesttesttesttesttesttesttesttesttestte...", "https://kubescape.io/docs/controls/ctrlid"},
expectedRows: []string{"❌", "testtesttesttesttesttesttesttesttesttesttesttestte...", "https://hub.armosec.io/docs/ctrlid"},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
row := generateCategoryStatusRow(tt.controlSummary)
assert.Equal(t, tt.expectedRows, row)
row := generateCategoryStatusRow(tt.controlSummary, tt.infoToPrintInfo)
assert.True(t, reflect.DeepEqual(row, tt.expectedRows))
})
}
}
func TestGetCategoryTableWriter(t *testing.T) {
tests := []struct {
name string
headers table.Row
columnAlignments []table.ColumnConfig
want string
name string
headers []string
columnAligments []int
want string
}{
{
name: "Test1",
headers: table.Row{"Control name", "Resources", "View details"},
columnAlignments: []table.ColumnConfig{{Number: 1, Align: text.AlignLeft}, {Number: 2, Align: text.AlignCenter}, {Number: 3, Align: text.AlignLeft}},
want: "──────────────┬───────────┬──────────────\n│ Control name │ Resources │ View details │\n├──────────────┼───────────┼──────────────┤\n──────────────┴───────────┴──────────────\n",
name: "Test1",
headers: []string{"Control name", "Resources", "View details"},
columnAligments: []int{tablewriter.ALIGN_LEFT, tablewriter.ALIGN_CENTER, tablewriter.ALIGN_LEFT},
want: "──────────────┬───────────┬──────────────\n│ Control name │ Resources │ View details │\n├──────────────┼───────────┼──────────────┤\n──────────────┴───────────┴──────────────\n",
},
{
name: "Test2",
headers: table.Row{"", "Control name", "Docs"},
columnAlignments: []table.ColumnConfig{{Number: 1, Align: text.AlignCenter}, {Number: 2, Align: text.AlignLeft}, {Number: 3, Align: text.AlignCenter}},
want: "──┬──────────────┬──────\n│ │ Control name │ Docs │\n├──┼──────────────┼──────┤\n──┴──────────────┴──────\n",
name: "Test2",
headers: []string{"", "Control name", "Docs"},
columnAligments: []int{tablewriter.ALIGN_CENTER, tablewriter.ALIGN_LEFT, tablewriter.ALIGN_CENTER},
want: "──┬──────────────┬──────\n│ │ Control name │ Docs │\n├──┼──────────────┼──────┤\n──┴──────────────┴──────\n",
},
}
for _, tt := range tests {
@@ -179,7 +219,7 @@ func TestGetCategoryTableWriter(t *testing.T) {
}
defer f.Close()
tableWriter := getCategoryTableWriter(f, tt.headers, tt.columnAlignments)
tableWriter := getCategoryTableWriter(f, tt.headers, tt.columnAligments)
// Redirect stderr to the temporary file
oldStderr := os.Stderr
@@ -205,61 +245,61 @@ func TestGetCategoryTableWriter(t *testing.T) {
func TestRenderSingleCategory(t *testing.T) {
tests := []struct {
name string
categoryName string
rows []table.Row
infoToPrintInfo []utils.InfoStars
headers table.Row
columnAlignments []table.ColumnConfig
want string
name string
categoryName string
rows [][]string
infoToPrintInfo []utils.InfoStars
headers []string
columnAligments []int
want string
}{
{
name: "Test1",
categoryName: "Resources",
rows: []table.Row{
rows: [][]string{
{"Regular", "regular line", "1"},
{"Thick", "particularly thick line", "2"},
{"Double", "double line", "3"},
},
infoToPrintInfo: []utils.InfoStars{
{
utils.InfoStars{
Stars: "1",
Info: "Low severity",
},
{
utils.InfoStars{
Stars: "5",
Info: "Critical severity",
},
},
headers: table.Row{"Control name", "Resources", "View details"},
columnAlignments: []table.ColumnConfig{{Number: 1, Align: text.AlignLeft}, {Number: 2, Align: text.AlignCenter}, {Number: 3, Align: text.AlignLeft}},
want: "Resources\n──────────────┬─────────────────────────┬──────────────\n│ Control name │ Resources │ View details │\n├──────────────┼─────────────────────────┼──────────────┤\n│ Regular │ regular line │ 1 │\n│ Thick │ particularly thick line │ 2 │\n│ Double │ double line │ 3 │\n──────────────┴─────────────────────────┴──────────────\n1 Low severity\n5 Critical severity\n\n",
headers: []string{"Control name", "Resources", "View details"},
columnAligments: []int{tablewriter.ALIGN_LEFT, tablewriter.ALIGN_CENTER, tablewriter.ALIGN_LEFT},
want: "Resources\n──────────────┬─────────────────────────┬──────────────\n│ Control name │ Resources │ View details │\n├──────────────┼─────────────────────────┼──────────────┤\n│ Regular │ regular line │ 1 │\n│ Thick │ particularly thick line │ 2 │\n│ Double │ double line │ 3 │\n──────────────┴─────────────────────────┴──────────────\n1 Low severity\n5 Critical severity\n\n",
},
{
name: "Test2",
categoryName: "Control name",
rows: []table.Row{
rows: [][]string{
{"Regular", "regular line", "1"},
{"Thick", "particularly thick line", "2"},
{"Double", "double line", "3"},
},
infoToPrintInfo: []utils.InfoStars{
{
utils.InfoStars{
Stars: "1",
Info: "Low severity",
},
{
utils.InfoStars{
Stars: "5",
Info: "Critical severity",
},
{
utils.InfoStars{
Stars: "4",
Info: "High severity",
},
},
headers: table.Row{"Control name", "Resources", "View details"},
columnAlignments: []table.ColumnConfig{{Number: 1, Align: text.AlignLeft}, {Number: 2, Align: text.AlignCenter}, {Number: 3, Align: text.AlignLeft}},
want: "Control name\n──────────────┬─────────────────────────┬──────────────\n│ Control name │ Resources │ View details │\n├──────────────┼─────────────────────────┼──────────────┤\n│ Regular │ regular line │ 1 │\n│ Thick │ particularly thick line │ 2 │\n│ Double │ double line │ 3 │\n──────────────┴─────────────────────────┴──────────────\n1 Low severity\n5 Critical severity\n4 High severity\n\n",
headers: []string{"Control name", "Resources", "View details"},
columnAligments: []int{tablewriter.ALIGN_LEFT, tablewriter.ALIGN_CENTER, tablewriter.ALIGN_LEFT},
want: "Control name\n──────────────┬─────────────────────────┬──────────────\n│ Control name │ Resources │ View details │\n├──────────────┼─────────────────────────┼──────────────┤\n│ Regular │ regular line │ 1 │\n│ Thick │ particularly thick line │ 2 │\n│ Double │ double line │ 3 │\n──────────────┴─────────────────────────┴──────────────\n1 Low severity\n5 Critical severity\n4 High severity\n\n",
},
}
for _, tt := range tests {
@@ -271,7 +311,7 @@ func TestRenderSingleCategory(t *testing.T) {
}
defer f.Close()
tableWriter := getCategoryTableWriter(f, tt.headers, tt.columnAlignments)
tableWriter := getCategoryTableWriter(f, tt.headers, tt.columnAligments)
// Redirect stderr to the temporary file
oldStderr := os.Stderr

View File

@@ -4,7 +4,6 @@ import (
"fmt"
"io"
"github.com/jedib0t/go-pretty/v6/table"
"github.com/jwalton/gchalk"
"github.com/kubescape/kubescape/v3/core/pkg/resultshandling/printer/v2/prettyprinter/tableprinter/utils"
"github.com/kubescape/opa-utils/reporthandling/results/v1/reportsummary"
@@ -18,11 +17,11 @@ func NewClusterPrinter() *ClusterPrinter {
var _ TablePrinter = &ClusterPrinter{}
func (cp *ClusterPrinter) PrintSummaryTable(_ io.Writer, _ *reportsummary.SummaryDetails, _ [][]string) {
func (cp *ClusterPrinter) PrintSummaryTable(writer io.Writer, summaryDetails *reportsummary.SummaryDetails, sortedControlIDs [][]string) {
}
func (cp *ClusterPrinter) PrintCategoriesTables(writer io.Writer, summaryDetails *reportsummary.SummaryDetails, _ [][]string) {
func (cp *ClusterPrinter) PrintCategoriesTables(writer io.Writer, summaryDetails *reportsummary.SummaryDetails, sortedControlIDs [][]string) {
categoriesToCategoryControls := mapCategoryToSummary(summaryDetails.ListControls(), mapClusterControlsToCategories)
@@ -39,17 +38,17 @@ func (cp *ClusterPrinter) PrintCategoriesTables(writer io.Writer, summaryDetails
func (cp *ClusterPrinter) renderSingleCategoryTable(categoryName string, categoryType CategoryType, writer io.Writer, controlSummaries []reportsummary.IControlSummary, infoToPrintInfo []utils.InfoStars) {
sortControlSummaries(controlSummaries)
headers, columnAlignments := initCategoryTableData(categoryType)
headers, columnAligments := initCategoryTableData(categoryType)
tableWriter := getCategoryTableWriter(writer, headers, columnAlignments)
table := getCategoryTableWriter(writer, headers, columnAligments)
var rows []table.Row
var rows [][]string
for _, ctrls := range controlSummaries {
var row table.Row
var row []string
if categoryType == TypeCounting {
row = cp.generateCountingCategoryRow(ctrls)
} else {
row = generateCategoryStatusRow(ctrls)
row = generateCategoryStatusRow(ctrls, infoToPrintInfo)
}
if len(row) > 0 {
rows = append(rows, row)
@@ -60,19 +59,19 @@ func (cp *ClusterPrinter) renderSingleCategoryTable(categoryName string, categor
return
}
renderSingleCategory(writer, categoryName, tableWriter, rows, infoToPrintInfo)
renderSingleCategory(writer, categoryName, table, rows, infoToPrintInfo)
}
func (cp *ClusterPrinter) generateCountingCategoryRow(controlSummary reportsummary.IControlSummary) table.Row {
func (cp *ClusterPrinter) generateCountingCategoryRow(controlSummary reportsummary.IControlSummary) []string {
row := make(table.Row, 3)
row := make([]string, 3)
row[0] = controlSummary.GetName()
failedResources := controlSummary.NumberOfResources().Failed()
if failedResources > 0 {
row[1] = gchalk.WithYellow().Bold(fmt.Sprintf("%d", failedResources))
row[1] = string(gchalk.WithYellow().Bold(fmt.Sprintf("%d", failedResources)))
} else {
row[1] = fmt.Sprintf("%d", failedResources)
}

View File

@@ -6,11 +6,11 @@ import (
"strconv"
"strings"
"github.com/jedib0t/go-pretty/v6/table"
"github.com/jedib0t/go-pretty/v6/text"
"github.com/jwalton/gchalk"
"github.com/kubescape/kubescape/v3/core/cautils"
"github.com/kubescape/kubescape/v3/core/pkg/resultshandling/printer/v2/prettyprinter/tableprinter/utils"
"github.com/kubescape/opa-utils/reporthandling/results/v1/reportsummary"
"github.com/olekukonko/tablewriter"
)
type FrameworkPrinter struct {
@@ -38,21 +38,19 @@ func (fp *FrameworkPrinter) PrintSummaryTable(writer io.Writer, summaryDetails *
// When scanning controls the framework list will be empty
cautils.SimpleDisplay(writer, utils.FrameworksScoresToString(summaryDetails.ListFrameworks())+"\n")
controlCountersTable := table.NewWriter()
controlCountersTable.SetOutputMirror(writer)
controlCountersTable := tablewriter.NewWriter(writer)
controlCountersTable.SetColumnConfigs([]table.ColumnConfig{{Number: 1, Align: text.AlignRight}, {Number: 2, Align: text.AlignLeft}})
controlCountersTable.Style().Box = table.StyleBoxRounded
controlCountersTable.AppendRows(ControlCountersForSummary(summaryDetails.NumberOfControls()))
controlCountersTable.SetColumnAlignment([]int{tablewriter.ALIGN_RIGHT, tablewriter.ALIGN_LEFT})
controlCountersTable.SetUnicodeHVC(tablewriter.Regular, tablewriter.Regular, gchalk.Ansi256(238))
controlCountersTable.AppendBulk(ControlCountersForSummary(summaryDetails.NumberOfControls()))
controlCountersTable.Render()
cautils.SimpleDisplay(writer, "\nFailed resources by severity:\n\n")
severityCountersTable := table.NewWriter()
severityCountersTable.SetOutputMirror(writer)
severityCountersTable.SetColumnConfigs([]table.ColumnConfig{{Number: 1, Align: text.AlignRight}, {Number: 2, Align: text.AlignLeft}})
severityCountersTable.Style().Box = table.StyleBoxRounded
severityCountersTable.AppendRows(renderSeverityCountersSummary(summaryDetails.GetResourcesSeverityCounters()))
severityCountersTable := tablewriter.NewWriter(writer)
severityCountersTable.SetColumnAlignment([]int{tablewriter.ALIGN_RIGHT, tablewriter.ALIGN_LEFT})
severityCountersTable.SetUnicodeHVC(tablewriter.Regular, tablewriter.Regular, gchalk.Ansi256(238))
severityCountersTable.AppendBulk(renderSeverityCountersSummary(summaryDetails.GetResourcesSeverityCounters()))
severityCountersTable.Render()
cautils.SimpleDisplay(writer, "\n")
@@ -61,15 +59,14 @@ func (fp *FrameworkPrinter) PrintSummaryTable(writer io.Writer, summaryDetails *
cautils.SimpleDisplay(writer, "Run with '--verbose'/'-v' to see control failures for each resource.\n\n")
}
summaryTable := table.NewWriter()
summaryTable.SetOutputMirror(writer)
summaryTable := tablewriter.NewWriter(writer)
summaryTable.Style().Options.SeparateHeader = true
summaryTable.Style().Format.HeaderAlign = text.AlignLeft
summaryTable.Style().Format.Header = text.FormatDefault
summaryTable.Style().Format.Footer = text.FormatDefault
summaryTable.SetColumnConfigs(GetColumnsAlignments())
summaryTable.Style().Box = table.StyleBoxRounded
summaryTable.SetAutoWrapText(false)
summaryTable.SetHeaderLine(true)
summaryTable.SetHeaderAlignment(tablewriter.ALIGN_LEFT)
summaryTable.SetAutoFormatHeaders(false)
summaryTable.SetColumnAlignment(GetColumnsAlignments())
summaryTable.SetUnicodeHVC(tablewriter.Regular, tablewriter.Regular, gchalk.Ansi256(238))
printAll := fp.getVerboseMode()
if summaryDetails.NumberOfResources().Failed() == 0 {
@@ -77,7 +74,7 @@ func (fp *FrameworkPrinter) PrintSummaryTable(writer io.Writer, summaryDetails *
printAll = true
}
var dataRows []table.Row
dataRows := [][]string{}
infoToPrintInfo := utils.MapInfoToPrintInfo(summaryDetails.Controls)
for i := len(sortedControlIDs) - 1; i >= 0; i-- {
@@ -91,23 +88,28 @@ func (fp *FrameworkPrinter) PrintSummaryTable(writer io.Writer, summaryDetails *
short := utils.CheckShortTerminalWidth(dataRows, GetControlTableHeaders(false))
if short {
summaryTable.Style().Options.SeparateRows = true
summaryTable.SetRowLine(true)
dataRows = shortFormatRow(dataRows)
} else {
summaryTable.SetColumnConfigs(GetColumnsAlignments())
summaryTable.Style().Format.FooterAlign = text.AlignCenter
summaryTable.SetColumnAlignment(GetColumnsAlignments())
}
summaryTable.AppendHeader(GetControlTableHeaders(short))
summaryTable.AppendFooter(GenerateFooter(summaryDetails, short))
summaryTable.SetHeader(GetControlTableHeaders(short))
summaryTable.SetFooter(GenerateFooter(summaryDetails, short))
summaryTable.AppendRows(dataRows)
var headerColors []tablewriter.Colors
for range dataRows[0] {
headerColors = append(headerColors, tablewriter.Colors{tablewriter.Bold, tablewriter.FgHiYellowColor})
}
summaryTable.SetHeaderColor(headerColors...)
summaryTable.AppendBulk(dataRows)
summaryTable.Render()
utils.PrintInfo(writer, infoToPrintInfo)
}
func shortFormatRow(dataRows []table.Row) []table.Row {
rows := make([]table.Row, 0, len(dataRows))
func shortFormatRow(dataRows [][]string) [][]string {
rows := [][]string{}
for _, dataRow := range dataRows {
// Define the row content using a formatted string
rowContent := fmt.Sprintf("Severity%s: %+v\nControl Name%s: %+v\nFailed Resources%s: %+v\nAll Resources%s: %+v\n%% Compliance-Score%s: %+v",
@@ -123,22 +125,22 @@ func shortFormatRow(dataRows []table.Row) []table.Row {
dataRow[summaryColumnComplianceScore])
// Append the formatted row content to the rows slice
rows = append(rows, table.Row{rowContent})
rows = append(rows, []string{rowContent})
}
return rows
}
func (fp *FrameworkPrinter) PrintCategoriesTables(_ io.Writer, _ *reportsummary.SummaryDetails, _ [][]string) {
func (fp *FrameworkPrinter) PrintCategoriesTables(writer io.Writer, summaryDetails *reportsummary.SummaryDetails, sortedControlIDs [][]string) {
}
func renderSeverityCountersSummary(counters reportsummary.ISeverityCounters) []table.Row {
func renderSeverityCountersSummary(counters reportsummary.ISeverityCounters) [][]string {
rows := make([]table.Row, 0, 4)
rows = append(rows, table.Row{"Critical", utils.GetColorForVulnerabilitySeverity("Critical")(strconv.Itoa(counters.NumberOfCriticalSeverity()))})
rows = append(rows, table.Row{"High", utils.GetColorForVulnerabilitySeverity("High")(strconv.Itoa(counters.NumberOfHighSeverity()))})
rows = append(rows, table.Row{"Medium", utils.GetColorForVulnerabilitySeverity("Medium")(strconv.Itoa(counters.NumberOfMediumSeverity()))})
rows = append(rows, table.Row{"Low", utils.GetColorForVulnerabilitySeverity("Low")(strconv.Itoa(counters.NumberOfLowSeverity()))})
rows := [][]string{}
rows = append(rows, []string{"Critical", utils.GetColorForVulnerabilitySeverity("Critical")(strconv.Itoa(counters.NumberOfCriticalSeverity()))})
rows = append(rows, []string{"High", utils.GetColorForVulnerabilitySeverity("High")(strconv.Itoa(counters.NumberOfHighSeverity()))})
rows = append(rows, []string{"Medium", utils.GetColorForVulnerabilitySeverity("Medium")(strconv.Itoa(counters.NumberOfMediumSeverity()))})
rows = append(rows, []string{"Low", utils.GetColorForVulnerabilitySeverity("Low")(strconv.Itoa(counters.NumberOfLowSeverity()))})
return rows
}

View File

@@ -5,7 +5,6 @@ import (
"os"
"testing"
"github.com/jedib0t/go-pretty/v6/table"
"github.com/kubescape/opa-utils/reporthandling/results/v1/reportsummary"
"github.com/stretchr/testify/assert"
)
@@ -33,7 +32,7 @@ func (m *MockISeverityCounters) NumberOfLowSeverity() int {
return m.LowCount
}
func (m *MockISeverityCounters) Increase(_ string, _ int) {
func (m *MockISeverityCounters) Increase(severity string, amount int) {
}
func TestNewFrameworkPrinter(t *testing.T) {
@@ -61,28 +60,28 @@ func TestGetVerboseMode(t *testing.T) {
func TestShortRowFormat(t *testing.T) {
tests := []struct {
name string
rows []table.Row
expectedRows []table.Row
rows [][]string
expectedRows [][]string
}{
{
name: "Test Empty rows",
rows: []table.Row{},
expectedRows: []table.Row{},
rows: [][]string{},
expectedRows: [][]string{},
},
{
name: "Test Non empty row",
rows: []table.Row{
rows: [][]string{
{"Medium", "Control 1", "2", "20", "0.8"},
},
expectedRows: []table.Row{{"Severity : Medium\nControl Name : Control 1\nFailed Resources : 2\nAll Resources : 20\n% Compliance-Score : 0.8"}},
expectedRows: [][]string{[]string{"Severity : Medium\nControl Name : Control 1\nFailed Resources : 2\nAll Resources : 20\n% Compliance-Score : 0.8"}},
},
{
name: "Test Non empty rows",
rows: []table.Row{
rows: [][]string{
{"Medium", "Control 1", "2", "20", "0.8"},
{"Low", "Control 2", "0", "30", "1.0"},
},
expectedRows: []table.Row{{"Severity : Medium\nControl Name : Control 1\nFailed Resources : 2\nAll Resources : 20\n% Compliance-Score : 0.8"}, {"Severity : Low\nControl Name : Control 2\nFailed Resources : 0\nAll Resources : 30\n% Compliance-Score : 1.0"}},
expectedRows: [][]string{[]string{"Severity : Medium\nControl Name : Control 1\nFailed Resources : 2\nAll Resources : 20\n% Compliance-Score : 0.8"}, []string{"Severity : Low\nControl Name : Control 2\nFailed Resources : 0\nAll Resources : 30\n% Compliance-Score : 1.0"}},
},
}
@@ -97,12 +96,12 @@ func TestRenderSeverityCountersSummary(t *testing.T) {
tests := []struct {
name string
counters MockISeverityCounters
expected []table.Row
expected [][]string
}{
{
name: "All empty",
counters: MockISeverityCounters{},
expected: []table.Row{{"Critical", "0"}, {"High", "0"}, {"Medium", "0"}, {"Low", "0"}},
expected: [][]string{[]string{"Critical", "0"}, []string{"High", "0"}, []string{"Medium", "0"}, []string{"Low", "0"}},
},
{
name: "All different",
@@ -112,7 +111,7 @@ func TestRenderSeverityCountersSummary(t *testing.T) {
MediumCount: 27,
LowCount: 37,
},
expected: []table.Row{{"Critical", "7"}, {"High", "17"}, {"Medium", "27"}, {"Low", "37"}},
expected: [][]string{[]string{"Critical", "7"}, []string{"High", "17"}, []string{"Medium", "27"}, []string{"Low", "37"}},
},
{
name: "All equal",
@@ -122,7 +121,7 @@ func TestRenderSeverityCountersSummary(t *testing.T) {
MediumCount: 7,
LowCount: 7,
},
expected: []table.Row{{"Critical", "7"}, {"High", "7"}, {"Medium", "7"}, {"Low", "7"}},
expected: [][]string{[]string{"Critical", "7"}, []string{"High", "7"}, []string{"Medium", "7"}, []string{"Low", "7"}},
},
}

View File

@@ -5,7 +5,6 @@ import (
"io"
"strings"
"github.com/jedib0t/go-pretty/v6/table"
"github.com/jwalton/gchalk"
"github.com/kubescape/kubescape/v3/core/pkg/resultshandling/printer/v2/prettyprinter/tableprinter/utils"
"github.com/kubescape/opa-utils/reporthandling"
@@ -25,15 +24,15 @@ func NewRepoPrinter(inputPatterns []string) *RepoPrinter {
var _ TablePrinter = &RepoPrinter{}
func (rp *RepoPrinter) PrintSummaryTable(_ io.Writer, _ *reportsummary.SummaryDetails, _ [][]string) {
func (rp *RepoPrinter) PrintSummaryTable(writer io.Writer, summaryDetails *reportsummary.SummaryDetails, sortedControlIDs [][]string) {
}
func (rp *RepoPrinter) PrintCategoriesTables(writer io.Writer, summaryDetails *reportsummary.SummaryDetails, _ [][]string) {
func (rp *RepoPrinter) PrintCategoriesTables(writer io.Writer, summaryDetails *reportsummary.SummaryDetails, sortedControlIDs [][]string) {
categoriesToCategoryControls := mapCategoryToSummary(summaryDetails.ListControls(), mapRepoControlsToCategories)
tableRendered := false
tableRended := false
for _, id := range repoCategoriesDisplayOrder {
categoryControl, ok := categoriesToCategoryControls[id]
if !ok {
@@ -44,10 +43,10 @@ func (rp *RepoPrinter) PrintCategoriesTables(writer io.Writer, summaryDetails *r
continue
}
tableRendered = tableRendered || rp.renderSingleCategoryTable(categoryControl.CategoryName, mapCategoryToType[id], writer, categoryControl.controlSummaries, utils.MapInfoToPrintInfoFromIface(categoryControl.controlSummaries))
tableRended = tableRended || rp.renderSingleCategoryTable(categoryControl.CategoryName, mapCategoryToType[id], writer, categoryControl.controlSummaries, utils.MapInfoToPrintInfoFromIface(categoryControl.controlSummaries))
}
if !tableRendered {
if !tableRended {
fmt.Fprintln(writer, gchalk.WithGreen().Bold("All controls passed. No issues found"))
}
@@ -56,21 +55,21 @@ func (rp *RepoPrinter) PrintCategoriesTables(writer io.Writer, summaryDetails *r
func (rp *RepoPrinter) renderSingleCategoryTable(categoryName string, categoryType CategoryType, writer io.Writer, controlSummaries []reportsummary.IControlSummary, infoToPrintInfo []utils.InfoStars) bool {
sortControlSummaries(controlSummaries)
headers, columnAlignments := initCategoryTableData(categoryType)
headers, columnAligments := initCategoryTableData(categoryType)
tableWriter := getCategoryTableWriter(writer, headers, columnAlignments)
table := getCategoryTableWriter(writer, headers, columnAligments)
var rows []table.Row
var rows [][]string
for _, ctrls := range controlSummaries {
if ctrls.NumberOfResources().Failed() == 0 {
continue
}
var row table.Row
var row []string
if categoryType == TypeCounting {
row = rp.generateCountingCategoryRow(ctrls, rp.inputPatterns)
} else {
row = generateCategoryStatusRow(ctrls)
row = generateCategoryStatusRow(ctrls, infoToPrintInfo)
}
if len(row) > 0 {
rows = append(rows, row)
@@ -81,18 +80,18 @@ func (rp *RepoPrinter) renderSingleCategoryTable(categoryName string, categoryTy
return false
}
renderSingleCategory(writer, categoryName, tableWriter, rows, infoToPrintInfo)
renderSingleCategory(writer, categoryName, table, rows, infoToPrintInfo)
return true
}
func (rp *RepoPrinter) generateCountingCategoryRow(controlSummary reportsummary.IControlSummary, inputPatterns []string) table.Row {
rows := make(table.Row, 3)
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] = gchalk.WithYellow().Bold(fmt.Sprintf("%d", failedResources))
rows[1] = string(gchalk.WithYellow().Bold(fmt.Sprintf("%d", failedResources)))
} else {
rows[1] = fmt.Sprintf("%d", failedResources)
}

View File

@@ -5,12 +5,11 @@ import (
"strconv"
"strings"
"github.com/jedib0t/go-pretty/v6/table"
"github.com/jedib0t/go-pretty/v6/text"
"github.com/kubescape/kubescape/v3/core/cautils"
"github.com/kubescape/kubescape/v3/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 (
@@ -22,12 +21,12 @@ const (
_summaryRowLen = iota
)
func ControlCountersForSummary(counters reportsummary.ICounters) []table.Row {
rows := make([]table.Row, 0, 4)
rows = append(rows, table.Row{"Controls", strconv.Itoa(counters.All())})
rows = append(rows, table.Row{"Passed", strconv.Itoa(counters.Passed())})
rows = append(rows, table.Row{"Failed", strconv.Itoa(counters.Failed())})
rows = append(rows, table.Row{"Action Required", strconv.Itoa(counters.Skipped())})
func ControlCountersForSummary(counters reportsummary.ICounters) [][]string {
rows := [][]string{}
rows = append(rows, []string{"Controls", strconv.Itoa(counters.All())})
rows = append(rows, []string{"Passed", strconv.Itoa(counters.Passed())})
rows = append(rows, []string{"Failed", strconv.Itoa(counters.Failed())})
rows = append(rows, []string{"Action Required", strconv.Itoa(counters.Skipped())})
return rows
}
@@ -36,13 +35,13 @@ func GetSeverityColumn(controlSummary reportsummary.IControlSummary) string {
return utils.GetColor(apis.ControlSeverityToInt(controlSummary.GetScoreFactor()))(apis.ControlSeverityToString(controlSummary.GetScoreFactor()))
}
func GetControlTableHeaders(short bool) table.Row {
var headers table.Row
func GetControlTableHeaders(short bool) []string {
var headers []string
if short {
headers = make(table.Row, 1)
headers = make([]string, 1)
headers[0] = "Controls"
} else {
headers = make(table.Row, _summaryRowLen)
headers = make([]string, _summaryRowLen)
headers[summaryColumnName] = "Control name"
headers[summaryColumnCounterFailed] = "Failed resources"
headers[summaryColumnCounterAll] = "All Resources"
@@ -52,22 +51,22 @@ func GetControlTableHeaders(short bool) table.Row {
return headers
}
func GetColumnsAlignments() []table.ColumnConfig {
return []table.ColumnConfig{
{Number: summaryColumnSeverity + 1, Align: text.AlignCenter},
{Number: summaryColumnName + 1, Align: text.AlignLeft},
{Number: summaryColumnCounterFailed + 1, Align: text.AlignCenter},
{Number: summaryColumnCounterAll + 1, Align: text.AlignCenter},
{Number: summaryColumnComplianceScore + 1, Align: text.AlignCenter},
}
func GetColumnsAlignments() []int {
alignments := make([]int, _summaryRowLen)
alignments[summaryColumnSeverity] = tablewriter.ALIGN_CENTER
alignments[summaryColumnName] = tablewriter.ALIGN_LEFT
alignments[summaryColumnCounterFailed] = tablewriter.ALIGN_CENTER
alignments[summaryColumnCounterAll] = tablewriter.ALIGN_CENTER
alignments[summaryColumnComplianceScore] = tablewriter.ALIGN_CENTER
return alignments
}
func GenerateRow(controlSummary reportsummary.IControlSummary, infoToPrintInfo []utils.InfoStars, verbose bool) table.Row {
row := make(table.Row, _summaryRowLen)
func GenerateRow(controlSummary reportsummary.IControlSummary, infoToPrintInfo []utils.InfoStars, verbose bool) []string {
row := make([]string, _summaryRowLen)
// ignore passed results
if !verbose && (controlSummary.GetStatus().IsPassed()) {
return table.Row{}
return []string{}
}
row[summaryColumnSeverity] = GetSeverityColumn(controlSummary)
@@ -99,14 +98,14 @@ func GetInfoColumn(controlSummary reportsummary.IControlSummary, infoToPrintInfo
return ""
}
func GenerateFooter(summaryDetails *reportsummary.SummaryDetails, short bool) table.Row {
var row table.Row
func GenerateFooter(summaryDetails *reportsummary.SummaryDetails, short bool) []string {
var row []string
if short {
row = make(table.Row, 1)
row = make([]string, 1)
row[0] = fmt.Sprintf("Resource Summary"+strings.Repeat(" ", 0)+"\n\nFailed Resources"+strings.Repeat(" ", 1)+": %d\nAll Resources"+strings.Repeat(" ", 4)+": %d\n%% Compliance-Score"+strings.Repeat(" ", 4)+": %.2f%%", summaryDetails.NumberOfResources().Failed(), summaryDetails.NumberOfResources().All(), summaryDetails.ComplianceScore)
} else {
// Severity | Control name | failed resources | all resources | % success
row = make(table.Row, _summaryRowLen)
row = make([]string, _summaryRowLen)
row[summaryColumnName] = "Resource Summary"
row[summaryColumnCounterFailed] = fmt.Sprintf("%d", summaryDetails.NumberOfResources().Failed())
row[summaryColumnCounterAll] = fmt.Sprintf("%d", summaryDetails.NumberOfResources().All())

View File

@@ -542,14 +542,14 @@ func TestGetDocsForControl(t *testing.T) {
controlSummary: &reportsummary.ControlSummary{
ControlID: "ctrlID1",
},
expectedDocsLink: "https://kubescape.io/docs/controls/ctrlid1",
expectedDocsLink: "https://hub.armosec.io/docs/ctrlid1",
},
{
name: "control with lowercase ID",
controlSummary: &reportsummary.ControlSummary{
ControlID: "ctrlid1",
},
expectedDocsLink: "https://kubescape.io/docs/controls/ctrlid1",
expectedDocsLink: "https://hub.armosec.io/docs/ctrlid1",
},
}

View File

@@ -3,7 +3,6 @@ package configurationprinter
import (
"io"
"github.com/jedib0t/go-pretty/v6/table"
"github.com/kubescape/kubescape/v3/core/pkg/resultshandling/printer/v2/prettyprinter/tableprinter/utils"
"github.com/kubescape/opa-utils/reporthandling/results/v1/reportsummary"
)
@@ -17,11 +16,11 @@ func NewWorkloadPrinter() *WorkloadPrinter {
return &WorkloadPrinter{}
}
func (wp *WorkloadPrinter) PrintSummaryTable(_ io.Writer, _ *reportsummary.SummaryDetails, _ [][]string) {
func (wp *WorkloadPrinter) PrintSummaryTable(writer io.Writer, summaryDetails *reportsummary.SummaryDetails, sortedControlIDs [][]string) {
}
func (wp *WorkloadPrinter) PrintCategoriesTables(writer io.Writer, summaryDetails *reportsummary.SummaryDetails, _ [][]string) {
func (wp *WorkloadPrinter) PrintCategoriesTables(writer io.Writer, summaryDetails *reportsummary.SummaryDetails, sortedControlIDs [][]string) {
categoriesToCategoryControls := mapCategoryToSummary(summaryDetails.ListControls(), mapWorkloadControlsToCategories)
@@ -31,20 +30,21 @@ func (wp *WorkloadPrinter) PrintCategoriesTables(writer io.Writer, summaryDetail
continue
}
wp.renderSingleCategoryTable(categoryControl.CategoryName, writer, categoryControl.controlSummaries, utils.MapInfoToPrintInfoFromIface(categoryControl.controlSummaries))
wp.renderSingleCategoryTable(categoryControl.CategoryName, mapCategoryToType[id], writer, categoryControl.controlSummaries, utils.MapInfoToPrintInfoFromIface(categoryControl.controlSummaries))
}
}
func (wp *WorkloadPrinter) renderSingleCategoryTable(categoryName string, writer io.Writer, controlSummaries []reportsummary.IControlSummary, infoToPrintInfo []utils.InfoStars) {
func (wp *WorkloadPrinter) renderSingleCategoryTable(categoryName string, categoryType CategoryType, writer io.Writer, controlSummaries []reportsummary.IControlSummary, infoToPrintInfo []utils.InfoStars) {
sortControlSummaries(controlSummaries)
headers, columnAlignments := wp.initCategoryTableData()
headers, columnAligments := wp.initCategoryTableData()
tableWriter := getCategoryTableWriter(writer, headers, columnAlignments)
table := getCategoryTableWriter(writer, headers, columnAligments)
var rows []table.Row
var rows [][]string
for _, ctrls := range controlSummaries {
row := generateCategoryStatusRow(ctrls)
var row []string
row = generateCategoryStatusRow(ctrls, infoToPrintInfo)
if len(row) > 0 {
rows = append(rows, row)
}
@@ -54,9 +54,9 @@ func (wp *WorkloadPrinter) renderSingleCategoryTable(categoryName string, writer
return
}
renderSingleCategory(writer, categoryName, tableWriter, rows, infoToPrintInfo)
renderSingleCategory(writer, categoryName, table, rows, infoToPrintInfo)
}
func (wp *WorkloadPrinter) initCategoryTableData() (table.Row, []table.ColumnConfig) {
func (wp *WorkloadPrinter) initCategoryTableData() ([]string, []int) {
return getCategoryStatusTypeHeaders(), getStatusTypeAlignments()
}

View File

@@ -3,19 +3,17 @@ package configurationprinter
import (
"testing"
"github.com/jedib0t/go-pretty/v6/table"
"github.com/jedib0t/go-pretty/v6/text"
"github.com/stretchr/testify/assert"
"github.com/olekukonko/tablewriter"
)
func TestWorkloadScan_InitCategoryTableData(t *testing.T) {
expectedHeader := []string{"", "Control name", "Docs"}
expectedAlign := []table.ColumnConfig{{Number: 1, Align: text.AlignCenter}, {Number: 2, Align: text.AlignLeft}, {Number: 3, Align: text.AlignCenter}}
expectedAlign := []int{tablewriter.ALIGN_CENTER, tablewriter.ALIGN_LEFT, tablewriter.ALIGN_CENTER}
workloadPrinter := NewWorkloadPrinter()
headers, columnAlignments := workloadPrinter.initCategoryTableData()
headers, columnAligments := workloadPrinter.initCategoryTableData()
for i := range headers {
if headers[i] != expectedHeader[i] {
@@ -23,8 +21,10 @@ func TestWorkloadScan_InitCategoryTableData(t *testing.T) {
}
}
for i := range columnAlignments {
assert.Equal(t, expectedAlign[i], columnAlignments[i])
for i := range columnAligments {
if columnAligments[i] != expectedAlign[i] {
t.Errorf("Expected column alignment %d, got %d", expectedAlign[i], columnAligments[i])
}
}
}

View File

@@ -42,7 +42,7 @@ func TestPrintImageScanningTable(t *testing.T) {
},
},
},
want: "──────────┬───────────────┬───────────┬─────────┬──────────\n│ Severity │ Vulnerability │ Component │ Version │ Fixed in │\n├──────────┼───────────────┼───────────┼─────────┼──────────┤\n│ High │ CVE-2020-0002 │ package2 │ 1.0.0 │ │\n│ Medium │ CVE-2020-0003 │ package3 │ 1.0.0 │ │\n│ Low │ CVE-2020-0001 │ package1 │ 1.0.0 │ │\n──────────┴───────────────┴───────────┴─────────┴──────────\n",
want: "──────────┬───────────────┬───────────┬─────────┬──────────\n│ Severity │ Vulnerability │ Component │ Version │ Fixed in │\n├──────────┼───────────────┼───────────┼─────────┼──────────┤\n│ High │ CVE-2020-0002 │ package2 │ 1.0.0 │ │\n│ Medium │ CVE-2020-0003 │ package3 │ 1.0.0 │ │\n│ Low │ CVE-2020-0001 │ package1 │ 1.0.0 │ │\n──────────┴───────────────┴───────────┴─────────┴──────────\n",
},
{
name: "check fixed CVEs show versions",
@@ -65,7 +65,7 @@ func TestPrintImageScanningTable(t *testing.T) {
},
},
},
want: "──────────┬───────────────┬───────────┬─────────┬──────────\n│ Severity │ Vulnerability │ Component │ Version │ Fixed in │\n├──────────┼───────────────┼───────────┼─────────┼──────────┤\n│ High │ CVE-2020-0002 │ package2 │ 1.0.0 │ v1,v2 │\n│ Low │ CVE-2020-0001 │ package1 │ 1.0.0 │ │\n──────────┴───────────────┴───────────┴─────────┴──────────\n",
want: "──────────┬───────────────┬───────────┬─────────┬──────────\n│ Severity │ Vulnerability │ Component │ Version │ Fixed in │\n├──────────┼───────────────┼───────────┼─────────┼──────────┤\n│ High │ CVE-2020-0002 │ package2 │ 1.0.0 │ v1,v2 │\n│ Low │ CVE-2020-0001 │ package1 │ 1.0.0 │ │\n──────────┴───────────────┴───────────┴─────────┴──────────\n",
},
}

View File

@@ -6,28 +6,33 @@ import (
"strings"
v5 "github.com/anchore/grype/grype/db/v5"
"github.com/jedib0t/go-pretty/v6/table"
"github.com/jedib0t/go-pretty/v6/text"
"github.com/jwalton/gchalk"
"github.com/kubescape/kubescape/v3/core/pkg/resultshandling/printer/v2/prettyprinter/tableprinter/utils"
"github.com/olekukonko/tablewriter"
)
func renderTable(writer io.Writer, headers table.Row, columnAlignments []table.ColumnConfig, rows []table.Row) {
tableWriter := table.NewWriter()
tableWriter.SetOutputMirror(writer)
tableWriter.AppendHeader(headers)
tableWriter.Style().Options.SeparateHeader = true
tableWriter.Style().Format.HeaderAlign = text.AlignLeft
tableWriter.Style().Format.Header = text.FormatDefault
tableWriter.SetColumnConfigs(columnAlignments)
tableWriter.Style().Box = table.StyleBoxRounded
func renderTable(writer io.Writer, headers []string, columnAlignments []int, rows [][]string) {
table := tablewriter.NewWriter(writer)
table.SetHeader(headers)
table.SetHeaderLine(true)
table.SetHeaderAlignment(tablewriter.ALIGN_LEFT)
table.SetAutoFormatHeaders(false)
table.SetColumnAlignment(columnAlignments)
table.SetUnicodeHVC(tablewriter.Regular, tablewriter.Regular, gchalk.Ansi256(238))
tableWriter.AppendRows(rows)
var headerColors []tablewriter.Colors
for range rows[0] {
headerColors = append(headerColors, tablewriter.Colors{tablewriter.Bold, tablewriter.FgHiYellowColor})
}
table.SetHeaderColor(headerColors...)
tableWriter.Render()
table.AppendBulk(rows)
table.Render()
}
func generateRows(summary ImageScanSummary) []table.Row {
rows := make([]table.Row, 0, len(summary.CVEs))
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 {
@@ -41,8 +46,8 @@ func generateRows(summary ImageScanSummary) []table.Row {
return rows
}
func generateRow(cve CVE) table.Row {
row := make(table.Row, 5)
func generateRow(cve CVE) []string {
row := make([]string, 5)
row[imageColumnSeverity] = utils.GetColorForVulnerabilitySeverity(cve.Severity)(cve.Severity)
row[imageColumnName] = cve.ID
row[imageColumnComponent] = cve.Package
@@ -54,15 +59,13 @@ func generateRow(cve CVE) table.Row {
// if the CVE is not fixed, show the state
} else if cve.FixedState == string(v5.WontFixState) {
row[imageColumnFixedIn] = cve.FixedState
} else {
row[imageColumnFixedIn] = ""
}
return row
}
func getImageScanningHeaders() table.Row {
headers := make(table.Row, 5)
func getImageScanningHeaders() []string {
headers := make([]string, 5)
headers[imageColumnSeverity] = "Severity"
headers[imageColumnName] = "Vulnerability"
headers[imageColumnComponent] = "Component"
@@ -71,12 +74,6 @@ func getImageScanningHeaders() table.Row {
return headers
}
func getImageScanningColumnsAlignments() []table.ColumnConfig {
return []table.ColumnConfig{
{Number: 1, Align: text.AlignCenter},
{Number: 2, Align: text.AlignLeft},
{Number: 3, Align: text.AlignLeft},
{Number: 4, Align: text.AlignLeft},
{Number: 5, Align: text.AlignLeft},
}
func getImageScanningColumnsAlignments() []int {
return []int{tablewriter.ALIGN_CENTER, tablewriter.ALIGN_LEFT, tablewriter.ALIGN_LEFT, tablewriter.ALIGN_LEFT, tablewriter.ALIGN_LEFT}
}

View File

@@ -6,6 +6,7 @@ import (
"testing"
v5 "github.com/anchore/grype/grype/db/v5"
"github.com/olekukonko/tablewriter"
"github.com/stretchr/testify/assert"
)
@@ -45,7 +46,7 @@ func TestRenderTable(t *testing.T) {
},
},
},
want: "──────────┬───────────────┬───────────┬─────────┬──────────\n│ Severity │ Vulnerability │ Component │ Version │ Fixed in │\n├──────────┼───────────────┼───────────┼─────────┼──────────┤\n│ High │ CVE-2020-0002 │ package2 │ 1.0.0 │ │\n│ Medium │ CVE-2020-0003 │ package3 │ 1.0.0 │ │\n│ Low │ CVE-2020-0001 │ package1 │ 1.0.0 │ │\n──────────┴───────────────┴───────────┴─────────┴──────────\n",
want: "──────────┬───────────────┬───────────┬─────────┬──────────\n│ Severity │ Vulnerability │ Component │ Version │ Fixed in │\n├──────────┼───────────────┼───────────┼─────────┼──────────┤\n│ High │ CVE-2020-0002 │ package2 │ 1.0.0 │ │\n│ Medium │ CVE-2020-0003 │ package3 │ 1.0.0 │ │\n│ Low │ CVE-2020-0001 │ package1 │ 1.0.0 │ │\n──────────┴───────────────┴───────────┴─────────┴──────────\n",
},
{
name: "check fixed CVEs show versions",
@@ -68,7 +69,7 @@ func TestRenderTable(t *testing.T) {
},
},
},
want: "──────────┬───────────────┬───────────┬─────────┬──────────\n│ Severity │ Vulnerability │ Component │ Version │ Fixed in │\n├──────────┼───────────────┼───────────┼─────────┼──────────┤\n│ High │ CVE-2020-0002 │ package2 │ 1.0.0 │ v1,v2 │\n│ Low │ CVE-2020-0001 │ package1 │ 1.0.0 │ │\n──────────┴───────────────┴───────────┴─────────┴──────────\n",
want: "──────────┬───────────────┬───────────┬─────────┬──────────\n│ Severity │ Vulnerability │ Component │ Version │ Fixed in │\n├──────────┼───────────────┼───────────┼─────────┼──────────┤\n│ High │ CVE-2020-0002 │ package2 │ 1.0.0 │ v1,v2 │\n│ Low │ CVE-2020-0001 │ package1 │ 1.0.0 │ │\n──────────┴───────────────┴───────────┴─────────┴──────────\n",
},
}
@@ -246,3 +247,15 @@ func TestGetImageScanningHeaders(t *testing.T) {
}
}
}
func TestGetImageScanningColumnsAlignments(t *testing.T) {
alignments := getImageScanningColumnsAlignments()
expectedAlignments := []int{tablewriter.ALIGN_CENTER, tablewriter.ALIGN_LEFT, tablewriter.ALIGN_LEFT, tablewriter.ALIGN_LEFT, tablewriter.ALIGN_LEFT}
for i := range alignments {
if alignments[i] != expectedAlignments[i] {
t.Errorf("expected %d, got %d", expectedAlignments[i], alignments[i])
}
}
}

View File

@@ -6,7 +6,6 @@ import (
"os"
"github.com/enescakir/emoji"
"github.com/jedib0t/go-pretty/v6/table"
"github.com/jwalton/gchalk"
"github.com/kubescape/kubescape/v3/core/cautils"
"github.com/kubescape/opa-utils/reporthandling/apis"
@@ -139,19 +138,19 @@ func GetStatusIcon(status apis.ScanningStatus) string {
}
}
func CheckShortTerminalWidth(rows []table.Row, headers table.Row) bool {
func CheckShortTerminalWidth(rows [][]string, headers []string) bool {
maxWidth := 0
for _, row := range rows {
rowWidth := 0
for idx, cell := range row {
cellLen := len(cell.(string))
cellLen := len(cell)
if cellLen > 50 { // Take only 50 characters of each sentence for counting size
cellLen = 50
}
if cellLen > len(headers[idx].(string)) {
if cellLen > len(headers[idx]) {
rowWidth += cellLen
} else {
rowWidth += len(headers[idx].(string))
rowWidth += len(headers[idx])
}
rowWidth += 2
}

View File

@@ -5,6 +5,7 @@ import (
"fmt"
"os"
"github.com/anchore/grype/grype/presenter/models"
"github.com/kubescape/go-logger"
"github.com/kubescape/go-logger/helpers"
"github.com/kubescape/k8s-interface/workloadinterface"
@@ -58,6 +59,9 @@ func (pp *PrometheusPrinter) generatePrometheusFormat(
return m
}
func (pp *PrometheusPrinter) PrintImageScan(context.Context, *models.PresenterConfig) {
}
func (pp *PrometheusPrinter) ActionPrint(ctx context.Context, opaSessionObj *cautils.OPASessionObj, imageScanData []cautils.ImageScanData) {
if opaSessionObj == nil {
logger.L().Ctx(ctx).Error("failed to print results, missing data")

View File

@@ -2,18 +2,16 @@ package printer
import (
"fmt"
"regexp"
"strconv"
"sort"
"strings"
"github.com/jedib0t/go-pretty/v6/table"
"github.com/jedib0t/go-pretty/v6/text"
"github.com/kubescape/k8s-interface/workloadinterface"
"github.com/jwalton/gchalk"
"github.com/kubescape/kubescape/v3/core/cautils"
"github.com/kubescape/kubescape/v3/core/pkg/resultshandling/printer/v2/prettyprinter"
"github.com/kubescape/kubescape/v3/core/pkg/resultshandling/printer/v2/prettyprinter/tableprinter/utils"
"github.com/kubescape/opa-utils/reporthandling/results/v1/reportsummary"
"github.com/kubescape/opa-utils/reporthandling/results/v1/resourcesresults"
"github.com/olekukonko/tablewriter"
)
const (
@@ -45,46 +43,61 @@ func (prettyPrinter *PrettyPrinter) resourceTable(opaSessionObj *cautils.OPASess
if resource.GetNamespace() != "" {
fmt.Fprintf(prettyPrinter.writer, "Namespace: %s\n", resource.GetNamespace())
}
fmt.Fprintf(prettyPrinter.writer, "\n%s\n\n", prettyprinter.ControlCountersForResource(result.ListControlsIDs(nil)))
fmt.Fprintf(prettyPrinter.writer, "\n"+prettyprinter.ControlCountersForResource(result.ListControlsIDs(nil))+"\n\n")
summaryTable := table.NewWriter()
summaryTable.SetOutputMirror(prettyPrinter.writer)
summaryTable := tablewriter.NewWriter(prettyPrinter.writer)
summaryTable.Style().Options.SeparateHeader = true
summaryTable.Style().Options.SeparateRows = true
summaryTable.Style().Format.HeaderAlign = text.AlignLeft
summaryTable.Style().Format.Header = text.FormatDefault
summaryTable.Style().Box = table.StyleBoxRounded
summaryTable.SetAutoWrapText(true)
summaryTable.SetAutoMergeCells(true)
summaryTable.SetHeaderLine(true)
summaryTable.SetRowLine(true)
summaryTable.SetHeaderAlignment(tablewriter.ALIGN_LEFT)
summaryTable.SetAutoFormatHeaders(false)
summaryTable.SetUnicodeHVC(tablewriter.Regular, tablewriter.Regular, gchalk.Ansi256(238))
resourceRows := generateResourceRows(result.ListControls(), &opaSessionObj.Report.SummaryDetails, resource)
resourceRows := [][]string{}
if raw := generateResourceRows(result.ListControls(), &opaSessionObj.Report.SummaryDetails); len(raw) > 0 {
resourceRows = append(resourceRows, raw...)
}
short := utils.CheckShortTerminalWidth(resourceRows, generateResourceHeader(false))
if short {
summaryTable.SetAutoWrapText(false)
summaryTable.SetAutoMergeCells(false)
resourceRows = shortFormatResource(resourceRows)
}
summaryTable.AppendHeader(generateResourceHeader(short))
summaryTable.SetHeader(generateResourceHeader(short))
summaryTable.AppendRows(resourceRows)
var headerColors []tablewriter.Colors
for range resourceRows[0] {
headerColors = append(headerColors, tablewriter.Colors{tablewriter.Bold, tablewriter.FgHiYellowColor})
}
summaryTable.SetHeaderColor(headerColors...)
data := Matrix{}
data = append(data, resourceRows...)
// For control scan framework will be nil
sort.Sort(data)
summaryTable.AppendBulk(data)
summaryTable.Render()
}
}
func generateResourceRows(controls []resourcesresults.ResourceAssociatedControl, summaryDetails *reportsummary.SummaryDetails, resource workloadinterface.IMetadata) []table.Row {
var rows []table.Row
func generateResourceRows(controls []resourcesresults.ResourceAssociatedControl, summaryDetails *reportsummary.SummaryDetails) [][]string {
rows := [][]string{}
for i := range controls {
row := make(table.Row, _resourceRowLen)
row := make([]string, _resourceRowLen)
if !controls[i].GetStatus(nil).IsFailed() {
continue
}
row[resourceColumnURL] = cautils.GetControlLink(controls[i].GetID())
paths := AssistedRemediationPathsToString(&controls[i])
addContainerNameToAssistedRemediation(resource, &paths)
row[resourceColumnPath] = strings.Join(paths, "\n")
row[resourceColumnPath] = strings.Join(AssistedRemediationPathsToString(&controls[i]), "\n")
row[resourceColumnName] = controls[i].GetName()
if c := summaryDetails.Controls.GetControl(reportsummary.EControlCriteriaID, controls[i].GetID()); c != nil {
@@ -97,32 +110,22 @@ func generateResourceRows(controls []resourcesresults.ResourceAssociatedControl,
return rows
}
func addContainerNameToAssistedRemediation(resource workloadinterface.IMetadata, paths *[]string) {
for i := range *paths {
re := regexp.MustCompile(`spec\.containers\[(\d+)]`)
match := re.FindStringSubmatch((*paths)[i])
if len(match) == 2 {
index, _ := strconv.Atoi(match[1])
wl := workloadinterface.NewWorkloadObj(resource.GetObject())
containers, _ := wl.GetContainers()
containerName := containers[index].Name
(*paths)[i] = (*paths)[i] + " (" + containerName + ")"
}
}
}
func generateResourceHeader(short bool) []string {
headers := make([]string, 0)
func generateResourceHeader(short bool) table.Row {
if short {
return table.Row{"Resources"}
headers = append(headers, "Resources")
} else {
return table.Row{"Severity", "Control name", "Docs", "Assisted remediation"}
headers = append(headers, []string{"Severity", "Control name", "Docs", "Assisted remediation"}...)
}
return headers
}
func shortFormatResource(resourceRows []table.Row) []table.Row {
rows := make([]table.Row, len(resourceRows))
for i, resourceRow := range resourceRows {
rows[i] = table.Row{fmt.Sprintf("Severity"+strings.Repeat(" ", 13)+": %+v\nControl Name"+strings.Repeat(" ", 9)+": %+v\nDocs"+strings.Repeat(" ", 17)+": %+v\nAssisted Remediation"+strings.Repeat(" ", 1)+": %+v", resourceRow[resourceColumnSeverity], resourceRow[resourceColumnName], resourceRow[resourceColumnURL], strings.Replace(resourceRow[resourceColumnPath].(string), "\n", "\n"+strings.Repeat(" ", 23), -1))}
func shortFormatResource(resourceRows [][]string) [][]string {
rows := [][]string{}
for _, resourceRow := range resourceRows {
rows = append(rows, []string{fmt.Sprintf("Severity"+strings.Repeat(" ", 13)+": %+v\nControl Name"+strings.Repeat(" ", 9)+": %+v\nDocs"+strings.Repeat(" ", 17)+": %+v\nAssisted Remediation"+strings.Repeat(" ", 1)+": %+v", resourceRow[resourceColumnSeverity], resourceRow[resourceColumnName], resourceRow[resourceColumnURL], strings.Replace(resourceRow[resourceColumnPath], "\n", "\n"+strings.Repeat(" ", 23), -1))})
}
return rows
}

View File

@@ -4,8 +4,6 @@ import (
"testing"
"github.com/armosec/armoapi-go/armotypes"
"github.com/jedib0t/go-pretty/v6/table"
"github.com/kubescape/k8s-interface/workloadinterface"
"github.com/kubescape/opa-utils/reporthandling/apis"
"github.com/kubescape/opa-utils/reporthandling/results/v1/reportsummary"
"github.com/kubescape/opa-utils/reporthandling/results/v1/resourcesresults"
@@ -328,15 +326,15 @@ func TestFailedPathsToString(t *testing.T) {
func TestShortFormatResource(t *testing.T) {
// Create a test case with an empty resourceRows slice
emptyResourceRows := []table.Row{}
emptyResourceRows := [][]string{}
// Create a test case with a single resource row
singleResourceRow := []table.Row{
singleResourceRow := [][]string{
{"High", "Control1", "https://example.com/doc1", "Path1"},
}
// Create a test case with multiple resource rows
multipleResourceRows := []table.Row{
multipleResourceRows := [][]string{
{"Medium", "Control2", "https://example.com/doc2", "Path2"},
{"Low", "Control3", "https://example.com/doc3", "Path3"},
}
@@ -345,11 +343,11 @@ func TestShortFormatResource(t *testing.T) {
assert.Empty(t, actualRows)
actualRows = shortFormatResource(singleResourceRow)
expectedRows := []table.Row{{"Severity : High\nControl Name : Control1\nDocs : https://example.com/doc1\nAssisted Remediation : Path1"}}
expectedRows := [][]string{{"Severity : High\nControl Name : Control1\nDocs : https://example.com/doc1\nAssisted Remediation : Path1"}}
assert.Equal(t, expectedRows, actualRows)
actualRows = shortFormatResource(multipleResourceRows)
expectedRows = []table.Row{{"Severity : Medium\nControl Name : Control2\nDocs : https://example.com/doc2\nAssisted Remediation : Path2"},
expectedRows = [][]string{{"Severity : Medium\nControl Name : Control2\nDocs : https://example.com/doc2\nAssisted Remediation : Path2"},
{"Severity : Low\nControl Name : Control3\nDocs : https://example.com/doc3\nAssisted Remediation : Path3"}}
assert.Equal(t, expectedRows, actualRows)
}
@@ -357,47 +355,33 @@ func TestShortFormatResource(t *testing.T) {
func TestGenerateResourceHeader(t *testing.T) {
// Test case 1: Short headers
shortHeaders := generateResourceHeader(true)
expectedShortHeaders := table.Row{"Resources"}
expectedShortHeaders := []string{"Resources"}
assert.Equal(t, expectedShortHeaders, shortHeaders)
// Test case 2: Full headers
fullHeaders := generateResourceHeader(false)
expectedFullHeaders := table.Row{"Severity", "Control name", "Docs", "Assisted remediation"}
expectedFullHeaders := []string{"Severity", "Control name", "Docs", "Assisted remediation"}
assert.Equal(t, expectedFullHeaders, fullHeaders)
}
func TestGenerateResourceRows_Loop(t *testing.T) {
tests := []struct {
name string
summaryDetails reportsummary.SummaryDetails
controls []resourcesresults.ResourceAssociatedControl
resource workloadinterface.IMetadata
expectedLen int
expectedContainerName string
name string
summaryDetails reportsummary.SummaryDetails
controls []resourcesresults.ResourceAssociatedControl
expectedLen int
}{
{
name: "Empty controls",
summaryDetails: reportsummary.SummaryDetails{},
controls: []resourcesresults.ResourceAssociatedControl{},
resource: workloadinterface.NewWorkloadObj(map[string]interface{}{
"kind": "Pod",
"spec": map[string]interface{}{
"containers": []interface{}{
map[string]interface{}{
"name": "alpine-container",
"image": "alpine:latest",
},
},
},
}),
expectedLen: 0,
expectedContainerName: "",
expectedLen: 0,
},
{
name: "2 Failed Controls",
summaryDetails: reportsummary.SummaryDetails{},
controls: []resourcesresults.ResourceAssociatedControl{
{
resourcesresults.ResourceAssociatedControl{
ControlID: "control-1",
Name: "Control 1",
Status: apis.StatusInfo{},
@@ -409,16 +393,16 @@ func TestGenerateResourceRows_Loop(t *testing.T) {
Paths: []armotypes.PosturePaths{
{
FailedPath: "spec.template.spec.containers[0].securityContext.runAsNonRoot=true",
FailedPath: "some-path1",
},
{
FailedPath: "spec.template.spec.containers[0].securityContext.runAsGroup=1000",
FailedPath: "random-path1",
},
},
},
},
},
{
resourcesresults.ResourceAssociatedControl{
ControlID: "control-2",
Name: "Control 2",
Status: apis.StatusInfo{},
@@ -429,35 +413,23 @@ func TestGenerateResourceRows_Loop(t *testing.T) {
SubStatus: "configuration",
Paths: []armotypes.PosturePaths{
{
FailedPath: "spec.template.spec.containers[0].securityContext.runAsNonRoot=true",
FailedPath: "some-path2",
},
{
FailedPath: "spec.template.spec.containers[0].securityContext.runAsGroup=true",
FailedPath: "random-path2",
},
},
},
},
},
},
resource: workloadinterface.NewWorkloadObj(map[string]interface{}{
"kind": "Pod",
"spec": map[string]interface{}{
"containers": []interface{}{
map[string]interface{}{
"name": "alpine-container",
"image": "alpine:latest",
},
},
},
}),
expectedLen: 2,
expectedContainerName: "alpine-container",
expectedLen: 2,
},
{
name: "One failed control",
summaryDetails: reportsummary.SummaryDetails{},
controls: []resourcesresults.ResourceAssociatedControl{
{
resourcesresults.ResourceAssociatedControl{
ControlID: "control-1",
Name: "Control 1",
Status: apis.StatusInfo{},
@@ -469,16 +441,16 @@ func TestGenerateResourceRows_Loop(t *testing.T) {
Paths: []armotypes.PosturePaths{
{
FailedPath: "spec.template.spec.containers[0].securityContext.runAsNonRoot=true",
FailedPath: "some-path1",
},
{
FailedPath: "spec.template.spec.containers[0].securityContext.runAsGroup=true",
FailedPath: "random-path1",
},
},
},
},
},
{
resourcesresults.ResourceAssociatedControl{
ControlID: "control-2",
Name: "Control 2",
Status: apis.StatusInfo{},
@@ -489,42 +461,24 @@ func TestGenerateResourceRows_Loop(t *testing.T) {
SubStatus: "configuration",
Paths: []armotypes.PosturePaths{
{
FailedPath: "spec.template.spec.containers[0].securityContext.runAsNonRoot=true",
FailedPath: "some-path2",
},
{
FailedPath: "spec.template.spec.containers[0].securityContext.runAsGroup=true",
FailedPath: "random-path2",
},
},
},
},
},
},
resource: workloadinterface.NewWorkloadObj(map[string]interface{}{
"kind": "Pod",
"spec": map[string]interface{}{
"containers": []interface{}{
map[string]interface{}{
"name": "nginx-container",
"image": "nginx:latest",
},
},
},
}),
expectedLen: 1,
expectedContainerName: "nginx-container",
expectedLen: 1,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
rows := generateResourceRows(tt.controls, &tt.summaryDetails, tt.resource)
rows := generateResourceRows(tt.controls, &tt.summaryDetails)
assert.Equal(t, tt.expectedLen, len(rows))
//remediation is the last column of the first row
if len(rows) != 0 {
remediation := rows[0][3]
assert.Contains(t, remediation, tt.expectedContainerName)
}
})
}
}

View File

@@ -12,9 +12,8 @@ import (
"strconv"
"strings"
"github.com/anchore/clio"
"github.com/anchore/grype/grype/presenter"
"github.com/anchore/grype/grype/presenter/models"
grypesarif "github.com/anchore/grype/grype/presenter/sarif"
"github.com/kubescape/go-logger"
"github.com/kubescape/go-logger/helpers"
"github.com/kubescape/kubescape/v3/core/cautils"
@@ -116,14 +115,12 @@ func (sp *SARIFPrinter) addResult(scanRun *sarif.Run, ctl reportsummary.IControl
})
}
func (sp *SARIFPrinter) printImageScan(ctx context.Context, scanResults cautils.ImageScanData) error {
model, err := models.NewDocument(clio.Identification{}, scanResults.Packages, scanResults.Context,
*scanResults.RemainingMatches, scanResults.IgnoredMatches, scanResults.VulnerabilityProvider, nil, nil, models.DefaultSortStrategy, false)
if err != nil {
return fmt.Errorf("failed to create document: %w", err)
func (sp *SARIFPrinter) printImageScan(ctx context.Context, scanResults *models.PresenterConfig) error {
if scanResults == nil {
return fmt.Errorf("no no image vulnerability data provided")
}
pres := grypesarif.NewPresenter(models.PresenterConfig{Document: model, SBOM: scanResults.SBOM})
pres := presenter.GetPresenter(printer.SARIFFormat, "", false, *scanResults)
if err := pres.Present(sp.writer); err != nil {
return err
}
@@ -167,7 +164,7 @@ func (sp *SARIFPrinter) ActionPrint(ctx context.Context, opaSessionObj *cautils.
}
// image scan
if err := sp.printImageScan(ctx, imageScanData[0]); err != nil {
if err := sp.printImageScan(ctx, imageScanData[0].PresenterConfig); err != nil {
logger.L().Ctx(ctx).Error("failed to write results in sarif format", helpers.Error(err))
return
}

View File

@@ -3,6 +3,7 @@ package printer
import (
"context"
"github.com/anchore/grype/grype/presenter/models"
"github.com/kubescape/kubescape/v3/core/cautils"
"github.com/kubescape/kubescape/v3/core/pkg/resultshandling/printer"
)
@@ -17,6 +18,9 @@ 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) {
}

View File

@@ -2,7 +2,7 @@ package printer
import (
v5 "github.com/anchore/grype/grype/db/v5"
"github.com/anchore/grype/grype/match"
"github.com/anchore/grype/grype/presenter/models"
"github.com/kubescape/k8s-interface/workloadinterface"
"github.com/kubescape/kubescape/v3/core/cautils"
"github.com/kubescape/kubescape/v3/core/pkg/resultshandling/printer/v2/prettyprinter/tableprinter/imageprinter"
@@ -103,39 +103,39 @@ func setSeverityToSummaryMap(cves []imageprinter.CVE, mapSeverityToSummary map[s
}
}
func setPkgNameToScoreMap(matches match.Matches, pkgScores map[string]*imageprinter.PackageScore) {
for _, m := range matches.Sorted() {
func setPkgNameToScoreMap(matches []models.Match, pkgScores map[string]*imageprinter.PackageScore) {
for i := range matches {
// key is pkg name + version to avoid version conflicts
key := m.Package.Name + m.Package.Version
key := matches[i].Artifact.Name + matches[i].Artifact.Version
if _, ok := pkgScores[key]; !ok {
pkgScores[key] = &imageprinter.PackageScore{
Version: m.Package.Version,
Name: m.Package.Name,
Version: matches[i].Artifact.Version,
Name: matches[i].Artifact.Name,
MapSeverityToCVEsNumber: make(map[string]int, 0),
}
}
if _, ok := pkgScores[key].MapSeverityToCVEsNumber[m.Vulnerability.Metadata.Severity]; !ok {
pkgScores[key].MapSeverityToCVEsNumber[m.Vulnerability.Metadata.Severity] = 1
if _, ok := pkgScores[key].MapSeverityToCVEsNumber[matches[i].Vulnerability.Severity]; !ok {
pkgScores[key].MapSeverityToCVEsNumber[matches[i].Vulnerability.Severity] = 1
} else {
pkgScores[key].MapSeverityToCVEsNumber[m.Vulnerability.Metadata.Severity] += 1
pkgScores[key].MapSeverityToCVEsNumber[matches[i].Vulnerability.Severity] += 1
}
pkgScores[key].Score += utils.ImageSeverityToInt(m.Vulnerability.Metadata.Severity)
pkgScores[key].Score += utils.ImageSeverityToInt(matches[i].Vulnerability.Severity)
}
}
func extractCVEs(matches match.Matches) []imageprinter.CVE {
var CVEs []imageprinter.CVE
for _, m := range matches.Sorted() {
func extractCVEs(matches []models.Match) []imageprinter.CVE {
CVEs := []imageprinter.CVE{}
for i := range matches {
cve := imageprinter.CVE{
ID: m.Vulnerability.Metadata.ID,
Severity: m.Vulnerability.Metadata.Severity,
Package: m.Package.Name,
Version: m.Package.Version,
FixVersions: m.Vulnerability.Fix.Versions,
FixedState: m.Vulnerability.Fix.State.String(),
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)
}

View File

@@ -4,9 +4,7 @@ import (
"testing"
v5 "github.com/anchore/grype/grype/db/v5"
"github.com/anchore/grype/grype/match"
"github.com/anchore/grype/grype/pkg"
"github.com/anchore/grype/grype/vulnerability"
"github.com/anchore/grype/grype/presenter/models"
"github.com/kubescape/kubescape/v3/core/pkg/resultshandling/printer/v2/prettyprinter/tableprinter/imageprinter"
"github.com/stretchr/testify/assert"
)
@@ -14,30 +12,29 @@ import (
func TestExtractCVEs(t *testing.T) {
tests := []struct {
name string
matches match.Matches
matches []models.Match
want []imageprinter.CVE
}{
{
name: "single vuln",
matches: match.NewMatches([]match.Match{
matches: []models.Match{
{
Package: pkg.Package{
ID: "1",
Artifact: models.Package{
Name: "foo",
Version: "1.2.3",
},
Vulnerability: vulnerability.Vulnerability{
Metadata: &vulnerability.Metadata{
Vulnerability: models.Vulnerability{
VulnerabilityMetadata: models.VulnerabilityMetadata{
ID: "CVE-2020-1234",
Severity: "High",
},
Fix: vulnerability.Fix{
Fix: models.Fix{
Versions: []string{"1.2.3"},
State: "Fixed",
},
},
},
}...),
},
want: []imageprinter.CVE{
{
ID: "CVE-2020-1234",
@@ -51,59 +48,56 @@ func TestExtractCVEs(t *testing.T) {
},
{
name: "multiple vulns",
matches: match.NewMatches([]match.Match{
matches: []models.Match{
{
Package: pkg.Package{
ID: "1",
Artifact: models.Package{
Name: "foo",
Version: "1.2.3",
},
Vulnerability: vulnerability.Vulnerability{
Metadata: &vulnerability.Metadata{
Vulnerability: models.Vulnerability{
VulnerabilityMetadata: models.VulnerabilityMetadata{
ID: "CVE-2020-1234",
Severity: "High",
},
Fix: vulnerability.Fix{
Fix: models.Fix{
Versions: []string{"1.2.3"},
State: "Fixed",
},
},
},
{
Package: pkg.Package{
ID: "2",
Artifact: models.Package{
Name: "test",
Version: "1",
},
Vulnerability: vulnerability.Vulnerability{
Metadata: &vulnerability.Metadata{
Vulnerability: models.Vulnerability{
VulnerabilityMetadata: models.VulnerabilityMetadata{
ID: "CVE-2020-1235",
Severity: "Critical",
},
Fix: vulnerability.Fix{
Fix: models.Fix{
Versions: []string{"1"},
State: "Fixed",
},
},
},
{
Package: pkg.Package{
ID: "3",
Artifact: models.Package{
Name: "test2",
Version: "3",
},
Vulnerability: vulnerability.Vulnerability{
Metadata: &vulnerability.Metadata{
Vulnerability: models.Vulnerability{
VulnerabilityMetadata: models.VulnerabilityMetadata{
ID: "CVE-2020-1236",
Severity: "Low",
},
Fix: vulnerability.Fix{
Fix: models.Fix{
Versions: []string{"2", "3", "4"},
State: "Not fixed",
},
},
},
}...),
},
want: []imageprinter.CVE{
{
ID: "CVE-2020-1234",
@@ -133,7 +127,7 @@ func TestExtractCVEs(t *testing.T) {
},
{
name: "empty vulns",
matches: match.NewMatches([]match.Match{}...),
matches: []models.Match{},
want: []imageprinter.CVE{},
},
}
@@ -177,26 +171,25 @@ func TestExtractCVEs(t *testing.T) {
func TestSetPkgNameToScoreMap(t *testing.T) {
tests := []struct {
name string
matches match.Matches
matches []models.Match
originalMap map[string]*imageprinter.PackageScore
want map[string]*imageprinter.PackageScore
}{
{
name: "single package",
matches: match.NewMatches([]match.Match{
matches: []models.Match{
{
Package: pkg.Package{
ID: "1",
Artifact: models.Package{
Name: "foo",
Version: "1.2.3",
},
Vulnerability: vulnerability.Vulnerability{
Metadata: &vulnerability.Metadata{
Vulnerability: models.Vulnerability{
VulnerabilityMetadata: models.VulnerabilityMetadata{
Severity: "High",
},
},
},
}...),
},
want: map[string]*imageprinter.PackageScore{
"foo1.2.3": {
Name: "foo",
@@ -210,44 +203,41 @@ func TestSetPkgNameToScoreMap(t *testing.T) {
},
{
name: "multiple packages - different versions",
matches: match.NewMatches([]match.Match{
matches: []models.Match{
{
Package: pkg.Package{
ID: "1",
Artifact: models.Package{
Name: "pkg1",
Version: "version1",
},
Vulnerability: vulnerability.Vulnerability{
Metadata: &vulnerability.Metadata{
Vulnerability: models.Vulnerability{
VulnerabilityMetadata: models.VulnerabilityMetadata{
Severity: "Critical",
},
},
},
{
Package: pkg.Package{
ID: "2",
Artifact: models.Package{
Name: "pkg2",
Version: "1.2",
},
Vulnerability: vulnerability.Vulnerability{
Metadata: &vulnerability.Metadata{
Vulnerability: models.Vulnerability{
VulnerabilityMetadata: models.VulnerabilityMetadata{
Severity: "Low",
},
},
},
{
Package: pkg.Package{
ID: "3",
Artifact: models.Package{
Name: "pkg3",
Version: "1.2.3",
},
Vulnerability: vulnerability.Vulnerability{
Metadata: &vulnerability.Metadata{
Vulnerability: models.Vulnerability{
VulnerabilityMetadata: models.VulnerabilityMetadata{
Severity: "High",
},
},
},
}...),
},
want: map[string]*imageprinter.PackageScore{
"pkg1version1": {
Name: "pkg1",
@@ -277,80 +267,74 @@ func TestSetPkgNameToScoreMap(t *testing.T) {
},
{
name: "multiple packages - mixed versions",
matches: match.NewMatches([]match.Match{
matches: []models.Match{
{
Package: pkg.Package{
ID: "1",
Artifact: models.Package{
Name: "pkg1",
Version: "version1",
},
Vulnerability: vulnerability.Vulnerability{
Metadata: &vulnerability.Metadata{
Vulnerability: models.Vulnerability{
VulnerabilityMetadata: models.VulnerabilityMetadata{
Severity: "High",
},
},
},
{
Package: pkg.Package{
ID: "2",
Artifact: models.Package{
Name: "pkg1",
Version: "version1",
},
Vulnerability: vulnerability.Vulnerability{
Metadata: &vulnerability.Metadata{
Vulnerability: models.Vulnerability{
VulnerabilityMetadata: models.VulnerabilityMetadata{
Severity: "High",
},
},
},
{
Package: pkg.Package{
ID: "3",
Artifact: models.Package{
Name: "pkg1",
Version: "version2",
},
Vulnerability: vulnerability.Vulnerability{
Metadata: &vulnerability.Metadata{
Vulnerability: models.Vulnerability{
VulnerabilityMetadata: models.VulnerabilityMetadata{
Severity: "Critical",
},
},
},
{
Package: pkg.Package{
ID: "4",
Artifact: models.Package{
Name: "pkg3",
Version: "1.2",
},
Vulnerability: vulnerability.Vulnerability{
Metadata: &vulnerability.Metadata{
Vulnerability: models.Vulnerability{
VulnerabilityMetadata: models.VulnerabilityMetadata{
Severity: "Medium",
},
},
},
{
Package: pkg.Package{
ID: "5",
Artifact: models.Package{
Name: "pkg3",
Version: "1.2",
},
Vulnerability: vulnerability.Vulnerability{
Metadata: &vulnerability.Metadata{
Vulnerability: models.Vulnerability{
VulnerabilityMetadata: models.VulnerabilityMetadata{
Severity: "Low",
},
},
},
{
Package: pkg.Package{
ID: "6",
Artifact: models.Package{
Name: "pkg4",
Version: "1.2.3",
},
Vulnerability: vulnerability.Vulnerability{
Metadata: &vulnerability.Metadata{
Vulnerability: models.Vulnerability{
VulnerabilityMetadata: models.VulnerabilityMetadata{
Severity: "High",
},
},
},
}...),
},
want: map[string]*imageprinter.PackageScore{
"pkg1version1": {
Name: "pkg1",
@@ -389,49 +373,46 @@ func TestSetPkgNameToScoreMap(t *testing.T) {
},
{
name: "empty packages",
matches: match.NewMatches(),
matches: []models.Match{},
want: map[string]*imageprinter.PackageScore{},
},
{
name: "original map not empty",
matches: match.NewMatches([]match.Match{
matches: []models.Match{
{
Package: pkg.Package{
ID: "1",
Artifact: models.Package{
Name: "pkg1",
Version: "version2",
},
Vulnerability: vulnerability.Vulnerability{
Metadata: &vulnerability.Metadata{
Vulnerability: models.Vulnerability{
VulnerabilityMetadata: models.VulnerabilityMetadata{
Severity: "Critical",
},
},
},
{
Package: pkg.Package{
ID: "2",
Artifact: models.Package{
Name: "pkg1",
Version: "version1",
},
Vulnerability: vulnerability.Vulnerability{
Metadata: &vulnerability.Metadata{
Vulnerability: models.Vulnerability{
VulnerabilityMetadata: models.VulnerabilityMetadata{
Severity: "High",
},
},
},
{
Package: pkg.Package{
ID: "3",
Artifact: models.Package{
Name: "pkg1",
Version: "version1",
},
Vulnerability: vulnerability.Vulnerability{
Metadata: &vulnerability.Metadata{
Vulnerability: models.Vulnerability{
VulnerabilityMetadata: models.VulnerabilityMetadata{
Severity: "High",
},
},
},
}...),
},
originalMap: map[string]*imageprinter.PackageScore{
"pkg41.2.3": {
Name: "pkg4",
@@ -471,44 +452,41 @@ func TestSetPkgNameToScoreMap(t *testing.T) {
},
{
name: "original map with same package",
matches: match.NewMatches([]match.Match{
matches: []models.Match{
{
Package: pkg.Package{
ID: "1",
Artifact: models.Package{
Name: "pkg1",
Version: "version2",
},
Vulnerability: vulnerability.Vulnerability{
Metadata: &vulnerability.Metadata{
Vulnerability: models.Vulnerability{
VulnerabilityMetadata: models.VulnerabilityMetadata{
Severity: "Critical",
},
},
},
{
Package: pkg.Package{
ID: "2",
Artifact: models.Package{
Name: "pkg1",
Version: "version1",
},
Vulnerability: vulnerability.Vulnerability{
Metadata: &vulnerability.Metadata{
Vulnerability: models.Vulnerability{
VulnerabilityMetadata: models.VulnerabilityMetadata{
Severity: "High",
},
},
},
{
Package: pkg.Package{
ID: "3",
Artifact: models.Package{
Name: "pkg1",
Version: "version1",
},
Vulnerability: vulnerability.Vulnerability{
Metadata: &vulnerability.Metadata{
Vulnerability: models.Vulnerability{
VulnerabilityMetadata: models.VulnerabilityMetadata{
Severity: "High",
},
},
},
}...),
},
originalMap: map[string]*imageprinter.PackageScore{
"pkg1version1": {
Name: "pkg1",
@@ -540,37 +518,37 @@ func TestSetPkgNameToScoreMap(t *testing.T) {
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if tt.originalMap == nil {
tt.originalMap = make(map[string]*imageprinter.PackageScore)
for i := range tests {
t.Run(tests[i].name, func(t *testing.T) {
if tests[i].originalMap == nil {
tests[i].originalMap = make(map[string]*imageprinter.PackageScore)
}
setPkgNameToScoreMap(tt.matches, tt.originalMap)
if len(tt.originalMap) == 0 {
assert.Equal(t, tt.want, tt.originalMap)
setPkgNameToScoreMap(tests[i].matches, tests[i].originalMap)
if len(tests[i].originalMap) == 0 {
assert.Equal(t, tests[i].want, tests[i].originalMap)
return
}
if len(tt.originalMap) != len(tt.want) {
t.Errorf("%s failed for length, got = %v, want %v", tt.name, len(tt.originalMap), len(tt.want))
if len(tests[i].originalMap) != len(tests[i].want) {
t.Errorf("%s failed for length, got = %v, want %v", tests[i].name, len(tests[i].originalMap), len(tests[i].want))
}
for k := range tt.originalMap {
if tt.originalMap[k].Score != tt.want[k].Score {
t.Errorf("%s failed for score, got = %v, want %v", tt.name, tt.want[k].Score, tt.originalMap[k].Score)
for k := range tests[i].originalMap {
if tests[i].originalMap[k].Score != tests[i].want[k].Score {
t.Errorf("%s failed for score, got = %v, want %v", tests[i].name, tests[i].want[k].Score, tests[i].originalMap[k].Score)
}
if tt.originalMap[k].Version != tt.want[k].Version {
t.Errorf("%s failed for version, got = %v, want %v", tt.name, tt.want[k].Version, tt.originalMap[k].Version)
if tests[i].originalMap[k].Version != tests[i].want[k].Version {
t.Errorf("%s failed for version, got = %v, want %v", tests[i].name, tests[i].want[k].Version, tests[i].originalMap[k].Version)
}
if tt.originalMap[k].Name != tt.want[k].Name {
t.Errorf("%s failed for name, got = %v, want %v", tt.name, tt.want[k].Name, tt.originalMap[k].Name)
if tests[i].originalMap[k].Name != tests[i].want[k].Name {
t.Errorf("%s failed for name, got = %v, want %v", tests[i].name, tests[i].want[k].Name, tests[i].originalMap[k].Name)
}
for s := range tt.originalMap[k].MapSeverityToCVEsNumber {
if tt.originalMap[k].MapSeverityToCVEsNumber[s] != tt.want[k].MapSeverityToCVEsNumber[s] {
t.Errorf("%s failed for severity %s, got = %v, want %v", tt.name, s, tt.want[k].MapSeverityToCVEsNumber[s], tt.originalMap[k].MapSeverityToCVEsNumber[s])
for s := range tests[i].originalMap[k].MapSeverityToCVEsNumber {
if tests[i].originalMap[k].MapSeverityToCVEsNumber[s] != tests[i].want[k].MapSeverityToCVEsNumber[s] {
t.Errorf("%s failed for severity %s, got = %v, want %v", tests[i].name, s, tests[i].want[k].MapSeverityToCVEsNumber[s], tests[i].originalMap[k].MapSeverityToCVEsNumber[s])
}
}
}

View File

@@ -75,7 +75,7 @@ func (rh *ResultsHandler) GetResults() *reporthandlingv2.PostureReport {
}
// HandleResults handles all necessary actions for the scan results
func (rh *ResultsHandler) HandleResults(ctx context.Context, scanInfo *cautils.ScanInfo) error {
func (rh *ResultsHandler) HandleResults(ctx context.Context) error {
// Display scan results in the UI first to give immediate value.
rh.UiPrinter.ActionPrint(ctx, rh.ScanData, rh.ImageScanData)
@@ -92,7 +92,7 @@ func (rh *ResultsHandler) HandleResults(ctx context.Context, scanInfo *cautils.S
// We should submit only after printing results, so a user can see
// results at all times, even if submission fails
if rh.ReporterObj != nil && scanInfo.Submit {
if rh.ReporterObj != nil {
if err := rh.ReporterObj.Submit(ctx, rh.ScanData); err != nil {
return err
}

View File

@@ -50,7 +50,7 @@ func TestResultsHandlerHandleResultsPrintsResultsToUI(t *testing.T) {
rh := NewResultsHandler(reporter, printers, uiPrinter)
rh.SetData(fakeScanData)
err := rh.HandleResults(context.TODO(), &cautils.ScanInfo{})
err := rh.HandleResults(context.TODO())
assert.NoError(t, err)
want := 1

View File

@@ -28,18 +28,18 @@ Kubescape security posture overview for cluster: minikube
In this overview, Kubescape shows you a summary of your cluster security posture, including the number of users who can perform administrative actions. For each result greater than 0, you should evaluate its need, and then define an exception to allow it. This baseline can be used to detect drift in future.
Control plane
────┬─────────────────────────────────────┬──────────────────────────────────────────────╮
│ │ Control Name │ Docs
├────┼─────────────────────────────────────┼──────────────────────────────────────────────
│ ✅ │ API server insecure port is enabled │ https://kubescape.io/docs/controls/c-0005/
│ ❌ │ Anonymous access enabled │ https://kubescape.io/docs/controls/c-0262/
│ ❌ │ Audit logs enabled │ https://kubescape.io/docs/controls/c-0067/
│ ✅ │ RBAC enabled │ https://kubescape.io/docs/controls/c-0088/
│ ❌ │ Secret/etcd encryption enabled │ https://kubescape.io/docs/controls/c-0066/
────┴─────────────────────────────────────┴──────────────────────────────────────────────╯
────┬─────────────────────────────────────┬────────────────────────────────────
│ │ Control Name │ Docs │
├────┼─────────────────────────────────────┼────────────────────────────────────┤
│ ✅ │ API server insecure port is enabled │ https://hub.armosec.io/docs/c-0005 │
│ ❌ │ Anonymous access enabled │ https://hub.armosec.io/docs/c-0262 │
│ ❌ │ Audit logs enabled │ https://hub.armosec.io/docs/c-0067 │
│ ✅ │ RBAC enabled │ https://hub.armosec.io/docs/c-0088 │
│ ❌ │ Secret/etcd encryption enabled │ https://hub.armosec.io/docs/c-0066 │
────┴─────────────────────────────────────┴────────────────────────────────────
Access control
─────────────────────────────────────────────────┬───────────┬────────────────────────────────────
─────────────────────────────────────────────────┬───────────┬────────────────────────────────────
│ Control Name │ Resources │ View Details │
├─────────────────────────────────────────────────┼───────────┼────────────────────────────────────┤
│ Cluster-admin binding │ 1 │ $ kubescape scan control C-0035 -v │
@@ -51,24 +51,24 @@ Access control
│ Portforwarding privileges │ 1 │ $ kubescape scan control C-0063 -v │
│ Validate admission controller (mutating)0 │ $ kubescape scan control C-0039 -v │
│ Validate admission controller (validating)0 │ $ kubescape scan control C-0036 -v │
─────────────────────────────────────────────────┴───────────┴────────────────────────────────────
─────────────────────────────────────────────────┴───────────┴────────────────────────────────────
Secrets
─────────────────────────────────────────────────┬───────────┬────────────────────────────────────
─────────────────────────────────────────────────┬───────────┬────────────────────────────────────
│ Control Name │ Resources │ View Details │
├─────────────────────────────────────────────────┼───────────┼────────────────────────────────────┤
│ Applications credentials in configuration files │ 1 │ $ kubescape scan control C-0012 -v │
─────────────────────────────────────────────────┴───────────┴────────────────────────────────────
─────────────────────────────────────────────────┴───────────┴────────────────────────────────────
Network
────────────────────────┬───────────┬────────────────────────────────────
────────────────────────┬───────────┬────────────────────────────────────
│ Control Name │ Resources │ View Details │
├────────────────────────┼───────────┼────────────────────────────────────┤
│ Missing network policy │ 13 │ $ kubescape scan control C-0260 -v │
────────────────────────┴───────────┴────────────────────────────────────
────────────────────────┴───────────┴────────────────────────────────────
Workload
─────────────────────────┬───────────┬────────────────────────────────────
─────────────────────────┬───────────┬────────────────────────────────────
│ Control Name │ Resources │ View Details │
├─────────────────────────┼───────────┼────────────────────────────────────┤
│ Host PID/IPC privileges │ 2 │ $ kubescape scan control C-0038 -v │
@@ -76,7 +76,7 @@ Workload
│ HostPath mount │ 1 │ $ kubescape scan control C-0048 -v │
│ Non-root containers │ 6 │ $ kubescape scan control C-0013 -v │
│ Privileged container │ 1 │ $ kubescape scan control C-0057 -v │
─────────────────────────┴───────────┴────────────────────────────────────
─────────────────────────┴───────────┴────────────────────────────────────
Highest-stake workloads
────────────────────────
@@ -144,7 +144,7 @@ kubescape scan framework mitre
```
#### Scan a control
Scan for a specific control, using the control name or control ID. [See the list of controls](https://kubescape.io/docs/controls/).
Scan for a specific control, using the control name or control ID. [See the list of controls](https://hub.armosec.io/docs/controls?utm_source=github&utm_medium=repository).
```bash
kubescape scan control c-0005 -v
@@ -331,7 +331,7 @@ kubescape scan image nginx:1.19.6 -v
### Scan periodically using Helm
We publish [a Helm chart](https://github.com/kubescape/helm-charts) for our in-cluster components. [Please follow the instructions here](https://kubescape.io/docs/install-operator/)
We publish [a Helm chart](https://github.com/kubescape/helm-charts) for our in-cluster components. [Please follow the instructions here](https://hub.armosec.io/docs/installation-of-armo-in-cluster?utm_source=github&utm_medium=repository)
### VS Code Extension

View File

@@ -708,14 +708,14 @@
<tr>
<td class="resourceSeverityCell">Low</td>
<td class="resourceNameCell">Network mapping</td>
<td class="resourceURLCell"><a href="https://kubescape.io/docs/controls/c-0049/">C-0049</a></td>
<td class="resourceURLCell"><a href="https://hub.armosec.io/docs/c-0049">C-0049</a></td>
<td class="resourceRemediationCell"></td>
</tr>
<tr>
<td class="resourceSeverityCell">Medium</td>
<td class="resourceNameCell">Cluster internal networking</td>
<td class="resourceURLCell"><a href="https://kubescape.io/docs/controls/c-0054/">C-0054</a></td>
<td class="resourceURLCell"><a href="https://hub.armosec.io/docs/c-0054">C-0054</a></td>
<td class="resourceRemediationCell"></td>
</tr>
@@ -742,77 +742,77 @@
<tr>
<td class="resourceSeverityCell">Medium</td>
<td class="resourceNameCell">Allow privilege escalation</td>
<td class="resourceURLCell"><a href="https://kubescape.io/docs/controls/c-0016/">C-0016</a></td>
<td class="resourceURLCell"><a href="https://hub.armosec.io/docs/c-0016">C-0016</a></td>
<td class="resourceRemediationCell"> <p>spec.template.spec.containers[0].securityContext.allowPrivilegeEscalation=false</p> </td>
</tr>
<tr>
<td class="resourceSeverityCell">Medium</td>
<td class="resourceNameCell">Ingress and Egress blocked</td>
<td class="resourceURLCell"><a href="https://kubescape.io/docs/controls/c-0030/">C-0030</a></td>
<td class="resourceURLCell"><a href="https://hub.armosec.io/docs/c-0030">C-0030</a></td>
<td class="resourceRemediationCell"></td>
</tr>
<tr>
<td class="resourceSeverityCell">High</td>
<td class="resourceNameCell">Resource limits</td>
<td class="resourceURLCell"><a href="https://kubescape.io/docs/controls/c-0009/">C-0009</a></td>
<td class="resourceURLCell"><a href="https://hub.armosec.io/docs/c-0009">C-0009</a></td>
<td class="resourceRemediationCell"> <p>spec.template.spec.containers[0].resources.limits.cpu=YOUR_VALUE</p> <p>spec.template.spec.containers[0].resources.limits.memory=YOUR_VALUE</p> </td>
</tr>
<tr>
<td class="resourceSeverityCell">Low</td>
<td class="resourceNameCell">Configured readiness probe</td>
<td class="resourceURLCell"><a href="https://kubescape.io/docs/controls/c-0018/">C-0018</a></td>
<td class="resourceURLCell"><a href="https://hub.armosec.io/docs/c-0018">C-0018</a></td>
<td class="resourceRemediationCell"> <p>spec.template.spec.containers[0].readinessProbe=YOUR_VALUE</p> </td>
</tr>
<tr>
<td class="resourceSeverityCell">Medium</td>
<td class="resourceNameCell">Non-root containers</td>
<td class="resourceURLCell"><a href="https://kubescape.io/docs/controls/c-0013/">C-0013</a></td>
<td class="resourceURLCell"><a href="https://hub.armosec.io/docs/c-0013">C-0013</a></td>
<td class="resourceRemediationCell"> <p>spec.template.spec.containers[0].securityContext.runAsNonRoot=true</p> <p>spec.template.spec.containers[0].securityContext.allowPrivilegeEscalation=false</p> </td>
</tr>
<tr>
<td class="resourceSeverityCell">Medium</td>
<td class="resourceNameCell">Automatic mapping of service account</td>
<td class="resourceURLCell"><a href="https://kubescape.io/docs/controls/c-0034/">C-0034</a></td>
<td class="resourceURLCell"><a href="https://hub.armosec.io/docs/c-0034">C-0034</a></td>
<td class="resourceRemediationCell"> <p>spec.template.spec.automountServiceAccountToken=false</p> </td>
</tr>
<tr>
<td class="resourceSeverityCell">Medium</td>
<td class="resourceNameCell">Linux hardening</td>
<td class="resourceURLCell"><a href="https://kubescape.io/docs/controls/c-0055/">C-0055</a></td>
<td class="resourceURLCell"><a href="https://hub.armosec.io/docs/c-0055">C-0055</a></td>
<td class="resourceRemediationCell"> <p>spec.template.spec.containers[0].securityContext.seccompProfile=YOUR_VALUE</p> <p>spec.template.spec.containers[0].securityContext.seLinuxOptions=YOUR_VALUE</p> <p>spec.template.spec.containers[0].securityContext.capabilities.drop[0]=YOUR_VALUE</p> </td>
</tr>
<tr>
<td class="resourceSeverityCell">Medium</td>
<td class="resourceNameCell">Configured liveness probe</td>
<td class="resourceURLCell"><a href="https://kubescape.io/docs/controls/c-0056/">C-0056</a></td>
<td class="resourceURLCell"><a href="https://hub.armosec.io/docs/c-0056">C-0056</a></td>
<td class="resourceRemediationCell"> <p>spec.template.spec.containers[0].livenessProbe=YOUR_VALUE</p> </td>
</tr>
<tr>
<td class="resourceSeverityCell">Low</td>
<td class="resourceNameCell">K8s common labels usage</td>
<td class="resourceURLCell"><a href="https://kubescape.io/docs/controls/c-0077/">C-0077</a></td>
<td class="resourceURLCell"><a href="https://hub.armosec.io/docs/c-0077">C-0077</a></td>
<td class="resourceRemediationCell"> <p>metadata.labels=YOUR_VALUE</p> <p>spec.template.metadata.labels=YOUR_VALUE</p> </td>
</tr>
<tr>
<td class="resourceSeverityCell">Low</td>
<td class="resourceNameCell">Pods in default namespace</td>
<td class="resourceURLCell"><a href="https://kubescape.io/docs/controls/c-0061/">C-0061</a></td>
<td class="resourceURLCell"><a href="https://hub.armosec.io/docs/c-0061">C-0061</a></td>
<td class="resourceRemediationCell"> <p>metadata.namespace</p> </td>
</tr>
<tr>
<td class="resourceSeverityCell">Low</td>
<td class="resourceNameCell">Immutable container filesystem</td>
<td class="resourceURLCell"><a href="https://kubescape.io/docs/controls/c-0017/">C-0017</a></td>
<td class="resourceURLCell"><a href="https://hub.armosec.io/docs/c-0017">C-0017</a></td>
<td class="resourceRemediationCell"> <p>spec.template.spec.containers[0].securityContext.readOnlyRootFilesystem=true</p> </td>
</tr>
@@ -839,7 +839,7 @@
<tr>
<td class="resourceSeverityCell">Medium</td>
<td class="resourceNameCell">Access container service account</td>
<td class="resourceURLCell"><a href="https://kubescape.io/docs/controls/c-0053/">C-0053</a></td>
<td class="resourceURLCell"><a href="https://hub.armosec.io/docs/c-0053">C-0053</a></td>
<td class="resourceRemediationCell"></td>
</tr>
@@ -866,7 +866,7 @@
<tr>
<td class="resourceSeverityCell">Medium</td>
<td class="resourceNameCell">Automatic mapping of service account</td>
<td class="resourceURLCell"><a href="https://kubescape.io/docs/controls/c-0034/">C-0034</a></td>
<td class="resourceURLCell"><a href="https://hub.armosec.io/docs/c-0034">C-0034</a></td>
<td class="resourceRemediationCell"> <p>automountServiceAccountToken=false</p> </td>
</tr>
@@ -893,77 +893,77 @@
<tr>
<td class="resourceSeverityCell">Medium</td>
<td class="resourceNameCell">Allow privilege escalation</td>
<td class="resourceURLCell"><a href="https://kubescape.io/docs/controls/c-0016/">C-0016</a></td>
<td class="resourceURLCell"><a href="https://hub.armosec.io/docs/c-0016">C-0016</a></td>
<td class="resourceRemediationCell"> <p>spec.template.spec.containers[0].securityContext.allowPrivilegeEscalation=false</p> </td>
</tr>
<tr>
<td class="resourceSeverityCell">Medium</td>
<td class="resourceNameCell">Ingress and Egress blocked</td>
<td class="resourceURLCell"><a href="https://kubescape.io/docs/controls/c-0030/">C-0030</a></td>
<td class="resourceURLCell"><a href="https://hub.armosec.io/docs/c-0030">C-0030</a></td>
<td class="resourceRemediationCell"></td>
</tr>
<tr>
<td class="resourceSeverityCell">High</td>
<td class="resourceNameCell">Resource limits</td>
<td class="resourceURLCell"><a href="https://kubescape.io/docs/controls/c-0009/">C-0009</a></td>
<td class="resourceURLCell"><a href="https://hub.armosec.io/docs/c-0009">C-0009</a></td>
<td class="resourceRemediationCell"> <p>spec.template.spec.containers[0].resources.limits.cpu=YOUR_VALUE</p> <p>spec.template.spec.containers[0].resources.limits.memory=YOUR_VALUE</p> </td>
</tr>
<tr>
<td class="resourceSeverityCell">Low</td>
<td class="resourceNameCell">Configured readiness probe</td>
<td class="resourceURLCell"><a href="https://kubescape.io/docs/controls/c-0018/">C-0018</a></td>
<td class="resourceURLCell"><a href="https://hub.armosec.io/docs/c-0018">C-0018</a></td>
<td class="resourceRemediationCell"> <p>spec.template.spec.containers[0].readinessProbe=YOUR_VALUE</p> </td>
</tr>
<tr>
<td class="resourceSeverityCell">Medium</td>
<td class="resourceNameCell">Non-root containers</td>
<td class="resourceURLCell"><a href="https://kubescape.io/docs/controls/c-0013/">C-0013</a></td>
<td class="resourceURLCell"><a href="https://hub.armosec.io/docs/c-0013">C-0013</a></td>
<td class="resourceRemediationCell"> <p>spec.template.spec.containers[0].securityContext.runAsNonRoot=true</p> <p>spec.template.spec.containers[0].securityContext.allowPrivilegeEscalation=false</p> </td>
</tr>
<tr>
<td class="resourceSeverityCell">Medium</td>
<td class="resourceNameCell">Automatic mapping of service account</td>
<td class="resourceURLCell"><a href="https://kubescape.io/docs/controls/c-0034/">C-0034</a></td>
<td class="resourceURLCell"><a href="https://hub.armosec.io/docs/c-0034">C-0034</a></td>
<td class="resourceRemediationCell"> <p>spec.template.spec.automountServiceAccountToken=false</p> </td>
</tr>
<tr>
<td class="resourceSeverityCell">Medium</td>
<td class="resourceNameCell">Linux hardening</td>
<td class="resourceURLCell"><a href="https://kubescape.io/docs/controls/c-0055/">C-0055</a></td>
<td class="resourceURLCell"><a href="https://hub.armosec.io/docs/c-0055">C-0055</a></td>
<td class="resourceRemediationCell"> <p>spec.template.spec.containers[0].securityContext.seccompProfile=YOUR_VALUE</p> <p>spec.template.spec.containers[0].securityContext.seLinuxOptions=YOUR_VALUE</p> <p>spec.template.spec.containers[0].securityContext.capabilities.drop[0]=YOUR_VALUE</p> </td>
</tr>
<tr>
<td class="resourceSeverityCell">Medium</td>
<td class="resourceNameCell">Configured liveness probe</td>
<td class="resourceURLCell"><a href="https://kubescape.io/docs/controls/c-0056/">C-0056</a></td>
<td class="resourceURLCell"><a href="https://hub.armosec.io/docs/c-0056">C-0056</a></td>
<td class="resourceRemediationCell"> <p>spec.template.spec.containers[0].livenessProbe=YOUR_VALUE</p> </td>
</tr>
<tr>
<td class="resourceSeverityCell">Low</td>
<td class="resourceNameCell">K8s common labels usage</td>
<td class="resourceURLCell"><a href="https://kubescape.io/docs/controls/c-0077/">C-0077</a></td>
<td class="resourceURLCell"><a href="https://hub.armosec.io/docs/c-0077">C-0077</a></td>
<td class="resourceRemediationCell"> <p>metadata.labels=YOUR_VALUE</p> <p>spec.template.metadata.labels=YOUR_VALUE</p> </td>
</tr>
<tr>
<td class="resourceSeverityCell">Low</td>
<td class="resourceNameCell">Pods in default namespace</td>
<td class="resourceURLCell"><a href="https://kubescape.io/docs/controls/c-0061/">C-0061</a></td>
<td class="resourceURLCell"><a href="https://hub.armosec.io/docs/c-0061">C-0061</a></td>
<td class="resourceRemediationCell"> <p>metadata.namespace</p> </td>
</tr>
<tr>
<td class="resourceSeverityCell">Low</td>
<td class="resourceNameCell">Immutable container filesystem</td>
<td class="resourceURLCell"><a href="https://kubescape.io/docs/controls/c-0017/">C-0017</a></td>
<td class="resourceURLCell"><a href="https://hub.armosec.io/docs/c-0017">C-0017</a></td>
<td class="resourceRemediationCell"> <p>spec.template.spec.containers[0].securityContext.readOnlyRootFilesystem=true</p> </td>
</tr>
@@ -990,21 +990,21 @@
<tr>
<td class="resourceSeverityCell">Medium</td>
<td class="resourceNameCell">Audit logs enabled</td>
<td class="resourceURLCell"><a href="https://kubescape.io/docs/controls/c-0067/">C-0067</a></td>
<td class="resourceURLCell"><a href="https://hub.armosec.io/docs/c-0067">C-0067</a></td>
<td class="resourceRemediationCell"> <p>spec.containers[0].command</p> </td>
</tr>
<tr>
<td class="resourceSeverityCell">Low</td>
<td class="resourceNameCell">PSP enabled</td>
<td class="resourceURLCell"><a href="https://kubescape.io/docs/controls/c-0068/">C-0068</a></td>
<td class="resourceURLCell"><a href="https://hub.armosec.io/docs/c-0068">C-0068</a></td>
<td class="resourceRemediationCell"> <p>spec.containers[0].command[5]</p> </td>
</tr>
<tr>
<td class="resourceSeverityCell">Medium</td>
<td class="resourceNameCell">Secret/ETCD encryption enabled</td>
<td class="resourceURLCell"><a href="https://kubescape.io/docs/controls/c-0066/">C-0066</a></td>
<td class="resourceURLCell"><a href="https://hub.armosec.io/docs/c-0066">C-0066</a></td>
<td class="resourceRemediationCell"> <p>spec.containers[0].command</p> </td>
</tr>
@@ -1031,14 +1031,14 @@
<tr>
<td class="resourceSeverityCell">Medium</td>
<td class="resourceNameCell">Data Destruction</td>
<td class="resourceURLCell"><a href="https://kubescape.io/docs/controls/c-0007/">C-0007</a></td>
<td class="resourceURLCell"><a href="https://hub.armosec.io/docs/c-0007">C-0007</a></td>
<td class="resourceRemediationCell"> <p>relatedObjects[1].rules[1].resources[1]</p> <p>relatedObjects[1].rules[1].verbs[0]</p> <p>relatedObjects[1].rules[1].apiGroups[0]</p> <p>relatedObjects[1].rules[1].apiGroups[1]</p> <p>relatedObjects[0].subjects[0]</p> <p>relatedObjects[0].roleRef.name</p> </td>
</tr>
<tr>
<td class="resourceSeverityCell">High</td>
<td class="resourceNameCell">List Kubernetes secrets</td>
<td class="resourceURLCell"><a href="https://kubescape.io/docs/controls/c-0015/">C-0015</a></td>
<td class="resourceURLCell"><a href="https://hub.armosec.io/docs/c-0015">C-0015</a></td>
<td class="resourceRemediationCell"> <p>relatedObjects[1].rules[0].resources[0]</p> <p>relatedObjects[1].rules[0].verbs[0]</p> <p>relatedObjects[1].rules[0].verbs[1]</p> <p>relatedObjects[1].rules[0].verbs[3]</p> <p>relatedObjects[1].rules[0].apiGroups[0]</p> <p>relatedObjects[0].subjects[0]</p> <p>relatedObjects[0].roleRef.name</p> </td>
</tr>
@@ -1065,7 +1065,7 @@
<tr>
<td class="resourceSeverityCell">Medium</td>
<td class="resourceNameCell">Automatic mapping of service account</td>
<td class="resourceURLCell"><a href="https://kubescape.io/docs/controls/c-0034/">C-0034</a></td>
<td class="resourceURLCell"><a href="https://hub.armosec.io/docs/c-0034">C-0034</a></td>
<td class="resourceRemediationCell"> <p>automountServiceAccountToken=false</p> </td>
</tr>
@@ -1092,56 +1092,56 @@
<tr>
<td class="resourceSeverityCell">Medium</td>
<td class="resourceNameCell">Ingress and Egress blocked</td>
<td class="resourceURLCell"><a href="https://kubescape.io/docs/controls/c-0030/">C-0030</a></td>
<td class="resourceURLCell"><a href="https://hub.armosec.io/docs/c-0030">C-0030</a></td>
<td class="resourceRemediationCell"></td>
</tr>
<tr>
<td class="resourceSeverityCell">High</td>
<td class="resourceNameCell">Resource limits</td>
<td class="resourceURLCell"><a href="https://kubescape.io/docs/controls/c-0009/">C-0009</a></td>
<td class="resourceURLCell"><a href="https://hub.armosec.io/docs/c-0009">C-0009</a></td>
<td class="resourceRemediationCell"> <p>spec.jobTemplate.spec.template.spec.containers[0].resources.limits.cpu=YOUR_VALUE</p> <p>spec.jobTemplate.spec.template.spec.containers[0].resources.limits.memory=YOUR_VALUE</p> </td>
</tr>
<tr>
<td class="resourceSeverityCell">Low</td>
<td class="resourceNameCell">Configured readiness probe</td>
<td class="resourceURLCell"><a href="https://kubescape.io/docs/controls/c-0018/">C-0018</a></td>
<td class="resourceURLCell"><a href="https://hub.armosec.io/docs/c-0018">C-0018</a></td>
<td class="resourceRemediationCell"> <p>spec.jobTemplate.spec.template.spec.containers[0].readinessProbe=YOUR_VALUE</p> </td>
</tr>
<tr>
<td class="resourceSeverityCell">Low</td>
<td class="resourceNameCell">Kubernetes CronJob</td>
<td class="resourceURLCell"><a href="https://kubescape.io/docs/controls/c-0026/">C-0026</a></td>
<td class="resourceURLCell"><a href="https://hub.armosec.io/docs/c-0026">C-0026</a></td>
<td class="resourceRemediationCell"></td>
</tr>
<tr>
<td class="resourceSeverityCell">Low</td>
<td class="resourceNameCell">Label usage for resources</td>
<td class="resourceURLCell"><a href="https://kubescape.io/docs/controls/c-0076/">C-0076</a></td>
<td class="resourceURLCell"><a href="https://hub.armosec.io/docs/c-0076">C-0076</a></td>
<td class="resourceRemediationCell"> <p>spec.jobTemplate.spec.template.metadata.labels=YOUR_VALUE</p> </td>
</tr>
<tr>
<td class="resourceSeverityCell">Medium</td>
<td class="resourceNameCell">Linux hardening</td>
<td class="resourceURLCell"><a href="https://kubescape.io/docs/controls/c-0055/">C-0055</a></td>
<td class="resourceURLCell"><a href="https://hub.armosec.io/docs/c-0055">C-0055</a></td>
<td class="resourceRemediationCell"> <p>spec.jobTemplate.spec.template.spec.containers[0].securityContext.seccompProfile=YOUR_VALUE</p> <p>spec.jobTemplate.spec.template.spec.containers[0].securityContext.seLinuxOptions=YOUR_VALUE</p> <p>spec.jobTemplate.spec.template.spec.containers[0].securityContext.capabilities.drop[0]=YOUR_VALUE</p> </td>
</tr>
<tr>
<td class="resourceSeverityCell">Medium</td>
<td class="resourceNameCell">Configured liveness probe</td>
<td class="resourceURLCell"><a href="https://kubescape.io/docs/controls/c-0056/">C-0056</a></td>
<td class="resourceURLCell"><a href="https://hub.armosec.io/docs/c-0056">C-0056</a></td>
<td class="resourceRemediationCell"> <p>spec.jobTemplate.spec.template.spec.containers[0].livenessProbe=YOUR_VALUE</p> </td>
</tr>
<tr>
<td class="resourceSeverityCell">Low</td>
<td class="resourceNameCell">K8s common labels usage</td>
<td class="resourceURLCell"><a href="https://kubescape.io/docs/controls/c-0077/">C-0077</a></td>
<td class="resourceURLCell"><a href="https://hub.armosec.io/docs/c-0077">C-0077</a></td>
<td class="resourceRemediationCell"> <p>metadata.labels=YOUR_VALUE</p> <p>spec.jobTemplate.spec.template.metadata.labels=YOUR_VALUE</p> </td>
</tr>
@@ -1168,63 +1168,63 @@
<tr>
<td class="resourceSeverityCell">Medium</td>
<td class="resourceNameCell">Allow privilege escalation</td>
<td class="resourceURLCell"><a href="https://kubescape.io/docs/controls/c-0016/">C-0016</a></td>
<td class="resourceURLCell"><a href="https://hub.armosec.io/docs/c-0016">C-0016</a></td>
<td class="resourceRemediationCell"> <p>spec.jobTemplate.spec.template.spec.containers[0].securityContext.allowPrivilegeEscalation=false</p> </td>
</tr>
<tr>
<td class="resourceSeverityCell">Medium</td>
<td class="resourceNameCell">Ingress and Egress blocked</td>
<td class="resourceURLCell"><a href="https://kubescape.io/docs/controls/c-0030/">C-0030</a></td>
<td class="resourceURLCell"><a href="https://hub.armosec.io/docs/c-0030">C-0030</a></td>
<td class="resourceRemediationCell"></td>
</tr>
<tr>
<td class="resourceSeverityCell">High</td>
<td class="resourceNameCell">Resource limits</td>
<td class="resourceURLCell"><a href="https://kubescape.io/docs/controls/c-0009/">C-0009</a></td>
<td class="resourceURLCell"><a href="https://hub.armosec.io/docs/c-0009">C-0009</a></td>
<td class="resourceRemediationCell"> <p>spec.jobTemplate.spec.template.spec.containers[0].resources.limits.cpu=YOUR_VALUE</p> <p>spec.jobTemplate.spec.template.spec.containers[0].resources.limits.memory=YOUR_VALUE</p> </td>
</tr>
<tr>
<td class="resourceSeverityCell">Low</td>
<td class="resourceNameCell">Configured readiness probe</td>
<td class="resourceURLCell"><a href="https://kubescape.io/docs/controls/c-0018/">C-0018</a></td>
<td class="resourceURLCell"><a href="https://hub.armosec.io/docs/c-0018">C-0018</a></td>
<td class="resourceRemediationCell"> <p>spec.jobTemplate.spec.template.spec.containers[0].readinessProbe=YOUR_VALUE</p> </td>
</tr>
<tr>
<td class="resourceSeverityCell">Low</td>
<td class="resourceNameCell">Kubernetes CronJob</td>
<td class="resourceURLCell"><a href="https://kubescape.io/docs/controls/c-0026/">C-0026</a></td>
<td class="resourceURLCell"><a href="https://hub.armosec.io/docs/c-0026">C-0026</a></td>
<td class="resourceRemediationCell"></td>
</tr>
<tr>
<td class="resourceSeverityCell">Medium</td>
<td class="resourceNameCell">Non-root containers</td>
<td class="resourceURLCell"><a href="https://kubescape.io/docs/controls/c-0013/">C-0013</a></td>
<td class="resourceURLCell"><a href="https://hub.armosec.io/docs/c-0013">C-0013</a></td>
<td class="resourceRemediationCell"> <p>spec.jobTemplate.spec.template.spec.containers[0].securityContext.runAsNonRoot=true</p> <p>spec.jobTemplate.spec.template.spec.containers[0].securityContext.allowPrivilegeEscalation=false</p> </td>
</tr>
<tr>
<td class="resourceSeverityCell">Medium</td>
<td class="resourceNameCell">Linux hardening</td>
<td class="resourceURLCell"><a href="https://kubescape.io/docs/controls/c-0055/">C-0055</a></td>
<td class="resourceURLCell"><a href="https://hub.armosec.io/docs/c-0055">C-0055</a></td>
<td class="resourceRemediationCell"> <p>spec.jobTemplate.spec.template.spec.containers[0].securityContext.seccompProfile=YOUR_VALUE</p> <p>spec.jobTemplate.spec.template.spec.containers[0].securityContext.seLinuxOptions=YOUR_VALUE</p> <p>spec.jobTemplate.spec.template.spec.containers[0].securityContext.capabilities.drop[0]=YOUR_VALUE</p> </td>
</tr>
<tr>
<td class="resourceSeverityCell">Medium</td>
<td class="resourceNameCell">Configured liveness probe</td>
<td class="resourceURLCell"><a href="https://kubescape.io/docs/controls/c-0056/">C-0056</a></td>
<td class="resourceURLCell"><a href="https://hub.armosec.io/docs/c-0056">C-0056</a></td>
<td class="resourceRemediationCell"> <p>spec.jobTemplate.spec.template.spec.containers[0].livenessProbe=YOUR_VALUE</p> </td>
</tr>
<tr>
<td class="resourceSeverityCell">Low</td>
<td class="resourceNameCell">Immutable container filesystem</td>
<td class="resourceURLCell"><a href="https://kubescape.io/docs/controls/c-0017/">C-0017</a></td>
<td class="resourceURLCell"><a href="https://hub.armosec.io/docs/c-0017">C-0017</a></td>
<td class="resourceRemediationCell"> <p>spec.jobTemplate.spec.template.spec.containers[0].securityContext.readOnlyRootFilesystem=true</p> </td>
</tr>
@@ -1251,21 +1251,21 @@
<tr>
<td class="resourceSeverityCell">Medium</td>
<td class="resourceNameCell">Data Destruction</td>
<td class="resourceURLCell"><a href="https://kubescape.io/docs/controls/c-0007/">C-0007</a></td>
<td class="resourceURLCell"><a href="https://hub.armosec.io/docs/c-0007">C-0007</a></td>
<td class="resourceRemediationCell"> <p>relatedObjects[1].rules[1].resources[0]</p> <p>relatedObjects[1].rules[1].verbs[0]</p> <p>relatedObjects[1].rules[1].apiGroups[0]</p> <p>relatedObjects[0].subjects[0]</p> <p>relatedObjects[0].roleRef.name</p> <p>relatedObjects[1].rules[2].resources[1]</p> <p>relatedObjects[1].rules[2].verbs[0]</p> <p>relatedObjects[1].rules[2].apiGroups[0]</p> <p>relatedObjects[0].subjects[0]</p> <p>relatedObjects[0].roleRef.name</p> </td>
</tr>
<tr>
<td class="resourceSeverityCell">Medium</td>
<td class="resourceNameCell">CoreDNS poisoning</td>
<td class="resourceURLCell"><a href="https://kubescape.io/docs/controls/c-0037/">C-0037</a></td>
<td class="resourceURLCell"><a href="https://hub.armosec.io/docs/c-0037">C-0037</a></td>
<td class="resourceRemediationCell"> <p>relatedObjects[1].rules[2].resources[0]</p> <p>relatedObjects[1].rules[2].verbs[0]</p> <p>relatedObjects[1].rules[2].apiGroups[0]</p> <p>relatedObjects[0].subjects[0]</p> <p>relatedObjects[0].roleRef.name</p> </td>
</tr>
<tr>
<td class="resourceSeverityCell">High</td>
<td class="resourceNameCell">List Kubernetes secrets</td>
<td class="resourceURLCell"><a href="https://kubescape.io/docs/controls/c-0015/">C-0015</a></td>
<td class="resourceURLCell"><a href="https://hub.armosec.io/docs/c-0015">C-0015</a></td>
<td class="resourceRemediationCell"> <p>relatedObjects[1].rules[0].resources[0]</p> <p>relatedObjects[1].rules[0].verbs[0]</p> <p>relatedObjects[1].rules[0].verbs[1]</p> <p>relatedObjects[1].rules[0].apiGroups[0]</p> <p>relatedObjects[0].subjects[0]</p> <p>relatedObjects[0].roleRef.name</p> <p>relatedObjects[1].rules[2].resources[1]</p> <p>relatedObjects[1].rules[2].verbs[0]</p> <p>relatedObjects[1].rules[2].apiGroups[0]</p> <p>relatedObjects[0].subjects[0]</p> <p>relatedObjects[0].roleRef.name</p> </td>
</tr>
@@ -1292,56 +1292,56 @@
<tr>
<td class="resourceSeverityCell">Medium</td>
<td class="resourceNameCell">Ingress and Egress blocked</td>
<td class="resourceURLCell"><a href="https://kubescape.io/docs/controls/c-0030/">C-0030</a></td>
<td class="resourceURLCell"><a href="https://hub.armosec.io/docs/c-0030">C-0030</a></td>
<td class="resourceRemediationCell"></td>
</tr>
<tr>
<td class="resourceSeverityCell">High</td>
<td class="resourceNameCell">Resource limits</td>
<td class="resourceURLCell"><a href="https://kubescape.io/docs/controls/c-0009/">C-0009</a></td>
<td class="resourceURLCell"><a href="https://hub.armosec.io/docs/c-0009">C-0009</a></td>
<td class="resourceRemediationCell"> <p>spec.jobTemplate.spec.template.spec.containers[0].resources.limits.cpu=YOUR_VALUE</p> <p>spec.jobTemplate.spec.template.spec.containers[0].resources.limits.memory=YOUR_VALUE</p> </td>
</tr>
<tr>
<td class="resourceSeverityCell">Low</td>
<td class="resourceNameCell">Configured readiness probe</td>
<td class="resourceURLCell"><a href="https://kubescape.io/docs/controls/c-0018/">C-0018</a></td>
<td class="resourceURLCell"><a href="https://hub.armosec.io/docs/c-0018">C-0018</a></td>
<td class="resourceRemediationCell"> <p>spec.jobTemplate.spec.template.spec.containers[0].readinessProbe=YOUR_VALUE</p> </td>
</tr>
<tr>
<td class="resourceSeverityCell">Low</td>
<td class="resourceNameCell">Kubernetes CronJob</td>
<td class="resourceURLCell"><a href="https://kubescape.io/docs/controls/c-0026/">C-0026</a></td>
<td class="resourceURLCell"><a href="https://hub.armosec.io/docs/c-0026">C-0026</a></td>
<td class="resourceRemediationCell"></td>
</tr>
<tr>
<td class="resourceSeverityCell">Low</td>
<td class="resourceNameCell">Label usage for resources</td>
<td class="resourceURLCell"><a href="https://kubescape.io/docs/controls/c-0076/">C-0076</a></td>
<td class="resourceURLCell"><a href="https://hub.armosec.io/docs/c-0076">C-0076</a></td>
<td class="resourceRemediationCell"> <p>spec.jobTemplate.spec.template.metadata.labels=YOUR_VALUE</p> </td>
</tr>
<tr>
<td class="resourceSeverityCell">Medium</td>
<td class="resourceNameCell">Linux hardening</td>
<td class="resourceURLCell"><a href="https://kubescape.io/docs/controls/c-0055/">C-0055</a></td>
<td class="resourceURLCell"><a href="https://hub.armosec.io/docs/c-0055">C-0055</a></td>
<td class="resourceRemediationCell"> <p>spec.jobTemplate.spec.template.spec.containers[0].securityContext.seccompProfile=YOUR_VALUE</p> <p>spec.jobTemplate.spec.template.spec.containers[0].securityContext.seLinuxOptions=YOUR_VALUE</p> <p>spec.jobTemplate.spec.template.spec.containers[0].securityContext.capabilities.drop[0]=YOUR_VALUE</p> </td>
</tr>
<tr>
<td class="resourceSeverityCell">Medium</td>
<td class="resourceNameCell">Configured liveness probe</td>
<td class="resourceURLCell"><a href="https://kubescape.io/docs/controls/c-0056/">C-0056</a></td>
<td class="resourceURLCell"><a href="https://hub.armosec.io/docs/c-0056">C-0056</a></td>
<td class="resourceRemediationCell"> <p>spec.jobTemplate.spec.template.spec.containers[0].livenessProbe=YOUR_VALUE</p> </td>
</tr>
<tr>
<td class="resourceSeverityCell">Low</td>
<td class="resourceNameCell">K8s common labels usage</td>
<td class="resourceURLCell"><a href="https://kubescape.io/docs/controls/c-0077/">C-0077</a></td>
<td class="resourceURLCell"><a href="https://hub.armosec.io/docs/c-0077">C-0077</a></td>
<td class="resourceRemediationCell"> <p>metadata.labels=YOUR_VALUE</p> <p>spec.jobTemplate.spec.template.metadata.labels=YOUR_VALUE</p> </td>
</tr>
@@ -1368,56 +1368,56 @@
<tr>
<td class="resourceSeverityCell">Medium</td>
<td class="resourceNameCell">Ingress and Egress blocked</td>
<td class="resourceURLCell"><a href="https://kubescape.io/docs/controls/c-0030/">C-0030</a></td>
<td class="resourceURLCell"><a href="https://hub.armosec.io/docs/c-0030">C-0030</a></td>
<td class="resourceRemediationCell"></td>
</tr>
<tr>
<td class="resourceSeverityCell">High</td>
<td class="resourceNameCell">Resource limits</td>
<td class="resourceURLCell"><a href="https://kubescape.io/docs/controls/c-0009/">C-0009</a></td>
<td class="resourceURLCell"><a href="https://hub.armosec.io/docs/c-0009">C-0009</a></td>
<td class="resourceRemediationCell"> <p>spec.jobTemplate.spec.template.spec.containers[0].resources.limits.cpu=YOUR_VALUE</p> <p>spec.jobTemplate.spec.template.spec.containers[0].resources.limits.memory=YOUR_VALUE</p> </td>
</tr>
<tr>
<td class="resourceSeverityCell">Low</td>
<td class="resourceNameCell">Configured readiness probe</td>
<td class="resourceURLCell"><a href="https://kubescape.io/docs/controls/c-0018/">C-0018</a></td>
<td class="resourceURLCell"><a href="https://hub.armosec.io/docs/c-0018">C-0018</a></td>
<td class="resourceRemediationCell"> <p>spec.jobTemplate.spec.template.spec.containers[0].readinessProbe=YOUR_VALUE</p> </td>
</tr>
<tr>
<td class="resourceSeverityCell">Low</td>
<td class="resourceNameCell">Kubernetes CronJob</td>
<td class="resourceURLCell"><a href="https://kubescape.io/docs/controls/c-0026/">C-0026</a></td>
<td class="resourceURLCell"><a href="https://hub.armosec.io/docs/c-0026">C-0026</a></td>
<td class="resourceRemediationCell"></td>
</tr>
<tr>
<td class="resourceSeverityCell">Low</td>
<td class="resourceNameCell">Label usage for resources</td>
<td class="resourceURLCell"><a href="https://kubescape.io/docs/controls/c-0076/">C-0076</a></td>
<td class="resourceURLCell"><a href="https://hub.armosec.io/docs/c-0076">C-0076</a></td>
<td class="resourceRemediationCell"> <p>spec.jobTemplate.spec.template.metadata.labels=YOUR_VALUE</p> </td>
</tr>
<tr>
<td class="resourceSeverityCell">Medium</td>
<td class="resourceNameCell">Linux hardening</td>
<td class="resourceURLCell"><a href="https://kubescape.io/docs/controls/c-0055/">C-0055</a></td>
<td class="resourceURLCell"><a href="https://hub.armosec.io/docs/c-0055">C-0055</a></td>
<td class="resourceRemediationCell"> <p>spec.jobTemplate.spec.template.spec.containers[0].securityContext.seccompProfile=YOUR_VALUE</p> <p>spec.jobTemplate.spec.template.spec.containers[0].securityContext.seLinuxOptions=YOUR_VALUE</p> <p>spec.jobTemplate.spec.template.spec.containers[0].securityContext.capabilities.drop[0]=YOUR_VALUE</p> </td>
</tr>
<tr>
<td class="resourceSeverityCell">Medium</td>
<td class="resourceNameCell">Configured liveness probe</td>
<td class="resourceURLCell"><a href="https://kubescape.io/docs/controls/c-0056/">C-0056</a></td>
<td class="resourceURLCell"><a href="https://hub.armosec.io/docs/c-0056">C-0056</a></td>
<td class="resourceRemediationCell"> <p>spec.jobTemplate.spec.template.spec.containers[0].livenessProbe=YOUR_VALUE</p> </td>
</tr>
<tr>
<td class="resourceSeverityCell">Low</td>
<td class="resourceNameCell">K8s common labels usage</td>
<td class="resourceURLCell"><a href="https://kubescape.io/docs/controls/c-0077/">C-0077</a></td>
<td class="resourceURLCell"><a href="https://hub.armosec.io/docs/c-0077">C-0077</a></td>
<td class="resourceRemediationCell"> <p>metadata.labels=YOUR_VALUE</p> <p>spec.jobTemplate.spec.template.metadata.labels=YOUR_VALUE</p> </td>
</tr>
@@ -1444,7 +1444,7 @@
<tr>
<td class="resourceSeverityCell">High</td>
<td class="resourceNameCell">List Kubernetes secrets</td>
<td class="resourceURLCell"><a href="https://kubescape.io/docs/controls/c-0015/">C-0015</a></td>
<td class="resourceURLCell"><a href="https://hub.armosec.io/docs/c-0015">C-0015</a></td>
<td class="resourceRemediationCell"> <p>relatedObjects[1].rules[0].resources[0]</p> <p>relatedObjects[1].rules[0].verbs[0]</p> <p>relatedObjects[1].rules[0].verbs[1]</p> <p>relatedObjects[1].rules[0].verbs[2]</p> <p>relatedObjects[1].rules[0].apiGroups[0]</p> <p>relatedObjects[0].subjects[0]</p> <p>relatedObjects[0].roleRef.name</p> </td>
</tr>
@@ -1471,63 +1471,63 @@
<tr>
<td class="resourceSeverityCell">Medium</td>
<td class="resourceNameCell">Allow privilege escalation</td>
<td class="resourceURLCell"><a href="https://kubescape.io/docs/controls/c-0016/">C-0016</a></td>
<td class="resourceURLCell"><a href="https://hub.armosec.io/docs/c-0016">C-0016</a></td>
<td class="resourceRemediationCell"> <p>spec.jobTemplate.spec.template.spec.containers[0].securityContext.allowPrivilegeEscalation=false</p> </td>
</tr>
<tr>
<td class="resourceSeverityCell">Medium</td>
<td class="resourceNameCell">Ingress and Egress blocked</td>
<td class="resourceURLCell"><a href="https://kubescape.io/docs/controls/c-0030/">C-0030</a></td>
<td class="resourceURLCell"><a href="https://hub.armosec.io/docs/c-0030">C-0030</a></td>
<td class="resourceRemediationCell"></td>
</tr>
<tr>
<td class="resourceSeverityCell">High</td>
<td class="resourceNameCell">Resource limits</td>
<td class="resourceURLCell"><a href="https://kubescape.io/docs/controls/c-0009/">C-0009</a></td>
<td class="resourceURLCell"><a href="https://hub.armosec.io/docs/c-0009">C-0009</a></td>
<td class="resourceRemediationCell"> <p>spec.jobTemplate.spec.template.spec.containers[0].resources.limits.cpu=YOUR_VALUE</p> <p>spec.jobTemplate.spec.template.spec.containers[0].resources.limits.memory=YOUR_VALUE</p> </td>
</tr>
<tr>
<td class="resourceSeverityCell">Low</td>
<td class="resourceNameCell">Configured readiness probe</td>
<td class="resourceURLCell"><a href=" https://kubescape.io/docs/controls/c-0018/">C-0018</a></td>
<td class="resourceURLCell"><a href="https://hub.armosec.io/docs/c-0018">C-0018</a></td>
<td class="resourceRemediationCell"> <p>spec.jobTemplate.spec.template.spec.containers[0].readinessProbe=YOUR_VALUE</p> </td>
</tr>
<tr>
<td class="resourceSeverityCell">Low</td>
<td class="resourceNameCell">Kubernetes CronJob</td>
<td class="resourceURLCell"><a href="https://kubescape.io/docs/controls/c-0026/">C-0026</a></td>
<td class="resourceURLCell"><a href="https://hub.armosec.io/docs/c-0026">C-0026</a></td>
<td class="resourceRemediationCell"></td>
</tr>
<tr>
<td class="resourceSeverityCell">Medium</td>
<td class="resourceNameCell">Non-root containers</td>
<td class="resourceURLCell"><a href="https://kubescape.io/docs/controls/c-0013/">C-0013</a></td>
<td class="resourceURLCell"><a href="https://hub.armosec.io/docs/c-0013">C-0013</a></td>
<td class="resourceRemediationCell"> <p>spec.jobTemplate.spec.template.spec.containers[0].securityContext.runAsNonRoot=true</p> <p>spec.jobTemplate.spec.template.spec.containers[0].securityContext.allowPrivilegeEscalation=false</p> </td>
</tr>
<tr>
<td class="resourceSeverityCell">Medium</td>
<td class="resourceNameCell">Linux hardening</td>
<td class="resourceURLCell"><a href="https://kubescape.io/docs/controls/c-0055/">C-0055</a></td>
<td class="resourceURLCell"><a href="https://hub.armosec.io/docs/c-0055">C-0055</a></td>
<td class="resourceRemediationCell"> <p>spec.jobTemplate.spec.template.spec.containers[0].securityContext.seccompProfile=YOUR_VALUE</p> <p>spec.jobTemplate.spec.template.spec.containers[0].securityContext.seLinuxOptions=YOUR_VALUE</p> <p>spec.jobTemplate.spec.template.spec.containers[0].securityContext.capabilities.drop[0]=YOUR_VALUE</p> </td>
</tr>
<tr>
<td class="resourceSeverityCell">Medium</td>
<td class="resourceNameCell">Configured liveness probe</td>
<td class="resourceURLCell"><a href="https://kubescape.io/docs/controls/c-0056/">C-0056</a></td>
<td class="resourceURLCell"><a href="https://hub.armosec.io/docs/c-0056">C-0056</a></td>
<td class="resourceRemediationCell"> <p>spec.jobTemplate.spec.template.spec.containers[0].livenessProbe=YOUR_VALUE</p> </td>
</tr>
<tr>
<td class="resourceSeverityCell">Low</td>
<td class="resourceNameCell">Immutable container filesystem</td>
<td class="resourceURLCell"><a href="https://kubescape.io/docs/controls/c-0017/">C-0017</a></td>
<td class="resourceURLCell"><a href="https://hub.armosec.io/docs/c-0017">C-0017</a></td>
<td class="resourceRemediationCell"> <p>spec.jobTemplate.spec.template.spec.containers[0].securityContext.readOnlyRootFilesystem=true</p> </td>
</tr>

630
go.mod
View File

@@ -1,132 +1,117 @@
module github.com/kubescape/kubescape/v3
go 1.24.1
go 1.23.0
toolchain go1.24.6
toolchain go1.23.4
require (
github.com/adrg/xdg v0.5.3
github.com/anchore/clio v0.0.0-20250715152405-a0fa658e5084
github.com/anchore/grype v0.99.1
github.com/anchore/stereoscope v0.1.9
github.com/anchore/syft v1.32.0
github.com/adrg/xdg v0.4.0
github.com/anchore/clio v0.0.0-20240209204744-cb94e40a4f65
github.com/anchore/grype v0.77.1
github.com/anchore/stereoscope v0.0.3-0.20240423181235-8b297badafd5
github.com/anchore/syft v1.3.0
github.com/anubhav06/copa-grype v1.0.3-alpha.1
github.com/armosec/armoapi-go v0.0.562
github.com/armosec/utils-go v0.0.58
github.com/armosec/utils-k8s-go v0.0.30
github.com/briandowns/spinner v1.23.2
github.com/armosec/armoapi-go v0.0.330
github.com/armosec/utils-go v0.0.57
github.com/armosec/utils-k8s-go v0.0.26
github.com/briandowns/spinner v1.23.1
github.com/chainguard-dev/git-urls v1.0.2
github.com/containerd/platforms v1.0.0-rc.1
github.com/distribution/reference v0.6.0
github.com/docker/buildx v0.21.3
github.com/docker/cli v28.3.3+incompatible
github.com/docker/distribution v2.8.3+incompatible
github.com/enescakir/emoji v1.0.0
github.com/francoispqt/gojay v1.2.13
github.com/go-git/go-git/v5 v5.16.2
github.com/google/go-containerregistry v0.20.6
github.com/go-git/go-git/v5 v5.13.0
github.com/google/go-containerregistry v0.19.1
github.com/google/uuid v1.6.0
github.com/jedib0t/go-pretty/v6 v6.6.8
github.com/johnfercher/go-tree v1.1.0
github.com/johnfercher/maroto/v2 v2.2.2
github.com/json-iterator/go v1.1.12
github.com/jwalton/gchalk v1.3.0
github.com/kubescape/backend v0.0.20
github.com/kubescape/go-git-url v0.0.30
github.com/kubescape/go-logger v0.0.25
github.com/kubescape/k8s-interface v0.0.195
github.com/kubescape/opa-utils v0.0.288
github.com/kubescape/go-logger v0.0.23
github.com/kubescape/k8s-interface v0.0.174
github.com/kubescape/opa-utils v0.0.287
github.com/kubescape/rbac-utils v0.0.21-0.20230806101615-07e36f555520
github.com/kubescape/regolibrary/v2 v2.0.1
github.com/kubescape/sizing-checker v0.0.0-20250323151332-73a18561dc73
github.com/kubescape/storage v0.0.184
github.com/mark3labs/mcp-go v0.29.0
github.com/kubescape/sizing-checker v0.0.0-20250225194755-bed52921170a
github.com/maruel/natural v1.1.1
github.com/matthyx/go-gitlog v0.0.0-20231005131906-9ffabe3c5bcd
github.com/mattn/go-isatty v0.0.20
github.com/mikefarah/yq/v4 v4.29.1
github.com/moby/buildkit v0.21.0
github.com/open-policy-agent/opa v1.4.0
github.com/olekukonko/tablewriter v0.0.6-0.20230417144759-edd1a71a5576
github.com/open-policy-agent/opa v0.68.0
github.com/owenrumney/go-sarif/v2 v2.2.0
github.com/project-copacetic/copacetic v0.10.0
github.com/quay/claircore v1.5.35
github.com/project-copacetic/copacetic v0.4.1-0.20231017020916-013c118454b8
github.com/schollz/progressbar/v3 v3.13.0
github.com/sergi/go-diff v1.4.0
github.com/sergi/go-diff v1.3.2-0.20230802210424-5b0b94c5c0d3
github.com/sigstore/cosign/v2 v2.2.4
github.com/sirupsen/logrus v1.9.4-0.20230606125235-dd1b4c2e81af
github.com/spf13/cobra v1.9.1
github.com/stretchr/testify v1.11.1
go.opentelemetry.io/otel v1.37.0
go.opentelemetry.io/otel/metric v1.37.0
golang.org/x/mod v0.27.0
golang.org/x/term v0.34.0
github.com/sirupsen/logrus v1.9.3
github.com/spf13/cobra v1.8.1
github.com/stretchr/testify v1.10.0
go.opentelemetry.io/otel v1.30.0
go.opentelemetry.io/otel/metric v1.30.0
golang.org/x/mod v0.20.0
golang.org/x/term v0.28.0
gopkg.in/op/go-logging.v1 v1.0.0-20160211212156-b2cb9fa56473
gopkg.in/yaml.v3 v3.0.1
helm.sh/helm/v3 v3.18.5
k8s.io/api v0.33.3
k8s.io/apimachinery v0.33.3
k8s.io/client-go v0.33.3
helm.sh/helm/v3 v3.14.4
k8s.io/api v0.32.2
k8s.io/apimachinery v0.32.2
k8s.io/client-go v0.32.2
k8s.io/utils v0.0.0-20241210054802-24370beab758
sigs.k8s.io/kustomize/api v0.19.0
sigs.k8s.io/kustomize/kyaml v0.19.0
sigs.k8s.io/yaml v1.5.0
sigs.k8s.io/kustomize/api v0.13.5-0.20230601165947-6ce0bf390ce3
sigs.k8s.io/kustomize/kyaml v0.14.3-0.20230601165947-6ce0bf390ce3
sigs.k8s.io/yaml v1.4.0
)
require github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 // indirect
require (
cel.dev/expr v0.24.0 // indirect
cloud.google.com/go v0.121.3 // indirect
cloud.google.com/go/auth v0.16.2 // indirect
cloud.google.com/go/auth/oauth2adapt v0.2.8 // indirect
cloud.google.com/go/compute/metadata v0.7.0 // indirect
cloud.google.com/go/container v1.43.0 // indirect
cloud.google.com/go/iam v1.5.2 // indirect
cloud.google.com/go/monitoring v1.24.2 // indirect
cloud.google.com/go/storage v1.55.0 // indirect
dario.cat/mergo v1.0.2 // indirect
cloud.google.com/go v0.112.1 // indirect
cloud.google.com/go/compute/metadata v0.5.0 // indirect
cloud.google.com/go/container v1.33.0 // indirect
cloud.google.com/go/iam v1.1.6 // indirect
cloud.google.com/go/storage v1.39.1 // indirect
dario.cat/mergo v1.0.0 // indirect
filippo.io/edwards25519 v1.1.0 // indirect
github.com/AdaLogics/go-fuzz-headers v0.0.0-20240806141605-e8a1dd7889d6 // indirect
github.com/AdamKorcz/go-118-fuzz-build v0.0.0-20250520111509-a70c2aa677fa // indirect
github.com/AdaLogics/go-fuzz-headers v0.0.0-20230811130428-ced1acdcaa24 // indirect
github.com/AdamKorcz/go-118-fuzz-build v0.0.0-20230306123547-8075edf89bb0 // indirect
github.com/AliyunContainerService/ack-ram-tool/pkg/credentials/alibabacloudsdkgo/helper v0.2.0 // indirect
github.com/Azure/azure-sdk-for-go v68.0.0+incompatible // indirect
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.17.1 // indirect
github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.8.2 // indirect
github.com/Azure/azure-sdk-for-go/sdk/internal v1.10.0 // indirect
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.11.1 // indirect
github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.6.0 // indirect
github.com/Azure/azure-sdk-for-go/sdk/internal v1.8.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/go-ansiterm v0.0.0-20250102033503-faa5f7b0171c // indirect
github.com/Azure/go-ansiterm v0.0.0-20230124172434-306776ec8161 // indirect
github.com/Azure/go-autorest v14.2.0+incompatible // indirect
github.com/Azure/go-autorest/autorest v0.11.29 // indirect
github.com/Azure/go-autorest/autorest/adal v0.9.24 // indirect
github.com/Azure/go-autorest/autorest/azure/auth v0.5.13 // indirect
github.com/Azure/go-autorest/autorest/adal v0.9.23 // indirect
github.com/Azure/go-autorest/autorest/azure/auth v0.5.12 // indirect
github.com/Azure/go-autorest/autorest/azure/cli v0.4.6 // indirect
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.3.3 // indirect
github.com/BurntSushi/toml v1.5.0 // indirect
github.com/CycloneDX/cyclonedx-go v0.9.2 // indirect
github.com/DataDog/zstd v1.5.7 // indirect
github.com/GoogleCloudPlatform/opentelemetry-operations-go/detectors/gcp v1.29.0 // indirect
github.com/GoogleCloudPlatform/opentelemetry-operations-go/exporter/metric v0.53.0 // indirect
github.com/GoogleCloudPlatform/opentelemetry-operations-go/internal/resourcemapping v0.53.0 // indirect
github.com/Intevation/gval v1.3.0 // indirect
github.com/Intevation/jsonpath v0.2.1 // indirect
github.com/MakeNowJust/heredoc v1.0.0 // indirect
github.com/AzureAD/microsoft-authentication-library-for-go v1.2.2 // indirect
github.com/BurntSushi/toml v1.3.2 // indirect
github.com/CycloneDX/cyclonedx-go v0.8.0 // indirect
github.com/DataDog/zstd v1.5.5 // indirect
github.com/Masterminds/goutils v1.1.1 // indirect
github.com/Masterminds/semver v1.5.0 // indirect
github.com/Masterminds/semver/v3 v3.4.0 // indirect
github.com/Masterminds/sprig/v3 v3.3.0 // indirect
github.com/Masterminds/semver/v3 v3.2.1 // indirect
github.com/Masterminds/sprig/v3 v3.2.3 // indirect
github.com/Microsoft/go-winio v0.6.2 // indirect
github.com/Microsoft/hcsshim v0.13.0 // indirect
github.com/Microsoft/hcsshim v0.11.7 // indirect
github.com/OneOfOne/xxhash v1.2.8 // indirect
github.com/ProtonMail/go-crypto v1.3.0 // indirect
github.com/STARRY-S/zip v0.2.3 // indirect
github.com/ProtonMail/go-crypto v1.1.3 // indirect
github.com/ThalesIgnite/crypto11 v1.2.5 // indirect
github.com/a8m/envsubst v1.3.0 // indirect
github.com/acobaugh/osrelease v0.1.0 // indirect
github.com/agext/levenshtein v1.2.3 // indirect
github.com/agnivade/levenshtein v1.2.1 // indirect
github.com/alecthomas/participle/v2 v2.1.0 // indirect
github.com/agl/ed25519 v0.0.0-20170116200512-5312a6153412 // 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
github.com/alibabacloud-go/cr-20160607 v1.0.1 // indirect
github.com/alibabacloud-go/cr-20181201 v1.0.10 // indirect
@@ -138,194 +123,178 @@ require (
github.com/alibabacloud-go/tea-utils v1.4.5 // indirect
github.com/alibabacloud-go/tea-xml v1.1.3 // indirect
github.com/aliyun/credentials-go v1.3.1 // indirect
github.com/anchore/archiver/v3 v3.5.3-0.20241210171143-5b1d8d1c7c51 // indirect
github.com/anchore/fangs v0.0.0-20250716230140-94c22408c232 // indirect
github.com/anchore/go-collections v0.0.0-20241211140901-567f400e9a46 // indirect
github.com/anchore/go-homedir v0.0.0-20250319154043-c29668562e4d // indirect
github.com/anchore/go-logger v0.0.0-20250318195838-07ae343dd722 // indirect
github.com/anchore/go-lzo v0.1.0 // indirect
github.com/anchore/go-macholibre v0.0.0-20250320151634-807da7ad2331 // indirect
github.com/anchore/go-rpmdb v0.0.0-20250516171929-f77691e1faec // indirect
github.com/anchore/go-struct-converter v0.0.0-20250211213226-cce56d595160 // indirect
github.com/anchore/go-sync v0.0.0-20250714163430-add63db73ad1 // indirect
github.com/anchore/fangs v0.0.0-20231201140849-5075d28d6d8b // indirect
github.com/anchore/go-collections v0.0.0-20240216171411-9321230ce537 // indirect
github.com/anchore/go-logger v0.0.0-20230725134548-c21dafa1ec5a // 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.20250220190351-d62adb6e1115 // indirect
github.com/andybalholm/brotli v1.2.0 // indirect
github.com/anchore/packageurl-go v0.1.1-0.20240312213626-055233e539b4 // indirect
github.com/andybalholm/brotli v1.0.4 // indirect
github.com/apparentlymart/go-textseg/v15 v15.0.0 // indirect
github.com/aquasecurity/go-pep440-version v0.0.1 // indirect
github.com/aquasecurity/go-version v0.0.1 // indirect
github.com/armosec/gojay v1.2.17 // indirect
github.com/aquasecurity/go-pep440-version v0.0.0-20210121094942-22b2f8951d46 // indirect
github.com/aquasecurity/go-version v0.0.0-20210121072130-637058cfe492 // indirect
github.com/armosec/gojay v1.2.15 // indirect
github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2 // indirect
github.com/aws/aws-sdk-go v1.55.7 // indirect
github.com/aws/aws-sdk-go-v2 v1.36.5 // indirect
github.com/aws/aws-sdk-go-v2/config v1.29.17 // indirect
github.com/aws/aws-sdk-go-v2/credentials v1.17.70 // indirect
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.32 // indirect
github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.36 // indirect
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.36 // indirect
github.com/aws/aws-sdk-go-v2/internal/ini v1.8.3 // indirect
github.com/aws/aws-sdk-go-v2/service/ecr v1.45.1 // indirect
github.com/aws/aws-sdk-go-v2/service/ecrpublic v1.33.2 // indirect
github.com/aws/aws-sdk-go v1.55.6-0.20240912145455-7112c0a0c2d0 // indirect
github.com/aws/aws-sdk-go-v2 v1.30.5 // indirect
github.com/aws/aws-sdk-go-v2/config v1.27.35 // indirect
github.com/aws/aws-sdk-go-v2/credentials v1.17.33 // indirect
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.13 // indirect
github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.17 // indirect
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.17 // indirect
github.com/aws/aws-sdk-go-v2/internal/ini v1.8.1 // indirect
github.com/aws/aws-sdk-go-v2/service/ecr v1.34.0 // indirect
github.com/aws/aws-sdk-go-v2/service/ecrpublic v1.25.7 // indirect
github.com/aws/aws-sdk-go-v2/service/eks v1.48.5 // indirect
github.com/aws/aws-sdk-go-v2/service/iam v1.35.3 // indirect
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.12.4 // indirect
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.12.17 // indirect
github.com/aws/aws-sdk-go-v2/service/sso v1.25.5 // indirect
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.30.3 // indirect
github.com/aws/aws-sdk-go-v2/service/sts v1.34.0 // indirect
github.com/aws/smithy-go v1.22.4 // indirect
github.com/awslabs/amazon-ecr-credential-helper/ecr-login v0.10.1 // indirect
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.11.4 // indirect
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.11.19 // indirect
github.com/aws/aws-sdk-go-v2/service/kms v1.35.8 // indirect
github.com/aws/aws-sdk-go-v2/service/sso v1.22.8 // indirect
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.26.8 // indirect
github.com/aws/aws-sdk-go-v2/service/sts v1.30.8 // indirect
github.com/aws/smithy-go v1.20.4 // indirect
github.com/awslabs/amazon-ecr-credential-helper/ecr-login v0.0.0-20231024185945-8841054dbdb8 // indirect
github.com/aymanbagabas/go-osc52/v2 v2.0.1 // indirect
github.com/becheran/wildmatch-go v1.0.0 // indirect
github.com/beorn7/perks v1.0.1 // indirect
github.com/bgentry/go-netrc v0.0.0-20140422174119-9fd32a8b3d3d // indirect
github.com/bitnami/go-version v0.0.0-20250505154626-452e8c5ee607 // indirect
github.com/blakesmith/ar v0.0.0-20190502131153-809d4375e1fb // indirect
github.com/blang/semver v3.5.1+incompatible // indirect
github.com/blang/semver/v4 v4.0.0 // indirect
github.com/bmatcuk/doublestar/v2 v2.0.4 // indirect
github.com/bmatcuk/doublestar/v4 v4.9.1 // indirect
github.com/bodgit/plumbing v1.3.0 // indirect
github.com/bodgit/sevenzip v1.6.1 // indirect
github.com/bodgit/windows v1.0.1 // indirect
github.com/bmatcuk/doublestar/v4 v4.6.1 // indirect
github.com/boombuler/barcode v1.0.2 // indirect
github.com/bugsnag/bugsnag-go/v2 v2.3.0 // indirect
github.com/bugsnag/panicwrap v1.3.4 // indirect
github.com/buildkite/agent/v3 v3.62.0 // indirect
github.com/buildkite/go-pipeline v0.3.2 // indirect
github.com/buildkite/interpolate v0.0.0-20200526001904-07f35b4ae251 // indirect
github.com/cenkalti/backoff v2.2.1+incompatible // indirect
github.com/cenkalti/backoff/v4 v4.3.0 // indirect
github.com/cenkalti/backoff/v5 v5.0.2 // indirect
github.com/cespare/xxhash/v2 v2.3.0 // indirect
github.com/chai2010/gettext-go v1.0.2 // indirect
github.com/charmbracelet/colorprofile v0.3.1 // indirect
github.com/charmbracelet/lipgloss v1.1.0 // indirect
github.com/charmbracelet/x/ansi v0.9.3 // indirect
github.com/charmbracelet/x/cellbuf v0.0.13 // indirect
github.com/charmbracelet/x/term v0.2.1 // indirect
github.com/charmbracelet/lipgloss v0.10.0 // indirect
github.com/chrismellard/docker-credential-acr-env v0.0.0-20230304212654-82a0ddb27589 // indirect
github.com/cilium/cilium v1.16.9 // indirect
github.com/clbanning/mxj/v2 v2.7.0 // indirect
github.com/cloudflare/circl v1.6.1 // indirect
github.com/cncf/xds/go v0.0.0-20250501225837-2ac532fd4443 // indirect
github.com/cloudflare/circl v1.3.7 // indirect
github.com/common-nighthawk/go-figure v0.0.0-20210622060536-734e95fb86be // indirect
github.com/containerd/cgroups/v3 v3.0.5 // indirect
github.com/containerd/console v1.0.4 // indirect
github.com/containerd/containerd v1.7.28 // indirect
github.com/containerd/containerd/api v1.9.0 // indirect
github.com/containerd/containerd/v2 v2.0.5 // indirect
github.com/containerd/continuity v0.4.5 // indirect
github.com/containerd/errdefs v1.0.0 // indirect
github.com/containerd/errdefs/pkg v0.3.0 // indirect
github.com/containerd/cgroups v1.1.0 // indirect
github.com/containerd/console v1.0.4-0.20230313162750-1ae8d489ac81 // indirect
github.com/containerd/containerd v1.7.21 // indirect
github.com/containerd/containerd/api v1.7.19 // indirect
github.com/containerd/continuity v0.4.2 // indirect
github.com/containerd/errdefs v0.1.0 // indirect
github.com/containerd/fifo v1.1.0 // indirect
github.com/containerd/log v0.1.0 // indirect
github.com/containerd/stargz-snapshotter/estargz v0.16.3 // indirect
github.com/containerd/ttrpc v1.2.7 // indirect
github.com/containerd/typeurl/v2 v2.2.3 // indirect
github.com/containers/common v0.63.0 // indirect
github.com/coreos/go-oidc/v3 v3.14.1 // indirect
github.com/cpuguy83/dockercfg v0.3.2 // indirect
github.com/cpuguy83/go-docker v0.3.0 // indirect
github.com/cyberphone/json-canonicalization v0.0.0-20241213102144-19d51d7fe467 // indirect
github.com/cyphar/filepath-securejoin v0.4.1 // indirect
github.com/containerd/platforms v0.2.1 // indirect
github.com/containerd/stargz-snapshotter/estargz v0.14.3 // indirect
github.com/containerd/ttrpc v1.2.5 // indirect
github.com/containerd/typeurl/v2 v2.1.1 // indirect
github.com/coreos/go-oidc v2.2.1+incompatible // indirect
github.com/coreos/go-oidc/v3 v3.10.0 // indirect
github.com/cpuguy83/dockercfg v0.3.1 // indirect
github.com/cpuguy83/go-docker v0.2.1 // indirect
github.com/cyberphone/json-canonicalization v0.0.0-20231011164504-785e29786b46 // indirect
github.com/cyphar/filepath-securejoin v0.2.5 // indirect
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
github.com/deitch/magic v0.0.0-20240306090643-c67ab88f10cb // indirect
github.com/deitch/magic v0.0.0-20230404182410-1ff89d7342da // indirect
github.com/digitorus/pkcs7 v0.0.0-20230818184609-3a137a874352 // indirect
github.com/digitorus/timestamp v0.0.0-20231217203849-220c5c2851b7 // indirect
github.com/dimchansky/utfbom v1.1.1 // indirect
github.com/diskfs/go-diskfs v1.7.0 // indirect
github.com/docker/distribution v2.8.3+incompatible // indirect
github.com/docker/docker v28.3.3+incompatible // indirect
github.com/docker/docker-credential-helpers v0.9.3 // indirect
github.com/docker/go-connections v0.6.0 // indirect
github.com/docker/go-events v0.0.0-20250114142523-c867878c5e32 // indirect
github.com/docker/buildx v0.11.2 // indirect
github.com/docker/cli v26.1.0+incompatible // indirect
github.com/docker/docker v26.1.5+incompatible // indirect
github.com/docker/docker-credential-helpers v0.8.0 // indirect
github.com/docker/go v1.5.1-1.0.20160303222718-d30aec9fd63c // indirect
github.com/docker/go-connections v0.4.0 // indirect
github.com/docker/go-events v0.0.0-20190806004212-e31b211e4f1c // indirect
github.com/docker/go-metrics v0.0.1 // indirect
github.com/docker/go-units v0.5.0 // indirect
github.com/dsnet/compress v0.0.2-0.20230904184137-39efe44ab707 // indirect
github.com/docker/libtrust v0.0.0-20160708172513-aabc10ec26b7 // indirect
github.com/dsnet/compress v0.0.2-0.20210315054119-f66993602bf5 // indirect
github.com/dustin/go-humanize v1.0.1 // indirect
github.com/edsrzf/mmap-go v1.1.0 // indirect
github.com/elliotchance/orderedmap v1.5.0 // indirect
github.com/elliotchance/phpserialize v1.4.0 // indirect
github.com/emicklei/go-restful/v3 v3.12.0 // indirect
github.com/emicklei/go-restful/v3 v3.11.0 // indirect
github.com/emirpasic/gods v1.18.1 // indirect
github.com/envoyproxy/go-control-plane/envoy v1.32.4 // indirect
github.com/envoyproxy/protoc-gen-validate v1.2.1 // indirect
github.com/evanphx/json-patch v5.9.11+incompatible // indirect
github.com/exponent-io/jsonpath v0.0.0-20210407135951-1de76d718b3f // indirect
github.com/evanphx/json-patch v5.7.0+incompatible // indirect
github.com/f-amaral/go-async v0.3.0 // indirect
github.com/facebookincubator/nvdtools v0.1.5 // indirect
github.com/fatih/color v1.18.0 // indirect
github.com/felixge/fgprof v0.9.5 // indirect
github.com/fatih/color v1.17.0 // indirect
github.com/felixge/fgprof v0.9.3 // indirect
github.com/felixge/httpsnoop v1.0.4 // indirect
github.com/fsnotify/fsnotify v1.9.0 // indirect
github.com/fsnotify/fsnotify v1.7.0 // indirect
github.com/fvbommel/sortorder v1.1.0 // indirect
github.com/fxamacker/cbor/v2 v2.7.0 // indirect
github.com/gabriel-vasile/mimetype v1.4.10 // indirect
github.com/github/go-spdx/v2 v2.3.3 // indirect
github.com/glebarez/go-sqlite v1.22.0 // indirect
github.com/gabriel-vasile/mimetype v1.4.3 // indirect
github.com/github/go-spdx/v2 v2.2.0 // indirect
github.com/glebarez/go-sqlite v1.21.2 // indirect
github.com/glebarez/sqlite v1.11.0 // indirect
github.com/go-chi/chi v4.1.2+incompatible // indirect
github.com/go-errors/errors v1.4.2 // indirect
github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 // indirect
github.com/go-git/go-billy/v5 v5.6.2 // indirect
github.com/go-git/go-billy/v5 v5.6.0 // indirect
github.com/go-gota/gota v0.12.0 // indirect
github.com/go-ini/ini v1.67.0 // indirect
github.com/go-jose/go-jose/v3 v3.0.4 // indirect
github.com/go-jose/go-jose/v4 v4.0.5 // indirect
github.com/go-logr/logr v1.4.3 // indirect
github.com/go-logr/logr v1.4.2 // indirect
github.com/go-logr/stdr v1.2.2 // indirect
github.com/go-openapi/analysis v0.23.0 // indirect
github.com/go-openapi/errors v0.22.1 // indirect
github.com/go-openapi/errors v0.22.0 // indirect
github.com/go-openapi/jsonpointer v0.21.0 // indirect
github.com/go-openapi/jsonreference v0.21.0 // indirect
github.com/go-openapi/loads v0.22.0 // indirect
github.com/go-openapi/runtime v0.28.0 // indirect
github.com/go-openapi/spec v0.21.0 // indirect
github.com/go-openapi/strfmt v0.23.0 // indirect
github.com/go-openapi/swag v0.23.1 // indirect
github.com/go-openapi/swag v0.23.0 // indirect
github.com/go-openapi/validate v0.24.0 // indirect
github.com/go-piv/piv-go v1.11.0 // indirect
github.com/go-restruct/restruct v1.2.0-alpha // indirect
github.com/go-viper/mapstructure/v2 v2.4.0 // indirect
github.com/go-test/deep v1.1.0 // indirect
github.com/gobwas/glob v0.2.3 // indirect
github.com/goccy/go-json v0.10.2 // indirect
github.com/goccy/go-yaml v1.18.0 // indirect
github.com/gocsaf/csaf/v3 v3.3.0 // indirect
github.com/gofrs/flock v0.12.1 // indirect
github.com/goccy/go-yaml v1.9.6 // indirect
github.com/gofrs/flock v0.8.1 // indirect
github.com/gofrs/uuid v4.3.1+incompatible // indirect
github.com/gogo/googleapis v1.4.1 // indirect
github.com/gogo/protobuf v1.3.2 // indirect
github.com/gohugoio/hashstructure v0.5.0 // indirect
github.com/golang-jwt/jwt/v4 v4.5.2 // indirect
github.com/golang-jwt/jwt/v5 v5.2.2 // indirect
github.com/golang/groupcache v0.0.0-20241129210726-2c02b8208cf8 // indirect
github.com/golang-jwt/jwt/v4 v4.5.1 // indirect
github.com/golang-jwt/jwt/v5 v5.2.1 // indirect
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect
github.com/golang/protobuf v1.5.4 // indirect
github.com/golang/snappy v1.0.0 // indirect
github.com/google/btree v1.1.3 // indirect
github.com/google/certificate-transparency-go v1.3.1 // indirect
github.com/golang/snappy v0.0.4 // indirect
github.com/google/certificate-transparency-go v1.1.8 // indirect
github.com/google/gnostic-models v0.6.9 // indirect
github.com/google/go-cmp v0.7.0 // indirect
github.com/google/go-cmp v0.6.0 // indirect
github.com/google/go-github/v55 v55.0.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/pprof v0.0.0-20250630185457-6e76a2b096b5 // indirect
github.com/google/s2a-go v0.1.9 // indirect
github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 // indirect
github.com/googleapis/enterprise-certificate-proxy v0.3.6 // indirect
github.com/googleapis/gax-go/v2 v2.15.0 // indirect
github.com/gookit/color v1.6.0 // indirect
github.com/google/pprof v0.0.0-20241029153458-d1b30febd7db // indirect
github.com/google/s2a-go v0.1.7 // indirect
github.com/googleapis/enterprise-certificate-proxy v0.3.2 // indirect
github.com/googleapis/gax-go/v2 v2.12.3 // indirect
github.com/gookit/color v1.5.4 // indirect
github.com/gorilla/mux v1.8.1 // indirect
github.com/gorilla/websocket v1.5.4-0.20250319132907-e064f32e3674 // indirect
github.com/gregjones/httpcache v0.0.0-20190611155906-901d90724c79 // indirect
github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.1 // indirect
github.com/gorilla/websocket v1.5.0 // indirect
github.com/grpc-ecosystem/go-grpc-middleware v1.4.0 // indirect
github.com/grpc-ecosystem/grpc-gateway/v2 v2.22.0 // 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.9 // indirect
github.com/hashicorp/go-getter v1.7.5 // indirect
github.com/hashicorp/go-multierror v1.1.1 // indirect
github.com/hashicorp/go-retryablehttp v0.7.7 // indirect
github.com/hashicorp/go-safetemp v1.0.0 // indirect
github.com/hashicorp/go-version v1.7.0 // indirect
github.com/hashicorp/golang-lru/v2 v2.0.7 // indirect
github.com/hashicorp/hcl/v2 v2.24.0 // indirect
github.com/hashicorp/go-version v1.6.0 // indirect
github.com/hashicorp/hcl v1.0.1-vault-5 // indirect
github.com/hhrutter/lzw v1.0.0 // indirect
github.com/hhrutter/tiff v1.0.1 // indirect
github.com/huandu/xstrings v1.5.0 // indirect
github.com/huandu/xstrings v1.4.0 // indirect
github.com/iancoleman/strcase v0.3.0 // indirect
github.com/imdario/mergo v0.3.16 // indirect
github.com/in-toto/in-toto-golang v0.9.0 // indirect
github.com/inconshreveable/mousetrap v1.1.0 // indirect
github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 // indirect
@@ -333,247 +302,222 @@ require (
github.com/jinzhu/copier v0.4.0 // indirect
github.com/jinzhu/inflection v1.0.0 // indirect
github.com/jinzhu/now v1.1.5 // indirect
github.com/jmespath/go-jmespath v0.4.1-0.20220621161143-b0104c826a24 // indirect
github.com/jmespath/go-jmespath v0.4.0 // indirect
github.com/josharian/intern v1.0.0 // indirect
github.com/jung-kurt/gofpdf v1.16.2 // indirect
github.com/jwalton/go-supportscolor v1.1.0 // indirect
github.com/kastenhq/goversion v0.0.0-20230811215019-93b2f8823953 // indirect
github.com/kevinburke/ssh_config v1.2.0 // indirect
github.com/klauspost/compress v1.18.0 // indirect
github.com/klauspost/compress v1.17.9 // indirect
github.com/klauspost/pgzip v1.2.6 // indirect
github.com/knqyf263/go-apk-version v0.0.0-20200609155635-041fdbb8563f // indirect
github.com/knqyf263/go-deb-version v0.0.0-20241115132648-6f4aee6ccd23 // indirect
github.com/knqyf263/go-deb-version v0.0.0-20230223133812-3ed183d23422 // indirect
github.com/knqyf263/go-rpm-version v0.0.0-20220614171824-631e686d1075 // indirect
github.com/knqyf263/go-rpmdb v0.1.0 // indirect
github.com/kylelemons/godebug v1.1.0 // indirect
github.com/letsencrypt/boulder v0.0.0-20240620165639-de9c06129bec // indirect
github.com/liggitt/tabwriter v0.0.0-20181228230101-89fcab3d43de // indirect
github.com/letsencrypt/boulder v0.0.0-20231026200631-000cd05d5491 // indirect
github.com/lucasb-eyer/go-colorful v1.2.0 // indirect
github.com/mackerelio/go-osstat v0.2.5 // indirect
github.com/magiconair/properties v1.8.10 // indirect
github.com/mailru/easyjson v0.9.0 // indirect
github.com/masahiro331/go-mvn-version v0.0.0-20250131095131-f4974fa13b8a // indirect
github.com/mattn/go-colorable v0.1.14 // indirect
github.com/magiconair/properties v1.8.7 // indirect
github.com/mailru/easyjson v0.7.7 // indirect
github.com/masahiro331/go-mvn-version v0.0.0-20210429150710-d3157d602a08 // indirect
github.com/mattn/go-colorable v0.1.13 // indirect
github.com/mattn/go-runewidth v0.0.16 // indirect
github.com/mgutz/ansi v0.0.0-20200706080929-d51e80ef957d // indirect
github.com/mholt/archives v0.1.5 // indirect
github.com/mholt/archiver/v3 v3.5.1 // indirect
github.com/microsoft/go-rustaudit v0.0.0-20220808201409-204dfee52032 // indirect
github.com/miekg/pkcs11 v1.1.1 // indirect
github.com/mikelolasagasti/xz v1.0.1 // indirect
github.com/minio/minlz v1.0.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-wordwrap v1.0.1 // 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/moby/buildkit v0.12.5 // indirect
github.com/moby/docker-image-spec v1.3.1 // indirect
github.com/moby/locker v1.0.1 // indirect
github.com/moby/patternmatcher v0.6.0 // indirect
github.com/moby/patternmatcher v0.5.0 // indirect
github.com/moby/spdystream v0.5.0 // indirect
github.com/moby/sys/atomicwriter v0.1.0 // indirect
github.com/moby/sys/mountinfo v0.7.2 // indirect
github.com/moby/sys/sequential v0.6.0 // indirect
github.com/moby/sys/signal v0.7.1 // indirect
github.com/moby/sys/user v0.4.0 // indirect
github.com/moby/sys/mountinfo v0.7.1 // indirect
github.com/moby/sys/sequential v0.5.0 // indirect
github.com/moby/sys/signal v0.7.0 // indirect
github.com/moby/sys/user v0.3.0 // indirect
github.com/moby/sys/userns v0.1.0 // indirect
github.com/moby/term v0.5.2 // indirect
github.com/moby/term v0.5.0 // indirect
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
github.com/modern-go/reflect2 v1.0.2 // indirect
github.com/monochromegane/go-gitignore v0.0.0-20200626010858-205db1a8cc00 // indirect
github.com/morikuni/aec v1.0.0 // indirect
github.com/mozillazg/docker-credential-acr-helper v0.3.0 // indirect
github.com/muesli/termenv v0.16.0 // indirect
github.com/muesli/reflow v0.3.0 // indirect
github.com/muesli/termenv v0.15.2 // indirect
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f // indirect
github.com/ncruces/go-strftime v0.1.9 // indirect
github.com/nix-community/go-nix v0.0.0-20250101154619-4bdde671e0a1 // indirect
github.com/nozzle/throttler v0.0.0-20180817012639-2ea982251481 // indirect
github.com/nwaples/rardecode v1.1.3 // indirect
github.com/nwaples/rardecode/v2 v2.2.0 // indirect
github.com/nwaples/rardecode v1.1.0 // indirect
github.com/oklog/ulid v1.3.1 // indirect
github.com/oleiade/reflections v1.0.1 // indirect
github.com/olekukonko/errors v1.1.0 // indirect
github.com/olekukonko/ll v0.0.9 // indirect
github.com/olekukonko/tablewriter v1.0.9 // indirect
github.com/olvrng/ujson v1.1.0 // indirect
github.com/opencontainers/go-digest v1.0.0 // indirect
github.com/opencontainers/image-spec v1.1.1 // indirect
github.com/opencontainers/runtime-spec v1.2.1 // indirect
github.com/opencontainers/selinux v1.12.0 // indirect
github.com/opentracing/opentracing-go v1.2.1-0.20220228012449-10b1cf09e00b // indirect
github.com/opencontainers/image-spec v1.1.0 // indirect
github.com/opencontainers/runtime-spec v1.1.0 // indirect
github.com/opencontainers/selinux v1.11.0 // indirect
github.com/opentracing/opentracing-go v1.2.0 // indirect
github.com/openvex/go-vex v0.2.5 // indirect
github.com/owenrumney/go-sarif v1.1.2-0.20231003122901-1000f5e05554 // indirect
github.com/package-url/packageurl-go v0.1.3 // indirect
github.com/pandatix/go-cvss v0.6.2 // indirect
github.com/package-url/packageurl-go v0.1.2-0.20230812223828-f8bb31c1f10b // indirect
github.com/pborman/indent v1.2.1 // indirect
github.com/pborman/uuid v1.2.1 // indirect
github.com/pdfcpu/pdfcpu v0.9.1 // indirect
github.com/pelletier/go-toml v1.9.5 // indirect
github.com/pelletier/go-toml/v2 v2.2.4 // indirect
github.com/peterbourgon/diskv v2.0.1+incompatible // indirect
github.com/petermattis/goid v0.0.0-20241211131331-93ee7e083c43 // indirect
github.com/pierrec/lz4/v4 v4.1.22 // indirect
github.com/pjbgf/sha1cd v0.4.0 // indirect
github.com/pelletier/go-toml/v2 v2.1.0 // 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-20240102092130-5ac0b6a4141c // indirect
github.com/pkg/errors v0.9.1 // indirect
github.com/pkg/profile v1.7.0 // indirect
github.com/pkg/xattr v0.4.12 // indirect
github.com/planetscale/vtprotobuf v0.6.1-0.20240319094008-0393e58bdf10 // indirect
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect
github.com/prometheus/client_golang v1.22.0 // indirect
github.com/pquerna/cachecontrol v0.2.0 // indirect
github.com/prometheus/client_golang v1.20.2 // indirect
github.com/prometheus/client_model v0.6.1 // indirect
github.com/prometheus/common v0.62.0 // indirect
github.com/prometheus/common v0.55.0 // indirect
github.com/prometheus/procfs v0.15.1 // indirect
github.com/quay/claircore/toolkit v1.2.4 // indirect
github.com/quay/zlog v1.1.8 // 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.7 // indirect
github.com/rs/zerolog v1.30.0 // indirect
github.com/russross/blackfriday/v2 v2.1.0 // indirect
github.com/rust-secure-code/go-rustaudit v0.0.0-20250226111315-e20ec32e963c // indirect
github.com/sagikazarmark/locafero v0.9.0 // indirect
github.com/saferwall/pe v1.5.2 // indirect
github.com/sagikazarmark/locafero v0.4.0 // indirect
github.com/sagikazarmark/slog-shim v0.1.0 // indirect
github.com/saintfish/chardet v0.0.0-20230101081208-5e3ef4b5456d // indirect
github.com/santhosh-tekuri/jsonschema/v6 v6.0.2 // indirect
github.com/sasha-s/go-deadlock v0.3.5 // indirect
github.com/sassoftware/go-rpmutils v0.4.0 // indirect
github.com/sassoftware/go-rpmutils v0.3.0 // indirect
github.com/sassoftware/relic v7.2.1+incompatible // indirect
github.com/scylladb/go-set v1.0.3-0.20200225121959-cc7b2070d91e // indirect
github.com/seccomp/libseccomp-golang v0.10.0 // indirect
github.com/secure-systems-lab/go-securesystemslib v0.9.0 // indirect
github.com/secure-systems-lab/go-securesystemslib v0.8.0 // indirect
github.com/segmentio/ksuid v1.0.4 // indirect
github.com/shibumi/go-pathspec v1.3.0 // indirect
github.com/shopspring/decimal v1.4.0 // indirect
github.com/sigstore/fulcio v1.6.6 // indirect
github.com/sigstore/protobuf-specs v0.4.1 // indirect
github.com/sigstore/rekor v1.3.10 // indirect
github.com/sigstore/sigstore v1.9.5 // indirect
github.com/shopspring/decimal v1.3.1 // indirect
github.com/sigstore/fulcio v1.4.5 // indirect
github.com/sigstore/rekor v1.3.6 // indirect
github.com/sigstore/sigstore v1.8.3 // indirect
github.com/sigstore/timestamp-authority v1.2.2 // indirect
github.com/skeema/knownhosts v1.3.1 // indirect
github.com/skeema/knownhosts v1.3.0 // indirect
github.com/skratchdot/open-golang v0.0.0-20200116055534-eef842397966 // indirect
github.com/sorairolake/lzip-go v0.3.8 // indirect
github.com/sourcegraph/conc v0.3.0 // indirect
github.com/spdx/gordf v0.0.0-20250128162952-000978ccd6fb // indirect
github.com/spdx/tools-golang v0.5.5 // indirect
github.com/spf13/afero v1.15.0 // indirect
github.com/spf13/cast v1.9.2 // indirect
github.com/spf13/pflag v1.0.7 // indirect
github.com/spf13/viper v1.20.1 // indirect
github.com/spiffe/go-spiffe/v2 v2.5.0 // indirect
github.com/stripe/stripe-go/v74 v74.30.0 // indirect
github.com/spdx/tools-golang v0.5.4 // indirect
github.com/spf13/afero v1.11.0 // indirect
github.com/spf13/cast v1.6.0 // indirect
github.com/spf13/pflag v1.0.5 // indirect
github.com/spf13/viper v1.18.2 // indirect
github.com/spiffe/go-spiffe/v2 v2.2.0 // indirect
github.com/stripe/stripe-go/v74 v74.28.0 // indirect
github.com/subosito/gotenv v1.6.0 // indirect
github.com/sylabs/sif/v2 v2.22.0 // indirect
github.com/sylabs/squashfs v1.0.6 // indirect
github.com/sylabs/sif/v2 v2.11.5 // 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.2 // indirect
github.com/tchap/go-patricia/v2 v2.3.1 // 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.7.0 // indirect
github.com/theupdateframework/notary v0.6.1 // indirect
github.com/titanous/rocacheck v0.0.0-20171023193734-afe73141d399 // indirect
github.com/tjfoc/gmsm v1.4.1 // indirect
github.com/tonistiigi/dchapes-mode v0.0.0-20250318174251-73d941a28323 // indirect
github.com/tonistiigi/fsutil v0.0.0-20250410151801-5b74a7ad7583 // indirect
github.com/tonistiigi/go-csvvalue v0.0.0-20240710180619-ddb21b71c0b4 // indirect
github.com/tonistiigi/fsutil v0.0.0-20230629203738-36ef4d8c0dbb // indirect
github.com/tonistiigi/units v0.0.0-20180711220420-6950e57a87ea // indirect
github.com/tonistiigi/vt100 v0.0.0-20240514184818-90bafcd6abab // indirect
github.com/tonistiigi/vt100 v0.0.0-20230623042737-f9a4f7ef6531 // indirect
github.com/transparency-dev/merkle v0.0.2 // indirect
github.com/ulikunitz/xz v0.5.15 // indirect
github.com/ulikunitz/xz v0.5.11 // indirect
github.com/uptrace/opentelemetry-go-extra/otelutil v0.3.2 // indirect
github.com/uptrace/opentelemetry-go-extra/otelzap v0.3.2 // indirect
github.com/uptrace/uptrace-go v1.37.0 // indirect
github.com/vbatts/go-mtree v0.5.4 // indirect
github.com/vbatts/tar-split v0.12.1 // indirect
github.com/uptrace/uptrace-go v1.30.1 // indirect
github.com/vbatts/go-mtree v0.5.3 // indirect
github.com/vbatts/tar-split v0.11.5 // indirect
github.com/vifraa/gopom v1.0.0 // indirect
github.com/vishvananda/netlink v1.3.1-0.20241022031324-976bd8de7d81 // indirect
github.com/vishvananda/netns v0.0.5 // 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-20230925121702-07e42b3cdba0 // indirect
github.com/x448/float16 v0.8.4 // indirect
github.com/xanzy/go-gitlab v0.102.0 // 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/xlab/treeprint v1.2.0 // indirect
github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e // indirect
github.com/xo/terminfo v0.0.0-20210125001918-ca9a967f8778 // indirect
github.com/yashtewari/glob-intersection v0.2.0 // indirect
github.com/yl2chen/cidranger v1.0.2 // indirect
github.com/yosida95/uritemplate/v3 v3.0.2 // indirect
github.com/zclconf/go-cty v1.16.3 // indirect
github.com/zeebo/errs v1.4.0 // indirect
go.etcd.io/bbolt v1.4.2 // indirect
go.mongodb.org/mongo-driver v1.17.1 // indirect
github.com/zclconf/go-cty v1.14.0 // indirect
github.com/zeebo/errs v1.3.0 // indirect
go.mongodb.org/mongo-driver v1.14.0 // indirect
go.mozilla.org/pkcs7 v0.0.0-20210826202110-33d05740a352 // indirect
go.opencensus.io v0.24.0 // indirect
go.opentelemetry.io/auto/sdk v1.1.0 // indirect
go.opentelemetry.io/contrib/detectors/gcp v1.37.0 // indirect
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.62.0 // indirect
go.opentelemetry.io/contrib/instrumentation/net/http/httptrace/otelhttptrace v0.56.0 // indirect
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.62.0 // indirect
go.opentelemetry.io/contrib/instrumentation/runtime v0.62.0 // indirect
go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploghttp v0.13.0 // indirect
go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.32.0 // indirect
go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp v1.37.0 // indirect
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.37.0 // indirect
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.35.0 // indirect
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.37.0 // indirect
go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.37.0 // indirect
go.opentelemetry.io/otel/log v0.13.0 // indirect
go.opentelemetry.io/otel/sdk v1.37.0 // indirect
go.opentelemetry.io/otel/sdk/log v0.13.0 // indirect
go.opentelemetry.io/otel/sdk/metric v1.37.0 // indirect
go.opentelemetry.io/otel/trace v1.37.0 // indirect
go.opentelemetry.io/proto/otlp v1.7.0 // indirect
go.step.sm/crypto v0.60.0 // indirect
go.uber.org/mock v0.5.0 // indirect
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.49.0 // indirect
go.opentelemetry.io/contrib/instrumentation/net/http/httptrace/otelhttptrace v0.44.0 // indirect
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.53.0 // indirect
go.opentelemetry.io/contrib/instrumentation/runtime v0.55.0 // indirect
go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploghttp v0.6.0 // indirect
go.opentelemetry.io/otel/exporters/otlp/otlpmetric v0.41.0 // indirect
go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v0.41.0 // indirect
go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp v1.30.0 // indirect
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.30.0 // indirect
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.28.0 // indirect
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.30.0 // indirect
go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.30.0 // indirect
go.opentelemetry.io/otel/log v0.6.0 // indirect
go.opentelemetry.io/otel/sdk v1.30.0 // indirect
go.opentelemetry.io/otel/sdk/log v0.6.0 // indirect
go.opentelemetry.io/otel/sdk/metric v1.30.0 // indirect
go.opentelemetry.io/otel/trace v1.30.0 // indirect
go.opentelemetry.io/proto/otlp v1.3.1 // indirect
go.starlark.net v0.0.0-20230525235612-a134d8f9ddca // indirect
go.step.sm/crypto v0.44.2 // indirect
go.uber.org/multierr v1.11.0 // indirect
go.uber.org/zap v1.27.0 // indirect
go.yaml.in/yaml/v2 v2.4.2 // indirect
go.yaml.in/yaml/v3 v3.0.4 // indirect
go4.org v0.0.0-20230225012048-214862532bf5 // indirect
go4.org/netipx v0.0.0-20231129151722-fdeea329fbba // indirect
golang.org/x/crypto v0.41.0 // indirect
golang.org/x/exp v0.0.0-20250711185948-6ae5c78190dc // indirect
golang.org/x/crypto v0.32.0 // indirect
golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56 // indirect
golang.org/x/image v0.24.0 // indirect
golang.org/x/net v0.43.0 // indirect
golang.org/x/oauth2 v0.30.0 // indirect
golang.org/x/sync v0.17.0 // indirect
golang.org/x/sys v0.35.0 // indirect
golang.org/x/text v0.29.0 // indirect
golang.org/x/time v0.12.0 // indirect
golang.org/x/tools v0.36.0 // indirect
golang.org/x/xerrors v0.0.0-20240903120638-7835f813f4da // indirect
gonum.org/v1/gonum v0.12.0 // indirect
google.golang.org/api v0.242.0 // indirect
google.golang.org/genproto v0.0.0-20250715232539-7130f93afb79 // indirect
google.golang.org/genproto/googleapis/api v0.0.0-20250715232539-7130f93afb79 // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20250715232539-7130f93afb79 // indirect
google.golang.org/grpc v1.74.0 // indirect
google.golang.org/protobuf v1.36.6 // indirect
golang.org/x/net v0.33.0 // indirect
golang.org/x/oauth2 v0.23.0 // indirect
golang.org/x/sync v0.11.0 // indirect
golang.org/x/sys v0.29.0 // indirect
golang.org/x/text v0.22.0 // indirect
golang.org/x/time v0.7.0 // indirect
golang.org/x/xerrors v0.0.0-20231012003039-104605ab7028 // indirect
gonum.org/v1/gonum v0.9.1 // indirect
google.golang.org/api v0.172.0 // indirect
google.golang.org/genproto v0.0.0-20240311173647-c811ad7063a7 // indirect
google.golang.org/genproto/googleapis/api v0.0.0-20240903143218-8af14fe29dc1 // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20240903143218-8af14fe29dc1 // indirect
google.golang.org/grpc v1.67.0 // indirect
google.golang.org/protobuf v1.35.1 // indirect
gopkg.in/evanphx/json-patch.v4 v4.12.0 // indirect
gopkg.in/go-jose/go-jose.v2 v2.6.3 // indirect
gopkg.in/inf.v0 v0.9.1 // indirect
gopkg.in/ini.v1 v1.67.0 // indirect
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.30.2 // indirect
k8s.io/apiextensions-apiserver v0.33.3 // indirect
k8s.io/apiserver v0.33.3 // indirect
k8s.io/cli-runtime v0.33.3 // indirect
k8s.io/component-base v0.33.3 // indirect
gorm.io/gorm v1.25.10 // indirect
k8s.io/apiextensions-apiserver v0.29.0 // indirect
k8s.io/klog/v2 v2.130.1 // indirect
k8s.io/kube-openapi v0.0.0-20250318190949-c8a335a9a2ff // indirect
k8s.io/kubectl v0.33.3 // indirect
modernc.org/libc v1.66.3 // indirect
modernc.org/mathutil v1.7.1 // indirect
modernc.org/memory v1.11.0 // indirect
modernc.org/sqlite v1.38.2 // indirect
oras.land/oras-go/v2 v2.6.0 // indirect
sigs.k8s.io/controller-runtime v0.18.4 // indirect
sigs.k8s.io/json v0.0.0-20241014173422-cfa47c3a1cc8 // indirect
sigs.k8s.io/randfill v1.0.0 // indirect
sigs.k8s.io/release-utils v0.9.0 // indirect
sigs.k8s.io/structured-merge-diff/v4 v4.6.0 // indirect
k8s.io/kube-openapi v0.0.0-20241212222426-2c72e554b1e7 // indirect
modernc.org/libc v1.49.3 // indirect
modernc.org/mathutil v1.6.0 // indirect
modernc.org/memory v1.8.0 // indirect
modernc.org/sqlite v1.29.8 // indirect
sigs.k8s.io/controller-runtime v0.15.0 // indirect
sigs.k8s.io/json v0.0.0-20241010143419-9aa6b5e7a4b3 // indirect
sigs.k8s.io/release-utils v0.7.7 // indirect
sigs.k8s.io/structured-merge-diff/v4 v4.5.0 // indirect
)
replace github.com/anchore/stereoscope => github.com/matthyx/stereoscope v0.0.0-20250916161743-dd57158479de
// Using the forked version of tablewriter
replace github.com/olekukonko/tablewriter => github.com/kubescape/tablewriter v0.0.6-0.20231106230230-aac7d2659c94
replace github.com/google/go-containerregistry => github.com/matthyx/go-containerregistry v0.0.0-20250916162850-293c5b36a9f8
replace github.com/anchore/stereoscope => github.com/matthyx/stereoscope v0.0.0-20240426103125-b762a3538c32
replace github.com/google/go-containerregistry => github.com/matthyx/go-containerregistry v0.0.0-20240227132928-63ceb71ae0b9
replace github.com/docker/distribution v2.8.3+incompatible => github.com/docker/distribution v2.8.2+incompatible
replace github.com/mholt/archiver/v3 v3.5.1 => github.com/anchore/archiver/v3 v3.5.2

2248
go.sum

File diff suppressed because it is too large Load Diff

View File

@@ -99,7 +99,7 @@ spec:
fieldPath: metadata.namespace
- name: "KS_SKIP_UPDATE_CHECK" # do not check latest version
value: "true"
- name: KS_ENABLE_HOST_SCANNER # enable host scanner -> https://kubescape.io/docs/components/host-sensor/
- name: KS_ENABLE_HOST_SCANNER # enable host scanner -> https://hub.armosec.io/docs/host-sensor
value: "true"
- name: KS_DOWNLOAD_ARTIFACTS # When set to true the artifacts will be downloaded every scan execution
value: "true"

View File

@@ -1,104 +1,93 @@
module github.com/kubescape/kubescape/v3/httphandler
go 1.24.1
go 1.23.0
toolchain go1.24.6
toolchain go1.23.4
replace github.com/kubescape/kubescape/v3 => ../
require (
github.com/armosec/armoapi-go v0.0.562
github.com/armosec/utils-go v0.0.58
github.com/armosec/utils-k8s-go v0.0.30
github.com/armosec/armoapi-go v0.0.330
github.com/armosec/utils-go v0.0.57
github.com/armosec/utils-k8s-go v0.0.26
github.com/go-openapi/runtime v0.28.0
github.com/google/uuid v1.6.0
github.com/gorilla/mux v1.8.1
github.com/gorilla/schema v1.4.1
github.com/kubescape/backend v0.0.20
github.com/kubescape/go-logger v0.0.25
github.com/kubescape/k8s-interface v0.0.195
github.com/kubescape/go-logger v0.0.23
github.com/kubescape/k8s-interface v0.0.174
github.com/kubescape/kubescape/v3 v3.0.4
github.com/kubescape/opa-utils v0.0.288
github.com/kubescape/storage v0.0.184
github.com/spf13/viper v1.20.1
github.com/stretchr/testify v1.11.1
github.com/kubescape/opa-utils v0.0.287
github.com/kubescape/storage v0.0.111
github.com/spf13/viper v1.18.2
github.com/stretchr/testify v1.10.0
go.opentelemetry.io/contrib/instrumentation/github.com/gorilla/mux/otelmux v0.45.0
go.opentelemetry.io/otel v1.37.0
k8s.io/apimachinery v0.33.3
k8s.io/client-go v0.33.3
go.opentelemetry.io/otel v1.30.0
k8s.io/apimachinery v0.32.2
k8s.io/client-go v0.32.2
k8s.io/utils v0.0.0-20241210054802-24370beab758
)
require (
go.opentelemetry.io/otel/trace v1.37.0
go.opentelemetry.io/otel/trace v1.30.0
go.uber.org/multierr v1.11.0 // indirect
go.uber.org/zap v1.27.0 // indirect
golang.org/x/crypto v0.41.0 // indirect
golang.org/x/exp v0.0.0-20250711185948-6ae5c78190dc // indirect
golang.org/x/mod v0.27.0 // indirect
golang.org/x/net v0.43.0 // indirect
golang.org/x/oauth2 v0.30.0 // indirect
google.golang.org/genproto v0.0.0-20250715232539-7130f93afb79 // indirect
google.golang.org/grpc v1.74.0 // indirect
golang.org/x/crypto v0.32.0 // indirect
golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56 // indirect
golang.org/x/mod v0.20.0 // indirect
golang.org/x/net v0.33.0 // indirect
golang.org/x/oauth2 v0.23.0 // indirect
google.golang.org/genproto v0.0.0-20240311173647-c811ad7063a7 // indirect
google.golang.org/grpc v1.67.0 // indirect
)
require (
cel.dev/expr v0.24.0 // indirect
cloud.google.com/go v0.121.3 // indirect
cloud.google.com/go/auth v0.16.2 // indirect
cloud.google.com/go/auth/oauth2adapt v0.2.8 // indirect
cloud.google.com/go/compute/metadata v0.7.0 // indirect
cloud.google.com/go/container v1.43.0 // indirect
cloud.google.com/go/iam v1.5.2 // indirect
cloud.google.com/go/monitoring v1.24.2 // indirect
cloud.google.com/go/storage v1.55.0 // indirect
dario.cat/mergo v1.0.2 // indirect
cloud.google.com/go v0.112.1 // indirect
cloud.google.com/go/compute/metadata v0.5.0 // indirect
cloud.google.com/go/container v1.33.0 // indirect
cloud.google.com/go/iam v1.1.6 // indirect
cloud.google.com/go/storage v1.39.1 // indirect
dario.cat/mergo v1.0.0 // indirect
filippo.io/edwards25519 v1.1.0 // indirect
github.com/AdaLogics/go-fuzz-headers v0.0.0-20240806141605-e8a1dd7889d6 // indirect
github.com/AdamKorcz/go-118-fuzz-build v0.0.0-20250520111509-a70c2aa677fa // indirect
github.com/AdaLogics/go-fuzz-headers v0.0.0-20230811130428-ced1acdcaa24 // indirect
github.com/AdamKorcz/go-118-fuzz-build v0.0.0-20230306123547-8075edf89bb0 // indirect
github.com/AliyunContainerService/ack-ram-tool/pkg/credentials/alibabacloudsdkgo/helper v0.2.0 // indirect
github.com/Azure/azure-sdk-for-go v68.0.0+incompatible // indirect
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.17.1 // indirect
github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.8.2 // indirect
github.com/Azure/azure-sdk-for-go/sdk/internal v1.10.0 // indirect
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.11.1 // indirect
github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.6.0 // indirect
github.com/Azure/azure-sdk-for-go/sdk/internal v1.8.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/go-ansiterm v0.0.0-20250102033503-faa5f7b0171c // indirect
github.com/Azure/go-ansiterm v0.0.0-20230124172434-306776ec8161 // indirect
github.com/Azure/go-autorest v14.2.0+incompatible // indirect
github.com/Azure/go-autorest/autorest v0.11.29 // indirect
github.com/Azure/go-autorest/autorest/adal v0.9.24 // indirect
github.com/Azure/go-autorest/autorest/azure/auth v0.5.13 // indirect
github.com/Azure/go-autorest/autorest/adal v0.9.23 // indirect
github.com/Azure/go-autorest/autorest/azure/auth v0.5.12 // indirect
github.com/Azure/go-autorest/autorest/azure/cli v0.4.6 // indirect
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.3.3 // indirect
github.com/BurntSushi/toml v1.5.0 // indirect
github.com/CycloneDX/cyclonedx-go v0.9.2 // indirect
github.com/DataDog/zstd v1.5.7 // indirect
github.com/GoogleCloudPlatform/opentelemetry-operations-go/detectors/gcp v1.29.0 // indirect
github.com/GoogleCloudPlatform/opentelemetry-operations-go/exporter/metric v0.53.0 // indirect
github.com/GoogleCloudPlatform/opentelemetry-operations-go/internal/resourcemapping v0.53.0 // indirect
github.com/Intevation/gval v1.3.0 // indirect
github.com/Intevation/jsonpath v0.2.1 // indirect
github.com/MakeNowJust/heredoc v1.0.0 // indirect
github.com/AzureAD/microsoft-authentication-library-for-go v1.2.2 // indirect
github.com/BurntSushi/toml v1.4.0 // indirect
github.com/CycloneDX/cyclonedx-go v0.8.0 // indirect
github.com/DataDog/zstd v1.5.5 // indirect
github.com/Masterminds/goutils v1.1.1 // indirect
github.com/Masterminds/semver v1.5.0 // indirect
github.com/Masterminds/semver/v3 v3.4.0 // indirect
github.com/Masterminds/sprig/v3 v3.3.0 // indirect
github.com/Masterminds/semver/v3 v3.2.1 // indirect
github.com/Masterminds/sprig/v3 v3.2.3 // indirect
github.com/Microsoft/go-winio v0.6.2 // indirect
github.com/Microsoft/hcsshim v0.13.0 // indirect
github.com/Microsoft/hcsshim v0.12.5 // indirect
github.com/OneOfOne/xxhash v1.2.8 // indirect
github.com/ProtonMail/go-crypto v1.3.0 // indirect
github.com/STARRY-S/zip v0.2.3 // indirect
github.com/ProtonMail/go-crypto v1.1.3 // indirect
github.com/ThalesIgnite/crypto11 v1.2.5 // indirect
github.com/a8m/envsubst v1.3.0 // indirect
github.com/acobaugh/osrelease v0.1.0 // indirect
github.com/adrg/xdg v0.5.3 // indirect
github.com/agext/levenshtein v1.2.3 // indirect
github.com/agnivade/levenshtein v1.2.1 // indirect
github.com/alecthomas/participle/v2 v2.1.0 // indirect
github.com/adrg/xdg v0.4.0 // indirect
github.com/agl/ed25519 v0.0.0-20170116200512-5312a6153412 // 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
github.com/alibabacloud-go/cr-20160607 v1.0.1 // indirect
github.com/alibabacloud-go/cr-20181201 v1.0.10 // indirect
@@ -110,216 +99,194 @@ require (
github.com/alibabacloud-go/tea-utils v1.4.5 // indirect
github.com/alibabacloud-go/tea-xml v1.1.3 // indirect
github.com/aliyun/credentials-go v1.3.1 // indirect
github.com/anchore/archiver/v3 v3.5.3-0.20241210171143-5b1d8d1c7c51 // indirect
github.com/anchore/clio v0.0.0-20250715152405-a0fa658e5084 // indirect
github.com/anchore/fangs v0.0.0-20250716230140-94c22408c232 // indirect
github.com/anchore/go-collections v0.0.0-20241211140901-567f400e9a46 // indirect
github.com/anchore/go-homedir v0.0.0-20250319154043-c29668562e4d // indirect
github.com/anchore/go-logger v0.0.0-20250318195838-07ae343dd722 // indirect
github.com/anchore/go-lzo v0.1.0 // indirect
github.com/anchore/go-macholibre v0.0.0-20250320151634-807da7ad2331 // indirect
github.com/anchore/go-rpmdb v0.0.0-20250516171929-f77691e1faec // indirect
github.com/anchore/go-struct-converter v0.0.0-20250211213226-cce56d595160 // indirect
github.com/anchore/go-sync v0.0.0-20250714163430-add63db73ad1 // indirect
github.com/anchore/clio v0.0.0-20240209204744-cb94e40a4f65 // indirect
github.com/anchore/fangs v0.0.0-20231201140849-5075d28d6d8b // indirect
github.com/anchore/go-collections v0.0.0-20240216171411-9321230ce537 // indirect
github.com/anchore/go-logger v0.0.0-20230725134548-c21dafa1ec5a // 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/grype v0.99.1 // indirect
github.com/anchore/packageurl-go v0.1.1-0.20250220190351-d62adb6e1115 // indirect
github.com/anchore/stereoscope v0.1.9 // indirect
github.com/anchore/syft v1.32.0 // indirect
github.com/andybalholm/brotli v1.2.0 // indirect
github.com/anchore/grype v0.77.1 // indirect
github.com/anchore/packageurl-go v0.1.1-0.20240312213626-055233e539b4 // indirect
github.com/anchore/stereoscope v0.0.3-0.20240423181235-8b297badafd5 // indirect
github.com/anchore/syft v1.3.0 // indirect
github.com/andybalholm/brotli v1.0.4 // indirect
github.com/anubhav06/copa-grype v1.0.3-alpha.1 // indirect
github.com/apparentlymart/go-textseg/v15 v15.0.0 // indirect
github.com/aquasecurity/go-pep440-version v0.0.1 // indirect
github.com/aquasecurity/go-version v0.0.1 // indirect
github.com/armosec/gojay v1.2.17 // indirect
github.com/aquasecurity/go-pep440-version v0.0.0-20210121094942-22b2f8951d46 // indirect
github.com/aquasecurity/go-version v0.0.0-20210121072130-637058cfe492 // indirect
github.com/armosec/gojay v1.2.15 // indirect
github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2 // indirect
github.com/aws/aws-sdk-go v1.55.7 // indirect
github.com/aws/aws-sdk-go-v2 v1.36.5 // indirect
github.com/aws/aws-sdk-go-v2/config v1.29.17 // indirect
github.com/aws/aws-sdk-go-v2/credentials v1.17.70 // indirect
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.32 // indirect
github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.36 // indirect
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.36 // indirect
github.com/aws/aws-sdk-go-v2/internal/ini v1.8.3 // indirect
github.com/aws/aws-sdk-go-v2/service/ecr v1.45.1 // indirect
github.com/aws/aws-sdk-go-v2/service/ecrpublic v1.33.2 // indirect
github.com/aws/aws-sdk-go v1.55.6-0.20240912145455-7112c0a0c2d0 // indirect
github.com/aws/aws-sdk-go-v2 v1.30.5 // indirect
github.com/aws/aws-sdk-go-v2/config v1.27.35 // indirect
github.com/aws/aws-sdk-go-v2/credentials v1.17.33 // indirect
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.13 // indirect
github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.17 // indirect
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.17 // indirect
github.com/aws/aws-sdk-go-v2/internal/ini v1.8.1 // indirect
github.com/aws/aws-sdk-go-v2/service/ecr v1.34.0 // indirect
github.com/aws/aws-sdk-go-v2/service/ecrpublic v1.25.7 // indirect
github.com/aws/aws-sdk-go-v2/service/eks v1.48.5 // indirect
github.com/aws/aws-sdk-go-v2/service/iam v1.35.3 // indirect
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.12.4 // indirect
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.12.17 // indirect
github.com/aws/aws-sdk-go-v2/service/sso v1.25.5 // indirect
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.30.3 // indirect
github.com/aws/aws-sdk-go-v2/service/sts v1.34.0 // indirect
github.com/aws/smithy-go v1.22.4 // indirect
github.com/awslabs/amazon-ecr-credential-helper/ecr-login v0.10.1 // indirect
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.11.4 // indirect
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.11.19 // indirect
github.com/aws/aws-sdk-go-v2/service/sso v1.22.8 // indirect
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.26.8 // indirect
github.com/aws/aws-sdk-go-v2/service/sts v1.30.8 // indirect
github.com/aws/smithy-go v1.20.4 // indirect
github.com/awslabs/amazon-ecr-credential-helper/ecr-login v0.0.0-20231024185945-8841054dbdb8 // indirect
github.com/aymanbagabas/go-osc52/v2 v2.0.1 // indirect
github.com/becheran/wildmatch-go v1.0.0 // indirect
github.com/beorn7/perks v1.0.1 // indirect
github.com/bgentry/go-netrc v0.0.0-20140422174119-9fd32a8b3d3d // indirect
github.com/bitnami/go-version v0.0.0-20250505154626-452e8c5ee607 // indirect
github.com/blakesmith/ar v0.0.0-20190502131153-809d4375e1fb // indirect
github.com/blang/semver v3.5.1+incompatible // indirect
github.com/blang/semver/v4 v4.0.0 // indirect
github.com/bmatcuk/doublestar/v2 v2.0.4 // indirect
github.com/bmatcuk/doublestar/v4 v4.9.1 // indirect
github.com/bodgit/plumbing v1.3.0 // indirect
github.com/bodgit/sevenzip v1.6.1 // indirect
github.com/bodgit/windows v1.0.1 // indirect
github.com/bmatcuk/doublestar/v4 v4.6.1 // indirect
github.com/boombuler/barcode v1.0.2 // indirect
github.com/briandowns/spinner v1.23.2 // indirect
github.com/briandowns/spinner v1.23.1 // indirect
github.com/buildkite/agent/v3 v3.62.0 // indirect
github.com/buildkite/go-pipeline v0.3.2 // indirect
github.com/buildkite/interpolate v0.0.0-20200526001904-07f35b4ae251 // indirect
github.com/cenkalti/backoff v2.2.1+incompatible // indirect
github.com/cenkalti/backoff/v4 v4.3.0 // indirect
github.com/cenkalti/backoff/v5 v5.0.2 // indirect
github.com/cespare/xxhash/v2 v2.3.0 // indirect
github.com/chai2010/gettext-go v1.0.2 // indirect
github.com/chainguard-dev/git-urls v1.0.2 // indirect
github.com/charmbracelet/colorprofile v0.3.1 // indirect
github.com/charmbracelet/lipgloss v1.1.0 // indirect
github.com/charmbracelet/x/ansi v0.9.3 // indirect
github.com/charmbracelet/x/cellbuf v0.0.13 // indirect
github.com/charmbracelet/x/term v0.2.1 // indirect
github.com/charmbracelet/lipgloss v0.10.0 // indirect
github.com/chrismellard/docker-credential-acr-env v0.0.0-20230304212654-82a0ddb27589 // indirect
github.com/cilium/cilium v1.16.9 // indirect
github.com/clbanning/mxj/v2 v2.7.0 // indirect
github.com/cloudflare/circl v1.6.1 // indirect
github.com/cncf/xds/go v0.0.0-20250501225837-2ac532fd4443 // indirect
github.com/cloudflare/circl v1.3.7 // indirect
github.com/common-nighthawk/go-figure v0.0.0-20210622060536-734e95fb86be // indirect
github.com/containerd/cgroups/v3 v3.0.5 // indirect
github.com/containerd/console v1.0.4 // indirect
github.com/containerd/containerd v1.7.28 // indirect
github.com/containerd/containerd/api v1.9.0 // indirect
github.com/containerd/containerd/v2 v2.0.5 // indirect
github.com/containerd/continuity v0.4.5 // indirect
github.com/containerd/errdefs v1.0.0 // indirect
github.com/containerd/errdefs/pkg v0.3.0 // indirect
github.com/containerd/cgroups/v3 v3.0.3 // indirect
github.com/containerd/console v1.0.4-0.20230313162750-1ae8d489ac81 // indirect
github.com/containerd/containerd v1.7.21 // indirect
github.com/containerd/containerd/api v1.7.19 // indirect
github.com/containerd/continuity v0.4.2 // indirect
github.com/containerd/errdefs v0.1.0 // indirect
github.com/containerd/fifo v1.1.0 // indirect
github.com/containerd/log v0.1.0 // indirect
github.com/containerd/platforms v1.0.0-rc.1 // indirect
github.com/containerd/stargz-snapshotter/estargz v0.16.3 // indirect
github.com/containerd/ttrpc v1.2.7 // indirect
github.com/containerd/typeurl/v2 v2.2.3 // indirect
github.com/containers/common v0.63.0 // indirect
github.com/coreos/go-oidc/v3 v3.14.1 // indirect
github.com/cpuguy83/dockercfg v0.3.2 // indirect
github.com/cpuguy83/go-docker v0.3.0 // indirect
github.com/cyberphone/json-canonicalization v0.0.0-20241213102144-19d51d7fe467 // indirect
github.com/cyphar/filepath-securejoin v0.4.1 // indirect
github.com/containerd/platforms v0.2.1 // indirect
github.com/containerd/stargz-snapshotter/estargz v0.15.1 // indirect
github.com/containerd/ttrpc v1.2.5 // indirect
github.com/containerd/typeurl/v2 v2.1.1 // indirect
github.com/containers/common v0.60.4 // indirect
github.com/coreos/go-oidc v2.2.1+incompatible // indirect
github.com/coreos/go-oidc/v3 v3.10.0 // indirect
github.com/cpuguy83/dockercfg v0.3.1 // indirect
github.com/cpuguy83/go-docker v0.2.1 // indirect
github.com/cyberphone/json-canonicalization v0.0.0-20231217050601-ba74d44ecf5f // indirect
github.com/cyphar/filepath-securejoin v0.3.1 // indirect
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
github.com/deitch/magic v0.0.0-20240306090643-c67ab88f10cb // indirect
github.com/deitch/magic v0.0.0-20230404182410-1ff89d7342da // indirect
github.com/digitorus/pkcs7 v0.0.0-20230818184609-3a137a874352 // indirect
github.com/digitorus/timestamp v0.0.0-20231217203849-220c5c2851b7 // indirect
github.com/dimchansky/utfbom v1.1.1 // indirect
github.com/diskfs/go-diskfs v1.7.0 // indirect
github.com/distribution/reference v0.6.0 // indirect
github.com/docker/buildx v0.21.3 // indirect
github.com/docker/cli v28.3.3+incompatible // indirect
github.com/docker/buildx v0.11.2 // indirect
github.com/docker/cli v26.1.0+incompatible // indirect
github.com/docker/distribution v2.8.3+incompatible // indirect
github.com/docker/docker v28.3.3+incompatible // indirect
github.com/docker/docker-credential-helpers v0.9.3 // indirect
github.com/docker/go-connections v0.6.0 // indirect
github.com/docker/go-events v0.0.0-20250114142523-c867878c5e32 // indirect
github.com/docker/docker v27.1.1+incompatible // indirect
github.com/docker/docker-credential-helpers v0.8.2 // indirect
github.com/docker/go v1.5.1-1.0.20160303222718-d30aec9fd63c // indirect
github.com/docker/go-connections v0.5.0 // indirect
github.com/docker/go-events v0.0.0-20190806004212-e31b211e4f1c // indirect
github.com/docker/go-metrics v0.0.1 // indirect
github.com/docker/go-units v0.5.0 // indirect
github.com/dsnet/compress v0.0.2-0.20230904184137-39efe44ab707 // indirect
github.com/dsnet/compress v0.0.2-0.20210315054119-f66993602bf5 // indirect
github.com/dustin/go-humanize v1.0.1 // indirect
github.com/edsrzf/mmap-go v1.1.0 // indirect
github.com/elliotchance/orderedmap v1.5.0 // indirect
github.com/elliotchance/phpserialize v1.4.0 // indirect
github.com/emicklei/go-restful/v3 v3.12.0 // indirect
github.com/emicklei/go-restful/v3 v3.11.0 // indirect
github.com/emirpasic/gods v1.18.1 // indirect
github.com/enescakir/emoji v1.0.0 // indirect
github.com/envoyproxy/go-control-plane/envoy v1.32.4 // indirect
github.com/envoyproxy/protoc-gen-validate v1.2.1 // indirect
github.com/evanphx/json-patch v5.9.11+incompatible // indirect
github.com/exponent-io/jsonpath v0.0.0-20210407135951-1de76d718b3f // indirect
github.com/evanphx/json-patch v5.7.0+incompatible // indirect
github.com/f-amaral/go-async v0.3.0 // indirect
github.com/facebookincubator/nvdtools v0.1.5 // indirect
github.com/fatih/color v1.18.0 // indirect
github.com/felixge/fgprof v0.9.5 // indirect
github.com/fatih/color v1.17.0 // indirect
github.com/felixge/fgprof v0.9.3 // indirect
github.com/felixge/httpsnoop v1.0.4 // indirect
github.com/francoispqt/gojay v1.2.13 // indirect
github.com/fsnotify/fsnotify v1.9.0 // indirect
github.com/fsnotify/fsnotify v1.7.0 // indirect
github.com/fvbommel/sortorder v1.1.0 // indirect
github.com/fxamacker/cbor/v2 v2.7.0 // indirect
github.com/gabriel-vasile/mimetype v1.4.10 // indirect
github.com/github/go-spdx/v2 v2.3.3 // indirect
github.com/glebarez/go-sqlite v1.22.0 // indirect
github.com/gabriel-vasile/mimetype v1.4.3 // indirect
github.com/github/go-spdx/v2 v2.2.0 // indirect
github.com/glebarez/go-sqlite v1.21.2 // indirect
github.com/glebarez/sqlite v1.11.0 // indirect
github.com/go-chi/chi v4.1.2+incompatible // indirect
github.com/go-errors/errors v1.4.2 // indirect
github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 // indirect
github.com/go-git/go-billy/v5 v5.6.2 // indirect
github.com/go-git/go-git/v5 v5.16.2 // indirect
github.com/go-git/go-billy/v5 v5.6.0 // indirect
github.com/go-git/go-git/v5 v5.13.0 // indirect
github.com/go-gota/gota v0.12.0 // indirect
github.com/go-ini/ini v1.67.0 // indirect
github.com/go-jose/go-jose/v3 v3.0.4 // indirect
github.com/go-jose/go-jose/v4 v4.0.5 // indirect
github.com/go-logr/logr v1.4.3 // indirect
github.com/go-logr/logr v1.4.2 // indirect
github.com/go-logr/stdr v1.2.2 // indirect
github.com/go-openapi/analysis v0.23.0 // indirect
github.com/go-openapi/errors v0.22.1 // indirect
github.com/go-openapi/errors v0.22.0 // indirect
github.com/go-openapi/jsonpointer v0.21.0 // indirect
github.com/go-openapi/jsonreference v0.21.0 // indirect
github.com/go-openapi/loads v0.22.0 // indirect
github.com/go-openapi/spec v0.21.0 // indirect
github.com/go-openapi/strfmt v0.23.0 // indirect
github.com/go-openapi/swag v0.23.1 // indirect
github.com/go-openapi/swag v0.23.0 // indirect
github.com/go-openapi/validate v0.24.0 // indirect
github.com/go-piv/piv-go v1.11.0 // indirect
github.com/go-restruct/restruct v1.2.0-alpha // indirect
github.com/go-viper/mapstructure/v2 v2.4.0 // indirect
github.com/go-test/deep v1.1.0 // indirect
github.com/gobwas/glob v0.2.3 // indirect
github.com/goccy/go-json v0.10.2 // indirect
github.com/goccy/go-yaml v1.18.0 // indirect
github.com/gocsaf/csaf/v3 v3.3.0 // indirect
github.com/gofrs/flock v0.12.1 // indirect
github.com/goccy/go-yaml v1.9.6 // indirect
github.com/gofrs/flock v0.8.1 // indirect
github.com/gogo/googleapis v1.4.1 // indirect
github.com/gogo/protobuf v1.3.2 // indirect
github.com/gohugoio/hashstructure v0.5.0 // indirect
github.com/golang-jwt/jwt/v4 v4.5.2 // indirect
github.com/golang-jwt/jwt/v5 v5.2.2 // indirect
github.com/golang/groupcache v0.0.0-20241129210726-2c02b8208cf8 // indirect
github.com/golang-jwt/jwt/v4 v4.5.1 // indirect
github.com/golang-jwt/jwt/v5 v5.2.1 // indirect
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect
github.com/golang/protobuf v1.5.4 // indirect
github.com/golang/snappy v1.0.0 // indirect
github.com/google/btree v1.1.3 // indirect
github.com/google/certificate-transparency-go v1.3.1 // indirect
github.com/golang/snappy v0.0.4 // indirect
github.com/google/certificate-transparency-go v1.1.8 // indirect
github.com/google/gnostic-models v0.6.9 // indirect
github.com/google/go-cmp v0.7.0 // indirect
github.com/google/go-containerregistry v0.20.6 // indirect
github.com/google/go-cmp v0.6.0 // indirect
github.com/google/go-containerregistry v0.20.0 // indirect
github.com/google/go-github/v55 v55.0.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/pprof v0.0.0-20250630185457-6e76a2b096b5 // indirect
github.com/google/s2a-go v0.1.9 // indirect
github.com/google/pprof v0.0.0-20241029153458-d1b30febd7db // indirect
github.com/google/s2a-go v0.1.7 // indirect
github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 // indirect
github.com/googleapis/enterprise-certificate-proxy v0.3.6 // indirect
github.com/googleapis/gax-go/v2 v2.15.0 // indirect
github.com/gookit/color v1.6.0 // indirect
github.com/gorilla/websocket v1.5.4-0.20250319132907-e064f32e3674 // indirect
github.com/gregjones/httpcache v0.0.0-20190611155906-901d90724c79 // indirect
github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.1 // indirect
github.com/googleapis/enterprise-certificate-proxy v0.3.2 // indirect
github.com/googleapis/gax-go/v2 v2.12.3 // indirect
github.com/gookit/color v1.5.4 // indirect
github.com/gorilla/websocket v1.5.0 // indirect
github.com/grpc-ecosystem/go-grpc-middleware v1.4.0 // indirect
github.com/grpc-ecosystem/grpc-gateway/v2 v2.22.0 // 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.9 // indirect
github.com/hashicorp/go-getter v1.7.5 // indirect
github.com/hashicorp/go-multierror v1.1.1 // indirect
github.com/hashicorp/go-retryablehttp v0.7.7 // indirect
github.com/hashicorp/go-safetemp v1.0.0 // indirect
github.com/hashicorp/go-version v1.7.0 // indirect
github.com/hashicorp/golang-lru/v2 v2.0.7 // indirect
github.com/hashicorp/hcl/v2 v2.24.0 // indirect
github.com/hashicorp/go-version v1.6.0 // indirect
github.com/hashicorp/hcl v1.0.1-vault-5 // indirect
github.com/hhrutter/lzw v1.0.0 // indirect
github.com/hhrutter/tiff v1.0.1 // indirect
github.com/huandu/xstrings v1.5.0 // indirect
github.com/huandu/xstrings v1.4.0 // indirect
github.com/iancoleman/strcase v0.3.0 // indirect
github.com/imdario/mergo v0.3.16 // indirect
github.com/in-toto/in-toto-golang v0.9.0 // indirect
github.com/inconshreveable/mousetrap v1.1.0 // indirect
github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 // indirect
github.com/jedib0t/go-pretty/v6 v6.6.8 // indirect
github.com/jedisct1/go-minisign v0.0.0-20230811132847-661be99b8267 // indirect
github.com/jinzhu/copier v0.4.0 // indirect
github.com/jinzhu/inflection v1.0.0 // indirect
github.com/jinzhu/now v1.1.5 // indirect
github.com/jmespath/go-jmespath v0.4.1-0.20220621161143-b0104c826a24 // indirect
github.com/jmespath/go-jmespath v0.4.0 // indirect
github.com/johnfercher/go-tree v1.1.0 // indirect
github.com/johnfercher/maroto/v2 v2.2.2 // indirect
github.com/josharian/intern v1.0.0 // indirect
@@ -329,256 +296,229 @@ require (
github.com/jwalton/go-supportscolor v1.1.0 // indirect
github.com/kastenhq/goversion v0.0.0-20230811215019-93b2f8823953 // indirect
github.com/kevinburke/ssh_config v1.2.0 // indirect
github.com/klauspost/compress v1.18.0 // indirect
github.com/klauspost/compress v1.17.9 // indirect
github.com/klauspost/pgzip v1.2.6 // indirect
github.com/knqyf263/go-apk-version v0.0.0-20200609155635-041fdbb8563f // indirect
github.com/knqyf263/go-deb-version v0.0.0-20241115132648-6f4aee6ccd23 // indirect
github.com/knqyf263/go-deb-version v0.0.0-20230223133812-3ed183d23422 // indirect
github.com/knqyf263/go-rpm-version v0.0.0-20220614171824-631e686d1075 // indirect
github.com/knqyf263/go-rpmdb v0.1.0 // indirect
github.com/kubescape/go-git-url v0.0.30 // indirect
github.com/kubescape/rbac-utils v0.0.21-0.20230806101615-07e36f555520 // indirect
github.com/kubescape/regolibrary/v2 v2.0.1 // indirect
github.com/kylelemons/godebug v1.1.0 // indirect
github.com/letsencrypt/boulder v0.0.0-20240620165639-de9c06129bec // indirect
github.com/liggitt/tabwriter v0.0.0-20181228230101-89fcab3d43de // indirect
github.com/letsencrypt/boulder v0.0.0-20240418210053-89b07f4543e0 // indirect
github.com/lucasb-eyer/go-colorful v1.2.0 // indirect
github.com/mackerelio/go-osstat v0.2.5 // indirect
github.com/magiconair/properties v1.8.10 // indirect
github.com/mailru/easyjson v0.9.0 // indirect
github.com/magiconair/properties v1.8.7 // indirect
github.com/mailru/easyjson v0.7.7 // indirect
github.com/maruel/natural v1.1.1 // indirect
github.com/masahiro331/go-mvn-version v0.0.0-20250131095131-f4974fa13b8a // indirect
github.com/masahiro331/go-mvn-version v0.0.0-20210429150710-d3157d602a08 // indirect
github.com/matthyx/go-gitlog v0.0.0-20231005131906-9ffabe3c5bcd // indirect
github.com/mattn/go-colorable v0.1.14 // indirect
github.com/mattn/go-colorable v0.1.13 // indirect
github.com/mattn/go-isatty v0.0.20 // indirect
github.com/mattn/go-runewidth v0.0.16 // indirect
github.com/mgutz/ansi v0.0.0-20200706080929-d51e80ef957d // indirect
github.com/mholt/archives v0.1.5 // indirect
github.com/mholt/archiver/v3 v3.5.1 // indirect
github.com/microsoft/go-rustaudit v0.0.0-20220808201409-204dfee52032 // indirect
github.com/miekg/pkcs11 v1.1.1 // indirect
github.com/mikefarah/yq/v4 v4.29.1 // indirect
github.com/mikelolasagasti/xz v1.0.1 // indirect
github.com/minio/minlz v1.0.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-wordwrap v1.0.1 // 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/moby/buildkit v0.21.0 // indirect
github.com/moby/buildkit v0.12.5 // indirect
github.com/moby/docker-image-spec v1.3.1 // indirect
github.com/moby/locker v1.0.1 // indirect
github.com/moby/patternmatcher v0.6.0 // indirect
github.com/moby/spdystream v0.5.0 // indirect
github.com/moby/sys/atomicwriter v0.1.0 // indirect
github.com/moby/sys/mountinfo v0.7.2 // indirect
github.com/moby/sys/sequential v0.6.0 // indirect
github.com/moby/sys/signal v0.7.1 // indirect
github.com/moby/sys/user v0.4.0 // indirect
github.com/moby/sys/sequential v0.5.0 // indirect
github.com/moby/sys/signal v0.7.0 // indirect
github.com/moby/sys/user v0.3.0 // indirect
github.com/moby/sys/userns v0.1.0 // indirect
github.com/moby/term v0.5.2 // indirect
github.com/moby/term v0.5.0 // indirect
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
github.com/modern-go/reflect2 v1.0.2 // indirect
github.com/monochromegane/go-gitignore v0.0.0-20200626010858-205db1a8cc00 // indirect
github.com/morikuni/aec v1.0.0 // indirect
github.com/mozillazg/docker-credential-acr-helper v0.3.0 // indirect
github.com/muesli/termenv v0.16.0 // indirect
github.com/muesli/reflow v0.3.0 // indirect
github.com/muesli/termenv v0.15.2 // indirect
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f // indirect
github.com/ncruces/go-strftime v0.1.9 // indirect
github.com/nix-community/go-nix v0.0.0-20250101154619-4bdde671e0a1 // indirect
github.com/nozzle/throttler v0.0.0-20180817012639-2ea982251481 // indirect
github.com/nwaples/rardecode v1.1.3 // indirect
github.com/nwaples/rardecode/v2 v2.2.0 // indirect
github.com/nwaples/rardecode v1.1.0 // indirect
github.com/oklog/ulid v1.3.1 // indirect
github.com/oleiade/reflections v1.0.1 // indirect
github.com/olekukonko/errors v1.1.0 // indirect
github.com/olekukonko/ll v0.0.9 // indirect
github.com/olekukonko/tablewriter v1.0.9 // indirect
github.com/olekukonko/tablewriter v0.0.6-0.20230417144759-edd1a71a5576 // indirect
github.com/olvrng/ujson v1.1.0 // indirect
github.com/open-policy-agent/opa v1.4.0 // indirect
github.com/open-policy-agent/opa v0.68.0 // indirect
github.com/opencontainers/go-digest v1.0.0 // indirect
github.com/opencontainers/image-spec v1.1.1 // indirect
github.com/opencontainers/runtime-spec v1.2.1 // indirect
github.com/opencontainers/selinux v1.12.0 // indirect
github.com/opentracing/opentracing-go v1.2.1-0.20220228012449-10b1cf09e00b // indirect
github.com/opencontainers/image-spec v1.1.0 // indirect
github.com/opencontainers/runtime-spec v1.2.0 // indirect
github.com/opencontainers/selinux v1.11.0 // indirect
github.com/opentracing/opentracing-go v1.2.0 // indirect
github.com/openvex/go-vex v0.2.5 // indirect
github.com/owenrumney/go-sarif v1.1.2-0.20231003122901-1000f5e05554 // indirect
github.com/owenrumney/go-sarif/v2 v2.3.0 // indirect
github.com/package-url/packageurl-go v0.1.3 // indirect
github.com/pandatix/go-cvss v0.6.2 // indirect
github.com/package-url/packageurl-go v0.1.2 // indirect
github.com/pborman/indent v1.2.1 // indirect
github.com/pborman/uuid v1.2.1 // indirect
github.com/pdfcpu/pdfcpu v0.9.1 // indirect
github.com/pelletier/go-toml v1.9.5 // indirect
github.com/pelletier/go-toml/v2 v2.2.4 // indirect
github.com/peterbourgon/diskv v2.0.1+incompatible // indirect
github.com/petermattis/goid v0.0.0-20241211131331-93ee7e083c43 // indirect
github.com/pierrec/lz4/v4 v4.1.22 // indirect
github.com/pjbgf/sha1cd v0.4.0 // indirect
github.com/pelletier/go-toml/v2 v2.1.0 // 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-20240102092130-5ac0b6a4141c // indirect
github.com/pkg/errors v0.9.1 // indirect
github.com/pkg/profile v1.7.0 // indirect
github.com/pkg/xattr v0.4.12 // indirect
github.com/planetscale/vtprotobuf v0.6.1-0.20240319094008-0393e58bdf10 // indirect
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect
github.com/project-copacetic/copacetic v0.10.0 // indirect
github.com/prometheus/client_golang v1.22.0 // indirect
github.com/pquerna/cachecontrol v0.2.0 // indirect
github.com/project-copacetic/copacetic v0.4.1-0.20231017020916-013c118454b8 // indirect
github.com/prometheus/client_golang v1.20.2 // indirect
github.com/prometheus/client_model v0.6.1 // indirect
github.com/prometheus/common v0.62.0 // indirect
github.com/prometheus/common v0.55.0 // indirect
github.com/prometheus/procfs v0.15.1 // indirect
github.com/quay/claircore v1.5.35 // indirect
github.com/quay/claircore/toolkit v1.2.4 // indirect
github.com/quay/zlog v1.1.8 // 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.7 // indirect
github.com/rs/zerolog v1.30.0 // indirect
github.com/russross/blackfriday/v2 v2.1.0 // indirect
github.com/rust-secure-code/go-rustaudit v0.0.0-20250226111315-e20ec32e963c // indirect
github.com/sagikazarmark/locafero v0.9.0 // indirect
github.com/saferwall/pe v1.5.2 // indirect
github.com/sagikazarmark/locafero v0.4.0 // indirect
github.com/sagikazarmark/slog-shim v0.1.0 // indirect
github.com/saintfish/chardet v0.0.0-20230101081208-5e3ef4b5456d // indirect
github.com/santhosh-tekuri/jsonschema/v6 v6.0.2 // indirect
github.com/sasha-s/go-deadlock v0.3.5 // indirect
github.com/sassoftware/go-rpmutils v0.4.0 // indirect
github.com/sassoftware/go-rpmutils v0.3.0 // indirect
github.com/sassoftware/relic v7.2.1+incompatible // indirect
github.com/schollz/progressbar/v3 v3.13.0 // indirect
github.com/scylladb/go-set v1.0.3-0.20200225121959-cc7b2070d91e // indirect
github.com/seccomp/libseccomp-golang v0.10.0 // indirect
github.com/secure-systems-lab/go-securesystemslib v0.9.0 // indirect
github.com/secure-systems-lab/go-securesystemslib v0.8.0 // indirect
github.com/segmentio/ksuid v1.0.4 // indirect
github.com/sergi/go-diff v1.4.0 // indirect
github.com/sergi/go-diff v1.3.2-0.20230802210424-5b0b94c5c0d3 // indirect
github.com/shibumi/go-pathspec v1.3.0 // indirect
github.com/shopspring/decimal v1.4.0 // indirect
github.com/shopspring/decimal v1.3.1 // indirect
github.com/sigstore/cosign/v2 v2.2.4 // indirect
github.com/sigstore/fulcio v1.6.6 // indirect
github.com/sigstore/protobuf-specs v0.4.1 // indirect
github.com/sigstore/rekor v1.3.10 // indirect
github.com/sigstore/sigstore v1.9.5 // indirect
github.com/sigstore/fulcio v1.4.5 // indirect
github.com/sigstore/rekor v1.3.6 // indirect
github.com/sigstore/sigstore v1.8.4 // indirect
github.com/sigstore/timestamp-authority v1.2.2 // indirect
github.com/sirupsen/logrus v1.9.4-0.20230606125235-dd1b4c2e81af // indirect
github.com/skeema/knownhosts v1.3.1 // indirect
github.com/sirupsen/logrus v1.9.3 // indirect
github.com/skeema/knownhosts v1.3.0 // indirect
github.com/skratchdot/open-golang v0.0.0-20200116055534-eef842397966 // indirect
github.com/sorairolake/lzip-go v0.3.8 // indirect
github.com/sourcegraph/conc v0.3.0 // indirect
github.com/spdx/gordf v0.0.0-20250128162952-000978ccd6fb // indirect
github.com/spdx/tools-golang v0.5.5 // indirect
github.com/spf13/afero v1.15.0 // indirect
github.com/spf13/cast v1.9.2 // indirect
github.com/spf13/cobra v1.9.1 // indirect
github.com/spf13/pflag v1.0.7 // indirect
github.com/spiffe/go-spiffe/v2 v2.5.0 // indirect
github.com/stripe/stripe-go/v74 v74.30.0 // indirect
github.com/spdx/tools-golang v0.5.4 // indirect
github.com/spf13/afero v1.11.0 // indirect
github.com/spf13/cast v1.6.0 // indirect
github.com/spf13/cobra v1.8.1 // indirect
github.com/spf13/pflag v1.0.5 // indirect
github.com/spiffe/go-spiffe/v2 v2.2.0 // indirect
github.com/stripe/stripe-go/v74 v74.28.0 // indirect
github.com/subosito/gotenv v1.6.0 // indirect
github.com/sylabs/sif/v2 v2.22.0 // indirect
github.com/sylabs/squashfs v1.0.6 // indirect
github.com/sylabs/sif/v2 v2.18.0 // 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.2 // indirect
github.com/tchap/go-patricia/v2 v2.3.1 // 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.7.0 // indirect
github.com/theupdateframework/notary v0.6.1 // indirect
github.com/titanous/rocacheck v0.0.0-20171023193734-afe73141d399 // indirect
github.com/tjfoc/gmsm v1.4.1 // indirect
github.com/tonistiigi/dchapes-mode v0.0.0-20250318174251-73d941a28323 // indirect
github.com/tonistiigi/fsutil v0.0.0-20250410151801-5b74a7ad7583 // indirect
github.com/tonistiigi/go-csvvalue v0.0.0-20240710180619-ddb21b71c0b4 // indirect
github.com/tonistiigi/fsutil v0.0.0-20230629203738-36ef4d8c0dbb // indirect
github.com/tonistiigi/units v0.0.0-20180711220420-6950e57a87ea // indirect
github.com/tonistiigi/vt100 v0.0.0-20240514184818-90bafcd6abab // indirect
github.com/tonistiigi/vt100 v0.0.0-20230623042737-f9a4f7ef6531 // indirect
github.com/transparency-dev/merkle v0.0.2 // indirect
github.com/ulikunitz/xz v0.5.15 // indirect
github.com/ulikunitz/xz v0.5.12 // indirect
github.com/uptrace/opentelemetry-go-extra/otelutil v0.3.2 // indirect
github.com/uptrace/opentelemetry-go-extra/otelzap v0.3.2 // indirect
github.com/uptrace/uptrace-go v1.37.0 // indirect
github.com/vbatts/go-mtree v0.5.4 // indirect
github.com/vbatts/tar-split v0.12.1 // indirect
github.com/uptrace/uptrace-go v1.30.1 // indirect
github.com/vbatts/go-mtree v0.5.3 // indirect
github.com/vbatts/tar-split v0.11.5 // indirect
github.com/vifraa/gopom v1.0.0 // indirect
github.com/vishvananda/netlink v1.3.1-0.20241022031324-976bd8de7d81 // indirect
github.com/vishvananda/netns v0.0.5 // 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-20230925121702-07e42b3cdba0 // indirect
github.com/x448/float16 v0.8.4 // indirect
github.com/xanzy/go-gitlab v0.102.0 // 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/xlab/treeprint v1.2.0 // indirect
github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e // indirect
github.com/xo/terminfo v0.0.0-20210125001918-ca9a967f8778 // indirect
github.com/yashtewari/glob-intersection v0.2.0 // indirect
github.com/yl2chen/cidranger v1.0.2 // indirect
github.com/zclconf/go-cty v1.16.3 // indirect
github.com/zeebo/errs v1.4.0 // indirect
go.etcd.io/bbolt v1.4.2 // indirect
go.mongodb.org/mongo-driver v1.17.1 // indirect
github.com/zclconf/go-cty v1.14.4 // indirect
github.com/zeebo/errs v1.3.0 // indirect
go.mongodb.org/mongo-driver v1.14.0 // indirect
go.mozilla.org/pkcs7 v0.0.0-20210826202110-33d05740a352 // indirect
go.opencensus.io v0.24.0 // indirect
go.opentelemetry.io/auto/sdk v1.1.0 // indirect
go.opentelemetry.io/contrib/detectors/gcp v1.37.0 // indirect
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.62.0 // indirect
go.opentelemetry.io/contrib/instrumentation/net/http/httptrace/otelhttptrace v0.56.0 // indirect
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.62.0 // indirect
go.opentelemetry.io/contrib/instrumentation/runtime v0.62.0 // indirect
go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploghttp v0.13.0 // indirect
go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.32.0 // indirect
go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp v1.37.0 // indirect
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.37.0 // indirect
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.35.0 // indirect
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.37.0 // indirect
go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.37.0 // indirect
go.opentelemetry.io/otel/log v0.13.0 // indirect
go.opentelemetry.io/otel/metric v1.37.0 // indirect
go.opentelemetry.io/otel/sdk v1.37.0 // indirect
go.opentelemetry.io/otel/sdk/log v0.13.0 // indirect
go.opentelemetry.io/otel/sdk/metric v1.37.0 // indirect
go.opentelemetry.io/proto/otlp v1.7.0 // indirect
go.step.sm/crypto v0.60.0 // indirect
go.uber.org/mock v0.5.0 // indirect
go.yaml.in/yaml/v2 v2.4.2 // indirect
go.yaml.in/yaml/v3 v3.0.4 // indirect
go4.org v0.0.0-20230225012048-214862532bf5 // indirect
go4.org/netipx v0.0.0-20231129151722-fdeea329fbba // indirect
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.49.0 // indirect
go.opentelemetry.io/contrib/instrumentation/net/http/httptrace/otelhttptrace v0.44.0 // indirect
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.53.0 // indirect
go.opentelemetry.io/contrib/instrumentation/runtime v0.55.0 // indirect
go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploghttp v0.6.0 // indirect
go.opentelemetry.io/otel/exporters/otlp/otlpmetric v0.41.0 // indirect
go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v0.41.0 // indirect
go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp v1.30.0 // indirect
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.30.0 // indirect
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.28.0 // indirect
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.30.0 // indirect
go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.30.0 // indirect
go.opentelemetry.io/otel/log v0.6.0 // indirect
go.opentelemetry.io/otel/metric v1.30.0 // indirect
go.opentelemetry.io/otel/sdk v1.30.0 // indirect
go.opentelemetry.io/otel/sdk/log v0.6.0 // indirect
go.opentelemetry.io/otel/sdk/metric v1.30.0 // indirect
go.opentelemetry.io/proto/otlp v1.3.1 // indirect
go.starlark.net v0.0.0-20230525235612-a134d8f9ddca // indirect
go.step.sm/crypto v0.44.2 // indirect
golang.org/x/image v0.24.0 // indirect
golang.org/x/sync v0.17.0 // indirect
golang.org/x/sys v0.35.0 // indirect
golang.org/x/term v0.34.0 // indirect
golang.org/x/text v0.29.0 // indirect
golang.org/x/time v0.12.0 // indirect
golang.org/x/tools v0.36.0 // indirect
golang.org/x/xerrors v0.0.0-20240903120638-7835f813f4da // indirect
gonum.org/v1/gonum v0.12.0 // indirect
google.golang.org/api v0.242.0 // indirect
google.golang.org/genproto/googleapis/api v0.0.0-20250715232539-7130f93afb79 // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20250715232539-7130f93afb79 // indirect
google.golang.org/protobuf v1.36.6 // indirect
golang.org/x/sync v0.11.0 // indirect
golang.org/x/sys v0.29.0 // indirect
golang.org/x/term v0.28.0 // indirect
golang.org/x/text v0.22.0 // indirect
golang.org/x/time v0.7.0 // indirect
golang.org/x/xerrors v0.0.0-20231012003039-104605ab7028 // indirect
gonum.org/v1/gonum v0.9.1 // indirect
google.golang.org/api v0.172.0 // indirect
google.golang.org/genproto/googleapis/api v0.0.0-20240903143218-8af14fe29dc1 // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20240903143218-8af14fe29dc1 // indirect
google.golang.org/protobuf v1.35.1 // indirect
gopkg.in/evanphx/json-patch.v4 v4.12.0 // indirect
gopkg.in/inf.v0 v0.9.1 // indirect
gopkg.in/ini.v1 v1.67.0 // indirect
gopkg.in/op/go-logging.v1 v1.0.0-20160211212156-b2cb9fa56473 // indirect
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
gopkg.in/yaml.v3 v3.0.1 // indirect
gorm.io/gorm v1.30.2 // indirect
helm.sh/helm/v3 v3.18.5 // indirect
k8s.io/api v0.33.3 // indirect
k8s.io/apiextensions-apiserver v0.33.3 // indirect
k8s.io/apiserver v0.33.3 // indirect
k8s.io/cli-runtime v0.33.3 // indirect
k8s.io/component-base v0.33.3 // indirect
gorm.io/gorm v1.25.10 // indirect
helm.sh/helm/v3 v3.14.4 // indirect
k8s.io/api v0.32.2 // indirect
k8s.io/apiextensions-apiserver v0.29.0 // indirect
k8s.io/klog/v2 v2.130.1 // indirect
k8s.io/kube-openapi v0.0.0-20250318190949-c8a335a9a2ff // indirect
k8s.io/kubectl v0.33.3 // indirect
modernc.org/libc v1.66.3 // indirect
modernc.org/mathutil v1.7.1 // indirect
modernc.org/memory v1.11.0 // indirect
modernc.org/sqlite v1.38.2 // indirect
oras.land/oras-go/v2 v2.6.0 // indirect
sigs.k8s.io/controller-runtime v0.18.4 // indirect
sigs.k8s.io/json v0.0.0-20241014173422-cfa47c3a1cc8 // indirect
sigs.k8s.io/kustomize/api v0.19.0 // indirect
sigs.k8s.io/kustomize/kyaml v0.19.0 // indirect
sigs.k8s.io/randfill v1.0.0 // indirect
sigs.k8s.io/release-utils v0.9.0 // indirect
sigs.k8s.io/structured-merge-diff/v4 v4.6.0 // indirect
sigs.k8s.io/yaml v1.5.0 // indirect
k8s.io/kube-openapi v0.0.0-20241212222426-2c72e554b1e7 // indirect
modernc.org/libc v1.49.3 // indirect
modernc.org/mathutil v1.6.0 // indirect
modernc.org/memory v1.8.0 // indirect
modernc.org/sqlite v1.29.8 // indirect
sigs.k8s.io/controller-runtime v0.15.0 // indirect
sigs.k8s.io/json v0.0.0-20241010143419-9aa6b5e7a4b3 // indirect
sigs.k8s.io/kustomize/api v0.13.5-0.20230601165947-6ce0bf390ce3 // indirect
sigs.k8s.io/kustomize/kyaml v0.14.3-0.20230601165947-6ce0bf390ce3 // indirect
sigs.k8s.io/release-utils v0.7.7 // indirect
sigs.k8s.io/structured-merge-diff/v4 v4.5.0 // indirect
sigs.k8s.io/yaml v1.4.0 // indirect
)
// Using the forked version of tablewriter
replace github.com/olekukonko/tablewriter => github.com/kubescape/tablewriter v0.0.6-0.20231106230230-aac7d2659c94
replace github.com/docker/distribution v2.8.3+incompatible => github.com/docker/distribution v2.8.2+incompatible
replace github.com/docker/docker v27.1.1+incompatible => github.com/docker/docker v26.1.5+incompatible
replace github.com/mholt/archiver/v3 v3.5.1 => github.com/anchore/archiver/v3 v3.5.2

File diff suppressed because it is too large Load Diff

View File

@@ -83,7 +83,7 @@ func scan(ctx context.Context, scanInfo *cautils.ScanInfo, scanID string) (*repo
if err != nil {
return nil, writeScanErrorToFile(err, scanID)
}
if err := result.HandleResults(ctx, scanInfo); err != nil {
if err := result.HandleResults(ctx); err != nil {
return nil, err
}
storage := storage.GetStorage()

View File

@@ -19,7 +19,7 @@ import (
_ "github.com/kubescape/kubescape/v3/httphandler/docs"
"github.com/kubescape/kubescape/v3/httphandler/listener"
"github.com/kubescape/kubescape/v3/httphandler/storage"
"github.com/kubescape/kubescape/v3/pkg/ksinit"
"k8s.io/client-go/rest"
)
const (
@@ -68,13 +68,19 @@ func initializeStorage(clusterName string, cfg config.Config) {
namespace := getNamespace(cfg)
logger.L().Debug("initializing storage", helpers.String("namespace", namespace))
// Use shared ksinit logic for storage connection
ksClient, err := ksinit.CreateKsObjectConnection(namespace, 0)
if err != nil {
logger.L().Fatal("storage initialization error", helpers.Error(err))
// for local storage, use the k8s config
var config *rest.Config
if os.Getenv("LOCAL_STORAGE") == "true" {
config = k8sinterface.GetK8sConfig()
} else {
var err error
config, err = rest.InClusterConfig()
if err != nil {
logger.L().Fatal("storage initialization error", helpers.Error(err))
}
}
s, err := storage.NewAPIServerStorage(clusterName, namespace, ksClient)
s, err := storage.NewAPIServerStorage(clusterName, namespace, config)
if err != nil {
logger.L().Fatal("storage initialization error", helpers.Error(err))
}

View File

@@ -19,10 +19,12 @@ import (
"github.com/kubescape/opa-utils/reporthandling/results/v1/resourcesresults"
v2 "github.com/kubescape/opa-utils/reporthandling/v2"
"github.com/kubescape/storage/pkg/apis/softwarecomposition/v1beta1"
"github.com/kubescape/storage/pkg/generated/clientset/versioned"
spdxv1beta1 "github.com/kubescape/storage/pkg/generated/clientset/versioned/typed/softwarecomposition/v1beta1"
"go.opentelemetry.io/otel"
"k8s.io/apimachinery/pkg/api/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/client-go/rest"
"k8s.io/client-go/util/retry"
)
@@ -52,9 +54,19 @@ func GetStorage() *APIServerStore {
}
// NewAPIServerStorage initializes the APIServerStore struct
func NewAPIServerStorage(clusterName string, namespace string, ksClient spdxv1beta1.SpdxV1beta1Interface) (*APIServerStore, error) {
func NewAPIServerStorage(clusterName string, namespace string, config *rest.Config) (*APIServerStore, error) {
// disable rate limiting
config.QPS = 0
config.RateLimiter = nil
// force GRPC
config.AcceptContentTypes = "application/vnd.kubernetes.protobuf"
config.ContentType = "application/vnd.kubernetes.protobuf"
clientset, err := versioned.NewForConfig(config)
if err != nil {
return nil, err
}
return &APIServerStore{
StorageClient: ksClient,
StorageClient: clientset.SpdxV1beta1(),
clusterName: clusterName,
namespace: namespace,
}, nil
@@ -166,6 +178,15 @@ func (a *APIServerStore) StoreWorkloadConfigurationScanResult(ctx context.Contex
},
}
// This is a workaround for the fact that the apiserver does not return already exist error on Create
existing, err := a.StorageClient.WorkloadConfigurationScans(namespace).Get(context.Background(), manifest.Name, metav1.GetOptions{})
if err == nil {
logger.L().Debug("found existing WorkloadConfigurationScan manifest in storage - merging manifests", helpers.String("name", manifest.Name))
manifest.Annotations = existing.Annotations
manifest.Labels = existing.Labels
manifest.Spec = mergeWorkloadConfigurationScanSpec(existing.Spec, manifest.Spec)
}
_, err = a.StorageClient.WorkloadConfigurationScans(namespace).Create(context.Background(), &manifest, metav1.CreateOptions{})
switch {
case errors.IsAlreadyExists(err):
@@ -177,8 +198,8 @@ func (a *APIServerStore) StoreWorkloadConfigurationScanResult(ctx context.Contex
return getErr
}
// update the workload configuration scan manifest
mergeMaps(result.Annotations, manifest.Annotations)
mergeMaps(result.Labels, manifest.Labels)
result.Annotations = manifest.Annotations
result.Labels = manifest.Labels
result.Spec = mergeWorkloadConfigurationScanSpec(result.Spec, manifest.Spec)
// try to send the updated workload configuration scan manifest
_, updateErr := a.StorageClient.WorkloadConfigurationScans(namespace).Update(context.Background(), result, metav1.UpdateOptions{})
@@ -200,9 +221,6 @@ func (a *APIServerStore) StoreWorkloadConfigurationScanResult(ctx context.Contex
}
func mergeWorkloadConfigurationScanSpec(existingSpec v1beta1.WorkloadConfigurationScanSpec, newSpec v1beta1.WorkloadConfigurationScanSpec) v1beta1.WorkloadConfigurationScanSpec {
if existingSpec.Controls == nil {
existingSpec.Controls = make(map[string]v1beta1.ScannedControl)
}
for ctrlID := range newSpec.Controls {
newCtrl := newSpec.Controls[ctrlID]
_, found := existingSpec.Controls[ctrlID]
@@ -223,9 +241,6 @@ func mergeWorkloadConfigurationScanSpec(existingSpec v1beta1.WorkloadConfigurati
}
func mergeWorkloadConfigurationScanSummarySpec(existingSpec v1beta1.WorkloadConfigurationScanSummarySpec, newSpec v1beta1.WorkloadConfigurationScanSummarySpec) v1beta1.WorkloadConfigurationScanSummarySpec {
if existingSpec.Controls == nil {
existingSpec.Controls = make(map[string]v1beta1.ScannedControlSummary)
}
for ctrlID := range newSpec.Controls {
newCtrl := newSpec.Controls[ctrlID]
_, found := existingSpec.Controls[ctrlID]
@@ -265,7 +280,16 @@ func (a *APIServerStore) StoreWorkloadConfigurationScanResultSummary(ctx context
},
}
_, err := a.StorageClient.WorkloadConfigurationScanSummaries(namespace).Create(context.Background(), &manifest, metav1.CreateOptions{})
// This is a workaround for the fact that the apiserver does not return already exist error on Create
existing, err := a.StorageClient.WorkloadConfigurationScanSummaries(namespace).Get(context.Background(), manifest.Name, metav1.GetOptions{})
if err == nil {
logger.L().Debug("found existing WorkloadConfigurationScanSummary manifest in storage - merging manifests", helpers.String("name", manifest.Name))
manifest.Annotations = existing.Annotations
manifest.Labels = existing.Labels
manifest.Spec = mergeWorkloadConfigurationScanSummarySpec(existing.Spec, manifest.Spec)
}
_, err = a.StorageClient.WorkloadConfigurationScanSummaries(namespace).Create(context.Background(), &manifest, metav1.CreateOptions{})
switch {
case errors.IsAlreadyExists(err):
retryErr := retry.RetryOnConflict(retry.DefaultRetry, func() error {
@@ -276,8 +300,8 @@ func (a *APIServerStore) StoreWorkloadConfigurationScanResultSummary(ctx context
return getErr
}
// update the manifest
mergeMaps(result.Annotations, manifest.Annotations)
mergeMaps(result.Labels, manifest.Labels)
result.Annotations = manifest.Annotations
result.Labels = manifest.Labels
result.Spec = mergeWorkloadConfigurationScanSummarySpec(result.Spec, manifest.Spec)
// try to send the updated manifest
_, updateErr := a.StorageClient.WorkloadConfigurationScanSummaries(namespace).Update(context.Background(), result, metav1.UpdateOptions{})
@@ -520,10 +544,3 @@ func parseWorkloadScanRelatedObjectList(relatedObjects []workloadinterface.IMeta
}
return r
}
// mergeMaps merges new into existing, overwriting existing keys with new values
func mergeMaps(existing, new map[string]string) {
for k, v := range new {
existing[k] = v
}
}

View File

@@ -484,51 +484,5 @@ func Test_RoleBindingResourceTripletToSlug(t *testing.T) {
assert.ElementsMatch(t, tt.expectedSlugs, slugs)
})
}
}
func TestMergeMaps(t *testing.T) {
tests := []struct {
name string
existing map[string]string
new map[string]string
expected map[string]string
}{
{
name: "merge with no conflicts",
existing: map[string]string{"key1": "value1"},
new: map[string]string{"key2": "value2"},
expected: map[string]string{"key1": "value1", "key2": "value2"},
},
{
name: "merge with conflicts",
existing: map[string]string{"key1": "value1"},
new: map[string]string{"key1": "newValue1", "key2": "value2"},
expected: map[string]string{"key1": "newValue1", "key2": "value2"},
},
{
name: "merge with empty new map",
existing: map[string]string{"key1": "value1"},
new: map[string]string{},
expected: map[string]string{"key1": "value1"},
},
{
name: "merge with empty existing map",
existing: map[string]string{},
new: map[string]string{"key1": "value1"},
expected: map[string]string{"key1": "value1"},
},
{
name: "merge with both maps empty",
existing: map[string]string{},
new: map[string]string{},
expected: map[string]string{},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
mergeMaps(tt.existing, tt.new)
assert.Equal(t, tt.expected, tt.existing)
})
}
}

View File

@@ -96,6 +96,5 @@ if ! kubectl get nodes &> /dev/null; then
fi
echo -e "\033[0;37;40m"
echo -e "\033[0;37;32mFinished Installation.\n"
$KUBESCAPE_EXEC version
echo -e "\033[0;37;35m\nUsage: $ kubescape scan"
echo -e "\033[0;37;32mExecuting Kubescape."
$KUBESCAPE_EXEC scan

View File

@@ -5,11 +5,8 @@ import (
"runtime"
)
// CurrentDir returns the directory of the file where this function is defined.
func CurrentDir() string {
_, filename, _, ok := runtime.Caller(1)
if !ok {
panic("failed to get current file info")
}
_, filename, _, _ := runtime.Caller(1)
return filepath.Dir(filename)
}

View File

@@ -20,12 +20,9 @@ func main() {
<-ctx.Done()
// Perform cleanup or graceful shutdown here
logger.L().StopError("Received interrupt signal, exiting...")
// Clear the signal handler so that a second interrupt signal shuts down immediately
stop()
}()
if err := cmd.Execute(ctx); err != nil {
stop()
logger.L().Fatal(err.Error())
}
}

View File

@@ -9,8 +9,7 @@ import (
"github.com/adrg/xdg"
"github.com/anchore/grype/grype"
"github.com/anchore/grype/grype/db/v6/distribution"
"github.com/anchore/grype/grype/db/v6/installation"
"github.com/anchore/grype/grype/db"
"github.com/anchore/grype/grype/grypeerr"
"github.com/anchore/grype/grype/match"
"github.com/anchore/grype/grype/matcher"
@@ -22,14 +21,15 @@ import (
"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"
"github.com/kubescape/kubescape/v3/core/cautils"
)
const (
defaultGrypeListingURL = "https://grype.anchore.io/databases"
defaultGrypeListingURL = "https://toolbox-data.anchore.io/grype/databases/listing.json"
defaultDBDirName = "grypedb"
)
@@ -42,22 +42,29 @@ func (c RegistryCredentials) IsEmpty() bool {
return c.Username == "" || c.Password == ""
}
func NewDefaultDBConfig() (distribution.Config, installation.Config, bool) {
// 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 {
if scanResults.MetadataProvider == nil {
return false
}
return grype.HasSeverityAtOrAbove(scanResults.MetadataProvider, severity, scanResults.Matches)
}
func NewDefaultDBConfig() (db.Config, bool) {
dir := filepath.Join(xdg.CacheHome, defaultDBDirName)
url := defaultGrypeListingURL
shouldUpdate := true
return distribution.Config{
LatestURL: url,
}, installation.Config{
DBRootDir: dir,
}, shouldUpdate
return db.Config{
DBRootDir: dir,
ListingURL: url,
}, shouldUpdate
}
func getMatchers(useDefaultMatchers bool) []match.Matcher {
if useDefaultMatchers {
return matcher.NewDefaultMatchers(defaultMatcherConfig())
}
func getMatchers() []matcher.Matcher {
return matcher.NewDefaultMatchers(
matcher.Config{
Java: java.MatcherConfig{
@@ -74,34 +81,15 @@ func getMatchers(useDefaultMatchers bool) []match.Matcher {
)
}
func defaultMatcherConfig() matcher.Config {
return matcher.Config{
Java: java.MatcherConfig{
ExternalSearchConfig: java.ExternalSearchConfig{MavenBaseURL: "https://search.maven.org/solrsearch/select"},
UseCPEs: false,
},
Ruby: ruby.MatcherConfig{UseCPEs: false},
Python: python.MatcherConfig{UseCPEs: false},
Dotnet: dotnet.MatcherConfig{UseCPEs: false},
Javascript: javascript.MatcherConfig{UseCPEs: false},
Golang: golang.MatcherConfig{
UseCPEs: false,
AlwaysUseCPEForStdlib: true,
AllowMainModulePseudoVersionComparison: false,
},
Stock: stock.MatcherConfig{UseCPEs: true},
}
}
func validateDBLoad(loadErr error, status *vulnerability.ProviderStatus) error {
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.Error != nil {
return fmt.Errorf("db could not be loaded: %w", status.Error)
if status.Err != nil {
return fmt.Errorf("db could not be loaded: %w", status.Err)
}
return nil
}
@@ -127,11 +115,10 @@ func getProviderConfig(creds RegistryCredentials) pkg.ProviderConfig {
//
// It performs image scanning and everything needed in between.
type Service struct {
useDefaultMatchers bool
vp vulnerability.Provider
dbCfg db.Config
}
func getIgnoredMatches(vulnerabilityExceptions []string, vp vulnerability.Provider, packages []pkg.Package, pkgContext pkg.Context, useDefaultMatchers bool) (*match.Matches, []match.IgnoredMatch, error) {
func getIgnoredMatches(vulnerabilityExceptions []string, store *store.Store, packages []pkg.Package, pkgContext pkg.Context) (*match.Matches, []match.IgnoredMatch, error) {
if vulnerabilityExceptions == nil {
vulnerabilityExceptions = []string{}
}
@@ -144,13 +131,13 @@ func getIgnoredMatches(vulnerabilityExceptions []string, vp vulnerability.Provid
ignoreRules = append(ignoreRules, rule)
}
vulnMatcher := grype.VulnerabilityMatcher{
VulnerabilityProvider: vp,
Matchers: getMatchers(useDefaultMatchers),
IgnoreRules: ignoreRules,
matcher := grype.VulnerabilityMatcher{
Store: *store,
Matchers: getMatchers(),
IgnoreRules: ignoreRules,
}
remainingMatches, ignoredMatches, err := vulnMatcher.FindMatches(packages, pkgContext)
remainingMatches, ignoredMatches, err := matcher.FindMatches(packages, pkgContext)
if err != nil {
if !errors.Is(err, grypeerr.ErrAboveSeverityThreshold) {
return nil, nil, err
@@ -160,8 +147,8 @@ func getIgnoredMatches(vulnerabilityExceptions []string, vp vulnerability.Provid
return remainingMatches, ignoredMatches, nil
}
// Filter the remaining matches based on severity exceptions.
func filterMatchesBasedOnSeverity(severityExceptions []string, remainingMatches match.Matches, vp vulnerability.Provider) match.Matches {
// Filter the remaing matches based on severity exceptions.
func filterMatchesBasedOnSeverity(severityExceptions []string, remainingMatches match.Matches, store *store.Store) match.Matches {
if severityExceptions == nil {
return remainingMatches
}
@@ -169,7 +156,7 @@ func filterMatchesBasedOnSeverity(severityExceptions []string, remainingMatches
filteredMatches := match.NewMatches()
for m := range remainingMatches.Enumerate() {
metadata, err := vp.VulnerabilityMetadata(m.Vulnerability.Reference)
metadata, err := store.GetMetadata(m.Vulnerability.ID, m.Vulnerability.Namespace)
if err != nil {
continue
}
@@ -191,73 +178,47 @@ func filterMatchesBasedOnSeverity(severityExceptions []string, remainingMatches
return filteredMatches
}
func (s *Service) Scan(_ context.Context, userInput string, creds RegistryCredentials, vulnerabilityExceptions, severityExceptions []string) (*cautils.ImageScanData, error) {
func (s *Service) Scan(ctx context.Context, userInput string, creds RegistryCredentials, vulnerabilityExceptions, severityExceptions []string) (*models.PresenterConfig, error) {
store, status, dbCloser, err := NewVulnerabilityDB(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
}
remainingMatches, ignoredMatches, err := getIgnoredMatches(vulnerabilityExceptions, s.vp, packages, pkgContext, s.useDefaultMatchers)
if dbCloser != nil {
defer dbCloser.Close()
}
remainingMatches, ignoredMatches, err := getIgnoredMatches(vulnerabilityExceptions, store, packages, pkgContext)
if err != nil {
return nil, err
}
filteredMatches := filterMatchesBasedOnSeverity(severityExceptions, *remainingMatches, s.vp)
filteredMatches := filterMatchesBasedOnSeverity(severityExceptions, *remainingMatches, store)
pb := cautils.ImageScanData{
Context: pkgContext,
IgnoredMatches: ignoredMatches,
Image: userInput,
Matches: filteredMatches,
Packages: packages,
RemainingMatches: remainingMatches,
SBOM: sbom,
VulnerabilityProvider: s.vp,
pb := models.PresenterConfig{
Matches: filteredMatches,
IgnoredMatches: ignoredMatches,
Packages: packages,
Context: pkgContext,
MetadataProvider: store,
SBOM: sbom,
AppConfig: nil,
DBStatus: status,
}
return &pb, nil
}
// 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 (s *Service) ExceedsSeverityThreshold(severity vulnerability.Severity, matches match.Matches) bool {
if severity == vulnerability.UnknownSeverity {
return false
}
for m := range matches.Enumerate() {
metadata, err := s.vp.VulnerabilityMetadata(m.Vulnerability.Reference)
if err != nil {
continue
}
if vulnerability.ParseSeverity(metadata.Severity) >= severity {
return true
}
}
return false
func NewVulnerabilityDB(cfg db.Config, update bool) (*store.Store, *db.Status, *db.Closer, error) {
return grype.LoadVulnerabilityDB(cfg, update)
}
func (s *Service) Close() {
_ = s.vp.Close()
}
func NewVulnerabilityDB(distCfg distribution.Config, installCfg installation.Config, update bool) (vulnerability.Provider, *vulnerability.ProviderStatus, error) {
return grype.LoadVulnerabilityDB(distCfg, installCfg, update)
}
func NewScanService(distCfg distribution.Config, installCfg installation.Config) (*Service, error) {
return NewScanServiceWithMatchers(distCfg, installCfg, true)
}
func NewScanServiceWithMatchers(distCfg distribution.Config, installCfg installation.Config, useDefaultMatchers bool) (*Service, error) {
vp, status, err := NewVulnerabilityDB(distCfg, installCfg, true)
if err = validateDBLoad(err, status); err != nil {
return nil, err
}
return &Service{
vp: vp,
useDefaultMatchers: useDefaultMatchers,
}, nil
func NewScanService(dbCfg db.Config) Service {
return Service{dbCfg: dbCfg}
}
// ParseSeverity returns a Grype severity given a severity string

View File

@@ -1,13 +1,134 @@
package imagescan
import (
"errors"
"net/http"
"path"
"testing"
"time"
"github.com/adrg/xdg"
"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"
"github.com/stretchr/testify/require"
)
func TestVulnerabilityAndSeverityExceptions(t *testing.T) {
go func() {
_ = http.ListenAndServe(":8000", http.FileServer(http.Dir("testdata"))) //nolint:gosec
}()
dbCfg := db.Config{
DBRootDir: path.Join(xdg.CacheHome, "grype-light", "db"),
ListingURL: "http://localhost:8000/listing.json",
}
svc := NewScanService(dbCfg)
creds := RegistryCredentials{}
tests := []struct {
name string
image string
vulnerabilityExceptions []string
ignoredLen int
severityExceptions []string
filteredLen int
}{
{
name: "alpine:3.19.1 without medium vulnerabilities",
image: "alpine:3.19.1",
ignoredLen: 0,
severityExceptions: []string{"MEDIUM"},
filteredLen: 0,
},
{
name: "alpine:3.9.6",
image: "alpine:3.9.6",
vulnerabilityExceptions: []string{"CVE-2020-1971", "CVE-2020-28928", "CVE-2021-23840"},
ignoredLen: 6,
severityExceptions: []string{"HIGH", "MEDIUM"},
filteredLen: 8,
},
{
name: "alpine:3.9.6 with invalid vulnerability and severity exceptions",
image: "alpine:3.9.6",
vulnerabilityExceptions: []string{"invalid-cve", "CVE-2020-28928", "CVE-2021-23840"},
ignoredLen: 4,
severityExceptions: []string{"CRITICAL", "MEDIUM", "invalid-severity"},
filteredLen: 10,
},
}
for _, tc := range tests {
t.Run(tc.name, func(t *testing.T) {
store, status, dbCloser, err := NewVulnerabilityDB(svc.dbCfg, true)
assert.NoError(t, validateDBLoad(err, status))
packages, pkgContext, _, err := pkg.Provide(tc.image, getProviderConfig(creds))
assert.NoError(t, err)
if dbCloser != nil {
defer dbCloser.Close()
}
remainingMatches, ignoredMatches, err := getIgnoredMatches(tc.vulnerabilityExceptions, store, packages, pkgContext)
assert.NoError(t, err)
assert.Equal(t, tc.ignoredLen, len(ignoredMatches))
filteredMatches := filterMatchesBasedOnSeverity(tc.severityExceptions, *remainingMatches, store)
assert.Equal(t, tc.filteredLen, filteredMatches.Count())
})
}
}
// 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 TestParseSeverity(t *testing.T) {
tests := []struct {
name string
@@ -97,6 +218,81 @@ func TestIsEmpty(t *testing.T) {
}
}
func TestNewDefaultDBConfig(t *testing.T) {
config, shouldUpdate := NewDefaultDBConfig()
assert.NotNil(t, config)
assert.Equal(t, true, shouldUpdate)
assert.Contains(t, config.DBRootDir, "grypedb")
assert.Equal(t, "https://toolbox-data.anchore.io/grype/databases/listing.json", config.ListingURL)
}
func TestValidateDBLoad(t *testing.T) {
currentTime := time.Now()
tests := []struct {
name string
loadErr error
status *db.Status
expectedErrMessage string
}{
{
name: "status nil",
loadErr: nil,
status: nil,
expectedErrMessage: "unable to determine the status of the vulnerability db",
},
{
name: "loadErr nil and status error nil",
loadErr: nil,
status: &db.Status{
Built: currentTime,
SchemaVersion: 7,
Location: "New Delhi",
Checksum: "invalid",
Err: nil,
},
expectedErrMessage: "",
},
{
name: "loadErr nil but status error not nil",
loadErr: nil,
status: &db.Status{
Built: currentTime,
SchemaVersion: 7,
Location: "New Delhi",
Checksum: "invalid",
Err: errors.New("Some error"),
},
expectedErrMessage: "db could not be loaded: Some error",
},
{
name: "loadErr not nil",
loadErr: errors.New("Some error"),
status: &db.Status{
Built: currentTime,
SchemaVersion: 7,
Location: "New Delhi",
Checksum: "invalid",
Err: errors.New("Some error"),
},
expectedErrMessage: "failed to load vulnerability db: Some error",
},
{
name: "load Error, no db status",
loadErr: errors.New("Some error"),
status: nil,
expectedErrMessage: "failed to load vulnerability db: Some error",
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
err := validateDBLoad(tt.loadErr, tt.status)
if err != nil {
assert.Equal(t, tt.expectedErrMessage, err.Error())
}
})
}
}
func TestGetProviderConfig(t *testing.T) {
tests := []struct {
name string
@@ -140,53 +336,105 @@ func TestGetProviderConfig(t *testing.T) {
}
}
func TestNewScanServiceWithDefaultMatchers(t *testing.T) {
// Test the Service struct creation with different useDefaultMatchers values
// This test doesn't require a real database
// Test with default matchers enabled
svcWithDefault := &Service{
useDefaultMatchers: true,
}
assert.True(t, svcWithDefault.useDefaultMatchers)
// Test with default matchers disabled
svcWithoutDefault := &Service{
useDefaultMatchers: false,
}
assert.False(t, svcWithoutDefault.useDefaultMatchers)
func TestNewScanService(t *testing.T) {
defaultConfig, _ := NewDefaultDBConfig()
svc := NewScanService(defaultConfig)
assert.Equal(t, defaultConfig, svc.dbCfg)
}
func TestNewScanServiceWithMatchers(t *testing.T) {
// Test the Service struct creation with different useDefaultMatchers values
// This test doesn't require a real database
func TestExceedsSeverityThreshold(t *testing.T) {
my_matches := match.NewMatches()
my_matches.Add(match.Match{
Vulnerability: vulnerability.Vulnerability{
ID: "CVE-2014-fake-1",
Namespace: "debian:distro:debian:8",
},
Package: pkg.Package{
ID: pkg.ID(uuid.NewString()),
Name: "the-package",
Version: "v0.1",
Type: syftPkg.RpmPkg,
},
Details: match.Details{
{
Type: match.ExactDirectMatch,
},
},
})
my_metadataProvider := db.NewVulnerabilityMetadataProvider(newFakeMetaProvider())
// Test with default matchers enabled
svcWithDefault := &Service{
useDefaultMatchers: true,
type args struct {
scanResults *models.PresenterConfig
severity vulnerability.Severity
}
assert.True(t, svcWithDefault.useDefaultMatchers)
// Test with default matchers disabled
svcWithoutDefault := &Service{
useDefaultMatchers: false,
tests := []struct {
name string
args args
want bool
}{
{
name: "No severity set",
args: args{
scanResults: &models.PresenterConfig{
Matches: match.NewMatches(),
MetadataProvider: nil,
},
severity: vulnerability.UnknownSeverity,
},
want: false,
},
{
name: "No MetadataProvider",
args: args{
scanResults: &models.PresenterConfig{
Matches: my_matches,
MetadataProvider: nil,
},
severity: vulnerability.MediumSeverity,
},
want: false,
},
{
name: "Severity higher than vulnerability",
args: args{
scanResults: &models.PresenterConfig{
Matches: my_matches,
MetadataProvider: my_metadataProvider,
},
severity: vulnerability.HighSeverity,
},
want: false,
},
{
name: "Severity equal to vulnerability",
args: args{
scanResults: &models.PresenterConfig{
Matches: my_matches,
MetadataProvider: my_metadataProvider,
},
severity: vulnerability.MediumSeverity,
},
want: true,
},
{
name: "Fail severity below found vulnerability",
args: args{
scanResults: &models.PresenterConfig{
Matches: my_matches,
MetadataProvider: my_metadataProvider,
},
severity: vulnerability.LowSeverity,
},
want: true,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got := ExceedsSeverityThreshold(tt.args.scanResults, tt.args.severity)
assert.Equal(t, tt.want, got)
})
}
assert.False(t, svcWithoutDefault.useDefaultMatchers)
}
func TestNewScanServiceWithMatchersIntegration(t *testing.T) {
// Test the actual NewScanServiceWithMatchers function
distCfg, installCfg, _ := NewDefaultDBConfig()
// Test with default matchers enabled
svcWithDefault, err := NewScanServiceWithMatchers(distCfg, installCfg, true)
require.NoError(t, err)
defer svcWithDefault.Close()
assert.True(t, svcWithDefault.useDefaultMatchers)
// Test with default matchers disabled
svcWithoutDefault, err := NewScanServiceWithMatchers(distCfg, installCfg, false)
require.NoError(t, err)
defer svcWithoutDefault.Close()
assert.False(t, svcWithoutDefault.useDefaultMatchers)
}

12
pkg/imagescan/testdata/listing.json vendored Normal file
View File

@@ -0,0 +1,12 @@
{
"available": {
"5": [
{
"built": "2023-12-13T01:27:01Z",
"version": 5,
"url": "http://localhost:8000/vulnerability-db_v5_2023-03-24T06_54_57Z_fab15e5405c096d82dfd.tar.gz",
"checksum": "sha256:99ad9fd54be5295351555a02a0fb6986a461a9d23eb8ae3b34ea892c252a8c80"
}
]
}
}

View File

@@ -1,40 +0,0 @@
package ksinit
import (
"os"
"path/filepath"
"time"
spdxv1beta1 "github.com/kubescape/storage/pkg/generated/clientset/versioned/typed/softwarecomposition/v1beta1"
"k8s.io/client-go/rest"
"k8s.io/client-go/tools/clientcmd"
)
// CreateKsObjectConnection initializes a KS object connection, shared by mcpserver and httphandler
func CreateKsObjectConnection(namespace string, maxElapsedTime time.Duration) (spdxv1beta1.SpdxV1beta1Interface, error) {
var cfg *rest.Config
var err error
if kubeconfig := os.Getenv("KUBECONFIG"); kubeconfig != "" {
cfg, err = clientcmd.BuildConfigFromFlags("", kubeconfig)
} else {
home := os.Getenv("HOME")
kubeconfigPath := filepath.Join(home, ".kube", "config")
cfg, err = clientcmd.BuildConfigFromFlags("", kubeconfigPath)
if err != nil {
cfg, err = rest.InClusterConfig()
}
}
if err != nil {
return nil, err
}
// disable rate limiting
cfg.QPS = 0
cfg.RateLimiter = nil
// force GRPC
cfg.AcceptContentTypes = "application/vnd.kubernetes.protobuf"
cfg.ContentType = "application/vnd.kubernetes.protobuf"
return spdxv1beta1.NewForConfig(cfg)
}