@westbayberry/dg 1.0.58 → 1.0.60
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/dist/index.mjs +109 -16
- package/package.json +1 -1
- package/README.md +0 -129
package/dist/index.mjs
CHANGED
|
@@ -29172,6 +29172,8 @@ var init_esm5 = __esm({
|
|
|
29172
29172
|
init_logger();
|
|
29173
29173
|
init_instrument();
|
|
29174
29174
|
init_sdk();
|
|
29175
|
+
init_onuncaughtexception();
|
|
29176
|
+
init_onunhandledrejection();
|
|
29175
29177
|
init_addOriginToSpan();
|
|
29176
29178
|
init_getRequestUrl();
|
|
29177
29179
|
init_nodeVersion();
|
|
@@ -46144,6 +46146,7 @@ var init_esm6 = __esm({
|
|
|
46144
46146
|
"node_modules/@sentry/node/build/esm/index.js"() {
|
|
46145
46147
|
init_sdk2();
|
|
46146
46148
|
init_esm3();
|
|
46149
|
+
init_esm5();
|
|
46147
46150
|
}
|
|
46148
46151
|
});
|
|
46149
46152
|
|
|
@@ -46158,6 +46161,18 @@ function initTelemetry(version) {
|
|
|
46158
46161
|
// TODO: reduce sample rate when CLI usage exceeds ~10k DAU
|
|
46159
46162
|
sampleRate: 1,
|
|
46160
46163
|
sendDefaultPii: false,
|
|
46164
|
+
// Sentry's default Node integrations patch `fs`, which collides
|
|
46165
|
+
// with node-sqlite3-wasm's NODEFS layer — every Database open
|
|
46166
|
+
// returned "unable to open database file" and the local scan
|
|
46167
|
+
// cache stayed empty (0 rows even after thousands of scans).
|
|
46168
|
+
// Opt out of the default integrations entirely; we keep crash +
|
|
46169
|
+
// unhandled-rejection reporting via the two explicit integrations
|
|
46170
|
+
// below, which don't touch fs.
|
|
46171
|
+
defaultIntegrations: false,
|
|
46172
|
+
integrations: [
|
|
46173
|
+
onUncaughtExceptionIntegration({ exitEvenIfOtherHandlersAreRegistered: false }),
|
|
46174
|
+
onUnhandledRejectionIntegration()
|
|
46175
|
+
],
|
|
46161
46176
|
beforeSend(event) {
|
|
46162
46177
|
if (event.exception?.values?.some(
|
|
46163
46178
|
(ex) => ex.value && /\.(invalid|test|example|localhost)\b/i.test(ex.value)
|
|
@@ -47020,6 +47035,20 @@ var init_config = __esm({
|
|
|
47020
47035
|
});
|
|
47021
47036
|
|
|
47022
47037
|
// src/commands/npm-wrapper.ts
|
|
47038
|
+
var npm_wrapper_exports = {};
|
|
47039
|
+
__export(npm_wrapper_exports, {
|
|
47040
|
+
handleWrapCommand: () => handleWrapCommand,
|
|
47041
|
+
parseNpmArgs: () => parseNpmArgs,
|
|
47042
|
+
parsePackageSpec: () => parsePackageSpec,
|
|
47043
|
+
pinTopLevelArgs: () => pinTopLevelArgs,
|
|
47044
|
+
readBareInstallPackages: () => readBareInstallPackages,
|
|
47045
|
+
readBareInstallPackagesTyped: () => readBareInstallPackagesTyped,
|
|
47046
|
+
readLockfilePins: () => readLockfilePins,
|
|
47047
|
+
resolvePackages: () => resolvePackages,
|
|
47048
|
+
resolveTreeNpm: () => resolveTreeNpm,
|
|
47049
|
+
resolveVersion: () => resolveVersion,
|
|
47050
|
+
runNpm: () => runNpm
|
|
47051
|
+
});
|
|
47023
47052
|
import { spawn as spawn2 } from "node:child_process";
|
|
47024
47053
|
import { readFileSync as readFileSync4, existsSync as existsSync4, mkdtempSync, writeFileSync as writeFileSync2, rmSync } from "node:fs";
|
|
47025
47054
|
import { join as join6 } from "node:path";
|
|
@@ -84502,7 +84531,9 @@ var init_local_cache = __esm({
|
|
|
84502
84531
|
// src/api/client.ts
|
|
84503
84532
|
var client_exports = {};
|
|
84504
84533
|
__export(client_exports, {
|
|
84534
|
+
ANON_BATCH_SIZE: () => ANON_BATCH_SIZE,
|
|
84505
84535
|
APIError: () => APIError,
|
|
84536
|
+
BATCH_SIZE: () => BATCH_SIZE,
|
|
84506
84537
|
ClientOutdatedError: () => ClientOutdatedError,
|
|
84507
84538
|
TrialExhaustedError: () => TrialExhaustedError,
|
|
84508
84539
|
callAnalyzeAPI: () => callAnalyzeAPI,
|
|
@@ -85073,8 +85104,8 @@ var init_client3 = __esm({
|
|
|
85073
85104
|
this.name = "ClientOutdatedError";
|
|
85074
85105
|
}
|
|
85075
85106
|
};
|
|
85076
|
-
BATCH_SIZE =
|
|
85077
|
-
ANON_BATCH_SIZE =
|
|
85107
|
+
BATCH_SIZE = 50;
|
|
85108
|
+
ANON_BATCH_SIZE = 50;
|
|
85078
85109
|
MAX_RETRIES = 2;
|
|
85079
85110
|
RETRY_DELAY_MS = 5e3;
|
|
85080
85111
|
DEFAULT_BATCH_CONCURRENCY = 4;
|
|
@@ -85129,6 +85160,19 @@ async function scanProjectAtPath(cwd2, config3, onProgress) {
|
|
|
85129
85160
|
cumulativeDone += pyPackages.length;
|
|
85130
85161
|
}
|
|
85131
85162
|
const allPackages = responses.flatMap((r) => r.packages);
|
|
85163
|
+
if (allPackages.length === 0) {
|
|
85164
|
+
const elapsed = elapsedMs();
|
|
85165
|
+
return {
|
|
85166
|
+
status: "api_returned_empty",
|
|
85167
|
+
result: {
|
|
85168
|
+
result: { score: 0, action: "pass", packages: [], safeVersions: {}, durationMs: elapsed },
|
|
85169
|
+
durationMs: elapsed,
|
|
85170
|
+
scannedCount: 0,
|
|
85171
|
+
skippedCount: discovery.skipped?.length ?? 0
|
|
85172
|
+
},
|
|
85173
|
+
message: `Analyze API returned 0 results for ${totalDiscovered} discovered packages (npm: ${npmPackages.length}, pypi: ${pyPackages.length}). This is not a 'nothing to scan' state \u2014 the API call succeeded but returned an empty packages list. Check API status and re-run.`
|
|
85174
|
+
};
|
|
85175
|
+
}
|
|
85132
85176
|
const maxScore = allPackages.length > 0 ? Math.max(0, ...allPackages.map((p) => p.score)) : 0;
|
|
85133
85177
|
const anyIncomplete = responses.some((r) => r.action === "analysis_incomplete") || allPackages.some((p) => Array.isArray(p.findings) && p.findings.some((f) => f?.id === "analysis_incomplete"));
|
|
85134
85178
|
const action = maxScore >= 70 ? "block" : maxScore >= 60 ? "warn" : anyIncomplete ? "analysis_incomplete" : "pass";
|
|
@@ -85578,12 +85622,24 @@ var init_protect = __esm({
|
|
|
85578
85622
|
# echo 'source ~/.dependency-guardian/aliases.sh' >> ~/.zshrc # or ~/.bashrc
|
|
85579
85623
|
# To turn it off, remove that line.
|
|
85580
85624
|
|
|
85581
|
-
# Only alias if dg is on PATH
|
|
85582
|
-
|
|
85625
|
+
# Only alias if a real dg binary is on PATH.
|
|
85626
|
+
#
|
|
85627
|
+
# We use \`type -p\` (bash + zsh) rather than POSIX \`command -v\`
|
|
85628
|
+
# because command -v ALSO returns true for shell functions and
|
|
85629
|
+
# aliases. If your rc defines a wrapper function like
|
|
85630
|
+
# dg() { ... command dg "$@"; }
|
|
85631
|
+
# command -v passes even when the dg binary isn't installed \u2014 and
|
|
85632
|
+
# the npm alias below then rewrites every \`npm install\` into a
|
|
85633
|
+
# command that fails silently. type -p returns ONLY the path of
|
|
85634
|
+
# an external executable, so this check is honest about whether
|
|
85635
|
+
# dg is actually reachable.
|
|
85636
|
+
__dg_bin=$(type -p dg 2>/dev/null)
|
|
85637
|
+
if [ -n "$__dg_bin" ] && [ -x "$__dg_bin" ]; then
|
|
85583
85638
|
alias npm='dg npm'
|
|
85584
85639
|
alias pip='dg pip'
|
|
85585
85640
|
alias pip3='dg pip'
|
|
85586
85641
|
fi
|
|
85642
|
+
unset __dg_bin
|
|
85587
85643
|
`;
|
|
85588
85644
|
USAGE3 = `
|
|
85589
85645
|
dg protect \u2014 opt-in low-friction protection for a project
|
|
@@ -86003,18 +86059,26 @@ function useInit(opts = {}) {
|
|
|
86003
86059
|
const maxScore = allPackages.length > 0 ? Math.max(0, ...allPackages.map((p) => p.score)) : 0;
|
|
86004
86060
|
const action = maxScore >= 70 ? "block" : maxScore >= 60 ? "warn" : "pass";
|
|
86005
86061
|
if (totalScanned === 0) {
|
|
86006
|
-
|
|
86007
|
-
|
|
86008
|
-
|
|
86009
|
-
|
|
86010
|
-
|
|
86011
|
-
|
|
86012
|
-
|
|
86013
|
-
|
|
86014
|
-
|
|
86062
|
+
const errOutcome = allOutcomes.find((o) => o.status === "error");
|
|
86063
|
+
const emptyApiOutcome = allOutcomes.find((o) => o.status === "api_returned_empty");
|
|
86064
|
+
const trialOutcome = allOutcomes.find((o) => o.status === "trial_exhausted");
|
|
86065
|
+
const meaningful = errOutcome ?? emptyApiOutcome ?? trialOutcome;
|
|
86066
|
+
if (meaningful) {
|
|
86067
|
+
dispatch({ type: "scan_done", outcome: meaningful });
|
|
86068
|
+
} else {
|
|
86069
|
+
dispatch({
|
|
86070
|
+
type: "scan_done",
|
|
86071
|
+
outcome: {
|
|
86072
|
+
status: "no_packages",
|
|
86073
|
+
result: {
|
|
86074
|
+
result: { score: 0, action: "pass", packages: [], safeVersions: {}, durationMs: totalDuration },
|
|
86075
|
+
durationMs: totalDuration,
|
|
86076
|
+
scannedCount: 0,
|
|
86077
|
+
skippedCount: 0
|
|
86078
|
+
}
|
|
86015
86079
|
}
|
|
86016
|
-
}
|
|
86017
|
-
}
|
|
86080
|
+
});
|
|
86081
|
+
}
|
|
86018
86082
|
} else {
|
|
86019
86083
|
dispatch({
|
|
86020
86084
|
type: "scan_done",
|
|
@@ -89096,6 +89160,12 @@ var init_ScanResultCard = __esm({
|
|
|
89096
89160
|
score = 0;
|
|
89097
89161
|
packages = [];
|
|
89098
89162
|
extraNote = "Nothing to scan yet. I'll catch new packages as you add them.";
|
|
89163
|
+
} else if (o.status === "api_returned_empty") {
|
|
89164
|
+
verdict = "EMPTY";
|
|
89165
|
+
verdictColor = "yellow";
|
|
89166
|
+
score = 0;
|
|
89167
|
+
packages = [];
|
|
89168
|
+
extraNote = o.message ?? "Analyze API returned no results for discovered packages.";
|
|
89099
89169
|
} else if (o.status === "trial_exhausted") {
|
|
89100
89170
|
verdict = "EMPTY";
|
|
89101
89171
|
verdictColor = "yellow";
|
|
@@ -89848,6 +89918,7 @@ var init_InitApp = __esm({
|
|
|
89848
89918
|
return "happy";
|
|
89849
89919
|
}
|
|
89850
89920
|
if (r.status === "no_packages") return "happy";
|
|
89921
|
+
if (r.status === "api_returned_empty") return "alert";
|
|
89851
89922
|
return "idle";
|
|
89852
89923
|
})();
|
|
89853
89924
|
const content = (() => {
|
|
@@ -90258,6 +90329,7 @@ var init_InitApp = __esm({
|
|
|
90258
90329
|
const r = state.scanResult;
|
|
90259
90330
|
const okScan = r?.status === "ok" && r.result;
|
|
90260
90331
|
const empty = r?.status === "no_packages";
|
|
90332
|
+
const apiEmpty = r?.status === "api_returned_empty";
|
|
90261
90333
|
const resultBody = [
|
|
90262
90334
|
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)(Text, { bold: true, children: "Scan complete." }, "hdr"),
|
|
90263
90335
|
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)(Text, { children: " " }, "sp1"),
|
|
@@ -90265,6 +90337,10 @@ var init_InitApp = __esm({
|
|
|
90265
90337
|
"I scanned ",
|
|
90266
90338
|
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)(Text, { bold: true, color: "cyan", children: projectName }),
|
|
90267
90339
|
"."
|
|
90340
|
+
] }, "line") : apiEmpty ? /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(Text, { children: [
|
|
90341
|
+
"I found packages in ",
|
|
90342
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)(Text, { bold: true, color: "cyan", children: projectName }),
|
|
90343
|
+
" but the analyze API returned nothing back. This is a server-side problem, not your project."
|
|
90268
90344
|
] }, "line") : empty ? /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(Text, { children: [
|
|
90269
90345
|
"I didn't find any packages to scan in ",
|
|
90270
90346
|
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)(Text, { bold: true, color: "cyan", children: projectName }),
|
|
@@ -90276,6 +90352,13 @@ var init_InitApp = __esm({
|
|
|
90276
90352
|
] }, "line"),
|
|
90277
90353
|
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)(Text, { children: " " }, "sp2")
|
|
90278
90354
|
];
|
|
90355
|
+
if (apiEmpty && r?.message) {
|
|
90356
|
+
resultBody.push(
|
|
90357
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)(Text, { color: "yellow", children: r.message }, "api-empty-detail")
|
|
90358
|
+
);
|
|
90359
|
+
resultBody.push(/* @__PURE__ */ (0, import_jsx_runtime6.jsx)(Text, { dimColor: true, children: "Check westbayberry.com/status and re-run." }, "api-empty-hint"));
|
|
90360
|
+
resultBody.push(/* @__PURE__ */ (0, import_jsx_runtime6.jsx)(Text, { children: " " }, "api-empty-sp"));
|
|
90361
|
+
}
|
|
90279
90362
|
if (state.scanResult) {
|
|
90280
90363
|
resultBody.push(
|
|
90281
90364
|
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)(ScanResultCard, { maxWidth: cardMaxWidth, outcome: { kind: "real", outcome: state.scanResult } }, "card")
|
|
@@ -90618,6 +90701,8 @@ var init_InitApp = __esm({
|
|
|
90618
90701
|
scan.result?.durationMs ?? 0,
|
|
90619
90702
|
"ms)"
|
|
90620
90703
|
] }, "scan-empty"));
|
|
90704
|
+
} else if (scan.status === "api_returned_empty") {
|
|
90705
|
+
scanLine.push(/* @__PURE__ */ (0, import_jsx_runtime6.jsx)(Text, { color: "yellow", children: "\u26A0 Analyze API returned no results \u2014 try again or check status" }, "scan-api-empty"));
|
|
90621
90706
|
} else if (scan.status === "trial_exhausted") {
|
|
90622
90707
|
scanLine.push(/* @__PURE__ */ (0, import_jsx_runtime6.jsx)(Text, { color: "yellow", children: "\u26A0 Free trial scans used up \u2014 `dg login` to continue" }, "scan-trial"));
|
|
90623
90708
|
} else if (scan.status === "error") {
|
|
@@ -97238,7 +97323,15 @@ async function main() {
|
|
|
97238
97323
|
const strictFlags = rawCommand !== "npm" && rawCommand !== "pip";
|
|
97239
97324
|
const config3 = parseConfig(process.argv, strictFlags);
|
|
97240
97325
|
const updatePromise = checkForUpdate(CLI_VERSION).catch(() => null);
|
|
97241
|
-
|
|
97326
|
+
let needsTermsGate = rawCommand === "scan";
|
|
97327
|
+
if (rawCommand === "npm") {
|
|
97328
|
+
const { parseNpmArgs: parseNpmArgs2 } = await Promise.resolve().then(() => (init_npm_wrapper(), npm_wrapper_exports));
|
|
97329
|
+
needsTermsGate = parseNpmArgs2(process.argv.slice(3)).shouldScan;
|
|
97330
|
+
} else if (rawCommand === "pip") {
|
|
97331
|
+
const { parsePipArgs: parsePipArgs2 } = await Promise.resolve().then(() => (init_pip_wrapper(), pip_wrapper_exports));
|
|
97332
|
+
needsTermsGate = parsePipArgs2(process.argv.slice(3)).shouldScan;
|
|
97333
|
+
}
|
|
97334
|
+
if (needsTermsGate) {
|
|
97242
97335
|
const { gateOrExit: gateOrExit2 } = await Promise.resolve().then(() => (init_terms_gate(), terms_gate_exports));
|
|
97243
97336
|
await gateOrExit2();
|
|
97244
97337
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@westbayberry/dg",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.60",
|
|
4
4
|
"description": "Supply chain security scanner for npm and Python dependencies — 35 behavioral detectors catch zero-day attacks CVE databases miss. 99.66% catch rate on 155K packages.",
|
|
5
5
|
"bin": {
|
|
6
6
|
"dependency-guardian": "dist/index.mjs",
|
package/README.md
DELETED
|
@@ -1,129 +0,0 @@
|
|
|
1
|
-
# @westbayberry/dg
|
|
2
|
-
|
|
3
|
-
Behavioral supply chain security for npm and Python. Reads what packages
|
|
4
|
-
actually do — install hooks, credential access, network exfiltration,
|
|
5
|
-
obfuscation — and gates risky installs before they touch your machine.
|
|
6
|
-
|
|
7
|
-
**53 detectors · 95.20% npm / 93.88% PyPI catch rate on 17,874 packages · 0.44% npm / 0.29% PyPI FPR** —
|
|
8
|
-
[published methodology](https://westbayberry.com/benchmark).
|
|
9
|
-
|
|
10
|
-
## What `dg` actually does
|
|
11
|
-
|
|
12
|
-
| Surface | What it does | Default mode |
|
|
13
|
-
|---|---|---|
|
|
14
|
-
| `dg npm install <pkg>` | Resolves the full dependency tree (top-level + transitive) without running install scripts, scans every package, then invokes the real `npm install` only if the verdict permits. Top-level versions are pinned to the exact version we scanned. Bare `dg npm install` honors `package-lock.json` pinned versions. | **block** |
|
|
15
|
-
| `dg pip install <pkg>` | Resolves the full pip tree via `pip install --dry-run --report=-` (pip ≥ 23.0); scans the full set before invoking real `pip install`. On older pip, transitive scan is reported as unavailable and (in block mode) the install is refused. | **block** |
|
|
16
|
-
| `dg scan` | Read-only audit. Diffs lockfiles and reports findings. Does **not** block anything by default. | warn |
|
|
17
|
-
| `dg hook install` | Adds a pre-commit hook that runs `dg scan --mode block` when a lockfile change is staged. Quiet when nothing is wrong. | block |
|
|
18
|
-
|
|
19
|
-
For maximum protection in CI/build pipelines, add `--strict` to the install
|
|
20
|
-
wrapper. `--strict` implies `--dg-no-scripts` and refuses any partial-coverage
|
|
21
|
-
scan (transitive enumeration must succeed).
|
|
22
|
-
|
|
23
|
-
DG only activates when you intentionally invoke it, install the hook, or
|
|
24
|
-
run it in CI. It does not globally hijack `npm`/`pip`.
|
|
25
|
-
|
|
26
|
-
## Install
|
|
27
|
-
|
|
28
|
-
```bash
|
|
29
|
-
npm install -g @westbayberry/dg
|
|
30
|
-
```
|
|
31
|
-
|
|
32
|
-
Requires Node.js 18+.
|
|
33
|
-
|
|
34
|
-
## Quick Start
|
|
35
|
-
|
|
36
|
-
```bash
|
|
37
|
-
dg login
|
|
38
|
-
dg scan # read-only audit of the current project
|
|
39
|
-
```
|
|
40
|
-
|
|
41
|
-
Scope-truthful output: a clean project shows
|
|
42
|
-
`✓ Dependency Guardian checked 142 packages. No risky behavior found.`
|
|
43
|
-
A flagged install shows only the warn/block packages with one-line
|
|
44
|
-
reasons and a clear next step.
|
|
45
|
-
|
|
46
|
-
Supports `package-lock.json`, `yarn.lock`, `pnpm-lock.yaml`,
|
|
47
|
-
`requirements.txt`, `Pipfile.lock`, and `poetry.lock`.
|
|
48
|
-
|
|
49
|
-
## Commands
|
|
50
|
-
|
|
51
|
-
```
|
|
52
|
-
dg scan Read-only audit of the project's lockfiles
|
|
53
|
-
dg status Show your auth state, plan tier, and whether protection is on
|
|
54
|
-
dg login / logout Manage authentication
|
|
55
|
-
dg npm install <pkg> Protective install wrapper (default mode: block)
|
|
56
|
-
dg pip install <pkg> Protective install wrapper (default mode: block)
|
|
57
|
-
dg protect Opt this project in to auto-protection on every install
|
|
58
|
-
dg protect off Disable opt-in protection in this project
|
|
59
|
-
dg wrap Print the shell-alias snippet without writing a project marker
|
|
60
|
-
dg hook install Install a git pre-commit hook that gates lockfile diffs
|
|
61
|
-
dg hook uninstall Remove the git pre-commit hook
|
|
62
|
-
dg publish-check Self-scan before `npm publish` / `pip upload`
|
|
63
|
-
dg update Check for and install the latest version
|
|
64
|
-
```
|
|
65
|
-
|
|
66
|
-
Full reference: <https://westbayberry.com/docs/cli-reference>
|
|
67
|
-
|
|
68
|
-
## Common Options
|
|
69
|
-
|
|
70
|
-
| Flag | Default | Description |
|
|
71
|
-
|------|---------|-------------|
|
|
72
|
-
| `--mode <mode>` | `warn` for `scan`, `block` for install wrappers | `block` / `warn` / `off` |
|
|
73
|
-
| `--details` | off | Verbose table view (the older format) |
|
|
74
|
-
| `--explain` | off | Plain-English explanation of findings |
|
|
75
|
-
| `--json` | off | Stable machine-readable JSON (CI) |
|
|
76
|
-
| `--ci` | auto when `CI=1` | Deterministic output, no spinners |
|
|
77
|
-
| `--dg-force` | off | Bypass a block (visible in output, audit-friendly) |
|
|
78
|
-
| `--dg-no-scripts` | off | Pass `--ignore-scripts` to the real install |
|
|
79
|
-
| `--strict` | off | Refuse partial-coverage scans + auto `--dg-no-scripts` |
|
|
80
|
-
| `--quiet` | off | Suppress login + update nudges (still prints findings) |
|
|
81
|
-
| `--workspace <dir>` | | Scan a specific workspace subdirectory |
|
|
82
|
-
|
|
83
|
-
## Exit Codes
|
|
84
|
-
|
|
85
|
-
| Code | Meaning |
|
|
86
|
-
|------|---------|
|
|
87
|
-
| `0` | Pass |
|
|
88
|
-
| `1` | Warning |
|
|
89
|
-
| `2` | Block |
|
|
90
|
-
| `3` | Error |
|
|
91
|
-
|
|
92
|
-
## CI
|
|
93
|
-
|
|
94
|
-
```bash
|
|
95
|
-
npx @westbayberry/dg login
|
|
96
|
-
npx @westbayberry/dg scan --mode block --json
|
|
97
|
-
```
|
|
98
|
-
|
|
99
|
-
Wire exit code `2` into your pipeline to fail the build.
|
|
100
|
-
|
|
101
|
-
## Configuration
|
|
102
|
-
|
|
103
|
-
Settings come from CLI flags, environment variables, or a `.dgrc.json` file in the project or home directory. CLI flags take precedence.
|
|
104
|
-
|
|
105
|
-
```json
|
|
106
|
-
{
|
|
107
|
-
"apiKey": "dg_...",
|
|
108
|
-
"mode": "block"
|
|
109
|
-
}
|
|
110
|
-
```
|
|
111
|
-
|
|
112
|
-
## Telemetry
|
|
113
|
-
|
|
114
|
-
The CLI sends anonymous crash reports to Sentry (error message, stack trace, Node version, OS, CLI version). It never sends package names, file paths, API keys, or scan results. API keys and home directory paths are redacted before transmission.
|
|
115
|
-
|
|
116
|
-
Opt out:
|
|
117
|
-
|
|
118
|
-
```bash
|
|
119
|
-
export DG_TELEMETRY=0 # or
|
|
120
|
-
export DO_NOT_TRACK=1
|
|
121
|
-
```
|
|
122
|
-
|
|
123
|
-
## Links
|
|
124
|
-
|
|
125
|
-
- [Dashboard](https://westbayberry.com/dashboard)
|
|
126
|
-
- [Documentation](https://westbayberry.com/docs) — setup walkthrough
|
|
127
|
-
- [CLI reference](https://westbayberry.com/docs/cli-reference) — every command and flag
|
|
128
|
-
- [Pricing](https://westbayberry.com/pricing)
|
|
129
|
-
- [Changelog](https://github.com/WestBayBerry/dependency-guardian-action/blob/main/CHANGELOG.md)
|