@slicedearth/whoisleuth-cli 1.47.4 → 2.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +74 -177
- package/SECURITY.md +6 -3
- package/bin/whoisleuth.mjs +2 -1
- package/cli/archive-inspect.mjs +28 -10
- package/cli/arguments.mjs +204 -174
- package/cli/artifact-structure.mjs +837 -163
- package/cli/artifact-verify.mjs +90 -61
- package/cli/bounded-json.mjs +1 -1
- package/cli/bulk-checkpoint.mjs +33 -20
- package/cli/bulk.mjs +9 -2
- package/cli/case-pack.mjs +180 -576
- package/cli/ci-report.mjs +6 -1
- package/cli/collection-preflight.mjs +2 -2
- package/cli/command-catalogue.mjs +12 -3
- package/cli/command-reference.mjs +910 -92
- package/cli/completion.mjs +504 -226
- package/cli/config-profile.mjs +13 -3
- package/cli/discover.mjs +7 -1
- package/cli/discovery-observation-snapshot.mjs +24 -28
- package/cli/discovery-scan-command-runner.mjs +9 -9
- package/cli/discovery-scan.mjs +17 -3
- package/cli/discovery-snapshot.mjs +15 -7
- package/cli/domain-control-monitor.mjs +203 -30
- package/cli/domain-control-observations.mjs +15 -21
- package/cli/errors.mjs +8 -3
- package/cli/evidence-signing.mjs +10 -20
- package/cli/external-observation-mapping.mjs +3 -3
- package/cli/formatters/json.mjs +20 -32
- package/cli/formatters/terminal.mjs +583 -22
- package/cli/input.mjs +7 -2
- package/cli/interactive-launcher.mjs +226 -0
- package/cli/interchange-report.mjs +19 -19
- package/cli/investigation-manifest.mjs +1 -1
- package/cli/investigation-plan.mjs +167 -16
- package/cli/investigation-run.mjs +9 -1
- package/cli/lookalike-calibration.mjs +3 -1
- package/cli/lookup-brief.mjs +13 -8
- package/cli/lookup-browser.mjs +844 -0
- package/cli/lookup-command-runner.mjs +94 -31
- package/cli/lookup-diff.mjs +2 -2
- package/cli/lookup-plan.mjs +1 -0
- package/cli/mail-review.mjs +206 -94
- package/cli/manual.mjs +5 -5
- package/cli/offline-evidence-review.mjs +18 -7
- package/cli/open-asset-model-bridge.mjs +1 -1
- package/cli/output-file.mjs +70 -14
- package/cli/page-compare.mjs +11 -6
- package/cli/progress-events.mjs +2 -1
- package/cli/progress.mjs +3 -3
- package/cli/registry-cohort.mjs +9 -51
- package/cli/registry-doctor.mjs +67 -28
- package/cli/retained-artifact-diff.mjs +7 -8
- package/cli/risk-calibration.mjs +85 -38
- package/cli/runner.mjs +189 -75
- package/cli/saved-lookup.mjs +424 -57
- package/cli/sharing-review.mjs +6 -1
- package/cli/source-reliability.mjs +75 -36
- package/cli/terminal-presentation.mjs +43 -18
- package/docs/cli-reference.md +125 -1708
- package/docs/cli.md +86 -254
- package/frontend/src/lib/analysis/domain-control-manifest-core.js +3 -145
- package/frontend/src/lib/analysis/domain-control-records.js +3 -88
- package/lib/availability.mjs +58 -40
- package/lib/bounded-contract-normalizers.mjs +2 -4
- package/lib/bounded-file.mjs +78 -4
- package/lib/bounded-json.mjs +51 -13
- package/lib/browser-library-profile.mjs +1 -1
- package/lib/classify.mjs +56 -5
- package/lib/client-behavior-profile.mjs +1 -1
- package/lib/credential-surface-profile.mjs +1 -1
- package/lib/cryptographic-assurance.mjs +35 -17
- package/lib/ct-query.mjs +3 -3
- package/lib/ct-search.mjs +37 -21
- package/lib/distributed-operation-budget.mjs +4 -2
- package/lib/dns-delegation-health.mjs +9 -6
- package/lib/dns-intelligence.mjs +1 -1
- package/lib/dnssec-chain-validation.mjs +2 -3
- package/lib/dnssec-evidence-validation.mjs +13 -7
- package/lib/domain-assurance.mjs +12 -12
- package/lib/domain-change-packet.mjs +1 -1
- package/lib/domain-control-flight-recorder.mjs +442 -78
- package/lib/domain-control-manifest.mjs +314 -59
- package/lib/domain-posture-analysis.mjs +5 -2
- package/lib/encrypted-dns-contract.mjs +2 -3
- package/lib/evidence-export.mjs +518 -36
- package/lib/external-intelligence-risk.mjs +26 -10
- package/lib/feature-policy.mjs +1 -1
- package/lib/homepage-metadata-contract.mjs +290 -0
- package/lib/hostname.mjs +1 -1
- package/lib/html-signals.mjs +89 -5
- package/lib/http-intelligence.mjs +331 -2
- package/lib/interchange-fidelity-registry.mjs +28 -26
- package/lib/lookup-cache.mjs +6 -4
- package/lib/lookup.mjs +25 -2
- package/lib/observed-network-context.mjs +1 -1
- package/lib/page-fingerprints.mjs +2 -2
- package/lib/page-role-profile.mjs +1 -1
- package/lib/portable-generator.mjs +8 -0
- package/lib/provider-policy-admission.mjs +3 -1
- package/lib/rdap-bootstrap.mjs +76 -16
- package/lib/rdap-client.mjs +20 -12
- package/lib/rdap-normalization.mjs +306 -75
- package/lib/rdap-transport.mjs +2 -2
- package/lib/rdap-validation.mjs +65 -14
- package/lib/rdap.mjs +2 -2
- package/lib/registrable-domain.mjs +36 -0
- package/lib/registry-insights.mjs +7 -0
- package/lib/response-policy.mjs +27 -4
- package/lib/risk-calibration-summary.mjs +35 -33
- package/lib/risk-scoring.mjs +4 -3
- package/lib/safe-fetch.mjs +62 -40
- package/lib/security-txt.mjs +1 -1
- package/lib/smtp-transport-review.mjs +8 -5
- package/lib/sslbl-intelligence.mjs +6 -8
- package/lib/static-html-analysis.mjs +301 -4
- package/lib/structured-data-identity.mjs +51 -19
- package/lib/threat-intelligence-fixtures.mjs +2 -4
- package/lib/threat-intelligence-manifests.mjs +2 -7
- package/lib/threat-intelligence-runtime.mjs +3 -5
- package/lib/threat-intelligence-types.mjs +3 -0
- package/lib/threatfox-intelligence.mjs +7 -3
- package/lib/tls-intelligence.mjs +4 -4
- package/lib/urlhaus-intelligence.mjs +4 -2
- package/lib/urlscan-intelligence.mjs +6 -5
- package/lib/website-security-posture.mjs +1 -1
- package/lib/website-technology.mjs +1 -1
- package/lib/whois-authority.mjs +43 -8
- package/lib/whois-chain.mjs +25 -3
- package/lib/whois-normalization.mjs +3 -1
- package/lib/whois-transport.mjs +12 -3
- package/lib/zone-intent-review.mjs +1 -1
- package/package.json +12 -4
- package/{frontend/src/lib/analysis/case-brand-profile-references.js → packages/cases/case-brand-profile-references.mjs} +2 -2
- package/{frontend/src/lib/analysis/case-evidence-model.js → packages/cases/case-evidence-model.mjs} +35 -14
- package/{frontend/src/lib/analysis/case-investigation-branch-model.js → packages/cases/case-investigation-branch-model.mjs} +10 -12
- package/packages/cases/case-migration-model.mjs +476 -0
- package/{frontend/src/lib/analysis/case-model.js → packages/cases/case-model.mjs} +3 -3
- package/{frontend/src/lib/analysis/case-record-contracts.js → packages/cases/case-record-contracts.mjs} +3 -21
- package/{frontend/src/lib/analysis/case-record-core.js → packages/cases/case-record-core.mjs} +58 -31
- package/packages/cases/case-record-model.mjs +9 -0
- package/{frontend/src/lib/analysis/case-record-operations.js → packages/cases/case-record-operations.mjs} +72 -29
- package/{frontend/src/lib/analysis/case-report.js → packages/cases/case-report.mjs} +108 -14
- package/packages/cases/case-response-model.mjs +1604 -0
- package/packages/cases/case-response-packet.mjs +1312 -0
- package/{frontend/src/lib/analysis/case-storage-model.js → packages/cases/case-storage-model.mjs} +2 -2
- package/{frontend/src/lib/analysis/http-summary.js → packages/cases/http-summary.mjs} +1 -1
- package/{frontend/src/lib/analysis/comparison-ledger-bulk.js → packages/comparison/comparison-ledger-bulk.mjs} +2 -2
- package/{frontend/src/lib/analysis/comparison-ledger-contract.js → packages/comparison/comparison-ledger-contract.mjs} +4 -4
- package/{frontend/src/lib/analysis/comparison-ledger-json.js → packages/comparison/comparison-ledger-json.mjs} +1 -0
- package/{frontend/src/lib/analysis/comparison-ledger-serialization.js → packages/comparison/comparison-ledger-serialization.mjs} +1 -0
- package/packages/comparison/favicon-similarity.mjs +78 -0
- package/{frontend/src/lib/analysis/page-similarity.js → packages/comparison/page-similarity.mjs} +4 -3
- package/{frontend/src/lib/analysis/relationship-evidence.js → packages/comparison/relationship-evidence.mjs} +6 -7
- package/packages/contracts/analyst-review-state-contract.mjs +61 -0
- package/packages/contracts/analyst-review-state.mjs +50 -0
- package/packages/contracts/case-portability.mjs +1310 -0
- package/packages/contracts/cli-command-catalogue.mjs +2 -0
- package/packages/contracts/cli-lookup.mjs +394 -0
- package/packages/contracts/domain-control-flight-recorder.mjs +461 -0
- package/packages/contracts/domain-control-manifest.mjs +1027 -0
- package/packages/contracts/domain-control-monitor.mjs +310 -0
- package/packages/contracts/domain-control-review.mjs +828 -0
- package/packages/contracts/domain-name.mjs +4 -0
- package/packages/contracts/external-observation-interchange.mjs +224 -0
- package/packages/contracts/extracted-domain-lifecycle.mjs +218 -0
- package/packages/contracts/investigation-portability.mjs +331 -0
- package/packages/contracts/offline-comparison.mjs +160 -0
- package/packages/contracts/risk-calibration.mjs +1288 -0
- package/packages/contracts/schema-compatibility.mjs +71 -0
- package/packages/contracts/schema-lifecycle.mjs +1702 -0
- package/packages/contracts/sslbl-snapshot.mjs +17 -0
- package/packages/contracts/workspace-portability.mjs +1347 -0
- package/{frontend/src/lib/analysis/artifact-integrity.js → packages/evidence/artifact-integrity.mjs} +13 -11
- package/packages/evidence/decision-fact.mjs +735 -0
- package/packages/evidence/domain-control-runtime.mjs +547 -0
- package/packages/evidence/domain-name.mjs +41 -0
- package/packages/evidence/observation.mjs +384 -0
- package/{frontend/src/lib/analysis/external-findings-import.js → packages/interchange/external-findings-import.mjs} +24 -22
- package/packages/interchange/retained-text.mjs +8 -0
- package/{frontend/src/lib/analysis/acquisition-decision-packet.js → packages/investigation/acquisition-decision-packet.mjs} +9 -6
- package/{frontend/src/lib/analysis/bulk-domain-comparison.js → packages/investigation/bulk-domain-comparison.mjs} +5 -6
- package/{frontend/src/lib/analysis/bulk-mail-exposure.js → packages/investigation/bulk-mail-exposure.mjs} +4 -6
- package/{frontend/src/lib/analysis/bulk-review-export.js → packages/investigation/bulk-review-export.mjs} +5 -5
- package/packages/investigation/bulk-review-policy.mjs +1 -0
- package/{frontend/src/lib/analysis/investigation-capsule.js → packages/investigation/investigation-capsule.mjs} +30 -32
- package/packages/investigation/lookup-artefact-inputs.mjs +1 -0
- package/{frontend/src/lib/analysis/lookup-asset-graph.js → packages/investigation/lookup-asset-graph.mjs} +4 -2
- package/{frontend/src/lib/analysis/lookup-claim-passport.js → packages/investigation/lookup-claim-passport.mjs} +6 -10
- package/{frontend/src/lib/analysis/lookup-claim-readiness.js → packages/investigation/lookup-claim-readiness.mjs} +9 -2
- package/packages/investigation/lookup-investigation-brief.mjs +172 -0
- package/packages/monitoring/analyst-review-state.mjs +680 -0
- package/{frontend/src/lib/analysis/brand-profile-model.js → packages/workspace/brand-profile-model.mjs} +70 -47
- package/{frontend/src/lib/analysis/bulk-review-model.js → packages/workspace/bulk-review-model.mjs} +13 -14
- package/{frontend/src/lib/analysis/bulk-session-model.js → packages/workspace/bulk-session-model.mjs} +33 -59
- package/{frontend/src/lib/analysis/bulk-sort.js → packages/workspace/bulk-sort.mjs} +8 -5
- package/{frontend/src/lib/analysis/campaign-model.js → packages/workspace/campaign-model.mjs} +20 -22
- package/{frontend/src/lib/analysis/detection-rule-model.js → packages/workspace/detection-rule-model.mjs} +15 -17
- package/packages/workspace/hostile-input.mjs +172 -0
- package/{frontend/src/lib/analysis/investigation-guide.js → packages/workspace/investigation-guide.mjs} +8 -45
- package/{frontend/src/lib/analysis/investigation-template-model.js → packages/workspace/investigation-template-model.mjs} +18 -21
- package/{frontend/src/lib/analysis/page-baseline.js → packages/workspace/page-baseline.mjs} +6 -13
- package/{frontend/src/lib/analysis/relationship-observation-model.js → packages/workspace/relationship-observation-model.mjs} +13 -17
- package/{frontend/src/lib/analysis/shortlist-model.js → packages/workspace/shortlist-model.mjs} +20 -18
- package/{frontend/src/lib/analysis/watchlist-history.js → packages/workspace/watchlist-history.mjs} +9 -16
- package/{frontend/src/lib/analysis/watchlist-store.js → packages/workspace/watchlist-store.mjs} +24 -17
- package/{frontend/src/lib/analysis/website-snapshot-model.js → packages/workspace/website-snapshot-model.mjs} +13 -13
- package/{frontend/src/lib/analysis/workspace-archive-crypto.js → packages/workspace/workspace-archive-crypto.mjs} +49 -19
- package/{frontend/src/lib/analysis/workspace-archive.js → packages/workspace/workspace-archive.mjs} +110 -89
- package/third-party-notices.txt +14 -14
- package/frontend/src/lib/analysis/bulk-retry-plan.js +0 -79
- package/frontend/src/lib/analysis/bulk-source-coverage.js +0 -76
- package/frontend/src/lib/analysis/case-migration-model.js +0 -305
- package/frontend/src/lib/analysis/case-record-model.js +0 -9
- package/frontend/src/lib/analysis/case-response-model.js +0 -728
- package/frontend/src/lib/analysis/case-response-packet.js +0 -583
- package/frontend/src/lib/analysis/lookup-decision-support.js +0 -542
- package/frontend/src/lib/analysis/lookup-investigation-brief.js +0 -153
- package/frontend/src/lib/analysis/lookup-presentation.js +0 -120
- package/frontend/src/lib/analysis/lookup-response.js +0 -2
- package/frontend/src/lib/analysis/lookup-source-refresh.js +0 -277
- package/frontend/src/lib/analysis/lookup-summary-model.js +0 -398
- package/frontend/src/lib/analysis/scoring.js +0 -55
- package/frontend/src/lib/analysis/utils.js +0 -255
- package/frontend/src/lib/analysis/visualization-bounds.js +0 -26
- package/frontend/src/lib/analysis/visualization-force-graph.js +0 -352
- package/frontend/src/lib/analysis/visualization-matrix.js +0 -131
- package/frontend/src/lib/analysis/visualization-models.js +0 -338
- package/frontend/src/lib/analysis/visualization-time-series.js +0 -185
- package/lib/bounded-json-response.mjs +0 -170
- package/lib/lookup-response-contract.mjs +0 -1034
- package/lib/observation.mjs +0 -115
- /package/{frontend/src/lib/analysis/opaque-reference-id.js → packages/cases/opaque-reference-id.mjs} +0 -0
- /package/{frontend/src/lib/analysis/acquisition-due-diligence.js → packages/investigation/acquisition-due-diligence.mjs} +0 -0
- /package/{frontend/src/lib/analysis/evidence-coverage-ledger.js → packages/investigation/evidence-coverage-ledger.mjs} +0 -0
- /package/{frontend/src/lib/analysis/dns-record-normalization.js → packages/workspace/dns-record-normalization.mjs} +0 -0
package/{frontend/src/lib/analysis/workspace-archive.js → packages/workspace/workspace-archive.mjs}
RENAMED
|
@@ -1,53 +1,27 @@
|
|
|
1
1
|
// Pure, bounded workspace archive composition. Existing typed store models remain the
|
|
2
2
|
// authority for normalization and merge semantics; this module only packages
|
|
3
3
|
// their portable contracts, verifies integrity, and previews conflicts.
|
|
4
|
-
import { buildCaseExport,
|
|
5
|
-
import { assertBrandProfileStoreBudget,
|
|
6
|
-
import { assertCampaignStoreBudget, buildCampaignExport,
|
|
7
|
-
import {
|
|
8
|
-
import {
|
|
9
|
-
import {
|
|
10
|
-
import {
|
|
11
|
-
import {
|
|
12
|
-
import {
|
|
13
|
-
import {
|
|
14
|
-
import {
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
export
|
|
22
|
-
export const MAX_WORKSPACE_ARCHIVE_SECTIONS = 12;
|
|
23
|
-
export const WORKSPACE_ARCHIVE_SECTION_IDS = [
|
|
24
|
-
'cases',
|
|
25
|
-
'campaigns',
|
|
26
|
-
'brandProfiles',
|
|
27
|
-
'watchlists',
|
|
28
|
-
'shortlist',
|
|
29
|
-
'detectionRules',
|
|
30
|
-
'relationshipObservations',
|
|
31
|
-
'bulkSessions',
|
|
32
|
-
'websiteSnapshots',
|
|
33
|
-
'investigationTemplates',
|
|
34
|
-
'bulkReview',
|
|
35
|
-
'settings',
|
|
36
|
-
];
|
|
37
|
-
export function isSupportedWorkspaceArchiveVersion(value) {
|
|
38
|
-
return typeof value === 'number'
|
|
39
|
-
&& Number.isSafeInteger(value)
|
|
40
|
-
&& SUPPORTED_WORKSPACE_ARCHIVE_VERSIONS.some((version) => version === value);
|
|
41
|
-
}
|
|
4
|
+
import { buildCaseExport, enforceStoreBudget, mergeCases, } from "../cases/case-model.mjs";
|
|
5
|
+
import { assertBrandProfileStoreBudget, buildBrandProfileExport, mergeBrandProfiles, normalizeBrandProfileId, } from "./brand-profile-model.mjs";
|
|
6
|
+
import { assertCampaignStoreBudget, buildCampaignExport, mergeCampaigns, } from "./campaign-model.mjs";
|
|
7
|
+
import { normalizeExplicitIsoTimestamp } from "../evidence/observation.mjs";
|
|
8
|
+
import { assertWorkspaceInputGraph } from "./hostile-input.mjs";
|
|
9
|
+
import { assertWatchlistStoreBudget, buildWatchlistExport, mergeWatchlistStores, } from "./watchlist-store.mjs";
|
|
10
|
+
import { assertShortlistStoreBudget, buildShortlistExport, mergeShortlistStores, } from "./shortlist-model.mjs";
|
|
11
|
+
import { assertDetectionRuleStoreBudget, buildDetectionRuleExport, mergeDetectionRules, } from "./detection-rule-model.mjs";
|
|
12
|
+
import { buildRelationshipObservationExport, mergeRelationshipObservations, } from "./relationship-observation-model.mjs";
|
|
13
|
+
import { buildBulkSessionExport, enforceBulkSessionStoreBudget, mergeBulkSessions, } from "./bulk-session-model.mjs";
|
|
14
|
+
import { buildWebsiteSnapshotExport, mergeWebsiteSnapshots, } from "./website-snapshot-model.mjs";
|
|
15
|
+
import { buildInvestigationTemplateExport, mergeInvestigationTemplates, } from "./investigation-template-model.mjs";
|
|
16
|
+
import { buildBulkReviewExport, mergeBulkReviewStores, } from "./bulk-review-model.mjs";
|
|
17
|
+
import { buildAnalystReviewStateExport, emptyAnalystReviewStateStore, mergeAnalystReviewStateStores, } from "../monitoring/analyst-review-state.mjs";
|
|
18
|
+
import { isSupportedWorkspaceArchiveVersion, MAX_WORKSPACE_ARCHIVE_BYTES, MAX_WORKSPACE_ARCHIVE_SECTION_BYTES, MAX_WORKSPACE_ARCHIVE_SECTIONS, serialiseWorkspaceArchiveSection, SUPPORTED_WORKSPACE_ARCHIVE_VERSIONS, WORKSPACE_ARCHIVE_CASE_SECTION, WORKSPACE_ARCHIVE_SCHEMA, WORKSPACE_ARCHIVE_SECTION_IDS, PUBLIC_WORKSPACE_ARCHIVE_SECTION_IDS, PUBLIC_WORKSPACE_ARCHIVE_VERSION, WORKSPACE_ARCHIVE_VERSION, WORKSPACE_SETTINGS_COMPATIBILITY, WORKSPACE_SETTINGS_SCHEMA, WORKSPACE_SETTINGS_VERSION, } from "../contracts/case-portability.mjs";
|
|
19
|
+
import { ANALYST_REVIEW_STATE_COMPATIBILITY } from "../contracts/analyst-review-state.mjs";
|
|
20
|
+
import { WORKSPACE_PORTABILITY_ARCHIVE_SECTION_REFERENCES } from "../contracts/workspace-portability.mjs";
|
|
21
|
+
export { isSupportedWorkspaceArchiveVersion, MAX_WORKSPACE_ARCHIVE_BYTES, MAX_WORKSPACE_ARCHIVE_SECTION_BYTES, MAX_WORKSPACE_ARCHIVE_SECTIONS, SUPPORTED_WORKSPACE_ARCHIVE_VERSIONS, WORKSPACE_ARCHIVE_CASE_SECTION, WORKSPACE_ARCHIVE_SCHEMA, WORKSPACE_ARCHIVE_SECTION_IDS, PUBLIC_WORKSPACE_ARCHIVE_SECTION_IDS, PUBLIC_WORKSPACE_ARCHIVE_VERSION, WORKSPACE_ARCHIVE_VERSION, WORKSPACE_SETTINGS_SCHEMA, WORKSPACE_SETTINGS_VERSION, };
|
|
42
22
|
const CONTROL_RE = /[\x00-\x1f\x7f]/;
|
|
43
23
|
const CHECKSUM_RE = /^sha256:[a-f0-9]{64}$/;
|
|
44
|
-
const WORKSPACE_ARCHIVE_ROOT_KEYS = Object.freeze(
|
|
45
|
-
1: Object.freeze(['schema', 'version', 'generatedAt', 'manifest', 'sections', 'limitations']),
|
|
46
|
-
2: Object.freeze(['schema', 'version', 'generatedAt', 'manifest', 'sections', 'limitations']),
|
|
47
|
-
3: Object.freeze(['schema', 'version', 'generatedAt', 'manifest', 'sections', 'limitations']),
|
|
48
|
-
4: Object.freeze(['schema', 'version', 'generatedAt', 'manifest', 'sections', 'limitations']),
|
|
49
|
-
5: Object.freeze(['schema', 'version', 'generatedAt', 'manifest', 'sections', 'limitations']),
|
|
50
|
-
});
|
|
24
|
+
const WORKSPACE_ARCHIVE_ROOT_KEYS = Object.freeze(['schema', 'version', 'generatedAt', 'manifest', 'sections', 'limitations']);
|
|
51
25
|
const WORKSPACE_ARCHIVE_MANIFEST_KEYS = Object.freeze(['sectionCount', 'totalRecords', 'sections']);
|
|
52
26
|
const WORKSPACE_ARCHIVE_MANIFEST_ENTRY_KEYS = Object.freeze(['id', 'schema', 'version', 'recordCount', 'bytes', 'checksum']);
|
|
53
27
|
function record(value) {
|
|
@@ -64,10 +38,7 @@ function byteLength(value) {
|
|
|
64
38
|
}
|
|
65
39
|
function serialize(value, message = 'The workspace archive contains data that cannot be serialised.') {
|
|
66
40
|
try {
|
|
67
|
-
|
|
68
|
-
if (typeof serialized !== 'string')
|
|
69
|
-
throw new Error(message);
|
|
70
|
-
return serialized;
|
|
41
|
+
return serialiseWorkspaceArchiveSection(value);
|
|
71
42
|
}
|
|
72
43
|
catch {
|
|
73
44
|
throw new Error(message);
|
|
@@ -77,10 +48,10 @@ function clone(value) {
|
|
|
77
48
|
return JSON.parse(serialize(value));
|
|
78
49
|
}
|
|
79
50
|
function timestamp(value, fallback = null) {
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
return
|
|
51
|
+
const explicit = normalizeExplicitIsoTimestamp(value);
|
|
52
|
+
if (explicit)
|
|
53
|
+
return explicit;
|
|
54
|
+
return fallback;
|
|
84
55
|
}
|
|
85
56
|
function boundedText(value, maximum = 300) {
|
|
86
57
|
if (typeof value !== 'string' || value.length > maximum * 4 || CONTROL_RE.test(value))
|
|
@@ -139,6 +110,7 @@ function workspaceArchiveSections(input, now) {
|
|
|
139
110
|
websiteSnapshots: buildWebsiteSnapshotExport(input.websiteSnapshots, now),
|
|
140
111
|
investigationTemplates: buildInvestigationTemplateExport(input.investigationTemplates, now),
|
|
141
112
|
bulkReview: buildBulkReviewExport(input.bulkReview),
|
|
113
|
+
analystReviewState: buildAnalystReviewStateExport(input.analystReviewState),
|
|
142
114
|
settings: settingsDocument(input),
|
|
143
115
|
};
|
|
144
116
|
}
|
|
@@ -151,10 +123,29 @@ function objectCount(data, key) {
|
|
|
151
123
|
const nested = value ? record(value[key]) : null;
|
|
152
124
|
return nested ? Object.keys(nested).length : 0;
|
|
153
125
|
}
|
|
126
|
+
function sectionContract(sectionId) {
|
|
127
|
+
const reference = WORKSPACE_PORTABILITY_ARCHIVE_SECTION_REFERENCES.find((entry) => entry.sectionId === sectionId);
|
|
128
|
+
if (!reference)
|
|
129
|
+
throw new TypeError(`Workspace archive section ${sectionId} has no canonical contract reference.`);
|
|
130
|
+
return Object.freeze({
|
|
131
|
+
id: reference.sectionId,
|
|
132
|
+
schema: reference.schema,
|
|
133
|
+
version: reference.version,
|
|
134
|
+
supportedVersions: reference.supportedVersions,
|
|
135
|
+
});
|
|
136
|
+
}
|
|
137
|
+
function compatibilitySchema(descriptor) {
|
|
138
|
+
if (!descriptor.schema)
|
|
139
|
+
throw new TypeError(`Workspace compatibility ${descriptor.id} must name a schema.`);
|
|
140
|
+
return descriptor.schema;
|
|
141
|
+
}
|
|
154
142
|
const SECTION_DEFINITIONS = [
|
|
155
143
|
{
|
|
156
|
-
id:
|
|
157
|
-
|
|
144
|
+
id: WORKSPACE_ARCHIVE_CASE_SECTION.id,
|
|
145
|
+
label: 'Cases',
|
|
146
|
+
schema: WORKSPACE_ARCHIVE_CASE_SECTION.schema,
|
|
147
|
+
version: WORKSPACE_ARCHIVE_CASE_SECTION.currentVersion,
|
|
148
|
+
supportedVersions: WORKSPACE_ARCHIVE_CASE_SECTION.supportedVersions,
|
|
158
149
|
count: (data) => arrayCount(data, 'cases'),
|
|
159
150
|
merge: (local, data) => {
|
|
160
151
|
const result = mergeCases(local.cases, data);
|
|
@@ -163,7 +154,7 @@ const SECTION_DEFINITIONS = [
|
|
|
163
154
|
},
|
|
164
155
|
},
|
|
165
156
|
{
|
|
166
|
-
|
|
157
|
+
...sectionContract('campaigns'), label: 'Campaigns',
|
|
167
158
|
count: (data) => arrayCount(data, 'campaigns'),
|
|
168
159
|
merge: (local, data) => {
|
|
169
160
|
const result = mergeCampaigns(local.campaigns, data);
|
|
@@ -171,8 +162,7 @@ const SECTION_DEFINITIONS = [
|
|
|
171
162
|
},
|
|
172
163
|
},
|
|
173
164
|
{
|
|
174
|
-
|
|
175
|
-
supportedVersions: SUPPORTED_BRAND_PROFILE_SCHEMA_VERSIONS,
|
|
165
|
+
...sectionContract('brandProfiles'), label: 'Brand profiles',
|
|
176
166
|
count: (data) => arrayCount(data, 'profiles'),
|
|
177
167
|
merge: (local, data, now) => {
|
|
178
168
|
const result = mergeBrandProfiles(local.brandProfiles, data, { nowIso: now });
|
|
@@ -180,7 +170,7 @@ const SECTION_DEFINITIONS = [
|
|
|
180
170
|
},
|
|
181
171
|
},
|
|
182
172
|
{
|
|
183
|
-
|
|
173
|
+
...sectionContract('watchlists'), label: 'Watchlists',
|
|
184
174
|
count: (data) => objectCount(data, 'watchlists'),
|
|
185
175
|
merge: (local, data) => {
|
|
186
176
|
const result = mergeWatchlistStores(local.watchlists, data);
|
|
@@ -188,8 +178,7 @@ const SECTION_DEFINITIONS = [
|
|
|
188
178
|
},
|
|
189
179
|
},
|
|
190
180
|
{
|
|
191
|
-
|
|
192
|
-
supportedVersions: SUPPORTED_SHORTLIST_SCHEMA_VERSIONS,
|
|
181
|
+
...sectionContract('shortlist'), label: 'Shortlist',
|
|
193
182
|
count: (data) => arrayCount(data, 'entries'),
|
|
194
183
|
merge: (local, data) => {
|
|
195
184
|
const result = mergeShortlistStores(local.shortlist, data);
|
|
@@ -197,7 +186,7 @@ const SECTION_DEFINITIONS = [
|
|
|
197
186
|
},
|
|
198
187
|
},
|
|
199
188
|
{
|
|
200
|
-
|
|
189
|
+
...sectionContract('detectionRules'), label: 'Detection rules',
|
|
201
190
|
count: (data) => arrayCount(data, 'rules'),
|
|
202
191
|
merge: (local, data) => {
|
|
203
192
|
const result = mergeDetectionRules(local.detectionRules, data);
|
|
@@ -205,19 +194,14 @@ const SECTION_DEFINITIONS = [
|
|
|
205
194
|
},
|
|
206
195
|
},
|
|
207
196
|
{
|
|
208
|
-
|
|
197
|
+
...sectionContract('relationshipObservations'),
|
|
209
198
|
label: 'Retained relationship observations',
|
|
210
|
-
schema: RELATIONSHIP_OBSERVATION_SCHEMA,
|
|
211
|
-
version: RELATIONSHIP_OBSERVATION_SCHEMA_VERSION,
|
|
212
199
|
count: (data) => arrayCount(data, 'observations'),
|
|
213
200
|
merge: (local, data) => mergeRelationshipObservations(local.relationshipObservations, data),
|
|
214
201
|
},
|
|
215
202
|
{
|
|
216
|
-
|
|
203
|
+
...sectionContract('bulkSessions'),
|
|
217
204
|
label: 'Saved Bulk sessions',
|
|
218
|
-
schema: BULK_SESSION_SCHEMA,
|
|
219
|
-
version: BULK_SESSION_SCHEMA_VERSION,
|
|
220
|
-
supportedVersions: SUPPORTED_BULK_SESSION_SCHEMA_VERSIONS,
|
|
221
205
|
count: (data) => arrayCount(data, 'sessions'),
|
|
222
206
|
merge: (local, data) => {
|
|
223
207
|
const result = mergeBulkSessions(local.bulkSessions, data);
|
|
@@ -226,32 +210,36 @@ const SECTION_DEFINITIONS = [
|
|
|
226
210
|
},
|
|
227
211
|
},
|
|
228
212
|
{
|
|
229
|
-
|
|
213
|
+
...sectionContract('websiteSnapshots'),
|
|
230
214
|
label: 'Website profile snapshots',
|
|
231
|
-
schema: WEBSITE_SNAPSHOT_SCHEMA,
|
|
232
|
-
version: WEBSITE_SNAPSHOT_SCHEMA_VERSION,
|
|
233
|
-
supportedVersions: SUPPORTED_WEBSITE_SNAPSHOT_SCHEMA_VERSIONS,
|
|
234
215
|
count: (data) => arrayCount(data, 'snapshots'),
|
|
235
216
|
merge: (local, data) => mergeWebsiteSnapshots(local.websiteSnapshots, data),
|
|
236
217
|
},
|
|
237
218
|
{
|
|
238
|
-
|
|
219
|
+
...sectionContract('investigationTemplates'),
|
|
239
220
|
label: 'Investigation templates',
|
|
240
|
-
schema: INVESTIGATION_TEMPLATE_SCHEMA,
|
|
241
|
-
version: INVESTIGATION_TEMPLATE_VERSION,
|
|
242
221
|
count: (data) => arrayCount(data, 'templates'),
|
|
243
222
|
merge: (local, data) => mergeInvestigationTemplates(local.investigationTemplates, data),
|
|
244
223
|
},
|
|
245
224
|
{
|
|
246
|
-
|
|
225
|
+
...sectionContract('bulkReview'),
|
|
247
226
|
label: 'Bulk saved views and review queue',
|
|
248
|
-
schema: BULK_REVIEW_SCHEMA,
|
|
249
|
-
version: BULK_REVIEW_SCHEMA_VERSION,
|
|
250
227
|
count: (data) => arrayCount(data, 'presets') + arrayCount(data, 'rows'),
|
|
251
228
|
merge: (local, data) => mergeBulkReviewStores(local.bulkReview, data),
|
|
252
229
|
},
|
|
253
230
|
{
|
|
254
|
-
id: '
|
|
231
|
+
id: 'analystReviewState',
|
|
232
|
+
label: 'Analyst Review Item lifecycle',
|
|
233
|
+
schema: compatibilitySchema(ANALYST_REVIEW_STATE_COMPATIBILITY),
|
|
234
|
+
version: ANALYST_REVIEW_STATE_COMPATIBILITY.currentVersion,
|
|
235
|
+
supportedVersions: ANALYST_REVIEW_STATE_COMPATIBILITY.supportedVersions,
|
|
236
|
+
count: (data) => arrayCount(data, 'records'),
|
|
237
|
+
merge: (local, data) => mergeAnalystReviewStateStores(local.analystReviewState, data),
|
|
238
|
+
},
|
|
239
|
+
{
|
|
240
|
+
id: 'settings', label: 'Workspace settings', schema: compatibilitySchema(WORKSPACE_SETTINGS_COMPATIBILITY),
|
|
241
|
+
version: WORKSPACE_SETTINGS_COMPATIBILITY.currentVersion,
|
|
242
|
+
supportedVersions: WORKSPACE_SETTINGS_COMPATIBILITY.supportedVersions,
|
|
255
243
|
count: () => 1,
|
|
256
244
|
merge: null,
|
|
257
245
|
},
|
|
@@ -277,6 +265,7 @@ function normalizedInput(input) {
|
|
|
277
265
|
websiteSnapshots: Array.isArray(value.websiteSnapshots) ? value.websiteSnapshots : [],
|
|
278
266
|
investigationTemplates: Array.isArray(value.investigationTemplates) ? value.investigationTemplates : [],
|
|
279
267
|
bulkReview: record(value.bulkReview) || {},
|
|
268
|
+
analystReviewState: record(value.analystReviewState) || emptyAnalystReviewStateStore(),
|
|
280
269
|
settings: record(value.settings) || {},
|
|
281
270
|
};
|
|
282
271
|
}
|
|
@@ -290,7 +279,9 @@ function ensureArchiveBudget(value) {
|
|
|
290
279
|
}
|
|
291
280
|
/** Build one deterministic, unencrypted archive from normalized local stores. */
|
|
292
281
|
export async function buildWorkspaceArchive(input, options = {}) {
|
|
293
|
-
const now = timestamp(options.generatedAt
|
|
282
|
+
const now = timestamp(options.generatedAt ?? new Date().toISOString());
|
|
283
|
+
if (!now)
|
|
284
|
+
throw new Error('Workspace archive generation time must use an explicit timezone.');
|
|
294
285
|
const source = normalizedInput(input);
|
|
295
286
|
const sections = workspaceArchiveSections(source, now);
|
|
296
287
|
const manifestSections = [];
|
|
@@ -347,19 +338,29 @@ function manifestEntry(raw) {
|
|
|
347
338
|
}
|
|
348
339
|
/** Validate structure, section byte counts, and checksums without applying data. */
|
|
349
340
|
export async function readWorkspaceArchive(raw, options = {}) {
|
|
341
|
+
assertWorkspaceInputGraph(raw, 'Workspace archive');
|
|
350
342
|
const value = record(raw);
|
|
351
343
|
if (!value || value.schema !== WORKSPACE_ARCHIVE_SCHEMA) {
|
|
352
344
|
throw new Error('This file is not a WHOISleuth workspace archive.');
|
|
353
345
|
}
|
|
354
346
|
if (!isSupportedWorkspaceArchiveVersion(value.version)) {
|
|
347
|
+
if (typeof value.version === 'number' && Number.isSafeInteger(value.version) && value.version < WORKSPACE_ARCHIVE_VERSION) {
|
|
348
|
+
throw new Error(`Workspace archive schema ${value.version} is retired. Export it as schema ${WORKSPACE_ARCHIVE_VERSION} with the last broad-reader release before importing; no data was changed.`);
|
|
349
|
+
}
|
|
355
350
|
if (typeof value.version === 'number' && Number.isSafeInteger(value.version) && value.version > WORKSPACE_ARCHIVE_VERSION) {
|
|
356
|
-
throw new Error(`
|
|
351
|
+
throw new Error(`Workspace archive schema ${value.version} is newer than the supported schema ${WORKSPACE_ARCHIVE_VERSION}; no data was changed.`);
|
|
357
352
|
}
|
|
358
|
-
throw new Error(`Expected workspace archive schema
|
|
353
|
+
throw new Error(`Expected a well-formed workspace archive using schema ${WORKSPACE_ARCHIVE_VERSION}; no data was changed.`);
|
|
359
354
|
}
|
|
360
355
|
const sourceVersion = value.version;
|
|
356
|
+
const expectedSectionIds = sourceVersion === PUBLIC_WORKSPACE_ARCHIVE_VERSION
|
|
357
|
+
? PUBLIC_WORKSPACE_ARCHIVE_SECTION_IDS
|
|
358
|
+
: WORKSPACE_ARCHIVE_SECTION_IDS;
|
|
359
|
+
const generatedAt = timestamp(value.generatedAt);
|
|
360
|
+
if (!generatedAt)
|
|
361
|
+
throw new Error('The workspace archive generation time is invalid.');
|
|
361
362
|
const { bytes } = ensureArchiveBudget(value);
|
|
362
|
-
assertExactKeys(value, WORKSPACE_ARCHIVE_ROOT_KEYS
|
|
363
|
+
assertExactKeys(value, WORKSPACE_ARCHIVE_ROOT_KEYS, `version ${sourceVersion} envelope`);
|
|
363
364
|
const manifest = record(value.manifest);
|
|
364
365
|
const sectionValues = record(value.sections);
|
|
365
366
|
if (!manifest || !sectionValues || !Array.isArray(manifest.sections)) {
|
|
@@ -413,9 +414,11 @@ export async function readWorkspaceArchive(raw, options = {}) {
|
|
|
413
414
|
}
|
|
414
415
|
else if (!(definition.supportedVersions ?? [definition.version]).includes(entry.version) || entry.schema !== definition.schema) {
|
|
415
416
|
status = 'unsupported';
|
|
416
|
-
reason = entry.
|
|
417
|
-
?
|
|
418
|
-
:
|
|
417
|
+
reason = entry.schema !== definition.schema
|
|
418
|
+
? 'This section uses an unsupported schema contract.'
|
|
419
|
+
: entry.version > definition.version
|
|
420
|
+
? `This section uses newer schema ${entry.version}.`
|
|
421
|
+
: `This section uses retired schema ${entry.version}. Export it with the last broad-reader release before importing; no data was changed.`;
|
|
419
422
|
}
|
|
420
423
|
else if (definition.count(data) !== entry.recordCount) {
|
|
421
424
|
throw new Error(`${entry.id} does not match its manifest record count.`);
|
|
@@ -429,12 +432,30 @@ export async function readWorkspaceArchive(raw, options = {}) {
|
|
|
429
432
|
if (manifest.sectionCount !== sections.length || manifest.totalRecords !== totalRecords) {
|
|
430
433
|
throw new Error('The workspace archive manifest totals do not match its sections.');
|
|
431
434
|
}
|
|
435
|
+
if (seen.size !== expectedSectionIds.length || expectedSectionIds.some((id) => !seen.has(id))) {
|
|
436
|
+
throw new Error(`Workspace archive schema ${sourceVersion} does not contain its exact required section set.`);
|
|
437
|
+
}
|
|
438
|
+
if (sourceVersion === PUBLIC_WORKSPACE_ARCHIVE_VERSION) {
|
|
439
|
+
const data = emptyAnalystReviewStateStore();
|
|
440
|
+
sections.push({
|
|
441
|
+
id: 'analystReviewState',
|
|
442
|
+
label: 'Analyst Review Item lifecycle',
|
|
443
|
+
schema: compatibilitySchema(ANALYST_REVIEW_STATE_COMPATIBILITY),
|
|
444
|
+
version: ANALYST_REVIEW_STATE_COMPATIBILITY.currentVersion,
|
|
445
|
+
recordCount: 0,
|
|
446
|
+
bytes: byteLength(serialize(data)),
|
|
447
|
+
checksum: await checksum(data, options.cryptoProvider),
|
|
448
|
+
status: 'ready',
|
|
449
|
+
reason: `Workspace schema ${PUBLIC_WORKSPACE_ARCHIVE_VERSION} did not contain Review Item lifecycle state. Import supplies an empty section and invents no analyst decisions.`,
|
|
450
|
+
data,
|
|
451
|
+
});
|
|
452
|
+
}
|
|
432
453
|
sections.sort(canonicalSectionOrder);
|
|
433
454
|
return {
|
|
434
455
|
schema: WORKSPACE_ARCHIVE_SCHEMA,
|
|
435
456
|
version: WORKSPACE_ARCHIVE_VERSION,
|
|
436
457
|
sourceVersion,
|
|
437
|
-
generatedAt
|
|
458
|
+
generatedAt,
|
|
438
459
|
bytes,
|
|
439
460
|
sections,
|
|
440
461
|
limitations: Array.isArray(value.limitations)
|
|
@@ -501,7 +522,7 @@ export async function previewWorkspaceArchive(raw, localInput, options = {}) {
|
|
|
501
522
|
status: 'ready',
|
|
502
523
|
reason: definition.id === 'settings' && selected && (result.skipped ?? 0) > 0
|
|
503
524
|
? result.reason ?? 'The imported workspace preference was skipped. Existing browser-local context is preserved.'
|
|
504
|
-
:
|
|
525
|
+
: section.reason,
|
|
505
526
|
added: result.added ?? 0,
|
|
506
527
|
updated: result.updated ?? 0,
|
|
507
528
|
skipped: result.skipped ?? 0,
|
package/third-party-notices.txt
CHANGED
|
@@ -7,7 +7,7 @@ The inventory is the exact transitive closure of the packaged CLI runtime depend
|
|
|
7
7
|
Package count: 27
|
|
8
8
|
|
|
9
9
|
================================================================================
|
|
10
|
-
@peculiar/asn1-cms@2.
|
|
10
|
+
@peculiar/asn1-cms@2.9.4
|
|
11
11
|
Relationship: transitive production dependency
|
|
12
12
|
Declared licence: MIT
|
|
13
13
|
Licence source: LICENSE
|
|
@@ -34,7 +34,7 @@ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
|
34
34
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
35
35
|
SOFTWARE.
|
|
36
36
|
================================================================================
|
|
37
|
-
@peculiar/asn1-csr@2.
|
|
37
|
+
@peculiar/asn1-csr@2.9.4
|
|
38
38
|
Relationship: transitive production dependency
|
|
39
39
|
Declared licence: MIT
|
|
40
40
|
Licence source: LICENSE
|
|
@@ -61,7 +61,7 @@ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
|
61
61
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
62
62
|
SOFTWARE.
|
|
63
63
|
================================================================================
|
|
64
|
-
@peculiar/asn1-ecc@2.
|
|
64
|
+
@peculiar/asn1-ecc@2.9.4
|
|
65
65
|
Relationship: transitive production dependency
|
|
66
66
|
Declared licence: MIT
|
|
67
67
|
Licence source: LICENSE
|
|
@@ -88,7 +88,7 @@ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
|
88
88
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
89
89
|
SOFTWARE.
|
|
90
90
|
================================================================================
|
|
91
|
-
@peculiar/asn1-pfx@2.
|
|
91
|
+
@peculiar/asn1-pfx@2.9.4
|
|
92
92
|
Relationship: transitive production dependency
|
|
93
93
|
Declared licence: MIT
|
|
94
94
|
Licence source: LICENSE
|
|
@@ -115,7 +115,7 @@ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
|
115
115
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
116
116
|
SOFTWARE.
|
|
117
117
|
================================================================================
|
|
118
|
-
@peculiar/asn1-pkcs8@2.
|
|
118
|
+
@peculiar/asn1-pkcs8@2.9.4
|
|
119
119
|
Relationship: transitive production dependency
|
|
120
120
|
Declared licence: MIT
|
|
121
121
|
Licence source: LICENSE
|
|
@@ -142,7 +142,7 @@ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
|
142
142
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
143
143
|
SOFTWARE.
|
|
144
144
|
================================================================================
|
|
145
|
-
@peculiar/asn1-pkcs9@2.
|
|
145
|
+
@peculiar/asn1-pkcs9@2.9.4
|
|
146
146
|
Relationship: transitive production dependency
|
|
147
147
|
Declared licence: MIT
|
|
148
148
|
Licence source: LICENSE
|
|
@@ -169,7 +169,7 @@ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
|
169
169
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
170
170
|
SOFTWARE.
|
|
171
171
|
================================================================================
|
|
172
|
-
@peculiar/asn1-rsa@2.
|
|
172
|
+
@peculiar/asn1-rsa@2.9.4
|
|
173
173
|
Relationship: transitive production dependency
|
|
174
174
|
Declared licence: MIT
|
|
175
175
|
Licence source: LICENSE
|
|
@@ -196,7 +196,7 @@ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
|
196
196
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
197
197
|
SOFTWARE.
|
|
198
198
|
================================================================================
|
|
199
|
-
@peculiar/asn1-schema@2.
|
|
199
|
+
@peculiar/asn1-schema@2.9.4
|
|
200
200
|
Relationship: transitive production dependency
|
|
201
201
|
Declared licence: MIT
|
|
202
202
|
Licence source: LICENSE
|
|
@@ -223,7 +223,7 @@ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
|
223
223
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
224
224
|
SOFTWARE.
|
|
225
225
|
================================================================================
|
|
226
|
-
@peculiar/asn1-x509@2.
|
|
226
|
+
@peculiar/asn1-x509@2.9.4
|
|
227
227
|
Relationship: transitive production dependency
|
|
228
228
|
Declared licence: MIT
|
|
229
229
|
Licence source: LICENSE
|
|
@@ -250,7 +250,7 @@ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
|
250
250
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
251
251
|
SOFTWARE.
|
|
252
252
|
================================================================================
|
|
253
|
-
@peculiar/asn1-x509-attr@2.
|
|
253
|
+
@peculiar/asn1-x509-attr@2.9.4
|
|
254
254
|
Relationship: transitive production dependency
|
|
255
255
|
Declared licence: MIT
|
|
256
256
|
Licence source: LICENSE
|
|
@@ -488,7 +488,7 @@ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
|
488
488
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
489
489
|
SOFTWARE.
|
|
490
490
|
================================================================================
|
|
491
|
-
pvutils@1.
|
|
491
|
+
pvutils@1.2.0
|
|
492
492
|
Relationship: transitive production dependency
|
|
493
493
|
Declared licence: MIT
|
|
494
494
|
Licence source: LICENSE
|
|
@@ -612,7 +612,7 @@ CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
|
|
612
612
|
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
|
613
613
|
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
614
614
|
================================================================================
|
|
615
|
-
tldts@7.4.
|
|
615
|
+
tldts@7.4.11
|
|
616
616
|
Relationship: direct production dependency
|
|
617
617
|
Declared licence: MIT
|
|
618
618
|
Licence source: LICENSE
|
|
@@ -631,7 +631,7 @@ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLI
|
|
|
631
631
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
|
632
632
|
WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
633
633
|
================================================================================
|
|
634
|
-
tldts-core@7.4.
|
|
634
|
+
tldts-core@7.4.11
|
|
635
635
|
Relationship: transitive production dependency
|
|
636
636
|
Declared licence: MIT
|
|
637
637
|
Licence source: LICENSE
|
|
@@ -713,7 +713,7 @@ MIT License
|
|
|
713
713
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
714
714
|
SOFTWARE
|
|
715
715
|
================================================================================
|
|
716
|
-
undici@8.
|
|
716
|
+
undici@8.10.0
|
|
717
717
|
Relationship: direct production dependency
|
|
718
718
|
Declared licence: MIT
|
|
719
719
|
Licence source: LICENSE
|
|
@@ -1,79 +0,0 @@
|
|
|
1
|
-
import { normalizeBulkSessionResult, } from "./bulk-session-model.js";
|
|
2
|
-
import { limitedBulkSources } from "./bulk-source-coverage.js";
|
|
3
|
-
export const MAX_BULK_RETRY_ROWS = 200;
|
|
4
|
-
export const BULK_REVIEW_STALE_AFTER_DAYS = 7;
|
|
5
|
-
function freshness(value, now = Date.now()) {
|
|
6
|
-
if (typeof value !== 'string' || !Number.isFinite(Date.parse(value))) {
|
|
7
|
-
return { observedAt: null, ageDays: null, state: 'unknown' };
|
|
8
|
-
}
|
|
9
|
-
const observedAt = new Date(value).toISOString();
|
|
10
|
-
const ageDays = Math.max(0, Math.floor((now - Date.parse(observedAt)) / 86_400_000));
|
|
11
|
-
return {
|
|
12
|
-
observedAt,
|
|
13
|
-
ageDays,
|
|
14
|
-
state: ageDays >= BULK_REVIEW_STALE_AFTER_DAYS ? 'stale' : 'current',
|
|
15
|
-
};
|
|
16
|
-
}
|
|
17
|
-
function retryRow(value, isStale) {
|
|
18
|
-
const limitedSources = limitedBulkSources(value.domain, value.sourceCoverage)
|
|
19
|
-
.sort()
|
|
20
|
-
.slice(0, 12);
|
|
21
|
-
const reasons = [];
|
|
22
|
-
if (value.status === 'error')
|
|
23
|
-
reasons.push('error');
|
|
24
|
-
if (limitedSources.length)
|
|
25
|
-
reasons.push('limited_source');
|
|
26
|
-
if (isStale)
|
|
27
|
-
reasons.push('stale');
|
|
28
|
-
return reasons.length ? { domain: value.domain, reasons, limitedSources } : null;
|
|
29
|
-
}
|
|
30
|
-
export function buildBulkRetryPlan(values, mode, observedAtRaw = null, now = Date.now()) {
|
|
31
|
-
const observationFreshness = freshness(observedAtRaw, now);
|
|
32
|
-
const candidates = values
|
|
33
|
-
.map((row) => normalizeBulkSessionResult(row))
|
|
34
|
-
.filter((value) => Boolean(value))
|
|
35
|
-
.map((value) => retryRow(value, observationFreshness.state === 'stale'))
|
|
36
|
-
.filter((value) => Boolean(value));
|
|
37
|
-
const rows = candidates.slice(0, MAX_BULK_RETRY_ROWS);
|
|
38
|
-
return {
|
|
39
|
-
mode,
|
|
40
|
-
lookupRequests: rows.length,
|
|
41
|
-
rows,
|
|
42
|
-
destinations: mode === 'deep'
|
|
43
|
-
? ['registry RDAP', 'registrar RDAP when advertised', 'WHOIS', 'DNS resolvers', 'target HTTP(S)', 'target TLS']
|
|
44
|
-
: ['registry RDAP', 'authoritative DNS delegation when registration sources are inconclusive'],
|
|
45
|
-
capped: candidates.length > rows.length,
|
|
46
|
-
freshness: observationFreshness,
|
|
47
|
-
limitations: [
|
|
48
|
-
'This repeats the selected lookup profile; the current API does not request only one upstream source.',
|
|
49
|
-
`Evidence is marked stale after ${BULK_REVIEW_STALE_AFTER_DAYS} days for review purposes; this is a local observation age, not an upstream record age.`,
|
|
50
|
-
'Optional third-party intelligence providers are not contacted by Bulk.',
|
|
51
|
-
'Sources deliberately skipped by the selected scan profile or deployment policy do not independently trigger a retry.',
|
|
52
|
-
'A retry can remain partial or fail and does not convert unavailable evidence into absence.',
|
|
53
|
-
],
|
|
54
|
-
};
|
|
55
|
-
}
|
|
56
|
-
export function preservePriorBulkResult(previousRaw, currentRaw) {
|
|
57
|
-
const previous = normalizeBulkSessionResult(previousRaw);
|
|
58
|
-
const current = normalizeBulkSessionResult(currentRaw);
|
|
59
|
-
if (!previous || !current || previous.domain !== current.domain || previous.status !== 'complete') {
|
|
60
|
-
return { preserve: false, reason: '' };
|
|
61
|
-
}
|
|
62
|
-
if (current.status === 'error') {
|
|
63
|
-
return { preserve: true, reason: 'The retry failed, so the prior settled result was retained.' };
|
|
64
|
-
}
|
|
65
|
-
if (previous.scanDepth === 'deep' && current.scanDepth === 'fast') {
|
|
66
|
-
return { preserve: true, reason: 'The retry returned only Fast evidence, so the prior Deep result was retained.' };
|
|
67
|
-
}
|
|
68
|
-
const currentSources = new Map(current.sourceCoverage.map((item) => [item.source, item.state]));
|
|
69
|
-
const regressed = previous.sourceCoverage
|
|
70
|
-
.filter((item) => item.state === 'complete')
|
|
71
|
-
.map((item) => item.source)
|
|
72
|
-
.filter((source) => currentSources.get(source) !== 'complete');
|
|
73
|
-
return regressed.length
|
|
74
|
-
? {
|
|
75
|
-
preserve: true,
|
|
76
|
-
reason: `The retry reduced complete coverage for ${regressed.slice(0, 6).join(', ')}, so the prior settled result was retained.`,
|
|
77
|
-
}
|
|
78
|
-
: { preserve: false, reason: '' };
|
|
79
|
-
}
|
|
@@ -1,76 +0,0 @@
|
|
|
1
|
-
// Authority-aware interpretation for compact Bulk source observations. Raw
|
|
2
|
-
// source states remain unchanged; this layer only decides whether an observed
|
|
3
|
-
// limitation was unexpected for the registry's published service paths.
|
|
4
|
-
import { registryCapabilityFor } from "../../../../lib/registry-capabilities.mjs";
|
|
5
|
-
const MAX_SOURCE_OBSERVATIONS = 32;
|
|
6
|
-
const MAX_SOURCE_TEXT_LENGTH = 40;
|
|
7
|
-
const CONTROL_RE = /[\u0000-\u001f\u007f]/u;
|
|
8
|
-
const LIMITED_SOURCE_STATES = new Set([
|
|
9
|
-
'error',
|
|
10
|
-
'failed',
|
|
11
|
-
'inconclusive',
|
|
12
|
-
'partial',
|
|
13
|
-
'rate_limited',
|
|
14
|
-
'unavailable',
|
|
15
|
-
'unsupported',
|
|
16
|
-
]);
|
|
17
|
-
function text(value) {
|
|
18
|
-
return typeof value === 'string'
|
|
19
|
-
&& value.length <= MAX_SOURCE_TEXT_LENGTH
|
|
20
|
-
&& !CONTROL_RE.test(value)
|
|
21
|
-
? value.trim().toLowerCase()
|
|
22
|
-
: '';
|
|
23
|
-
}
|
|
24
|
-
function record(value) {
|
|
25
|
-
return value && typeof value === 'object' && !Array.isArray(value)
|
|
26
|
-
? value
|
|
27
|
-
: null;
|
|
28
|
-
}
|
|
29
|
-
function observations(value) {
|
|
30
|
-
return Array.isArray(value)
|
|
31
|
-
? value.slice(0, MAX_SOURCE_OBSERVATIONS).flatMap((entry) => {
|
|
32
|
-
const item = record(entry);
|
|
33
|
-
const source = text(item?.source);
|
|
34
|
-
const state = text(item?.state);
|
|
35
|
-
return source && state ? [{ source, state }] : [];
|
|
36
|
-
})
|
|
37
|
-
: [];
|
|
38
|
-
}
|
|
39
|
-
function expectedUnsupportedSources(domain) {
|
|
40
|
-
const capability = registryCapabilityFor(domain);
|
|
41
|
-
if (!capability)
|
|
42
|
-
return new Set();
|
|
43
|
-
const expected = new Set();
|
|
44
|
-
if (capability.rdapAccessProfile === 'no-iana-service')
|
|
45
|
-
expected.add('rdap');
|
|
46
|
-
if (capability.whoisAccessProfile === 'no-iana-service')
|
|
47
|
-
expected.add('whois');
|
|
48
|
-
return expected;
|
|
49
|
-
}
|
|
50
|
-
export function isExpectedUnsupportedBulkSource(domain, observation) {
|
|
51
|
-
const source = text(observation.source);
|
|
52
|
-
const state = text(observation.state);
|
|
53
|
-
return state === 'unsupported' && expectedUnsupportedSources(domain).has(source);
|
|
54
|
-
}
|
|
55
|
-
export function limitedBulkSources(domain, coverage) {
|
|
56
|
-
const expectedUnsupported = expectedUnsupportedSources(domain);
|
|
57
|
-
return observations(coverage)
|
|
58
|
-
.filter(({ source, state }) => (LIMITED_SOURCE_STATES.has(state)
|
|
59
|
-
&& !(state === 'unsupported' && expectedUnsupported.has(source))))
|
|
60
|
-
.map(({ source }) => source);
|
|
61
|
-
}
|
|
62
|
-
export function classifyBulkSourceCoverage(domain, coverage) {
|
|
63
|
-
const retained = observations(coverage);
|
|
64
|
-
if (!retained.length)
|
|
65
|
-
return 'unrecorded';
|
|
66
|
-
return limitedBulkSources(domain, retained).length ? 'limited' : 'complete';
|
|
67
|
-
}
|
|
68
|
-
export function describeBulkSourceCoverage(domain, observation) {
|
|
69
|
-
const source = text(observation.source);
|
|
70
|
-
const state = text(observation.state);
|
|
71
|
-
if (!source || !state)
|
|
72
|
-
return 'unrecorded source';
|
|
73
|
-
return isExpectedUnsupportedBulkSource(domain, observation)
|
|
74
|
-
? `${source}: unsupported (no IANA-published service)`
|
|
75
|
-
: `${source}: ${state}`;
|
|
76
|
-
}
|