diff --git a/checker/rules_redirect.go b/checker/rules_redirect.go index 2ec5ab7..e622aef 100644 --- a/checker/rules_redirect.go +++ b/checker/rules_redirect.go @@ -38,8 +38,13 @@ func (r *httpsRedirectRule) Evaluate(ctx context.Context, obs sdk.ObservationGet // simply not running is fine for redirect-purposes. return } + // Prefer the last redirect hop's destination over FinalURL. When the + // probe stops at a scheme-changing redirect (http→https), it does not + // follow the hop, so FinalURL stays the pre-redirect http:// URL while + // the chain records the real https:// target. Judging by FinalURL alone + // would mis-flag a correct http→https redirect as no_https_redirect. final := p.FinalURL - if final == "" && len(p.RedirectChain) > 0 { + if len(p.RedirectChain) > 0 { final = p.RedirectChain[len(p.RedirectChain)-1].To } isHTTPS := false diff --git a/checker/rules_redirect_test.go b/checker/rules_redirect_test.go index 772f681..0cefc03 100644 --- a/checker/rules_redirect_test.go +++ b/checker/rules_redirect_test.go @@ -37,6 +37,22 @@ func TestHTTPSRedirectRule_OKViaRedirectChain(t *testing.T) { mustStatus(t, states, sdk.StatusOK) } +func TestHTTPSRedirectRule_OKViaRedirectChain_FinalURLStillHTTP(t *testing.T) { + // Real-world case: the probe stops at the scheme-changing redirect, so it + // never follows the https:// hop. FinalURL therefore remains the original + // http:// URL while RedirectChain records the 308 to https. This must be + // treated as a correct redirect, not no_https_redirect. + p := httpProbe("a:80") + p.StatusCode = 308 + p.FinalURL = "http://example.test/" + p.RedirectChain = []RedirectStep{{From: "http://example.test/", To: "https://example.test/", Status: 308}} + states := runRule(t, &httpsRedirectRule{}, &HTTPData{Probes: []HTTPProbe{p}}, sdk.CheckerOptions{OptionRequireHTTPS: true}) + mustStatus(t, states, sdk.StatusOK) + if !hasCode(states, "http.https_redirect.ok") { + t.Errorf("expected redirect.ok, got %+v", states) + } +} + func TestHTTPSRedirectRule_NoRedirect_Required(t *testing.T) { p := httpProbe("a:80") p.StatusCode = 200