Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

test(gw): improve CORS tests #8718

Merged
merged 1 commit into from
Aug 17, 2022
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
167 changes: 120 additions & 47 deletions test/sharness/t0112-gateway-cors.sh
Original file line number Diff line number Diff line change
@@ -1,57 +1,75 @@
#!/usr/bin/env bash
#
# Copyright (c) 2016 Marcin Rataj
# MIT Licensed; see the LICENSE file in this repository.
#

test_description="Test HTTP Gateway CORS Support"
test_description="Test CORS behavior on Gateway port"

. lib/test-lib.sh

test_init_ipfs

# Default config
test_expect_success "Default Gateway.HTTPHeaders config match expected values" '
cat <<EOF > expected
{
"Access-Control-Allow-Headers": [
"X-Requested-With",
"Range",
"User-Agent"
],
"Access-Control-Allow-Methods": [
"GET"
],
"Access-Control-Allow-Origin": [
"*"
]
}
EOF
ipfs config --json Gateway.HTTPHeaders > actual &&
test_cmp expected actual
'

test_launch_ipfs_daemon

thash='QmUNLLsPACCz1vLxQVkXqqLX5R1X345qqfHbsf67hvA3Nn'
thash='bafkqabtimvwgy3yk' # hello

# Gateway

# HTTP GET Request
test_expect_success "GET to Gateway succeeds" '
curl -svX GET "http://127.0.0.1:$GWAY_PORT/ipfs/$thash" >/dev/null 2>curl_output &&
curl -svX GET -H "Origin: https://example.com" "http://127.0.0.1:$GWAY_PORT/ipfs/$thash" >/dev/null 2>curl_output &&
cat curl_output
'

# GET Response from Gateway should contain CORS headers
test_expect_success "GET response for Gateway resource looks good" '
grep "< Access-Control-Allow-Origin: \*" curl_output &&
grep "< Access-Control-Allow-Methods: GET" curl_output &&
grep "< Access-Control-Allow-Headers: Range" curl_output &&
grep "< Access-Control-Expose-Headers: Content-Range" curl_output &&
grep "< Access-Control-Expose-Headers: Content-Length" curl_output &&
grep "< Access-Control-Expose-Headers: X-Ipfs-Path" curl_output &&
grep "< Access-Control-Expose-Headers: X-Ipfs-Roots" curl_output
test_should_contain "< Access-Control-Allow-Origin: \*" curl_output &&
test_should_contain "< Access-Control-Allow-Methods: GET" curl_output &&
test_should_contain "< Access-Control-Allow-Headers: Range" curl_output &&
test_should_contain "< Access-Control-Expose-Headers: Content-Range" curl_output &&
test_should_contain "< Access-Control-Expose-Headers: Content-Length" curl_output &&
test_should_contain "< Access-Control-Expose-Headers: X-Ipfs-Path" curl_output &&
test_should_contain "< Access-Control-Expose-Headers: X-Ipfs-Roots" curl_output
'

# HTTP OPTIONS Request
test_expect_success "OPTIONS to Gateway succeeds" '
curl -svX OPTIONS "http://127.0.0.1:$GWAY_PORT/ipfs/$thash" 2>curl_output &&
curl -svX OPTIONS -H "Origin: https://example.com" "http://127.0.0.1:$GWAY_PORT/ipfs/$thash" 2>curl_output &&
cat curl_output
'

# OPTION Response from Gateway should contain CORS headers
test_expect_success "OPTIONS response for Gateway resource looks good" '
grep "< Access-Control-Allow-Origin: \*" curl_output &&
grep "< Access-Control-Allow-Methods: GET" curl_output &&
grep "< Access-Control-Allow-Headers: Range" curl_output &&
grep "< Access-Control-Expose-Headers: Content-Range" curl_output &&
grep "< Access-Control-Expose-Headers: Content-Length" curl_output &&
grep "< Access-Control-Expose-Headers: X-Ipfs-Path" curl_output &&
grep "< Access-Control-Expose-Headers: X-Ipfs-Roots" curl_output
test_should_contain "< Access-Control-Allow-Origin: \*" curl_output &&
test_should_contain "< Access-Control-Allow-Methods: GET" curl_output &&
test_should_contain "< Access-Control-Allow-Headers: Range" curl_output &&
test_should_contain "< Access-Control-Expose-Headers: Content-Range" curl_output &&
test_should_contain "< Access-Control-Expose-Headers: Content-Length" curl_output &&
test_should_contain "< Access-Control-Expose-Headers: X-Ipfs-Path" curl_output &&
test_should_contain "< Access-Control-Expose-Headers: X-Ipfs-Roots" curl_output
'

test_kill_ipfs_daemon

