@slicedearth/whoisleuth-cli 2.1.0 → 2.3.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.
Files changed (77) hide show
  1. package/README.md +2 -1
  2. package/cli/arguments.mjs +389 -1745
  3. package/cli/artifact-validation/case-response.mjs +55 -30
  4. package/cli/artifact-validation/signed-review.mjs +30 -12
  5. package/cli/bulk-checkpoint.mjs +34 -3
  6. package/cli/case-pack.mjs +39 -92
  7. package/cli/command-argument-grammar.mjs +211 -0
  8. package/cli/command-reference.mjs +200 -213
  9. package/cli/completion.mjs +5 -1
  10. package/cli/discovery-observation-snapshot.mjs +16 -3
  11. package/cli/discovery-scan-command-runner.mjs +19 -0
  12. package/cli/discovery-scan.mjs +6 -0
  13. package/cli/domain-control-observations.mjs +1 -1
  14. package/cli/inline-command-families.mjs +1 -0
  15. package/cli/input.mjs +116 -0
  16. package/cli/interchange-report.mjs +14 -0
  17. package/cli/lookalike-calibration.mjs +7 -2
  18. package/cli/lookup-brief.mjs +4 -2
  19. package/cli/lookup-browser-view.mjs +1 -1
  20. package/cli/mail-header-review.mjs +467 -0
  21. package/cli/review-command-runner.mjs +25 -0
  22. package/cli/risk-calibration.mjs +19 -17
  23. package/cli/runner.mjs +7 -1
  24. package/docs/cli-reference.md +8 -1
  25. package/docs/cli.md +24 -1
  26. package/lib/bounded-json.mjs +9 -6
  27. package/lib/dns-convergence-review.mjs +1 -0
  28. package/lib/dns-intelligence.mjs +37 -3
  29. package/lib/dnssec-chain-validation.mjs +6 -0
  30. package/lib/domain-change-packet.mjs +42 -14
  31. package/lib/domain-control-flight-recorder.mjs +4 -1
  32. package/lib/domain-control-manifest.mjs +16 -16
  33. package/lib/domain-posture-parsers.mjs +114 -3
  34. package/lib/domain-posture.mjs +34 -3
  35. package/lib/evidence-export.mjs +63 -13
  36. package/lib/evidence-report.mjs +2 -2
  37. package/lib/html-signals.mjs +39 -33
  38. package/lib/local-geoip-evidence.mjs +19 -3
  39. package/lib/lookup-response.mjs +5 -4
  40. package/lib/lookup-source-progress.mjs +27 -4
  41. package/lib/nameserver-preflight-review.mjs +1 -0
  42. package/lib/rdap-capabilities.mjs +1 -1
  43. package/lib/registry-insights.mjs +35 -22
  44. package/lib/risk-calibration-summary.mjs +1 -3
  45. package/lib/security-txt.mjs +33 -1
  46. package/lib/static-html-analysis.mjs +69 -13
  47. package/lib/website-technology.mjs +79 -11
  48. package/lib/zone-intent-review.mjs +16 -6
  49. package/package.json +1 -1
  50. package/packages/cases/case-migration-model.mjs +71 -22
  51. package/packages/cases/case-record-contracts.mjs +5 -20
  52. package/packages/cases/case-record-core.mjs +10 -21
  53. package/packages/cases/case-record-decisions.mjs +72 -0
  54. package/packages/cases/case-record-model.mjs +1 -0
  55. package/packages/cases/case-record-operations.mjs +240 -3
  56. package/packages/cases/case-record-projection.mjs +218 -0
  57. package/packages/cases/case-report.mjs +11 -6
  58. package/packages/cases/case-response-model.mjs +43 -6
  59. package/packages/cases/case-response-packet.mjs +78 -27
  60. package/packages/cases/case-storage-model.mjs +6 -2
  61. package/packages/comparison/comparison-ledger-bulk.mjs +9 -10
  62. package/packages/comparison/page-similarity.mjs +3 -1
  63. package/packages/contracts/case-portability.mjs +183 -53
  64. package/packages/contracts/cli-command-semantics.mjs +93 -0
  65. package/packages/contracts/risk-calibration.mjs +15 -10
  66. package/packages/contracts/workspace-portability.mjs +44 -7
  67. package/packages/evidence/domain-control-runtime.mjs +46 -6
  68. package/packages/interchange/external-findings-import.mjs +115 -46
  69. package/packages/investigation/lookup-asset-graph.mjs +35 -27
  70. package/packages/workspace/brand-profile-model.mjs +118 -2
  71. package/packages/workspace/bulk-review-model.mjs +13 -7
  72. package/packages/workspace/bulk-session-model.mjs +5 -4
  73. package/packages/workspace/detection-rule-model.mjs +70 -22
  74. package/packages/workspace/relationship-observation-model.mjs +4 -1
  75. package/packages/workspace/website-snapshot-model.mjs +106 -8
  76. package/packages/workspace/workspace-archive.mjs +2 -0
  77. package/cli/evidence-command-arguments.mjs +0 -156
