@westbayberry/dg 2.0.0 → 2.0.2
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/api/analyze.js
CHANGED
|
@@ -182,6 +182,14 @@ function resolveApiBaseUrl(env) {
|
|
|
182
182
|
function resolveToken(env) {
|
|
183
183
|
return envAuthToken(env) ?? readAuthStateSafe(env)?.token;
|
|
184
184
|
}
|
|
185
|
+
export function identityHeaders(env) {
|
|
186
|
+
const headers = { "X-Device-Id": getOrCreateDeviceId(env) };
|
|
187
|
+
const token = resolveToken(env);
|
|
188
|
+
if (token) {
|
|
189
|
+
headers.Authorization = `Bearer ${token}`;
|
|
190
|
+
}
|
|
191
|
+
return headers;
|
|
192
|
+
}
|
|
185
193
|
function readAuthStateSafe(env) {
|
|
186
194
|
try {
|
|
187
195
|
return readAuthState(env);
|
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
|
@@ -15,6 +15,7 @@ import { artifactDisplayName, artifactUrlHash, extractRegistryMetadataIdentities
|
|
|
15
15
|
import { authorityFor, connectViaUpstreamProxy, selectUpstreamProxy } from "./upstream-proxy.js";
|
|
16
16
|
import { redactSecrets } from "../launcher/output-redaction.js";
|
|
17
17
|
import { envAuthToken } from "../auth/env-token.js";
|
|
18
|
+
import { identityHeaders } from "../api/analyze.js";
|
|
18
19
|
export async function startProductionHttpProxy(options) {
|
|
19
20
|
const ca = createEphemeralCertificateAuthority(options.session.files.ca);
|
|
20
21
|
const state = {
|
|
@@ -629,7 +630,8 @@ async function lookupVerdict(options, target, sha256, upstream, identity) {
|
|
|
629
630
|
const response = await fetch(`${options.apiBaseUrl}/v1/install-verdict`, {
|
|
630
631
|
method: "POST",
|
|
631
632
|
headers: {
|
|
632
|
-
"Content-Type": "application/json"
|
|
633
|
+
"Content-Type": "application/json",
|
|
634
|
+
...identityHeaders(options.env)
|
|
633
635
|
},
|
|
634
636
|
body: JSON.stringify({
|
|
635
637
|
manager: options.classification.manager,
|
|
@@ -655,6 +657,15 @@ async function lookupVerdict(options, target, sha256, upstream, identity) {
|
|
|
655
657
|
reason: "You've reached your monthly scan limit. Upgrade at westbayberry.com/pricing or wait for it to reset."
|
|
656
658
|
};
|
|
657
659
|
}
|
|
660
|
+
if (response.status === 401) {
|
|
661
|
+
return {
|
|
662
|
+
verdict: "block",
|
|
663
|
+
packageName: artifactDisplayName(identity),
|
|
664
|
+
cause: "needs-login",
|
|
665
|
+
unauthenticated: true,
|
|
666
|
+
reason: "Checking a package from the registry before it installs requires sign-in."
|
|
667
|
+
};
|
|
668
|
+
}
|
|
658
669
|
if (!response.ok) {
|
|
659
670
|
return {
|
|
660
671
|
verdict: "block",
|
|
@@ -898,6 +909,7 @@ function isProxyCause(value) {
|
|
|
898
909
|
"license",
|
|
899
910
|
"hash-mismatch",
|
|
900
911
|
"private-upload-disabled",
|
|
912
|
+
"needs-login",
|
|
901
913
|
"api-unavailable",
|
|
902
914
|
"quota-exceeded",
|
|
903
915
|
"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,
|