1package pagewriter
2
3import (
4	"errors"
5	"html/template"
6	"io/ioutil"
7	"net/http/httptest"
8
9	middlewareapi "github.com/oauth2-proxy/oauth2-proxy/v7/pkg/apis/middleware"
10	. "github.com/onsi/ginkgo"
11	. "github.com/onsi/gomega"
12)
13
14var _ = Describe("Error Page Writer", func() {
15	var errorPage *errorPageWriter
16
17	BeforeEach(func() {
18		tmpl, err := template.New("").Parse("{{.Title}} {{.Message}} {{.ProxyPrefix}} {{.StatusCode}} {{.Redirect}} {{.RequestID}} {{.Footer}} {{.Version}}")
19		Expect(err).ToNot(HaveOccurred())
20
21		errorPage = &errorPageWriter{
22			template:    tmpl,
23			proxyPrefix: "/prefix/",
24			footer:      "Custom Footer Text",
25			version:     "v0.0.0-test",
26		}
27	})
28
29	Context("WriteErrorPage", func() {
30		It("Writes the template to the response writer", func() {
31			recorder := httptest.NewRecorder()
32			errorPage.WriteErrorPage(recorder, ErrorPageOpts{
33				Status:      403,
34				RedirectURL: "/redirect",
35				RequestID:   testRequestID,
36				AppError:    "Access Denied",
37			})
38
39			body, err := ioutil.ReadAll(recorder.Result().Body)
40			Expect(err).ToNot(HaveOccurred())
41			Expect(string(body)).To(Equal("Forbidden You do not have permission to access this resource. /prefix/ 403 /redirect 11111111-2222-4333-8444-555555555555 Custom Footer Text v0.0.0-test"))
42		})
43
44		It("With a different code, uses the stock message for the correct code", func() {
45			recorder := httptest.NewRecorder()
46			errorPage.WriteErrorPage(recorder, ErrorPageOpts{
47				Status:      500,
48				RedirectURL: "/redirect",
49				RequestID:   testRequestID,
50				AppError:    "Access Denied",
51			})
52
53			body, err := ioutil.ReadAll(recorder.Result().Body)
54			Expect(err).ToNot(HaveOccurred())
55			Expect(string(body)).To(Equal("Internal Server Error Oops! Something went wrong. For more information contact your server administrator. /prefix/ 500 /redirect 11111111-2222-4333-8444-555555555555 Custom Footer Text v0.0.0-test"))
56		})
57
58		It("With a message override, uses the message", func() {
59			recorder := httptest.NewRecorder()
60			errorPage.WriteErrorPage(recorder, ErrorPageOpts{
61				Status:      403,
62				RedirectURL: "/redirect",
63				RequestID:   testRequestID,
64				AppError:    "Access Denied",
65				Messages: []interface{}{
66					"An extra message: %s",
67					"with more context.",
68				},
69			})
70
71			body, err := ioutil.ReadAll(recorder.Result().Body)
72			Expect(err).ToNot(HaveOccurred())
73			Expect(string(body)).To(Equal("Forbidden An extra message: with more context. /prefix/ 403 /redirect 11111111-2222-4333-8444-555555555555 Custom Footer Text v0.0.0-test"))
74		})
75
76		It("Sanitizes malicious user input", func() {
77			recorder := httptest.NewRecorder()
78			errorPage.WriteErrorPage(recorder, ErrorPageOpts{
79				Status:      403,
80				RedirectURL: "/redirect",
81				RequestID:   "<script>alert(1)</script>",
82				AppError:    "Access Denied",
83			})
84
85			body, err := ioutil.ReadAll(recorder.Result().Body)
86			Expect(err).ToNot(HaveOccurred())
87			Expect(string(body)).To(Equal("Forbidden You do not have permission to access this resource. /prefix/ 403 /redirect &lt;script&gt;alert(1)&lt;/script&gt; Custom Footer Text v0.0.0-test"))
88		})
89	})
90
91	Context("ProxyErrorHandler", func() {
92		It("Writes a bad gateway error the response writer", func() {
93			req := httptest.NewRequest("", "/bad-gateway", nil)
94			req = middlewareapi.AddRequestScope(req, &middlewareapi.RequestScope{
95				RequestID: testRequestID,
96			})
97			recorder := httptest.NewRecorder()
98			errorPage.ProxyErrorHandler(recorder, req, errors.New("some upstream error"))
99
100			body, err := ioutil.ReadAll(recorder.Result().Body)
101			Expect(err).ToNot(HaveOccurred())
102			Expect(string(body)).To(Equal("Bad Gateway There was a problem connecting to the upstream server. /prefix/ 502  11111111-2222-4333-8444-555555555555 Custom Footer Text v0.0.0-test"))
103		})
104	})
105
106	Context("With Debug enabled", func() {
107		BeforeEach(func() {
108			tmpl, err := template.New("").Parse("{{.Message}}")
109			Expect(err).ToNot(HaveOccurred())
110
111			errorPage.template = tmpl
112			errorPage.debug = true
113		})
114
115		Context("WriteErrorPage", func() {
116			It("Writes the detailed error in place of the message", func() {
117				recorder := httptest.NewRecorder()
118				errorPage.WriteErrorPage(recorder, ErrorPageOpts{
119					Status:      403,
120					RedirectURL: "/redirect",
121					AppError:    "Debug error",
122				})
123
124				body, err := ioutil.ReadAll(recorder.Result().Body)
125				Expect(err).ToNot(HaveOccurred())
126				Expect(string(body)).To(Equal("Debug error"))
127			})
128		})
129
130		Context("ProxyErrorHandler", func() {
131			It("Writes a bad gateway error the response writer", func() {
132				req := httptest.NewRequest("", "/bad-gateway", nil)
133				req = middlewareapi.AddRequestScope(req, &middlewareapi.RequestScope{
134					RequestID: testRequestID,
135				})
136				recorder := httptest.NewRecorder()
137				errorPage.ProxyErrorHandler(recorder, req, errors.New("some upstream error"))
138
139				body, err := ioutil.ReadAll(recorder.Result().Body)
140				Expect(err).ToNot(HaveOccurred())
141				Expect(string(body)).To(Equal("some upstream error"))
142			})
143		})
144	})
145})
146