# Change headers
# Test CORS safelisting of custom headers
test_expect_success "Can configure gateway headers" '
ipfs config --json Gateway.HTTPHeaders.Access-Control-Allow-Headers "[\"X-Custom1\"]" &&
ipfs config --json Gateway.HTTPHeaders.Access-Control-Expose-Headers "[\"X-Custom2\"]" &&
Expand All @@ -60,46 +78,101 @@ test_expect_success "Can configure gateway headers" '

test_launch_ipfs_daemon

test_expect_success "OPTIONS to Gateway succeeds" '
curl -svX OPTIONS "http://127.0.0.1:$GWAY_PORT/ipfs/$thash" 2>curl_output &&
test_expect_success "OPTIONS to Gateway without custom headers succeeds" '
curl -svX OPTIONS -H "Origin: https://example.com" "http://127.0.0.1:$GWAY_PORT/ipfs/$thash" 2>curl_output &&
cat curl_output
'
# Range and Content-Range are safelisted by default, and keeping them makes better devexp
# because it does not cause regressions in range requests made by JS
test_expect_success "Access-Control-Allow-Headers extends the implicit list" '
test_should_contain "< Access-Control-Allow-Headers: Range" curl_output &&
test_should_contain "< Access-Control-Allow-Headers: X-Custom1" curl_output &&
test_should_contain "< Access-Control-Expose-Headers: Content-Range" curl_output &&
test_should_contain "< Access-Control-Expose-Headers: Content-Length" curl_output &&
test_should_contain "< Access-Control-Expose-Headers: X-Ipfs-Path" curl_output &&
test_should_contain "< Access-Control-Expose-Headers: X-Ipfs-Roots" curl_output &&
test_should_contain "< Access-Control-Expose-Headers: X-Custom2" curl_output
'

test_expect_success "OPTIONS to Gateway with a custom header succeeds" '
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 &&
cat curl_output
'
test_expect_success "Access-Control-Allow-Headers extends the implicit list" '
test_should_not_contain "< Access-Control-Allow-Headers: X-Unexpected-Custom" curl_output &&
test_should_contain "< Access-Control-Allow-Headers: Range" curl_output &&
test_should_contain "< Access-Control-Allow-Headers: X-Custom1" curl_output &&
test_should_contain "< Access-Control-Expose-Headers: Content-Range" curl_output &&
test_should_contain "< Access-Control-Expose-Headers: X-Custom2" curl_output
'

# Origin is sensitive security perimeter, and we assume override should remove
# any implicit records
test_expect_success "Access-Control-Allow-Origin replaces the implicit list" '
test_should_contain "< Access-Control-Allow-Origin: localhost" curl_output
'

# Read-Only /api/v0 RPC API (legacy subset, exposed on the Gateway Port)
# TODO: we want to remove it, but for now this guards the legacy behavior to not go any further

# also check this, as due to legacy reasons Kubo exposes small subset of /api/v0 on GW port
test_expect_success "Assert the default API.HTTPHeaders config is empty" '
echo "{}" > expected &&
ipfs config --json API.HTTPHeaders > actual &&
test_cmp expected actual
'

test_expect_success "Access-Control-Allow-Headers extends" '
grep "< Access-Control-Allow-Headers: Range" curl_output &&
grep "< Access-Control-Allow-Headers: X-Custom1" curl_output &&
grep "< Access-Control-Expose-Headers: Content-Range" curl_output &&
grep "< Access-Control-Expose-Headers: Content-Length" curl_output &&
grep "< Access-Control-Expose-Headers: X-Ipfs-Path" curl_output &&
grep "< Access-Control-Expose-Headers: X-Ipfs-Roots" curl_output &&
grep "< Access-Control-Expose-Headers: X-Custom2" curl_output
# HTTP GET Request
test_expect_success "Default CORS GET to {gw}/api/v0" '
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
'
test_expect_success "Default CORS GET response from {gw}/api/v0 is 403 Forbidden and has no CORS headers" '
test_should_contain "HTTP/1.1 403 Forbidden" curl_output &&
test_should_not_contain "< Access-Control-" curl_output
'

test_expect_success "Access-Control-Allow-Origin replaces" '
grep "< Access-Control-Allow-Origin: localhost" curl_output
# HTTP OPTIONS Request
test_expect_success "Default OPTIONS to {gw}/api/v0" '
curl -svX OPTIONS -H "Origin: https://example.com" "http://127.0.0.1:$GWAY_PORT/api/v0/cat?arg=$thash" 2>curl_output
'
# OPTIONS Response from the API should NOT contain CORS headers
test_expect_success "OPTIONS response from {gw}/api/v0 has no CORS header" '
test_should_not_contain "< Access-Control-" curl_output
'
Copy link

@mar1n3r0 mar1n3r0 Feb 11, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Tests for the examples you have provided here : #7667

# HTTP OPTIONS Request
test_expect_success "OPTIONS to {gw}/api/v0 succeeds" '
  curl -svX OPTIONS -H "Origin: https://example.com" "http://127.0.0.1:$GWAY_PORT/api/v0/cat?arg=$thash" 2>curl_output
