@aldegad/safedeps 2.1.0 → 2.2.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.
- package/ARCHITECTURE.md +268 -462
- package/README.ko.md +168 -0
- package/README.md +88 -44
- package/ROADMAP.md +82 -87
- package/SKILL.md +13 -7
- package/bin/safedeps +385 -52
- package/lib/gates/audit.sh +36 -0
- package/lib/gates/hooks.sh +93 -0
- package/lib/gates/repo-profile.sh +60 -0
- package/lib/gates/scan.sh +94 -0
- package/lib/ledger/ledger.sh +94 -16
- package/lib/npm/closure.sh +115 -0
- package/lib/providers/providers.sh +244 -25
- package/package.json +2 -1
- package/scripts/install/install-safedeps-hooks.mjs +62 -23
- package/scripts/release-gates.sh +252 -0
- package/scripts/safedeps-post-verify.sh +167 -10
- package/scripts/safedeps-pre-guard.sh +270 -32
- package/scripts/test/e2e.sh +180 -4
- package/scripts/test/fixture-provider.mjs +21 -0
- package/scripts/test/smoke.sh +135 -10
package/ARCHITECTURE.md
CHANGED
|
@@ -1,166 +1,122 @@
|
|
|
1
|
-
# Safedeps Architecture
|
|
1
|
+
# Safedeps Architecture
|
|
2
2
|
|
|
3
|
-
>
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
- **README.md** — 사용자 설치/사용 가이드 (rename 후 갱신 예정)
|
|
7
|
-
- **SKILL.md** — 스킬 메타 + hook 선언 (Claude/Codex skill loader 가 읽는 SSoT)
|
|
8
|
-
- **ARCHITECTURE.md** — 내부 흐름·설계 (이 문서)
|
|
3
|
+
> Internal design and runtime flow. User-facing setup lives in [`README.md`](./README.md); the skill manifest and hook declarations live in [`SKILL.md`](./SKILL.md). *(한국어 → [ARCHITECTURE.ko.md](./ARCHITECTURE.ko.md))*
|
|
4
|
+
>
|
|
5
|
+
> **Naming** — the project shipped as `npm-reorg-guard` in v1. v2 unified ecosystems and added the advisory ledger, renaming the product and CLI to **`safedeps`**. The post-install rollback engine still inherits the v1 `reorg-guard` design, and for npm the PostToolUse effect gate is the primary enforcement surface.
|
|
9
6
|
|
|
10
7
|
---
|
|
11
8
|
|
|
12
|
-
##
|
|
9
|
+
## Core idea
|
|
13
10
|
|
|
14
|
-
>
|
|
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.*
|
|
11
|
+
> Safedeps does not decide at install time from several live truths at once. It approves one dependency closure from provider evidence *first*; then the post-install hook treats the installed lockfile closure as the authority, and reorg rolls back any unapproved or newly-vulnerable effect.
|
|
16
12
|
|
|
17
|
-
|
|
13
|
+
Approval happens **before** the install, against canonical advisory evidence. Enforcement happens **after** the install, against what actually landed on disk. For npm, the full closure (direct + transitive) is checked through OSV `/v1/querybatch` with a 24-hour per-`pkg@version` cache. Closure resolution for the other ecosystems is future work.
|
|
18
14
|
|
|
19
15
|
---
|
|
20
16
|
|
|
21
|
-
## 1.
|
|
17
|
+
## 1. Two lanes, one umbrella
|
|
18
|
+
|
|
19
|
+
safedeps owns security gates at two distinct moments, under one skill. It absorbed the v1 `npm-reorg-guard` (install-time reorg) and then the `security-release-gates` project (release-time checks, 2026-05-24). The goal is not to pile every "security" concern into one file — it is to give the gates a single canonical owner while keeping each lane's responsibility separate (SRP).
|
|
22
20
|
|
|
21
|
+
```text
|
|
22
|
+
┌──────────────────────────────────────────────────────────────────────┐
|
|
23
|
+
│ safedeps — one security umbrella │
|
|
24
|
+
│ │
|
|
25
|
+
│ INSTALL-TIME lane RELEASE-TIME lane │
|
|
26
|
+
│ (during development, per install) (before a release / push) │
|
|
27
|
+
│ ───────────────────── ────────────────── │
|
|
28
|
+
│ advisory check (npm: OSV batch) safedeps scan secrets │
|
|
29
|
+
│ fast command gate (PreToolUse) safedeps audit deps │
|
|
30
|
+
│ npm effect gate (PostToolUse) safedeps hooks install|check │
|
|
31
|
+
│ safedeps git pre-commit │
|
|
32
|
+
│ scope: the package being installed scope: the whole repo tree │
|
|
33
|
+
│ (absorbed from │
|
|
34
|
+
│ security-release-gates) │
|
|
35
|
+
│ │
|
|
36
|
+
│ shared: public DBs (OSV/KEV/GHSA) · local-first · no silent │
|
|
37
|
+
│ fallback (a provider/scanner miss is fail-closed) │
|
|
38
|
+
└──────────────────────────────────────────────────────────────────────┘
|
|
23
39
|
```
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
│
|
|
40
|
-
│
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
│
|
|
46
|
-
│
|
|
47
|
-
│
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
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
|
-
└─────────────────────────────────────────────────────────────────────┘
|
|
40
|
+
|
|
41
|
+
- **Install-time lane** (sections 2–13 below) — advisory check, fast command guard, and the npm effect gate + reorg. Per-package and proactive.
|
|
42
|
+
- **Release-time lane** — the repo-tree checks from `security-release-gates` (secret scan, dependency audit, repo hook install/check, privacy profile), exposed under the `safedeps scan|audit|hooks|git` command namespace. Repo-specific policy (`.gitleaks.toml`, lockfiles) stays in the target repo; safedeps owns execution, install, and verification.
|
|
43
|
+
|
|
44
|
+
The two lanes differ in timing and scope (one package's effect before/after install vs. the whole repo before a release). They live under one umbrella but stay separated by command namespace.
|
|
45
|
+
|
|
46
|
+
**The effect-primary model is npm-only.** `pip`, `cargo`, `go`, `gem`, `maven`, and `nuget` stay on the v2.1 command-gate + reorg model until their closure resolvers land; they are not described as having PostToolUse closure authority.
|
|
47
|
+
|
|
48
|
+
### Install-time flow
|
|
49
|
+
|
|
50
|
+
```
|
|
51
|
+
intent ("I want to install this package")
|
|
52
|
+
│
|
|
53
|
+
▼
|
|
54
|
+
┌─────────────┐ OSV.dev ──canonical──►
|
|
55
|
+
│ safedeps │ CISA KEV ──hard-risk──► advisory check
|
|
56
|
+
│ check │ GHSA ──enrichment─► (Phase 1)
|
|
57
|
+
└──────┬──────┘
|
|
58
|
+
│ approve
|
|
59
|
+
▼
|
|
60
|
+
┌──────────────────────┐
|
|
61
|
+
│ approved-spec ledger │ ~/.safedeps/approved-specs/<hash>.json
|
|
62
|
+
│ ecosystem · pkg@ver │ + transitive_specs (npm closure)
|
|
63
|
+
│ approved_at/expires │
|
|
64
|
+
└──────────────────────┘
|
|
65
|
+
│
|
|
66
|
+
▼
|
|
67
|
+
install command issued ──► PreToolUse hook (fast command guard, Phase 2)
|
|
68
|
+
│ ledger match? ── miss ──► BLOCK + "run safedeps check first"
|
|
69
|
+
│ match ──► run
|
|
70
|
+
▼
|
|
71
|
+
install runs
|
|
72
|
+
│
|
|
73
|
+
▼
|
|
74
|
+
PostToolUse hook (npm effect gate, Phase 3)
|
|
75
|
+
│ lockfile closure vs ledger + OSV batch
|
|
76
|
+
├─ approved & clean ──► CONFIRM (new safe baseline)
|
|
77
|
+
└─ unapproved / vulnerable ──► REORG (roll back to last confirmed)
|
|
106
78
|
```
|
|
107
79
|
|
|
108
|
-
|
|
109
|
-
- **Phase
|
|
110
|
-
- **Phase
|
|
111
|
-
- **Phase 3 = post-install rollback**. v1 reorg engine 그대로. install 결과가 approved spec 과 어긋나면 마지막 confirmed 로 복원.
|
|
80
|
+
- **Phase 1 — advisory check.** For npm, safedeps builds a script-free lockfile in a temp dir (`npm install <pkg>@<version> --package-lock-only --ignore-scripts`), extracts the full closure, and queries OSV `/v1/querybatch` for direct and transitive packages together. When clean, the direct ledger entry records `transitive_specs`.
|
|
81
|
+
- **Phase 2 — fast command gate.** The PreToolUse hook parses the command, blocks obvious unapproved installs, and snapshots dependency files. It is a best-effort advisory layer that gives the agent immediate feedback — not the final authority. On Claude Code it also rewrites an npm install to add `--ignore-scripts` (via the hook `updatedInput` capability), so the install runs inert and no lifecycle script executes until the effect gate has verified the closure.
|
|
82
|
+
- **Phase 3 — npm primary effect gate.** The PostToolUse hook compares the actual `package-lock.json` closure against the ledger's direct entries and their `transitive_specs`, and re-queries OSV in batch. Any unapproved or vulnerable package triggers a reorg to the last confirmed snapshot. This authority is scoped to the npm closure.
|
|
112
83
|
|
|
113
84
|
---
|
|
114
85
|
|
|
115
|
-
## 2.
|
|
86
|
+
## 2. Advisory sources — one canonical truth
|
|
116
87
|
|
|
117
88
|
```
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
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
|
-
└──────────────────────────────────────────────────────────────────────┘
|
|
89
|
+
TIER 1 — PRIMARY (canonical truth)
|
|
90
|
+
OSV.dev
|
|
91
|
+
• multi-ecosystem (npm, pip, cargo, go, gem, maven, nuget, …)
|
|
92
|
+
• normalized package@version queries · free JSON API (Google)
|
|
93
|
+
• aggregates GHSA, RustSec, GoVulnDB, and more
|
|
94
|
+
→ the first query target for every advisory
|
|
95
|
+
|
|
96
|
+
TIER 2 — OVERLAY (hard-risk signal)
|
|
97
|
+
CISA KEV (Known Exploited Vulnerabilities)
|
|
98
|
+
• only "confirmed exploited in the wild"
|
|
99
|
+
• cross-referenced with OSV results; a KEV match is a hard block (no override)
|
|
100
|
+
→ the line between an ordinary CVE and an urgent one
|
|
101
|
+
|
|
102
|
+
TIER 3 — ENRICHMENT / CROSS-CHECK
|
|
103
|
+
GitHub Advisory (GHSA) — developer-friendly patched-version metadata; surfaced when it disagrees with OSV
|
|
104
|
+
NVD — CVE source, CVSS scores, KEV flag (for score-based prioritization)
|
|
105
|
+
deps.dev — OSV-based package graph metadata (transitive risk)
|
|
106
|
+
Snyk DB — optional configured feed only (free-quota limited)
|
|
151
107
|
```
|
|
152
108
|
|
|
153
|
-
|
|
109
|
+
Design principle: **OSV is the one canonical truth.** Every other source is overlay or enrichment. Treating several live sources as co-equal truths invites cross-fire; instead OSV is the truth, and KEV/GHSA/NVD/deps.dev only surface signals that disagree with OSV or that OSV did not see.
|
|
154
110
|
|
|
155
111
|
---
|
|
156
112
|
|
|
157
|
-
## 3. Approved
|
|
113
|
+
## 3. Approved-spec ledger (SSoT)
|
|
158
114
|
|
|
159
115
|
`~/.safedeps/approved-specs/<hash>.json`:
|
|
160
116
|
|
|
161
117
|
```json
|
|
162
118
|
{
|
|
163
|
-
"hash": "sha256:abc123
|
|
119
|
+
"hash": "sha256:abc123…",
|
|
164
120
|
"ecosystem": "npm",
|
|
165
121
|
"package": "@jackwener/opencli",
|
|
166
122
|
"version": "1.7.16",
|
|
@@ -169,427 +125,277 @@
|
|
|
169
125
|
"expires_at": "2026-06-18T13:00:00Z",
|
|
170
126
|
"approved_by": "user@example.com",
|
|
171
127
|
"evidence": {
|
|
172
|
-
"
|
|
173
|
-
"
|
|
174
|
-
"
|
|
128
|
+
"closure_checked": true,
|
|
129
|
+
"provider": { "type": "osv-querybatch", "results": [] },
|
|
130
|
+
"closure": []
|
|
175
131
|
},
|
|
176
132
|
"transitive_specs": [
|
|
177
|
-
{ "
|
|
133
|
+
{ "ecosystem": "npm", "package": "…", "version": "…" }
|
|
178
134
|
]
|
|
179
135
|
}
|
|
180
136
|
```
|
|
181
137
|
|
|
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\" 도 감지 가능.
|
|
138
|
+
Key fields:
|
|
187
139
|
|
|
188
|
-
|
|
140
|
+
- `hash` — a deterministic hash of `(ecosystem, package, version)`. The hook derives the same hash from a command and looks the ledger up by it.
|
|
141
|
+
- `approved_at` / `expires_at` — lifecycle TTL, 30 days by default. After expiry a new CVE may exist, so the spec is auto-revoked and re-check is forced.
|
|
142
|
+
- `evidence` — which source saw what, at approval time. An audit trail.
|
|
143
|
+
- `transitive_specs` — the full transitive closure the direct entry approved. The npm effect gate reorgs any `pkg@version` that appears in the lockfile but is in neither the direct entry nor this array.
|
|
144
|
+
|
|
145
|
+
Lifecycle:
|
|
189
146
|
|
|
190
147
|
```
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
▼
|
|
199
|
-
┌──────┴──────┐
|
|
200
|
-
▼ ▼
|
|
201
|
-
여전히 clean 새 CVE 발견
|
|
202
|
-
│ │
|
|
203
|
-
▼ ▼
|
|
204
|
-
expires_at ledger revoke
|
|
205
|
-
연장 + 사용자 경고
|
|
206
|
-
+ (옵션) auto reorg
|
|
148
|
+
approve install confirm re-check (daily)
|
|
149
|
+
─────── ─────── ─────── ────────────────
|
|
150
|
+
ledger entry ──► hook passes ──► post-verify match ──► OSV re-query
|
|
151
|
+
approved_at=now spec matches confirmed = true │
|
|
152
|
+
expires_at=+30d ▼
|
|
153
|
+
still clean ──► extend expiry
|
|
154
|
+
new CVE ──► revoke + warn (+ optional reorg)
|
|
207
155
|
```
|
|
208
156
|
|
|
209
157
|
---
|
|
210
158
|
|
|
211
|
-
## 4. Runtime
|
|
159
|
+
## 4. Runtime flow in detail
|
|
212
160
|
|
|
213
161
|
### Phase 1 — `safedeps check <ecosystem> <pkg>@<range>`
|
|
214
162
|
|
|
215
163
|
```
|
|
216
|
-
|
|
217
|
-
"@jackwener/opencli ^1.7.0 깔고 싶음"
|
|
164
|
+
safedeps check npm "@jackwener/opencli@^1.7.0"
|
|
218
165
|
│
|
|
166
|
+
├─► ledger lookup ── hit (valid) ──► "already safe, install is fine"
|
|
167
|
+
│ └ miss/expired ──► proceed to check
|
|
219
168
|
▼
|
|
220
|
-
|
|
221
|
-
│
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
▼
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
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, ...}
|
|
169
|
+
resolve range → concrete version(s)
|
|
170
|
+
│
|
|
171
|
+
▼
|
|
172
|
+
OSV query ──► KEV overlay ──► GHSA cross-check
|
|
173
|
+
│
|
|
174
|
+
▼
|
|
175
|
+
classify:
|
|
176
|
+
• clean → approve
|
|
177
|
+
• patched available → approve, rewrite spec to the fixed version (^1.7.0 → ^1.7.16)
|
|
178
|
+
• KEV hit → HARD BLOCK ("exploited in the wild; do not install")
|
|
179
|
+
• CVE, no patch → WARN (user decision required)
|
|
180
|
+
│
|
|
181
|
+
▼
|
|
182
|
+
write a new approved-spec ledger entry
|
|
263
183
|
```
|
|
264
184
|
|
|
265
|
-
|
|
185
|
+
For npm, "OSV query" runs over the **whole resolved closure** in one `/v1/querybatch` call, and the approved entry records every transitive package in `transitive_specs`.
|
|
186
|
+
|
|
187
|
+
### Phase 2 — fast command guard (PreToolUse / `safedeps-pre-guard.sh`)
|
|
266
188
|
|
|
267
189
|
```
|
|
268
|
-
Claude
|
|
190
|
+
Claude runs: npm install @jackwener/opencli@^1.7.16
|
|
269
191
|
│
|
|
270
192
|
▼
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
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
|
-
└──────────────────────┘
|
|
193
|
+
parse command → ecosystem, package, version_range
|
|
194
|
+
compute spec hash → ledger lookup
|
|
195
|
+
│
|
|
196
|
+
├─ hit (approved, not expired) ──► PASS (run the command)
|
|
197
|
+
└─ miss / expired ──────────────► BLOCK + "run `safedeps check …` first, then retry"
|
|
296
198
|
```
|
|
297
199
|
|
|
298
|
-
|
|
200
|
+
The guard also snapshots lockfiles/manifests and keeps the v1 hardcoded pattern blocks (see section 5). It is fast and advisory; the authority is the post-install gate.
|
|
201
|
+
|
|
202
|
+
### Phase 3 — npm primary effect gate + reorg (PostToolUse / `safedeps-post-verify.sh`)
|
|
299
203
|
|
|
300
204
|
```
|
|
301
|
-
install
|
|
205
|
+
install done → safedeps-post-verify.sh
|
|
302
206
|
│
|
|
303
207
|
▼
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
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 한테 경고
|
|
208
|
+
read the actual package-lock.json closure
|
|
209
|
+
check every pkg@version against the ledger (direct entries + transitive_specs)
|
|
210
|
+
re-query OSV in batch for the whole closure
|
|
211
|
+
inspect install scripts + native binaries (v1 reorg-guard logic)
|
|
212
|
+
│
|
|
213
|
+
├─ all approved, clean, no suspicion ──► CONFIRM (new safe baseline)
|
|
214
|
+
└─ unapproved / vulnerable / suspicious ──► REORG:
|
|
215
|
+
• restore lockfile from the last confirmed snapshot
|
|
216
|
+
• rm -rf node_modules; reinstall to match the ledger
|
|
217
|
+
• append to reorg.log; message the agent
|
|
328
218
|
```
|
|
329
219
|
|
|
330
220
|
---
|
|
331
221
|
|
|
332
|
-
## 5. Threat
|
|
222
|
+
## 5. Threat model
|
|
333
223
|
|
|
334
224
|
```
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
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
|
-
└─────────────────────────────────────────────────────────────────────┘
|
|
225
|
+
ADVISORY CHECK (safedeps check)
|
|
226
|
+
• known-CVE matching (OSV, multi-ecosystem)
|
|
227
|
+
• KEV match → hard block (no user override)
|
|
228
|
+
• patched-available → auto-rewrite the spec to the fixed version
|
|
229
|
+
• transitive vulns recorded in the ledger, so sub-dependency compromise is detectable
|
|
230
|
+
|
|
231
|
+
FAST COMMAND GUARD (safedeps-pre-guard.sh)
|
|
232
|
+
v1 hardcoded patterns (defense-in-depth): typosquat list · curl|bash pipes ·
|
|
233
|
+
non-standard --registry · install-script-safety disabling · eval/subshell indirection
|
|
234
|
+
+ fast advisory ledger check: missing/expired spec → block with advisory-gate guidance
|
|
235
|
+
|
|
236
|
+
npm PRIMARY EFFECT GATE + REORG (safedeps-post-verify.sh)
|
|
237
|
+
• install-script network / code-execution / sensitive-path access
|
|
238
|
+
• base64 / hex obfuscation
|
|
239
|
+
• non-standard registry resolved URLs · 50+ dependency explosion · native binaries
|
|
240
|
+
• npm lockfile closure diverging from approved specs / transitive_specs → REORG
|
|
363
241
|
```
|
|
364
242
|
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
243
|
+
**Install-script timing.** A package's `postinstall` script runs *during* `npm install`. On Claude Code, the Phase 2 hook injects `--ignore-scripts`, so the install is inert and scripts run only after the effect gate confirms the closure (via `npm rebuild`) — a rejected package's scripts never run. On Codex CLI, which does not expose the `updatedInput` hook capability, the install runs normally and a malicious install script can execute once before the post-install reorg cleans up. (The package's *runtime* code is removed before your app runs it on both engines; only install-time lifecycle scripts have this Codex window.)
|
|
244
|
+
|
|
245
|
+
**What it does not stop (current limits):**
|
|
246
|
+
|
|
247
|
+
- A zero-day discovered *after* `approved_at` — only the daily re-check catches it, not the install itself.
|
|
248
|
+
- Compromise of the npm registry itself.
|
|
249
|
+
- An install the user explicitly waved through with `--allow-unverified` (observable, logged).
|
|
250
|
+
- An attacker writing to `~/.safedeps/approved-specs/` directly under the same OS user. The ledger is a local convenience cache; until signing/HMAC or install-time re-validation is added, it is not a security boundary against a same-user attacker. (The effect gate's OSV re-query does, however, still catch a forged approval for a *known-vulnerable* package — see [`ROADMAP.md`](./ROADMAP.md) "Ledger tamper resistance".)
|
|
369
251
|
|
|
370
252
|
---
|
|
371
253
|
|
|
372
|
-
## 6. Provider
|
|
254
|
+
## 6. Provider failure modes (no silent fallback)
|
|
373
255
|
|
|
374
256
|
```
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
│ GHSA / NVD 응답 무 │
|
|
388
|
-
│ ──────────────────── │
|
|
389
|
-
│ • enrichment 결이라 fail-open 허용 │
|
|
390
|
-
│ • OSV 결과로만 진행 + log 에 "GHSA cross-check skipped" │
|
|
391
|
-
└────────────────────────────────────────────────────────────────────┘
|
|
257
|
+
OSV.dev — no response / timeout
|
|
258
|
+
• first: use the local provider cache (24h TTL)
|
|
259
|
+
• cache miss → fail-closed (block; "no OSV response, retry")
|
|
260
|
+
• bypass only with explicit --allow-unverified, and it is logged
|
|
261
|
+
|
|
262
|
+
CISA KEV — no response
|
|
263
|
+
• KEV is a static catalog downloaded once a day; only the local cache is used
|
|
264
|
+
• warn when it is more than 24h stale
|
|
265
|
+
|
|
266
|
+
GHSA / NVD — no response
|
|
267
|
+
• enrichment only, so fail-open is allowed
|
|
268
|
+
• proceed on OSV alone and log "GHSA cross-check skipped"
|
|
392
269
|
```
|
|
393
270
|
|
|
394
|
-
|
|
271
|
+
Design principle: **no silent fallback.** Every bypass is observable and logged. When the canonical truth (OSV) cannot answer, the default is fail-closed.
|
|
395
272
|
|
|
396
273
|
---
|
|
397
274
|
|
|
398
|
-
## 7. State
|
|
275
|
+
## 7. State layout — `~/.safedeps/`
|
|
399
276
|
|
|
400
277
|
```
|
|
401
278
|
~/.safedeps/
|
|
402
|
-
├── approved-specs/
|
|
403
|
-
│ ├── sha256-abc123.json
|
|
404
|
-
│
|
|
405
|
-
|
|
406
|
-
│
|
|
407
|
-
|
|
408
|
-
|
|
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
|
-
│
|
|
279
|
+
├── approved-specs/ ← ledger SSoT, one JSON file per (ecosystem, package, version)
|
|
280
|
+
│ ├── sha256-abc123.json
|
|
281
|
+
│ └── …
|
|
282
|
+
├── snapshots/ ← reorg snapshots (inherited from v1, extended to all lockfiles)
|
|
283
|
+
│ └── <id>/ { package-lock.json, yarn.lock, pnpm-lock.yaml, poetry.lock, uv.lock,
|
|
284
|
+
│ Cargo.lock, go.sum, Gemfile.lock, meta.json }
|
|
285
|
+
├── confirmed_${dir_hash} ← per-project last confirmed snapshot
|
|
422
286
|
├── cache/
|
|
423
|
-
│ ├── osv/
|
|
424
|
-
│
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
├── locks/ ← atomic state (TOCTOU 방지)
|
|
429
|
-
├── reorg.log ← REORG event 기록 (append-only)
|
|
430
|
-
└── advisory.log ← advisory gate event 기록 (block/approve)
|
|
287
|
+
│ ├── osv/ ← OSV query responses (24h TTL)
|
|
288
|
+
│ └── kev/ ← CISA KEV daily catalog
|
|
289
|
+
├── locks/ ← atomic state (TOCTOU guard)
|
|
290
|
+
├── reorg.log ← reorg events (append-only)
|
|
291
|
+
└── advisory.log ← advisory-gate decisions (approve / block)
|
|
431
292
|
```
|
|
432
293
|
|
|
433
|
-
|
|
434
|
-
- `
|
|
435
|
-
- `
|
|
436
|
-
- `
|
|
437
|
-
- `advisory.log` = advisory gate 의 모든 approve/block decision audit trail.
|
|
294
|
+
- `approved-specs/` is the ledger SSoT, one atomic JSON write per spec.
|
|
295
|
+
- `snapshots/` keeps the v1 design plus the Python/Rust/Go/Ruby lockfiles.
|
|
296
|
+
- `cache/osv/` and `cache/kev/` hold provider responses under TTL.
|
|
297
|
+
- `advisory.log` is the audit trail of every approve/block decision.
|
|
438
298
|
|
|
439
299
|
---
|
|
440
300
|
|
|
441
|
-
## 8. Multi-
|
|
301
|
+
## 8. Multi-ecosystem support
|
|
442
302
|
|
|
443
|
-
| Ecosystem | Manifest | Lockfile | safedeps check
|
|
303
|
+
| Ecosystem | Manifest | Lockfile | `safedeps check` |
|
|
444
304
|
|---|---|---|---|
|
|
445
305
|
| npm | `package.json` | `package-lock.json` | `safedeps check npm <pkg>@<range>` |
|
|
446
|
-
| yarn | `package.json` | `yarn.lock` | `safedeps check npm <pkg>@<range>`
|
|
306
|
+
| yarn | `package.json` | `yarn.lock` | `safedeps check npm <pkg>@<range>` |
|
|
447
307
|
| pnpm | `package.json` | `pnpm-lock.yaml` | `safedeps check npm <pkg>@<range>` |
|
|
448
308
|
| pip (Poetry) | `pyproject.toml` | `poetry.lock` | `safedeps check pypi <pkg>@<range>` |
|
|
449
309
|
| pip (uv) | `pyproject.toml` | `uv.lock` | `safedeps check pypi <pkg>@<range>` |
|
|
450
310
|
| pip (Pipenv) | `Pipfile` | `Pipfile.lock` | `safedeps check pypi <pkg>@<range>` |
|
|
451
|
-
| pip (raw) | `requirements.txt` | (
|
|
311
|
+
| pip (raw) | `requirements.txt` | (weak) | `safedeps check pypi <pkg>@<range>` |
|
|
452
312
|
| cargo | `Cargo.toml` | `Cargo.lock` | `safedeps check crates.io <pkg>@<range>` |
|
|
453
313
|
| go | `go.mod` | `go.sum` | `safedeps check go <pkg>@<range>` |
|
|
454
314
|
| ruby | `Gemfile` | `Gemfile.lock` | `safedeps check rubygems <pkg>@<range>` |
|
|
455
|
-
| maven | `pom.xml` | (
|
|
315
|
+
| maven | `pom.xml` | (directory) | `safedeps check maven <group>:<artifact>@<range>` |
|
|
456
316
|
| nuget | `*.csproj` | `packages.lock.json` | `safedeps check nuget <pkg>@<range>` |
|
|
457
317
|
|
|
458
|
-
|
|
318
|
+
OSV normalizes ecosystem names, so one API path covers all of them at advisory-check time. Per-ecosystem typosquat lists and install-script risk patterns live in separate static lists. Note that the npm effect gate (closure-vs-ledger enforcement) is npm-only today; the other ecosystems use the command-gate + reorg model.
|
|
459
319
|
|
|
460
320
|
---
|
|
461
321
|
|
|
462
|
-
## 9.
|
|
322
|
+
## 9. Component responsibilities (SoC)
|
|
463
323
|
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
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
|
-
```
|
|
324
|
+
| Component | Responsibility |
|
|
325
|
+
|---|---|
|
|
326
|
+
| `SKILL.md` | The SSoT the Claude/Codex skill loader reads — hook declarations + advisory-gate usage. |
|
|
327
|
+
| `README.md` | User install guide. |
|
|
328
|
+
| `ARCHITECTURE.md` | This document — internal flow and design. |
|
|
329
|
+
| `bin/safedeps` | CLI entry — advisory check, ledger management, re-check, migrate. |
|
|
330
|
+
| `scripts/safedeps-pre-guard.sh` | PreToolUse hook — ledger match + v1 hardcoded patterns + snapshots. |
|
|
331
|
+
| `scripts/safedeps-post-verify.sh` | PostToolUse hook — closure-vs-ledger effect gate + reorg. |
|
|
332
|
+
| `lib/providers/` | OSV / KEV / GHSA (and optional NVD / deps.dev / Snyk) adapters behind one query interface. |
|
|
333
|
+
| `lib/ledger/` | Approved-spec ledger I/O — atomic write, hashing, TTL checks. |
|
|
334
|
+
| `lib/npm/closure.sh` | npm closure resolution from a lockfile. |
|
|
489
335
|
|
|
490
336
|
---
|
|
491
337
|
|
|
492
|
-
## 10.
|
|
338
|
+
## 10. How safedeps differs from existing tools
|
|
493
339
|
|
|
494
|
-
|
|
|
340
|
+
| Tool | Focus | When | Difference from safedeps |
|
|
495
341
|
|---|---|---|---|
|
|
496
|
-
| `npm audit` |
|
|
497
|
-
| `pip-audit` / `cargo audit` / `bundler-audit` |
|
|
498
|
-
|
|
|
499
|
-
|
|
|
500
|
-
|
|
|
501
|
-
|
|
|
502
|
-
|
|
|
503
|
-
|
|
|
504
|
-
| **`safedeps
|
|
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) 만**.
|
|
342
|
+
| `npm audit` | report vulns from the materialized lock | post-install | reports only; no spec decision or blocking |
|
|
343
|
+
| `pip-audit` / `cargo audit` / `bundler-audit` | same, other ecosystems | post-install | same |
|
|
344
|
+
| socket.dev | SaaS risk intelligence (behavioral + static) | pre/post-install | cloud-dependent, free-quota limited, external SaaS |
|
|
345
|
+
| lavamoat | runtime permission sandbox | runtime | no pre-install block; heavy on the dev loop |
|
|
346
|
+
| pnpm `onlyBuiltDependencies` | lifecycle-script allowlist | install | no typosquat/vuln DB; script blocking only |
|
|
347
|
+
| deps.dev | package graph metadata | query only | data, not an active gate |
|
|
348
|
+
| OSV-Scanner | OSV scan of a lockfile | post-install (CI) | reports the lockfile; no spec gate |
|
|
349
|
+
| GitHub Dependabot | PR-based dep updates | repo (PR) | no local install block; PR stage only |
|
|
350
|
+
| **`safedeps`** | **advisory check + approved-spec ledger + npm effect gate + reorg** | **pre/install/post** | **closure-level enforcement, multi-ecosystem command guard, local-first** |
|
|
351
|
+
|
|
352
|
+
In short: other tools focus on one of "report," "sandbox," "script-block," or "PR suggestion." safedeps layers advisory check → fast command guard → npm effect gate + reorg into defense-in-depth, and — unlike Snyk or socket.dev — depends on no SaaS, only the local CLI plus public databases (OSV / KEV / GHSA).
|
|
510
353
|
|
|
511
354
|
---
|
|
512
355
|
|
|
513
|
-
## 11.
|
|
356
|
+
## 11. Operational logs
|
|
514
357
|
|
|
515
358
|
```bash
|
|
516
|
-
# advisory
|
|
517
|
-
tail -f ~/.safedeps/
|
|
518
|
-
|
|
519
|
-
#
|
|
520
|
-
|
|
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/
|
|
359
|
+
tail -f ~/.safedeps/advisory.log # advisory-gate decisions (approve / block)
|
|
360
|
+
tail -f ~/.safedeps/reorg.log # reorg events
|
|
361
|
+
ls -lt ~/.safedeps/approved-specs/ # current approved specs
|
|
362
|
+
jq '.evidence' ~/.safedeps/approved-specs/sha256-abc123.json # one spec's evidence
|
|
363
|
+
rm -rf ~/.safedeps/cache/osv/ # clear the OSV cache (force re-query)
|
|
533
364
|
```
|
|
534
365
|
|
|
535
366
|
---
|
|
536
367
|
|
|
537
|
-
## 12. v1 → v2
|
|
368
|
+
## 12. Legacy / migration: v1 `npm-reorg-guard` → v2
|
|
538
369
|
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
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
|
-
```
|
|
370
|
+
| v1 (`npm-reorg-guard`) | v2 (`safedeps`) |
|
|
371
|
+
|---|---|
|
|
372
|
+
| `~/.npm-reorg-guard/` | `~/.safedeps/` |
|
|
373
|
+
| `~/.claude/skills/npm-reorg-guard/` | `~/.claude/skills/safedeps/` |
|
|
374
|
+
| `scripts/guard.sh` (pattern match only) | `scripts/safedeps-pre-guard.sh` (+ ledger lookup, namespaced) |
|
|
375
|
+
| `scripts/verify.sh` (lockfile diff + reorg) | `scripts/safedeps-post-verify.sh` (+ approved-spec diff, namespaced) |
|
|
376
|
+
| — | `bin/safedeps` — new CLI (check / approve / revoke / re-check / ledger) |
|
|
377
|
+
| — | `lib/providers/`, `lib/ledger/` |
|
|
378
|
+
| GitHub `aldegad/npm-reorg-guard` | `aldegad/safedeps` (redirect only) |
|
|
569
379
|
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
- v1
|
|
573
|
-
-
|
|
380
|
+
Migration:
|
|
381
|
+
|
|
382
|
+
- The v1 hook path (`~/.claude/skills/npm-reorg-guard/scripts/*.sh`) is not canonical; settings point at `~/.claude/skills/safedeps/scripts/*.sh`.
|
|
383
|
+
- When a `~/.npm-reorg-guard/` directory is found, its state migrates to `~/.safedeps/` (snapshot chain preserved).
|
|
384
|
+
- A v1 user runs `safedeps migrate` once: it creates the ledger and carries existing confirmed snapshots over.
|
|
574
385
|
|
|
575
386
|
---
|
|
576
387
|
|
|
577
|
-
## 13.
|
|
388
|
+
## 13. Limits and future direction
|
|
578
389
|
|
|
579
|
-
|
|
580
|
-
- approved_at 이후 발견된 zero-day 는 daily re-check 로만 catch.
|
|
581
|
-
- npm/PyPI 같은 registry 자체 손상 시 우리도 못 막음.
|
|
582
|
-
- KEV 는 24h 단위 update — 그 사이 새 KEV 등재 못 잡음.
|
|
583
|
-
- transitive dep 의 vuln 검사는 ledger 폭증 가능 (수백 개 dep). 최적화 필요.
|
|
390
|
+
**Current limits:**
|
|
584
391
|
|
|
585
|
-
|
|
586
|
-
-
|
|
587
|
-
-
|
|
588
|
-
-
|
|
589
|
-
- **multi-machine ledger sync** — 팀 차원의 approved spec 공유.
|
|
590
|
-
- **deps.dev graph 활용** — transitive dep 의 \"위험 score\" 시각화.
|
|
591
|
-
- **AI agent integration** — Claude / Codex 가 \"이 패키지 알려진 vuln 있음, 대체 모듈 X 권장\" 결로 직접 제안.
|
|
392
|
+
- A zero-day discovered after `approved_at` is caught only by the daily re-check.
|
|
393
|
+
- A compromise of the registry itself (npm/PyPI/…) is out of reach.
|
|
394
|
+
- KEV updates once a day; a KEV listed in between is not caught until the next refresh.
|
|
395
|
+
- Transitive-closure checking can grow the ledger to hundreds of entries; this needs optimization.
|
|
592
396
|
|
|
593
|
-
|
|
397
|
+
**Future direction** (see [`ROADMAP.md`](./ROADMAP.md)):
|
|
594
398
|
|
|
595
|
-
|
|
399
|
+
- Effect-based closure enforcement for the non-npm ecosystems.
|
|
400
|
+
- Ledger tamper resistance (OSV-as-authority + tamper detection; no local signing).
|
|
401
|
+
- Plugin providers, a `.safedeps.toml` policy file, CI mode, multi-machine ledger sync, and agent-suggested safe replacements.
|