@aldegad/safedeps 2.1.1 → 2.4.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/ROADMAP.md CHANGED
@@ -1,131 +1,170 @@
1
1
  # Safedeps Roadmap
2
2
 
3
- > 시간축 + 우선순위. **왜·어떻게** `ARCHITECTURE.md`, **언제·뭐 먼저** 파일.
3
+ > Timeline and priorities. The **why / how** lives in [`ARCHITECTURE.md`](./ARCHITECTURE.md); the **when / what first** lives here. *(한국어 → [ROADMAP.ko.md](./ROADMAP.ko.md))*
4
4
 
5
5
  ---
6
6
 
7
- ## 결정 SSoT
7
+ ## Scope
8
8
 
9
- **스코프 = 개발 의존성만** (npm / pip / cargo / go / gem / maven / nuget).
10
- OS-level (nginx / apt / brew / system binary) 은 **별도 도구로 분리**. SRP 측면 더 깔끔.
9
+ Safedeps gates **development dependency installs** (npm / pip / cargo / go / gem / maven / nuget). At release time it also runs a repo-tree secret scan, dependency audit, and git-hook install/check (the lane absorbed from the former `security-release-gates`).
11
10
 
12
- 근거: dev 의존성과 OS 패키지는 운영 결이 다름.
13
- - dev = "새 기능 → install 명령 → 매번 새 패키지 진입" (install 순간 게이트가 의미 큼).
14
- - OS = "기존 패키지 security update" (cron 주기 audit + RSS 알람이 더 맞음).
11
+ Out of scope: OS / system packages, container images, runtime sandboxing, registry integrity, and reputation analysis. Those are different security layers and stay in different tools — see [`ARCHITECTURE.md`](./ARCHITECTURE.md) §1 for the boundary.
15
12
 
16
- → safedeps = dev 의존성 install 단계 결.
17
- → OS-level 은 별도 (가칭 `infra-cve-monitor` — 미래 v3+).
13
+ ---
14
+
15
+ ## v1 — `npm-reorg-guard` (shipped)
16
+
17
+ - npm-only, self-contained, no external advisory database.
18
+ - PreToolUse hook: typosquat / `curl | bash` / non-standard registry pattern blocks.
19
+ - PostToolUse hook: lockfile diff + install-script analysis → reorg (rollback) on suspicion.
20
+
21
+ Limits: npm only, no CVE lookup (pattern matching), evadable by a determined adversary. The GitHub repo has since been renamed `aldegad/safedeps`.
18
22
 
19
23
  ---
20
24
 
21
- ## v1 (출시 완료)
25
+ ## v2 — `safedeps` (shipped, v2.1.x)
22
26
 
23
- **이름**: `npm-reorg-guard`
24
- **Status**: shipped as v1. GitHub repo has since been renamed to `aldegad/safedeps`.
27
+ The internal engine keeps the v1 `reorg-guard` assets.
25
28
 
26
- - npm ecosystem 전용.
27
- - PreToolUse hook (guard.sh): typosquat / curl|bash / 비표준 registry 등 **hardcoded pattern** 차단.
28
- - PostToolUse hook (verify.sh): lockfile diff + install script 검사 → 의심 시 **reorg** (rollback).
29
- - 외부 vuln DB 조회 0. self-contained.
29
+ ### What changed
30
30
 
31
- **한계**:
32
- - npm 만.
33
- - 알려진 CVE 검사 X (pattern matching 위주).
34
- - adversarial 회피 가능.
31
+ - **Multi-ecosystem**: npm / yarn / pnpm / pip (poetry, uv, pipenv) / cargo / go / gem / maven / nuget.
32
+ - **External advisory databases**: OSV.dev (canonical) + CISA KEV (hard-risk overlay) + GitHub Advisory (enrichment).
33
+ - **Three-phase defense**:
34
+ 1. Advisory gate (`safedeps check`) — query the advisory databases before the install command is written, decide a safe spec, and record it to the `~/.safedeps/approved-specs/` ledger.
35
+ 2. Hook enforcement (`safedeps-pre-guard.sh`) — verify the install matches the ledger.
36
+ 3. Post-install reorg (`safedeps-post-verify.sh`) — the v1 engine, rolling back on divergence.
37
+ - **Approved-spec TTL** (30 days) + **daily re-check** (revoke + alert when a new CVE appears).
38
+ - **No silent fallback**: a provider failure is fail-closed; any override is explicit and observable.
35
39
 
36
- ---
40
+ ### Milestones (all shipped)
41
+
42
+ | Milestone | Output |
43
+ |---|---|
44
+ | `v2.0-doc` | `ARCHITECTURE.md` v2 written and pushed. |
45
+ | `v2.1-rename` | Repo / skill id / paths renamed to `safedeps`; `safedeps migrate` moves legacy `~/.npm-reorg-guard` state to `~/.safedeps` and cleans up legacy hooks. |
46
+ | `v2.1-providers` | `lib/providers/` — OSV / KEV / GHSA adapters behind one query interface, with a 24h response cache. |
47
+ | `v2.1-ledger` | `lib/ledger/` — approved-spec JSON I/O (atomic write, hash, TTL check). |
48
+ | `v2.1-cli` | `bin/safedeps` — `check`, `ledger`, `revoke`, `re-check`, `migrate`, `version` subcommands. |
49
+ | `v2.1-guard-patch` | `safedeps-pre-guard.sh` — ledger enforcement on top of the v1 pattern blocks. |
50
+ | `v2.1-verify-patch` | `safedeps-post-verify.sh` — lockfile-diff comparison against the approved spec on top of the v1 reorg. |
51
+ | `v2.1-multi-ecosystem` | pip / cargo / go / gem / maven / nuget command parsing + lockfile snapshots, shared as rollback truth across both hooks. |
52
+ | `v2.1-hook-rename` | Hook file namespacing + cross-engine installer (`install-safedeps-hooks.mjs`, idempotent, `--uninstall`). |
53
+ | `v2.1-recheck-cron` | Daily re-check LaunchAgent — re-queries every approved spec, revokes + notifies on new CVE/KEV/provider-skip. |
54
+ | `v2.1-tests` | End-to-end tests — fixture provider responses drive ledger / hook / re-check / migration checks. |
55
+ | `v2.1-release` | npm publish (`@aldegad/safedeps`) + GitHub release. |
37
56
 
