Skip to content

Commit f571dc9

Browse files
feat: Trivy Image Scanning (#3373)
* wip * Sql script fix * Sql Fix * Trivy and generic image scanning sql * script number change * Module Integration Changes * Module Integration Changes * wire changes * script renaming * Handling default value false * Script Number change * Inclusion of module-type and enabled state for all modules * Adding enabled * Handling Version of Clair * Distinct condition * Distinct condition change * Testing changes * Testing changes * Update devtron.yaml * Update installation-script * Final changes * Final changes * --Final changes-- * --Finalchanges-- * Helm Command Handling * Helm Command Handling * minor changes * Pushing scantoolid in execution result * Image scan Scan Tool Id handling * No vulnerability image scan tool id handling * Script number change * handling latest image scan deploy info * script no change * Handling For CICD enabling state * review changes * final changes * fixes minor * fixes minor * fixes minor * review changes * changes for empty module type handling * review changes and main sync script --------- Co-authored-by: kartik-579 <[email protected]>
1 parent e2a59cc commit f571dc9

21 files changed

+851
-72
lines changed

Wire.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -585,6 +585,8 @@ func InitializeApp() (*App, error) {
585585
wire.Bind(new(security2.CveStoreRepository), new(*security2.CveStoreRepositoryImpl)),
586586
security2.NewImageScanDeployInfoRepositoryImpl,
587587
wire.Bind(new(security2.ImageScanDeployInfoRepository), new(*security2.ImageScanDeployInfoRepositoryImpl)),
588+
security2.NewScanToolMetadataRepositoryImpl,
589+
wire.Bind(new(security2.ScanToolMetadataRepository), new(*security2.ScanToolMetadataRepositoryImpl)),
588590
router.NewPolicyRouterImpl,
589591
wire.Bind(new(router.PolicyRouter), new(*router.PolicyRouterImpl)),
590592
restHandler.NewPolicyRestHandlerImpl,
@@ -593,6 +595,8 @@ func InitializeApp() (*App, error) {
593595
wire.Bind(new(security.PolicyService), new(*security.PolicyServiceImpl)),
594596
security2.NewPolicyRepositoryImpl,
595597
wire.Bind(new(security2.CvePolicyRepository), new(*security2.CvePolicyRepositoryImpl)),
598+
security2.NewScanToolExecutionHistoryMappingRepositoryImpl,
599+
wire.Bind(new(security2.ScanToolExecutionHistoryMappingRepository), new(*security2.ScanToolExecutionHistoryMappingRepositoryImpl)),
596600

597601
argocdServer.NewArgoK8sClientImpl,
598602
wire.Bind(new(argocdServer.ArgoK8sClient), new(*argocdServer.ArgoK8sClientImpl)),

api/module/ModuleRestHandler.go

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ type ModuleRestHandler interface {
3434
GetModuleInfo(w http.ResponseWriter, r *http.Request)
3535
GetModuleConfig(w http.ResponseWriter, r *http.Request)
3636
HandleModuleAction(w http.ResponseWriter, r *http.Request)
37+
EnableModule(w http.ResponseWriter, r *http.Request)
3738
}
3839

3940
type ModuleRestHandlerImpl struct {
@@ -164,3 +165,46 @@ func (impl ModuleRestHandlerImpl) HandleModuleAction(w http.ResponseWriter, r *h
164165
}
165166
common.WriteJsonResp(w, err, res, http.StatusOK)
166167
}
168+
169+
func (impl ModuleRestHandlerImpl) EnableModule(w http.ResponseWriter, r *http.Request) {
170+
// check if user is logged in or not
171+
userId, err := impl.userService.GetLoggedInUser(r)
172+
if userId == 0 || err != nil {
173+
common.WriteJsonResp(w, err, "Unauthorized User", http.StatusUnauthorized)
174+
return
175+
}
176+
177+
// check query param
178+
params := mux.Vars(r)
179+
moduleName := params["name"]
180+
if len(moduleName) == 0 {
181+
impl.logger.Error("module name is not supplied")
182+
common.WriteJsonResp(w, errors.New("module name is not supplied"), nil, http.StatusBadRequest)
183+
return
184+
}
185+
// decode request
186+
decoder := json.NewDecoder(r.Body)
187+
var moduleEnableRequestDto module.ModuleEnableRequestDto
188+
err = decoder.Decode(&moduleEnableRequestDto)
189+
if err != nil {
190+
impl.logger.Errorw("error in decoding request in ModuleEnableRequestDto", "err", err)
191+
common.WriteJsonResp(w, err, nil, http.StatusBadRequest)
192+
return
193+
}
194+
195+
// handle super-admin RBAC
196+
token := r.Header.Get("token")
197+
if ok := impl.enforcer.Enforce(token, casbin.ResourceGlobal, casbin.ActionUpdate, "*"); !ok {
198+
common.WriteJsonResp(w, errors.New("unauthorized"), nil, http.StatusForbidden)
199+
return
200+
}
201+
202+
// service call
203+
res, err := impl.moduleService.EnableModule(moduleName, moduleEnableRequestDto.Version)
204+
if err != nil {
205+
impl.logger.Errorw("service err, Enabling Module", "err", err, "moduleName", moduleName, "toolVersion", moduleEnableRequestDto.Version)
206+
common.WriteJsonResp(w, err, nil, http.StatusInternalServerError)
207+
return
208+
}
209+
common.WriteJsonResp(w, err, res, http.StatusOK)
210+
}

api/module/ModuleRouter.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,4 +21,5 @@ func (impl ModuleRouterImpl) Init(configRouter *mux.Router) {
2121
configRouter.Path("").HandlerFunc(impl.moduleRestHandler.GetModuleInfo).Methods("GET")
2222
configRouter.Path("/config").HandlerFunc(impl.moduleRestHandler.GetModuleConfig).Queries("name", "{name}").Methods("GET")
2323
configRouter.Path("").HandlerFunc(impl.moduleRestHandler.HandleModuleAction).Queries("name", "{name}").Methods("POST")
24+
configRouter.Path("/enable").HandlerFunc(impl.moduleRestHandler.EnableModule).Queries("name", "{name}").Methods("POST")
2425
}

cmd/external-app/wire.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ import (
3131
app2 "github.com/devtron-labs/devtron/internal/sql/repository/app"
3232
"github.com/devtron-labs/devtron/internal/sql/repository/appStatus"
3333
"github.com/devtron-labs/devtron/internal/sql/repository/pipelineConfig"
34+
security2 "github.com/devtron-labs/devtron/internal/sql/repository/security"
3435
"github.com/devtron-labs/devtron/internal/util"
3536
"github.com/devtron-labs/devtron/pkg/app"
3637
appStoreBean "github.com/devtron-labs/devtron/pkg/appStore/bean"
@@ -174,6 +175,9 @@ func InitializeApp() (*App, error) {
174175
wire.Value(appStoreBean.RefChartProxyDir("scripts/devtron-reference-helm-charts")),
175176
util.NewGitFactory,
176177
util.NewGitCliUtil,
178+
179+
security2.NewScanToolMetadataRepositoryImpl,
180+
wire.Bind(new(security2.ScanToolMetadataRepository), new(*security2.ScanToolMetadataRepositoryImpl)),
177181
)
178182
return &App{}, nil
179183
}

cmd/external-app/wire_gen.go

Lines changed: 3 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

internal/sql/repository/security/ImageScanDeployInfoRepository.go

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,8 @@ import (
2828
"go.uber.org/zap"
2929
)
3030

31-
/**
31+
/*
32+
*
3233
this table contains scanned images registry for deployed object and apps,
3334
images which are deployed on cluster by anyway and has scanned result
3435
*/
@@ -126,8 +127,8 @@ func (impl ImageScanDeployInfoRepositoryImpl) FindByIds(ids []int) ([]*ImageScan
126127
return models, err
127128
}
128129

129-
func (impl ImageScanDeployInfoRepositoryImpl) Update(team *ImageScanDeployInfo) error {
130-
err := impl.dbConnection.Update(team)
130+
func (impl ImageScanDeployInfoRepositoryImpl) Update(model *ImageScanDeployInfo) error {
131+
err := impl.dbConnection.Update(model)
131132
return err
132133
}
133134

internal/sql/repository/security/ImageScanResultRepository.go

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ type ImageScanExecutionResult struct {
2727
Id int `sql:"id,pk"`
2828
CveStoreName string `sql:"cve_store_name,notnull"`
2929
ImageScanExecutionHistoryId int `sql:"image_scan_execution_history_id"`
30+
ScanToolId int `sql:"scan_tool_id"`
3031
CveStore CveStore
3132
ImageScanExecutionHistory ImageScanExecutionHistory
3233
}
@@ -93,30 +94,30 @@ func (impl ImageScanResultRepositoryImpl) FetchByScanExecutionId(scanExecutionId
9394
Where("image_scan_execution_result.scan_execution_id = ?", id).Select()
9495
*/
9596

96-
err := impl.dbConnection.Model(&models).Column("image_scan_execution_result.*", "CveStore").
97+
err := impl.dbConnection.Model(&models).ColumnExpr("DISTINCT cve_store_name").Column("image_scan_execution_result.*", "CveStore").
9798
Where("image_scan_execution_result.image_scan_execution_history_id = ?", scanExecutionId).
9899
Select()
99100
return models, err
100101
}
101102

102103
func (impl ImageScanResultRepositoryImpl) FetchByScanExecutionIds(ids []int) ([]*ImageScanExecutionResult, error) {
103104
var models []*ImageScanExecutionResult
104-
err := impl.dbConnection.Model(&models).Column("image_scan_execution_result.*", "ImageScanExecutionHistory", "CveStore").
105+
err := impl.dbConnection.Model(&models).ColumnExpr("DISTINCT cve_store_name").Column("image_scan_execution_result.*", "ImageScanExecutionHistory", "CveStore").
105106
Where("image_scan_execution_result.image_scan_execution_history_id in(?)", pg.In(ids)).
106107
Select()
107108
return models, err
108109
}
109110

110111
func (impl ImageScanResultRepositoryImpl) FindByImageDigest(imageDigest string) ([]*ImageScanExecutionResult, error) {
111112
var model []*ImageScanExecutionResult
112-
err := impl.dbConnection.Model(&model).Column("image_scan_execution_result.*", "ImageScanExecutionHistory", "CveStore").
113+
err := impl.dbConnection.Model(&model).ColumnExpr("DISTINCT cve_store_name").Column("image_scan_execution_result.*", "ImageScanExecutionHistory", "CveStore").
113114
Where("image_scan_execution_history.image_hash = ?", imageDigest).Order("image_scan_execution_history.execution_time desc").Select()
114115
return model, err
115116
}
116117

117118
func (impl ImageScanResultRepositoryImpl) FindByImageDigests(digest []string) ([]*ImageScanExecutionResult, error) {
118119
var models []*ImageScanExecutionResult
119-
err := impl.dbConnection.Model(&models).Column("image_scan_execution_result.*", "ImageScanExecutionHistory", "CveStore").
120+
err := impl.dbConnection.Model(&models).ColumnExpr("DISTINCT cve_store_name").Column("image_scan_execution_result.*", "ImageScanExecutionHistory", "CveStore").
120121
Where("image_hash in (?)", pg.In(digest)).Order("execution_time desc").Select()
121122
return models, err
122123
}
Lines changed: 127 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,127 @@
1+
package security
2+
3+
import (
4+
serverBean "github.com/devtron-labs/devtron/pkg/server/bean"
5+
"github.com/devtron-labs/devtron/pkg/sql"
6+
"github.com/go-pg/pg"
7+
"go.uber.org/zap"
8+
"time"
9+
)
10+
11+
type ScanToolExecutionHistoryMapping struct {
12+
tableName struct{} `sql:"scan_tool_execution_history_mapping" pg:",discard_unknown_columns"`
13+
Id int `sql:"id,pk"`
14+
ImageScanExecutionHistoryId int `sql:"image_scan_execution_history_id"`
15+
ScanToolId int `sql:"scan_tool_id"`
16+
ExecutionStartTime time.Time `sql:"execution_start_time,notnull"`
17+
ExecutionFinishTime time.Time `sql:"execution_finish_time,notnull"`
18+
State serverBean.ScanExecutionProcessState `sql:"state"`
19+
TryCount int `sql:"try_count"`
20+
sql.AuditLog
21+
}
22+
23+
type ScanToolExecutionHistoryMappingRepository interface {
24+
Save(model *ScanToolExecutionHistoryMapping) error
25+
SaveInBatch(models []*ScanToolExecutionHistoryMapping) error
26+
UpdateStateByToolAndExecutionHistoryId(executionHistoryId, toolId int, state serverBean.ScanExecutionProcessState, executionFinishTime time.Time) error
27+
MarkAllRunningStateAsFailedHavingTryCountReachedLimit(tryCount int) error
28+
GetAllScanHistoriesByState(state serverBean.ScanExecutionProcessState) ([]*ScanToolExecutionHistoryMapping, error)
29+
GetAllScanHistoriesByExecutionHistoryIdAndStates(executionHistoryId int, states []serverBean.ScanExecutionProcessState) ([]*ScanToolExecutionHistoryMapping, error)
30+
GetAllScanHistoriesByExecutionHistoryIds(ids []int) ([]*ScanToolExecutionHistoryMapping, error)
31+
}
32+
33+
type ScanToolExecutionHistoryMappingRepositoryImpl struct {
34+
dbConnection *pg.DB
35+
logger *zap.SugaredLogger
36+
}
37+
38+
func NewScanToolExecutionHistoryMappingRepositoryImpl(dbConnection *pg.DB,
39+
logger *zap.SugaredLogger) *ScanToolExecutionHistoryMappingRepositoryImpl {
40+
return &ScanToolExecutionHistoryMappingRepositoryImpl{
41+
dbConnection: dbConnection,
42+
logger: logger,
43+
}
44+
}
45+
46+
func (repo *ScanToolExecutionHistoryMappingRepositoryImpl) Save(model *ScanToolExecutionHistoryMapping) error {
47+
err := repo.dbConnection.Insert(model)
48+
if err != nil {
49+
repo.logger.Errorw("error in ScanToolExecutionHistoryMappingRepository, Save", "model", model, "err", err)
50+
return err
51+
}
52+
return nil
53+
}
54+
55+
func (repo *ScanToolExecutionHistoryMappingRepositoryImpl) SaveInBatch(models []*ScanToolExecutionHistoryMapping) error {
56+
err := repo.dbConnection.Insert(&models)
57+
if err != nil {
58+
repo.logger.Errorw("error in ScanToolExecutionHistoryMappingRepository, SaveInBatch", "err", err, "models", models)
59+
return err
60+
}
61+
return nil
62+
}
63+
64+
func (repo *ScanToolExecutionHistoryMappingRepositoryImpl) UpdateStateByToolAndExecutionHistoryId(executionHistoryId, toolId int,
65+
state serverBean.ScanExecutionProcessState, executionFinishTime time.Time) error {
66+
model := &ScanToolExecutionHistoryMapping{}
67+
_, err := repo.dbConnection.Model(model).Set("state = ?", state).
68+
Set("execution_finish_time = ?", executionFinishTime).
69+
Set("updated_on = ?", time.Now()).
70+
Set("updated_by =?", time.Now()).
71+
Where("image_scan_execution_history_id = ?", executionHistoryId).
72+
Where("scan_tool_id = ?", toolId).Update()
73+
if err != nil {
74+
repo.logger.Errorw("error in ScanToolExecutionHistoryMappingRepository, SaveInBatch", "err", err, "model", model)
75+
return err
76+
}
77+
return nil
78+
}
79+
80+
func (repo *ScanToolExecutionHistoryMappingRepositoryImpl) MarkAllRunningStateAsFailedHavingTryCountReachedLimit(tryCount int) error {
81+
var models []*ScanToolExecutionHistoryMapping
82+
_, err := repo.dbConnection.Model(&models).
83+
Set("state = ?", serverBean.ScanExecutionProcessStateFailed).
84+
Set("updated_on = ?", time.Now()).
85+
Set("updated_by =?", time.Now()).
86+
Where("state = ?", serverBean.ScanExecutionProcessStateRunning).
87+
Where("try_count > ?", tryCount).Update()
88+
if err != nil {
89+
repo.logger.Errorw("error in ScanToolExecutionHistoryMappingRepository, MarkAllRunningStateAsFailedHavingTryCountReachedLimit", "err", err)
90+
return err
91+
}
92+
return nil
93+
}
94+
95+
func (repo *ScanToolExecutionHistoryMappingRepositoryImpl) GetAllScanHistoriesByState(state serverBean.ScanExecutionProcessState) ([]*ScanToolExecutionHistoryMapping, error) {
96+
var models []*ScanToolExecutionHistoryMapping
97+
err := repo.dbConnection.Model(&models).Column("scan_tool_execution_history_mapping.*").
98+
Where("state = ?", state).Select()
99+
if err != nil {
100+
repo.logger.Errorw("error in ScanToolExecutionHistoryMappingRepository, GetAllScanHistoriesByState", "err", err)
101+
return nil, err
102+
}
103+
return models, nil
104+
}
105+
106+
func (repo *ScanToolExecutionHistoryMappingRepositoryImpl) GetAllScanHistoriesByExecutionHistoryIdAndStates(executionHistoryId int, states []serverBean.ScanExecutionProcessState) ([]*ScanToolExecutionHistoryMapping, error) {
107+
var models []*ScanToolExecutionHistoryMapping
108+
err := repo.dbConnection.Model(&models).Column("scan_tool_execution_history_mapping.*").
109+
Where("image_scan_execution_history_id = ?", executionHistoryId).
110+
Where("state in (?)", pg.In(states)).Select()
111+
if err != nil {
112+
repo.logger.Errorw("error in ScanToolExecutionHistoryMappingRepository, GetAllScanHistoriesByState", "err", err)
113+
return nil, err
114+
}
115+
return models, nil
116+
}
117+
func (repo *ScanToolExecutionHistoryMappingRepositoryImpl) GetAllScanHistoriesByExecutionHistoryIds(ids []int) ([]*ScanToolExecutionHistoryMapping, error) {
118+
var models []*ScanToolExecutionHistoryMapping
119+
err := repo.dbConnection.Model(&models).Column("scan_tool_execution_history_mapping.*").
120+
Where("image_scan_execution_history_id in (?)", pg.In(ids)).
121+
Select()
122+
if err != nil {
123+
repo.logger.Errorw("error in getting ScanToolExecutionHistoryMappingRepository, GetAllScanHistoriesByState", "err", err)
124+
return nil, err
125+
}
126+
return models, nil
127+
}

0 commit comments

Comments
 (0)