@aldegad/safedeps 2.5.0 → 2.5.1
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/README.ko.md +26 -8
- package/README.md +28 -6
- package/bin/safedeps +1 -1
- package/package.json +1 -1
- package/scripts/test/e2e.sh +51 -1
package/README.ko.md
CHANGED
|
@@ -1,18 +1,28 @@
|
|
|
1
|
-
#
|
|
1
|
+
# safedeps
|
|
2
2
|
|
|
3
|
-
>
|
|
3
|
+
> **AI 코딩 에이전트가 취약하거나 미승인된 의존성을 설치하지 못하게 막고, 빠져나간 건 롤백한다.**
|
|
4
4
|
>
|
|
5
|
-
>
|
|
5
|
+
> `safedeps` 는 Claude Code·Codex CLI 에이전트가 실행하는 모든 의존성 install 을 게이트한다. 패키지를 OSV / CISA KEV / GitHub Advisory 로 사전 승인하고, 실제로 lockfile 에 깔린 closure 를 다시 검증하며, 어긋난 건 자동으로 롤백한다. 전부 로컬에서 돌고 런타임 의존성은 0.
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
- **사전 승인** — 모든 `pkg@version` 과 (npm 은) 그 전체 transitive closure 를 설치 *전에* OSV(정본)·CISA KEV·GitHub Advisory 로 검사한다.
|
|
8
|
+
- **실제 effect 강제** — 설치 후 실제 `package-lock.json` closure 를 다시 확인하므로, 래핑·난독화된 명령도 게이트를 못 빠져나간다.
|
|
9
|
+
- **롤백** — 미승인·신규 취약 패키지는 마지막 확정 안전 snapshot 으로 되돌린다. Claude Code 에서는 install 이 inert(`--ignore-scripts`)로 돌아 거부된 패키지의 lifecycle script 가 아예 실행되지 않는다.
|
|
8
10
|
|
|
9
|
-
|
|
11
|
+
## Quickstart
|
|
10
12
|
|
|
11
|
-
|
|
13
|
+
```bash
|
|
14
|
+
# 1. CLI 설치 — npm 패키지는 scoped, @aldegad/ 접두사 주의
|
|
15
|
+
npm install -g @aldegad/safedeps
|
|
12
16
|
|
|
13
|
-
|
|
17
|
+
# 2. Claude Code / Codex 에 hook 연결 (idempotent)
|
|
18
|
+
cd "$(npm root -g)/@aldegad/safedeps" && node scripts/install/install-safedeps-hooks.mjs
|
|
14
19
|
|
|
15
|
-
|
|
20
|
+
# 3. 끝 — 이제 에이전트가 실행하는 모든 의존성 install 이 자동으로 게이트된다.
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
> `safedeps` 는 CLI 명령어이고, npm 패키지는 **`@aldegad/safedeps`** 다 — npm 의 unscoped `safedeps` 는 무관한 남의 패키지. 전체 skill 소스 트리를 원하면 [설치](#설치) 참고.
|
|
24
|
+
|
|
25
|
+
*Detailed reference → [README.md](./README.md) (영문, SSoT)*
|
|
16
26
|
|
|
17
27
|
---
|
|
18
28
|
|
|
@@ -193,6 +203,14 @@ node scripts/install/install-safedeps-recheck-agent.mjs install --hour 9 --minut
|
|
|
193
203
|
|
|
194
204
|
---
|
|
195
205
|
|
|
206
|
+
## "reorg" 는 뭐고 왜 그 비유인가
|
|
207
|
+
|
|
208
|
+
블록체인에서 **reorg (재편성)** 은 미확정 블록 시퀀스를 무효화하고 마지막 확정된 안전 상태로 체인을 되돌린다. `safedeps` 는 모든 install 을 똑같이 본다 — 일련의 공급망 보안 검사를 통과하기 전까지는 미확정 블록 후보다. 설치된 effect 가 어긋나면 도구가 **reorg** 를 수행한다 — lock 파일, `package.json`, `node_modules` 를 마지막 확정된 안전 snapshot 으로 되돌린다.
|
|
209
|
+
|
|
210
|
+
하지만 reorg 는 **최전선이 아니라 backstop 이다.** 나쁜 install 의 대부분은 여기까지 오지도 않는다 — 사전 승인 게이트가 미승인·플래그된 패키지를 실행 전에 *거부* 하고, Claude Code 에서는 install 이 **inert (`--ignore-scripts`)** 로 돌아 closure 가 깨끗하다고 검증되기 전까지 lifecycle script 가 실행되지 않는다. reorg 가 발동하는 건 잔여 케이스뿐이다 — 승인된 직접 패키지가 미승인·취약 transitive 를 끌어오거나, 래핑된 명령이 advisory 계층을 빠져나간 경우 — 그리고 그때조차 실행되지도 못한 파일을 되돌린다.
|
|
211
|
+
|
|
212
|
+
빠른 advisory 피드백, 관측 가능한 rollback, silent fallback 없음. command guard 는 best-effort UX 이고, 설치 결과 effect 가 backstop 이다.
|
|
213
|
+
|
|
196
214
|
## 다른 도구와 뭐가 다른가
|
|
197
215
|
|
|
198
216
|
`safedeps` 는 **AI 에이전트가 코딩 중 install 명령을 작성하는 순간**에 끼어드는 도구다. CI 스캔, PR 권장, runtime sandbox 처럼 다른 시점에서 동작하는 도구들과 핵심 차별점이 여기 있다.
|
package/README.md
CHANGED
|
@@ -1,14 +1,28 @@
|
|
|
1
|
-
#
|
|
1
|
+
# safedeps
|
|
2
2
|
|
|
3
|
-
> **
|
|
3
|
+
> **Stop your AI coding agent from installing vulnerable or unapproved dependencies — and roll back the ones that slip through.**
|
|
4
4
|
>
|
|
5
|
-
>
|
|
5
|
+
> `safedeps` gates every dependency install your Claude Code or Codex CLI agent runs. It pre-approves packages against OSV / CISA KEV / GitHub Advisory, re-verifies the closure that actually lands in your lockfile, and auto-rolls-back anything that diverges. Local-only, with zero runtime dependencies. *(한국어 README → [README.ko.md](./README.ko.md))*
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
- **Pre-approve** — every `pkg@version`, plus its full transitive closure for npm, is cleared against OSV (canonical), CISA KEV, and GitHub Advisory *before* it installs.
|
|
8
|
+
- **Enforce the real effect** — after the install, the actual `package-lock.json` closure is re-checked, so a wrapped or obfuscated command can't sneak a package past the gate.
|
|
9
|
+
- **Roll back** — anything unapproved or newly-vulnerable is reverted to the last confirmed safe snapshot. On Claude Code the install runs inert (`--ignore-scripts`), so a rejected package's lifecycle scripts never run.
|
|
8
10
|
|
|
9
|
-
|
|
11
|
+
## Quickstart
|
|
10
12
|
|
|
11
|
-
|
|
13
|
+
```bash
|
|
14
|
+
# 1. Install the CLI — the npm package is scoped, note the @aldegad/ prefix
|
|
15
|
+
npm install -g @aldegad/safedeps
|
|
16
|
+
|
|
17
|
+
# 2. Wire the hooks into Claude Code / Codex (idempotent)
|
|
18
|
+
cd "$(npm root -g)/@aldegad/safedeps" && node scripts/install/install-safedeps-hooks.mjs
|
|
19
|
+
|
|
20
|
+
# 3. Done — every dependency install your agent runs is now gated.
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
> `safedeps` is the CLI command; the npm package is **`@aldegad/safedeps`** — the unscoped `safedeps` on npm is an unrelated package. Prefer the full skill source tree? See [Installation](#installation).
|
|
24
|
+
|
|
25
|
+
<!-- TODO(demo): add a 15-20s asciinema/VHS recording of safedeps catching a malicious install live (inert -> reorg). Highest-leverage conversion asset per launch review. -->
|
|
12
26
|
|
|
13
27
|
## Distribution Model
|
|
14
28
|
|
|
@@ -115,6 +129,14 @@ After the install command completes, the verify hook analyzes what changed. For
|
|
|
115
129
|
4. The event is logged to `~/.safedeps/reorg.log`.
|
|
116
130
|
5. Claude Code receives a system message detailing the detected threats and rollback actions.
|
|
117
131
|
|
|
132
|
+
## Why "reorg"?
|
|
133
|
+
|
|
134
|
+
The name borrows from blockchain, where a **reorganization (reorg)** invalidates a sequence of unconfirmed blocks and reverts the chain to its last confirmed safe state. `safedeps` treats every install the same way: an unconfirmed block candidate until it passes a battery of supply-chain checks. If the installed effect diverges, the tool performs a **reorg** -- rolling the lock file, `package.json`, and `node_modules` back to the last confirmed safe snapshot.
|
|
135
|
+
|
|
136
|
+
But the reorg is the **backstop, not the front line.** Most bad installs never reach it: the pre-approval gate *denies* an unapproved or flagged package before it runs, and on Claude Code the install runs **inert** (`--ignore-scripts`) so lifecycle scripts do not execute until the closure verifies clean. The reorg fires for the residual case -- an approved direct package that pulls in an unapproved or vulnerable transitive, or a wrapped command that slips past the advisory layer -- and even then it rolls back files that never got to run.
|
|
137
|
+
|
|
138
|
+
Fast advisory feedback, observable rollback, and no hidden fallback. The command guard is best-effort UX; the installed effect is the backstop.
|
|
139
|
+
|
|
118
140
|
## The Blockchain Analogy
|
|
119
141
|
|
|
120
142
|
| Blockchain Concept | Safedeps Equivalent |
|
package/bin/safedeps
CHANGED
package/package.json
CHANGED
package/scripts/test/e2e.sh
CHANGED
|
@@ -190,6 +190,50 @@ EOF
|
|
|
190
190
|
grep -qx 'rebuild' "${tmp_root}/npm-calls.log" || fail "post hook runs npm rebuild after verified injected install"
|
|
191
191
|
pass "post hook rebuilds after verified inert install"
|
|
192
192
|
|
|
193
|
+
# Reorg must actually revert the on-disk lockfile, not just print the message. The
|
|
194
|
+
# missing-transitive test below proves the systemMessage; this proves the stronger
|
|
195
|
+
# claim — a tampered lockfile is restored byte-for-byte to the last confirmed safe
|
|
196
|
+
# snapshot on disk. Regression guard so a future change cannot break the rollback
|
|
197
|
+
# while keeping the message green. Stub npm keeps `npm ci` from rewriting the file.
|
|
198
|
+
revert_project="${tmp_root}/revert-project"
|
|
199
|
+
mkdir -p "${revert_project}"
|
|
200
|
+
printf '{"dependencies":{"fixture-parent":"1.0.0"}}\n' > "${revert_project}/package.json"
|
|
201
|
+
cat > "${revert_project}/package-lock.json" <<'EOF'
|
|
202
|
+
{
|
|
203
|
+
"name": "revert-project",
|
|
204
|
+
"lockfileVersion": 3,
|
|
205
|
+
"packages": {
|
|
206
|
+
"": {"dependencies": {"fixture-parent": "1.0.0"}},
|
|
207
|
+
"node_modules/fixture-parent": {"version": "1.0.0", "dependencies": {"fixture-child": "1.0.0"}},
|
|
208
|
+
"node_modules/fixture-child": {"version": "1.0.0"}
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
EOF
|
|
212
|
+
cp "${revert_project}/package-lock.json" "${tmp_root}/revert-safe-lock.json"
|
|
213
|
+
scripts/safedeps-pre-guard.sh > /dev/null <<EOF
|
|
214
|
+
{"tool_name":"Bash","tool_input":{"command":"npm install fixture-parent@1.0.0"},"cwd":"${revert_project}"}
|
|
215
|
+
EOF
|
|
216
|
+
cat > "${revert_project}/package-lock.json" <<'EOF'
|
|
217
|
+
{
|
|
218
|
+
"name": "revert-project",
|
|
219
|
+
"lockfileVersion": 3,
|
|
220
|
+
"packages": {
|
|
221
|
+
"": {"dependencies": {"fixture-parent": "1.0.0"}},
|
|
222
|
+
"node_modules/fixture-parent": {"version": "1.0.0", "dependencies": {"fixture-child": "1.0.0"}},
|
|
223
|
+
"node_modules/fixture-child": {"version": "1.0.0"},
|
|
224
|
+
"node_modules/fixture-evil": {"version": "6.6.6", "resolved": "git://evil.example.com/fixture-evil.git"}
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
EOF
|
|
228
|
+
revert_post=$(
|
|
229
|
+
PATH="${stub_bin}:${PATH}" scripts/safedeps-post-verify.sh <<EOF
|
|
230
|
+
{"tool_name":"Bash","tool_input":{"command":"npm install fixture-parent@1.0.0"},"cwd":"${revert_project}"}
|
|
231
|
+
EOF
|
|
232
|
+
)
|
|
233
|
+
grep -q '의심스러운 패키지 변경 감지' <<< "${revert_post}" || fail "reorg fires on a tampered lockfile"
|
|
234
|
+
cmp -s "${revert_project}/package-lock.json" "${tmp_root}/revert-safe-lock.json" || fail "reorg restores the exact safe lockfile content on disk"
|
|
235
|
+
pass "reorg reverts a tampered lockfile to safe content on disk"
|
|
236
|
+
|
|
193
237
|
export SAFEDEPS_HOME="${tmp_root}/safe-missing-transitive"
|
|
194
238
|
export SAFEDEPS_OSV_API_URL="http://127.0.0.1:${port}/osv/v1/query"
|
|
195
239
|
export SAFEDEPS_OSV_BATCH_API_URL="http://127.0.0.1:${port}/osv/v1/querybatch"
|
|
@@ -225,7 +269,13 @@ EOF
|
|
|
225
269
|
)
|
|
226
270
|
grep -q '의심스러운 패키지 변경 감지' <<< "${missing_post}" || fail "post hook reorgs unapproved transitive package"
|
|
227
271
|
grep -q 'fixture-child@1.0.0' <<< "${missing_post}" || fail "post hook names unapproved transitive package"
|
|
228
|
-
|
|
272
|
+
# Not just the message — the unapproved transitive must be gone from the on-disk
|
|
273
|
+
# lockfile. (Reorg removes the tampered lockfile; a no-network reinstall may recreate
|
|
274
|
+
# an empty one, so assert fixture-child is absent rather than the file itself.)
|
|
275
|
+
if grep -q 'fixture-child' "${missing_project}/package-lock.json" 2>/dev/null; then
|
|
276
|
+
fail "post hook reorg leaves the unapproved transitive in the on-disk lockfile"
|
|
277
|
+
fi
|
|
278
|
+
pass "post hook reorgs unapproved transitive package (verified on disk)"
|
|
229
279
|
|
|
230
280
|
export SAFEDEPS_HOME="${tmp_root}/safe"
|
|
231
281
|
export SAFEDEPS_OSV_API_URL="http://127.0.0.1:${port}/osv/v1/query"
|