zeuslock-dlp-cli 0.2.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/README.md +570 -0
- package/bin/zeuslock.js +9 -0
- package/package.json +33 -0
- package/src/cli.js +78 -0
- package/src/commands/agents.js +211 -0
- package/src/commands/anonymize.js +83 -0
- package/src/commands/auth.js +337 -0
- package/src/commands/deploy.js +515 -0
- package/src/commands/extensions.js +73 -0
- package/src/commands/hook.js +221 -0
- package/src/commands/incidents.js +436 -0
- package/src/commands/keys.js +211 -0
- package/src/commands/mcp.js +322 -0
- package/src/commands/rules.js +432 -0
- package/src/commands/scan.js +178 -0
- package/src/commands/shadow-ai.js +255 -0
- package/src/commands/siem.js +241 -0
- package/src/commands/status.js +170 -0
- package/src/commands/tokens.js +293 -0
- package/src/commands/users.js +255 -0
- package/src/commands/whoami.js +43 -0
- package/src/lib/api-client.js +308 -0
- package/src/lib/api-key-store.js +84 -0
- package/src/lib/auth-store.js +123 -0
- package/src/lib/cli-token.js +22 -0
- package/src/lib/command-token.js +15 -0
- package/src/lib/config.js +27 -0
- package/src/lib/dlp-scan.js +146 -0
- package/src/lib/package-info.js +11 -0
- package/src/lib/prompt.js +55 -0
- package/src/lib/siem-cursors.js +64 -0
- package/src/lib/table.js +30 -0
- package/src/lib/time.js +33 -0
- package/src/lib/version.js +24 -0
|
@@ -0,0 +1,337 @@
|
|
|
1
|
+
import { Command } from "commander";
|
|
2
|
+
|
|
3
|
+
import { getCliTokenIdentity, login, requestApiKeyJson, resolveApiUrlForRequest, verifyMfa } from "../lib/api-client.js";
|
|
4
|
+
import {
|
|
5
|
+
createApiKeyRecord,
|
|
6
|
+
deleteApiKeyStore,
|
|
7
|
+
maskApiKey,
|
|
8
|
+
readApiKeyStore,
|
|
9
|
+
writeApiKeyStore
|
|
10
|
+
} from "../lib/api-key-store.js";
|
|
11
|
+
import { createAuthRecord, createCliTokenAuthRecord, deleteAuthStore, readAuthStore, writeAuthStore } from "../lib/auth-store.js";
|
|
12
|
+
import { assertCliAccessToken } from "../lib/cli-token.js";
|
|
13
|
+
import { getCommandToken } from "../lib/command-token.js";
|
|
14
|
+
import { DEFAULT_API_URL, normalizeApiUrl } from "../lib/config.js";
|
|
15
|
+
import { promptPassword, promptText } from "../lib/prompt.js";
|
|
16
|
+
import { createWhoamiCommand } from "./whoami.js";
|
|
17
|
+
|
|
18
|
+
export function createAuthCommand({
|
|
19
|
+
stdin = process.stdin,
|
|
20
|
+
stdout = process.stdout,
|
|
21
|
+
stderr = process.stderr,
|
|
22
|
+
env = process.env
|
|
23
|
+
} = {}) {
|
|
24
|
+
const command = new Command("auth")
|
|
25
|
+
.description("Authenticate with ZeusLock");
|
|
26
|
+
|
|
27
|
+
command.addCommand(createLoginCommand({ stdin, stdout, env }));
|
|
28
|
+
command.addCommand(createLogoutCommand({ stdout, env }));
|
|
29
|
+
command.addCommand(createApiKeyCommand({ stdin, stdout, env }));
|
|
30
|
+
command.addCommand(createWhoamiCommand({ stdout, stderr, env }));
|
|
31
|
+
|
|
32
|
+
return command;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
function createApiKeyCommand({
|
|
36
|
+
stdin = process.stdin,
|
|
37
|
+
stdout = process.stdout,
|
|
38
|
+
env = process.env
|
|
39
|
+
} = {}) {
|
|
40
|
+
const command = new Command("api-key")
|
|
41
|
+
.description("Configure the organization API key used by scan, anonymize, and hooks");
|
|
42
|
+
|
|
43
|
+
command.addCommand(createApiKeySetCommand({ stdin, stdout, env }));
|
|
44
|
+
command.addCommand(createApiKeyClearCommand({ stdout, env }));
|
|
45
|
+
command.addCommand(createApiKeyStatusCommand({ stdout, env }));
|
|
46
|
+
|
|
47
|
+
return command;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
function createApiKeySetCommand({ stdin, stdout, env }) {
|
|
51
|
+
return new Command("set")
|
|
52
|
+
.description("Validate and save an organization API key locally")
|
|
53
|
+
.argument("[api_key]", "organization API key from the ZeusLock dashboard")
|
|
54
|
+
.option("--api-url <url>", "ZeusLock API base URL")
|
|
55
|
+
.option("--json", "print machine-readable output")
|
|
56
|
+
.action(async (apiKeyArg, options) => {
|
|
57
|
+
const apiKey = await resolveApiKeyValue(apiKeyArg, { stdin, stdout, env });
|
|
58
|
+
const apiUrl = normalizeApiUrl(options.apiUrl || env.ZEUSLOCK_API_URL) || await resolveApiUrlForRequest(env);
|
|
59
|
+
const verification = await requestApiKeyJson("/api/rules/sync", {
|
|
60
|
+
apiUrl,
|
|
61
|
+
apiKey,
|
|
62
|
+
env
|
|
63
|
+
});
|
|
64
|
+
const record = await writeApiKeyStore(createApiKeyRecord({
|
|
65
|
+
apiUrl,
|
|
66
|
+
apiKey,
|
|
67
|
+
verified: {
|
|
68
|
+
version: verification?.version ?? null,
|
|
69
|
+
license_status: verification?.license_status || null,
|
|
70
|
+
subscription_status: verification?.subscription_status || null,
|
|
71
|
+
policy_id: verification?.policy_id || null,
|
|
72
|
+
policy_version: verification?.policy_version || null,
|
|
73
|
+
saved_from: "rules_sync"
|
|
74
|
+
}
|
|
75
|
+
}), env);
|
|
76
|
+
const output = {
|
|
77
|
+
configured: true,
|
|
78
|
+
apiUrl,
|
|
79
|
+
apiKeyMasked: record.apiKeyMasked,
|
|
80
|
+
rulesVersion: verification?.version ?? null,
|
|
81
|
+
licenseStatus: verification?.license_status || null,
|
|
82
|
+
subscriptionStatus: verification?.subscription_status || null
|
|
83
|
+
};
|
|
84
|
+
|
|
85
|
+
if (options.json) {
|
|
86
|
+
stdout.write(`${JSON.stringify(output)}\n`);
|
|
87
|
+
return;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
stdout.write(`Saved organization API key ${output.apiKeyMasked}\n`);
|
|
91
|
+
stdout.write(`API URL: ${apiUrl}\n`);
|
|
92
|
+
if (output.rulesVersion !== null) {
|
|
93
|
+
stdout.write(`Rules version: ${output.rulesVersion}\n`);
|
|
94
|
+
}
|
|
95
|
+
});
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
function createApiKeyClearCommand({ stdout, env }) {
|
|
99
|
+
return new Command("clear")
|
|
100
|
+
.description("Remove the locally saved organization API key")
|
|
101
|
+
.option("--json", "print machine-readable output")
|
|
102
|
+
.action(async (options) => {
|
|
103
|
+
await deleteApiKeyStore(env);
|
|
104
|
+
if (options.json) {
|
|
105
|
+
stdout.write(`${JSON.stringify({ configured: false, cleared: true })}\n`);
|
|
106
|
+
return;
|
|
107
|
+
}
|
|
108
|
+
stdout.write("Removed saved organization API key\n");
|
|
109
|
+
});
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
function createApiKeyStatusCommand({ stdout, env }) {
|
|
113
|
+
return new Command("status")
|
|
114
|
+
.description("Show whether an organization API key is configured")
|
|
115
|
+
.option("--json", "print machine-readable output")
|
|
116
|
+
.action(async (options) => {
|
|
117
|
+
const stored = await readApiKeyStore(env);
|
|
118
|
+
const envKey = String(env.ZEUSLOCK_API_KEY || "").trim();
|
|
119
|
+
const output = {
|
|
120
|
+
configured: Boolean(envKey || stored?.apiKey),
|
|
121
|
+
source: envKey ? "env" : stored?.apiKey ? "store" : null,
|
|
122
|
+
apiUrl: normalizeApiUrl(env.ZEUSLOCK_API_URL) || stored?.apiUrl || null,
|
|
123
|
+
apiKeyMasked: envKey ? maskApiKey(envKey) : stored?.apiKeyMasked || null,
|
|
124
|
+
savedAt: stored?.savedAt || null
|
|
125
|
+
};
|
|
126
|
+
|
|
127
|
+
if (options.json) {
|
|
128
|
+
stdout.write(`${JSON.stringify(output)}\n`);
|
|
129
|
+
return;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
stdout.write(`Org API key: ${output.configured ? "configured" : "not configured"}\n`);
|
|
133
|
+
if (output.apiUrl) {
|
|
134
|
+
stdout.write(`API URL: ${output.apiUrl}\n`);
|
|
135
|
+
}
|
|
136
|
+
if (output.apiKeyMasked) {
|
|
137
|
+
stdout.write(`Key: ${output.apiKeyMasked}\n`);
|
|
138
|
+
}
|
|
139
|
+
});
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
function createLoginCommand({
|
|
143
|
+
stdin = process.stdin,
|
|
144
|
+
stdout = process.stdout,
|
|
145
|
+
env = process.env
|
|
146
|
+
} = {}) {
|
|
147
|
+
return new Command("login")
|
|
148
|
+
.description("Authenticate with the ZeusLock backend and save user auth")
|
|
149
|
+
.option("--email <email>", "ZeusLock account email")
|
|
150
|
+
.option("-u, --username <email>", "ZeusLock account email")
|
|
151
|
+
.option("--password <password>", "ZeusLock account password")
|
|
152
|
+
.option("--mfa-code <code>", "MFA verification code, when required")
|
|
153
|
+
.option("--recaptcha-token <token>", "reCAPTCHA token, when the backend requires it")
|
|
154
|
+
.option("--token <zlu_token>", "dashboard-generated CLI access token")
|
|
155
|
+
.option("--api-url <url>", "ZeusLock API base URL")
|
|
156
|
+
.option("--json", "print machine-readable output")
|
|
157
|
+
.action(async (options) => {
|
|
158
|
+
const apiUrl = resolveLoginApiUrl(options, env);
|
|
159
|
+
const cliToken = options.token || getCommandToken(env);
|
|
160
|
+
if (cliToken) {
|
|
161
|
+
await loginWithCliAccessToken({ apiUrl, token: cliToken, options, stdout, env });
|
|
162
|
+
return;
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
const email = await resolveEmail(options, { stdin, stdout, env });
|
|
166
|
+
const password = await resolvePassword(options, { stdin, stdout, env });
|
|
167
|
+
|
|
168
|
+
let tokenResponse = await login({
|
|
169
|
+
apiUrl,
|
|
170
|
+
email,
|
|
171
|
+
password,
|
|
172
|
+
recaptchaToken: options.recaptchaToken || env.ZEUSLOCK_RECAPTCHA_TOKEN || null
|
|
173
|
+
});
|
|
174
|
+
|
|
175
|
+
if (tokenResponse.mfa_required) {
|
|
176
|
+
const code = await resolveMfaCode(options, { stdin, stdout, env });
|
|
177
|
+
tokenResponse = await verifyMfa({
|
|
178
|
+
apiUrl,
|
|
179
|
+
session: tokenResponse.session,
|
|
180
|
+
code
|
|
181
|
+
});
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
if (!tokenResponse.access_token) {
|
|
185
|
+
throw new Error(tokenResponse.error || "Login response did not include an access token");
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
const authRecord = createAuthRecord({
|
|
189
|
+
apiUrl,
|
|
190
|
+
email,
|
|
191
|
+
tokenResponse
|
|
192
|
+
});
|
|
193
|
+
await writeAuthStore(authRecord, env);
|
|
194
|
+
|
|
195
|
+
if (options.json) {
|
|
196
|
+
stdout.write(`${JSON.stringify({
|
|
197
|
+
authenticated: true,
|
|
198
|
+
apiUrl,
|
|
199
|
+
email,
|
|
200
|
+
expiresAt: authRecord.expiresAt
|
|
201
|
+
})}\n`);
|
|
202
|
+
return;
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
stdout.write(`Logged in as ${email}\n`);
|
|
206
|
+
});
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
function createLogoutCommand({ stdout, env }) {
|
|
210
|
+
return new Command("logout")
|
|
211
|
+
.description("Remove saved user authentication")
|
|
212
|
+
.option("--json", "print machine-readable output")
|
|
213
|
+
.action(async (options) => {
|
|
214
|
+
const hadAuth = Boolean(await readAuthStore(env));
|
|
215
|
+
await deleteAuthStore(env);
|
|
216
|
+
|
|
217
|
+
if (options.json) {
|
|
218
|
+
stdout.write(`${JSON.stringify({ authenticated: false, cleared: true, hadAuth })}\n`);
|
|
219
|
+
return;
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
stdout.write(hadAuth ? "Removed saved user authentication\n" : "No saved user authentication found\n");
|
|
223
|
+
});
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
async function loginWithCliAccessToken({ apiUrl, token, options, stdout, env }) {
|
|
227
|
+
if (options.email || options.username || options.password || options.mfaCode || options.recaptchaToken) {
|
|
228
|
+
throw new Error("--token cannot be combined with email/password, MFA, or reCAPTCHA options.");
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
const normalizedToken = String(token || "").trim();
|
|
232
|
+
assertCliAccessToken(normalizedToken, "--token");
|
|
233
|
+
const identity = await getCliTokenIdentity({ apiUrl, accessToken: normalizedToken });
|
|
234
|
+
const email = extractIdentityEmail(identity);
|
|
235
|
+
const authRecord = createCliTokenAuthRecord({
|
|
236
|
+
apiUrl,
|
|
237
|
+
accessToken: normalizedToken,
|
|
238
|
+
email
|
|
239
|
+
});
|
|
240
|
+
await writeAuthStore(authRecord, env);
|
|
241
|
+
|
|
242
|
+
if (options.json) {
|
|
243
|
+
stdout.write(`${JSON.stringify({
|
|
244
|
+
authenticated: true,
|
|
245
|
+
mode: "token",
|
|
246
|
+
apiUrl,
|
|
247
|
+
email
|
|
248
|
+
})}\n`);
|
|
249
|
+
return;
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
stdout.write(email ? `Logged in with CLI token as ${email}\n` : "Logged in with CLI token\n");
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
function extractIdentityEmail(identity) {
|
|
256
|
+
return identity?.email ||
|
|
257
|
+
identity?.user?.email ||
|
|
258
|
+
identity?.user_email ||
|
|
259
|
+
identity?.account?.email ||
|
|
260
|
+
null;
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
function resolveLoginApiUrl(options, env) {
|
|
264
|
+
return normalizeApiUrl(options.apiUrl || env.ZEUSLOCK_API_URL) || DEFAULT_API_URL;
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
async function resolveEmail(options, context) {
|
|
268
|
+
const email =
|
|
269
|
+
options.email ||
|
|
270
|
+
options.username ||
|
|
271
|
+
context.env.ZEUSLOCK_EMAIL ||
|
|
272
|
+
context.env.ZEUSLOCK_USERNAME ||
|
|
273
|
+
await promptText({
|
|
274
|
+
message: "Email: ",
|
|
275
|
+
stdin: context.stdin,
|
|
276
|
+
stdout: context.stdout,
|
|
277
|
+
optionName: "--email"
|
|
278
|
+
});
|
|
279
|
+
|
|
280
|
+
const normalized = String(email).trim().toLowerCase();
|
|
281
|
+
if (!normalized) {
|
|
282
|
+
throw new Error("Email is required");
|
|
283
|
+
}
|
|
284
|
+
return normalized;
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
async function resolvePassword(options, context) {
|
|
288
|
+
const password =
|
|
289
|
+
options.password ??
|
|
290
|
+
context.env.ZEUSLOCK_PASSWORD ??
|
|
291
|
+
await promptPassword({
|
|
292
|
+
stdin: context.stdin,
|
|
293
|
+
stdout: context.stdout,
|
|
294
|
+
optionName: "--password"
|
|
295
|
+
});
|
|
296
|
+
|
|
297
|
+
if (!String(password)) {
|
|
298
|
+
throw new Error("Password is required");
|
|
299
|
+
}
|
|
300
|
+
return String(password);
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
async function resolveApiKeyValue(apiKeyArg, context) {
|
|
304
|
+
const apiKey =
|
|
305
|
+
apiKeyArg ||
|
|
306
|
+
context.env.ZEUSLOCK_API_KEY ||
|
|
307
|
+
await promptPassword({
|
|
308
|
+
message: "API key: ",
|
|
309
|
+
stdin: context.stdin,
|
|
310
|
+
stdout: context.stdout,
|
|
311
|
+
optionName: "api_key"
|
|
312
|
+
});
|
|
313
|
+
|
|
314
|
+
const normalized = String(apiKey).trim();
|
|
315
|
+
if (!normalized) {
|
|
316
|
+
throw new Error("API key is required");
|
|
317
|
+
}
|
|
318
|
+
return normalized;
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
async function resolveMfaCode(options, context) {
|
|
322
|
+
const code =
|
|
323
|
+
options.mfaCode ||
|
|
324
|
+
context.env.ZEUSLOCK_MFA_CODE ||
|
|
325
|
+
await promptText({
|
|
326
|
+
message: "MFA code: ",
|
|
327
|
+
stdin: context.stdin,
|
|
328
|
+
stdout: context.stdout,
|
|
329
|
+
optionName: "--mfa-code"
|
|
330
|
+
});
|
|
331
|
+
|
|
332
|
+
const normalized = String(code).trim();
|
|
333
|
+
if (!normalized) {
|
|
334
|
+
throw new Error("MFA code is required");
|
|
335
|
+
}
|
|
336
|
+
return normalized;
|
|
337
|
+
}
|