@slicedearth/whoisleuth-cli 1.32.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 (189) hide show
  1. package/LICENSE +661 -0
  2. package/LICENSES/Retire.js-Apache-2.0.txt +199 -0
  3. package/NOTICE +76 -0
  4. package/README.md +84 -0
  5. package/TRADEMARKS.md +41 -0
  6. package/bin/whoisleuth.mjs +35 -0
  7. package/cli/archive-inspect.mjs +198 -0
  8. package/cli/arguments.mjs +1022 -0
  9. package/cli/artifact-verify.mjs +210 -0
  10. package/cli/bulk-checkpoint.mjs +218 -0
  11. package/cli/bulk-command-runner.mjs +109 -0
  12. package/cli/bulk-output.mjs +112 -0
  13. package/cli/bulk.mjs +133 -0
  14. package/cli/compare.mjs +238 -0
  15. package/cli/completion.mjs +175 -0
  16. package/cli/csv.mjs +1 -0
  17. package/cli/discover.mjs +50 -0
  18. package/cli/discovery-command-runner.mjs +36 -0
  19. package/cli/discovery-observation-snapshot.mjs +315 -0
  20. package/cli/discovery-scan-command-runner.mjs +141 -0
  21. package/cli/discovery-scan.mjs +325 -0
  22. package/cli/discovery-snapshot.mjs +133 -0
  23. package/cli/discovery-workflow.mjs +73 -0
  24. package/cli/doctor.mjs +137 -0
  25. package/cli/errors.mjs +21 -0
  26. package/cli/evidence-command-arguments.mjs +145 -0
  27. package/cli/evidence-command-runner.mjs +106 -0
  28. package/cli/evidence-signing.mjs +215 -0
  29. package/cli/exit-codes.mjs +9 -0
  30. package/cli/export-evidence.mjs +28 -0
  31. package/cli/formatters/evidence-report.mjs +3 -0
  32. package/cli/formatters/html.mjs +69 -0
  33. package/cli/formatters/json.mjs +135 -0
  34. package/cli/formatters/markdown.mjs +3 -0
  35. package/cli/formatters/terminal.mjs +658 -0
  36. package/cli/http.mjs +27 -0
  37. package/cli/lookup-command-runner.mjs +87 -0
  38. package/cli/lookup-diff.mjs +171 -0
  39. package/cli/mail-review.mjs +204 -0
  40. package/cli/manual.mjs +34 -0
  41. package/cli/network-command-runner.mjs +88 -0
  42. package/cli/output-file.mjs +97 -0
  43. package/cli/page-compare.mjs +123 -0
  44. package/cli/posture.mjs +20 -0
  45. package/cli/progress-events.mjs +53 -0
  46. package/cli/progress.mjs +56 -0
  47. package/cli/registry-access.mjs +3 -0
  48. package/cli/registry-support.mjs +120 -0
  49. package/cli/risk-calibration.mjs +294 -0
  50. package/cli/runner-types.mjs +1 -0
  51. package/cli/runner.mjs +726 -0
  52. package/cli/saved-lookup.mjs +87 -0
  53. package/cli/source-reliability.mjs +727 -0
  54. package/cli/strict-exit.mjs +55 -0
  55. package/cli/terminal-presentation.mjs +147 -0
  56. package/docs/cli.md +827 -0
  57. package/frontend/src/lib/analysis/acquisition-decision-packet.js +93 -0
  58. package/frontend/src/lib/analysis/acquisition-due-diligence.js +391 -0
  59. package/frontend/src/lib/analysis/artifact-integrity.js +21 -0
  60. package/frontend/src/lib/analysis/brand-profile-model.js +421 -0
  61. package/frontend/src/lib/analysis/bulk-domain-comparison.js +246 -0
  62. package/frontend/src/lib/analysis/bulk-mail-exposure.js +192 -0
  63. package/frontend/src/lib/analysis/bulk-retry-plan.js +86 -0
  64. package/frontend/src/lib/analysis/bulk-review-export.js +62 -0
  65. package/frontend/src/lib/analysis/bulk-review-model.js +266 -0
  66. package/frontend/src/lib/analysis/bulk-session-model.js +515 -0
  67. package/frontend/src/lib/analysis/bulk-sort.js +49 -0
  68. package/frontend/src/lib/analysis/campaign-model.js +265 -0
  69. package/frontend/src/lib/analysis/case-evidence-model.js +642 -0
  70. package/frontend/src/lib/analysis/case-migration-model.js +248 -0
  71. package/frontend/src/lib/analysis/case-model.js +5 -0
  72. package/frontend/src/lib/analysis/case-record-contracts.js +44 -0
  73. package/frontend/src/lib/analysis/case-record-core.js +232 -0
  74. package/frontend/src/lib/analysis/case-record-model.js +7 -0
  75. package/frontend/src/lib/analysis/case-record-operations.js +224 -0
  76. package/frontend/src/lib/analysis/case-response-model.js +640 -0
  77. package/frontend/src/lib/analysis/case-response-packet.js +573 -0
  78. package/frontend/src/lib/analysis/case-storage-model.js +93 -0
  79. package/frontend/src/lib/analysis/detection-rule-model.js +306 -0
  80. package/frontend/src/lib/analysis/dns-record-normalization.js +11 -0
  81. package/frontend/src/lib/analysis/http-summary.js +165 -0
  82. package/frontend/src/lib/analysis/investigation-guide.js +500 -0
  83. package/frontend/src/lib/analysis/investigation-template-model.js +144 -0
  84. package/frontend/src/lib/analysis/page-baseline.js +272 -0
  85. package/frontend/src/lib/analysis/page-similarity.js +179 -0
  86. package/frontend/src/lib/analysis/relationship-evidence.js +265 -0
  87. package/frontend/src/lib/analysis/relationship-observation-model.js +423 -0
  88. package/frontend/src/lib/analysis/scoring.js +135 -0
  89. package/frontend/src/lib/analysis/shortlist-model.js +209 -0
  90. package/frontend/src/lib/analysis/utils.js +265 -0
  91. package/frontend/src/lib/analysis/watchlist-history.js +643 -0
  92. package/frontend/src/lib/analysis/watchlist-store.js +127 -0
  93. package/frontend/src/lib/analysis/website-snapshot-model.js +302 -0
  94. package/frontend/src/lib/analysis/workspace-archive-crypto.js +282 -0
  95. package/frontend/src/lib/analysis/workspace-archive.js +472 -0
  96. package/lib/abort.mjs +26 -0
  97. package/lib/abusech-auth.mjs +19 -0
  98. package/lib/availability.mjs +716 -0
  99. package/lib/bounded-contract-normalizers.mjs +61 -0
  100. package/lib/browser-library-profile.mjs +310 -0
  101. package/lib/bulk-comparison-evidence.mjs +83 -0
  102. package/lib/classify.mjs +93 -0
  103. package/lib/client-behavior-profile.mjs +83 -0
  104. package/lib/credential-surface-profile.mjs +66 -0
  105. package/lib/csv.mjs +23 -0
  106. package/lib/ct-query.mjs +27 -0
  107. package/lib/ct-search.mjs +393 -0
  108. package/lib/distributed-operation-budget.mjs +461 -0
  109. package/lib/dns-delegation-health.mjs +344 -0
  110. package/lib/dns-intelligence.mjs +739 -0
  111. package/lib/dns-mx.mjs +24 -0
  112. package/lib/dns-resolver-selection.mjs +33 -0
  113. package/lib/domain-posture-analysis.mjs +254 -0
  114. package/lib/domain-posture-parsers.mjs +358 -0
  115. package/lib/domain-posture.mjs +745 -0
  116. package/lib/error-detail.mjs +10 -0
  117. package/lib/evidence-export.mjs +431 -0
  118. package/lib/evidence-report-markdown.mjs +71 -0
  119. package/lib/evidence-report.mjs +391 -0
  120. package/lib/external-intelligence-risk.mjs +108 -0
  121. package/lib/favicon.mjs +146 -0
  122. package/lib/feature-policy.mjs +70 -0
  123. package/lib/generated/retire-browser-catalog.mjs +8724 -0
  124. package/lib/generated/unicode-confusables-17.mjs +93 -0
  125. package/lib/hostname.mjs +38 -0
  126. package/lib/html-signals.mjs +680 -0
  127. package/lib/http-intelligence.mjs +232 -0
  128. package/lib/idn-confusable-policy.mjs +86 -0
  129. package/lib/idn-confusables.mjs +465 -0
  130. package/lib/lookup-cache.mjs +130 -0
  131. package/lib/lookup-progress.mjs +291 -0
  132. package/lib/lookup-source-progress.mjs +109 -0
  133. package/lib/lookup-threat-provider-inventory.mjs +13 -0
  134. package/lib/lookup.mjs +592 -0
  135. package/lib/observation.mjs +114 -0
  136. package/lib/observed-network-context.mjs +355 -0
  137. package/lib/operation-budget.mjs +489 -0
  138. package/lib/page-fingerprints.mjs +481 -0
  139. package/lib/page-role-profile.mjs +117 -0
  140. package/lib/perceptual-hash.mjs +451 -0
  141. package/lib/rdap-attempts.mjs +34 -0
  142. package/lib/rdap-bootstrap.mjs +173 -0
  143. package/lib/rdap-capabilities.mjs +292 -0
  144. package/lib/rdap-client.mjs +125 -0
  145. package/lib/rdap-normalization.mjs +691 -0
  146. package/lib/rdap-registrar.mjs +196 -0
  147. package/lib/rdap-transport.mjs +39 -0
  148. package/lib/rdap-types.mjs +1 -0
  149. package/lib/rdap-validation.mjs +94 -0
  150. package/lib/rdap.mjs +16 -0
  151. package/lib/registry-access.mjs +32 -0
  152. package/lib/registry-capabilities.mjs +117 -0
  153. package/lib/registry-capability-catalogue.mjs +1206 -0
  154. package/lib/registry-comparison.mjs +318 -0
  155. package/lib/registry-dates.mjs +190 -0
  156. package/lib/registry-insights.mjs +325 -0
  157. package/lib/response-policy.mjs +256 -0
  158. package/lib/risk-scoring.mjs +164 -0
  159. package/lib/safe-fetch.mjs +446 -0
  160. package/lib/security-txt.mjs +359 -0
  161. package/lib/service-binding-dns.mjs +608 -0
  162. package/lib/sslbl-certificates.generated.mjs +143 -0
  163. package/lib/sslbl-intelligence.mjs +241 -0
  164. package/lib/static-html-analysis.mjs +340 -0
  165. package/lib/structured-data-identity.mjs +323 -0
  166. package/lib/threat-intelligence-contract.mjs +5 -0
  167. package/lib/threat-intelligence-definition-registry.mjs +34 -0
  168. package/lib/threat-intelligence-fixtures.mjs +82 -0
  169. package/lib/threat-intelligence-manifests.mjs +512 -0
  170. package/lib/threat-intelligence-runtime.mjs +662 -0
  171. package/lib/threat-intelligence-targets.mjs +168 -0
  172. package/lib/threat-intelligence-types.mjs +3 -0
  173. package/lib/threatfox-intelligence.mjs +307 -0
  174. package/lib/tls-intelligence.mjs +814 -0
  175. package/lib/typosquat-generator.mjs +884 -0
  176. package/lib/urlhaus-intelligence.mjs +361 -0
  177. package/lib/urlscan-intelligence.mjs +349 -0
  178. package/lib/website-security-posture.mjs +325 -0
  179. package/lib/website-technology.mjs +437 -0
  180. package/lib/whois-authority.mjs +98 -0
  181. package/lib/whois-chain.mjs +261 -0
  182. package/lib/whois-contacts.mjs +163 -0
  183. package/lib/whois-contracts.mjs +3 -0
  184. package/lib/whois-normalization.mjs +27 -0
  185. package/lib/whois-transport.mjs +100 -0
  186. package/lib/whois-values.mjs +92 -0
  187. package/lib/whois.mjs +1187 -0
  188. package/package.json +55 -0
  189. package/third-party-notices.txt +5571 -0
