fullcourtdefense-cli 1.7.2 → 1.7.3
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/discover.js +31 -6
- package/dist/commands/hook.js +1 -1
- package/dist/commands/installCursorHook.js +1 -1
- package/dist/commands/mcpGateway.js +1 -1
- package/dist/config.js +6 -6
- package/dist/index.js +14 -10
- package/dist/version.json +1 -1
- package/package.json +1 -1
|
@@ -64,6 +64,30 @@ function parseSurfaces(args) {
|
|
|
64
64
|
}
|
|
65
65
|
const SECRET_VALUE = /sk-[A-Za-z0-9_-]{12,}|ghp_[A-Za-z0-9_]{20,}|xox[baprs]-[A-Za-z0-9-]{20,}|AKIA[0-9A-Z]{16}|AIza[0-9A-Za-z_-]{20,}/;
|
|
66
66
|
const SECRET_KEY = /key|token|secret|password|credential|api[_-]?key/i;
|
|
67
|
+
const SECRET_ARG_NAME = /^(?:--?(?:shield-key|api-key|token|mcp-token|mcp-api-key|key|secret|password|credential)|(?:.*(?:KEY|TOKEN|SECRET|PASSWORD|CREDENTIAL).*))$/i;
|
|
68
|
+
function redactCommandArgs(args) {
|
|
69
|
+
const out = [];
|
|
70
|
+
let redactNext = false;
|
|
71
|
+
for (const arg of args || []) {
|
|
72
|
+
if (redactNext) {
|
|
73
|
+
out.push('[redacted]');
|
|
74
|
+
redactNext = false;
|
|
75
|
+
continue;
|
|
76
|
+
}
|
|
77
|
+
const eq = arg.indexOf('=');
|
|
78
|
+
if (eq > 0 && SECRET_ARG_NAME.test(arg.slice(0, eq))) {
|
|
79
|
+
out.push(`${arg.slice(0, eq + 1)}[redacted]`);
|
|
80
|
+
continue;
|
|
81
|
+
}
|
|
82
|
+
if (SECRET_ARG_NAME.test(arg)) {
|
|
83
|
+
out.push(arg);
|
|
84
|
+
redactNext = true;
|
|
85
|
+
continue;
|
|
86
|
+
}
|
|
87
|
+
out.push(arg.replace(SECRET_VALUE, '[redacted]'));
|
|
88
|
+
}
|
|
89
|
+
return out;
|
|
90
|
+
}
|
|
67
91
|
function machineFingerprint() {
|
|
68
92
|
const seed = [os.hostname(), os.userInfo().username, os.platform(), os.arch()].join('|');
|
|
69
93
|
return crypto.createHash('sha256').update(seed).digest('hex').slice(0, 16);
|
|
@@ -505,10 +529,11 @@ async function upload(servers, host, clientCoverage, apiUrl, auth, connectorName
|
|
|
505
529
|
tools: s.tools,
|
|
506
530
|
})),
|
|
507
531
|
};
|
|
508
|
-
// Prefer the
|
|
509
|
-
// `
|
|
532
|
+
// Prefer the enrolled Shield-key endpoint when available so the zero-paste
|
|
533
|
+
// `login --token` flow works even if an older org API key remains in config.
|
|
534
|
+
// Passing --api-key is the explicit opt-in to the org API key path.
|
|
510
535
|
const base = apiUrl.replace(/\/$/, '');
|
|
511
|
-
const useShieldKey = !auth.
|
|
536
|
+
const useShieldKey = !auth.preferApiKey && !!(auth.shieldId && auth.shieldKey);
|
|
512
537
|
const url = useShieldKey ? `${base}/api/cli/discovery` : `${base}/api/cicd/discovery/mcp`;
|
|
513
538
|
const headers = { 'Content-Type': 'application/json' };
|
|
514
539
|
if (useShieldKey) {
|
|
@@ -668,11 +693,11 @@ async function discoverCommand(args, config) {
|
|
|
668
693
|
const hasShieldCreds = !!(creds.shieldId && creds.shieldKey);
|
|
669
694
|
if (!creds.apiKey && !hasShieldCreds) {
|
|
670
695
|
if (!silent) {
|
|
671
|
-
console.error(`\n${COLOR.red}--upload needs credentials.${COLOR.reset} Run \`fullcourtdefense login --token <fleet-token>\`
|
|
696
|
+
console.error(`\n${COLOR.red}--upload needs credentials.${COLOR.reset} Run \`fullcourtdefense login --token <fleet-enrollment-token>\` first.`);
|
|
672
697
|
}
|
|
673
698
|
process.exit(1);
|
|
674
699
|
}
|
|
675
|
-
await upload(found, host, clientCoverage, creds.apiUrl, { apiKey: creds.apiKey, shieldId: creds.shieldId, shieldKey: creds.shieldKey }, args.connectorName, uploadExtras);
|
|
700
|
+
await upload(found, host, clientCoverage, creds.apiUrl, { apiKey: creds.apiKey, shieldId: creds.shieldId, shieldKey: creds.shieldKey, preferApiKey: !!args.apiKey }, args.connectorName, uploadExtras);
|
|
676
701
|
}
|
|
677
702
|
if (silent) {
|
|
678
703
|
if (args.upload === 'true') {
|
|
@@ -714,7 +739,7 @@ async function discoverCommand(args, config) {
|
|
|
714
739
|
found.sort((a, b) => order[a.riskLevel] - order[b.riskLevel]);
|
|
715
740
|
for (const s of found) {
|
|
716
741
|
const col = riskColor(s.riskLevel);
|
|
717
|
-
const target = s.command ? `${s.command} ${(s.args
|
|
742
|
+
const target = s.command ? `${s.command} ${redactCommandArgs(s.args).join(' ')}`.trim() : (s.url || 'unknown');
|
|
718
743
|
console.log(` ${col}[${s.riskLevel.toUpperCase()}]${COLOR.reset} ${s.serverName} — ${target} (${proxyStatusLabel(s.proxyStatus)})`);
|
|
719
744
|
}
|
|
720
745
|
console.log('');
|
package/dist/commands/hook.js
CHANGED
|
@@ -409,7 +409,7 @@ async function hookCommand(args, config) {
|
|
|
409
409
|
// No Shield configured → allow (fail-open by design: a misconfigured machine
|
|
410
410
|
// must not brick the developer's IDE).
|
|
411
411
|
if (!shieldId) {
|
|
412
|
-
respond(false, undefined, 'FullCourtDefense hook installed but no Shield ID configured (run `fullcourtdefense
|
|
412
|
+
respond(false, undefined, 'FullCourtDefense hook installed but no Shield ID configured (run `fullcourtdefense login --token <fleet-enrollment-token>`).');
|
|
413
413
|
}
|
|
414
414
|
// --- Action Policy enforcement for actions (shell / mcp / file / read) ---
|
|
415
415
|
if (event === 'shell' || event === 'mcp' || event === 'file' || event === 'read') {
|
|
@@ -168,7 +168,7 @@ async function installCursorHookCommand(args, config) {
|
|
|
168
168
|
console.log(`${COLOR.gray}Creds:${COLOR.reset} ${credFile}`);
|
|
169
169
|
if (!creds.shieldId) {
|
|
170
170
|
console.log('');
|
|
171
|
-
console.log(`${COLOR.yellow}No Shield ID set.${COLOR.reset} Run ${COLOR.bold}fullcourtdefense
|
|
171
|
+
console.log(`${COLOR.yellow}No Shield ID set.${COLOR.reset} Run ${COLOR.bold}fullcourtdefense login --token <fleet-enrollment-token>${COLOR.reset} first, then re-run install.`);
|
|
172
172
|
}
|
|
173
173
|
console.log('');
|
|
174
174
|
(0, restartNotice_1.printRestartNotice)('Cursor');
|
|
@@ -172,7 +172,7 @@ function resolveGatewayConfig(args, config, defaults = {}) {
|
|
|
172
172
|
apiUrl: args.apiUrl,
|
|
173
173
|
});
|
|
174
174
|
if (!creds.shieldId) {
|
|
175
|
-
throw new Error('Missing Shield ID. Run `fullcourtdefense
|
|
175
|
+
throw new Error('Missing Shield ID. Run `fullcourtdefense login --token <fleet-enrollment-token>` once (saves per-machine Shield credentials to ~/.fullcourtdefense.yml).');
|
|
176
176
|
}
|
|
177
177
|
const agentClient = normalizeAgentClient(args.agentClient || process.env.FCD_AGENT_CLIENT || detectRuntimeAgentClient(), defaults.agentClient || 'cursor');
|
|
178
178
|
const developerName = detectedDeveloperName(args);
|
package/dist/config.js
CHANGED
|
@@ -177,11 +177,11 @@ function loadConfig(configPath) {
|
|
|
177
177
|
}
|
|
178
178
|
}
|
|
179
179
|
}
|
|
180
|
-
// Plain CLI usage is intentionally global: `fullcourtdefense
|
|
181
|
-
// ~/.fullcourtdefense.yml, then
|
|
182
|
-
// Shield from any repo. Project
|
|
183
|
-
//
|
|
184
|
-
// user's
|
|
180
|
+
// Plain CLI usage is intentionally global: `fullcourtdefense login --token ...`
|
|
181
|
+
// writes per-machine Shield credentials to ~/.fullcourtdefense.yml, then
|
|
182
|
+
// `install-all` should use that same enrolled Shield from any repo. Project
|
|
183
|
+
// config is still supported for scan defaults and explicit `--config`
|
|
184
|
+
// workflows, but it must not silently override the user's machine enrollment.
|
|
185
185
|
return configPath
|
|
186
186
|
? mergeConfig(homeConfig, projectConfig)
|
|
187
187
|
: mergeConfig(projectConfig, homeConfig);
|
|
@@ -237,7 +237,7 @@ function requireCliSetup(config, overrides = {}, options = {}) {
|
|
|
237
237
|
if (requireOrganizationId && !creds.organizationId)
|
|
238
238
|
missing.push('organizationId');
|
|
239
239
|
if (missing.length > 0) {
|
|
240
|
-
throw new Error(`Missing ${missing.join(', ')}. Run \`fullcourtdefense
|
|
240
|
+
throw new Error(`Missing ${missing.join(', ')}. Run \`fullcourtdefense login --token <fleet-enrollment-token>\` once — it saves per-machine Shield credentials to ~/.fullcourtdefense.yml so you never pass them again.`);
|
|
241
241
|
}
|
|
242
242
|
return creds;
|
|
243
243
|
}
|
package/dist/index.js
CHANGED
|
@@ -112,9 +112,11 @@ function printHelp() {
|
|
|
112
112
|
\x1b[1mCommands:\x1b[0m
|
|
113
113
|
help Show this onboarding guide and command reference.
|
|
114
114
|
doctor First step. Checks outbound HTTPS access to FullCourtDefense.
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
115
|
+
login Enrolls this machine with a fleet enrollment token and saves
|
|
116
|
+
per-machine Shield credentials to ~/.fullcourtdefense.yml.
|
|
117
|
+
configure Legacy/manual setup: saves org API key, Organization ID,
|
|
118
|
+
Shield ID, and Shield key to ~/.fullcourtdefense.yml.
|
|
119
|
+
setup Same as configure — manual fallback when not using fleet login.
|
|
118
120
|
install-all One-click: protect existing MCP servers in every AI client and
|
|
119
121
|
install Cursor hooks for built-in Cursor actions. No folder path
|
|
120
122
|
or --mcp-command needed. Use --hooks false to skip Cursor hooks.
|
|
@@ -167,21 +169,23 @@ function printHelp() {
|
|
|
167
169
|
init Creates a starter .fullcourtdefense.yml config file.
|
|
168
170
|
|
|
169
171
|
\x1b[1mNew User Process:\x1b[0m
|
|
170
|
-
1. In the web app,
|
|
171
|
-
|
|
172
|
+
1. In the web app, an org admin creates a fleet enrollment token:
|
|
173
|
+
AI Fleet → Settings → Fleet enrollment token.
|
|
172
174
|
|
|
173
175
|
2. Check outbound connectivity from the customer machine:
|
|
174
176
|
fullcourtdefense doctor
|
|
175
177
|
This confirms the machine can reach https://api.fullcourtdefense.ai over HTTPS.
|
|
176
178
|
|
|
177
|
-
3.
|
|
178
|
-
fullcourtdefense
|
|
179
|
-
|
|
179
|
+
3. Enroll this developer machine (one time):
|
|
180
|
+
fullcourtdefense login --token <fleet-enrollment-token>
|
|
181
|
+
Or set FCD_ENROLL_TOKEN and run: fullcourtdefense login
|
|
182
|
+
Saves per-machine Shield credentials to ~/.fullcourtdefense.yml —
|
|
183
|
+
install, discover --upload, scan, gateway, and hooks use it automatically.
|
|
180
184
|
|
|
181
185
|
4. One-click protect every AI client on this machine with Action Policies:
|
|
182
186
|
fullcourtdefense install-all
|
|
183
187
|
Verify installs:
|
|
184
|
-
fullcourtdefense
|
|
188
|
+
fullcourtdefense protect-all --dry-run true
|
|
185
189
|
Or install one client: fullcourtdefense install-cursor-mcp-gateway ...
|
|
186
190
|
|
|
187
191
|
5. Run a guided local scan:
|
|
@@ -298,7 +302,7 @@ function printHelp() {
|
|
|
298
302
|
$ fullcourtdefense install-claude-code-mcp-gateway --scope project --mcp-command npm --mcp-args "run mcp"
|
|
299
303
|
$ fullcourtdefense install-claude-desktop-mcp-gateway --mcp-command npm --mcp-args "run mcp"
|
|
300
304
|
$ fullcourtdefense mcp-gateway --mcp-command npm --mcp-args "run mcp"
|
|
301
|
-
$ fullcourtdefense
|
|
305
|
+
$ fullcourtdefense login --token <fleet-enrollment-token>
|
|
302
306
|
$ fullcourtdefense install-all
|
|
303
307
|
$ fullcourtdefense install-all --auto-protect true --upload
|
|
304
308
|
$ fullcourtdefense install-mcp-gateway --clients cursor,codex --mcp-command npm --mcp-args "run mcp"
|
package/dist/version.json
CHANGED