From 02fa6c3d635873234c7599b39af35badbb24ae95 Mon Sep 17 00:00:00 2001 From: rissrice2105-agent Date: Wed, 15 Jul 2026 00:45:09 -0600 Subject: [PATCH] fix(files): honor forwarded HTTPS proxy chains --- internal/files/web.go | 6 +++++- internal/files/web_test.go | 22 ++++++++++++++++++++++ 2 files changed, 27 insertions(+), 1 deletion(-) diff --git a/internal/files/web.go b/internal/files/web.go index 01f1609..3874970 100644 --- a/internal/files/web.go +++ b/internal/files/web.go @@ -115,7 +115,11 @@ func (h *webSrv) clear(w http.ResponseWriter, r *http.Request) { } func secureReq(r *http.Request) bool { - return r.TLS != nil || strings.EqualFold(r.Header.Get("X-Forwarded-Proto"), "https") + if r.TLS != nil { + return true + } + proto, _, _ := strings.Cut(r.Header.Get("X-Forwarded-Proto"), ",") + return strings.EqualFold(strings.TrimSpace(proto), "https") } func randHex(n int) string { diff --git a/internal/files/web_test.go b/internal/files/web_test.go index 3dd6677..e899ffd 100644 --- a/internal/files/web_test.go +++ b/internal/files/web_test.go @@ -61,6 +61,28 @@ func TestWebRequiresAuth(t *testing.T) { } } +func TestSecureReqUsesFirstForwardedProto(t *testing.T) { + for _, tc := range []struct { + name string + proto string + secure bool + }{ + {name: "https", proto: "https", secure: true}, + {name: "proxy chain", proto: "https, http", secure: true}, + {name: "case and whitespace", proto: " HTTPS , http", secure: true}, + {name: "http", proto: "http", secure: false}, + {name: "untrusted later value", proto: "http, https", secure: false}, + } { + t.Run(tc.name, func(t *testing.T) { + req := httptest.NewRequest(http.MethodGet, "/", nil) + req.Header.Set("X-Forwarded-Proto", tc.proto) + if got := secureReq(req); got != tc.secure { + t.Fatalf("secureReq() = %v, want %v for %q", got, tc.secure, tc.proto) + } + }) + } +} + func TestWebRoundTrip(t *testing.T) { h, _ := webTestHandler(t)