CORSと認証の境界をテストする
目標
許可されるプリフライトと拒否されるプリフライトを比較し、CORSを認証と誤解するリグレッションを防ぎます。
なぜ重要なのか
許可されていないOriginからのリクエストがサーバーで実行されたため、開発者はCORSライブラリのバグだと判断しました。しかし、ブラウザーの読み取り制限と、サーバーの権限検査は、別の責務でした。テスト名とアサーションが何を保証するのかを、正確に区別する必要があります。
ステップ
/root/work/test-cors-browser-boundary-lab/test_service.pyで、提供されたservice.pyの次の公開契約をテストしてください: origin(value)は、httpまたはhttpsのURLで、hostがあり、path・query・fragment・ユーザー情報がなければ、入力の文字列を返します。それ以外はValueErrorです。末尾の/もpathなので拒否します。正常な実装では成功し、この契約に違反する実装では、実際のテスト本文の失敗として検出する必要があります。前のステップのテストを維持したまま、test_関数を追加してください。
最初に1回だけ準備してください。既存のファイルは上書きしません。
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
-
/root/work/test-cors-browser-boundary-lab/test_service.pyで、提供されたservice.pyの次の公開契約をテストしてください: origins(values)は、各項目をoriginで検証した後、最初に出てきた順序で重複を除去した新しいリストです。正常な実装では成功し、この契約に違反する実装では、実際のテスト本文の失敗として検出する必要があります。前のステップのテストを維持したまま、test_関数を追加してください。 -
/root/work/test-cors-browser-boundary-lab/test_service.pyで、提供されたservice.pyの次の公開契約をテストしてください: methods(values)は、GET・POST・PUT・DELETE・OPTIONSだけを許可し、大文字に変換して重複を除去します。空のリストやそれ以外の値はValueErrorです。正常な実装では成功し、この契約に違反する実装では、実際のテスト本文の失敗として検出する必要があります。前のステップのテストを維持したまま、test_関数を追加してください。 -
/root/work/test-cors-browser-boundary-lab/test_service.pyで、提供されたservice.pyの次の公開契約をテストしてください: policy(allowed, credentials)は、credentialsがboolであることを確認します。allowedに'*'があればValueErrorで、{allow_origins:origins(allowed), allow_credentials:credentials}を返します。正常な実装では成功し、この契約に違反する実装では、実際のテスト本文の失敗として検出する必要があります。前のステップのテストを維持したまま、test_関数を追加してください。 -
/root/work/test-cors-browser-boundary-lab/test_service.pyで、提供されたservice.pyの次の公開契約をテストしてください: create_app(allowed, credentials=True)は、policyを検証し、CORSMiddlewareを設定したアプリです。GET/POSTだけを許可し、Content-TypeとX-Request-IDのリクエストヘッダーを許可し、X-Traceレスポンスヘッダーをexposeします。GET /dataは{ok:True}とX-Trace='trace-1'を返します。正常な実装では成功し、この契約に違反する実装では、実際のテスト本文の失敗として検出する必要があります。前のステップのテストを維持したまま、test_関数を追加してください。 -
/root/work/test-cors-browser-boundary-lab/test_service.pyで、提供されたservice.pyの次の公開契約をテストしてください: preflight_headers(source, method, requested='X-Request-ID')は、Origin、Access-Control-Request-Method、Access-Control-Request-Headersの3つのキーを持つ辞書です。methodは大文字です。正常な実装では成功し、この契約に違反する実装では、実際のテスト本文の失敗として検出する必要があります。前のステップのテストを維持したまま、test_関数を追加してください。 -
/root/work/test-cors-browser-boundary-lab/test_service.pyで、提供されたservice.pyの次の公開契約をテストしてください: preflight_status(app, source, method, requested='X-Request-ID')は、TestClientで/dataにOPTIONSリクエストを送り、HTTPステータスを返します。別のオリジン・DELETE・X-Secretヘッダーは400になる必要があります。正常な実装では成功し、この契約に違反する実装では、実際のテスト本文の失敗として検出する必要があります。前のステップのテストを維持したまま、test_関数を追加してください。 -
/root/work/test-cors-browser-boundary-lab/test_service.pyで、提供されたservice.pyの次の公開契約をテストしてください: cors_observation(app, source)は、GET /dataを送り、(ステータス、Access-Control-Allow-Originの値またはNone、JSON本文)を返します。許可されていないオリジンでも200の本文は実行されますが、許可オリジンのヘッダーはない必要があります。正常な実装では成功し、この契約に違反する実装では、実際のテスト本文の失敗として検出する必要があります。前のステップのテストを維持したまま、test_関数を追加してください。
参考
- インターネットやパッケージのインストールなしで、既存のlab-dev環境で行います。
- 各ステップは、45秒の採点バジェットの範囲内で実行されます。実際のsleepやネットワーク呼び出しを追加しないでください。
- 提出したテストは、別の一時フォルダーで、正常な実装と欠陥のある実装に対して実行されます。正常な実装では、実際に実行したテストがすべて成功し、欠陥のある実装では、テスト本文が失敗する必要があります。収集エラー、実行0件、すべてスキップ、強制終了は成功ではありません。pytestの基本機能と提供されたライブラリだけを使ってください。
- FastAPI公式ドキュメント・pytest公式ドキュメント・Python sqlite3
- 制限: TestClientはブラウザーではありません。CORSのレスポンスヘッダーとプリフライトは検査しますが、ブラウザー自体の読み取りブロックまでは実装していません。許可されていないOriginを送った通常のGETも、サーバーで実行されることがあります。機密性の高い操作は、別の認証・権限・CSRFのポリシーで保護する必要があります。提供された実装は読んでもかまいませんが、採点は別のコピーを使用します。ソースの文言の検査やファイルの修正で欠陥を回避せず、公開インターフェースの実行結果を検査してください。
オリジンの形式を検証する(テスト)
/root/work/test-cors-browser-boundary-lab/test_service.pyで、提供されたservice.pyの次の公開契約をテストしてください: origin(value)は、httpまたはhttpsのURLで、hostがあり、path・query・fragment・ユーザー情報がなければ、入力の文字列を返します。それ以外はValueErrorです。末尾の/もpathなので拒否します。正常な実装では成功し、この契約に違反する実装では、実際のテスト本文の失敗として検出する必要があります。前のステップのテストを維持したまま、test_関数を追加してください。
最初に1回だけ準備してください。既存のファイルは上書きしません。
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
URL全体をオリジンとして許可すると、パスやユーザー情報と混同してしまうことがあります。実装ファイルは修正しません。pytest.raisesで期待する例外を確認し、正常な結果には具体的な期待値をassertしてください。
保存後、bash /opt/lab/checks/test-cors-browser-boundary-lab/01-contract.shで確認してください。
重複したオリジンを除去する(テスト)
/root/work/test-cors-browser-boundary-lab/test_service.pyで、提供されたservice.pyの次の公開契約をテストしてください: origins(values)は、各項目をoriginで検証した後、最初に出てきた順序で重複を除去した新しいリストです。正常な実装では成功し、この契約に違反する実装では、実際のテスト本文の失敗として検出する必要があります。前のステップのテストを維持したまま、test_関数を追加してください。
許可リストは、文字列の部分一致ではなく、正確なオリジンのリストです。実装ファイルは修正しません。pytest.raisesで期待する例外を確認し、正常な結果には具体的な期待値をassertしてください。
保存後、bash /opt/lab/checks/test-cors-browser-boundary-lab/02-contract.shで確認してください。
メソッドを許可リストで制限する(テスト)
/root/work/test-cors-browser-boundary-lab/test_service.pyで、提供されたservice.pyの次の公開契約をテストしてください: methods(values)は、GET・POST・PUT・DELETE・OPTIONSだけを許可し、大文字に変換して重複を除去します。空のリストやそれ以外の値はValueErrorです。正常な実装では成功し、この契約に違反する実装では、実際のテスト本文の失敗として検出する必要があります。前のステップのテストを維持したまま、test_関数を追加してください。
許可していないPATCHや任意のメソッドを、黙って追加しません。実装ファイルは修正しません。pytest.raisesで期待する例外を確認し、正常な結果には具体的な期待値をassertしてください。
保存後、bash /opt/lab/checks/test-cors-browser-boundary-lab/03-contract.shで確認してください。
資格情報とアスタリスクを同時に許可しない(テスト)
/root/work/test-cors-browser-boundary-lab/test_service.pyで、提供されたservice.pyの次の公開契約をテストしてください: policy(allowed, credentials)は、credentialsがboolであることを確認します。allowedに'*'があればValueErrorで、{allow_origins:origins(allowed), allow_credentials:credentials}を返します。正常な実装では成功し、この契約に違反する実装では、実際のテスト本文の失敗として検出する必要があります。前のステップのテストを維持したまま、test_関数を追加してください。
このラボの明示的なポリシーは、資格情報の有無に関係なく、アスタリスクを受け付けません。実装ファイルは修正しません。pytest.raisesで期待する例外を確認し、正常な結果には具体的な期待値をassertしてください。
保存後、bash /opt/lab/checks/test-cors-browser-boundary-lab/04-contract.shで確認してください。
本物のCORSミドルウェアを付ける(テスト)
/root/work/test-cors-browser-boundary-lab/test_service.pyで、提供されたservice.pyの次の公開契約をテストしてください: create_app(allowed, credentials=True)は、policyを検証し、CORSMiddlewareを設定したアプリです。GET/POSTだけを許可し、Content-TypeとX-Request-IDのリクエストヘッダーを許可し、X-Traceレスポンスヘッダーをexposeします。GET /dataは{ok:True}とX-Trace='trace-1'を返します。正常な実装では成功し、この契約に違反する実装では、実際のテスト本文の失敗として検出する必要があります。前のステップのテストを維持したまま、test_関数を追加してください。
プリフライトと実際のレスポンスに、ヘッダーを手で別々に付けると、2つのポリシーが簡単にずれてしまいます。実装ファイルは修正しません。pytest.raisesで期待する例外を確認し、正常な結果には具体的な期待値をassertしてください。
保存後、bash /opt/lab/checks/test-cors-browser-boundary-lab/05-contract.shで確認してください。
プリフライトのリクエストを作る(テスト)
/root/work/test-cors-browser-boundary-lab/test_service.pyで、提供されたservice.pyの次の公開契約をテストしてください: preflight_headers(source, method, requested='X-Request-ID')は、Origin、Access-Control-Request-Method、Access-Control-Request-Headersの3つのキーを持つ辞書です。methodは大文字です。正常な実装では成功し、この契約に違反する実装では、実際のテスト本文の失敗として検出する必要があります。前のステップのテストを維持したまま、test_関数を追加してください。
実際のリクエストメソッドはOPTIONSで、検査したいメソッドは別のヘッダーにあります。実装ファイルは修正しません。pytest.raisesで期待する例外を確認し、正常な結果には具体的な期待値をassertしてください。
保存後、bash /opt/lab/checks/test-cors-browser-boundary-lab/06-contract.shで確認してください。
拒否の行列を計算する(テスト)
/root/work/test-cors-browser-boundary-lab/test_service.pyで、提供されたservice.pyの次の公開契約をテストしてください: preflight_status(app, source, method, requested='X-Request-ID')は、TestClientで/dataにOPTIONSリクエストを送り、HTTPステータスを返します。別のオリジン・DELETE・X-Secretヘッダーは400になる必要があります。正常な実装では成功し、この契約に違反する実装では、実際のテスト本文の失敗として検出する必要があります。前のステップのテストを維持したまま、test_関数を追加してください。
拒否の理由3種類を1つのリクエストに混ぜないようにして初めて、欠けているポリシーを見つけられます。実装ファイルは修正しません。pytest.raisesで期待する例外を確認し、正常な結果には具体的な期待値をassertしてください。
保存後、bash /opt/lab/checks/test-cors-browser-boundary-lab/07-contract.shで確認してください。
CORSと認証の違いを観察する(テスト)
/root/work/test-cors-browser-boundary-lab/test_service.pyで、提供されたservice.pyの次の公開契約をテストしてください: cors_observation(app, source)は、GET /dataを送り、(ステータス、Access-Control-Allow-Originの値またはNone、JSON本文)を返します。許可されていないオリジンでも200の本文は実行されますが、許可オリジンのヘッダーはない必要があります。正常な実装では成功し、この契約に違反する実装では、実際のテスト本文の失敗として検出する必要があります。前のステップのテストを維持したまま、test_関数を追加してください。
curlやサーバー間のリクエストは、ブラウザーのCORSによる読み取り制限に従いません。実装ファイルは修正しません。pytest.raisesで期待する例外を確認し、正常な結果には具体的な期待値をassertしてください。
保存後、bash /opt/lab/checks/test-cors-browser-boundary-lab/08-contract.shで確認してください。