mirror of
https://github.com/WJQSERVER-STUDIO/ghproxy.git
synced 2026-06-13 15:47:37 +08:00
fix(proxy): restore header filtering and API matcher consistency
- Canonicalize filtered header deny-lists so Cloudflare and CDN headers are still removed - Normalize incomplete API repo paths to stable owner-level matcher output regardless of trailing slash or query - Add regression tests covering header canonicalization and incomplete API repo path parsing
This commit is contained in:
parent
ba3dcf7624
commit
e9e48fcefd
4 changed files with 73 additions and 9 deletions
|
|
@ -65,6 +65,31 @@ func TestCopyHeaderFiltered(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
func TestCopyHeaderFiltered_CanonicalizesDenylist(t *testing.T) {
|
||||
src := http.Header{
|
||||
"Cf-Ipcountry": {"CN"},
|
||||
"Cf-Ray": {"abc123"},
|
||||
"Cf-Ew-Via": {"edge"},
|
||||
"X-Forwarded-For": {"127.0.0.1"},
|
||||
}
|
||||
dst := make(http.Header)
|
||||
|
||||
copyHeaderFiltered(dst, src, reqHeadersToRemove)
|
||||
|
||||
if got := dst.Values("Cf-Ipcountry"); len(got) != 0 {
|
||||
t.Fatalf("Cf-Ipcountry should be filtered, got %v", got)
|
||||
}
|
||||
if got := dst.Values("Cf-Ray"); len(got) != 0 {
|
||||
t.Fatalf("Cf-Ray should be filtered, got %v", got)
|
||||
}
|
||||
if got := dst.Values("Cf-Ew-Via"); len(got) != 0 {
|
||||
t.Fatalf("Cf-Ew-Via should be filtered, got %v", got)
|
||||
}
|
||||
if got := dst.Values("X-Forwarded-For"); !reflect.DeepEqual(got, []string{"127.0.0.1"}) {
|
||||
t.Fatalf("X-Forwarded-For = %v, want [127.0.0.1]", got)
|
||||
}
|
||||
}
|
||||
|
||||
func TestCopyHeaderFiltered_AllowsAllWhenDenylistEmpty(t *testing.T) {
|
||||
src := http.Header{
|
||||
"X-Test": {"one", "two"},
|
||||
|
|
|
|||
|
|
@ -161,21 +161,28 @@ func Matcher(rawPath string, cfg *config.Config) (string, string, string, *GHPro
|
|||
var user, repo string
|
||||
if strings.HasPrefix(remaining, "repos/") {
|
||||
remaining = remaining[6:]
|
||||
if q := strings.IndexByte(remaining, '?'); q != -1 {
|
||||
remaining = remaining[:q]
|
||||
}
|
||||
if remaining != "" && !strings.ContainsRune(remaining, '/') {
|
||||
user = remaining
|
||||
return user, "", "api", nil
|
||||
}
|
||||
i := strings.IndexByte(remaining, '/')
|
||||
if i > 0 {
|
||||
userCandidate := remaining[:i]
|
||||
user = remaining[:i]
|
||||
rest := remaining[i+1:]
|
||||
if rest != "" {
|
||||
if j := strings.IndexByte(rest, '/'); j != -1 {
|
||||
repo = rest[:j]
|
||||
} else {
|
||||
repo = rest
|
||||
}
|
||||
user = userCandidate
|
||||
}
|
||||
}
|
||||
} else if strings.HasPrefix(remaining, "users/") {
|
||||
remaining = remaining[6:]
|
||||
if q := strings.IndexByte(remaining, '?'); q != -1 {
|
||||
remaining = remaining[:q]
|
||||
}
|
||||
if remaining != "" {
|
||||
if i := strings.IndexByte(remaining, '/'); i != -1 {
|
||||
user = remaining[:i]
|
||||
|
|
|
|||
|
|
@ -151,7 +151,19 @@ func TestMatcher_Compatibility(t *testing.T) {
|
|||
name: "API Repos Path (missing repo)",
|
||||
rawPath: "https://api.github.com/repos/owner",
|
||||
config: cfgWithAuth,
|
||||
expectedUser: "", expectedRepo: "", expectedMatcher: "api",
|
||||
expectedUser: "owner", expectedRepo: "", expectedMatcher: "api",
|
||||
},
|
||||
{
|
||||
name: "API Repos Path (trailing slash)",
|
||||
rawPath: "https://api.github.com/repos/owner/",
|
||||
config: cfgWithAuth,
|
||||
expectedUser: "owner", expectedRepo: "", expectedMatcher: "api",
|
||||
},
|
||||
{
|
||||
name: "API Repos Path (missing repo with query)",
|
||||
rawPath: "https://api.github.com/repos/owner?per_page=1",
|
||||
config: cfgWithAuth,
|
||||
expectedUser: "owner", expectedRepo: "", expectedMatcher: "api",
|
||||
},
|
||||
{
|
||||
name: "API Users Path (exact user)",
|
||||
|
|
|
|||
|
|
@ -60,6 +60,26 @@ func copyHeader(dst, src http.Header) {
|
|||
}
|
||||
}
|
||||
|
||||
func canonicalizeHeaderSet(headers map[string]struct{}) map[string]struct{} {
|
||||
canonicalized := make(map[string]struct{}, len(headers))
|
||||
for key := range headers {
|
||||
canonicalized[http.CanonicalHeaderKey(key)] = struct{}{}
|
||||
}
|
||||
return canonicalized
|
||||
}
|
||||
|
||||
func init() {
|
||||
reqHeadersToRemove = canonicalizeHeaderSet(reqHeadersToRemove)
|
||||
cloneHeadersToRemove = canonicalizeHeaderSet(cloneHeadersToRemove)
|
||||
respHeadersToRemove = canonicalizeHeaderSet(respHeadersToRemove)
|
||||
defaultHeaders = map[string]string{
|
||||
"Accept": "*/*",
|
||||
"Accept-Encoding": "",
|
||||
"Transfer-Encoding": "chunked",
|
||||
"User-Agent": "GHProxy/1.0",
|
||||
}
|
||||
}
|
||||
|
||||
func copyHeaderFiltered(dst, src http.Header, denylist map[string]struct{}) {
|
||||
for k, vv := range src {
|
||||
if _, denied := denylist[k]; denied {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue