@slicedearth/whoisleuth-cli 1.47.4 → 2.0.1
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/project-metadata.mjs +1 -1
- 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 +13 -5
- 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} +71 -48
- 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
|
@@ -49,18 +49,24 @@ function truncatedText(value, maxLength) {
|
|
|
49
49
|
return null;
|
|
50
50
|
return value.replace(/\s+/g, ' ').trim().slice(0, maxLength).trim() || null;
|
|
51
51
|
}
|
|
52
|
-
function
|
|
52
|
+
function flattenScalarValuesWithCompleteness(value, maxValues = 32) {
|
|
53
53
|
const output = [];
|
|
54
54
|
const stack = [{ value, depth: 0 }];
|
|
55
55
|
let visited = 0;
|
|
56
|
+
let truncated = false;
|
|
56
57
|
while (stack.length && output.length < maxValues && visited < 128) {
|
|
57
58
|
const current = stack.pop();
|
|
58
59
|
if (!current)
|
|
59
60
|
continue;
|
|
60
61
|
visited += 1;
|
|
61
62
|
if (Array.isArray(current.value)) {
|
|
62
|
-
if (current.depth >= MAX_RDAP_ENTITY_DEPTH)
|
|
63
|
+
if (current.depth >= MAX_RDAP_ENTITY_DEPTH) {
|
|
64
|
+
if (current.value.length > 0)
|
|
65
|
+
truncated = true;
|
|
63
66
|
continue;
|
|
67
|
+
}
|
|
68
|
+
if (current.value.length > maxValues)
|
|
69
|
+
truncated = true;
|
|
64
70
|
for (let i = Math.min(current.value.length, maxValues) - 1; i >= 0; i -= 1) {
|
|
65
71
|
stack.push({ value: current.value[i], depth: current.depth + 1 });
|
|
66
72
|
}
|
|
@@ -71,8 +77,16 @@ function flattenScalarValues(value, maxValues = 32) {
|
|
|
71
77
|
else if (typeof current.value === 'number' && Number.isFinite(current.value)) {
|
|
72
78
|
output.push(String(current.value));
|
|
73
79
|
}
|
|
80
|
+
else {
|
|
81
|
+
truncated = true;
|
|
82
|
+
}
|
|
74
83
|
}
|
|
75
|
-
|
|
84
|
+
if (stack.length > 0)
|
|
85
|
+
truncated = true;
|
|
86
|
+
return { values: output, truncated };
|
|
87
|
+
}
|
|
88
|
+
function flattenScalarValues(value, maxValues = 32) {
|
|
89
|
+
return flattenScalarValuesWithCompleteness(value, maxValues).values;
|
|
76
90
|
}
|
|
77
91
|
function vcardRawValues(vcardArray, field) {
|
|
78
92
|
if (!Array.isArray(vcardArray) || !Array.isArray(vcardArray[1]))
|
|
@@ -119,79 +133,156 @@ function normalizeAddresses(vcardArray) {
|
|
|
119
133
|
return addresses;
|
|
120
134
|
}
|
|
121
135
|
function contactValuesTruncated(vcardArray) {
|
|
122
|
-
|
|
136
|
+
const scalarFields = [
|
|
137
|
+
{ field: 'fn', maxLength: 300, lower: false },
|
|
138
|
+
{ field: 'org', maxLength: 300, lower: false },
|
|
139
|
+
{ field: 'email', maxLength: 320, lower: true },
|
|
140
|
+
{ field: 'tel', maxLength: 100, lower: false },
|
|
141
|
+
];
|
|
142
|
+
for (const { field, maxLength, lower } of scalarFields) {
|
|
123
143
|
let count = 0;
|
|
124
144
|
for (const raw of vcardRawValues(vcardArray, field)) {
|
|
125
|
-
|
|
145
|
+
const flattened = flattenScalarValuesWithCompleteness(raw, MAX_CONTACT_VALUES + 1);
|
|
146
|
+
if (flattened.truncated
|
|
147
|
+
|| flattened.values.some((value) => boundedString(value, maxLength, { lower }) === null))
|
|
148
|
+
return true;
|
|
149
|
+
count += flattened.values.length;
|
|
126
150
|
if (count > MAX_CONTACT_VALUES)
|
|
127
151
|
return true;
|
|
128
152
|
}
|
|
129
153
|
}
|
|
130
|
-
|
|
154
|
+
const addressValues = vcardRawValues(vcardArray, 'adr');
|
|
155
|
+
if (addressValues.length > MAX_CONTACT_VALUES)
|
|
156
|
+
return true;
|
|
157
|
+
for (const raw of addressValues) {
|
|
158
|
+
const flattened = flattenScalarValuesWithCompleteness(raw, 33);
|
|
159
|
+
if (flattened.truncated || flattened.values.length > 32)
|
|
160
|
+
return true;
|
|
161
|
+
const parts = flattened.values.map((part) => boundedString(part, 300));
|
|
162
|
+
const rejectedNonBlankPart = parts.some((part, index) => {
|
|
163
|
+
if (part !== null)
|
|
164
|
+
return false;
|
|
165
|
+
const rawPart = flattened.values[index] ?? '';
|
|
166
|
+
return rawPart.length > 300 || /[\u0000-\u001f\u007f]/u.test(rawPart) || rawPart.trim() !== '';
|
|
167
|
+
});
|
|
168
|
+
const retainedParts = parts.filter((part) => part !== null);
|
|
169
|
+
if (rejectedNonBlankPart
|
|
170
|
+
|| boundedString(retainedParts.join(', '), 1000) === null)
|
|
171
|
+
return true;
|
|
172
|
+
}
|
|
173
|
+
return false;
|
|
174
|
+
}
|
|
175
|
+
function malformedVcardEvidence(vcardArray) {
|
|
176
|
+
if (vcardArray === undefined)
|
|
177
|
+
return false;
|
|
178
|
+
if (!Array.isArray(vcardArray)
|
|
179
|
+
|| String(vcardArray[0]).toLowerCase() !== 'vcard'
|
|
180
|
+
|| !Array.isArray(vcardArray[1]))
|
|
181
|
+
return true;
|
|
182
|
+
const entries = vcardArray[1];
|
|
183
|
+
if (entries.length > MAX_VCARD_ENTRIES)
|
|
184
|
+
return true;
|
|
185
|
+
return entries.slice(0, MAX_VCARD_ENTRIES).some((entry) => (!Array.isArray(entry)
|
|
186
|
+
|| entry.length < 4
|
|
187
|
+
|| typeof entry[0] !== 'string'
|
|
188
|
+
|| !entry[0].trim()));
|
|
131
189
|
}
|
|
132
190
|
function normalizeLinks(links, maxLinks = MAX_RDAP_LINKS) {
|
|
133
|
-
if (!Array.isArray(links))
|
|
134
|
-
return [];
|
|
135
191
|
const normalized = [];
|
|
192
|
+
if (links === undefined)
|
|
193
|
+
return { items: normalized, truncated: false };
|
|
194
|
+
if (!Array.isArray(links))
|
|
195
|
+
return { items: normalized, truncated: true };
|
|
196
|
+
let truncated = links.length > 100 || links.length > maxLinks;
|
|
136
197
|
for (const linkValue of links.slice(0, 100)) {
|
|
137
|
-
if (!linkValue || typeof linkValue !== 'object' || Array.isArray(linkValue))
|
|
198
|
+
if (!linkValue || typeof linkValue !== 'object' || Array.isArray(linkValue)) {
|
|
199
|
+
truncated = true;
|
|
138
200
|
continue;
|
|
201
|
+
}
|
|
139
202
|
const link = linkValue;
|
|
140
203
|
const href = boundedString(link.href, 2048);
|
|
141
|
-
if (!href)
|
|
204
|
+
if (!href) {
|
|
205
|
+
truncated = true;
|
|
142
206
|
continue;
|
|
207
|
+
}
|
|
143
208
|
try {
|
|
144
209
|
const parsed = new URL(href);
|
|
145
|
-
if (parsed.protocol !== 'https:' && parsed.protocol !== 'http:')
|
|
210
|
+
if (parsed.protocol !== 'https:' && parsed.protocol !== 'http:') {
|
|
211
|
+
truncated = true;
|
|
146
212
|
continue;
|
|
213
|
+
}
|
|
147
214
|
}
|
|
148
215
|
catch {
|
|
216
|
+
truncated = true;
|
|
149
217
|
continue;
|
|
150
218
|
}
|
|
219
|
+
const rel = boundedString(link.rel, 100, { lower: true });
|
|
220
|
+
const type = boundedString(link.type, 160, { lower: true });
|
|
221
|
+
const title = boundedString(link.title, 300);
|
|
222
|
+
if (link.rel !== undefined && !rel
|
|
223
|
+
|| link.type !== undefined && !type
|
|
224
|
+
|| link.title !== undefined && !title)
|
|
225
|
+
truncated = true;
|
|
151
226
|
normalized.push({
|
|
152
|
-
rel
|
|
227
|
+
rel,
|
|
153
228
|
href,
|
|
154
|
-
type
|
|
155
|
-
title
|
|
229
|
+
type,
|
|
230
|
+
title,
|
|
156
231
|
});
|
|
157
232
|
if (normalized.length >= maxLinks)
|
|
158
233
|
break;
|
|
159
234
|
}
|
|
160
|
-
return normalized;
|
|
235
|
+
return { items: normalized, truncated };
|
|
161
236
|
}
|
|
162
237
|
function normalizePublicIds(publicIds) {
|
|
163
|
-
if (!Array.isArray(publicIds))
|
|
164
|
-
return [];
|
|
165
238
|
const normalized = [];
|
|
239
|
+
if (publicIds === undefined)
|
|
240
|
+
return { items: normalized, truncated: false };
|
|
241
|
+
if (!Array.isArray(publicIds))
|
|
242
|
+
return { items: normalized, truncated: true };
|
|
243
|
+
let truncated = publicIds.length > 100 || publicIds.length > 20;
|
|
166
244
|
for (const itemValue of publicIds.slice(0, 100)) {
|
|
167
|
-
if (!itemValue || typeof itemValue !== 'object' || Array.isArray(itemValue))
|
|
245
|
+
if (!itemValue || typeof itemValue !== 'object' || Array.isArray(itemValue)) {
|
|
246
|
+
truncated = true;
|
|
168
247
|
continue;
|
|
248
|
+
}
|
|
169
249
|
const item = itemValue;
|
|
170
250
|
const type = boundedString(item.type, 160);
|
|
171
251
|
const identifier = boundedString(item.identifier, 300);
|
|
172
|
-
if (!type || !identifier)
|
|
252
|
+
if (!type || !identifier) {
|
|
253
|
+
truncated = true;
|
|
173
254
|
continue;
|
|
255
|
+
}
|
|
174
256
|
normalized.push({ type, identifier });
|
|
175
257
|
if (normalized.length >= 20)
|
|
176
258
|
break;
|
|
177
259
|
}
|
|
178
|
-
return normalized;
|
|
260
|
+
return { items: normalized, truncated };
|
|
179
261
|
}
|
|
180
262
|
function normalizeStringList(value, maxItems, maxLength, { lower = false } = {}) {
|
|
263
|
+
if (value === undefined)
|
|
264
|
+
return { items: [], truncated: false };
|
|
181
265
|
if (!Array.isArray(value))
|
|
182
|
-
return [];
|
|
266
|
+
return { items: [], truncated: true };
|
|
183
267
|
const output = [];
|
|
184
268
|
const seen = new Set();
|
|
185
|
-
|
|
269
|
+
const inspectionLimit = Math.max(maxItems * 4, maxItems);
|
|
270
|
+
let truncated = value.length > inspectionLimit;
|
|
271
|
+
for (const item of value.slice(0, inspectionLimit)) {
|
|
186
272
|
const normalized = boundedString(item, maxLength, { lower });
|
|
187
|
-
if (!normalized
|
|
273
|
+
if (!normalized) {
|
|
274
|
+
truncated = true;
|
|
275
|
+
continue;
|
|
276
|
+
}
|
|
277
|
+
if (seen.has(normalized.toLowerCase()))
|
|
188
278
|
continue;
|
|
189
279
|
seen.add(normalized.toLowerCase());
|
|
190
|
-
output.
|
|
191
|
-
|
|
192
|
-
|
|
280
|
+
if (output.length < maxItems)
|
|
281
|
+
output.push(normalized);
|
|
282
|
+
else
|
|
283
|
+
truncated = true;
|
|
193
284
|
}
|
|
194
|
-
return output;
|
|
285
|
+
return { items: output, truncated };
|
|
195
286
|
}
|
|
196
287
|
function redactionLabel(value) {
|
|
197
288
|
if (typeof value === 'string')
|
|
@@ -201,16 +292,36 @@ function redactionLabel(value) {
|
|
|
201
292
|
const record = value;
|
|
202
293
|
return boundedString(record.type, 160) || boundedString(record.description, 300);
|
|
203
294
|
}
|
|
295
|
+
function suppliedBoundedStringRejected(record, field, maxLength, options = {}) {
|
|
296
|
+
return Object.hasOwn(record, field) && boundedString(record[field], maxLength, options) === null;
|
|
297
|
+
}
|
|
298
|
+
function redactionLabelHasRejectedContent(value) {
|
|
299
|
+
if (typeof value === 'string')
|
|
300
|
+
return boundedString(value, 300) === null;
|
|
301
|
+
if (!value || typeof value !== 'object' || Array.isArray(value))
|
|
302
|
+
return true;
|
|
303
|
+
const label = value;
|
|
304
|
+
const hasType = Object.hasOwn(label, 'type');
|
|
305
|
+
const hasDescription = Object.hasOwn(label, 'description');
|
|
306
|
+
return (!hasType && !hasDescription)
|
|
307
|
+
|| (hasType && boundedString(label.type, 160) === null)
|
|
308
|
+
|| (hasDescription && boundedString(label.description, 300) === null);
|
|
309
|
+
}
|
|
204
310
|
function normalizeRedactions(redacted) {
|
|
205
|
-
if (
|
|
311
|
+
if (redacted === undefined)
|
|
206
312
|
return { redactions: [], redactionsTruncated: false };
|
|
313
|
+
if (!Array.isArray(redacted))
|
|
314
|
+
return { redactions: [], redactionsTruncated: true };
|
|
207
315
|
const redactions = [];
|
|
208
316
|
const candidates = redacted.slice(0, MAX_RDAP_REDACTIONS * 2);
|
|
209
317
|
let stoppedAt = candidates.length;
|
|
318
|
+
let truncated = redacted.length > candidates.length;
|
|
210
319
|
for (let index = 0; index < candidates.length; index += 1) {
|
|
211
320
|
const itemValue = candidates[index];
|
|
212
|
-
if (!itemValue || typeof itemValue !== 'object' || Array.isArray(itemValue))
|
|
321
|
+
if (!itemValue || typeof itemValue !== 'object' || Array.isArray(itemValue)) {
|
|
322
|
+
truncated = true;
|
|
213
323
|
continue;
|
|
324
|
+
}
|
|
214
325
|
const item = itemValue;
|
|
215
326
|
const entry = {
|
|
216
327
|
name: redactionLabel(item.name),
|
|
@@ -221,8 +332,19 @@ function normalizeRedactions(redacted) {
|
|
|
221
332
|
postPath: boundedString(item.postPath, 512),
|
|
222
333
|
replacementPath: boundedString(item.replacementPath, 512),
|
|
223
334
|
};
|
|
224
|
-
if (
|
|
335
|
+
if (Object.hasOwn(item, 'name') && redactionLabelHasRejectedContent(item.name)
|
|
336
|
+
|| Object.hasOwn(item, 'reason') && redactionLabelHasRejectedContent(item.reason)
|
|
337
|
+
|| suppliedBoundedStringRejected(item, 'method', 80, { lower: true })
|
|
338
|
+
|| suppliedBoundedStringRejected(item, 'pathLang', 80, { lower: true })
|
|
339
|
+
|| suppliedBoundedStringRejected(item, 'prePath', 512)
|
|
340
|
+
|| suppliedBoundedStringRejected(item, 'postPath', 512)
|
|
341
|
+
|| suppliedBoundedStringRejected(item, 'replacementPath', 512)) {
|
|
342
|
+
truncated = true;
|
|
343
|
+
}
|
|
344
|
+
if (!Object.values(entry).some(Boolean)) {
|
|
345
|
+
truncated = true;
|
|
225
346
|
continue;
|
|
347
|
+
}
|
|
226
348
|
redactions.push(entry);
|
|
227
349
|
if (redactions.length >= MAX_RDAP_REDACTIONS) {
|
|
228
350
|
stoppedAt = index + 1;
|
|
@@ -231,38 +353,58 @@ function normalizeRedactions(redacted) {
|
|
|
231
353
|
}
|
|
232
354
|
return {
|
|
233
355
|
redactions,
|
|
234
|
-
redactionsTruncated: stoppedAt < redacted.length,
|
|
356
|
+
redactionsTruncated: truncated || stoppedAt < redacted.length,
|
|
235
357
|
};
|
|
236
358
|
}
|
|
237
359
|
function normalizeDomainVariants(value) {
|
|
238
|
-
if (
|
|
360
|
+
if (value === undefined)
|
|
239
361
|
return { variants: [], variantsTruncated: false };
|
|
362
|
+
if (!Array.isArray(value))
|
|
363
|
+
return { variants: [], variantsTruncated: true };
|
|
240
364
|
const variants = [];
|
|
241
365
|
let truncated = value.length > MAX_RDAP_VARIANT_GROUPS;
|
|
242
366
|
for (const groupValue of value.slice(0, MAX_RDAP_VARIANT_GROUPS)) {
|
|
243
|
-
if (!groupValue || typeof groupValue !== 'object' || Array.isArray(groupValue))
|
|
367
|
+
if (!groupValue || typeof groupValue !== 'object' || Array.isArray(groupValue)) {
|
|
368
|
+
truncated = true;
|
|
244
369
|
continue;
|
|
370
|
+
}
|
|
245
371
|
const group = groupValue;
|
|
246
372
|
const sourceNames = Array.isArray(group.variantNames) ? group.variantNames : [];
|
|
373
|
+
if (group.variantNames !== undefined && !Array.isArray(group.variantNames))
|
|
374
|
+
truncated = true;
|
|
247
375
|
const variantNames = [];
|
|
248
376
|
for (const nameValue of sourceNames.slice(0, MAX_RDAP_VARIANT_NAMES * 2)) {
|
|
249
|
-
if (!nameValue || typeof nameValue !== 'object' || Array.isArray(nameValue))
|
|
377
|
+
if (!nameValue || typeof nameValue !== 'object' || Array.isArray(nameValue)) {
|
|
378
|
+
truncated = true;
|
|
250
379
|
continue;
|
|
380
|
+
}
|
|
251
381
|
const name = nameValue;
|
|
252
382
|
const ldhName = boundedString(name.ldhName, 253);
|
|
253
383
|
const unicodeName = boundedString(name.unicodeName, 253);
|
|
254
|
-
if (
|
|
384
|
+
if (suppliedBoundedStringRejected(name, 'ldhName', 253)
|
|
385
|
+
|| suppliedBoundedStringRejected(name, 'unicodeName', 253))
|
|
386
|
+
truncated = true;
|
|
387
|
+
if (!ldhName && !unicodeName) {
|
|
388
|
+
truncated = true;
|
|
255
389
|
continue;
|
|
390
|
+
}
|
|
256
391
|
variantNames.push({ ldhName, unicodeName });
|
|
257
392
|
if (variantNames.length >= MAX_RDAP_VARIANT_NAMES)
|
|
258
393
|
break;
|
|
259
394
|
}
|
|
260
395
|
if (sourceNames.length > MAX_RDAP_VARIANT_NAMES)
|
|
261
396
|
truncated = true;
|
|
262
|
-
const
|
|
397
|
+
const relationInfo = normalizeStringList(group.relation, 20, 100, { lower: true });
|
|
398
|
+
if (relationInfo.truncated)
|
|
399
|
+
truncated = true;
|
|
400
|
+
const relation = relationInfo.items;
|
|
263
401
|
const idnTable = boundedString(group.idnTable, 300);
|
|
264
|
-
if (
|
|
402
|
+
if (group.idnTable !== undefined && !idnTable)
|
|
403
|
+
truncated = true;
|
|
404
|
+
if (!variantNames.length && !relation.length && !idnTable) {
|
|
405
|
+
truncated = true;
|
|
265
406
|
continue;
|
|
407
|
+
}
|
|
266
408
|
variants.push({ relation, idnTable, variantNames });
|
|
267
409
|
}
|
|
268
410
|
return { variants, variantsTruncated: truncated };
|
|
@@ -279,36 +421,50 @@ function textWouldTruncate(value, maxLength) {
|
|
|
279
421
|
return value.replace(/\s+/g, ' ').trim().length > maxLength;
|
|
280
422
|
}
|
|
281
423
|
function summarizeTextBlocks(blocks) {
|
|
282
|
-
if (!Array.isArray(blocks))
|
|
283
|
-
return { items: [], truncated: false };
|
|
284
424
|
const output = [];
|
|
425
|
+
if (blocks === undefined)
|
|
426
|
+
return { items: output, truncated: false };
|
|
427
|
+
if (!Array.isArray(blocks))
|
|
428
|
+
return { items: output, truncated: true };
|
|
285
429
|
let truncated = blocks.length > 50;
|
|
286
430
|
for (const blockValue of blocks.slice(0, 50)) {
|
|
287
|
-
if (!blockValue || typeof blockValue !== 'object' || Array.isArray(blockValue))
|
|
431
|
+
if (!blockValue || typeof blockValue !== 'object' || Array.isArray(blockValue)) {
|
|
432
|
+
truncated = true;
|
|
288
433
|
continue;
|
|
434
|
+
}
|
|
289
435
|
const block = blockValue;
|
|
290
436
|
const descriptions = [];
|
|
291
437
|
const sourceDescriptions = Array.isArray(block.description) ? block.description : [];
|
|
438
|
+
if (!Array.isArray(block.description))
|
|
439
|
+
truncated = true;
|
|
292
440
|
if (sourceDescriptions.length > 20)
|
|
293
441
|
truncated = true;
|
|
294
442
|
for (const text of sourceDescriptions.slice(0, 20)) {
|
|
295
443
|
if (textWouldTruncate(text, 800))
|
|
296
444
|
truncated = true;
|
|
297
445
|
const description = truncatedText(text, 800);
|
|
298
|
-
if (!description)
|
|
446
|
+
if (!description) {
|
|
447
|
+
truncated = true;
|
|
299
448
|
continue;
|
|
449
|
+
}
|
|
300
450
|
if (descriptions.length < 6)
|
|
301
451
|
descriptions.push(description);
|
|
302
452
|
else
|
|
303
453
|
truncated = true;
|
|
304
454
|
}
|
|
305
|
-
if (!descriptions.length)
|
|
455
|
+
if (!descriptions.length) {
|
|
456
|
+
truncated = true;
|
|
306
457
|
continue;
|
|
458
|
+
}
|
|
307
459
|
if (textWouldTruncate(block.title, 160))
|
|
308
460
|
truncated = true;
|
|
461
|
+
const title = truncatedText(block.title, 160);
|
|
462
|
+
const type = boundedString(block.type, 160, { lower: true });
|
|
463
|
+
if (block.title !== undefined && !title || block.type !== undefined && !type)
|
|
464
|
+
truncated = true;
|
|
309
465
|
const item = {
|
|
310
|
-
title:
|
|
311
|
-
type
|
|
466
|
+
title: title || 'Notice',
|
|
467
|
+
type,
|
|
312
468
|
descriptions,
|
|
313
469
|
};
|
|
314
470
|
if (output.length < 12)
|
|
@@ -369,9 +525,10 @@ function lifecycleDate(events, action, newest) {
|
|
|
369
525
|
for (const event of events) {
|
|
370
526
|
if (event.action !== action || !event.date)
|
|
371
527
|
continue;
|
|
372
|
-
const
|
|
373
|
-
if (!
|
|
528
|
+
const normalized = registryDateIso(event.date);
|
|
529
|
+
if (!normalized)
|
|
374
530
|
continue;
|
|
531
|
+
const time = Date.parse(normalized);
|
|
375
532
|
if ((newest && time > selectedTime) || (!newest && time < selectedTime)) {
|
|
376
533
|
selected = event.date;
|
|
377
534
|
selectedTime = time;
|
|
@@ -407,16 +564,25 @@ function summarizeEntity(entity) {
|
|
|
407
564
|
return null;
|
|
408
565
|
const record = entity;
|
|
409
566
|
const roles = [];
|
|
567
|
+
let rolesTruncated = record.roles !== undefined && !Array.isArray(record.roles);
|
|
410
568
|
if (Array.isArray(record.roles)) {
|
|
569
|
+
if (record.roles.length > MAX_ENTITY_ROLES)
|
|
570
|
+
rolesTruncated = true;
|
|
411
571
|
for (const rawRole of record.roles.slice(0, 100)) {
|
|
412
572
|
const role = boundedString(rawRole, 80, { lower: true });
|
|
413
|
-
if (!role
|
|
573
|
+
if (!role) {
|
|
574
|
+
rolesTruncated = true;
|
|
575
|
+
continue;
|
|
576
|
+
}
|
|
577
|
+
if (roles.includes(role))
|
|
414
578
|
continue;
|
|
415
579
|
roles.push(role);
|
|
416
580
|
if (roles.length >= MAX_ENTITY_ROLES)
|
|
417
581
|
break;
|
|
418
582
|
}
|
|
419
583
|
}
|
|
584
|
+
const handle = boundedString(record.handle, 200);
|
|
585
|
+
const handleTruncated = record.handle !== undefined && handle === null;
|
|
420
586
|
const names = normalizeContactValues(record.vcardArray, 'fn', 300);
|
|
421
587
|
const organizations = normalizeContactValues(record.vcardArray, 'org', 300);
|
|
422
588
|
const emails = normalizeContactValues(record.vcardArray, 'email', 320, { lower: true });
|
|
@@ -425,8 +591,10 @@ function summarizeEntity(entity) {
|
|
|
425
591
|
const vcardEntries = Array.isArray(record.vcardArray) && Array.isArray(record.vcardArray[1])
|
|
426
592
|
? record.vcardArray[1]
|
|
427
593
|
: [];
|
|
594
|
+
const publicIds = normalizePublicIds(record.publicIds);
|
|
595
|
+
const links = normalizeLinks(record.links, MAX_ENTITY_LINKS);
|
|
428
596
|
const summary = {
|
|
429
|
-
handle
|
|
597
|
+
handle,
|
|
430
598
|
roles,
|
|
431
599
|
name: names[0] || null,
|
|
432
600
|
names,
|
|
@@ -438,13 +606,15 @@ function summarizeEntity(entity) {
|
|
|
438
606
|
phones,
|
|
439
607
|
address: addresses[0] || null,
|
|
440
608
|
addresses,
|
|
441
|
-
publicIds:
|
|
442
|
-
links:
|
|
443
|
-
truncated: Boolean(
|
|
609
|
+
publicIds: publicIds.items,
|
|
610
|
+
links: links.items,
|
|
611
|
+
truncated: Boolean(rolesTruncated
|
|
612
|
+
|| handleTruncated
|
|
444
613
|
|| vcardEntries.length > MAX_VCARD_ENTRIES
|
|
614
|
+
|| malformedVcardEvidence(record.vcardArray)
|
|
445
615
|
|| contactValuesTruncated(record.vcardArray)
|
|
446
|
-
||
|
|
447
|
-
||
|
|
616
|
+
|| publicIds.truncated
|
|
617
|
+
|| links.truncated),
|
|
448
618
|
};
|
|
449
619
|
const hasAny = Boolean(summary.handle || summary.name || summary.org || summary.email
|
|
450
620
|
|| summary.phone || summary.address || summary.publicIds.length || summary.links.length);
|
|
@@ -453,7 +623,8 @@ function summarizeEntity(entity) {
|
|
|
453
623
|
function summarizeEntities(entities) {
|
|
454
624
|
const summaries = [];
|
|
455
625
|
const source = Array.isArray(entities) ? entities : [];
|
|
456
|
-
let truncated =
|
|
626
|
+
let truncated = entities !== undefined && !Array.isArray(entities)
|
|
627
|
+
|| source.length > MAX_RDAP_ENTITIES;
|
|
457
628
|
const stack = source
|
|
458
629
|
.slice(0, MAX_RDAP_ENTITIES)
|
|
459
630
|
.reverse()
|
|
@@ -465,15 +636,26 @@ function summarizeEntities(entities) {
|
|
|
465
636
|
if (!current)
|
|
466
637
|
continue;
|
|
467
638
|
const { entity, depth } = current;
|
|
468
|
-
if (!entity || typeof entity !== 'object' || Array.isArray(entity) || seen.has(entity))
|
|
639
|
+
if (!entity || typeof entity !== 'object' || Array.isArray(entity) || seen.has(entity)) {
|
|
640
|
+
truncated = true;
|
|
469
641
|
continue;
|
|
642
|
+
}
|
|
470
643
|
seen.add(entity);
|
|
471
644
|
visited += 1;
|
|
472
645
|
const summary = summarizeEntity(entity);
|
|
473
646
|
if (summary)
|
|
474
647
|
summaries.push(summary);
|
|
648
|
+
else
|
|
649
|
+
truncated = true;
|
|
475
650
|
const record = entity;
|
|
476
|
-
if (depth >= MAX_RDAP_ENTITY_DEPTH
|
|
651
|
+
if (depth >= MAX_RDAP_ENTITY_DEPTH) {
|
|
652
|
+
if (Array.isArray(record.entities) && record.entities.length)
|
|
653
|
+
truncated = true;
|
|
654
|
+
continue;
|
|
655
|
+
}
|
|
656
|
+
if (record.entities !== undefined && !Array.isArray(record.entities))
|
|
657
|
+
truncated = true;
|
|
658
|
+
if (!Array.isArray(record.entities))
|
|
477
659
|
continue;
|
|
478
660
|
const remaining = Math.max(0, MAX_RDAP_ENTITIES - visited - stack.length);
|
|
479
661
|
const nested = record.entities.slice(0, remaining);
|
|
@@ -519,6 +701,8 @@ function parseRdapObject(type, data) {
|
|
|
519
701
|
const redactionInfo = normalizeRedactions(data.redacted);
|
|
520
702
|
const noticesInfo = summarizeTextBlocks(data.notices);
|
|
521
703
|
const remarksInfo = summarizeTextBlocks(data.remarks);
|
|
704
|
+
const linksInfo = normalizeLinks(data.links);
|
|
705
|
+
const conformanceInfo = normalizeStringList(data.rdapConformance, 50, 160, { lower: true });
|
|
522
706
|
const serverTruncationReasons = summarizeServerTruncation(data.notices, data.remarks);
|
|
523
707
|
// Preserve the established status-array contract while making it available
|
|
524
708
|
// to every RDAP object type. Unlike set-like fields, status order and
|
|
@@ -528,16 +712,18 @@ function parseRdapObject(type, data) {
|
|
|
528
712
|
.map((status) => boundedString(status, 160))
|
|
529
713
|
.filter((status) => status !== null)
|
|
530
714
|
: [];
|
|
715
|
+
const inspectedStatusCount = Array.isArray(data.status) ? Math.min(data.status.length, 100) : 0;
|
|
716
|
+
const inspectedEventCount = Array.isArray(data.events) ? Math.min(data.events.length, 100) : 0;
|
|
531
717
|
const common = {
|
|
532
718
|
objectClassName: boundedString(data.objectClassName, 80, { lower: true }),
|
|
533
719
|
language: boundedString(data.lang, 35, { lower: true }),
|
|
534
|
-
conformance:
|
|
535
|
-
conformanceTruncated:
|
|
720
|
+
conformance: conformanceInfo.items,
|
|
721
|
+
conformanceTruncated: conformanceInfo.truncated,
|
|
536
722
|
...redactionInfo,
|
|
537
723
|
port43: boundedString(data.port43, 300),
|
|
538
724
|
parentHandle: boundedString(data.parentHandle, 300),
|
|
539
|
-
links:
|
|
540
|
-
linksTruncated:
|
|
725
|
+
links: linksInfo.items,
|
|
726
|
+
linksTruncated: linksInfo.truncated,
|
|
541
727
|
notices: noticesInfo.items,
|
|
542
728
|
noticesTruncated: noticesInfo.truncated,
|
|
543
729
|
remarks: remarksInfo.items,
|
|
@@ -545,25 +731,42 @@ function parseRdapObject(type, data) {
|
|
|
545
731
|
serverTruncated: serverTruncationReasons.length > 0,
|
|
546
732
|
serverTruncationReasons,
|
|
547
733
|
statuses,
|
|
548
|
-
statusesTruncated:
|
|
734
|
+
statusesTruncated: data.status !== undefined && (!Array.isArray(data.status)
|
|
735
|
+
|| data.status.length > 100
|
|
736
|
+
|| statuses.length < inspectedStatusCount),
|
|
549
737
|
events,
|
|
550
|
-
eventsTruncated:
|
|
738
|
+
eventsTruncated: data.events !== undefined && (!Array.isArray(data.events)
|
|
739
|
+
|| data.events.length > 100
|
|
740
|
+
|| events.length < inspectedEventCount),
|
|
551
741
|
lifecycle: summarizeLifecycle(events),
|
|
552
742
|
};
|
|
553
743
|
if (type === 'domain') {
|
|
554
744
|
const { entitiesByRole, entitiesTruncated, truncatedEntityRoles } = entityInventory(data.entities);
|
|
555
745
|
const registrarEntity = entitiesByRole.registrar && entitiesByRole.registrar[0];
|
|
556
746
|
let nameserverAddressesTruncated = false;
|
|
747
|
+
let invalidNameservers = 0;
|
|
557
748
|
const nameserverDetails = Array.isArray(data.nameservers)
|
|
558
749
|
? data.nameservers.slice(0, 200).map((ns) => {
|
|
559
|
-
if (!ns || typeof ns !== 'object' || Array.isArray(ns))
|
|
750
|
+
if (!ns || typeof ns !== 'object' || Array.isArray(ns)) {
|
|
751
|
+
invalidNameservers += 1;
|
|
560
752
|
return null;
|
|
753
|
+
}
|
|
561
754
|
const nameserver = ns;
|
|
562
|
-
const
|
|
755
|
+
const validIpAddresses = nameserver.ipAddresses !== null
|
|
756
|
+
&& typeof nameserver.ipAddresses === 'object'
|
|
757
|
+
&& !Array.isArray(nameserver.ipAddresses);
|
|
758
|
+
const ipAddresses = validIpAddresses
|
|
563
759
|
? nameserver.ipAddresses
|
|
564
760
|
: {};
|
|
761
|
+
if (nameserver.ipAddresses !== undefined && !validIpAddresses) {
|
|
762
|
+
nameserverAddressesTruncated = true;
|
|
763
|
+
}
|
|
565
764
|
const v4 = Array.isArray(ipAddresses.v4) ? ipAddresses.v4 : [];
|
|
566
765
|
const v6 = Array.isArray(ipAddresses.v6) ? ipAddresses.v6 : [];
|
|
766
|
+
if (ipAddresses.v4 !== undefined && !Array.isArray(ipAddresses.v4)
|
|
767
|
+
|| ipAddresses.v6 !== undefined && !Array.isArray(ipAddresses.v6)) {
|
|
768
|
+
nameserverAddressesTruncated = true;
|
|
769
|
+
}
|
|
567
770
|
const maximumCandidates = 200;
|
|
568
771
|
const maximumAddresses = 20;
|
|
569
772
|
const addresses = [];
|
|
@@ -576,6 +779,8 @@ function parseRdapObject(type, data) {
|
|
|
576
779
|
const address = boundedString(candidates[index], 80);
|
|
577
780
|
if (address && net.isIP(address) === family)
|
|
578
781
|
addresses.push(address);
|
|
782
|
+
else
|
|
783
|
+
nameserverAddressesTruncated = true;
|
|
579
784
|
}
|
|
580
785
|
if (visited >= maximumCandidates || addresses.length >= maximumAddresses)
|
|
581
786
|
break;
|
|
@@ -583,8 +788,13 @@ function parseRdapObject(type, data) {
|
|
|
583
788
|
if (v4.length + v6.length > visited || v4.length + v6.length > maximumAddresses) {
|
|
584
789
|
nameserverAddressesTruncated = true;
|
|
585
790
|
}
|
|
791
|
+
const name = boundedString(nameserver.ldhName || nameserver.unicodeName, 253);
|
|
792
|
+
if (!name) {
|
|
793
|
+
invalidNameservers += 1;
|
|
794
|
+
return null;
|
|
795
|
+
}
|
|
586
796
|
return {
|
|
587
|
-
name
|
|
797
|
+
name,
|
|
588
798
|
addresses,
|
|
589
799
|
};
|
|
590
800
|
}).filter((ns) => Boolean(ns?.name))
|
|
@@ -595,10 +805,13 @@ function parseRdapObject(type, data) {
|
|
|
595
805
|
? data.secureDNS
|
|
596
806
|
: null;
|
|
597
807
|
const variantInfo = normalizeDomainVariants(data.variants);
|
|
808
|
+
let invalidDsData = 0;
|
|
598
809
|
const dsData = secureDns && Array.isArray(secureDns.dsData)
|
|
599
810
|
? secureDns.dsData.slice(0, 50).map((ds) => {
|
|
600
|
-
if (!ds || typeof ds !== 'object' || Array.isArray(ds))
|
|
811
|
+
if (!ds || typeof ds !== 'object' || Array.isArray(ds)) {
|
|
812
|
+
invalidDsData += 1;
|
|
601
813
|
return null;
|
|
814
|
+
}
|
|
602
815
|
const record = ds;
|
|
603
816
|
const digest = boundedString(record.digest, 512);
|
|
604
817
|
const normalized = {
|
|
@@ -606,9 +819,11 @@ function parseRdapObject(type, data) {
|
|
|
606
819
|
digestType: boundedInteger(record.digestType, 0, 255),
|
|
607
820
|
digest: digest && digest.length % 2 === 0 && /^[0-9a-f]+$/i.test(digest) ? digest : null,
|
|
608
821
|
};
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
822
|
+
if (Object.values(normalized).every((value) => value !== null)) {
|
|
823
|
+
return normalized;
|
|
824
|
+
}
|
|
825
|
+
invalidDsData += 1;
|
|
826
|
+
return null;
|
|
612
827
|
}).filter((value) => value !== null)
|
|
613
828
|
: [];
|
|
614
829
|
return {
|
|
@@ -619,7 +834,9 @@ function parseRdapObject(type, data) {
|
|
|
619
834
|
handle: boundedString(data.handle, 300),
|
|
620
835
|
nameservers: nameserverDetails.map((ns) => ns.name),
|
|
621
836
|
nameserverDetails,
|
|
622
|
-
nameserversTruncated:
|
|
837
|
+
nameserversTruncated: data.nameservers !== undefined && (!Array.isArray(data.nameservers)
|
|
838
|
+
|| data.nameservers.length > 200
|
|
839
|
+
|| invalidNameservers > 0),
|
|
623
840
|
nameserverAddressesTruncated,
|
|
624
841
|
dnssec: secureDns && secureDns.delegationSigned === true
|
|
625
842
|
? 'Signed' : secureDns && secureDns.delegationSigned === false ? 'Unsigned' : 'Unknown',
|
|
@@ -627,7 +844,10 @@ function parseRdapObject(type, data) {
|
|
|
627
844
|
delegationSigned: secureDns && typeof secureDns.delegationSigned === 'boolean'
|
|
628
845
|
? secureDns.delegationSigned : null,
|
|
629
846
|
dsData,
|
|
630
|
-
dsDataTruncated: Boolean(
|
|
847
|
+
dsDataTruncated: Boolean(data.secureDNS !== undefined && !secureDns
|
|
848
|
+
|| secureDns && secureDns.dsData !== undefined && (!Array.isArray(secureDns.dsData)
|
|
849
|
+
|| secureDns.dsData.length > 50
|
|
850
|
+
|| invalidDsData > 0)),
|
|
631
851
|
...variantInfo,
|
|
632
852
|
registrarIanaId: publicId(registrarEntity, /iana registrar id/i),
|
|
633
853
|
entitiesByRole,
|
|
@@ -643,17 +863,26 @@ function parseRdapObject(type, data) {
|
|
|
643
863
|
}
|
|
644
864
|
if (type === 'ipv4' || type === 'ipv6') {
|
|
645
865
|
const { entitiesByRole, entitiesTruncated, truncatedEntityRoles } = entityInventory(data.entities);
|
|
866
|
+
let invalidCidrs = 0;
|
|
646
867
|
const cidrs = Array.isArray(data.cidr0_cidrs)
|
|
647
868
|
? data.cidr0_cidrs.slice(0, 200)
|
|
648
869
|
.map((c) => {
|
|
649
|
-
if (!c || typeof c !== 'object' || Array.isArray(c))
|
|
870
|
+
if (!c || typeof c !== 'object' || Array.isArray(c)) {
|
|
871
|
+
invalidCidrs += 1;
|
|
650
872
|
return null;
|
|
873
|
+
}
|
|
651
874
|
const expectedFamily = type === 'ipv4' ? 4 : 6;
|
|
652
875
|
const prefix = boundedString(expectedFamily === 4 ? c.v4prefix : c.v6prefix, 80);
|
|
653
|
-
if (!prefix || net.isIP(prefix) !== expectedFamily)
|
|
876
|
+
if (!prefix || net.isIP(prefix) !== expectedFamily) {
|
|
877
|
+
invalidCidrs += 1;
|
|
654
878
|
return null;
|
|
879
|
+
}
|
|
655
880
|
const length = boundedInteger(c.length, 0, expectedFamily === 4 ? 32 : 128);
|
|
656
|
-
|
|
881
|
+
if (length === null) {
|
|
882
|
+
invalidCidrs += 1;
|
|
883
|
+
return null;
|
|
884
|
+
}
|
|
885
|
+
return `${prefix}/${length}`;
|
|
657
886
|
})
|
|
658
887
|
.filter((value) => value !== null)
|
|
659
888
|
: [];
|
|
@@ -664,7 +893,9 @@ function parseRdapObject(type, data) {
|
|
|
664
893
|
startAddress: boundedString(data.startAddress, 80),
|
|
665
894
|
endAddress: boundedString(data.endAddress, 80),
|
|
666
895
|
cidrs,
|
|
667
|
-
cidrsTruncated:
|
|
896
|
+
cidrsTruncated: data.cidr0_cidrs !== undefined && (!Array.isArray(data.cidr0_cidrs)
|
|
897
|
+
|| data.cidr0_cidrs.length > 200
|
|
898
|
+
|| invalidCidrs > 0),
|
|
668
899
|
country: boundedString(data.country, 2),
|
|
669
900
|
networkType: boundedString(data.type, 160),
|
|
670
901
|
entitiesByRole,
|