@@ -1,7 +1,6 @@
11package main
22
33import (
4− "bufio"
54 "context"
65 "crypto"
76 "encoding/base64"
@@ -11,7 +10,6 @@ import (
1110 "net/http"
1211 "net/http/httptest"
1312 "net/url"
14− "os"
1513 "regexp"
1614 "strings"
1715 "testing"
@@ -19,7 +17,6 @@ import (
1917
2018 "github.com/coreos/go-oidc"
2119 "github.com/mbland/hmacauth"
22− "github.com/oauth2-proxy/oauth2-proxy/v7/pkg/apis/middleware"
2320 "github.com/oauth2-proxy/oauth2-proxy/v7/pkg/apis/options"
2421 "github.com/oauth2-proxy/oauth2-proxy/v7/pkg/apis/sessions"
2522 "github.com/oauth2-proxy/oauth2-proxy/v7/pkg/cookies"
@@ -28,10 +25,7 @@ import (
2825 "github.com/oauth2-proxy/oauth2-proxy/v7/pkg/upstream"
2926 "github.com/oauth2-proxy/oauth2-proxy/v7/pkg/validation"
3027 "github.com/oauth2-proxy/oauth2-proxy/v7/providers"
31− . "github.com/onsi/ginkgo"
32− . "github.com/onsi/gomega"
3328 "github.com/stretchr/testify/assert"
34− "github.com/stretchr/testify/require"
3529)
3630
3731const (
@@ -63,304 +57,6 @@ func TestRobotsTxt(t *testing.T) {
6357 assert.Equal(t, "User-agent: *\nDisallow: /\n", rw.Body.String())
6458}
6559
66−func TestIsValidRedirect(t *testing.T) {
67− opts := baseTestOptions()
68− // Should match domains that are exactly foo.bar and any subdomain of bar.foo
69− opts.WhitelistDomains = []string{
70− "foo.bar",
71− ".bar.foo",
72− "port.bar:8080",
73− ".sub.port.bar:8080",
74− "anyport.bar:*",
75− ".sub.anyport.bar:*",
76− }
77− err := validation.Validate(opts)
78− assert.NoError(t, err)
79−
80− proxy, err := NewOAuthProxy(opts, func(string) bool { return true })
81− if err != nil {
82− t.Fatal(err)
83− }
84−
85− testCases := []struct {
86− Desc, Redirect string
87− ExpectedResult bool
88− }{
89− {
90− Desc: "noRD",
91− Redirect: "",
92− ExpectedResult: false,
93− },
94− {
95− Desc: "singleSlash",
96− Redirect: "/redirect",
97− ExpectedResult: true,
98− },
99− {
100− Desc: "doubleSlash",
101− Redirect: "//redirect",
102− ExpectedResult: false,
103− },
104− {
105− Desc: "validHTTP",
106− Redirect: "http://foo.bar/redirect",
107− ExpectedResult: true,
108− },
109− {
110− Desc: "validHTTPS",
111− Redirect: "https://foo.bar/redirect",
112− ExpectedResult: true,
113− },
114− {
115− Desc: "invalidHTTPSubdomain",
116− Redirect: "http://baz.foo.bar/redirect",
117− ExpectedResult: false,
118− },
119− {
120− Desc: "invalidHTTPSSubdomain",
121− Redirect: "https://baz.foo.bar/redirect",
122− ExpectedResult: false,
123− },
124− {
125− Desc: "validHTTPSubdomain",
126− Redirect: "http://baz.bar.foo/redirect",
127− ExpectedResult: true,
128− },
129− {
130− Desc: "validHTTPSSubdomain",
131− Redirect: "https://baz.bar.foo/redirect",
132− ExpectedResult: true,
133− },
134− {
135− Desc: "validHTTPDomain",
136− Redirect: "http://bar.foo/redirect",
137− ExpectedResult: true,
138− },
139− {
140− Desc: "invalidHTTP1",
141− Redirect: "http://foo.bar.evil.corp/redirect",
142− ExpectedResult: false,
143− },
144− {
145− Desc: "invalidHTTPS1",
146− Redirect: "https://foo.bar.evil.corp/redirect",
147− ExpectedResult: false,
148− },
149− {
150− Desc: "invalidHTTP2",
151− Redirect: "http://evil.corp/redirect?rd=foo.bar",
152− ExpectedResult: false,
153− },
154− {
155− Desc: "invalidHTTPS2",
156− Redirect: "https://evil.corp/redirect?rd=foo.bar",
157− ExpectedResult: false,
158− },
159− {
160− Desc: "invalidPort",
161− Redirect: "https://evil.corp:3838/redirect",
162− ExpectedResult: false,
163− },
164− {
165− Desc: "invalidEmptyPort",
166− Redirect: "http://foo.bar:3838/redirect",
167− ExpectedResult: false,
168− },
169− {
170− Desc: "invalidEmptyPortSubdomain",
171− Redirect: "http://baz.bar.foo:3838/redirect",
172− ExpectedResult: false,
173− },
174− {
175− Desc: "validSpecificPort",
176− Redirect: "http://port.bar:8080/redirect",
177− ExpectedResult: true,
178− },
179− {
180− Desc: "invalidSpecificPort",
181− Redirect: "http://port.bar:3838/redirect",
182− ExpectedResult: false,
183− },
184− {
185− Desc: "validSpecificPortSubdomain",
186− Redirect: "http://foo.sub.port.bar:8080/redirect",
187− ExpectedResult: true,
188− },
189− {
190− Desc: "invalidSpecificPortSubdomain",
191− Redirect: "http://foo.sub.port.bar:3838/redirect",
192− ExpectedResult: false,
193− },
194− {
195− Desc: "validAnyPort1",
196− Redirect: "http://anyport.bar:8080/redirect",
197− ExpectedResult: true,
198− },
199− {
200− Desc: "validAnyPort2",
201− Redirect: "http://anyport.bar:8081/redirect",
202− ExpectedResult: true,
203− },
204− {
205− Desc: "validAnyPortSubdomain1",
206− Redirect: "http://a.sub.anyport.bar:8080/redirect",
207− ExpectedResult: true,
208− },
209− {
210− Desc: "validAnyPortSubdomain2",
211− Redirect: "http://a.sub.anyport.bar:8081/redirect",
212− ExpectedResult: true,
213− },
214− {
215− Desc: "openRedirect1",
216− Redirect: "/\\evil.com",
217− ExpectedResult: false,
218− },
219− {
220− Desc: "openRedirectSpace1",
221− Redirect: "/ /evil.com",
222− ExpectedResult: false,
223− },
224− {
225− Desc: "openRedirectSpace2",
226− Redirect: "/ \\evil.com",
227− ExpectedResult: false,
228− },
229− {
230− Desc: "openRedirectTab1",
231− Redirect: "/\t/evil.com",
232− ExpectedResult: false,
233− },
234− {
235− Desc: "openRedirectTab2",
236− Redirect: "/\t\\evil.com",
237− ExpectedResult: false,
238− },
239− {
240− Desc: "openRedirectVerticalTab1",
241− Redirect: "/\v/evil.com",
242− ExpectedResult: false,
243− },
244− {
245− Desc: "openRedirectVerticalTab2",
246− Redirect: "/\v\\evil.com",
247− ExpectedResult: false,
248− },
249− {
250− Desc: "openRedirectNewLine1",
251− Redirect: "/\n/evil.com",
252− ExpectedResult: false,
253− },
254− {
255− Desc: "openRedirectNewLine2",
256− Redirect: "/\n\\evil.com",
257− ExpectedResult: false,
258− },
259− {
260− Desc: "openRedirectCarriageReturn1",
261− Redirect: "/\r/evil.com",
262− ExpectedResult: false,
263− },
264− {
265− Desc: "openRedirectCarriageReturn2",
266− Redirect: "/\r\\evil.com",
267− ExpectedResult: false,
268− },
269− {
270− Desc: "openRedirectTripleTab",
271− Redirect: "/\t\t/\t/evil.com",
272− ExpectedResult: false,
273− },
274− {
275− Desc: "openRedirectTripleTab2",
276− Redirect: "/\t\t\\\t/evil.com",
277− ExpectedResult: false,
278− },
279− {
280− Desc: "openRedirectQuadTab1",
281− Redirect: "/\t\t/\t\t\\evil.com",
282− ExpectedResult: false,
283− },
284− {
285− Desc: "openRedirectQuadTab2",
286− Redirect: "/\t\t\\\t\t/evil.com",
287− ExpectedResult: false,
288− },
289− {
290− Desc: "openRedirectPeriod1",
291− Redirect: "/./\\evil.com",
292− ExpectedResult: false,
293− },
294− {
295− Desc: "openRedirectPeriod2",
296− Redirect: "/./../../\\evil.com",
297− ExpectedResult: false,
298− },
299− {
300− Desc: "openRedirectDoubleTab",
301− Redirect: "/\t/\t\\evil.com",
302− ExpectedResult: false,
303− },
304− {
305− Desc: "openRedirectPartialSubdomain",
306− Redirect: "http://evilbar.foo",
307− ExpectedResult: false,
308− },
309− }
310−
311− for _, tc := range testCases {
312− t.Run(tc.Desc, func(t *testing.T) {
313− result := proxy.IsValidRedirect(tc.Redirect)
314−
315− if result != tc.ExpectedResult {
316− t.Errorf("expected %t got %t", tc.ExpectedResult, result)
317− }
318− })
319− }
320−}
321−
322−var _ = Describe("OpenRedirect Tests", func() {
323− var proxy *OAuthProxy
324−
325− BeforeEach(func() {
326− opts := baseTestOptions()
327− // Should match domains that are exactly foo.bar and any subdomain of bar.foo
328− opts.WhitelistDomains = []string{
329− "foo.bar",
330− ".bar.foo",
331− "port.bar:8080",
332− ".sub.port.bar:8080",
333− "anyport.bar:*",
334− ".sub.anyport.bar:*",
335− "www.whitelisteddomain.tld",
336− }
337− Expect(validation.Validate(opts)).To(Succeed())
338−
339− var err error
340− proxy, err = NewOAuthProxy(opts, func(string) bool { return true })
341− Expect(err).ToNot(HaveOccurred())
342− })
343−
344− file, err := os.Open("./testdata/openredirects.txt")
345− Expect(err).ToNot(HaveOccurred())
346− defer func() {
347− Expect(file.Close()).To(Succeed())
348− }()
349−
350− scanner := bufio.NewScanner(file)
351− for scanner.Scan() {
352− rd := scanner.Text()
353− It(rd, func() {
354− rdUnescaped, err := url.QueryUnescape(rd)
355− Expect(err).ToNot(HaveOccurred())
356−
357− Expect(proxy.IsValidRedirect(rdUnescaped)).To(BeFalse(), "Expected redirect not to be valid")
358− })
359− }
360−
361− Expect(scanner.Err()).ToNot(HaveOccurred())
362−})
363−
36460type TestProvider struct {
36561 *providers.ProviderData
36662 EmailAddress string
@@ -1770,165 +1466,6 @@ func TestRequestSignature(t *testing.T) {
17701466 }
17711467}
17721468
1773−func Test_getAppRedirect(t *testing.T) {
1774− opts := baseTestOptions()
1775− opts.WhitelistDomains = append(opts.WhitelistDomains, ".example.com", ".example.com:8443")
1776− err := validation.Validate(opts)
1777− assert.NoError(t, err)
1778− require.NotEmpty(t, opts.ProxyPrefix)
1779− proxy, err := NewOAuthProxy(opts, func(s string) bool { return false })
1780− if err != nil {
1781− t.Fatal(err)
1782− }
1783−
1784− tests := []struct {
1785− name string
1786− url string
1787− headers map[string]string
1788− reverseProxy bool
1789− expectedRedirect string
1790− }{
1791− {
1792− name: "request outside of ProxyPrefix redirects to original URL",
1793− url: "/foo/bar",
1794− headers: nil,
1795− reverseProxy: false,
1796− expectedRedirect: "/foo/bar",
1797− },
1798− {
1799− name: "request with query preserves query",
1800− url: "/foo?bar",
1801− headers: nil,
1802− reverseProxy: false,
1803− expectedRedirect: "/foo?bar",
1804− },
1805− {
1806− name: "request under ProxyPrefix redirects to root",
1807− url: proxy.ProxyPrefix + "/foo/bar",
1808− headers: nil,
1809− reverseProxy: false,
1810− expectedRedirect: "/",
1811− },
1812− {
1813− name: "proxied request outside of ProxyPrefix redirects to proxied URL",
1814− url: "https://oauth.example.com/foo/bar",
1815− headers: map[string]string{
1816− "X-Forwarded-Proto": "https",
1817− "X-Forwarded-Host": "a-service.example.com",
1818− "X-Forwarded-Uri": "/foo/bar",
1819− },
1820− reverseProxy: true,
1821− expectedRedirect: "https://a-service.example.com/foo/bar",
1822− },
1823− {
1824− name: "non-proxied request with spoofed proxy headers wouldn't redirect",
1825− url: "https://oauth.example.com/foo?bar",
1826− headers: map[string]string{
1827− "X-Forwarded-Proto": "https",
1828− "X-Forwarded-Host": "a-service.example.com",
1829− "X-Forwarded-Uri": "/foo/bar",
1830− },
1831− reverseProxy: false,
1832− expectedRedirect: "/foo?bar",
1833− },
1834− {
1835− name: "proxied request under ProxyPrefix redirects to root",
1836− url: "https://oauth.example.com" + proxy.ProxyPrefix + "/foo/bar",
1837− headers: map[string]string{
1838− "X-Forwarded-Proto": "https",
1839− "X-Forwarded-Host": "a-service.example.com",
1840− "X-Forwarded-Uri": proxy.ProxyPrefix + "/foo/bar",
1841− },
1842− reverseProxy: true,
1843− expectedRedirect: "https://a-service.example.com/",
1844− },
1845− {
1846− name: "proxied request with port under ProxyPrefix redirects to root",
1847− url: "https://oauth.example.com" + proxy.ProxyPrefix + "/foo/bar",
1848− headers: map[string]string{
1849− "X-Forwarded-Proto": "https",
1850− "X-Forwarded-Host": "a-service.example.com:8443",
1851− "X-Forwarded-Uri": proxy.ProxyPrefix + "/foo/bar",
1852− },
1853− reverseProxy: true,
1854− expectedRedirect: "https://a-service.example.com:8443/",
1855− },
1856− {
1857− name: "proxied request with missing uri header would still redirect to desired redirect",
1858− url: "https://oauth.example.com/foo?bar",
1859− headers: map[string]string{
1860− "X-Forwarded-Proto": "https",
1861− "X-Forwarded-Host": "a-service.example.com",
1862− },
1863− reverseProxy: true,
1864− expectedRedirect: "https://a-service.example.com/foo?bar",
1865− },
1866− {
1867− name: "request with headers proxy not being set (and reverse proxy enabled) would still redirect to desired redirect",
1868− url: "https://oauth.example.com/foo?bar",
1869− headers: nil,
1870− reverseProxy: true,
1871− expectedRedirect: "/foo?bar",
1872− },
1873− {
1874− name: "proxied request with X-Auth-Request-Redirect being set outside of ProxyPrefix redirects to proxied URL",
1875− url: "https://oauth.example.com/foo/bar",
1876− headers: map[string]string{
1877− "X-Auth-Request-Redirect": "https://a-service.example.com/foo/bar",
1878− },
1879− reverseProxy: true,
1880− expectedRedirect: "https://a-service.example.com/foo/bar",
1881− },
1882− {
1883− name: "proxied request with rd query string redirects to proxied URL",
1884− url: "https://oauth.example.com/foo/bar?rd=https%3A%2F%2Fa%2Dservice%2Eexample%2Ecom%2Ffoo%2Fbar",
1885− headers: nil,
1886− reverseProxy: false,
1887− expectedRedirect: "https://a-service.example.com/foo/bar",
1888− },
1889− {
1890− name: "proxied request with rd query string and all headers set (and reverse proxy not enabled) redirects to proxied URL on rd query string",
1891− url: "https://oauth.example.com/foo/bar?rd=https%3A%2F%2Fa%2Dservice%2Eexample%2Ecom%2Ffoo%2Fjazz",
1892− headers: map[string]string{
1893− "X-Auth-Request-Redirect": "https://a-service.example.com/foo/baz",
1894− "X-Forwarded-Proto": "http",
1895− "X-Forwarded-Host": "another-service.example.com",
1896− "X-Forwarded-Uri": "/seasons/greetings",
1897− },
1898− reverseProxy: false,
1899− expectedRedirect: "https://a-service.example.com/foo/jazz",
1900− },
1901− {
1902− name: "proxied request with rd query string and some headers set redirects to proxied URL on rd query string",
1903− url: "https://oauth.example.com/foo/bar?rd=https%3A%2F%2Fa%2Dservice%2Eexample%2Ecom%2Ffoo%2Fbaz",
1904− headers: map[string]string{
1905− "X-Forwarded-Proto": "https",
1906− "X-Forwarded-Host": "another-service.example.com",
1907− "X-Forwarded-Uri": "/seasons/greetings",
1908− },
1909− reverseProxy: true,
1910− expectedRedirect: "https://a-service.example.com/foo/baz",
1911− },
1912− }
1913− for _, tt := range tests {
1914− t.Run(tt.name, func(t *testing.T) {
1915− req, _ := http.NewRequest("GET", tt.url, nil)
1916− for header, value := range tt.headers {
1917− if value != "" {
1918− req.Header.Add(header, value)
1919− }
1920− }
1921− req = middleware.AddRequestScope(req, &middleware.RequestScope{
1922− ReverseProxy: tt.reverseProxy,
1923− })
1924− redirect, err := proxy.getAppRedirect(req)
1925−
1926− assert.NoError(t, err)
1927− assert.Equal(t, tt.expectedRedirect, redirect)
1928− })
1929− }
1930−}
1931−
19321469type ajaxRequestTest struct {
19331470 opts *options.Options
19341471 proxy *OAuthProxy