Skip to content

test(byok): check that the operator's key leaves by no way in own-credential mode - #1051

Merged
yeongseon merged 1 commit into
mainfrom
test/issue-990-operator-key-leak
Oct 6, 2026
Merged

yeongseon merged 1 commit into
mainfrom
test/issue-990-operator-key-leak

Conversation

@Eomdahyeon

Copy link
Copy Markdown
Collaborator

Closes #990

#1050 리뷰가 남긴 것: "#990 은 누출 테스트(완료 조건 3)까지 보고 닫을지 정하면 됩니다". 그 테스트다. 코드 변경은 없다.

이슈의 세 조건과 근거

  1. 키 없는 빌드가 거부된다 — test_require_own_credential.py::test_build_is_refused_before_any_client_exists (fix(byok): keep the operator's environment key out of an own-credential client #1014). 이 PR 의 두 번째 테스트도 실제 client 로 같은 것을 본다.
  2. env_keys 가 없는 kpubdata 로는 이 모드로 기동하지 않는다 — test_serve_refuses_to_start_when_it_cannot_keep_the_promise, test_a_kpubdata_without_env_keys_is_refused_not_silently_used (fix(byok): keep the operator's environment key out of an own-credential client #1014).
  3. 누출 테스트: 운영자 키 문자열이 요청·로그·매니페스트 어디에도 없다 — 이 PR. 지금까지의 테스트는 client 가 무엇으로 만들어지는지(env_keys=False, 키 조회가 None)를 봤고, 실제로 무엇이 나가는지는 보지 않았다. 0.8.0 에서는 볼 수 없었다.

테스트 (tests/unit/test_operator_key_never_leaves.py)

진짜 kpubdata Client(cli._create_client)와 진짜 HttpTransport 로 BuilderService.build 를 돌리고, 소켓(httpx.Client.send)만 바꿨다. 운영자 키는 kpubdata 가 읽는 이름 셋(KPUBDATA_DATAGO_API_KEY, DATAGO_API_KEY, KPUBDATA_API_KEY) 모두에 canary 값으로 넣었다.

  • 자기 키가 있는 요청자의 빌드: 요청이 실제로 나갔고, 나간 요청마다 요청자의 키가 있고 운영자 키는 없다. 응답 본문, DEBUG 로그(본문과 레코드의 extra), run 디렉터리의 모든 파일(매니페스트·스냅샷·이벤트·산출물)에 운영자 키가 없다. 요청자의 키도 로그와 디스크에 없다.
  • 키가 없는 요청자: 403 provider_credential_required 이고 요청이 하나도 나가지 않는다(나가면 테스트가 그 자리에서 실패한다).
  • 측정이 실제로 보는지: 스위치를 끄면 같은 키 없는 빌드가 운영자 키로 나가고, 위 캡처에 그 키가 잡힌다. 이것이 없으면 "없다"는 단언들이 아무것도 안 나가도 통과한다.

데이터셋은 datago.apt_trade 를 썼다 — 0.9.0 에서 약관이 allowed 라 키 말고는 빌드를 막는 것이 없다(air_quality 는 이제 forbidden).

검증

  • pytest tests/unit/test_operator_key_never_leaves.py tests/unit/test_require_own_credential.py → 14 passed.
  • ruff check, ruff format 통과. # type: ignore 없음.
  • kpubdata 가 env_keys 를 받지 않으면 파일 전체가 skip 된다(핀이 0.9 라인이라 CI 에서는 돈다).
  • 전체 스위트는 CI 에 맡긴다. 실제 provider 호출은 아니다 — 응답은 표준 envelope 한 건을 흉내 낸 것이다.

🤖 Generated with Claude Code

…dential mode

The tests for this mode checked what the client is built with. With kpubdata
0.9 pinned, run a build through a real Client and transport with only the
socket replaced, and look at what leaves: the operator's key is in no request,
log record or file under the run; the requester's own key is what was sent; a
requester without one sends nothing.

Closes #990

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

@yeongseon yeongseon left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

diff 를 읽었고 승인합니다. 코드 변경 없이 테스트 한 파일과 CHANGELOG 한 줄입니다.

확인한 것:

  • 스킵되지 않고 돌았습니다. 파일 전체에 skipif 가 걸려 있어 CI 로그를 봤습니다 — Tests (py3.12) 에서 tests/unit/test_operator_key_never_leaves.py ... 세 건이 통과로 찍혔고, 스킵 10건 목록에 이 파일은 없습니다(4646 passed, 10 skipped).
  • "없다" 가 빈 통과가 아닙니다. 첫 테스트는 assert sent 와 요청자 키가 요청마다 있는 것을 먼저 보고, 세 번째 테스트는 스위치를 끄면 같은 캡처에 운영자 키가 잡히는 것을 보입니다. 캡처가 URL 과 헤더를 함께 봅니다.
  • 키 없는 요청자는 소켓에 닿으면 그 자리에서 실패하게 돼 있습니다.

알아 둘 것 (막지 않음):

  • 캡처는 URL 과 헤더만 봅니다. 요청 본문은 보지 않습니다 — datago 는 GET 이라 지금은 차이가 없고, 본문에 키를 싣는 provider 가 이 모드에 들어오면 그때 넓혀야 합니다.
  • provider 하나(datago), 데이터셋 하나(apt_trade)로 본 것입니다. 다른 provider 의 키 이름(KPUBDATA_<PROVIDER>_API_KEY)까지 본 것은 아닙니다.

이것으로 #990 의 완료 조건 셋이 모두 테스트로 남으므로 Closes #990 에 동의합니다.

@yeongseon
yeongseon merged commit ecc75f5 into main Oct 6, 2026
20 checks passed
yeongseon added a commit that referenced this pull request Oct 6, 2026
…play (#1052)

## 문제

#1051 을 머지한 뒤 `main` 의 `Cross-repo contract` 가 빨갛다 (run 37410221619,
`ecc75f5`).

```
FAILED tests/unit/test_operator_key_never_leaves.py::test_the_operators_key_is_in_no_request_log_or_file
FAILED tests/unit/test_operator_key_never_leaves.py::test_the_measurement_sees_the_operators_key_when_the_switch_is_off
kpubdata.exceptions.InvalidRequestError: replay 매칭 실패: datago.apt_trade (... 'lawd_cd': '11680', 'deal_ymd': '202401')
```

그 잡은 `KPUBDATA_REPLAY_DIR` 을 준다. 그러면 kpubdata 가 HTTP 대신 기록된 fixture 를
재생하므로, 이 테스트가 바꿔 끼운 소켓(`httpx.Client.send`)에 닿지 않는다. 이 워크플로는 경로 필터 때문에
#1051 의 PR 에서는 돌지 않았고, 내가 승인할 때 그 점을 보지 않았다.

## 변경

fixture 에서 `KPUBDATA_REPLAY_DIR` 을 지운다 — `test_credential_hosts.py`,
`test_canary_leak_gate.py` 가 같은 이유로 하는 것과 같다. 한 줄과 주석.

## 검증

- `ruff check`, `ruff format --check` 통과.
- 로컬에서 pytest 는 돌리지 못했다. 이 브랜치에서 `Cross-repo contract` 를 수동 실행했고 결과를 아래
코멘트에 붙인다.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

fix(byok): REQUIRE_OWN_PROVIDER_CREDENTIAL still runs a keyless request on the operator's environment key

2 participants