--- kind: CASE slug: analysis-finding-a16-f005 title: 설정으로 정한 파서 한계가 graphql-java에 설치되지 않는다 topic: graphql-surface project: clean-architecture-backend-template status: 게시 전 sourceRevision: 21234e38cdb9a926cbc92bb97a2aee2e4a7d2916 rootTreeNode: case:analysis-finding-a16-f005 evidenceCapturedOn: 2026-09-01 body: case-analysis-finding-a16-f005.body.md assets: - key: analysis-finding-a16-f005 file: ../../../final/evidence/rendered/analysis-finding-a16-f005.svg evidence: - ../../../final/evidence/raw/analysis-finding-a16-f005.txt source: - 원본 분석 절은 analysis/16-adapter-inbound-graphql.md#L511 이다. --- # 설정으로 정한 파서 한계가 graphql-java에 설치되지 않는다 파서 계층은 이 방어의 첫 번째 관문이다. 리프가 한계를 값으로 갖고 설치 함수도 갖는데 호출하는 코드가 없다. 파서 옵션이 정적 전역이라 시작 시 한 번 설치하지 않으면 라이브러리 기본값이 유지된다. ## 관계 - **배열 원소 상한이 선언만 되고 강제되지 않으며 백스톱도 없다** 같은 형태의 선언과 강제 어긋남이다. - **5계층 예산 모델에서 요청 계층만 강제되고 나머지 파생이 전부 미배선이다** 같은 리프의 다른 미배선 사례다. - **설정은 받아들여지고 검증되며 효과가 없다** 이 사례가 그 규칙의 형태다. ## 문제 파서 계층은 이 프로토콜의 서비스 거부 방어에서 첫 번째 관문이다. 복잡도 계산도 구조 분석도 문서를 파싱한 뒤에 일어나므로, 파싱 자체를 폭발시키는 문서는 그 앞에서 막아야 한다. 이 리프가 그 한계를 실제로 거는지 확인했다. ## 결론 걸지 않는다. 한계를 값으로 갖는 타입이 있고 클라이언트 정책에서 파생된다. 설치 함수도 있다. 연산 기본값 설치라는 이름이다. 호출하는 코드가 없다. 이름이 가리키듯 이 라이브러리의 파서 옵션은 정적 전역이다. 시작 시 한 번 설치하지 않으면 라이브러리 기본값이 유지된다. 실패 시나리오는 이렇다. 운영자가 설정으로 파서 한계를 조인다. 그 값은 플랫폼 설정을 거쳐 클라이언트 정책까지 도달한다. 그러나 한계 타입을 만드는 팩토리를 부르는 코드가 없어 파서에 닿지 않는다. 실제로 적용되는 것은 라이브러리의 기본값이다. 설정은 받아들여지고 검증되며 효과가 없다. 노출의 크기는 라이브러리 기본값이 정한다. 이 판본은 토큰 수와 공백 토큰 수와 규칙 깊이에 자체 기본 상한을 두므로 무제한은 아니다. 그리고 구조 한계와 복잡도 계산은 배선되어 있어 파싱 이후 계층은 작동한다. 그래서 P1 이 아니라 P2 다. 침묵하는 설정 표면이자 방어 계층 하나의 부재다. 권고는 플랫폼 자동 설정에 시작 시 한 번 설치를 부르는 초기화 지점을 두는 것이다. 정적 전역이므로 빈 메서드보다 초기화 콜백이 적절하다. ## 검증 환경 확인 방식 : 설치 함수 호출자 검색과 파서 옵션 전역성 확인 소스 수정 : x ## 재현 조건 원문은 final/evidence/raw/182 계열에 있다. 1. 파서 한계 타입과 설치 함수를 확인한다. 2. 설치 함수의 호출자를 센다. 3. 라이브러리의 파서 옵션이 정적 전역인지 확인한다. 4. 설정 값이 어디까지 도달하는지 추적한다. 5. 구조 한계와 복잡도 계산이 배선되는지 확인한다. ## 본문 파서 계층은 GraphQL DoS 방어의 **첫 번째** 관문이다 — 복잡도 계산도 구조 분석도 문서를 파싱한 뒤에 일어나므로, 파싱 자체를 폭발시키는 문서는 그 앞에서 막아야 한다. ## 파서 계층이 첫 관문인 이유 :::evidence key="analysis-finding-a16-f005" alt="분석 문서 analysis/16-adapter-inbound-graphql.md 에서 이 기록의 근거 절을 그대로 잘라낸 15줄. 코드베이스를 측정한 것이 아니라 원본 판정이 무엇을 적었는지를 보여 준다." caption="analysis/16-adapter-inbound-graphql.md 발췌 — 15줄" zoom="true" ::: ## 값도 있고 설치 함수도 있고 호출하는 코드가 없다 이 leaf는 그 한계를 값으로 갖고(`GraphQlParserLimits`, 클라이언트 정책에서 파생), 설치 함수를 갖는다(`GraphQlParserOptionsFactory.installOperationDefaults`). `installOperationDefaults`라는 이름이 가리키듯 graphql-java의 파서 옵션은 정적 전역(`ParserOptions.setDefaultOperationParserOptions`)이고, 시작 시 한 번 설치하지 않으면 라이브러리 기본값이 유지된다. ## 실패 시나리오 운영자가 `backend.graphql.limits.*`로 파서 한계를 조인다. 그 값은 `GraphQlPlatformSettings` → `GraphQlClientPolicy`까지 도달하지만 `GraphQlParserLimits.from(...)`을 부르는 코드가 없어 파서에 닿지 않는다. 실제로 적용되는 것은 graphql-java 25.0의 기본값이다. 설정은 받아들여지고 검증되며 효과가 없다. ## 노출의 크기는 라이브러리 기본값이 정한다 graphql-java 25.0은 토큰 수·공백 토큰 수·규칙 깊이에 자체 기본 상한을 두므로 무제한은 아니다. 그리고 구조 한계(`GraphQlDocumentShapeAnalyzer`, autoconf=4)와 복잡도 계산(autoconf=4)은 배선돼 있어 파싱 이후 계층은 작동한다. 그래서 P1이 아니라 P2다 — 침묵하는 설정 표면이자 방어 계층 하나의 부재다. ## 권고 `GraphQlPlatformAutoConfiguration`에 시작 시 `GraphQlParserOptionsFactory.installOperationDefaults(GraphQlParserLimits.from(clientPolicy))`를 한 번 호출하는 초기화 지점을 둔다. 정적 전역이므로 `@Bean` 메서드보다 `InitializingBean`/`SmartInitializingSingleton`이 적절하다. ## 확인하지 못한 것 파서를 폭발시키는 문서를 보내 라이브러리 기본값이 적용되는지 재현하지 않았다. 호출자 부재상 그 결과가 나온다.