Skip to content

Commit e696b40

Browse files
committed
Merge branch 'main' of github.com:l3montree-dev/flawfix
2 parents de77a72 + cf3f6ee commit e696b40

20 files changed

Lines changed: 960 additions & 73 deletions

.github/workflows/devguard-scanner.yaml

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,12 @@ on:
66
workflow_dispatch:
77
push:
88

9+
permissions:
10+
contents: read
11+
actions: read
12+
security-events: write
13+
packages: write
14+
915

1016
jobs:
1117
golangci:
@@ -57,7 +63,7 @@ jobs:
5763
fail-on-cvss: high
5864
web-ui: https://main.devguard.org
5965
should-deploy: ${{ github.ref == 'refs/heads/main' || startsWith(github.ref, 'refs/tags/') }}
60-
continue-on-open-code-risk: false
66+
continue-on-open-code-risk: true
6167
secrets:
6268
devguard-token: ${{ secrets.DEVGUARD_TOKEN }}
6369
build-args: "--context=. --dockerfile=Dockerfile --build-arg GITHUB_REF_NAME=$GITHUB_REF_NAME"
@@ -129,4 +135,4 @@ jobs:
129135
api-url: https://api.main.devguard.org
130136
artifact-name: "scanner"
131137
secrets:
132-
devguard-token: ${{ secrets.DEVGUARD_TOKEN }}
138+
devguard-token: ${{ secrets.DEVGUARD_TOKEN }}

.github/workflows/postgresql.yaml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,10 @@ on:
55
tags:
66
- '*'
77

8+
permissions:
9+
contents: read
10+
packages: write
11+
812
# There is a single job in this workflow. It's configured to run on the latest available version of Ubuntu.
913
jobs:
1014
# Docker image build job

.github/workflows/vulndb.yaml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,10 @@ on:
55
schedule:
66
- cron: '0 */6 * * *' # every hour
77

