@slicedearth/whoisleuth-cli 2.2.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.
Files changed (282) hide show
  1. package/NOTICE +6 -0
  2. package/README.md +20 -15
  3. package/bin/whoisleuth.mjs +8 -1
  4. package/cli/archive-content-digest.mjs +14 -0
  5. package/cli/archive-inspect.mjs +20 -53
  6. package/cli/arguments.mjs +454 -1748
  7. package/cli/artifact-validation/case-response.mjs +66 -35
  8. package/cli/artifact-validation/investigation-capsule.mjs +52 -31
  9. package/cli/artifact-validation/lookup-evidence.mjs +12 -6
  10. package/cli/artifact-validation/signed-review.mjs +112 -72
  11. package/cli/artifact-validation/structure-primitives.mjs +1 -105
  12. package/cli/artifact-verify.mjs +90 -24
  13. package/cli/assurance-command-runner.mjs +39 -4
  14. package/cli/bagit-folder.mjs +57 -0
  15. package/cli/bagit-review.mjs +23 -0
  16. package/cli/bounded-json.mjs +1 -1
  17. package/cli/bulk-checkpoint.mjs +34 -3
  18. package/cli/bulk-command-runner.mjs +2 -0
  19. package/cli/bulk-output.mjs +29 -3
  20. package/cli/case-command.mjs +195 -0
  21. package/cli/case-pack.mjs +66 -111
  22. package/cli/command-argument-grammar.mjs +215 -0
  23. package/cli/command-reference.mjs +342 -285
  24. package/cli/completion.mjs +303 -80
  25. package/cli/config-profile.mjs +7 -5
  26. package/cli/ct-event-intake.mjs +12 -12
  27. package/cli/discover.mjs +4 -3
  28. package/cli/discovery-observation-snapshot.mjs +59 -18
  29. package/cli/discovery-scan-command-runner.mjs +21 -0
  30. package/cli/discovery-scan.mjs +13 -4
  31. package/cli/discovery-workflow.mjs +1 -1
  32. package/cli/domain-control-monitor.mjs +50 -20
  33. package/cli/domain-control-observations.mjs +149 -102
  34. package/cli/errors.mjs +33 -1
  35. package/cli/evidence-command-runner.mjs +16 -5
  36. package/cli/evidence-signing.mjs +2 -2
  37. package/cli/external-observation-mapping.mjs +7 -8
  38. package/cli/formatters/terminal-lookup.mjs +18 -46
  39. package/cli/formatters/terminal-shared.mjs +5 -3
  40. package/cli/formatters/terminal-technology.mjs +114 -0
  41. package/cli/inline-command-families.mjs +2 -0
  42. package/cli/input.mjs +116 -0
  43. package/cli/interactive-launcher.mjs +2 -160
  44. package/cli/interchange-report.mjs +18 -8
  45. package/cli/investigation-artifacts.mjs +176 -0
  46. package/cli/investigation-checkpoint.mjs +10 -0
  47. package/cli/investigation-folder.mjs +114 -0
  48. package/cli/investigation-manifest.mjs +1 -112
  49. package/cli/investigation-package-review.mjs +103 -0
  50. package/cli/investigation-plan.mjs +32 -19
  51. package/cli/investigation-run.mjs +188 -33
  52. package/cli/local-document-checkpoint.mjs +170 -0
  53. package/cli/lookalike-calibration.mjs +7 -2
  54. package/cli/lookup-brief.mjs +4 -2
  55. package/cli/lookup-browser-view.mjs +1 -1
  56. package/cli/lookup-browser.mjs +5 -12
  57. package/cli/lookup-command-runner.mjs +17 -6
  58. package/cli/lookup-diff.mjs +37 -3
  59. package/cli/lookup-plan.mjs +11 -5
  60. package/cli/lookup-reconcile.mjs +10 -8
  61. package/cli/mail-header-review.mjs +467 -0
  62. package/cli/mail-review.mjs +66 -14
  63. package/cli/manual.mjs +1 -0
  64. package/cli/network-command-runner.mjs +2 -0
  65. package/cli/output-file.mjs +31 -5
  66. package/cli/registry-doctor.mjs +3 -1
  67. package/cli/registry-support.mjs +1 -1
  68. package/cli/retained-artifact-diff.mjs +2 -2
  69. package/cli/review-command-runner.mjs +70 -0
  70. package/cli/risk-calibration.mjs +19 -17
  71. package/cli/runner.mjs +62 -7
  72. package/cli/sharing-review.mjs +21 -34
  73. package/cli/signer-trust.mjs +104 -0
  74. package/cli/source-reliability.mjs +74 -83
  75. package/cli/terminal-input.mjs +168 -0
  76. package/cli/workflow-command-runner.mjs +68 -13
  77. package/docs/cli-reference.md +77 -24
  78. package/docs/cli.md +300 -25
  79. package/lib/abort.mjs +4 -1
  80. package/lib/availability.mjs +161 -112
  81. package/lib/bounded-file.mjs +3 -1
  82. package/lib/bounded-html-document.mjs +125 -0
  83. package/lib/bounded-json.mjs +51 -8
  84. package/lib/browser-library-profile.mjs +42 -51
  85. package/lib/browser-library-worker.mjs +112 -0
  86. package/lib/client-behavior-profile.mjs +3 -3
  87. package/lib/ct-search.mjs +11 -4
  88. package/lib/dns-convergence-review.mjs +1 -0
  89. package/lib/dns-delegation-health.mjs +24 -14
  90. package/lib/dns-inheritance-review.mjs +217 -0
  91. package/lib/dns-intelligence.mjs +73 -26
  92. package/lib/dns-mx.mjs +1 -1
  93. package/lib/dnssec-chain-validation.mjs +6 -0
  94. package/lib/dnssec-evidence-validation.mjs +11 -3
  95. package/lib/domain-change-packet.mjs +49 -16
  96. package/lib/domain-change-review.mjs +14 -8
  97. package/lib/domain-control-flight-recorder.mjs +221 -117
  98. package/lib/domain-control-manifest.mjs +129 -69
  99. package/lib/domain-posture-analysis.mjs +20 -7
  100. package/lib/domain-posture-parsers.mjs +201 -8
  101. package/lib/domain-posture.mjs +147 -29
  102. package/lib/evidence-export-privacy.mjs +561 -0
  103. package/lib/evidence-export.mjs +11 -543
  104. package/lib/evidence-report.mjs +5 -3
  105. package/lib/favicon.mjs +117 -41
  106. package/lib/generated/cisa-kev-catalog.mjs +12 -4
  107. package/lib/generated/registrar-standing-catalogue.mjs +7 -7
  108. package/lib/generated/retire-browser-catalog.mjs +2335 -940
  109. package/lib/hostname.mjs +2 -38
  110. package/lib/html-signals.mjs +111 -108
  111. package/lib/http-evidence-bounds.mjs +2 -1
  112. package/lib/http-intelligence.mjs +39 -26
  113. package/lib/local-geoip-evidence.mjs +19 -3
  114. package/lib/lookup-cache.mjs +34 -11
  115. package/lib/lookup-child-profile-contract.mjs +24 -15
  116. package/lib/lookup-diagnostics.mjs +24 -1
  117. package/lib/lookup-progress.mjs +30 -13
  118. package/lib/lookup-response-contract.mjs +1048 -0
  119. package/lib/lookup-response.mjs +8 -10
  120. package/lib/lookup-source-progress.mjs +40 -14
  121. package/lib/lookup.mjs +48 -27
  122. package/lib/nameserver-preflight-review.mjs +1 -0
  123. package/lib/observed-network-context.mjs +43 -27
  124. package/lib/outbound-request-bounds.mjs +4 -1
  125. package/lib/page-fingerprints.mjs +69 -133
  126. package/lib/page-role-profile.mjs +12 -8
  127. package/lib/perceptual-hash.mjs +43 -14
  128. package/lib/rdap-bootstrap.mjs +72 -22
  129. package/lib/rdap-capabilities.mjs +1 -1
  130. package/lib/rdap-client.mjs +15 -7
  131. package/lib/rdap-normalization.mjs +33 -18
  132. package/lib/rdap-search-workbench.mjs +1 -1
  133. package/lib/rdap-transport.mjs +47 -20
  134. package/lib/rdap-types.mjs +2 -1
  135. package/lib/rdap.mjs +18 -3
  136. package/lib/registrable-domain.mjs +21 -1
  137. package/lib/registrar-standing-contract.mjs +1 -0
  138. package/lib/registry-capabilities.mjs +5 -1
  139. package/lib/registry-capability-catalogue.mjs +2 -1
  140. package/lib/registry-dates.mjs +2 -190
  141. package/lib/registry-insights.mjs +85 -34
  142. package/lib/response-policy.mjs +86 -48
  143. package/lib/risk-calibration-summary.mjs +18 -6
  144. package/lib/rpki-evidence.mjs +8 -5
  145. package/lib/safe-fetch.mjs +54 -47
  146. package/lib/security-txt.mjs +42 -7
  147. package/lib/service-binding-dns.mjs +8 -0
  148. package/lib/smtp-transport-review.mjs +27 -15
  149. package/lib/sslbl-certificates.generated.mjs +136 -135
  150. package/lib/static-html-analysis.mjs +178 -151
  151. package/lib/structured-data-identity.mjs +1 -0
  152. package/lib/technology-markup.mjs +100 -0
  153. package/lib/threat-intelligence-manifests.mjs +11 -139
  154. package/lib/threat-intelligence-runtime.mjs +24 -52
  155. package/lib/threat-intelligence-targets.mjs +6 -34
  156. package/lib/threat-intelligence-types.mjs +39 -0
  157. package/lib/typosquat-generator.mjs +19 -15
  158. package/lib/urlhaus-intelligence.mjs +8 -3
  159. package/lib/urlscan-intelligence.mjs +1 -1
  160. package/lib/website-security-posture.mjs +30 -6
  161. package/lib/website-technology.mjs +153 -45
  162. package/lib/whois-authority.mjs +14 -2
  163. package/lib/whois-chain.mjs +9 -2
  164. package/lib/whois-common-formats.mjs +5 -5
  165. package/lib/whois-contracts.mjs +2 -1
  166. package/lib/whois-dialects.mjs +2 -2
  167. package/lib/whois-parser-support.mjs +8 -1
  168. package/lib/whois-transport.mjs +23 -7
  169. package/lib/zone-intent-review.mjs +16 -6
  170. package/package.json +2 -1
  171. package/packages/cases/case-attachment-model.mjs +83 -0
  172. package/packages/cases/case-evidence-model.mjs +156 -62
  173. package/packages/cases/case-export-input.mjs +53 -0
  174. package/packages/cases/case-migration-model.mjs +127 -44
  175. package/packages/cases/case-model.mjs +1 -0
  176. package/packages/cases/case-recheck-model.mjs +91 -0
  177. package/packages/cases/case-record-contracts.mjs +5 -20
  178. package/packages/cases/case-record-core.mjs +12 -22
  179. package/packages/cases/case-record-decisions.mjs +72 -0
  180. package/packages/cases/case-record-model.mjs +1 -0
  181. package/packages/cases/case-record-operations.mjs +95 -31
  182. package/packages/cases/case-record-projection.mjs +221 -0
  183. package/packages/cases/case-report.mjs +70 -54
  184. package/packages/cases/case-response-actions.mjs +654 -0
  185. package/packages/cases/case-response-model.mjs +64 -1115
  186. package/packages/cases/case-response-outcomes.mjs +317 -0
  187. package/packages/cases/case-response-packet-render.mjs +153 -0
  188. package/packages/cases/case-response-packet-types.mjs +1 -0
  189. package/packages/cases/case-response-packet-vocabulary.mjs +48 -0
  190. package/packages/cases/case-response-packet.mjs +74 -572
  191. package/packages/cases/case-response-records.mjs +94 -0
  192. package/packages/cases/case-response-review-inputs.mjs +371 -0
  193. package/packages/cases/case-response-values.mjs +85 -0
  194. package/packages/cases/case-review-package.mjs +45 -0
  195. package/packages/cases/case-selection.mjs +46 -0
  196. package/packages/cases/case-storage-model.mjs +31 -2
  197. package/packages/cases/response-route-freshness.mjs +16 -0
  198. package/packages/comparison/capture-context.mjs +61 -0
  199. package/packages/comparison/comparison-ledger-bulk.mjs +13 -11
  200. package/packages/comparison/page-similarity.mjs +5 -2
  201. package/packages/comparison/relationship-evidence.mjs +85 -8
  202. package/packages/comparison/relationship-provenance.mjs +93 -0
  203. package/packages/contracts/analyst-interchange.mjs +216 -0
  204. package/packages/contracts/analyst-review-state-contract.mjs +11 -0
  205. package/packages/contracts/analyst-review-state.mjs +5 -4
  206. package/packages/contracts/case-portability.mjs +366 -83
  207. package/packages/contracts/case-views-contract.mjs +12 -0
  208. package/packages/contracts/case-views.mjs +26 -0
  209. package/packages/contracts/cli-command-semantics.mjs +94 -0
  210. package/packages/contracts/cli-lookup.mjs +0 -1
  211. package/packages/contracts/domain-control-flight-recorder.mjs +80 -42
  212. package/packages/contracts/domain-control-manifest.mjs +127 -73
  213. package/packages/contracts/domain-control-monitor.mjs +48 -45
  214. package/packages/contracts/domain-control-review.mjs +179 -121
  215. package/packages/contracts/domain-name.mjs +21 -0
  216. package/packages/contracts/external-observation-interchange.mjs +2 -1
  217. package/packages/contracts/extracted-domain-lifecycle.mjs +11 -32
  218. package/packages/contracts/http-url.mjs +2 -0
  219. package/packages/contracts/investigation-package-limits.mjs +24 -0
  220. package/packages/contracts/investigation-portability.mjs +56 -27
  221. package/packages/contracts/investigation-run.mjs +16 -0
  222. package/packages/contracts/ip-address.mjs +34 -0
  223. package/packages/contracts/lookup-evidence.mjs +35 -0
  224. package/packages/contracts/offline-comparison.mjs +1 -2
  225. package/packages/contracts/page-fingerprints.mjs +15 -0
  226. package/packages/contracts/risk-calibration.mjs +15 -11
  227. package/packages/contracts/schema-compatibility.mjs +1 -1
  228. package/packages/contracts/schema-lifecycle.mjs +83 -153
  229. package/packages/contracts/selected-file-limits.mjs +6 -0
  230. package/packages/contracts/vulnerability-identifiers.mjs +5 -0
  231. package/packages/contracts/web-capture.mjs +10 -0
  232. package/packages/contracts/workspace-portability.mjs +183 -211
  233. package/packages/evidence/artifact-integrity.mjs +8 -2
  234. package/packages/evidence/artifact-structure.mjs +105 -0
  235. package/packages/evidence/decision-fact.mjs +4 -1
  236. package/packages/evidence/domain-control-runtime.mjs +201 -33
  237. package/packages/evidence/domain-posture-context.mjs +79 -0
  238. package/packages/evidence/image-regions.mjs +56 -0
  239. package/packages/evidence/latest-observations.mjs +28 -0
  240. package/packages/evidence/lookup-target.mjs +104 -0
  241. package/packages/evidence/observation.mjs +16 -1
  242. package/packages/evidence/passphrase-encryption.mjs +44 -0
  243. package/packages/evidence/record-identity.mjs +72 -0
  244. package/packages/evidence/registry-dates.mjs +190 -0
  245. package/packages/evidence/retained-file.mjs +69 -0
  246. package/packages/evidence/review-age.mjs +14 -0
  247. package/packages/interchange/bagit.mjs +342 -0
  248. package/packages/interchange/bounded-zip-extraction.mjs +278 -0
  249. package/packages/interchange/crc32.mjs +17 -0
  250. package/packages/interchange/external-findings-import.mjs +92 -51
  251. package/packages/interchange/web-capture-import.mjs +382 -0
  252. package/packages/investigation/bulk-mail-exposure.mjs +2 -1
  253. package/packages/investigation/investigation-bagit.mjs +14 -0
  254. package/packages/investigation/investigation-capsule.mjs +33 -5
  255. package/packages/investigation/investigation-manifest.mjs +200 -0
  256. package/packages/investigation/investigation-package-crypto.mjs +98 -0
  257. package/packages/investigation/investigation-package.mjs +200 -0
  258. package/packages/investigation/lookup-asset-graph-inputs.mjs +38 -0
  259. package/packages/investigation/lookup-asset-graph.mjs +193 -165
  260. package/packages/investigation/lookup-claim-readiness.mjs +18 -0
  261. package/packages/investigation/lookup-investigation-brief.mjs +3 -3
  262. package/packages/investigation/lookup-task-guidance.mjs +58 -0
  263. package/packages/monitoring/analyst-review-state.mjs +38 -90
  264. package/packages/workspace/brand-profile-model.mjs +276 -22
  265. package/packages/workspace/bulk-columns.mjs +17 -0
  266. package/packages/workspace/bulk-review-model.mjs +23 -11
  267. package/packages/workspace/bulk-session-model.mjs +100 -24
  268. package/packages/workspace/bulk-sort.mjs +1 -0
  269. package/packages/workspace/case-views.mjs +102 -0
  270. package/packages/workspace/detection-rule-model.mjs +70 -22
  271. package/packages/workspace/hostile-input.mjs +29 -8
  272. package/packages/workspace/investigation-template-model.mjs +19 -4
  273. package/packages/workspace/page-baseline.mjs +18 -15
  274. package/packages/workspace/relationship-observation-model.mjs +41 -23
  275. package/packages/workspace/watchlist-history.mjs +2 -2
  276. package/packages/workspace/watchlist-store.mjs +8 -2
  277. package/packages/workspace/website-snapshot-model.mjs +164 -26
  278. package/packages/workspace/workspace-archive-crypto.mjs +41 -61
  279. package/packages/workspace/workspace-archive.mjs +87 -21
  280. package/third-party-notices.txt +29 -2
  281. package/cli/evidence-command-arguments.mjs +0 -156
  282. package/packages/investigation/bulk-review-export.mjs +0 -63