38
- ## v2 — Safedeps (현재 작업 중)
57
+ ### Release notes
39
58
 
40
- **이름**: `safedeps`, 내부 engine = `reorg-guard` (v1 자산 보존).
41
- **Status**: `ARCHITECTURE.md` v2 작성 완료, v2.1 provider/ledger 구현 시작.
59
+ - The npm package version in `package.json` is the single source of truth. `bin/safedeps` `SAFEDEPS_VERSION` tracks it and the smoke test reads `package.json` to compare (current: v2.4.0).
60
+ - `npm test` runs the release smoke suite; the full fixture E2E lives under `v2.1-tests`.
61
+ - The daily re-check uses no LLM tokens. It is opt-in: a macOS `launchd` user agent runs `safedeps re-check --json` daily, installed atomically by `install-safedeps-recheck-agent.mjs`. It writes `~/.safedeps/recheck.log` and `~/.safedeps/recheck-alerts.jsonl` and raises a macOS notification on a new CVE/KEV/revoke/provider-skip. Network is used only for OSV / CISA / GHSA queries.
42
62
 
43
- ### 핵심 변화
44
- - ecosystem 통합: npm / yarn / pnpm / pip (poetry, uv, pipenv) / cargo / go / gem / maven / nuget.
45
- - **외부 vuln DB 결합**: OSV.dev (primary) + CISA KEV (hard-risk overlay) + GHSA (cross-check). NVD / deps.dev / Snyk = enrichment.
46
- - **3-phase defense**:
47
- 1. Advisory Gate (`safedeps check`) — install 명령 *작성 전* vuln DB 조회 → 안전 spec 결정 → `~/.safedeps/approved-specs/<hash>.json` ledger 기록.
48
- 2. Hook Enforcement (`safedeps-pre-guard.sh`) — ledger 일치 검증.
49
- 3. Post-Install Reorg (`safedeps-post-verify.sh`) — v1 engine 그대로 (rollback fallback).
50
- - Approved spec **TTL** (30일) + **daily re-check** cron (새 CVE 발견 시 revoke + 알람).
51
- - **No silent fallback**: provider fail = fail-closed + `--allow-unverified` explicit override (observable).
63
+ ## v2.2 — effect-based enforcement (npm)
52
64
 
53
- ### 구현 마일스톤
65
+ Status: shipped as v2.2.0 (npm-first).
54
66
 
55
- | 마일스톤 | 산출물 | 의존 |
56
- |---|---|---|
57
- | **v2.0-doc** ✅ | `ARCHITECTURE.md` v2 작성·push | — |
58
- | **v2.0-roadmap** ✅ | 이 문서 | — |
59
- | **v2.1-rename** ✅ | GitHub repo rename `aldegad/npm-reorg-guard` → `aldegad/safedeps` ✅. 로컬 repo/skill id/path `safedeps` ✅. `safedeps migrate` 로 legacy `~/.npm-reorg-guard` → `~/.safedeps` 이전 + legacy hook cleanup. | v2.0-doc |
60
- | **v2.1-providers** ✅ | `lib/providers/` 신규 — OSV / KEV / GHSA adapter. 단일 query interface. 응답 cache (TTL 24h). | — |
61
- | **v2.1-ledger** ✅ | `lib/ledger/` 신규 — approved spec JSON I/O (atomic write, hash 계산, TTL 검사). | v2.1-providers |
62
- | **v2.1-cli** ✅ | `bin/safedeps` 신규 — `check`, `ledger`, `revoke`, `re-check`, `migrate`, `version` 서브커맨드. | v2.1-providers, v2.1-ledger |
63
- | **v2.1-guard-patch** ✅ | `scripts/safedeps-pre-guard.sh` 갱신 — ledger 일치 검증 추가 + v1 pattern 유지. | v2.1-ledger |
64
- | **v2.1-verify-patch** ✅ | `scripts/safedeps-post-verify.sh` 갱신 — approved spec 과 lockfile diff 비교 추가 + v1 reorg 유지. | v2.1-ledger |
65
- | **v2.1-multi-ecosystem** ✅ | pip / cargo / go / gem / maven / nuget 명령 파싱 + lockfile snapshot. `safedeps-pre-guard.sh` 는 install 분류와 typosquat pattern 을 확장했고, `safedeps-post-verify.sh` 는 monitored dependency files 기준으로 rollback truth 를 공유한다. | v2.1-guard-patch |
66
- | **v2.1-hook-rename** ✅ | hook 파일 namespacing (`guard.sh` → `safedeps-pre-guard.sh`, `verify.sh` → `safedeps-post-verify.sh`) + cross-engine installer (`scripts/install/install-safedeps-hooks.mjs`, ~/.claude + ~/.codex 자동 등록, idempotent, --uninstall). | v2.1-cli |
67
- | **v2.1-recheck-cron** ✅ | daily re-check launchd — 전체 approved spec 재 query → 새 CVE/KEV/provider-skip 시 revoke + macOS 알림. | v2.1-providers, v2.1-ledger |
68
- | **v2.1-tests** ✅ | end-to-end 테스트 — fixture provider 응답 → 명령 시뮬레이션 → ledger / hook / re-check / migration 동작 검증. | 모든 v2.1 |
69
- | **v2.1-release** | npm package publish (`@aldegad/safedeps`) + GitHub release v2.1.0. | 모든 v2.1 |
67
+ ### What changed
70
68
 
