@slicedearth/whoisleuth-cli 1.47.4 → 2.0.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.
- package/README.md +74 -177
- package/SECURITY.md +6 -3
- package/bin/whoisleuth.mjs +2 -1
- package/cli/archive-inspect.mjs +28 -10
- package/cli/arguments.mjs +204 -174
- package/cli/artifact-structure.mjs +837 -163
- package/cli/artifact-verify.mjs +90 -61
- package/cli/bounded-json.mjs +1 -1
- package/cli/bulk-checkpoint.mjs +33 -20
- package/cli/bulk.mjs +9 -2
- package/cli/case-pack.mjs +180 -576
- package/cli/ci-report.mjs +6 -1
- package/cli/collection-preflight.mjs +2 -2
- package/cli/command-catalogue.mjs +12 -3
- package/cli/command-reference.mjs +910 -92
- package/cli/completion.mjs +504 -226
- package/cli/config-profile.mjs +13 -3
- package/cli/discover.mjs +7 -1
- package/cli/discovery-observation-snapshot.mjs +24 -28
- package/cli/discovery-scan-command-runner.mjs +9 -9
- package/cli/discovery-scan.mjs +17 -3
- package/cli/discovery-snapshot.mjs +15 -7
- package/cli/domain-control-monitor.mjs +203 -30
- package/cli/domain-control-observations.mjs +15 -21
- package/cli/errors.mjs +8 -3
- package/cli/evidence-signing.mjs +10 -20
- package/cli/external-observation-mapping.mjs +3 -3
- package/cli/formatters/json.mjs +20 -32
- package/cli/formatters/terminal.mjs +583 -22
- package/cli/input.mjs +7 -2
- package/cli/interactive-launcher.mjs +226 -0
- package/cli/interchange-report.mjs +19 -19
- package/cli/investigation-manifest.mjs +1 -1
- package/cli/investigation-plan.mjs +167 -16
- package/cli/investigation-run.mjs +9 -1
- package/cli/lookalike-calibration.mjs +3 -1
- package/cli/lookup-brief.mjs +13 -8
- package/cli/lookup-browser.mjs +844 -0
- package/cli/lookup-command-runner.mjs +94 -31
- package/cli/lookup-diff.mjs +2 -2
- package/cli/lookup-plan.mjs +1 -0
- package/cli/mail-review.mjs +206 -94
- package/cli/manual.mjs +5 -5
- package/cli/offline-evidence-review.mjs +18 -7
- package/cli/open-asset-model-bridge.mjs +1 -1
- package/cli/output-file.mjs +70 -14
- package/cli/page-compare.mjs +11 -6
- package/cli/progress-events.mjs +2 -1
- package/cli/progress.mjs +3 -3
- package/cli/registry-cohort.mjs +9 -51
- package/cli/registry-doctor.mjs +67 -28
- package/cli/retained-artifact-diff.mjs +7 -8
- package/cli/risk-calibration.mjs +85 -38
- package/cli/runner.mjs +189 -75
- package/cli/saved-lookup.mjs +424 -57
- package/cli/sharing-review.mjs +6 -1
- package/cli/source-reliability.mjs +75 -36
- package/cli/terminal-presentation.mjs +43 -18
- package/docs/cli-reference.md +125 -1708
- package/docs/cli.md +86 -254
- package/frontend/src/lib/analysis/domain-control-manifest-core.js +3 -145
- package/frontend/src/lib/analysis/domain-control-records.js +3 -88
- package/lib/availability.mjs +58 -40
- package/lib/bounded-contract-normalizers.mjs +2 -4
- package/lib/bounded-file.mjs +78 -4
- package/lib/bounded-json.mjs +51 -13
- package/lib/browser-library-profile.mjs +1 -1
- package/lib/classify.mjs +56 -5
- package/lib/client-behavior-profile.mjs +1 -1
- package/lib/credential-surface-profile.mjs +1 -1
- package/lib/cryptographic-assurance.mjs +35 -17
- package/lib/ct-query.mjs +3 -3
- package/lib/ct-search.mjs +37 -21
- package/lib/distributed-operation-budget.mjs +4 -2
- package/lib/dns-delegation-health.mjs +9 -6
- package/lib/dns-intelligence.mjs +1 -1
- package/lib/dnssec-chain-validation.mjs +2 -3
- package/lib/dnssec-evidence-validation.mjs +13 -7
- package/lib/domain-assurance.mjs +12 -12
- package/lib/domain-change-packet.mjs +1 -1
- package/lib/domain-control-flight-recorder.mjs +442 -78
- package/lib/domain-control-manifest.mjs +314 -59
- package/lib/domain-posture-analysis.mjs +5 -2
- package/lib/encrypted-dns-contract.mjs +2 -3
- package/lib/evidence-export.mjs +518 -36
- package/lib/external-intelligence-risk.mjs +26 -10
- package/lib/feature-policy.mjs +1 -1
- package/lib/homepage-metadata-contract.mjs +290 -0
- package/lib/hostname.mjs +1 -1
- package/lib/html-signals.mjs +89 -5
- package/lib/http-intelligence.mjs +331 -2
- package/lib/interchange-fidelity-registry.mjs +28 -26
- package/lib/lookup-cache.mjs +6 -4
- package/lib/lookup.mjs +25 -2
- package/lib/observed-network-context.mjs +1 -1
- package/lib/page-fingerprints.mjs +2 -2
- package/lib/page-role-profile.mjs +1 -1
- package/lib/portable-generator.mjs +8 -0
- package/lib/provider-policy-admission.mjs +3 -1
- package/lib/rdap-bootstrap.mjs +76 -16
- package/lib/rdap-client.mjs +20 -12
- package/lib/rdap-normalization.mjs +306 -75
- package/lib/rdap-transport.mjs +2 -2
- package/lib/rdap-validation.mjs +65 -14
- package/lib/rdap.mjs +2 -2
- package/lib/registrable-domain.mjs +36 -0
- package/lib/registry-insights.mjs +7 -0
- package/lib/response-policy.mjs +27 -4
- package/lib/risk-calibration-summary.mjs +35 -33
- package/lib/risk-scoring.mjs +4 -3
- package/lib/safe-fetch.mjs +62 -40
- package/lib/security-txt.mjs +1 -1
- package/lib/smtp-transport-review.mjs +8 -5
- package/lib/sslbl-intelligence.mjs +6 -8
- package/lib/static-html-analysis.mjs +301 -4
- package/lib/structured-data-identity.mjs +51 -19
- package/lib/threat-intelligence-fixtures.mjs +2 -4
- package/lib/threat-intelligence-manifests.mjs +2 -7
- package/lib/threat-intelligence-runtime.mjs +3 -5
- package/lib/threat-intelligence-types.mjs +3 -0
- package/lib/threatfox-intelligence.mjs +7 -3
- package/lib/tls-intelligence.mjs +4 -4
- package/lib/urlhaus-intelligence.mjs +4 -2
- package/lib/urlscan-intelligence.mjs +6 -5
- package/lib/website-security-posture.mjs +1 -1
- package/lib/website-technology.mjs +1 -1
- package/lib/whois-authority.mjs +43 -8
- package/lib/whois-chain.mjs +25 -3
- package/lib/whois-normalization.mjs +3 -1
- package/lib/whois-transport.mjs +12 -3
- package/lib/zone-intent-review.mjs +1 -1
- package/package.json +12 -4
- package/{frontend/src/lib/analysis/case-brand-profile-references.js → packages/cases/case-brand-profile-references.mjs} +2 -2
- package/{frontend/src/lib/analysis/case-evidence-model.js → packages/cases/case-evidence-model.mjs} +35 -14
- package/{frontend/src/lib/analysis/case-investigation-branch-model.js → packages/cases/case-investigation-branch-model.mjs} +10 -12
- package/packages/cases/case-migration-model.mjs +476 -0
- package/{frontend/src/lib/analysis/case-model.js → packages/cases/case-model.mjs} +3 -3
- package/{frontend/src/lib/analysis/case-record-contracts.js → packages/cases/case-record-contracts.mjs} +3 -21
- package/{frontend/src/lib/analysis/case-record-core.js → packages/cases/case-record-core.mjs} +58 -31
- package/packages/cases/case-record-model.mjs +9 -0
- package/{frontend/src/lib/analysis/case-record-operations.js → packages/cases/case-record-operations.mjs} +72 -29
- package/{frontend/src/lib/analysis/case-report.js → packages/cases/case-report.mjs} +108 -14
- package/packages/cases/case-response-model.mjs +1604 -0
- package/packages/cases/case-response-packet.mjs +1312 -0
- package/{frontend/src/lib/analysis/case-storage-model.js → packages/cases/case-storage-model.mjs} +2 -2
- package/{frontend/src/lib/analysis/http-summary.js → packages/cases/http-summary.mjs} +1 -1
- package/{frontend/src/lib/analysis/comparison-ledger-bulk.js → packages/comparison/comparison-ledger-bulk.mjs} +2 -2
- package/{frontend/src/lib/analysis/comparison-ledger-contract.js → packages/comparison/comparison-ledger-contract.mjs} +4 -4
- package/{frontend/src/lib/analysis/comparison-ledger-json.js → packages/comparison/comparison-ledger-json.mjs} +1 -0
- package/{frontend/src/lib/analysis/comparison-ledger-serialization.js → packages/comparison/comparison-ledger-serialization.mjs} +1 -0
- package/packages/comparison/favicon-similarity.mjs +78 -0
- package/{frontend/src/lib/analysis/page-similarity.js → packages/comparison/page-similarity.mjs} +4 -3
- package/{frontend/src/lib/analysis/relationship-evidence.js → packages/comparison/relationship-evidence.mjs} +6 -7
- package/packages/contracts/analyst-review-state-contract.mjs +61 -0
- package/packages/contracts/analyst-review-state.mjs +50 -0
- package/packages/contracts/case-portability.mjs +1310 -0
- package/packages/contracts/cli-command-catalogue.mjs +2 -0
- package/packages/contracts/cli-lookup.mjs +394 -0
- package/packages/contracts/domain-control-flight-recorder.mjs +461 -0
- package/packages/contracts/domain-control-manifest.mjs +1027 -0
- package/packages/contracts/domain-control-monitor.mjs +310 -0
- package/packages/contracts/domain-control-review.mjs +828 -0
- package/packages/contracts/domain-name.mjs +4 -0
- package/packages/contracts/external-observation-interchange.mjs +224 -0
- package/packages/contracts/extracted-domain-lifecycle.mjs +218 -0
- package/packages/contracts/investigation-portability.mjs +331 -0
- package/packages/contracts/offline-comparison.mjs +160 -0
- package/packages/contracts/risk-calibration.mjs +1288 -0
- package/packages/contracts/schema-compatibility.mjs +71 -0
- package/packages/contracts/schema-lifecycle.mjs +1702 -0
- package/packages/contracts/sslbl-snapshot.mjs +17 -0
- package/packages/contracts/workspace-portability.mjs +1347 -0
- package/{frontend/src/lib/analysis/artifact-integrity.js → packages/evidence/artifact-integrity.mjs} +13 -11
- package/packages/evidence/decision-fact.mjs +735 -0
- package/packages/evidence/domain-control-runtime.mjs +547 -0
- package/packages/evidence/domain-name.mjs +41 -0
- package/packages/evidence/observation.mjs +384 -0
- package/{frontend/src/lib/analysis/external-findings-import.js → packages/interchange/external-findings-import.mjs} +24 -22
- package/packages/interchange/retained-text.mjs +8 -0
- package/{frontend/src/lib/analysis/acquisition-decision-packet.js → packages/investigation/acquisition-decision-packet.mjs} +9 -6
- package/{frontend/src/lib/analysis/bulk-domain-comparison.js → packages/investigation/bulk-domain-comparison.mjs} +5 -6
- package/{frontend/src/lib/analysis/bulk-mail-exposure.js → packages/investigation/bulk-mail-exposure.mjs} +4 -6
- package/{frontend/src/lib/analysis/bulk-review-export.js → packages/investigation/bulk-review-export.mjs} +5 -5
- package/packages/investigation/bulk-review-policy.mjs +1 -0
- package/{frontend/src/lib/analysis/investigation-capsule.js → packages/investigation/investigation-capsule.mjs} +30 -32
- package/packages/investigation/lookup-artefact-inputs.mjs +1 -0
- package/{frontend/src/lib/analysis/lookup-asset-graph.js → packages/investigation/lookup-asset-graph.mjs} +4 -2
- package/{frontend/src/lib/analysis/lookup-claim-passport.js → packages/investigation/lookup-claim-passport.mjs} +6 -10
- package/{frontend/src/lib/analysis/lookup-claim-readiness.js → packages/investigation/lookup-claim-readiness.mjs} +9 -2
- package/packages/investigation/lookup-investigation-brief.mjs +172 -0
- package/packages/monitoring/analyst-review-state.mjs +680 -0
- package/{frontend/src/lib/analysis/brand-profile-model.js → packages/workspace/brand-profile-model.mjs} +70 -47
- package/{frontend/src/lib/analysis/bulk-review-model.js → packages/workspace/bulk-review-model.mjs} +13 -14
- package/{frontend/src/lib/analysis/bulk-session-model.js → packages/workspace/bulk-session-model.mjs} +33 -59
- package/{frontend/src/lib/analysis/bulk-sort.js → packages/workspace/bulk-sort.mjs} +8 -5
- package/{frontend/src/lib/analysis/campaign-model.js → packages/workspace/campaign-model.mjs} +20 -22
- package/{frontend/src/lib/analysis/detection-rule-model.js → packages/workspace/detection-rule-model.mjs} +15 -17
- package/packages/workspace/hostile-input.mjs +172 -0
- package/{frontend/src/lib/analysis/investigation-guide.js → packages/workspace/investigation-guide.mjs} +8 -45
- package/{frontend/src/lib/analysis/investigation-template-model.js → packages/workspace/investigation-template-model.mjs} +18 -21
- package/{frontend/src/lib/analysis/page-baseline.js → packages/workspace/page-baseline.mjs} +6 -13
- package/{frontend/src/lib/analysis/relationship-observation-model.js → packages/workspace/relationship-observation-model.mjs} +13 -17
- package/{frontend/src/lib/analysis/shortlist-model.js → packages/workspace/shortlist-model.mjs} +20 -18
- package/{frontend/src/lib/analysis/watchlist-history.js → packages/workspace/watchlist-history.mjs} +9 -16
- package/{frontend/src/lib/analysis/watchlist-store.js → packages/workspace/watchlist-store.mjs} +24 -17
- package/{frontend/src/lib/analysis/website-snapshot-model.js → packages/workspace/website-snapshot-model.mjs} +13 -13
- package/{frontend/src/lib/analysis/workspace-archive-crypto.js → packages/workspace/workspace-archive-crypto.mjs} +49 -19
- package/{frontend/src/lib/analysis/workspace-archive.js → packages/workspace/workspace-archive.mjs} +110 -89
- package/third-party-notices.txt +14 -14
- package/frontend/src/lib/analysis/bulk-retry-plan.js +0 -79
- package/frontend/src/lib/analysis/bulk-source-coverage.js +0 -76
- package/frontend/src/lib/analysis/case-migration-model.js +0 -305
- package/frontend/src/lib/analysis/case-record-model.js +0 -9
- package/frontend/src/lib/analysis/case-response-model.js +0 -728
- package/frontend/src/lib/analysis/case-response-packet.js +0 -583
- package/frontend/src/lib/analysis/lookup-decision-support.js +0 -542
- package/frontend/src/lib/analysis/lookup-investigation-brief.js +0 -153
- package/frontend/src/lib/analysis/lookup-presentation.js +0 -120
- package/frontend/src/lib/analysis/lookup-response.js +0 -2
- package/frontend/src/lib/analysis/lookup-source-refresh.js +0 -277
- package/frontend/src/lib/analysis/lookup-summary-model.js +0 -398
- package/frontend/src/lib/analysis/scoring.js +0 -55
- package/frontend/src/lib/analysis/utils.js +0 -255
- package/frontend/src/lib/analysis/visualization-bounds.js +0 -26
- package/frontend/src/lib/analysis/visualization-force-graph.js +0 -352
- package/frontend/src/lib/analysis/visualization-matrix.js +0 -131
- package/frontend/src/lib/analysis/visualization-models.js +0 -338
- package/frontend/src/lib/analysis/visualization-time-series.js +0 -185
- package/lib/bounded-json-response.mjs +0 -170
- package/lib/lookup-response-contract.mjs +0 -1034
- package/lib/observation.mjs +0 -115
- /package/{frontend/src/lib/analysis/opaque-reference-id.js → packages/cases/opaque-reference-id.mjs} +0 -0
- /package/{frontend/src/lib/analysis/acquisition-due-diligence.js → packages/investigation/acquisition-due-diligence.mjs} +0 -0
- /package/{frontend/src/lib/analysis/evidence-coverage-ledger.js → packages/investigation/evidence-coverage-ledger.mjs} +0 -0
- /package/{frontend/src/lib/analysis/dns-record-normalization.js → packages/workspace/dns-record-normalization.mjs} +0 -0
|
@@ -1,6 +1,15 @@
|
|
|
1
1
|
import { registryAccessProfileLabel } from "../registry-access.mjs";
|
|
2
2
|
import { unicodeDomainFromAscii } from "../../lib/idn-confusables.mjs";
|
|
3
|
+
import { validHttpDeliveryMetadata, validPagePublicationMetadata, } from "../../lib/homepage-metadata-contract.mjs";
|
|
3
4
|
const MAX_TERMINAL_VALUE_LENGTH = 240;
|
|
5
|
+
const MAX_LOOKUP_TERMINAL_RECORDS = 5;
|
|
6
|
+
const MAX_LOOKUP_TERMINAL_NAMES = 5;
|
|
7
|
+
const MAX_LOOKUP_TERMINAL_ALPN_IDS = 4;
|
|
8
|
+
const MAX_LOOKUP_TERMINAL_ALPN_ID_LENGTH = 32;
|
|
9
|
+
const MAX_LOOKUP_TERMINAL_LIMITATIONS = 3;
|
|
10
|
+
const MAX_LOOKUP_TERMINAL_CIDRS = 5;
|
|
11
|
+
const MAX_LOOKUP_TERMINAL_FINDINGS = 5;
|
|
12
|
+
const MAX_LOOKUP_TERMINAL_ABUSE_ROUTES = 6;
|
|
4
13
|
const MAX_CT_TERMINAL_MATCHES = 100;
|
|
5
14
|
const MAX_CT_TERMINAL_HOSTNAMES = 5;
|
|
6
15
|
const MAX_DISCOVER_TERMINAL_CANDIDATES = 200;
|
|
@@ -8,15 +17,49 @@ const MAX_POSTURE_TERMINAL_RECORDS = 5;
|
|
|
8
17
|
const MAX_TLS_TERMINAL_ALT_NAMES = 10;
|
|
9
18
|
const MAX_TLS_TERMINAL_PURPOSES = 8;
|
|
10
19
|
const MAX_RISK_CALIBRATION_TERMINAL_RECORDS = 100;
|
|
20
|
+
// Archive values are attacker-controlled evidence. Remove the complete Unicode
|
|
21
|
+
// default-ignorable class so visually identical identifiers cannot differ only
|
|
22
|
+
// through soft hyphens, combining grapheme joiners, variation selectors, tag
|
|
23
|
+
// characters, bidi controls, or other zero-width formatting code points.
|
|
24
|
+
const TERMINAL_DEFAULT_IGNORABLE_RE = /\p{Default_Ignorable_Code_Point}/gu;
|
|
11
25
|
function safeTerminalValue(value, fallback = '—') {
|
|
12
26
|
if (value === null || value === undefined || value === '')
|
|
13
27
|
return fallback;
|
|
14
28
|
const normalized = String(value)
|
|
15
|
-
.replace(/[\x00-\x1f\x7f]+/g, ' ')
|
|
29
|
+
.replace(/[\x00-\x1f\x7f-\x9f]+/g, ' ')
|
|
30
|
+
.replace(TERMINAL_DEFAULT_IGNORABLE_RE, '')
|
|
16
31
|
.replace(/\s+/g, ' ')
|
|
17
|
-
.trim()
|
|
18
|
-
|
|
19
|
-
|
|
32
|
+
.trim();
|
|
33
|
+
if (!normalized)
|
|
34
|
+
return fallback;
|
|
35
|
+
return normalized.length > MAX_TERMINAL_VALUE_LENGTH
|
|
36
|
+
? `${normalized.slice(0, MAX_TERMINAL_VALUE_LENGTH - 1)}…`
|
|
37
|
+
: normalized;
|
|
38
|
+
}
|
|
39
|
+
function boundedTerminalList(values, omitted) {
|
|
40
|
+
const suffix = omitted > 0 ? ` · +${omitted} more` : '';
|
|
41
|
+
const maximumBodyLength = Math.max(1, MAX_TERMINAL_VALUE_LENGTH - suffix.length);
|
|
42
|
+
const joined = values.join(', ');
|
|
43
|
+
const body = joined.length > maximumBodyLength
|
|
44
|
+
? `${joined.slice(0, Math.max(0, maximumBodyLength - 1))}…`
|
|
45
|
+
: joined;
|
|
46
|
+
return `${body}${suffix}`;
|
|
47
|
+
}
|
|
48
|
+
function boundedTerminalComponent(value, maximum) {
|
|
49
|
+
const normalized = safeTerminalValue(value);
|
|
50
|
+
return normalized.length > maximum ? `${normalized.slice(0, Math.max(0, maximum - 1))}…` : normalized;
|
|
51
|
+
}
|
|
52
|
+
function boundedTerminalWithSuffix(value, suffix) {
|
|
53
|
+
if (!suffix)
|
|
54
|
+
return safeTerminalValue(value);
|
|
55
|
+
const retainedSuffix = suffix.length >= MAX_TERMINAL_VALUE_LENGTH
|
|
56
|
+
? `${suffix.slice(0, MAX_TERMINAL_VALUE_LENGTH - 1)}…`
|
|
57
|
+
: suffix;
|
|
58
|
+
const maximumValueLength = Math.max(1, MAX_TERMINAL_VALUE_LENGTH - retainedSuffix.length);
|
|
59
|
+
const retainedValue = value.length > maximumValueLength
|
|
60
|
+
? `${value.slice(0, Math.max(0, maximumValueLength - 1))}…`
|
|
61
|
+
: value;
|
|
62
|
+
return `${retainedValue}${retainedSuffix}`;
|
|
20
63
|
}
|
|
21
64
|
function titleCase(value) {
|
|
22
65
|
const text = safeTerminalValue(value, 'unknown').replaceAll('_', ' ');
|
|
@@ -31,14 +74,76 @@ function terminalCount(value) {
|
|
|
31
74
|
const count = Number(value);
|
|
32
75
|
return Number.isSafeInteger(count) && count >= 0 ? Math.min(count, 999) : 0;
|
|
33
76
|
}
|
|
77
|
+
function terminalDisplayCount(value) {
|
|
78
|
+
const count = Number(value);
|
|
79
|
+
return Number.isSafeInteger(count) && count >= 0 ? String(count) : '0';
|
|
80
|
+
}
|
|
34
81
|
function terminalCountSummary(value, labels) {
|
|
35
82
|
const source = terminalRecord(value);
|
|
36
83
|
return labels
|
|
37
|
-
.map(([key, label]) =>
|
|
84
|
+
.map(([key, label]) => {
|
|
85
|
+
const count = Number(source[key]);
|
|
86
|
+
return [label, Number.isSafeInteger(count) && count >= 0 ? count : 0];
|
|
87
|
+
})
|
|
38
88
|
.filter(([, count]) => count > 0)
|
|
39
|
-
.map(([label, count]) => `${label} ${count}`)
|
|
89
|
+
.map(([label, count]) => `${label} ${terminalDisplayCount(count)}`)
|
|
40
90
|
.join(', ');
|
|
41
91
|
}
|
|
92
|
+
function appendPublicationMetadataLines(lines, value, detail) {
|
|
93
|
+
if (detail === 'summary' || !validPagePublicationMetadata(value))
|
|
94
|
+
return;
|
|
95
|
+
const metadata = terminalRecord(value);
|
|
96
|
+
const robots = terminalRecord(metadata.robots);
|
|
97
|
+
const twitter = terminalRecord(metadata.twitterCard);
|
|
98
|
+
const headings = terminalRecord(metadata.headings);
|
|
99
|
+
const images = terminalRecord(metadata.images);
|
|
100
|
+
const blocking = terminalRecord(metadata.renderBlockingCandidates);
|
|
101
|
+
lines.push(`Publication ${metadata.complete === true ? 'Complete' : 'Partial'} · robots ${titleCase(robots.status)} · card ${titleCase(twitter.status)}`);
|
|
102
|
+
const directives = Array.isArray(robots.directives) ? robots.directives.map((item) => safeTerminalValue(item)) : [];
|
|
103
|
+
if (directives.length)
|
|
104
|
+
lines.push(`Robots ${safeTerminalValue(directives.join(', '))}${robots.conflicting === true ? ' · conflicting' : ''}`);
|
|
105
|
+
else if (robots.status === 'not_observed')
|
|
106
|
+
lines.push('Robots No declaration observed in captured static HTML');
|
|
107
|
+
if (twitter.cardType)
|
|
108
|
+
lines.push(`Card type ${safeTerminalValue(twitter.cardType)}`);
|
|
109
|
+
lines.push(`Static page headings ${terminalDisplayCount(headings.total)} · images ${terminalDisplayCount(images.total)} · blocking candidates ${terminalDisplayCount(blocking.total)}`);
|
|
110
|
+
if (detail === 'verbose') {
|
|
111
|
+
lines.push(`Image alt missing ${terminalDisplayCount(images.altMissing)} · empty ${terminalDisplayCount(images.altEmpty)} · non-empty ${terminalDisplayCount(images.altNonEmpty)} · unclassified ${terminalDisplayCount(images.altUnclassified)}`);
|
|
112
|
+
lines.push(`Blocking scripts ${terminalDisplayCount(blocking.script)} · stylesheets ${terminalDisplayCount(blocking.stylesheet)} · static candidates only`);
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
function appendDeliveryMetadataLines(lines, value, verbose = false) {
|
|
116
|
+
if (!validHttpDeliveryMetadata(value))
|
|
117
|
+
return;
|
|
118
|
+
const metadata = terminalRecord(value);
|
|
119
|
+
const encoding = terminalRecord(metadata.contentEncoding);
|
|
120
|
+
const cache = terminalRecord(metadata.cachePolicy);
|
|
121
|
+
const codings = Array.isArray(encoding.codings) ? encoding.codings.map((item) => safeTerminalValue(item)) : [];
|
|
122
|
+
lines.push(`Delivery ${metadata.complete === true ? 'Complete' : 'Partial'} · encoding ${titleCase(encoding.status)} · cache ${titleCase(cache.status)}`);
|
|
123
|
+
if (codings.length)
|
|
124
|
+
lines.push(`Content coding ${safeTerminalValue(codings.join(', '))}`);
|
|
125
|
+
const cacheDirectives = [
|
|
126
|
+
['no-store', cache.noStore], ['no-cache', cache.noCache], ['must-revalidate', cache.mustRevalidate],
|
|
127
|
+
['public', cache.public], ['private', cache.private], ['immutable', cache.immutable],
|
|
128
|
+
].filter(([, present]) => present === true).map(([label]) => label);
|
|
129
|
+
if (cacheDirectives.length)
|
|
130
|
+
lines.push(`Cache policy ${safeTerminalValue(cacheDirectives.join(', '))}`);
|
|
131
|
+
if (verbose) {
|
|
132
|
+
const seconds = [
|
|
133
|
+
cache.maxAgeSeconds === null ? null : `max-age ${terminalDisplayCount(cache.maxAgeSeconds)}s`,
|
|
134
|
+
cache.sMaxAgeSeconds === null ? null : `s-maxage ${terminalDisplayCount(cache.sMaxAgeSeconds)}s`,
|
|
135
|
+
cache.ageSeconds === null ? null : `Age ${terminalDisplayCount(cache.ageSeconds)}s`,
|
|
136
|
+
].filter(Boolean);
|
|
137
|
+
if (seconds.length)
|
|
138
|
+
lines.push(`Cache timing ${safeTerminalValue(seconds.join(' · '))}`);
|
|
139
|
+
const declared = [
|
|
140
|
+
terminalRecord(cache.etag).present === true ? 'ETag' : null,
|
|
141
|
+
terminalRecord(cache.lastModified).present === true ? 'Last-Modified' : null,
|
|
142
|
+
terminalRecord(cache.expires).present === true ? 'Expires' : null,
|
|
143
|
+
].filter(Boolean);
|
|
144
|
+
lines.push(`Validators ${declared.length ? safeTerminalValue(declared.join(', ')) : 'No validator declaration observed'}`);
|
|
145
|
+
}
|
|
146
|
+
}
|
|
42
147
|
function appendSection(lines, label, values) {
|
|
43
148
|
if (!values.length)
|
|
44
149
|
return;
|
|
@@ -46,6 +151,197 @@ function appendSection(lines, label, values) {
|
|
|
46
151
|
lines.push('');
|
|
47
152
|
lines.push(`${label}:`, ...values);
|
|
48
153
|
}
|
|
154
|
+
function formatLookupDnsRecord(value) {
|
|
155
|
+
if (typeof value === 'string' || typeof value === 'number')
|
|
156
|
+
return safeTerminalValue(value);
|
|
157
|
+
const record = terminalRecord(value);
|
|
158
|
+
if (!Object.keys(record).length)
|
|
159
|
+
return null;
|
|
160
|
+
if ('exchange' in record) {
|
|
161
|
+
return safeTerminalValue(`${safeTerminalValue(record.priority, '0')} ${record.exchange ? safeTerminalValue(record.exchange) : '.'}`);
|
|
162
|
+
}
|
|
163
|
+
if ('tag' in record && 'value' in record) {
|
|
164
|
+
return safeTerminalValue(`${record.critical ? 'critical ' : ''}${safeTerminalValue(record.tag)} ${safeTerminalValue(record.value)}`);
|
|
165
|
+
}
|
|
166
|
+
if ('nsname' in record) {
|
|
167
|
+
return safeTerminalValue(`${safeTerminalValue(record.nsname)} · serial ${safeTerminalValue(record.serial, 'unknown')}`);
|
|
168
|
+
}
|
|
169
|
+
if (record.type === 'HTTPS') {
|
|
170
|
+
const parameters = terminalRecord(record.parameters);
|
|
171
|
+
const alpnValues = Array.isArray(parameters.alpn) ? parameters.alpn : [];
|
|
172
|
+
const alpnVisible = alpnValues
|
|
173
|
+
.slice(0, MAX_LOOKUP_TERMINAL_ALPN_IDS)
|
|
174
|
+
.map((item) => boundedTerminalComponent(item, MAX_LOOKUP_TERMINAL_ALPN_ID_LENGTH));
|
|
175
|
+
const alpn = alpnVisible.length
|
|
176
|
+
? boundedTerminalList(alpnVisible, Math.max(0, alpnValues.length - alpnVisible.length))
|
|
177
|
+
: '';
|
|
178
|
+
const suffix = [parameters.port ? `port ${safeTerminalValue(parameters.port)}` : '', alpn ? `ALPN ${alpn}` : ''].filter(Boolean).join(' · ');
|
|
179
|
+
const target = `${safeTerminalValue(record.priority, '0')} ${safeTerminalValue(record.target, '.')}`;
|
|
180
|
+
return boundedTerminalWithSuffix(target, suffix ? ` · ${suffix}` : '');
|
|
181
|
+
}
|
|
182
|
+
return null;
|
|
183
|
+
}
|
|
184
|
+
function appendLookupRecordLine(lines, label, value) {
|
|
185
|
+
if (!Array.isArray(value) || value.length === 0)
|
|
186
|
+
return;
|
|
187
|
+
const visible = value
|
|
188
|
+
.slice(0, MAX_LOOKUP_TERMINAL_RECORDS)
|
|
189
|
+
.map(formatLookupDnsRecord)
|
|
190
|
+
.filter((item) => item !== null);
|
|
191
|
+
if (!visible.length)
|
|
192
|
+
return;
|
|
193
|
+
const omitted = Math.max(0, value.length - visible.length);
|
|
194
|
+
lines.push(`${label.padEnd(14)}${boundedTerminalList(visible, omitted)}`);
|
|
195
|
+
}
|
|
196
|
+
function lookupObservationLabel(value, observed, notObserved) {
|
|
197
|
+
return value === true ? observed : value === false ? notObserved : 'Unavailable';
|
|
198
|
+
}
|
|
199
|
+
function boundedTerminalNames(value) {
|
|
200
|
+
if (typeof value === 'string')
|
|
201
|
+
return safeTerminalValue(value);
|
|
202
|
+
if (!Array.isArray(value))
|
|
203
|
+
return null;
|
|
204
|
+
const names = value
|
|
205
|
+
.filter((item) => typeof item === 'string' && Boolean(item.trim()))
|
|
206
|
+
.slice(0, MAX_LOOKUP_TERMINAL_NAMES)
|
|
207
|
+
.map((item) => safeTerminalValue(item));
|
|
208
|
+
if (!names.length)
|
|
209
|
+
return null;
|
|
210
|
+
return boundedTerminalList(names, Math.max(0, value.length - names.length));
|
|
211
|
+
}
|
|
212
|
+
function boundedTerminalStrings(value, maximum = MAX_LOOKUP_TERMINAL_NAMES) {
|
|
213
|
+
if (!Array.isArray(value))
|
|
214
|
+
return null;
|
|
215
|
+
const inspected = Math.min(value.length, maximum);
|
|
216
|
+
const retained = [];
|
|
217
|
+
let invalid = 0;
|
|
218
|
+
for (let index = 0; index < inspected; index += 1) {
|
|
219
|
+
const item = value[index];
|
|
220
|
+
if (typeof item === 'string' || typeof item === 'number')
|
|
221
|
+
retained.push(safeTerminalValue(item));
|
|
222
|
+
else
|
|
223
|
+
invalid += 1;
|
|
224
|
+
}
|
|
225
|
+
const uninspected = Math.max(0, value.length - inspected);
|
|
226
|
+
const disclosures = [
|
|
227
|
+
invalid > 0 ? `${invalid} malformed retained entr${invalid === 1 ? 'y' : 'ies'} omitted` : null,
|
|
228
|
+
uninspected > 0 ? `+${uninspected} more retained entr${uninspected === 1 ? 'y' : 'ies'}` : null,
|
|
229
|
+
].filter(Boolean).join(' · ');
|
|
230
|
+
if (!retained.length)
|
|
231
|
+
return disclosures || null;
|
|
232
|
+
return boundedTerminalWithSuffix(retained.join(', '), disclosures ? ` · ${disclosures}` : '');
|
|
233
|
+
}
|
|
234
|
+
function sourceCompleteness(value) {
|
|
235
|
+
if (value.complete === true && value.truncated !== true)
|
|
236
|
+
return 'Complete';
|
|
237
|
+
if (value.complete === false || value.truncated === true)
|
|
238
|
+
return value.truncated === true ? 'Incomplete · truncated' : 'Incomplete';
|
|
239
|
+
return null;
|
|
240
|
+
}
|
|
241
|
+
function positiveSourceStatus(value) {
|
|
242
|
+
return value === 'success' || value === 'partial';
|
|
243
|
+
}
|
|
244
|
+
function lifecycleDate(value, field) {
|
|
245
|
+
const lifecycle = terminalRecord(value.lifecycle);
|
|
246
|
+
return lifecycle[`${field}Iso`] ?? lifecycle[field];
|
|
247
|
+
}
|
|
248
|
+
function addressSelectionLabel(value) {
|
|
249
|
+
const labels = {
|
|
250
|
+
tls_connection: 'TLS connection',
|
|
251
|
+
dns_a: 'DNS A',
|
|
252
|
+
dns_aaaa: 'DNS AAAA',
|
|
253
|
+
};
|
|
254
|
+
return labels[String(value)] || titleCase(value);
|
|
255
|
+
}
|
|
256
|
+
function retainedUrlRelationship(value, target) {
|
|
257
|
+
const urlValue = terminalRecord(value);
|
|
258
|
+
if (typeof urlValue.url !== 'string')
|
|
259
|
+
return null;
|
|
260
|
+
try {
|
|
261
|
+
const declared = new URL(urlValue.url);
|
|
262
|
+
const targetValue = String(target || '').trim().replace(/\.$/u, '');
|
|
263
|
+
const targetHost = new URL(`http://${targetValue}`).hostname.toLowerCase();
|
|
264
|
+
const relationship = declared.hostname.toLowerCase() === targetHost
|
|
265
|
+
? 'Same target host'
|
|
266
|
+
: 'Different host declared';
|
|
267
|
+
return [
|
|
268
|
+
relationship,
|
|
269
|
+
urlValue.queryOmitted === true ? 'query omitted' : null,
|
|
270
|
+
urlValue.pathTruncated === true ? 'path shortened' : null,
|
|
271
|
+
].filter(Boolean).join(' · ');
|
|
272
|
+
}
|
|
273
|
+
catch {
|
|
274
|
+
return null;
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
function boundedFindingLabels(value, formatter) {
|
|
278
|
+
if (!Array.isArray(value))
|
|
279
|
+
return null;
|
|
280
|
+
const inspected = Math.min(value.length, MAX_LOOKUP_TERMINAL_FINDINGS);
|
|
281
|
+
const retained = value
|
|
282
|
+
.slice(0, inspected)
|
|
283
|
+
.map(terminalRecord)
|
|
284
|
+
.map(formatter)
|
|
285
|
+
.filter(Boolean);
|
|
286
|
+
if (!retained.length)
|
|
287
|
+
return null;
|
|
288
|
+
return boundedTerminalList(retained, Math.max(0, value.length - inspected));
|
|
289
|
+
}
|
|
290
|
+
function appendNetworkRegistrationLines(lines, parsed, type, detail) {
|
|
291
|
+
if (!Object.keys(parsed).length || detail === 'summary')
|
|
292
|
+
return;
|
|
293
|
+
if (parsed.handle)
|
|
294
|
+
lines.push(`RDAP handle ${safeTerminalValue(parsed.handle)}`);
|
|
295
|
+
if (parsed.name)
|
|
296
|
+
lines.push(`RDAP name ${safeTerminalValue(parsed.name)}`);
|
|
297
|
+
if (type === 'asn') {
|
|
298
|
+
if (parsed.startAutnum !== undefined && parsed.endAutnum !== undefined) {
|
|
299
|
+
const range = parsed.startAutnum === parsed.endAutnum
|
|
300
|
+
? safeTerminalValue(parsed.startAutnum)
|
|
301
|
+
: `${safeTerminalValue(parsed.startAutnum)} to ${safeTerminalValue(parsed.endAutnum)}`;
|
|
302
|
+
lines.push(`ASN range ${range}`);
|
|
303
|
+
}
|
|
304
|
+
else if (parsed.startAutnum !== undefined) {
|
|
305
|
+
lines.push(`ASN start ${safeTerminalValue(parsed.startAutnum)}`);
|
|
306
|
+
}
|
|
307
|
+
else if (parsed.endAutnum !== undefined) {
|
|
308
|
+
lines.push(`ASN end ${safeTerminalValue(parsed.endAutnum)}`);
|
|
309
|
+
}
|
|
310
|
+
if (parsed.autnumType)
|
|
311
|
+
lines.push(`Allocation ${safeTerminalValue(parsed.autnumType)}`);
|
|
312
|
+
}
|
|
313
|
+
else {
|
|
314
|
+
if (parsed.startAddress && parsed.endAddress) {
|
|
315
|
+
lines.push(`Address range ${safeTerminalValue(parsed.startAddress)} to ${safeTerminalValue(parsed.endAddress)}`);
|
|
316
|
+
}
|
|
317
|
+
else if (parsed.startAddress) {
|
|
318
|
+
lines.push(`Address start ${safeTerminalValue(parsed.startAddress)}`);
|
|
319
|
+
}
|
|
320
|
+
else if (parsed.endAddress) {
|
|
321
|
+
lines.push(`Address end ${safeTerminalValue(parsed.endAddress)}`);
|
|
322
|
+
}
|
|
323
|
+
const cidrs = boundedTerminalStrings(parsed.cidrs, MAX_LOOKUP_TERMINAL_CIDRS);
|
|
324
|
+
if (cidrs)
|
|
325
|
+
lines.push(`CIDR prefixes ${cidrs}`);
|
|
326
|
+
if (parsed.networkType)
|
|
327
|
+
lines.push(`Allocation ${safeTerminalValue(parsed.networkType)}`);
|
|
328
|
+
}
|
|
329
|
+
if (parsed.country)
|
|
330
|
+
lines.push(`Country ${safeTerminalValue(parsed.country)}`);
|
|
331
|
+
const statuses = boundedTerminalStrings(parsed.statuses);
|
|
332
|
+
if (statuses)
|
|
333
|
+
lines.push(`Statuses ${statuses}`);
|
|
334
|
+
if (detail === 'verbose') {
|
|
335
|
+
const created = lifecycleDate(parsed, 'createdDate');
|
|
336
|
+
const updated = lifecycleDate(parsed, 'updatedDate');
|
|
337
|
+
if (created)
|
|
338
|
+
lines.push(`Created ${safeTerminalValue(created)}`);
|
|
339
|
+
if (updated)
|
|
340
|
+
lines.push(`Updated ${safeTerminalValue(updated)}`);
|
|
341
|
+
if (parsed.serverTruncated === true || parsed.cidrsTruncated === true)
|
|
342
|
+
lines.push('RDAP detail Source declared or local list truncation');
|
|
343
|
+
}
|
|
344
|
+
}
|
|
49
345
|
function formatTerminalLookup(document, { detail = 'standard' } = {}) {
|
|
50
346
|
const availability = terminalRecord(document.availability);
|
|
51
347
|
const diagnostics = terminalRecord(document.diagnostics);
|
|
@@ -78,6 +374,10 @@ function formatTerminalLookup(document, { detail = 'standard' } = {}) {
|
|
|
78
374
|
registrationLines.push(`Registrar source ${safeTerminalValue(registrarRdap.endpoint)}`);
|
|
79
375
|
}
|
|
80
376
|
}
|
|
377
|
+
if (['ipv4', 'ipv6', 'asn'].includes(String(document.type)) && positiveSourceStatus(rdapDiagnostics.status)) {
|
|
378
|
+
const rdap = terminalRecord(document.rdap);
|
|
379
|
+
appendNetworkRegistrationLines(registrationLines, terminalRecord(rdap.parsed), document.type, detail);
|
|
380
|
+
}
|
|
81
381
|
const registryInsights = terminalRecord(document.registryInsights);
|
|
82
382
|
if (detail !== 'summary' && registryInsights.version === 1) {
|
|
83
383
|
const lifecycle = terminalRecord(registryInsights.lifecycle);
|
|
@@ -98,24 +398,161 @@ function formatTerminalLookup(document, { detail = 'standard' } = {}) {
|
|
|
98
398
|
registrationLines.push(`Reconciliation ${titleCase(reconciliation.state)}`);
|
|
99
399
|
registrationLines.push(`Publications ${publicationCounts.complete} complete · ${publicationCounts.partial} partial · ${publicationCounts.unavailable} unavailable`);
|
|
100
400
|
}
|
|
101
|
-
const
|
|
401
|
+
const dnsLines = [];
|
|
402
|
+
const mailLines = [];
|
|
403
|
+
const tlsLines = [];
|
|
102
404
|
if (document.mode === 'deep' && document.type === 'domain') {
|
|
103
405
|
const dns = terminalRecord(availability.dns);
|
|
406
|
+
const dnsRecords = terminalRecord(dns.records);
|
|
407
|
+
const dnsDiagnostics = terminalRecord(dns.diagnostics);
|
|
408
|
+
if (Object.keys(dns).length) {
|
|
409
|
+
dnsLines.push(`Evidence ${titleCase(dns.status)}`);
|
|
410
|
+
dnsLines.push(`Completeness ${dns.complete === true ? 'Complete' : 'Incomplete'}`);
|
|
411
|
+
const recordTypes = [
|
|
412
|
+
['a', 'A'], ['aaaa', 'AAAA'], ['cname', 'CNAME'], ['ns', 'NS'],
|
|
413
|
+
['mx', 'MX'], ['spf', 'SPF'], ['dmarc', 'DMARC'], ['caa', 'CAA'],
|
|
414
|
+
['soa', 'SOA'], ['https', 'HTTPS'],
|
|
415
|
+
];
|
|
416
|
+
const retainedCounts = recordTypes
|
|
417
|
+
.map(([key, label]) => [label, Array.isArray(dnsRecords[key]) ? dnsRecords[key].length : 0])
|
|
418
|
+
.filter(([, count]) => count > 0)
|
|
419
|
+
.map(([label, count]) => `${label} ${count}`);
|
|
420
|
+
dnsLines.push(`Retained ${retainedCounts.length ? safeTerminalValue(retainedCounts.join(' · ')) : 'No records retained'}`);
|
|
421
|
+
if (detail === 'verbose') {
|
|
422
|
+
const unsettled = recordTypes.flatMap(([key, label]) => {
|
|
423
|
+
const diagnostic = terminalRecord(dnsDiagnostics[key]);
|
|
424
|
+
return diagnostic.status && !['success', 'not_found'].includes(String(diagnostic.status))
|
|
425
|
+
? [`${label} ${titleCase(diagnostic.status)}`]
|
|
426
|
+
: [];
|
|
427
|
+
});
|
|
428
|
+
if (unsettled.length)
|
|
429
|
+
dnsLines.push(`Query states ${safeTerminalValue(unsettled.join(' · '))}`);
|
|
430
|
+
}
|
|
431
|
+
if (detail !== 'summary') {
|
|
432
|
+
for (const [key, label] of recordTypes)
|
|
433
|
+
appendLookupRecordLine(dnsLines, label, dnsRecords[key]);
|
|
434
|
+
}
|
|
435
|
+
mailLines.push(`Evidence ${titleCase(dns.status)}`);
|
|
436
|
+
mailLines.push(`Mail exchange ${lookupObservationLabel(dns.hasNullMx === true ? true : dns.hasMx, dns.hasNullMx === true ? 'Null MX observed' : 'MX observed', 'No MX observed')}`);
|
|
437
|
+
mailLines.push(`SPF ${lookupObservationLabel(dns.hasSpf, 'Record observed', 'No record observed')}`);
|
|
438
|
+
mailLines.push(`DMARC ${lookupObservationLabel(dns.hasDmarc, 'Record observed', 'No record observed')}`);
|
|
439
|
+
if (detail !== 'summary') {
|
|
440
|
+
appendLookupRecordLine(mailLines, 'MX hosts', Array.isArray(dns.mxHosts) ? dns.mxHosts : []);
|
|
441
|
+
appendLookupRecordLine(mailLines, 'SPF policy', dnsRecords.spf);
|
|
442
|
+
appendLookupRecordLine(mailLines, 'DMARC policy', dnsRecords.dmarc);
|
|
443
|
+
}
|
|
444
|
+
}
|
|
445
|
+
const tls = terminalRecord(availability.tls);
|
|
446
|
+
if (Object.keys(tls).length) {
|
|
447
|
+
const usableTls = positiveSourceStatus(tls.status);
|
|
448
|
+
const certificate = usableTls ? terminalRecord(tls.certificate) : {};
|
|
449
|
+
const subject = terminalRecord(certificate.subject);
|
|
450
|
+
const issuer = terminalRecord(certificate.issuer);
|
|
451
|
+
const authorization = usableTls ? terminalRecord(tls.authorization) : {};
|
|
452
|
+
const hostname = usableTls ? terminalRecord(tls.hostname) : {};
|
|
453
|
+
const validity = usableTls ? terminalRecord(tls.validity) : {};
|
|
454
|
+
const cipher = usableTls ? terminalRecord(tls.cipher) : {};
|
|
455
|
+
tlsLines.push(`Evidence ${titleCase(tls.status)}`);
|
|
456
|
+
const completeness = sourceCompleteness(tls);
|
|
457
|
+
if (completeness)
|
|
458
|
+
tlsLines.push(`Completeness ${completeness}`);
|
|
459
|
+
if (usableTls && tls.connectedAddress && detail !== 'summary')
|
|
460
|
+
tlsLines.push(`Address ${safeTerminalValue(tls.connectedAddress)}`);
|
|
461
|
+
if (usableTls && tls.protocol)
|
|
462
|
+
tlsLines.push(`Protocol ${safeTerminalValue(tls.protocol)}`);
|
|
463
|
+
if (usableTls && tls.alpnProtocol && detail !== 'summary')
|
|
464
|
+
tlsLines.push(`ALPN ${safeTerminalValue(tls.alpnProtocol)}`);
|
|
465
|
+
if (cipher.standardName || cipher.name)
|
|
466
|
+
tlsLines.push(`Cipher ${safeTerminalValue(cipher.standardName || cipher.name)}`);
|
|
467
|
+
tlsLines.push(`Chain trust ${authorization.authorized === true ? 'Authorised' : authorization.authorized === false ? 'Not authorised' : 'Unavailable'}`);
|
|
468
|
+
tlsLines.push(`Hostname ${hostname.matches === true ? 'Matched' : hostname.matches === false ? 'Not matched' : 'Unavailable'}`);
|
|
469
|
+
tlsLines.push(`Validity ${validity.status ? titleCase(validity.status) : 'Unavailable'}`);
|
|
470
|
+
const subjectName = boundedTerminalNames(subject.commonNames ?? subject.CN);
|
|
471
|
+
const issuerName = boundedTerminalNames(issuer.commonNames ?? issuer.CN);
|
|
472
|
+
if (detail !== 'summary' && subjectName)
|
|
473
|
+
tlsLines.push(`Subject ${subjectName}`);
|
|
474
|
+
if (detail !== 'summary' && issuerName)
|
|
475
|
+
tlsLines.push(`Issuer ${issuerName}`);
|
|
476
|
+
if (detail !== 'summary' && certificate.validFrom)
|
|
477
|
+
tlsLines.push(`Valid from ${safeTerminalValue(certificate.validFrom)}`);
|
|
478
|
+
if (detail !== 'summary' && certificate.validTo)
|
|
479
|
+
tlsLines.push(`Valid until ${safeTerminalValue(certificate.validTo)}`);
|
|
480
|
+
const publicKey = terminalRecord(certificate.publicKey);
|
|
481
|
+
if (detail !== 'summary' && (publicKey.type || publicKey.bits || publicKey.curve)) {
|
|
482
|
+
tlsLines.push(`Public key ${safeTerminalValue([publicKey.type, publicKey.bits ? `${publicKey.bits} bits` : null, publicKey.curve].filter(Boolean).join(' '))}`);
|
|
483
|
+
}
|
|
484
|
+
const signature = terminalRecord(certificate.signature);
|
|
485
|
+
if (detail !== 'summary' && (signature.algorithm || signature.oid)) {
|
|
486
|
+
tlsLines.push(`Signature ${safeTerminalValue(signature.algorithm || signature.oid)}`);
|
|
487
|
+
}
|
|
488
|
+
const altNames = terminalRecord(certificate.subjectAltNames);
|
|
489
|
+
const dnsAltNames = Array.isArray(altNames.dnsNames) ? altNames.dnsNames : [];
|
|
490
|
+
const ipAltNames = Array.isArray(altNames.ipAddresses) ? altNames.ipAddresses : [];
|
|
491
|
+
const sanClasses = terminalCountSummary(altNames.classes, [
|
|
492
|
+
['dns', 'DNS'], ['ip', 'IP'], ['email', 'email'], ['uri', 'URI'],
|
|
493
|
+
['directoryName', 'directory name'], ['registeredId', 'registered ID'],
|
|
494
|
+
['otherName', 'other name'], ['unclassified', 'other'],
|
|
495
|
+
]);
|
|
496
|
+
if (detail !== 'summary' && (dnsAltNames.length || ipAltNames.length || sanClasses)) {
|
|
497
|
+
tlsLines.push(`SAN summary ${sanClasses || `DNS ${dnsAltNames.length} · IP ${ipAltNames.length}`}${altNames.truncated ? ' · truncated' : ''}`);
|
|
498
|
+
}
|
|
499
|
+
const chainCount = Array.isArray(tls.chain) ? tls.chain.length : 0;
|
|
500
|
+
if (detail !== 'summary' && (chainCount || tls.chainTruncated === true)) {
|
|
501
|
+
tlsLines.push(`Chain ${chainCount} retained certificate${chainCount === 1 ? '' : 's'}${tls.chainTruncated ? ' · truncated' : ''}`);
|
|
502
|
+
}
|
|
503
|
+
const aia = terminalRecord(certificate.authorityInformationAccess);
|
|
504
|
+
if (detail !== 'summary' && Object.keys(aia).length) {
|
|
505
|
+
const ocsp = terminalRecord(aia.ocsp);
|
|
506
|
+
const caIssuers = terminalRecord(aia.caIssuers);
|
|
507
|
+
tlsLines.push(`AIA presence OCSP ${terminalCount(ocsp.total)} · CA issuers ${terminalCount(caIssuers.total)} · `
|
|
508
|
+
+ `unknown methods ${terminalCount(aia.unknownMethods)}${aia.truncated ? ' · truncated' : ''}`);
|
|
509
|
+
}
|
|
510
|
+
if (detail === 'verbose') {
|
|
511
|
+
const visibleAltNames = [...dnsAltNames, ...ipAltNames]
|
|
512
|
+
.slice(0, MAX_TLS_TERMINAL_ALT_NAMES)
|
|
513
|
+
.map((value) => safeTerminalValue(value));
|
|
514
|
+
if (visibleAltNames.length)
|
|
515
|
+
tlsLines.push(`Alt names ${boundedTerminalList(visibleAltNames, Math.max(0, dnsAltNames.length + ipAltNames.length - visibleAltNames.length))}`);
|
|
516
|
+
const purposes = terminalRecord(certificate.extendedKeyUsage);
|
|
517
|
+
const purposeValues = Array.isArray(purposes.values) ? purposes.values : [];
|
|
518
|
+
const visiblePurposes = purposeValues.slice(0, MAX_TLS_TERMINAL_PURPOSES).map((value) => {
|
|
519
|
+
const purpose = terminalRecord(value);
|
|
520
|
+
return safeTerminalValue(purpose.name || purpose.oid || 'Unrecognized purpose');
|
|
521
|
+
});
|
|
522
|
+
if (visiblePurposes.length)
|
|
523
|
+
tlsLines.push(`Purposes ${boundedTerminalList(visiblePurposes, Math.max(0, purposeValues.length - visiblePurposes.length))}${purposes.truncated ? ' · truncated' : ''}`);
|
|
524
|
+
const findings = boundedFindingLabels(tls.findings, (finding) => safeTerminalValue(finding.label, 'Unlabelled finding'));
|
|
525
|
+
if (findings)
|
|
526
|
+
tlsLines.push(`Findings ${findings}`);
|
|
527
|
+
}
|
|
528
|
+
if (detail === 'verbose' && certificate.fingerprintSha256)
|
|
529
|
+
tlsLines.push(`SHA-256 ${safeTerminalValue(certificate.fingerprintSha256)}`);
|
|
530
|
+
if (detail === 'verbose' && Array.isArray(tls.limitations)) {
|
|
531
|
+
for (const limitation of tls.limitations.slice(0, MAX_LOOKUP_TERMINAL_LIMITATIONS))
|
|
532
|
+
tlsLines.push(`Limitation ${safeTerminalValue(limitation)}`);
|
|
533
|
+
const omittedLimitations = Math.max(0, tls.limitations.length - MAX_LOOKUP_TERMINAL_LIMITATIONS);
|
|
534
|
+
if (omittedLimitations)
|
|
535
|
+
tlsLines.push(`Limitation +${omittedLimitations} more retained limitation${omittedLimitations === 1 ? '' : 's'}`);
|
|
536
|
+
}
|
|
537
|
+
}
|
|
538
|
+
}
|
|
539
|
+
const websiteLines = [];
|
|
540
|
+
if (document.mode === 'deep' && document.type === 'domain') {
|
|
104
541
|
const http = terminalRecord(availability.http);
|
|
105
542
|
const httpResponse = terminalRecord(http.response);
|
|
106
|
-
const tls = terminalRecord(availability.tls);
|
|
107
543
|
const credentialSurface = terminalRecord(availability.credentialSurfaceProfile);
|
|
108
544
|
const structuredIdentity = terminalRecord(availability.structuredDataIdentity);
|
|
109
545
|
const technology = terminalRecord(availability.technologyProfile);
|
|
110
546
|
const browserLibraries = terminalRecord(technology.browserLibraryProfile);
|
|
111
547
|
const posture = terminalRecord(availability.securityPosture);
|
|
112
548
|
const postureSummary = terminalRecord(posture.summary);
|
|
549
|
+
const pageIdentity = terminalRecord(availability.pageIdentity);
|
|
550
|
+
const pageRole = terminalRecord(availability.pageRoleProfile);
|
|
551
|
+
const clientBehavior = terminalRecord(availability.clientBehaviorProfile);
|
|
113
552
|
if (availability.activityStatus)
|
|
114
553
|
websiteLines.push(`Web activity ${titleCase(availability.activityStatus)}`);
|
|
115
554
|
if (detail !== 'summary' && availability.pageTitle)
|
|
116
555
|
websiteLines.push(`Page title ${safeTerminalValue(availability.pageTitle)}`);
|
|
117
|
-
if (dns.status)
|
|
118
|
-
websiteLines.push(`DNS evidence ${titleCase(dns.status)}`);
|
|
119
556
|
if (http.status) {
|
|
120
557
|
websiteLines.push(`HTTP evidence ${titleCase(http.status)}`);
|
|
121
558
|
const responseDetail = [
|
|
@@ -124,11 +561,49 @@ function formatTerminalLookup(document, { detail = 'standard' } = {}) {
|
|
|
124
561
|
].filter(Boolean).join(' · ');
|
|
125
562
|
if (detail !== 'summary' && responseDetail)
|
|
126
563
|
websiteLines.push(`HTTP response ${responseDetail}`);
|
|
564
|
+
if (detail !== 'summary')
|
|
565
|
+
appendDeliveryMetadataLines(websiteLines, httpResponse.deliveryMetadata, detail === 'verbose');
|
|
127
566
|
}
|
|
128
|
-
if (
|
|
129
|
-
websiteLines.push(`
|
|
130
|
-
|
|
131
|
-
|
|
567
|
+
if (pageIdentity.status || pageIdentity.source === 'html') {
|
|
568
|
+
websiteLines.push(`Page identity ${titleCase(pageIdentity.status)}`);
|
|
569
|
+
const completeness = sourceCompleteness(pageIdentity);
|
|
570
|
+
if (completeness)
|
|
571
|
+
websiteLines.push(`Page coverage ${completeness}`);
|
|
572
|
+
if (positiveSourceStatus(pageIdentity.status) && detail !== 'summary') {
|
|
573
|
+
if (pageIdentity.documentLanguage)
|
|
574
|
+
websiteLines.push(`Language ${safeTerminalValue(pageIdentity.documentLanguage)}`);
|
|
575
|
+
const canonical = retainedUrlRelationship(pageIdentity.canonical, document.registrableDomain || availability.domain || document.query);
|
|
576
|
+
if (canonical)
|
|
577
|
+
websiteLines.push(`Canonical ${canonical}`);
|
|
578
|
+
const openGraph = terminalRecord(pageIdentity.openGraph);
|
|
579
|
+
if (openGraph.title || openGraph.siteName) {
|
|
580
|
+
websiteLines.push(`Open Graph ${safeTerminalValue([openGraph.siteName, openGraph.title].filter(Boolean).join(' · '))}`);
|
|
581
|
+
}
|
|
582
|
+
if (pageIdentity.generator)
|
|
583
|
+
websiteLines.push(`Generator ${safeTerminalValue(pageIdentity.generator)}`);
|
|
584
|
+
const forms = terminalRecord(pageIdentity.forms);
|
|
585
|
+
if (Object.keys(forms).length)
|
|
586
|
+
websiteLines.push(`Page forms ${terminalDisplayCount(forms.count)} total · ${terminalDisplayCount(forms.postCount)} POST · ${terminalDisplayCount(forms.insecureActionCount)} insecure action`);
|
|
587
|
+
const resources = terminalRecord(pageIdentity.resources);
|
|
588
|
+
const resourceTypes = terminalCountSummary(resources.byType, [
|
|
589
|
+
['image', 'image'], ['script', 'script'], ['stylesheet', 'stylesheet'], ['link', 'link'],
|
|
590
|
+
['frame', 'frame'], ['media', 'media'], ['object', 'object'],
|
|
591
|
+
]);
|
|
592
|
+
if (Object.keys(resources).length)
|
|
593
|
+
websiteLines.push(`Resources ${terminalDisplayCount(resources.count)} retained${resourceTypes ? ` · ${resourceTypes}` : ''}${resources.truncated ? ' · truncated' : ''}`);
|
|
594
|
+
const downloads = terminalRecord(pageIdentity.downloads);
|
|
595
|
+
if (Object.keys(downloads).length)
|
|
596
|
+
websiteLines.push(`Downloads ${terminalDisplayCount(downloads.count)} retained · ${terminalDisplayCount(downloads.riskyCount)} review file${Number(downloads.riskyCount) === 1 ? '' : 's'}${downloads.truncated ? ' · truncated' : ''}`);
|
|
597
|
+
if ([pageIdentity.embeddedOrigins, pageIdentity.contactDomains, pageIdentity.trackingIdentifiers].some(Array.isArray)) {
|
|
598
|
+
websiteLines.push(`Relationships ${Array.isArray(pageIdentity.embeddedOrigins) ? pageIdentity.embeddedOrigins.length : 0} embedded retained · `
|
|
599
|
+
+ `${Array.isArray(pageIdentity.contactDomains) ? pageIdentity.contactDomains.length : 0} contact domain${Array.isArray(pageIdentity.contactDomains) && pageIdentity.contactDomains.length === 1 ? '' : 's'} retained · `
|
|
600
|
+
+ `${Array.isArray(pageIdentity.trackingIdentifiers) ? pageIdentity.trackingIdentifiers.length : 0} tracking identifier${Array.isArray(pageIdentity.trackingIdentifiers) && pageIdentity.trackingIdentifiers.length === 1 ? '' : 's'} retained`);
|
|
601
|
+
}
|
|
602
|
+
appendPublicationMetadataLines(websiteLines, pageIdentity.publicationMetadata, detail);
|
|
603
|
+
}
|
|
604
|
+
}
|
|
605
|
+
if (availability.phishingLanguageMatch && detail !== 'summary') {
|
|
606
|
+
websiteLines.push(`Content cue ${safeTerminalValue(availability.phishingLanguageMatch)} · static review label`);
|
|
132
607
|
}
|
|
133
608
|
if (credentialSurface.status || credentialSurface.source === 'html') {
|
|
134
609
|
const forms = terminalRecord(credentialSurface.forms);
|
|
@@ -183,35 +658,117 @@ function formatTerminalLookup(document, { detail = 'standard' } = {}) {
|
|
|
183
658
|
}
|
|
184
659
|
if (posture.status || posture.source === 'derived') {
|
|
185
660
|
websiteLines.push(`Posture ${titleCase(posture.status)}`);
|
|
186
|
-
if (detail !== 'summary')
|
|
661
|
+
if (detail !== 'summary' && Object.keys(postureSummary).length)
|
|
187
662
|
websiteLines.push(`Posture counts ${terminalCount(postureSummary.observed)} observed · `
|
|
188
663
|
+ `${terminalCount(postureSummary.potentialExposure)} potential exposure · `
|
|
189
664
|
+ `${terminalCount(postureSummary.observedAbsence)} observed absence · `
|
|
190
665
|
+ `${terminalCount(postureSummary.unavailable)} unavailable`);
|
|
666
|
+
if (detail === 'verbose' && positiveSourceStatus(posture.status)) {
|
|
667
|
+
const findings = boundedFindingLabels(posture.findings, (finding) => {
|
|
668
|
+
const state = finding.state ? ` (${titleCase(finding.state)})` : '';
|
|
669
|
+
return `${safeTerminalValue(finding.label, 'Unlabelled finding')}${state}`;
|
|
670
|
+
});
|
|
671
|
+
if (findings)
|
|
672
|
+
websiteLines.push(`Posture labels ${findings}`);
|
|
673
|
+
}
|
|
674
|
+
}
|
|
675
|
+
if (pageRole.status || pageRole.source === 'derived') {
|
|
676
|
+
websiteLines.push(`Page role ${titleCase(pageRole.status)}`);
|
|
677
|
+
const roleFindings = Array.isArray(pageRole.findings) ? pageRole.findings : [];
|
|
678
|
+
const primary = roleFindings.map(terminalRecord).find((finding) => finding.role === pageRole.primaryRole)
|
|
679
|
+
|| terminalRecord(roleFindings[0]);
|
|
680
|
+
if (positiveSourceStatus(pageRole.status) && detail !== 'summary' && Object.keys(primary).length) {
|
|
681
|
+
websiteLines.push(`Primary role ${safeTerminalValue(primary.label, 'Unclassified')} · ${titleCase(primary.confidence)}`);
|
|
682
|
+
}
|
|
683
|
+
if (positiveSourceStatus(pageRole.status) && detail === 'verbose') {
|
|
684
|
+
const findings = boundedFindingLabels(roleFindings, (finding) => `${safeTerminalValue(finding.label, 'Unclassified')} (${titleCase(finding.confidence)})`);
|
|
685
|
+
if (findings)
|
|
686
|
+
websiteLines.push(`Role labels ${findings}`);
|
|
687
|
+
}
|
|
688
|
+
}
|
|
689
|
+
if (clientBehavior.status || clientBehavior.source === 'derived') {
|
|
690
|
+
websiteLines.push(`Client signals ${titleCase(clientBehavior.status)}`);
|
|
691
|
+
const scriptSummary = terminalRecord(clientBehavior.scriptSummary);
|
|
692
|
+
if (positiveSourceStatus(clientBehavior.status) && detail !== 'summary' && Object.keys(scriptSummary).length)
|
|
693
|
+
websiteLines.push(`Scripts ${terminalCount(scriptSummary.elementsObserved)} elements · ${terminalCount(scriptSummary.referencedScripts)} referenced · `
|
|
694
|
+
+ `${terminalCount(scriptSummary.inlineScripts)} inline · ${terminalCount(scriptSummary.moduleScripts)} modules`);
|
|
695
|
+
if (positiveSourceStatus(clientBehavior.status) && detail === 'verbose') {
|
|
696
|
+
const indicators = boundedFindingLabels(clientBehavior.indicators, (indicator) => {
|
|
697
|
+
const occurrences = terminalCount(indicator.occurrences);
|
|
698
|
+
return `${safeTerminalValue(indicator.label, 'Static indicator')} (${titleCase(indicator.evidenceClass)}, ${occurrences})`;
|
|
699
|
+
});
|
|
700
|
+
if (indicators)
|
|
701
|
+
websiteLines.push(`Client labels ${indicators}`);
|
|
702
|
+
}
|
|
191
703
|
}
|
|
192
704
|
}
|
|
193
705
|
const networkLines = [];
|
|
194
706
|
if (document.mode === 'deep' && (document.type === 'ipv4' || document.type === 'ipv6')) {
|
|
195
707
|
const reverseDns = terminalRecord(document.reverseDns);
|
|
196
708
|
const reverseDnsRecords = terminalRecord(reverseDns.records);
|
|
197
|
-
const ptrNames =
|
|
198
|
-
? reverseDnsRecords.ptr.slice(0, 5).map((value) => safeTerminalValue(value))
|
|
199
|
-
: [];
|
|
709
|
+
const ptrNames = boundedTerminalStrings(reverseDnsRecords.ptr);
|
|
200
710
|
if (reverseDns.status)
|
|
201
711
|
networkLines.push(`Reverse DNS ${titleCase(reverseDns.status)}`);
|
|
202
|
-
|
|
203
|
-
|
|
712
|
+
const completeness = sourceCompleteness(reverseDns);
|
|
713
|
+
if (completeness)
|
|
714
|
+
networkLines.push(`PTR coverage ${completeness}`);
|
|
715
|
+
if (detail !== 'summary' && ptrNames)
|
|
716
|
+
networkLines.push(`PTR names ${ptrNames}`);
|
|
717
|
+
if (detail !== 'summary' && !ptrNames && reverseDns.status)
|
|
718
|
+
networkLines.push('PTR names No names retained; source state remains inconclusive');
|
|
204
719
|
}
|
|
205
720
|
const network = terminalRecord(document.networkContext);
|
|
206
721
|
if (network.contextVersion === 1) {
|
|
207
722
|
const endpoint = terminalRecord(network.endpoint);
|
|
208
723
|
const networkRecord = terminalRecord(network.network);
|
|
209
724
|
networkLines.push(`Network RDAP ${titleCase(network.status)}`);
|
|
725
|
+
const completeness = sourceCompleteness(network);
|
|
726
|
+
if (completeness)
|
|
727
|
+
networkLines.push(`Completeness ${completeness}`);
|
|
210
728
|
if (detail !== 'summary' && endpoint.address)
|
|
211
729
|
networkLines.push(`Selected IP ${safeTerminalValue(endpoint.address)}`);
|
|
212
|
-
if (
|
|
730
|
+
if (detail !== 'summary' && endpoint.selectedFrom)
|
|
731
|
+
networkLines.push(`Selected from ${addressSelectionLabel(endpoint.selectedFrom)}`);
|
|
732
|
+
if (positiveSourceStatus(network.status) && (networkRecord.name || networkRecord.holder)) {
|
|
213
733
|
networkLines.push(`Network ${safeTerminalValue(networkRecord.name || networkRecord.holder)}`);
|
|
214
734
|
}
|
|
735
|
+
if (positiveSourceStatus(network.status) && detail !== 'summary') {
|
|
736
|
+
if (networkRecord.handle)
|
|
737
|
+
networkLines.push(`Network handle ${safeTerminalValue(networkRecord.handle)}`);
|
|
738
|
+
if (networkRecord.holder && networkRecord.holder !== networkRecord.name)
|
|
739
|
+
networkLines.push(`Holder ${safeTerminalValue(networkRecord.holder)}`);
|
|
740
|
+
const cidrs = boundedTerminalStrings(networkRecord.cidrs, MAX_LOOKUP_TERMINAL_CIDRS);
|
|
741
|
+
if (cidrs)
|
|
742
|
+
networkLines.push(`CIDR prefixes ${cidrs}`);
|
|
743
|
+
if (networkRecord.startAddress || networkRecord.endAddress)
|
|
744
|
+
networkLines.push(`Address range ${safeTerminalValue(networkRecord.startAddress)} to ${safeTerminalValue(networkRecord.endAddress)}`);
|
|
745
|
+
if (networkRecord.country)
|
|
746
|
+
networkLines.push(`Country ${safeTerminalValue(networkRecord.country)}`);
|
|
747
|
+
if (networkRecord.networkType)
|
|
748
|
+
networkLines.push(`Allocation ${safeTerminalValue(networkRecord.networkType)}`);
|
|
749
|
+
const allAbuseRoutes = Array.isArray(network.abuseRouting) ? network.abuseRouting : [];
|
|
750
|
+
const inspectedRoutes = Math.min(allAbuseRoutes.length, MAX_LOOKUP_TERMINAL_ABUSE_ROUTES);
|
|
751
|
+
const abuseRoutes = allAbuseRoutes.slice(0, inspectedRoutes).map(terminalRecord);
|
|
752
|
+
if (abuseRoutes.length) {
|
|
753
|
+
const channelCounts = new Map();
|
|
754
|
+
for (const route of abuseRoutes) {
|
|
755
|
+
const channel = ['email', 'phone'].includes(String(route.channel)) ? String(route.channel) : 'other';
|
|
756
|
+
channelCounts.set(channel, (channelCounts.get(channel) || 0) + 1);
|
|
757
|
+
}
|
|
758
|
+
const summary = [...channelCounts].map(([channel, count]) => `${channel} ${count}`).join(' · ');
|
|
759
|
+
const omittedRoutes = Math.max(0, allAbuseRoutes.length - inspectedRoutes);
|
|
760
|
+
networkLines.push(`Published routes ${abuseRoutes.length} retained${omittedRoutes ? ` · +${omittedRoutes} more entries` : ''} (${summary}); values omitted`);
|
|
761
|
+
}
|
|
762
|
+
if (detail === 'verbose' && networkRecord.databaseUpdatedAt)
|
|
763
|
+
networkLines.push(`Registry date ${safeTerminalValue(networkRecord.databaseUpdatedAt)}`);
|
|
764
|
+
if (detail === 'verbose' && Array.isArray(network.limitations)) {
|
|
765
|
+
for (const limitation of network.limitations.slice(0, MAX_LOOKUP_TERMINAL_LIMITATIONS))
|
|
766
|
+
networkLines.push(`Limitation ${safeTerminalValue(limitation)}`);
|
|
767
|
+
const omitted = Math.max(0, network.limitations.length - MAX_LOOKUP_TERMINAL_LIMITATIONS);
|
|
768
|
+
if (omitted)
|
|
769
|
+
networkLines.push(`Limitation +${omitted} more retained limitation${omitted === 1 ? '' : 's'}`);
|
|
770
|
+
}
|
|
771
|
+
}
|
|
215
772
|
}
|
|
216
773
|
const sourceHealthLines = [];
|
|
217
774
|
const registryAccess = terminalRecord(diagnostics.registryAccess);
|
|
@@ -236,7 +793,10 @@ function formatTerminalLookup(document, { detail = 'standard' } = {}) {
|
|
|
236
793
|
const lines = [];
|
|
237
794
|
appendSection(lines, 'Target', targetLines);
|
|
238
795
|
appendSection(lines, 'Registration', registrationLines);
|
|
796
|
+
appendSection(lines, 'DNS', dnsLines);
|
|
797
|
+
appendSection(lines, 'Mail', mailLines);
|
|
239
798
|
appendSection(lines, 'Website and security', websiteLines);
|
|
799
|
+
appendSection(lines, 'TLS and certificate', tlsLines);
|
|
240
800
|
appendSection(lines, 'Network', networkLines);
|
|
241
801
|
appendSection(lines, 'Source health', sourceHealthLines);
|
|
242
802
|
appendSection(lines, 'Collection', collectionLines);
|
|
@@ -446,6 +1006,7 @@ function formatTerminalHttp(document) {
|
|
|
446
1006
|
];
|
|
447
1007
|
if (document.detail)
|
|
448
1008
|
lines.push(`Detail ${safeTerminalValue(document.detail)}`);
|
|
1009
|
+
appendDeliveryMetadataLines(lines, response.deliveryMetadata, true);
|
|
449
1010
|
const bodyHash = terminalRecord(response.bodyHash);
|
|
450
1011
|
if (bodyHash.value) {
|
|
451
1012
|
lines.push(`Body hash ${safeTerminalValue(`${bodyHash.algorithm}:${bodyHash.value} (${bodyHash.scope})`)}`);
|
|
@@ -668,4 +1229,4 @@ function formatTerminalRiskCalibration(document) {
|
|
|
668
1229
|
lines.push('', safeTerminalValue(terminalRecord(document.interpretation).statement));
|
|
669
1230
|
return `${lines.join('\n')}\n`;
|
|
670
1231
|
}
|
|
671
|
-
export { MAX_CT_TERMINAL_HOSTNAMES, MAX_CT_TERMINAL_MATCHES, MAX_DISCOVER_TERMINAL_CANDIDATES, MAX_POSTURE_TERMINAL_RECORDS, MAX_TLS_TERMINAL_ALT_NAMES, MAX_TLS_TERMINAL_PURPOSES, MAX_RISK_CALIBRATION_TERMINAL_RECORDS, MAX_TERMINAL_VALUE_LENGTH, formatTerminalBulk, formatTerminalCompare, formatTerminalCtSearch, formatTerminalDiscover, formatTerminalHttp, formatTerminalLookup, formatTerminalPosture, formatTerminalRegistrySupport, formatTerminalRiskCalibration, formatTerminalTls, safeTerminalValue, };
|
|
1232
|
+
export { MAX_CT_TERMINAL_HOSTNAMES, MAX_CT_TERMINAL_MATCHES, MAX_DISCOVER_TERMINAL_CANDIDATES, MAX_LOOKUP_TERMINAL_ALPN_IDS, MAX_LOOKUP_TERMINAL_ALPN_ID_LENGTH, MAX_LOOKUP_TERMINAL_LIMITATIONS, MAX_LOOKUP_TERMINAL_NAMES, MAX_LOOKUP_TERMINAL_RECORDS, MAX_POSTURE_TERMINAL_RECORDS, MAX_TLS_TERMINAL_ALT_NAMES, MAX_TLS_TERMINAL_PURPOSES, MAX_RISK_CALIBRATION_TERMINAL_RECORDS, MAX_TERMINAL_VALUE_LENGTH, formatTerminalBulk, formatTerminalCompare, formatTerminalCtSearch, formatTerminalDiscover, formatTerminalHttp, formatTerminalLookup, formatTerminalPosture, formatTerminalRegistrySupport, formatTerminalRiskCalibration, formatTerminalTls, safeTerminalValue, };
|