@slicedearth/whoisleuth-cli 1.43.0 → 1.47.4

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 (153) hide show
  1. package/DISCLOSURE +7 -3
  2. package/NOTICE +5 -4
  3. package/README.md +67 -10
  4. package/bin/whoisleuth.mjs +22 -9
  5. package/cli/arguments.mjs +256 -9
  6. package/cli/artifact-structure.mjs +1833 -0
  7. package/cli/artifact-verify.mjs +242 -49
  8. package/cli/bounded-json.mjs +1 -0
  9. package/cli/bulk-checkpoint.mjs +67 -31
  10. package/cli/bulk-command-runner.mjs +16 -6
  11. package/cli/bulk.mjs +18 -7
  12. package/cli/case-pack.mjs +819 -34
  13. package/cli/command-reference.mjs +48 -24
  14. package/cli/completion.mjs +50 -11
  15. package/cli/config-profile.mjs +15 -12
  16. package/cli/ct-event-intake.mjs +32 -2
  17. package/cli/discovery-observation-snapshot.mjs +8 -14
  18. package/cli/discovery-scan-command-runner.mjs +18 -6
  19. package/cli/discovery-snapshot.mjs +8 -15
  20. package/cli/discovery-workflow.mjs +2 -3
  21. package/cli/doctor.mjs +2 -2
  22. package/cli/domain-control-monitor.mjs +21 -15
  23. package/cli/domain-control-observations.mjs +25 -2
  24. package/cli/errors.mjs +7 -1
  25. package/cli/evidence-command-runner.mjs +17 -11
  26. package/cli/evidence-signing.mjs +73 -45
  27. package/cli/export-evidence.mjs +11 -4
  28. package/cli/formatters/html.mjs +3 -3
  29. package/cli/formatters/json.mjs +12 -4
  30. package/cli/formatters/terminal.mjs +4 -1
  31. package/cli/http.mjs +2 -1
  32. package/cli/input.mjs +57 -0
  33. package/cli/interchange-report.mjs +226 -0
  34. package/cli/investigation-manifest.mjs +16 -17
  35. package/cli/investigation-run.mjs +71 -23
  36. package/cli/local-mmdb-review.mjs +45 -14
  37. package/cli/lookup-command-runner.mjs +2 -2
  38. package/cli/lookup-diff.mjs +9 -0
  39. package/cli/lookup-reconcile.mjs +1 -1
  40. package/cli/mail-review.mjs +5 -5
  41. package/cli/manual.mjs +1 -1
  42. package/cli/network-command-runner.mjs +78 -0
  43. package/cli/offline-evidence-review.mjs +28 -8
  44. package/cli/page-compare.mjs +8 -5
  45. package/cli/registry-cohort.mjs +385 -61
  46. package/cli/registry-fixture-scaffold.mjs +4 -3
  47. package/cli/registry-support.mjs +2 -1
  48. package/cli/retained-artifact-diff.mjs +376 -0
  49. package/cli/risk-calibration.mjs +7 -5
  50. package/cli/runner.mjs +150 -177
  51. package/cli/saved-lookup.mjs +26 -3
  52. package/cli/sharing-review.mjs +12 -6
  53. package/docs/cli-reference.md +467 -74
  54. package/docs/cli.md +25 -5
  55. package/frontend/src/lib/analysis/acquisition-decision-packet.js +4 -4
  56. package/frontend/src/lib/analysis/acquisition-due-diligence.js +1 -1
  57. package/frontend/src/lib/analysis/artifact-integrity.js +42 -0
  58. package/frontend/src/lib/analysis/brand-profile-model.js +33 -7
  59. package/frontend/src/lib/analysis/bulk-domain-comparison.js +25 -5
  60. package/frontend/src/lib/analysis/bulk-mail-exposure.js +66 -15
  61. package/frontend/src/lib/analysis/bulk-retry-plan.js +1 -1
  62. package/frontend/src/lib/analysis/bulk-review-export.js +7 -6
  63. package/frontend/src/lib/analysis/bulk-review-model.js +3 -2
  64. package/frontend/src/lib/analysis/bulk-session-model.js +192 -26
  65. package/frontend/src/lib/analysis/bulk-sort.js +4 -0
  66. package/frontend/src/lib/analysis/case-brand-profile-references.js +85 -0
  67. package/frontend/src/lib/analysis/case-evidence-model.js +9 -1
  68. package/frontend/src/lib/analysis/case-investigation-branch-model.js +134 -0
  69. package/frontend/src/lib/analysis/case-migration-model.js +83 -31
  70. package/frontend/src/lib/analysis/case-record-contracts.js +4 -2
  71. package/frontend/src/lib/analysis/case-record-core.js +1 -1
  72. package/frontend/src/lib/analysis/case-record-model.js +2 -0
  73. package/frontend/src/lib/analysis/case-record-operations.js +43 -13
  74. package/frontend/src/lib/analysis/case-report.js +39 -6
  75. package/frontend/src/lib/analysis/case-response-model.js +66 -7
  76. package/frontend/src/lib/analysis/case-response-packet.js +15 -5
  77. package/frontend/src/lib/analysis/comparison-ledger-bulk.js +366 -0
  78. package/frontend/src/lib/analysis/comparison-ledger-contract.js +241 -0
  79. package/frontend/src/lib/analysis/comparison-ledger-json.js +79 -0
  80. package/frontend/src/lib/analysis/comparison-ledger-serialization.js +51 -0
  81. package/frontend/src/lib/analysis/domain-control-manifest-core.js +145 -0
  82. package/frontend/src/lib/analysis/domain-control-records.js +88 -0
  83. package/frontend/src/lib/analysis/external-findings-import.js +70 -6
  84. package/frontend/src/lib/analysis/investigation-capsule.js +47 -13
  85. package/frontend/src/lib/analysis/investigation-guide.js +52 -10
  86. package/frontend/src/lib/analysis/investigation-template-model.js +9 -6
  87. package/frontend/src/lib/analysis/lookup-asset-graph.js +8 -8
  88. package/frontend/src/lib/analysis/lookup-claim-passport.js +236 -0
  89. package/frontend/src/lib/analysis/lookup-claim-readiness.js +280 -0
  90. package/frontend/src/lib/analysis/lookup-decision-support.js +63 -8
  91. package/frontend/src/lib/analysis/lookup-presentation.js +70 -14
  92. package/frontend/src/lib/analysis/lookup-source-refresh.js +36 -63
  93. package/frontend/src/lib/analysis/opaque-reference-id.js +9 -0
  94. package/frontend/src/lib/analysis/utils.js +27 -4
  95. package/frontend/src/lib/analysis/visualization-matrix.js +71 -28
  96. package/frontend/src/lib/analysis/visualization-models.js +63 -94
  97. package/frontend/src/lib/analysis/visualization-time-series.js +53 -12
  98. package/frontend/src/lib/analysis/website-snapshot-model.js +6 -0
  99. package/frontend/src/lib/analysis/workspace-archive-crypto.js +1 -4
  100. package/frontend/src/lib/analysis/workspace-archive.js +105 -19
  101. package/lib/application-version.mjs +3 -0
  102. package/lib/availability.mjs +14 -12
  103. package/lib/bounded-file.mjs +82 -0
  104. package/lib/bounded-json-response.mjs +170 -0
  105. package/lib/bounded-json.mjs +249 -0
  106. package/lib/browser-library-profile.mjs +167 -38
  107. package/lib/certificate-extension-profile.mjs +2 -1
  108. package/lib/cryptographic-assurance.mjs +155 -0
  109. package/lib/ct-search.mjs +87 -47
  110. package/lib/dns-intelligence.mjs +3 -2
  111. package/lib/dns-resolver-selection.mjs +6 -4
  112. package/lib/dnssec-chain-validation.mjs +1053 -0
  113. package/lib/dnssec-evidence-validation.mjs +10 -3
  114. package/lib/domain-change-packet.mjs +7 -5
  115. package/lib/domain-control-manifest.mjs +17 -129
  116. package/lib/domain-posture.mjs +7 -4
  117. package/lib/evidence-export.mjs +751 -78
  118. package/lib/evidence-report-markdown.mjs +3 -0
  119. package/lib/evidence-report.mjs +5 -0
  120. package/lib/favicon.mjs +8 -5
  121. package/lib/http-evidence-bounds.mjs +3 -0
  122. package/lib/http-intelligence.mjs +2 -5
  123. package/lib/interchange-fidelity-registry.mjs +122 -0
  124. package/lib/local-geoip-evidence.mjs +2 -1
  125. package/lib/lookup-cache.mjs +5 -1
  126. package/lib/lookup-network-evidence-bounds.mjs +9 -0
  127. package/lib/lookup-response-contract.mjs +691 -3
  128. package/lib/lookup.mjs +2 -2
  129. package/lib/observation.mjs +7 -6
  130. package/lib/outbound-identity.mjs +2 -3
  131. package/lib/perceptual-hash.mjs +12 -4
  132. package/lib/portable-generator.mjs +21 -0
  133. package/lib/provider-policy-admission.mjs +9 -4
  134. package/lib/rdap-normalization.mjs +18 -5
  135. package/lib/rdap-registrar.mjs +5 -1
  136. package/lib/rdap-transport.mjs +6 -6
  137. package/lib/registry-capabilities.mjs +3 -0
  138. package/lib/registry-capability-catalogue.mjs +12 -2
  139. package/lib/registry-comparison.mjs +28 -11
  140. package/lib/risk-calibration-summary.mjs +338 -0
  141. package/lib/rpki-evidence.mjs +9 -8
  142. package/lib/safe-fetch.mjs +96 -42
  143. package/lib/security-txt.mjs +26 -3
  144. package/lib/service-binding-dns.mjs +6 -1
  145. package/lib/smtp-transport-review.mjs +784 -0
  146. package/lib/sslbl-certificates.generated.mjs +136 -134
  147. package/lib/threat-intelligence-runtime.mjs +4 -13
  148. package/lib/threat-intelligence-types.mjs +18 -1
  149. package/lib/tls-intelligence.mjs +11 -5
  150. package/lib/tlsa-evidence.mjs +37 -7
  151. package/lib/website-technology.mjs +2 -6
  152. package/package.json +1 -1
  153. package/third-party-notices.txt +95 -5817
