From a75817f5500c5210a3892a21e4e712477b2fdadc Mon Sep 17 00:00:00 2001 From: wangjingshuiku Date: Sun, 13 Sep 2026 11:08:09 +0900 Subject: [PATCH] fix: require module path boundaries when matching packages (#7957) Signed-off-by: wangjingshuiku --- cmd/packagesfuncs.go | 8 ++++++-- cmd/packagesfuncs_test.go | 22 ++++++++++++++++++++++ 2 files changed, 28 insertions(+), 2 deletions(-) diff --git a/cmd/packagesfuncs.go b/cmd/packagesfuncs.go index 58d9b6181..014442cab 100644 --- a/cmd/packagesfuncs.go +++ b/cmd/packagesfuncs.go @@ -253,7 +253,7 @@ func getModules() (standard, nonstandard, unknown []moduleInfo, err error) { // longest matching prefix in case there are nested modules var matched *debug.Module for _, dep := range bi.Deps { - if strings.HasPrefix(modPkgPath, dep.Path) { + if moduleContainsPackage(dep.Path, modPkgPath) { if matched == nil || len(dep.Path) > len(matched.Path) { matched = dep } @@ -262,7 +262,7 @@ func getModules() (standard, nonstandard, unknown []moduleInfo, err error) { caddyModGoMod := moduleInfo{caddyModuleID: modID, goModule: matched} - if strings.HasPrefix(modPkgPath, caddy.ImportPath) { + if moduleContainsPackage(caddy.ImportPath, modPkgPath) { standard = append(standard, caddyModGoMod) } else { nonstandard = append(nonstandard, caddyModGoMod) @@ -271,6 +271,10 @@ func getModules() (standard, nonstandard, unknown []moduleInfo, err error) { return standard, nonstandard, unknown, err } +func moduleContainsPackage(modulePath, packagePath string) bool { + return packagePath == modulePath || strings.HasPrefix(packagePath, modulePath+"/") +} + func listModules(path string) error { cmd := exec.Command(path, "list-modules", "--versions", "--skip-standard") cmd.Stdout = os.Stdout diff --git a/cmd/packagesfuncs_test.go b/cmd/packagesfuncs_test.go index 889303114..bb22c7958 100644 --- a/cmd/packagesfuncs_test.go +++ b/cmd/packagesfuncs_test.go @@ -39,6 +39,28 @@ func TestDownloadBuildClosesErrorResponseBody(t *testing.T) { } } +func TestModuleContainsPackage(t *testing.T) { + tests := []struct { + name string + modulePath string + packagePath string + want bool + }{ + {name: "module root", modulePath: "example.com/mod", packagePath: "example.com/mod", want: true}, + {name: "module package", modulePath: "example.com/mod", packagePath: "example.com/mod/pkg", want: true}, + {name: "shared prefix", modulePath: "example.com/mod", packagePath: "example.com/module", want: false}, + {name: "caddy module shared prefix", modulePath: "github.com/caddyserver/caddy/v2", packagePath: "github.com/caddyserver/caddy/v20", want: false}, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if got := moduleContainsPackage(tt.modulePath, tt.packagePath); got != tt.want { + t.Fatalf("moduleContainsPackage(%q, %q) = %v, want %v", tt.modulePath, tt.packagePath, got, tt.want) + } + }) + } +} + type roundTripperFunc func(*http.Request) (*http.Response, error) func (f roundTripperFunc) RoundTrip(req *http.Request) (*http.Response, error) {