71
- ### 2026-05-18 릴리즈 정리 메모
69
+ - **Authority moved to effects**: PostToolUse now reads the actual `package-lock.json` closure and compares every installed `pkg@version` against approved direct specs plus their `transitive_specs`.
70
+ - **Full closure approval for npm**: `safedeps check npm <pkg>@<version>` resolves a script-free lockfile in a temp dir with `npm install --package-lock-only --ignore-scripts`, extracts the full closure, and queries OSV `/v1/querybatch`.
71
+ - **Batch + cache**: OSV batch responses are written back into the same per `pkg@version` 24h cache used by single-package provider queries.
72
+ - **No blind trust for transitives**: a clean direct package with an unapproved or vulnerable transitive dependency is not enough; the full closure must be clean and recorded.
73
+ - **PreToolUse demoted to fast UX guard**: command parsing still blocks obvious unapproved install attempts and keeps the bypass regression coverage, but PostToolUse is the primary enforcement surface.
74
+ - **Inert install (Claude Code)**: the PreToolUse hook rewrites an npm install to add `--ignore-scripts` via the hook `updatedInput` capability, so the install runs inert; PostToolUse runs `npm rebuild` only after the closure is verified clean, so a rejected package's lifecycle scripts never run. Codex CLI lacks `updatedInput`, so it stays on detect-and-rollback.
72
75
 
73
- - npm package metadata 는 v2.1.0 기준으로 정합화한다 (`package.json` version + `bin.safedeps`).
74
- - `npm test` 는 release smoke suite 를 실행한다. full fixture E2E 는 `v2.1-tests` 후속으로 남긴다.
75
- - daily re-check 는 토큰을 쓰지 않는다. 알렉스가 opt-in 하면 macOS `launchd` user agent 로 `safedeps re-check --json` 을 매일 실행하는 구조가 기본이다. 네트워크는 OSV/CISA/GHSA provider query 에만 사용된다.
76
- - 실제 local background job 은 `scripts/install/install-safedeps-recheck-agent.mjs` 로 atomic install 한다. wrapper 는 `~/.safedeps/recheck.log` 와 `~/.safedeps/recheck-alerts.jsonl` 를 쓰고, 새 CVE/KEV/revoke/provider-skip 이 있으면 macOS notification 을 띄운다.
76
+ ### npm-only boundary
77
77
 
78
- ### 후속 로드맵 전체 workspace inventory audit
78
+ This phase covers npm lockfile closure only. pip / cargo / go / gem / maven / nuget keep the v2.1 command/ledger/reorg behavior until each ecosystem has an explicit closure resolver and script/no-execution policy.
79
79
 
80
- 전체 repo/lockfile inventory scan 은 safedeps v2.1 daily re-check 와 분리한다. 후보 설계는 최초 one-shot inventory scan 으로 workspace 의 manifest/lockfile 을 발견하고, 사용자가 채택한 spec 만 approved ledger 또는 별도 inventory ledger 에 넣은 뒤 주기 re-check 대상으로 삼는 방식이다. 이렇게 해야 safedeps 의 현재 책임인 install approval gate 와 이미 디스크에 존재하는 dependency audit 의 책임 소재가 섞이지 않는다.
80
+ ### Verification
81
81
 
82
- ### 우선순위
82
+ - closure approval records `transitive_specs`
83
+ - unapproved transitive package in `package-lock.json` triggers post-verify reorg
84
+ - approved full-closure install passes without false reorg
85
+ - heredoc / echo text does not trigger install detection
86
+ - existing smoke + fixture E2E regression suite remains green
83
87
 
84
- 1. v2.1-release: commit / tag / GitHub release / npm publish.
88
+ ### Current focus
89
+
90
+ 1. `v2.2.0-release`: merged `safedeps-security-hardening`, tagged `v2.2.0` (GitHub release + `npm publish`).
85
91
 
86
92
  ---
87
93
 
88
- ## v3 (미래 알렉스 결정 시점)
94
+ ## v2.3secret-leak lane doctor + scaffold (shipped)
89
95
 
90
- - **plugin model** — 사용자 정의 provider (회사 내부 vuln DB, private registry).
91
- - **policy file** — `.safedeps.toml` 로 \"우리 팀 정책: KEV hit 자동 block, CVSS 7+ 사용자 컨펌, 특정 패키지 allowlist\" 명시.
92
- - **CI mode** — `safedeps check --ci` 로 GitHub Actions / CircleCI fail-fast.
93
- - **transitive risk score** — deps.dev graph 통합. 직접 dep 만 아니라 transitive dep 의 \"위험 점수\" 시각화.
96
+ Status: shipped as v2.3.0.
94
97
 
