Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
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
2 changes: 2 additions & 0 deletions Wire.go
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ import (
"github.com/devtron-labs/devtron/api/infraConfig"
"github.com/devtron-labs/devtron/api/k8s"
"github.com/devtron-labs/devtron/api/module"
"github.com/devtron-labs/devtron/api/resourceScan"
"github.com/devtron-labs/devtron/api/restHandler"
"github.com/devtron-labs/devtron/api/restHandler/app/appInfo"
appList2 "github.com/devtron-labs/devtron/api/restHandler/app/appList"
Expand Down Expand Up @@ -210,6 +211,7 @@ func InitializeApp() (*App, error) {
imageTagging.WireSet,
devtronResource.DevtronResourceWireSet,
policyGovernance.PolicyGovernanceWireSet,
resourceScan.ScanningResultWireSet,

// -------wireset end ----------
// -------
Expand Down
133 changes: 133 additions & 0 deletions api/resourceScan/resourceScanRestHandler.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,133 @@
/*
* Copyright (c) 2024. Devtron Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package resourceScan

import (
"fmt"
"github.com/devtron-labs/devtron/api/restHandler/common"
"github.com/devtron-labs/devtron/pkg/auth/authorisation/casbin"
"github.com/devtron-labs/devtron/pkg/auth/user"
"github.com/devtron-labs/devtron/pkg/policyGovernance/security/imageScanning"
"github.com/devtron-labs/devtron/pkg/policyGovernance/security/imageScanning/bean"
"github.com/devtron-labs/devtron/util/rbac"
"go.uber.org/zap"
"gopkg.in/go-playground/validator.v9"
"net/http"
)

type ScanningResultRestHandler interface {
ScanResults(w http.ResponseWriter, r *http.Request)
}

type ScanningResultRestHandlerImpl struct {
logger *zap.SugaredLogger
userService user.UserService
scanService imageScanning.ImageScanService
enforcer casbin.Enforcer
enforcerUtil rbac.EnforcerUtil
validator *validator.Validate
}

func NewScanningResultRestHandlerImpl(
logger *zap.SugaredLogger,
userService user.UserService,
scanService imageScanning.ImageScanService,
enforcer casbin.Enforcer,
enforcerUtil rbac.EnforcerUtil,
validator *validator.Validate,
) *ScanningResultRestHandlerImpl {
return &ScanningResultRestHandlerImpl{
logger: logger,
userService: userService,
scanService: scanService,
enforcer: enforcer,
enforcerUtil: enforcerUtil,
validator: validator,
}
}

func getResourceScanQueryParams(w http.ResponseWriter, r *http.Request) (*bean.ResourceScanQueryParams, error) {
queryParams := &bean.ResourceScanQueryParams{}
var appId, envId, installedAppId, artifactId, installedAppVersionHistoryId int
var err error
appId, err = common.ExtractIntQueryParam(w, r, "appId", 0)
if err != nil {
return queryParams, err
}
queryParams.AppId = appId

installedAppId, err = common.ExtractIntQueryParam(w, r, "installedAppId", 0)
if err != nil {
return queryParams, err
}
queryParams.InstalledAppId = installedAppId

installedAppVersionHistoryId, err = common.ExtractIntQueryParam(w, r, "installedAppVersionHistoryId", 0)
if err != nil {
return queryParams, err
}
queryParams.InstalledAppVersionHistoryId = installedAppVersionHistoryId

envId, err = common.ExtractIntQueryParam(w, r, "envId", 0)
if err != nil {
return queryParams, err
}
queryParams.EnvId = envId

artifactId, err = common.ExtractIntQueryParam(w, r, "artifactId", 0)
if err != nil {
return queryParams, err
}
queryParams.ArtifactId = artifactId
return queryParams, nil
}

func (impl ScanningResultRestHandlerImpl) ScanResults(w http.ResponseWriter, r *http.Request) {
userId, err := impl.userService.GetLoggedInUser(r)
if userId == 0 || err != nil {
common.WriteJsonResp(w, err, "Unauthorized User", http.StatusUnauthorized)
return
}
resourceScanQueryParams, err := getResourceScanQueryParams(w, r)
if err != nil {
return
}
// RBAC
token := r.Header.Get("token")
object := impl.enforcerUtil.GetAppRBACNameByAppId(resourceScanQueryParams.AppId)
if ok := impl.enforcer.Enforce(token, casbin.ResourceApplications, casbin.ActionGet, object); !ok {
common.WriteJsonResp(w, fmt.Errorf("unauthorized user"), "Unauthorized User", http.StatusForbidden)
return
}
if resourceScanQueryParams.EnvId > 0 {
object = impl.enforcerUtil.GetEnvRBACNameByAppId(resourceScanQueryParams.AppId, resourceScanQueryParams.EnvId)
if ok := impl.enforcer.Enforce(token, casbin.ResourceEnvironment, casbin.ActionGet, object); !ok {
common.WriteJsonResp(w, fmt.Errorf("unauthorized user"), "Unauthorized User", http.StatusForbidden)
return
}
}
// RBAC
resp, err := impl.scanService.GetScanResults(resourceScanQueryParams)
if err != nil {
impl.logger.Errorw("service err, GetScanResults", "resourceScanQueryParams", resourceScanQueryParams, "err", err)
common.WriteJsonResp(w, err, nil, http.StatusInternalServerError)
return
}

common.WriteJsonResp(w, nil, resp, http.StatusOK)

}
25 changes: 25 additions & 0 deletions api/resourceScan/resourceScanRouter.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
/*
* Copyright (c) 2024. Devtron Inc.
*/

package resourceScan

import (
"github.com/gorilla/mux"
)

type ScanningResultRouter interface {
InitScanningResultRouter(configRouter *mux.Router)
}

type ScanningResultRouterImpl struct {
ScanningResultRestHandler ScanningResultRestHandler
}

func NewScanningResultRouterImpl(ScanningResultRestHandler ScanningResultRestHandler) *ScanningResultRouterImpl {
return &ScanningResultRouterImpl{ScanningResultRestHandler: ScanningResultRestHandler}
}

func (router *ScanningResultRouterImpl) InitScanningResultRouter(configRouter *mux.Router) {
configRouter.Path("").HandlerFunc(router.ScanningResultRestHandler.ScanResults).Methods("GET")
}
16 changes: 16 additions & 0 deletions api/resourceScan/wire_scan.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
/*
* Copyright (c) 2024. Devtron Inc.
*/

package resourceScan

import (
"github.com/google/wire"
)

var ScanningResultWireSet = wire.NewSet(
NewScanningResultRouterImpl,
wire.Bind(new(ScanningResultRouter), new(*ScanningResultRouterImpl)),
NewScanningResultRestHandlerImpl,
wire.Bind(new(ScanningResultRestHandler), new(*ScanningResultRestHandlerImpl)),
)
8 changes: 8 additions & 0 deletions api/router/router.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ import (
"github.com/devtron-labs/devtron/api/k8s/application"
"github.com/devtron-labs/devtron/api/k8s/capacity"
"github.com/devtron-labs/devtron/api/module"
"github.com/devtron-labs/devtron/api/resourceScan"
"github.com/devtron-labs/devtron/api/restHandler/common"
"github.com/devtron-labs/devtron/api/router/app"
"github.com/devtron-labs/devtron/api/router/app/configDiff"
Expand Down Expand Up @@ -120,6 +121,7 @@ type MuxRouter struct {
argoApplicationRouter argoApplication.ArgoApplicationRouter
fluxApplicationRouter fluxApplication2.FluxApplicationRouter
devtronResourceRouter devtronResource.DevtronResourceRouter
scanningResultRouter resourceScan.ScanningResultRouter
}

func NewMuxRouter(logger *zap.SugaredLogger,
Expand Down Expand Up @@ -153,6 +155,7 @@ func NewMuxRouter(logger *zap.SugaredLogger,
argoApplicationRouter argoApplication.ArgoApplicationRouter,
devtronResourceRouter devtronResource.DevtronResourceRouter,
fluxApplicationRouter fluxApplication2.FluxApplicationRouter,
scanningResultRouter resourceScan.ScanningResultRouter,
) *MuxRouter {
r := &MuxRouter{
Router: mux.NewRouter(),
Expand Down Expand Up @@ -218,6 +221,7 @@ func NewMuxRouter(logger *zap.SugaredLogger,
argoApplicationRouter: argoApplicationRouter,
devtronResourceRouter: devtronResourceRouter,
fluxApplicationRouter: fluxApplicationRouter,
scanningResultRouter: scanningResultRouter,
}
return r
}
Expand Down Expand Up @@ -321,6 +325,9 @@ func (r MuxRouter) Init() {
imageScanRouter := r.Router.PathPrefix("/orchestrator/security/scan").Subrouter()
r.imageScanRouter.InitImageScanRouter(imageScanRouter)

scanResultRouter := r.Router.PathPrefix("/orchestrator/scan-result").Subrouter()
r.scanningResultRouter.InitScanningResultRouter(scanResultRouter)

policyRouter := r.Router.PathPrefix("/orchestrator/security/policy").Subrouter()
r.policyRouter.InitPolicyRouter(policyRouter)

Expand Down Expand Up @@ -429,4 +436,5 @@ func (r MuxRouter) Init() {

fluxApplicationRouter := r.Router.PathPrefix("/orchestrator/flux-application").Subrouter()
r.fluxApplicationRouter.InitFluxApplicationRouter(fluxApplicationRouter)

}
2 changes: 1 addition & 1 deletion cmd/external-app/wire_gen.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

68 changes: 66 additions & 2 deletions pkg/policyGovernance/security/imageScanning/ImageScanService.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,10 +18,13 @@ package imageScanning

import (
"context"
bean4 "github.com/devtron-labs/devtron/api/bean"
"github.com/devtron-labs/devtron/pkg/cluster/environment"
"github.com/devtron-labs/devtron/pkg/cluster/environment/bean"
bean2 "github.com/devtron-labs/devtron/pkg/deployment/trigger/devtronApps/bean"
"github.com/devtron-labs/devtron/pkg/policyGovernance/security/imageScanning/adapter"
bean3 "github.com/devtron-labs/devtron/pkg/policyGovernance/security/imageScanning/bean"
"github.com/devtron-labs/devtron/pkg/policyGovernance/security/imageScanning/helper/parser"
repository3 "github.com/devtron-labs/devtron/pkg/policyGovernance/security/imageScanning/repository"
securityBean "github.com/devtron-labs/devtron/pkg/policyGovernance/security/imageScanning/repository/bean"
serverBean "github.com/devtron-labs/devtron/pkg/server/bean"
Expand All @@ -45,6 +48,8 @@ type ImageScanService interface {
VulnerabilityExposure(request *repository3.VulnerabilityRequest) (*repository3.VulnerabilityExposureListingResponse, error)
GetArtifactVulnerabilityStatus(ctx context.Context, request *bean2.VulnerabilityCheckRequest) (bool, error)
IsImageScanExecutionCompleted(image, imageDigest string) (bool, error)
// resource scanning functions below
GetScanResults(resourceScanQueryParams *bean3.ResourceScanQueryParams) (parser.ResourceScanResponseDto, error)
}

type ImageScanServiceImpl struct {
Expand All @@ -64,6 +69,7 @@ type ImageScanServiceImpl struct {
scanToolMetaDataRepository repository3.ScanToolMetadataRepository
scanToolExecutionHistoryMappingRepository repository3.ScanToolExecutionHistoryMappingRepository
cvePolicyRepository repository3.CvePolicyRepository
cdWorkflowRepo pipelineConfig.CdWorkflowRepository
}

func NewImageScanServiceImpl(Logger *zap.SugaredLogger, scanHistoryRepository repository3.ImageScanHistoryRepository,
Expand All @@ -73,7 +79,8 @@ func NewImageScanServiceImpl(Logger *zap.SugaredLogger, scanHistoryRepository re
appRepository repository1.AppRepository,
envService environment.EnvironmentService, ciArtifactRepository repository.CiArtifactRepository, policyService PolicyService,
pipelineRepository pipelineConfig.PipelineRepository, ciPipelineRepository pipelineConfig.CiPipelineRepository, scanToolMetaDataRepository repository3.ScanToolMetadataRepository, scanToolExecutionHistoryMappingRepository repository3.ScanToolExecutionHistoryMappingRepository,
cvePolicyRepository repository3.CvePolicyRepository) *ImageScanServiceImpl {
cvePolicyRepository repository3.CvePolicyRepository,
cdWorkflowRepo pipelineConfig.CdWorkflowRepository) *ImageScanServiceImpl {
return &ImageScanServiceImpl{Logger: Logger, scanHistoryRepository: scanHistoryRepository, scanResultRepository: scanResultRepository,
scanObjectMetaRepository: scanObjectMetaRepository, cveStoreRepository: cveStoreRepository,
imageScanDeployInfoRepository: imageScanDeployInfoRepository,
Expand All @@ -87,6 +94,7 @@ func NewImageScanServiceImpl(Logger *zap.SugaredLogger, scanHistoryRepository re
scanToolMetaDataRepository: scanToolMetaDataRepository,
scanToolExecutionHistoryMappingRepository: scanToolExecutionHistoryMappingRepository,
cvePolicyRepository: cvePolicyRepository,
cdWorkflowRepo: cdWorkflowRepo,
}
}

Expand Down Expand Up @@ -249,13 +257,40 @@ func (impl ImageScanServiceImpl) fetchImageExecutionHistoryMapByIds(historyIds [
}
return mapOfExecutionHistoryIdVsExecutionTime, nil
}
func (impl ImageScanServiceImpl) fetchResourceScanningDataForEnv(appId, envId int) (int, error) {
cdWfRunner, err := impl.cdWorkflowRepo.FindLatestCdWorkflowRunnerByEnvironmentIdAndRunnerType(appId, envId, bean4.CD_WORKFLOW_TYPE_DEPLOY)
if err != nil {
impl.Logger.Errorw("error in fetching latest cd workflow runner of type DEPLOY for appId and envId", "appId", appId, "envId", envId, "err", err)
return 0, err
}
var ciArtifactId int
if cdWfRunner.CdWorkflow != nil && cdWfRunner.CdWorkflow.CiArtifact != nil {
// for images built by us or provided in external ci case.
ciArtifactId = cdWfRunner.CdWorkflow.CiArtifactId
if cdWfRunner.CdWorkflow.CiArtifact.ParentCiArtifact > 0 {
//linked ci case, parent ci artifact already scanned
ciArtifactId = cdWfRunner.CdWorkflow.CiArtifact.ParentCiArtifact
}
}

return ciArtifactId, nil
}

func (impl ImageScanServiceImpl) FetchExecutionDetailResult(request *bean3.ImageScanRequest) (*bean3.ImageScanExecutionDetail, error) {
//var scanExecution *security.ImageScanExecutionHistory
var scanExecutionIds []int
var executionTime time.Time
imageScanResponse := &bean3.ImageScanExecutionDetail{}
isRegularApp := false
if request.AppId > 0 && request.EnvId > 0 {
artifactId, err := impl.fetchResourceScanningDataForEnv(request.AppId, request.EnvId)
if err != nil {
impl.Logger.Errorw("error while fetching scan execution result", "appId", request.AppId, "envId", request.EnvId, "err", err)
return nil, err
}
// setting latest artifact deploy on app and env
request.ArtifactId = artifactId
}
if request.ImageScanDeployInfoId > 0 {
// scan detail for deployed images
scanDeployInfo, err := impl.imageScanDeployInfoRepository.FindOne(request.ImageScanDeployInfoId)
Expand Down Expand Up @@ -288,7 +323,7 @@ func (impl ImageScanServiceImpl) FetchExecutionDetailResult(request *bean3.Image
return nil, err
}
imageScanResponse.Image = ciArtifact.Image
scanExecution, err := impl.scanHistoryRepository.FindByImageAndDigest(ciArtifact.ImageDigest, ciArtifact.Image)
scanExecution, err := impl.scanHistoryRepository.FindByImageAndDigestWithState(ciArtifact.ImageDigest, ciArtifact.Image)
if err != nil {
impl.Logger.Errorw("error while fetching scan execution result", "err", err)
return nil, err
Expand All @@ -304,6 +339,9 @@ func (impl ImageScanServiceImpl) FetchExecutionDetailResult(request *bean3.Image
executionTime = scanExecution.ExecutionTime
imageScanResponse.ScanEnabled = ciArtifact.ScanEnabled
imageScanResponse.Scanned = ciArtifact.Scanned
if scanExecution.ScanToolExecutionHistoryMapping != nil {
imageScanResponse.Status = scanExecution.ScanToolExecutionHistoryMapping.State
}
if ciArtifact.ScanEnabled == false {
impl.Logger.Debugw("returning without result as scan disabled for this artifact", "ciArtifact", ciArtifact)
return imageScanResponse, nil
Expand Down Expand Up @@ -438,6 +476,15 @@ func (impl ImageScanServiceImpl) FetchExecutionDetailResult(request *bean3.Image
}
}
}
// setting scan tool name if scan tool id is present
if imageScanResponse.ScanToolId > 0 {
scanToolName, err := impl.scanToolMetaDataRepository.FindNameById(imageScanResponse.ScanToolId)
if err != nil {
impl.Logger.Errorw("error in getting scan tool name by id", "scanToolId", imageScanResponse.ScanToolId, "err", err)
return nil, err
}
imageScanResponse.ScanToolName = scanToolName
}
return imageScanResponse, nil
}

Expand Down Expand Up @@ -662,3 +709,20 @@ func (impl ImageScanServiceImpl) IsImageScanExecutionCompleted(image, imageDiges
}
return isScanningCompleted, nil
}

func (impl ImageScanServiceImpl) GetScanResults(resourceScanQueryParams *bean3.ResourceScanQueryParams) (resp parser.ResourceScanResponseDto, err error) {
request := &bean3.ImageScanRequest{
ArtifactId: resourceScanQueryParams.ArtifactId,
AppId: resourceScanQueryParams.AppId,
EnvId: resourceScanQueryParams.EnvId,
}
respFromExecutionDetail, err := impl.FetchExecutionDetailResult(request)
if err != nil {
impl.Logger.Errorw("error encountered in GetScanResults", "req", request, "err", err)
return resp, err
}

// build an adapter to convert the respFromExecutionDetail to the required ResourceScanResponseDto format
return adapter.ExecutionDetailsToResourceScanResponseDto(respFromExecutionDetail), nil

}
Loading
Loading