mirror of
https://github.com/kubescape/kubescape.git
synced 2026-04-15 06:58:11 +00:00
fix patch command logic
Signed-off-by: Anubhav Gupta <mail.anubhav06@gmail.com>
This commit is contained in:
@@ -4,9 +4,11 @@ import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
ref "github.com/distribution/distribution/reference"
|
||||
"github.com/docker/distribution/reference"
|
||||
|
||||
"github.com/kubescape/go-logger"
|
||||
"github.com/kubescape/kubescape/v2/core/cautils"
|
||||
@@ -47,7 +49,6 @@ func GetPatchCmd(ks meta.IKubescape) *cobra.Command {
|
||||
patchCmd.PersistentFlags().StringVarP(&patchInfo.PatchedImageTag, "tag", "t", "", "Tag for the patched image. Defaults to '<image-tag>-patched' ")
|
||||
patchCmd.PersistentFlags().StringVarP(&patchInfo.BuildkitAddress, "address", "a", "unix:///run/buildkit/buildkitd.sock", "Address of buildkitd service, defaults to local buildkitd.sock")
|
||||
patchCmd.PersistentFlags().DurationVar(&patchInfo.Timeout, "timeout", 5*time.Minute, "Timeout for the operation, defaults to '5m'")
|
||||
patchCmd.PersistentFlags().BoolVarP(&patchInfo.IncludeReport, "report", "r", false, "Generate & save a before and after report for the image (json format))")
|
||||
|
||||
patchCmd.PersistentFlags().StringVarP(&patchInfo.Username, "username", "u", "", "Username for registry login")
|
||||
patchCmd.PersistentFlags().StringVarP(&patchInfo.Password, "password", "p", "", "Password for registry login")
|
||||
@@ -62,10 +63,15 @@ func validateImagePatchInfo(patchInfo *metav1.PatchInfo) error {
|
||||
return errors.New("image tag is required")
|
||||
}
|
||||
|
||||
// Check if image is in canonical format (required by copacetic for patching images)
|
||||
// Convert image to canonical format (required by copacetic for patching images)
|
||||
patchInfoImage, err := cautils.Normalize_image_name(patchInfo.Image)
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
patchInfo.Image = patchInfoImage
|
||||
// Parse the image full name to get image name and tag
|
||||
named, err := ref.ParseNamed(patchInfo.Image)
|
||||
if err != nil{
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -87,8 +93,18 @@ func validateImagePatchInfo(patchInfo *metav1.PatchInfo) error {
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
patchInfo.ImageName = named.Name()
|
||||
|
||||
// Extract the "image" name from the canonical Image URL
|
||||
// If it's an official docker image, we store just the "image-name". Else if a docker repo then we store as "repo/image". Else complete URL
|
||||
ref, _ := reference.ParseNormalizedNamed(patchInfoImage)
|
||||
imageName := named.Name()
|
||||
if strings.Contains(imageName, "docker.io/library/") {
|
||||
imageName = reference.Path(ref)
|
||||
imageName = imageName[strings.LastIndex(imageName, "/")+1:]
|
||||
} else if strings.Contains(imageName, "docker.io/") {
|
||||
imageName = reference.Path(ref)
|
||||
}
|
||||
patchInfo.ImageName = imageName
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
package opaprocessor
|
||||
package cautils
|
||||
|
||||
import (
|
||||
"github.com/docker/distribution/reference"
|
||||
)
|
||||
|
||||
func normalize_image_name(img string) (string, error) {
|
||||
func Normalize_image_name(img string) (string, error) {
|
||||
name, err := reference.ParseNormalizedNamed(img)
|
||||
if err != nil {
|
||||
return "", err
|
||||
@@ -1,4 +1,4 @@
|
||||
package opaprocessor
|
||||
package cautils
|
||||
|
||||
import (
|
||||
"testing"
|
||||
@@ -21,7 +21,7 @@ func Test_normalize_name(t *testing.T) {
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
name, _ := normalize_image_name(tt.img)
|
||||
name, _ := Normalize_image_name(tt.img)
|
||||
assert.Equal(t, tt.want, name, tt.name)
|
||||
})
|
||||
}
|
||||
@@ -17,6 +17,7 @@ import (
|
||||
|
||||
"github.com/kubescape/kubescape/v2/core/pkg/resultshandling"
|
||||
copa "github.com/project-copacetic/copacetic/pkg/patch"
|
||||
log "github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
func (ks *Kubescape) Patch(ctx context.Context, patchInfo *ksmetav1.PatchInfo) error {
|
||||
@@ -52,40 +53,37 @@ func (ks *Kubescape) Patch(ctx context.Context, patchInfo *ksmetav1.PatchInfo) e
|
||||
|
||||
// ===================== Patch the image using copacetic =====================
|
||||
logger.L().Start("Patching image...")
|
||||
|
||||
sout, serr := os.Stdout, os.Stderr
|
||||
if logger.L().GetLevel() != "debug" {
|
||||
disableCopaLogger()
|
||||
}
|
||||
|
||||
if err := copa.Patch(ctx, patchInfo.Timeout, patchInfo.BuildkitAddress, patchInfo.Image, fileName, patchInfo.PatchedImageTag, ""); err != nil {
|
||||
return err
|
||||
}
|
||||
logger.L().StopSuccess("Patched image successfully")
|
||||
|
||||
if logger.L().GetLevel() != "debug" {
|
||||
enableCopaLogger(sout, serr)
|
||||
}
|
||||
|
||||
patchedImageName := fmt.Sprintf("%s:%s", patchInfo.ImageName, patchInfo.PatchedImageTag)
|
||||
logger.L().StopSuccess(fmt.Sprintf("Patched image successfully. Loaded image: %s", patchedImageName))
|
||||
|
||||
// ===================== Re-scan the image =====================
|
||||
|
||||
// Re-scan the image
|
||||
patchedImageName := fmt.Sprintf("%s:%s", patchInfo.ImageName, patchInfo.PatchedImageTag)
|
||||
logger.L().Start(fmt.Sprintf("Re-Scanning image: %s", patchedImageName))
|
||||
|
||||
scanResultsPatched, err := svc.Scan(ctx, patchedImageName, creds)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// Save the patched image's scan results to a file in json format, if requested
|
||||
fileNamePatched := fmt.Sprintf("%s:%s.json", patchInfo.ImageName, patchInfo.PatchedImageTag)
|
||||
fileNamePatched = strings.ReplaceAll(fileNamePatched, "/", "-")
|
||||
|
||||
if patchInfo.IncludeReport {
|
||||
pres = presenter.GetPresenter("json", "", false, *scanResultsPatched)
|
||||
writer = printer.GetWriter(ctx, fileNamePatched)
|
||||
if err := pres.Present(writer); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
logger.L().StopSuccess(fmt.Sprintf("Successfully re-scanned image: %s", patchedImageName))
|
||||
|
||||
// ===================== Clean up =====================
|
||||
// Remove the scan results files, which were used to patch the image
|
||||
if !patchInfo.IncludeReport {
|
||||
if err := os.Remove(fileName); err != nil {
|
||||
logger.L().Warning(fmt.Sprintf("failed to remove residual file: %v", fileName), helpers.Error(err))
|
||||
}
|
||||
// Remove the scan results file, which was used to patch the image
|
||||
if err := os.Remove(fileName); err != nil {
|
||||
logger.L().Warning(fmt.Sprintf("failed to remove residual file: %v", fileName), helpers.Error(err))
|
||||
}
|
||||
|
||||
// ===================== Results Handling =====================
|
||||
@@ -103,11 +101,16 @@ func (ks *Kubescape) Patch(ctx context.Context, patchInfo *ksmetav1.PatchInfo) e
|
||||
}
|
||||
resultsHandler.HandleResults(ctx)
|
||||
|
||||
if patchInfo.IncludeReport {
|
||||
logger.L().Success("Results saved", helpers.String("filename", fileName))
|
||||
logger.L().Success("Results saved", helpers.String("filename", fileNamePatched))
|
||||
}
|
||||
|
||||
return nil
|
||||
|
||||
}
|
||||
|
||||
func disableCopaLogger() {
|
||||
os.Stdout, os.Stderr = nil, nil
|
||||
null, _ := os.Open(os.DevNull)
|
||||
log.SetOutput(null)
|
||||
}
|
||||
|
||||
func enableCopaLogger(sout, serr *os.File) {
|
||||
os.Stdout, os.Stderr = sout, serr
|
||||
log.SetOutput(os.Stdout)
|
||||
}
|
||||
|
||||
@@ -7,9 +7,8 @@ type PatchInfo struct {
|
||||
PatchedImageTag string // can be empty, if empty then the image tag will be patched with the latest tag
|
||||
BuildkitAddress string // buildkit address
|
||||
Timeout time.Duration // timeout for patching an image
|
||||
IncludeReport bool // include report in the output
|
||||
|
||||
// Image registry credentials
|
||||
|
||||
// Image registry credentials
|
||||
Username string // username for registry login
|
||||
Password string // password for registry login
|
||||
|
||||
|
||||
@@ -106,6 +106,6 @@ var imageNameNormalizeDefinition = func(bctx rego.BuiltinContext, a *ast.Term) (
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("invalid parameter type: %v", err)
|
||||
}
|
||||
normalizedName, err := normalize_image_name(string(aStr))
|
||||
normalizedName, err := cautils.Normalize_image_name(string(aStr))
|
||||
return ast.StringTerm(normalizedName), err
|
||||
}
|
||||
|
||||
@@ -75,7 +75,6 @@ func printConfigurationsScanning(opaSessionObj *cautils.OPASessionObj, ctx conte
|
||||
}
|
||||
|
||||
func (jp *JsonPrinter) PrintImageScan(ctx context.Context, scanResults *models.PresenterConfig) error {
|
||||
// presenterConfig, _ := presenter.ValidatedConfig("json", "", false)
|
||||
pres := presenter.GetPresenter("json", "", false, *scanResults)
|
||||
|
||||
return pres.Present(jp.writer)
|
||||
|
||||
@@ -48,8 +48,8 @@ func (ip *ImagePrinter) PrintConfigurationsScanning(summaryDetails *reportsummar
|
||||
|
||||
func (ip *ImagePrinter) PrintNextSteps() {
|
||||
if ip.verboseMode {
|
||||
printNextSteps(ip.writer, []string{CICDSetupText, installHelmText, imagePatchText}, true)
|
||||
printNextSteps(ip.writer, []string{CICDSetupText, installHelmText}, true)
|
||||
return
|
||||
}
|
||||
printNextSteps(ip.writer, []string{imageScanVerboseRunText, CICDSetupText, installHelmText, imagePatchText}, true)
|
||||
printNextSteps(ip.writer, []string{imageScanVerboseRunText, CICDSetupText, installHelmText}, true)
|
||||
}
|
||||
|
||||
@@ -27,7 +27,6 @@ var (
|
||||
clusterScanRunText = fmt.Sprintf("Run a cluster scan: %s", getCallToActionString("'$ kubescape scan'"))
|
||||
installHelmText = fmt.Sprintf("Install Kubescape in your cluster for continuous monitoring: %s", linkToHelm)
|
||||
CICDSetupText = fmt.Sprintf("Add Kubescape to your CI/CD pipeline: %s", linkToCICDSetup)
|
||||
imagePatchText = fmt.Sprintf("Try patching your images with: %s", getCallToActionString("'$ kubescape patch'"))
|
||||
complianceFrameworks = []string{"nsa", "mitre"}
|
||||
cveSeverities = []string{"Critical", "High", "Medium", "Low", "Negligible", "Unknown"}
|
||||
)
|
||||
|
||||
2
go.mod
2
go.mod
@@ -38,6 +38,7 @@ require (
|
||||
github.com/schollz/progressbar/v3 v3.13.0
|
||||
github.com/sergi/go-diff v1.3.1
|
||||
github.com/sigstore/cosign/v2 v2.1.1
|
||||
github.com/sirupsen/logrus v1.9.3
|
||||
github.com/spf13/cobra v1.7.0
|
||||
github.com/stretchr/testify v1.8.4
|
||||
github.com/whilp/git-urls v1.0.0
|
||||
@@ -343,7 +344,6 @@ require (
|
||||
github.com/sigstore/rekor v1.2.2-0.20230530122220-67cc9e58bd23 // indirect
|
||||
github.com/sigstore/sigstore v1.7.1 // indirect
|
||||
github.com/sigstore/timestamp-authority v1.1.1 // indirect
|
||||
github.com/sirupsen/logrus v1.9.3 // indirect
|
||||
github.com/skeema/knownhosts v1.2.0 // indirect
|
||||
github.com/skratchdot/open-golang v0.0.0-20200116055534-eef842397966 // indirect
|
||||
github.com/spdx/tools-golang v0.5.3 // indirect
|
||||
|
||||
Reference in New Issue
Block a user