@@ -19,13 +19,16 @@ package restHandler
1919import (
2020 "encoding/json"
2121 "fmt"
22+ "net/http"
23+ "strconv"
24+
2225 "github.com/devtron-labs/devtron/pkg/cluster/environment"
2326 "github.com/devtron-labs/devtron/pkg/policyGovernance/security/imageScanning"
2427 securityBean "github.com/devtron-labs/devtron/pkg/policyGovernance/security/imageScanning/bean"
2528 security2 "github.com/devtron-labs/devtron/pkg/policyGovernance/security/imageScanning/repository"
29+ "github.com/devtron-labs/devtron/pkg/policyGovernance/security/imageScanning/repository/bean"
2630 "github.com/devtron-labs/devtron/util/sliceUtil"
27- "net/http"
28- "strconv"
31+ "go.opentelemetry.io/otel"
2932
3033 "github.com/devtron-labs/devtron/api/restHandler/common"
3134 "github.com/devtron-labs/devtron/internal/util"
@@ -46,6 +49,8 @@ type ImageScanRestHandler interface {
4649 FetchExecutionDetail (w http.ResponseWriter , r * http.Request )
4750 FetchMinScanResultByAppIdAndEnvId (w http.ResponseWriter , r * http.Request )
4851 VulnerabilityExposure (w http.ResponseWriter , r * http.Request )
52+ VulnerabilitySummary (w http.ResponseWriter , r * http.Request )
53+ VulnerabilityListing (w http.ResponseWriter , r * http.Request )
4954}
5055
5156type ImageScanRestHandlerImpl struct {
@@ -402,3 +407,229 @@ func (impl ImageScanRestHandlerImpl) VulnerabilityExposure(w http.ResponseWriter
402407 results .VulnerabilityExposure = vulnerabilityExposure
403408 common .WriteJsonResp (w , err , results , http .StatusOK )
404409}
410+
411+ func (impl ImageScanRestHandlerImpl ) VulnerabilitySummary (w http.ResponseWriter , r * http.Request ) {
412+ ctx , span := otel .Tracer ("imageScanRestHandler" ).Start (r .Context (), "VulnerabilitySummary" )
413+ defer span .End ()
414+
415+ userId , err := impl .userService .GetLoggedInUser (r )
416+ if userId == 0 || err != nil {
417+ common .HandleUnauthorized (w , r )
418+ return
419+ }
420+
421+ // Parse request body with filters
422+ decoder := json .NewDecoder (r .Body )
423+ var summaryRequest * securityBean.VulnerabilitySummaryRequest
424+ err = decoder .Decode (& summaryRequest )
425+ if err != nil {
426+ impl .logger .Errorw ("request err, VulnerabilitySummary" , "err" , err , "payload" , r .Body )
427+ common .WriteJsonResp (w , err , nil , http .StatusBadRequest )
428+ return
429+ }
430+
431+ // Create ImageScanRequest with filters for fetching deploy info
432+ request := & securityBean.ImageScanRequest {
433+ ImageScanFilter : bean.ImageScanFilter {
434+ EnvironmentIds : summaryRequest .EnvironmentIds ,
435+ ClusterIds : summaryRequest .ClusterIds ,
436+ },
437+ }
438+
439+ deployInfoList , err := impl .imageScanService .FetchAllDeployInfo (request )
440+ if err != nil {
441+ impl .logger .Errorw ("service err, VulnerabilitySummary" , "err" , err )
442+ if util .IsErrNoRows (err ) {
443+ emptySummary := & securityBean.VulnerabilitySummary {
444+ TotalVulnerabilities : 0 ,
445+ SeverityCount : & securityBean.SeverityCount {
446+ Critical : 0 ,
447+ High : 0 ,
448+ Medium : 0 ,
449+ Low : 0 ,
450+ Unknown : 0 ,
451+ },
452+ FixableVulnerabilities : 0 ,
453+ NotFixableVulnerabilities : 0 ,
454+ }
455+ common .WriteJsonResp (w , nil , emptySummary , http .StatusOK )
456+ } else {
457+ common .WriteJsonResp (w , err , nil , http .StatusInternalServerError )
458+ }
459+ return
460+ }
461+
462+ filteredDeployInfoList , err := impl .imageScanService .FilterDeployInfoByScannedArtifactsDeployedInEnv (deployInfoList )
463+ if err != nil {
464+ impl .logger .Errorw ("request err, FilterDeployInfoListForScannedArtifacts" , "err" , err )
465+ common .WriteJsonResp (w , err , nil , http .StatusInternalServerError )
466+ return
467+ }
468+
469+ _ , rbacSpan := otel .Tracer ("imageScanRestHandler" ).Start (ctx , "RBACProcessing" )
470+ token := r .Header .Get ("token" )
471+ isSuperAdmin := false
472+ if ok := impl .enforcer .Enforce (token , casbin .ResourceGlobal , casbin .ActionGet , "*" ); ok {
473+ isSuperAdmin = true
474+ }
475+ var ids []int
476+ if isSuperAdmin {
477+ ids = sliceUtil .NewSliceFromFuncExec (filteredDeployInfoList , func (item * security2.ImageScanDeployInfo ) int {
478+ return item .Id
479+ })
480+ } else {
481+ ids , err = impl .getAuthorisedImageScanDeployInfoIds (token , filteredDeployInfoList )
482+ if err != nil {
483+ impl .logger .Errorw ("error in getting authorised image scan deploy info ids" , "err" , err )
484+ common .WriteJsonResp (w , err , nil , http .StatusInternalServerError )
485+ return
486+ }
487+ }
488+ rbacSpan .End ()
489+
490+ if len (ids ) == 0 {
491+ emptySummary := & securityBean.VulnerabilitySummary {
492+ TotalVulnerabilities : 0 ,
493+ SeverityCount : & securityBean.SeverityCount {
494+ Critical : 0 ,
495+ High : 0 ,
496+ Medium : 0 ,
497+ Low : 0 ,
498+ Unknown : 0 ,
499+ },
500+ FixableVulnerabilities : 0 ,
501+ NotFixableVulnerabilities : 0 ,
502+ }
503+ common .WriteJsonResp (w , nil , emptySummary , http .StatusOK )
504+ return
505+ }
506+
507+ summary , err := impl .imageScanService .FetchVulnerabilitySummary (ctx , summaryRequest , ids )
508+ if err != nil {
509+ impl .logger .Errorw ("service err, VulnerabilitySummary" , "err" , err )
510+ if util .IsErrNoRows (err ) {
511+ emptySummary := & securityBean.VulnerabilitySummary {
512+ TotalVulnerabilities : 0 ,
513+ SeverityCount : & securityBean.SeverityCount {
514+ Critical : 0 ,
515+ High : 0 ,
516+ Medium : 0 ,
517+ Low : 0 ,
518+ Unknown : 0 ,
519+ },
520+ FixableVulnerabilities : 0 ,
521+ NotFixableVulnerabilities : 0 ,
522+ }
523+ common .WriteJsonResp (w , nil , emptySummary , http .StatusOK )
524+ } else {
525+ common .WriteJsonResp (w , err , nil , http .StatusInternalServerError )
526+ }
527+ return
528+ }
529+ common .WriteJsonResp (w , err , summary , http .StatusOK )
530+ }
531+
532+ func (impl ImageScanRestHandlerImpl ) VulnerabilityListing (w http.ResponseWriter , r * http.Request ) {
533+ ctx , span := otel .Tracer ("imageScanRestHandler" ).Start (r .Context (), "VulnerabilityListing" )
534+ defer span .End ()
535+
536+ userId , err := impl .userService .GetLoggedInUser (r )
537+ if userId == 0 || err != nil {
538+ common .HandleUnauthorized (w , r )
539+ return
540+ }
541+
542+ // Parse request body
543+ decoder := json .NewDecoder (r .Body )
544+ var request * securityBean.VulnerabilityListingRequest
545+ err = decoder .Decode (& request )
546+ if err != nil {
547+ impl .logger .Errorw ("request err, VulnerabilityListing" , "err" , err , "payload" , r .Body )
548+ common .WriteJsonResp (w , err , nil , http .StatusBadRequest )
549+ return
550+ }
551+
552+ // Fetch all deploy info to apply RBAC
553+ deployInfoRequest := & securityBean.ImageScanRequest {
554+ ImageScanFilter : bean.ImageScanFilter {
555+ EnvironmentIds : request .EnvironmentIds ,
556+ ClusterIds : request .ClusterIds ,
557+ },
558+ }
559+
560+ deployInfoList , err := impl .imageScanService .FetchAllDeployInfo (deployInfoRequest )
561+ if err != nil {
562+ impl .logger .Errorw ("service err, VulnerabilityListing" , "err" , err )
563+ if util .IsErrNoRows (err ) {
564+ emptyResponse := & securityBean.VulnerabilityListingResponse {
565+ Offset : request .Offset ,
566+ Size : request .Size ,
567+ Total : 0 ,
568+ Vulnerabilities : []* securityBean.VulnerabilityDetail {},
569+ }
570+ common .WriteJsonResp (w , nil , emptyResponse , http .StatusOK )
571+ } else {
572+ common .WriteJsonResp (w , err , nil , http .StatusInternalServerError )
573+ }
574+ return
575+ }
576+
577+ filteredDeployInfoList , err := impl .imageScanService .FilterDeployInfoByScannedArtifactsDeployedInEnv (deployInfoList )
578+ if err != nil {
579+ impl .logger .Errorw ("request err, FilterDeployInfoListForScannedArtifacts" , "err" , err )
580+ common .WriteJsonResp (w , err , nil , http .StatusInternalServerError )
581+ return
582+ }
583+
584+ // Apply RBAC
585+ _ , rbacSpan := otel .Tracer ("imageScanRestHandler" ).Start (ctx , "RBACProcessing" )
586+ token := r .Header .Get ("token" )
587+ isSuperAdmin := false
588+ if ok := impl .enforcer .Enforce (token , casbin .ResourceGlobal , casbin .ActionGet , "*" ); ok {
589+ isSuperAdmin = true
590+ }
591+ var ids []int
592+ if isSuperAdmin {
593+ ids = sliceUtil .NewSliceFromFuncExec (filteredDeployInfoList , func (item * security2.ImageScanDeployInfo ) int {
594+ return item .Id
595+ })
596+ } else {
597+ ids , err = impl .getAuthorisedImageScanDeployInfoIds (token , filteredDeployInfoList )
598+ if err != nil {
599+ impl .logger .Errorw ("error in getting authorised image scan deploy info ids" , "err" , err )
600+ common .WriteJsonResp (w , err , nil , http .StatusInternalServerError )
601+ return
602+ }
603+ }
604+ rbacSpan .End ()
605+
606+ if len (ids ) == 0 {
607+ emptyResponse := & securityBean.VulnerabilityListingResponse {
608+ Offset : request .Offset ,
609+ Size : request .Size ,
610+ Total : 0 ,
611+ Vulnerabilities : []* securityBean.VulnerabilityDetail {},
612+ }
613+ common .WriteJsonResp (w , nil , emptyResponse , http .StatusOK )
614+ return
615+ }
616+
617+ // Fetch vulnerability listing
618+ listing , err := impl .imageScanService .FetchVulnerabilityListing (ctx , request , ids )
619+ if err != nil {
620+ impl .logger .Errorw ("service err, VulnerabilityListing" , "err" , err )
621+ if util .IsErrNoRows (err ) {
622+ emptyResponse := & securityBean.VulnerabilityListingResponse {
623+ Offset : request .Offset ,
624+ Size : request .Size ,
625+ Total : 0 ,
626+ Vulnerabilities : []* securityBean.VulnerabilityDetail {},
627+ }
628+ common .WriteJsonResp (w , nil , emptyResponse , http .StatusOK )
629+ } else {
630+ common .WriteJsonResp (w , err , nil , http .StatusInternalServerError )
631+ }
632+ return
633+ }
634+ common .WriteJsonResp (w , err , listing , http .StatusOK )
635+ }
0 commit comments