Conversation
…m context Signed-off-by: rafi <refaei.shikho@hotmail.com>
Signed-off-by: rafi <refaei.shikho@hotmail.com>
Signed-off-by: rafi <refaei.shikho@hotmail.com>
Signed-off-by: rafi <refaei.shikho@hotmail.com>
Signed-off-by: rafi <refaei.shikho@hotmail.com>
Signed-off-by: rafi <refaei.shikho@hotmail.com>
…nfiguration Signed-off-by: rafi <refaei.shikho@hotmail.com>
Signed-off-by: rafi <refaei.shikho@hotmail.com>
Signed-off-by: rafi <refaei.shikho@hotmail.com>
There was a problem hiding this comment.
Pull request overview
Adds secret-scoped “shared” dependency-proxy endpoints and introduces proxy configuration enforcement (deny/allow rules + minimum package age) backed by a new dependency_proxy_secrets table/service.
Changes:
- Add secret-backed dependency-proxy URLs and a new share router (
/api/v1/dependency-proxy/:secret/...) for npm/go/pypi. - Add config loading (
dependency-proxy-configs) plus rule-based blocking andminReleaseTimeenforcement in the dependency proxy controller. - Introduce dependency-proxy secret model/repository/service with a DB migration and DI wiring.
Reviewed changes
Copilot reviewed 20 out of 20 changed files in this pull request and generated 10 comments.
Show a summary per file
| File | Description |
|---|---|
| tests/dependency_proxy_controller_test.go | Updates controller constructor usage and adds tests for rule matching + proxy-prefix trimming. |
| shared/context_utils.go | Adds MaybeGetOrganization/Project/Asset helpers used to derive proxy secret scope. |
| shared/common_interfaces.go | Adds DependencyProxySecretRepository and DependencyProxySecretService interfaces. |
| services/providers.go | Registers the new dependency-proxy secret service with Fx. |
| services/dependency_proxy_secret_service.go | Implements secret lookup and GetOrCreate helpers for org/project/asset scopes. |
| router/share_dependency_proxy_router.go | New router exposing secret-scoped proxy endpoints. |
| router/providers.go | Wires the new share dependency proxy router. |
| router/project_router.go | Adds GET /dependency-proxy-urls/ to project scope. |
| router/org_router.go | Adds GET /dependency-proxy-urls/ to organization scope. |
| router/asset_router.go | Adds GET /dependency-proxy-urls/ to asset scope. |
| database/repositories/providers.go | Registers the new dependency-proxy secret repository with Fx. |
| database/repositories/dependency_proxy_secret_repository.go | Implements GetOrCreate, rotation, and secret lookup queries for proxy secrets. |
| database/models/dependency_proxy_secret_model.go | Adds the DependencyProxySecret model. |
| database/migrations/20260410163018_add-dependency-proxy.up.sql | Creates the dependency_proxy_secrets table. |
| database/migrations/20260410163018_add-dependency-proxy.down.sql | Drops the dependency_proxy_secrets table. |
| controllers/providers.go | Renames/provides dependency-proxy cache config provider. |
| controllers/dependency_proxy_controller.go | Adds configs/rules/min-age enforcement, secret-based config loading, and URL join fixes. |
| cmd/devguard/main.go | Invokes the new share dependency proxy router. |
| cmd/devguard-cli/commands/vulndb.go | Invokes the new share dependency proxy router in CLI migration runner. |
| .env.example | Documents DEPENDENCY_PROXY_BASE_URL. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Signed-off-by: rafi <refaei.shikho@hotmail.com>
…te path handling Signed-off-by: rafi <refaei.shikho@hotmail.com>
Signed-off-by: rafi <refaei.shikho@hotmail.com>
…dation checks for malicious packages across NPM, Go, and PyPI proxies Signed-off-by: rafi <refaei.shikho@hotmail.com>
Signed-off-by: rafi <refaei.shikho@hotmail.com>
…PM and PyPI requests Signed-off-by: rafi <refaei.shikho@hotmail.com>
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 23 out of 23 changed files in this pull request and generated 19 comments.
Comments suppressed due to low confidence (1)
controllers/dependency_proxy_controller.go:1144
- ParsePackageFromPath(PyPIProxy) trims the leading '/', but then checks strings.HasPrefix(path, "/packages/") which can never be true. This makes version extraction for /packages/* paths unreachable; check for "packages/" (or move the TrimPrefix logic).
path = strings.TrimPrefix(path, "/")
if after, ok := strings.CutPrefix(path, "simple/"); ok {
pkgName := after
pkgName = strings.TrimSuffix(pkgName, "/")
return pkgName, ""
} else if strings.HasPrefix(path, "/packages/") {
filename := filepath.Base(path)
// Try to extract package name and version from filename
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Signed-off-by: rafi <refaei.shikho@hotmail.com>
…Checker mock Signed-off-by: rafi <refaei.shikho@hotmail.com>
| fx.Invoke(func(ShareRouter router.ShareRouter) {}), | ||
| fx.Invoke(func(VulnDBRouter router.VulnDBRouter) {}), | ||
| fx.Invoke(func(dependencyProxyRouter router.DependencyProxyRouter) {}), | ||
| fx.Invoke(func(shareDependencyProxyRouter router.ShareDependencyProxyRouter) {}), |
There was a problem hiding this comment.
I dislike the name, since it has nothing todo with really sharing something. Can't we just put the routes inside the DependencyProxyRouter itself?
| assetRepository shared.AssetRepository | ||
| projectRepository shared.ProjectRepository | ||
| orgRepository shared.OrganizationRepository | ||
| dependencyProxyService shared.DependencyProxySecretService |
| // trimProxyPrefix strips the /api/v1/dependency-proxy/[secret/]<ecosystem> prefix from the path. | ||
| // The secret segment is optional to support routes with and without a secret. | ||
| func TrimProxyPrefix(path string, ecosystem ProxyType) string { | ||
| encodedPackage := regexp.MustCompile(`^/api/v1/dependency-proxy/(?:[^/]+/)?`+regexp.QuoteMeta(string(ecosystem))+`(?:/|$)`).ReplaceAllString(path, "") |
There was a problem hiding this comment.
Lets move the MustCompile outside the function call itself. I am afraid, that we did not test an ecosystem and have a runtime panic. I think we can just hard code the 3 different regexes.
| slog.Info("Removed malicious package from cache", "path", cachePath) | ||
| if hasExplicitVersion { | ||
|
|
||
| notAllowed, notAllowedReason := d.CheckNotAllowedPackage(ctx, NPMProxy, requestPath, configs) |
There was a problem hiding this comment.
We should add a log statement right here - same like in the malicious packages branch
|
|
||
| // Parse the JSON response to extract the version that would be installed | ||
| if resolvedVersion := d.ExtractNPMVersionFromMetadata(data); resolvedVersion != "" { | ||
| if resolvedVersion != "" && d.maliciousChecker != nil { |
There was a problem hiding this comment.
I think we should fail - even panic if d.maliciousChecker IS nil. It should NEVER be nil. This if statement will result in: "There is a bug in devguard itself, this will not THROW and silently disable the dependency proxy firewall without telling the user"
| shareDependencyProxyRouter := apiV1Group.Group.Group("/dependency-proxy/:secret") | ||
|
|
||
| shareDependencyProxyRouter.GET("/npm", dependencyProxyController.ProxyNPM) | ||
| shareDependencyProxyRouter.GET("/npm/*", dependencyProxyController.ProxyNPM) |
There was a problem hiding this comment.
Lets make this explicit. I think it is super hard to follow the code, since each proxy function handles different requests. One for tar.gz, metadata requests etc. I think it would be much simpler to understand this security critical part of devguard after splitting each route into its own dedicated function. What do you think?
No description provided.