8+
permissions:
9+
contents: read
10+
packages: write
11+
812
env:
913
POSTGRES_DB: devguard
1014
POSTGRES_USER: devguard

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -105,7 +105,7 @@ DevGuard uses [golang-migrate](https://github.com/golang-migrate/migrate) for da
105105
### How Database Migrations Work
106106

107107
1. **Automatic Migration**: By default, migrations run automatically when the application starts
108-
2. **Environment Control**: Set `AUTO_MIGRATE=false` to disable automatic migrations
108+
2. **Environment Control**: Set `DISABLE_AUTOMIGRATE=true` to disable automatic migrations
109109
3. **Embedded Migrations**: Migration files are embedded in the binary for easy deployment
110110
4. **Idempotent**: Migrations can be run multiple times safely
111111

cmd/devguard-cli/commands/vulndb.go

Lines changed: 37 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,17 @@
11
package commands
22

33
import (
4+
"errors"
45
"log/slog"
6+
"os"
57
"regexp"
68
"strings"
79
"time"
810

911
"github.com/l3montree-dev/devguard/internal/core"
1012
"github.com/l3montree-dev/devguard/internal/core/vulndb"
13+
"github.com/l3montree-dev/devguard/internal/database"
14+
"github.com/l3montree-dev/devguard/internal/database/models"
1115
"github.com/l3montree-dev/devguard/internal/database/repositories"
1216
"github.com/spf13/cobra"
1317
)
@@ -51,19 +55,41 @@ func isValidCVE(cveID string) bool {
5155
return r.MatchString(cveID)
5256
}
5357

58+
func migrateDB(db core.DB) {
59+
// Run database migrations using the existing database connection
60+
disableAutoMigrate := os.Getenv("DISABLE_AUTOMIGRATE")
61+
if disableAutoMigrate != "true" {
62+
slog.Info("running database migrations...")
63+
if err := database.RunMigrationsWithDB(db); err != nil {
64+
slog.Error("failed to run database migrations", "error", err)
65+
panic(errors.New("Failed to run database migrations"))
66+
}
67+
68+
// Run hash migrations if needed (when algorithm version changes)
69+
if err := models.RunHashMigrationsIfNeeded(db); err != nil {
70+
slog.Error("failed to run hash migrations", "error", err)
71+
panic(errors.New("Failed to run hash migrations"))
72+
}
73+
} else {
74+
slog.Info("automatic migrations disabled via DISABLE_AUTOMIGRATE=true")
75+
}
76+
}
77+
5478
func newImportCVECommand() *cobra.Command {
5579
importCmd := &cobra.Command{
5680
Use: "import-cve",
5781
Short: "Will import the vulnerability database",
5882
Args: cobra.ExactArgs(1),
5983
Run: func(cmd *cobra.Command, args []string) {
6084
core.LoadConfig() // nolint
61-
database, err := core.DatabaseFactory()
85+
db, err := core.DatabaseFactory()
6286
if err != nil {
6387
slog.Error("could not connect to database", "err", err)
6488
return
6589
}
6690

91+
migrateDB(db)
92+
6793
cveID := args[0]
6894
cveID = strings.TrimSpace(strings.ToUpper(cveID))
6995
// check if first argument is valid cve
@@ -72,9 +98,9 @@ func newImportCVECommand() *cobra.Command {
7298
return
7399
}
74100

75-
cveRepository := repositories.NewCVERepository(database)
101+
cveRepository := repositories.NewCVERepository(db)
76102
nvdService := vulndb.NewNVDService(cveRepository)
77-
osvService := vulndb.NewOSVService(repositories.NewAffectedComponentRepository(database))
103+
osvService := vulndb.NewOSVService(repositories.NewAffectedComponentRepository(db))
78104

79105
cve, err := nvdService.ImportCVE(cveID)
80106

@@ -155,27 +181,29 @@ func newSyncCommand() *cobra.Command {
155181

156182
core.LoadConfig() // nolint
157183

158-
database, err := core.DatabaseFactory()
184+
db, err := core.DatabaseFactory()
159185
if err != nil {
160186
slog.Error("could not connect to database", "err", err)
161187
return
162188
}
163189

190+
migrateDB(db)
191+
164192
databasesToSync, _ := cmd.Flags().GetStringArray("databases")
165193

166-
cveRepository := repositories.NewCVERepository(database)
167-
cweRepository := repositories.NewCWERepository(database)
168-
affectedCmpRepository := repositories.NewAffectedComponentRepository(database)
194+
cveRepository := repositories.NewCVERepository(db)
195+
cweRepository := repositories.NewCWERepository(db)
196+
affectedCmpRepository := repositories.NewAffectedComponentRepository(db)
169197
nvdService := vulndb.NewNVDService(cveRepository)
170198
mitreService := vulndb.NewMitreService(cweRepository)
171199
epssService := vulndb.NewEPSSService(nvdService, cveRepository)
172200
osvService := vulndb.NewOSVService(affectedCmpRepository)
173201
// cvelistService := vulndb.NewCVEListService(cveRepository)
174202
debianSecurityTracker := vulndb.NewDebianSecurityTracker(affectedCmpRepository)
175203

176-
expoitDBService := vulndb.NewExploitDBService(nvdService, repositories.NewExploitRepository(database))
204+
expoitDBService := vulndb.NewExploitDBService(nvdService, repositories.NewExploitRepository(db))
177205

178-
githubExploitDBService := vulndb.NewGithubExploitDBService(repositories.NewExploitRepository(database))
206+
githubExploitDBService := vulndb.NewGithubExploitDBService(repositories.NewExploitRepository(db))
179207

180208
if emptyOrContains(databasesToSync, "cwe") {
181209
now := time.Now()

cmd/devguard/api/api.go

Lines changed: 24 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ import (
2020
"os"
2121
"sort"
2222
"strings"
23+
"sync"
2324
"time"
2425

2526
"github.com/prometheus/client_golang/prometheus/promhttp"
@@ -254,16 +255,20 @@ func neededScope(neededScopes []string) core.MiddlewareFunc {
254255
}
255256

256257
func externalEntityProviderOrgSyncMiddleware(externalEntityProviderService core.ExternalEntityProviderService) core.MiddlewareFunc {
257-
limiter := map[string]time.Time{}
258+
limiter := &sync.Map{}
258259
return func(next echo.HandlerFunc) echo.HandlerFunc {
259260
return func(ctx core.Context) error {
260261

261262
key := core.GetSession(ctx).GetUserID()
262-
if _, ok := limiter[key]; !ok || time.Now().After(limiter[key]) {
263+
now := time.Now()
264+
265+
if value, ok := limiter.Load(key); !ok || now.After(value.(time.Time)) {
263266
slog.Info("syncing external entity provider orgs", "userID", key)
264-
limiter[key] = time.Now().Add(15 * time.Minute)
267+
limiter.Store(key, now.Add(15*time.Minute))
268+
// Create a goroutine-safe context to avoid using the request context
269+
safeCtx := core.GoroutineSafeContext(ctx)
265270
go func() {
266-
if _, err := externalEntityProviderService.SyncOrgs(ctx); err != nil {
271+
if _, err := externalEntityProviderService.SyncOrgs(safeCtx); err != nil {
267272
slog.Error("could not sync external entity provider orgs", "err", err, "userID", key)
268273
}
269274
}()
@@ -274,24 +279,32 @@ func externalEntityProviderOrgSyncMiddleware(externalEntityProviderService core.
274279
}
275280

276281
func externalEntityProviderRefreshMiddleware(externalEntityProviderService core.ExternalEntityProviderService) core.MiddlewareFunc {
277-
limiter := map[string]time.Time{}
282+
limiter := &sync.Map{}
278283

279284
return func(next echo.HandlerFunc) echo.HandlerFunc {
280285
// get the current org
281286
return func(ctx core.Context) error {
282287
org := core.GetOrg(ctx)
283288

284289
if org.IsExternalEntity() {
285-
// check if we are allowed to refresh the external entity provider projects
286-
if time.Now().After(limiter[org.GetID().String()+"/"+core.GetSession(ctx).GetUserID()]) {
287-
limiter[org.GetID().String()+"/"+core.GetSession(ctx).GetUserID()] = time.Now().Add(15 * time.Minute)
290+
key := org.GetID().String() + "/" + core.GetSession(ctx).GetUserID()
291+
now := time.Now()
292+
293+
// Check if we are allowed to refresh the external entity provider projects
294+
if value, ok := limiter.Load(key); !ok || now.After(value.(time.Time)) {
295+
limiter.Store(key, now.Add(15*time.Minute))
296+
297+
// Create a goroutine-safe context and capture the values we need
298+
safeCtx := core.GoroutineSafeContext(ctx)
299+
userID := core.GetSession(ctx).GetUserID()
300+
orgID := org.GetID()
288301

289302
go func() {
290-
err := externalEntityProviderService.RefreshExternalEntityProviderProjects(ctx, org, core.GetSession(ctx).GetUserID())
303+
err := externalEntityProviderService.RefreshExternalEntityProviderProjects(safeCtx, org, userID)
291304
if err != nil {
292-
slog.Error("could not refresh external entity provider projects", "err", err, "orgID", org.GetID(), "userID", core.GetSession(ctx).GetUserID())
305+
slog.Error("could not refresh external entity provider projects", "err", err, "orgID", orgID, "userID", userID)
293306
} else {
294-
slog.Info("refreshed external entity provider projects", "orgID", org.GetID(), "userID", core.GetSession(ctx).GetUserID())
307+
slog.Info("refreshed external entity provider projects", "orgID", orgID, "userID", userID)
295308
}
296309
}()
297310
}

cmd/devguard/main.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -72,8 +72,8 @@ func main() {
7272
}
7373

7474
// Run database migrations using the existing database connection
75-
autoMigrate := os.Getenv("AUTO_MIGRATE")
76-
if autoMigrate == "" || autoMigrate == "true" {
75+
disableAutoMigrate := os.Getenv("DISABLE_AUTOMIGRATE")
76+
if disableAutoMigrate != "true" {
7777
slog.Info("running database migrations...")
7878
if err := database.RunMigrationsWithDB(db); err != nil {
7979
slog.Error("failed to run database migrations", "error", err)
@@ -86,7 +86,7 @@ func main() {
8686
panic(errors.New("Failed to run hash migrations"))
8787
}
8888
} else {
89-
slog.Info("automatic migrations disabled via AUTO_MIGRATE=false")
89+
slog.Info("automatic migrations disabled via DISABLE_AUTOMIGRATE=true")
9090
}
9191

9292
daemon.Start(db)

go.mod

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
module github.com/l3montree-dev/devguard
22

3-
go 1.24.0
3+
go 1.24.5
44

55
require (
66
github.com/CycloneDX/cyclonedx-go v0.9.2

internal/core/common_interfaces.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -223,8 +223,8 @@ type InvitationRepository interface {
223223

224224
type ExternalEntityProviderService interface {
225225
RefreshExternalEntityProviderProjects(ctx Context, org models.Org, user string) error
226-
TriggerOrgSync(c echo.Context) error
227-
SyncOrgs(c echo.Context) ([]*models.Org, error)
226+
TriggerOrgSync(c Context) error
227+
SyncOrgs(c Context) ([]*models.Org, error)
228228
}
229229

230230
type ProjectService interface {

internal/core/context_utils.go

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,11 +17,13 @@ package core
1717
import (
1818
"context"
1919
"fmt"
20+
"net/http/httptest"
2021
"regexp"
2122
"strconv"
2223
"strings"
2324

2425
"github.com/l3montree-dev/devguard/internal/database/models"
26+
"github.com/l3montree-dev/devguard/internal/echohttp"
2527
"github.com/l3montree-dev/devguard/internal/utils"
2628

2729
"github.com/ory/client-go"
@@ -592,3 +594,61 @@ func GetBadgeSVG(label string, values []BadgeValues) string {
592594

593595
return sb.String()
594596
}
597+
598+
func GoroutineSafeContext(c Context) Context {
599+
// create a new context - with only the values
600+
ctx := echohttp.E.NewContext(nil, httptest.NewRecorder())
601+
602+
// copy all values from the original context that might be needed in goroutines
603+
if thirdParty, ok := c.Get("thirdPartyIntegration").(IntegrationAggregate); ok {
604+
ctx.Set("thirdPartyIntegration", thirdParty)
605+
}
606+
607+
if session, ok := c.Get("session").(AuthSession); ok {
608+
ctx.Set("session", session)
609+
}
610+
611+
if org, ok := c.Get("organization").(models.Org); ok {
612+
ctx.Set("organization", org)
613+
}
614+
615+
if project, ok := c.Get("project").(models.Project); ok {
616+
ctx.Set("project", project)
617+
}
618+
619+
if asset, ok := c.Get("asset").(models.Asset); ok {
620+
ctx.Set("asset", asset)
621+
}
622+
623+
if assetVersion, ok := c.Get("assetVersion").(models.AssetVersion); ok {
624+
ctx.Set("assetVersion", assetVersion)
625+
}
626+
627+
if rbac, ok := c.Get("rbac").(AccessControl); ok {
628+
ctx.Set("rbac", rbac)
629+
}
630+
631+
if authClient, ok := c.Get("authAdminClient").(AdminClient); ok {
632+
ctx.Set("authAdminClient", authClient)
633+
}
634+
635+
// Copy string values that might be needed
636+
if orgSlug, ok := c.Get("orgSlug").(string); ok {
637+
ctx.Set("orgSlug", orgSlug)
638+
}
639+
640+
if projectSlug, ok := c.Get("projectSlug").(string); ok {
641+
ctx.Set("projectSlug", projectSlug)
642+
}
643+
644+
if assetSlug, ok := c.Get("assetSlug").(string); ok {
645+
ctx.Set("assetSlug", assetSlug)
646+
}
647+
648+
// Copy public request flag
649+
if c.Get("publicRequest") != nil {
650+
ctx.Set("publicRequest", true)
651+
}
652+
653+
return ctx
654+
}

0 commit comments

Comments
 (0)