@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/ARCHITECTURE.md +273 -463
- package/README.ko.md +76 -12
- package/README.md +107 -38
- package/ROADMAP.md +123 -84
- package/SECURITY.md +45 -0
- package/SKILL.md +86 -143
- package/bin/safedeps +419 -52
- package/lib/gates/audit.sh +36 -0
- package/lib/gates/doctor.sh +212 -0
- package/lib/gates/hooks.sh +131 -0
- package/lib/gates/repo-profile.sh +60 -0
- package/lib/gates/scan.sh +94 -0
- package/lib/gates/templates/gitleaks.private.toml.tmpl +45 -0
- package/lib/gates/templates/gitleaks.toml.tmpl +43 -0
- package/lib/gates/templates/pre-commit.tmpl +49 -0
- package/lib/ledger/ledger.sh +94 -16
- package/lib/npm/closure.sh +115 -0
- package/lib/providers/providers.sh +248 -26
- package/package.json +2 -1
- package/scripts/install/install-safedeps-hooks.mjs +65 -23
- package/scripts/release-gates.sh +252 -0
- package/scripts/safedeps-post-verify.sh +185 -15
- package/scripts/safedeps-pre-guard.sh +309 -39
- package/scripts/test/e2e.sh +228 -4
- package/scripts/test/fixture-provider.mjs +21 -0
- package/scripts/test/smoke.sh +212 -10
package/ROADMAP.md
CHANGED
|
@@ -1,131 +1,170 @@
|
|
|
1
1
|
# Safedeps Roadmap
|
|
2
2
|
|
|
3
|
-
>
|
|
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
|
-
##
|
|
7
|
+
## Scope
|
|
8
8
|
|
|
9
|
-
|
|
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
|
-
|
|
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
|
-
|
|
17
|
-
|
|
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
|
-
##
|
|
25
|
+
## v2 — `safedeps` (shipped, v2.1.x)
|
|
22
26
|
|
|
23
|
-
|
|
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
|
-
|
|
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
|
-
-
|
|
33
|
-
-
|
|
34
|
-
|
|
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
|
-
|
|
57
|
+
### Release notes
|
|
39
58
|
|
|
40
|
-
|
|
41
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
##
|
|
94
|
+
## v2.3 — secret-leak lane doctor + scaffold (shipped)
|
|
89
95
|
|
|
90
|
-
|
|
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
|
-
|
|
110
|
+
### Verification
|
|
98
111
|
|
|
99
|
-
-
|
|
100
|
-
-
|
|
101
|
-
-
|
|
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
|
-
##
|
|
119
|
+
## v2.4 — fail-closed hooks + supply-chain hardening (shipped)
|
|
106
120
|
|
|
107
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
##
|
|
168
|
+
## History
|
|
130
169
|
|
|
131
|
-
- 2026-05-18: ROADMAP
|
|
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
|
-
|
|
13
|
+
Two gates, one skill. You (the agent) are the primary user — drive both:
|
|
14
14
|
|
|
15
|
-
|
|
16
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
49
|
+
### Rules
|
|
43
50
|
|
|
44
|
-
|
|
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
|
-
|
|
55
|
+
### When the hook blocks you
|
|
47
56
|
|
|
48
|
-
|
|
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
|
-
|
|
59
|
+
---
|
|
51
60
|
|
|
52
|
-
|
|
53
|
-
safedeps check <ecosystem> <pkg>@<range> --json
|
|
54
|
-
```
|
|
61
|
+
## Secret-leak gate (per-repo)
|
|
55
62
|
|
|
56
|
-
|
|
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
|
-
|
|
95
|
+
**Global flags**: `--json` (stable machine-readable schema), `--no-color`.
|
|
59
96
|
|
|
60
|
-
|
|
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
|
-
|
|
99
|
+
---
|
|
70
100
|
|
|
71
|
-
|
|
101
|
+
## JSON schemas (agent-facing)
|
|
72
102
|
|
|
73
|
-
`check
|
|
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 `
|
|
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
|
-
`
|
|
122
|
+
`doctor`:
|
|
103
123
|
|
|
104
124
|
```json
|
|
105
125
|
{
|
|
106
|
-
"
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
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
|
-
|
|
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
|
-
-
|
|
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
|
-
|
|
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.
|