@slicedearth/whoisleuth-cli 1.45.0 → 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/DISCLOSURE +7 -3
- package/NOTICE +5 -4
- package/README.md +74 -124
- package/SECURITY.md +6 -3
- package/bin/whoisleuth.mjs +7 -4
- package/cli/archive-inspect.mjs +28 -10
- package/cli/arguments.mjs +450 -173
- package/cli/artifact-structure.mjs +2507 -0
- package/cli/artifact-verify.mjs +286 -64
- package/cli/bounded-json.mjs +1 -0
- package/cli/bulk-checkpoint.mjs +35 -23
- package/cli/bulk-command-runner.mjs +2 -5
- package/cli/bulk.mjs +9 -2
- package/cli/case-pack.mjs +435 -46
- 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 +948 -106
- package/cli/completion.mjs +504 -218
- package/cli/config-profile.mjs +13 -3
- package/cli/ct-event-intake.mjs +32 -2
- package/cli/discover.mjs +7 -1
- package/cli/discovery-observation-snapshot.mjs +24 -28
- package/cli/discovery-scan-command-runner.mjs +13 -14
- package/cli/discovery-scan.mjs +17 -3
- package/cli/discovery-snapshot.mjs +15 -7
- package/cli/discovery-workflow.mjs +2 -3
- package/cli/doctor.mjs +2 -2
- package/cli/domain-control-monitor.mjs +215 -36
- package/cli/domain-control-observations.mjs +34 -17
- package/cli/errors.mjs +8 -3
- package/cli/evidence-command-runner.mjs +17 -11
- package/cli/evidence-signing.mjs +68 -50
- package/cli/export-evidence.mjs +11 -4
- package/cli/external-observation-mapping.mjs +3 -3
- package/cli/formatters/html.mjs +3 -3
- package/cli/formatters/json.mjs +20 -32
- package/cli/formatters/terminal.mjs +586 -22
- package/cli/input.mjs +62 -0
- package/cli/interactive-launcher.mjs +226 -0
- package/cli/interchange-report.mjs +226 -0
- package/cli/investigation-manifest.mjs +16 -17
- package/cli/investigation-plan.mjs +167 -16
- package/cli/investigation-run.mjs +80 -24
- package/cli/local-mmdb-review.mjs +45 -14
- 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 +96 -33
- package/cli/lookup-diff.mjs +11 -2
- package/cli/lookup-plan.mjs +1 -0
- package/cli/mail-review.mjs +206 -94
- package/cli/manual.mjs +5 -5
- package/cli/network-command-runner.mjs +78 -0
- package/cli/offline-evidence-review.mjs +45 -14
- package/cli/open-asset-model-bridge.mjs +1 -1
- package/cli/output-file.mjs +70 -14
- package/cli/page-compare.mjs +18 -10
- package/cli/progress-events.mjs +2 -1
- package/cli/progress.mjs +3 -3
- package/cli/registry-cohort.mjs +343 -61
- package/cli/registry-doctor.mjs +67 -28
- package/cli/registry-support.mjs +2 -1
- package/cli/retained-artifact-diff.mjs +375 -0
- package/cli/risk-calibration.mjs +89 -40
- package/cli/runner.mjs +322 -246
- package/cli/saved-lookup.mjs +439 -49
- package/cli/sharing-review.mjs +18 -7
- package/cli/source-reliability.mjs +75 -36
- package/cli/terminal-presentation.mjs +43 -18
- package/docs/cli-reference.md +125 -1334
- package/docs/cli.md +87 -236
- package/frontend/src/lib/analysis/domain-control-manifest-core.js +3 -0
- package/frontend/src/lib/analysis/domain-control-records.js +3 -0
- package/lib/application-version.mjs +3 -0
- package/lib/availability.mjs +72 -52
- package/lib/bounded-contract-normalizers.mjs +2 -4
- package/lib/bounded-file.mjs +91 -4
- package/lib/bounded-json.mjs +287 -0
- package/lib/browser-library-profile.mjs +1 -1
- package/lib/certificate-extension-profile.mjs +2 -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 +173 -0
- package/lib/ct-query.mjs +3 -3
- package/lib/ct-search.mjs +120 -64
- package/lib/distributed-operation-budget.mjs +4 -2
- package/lib/dns-delegation-health.mjs +9 -6
- package/lib/dns-intelligence.mjs +4 -3
- package/lib/dns-resolver-selection.mjs +6 -4
- package/lib/dnssec-chain-validation.mjs +1052 -0
- package/lib/dnssec-evidence-validation.mjs +23 -10
- package/lib/domain-assurance.mjs +12 -12
- package/lib/domain-change-packet.mjs +7 -5
- package/lib/domain-control-flight-recorder.mjs +442 -78
- package/lib/domain-control-manifest.mjs +314 -171
- package/lib/domain-posture-analysis.mjs +5 -2
- package/lib/domain-posture.mjs +7 -4
- package/lib/encrypted-dns-contract.mjs +2 -3
- package/lib/evidence-export.mjs +1243 -88
- package/lib/evidence-report-markdown.mjs +3 -0
- package/lib/evidence-report.mjs +5 -0
- package/lib/external-intelligence-risk.mjs +26 -10
- package/lib/favicon.mjs +8 -5
- 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-evidence-bounds.mjs +3 -0
- package/lib/http-intelligence.mjs +332 -6
- package/lib/interchange-fidelity-registry.mjs +124 -0
- package/lib/local-geoip-evidence.mjs +2 -1
- package/lib/lookup-cache.mjs +6 -4
- package/lib/lookup-network-evidence-bounds.mjs +9 -0
- package/lib/lookup.mjs +27 -4
- package/lib/observed-network-context.mjs +1 -1
- package/lib/outbound-identity.mjs +2 -3
- package/lib/page-fingerprints.mjs +2 -2
- package/lib/page-role-profile.mjs +1 -1
- package/lib/perceptual-hash.mjs +12 -4
- package/lib/portable-generator.mjs +29 -0
- package/lib/provider-policy-admission.mjs +11 -4
- package/lib/rdap-bootstrap.mjs +76 -16
- package/lib/rdap-client.mjs +20 -12
- package/lib/rdap-normalization.mjs +324 -80
- package/lib/rdap-registrar.mjs +5 -1
- 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-capabilities.mjs +3 -0
- package/lib/registry-capability-catalogue.mjs +12 -2
- package/lib/registry-comparison.mjs +28 -11
- package/lib/registry-insights.mjs +7 -0
- package/lib/response-policy.mjs +27 -4
- package/lib/risk-calibration-summary.mjs +340 -0
- package/lib/risk-scoring.mjs +4 -3
- package/lib/rpki-evidence.mjs +9 -8
- package/lib/safe-fetch.mjs +121 -50
- package/lib/security-txt.mjs +27 -4
- package/lib/service-binding-dns.mjs +6 -1
- package/lib/smtp-transport-review.mjs +787 -0
- package/lib/sslbl-certificates.generated.mjs +136 -134
- 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 +5 -15
- package/lib/threat-intelligence-types.mjs +21 -1
- package/lib/threatfox-intelligence.mjs +7 -3
- package/lib/tls-intelligence.mjs +15 -9
- package/lib/tlsa-evidence.mjs +37 -7
- 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/packages/cases/case-brand-profile-references.mjs +85 -0
- package/{frontend/src/lib/analysis/case-evidence-model.js → packages/cases/case-evidence-model.mjs} +43 -14
- package/packages/cases/case-investigation-branch-model.mjs +132 -0
- 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 -19
- 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} +106 -33
- package/{frontend/src/lib/analysis/case-report.js → packages/cases/case-report.mjs} +143 -16
- 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/packages/cases/opaque-reference-id.mjs +9 -0
- package/packages/comparison/comparison-ledger-bulk.mjs +366 -0
- package/packages/comparison/comparison-ledger-contract.mjs +241 -0
- package/packages/comparison/comparison-ledger-json.mjs +80 -0
- package/packages/comparison/comparison-ledger-serialization.mjs +52 -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/packages/evidence/artifact-integrity.mjs +65 -0
- 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} +86 -20
- package/packages/interchange/retained-text.mjs +8 -0
- package/{frontend/src/lib/analysis/acquisition-decision-packet.js → packages/investigation/acquisition-decision-packet.mjs} +11 -8
- package/{frontend/src/lib/analysis/bulk-domain-comparison.js → packages/investigation/bulk-domain-comparison.mjs} +28 -9
- package/{frontend/src/lib/analysis/bulk-mail-exposure.js → packages/investigation/bulk-mail-exposure.mjs} +67 -18
- package/{frontend/src/lib/analysis/bulk-review-export.js → packages/investigation/bulk-review-export.mjs} +10 -9
- package/packages/investigation/bulk-review-policy.mjs +1 -0
- package/{frontend/src/lib/analysis/investigation-capsule.js → packages/investigation/investigation-capsule.mjs} +50 -18
- 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/packages/investigation/lookup-claim-passport.mjs +232 -0
- package/packages/investigation/lookup-claim-readiness.mjs +287 -0
- 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} +97 -50
- package/{frontend/src/lib/analysis/bulk-review-model.js → packages/workspace/bulk-review-model.mjs} +15 -15
- package/{frontend/src/lib/analysis/bulk-session-model.js → packages/workspace/bulk-session-model.mjs} +179 -39
- package/{frontend/src/lib/analysis/bulk-sort.js → packages/workspace/bulk-sort.mjs} +12 -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} +51 -46
- package/{frontend/src/lib/analysis/investigation-template-model.js → packages/workspace/investigation-template-model.mjs} +15 -15
- 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} +18 -12
- 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} +195 -84
- package/third-party-notices.txt +112 -5834
- package/frontend/src/lib/analysis/artifact-integrity.js +0 -21
- 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 -253
- package/frontend/src/lib/analysis/case-record-model.js +0 -7
- package/frontend/src/lib/analysis/case-response-model.js +0 -669
- package/frontend/src/lib/analysis/case-response-packet.js +0 -573
- package/frontend/src/lib/analysis/lookup-decision-support.js +0 -487
- package/frontend/src/lib/analysis/lookup-investigation-brief.js +0 -153
- package/frontend/src/lib/analysis/lookup-presentation.js +0 -58
- 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 -232
- 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 -88
- package/frontend/src/lib/analysis/visualization-models.js +0 -369
- package/frontend/src/lib/analysis/visualization-time-series.js +0 -144
- package/lib/bounded-json-response.mjs +0 -157
- package/lib/lookup-response-contract.mjs +0 -346
- package/lib/observation.mjs +0 -114
- /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,7 @@
|
|
|
1
1
|
import { Buffer } from 'node:buffer';
|
|
2
2
|
import { createHash } from 'node:crypto';
|
|
3
3
|
import { domainToASCII } from 'node:url';
|
|
4
|
+
import { normalizeLegacyIsoTimestamp } from "../packages/evidence/observation.mjs";
|
|
4
5
|
const DNSSEC_EVIDENCE_SCHEMA = 'whoisleuth.dnssec-evidence-validation';
|
|
5
6
|
const DNSSEC_EVIDENCE_VERSION = 1;
|
|
6
7
|
const MAX_DNSSEC_RECORDS = 50;
|
|
@@ -99,20 +100,25 @@ function dsDigest(owner, dnskey, digestType) {
|
|
|
99
100
|
}
|
|
100
101
|
function normalizeRrsig(value) {
|
|
101
102
|
const source = record(value);
|
|
102
|
-
if (!source
|
|
103
|
+
if (!source)
|
|
104
|
+
return null;
|
|
105
|
+
const normalizedInception = normalizeLegacyIsoTimestamp(source.inception);
|
|
106
|
+
const normalizedExpiration = normalizeLegacyIsoTimestamp(source.expiration);
|
|
107
|
+
if (!normalizedInception || !normalizedExpiration)
|
|
103
108
|
return null;
|
|
104
|
-
const inception = Date.parse(
|
|
105
|
-
const expiration = Date.parse(
|
|
106
|
-
if (
|
|
109
|
+
const inception = Date.parse(normalizedInception);
|
|
110
|
+
const expiration = Date.parse(normalizedExpiration);
|
|
111
|
+
if (inception > expiration)
|
|
107
112
|
return null;
|
|
108
113
|
return Object.freeze({ inception, expiration });
|
|
109
114
|
}
|
|
110
115
|
function signatureTimeState(value, observedAt) {
|
|
111
|
-
if (!value.length
|
|
116
|
+
if (!value.length)
|
|
112
117
|
return 'unavailable';
|
|
113
|
-
const
|
|
114
|
-
if (!
|
|
118
|
+
const normalizedObservedAt = normalizeLegacyIsoTimestamp(observedAt);
|
|
119
|
+
if (!normalizedObservedAt)
|
|
115
120
|
return 'unavailable';
|
|
121
|
+
const observed = Date.parse(normalizedObservedAt);
|
|
116
122
|
let withinWindow = 0;
|
|
117
123
|
for (const item of value) {
|
|
118
124
|
if (observed >= item.inception && observed <= item.expiration)
|
|
@@ -160,7 +166,8 @@ function validateDnssecEvidence(input) {
|
|
|
160
166
|
limitations: Object.freeze(limitations),
|
|
161
167
|
});
|
|
162
168
|
}
|
|
163
|
-
|
|
169
|
+
const incompleteInput = rejectedCount > 0 || truncated;
|
|
170
|
+
if (input.delegationSigned === false && dsRecords.length === 0 && !incompleteInput) {
|
|
164
171
|
findings.push('The supplied delegation state was unsigned and no DS records were supplied.');
|
|
165
172
|
return Object.freeze({
|
|
166
173
|
schema: DNSSEC_EVIDENCE_SCHEMA,
|
|
@@ -191,18 +198,24 @@ function validateDnssecEvidence(input) {
|
|
|
191
198
|
if (matched)
|
|
192
199
|
matchedDsCount += 1;
|
|
193
200
|
}
|
|
194
|
-
const
|
|
201
|
+
const observedConflict = input.delegationSigned === false && dsRecords.length > 0
|
|
195
202
|
|| input.delegationSigned === true && dsRecords.length === 0 && !truncated
|
|
196
203
|
|| signatureState === 'outside_window'
|
|
197
204
|
|| (dsRecords.length > 0 && dnskeys.length > 0 && matchedDsCount === 0 && unsupportedDigestCount < dsRecords.length);
|
|
205
|
+
const definiteConflict = observedConflict && !incompleteInput;
|
|
198
206
|
if (definiteConflict)
|
|
199
207
|
findings.push('The supplied delegation, signature-time, DS, or DNSKEY evidence conflicts.');
|
|
208
|
+
else if (observedConflict)
|
|
209
|
+
findings.push('The retained subset contains conflicting evidence, but omitted or rejected records prevent a definitive conflict conclusion.');
|
|
200
210
|
if (matchedDsCount > 0)
|
|
201
211
|
findings.push(`${matchedDsCount} supplied DS record(s) matched a supplied DNSKEY.`);
|
|
202
212
|
if (unsupportedDigestCount > 0)
|
|
203
213
|
findings.push(`${unsupportedDigestCount} DS digest type(s) were retained as unsupported.`);
|
|
204
214
|
if (signatureState === 'mixed')
|
|
205
215
|
findings.push('The supplied RRSIG validity windows were mixed at the observation time.');
|
|
216
|
+
if (input.delegationSigned === false && dsRecords.length === 0 && incompleteInput) {
|
|
217
|
+
findings.push('The retained subset contains no usable DS record, but omitted or rejected records prevent a definitive unsigned conclusion.');
|
|
218
|
+
}
|
|
206
219
|
if (rejectedCount > 0)
|
|
207
220
|
findings.push(`${rejectedCount} malformed record(s) were rejected.`);
|
|
208
221
|
if (truncated)
|
|
@@ -210,7 +223,7 @@ function validateDnssecEvidence(input) {
|
|
|
210
223
|
const completeLocalRelationship = dsRecords.length > 0
|
|
211
224
|
&& dnskeys.length > 0
|
|
212
225
|
&& matchedDsCount > 0
|
|
213
|
-
&& !
|
|
226
|
+
&& !observedConflict
|
|
214
227
|
&& rejectedCount === 0
|
|
215
228
|
&& !truncated
|
|
216
229
|
&& unsupportedDigestCount === 0
|
package/lib/domain-assurance.mjs
CHANGED
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
import { domainToASCII } from 'node:url';
|
|
2
2
|
import { exactKeys } from "./bounded-contract-normalizers.mjs";
|
|
3
|
+
import { normalizeExplicitIsoTimestamp } from "../packages/evidence/observation.mjs";
|
|
3
4
|
const DOMAIN_ASSURANCE_INPUT_SCHEMA = 'whoisleuth.domain-assurance.input';
|
|
4
5
|
const DOMAIN_ASSURANCE_SCHEMA = 'whoisleuth.domain-assurance';
|
|
5
6
|
const DOMAIN_ASSURANCE_VERSION = 2;
|
|
6
|
-
const DOMAIN_ASSURANCE_SUPPORTED_INPUT_VERSIONS = Object.freeze([
|
|
7
|
+
const DOMAIN_ASSURANCE_SUPPORTED_INPUT_VERSIONS = Object.freeze([DOMAIN_ASSURANCE_VERSION]);
|
|
7
8
|
const MAX_ASSURANCE_INPUT_BYTES = 2 * 1024 * 1024;
|
|
8
9
|
const ROOT_KEYS = Object.freeze({
|
|
9
10
|
'planned-change': new Set(['schema', 'version', 'kind', 'domain', 'change']),
|
|
@@ -45,9 +46,10 @@ function optionalText(value, maximum = 160) {
|
|
|
45
46
|
}
|
|
46
47
|
function timestamp(value, field) {
|
|
47
48
|
const normalized = text(value, field, 64);
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
49
|
+
const canonical = normalizeExplicitIsoTimestamp(normalized);
|
|
50
|
+
if (canonical)
|
|
51
|
+
return canonical;
|
|
52
|
+
throw new Error(`${field} must be an ISO timestamp with an explicit timezone.`);
|
|
51
53
|
}
|
|
52
54
|
function optionalTimestamp(value, field) {
|
|
53
55
|
if (value === null || value === undefined || value === '')
|
|
@@ -252,7 +254,7 @@ const RETIREMENT_CHECKS = Object.freeze([
|
|
|
252
254
|
['delegatedDnsMonitored', 'Delegated DNS remains monitored during retirement', true],
|
|
253
255
|
['reRegistrationPrevented', 'Registration continuity or defensive renewal is planned', true],
|
|
254
256
|
]);
|
|
255
|
-
function buildRetirement(input
|
|
257
|
+
function buildRetirement(input) {
|
|
256
258
|
const checksInput = record(input.checks);
|
|
257
259
|
exactKeys(checksInput, new Set(RETIREMENT_CHECKS.map(([id]) => id)), 'checks');
|
|
258
260
|
const fixedChecks = RETIREMENT_CHECKS.map(([id, checkLabel, expected]) => {
|
|
@@ -266,7 +268,7 @@ function buildRetirement(input, inputVersion) {
|
|
|
266
268
|
});
|
|
267
269
|
const fixedIds = new Set(RETIREMENT_CHECKS.map(([id]) => id));
|
|
268
270
|
const customIds = new Set();
|
|
269
|
-
const customChecks =
|
|
271
|
+
const customChecks = input.customChecks !== undefined
|
|
270
272
|
? boundedArray(input.customChecks, 'customChecks', 0, 20).map((raw, index) => {
|
|
271
273
|
const item = record(raw);
|
|
272
274
|
exactKeys(item, CUSTOM_RETIREMENT_CHECK_KEYS, `customChecks[${index}]`);
|
|
@@ -304,18 +306,16 @@ function buildRetirement(input, inputVersion) {
|
|
|
304
306
|
}
|
|
305
307
|
function buildDomainAssurance(inputRaw, generatedAt = new Date().toISOString()) {
|
|
306
308
|
const input = record(inputRaw);
|
|
307
|
-
if (input.schema !== DOMAIN_ASSURANCE_INPUT_SCHEMA
|
|
308
|
-
|
|
309
|
-
throw new Error(`Domain assurance input must use ${DOMAIN_ASSURANCE_INPUT_SCHEMA} version 1 or ${DOMAIN_ASSURANCE_VERSION}.`);
|
|
309
|
+
if (input.schema !== DOMAIN_ASSURANCE_INPUT_SCHEMA || input.version !== DOMAIN_ASSURANCE_VERSION) {
|
|
310
|
+
throw new Error(`Domain assurance input must use ${DOMAIN_ASSURANCE_INPUT_SCHEMA} version ${DOMAIN_ASSURANCE_VERSION}.`);
|
|
310
311
|
}
|
|
311
|
-
const inputVersion = input.version;
|
|
312
312
|
const kind = enumValue(input.kind, 'kind', ['planned-change', 'recovery-dependencies', 'retirement']);
|
|
313
|
-
exactKeys(input, kind === 'retirement'
|
|
313
|
+
exactKeys(input, kind === 'retirement' ? RETIREMENT_ROOT_KEYS_V2 : ROOT_KEYS[kind], 'Domain assurance input');
|
|
314
314
|
const result = kind === 'planned-change'
|
|
315
315
|
? buildPlannedChange(input)
|
|
316
316
|
: kind === 'recovery-dependencies'
|
|
317
317
|
? buildRecoveryDependencies(input)
|
|
318
|
-
: buildRetirement(input
|
|
318
|
+
: buildRetirement(input);
|
|
319
319
|
return {
|
|
320
320
|
schema: DOMAIN_ASSURANCE_SCHEMA,
|
|
321
321
|
version: DOMAIN_ASSURANCE_VERSION,
|
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
import { exactKeys, requireBoundedString, requireDomainName, requireIsoTimestamp, requireRecord, } from "./bounded-contract-normalizers.mjs";
|
|
2
2
|
import { DOMAIN_ASSURANCE_INPUT_SCHEMA, buildDomainAssurance, } from "./domain-assurance.mjs";
|
|
3
3
|
import { DOMAIN_CHANGE_INPUT_SCHEMA, reviewDomainChange, } from "./domain-change-review.mjs";
|
|
4
|
-
import {
|
|
4
|
+
import { SORTED_JSON_V2, sha256ArtifactDigestV2 } from "../packages/evidence/artifact-integrity.mjs";
|
|
5
5
|
export const DOMAIN_CHANGE_PACKET_INPUT_SCHEMA = 'whoisleuth.domain-change-packet.input';
|
|
6
6
|
export const DOMAIN_CHANGE_PACKET_SCHEMA = 'whoisleuth.domain-change-packet';
|
|
7
|
-
export const
|
|
7
|
+
export const DOMAIN_CHANGE_PACKET_INPUT_VERSION = 1;
|
|
8
|
+
export const DOMAIN_CHANGE_PACKET_VERSION = 2;
|
|
8
9
|
export const MAX_DOMAIN_CHANGE_PACKET_INPUT_BYTES = 6 * 1024 * 1024;
|
|
9
10
|
const ROOT_KEYS = new Set([
|
|
10
11
|
'schema',
|
|
@@ -57,8 +58,8 @@ function changeSummary(before, after) {
|
|
|
57
58
|
}
|
|
58
59
|
export async function buildDomainChangePacket(inputRaw, generatedAtValue = new Date().toISOString()) {
|
|
59
60
|
const input = record(inputRaw, 'Domain change packet input');
|
|
60
|
-
if (input.schema !== DOMAIN_CHANGE_PACKET_INPUT_SCHEMA || input.version !==
|
|
61
|
-
throw new TypeError(`Domain change packet input must use ${DOMAIN_CHANGE_PACKET_INPUT_SCHEMA} version
|
|
61
|
+
if (input.schema !== DOMAIN_CHANGE_PACKET_INPUT_SCHEMA || input.version !== DOMAIN_CHANGE_PACKET_INPUT_VERSION) {
|
|
62
|
+
throw new TypeError(`Domain change packet input must use ${DOMAIN_CHANGE_PACKET_INPUT_SCHEMA} version ${DOMAIN_CHANGE_PACKET_INPUT_VERSION}.`);
|
|
62
63
|
}
|
|
63
64
|
exactKeys(input, ROOT_KEYS, 'Domain change packet input');
|
|
64
65
|
const domain = requireDomainName(input.domain, 'domain');
|
|
@@ -105,7 +106,8 @@ export async function buildDomainChangePacket(inputRaw, generatedAtValue = new D
|
|
|
105
106
|
...unsigned,
|
|
106
107
|
integrity: Object.freeze({
|
|
107
108
|
algorithm: 'SHA-256',
|
|
108
|
-
|
|
109
|
+
canonicalization: SORTED_JSON_V2,
|
|
110
|
+
digestSha256: await sha256ArtifactDigestV2(unsigned),
|
|
109
111
|
}),
|
|
110
112
|
});
|
|
111
113
|
}
|