Files
llm-wiki/docs/superpowers/specs/2026-07-18-keycloak-branch-note-consistency/adversarial-review.md
T

31 KiB

Keycloak Branch-note Adversarial Review

대상은 lane 4개에서 추출한 60개 finding 전부다. 각 행은 원 주장을 실무성, 과장, 전제의 세 관점에서 반증하려고 시도한 결과다.

Summary

Metric Count
KEEP 21
DOWNGRADE 27
REJECT 12
Final High 10
Final Medium 18
Final Low 20
Removed 12

Per-finding Falsification

Finding ID Original Claim Strongest Counterargument Evidence Needed To Falsify Falsification Result Verdict Final Severity
L1-F01 ROLE_* 입력과 raw-role registry 계약이 다르다. ROLE_*는 Spring Security 경계에서 생성되는 GrantedAuthority이고 raw role은 그 이전 AuthenticatedUser 저장 형식일 수 있다. registry 호출 직전에 명시적 변환이 있다면 서로 다른 처리 단계를 말하므로 충돌하지 않는다. AuthorizationAdapter 호출 직전 값, registry key, binding test에서 admin과 ROLE_ADMIN 중 실제 lookup key를 확인한다. 중간 변환 증거가 없고 명세는 registry가 raw role을 소비한다고 적는다. KEEP Medium
L1-F02 unlink guard의 consumer 상태가 owner보다 오래됐다. owner 근거가 Keycloak main 소스라면 실제 배포 release tag의 guard와 다를 수 있다. consumer의 needs-confirmation은 stale 상태가 아니라 배포 이미지 기준 확인을 남긴 보수적 gate일 수 있으므로 영향은 라벨 정합에 한정될 수 있다. 사용 image tag의 LinkedAccountsResource와 마지막 identity unlink 응답을 main과 비교한다. 배포 tag 동일성이 확인되지 않아 영향 강도를 낮춘다. DOWNGRADE Low
L1-F03 위임한 email_verified 정책이 active owned decision으로 남았다. 같은 Decisions 섹션 뒤에 재프레이밍과 owner 위임이 있어 앞 문장은 역사 보존으로 읽을 수 있다. 최신 DEM과 구현 가이드만 실행 기준으로 쓰는 저장소 관행이 확실하다면 실제 오귀속 가능성은 제한된다. 신규 구현자가 선택하는 owner와 checker가 오래된 bullet을 active D-row로 파싱하는지 확인한다. 위임은 유효하지만 오래된 active-looking 문구의 영향은 낮다. DOWNGRADE Low
L1-F04 password 확인 고정 흐름과 SMTP email verification 기본이 갈린다. SPA UX는 security-first profile을 설명하고 owner는 그 profile에서 email authenticator를 끄고 password 재인증을 고를 수 있게 한다. 제품 기본값과 프로젝트 강화 profile은 동시에 성립할 수 있어 무조건적인 기술 모순은 아니다. realm export의 First Broker Login execution과 SMTP 설정, password 화면 E2E를 확인한다. 조건 없는 문구 drift만 남고 profile이 고정되면 양립한다. DOWNGRADE Medium
L1-F05 IMPORT의 takeover 근거가 최신 DEM과 반대다. 오래된 사용자 결정 뒤 correction memo와 D5가 잘못된 인과를 이미 정정한다. DEM을 정본으로 소비하면 잘못된 IMPORT 선택은 실행되지 않으며 문제는 active 정책보다 역사 문장의 상태 표시다. 실제 realm Sync Mode, ADR 선택 이유, correction을 보지 않은 구현 사례를 확인한다. 정정이 이미 있어 실무 영향은 낮다. DOWNGRADE Low
L1-F06 persistent sub key와 collision locator가 합쳐졌다. D1과 구현 가이드는 IdP alias와 sub를 저장 key로 두면서 OOTB collision discovery는 email 또는 username임을 별도 위험으로 기록한다. 이미 저장 key와 최초 후보 탐색을 나눴으므로 finding이 전제한 단일 key 강제가 없다. E2E에서 email은 후보 탐색에만 쓰고 federated identity에는 sub가 저장되는지 확인한다. 원문이 두 단계를 이미 분리한다. REJECT N/A
L1-F07 OAuth 2.1의 조건부 BFF 권고를 일반 권고처럼 표시한다. 같은 문서 D5와 인접 설명에 client credentials 조건이 있어 전체를 읽으면 선택 오류가 줄어든다. 조직 정책으로 BFF를 우선할 수도 있으므로 matrix 한 셀만으로 실제 architecture 오선택까지 단정하기는 어렵다. matrix만 사용한 ADR 결과와 전체 문서를 사용한 결정 결과를 비교한다. wording drift는 확인되나 failure 인과가 약하다. DOWNGRADE Low
L1-F08 bridge network의 localhost issuer와 extra_hosts 기본은 함께 동작하지 않는다. rendered Compose가 host network, localhost host-gateway alias, 또는 별도 jwk-set-uri를 사용하면 정상일 수 있다. 그러나 현재 문서는 host.docker.internal만 추가하면서 discovery URL을 localhost로 유지해 그 반론을 뒷받침하지 않는다. docker compose config, 컨테이너 DNS, discovery URL, JWKS fetch log를 함께 확인한다. 현재 선택 기본을 구제하는 구성이 문서에 없다. KEEP High
L1-F09 parent와 child가 Google sub federation key를 함께 소유한다. parent가 pattern requirement를, child가 Keycloak mechanism을 소유하는 분할은 가능하다. 하지만 parent가 D1~D5를 설정 SSOT라고 선언하고 양쪽이 같은 key와 email 금지를 결정하므로 concern 층위 분리가 현재 텍스트에는 없다. requirement와 mechanism concern ID, inbound reference, 변경 승인 주체를 확인한다. 같은 mutable key 정책에 두 SSOT가 있다. KEEP High
L1-F10 P1B가 P1A와 동일하다면서 backend trust를 바꾼다. P1B가 defense-in-depth JWT validation을 추가한 별도 variant라면 달라도 된다. 그러나 비교 표가 backend 검증 행을 동일이라고 직접 표시해 별도 variant 해석이 현재 텍스트와 양립하지 않는다. 두 pattern의 dependency, SecurityFilterChain, token과 header forwarding을 diff한다. 동일 표시와 header-only owner 계약이 직접 충돌한다. KEEP High
L1-F11 oauth2-proxy와 Traefik ForwardAuth를 대안으로 쓰지만 함께 사용한다. Traefik ForwardAuth가 middleware와 외부 auth service 묶음을 줄인 표현일 수 있고 숙련자는 forwardAuth.address가 oauth2-proxy를 호출함을 알아낼 수 있다. 따라서 architecture 모순보다 선택축 이름이 부정확한 문제에 가깝다. 각 option의 service와 forwardAuth target을 적은 배포표로 신규 구현자의 선택을 관찰한다. 상호 배타적 표현은 고쳐야 하나 심각도는 낮춘다. DOWNGRADE Medium
L1-F12 브라우저 token 부재와 cookie token 저장이 같은 threat model에 섞였다. 노출 없음은 브라우저가 bytes를 보유하지 않는다는 뜻보다 JavaScript가 raw token을 읽지 못한다는 축약일 수 있다. cookie session과 server-side store를 조건부 대안으로 설명했다면 두 문장이 같은 배치를 뜻하지도 않는다. session-store-type, cookie payload, Redis 사용 여부로 실제 custody를 구분한다. 물리 보유와 JS 접근성의 용어 drift만 확정된다. DOWNGRADE Low
L1-F13 zero-change 범위가 hosted login보다 넓게 읽힌다. 문서는 Account Linking을 명시적으로 out of scope로 두고 기존 SPA login path 유지로 목표를 제한한다. 제외된 별도 기능까지 제목이 포함한다고 해석하면 이미 제공된 scope boundary를 무시하게 된다. parent acceptance와 branch TODO에 CIAL 또는 linked-account API가 배정됐는지 확인한다. explicit out-of-scope가 원 주장을 반증한다. REJECT N/A
L1-F14 zero-change aud owner와 값이 모호하다. 더 직접적인 L4-F08이 같은 audience mapper와 validator root cause 및 remediation을 포괄한다. 독립 유지하면 동일 결함을 두 priority로 계산하므로 위험 수를 부풀리게 된다. realm client ID, mapper target, backend expected value를 한 matrix에서 비교한다. L4-F08과 독립 조치가 없는 중복이다. REJECT N/A
L2-F01 미아카이브 CVE 때문에 Confirm Link에 version gate가 필요하다. draft가 해당 CVE의 raw 출처와 영향 버전을 UNVERIFIED로 분류한다. 존재와 영향 범위가 확인되지 않은 외부 메모만으로 source-backed owner의 보장을 충돌로 판정하면 severity가 검증되지 않은 주장 하나에 의존한다. 공식 advisory, CVE record, 영향 및 패치 버전, 배포 image tag와 재현 flow를 확인한다. 핵심 반증 근거가 trace되지 않아 독립 finding으로 유지하지 않는다. REJECT N/A
L2-F02 email_verified=false가 silent-link 차단과 hard-reject 사이에서 갈린다. 다른 repository나 deferred branch에 custom SPI가 이미 있다면 hard-reject consumer 문장이 맞을 수 있다. 그러나 corpus는 SPI를 out of scope로 두고 consumer는 구현 근거 없이 링크와 생성 거부를 acceptance처럼 사용한다. authentication flow export, provider JAR, false-email E2E 응답으로 custom reject 존재를 증명한다. corpus 안에 hard-reject owner와 artifact가 없다. KEEP High
L2-F03 IdP IMPORT와 role mapper FORCE의 적용 범위가 불명확하다. IdP default IMPORT 위에 특정 mapper의 Sync Mode Override FORCE를 두는 계층 설정은 의도적으로 공존할 수 있다. attribute owner도 role freshness가 필요한 sibling의 FORCE를 설명해 실제 contradiction보다 범위 표기가 부족한 문제일 가능성이 높다. export에서 IdP syncMode와 mapper syncModeOverride, 로그인 후 profile과 role 갱신을 확인한다. 공존 가능한 설정이므로 심각도를 낮춘다. DOWNGRADE Low
L2-F04 basic-scope 예외를 오래된 test-user 설명이 덮는다. 실제 authorize request에 sensitive scope가 더 있거나 Console audience 조건이 다르면 옛 TODO가 별도 scenario로 맞을 수 있다. 그러나 현재 D5는 openid profile email만 고정해 그 조건이 문서에 없다. 실제 scope와 Console audience, 비등록 계정 동의 및 만료 동작을 확인한다. 현재 선택 scope에서는 authoritative D5와 어긋난다. KEEP Low
L2-F05 JavaScript origins를 owner는 비우고 consumer는 등록한다. 향후 Google browser SDK를 쓴다면 origin이 필요할 수 있고 불필요한 origin 하나가 즉시 인증 실패를 만들지는 않는다. 현재 server-side brokering에 한정하면 stale이지만 영향은 낮다. browser가 Google SDK를 직접 호출하는지 network trace와 두 client E2E로 확인한다. 현재 topology에서 불필요하지만 낮은 위험이다. DOWNGRADE Low
L2-F06 Caddy default HSTS 설명이 명시적 구성과 상충한다. Cloudflare나 상위 load balancer가 HSTS를 넣으면 최종 응답에는 header가 있을 수 있다. 그래도 제품 default와 외부 주입은 다른 사실이므로 Caddy default라는 설명을 구제하지 못한다. 최소 Caddyfile와 production edge 응답을 각각 캡처해 header 생성 주체를 확인한다. default 설명은 현재 authoritative 구성과 어긋난다. KEEP Medium
L2-F07 Reference-Only를 선언하고 foreign detail을 유지한다. composition hub의 end-to-end 상세는 유용하고 owner 우선 주석이 사람에게 정본을 알려줄 수 있다. 하지만 저장소 계약은 자동 생성 view가 아닌 수동 상세 복제를 금지하므로 규칙 변경이나 동기화 장치 없이는 반론이 성립하지 않는다. owner 변경 시 consumer가 자동 갱신되거나 CI가 drift를 검출하는지 확인한다. 현 상태는 수동 상세 복제다. KEEP Medium
L2-F08 P2A와 P2B가 brokering zero-change를 함께 소유한다. baseline은 확장 가능성, P2B는 실제 acceptance를 소유한다고 나눌 수 있다. 그러나 두 D-row가 같은 code-zero invariant를 담고 consumer가 이중 주장으로 표기해 change authority 경계가 없다. 두 D-row의 concern ID, approval owner, zero-change branch inbound map을 확인한다. 동일 mutable invariant가 여러 D-row에 있다. KEEP Medium
L2-F09 SPA Direct 안의 HttpOnly cookie는 사실상 BFF다. consumer가 이미 BFF 변형이라고 표시해 pure SPA 기본을 몰래 바꾼 결정이 아닐 수 있다. 더 직접적인 owner finding L4-F04가 architecture tension과 조치를 포괄한다. P2A 기본 storage와 refresh endpoint 유무를 L4-F04 owner와 비교한다. explicit variant 표기와 중복 때문에 제거한다. REJECT N/A
L3-F01 auth_request body 설명과 proxy_pass_request_body default가 충돌한다. 첫 문장은 auth_request core subrequest, 표는 proxy handler가 upstream으로 body를 전달하는 별도 단계를 설명할 수 있다. 층위를 분리하면 양립하며 실행 config는 이미 off를 선택한다. off 제거 전후 auth upstream의 Content-Length와 body bytes, nginx -T를 비교한다. 설명 층위는 섞였지만 실행 실패는 현재 방지된다. DOWNGRADE Medium
L3-F02 sign-in TODO가 browser와 API 모두 302로 만들 수 있다. TODO는 named location을 작성하라는 작업일 뿐 어느 route에 적용하라는 지시가 아니다. 뒤 D9가 browser만 연결하고 API는 401로 두므로 전 route 적용 가정은 텍스트에서 나오지 않는다. rendered config의 error_page가 붙은 location 목록과 route별 E2E 응답을 확인한다. named location 생성과 적용 범위를 혼동했다. REJECT N/A
L3-F03 local JWT 동작을 token introspection이라고 부른다. 팀 내부 shorthand로만 쓰면 운영 영향이 작을 수 있다. 그러나 RFC 7662 network call 여부는 latency, revocation, firewall 설계를 바꾸므로 protocol 용어를 그대로 두면 실제 오해가 생긴다. oauth2-proxy debug log와 outbound capture에서 introspection endpoint 호출을 확인한다. rename 비용이 작고 오해 위험이 남는다. KEEP Low
L3-F04 세 decision reference가 parser 형식이 아니다. 사람은 bare slug와 D-id를 이해하고 owner 변경이 드물면 drift가 없을 수 있다. 하지만 저장소가 deterministic impact analysis를 목표로 하고 checker가 wikilink 형식을 요구해 사람 가독성만으로 계약을 충족하지 못한다. 수정 전후 checker packet과 inbound reference에 세 edge가 들어오는지 비교한다. deterministic reference contract를 충족하지 않는다. KEEP Low
L3-F05 폐기한 numbered naming이 active-looking D2로 남았다. 폐기 경고가 앞에 있고 D2가 역사 bullet이라 checker가 active owner로 보지 않을 수 있다. 독자가 경고를 먼저 보면 신규 branch가 옛 규칙을 채택할 가능성도 낮다. D-row parser 결과, inbound D2 reference, scaffold가 numbered slug를 만드는지 확인한다. stale 표현은 맞지만 영향은 낮다. DOWNGRADE Low
L3-F06 D1과 D5가 S256 enforcement를 함께 소유한다. D5는 TODO step을 D1에 매핑한 trace row로 읽히고 같은 문서 안에서 값도 같다. 별도 owner 경쟁이나 상충 값이 없어 task mapping을 독립 mutable decision으로 본 것이 과하다. inbound reference가 D5를 owner로 쓰는지와 변경 승인 주체를 확인한다. 독립 ownership 증거가 없다. REJECT N/A
L3-F07 오래된 PKCE Admin label이 TODO에 남았다. Keycloak version과 locale에 따라 옛 label이 남을 수 있고 최신 구현 가이드도 함께 있다. 하지만 pinned target에서 TODO는 실행 checklist이므로 실제 UI와 다르면 작업을 방해한다. target image Admin Console과 realm export field명으로 실제 label을 고정한다. version 확인 뒤 국소 수정할 유효 drift다. KEEP Low
L3-F08 trycloudflare.com을 영구와 random으로 함께 설명한다. 특정 client가 named hostname을 지속 제공한다면 첫 설명이 맞을 수 있다. 그러나 보존 source model은 random quick tunnel과 managed custom hostname을 나누며 parent도 정적 redirect domain을 요구한다. tunnel create와 restart 전후 hostname, DNS route, Google redirect E2E를 기록한다. 현재 source model에서는 두 유형이 양립하지 않는다. KEEP High
L3-F09 realm export가 import wiring을 scope에 남겼다. 자동 import는 system outcome이고 mount path와 startup flag의 변경 권한을 주장하지 않을 수 있다. 명시적 trace가 stack owner를 지목하므로 실제 ownership 충돌보다 scope 문구가 모호한 문제다. 두 branch의 artifact 목록과 실제 PR 변경 파일로 wiring 수정 주체를 확인한다. owner trace가 있어 severity를 낮춘다. DOWNGRADE Low
L3-F10 credential export를 근거 없이 사실로 단정한다. 기본 realm export와 별도 user export는 다른 mode라 password 없음과 특정 credentials 포함 가능이 동시에 맞을 수 있다. 문제는 상호 모순보다 pinned version의 primary evidence가 없다는 점이다. target kc.sh export help, JSON credentials, reimport 로그인 결과를 보존한다. unsupported certainty는 남지만 모순 서사는 약하다. DOWNGRADE Medium
L3-F11 rotation concept와 execution note가 같은 sequence를 함께 소유한다. 한쪽은 protocol contract이고 다른 쪽은 executable scenario라 기대값 반복은 자연스럽다. P3A D5가 시연으로 명명되고 양쪽이 미근거 family behavior를 표시해 역할 분리도 일부 존재한다. inbound reference와 approval owner로 P3A가 concept D4를 consume만 하는지 확인한다. owner 경쟁보다 invariant와 test split일 가능성이 높다. DOWNGRADE Medium
L3-F12 back-channel receiver가 위임됐지만 destination은 optional이다. optional demonstration에 receiver 구현과 token validation이 포함될 수 있어 한 줄만으로 owner 부재를 단정할 수 없다. 다만 planned artifact와 endpoint acceptance가 없어 구현자가 포함 범위를 알기 어렵다. P3A planned files의 endpoint, validator, provider-trigger E2E를 확인한다. owner gap 가능성은 있으나 전체 context가 부족하다. DOWNGRADE Medium
L3-F13 Max Reuse >0을 무의미와 약한 대안으로 함께 쓴다. 무의미는 strict stolen-token demonstration에 부적합하다는 축약일 수 있고 다른 note도 값이 커지면 탐지력이 약해져 해당 시연이 불가하다고 적는다. 범위를 좁히면 같은 trade-off다. target에서 값 0과 1, 병렬 refresh, RT 재사용 결과를 측정한다. incompatible policy보다 범위 없는 표현 문제다. DOWNGRADE Medium
L3-F14 Caddy handle_path가 method B의 prefix를 제거한다. 다른 rewrite가 prefix를 복구하거나 Keycloak이 root path로 실행되면 동작할 수 있다. 그러나 copyable draft와 D3에는 그런 layer가 없고 note 자체도 handle로 바꾸라고 정정한다. caddy adapt와 upstream access log에서 discovery, auth, token path를 확인한다. 현재 선택 조합은 직접 404를 만들 수 있다. KEEP High
L3-F15 parent와 child가 Cloudflare 우선 선택을 함께 소유한다. integration parent가 provider를 고르고 child가 운영 detail을 소유하는 분할은 가능하다. 그러나 양쪽 D-row가 provider 우선순위와 fallback 조건까지 결정해 selection과 detail의 경계가 없다. owner map과 inbound D3 및 D1 reference, 변경 승인 주체를 확인한다. 같은 mutable preference가 두 normative D-row에 있다. KEEP Medium
L3-F16 parent exact proxy bundle과 child detail owner가 겹친다. integration parent는 여러 child 결과를 조립한 deployable bundle을 소유할 수 있고 detail owner는 설명과 근거만 뜻할 수 있다. child의 변수별 D-row를 함께 보지 않으면 exact 이중 ownership은 입증되지 않는다. child DEM의 다섯 변수 D-row와 parent D4의 change authority를 비교한다. assembly와 detail 경계 context가 부족하다. DOWNGRADE Medium
L4-F01 localhost와 extra_hosts 대안이 실행 구성으로 종결되지 않았다. L1-F08이 실제 Compose 기본의 hostname 불일치를 더 직접적으로 포착하고 iss-mismatch note는 대안과 미승인 상태를 상세히 기록한다. 이 행은 같은 root cause를 덜 정확하게 반복한다. parent D3, child D6, rendered Compose의 DNS와 discovery 역할을 비교한다. L1-F08과 조치가 같은 중복이다. REJECT N/A
L4-F02 audience owner 정정 뒤 parent 표가 옛 owner를 지목한다. 표가 과거 child inventory라면 history일 수 있다. 그러나 셀은 현재 component 구현 근거를 직접 가리키고 정정과 같은 문서에 있으면서 역사 표시가 없다. pointer 수정 전후 packet과 구현자가 선택한 owner를 비교한다. current reference가 명시적 owner split과 충돌한다. KEEP Medium
L4-F03 PKCE 순서가 parent manual-first와 child library-first로 반대다. parent의 이어지는 문구가 child D1을 정본으로 두고 실제 coding은 library-first라고 drift를 이미 명시한다. 앞 절반만 취하면 해소된 경고를 active contradiction처럼 계산한다. parent 전체 D4 cell과 implementation plan이 쓰는 순서를 확인한다. source가 child order를 canonical로 확정한다. REJECT N/A
L4-F04 SPA Direct의 HttpOnly refresh cookie는 TMB를 요구한다. taxonomy가 기존 Resource Server의 작은 refresh endpoint를 AP1 변형으로 허용하면 명칭은 유지할 수 있다. 그래도 server-side custody, Set-Cookie, CSRF, rotation 책임이 추가되어 trust boundary가 달라진다. architecture owner의 AP1 정의와 실제 refresh endpoint, cookie issuer, CSRF test를 확인한다. 문서도 사실상 TMB라 인정하지만 기본 선택을 닫지 않았다. KEEP High
L4-F05 근거를 해소한 D3와 D6가 Claims에서 unsupported로 남았다. Claims 표가 조사 전 history라면 모순이 아니고 runtime E2E는 여전히 남는다. 다만 날짜와 resolved 표시 없이 active queue에 있어 문헌 근거와 runtime 검증을 혼동시킨다. Claims 상태 transition과 후속 task의 중복 조사 여부를 확인한다. stale이지만 영향은 재조사 비용에 가깝다. DOWNGRADE Low
L4-F06 cookie-CSRF를 위임한 audience owner에 관련 decision이 없다. TMB 변형을 채택하지 않으면 cookie CSRF concern이 생기지 않고 unseen BFF owner가 소유할 수도 있다. 두 note만으로 project-wide owner 부재를 확정하기에는 enumeration이 부족하다. 선택 storage profile, refresh endpoint owner, CSRF D-row와 negative test를 확인한다. 조건부 concern이고 전역 context가 부족하다. DOWNGRADE Low
L4-F07 custom audience validator 필수 목표와 property-first 선택이 어긋난다. custom validator를 학습 예제로 두고 production baseline은 property-first로 둘 수 있다. D1과 구현 가이드가 단일 audience는 property, 복합은 custom으로 이미 조건을 기록해 실제 선택은 종결됐다. TODO와 plan에서 baseline과 비교 학습 단계가 분리됐는지 확인한다. 상단 wording만 stale이라 severity를 낮춘다. DOWNGRADE Low
L4-F08 expected audience가 backend-client-id와 spa-client로 갈린다. API audience를 의도적으로 spa-client와 같게 둘 수 있다. 그러나 owner는 backend mapper를 필수로 하고 consumer는 다른 literal을 고정하며 그 동일성 결정이 없어 정상 acceptance를 신뢰할 수 없다. realm client IDs, Audience mapper, token aud, backend expected value와 negative test를 캡처한다. 별도 client 전제에서 401 또는 잘못된 허용으로 이어진다. KEEP High
L4-F09 audience owner 위임 뒤 role note가 세부를 재명세한다. 한 파일에서 RS setup과 RBAC를 함께 구현하는 fold-in snapshot일 수 있고 owner 주석이 정본을 알려준다. 다만 복제된 spa-client 값이 owner와 이미 갈려 drift가 현실화했다. role PR이 owner 없이 audience 값을 바꾸는지와 checker 추적 결과를 확인한다. ownership 문제는 남지만 L4-F08과 영향이 겹친다. DOWNGRADE Low
L4-F10 realm-role-only의 single-client 전제가 multi-client base와 stale하다. RBAC branch는 현재 한 client만 대상으로 하거나 여러 client에 같은 realm-global policy를 의도할 수 있다. 서로 다른 rollout 단계와 authz scope를 비교해 stale이라고 단정했다. rollout target과 각 client permission namespace 필요를 realm export로 확인한다. multi-client가 곧 client role 필요를 뜻하지 않는다. DOWNGRADE Low
L4-F11 3-leg 표기에 Backend가 없지만 Hop 3 verifier는 Backend다. 3-leg는 federation actor chain의 학습 약칭이고 hop table은 API validation을 더한 별도 sequence일 수 있다. 실제 표에는 Backend가 있어 구현 verifier 누락이라는 failure도 이미 완화된다. diagram과 acceptance가 Backend validation을 빠뜨리는지 확인한다. naming ambiguity만으로 독립 defect를 구성하기 어렵다. REJECT N/A
L4-F12 면접 문장은 nonce 자동 처리를 사실로 말하지만 D3는 미검증이다. OIDC broker가 nonce를 처리할 것이라는 기대는 합리적이고 표준 의무를 요약했을 수 있다. 그래도 특정 Keycloak version의 자동 처리를 외부 답변으로 말하려면 표준 의무와 제품 관측을 나눠야 한다. source 또는 HAR에서 nonce 발행, upstream token, callback 대조와 거부를 확인한다. 문서가 제품 동작 미검증을 인정하면서 외부 문장은 사실형이다. KEEP Medium
L4-F13 out-of-scope mapper와 linking을 구체 정책으로 재진술한다. 해당 row는 관심사 이름과 owner link만 한 줄로 요약해 상세 mechanism 복제보다 Reference-Only가 허용하는 pointer와 요약에 가깝다. 실제 설정값이나 예외도 재명세하지 않는다. row가 owner D-number와 한 줄 조건만 갖고 구체 값이 없는지 확인한다. 상세 재진술 분류가 source 추상도와 맞지 않는다. REJECT N/A
L4-F14 Traefik note가 P3A를 edge-auth pattern으로 잘못 귀속한다. 같은 EC2에 oauth2-proxy를 추가한 배포 변형은 가능하다. 그러나 P3A owner가 SPA Direct와 backend JWT validation을 정의하므로 배포 가능성만으로 인증 pattern ID를 바꿀 수 없다. AP taxonomy, P3A component diagram, Traefik target pattern을 대조한다. 배포 축과 authentication architecture 축이 혼동됐다. KEEP High
L4-F15 middleware가 302를 생성한다는 설명과 pass-through D3가 갈린다. browser 관점에서는 Traefik endpoint에서 302를 받으므로 middleware가 발급했다는 관찰 축약일 수 있다. 생성 주체와 전달 주체 구분은 장애 분석에 유용하지만 architecture 자체를 바꾸는 차이는 아니다. oauth2-proxy와 Traefik log, Location header origin을 함께 추적한다. 책임 표현 drift는 맞지만 영향이 낮다. DOWNGRADE Low
L4-F16 relative API와 token URL이 static-only nginx로 향한다. 실제 nginx나 dev server에 문서 밖 proxy가 있으면 정상이다. 그러나 parent가 nginx를 static-only로 명시하고 3-port CORS topology를 선택해 그런 proxy를 배제한다. deployed nginx config, browser resolved URL, backend와 Keycloak access log를 확인한다. 현재 명세대로면 두 request가 nginx에서 실패한다. KEEP High
L4-F17 oidc-client-ts default를 localStorage라 하면서 미증명으로 둔다. pinned version에서 실제 default가 localStorage라면 기술값은 맞고 raw evidence만 보강하면 된다. stateStore와 userStore가 다른 default를 가져 단일 문장이 지나치게 뭉쳤을 수도 있다. lock version의 공식 API와 runtime storage keys로 두 store와 token persistence를 확인한다. evidence drift는 맞지만 기술값 오류는 미확정이다. DOWNGRADE Low
L4-F18 silent SSO는 제외했는데 automaticSilentRenew mechanism은 미결정이다. 문서가 이를 Claims To Verify로 명시해 아직 선택되지 않은 planned work로 관리한다. 미결정 상태는 양립 불가능한 active decision이 아니고 network trace로 닫는 gate도 있다. pinned config와 expiry trace에서 refresh grant 또는 iframe 호출을 확인한다. explicit open question을 consistency defect로 올렸다. REJECT N/A
L4-F19 Phase C2 뒤 active 표가 codes와 CORS와 redaction을 미구현으로 둔다. raw branch note가 설계 시점과 as-built audit를 시간순으로 보존하고 하단 C2 표가 최신임을 표시한다면 오래된 행은 history다. 신규 task가 옛 표를 소비하지 않으면 runtime 위험보다 탐색 비용 문제다. current code와 C2 artifact, 옛 표의 downstream 참조를 확인한다. stale은 맞지만 실제 영향 근거가 부족하다. DOWNGRADE Medium
L4-F20 decoder와 clock skew가 세 상태로 기록된다. custom wrapper 안에서 vendor default validator를 쓸 수 있어 custom bean과 default leeway는 동시에 성립한다. code owner를 보지 않고 세 문장을 모두 상호 배타적으로 분류한 부분이 있다. bean graph, validator chain, skew boundary test로 현재 mechanism을 확인한다. custom 미작성 drift는 남지만 source만으로 정확한 상태를 못 정한다. DOWNGRADE Medium
L4-F21 public-path 계약이 reflection과 env snapshot을 함께 유지한다. 앞쪽 정정이 reflection 폐기와 env gate의 한계를 명시하고 C2 표도 current task를 제공하면 뒤 Test Contract는 역사 초안으로 식별할 수 있다. 실제 구현자가 정정을 읽는다면 옛 task 재생성 가능성은 제한된다. snapshot task, CI wiring, reflection command의 downstream 참조를 확인한다. active-looking command drift는 남지만 current mechanism은 정정됐다. DOWNGRADE Medium

