Skip to content
Draft
Show file tree
Hide file tree
Changes from all 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
33 changes: 33 additions & 0 deletions controllers/integration_controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,9 +21,11 @@ import (
"github.com/l3montree-dev/devguard/integrations/githubint"
"github.com/l3montree-dev/devguard/integrations/gitlabint"
"github.com/l3montree-dev/devguard/integrations/jiraint"
"github.com/l3montree-dev/devguard/integrations/trivyoperatorint"
"github.com/l3montree-dev/devguard/shared"
)


type IntegrationController struct {
gitlabOauth2Integration map[string]*gitlabint.GitlabOauth2Config
}
Expand Down Expand Up @@ -192,3 +194,34 @@ func (c *IntegrationController) DeleteJiraAccessToken(ctx shared.Context) error

return nil
}

func (c *IntegrationController) HandleTrivyOperatorWebhook(ctx shared.Context) error {
thirdPartyIntegration := shared.GetThirdPartyIntegration(ctx)
t := thirdPartyIntegration.GetIntegration(shared.TrivyOperatorIntegrationID)
if t == nil {
return ctx.JSON(404, "Trivy Operator integration not enabled")
}
return t.(*trivyoperatorint.TrivyOperatorIntegration).HandleWebhook(ctx)
}

func (c *IntegrationController) CreateTrivyOperatorIntegration(ctx shared.Context) error {
thirdPartyIntegration := shared.GetThirdPartyIntegration(ctx)
t := thirdPartyIntegration.GetIntegration(shared.TrivyOperatorIntegrationID)
if t == nil {
return ctx.JSON(404, "Trivy Operator integration not enabled")
}
return t.(*trivyoperatorint.TrivyOperatorIntegration).Create(ctx)
}

