@slicedearth/whoisleuth-cli 2.0.1 → 2.1.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 (95) hide show
  1. package/cli/arguments.mjs +21 -5
  2. package/cli/artifact-validation/case-response.mjs +771 -0
  3. package/cli/artifact-validation/investigation-capsule.mjs +426 -0
  4. package/cli/artifact-validation/lookup-evidence.mjs +718 -0
  5. package/cli/artifact-validation/signed-review.mjs +598 -0
  6. package/cli/artifact-validation/structure-primitives.mjs +105 -0
  7. package/cli/artifact-verify.mjs +1 -1
  8. package/cli/assurance-command-runner.mjs +324 -0
  9. package/cli/command-reference.mjs +1175 -843
  10. package/cli/completion.mjs +56 -9
  11. package/cli/discriminated-command-handlers.mjs +11 -0
  12. package/cli/doctor.mjs +2 -2
  13. package/cli/evidence-command-runner.mjs +41 -31
  14. package/cli/export-evidence.mjs +1 -1
  15. package/cli/fail-policy.mjs +33 -8
  16. package/cli/formatters/terminal-command-formats.mjs +437 -0
  17. package/cli/formatters/terminal-lookup.mjs +758 -0
  18. package/cli/formatters/terminal-metadata.mjs +58 -0
  19. package/cli/formatters/terminal-shared.mjs +83 -0
  20. package/cli/formatters/terminal.mjs +5 -1232
  21. package/cli/history-command-runner.mjs +132 -0
  22. package/cli/inline-command-families.mjs +45 -0
  23. package/cli/inline-command-runner.mjs +55 -0
  24. package/cli/investigation-plan.mjs +9 -2
  25. package/cli/investigation-run.mjs +114 -9
  26. package/cli/lookup-brief.mjs +141 -23
  27. package/cli/lookup-browser-view.mjs +289 -0
  28. package/cli/lookup-browser.mjs +3 -288
  29. package/cli/mail-review.mjs +2 -6
  30. package/cli/offline-artifact-validation.mjs +19 -0
  31. package/cli/review-command-runner.mjs +269 -0
  32. package/cli/runner.mjs +15 -972
  33. package/cli/saved-lookup.mjs +19 -2
  34. package/cli/support-command-runner.mjs +229 -0
  35. package/cli/workflow-command-runner.mjs +129 -0
  36. package/docs/cli-reference.md +16 -9
  37. package/docs/cli.md +4 -2
  38. package/lib/availability.mjs +12 -9
  39. package/lib/bounded-contract-normalizers.mjs +2 -9
  40. package/lib/browser-library-profile.mjs +1 -2
  41. package/lib/client-behavior-profile.mjs +1 -2
  42. package/lib/credential-surface-profile.mjs +1 -1
  43. package/lib/domain-posture-analysis.mjs +10 -1
  44. package/lib/domain-posture.mjs +51 -23
  45. package/lib/evidence-export.mjs +46 -5
  46. package/lib/evidence-report-markdown.mjs +4 -0
  47. package/lib/evidence-report.mjs +50 -0
  48. package/lib/generated/cisa-kev-catalog.mjs +34 -4
  49. package/lib/generated/registrar-standing-catalogue.mjs +367 -0
  50. package/lib/json-record.mjs +12 -0
  51. package/lib/lookup-child-profile-contract.mjs +858 -0
  52. package/lib/lookup-contract-primitives.mjs +98 -0
  53. package/lib/lookup-diagnostics.mjs +96 -0
  54. package/lib/lookup-network-evidence-bounds.mjs +1 -0
  55. package/lib/lookup-response.mjs +367 -0
  56. package/lib/lookup.mjs +28 -443
  57. package/lib/page-role-profile.mjs +1 -3
  58. package/lib/registrar-standing-catalogue-contract.mjs +70 -0
  59. package/lib/registrar-standing-contract.mjs +300 -0
  60. package/lib/registrar-standing.mjs +269 -0
  61. package/lib/registry-insights.mjs +2 -8
  62. package/lib/risk-scoring.mjs +26 -10
  63. package/lib/sslbl-certificates.generated.mjs +138 -136
  64. package/lib/structured-data-identity.mjs +1 -3
  65. package/lib/technology-evidence-role.mjs +51 -0
  66. package/lib/threat-intelligence-runtime.mjs +3 -3
  67. package/lib/threat-intelligence-types.mjs +13 -0
  68. package/lib/threatfox-intelligence.mjs +1 -3
  69. package/lib/tls-intelligence.mjs +1 -2
  70. package/lib/urlhaus-intelligence.mjs +1 -3
  71. package/lib/urlscan-intelligence.mjs +1 -3
  72. package/lib/website-security-posture.mjs +1 -2
  73. package/lib/website-technology.mjs +72 -23
  74. package/lib/whois-common-formats.mjs +276 -0
  75. package/lib/whois-dialects.mjs +534 -0
  76. package/lib/whois-parser-support.mjs +305 -0
  77. package/lib/whois-parser.mjs +135 -0
  78. package/lib/whois.mjs +6 -1187
  79. package/package.json +2 -2
  80. package/packages/cases/case-evidence-model.mjs +9 -0
  81. package/packages/cases/case-migration-model.mjs +17 -11
  82. package/packages/cases/case-response-model.mjs +13 -9
  83. package/packages/cases/case-response-packet.mjs +297 -112
  84. package/packages/comparison/page-similarity.mjs +23 -0
  85. package/packages/contracts/case-portability.mjs +225 -61
  86. package/packages/contracts/cli-lookup.mjs +4 -3
  87. package/packages/contracts/domain-control-manifest.mjs +1 -1
  88. package/packages/contracts/investigation-portability.mjs +1 -1
  89. package/packages/contracts/risk-calibration.mjs +2 -2
  90. package/packages/contracts/workspace-portability.mjs +34 -6
  91. package/packages/investigation/evidence-coverage-ledger.mjs +21 -0
  92. package/packages/investigation/lookup-claim-readiness.mjs +2 -2
  93. package/packages/workspace/website-snapshot-model.mjs +79 -6
  94. package/third-party-notices.txt +1 -1
  95. package/cli/artifact-structure.mjs +0 -2507
