mirror of
https://github.com/gomods/athens
synced 2026-02-03 11:00:32 +00:00
Use glob matching for package paths (#1409)
- Match the behavior of GOPRIVATE/GONOPROXY/GONOSUMDB
This commit is contained in:
committed by
Marwan Sulaiman
parent
90b3c1dc97
commit
ed66d85f3f
@@ -4,8 +4,9 @@ import (
|
||||
"net/http"
|
||||
"net/http/httputil"
|
||||
"net/url"
|
||||
"path"
|
||||
"strings"
|
||||
|
||||
"github.com/gomods/athens/pkg/paths"
|
||||
)
|
||||
|
||||
func sumdbPoxy(url *url.URL, nosumPatterns []string) http.Handler {
|
||||
@@ -25,7 +26,7 @@ func noSumWrapper(h http.Handler, host string, patterns []string) http.Handler {
|
||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
if strings.HasPrefix(r.URL.Path, "/lookup/") {
|
||||
for _, p := range patterns {
|
||||
if matchesPattern(p, r.URL.Path[len("/lookup/"):]) {
|
||||
if paths.MatchesPattern(p, r.URL.Path[len("/lookup/"):]) {
|
||||
w.WriteHeader(http.StatusForbidden)
|
||||
return
|
||||
}
|
||||
@@ -34,29 +35,3 @@ func noSumWrapper(h http.Handler, host string, patterns []string) http.Handler {
|
||||
h.ServeHTTP(w, r)
|
||||
})
|
||||
}
|
||||
|
||||
// matchesPattern is adopted from
|
||||
// https://github.com/golang/go/blob/a11644a26557ea436d456f005f39f4e01902bafe/src/cmd/go/internal/str/path.go#L58
|
||||
// this function matches based on path prefixes and
|
||||
// tries to keep the same behavior as GONOSUMDB and friends
|
||||
func matchesPattern(pattern, target string) bool {
|
||||
n := strings.Count(pattern, "/")
|
||||
prefix := target
|
||||
for i := 0; i < len(target); i++ {
|
||||
if target[i] == '/' {
|
||||
if n == 0 {
|
||||
prefix = target[:i]
|
||||
break
|
||||
}
|
||||
n--
|
||||
}
|
||||
}
|
||||
if n > 0 {
|
||||
return false
|
||||
}
|
||||
matched, _ := path.Match(pattern, prefix)
|
||||
if matched {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
@@ -4,10 +4,10 @@ import (
|
||||
"encoding/base64"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"path"
|
||||
"strings"
|
||||
|
||||
"github.com/gomods/athens/pkg/errors"
|
||||
"github.com/gomods/athens/pkg/paths"
|
||||
"github.com/hashicorp/hcl2/gohcl"
|
||||
"github.com/hashicorp/hcl2/hclparse"
|
||||
)
|
||||
@@ -118,7 +118,7 @@ func (d *DownloadFile) validate() error {
|
||||
// exist or match.
|
||||
func (d *DownloadFile) Match(mod string) Mode {
|
||||
for _, p := range d.Paths {
|
||||
if hasMatch, err := path.Match(p.Pattern, mod); hasMatch && err == nil {
|
||||
if paths.MatchesPattern(p.Pattern, mod) {
|
||||
return p.Mode
|
||||
}
|
||||
}
|
||||
@@ -130,7 +130,7 @@ func (d *DownloadFile) Match(mod string) Mode {
|
||||
// the top level downloadURL is returned.
|
||||
func (d *DownloadFile) URL(mod string) string {
|
||||
for _, p := range d.Paths {
|
||||
if hasMatch, err := path.Match(p.Pattern, mod); hasMatch && err == nil {
|
||||
if paths.MatchesPattern(p.Pattern, mod) {
|
||||
if p.DownloadURL != "" {
|
||||
return p.DownloadURL
|
||||
}
|
||||
|
||||
@@ -36,6 +36,28 @@ var testCases = []struct {
|
||||
input: "github.com/gomods/athens",
|
||||
expectedMode: None,
|
||||
},
|
||||
{
|
||||
name: "multiple depth pattern match",
|
||||
file: &DownloadFile{
|
||||
Mode: Sync,
|
||||
Paths: []*DownloadPath{
|
||||
{Pattern: "github.com/*", Mode: None},
|
||||
},
|
||||
},
|
||||
input: "github.com/gomods/athens/pkg/mode",
|
||||
expectedMode: None,
|
||||
},
|
||||
{
|
||||
name: "subdomain pattern match",
|
||||
file: &DownloadFile{
|
||||
Mode: Sync,
|
||||
Paths: []*DownloadPath{
|
||||
{Pattern: "*.github.com/gomods/*", Mode: None},
|
||||
},
|
||||
},
|
||||
input: "athens.github.com/gomods/pkg/mode",
|
||||
expectedMode: None,
|
||||
},
|
||||
{
|
||||
name: "pattern fallback",
|
||||
file: &DownloadFile{
|
||||
|
||||
@@ -2,6 +2,8 @@ package paths
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"path"
|
||||
"strings"
|
||||
|
||||
"github.com/gomods/athens/pkg/errors"
|
||||
"github.com/gorilla/mux"
|
||||
@@ -50,3 +52,28 @@ func GetAllParams(r *http.Request) (*AllPathParams, error) {
|
||||
|
||||
return &AllPathParams{Module: mod, Version: version}, nil
|
||||
}
|
||||
|
||||
// MatchesPattern reports whether the path prefix of target matches
|
||||
// pattern (as defined by path.Match)
|
||||
//
|
||||
// This tries to keep the same behavior as GOPRIVATE/GONOPROXY/GONOSUMDB,
|
||||
// and is adopted from:
|
||||
// https://github.com/golang/go/blob/a11644a26557ea436d456f005f39f4e01902bafe/src/cmd/go/internal/str/path.go#L58
|
||||
func MatchesPattern(pattern, target string) bool {
|
||||
n := strings.Count(pattern, "/")
|
||||
prefix := target
|
||||
for i := 0; i < len(target); i++ {
|
||||
if target[i] == '/' {
|
||||
if n == 0 {
|
||||
prefix = target[:i]
|
||||
break
|
||||
}
|
||||
n--
|
||||
}
|
||||
}
|
||||
if n > 0 {
|
||||
return false
|
||||
}
|
||||
matched, _ := path.Match(pattern, prefix)
|
||||
return matched
|
||||
}
|
||||
|
||||
@@ -0,0 +1,105 @@
|
||||
package paths
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestMatchesPattern(t *testing.T) {
|
||||
type args struct {
|
||||
pattern string
|
||||
name string
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
args args
|
||||
want bool
|
||||
}{
|
||||
{
|
||||
name: "standard match",
|
||||
args: args{
|
||||
pattern: "example.com/*",
|
||||
name: "example.com/athens",
|
||||
},
|
||||
want: true,
|
||||
},
|
||||
{
|
||||
name: "mutiple depth match",
|
||||
args: args{
|
||||
pattern: "example.com/*",
|
||||
name: "example.com/athens/pkg",
|
||||
},
|
||||
want: true,
|
||||
},
|
||||
{
|
||||
name: "subdomain match",
|
||||
args: args{
|
||||
pattern: "*.example.com/*",
|
||||
name: "go.example.com/athens/pkg",
|
||||
},
|
||||
want: true,
|
||||
},
|
||||
{
|
||||
name: "subdirectory exact match",
|
||||
args: args{
|
||||
pattern: "*.example.com/mod",
|
||||
name: "go.example.com/mod/example",
|
||||
},
|
||||
want: true,
|
||||
},
|
||||
{
|
||||
name: "subdirectory mismatch",
|
||||
args: args{
|
||||
pattern: "*.example.com/mod",
|
||||
name: "go.example.com/pkg/example",
|
||||
},
|
||||
want: false,
|
||||
},
|
||||
{
|
||||
name: "shorter name mismatch",
|
||||
args: args{
|
||||
pattern: "*.example.com/mod/pkg",
|
||||
name: "go.example.com/pkg",
|
||||
},
|
||||
want: false,
|
||||
},
|
||||
{
|
||||
name: "no subdirectory mismatch",
|
||||
args: args{
|
||||
pattern: "*.example.com/mod/pkg",
|
||||
name: "go.example.com/pkg",
|
||||
},
|
||||
want: false,
|
||||
},
|
||||
{
|
||||
name: "bad pattern",
|
||||
args: args{
|
||||
pattern: "[]a]",
|
||||
name: "go.example.com/pkg",
|
||||
},
|
||||
want: false,
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
got := MatchesPattern(tt.args.pattern, tt.args.name)
|
||||
if got != tt.want {
|
||||
t.Errorf("MatchGlobPattern() = %v, want %v", got, tt.want)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkMatchesPattern(b *testing.B) {
|
||||
for i := 1; i < 5; i++ {
|
||||
target := "git.example.com" + strings.Repeat("/path", i) + "/pkg"
|
||||
b.Run(fmt.Sprintf("MatchPattern/%d", i), func(b *testing.B) {
|
||||
for n := 0; n < b.N; n++ {
|
||||
if !MatchesPattern("*.example.com/*", target) {
|
||||
b.Error("mismatch")
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user