@slicedearth/whoisleuth-cli 2.6.0 → 2.7.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/DISCLOSURE +8 -5
- package/NOTICE +3 -2
- package/README.md +60 -67
- package/cli/artifact-validation/case-response.mjs +6 -857
- package/cli/artifact-validation/lookup-evidence.mjs +15 -2
- package/cli/artifact-verify.mjs +32 -1
- package/cli/case-command.mjs +17 -17
- package/cli/case-pack.mjs +7 -1
- package/cli/ci-report.mjs +17 -5
- package/cli/command-definition.mjs +5 -5
- package/cli/doctor.mjs +37 -3
- package/cli/formatters/html.mjs +3 -0
- package/cli/formatters/json.mjs +8 -1
- package/cli/formatters/terminal-lookup.mjs +24 -1
- package/cli/history-command-definitions.mjs +1 -1
- package/cli/investigation-plan.mjs +15 -0
- package/cli/lookup-brief.mjs +11 -5
- package/cli/lookup-command-runner.mjs +20 -13
- package/cli/review-command-definitions.mjs +11 -11
- package/cli/saved-lookup.mjs +22 -7
- package/cli/source-reliability.mjs +12 -10
- package/cli/support-arguments.mjs +1 -1
- package/cli/support-command-definitions.mjs +5 -4
- package/cli/support-command-runner.mjs +6 -4
- package/cli/workflow-command-definitions.mjs +2 -1
- package/docs/cli-reference.md +49 -11
- package/docs/cli.md +109 -32
- package/docs/contextual-reviews.md +10 -0
- package/docs/infrastructure-observations.md +9 -0
- package/lib/dmarc-discovery.mjs +4 -2
- package/lib/dns-inheritance-review.mjs +14 -1
- package/lib/evidence-export.mjs +10 -4
- package/lib/evidence-report.mjs +2 -1
- package/lib/generated/cisa-kev-catalog.mjs +17 -4
- package/lib/generated/registrar-standing-catalogue.mjs +7 -7
- package/lib/generated/retire-browser-catalog.mjs +178 -5
- package/lib/idn-confusables.mjs +5 -1
- package/lib/lookup-collection-receipt.mjs +52 -0
- package/lib/lookup-diagnostics.mjs +2 -12
- package/lib/lookup-progress.mjs +29 -31
- package/lib/lookup-response-contract.mjs +9 -0
- package/lib/lookup-response.mjs +15 -14
- package/lib/lookup-source-progress.mjs +4 -2
- package/lib/lookup.mjs +33 -2
- package/lib/rdap-capabilities.mjs +43 -9
- package/lib/registry-insights.mjs +17 -6
- package/lib/sslbl-certificates.generated.mjs +143 -142
- package/lib/whois-authority.mjs +56 -1
- package/lib/whois-chain.mjs +2 -1
- package/lib/whois-contracts.mjs +2 -0
- package/lib/whois-normalization.mjs +4 -4
- package/package.json +6 -6
- package/packages/analysis/bounded-json.mjs +12 -2
- package/packages/cases/case-attachment-model.mjs +36 -1
- package/packages/cases/case-checkpoint-receipts.mjs +68 -0
- package/packages/cases/case-evidence-links.mjs +28 -0
- package/packages/cases/case-evidence-model.mjs +39 -6
- package/packages/cases/case-migration-model.mjs +9 -1
- package/packages/cases/case-object-coverage.mjs +106 -20
- package/packages/cases/case-record-operations.mjs +13 -0
- package/packages/cases/case-record-projection.mjs +28 -2
- package/packages/cases/case-report.mjs +5 -4
- package/packages/cases/case-response-packet-validation.mjs +880 -0
- package/packages/cases/case-response-packet-vocabulary.mjs +12 -1
- package/packages/cases/case-response-packet.mjs +2 -15
- package/packages/cases/case-response-review-inputs.mjs +18 -1
- package/packages/cases/case-type-presentation.mjs +10 -0
- package/packages/cases/case-workflow-metadata.mjs +0 -8
- package/packages/comparison/capture-context.mjs +3 -2
- package/packages/contracts/analyst-review-state-contract.mjs +1 -1
- package/packages/contracts/case-portability.mjs +59 -28
- package/packages/contracts/cli-doctor.mjs +4 -0
- package/packages/contracts/cli-lookup.mjs +57 -46
- package/packages/contracts/lookup-evidence.mjs +12 -2
- package/packages/contracts/registry-insights.mjs +6 -0
- package/packages/evidence/dmarc-policy-context.mjs +80 -0
- package/packages/evidence/lookup-collection-receipt.mjs +140 -0
- package/packages/evidence/lookup-collection-review.mjs +74 -0
- package/packages/interchange/external-findings-import.mjs +8 -4
- package/packages/investigation/bulk-mail-exposure.mjs +2 -2
- package/packages/investigation/domain-history-review.mjs +7 -1
- package/packages/workspace/brand-keyword-campaign.mjs +11 -1
- package/packages/workspace/bulk-session-model.mjs +8 -0
- package/third-party-notices.txt +29 -29
package/lib/lookup-response.mjs
CHANGED
|
@@ -76,12 +76,16 @@ async function buildUnifiedLookupResponse(context) {
|
|
|
76
76
|
? { skipped: true, detail: 'RDAP is disabled by deployment policy.' }
|
|
77
77
|
: rdapRecord
|
|
78
78
|
? { ...rdapRecord, ...(registrarRdap ? { registrarRdap } : {}) }
|
|
79
|
-
:
|
|
80
|
-
error: rdapResult.
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
79
|
+
: rdapResult.status === 'rejected'
|
|
80
|
+
? { error: boundedSourceDetail(rdapResult.reason, 'RDAP lookup failed'), attempts: rdapAttempts }
|
|
81
|
+
: { status: 'unsupported', detail: 'No RDAP service is listed for this target in the IANA bootstrap.', attempts: rdapAttempts };
|
|
82
|
+
const whoisStatus = !whoisEnabled
|
|
83
|
+
? 'disabled'
|
|
84
|
+
: skipWhois
|
|
85
|
+
? 'skipped'
|
|
86
|
+
: whoisResult.status === 'rejected'
|
|
87
|
+
? 'error'
|
|
88
|
+
: whoisCollectionStatus(whoisChain, classified.type === 'domain' ? classified.registrableDomain ?? classified.value : undefined);
|
|
85
89
|
let whois;
|
|
86
90
|
if (!whoisEnabled) {
|
|
87
91
|
whois = { skipped: true, detail: 'WHOIS is disabled by deployment policy.' };
|
|
@@ -90,7 +94,11 @@ async function buildUnifiedLookupResponse(context) {
|
|
|
90
94
|
whois = { skipped: true, detail: 'WHOIS is omitted in fast RDAP-only mode.' };
|
|
91
95
|
}
|
|
92
96
|
else if (Array.isArray(whoisChain)) {
|
|
93
|
-
whois =
|
|
97
|
+
whois = whoisStatus === 'unsupported' || whoisStatus === 'skipped'
|
|
98
|
+
? { status: whoisStatus, chain: whoisChain,
|
|
99
|
+
detail: whoisChain[0]?.detail ?? 'IANA did not publish a domain WHOIS referral for this target.' }
|
|
100
|
+
: { chain: whoisChain, parsed: parseWhoisChain(whoisChain, classified.type === 'domain' ? classified.registrableDomain ?? classified.value : undefined),
|
|
101
|
+
...(whoisStatus === 'error' ? { error: boundedSourceDetail({ message: whoisChain[0]?.error }, 'WHOIS lookup failed') } : {}) };
|
|
94
102
|
}
|
|
95
103
|
else {
|
|
96
104
|
whois = {
|
|
@@ -138,13 +146,6 @@ async function buildUnifiedLookupResponse(context) {
|
|
|
138
146
|
: !rdapRecord
|
|
139
147
|
? 'unsupported'
|
|
140
148
|
: rdapRecord.upstreamStatus === 404 ? 'not_found' : 'success';
|
|
141
|
-
const whoisStatus = !whoisEnabled
|
|
142
|
-
? 'disabled'
|
|
143
|
-
: skipWhois
|
|
144
|
-
? 'skipped'
|
|
145
|
-
: whoisResult.status === 'rejected'
|
|
146
|
-
? 'error'
|
|
147
|
-
: whoisCollectionStatus(whoisChain, classified.type === 'domain' ? classified.registrableDomain ?? classified.value : undefined);
|
|
148
149
|
const availabilityStatus = classified.type !== 'domain'
|
|
149
150
|
? 'not_applicable'
|
|
150
151
|
: !availabilityEnabled ? 'disabled'
|
|
@@ -100,9 +100,11 @@ function normalizeLookupSourceSettlement(source, outcome, value, requestedDomain
|
|
|
100
100
|
const limitation = outcome === 'rejected'
|
|
101
101
|
? 'This source did not complete. No absence or safety conclusion was inferred.'
|
|
102
102
|
: state === 'skipped'
|
|
103
|
-
? 'This source was not
|
|
103
|
+
? 'This source was not queried: it was not requested, did not apply to the target, or was excluded by an access policy.'
|
|
104
104
|
: state === 'unsupported'
|
|
105
|
-
?
|
|
105
|
+
? source === 'rdap' || source === 'whois'
|
|
106
|
+
? 'No service is published through the applicable registry discovery source for this target; this is not a failed lookup or an absence finding.'
|
|
107
|
+
: 'This source could not provide authoritative evidence for this target.'
|
|
106
108
|
: state === 'partial'
|
|
107
109
|
? 'This source returned incomplete evidence; missing fields remain unknown.'
|
|
108
110
|
: null;
|
package/lib/lookup.mjs
CHANGED
|
@@ -22,6 +22,7 @@ import { normalizeLookupSourceSettlement, plannedLookupProgressSources, } from "
|
|
|
22
22
|
import { buildUnifiedLookupResponse } from "./lookup-response.mjs";
|
|
23
23
|
import { createLookupTimingTracker } from "./lookup-diagnostics.mjs";
|
|
24
24
|
import { prepareSelectedLookupUrl } from "../packages/evidence/lookup-target.mjs";
|
|
25
|
+
import { createLookupCollectionReceiptTracker } from "./lookup-collection-receipt.mjs";
|
|
25
26
|
async function runUnifiedLookup(classified, options = {}) {
|
|
26
27
|
const fetchRdap = options.fetchRdapRecord || fetchRdapRecord;
|
|
27
28
|
const fetchRegistrarRdap = options.fetchRegistrarRdapRecord || fetchRegistrarRdapRecord;
|
|
@@ -45,18 +46,42 @@ async function runUnifiedLookup(classified, options = {}) {
|
|
|
45
46
|
const securityTxtRequested = options.securityTxt === true;
|
|
46
47
|
const featurePolicy = options.featurePolicy || networkFeaturePolicy();
|
|
47
48
|
const timing = createLookupTimingTracker(!fast && !compact, options.now || Date.now);
|
|
49
|
+
const receipt = !compact && options.onCollectionReceipt
|
|
50
|
+
? createLookupCollectionReceiptTracker(classified, options) : null;
|
|
48
51
|
const startedCollectors = [];
|
|
49
52
|
const measure = (source, operation) => (timing.measure(source, () => abortable(() => {
|
|
53
|
+
receipt?.started(source);
|
|
50
54
|
const running = Promise.resolve(operation());
|
|
51
55
|
if (options.waitForStartedCollectors)
|
|
52
56
|
startedCollectors.push(running.catch(() => { }));
|
|
53
57
|
return running;
|
|
54
|
-
}, options.signal))
|
|
58
|
+
}, options.signal)).then(value => {
|
|
59
|
+
receipt?.settled(source, 'fulfilled', value);
|
|
60
|
+
return value;
|
|
61
|
+
}, error => {
|
|
62
|
+
receipt?.settled(source, 'rejected', error);
|
|
63
|
+
throw error;
|
|
64
|
+
}));
|
|
55
65
|
const rdapEnabled = featureDecision('rdap', featurePolicy).enabled;
|
|
56
66
|
const whoisEnabled = featureDecision('whois', featurePolicy).enabled;
|
|
57
67
|
const availabilityEnabled = featureDecision('availability', featurePolicy).enabled;
|
|
58
68
|
const websiteProbeEnabled = featureDecision('website_probe', featurePolicy).enabled;
|
|
59
69
|
const dnsIntelligenceEnabled = featureDecision('dns_intelligence', featurePolicy).enabled;
|
|
70
|
+
if (!rdapEnabled) {
|
|
71
|
+
receipt?.admission('rdap', 'disabled');
|
|
72
|
+
receipt?.admission('registrar_rdap', 'disabled');
|
|
73
|
+
receipt?.admission('network_context', 'disabled');
|
|
74
|
+
}
|
|
75
|
+
if (!whoisEnabled)
|
|
76
|
+
receipt?.admission('whois', 'disabled');
|
|
77
|
+
if (!availabilityEnabled) {
|
|
78
|
+
receipt?.admission('domain_evidence', 'disabled');
|
|
79
|
+
receipt?.admission('network_context', 'disabled');
|
|
80
|
+
}
|
|
81
|
+
if (!dnsIntelligenceEnabled)
|
|
82
|
+
receipt?.admission('reverse_dns', 'disabled');
|
|
83
|
+
if (!websiteProbeEnabled)
|
|
84
|
+
receipt?.admission('security_txt', 'disabled');
|
|
60
85
|
const skipWhois = fast || !whoisEnabled;
|
|
61
86
|
const selectedUrl = options.selectedUrl === undefined ? undefined
|
|
62
87
|
: prepareSelectedLookupUrl(options.selectedUrl, classified.inputHostname ?? classified.value);
|
|
@@ -72,6 +97,8 @@ async function runUnifiedLookup(classified, options = {}) {
|
|
|
72
97
|
// Registrar RDAP is a separately attributed deep-lookup enrichment. It may
|
|
73
98
|
// overlap the WHOIS chain, but it never joins the promises used to decide
|
|
74
99
|
// availability and can add up to its own bounded timeout to a deep lookup.
|
|
100
|
+
if (classified.type === 'domain' && rdapEnabled && !fast && !compact)
|
|
101
|
+
receipt?.admission('registrar_rdap', 'prerequisite_unavailable');
|
|
75
102
|
const registrarRdapPromise = classified.type === 'domain' && rdapEnabled && !fast && !compact
|
|
76
103
|
? rdapPromise.then((record) => record && record.upstreamStatus === 200 && record.parsed
|
|
77
104
|
? measure('registrar_rdap', () => fetchRegistrarRdap(classified.value, record))
|
|
@@ -187,7 +214,7 @@ async function runUnifiedLookup(classified, options = {}) {
|
|
|
187
214
|
}
|
|
188
215
|
}
|
|
189
216
|
try {
|
|
190
|
-
|
|
217
|
+
const result = await buildUnifiedLookupResponse({
|
|
191
218
|
availabilityEnabled,
|
|
192
219
|
availabilityPromise,
|
|
193
220
|
classified,
|
|
@@ -211,6 +238,10 @@ async function runUnifiedLookup(classified, options = {}) {
|
|
|
211
238
|
whoisEnabled,
|
|
212
239
|
whoisPromise,
|
|
213
240
|
});
|
|
241
|
+
options.signal?.throwIfAborted();
|
|
242
|
+
if (receipt)
|
|
243
|
+
options.onCollectionReceipt?.(receipt.finish());
|
|
244
|
+
return result;
|
|
214
245
|
}
|
|
215
246
|
finally {
|
|
216
247
|
if (options.waitForStartedCollectors)
|
|
@@ -3,14 +3,13 @@
|
|
|
3
3
|
// search and does not treat an absent extension declaration as proof that a
|
|
4
4
|
// server cannot support the related operation.
|
|
5
5
|
export const RDAP_CAPABILITY_INSPECTION_VERSION = 1;
|
|
6
|
-
export const RDAP_EXTENSION_CATALOG_REVIEWED_AT = '2026-
|
|
6
|
+
export const RDAP_EXTENSION_CATALOG_REVIEWED_AT = '2026-10-14';
|
|
7
7
|
export const MAX_RDAP_CAPABILITY_DECLARATIONS = 50;
|
|
8
8
|
export const MAX_RDAP_UNKNOWN_CAPABILITIES = 20;
|
|
9
9
|
const CONTROL_RE = /[\u0000-\u001f\u007f]/u;
|
|
10
|
-
//
|
|
11
|
-
//
|
|
12
|
-
|
|
13
|
-
const EXTENSION_CATALOG = Object.freeze({
|
|
10
|
+
// Published interpretation snapshot. Saved evidence selects this exact revision;
|
|
11
|
+
// later catalogue revisions extend or replace entries without changing it.
|
|
12
|
+
const JULY_2026_EXTENSION_CATALOG = Object.freeze({
|
|
14
13
|
rdap_level_0: {
|
|
15
14
|
label: 'Core RDAP response',
|
|
16
15
|
category: 'core',
|
|
@@ -164,8 +163,39 @@ const EXTENSION_CATALOG = Object.freeze({
|
|
|
164
163
|
capability: 'DNS time-to-live values in RDAP responses.',
|
|
165
164
|
},
|
|
166
165
|
});
|
|
166
|
+
const RDAP_EXTENSION_CATALOGS = Object.freeze({
|
|
167
|
+
'2026-07-31': JULY_2026_EXTENSION_CATALOG,
|
|
168
|
+
'2026-10-14': Object.freeze({
|
|
169
|
+
...JULY_2026_EXTENSION_CATALOG,
|
|
170
|
+
reliabilityassessment: {
|
|
171
|
+
label: 'Reliability assessment metadata',
|
|
172
|
+
category: 'operator',
|
|
173
|
+
capability: 'Operator-declared assessment metadata; no assessment is retrieved or verified.',
|
|
174
|
+
},
|
|
175
|
+
}),
|
|
176
|
+
});
|
|
177
|
+
const CATALOG_EPOCHS = Object.keys(RDAP_EXTENSION_CATALOGS).sort();
|
|
178
|
+
export function resolveRdapCapabilityCatalogRevision(value) {
|
|
179
|
+
if (typeof value !== 'string' || !/^\d{4}-\d{2}-\d{2}$/u.test(value)
|
|
180
|
+
|| value < CATALOG_EPOCHS[0] || value > RDAP_EXTENSION_CATALOG_REVIEWED_AT)
|
|
181
|
+
return null;
|
|
182
|
+
const time = Date.parse(`${value}T00:00:00.000Z`);
|
|
183
|
+
if (!Number.isFinite(time) || new Date(time).toISOString().slice(0, 10) !== value)
|
|
184
|
+
return null;
|
|
185
|
+
// Review-only renewals retain the same semantic epoch and the recorded date.
|
|
186
|
+
for (let index = CATALOG_EPOCHS.length - 1; index >= 0; index -= 1) {
|
|
187
|
+
const epoch = CATALOG_EPOCHS[index];
|
|
188
|
+
if (epoch <= value)
|
|
189
|
+
return epoch;
|
|
190
|
+
}
|
|
191
|
+
return null;
|
|
192
|
+
}
|
|
167
193
|
export function reviewedRdapExtensionCatalog() {
|
|
168
|
-
|
|
194
|
+
const revision = resolveRdapCapabilityCatalogRevision(RDAP_EXTENSION_CATALOG_REVIEWED_AT);
|
|
195
|
+
if (revision === null)
|
|
196
|
+
throw new TypeError('Unsupported RDAP capability catalogue revision.');
|
|
197
|
+
const catalog = RDAP_EXTENSION_CATALOGS[revision];
|
|
198
|
+
return Object.entries(catalog)
|
|
169
199
|
.filter(([, entry]) => entry.category !== 'core')
|
|
170
200
|
.map(([identifier, entry]) => Object.freeze({
|
|
171
201
|
identifier,
|
|
@@ -232,7 +262,11 @@ function reverseSearchCapability(state, declared) {
|
|
|
232
262
|
],
|
|
233
263
|
};
|
|
234
264
|
}
|
|
235
|
-
export function inspectRdapCapabilities(parsedValue, status) {
|
|
265
|
+
export function inspectRdapCapabilities(parsedValue, status, catalogReviewedAt = RDAP_EXTENSION_CATALOG_REVIEWED_AT) {
|
|
266
|
+
const revision = resolveRdapCapabilityCatalogRevision(catalogReviewedAt);
|
|
267
|
+
if (revision === null)
|
|
268
|
+
throw new TypeError('Unsupported RDAP capability catalogue revision.');
|
|
269
|
+
const catalog = RDAP_EXTENSION_CATALOGS[revision];
|
|
236
270
|
const parsed = record(parsedValue);
|
|
237
271
|
const state = sourceState(parsed, status);
|
|
238
272
|
const rawDeclarations = Array.isArray(parsed?.conformance) ? parsed.conformance : [];
|
|
@@ -245,7 +279,7 @@ export function inspectRdapCapabilities(parsedValue, status) {
|
|
|
245
279
|
break;
|
|
246
280
|
}
|
|
247
281
|
const declarations = [...unique].sort().map((value) => {
|
|
248
|
-
const entry = Object.hasOwn(
|
|
282
|
+
const entry = Object.hasOwn(catalog, value) ? catalog[value] : undefined;
|
|
249
283
|
return entry
|
|
250
284
|
? {
|
|
251
285
|
identifier: value,
|
|
@@ -281,7 +315,7 @@ export function inspectRdapCapabilities(parsedValue, status) {
|
|
|
281
315
|
}
|
|
282
316
|
return {
|
|
283
317
|
version: RDAP_CAPABILITY_INSPECTION_VERSION,
|
|
284
|
-
catalogReviewedAt
|
|
318
|
+
catalogReviewedAt,
|
|
285
319
|
state,
|
|
286
320
|
declarations,
|
|
287
321
|
unknownIdentifiers,
|
|
@@ -9,7 +9,8 @@ import { inspectRdapCapabilities } from "./rdap-capabilities.mjs";
|
|
|
9
9
|
import { isRecord, recordOrEmpty } from "./json-record.mjs";
|
|
10
10
|
import { MAX_RDAP_REDACTIONS, MAX_RDAP_STATUSES } from "./rdap-types.mjs";
|
|
11
11
|
import { MAX_WHOIS_STATUSES } from "./whois-contracts.mjs";
|
|
12
|
-
|
|
12
|
+
import { LEGACY_REGISTRY_INSIGHTS_VERSION, REGISTRY_INSIGHTS_VERSION, isSupportedRegistryInsightsVersion, } from "../packages/contracts/registry-insights.mjs";
|
|
13
|
+
export { REGISTRY_INSIGHTS_VERSION } from "../packages/contracts/registry-insights.mjs";
|
|
13
14
|
export const MAX_REGISTRY_INSIGHT_STATUSES = 40;
|
|
14
15
|
export const MAX_REGISTRY_INSIGHT_ISSUES = 12;
|
|
15
16
|
export const MAX_REGISTRY_ABUSE_ROUTES = 8;
|
|
@@ -288,7 +289,10 @@ function contactRoute(kind, contact, source) {
|
|
|
288
289
|
],
|
|
289
290
|
}));
|
|
290
291
|
}
|
|
291
|
-
export function buildRegistryInsights(input) {
|
|
292
|
+
export function buildRegistryInsights(input, options = {}) {
|
|
293
|
+
const version = options.version ?? REGISTRY_INSIGHTS_VERSION;
|
|
294
|
+
if (!isSupportedRegistryInsightsVersion(version))
|
|
295
|
+
throw new TypeError('Unsupported registry insight version.');
|
|
292
296
|
const rdap = record(input.rdapParsed);
|
|
293
297
|
const whois = record(input.whoisParsed);
|
|
294
298
|
const registrarRdap = record(input.registrarRdapParsed);
|
|
@@ -322,7 +326,14 @@ export function buildRegistryInsights(input) {
|
|
|
322
326
|
];
|
|
323
327
|
const lifecycleCandidates = [
|
|
324
328
|
{ statuses: rdapStatuses.values, state: publications[0].state },
|
|
325
|
-
{
|
|
329
|
+
{
|
|
330
|
+
statuses: whoisStatuses.values,
|
|
331
|
+
// A completed response describes collection, not positive registration.
|
|
332
|
+
// Preserve the old derivation only when verifying a historical export.
|
|
333
|
+
state: version === LEGACY_REGISTRY_INSIGHTS_VERSION || whois.registrationStatus === 'registered'
|
|
334
|
+
? publications[1].state
|
|
335
|
+
: 'unavailable',
|
|
336
|
+
},
|
|
326
337
|
].filter((candidate) => candidate.state !== 'unavailable');
|
|
327
338
|
const lifecycleSource = lifecycleCandidates.find((candidate) => candidate.statuses.length)
|
|
328
339
|
?? lifecycleCandidates[0]
|
|
@@ -343,7 +354,7 @@ export function buildRegistryInsights(input) {
|
|
|
343
354
|
break;
|
|
344
355
|
}
|
|
345
356
|
return {
|
|
346
|
-
version
|
|
357
|
+
version,
|
|
347
358
|
contactDisclosure: {
|
|
348
359
|
registryRdap: disclosure('registry_rdap', input.rdapParsed, input.rdapStatus),
|
|
349
360
|
whois: disclosure('whois', input.whoisParsed, input.whoisStatus),
|
|
@@ -369,8 +380,8 @@ export function buildRegistryInsights(input) {
|
|
|
369
380
|
},
|
|
370
381
|
publications,
|
|
371
382
|
rdapCapabilities: {
|
|
372
|
-
registry: inspectRdapCapabilities(input.rdapParsed, input.rdapStatus),
|
|
373
|
-
registrar: inspectRdapCapabilities(input.registrarRdapParsed, input.registrarRdapStatus),
|
|
383
|
+
registry: inspectRdapCapabilities(input.rdapParsed, input.rdapStatus, options.rdapCapabilityCatalogReviewedAt),
|
|
384
|
+
registrar: inspectRdapCapabilities(input.registrarRdapParsed, input.registrarRdapStatus, options.rdapCapabilityCatalogReviewedAt),
|
|
374
385
|
},
|
|
375
386
|
abuseRouting: [...uniqueRoutes.values()],
|
|
376
387
|
};
|