@westbayberry/dg 1.3.3 → 2.0.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.
Files changed (126) hide show
  1. package/LICENSE +1 -201
  2. package/NOTICE +1 -4
  3. package/README.md +293 -0
  4. package/dist/api/analyze.js +210 -0
  5. package/dist/audit/deep.js +180 -0
  6. package/dist/audit/detectors.js +247 -0
  7. package/dist/audit/events.js +41 -0
  8. package/dist/audit/rules.js +426 -0
  9. package/dist/audit-ui/AuditApp.js +39 -0
  10. package/dist/audit-ui/components/AuditHeader.js +24 -0
  11. package/dist/audit-ui/components/AuditResultsView.js +307 -0
  12. package/dist/audit-ui/components/DeepStatusRow.js +11 -0
  13. package/dist/audit-ui/export.js +85 -0
  14. package/dist/audit-ui/format.js +34 -0
  15. package/dist/audit-ui/launch.js +34 -0
  16. package/dist/auth/device-login.js +271 -0
  17. package/dist/auth/env-token.js +6 -0
  18. package/dist/auth/login-app.js +156 -0
  19. package/dist/auth/store.js +147 -0
  20. package/dist/bin/dg.js +71 -0
  21. package/dist/commands/audit.js +357 -0
  22. package/dist/commands/completion.js +116 -0
  23. package/dist/commands/config.js +99 -0
  24. package/dist/commands/doctor.js +39 -0
  25. package/dist/commands/explain.js +100 -0
  26. package/dist/commands/guard-commit.js +158 -0
  27. package/dist/commands/help.js +74 -0
  28. package/dist/commands/licenses.js +435 -0
  29. package/dist/commands/login.js +81 -0
  30. package/dist/commands/logout.js +37 -0
  31. package/dist/commands/router.js +98 -0
  32. package/dist/commands/scan.js +18 -0
  33. package/dist/commands/service.js +475 -0
  34. package/dist/commands/setup.js +302 -0
  35. package/dist/commands/status.js +115 -0
  36. package/dist/commands/suggest.js +35 -0
  37. package/dist/commands/types.js +4 -0
  38. package/dist/commands/unavailable.js +11 -0
  39. package/dist/commands/uninstall.js +111 -0
  40. package/dist/commands/update.js +210 -0
  41. package/dist/commands/verify.js +151 -0
  42. package/dist/commands/version.js +22 -0
  43. package/dist/commands/wrap.js +55 -0
  44. package/dist/config/settings.js +302 -0
  45. package/dist/install-ui/LiveInstall.js +24 -0
  46. package/dist/install-ui/block-render.js +83 -0
  47. package/dist/install-ui/live-install-app.js +48 -0
  48. package/dist/install-ui/prompt.js +24 -0
  49. package/dist/launcher/classify.js +116 -0
  50. package/dist/launcher/env.js +53 -0
  51. package/dist/launcher/live-install.js +50 -0
  52. package/dist/launcher/output-redaction.js +77 -0
  53. package/dist/launcher/preflight-prompt.js +139 -0
  54. package/dist/launcher/resolve-real-binary.js +73 -0
  55. package/dist/launcher/run.js +417 -0
  56. package/dist/policy/evaluate.js +128 -0
  57. package/dist/presentation/mode.js +52 -0
  58. package/dist/presentation/theme.js +29 -0
  59. package/dist/proxy/buffer-budget.js +64 -0
  60. package/dist/proxy/ca.js +126 -0
  61. package/dist/proxy/classify-host.js +26 -0
  62. package/dist/proxy/enforcement.js +102 -0
  63. package/dist/proxy/metadata-map.js +336 -0
  64. package/dist/proxy/server.js +909 -0
  65. package/dist/proxy/upstream-proxy.js +102 -0
  66. package/dist/proxy/worker.js +39 -0
  67. package/dist/publish-set/collect.js +51 -0
  68. package/dist/publish-set/no-exec-shell.js +19 -0
  69. package/dist/publish-set/npm.js +109 -0
  70. package/dist/publish-set/pack.js +36 -0
  71. package/dist/publish-set/pypi.js +59 -0
  72. package/dist/runtime/cli.js +17 -0
  73. package/dist/runtime/first-run.js +60 -0
  74. package/dist/runtime/node-version.js +58 -0
  75. package/dist/runtime/nudges.js +105 -0
  76. package/dist/scan/analyze-worker.js +21 -0
  77. package/dist/scan/collect.js +153 -0
  78. package/dist/scan/command.js +159 -0
  79. package/dist/scan/discovery.js +209 -0
  80. package/dist/scan/render.js +240 -0
  81. package/dist/scan/scanner-report.js +82 -0
  82. package/dist/scan/staged.js +173 -0
  83. package/dist/scan/types.js +1 -0
  84. package/dist/scan-ui/LegacyApp.js +156 -0
  85. package/dist/scan-ui/alt-screen.js +84 -0
  86. package/dist/scan-ui/api-aliases.js +1 -0
  87. package/dist/scan-ui/components/ErrorView.js +23 -0
  88. package/dist/scan-ui/components/InteractiveResultsView.js +1166 -0
  89. package/dist/scan-ui/components/ProgressBar.js +89 -0
  90. package/dist/scan-ui/components/ProjectSelector.js +62 -0
  91. package/dist/scan-ui/components/ScoreHeader.js +20 -0
  92. package/dist/scan-ui/components/SetupBanner.js +13 -0
  93. package/dist/scan-ui/components/Spinner.js +4 -0
  94. package/dist/scan-ui/format-helpers.js +40 -0
  95. package/dist/scan-ui/hooks/useExpandAnimation.js +40 -0
  96. package/dist/scan-ui/hooks/useScan.js +113 -0
  97. package/dist/scan-ui/hooks/useTerminalSize.js +24 -0
  98. package/dist/scan-ui/launch.js +27 -0
  99. package/dist/scan-ui/logo.js +91 -0
  100. package/dist/scan-ui/shims.js +30 -0
  101. package/dist/security/sanitize.js +28 -0
  102. package/dist/service/state.js +837 -0
  103. package/dist/service/trust-store.js +234 -0
  104. package/dist/service/worker.js +88 -0
  105. package/dist/setup/git-hook.js +244 -0
  106. package/dist/setup/optional-support.js +58 -0
  107. package/dist/setup/plan.js +899 -0
  108. package/dist/state/cleanup-registry.js +60 -0
  109. package/dist/state/index.js +5 -0
  110. package/dist/state/locks.js +161 -0
  111. package/dist/state/paths.js +24 -0
  112. package/dist/state/sessions.js +170 -0
  113. package/dist/state/store.js +50 -0
  114. package/dist/telemetry/events.js +40 -0
  115. package/dist/util/git.js +20 -0
  116. package/dist/util/tty-prompt.js +43 -0
  117. package/dist/verify/local.js +400 -0
  118. package/dist/verify/package-check.js +240 -0
  119. package/dist/verify/preflight.js +698 -0
  120. package/dist/verify/render.js +184 -0
  121. package/dist/verify/types.js +1 -0
  122. package/package.json +33 -50
  123. package/dist/index.mjs +0 -54116
  124. package/dist/postinstall.mjs +0 -731
  125. package/dist/python-hook/dg_pip_hook.pth +0 -1
  126. package/dist/python-hook/dg_pip_hook.py +0 -130