@@ -0,0 +1,58 @@
1
+ import { validHttpDeliveryMetadata, validPagePublicationMetadata, } from "../../lib/homepage-metadata-contract.mjs";
2
+ import { safeTerminalValue, terminalDisplayCount, terminalRecord, titleCase, } from "./terminal-shared.mjs";
3
+ function appendPublicationMetadataLines(lines, value, detail) {
4
+ if (detail === 'summary' || !validPagePublicationMetadata(value))
5
+ return;
6
+ const metadata = terminalRecord(value);
7
+ const robots = terminalRecord(metadata.robots);
8
+ const twitter = terminalRecord(metadata.twitterCard);
9
+ const headings = terminalRecord(metadata.headings);
10
+ const images = terminalRecord(metadata.images);
11
+ const blocking = terminalRecord(metadata.renderBlockingCandidates);
12
+ lines.push(`Publication ${metadata.complete === true ? 'Complete' : 'Partial'} · robots ${titleCase(robots.status)} · card ${titleCase(twitter.status)}`);
13
+ const directives = Array.isArray(robots.directives) ? robots.directives.map((item) => safeTerminalValue(item)) : [];
14
+ if (directives.length)
15
+ lines.push(`Robots ${safeTerminalValue(directives.join(', '))}${robots.conflicting === true ? ' · conflicting' : ''}`);
16
+ else if (robots.status === 'not_observed')
17
+ lines.push('Robots No declaration observed in captured static HTML');
18
+ if (twitter.cardType)
19
+ lines.push(`Card type ${safeTerminalValue(twitter.cardType)}`);
20
+ lines.push(`Static page headings ${terminalDisplayCount(headings.total)} · images ${terminalDisplayCount(images.total)} · blocking candidates ${terminalDisplayCount(blocking.total)}`);
21
+ if (detail === 'verbose') {
22
+ lines.push(`Image alt missing ${terminalDisplayCount(images.altMissing)} · empty ${terminalDisplayCount(images.altEmpty)} · non-empty ${terminalDisplayCount(images.altNonEmpty)} · unclassified ${terminalDisplayCount(images.altUnclassified)}`);
23
+ lines.push(`Blocking scripts ${terminalDisplayCount(blocking.script)} · stylesheets ${terminalDisplayCount(blocking.stylesheet)} · static candidates only`);
24
+ }
25
+ }
26
+ function appendDeliveryMetadataLines(lines, value, verbose = false) {
27
+ if (!validHttpDeliveryMetadata(value))
28
+ return;
29
+ const metadata = terminalRecord(value);
30
+ const encoding = terminalRecord(metadata.contentEncoding);
31
+ const cache = terminalRecord(metadata.cachePolicy);
32
+ const codings = Array.isArray(encoding.codings) ? encoding.codings.map((item) => safeTerminalValue(item)) : [];
33
+ lines.push(`Delivery ${metadata.complete === true ? 'Complete' : 'Partial'} · encoding ${titleCase(encoding.status)} · cache ${titleCase(cache.status)}`);
34
+ if (codings.length)
35
+ lines.push(`Content coding ${safeTerminalValue(codings.join(', '))}`);
36
+ const cacheDirectives = [
37
+ ['no-store', cache.noStore], ['no-cache', cache.noCache], ['must-revalidate', cache.mustRevalidate],
38
+ ['public', cache.public], ['private', cache.private], ['immutable', cache.immutable],
39
+ ].filter(([, present]) => present === true).map(([label]) => label);
40
+ if (cacheDirectives.length)
41
+ lines.push(`Cache policy ${safeTerminalValue(cacheDirectives.join(', '))}`);
42
+ if (verbose) {
43
+ const seconds = [
44
+ cache.maxAgeSeconds === null ? null : `max-age ${terminalDisplayCount(cache.maxAgeSeconds)}s`,
45
+ cache.sMaxAgeSeconds === null ? null : `s-maxage ${terminalDisplayCount(cache.sMaxAgeSeconds)}s`,
46
+ cache.ageSeconds === null ? null : `Age ${terminalDisplayCount(cache.ageSeconds)}s`,
47
+ ].filter(Boolean);
48
+ if (seconds.length)
49
+ lines.push(`Cache timing ${safeTerminalValue(seconds.join(' · '))}`);
50
+ const declared = [
51
+ terminalRecord(cache.etag).present === true ? 'ETag' : null,
52
+ terminalRecord(cache.lastModified).present === true ? 'Last-Modified' : null,
53
+ terminalRecord(cache.expires).present === true ? 'Expires' : null,
54
+ ].filter(Boolean);
55
+ lines.push(`Validators ${declared.length ? safeTerminalValue(declared.join(', ')) : 'No validator declaration observed'}`);
56
+ }
57
+ }
58
+ export { appendDeliveryMetadataLines, appendPublicationMetadataLines };
@@ -0,0 +1,83 @@
1
+ const MAX_TERMINAL_VALUE_LENGTH = 240;
2
+ const MAX_TLS_TERMINAL_ALT_NAMES = 10;
3
+ const MAX_TLS_TERMINAL_PURPOSES = 8;
4
+ // Archive values are attacker-controlled evidence. Remove the complete Unicode
5
+ // default-ignorable class so visually identical identifiers cannot differ only
6
+ // through soft hyphens, combining grapheme joiners, variation selectors, tag
7
+ // characters, bidi controls, or other zero-width formatting code points.
8
+ const TERMINAL_DEFAULT_IGNORABLE_RE = /\p{Default_Ignorable_Code_Point}/gu;
9
+ function safeTerminalValue(value, fallback = '—') {
10
+ if (value === null || value === undefined || value === '')
11
+ return fallback;
12
+ const normalized = String(value)
13
+ .replace(/[\x00-\x1f\x7f-\x9f]+/g, ' ')
14
+ .replace(TERMINAL_DEFAULT_IGNORABLE_RE, '')
15
+ .replace(/\s+/g, ' ')
16
+ .trim();
17
+ if (!normalized)
18
+ return fallback;
19
+ return normalized.length > MAX_TERMINAL_VALUE_LENGTH
20
+ ? `${normalized.slice(0, MAX_TERMINAL_VALUE_LENGTH - 1)}…`
21
+ : normalized;
22
+ }
23
+ function boundedTerminalList(values, omitted) {
24
+ const suffix = omitted > 0 ? ` · +${omitted} more` : '';
25
+ const maximumBodyLength = Math.max(1, MAX_TERMINAL_VALUE_LENGTH - suffix.length);
26
+ const joined = values.join(', ');
27
+ const body = joined.length > maximumBodyLength
28
+ ? `${joined.slice(0, Math.max(0, maximumBodyLength - 1))}…`
29
+ : joined;
30
+ return `${body}${suffix}`;
31
+ }
32
+ function boundedTerminalComponent(value, maximum) {
33
+ const normalized = safeTerminalValue(value);
34
+ return normalized.length > maximum ? `${normalized.slice(0, Math.max(0, maximum - 1))}…` : normalized;
35
+ }
36
+ function boundedTerminalWithSuffix(value, suffix) {
37
+ if (!suffix)
38
+ return safeTerminalValue(value);
39
+ const retainedSuffix = suffix.length >= MAX_TERMINAL_VALUE_LENGTH
40
+ ? `${suffix.slice(0, MAX_TERMINAL_VALUE_LENGTH - 1)}…`
41
+ : suffix;
42
+ const maximumValueLength = Math.max(1, MAX_TERMINAL_VALUE_LENGTH - retainedSuffix.length);
43
+ const retainedValue = value.length > maximumValueLength
44
+ ? `${value.slice(0, Math.max(0, maximumValueLength - 1))}…`
45
+ : value;
46
+ return `${retainedValue}${retainedSuffix}`;
47
+ }
48
+ function titleCase(value) {
49
+ const text = safeTerminalValue(value, 'unknown').replaceAll('_', ' ');
50
+ return text.charAt(0).toUpperCase() + text.slice(1);
51
+ }
52
+ function terminalRecord(value) {
53
+ return value && typeof value === 'object' && !Array.isArray(value)
54
+ ? value
55
+ : {};
56
+ }
57
+ function terminalCount(value) {
58
+ const count = Number(value);
59
+ return Number.isSafeInteger(count) && count >= 0 ? Math.min(count, 999) : 0;
60
+ }
61
+ function terminalDisplayCount(value) {
62
+ const count = Number(value);
63
+ return Number.isSafeInteger(count) && count >= 0 ? String(count) : '0';
64
+ }
65
+ function terminalCountSummary(value, labels) {
66
+ const source = terminalRecord(value);
67
+ return labels
68
+ .map(([key, label]) => {
69
+ const count = Number(source[key]);
70
+ return [label, Number.isSafeInteger(count) && count >= 0 ? count : 0];
71
+ })
72
+ .filter(([, count]) => count > 0)
73
+ .map(([label, count]) => `${label} ${terminalDisplayCount(count)}`)
74
+ .join(', ');
75
+ }
76
+ function appendSection(lines, label, values) {
77
+ if (!values.length)
78
+ return;
79
+ if (lines.length)
80
+ lines.push('');
81
+ lines.push(`${label}:`, ...values);
82
+ }
83
+ export { MAX_TERMINAL_VALUE_LENGTH, MAX_TLS_TERMINAL_ALT_NAMES, MAX_TLS_TERMINAL_PURPOSES, appendSection, boundedTerminalComponent, boundedTerminalList, boundedTerminalWithSuffix, safeTerminalValue, terminalCount, terminalCountSummary, terminalDisplayCount, terminalRecord, titleCase, };