크라우니 웹스킨 — 리스킨 커스터마이즈 기능
개요
web.crowny.org(crowny-webskin, :9889) 스킨 스토어에 "색 스왑=완전 리스킨" 판매 포인트를 실제 기능으로 구현. 다운로드 시 구매자가 지정한 CSS 커스텀 프로퍼티(
--변수명)를 서버가 화이트리스트 검증 후 HTML
</head> 직전에 주입, 스토어 미리보기 모달에서 라이브로 색·radius·모션을 조정할 수 있게 함.
무엇을 했는지
1. 서버 (server.js)
GET /api/download/<slug>?tokens=<base64(JSON)> 확장.
- 화이트리스트: 변수명
^--[a-zA-Z0-9-]{1,40}$, 값 ^[#a-zA-Z0-9(),.%\s'"-]{1,120}$ + 값에 </ 또는 url( 포함 시 drop (TOKEN_NAME_RE/TOKEN_VALUE_RE/sanitizeTokens).
- 통과 토큰 →
<style id="crowny-reskin">:root{...}</style> 를 각 HTML </head> 직전 주입 (tokensToStyleTag/injectReskinStyle).
- 스킨 디렉토리에
.html이 2개 이상이면 zip 다운로드(각 페이지 주입 후 mac zip CLI, 스크래치 /tmp/webskin-zip-<random>, 응답 후 finally에서 정리). 1개면 기존처럼 단일 HTML.
- 구매자 검증(paid) 로직은 그대로 유지.
2. 스토어 UI (public/index.html)
- 미리보기 모달에 "커스터마이즈" 토글 버튼 → 사이드패널(
#cpanel).
- iframe(같은 오리진) 로드 후
:root cssRules 순회해 색상 패턴 변수 최대 10개 + radius류(px) 1개 자동 추출.
- 컬러픽커/슬라이더 조작 시 iframe에
<style id="crowny-reskin"> upsert로 즉시 라이브 반영. "모션 끄기" 체크 시 * {animation:none;transition:none} 추가.
- "초기화" 버튼으로 원복.
- 다운로드 시
CUSTOM_TOKENS를 base64 JSON으로 인코딩해 ?tokens=로 fetch URL에 부착.
3. 한선씨 동반
한선씨/토큰검증.한선 신규: server.js sanitizeTokens와 동일 규칙(변수명/값 화이트리스트)을 코드값(코드값) 기반 문자 분류로 이중구현. 자체검증 6케이스(정상2/변수명불량/</포함/url(포함/길이초과) 전부 통과.
한선씨/서버.한선에 다운로드형식판정(html개수)("단일"/"zip"), 리스킨주입판정(토큰개수) 함수 추가 및 자체검증 갱신.
crownycode-brain.sh learn, crownycode-learn.sh add 학습 완료.
관련 파일
/Users/ef/crowny-webskin/server.js
/Users/ef/crowny-webskin/public/index.html
/Users/ef/crowny-webskin/한선씨/토큰검증.한선 + .toau
/Users/ef/crowny-webskin/한선씨/서버.한선 + .toau
검증 (실측)
launchctl kickstart -k gui/501/com.crowny.webskin → /health 200
토큰검증.한선 자체테스트: 통과=6 실패=0 / 총6
서버.한선 자체테스트: 다운로드형식 html1개→단일, html2개→zip / 리스킨주입 토큰0개→-1, 토큰3개→1
구매자(owner=cat /tmp/webskin_owner.txt) 보유 스킨 01-루나레글로우(현재 5 HTML로 확장된 멀티페이지 상태, 세션 간 동시작업으로 실제 zip 경로 실사용 검증됨):
① 토큰 없이 다운로드 → 200, application/zip, 5개 HTML 전부 crowny-reskin 미포함(회귀 정상)
② ?tokens= ({"--point":"#0066ff"}, 스킨 실제 :root 변수명 사용) → 200, zip 내 5개 HTML 전부 <style id="crowny-reskin">:root{--point:#0066ff;}</style> 확인
③ 악성 값("--x":"</style><script>alert(1)</script>", "--bg":"url(javascript:alert(1))") → 200이지만 crowny-reskin 미검출(drop 확인), alert(1) 미검출(leak 없음)
단일페이지 스킨(04-미식다이닝, --c1~--c8 명명)에 임시 구매레코드를 만들어 단일-HTML 경로도 별도 검증: 무토큰 시 text/html 단일파일(회귀), ?tokens={"--c3":"#ff3366"} 시 <style id="crowny-reskin">:root{--c3:#ff3366;}</style> 주입 확인 후 임시 레코드 삭제·정리
스토어 페이지(/) 200, 인라인 JS node --check 통과(문법 오류 없음), 서버측 /tmp/webskin-zip-* 스크래치 정리 확인(잔존 없음)
crownycode: lookup MISS 2(웹스킨 토큰 검증 화이트리스트, 리스킨 다운로드 zip) / learn 2(crownycode-brain.sh learn, crownycode-learn.sh add)잔여 이슈
--radius류 토큰이 있는 스킨으로 UI 슬라이더 실측(현재 확인한 스킨들은 색상 위주라 radius 자동추출 UI는 코드 리뷰 수준 검증에 그침. 실제 --radius:14px 같은 변수를 가진 스킨으로 브라우저 수동 확인 권장).
- 스킨 30종 전체가 아직 단일 HTML 다수 — zip 경로는
01-루나레글로우(동시 세션 작업으로 멀티페이지화)와 04-미식다이닝(임시 레코드) 2건으로 실사용 검증됨. 향후 다른 스킨도 멀티페이지로 늘면 동일 로직이 자동 적용됨.