Skip to content

Commit dda532d

Browse files
authored
Merge pull request #878 from l3montree-dev/844-license-risks
844 license risks
2 parents b6c0288 + 8b77f21 commit dda532d

37 files changed

Lines changed: 3297 additions & 1264 deletions

.env.example

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@ INSTANCE_DOMAIN=https://api.main.devguard.org #Choose which version of devguar
1616

1717
FRONTEND_URL=http://localhost:3000
1818

19+
OSI_LICENSES_API=https://opensource.org/api/license/
20+
1921
PDF_GENERATION_API=https://dwt-api.dev-l3montree.cloud/pdf
2022
# comment to disable error tracking
2123
ERROR_TRACKING_DSN="https://<your-error-tracking-dsn>"

cmd/devguard-cli/commands/components.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import (
55

66
"github.com/l3montree-dev/devguard/internal/core"
77
"github.com/l3montree-dev/devguard/internal/core/component"
8+
"github.com/l3montree-dev/devguard/internal/core/vuln"
89
"github.com/l3montree-dev/devguard/internal/core/vulndb"
910
"github.com/l3montree-dev/devguard/internal/database/models"
1011
"github.com/l3montree-dev/devguard/internal/database/repositories"
@@ -37,6 +38,7 @@ func newUpdateDepsDevInformation() *cobra.Command {
3738
depsDevService := vulndb.NewDepsDevService()
3839
componentProjectRepository := repositories.NewComponentProjectRepository(database)
3940
componentRepository := repositories.NewComponentRepository(database)
41+
licenseRiskService := vuln.NewLicenseRiskService(repositories.NewLicenseRiskRepository(database), repositories.NewVulnEventRepository(database))
4042

4143
components, err := componentRepository.All()
4244
if err != nil {
@@ -48,6 +50,7 @@ func newUpdateDepsDevInformation() *cobra.Command {
4850
&depsDevService,
4951
componentProjectRepository,
5052
componentRepository,
53+
licenseRiskService,
5154
)
5255

5356
bar := progressbar.Default(int64(len(components)))

cmd/devguard/api/api.go

Lines changed: 15 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -399,7 +399,7 @@ func BuildRouter(db core.DB) *echo.Echo {
399399
supplyChainRepository := repositories.NewSupplyChainRepository(db)
400400
attestationRepository := repositories.NewAttestationRepository(db)
401401
policyRepository := repositories.NewPolicyRepository(db)
402-
licenseOverwriteRepository := repositories.NewLicenseOverwriteRepository(db)
402+
licenseRiskRepository := repositories.NewLicenseRiskRepository(db)
403403

404404
dependencyVulnService := vuln.NewService(dependencyVulnRepository, vulnEventRepository, assetRepository, cveRepository, orgRepository, projectRepository, thirdPartyIntegration, assetVersionRepository)
405405
firstPartyVulnService := vuln.NewFirstPartyVulnService(firstPartyVulnRepository, vulnEventRepository, assetRepository)
@@ -411,7 +411,8 @@ func BuildRouter(db core.DB) *echo.Echo {
411411
assetService := asset.NewService(assetRepository, dependencyVulnRepository, dependencyVulnService)
412412
depsDevService := vulndb.NewDepsDevService()
413413
componentProjectRepository := repositories.NewComponentProjectRepository(db)
414-
componentService := component.NewComponentService(&depsDevService, componentProjectRepository, componentRepository)
414+
licenseRiskService := vuln.NewLicenseRiskService(licenseRiskRepository, vulnEventRepository)
415+
componentService := component.NewComponentService(&depsDevService, componentProjectRepository, componentRepository, licenseRiskService)
415416

416417
assetVersionService := assetversion.NewService(assetVersionRepository, componentRepository, dependencyVulnRepository, firstPartyVulnRepository, dependencyVulnService, firstPartyVulnService, assetRepository, vulnEventRepository, &componentService)
417418
statisticsService := statistics.NewService(statisticsRepository, componentRepository, assetRiskAggregationRepository, dependencyVulnRepository, assetVersionRepository, projectRepository, repositories.NewProjectRiskHistoryRepository(db))
@@ -430,15 +431,16 @@ func BuildRouter(db core.DB) *echo.Echo {
430431

431432
scanController := scan.NewHTTPController(db, cveRepository, componentRepository, assetRepository, assetVersionRepository, assetVersionService, statisticsService, dependencyVulnService)
432433

433-
assetVersionController := assetversion.NewAssetVersionController(assetVersionRepository, assetVersionService, dependencyVulnRepository, componentRepository, dependencyVulnService, supplyChainRepository, licenseOverwriteRepository)
434+
assetVersionController := assetversion.NewAssetVersionController(assetVersionRepository, assetVersionService, dependencyVulnRepository, componentRepository, dependencyVulnService, supplyChainRepository, licenseRiskRepository)
434435
attestationController := attestation.NewAttestationController(attestationRepository, assetVersionRepository)
435436
intotoController := intoto.NewHTTPController(intotoLinkRepository, supplyChainRepository, assetVersionRepository, patRepository, intotoService)
436-
componentController := component.NewHTTPController(componentRepository, assetVersionRepository, licenseOverwriteRepository)
437+
componentController := component.NewHTTPController(componentRepository, assetVersionRepository, licenseRiskRepository)
438+
437439
complianceController := compliance.NewHTTPController(assetVersionRepository, attestationRepository, policyRepository)
438440

439441
statisticsController := statistics.NewHTTPController(statisticsService, statisticsRepository, assetRepository, assetVersionRepository, projectService)
440442
firstPartyVulnController := vuln.NewFirstPartyVulnController(firstPartyVulnRepository, firstPartyVulnService, projectService)
441-
licenseOverwriteController := component.NewLicenseOverwriteController(licenseOverwriteRepository)
443+
licenseRiskController := vuln.NewLicenseRiskController(licenseRiskRepository, licenseRiskService)
442444

443445
patService := pat.NewPatService(patRepository)
444446

@@ -556,8 +558,6 @@ func BuildRouter(db core.DB) *echo.Echo {
556558
organizationRouter.POST("/projects/", projectController.Create, neededScope([]string{"manage"}), accessControlMiddleware(core.ObjectOrganization, core.ActionUpdate))
557559

558560
organizationRouter.GET("/config-files/:config-file/", orgController.GetConfigFile)
559-
organizationRouter.PUT("/license-overwrite/", licenseOverwriteController.Create, neededScope([]string{"manage"}))
560-
organizationRouter.DELETE("/license-overwrite/:componentPurl", licenseOverwriteController.Delete, neededScope([]string{"manage"}))
561561
//Api functions for interacting with a project inside an organization -> .../organizations/<organization-name>/projects/<project-name>/...
562562
projectRouter := organizationRouter.Group("/projects/:projectSlug", projectAccessControl(projectService, "project", core.ActionRead))
563563
projectRouter.GET("/", projectController.Read)
@@ -674,10 +674,8 @@ func BuildRouter(db core.DB) *echo.Echo {
674674
dependencyVulnRouter := assetVersionRouter.Group("/dependency-vulns")
675675
dependencyVulnRouter.GET("/", dependencyVulnController.ListPaged)
676676
dependencyVulnRouter.GET("/:dependencyVulnID/", dependencyVulnController.Read)
677-
678677
dependencyVulnRouter.POST("/:dependencyVulnID/", dependencyVulnController.CreateEvent, neededScope([]string{"manage"}), projectScopedRBAC(core.ObjectAsset, core.ActionUpdate))
679678
dependencyVulnRouter.POST("/:dependencyVulnID/mitigate/", dependencyVulnController.Mitigate, neededScope([]string{"manage"}), projectScopedRBAC(core.ObjectAsset, core.ActionUpdate))
680-
681679
dependencyVulnRouter.GET("/:dependencyVulnID/events/", vulnEventController.ReadAssetEventsByVulnID)
682680

683681
firstPartyVulnRouter := assetVersionRouter.Group("/first-party-vulns")
@@ -687,6 +685,14 @@ func BuildRouter(db core.DB) *echo.Echo {
687685
firstPartyVulnRouter.POST("/:firstPartyVulnID/mitigate/", firstPartyVulnController.Mitigate, neededScope([]string{"manage"}), projectScopedRBAC(core.ObjectAsset, core.ActionUpdate))
688686
firstPartyVulnRouter.GET("/:firstPartyVulnID/events/", vulnEventController.ReadAssetEventsByVulnID)
689687

688+
assetVersionRouter.POST("/license-risks/", licenseRiskController.Create)
689+
licenseRiskRouter := assetVersionRouter.Group("/license-risks")
690+
licenseRiskRouter.GET("/", licenseRiskController.ListPaged)
691+
licenseRiskRouter.GET("/:licenseRiskID/", licenseRiskController.Read)
692+
licenseRiskRouter.POST("/:licenseRiskID/", licenseRiskController.CreateEvent, neededScope([]string{"manage"}), projectScopedRBAC(core.ObjectAsset, core.ActionUpdate))
693+
licenseRiskRouter.POST("/:licenseRiskID/mitigate", licenseRiskController.Mitigate, neededScope([]string{"manage"}), projectScopedRBAC(core.ObjectAsset, core.ActionUpdate))
694+
licenseRiskRouter.POST("/:licenseRiskID/final-license-decision", licenseRiskController.MakeFinalLicenseDecision, neededScope([]string{"manage"}), projectScopedRBAC(core.ObjectAsset, core.ActionUpdate))
695+
690696
routes := server.Routes()
691697
sort.Slice(routes, func(i, j int) bool {
692698
return routes[i].Path < routes[j].Path

internal/core/assetversion/asset_version_controller.go

Lines changed: 16 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -26,13 +26,13 @@ import (
2626
)
2727

2828
type AssetVersionController struct {
29-
assetVersionRepository core.AssetVersionRepository
30-
assetVersionService core.AssetVersionService
31-
dependencyVulnRepository core.DependencyVulnRepository
32-
componentRepository core.ComponentRepository
33-
dependencyVulnService core.DependencyVulnService
34-
supplyChainRepository core.SupplyChainRepository
35-
licenseOverwriteRepository core.LicenseOverwriteRepository
29+
assetVersionRepository core.AssetVersionRepository
30+
assetVersionService core.AssetVersionService
31+
dependencyVulnRepository core.DependencyVulnRepository
32+
componentRepository core.ComponentRepository
33+
dependencyVulnService core.DependencyVulnService
34+
supplyChainRepository core.SupplyChainRepository
35+
licenseRiskRepository core.LicenseRiskRepository
3636
}
3737

3838
func NewAssetVersionController(
@@ -42,16 +42,16 @@ func NewAssetVersionController(
4242
componentRepository core.ComponentRepository,
4343
dependencyVulnService core.DependencyVulnService,
4444
supplyChainRepository core.SupplyChainRepository,
45-
licenseOverwriteRepository core.LicenseOverwriteRepository,
45+
licenseRiskRepository core.LicenseRiskRepository,
4646
) *AssetVersionController {
4747
return &AssetVersionController{
48-
assetVersionRepository: assetVersionRepository,
49-
assetVersionService: assetVersionService,
50-
dependencyVulnRepository: dependencyVulnRepository,
51-
componentRepository: componentRepository,
52-
dependencyVulnService: dependencyVulnService,
53-
supplyChainRepository: supplyChainRepository,
54-
licenseOverwriteRepository: licenseOverwriteRepository,
48+
assetVersionRepository: assetVersionRepository,
49+
assetVersionService: assetVersionService,
50+
dependencyVulnRepository: dependencyVulnRepository,
51+
componentRepository: componentRepository,
52+
dependencyVulnService: dependencyVulnService,
53+
supplyChainRepository: supplyChainRepository,
54+
licenseRiskRepository: licenseRiskRepository,
5555
}
5656
}
5757

@@ -218,7 +218,7 @@ func (a *AssetVersionController) buildSBOM(ctx core.Context) (*cdx.BOM, error) {
218218

219219
scannerID := ctx.QueryParam("scanner")
220220

221-
overwrittenLicenses, err := a.licenseOverwriteRepository.GetAllOverwritesForOrganization(org.ID)
221+
overwrittenLicenses, err := a.licenseRiskRepository.GetAllOverwrittenLicensesForAssetVersion(assetVersion.AssetID, assetVersion.Name)
222222
if err != nil {
223223
return nil, err
224224
}

internal/core/assetversion/asset_version_service.go

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -427,8 +427,8 @@ func (s *service) UpdateSBOM(assetVersion models.AssetVersion, scannerID string,
427427
}
428428

429429
existingComponentPurls := make(map[string]bool)
430-
for _, c := range assetComponents {
431-
existingComponentPurls[c.Component.Purl] = true
430+
for _, currentComponent := range assetComponents {
431+
existingComponentPurls[currentComponent.Component.Purl] = true
432432
}
433433

434434
// we need to check if the SBOM is new or if it already exists.
@@ -524,7 +524,7 @@ func (s *service) UpdateSBOM(assetVersion models.AssetVersion, scannerID string,
524524
go func() {
525525
slog.Info("updating license information in background", "asset", assetVersion.Name, "assetID", assetVersion.AssetID)
526526

527-
_, err := s.componentService.GetAndSaveLicenseInformation(assetVersion.Name, assetVersion.AssetID, scannerID)
527+
_, err := s.componentService.GetAndSaveLicenseInformation(assetVersion, scannerID)
528528
if err != nil {
529529
slog.Error("could not update license information", "asset", assetVersion.Name, "assetID", assetVersion.AssetID, "err", err)
530530
} else {
@@ -576,6 +576,7 @@ func (s *service) BuildSBOM(assetVersion models.AssetVersion, version string, or
576576
licenses := cdx.Licenses{}
577577
//technically redundant call to c.Dependency.ComponentProject.License
578578
if c.Dependency.ComponentProject != nil && c.Dependency.ComponentProject.License != "" {
579+
// if the license is not a valid osi license we need to assign that to the name attribute in the license choice struct, because ID can only contain valid IDs
579580
if c.Dependency.ComponentProject.License != "non-standard" {
580581
licenses = append(licenses, cdx.LicenseChoice{
581582
License: &cdx.License{

internal/core/common_interfaces.go

Lines changed: 19 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -123,7 +123,7 @@ type ComponentRepository interface {
123123
common.Repository[string, models.Component, DB]
124124

125125
LoadComponents(tx DB, assetVersionName string, assetID uuid.UUID, scannerID string) ([]models.ComponentDependency, error)
126-
LoadComponentsWithProject(tx DB, overwrittenLicenses []models.LicenseOverwrite, assetVersionName string, assetID uuid.UUID, scannerID string, pageInfo PageInfo, search string, filter []FilterQuery, sort []SortQuery) (Paged[models.ComponentDependency], error)
126+
LoadComponentsWithProject(tx DB, overwrittenLicenses []models.LicenseRisk, assetVersionName string, assetID uuid.UUID, scannerID string, pageInfo PageInfo, search string, filter []FilterQuery, sort []SortQuery) (Paged[models.ComponentDependency], error)
127127
LoadPathToComponent(tx DB, assetVersionName string, assetID uuid.UUID, pURL string, scannerID string) ([]models.ComponentDependency, error)
128128
SaveBatch(tx DB, components []models.Component) error
129129
FindByPurl(tx DB, purl string) (models.Component, error)
@@ -164,6 +164,17 @@ type FirstPartyVulnRepository interface {
164164
GetByAssetVersion(tx DB, assetVersionName string, assetID uuid.UUID) ([]models.FirstPartyVuln, error)
165165
}
166166

167+
type LicenseRiskRepository interface {
168+
common.Repository[string, models.LicenseRisk, DB]
169+
GetAllLicenseRisksForAssetVersionPaged(tx DB, assetID uuid.UUID, assetVersionName string, pageInfo PageInfo, search string, filter []FilterQuery, sort []SortQuery) (Paged[models.LicenseRisk], error)
170+
GetAllLicenseRisksForAssetVersion(assetID uuid.UUID, assetVersionName string) ([]models.LicenseRisk, error)
171+
GetAllOverwrittenLicensesForAssetVersion(assetID uuid.UUID, assetVersionName string) ([]models.LicenseRisk, error)
172+
MaybeGetLicenseOverwriteForComponent(assetID uuid.UUID, assetVersionName string, pURL packageurl.PackageURL) (models.LicenseRisk, error)
173+
DeleteByComponentPurl(assetID uuid.UUID, assetVersionName string, purl packageurl.PackageURL) error
174+
ListByScanner(assetVersionName string, assetID uuid.UUID, scannerID string) ([]models.LicenseRisk, error)
175+
ApplyAndSave(tx DB, licenseRisk *models.LicenseRisk, vulnEvent *models.VulnEvent) error
176+
}
177+
167178
type InTotoLinkRepository interface {
168179
common.Repository[uuid.UUID, models.InTotoLink, DB]
169180
FindByAssetAndSupplyChainID(assetID uuid.UUID, supplyChainID string) ([]models.InTotoLink, error)
@@ -304,13 +315,6 @@ type VulnRepository interface {
304315
ApplyAndSave(tx DB, dependencyVuln models.Vuln, vulnEvent *models.VulnEvent) error
305316
}
306317

307-
type LicenseOverwriteRepository interface {
308-
common.Repository[string, models.LicenseOverwrite, DB]
309-
GetAllOverwritesForOrganization(orgID uuid.UUID) ([]models.LicenseOverwrite, error)
310-
MaybeGetOverwriteForComponent(orgID uuid.UUID, pURL packageurl.PackageURL) (models.LicenseOverwrite, error)
311-
DeleteByComponentPurlAndOrgID(orgID uuid.UUID, purl string) error
312-
}
313-
314318
type ExternalUserRepository interface {
315319
Save(db DB, user *models.ExternalUser) error
316320
GetDB(tx DB) DB
@@ -381,11 +385,17 @@ type ComponentProjectRepository interface {
381385
}
382386

383387
type ComponentService interface {
384-
GetAndSaveLicenseInformation(assetVersionName string, assetID uuid.UUID, scannerID string) ([]models.Component, error)
388+
GetAndSaveLicenseInformation(assetVersion models.AssetVersion, scannerID string) ([]models.Component, error)
385389
RefreshComponentProjectInformation(project models.ComponentProject)
386390
GetLicense(component models.Component) (models.Component, error)
387391
}
388392

393+
type LicenseRiskService interface {
394+
FindLicenseRisksInComponents(assetVersion models.AssetVersion, components []models.Component, scannerID string) error
395+
UpdateLicenseRiskState(tx DB, userID string, licenseRisk *models.LicenseRisk, statusType string, justification string, mechanicalJustification models.MechanicalJustificationType) (models.VulnEvent, error)
396+
MakeFinalLicenseDecision(vulnID string, finalLicense string, userID string) error
397+
}
398+
389399
type AccessControl interface {
390400
HasAccess(subject string) (bool, error) // return error if couldnt be checked due to unauthorized access or other issues
391401

internal/core/component/component_controller.go

Lines changed: 8 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -5,16 +5,16 @@ import (
55
)
66

77
type httpController struct {
8-
componentRepository core.ComponentRepository
9-
assetVersionRepository core.AssetVersionRepository
10-
licenseOverwriteRepository core.LicenseOverwriteRepository
8+
componentRepository core.ComponentRepository
9+
assetVersionRepository core.AssetVersionRepository
10+
licenseRiskRepository core.LicenseRiskRepository
1111
}
1212

13-
func NewHTTPController(componentRepository core.ComponentRepository, assetVersionRepository core.AssetVersionRepository, licenseOverwriteRepository core.LicenseOverwriteRepository) *httpController {
13+
func NewHTTPController(componentRepository core.ComponentRepository, assetVersionRepository core.AssetVersionRepository, licenseOverwriteRepository core.LicenseRiskRepository) *httpController {
1414
return &httpController{
15-
componentRepository: componentRepository,
16-
assetVersionRepository: assetVersionRepository,
17-
licenseOverwriteRepository: licenseOverwriteRepository,
15+
componentRepository: componentRepository,
16+
assetVersionRepository: assetVersionRepository,
17+
licenseRiskRepository: licenseOverwriteRepository,
1818
}
1919
}
2020

@@ -74,9 +74,7 @@ func (httpController httpController) ListPaged(ctx core.Context) error {
7474
search := ctx.QueryParam("search")
7575
sort := core.GetSortQuery(ctx)
7676

77-
orgID := core.GetOrg(ctx).ID
78-
79-
overwrittenLicense, err := httpController.licenseOverwriteRepository.GetAllOverwritesForOrganization(orgID)
77+
overwrittenLicense, err := httpController.licenseRiskRepository.GetAllOverwrittenLicensesForAssetVersion(assetVersion.AssetID, assetVersion.Name)
8078
if err != nil {
8179
return err
8280
}

0 commit comments

Comments
 (0)