95
- ---
98
+ ### What changed
99
+
100
+ - **`safedeps doctor`** — a repo-entry posture check. It diagnoses the per-repo secret-leak lane (`.gitleaks` policy, `.githooks/pre-commit`, active `core.hooksPath`, scanner availability) and reports the global install-time gate too. Read-only by default, `--json` for agents, exits non-zero when the secret-leak lane has gaps.
101
+ - **`safedeps doctor --fix` / `safedeps hooks init`** — scaffolds a starter `.gitleaks.toml` (or `.gitleaks.private.toml`) and `.githooks/pre-commit` from `lib/gates/templates/`, then activates the hooks. Non-destructive: an existing repo-owned policy is never overwritten.
102
+ - **Agent-as-security-role framing** — `SKILL.md` makes `safedeps doctor` a repo-entry step so the agent, not a later leak, closes the secret-lane gap. The installer prints a per-repo nudge (no auto-write into repos — the policy boundary stays with the repo).
103
+ - **Fail-closed delegation** — the scaffolded `pre-commit` delegates to `safedeps scan secrets --staged` (one canonical scanner path); an unresolvable `safedeps` or a missing scanner blocks the commit rather than skipping silently.
104
+
105
+ ### Design decisions
106
+
107
+ - `doctor` is holistic but **secret-lane-centric**: its exit code reflects the per-repo lane only; the global dependency gate is reported (`deps` check) but does not gate the repo result.
108
+ - safedeps owns **execution**, the repo owns **policy**. Templates are seeds the repo tunes, consistent with the existing Two Lanes invariant.
96
109
 
97
- ## v4+ (장기)
110
+ ### Verification
98
111
 
99
- - **team-shared ledger** multi-machine approved spec sync (회사 dev 모두가 같은 ledger 공유).
100
- - **AI agent integration** Claude / Codex vuln 발견 시 \"대체 모듈 X 권장\" 직접 제안 (LLM-as-judge).
101
- - **diff visualization UI** approved spec snapshot 사이의 dep tree diff 시각화.
112
+ - `safedeps doctor` flags gaps on an unconfigured repo and reports clean after `--fix`
113
+ - `hooks init` is non-destructive across a re-run (repo edits survive)
114
+ - pre-commit gate denies a committed secret, passes clean and `.env.example` placeholder commits (bypass harness + regression)
115
+ - existing smoke + fixture E2E regression suite remains green
102
116
 
103
117
  ---
104
118
 
105
- ## 명시적 NON-GOAL (이 도구는 하지 않는다)
119
+ ## v2.4 — fail-closed hooks + supply-chain hardening (shipped)
106
120
 
107
- - **OS-level CVE 감시** (nginx, apt 패키지, system binary). → 별도 도구 `infra-cve-monitor` 결로 분리.
108
- - **컨테이너 이미지 스캔**. → Trivy / Grype.
109
- - **runtime 권한 sandbox**. → lavamoat / firejail.
110
- - **registry 자체의 손상 감지**. → registry 운영사 책임.
111
- - **사용자 평판 분석 (behavioral)**. → socket.dev.
121
+ Status: shipped as v2.4.0.
112
122
 
113
- safedeps 결 = **\"개발 의존성 install 단계의 advisory + spec gate + rollback\"** 한 줄. 다른 결로 확장하지 않는다 — SRP 측면 다른 도구로 분리.
123
+ ### What changed
124
+
125
+ - **Fail-closed gate** — the PreToolUse/PostToolUse hooks no longer `exit 0` (silent pass) when they cannot run. A lock-unavailable install now **denies** fail-closed; an unavoidable `jq`-missing case becomes an **explicit allow-with-warning**; every such outcome is recorded in `~/.safedeps/advisory.log` (observable, per the no-silent-fallback invariant). The PostToolUse path records an un-runnable gate as **UNVERIFIED** rather than a clean pass.
126
+ - **`SECURITY.md`** — vulnerability disclosure policy, supported versions, scope, and the by-design security properties (no SaaS, zero deps, no silent fallback).
127
+ - **CI hardening** — `actions/*` pinned to commit SHA; the gitleaks download is checksum-verified; a ShellCheck gate (error-clean); a macOS + Linux matrix (the v2.3 `stat` fix proved cross-OS coverage matters); and an `npm pack` step that keeps the zero-dependency property honest.
128
+
129
+ ### Verification
130
+
131
+ - lock-unavailable install denies fail-closed and logs to `advisory.log`
132
+ - jq-missing is logged as an observable allow-with-warning, never a silent skip
133
+ - ShellCheck (`--severity=error`) is clean across all shell sources
134
+ - existing smoke + e2e regression suite remains green on both Linux and macOS
114
135
 
115
136
  ---
116
137
 
117
- ## 관련 미래 도구 (분리 권장)
138
+ ## v3 (future)
139
+
140
+ ### Ledger tamper resistance
141
+
142
+ Defends the second-order attack where a malicious package's `postinstall` (running as the user) forges a "B approved" ledger entry so a later install of B skips the advisory check. The package cannot do this *before* it runs, so closing the install-time gate is the first line of defense; this hardens the case where a first compromise already happened.
143
+
144
+ Approach — **treat OSV as the authority and the ledger as a cache**, plus tamper detection. Cheap, layers onto existing infra:
145
+
146
+ 1. **Re-validate at enforcement / re-check** — verify the stored evidence against OSV instead of trusting the ledger verdict. A forged entry with no real evidence (or for a package OSV reports as vulnerable) is caught and revoked. Reduces the ledger to memoization with OSV as SSoT.
147
+ 2. **Watch `~/.safedeps/` in the post-install scan** — the post-verify hook already flags `postinstall` scripts that touch `~/.ssh` / `.env`; add `~/.safedeps/` so a package that writes the ledger trips a reorg — catching the forge in the act.
148
+ 3. **Provenance cross-check in daily re-check** — flag ledger entries with no matching `advisory.log` record (i.e. no real `safedeps check` ever ran) as suspected forgeries.
149
+
150
+ Explicit non-approach: **cryptographic ledger signing is not pursued** — a same-uid attacker can read the signing key and re-sign forgeries, so a local HMAC/signature adds no real boundary. The defense is authority-elsewhere (OSV) + detection, not local secrets.
151
+
152
+ ### Other v3 work
153
+
154
+ - **Plugin providers** — user-defined advisory sources (internal vuln DB, private registry).
155
+ - **Policy file** — `.safedeps.toml` for team policy (auto-block on KEV hit, user confirm on CVSS 7+, per-package allowlist).
156
+ - **CI mode** — `safedeps check --ci` for fail-fast in GitHub Actions / CircleCI.
157
+ - **Closure expansion beyond npm** — pip / cargo / go / gem / maven / nuget closure resolvers with explicit no-script/no-build policies.
158
+ - **Transitive risk score** — deps.dev graph integration; risk visualization beyond direct dependencies.
118
159
 
