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