Test the boundary between CORS and authentication
한국어 원문으로 표시합니다.
목표
허용·거절 프리플라이트를 비교하고 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_ 함수를 추가하세요.
처음 한 번 준비하세요. 기존 파일은 덮어쓰지 않습니다.
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 세 키를 가진 딕셔너리입니다. 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_ 함수를 추가하세요.
처음 한 번 준비하세요. 기존 파일은 덮어쓰지 않습니다.
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로 예상 예외를 확인하고 정상 결과에는 구체적인 예상값을 단언하세요.
저장 후 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로 예상 예외를 확인하고 정상 결과에는 구체적인 예상값을 단언하세요.
저장 후 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로 예상 예외를 확인하고 정상 결과에는 구체적인 예상값을 단언하세요.
저장 후 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로 예상 예외를 확인하고 정상 결과에는 구체적인 예상값을 단언하세요.
저장 후 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_ 함수를 추가하세요.
preflight와 실제 응답에 헤더를 손으로 따로 붙이면 두 정책이 쉽게 어긋납니다. 구현 파일은 수정하지 않습니다. pytest.raises로 예상 예외를 확인하고 정상 결과에는 구체적인 예상값을 단언하세요.
저장 후 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 세 키를 가진 딕셔너리입니다. method는 대문자입니다. 정상 구현에서는 통과하고 이 계약을 어긴 구현에서는 실제 테스트 본문의 실패로 검출해야 합니다. 앞 단계 테스트를 유지하며 test_ 함수를 추가하세요.
실제 요청 메서드는 OPTIONS이며 검사하려는 메서드는 별도 헤더에 있습니다. 구현 파일은 수정하지 않습니다. pytest.raises로 예상 예외를 확인하고 정상 결과에는 구체적인 예상값을 단언하세요.
저장 후 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_ 함수를 추가하세요.
거절 사유 세 종류를 한 요청에 섞지 않아야 빠진 정책을 찾을 수 있습니다. 구현 파일은 수정하지 않습니다. pytest.raises로 예상 예외를 확인하고 정상 결과에는 구체적인 예상값을 단언하세요.
저장 후 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로 예상 예외를 확인하고 정상 결과에는 구체적인 예상값을 단언하세요.
저장 후 bash /opt/lab/checks/test-cors-browser-boundary-lab/08-contract.sh로 확인하세요.