diff --git a/cmd/devguard/main.go b/cmd/devguard/main.go
index 2c091ade5..ef1653942 100644
--- a/cmd/devguard/main.go
+++ b/cmd/devguard/main.go
@@ -155,6 +155,7 @@ func main() {
fx.Invoke(func(FalsePositiveRuleRouter router.VEXRuleRouter) {}),
fx.Invoke(func(ExternalReferenceRouter router.ExternalReferenceRouter) {}),
fx.Invoke(func(CrowdsourcedVexingRouter router.CrowdsourcedVexingRouter) {}),
+ fx.Invoke(func(AdvisoryRouter router.AdvisoryRouter) {}),
fx.Invoke(func(lc fx.Lifecycle, encryptionService shared.DBEncryptionService) {
lc.Append(fx.Hook{
OnStart: func(ctx context.Context) error {
diff --git a/controllers/advisory_controller.go b/controllers/advisory_controller.go
new file mode 100644
index 000000000..6611147a7
--- /dev/null
+++ b/controllers/advisory_controller.go
@@ -0,0 +1,51 @@
+// Copyright (C) 2023 Tim Bastin, l3montree GmbH
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Affero General Public License as
+// published by the Free Software Foundation, either version 3 of the
+// License, or (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Affero General Public License for more details.
+//
+// You should have received a copy of the GNU Affero General Public License
+// along with this program. If not, see .
+
+package controllers
+
+import (
+ "github.com/l3montree-dev/devguard/dtos"
+ "github.com/l3montree-dev/devguard/shared"
+ "github.com/labstack/echo/v4"
+ "golang.org/x/exp/slog"
+)
+
+type AdvisoryController struct {
+ advisoryService shared.AdvisoryService
+}
+
+func NewAdvisoryController(advisoryService shared.AdvisoryService) *AdvisoryController {
+ return &AdvisoryController{
+ advisoryService: advisoryService,
+ }
+}
+
+func (controller *AdvisoryController) CreateName(ctx shared.Context) error {
+ slog.Info("test")
+ var req dtos.AdvisoryCreateName
+ if err := ctx.Bind(&req); err != nil {
+ return echo.NewHTTPError(400, "unable to process request").WithInternal(err)
+ }
+
+ newName := req.Name
+
+ err := controller.advisoryService.CreateName(ctx.Request().Context(), newName)
+
+ if err != nil {
+ return echo.NewHTTPError(409, "could not set name").WithInternal(err)
+ }
+
+ return ctx.JSON(200, newName)
+}
diff --git a/controllers/providers.go b/controllers/providers.go
index b7d5696cf..df0b40cf5 100644
--- a/controllers/providers.go
+++ b/controllers/providers.go
@@ -122,4 +122,6 @@ var ControllerModule = fx.Options(
//Crowdsourced Vexing
fx.Provide(NewCrowdsourcedVexingController),
+
+ fx.Provide(NewAdvisoryController),
)
diff --git a/database/migrations/20260623101120_add_advisory_table.up.sql b/database/migrations/20260623101120_add_advisory_table.up.sql
new file mode 100644
index 000000000..8d361fea0
--- /dev/null
+++ b/database/migrations/20260623101120_add_advisory_table.up.sql
@@ -0,0 +1,6 @@
+CREATE TABLE public.advisories (
+ id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
+ created_at TIMESTAMP NOT NULL DEFAULT NOW(),
+ updated_at TIMESTAMP NOT NULL DEFAULT NOW(),
+ advisory_name TEXT NOT NULL
+)
\ No newline at end of file
diff --git a/database/models/advisory_model.go b/database/models/advisory_model.go
new file mode 100644
index 000000000..340021711
--- /dev/null
+++ b/database/models/advisory_model.go
@@ -0,0 +1,10 @@
+package models
+
+type Advisory struct {
+ Model
+ AdvisoryName string `json:"name" gorm:"type:text;column:advisory_name"`
+}
+
+func (m Advisory) TableName() string {
+ return "advisories"
+}
diff --git a/database/repositories/advisory_repository.go b/database/repositories/advisory_repository.go
new file mode 100644
index 000000000..62c448afd
--- /dev/null
+++ b/database/repositories/advisory_repository.go
@@ -0,0 +1,33 @@
+package repositories
+
+import (
+ "context"
+
+ "github.com/google/uuid"
+ "github.com/l3montree-dev/devguard/database/models"
+ "github.com/l3montree-dev/devguard/shared"
+ "github.com/l3montree-dev/devguard/utils"
+ "gorm.io/gorm"
+)
+
+type AdvisoryRepository struct {
+ db *gorm.DB
+ utils.Repository[uuid.UUID, models.Advisory, *gorm.DB]
+}
+
+func NewAdvisoryRepository(db *gorm.DB) *AdvisoryRepository {
+ return &AdvisoryRepository{
+ db: db,
+ Repository: newGormRepository[uuid.UUID, models.Advisory](db),
+ }
+}
+
+var _ shared.AdvisoryRepository = (*AdvisoryRepository)(nil)
+
+func (advisoryRepository *AdvisoryRepository) CreateName(ctx context.Context, tx *gorm.DB, name string) error {
+ err := advisoryRepository.GetDB(ctx, tx).Create(&models.Advisory{AdvisoryName: name}).Error
+ if err != nil {
+ return err
+ }
+ return nil
+}
diff --git a/database/repositories/providers.go b/database/repositories/providers.go
index 84d540a31..fd9cc645c 100644
--- a/database/repositories/providers.go
+++ b/database/repositories/providers.go
@@ -59,4 +59,5 @@ var Module = fx.Options(
fx.Provide(fx.Annotate(NewTrustedEntityRepository, fx.As(new(shared.TrustedEntityRepository)))),
fx.Provide(fx.Annotate(NewDependencyProxyRepository, fx.As(new(shared.DependencyProxySecretRepository)))),
fx.Provide(fx.Annotate(NewAdminRepository, fx.As(new(shared.AdminRepository)))),
+ fx.Provide(fx.Annotate(NewAdvisoryRepository, fx.As(new(shared.AdvisoryRepository)))),
)
diff --git a/dtos/advisory_dto.go b/dtos/advisory_dto.go
new file mode 100644
index 000000000..7242f601f
--- /dev/null
+++ b/dtos/advisory_dto.go
@@ -0,0 +1,24 @@
+// Copyright (C) 2023 Tim Bastin, l3montree GmbH
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Affero General Public License as
+// published by the Free Software Foundation, either version 3 of the
+// License, or (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Affero General Public License for more details.
+//
+// You should have received a copy of the GNU Affero General Public License
+// along with this program. If not, see .
+
+package dtos
+
+type AdvisoryCreateName struct {
+ Name string `json:"name" validate:"required"`
+}
+
+type AdvisoryDTO struct {
+ Name string `json:"name"`
+}
diff --git a/router/advisory_router.go b/router/advisory_router.go
new file mode 100644
index 000000000..e8efd528e
--- /dev/null
+++ b/router/advisory_router.go
@@ -0,0 +1,38 @@
+// Copyright (C) 2024 Tim Bastin, l3montree GmbH
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Affero General Public License as
+// published by the Free Software Foundation, either version 3 of the
+// License, or (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Affero General Public License for more details.
+//
+// You should have received a copy of the GNU Affero General Public License
+// along with this program. If not, see .
+
+package router
+
+import (
+ "github.com/l3montree-dev/devguard/controllers"
+ "github.com/l3montree-dev/devguard/shared"
+ "github.com/labstack/echo/v4"
+)
+
+type AdvisoryRouter struct {
+ *echo.Group
+}
+
+func NewAdvisoryRouter(
+ assetRepository shared.AssetRepository,
+ assetVersionGroup AssetVersionRouter,
+ advisoryController *controllers.AdvisoryController,
+) AdvisoryRouter {
+ advisoryRouter := assetVersionGroup.Group.Group("/advisory")
+ advisoryRouter.POST("/submit/", advisoryController.CreateName)
+ //advisoryRouter.GET("/", advisoryController.ReadName)
+
+ return AdvisoryRouter{Group: advisoryRouter}
+}
diff --git a/router/providers.go b/router/providers.go
index 7d5056903..d0624d0b7 100644
--- a/router/providers.go
+++ b/router/providers.go
@@ -22,4 +22,5 @@ var RouterModule = fx.Options(
fx.Provide(NewVEXRuleRouter),
fx.Provide(NewExternalReferenceRouter),
fx.Provide(NewCrowdsourcedVexingRouter),
+ fx.Provide(NewAdvisoryRouter),
)
diff --git a/services/advisory_service.go b/services/advisory_service.go
new file mode 100644
index 000000000..d77179218
--- /dev/null
+++ b/services/advisory_service.go
@@ -0,0 +1,23 @@
+package services
+
+import (
+ "context"
+
+ "github.com/l3montree-dev/devguard/shared"
+)
+
+type AdvisoryService struct {
+ advisoryRepository shared.AdvisoryRepository
+}
+
+func NewAdvisoryService(advisoryRepository shared.AdvisoryRepository) *AdvisoryService {
+ return &AdvisoryService{
+ advisoryRepository: advisoryRepository,
+ }
+}
+
+var _ shared.AdvisoryService = (*AdvisoryService)(nil)
+
+func (s *AdvisoryService) CreateName(ctx context.Context, name string) error {
+ return s.advisoryRepository.CreateName(ctx, nil, name)
+}
diff --git a/services/providers.go b/services/providers.go
index 54683f373..6ea5b6792 100644
--- a/services/providers.go
+++ b/services/providers.go
@@ -39,4 +39,5 @@ var ServiceModule = fx.Options(
fx.Provide(fx.Annotate(NewAdminService, fx.As(new(shared.AdminService)))),
fx.Provide(fx.Annotate(NewCrowdsourcedVexingService, fx.As(new(shared.CrowdSourcedVexingService)))),
fx.Provide(fx.Annotate(NewDBEncryptionService, fx.As(new(shared.DBEncryptionService)))),
+ fx.Provide(fx.Annotate(NewAdvisoryService, fx.As(new(shared.AdvisoryService)))),
)
diff --git a/shared/common_interfaces.go b/shared/common_interfaces.go
index c6ff223a3..ed2ade03b 100644
--- a/shared/common_interfaces.go
+++ b/shared/common_interfaces.go
@@ -761,6 +761,14 @@ type RBACProvider interface {
GetAllUsers() ([]string, error)
}
+type AdvisoryService interface {
+ CreateName(ctx context.Context, name string) error
+}
+
+type AdvisoryRepository interface {
+ CreateName(ctx context.Context, tx DB, name string) error
+}
+
type RBACMiddleware = func(obj Object, act Action) echo.MiddlewareFunc
type Role string