Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: group image vulnerabilities by base/os image #5680

Merged
merged 27 commits into from
Aug 23, 2024
Merged
Show file tree
Hide file tree
Changes from 24 commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
942f0a4
feat: add support for app and env sorting in scan list api and add me…
gireesh-naidu Aug 2, 2024
0a991b6
fix: query fix for appName sort or envName sort
gireesh-naidu Aug 2, 2024
e5f0bb4
Merge branch 'main' into cve-severity-image-scan-oss
gireesh-naidu Aug 5, 2024
c6d622b
Merge branch 'main' into cve-severity-image-scan-oss
gireesh-naidu Aug 5, 2024
b928140
fix: sql script number change
gireesh-naidu Aug 5, 2024
0279d8a
fix: minor changes
gireesh-naidu Aug 6, 2024
8f03c8f
fix: review fix
gireesh-naidu Aug 6, 2024
05246c3
fix: remove dml on cve_store and handle it in code handling this vers…
gireesh-naidu Aug 8, 2024
99dd81b
Merge branch 'main' into cve-severity-image-scan-oss
gireesh-naidu Aug 8, 2024
7d7bde7
fix: review comments
gireesh-naidu Aug 8, 2024
f0d5ae5
Merge branch 'main' into cve-severity-image-scan-oss
gireesh-naidu Aug 8, 2024
b8df085
Merge branch 'main' into cve-severity-image-scan-oss
gireesh-naidu Aug 12, 2024
bf6c26e
Merge branch 'main' into cve-severity-image-scan-oss
gireesh-naidu Aug 12, 2024
19e0262
feat: storing target,class and type values in imageScanExecutionResults
gireesh-naidu Aug 12, 2024
5e21bc7
feat: add sql script
gireesh-naidu Aug 12, 2024
5aa58d6
feat: add sql script
gireesh-naidu Aug 12, 2024
f87af7e
fix: add new columns
gireesh-naidu Aug 12, 2024
32c518b
fix: update script numbers
gireesh-naidu Aug 12, 2024
8a59aa3
Merge branch 'main' into cve-severity-image-scan-oss
gireesh-naidu Aug 13, 2024
477666b
Merge branch 'cve-severity-image-scan-oss' into vulnerability-grouping
gireesh-naidu Aug 13, 2024
3027397
Merge branch 'develop-oss' into cve-severity-image-scan-oss
gireesh-naidu Aug 13, 2024
546fb9e
Merge branch 'cve-severity-image-scan-oss' into vulnerability-grouping
gireesh-naidu Aug 13, 2024
2e8af31
fix: correct down script
gireesh-naidu Aug 13, 2024
099e890
fix: minor fix
gireesh-naidu Aug 14, 2024
46c9127
chore: script number update
gireesh-naidu Aug 23, 2024
5361a8d
Merge branch 'develop-oss' into vulnerability-grouping
gireesh-naidu Aug 23, 2024
319a141
Merge branch 'develop-oss' into vulnerability-grouping
gireesh-naidu Aug 23, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 12 additions & 11 deletions api/restHandler/ImageScanRestHandler.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ package restHandler
import (
"encoding/json"
"fmt"
securityBean "github.com/devtron-labs/devtron/pkg/security/bean"
"net/http"
"strconv"

Expand Down Expand Up @@ -70,7 +71,7 @@ func (impl ImageScanRestHandlerImpl) ScanExecutionList(w http.ResponseWriter, r
}

decoder := json.NewDecoder(r.Body)
var request *security.ImageScanRequest
var request *securityBean.ImageScanRequest
err = decoder.Decode(&request)
if err != nil {
impl.logger.Errorw("request err, ScanExecutionList", "err", err, "payload", request)
Expand All @@ -82,8 +83,8 @@ func (impl ImageScanRestHandlerImpl) ScanExecutionList(w http.ResponseWriter, r
if err != nil {
impl.logger.Errorw("service err, ScanExecutionList", "err", err, "payload", request)
if util.IsErrNoRows(err) {
responseList := make([]*security.ImageScanHistoryResponse, 0)
common.WriteJsonResp(w, nil, &security.ImageScanHistoryListingResponse{ImageScanHistoryResponse: responseList}, http.StatusOK)
responseList := make([]*securityBean.ImageScanHistoryResponse, 0)
common.WriteJsonResp(w, nil, &securityBean.ImageScanHistoryListingResponse{ImageScanHistoryResponse: responseList}, http.StatusOK)
} else {
common.WriteJsonResp(w, err, nil, http.StatusInternalServerError)
}
Expand Down Expand Up @@ -126,8 +127,8 @@ func (impl ImageScanRestHandlerImpl) ScanExecutionList(w http.ResponseWriter, r
if err != nil {
impl.logger.Errorw("service err, ScanExecutionList", "err", err, "payload", request)
if util.IsErrNoRows(err) {
responseList := make([]*security.ImageScanHistoryResponse, 0)
common.WriteJsonResp(w, nil, &security.ImageScanHistoryListingResponse{ImageScanHistoryResponse: responseList}, http.StatusOK)
responseList := make([]*securityBean.ImageScanHistoryResponse, 0)
common.WriteJsonResp(w, nil, &securityBean.ImageScanHistoryListingResponse{ImageScanHistoryResponse: responseList}, http.StatusOK)
} else {
common.WriteJsonResp(w, err, nil, http.StatusInternalServerError)
}
Expand Down Expand Up @@ -177,7 +178,7 @@ func (impl ImageScanRestHandlerImpl) FetchExecutionDetail(w http.ResponseWriter,
}
}
image := v.Get("image")
request := &security.ImageScanRequest{
request := &securityBean.ImageScanRequest{
ImageScanDeployInfoId: imageScanDeployInfoId,
Image: image,
ArtifactId: artifactId,
Expand All @@ -189,7 +190,7 @@ func (impl ImageScanRestHandlerImpl) FetchExecutionDetail(w http.ResponseWriter,
if err != nil {
impl.logger.Errorw("service err, FetchExecutionDetail", "err", err, "payload", request)
if util.IsErrNoRows(err) {
common.WriteJsonResp(w, nil, &security.ImageScanExecutionDetail{}, http.StatusOK)
common.WriteJsonResp(w, nil, &securityBean.ImageScanExecutionDetail{}, http.StatusOK)
} else {
common.WriteJsonResp(w, err, nil, http.StatusInternalServerError)
}
Expand Down Expand Up @@ -221,7 +222,7 @@ func (impl ImageScanRestHandlerImpl) FetchExecutionDetail(w http.ResponseWriter,
}
//RBAC
} else {
common.WriteJsonResp(w, err, &security.ImageScanExecutionDetail{}, http.StatusOK)
common.WriteJsonResp(w, err, &securityBean.ImageScanExecutionDetail{}, http.StatusOK)
}

common.WriteJsonResp(w, err, executionDetail, http.StatusOK)
Expand All @@ -230,7 +231,7 @@ func (impl ImageScanRestHandlerImpl) FetchExecutionDetail(w http.ResponseWriter,
func (impl ImageScanRestHandlerImpl) FetchMinScanResultByAppIdAndEnvId(w http.ResponseWriter, r *http.Request) {
v := r.URL.Query()
var appId, envId int
request := &security.ImageScanRequest{}
request := &securityBean.ImageScanRequest{}
appIds := v.Get("appId")
if len(appIds) > 0 {
appId, err := strconv.Atoi(appIds)
Expand Down Expand Up @@ -299,8 +300,8 @@ func (impl ImageScanRestHandlerImpl) VulnerabilityExposure(w http.ResponseWriter
if err != nil {
impl.logger.Errorw("service err, VulnerabilityExposure", "err", err, "payload", request)
if util.IsErrNoRows(err) {
responseList := make([]*security.ImageScanHistoryResponse, 0)
common.WriteJsonResp(w, nil, &security.ImageScanHistoryListingResponse{ImageScanHistoryResponse: responseList}, http.StatusOK)
responseList := make([]*securityBean.ImageScanHistoryResponse, 0)
common.WriteJsonResp(w, nil, &securityBean.ImageScanHistoryListingResponse{ImageScanHistoryResponse: responseList}, http.StatusOK)
} else {
common.WriteJsonResp(w, err, nil, http.StatusInternalServerError)
}
Expand Down
20 changes: 10 additions & 10 deletions api/restHandler/PolicyRestHandler.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,12 +20,12 @@ import (
"encoding/json"
"errors"
"fmt"
securityBean "github.com/devtron-labs/devtron/internal/sql/repository/security/bean"
"net/http"
"strconv"

"github.com/devtron-labs/devtron/api/bean"
"github.com/devtron-labs/devtron/api/restHandler/common"
security2 "github.com/devtron-labs/devtron/internal/sql/repository/security"
"github.com/devtron-labs/devtron/pkg/auth/authorisation/casbin"
user2 "github.com/devtron-labs/devtron/pkg/auth/user"
"github.com/devtron-labs/devtron/pkg/cluster"
Expand Down Expand Up @@ -221,18 +221,18 @@ func (impl PolicyRestHandlerImpl) GetPolicy(w http.ResponseWriter, r *http.Reque
req.Id = ids
}
var clusterId, environmentId, appId int
var policyLevel security2.PolicyLevel
if level == security2.Global.String() {
policyLevel = security2.Global
} else if level == security2.Cluster.String() {
var policyLevel securityBean.PolicyLevel
if level == securityBean.Global.String() {
policyLevel = securityBean.Global
} else if level == securityBean.Cluster.String() {
clusterId = req.Id
policyLevel = security2.Cluster
} else if level == security2.Environment.String() {
policyLevel = securityBean.Cluster
} else if level == securityBean.Environment.String() {
environmentId = req.Id
policyLevel = security2.Environment
} else if level == security2.Application.String() {
policyLevel = securityBean.Environment
} else if level == securityBean.Application.String() {
appId = req.Id
policyLevel = security2.Application
policyLevel = securityBean.Application
}

token := r.Header.Get("token")
Expand Down
134 changes: 36 additions & 98 deletions internal/sql/repository/security/CvePolicyControle.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,99 +18,37 @@ package security

import (
"fmt"
securityBean "github.com/devtron-labs/devtron/internal/sql/repository/security/bean"
"github.com/devtron-labs/devtron/pkg/sql"
"github.com/go-pg/pg"
"github.com/go-pg/pg/orm"
"time"
)

type CvePolicy struct {
tableName struct{} `sql:"cve_policy_control" pg:",discard_unknown_columns"`
Id int `sql:"id,pk"`
Global bool `sql:"global,notnull"`
ClusterId int `sql:"cluster_id"`
EnvironmentId int `sql:"env_id"`
AppId int `sql:"app_id"`
CVEStoreId string `sql:"cve_store_id"`
Action PolicyAction `sql:"action, notnull"`
Severity *Severity `sql:"severity, notnull "`
Deleted bool `sql:"deleted, notnull"`
tableName struct{} `sql:"cve_policy_control" pg:",discard_unknown_columns"`
Id int `sql:"id,pk"`
Global bool `sql:"global,notnull"`
ClusterId int `sql:"cluster_id"`
EnvironmentId int `sql:"env_id"`
AppId int `sql:"app_id"`
CVEStoreId string `sql:"cve_store_id"`
Action securityBean.PolicyAction `sql:"action, notnull"`
Severity *securityBean.Severity `sql:"severity, notnull "`
Deleted bool `sql:"deleted, notnull"`
sql.AuditLog
CveStore *CveStore
}

type PolicyAction int

const (
Inherit PolicyAction = iota
Allow
Block
Blockiffixed
)

func (d PolicyAction) String() string {
return [...]string{"inherit", "allow", "block", "blockiffixed"}[d]
}

// ------------------
type Severity int

const (
Low Severity = iota
Medium
Critical
High
Safe
)
const (
HIGH string = "high"
CRITICAL string = "critical"
SAFE string = "safe"
LOW string = "low"
MEDIUM string = "medium"
MODERATE string = "moderate"
)

// Handling for future use
func (d Severity) ValuesOf(severity string) Severity {
if severity == CRITICAL || severity == HIGH {
return Critical
} else if severity == MODERATE || severity == MEDIUM {
return Medium
} else if severity == LOW || severity == SAFE {
return Low
}
return Low
}

// Updating it for future use(not in use for standard severity)
func (d Severity) String() string {
return [...]string{"low", "moderate", "critical", "high", "safe"}[d]
}

// ----------------
type PolicyLevel int

const (
Global PolicyLevel = iota
Cluster
Environment
Application
)

func (d PolicyLevel) String() string {
return [...]string{"global", "cluster", "environment", "application"}[d]
}

func (policy *CvePolicy) PolicyLevel() PolicyLevel {
func (policy *CvePolicy) PolicyLevel() securityBean.PolicyLevel {
if policy.ClusterId != 0 {
return Cluster
return securityBean.Cluster
} else if policy.AppId != 0 {
return Application
return securityBean.Application
} else if policy.EnvironmentId != 0 {
return Environment
return securityBean.Environment
} else {
return Global
return securityBean.Global
}
}

Expand Down Expand Up @@ -250,37 +188,37 @@ func (impl *CvePolicyRepositoryImpl) GetBlockedCVEList(cves []*CveStore, cluster
return blockedCve, nil
}

func EnforceCvePolicy(cves []*CveStore, cvePolicy map[string]*CvePolicy, severityPolicy map[Severity]*CvePolicy) (blockedCVE []*CveStore) {
func EnforceCvePolicy(cves []*CveStore, cvePolicy map[string]*CvePolicy, severityPolicy map[securityBean.Severity]*CvePolicy) (blockedCVE []*CveStore) {

for _, cve := range cves {
if policy, ok := cvePolicy[cve.Name]; ok {
if policy.Action == Allow {
if policy.Action == securityBean.Allow {
continue
} else if (policy.Action == Block) || (policy.Action == Blockiffixed && cve.FixedVersion != "") {
} else if (policy.Action == securityBean.Block) || (policy.Action == securityBean.Blockiffixed && cve.FixedVersion != "") {
blockedCVE = append(blockedCVE, cve)
}
} else {
if severityPolicy[cve.Severity] != nil && severityPolicy[cve.Severity].Action == Allow {
if severityPolicy[cve.GetSeverity()] != nil && severityPolicy[cve.GetSeverity()].Action == securityBean.Allow {
continue
} else if severityPolicy[cve.Severity] != nil && (severityPolicy[cve.Severity].Action == Block || (severityPolicy[cve.Severity].Action == Blockiffixed && cve.FixedVersion != "")) {
} else if severityPolicy[cve.GetSeverity()] != nil && (severityPolicy[cve.GetSeverity()].Action == securityBean.Block || (severityPolicy[cve.GetSeverity()].Action == securityBean.Blockiffixed && cve.FixedVersion != "")) {
blockedCVE = append(blockedCVE, cve)
}
}
}
return blockedCVE
}

func (impl *CvePolicyRepositoryImpl) getApplicablePolicy(clusterId, envId, appId int, isAppstore bool) (map[string]*CvePolicy, map[Severity]*CvePolicy, error) {
func (impl *CvePolicyRepositoryImpl) getApplicablePolicy(clusterId, envId, appId int, isAppstore bool) (map[string]*CvePolicy, map[securityBean.Severity]*CvePolicy, error) {

var policyLevel PolicyLevel
var policyLevel securityBean.PolicyLevel
if isAppstore && appId > 0 && envId > 0 && clusterId > 0 {
policyLevel = Environment
policyLevel = securityBean.Environment
} else if appId > 0 && envId > 0 && clusterId > 0 {
policyLevel = Application
policyLevel = securityBean.Application
} else if envId > 0 && clusterId > 0 {
policyLevel = Environment
policyLevel = securityBean.Environment
} else if clusterId > 0 {
policyLevel = Cluster
policyLevel = securityBean.Cluster
} else {
//error in case of global or other policy
return nil, nil, fmt.Errorf("policy not identified")
Expand All @@ -290,16 +228,16 @@ func (impl *CvePolicyRepositoryImpl) getApplicablePolicy(clusterId, envId, appId
return cvePolicy, severityPolicy, err
}

func (impl *CvePolicyRepositoryImpl) getPolicies(policyLevel PolicyLevel, clusterId, environmentId, appId int) (map[string]*CvePolicy, map[Severity]*CvePolicy, error) {
func (impl *CvePolicyRepositoryImpl) getPolicies(policyLevel securityBean.PolicyLevel, clusterId, environmentId, appId int) (map[string]*CvePolicy, map[securityBean.Severity]*CvePolicy, error) {
var policies []*CvePolicy
var err error
if policyLevel == Global {
if policyLevel == securityBean.Global {
policies, err = impl.GetGlobalPolicies()
} else if policyLevel == Cluster {
} else if policyLevel == securityBean.Cluster {
policies, err = impl.GetClusterPolicies(clusterId)
} else if policyLevel == Environment {
} else if policyLevel == securityBean.Environment {
policies, err = impl.GetEnvPolicies(clusterId, environmentId)
} else if policyLevel == Application {
} else if policyLevel == securityBean.Application {
policies, err = impl.GetAppEnvPolicies(clusterId, environmentId, appId)
} else {
return nil, nil, fmt.Errorf("unsupported policy level: %s", policyLevel)
Expand All @@ -314,9 +252,9 @@ func (impl *CvePolicyRepositoryImpl) getPolicies(policyLevel PolicyLevel, cluste
return cvePolicy, severityPolicy, nil
}

func (impl *CvePolicyRepositoryImpl) getApplicablePolicies(policies []*CvePolicy) (map[string]*CvePolicy, map[Severity]*CvePolicy) {
func (impl *CvePolicyRepositoryImpl) getApplicablePolicies(policies []*CvePolicy) (map[string]*CvePolicy, map[securityBean.Severity]*CvePolicy) {
cvePolicy := make(map[string][]*CvePolicy)
severityPolicy := make(map[Severity][]*CvePolicy)
severityPolicy := make(map[securityBean.Severity][]*CvePolicy)
for _, policy := range policies {
if policy.CVEStoreId != "" {
cvePolicy[policy.CveStore.Name] = append(cvePolicy[policy.CveStore.Name], policy)
Expand Down Expand Up @@ -347,8 +285,8 @@ func (impl *CvePolicyRepositoryImpl) getHighestPolicy(allPolicies map[string][]*
return applicablePolicies
}

func (impl *CvePolicyRepositoryImpl) getHighestPolicyS(allPolicies map[Severity][]*CvePolicy) map[Severity]*CvePolicy {
applicablePolicies := make(map[Severity]*CvePolicy)
func (impl *CvePolicyRepositoryImpl) getHighestPolicyS(allPolicies map[securityBean.Severity][]*CvePolicy) map[securityBean.Severity]*CvePolicy {
applicablePolicies := make(map[securityBean.Severity]*CvePolicy)
for key, policies := range allPolicies {
var applicablePolicy *CvePolicy
for _, policy := range policies {
Expand Down
39 changes: 33 additions & 6 deletions internal/sql/repository/security/CveStoreRepository.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ package security
import (
"fmt"
"github.com/devtron-labs/devtron/internal/sql/repository/helper"
securityBean "github.com/devtron-labs/devtron/internal/sql/repository/security/bean"
"github.com/devtron-labs/devtron/pkg/sql"
"github.com/go-pg/pg"
"go.uber.org/zap"
Expand All @@ -27,15 +28,41 @@ import (
)

type CveStore struct {
tableName struct{} `sql:"cve_store" pg:",discard_unknown_columns"`
Name string `sql:"name,pk"`
Severity Severity `sql:"severity,notnull"`
Package string `sql:"package,notnull"` // deprecated
Version string `sql:"version,notnull"`
FixedVersion string `sql:"fixed_version,notnull"`
tableName struct{} `sql:"cve_store" pg:",discard_unknown_columns"`
Name string `sql:"name,pk"`

// Deprecated: Severity, use StandardSeverity for all read purposes
Severity securityBean.Severity `sql:"severity,notnull"`
// Deprecated: Package
Package string `sql:"package,notnull"` // deprecated, storing package data in image_scan_execution_result table
// Deprecated: Version
Version string `sql:"version,notnull"`
// Deprecated: FixedVersion
FixedVersion string `sql:"fixed_version,notnull"`

// StandardSeverity is the actual severity. use GetSeverity method to get severity of the vulnerability
// earlier severity is maintained in Severity column by merging HIGH and CRITICAL severities.
// later we introduced new column StandardSeverity to store raw severity, but didn't migrate the existing Severity data to StandardSeverity.
// currently, we deprecated Severity.
StandardSeverity *securityBean.Severity `sql:"standard_severity"`
sql.AuditLog
}

// GetSeverity returns the actual severity of the vulnerability.
func (cve *CveStore) GetSeverity() securityBean.Severity {
if cve.StandardSeverity == nil {
// we need this as there was a time when StandardSeverity didn't exist.
// and migration of Severity data to StandardSeverity is not done.
return cve.Severity
}
return *cve.StandardSeverity
}

func (cve *CveStore) SetStandardSeverity(severity securityBean.Severity) {
cve.Severity = severity
cve.StandardSeverity = &severity
}

type VulnerabilityRequest struct {
AppName string `json:"appName"`
CveName string `json:"cveName"`
Expand Down
Loading