@@ -0,0 +1,100 @@
1
+ import { EXIT_USAGE } from "./types.js";
2
+ import { closestCommand } from "./suggest.js";
3
+ export const EXPLANATIONS = {
4
+ "npm-lifecycle-script": {
5
+ what: "The package declares an install lifecycle script (preinstall/install/postinstall/prepare) that runs arbitrary code on your machine during 'npm install'.",
6
+ next: "Review the script in the package source. Prefer a version without install scripts, or install with --ignore-scripts."
7
+ },
8
+ "unverified-network-dependency": {
9
+ what: "A dependency is fetched from a raw URL (http/git) instead of the registry, so it has no registry identity or integrity to verify.",
10
+ next: "Pin the dependency to a registry version, or run 'dg verify <url>' before installing."
11
+ },
12
+ "local-artifact-dependency": {
13
+ what: "A dependency points at a local file path; its contents are whatever is on disk and are not registry-verified.",
14
+ next: "Run 'dg verify <path>' on the artifact, or replace it with a registry version."
15
+ },
16
+ malware: {
17
+ what: "The scanner confirmed this package version contains malicious code (for example credential theft or a backdoor).",
18
+ next: "Do not install it. Remove the dependency or pin a known-safe version; the dashboard link in the block shows the evidence."
19
+ },
20
+ policy: {
21
+ what: "Your organization or local policy denies this package, independent of a malware verdict.",
22
+ next: "Pick a compliant alternative, or ask your policy admin to allow it."
23
+ },
24
+ license: {
25
+ what: "The package license violates your license policy (for example a copyleft license your policy denies).",
26
+ next: "Replace the dependency, or update the license policy if the obligation is acceptable."
27
+ },
28
+ "hash-mismatch": {
29
+ what: "The downloaded artifact's hash does not match the artifact the scanner verified, which can indicate a tampered mirror or cache.",
30
+ next: "Clear your package cache and retry. If it persists, do not install and report it."
31
+ },
32
+ "private-upload-disabled": {
33
+ what: "The artifact comes from a private registry and private-artifact scanning is disabled, so the scanner never saw its contents.",
34
+ next: "Enable private artifact scanning (DG_SCAN_TARBALL_UPLOAD) or verify the artifact out of band."
35
+ },
36
+ "api-unavailable": {
37
+ what: "The scanner could not be reached, so no verdict exists for this install; policy decided fail-open or fail-closed.",
38
+ next: "Run 'dg doctor' and 'dg login' to check connectivity, then retry or re-check with 'dg verify'."
39
+ },
40
+ "quota-exceeded": {
41
+ what: "You've used your plan's monthly scan allowance, so this install could not be verified — it was blocked rather than installed unverified.",
42
+ next: "Upgrade your plan at westbayberry.com/pricing, or wait for your monthly limit to reset."
43
+ },
44
+ "api-timeout": {
45
+ what: "The scanner did not answer within the verdict timeout, so no verdict exists for this install.",
46
+ next: "Retry; if it persists, run 'dg doctor' to check connectivity."
47
+ },
48
+ "registry-timeout": {
49
+ what: "The upstream package registry did not respond, so the artifact was never fetched or verified.",
50
+ next: "Check the registry status and retry."
51
+ },
52
+ "analysis-incomplete": {
53
+ what: "The scanner analyzed only part of this package; the unanalyzed remainder is unverified, not safe.",
54
+ next: "Retry later, or override with --dg-force-install only if you accept the risk."
55
+ },
56
+ "unsupported-manager": {
57
+ what: "This package manager is gated in this dg build, so installs through it are not protected.",
58
+ next: "Use a supported manager through dg (npm, pnpm, yarn, pip, pipx, uv, uvx, cargo)."
59
+ },
60
+ "proxy-setup-failure": {
61
+ what: "dg's per-install enforcement proxy could not start, so protected installs fail closed rather than run unverified.",
62
+ next: "Run 'dg doctor' for the failing check, then retry."
63
+ }
64
+ };
65
+ export const explainCommand = {
66
+ name: "explain",
67
+ summary: "Explain a dg finding or block cause and what to do about it.",
68
+ usage: "dg explain <finding-or-cause-id>",
69
+ args: [{ name: "<id>", summary: "A finding id or block cause (see the list below)." }],
70
+ examples: ["dg explain malware", "dg explain quota-exceeded"],
71
+ details: [
72
+ `Known ids: ${Object.keys(EXPLANATIONS).sort().join(", ")}`
73
+ ],
74
+ handler: (context) => runExplain(context.args)
75
+ };
76
+ function runExplain(args) {
77
+ const id = args[0];
78
+ if (!id || args.length > 1) {
79
+ return {
80
+ exitCode: EXIT_USAGE,
81
+ stdout: "",
82
+ stderr: "dg explain: expected exactly one finding or cause id. Run 'dg explain --help'.\n"
83
+ };
84
+ }
85
+ const explanation = EXPLANATIONS[id];
86
+ if (!explanation) {
87
+ const suggestion = closestCommand(id, Object.keys(EXPLANATIONS));
88
+ const hint = suggestion ? ` Did you mean '${suggestion}'?` : "";
89
+ return {
90
+ exitCode: EXIT_USAGE,
91
+ stdout: "",
92
+ stderr: `dg explain: unknown id '${id}'.${hint} Run 'dg explain --help' for the list.\n`
93
+ };
94
+ }
95
+ return {
96
+ exitCode: 0,
97
+ stdout: `${id}\n\n${explanation.what}\n\nNext: ${explanation.next}\n`,
98
+ stderr: ""
99
+ };
100
+ }
@@ -0,0 +1,158 @@
1
+ import { EXIT_USAGE } from "./types.js";
2
+ import { resolvePresentation } from "../presentation/mode.js";
3
+ import { createTheme } from "../presentation/theme.js";
4
+ import { promptYesNo } from "../util/tty-prompt.js";
5
+ import { applyGitHook, gitHookState, planGitHook, removeGitHookForRepo, resolveGitRepo, verifyGitHook } from "../setup/git-hook.js";
6
+ export const guardCommitCommand = {
7
+ name: "guard-commit",
8
+ summary: "Scan staged dependencies before every commit in this repo.",
9
+ usage: "dg guard-commit [off | --check | --print] [--yes]",
10
+ args: [{ name: "[off]", summary: "Remove the hook and restore any hook it chained (alias remove/uninstall)." }],
11
+ flags: [
12
+ { flag: "--check", summary: "Verify the hook is installed and will actually fire." },
13
+ { flag: "--print", summary: "Preview what it will write, change nothing." },
14
+ { flag: "--yes", summary: "Install without the confirmation prompt (alias -y)." }
15
+ ],
16
+ examples: ["dg guard-commit", "dg guard-commit --check", "dg guard-commit off"],
17
+ details: [
18
+ "Installs a git pre-commit hook that scans staged lockfile changes and blocks a commit that would add a malicious dependency. Override a block with 'git commit --no-verify'.",
19
+ "It resolves the real hooks directory (honouring core.hooksPath/husky and worktrees) and chains any existing pre-commit hook. Tune behaviour with the gitHook.onWarn / gitHook.onIncomplete config keys."
20
+ ],
21
+ handler: (context) => run(context)
22
+ };
23
+ function parse(args) {
24
+ let off = false;
25
+ let check = false;
26
+ let print = false;
27
+ let yes = false;
28
+ for (const arg of args) {
29
+ if (arg === "off" || arg === "remove" || arg === "uninstall") {
30
+ off = true;
31
+ }
32
+ else if (arg === "install" || arg === "on") {
33
+ off = false;
34
+ }
35
+ else if (arg === "--check") {
36
+ check = true;
37
+ }
38
+ else if (arg === "--print") {
39
+ print = true;
40
+ }
41
+ else if (arg === "--yes" || arg === "-y") {
42
+ yes = true;
43
+ }
44
+ else {
45
+ return { error: `unknown argument '${arg}'` };
46
+ }
47
+ }
48
+ return { off, check, print, yes };
49
+ }
50
+ function run(context) {
51
+ const parsed = parse(context.args);
52
+ if ("error" in parsed) {
53
+ return {
54
+ exitCode: EXIT_USAGE,
55
+ stdout: "",
56
+ stderr: `dg guard-commit: ${parsed.error}. Usage: ${guardCommitCommand.usage}\n`
57
+ };
58
+ }
59
+ const theme = createTheme(resolvePresentation().color);
60
+ const repo = resolveGitRepo();
61
+ if ("error" in repo) {
62
+ return { exitCode: EXIT_USAGE, stdout: "", stderr: `dg guard-commit: ${repo.error}.\n` };
63
+ }
64
+ if (parsed.off) {
65
+ return remove(repo, theme);
66
+ }
67
+ if (parsed.check) {
68
+ return check(repo, theme);
69
+ }
70
+ if (parsed.print) {
71
+ return print(repo);
72
+ }
73
+ return install(repo, theme, parsed.yes);
74
+ }
75
+ function install(repo, theme, yes) {
76
+ const plan = planGitHook(repo);
77
+ const err = process.stderr;
78
+ const accent = (text) => theme.paint("accent", text);
79
+ const muted = (text) => theme.paint("muted", text);
80
+ if (resolvePresentation().mode === "rich" && !yes) {
81
+ err.write(`\n Scan staged dependencies before every commit in ${accent(repo.root)}${muted(".")}\n`);
82
+ if (plan.willChain) {
83
+ err.write(` ${muted("Your existing pre-commit hook will be kept and run after dg.")}\n`);
84
+ }
85
+ err.write(` ${muted("Reversible with")} ${accent("dg guard-commit off")}${muted(".")}\n\n`);
86
+ const proceed = promptYesNo(` ${accent("Proceed?")}`, true);
87
+ if (proceed === false) {
88
+ return { exitCode: 0, stdout: "", stderr: ` ${muted("cancelled — nothing written")}\n` };
89
+ }
90
+ }
91
+ const result = applyGitHook(repo);
92
+ if (result.active) {
93
+ return {
94
+ exitCode: 0,
95
+ stdout: "",
96
+ stderr: `\n ${theme.paint("pass", "✓ commits in this repo are now scanned")}\n\n`
97
+ };
98
+ }
99
+ return {
100
+ exitCode: 1,
101
+ stdout: "",
102
+ stderr: `\n ${theme.paint("warn", "⚠ installed, but it is not active yet:")}\n${renderChecks(result.checks, theme)}`
103
+ };
104
+ }
105
+ function check(repo, theme) {
106
+ if (gitHookState(repo) !== "managed") {
107
+ return {
108
+ exitCode: 1,
109
+ stdout: "",
110
+ stderr: ` ${theme.paint("warn", "⚠")} not set up in this repo — run ${theme.paint("accent", "dg guard-commit")}\n`
111
+ };
112
+ }
113
+ const checks = verifyGitHook(repo);
114
+ const active = checks.every((entry) => entry.ok);
115
+ const head = active
116
+ ? ` ${theme.paint("pass", "✓ commit guard is active")}\n`
117
+ : ` ${theme.paint("warn", "⚠ commit guard is installed but not firing:")}\n`;
118
+ return { exitCode: active ? 0 : 1, stdout: "", stderr: `${head}${renderChecks(checks, theme)}` };
119
+ }
120
+ function print(repo) {
121
+ const plan = planGitHook(repo);
122
+ const lines = [
123
+ "dg guard-commit write plan",
124
+ "",
125
+ "No files are changed until you run dg guard-commit.",
126
+ `- write pre-commit hook: ${plan.context.hookTarget}`
127
+ ];
128
+ if (plan.willChain) {
129
+ lines.push("- back up + chain your existing hook so it still runs");
130
+ }
131
+ else if (plan.state === "managed") {
132
+ lines.push("- (a dg hook is already installed; it will be refreshed)");
133
+ }
134
+ lines.push("- record it for 'dg guard-commit off' / 'dg uninstall'");
135
+ return { exitCode: 0, stdout: `${lines.join("\n")}\n`, stderr: "" };
136
+ }
137
+ function remove(repo, theme) {
138
+ const result = removeGitHookForRepo(repo);
139
+ if (result.found === 0) {
140
+ return {
141
+ exitCode: 0,
142
+ stdout: "",
143
+ stderr: ` ${theme.paint("muted", "no dg guard-commit hook in this repo")}\n`
144
+ };
145
+ }
146
+ const restored = result.removed.some((path) => path.includes(".dg-chained-"));
147
+ const note = restored ? ` ${theme.paint("muted", "(restored your previous hook)")}` : "";
148
+ return {
149
+ exitCode: 0,
150
+ stdout: "",
151
+ stderr: ` ${theme.paint("pass", "✓ removed dg commit guard")}${note}\n`
152
+ };
153
+ }
154
+ function renderChecks(checks, theme) {
155
+ return `${checks
156
+ .map((entry) => ` ${entry.ok ? theme.paint("pass", "✓") : theme.paint("block", "✘")} ${theme.paint("muted", entry.detail)}`)
157
+ .join("\n")}\n`;
158
+ }
@@ -0,0 +1,74 @@
1
+ const PRODUCT_DESCRIPTION = "Dependency Guardian supply-chain firewall CLI.";
2
+ const COMMON_COMMANDS = ["scan", "verify", "audit", "licenses", "setup", "guard-commit", "doctor", "login"];
3
+ const GLOBAL_FLAGS_FOOTER = "Global: --help/-h · --version/-v · --json (where supported) · --no-color / --force-color (also NO_COLOR, FORCE_COLOR).";
4
+ function isWrapper(command) {
5
+ return command.summary.includes("prefix-mode routing");
6
+ }
7
+ export function renderRootHelp(version, commands, options = {}) {
8
+ const header = [
9
+ `dg ${version}`,
10
+ "",
11
+ PRODUCT_DESCRIPTION,
12
+ "",
13
+ "Usage:",
14
+ " dg <command> [options]",
15
+ ""
16
+ ];
17
+ if (!options.all) {
18
+ const common = COMMON_COMMANDS
19
+ .map((name) => commands.find((command) => command.name === name))
20
+ .filter((command) => Boolean(command));
21
+ const lines = [
22
+ ...header,
23
+ "Protect installs: dg npm install <pkg> (or run 'dg setup' once)",
24
+ "Audit a project: dg scan",
25
+ "",
26
+ "Common commands:"
27
+ ];
28
+ for (const command of common) {
29
+ lines.push(` ${command.name.padEnd(12)} ${command.summary}`);
30
+ }
31
+ lines.push("", "Run 'dg <command> --help' for flags and examples, or 'dg --help-all' for every command.", GLOBAL_FLAGS_FOOTER);
32
+ return `${lines.join("\n")}\n`;
33
+ }
34
+ const wrappers = commands.filter(isWrapper);
35
+ const standalone = commands.filter((command) => !isWrapper(command));
36
+ const lines = [...header, "Commands:"];
37
+ for (const command of standalone) {
38
+ lines.push(` ${command.name.padEnd(14)} ${command.summary}`);
39
+ }
40
+ lines.push("", "Package-manager prefix mode (run any through dg):", ` ${wrappers.map((command) => command.name).join(", ")}`);
41
+ lines.push("", "Run 'dg <command> --help' for that command's flags and examples.", GLOBAL_FLAGS_FOOTER);
42
+ return `${lines.join("\n")}\n`;
43
+ }
44
+ function column(rows) {
45
+ const width = Math.max(...rows.map((row) => row.left.length));
46
+ return rows.map((row) => ` ${row.left.padEnd(width)} ${row.summary}`);
47
+ }
48
+ export function renderCommandHelp(command, path = [command.name]) {
49
+ const lines = [
50
+ `dg ${path.join(" ")}`,
51
+ "",
52
+ command.summary,
53
+ "",
54
+ "Usage:",
55
+ ` ${command.usage}`
56
+ ];
57
+ if (command.args && command.args.length > 0) {
58
+ lines.push("", "Arguments:", ...column(command.args.map((arg) => ({ left: arg.name, summary: arg.summary }))));
59
+ }
60
+ if (command.subcommands && command.subcommands.length > 0) {
61
+ lines.push("", "Subcommands:", ...column(command.subcommands.map((subcommand) => ({ left: subcommand.name, summary: subcommand.summary }))));
62
+ }
63
+ if (command.flags && command.flags.length > 0) {
64
+ lines.push("", "Flags:", ...column(command.flags.map((flag) => ({ left: flag.value ? `${flag.flag} ${flag.value}` : flag.flag, summary: flag.summary }))));
65
+ }
66
+ if (command.examples && command.examples.length > 0) {
67
+ lines.push("", "Examples:", ...command.examples.map((example) => ` ${example}`));
68
+ }
69
+ if (command.details.length > 0) {
70
+ lines.push("", ...command.details);
71
+ }
72
+ lines.push("", GLOBAL_FLAGS_FOOTER);
73
+ return `${lines.join("\n")}\n`;
74
+ }