Files
document-haness/docs/clean-architecture-backend-template/tech-log-studio/web-inbound-and-http-surface/case/case-analysis-finding-a14-f003.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

7.5 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-a14-f003 플랫폼 요청 컨텍스트가 서블릿에는 생산자가 없고, 리액티브에는 익명 액터로 고정되어 있다 web-inbound-and-http-surface clean-architecture-backend-template 게시 전 21234e38cdb9a926cbc92bb97a2aee2e4a7d2916 case:analysis-finding-a14-f003 2026-09-01 case-analysis-finding-a14-f003.body.md
key file
analysis-finding-a14-f003 ../../../final/evidence/rendered/analysis-finding-a14-f003.svg
../../../final/evidence/raw/analysis-finding-a14-f003.txt
원본 분석 절은 final/document.md#a14#L503 이다.

플랫폼 요청 컨텍스트가 서블릿에는 생산자가 없고, 리액티브에는 익명 액터로 고정되어 있다

서블릿 쪽은 컨텍스트를 저장하는 코드가 0 이라 그것을 요구하는 인자 해석기가 항상 던진다. 반응형 쪽은 생산자가 하나 있는데 열 성분 중 넷을 상수로 채운다. 인증 결과가 요청 컨텍스트에 도달하는 경로가 없다.

관계

  • 두 개의 outbox 중 하나만 조립되어 있다 같은 형태의 절반 조립이다.
  • 한쪽의 javadoc이 다른 쪽의 동작을 규탄한다 이 사례가 그 규칙의 형태다.
  • 픽스처가 생산자를 우회하면 게이트가 도는 것은 픽스처다 테스트가 못 잡는 이유다.

문제

플랫폼 요청 컨텍스트는 컨트롤러 서명에서 하위 전송 타입을 몰아내려고 만든 값이다.

두 전송에서 그 값이 어떻게 만들어지는지 확인했다.

결론

두 전송이 각각 다르게 망가져 있다.

서블릿 절반부터 본다.

저장 메서드의 호출자가 저장소 전체에서 0 이다.

그런데 그것을 읽는 쪽은 자동 설정이 등록한다. 인자 해석기를 설정에 추가하는 빈이 있다.

그 해석기가 하는 일은 요구 메서드 하나이고, 속성이 없으면 던진다.

던지는 메시지가 이유를 적는다. 여기서 하나를 지어내면 인증되지 않은 호출이 누군가 고른 익명 행위자처럼 보이게 된다는 것이다.

속성을 놓는 코드가 없으므로 이 예외는 항상 던져진다.

서블릿 컨트롤러가 그 컨텍스트를 매개변수로 선언하면 그 종점은 언제나 서버 오류다.

컨트롤러의 서명에서 하위 전송 타입을 몰아내려고 만든 기능이, 쓰면 반드시 실패하는 기능이다.

반응형 절반은 다르다.

생산자가 하나 있고, 열 성분 중 넷을 상수로 채운다. 주 판본과 행위자와 소속과 지역이다.

이 필터는 보안 문맥 보유자도, 보안 문맥 다리도, 인증 조회도 참조하지 않는다.

인증 결과가 요청 컨텍스트에 도달하는 경로가 없다.

인증된 호출자든 아니든 컨텍스트의 행위자는 익명이고 소속은 없음이다.

같은 저장소가 이 정확한 위험을 서블릿 쪽에서는 이름 붙여 거부한다.

요구 메서드의 메시지가 지어낸 컨텍스트는 인증되지 않은 호출을 누군가 고른 익명 행위자처럼 보이게 한다고 적는다.

반응형 생산자가 하는 일이 정확히 그 지어내기다.

두 전송이 같은 위험에 정반대로 대응했고, 한쪽의 자바독이 다른 쪽의 동작을 규탄한다.

반응형 실패 시나리오는 이렇다.

인증된 사용자가 자기 비동기 작업 결과를 조회한다.

반응형 컨트롤러가 접근 정책을 부르고, 행위자의 인증 여부가 항상 거짓이므로 모든 조회가 거부된다.

정책의 주석이 부재와 금지를 의도적으로 같게 답한다고 적으므로 클라이언트는 없음 응답을 받는다.