@@ -1,21 +1,32 @@
1
1
  import { WORKSPACE_ARCHIVE_SECTION_IDS } from "./case-portability.mjs";
2
+ export { PAGE_FINGERPRINT_VERSION, PAGE_FINGERPRINT_PARSERS } from "./page-fingerprints.mjs";
2
3
  import { defineSchemaCompatibility } from "./schema-compatibility.mjs";
3
4
  import { defineSchemaLifecycleFamily, } from "./schema-lifecycle.mjs";
4
5
  export const WORKSPACE_CONTRACT_OWNER = 'packages/contracts/workspace-portability.mts';
5
6
  export const BRAND_PROFILE_SCHEMA = 'whoisleuth.brand-profiles';
6
7
  export const PUBLIC_BRAND_PROFILE_SCHEMA_VERSION = 6;
7
- export const BRAND_PROFILE_SCHEMA_VERSION = 7;
8
- export const BRAND_PROFILE_BROWSER_SUPPORTED_VERSIONS = Object.freeze([PUBLIC_BRAND_PROFILE_SCHEMA_VERSION, BRAND_PROFILE_SCHEMA_VERSION]);
8
+ // Historical readers retain a fixed published identity when the writer moves.
9
+ export const PUBLISHED_V2_2_BRAND_PROFILE_SCHEMA_VERSION = 7;
10
+ export const BRAND_PROFILE_SCHEMA_VERSION = 9;
11
+ export const BRAND_PROFILE_BROWSER_SUPPORTED_VERSIONS = Object.freeze([
12
+ PUBLIC_BRAND_PROFILE_SCHEMA_VERSION,
13
+ PUBLISHED_V2_2_BRAND_PROFILE_SCHEMA_VERSION,
14
+ 8,
15
+ BRAND_PROFILE_SCHEMA_VERSION,
16
+ ]);
9
17
  export const SUPPORTED_BRAND_PROFILE_SCHEMA_VERSIONS = BRAND_PROFILE_BROWSER_SUPPORTED_VERSIONS;
