Files
document-haness/docs/clean-architecture-backend-template/tech-log-studio/multitenancy-isolation/case/case-analysis-finding-a06-f023.md
T
DongHyeonkaandClaude Fable 5.1 b25357c48a docs(clean-architecture-backend-template): fold analysis into final and re-select one topic
- analysis/·source-index·state.json 을 final/document.md 제2부·제3부로 접었다. SSOT 는 하나다
- 파일럿 — commit-ambiguity-as-a-result 를 새 기준으로 재선별. 후보 14 → 글감 5
  (PROMOTE 5 · MERGE_INTO 3 · KEEP_IN_SSOT 4 · 보류 2). 기록 5건을 다시 썼고 그림 1개를
  techviz 로 만들었다
- 재선별이 잡은 것: 제1부 §6.2·§11.1 이 자기 §13.2 와 어긋나 있었다(레인을 안 돌렸다 vs
  돌렸다) — 정정. 이미 답이 나와 있던 Question 을 HEAD 재실행 질문으로 다시 세웠다.
  Concept 이 인용한 코드가 SSOT 에 없어 뺐다
- candidateScope·sourceRepository 기록. 나머지 43개 주제는 재선별 대기(PENDING 905)

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-07 12:39:20 +09:00

149 lines
15 KiB
Markdown

