fix(byok): keep the operator's environment key out of an own-credential client - #1014
Merged
Merged
Conversation
Eomdahyeon
force-pushed
the
fix/issue-990-own-credential-env-keys
branch
from
October 4, 2026 13:35
725a537 to
6e6721c
Compare
yeongseon
reviewed
Oct 4, 2026
yeongseon
left a comment
Collaborator
There was a problem hiding this comment.
리뷰 (코멘트, 승인 아님 — R3).
diff 를 읽었고 걸리는 점이 없습니다. 시그니처를 먼저 확인해 조용한 무시를 막은 것, 저장 여부가 아니라 동작(get_provider_key("datago") is None)으로 테스트를 바꾼 것이 핵심입니다.
- 머지되면 kpubdata 0.8.0 에서는 own-credential·멀티유저 모드의
serve가 기동을 거부합니다. 의도된 실패이고, 멀티유저는 이 PR 과 #1008, 그리고env_keys를 담은 kpubdata 릴리스가 모두 있은 뒤에 켭니다. - 그 kpubdata 릴리스(kpubdata#780)는 10월 정기 릴리스에 싣기로 했습니다 — 별도로 앞당기지 않습니다. 핀 상향은 그 뒤에.
yeongseon
approved these changes
Oct 4, 2026
yeongseon
left a comment
Collaborator
There was a problem hiding this comment.
승인합니다. 앞선 리뷰 코멘트 그대로입니다.
…al client The client is built with env_keys=False, and a kpubdata without that option is refused instead of silently falling back to the environment. Refs #990 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Eomdahyeon
force-pushed
the
fix/issue-990-own-credential-env-keys
branch
from
October 4, 2026 14:42
6e6721c to
9577285
Compare
yeongseon
pushed a commit
that referenced
this pull request
Oct 6, 2026
Closes #1002 Refs #990, kpubdata-lab/kpubdata#780 kpubdata 0.9.0 이 PyPI 에 올라왔다(`https://pypi.org/pypi/kpubdata/json` → `0.9.0`). kpubdata#812 가 정한 대로 핀을 옮긴다: `kpubdata>=0.9.0,<0.10`. > **머지 전에 읽어 주세요 — 배포의 동작이 바뀝니다.** 아래 "0.9.0 카탈로그가 말하는 약관" 절. ## 변경 - `pyproject.toml`, `uv.lock`: `kpubdata>=0.8.0,<0.9` → `>=0.9.0,<0.10`. 잠금 파일에서 바뀐 것은 kpubdata 항목뿐이다(`uv lock --no-sources --upgrade-package kpubdata`, `--check` 통과). - **#1002:** kpubdata 의 private 모듈에서 가져오던 import 7개를 public 이름으로 바꾸고 allowlist 항목을 지웠다. - `find_spec` (agent/monitor.py, agent/pipeline.py, cli.py), `discover_specs` (cli.py), `KPubDataConfig` (service/providers.py, verify/runner.py) — 이슈가 적은 6개. - `SENSITIVE_PARAM_KEYS` (logging_redaction.py) — 0.9.0 이 public 으로 내보낸다(kpubdata#782). 이슈에는 "남는 항목"으로 적혀 있었지만 이제 필요 없다. - 0.9.0 을 설치하면 게이트가 **스스로 이것을 요구한다**: 핀만 올린 상태에서 `test_the_repository_passes_against_the_installed_kpubdata` 가 실패했고, 메시지가 위 6개를 하나씩 짚었다. - 남은 allowlist 는 `verify/runner.py` 의 7개다: `SpecExecutor`, `check_payload_error`, `extract_items`, `extract_total_count`, `ExampleSpec`, `SpecDefinition`, `HttpTransport`. - `tests/unit/test_verify_cli.py`: `find_spec` 을 패치하는 대상을 `kpubdata.core.spec` → `kpubdata` 로. import 경로가 바뀌어 예전 대상은 더 이상 닿지 않는다(테스트 하나가 실패했다). - `pyproject.toml` 주석, `CONTRIBUTING.md` 의 핀 설명, `cli.py` 의 안내 문구를 0.9 기준으로. - CHANGELOG. ## #990 에 대해 #1014 가 "`env_keys` 없는 kpubdata 로는 이 모드로 기동하지 않는다"를 넣었고, 0.8.0 에서는 그 거부가 항상 걸렸다. 0.9 라인에서는 `Client` 가 `env_keys` 를 받으므로 다중 사용자 모드가 기동한다. 0.8.0 이라 건너뛰던 테스트(`test_require_own_credential.py` 의 `env_keys` 실동작 테스트)가 이제 **돌고 통과한다** — skip 수가 9 → 8 로 줄었다. 이슈의 세 조건이 이 핀으로 모두 충족되는지는 판단을 맡긴다. 그래서 `Closes` 가 아니라 `Refs` 다. `client_keeps_environment_keys_out()` 확인은 남겨 두었다 — 누군가 더 낮은 버전을 설치한 환경을 위한 것이다. ## 0.9.0 카탈로그가 말하는 약관 — 배포에서 달라지는 것 설치된 0.9.0 카탈로그를 읽으면(`Client(...).datasets.list()`): 약관 선언 없음 134, `unknown` 18, **`forbidden` 1**, `allowed` 3. - **`datago.air_quality` 가 `forbidden` 이다.** 0.8.0 에서는 `redistribution` 선언이 없어 `unknown` 이었다. Builder 는 `forbidden` 에서 게시(공개·비공개 모두)와 `/query`·`/preview`·stage sample·warehouse 읽기·export·artifact 다운로드를 **모두 막는다**(#688). 이 핀이 들어가면 air_quality 로 만든 빌드는 Builder 밖으로 한 행도 나가지 않는다. #759("air_quality 는 KOGL 3유형인데 변형해 게시된다")가 다루던 문제의 답이 이것이라면 의도된 결과다 — 다만 **데모·문서·e2e 가 air_quality 를 예제로 많이 쓴다**는 점은 확인이 필요하다. 단위 테스트는 약관 조회를 conftest 에서 고정하므로 이 변화를 보지 못한다. - **baseline 의 16개는 `unknown` 이다** — kpubdata 가 직접 그렇게 보고하고(kpubdata#814), Builder 의 #1036 도 같은 판정을 한다. 16개 전부를 실제 카탈로그로 확인했다: `source_verdict(...)` → 16개 모두 `unknown`. #1036 리뷰가 남긴 "핀을 올리는 PR 에서 봐야 한다"는 확인이 이것이다. - `allowed` 로 남는 것은 `apt_trade`, `apt_rent`, `village_fcst` 셋이다. ## 검증 - `pytest tests/unit` (kpubdata 0.9.0 설치) → `4409 passed, 8 skipped` (8분 12초). - `scripts/check_kpubdata_imports.py` → `checked 246 files: no unlisted kpubdata private imports`. - `ruff check`, `ruff format --check`, `mypy src` 통과. - **하지 않은 것:** 실제 Builder 를 띄워 air_quality 빌드가 어떻게 답하는지 보지 않았다. e2e·데모 스크립트를 0.9.0 으로 돌리지 않았다. kpubdata 0.9.0 의 다른 breaking 변경(401 → `AuthError`, 건수 0, NODATA, 자격 증명 요청의 리다이렉트 미추적)이 Builder 의 실제 provider 호출 경로에 주는 영향은 단위 테스트가 통과한다는 것까지만 확인했다. - CI 의 `kpubdata 0.8.0` 잡은 이름이 워크플로에 박혀 있다 — 핀이 바뀌면 그 잡이 무엇을 설치하는지 확인이 필요하고, 워크플로 파일은 제가 고칠 수 없다. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-authored-by: Eomdahyeon <213566566+Eomdahyeon@users.noreply.github.com> Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
yeongseon
pushed a commit
that referenced
this pull request
Oct 6, 2026
…dential mode (#1051) Closes #990 #1050 리뷰가 남긴 것: "#990 은 누출 테스트(완료 조건 3)까지 보고 닫을지 정하면 됩니다". 그 테스트다. 코드 변경은 없다. ## 이슈의 세 조건과 근거 1. **키 없는 빌드가 거부된다** — `test_require_own_credential.py::test_build_is_refused_before_any_client_exists` (#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` (#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](https://claude.com/claude-code) Co-authored-by: Eomdahyeon <213566566+Eomdahyeon@users.noreply.github.com> Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Refs #990
문제
environment_keys=False로 만든 클라이언트가 환경변수 키를 막지 못한다.provider_keys에서 운영자 키를 빼도, kpubdata 클라이언트는 사용 시점에 없는 키를 환경에서 찾는다. 자기 키가 없는 사용자의 요청이 운영자 키로 실행된다.이 PR 이 하는 것 — kpubdata 릴리스 없이 지금 할 수 있는 부분
_create_client(environment_keys=False)가Client(..., env_keys=False)로 만든다.env_keys가 없고**extra가 조용히 삼키므로, 시그니처를 먼저 확인한다(client_keeps_environment_keys_out()). 없으면:RuntimeError— 환경 키로 내려가지 않는다.serve는 이 모드(및 이를 함의하는 멀티유저 모드)에서 기동을 거부한다 (종료 코드 1, 사유 출력). 완료 조건의 "조용한 무시 방지"."operator-key" not in repr(vars(client._config))는 폴백이_config에 저장되지 않으므로 항상 통과했다. 동작으로 바꿨다 —get_provider_key("datago") is None.docs/deployment.md의 설정 표, CHANGELOG[Unreleased]Security.이 PR 이 하지 않는 것 — 그래서
Refsenv_keys를 담은 kpubdata 릴리스가 없다(chore(release): ship env_keys=False — Builder's own-credential gate cannot work on 0.8.0 kpubdata#780, 사람의 일). 핀은>=0.8.0,<0.9그대로다.env_keys가 있는 kpubdata 에서만 의미가 있어 넣지 않았다.검증
uv run pytest(kpubdata 0.8.0):4377 passed, 11 skipped, 16 deselected.env_keys없는 Client →RuntimeError이고 클라이언트가 만들어지지 않음 / 있는 Client →env_keys=False가 전달됨 /serve가 기동 거부.test_the_real_client_cannot_reach_the_operators_key는 설치된 kpubdata 에env_keys가 있을 때만 돈다 — 로컬(0.8.0)에서는 skip 됐다.kpubdata main ↔ BuilderCI 잡에서 실행되는지는 이 PR 의 CI 에서 확인해야 한다.review:R3— 작성자가 아닌 사람의 승인이 필요하다.🤖 Generated with Claude Code