@aldegad/safedeps 2.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,595 @@
1
+ # Safedeps Architecture (v2)
2
+
3
+ > **Note on naming**: 이 repo 는 v1 시절 `npm-reorg-guard` 로 출시됐다. v2 부터 ecosystem 통합 + advisory-first 게이트를 도입하면서 제품/CLI 이름을 **`safedeps`** 로 rename 한다. 내부 post-install rollback engine 은 v1 의 `reorg-guard` 컨셉을 그대로 계승. 이 문서는 v2 의 SSoT.
4
+
5
+ 기능 분리:
6
+ - **README.md** — 사용자 설치/사용 가이드 (rename 후 갱신 예정)
7
+ - **SKILL.md** — 스킬 메타 + hook 선언 (Claude/Codex skill loader 가 읽는 SSoT)
8
+ - **ARCHITECTURE.md** — 내부 흐름·설계 (이 문서)
9
+
10
+ ---
11
+
12
+ ## 0. 핵심 한 문장 (코덱시 합의)
13
+
14
+ > *Safedeps does not decide at install time from multiple live truths.*
15
+ > *It approves one dependency spec from provider evidence first, then the hook only enforces that approved spec; reorg remains the rollback layer if the install result diverges.*
16
+
17
+ 번역: Safedeps 는 install 순간에 여러 라이브 truth 를 동시에 조회해서 결정하지 않는다. **사전에 provider evidence 로 안전한 dependency spec 을 먼저 승인** (approve) 하고, **hook 은 그 승인된 spec 과 명령이 일치할 때만 통과**시킨다. **reorg 는 install 결과가 spec 과 어긋날 때의 rollback layer** 로 남는다.
18
+
19
+ ---
20
+
21
+ ## 1. The Big Picture — 3-Phase Defense
22
+
23
+ ```
24
+ ┌─────────────────────────────────────────────────────────────────────┐
25
+ │ │
26
+ │ Phase 1: ADVISORY GATE Phase 2: HOOK ENFORCEMENT │
27
+ │ ──────────────────── ──────────────────── │
28
+ │ │
29
+ │ 사용자 의도 │ │
30
+ │ (intent: 이 패키지 설치 │ │
31
+ │ 하고 싶다) │ │
32
+ │ │ │ │
33
+ │ ▼ │ │
34
+ │ ┌─────────────┐ OSV.dev │ │
35
+ │ │ safedeps │◄────primary─────┤ │
36
+ │ │ check │ CISA KEV │ │
37
+ │ │ │◄──hard-risk────┤ │
38
+ │ │ │ GHSA │ │
39
+ │ │ │◄──enrichment───┤ │
40
+ │ └──────┬──────┘ │ │
41
+ │ │ │
42
+ │ ▼ │
43
+ │ ┌──────────────────────┐ │
44
+ │ │ approved spec ledger │ │
45
+ │ │ .safedeps/ │ │
46
+ │ │ approved-specs/ │ │
47
+ │ │ <hash>.json │ │
48
+ │ │ • ecosystem │ │
49
+ │ │ • package@version │ │
50
+ │ │ • approved_at │ │
51
+ │ │ • expires_at │ │
52
+ │ │ • evidence sources │ │
53
+ │ └──────┬───────────────┘ │
54
+ │ │ │
55
+ │ ▼ │
56
+ │ ┌─────────────────┐ │
57
+ │ │ 사용자 / Claude │ │
58
+ │ │ install 명령 발행 │ │
59
+ │ │ (npm install ..) │ │
60
+ │ └────────┬────────┘ │
61
+ │ │ │
62
+ │ ▼ │
63
+ │ ╔═════════════════════════╗ │
64
+ │ ║ PreToolUse HOOK ║ │
65
+ │ ║ safedeps-pre-guard.sh ║ │
66
+ │ ╚════════╤════════════════╝ │
67
+ │ │ │
68
+ │ ledger 조회: │
69
+ │ "이 명령의 ecosystem + │
70
+ │ pkg@version 이 approved │
71
+ │ spec 과 일치?" │
72
+ │ │ │
73
+ │ ┌────────┴────────┐ │
74
+ │ ▼ ▼ │
75
+ │ match O match X │
76
+ │ │ │ │
77
+ │ ▼ ▼ │
78
+ │ 명령 실행 BLOCK + 안내 │
79
+ │ │ ("safedeps │
80
+ │ │ check ... 먼저 해") │
81
+ │ ▼ │
82
+ │ install 실행 │
83
+ │ │ │
84
+ │ ▼ │
85
+ │ ╔══════════════════════════════╗ │
86
+ │ ║ PostToolUse HOOK ║ │
87
+ │ ║ safedeps-post-verify.sh ║ │
88
+ │ ╚════════╤═════════════════════╝ │
89
+ │ │ │
90
+ │ lockfile diff vs │
91
+ │ approved spec 비교 │
92
+ │ │ │
93
+ │ ┌────────┴────────┐ │
94
+ │ ▼ ▼ │
95
+ │ spec 과 동일 diverged │
96
+ │ │ │ │
97
+ │ ▼ ▼ │
98
+ │ CONFIRM REORG │
99
+ │ (clean baseline) (rollback to │
100
+ │ last confirmed) │
101
+ │ │
102
+ │ Phase 3: POST-INSTALL SAFETY NET (v1 의 reorg engine 계승) │
103
+ │ ─────────────────────────────────────────────── │
104
+ │ │
105
+ └─────────────────────────────────────────────────────────────────────┘
106
+ ```
107
+
108
+ 핵심 통찰:
109
+ - **Phase 1 = pre-install advisory gate** (새로 추가). vuln DB 조회 → 안전 spec 결정. 사용자/Claude 가 install 명령 *작성하기 전에* 끝남.
110
+ - **Phase 2 = hook enforcement**. hook 자체는 vuln DB 조회 안 함. **approved spec ledger 와의 일치만 검증**. 빠르고 결정론적.
111
+ - **Phase 3 = post-install rollback**. v1 reorg engine 그대로. install 결과가 approved spec 과 어긋나면 마지막 confirmed 로 복원.
112
+
113
+ ---
114
+
115
+ ## 2. Vulnerability DB — Source Hierarchy
116
+
117
+ ```
118
+ ┌──────────────────────────────────────────────────────────────────────┐
119
+ │ TIER 1 — PRIMARY (canonical truth) │
120
+ │ ──────────────────────────────────────── │
121
+ │ OSV.dev │
122
+ │ • multi-ecosystem (npm, pip, cargo, go, gem, maven, nuget, ...) │
123
+ │ • package@version 질의 표준화 │
124
+ │ • Google 운영, 무료 API, JSON │
125
+ │ • GHSA, RustSec, GoVulnDB 등 다 aggregate │
126
+ │ → 모든 advisory 의 1차 query target │
127
+ ├──────────────────────────────────────────────────────────────────────┤
128
+ │ TIER 2 — OVERLAY (hard-risk signal) │
129
+ │ ──────────────────────────────────────── │
130
+ │ CISA KEV (Known Exploited Vulnerabilities) │
131
+ │ • "실제 야생에서 exploit 확인" 만 추림 │
132
+ │ • OSV 결과와 cross-reference: KEV 매치 시 hard-block (override 불가) │
133
+ │ → 일반 CVE vs "급박한 CVE" 의 구분선 │
134
+ ├──────────────────────────────────────────────────────────────────────┤
135
+ │ TIER 3 — ENRICHMENT / CROSS-CHECK │
136
+ │ ──────────────────────────────────────── │
137
+ │ GitHub Advisory (GHSA) │
138
+ │ • OSV 에 일부 들어오지만 first_patched_version / │
139
+ │ vulnerable_version_range 같은 metadata 가 개발자 친화 │
140
+ │ • "OSV 와 다른 결" 만 surface — discrepancy verifier 결 │
141
+ │ NVD │
142
+ │ • CVE 원본, CVSS 점수, CPE 매핑, hasKev flag │
143
+ │ • 점수 기반 우선순위 계산 │
144
+ │ deps.dev (Google) │
145
+ │ • OSV 기반 + package graph metadata (depended_by, license, ...) │
146
+ │ • transitive dep 위험 분석 │
147
+ │ Snyk DB (optional) │
148
+ │ • purl 기반 query, UI 풍부 │
149
+ │ • 무료 quota 한도, configured optional feed 만 사용 │
150
+ └──────────────────────────────────────────────────────────────────────┘
151
+ ```
152
+
153
+ 설계 원칙: **canonical truth 는 OSV 하나**. 다른 source 는 enrichment 또는 overlay. 여러 라이브 source 를 \"동급 진실\" 로 두면 cross-fire 발생 — 우리는 OSV 를 truth 로 두고 KEV/GHSA/NVD/deps.dev 는 \"OSV 와 다르거나, OSV 가 못 본 신호\" 만 surface.
154
+
155
+ ---
156
+
157
+ ## 3. Approved Spec Ledger — SSoT
158
+
159
+ `~/.safedeps/approved-specs/<hash>.json`:
160
+
161
+ ```json
162
+ {
163
+ "hash": "sha256:abc123...",
164
+ "ecosystem": "npm",
165
+ "package": "@jackwener/opencli",
166
+ "version": "1.7.16",
167
+ "version_range": "^1.7.16",
168
+ "approved_at": "2026-05-18T13:00:00Z",
169
+ "expires_at": "2026-06-18T13:00:00Z",
170
+ "approved_by": "user@example.com",
171
+ "evidence": {
172
+ "osv": { "queried_at": "2026-05-18T13:00:00Z", "vulnerabilities": [] },
173
+ "kev": { "queried_at": "2026-05-18T13:00:00Z", "exploited": false },
174
+ "ghsa": { "queried_at": "2026-05-18T13:00:00Z", "advisories": [] }
175
+ },
176
+ "transitive_specs": [
177
+ { "package": "...", "version": "...", "spec_hash": "..." }
178
+ ]
179
+ }
180
+ ```
181
+
182
+ **핵심 필드**:
183
+ - `hash` — `(ecosystem, package, version)` 의 deterministic hash. hook 이 명령에서 같은 hash 추출 → ledger 조회.
184
+ - `approved_at` / `expires_at` — **lifecycle TTL**. 기본 30일. 만료 후 새 CVE 가능성 있어 자동 revoke + re-check 강제.
185
+ - `evidence` — 그 시점에 어느 source 가 무엇을 봤는지. audit trail.
186
+ - `transitive_specs` — direct 만 아니라 transitive dep 도 ledger 에 박아 \"sub-dep 의 zero-day\" 도 감지 가능.
187
+
188
+ **Lifecycle**:
189
+
190
+ ```
191
+ approve install confirm re-check
192
+ ─────── ─────── ─────── ────────
193
+ │ │ │ │
194
+ ▼ ▼ ▼ ▼
195
+ ledger 신규 hook 통과 ledger.confirmed=true daily cron
196
+ approved_at=now spec hash 일치 post-verify 일치 OSV re-query
197
+ expires_at=+30d │
198
+
199
+ ┌──────┴──────┐
200
+ ▼ ▼
201
+ 여전히 clean 새 CVE 발견
202
+ │ │
203
+ ▼ ▼
204
+ expires_at ledger revoke
205
+ 연장 + 사용자 경고
206
+ + (옵션) auto reorg
207
+ ```
208
+
209
+ ---
210
+
211
+ ## 4. Runtime Flow — 3-Phase Detail
212
+
213
+ ### Phase 1 — `safedeps check <ecosystem> <pkg>@<range>`
214
+
215
+ ```
216
+ 사용자 / Claude:
217
+ "@jackwener/opencli ^1.7.0 깔고 싶음"
218
+
219
+
220
+ ┌─────────────────────────────────────────────────────────┐
221
+ │ safedeps check npm "@jackwener/opencli@^1.7.0" │
222
+ └────────────────────┬────────────────────────────────────┘
223
+
224
+ ┌────────────┴────────────────────────────┐
225
+ ▼ ▼
226
+ ┌──────────────┐ ┌──────────────┐
227
+ │ resolve │ │ ledger lookup │
228
+ │ version │ │ "이미 approved?" │
229
+ │ range → │ └───────┬──────┘
230
+ │ candidate │ │
231
+ │ versions │ ▼
232
+ └──────┬───────┘ ┌───────┴─────┐
233
+ │ ▼ ▼
234
+ ▼ hit (valid) miss / expired
235
+ ┌──────────────┐ │ │
236
+ │ OSV query │ ▼ ▼
237
+ │ per version │ "이미 안전, install "새로 check"
238
+ └──────┬───────┘ 해도 됨"
239
+
240
+
241
+ ┌──────────────┐
242
+ │ KEV overlay │
243
+ │ GHSA cross │
244
+ └──────┬───────┘
245
+
246
+
247
+ ┌─────────────────────────────────┐
248
+ │ 결과 분류: │
249
+ │ • clean (no vuln) → approve │
250
+ │ • patched_available → approve │
251
+ │ 안전 버전으로 spec 재작성 │
252
+ │ 예: ^1.7.0 → ^1.7.16 │
253
+ │ • KEV hit → HARD BLOCK │
254
+ │ "이 패키지는 실제 exploit 됨, │
255
+ │ 설치 X. 대체 모듈: AAA, BBB" │
256
+ │ • CVE 있는데 patch X → WARN │
257
+ │ "취약점 있음, 사용자 결정 필요" │
258
+ └─────────────────────────────────┘
259
+
260
+
261
+ approved spec ledger 신규 entry 작성
262
+ sha256:abc123... = {ecosystem: npm, pkg, version, ...}
263
+ ```
264
+
265
+ ### Phase 2 — Hook Enforcement (PreToolUse / `safedeps-pre-guard.sh`)
266
+
267
+ ```
268
+ Claude 가 Bash 실행 요청: "npm install @jackwener/opencli@^1.7.16"
269
+
270
+
271
+ ┌─────────────────────────────────┐
272
+ │ safedeps-pre-guard.sh │
273
+ │ 1. 명령 파싱: │
274
+ │ ecosystem = npm │
275
+ │ pkg = @jackwener/opencli │
276
+ │ version_range = ^1.7.16 │
277
+ │ 2. spec hash 계산 │
278
+ │ 3. ledger 조회 │
279
+ └────────────┬────────────────────┘
280
+
281
+ ┌────────┴────────┐
282
+ ▼ ▼
283
+ ledger hit ledger miss
284
+ (approved + (또는 expired)
285
+ not expired) │
286
+ │ ▼
287
+ ▼ ┌──────────────────────┐
288
+ PASS │ BLOCK + Claude 한테 │
289
+ (명령 실행) │ 안내: │
290
+ │ "이 패키지가 advisory │
291
+ │ gate 통과 안 됨. │
292
+ │ safedeps check ... │
293
+ │ 먼저 실행해서 spec │
294
+ │ approve 해야 함" │
295
+ └──────────────────────┘
296
+ ```
297
+
298
+ ### Phase 3 — Post-Install Rollback (PostToolUse / `safedeps-post-verify.sh`)
299
+
300
+ ```
301
+ install 완료 → safedeps-post-verify.sh
302
+
303
+
304
+ ┌─────────────────────────────────────────┐
305
+ │ 1. lockfile 새 entry 추출 │
306
+ │ 2. 각 entry 의 spec hash 계산 │
307
+ │ 3. ledger 와 비교 │
308
+ │ 4. install script / native binary 검사 │
309
+ │ (v1 reorg-guard 로직 그대로) │
310
+ └────────────┬────────────────────────────┘
311
+
312
+ ┌────────┴────────┐
313
+ ▼ ▼
314
+ spec 과 동일 diverged
315
+ + 의심 없음 (예: transitive dep
316
+ │ 이 ledger 에 없는
317
+ ▼ 버전으로 깔림,
318
+ CONFIRM install script 의심,
319
+ (ledger 의 native binary 출현)
320
+ confirmed=true) │
321
+
322
+ REORG (v1 engine):
323
+ • lockfile ← 마지막 confirmed snapshot
324
+ • rm -rf node_modules
325
+ • npm install (재설치 = ledger 와 일치)
326
+ • reorg.log 기록
327
+ • Claude 한테 경고
328
+ ```
329
+
330
+ ---
331
+
332
+ ## 5. Threat Model (v2 갱신)
333
+
334
+ ```
335
+ ┌─────────────────────────────────────────────────────────────────────┐
336
+ │ PHASE 1 — ADVISORY GATE (`safedeps check`) │
337
+ ├─────────────────────────────────────────────────────────────────────┤
338
+ │ • 알려진 CVE 매칭 (OSV.dev 기반, multi-ecosystem) │
339
+ │ • KEV 매칭 시 hard-block (사용자 override 불가) │
340
+ │ • patched_available 시 안전 버전으로 spec auto-rewrite │
341
+ │ • transitive dep 의 vuln 도 ledger 에 박아 sub-dep 침해 감지 │
342
+ ├─────────────────────────────────────────────────────────────────────┤
343
+ │ PHASE 2 — HOOK ENFORCEMENT (`safedeps-pre-guard.sh`) │
344
+ ├─────────────────────────────────────────────────────────────────────┤
345
+ │ v1 의 hardcoded pattern 결도 유지 (defense-in-depth): │
346
+ │ • typosquat 명단 매치 │
347
+ │ • curl|bash 류 pipe execution │
348
+ │ • 비표준 --registry URL │
349
+ │ • install script safety disabling │
350
+ │ • eval / subshell indirection │
351
+ │ + 새로운 enforcement: │
352
+ │ • spec hash 가 ledger 에 없거나 expired → BLOCK + advisory gate 안내 │
353
+ ├─────────────────────────────────────────────────────────────────────┤
354
+ │ PHASE 3 — POST-INSTALL REORG (`safedeps-post-verify.sh`) │
355
+ ├─────────────────────────────────────────────────────────────────────┤
356
+ │ • install script 의 network/code execution/sensitive path 검사 │
357
+ │ • base64/hex obfuscation │
358
+ │ • 비표준 registry resolved URL │
359
+ │ • 50+ dep explosion │
360
+ │ • native binary 출현 │
361
+ │ • lockfile entry 가 approved spec 과 diverged → REORG │
362
+ └─────────────────────────────────────────────────────────────────────┘
363
+ ```
364
+
365
+ **막지 않는 것 (현재 한계)**:
366
+ - ledger 의 approved_at 이후 새로 발견된 zero-day — daily re-check 로만 catch 가능, install 시점엔 못 잡음.
367
+ - npm registry 자체의 손상.
368
+ - 사용자가 `--allow-unverified` 명시 우회한 경우 (observable, log 기록).
369
+
370
+ ---
371
+
372
+ ## 6. Provider Failure Modes (No Silent Fallback)
373
+
374
+ ```
375
+ ┌────────────────────────────────────────────────────────────────────┐
376
+ │ OSV.dev 응답 무 / timeout │
377
+ │ ──────────────────── │
378
+ │ • 1차: provider cache (local TTL 24h) 사용 │
379
+ │ • cache miss → fail-closed (block + "OSV 응답 없음, 재시도") │
380
+ │ • 사용자 explicit `--allow-unverified` 시만 우회 + log │
381
+ ├────────────────────────────────────────────────────────────────────┤
382
+ │ CISA KEV 응답 무 │
383
+ │ ──────────────────── │
384
+ │ • KEV 는 매일 1회 download (정적 catalog), 로컬 cache 만 사용 │
385
+ │ • 24h 이상 stale 시 경고 │
386
+ ├────────────────────────────────────────────────────────────────────┤
387
+ │ GHSA / NVD 응답 무 │
388
+ │ ──────────────────── │
389
+ │ • enrichment 결이라 fail-open 허용 │
390
+ │ • OSV 결과로만 진행 + log 에 "GHSA cross-check skipped" │
391
+ └────────────────────────────────────────────────────────────────────┘
392
+ ```
393
+
394
+ 설계 원칙: **silent fallback 금지**. 모든 우회는 observable + log. canonical truth (OSV) 가 응답 못 하면 default = fail-closed.
395
+
396
+ ---
397
+
398
+ ## 7. State Layout — `~/.safedeps/`
399
+
400
+ ```
401
+ ~/.safedeps/
402
+ ├── approved-specs/
403
+ │ ├── sha256-abc123.json ← 한 (ecosystem, package, version) 당 한 파일
404
+ │ ├── sha256-def456.json
405
+ │ └── ...
406
+
407
+ ├── snapshots/ ← v1 reorg snapshot 그대로 계승
408
+ │ ├── 20260518-130000-xyz/
409
+ │ │ ├── package-lock.json
410
+ │ │ ├── yarn.lock
411
+ │ │ ├── pnpm-lock.yaml
412
+ │ │ ├── poetry.lock ← v2 ecosystem 확장
413
+ │ │ ├── uv.lock
414
+ │ │ ├── Cargo.lock
415
+ │ │ ├── go.sum
416
+ │ │ ├── Gemfile.lock
417
+ │ │ └── meta.json
418
+ │ └── ...
419
+
420
+ ├── confirmed_${dir_hash} ← 프로젝트별 마지막 confirmed snapshot
421
+
422
+ ├── cache/
423
+ │ ├── osv/ ← OSV query 응답 cache (24h TTL)
424
+ │ │ └── npm-@jackwener-opencli-1.7.16.json
425
+ │ └── kev/ ← CISA KEV daily catalog
426
+ │ └── kev-2026-05-18.json
427
+
428
+ ├── locks/ ← atomic state (TOCTOU 방지)
429
+ ├── reorg.log ← REORG event 기록 (append-only)
430
+ └── advisory.log ← advisory gate event 기록 (block/approve)
431
+ ```
432
+
433
+ 설계 결정:
434
+ - `approved-specs/` = ledger SSoT, JSON-per-spec (atomic write).
435
+ - `snapshots/` = v1 결 그대로 + py/rust/go/ruby lockfile 추가.
436
+ - `cache/osv/`, `cache/kev/` = provider 응답 cache (TTL).
437
+ - `advisory.log` = advisory gate 의 모든 approve/block decision audit trail.
438
+
439
+ ---
440
+
441
+ ## 8. Multi-Ecosystem Support (v2 확장)
442
+
443
+ | Ecosystem | Manifest | Lockfile | safedeps check 명령 |
444
+ |---|---|---|---|
445
+ | npm | `package.json` | `package-lock.json` | `safedeps check npm <pkg>@<range>` |
446
+ | yarn | `package.json` | `yarn.lock` | `safedeps check npm <pkg>@<range>` (OSV 공통) |
447
+ | pnpm | `package.json` | `pnpm-lock.yaml` | `safedeps check npm <pkg>@<range>` |
448
+ | pip (Poetry) | `pyproject.toml` | `poetry.lock` | `safedeps check pypi <pkg>@<range>` |
449
+ | pip (uv) | `pyproject.toml` | `uv.lock` | `safedeps check pypi <pkg>@<range>` |
450
+ | pip (Pipenv) | `Pipfile` | `Pipfile.lock` | `safedeps check pypi <pkg>@<range>` |
451
+ | pip (raw) | `requirements.txt` | (약함) | `safedeps check pypi <pkg>@<range>` |
452
+ | cargo | `Cargo.toml` | `Cargo.lock` | `safedeps check crates.io <pkg>@<range>` |
453
+ | go | `go.mod` | `go.sum` | `safedeps check go <pkg>@<range>` |
454
+ | ruby | `Gemfile` | `Gemfile.lock` | `safedeps check rubygems <pkg>@<range>` |
455
+ | maven | `pom.xml` | (해당 디렉토리) | `safedeps check maven <group>:<artifact>@<range>` |
456
+ | nuget | `*.csproj` | `packages.lock.json` | `safedeps check nuget <pkg>@<range>` |
457
+
458
+ 각 ecosystem 의 typosquat 명단·install script 위험 패턴은 별도 정적 list 로. OSV.dev 가 ecosystem 정규화를 표준화해줘서 single API 결로 모두 cover.
459
+
460
+ ---
461
+
462
+ ## 9. 컴포넌트 책임 분리 (SoC)
463
+
464
+ ```
465
+ ┌─────────────────────────────────────────────────────────────────────┐
466
+ │ SKILL.md — Claude/Codex skill loader 가 읽는 SSoT. │
467
+ │ hook 선언 + advisory gate 사용법 안내. │
468
+ ├─────────────────────────────────────────────────────────────────────┤
469
+ │ README.md — 사용자 install 가이드. │
470
+ ├─────────────────────────────────────────────────────────────────────┤
471
+ │ ARCHITECTURE.md — 이 문서. 내부 흐름·설계. │
472
+ ├─────────────────────────────────────────────────────────────────────┤
473
+ │ bin/safedeps — CLI entry (advisory check, ledger 관리, │
474
+ │ 재검증 등). │
475
+ ├─────────────────────────────────────────────────────────────────────┤
476
+ │ scripts/safedeps-pre-guard.sh — PreToolUse hook. ledger 일치 검증 │
477
+ │ + v1 의 hardcoded pattern. │
478
+ ├─────────────────────────────────────────────────────────────────────┤
479
+ │ scripts/safedeps-post-verify.sh — PostToolUse hook. lockfile diff + │
480
+ │ spec 비교 + reorg. │
481
+ ├─────────────────────────────────────────────────────────────────────┤
482
+ │ lib/providers/ — OSV / KEV / GHSA / NVD / deps.dev / Snyk │
483
+ │ adapter. 하나의 query interface. │
484
+ ├─────────────────────────────────────────────────────────────────────┤
485
+ │ lib/ledger/ — approved spec ledger I/O. atomic write, │
486
+ │ hash 계산, TTL 검사. │
487
+ └─────────────────────────────────────────────────────────────────────┘
488
+ ```
489
+
490
+ ---
491
+
492
+ ## 10. 비교 — 기존 도구들과 결 (정확화)
493
+
494
+ | 도구 | 결 | 동작 시점 | safedeps 와 차이 |
495
+ |---|---|---|---|
496
+ | `npm audit` | materialized lock 기반 취약점 보고 | post-install | spec 결정 / 차단 안 함, **보고만** |
497
+ | `pip-audit` / `cargo audit` / `bundler-audit` | 같은 결, 다른 ecosystem | post-install | 같음 |
498
+ | **socket.dev** | SaaS risk intelligence (behavioral + static) | pre-install + post-install | 클라우드 의존, 무료 quota 한도, **외부 SaaS** |
499
+ | **lavamoat** | runtime permission containment (sandbox) | runtime | install 전 차단 X, **무겁고 dev 단계 부담** |
500
+ | **pnpm `onlyBuiltDependencies`** | lifecycle script allowlist | install 단계 | typosquat / vuln DB X, **script 차단만** |
501
+ | **deps.dev** | package graph metadata | query only | active gate 아님, **데이터만** |
502
+ | **OSV-Scanner** | OSV 결 lockfile 스캔 | post-install (CI) | spec gate X, **lockfile 리포트만** |
503
+ | **GitHub Dependabot** | PR 기반 dep update 권장 | repo-level (PR) | local install 차단 X, **PR 단계만** |
504
+ | **`safedeps` (이것)** | **advisory gate + approved spec ledger + post-install reorg** | **pre-install + install + post-install** | **3-phase, multi-ecosystem, 로컬 first** |
505
+
506
+ 차별점 요약:
507
+ - 다른 도구는 \"보고\" / \"sandbox\" / \"script 차단\" / \"PR 권장\" 중 하나에 집중.
508
+ - safedeps 는 **\"advisory gate (Phase 1) → hook enforcement (Phase 2) → reorg fallback (Phase 3)\" 의 3겹 defense-in-depth**.
509
+ - Snyk / socket.dev 와 달리 **SaaS 의존 X, 로컬 + 공개 DB (OSV/KEV/GHSA) 만**.
510
+
511
+ ---
512
+
513
+ ## 11. 운영 로그
514
+
515
+ ```bash
516
+ # advisory gate decision (approve / block)
517
+ tail -f ~/.safedeps/advisory.log
518
+
519
+ # reorg event
520
+ tail -f ~/.safedeps/reorg.log
521
+
522
+ # 현재 approved specs
523
+ ls -lt ~/.safedeps/approved-specs/
524
+
525
+ # 특정 spec 의 evidence
526
+ cat ~/.safedeps/approved-specs/sha256-abc123.json | jq '.evidence'
527
+
528
+ # expired specs (만료된 거 재검증 필요)
529
+ find ~/.safedeps/approved-specs -name '*.json' -exec jq -r 'select(.expires_at < now) | "\(.package)@\(.version) expired \(.expires_at)"' {} \;
530
+
531
+ # OSV cache 비우기 (강제 re-query)
532
+ rm -rf ~/.safedeps/cache/osv/
533
+ ```
534
+
535
+ ---
536
+
537
+ ## 12. v1 → v2 마이그레이션
538
+
539
+ ```
540
+ v1 (npm-reorg-guard) v2 (safedeps)
541
+ ──────────────────── ──────────────
542
+
543
+ ~/.npm-reorg-guard/ → ~/.safedeps/
544
+ ~/.claude/skills/ ~/.claude/skills/
545
+ npm-reorg-guard/ → safedeps/
546
+ (old local skill path is not canonical)
547
+
548
+ scripts/guard.sh → scripts/safedeps-pre-guard.sh
549
+ (typosquat / pattern + ledger lookup
550
+ 매칭만) + v1 pattern 유지
551
+ + namespaced filename
552
+
553
+ scripts/verify.sh → scripts/safedeps-post-verify.sh
554
+ (lockfile diff + reorg) + approved spec diff
555
+ + v1 reorg 유지
556
+ + namespaced filename
557
+
558
+ (없음) → bin/safedeps ← 새 CLI
559
+ check / approve / revoke /
560
+ re-check / ledger
561
+
562
+ (없음) → lib/providers/ ← OSV / KEV / GHSA / ...
563
+ (없음) → lib/ledger/ ← approved spec I/O
564
+
565
+ GitHub repo:
566
+ aldegad/npm-reorg-guard → aldegad/safedeps
567
+ (GitHub redirect only)
568
+ ```
569
+
570
+ v1 migration:
571
+ - v1 hook 등록 path (`~/.claude/skills/npm-reorg-guard/scripts/*.sh`) 는 canonical 이 아니다. settings 는 `~/.claude/skills/safedeps/scripts/*.sh` 로 갱신한다.
572
+ - v1 의 `~/.npm-reorg-guard/` 디렉토리 발견 시 `~/.safedeps/` 으로 마이그레이션한다 (snapshot chain 보존).
573
+ - v1 사용자는 `safedeps migrate` 한 줄로 ledger 신규 생성 + 기존 confirmed snapshot 들을 그대로 approved spec 으로 변환.
574
+
575
+ ---
576
+
577
+ ## 13. 한계 + 미래 방향
578
+
579
+ **현재 한계 (v2 첫 출시)**:
580
+ - approved_at 이후 발견된 zero-day 는 daily re-check 로만 catch.
581
+ - npm/PyPI 같은 registry 자체 손상 시 우리도 못 막음.
582
+ - KEV 는 24h 단위 update — 그 사이 새 KEV 등재 못 잡음.
583
+ - transitive dep 의 vuln 검사는 ledger 폭증 가능 (수백 개 dep). 최적화 필요.
584
+
585
+ **미래 확장**:
586
+ - **plugin model** — 사용자 정의 provider (회사 내부 vuln DB, private registry).
587
+ - **policy file** — `.safedeps.toml` 로 \"우리 팀 정책: KEV hit 자동 block, CVSS 7+ 는 사용자 컨펌\" 같이 명시.
588
+ - **CI mode** — `safedeps check --ci` 로 GitHub Actions / CircleCI 등에서 fail fast.
589
+ - **multi-machine ledger sync** — 팀 차원의 approved spec 공유.
590
+ - **deps.dev graph 활용** — transitive dep 의 \"위험 score\" 시각화.
591
+ - **AI agent integration** — Claude / Codex 가 \"이 패키지 알려진 vuln 있음, 대체 모듈 X 권장\" 결로 직접 제안.
592
+
593
+ ---
594
+
595
+ *문서 history*: v1 (`npm-reorg-guard`) 2026-05-18 작성 → v2 (`safedeps`) 2026-05-18 재작성. 코덱시 (surface:195) 와 클로디시 (surface:61) peer-question 합의안 기반.