10
18
  export const MAX_PROFILES = 100;
11
19
  export const MAX_PROFILE_VALUES = 200;
12
20
  export const MAX_PROFILE_VALUE_INPUTS = MAX_PROFILE_VALUES * 4;
13
- export const MAX_PROFILE_STORE_BYTES = 1024 * 1024;
14
- export const MAX_PROFILE_IMPORT_BYTES = 2 * 1024 * 1024;
21
+ export const MAX_PROFILE_STORE_BYTES = 4 * 1024 * 1024;
22
+ export const MAX_PROFILE_IMPORT_BYTES = MAX_PROFILE_STORE_BYTES * 8;
15
23
  export const MAX_PROFILE_NAME_LENGTH = 100;
16
24
  export const MAX_PROFILE_TEXT_LENGTH = 200;
17
25
  export const MAX_PROFILE_DOMAIN_LENGTH = 253;
26
+ export const MAX_PROFILE_URL_LENGTH = 2_048;
18
27
  export const MAX_PROFILE_TLD_LENGTH = 63;
28
+ export const MAX_OFFICIAL_CHANNELS = 30;
29
+ export const MAX_RIGHTS_REFERENCES = 30;
19
30
  export const MAX_DKIM_SELECTOR_LENGTH = 253;
20
31
  export const MAX_DKIM_SELECTORS = 10;
21
32
  export const MAX_PROTECTION_ATTESTATIONS = 6;
@@ -85,9 +96,9 @@ export const MAX_CONDITION_VALUE_LENGTH = 200;
85
96
  export const MAX_RULE_RISK_DELTA = 25;
86
97
  export const MAX_CUSTOM_RISK_TOTAL = 50;
87
98
  export const RELATIONSHIP_OBSERVATION_SCHEMA = 'whoisleuth.relationship-observations';
88
- export const RELATIONSHIP_OBSERVATION_SCHEMA_VERSION = 1;
89
- export const RELATIONSHIP_OBSERVATION_BROWSER_SUPPORTED_VERSIONS = Object.freeze([RELATIONSHIP_OBSERVATION_SCHEMA_VERSION]);
90
- export const RELATIONSHIP_OBSERVATION_EXPORT_SUPPORTED_VERSIONS = Object.freeze([RELATIONSHIP_OBSERVATION_SCHEMA_VERSION]);
99
+ export const RELATIONSHIP_OBSERVATION_SCHEMA_VERSION = 2;
100
+ export const RELATIONSHIP_OBSERVATION_BROWSER_SUPPORTED_VERSIONS = Object.freeze([1, RELATIONSHIP_OBSERVATION_SCHEMA_VERSION]);
101
+ export const RELATIONSHIP_OBSERVATION_EXPORT_SUPPORTED_VERSIONS = RELATIONSHIP_OBSERVATION_BROWSER_SUPPORTED_VERSIONS;
91
102
  export const MAX_RELATIONSHIP_OBSERVATIONS = 300;
92
103
  export const MAX_RELATIONSHIP_OBSERVATION_INPUTS = 1_200;
93
104
  export const MAX_RELATIONSHIP_OBSERVATION_DOMAINS = 50;
@@ -95,16 +106,16 @@ export const MAX_RELATIONSHIP_OBSERVATION_LIMITATIONS = 8;
95
106
  export const MAX_RELATIONSHIP_OBSERVATION_VALUE_LENGTH = 20_000;
96
107
  export const MAX_RELATIONSHIP_OBSERVATION_STORE_BYTES = 768 * 1024;
97
108
  export const WEBSITE_SNAPSHOT_SCHEMA = 'whoisleuth.website-profile-snapshots';
98
- export const WEBSITE_SNAPSHOT_SCHEMA_VERSION = 5;
99
- export const WEBSITE_SNAPSHOT_BROWSER_SUPPORTED_VERSIONS = Object.freeze([4, WEBSITE_SNAPSHOT_SCHEMA_VERSION]);
109
+ export const WEBSITE_SNAPSHOT_SCHEMA_VERSION = 6;
110
+ export const WEBSITE_SNAPSHOT_BROWSER_SUPPORTED_VERSIONS = Object.freeze([4, 5, WEBSITE_SNAPSHOT_SCHEMA_VERSION]);
100
111
  export const SUPPORTED_WEBSITE_SNAPSHOT_SCHEMA_VERSIONS = WEBSITE_SNAPSHOT_BROWSER_SUPPORTED_VERSIONS;
101
112
  export const MAX_WEBSITE_SNAPSHOTS = 60;
102
113
  export const MAX_WEBSITE_SNAPSHOTS_PER_DOMAIN = 12;
103
114
  export const MAX_WEBSITE_SNAPSHOT_STORE_BYTES = 512 * 1024;
104
115
  export const MAX_WEBSITE_SNAPSHOT_IMPORT_BYTES = 768 * 1024;
105
116
  export const BULK_SESSION_SCHEMA = 'whoisleuth.bulk-sessions';
106
- export const BULK_SESSION_SCHEMA_VERSION = 4;
107
- export const BULK_SESSION_BROWSER_SUPPORTED_VERSIONS = Object.freeze([BULK_SESSION_SCHEMA_VERSION]);
117
+ export const BULK_SESSION_SCHEMA_VERSION = 5;
118
+ export const BULK_SESSION_BROWSER_SUPPORTED_VERSIONS = Object.freeze([4, BULK_SESSION_SCHEMA_VERSION]);
108
119
  export const SUPPORTED_BULK_SESSION_SCHEMA_VERSIONS = BULK_SESSION_BROWSER_SUPPORTED_VERSIONS;
109
120
  export const MAX_BULK_SESSIONS = 10;
110
121
  export const MAX_BULK_SESSION_ROWS = 2_000;
@@ -121,9 +132,9 @@ export const MAX_INVESTIGATION_TEMPLATES = 20;
121
132
  export const MAX_INVESTIGATION_TEMPLATE_STORE_BYTES = 256 * 1024;
122
133
  export const MAX_INVESTIGATION_TEMPLATE_IMPORT_BYTES = 384 * 1024;
123
134
  export const BULK_REVIEW_SCHEMA = 'whoisleuth.bulk-review';
124
- export const BULK_REVIEW_SCHEMA_VERSION = 1;
125
- export const BULK_REVIEW_BROWSER_SUPPORTED_VERSIONS = Object.freeze([BULK_REVIEW_SCHEMA_VERSION]);
126
- export const BULK_REVIEW_EXPORT_SUPPORTED_VERSIONS = Object.freeze([BULK_REVIEW_SCHEMA_VERSION]);
135
+ export const BULK_REVIEW_SCHEMA_VERSION = 2;
136
+ export const BULK_REVIEW_BROWSER_SUPPORTED_VERSIONS = Object.freeze([1, BULK_REVIEW_SCHEMA_VERSION]);
137
+ export const BULK_REVIEW_EXPORT_SUPPORTED_VERSIONS = Object.freeze([1, BULK_REVIEW_SCHEMA_VERSION]);
127
138
  export const MAX_BULK_REVIEW_PRESETS = 24;