'
# OPTIONS Response from the API should NOT contain CORS headers
test_expect_success "OPTIONS response from {gw}/api/v0 has no CORS header" '
  cat curl_output &&
  grep -q "Access-Control-Allow-" curl_output && false || true
'

test_kill_ipfs_daemon

# Test CORS safelisting of standard headers
test_expect_success "Can configure standard CORS headers" '
  ipfs config --json Gateway.HTTPHeaders.Access-Control-Allow-Headers "[\"Content-Disposition\"]" &&
  ipfs config --json API.HTTPHeaders.Access-Control-Allow-Origin  "[\"https://example.com\"]" &&
  ipfs config --json API.HTTPHeaders.Access-Control-Allow-Methods "[\"PUT\", \"POST\"]"
'

test_launch_ipfs_daemon

# HTTP OPTIONS POST Request
test_expect_success "OPTIONS to {gw}/api/v0 CORS headers" '
  curl -svX OPTIONS -H "Access-Control-Request-Method: POST" -H "Access-Control-Request-Headers: Content-Disposition" -H "Origin: https://example.com" "http://127.0.0.1:$GWAY_PORT/api/v0/cat?arg=$thash" 2>curl_output
'

# OPTIONS POST Response from the API should contain custom CORS headers
test_expect_success "OPTIONS response from {gw}/api/v0 has custom CORS header" '
  test_expect_code 1 grep "< Access-Control-Allow-Headers: Content-Disposition" curl_output
'

# OPTIONS POST Response from the API should contain default CORS headers
test_expect_success "OPTIONS response from {gw}/api/v0 has default CORS headers " '
  grep "< Access-Control-Allow-Methods: POST" curl_output &&
  grep "< Access-Control-Allow-Origin: https://example.com" curl_output
'


# Read-Only API (at the Gateway Port)
test_kill_ipfs_daemon

# TODO: /api/v0 with CORS headers set in API.HTTPHeaders does not really work,
# as not all headers are correctly set. Below is only a basic regression test that documents
# current state. Fixing CORS on /api/v0 (RPC and Gateway port) is tracked in https://github.com/ipfs/kubo/issues/7667

test_expect_success "Manually set API.HTTPHeaders config to be as relaxed as Gateway.HTTPHeaders" "
ipfs config --json API.HTTPHeaders.Access-Control-Allow-Origin '[\"https://example.com\"]'
"
# TODO: ipfs config --json API.HTTPHeaders.Access-Control-Allow-Methods '[\"GET\",\"POST\"]' &&
# TODO: ipfs config --json API.HTTPHeaders.Access-Control-Allow-Headers '[\"X-Requested-With\", \"Range\", \"User-Agent\"]'

test_launch_ipfs_daemon

# HTTP GET Request
test_expect_success "GET to API succeeds" '
curl -svX GET "http://127.0.0.1:$GWAY_PORT/api/v0/cat?arg=$thash" >/dev/null 2>curl_output
test_expect_success "Manually relaxed CORS GET to {gw}/api/v0" '
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
'
# GET Response from the API should NOT contain CORS headers
# Blacklisting: https://github.com/ipfs/go-ipfs/blob/5d9ee59908099df3f7e85679f7384c98d4ac8111/commands/http/handler.go#L71-L82
# Rationale: https://github.com/ipfs/go-ipfs/pull/1529#issuecomment-125702347
test_expect_success "OPTIONS response for API looks good" '
grep -q "Access-Control-Allow-" curl_output && false || true
test_expect_success "Manually relaxed CORS GET response from {gw}/api/v0 is the same as Gateway" '
test_should_contain "HTTP/1.1 200 OK" curl_output &&
test_should_contain "< Access-Control-Allow-Origin: https://example.com" curl_output
'
# TODO: test_should_contain "< Access-Control-Allow-Methods: GET" curl_output

# HTTP OPTIONS Request
test_expect_success "OPTIONS to API succeeds" '
curl -svX OPTIONS "http://127.0.0.1:$GWAY_PORT/api/v0/cat?arg=$thash" 2>curl_output
test_expect_success "Manually relaxed OPTIONS to {gw}/api/v0" '
curl -svX OPTIONS -H "Origin: https://example.com" "http://127.0.0.1:$GWAY_PORT/api/v0/cat?arg=$thash" 2>curl_output
'
# OPTIONS Response from the API should NOT contain CORS headers
test_expect_success "OPTIONS response for API looks good" '
grep -q "Access-Control-Allow-" curl_output && false || true
test_expect_success "Manually relaxed OPTIONS response from {gw}/api/v0 is the same as Gateway" '
test_should_contain "< Access-Control-Allow-Origin: https://example.com" curl_output
'
# TODO: test_should_contain "< Access-Control-Allow-Methods: GET" curl_output

test_kill_ipfs_daemon

Expand Down