비동기 작업 기능이 반응형 전송에서 동작하지 않는다.

서블릿 실패 시나리오는 앞서 본 대로다. 같은 종점이 컨텍스트를 매개변수로 받으면 해석기가 던져 서버 오류이고, 받지 않으면 컨텍스트를 얻을 경로가 없다.

방향은 닫힌 실패다. 데이터 유출이 아니라 기능 불능이다.

그래서 보안 사고가 아니라 지금 틀린 동작으로 P1 이다.

테스트가 잡지 못하는 이유는 구조에 있다.

그 컨텍스트를 만드는 다른 열한 곳이 전부 테스트와 테스트킷이고 전부 손으로 채운다.

계약 레인의 픽스처 컨트롤러조차 해석기를 쓰지 않고 자기 컨텍스트를 만든다.

네 런타임을 가로지르는 교차 스택 게이트가 있어도, 그 게이트가 도는 픽스처가 생산자를 우회한다.

권고는 셋이다.

서블릿에 저장을 부르는 필터를 추가한다. 요청당 한 번 도는 자리가 이미 있다.

두 생산자가 보안 문맥에서 행위자와 소속을 읽게 한다. 그 목적의 다리가 이미 존재한다.

그리고 픽스처가 손으로 컨텍스트를 만드는 대신 해석기와 필터를 지나게 한다. 셋째 없이는 같은 상태가 다시 성립한다.

검증 환경

Spring Boot : 4.0.8 확인 방식 : 호출자 전수 검색과 생산자 코드 확인 소스 수정 : x

재현 조건

원문은 final/evidence/raw/176 계열에 있다.

  1. 서블릿 컨텍스트 보유자의 저장 메서드 호출자를 센다.
  2. 인자 해석기를 등록하는 자동 설정을 확인한다.
  3. 해석기가 부르는 요구 메서드의 실패 경로를 읽는다.
  4. 반응형 생산자가 각 성분을 무엇으로 채우는지 확인한다.
  5. 그 필터가 보안 문맥을 참조하는지 확인한다.
  6. 테스트와 픽스처가 컨텍스트를 어떻게 만드는지 센다.

본문

서블릿 절반. WebMvcRequestContextHolder.store(...)의 호출자가 저장소 전체에서 0이다.

서블릿 쪽 생산자 계수

:::evidence key="analysis-finding-a14-f003" alt="분석 문서 final/document.md#a14 에서 이 기록의 근거 절을 그대로 잘라낸 15줄. 코드베이스를 측정한 것이 아니라 원본 판정이 무엇을 적었는지를 보여 준다." caption="final/document.md#a14 발췌 — 15줄" zoom="true" :::

읽는 쪽은 자동설정이 등록한다

// WebMvcPlatformAutoConfiguration.java:158-166
@Bean @ConditionalOnMissingBean(name = "webMvcRequestContextConfigurer")
public WebMvcConfigurer webMvcRequestContextConfigurer() {
  return new WebMvcConfigurer() {
    @Override public void addArgumentResolvers(List<HandlerMethodArgumentResolver> resolvers) {
      resolvers.add(new WebMvcRequestContextArgumentResolver());
    }
  };
}

리졸버가 하는 일은 하나이고 없으면 던진다

WebMvcRequestContextHolder.require(request) 하나이며, 속성이 없으면 던진다 — "no web request context on this request; a fabricated one here would make an unauthenticated call look like an anonymous actor somebody chose". 리액티브 쪽은 익명 액터로 고정되어 있다. P1.

권고

(1) 서블릿에 store를 부르는 필터를 추가한다(WebMvcEvidenceFilter가 이미 요청당 한 번 도는 자리다). (2) 두 생산자가 보안 컨텍스트에서 액터·테넌트를 읽게 한다 — WebSecurityContextBridge가 그 목적으로 이미 존재한다(§12.2). (3) 픽스처가 손으로 컨텍스트를 만드는 대신 리졸버/필터를 지나게 한다. (3) 없이는 같은 상태가 다시 성립한다.

확인하지 못한 것

두 종점에 실제 요청을 보내 오류와 거부를 재현하지 않았다. 호출자 부재와 상수 고정상 그 결과가 나온다.