---
kind: CASE
slug: analysis-finding-a06-f023
title: 고위험 관리 작업 넷이 승인을 널로 넘기는 오버로드를 부른다
topic: multitenancy-isolation
project: clean-architecture-backend-template
status: 게시 전
sourceRevision: 21234e38cdb9a926cbc92bb97a2aee2e4a7d2916
rootTreeNode: case:analysis-finding-a06-f023
evidenceCapturedOn: 2026-09-04
body: case-analysis-finding-a06-f023.body.md
assets:
- key: analysis-finding-a06-f023
file: ../../../final/evidence/rendered/analysis-finding-a06-f023.svg
evidence:
- ../../../final/evidence/raw/analysis-finding-a06-f023.txt
source:
- 원본 분석 절은 final/document.md#a06 §85 이다.
---
# 고위험 관리 작업 넷이 승인을 널로 넘기는 오버로드를 부른다
`MongoAdminGateway:66` 이 승인 자리에 `null` 을 넣은 채 세 인자 오버로드를 부른다. `:94`~`:102` 는 고위험 연산에 승인이 없으면 거절한다. 그 편의 오버로드를 부르는 main 일곱 줄 가운데 넷이 고위험 연산을 넘긴다.
## 관계
- **권한이 센 절반이 설정 한 줄로 켜지면 안 된다**
그 규칙이 다루는 것이 승인 어휘가 둘일 때의 문제다. 여기서는 편의 오버로드가 승인 없는 쪽을 기본으로 만든다.
- **같은 개념의 두 어휘가 공존하면 하나를 죽은 것으로 표시한다**
`execute` 가 둘인데 승인을 받는 쪽을 부르는 main 호출이 0 이다.
- **두 형태를 나란히 내놓는 포트는 이미 안전하지 않은 쪽을 고른 것이다**
그 규칙 2 가 폐기 표시도 이름 차이도 가시성 차이도 없으면 호출자가 짧은 쪽을 고른다고 적는다. `execute` 둘이 이름도 가시성도 같고, main 일곱 호출이 전부 짧은 쪽이다.
## 문제
이 평면의 실행 경로는 고위험 연산에 명령별 승인을 강제한다.
승인을 실제로 넘기는 호출이 있는지 확인했다.
## 결론
편의 오버로드가 :63~:67 에서 본 메서드를 부르면서 :66 의 승인 자리에 null 을 넣는다.
:94~:102 가 그 null 을 거절한다. 고위험 연산에 승인이 없으면 MongoOperationRejectedException 이 나가고, 메시지가 그런 연산은 명령에 묶인 승인 아래에서만 돈다고 적는다.
그 검사에 걸릴 수 있는 연산은 여덟이다. 그중 넷이 다섯 인자 오버로드로 넘어간다. MongoShardingAdminGateway:64 의 SHARD_COLLECTION, :75 의 REFINE_SHARD_KEY, :86~:87 의 RESHARD_COLLECTION, 그리고 MongoQueryableEncryptionCollectionManager:63~:64 의 MANAGE_ENCRYPTION_KEY 다.
넷 다 인자를 무엇으로 채워도 완료되지 않는다. 승인 자리가 null 로 고정돼 있어 :95 에서 걸린다.
MongoShardingAdminGateway:92 의 BALANCER_CONTROL 은 highRisk(false) 라 이 검사에 걸리지 않는다. MongoQueryableEncryptionCollectionManager:42 의 CREATE_COLLECTION 과 :70 의 COLL_MOD 도 그렇다.
승인 객체를 만드는 프로덕션 코드가 없다. MongoAdminApproval.of(...) 를 부르는 main 줄이 0 이고, 그 타입이 main 에 등장하는 다섯 자리는 전부 자기 선언이거나 게이트웨이 서명이다. 시험에는 그것을 만드는 줄이 넷 있다.
샤딩 게이트웨이가 받는 ReshardApproval 은 다른 타입이다. main 에 나오는 넷이 전부 자기 선언이거나 파라미터이고, 그것을 MongoAdminApproval 로 옮기는 자리가 없다.
MongoShardingAdminGateway:49~:63 이 그 앞에서 승인 준비 상태와 지원 인덱스를 검사하지만, 그것을 통과해도 :64 에서 막힌다.
## 검증 환경
OpenJDK : 21.0.12
확인 방식 : 샤딩 게이트웨이의 네 메서드 전문 인용과 그 호출 계수, 다섯 인자 오버로드 인용, 세 인자 오버로드의 승인 검사 인용, 연산 열거형의 위험 등급 인용과 계수, main 에 나오는 연산마다 위험 등급과 등장 줄 대조, 고위험을 넘기는 자리 전수 인용, 승인 객체를 만드는 main 계수와 그 이름의 main 등장 전수와 시험 대조, ReshardApproval 의 main 등장 전수
소스 수정 : x
## 재현 조건
1. 샤딩 게이트웨이의 네 메서드를 전문으로 싣고 adminGateway.execute 호출을 센다.
2. 다섯 인자 오버로드를 인용해 승인 자리에 무엇이 들어가는지 보인다.
3. 실행 경로의 고위험 승인 검사를 인용한다.
4. 그 오버로드를 지나는 연산 상수의 위험 등급을 열거형에서 인용하고 전체 상수와 고위험 개수를 함께 센다.
5. main 에 나오는 연산 상수마다 위험 등급과 등장 줄을 대조한다.
6. 고위험을 다섯 인자 오버로드로 넘기는 자리를 전부 인용한다.
7. 승인 객체를 만드는 main 줄을 세고 그 타입이 main 에 등장하는 자리를 전부 나열한 뒤, 시험 쪽 계수를 대조로 낸다.
8. ReshardApproval 이 main 에 나오는 자리를 전부 나열해 두 승인 타입을 잇는 코드가 있는지 본다.
## 본문
<!-- body:start -->
`MongoAdminOperation:4` 자바독이 이 열거형을 D4 평면이 수행할 수 있는 관리 연산이라고 부른다. 그 평면은 고위험 연산에 명령별 승인을 요구한다. 샤딩 게이트웨이가 네 작업을 그 위에 올린다.
## 샤딩 게이트웨이의 네 메서드
:::evidence key="analysis-finding-a06-f023" alt="저장소 루트에서 돌린 정적 검색 출력 215줄. 먼저 MongoShardingAdminGateway 33~93번이 실린다. 38~65번의 shardCollection 이 컬렉션과 샤드 키와 준비도 보고서와 지원 인덱스 필드를 받아 49~53번에서 승인되지 않은 샤드 키를 거절하고 54~63번에서 지원 인덱스가 샤드 키로 시작하지 않으면 거절한 뒤 64번에서 adminGateway.execute 에 SHARD_COLLECTION 을 넘긴다. 68~76번의 refineShardKey 와 79~88번의 reshardCollection 은 ReshardApproval 을 require 한 뒤 각각 75번과 86~87번에서 REFINE_SHARD_KEY 와 RESHARD_COLLECTION 을 넘기고, 91~93번의 controlBalancer 는 92번에서 BALANCER_CONTROL 을 넘긴다. 네 메서드가 부르는 execute 가 4 개다. 이어서 MongoAdminGateway 40~68번이 실리는데 47~56번 자바독이 다섯 인자의 뜻을 적고, 57~62번 서명이 연산과 대상과 운영자와 사유와 본문을 받으며, 63~67번이 MongoAdminCommand.routine 으로 명령을 만들어 66번에서 승인 자리에 null 을, 67번에서 본문을 넘긴다. 다음으로 69~112번의 세 인자 오버로드가 실린다. 70~81번 자바독은 단일 레코드 버전에서 셋이 바뀌었다며 의도가 명령 실행 전에 기록되고 아무것도 주장하지 않으며 무엇이 실제로 일어났는지 적는 종결 레코드가 뒤따르고 실패하는 감사 싱크가 명령을 멈추는데 감사할 수 없는 관리 작업은 나중에 누구도 재구성할 수 없기 때문이고 drop 이나 reshard 에는 그것이 이 평면을 두는 이유 전부라고 적으며, 79번이 승인은 고위험 연산에 필요하고 이 명령에 묶인다고 적는다. 83번 서명이 명령과 승인과 본문 셋을 받고, 87번이 권한을 요구하고 88~93번이 만료된 명령을 거절하며, 94~102번이 고위험 연산에 승인이 null 이면 데이터를 파괴하거나 컬렉션을 다시 쓰는 연산은 이 명령에 묶인 승인 아래에서 돌거나 아예 돌지 않는다는 메시지로 거절하고, 103번이 승인을 검사하며 104~110번이 단일 사용을 강제하면서 그렇지 않으면 한 reshard 에 대한 승인 하나가 같은 모양의 이후 모든 reshard 를 허가하게 된다고 주석에 적는다. 이어서 MongoAdminOperation 3~10번 자바독이 이것이 D4 평면이 수행할 수 있는 관리 연산의 닫힌 열거형이고 모든 상수가 애플리케이션 런타임이 닿으면 안 되는 것이며 여기 모아 두는 것이 경계를 어떤 메서드가 있느냐에서 나오는 성질이 아니라 누군가 검토할 수 있는 목록으로 만든다고 적는다. 13번 CREATE_COLLECTION 과 16번 COLL_MOD 와 46번 BALANCER_CONTROL 이 false 이고 37번 SHARD_COLLECTION 과 40번 REFINE_SHARD_KEY 와 43번 RESHARD_COLLECTION 과 49번 MANAGE_ENCRYPTION_KEY 가 true 이며, 그 일곱 가운데 고위험이 4 개, 열거형 전체에서 고위험이 8 개, 상수 전체가 15 개다. 다음으로 승인을 실제로 넘기는 main 호출이 0 개이고, execute 를 부르는 main 자리 일곱이 나열되는데 MongoQueryableEncryptionCollectionManager 41번과 63번과 70번, MongoShardingAdminGateway 64번과 75번과 86번과 92번이며, 시험에서 세 인자 execute 를 부르는 줄은 10 개이고, MongoAdminApproval 을 만드는 main 줄은 0 개이며 그 이름이 main 에 나오는 자리는 MongoAdminApproval 자신의 19·21·31·33번과 MongoAdminGateway 83번의 서명 다섯이고, 시험에서 그것을 만드는 줄은 4 개다. 이어서 main 에 나오는 연산 상수마다 위험 등급과 등장 줄이 대조되는데 BALANCER_CONTROL 과 COLL_MOD 와 CREATE_COLLECTION 이 false 이고 MANAGE_ENCRYPTION_KEY 와 REFINE_SHARD_KEY 와 RESHARD_COLLECTION 과 SHARD_COLLECTION 이 true 이며 각각 한 줄씩이다. 고위험인데 다섯 인자 오버로드로 넘어가는 자리 넷이 실리는데 MongoShardingAdminGateway 64번과 75번과 86~87번, 그리고 MongoQueryableEncryptionCollectionManager 61~65번의 rotateDataKey 가 64번에서 MANAGE_ENCRYPTION_KEY 를 넘기는 자리이며, 대조로 같은 클래스 34~43번의 createEncryptedCollection 이 41~42번에서 넘기는 CREATE_COLLECTION 은 false 다. 마지막으로 ReshardApproval 이 main 에 나오는 줄이 4 개라고 나오고 그 넷이 MongoShardingAdminGateway 70번과 81번의 파라미터와 ReshardApproval 자신의 15번과 22번이다." caption="샤딩 게이트웨이의 네 메서드와 그 네 호출 · 승인 자리에 null 을 넣는 다섯 인자 오버로드 · 고위험에 승인을 요구하는 세 인자 오버로드 · 연산 열거형의 위험 등급과 계수 · 승인을 넘기는 main 호출 0 과 execute 일곱 자리 · 고위험을 넘기는 넷 · 두 승인 타입을 잇는 코드 부재 — 215줄 · exit 0" zoom="true"
:::
`MongoShardingAdminGateway:38``shardCollection` 이 컬렉션과 샤드 키와 준비도 보고서와 지원 인덱스 필드를 받는다. `:49`\~`:53` 이 승인되지 않은 샤드 키를 거절하고, `:54`\~`:63` 이 지원 인덱스가 샤드 키로 시작하지 않으면 거절한다.
`:68``refineShardKey``:79``reshardCollection``ReshardApproval``require()` 한다. `:91``controlBalancer` 는 앞선 검사가 없다.
넷 다 마지막 줄에서 `adminGateway.execute(...)` 를 부른다.
## 그 오버로드가 승인 자리에 넣는 것
`MongoAdminGateway``execute` 가 둘이다. `:57`\~`:62` 의 다섯 인자 오버로드가 연산과 대상과 운영자와 사유와 본문을 받고, `:83` 의 세 인자 오버로드가 `MongoAdminCommand``MongoAdminApproval` 과 본문을 받는다.
다섯 인자 쪽이 `:63`\~`:67` 에서 `MongoAdminCommand.routine(...)` 으로 일상 명령을 만들어 세 인자 쪽에 넘긴다. `:66` 이 승인 자리다. `null` 이다.
## 세 인자 오버로드가 그 null 을 거절한다
`:87` 이 권한을 요구하고 `:88`\~`:93` 이 만료된 명령을 거절한다.
`:94` 가 연산이 고위험인지 본다. 고위험이면 `:95` 가 승인이 `null` 인지 보고, 그렇다면 `:96`\~`:102` 가 거절한다. 메시지는 데이터를 파괴하거나 컬렉션을 다시 쓰는 연산은 이 명령에 묶인 승인 아래에서 돌거나 아예 돌지 않는다는 것이다.
`:103` 이 승인 자체를 검사하고 `:106` 이 단일 사용을 강제한다. `:104`\~`:105` 주석은 그것이 없으면 한 reshard 에 대한 승인 하나가 같은 모양의 이후 모든 reshard 를 허가하게 된다고 적는다.
승인이 `null` 인 고위험 명령은 `:95` 에서 끝난다.
## 넷이 그 조합에 걸린다
`MongoAdminOperation` 상수는 열다섯이고 그중 여덟이 `highRisk(true)` 다.
main 에서 다섯 인자 오버로드로 넘어가는 연산은 일곱 줄에 나뉘어 있다. `BALANCER_CONTROL``COLL_MOD``CREATE_COLLECTION``false` 이고, `SHARD_COLLECTION``REFINE_SHARD_KEY``RESHARD_COLLECTION``MANAGE_ENCRYPTION_KEY``true` 다.
고위험 넷의 위치는 `MongoShardingAdminGateway:64`·`:75`·`:86`\~`:87``MongoQueryableEncryptionCollectionManager:63`\~`:64` 다. 마지막 것은 `:62``rotateDataKey` 로, 자바독이 자기 런북과 증거를 요구한다고 적는다.
넷 다 호출자가 무엇을 넘겨도 `:95` 를 지나지 못한다. 승인 자리가 그 오버로드 안에서 이미 정해져 있기 때문이다.
## 승인 객체를 만드는 main 코드가 없다
`MongoAdminApproval.of(...)``new MongoAdminApproval` 을 부르는 main 줄이 0 이다.
그 이름이 main 에 나오는 자리는 다섯인데, 넷이 `MongoAdminApproval` 자신의 선언과 팩터리이고 하나가 `MongoAdminGateway:83` 의 서명이다.
세 인자 `execute` 를 부르는 시험 줄은 `MongoAdminAuditStateMachineTest` 에 10 이고, 승인 객체를 만드는 시험 줄은 4 다. 프로덕션에는 그 짝이 없다.
`MongoShardingAdminGateway` 의 앞선 검사들은 그 뒤를 바꾸지 못한다. `:49` 의 준비도 검사와 `:54` 의 인덱스 검사와 `:74`·`:85``ReshardApproval.require()` 를 전부 통과해도 `:64`·`:75`·`:86` 에서 같은 자리에 막힌다.
`ReshardApproval``MongoAdminApproval` 은 다른 타입이다. `ReshardApproval` 이 main 에 나오는 줄은 넷인데 `MongoShardingAdminGateway:70`·`:81` 의 파라미터 둘과 `ReshardApproval:15`·`:22` 의 자기 선언 둘이다. `MongoAdminApproval` 을 만드는 자리는 그 안에 없다.
## 원문에 없는 것
원문은 샤딩 게이트웨이의 네 작업 중 셋이 어떤 입력으로도 완료될 수 없다고 적는다. 그 셋은 그대로 확인된다.
네 번째가 있다. `MongoQueryableEncryptionCollectionManager:63`\~`:64``rotateDataKey` 도 같은 오버로드에 `MANAGE_ENCRYPTION_KEY` 를 넘기고, 그 상수도 `highRisk(true)` 다. 같은 클래스 `:41`\~`:42``CREATE_COLLECTION``false` 라 걸리지 않는다.
다섯 인자 오버로드에 고위험 연산을 넘기는 main 자리를 저장소 전체에서 세면 넷이고, 그 넷이 두 클래스에 나뉘어 있다. 샤딩 리프만의 문제가 아니다.
## 확인하지 못한 것
그 넷을 불러 예외가 나는 것을 프로브로 보이지 않았다.
`MongoShardingAdminGateway``MongoQueryableEncryptionCollectionManager` 를 만드는 프로덕션 코드가 있는지 조립 경로를 세지 않았다.
포크가 승인을 어디서 만들어 넘기도록 설계된 것인지 설계 문서로 판단하지 않았다.
`ReshardApproval``MongoAdminApproval` 로 옮기는 코드가 왜 없는지, 두 타입의 관계를 설계 문서로 확인하지 않았다.
<!-- body:end -->