@@ -0,0 +1,55 @@
1
+ const STRICT_EXIT_FAILURE_STATES = Object.freeze([
2
+ 'error',
3
+ 'failed',
4
+ 'partial',
5
+ 'rate_limited',
6
+ 'timeout',
7
+ 'unavailable',
8
+ ]);
9
+ const MAX_STRICT_EXIT_NODES = 2_000;
10
+ const FAILURE_STATES = new Set(STRICT_EXIT_FAILURE_STATES);
11
+ const STATUS_FIELDS = new Set(['state', 'status']);
12
+ function object(value) {
13
+ return value && typeof value === 'object' && !Array.isArray(value)
14
+ ? value
15
+ : null;
16
+ }
17
+ function lookupStrictExitFindings(document) {
18
+ const root = object(document);
19
+ const diagnostics = object(root?.diagnostics);
20
+ if (!diagnostics)
21
+ return [{ path: 'diagnostics', state: 'unavailable' }];
22
+ const findings = [];
23
+ const pending = [{ path: 'diagnostics', value: diagnostics }];
24
+ let visited = 0;
25
+ while (pending.length && visited < MAX_STRICT_EXIT_NODES) {
26
+ const current = pending.shift();
27
+ if (!current)
28
+ break;
29
+ visited += 1;
30
+ if (Array.isArray(current.value)) {
31
+ current.value.slice(0, 100).forEach((value, index) => {
32
+ pending.push({ path: `${current.path}[${index}]`, value });
33
+ });
34
+ continue;
35
+ }
36
+ const record = object(current.value);
37
+ if (!record)
38
+ continue;
39
+ for (const [key, value] of Object.entries(record).slice(0, 100)) {
40
+ const path = `${current.path}.${key}`;
41
+ if (STATUS_FIELDS.has(key) && typeof value === 'string' && FAILURE_STATES.has(value.toLowerCase())) {
42
+ findings.push({ path, state: value.toLowerCase() });
43
+ if (findings.length >= 100)
44
+ return findings;
45
+ }
46
+ else if (value && typeof value === 'object') {
47
+ pending.push({ path, value });
48
+ }
49
+ }
50
+ }
51
+ if (visited >= MAX_STRICT_EXIT_NODES)
52
+ findings.push({ path: 'diagnostics', state: 'partial' });
53
+ return findings;
54
+ }
55
+ export { lookupStrictExitFindings };
@@ -0,0 +1,147 @@
1
+ const MIN_TERMINAL_WIDTH = 40;
2
+ const MAX_TERMINAL_WIDTH = 160;
3
+ const DEFAULT_TERMINAL_WIDTH = 100;
4
+ const ANSI_RESET = '\u001b[0m';
5
+ const ANSI_BY_TONE = Object.freeze({
6
+ accent: '\u001b[1;36m',
7
+ danger: '\u001b[31m',
8
+ dim: '\u001b[2m',
9
+ info: '\u001b[36m',
10
+ success: '\u001b[32m',
11
+ warning: '\u001b[33m',
12
+ });
13
+ const SECTION_LABELS = new Set([
14
+ 'Collection',
15
+ 'Discover',
16
+ 'Integrity and calibration',
17
+ 'Investigate',
18
+ 'Network',
19
+ 'Quick start',
20
+ 'Registration',
21
+ 'Result',
22
+ 'Review saved evidence',
23
+ 'Runtime',
24
+ 'Source health',
25
+ 'Target',
26
+ 'Terminal',
27
+ 'Website and security',
28
+ ]);
29
+ function boundedTerminalWidth(value) {
30
+ const width = Number(value);
31
+ if (!Number.isSafeInteger(width))
32
+ return DEFAULT_TERMINAL_WIDTH;
33
+ return Math.max(MIN_TERMINAL_WIDTH, Math.min(MAX_TERMINAL_WIDTH, width));
34
+ }
35
+ function terminalPresentation(stream, requestedColor = true, environment = process.env) {
36
+ const interactive = stream?.isTTY === true
37
+ && environment.TERM !== 'dumb'
38
+ && !environment.CI;
39
+ const color = interactive
40
+ && requestedColor
41
+ && (environment.NO_COLOR === undefined || environment.NO_COLOR === '');
42
+ return Object.freeze({
43
+ color,
44
+ interactive,
45
+ width: interactive ? boundedTerminalWidth(stream?.columns) : null,
46
+ });
47
+ }
48
+ function tone(value, selectedTone, enabled) {
49
+ return enabled ? `${ANSI_BY_TONE[selectedTone]}${value}${ANSI_RESET}` : value;
50
+ }
51
+ function statusTone(value) {
52
+ const normalized = value.trim().toLowerCase();
53
+ if (/^(?:success|complete|completed|available|active|observed|pass|passed|equivalent|yes)(?:\b|\s|·|\()/u.test(normalized)) {
54
+ return 'success';
55
+ }
56
+ if (/^(?:error|failed|failure|danger|conflict|invalid)(?:\b|\s|·|\()/u.test(normalized)) {
57
+ return 'danger';
58
+ }
59
+ if (/^(?:partial|warning|review|unknown|inconclusive|rate limited|rate_limited|unsupported|unavailable|skipped|not found)(?:\b|\s|·|\()/u.test(normalized)) {
60
+ return 'warning';
61
+ }
62
+ return null;
63
+ }
64
+ function splitAtWord(value, maximum) {
65
+ if (value.length <= maximum)
66
+ return null;
67
+ const boundary = value.lastIndexOf(' ', maximum);
68
+ if (boundary <= 0) {
69
+ const nextBoundary = value.indexOf(' ', maximum);
70
+ return nextBoundary > 0
71
+ ? [value.slice(0, nextBoundary), value.slice(nextBoundary + 1).trimStart()]
72
+ : null;
73
+ }
74
+ return [value.slice(0, boundary), value.slice(boundary + 1).trimStart()];
75
+ }
76
+ function wrapLine(line, width) {
77
+ if (!line || line.length <= width)
78
+ return [line];
79
+ if (/^\s*(?:whoisleuth\b|node\s+bin\/whoisleuth\.mts\b|(?:cat|printf)\b.*\|\s*whoisleuth\b)/u.test(line)) {
80
+ return [line];
81
+ }
82
+ const keyValue = line.match(/^(\s{0,4}\S(?:.*?\S)?)(\s{2,})(\S.*)$/u);
83
+ const leading = line.match(/^\s*/u)?.[0] || '';
84
+ const prefix = keyValue ? `${keyValue[1]}${keyValue[2]}` : leading;
85
+ let remaining = keyValue ? keyValue[3] || '' : line.slice(leading.length);
86
+ const firstPrefix = prefix;
87
+ const continuationPrefix = ' '.repeat(Math.min(prefix.length, Math.floor(width / 2)));
88
+ const lines = [];
89
+ let currentPrefix = firstPrefix;
90
+ while (remaining) {
91
+ const available = Math.max(16, width - currentPrefix.length);
92
+ const split = splitAtWord(remaining, available);
93
+ if (!split) {
94
+ lines.push(`${currentPrefix}${remaining}`);
95
+ break;
96
+ }
97
+ lines.push(`${currentPrefix}${split[0]}`);
98
+ remaining = split[1];
99
+ currentPrefix = continuationPrefix;
100
+ }
101
+ return lines.length ? lines : [line];
102
+ }
103
+ function wrapTerminalOutput(value, width) {
104
+ if (width === null)
105
+ return value;
106
+ const trailingNewline = value.endsWith('\n');
107
+ const lines = value.replace(/\n$/u, '').split('\n').flatMap((line) => wrapLine(line, width));
108
+ return `${lines.join('\n')}${trailingNewline ? '\n' : ''}`;
109
+ }
110
+ function colorizeLine(line) {
111
+ const trimmed = line.trim();
112
+ if (!trimmed)
113
+ return line;
114
+ const section = trimmed.endsWith(':') ? trimmed.slice(0, -1) : trimmed;
115
+ if (SECTION_LABELS.has(section)) {
116
+ const prefixLength = line.indexOf(trimmed);
117
+ return `${line.slice(0, prefixLength)}${tone(trimmed, 'accent', true)}${line.slice(prefixLength + trimmed.length)}`;
118
+ }
119
+ if (trimmed.startsWith('✓'))
120
+ return line.replace('✓', tone('✓', 'success', true));
121
+ if (trimmed.startsWith('!'))
122
+ return line.replace('!', tone('!', 'danger', true));
123
+ if (/^(?:Limitation|Access note|Boundary)\s{2,}/u.test(trimmed)) {
124
+ return tone(line, 'dim', true);
125
+ }
126
+ const bracket = line.match(/^(\s*)\[([^\]]+)\](.*)$/u);
127
+ if (bracket) {
128
+ const selectedTone = statusTone(bracket[2] || '') || 'info';
129
+ return `${bracket[1]}${tone(`[${bracket[2]}]`, selectedTone, true)}${bracket[3]}`;
130
+ }
131
+ const keyValue = line.match(/^(\s*)(\S(?:.*?\S)?)(\s{2,})(\S.*)$/u);
132
+ if (keyValue) {
133
+ const value = keyValue[4] || '';
134
+ const selectedTone = statusTone(value);
135
+ return `${keyValue[1]}${tone(keyValue[2] || '', 'info', true)}${keyValue[3]}${selectedTone ? tone(value, selectedTone, true) : value}`;
136
+ }
137
+ return line;
138
+ }
139
+ function presentTerminalOutput(value, presentation) {
140
+ const wrapped = wrapTerminalOutput(value, presentation.width);
141
+ if (!presentation.color)
142
+ return wrapped;
143
+ const trailingNewline = wrapped.endsWith('\n');
144
+ const lines = wrapped.replace(/\n$/u, '').split('\n').map(colorizeLine);
145
+ return `${lines.join('\n')}${trailingNewline ? '\n' : ''}`;
146
+ }
147
+ export { presentTerminalOutput, terminalPresentation, tone, wrapTerminalOutput, };