--- kind: CASE slug: messaging-admin-runtime-f08 title: 격리 리플레이의 guard 우회가 dryRun 파라미터로 표현된다 topic: security-policy-enforcement project: clean-architecture-backend-template status: 게시 전 sourceRevision: 21234e38cdb9a926cbc92bb97a2aee2e4a7d2916 rootTreeNode: case:messaging-admin-runtime-f08 evidenceCapturedOn: 2026-09-01 assets: - key: messaging-admin-runtime-f08 file: ../../../final/evidence/rendered/messaging-admin-runtime-f08.svg evidence: - ../../../final/evidence/raw/messaging-admin-runtime-f08.txt source: - 원본 분석 절은 analysis/messaging/messaging-admin-runtime.md#L948 이다. module: messaging-admin-runtime priority: P3 --- # 격리 리플레이의 guard 우회가 dryRun 파라미터로 표현된다 판단 자체는 근거가 있다. 다만 "승인이 필요 없다" 와 "실제로는 아무것도 하지 않는다" 가 guard 입장에서 구별되지 않는다. ## 문제 판단 자체는 근거가 있다. 다만 "승인이 필요 없다" 와 "실제로는 아무것도 하지 않는다" 가 guard 입장에서 구별되지 않는다. ## 결론 DestructiveOperationGuard 에 skipAuthorization 성격의 별도 경로를 두거나, 격리 리플레이는 애초에 guard 를 거치지 않는 편이 의도를 드러낸다. ## 검증 환경 OpenJDK : 21.0.12 java -version 으로 확인 Gradle : 9.0.0 src/gradle/wrapper/gradle-wrapper.properties 의 distributionUrl 로 확인 확인 방식 : DestructiveOperationGuard 참조 19건 검색과 guard 에 넘어가는 인자의 의미 대조 소스 수정 : x ## 재현 조건 원문은 analysis/messaging/messaging-admin-runtime.md#L948 에 있다. ## 본문 guard 호출이 두 뜻을 한 인자로 합친다. ```java // ReplayService.java:58-64 guard.authorize(REPLAY, request.destination(), approval, request.dryRun() || !needsApproval, now); ``` ## DestructiveOperationGuard 참조 위치 :::evidence key="messaging-admin-runtime-f08" alt="코드베이스에서 DestructiveOperationGuard 를 검색한 출력 19줄. 이 기록이 세는 참조가 그 출력에 그대로 보인다." caption="DestructiveOperationGuard 코드베이스 검색 — 19줄 · exit 0" zoom="true" ::: ## 판단 자체는 근거가 있다 다만 "승인이 필요 없다" 와 "실제로는 아무것도 하지 않는다" 가 guard 입장에서 구별되지 않는다. `DestructiveOperationGuard` 에 `skipAuthorization` 성격의 별도 경로를 두거나, 격리 리플레이는 애초에 guard 를 거치지 않는 편이 의도를 드러낸다. ## 확인하지 못한 것 guard 를 실제로 호출해 두 경우가 구별되지 않는 것을 관측하지 않았다. 인자 하나가 두 뜻을 겸한다는 호출 형태로 판정했다.