From eb8dac0b10fdb118fffd497ddb47ca5149014906 Mon Sep 17 00:00:00 2001 From: Mujib Ahasan Date: Sat, 7 Mar 2026 02:57:23 +0530 Subject: [PATCH 1/4] feat: new falg --grype-db-url added to overload the url in kubescape scan command Signed-off-by: Mujib Ahasan --- cmd/patch/patch.go | 1 + cmd/scan/scan.go | 1 + core/cautils/scaninfo.go | 1 + core/core/image_scan.go | 6 +++++- core/core/patch.go | 6 +++++- core/core/scan.go | 6 +++++- pkg/imagescan/imagescan.go | 22 ++++++++++++++++++---- pkg/imagescan/imagescan_test.go | 2 +- 8 files changed, 37 insertions(+), 8 deletions(-) diff --git a/cmd/patch/patch.go b/cmd/patch/patch.go index 7a43f5a6..a1d58c12 100644 --- a/cmd/patch/patch.go +++ b/cmd/patch/patch.go @@ -80,6 +80,7 @@ func GetPatchCmd(ks meta.IKubescape) *cobra.Command { 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") + patchCmd.PersistentFlags().StringVar(&scanInfo.ListingURL, "grype-db-url", "", "Grype vulnerability database URL") return patchCmd } diff --git a/cmd/scan/scan.go b/cmd/scan/scan.go index 0dc41ff9..0f07a800 100644 --- a/cmd/scan/scan.go +++ b/cmd/scan/scan.go @@ -94,6 +94,7 @@ func GetScanCommand(ks meta.IKubescape) *cobra.Command { 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().StringSliceVar(&scanInfo.LabelsToCopy, "labels-to-copy", nil, "Labels to copy from workloads to scan reports for easy identification. e.g: --labels-to-copy=app,team,environment") + scanCmd.PersistentFlags().StringVar(&scanInfo.ListingURL, "grype-db-url", "", "Grype vulnerability database URL") 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.") diff --git a/core/cautils/scaninfo.go b/core/cautils/scaninfo.go index 810aac9a..d557051a 100644 --- a/core/cautils/scaninfo.go +++ b/core/cautils/scaninfo.go @@ -143,6 +143,7 @@ type ScanInfo struct { LabelsToCopy []string // Labels to copy from workloads to scan reports scanningContext *ScanningContext cleanups []func() + ListingURL string //Grype vulnerability database URL } type Getters struct { diff --git a/core/core/image_scan.go b/core/core/image_scan.go index 371e4716..046cc9cd 100644 --- a/core/core/image_scan.go +++ b/core/core/image_scan.go @@ -165,7 +165,11 @@ func getUniqueVulnerabilitiesAndSeverities(policies []VulnerabilitiesIgnorePolic func (ks *Kubescape) ScanImage(imgScanInfo *ksmetav1.ImageScanInfo, scanInfo *cautils.ScanInfo) (bool, error) { logger.L().Start(fmt.Sprintf("Scanning image %s...", imgScanInfo.Image)) - distCfg, installCfg, _ := imagescan.NewDefaultDBConfig() + distCfg, installCfg, _, err := imagescan.NewDefaultDBConfig(scanInfo.ListingURL) + if err != nil { + logger.L().StopError(fmt.Sprintf("Invalid Grype database URL '%s': %v", scanInfo.ListingURL, err)) + return false, err + } svc, err := imagescan.NewScanServiceWithMatchers(distCfg, installCfg, imgScanInfo.UseDefaultMatchers) if err != nil { logger.L().StopError(fmt.Sprintf("Failed to initialize image scanner: %s", err)) diff --git a/core/core/patch.go b/core/core/patch.go index 9b7d1409..d5d3e85f 100644 --- a/core/core/patch.go +++ b/core/core/patch.go @@ -48,7 +48,11 @@ func (ks *Kubescape) Patch(patchInfo *ksmetav1.PatchInfo, scanInfo *cautils.Scan logger.L().Start(fmt.Sprintf("Scanning image: %s", patchInfo.Image)) // Setup the scan service - distCfg, installCfg, _ := imagescan.NewDefaultDBConfig() + distCfg, installCfg, _, err := imagescan.NewDefaultDBConfig(scanInfo.ListingURL) + if err != nil { + logger.L().StopError(fmt.Sprintf("Invalid Grype database URL '%s': %v", scanInfo.ListingURL, err)) + return false, err + } svc, err := imagescan.NewScanServiceWithMatchers(distCfg, installCfg, scanInfo.UseDefaultMatchers) if err != nil { logger.L().StopError(fmt.Sprintf("Failed to initialize image scanner: %s", err)) diff --git a/core/core/scan.go b/core/core/scan.go index 572a3de6..fd3213fb 100644 --- a/core/core/scan.go +++ b/core/core/scan.go @@ -249,7 +249,11 @@ func scanImages(scanType cautils.ScanTypes, scanData *cautils.OPASessionObj, ctx } } - distCfg, installCfg, _ := imagescan.NewDefaultDBConfig() + distCfg, installCfg, _, err := imagescan.NewDefaultDBConfig(scanInfo.ListingURL) + if err != nil { + logger.L().StopError(fmt.Sprintf("Invalid Grype database URL '%s': %v", scanInfo.ListingURL, err)) + return + } svc, err := imagescan.NewScanServiceWithMatchers(distCfg, installCfg, scanInfo.UseDefaultMatchers) if err != nil { logger.L().StopError(fmt.Sprintf("Failed to initialize image scanner: %s", err)) diff --git a/pkg/imagescan/imagescan.go b/pkg/imagescan/imagescan.go index 38171b94..71cc90fd 100644 --- a/pkg/imagescan/imagescan.go +++ b/pkg/imagescan/imagescan.go @@ -4,6 +4,7 @@ import ( "context" "errors" "fmt" + "net/url" "path/filepath" "strings" @@ -42,16 +43,29 @@ func (c RegistryCredentials) IsEmpty() bool { return c.Username == "" || c.Password == "" } -func NewDefaultDBConfig() (distribution.Config, installation.Config, bool) { +func NewDefaultDBConfig(grypeURL string) (distribution.Config, installation.Config, bool, error) { dir := filepath.Join(xdg.CacheHome, defaultDBDirName) - url := defaultGrypeListingURL + finalURL := defaultGrypeListingURL + if grypeURL != "" { + parsed, err := url.ParseRequestURI(grypeURL) + if err != nil || parsed.Host == "" { + return distribution.Config{}, installation.Config{}, false, err + } + + if parsed.Scheme != "https" && parsed.Scheme != "http" { + return distribution.Config{}, installation.Config{}, false, fmt.Errorf("invalid scheme: %s", parsed.Scheme) + } + + finalURL = grypeURL + } + shouldUpdate := true return distribution.Config{ - LatestURL: url, + LatestURL: finalURL, }, installation.Config{ DBRootDir: dir, - }, shouldUpdate + }, shouldUpdate, nil } func getMatchers(useDefaultMatchers bool) []match.Matcher { diff --git a/pkg/imagescan/imagescan_test.go b/pkg/imagescan/imagescan_test.go index ff7d54e0..abd5a311 100644 --- a/pkg/imagescan/imagescan_test.go +++ b/pkg/imagescan/imagescan_test.go @@ -176,7 +176,7 @@ func TestNewScanServiceWithMatchers(t *testing.T) { func TestNewScanServiceWithMatchersIntegration(t *testing.T) { // Test the actual NewScanServiceWithMatchers function - distCfg, installCfg, _ := NewDefaultDBConfig() + distCfg, installCfg, _, _ := NewDefaultDBConfig("") // Test with default matchers enabled svcWithDefault, err := NewScanServiceWithMatchers(distCfg, installCfg, true) From d7be453fea6a4310e70556dc0f266b68be2145ff Mon Sep 17 00:00:00 2001 From: Mujib Ahasan Date: Sat, 7 Mar 2026 03:15:34 +0530 Subject: [PATCH 2/4] fix: missing host do not return nil error Signed-off-by: Mujib Ahasan --- pkg/imagescan/imagescan.go | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/pkg/imagescan/imagescan.go b/pkg/imagescan/imagescan.go index 71cc90fd..a15673d7 100644 --- a/pkg/imagescan/imagescan.go +++ b/pkg/imagescan/imagescan.go @@ -48,10 +48,14 @@ func NewDefaultDBConfig(grypeURL string) (distribution.Config, installation.Conf finalURL := defaultGrypeListingURL if grypeURL != "" { parsed, err := url.ParseRequestURI(grypeURL) - if err != nil || parsed.Host == "" { + if err != nil { return distribution.Config{}, installation.Config{}, false, err } + if parsed.Host == "" { + return distribution.Config{}, installation.Config{}, false, fmt.Errorf("invalid grype DB URL: missing host") + } + if parsed.Scheme != "https" && parsed.Scheme != "http" { return distribution.Config{}, installation.Config{}, false, fmt.Errorf("invalid scheme: %s", parsed.Scheme) } From 0372a4fca670b3637f4f0274782736203ec0eef7 Mon Sep 17 00:00:00 2001 From: Mujib Ahasan Date: Tue, 10 Mar 2026 23:14:33 +0530 Subject: [PATCH 3/4] log added in scanImage(): value of scanInfo.ListingURL for reference Signed-off-by: Mujib Ahasan --- pkg/imagescan/imagescan.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/pkg/imagescan/imagescan.go b/pkg/imagescan/imagescan.go index a15673d7..25be72f8 100644 --- a/pkg/imagescan/imagescan.go +++ b/pkg/imagescan/imagescan.go @@ -26,6 +26,7 @@ import ( "github.com/anchore/grype/grype/vulnerability" "github.com/anchore/stereoscope/pkg/image" "github.com/anchore/syft/syft" + "github.com/kubescape/go-logger" "github.com/kubescape/kubescape/v3/core/cautils" ) @@ -47,6 +48,7 @@ func NewDefaultDBConfig(grypeURL string) (distribution.Config, installation.Conf dir := filepath.Join(xdg.CacheHome, defaultDBDirName) finalURL := defaultGrypeListingURL if grypeURL != "" { + logger.L().Info(fmt.Sprintf("Using custom Grype database URL: %s", grypeURL)) parsed, err := url.ParseRequestURI(grypeURL) if err != nil { return distribution.Config{}, installation.Config{}, false, err From ab97d676ae42202681ea9cef84b04dcd83ffc676 Mon Sep 17 00:00:00 2001 From: Mujib Ahasan Date: Sun, 15 Mar 2026 23:50:58 +0530 Subject: [PATCH 4/4] README.md updated Signed-off-by: Mujib Ahasan --- README.md | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/README.md b/README.md index f880b708..798e632d 100644 --- a/README.md +++ b/README.md @@ -237,6 +237,15 @@ kubescape scan image nginx:1.21 -v kubescape scan image myregistry/myimage:tag --username user --password pass ``` +#### Using an Offline Grype Database +```bash +# Start the offline Grype-DB server (using docker) +docker run --rm -p8080:8080 quay.io/kubescape/grype-offline-db:v6-latest + +# Scan an image using the offline database: +kubescape scan image --grype-db-url http://localhost:8080/databases/ nginx:latest +``` + ### Auto-Fix Automatically fix misconfigurations in your manifest files: