보안 · 프라이버시 · APPI 준수 (SR-*)

03-quality/security-privacy.html · LogiNippon · TRD · 2026-06-13 · 신뢰도 라벨 확인 추정 설계

이 문서는 techspec 07-security/security-privacy.md의 보안·프라이버시 설계를 테스트 가능한 보안 요구사항(SR-*)으로 형식화한다. 멀티테넌트 격리(애플리케이션 레벨 RLS), scope→endpoint→role 인가 매트릭스, 個人情報保護法(APPI) 5대 의무(동의·목적 제한·보존·삭제·안전관리), 마스킹 함수, 암호화·키 관리, 감사 로그를 고정한다. 모든 보존/삭제 기간은 법무 서명 전 추정치이며, 법령 사실에는 확인, 미확정 규제 포맷에는 RR-LEGAL-001 법무 서명 플래그를 단다. 모든 수치는 마스터 §4 정규표에서 인용한다.

소유·참조 경계. 이 페이지는 SR-* 보안 요구사항만 소유한다. 토큰 TTL 정책(FR-AUTH-001: access 3600s · refresh 2592000s · ingest 604800s)은 functional 페이지가 소유하므로 여기서는 링크만 한다. 보존 기간 정규치(RET-DO/EVENT/GPS/REG-001)는 마스터 §4.5에서 인용하고 본 페이지의 SR-APPI-RETENTION-001에서 보존 의무로 형식화한다.
404
타 테넌트 리소스 응답(존재 비노출)
≤60s
KV revocation 전파(FR-AUTH-001)
5종
APPI 5대 의무 → SR 카드
±300s
웹훅 replay 윈도(FR-DLV-WH-001)

1. 위협 모델 (SR-THREAT-001)

자산 → 위협 → 1차 완화 → 이를 형식화하는 SR. 진입 단계 설계 의도이며 일부 한도·정책은 운영 베이스라인으로 조정한다. 시장·법령 사실(個人情報保護法상 위치=개인정보 가능성, project44형 캐리어 공유 제어)에는 확인을 붙인다.

SR-THREAT-001 위협 모델 — 자산·위협·완화 등록 P0Phase 1A

요구. 시스템은 아래 위협 모델 표의 각 자산·위협에 대해 표기된 1차 완화를 반드시 구현하고, 각 행을 그에 대응하는 SR로 추적해야 한다(MUST). 위협 모델은 분기 1회 또는 신규 외부 인터페이스 도입 시 반드시 재검토해야 한다(MUST).

자산위협(STRIDE 매핑)1차 완화관련 SR
테넌트 화물·캐리어 데이터(D1)Information Disclosure — 화주 A가 화주 B 화물 열람모든 D1 조회 tenant_id 강제(앱-레벨 RLS), 타 테넌트는 404SR-TENANT-001 · SR-AUTHZ-001
드라이버 위치·실명·番号板Information Disclosure — 다계층(화주↔元請↔下請↔孫請) 과다 공개案件 배정 기간 한정 공개, 스코프별 PII 마스킹, 受取人은 ETA만SR-PII-001 · SR-APPI-PURPOSE-001
API Key / JWTSpoofing / Elevation — 키 탈취 후 대량 추출최소 스코프, kid+revocation-list, 레이트리밋(NFR-SEC-RL-001)SR-CRYPTO-001 · SR-AUTHZ-001
화주 웹훅 엔드포인트Tampering / Replay — 가짜 이벤트·재전송HMAC-SHA256({timestamp}.{raw_body}) + replay ±300sFR-DLV-WH-001(참조)
화물 ID 공간(SHP-…)Information Disclosure — 순차 ID 열거불투명 ID + 테넌트 스코프 + 404 통일 + 레이트리밋SR-TENANT-001
전송·저장 채널Information Disclosure — 중간자·스토리지 접근TLS 종단(엣지), 시크릿은 wrangler secret/KV, PII 마스킹·보관 분리SR-CRYPTO-001
console 브라우저 토큰Tampering — XSS로 localStorage 토큰 탈취httpOnly 쿠키 하드닝 + sunset 일정SR-TOKEN-STORE-001
드라이버 동의 상태Repudiation / Compliance — 동의 없는 수집driver.consent_at 없으면 ingest 거부(하드게이트)SR-APPI-CONSENT-001

