- SafeExpressionEvaluator 교체(engine-v1.60.0)가 구 평가기가 허용하던 화살표 파라미터 배열 구조분해(([k, v]) / ([, vid]))를 누락해 해당 문법을 쓰는 액션 params 가 미평가 원문으로 전송, 장바구니 담기/바로 구매가 전 상품 불능이던 회귀를 수정 (engine-v1.60.5, production 재빌드 동반). 저장소 레이아웃 54파일 104곳 사용 형태 전수를 단위/E2E 테스트로 잠금 - TrimStrings 가 타이틀 접미사의 선행 공백을 제거해 봇 렌더 제목이 "제목| 사이트명" 으로 접착되고 빈 제목 화면은 "| 사이트명" 이 매달리던 기존 결함 수정 — 조립 규칙을 composeTitleSuffix 단일 지점(SSoT)으로 정규화 - JSON-LD 직렬화에 JSON_HEX_TAG 미적용으로 검색어의 </script> 가 스크립트 컨텍스트를 조기 종료해 실행 가능한 script 요소가 재생성되던 반사형 XSS(기존 결함) 차단 — 태그 문자 이스케이프, 구조화 데이터 의미는 보존
53 lines
3.2 KiB
YAML
53 lines
3.2 KiB
YAML
feature: 레이아웃 표현식 평가 샌드박스 (KVE-2026-1915)
|
|
|
|
description: |
|
|
레이아웃 표현식은 AST 화이트리스트 인터프리터(SafeExpressionEvaluator)로 평가된다.
|
|
정상 표현식(삼항·옵셔널체이닝·nullish·화살표함수·스프레드·템플릿리터럴·new Date)은 그대로
|
|
평가되고, constructor 체인/Function 생성자를 통한 샌드박스 탈출은 코드를 실행하지 못한다.
|
|
|
|
단위 테스트가 소스를 잠그지만 사용자가 실행하는 것은 배포 번들이므로, 브라우저에서 배포
|
|
번들의 공개 API(`G7Core.evaluateCondition`)를 직접 호출해 (a) 정상 회귀 없음 + (b) 익스플로잇
|
|
미실행(전역 canary `window.__g7jsi` 미설정) 양면을 잠근다.
|
|
|
|
axes:
|
|
case: [safe_expression, arrow_template_literal, destructuring_param, constructor_escape, nonstring_key_escape, object_facade_safe]
|
|
|
|
effects:
|
|
- same_expression_same_value_across_paths
|
|
- sandbox_escape_blocked
|
|
- dangerous_payload_does_not_set_global
|
|
|
|
# 저장측 외부 URL 차단은 표현식 실행 축(case)과 곱해지지 않는 별개 관심사다 — case 는 "그 식이
|
|
# 실행되는가" 를, URL 축은 "그 값이 외부 origin 을 가리키는가" 를 본다. 축으로 올리면 조합을
|
|
# 관측하는 지점이 없어 어떤 칸도 커버되지 않으므로 sub_flow 로 분리한다.
|
|
sub_flows:
|
|
- id: external_url_storage_gate
|
|
description: |
|
|
레이아웃 저장 시 외부 origin URL 을 거부하는 축. 세 갈래를 함께 잠근다.
|
|
|
|
- 순회 대상: props·actions 뿐 아니라 init_actions(로드 시 자동 실행 → 외부 nav/apiCall 이
|
|
자동 리다이렉트·유출로 이어진다)까지. state/computed 는 데이터 값이라 의도적 제외이며,
|
|
실제 위험은 바인딩되는 sink(컴포넌트 prop)에서 발생하고 그 sink 는 이미 검사된다.
|
|
- 값 형태: 규칙이 배열 트리뿐 아니라 **문자열 스칼라**도 처리해야 한다. 문자열 필드에서
|
|
조기 반환하면 그 부착이 조용한 no-op 이 된다.
|
|
- 부착 패리티: Store·Update·UpdateContent·UpdateExtensionContent 4 경로가 동일 강도여야
|
|
한다. 편집기 저장 경로만 빠지면 그 경로가 우회로다.
|
|
effects:
|
|
- init_actions_external_url_rejected
|
|
- same_origin_path_in_init_actions_allowed
|
|
- plain_string_value_is_inspected_not_skipped
|
|
- same_origin_plain_string_allowed
|
|
- rule_attached_to_every_layout_content_form_request
|
|
|
|
test_files:
|
|
# 배포 번들 종단 (축·효과 마커 보유)
|
|
- tests/Playwright/specs/layout-expression-sandbox.spec.ts
|
|
# 소스 잠금 — 평가기 본체와 소비 싱크(바인딩·computed·scripts[].if·렌더 조건), 그리고
|
|
# 저장 단계 거부. 축(case)은 배포 번들 E2E 가 커버하고 이 파일들은 효과를 떠받친다.
|
|
- resources/js/core/template-engine/__tests__/SafeExpressionEvaluator.test.ts
|
|
- resources/js/core/template-engine/__tests__/ConditionEvaluator.sandbox.test.ts
|
|
- resources/js/core/__tests__/TemplateApp.safeComputed.test.ts
|
|
- tests/Feature/Rules/SafeLayoutExpressionsTest.php
|
|
# sub_flow external_url_storage_gate 를 떠받친다.
|
|
- tests/Feature/Rules/NoExternalUrlsTest.php
|