@westbayberry/dg 2.0.11 → 2.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +216 -226
- package/dist/agents/claude-code.js +113 -0
- package/dist/agents/codex.js +65 -0
- package/dist/agents/copilot-cli.js +115 -0
- package/dist/agents/cursor.js +113 -0
- package/dist/agents/gemini.js +107 -0
- package/dist/agents/persistence.js +285 -0
- package/dist/agents/registry.js +127 -0
- package/dist/agents/types.js +1 -0
- package/dist/agents/windsurf.js +93 -0
- package/dist/api/analyze.js +6 -4
- package/dist/audit/detectors.js +0 -11
- package/dist/audit/events.js +5 -21
- package/dist/audit-ui/AuditApp.js +2 -0
- package/dist/audit-ui/components/AuditResultsView.js +55 -92
- package/dist/audit-ui/export.js +0 -4
- package/dist/audit-ui/format.js +0 -3
- package/dist/audit-ui/launch.js +10 -1
- package/dist/auth/device-login.js +4 -5
- package/dist/auth/login-app.js +7 -7
- package/dist/auth/store.js +8 -3
- package/dist/bin/dg.js +59 -52
- package/dist/commands/agents.js +231 -0
- package/dist/commands/audit.js +22 -3
- package/dist/commands/config.js +26 -10
- package/dist/commands/cooldown.js +389 -0
- package/dist/commands/decisions.js +169 -0
- package/dist/commands/doctor.js +1 -1
- package/dist/commands/help.js +1 -1
- package/dist/commands/licenses.js +10 -22
- package/dist/commands/logout.js +4 -11
- package/dist/commands/router.js +8 -4
- package/dist/commands/sbom.js +206 -0
- package/dist/commands/scan.js +3 -2
- package/dist/commands/service.js +32 -13
- package/dist/commands/setup.js +197 -27
- package/dist/commands/status.js +5 -2
- package/dist/commands/types.js +1 -0
- package/dist/commands/update.js +17 -8
- package/dist/commands/verify.js +8 -5
- package/dist/config/settings.js +154 -65
- package/dist/decisions/apply.js +128 -0
- package/dist/decisions/remember-prompt.js +92 -0
- package/dist/export-ui/ExportDialog.js +198 -0
- package/dist/install-ui/LiveInstall.js +2 -2
- package/dist/install-ui/block-render.js +21 -4
- package/dist/install-ui/prep-spinner.js +32 -0
- package/dist/install-ui/prompt.js +14 -0
- package/dist/launcher/agent-check.js +466 -0
- package/dist/launcher/agent-hook-exec.js +70 -0
- package/dist/launcher/agent-hook-io.js +31 -0
- package/dist/launcher/cargo-cache.js +40 -0
- package/dist/launcher/classify.js +17 -6
- package/dist/launcher/env.js +71 -24
- package/dist/launcher/install-preflight.js +167 -17
- package/dist/launcher/live-install.js +25 -5
- package/dist/launcher/output-redaction.js +7 -4
- package/dist/launcher/preflight-prompt.js +43 -3
- package/dist/launcher/run.js +100 -86
- package/dist/launcher/spawn-invocation.js +21 -0
- package/dist/policy/cooldown.js +117 -0
- package/dist/policy/evaluate.js +5 -21
- package/dist/policy/pypi-name.js +17 -0
- package/dist/presentation/mode.js +3 -2
- package/dist/presentation/package-page.js +9 -0
- package/dist/presentation/provenance.js +23 -0
- package/dist/presentation/theme.js +7 -7
- package/dist/project/dgfile.js +446 -0
- package/dist/proxy/auth.js +42 -0
- package/dist/proxy/ca.js +29 -9
- package/dist/proxy/cooldown-exemptions-file.js +33 -0
- package/dist/proxy/enforcement.js +57 -17
- package/dist/proxy/metadata-map.js +66 -4
- package/dist/proxy/preverified.js +55 -0
- package/dist/proxy/server.js +473 -45
- package/dist/proxy/worker.js +16 -1
- package/dist/publish-set/collect.js +1 -4
- package/dist/publish-set/npm.js +8 -5
- package/dist/publish-set/pack.js +9 -3
- package/dist/runtime/cli.js +0 -4
- package/dist/runtime/fatal.js +31 -0
- package/dist/runtime/first-run.js +12 -11
- package/dist/runtime/node-version.js +43 -6
- package/dist/runtime/nudges.js +35 -2
- package/dist/sbom/cyclonedx.js +211 -0
- package/dist/sbom-ui/SbomApp.js +158 -0
- package/dist/sbom-ui/components/SbomHeader.js +32 -0
- package/dist/sbom-ui/components/SbomList.js +52 -0
- package/dist/sbom-ui/inventory.js +128 -0
- package/dist/sbom-ui/launch.js +51 -0
- package/dist/sbom-ui/run.js +55 -0
- package/dist/sbom-ui/store.js +26 -0
- package/dist/scan/collect.js +10 -6
- package/dist/scan/command.js +51 -17
- package/dist/scan/discovery.js +11 -2
- package/dist/scan/render.js +63 -8
- package/dist/scan/scanner-report.js +42 -9
- package/dist/scan/staged.js +71 -11
- package/dist/scan-ui/LegacyApp.js +12 -16
- package/dist/scan-ui/alt-screen.js +5 -8
- package/dist/scan-ui/components/InteractiveResultsView.js +193 -123
- package/dist/scan-ui/components/ProgressBar.js +3 -14
- package/dist/scan-ui/components/ProjectSelector.js +1 -1
- package/dist/scan-ui/components/ScoreHeader.js +2 -3
- package/dist/scan-ui/components/SetupBanner.js +0 -6
- package/dist/scan-ui/format-helpers.js +61 -5
- package/dist/scan-ui/hooks/useResizeRepaint.js +25 -0
- package/dist/scan-ui/hooks/useScan.js +46 -4
- package/dist/scan-ui/launch.js +7 -4
- package/dist/scan-ui/shims.js +14 -4
- package/dist/scripts/detect.js +158 -0
- package/dist/scripts/gate.js +170 -0
- package/dist/service/state.js +27 -8
- package/dist/service/trust-refresh.js +92 -0
- package/dist/service/worker.js +23 -1
- package/dist/setup/activate-shell.js +28 -0
- package/dist/setup/git-hook.js +49 -4
- package/dist/setup/plan.js +98 -29
- package/dist/setup-ui/gate.js +39 -0
- package/dist/setup-ui/offer.js +42 -0
- package/dist/setup-ui/selector.js +27 -0
- package/dist/setup-ui/tasks.js +56 -0
- package/dist/setup-ui/wizard.js +225 -0
- package/dist/state/cooldown-held.js +66 -0
- package/dist/state/index.js +1 -0
- package/dist/state/locks.js +4 -2
- package/dist/state/store.js +2 -1
- package/dist/util/external-tool.js +25 -0
- package/dist/util/git.js +10 -3
- package/dist/util/json-file.js +24 -0
- package/dist/util/report-writer.js +57 -0
- package/dist/util/tty-prompt.js +13 -6
- package/dist/verify/local.js +240 -42
- package/dist/verify/package-check.js +86 -18
- package/dist/verify/preflight.js +242 -49
- package/dist/verify/render.js +15 -1
- package/npm-shrinkwrap.json +2383 -0
- package/package.json +14 -8
- package/NOTICE +0 -5
- package/dist/commands/completion.js +0 -116
- package/dist/commands/explain.js +0 -232
- package/dist/commands/unavailable.js +0 -11
- package/dist/telemetry/events.js +0 -40
package/README.md
CHANGED
|
@@ -1,8 +1,14 @@
|
|
|
1
1
|
# @westbayberry/dg
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
3
|
+
[](https://www.npmjs.com/package/@westbayberry/dg)
|
|
4
|
+
[](https://github.com/WestBayBerry/DG_CLI/actions/workflows/ci.yml)
|
|
5
|
+
[](./LICENSE)
|
|
6
|
+
|
|
7
|
+
Dependency Guardian is a supply-chain firewall for npm and PyPI. The `dg` CLI
|
|
8
|
+
checks a package against the scanner before its code can run, so a malicious
|
|
9
|
+
install is blocked before its install scripts execute, not flagged afterward.
|
|
10
|
+
It scans published package artifacts from the registry; your source code stays
|
|
11
|
+
on your machine.
|
|
6
12
|
|
|
7
13
|
## Install
|
|
8
14
|
|
|
@@ -10,289 +16,273 @@ verified them, flagged them, or blocked them before they reach your machine.
|
|
|
10
16
|
npm install -g @westbayberry/dg
|
|
11
17
|
```
|
|
12
18
|
|
|
13
|
-
Requires Node.js >= 22.14.
|
|
14
|
-
|
|
15
|
-
|
|
19
|
+
Requires Node.js >= 22.14. Installing the package adds only the `dg` binary and
|
|
20
|
+
runs no install lifecycle scripts of its own. The published tarball ships an
|
|
21
|
+
`npm-shrinkwrap.json`, so every install resolves the exact dependency tree the
|
|
22
|
+
release was audited with. Nothing touches your shell until you run `dg setup`.
|
|
16
23
|
|
|
17
24
|
## Quickstart
|
|
18
25
|
|
|
19
|
-
|
|
26
|
+
Audit the project in the current directory:
|
|
20
27
|
|
|
21
28
|
```bash
|
|
22
29
|
dg scan
|
|
23
30
|
```
|
|
24
31
|
|
|
25
|
-
`dg scan`
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
32
|
+
`dg scan` reads your lockfiles, asks the scanner for a verdict on each
|
|
33
|
+
dependency, and opens a full-screen results browser in an interactive terminal
|
|
34
|
+
(plain text or machine output otherwise). It installs nothing and runs no
|
|
35
|
+
package scripts. Exit code 0 means the project is clean, 2 means a block-level
|
|
36
|
+
package was found.
|
|
30
37
|
|
|
31
|
-
|
|
32
|
-
**WARN** (DG flagged, review advised), or **BLOCK** (DG blocked, do not
|
|
33
|
-
install). The exit code follows the verdict.
|
|
38
|
+
## Verdicts
|
|
34
39
|
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
server is unreachable, `dg scan` falls back to local heuristics and marks the
|
|
39
|
-
report as such (`scannerUnavailable` in JSON output) rather than failing
|
|
40
|
-
silently.
|
|
40
|
+
Every result is one of three states the scanner returns: **PASS** (verified),
|
|
41
|
+
**WARN** (flagged, review advised), or **BLOCK** (refused). The exit code
|
|
42
|
+
follows the verdict.
|
|
41
43
|
|
|
42
|
-
**The
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
prompts `Proceed? [y/N]` (default No) in a TTY. Passthrough commands (those
|
|
48
|
-
that fetch nothing) run without a proxy.
|
|
44
|
+
**The server is the verdict source.** For registry checks and the install
|
|
45
|
+
firewall the scanner returns the verdict and the CLI displays it; the CLI never
|
|
46
|
+
derives a verdict from a local score. If the scanner is unreachable, `dg scan`
|
|
47
|
+
falls back to local heuristics and marks the report (`scannerUnavailable` in
|
|
48
|
+
JSON output) rather than passing silently.
|
|
49
49
|
|
|
50
|
-
##
|
|
50
|
+
## The install firewall
|
|
51
51
|
|
|
52
|
-
|
|
52
|
+
Prefix any install command with `dg` and the package is checked before its
|
|
53
|
+
bytes reach your package manager:
|
|
53
54
|
|
|
54
55
|
```bash
|
|
55
|
-
dg
|
|
56
|
+
dg npm install lodash
|
|
57
|
+
dg pnpm add react
|
|
58
|
+
dg pip install requests
|
|
59
|
+
dg cargo add serde
|
|
56
60
|
```
|
|
57
61
|
|
|
58
|
-
|
|
62
|
+
The prefix starts a local proxy for just that command, hashes each artifact as
|
|
63
|
+
it downloads, asks the scanner for a verdict, and refuses a blocked package
|
|
64
|
+
before delivery. Supported prefixes include npm, npx, pnpm, pnpx, Yarn classic,
|
|
65
|
+
pip, pipx, uv, uvx, and cargo. A block exits 2 and prints the override command;
|
|
66
|
+
a warn prompts `Proceed? [y/N]` (default No) in a terminal.
|
|
59
67
|
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
68
|
+
To protect bare commands too, run `dg setup`. It shows one consent screen
|
|
69
|
+
listing exactly what it writes, then installs reversible, user-local shims so
|
|
70
|
+
plain `npm install` and `pip install` are scanned automatically, plus a native
|
|
71
|
+
hook in each AI coding agent it detects. Undo all of it with `dg uninstall`.
|
|
63
72
|
|
|
64
|
-
|
|
73
|
+
## Command reference
|
|
65
74
|
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
75
|
+
Run `dg <command> --help` for full flags and examples, or `dg --help-all` for
|
|
76
|
+
the complete list. The exhaustive reference lives at
|
|
77
|
+
[westbayberry.com/docs/cli-reference](https://westbayberry.com/docs/cli-reference).
|
|
69
78
|
|
|
70
|
-
|
|
71
|
-
scripts, fully locally:
|
|
79
|
+
### dg scan [path]
|
|
72
80
|
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
81
|
+
Audits the project's lockfiles for the path (current directory by default).
|
|
82
|
+
Read-only: it never installs, runs package scripts, or changes setup state.
|
|
83
|
+
Reads `package-lock.json`, `npm-shrinkwrap.json`, `yarn.lock`,
|
|
84
|
+
`pnpm-lock.yaml`, `requirements.txt`, `poetry.lock`, `Pipfile.lock`, and
|
|
85
|
+
`uv.lock`, including nested projects in a monorepo.
|
|
76
86
|
|
|
77
|
-
|
|
87
|
+
- Flags: `--staged`, `--json`, `--sarif`, `--output <file>`, `--no-decisions`.
|
|
88
|
+
- Exit codes: 0 pass, 1 warn (strict mode upgrades warn to 2), 2 block,
|
|
89
|
+
4 analysis incomplete, 10 nothing to scan. JSON output carries a
|
|
90
|
+
`schemaVersion` field and sets `scannerUnavailable` when the scanner could
|
|
91
|
+
not be reached.
|
|
78
92
|
|
|
79
|
-
|
|
80
|
-
dg scan --sarif --output dg-scan.sarif
|
|
81
|
-
```
|
|
93
|
+
### dg sbom [path]
|
|
82
94
|
|
|
83
|
-
|
|
95
|
+
Inventories the resolved dependency tree as a CycloneDX 1.5 software bill of
|
|
96
|
+
materials, with purl, license, and integrity hash per component. Reads the same
|
|
97
|
+
lockfiles as `dg scan` plus `Cargo.lock`. In a terminal it streams
|
|
98
|
+
BLOCK/WARN/PASS verdicts for npm and PyPI components when signed in; cargo stays
|
|
99
|
+
inventory only. Piped, with `--output` (alias `-o`), or with `--json` it stays
|
|
100
|
+
offline and prints the raw CycloneDX document.
|
|
84
101
|
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
```
|
|
102
|
+
- Flags: `--output <path>` / `-o`, `--json`, `--reproducible` (byte-stable
|
|
103
|
+
output; honors `SOURCE_DATE_EPOCH`).
|
|
88
104
|
|
|
89
|
-
|
|
105
|
+
### dg verify <registry:package[@version] | path | lockfile>
|
|
90
106
|
|
|
91
|
-
|
|
107
|
+
Runs a real scanner check on a published package before you install it. Name
|
|
108
|
+
the registry, `npm:react` or `pypi:requests`, with an optional `@version`
|
|
109
|
+
(latest by default). Works signed out with a free verdict summary (the verdict,
|
|
110
|
+
top reasons, provenance, and the package page link); signing in unlocks the
|
|
111
|
+
full reason list, license info, `--json`, and `--output`. Verifying a local
|
|
112
|
+
path, lockfile, or tarball stays free and offline.
|
|
92
113
|
|
|
93
|
-
|
|
94
|
-
current directory by default). Reads manifests only; it never runs package
|
|
95
|
-
scripts, installs dependencies, loads project-local config, or changes setup
|
|
96
|
-
state.
|
|
114
|
+
### dg licenses [path]
|
|
97
115
|
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
4 analysis incomplete, 10 nothing to scan. Server unreachable falls back to
|
|
102
|
-
local heuristics and sets `scannerUnavailable` in JSON.
|
|
116
|
+
SPDX license report for the dependency tree, grouped by risk. Use `--csv` or
|
|
117
|
+
`--markdown` for CI artifacts and `--fail-on <risk>[,<risk>]` to exit non-zero
|
|
118
|
+
when a dependency carries a named license-risk tier.
|
|
103
119
|
|
|
104
|
-
### dg
|
|
120
|
+
### dg audit [path]
|
|
105
121
|
|
|
106
|
-
|
|
122
|
+
Pre-publish check of a package of your own.
|
|
123
|
+
Inspects exactly the resolved publish set of one package, never the whole repo.
|
|
124
|
+
Basic checks run 100% locally and upload nothing: leaked secrets and keys,
|
|
125
|
+
credential files, source maps, `.git`, terraform state, risky lifecycle
|
|
126
|
+
scripts, and a missing `files` allowlist. On a paid plan, and if your org allows
|
|
127
|
+
it, it also runs a deep behavioral scan: a packed copy is uploaded with no
|
|
128
|
+
lifecycle scripts run, raw bytes are never retained, and only the verdict and
|
|
129
|
+
redacted findings reach your dashboard.
|
|
107
130
|
|
|
108
|
-
- `
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
directory, workspace, lockfile, or `.tgz`/`.tar.gz`/`.zip`/`.whl` archive.
|
|
113
|
-
Archive verification computes SHA-256 and checks archive paths before reading;
|
|
114
|
-
it never extracts to disk or runs package code. Flags: `--json`, `--sarif`,
|
|
115
|
-
`--output`.
|
|
116
|
-
- Exit codes (both paths): 0 PASS, 1 WARN (or scan error on the advisory path),
|
|
117
|
-
2 BLOCK, 4 analysis incomplete (registry path).
|
|
131
|
+
- Flags: `--local` (skip the upload), `--require-deep` (fail when deep can't
|
|
132
|
+
run), `--fail-on warn`.
|
|
133
|
+
- Exit codes: 0 clean, 1 warn (with `--fail-on warn`), 2 block, 3 deep required
|
|
134
|
+
but unavailable, 4 analysis incomplete.
|
|
118
135
|
|
|
119
|
-
### dg
|
|
136
|
+
### dg setup
|
|
120
137
|
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
scanner for a behavioral scan: no lifecycle scripts are run, raw bytes are never
|
|
127
|
-
retained, and only the verdict plus redacted findings reach your dashboard.
|
|
128
|
-
|
|
129
|
-
- Flags: `--json`, `--output`/`-o`, `--local` (skip the deep upload),
|
|
130
|
-
`--require-deep` (fail if the deep audit could not run), `--fail-on
|
|
131
|
-
<warn | block>`.
|
|
132
|
-
- Exit codes: 0 clean (WARN counts as clean under the default `--fail-on
|
|
133
|
-
block`), 1 WARN with `--fail-on warn`, 2 BLOCK, 3 deep audit required but
|
|
134
|
-
unavailable, 4 analysis incomplete.
|
|
138
|
+
Installs the shell shims so bare `npm install` / `pip install` are scanned, and
|
|
139
|
+
the agent hooks, behind one consent screen. `--print` previews the exact write
|
|
140
|
+
plan and changes nothing; `--yes` applies just the shell shims non-interactively
|
|
141
|
+
(add `--agents` or `--guard-commit` for the others). Everything it writes is
|
|
142
|
+
dg-owned and reversible with `dg uninstall`.
|
|
135
143
|
|
|
136
|
-
### dg
|
|
144
|
+
### dg guard-commit
|
|
137
145
|
|
|
138
|
-
|
|
139
|
-
|
|
146
|
+
Installs a per-repo git pre-commit hook that scans the staged lockfile changes
|
|
147
|
+
and blocks a commit that would add a malicious dependency. It chains any
|
|
148
|
+
existing pre-commit hook and self-checks that git will fire it. Reverse with
|
|
149
|
+
`dg guard-commit off`. `git commit --no-verify` is the override.
|
|
140
150
|
|
|
141
|
-
|
|
142
|
-
`--deny-license <id>`.
|
|
143
|
-
- Exit codes: 0 clean, 1 when a license gate trips.
|
|
151
|
+
### dg agents
|
|
144
152
|
|
|
145
|
-
|
|
153
|
+
Routes AI coding agents' installs through the firewall with a native
|
|
154
|
+
pre-command hook in each agent's own config, so a malicious or unresolvable
|
|
155
|
+
package is blocked before it is fetched. Supported: Claude Code, Codex CLI,
|
|
156
|
+
Cursor, Gemini CLI, GitHub Copilot CLI, and Windsurf. Bare `dg agents` lists
|
|
157
|
+
each agent and whether it is protected; `dg agents on` hooks every detected
|
|
158
|
+
agent (or one, `dg agents on claude-code`); `dg agents --check` verifies the
|
|
159
|
+
wiring; `dg agents off` removes it. `dg hook claude-code` keeps working as an
|
|
160
|
+
alias.
|
|
146
161
|
|
|
147
|
-
|
|
148
|
-
`dg uv`, `dg uvx`, `dg cargo` route protected install/fetch commands through
|
|
149
|
-
the per-invocation proxy. `dg yarn` claims Yarn classic only; `dg bun`,
|
|
150
|
-
`dg conda`, and `dg mamba` and Yarn Berry remain gated and fail visibly before
|
|
151
|
-
spawning.
|
|
162
|
+
### dg decisions <list | revoke>
|
|
152
163
|
|
|
153
|
-
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
- Signal deaths exit `128 + n`.
|
|
164
|
+
Warn-level findings you accept at an install prompt or commit guard can be
|
|
165
|
+
remembered in a committable `dg.json` at the project root, so repeat runs only
|
|
166
|
+
surface new problems. A new finding category, a severity escalation, or a block
|
|
167
|
+
always resurfaces; blocks can never be acknowledged away. `dg decisions list`
|
|
168
|
+
shows what the project accepted; `dg decisions revoke` removes an entry.
|
|
159
169
|
|
|
160
|
-
### dg
|
|
170
|
+
### dg cooldown [<age> | off]
|
|
171
|
+
|
|
172
|
+
Quarantines new installs of a registry release younger than `cooldown.age`
|
|
173
|
+
(default `24h`), since most malicious releases are caught within their first
|
|
174
|
+
day. Change it with `dg cooldown 7d` or disable it with `dg cooldown off`.
|
|
175
|
+
Lockfile scans are never failed by cooldown; packages you already depend on get
|
|
176
|
+
a display annotation only. `dg cooldown exempt <name>` records a shareable
|
|
177
|
+
exemption in `dg.json`.
|
|
178
|
+
|
|
179
|
+
### dg config <get | set | unset | list>
|
|
161
180
|
|
|
162
|
-
|
|
181
|
+
Views or edits user-global configuration in `~/.dg/config.json`. The everyday
|
|
182
|
+
key is `policy.mode` (`off`, `warn`, `block` the default, or `strict`), which
|
|
183
|
+
sets how a flagged package is handled. There is no detection-tuning knob;
|
|
184
|
+
`policy.mode` changes only how a flagged package is handled, not what the
|
|
185
|
+
scanner looks for.
|
|
163
186
|
|
|
164
|
-
|
|
165
|
-
`--print`, `--yes`.
|
|
166
|
-
- Chains any pre-existing pre-commit hook; `dg guard-commit off` removes only
|
|
167
|
-
the dg-owned hook.
|
|
187
|
+
### dg login / dg logout --yes
|
|
168
188
|
|
|
169
|
-
|
|
189
|
+
`dg login` signs in via your browser and links this machine to your account;
|
|
190
|
+
`dg login --token dg_live_...` authenticates CI without the browser flow.
|
|
191
|
+
Scanning works without an account. `dg logout --yes` removes the saved token
|
|
192
|
+
from this machine (the token stays valid until you revoke it from the
|
|
193
|
+
dashboard).
|
|
170
194
|
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
Flags: `--yes`, `--keep-config`, `--all`, `--service`. `--all` conflicts
|
|
177
|
-
with `--keep-config`.
|
|
178
|
-
- `dg doctor` reports local setup health and prints concrete unavailable
|
|
179
|
-
status for optional gates (Windows, Python `.pth` hook, git hooks, Bun, Yarn
|
|
180
|
-
Berry, conda, mamba) instead of claiming them. `--json` for machine output.
|
|
195
|
+
### dg doctor
|
|
196
|
+
|
|
197
|
+
Checks runtime, auth, API, policy, setup shims, PATH precedence, real-binary
|
|
198
|
+
resolution, stale state, and service state. Every non-pass line ends with the
|
|
199
|
+
exact fix command. `--json` for machine output.
|
|
181
200
|
|
|
182
201
|
### dg service
|
|
183
202
|
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
203
|
+
Service mode runs a persistent local proxy plus a managed certificate authority
|
|
204
|
+
so installs are scanned continuously, for CI runners and private registries
|
|
205
|
+
where a per-command proxy is not enough. A paid feature; run `dg login` first,
|
|
206
|
+
then `dg service start`. Fully reversible with `dg uninstall --service`.
|
|
207
|
+
|
|
208
|
+
### dg update
|
|
189
209
|
|
|
190
|
-
|
|
210
|
+
Checks whether a newer CLI version is published and prints the exact
|
|
211
|
+
`npm install -g` command to run. It never installs anything and never mutates
|
|
212
|
+
the installed package; you run the printed command yourself. Alias: `dg upgrade`.
|
|
191
213
|
|
|
192
|
-
|
|
193
|
-
prints only a redacted preview. `dg logout --yes` removes that auth file
|
|
194
|
-
without touching setup state or user config.
|
|
214
|
+
### dg uninstall
|
|
195
215
|
|
|
196
|
-
|
|
216
|
+
Removes registered dg-owned setup and service writes, state, cache, sessions,
|
|
217
|
+
shims, and shell-rc sentinel blocks while preserving unrelated user content.
|
|
218
|
+
Run it before `npm uninstall -g @westbayberry/dg`.
|
|
197
219
|
|
|
198
|
-
|
|
199
|
-
(`--json`). Keys: `api.baseUrl`, `org.id`, `policy.mode`,
|
|
200
|
-
`policy.trustProjectAllowlists`, `policy.allowForceOverride`,
|
|
201
|
-
`policy.scriptHardening`, `telemetry.enabled`, `webhooks.enabled`.
|
|
220
|
+
## Exit codes
|
|
202
221
|
|
|
203
|
-
|
|
222
|
+
Stable, so CI and shells can read them directly:
|
|
204
223
|
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
224
|
+
| Code | Meaning |
|
|
225
|
+
| --- | --- |
|
|
226
|
+
| `0` | Clean. The requested action is allowed. |
|
|
227
|
+
| `1` | Warn-level result (strict policy upgrades this to `2`). |
|
|
228
|
+
| `2` | Block: a block-level package, a denied install, or an unknown command. |
|
|
229
|
+
| `3` | `dg audit --require-deep` ran but the deep scan was unavailable. |
|
|
230
|
+
| `4` | Analysis incomplete; not a clean pass. |
|
|
231
|
+
| `10` | `dg scan` found nothing to scan. |
|
|
232
|
+
| `64` | A usage error (bad flags or arguments). |
|
|
233
|
+
| `69` | A command unavailable or gated on this platform, or a signed-out `dg verify` asking for `--json`/`--output`. |
|
|
234
|
+
| `70` | Internal tool error (for example, a report file could not be written). |
|
|
235
|
+
| `130` / `143` | Interrupted by SIGINT (Ctrl-C) or SIGTERM. |
|
|
236
|
+
|
|
237
|
+
## What dg sends to the API
|
|
238
|
+
|
|
239
|
+
The CLI sends no telemetry: no crash reporter and no analytics endpoint. It
|
|
240
|
+
talks to the network only when a command needs it.
|
|
241
|
+
|
|
242
|
+
- **Scanning** sends package names and pinned versions from your lockfiles to
|
|
243
|
+
the scan API, which pulls the actual package artifacts from the public
|
|
244
|
+
registry on its side. Your source never leaves your machine.
|
|
245
|
+
- **The install firewall** asks the server for a verdict on each artifact as it
|
|
246
|
+
downloads: name, version, ecosystem, registry host, download URL, and the
|
|
247
|
+
artifact's SHA-256.
|
|
248
|
+
- **Deep audit** (`dg audit`, opt-in, paid) uploads a packed copy of your own
|
|
249
|
+
package to `/v1/scan-tarball`; raw bytes are never retained.
|
|
250
|
+
- **Version check** queries the public npm registry for `@westbayberry/dg` at
|
|
251
|
+
most once a day in an interactive terminal. Set `CI=1` to skip it.
|
|
252
|
+
|
|
253
|
+
Authenticate CI with `DG_API_KEY` (a `dg_live_*` or `dg_test_*` key;
|
|
254
|
+
`DG_API_TOKEN` is an accepted alias). Full detail:
|
|
255
|
+
[westbayberry.com/docs/telemetry](https://westbayberry.com/docs/telemetry).
|
|
256
|
+
|
|
257
|
+
## Use in CI
|
|
209
258
|
|
|
210
|
-
|
|
259
|
+
```bash
|
|
260
|
+
npm install -g @westbayberry/dg
|
|
261
|
+
export DG_API_KEY="dg_live_..." # from Settings -> CI keys
|
|
262
|
+
dg scan
|
|
263
|
+
```
|
|
211
264
|
|
|
212
|
-
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
-
|
|
265
|
+
A non-zero exit means a block-level finding; wire that into your pipeline's
|
|
266
|
+
pass/fail. The CLI auto-detects CI and switches to non-interactive output; pass
|
|
267
|
+
`--json` or `--sarif` when a parser reads stdout. On GitHub, the
|
|
268
|
+
[GitHub App](https://westbayberry.com/docs#github-app) is the simpler path and
|
|
269
|
+
posts a required PR status check.
|
|
216
270
|
|
|
217
|
-
##
|
|
271
|
+
## Documentation
|
|
218
272
|
|
|
219
|
-
|
|
220
|
-
|
|
273
|
+
- [Getting started](https://westbayberry.com/docs)
|
|
274
|
+
- [CLI reference](https://westbayberry.com/docs/cli-reference)
|
|
275
|
+
- [Blocking behavior](https://westbayberry.com/docs/blocking)
|
|
276
|
+
- [Integrations](https://westbayberry.com/docs/integrations)
|
|
277
|
+
- [Telemetry](https://westbayberry.com/docs/telemetry)
|
|
221
278
|
|
|
222
|
-
|
|
223
|
-
| --- | --- |
|
|
224
|
-
| `api.baseUrl` | Dependency Guardian API endpoint |
|
|
225
|
-
| `org.id` | Organization the CLI reports to |
|
|
226
|
-
| `policy.mode` | `off`, `warn`, `block`, or `strict` |
|
|
227
|
-
| `policy.trustProjectAllowlists` | Whether project-local allowlists are trusted |
|
|
228
|
-
| `policy.allowForceOverride` | Whether `--dg-force-install` is permitted |
|
|
229
|
-
| `policy.scriptHardening` | Preserve script-disabling flags; never re-enable scripts |
|
|
230
|
-
| `telemetry.enabled` | Telemetry (off by default) |
|
|
231
|
-
| `webhooks.enabled` | Webhook outbox delivery |
|
|
232
|
-
|
|
233
|
-
Project-local allowlists never suppress an install firewall block unless
|
|
234
|
-
user-global or org policy explicitly trusts them.
|
|
235
|
-
|
|
236
|
-
Telemetry is disabled by default. When enabled, the CLI appends command and
|
|
237
|
-
install-decision events to a local `telemetry.jsonl` file in the dg state
|
|
238
|
-
directory, limited to a fixed attribute allowlist. Nothing is transmitted
|
|
239
|
-
anywhere; the file never leaves your machine.
|
|
240
|
-
|
|
241
|
-
### Environment variables
|
|
242
|
-
|
|
243
|
-
- `DG_API_TOKEN`: auth token; overrides the token stored in the config file.
|
|
244
|
-
- `DG_PRIVATE_REGISTRY_HOSTS`: comma-separated hosts whose URL-fallback
|
|
245
|
-
artifacts are eligible for private-registry scan upload.
|
|
246
|
-
- `DG_SCAN_TARBALL_UPLOAD`: set to `1` to enable private-registry artifact
|
|
247
|
-
upload to `/v1/scan-tarball` (requires `DG_API_TOKEN`).
|
|
248
|
-
- `DG_SERVICE_TRUST_STORE_BACKEND` / `DG_SERVICE_TRUST_STORE_DIR`: for
|
|
249
|
-
CI/Docker, set the backend to `file` and a directory to copy the active
|
|
250
|
-
public CA certificate into an image-local trust directory without touching
|
|
251
|
-
the host OS trust store.
|
|
252
|
-
- `NO_COLOR` / `FORCE_COLOR`: disable or force ANSI color output.
|
|
279
|
+
## Reporting a vulnerability
|
|
253
280
|
|
|
254
|
-
|
|
281
|
+
Please report security issues privately through GitHub's
|
|
282
|
+
[private vulnerability reporting](https://github.com/WestBayBerry/DG_CLI/security/advisories/new)
|
|
283
|
+
rather than a public issue.
|
|
255
284
|
|
|
256
|
-
|
|
285
|
+
## License
|
|
257
286
|
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
| 0 | PASS / clean |
|
|
261
|
-
| 1 | WARN |
|
|
262
|
-
| 2 | BLOCK, or a usage error |
|
|
263
|
-
| 4 | analysis incomplete |
|
|
264
|
-
| 69 | command unavailable or gated on this platform |
|
|
265
|
-
| 70 | internal error |
|
|
266
|
-
| 128 + n | child killed by signal n (firewall installs) |
|
|
267
|
-
| 130 / 143 | interrupted (SIGINT / SIGTERM) |
|
|
268
|
-
|
|
269
|
-
Per-command specifics:
|
|
270
|
-
|
|
271
|
-
- `dg scan`: strict mode upgrades WARN (1) to BLOCK (2); 10 means nothing to
|
|
272
|
-
scan.
|
|
273
|
-
- `dg audit`: 3 means a deep audit was required but unavailable; under the
|
|
274
|
-
default `--fail-on block`, WARN exits 0.
|
|
275
|
-
- `dg verify` (registry path): 4 means analysis incomplete; the advisory path
|
|
276
|
-
reports a scan error as 1.
|
|
277
|
-
|
|
278
|
-
## Troubleshooting
|
|
279
|
-
|
|
280
|
-
Run `dg doctor` first; it reports Node version, package health, config
|
|
281
|
-
readability, shim/PATH state, and which optional gates are unavailable.
|
|
282
|
-
|
|
283
|
-
- "Scanner unavailable" / `scannerUnavailable` in JSON: the API was
|
|
284
|
-
unreachable and `dg scan` fell back to local heuristics. Check `api.baseUrl`
|
|
285
|
-
and network access.
|
|
286
|
-
- A safe package was blocked: confirm `policy.mode`; `strict` upgrades WARN to
|
|
287
|
-
BLOCK. Use `dg explain <id>` to see the cause.
|
|
288
|
-
- An install firewall block you need to override: rerun with
|
|
289
|
-
`--dg-force-install`, allowed only when `policy.allowForceOverride` is set;
|
|
290
|
-
the override is audited.
|
|
291
|
-
- `dg setup` exits 69 on Windows: setup is POSIX-only.
|
|
292
|
-
- An older `dg` runs instead of the installed one: run `which -a dg` and fix
|
|
293
|
-
PATH precedence.
|
|
294
|
-
|
|
295
|
-
## Links
|
|
296
|
-
|
|
297
|
-
- Documentation: https://westbayberry.com/docs
|
|
298
|
-
- Pricing: https://westbayberry.com/pricing
|
|
287
|
+
Apache-2.0. See [LICENSE](./LICENSE). Issues and pull requests are welcome at
|
|
288
|
+
[github.com/WestBayBerry/DG_CLI](https://github.com/WestBayBerry/DG_CLI).
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
import { join } from "node:path";
|
|
2
|
+
import { dirExists, mergedJsonHook, LEGACY_AGENT_HOOK_SENTINEL } from "./persistence.js";
|
|
3
|
+
const SIGNATURE = "hook-exec claude-code";
|
|
4
|
+
function configPath(home) {
|
|
5
|
+
return join(home, ".claude", "settings.json");
|
|
6
|
+
}
|
|
7
|
+
function isDgGroup(group) {
|
|
8
|
+
if (typeof group !== "object" || group === null) {
|
|
9
|
+
return false;
|
|
10
|
+
}
|
|
11
|
+
const hooks = group.hooks;
|
|
12
|
+
if (!Array.isArray(hooks)) {
|
|
13
|
+
return false;
|
|
14
|
+
}
|
|
15
|
+
return hooks.some((h) => typeof h === "object" &&
|
|
16
|
+
h !== null &&
|
|
17
|
+
typeof h.command === "string" &&
|
|
18
|
+
h.command.includes(SIGNATURE));
|
|
19
|
+
}
|
|
20
|
+
function dgGroup(dgCommand) {
|
|
21
|
+
return { matcher: "Bash", hooks: [{ type: "command", command: dgCommand, timeout: 60 }] };
|
|
22
|
+
}
|
|
23
|
+
// Idempotently insert the dg PreToolUse group, preserving every other key the
|
|
24
|
+
// user has in their settings (round-trips unknown keys).
|
|
25
|
+
export function insertDgHook(settings, dgCommand) {
|
|
26
|
+
const next = { ...settings };
|
|
27
|
+
const hooks = typeof next.hooks === "object" && next.hooks !== null ? { ...next.hooks } : {};
|
|
28
|
+
const pre = Array.isArray(hooks.PreToolUse) ? hooks.PreToolUse : [];
|
|
29
|
+
hooks.PreToolUse = [...pre.filter((g) => !isDgGroup(g)), dgGroup(dgCommand)];
|
|
30
|
+
next.hooks = hooks;
|
|
31
|
+
return next;
|
|
32
|
+
}
|
|
33
|
+
export function removeDgHook(settings) {
|
|
34
|
+
const next = { ...settings };
|
|
35
|
+
if (typeof next.hooks !== "object" || next.hooks === null) {
|
|
36
|
+
return { settings: next, changed: false, empty: Object.keys(next).length === 0 };
|
|
37
|
+
}
|
|
38
|
+
const hooks = { ...next.hooks };
|
|
39
|
+
const pre = Array.isArray(hooks.PreToolUse) ? hooks.PreToolUse : [];
|
|
40
|
+
const withoutDg = pre.filter((g) => !isDgGroup(g));
|
|
41
|
+
const changed = withoutDg.length !== pre.length;
|
|
42
|
+
if (withoutDg.length > 0) {
|
|
43
|
+
hooks.PreToolUse = withoutDg;
|
|
44
|
+
}
|
|
45
|
+
else {
|
|
46
|
+
delete hooks.PreToolUse;
|
|
47
|
+
}
|
|
48
|
+
if (Object.keys(hooks).length > 0) {
|
|
49
|
+
next.hooks = hooks;
|
|
50
|
+
}
|
|
51
|
+
else {
|
|
52
|
+
delete next.hooks;
|
|
53
|
+
}
|
|
54
|
+
return { settings: next, changed, empty: Object.keys(next).length === 0 };
|
|
55
|
+
}
|
|
56
|
+
function hasDgHook(settings) {
|
|
57
|
+
const hooks = settings.hooks?.PreToolUse;
|
|
58
|
+
return Array.isArray(hooks) && hooks.some((g) => isDgGroup(g));
|
|
59
|
+
}
|
|
60
|
+
function parseInput(stdin) {
|
|
61
|
+
try {
|
|
62
|
+
const obj = JSON.parse(stdin);
|
|
63
|
+
const command = obj.tool_input?.command;
|
|
64
|
+
if (typeof command !== "string") {
|
|
65
|
+
return null;
|
|
66
|
+
}
|
|
67
|
+
return typeof obj.cwd === "string" ? { command, cwd: obj.cwd } : { command };
|
|
68
|
+
}
|
|
69
|
+
catch {
|
|
70
|
+
return null;
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
// Claude Code PreToolUse contract: emit a decision only to deny/ask; stay
|
|
74
|
+
// silent ({}) on allow so we never override the user's normal permission flow.
|
|
75
|
+
function emitDecision(verdict) {
|
|
76
|
+
if (verdict.decision === "allow") {
|
|
77
|
+
return { stdout: "{}", exitCode: 0 };
|
|
78
|
+
}
|
|
79
|
+
return {
|
|
80
|
+
stdout: JSON.stringify({
|
|
81
|
+
hookSpecificOutput: {
|
|
82
|
+
hookEventName: "PreToolUse",
|
|
83
|
+
permissionDecision: verdict.decision,
|
|
84
|
+
...(verdict.reason ? { permissionDecisionReason: verdict.reason } : {}),
|
|
85
|
+
},
|
|
86
|
+
}),
|
|
87
|
+
exitCode: 0,
|
|
88
|
+
};
|
|
89
|
+
}
|
|
90
|
+
export const claudeCodeIntegration = {
|
|
91
|
+
id: "claude-code",
|
|
92
|
+
label: "Claude Code",
|
|
93
|
+
kind: "merged-json",
|
|
94
|
+
maturity: "verified",
|
|
95
|
+
minVersion: null,
|
|
96
|
+
configPath,
|
|
97
|
+
detect: (home) => dirExists(join(home, ".claude")),
|
|
98
|
+
probeHookSupport: (home) => ({
|
|
99
|
+
supported: true,
|
|
100
|
+
detail: dirExists(join(home, ".claude"))
|
|
101
|
+
? "Claude Code settings directory found"
|
|
102
|
+
: "settings hooks are supported by every Claude Code version; ~/.claude will be created",
|
|
103
|
+
}),
|
|
104
|
+
parseInput,
|
|
105
|
+
emitDecision,
|
|
106
|
+
...mergedJsonHook({
|
|
107
|
+
checkName: "dg PreToolUse hook",
|
|
108
|
+
legacySentinels: [LEGACY_AGENT_HOOK_SENTINEL],
|
|
109
|
+
insert: insertDgHook,
|
|
110
|
+
remove: removeDgHook,
|
|
111
|
+
isInstalled: hasDgHook,
|
|
112
|
+
}),
|
|
113
|
+
};
|