Files
Gnuboard7/tests/scenarios/trusted-script-hosts.yaml
T
HeuJung 1e89335258 fix(core,extensions): 동적 스크립트 주입 경로 전부에 출처 게이트 적용
레이아웃 scripts[] 에만 있던 원격 스크립트 차단 게이트가 다른 주입 경로에는
없어, 저장측(SafeLayoutExpressions·NoExternalUrls)이 외부 URL 저장을 422 로
막아도 런타임 디스패치 한 번으로 임의 원격 코드가 로드됐다. 저장 검증이
통째로 무의미해지는 상태였고 오류도 경고도 남지 않았다.

판정식을 support/scriptSrcPolicy.ts 로 분리해 런타임 SSoT 를 하나로 두고,
loadScript 액션 · 확장 핸들러 재로드 · 편집기 프리뷰 · 편집기 자산 매니페스트 ·
G7Core.asset.loadScript · PG 플러그인 SDK 주입이 모두 그것을 경유하게 했다.
사본을 두지 않은 이유는 차집합이 그대로 우회로가 되기 때문이다.

- 게이트는 캐시 검사보다 앞에 둔다 (뒤에 두면 이미 로드된 미신뢰 스크립트가
 캐시 히트로 통과한다)
- NoExternalUrls 순회를 9키로 확대 — 번들 레이아웃 601개 사전 스캔으로
 정당한 외부 URL 0건을 확인한 뒤 넓혔다 (저장 회귀 없음)
- callExternal 은 생성자를 참조 동일성으로 거부하고(별칭 전역 포함),
 프로토타입 경로 세그먼트를 읽기·매핑 키 양쪽에서 차단한다
- PG SDK 는 서비스 SDK 라 자체 호스팅이 불가능하므로 manifest 선언 +
 주입 직전 호스트 확인이 게이트다. tosspayments SDK URL 은 확장자가 없어
 정적 검사에 걸리지 않아 그 런타임 확인이 유일한 게이트다
- 함께 드러난 결함: 동시 loadScript 가 로드 전에 완료 처리되던 문제,
 확장 재로드에서 script 기존재가 CSS 까지 건너뛰던 문제, loadCSS 의
 in-flight 미공유
2026-09-02 21:43:13 +09:00

50 lines
3.2 KiB
YAML

feature: 레이아웃 scripts[].src 신뢰 호스트 allowlist (KVE-2026-1915 B-2)
description: |
레이아웃이 로드하는 외부 스크립트(scripts[].src)는 same-origin 또는 manifest 가 선언한
신뢰 호스트(활성 확장이 등록한 CDN 등)만 허용한다. 편집자는 신뢰 호스트를 추가할 수 없고
(manifest 는 배포물), 그 외 외부 origin 스크립트는 로드되지 않는다.
집계 체인은 런타임·저장측·정적검사 3층이 동형이며, 저장측(SafeLayoutExpressions/NoExternalUrls)
이 외부 URL 저장을 422 로 차단한다. 본 E2E 는 실제 화면 로드의 모든 script 요청이 same-origin
또는 신뢰 호스트임을 네트워크 레벨에서 무조건 측정한다.
같은 판정은 레이아웃 scripts[] 뿐 아니라 브라우저에 새 <script> 를 붙이는 모든 경로
(loadScript 액션 · 확장 핸들러 재로드 · 편집기 프리뷰 · G7Core.asset.loadScript · PG SDK 주입)
가 공유한다. loadScript 액션은 종전 이 게이트 밖이라 저장 검증을 우회한 임의 원격 코드가
런타임에 그대로 로드됐다 — 그 축을 loadscript-action-* 케이스가 잠근다.
axes:
case: [trusted_hosts_exposed_in_config, active_extension_host_reflected, untrusted_external_script_not_loaded, loadscript-action-untrusted, loadscript-action-sameorigin]
effects:
- trusted_script_host_allowlist_wired
- untrusted_external_script_blocked
- loadscript_action_gate_wired
# 선언 주체(모듈/플러그인/템플릿)는 브라우저 관찰 축(case)과 교차하지 않는 별개 관심사다 —
# case 는 배포 번들이 무엇을 로드하는가를, 선언 주체는 서버가 무엇을 집계하는가를 본다.
# 축으로 올리면 9조합이 되지만 그 조합을 관측하는 지점이 없어(마커가 두 파일에 갈려 있어)
# 어떤 조합도 커버되지 않는다. 관심사를 sub_flow 로 분리한다.
sub_flows:
- id: trusted_host_declaring_subject
description: |
신뢰 호스트 집계는 활성 모듈·플러그인·템플릿 세 주체를 모두 읽는다. 템플릿은 PHP 확장
추상 클래스가 없어(AbstractTemplate 부재) 모듈/플러그인과 집계 경로가 다르므로 별도
회귀 가드가 필요하다 — 이 구분이 없던 동안 코어가 템플릿만 집계하지 않는 상태
(정적검사·문서는 인정, 런타임·저장측은 차단)가 드러나지 않았다.
한 주체의 집계가 실패해도 나머지 주체 집계는 계속되어야 한다(실패 격리).
effects:
- every_extension_type_declaration_is_collected
- malformed_or_absent_declaration_is_ignored
test_files:
- tests/Playwright/specs/layout-script-src-trusted-host.spec.ts
- tests/Playwright/specs/loadscript-action-gate.spec.ts
- tests/Unit/Support/TrustedScriptHostsTest.php
- tests/Feature/Rules/NoExternalUrlsTest.php
# 차단 결정 함수(isAllowedScriptSrc)는 minify 된 배포 번들에서 이름으로 호출할 수 없어
# E2E 는 그 결과만 관찰한다. 결정 자체(신뢰 판정·authority 우회 차단)는 여기서 잠근다.
- resources/js/core/support/__tests__/scriptSrcPolicy.test.ts
- resources/js/core/__tests__/TemplateApp.scriptSrc.test.ts