internal/httpd/pagesredirect_test.go
57 lines · 2025 bytes
3 symbols in this file
1package httpd
2
3import (
4 "net/http/httptest"
5 "strings"
6 "testing"
7)
8
9// A redirect target built from the request path must not be able to leave
10// the site. `r.URL.Path` keeps a leading `//` — Go's URL parser does not
11// normalise it — and `Location: //evil.example/` is protocol-relative, so
12// a browser follows it to another origin (gosecurity:S5146, #153).
13//
14// The directory-redirect targets are derived from the cleaned path, so
15// this asserts the property rather than the spelling: whatever the code
16// emits, it must be a same-origin path.
17func TestPageRedirectCannotLeaveTheSite(t *testing.T) {
18 cases := []string{
19 "//evil.example",
20 "//evil.example/deep",
21 "///evil.example",
22 "/\\evil.example",
23 "//evil.example/../..",
24 }
25 for _, p := range cases {
26 got := pageRedirectTarget(p)
27 if got == "" {
28 continue // no redirect for this shape is a fine answer
29 }
30 if strings.HasPrefix(got, "//") || strings.HasPrefix(got, "/\\") {
31 t.Errorf("request %q redirects to %q, which a browser reads as another origin", p, got)
32 }
33 if !strings.HasPrefix(got, "/") {
34 t.Errorf("request %q redirects to %q, which is not an absolute path", p, got)
35 }
36 }
37}
38
39// The ordinary case still behaves: a directory URL without a trailing
40// slash gains one, so relative links inside the page resolve.
41func TestPageRedirectAddsTrailingSlash(t *testing.T) {
42 if got, want := pageRedirectTarget("/guide"), "/guide/"; got != want {
43 t.Errorf("pageRedirectTarget(/guide) = %q, want %q", got, want)
44 }
45 if got, want := pageRedirectTarget("/a/b/c"), "/a/b/c/"; got != want {
46 t.Errorf("pageRedirectTarget(/a/b/c) = %q, want %q", got, want)
47 }
48}
49
50// Guard against a regression in the shape the fix relies on: httptest
51// builds the request the same way the server sees it.
52func TestRawPathKeepsDoubleSlash(t *testing.T) {
53 r := httptest.NewRequest("GET", "http://host//evil.example", nil)
54 if r.URL.Path != "//evil.example" {
55 t.Skipf("net/url normalised the path to %q; the hazard this guards is gone", r.URL.Path)
56 }
57}