수용기준

  • Given 위협 모델 표의 임의 행, When 해당 완화가 코드/테스트에 부재, Then 릴리스 게이트는 그 행의 관련 SR을 미충족으로 표시한다(추적성 매트릭스 연동 — acceptance-traceability).
  • Given 신규 외부 인터페이스(예: EDI·텔레매틱스 수신), When 위협 모델 재검토 없이 머지 시도, Then PR 체크가 차단한다(pipeline-ops).
근거 techspec 07-security §위협 모델 · Research/delivery-layer 확인 · 구현 완화별 상이(아래 SR 참조) · 검증 Analysis + 회귀셋

2. 인가 매트릭스 (SR-AUTHZ-001)

인가는 두 축이다 — 사람 role(JWT role 클레임)과 머신 scope(API Key/토큰 scopes). scope는 endpoint와 1:1로 매핑되고, role은 기본 scope 집합을 부여한다. 아래 매트릭스는 server/src/lib/auth.tsROLE_SCOPESserver/src/types/index.tsScope·Role 유니온을 as-built 그대로 형식화한 것이다.

SR-AUTHZ-001 scope→endpoint→role 권한 매트릭스 P0Phase 1T

요구. 모든 보호 엔드포인트는 자신이 요구하는 scope를 requireScope(ctx, …)로, role 제약은 requireRole(ctx, …)반드시 강제해야 한다(MUST). 스코프 누락은 403 PERMISSION_DENIED로 응답해야 한다(MUST). API Key는 반드시 최소 scope만 부여해야 한다(MUST, least-privilege).

role × scope 매트릭스 (✓=기본 부여, —=미부여; auth.ts ROLE_SCOPES as-built)

role \ scopeshipments:
read
shipments:
write
positions:
read
positions:
write
vehicles:
read
geofences:
read
webhooks:
*
analytics:
read
reports:
read
SHIPPER_ADMIN✓ r/w
SHIPPER_VIEWER
CARRIER_OPS
DRIVER(*)
SUPPORT(내부)
CONSIGNEE(受取人, Phase 1 미존재)tracking-only

(*) DRIVER는 read 라우트의 positions:read(경로 replay)를 받지 않는다. 백그라운드 GPS 업로드용 positions:write는 일반 role-scope가 아니라, issueIngestToken()강제로 ['positions:write'] 실은 ingest 전용 토큰(FR-AUTH-001 · ingest TTL 604800s)을 통해서만 부여된다. CONSIGNEE role은 아직 코드에 없으며(Role 유니온은 5종), 受取人 가시성은 role이 아닌 tracking 응답의 position 생략으로 구현한다(SR-PII-001).

수용기준

  • Given SHIPPER_VIEWER JWT, When POST /v1/shipments(shipments:write 필요) 호출, Then 403 PERMISSION_DENIED(missing scope: shipments:write).
  • Given ingest 토큰(scopes=['positions:write']), When GET /v1/shipments/:id/positions(positions:read 필요) 호출, Then 403. When POST /v1/ingest/positions 호출, Then 정상 처리.
  • Given role='constructor' 등 프로토타입체인 오염 시도, When 토큰 검증, Then VALID_ROLES Set 멤버십으로 거부(UNAUTHENTICATED) — in 연산자 구멍 차단.
근거 techspec 07-security §인가 · 구현 server/src/lib/auth.ts(ROLE_SCOPES·requireScope·requireRole) DONE · 검증 auth 테스트(role/scope 매트릭스)

3. 테넌트 격리 (SR-TENANT-001)

SR-TENANT-001 멀티테넌트 격리 — 애플리케이션 레벨 RLS P0Phase 1T

요구. 모든 D1 읽기/쓰기는 인증 컨텍스트의 tenant_id를 WHERE 절에 반드시 바인딩해야 하며, 이 tenant_id토큰/키 클레임에서만 주입되고 클라이언트 입력으로는 절대 바인딩되지 않아야 한다(MUST). 모든 데이터 접근은 단일 리포지토리 레이어(server/src/lib/db.ts)를 통과시켜, tenant_id 바인딩 없는 쿼리가 코드상 작성 불가능하도록 한다(앱-레벨 RLS). 타 테넌트 리소스 요청은 403이 아니라 404로 응답하여 ID 존재 여부조차 노출하지 않아야 한다(MUST). DRIVER role 컨텍스트는 자신의 driver_id(=ctx.sub)로 한 번 더 스코프하여 배정된 案件만 보이게 해야 한다(MUST).