Consolidation

  • Issuer/network: L1-F08을 유지하고 L4-F01은 중복 제거.
  • Audience: L4-F08을 대표로 두고 L1-F14는 제거, L4-F02와 L4-F09는 보조 finding.
  • SPA Direct/TMB: L4-F04를 대표로 두고 L2-F09는 제거, L4-F06은 조건부 owner risk.
  • Account linking: L2-F02를 대표로 유지하고 L1-F03은 낮은 우선순위로 조정.
  • Rotation: L3-F11, L3-F12, L3-F13을 한 승인 단위에서 함께 검증.
  • Security baseline: L4-F19, L4-F20, L4-F21을 as-built 대조 한 번으로 처리.

Machine Summary

agent: wiki-adversarial-reviewer
KEEP: L1-F01,L1-F08,L1-F09,L1-F10,L2-F02,L2-F04,L2-F06,L2-F07,L2-F08,L3-F03,L3-F04,L3-F07,L3-F08,L3-F14,L3-F15,L4-F02,L4-F04,L4-F08,L4-F12,L4-F14,L4-F16
DOWNGRADE: L1-F02,L1-F03,L1-F04,L1-F05,L1-F07,L1-F11,L1-F12,L2-F03,L2-F05,L3-F01,L3-F05,L3-F09,L3-F10,L3-F11,L3-F12,L3-F13,L3-F16,L4-F05,L4-F06,L4-F07,L4-F09,L4-F10,L4-F15,L4-F17,L4-F19,L4-F20,L4-F21
REJECT: L1-F06,L1-F13,L1-F14,L2-F01,L2-F09,L3-F02,L3-F06,L4-F01,L4-F03,L4-F11,L4-F13,L4-F18
agent: wiki-adversarial-reviewer
found: 60
processed: 60
dropped: 0