119
- | 도구 | 결 | 관계 |
120
- |---|---|---|
121
- | `safedeps` (이것) | dev 의존성 (npm/pip/cargo/...) install 단계 | 현재 작업 |
122
- | `infra-cve-monitor` (가칭) | nginx / apt / OS package 주기적 audit + RSS 알람 | 미래 별 도구 |
123
- | `container-scan-bridge` (가칭) | Trivy / Grype wrapper, 컨테이너 이미지 결 | 미래 별 도구 |
160
+ ## v4+ (long-term)
124
161
 
125
- 도구가 같은 (\"보안\") 다른 layer. 한 skill 에 통합하지 않고 분리.
162
+ - **Team-shared ledger** multi-machine approved-spec sync.
163
+ - **Agent remediation** — Claude / Codex suggests a safer replacement when a vuln is found (LLM-as-judge).
164
+ - **Diff visualization** — dependency-tree diff between two approved-spec snapshots.
126
165
 
127
166
  ---
128
167
 
129
- ## 변경 history
168
+ ## History
130
169
 
131
- - 2026-05-18: ROADMAP.md 최초 작성. v1 → v2 결정 + v3 / v4 / NON-GOAL 명시. (코덱시 surface:195 합의 + 클로디시 surface:61 작성.)
170
+ - 2026-05-18: Initial ROADMAP v1 → v2 decision plus v3 / v4 outline.
package/SECURITY.md ADDED
@@ -0,0 +1,45 @@
1
+ # Security Policy
2
+
3
+ safedeps is a security tool, so it holds itself to the posture it enforces: local-only, no SaaS, **zero runtime npm dependencies**, fail-closed gates, and observable bypasses.
4
+
5
+ ## Supported versions
6
+
7
+ | Version | Supported |
8
+ |---|---|
9
+ | 2.x (latest minor) | ✅ |
10
+ | < 2.0 (`npm-reorg-guard`) | ❌ — migrate with `safedeps migrate` |
11
+
12
+ Fixes land on the latest minor. Pin to a released tag for reproducibility.
13
+
14
+ ## Reporting a vulnerability
15
+
16
+ **Do not open a public issue for a vulnerability.** Report it privately:
17
+
18
+ 1. **Preferred** — GitHub private advisory: the repo's **Security → Report a vulnerability** tab (<https://github.com/aldegad/safedeps/security/advisories/new>).
19
+ 2. **Email** — `aldegad@gmail.com` with `[safedeps security]` in the subject.
20
+
21
+ Please include: affected version (`safedeps --json version`), a minimal reproduction, the impact (e.g. a gate bypass, a fail-open path, a reorg that misses a threat), and any logs from `~/.safedeps/advisory.log` / `~/.safedeps/reorg.log`.
22
+
23
+ ### What counts
24
+
25
+ In scope — anything that defeats a gate or weakens its guarantees, for example:
26
+
27
+ - a dependency-install command that bypasses the PreToolUse guard or the PostToolUse effect gate;
28
+ - a malicious package whose closure or install scripts pass verification when they should reorg;
29
+ - a **fail-open** path (a gate that silently passes when it cannot run);
30
+ - a secret-leak lane bypass (a committed secret the scaffolded gate misses);
31
+ - ledger tampering that grants an unapproved spec (note: the ledger is a same-user convenience cache, **not** a boundary against a same-user attacker until signing lands — that limit is documented, not a vuln).
32
+
33
+ Out of scope — issues requiring an already-root/same-user attacker beyond the documented threat model, or third-party advisory-database accuracy (OSV/KEV/GHSA own their data).
34
+
35
+ ## Response
36
+
37
+ - Acknowledgement within ~72 hours.
38
+ - A fix or mitigation plan for confirmed reports, coordinated before public disclosure.
39
+ - Credit in the release notes if you'd like it.
40
+
41
+ ## Security properties (by design)
42
+
43
+ - **No SaaS** — local CLI + public databases (OSV / CISA KEV / GHSA) only.
44
+ - **Zero npm dependencies** — the tool ships no runtime deps; this is a deliberate security property, kept enforced by `npm pack` review in CI.
45
+ - **No silent fallback** — a provider/scanner miss is fail-closed (the install is denied). The one unavoidable exception is `jq` being absent — it is needed to parse the hook payload — and even then the guard reads the raw payload and denies anything that looks like a dependency install, falling back to an explicit allow-with-warning only for non-install commands. Every such outcome is recorded in `~/.safedeps/advisory.log`.
package/SKILL.md CHANGED
@@ -10,67 +10,97 @@ hooks:
10
10
 
11
11
  # Safedeps
12
12
 
13
- Safedeps protects development dependency installs with a three-phase flow:
13
+ Two gates, one skill. You (the agent) are the primary user — drive both:
14
14
 
15
- 1. **Phase 1Advisory gate (`safedeps check`)**: query OSV (canonical advisory truth), CISA KEV (overlay), and GitHub Advisory (enrichment) before install. Write the approved (ecosystem, package, version) tuple to `~/.safedeps/approved-specs/<hash>.json` with a 30-day TTL.
16
- 2. **Phase 2Hook enforcement (`scripts/safedeps-pre-guard.sh`)**: the PreToolUse hook does not call providers. It only checks the approved-spec ledger for the package/version in the about-to-run install command. Miss or expired → block with a structured message that names the exact `safedeps check` command to run next.
17
- 3. **Phase 3 — Post-install reorg (`scripts/safedeps-post-verify.sh`)**: v1 reorg engine rolls back when the lockfile diverges from the approved spec or install scripts look suspicious.
15
+ - **Install-time gate**clear every dependency install through an OSV-backed advisory check before it runs.
16
+ - **Secret-leak gate**stop a secret or a real `.env` from being committed (per-repo).
18
17
 
19
- ## CLI Reference
18
+ ---
20
19
 
20
+ ## Install-time gate
21
+
22
+ The hooks enforce this; you just run `check` first.
23
+
24
+ - **PreToolUse** blocks an install whose spec is not approved and quotes the exact `safedeps check` to run. On Claude Code it also rewrites an npm install with `--ignore-scripts`, so it runs **inert** until verified; Codex CLI uses detect-and-rollback.
25
+ - **PostToolUse** is the npm enforcement authority: it reads the real `package-lock.json` closure, checks every package against the ledger + an OSV batch, and **reorgs** (rolls back) anything unapproved, vulnerable, or with suspicious install scripts. Effect-primary is **npm-only**; pip/cargo/go/gem/maven/nuget use the command-gate + reorg model.
26
+
27
+ ### Before every install, run
28
+
29
+ ```bash
30
+ safedeps check <ecosystem> <pkg>@<range> --json
21
31
  ```
22
- safedeps check <ecosystem> <pkg>@<version|range> [--json]
23
- safedeps ledger [--json]
24
- safedeps revoke <hash> | <ecosystem> <pkg>@<version> | <pkg>@<version> [--reason <r>] [--json]
25
- safedeps re-check [--json]
26
- safedeps migrate [--keep-legacy]
27
- safedeps help [command]
28
- safedeps version
29
- ```
30
32
 
31
- **Ecosystems** (OSV-normalized): `npm`, `pypi`, `crates.io`, `go`, `rubygems`, `maven`, `nuget`.
33
+ - Ecosystems: `npm`, `pypi`, `crates.io`, `go`, `rubygems`, `maven`, `nuget`.
34
+ - Not on PATH? Use `~/.claude/skills/safedeps/bin/safedeps` (Claude) or `~/.codex/skills/safedeps/bin/safedeps` (Codex). Block messages already quote a runnable path.
35
+
36
+ ### Then act on `result`
32
37
 
33
- **Exit codes**:
38
+ | `result` | Action |
39
+ |---|---|
40
+ | `clean` / `already_approved` | Install. Use `install_hint` verbatim — it pins the approved version. |
41
+ | `patched_available` | Install `suggested_spec` instead (e.g. `^14.0.0` → `14.0.5`). |
42
+ | `cve_unpatched` | Do not install. Surface the CVEs, propose an alternative package. |
43
+ | `kev_hard_block` | Do not install. Recommend an alternative — actively exploited in the wild. |
44
+ | `provider_unavailable` | Do not install. OSV unreachable, no fresh cache. Retry later or tell the human. |
45
+ | `error` | Fix the spec (e.g. an unpublished version) and retry. |
34
46
 
35
- | Code | Meaning |
36
- |---:|---|
37
- | 0 | clean — spec approved, install is safe |
38
- | 2 | CVE found, no patched version available — install blocked, human decision required |
39
- | 3 | CISA KEV match — hard block, install must not proceed |
40
- | 4 | input error or provider unavailable (fail-closed) |
47
+ Install only after approval. If the approved version differs from your argument, the hook blocks again — re-narrow and retry.
41
48
 
42
- **Global flags**: `--json` (machine-readable, stable schema, no color, no spinner), `--no-color` (disable ANSI colors in human mode).
49
+ ### Rules
43
50
 
44
- ## For AI Agents
51
+ - Never bypass the gate. A provider miss is **fail-closed**, never fail-open.
52
+ - `kev_hard_block` is non-negotiable — recommend an alternative, never ask the human to override.
53
+ - Use `install_hint` / `suggested_spec` verbatim — a fresh range defeats the spec lock.
45
54
 
46
- You are the primary user of this skill when you propose `npm install`, `pip install`, `cargo add`, etc. Treat `safedeps check` as a mandatory pre-step. The PreToolUse hook will block the install if you skip it.
55
+ ### When the hook blocks you
47
56
 
48
- ### Workflow
57
+ The block message is `permissionDecisionReason`. The command quoted in backticks is runnable as-is — run it **verbatim**, parse the `--json` output, and retry the install with the approved version.
49
58
 
50
- 1. **Before issuing any install command**, call:
59
+ ---
51
60
 
52
- ```bash
53
- safedeps check <ecosystem> <pkg>@<range> --json
54
- ```
61
+ ## Secret-leak gate (per-repo)
55
62
 
56
- Use `--json` so the output is parseable. Read the `result` field.
63
+ The install-time gate is global; this one is per-repo and opt-in. Make it part of entering a repo, so the agent — not a later leak — is on guard.
64
+
65
+ ```bash
66
+ safedeps doctor # diagnose; exits non-zero if the secret lane has gaps
67
+ safedeps doctor --fix # scaffold the policy + activate the hooks (non-destructive)
68
+ ```
69
+
70
+ 1. On repo entry, run `safedeps doctor` (`--json` for the `checks` array).
71
+ 2. Gaps? Run `safedeps doctor --fix`. It scaffolds `.gitleaks.toml` (or `.gitleaks.private.toml`) and `.githooks/pre-commit`, then activates them. Existing repo files are never overwritten.
72
+ 3. Tune the scaffolded `.gitleaks.toml` for the repo — allowlist fixtures, add rules. You own the policy; safedeps runs it (gitleaks via `safedeps scan secrets`).
73
+
74
+ The pre-commit hook delegates to `safedeps scan secrets --staged` and is **fail-closed**: no scanner → it blocks the commit. The only bypass is the human's `git commit --no-verify`.
75
+
76
+ ---
77
+
78
+ ## CLI reference
79
+
80
+ ```
81
+ safedeps check <ecosystem> <pkg>@<version|range> [--json]
82
+ safedeps ledger [--json]
83
+ safedeps revoke <hash> | <ecosystem> <pkg>@<version> | <pkg>@<version> [--reason <r>] [--json]
84
+ safedeps re-check [--json]
85
+ safedeps doctor [--root <repo>] [--fix] [--json]
86
+ safedeps hooks <install|check|init> [--root <repo>]
87
+ safedeps scan secrets [--repo|--worktree|--staged] [--root <repo>]
88
+ safedeps audit [npm] [--root <repo>]
89
+ safedeps gates [run] [--root <repo>] [--strict] [--no-run]
90
+ safedeps migrate [--keep-legacy]
91
+ safedeps help [command]
92
+ safedeps version
93
+ ```
57
94
 
58
- 2. **Decide from `result`**:
95
+ **Global flags**: `--json` (stable machine-readable schema), `--no-color`.
59
96
 
60
- | `result` | Action |
61
- |---|---|
62
- | `clean` / `already_approved` | Proceed with the install. Use `install_hint` verbatim if present (it pins to the exact approved version). |
63
- | `patched_available` | Approved spec narrowed to a safe version. Replace your install argument with `suggested_spec`. Example: `^14.0.0` → `14.0.5`. |
64
- | `cve_unpatched` | Do **not** install. Surface the CVE list to the human, propose an alternative package. |
65
- | `kev_hard_block` | Do **not** install. Recommend an alternative module — the package is actively exploited in the wild. |
66
- | `provider_unavailable` | OSV is unreachable and there is no fresh cache. Do not install. Retry later or tell the human. |
67
- | `error` | Argument parsing failed. Fix and retry. |
97
+ **Exit codes**: `0` clean/approved · `2` CVE, no patch (human decision) · `3` CISA KEV hard block · `4` input error or provider unavailable (fail-closed).
68
98
 
69
- 3. **Issue the install** only after the spec is approved. The hook re-checks the ledger; if the approved version differs from your install argument, the hook will block again — re-narrow and retry.
99
+ ---
70
100
 
71
- ### JSON schema (stable, agent-facing)
101
+ ## JSON schemas (agent-facing)
72
102
 
73
- `check` result envelope, common fields:
103
+ `check`:
74
104
 
75
105
  ```json
76
106
  {
@@ -87,114 +117,27 @@ You are the primary user of this skill when you propose `npm install`, `pip inst
87
117
  }
88
118
  ```
89
119
 
90
- `patched_available` adds `"suggested_spec": "1.2.5"`. `kev_hard_block` retains the full `vulnerabilities` and `kev.matches` arrays for evidence. `cve_unpatched` retains `vulnerabilities`.
91
-
92
- `ledger` returns `{ "command": "ledger", "count": N, "specs": [...] }` where each spec has `hash`, `ecosystem`, `package`, `version`, `version_range`, `approved_at`, `expires_at`, `approved_by`, `expired` (bool), `revoked` (bool).
93
-
94
- `revoke` returns `{ "command": "revoke", "revoked": true, "reason": "...", "spec": {...} }`.
95
-
96
- `re-check` returns `{ "command": "re-check", "checked": N, "still_clean": N, "newly_vulnerable": [...], "kev_hit": [...], "revoked": [...] }`.
97
-
98
- `migrate` returns `{ "migrated": bool, "legacyRoot": "...", "targetRoot": "...", "copied": N, "skipped": N, "archivedAs": "..." }`.
99
-
100
- ### When the hook blocks you
120
+ - `patched_available` adds `suggested_spec`. `kev_hard_block` keeps `vulnerabilities` + `kev.matches`. `cve_unpatched` keeps `vulnerabilities`.
101
121
 
102
- `scripts/safedeps-pre-guard.sh` emits a Claude Code / Codex CLI hook decision using the modern PreToolUse schema:
122
+ `doctor`:
103
123
 
104
124
  ```json
105
125
  {
106
- "hookSpecificOutput": {
107
- "hookEventName": "PreToolUse",
108
- "permissionDecision": "deny",
109
- "permissionDecisionReason": "safedeps: install not approved (ecosystem=npm) — run `safedeps check npm @scope/pkg@^1.2.0` first, then retry the install using the approved version (see install_hint in the check output)."
110
- }
126
+ "command": "doctor",
127
+ "repo": "/path/to/repo",
128
+ "profile": "public | private",
129
+ "gaps": 0,
130
+ "ok": true,
131
+ "checks": [
132
+ { "lane": "secret | deps", "status": "ok | gap | na", "label": "...", "remedy": "safedeps hooks init ..." }
133
+ ]
111
134
  }
112
135
  ```
113
136
 
114
- Both engines surface `permissionDecisionReason` back to you as the block message. Run the `safedeps check ...` command quoted inside backticks, parse the `--json` output, and retry the install with the approved version.
115
-
116
- ### Hard rules
117
-
118
- - Never bypass the advisory gate. There is no silent fallback. If the provider is unreachable, fail-closed is the correct outcome.
119
- - KEV (`result: "kev_hard_block"`) is non-negotiable. Recommend an alternative; do not ask the human to override.
120
- - Use `install_hint` or `suggested_spec` verbatim. Do not rewrite the version with a fresh range — that defeats the spec lock.
121
-
122
- ## For Humans
123
-
124
- Default output is Korean + ANSI color + braille spinner during long calls. Designed for terminal use, no flags needed.
125
-
126
- ```bash
127
- $ safedeps check npm "@scope/pkg@^14.0.0"
128
- · 버전 해석 중 (@scope/pkg@^14.0.0)
129
- · 취약점 조회 중 (OSV / KEV / GHSA)
130
- ⚠ @scope/pkg@14.0.3 에 2 개 CVE — 안전 버전 14.0.5 으로 좁혀 재조회합니다.
131
- · 14.0.5 재조회 중
132
- ✓ @scope/pkg@14.0.5 승인 (until 2026-06-17T...)
133
- · ledger: sha256:abc123...
134
-
135
- $ safedeps ledger
136
- STATE ECOSYSTEM PACKAGE VERSION APPROVED EXPIRES HASH
137
- ACTIVE npm @scope/pkg 14.0.5 2026-05-18T... 2026-06-17T... sha256:abc...
138
-
139
- $ safedeps revoke @scope/pkg@14.0.5 --reason "team policy change"
140
- ✓ 취소: npm @scope/pkg@14.0.5
141
-
142
- $ safedeps re-check
143
- · 재검증 npm left-pad@1.3.0
144
- · 검증 완료: 1 개 중 1 개 clean
145
- ```
146
-
147
- ### Color legend
137
+ - `gaps` / `ok` reflect the per-repo secret-leak lane only; a missing global gate is a `deps` check but does not change `ok`.
148
138
 
149
- - gray` info / progress
150
- - `✓ green` — safe, approved
151
- - `⚠ yellow` — warning, manual decision required
152
- - `✗ red` — hard error or KEV block
139
+ `ledger`, `revoke`, `re-check`, `migrate` each return a `{ "command": "...", ... }` envelope; run `safedeps help <command>` for the fields.
153
140
 
154
- Disable color with `--no-color` or `NO_COLOR=1`. Non-TTY pipes also strip color automatically.
155
-
156
- ## Current Components
157
-
158
- - `bin/safedeps` — CLI (this entry point). Bash. Source-loads providers + ledger libs.
159
- - `lib/providers/providers.sh` — OSV / CISA KEV / GitHub Advisory adapters with a single query interface and 24h local cache under `~/.safedeps/cache/`.
160
- - `lib/ledger/ledger.sh` — approved spec JSON ledger I/O under `~/.safedeps/approved-specs/`, deterministic spec hash, TTL checks, atomic writes.
161
- - `scripts/safedeps-pre-guard.sh` — PreToolUse hook. v1 command pattern guard + ledger lookup for install commands.
162
- - `scripts/safedeps-post-verify.sh` — PostToolUse hook. v1 rollback engine + approved-spec diff.
163
- - `scripts/install/install-safedeps-hooks.mjs` — cross-engine installer. Symlinks `~/.claude/skills/safedeps` and `~/.codex/skills/safedeps`, idempotently patches `~/.claude/settings.json` and `~/.codex/hooks.json`. `--uninstall` removes both.
164
-
165
- ## Provider Failure Policy
166
-
167
- - OSV.dev is primary. Fresh cache may be used for 24h; cache miss or stale cache on OSV failure is fail-closed (`safedeps check` exits 4, hook blocks).
168
- - CISA KEV is an overlay. Stale or unavailable catalog is surfaced as a warning/status, not hidden.
169
- - GitHub Advisory is enrichment. Failure is fail-open only with an observable skipped status and advisory log entry.
170
-
171
- ## Installation
172
-
173
- The cross-engine installer registers the skill + hooks for Claude Code and Codex CLI in one shot. It is idempotent and backs up `settings.json` / `hooks.json` before writing.
174
-
175
- ```bash
176
- node scripts/install/install-safedeps-hooks.mjs # install
177
- node scripts/install/install-safedeps-hooks.mjs --uninstall # remove
178
- ```
179
-
180
- What it does:
181
-
182
- - Symlink the repo at `~/.claude/skills/safedeps` (when `~/.claude` exists) and `~/.codex/skills/safedeps` (when `~/.codex` exists).
183
- - Patch `~/.claude/settings.json` `hooks.PreToolUse[matcher=Bash]` and `hooks.PostToolUse[matcher=Bash]` with the canonical script paths.
184
- - Patch `~/.codex/hooks.json` with the same matcher and paths.
185
- - Optionally symlink `~/.local/bin/safedeps -> bin/safedeps` so the CLI is on PATH.
186
-
187
- Manual registration is also supported — see [Claude Code Hooks reference](https://code.claude.com/docs/en/hooks) and [Codex CLI Hooks](https://developers.openai.com/codex/hooks). The canonical script paths are:
188
-
189
- - PreToolUse: `<skills-root>/safedeps/scripts/safedeps-pre-guard.sh`
190
- - PostToolUse: `<skills-root>/safedeps/scripts/safedeps-post-verify.sh`
191
-
192
- ## State
193
-
194
- - Approved specs: `~/.safedeps/approved-specs/`
195
- - Provider cache: `~/.safedeps/cache/`
196
- - Reorg snapshots: `~/.safedeps/snapshots/`
197
- - Advisory log: `~/.safedeps/advisory.log`
198
- - Reorg log: `~/.safedeps/reorg.log`
141
+ ---
199
142
 
200
- `safedeps` is the canonical skill name, state namespace, and repository name.
143
+ Human terminal guide, install steps, and internal design: [`README.md`](./README.md), [`ARCHITECTURE.md`](./ARCHITECTURE.md). `safedeps` is the canonical skill name, state namespace, and repository name.