- 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>
12 KiB
kind, slug, title, topic, project, status, sourceRevision, rootTreeNode, evidenceCapturedOn, body, assets, evidence, source
| kind | slug | title | topic | project | status | sourceRevision | rootTreeNode | evidenceCapturedOn | body | assets | evidence | source | |||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| CASE | analysis-finding-a06-f021 | 드라이런만 감사 싱크를 직접 불러 번역을 거치지 않는다 | multitenancy-isolation | clean-architecture-backend-template | 게시 전 | 21234e38cdb9a926cbc92bb97a2aee2e4a7d2916 | case:analysis-finding-a06-f021 | 2026-09-04 | case-analysis-finding-a06-f021.body.md |
|
|
|
드라이런만 감사 싱크를 직접 불러 번역을 거치지 않는다
감사 레코드를 만드는 자리는 넷이다. :114·:117·:122 가 MongoAdminGateway:127 의 audit 헬퍼를 지나 싱크 실패를 MongoOperationRejectedException 으로 바꾸고, dryRun:147 만 auditSink.accept 를 직접 부른다.
관계
- 단일 admission point는 우회 경로를 세어야 성립한다
그 규칙 2 가 하위 계층 타입을 직접 참조하는 곳을 세라고 적는다. 감사 레코드를 만드는 네 자리 가운데
dryRun:147하나가auditSink를 직접 부른다. - 도달성 판정은 단어가 아니라 import로 확인한다
dryRun호출 0 이라는 판정을 같은 pathspec 과 같은 정규식 모양이execute에 대해 18 줄을 찾은 것과 대조해 확인했다. - 실패 번역 사슬의 순서는 계약이다
audit헬퍼가 싱크의RuntimeException을MongoOperationRejectedException으로 바꾸는 단일 번역 지점인데,dryRun:147이 그것을 지나지 않아 싱크가 던진 예외가 그대로 호출자에게 간다.
문제
관리 게이트웨이는 모든 감사 쓰기를 헬퍼로 보내고, 그 헬퍼가 싱크 실패를 플랫폼 거부 예외로 바꾼다.
그 헬퍼를 지나지 않는 감사 쓰기가 있는지 확인했다.
결론
audit:127~:137 이 싱크 호출을 try 로 감싸고 RuntimeException 을 플랫폼 거부 예외로 바꾼다. 거절된 레코드의 단계 이름이 메시지에 들어간다.
실행 경로의 세 기록이 그 헬퍼를 지난다. :114 의 의도와 :117 의 성공과 :122 의 실패다. :113 이 그 순서를 fail closed 라고 부른다.
싱크를 직접 부르는 줄은 헬퍼 안의 :129 와 드라이런 안의 :147 둘이고, 뒤쪽만 번역을 거치지 않는다.
dryRun:145~:149 는 권한 검사 뒤에 싱크를 바로 부른다. 그래서 싱크가 던진 예외가 그대로 호출자에게 도달한다.
:142~:143 자바독은 드라이런을 고위험 작업의 전제 조건으로 놓는다. 자바독대로면 고위험 명령은 감사가 강제되지 않는 호출을 먼저 지난다.
시험은 execute 쪽만 고정한다. MongoAdminAuditStateMachineTest:86 이 던지는 싱크로 거부 예외와 본문 미실행을 함께 단언하는데, 같은 파일에 dryRun 을 거는 줄이 0 이다.
dryRun 을 부르는 줄이 저장소 전체에 0 이다. 같은 게이트웨이의 execute 는 18 줄이 부르고 그중 일곱이 main 이다.
검증 환경
OpenJDK : 21.0.12 확인 방식 : 감사 헬퍼와 그 번역 인용, 실행 경로가 헬퍼를 부르는 세 자리 인용, auditSink 이름이 나오는 자리 전수와 헬퍼 호출 계수, dryRun 본문과 그 자바독 인용, 감사 실패를 고정하는 시험 인용과 그 파일의 dryRun 계수, 두 진입점의 호출 계수와 다른 게이트웨이 오염분 대조 소스 수정 : x
재현 조건
- audit 헬퍼와 그것이 던지는 예외를 인용한다.
- 실행 경로가 그 헬퍼를 부르는 세 자리를 인용한다.
- auditSink 라는 이름이 나오는 자리와 audit 헬퍼를 부르는 자리를 각각 전부 찾는다.
- dryRun 본문과 그 자바독을 인용한다.
- 감사 실패를 고정하는 시험을 인용하고, 같은 파일에서 dryRun 이 나오는 줄을 센다.
- dryRun 과 execute 를 부르는 줄을 각각 세어 대조한다.
본문
관리 게이트웨이는 감사 쓰기를 헬퍼 하나로 모은다.
감사 실패를 거부로 바꾸는 헬퍼
:::evidence key="analysis-finding-a06-f021" alt="저장소 루트에서 돌린 정적 검색 출력 145줄. 먼저 MongoAdminGateway 120137번이 실린다. 120121번 주석이 종결 레코드가 실패 전파 전에 쓰이고 그 세부가 예외 메시지가 아니라 예외 타입이라고 적고, 122123번이 실패 레코드를 남긴 뒤 다시 던진다. 127137번의 private audit 헬퍼가 128129번에서 auditSink.accept 를 try 로 감싸고 130136번에서 RuntimeException 을 잡아 MongoOperationRejectedException 으로 바꾸는데, 메시지가 관리 감사 싱크가 해당 단계 레코드를 거절했으며 감사할 수 없는 관리 작업은 실행되지 않는다는 것이다. 이어서 실행 경로 전체인 83126번이 실린다. 83번 서명이 명령과 승인과 본문 셋을 받고, 87번이 authorization.require 로 권한을 요구하며, 8893번이 만료된 명령을 거절하는데 오래된 명령은 더 이상 그것이 쓰인 때의 모습이 아닌 클러스터를 서술할 수 있다는 메시지다. 94번이 연산의 highRisk 를 보고 95102번이 승인이 null 이면 데이터를 파괴하거나 컬렉션을 다시 쓰는 연산은 이 명령에 묶인 승인 아래에서 돌거나 아예 돌지 않는다며 거절하고, 103번이 approval.require 를, 106110번이 단일 사용을 강제하며 104105번 주석이 그것이 없으면 한 reshard 에 대한 승인 하나가 같은 모양의 이후 모든 reshard 를 허가한다고 적는다. 113번 주석이 fail closed 라며 의도를 기록할 수 없으면 명령이 돌지 않는다고 적고, 114번이 의도 레코드를, 117번이 성공 레코드를, 122번이 실패 레코드를 헬퍼로 보낸다. 그 아래 auditSink.accept 를 부르는 줄이 2 개이고 audit 헬퍼를 부르는 줄이 3 개라고 나오며, auditSink 라는 이름이 나오는 자리가 26번 필드와 38번 생성자 인자와 42번 대입과 129번과 147번 다섯으로, 헬퍼를 부르는 자리가 114·117·122번 셋으로 나열된다. 다음으로 MongoAdminGateway 139149번의 dryRun 이 실린다. 140144번 자바독은 이것이 실행 없이 연산을 검증하고 의도를 기록하는 것이며, 드라이런이 모든 고위험 작업의 전제 조건이라 누군가 기억해서 넘기는 플래그가 아니라 일급 호출이어야 한다고 적는다. 145번 서명 뒤 146번이 권한을 요구하고 147148번이 auditSink.accept 를 직접 부른다. 헬퍼를 거치지 않는다. 마지막으로 MongoAdminAuditStateMachineTest 84113번이 실린다. 85번 DisplayName 이 실패하는 감사 싱크가 명령이 도는 것을 막는다고 적고, 86번의 anUnauditableCommandDoesNotRun 이 8894번에서 감사 컬렉션에 닿을 수 없다며 던지는 싱크로 게이트웨이를 만들고 95번에서 ran 플래그를 두며, 97107번이 COLL_MOD 일상 명령을 승인 null 로 execute 에 넘기고, 108109번이 MongoOperationRejectedException 과 cannot be audited 메시지를, 110~112번이 나중에 아무도 재구성할 수 없는 관리 작업은 일어나면 안 된다는 설명과 함께 본문이 돌지 않았다는 것을 단언한다. 그 아래 드라이런의 감사 실패를 보는 시험 줄이 0 개이고, 이 게이트웨이의 dryRun 을 부르는 줄이 저장소 전체에 0 개이며, 대조로 센 같은 게이트웨이의 execute 는 18 개이고 그 호출 자리가 main 일곱과 test 열하나로 나뉘어 전부 나열된 뒤, 같은 정규식이 다른 게이트웨이에서 잡는 줄이 8 개라고 참고로 붙는다." caption="감사 실패를 플랫폼 예외로 바꾸는 헬퍼 · execute 가 감사 앞에서 지나는 네 검사와 세 기록이 헬퍼를 지나는 자리 · auditSink 를 부르는 자리 전수 · 헬퍼를 거치지 않는 dryRun 과 그 자바독 · 감사 실패를 고정하는 시험의 단언 셋과 dryRun 계수 0 · 두 진입점의 호출 대조 — 145줄 · exit 0" zoom="true"
:::
MongoAdminGateway:127~:137 의 audit 이 auditSink.accept(record) 를 try 로 감싼다.
:130 이 RuntimeException 을 잡아 :131~:135 에서 MongoOperationRejectedException 으로 바꾼다. 메시지는 감사 싱크가 그 단계 레코드를 거절했고, 감사할 수 없는 관리 작업은 실행되지 않는다는 것이다.
실행 경로의 세 기록
:114 가 의도 레코드를 헬퍼로 보낸다. :113 주석에는 fail closed 라고, 의도를 기록할 수 없으면 명령이 돌지 않는다고 적혀 있다.
:117 이 성공 레코드를, :122 가 실패 레코드를 보낸다. :120~:121 주석은 종결 레코드가 실패 전파 전에 쓰이고 그 세부가 예외 메시지가 아니라 예외 타입이라고 적는다.
셋 다 헬퍼를 지난다. 싱크가 던지면 셋 다 플랫폼 예외가 된다.
audit 헬퍼를 지나지 않는 dryRun:147
auditSink.accept 를 부르는 줄은 둘이다. :129 가 헬퍼 안이고 :147 이 드라이런 안이다. audit 헬퍼를 부르는 줄은 셋이고 전부 실행 경로에 있다.
dryRun:145~:149 는 :146 에서 권한을 요구한 뒤 :147~:148 에서 auditSink.accept(...) 를 바로 부른다.
싱크가 던지면 그 예외가 번역 없이 호출자에게 간다. MongoOperationRejectedException 이 아니라 싱크가 던진 것 그대로다.
자바독이 적는 드라이런의 지위
:142~:143 자바독에는 드라이런이 모든 고위험 작업의 전제 조건이라서, 누군가 기억해서 넘기는 플래그가 아니라 일급 호출이어야 한다고 적혀 있다.
자바독대로라면 고위험 작업은 드라이런이 먼저 돌아야 하는데, 그 드라이런이 남기는 레코드는 싱크가 거절해도 명령을 멈추지 않는다. execute 가 남기는 세 레코드와 다른 점이 이것이다.
시험이 거는 것은 execute 뿐이다
MongoAdminAuditStateMachineTest:86 의 anUnauditableCommandDoesNotRun 이 :88~:94 에서 던지는 싱크로 게이트웨이를 만들고, :108~:109 에서 MongoOperationRejectedException 과 cannot be audited 메시지를, :110~:112 에서 본문이 돌지 않았다는 것을 단언한다.
같은 파일에서 dryRun 을 다루는 줄은 0 이다.
dryRun 을 부르는 코드가 없다
그 메서드를 부르는 줄이 저장소 전체에 하나도 없다. 형제인 execute 는 18 줄이 부른다.
같은 pathspec 과 같은 정규식 모양이 한쪽에서 18 을 찾았으므로, 0 은 검색이 아무것도 훑지 못해 나온 값이 아니다. 그 정규식은 다른 게이트웨이에서도 8 줄을 잡는데 RedisRawGatewayContractTest 와 PolicyAwareMongoNativeGatewayTest 의 것이라 이 계수에서 뺐다.
원문에 없는 것
원문은 두 감사 경로 중 하나만 fail-closed 이고 그 차이가 문서화돼 있지 않다고 적는다. 그 판정은 그대로다.
원문이 적지 않은 것은 execute 와 dryRun 이 감사 앞에서 지나는 검사의 수다. execute 는 :87 의 권한과 :88 의 만료와 :94~:102 의 승인과 :106 의 단일 사용 넷을 지난 뒤에 :114 로 간다. dryRun 은 :146 의 권한 하나만 지난다.
확인하지 못한 것
던지는 싱크를 넣고 dryRun 을 불러 어떤 예외가 나오는지 프로브로 재현하지 않았다.
포크가 드라이런을 어디서 부르도록 설계된 것인지 설계 문서로 판단하지 않았다.
dryRun 이 남기는 레코드를 승인 사슬이 어떻게 참조하는지 그 소비자를 찾지 않았다.
던지는 싱크를 넣고 dryRun 을 불러 어떤 예외가 나오는지 프로브로 재현하지 않았다.
dryRun 이 남기는 레코드를 승인 사슬이 어떻게 참조하는지 그 소비자를 찾지 않았다.