@slicedearth/whoisleuth-cli 2.3.0 → 2.4.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/NOTICE +6 -0
- package/README.md +19 -15
- package/bin/whoisleuth.mjs +8 -1
- package/cli/archive-content-digest.mjs +14 -0
- package/cli/archive-inspect.mjs +20 -53
- package/cli/arguments.mjs +68 -6
- package/cli/artifact-validation/case-response.mjs +47 -41
- package/cli/artifact-validation/investigation-capsule.mjs +52 -31
- package/cli/artifact-validation/lookup-evidence.mjs +12 -6
- package/cli/artifact-validation/signed-review.mjs +83 -61
- package/cli/artifact-validation/structure-primitives.mjs +1 -105
- package/cli/artifact-verify.mjs +90 -24
- package/cli/assurance-command-runner.mjs +39 -4
- package/cli/bagit-folder.mjs +57 -0
- package/cli/bagit-review.mjs +23 -0
- package/cli/bounded-json.mjs +1 -1
- package/cli/bulk-command-runner.mjs +2 -0
- package/cli/bulk-output.mjs +29 -3
- package/cli/case-command.mjs +195 -0
- package/cli/case-pack.mjs +39 -31
- package/cli/command-argument-grammar.mjs +4 -0
- package/cli/command-reference.mjs +145 -75
- package/cli/completion.mjs +298 -79
- package/cli/config-profile.mjs +7 -5
- package/cli/ct-event-intake.mjs +12 -12
- package/cli/discover.mjs +4 -3
- package/cli/discovery-observation-snapshot.mjs +44 -16
- package/cli/discovery-scan-command-runner.mjs +2 -0
- package/cli/discovery-scan.mjs +7 -4
- package/cli/discovery-workflow.mjs +1 -1
- package/cli/domain-control-monitor.mjs +50 -20
- package/cli/domain-control-observations.mjs +149 -102
- package/cli/errors.mjs +33 -1
- package/cli/evidence-command-runner.mjs +16 -5
- package/cli/evidence-signing.mjs +2 -2
- package/cli/external-observation-mapping.mjs +7 -8
- package/cli/formatters/terminal-lookup.mjs +18 -46
- package/cli/formatters/terminal-shared.mjs +5 -3
- package/cli/formatters/terminal-technology.mjs +114 -0
- package/cli/inline-command-families.mjs +1 -0
- package/cli/interactive-launcher.mjs +2 -160
- package/cli/interchange-report.mjs +4 -8
- package/cli/investigation-artifacts.mjs +176 -0
- package/cli/investigation-checkpoint.mjs +10 -0
- package/cli/investigation-folder.mjs +114 -0
- package/cli/investigation-manifest.mjs +1 -112
- package/cli/investigation-package-review.mjs +103 -0
- package/cli/investigation-plan.mjs +32 -19
- package/cli/investigation-run.mjs +188 -33
- package/cli/local-document-checkpoint.mjs +170 -0
- package/cli/lookup-browser.mjs +5 -12
- package/cli/lookup-command-runner.mjs +17 -6
- package/cli/lookup-diff.mjs +37 -3
- package/cli/lookup-plan.mjs +11 -5
- package/cli/lookup-reconcile.mjs +10 -8
- package/cli/mail-review.mjs +66 -14
- package/cli/manual.mjs +1 -0
- package/cli/network-command-runner.mjs +2 -0
- package/cli/output-file.mjs +31 -5
- package/cli/registry-doctor.mjs +3 -1
- package/cli/registry-support.mjs +1 -1
- package/cli/retained-artifact-diff.mjs +2 -2
- package/cli/review-command-runner.mjs +45 -0
- package/cli/runner.mjs +55 -6
- package/cli/sharing-review.mjs +21 -34
- package/cli/signer-trust.mjs +104 -0
- package/cli/source-reliability.mjs +74 -83
- package/cli/terminal-input.mjs +168 -0
- package/cli/workflow-command-runner.mjs +68 -13
- package/docs/cli-reference.md +76 -30
- package/docs/cli.md +289 -25
- package/lib/abort.mjs +4 -1
- package/lib/availability.mjs +161 -112
- package/lib/bounded-file.mjs +3 -1
- package/lib/bounded-html-document.mjs +125 -0
- package/lib/bounded-json.mjs +43 -3
- package/lib/browser-library-profile.mjs +42 -51
- package/lib/browser-library-worker.mjs +112 -0
- package/lib/client-behavior-profile.mjs +3 -3
- package/lib/ct-search.mjs +11 -4
- package/lib/dns-delegation-health.mjs +24 -14
- package/lib/dns-inheritance-review.mjs +217 -0
- package/lib/dns-intelligence.mjs +36 -23
- package/lib/dns-mx.mjs +1 -1
- package/lib/dnssec-evidence-validation.mjs +11 -3
- package/lib/domain-change-packet.mjs +7 -2
- package/lib/domain-change-review.mjs +14 -8
- package/lib/domain-control-flight-recorder.mjs +219 -118
- package/lib/domain-control-manifest.mjs +117 -57
- package/lib/domain-posture-analysis.mjs +20 -7
- package/lib/domain-posture-parsers.mjs +88 -6
- package/lib/domain-posture.mjs +113 -26
- package/lib/evidence-export-privacy.mjs +561 -0
- package/lib/evidence-export.mjs +11 -592
- package/lib/evidence-report.mjs +3 -1
- package/lib/favicon.mjs +117 -41
- package/lib/generated/cisa-kev-catalog.mjs +12 -4
- package/lib/generated/registrar-standing-catalogue.mjs +7 -7
- package/lib/generated/retire-browser-catalog.mjs +2335 -940
- package/lib/hostname.mjs +2 -38
- package/lib/html-signals.mjs +82 -85
- package/lib/http-evidence-bounds.mjs +2 -1
- package/lib/http-intelligence.mjs +39 -26
- package/lib/lookup-cache.mjs +34 -11
- package/lib/lookup-child-profile-contract.mjs +24 -15
- package/lib/lookup-diagnostics.mjs +24 -1
- package/lib/lookup-progress.mjs +30 -13
- package/lib/lookup-response-contract.mjs +1048 -0
- package/lib/lookup-response.mjs +7 -10
- package/lib/lookup-source-progress.mjs +17 -14
- package/lib/lookup.mjs +48 -27
- package/lib/observed-network-context.mjs +43 -27
- package/lib/outbound-request-bounds.mjs +4 -1
- package/lib/page-fingerprints.mjs +69 -133
- package/lib/page-role-profile.mjs +12 -8
- package/lib/perceptual-hash.mjs +43 -14
- package/lib/rdap-bootstrap.mjs +72 -22
- package/lib/rdap-client.mjs +15 -7
- package/lib/rdap-normalization.mjs +33 -18
- package/lib/rdap-search-workbench.mjs +1 -1
- package/lib/rdap-transport.mjs +47 -20
- package/lib/rdap-types.mjs +2 -1
- package/lib/rdap.mjs +18 -3
- package/lib/registrable-domain.mjs +21 -1
- package/lib/registrar-standing-contract.mjs +1 -0
- package/lib/registry-capabilities.mjs +5 -1
- package/lib/registry-capability-catalogue.mjs +2 -1
- package/lib/registry-dates.mjs +2 -190
- package/lib/registry-insights.mjs +56 -18
- package/lib/response-policy.mjs +86 -48
- package/lib/risk-calibration-summary.mjs +17 -3
- package/lib/rpki-evidence.mjs +8 -5
- package/lib/safe-fetch.mjs +54 -47
- package/lib/security-txt.mjs +10 -7
- package/lib/service-binding-dns.mjs +8 -0
- package/lib/smtp-transport-review.mjs +27 -15
- package/lib/sslbl-certificates.generated.mjs +136 -135
- package/lib/static-html-analysis.mjs +117 -146
- package/lib/structured-data-identity.mjs +1 -0
- package/lib/technology-markup.mjs +100 -0
- package/lib/threat-intelligence-manifests.mjs +11 -139
- package/lib/threat-intelligence-runtime.mjs +24 -52
- package/lib/threat-intelligence-targets.mjs +6 -34
- package/lib/threat-intelligence-types.mjs +39 -0
- package/lib/typosquat-generator.mjs +19 -15
- package/lib/urlhaus-intelligence.mjs +8 -3
- package/lib/urlscan-intelligence.mjs +1 -1
- package/lib/website-security-posture.mjs +30 -6
- package/lib/website-technology.mjs +111 -71
- package/lib/whois-authority.mjs +14 -2
- package/lib/whois-chain.mjs +9 -2
- package/lib/whois-common-formats.mjs +5 -5
- package/lib/whois-contracts.mjs +2 -1
- package/lib/whois-dialects.mjs +2 -2
- package/lib/whois-parser-support.mjs +8 -1
- package/lib/whois-transport.mjs +23 -7
- package/package.json +2 -1
- package/packages/cases/case-attachment-model.mjs +83 -0
- package/packages/cases/case-evidence-model.mjs +156 -62
- package/packages/cases/case-export-input.mjs +53 -0
- package/packages/cases/case-migration-model.mjs +59 -25
- package/packages/cases/case-model.mjs +1 -0
- package/packages/cases/case-recheck-model.mjs +91 -0
- package/packages/cases/case-record-core.mjs +3 -2
- package/packages/cases/case-record-operations.mjs +89 -27
- package/packages/cases/case-record-projection.mjs +23 -20
- package/packages/cases/case-report.mjs +63 -52
- package/packages/cases/case-response-actions.mjs +654 -0
- package/packages/cases/case-response-model.mjs +41 -1129
- package/packages/cases/case-response-outcomes.mjs +317 -0
- package/packages/cases/case-response-packet-render.mjs +153 -0
- package/packages/cases/case-response-packet-types.mjs +1 -0
- package/packages/cases/case-response-packet-vocabulary.mjs +48 -0
- package/packages/cases/case-response-packet.mjs +38 -587
- package/packages/cases/case-response-records.mjs +94 -0
- package/packages/cases/case-response-review-inputs.mjs +371 -0
- package/packages/cases/case-response-values.mjs +85 -0
- package/packages/cases/case-review-package.mjs +45 -0
- package/packages/cases/case-selection.mjs +46 -0
- package/packages/cases/case-storage-model.mjs +25 -0
- package/packages/cases/response-route-freshness.mjs +16 -0
- package/packages/comparison/capture-context.mjs +61 -0
- package/packages/comparison/comparison-ledger-bulk.mjs +4 -1
- package/packages/comparison/page-similarity.mjs +2 -1
- package/packages/comparison/relationship-evidence.mjs +85 -8
- package/packages/comparison/relationship-provenance.mjs +93 -0
- package/packages/contracts/analyst-interchange.mjs +216 -0
- package/packages/contracts/analyst-review-state-contract.mjs +11 -0
- package/packages/contracts/analyst-review-state.mjs +5 -4
- package/packages/contracts/case-portability.mjs +279 -116
- package/packages/contracts/case-views-contract.mjs +12 -0
- package/packages/contracts/case-views.mjs +26 -0
- package/packages/contracts/cli-command-semantics.mjs +1 -0
- package/packages/contracts/cli-lookup.mjs +0 -1
- package/packages/contracts/domain-control-flight-recorder.mjs +80 -42
- package/packages/contracts/domain-control-manifest.mjs +127 -73
- package/packages/contracts/domain-control-monitor.mjs +48 -45
- package/packages/contracts/domain-control-review.mjs +179 -121
- package/packages/contracts/domain-name.mjs +21 -0
- package/packages/contracts/external-observation-interchange.mjs +2 -1
- package/packages/contracts/extracted-domain-lifecycle.mjs +11 -32
- package/packages/contracts/http-url.mjs +2 -0
- package/packages/contracts/investigation-package-limits.mjs +24 -0
- package/packages/contracts/investigation-portability.mjs +56 -27
- package/packages/contracts/investigation-run.mjs +16 -0
- package/packages/contracts/ip-address.mjs +34 -0
- package/packages/contracts/lookup-evidence.mjs +35 -0
- package/packages/contracts/offline-comparison.mjs +1 -2
- package/packages/contracts/page-fingerprints.mjs +15 -0
- package/packages/contracts/risk-calibration.mjs +0 -1
- package/packages/contracts/schema-compatibility.mjs +1 -1
- package/packages/contracts/schema-lifecycle.mjs +83 -153
- package/packages/contracts/selected-file-limits.mjs +6 -0
- package/packages/contracts/vulnerability-identifiers.mjs +5 -0
- package/packages/contracts/web-capture.mjs +10 -0
- package/packages/contracts/workspace-portability.mjs +160 -225
- package/packages/evidence/artifact-integrity.mjs +8 -2
- package/packages/evidence/artifact-structure.mjs +105 -0
- package/packages/evidence/decision-fact.mjs +4 -1
- package/packages/evidence/domain-control-runtime.mjs +155 -27
- package/packages/evidence/domain-posture-context.mjs +79 -0
- package/packages/evidence/image-regions.mjs +56 -0
- package/packages/evidence/latest-observations.mjs +28 -0
- package/packages/evidence/lookup-target.mjs +104 -0
- package/packages/evidence/observation.mjs +16 -1
- package/packages/evidence/passphrase-encryption.mjs +44 -0
- package/packages/evidence/record-identity.mjs +72 -0
- package/packages/evidence/registry-dates.mjs +190 -0
- package/packages/evidence/retained-file.mjs +69 -0
- package/packages/evidence/review-age.mjs +14 -0
- package/packages/interchange/bagit.mjs +342 -0
- package/packages/interchange/bounded-zip-extraction.mjs +278 -0
- package/packages/interchange/crc32.mjs +17 -0
- package/packages/interchange/external-findings-import.mjs +89 -49
- package/packages/interchange/web-capture-import.mjs +382 -0
- package/packages/investigation/bulk-mail-exposure.mjs +2 -1
- package/packages/investigation/investigation-bagit.mjs +14 -0
- package/packages/investigation/investigation-capsule.mjs +33 -5
- package/packages/investigation/investigation-manifest.mjs +200 -0
- package/packages/investigation/investigation-package-crypto.mjs +98 -0
- package/packages/investigation/investigation-package.mjs +200 -0
- package/packages/investigation/lookup-asset-graph-inputs.mjs +38 -0
- package/packages/investigation/lookup-asset-graph.mjs +161 -141
- package/packages/investigation/lookup-claim-readiness.mjs +18 -0
- package/packages/investigation/lookup-investigation-brief.mjs +3 -3
- package/packages/investigation/lookup-task-guidance.mjs +58 -0
- package/packages/monitoring/analyst-review-state.mjs +38 -90
- package/packages/workspace/brand-profile-model.mjs +158 -20
- package/packages/workspace/bulk-columns.mjs +17 -0
- package/packages/workspace/bulk-review-model.mjs +11 -5
- package/packages/workspace/bulk-session-model.mjs +95 -20
- package/packages/workspace/bulk-sort.mjs +1 -0
- package/packages/workspace/case-views.mjs +102 -0
- package/packages/workspace/hostile-input.mjs +29 -8
- package/packages/workspace/investigation-template-model.mjs +19 -4
- package/packages/workspace/page-baseline.mjs +18 -15
- package/packages/workspace/relationship-observation-model.mjs +41 -26
- package/packages/workspace/watchlist-history.mjs +2 -2
- package/packages/workspace/watchlist-store.mjs +8 -2
- package/packages/workspace/website-snapshot-model.mjs +70 -30
- package/packages/workspace/workspace-archive-crypto.mjs +41 -61
- package/packages/workspace/workspace-archive.mjs +85 -21
- package/third-party-notices.txt +29 -2
- package/packages/investigation/bulk-review-export.mjs +0 -63
|
@@ -1,19 +1,21 @@
|
|
|
1
1
|
// Browser-safe version and collection boundaries shared by Lookup child
|
|
2
2
|
// profile producers and the client response trust boundary.
|
|
3
3
|
import { MAX_OBSERVATION_LIMITATIONS, MAX_OBSERVATION_LIMITATION_LENGTH, OBSERVATION_VERSION, normalizeExplicitIsoTimestamp, } from "../packages/evidence/observation.mjs";
|
|
4
|
-
import {
|
|
4
|
+
import { PAGE_FINGERPRINT_PARSERS, PAGE_IDENTITY_VERSION, } from "../packages/contracts/workspace-portability.mjs";
|
|
5
5
|
import { validPagePublicationMetadata } from "./homepage-metadata-contract.mjs";
|
|
6
|
+
import { PAGE_FINGERPRINT_TOKEN_LIMITS } from "../packages/contracts/page-fingerprints.mjs";
|
|
6
7
|
import { MAX_LOOKUP_TLS_ALT_NAMES, MAX_LOOKUP_TLS_CERTIFICATE_POLICIES, MAX_LOOKUP_TLS_CHAIN_CERTIFICATES, MAX_LOOKUP_TLS_FINDINGS, MAX_LOOKUP_TLS_NAME_VALUES, TLS_PROFILE_VERSION, } from "./lookup-network-evidence-bounds.mjs";
|
|
7
8
|
import { isJsonObject, normalizedDomain, validBoundedString, validHttpProvenanceUrl, validObservationDiagnostics, validOptionalNullableText, validStringArray, validUint, } from "./lookup-contract-primitives.mjs";
|
|
8
9
|
import { TECHNOLOGY_EVIDENCE_ROLE_ORDER } from "./technology-evidence-role.mjs";
|
|
9
|
-
|
|
10
|
-
export const
|
|
10
|
+
import { MAX_HOMEPAGE_BYTES } from "./outbound-request-bounds.mjs";
|
|
11
|
+
export const TECHNOLOGY_PROFILE_VERSION = 12;
|
|
12
|
+
export const SUPPORTED_TECHNOLOGY_PROFILE_VERSIONS = Object.freeze([10, 11, TECHNOLOGY_PROFILE_VERSION]);
|
|
11
13
|
export const MAX_TECHNOLOGY_FINDINGS = 24;
|
|
12
14
|
export const MAX_EVIDENCE_PER_TECHNOLOGY = 4;
|
|
13
15
|
export const MAX_TECHNOLOGY_EVIDENCE_DESCRIPTION_LENGTH = 180;
|
|
14
16
|
export const BROWSER_LIBRARY_PROFILE_VERSION = 2;
|
|
15
17
|
export const MAX_LIBRARY_FINDINGS = 16;
|
|
16
|
-
export const WEBSITE_SECURITY_POSTURE_VERSION =
|
|
18
|
+
export const WEBSITE_SECURITY_POSTURE_VERSION = 3;
|
|
17
19
|
export const MAX_SECURITY_POSTURE_FINDINGS = 32;
|
|
18
20
|
export const CREDENTIAL_SURFACE_PROFILE_VERSION = 1;
|
|
19
21
|
export const STRUCTURED_DATA_IDENTITY_VERSION = 1;
|
|
@@ -169,6 +171,7 @@ function validFingerprintIdentifier(value) {
|
|
|
169
171
|
&& /^[A-Z0-9-]{1,64}$/u.test(value.value);
|
|
170
172
|
}
|
|
171
173
|
function validPageFingerprintProfile(value) {
|
|
174
|
+
const tokenLimit = PAGE_FINGERPRINT_TOKEN_LIMITS[value.fingerprintVersion];
|
|
172
175
|
const normalizedHtml = value.normalizedHtml;
|
|
173
176
|
const visibleText = value.visibleText;
|
|
174
177
|
const domStructure = value.domStructure;
|
|
@@ -181,7 +184,7 @@ function validPageFingerprintProfile(value) {
|
|
|
181
184
|
|| normalizedHtml.algorithm !== 'sha256'
|
|
182
185
|
|| typeof normalizedHtml.value !== 'string'
|
|
183
186
|
|| !SHA256_RE.test(normalizedHtml.value)
|
|
184
|
-
|| !validUint(normalizedHtml.tokenCount,
|
|
187
|
+
|| !validUint(normalizedHtml.tokenCount, tokenLimit)
|
|
185
188
|
|| typeof normalizedHtml.truncated !== 'boolean'
|
|
186
189
|
|| !(visibleText === null || isJsonObject(visibleText)
|
|
187
190
|
&& hasExactKeys(visibleText, ['algorithm', 'value', 'tokenCount', 'featureCount', 'truncated'])
|
|
@@ -197,16 +200,16 @@ function validPageFingerprintProfile(value) {
|
|
|
197
200
|
|| domStructure.algorithm !== 'sha256'
|
|
198
201
|
|| typeof domStructure.value !== 'string'
|
|
199
202
|
|| !SHA256_RE.test(domStructure.value)
|
|
200
|
-
|| !validUint(domStructure.nodeCount,
|
|
201
|
-
|| domStructure.parser !==
|
|
203
|
+
|| !validUint(domStructure.nodeCount, tokenLimit)
|
|
204
|
+
|| domStructure.parser !== PAGE_FINGERPRINT_PARSERS[value.fingerprintVersion]
|
|
202
205
|
|| typeof domStructure.truncated !== 'boolean'
|
|
203
206
|
|| !(domStructure.similarity === undefined || domStructure.similarity === null || isJsonObject(domStructure.similarity)
|
|
204
207
|
&& hasExactKeys(domStructure.similarity, ['algorithm', 'value', 'tokenCount', 'featureCount', 'truncated'])
|
|
205
208
|
&& domStructure.similarity.algorithm === 'simhash64-v1'
|
|
206
209
|
&& typeof domStructure.similarity.value === 'string'
|
|
207
210
|
&& SIMHASH64_RE.test(domStructure.similarity.value)
|
|
208
|
-
&& validUint(domStructure.similarity.tokenCount,
|
|
209
|
-
&& validUint(domStructure.similarity.featureCount,
|
|
211
|
+
&& validUint(domStructure.similarity.tokenCount, tokenLimit)
|
|
212
|
+
&& validUint(domStructure.similarity.featureCount, tokenLimit)
|
|
210
213
|
&& typeof domStructure.similarity.truncated === 'boolean')
|
|
211
214
|
|| !(formStructure === null || isJsonObject(formStructure)
|
|
212
215
|
&& hasExactKeys(formStructure, ['algorithm', 'value', 'formCount', 'controlCount', 'truncated'])
|
|
@@ -461,7 +464,7 @@ function browserLibraryProfileContractState(value) {
|
|
|
461
464
|
|| !isJsonObject(knownExploitedCatalog)
|
|
462
465
|
|| !hasOnlyKeys(knownExploitedCatalog, ['name', 'version', 'releasedAt'])
|
|
463
466
|
|| !['name', 'version', 'releasedAt'].every((field) => validBoundedString(knownExploitedCatalog[field], 160))
|
|
464
|
-
|| normalizeExplicitIsoTimestamp(knownExploitedCatalog.releasedAt)
|
|
467
|
+
|| normalizeExplicitIsoTimestamp(knownExploitedCatalog.releasedAt) === null
|
|
465
468
|
|| !Array.isArray(profile.findings)
|
|
466
469
|
|| profile.findings.length > MAX_LIBRARY_FINDINGS)
|
|
467
470
|
return 'invalid';
|
|
@@ -488,6 +491,8 @@ function browserLibraryProfileContractState(value) {
|
|
|
488
491
|
|| !validExactStringArray(candidate.knownExploitedIdentifiers, 16, 80)
|
|
489
492
|
|| !validExactStringArray(candidate.weaknessClasses, 12, 80))
|
|
490
493
|
return 'invalid';
|
|
494
|
+
// Published profiles could retain legacy upstream identifier spellings.
|
|
495
|
+
// Keep that reader boundary; current catalogue writers validate syntax.
|
|
491
496
|
if (findingIds.has(candidate.id)
|
|
492
497
|
|| !candidate.advisoryIdentifiers.every((identifier) => typeof identifier === 'string'
|
|
493
498
|
&& /^(?:CVE-[0-9X-]+|GHSA-[A-Z0-9-]+)$/u.test(identifier))
|
|
@@ -495,7 +500,9 @@ function browserLibraryProfileContractState(value) {
|
|
|
495
500
|
&& /^CVE-[0-9X-]+$/u.test(identifier))
|
|
496
501
|
|| !candidate.weaknessClasses.every((identifier) => typeof identifier === 'string'
|
|
497
502
|
&& /^CWE-[0-9]+$/u.test(identifier))
|
|
498
|
-
|
|
503
|
+
// One advisory can have several identifiers, including CVE and GHSA
|
|
504
|
+
// aliases. Their count is not the number of matching advisory records.
|
|
505
|
+
|| (Number(candidate.advisoryCount) === 0 && candidate.advisoryIdentifiers.length > 0)
|
|
499
506
|
|| Number(candidate.knownExploitedCount) < candidate.knownExploitedIdentifiers.length)
|
|
500
507
|
return 'invalid';
|
|
501
508
|
findingIds.add(candidate.id);
|
|
@@ -503,7 +510,7 @@ function browserLibraryProfileContractState(value) {
|
|
|
503
510
|
return 'supported';
|
|
504
511
|
}
|
|
505
512
|
function pageFingerprintContractState(value) {
|
|
506
|
-
const versionState = childVersionState(value, 'fingerprintVersion',
|
|
513
|
+
const versionState = childVersionState(value, 'fingerprintVersion', Object.keys(PAGE_FINGERPRINT_PARSERS).map(Number));
|
|
507
514
|
if (versionState !== 'supported')
|
|
508
515
|
return versionState;
|
|
509
516
|
const profile = value;
|
|
@@ -520,7 +527,9 @@ function pageFingerprintContractState(value) {
|
|
|
520
527
|
|| typeof profile.exact.value !== 'string'
|
|
521
528
|
|| !SHA256_RE.test(profile.exact.value)
|
|
522
529
|
|| !['complete-body', 'captured-prefix'].includes(String(profile.exact.scope))
|
|
523
|
-
|
|
530
|
+
// The published first-generation writer had a fixed 300,000-byte ceiling.
|
|
531
|
+
// Current native fingerprints share the collector's source-size policy.
|
|
532
|
+
|| !validUint(profile.exact.bytes, profile.fingerprintVersion === 1 ? 300_000 : MAX_HOMEPAGE_BYTES)
|
|
524
533
|
|| !['captured-response-bytes', 'decoded-markup'].includes(String(profile.exact.source))
|
|
525
534
|
|| !validPageFingerprintProfile(profile)
|
|
526
535
|
|| profile.complete !== (profile.truncated !== true))
|
|
@@ -647,7 +656,7 @@ function tlsProfileContractState(value) {
|
|
|
647
656
|
return 'supported';
|
|
648
657
|
}
|
|
649
658
|
function securityPostureContractState(value) {
|
|
650
|
-
const versionState = childVersionState(value, 'postureVersion', [WEBSITE_SECURITY_POSTURE_VERSION]);
|
|
659
|
+
const versionState = childVersionState(value, 'postureVersion', [2, WEBSITE_SECURITY_POSTURE_VERSION]);
|
|
651
660
|
if (versionState !== 'supported')
|
|
652
661
|
return versionState;
|
|
653
662
|
const profile = value;
|
|
@@ -855,4 +864,4 @@ function sanitizeLookupChildProfiles(value) {
|
|
|
855
864
|
}
|
|
856
865
|
return output;
|
|
857
866
|
}
|
|
858
|
-
export { sanitizeLookupChildProfiles, validSecurityPostureFinding, validTlsChainCertificate, validTlsFinding, };
|
|
867
|
+
export { sanitizeLookupChildProfiles, technologyProfileContractState, validSecurityPostureFinding, validTlsChainCertificate, validTlsFinding, };
|
|
@@ -3,6 +3,29 @@ import { OPERATION_BUDGET_ERROR_CODE } from "./operation-budget.mjs";
|
|
|
3
3
|
import { FEATURE_DISABLED_ERROR_CODE } from "./feature-policy.mjs";
|
|
4
4
|
const LOOKUP_DIAGNOSTICS_VERSION = 8;
|
|
5
5
|
const LOOKUP_LEGACY_DIAGNOSTICS_VERSION = 7;
|
|
6
|
+
const LOOKUP_SOURCE_STATES = Object.freeze([
|
|
7
|
+
'success', 'partial', 'not_found', 'skipped', 'error', 'unsupported',
|
|
8
|
+
'not_applicable', 'unavailable', 'rate_limited', 'complete', 'disabled', 'stale',
|
|
9
|
+
]);
|
|
10
|
+
const SOURCE_STATES = new Set(LOOKUP_SOURCE_STATES);
|
|
11
|
+
function lookupDiagnosticStates(value) {
|
|
12
|
+
const record = (input) => input && typeof input === 'object' && !Array.isArray(input)
|
|
13
|
+
? input : {};
|
|
14
|
+
const diagnostics = record(value);
|
|
15
|
+
const candidates = {
|
|
16
|
+
rdap: record(diagnostics.rdap).status,
|
|
17
|
+
registrar_rdap: record(record(diagnostics.rdap).registrar).status,
|
|
18
|
+
whois: record(diagnostics.whois).status,
|
|
19
|
+
availability: record(diagnostics.availability).status,
|
|
20
|
+
reverse_dns: record(diagnostics.reverseDns).status,
|
|
21
|
+
network_context: record(diagnostics.network).status,
|
|
22
|
+
security_txt: record(diagnostics.securityTxt).status,
|
|
23
|
+
sslbl: record(diagnostics.sslbl).status,
|
|
24
|
+
};
|
|
25
|
+
return Object.freeze(Object.fromEntries(Object.entries(candidates).map(([source, state]) => [
|
|
26
|
+
source, typeof state === 'string' && SOURCE_STATES.has(state) ? state : null,
|
|
27
|
+
])));
|
|
28
|
+
}
|
|
6
29
|
const LOOKUP_TIMING_VERSION = 1;
|
|
7
30
|
const MAX_LOOKUP_TIMING_MS = 120_000;
|
|
8
31
|
const LOOKUP_TIMING_SOURCE_ORDER = Object.freeze([
|
|
@@ -93,4 +116,4 @@ function createLookupTimingTracker(enabled, now) {
|
|
|
93
116
|
},
|
|
94
117
|
};
|
|
95
118
|
}
|
|
96
|
-
export { LOOKUP_DIAGNOSTICS_VERSION, LOOKUP_LEGACY_DIAGNOSTICS_VERSION, LOOKUP_TIMING_VERSION, MAX_LOOKUP_TIMING_MS, LOOKUP_ERROR_CODES, boundedSourceDetail, createLookupTimingTracker, errorMessage, };
|
|
119
|
+
export { LOOKUP_DIAGNOSTICS_VERSION, LOOKUP_LEGACY_DIAGNOSTICS_VERSION, LOOKUP_SOURCE_STATES, LOOKUP_TIMING_VERSION, MAX_LOOKUP_TIMING_MS, LOOKUP_ERROR_CODES, boundedSourceDetail, createLookupTimingTracker, errorMessage, lookupDiagnosticStates, };
|
package/lib/lookup-progress.mjs
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
// Portable bounded NDJSON Lookup progress contract. Partial events are
|
|
2
2
|
// presentation-only; only a validated final ordinary Lookup result may cross
|
|
3
3
|
// the persistence boundary.
|
|
4
|
+
import { scanBoundedJson, MAX_BOUNDED_JSON_DEPTH, MAX_BOUNDED_JSON_KEYS, MAX_BOUNDED_JSON_VALUES } from "./bounded-json.mjs";
|
|
4
5
|
export const LOOKUP_PROGRESS_SCHEMA = 'whoisleuth.lookup-progress';
|
|
5
6
|
export const LOOKUP_PROGRESS_VERSION = 1;
|
|
6
7
|
export const MAX_LOOKUP_PROGRESS_SOURCES = 16;
|
|
@@ -241,20 +242,25 @@ export function createLookupProgressNdjsonDecoder(onEvent) {
|
|
|
241
242
|
throw new TypeError('NDJSON decoder requires an event consumer.');
|
|
242
243
|
const decoder = new TextDecoder('utf-8', { fatal: true });
|
|
243
244
|
let pending = '';
|
|
245
|
+
let pendingBytes = 0;
|
|
244
246
|
let totalBytes = 0;
|
|
245
247
|
let eventCount = 0;
|
|
246
248
|
function consumeLine(line) {
|
|
247
249
|
if (!line)
|
|
248
250
|
return;
|
|
249
|
-
if (byteLength(line) > MAX_LOOKUP_PROGRESS_FINAL_BYTES + MAX_LOOKUP_PROGRESS_FRAGMENT_BYTES) {
|
|
250
|
-
throw new TypeError('Lookup progress line exceeds its byte bound.');
|
|
251
|
-
}
|
|
252
251
|
eventCount += 1;
|
|
253
252
|
if (eventCount > MAX_LOOKUP_PROGRESS_EVENTS) {
|
|
254
253
|
throw new TypeError('Lookup progress stream contains too many events.');
|
|
255
254
|
}
|
|
256
255
|
let parsed;
|
|
257
256
|
try {
|
|
257
|
+
// The ordinary result keeps its original limits after decoding. Allow
|
|
258
|
+
// only the fixed progress envelope overhead around that same document.
|
|
259
|
+
scanBoundedJson(line, {
|
|
260
|
+
maximumDepth: MAX_BOUNDED_JSON_DEPTH + 1,
|
|
261
|
+
maximumKeys: MAX_BOUNDED_JSON_KEYS + 8,
|
|
262
|
+
maximumValues: MAX_BOUNDED_JSON_VALUES + MAX_LOOKUP_PROGRESS_SOURCES + 16,
|
|
263
|
+
});
|
|
258
264
|
parsed = JSON.parse(line);
|
|
259
265
|
}
|
|
260
266
|
catch {
|
|
@@ -269,16 +275,26 @@ export function createLookupProgressNdjsonDecoder(onEvent) {
|
|
|
269
275
|
if (totalBytes > MAX_LOOKUP_PROGRESS_STREAM_BYTES) {
|
|
270
276
|
throw new TypeError('Lookup progress stream exceeds its total byte bound.');
|
|
271
277
|
}
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
278
|
+
// Count incoming bytes once, before decoding or accumulation. LF cannot
|
|
279
|
+
// occur inside a valid UTF-8 sequence; the fatal decoder still receives
|
|
280
|
+
// every byte, including framing, so chunk boundaries cannot hide errors.
|
|
281
|
+
for (let start = 0; start < chunk.length;) {
|
|
282
|
+
const newline = chunk.indexOf(10, start);
|
|
283
|
+
const end = newline < 0 ? chunk.length : newline;
|
|
284
|
+
pendingBytes += end - start;
|
|
285
|
+
const maximum = MAX_LOOKUP_PROGRESS_FINAL_BYTES + MAX_LOOKUP_PROGRESS_FRAGMENT_BYTES;
|
|
286
|
+
const trailingCr = end > start ? chunk[end - 1] === 13 : pending.endsWith('\r');
|
|
287
|
+
if (pendingBytes > maximum + (trailingCr ? 1 : 0)) {
|
|
288
|
+
throw new TypeError('Lookup progress line exceeds its byte bound.');
|
|
289
|
+
}
|
|
290
|
+
const decoded = decoder.decode(chunk.subarray(start, end + (newline < 0 ? 0 : 1)), { stream: true });
|
|
291
|
+
pending += newline < 0 ? decoded : decoded.slice(0, -1);
|
|
292
|
+
if (newline >= 0) {
|
|
293
|
+
consumeLine(pending.replace(/\r$/u, ''));
|
|
294
|
+
pending = '';
|
|
295
|
+
pendingBytes = 0;
|
|
296
|
+
}
|
|
297
|
+
start = end + 1;
|
|
282
298
|
}
|
|
283
299
|
}
|
|
284
300
|
function finish() {
|
|
@@ -286,6 +302,7 @@ export function createLookupProgressNdjsonDecoder(onEvent) {
|
|
|
286
302
|
if (pending)
|
|
287
303
|
consumeLine(pending.replace(/\r$/u, ''));
|
|
288
304
|
pending = '';
|
|
305
|
+
pendingBytes = 0;
|
|
289
306
|
}
|
|
290
307
|
return Object.freeze({ push, finish });
|
|
291
308
|
}
|