mirror of
https://github.com/kubescape/kubescape.git
synced 2026-04-15 06:58:11 +00:00
Resolving conflicts
Signed-off-by: Amit Schendel <amitschendel@gmail.com>
This commit is contained in:
@@ -150,7 +150,7 @@ func (policyHandler *PolicyHandler) downloadScanPolicies(ctx context.Context, po
|
||||
logger.L().Debug("Downloading framework", helpers.String("framework", rule.Identifier))
|
||||
receivedFramework, err := policyHandler.getters.PolicyGetter.GetFramework(rule.Identifier)
|
||||
if err != nil {
|
||||
return frameworks, policyDownloadError(err)
|
||||
return frameworks, frameworkDownloadError(err, rule.Identifier)
|
||||
}
|
||||
if err := validateFramework(receivedFramework); err != nil {
|
||||
return frameworks, err
|
||||
@@ -171,7 +171,7 @@ func (policyHandler *PolicyHandler) downloadScanPolicies(ctx context.Context, po
|
||||
logger.L().Debug("Downloading control", helpers.String("control", policy.Identifier))
|
||||
receivedControl, err = policyHandler.getters.PolicyGetter.GetControl(policy.Identifier)
|
||||
if err != nil {
|
||||
return frameworks, policyDownloadError(err)
|
||||
return frameworks, controlDownloadError(err, policy.Identifier)
|
||||
}
|
||||
if receivedControl != nil {
|
||||
f.Controls = append(f.Controls, *receivedControl)
|
||||
|
||||
@@ -17,10 +17,22 @@ func getScanKind(policyIdentifier []cautils.PolicyIdentifier) apisv1.Notificatio
|
||||
}
|
||||
return "unknown"
|
||||
}
|
||||
func policyDownloadError(err error) error {
|
||||
func frameworkDownloadError(err error, fwName string) error {
|
||||
if strings.Contains(err.Error(), "unsupported protocol scheme") {
|
||||
err = fmt.Errorf("failed to download from GitHub release, try running with `--use-default` flag")
|
||||
}
|
||||
if strings.Contains(err.Error(), "not found") {
|
||||
err = fmt.Errorf("framework '%s' not found, run `kubescape list frameworks` for available frameworks", fwName)
|
||||
}
|
||||
return err
|
||||
}
|
||||
func controlDownloadError(err error, controls string) error {
|
||||
if strings.Contains(err.Error(), "unsupported protocol scheme") {
|
||||
err = fmt.Errorf("failed to download from GitHub release, try running with `--use-default` flag")
|
||||
}
|
||||
if strings.Contains(err.Error(), "not found") {
|
||||
err = fmt.Errorf("control '%s' not found, run `kubescape list controls` for available controls", controls)
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
|
||||
@@ -89,6 +89,8 @@ func TestPolicyDownloadError(t *testing.T) {
|
||||
tests := []struct {
|
||||
err error
|
||||
want error
|
||||
name string
|
||||
kind string
|
||||
}{
|
||||
{
|
||||
err: errors.New("Some error"),
|
||||
@@ -98,11 +100,31 @@ func TestPolicyDownloadError(t *testing.T) {
|
||||
err: errors.New("unsupported protocol scheme"),
|
||||
want: fmt.Errorf("failed to download from GitHub release, try running with `--use-default` flag"),
|
||||
},
|
||||
{
|
||||
err: errors.New("framework 'cis' not found"),
|
||||
want: fmt.Errorf("framework 'cis' not found, run `kubescape list frameworks` for available frameworks"),
|
||||
name: "cis",
|
||||
kind: "framework",
|
||||
},
|
||||
{
|
||||
err: errors.New("control 'c-0005' not found"),
|
||||
want: fmt.Errorf("control 'c-0005' not found, run `kubescape list controls` for available controls"),
|
||||
name: "c-0005",
|
||||
kind: "control",
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run("", func(t *testing.T) {
|
||||
assert.Equal(t, tt.want, policyDownloadError(tt.err))
|
||||
switch tt.kind {
|
||||
case "framework":
|
||||
assert.Equal(t, tt.want, frameworkDownloadError(tt.err, tt.name))
|
||||
case "control":
|
||||
assert.Equal(t, tt.want, controlDownloadError(tt.err, tt.name))
|
||||
default:
|
||||
assert.Equal(t, tt.want, frameworkDownloadError(tt.err, tt.name))
|
||||
assert.Equal(t, tt.want, controlDownloadError(tt.err, tt.name))
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -41,22 +41,27 @@ func (fileHandler *FileResourceHandler) GetResources(ctx context.Context, sessio
|
||||
for path := range scanInfo.InputPatterns {
|
||||
var workloadIDToSource map[string]reporthandling.Source
|
||||
var workloads []workloadinterface.IMetadata
|
||||
var workloadIDToMappingNodes map[string]cautils.MappingNodes
|
||||
var err error
|
||||
|
||||
if scanInfo.ChartPath != "" && scanInfo.FilePath != "" {
|
||||
workloadIDToSource, workloads, err = getWorkloadFromHelmChart(ctx, scanInfo.ChartPath, scanInfo.FilePath)
|
||||
workloadIDToSource, workloads, workloadIDToMappingNodes, err = getWorkloadFromHelmChart(ctx, scanInfo.ChartPath, scanInfo.FilePath)
|
||||
if err != nil {
|
||||
// We should probably ignore the error so we can continue scanning other charts
|
||||
}
|
||||
} else {
|
||||
workloadIDToSource, workloads, err = getResourcesFromPath(ctx, scanInfo.InputPatterns[path])
|
||||
workloadIDToSource, workloads, workloadIDToMappingNodes, err = getResourcesFromPath(ctx, scanInfo.InputPatterns[path])
|
||||
if err != nil {
|
||||
return nil, allResources, nil, nil, err
|
||||
}
|
||||
}
|
||||
if len(workloads) == 0 {
|
||||
logger.L().Debug("path ignored because contains only a non-kubernetes file", helpers.String("path", scanInfo.InputPatterns[path]))
|
||||
continue
|
||||
}
|
||||
|
||||
for k, v := range workloadIDToSource {
|
||||
sessionObj.ResourceSource[k] = v
|
||||
sessionObj.TemplateMapping[k] = workloadIDToMappingNodes[k]
|
||||
}
|
||||
|
||||
// map all resources: map["/apiVersion/version/kind"][]<k8s workloads>
|
||||
@@ -102,10 +107,10 @@ func (fileHandler *FileResourceHandler) GetResources(ctx context.Context, sessio
|
||||
func (fileHandler *FileResourceHandler) GetCloudProvider() string {
|
||||
return ""
|
||||
}
|
||||
func getWorkloadFromHelmChart(ctx context.Context, helmPath, workloadPath string) (map[string]reporthandling.Source, []workloadinterface.IMetadata, error) {
|
||||
func getWorkloadFromHelmChart(ctx context.Context, helmPath, workloadPath string) (map[string]reporthandling.Source, []workloadinterface.IMetadata, map[string]cautils.MappingNodes, error) {
|
||||
clonedRepo, err := cloneGitRepo(&helmPath)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
return nil, nil, nil, err
|
||||
}
|
||||
if clonedRepo != "" {
|
||||
defer os.RemoveAll(clonedRepo)
|
||||
@@ -114,7 +119,7 @@ func getWorkloadFromHelmChart(ctx context.Context, helmPath, workloadPath string
|
||||
// Get repo root
|
||||
repoRoot, gitRepo := extractGitRepo(helmPath)
|
||||
|
||||
helmSourceToWorkloads, helmSourceToChart := cautils.LoadResourcesFromHelmCharts(ctx, helmPath)
|
||||
helmSourceToWorkloads, helmSourceToChart, helmSourceToNodes := cautils.LoadResourcesFromHelmCharts(ctx, helmPath)
|
||||
|
||||
if clonedRepo != "" {
|
||||
workloadPath = clonedRepo + workloadPath
|
||||
@@ -122,27 +127,34 @@ func getWorkloadFromHelmChart(ctx context.Context, helmPath, workloadPath string
|
||||
|
||||
wlSource, ok := helmSourceToWorkloads[workloadPath]
|
||||
if !ok {
|
||||
return nil, nil, fmt.Errorf("workload %s not found in chart %s", workloadPath, helmPath)
|
||||
return nil, nil, nil, fmt.Errorf("workload %s not found in chart %s", workloadPath, helmPath)
|
||||
}
|
||||
|
||||
if len(wlSource) != 1 {
|
||||
return nil, nil, fmt.Errorf("workload %s found multiple times in chart %s", workloadPath, helmPath)
|
||||
return nil, nil, nil, fmt.Errorf("workload %s found multiple times in chart %s", workloadPath, helmPath)
|
||||
}
|
||||
|
||||
helmChart, ok := helmSourceToChart[workloadPath]
|
||||
if !ok {
|
||||
return nil, nil, fmt.Errorf("helmChart not found for workload %s", workloadPath)
|
||||
return nil, nil, nil, fmt.Errorf("helmChart not found for workload %s", workloadPath)
|
||||
}
|
||||
|
||||
templatesNodes, ok := helmSourceToNodes[workloadPath]
|
||||
if !ok {
|
||||
return nil, nil, nil, fmt.Errorf("templatesNodes not found for workload %s", workloadPath)
|
||||
}
|
||||
|
||||
workloadSource := getWorkloadSourceHelmChart(repoRoot, helmPath, gitRepo, helmChart)
|
||||
|
||||
workloadIDToSource := make(map[string]reporthandling.Source, 1)
|
||||
workloadIDToNodes := make(map[string]cautils.MappingNodes, 1)
|
||||
workloadIDToSource[wlSource[0].GetID()] = workloadSource
|
||||
workloadIDToNodes[wlSource[0].GetID()] = templatesNodes
|
||||
|
||||
workloads := []workloadinterface.IMetadata{}
|
||||
workloads = append(workloads, wlSource...)
|
||||
|
||||
return workloadIDToSource, workloads, nil
|
||||
return workloadIDToSource, workloads, workloadIDToNodes, nil
|
||||
|
||||
}
|
||||
|
||||
@@ -176,13 +188,14 @@ func getWorkloadSourceHelmChart(repoRoot string, source string, gitRepo *cautils
|
||||
}
|
||||
}
|
||||
|
||||
func getResourcesFromPath(ctx context.Context, path string) (map[string]reporthandling.Source, []workloadinterface.IMetadata, error) {
|
||||
func getResourcesFromPath(ctx context.Context, path string) (map[string]reporthandling.Source, []workloadinterface.IMetadata, map[string]cautils.MappingNodes, error) {
|
||||
workloadIDToSource := make(map[string]reporthandling.Source, 0)
|
||||
workloadIDToNodes := make(map[string]cautils.MappingNodes)
|
||||
workloads := []workloadinterface.IMetadata{}
|
||||
|
||||
clonedRepo, err := cloneGitRepo(&path)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
return nil, nil, nil, err
|
||||
}
|
||||
if clonedRepo != "" {
|
||||
defer os.RemoveAll(clonedRepo)
|
||||
@@ -266,10 +279,11 @@ func getResourcesFromPath(ctx context.Context, path string) (map[string]reportha
|
||||
}
|
||||
|
||||
// load resources from helm charts
|
||||
helmSourceToWorkloads, helmSourceToChart := cautils.LoadResourcesFromHelmCharts(ctx, path)
|
||||
helmSourceToWorkloads, helmSourceToChart, helmSourceToNodes := cautils.LoadResourcesFromHelmCharts(ctx, path)
|
||||
for source, ws := range helmSourceToWorkloads {
|
||||
workloads = append(workloads, ws...)
|
||||
helmChart := helmSourceToChart[source]
|
||||
templatesNodes := helmSourceToNodes[source]
|
||||
|
||||
if clonedRepo != "" {
|
||||
url, err := gitRepo.GetRemoteUrl()
|
||||
@@ -280,21 +294,29 @@ func getResourcesFromPath(ctx context.Context, path string) (map[string]reportha
|
||||
helmChart.Path = strings.TrimSuffix(url, ".git")
|
||||
repoRoot = ""
|
||||
source = strings.TrimPrefix(source, fmt.Sprintf("%s/", clonedRepo))
|
||||
templatesNodes.TemplateFileName = source
|
||||
}
|
||||
|
||||
workloadSource := getWorkloadSourceHelmChart(repoRoot, source, gitRepo, helmChart)
|
||||
|
||||
for i := range ws {
|
||||
workloadIDToSource[ws[i].GetID()] = workloadSource
|
||||
workloadIDToNodes[ws[i].GetID()] = templatesNodes
|
||||
// workloadIDToNodes[ws[i].GetID()].Nodes = templatesNodes.Nodes
|
||||
// workloadIDToNodes[ws[i].GetID()].TemplateFileName = templatesNodes.TemplateFileName
|
||||
// helmSourceToNodes[source]
|
||||
}
|
||||
}
|
||||
|
||||
if len(helmSourceToWorkloads) > 0 {
|
||||
if len(helmSourceToWorkloads) > 0 { // && len(helmSourceToNodes) > 0
|
||||
logger.L().Debug("helm templates found in local storage", helpers.Int("helmTemplates", len(helmSourceToWorkloads)), helpers.Int("workloads", len(workloads)))
|
||||
} else {
|
||||
workloadIDToNodes = nil
|
||||
}
|
||||
|
||||
//patch, get value from env
|
||||
// Load resources from Kustomize directory
|
||||
kustomizeSourceToWorkloads, kustomizeDirectoryName := cautils.LoadResourcesFromKustomizeDirectory(ctx, path)
|
||||
kustomizeSourceToWorkloads, kustomizeDirectoryName := cautils.LoadResourcesFromKustomizeDirectory(ctx, path) //?
|
||||
|
||||
// update workloads and workloadIDToSource with workloads from Kustomize Directory
|
||||
for source, ws := range kustomizeSourceToWorkloads {
|
||||
@@ -331,7 +353,7 @@ func getResourcesFromPath(ctx context.Context, path string) (map[string]reportha
|
||||
}
|
||||
}
|
||||
|
||||
return workloadIDToSource, workloads, nil
|
||||
return workloadIDToSource, workloads, workloadIDToNodes, nil
|
||||
}
|
||||
|
||||
func extractGitRepo(path string) (string, *cautils.LocalGitRepository) {
|
||||
|
||||
@@ -9,7 +9,6 @@ import (
|
||||
"github.com/kubescape/go-logger/helpers"
|
||||
"github.com/kubescape/k8s-interface/k8sinterface"
|
||||
"github.com/kubescape/k8s-interface/workloadinterface"
|
||||
"github.com/kubescape/kubescape/v3/core/cautils"
|
||||
"github.com/kubescape/opa-utils/objectsenvelopes"
|
||||
)
|
||||
|
||||
@@ -17,20 +16,23 @@ import (
|
||||
func cloneGitRepo(path *string) (string, error) {
|
||||
var clonedDir string
|
||||
|
||||
// Clone git repository if needed
|
||||
gitURL, err := giturl.NewGitAPI(*path)
|
||||
if err == nil {
|
||||
logger.L().Info("cloning", helpers.String("repository url", gitURL.GetURL().String()))
|
||||
cautils.StartSpinner()
|
||||
clonedDir, err = cloneRepo(gitURL)
|
||||
cautils.StopSpinner()
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("failed to clone git repo '%s', %w", gitURL.GetURL().String(), err)
|
||||
}
|
||||
|
||||
*path = filepath.Join(clonedDir, gitURL.GetPath())
|
||||
|
||||
if err != nil {
|
||||
return "", nil
|
||||
}
|
||||
|
||||
// Clone git repository if needed
|
||||
logger.L().Start("cloning", helpers.String("repository url", gitURL.GetURL().String()))
|
||||
|
||||
clonedDir, err = cloneRepo(gitURL)
|
||||
if err != nil {
|
||||
logger.L().StopError("failed to clone git repo", helpers.String("url", gitURL.GetURL().String()), helpers.Error(err))
|
||||
return "", fmt.Errorf("failed to clone git repo '%s', %w", gitURL.GetURL().String(), err)
|
||||
}
|
||||
|
||||
*path = filepath.Join(clonedDir, gitURL.GetPath())
|
||||
logger.L().StopSuccess("Done accessing local objects")
|
||||
|
||||
return clonedDir, nil
|
||||
}
|
||||
|
||||
|
||||
@@ -36,8 +36,8 @@ func CollectResources(ctx context.Context, rsrcHandler IResourceHandler, policyI
|
||||
opaSessionObj.ExternalResources = externalResources
|
||||
opaSessionObj.ExcludedRules = excludedRulesMap
|
||||
|
||||
if (opaSessionObj.K8SResources == nil || len(opaSessionObj.K8SResources) == 0) && (opaSessionObj.ExternalResources == nil || len(opaSessionObj.ExternalResources) == 0) {
|
||||
return fmt.Errorf("empty list of resources")
|
||||
if (opaSessionObj.K8SResources == nil || len(opaSessionObj.K8SResources) == 0) && (opaSessionObj.ExternalResources == nil || len(opaSessionObj.ExternalResources) == 0) || len(opaSessionObj.AllResources) == 0 {
|
||||
return fmt.Errorf("no resources found to scan")
|
||||
}
|
||||
|
||||
return nil
|
||||
|
||||
@@ -132,7 +132,7 @@ func (k8sHandler *K8sResourceHandler) GetResources(ctx context.Context, sessionO
|
||||
cautils.StopSpinner()
|
||||
logger.L().Success("Requested Host scanner data")
|
||||
} else {
|
||||
cautils.SetInfoMapForResources("This control requires the Kubescape operator installed. To install it, go to\n https://kubescape.io/docs/install-operator/.", hostResources, sessionObj.InfoMap)
|
||||
cautils.SetInfoMapForResources("This control is scanned exclusively by the Kubescape operator, not the Kubescape CLI. Install the Kubescape operator:\n https://kubescape.io/docs/install-operator/.", hostResources, sessionObj.InfoMap)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -8,12 +8,16 @@ import (
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"github.com/anchore/clio"
|
||||
"github.com/anchore/grype/grype/presenter"
|
||||
"github.com/anchore/grype/grype/presenter/models"
|
||||
logger "github.com/kubescape/go-logger"
|
||||
"github.com/kubescape/go-logger/helpers"
|
||||
"github.com/kubescape/kubescape/v3/core/cautils"
|
||||
"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 (
|
||||
@@ -54,11 +58,41 @@ func (jp *JsonPrinter) Score(score float32) {
|
||||
fmt.Fprintf(os.Stderr, "\nOverall compliance-score (100- Excellent, 0- All failed): %d\n", cautils.Float32ToInt(score))
|
||||
|
||||
}
|
||||
func (jp *JsonPrinter) convertToImageScanSummary(imageScanData []cautils.ImageScanData) (*imageprinter.ImageScanSummary, error) {
|
||||
imageScanSummary := imageprinter.ImageScanSummary{
|
||||
CVEs: []imageprinter.CVE{},
|
||||
PackageScores: map[string]*imageprinter.PackageScore{},
|
||||
MapsSeverityToSummary: map[string]*imageprinter.SeveritySummary{},
|
||||
}
|
||||
|
||||
for i := range imageScanData {
|
||||
if !slices.Contains(imageScanSummary.Images, imageScanData[i].Image) {
|
||||
imageScanSummary.Images = append(imageScanSummary.Images, imageScanData[i].Image)
|
||||
}
|
||||
|
||||
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(doc.Matches, imageScanSummary.PackageScores)
|
||||
|
||||
setSeverityToSummaryMap(CVEs, imageScanSummary.MapsSeverityToSummary)
|
||||
}
|
||||
|
||||
return &imageScanSummary, nil
|
||||
}
|
||||
|
||||
func (jp *JsonPrinter) ActionPrint(ctx context.Context, opaSessionObj *cautils.OPASessionObj, imageScanData []cautils.ImageScanData) {
|
||||
var err error
|
||||
|
||||
if opaSessionObj != nil {
|
||||
err = printConfigurationsScanning(opaSessionObj, ctx, jp)
|
||||
err = printConfigurationsScanning(opaSessionObj, ctx, imageScanData, jp)
|
||||
} else if imageScanData != nil {
|
||||
err = jp.PrintImageScan(ctx, imageScanData[0].PresenterConfig)
|
||||
} else {
|
||||
@@ -73,16 +107,67 @@ func (jp *JsonPrinter) ActionPrint(ctx context.Context, opaSessionObj *cautils.O
|
||||
printer.LogOutputFile(jp.writer.Name())
|
||||
}
|
||||
|
||||
func printConfigurationsScanning(opaSessionObj *cautils.OPASessionObj, ctx context.Context, jp *JsonPrinter) error {
|
||||
r, err := json.Marshal(FinalizeResults(opaSessionObj))
|
||||
if err != nil {
|
||||
return err
|
||||
func printConfigurationsScanning(opaSessionObj *cautils.OPASessionObj, ctx context.Context, imageScanData []cautils.ImageScanData, jp *JsonPrinter) error {
|
||||
|
||||
if imageScanData != nil {
|
||||
imageScanSummary, err := jp.convertToImageScanSummary(imageScanData)
|
||||
if err != nil {
|
||||
logger.L().Error("failed to convert to image scan summary", helpers.Error(err))
|
||||
return err
|
||||
}
|
||||
opaSessionObj.Report.SummaryDetails.Vulnerabilities.MapsSeverityToSummary = convertToReportSummary(imageScanSummary.MapsSeverityToSummary)
|
||||
opaSessionObj.Report.SummaryDetails.Vulnerabilities.CVESummary = convertToCVESummary(imageScanSummary.CVEs)
|
||||
opaSessionObj.Report.SummaryDetails.Vulnerabilities.PackageScores = convertToPackageScores(imageScanSummary.PackageScores)
|
||||
opaSessionObj.Report.SummaryDetails.Vulnerabilities.Images = imageScanSummary.Images
|
||||
}
|
||||
|
||||
r, err := json.Marshal(FinalizeResults(opaSessionObj))
|
||||
_, err = jp.writer.Write(r)
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
func convertToPackageScores(packageScores map[string]*imageprinter.PackageScore) map[string]*reportsummary.PackageSummary {
|
||||
convertedPackageScores := make(map[string]*reportsummary.PackageSummary)
|
||||
for pkg, score := range packageScores {
|
||||
convertedPackageScores[pkg] = &reportsummary.PackageSummary{
|
||||
Name: score.Name,
|
||||
Version: score.Version,
|
||||
Score: score.Score,
|
||||
MapSeverityToCVEsNumber: score.MapSeverityToCVEsNumber,
|
||||
}
|
||||
}
|
||||
return convertedPackageScores
|
||||
}
|
||||
|
||||
func convertToCVESummary(cves []imageprinter.CVE) []reportsummary.CVESummary {
|
||||
cveSummary := make([]reportsummary.CVESummary, len(cves))
|
||||
i := 0
|
||||
for _, cve := range cves {
|
||||
var a reportsummary.CVESummary
|
||||
a.Severity = cve.Severity
|
||||
a.ID = cve.ID
|
||||
a.Package = cve.Package
|
||||
a.Version = cve.Version
|
||||
a.FixVersions = cve.FixVersions
|
||||
a.FixedState = cve.FixedState
|
||||
cveSummary[i] = a
|
||||
i++
|
||||
}
|
||||
return cveSummary
|
||||
}
|
||||
|
||||
func convertToReportSummary(input map[string]*imageprinter.SeveritySummary) map[string]*reportsummary.SeveritySummary {
|
||||
output := make(map[string]*reportsummary.SeveritySummary)
|
||||
for key, value := range input {
|
||||
output[key] = &reportsummary.SeveritySummary{
|
||||
NumberOfCVEs: value.NumberOfCVEs,
|
||||
NumberOfFixableCVEs: value.NumberOfFixableCVEs,
|
||||
}
|
||||
}
|
||||
return output
|
||||
}
|
||||
|
||||
func (jp *JsonPrinter) PrintImageScan(ctx context.Context, scanResults *models.PresenterConfig) error {
|
||||
if scanResults == nil {
|
||||
return fmt.Errorf("no image vulnerability data provided")
|
||||
|
||||
@@ -5,6 +5,8 @@ import (
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
"github.com/kubescape/kubescape/v3/core/pkg/resultshandling/printer/v2/prettyprinter/tableprinter/imageprinter"
|
||||
"github.com/kubescape/opa-utils/reporthandling/results/v1/reportsummary"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
@@ -83,3 +85,110 @@ func TestScore_Json(t *testing.T) {
|
||||
})
|
||||
}
|
||||
}
|
||||
func TestConvertToCVESummary(t *testing.T) {
|
||||
cves := []imageprinter.CVE{
|
||||
{
|
||||
Severity: "High",
|
||||
ID: "CVE-2021-1234",
|
||||
Package: "example-package",
|
||||
Version: "1.0.0",
|
||||
FixVersions: []string{"1.0.1", "1.0.2"},
|
||||
FixedState: "true",
|
||||
},
|
||||
{
|
||||
Severity: "Medium",
|
||||
ID: "CVE-2021-5678",
|
||||
Package: "another-package",
|
||||
Version: "2.0.0",
|
||||
FixVersions: []string{"2.0.1"},
|
||||
FixedState: "false",
|
||||
},
|
||||
}
|
||||
|
||||
want := []reportsummary.CVESummary{
|
||||
{
|
||||
Severity: "High",
|
||||
ID: "CVE-2021-1234",
|
||||
Package: "example-package",
|
||||
Version: "1.0.0",
|
||||
FixVersions: []string{"1.0.1", "1.0.2"},
|
||||
FixedState: "true",
|
||||
},
|
||||
{
|
||||
Severity: "Medium",
|
||||
ID: "CVE-2021-5678",
|
||||
Package: "another-package",
|
||||
Version: "2.0.0",
|
||||
FixVersions: []string{"2.0.1"},
|
||||
FixedState: "false",
|
||||
},
|
||||
}
|
||||
|
||||
got := convertToCVESummary(cves)
|
||||
|
||||
assert.Equal(t, want, got)
|
||||
}
|
||||
|
||||
func TestConvertToPackageScores(t *testing.T) {
|
||||
packageScores := map[string]*imageprinter.PackageScore{
|
||||
"example-package": {
|
||||
Name: "example-package",
|
||||
Version: "1.0.0",
|
||||
Score: 80.0,
|
||||
MapSeverityToCVEsNumber: map[string]int{"High": 2, "Medium": 1},
|
||||
},
|
||||
"another-package": {
|
||||
Name: "another-package",
|
||||
Version: "2.0.0",
|
||||
Score: 60.0,
|
||||
MapSeverityToCVEsNumber: map[string]int{"High": 1, "Medium": 0},
|
||||
},
|
||||
}
|
||||
|
||||
want := map[string]*reportsummary.PackageSummary{
|
||||
"example-package": {
|
||||
Name: "example-package",
|
||||
Version: "1.0.0",
|
||||
Score: 80.0,
|
||||
MapSeverityToCVEsNumber: map[string]int{"High": 2, "Medium": 1},
|
||||
},
|
||||
"another-package": {
|
||||
Name: "another-package",
|
||||
Version: "2.0.0",
|
||||
Score: 60.0,
|
||||
MapSeverityToCVEsNumber: map[string]int{"High": 1, "Medium": 0},
|
||||
},
|
||||
}
|
||||
|
||||
got := convertToPackageScores(packageScores)
|
||||
|
||||
assert.Equal(t, want, got)
|
||||
}
|
||||
|
||||
func TestConvertToReportSummary(t *testing.T) {
|
||||
input := map[string]*imageprinter.SeveritySummary{
|
||||
"High": &imageprinter.SeveritySummary{
|
||||
NumberOfCVEs: 10,
|
||||
NumberOfFixableCVEs: 5,
|
||||
},
|
||||
"Medium": &imageprinter.SeveritySummary{
|
||||
NumberOfCVEs: 5,
|
||||
NumberOfFixableCVEs: 2,
|
||||
},
|
||||
}
|
||||
|
||||
want := map[string]*reportsummary.SeveritySummary{
|
||||
"High": &reportsummary.SeveritySummary{
|
||||
NumberOfCVEs: 10,
|
||||
NumberOfFixableCVEs: 5,
|
||||
},
|
||||
"Medium": &reportsummary.SeveritySummary{
|
||||
NumberOfCVEs: 5,
|
||||
NumberOfFixableCVEs: 2,
|
||||
},
|
||||
}
|
||||
|
||||
got := convertToReportSummary(input)
|
||||
|
||||
assert.Equal(t, want, got)
|
||||
}
|
||||
|
||||
@@ -14,6 +14,7 @@ import (
|
||||
"github.com/kubescape/go-logger/helpers"
|
||||
"github.com/kubescape/kubescape/v3/core/cautils"
|
||||
"github.com/kubescape/kubescape/v3/core/pkg/resultshandling/printer"
|
||||
"github.com/kubescape/kubescape/v3/core/pkg/resultshandling/printer/v2/prettyprinter/tableprinter/utils"
|
||||
"github.com/kubescape/opa-utils/reporthandling/results/v1/reportsummary"
|
||||
|
||||
"github.com/johnfercher/maroto/pkg/color"
|
||||
@@ -168,7 +169,7 @@ func (pp *PdfPrinter) printHeader(m pdf.Maroto) {
|
||||
// printFramework prints the PDF frameworks after the PDF header
|
||||
func (pp *PdfPrinter) printFramework(m pdf.Maroto, frameworks []reportsummary.IFrameworkSummary) {
|
||||
m.Row(10, func() {
|
||||
m.Text(frameworksScoresToString(frameworks), props.Text{
|
||||
m.Text(utils.FrameworksScoresToString(frameworks), props.Text{
|
||||
Align: consts.Center,
|
||||
Size: 8,
|
||||
Family: consts.Arial,
|
||||
|
||||
@@ -7,6 +7,7 @@ import (
|
||||
"sort"
|
||||
"strings"
|
||||
|
||||
"github.com/anchore/clio"
|
||||
"github.com/anchore/grype/grype/presenter/models"
|
||||
"github.com/enescakir/emoji"
|
||||
"github.com/jwalton/gchalk"
|
||||
@@ -32,15 +33,15 @@ const (
|
||||
var _ printer.IPrinter = &PrettyPrinter{}
|
||||
|
||||
type PrettyPrinter struct {
|
||||
mainPrinter prettyprinter.MainPrinter
|
||||
writer *os.File
|
||||
formatVersion string
|
||||
viewType cautils.ViewTypes
|
||||
scanType cautils.ScanTypes
|
||||
clusterName string
|
||||
inputPatterns []string
|
||||
verboseMode bool
|
||||
printAttackTree bool
|
||||
scanType cautils.ScanTypes
|
||||
inputPatterns []string
|
||||
mainPrinter prettyprinter.MainPrinter
|
||||
clusterName string
|
||||
}
|
||||
|
||||
func NewPrettyPrinter(verboseMode bool, formatVersion string, attackTree bool, viewType cautils.ViewTypes, scanType cautils.ScanTypes, inputPatterns []string, clusterName string) *PrettyPrinter {
|
||||
@@ -90,7 +91,7 @@ func (pp *PrettyPrinter) convertToImageScanSummary(imageScanData []cautils.Image
|
||||
}
|
||||
|
||||
presenterConfig := imageScanData[i].PresenterConfig
|
||||
doc, err := models.NewDocument(presenterConfig.Packages, presenterConfig.Context, presenterConfig.Matches, presenterConfig.IgnoredMatches, presenterConfig.MetadataProvider, nil, presenterConfig.DBStatus)
|
||||
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
|
||||
@@ -165,9 +166,11 @@ func (pp *PrettyPrinter) printOverview(opaSessionObj *cautils.OPASessionObj, pri
|
||||
}
|
||||
|
||||
func (pp *PrettyPrinter) printHeader(opaSessionObj *cautils.OPASessionObj) {
|
||||
if pp.scanType == cautils.ScanTypeCluster || pp.scanType == cautils.ScanTypeRepo {
|
||||
cautils.InfoDisplay(pp.writer, fmt.Sprintf("\nKubescape security posture overview for cluster: %s\n\n", pp.clusterName))
|
||||
if pp.scanType == cautils.ScanTypeCluster {
|
||||
cautils.InfoDisplay(pp.writer, fmt.Sprintf("\nSecurity posture overview for cluster: '%s'\n\n", pp.clusterName))
|
||||
cautils.SimpleDisplay(pp.writer, "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.\n\n")
|
||||
} else if pp.scanType == cautils.ScanTypeRepo {
|
||||
cautils.InfoDisplay(pp.writer, fmt.Sprintf("\nSecurity posture overview for repo: '%s'\n\n", strings.Join(pp.inputPatterns, ", ")))
|
||||
} else if pp.scanType == cautils.ScanTypeWorkload {
|
||||
cautils.InfoDisplay(pp.writer, "Workload security posture overview for:\n")
|
||||
ns := opaSessionObj.SingleResourceScan.GetNamespace()
|
||||
@@ -321,23 +324,6 @@ func generateRelatedObjectsStr(workload WorkloadSummary) string {
|
||||
return relatedStr
|
||||
}
|
||||
|
||||
func frameworksScoresToString(frameworks []reportsummary.IFrameworkSummary) string {
|
||||
if len(frameworks) == 1 {
|
||||
if frameworks[0].GetName() != "" {
|
||||
return fmt.Sprintf("Framework scanned: %s\n", frameworks[0].GetName())
|
||||
}
|
||||
} else if len(frameworks) > 1 {
|
||||
p := "Frameworks scanned: "
|
||||
i := 0
|
||||
for ; i < len(frameworks)-1; i++ {
|
||||
p += fmt.Sprintf("%s (compliance score: %.2f%%), ", frameworks[i].GetName(), frameworks[i].GetComplianceScore())
|
||||
}
|
||||
p += fmt.Sprintf("%s (compliance score: %.2f%%)\n", frameworks[i].GetName(), frameworks[i].GetComplianceScore())
|
||||
return p
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func getSeparator(sep string) string {
|
||||
s := ""
|
||||
for i := 0; i < 80; i++ {
|
||||
|
||||
+9
-3
@@ -32,6 +32,7 @@ func (rp *RepoPrinter) PrintCategoriesTables(writer io.Writer, summaryDetails *r
|
||||
|
||||
categoriesToCategoryControls := mapCategoryToSummary(summaryDetails.ListControls(), mapRepoControlsToCategories)
|
||||
|
||||
tableRended := false
|
||||
for _, id := range repoCategoriesDisplayOrder {
|
||||
categoryControl, ok := categoriesToCategoryControls[id]
|
||||
if !ok {
|
||||
@@ -42,12 +43,16 @@ func (rp *RepoPrinter) PrintCategoriesTables(writer io.Writer, summaryDetails *r
|
||||
continue
|
||||
}
|
||||
|
||||
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 !tableRended {
|
||||
fmt.Fprintln(writer, gchalk.WithGreen().Bold("All controls passed. No issues found"))
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func (rp *RepoPrinter) renderSingleCategoryTable(categoryName string, categoryType CategoryType, writer io.Writer, controlSummaries []reportsummary.IControlSummary, infoToPrintInfo []utils.InfoStars) {
|
||||
func (rp *RepoPrinter) renderSingleCategoryTable(categoryName string, categoryType CategoryType, writer io.Writer, controlSummaries []reportsummary.IControlSummary, infoToPrintInfo []utils.InfoStars) bool {
|
||||
sortControlSummaries(controlSummaries)
|
||||
|
||||
headers, columnAligments := initCategoryTableData(categoryType)
|
||||
@@ -72,10 +77,11 @@ func (rp *RepoPrinter) renderSingleCategoryTable(categoryName string, categoryTy
|
||||
}
|
||||
|
||||
if len(rows) == 0 {
|
||||
return
|
||||
return false
|
||||
}
|
||||
|
||||
renderSingleCategory(writer, categoryName, table, rows, infoToPrintInfo)
|
||||
return true
|
||||
}
|
||||
|
||||
func (rp *RepoPrinter) generateCountingCategoryRow(controlSummary reportsummary.IControlSummary, inputPatterns []string) []string {
|
||||
|
||||
@@ -97,9 +97,9 @@ func FrameworksScoresToString(frameworks []reportsummary.IFrameworkSummary) stri
|
||||
p := "Frameworks scanned: "
|
||||
i := 0
|
||||
for ; i < len(frameworks)-1; i++ {
|
||||
p += fmt.Sprintf("%s (compliance score: %.2f%%), ", frameworks[i].GetName(), frameworks[i].GetComplianceScore())
|
||||
p += fmt.Sprintf("%s (compliance score: %.2f), ", frameworks[i].GetName(), frameworks[i].GetComplianceScore())
|
||||
}
|
||||
p += fmt.Sprintf("%s (compliance score: %.2f%%)\n", frameworks[i].GetName(), frameworks[i].GetComplianceScore())
|
||||
p += fmt.Sprintf("%s (compliance score: %.2f)\n", frameworks[i].GetName(), frameworks[i].GetComplianceScore())
|
||||
return p
|
||||
}
|
||||
return ""
|
||||
|
||||
@@ -160,14 +160,18 @@ func failedPathsToString(control *resourcesresults.ResourceAssociatedControl) []
|
||||
return paths
|
||||
}
|
||||
|
||||
func fixPathsToString(control *resourcesresults.ResourceAssociatedControl) []string {
|
||||
func fixPathsToString(control *resourcesresults.ResourceAssociatedControl, onlyPath bool) []string {
|
||||
var paths []string
|
||||
|
||||
for j := range control.ResourceAssociatedRules {
|
||||
for k := range control.ResourceAssociatedRules[j].Paths {
|
||||
if p := control.ResourceAssociatedRules[j].Paths[k].FixPath.Path; p != "" {
|
||||
v := control.ResourceAssociatedRules[j].Paths[k].FixPath.Value
|
||||
paths = append(paths, fmt.Sprintf("%s=%s", p, v))
|
||||
if onlyPath {
|
||||
paths = append(paths, p)
|
||||
} else {
|
||||
v := control.ResourceAssociatedRules[j].Paths[k].FixPath.Value
|
||||
paths = append(paths, fmt.Sprintf("%s=%s", p, v))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -201,7 +205,7 @@ func reviewPathsToString(control *resourcesresults.ResourceAssociatedControl) []
|
||||
}
|
||||
|
||||
func AssistedRemediationPathsToString(control *resourcesresults.ResourceAssociatedControl) []string {
|
||||
paths := append(fixPathsToString(control), append(deletePathsToString(control), reviewPathsToString(control)...)...)
|
||||
paths := append(fixPathsToString(control, false), append(deletePathsToString(control), reviewPathsToString(control)...)...)
|
||||
// TODO - deprecate failedPaths once all controls support review/delete paths
|
||||
paths = appendFailedPathsIfNotInPaths(paths, failedPathsToString(control))
|
||||
return paths
|
||||
|
||||
@@ -254,16 +254,16 @@ func TestFixPathsToString(t *testing.T) {
|
||||
}
|
||||
|
||||
// Test case 1: Empty ResourceAssociatedRules
|
||||
actualPaths := fixPathsToString(emptyControl)
|
||||
actualPaths := fixPathsToString(emptyControl, false)
|
||||
assert.Nil(t, actualPaths)
|
||||
|
||||
// Test case 2: Single ResourceAssociatedRule and one ReviewPath
|
||||
actualPaths = fixPathsToString(singleRuleControl)
|
||||
actualPaths = fixPathsToString(singleRuleControl, false)
|
||||
expectedPath := []string{"fix-path1=fix-path-value1"}
|
||||
assert.Equal(t, expectedPath, actualPaths)
|
||||
|
||||
// Test case 3: Multiple ResourceAssociatedRules and multiple ReviewPaths
|
||||
actualPaths = fixPathsToString(multipleRulesControl)
|
||||
actualPaths = fixPathsToString(multipleRulesControl, false)
|
||||
expectedPath = []string{"fix-path2=fix-path-value2", "fix-path3=fix-path-value3"}
|
||||
assert.Equal(t, expectedPath, actualPaths)
|
||||
}
|
||||
|
||||
@@ -187,8 +187,10 @@ func (sp *SARIFPrinter) printConfigurationScan(ctx context.Context, opaSessionOb
|
||||
run := sarif.NewRunWithInformationURI(toolName, toolInfoURI)
|
||||
basePath := getBasePathFromMetadata(*opaSessionObj)
|
||||
|
||||
for resourceID, result := range opaSessionObj.ResourcesResult {
|
||||
for resourceID, result := range opaSessionObj.ResourcesResult { //
|
||||
if result.GetStatus(nil).IsFailed() {
|
||||
helmChartFileType := false
|
||||
var mappingnodes []map[string]cautils.MappingNode
|
||||
resourceSource := opaSessionObj.ResourceSource[resourceID]
|
||||
filepath := resourceSource.RelativePath
|
||||
|
||||
@@ -197,9 +199,15 @@ func (sp *SARIFPrinter) printConfigurationScan(ctx context.Context, opaSessionOb
|
||||
continue
|
||||
}
|
||||
|
||||
// If the fileType is helm chart
|
||||
if templateNodes, ok := opaSessionObj.TemplateMapping[resourceID]; ok {
|
||||
mappingnodes = templateNodes.Nodes
|
||||
helmChartFileType = true
|
||||
}
|
||||
|
||||
rsrcAbsPath := path.Join(basePath, filepath)
|
||||
locationResolver, err := locationresolver.NewFixPathLocationResolver(rsrcAbsPath)
|
||||
if err != nil {
|
||||
locationResolver, err := locationresolver.NewFixPathLocationResolver(rsrcAbsPath) //
|
||||
if err != nil && !helmChartFileType {
|
||||
logger.L().Debug("failed to create location resolver", helpers.Error(err))
|
||||
continue
|
||||
}
|
||||
@@ -208,12 +216,24 @@ func (sp *SARIFPrinter) printConfigurationScan(ctx context.Context, opaSessionOb
|
||||
ac := toPin
|
||||
|
||||
if ac.GetStatus(nil).IsFailed() {
|
||||
var location locationresolver.Location
|
||||
|
||||
ctl := opaSessionObj.Report.SummaryDetails.Controls.GetControl(reportsummary.EControlCriteriaID, ac.GetID())
|
||||
location := sp.resolveFixLocation(opaSessionObj, locationResolver, &ac, resourceID)
|
||||
if helmChartFileType {
|
||||
for _, subfileNodes := range mappingnodes {
|
||||
// first get the failed path, then if cannot find it, use the Fix path, cui it to find the closest error.
|
||||
location, split := resolveFixLocation(subfileNodes, &ac)
|
||||
sp.addRule(run, ctl)
|
||||
result := sp.addResult(run, ctl, filepath, location)
|
||||
collectFixesFromMappingNodes(ctx, result, ac, opaSessionObj, resourceID, filepath, rsrcAbsPath, location, subfileNodes, split)
|
||||
}
|
||||
} else {
|
||||
location = sp.resolveFixLocation(opaSessionObj, locationResolver, &ac, resourceID)
|
||||
sp.addRule(run, ctl)
|
||||
result := sp.addResult(run, ctl, filepath, location)
|
||||
collectFixes(ctx, result, ac, opaSessionObj, resourceID, filepath, rsrcAbsPath)
|
||||
}
|
||||
|
||||
sp.addRule(run, ctl)
|
||||
result := sp.addResult(run, ctl, filepath, location)
|
||||
collectFixes(ctx, result, ac, opaSessionObj, resourceID, filepath)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -257,6 +277,56 @@ func (sp *SARIFPrinter) resolveFixLocation(opaSessionObj *cautils.OPASessionObj,
|
||||
return location
|
||||
}
|
||||
|
||||
func getFixPath(ac *resourcesresults.ResourceAssociatedControl, onlyPath bool) string {
|
||||
fixPaths := failedPathsToString(ac)
|
||||
if len(fixPaths) == 0 {
|
||||
fixPaths = fixPathsToString(ac, onlyPath)
|
||||
}
|
||||
var fixPath string
|
||||
if len(fixPaths) > 0 {
|
||||
fixPath = fixPaths[0]
|
||||
}
|
||||
return fixPath
|
||||
}
|
||||
|
||||
func resolveFixLocation(mappingnodes map[string]cautils.MappingNode, ac *resourcesresults.ResourceAssociatedControl) (locationresolver.Location, int) {
|
||||
defaultLocation := locationresolver.Location{Line: 1, Column: 1}
|
||||
fixPath := getFixPath(ac, true)
|
||||
if fixPath == "" {
|
||||
return defaultLocation, -1
|
||||
}
|
||||
location, split := getLocationFromMappingNodes(mappingnodes, fixPath)
|
||||
return location, split
|
||||
}
|
||||
|
||||
func getLocationFromNode(node cautils.MappingNode, path string) locationresolver.Location {
|
||||
line := node.TemplateLineNumber
|
||||
column := (len(strings.Split(path, "."))-1)*2 + 1 //column begins with 1 instead of 0
|
||||
return locationresolver.Location{Line: line, Column: column}
|
||||
}
|
||||
|
||||
func getLocationFromMappingNodes(mappingnodes map[string]cautils.MappingNode, fixPath string) (locationresolver.Location, int) {
|
||||
var location locationresolver.Location
|
||||
// If cannot match any node, return default location
|
||||
location = locationresolver.Location{Line: 1, Column: 1}
|
||||
split := -1
|
||||
if node, ok := mappingnodes[fixPath]; ok {
|
||||
location = getLocationFromNode(node, fixPath)
|
||||
} else {
|
||||
fields := strings.Split(fixPath, ".")
|
||||
for i := len(fields) - 1; i >= 0; i-- {
|
||||
field := fields[:i]
|
||||
closestPath := strings.Join(field, ".")
|
||||
if node, ok := mappingnodes[closestPath]; ok {
|
||||
location = getLocationFromNode(node, closestPath)
|
||||
split = i
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
return location, split
|
||||
}
|
||||
|
||||
func addFix(result *sarif.Result, filepath string, startLine, startColumn, endLine, endColumn int, text string) {
|
||||
// Create a new replacement with the specified start and end lines and columns, and the inserted text.
|
||||
replacement := sarif.NewReplacement(
|
||||
@@ -337,33 +407,37 @@ func collectDiffs(dmp *diffmatchpatch.DiffMatchPatch, diffs []diffmatchpatch.Dif
|
||||
}
|
||||
}
|
||||
|
||||
func collectFixes(ctx context.Context, result *sarif.Result, ac resourcesresults.ResourceAssociatedControl, opaSessionObj *cautils.OPASessionObj, resourceID string, filepath string) {
|
||||
func collectFixes(ctx context.Context, result *sarif.Result, ac resourcesresults.ResourceAssociatedControl, opaSessionObj *cautils.OPASessionObj, resourceID string, filepath string, rsrcAbsPath string) {
|
||||
for _, rule := range ac.ResourceAssociatedRules {
|
||||
if !rule.GetStatus(nil).IsFailed() {
|
||||
continue
|
||||
}
|
||||
|
||||
for _, rulePaths := range rule.Paths {
|
||||
if rulePaths.FixPath.Path == "" {
|
||||
continue
|
||||
}
|
||||
// if strings.HasPrefix(rulePaths.FixPath.Value, fixhandler.UserValuePrefix) {
|
||||
// continue
|
||||
// }
|
||||
|
||||
documentIndex, ok := getDocIndex(opaSessionObj, resourceID)
|
||||
if !ok {
|
||||
fixPath := rulePaths.FixPath.Path
|
||||
if fixPath == "" {
|
||||
continue
|
||||
}
|
||||
|
||||
yamlExpression := fixhandler.FixPathToValidYamlExpression(rulePaths.FixPath.Path, rulePaths.FixPath.Value, documentIndex)
|
||||
fileAsString, err := fixhandler.GetFileString(filepath)
|
||||
fileAsString, err := fixhandler.GetFileString(rsrcAbsPath)
|
||||
if err != nil {
|
||||
logger.L().Debug("failed to access "+filepath, helpers.Error(err))
|
||||
continue
|
||||
}
|
||||
|
||||
fixedYamlString, err := fixhandler.ApplyFixToContent(ctx, fileAsString, yamlExpression)
|
||||
var fixedYamlString string
|
||||
|
||||
// if strings.HasPrefix(rulePaths.FixPath.Value, fixhandler.UserValuePrefix) {
|
||||
// continue
|
||||
// }
|
||||
documentIndex, ok := getDocIndex(opaSessionObj, resourceID)
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
|
||||
yamlExpression := fixhandler.FixPathToValidYamlExpression(fixPath, rulePaths.FixPath.Value, documentIndex)
|
||||
|
||||
fixedYamlString, err = fixhandler.ApplyFixToContent(ctx, fileAsString, yamlExpression)
|
||||
if err != nil {
|
||||
logger.L().Debug("failed to fix "+filepath+" with "+yamlExpression, helpers.Error(err))
|
||||
continue
|
||||
@@ -376,6 +450,98 @@ func collectFixes(ctx context.Context, result *sarif.Result, ac resourcesresults
|
||||
}
|
||||
}
|
||||
|
||||
func collectFixesFromMappingNodes(ctx context.Context, result *sarif.Result, ac resourcesresults.ResourceAssociatedControl, opaSessionObj *cautils.OPASessionObj, resourceID string, filepath string, rsrcAbsPath string, location locationresolver.Location, subFileNodes map[string]cautils.MappingNode, split int) {
|
||||
for _, rule := range ac.ResourceAssociatedRules {
|
||||
if !rule.GetStatus(nil).IsFailed() {
|
||||
continue
|
||||
}
|
||||
|
||||
for _, rulePaths := range rule.Paths {
|
||||
fixPath := rulePaths.FixPath.Path
|
||||
if fixPath == "" {
|
||||
continue
|
||||
}
|
||||
|
||||
fileAsString, err := fixhandler.GetFileString(rsrcAbsPath)
|
||||
if err != nil {
|
||||
logger.L().Debug("failed to access "+filepath, helpers.Error(err))
|
||||
continue
|
||||
}
|
||||
|
||||
var fixedYamlString string
|
||||
fixValue := rulePaths.FixPath.Value
|
||||
if split == -1 { //replaceNode
|
||||
node := subFileNodes[fixPath]
|
||||
fixedYamlString = formReplaceFixedYamlString(node, fileAsString, location, fixValue, fixPath)
|
||||
} else { //insertNode
|
||||
maxLineNumber := getTheLocationOfAddPart(split, fixPath, subFileNodes)
|
||||
fixedYamlString = applyFixToContent(split, fixPath, fileAsString, maxLineNumber, fixValue)
|
||||
}
|
||||
|
||||
dmp := diffmatchpatch.New()
|
||||
diffs := dmp.DiffMain(fileAsString, fixedYamlString, false)
|
||||
collectDiffs(dmp, diffs, result, filepath, fileAsString)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func applyFixToContent(split int, fixPath string, fileAsString string, addLine int, value string) string {
|
||||
addLines := make([]string, 0)
|
||||
fields := strings.Split(fixPath, ".")
|
||||
for i := split; i < len(fields); i++ {
|
||||
field := fields[i]
|
||||
var addedLine string
|
||||
if i != len(fields)-1 {
|
||||
addedLine = strings.Repeat(" ", (i*2)) + field + ":"
|
||||
} else {
|
||||
addedLine = strings.Repeat(" ", (i*2)) + field + ": " + value
|
||||
}
|
||||
addLines = append(addLines, addedLine)
|
||||
}
|
||||
fixedYamlString := formAddFixedYamlString(fileAsString, addLine, addLines)
|
||||
|
||||
return fixedYamlString
|
||||
}
|
||||
|
||||
func formReplaceFixedYamlString(node cautils.MappingNode, fileAsString string, location locationresolver.Location, fixValue string, fixPath string) string {
|
||||
replcaedValue := node.Value
|
||||
yamlLines := strings.Split(fileAsString, "\n")
|
||||
if replcaedValue == "" {
|
||||
yamlLines[location.Line] = yamlLines[location.Line] + " # This is the suggested modification, the value for " + fixPath + " is " + fixValue + "\n"
|
||||
} else {
|
||||
replacedLine := "# This is the suggested modification\n" + yamlLines[location.Line]
|
||||
newLine := strings.Replace(replacedLine, replcaedValue, fixValue, -1)
|
||||
yamlLines[location.Line] = newLine
|
||||
}
|
||||
fixedYamlString := strings.Join(yamlLines, "\n")
|
||||
return fixedYamlString
|
||||
}
|
||||
|
||||
func formAddFixedYamlString(fileAsString string, addLine int, addLines []string) string {
|
||||
yamlLines := strings.Split(fileAsString, "\n")
|
||||
newYamlLines := append(yamlLines[:addLine], "# This is the suggested modification")
|
||||
newYamlLines = append(newYamlLines, addLines...)
|
||||
yamlLines = strings.Split(fileAsString, "\n")
|
||||
newYamlLines = append(newYamlLines, yamlLines[addLine:]...)
|
||||
fixedYamlString := strings.Join(newYamlLines, "\n")
|
||||
return fixedYamlString
|
||||
}
|
||||
|
||||
func getTheLocationOfAddPart(split int, fixPath string, mappingnodes map[string]cautils.MappingNode) int {
|
||||
fields := strings.Split(fixPath, ".")
|
||||
field := fields[:split]
|
||||
closestPath := strings.Join(field, ".")
|
||||
maxLineNumber := -1
|
||||
for k, v := range mappingnodes {
|
||||
if strings.Index(k, closestPath) == 0 {
|
||||
if v.TemplateLineNumber > maxLineNumber {
|
||||
maxLineNumber = v.TemplateLineNumber
|
||||
}
|
||||
}
|
||||
}
|
||||
return maxLineNumber
|
||||
}
|
||||
|
||||
func getDocIndex(opaSessionObj *cautils.OPASessionObj, resourceID string) (int, bool) {
|
||||
resource := opaSessionObj.AllResources[resourceID]
|
||||
localworkload, ok := resource.(*localworkload.LocalWorkload)
|
||||
|
||||
@@ -128,7 +128,7 @@ func NewPrinter(ctx context.Context, printFormat string, scanInfo *cautils.ScanI
|
||||
if printFormat != printer.PrettyFormat {
|
||||
logger.L().Ctx(ctx).Warning(fmt.Sprintf("Invalid format \"%s\", default format \"pretty-printer\" is applied", printFormat))
|
||||
}
|
||||
return printerv2.NewPrettyPrinter(scanInfo.VerboseMode, scanInfo.FormatVersion, scanInfo.PrintAttackTree, cautils.ViewTypes(scanInfo.View), scanInfo.ScanType, nil, clusterName)
|
||||
return printerv2.NewPrettyPrinter(scanInfo.VerboseMode, scanInfo.FormatVersion, scanInfo.PrintAttackTree, cautils.ViewTypes(scanInfo.View), scanInfo.ScanType, scanInfo.InputPatterns, clusterName)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user