128
139
  export const MAX_BULK_REVIEW_ROWS = 1_900;
129
140
  export const MAX_BULK_REVIEW_STORE_BYTES = 512 * 1024;
@@ -135,7 +146,6 @@ export const MAX_WORKSPACE_INPUT_OBJECT_KEYS = 4_096;
135
146
  export const MAX_WORKSPACE_INPUT_STRING_CODE_UNITS = 8 * 1024 * 1024;
136
147
  export const PAGE_BASELINE_VERSION = 1;
137
148
  export const PAGE_IDENTITY_VERSION = 3;
138
- export const PAGE_FINGERPRINT_VERSION = 1;
139
149
  export const MAX_BASELINE_TITLE_LENGTH = 200;
140
150
  export const MAX_BASELINE_RESOURCE_HOSTS = 30;
141
151
  export const MAX_BASELINE_IDENTIFIERS = 30;
@@ -151,7 +161,7 @@ export const BULK_SESSION_BROWSER_STORE_LIFECYCLE_SCHEMA = 'whoisleuth.browser.b
151
161
  export const INVESTIGATION_TEMPLATE_BROWSER_STORE_LIFECYCLE_SCHEMA = 'whoisleuth.browser.investigation-template-store';
152
162
  export const BULK_REVIEW_BROWSER_STORE_LIFECYCLE_SCHEMA = 'whoisleuth.browser.bulk-review-store';
