# 주제: 같은 리프 안에서 "정규 형식" 인코딩이 두 가지이며, 그중 하나는
#       바로 옆 파일이 명시적으로 금지한 방식이다
# revision: 21234e38cdb9a926cbc92bb97a2aee2e4a7d2916

# (A) 길이 접두 방식 — ApprovalGrant.canonicalForm() (ApprovalGrant.java:61-87)
#   javadoc:
#     "Every field is length-prefixed rather than delimited. A delimiter can be smuggled into a
#      ticket or an identity to make two different grants render identically; a length prefix
#      cannot."
#   구현:
#     private static void appendField(StringBuilder canonical, String value) {
#       canonical.append(value.length()).append(':').append(value);
#     }
#   대상 필드 11개: ticket, approvedBy, requestedBy, operation, source, target, topologyVersion,
#                   maxImpact, planDigest, approvedAt, validUntil

# (B) 구분자 결합 방식 — 계획 다이제스트 2곳
#   ReplayPlan.digest()  (ReplayPlan.java:51-64)
#     PlanDigest.ofCanonical(String.join("|",
#         "REPLAY", replayId, destination, isolatedConsumerGroup, from, to, estimatedMessages,
#         topologyVersion, targetsLiveConsumerGroup))
#   RedrivePlan.digest() (RedrivePlan.java:50-62)
#     PlanDigest.ofCanonical(String.join("|",
#         "REDRIVE", redriveId, source, target, batchSize, candidates,
#         alreadyRedrivenCandidates, topologyVersion))

# ---- 각 필드가 '|' 를 담을 수 있는가 ----
# command: cat messaging-core-api/.../destination/DestinationName.java
#   private static final Pattern VALID = Pattern.compile("[a-z0-9][a-z0-9.-]{0,159}");
#   -> destination/source/target 는 '|' 불가.
# replayId/redriveId : UUID.toString()      -> 불가
# 불리언             : "true"/"false"        -> 불가
# from/to            : Instant.toString()    -> 불가 ("" 가능)
# 정수/long          : 10진수                -> 불가
# topologyVersion    : String, 검증은 isBlank() 뿐  -> **가능**
#   command: git grep -n "topologyVersion" -- src/messaging/messaging-admin-api/src/main
#     ReplayPlan.java:37-38    if (topologyVersion == null || topologyVersion.isBlank()) throw …
#     RedrivePlan.java:37-38   동일
#   생산처: BrokerTopologyInspector.topologyVersion()  (messaging-admin-runtime, 애플리케이션이 구현하는 SPI)
#   -> 형식 제약 없음.

# ---- 현재 충돌 가능성 판정 ----
# 자유 필드가 topologyVersion 하나뿐이고, 그 앞의 모든 필드가 '|' 를 담을 수 없으므로
# 왼쪽에서 '|' 를 세면 앞 필드들의 경계가 확정되고, 마지막 필드(고정 형식)도 오른쪽에서 확정된다.
# 따라서 현재 필드 구성에서는 인코딩이 단사(injective)이며 **충돌을 만들 수 없다**.
#
# 판정: 지금은 악용 가능한 결함이 아니다. 기록하는 이유는 두 가지다.
#   1) 같은 리프의 ApprovalGrant 가 정확히 이 위험("구분자 밀반입")을 이유로 길이 접두를 쓰고
#      그 이유를 javadoc 에 남겼는데, 승인이 서명으로 묶이는 대상인 계획 다이제스트 두 곳은
#      그 규칙을 따르지 않는다.
#   2) 단사성이 "자유 형식 필드가 하나뿐" 이라는 우연한 조건에 의존한다.
#      자유 형식 필드가 하나 더 추가되거나 topologyVersion 의 위치가 바뀌면
#      조용히 깨지며, 깨졌을 때의 결과는 "한 승인이 다른 계획을 인가" 로
#      PlanDigest 가 존재하는 이유 자체가 무력화되는 것이다.

# 참고: PlanDigest 자체는 형식을 강하게 검증한다 (PlanDigest.java:23-28)
#   if (!value.matches("[0-9a-f]{64}")) throw new IllegalArgumentException(…)
