@westbayberry/dg 2.0.0 → 2.0.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/dist/commands/audit.js
CHANGED
|
@@ -12,6 +12,7 @@ import { loadUserConfig, saveUserConfig, setConfigValue } from "../config/settin
|
|
|
12
12
|
import { promptYesNo } from "../util/tty-prompt.js";
|
|
13
13
|
import { authStatus } from "../auth/store.js";
|
|
14
14
|
import { shouldLaunchAuditTui, launchAuditTui } from "../audit-ui/launch.js";
|
|
15
|
+
import { renderCommandHelp } from "./help.js";
|
|
15
16
|
export const auditCommand = {
|
|
16
17
|
name: "audit",
|
|
17
18
|
summary: "Audit the package you're about to publish for leaked secrets and risky files.",
|
|
@@ -95,7 +96,11 @@ export async function maybeAudit(args) {
|
|
|
95
96
|
if (args[0] !== "audit") {
|
|
96
97
|
return { handled: false, result: { exitCode: 0, stdout: "", stderr: "" } };
|
|
97
98
|
}
|
|
98
|
-
const
|
|
99
|
+
const sub = args.slice(1);
|
|
100
|
+
if (sub[0] === "--help" || sub[0] === "-h" || sub[0] === "help") {
|
|
101
|
+
return { handled: true, result: { exitCode: 0, stdout: renderCommandHelp(auditCommand), stderr: "" } };
|
|
102
|
+
}
|
|
103
|
+
const gathered = gather(sub);
|
|
99
104
|
if ("error" in gathered) {
|
|
100
105
|
return { handled: true, result: gatherError(gathered) };
|
|
101
106
|
}
|
|
@@ -13,6 +13,7 @@ const HEADLINES = {
|
|
|
13
13
|
license: "license policy",
|
|
14
14
|
"hash-mismatch": "artifact integrity mismatch",
|
|
15
15
|
"private-upload-disabled": "private artifact not scanned",
|
|
16
|
+
"needs-login": "sign-in required",
|
|
16
17
|
"api-unavailable": "scanner unavailable",
|
|
17
18
|
"quota-exceeded": "monthly scan limit reached",
|
|
18
19
|
"api-timeout": "scanner timed out",
|
|
@@ -27,6 +28,7 @@ const NEXT_STEP = {
|
|
|
27
28
|
license: "Replace the dependency or update your license policy.",
|
|
28
29
|
"hash-mismatch": "Clear your package cache and retry. If it persists, do not install.",
|
|
29
30
|
"private-upload-disabled": "Enable private artifact scanning to verify this package.",
|
|
31
|
+
"needs-login": "Run 'dg login' (free) to check packages from the registry before they install.",
|
|
30
32
|
"quota-exceeded": "Upgrade your plan or wait for your monthly limit to reset. See westbayberry.com/pricing."
|
|
31
33
|
};
|
|
32
34
|
export function describeBlockedInstall(decision) {
|
|
@@ -34,7 +36,7 @@ export function describeBlockedInstall(decision) {
|
|
|
34
36
|
const override = decision.forceOverride && !decision.forceOverride.allowed
|
|
35
37
|
? "not allowed by your policy"
|
|
36
38
|
: "re-run with --dg-force-install";
|
|
37
|
-
const nextStep = verifiedBad
|
|
39
|
+
const nextStep = verifiedBad || decision.cause === "needs-login"
|
|
38
40
|
? NEXT_STEP[decision.cause]
|
|
39
41
|
: "Re-check later with 'dg verify', or override if you accept the risk.";
|
|
40
42
|
return {
|
|
@@ -67,13 +69,13 @@ export function renderInstallDecision(decision) {
|
|
|
67
69
|
if (decision.dashboardUrl) {
|
|
68
70
|
lines.push(` Evidence: ${decision.dashboardUrl}`);
|
|
69
71
|
}
|
|
70
|
-
if (decision.unauthenticated) {
|
|
72
|
+
if (decision.unauthenticated && decision.cause !== "needs-login") {
|
|
71
73
|
lines.push(" Auth: local policy only (run 'dg login' for full coverage)");
|
|
72
74
|
}
|
|
73
75
|
lines.push(decision.forceOverride && !decision.forceOverride.allowed
|
|
74
76
|
? " Override: not allowed by your policy"
|
|
75
77
|
: " Override: re-run with --dg-force-install");
|
|
76
|
-
const next = verifiedBad
|
|
78
|
+
const next = verifiedBad || decision.cause === "needs-login"
|
|
77
79
|
? NEXT_STEP[decision.cause]
|
|
78
80
|
: "Re-check later with 'dg verify', or override if you accept the risk.";
|
|
79
81
|
if (next) {
|
package/dist/proxy/server.js
CHANGED
|
@@ -655,6 +655,15 @@ async function lookupVerdict(options, target, sha256, upstream, identity) {
|
|
|
655
655
|
reason: "You've reached your monthly scan limit. Upgrade at westbayberry.com/pricing or wait for it to reset."
|
|
656
656
|
};
|
|
657
657
|
}
|
|
658
|
+
if (response.status === 401) {
|
|
659
|
+
return {
|
|
660
|
+
verdict: "block",
|
|
661
|
+
packageName: artifactDisplayName(identity),
|
|
662
|
+
cause: "needs-login",
|
|
663
|
+
unauthenticated: true,
|
|
664
|
+
reason: "Checking a package from the registry before it installs requires sign-in."
|
|
665
|
+
};
|
|
666
|
+
}
|
|
658
667
|
if (!response.ok) {
|
|
659
668
|
return {
|
|
660
669
|
verdict: "block",
|
|
@@ -898,6 +907,7 @@ function isProxyCause(value) {
|
|
|
898
907
|
"license",
|
|
899
908
|
"hash-mismatch",
|
|
900
909
|
"private-upload-disabled",
|
|
910
|
+
"needs-login",
|
|
901
911
|
"api-unavailable",
|
|
902
912
|
"quota-exceeded",
|
|
903
913
|
"api-timeout",
|
|
@@ -37,6 +37,19 @@ function actionBadge(action) {
|
|
|
37
37
|
return { label: "Unknown", color: chalk.cyan };
|
|
38
38
|
return { label: "Pass", color: chalk.green };
|
|
39
39
|
}
|
|
40
|
+
function isYankedIncomplete(pkg) {
|
|
41
|
+
if (pkg.action !== "analysis_incomplete")
|
|
42
|
+
return false;
|
|
43
|
+
const haystack = [...(pkg.reasons ?? []), ...pkg.findings.map((f) => f.title ?? "")]
|
|
44
|
+
.join(" ")
|
|
45
|
+
.toLowerCase();
|
|
46
|
+
return haystack.includes("unpublish") || haystack.includes("yank") || haystack.includes("removed from the registr");
|
|
47
|
+
}
|
|
48
|
+
export function packageBadge(pkg) {
|
|
49
|
+
if (isYankedIncomplete(pkg))
|
|
50
|
+
return { label: "Removed", color: chalk.yellow };
|
|
51
|
+
return actionBadge(pkg.action);
|
|
52
|
+
}
|
|
40
53
|
const EVIDENCE_LIMIT = 2;
|
|
41
54
|
// Fixed lines outside the scrollable group area:
|
|
42
55
|
// 5 ScoreHeader box | 2 Flagged box top | 2 scroll indicators
|
|
@@ -1066,7 +1079,7 @@ export const InteractiveResultsView = ({ result, config: _config, durationMs, on
|
|
|
1066
1079
|
const dpGroup = groups[detailPane.groupIndex];
|
|
1067
1080
|
if (dpGroup) {
|
|
1068
1081
|
const dpRep = firstPackage(dpGroup);
|
|
1069
|
-
const { color: dpColor } =
|
|
1082
|
+
const { color: dpColor } = packageBadge(dpRep);
|
|
1070
1083
|
const dpScroll = detailPane.scroll;
|
|
1071
1084
|
const dpAbove = dpScroll;
|
|
1072
1085
|
const dpBelow = Math.max(0, detailLines.length - dpScroll - detailContentRows);
|
|
@@ -1080,7 +1093,7 @@ export const InteractiveResultsView = ({ result, config: _config, durationMs, on
|
|
|
1080
1093
|
const isCursor = globalIdx === clampedCursor;
|
|
1081
1094
|
const level = getLevel(globalIdx);
|
|
1082
1095
|
const rep = firstPackage(group);
|
|
1083
|
-
const { label, color } =
|
|
1096
|
+
const { label, color } = packageBadge(rep);
|
|
1084
1097
|
const names = groupNames(group);
|
|
1085
1098
|
const scoreStr = String(rep.score);
|
|
1086
1099
|
const lcInfo = rep.license;
|
package/dist/setup/plan.js
CHANGED
|
@@ -17,6 +17,10 @@ export const RC_SENTINEL = "dg-shell-rc-v1";
|
|
|
17
17
|
export const GUARD_HOOK_SENTINEL = "dg-git-hook-v1";
|
|
18
18
|
export const RC_BEGIN = "# >>> dg setup >>>";
|
|
19
19
|
export const RC_END = "# <<< dg setup <<<";
|
|
20
|
+
const LEGACY_RC_MARKERS = [
|
|
21
|
+
{ begin: "# >>> dg-managed >>>", end: "# <<< dg-managed <<<" }
|
|
22
|
+
];
|
|
23
|
+
const LEGACY_RC_CANDIDATES = [".zshrc", ".bashrc", ".bash_profile", ".profile", join(".config", "fish", "config.fish")];
|
|
20
24
|
export const SETUP_UNINSTALL_LOCK = "setup-uninstall";
|
|
21
25
|
export const SETUP_UNINSTALL_LOCK_STALE_MS = 30 * 60 * 1000;
|
|
22
26
|
export const STALE_SESSION_OLDER_THAN_MS = 24 * 60 * 60 * 1000;
|
|
@@ -191,6 +195,7 @@ export function uninstallSetup(options) {
|
|
|
191
195
|
reverseGitHookEntry(entry, removed, missing, warnings);
|
|
192
196
|
}
|
|
193
197
|
}
|
|
198
|
+
sweepLegacyRcBlocks(paths.homeDir, removed, warnings);
|
|
194
199
|
if (!options.all && !registryRead.malformed && options.keepConfig) {
|
|
195
200
|
writeRegistryWithLock(paths, {
|
|
196
201
|
version: 1,
|
|
@@ -481,6 +486,37 @@ function stripRcBlock(existing) {
|
|
|
481
486
|
const unterminatedPattern = new RegExp(`${escapeRegex(RC_BEGIN)}\\n[\\s\\S]*$`, "g");
|
|
482
487
|
return existing.replace(pattern, "").replace(unterminatedPattern, "");
|
|
483
488
|
}
|
|
489
|
+
function sweepLegacyRcBlocks(homeDir, removed, warnings) {
|
|
490
|
+
for (const rel of LEGACY_RC_CANDIDATES) {
|
|
491
|
+
const rcPath = join(homeDir, rel);
|
|
492
|
+
const existing = readText(rcPath);
|
|
493
|
+
if (!existing) {
|
|
494
|
+
continue;
|
|
495
|
+
}
|
|
496
|
+
let next = existing;
|
|
497
|
+
for (const marker of LEGACY_RC_MARKERS) {
|
|
498
|
+
if (!next.includes(marker.begin)) {
|
|
499
|
+
continue;
|
|
500
|
+
}
|
|
501
|
+
if (!next.includes(marker.end)) {
|
|
502
|
+
warnings.push(`legacy dg block in ${rcPath} is missing its end marker; left untouched`);
|
|
503
|
+
continue;
|
|
504
|
+
}
|
|
505
|
+
const pattern = new RegExp(`${escapeRegex(marker.begin)}\\n[\\s\\S]*?${escapeRegex(marker.end)}\\n?`, "g");
|
|
506
|
+
next = next.replace(pattern, "");
|
|
507
|
+
}
|
|
508
|
+
if (next === existing) {
|
|
509
|
+
continue;
|
|
510
|
+
}
|
|
511
|
+
try {
|
|
512
|
+
writeFileSync(rcPath, next, "utf8");
|
|
513
|
+
removed.push(`${rcPath} (legacy dg block)`);
|
|
514
|
+
}
|
|
515
|
+
catch (error) {
|
|
516
|
+
warnings.push(`could not strip legacy dg block from ${rcPath}: ${error instanceof Error ? error.message : "write error"}`);
|
|
517
|
+
}
|
|
518
|
+
}
|
|
519
|
+
}
|
|
484
520
|
export function cleanupEntry(kind, path, mode, now, sentinel) {
|
|
485
521
|
return {
|
|
486
522
|
kind,
|