Commit Graph

5 Commits

Author SHA1 Message Date
ttipo 7734d5de7f chore(backend): 미뤄 둔 의존성 넷을 배포 2주 지난 판으로 올린다
security.md 유예 표에서 11-05 를 기다리던 항목 — body-parser 2.4.0(lock 만, qs 6.16.1 ·
iconv-lite 0.7.3 이 따라옴) · multer override ^2.5.0(platform-express 12.3.0 이 2.5.0 을
선언한다 — 11.x 최신도 2.4.0 고정이라 override 를 남긴다) · sanitize-html 2.18.1 · fast-uri 3.1.9.
모두 도달 경로가 없던 권고라 2주 규칙대로 골랐다(--before 로 그보다 새 판은 받지 않았다).
audit 29 → 23(운영 11 → 5, high 3 → 0). js-yaml 은 swagger 11.4.8(11-04)이 2주가 안 돼 그대로 둔다.

npm 11.6.2 컨테이너에서 바꿨다. 임시 복사본에서 npm ci, 인자 없는 npm install 이 lock 을 바이트
그대로 두는지, npm ci --omit=dev 뒤 require, lint · type-check · 단위 3,203 · e2e 266 · build 통과.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-05 12:39:38 +09:00
ttipo 6735bba43d chore(backend): npm 11.6.2 에서 lock 이 깨지지 않게 unrs-resolver 를 1.11.1 로
컨테이너에서 아무 패키지나 npm install 하면 lock 에서 @emnapi/core·@emnapi/runtime 이 빠지고 npm ci 가 실패했다.
원인은 npm 11.6.1~11.6.2 의 버그다 — npm install 이 의존 표시를 다시 계산하면서, 들어오는 peer 간선이 있는 패키지를
일반 의존성이어도 peer 로 남기고 peer 이면서 optional 인 패키지를 지운다. jest 가 쓰는 unrs-resolver 1.12.x 의 wasm32 바인딩이
그 모양이다(@napi-rs/wasm-runtime 1.x 가 emnapi 를 필수 peer 로 둔다).

- unrs-resolver 와 바인딩을 1.11.1(2025-07), @napi-rs/wasm-runtime 을 0.2.12 로 내렸다 — emnapi 를 일반 의존성으로 쓴다.
  모두 개발 전용이고 package.json·운영 의존성·npm audit 결과는 그대로다
- 깨끗한 리눅스 컨테이너(npm 11.6.2)에서 확인: npm ci, 인자 없는 npm install 뒤 lock 바이트 동일,
  다른 패키지를 설치한 뒤 npm ci, npm ci --omit=dev, 운영 이미지 빌드
- npm 을 11.6.3 이상으로 올리면 원인이 사라진다 (docker 스킬 "의존성은 컨테이너 안에서")

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-27 13:00:09 +09:00
ttipo 70103e4968 feat(backend): 메일 서식 본문 발송과 정화 전 비용 검사
메일 쓰기에 서식 편집기가 들어오면서 서버가 HTML 본문을 받아 보낸다. 편집기의 출력은 믿지 않고 서버가 다시 정화한다.

- 서식 본문(bodyHtml)은 보내는 메일 전용 허용 목록으로 다시 정화해 텍스트 대체 본문과 multipart/alternative 로 보낸다
  (style 은 속성마다 값 형식을 처음부터 끝까지 맞추고, 링크는 http·https·mailto 만). 정화해서 비면 텍스트 메일로 보낸다
- 받은 메일 정화가 편집기 서식을 모두 남기도록 넓혔다 — 보낸편지함·받는 사람 화면에서 서식이 사라지지 않게
- 발송 POST /api/messages 만 JSON 4MB (서식 본문·긴 인용). 중복 방지 지문에 정화한 서식 본문을 넣는다
- htmlparser2 는 중첩 깊이의 제곱으로 느려져, 깊게 중첩한 2MB 메일 한 통에 3분 가까이 서버가 멈췄다.
  정화 전에 선형 시간으로 파서 스택 비용을 재고, 넘으면 받은 메일은 htmlBody 없이(텍스트로), 서식 본문은 VAL_001
- 정화한 서식 본문이 2,000,000바이트(본문 읽기 한도)를 넘으면 VAL_001 — 다시 읽을 때 표시 없이 잘리지 않게
- 받은 메일 정화의 다른 제곱 시간 경로 2개(style 의 url() 정규식, 주소 없는 img 를 지우는 필터)도 고쳤다
- htmlparser2 를 직접 의존성으로 선언한다 — sanitize-html 이 쓰는 12.0.0 과 같은 판이라 lock 은 루트 한 줄만 바뀐다

계약: docs/mail/api-contract.md 2절 "서식 본문"·"읽기 한도"

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-27 07:49:39 +09:00
ttipo 71f2e01d83 feat(backend): 직원 인증(쿠키 JWT·DB 세션)과 웹메일 API(Stalwart JMAP 중계)
첫 사내 모듈인 웹메일의 백엔드다. 메일 엔진은 외부(Stalwart)에 두고 백엔드가 JMAP 으로 중계한다.

- 직원 로그인 = 메일 계정: 엔진으로 비밀번호를 확인한 뒤 HttpOnly 쿠키 JWT 를 발급한다.
  리프레시 세션은 DB 에 해시로 저장하고 1회용으로 회전한다 (이미 쓴 토큰이 오면 세션 폐기)
- 로그인 실패 한도는 엔진에 묻기 전에 원자적으로 차지하고, IPv6 는 /64 로 묶어 센다
- 상태를 바꾸는 요청은 출처(Sec-Fetch-Site·Origin)를 검사하고, 클라이언트 IP 는 TRUST_PROXY 로만 해석한다
- 메일함·목록·읽기·발송·첨부 API: 받은 HTML 은 정화해서 내보내고, 발송은 Idempotency-Key 로 중복을 막는다
- 메일 엔진 관리자 계정은 로그인·대리 접근 대상에서 막는다 (게이트웨이가 관리자까지 대리할 수 있다)
- 에러 코드 AUTH_004·MAIL_001~006 을 추가하고 클라이언트 에러 사전을 다시 생성했다

계약: docs/mail/api-contract.md

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-27 01:31:38 +09:00
ttipo 18f10cece4 Initial commit 2026-10-26 12:28:48 +09:00