Test the boundary between CORS and authentication
Goal
You compare allowed and rejected preflights and prevent regressions that mistake CORS for authentication.
Why it matters
When a request from a disallowed Origin ran on the server, a developer judged it to be a bug in the CORS library. But the browser's read restriction and the server's permission check were different responsibilities. You have to distinguish exactly what the test names and assertions guarantee.
Steps
- In
/root/work/test-cors-browser-boundary-lab/test_service.py, test the following public contract of the provided service.py: origin(value) returns the input string if it is an http or https URL that has a host and has no path, query, fragment, or user information. Otherwise it is ValueError. A trailing / is also a path, so it is rejected. It must pass against the correct implementation and be caught, through a failure in the body of an actual test, in an implementation that breaks this contract. Keep the tests from the earlier steps and add a test_ function.
Prepare this once at the start. Existing files are not overwritten.
mkdir -p /root/work/test-cors-browser-boundary-lab
test -e /root/work/test-cors-browser-boundary-lab/service.py || cp /opt/fixtures/ten_labs/test-cors-browser-boundary-lab/service.py /root/work/test-cors-browser-boundary-lab/service.py
test -e /root/work/test-cors-browser-boundary-lab/test_service.py || cp /opt/fixtures/ten_labs/test-cors-browser-boundary-lab/test_service.py /root/work/test-cors-browser-boundary-lab/test_service.py
cd /root/work/test-cors-browser-boundary-lab
-
In
/root/work/test-cors-browser-boundary-lab/test_service.py, test the following public contract of the provided service.py: origins(values) is a new list that validates each item with origin and then removes duplicates, keeping the order of first appearance. It must pass against the correct implementation and be caught, through a failure in the body of an actual test, in an implementation that breaks this contract. Keep the tests from the earlier steps and add a test_ function. -
In
/root/work/test-cors-browser-boundary-lab/test_service.py, test the following public contract of the provided service.py: methods(values) allows only GET, POST, PUT, DELETE, and OPTIONS, converts them to uppercase, and removes duplicates. An empty list or any other value is ValueError. It must pass against the correct implementation and be caught, through a failure in the body of an actual test, in an implementation that breaks this contract. Keep the tests from the earlier steps and add a test_ function. -
In
/root/work/test-cors-browser-boundary-lab/test_service.py, test the following public contract of the provided service.py: policy(allowed, credentials) checks that credentials is a bool. If allowed contains '*', it is ValueError, and otherwise it returns {allow_origins:origins(allowed), allow_credentials:credentials}. It must pass against the correct implementation and be caught, through a failure in the body of an actual test, in an implementation that breaks this contract. Keep the tests from the earlier steps and add a test_ function. -
In
/root/work/test-cors-browser-boundary-lab/test_service.py, test the following public contract of the provided service.py: create_app(allowed, credentials=True) is an app that validates the policy and configures CORSMiddleware. It allows only GET and POST, allows the Content-Type and X-Request-ID request headers, and exposes the X-Trace response header. GET /data returns {ok:True} with X-Trace='trace-1'. It must pass against the correct implementation and be caught, through a failure in the body of an actual test, in an implementation that breaks this contract. Keep the tests from the earlier steps and add a test_ function. -
In
/root/work/test-cors-browser-boundary-lab/test_service.py, test the following public contract of the provided service.py: preflight_headers(source, method, requested='X-Request-ID') is a dictionary with three keys: Origin, Access-Control-Request-Method, and Access-Control-Request-Headers. method is uppercase. It must pass against the correct implementation and be caught, through a failure in the body of an actual test, in an implementation that breaks this contract. Keep the tests from the earlier steps and add a test_ function. -
In
/root/work/test-cors-browser-boundary-lab/test_service.py, test the following public contract of the provided service.py: preflight_status(app, source, method, requested='X-Request-ID') sends an OPTIONS request to /data with TestClient and returns the HTTP status. A different origin, DELETE, and an X-Secret header must each give 400. It must pass against the correct implementation and be caught, through a failure in the body of an actual test, in an implementation that breaks this contract. Keep the tests from the earlier steps and add a test_ function. -
In
/root/work/test-cors-browser-boundary-lab/test_service.py, test the following public contract of the provided service.py: cors_observation(app, source) sends GET /data and returns (status, the Access-Control-Allow-Origin value or None, the JSON body). Even for an origin that is not allowed, the 200 body still runs, but the allow-origin header must be absent. It must pass against the correct implementation and be caught, through a failure in the body of an actual test, in an implementation that breaks this contract. Keep the tests from the earlier steps and add a test_ function.
Notes
- You work in the existing lab-dev environment with no internet and no package installation.
- Each step runs within a 45-second grading budget. Do not add real sleeps or network calls.
- The submitted tests are run in a separate temporary folder against the correct and defective implementations. Against the correct one, every test that actually runs must pass, and against a defective one, the body of a test must fail. A collection error, zero tests run, everything skipped, and forced termination are not a pass. Use only the basic pytest features and the provided libraries.
- FastAPI official documentation · pytest official documentation · Python sqlite3
- Limitation: TestClient is not a browser. It checks CORS response headers and preflights, but it does not implement the browser's own read blocking. Even an ordinary GET that sends a disallowed Origin can execute on the server. Sensitive actions must be protected by separate authentication, authorization, and CSRF policies. You may read the provided implementation, but grading uses a separate copy. Do not work around a defect by checking the wording of the source or by modifying files; check the execution results of the public interface.
Validate the origin format — test
In /root/work/test-cors-browser-boundary-lab/test_service.py, test the following public contract of the provided service.py: origin(value) returns the input string if it is an http or https URL that has a host and has no path, query, fragment, or user information. Otherwise it is ValueError. A trailing / is also a path, so it is rejected. It must pass against the correct implementation and be caught, through a failure in the body of an actual test, in an implementation that breaks this contract. Keep the tests from the earlier steps and add a test_ function.
Prepare this once at the start. Existing files are not overwritten.
mkdir -p /root/work/test-cors-browser-boundary-lab
test -e /root/work/test-cors-browser-boundary-lab/service.py || cp /opt/fixtures/ten_labs/test-cors-browser-boundary-lab/service.py /root/work/test-cors-browser-boundary-lab/service.py
test -e /root/work/test-cors-browser-boundary-lab/test_service.py || cp /opt/fixtures/ten_labs/test-cors-browser-boundary-lab/test_service.py /root/work/test-cors-browser-boundary-lab/test_service.py
cd /root/work/test-cors-browser-boundary-lab
If you allow a whole URL as an origin, you can confuse a path or user information with the origin. Do not modify the implementation file. Use pytest.raises to check the expected exception, and assert a concrete expected value for the normal result.
After saving, check with bash /opt/lab/checks/test-cors-browser-boundary-lab/01-contract.sh.
Remove duplicate origins — test
In /root/work/test-cors-browser-boundary-lab/test_service.py, test the following public contract of the provided service.py: origins(values) is a new list that validates each item with origin and then removes duplicates, keeping the order of first appearance. It must pass against the correct implementation and be caught, through a failure in the body of an actual test, in an implementation that breaks this contract. Keep the tests from the earlier steps and add a test_ function.
The allow list is a list of exact origins, not a substring match on strings. Do not modify the implementation file. Use pytest.raises to check the expected exception, and assert a concrete expected value for the normal result.
After saving, check with bash /opt/lab/checks/test-cors-browser-boundary-lab/02-contract.sh.
Limit the methods to an allow list — test
In /root/work/test-cors-browser-boundary-lab/test_service.py, test the following public contract of the provided service.py: methods(values) allows only GET, POST, PUT, DELETE, and OPTIONS, converts them to uppercase, and removes duplicates. An empty list or any other value is ValueError. It must pass against the correct implementation and be caught, through a failure in the body of an actual test, in an implementation that breaks this contract. Keep the tests from the earlier steps and add a test_ function.
Do not quietly add PATCH or arbitrary methods that were not allowed. Do not modify the implementation file. Use pytest.raises to check the expected exception, and assert a concrete expected value for the normal result.
After saving, check with bash /opt/lab/checks/test-cors-browser-boundary-lab/03-contract.sh.
Do not allow credentials together with an asterisk — test
In /root/work/test-cors-browser-boundary-lab/test_service.py, test the following public contract of the provided service.py: policy(allowed, credentials) checks that credentials is a bool. If allowed contains '*', it is ValueError, and otherwise it returns {allow_origins:origins(allowed), allow_credentials:credentials}. It must pass against the correct implementation and be caught, through a failure in the body of an actual test, in an implementation that breaks this contract. Keep the tests from the earlier steps and add a test_ function.
The explicit policy of this lab does not accept an asterisk regardless of whether credentials are allowed. Do not modify the implementation file. Use pytest.raises to check the expected exception, and assert a concrete expected value for the normal result.
After saving, check with bash /opt/lab/checks/test-cors-browser-boundary-lab/04-contract.sh.
Attach the real CORS middleware — test
In /root/work/test-cors-browser-boundary-lab/test_service.py, test the following public contract of the provided service.py: create_app(allowed, credentials=True) is an app that validates the policy and configures CORSMiddleware. It allows only GET and POST, allows the Content-Type and X-Request-ID request headers, and exposes the X-Trace response header. GET /data returns {ok:True} with X-Trace='trace-1'. It must pass against the correct implementation and be caught, through a failure in the body of an actual test, in an implementation that breaks this contract. Keep the tests from the earlier steps and add a test_ function.
If you attach headers by hand separately to the preflight and to the real response, the two policies easily drift apart. Do not modify the implementation file. Use pytest.raises to check the expected exception, and assert a concrete expected value for the normal result.
After saving, check with bash /opt/lab/checks/test-cors-browser-boundary-lab/05-contract.sh.
Build the preflight request — test
In /root/work/test-cors-browser-boundary-lab/test_service.py, test the following public contract of the provided service.py: preflight_headers(source, method, requested='X-Request-ID') is a dictionary with three keys: Origin, Access-Control-Request-Method, and Access-Control-Request-Headers. method is uppercase. It must pass against the correct implementation and be caught, through a failure in the body of an actual test, in an implementation that breaks this contract. Keep the tests from the earlier steps and add a test_ function.
The method of the actual request is OPTIONS, and the method you want to check is in a separate header. Do not modify the implementation file. Use pytest.raises to check the expected exception, and assert a concrete expected value for the normal result.
After saving, check with bash /opt/lab/checks/test-cors-browser-boundary-lab/06-contract.sh.
Compute the rejection matrix — test
In /root/work/test-cors-browser-boundary-lab/test_service.py, test the following public contract of the provided service.py: preflight_status(app, source, method, requested='X-Request-ID') sends an OPTIONS request to /data with TestClient and returns the HTTP status. A different origin, DELETE, and an X-Secret header must each give 400. It must pass against the correct implementation and be caught, through a failure in the body of an actual test, in an implementation that breaks this contract. Keep the tests from the earlier steps and add a test_ function.
Do not mix the three kinds of rejection reasons into one request, or you cannot find the missing policy. Do not modify the implementation file. Use pytest.raises to check the expected exception, and assert a concrete expected value for the normal result.
After saving, check with bash /opt/lab/checks/test-cors-browser-boundary-lab/07-contract.sh.
Observe the difference between CORS and authentication — test
In /root/work/test-cors-browser-boundary-lab/test_service.py, test the following public contract of the provided service.py: cors_observation(app, source) sends GET /data and returns (status, the Access-Control-Allow-Origin value or None, the JSON body). Even for an origin that is not allowed, the 200 body still runs, but the allow-origin header must be absent. It must pass against the correct implementation and be caught, through a failure in the body of an actual test, in an implementation that breaks this contract. Keep the tests from the earlier steps and add a test_ function.
curl and server-to-server requests do not follow the browser's CORS read restriction. Do not modify the implementation file. Use pytest.raises to check the expected exception, and assert a concrete expected value for the normal result.
After saving, check with bash /opt/lab/checks/test-cors-browser-boundary-lab/08-contract.sh.