임베딩 검색을 요청자 소유 문서로 제한 - #217
Merged
Merged
Conversation
POST /api/internal/embeddings/search 에는 userId 파라미터가 아예 없었다. documentIds 가 비면 스펙상 "전체 검색" 이라 다른 사용자의 청크까지 대상이고, id 를 줘도 소유권을 확인하지 않아 남의 문서 id 를 넣으면 그대로 조회됐다. 실제 유출은 없었다 — AI 호출부 3곳이 모두 빈 목록을 사전에 걸러 (none) 을 반환한다. 하지만 방어가 전적으로 호출자에게 있었다. 호출부가 하나 늘거나 가드를 빠뜨리면 남의 이력서 청크가 프롬프트로 들어간다. Core 가 스코프를 확정한다: - userId 필수(@NotNull) - documentIds 를 주면 소유 문서와의 교집합만 — 요청한 id 를 그대로 믿지 않는다 - 비면 그 사용자의 활성 문서 전체 ("비면 전체 사용자" 규약 폐기) - 교집합이 비면 검색하지 않고 빈 결과 (빈 목록을 넘기면 다시 전체 검색이 된다) AI 는 envelope.context.user_id 를 싣는다 — Core 가 generate.questions/ followup/feedback 발행 시 이미 채우고 있어서 메시지 계약 변경이 없다. user_id 를 못 얻으면 검색을 건너뛰고 (none) 으로 폴백한다.
This was referenced Aug 23, 2026
Merged
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.
문제
#216 을 작업하며 함께 보고했던 건이다.
POST /api/internal/embeddings/search에는userId파라미터가 아예 없었다.documentIds가 비면 스펙상 "전체 검색" — 다른 사용자의 청크까지 대상document_embeddings에는 청크 원문이 저장되므로, 나오면 곧 남의 이력서 문장이다실제 유출은 없었다. AI 호출부 3곳(
questions/followup/feedback)이 모두 빈 목록을 사전에 걸러(none)을 반환한다. 확인했다.문제는 방어가 전적으로 호출자에게 있었다는 점이다. 호출부가 하나 늘거나 가드를 빠뜨리면 남의 이력서 청크가 프롬프트로 들어간다. 내부 API 라
X-Internal-API-Key뒤에 있지만, 그건 "AI 서버가 맞나"만 확인할 뿐 "누구 데이터를 볼 자격이 있나"는 묻지 않는다.수정
Core 가 스코프를 확정한다 — 호출자가 무엇을 보내든 요청자 소유 문서를 벗어날 수 없다.
userId@NotNull)documentIds있음documentIds비음AI 는
envelope.context.user_id를 싣는다. Core 가generate.questions/followup/feedback발행 시MessageContext에 이미 userId 를 채우고 있고 AI 모델에도 필드가 있어서 — 메시지 계약 변경이 없다. 세 consumer 의_process(envelope)에서 검색까지 값을 흘리기만 하면 됐다.user_id를 못 얻는 경우(구버전 발행 등)는 검색을 건너뛰고(none)으로 폴백한다. RAG 는 보강용이라 없어도 생성은 진행된다.테스트
Core (#197 인프라, 실제 pgvector):
serviceScopesSearchToRequestingUsersDocuments— 남의 문서 id 를 명시해도 결과에 없다.documentIds를 비워도 전체 검색이 되지 않는다serviceReturnsEmptyWhenUserOwnsNothing— 소유 문서가 없으면 빈 결과 (빈 목록 위임 금지)AI: 기존
test_search_embeddings_uses_latest_core_contract가 새 필수 인자를 바로 잡아냈다(계약 테스트가 제 역할을 했다). 요청 body 기대값에userId를 반영. 393 tests 통과.배포 순서
deploy-app.yml이 백엔드·AI 를 같은 워크플로에서 배포한다. 그 사이 짧은 창에서 구버전 AI 가userId없이 호출하면 400 → AI 는 예외를 잡아(none)으로 폴백한다. 면접·피드백 생성은 계속되고 RAG 보강만 잠시 빠진다 — 실패 경로가 이미 non-fatal 로 설계돼 있어 별도 조치가 필요 없다.문서
docs/messaging.md §10.1신설 — 스코프 규약, 이전 규약 폐기,user_id부재 시 폴백을 명시.