func (c *IntegrationController) DeleteTrivyOperatorIntegration(ctx shared.Context) error {
thirdPartyIntegration := shared.GetThirdPartyIntegration(ctx)
t := thirdPartyIntegration.GetIntegration(shared.TrivyOperatorIntegrationID)
if t == nil {
return ctx.JSON(404, "Trivy Operator integration not enabled")
}
if err := t.(*trivyoperatorint.TrivyOperatorIntegration).Delete(ctx); err != nil {
slog.Error("could not delete trivy operator integration", "err", err)
return err
}
return nil
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
CREATE TABLE trivy_operator_integrations (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
updated_at TIMESTAMPTZ NOT NULL DEFAULT now(),
deleted_at TIMESTAMPTZ,
name VARCHAR(255) NOT NULL,
cluster_id VARCHAR(255) NOT NULL,
secret TEXT NOT NULL,
org_id UUID NOT NULL REFERENCES organizations(id) ON DELETE CASCADE,
CONSTRAINT uq_trivy_operator_cluster_org UNIQUE (cluster_id, org_id)
);
2 changes: 2 additions & 0 deletions database/models/org_model.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@ type Org struct {

JiraIntegrations []JiraIntegration `json:"jiraIntegrations" gorm:"foreignKey:OrgID;"`

TrivyOperatorIntegrations []TrivyOperatorIntegration `json:"trivyOperatorIntegrations" gorm:"foreignKey:OrgID;"`

SharesVulnInformation bool `json:"sharesVulnInformation" gorm:"default:false"`
Webhooks []WebhookIntegration `json:"webhooks" gorm:"foreignKey:OrgID;"`

Expand Down
20 changes: 20 additions & 0 deletions database/models/trivy_operator_model.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
// Copyright (C) 2026 l3montree GmbH
// SPDX-License-Identifier: AGPL-3.0-or-later

package models

import "github.com/google/uuid"

type TrivyOperatorIntegration struct {
Model

Name string `json:"name" gorm:"type:varchar(255);not null"`
ClusterID string `json:"clusterId" gorm:"column:cluster_id;type:varchar(255);not null;uniqueIndex:uq_trivy_operator_cluster_org"`
Secret string `json:"secret" gorm:"type:text;not null"`
Org Org `json:"-" gorm:"foreignKey:OrgID;constraint:OnDelete:CASCADE;"`
OrgID uuid.UUID `json:"orgId" gorm:"column:org_id;uniqueIndex:uq_trivy_operator_cluster_org"`
}

func (TrivyOperatorIntegration) TableName() string {
return "trivy_operator_integrations"
}
2 changes: 1 addition & 1 deletion database/repositories/org_repository.go
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ func (g *orgRepository) Save(ctx context.Context, tx *gorm.DB, org *models.Org)

func (g *orgRepository) ReadBySlug(ctx context.Context, tx *gorm.DB, slug string) (models.Org, error) {
var t models.Org
err := g.GetDB(ctx, tx).Model(models.Org{}).Preload("GithubAppInstallations").Preload("JiraIntegrations").Preload("GitLabIntegrations").Preload("Webhooks", "project_id IS NULL").Where("slug = ?", slug).First(&t).Error
err := g.GetDB(ctx, tx).Model(models.Org{}).Preload("GithubAppInstallations").Preload("JiraIntegrations").Preload("GitLabIntegrations").Preload("TrivyOperatorIntegrations").Preload("Webhooks", "project_id IS NULL").Where("slug = ?", slug).First(&t).Error
return t, err
}

Expand Down
1 change: 1 addition & 0 deletions database/repositories/providers.go
Original file line number Diff line number Diff line change
Expand Up @@ -58,4 +58,5 @@ var Module = fx.Options(
fx.Provide(fx.Annotate(NewExternalReferenceRepository, fx.As(new(shared.ExternalReferenceRepository)))),
fx.Provide(fx.Annotate(NewTrustedEntityRepository, fx.As(new(shared.TrustedEntityRepository)))),
fx.Provide(fx.Annotate(NewDependencyProxyRepository, fx.As(new(shared.DependencyProxySecretRepository)))),
fx.Provide(fx.Annotate(NewTrivyOperatorIntegrationRepository, fx.As(new(shared.TrivyOperatorIntegrationRepository)))),
)
41 changes: 41 additions & 0 deletions database/repositories/trivy_operator_integration_repository.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
// Copyright (C) 2026 l3montree GmbH
// SPDX-License-Identifier: AGPL-3.0-or-later

package repositories

import (
"context"

"github.com/google/uuid"
"github.com/l3montree-dev/devguard/database/models"
"github.com/l3montree-dev/devguard/utils"
"gorm.io/gorm"
)

type trivyOperatorIntegrationRepository struct {
db *gorm.DB
utils.Repository[uuid.UUID, models.TrivyOperatorIntegration, *gorm.DB]
}

func NewTrivyOperatorIntegrationRepository(db *gorm.DB) *trivyOperatorIntegrationRepository {
return &trivyOperatorIntegrationRepository{
db: db,
Repository: newGormRepository[uuid.UUID, models.TrivyOperatorIntegration](db),
}
}

func (r *trivyOperatorIntegrationRepository) FindByOrganizationID(ctx context.Context, tx *gorm.DB, orgID uuid.UUID) ([]models.TrivyOperatorIntegration, error) {
var integrations []models.TrivyOperatorIntegration
if err := r.GetDB(ctx, tx).Find(&integrations, "org_id = ?", orgID).Error; err != nil {
return nil, err
}
return integrations, nil
}

func (r *trivyOperatorIntegrationRepository) FindBySecret(ctx context.Context, tx *gorm.DB, secret string) (models.TrivyOperatorIntegration, error) {
var integration models.TrivyOperatorIntegration
if err := r.GetDB(ctx, tx).First(&integration, "secret = ?", secret).Error; err != nil {
return models.TrivyOperatorIntegration{}, err
}
return integration, nil
}
7 changes: 7 additions & 0 deletions dtos/integrations_obj.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,13 @@ type JiraIntegrationDTO struct {
UserEmail string `json:"userEmail"`
}

type TrivyOperatorIntegrationDTO struct {
ID string `json:"id"`
Name string `json:"name"`
ClusterID string `json:"clusterId"`
Secret string `json:"secret"`
}

type WebhookIntegrationDTO struct {
ID string `json:"id"`
Name string `json:"name"`
Expand Down
2 changes: 2 additions & 0 deletions dtos/org_dto.go
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,8 @@ type OrgDTO struct {

JiraIntegrations []JiraIntegrationDTO `json:"jiraIntegrations" gorm:"foreignKey:OrgID;"`

TrivyOperatorIntegrations []TrivyOperatorIntegrationDTO `json:"trivyOperatorIntegrations"`

SharesVulnInformation bool `json:"sharesVulnInformation"`
IsPublic bool `json:"isPublic" gorm:"default:false;"`
ConfigFiles map[string]any `json:"configFiles"`
Expand Down
19 changes: 16 additions & 3 deletions integrations/providers.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import (
"github.com/l3montree-dev/devguard/integrations/githubint"
"github.com/l3montree-dev/devguard/integrations/gitlabint"
"github.com/l3montree-dev/devguard/integrations/jiraint"
"github.com/l3montree-dev/devguard/integrations/trivyoperatorint"
"github.com/l3montree-dev/devguard/shared"
"go.uber.org/fx"
)
Expand All @@ -37,10 +38,22 @@ var Module = fx.Options(
// Jira Integration
fx.Provide(jiraint.NewJiraIntegration),

// Aggregated Third Party Integration
// Trivy Operator Integration (ScanService and AssetService injected via Invoke to break DI cycle)
fx.Provide(trivyoperatorint.NewTrivyOperatorIntegration),
fx.Invoke(func(t *trivyoperatorint.TrivyOperatorIntegration, s shared.ScanService, a shared.AssetService) {
t.SetScanService(s)
t.SetAssetService(a)
}),
fx.Provide(fx.Annotate(
func(externalUserRepository shared.ExternalUserRepository, gitlabIntegration *gitlabint.GitlabIntegration, githubIntegration *githubint.GithubIntegration, jiraIntegration *jiraint.JiraIntegration, webhookIntegration *controllers.WebhookController) shared.IntegrationAggregate {
return NewThirdPartyIntegrations(externalUserRepository, githubIntegration, jiraIntegration, gitlabIntegration, webhookIntegration)
func(
externalUserRepository shared.ExternalUserRepository,
gitlabIntegration *gitlabint.GitlabIntegration,
githubIntegration *githubint.GithubIntegration,
jiraIntegration *jiraint.JiraIntegration,
webhookIntegration *controllers.WebhookController,
trivyOperatorIntegration *trivyoperatorint.TrivyOperatorIntegration,
) shared.IntegrationAggregate {
return NewThirdPartyIntegrations(externalUserRepository, githubIntegration, jiraIntegration, gitlabIntegration, webhookIntegration, trivyOperatorIntegration)
},
fx.As(new(shared.IntegrationAggregate)),
)),
Expand Down
Loading
Loading