chainlesschain 0.66.0 → 0.81.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.
@@ -16,6 +16,20 @@ import {
16
16
  updatePolicy,
17
17
  deletePolicy,
18
18
  listDLPPolicies,
19
+ DLP_ACTION,
20
+ DLP_CHANNEL,
21
+ DLP_SEVERITY,
22
+ DLP_DEFAULT_MAX_CONTENT_SIZE,
23
+ createPolicyV2,
24
+ getPolicyV2,
25
+ listActivePoliciesForChannel,
26
+ scanContentV2,
27
+ listIncidentsV2,
28
+ getIncidentV2,
29
+ listBuiltinPolicyTemplates,
30
+ installBuiltinPolicies,
31
+ getDLPStatsV2,
32
+ getHighestUnresolvedSeverity,
19
33
  } from "../lib/dlp-engine.js";
20
34
 
21
35
  export function registerDlpCommand(program) {
@@ -269,6 +283,333 @@ export function registerDlpCommand(program) {
269
283
  deletePolicy(db, policyId);
270
284
  logger.success(`Policy ${chalk.cyan(policyId.slice(0, 8))} deleted`);
271
285
 
286
+ await shutdown();
287
+ } catch (err) {
288
+ logger.error(`Failed: ${err.message}`);
289
+ process.exit(1);
290
+ }
291
+ });
292
+
293
+ // ═══════════════════════════════════════════════════════════════
294
+ // V2 Canonical Subcommands (Phase 50)
295
+ // ═══════════════════════════════════════════════════════════════
296
+
297
+ dlp
298
+ .command("actions")
299
+ .description("List DLP actions (V2)")
300
+ .action(() => {
301
+ console.log(JSON.stringify(Object.values(DLP_ACTION), null, 2));
302
+ });
303
+
304
+ dlp
305
+ .command("channels")
306
+ .description("List DLP channels (V2)")
307
+ .action(() => {
308
+ console.log(JSON.stringify(Object.values(DLP_CHANNEL), null, 2));
309
+ });
310
+
311
+ dlp
312
+ .command("severities")
313
+ .description("List DLP severity levels (V2)")
314
+ .action(() => {
315
+ console.log(JSON.stringify(Object.values(DLP_SEVERITY), null, 2));
316
+ });
317
+
318
+ dlp
319
+ .command("default-max-size")
320
+ .description("Show default DLP scan size limit in bytes (V2)")
321
+ .action(() => {
322
+ console.log(JSON.stringify({ bytes: DLP_DEFAULT_MAX_CONTENT_SIZE }));
323
+ });
324
+
325
+ // policy-v2 create — channel-aware + description
326
+ policy
327
+ .command("create-v2 <name>")
328
+ .description("Create a V2 policy with channel filter + description")
329
+ .option("-d, --description <text>", "Description", "")
330
+ .option(
331
+ "-c, --channels <list>",
332
+ "Comma-separated channels (empty = all)",
333
+ "",
334
+ )
335
+ .option("-p, --patterns <list>", "Comma-separated regex patterns", "")
336
+ .option("-k, --keywords <list>", "Comma-separated keywords", "")
337
+ .option("-a, --action <action>", "Action", "alert")
338
+ .option("-s, --severity <level>", "Severity", "medium")
339
+ .action(async (name, options) => {
340
+ try {
341
+ const ctx = await bootstrap({ verbose: program.opts().verbose });
342
+ if (!ctx.db) {
343
+ logger.error("Database not available");
344
+ process.exit(1);
345
+ }
346
+ const db = ctx.db.getDatabase();
347
+ ensureDLPTables(db);
348
+
349
+ const result = createPolicyV2(db, {
350
+ name,
351
+ description: options.description,
352
+ channels: options.channels
353
+ ? options.channels
354
+ .split(",")
355
+ .map((s) => s.trim())
356
+ .filter(Boolean)
357
+ : [],
358
+ patterns: options.patterns
359
+ ? options.patterns
360
+ .split(",")
361
+ .map((s) => s.trim())
362
+ .filter(Boolean)
363
+ : [],
364
+ keywords: options.keywords
365
+ ? options.keywords
366
+ .split(",")
367
+ .map((s) => s.trim())
368
+ .filter(Boolean)
369
+ : [],
370
+ action: options.action,
371
+ severity: options.severity,
372
+ });
373
+ console.log(JSON.stringify(result, null, 2));
374
+
375
+ await shutdown();
376
+ } catch (err) {
377
+ logger.error(`Failed: ${err.message}`);
378
+ process.exit(1);
379
+ }
380
+ });
381
+
382
+ policy
383
+ .command("show-v2 <policy-id>")
384
+ .description("Show V2 policy with description + channels")
385
+ .action(async (policyId) => {
386
+ try {
387
+ const ctx = await bootstrap({ verbose: program.opts().verbose });
388
+ if (!ctx.db) {
389
+ logger.error("Database not available");
390
+ process.exit(1);
391
+ }
392
+ const db = ctx.db.getDatabase();
393
+ ensureDLPTables(db);
394
+
395
+ console.log(JSON.stringify(getPolicyV2(policyId), null, 2));
396
+
397
+ await shutdown();
398
+ } catch (err) {
399
+ logger.error(`Failed: ${err.message}`);
400
+ process.exit(1);
401
+ }
402
+ });
403
+
404
+ policy
405
+ .command("active-for <channel>")
406
+ .description("List active V2 policies applicable to a channel")
407
+ .action(async (channel) => {
408
+ try {
409
+ const ctx = await bootstrap({ verbose: program.opts().verbose });
410
+ if (!ctx.db) {
411
+ logger.error("Database not available");
412
+ process.exit(1);
413
+ }
414
+ const db = ctx.db.getDatabase();
415
+ ensureDLPTables(db);
416
+
417
+ console.log(
418
+ JSON.stringify(listActivePoliciesForChannel(channel), null, 2),
419
+ );
420
+
421
+ await shutdown();
422
+ } catch (err) {
423
+ logger.error(`Failed: ${err.message}`);
424
+ process.exit(1);
425
+ }
426
+ });
427
+
428
+ // Built-in templates
429
+ const templates = dlp
430
+ .command("templates")
431
+ .description("Built-in policy templates");
432
+
433
+ templates
434
+ .command("list")
435
+ .description("List built-in policy templates")
436
+ .action(() => {
437
+ console.log(JSON.stringify(listBuiltinPolicyTemplates(), null, 2));
438
+ });
439
+
440
+ templates
441
+ .command("install [names...]")
442
+ .description("Install built-in policy templates (all if no names given)")
443
+ .action(async (names) => {
444
+ try {
445
+ const ctx = await bootstrap({ verbose: program.opts().verbose });
446
+ if (!ctx.db) {
447
+ logger.error("Database not available");
448
+ process.exit(1);
449
+ }
450
+ const db = ctx.db.getDatabase();
451
+ ensureDLPTables(db);
452
+
453
+ const installed = installBuiltinPolicies(
454
+ db,
455
+ names && names.length > 0 ? names : undefined,
456
+ );
457
+ console.log(JSON.stringify(installed, null, 2));
458
+
459
+ await shutdown();
460
+ } catch (err) {
461
+ logger.error(`Failed: ${err.message}`);
462
+ process.exit(1);
463
+ }
464
+ });
465
+
466
+ // scan-v2 — channel filter + size gate + metadata
467
+ dlp
468
+ .command("scan-v2 <content>")
469
+ .description("Scan content with V2 channel filter + metadata")
470
+ .option("-c, --channel <channel>", "Channel")
471
+ .option("-u, --user <id>", "User ID")
472
+ .option("-m, --metadata <json>", "Metadata JSON", "{}")
473
+ .option("--max-size <bytes>", "Override max content size")
474
+ .action(async (content, options) => {
475
+ try {
476
+ const ctx = await bootstrap({ verbose: program.opts().verbose });
477
+ if (!ctx.db) {
478
+ logger.error("Database not available");
479
+ process.exit(1);
480
+ }
481
+ const db = ctx.db.getDatabase();
482
+ ensureDLPTables(db);
483
+
484
+ let metadata = {};
485
+ try {
486
+ metadata = JSON.parse(options.metadata);
487
+ } catch (_err) {
488
+ logger.error("Invalid --metadata JSON");
489
+ process.exit(1);
490
+ }
491
+ const result = scanContentV2(db, {
492
+ content,
493
+ channel: options.channel,
494
+ userId: options.user,
495
+ metadata,
496
+ maxContentSize: options.maxSize ? Number(options.maxSize) : undefined,
497
+ });
498
+ console.log(JSON.stringify(result, null, 2));
499
+
500
+ await shutdown();
501
+ } catch (err) {
502
+ logger.error(`Failed: ${err.message}`);
503
+ process.exit(1);
504
+ }
505
+ });
506
+
507
+ // incidents-v2 — rich filter
508
+ dlp
509
+ .command("incidents-v2")
510
+ .description(
511
+ "List V2 incidents (channel/severity/resolved/user/policy/date)",
512
+ )
513
+ .option("-c, --channel <channel>", "Filter by channel")
514
+ .option("-s, --severity <level>", "Filter by severity")
515
+ .option("-r, --resolved <bool>", "Filter by resolved (true/false)")
516
+ .option("-u, --user <id>", "Filter by userId")
517
+ .option("-p, --policy <id>", "Filter by policyId")
518
+ .option("--from <iso>", "fromDate ISO string")
519
+ .option("--to <iso>", "toDate ISO string")
520
+ .option("-l, --limit <n>", "Limit", "50")
521
+ .action(async (options) => {
522
+ try {
523
+ const ctx = await bootstrap({ verbose: program.opts().verbose });
524
+ if (!ctx.db) {
525
+ logger.error("Database not available");
526
+ process.exit(1);
527
+ }
528
+ const db = ctx.db.getDatabase();
529
+ ensureDLPTables(db);
530
+
531
+ const filter = {};
532
+ if (options.channel) filter.channel = options.channel;
533
+ if (options.severity) filter.severity = options.severity;
534
+ if (options.resolved === "true") filter.resolved = true;
535
+ else if (options.resolved === "false") filter.resolved = false;
536
+ if (options.user) filter.userId = options.user;
537
+ if (options.policy) filter.policyId = options.policy;
538
+ if (options.from) filter.fromDate = options.from;
539
+ if (options.to) filter.toDate = options.to;
540
+ filter.limit = Number(options.limit);
541
+ console.log(JSON.stringify(listIncidentsV2(filter), null, 2));
542
+
543
+ await shutdown();
544
+ } catch (err) {
545
+ logger.error(`Failed: ${err.message}`);
546
+ process.exit(1);
547
+ }
548
+ });
549
+
550
+ dlp
551
+ .command("incident-v2 <incident-id>")
552
+ .description("Show V2 incident with metadata")
553
+ .action(async (incidentId) => {
554
+ try {
555
+ const ctx = await bootstrap({ verbose: program.opts().verbose });
556
+ if (!ctx.db) {
557
+ logger.error("Database not available");
558
+ process.exit(1);
559
+ }
560
+ const db = ctx.db.getDatabase();
561
+ ensureDLPTables(db);
562
+
563
+ console.log(JSON.stringify(getIncidentV2(incidentId), null, 2));
564
+
565
+ await shutdown();
566
+ } catch (err) {
567
+ logger.error(`Failed: ${err.message}`);
568
+ process.exit(1);
569
+ }
570
+ });
571
+
572
+ dlp
573
+ .command("stats-v2")
574
+ .description(
575
+ "Show V2 DLP stats (byAction/bySeverity/byChannel/topPolicies)",
576
+ )
577
+ .action(async () => {
578
+ try {
579
+ const ctx = await bootstrap({ verbose: program.opts().verbose });
580
+ if (!ctx.db) {
581
+ logger.error("Database not available");
582
+ process.exit(1);
583
+ }
584
+ const db = ctx.db.getDatabase();
585
+ ensureDLPTables(db);
586
+
587
+ console.log(JSON.stringify(getDLPStatsV2(), null, 2));
588
+
589
+ await shutdown();
590
+ } catch (err) {
591
+ logger.error(`Failed: ${err.message}`);
592
+ process.exit(1);
593
+ }
594
+ });
595
+
596
+ dlp
597
+ .command("highest-severity")
598
+ .description("Show highest unresolved incident severity (V2)")
599
+ .action(async () => {
600
+ try {
601
+ const ctx = await bootstrap({ verbose: program.opts().verbose });
602
+ if (!ctx.db) {
603
+ logger.error("Database not available");
604
+ process.exit(1);
605
+ }
606
+ const db = ctx.db.getDatabase();
607
+ ensureDLPTables(db);
608
+
609
+ console.log(
610
+ JSON.stringify({ highestSeverity: getHighestUnresolvedSeverity() }),
611
+ );
612
+
272
613
  await shutdown();
273
614
  } catch (err) {
274
615
  logger.error(`Failed: ${err.message}`);