@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/README.ko.md
ADDED
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
# Safedeps
|
|
2
|
+
|
|
3
|
+
> **모든 install 을 미확정 블록으로 본다 — `safedeps` 는 안전한 것만 승인하고, 그렇지 않으면 reorg 한다.**
|
|
4
|
+
>
|
|
5
|
+
> OSV / CISA KEV / GitHub Advisory 로 의존성 spec 을 사전 승인하고, Claude Code 와 Codex CLI 의 hook 에서 실제 설치 closure 를 강제하며, 승인과 어긋난 install 은 자동으로 마지막 안전 snapshot 으로 롤백한다.
|
|
6
|
+
|
|
7
|
+
*Detailed reference → [README.md](./README.md) (영문, SSoT)*
|
|
8
|
+
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
## "reorg" 는 뭐고 왜 그 비유인가
|
|
12
|
+
|
|
13
|
+
블록체인에서 **reorg (재편성)** 은 미확정 블록 시퀀스를 무효화하고 마지막 확정된 안전 상태로 체인을 되돌린다. `safedeps` 는 같은 원리를 `node_modules` 에 적용한다 — 모든 install 은 일련의 공급망 보안 검사를 통과하기 전까지는 **미확정 블록 후보** 로 취급된다. 의심스러우면 도구가 **reorg** 를 수행한다 — lock 파일, `package.json`, `node_modules` 를 마지막 확정된 안전 snapshot 으로 되돌린다.
|
|
14
|
+
|
|
15
|
+
빠른 advisory 피드백, 관측 가능한 rollback, silent fallback 없음. command guard 는 best-effort UX 이고, 설치 결과 effect 가 backstop 이다.
|
|
16
|
+
|
|
17
|
+
---
|
|
18
|
+
|
|
19
|
+
## 두 lane
|
|
20
|
+
|
|
21
|
+
`safedeps` 는 두 보안 lane 을 소유한다 (전체 설계: [`ARCHITECTURE.md`](./ARCHITECTURE.md) §1):
|
|
22
|
+
|
|
23
|
+
- **install-time** (이 README 의 초점) — advisory check + approved-spec ledger + 빠른 PreToolUse guard + PostToolUse effect enforcement + post-install reorg. 패키지 단위, install 명령과 실제 lockfile effect 주변.
|
|
24
|
+
- **release-time** — `safedeps gates run`, `safedeps scan secrets [--repo|--worktree|--staged]`, `safedeps audit npm`, `safedeps hooks install|check`. repo 트리 secret scan, 의존성 audit, repo-local git hook 설치/검사 (push/release 전). repo-specific policy(gitleaks config, privacy 경로)는 대상 repo 에 남고 safedeps 는 실행 owner. *(옛 `security-release-gates` 흡수.)*
|
|
25
|
+
|
|
26
|
+
---
|
|
27
|
+
|
|
28
|
+
## 어떻게 동작
|
|
29
|
+
|
|
30
|
+
`safedeps` 는 모든 install 을 두 동작으로 감싼다:
|
|
31
|
+
|
|
32
|
+
- **설치 전** — `safedeps check` 가 패키지를 OSV(canonical) + CISA KEV + GitHub Advisory 로 검사하고 로컬 ledger 에 승인을 기록한다. npm 은 패키지의 전체 의존성 closure 를 풀어 딸린 모든 transitive 패키지까지 검사한다.
|
|
33
|
+
- **설치 후** — PostToolUse hook 이 실제 `package-lock.json` 에 뭐가 깔렸는지 다시 읽고, ledger 에 없거나 advisory DB 가 새로 취약하다고 답하는 패키지를 reorg(롤백)한다.
|
|
34
|
+
|
|
35
|
+
설치 전 command hook(PreToolUse)은 빠른 advisory 넛지다 — 명백한 미승인 install 과 위험한 명령 형태를 막아 에이전트에게 즉시 피드백을 준다. 하지만 npm 의 진짜 권위는 설치 후 effect gate 다: 명령이 *어떻게 생겼는지* 가 아니라 *실제로 뭐가 깔렸는지* 를 보기 때문에, wrapper 나 난독화된 install 명령으로도 패키지를 통과시킬 수 없다.
|
|
36
|
+
|
|
37
|
+
**스크립트 안전 (무실행 설치).** Claude Code 에서는 PreToolUse hook 이 npm install 에 `--ignore-scripts` 를 붙여 rewrite 한다 — 그래서 설치가 **무실행(inert)** 으로 돈다(패키지는 디스크에 앉되 lifecycle script 는 아직 안 돎). 그다음 effect gate 가 closure 를 검증하고, 통과했을 때만 PostToolUse 가 `npm rebuild` 로 이제서야 검증된 스크립트를 실행한다. 게이트가 거부한 패키지는 *스크립트가 한 번도 돌기 전에* reorg 된다. (Claude Code 의 hook `updatedInput` 기능을 쓴다. Codex CLI 는 이 기능이 없어 Codex 에서는 install 이 정상 실행되고 effect gate 는 detect-and-rollback 이다 — 악성 install script 가 롤백 전에 1회 실행될 수 있다.)
|
|
38
|
+
|
|
39
|
+
이 effect-primary 모델은 현재 npm 한정이다. `pip`, `cargo`, `go`, `gem`, `maven`, `nuget` 은 closure resolver 가 붙기 전까지 v2.1 command-gate + reorg 모델을 유지한다.
|
|
40
|
+
|
|
41
|
+
```
|
|
42
|
+
PreToolUse PostToolUse
|
|
43
|
+
(safedeps-pre-guard.sh) (safedeps-post-verify.sh)
|
|
44
|
+
| |
|
|
45
|
+
install cmd ──> [ Advisory / ledger UX ] ──> [ Execute ] ──> [ npm effect gate ]
|
|
46
|
+
| | | |
|
|
47
|
+
명백한 miss/ lock / manifest 깨끗? 의심?
|
|
48
|
+
risk block snapshot | |
|
|
49
|
+
Confirm REORG
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
### 3 단계 구성
|
|
53
|
+
|
|
54
|
+
**① Advisory check (`safedeps check`)** — `safedeps check <ecosystem> <pkg>@<range>` 가 OSV(canonical) + CISA KEV(hard-risk overlay) + GHSA(enrichment) 를 조회한다. npm 은 temp dir 에서 `npm install --package-lock-only --ignore-scripts` 로 스크립트 실행 없는 lockfile 을 만들어 전체 closure 를 뽑고 OSV `/v1/querybatch` 로 묶어 검사한다. 깨끗하면 direct spec 과 `transitive_specs` 를 `~/.safedeps/approved-specs/<hash>.json` 에 30일 TTL 로 기록한다.
|
|
55
|
+
|
|
56
|
+
**② 빠른 command guard (`safedeps-pre-guard.sh`, PreToolUse)** — install 명령의 `pkg@version` 토큰을 ledger 와 대조하고 lockfile/manifest 를 snapshot 한다. 미승인이면 차단하고, 다음에 실행할 `safedeps check ...` 명령을 차단 사유에 박아준다 (PATH 에 있으면 `safedeps`, 없으면 절대경로). 에이전트는 그 메시지로 check → 재시도하면 된다. 이 단계는 빠른 advisory/UX 일 뿐 최종 권위는 아니다.
|
|
57
|
+
|
|
58
|
+
**③ Effect gate + reorg (`safedeps-post-verify.sh`, PostToolUse)** — 설치 후 실제 `package-lock.json` closure 전체를 ledger 의 direct entry + `transitive_specs` 와 대조하고 OSV batch 로 재확인한다. **npm 에서는 이 effect gate 가 primary 권위다.** 미승인·취약·provider fail-closed, 또는 의심스러운 install script·native binary 가 있으면 마지막 confirmed snapshot 으로 자동 reorg 한다.
|
|
59
|
+
|
|
60
|
+
---
|
|
61
|
+
|
|
62
|
+
## 어떤 명령을 보호하는가
|
|
63
|
+
|
|
64
|
+
| Ecosystem | 매칭하는 명령 |
|
|
65
|
+
|---|---|
|
|
66
|
+
| npm / pnpm / yarn | `npm install`, `npm add`, `pnpm add`, `yarn add`, `npx`, `pnpm dlx`, `yarn dlx` 등 |
|
|
67
|
+
| pip / poetry / uv / pipenv | `pip install`, `poetry add`, `uv add`, `uv pip install`, `pipenv install` |
|
|
68
|
+
| cargo | `cargo add`, `cargo install` |
|
|
69
|
+
| go | `go get`, `go install` |
|
|
70
|
+
| ruby | `gem install`, `bundle add` |
|
|
71
|
+
| maven / nuget | `mvn dependency:get`, `dotnet add package` |
|
|
72
|
+
|
|
73
|
+
---
|
|
74
|
+
|
|
75
|
+
## 설치
|
|
76
|
+
|
|
77
|
+
### 1) GitHub clone (skill 본체 설치, canonical)
|
|
78
|
+
|
|
79
|
+
```bash
|
|
80
|
+
git clone https://github.com/aldegad/safedeps.git
|
|
81
|
+
cd safedeps
|
|
82
|
+
node scripts/install/install-safedeps-hooks.mjs
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
Cross-engine installer 가 `~/.claude/skills/safedeps` 와 `~/.codex/skills/safedeps` 로 심볼릭 링크하고, `~/.claude/settings.json` 과 `~/.codex/hooks.json` 의 PreToolUse / PostToolUse hook 을 idempotent 하게 등록한다. backup-before-write. `--uninstall` 로 제거 가능.
|
|
86
|
+
|
|
87
|
+
### 2) npm 패키지 (CLI 편의 설치)
|
|
88
|
+
|
|
89
|
+
```bash
|
|
90
|
+
npm install -g @aldegad/safedeps
|
|
91
|
+
safedeps version
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
npm 은 표준 `bin` 엔트리로 `safedeps` 를 PATH 에 올린다. **에이전트 skill / hook 등록은 별도** — Claude Code / Codex 에서 자동 enforcement 가 필요하면 설치 후 한 번 더:
|
|
95
|
+
|
|
96
|
+
```bash
|
|
97
|
+
cd "$(npm root -g)/@aldegad/safedeps"
|
|
98
|
+
node scripts/install/install-safedeps-hooks.mjs
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
### 일일 재검증 (macOS LaunchAgent, 옵션)
|
|
102
|
+
|
|
103
|
+
```bash
|
|
104
|
+
node scripts/install/install-safedeps-recheck-agent.mjs install --hour 9 --minute 0
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
매일 1회 `safedeps re-check --json` 을 돌려 ledger 의 모든 승인 spec 을 재조회. LLM 토큰은 안 쓴다 (OSV / CISA / GHSA provider 호출만). 새 CVE / KEV / provider skip 이 발견되면 spec 을 revoke 하고 macOS notification 을 띄운다.
|
|
108
|
+
|
|
109
|
+
---
|
|
110
|
+
|
|
111
|
+
## 실제 supply-chain 공격에 어떻게 대응되나
|
|
112
|
+
|
|
113
|
+
| 사건 | 어떤 체크가 잡는가 |
|
|
114
|
+
|---|---|
|
|
115
|
+
| `event-stream` (2018) — `postinstall` 의 난독화 코드가 암호화폐 지갑 키 유출 | install script 분석 (난독화 + 네트워크 액세스 탐지) |
|
|
116
|
+
| `ua-parser-js` 탈취 (2021) — `preinstall` 이 cryptominer 다운로드·실행 | install script 분석 (네트워크 + 코드 실행) |
|
|
117
|
+
| `colors` / `faker` sabotage (2022) — 비정상적 dep 폭증 | dep explosion 검사 |
|
|
118
|
+
| typosquat 캠페인 (`crossenv`, `babelcli` 등) | pre-flight typosquat 패턴 매칭 |
|
|
119
|
+
| dependency confusion — 사내 패키지명을 public 에 더 높은 버전으로 publish | 비표준 registry 탐지 + 큰 dep 변경 검사 |
|
|
120
|
+
|
|
121
|
+
---
|
|
122
|
+
|
|
123
|
+
## 로그와 snapshot
|
|
124
|
+
|
|
125
|
+
| 경로 | 내용 |
|
|
126
|
+
|---|---|
|
|
127
|
+
| `~/.safedeps/advisory.log` | Advisory gate 의 모든 approve / block 결정 |
|
|
128
|
+
| `~/.safedeps/reorg.log` | Reorg 이벤트 history (timestamp, 사유, 되돌린 파일) |
|
|
129
|
+
| `~/.safedeps/approved-specs/` | 승인된 spec JSON 들 (per hash) |
|
|
130
|
+
| `~/.safedeps/snapshots/` | install 전 lock / manifest snapshot |
|
|
131
|
+
| `~/.safedeps/confirmed_<dir>` | 프로젝트별 마지막 confirmed snapshot id |
|
|
132
|
+
| `~/.safedeps/recheck.log` | 일일 재검증 wrapper 로그 |
|
|
133
|
+
| `~/.safedeps/recheck-alerts.jsonl` | 재검증으로 발견된 새 CVE / KEV / revoke 알람 jsonl |
|
|
134
|
+
|
|
135
|
+
---
|
|
136
|
+
|
|
137
|
+
## 다른 도구와 뭐가 다른가
|
|
138
|
+
|
|
139
|
+
`safedeps` 는 **AI 에이전트가 코딩 중 install 명령을 작성하는 순간**에 끼어드는 도구다. CI 스캔, PR 권장, runtime sandbox 처럼 다른 시점에서 동작하는 도구들과 핵심 차별점이 여기 있다.
|
|
140
|
+
|
|
141
|
+
전형적인 흐름:
|
|
142
|
+
|
|
143
|
+
1. 에이전트가 `npm install foo@1.2.3` 같은 명령을 작성한다.
|
|
144
|
+
2. PreToolUse hook 이 빠른 advisory ledger check 를 수행한다. direct spec 이 없거나 만료됐거나 명백히 위험하면 install 을 **차단**하고, 다음에 실행할 정확한 `safedeps check npm foo@1.2.3` 명령을 reason 에 박아 에이전트에게 돌려준다.
|
|
145
|
+
3. 에이전트가 그 안내를 받아 `safedeps check` 를 호출 → OSV / CISA KEV / GitHub Advisory 조회 → 안전하면 **허용목록 (ledger) 에 박는다**. KEV 매치면 hard-block (override 불가), CVE 에 patch 가 있으면 안전 버전으로 자동 narrow.
|
|
146
|
+
4. 다시 install 시도 → 이번엔 ledger 매치되어 **통과**.
|
|
147
|
+
5. install 후 PostToolUse hook 이 npm primary authority 로 실제 lockfile closure 를 direct ledger / `transitive_specs` / OSV batch 와 대조하고, install script / native binary 를 검증한다. 어긋나면 마지막 안전 snapshot 으로 **자동 reorg**.
|
|
148
|
+
|
|
149
|
+
이 흐름으로 일반적인 package-manager install 명령은 실행 전에 빠른 advisory 피드백을 받고, npm install 은 실행 후 effect-time closure enforcement 를 받는다. 다만 command hook 은 best-effort 명령 휴리스틱이지 sandbox 가 아니다. 비정상 wrapper/interpreter 경로나 같은 사용자 권한으로 로컬 safedeps 상태를 조작하는 공격은 ledger 서명/강화 전까지 trust boundary 밖이다. npm effect-primary 모델은 아직 `pip`, `cargo`, `go`, `gem`, `maven`, `nuget` 에 적용되지 않으며, 이 ecosystem 들은 v2.1 command-gate + reorg 모델을 유지한다. SaaS 의존 없이 로컬 + 공개 DB (OSV / KEV / GHSA) 만 쓴다.
|
|
150
|
+
|
|
151
|
+
---
|
|
152
|
+
|
|
153
|
+
## Legacy / Migration: v1 `npm-reorg-guard`
|
|
154
|
+
|
|
155
|
+
v1 시절 이름이 `npm-reorg-guard` 였고 state 디렉토리가 `~/.npm-reorg-guard/` 였다. v2 로 rename 되면서 state 도 `~/.safedeps/` 로 옮겨야 하는데, 그 1회 이전을 자동화한 명령이 있다:
|
|
156
|
+
|
|
157
|
+
```bash
|
|
158
|
+
safedeps migrate
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
- `~/.npm-reorg-guard/` 가 있으면 snapshot chain / confirmed / log 들을 `~/.safedeps/` 로 복사하고 legacy 디렉토리를 archive 처리.
|
|
162
|
+
- 없으면 no-op (v2 처음 깐 사용자는 무관).
|
|
163
|
+
|
|
164
|
+
---
|
|
165
|
+
|
|
166
|
+
## 라이선스
|
|
167
|
+
|
|
168
|
+
[Apache License 2.0](LICENSE)
|
package/README.md
CHANGED
|
@@ -1,12 +1,14 @@
|
|
|
1
1
|
# Safedeps
|
|
2
2
|
|
|
3
|
-
>
|
|
3
|
+
> **Treat every install as an unconfirmed block — `safedeps` approves the safe ones, reorgs the rest.**
|
|
4
|
+
>
|
|
5
|
+
> Pre-approve dependency installs against OSV / CISA KEV / GitHub Advisory, enforce the installed closure from Claude Code and Codex CLI hooks, and auto-rollback any install that diverges from the approved closure. *(한국어 README → [README.ko.md](./README.ko.md))*
|
|
4
6
|
|
|
5
7
|
## Why "reorg"?
|
|
6
8
|
|
|
7
9
|
In blockchain networks, a **reorganization (reorg)** invalidates a sequence of blocks and reverts the chain to a previously confirmed safe state. `safedeps` applies the same principle to your `node_modules`: every install is treated as an unconfirmed block candidate until it passes a battery of supply-chain security checks. If anything looks wrong, the tool performs a **reorg** -- rolling back lock files, `package.json`, and `node_modules` to the last confirmed safe snapshot.
|
|
8
10
|
|
|
9
|
-
|
|
11
|
+
Fast advisory feedback, observable rollback, and no hidden fallback. The command guard is best-effort UX; the installed effect is the backstop.
|
|
10
12
|
|
|
11
13
|
## Distribution Model
|
|
12
14
|
|
|
@@ -17,19 +19,35 @@ Safedeps has two distribution surfaces:
|
|
|
17
19
|
|
|
18
20
|
Use the GitHub release when you want the full skill/hook source tree as the canonical artifact. Use npm when you mainly want a versioned global CLI.
|
|
19
21
|
|
|
22
|
+
## Two Lanes
|
|
23
|
+
|
|
24
|
+
`safedeps` owns two security lanes (full design in [`ARCHITECTURE.md`](./ARCHITECTURE.md) §1):
|
|
25
|
+
|
|
26
|
+
- **Install-time** (the focus of this README) — advisory check + approved-spec ledger + fast PreToolUse guard + PostToolUse effect enforcement + post-install reorg. Per-package, around the install command and its actual lockfile effect.
|
|
27
|
+
- **Release-time** — `safedeps gates run`, `safedeps scan secrets [--repo|--worktree|--staged]`, `safedeps audit npm`, `safedeps hooks install|check`. Repo-tree secret scan, dependency audit, and repo-local git hook install/check before push/release. Repo-specific policy (gitleaks config, privacy paths) stays in the target repo; safedeps owns execution. *(Absorbed the former `security-release-gates`.)*
|
|
28
|
+
|
|
20
29
|
## How It Works
|
|
21
30
|
|
|
22
|
-
`safedeps`
|
|
31
|
+
`safedeps` works in two moves around every install:
|
|
32
|
+
|
|
33
|
+
- **Before** — `safedeps check` clears a package against OSV (canonical), CISA KEV, and GitHub Advisory, then records the approval in a local ledger. For npm it resolves the package's full dependency closure and checks every transitive package too.
|
|
34
|
+
- **After** — the PostToolUse hook re-reads what actually landed in `package-lock.json` and reorgs (rolls back) anything that isn't in the ledger or that the advisory databases now flag.
|
|
35
|
+
|
|
36
|
+
The pre-install command hook (PreToolUse) is a fast advisory nudge — it blocks obvious unapproved installs and risky command forms so the agent gets immediate feedback. But for npm the real authority is the post-install effect gate: it judges what was *actually installed*, not what the command looked like, so a wrapped or obfuscated install command can't slip a package past it.
|
|
37
|
+
|
|
38
|
+
**Script safety (inert install).** On Claude Code, the PreToolUse hook rewrites an npm install to add `--ignore-scripts`, so the install runs **inert** — packages land on disk but no lifecycle script runs yet. The effect gate then verifies the closure; only if it passes does the PostToolUse hook run `npm rebuild` to execute the now-verified scripts. A package the gate rejects is reorged before any of its scripts run. (This uses the Claude Code hook `updatedInput` capability. Codex CLI does not expose it, so on Codex the install runs normally and the effect gate is detect-and-rollback — a malicious install script can run once before the rollback.)
|
|
39
|
+
|
|
40
|
+
This effect-primary model is npm-only for now. `pip`, `cargo`, `go`, `gem`, `maven`, and `nuget` stay on the v2.1 command-gate + reorg model until their closure resolvers land.
|
|
23
41
|
|
|
24
42
|
```
|
|
25
43
|
PreToolUse PostToolUse
|
|
26
44
|
(safedeps-pre-guard.sh) (safedeps-post-verify.sh)
|
|
27
45
|
| |
|
|
28
|
-
install cmd ──> [ Advisory/ledger
|
|
46
|
+
install cmd ──> [ Advisory/ledger UX ] ──> [ Execute ] ──> [ npm effect gate ]
|
|
29
47
|
| | | |
|
|
30
|
-
Block
|
|
31
|
-
|
|
32
|
-
|
|
48
|
+
Block obvious Snapshot Clean? Suspicious?
|
|
49
|
+
misses/risk lock/manifest files, | |
|
|
50
|
+
package listings Confirm REORG
|
|
33
51
|
| |
|
|
34
52
|
| v v
|
|
35
53
|
+--- parent_snapshot_id ──> confirmed
|
|
@@ -38,7 +56,7 @@ Use the GitHub release when you want the full skill/hook source tree as the cano
|
|
|
38
56
|
confirmed snapshot
|
|
39
57
|
```
|
|
40
58
|
|
|
41
|
-
### Phase 1: Advisory
|
|
59
|
+
### Phase 1: Advisory Check (`safedeps check`)
|
|
42
60
|
|
|
43
61
|
Before an agent installs a dependency, it should run:
|
|
44
62
|
|
|
@@ -46,40 +64,44 @@ Before an agent installs a dependency, it should run:
|
|
|
46
64
|
safedeps check <ecosystem> <pkg>@<version|range> --json
|
|
47
65
|
```
|
|
48
66
|
|
|
49
|
-
That command queries OSV (canonical), CISA KEV (hard-risk overlay), and GitHub Advisory (enrichment). Clean or safely narrowed specs are written to `~/.safedeps/approved-specs
|
|
67
|
+
That command queries OSV (canonical), CISA KEV (hard-risk overlay), and GitHub Advisory (enrichment). For npm, it first creates a script-free temp lockfile with `npm install --package-lock-only --ignore-scripts`, extracts the full dependency closure, and queries OSV `/v1/querybatch`. Clean or safely narrowed specs are written to `~/.safedeps/approved-specs/`; npm entries also record `transitive_specs`.
|
|
68
|
+
|
|
69
|
+
### Phase 2: Fast Command Guard + Snapshots (PreToolUse)
|
|
50
70
|
|
|
51
|
-
When Claude Code or Codex CLI is about to run `npm install`, `pip install`, `cargo add`, `go get`, `gem install`, or similar commands, the guard hook:
|
|
71
|
+
When Claude Code or Codex CLI is about to run `npm install`, `pip install`, `cargo add`, `go get`, `gem install`, or similar commands, the guard hook provides a fast advisory/UX layer:
|
|
52
72
|
|
|
53
73
|
1. **Snapshots** the current `package-lock.json`, `pnpm-lock.yaml`, `yarn.lock`, and `package.json` into `~/.safedeps/snapshots/`.
|
|
54
74
|
2. **Records metadata** including a `parent_snapshot_id` linking to the previous confirmed snapshot (forming a chain, just like blocks).
|
|
55
75
|
3. **Captures pre-install state** of `node_modules` (package listings and binary listings) for diff-based detection later.
|
|
56
|
-
4. **
|
|
76
|
+
4. **Fast-checks the approved-spec ledger** for explicit `pkg@version` install commands.
|
|
57
77
|
5. **Runs pre-flight checks** and **blocks** the command entirely if it detects:
|
|
58
78
|
- Typosquatting package names (`lod_sh`, `reacct`, `axois`, etc.)
|
|
59
79
|
- Non-standard `--registry` URLs (anything outside `registry.npmjs.org` and `registry.yarnpkg.com`)
|
|
60
80
|
- Piped remote execution patterns (`curl ... | bash`)
|
|
61
81
|
- Explicit disabling of install script safety (`npm config set ignore-scripts false`)
|
|
62
82
|
|
|
63
|
-
If the ledger gate or a pre-flight check fails, the command is **blocked before execution** -- nothing is installed.
|
|
83
|
+
If the ledger gate or a pre-flight check fails, the command is **blocked before execution** -- nothing is installed. This command guard is intentionally best-effort; it improves the agent loop and catches direct misses, while npm authority lives in the post-install effect gate.
|
|
64
84
|
|
|
65
|
-
### Phase
|
|
85
|
+
### Phase 3: Post-install Effect Enforcement (`safedeps-post-verify.sh` -- PostToolUse)
|
|
66
86
|
|
|
67
|
-
After the install command completes, the verify hook analyzes what changed:
|
|
87
|
+
After the install command completes, the verify hook analyzes what changed. For npm, this is the primary enforcement surface: it reads the actual `package-lock.json` closure, verifies every package against approved direct entries and their `transitive_specs`, and re-checks the closure with OSV batch.
|
|
68
88
|
|
|
69
|
-
1. **
|
|
89
|
+
1. **npm effect gate** -- Reorgs if any lockfile package is unapproved, KEV-blocked, vulnerable, or cannot be verified fail-closed.
|
|
90
|
+
|
|
91
|
+
2. **Install script analysis** -- Scans newly added packages for `preinstall`, `install`, and `postinstall` scripts containing:
|
|
70
92
|
- Network access (`curl`, `wget`, `fetch`, `http`, `socket`, `dns`)
|
|
71
93
|
- Dynamic code execution (`eval`, `exec`, `spawn`, `child_process`, `Function()`)
|
|
72
94
|
- Sensitive path access (`~/.ssh`, `.env`, `.aws`, `credentials`)
|
|
73
95
|
- Obfuscated content (`base64`, `atob`, `Buffer.from`, hex/unicode escapes)
|
|
74
96
|
|
|
75
|
-
|
|
97
|
+
3. **Lock file diff analysis** -- Compares the snapshotted lock file content against the post-install version:
|
|
76
98
|
- Resolved URLs pointing to non-standard registries
|
|
77
99
|
- Insecure protocols (`http://`, `git://`) in resolved URLs
|
|
78
100
|
- Unusually large dependency additions (>50 new resolved entries, indicating potential dependency confusion)
|
|
79
101
|
|
|
80
|
-
|
|
102
|
+
4. **Binary inspection** -- Checks `node_modules/.bin/` for newly added native binaries (ELF, Mach-O, shared objects) that should not appear in a JavaScript project.
|
|
81
103
|
|
|
82
|
-
###
|
|
104
|
+
### Confirm or Reorg
|
|
83
105
|
|
|
84
106
|
- **All checks pass** -- The snapshot is marked as **confirmed** in `~/.safedeps/confirmed`. This becomes the new safe baseline.
|
|
85
107
|
- **Any check fails** -- A **reorg** is triggered:
|
|
@@ -94,7 +116,7 @@ After the install command completes, the verify hook analyzes what changed:
|
|
|
94
116
|
| Blockchain Concept | Safedeps Equivalent |
|
|
95
117
|
|---|---|
|
|
96
118
|
| **Block candidate** | Snapshot taken before `npm install` |
|
|
97
|
-
| **Block validation** | Post-install
|
|
119
|
+
| **Block validation** | Post-install effect checks (npm closure, scripts, lock diff, binaries) |
|
|
98
120
|
| **Finality / confirmation** | Snapshot ID written to `~/.safedeps/confirmed` |
|
|
99
121
|
| **Chain reorganization** | Rollback to last confirmed snapshot + `node_modules` rebuild |
|
|
100
122
|
| **Parent hash linking** | `parent_snapshot_id` in each snapshot's `_meta.json` |
|
|
@@ -104,18 +126,20 @@ After the install command completes, the verify hook analyzes what changed:
|
|
|
104
126
|
|
|
105
127
|
| Category | What it catches | Phase | Action |
|
|
106
128
|
|---|---|---|---|
|
|
107
|
-
| Typosquatting | Known misspelling patterns of popular packages |
|
|
108
|
-
| Pipe execution | `curl \| bash`, `wget \| sh` |
|
|
109
|
-
| Registry hijack | `--registry` pointing to unofficial sources |
|
|
110
|
-
| Script safety bypass | `npm config set ignore-scripts false` |
|
|
111
|
-
| Command indirection | `eval "npm install ..."`, subshell expansion, variable indirection |
|
|
112
|
-
| npx/dlx execution | `npx`, `pnpm dlx`, `yarn dlx` package execution |
|
|
113
|
-
|
|
|
114
|
-
|
|
|
115
|
-
|
|
|
116
|
-
|
|
|
117
|
-
|
|
|
118
|
-
|
|
|
129
|
+
| Typosquatting | Known misspelling patterns of popular packages | PreToolUse advisory guard | **Block** |
|
|
130
|
+
| Pipe execution | `curl \| bash`, `wget \| sh` | PreToolUse advisory guard | **Block** |
|
|
131
|
+
| Registry hijack | `--registry` pointing to unofficial sources | PreToolUse advisory guard | **Block** |
|
|
132
|
+
| Script safety bypass | `npm config set ignore-scripts false` | PreToolUse advisory guard | **Block** |
|
|
133
|
+
| Command indirection | `eval "npm install ..."`, subshell expansion, variable indirection | PreToolUse advisory guard | **Guard** |
|
|
134
|
+
| npx/dlx execution | `npx`, `pnpm dlx`, `yarn dlx` package execution | PreToolUse advisory guard | **Guard** |
|
|
135
|
+
| Unapproved transitive dependency | npm `package-lock.json` package missing from direct ledger or `transitive_specs` | PostToolUse npm primary effect gate | **Reorg** |
|
|
136
|
+
| Vulnerable closure package | npm direct/transitive package with OSV/KEV hit | PostToolUse npm primary effect gate | **Reorg** |
|
|
137
|
+
| Malicious install scripts | Network calls, `eval`/`exec`, sensitive path access in hooks | PostToolUse effect verify | **Reorg** |
|
|
138
|
+
| Obfuscated code | Base64, hex encoding, `Buffer.from` in install scripts | PostToolUse effect verify | **Reorg** |
|
|
139
|
+
| Lock file tampering | Resolved URLs from non-standard registries | PostToolUse effect verify | **Reorg** |
|
|
140
|
+
| Insecure protocols | `http://` or `git://` resolved URLs | PostToolUse effect verify | **Reorg** |
|
|
141
|
+
| Dependency confusion | >50 new dependencies in a single install | PostToolUse effect verify | **Reorg** |
|
|
142
|
+
| Native binaries | Compiled executables in `node_modules/.bin/` | PostToolUse effect verify | **Reorg** |
|
|
119
143
|
|
|
120
144
|
## Installation
|
|
121
145
|
|
|
@@ -149,7 +173,7 @@ cd safedeps
|
|
|
149
173
|
node scripts/install/install-safedeps-hooks.mjs
|
|
150
174
|
```
|
|
151
175
|
|
|
152
|
-
The installer is idempotent. It symlinks the skill into `~/.claude/skills/safedeps` and `~/.codex/skills/safedeps` when those roots exist, patches the matching hook config, and can also place `safedeps` on PATH through `~/.local/bin`.
|
|
176
|
+
The installer is idempotent. It symlinks the skill into `~/.claude/skills/safedeps` and `~/.codex/skills/safedeps` when those roots exist, patches the matching hook config, and — with `--link-bin` — can also place `safedeps` on PATH through `~/.local/bin`. That PATH link is optional: the hooks name an absolute fallback path in their block messages, so the gate is self-contained and works with zero PATH setup.
|
|
153
177
|
|
|
154
178
|
**3. Manual hook registration, if needed:**
|
|
155
179
|
|
|
@@ -229,16 +253,6 @@ node scripts/install/install-safedeps-recheck-agent.mjs uninstall
|
|
|
229
253
|
tail -f ~/.safedeps/recheck.log
|
|
230
254
|
```
|
|
231
255
|
|
|
232
|
-
### Legacy State Migration
|
|
233
|
-
|
|
234
|
-
If you used the old `npm-reorg-guard` state directory, migrate it once:
|
|
235
|
-
|
|
236
|
-
```bash
|
|
237
|
-
safedeps migrate
|
|
238
|
-
```
|
|
239
|
-
|
|
240
|
-
This copies missing state from `~/.npm-reorg-guard` into `~/.safedeps` and archives the legacy directory so there is no second active state root.
|
|
241
|
-
|
|
242
256
|
## Real-World Attack Coverage
|
|
243
257
|
|
|
244
258
|
`safedeps` is designed to catch the patterns behind real supply-chain incidents:
|
|
@@ -294,8 +308,8 @@ safedeps/
|
|
|
294
308
|
providers/ # OSV / CISA KEV / GHSA adapters
|
|
295
309
|
ledger/ # approved-spec ledger
|
|
296
310
|
scripts/
|
|
297
|
-
safedeps-pre-guard.sh # PreToolUse hook --
|
|
298
|
-
safedeps-post-verify.sh # PostToolUse hook --
|
|
311
|
+
safedeps-pre-guard.sh # PreToolUse hook -- advisory ledger UX + snapshots
|
|
312
|
+
safedeps-post-verify.sh # PostToolUse hook -- npm primary effect verification + reorg
|
|
299
313
|
install/install-safedeps-hooks.mjs
|
|
300
314
|
install/install-safedeps-recheck-agent.mjs
|
|
301
315
|
install/migrate-safedeps-state.mjs
|
|
@@ -306,6 +320,36 @@ safedeps/
|
|
|
306
320
|
LICENSE # Apache-2.0
|
|
307
321
|
```
|
|
308
322
|
|
|
323
|
+
## What's Different
|
|
324
|
+
|
|
325
|
+
`safedeps` intercepts package installs at **the moment an AI coding agent writes the install command** — not at CI scan time, PR review time, or runtime sandbox time. That timing is the core differentiator.
|
|
326
|
+
|
|
327
|
+
Typical flow:
|
|
328
|
+
|
|
329
|
+
1. The agent writes `npm install foo@1.2.3` (or any of the other supported install verbs).
|
|
330
|
+
2. The PreToolUse hook does a fast advisory ledger check. If the direct spec is missing, expired, or obviously risky, it **blocks** the install and returns the exact `safedeps check npm foo@1.2.3` command the agent should run next, in the block reason.
|
|
331
|
+
3. The agent runs `safedeps check`. The CLI queries OSV / CISA KEV / GitHub Advisory and, if safe, **adds the spec to the ledger**. KEV matches are hard-block (no override). CVEs with an available patch are auto-narrowed to the fixed version.
|
|
332
|
+
4. The agent retries the install. The ledger entry now matches, so the install **proceeds**.
|
|
333
|
+
5. After the install, the PostToolUse hook is the npm primary authority: it verifies the actual lockfile closure against direct ledger entries, `transitive_specs`, and OSV batch, then checks install scripts and native binaries and **auto-reorgs** to the last confirmed snapshot if anything diverged.
|
|
334
|
+
|
|
335
|
+
Every install command gets fast advisory feedback before it runs; every npm install gets closure-level enforcement after it runs. The suspicious package a human would catch at PR review is already caught at install time — and there is no SaaS dependency, only the local CLI plus public databases (OSV / KEV / GHSA).
|
|
336
|
+
|
|
337
|
+
Two honest boundaries:
|
|
338
|
+
|
|
339
|
+
- **The command hook is a heuristic, not a sandbox.** Unusual wrappers, shell interpreters, or same-user tampering with local `~/.safedeps` state sit outside its trust boundary. The npm effect gate is the backstop — it catches what the command hook misses, because it inspects the installed result rather than the command text.
|
|
340
|
+
- **Effect-primary enforcement is npm-only today.** `pip`, `cargo`, `go`, `gem`, `maven`, and `nuget` stay on the v2.1 command-gate + reorg model until their closure resolvers land.
|
|
341
|
+
|
|
342
|
+
## Legacy / Migration: v1 `npm-reorg-guard`
|
|
343
|
+
|
|
344
|
+
The v1 product was named `npm-reorg-guard` and used `~/.npm-reorg-guard/` as the state directory. v2 moves state to `~/.safedeps/`. A one-shot migration is provided:
|
|
345
|
+
|
|
346
|
+
```bash
|
|
347
|
+
safedeps migrate
|
|
348
|
+
```
|
|
349
|
+
|
|
350
|
+
- If `~/.npm-reorg-guard/` exists, it copies the snapshot chain, confirmed pointers, and logs into `~/.safedeps/` and archives the legacy directory so there is no second active state root.
|
|
351
|
+
- If it does not exist, the command is a no-op (fresh v2 users do not need it).
|
|
352
|
+
|
|
309
353
|
## License
|
|
310
354
|
|
|
311
355
|
[Apache License 2.0](LICENSE)
|