docs(rules): 서식 본문 정화·정화 전 비용 검사·숫자 env 표기 규칙

- security: 사용자가 쓴 HTML 은 서버가 허용 목록으로 다시 정화한다, 밖에서 온 HTML 은 정화 전에 비용부터 잰다,
  브라우저에서 외부 HTML 을 고칠 때는 떨어진 문서 안에서 끝낸다
- env-structure: 숫자 env 는 앞자리 0 없는 10진수, nginx 도 읽는 크기 값은 6자리 이하
- nestjs·vue3·docker 스킬: 골격 표(정화·사전 검사·편집기·값 규칙 짝 파일)와 체크리스트, 발송 경로 4MB,
  컨테이너의 npm install 이 lock 을 깨뜨린 사례(2026-10-27)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
2026-10-27 07:49:41 +09:00
parent d807cb4441
commit 13888af2c2
5 changed files with 35 additions and 4 deletions
+5
View File
@@ -37,6 +37,11 @@ description: 환경변수 단일 진실 공급원(SSOT) 구조 — 루트 .env.{
- **compose 는 정의되지 않은 변수를 빈 문자열로 주입한다.** 코드에서 `??` 기본값만 쓰면 `Number('')===0` 같은 함정에 빠진다 —
숫자 설정값은 `shared/utils/env.ts` 의 `parsePositiveInt()` 처럼 빈 문자열까지 방어하는 파서를 거친다.
(기존 `.env` 를 쓰는 사람이 새 키를 추가하지 않아도 안전해야 한다)
- **숫자 값은 앞자리 0 없는 10진수로만 쓴다** — `08`·`+5`·`5.0`·`1e3`·`0x10` 은 env 검증이 기동을 막는다
(기간 `015m`, `TRUST_PROXY` 홉 수·CIDR 접두도 같다. 규칙은 `shared/utils/env.ts` 의 `isPositiveIntString()`).
같은 값을 읽는 nginx 템플릿(`frontend/nginx-env.envsh`)이 셸 산술 때문에 `08` 을 거부하므로 백엔드도 같은 규칙을 쓴다 (2026-10-27).
nginx 본문 한도가 되는 `MAIL_ATTACHMENT_MAX_SIZE_MB`·`UPLOAD_MAX_SIZE_MB` 는 템플릿과 같이 **6자리(999999) 이하**만 받는다
(백엔드만 뜨고 frontend 가 뜨지 않는 조합을 막는다 — `env.validation.ts` 의 `NGINX_SHARED_INT_KEYS`).
- `docker-compose.yml` 의 `env_file` 지시자 때문에 `backend/.env.{APP_ENV}` 파일은 비어 있어도 **존재해야 한다.**
- **프론트엔드 dev 서버는 compose 를 거치지 않는다** — 호스트에서 직접 띄우므로([[docker]])
`vite.config.ts` 가 루트 `.env.{mode}` 를 `loadEnv` 로 읽어 포트·프록시 대상을 정한다.
+6
View File
@@ -33,6 +33,12 @@
- **서버 서명 키(`JWT_SECRET` 등)나 거기서 파생한 키를 쓰지 않는다** — 평문을 아는 받은 사람이 (평문, HMAC) 쌍으로
서명 키 후보를 오프라인에서 대입해 볼 수 있고, 맞히면 토큰을 위조할 수 있다 (2026-10-26 리뷰)
- 에러 응답에 스택·쿼리·내부 호스트명을 싣지 않는다 — 미정의 예외는 항상 `SYS_001` 로 수렴시킨다 ([[error-codes]])
- 사용자가 쓴 HTML(메일 서식 본문)은 **서버가 허용 목록으로 다시 정화**한 뒤 내보낸다 — 클라이언트 편집기의 출력을 믿지 않는다.
style 은 속성마다 값 형식을 처음부터 끝까지(`^…$`) 맞춘다 (`modules/mail/html/sanitize-outgoing-html.ts`, 계약 2절 "서식 본문")
- 받은 메일·서식 본문처럼 **밖에서 온 HTML 은 정화하기 전에 비용부터 잰다** — 정화기(htmlparser2)는 중첩 깊이의 제곱으로 느려져
메일 한 통으로 서버 전체를 수 분 멈출 수 있다 (2026-10-27 리뷰). 넘으면 정화하지 않는다 (`modules/mail/html/html-parse-budget.ts`, [[nestjs]])
- 브라우저에서 외부 HTML 을 고쳐 쓸 때는 떨어진 문서(`<template>`·`DOMParser`) 안에서 끝낸다 — 화면 문서로 옮기면
`img onerror` 같은 처리기가 실행된다. CSP 가 막아도 그것에만 기대지 않는다 ([[vue3]] 체크리스트 "외부 HTML")
### 파일 업로드
템플릿은 `modules/upload` 에 로컬 디스크 기준 골격을 제공한다. 새 업로드 엔드포인트는 이 패턴을 복제한다.