@westbayberry/dg 2.0.10 → 2.1.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/dist/api/analyze.js +5 -3
- package/dist/bin/dg.js +1 -1
- package/dist/commands/completion.js +2 -1
- package/dist/commands/config.js +11 -3
- package/dist/commands/decisions.js +155 -0
- package/dist/commands/explain.js +6 -2
- package/dist/commands/router.js +2 -0
- package/dist/commands/scan.js +2 -1
- package/dist/commands/status.js +5 -2
- package/dist/config/settings.js +144 -25
- package/dist/decisions/apply.js +128 -0
- package/dist/decisions/remember-prompt.js +97 -0
- package/dist/install-ui/block-render.js +21 -4
- package/dist/install-ui/prompt.js +14 -0
- package/dist/launcher/install-preflight.js +126 -13
- package/dist/launcher/preflight-prompt.js +29 -2
- package/dist/launcher/run.js +14 -3
- package/dist/policy/cooldown.js +104 -0
- package/dist/policy/evaluate.js +0 -15
- package/dist/presentation/provenance.js +23 -0
- package/dist/project/dgfile.js +307 -0
- package/dist/proxy/enforcement.js +2 -1
- package/dist/proxy/metadata-map.js +25 -1
- package/dist/proxy/server.js +31 -2
- package/dist/scan/collect.js +10 -4
- package/dist/scan/command.js +35 -8
- package/dist/scan/discovery.js +66 -4
- package/dist/scan/render.js +35 -4
- package/dist/scan/scanner-report.js +31 -4
- package/dist/scan/staged.js +69 -10
- package/dist/scan-ui/LegacyApp.js +4 -4
- package/dist/scan-ui/components/InteractiveResultsView.js +64 -7
- package/dist/scan-ui/hooks/useScan.js +31 -3
- package/dist/scan-ui/launch.js +4 -1
- package/dist/scan-ui/shims.js +3 -0
- package/dist/scripts/detect.js +153 -0
- package/dist/scripts/gate.js +170 -0
- package/dist/scripts/rebuild.js +28 -0
- package/dist/setup/plan.js +36 -1
- package/dist/util/json-file.js +24 -0
- package/dist/util/tty-prompt.js +13 -6
- package/dist/verify/package-check.js +12 -0
- package/package.json +9 -1
package/dist/setup/plan.js
CHANGED
|
@@ -8,6 +8,7 @@ import { dgVersion } from "../commands/version.js";
|
|
|
8
8
|
import { compareVersions, readLatestVersion } from "../commands/update.js";
|
|
9
9
|
import { AuthError, authStatus, displayTier, readAuthState } from "../auth/store.js";
|
|
10
10
|
import { ConfigError, loadUserConfig } from "../config/settings.js";
|
|
11
|
+
import { describeCooldownSettings } from "../policy/cooldown.js";
|
|
11
12
|
import { resolveRealBinary } from "../launcher/resolve-real-binary.js";
|
|
12
13
|
import { packageManagerNames } from "../launcher/classify.js";
|
|
13
14
|
import { readServiceState } from "../service/state.js";
|
|
@@ -42,6 +43,7 @@ const DOCTOR_GROUP_BY_NAME = {
|
|
|
42
43
|
"cleanup-registry-stale-entries": "setup",
|
|
43
44
|
config: "setup",
|
|
44
45
|
policy: "setup",
|
|
46
|
+
"script-gate": "setup",
|
|
45
47
|
telemetry: "setup",
|
|
46
48
|
shims: "setup",
|
|
47
49
|
"shell-rc": "setup",
|
|
@@ -275,6 +277,7 @@ export function doctorReport(options = {}) {
|
|
|
275
277
|
checks.push(configCheck(paths, env));
|
|
276
278
|
checks.push(authCheck(env));
|
|
277
279
|
checks.push(policyCheck(env));
|
|
280
|
+
checks.push(scriptGateCheck(env));
|
|
278
281
|
checks.push(telemetryCheck(env));
|
|
279
282
|
const missingShims = SHIM_COMMANDS.filter((command) => !validShim(join(shimDir, command), command));
|
|
280
283
|
checks.push({
|
|
@@ -1090,7 +1093,7 @@ function policyCheck(env) {
|
|
|
1090
1093
|
return {
|
|
1091
1094
|
name: "policy",
|
|
1092
1095
|
status: "pass",
|
|
1093
|
-
message: `Local policy mode ${config.policy.mode}; project allowlists trusted: ${config.policy.trustProjectAllowlists}`
|
|
1096
|
+
message: `Local policy mode ${config.policy.mode}; project allowlists trusted: ${config.policy.trustProjectAllowlists}; release cooldown ${describeCooldownSettings(config, env)}`
|
|
1094
1097
|
};
|
|
1095
1098
|
}
|
|
1096
1099
|
catch (error) {
|
|
@@ -1101,6 +1104,38 @@ function policyCheck(env) {
|
|
|
1101
1104
|
};
|
|
1102
1105
|
}
|
|
1103
1106
|
}
|
|
1107
|
+
function scriptGateCheck(env) {
|
|
1108
|
+
try {
|
|
1109
|
+
const mode = loadUserConfig(env).scriptGate.mode;
|
|
1110
|
+
if (mode === "off") {
|
|
1111
|
+
return {
|
|
1112
|
+
name: "script-gate",
|
|
1113
|
+
status: "pass",
|
|
1114
|
+
message: "Install-script gate is off; protected installs do not report script-running packages"
|
|
1115
|
+
};
|
|
1116
|
+
}
|
|
1117
|
+
if (mode === "enforce") {
|
|
1118
|
+
return {
|
|
1119
|
+
name: "script-gate",
|
|
1120
|
+
status: "warn",
|
|
1121
|
+
message: "scriptGate.mode is enforce, but this dg release observes only — installs report script-running packages without blocking",
|
|
1122
|
+
fix: "enforcement ships in an upcoming release; dg config set scriptGate.mode observe clears this warning"
|
|
1123
|
+
};
|
|
1124
|
+
}
|
|
1125
|
+
return {
|
|
1126
|
+
name: "script-gate",
|
|
1127
|
+
status: "pass",
|
|
1128
|
+
message: "Install-script gate observes: protected npm/yarn installs report packages that ran install scripts (pnpm blocks them natively)"
|
|
1129
|
+
};
|
|
1130
|
+
}
|
|
1131
|
+
catch (error) {
|
|
1132
|
+
return {
|
|
1133
|
+
name: "script-gate",
|
|
1134
|
+
status: "fail",
|
|
1135
|
+
message: error instanceof ConfigError ? error.message : "Unable to read script gate config"
|
|
1136
|
+
};
|
|
1137
|
+
}
|
|
1138
|
+
}
|
|
1104
1139
|
function telemetryCheck(env) {
|
|
1105
1140
|
try {
|
|
1106
1141
|
const config = loadUserConfig(env);
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { mkdirSync, renameSync, rmSync, writeFileSync } from "node:fs";
|
|
2
|
+
import { randomUUID } from "node:crypto";
|
|
3
|
+
import { dirname } from "node:path";
|
|
4
|
+
export function writeJsonAtomic(path, value, options = {}) {
|
|
5
|
+
mkdirSync(dirname(path), {
|
|
6
|
+
recursive: true,
|
|
7
|
+
mode: options.dirMode ?? 0o700
|
|
8
|
+
});
|
|
9
|
+
const tempPath = `${path}.${process.pid}.${randomUUID()}.tmp`;
|
|
10
|
+
try {
|
|
11
|
+
writeFileSync(tempPath, `${JSON.stringify(value, null, 2)}\n`, {
|
|
12
|
+
encoding: "utf8",
|
|
13
|
+
flag: "wx",
|
|
14
|
+
mode: options.fileMode ?? 0o600
|
|
15
|
+
});
|
|
16
|
+
renameSync(tempPath, path);
|
|
17
|
+
}
|
|
18
|
+
catch (error) {
|
|
19
|
+
rmSync(tempPath, {
|
|
20
|
+
force: true
|
|
21
|
+
});
|
|
22
|
+
throw error;
|
|
23
|
+
}
|
|
24
|
+
}
|
package/dist/util/tty-prompt.js
CHANGED
|
@@ -1,5 +1,16 @@
|
|
|
1
1
|
import { closeSync, openSync, readSync } from "node:fs";
|
|
2
2
|
export function promptYesNo(question, defaultYes, out = process.stderr) {
|
|
3
|
+
const answer = promptLine(`${question} ${defaultYes ? "[Y/n]" : "[y/N]"} `, out);
|
|
4
|
+
if (answer === null) {
|
|
5
|
+
return null;
|
|
6
|
+
}
|
|
7
|
+
const normalized = answer.trim().toLowerCase();
|
|
8
|
+
if (normalized === "") {
|
|
9
|
+
return defaultYes;
|
|
10
|
+
}
|
|
11
|
+
return normalized === "y" || normalized === "yes";
|
|
12
|
+
}
|
|
13
|
+
export function promptLine(question, out = process.stderr) {
|
|
3
14
|
let tty;
|
|
4
15
|
try {
|
|
5
16
|
tty = openSync("/dev/tty", "rs");
|
|
@@ -8,7 +19,7 @@ export function promptYesNo(question, defaultYes, out = process.stderr) {
|
|
|
8
19
|
return null;
|
|
9
20
|
}
|
|
10
21
|
try {
|
|
11
|
-
out.write(
|
|
22
|
+
out.write(question);
|
|
12
23
|
const byte = Buffer.alloc(1);
|
|
13
24
|
let answer = "";
|
|
14
25
|
for (;;) {
|
|
@@ -31,11 +42,7 @@ export function promptYesNo(question, defaultYes, out = process.stderr) {
|
|
|
31
42
|
}
|
|
32
43
|
answer += char;
|
|
33
44
|
}
|
|
34
|
-
|
|
35
|
-
if (normalized === "") {
|
|
36
|
-
return defaultYes;
|
|
37
|
-
}
|
|
38
|
-
return normalized === "y" || normalized === "yes";
|
|
45
|
+
return answer;
|
|
39
46
|
}
|
|
40
47
|
finally {
|
|
41
48
|
closeSync(tty);
|
|
@@ -2,6 +2,7 @@ import { existsSync, writeFileSync } from "node:fs";
|
|
|
2
2
|
import { resolve } from "node:path";
|
|
3
3
|
import { analyzePackages, AnalyzeError } from "../api/analyze.js";
|
|
4
4
|
import { createTheme } from "../presentation/theme.js";
|
|
5
|
+
import { provenanceLabel, provenanceDowngradeLine } from "../presentation/provenance.js";
|
|
5
6
|
import { resolvePresentation } from "../presentation/mode.js";
|
|
6
7
|
import { isRemotePackageSpec, isSupportedLockfilePath } from "./preflight.js";
|
|
7
8
|
import { authStatus } from "../auth/store.js";
|
|
@@ -62,6 +63,16 @@ function renderResult(spec, version, result, theme, verbose) {
|
|
|
62
63
|
const action = result.action ?? "pass";
|
|
63
64
|
const badge = theme.badge(action);
|
|
64
65
|
const lines = [`${badge} ${result.name}@${version} (${spec.ecosystem}) ${theme.paint("muted", `score ${result.score}`)}`];
|
|
66
|
+
if (result.provenance) {
|
|
67
|
+
const label = verbose && result.provenance.predicateType
|
|
68
|
+
? `provenance ${provenanceLabel(result.provenance)} · ${result.provenance.predicateType}`
|
|
69
|
+
: `provenance ${provenanceLabel(result.provenance)}`;
|
|
70
|
+
lines.push(` ${theme.paint("muted", label)}`);
|
|
71
|
+
const downgrade = provenanceDowngradeLine(version, result.provenance);
|
|
72
|
+
if (downgrade) {
|
|
73
|
+
lines.push(` ${theme.paint("warn", `⚠ ${downgrade}`)}`);
|
|
74
|
+
}
|
|
75
|
+
}
|
|
65
76
|
const reasons = verbose ? result.reasons : result.reasons.slice(0, 6);
|
|
66
77
|
for (const reason of reasons) {
|
|
67
78
|
const glyph = action === "block" ? theme.paint("block", "✘") : action === "warn" ? theme.paint("warn", "⚠") : theme.paint("muted", "·");
|
|
@@ -157,6 +168,7 @@ export async function runPackageCheck(target, io = {}, options = {}) {
|
|
|
157
168
|
score: result.score,
|
|
158
169
|
reasons: result.reasons,
|
|
159
170
|
findings: result.findings,
|
|
171
|
+
...(result.provenance ? { provenance: result.provenance } : {}),
|
|
160
172
|
...(result.recommendation ? { recommendation: result.recommendation } : {})
|
|
161
173
|
}, null, 2)}\n`
|
|
162
174
|
: renderResult(parsed, version, result, theme, options.verbose ?? false);
|
package/package.json
CHANGED
|
@@ -1,7 +1,15 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@westbayberry/dg",
|
|
3
|
-
"version": "2.0
|
|
3
|
+
"version": "2.1.0",
|
|
4
4
|
"description": "Dependency Guardian supply-chain firewall CLI",
|
|
5
|
+
"repository": {
|
|
6
|
+
"type": "git",
|
|
7
|
+
"url": "git+https://github.com/WestBayBerry/DG_CLI.git"
|
|
8
|
+
},
|
|
9
|
+
"bugs": {
|
|
10
|
+
"url": "https://github.com/WestBayBerry/DG_CLI/issues"
|
|
11
|
+
},
|
|
12
|
+
"homepage": "https://github.com/WestBayBerry/DG_CLI#readme",
|
|
5
13
|
"type": "module",
|
|
6
14
|
"bin": {
|
|
7
15
|
"dg": "./dist/bin/dg.js"
|