@aldegad/safedeps 2.2.0 → 2.4.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/ARCHITECTURE.md +5 -1
- package/README.ko.md +42 -0
- package/README.md +42 -0
- package/ROADMAP.md +51 -2
- package/SECURITY.md +45 -0
- package/SKILL.md +84 -147
- package/bin/safedeps +36 -2
- package/lib/gates/doctor.sh +212 -0
- package/lib/gates/hooks.sh +40 -2
- 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/providers/providers.sh +4 -1
- package/package.json +2 -1
- package/scripts/install/install-safedeps-hooks.mjs +3 -0
- package/scripts/safedeps-post-verify.sh +74 -15
- package/scripts/safedeps-pre-guard.sh +73 -9
- package/scripts/test/e2e.sh +51 -3
- package/scripts/test/smoke.sh +106 -6
package/ARCHITECTURE.md
CHANGED
|
@@ -39,10 +39,12 @@ safedeps owns security gates at two distinct moments, under one skill. It absorb
|
|
|
39
39
|
```
|
|
40
40
|
|
|
41
41
|
- **Install-time lane** (sections 2–13 below) — advisory check, fast command guard, and the npm effect gate + reorg. Per-package and proactive.
|
|
42
|
-
- **Release-time lane** — the repo-tree checks from `security-release-gates` (secret scan, dependency audit, repo hook install/check, privacy profile), exposed under the `safedeps scan|audit|hooks|
|
|
42
|
+
- **Release-time lane** — the repo-tree checks from `security-release-gates` (secret scan, dependency audit, repo hook install/check, privacy profile), exposed under the `safedeps scan|audit|hooks|doctor` command namespace. Repo-specific policy (`.gitleaks.toml`, lockfiles) stays in the target repo; safedeps owns execution, install, and verification.
|
|
43
43
|
|
|
44
44
|
The two lanes differ in timing and scope (one package's effect before/after install vs. the whole repo before a release). They live under one umbrella but stay separated by command namespace.
|
|
45
45
|
|
|
46
|
+
**The secret-leak side of the release-time lane is per-repo and opt-in.** Its detection policy lives in the target repo, not in safedeps, so it does nothing until the repo provides a `.gitleaks` config and an active `.githooks/pre-commit`. `safedeps doctor` is the repo-entry diagnostic that closes that gap: it reports each piece of the secret-leak lane (`.gitleaks` policy, `pre-commit`, `core.hooksPath`, scanner availability) plus the global install-time gate, and exits non-zero when the per-repo lane has gaps. `safedeps doctor --fix` (= `safedeps hooks init` then `safedeps hooks install`) scaffolds a starter policy from `lib/gates/templates/` and activates the hooks. The scaffold is **non-destructive** — an existing repo-owned config is never overwritten — preserving the invariant that safedeps owns *execution*, not *policy*. The scaffolded `pre-commit` delegates to `safedeps scan secrets --staged` (a single canonical scanner path) and is fail-closed: an unresolvable `safedeps` or missing scanner blocks the commit rather than skipping silently.
|
|
47
|
+
|
|
46
48
|
**The effect-primary model is npm-only.** `pip`, `cargo`, `go`, `gem`, `maven`, and `nuget` stay on the v2.1 command-gate + reorg model until their closure resolvers land; they are not described as having PostToolUse closure authority.
|
|
47
49
|
|
|
48
50
|
### Install-time flow
|
|
@@ -332,6 +334,8 @@ OSV normalizes ecosystem names, so one API path covers all of them at advisory-c
|
|
|
332
334
|
| `lib/providers/` | OSV / KEV / GHSA (and optional NVD / deps.dev / Snyk) adapters behind one query interface. |
|
|
333
335
|
| `lib/ledger/` | Approved-spec ledger I/O — atomic write, hashing, TTL checks. |
|
|
334
336
|
| `lib/npm/closure.sh` | npm closure resolution from a lockfile. |
|
|
337
|
+
| `lib/gates/` | Release-time repo lane — `scan.sh` (gitleaks runner), `audit.sh` (npm lockfile audit), `hooks.sh` (`install`/`check`/`init`), `doctor.sh` (posture diagnose + `--fix`), `repo-profile.sh` (public/private resolution). Owns *execution*; the repo owns *policy*. |
|
|
338
|
+
| `lib/gates/templates/` | Starter `.gitleaks[.private].toml` + `.githooks/pre-commit`, scaffolded by `hooks init`. Seeds the repo owns and tunes — never overwritten on re-run. |
|
|
335
339
|
|
|
336
340
|
---
|
|
337
341
|
|
package/README.ko.md
CHANGED
|
@@ -23,6 +23,8 @@
|
|
|
23
23
|
- **install-time** (이 README 의 초점) — advisory check + approved-spec ledger + 빠른 PreToolUse guard + PostToolUse effect enforcement + post-install reorg. 패키지 단위, install 명령과 실제 lockfile effect 주변.
|
|
24
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
25
|
|
|
26
|
+
release-time lane 의 secret 누출 쪽은 **repo 별이고 opt-in** 이다. `safedeps doctor` 가 그 repo-entry 점검이다 — repo 의 `.gitleaks` policy, `.githooks/pre-commit`, 활성 `core.hooksPath`, scanner 가용성을 진단하고(전역 install-time gate 상태도 같이 보고), `safedeps doctor --fix` 가 시작 policy 를 scaffold(`safedeps hooks init`)하고 활성화(`safedeps hooks install`)한다. scaffold 는 비파괴적이라 repo 가 소유한 기존 `.gitleaks.toml` 은 덮어쓰지 않으며, pre-commit hook 은 `safedeps scan secrets --staged` 에 위임하고 fail-closed 다. [secret 누출 lane (repo 별)](#secret-누출-lane-repo-별) 참고.
|
|
27
|
+
|
|
26
28
|
---
|
|
27
29
|
|
|
28
30
|
## 어떻게 동작
|
|
@@ -72,6 +74,46 @@
|
|
|
72
74
|
|
|
73
75
|
---
|
|
74
76
|
|
|
77
|
+
## secret 누출 lane (repo 별)
|
|
78
|
+
|
|
79
|
+
install-time gate 는 전역이지만, secret 이나 진짜 `.env` 가 커밋되는 걸 막는 건 **repo 별이고 opt-in** 이다 — 탐지 policy 가 safedeps 가 아니라 각 repo 에 있기 때문이다. `safedeps doctor` 가 그 빈틈을 메우는 진입점이다.
|
|
80
|
+
|
|
81
|
+
```bash
|
|
82
|
+
# 이 repo 의 자세 진단 (read-only). secret lane 에 gap 이 있으면 non-zero 로 끝난다.
|
|
83
|
+
$ safedeps doctor
|
|
84
|
+
safedeps doctor — repo security posture
|
|
85
|
+
repo: /path/to/repo
|
|
86
|
+
profile: public
|
|
87
|
+
|
|
88
|
+
Secret-leak lane (per-repo)
|
|
89
|
+
✓ git worktree
|
|
90
|
+
✗ gitleaks config (.gitleaks.toml) → safedeps hooks init --root "/path/to/repo"
|
|
91
|
+
✗ .githooks/pre-commit (present) → safedeps hooks init --root "/path/to/repo"
|
|
92
|
+
✗ git hooks active (core.hooksPath=<unset>) → safedeps hooks install --root "/path/to/repo"
|
|
93
|
+
✓ secret scanner available (gitleaks)
|
|
94
|
+
|
|
95
|
+
Dependency-install gate (global, all repos)
|
|
96
|
+
✓ dependency-install gate installed (~/.claude/skills/safedeps)
|
|
97
|
+
|
|
98
|
+
3 gap(s) in the secret-leak lane.
|
|
99
|
+
Fix all at once: safedeps doctor --fix --root "/path/to/repo"
|
|
100
|
+
|
|
101
|
+
# 시작 policy scaffold + hook 활성화 (비파괴적).
|
|
102
|
+
$ safedeps doctor --fix
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
lane 구성 요소:
|
|
106
|
+
|
|
107
|
+
- **`safedeps hooks init`** 가 시작용 `.gitleaks.toml`(private repo 면 `.gitleaks.private.toml`)과 `.githooks/pre-commit` 을 scaffold 한다. 기존 파일은 덮지 않고 유지 — policy 는 repo 가 소유한다.
|
|
108
|
+
- **`safedeps hooks install`** 이 repo-local hook 을 활성화한다(`core.hooksPath = .githooks`).
|
|
109
|
+
- **pre-commit hook 은 `safedeps scan secrets --staged` 에 위임**하고 **fail-closed** 다: scanner(로컬 `gitleaks` 또는 Docker)가 못 돌면 silent skip 이 아니라 커밋을 막는다. 의도된 우회는 사람이 소유하는 `git commit --no-verify` 뿐이다.
|
|
110
|
+
|
|
111
|
+
scaffold 된 `.gitleaks.toml` 은 **네가 손보는 시작점**이다: gitleaks 기본 ruleset 을 extend 하고, 값이 할당된 `.env` 커밋을 잡는 rule 을 더하며(`.env.example`/`.sample`/`.template` 변형은 allowlist), fixture 용 repo-owned `[allowlist]` 블록을 남겨둔다. safedeps 는 *실행* — `safedeps scan secrets` 로 gitleaks 구동 — 만 소유하고 policy 내용은 소유하지 않는다.
|
|
112
|
+
|
|
113
|
+
`safedeps doctor --json` 은 `{ command, repo, profile, gaps, ok, checks[] }` 를 돌려준다; `gaps`/`ok` 는 repo 별 secret 누출 lane 만 반영한다.
|
|
114
|
+
|
|
115
|
+
---
|
|
116
|
+
|
|
75
117
|
## 설치
|
|
76
118
|
|
|
77
119
|
### 1) GitHub clone (skill 본체 설치, canonical)
|
package/README.md
CHANGED
|
@@ -26,6 +26,8 @@ Use the GitHub release when you want the full skill/hook source tree as the cano
|
|
|
26
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
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
28
|
|
|
29
|
+
The secret-leak side of the release-time lane is **per-repo and opt-in**. `safedeps doctor` is its repo-entry check: it diagnoses the repo's `.gitleaks` policy, `.githooks/pre-commit`, the active `core.hooksPath`, and scanner availability (and reports the global install-time gate too), then `safedeps doctor --fix` scaffolds a starter policy (`safedeps hooks init`) and activates it (`safedeps hooks install`). The scaffold is non-destructive — an existing repo-owned `.gitleaks.toml` is never overwritten — and the pre-commit hook delegates to `safedeps scan secrets --staged`, fail-closed. See [Secret-Leak Lane (per-repo)](#secret-leak-lane-per-repo).
|
|
30
|
+
|
|
29
31
|
## How It Works
|
|
30
32
|
|
|
31
33
|
`safedeps` works in two moves around every install:
|
|
@@ -141,6 +143,44 @@ After the install command completes, the verify hook analyzes what changed. For
|
|
|
141
143
|
| Dependency confusion | >50 new dependencies in a single install | PostToolUse effect verify | **Reorg** |
|
|
142
144
|
| Native binaries | Compiled executables in `node_modules/.bin/` | PostToolUse effect verify | **Reorg** |
|
|
143
145
|
|
|
146
|
+
## Secret-Leak Lane (per-repo)
|
|
147
|
+
|
|
148
|
+
The install-time gate is global, but stopping a secret or a real `.env` from being committed is **per-repo** and stays opt-in — its detection policy lives in each repo, not in safedeps. `safedeps doctor` is the entry point that closes that gap.
|
|
149
|
+
|
|
150
|
+
```bash
|
|
151
|
+
# Diagnose this repo's posture (read-only). Exits non-zero if the secret lane has gaps.
|
|
152
|
+
$ safedeps doctor
|
|
153
|
+
safedeps doctor — repo security posture
|
|
154
|
+
repo: /path/to/repo
|
|
155
|
+
profile: public
|
|
156
|
+
|
|
157
|
+
Secret-leak lane (per-repo)
|
|
158
|
+
✓ git worktree
|
|
159
|
+
✗ gitleaks config (.gitleaks.toml) → safedeps hooks init --root "/path/to/repo"
|
|
160
|
+
✗ .githooks/pre-commit (present) → safedeps hooks init --root "/path/to/repo"
|
|
161
|
+
✗ git hooks active (core.hooksPath=<unset>) → safedeps hooks install --root "/path/to/repo"
|
|
162
|
+
✓ secret scanner available (gitleaks)
|
|
163
|
+
|
|
164
|
+
Dependency-install gate (global, all repos)
|
|
165
|
+
✓ dependency-install gate installed (~/.claude/skills/safedeps)
|
|
166
|
+
|
|
167
|
+
3 gap(s) in the secret-leak lane.
|
|
168
|
+
Fix all at once: safedeps doctor --fix --root "/path/to/repo"
|
|
169
|
+
|
|
170
|
+
# Scaffold the starter policy + activate the hooks (non-destructive).
|
|
171
|
+
$ safedeps doctor --fix
|
|
172
|
+
```
|
|
173
|
+
|
|
174
|
+
What the lane is made of:
|
|
175
|
+
|
|
176
|
+
- **`safedeps hooks init`** scaffolds a starter `.gitleaks.toml` (or `.gitleaks.private.toml` for a private repo) and a `.githooks/pre-commit`. Existing files are kept, never overwritten — the repo owns the policy.
|
|
177
|
+
- **`safedeps hooks install`** activates the repo-local hooks (`core.hooksPath = .githooks`).
|
|
178
|
+
- The **pre-commit hook delegates to `safedeps scan secrets --staged`** and is **fail-closed**: if the scanner (local `gitleaks` or Docker) cannot run, it blocks the commit instead of skipping silently. The only intentional bypass is `git commit --no-verify`, which the human owns.
|
|
179
|
+
|
|
180
|
+
The scaffolded `.gitleaks.toml` is a **starter you tune**: it extends gitleaks' default ruleset, adds a rule for a committed `.env` with an assigned secret (the `.env.example`/`.sample`/`.template` variants are allowlisted), and leaves a repo-owned `[allowlist]` block for your fixtures. safedeps owns *execution* — running gitleaks via `safedeps scan secrets` — not the policy content.
|
|
181
|
+
|
|
182
|
+
`safedeps doctor --json` returns `{ command, repo, profile, gaps, ok, checks[] }`; `gaps`/`ok` reflect the per-repo secret-leak lane only.
|
|
183
|
+
|
|
144
184
|
## Installation
|
|
145
185
|
|
|
146
186
|
### Prerequisites
|
|
@@ -307,6 +347,8 @@ safedeps/
|
|
|
307
347
|
lib/
|
|
308
348
|
providers/ # OSV / CISA KEV / GHSA adapters
|
|
309
349
|
ledger/ # approved-spec ledger
|
|
350
|
+
npm/ # lockfile closure resolver
|
|
351
|
+
gates/ # repo-tree lane: scan / audit / hooks / doctor + templates/
|
|
310
352
|
scripts/
|
|
311
353
|
safedeps-pre-guard.sh # PreToolUse hook -- advisory ledger UX + snapshots
|
|
312
354
|
safedeps-post-verify.sh # PostToolUse hook -- npm primary effect verification + reorg
|
package/ROADMAP.md
CHANGED
|
@@ -56,7 +56,7 @@ The internal engine keeps the v1 `reorg-guard` assets.
|
|
|
56
56
|
|
|
57
57
|
### Release notes
|
|
58
58
|
|
|
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.
|
|
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.1).
|
|
60
60
|
- `npm test` runs the release smoke suite; the full fixture E2E lives under `v2.1-tests`.
|
|
61
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.
|
|
62
62
|
|
|
@@ -87,7 +87,56 @@ This phase covers npm lockfile closure only. pip / cargo / go / gem / maven / nu
|
|
|
87
87
|
|
|
88
88
|
### Current focus
|
|
89
89
|
|
|
90
|
-
1. `v2.2.0-release`:
|
|
90
|
+
1. `v2.2.0-release`: merged `safedeps-security-hardening`, tagged `v2.2.0` (GitHub release + `npm publish`).
|
|
91
|
+
|
|
92
|
+
---
|
|
93
|
+
|
|
94
|
+
## v2.3 — secret-leak lane doctor + scaffold (shipped)
|
|
95
|
+
|
|
96
|
+
Status: shipped as v2.3.0.
|
|
97
|
+
|
|
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.
|
|
109
|
+
|
|
110
|
+
### Verification
|
|
111
|
+
|
|
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
|
|
116
|
+
|
|
117
|
+
---
|
|
118
|
+
|
|
119
|
+
## v2.4 — fail-closed hooks + supply-chain hardening (shipped)
|
|
120
|
+
|
|
121
|
+
Status: shipped as v2.4.0.
|
|
122
|
+
|
|
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 denies a likely install (best-effort fail-closed) and logs it; only non-install commands fall through
|
|
133
|
+
- a missing ledger library denies fail-closed instead of falling through to allow
|
|
134
|
+
- ShellCheck (`--severity=error`) is clean across all shell sources
|
|
135
|
+
- existing smoke + e2e regression suite remains green on both Linux and macOS
|
|
136
|
+
|
|
137
|
+
### v2.4.1 — concurrent-install race fix (#5)
|
|
138
|
+
|
|
139
|
+
The pending state PreToolUse hands to PostToolUse was a single global `current_state` file, so two installs overlapping in one project could clobber each other and the effect gate could verify the wrong install (or skip one). Pending state is now keyed **per install** — `dir_hash` + a hash of the command with the inert-install rewrite normalized out — so PreToolUse and PostToolUse of the same install agree on a key while concurrent installs stay isolated. A concurrency harness (two installs → two pending files; a post consumes only its own) guards it.
|
|
91
140
|
|
|
92
141
|
---
|
|
93
142
|
|
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,72 +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 — Effect enforcement + reorg (`scripts/safedeps-post-verify.sh`)**: PostToolUse is the primary enforcement surface for npm. It compares the actual `package-lock.json` closure against direct ledger entries and their `transitive_specs`, re-checks the closure with OSV batch, and rolls back when any package is unapproved/vulnerable or install scripts look suspicious. After a verified inert install it runs `npm rebuild` so the now-trusted lifecycle scripts execute.
|
|
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
|
|
|
21
|
-
##
|
|
20
|
+
## Install-time gate
|
|
22
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
|
|
23
31
|
```
|
|
24
|
-
safedeps check <ecosystem> <pkg>@<version|range> [--json]
|
|
25
|
-
safedeps ledger [--json]
|
|
26
|
-
safedeps revoke <hash> | <ecosystem> <pkg>@<version> | <pkg>@<version> [--reason <r>] [--json]
|
|
27
|
-
safedeps re-check [--json]
|
|
28
|
-
safedeps migrate [--keep-legacy]
|
|
29
|
-
safedeps gates [run] [--root <repo>] [--strict] [--no-run]
|
|
30
|
-
safedeps help [command]
|
|
31
|
-
safedeps version
|
|
32
|
-
```
|
|
33
32
|
|
|
34
|
-
|
|
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`
|
|
35
37
|
|
|
36
|
-
|
|
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. |
|
|
37
46
|
|
|
38
|
-
|
|
39
|
-
|---:|---|
|
|
40
|
-
| 0 | clean — spec approved, install is safe |
|
|
41
|
-
| 2 | CVE found, no patched version available — install blocked, human decision required |
|
|
42
|
-
| 3 | CISA KEV match — hard block, install must not proceed |
|
|
43
|
-
| 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.
|
|
44
48
|
|
|
45
|
-
|
|
49
|
+
### Rules
|
|
46
50
|
|
|
47
|
-
|
|
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.
|
|
48
54
|
|
|
49
|
-
|
|
55
|
+
### When the hook blocks you
|
|
50
56
|
|
|
51
|
-
|
|
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.
|
|
52
58
|
|
|
53
|
-
|
|
59
|
+
---
|
|
54
60
|
|
|
55
|
-
|
|
56
|
-
safedeps check <ecosystem> <pkg>@<range> --json
|
|
57
|
-
```
|
|
61
|
+
## Secret-leak gate (per-repo)
|
|
58
62
|
|
|
59
|
-
|
|
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.
|
|
60
64
|
|
|
61
|
-
|
|
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
|
+
```
|
|
62
94
|
|
|
63
|
-
|
|
95
|
+
**Global flags**: `--json` (stable machine-readable schema), `--no-color`.
|
|
64
96
|
|
|
65
|
-
|
|
66
|
-
|---|---|
|
|
67
|
-
| `clean` / `already_approved` | Proceed with the install. Use `install_hint` verbatim if present (it pins to the exact approved version). |
|
|
68
|
-
| `patched_available` | Approved spec narrowed to a safe version. Replace your install argument with `suggested_spec`. Example: `^14.0.0` → `14.0.5`. |
|
|
69
|
-
| `cve_unpatched` | Do **not** install. Surface the CVE list to the human, propose an alternative package. |
|
|
70
|
-
| `kev_hard_block` | Do **not** install. Recommend an alternative module — the package is actively exploited in the wild. |
|
|
71
|
-
| `provider_unavailable` | OSV is unreachable and there is no fresh cache. Do not install. Retry later or tell the human. |
|
|
72
|
-
| `error` | Argument parsing or version/closure resolution failed (e.g. an unpublished version). Fix the spec 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).
|
|
73
98
|
|
|
74
|
-
|
|
99
|
+
---
|
|
75
100
|
|
|
76
|
-
|
|
101
|
+
## JSON schemas (agent-facing)
|
|
77
102
|
|
|
78
|
-
`check
|
|
103
|
+
`check`:
|
|
79
104
|
|
|
80
105
|
```json
|
|
81
106
|
{
|
|
@@ -92,115 +117,27 @@ You are the primary user of this skill when you propose `npm install`, `pip inst
|
|
|
92
117
|
}
|
|
93
118
|
```
|
|
94
119
|
|
|
95
|
-
`patched_available` adds `
|
|
96
|
-
|
|
97
|
-
`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).
|
|
98
|
-
|
|
99
|
-
`revoke` returns `{ "command": "revoke", "revoked": true, "reason": "...", "spec": {...} }`.
|
|
100
|
-
|
|
101
|
-
`re-check` returns `{ "command": "re-check", "checked": N, "still_clean": N, "newly_vulnerable": [...], "kev_hit": [...], "revoked": [...] }`.
|
|
102
|
-
|
|
103
|
-
`migrate` returns `{ "migrated": bool, "legacyRoot": "...", "targetRoot": "...", "copied": N, "skipped": N, "archivedAs": "..." }`.
|
|
104
|
-
|
|
105
|
-
### When the hook blocks you
|
|
120
|
+
- `patched_available` adds `suggested_spec`. `kev_hard_block` keeps `vulnerabilities` + `kev.matches`. `cve_unpatched` keeps `vulnerabilities`.
|
|
106
121
|
|
|
107
|
-
`
|
|
122
|
+
`doctor`:
|
|
108
123
|
|
|
109
124
|
```json
|
|
110
125
|
{
|
|
111
|
-
"
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
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
|
+
]
|
|
116
134
|
}
|
|
117
135
|
```
|
|
118
136
|
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
### Hard rules
|
|
122
|
-
|
|
123
|
-
- Never bypass the advisory gate. There is no silent fallback. If the provider is unreachable, fail-closed is the correct outcome.
|
|
124
|
-
- KEV (`result: "kev_hard_block"`) is non-negotiable. Recommend an alternative; do not ask the human to override.
|
|
125
|
-
- Use `install_hint` or `suggested_spec` verbatim. Do not rewrite the version with a fresh range — that defeats the spec lock.
|
|
126
|
-
|
|
127
|
-
## For Humans
|
|
128
|
-
|
|
129
|
-
Default output is Korean + ANSI color + braille spinner during long calls. Designed for terminal use, no flags needed.
|
|
130
|
-
|
|
131
|
-
```bash
|
|
132
|
-
$ safedeps check npm "@scope/pkg@^14.0.0"
|
|
133
|
-
· 버전 해석 중 (@scope/pkg@^14.0.0)
|
|
134
|
-
· 취약점 조회 중 (OSV / KEV / GHSA)
|
|
135
|
-
⚠ @scope/pkg@14.0.3 에 2 개 CVE — 안전 버전 14.0.5 으로 좁혀 재조회합니다.
|
|
136
|
-
· 14.0.5 재조회 중
|
|
137
|
-
✓ @scope/pkg@14.0.5 승인 (until 2026-06-17T...)
|
|
138
|
-
· ledger: sha256:abc123...
|
|
139
|
-
|
|
140
|
-
$ safedeps ledger
|
|
141
|
-
STATE ECOSYSTEM PACKAGE VERSION APPROVED EXPIRES HASH
|
|
142
|
-
ACTIVE npm @scope/pkg 14.0.5 2026-05-18T... 2026-06-17T... sha256:abc...
|
|
143
|
-
|
|
144
|
-
$ safedeps revoke @scope/pkg@14.0.5 --reason "team policy change"
|
|
145
|
-
✓ 취소: npm @scope/pkg@14.0.5
|
|
146
|
-
|
|
147
|
-
$ safedeps re-check
|
|
148
|
-
· 재검증 npm left-pad@1.3.0
|
|
149
|
-
· 검증 완료: 1 개 중 1 개 clean
|
|
150
|
-
```
|
|
151
|
-
|
|
152
|
-
### 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`.
|
|
153
138
|
|
|
154
|
-
-
|
|
155
|
-
- `✓ green` — safe, approved
|
|
156
|
-
- `⚠ yellow` — warning, manual decision required
|
|
157
|
-
- `✗ red` — hard error or KEV block
|
|
139
|
+
`ledger`, `revoke`, `re-check`, `migrate` each return a `{ "command": "...", ... }` envelope; run `safedeps help <command>` for the fields.
|
|
158
140
|
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
## Current Components
|
|
162
|
-
|
|
163
|
-
- `bin/safedeps` — CLI (this entry point). Bash. Source-loads providers + ledger libs.
|
|
164
|
-
- `lib/providers/providers.sh` — OSV / CISA KEV / GitHub Advisory adapters with a single query interface and 24h local cache under `~/.safedeps/cache/`.
|
|
165
|
-
- `lib/ledger/ledger.sh` — approved spec JSON ledger I/O under `~/.safedeps/approved-specs/`, deterministic spec hash, TTL checks, atomic writes.
|
|
166
|
-
- `scripts/safedeps-pre-guard.sh` — PreToolUse hook. v1 command pattern guard + ledger lookup for install commands.
|
|
167
|
-
- `scripts/safedeps-post-verify.sh` — PostToolUse hook. v1 rollback engine + npm effect gate (lockfile closure vs ledger + OSV batch).
|
|
168
|
-
- `lib/npm/closure.sh` — npm lockfile closure extraction and script-free temp lockfile resolver.
|
|
169
|
-
- `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.
|
|
170
|
-
|
|
171
|
-
## Provider Failure Policy
|
|
172
|
-
|
|
173
|
-
- 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).
|
|
174
|
-
- CISA KEV is an overlay. Stale or unavailable catalog is surfaced as a warning/status, not hidden.
|
|
175
|
-
- GitHub Advisory is enrichment. Failure is fail-open only with an observable skipped status and advisory log entry.
|
|
176
|
-
|
|
177
|
-
## Installation
|
|
178
|
-
|
|
179
|
-
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.
|
|
180
|
-
|
|
181
|
-
```bash
|
|
182
|
-
node scripts/install/install-safedeps-hooks.mjs # install
|
|
183
|
-
node scripts/install/install-safedeps-hooks.mjs --uninstall # remove
|
|
184
|
-
```
|
|
185
|
-
|
|
186
|
-
What it does:
|
|
187
|
-
|
|
188
|
-
- Symlink the repo at `~/.claude/skills/safedeps` (when `~/.claude` exists) and `~/.codex/skills/safedeps` (when `~/.codex` exists).
|
|
189
|
-
- Patch `~/.claude/settings.json` `hooks.PreToolUse[matcher=Bash]` and `hooks.PostToolUse[matcher=Bash]` with the canonical script paths.
|
|
190
|
-
- Patch `~/.codex/hooks.json` with the same matcher and paths.
|
|
191
|
-
- Optionally symlink `~/.local/bin/safedeps -> bin/safedeps` (via `--link-bin`) for a clean `safedeps` on PATH. **Not required** — the hook block messages and this skill fall back to the absolute skill-relative path, so the gate is fully self-contained without any PATH setup.
|
|
192
|
-
|
|
193
|
-
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:
|
|
194
|
-
|
|
195
|
-
- PreToolUse: `<skills-root>/safedeps/scripts/safedeps-pre-guard.sh`
|
|
196
|
-
- PostToolUse: `<skills-root>/safedeps/scripts/safedeps-post-verify.sh`
|
|
197
|
-
|
|
198
|
-
## State
|
|
199
|
-
|
|
200
|
-
- Approved specs: `~/.safedeps/approved-specs/`
|
|
201
|
-
- Provider cache: `~/.safedeps/cache/`
|
|
202
|
-
- Reorg snapshots: `~/.safedeps/snapshots/`
|
|
203
|
-
- Advisory log: `~/.safedeps/advisory.log`
|
|
204
|
-
- Reorg log: `~/.safedeps/reorg.log`
|
|
141
|
+
---
|
|
205
142
|
|
|
206
|
-
`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.
|
package/bin/safedeps
CHANGED
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
|
|
8
8
|
set -euo pipefail
|
|
9
9
|
|
|
10
|
-
SAFEDEPS_VERSION="2.
|
|
10
|
+
SAFEDEPS_VERSION="2.4.1"
|
|
11
11
|
|
|
12
12
|
# ---- repo / lib bootstrap ----------------------------------------------------
|
|
13
13
|
|
|
@@ -1027,6 +1027,11 @@ cmd_hooks() {
|
|
|
1027
1027
|
exec bash "${SAFEDEPS_REPO_DIR}/lib/gates/hooks.sh" "$@"
|
|
1028
1028
|
}
|
|
1029
1029
|
|
|
1030
|
+
cmd_doctor() {
|
|
1031
|
+
exec env SAFEDEPS_JSON_MODE="${SAFEDEPS_JSON_MODE}" \
|
|
1032
|
+
bash "${SAFEDEPS_REPO_DIR}/lib/gates/doctor.sh" "$@"
|
|
1033
|
+
}
|
|
1034
|
+
|
|
1030
1035
|
# ---- help / version ----------------------------------------------------------
|
|
1031
1036
|
|
|
1032
1037
|
cmd_version() {
|
|
@@ -1081,6 +1086,33 @@ safedeps migrate [--keep-legacy]
|
|
|
1081
1086
|
|
|
1082
1087
|
Migrate legacy ~/.npm-reorg-guard state into ~/.safedeps.
|
|
1083
1088
|
By default the legacy directory is archived to remove the old active truth path.
|
|
1089
|
+
EOF
|
|
1090
|
+
;;
|
|
1091
|
+
doctor)
|
|
1092
|
+
cat <<'EOF'
|
|
1093
|
+
safedeps doctor [--root <repo>] [--fix] [--json]
|
|
1094
|
+
|
|
1095
|
+
Diagnose this repo's security posture: the per-repo secret-leak lane
|
|
1096
|
+
(.gitleaks policy + .githooks/pre-commit + active core.hooksPath + an
|
|
1097
|
+
available scanner) plus the global dependency-install gate. Read-only by
|
|
1098
|
+
default; exits 1 when the secret-leak lane has gaps.
|
|
1099
|
+
|
|
1100
|
+
--fix Scaffold the missing policy (hooks init) and activate it (hooks
|
|
1101
|
+
install). Non-destructive: existing repo-owned files are kept.
|
|
1102
|
+
|
|
1103
|
+
The scaffolded .gitleaks.toml is a STARTER you own and tune; safedeps owns
|
|
1104
|
+
execution (running gitleaks via `safedeps scan secrets`), not the policy.
|
|
1105
|
+
EOF
|
|
1106
|
+
;;
|
|
1107
|
+
hooks)
|
|
1108
|
+
cat <<'EOF'
|
|
1109
|
+
safedeps hooks <install|check|init> [--root <repo>] [--hooks-path <dir>] [--auto]
|
|
1110
|
+
|
|
1111
|
+
init Scaffold the repo's secret-leak policy from starter templates
|
|
1112
|
+
(.gitleaks[.private].toml + .githooks/pre-commit). Non-destructive.
|
|
1113
|
+
install Activate repo-local git hooks (sets core.hooksPath). Requires the
|
|
1114
|
+
hook file to exist — run `init` first if the repo has none.
|
|
1115
|
+
check Verify the hooks are active, executable, and a scanner is available.
|
|
1084
1116
|
EOF
|
|
1085
1117
|
;;
|
|
1086
1118
|
*)
|
|
@@ -1099,7 +1131,8 @@ COMMANDS
|
|
|
1099
1131
|
gates [run] Release-time repo gates: secret scan, dep audit, hook/CI check.
|
|
1100
1132
|
scan secrets [--repo|--worktree|--staged] Run gitleaks secret scan (repo profile aware).
|
|
1101
1133
|
audit [npm] Run npm lockfile audit.
|
|
1102
|
-
hooks <install|check>
|
|
1134
|
+
hooks <install|check|init> Install/verify/scaffold repo-local git hooks.
|
|
1135
|
+
doctor [--fix] Diagnose this repo's secret-leak lane (--fix scaffolds + activates).
|
|
1103
1136
|
help [command] Show help.
|
|
1104
1137
|
version Print version.
|
|
1105
1138
|
|
|
@@ -1163,6 +1196,7 @@ main() {
|
|
|
1163
1196
|
scan) cmd_scan "$@" ;;
|
|
1164
1197
|
audit) cmd_audit "$@" ;;
|
|
1165
1198
|
hooks) cmd_hooks "$@" ;;
|
|
1199
|
+
doctor) cmd_doctor "$@" ;;
|
|
1166
1200
|
help) cmd_help "$@" ;;
|
|
1167
1201
|
version) cmd_version ;;
|
|
1168
1202
|
*)
|