@cryptotaxi247 / kubo / commits / 84ed0ec59

test(gw): cors preflight with custom hearder

This cleans up old CORS tests and adds more resolution (proper Origin test, testing custom header behavior) It also adds basic regression tests for /api/v0 subset exposed on Gateway port.

Marcin Rataj committed Feb 4, 2022 at 20:06 UTC 84ed0ec59f2472e8ca4ef7f5f3f4027fe58b10ac
1 file changed +120 -47
test/sharness/t0112-gateway-cors.sh
+120 -47
@@ -1,57 +1,75 @@
1 #!/usr/bin/env bash
2 -#
3 -# Copyright (c) 2016 Marcin Rataj
4 -# MIT Licensed; see the LICENSE file in this repository.
5 -#
2
7 -test_description="Test HTTP Gateway CORS Support"
3 +test_description="Test CORS behavior on Gateway port"
4
5 . lib/test-lib.sh
6
7 test_init_ipfs
8 +
9 +# Default config
10 +test_expect_success "Default Gateway.HTTPHeaders config match expected values" '
11 +cat <<EOF > expected
12 +{
13 + "Access-Control-Allow-Headers": [
14 + "X-Requested-With",
15 + "Range",
16 + "User-Agent"
17 + ],
18 + "Access-Control-Allow-Methods": [
19 + "GET"
20 + ],
21 + "Access-Control-Allow-Origin": [
22 + "*"
23 + ]
24 +}
25 +EOF
26 + ipfs config --json Gateway.HTTPHeaders > actual &&
27 + test_cmp expected actual
28 +'
29 +
30 test_launch_ipfs_daemon
31
14 -thash='QmUNLLsPACCz1vLxQVkXqqLX5R1X345qqfHbsf67hvA3Nn'
32 +thash='bafkqabtimvwgy3yk' # hello
33
34 # Gateway
35
36 # HTTP GET Request
37 test_expect_success "GET to Gateway succeeds" '
20 - curl -svX GET "http://127.0.0.1:$GWAY_PORT/ipfs/$thash" >/dev/null 2>curl_output &&
38 + curl -svX GET -H "Origin: https://example.com" "http://127.0.0.1:$GWAY_PORT/ipfs/$thash" >/dev/null 2>curl_output &&
39 cat curl_output
40 '
41
42 # GET Response from Gateway should contain CORS headers
43 test_expect_success "GET response for Gateway resource looks good" '
26 - grep "< Access-Control-Allow-Origin: \*" curl_output &&
27 - grep "< Access-Control-Allow-Methods: GET" curl_output &&
28 - grep "< Access-Control-Allow-Headers: Range" curl_output &&
29 - grep "< Access-Control-Expose-Headers: Content-Range" curl_output &&
30 - grep "< Access-Control-Expose-Headers: Content-Length" curl_output &&
31 - grep "< Access-Control-Expose-Headers: X-Ipfs-Path" curl_output &&
32 - grep "< Access-Control-Expose-Headers: X-Ipfs-Roots" curl_output
44 + test_should_contain "< Access-Control-Allow-Origin: \*" curl_output &&
45 + test_should_contain "< Access-Control-Allow-Methods: GET" curl_output &&
46 + test_should_contain "< Access-Control-Allow-Headers: Range" curl_output &&
47 + test_should_contain "< Access-Control-Expose-Headers: Content-Range" curl_output &&
48 + test_should_contain "< Access-Control-Expose-Headers: Content-Length" curl_output &&
49 + test_should_contain "< Access-Control-Expose-Headers: X-Ipfs-Path" curl_output &&
50 + test_should_contain "< Access-Control-Expose-Headers: X-Ipfs-Roots" curl_output
51 '
52
53 # HTTP OPTIONS Request
54 test_expect_success "OPTIONS to Gateway succeeds" '
37 - curl -svX OPTIONS "http://127.0.0.1:$GWAY_PORT/ipfs/$thash" 2>curl_output &&
55 + curl -svX OPTIONS -H "Origin: https://example.com" "http://127.0.0.1:$GWAY_PORT/ipfs/$thash" 2>curl_output &&
56 cat curl_output
57 '
58
59 # OPTION Response from Gateway should contain CORS headers
60 test_expect_success "OPTIONS response for Gateway resource looks good" '
43 - grep "< Access-Control-Allow-Origin: \*" curl_output &&
44 - grep "< Access-Control-Allow-Methods: GET" curl_output &&
45 - grep "< Access-Control-Allow-Headers: Range" curl_output &&
46 - grep "< Access-Control-Expose-Headers: Content-Range" curl_output &&
47 - grep "< Access-Control-Expose-Headers: Content-Length" curl_output &&
48 - grep "< Access-Control-Expose-Headers: X-Ipfs-Path" curl_output &&
49 - grep "< Access-Control-Expose-Headers: X-Ipfs-Roots" curl_output
61 + test_should_contain "< Access-Control-Allow-Origin: \*" curl_output &&
62 + test_should_contain "< Access-Control-Allow-Methods: GET" curl_output &&
63 + test_should_contain "< Access-Control-Allow-Headers: Range" curl_output &&
64 + test_should_contain "< Access-Control-Expose-Headers: Content-Range" curl_output &&
65 + test_should_contain "< Access-Control-Expose-Headers: Content-Length" curl_output &&
66 + test_should_contain "< Access-Control-Expose-Headers: X-Ipfs-Path" curl_output &&
67 + test_should_contain "< Access-Control-Expose-Headers: X-Ipfs-Roots" curl_output
68 '
69
70 test_kill_ipfs_daemon
71
54 -# Change headers
72 +# Test CORS safelisting of custom headers
73 test_expect_success "Can configure gateway headers" '
74 ipfs config --json Gateway.HTTPHeaders.Access-Control-Allow-Headers "[\"X-Custom1\"]" &&
75 ipfs config --json Gateway.HTTPHeaders.Access-Control-Expose-Headers "[\"X-Custom2\"]" &&
@@ -60,46 +78,101 @@ test_expect_success "Can configure gateway headers" '
78
79 test_launch_ipfs_daemon
80
63 -test_expect_success "OPTIONS to Gateway succeeds" '
64 - curl -svX OPTIONS "http://127.0.0.1:$GWAY_PORT/ipfs/$thash" 2>curl_output &&
81 +test_expect_success "OPTIONS to Gateway without custom headers succeeds" '
82 + curl -svX OPTIONS -H "Origin: https://example.com" "http://127.0.0.1:$GWAY_PORT/ipfs/$thash" 2>curl_output &&
83 + cat curl_output
84 +'
85 +# Range and Content-Range are safelisted by default, and keeping them makes better devexp
86 +# because it does not cause regressions in range requests made by JS
87 +test_expect_success "Access-Control-Allow-Headers extends the implicit list" '
88 + test_should_contain "< Access-Control-Allow-Headers: Range" curl_output &&
89 + test_should_contain "< Access-Control-Allow-Headers: X-Custom1" curl_output &&
90 + test_should_contain "< Access-Control-Expose-Headers: Content-Range" curl_output &&
91 + test_should_contain "< Access-Control-Expose-Headers: Content-Length" curl_output &&
92 + test_should_contain "< Access-Control-Expose-Headers: X-Ipfs-Path" curl_output &&
93 + test_should_contain "< Access-Control-Expose-Headers: X-Ipfs-Roots" curl_output &&
94 + test_should_contain "< Access-Control-Expose-Headers: X-Custom2" curl_output
95 +'
96 +
97 +test_expect_success "OPTIONS to Gateway with a custom header succeeds" '
98 + curl -svX OPTIONS -H "Origin: https://example.com" -H "Access-Control-Request-Headers: X-Unexpected-Custom" "http://127.0.0.1:$GWAY_PORT/ipfs/$thash" 2>curl_output &&
99 cat curl_output
100 '
101 +test_expect_success "Access-Control-Allow-Headers extends the implicit list" '
102 + test_should_not_contain "< Access-Control-Allow-Headers: X-Unexpected-Custom" curl_output &&
103 + test_should_contain "< Access-Control-Allow-Headers: Range" curl_output &&
104 + test_should_contain "< Access-Control-Allow-Headers: X-Custom1" curl_output &&
105 + test_should_contain "< Access-Control-Expose-Headers: Content-Range" curl_output &&
106 + test_should_contain "< Access-Control-Expose-Headers: X-Custom2" curl_output
107 +'
108 +
109 +# Origin is sensitive security perimeter, and we assume override should remove
110 +# any implicit records
111 +test_expect_success "Access-Control-Allow-Origin replaces the implicit list" '
112 + test_should_contain "< Access-Control-Allow-Origin: localhost" curl_output
113 +'
114 +
115 +# Read-Only /api/v0 RPC API (legacy subset, exposed on the Gateway Port)
116 +# TODO: we want to remove it, but for now this guards the legacy behavior to not go any further
117 +
118 +# also check this, as due to legacy reasons Kubo exposes small subset of /api/v0 on GW port
119 +test_expect_success "Assert the default API.HTTPHeaders config is empty" '
120 + echo "{}" > expected &&
121 + ipfs config --json API.HTTPHeaders > actual &&
122 + test_cmp expected actual
123 +'
124
68 -test_expect_success "Access-Control-Allow-Headers extends" '
69 - grep "< Access-Control-Allow-Headers: Range" curl_output &&
70 - grep "< Access-Control-Allow-Headers: X-Custom1" curl_output &&
71 - grep "< Access-Control-Expose-Headers: Content-Range" curl_output &&
72 - grep "< Access-Control-Expose-Headers: Content-Length" curl_output &&
73 - grep "< Access-Control-Expose-Headers: X-Ipfs-Path" curl_output &&
74 - grep "< Access-Control-Expose-Headers: X-Ipfs-Roots" curl_output &&
75 - grep "< Access-Control-Expose-Headers: X-Custom2" curl_output
125 +# HTTP GET Request
126 +test_expect_success "Default CORS GET to {gw}/api/v0" '
127 + curl -svX GET -H "Origin: https://example.com" "http://127.0.0.1:$GWAY_PORT/api/v0/cat?arg=$thash" >/dev/null 2>curl_output
128 +'
129 +test_expect_success "Default CORS GET response from {gw}/api/v0 is 403 Forbidden and has no CORS headers" '
130 + test_should_contain "HTTP/1.1 403 Forbidden" curl_output &&
131 + test_should_not_contain "< Access-Control-" curl_output
132 '
133
78 -test_expect_success "Access-Control-Allow-Origin replaces" '
79 - grep "< Access-Control-Allow-Origin: localhost" curl_output
134 +# HTTP OPTIONS Request
135 +test_expect_success "Default OPTIONS to {gw}/api/v0" '
136 + curl -svX OPTIONS -H "Origin: https://example.com" "http://127.0.0.1:$GWAY_PORT/api/v0/cat?arg=$thash" 2>curl_output
137 +'
138 +# OPTIONS Response from the API should NOT contain CORS headers
139 +test_expect_success "OPTIONS response from {gw}/api/v0 has no CORS header" '
140 + test_should_not_contain "< Access-Control-" curl_output
141 '
142
82 -# Read-Only API (at the Gateway Port)
143 +test_kill_ipfs_daemon
144 +
145 +# TODO: /api/v0 with CORS headers set in API.HTTPHeaders does not really work,
146 +# as not all headers are correctly set. Below is only a basic regression test that documents
147 +# current state. Fixing CORS on /api/v0 (RPC and Gateway port) is tracked in https://github.com/ipfs/kubo/issues/7667
148 +
149 +test_expect_success "Manually set API.HTTPHeaders config to be as relaxed as Gateway.HTTPHeaders" "
150 + ipfs config --json API.HTTPHeaders.Access-Control-Allow-Origin '[\"https://example.com\"]'
151 +"
152 +# TODO: ipfs config --json API.HTTPHeaders.Access-Control-Allow-Methods '[\"GET\",\"POST\"]' &&
153 +# TODO: ipfs config --json API.HTTPHeaders.Access-Control-Allow-Headers '[\"X-Requested-With\", \"Range\", \"User-Agent\"]'
154 +
155 +test_launch_ipfs_daemon
156
157 # HTTP GET Request
85 -test_expect_success "GET to API succeeds" '
86 - curl -svX GET "http://127.0.0.1:$GWAY_PORT/api/v0/cat?arg=$thash" >/dev/null 2>curl_output
158 +test_expect_success "Manually relaxed CORS GET to {gw}/api/v0" '
159 + curl -svX GET -H "Origin: https://example.com" "http://127.0.0.1:$GWAY_PORT/api/v0/cat?arg=$thash" >/dev/null 2>curl_output
160 '
88 -# GET Response from the API should NOT contain CORS headers
89 -# Blacklisting: https://github.com/ipfs/go-ipfs/blob/5d9ee59908099df3f7e85679f7384c98d4ac8111/commands/http/handler.go#L71-L82
90 -# Rationale: https://github.com/ipfs/go-ipfs/pull/1529#issuecomment-125702347
91 -test_expect_success "OPTIONS response for API looks good" '
92 - grep -q "Access-Control-Allow-" curl_output && false || true
161 +test_expect_success "Manually relaxed CORS GET response from {gw}/api/v0 is the same as Gateway" '
162 + test_should_contain "HTTP/1.1 200 OK" curl_output &&
163 + test_should_contain "< Access-Control-Allow-Origin: https://example.com" curl_output
164 '
165 +# TODO: test_should_contain "< Access-Control-Allow-Methods: GET" curl_output
166
167 # HTTP OPTIONS Request
96 -test_expect_success "OPTIONS to API succeeds" '
97 - curl -svX OPTIONS "http://127.0.0.1:$GWAY_PORT/api/v0/cat?arg=$thash" 2>curl_output
168 +test_expect_success "Manually relaxed OPTIONS to {gw}/api/v0" '
169 + curl -svX OPTIONS -H "Origin: https://example.com" "http://127.0.0.1:$GWAY_PORT/api/v0/cat?arg=$thash" 2>curl_output
170 '
171 # OPTIONS Response from the API should NOT contain CORS headers
100 -test_expect_success "OPTIONS response for API looks good" '
101 - grep -q "Access-Control-Allow-" curl_output && false || true
172 +test_expect_success "Manually relaxed OPTIONS response from {gw}/api/v0 is the same as Gateway" '
173 + test_should_contain "< Access-Control-Allow-Origin: https://example.com" curl_output
174 '
175 +# TODO: test_should_contain "< Access-Control-Allow-Methods: GET" curl_output
176
177 test_kill_ipfs_daemon
178