@@ -296,14 +296,89 @@ export function mergeWebsiteSnapshots(localRaw, incomingRaw) {
296
296
  const snapshots = normalizeWebsiteSnapshotStore([...byId.values()]).snapshots;
297
297
  return { snapshots, added, updated, skipped: Math.max(0, incoming.length - added - updated) };
298
298
  }
299
- function compareMap(field, before, after) {
299
+ const MAX_RECONCILED_WEBSITE_SOURCES = 16;
300
+ const UNAVAILABLE_WEBSITE_SOURCE_STATES = new Set([
301
+ 'blocked', 'error', 'not_found', 'rate_limited', 'unavailable', 'unsupported',
302
+ ]);
303
+ export function canonicalWebsiteSourceState(value) {
304
+ return value.trim().toLowerCase().replace(/[\s-]+/gu, '_');
305
+ }
306
+ function websiteSourceStateSeverity(value) {
307
+ if (UNAVAILABLE_WEBSITE_SOURCE_STATES.has(value))
308
+ return 2;
309
+ return ['complete', 'success'].includes(value) ? 0 : 1;
310
+ }
311
+ export function reconciledWebsiteSources(snapshot) {
312
+ const selected = new Map();
313
+ for (const sourceEntry of snapshot.sources.slice(0, MAX_RECONCILED_WEBSITE_SOURCES)) {
314
+ const sourceId = sourceEntry.source.trim().toLowerCase();
315
+ if (!sourceId)
316
+ continue;
317
+ const state = canonicalWebsiteSourceState(sourceEntry.state);
318
+ const severity = websiteSourceStateSeverity(state);
319
+ const current = selected.get(sourceId);
320
+ if (!current || severity > current.severity || (severity === current.severity && state.localeCompare(current.state) < 0)) {
321
+ selected.set(sourceId, { state, severity });
322
+ }
323
+ }
324
+ return [...selected.entries()]
325
+ .sort(([left], [right]) => left.localeCompare(right))
326
+ .map(([sourceName, value]) => ({ source: sourceName, state: value.state }));
327
+ }
328
+ export function websiteWithReconciledSources(snapshot) {
329
+ return { ...snapshot, sources: reconciledWebsiteSources(snapshot) };
330
+ }
331
+ export function reconciledWebsiteSourceState(snapshot, sourceId) {
332
+ const canonicalId = sourceId.trim().toLowerCase();
333
+ return reconciledWebsiteSources(snapshot)
334
+ .find((sourceEntry) => sourceEntry.source === canonicalId)?.state ?? null;
335
+ }
336
+ function completeWebsiteSourceState(value) {
337
+ const state = canonicalWebsiteSourceState(value);
338
+ return state === 'complete' || state === 'success';
339
+ }
340
+ export function websiteSnapshotFieldSource(field) {
341
+ if (field.startsWith('certificate.'))
342
+ return 'tls';
343
+ if (field.startsWith('source.'))
344
+ return field.slice('source.'.length);
345
+ if (field.startsWith('dependency.')) {
346
+ const recordType = field.slice('dependency.'.length).split(':')[0];
347
+ return recordType === 'HTTP' ? 'http' : 'dns';
348
+ }
349
+ if (['technology', 'posture', 'identity'].some((family) => field.startsWith(family)))
350
+ return 'http';
351
+ return '';
352
+ }
353
+ export function websiteSnapshotFieldComplete(snapshot, field) {
354
+ const sourceName = websiteSnapshotFieldSource(field);
355
+ const retainedSource = sourceName ? reconciledWebsiteSourceState(snapshot, sourceName) : null;
356
+ const retainedSourceComplete = !retainedSource || completeWebsiteSourceState(retainedSource);
357
+ if (field.startsWith('certificate.')) {
358
+ return Boolean(snapshot.certificate?.complete && !snapshot.certificate.truncated && retainedSourceComplete);
359
+ }
360
+ return snapshot.complete && !snapshot.truncated && retainedSourceComplete;
361
+ }
362
+ export function websiteSnapshotComparisonEvidenceComplete(snapshot) {
363
+ return websiteSnapshotFieldComplete(snapshot, 'technology')
364
+ && websiteSnapshotFieldComplete(snapshot, 'posture')
365
+ && websiteSnapshotFieldComplete(snapshot, 'identity')
366
+ && websiteSnapshotFieldComplete(snapshot, 'dependency.CNAME')
367
+ && (!snapshot.certificate || websiteSnapshotFieldComplete(snapshot, 'certificate.observation'));
368
+ }
369
+ function compareMap(field, before, after, options = {}) {
300
370
  const changes = [];
301
371
  for (const key of [...new Set([...before.keys(), ...after.keys()])].sort()) {
302
372
  const left = before.get(key) ?? null;
303
373
  const right = after.get(key) ?? null;
304
374
  if (left === right)
305
375
  continue;
306
- changes.push({ field: `${field}.${key}`, state: left === null ? 'added' : right === null ? 'removed' : 'changed', before: left, after: right });
376
+ const state = left === null
377
+ ? options.beforeComplete?.(key) === false ? 'incomparable' : 'added'
378
+ : right === null
379
+ ? options.afterComplete?.(key) === false ? 'incomparable' : 'removed'
380
+ : 'changed';
381
+ changes.push({ field: `${field}.${key}`, state, before: left, after: right });
307
382
  }
308
383
  return changes;
309
384
  }
@@ -313,6 +388,7 @@ export function compareWebsiteSnapshots(beforeRaw, afterRaw) {
313
388
  if (!before || !after || before.domain !== after.domain) {
314
389
  return {
315
390
  compatible: false,
391
+ complete: false,
316
392
  changes: [{ field: 'snapshot', state: 'incomparable', before: before?.domain ?? null, after: after?.domain ?? null }],
317
393
  dependencyTransitions: [],
318
394
  };
@@ -328,20 +404,29 @@ export function compareWebsiteSnapshots(beforeRaw, afterRaw) {
328
404
  ? 'legacy_unknown'
329
405
  : postureVersionBefore === postureVersionAfter ? 'comparable' : 'detector_changed';
330
406
  const changes = [
331
- ...(technologyComparability === 'comparable' ? compareMap('technology', new Map(before.technologies.map((item) => [item.id, `${item.name}|${item.category}|${item.confidence}|${item.roles.join(',')}`])), new Map(after.technologies.map((item) => [item.id, `${item.name}|${item.category}|${item.confidence}|${item.roles.join(',')}`]))) : [{
407
+ ...(technologyComparability === 'comparable' ? compareMap('technology', new Map(before.technologies.map((item) => [item.id, `${item.name}|${item.category}|${item.confidence}|${item.roles.join(',')}`])), new Map(after.technologies.map((item) => [item.id, `${item.name}|${item.category}|${item.confidence}|${item.roles.join(',')}`])), {
408
+ beforeComplete: () => websiteSnapshotFieldComplete(before, 'technology'),
409
+ afterComplete: () => websiteSnapshotFieldComplete(after, 'technology'),
410
+ }) : [{
332
411
  field: 'technology.profileVersion',
333
412
  state: technologyComparability === 'detector_changed' ? 'changed' : 'incomparable',
334
413
  before: technologyVersionBefore === null ? null : String(technologyVersionBefore),
335
414
  after: technologyVersionAfter === null ? null : String(technologyVersionAfter),
336
415
  }]),
337
- ...(postureComparability === 'comparable' ? compareMap('posture', new Map(before.posture.map((item) => [item.id, item.state])), new Map(after.posture.map((item) => [item.id, item.state]))) : [{
416
+ ...(postureComparability === 'comparable' ? compareMap('posture', new Map(before.posture.map((item) => [item.id, item.state])), new Map(after.posture.map((item) => [item.id, item.state])), {
417
+ beforeComplete: () => websiteSnapshotFieldComplete(before, 'posture'),
418
+ afterComplete: () => websiteSnapshotFieldComplete(after, 'posture'),
419
+ }) : [{
338
420
  field: 'posture.profileVersion',
339
421
  state: postureComparability === 'detector_changed' ? 'changed' : 'incomparable',
340
422
  before: postureVersionBefore === null ? null : String(postureVersionBefore),
341
423
  after: postureVersionAfter === null ? null : String(postureVersionAfter),
342
424
  }]),
343
425
  ...compareMap('source', new Map(before.sources.map((item) => [item.source, item.state])), new Map(after.sources.map((item) => [item.source, item.state]))),
344
- ...compareMap('dependency', new Map(before.dependencies.map((item) => [`${item.recordType}:${item.target}`, `${item.state}|${item.qualification}`])), new Map(after.dependencies.map((item) => [`${item.recordType}:${item.target}`, `${item.state}|${item.qualification}`]))),
426
+ ...compareMap('dependency', new Map(before.dependencies.map((item) => [`${item.recordType}:${item.target}`, `${item.state}|${item.qualification}`])), new Map(after.dependencies.map((item) => [`${item.recordType}:${item.target}`, `${item.state}|${item.qualification}`])), {
427
+ beforeComplete: (key) => websiteSnapshotFieldComplete(before, `dependency.${key}`),
428
+ afterComplete: (key) => websiteSnapshotFieldComplete(after, `dependency.${key}`),
429
+ }),
345
430
  ];
346
431
  const dependencyTransitions = [];
347
432
  const beforeDependencies = new Map(before.dependencies.map((item) => [`${item.recordType}:${item.target}`, item]));
@@ -365,10 +450,10 @@ export function compareWebsiteSnapshots(beforeRaw, afterRaw) {
365
450
  detail: 'An earlier active dependency is unresolved in the later retained observation. Resolver or collection failure remains possible.',
366
451
  });
367
452
  }
368
- else if (!left && right) {
453
+ else if (!left && right && websiteSnapshotFieldComplete(before, `dependency.${key}`)) {
369
454
  dependencyTransitions.push({ target: right.target, recordType: right.recordType, state: 'added', detail: 'This dependency first appears in the later retained snapshot.' });
370
455
  }
371
- else if (left && !right && after.complete) {
456
+ else if (left && !right && websiteSnapshotFieldComplete(after, `dependency.${key}`)) {
372
457
  dependencyTransitions.push({ target: left.target, recordType: left.recordType, state: 'removed', detail: 'This dependency is not represented in the later complete snapshot. It may have been intentionally removed or replaced.' });
373
458
  }
374
459
  if (dependencyTransitions.length >= 20)
@@ -381,7 +466,16 @@ export function compareWebsiteSnapshots(beforeRaw, afterRaw) {
381
466
  continue;
382
467
  changes.push({ field: `identity.${key}`, state: left === null || right === null ? 'unavailable' : 'changed', before: left, after: right });
383
468
  }
384
- changes.push(...compareMap('identityValues.resourceHosts', new Map(before.identityValues.resourceHosts.map((item) => [item, item])), new Map(after.identityValues.resourceHosts.map((item) => [item, item]))), ...compareMap('identityValues.trackingIdentifiers', new Map(before.identityValues.trackingIdentifiers.map((item) => [`${item.type}:${item.value}`, item.value])), new Map(after.identityValues.trackingIdentifiers.map((item) => [`${item.type}:${item.value}`, item.value]))), ...compareMap('identityValues.formActionOrigins', new Map(before.identityValues.formActionOrigins.map((item) => [item, item])), new Map(after.identityValues.formActionOrigins.map((item) => [item, item]))));
469
+ changes.push(...compareMap('identityValues.resourceHosts', new Map(before.identityValues.resourceHosts.map((item) => [item, item])), new Map(after.identityValues.resourceHosts.map((item) => [item, item])), {
470
+ beforeComplete: () => websiteSnapshotFieldComplete(before, 'identityValues.resourceHosts'),
471
+ afterComplete: () => websiteSnapshotFieldComplete(after, 'identityValues.resourceHosts'),
472
+ }), ...compareMap('identityValues.trackingIdentifiers', new Map(before.identityValues.trackingIdentifiers.map((item) => [`${item.type}:${item.value}`, item.value])), new Map(after.identityValues.trackingIdentifiers.map((item) => [`${item.type}:${item.value}`, item.value])), {
473
+ beforeComplete: () => websiteSnapshotFieldComplete(before, 'identityValues.trackingIdentifiers'),
474
+ afterComplete: () => websiteSnapshotFieldComplete(after, 'identityValues.trackingIdentifiers'),
475
+ }), ...compareMap('identityValues.formActionOrigins', new Map(before.identityValues.formActionOrigins.map((item) => [item, item])), new Map(after.identityValues.formActionOrigins.map((item) => [item, item])), {
476
+ beforeComplete: () => websiteSnapshotFieldComplete(before, 'identityValues.formActionOrigins'),
477
+ afterComplete: () => websiteSnapshotFieldComplete(after, 'identityValues.formActionOrigins'),
478
+ }));
385
479
  if (before.certificate || after.certificate) {
386
480
  if (!before.certificate || !after.certificate) {
387
481
  changes.push({
@@ -425,6 +519,10 @@ export function compareWebsiteSnapshots(beforeRaw, afterRaw) {
425
519
  }
426
520
  return {
427
521
  compatible: true,
522
+ complete: websiteSnapshotComparisonEvidenceComplete(before)
523
+ && websiteSnapshotComparisonEvidenceComplete(after)
524
+ && technologyComparability === 'comparable'
525
+ && postureComparability === 'comparable',
428
526
  profileComparability: {
429
527
  technology: technologyComparability,
430
528
  securityPosture: postureComparability,
@@ -528,6 +528,7 @@ export async function previewWorkspaceArchive(raw, localInput, options = {}) {
528
528
  skipped: result.skipped ?? 0,
529
529
  pruned: result.pruned ?? 0,
530
530
  brandProfileReferencesOmitted: result.brandProfileReferencesOmitted ?? 0,
531
+ authoredHistoryOmitted: result.authoredHistoryOmitted ?? 0,
531
532
  selected,
532
533
  normalizedSettings: result.settings || null,
533
534
  });
@@ -567,6 +568,7 @@ export async function previewWorkspaceArchive(raw, localInput, options = {}) {
567
568
  skipped: dependentSection.recordCount,
568
569
  pruned: 0,
569
570
  brandProfileReferencesOmitted: 0,
571
+ authoredHistoryOmitted: 0,
570
572
  selected: false,
571
573
  normalizedSettings: dependentId === 'settings' ? null : dependentSection.normalizedSettings ?? null,
572
574
  };
@@ -1,156 +0,0 @@
1
- import { CliUsageError } from "./errors.mjs";
2
- export function parseInspectArchiveArguments(argv) {
3
- let source = null;
4
- let passphraseSource = null;
5
- let search = null;
6
- let reveal = false;
7
- let requireMatch = false;
8
- let expectedContentDigest = null;
9
- let output = 'terminal';
10
- let quiet = false;
11
- let color = true;
12
- for (let index = 0; index < argv.length; index++) {
13
- const argument = argv[index];
14
- if (argument === undefined)
15
- break;
16
- if (argument === '--json') {
17
- if (output !== 'terminal')
18
- throw new CliUsageError('--json may be supplied only once.');
19
- output = 'json';
20
- }
21
- else if (argument === '--passphrase-file') {
22
- if (passphraseSource !== null)
23
- throw new CliUsageError('--passphrase-file may be supplied only once.');
24
- const value = argv[++index];
25
- if (!value || value.startsWith('-'))
26
- throw new CliUsageError('--passphrase-file requires one bounded UTF-8 file.');
27
- passphraseSource = value;
28
- }
29
- else if (argument === '--search') {
30
- if (search !== null)
31
- throw new CliUsageError('--search may be supplied only once.');
32
- const value = argv[++index];
33
- if (!value || value.startsWith('-'))
34
- throw new CliUsageError('--search requires one exact bounded value.');
35
- search = value;
36
- }
37
- else if (argument === '--reveal') {
38
- if (reveal)
39
- throw new CliUsageError('--reveal may be supplied only once.');
40
- reveal = true;
41
- }
42
- else if (argument === '--require-match') {
43
- if (requireMatch)
44
- throw new CliUsageError('--require-match may be supplied only once.');
45
- requireMatch = true;
46
- }
47
- else if (argument === '--expect-content-digest') {
48
- if (expectedContentDigest !== null)
49
- throw new CliUsageError('--expect-content-digest may be supplied only once.');
50
- const value = argv[++index];
51
- if (!value || !/^sha256:[a-f0-9]{64}$/u.test(value)) {
52
- throw new CliUsageError('--expect-content-digest requires sha256 followed by 64 lowercase hexadecimal characters.');
53
- }
54
- expectedContentDigest = value;
55
- }
56
- else if (argument === '--quiet')
57
- quiet = true;
58
- else if (argument === '--no-color')
59
- color = false;
60
- else if (argument.startsWith('-'))
61
- throw new CliUsageError(`Unknown option "${argument}".`);
62
- else if (source === null)
63
- source = argument;
64
- else
65
- throw new CliUsageError('inspect-archive accepts one optional JSON file. Otherwise pipe one archive on stdin.');
66
- }
67
- if (reveal && search === null)
68
- throw new CliUsageError('--reveal requires --search.');
69
- if (requireMatch && search === null)
70
- throw new CliUsageError('--require-match requires --search.');
71
- if (quiet && output !== 'terminal')
72
- throw new CliUsageError('--quiet cannot be combined with machine-readable output.');
73
- return {
74
- action: 'inspect-archive',
75
- source,
76
- passphraseSource,
77
- search,
78
- reveal,
79
- requireMatch,
80
- expectedContentDigest,
81
- output,
82
- quiet,
83
- color,
84
- };
85
- }
86
- export function parseSignArtifactArguments(argv) {
87
- let source = null;
88
- let privateKeySource = null;
89
- for (let index = 0; index < argv.length; index++) {
90
- const argument = argv[index];
91
- if (argument === undefined)
92
- break;
93
- if (argument === '--private-key-file') {
94
- if (privateKeySource !== null)
95
- throw new CliUsageError('--private-key-file may be supplied only once.');
96
- const value = argv[++index];
97
- if (!value || value.startsWith('-'))
98
- throw new CliUsageError('--private-key-file requires one bounded PEM file.');
99
- privateKeySource = value;
100
- }
101
- else if (argument.startsWith('-'))
102
- throw new CliUsageError(`Unknown option "${argument}".`);
103
- else if (source === null)
104
- source = argument;
105
- else
106
- throw new CliUsageError('sign-artifact accepts one optional JSON file. Otherwise pipe one artefact on stdin.');
107
- }
108
- if (!privateKeySource)
109
- throw new CliUsageError('sign-artifact requires --private-key-file.');
110
- return { action: 'sign-artifact', source, privateKeySource };
111
- }
112
- export function parseVerifySignatureArguments(argv) {
113
- let source = null;
114
- let publicKeySource = null;
115
- let output = 'terminal';
116
- let quiet = false;
117
- let color = true;
118
- for (let index = 0; index < argv.length; index++) {
119
- const argument = argv[index];
120
- if (argument === undefined)
121
- break;
122
- if (argument === '--json') {
123
- if (output !== 'terminal')
124
- throw new CliUsageError('--json may be supplied only once.');
125
- output = 'json';
126
- }
127
- else if (argument === '--public-key-file') {
128
- if (publicKeySource !== null)
129
- throw new CliUsageError('--public-key-file may be supplied only once.');
130
- const value = argv[++index];
131
- if (!value || value.startsWith('-'))
132
- throw new CliUsageError('--public-key-file requires one bounded PEM file.');
133
- publicKeySource = value;
134
- }
135
- else if (argument === '--quiet')
136
- quiet = true;
137
- else if (argument === '--no-color')
138
- color = false;
139
- else if (argument.startsWith('-'))
140
- throw new CliUsageError(`Unknown option "${argument}".`);
141
- else if (source === null)
142
- source = argument;
143
- else
144
- throw new CliUsageError('verify-signature accepts one optional JSON file. Otherwise pipe one package on stdin.');
145
- }
146
- if (quiet && output !== 'terminal')
147
- throw new CliUsageError('--quiet cannot be combined with machine-readable output.');
148
- return {
149
- action: 'verify-signature',
150
- source,
151
- publicKeySource,
152
- output,
153
- quiet,
154
- color,
155
- };
156
- }