@@ -61,6 +61,23 @@ function normalizedPublishedUri(value, schemes) {
61
61
  function normalizedHttpsUrl(value) {
62
62
  return normalizedPublishedUri(value, HTTPS_PROTOCOLS);
63
63
  }
64
+ function normalizedCanonicalComparisonUrl(value) {
65
+ if (!value || value.length > MAX_SECURITY_TXT_URL_LENGTH || URI_CONTROL_CHARACTER_RE.test(value))
66
+ return null;
67
+ try {
68
+ const url = new URL(value.trim());
69
+ if (url.protocol !== 'https:' || !url.hostname || url.username || url.password)
70
+ return null;
71
+ // A fragment is never part of the retrieval URI. Preserve the query only
72
+ // for the transient RFC 9116 Canonical comparison; retained values remain
73
+ // query-free under the existing privacy-minimised output contract.
74
+ url.hash = '';
75
+ return url.toString().slice(0, MAX_SECURITY_TXT_URL_LENGTH);
76
+ }
77
+ catch {
78
+ return null;
79
+ }
80
+ }
64
81
  function eligibleHttpsFetchUrl(value) {
65
82
  if (!value || value.length > MAX_SECURITY_TXT_URL_LENGTH || URI_CONTROL_CHARACTER_RE.test(value))
66
83
  return null;
@@ -113,6 +130,7 @@ function parseSecurityTxt(text, options = {}) {
113
130
  const policies = [];
114
131
  const encryption = [];
115
132
  const canonical = [];
133
+ const canonicalComparison = [];
116
134
  const preferredLanguages = [];
117
135
  const expires = [];
118
136
  for (const rawLine of lines.slice(0, MAX_SECURITY_TXT_LINES)) {
@@ -157,8 +175,11 @@ function parseSecurityTxt(text, options = {}) {
157
175
  }
158
176
  else if (field === 'canonical') {
159
177
  normalized = normalizedHttpsUrl(value);
160
- if (normalized)
178
+ const comparisonValue = normalizedCanonicalComparisonUrl(value);
179
+ if (normalized && comparisonValue) {
161
180
  canonical.push(normalized);
181
+ canonicalComparison.push(comparisonValue);
182
+ }
162
183
  else
163
184
  malformedCount += 1;
164
185
  }
@@ -187,8 +208,10 @@ function parseSecurityTxt(text, options = {}) {
187
208
  .some((entry) => entry.truncated);
188
209
  const expiresAt = expires.length === 1 ? expires[0] : null;
189
210
  const finalUrl = options.finalUrl ? normalizedHttpsUrl(options.finalUrl) : null;
190
- const canonicalMatches = boundedCanonical.values.length && finalUrl
191
- ? boundedCanonical.values.some((value) => value.toLowerCase() === finalUrl.toLowerCase())
211
+ const finalComparisonUrl = options.finalUrl ? normalizedCanonicalComparisonUrl(options.finalUrl) : null;
212
+ const canonicalMatches = boundedCanonical.values.length && finalUrl && finalComparisonUrl
213
+ ? canonicalComparison.slice(0, MAX_SECURITY_TXT_VALUES)
214
+ .some((value) => value.toLowerCase() === finalComparisonUrl.toLowerCase())
192
215
  : null;
193
216
  const stale = Boolean(expiresAt && Date.parse(expiresAt) <= (options.now ?? Date.now()));
194
217
  const requiredMalformed = boundedContacts.values.length === 0 || expires.length !== 1;
@@ -450,16 +450,21 @@ function defaultTcpExchange(query, resolver, options) {
450
450
  const chunks = [];
451
451
  let totalLength = 0;
452
452
  let settled = false;
453
+ let hardDeadline = null;
453
454
  const finish = (error, response) => {
454
455
  if (settled)
455
456
  return;
456
457
  settled = true;
458
+ if (hardDeadline)
459
+ clearTimeout(hardDeadline);
457
460
  socket.destroy();
458
461
  if (error)
459
462
  reject(error);
460
463
  else
461
464
  resolve(response);
462
465
  };
466
+ hardDeadline = setTimeout(() => finish(new ServiceBindingDnsError('DNS TCP query timed out', 'ETIMEOUT')), options.timeoutMs);
467
+ hardDeadline.unref();
463
468
  socket.setTimeout(options.timeoutMs, () => finish(new ServiceBindingDnsError('DNS TCP query timed out', 'ETIMEOUT')));
464
469
  socket.once('error', (error) => finish(error));
465
470
  socket.on('data', (chunk) => {
@@ -605,4 +610,4 @@ async function resolveServiceBindingRecords(nameValue, type, options = {}) {
605
610
  .slice(0, MAX_ERROR_LENGTH);
606
611
  throw new ServiceBindingDnsError(detail || 'DNS query failed', 'EDNSFAIL');
607
612
  }
608
- export { DNS_TIMEOUT_MS as SERVICE_BINDING_DNS_TIMEOUT_MS, HTTPS_TYPE, MAX_SERVICE_BINDING_RECORDS, SVCB_TYPE, ServiceBindingDnsError, buildServiceBindingDnsQuery, formatIpv6, parseResolverEndpoint, parseServiceBindingDnsResponse, resolveServiceBindingRecords, };
613
+ export { DNS_TIMEOUT_MS as SERVICE_BINDING_DNS_TIMEOUT_MS, HTTPS_TYPE, MAX_DNS_MESSAGE_BYTES, MAX_SERVICE_BINDING_RECORDS, SVCB_TYPE, ServiceBindingDnsError, buildServiceBindingDnsQuery, defaultTcpExchange, formatIpv6, parseResolverEndpoint, parseServiceBindingDnsResponse, resolveServiceBindingRecords, };