← All reports

CSS module scripts

Component: JSC / WebCore module loader | b1913f6

LayoutTests/imported/w3c/web-platform-tests/content-security-policy/style-src/import-style-blocked.sub-expected.txt

-Harness Error (TIMEOUT), message = null
-
-TIMEOUT import-style-blocked Test timed out
+PASS import-style-blocked

LayoutTests/imported/w3c/web-platform-tests/fetch/api/request/destination/fetch-destination.https-expected.txt

-FAIL Import declaration with `type: "css"` fetches with a "style" Request.destination promise_test: Unhandled rejection with value: "TypeError: Import attribute type \"css\" is not valid"
+PASS Import declaration with `type: "css"` fetches with a "style" Request.destination

JSC의 module loader는 module source를 가져오는 작업을 embedder인 WebCore에 맡깁니다. 이후 가져온 source를 parsing하여 module record를 생성하는데, record의 타입은 type import attribute에 따라 결정됩니다. 문제는 stylesheet가 WebCore 쪽 개념이라 JSC가 직접 parsing할 수 없다는 점입니다. 그래서 이번 변경으로 fetch hook이 source text 외에 opaque JSValue도 반환할 수 있게 되었습니다. JSC는 이 값을 별도 검증 없이 신뢰하고, 그대로 module의 default export로 설치합니다.

CSSModuleScriptsEnabled flag(기본값 off)가 켜진 경우를 살펴보면, import sheet from './a.css' with { type: 'css' }와 그 dynamic import 형태는 "style" destination과 style-src CSP를 적용해 stylesheet를 가져옵니다. 이어서 replaceSync로 CSSStyleSheet를 생성한 뒤, JSC에 JSCSSStyleSheet를 전달합니다. 한편 jsc, worker, worklet에서는 이 import가 거부됩니다.

Host가 parsing 과정 없이 완성된 JSValue를 JSC에 전달해 module export로 설치할 수 있게 된 것으로, host와 engine 사이에 새로운 신뢰 계약이 생긴 셈입니다.

JSModuleLoader::makeModule, ModuleRegistryEntry::provideFetch, 그리고 WebCore 쪽 호출 지점 사이의 계약을 점검할 필요가 있습니다. 확인할 지점은 다음과 같습니다. - 동일한 specifier가 서로 다른 type attribute로 요청되는 경우, cache된 entry나 JSValue가 타입 간에 혼동될 수 있는지 여부. - createCSSModuleScript/setCSSStyleSheet부터 JSC microtask(moduleRegistryFetchSettled, moduleLoadTopSettled)까지 이어지는 구간에서 sheet의 GC 및 lifetime. 특히 fetch 도중 document나 global object가 해제되는 경우. - Sheet가 생성되는 realm과 import를 수행한 realm의 일치 여부. - moduleTypeIsAllowed를 우회하는 경로가 존재하는지 여부. - CSP/CORS/credentials/integrity/redirect 처리가 script module과 동일하게 적용되는지 여부. - 아직 로딩 중이거나 charset 또는 BOM이 다른 sheet에 대해 replaceSync와 sheetText가 사용되는 경우.

Fuzzing 측면에서는 같은 URL에 대한 static import와 dynamic import를 섞고, Content-Type을 바꿔가며 요청하는 방식을 고려할 수 있습니다. 여기에 fetch 도중의 detach나 navigation을 함께 조합하는 것이 좋습니다.