수용기준

  • Given 테넌트 A 토큰, When 테넌트 B 소유 shipment_idGET /v1/shipments/:id, Then 0행 → 404 SHIPMENT_NOT_FOUND(존재 비노출). 응답·로그에 테넌트 B 식별자 미노출.
  • Given ingest 배치에 타 테넌트/미배정 shipment_id 포함, When POST /v1/ingest/positions, Then 접근 가능한 핑만 수용(repo.getShipment 스코프 필터), 전부 불가 시 403 PERMISSION_DENIED.
  • Given 리포지토리 레이어 우회 쿼리 PR, When CI, Then 회귀셋이 차단(pipeline-ops).
근거 DM-ISO-001(데이터 격리 규칙) · techspec 07-security §테넌트 격리 · Research/delivery-layer row-level 격리 확인 · 구현 server/src/lib/db.ts SECURITY INVARIANT(앱-레벨 RLS), ingest 경로 스코프 필터 DONE · 검증 tenant-scoped repo 테스트(404 격리)

4. PII 마스킹 함수 (SR-PII-001)

스코프별로 개인정보 노출을 최소화한다(data minimization). 마스킹은 뷰 시점에서 결정론적 순수 함수로 적용한다 — 원시값은 운송사(고용 관계) 뷰에서만 노출. 대상 필드는 server/src/types/index.tsdriver.display_name(// masked in shipper scope (APPI)vehicle.plate(// 番号板 — masked in shipper scope (APPI))이다.

SR-PII-001 스코프별 PII 마스킹 함수 P0Phase 1T

요구. 화주(SHIPPER_*) 및 受取人(CONSIGNEE) 스코프로 직렬화되는 모든 응답은 아래 마스킹 규칙을 반드시 적용해야 한다(MUST). 운송사(CARRIER_OPS, 자사) 스코프는 자사 드라이버 실명·번호판을 원시 노출할 수 있다(MAY, 고용 관계); 타 캐리어 데이터는 노출 불가(MUST NOT).

마스킹 규칙 (함수형 정의)

필드함수화주 뷰 출력 예운송사(자사) 뷰受取人 뷰
driver.display_namemaskName(s) = head(s,1) + "**" (姓 1자 유지, 잔여 별표화)佐**원시 실명미노출
vehicle.platemaskPlate(p) = region + " " + cls[0] + "*-**" (지역명+분류 1자리 유지)品川 1*-**원시 番号板미노출
position(실시간 좌표)maskPosition(scope) = (scope==CONSIGNEE) ? omit : pass노출(ETA+위치)노출생략(omit) — ETA만
受取人 위치 비노출은 구조적으로 강제. tracking read model(TrackingSnapshotDto)의 position?는 옵셔널이며, 주석 // `position` is omitted for consignee scope (APPI masking) 그대로 受取人 스코프에서 키 자체를 제거한다(null 전송이 아님). display_name·plate 마스킹은 현재 server/src/routes/shipments.ts// TODO (Phase 1 late): … scope-based masking of PII (APPI)미구현(STUB) — 본 SR이 그 TODO를 정규 수용기준으로 승격한다.

수용기준

  • Given 화주 토큰, When 드라이버 display_name="佐藤太郎"·plate="品川 12-34" 화물 조회, Then 응답은 佐**·品川 1*-**만 포함하고 원시값을 절대 포함하지 않는다(스냅샷 테스트).
  • Given 受取人 스코프 tracking 조회, When 응답 직렬화, Then position 키 부재 AND next_stop.eta 존재.
  • Given 마스킹 함수, When 동일 입력 반복, Then 동일 출력(결정론적, 가역 정보 누출 없음).
근거 techspec 07-security §마스킹 · model.md(display_name·plate 마스킹 고려) · Research/delivery-layer 화주 스코프 캐리어 PII 마스킹 확인 · 구현 受取人 position 생략(타입 차원) PARTIAL · 이름/번호판 마스킹 STUB(shipments.ts TODO) · 검증 마스킹 스냅샷 테스트(요구)

5. 個人情報保護法(APPI) 5대 의무

드라이버 위치는 차량 위치·番号板·실명과 결합 가능하므로 個人情報保護法상 개인정보로 다룬다 확인(Research/delivery-layer). 아래 5개 요구사항 카드가 수집 목적 명시·동의, 이용 제한, 보존, 삭제권, 안전관리(암호화는 §6)를 형식화한다. 모든 보존/삭제 기간은 법무 서명 전 추정이며, 미확정 항목은 RR-LEGAL-001 법무 서명 플래그를 단다.

SR-APPI-PURPOSE-001 이용 목적 제한 P1Phase 1I

요구. 수집한 위치는 명시한 목적(가시성·荷待ち 측정·ETA·규제 산출물) 내에서만 이용해야 한다(MUST). 목적 외(무관한 마케팅·개별 드라이버 감시)로 전용하지 않아야 한다(MUST NOT). 위치 공유는 案件이 실린 기간 동안만 ON("항상 추적"이 아니라 "업무 중 추적")이며, 화주↔캐리어 공유 범위는 계약서와 시스템 권한이 일치해야 한다(MUST). 案件 종료 시 공유는 자동 OFF되어야 한다(MUST).

수용기준

  • Given 案件이 종료된 shipment, When 화주가 해당 캐리어 차량 실시간 위치 조회, Then 위치 미노출(공유 OFF).
  • Given 신규 데이터 사용처 추가 PR, When 리뷰, Then 목적 적합성 점검(Inspection) 체크리스트 통과 필수.
근거 techspec 07-security §이용 제한 확인 · SR-AUTHZ-001(案件 기간 공유) · 구현 공유 토글 세분화는 Phase 2 설계 · 검증 Inspection + 계약 점검
SR-APPI-RETENTION-001 보존 기간 정책 (RET-* 표) P0Phase 1T

요구. 데이터는 수명별로 저장 위치·보존 기간을 분리하고(핫/이벤트/원시/규제), 보존 기간 경과분은 자동 만료해야 한다(MUST). 무기한 보존하지 않으며, 목적이 끝난 데이터는 아카이브/삭제 경로를 둔다(MUST). 아래 보존표의 수치는 마스터 §4.5 정규치이며 전부 법무 서명 플래그가 필요하다.

보존표 (마스터 §4.5 인용, 기간은 추정 — 법무 서명 전)

ID데이터저장 위치보존 기간(정규치)법무 서명
RET-DO-001DO 라이브 상태(핫)Durable Object운행 + hibernation 후 1h추정 플래그
RET-EVENT-001D1 의미 이벤트(arrival/exit/dwell)D1 event·stop5년(분쟁/규제)추정 플래그
RET-GPS-001R2 원시 GPS 아카이브(고빈도)R2 GPS_ARCHIVE365일 후 purge(ML 윈도)추정 플래그
RET-REG-001R2 규제 산출물(PDF/CSV)R2 + D1 메타7년(법정 최소)추정 플래그 RR-LEGAL-001

수용기준

  • Given 원시 GPS 아카이브 객체 age > 365일, When R2 라이프사이클/Cron 배치 실행, Then 해당 객체 purge(테스트: age 경계 ±1일).
  • Given 보존표의 임의 기간, When 법무 서명 미완, Then 문서·릴리스 게이트에 추정 플래그 표기 유지(서명 시 확인으로 승격).
근거 마스터 §4.5(RET-DO/EVENT/GPS/REG-001) · techspec 07-security §보관 기간 · cloudflare-stack(R2 라이프사이클) · 구현 Cron purge 설계(cron.ts 빈 구현 보완 필요) · 검증 age 경계 테스트(요구)
SR-APPI-DELETE-001 삭제권 SLA + R2 주체 삭제 메커니즘 P1Phase 2T

요구. 데이터 주체(드라이버)의 삭제 요청에 대응하는 경로를 두고, 개인 식별 필드를 삭제/익명화하고 R2 원시 GPS 아카이브에서 해당 주체분을 제거하는 절차를 제공해야 한다(MUST). 삭제 요청 접수 후 처리 SLA를 명시해야 한다(초기 확정치 30일 이내 처리, 운영 베이스라인으로 조정 — 설계). 단, 법정 보존 의무가 있는 규제 산출물(RET-REG-001 7년)은 삭제권보다 보존 의무가 우선할 수 있으며(MUST), 충돌 시 "개인 식별과 사실 기록 분리"로 양립시킨다.

R2 주체 삭제 메커니즘. 원시 GPS 객체 키에 driver_id(또는 해시) 접두 파티션을 부여해, 주체 삭제 요청 시 prefix 리스트→batch delete로 O(주체 객체 수)에 제거하는 설계. D1은 식별 필드 익명화(display_name=NULL 등). 절차 상세는 법무·운영 확정 — 추정.

수용기준

  • Given 드라이버 삭제 요청, When 삭제 잡 실행, Then 해당 driver_id 원시 GPS R2 객체 0개 잔존 AND D1 식별 필드 익명화 AND 규제 산출물은 보존(분리 검증).
  • Given 삭제 요청 접수, When 30일 경과, Then 미처리 건은 알람(SLA 위반).
근거 techspec 07-security §데이터 주체 권리 · 구현 Phase 2 설계 · 검증 삭제 잡 통합 테스트(요구) · 법무 서명 플래그 SLA·절차 미확정 RR-LEGAL-001

6. 암호화 · 시크릿 · 키 관리 (SR-CRYPTO-001)

SR-CRYPTO-001 전송 암호화 · 시크릿 · 키 로테이션·revocation P0Phase 1T

요구. 모든 클라이언트↔엣지·엣지↔외부(웹훅·지도/JMA API) 통신은 TLS(HTTPS)를 반드시 사용해야 한다(MUST). 시크릿(JWT_SIGNING_KEY·WEBHOOK_SIGNING_KEY·MAPS_API_KEY·JMA_API_KEY·LINE_CHANNEL_TOKEN)은 반드시 wrangler secret(환경별)으로 주입하고 코드/레포에 하드코딩하지 않아야 한다(MUST NOT). JWT 서명 키는 키 ID(kid) 기반 다중 키로 운용하여 무중단 교체가 가능해야 하며(MUST), 폐기 키·revocation 목록을 AUTH_KEYS KV로 전파하여 ≤60s 내 무효화(FR-AUTH-001)해야 한다(MUST).

현재 갭(설계 vs 코드). techspec는 kid 다중 키 + KV revocation을 기술하나, server/src/lib/auth.ts의 실제 구현은 단일 정적 키 JWT_SIGNING_KEY · HS256 · 헤더 kid 없음이다(jwtSign(payload, key, 'HS256') / jwtVerify(token, signingKey(env), 'HS256')). KV 바인딩 AUTH_KEYS는 이미 존재(// API-key meta, JWT key state, revocation list)하나 JWT 검증 경로가 그것을 읽지 않는다. 본 SR은 (1) 토큰 헤더에 kid 부여, (2) AUTH_KEYS KV의 키-상태/revocation-list를 검증 시 강제 조회를 정규 요구로 고정한다. API Key 자체는 api_key.revoked 컬럼으로 즉시 폐기 가능(WHERE … revoked = 0) — DONE.

수용기준

  • Given 발급된 JWT, When 토큰 헤더 검사, Then kid 클레임 존재 AND 검증기가 kidAUTH_KEYS에서 해당 키 상태(active/retired/revoked) 조회.
  • Given kid가 revocation-list에 등재, When 그 키로 서명된 토큰 제시, Then 401 UNAUTHENTICATED ≤60s 내(KV 전파).
  • Given 시크릿, When 레포 스캔, Then 평문 시크릿 0건(CI secret-scan).
  • Given API Key revoked=1, When 해당 키로 호출, Then 401(resolveApiKey revoked=0 필터) — 이미 DONE.
근거 techspec 07-security §암호화·시크릿·키 관리 · FR-AUTH-001(KV revocation ≤60s) · 구현 TLS(엣지) DONE · 시크릿 wrangler secret DONE · API Key 폐기 DONE · JWT kid+KV revocation ABSENT(단일 정적 키) · 검증 kid/revocation 테스트(요구)

7. 토큰 저장 하드닝 (SR-TOKEN-STORE-001)

SR-TOKEN-STORE-001 console 토큰 저장 — httpOnly 쿠키 하드닝 + sunset P1Phase 1T

요구. 브라우저 컨트롤 타워(console)의 인증 토큰은 XSS로 탈취 가능한 localStorage가 아니라 httpOnly·Secure·SameSite 쿠키로 저장해야 한다(권장→MUST). 전환에는 sunset 일정(localStorage 경로 폐기 기한)을 명시하고, 그 기한까지는 짧은 액세스 TTL과 회전으로 노출 창을 최소화해야 한다(MUST). 결정 추적은 OD-003(토큰 저장 하드닝)이 소유한다.

현재 갭. console/src/lib/auth.ts는 토큰을 localStorage에 둔다(주석: // MVP: 토큰을 localStorage 에 둔다(웹). 후속에 httpOnly 쿠키+짧은 수명/회전으로 강화(07 보안).). XSS 1건이 토큰 전체 탈취로 직결되는 노출면. drift 기간 동안 CSP·입력 sanitize로 XSS 표면을 줄이되, 근본 해소는 httpOnly 쿠키 전환.

수용기준

  • Given httpOnly 쿠키 전환 완료, When 브라우저 콘솔에서 localStorage 조회, Then 인증 토큰 부재 AND 쿠키는 HttpOnly; Secure; SameSite 속성 보유.
  • Given sunset 기한, When 기한 경과, Then localStorage 토큰 경로 코드 제거(회귀셋).
근거 techspec 07-security §클라이언트 토큰 · 구현 console/src/lib/auth.ts localStorage PARTIAL(하드닝 미적용) · 결정 OD-003 · 검증 쿠키 속성·sunset 테스트(요구)

8. 감사 로그 (SR-AUDIT-001)

SR-AUDIT-001 감사 로그 스키마 — 누가·무엇·언제·request_id P1Phase 1T

요구. 보안 관련 사건(인증 실패·권한 거부·키 발급/폐기·웹훅 비활성·규제 산출물 다운로드·데이터 주체 삭제)은 반드시 감사 로그로 남겨야 한다(MUST). 각 레코드는 누가(actor=ctx.sub/tenant_id)·무엇(action·resource)·언제(occurred_at, ISO 8601 +09:00 — DM-TS-001request_id·outcome(allow/deny)를 포함해야 한다(MUST). 로그에는 개인정보를 최소화하여(위치 원좌표·실명 대신 식별자) request_id로 상관해야 한다(MUST).

감사 레코드 스키마

필드의미원천
request_id요청 상관 키(UUID)requestIdMiddleware(X-Request-Id 헤더/crypto.randomUUID())
actor누가(ctx.sub · apikey:<key_id>)auth 컨텍스트
tenant_id어느 테넌트토큰/키 클레임
action · resource무엇을(예 report.download · SHP-…)라우트
occurred_at언제(ISO 8601 +09:00)DM-TS-001
outcomeallow / deny / error_code핸들러/에러맵

수용기준

  • Given 권한 거부(403) 발생, When 요청 처리, Then 감사 레코드 1건 생성(action·actor·request_id·outcome=deny) AND 위치 원좌표·실명 미포함.
  • Given 규제 산출물 다운로드, When 응답, Then 감사 레코드의 request_id가 API 응답 X-Request-Id 및 에러 엔벨로프 request_id와 동일(상관 가능).
근거 techspec 07-security §감사·로깅 · observability-slo(request_id 상관) · 구현 request_id 미들웨어·에러 엔벨로프 DONE · 전용 감사 로그 스토어 ABSENT(요구) · 검증 감사 레코드 생성 테스트(요구)

아래는 functional 페이지 소유 ID로, 본 페이지 보안 모델이 의존하나 여기서 재정의하지 않는다.

근거·상호참조

  • techspec: 07-security/security-privacy.md (위협 모델·인증·인가·APPI·암호화·키 관리·감사) — 본 페이지의 1차 근거
  • techspec: 05-delivery-layer/delivery.md (스코프·엔드포인트·404 격리·웹훅 HMAC·受取人 ETA-only)
  • techspec: 02-data-model/model.md (display_name·plate 마스킹 필드·carrier.parent_carrier_id)
  • techspec: 01-architecture/cloudflare-stack.md (KV·Secrets·R2 라이프사이클)
  • Research: Research/delivery-layer (멀티테넌시·캐리어 데이터 공유·個人情報保護法) 확인
  • code: server/src/lib/auth.ts (ROLE_SCOPES·requireScope·requireRole·consentGate·issueIngestToken·HS256 단일키)
  • code: server/src/lib/db.ts (앱-레벨 RLS SECURITY INVARIANT·tenant 스코프·404 비노출)
  • code: server/src/ingest/positions.ts (인제스트 핫패스·tenant 스코프 필터·consentGate 미배선)
  • code: server/src/routes/shipments.ts (PII 마스킹 TODO·受取人 position 생략)
  • code: server/src/types/index.ts (Scope·Role 유니온·AUTH_KEYS KV·시크릿·TrackingSnapshotDto·request_id)
  • code: server/src/middleware/error.ts (requestIdMiddleware·에러 엔벨로프 request_id)
  • code: console/src/lib/auth.ts (localStorage 토큰 — 하드닝 대상)
  • TRD 내부: FR-AUTH-001 · DM-ISO-001 · DM-TS-001 · NFR-SEC-RL-001 · RR-LEGAL-001 · OD-003 · acceptance-traceability