153
163
  export const WORKSPACE_PORTABILITY_IDENTITY_CONSTANTS = Object.freeze([
154
- 'BRAND_PROFILE_SCHEMA', 'BRAND_PROFILE_SCHEMA_VERSION', 'BRAND_PROFILE_BROWSER_SUPPORTED_VERSIONS', 'SUPPORTED_BRAND_PROFILE_SCHEMA_VERSIONS',
164
+ 'BRAND_PROFILE_SCHEMA', 'PUBLIC_BRAND_PROFILE_SCHEMA_VERSION', 'PUBLISHED_V2_2_BRAND_PROFILE_SCHEMA_VERSION', 'BRAND_PROFILE_SCHEMA_VERSION', 'BRAND_PROFILE_BROWSER_SUPPORTED_VERSIONS', 'SUPPORTED_BRAND_PROFILE_SCHEMA_VERSIONS',
155
165
  'CAMPAIGN_SCHEMA', 'CAMPAIGN_SCHEMA_VERSION', 'CAMPAIGN_BROWSER_SUPPORTED_VERSIONS', 'CAMPAIGN_EXPORT_SUPPORTED_VERSIONS',
156
166
  'WATCHLIST_SCHEMA', 'WATCHLIST_SCHEMA_VERSION', 'WATCHLIST_BROWSER_SUPPORTED_VERSIONS', 'WATCHLIST_EXPORT_SUPPORTED_VERSIONS',
157
167
  'SHORTLIST_SCHEMA', 'SHORTLIST_SCHEMA_VERSION', 'SHORTLIST_BROWSER_SUPPORTED_VERSIONS', 'SUPPORTED_SHORTLIST_SCHEMA_VERSIONS',
@@ -169,7 +179,8 @@ export const WORKSPACE_PORTABILITY_IDENTITY_CONSTANTS = Object.freeze([
169
179
  ]);
170
180
  export const WORKSPACE_PORTABILITY_BOUND_CONSTANTS = Object.freeze([
171
181
  'MAX_PROFILES', 'MAX_PROFILE_VALUES', 'MAX_PROFILE_VALUE_INPUTS', 'MAX_PROFILE_STORE_BYTES', 'MAX_PROFILE_IMPORT_BYTES',
172
- 'MAX_PROFILE_NAME_LENGTH', 'MAX_PROFILE_TEXT_LENGTH', 'MAX_PROFILE_DOMAIN_LENGTH', 'MAX_PROFILE_TLD_LENGTH', 'MAX_DKIM_SELECTOR_LENGTH',
182
+ 'MAX_PROFILE_NAME_LENGTH', 'MAX_PROFILE_TEXT_LENGTH', 'MAX_PROFILE_DOMAIN_LENGTH', 'MAX_PROFILE_URL_LENGTH', 'MAX_PROFILE_TLD_LENGTH',
183
+ 'MAX_OFFICIAL_CHANNELS', 'MAX_RIGHTS_REFERENCES', 'MAX_DKIM_SELECTOR_LENGTH',
173
184
  'MAX_DKIM_SELECTORS', 'MAX_PROTECTION_ATTESTATIONS', 'MAX_DESIRED_POSTURE_BASELINES', 'MAX_DESIRED_POSTURE_RECORDS',
174
185
  'MAX_DESIRED_POSTURE_SUPPRESSIONS', 'MAX_DESIRED_POSTURE_OBSERVATIONS', 'MAX_DESIRED_POSTURE_CHANGE_WINDOWS',
175
186
  'MAX_CAMPAIGNS', 'MAX_CAMPAIGN_DOMAINS', 'MAX_CAMPAIGN_NAME_LENGTH', 'MAX_CAMPAIGN_DESCRIPTION_LENGTH',
@@ -191,7 +202,7 @@ export const WORKSPACE_PORTABILITY_BOUND_CONSTANTS = Object.freeze([
191
202
  'MAX_BULK_REVIEW_PRESETS', 'MAX_BULK_REVIEW_ROWS', 'MAX_BULK_REVIEW_STORE_BYTES', 'MAX_BULK_REVIEW_NAME_LENGTH',
192
203
  'MAX_WORKSPACE_INPUT_GRAPH_DEPTH', 'MAX_WORKSPACE_INPUT_GRAPH_NODES', 'MAX_WORKSPACE_INPUT_ARRAY_LENGTH',
193
204
  'MAX_WORKSPACE_INPUT_OBJECT_KEYS', 'MAX_WORKSPACE_INPUT_STRING_CODE_UNITS',
194
- 'PAGE_BASELINE_VERSION', 'PAGE_IDENTITY_VERSION', 'PAGE_FINGERPRINT_VERSION', 'MAX_BASELINE_TITLE_LENGTH',
205
+ 'PAGE_BASELINE_VERSION', 'PAGE_IDENTITY_VERSION', 'PAGE_FINGERPRINT_VERSION', 'PAGE_FINGERPRINT_PARSERS', 'MAX_BASELINE_TITLE_LENGTH',
195
206
  'MAX_BASELINE_RESOURCE_HOSTS', 'MAX_BASELINE_IDENTIFIERS',
196
207
  ]);
197
208
  export const WORKSPACE_DOMAIN_COMPATIBILITY_FACADES = Object.freeze([
@@ -218,7 +229,7 @@ export const BRAND_PROFILE_BROWSER_COMPATIBILITY = defineSchemaCompatibility({
218
229
  supportedVersions: BRAND_PROFILE_BROWSER_SUPPORTED_VERSIONS, acceptsUnversionedLegacy: false,
219
230
  futureVersionBehavior: 'preserve_without_write', migration: 'normalize_to_current', writeSemantics: 'normalized_rewrite',
220
231
  byteBudget: MAX_PROFILE_STORE_BYTES, owner: WORKSPACE_CONTRACT_OWNER,
221
- note: 'Exact public version 6 profiles migrate directly to version 7, which gives each approved change window a durable opaque identity; other historical stores are outside the compatibility boundary.',
232
+ note: 'Published versions 6, 7 and 8 migrate directly to the current profile envelope. Version 9 preserves earlier page baselines while admitting independently versioned native-tree fingerprints; incompatible fingerprint algorithms are not compared.',
222
233
  });
223
234
  export const CAMPAIGN_BROWSER_COMPATIBILITY = defineSchemaCompatibility({
224
235
  id: 'browser.campaigns', kind: 'browser_store', schema: null, currentVersion: CAMPAIGN_SCHEMA_VERSION,
@@ -258,23 +269,23 @@ export const DETECTION_RULE_BROWSER_COMPATIBILITY = defineSchemaCompatibility({
258
269
  export const RELATIONSHIP_OBSERVATION_BROWSER_COMPATIBILITY = defineSchemaCompatibility({
259
270
  id: 'browser.relationship-observations', kind: 'browser_store', schema: null,
260
271
  currentVersion: RELATIONSHIP_OBSERVATION_SCHEMA_VERSION, supportedVersions: RELATIONSHIP_OBSERVATION_BROWSER_SUPPORTED_VERSIONS,
261
- acceptsUnversionedLegacy: false, futureVersionBehavior: 'preserve_without_write', migration: 'exact_current_only',
272
+ acceptsUnversionedLegacy: false, futureVersionBehavior: 'preserve_without_write', migration: 'normalize_to_current',
262
273
  writeSemantics: 'normalized_rewrite', byteBudget: MAX_RELATIONSHIP_OBSERVATION_STORE_BYTES, owner: WORKSPACE_CONTRACT_OWNER,
263
- note: 'Only explicit analyst selections from bounded Bulk relationship evidence are retained; identities are re-derived from normalised values and members.',
274
+ note: 'Public version 1 observations migrate with unknown contributing-source provenance. Version 2 retains bounded per-domain source states and times; identities remain derived from normalised values and members.',
264
275
  });
265
276
  export const WEBSITE_SNAPSHOT_BROWSER_COMPATIBILITY = defineSchemaCompatibility({
266
277
  id: 'browser.website-snapshots', kind: 'browser_store', schema: null,
267
278
  currentVersion: WEBSITE_SNAPSHOT_SCHEMA_VERSION, supportedVersions: WEBSITE_SNAPSHOT_BROWSER_SUPPORTED_VERSIONS,
268
279
  acceptsUnversionedLegacy: false, futureVersionBehavior: 'preserve_without_write', migration: 'normalize_to_current',
269
280
  writeSemantics: 'normalized_rewrite', byteBudget: MAX_WEBSITE_SNAPSHOT_STORE_BYTES, owner: WORKSPACE_CONTRACT_OWNER,
270
- note: 'Public version 4 records normalise to version 5 with explicit unknown profile provenance; raw lookup payloads, contact data, certificate bytes, and fetched bodies remain excluded.',
281
+ note: 'Public versions 4 and 5 retain their original fingerprint algorithm on migration. Current snapshots record fingerprint provenance so parser changes cannot become target changes; raw lookup payloads, contact data, certificate bytes and fetched bodies remain excluded.',
271
282
  });
272
283
  export const BULK_SESSION_BROWSER_COMPATIBILITY = defineSchemaCompatibility({
273
284
  id: 'browser.bulk-sessions', kind: 'browser_store', schema: null, currentVersion: BULK_SESSION_SCHEMA_VERSION,
274
285
  supportedVersions: BULK_SESSION_BROWSER_SUPPORTED_VERSIONS, acceptsUnversionedLegacy: false,
275
- futureVersionBehavior: 'preserve_without_write', migration: 'exact_current_only', writeSemantics: 'normalized_rewrite',
286
+ futureVersionBehavior: 'preserve_without_write', migration: 'normalize_to_current', writeSemantics: 'normalized_rewrite',
276
287
  byteBudget: MAX_BULK_SESSION_STORE_BYTES, owner: WORKSPACE_CONTRACT_OWNER,
277
- note: 'The unchanged public schema 4 writer is the exact browser baseline with bounded Brand Profile provenance; unsupported local checkpoints are preserved without rewrite.',
288
+ note: 'Public schema 4 sessions migrate with unknown row and per-source observation times and contributing-source provenance. Schema 5 retains those bounded fields without retaining raw lookup payloads; future versions are preserved without rewrite.',
278
289
  });
279
290
  export const INVESTIGATION_TEMPLATE_BROWSER_COMPATIBILITY = defineSchemaCompatibility({
280
291
  id: 'browser.investigation-templates', kind: 'browser_store', schema: null,
@@ -286,7 +297,7 @@ export const INVESTIGATION_TEMPLATE_BROWSER_COMPATIBILITY = defineSchemaCompatib
286
297
  export const BULK_REVIEW_BROWSER_COMPATIBILITY = defineSchemaCompatibility({
287
298
  id: 'browser.bulk-review', kind: 'browser_store', schema: null, currentVersion: BULK_REVIEW_SCHEMA_VERSION,
288
299
  supportedVersions: BULK_REVIEW_BROWSER_SUPPORTED_VERSIONS, acceptsUnversionedLegacy: false,
289
- futureVersionBehavior: 'preserve_without_write', migration: 'exact_current_only', writeSemantics: 'normalized_rewrite',
300
+ futureVersionBehavior: 'preserve_without_write', migration: 'normalize_to_current', writeSemantics: 'normalized_rewrite',
290
301
  byteBudget: MAX_BULK_REVIEW_STORE_BYTES, owner: WORKSPACE_CONTRACT_OWNER,
291
302
  note: 'Bounded saved Bulk filter views and per-domain review states only; scan results, contacts, notes, and case disposition remain separate.',
292
303
  });
@@ -328,23 +339,23 @@ export const DETECTION_RULE_EXPORT_COMPATIBILITY = defineSchemaCompatibility({
328
339
  export const RELATIONSHIP_OBSERVATION_EXPORT_COMPATIBILITY = defineSchemaCompatibility({
329
340
  id: 'export.relationship-observations', kind: 'export', schema: RELATIONSHIP_OBSERVATION_SCHEMA,
330
341
  currentVersion: RELATIONSHIP_OBSERVATION_SCHEMA_VERSION, supportedVersions: RELATIONSHIP_OBSERVATION_EXPORT_SUPPORTED_VERSIONS,
331
- acceptsUnversionedLegacy: false, futureVersionBehavior: 'reject', migration: 'exact_current_only',
342
+ acceptsUnversionedLegacy: false, futureVersionBehavior: 'reject', migration: 'normalize_to_current',
332
343
  writeSemantics: 'non_destructive_merge', byteBudget: MAX_RELATIONSHIP_OBSERVATION_STORE_BYTES, owner: WORKSPACE_CONTRACT_OWNER,
333
- note: 'Workspace-archive section for bounded analyst-selected derived pivots; raw scan and lookup responses are excluded.',
344
+ note: 'Version 1 archive sections migrate to version 2 without inventing source provenance. Bounded contributing-source states and times survive current export; raw lookup responses remain excluded.',
334
345
  });
335
346
  export const WEBSITE_SNAPSHOT_EXPORT_COMPATIBILITY = defineSchemaCompatibility({
336
347
  id: 'export.website-snapshots', kind: 'export', schema: WEBSITE_SNAPSHOT_SCHEMA,
337
348
  currentVersion: WEBSITE_SNAPSHOT_SCHEMA_VERSION, supportedVersions: SUPPORTED_WEBSITE_SNAPSHOT_SCHEMA_VERSIONS,
338
349
  acceptsUnversionedLegacy: false, futureVersionBehavior: 'reject', migration: 'normalize_to_current',
339
350
  writeSemantics: 'non_destructive_merge', byteBudget: MAX_WEBSITE_SNAPSHOT_IMPORT_BYTES, owner: WORKSPACE_CONTRACT_OWNER,
340
- note: 'Public schema 4 archive sections normalise to version 5 with explicit unknown profile provenance and merge non-destructively without retaining fetched bodies.',
351
+ note: 'Public schema 4 and 5 sections migrate non-destructively with preserved fingerprint provenance. Unknown detector versions remain unknown; fetched bodies remain excluded.',
341
352
  });
342
353
  export const BULK_SESSION_EXPORT_COMPATIBILITY = defineSchemaCompatibility({
343
354
  id: 'export.bulk-sessions', kind: 'export', schema: BULK_SESSION_SCHEMA, currentVersion: BULK_SESSION_SCHEMA_VERSION,
344
355
  supportedVersions: SUPPORTED_BULK_SESSION_SCHEMA_VERSIONS, acceptsUnversionedLegacy: false,
345
- futureVersionBehavior: 'reject', migration: 'exact_current_only', writeSemantics: 'non_destructive_merge',
356
+ futureVersionBehavior: 'reject', migration: 'normalize_to_current', writeSemantics: 'non_destructive_merge',
346
357
  byteBudget: MAX_BULK_SESSION_STORE_BYTES, owner: WORKSPACE_CONTRACT_OWNER,
347
- note: 'The unchanged public schema 4 portable sessions retain explicit source states, comparison limitations, and bounded profile-context provenance.',
358
+ note: 'Public schema 4 sessions migrate directly to schema 5 with unknown historical row and per-source observation times and source provenance. Current exports retain source-qualified relationship evidence and bounded profile context.',
348
359
  });
349
360
  export const INVESTIGATION_TEMPLATE_EXPORT_COMPATIBILITY = defineSchemaCompatibility({
350
361
  id: 'export.investigation-templates', kind: 'export', schema: INVESTIGATION_TEMPLATE_SCHEMA,
@@ -356,7 +367,7 @@ export const INVESTIGATION_TEMPLATE_EXPORT_COMPATIBILITY = defineSchemaCompatibi
356
367
  export const BULK_REVIEW_EXPORT_COMPATIBILITY = defineSchemaCompatibility({
357
368
  id: 'export.bulk-review', kind: 'export', schema: BULK_REVIEW_SCHEMA, currentVersion: BULK_REVIEW_SCHEMA_VERSION,
358
369
  supportedVersions: BULK_REVIEW_EXPORT_SUPPORTED_VERSIONS, acceptsUnversionedLegacy: false,
359
- futureVersionBehavior: 'reject', migration: 'exact_current_only', writeSemantics: 'non_destructive_merge',
370
+ futureVersionBehavior: 'reject', migration: 'normalize_to_current', writeSemantics: 'non_destructive_merge',
360
371
  byteBudget: MAX_BULK_REVIEW_STORE_BYTES, owner: WORKSPACE_CONTRACT_OWNER,
361
372
  note: 'Workspace-archive section for saved Bulk views and explicit review states; network collection and case disposition are never changed by import.',
362
373
  });
@@ -643,350 +654,296 @@ const WORKSPACE_LIFECYCLE_FIXTURE_SOURCE = Object.freeze([
643
654
  "path": "test/fixtures/workspace-lifecycle/browser-brand-v6.json",
644
655
  "bytes": 37,
645
656
  "sha256": "624d0b889df19b0f1abfd4026a4dd868003188ed4f4294f6e912589255847df9",
646
- "contentDigestSha256": null,
647
657
  "schema": "whoisleuth.browser.brand-profile-store",
648
- "version": 6,
649
- "role": "historical",
650
- "expectation": "normalises_to_current_output",
651
- "expectedOutputFixtureId": "workspace.browser.brand.v7",
652
- "scope": "repository",
653
- "shapeId": "workspace.browser.brand.shape"
658
+ "version": 6
654
659
  },
655
660
  {
656
661
  "id": "workspace.browser.brand.v7",
657
662
  "path": "test/fixtures/workspace-lifecycle/browser-brand-v7.json",
658
663
  "bytes": 37,
659
664
  "sha256": "c866b2aff3dad05236114e96d3997a1c6af571658b38faa6aeb3c224d125a46b",
660
- "contentDigestSha256": null,
661
665
  "schema": "whoisleuth.browser.brand-profile-store",
662
- "version": 7,
663
- "role": "current",
664
- "expectation": "accepted_exact",
665
- "expectedOutputFixtureId": null,
666
- "scope": "repository",
667
- "shapeId": "workspace.browser.brand.shape"
666
+ "version": 7
667
+ },
668
+ {
669
+ "id": "workspace.browser.brand.v8",
670
+ "path": "test/fixtures/workspace-lifecycle/browser-brand-v8.json",
671
+ "bytes": 37,
672
+ "sha256": "6c2310d9da626cb8acd88a47ccb60fa46051a63b23675cdc4801794ec5cc52e3",
673
+ "schema": "whoisleuth.browser.brand-profile-store",
674
+ "version": 8
668
675
  },
669
676
  {
670
677
  "id": "workspace.browser.bulk.v4",
671
678
  "path": "test/fixtures/workspace-lifecycle/browser-bulk-v4.json",
672
679
  "bytes": 77,
673
680
  "sha256": "605442d181a35a1809edb2ff0b5be17920d52f04276cacaf1786dcc41d3c96ec",
674
- "contentDigestSha256": null,
675
681
  "schema": "whoisleuth.browser.bulk-session-store",
676
- "version": 4,
677
- "role": "current",
678
- "expectation": "accepted_exact",
679
- "expectedOutputFixtureId": null,
680
- "scope": "repository",
681
- "shapeId": "workspace.browser.bulk.shape"
682
+ "version": 4
683
+ },
684
+ {
685
+ "id": "workspace.browser.bulk.v5",
686
+ "path": "test/fixtures/workspace-lifecycle/browser-bulk-v5.json",
687
+ "bytes": 77,
688
+ "sha256": "fca31e3190ff54669c06e14e9785f6d2e504ccbf837eb57caa881b5a8874ebfe",
689
+ "schema": "whoisleuth.browser.bulk-session-store",
690
+ "version": 5
682
691
  },
683
692
  {
684
693
  "id": "workspace.browser.campaign.v1",
685
694
  "path": "test/fixtures/workspace-lifecycle/browser-campaign-v1.json",
686
695
  "bytes": 38,
687
696
  "sha256": "c9f2a2f619c6237230e609a68c895c819472eba644ac4185b3eac3af256c7709",
688
- "contentDigestSha256": null,
689
697
  "schema": "whoisleuth.browser.campaign-store",
690
- "version": 1,
691
- "role": "current",
692
- "expectation": "accepted_exact",
693
- "expectedOutputFixtureId": null,
694
- "scope": "repository",
695
- "shapeId": "workspace.browser.campaign.shape"
698
+ "version": 1
696
699
  },
697
700
  {
698
701
  "id": "workspace.browser.ct.v3",
699
702
  "path": "test/fixtures/workspace-lifecycle/browser-ct-v3.json",
700
703
  "bytes": 36,
701
704
  "sha256": "48e7de97a1b64878f450f12238f7c9f5a41b85e07db3b86b0bc95ca5a5630685",
702
- "contentDigestSha256": null,
703
705
  "schema": "whoisleuth.browser.ct-history-store",
704
- "version": 3,
705
- "role": "current",
706
- "expectation": "accepted_exact",
707
- "expectedOutputFixtureId": null,
708
- "scope": "repository",
709
- "shapeId": "workspace.browser.ct.shape"
706
+ "version": 3
710
707
  },
711
708
  {
712
709
  "id": "workspace.browser.detection.v1",
713
710
  "path": "test/fixtures/workspace-lifecycle/browser-detection-v1.json",
714
711
  "bytes": 34,
715
712
  "sha256": "8772df49af2416bdf2f4881dd6f70452b3a0a8c31cf93f4aa1d7cb40ba0537a4",
716
- "contentDigestSha256": null,
717
713
  "schema": "whoisleuth.browser.detection-rule-store",
718
- "version": 1,
719
- "role": "current",
720
- "expectation": "accepted_exact",
721
- "expectedOutputFixtureId": null,
722
- "scope": "repository",
723
- "shapeId": "workspace.browser.detection.shape"
714
+ "version": 1
724
715
  },
725
716
  {
726
717
  "id": "workspace.browser.relationship.v1",
727
718
  "path": "test/fixtures/workspace-lifecycle/browser-relationship-v1.json",
728
719
  "bytes": 41,
729
720
  "sha256": "d26f28d5d0a654c2b2eab004f2e1a72c05845f433e8b47716f416b7271142668",
730
- "contentDigestSha256": null,
731
721
  "schema": "whoisleuth.browser.relationship-observation-store",
732
- "version": 1,
733
- "role": "current",
734
- "expectation": "accepted_exact",
735
- "expectedOutputFixtureId": null,
736
- "scope": "repository",
737
- "shapeId": "workspace.browser.relationship.shape"
722
+ "version": 1
723
+ },
724
+ {
725
+ "id": "workspace.browser.relationship.v2",
726
+ "path": "test/fixtures/workspace-lifecycle/browser-relationship-v2.json",
727
+ "bytes": 41,
728
+ "sha256": "951dbf0861636b4cf1389e954eb55e81e19dd94a6bf83236ed127f288788f564",
729
+ "schema": "whoisleuth.browser.relationship-observation-store",
730
+ "version": 2
738
731
  },
739
732
  {
740
733
  "id": "workspace.browser.review.v1",
741
734
  "path": "test/fixtures/workspace-lifecycle/browser-review-v1.json",
742
735
  "bytes": 88,
743
736
  "sha256": "9da86165eee183509ad3770b4f7bcf5296040c16ac108024e10b97c54c653609",
744
- "contentDigestSha256": null,
745
737
  "schema": "whoisleuth.browser.bulk-review-store",
746
- "version": 1,
747
- "role": "current",
748
- "expectation": "accepted_exact",
749
- "expectedOutputFixtureId": null,
750
- "scope": "repository",
751
- "shapeId": "workspace.browser.review.shape"
738
+ "version": 1
739
+ },
740
+ {
741
+ "id": "workspace.browser.review.v2",
742
+ "path": "test/fixtures/workspace-lifecycle/browser-review-v2.json",
743
+ "bytes": 88,
744
+ "sha256": "2e22d70101443016b12803835d29ce35cd61a513c70976af1fca1c5de18e222d",
745
+ "schema": "whoisleuth.browser.bulk-review-store",
746
+ "version": 2
752
747
  },
753
748
  {
754
749
  "id": "workspace.browser.shortlist.v3",
755
750
  "path": "test/fixtures/workspace-lifecycle/browser-shortlist-v3.json",
756
751
  "bytes": 72,
757
752
  "sha256": "8ee7f00e2cdd899893549823df9699cf800a392e8a74cbe2907d2a8b2340ebca",
758
- "contentDigestSha256": null,
759
753
  "schema": "whoisleuth.browser.shortlist-store",
760
- "version": 3,
761
- "role": "current",
762
- "expectation": "accepted_exact",
763
- "expectedOutputFixtureId": null,
764
- "scope": "repository",
765
- "shapeId": "workspace.browser.shortlist.shape"
754
+ "version": 3
766
755
  },
767
756
  {
768
757
  "id": "workspace.browser.template.v2",
769
758
  "path": "test/fixtures/workspace-lifecycle/browser-template-v2.json",
770
759
  "bytes": 88,
771
760
  "sha256": "52c1a5a671bf3bd0d052fb7c75d746ed3527898b18ba6532fa9103bd240920aa",
772
- "contentDigestSha256": null,
773
761
  "schema": "whoisleuth.browser.investigation-template-store",
774
- "version": 2,
775
- "role": "current",
776
- "expectation": "accepted_exact",
777
- "expectedOutputFixtureId": null,
778
- "scope": "repository",
779
- "shapeId": "workspace.browser.template.shape"
762
+ "version": 2
780
763
  },
781
764
  {
782
765
  "id": "workspace.browser.watchlist.v2",
783
766
  "path": "test/fixtures/workspace-lifecycle/browser-watchlist-v2.json",
784
767
  "bytes": 76,
785
768
  "sha256": "230e7b6c83659a096e2ccd0da251558088f38b24e438c713bba1012fea788b2c",
786
- "contentDigestSha256": null,
787
769
  "schema": "whoisleuth.browser.watchlist-store",
788
- "version": 2,
789
- "role": "current",
790
- "expectation": "accepted_exact",
791
- "expectedOutputFixtureId": null,
792
- "scope": "repository",
793
- "shapeId": "workspace.browser.watchlist.shape"
770
+ "version": 2
794
771
  },
795
772
  {
796
773
  "id": "workspace.browser.website.v4",
797
774
  "path": "test/fixtures/workspace-lifecycle/browser-website-v4.json",
798
775
  "bytes": 90,
799
776
  "sha256": "b6f1aa0f80fa902c085d831c7bb3334b7c95db49906ffc8dd452173f423b85a4",
800
- "contentDigestSha256": null,
801
777
  "schema": "whoisleuth.browser.website-snapshot-store",
802
- "version": 4,
803
- "role": "historical",
804
- "expectation": "normalises_to_current_output",
805
- "expectedOutputFixtureId": "workspace.browser.website.v5",
806
- "scope": "repository",
807
- "shapeId": "workspace.browser.website.shape"
778
+ "version": 4
808
779
  },
809
780
  {
810
781
  "id": "workspace.browser.website.v5",
811
782
  "path": "test/fixtures/workspace-lifecycle/browser-website-v5.json",
812
783
  "bytes": 90,
813
784
  "sha256": "16550a8c24d5c7bcfe271086071fee7f3392279990cc6fe91e3b9c95f07d0759",
814
- "contentDigestSha256": null,
815
785
  "schema": "whoisleuth.browser.website-snapshot-store",
816
- "version": 5,
817
- "role": "current",
818
- "expectation": "accepted_exact",
819
- "expectedOutputFixtureId": null,
820
- "scope": "repository",
821
- "shapeId": "workspace.browser.website.shape"
786
+ "version": 5
822
787
  },
823
788
  {
824
789
  "id": "workspace.portable.brand.v6",
825
790
  "path": "test/fixtures/workspace-lifecycle/portable-brand-v6.json",
826
791
  "bytes": 122,
827
792
  "sha256": "19e8ab9772bcb07d989249576feab1d71a1a62d401a1a1a7cecf1c65cd7d5f80",
828
- "contentDigestSha256": null,
829
793
  "schema": "whoisleuth.brand-profiles",
830
- "version": 6,
831
- "role": "historical",
832
- "expectation": "normalises_to_current_output",
833
- "expectedOutputFixtureId": "workspace.portable.brand.v7",
834
- "scope": "repository",
835
- "shapeId": "workspace.portable.brand.shape"
794
+ "version": 6
836
795
  },
837
796
  {
838
797
  "id": "workspace.portable.brand.v7",
839
798
  "path": "test/fixtures/workspace-lifecycle/portable-brand-v7.json",
840
799
  "bytes": 122,
841
800
  "sha256": "49db0b0a2d5c00da9dc55fd5b03702d7412550e17329b917567acf6c4ec3ebb9",
842
- "contentDigestSha256": null,
843
801
  "schema": "whoisleuth.brand-profiles",
844
- "version": 7,
845
- "role": "current",
846
- "expectation": "accepted_exact",
847
- "expectedOutputFixtureId": null,
848
- "scope": "repository",
849
- "shapeId": "workspace.portable.brand.shape"
802
+ "version": 7
803
+ },
804
+ {
805
+ "id": "workspace.portable.brand.v8",
806
+ "path": "test/fixtures/workspace-lifecycle/portable-brand-v8.json",
807
+ "bytes": 122,
808
+ "sha256": "4e4af17a0e19cb841ea50fe150caa3de79b978311b51a334ed7f6f3d2e1c6a1c",
809
+ "schema": "whoisleuth.brand-profiles",
810
+ "version": 8
850
811
  },
851
812
  {
852
813
  "id": "workspace.portable.bulk.v4",
853
814
  "path": "test/fixtures/workspace-lifecycle/portable-bulk-v4.json",
854
815
  "bytes": 338,
855
816
  "sha256": "5b3adc2e58c2e47399cf8c0634225dd6cad18d6189d55520603b914a6e719785",
856
- "contentDigestSha256": null,
857
817
  "schema": "whoisleuth.bulk-sessions",
858
- "version": 4,
859
- "role": "current",
860
- "expectation": "accepted_exact",
861
- "expectedOutputFixtureId": null,
862
- "scope": "repository",
863
- "shapeId": "workspace.portable.bulk.shape"
818
+ "version": 4
819
+ },
820
+ {
821
+ "id": "workspace.portable.bulk.v5",
822
+ "path": "test/fixtures/workspace-lifecycle/portable-bulk-v5.json",
823
+ "bytes": 338,
824
+ "sha256": "c58b685ef707b713a08c5ae5986128d2ed1162646f84d926316de194b2b67232",
825
+ "schema": "whoisleuth.bulk-sessions",
826
+ "version": 5
864
827
  },
865
828
  {
866
829
  "id": "workspace.portable.campaign.v1",
867
830
  "path": "test/fixtures/workspace-lifecycle/portable-campaign-v1.json",
868
831
  "bytes": 281,
869
832
  "sha256": "2a0618a9f639cd8375eb46f9bdd5fc9fbb5d8f9ce347f1b93c2d760a91162ccd",
870
- "contentDigestSha256": null,
871
833
  "schema": "whoisleuth.campaigns",
872
- "version": 1,
873
- "role": "current",
874
- "expectation": "accepted_exact",
875
- "expectedOutputFixtureId": null,
876
- "scope": "repository",
877
- "shapeId": "workspace.portable.campaign.shape"
834
+ "version": 1
878
835
  },
879
836
  {
880
837
  "id": "workspace.portable.detection.v1",
881
838
  "path": "test/fixtures/workspace-lifecycle/portable-detection-v1.json",
882
839
  "bytes": 293,
883
840
  "sha256": "0c1306057547a5bf4a5c8c37f5bfaf9195a15a4971adc8e5c9085e8e87946508",
884
- "contentDigestSha256": null,
885
841
  "schema": "whoisleuth.detection-rules",
886
- "version": 1,
887
- "role": "current",
888
- "expectation": "accepted_exact",
889
- "expectedOutputFixtureId": null,
890
- "scope": "repository",
891
- "shapeId": "workspace.portable.detection.shape"
842
+ "version": 1
892
843
  },
893
844
  {
894
845
  "id": "workspace.portable.relationship.v1",
895
846
  "path": "test/fixtures/workspace-lifecycle/portable-relationship-v1.json",
896
847
  "bytes": 363,
897
848
  "sha256": "d16411297ec5be4eb0c87dbc36e1ed34e55ef3699084af2d5f18d76b042d398a",
898
- "contentDigestSha256": null,
899
849
  "schema": "whoisleuth.relationship-observations",
900
- "version": 1,
901
- "role": "current",
902
- "expectation": "accepted_exact",
903
- "expectedOutputFixtureId": null,
904
- "scope": "repository",
905
- "shapeId": "workspace.portable.relationship.shape"
850
+ "version": 1
851
+ },
852
+ {
853
+ "id": "workspace.portable.relationship.v2",
854
+ "path": "test/fixtures/workspace-lifecycle/portable-relationship-v2.json",
855
+ "bytes": 363,
856
+ "sha256": "28322f41c059a060e77673e4aad0d9d5b7b1f6021eefcf5bfed9426588345fdf",
857
+ "schema": "whoisleuth.relationship-observations",
858
+ "version": 2
906
859
  },
907
860
  {
908
861
  "id": "workspace.portable.review.v1",
909
862
  "path": "test/fixtures/workspace-lifecycle/portable-review-v1.json",
910
863
  "bytes": 88,
911
864
  "sha256": "9da86165eee183509ad3770b4f7bcf5296040c16ac108024e10b97c54c653609",
912
- "contentDigestSha256": null,
913
865
  "schema": "whoisleuth.bulk-review",
914
- "version": 1,
915
- "role": "current",
916
- "expectation": "accepted_exact",
917
- "expectedOutputFixtureId": null,
918
- "scope": "repository",
919
- "shapeId": "workspace.portable.review.shape"
866
+ "version": 1
867
+ },
868
+ {
869
+ "id": "workspace.portable.review.v2",
870
+ "path": "test/fixtures/workspace-lifecycle/portable-review-v2.json",
871
+ "bytes": 88,
872
+ "sha256": "2e22d70101443016b12803835d29ce35cd61a513c70976af1fca1c5de18e222d",
873
+ "schema": "whoisleuth.bulk-review",
874
+ "version": 2
920
875
  },
921
876
  {
922
877
  "id": "workspace.portable.shortlist.v3",
923
878
  "path": "test/fixtures/workspace-lifecycle/portable-shortlist-v3.json",
924
879
  "bytes": 116,
925
880
  "sha256": "282569f92d9dc0cd0ef3fdca985ba066e7003477398c2aea64149a036e00e627",
926
- "contentDigestSha256": null,
927
881
  "schema": "whoisleuth.shortlist",
928
- "version": 3,
929
- "role": "current",
930
- "expectation": "accepted_exact",
931
- "expectedOutputFixtureId": null,
932
- "scope": "repository",
933
- "shapeId": "workspace.portable.shortlist.shape"
882
+ "version": 3
934
883
  },
935
884
  {
936
885
  "id": "workspace.portable.template.v2",
937
886
  "path": "test/fixtures/workspace-lifecycle/portable-template-v2.json",
938
887
  "bytes": 471,
939
888
  "sha256": "827aaa27e86cc2e22247f5709fb0df8fa92226ae0691750d7bfdb9a85ca6e6b5",
940
- "contentDigestSha256": null,
941
889
  "schema": "whoisleuth.investigation-templates",
942
- "version": 2,
943
- "role": "current",
944
- "expectation": "accepted_exact",
945
- "expectedOutputFixtureId": null,
946
- "scope": "repository",
947
- "shapeId": "workspace.portable.template.shape"
890
+ "version": 2
948
891
  },
949
892
  {
950
893
  "id": "workspace.portable.watchlist.v2",
951
894
  "path": "test/fixtures/workspace-lifecycle/portable-watchlist-v2.json",
952
895
  "bytes": 120,
953
896
  "sha256": "1176a691294968af187a383839212a75dedbc0089b7a23bbb3d29a079f91e29b",
954
- "contentDigestSha256": null,
955
897
  "schema": "whoisleuth.watchlists",
956
- "version": 2,
957
- "role": "current",
958
- "expectation": "accepted_exact",
959
- "expectedOutputFixtureId": null,
960
- "scope": "repository",
961
- "shapeId": "workspace.portable.watchlist.shape"
898
+ "version": 2
962
899
  },
963
900
  {
964
901
  "id": "workspace.portable.website.v4",
965
902
  "path": "test/fixtures/workspace-lifecycle/portable-website-v4.json",
966
903
  "bytes": 135,
967
904
  "sha256": "08eef9ee031d5226464a0da5624c444c69eb48ca9dc2cb2fd2f1551018c4b11c",
968
- "contentDigestSha256": null,
969
905
  "schema": "whoisleuth.website-profile-snapshots",
970
- "version": 4,
971
- "role": "historical",
972
- "expectation": "normalises_to_current_output",
973
- "expectedOutputFixtureId": "workspace.portable.website.v5",
974
- "scope": "repository",
975
- "shapeId": "workspace.portable.website.shape"
906
+ "version": 4
976
907
  },
977
908
  {
978
909
  "id": "workspace.portable.website.v5",
979
910
  "path": "test/fixtures/workspace-lifecycle/portable-website-v5.json",
980
911
  "bytes": 135,
981
912
  "sha256": "0cab1675a632793141107fac330a130e7d300a7726e9963f8409199902bf0d23",
982
- "contentDigestSha256": null,
983
913
  "schema": "whoisleuth.website-profile-snapshots",
984
- "version": 5,
985
- "role": "current",
986
- "expectation": "accepted_exact",
987
- "expectedOutputFixtureId": null,
988
- "scope": "repository",
989
- "shapeId": "workspace.portable.website.shape"
914
+ "version": 5
915
+ },
916
+ {
917
+ "id": "workspace.browser.brand.v9",
918
+ "path": "test/fixtures/workspace-lifecycle/browser-brand-v9.json",
919
+ "bytes": 37,
920
+ "sha256": "d03cf3bad923ffa3411d218c31a910933c5bb314cc99b9df77f8ae22324c68dc",
921
+ "schema": "whoisleuth.browser.brand-profile-store",
922
+ "version": 9
923
+ },
924
+ {
925
+ "id": "workspace.browser.website.v6",
926
+ "path": "test/fixtures/workspace-lifecycle/browser-website-v6.json",
927
+ "bytes": 90,
928
+ "sha256": "89a4ae5103d848798d77c29b293316284dcf5c2b38417471ad4d55404a8a2110",
929
+ "schema": "whoisleuth.browser.website-snapshot-store",
930
+ "version": 6
931
+ },
932
+ {
933
+ "id": "workspace.portable.brand.v9",
934
+ "path": "test/fixtures/workspace-lifecycle/portable-brand-v9.json",
935
+ "bytes": 122,
936
+ "sha256": "d1ad5a1c56613b31f3bf6195bbda01f4ee9efd528c5fe0cfa5011a6549324853",
937
+ "schema": "whoisleuth.brand-profiles",
938
+ "version": 9
939
+ },
940
+ {
941
+ "id": "workspace.portable.website.v6",
942
+ "path": "test/fixtures/workspace-lifecycle/portable-website-v6.json",
943
+ "bytes": 135,
944
+ "sha256": "a9013e750b7feadbf5dc5d3de5794c27e03d73ba886ad752600df4f0c0cfed35",
945
+ "schema": "whoisleuth.website-profile-snapshots",
946
+ "version": 6
990
947
  }
991
948
  ]);
992
949
  function workspaceByteBudget(descriptor) {
@@ -1001,10 +958,26 @@ function workspaceByteBoundId(descriptor) {
1001
958
  function workspaceShapeId(plane, slug, lifecycle) {
1002
959
  return 'workspace.' + plane + '.' + slug + '.' + lifecycle + '.shape';
1003
960
  }
1004
- const WORKSPACE_LIFECYCLE_FIXTURES = Object.freeze(WORKSPACE_LIFECYCLE_FIXTURE_SOURCE.map((fixture) => ({
1005
- ...fixture,
1006
- shapeId: workspaceShapeId(fixture.id.includes('.browser.') ? 'browser' : 'portable', fixture.id.split('.')[2] ?? '', fixture.role === 'historical' ? 'historical' : 'current'),
1007
- })));
961
+ const WORKSPACE_LIFECYCLE_FIXTURES = Object.freeze(WORKSPACE_LIFECYCLE_FIXTURE_SOURCE.map((fixture) => {
962
+ const definition = WORKSPACE_LIFECYCLE_DEFINITIONS.find((definition) => definition.browserSchema === fixture.schema || definition.portable?.schema === fixture.schema);
963
+ if (!definition)
964
+ throw new TypeError(`Workspace fixture ${fixture.id} has no collection owner.`);
965
+ const browser = definition.browserSchema === fixture.schema;
966
+ const descriptor = browser ? definition.browserDescriptor : definition.portable.descriptor;
967
+ const current = fixture.version === descriptor.currentVersion;
968
+ const target = WORKSPACE_LIFECYCLE_FIXTURE_SOURCE.find((candidate) => candidate.schema === fixture.schema && candidate.version === descriptor.currentVersion);
969
+ if (!target)
970
+ throw new TypeError(`Workspace schema ${fixture.schema} needs an immutable version ${descriptor.currentVersion} fixture.`);
971
+ return {
972
+ ...fixture,
973
+ contentDigestSha256: null,
974
+ role: current ? 'current' : 'historical',
975
+ expectation: current ? 'accepted_exact' : 'normalises_to_current_output',
976
+ expectedOutputFixtureId: current ? null : target.id,
977
+ scope: 'repository',
978
+ shapeId: workspaceShapeId(browser ? 'browser' : 'portable', definition.slug, current ? 'current' : 'historical'),
979
+ };
980
+ }));
1008
981
  function workspaceFixtureIds(schema, version) {
1009
982
  return WORKSPACE_LIFECYCLE_FIXTURES
1010
983
  .filter((fixture) => fixture.schema === schema && fixture.version === version)
@@ -1361,7 +1334,6 @@ export const WORKSPACE_PORTABILITY_LIFECYCLE_FAMILY = defineSchemaLifecycleFamil
1361
1334
  contracts: WORKSPACE_LIFECYCLE_CONTRACTS,
1362
1335
  fixtures: WORKSPACE_LIFECYCLE_FIXTURES,
1363
1336
  metadata: {
1364
- metadataVersion: 4,
1365
1337
  enforcement: 'declarative_only',
1366
1338
  shapes: WORKSPACE_LIFECYCLE_SHAPES,
1367
1339
  boundProfiles: WORKSPACE_LIFECYCLE_BOUNDS,