@slicedearth/whoisleuth-cli 2.4.0 → 2.5.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (300) hide show
  1. package/NOTICE +2 -2
  2. package/cli/argument-values.mjs +55 -0
  3. package/cli/arguments.mjs +24 -470
  4. package/cli/artifact-validation/case-response.mjs +21 -6
  5. package/cli/artifact-validation/lookup-evidence.mjs +3 -3
  6. package/cli/artifact-validation/signed-review.mjs +59 -13
  7. package/cli/artifact-verify.mjs +2 -0
  8. package/cli/assurance-arguments.mjs +56 -0
  9. package/cli/assurance-command-definitions.mjs +179 -0
  10. package/cli/assurance-command-runner.mjs +1 -1
  11. package/cli/bounded-json.mjs +1 -1
  12. package/cli/bulk-checkpoint.mjs +1 -1
  13. package/cli/bulk-command-runner.mjs +2 -2
  14. package/cli/bulk.mjs +2 -3
  15. package/cli/case-command.mjs +20 -3
  16. package/cli/case-pack.mjs +12 -2
  17. package/cli/ci-report.mjs +6 -3
  18. package/cli/collection-arguments.mjs +123 -0
  19. package/cli/collection-command-definitions.mjs +330 -0
  20. package/cli/collection-preflight.mjs +8 -1
  21. package/cli/command-argument-grammar.mjs +8 -210
  22. package/cli/command-definition.mjs +329 -0
  23. package/cli/command-reference.mjs +55 -1388
  24. package/cli/command-seeds.mjs +18 -0
  25. package/cli/compare.mjs +1 -1
  26. package/cli/config-profile.mjs +1 -1
  27. package/cli/ct-event-intake.mjs +4 -4
  28. package/cli/discover.mjs +2 -2
  29. package/cli/discovery-observation-snapshot.mjs +2 -2
  30. package/cli/discovery-scan-command-runner.mjs +3 -3
  31. package/cli/discovery-scan.mjs +1 -1
  32. package/cli/discovery-snapshot.mjs +2 -2
  33. package/cli/discovery-workflow.mjs +4 -4
  34. package/cli/domain-control-monitor.mjs +6 -6
  35. package/cli/domain-control-observations.mjs +2 -1
  36. package/cli/errors.mjs +5 -6
  37. package/cli/evidence-arguments.mjs +46 -0
  38. package/cli/evidence-command-definitions.mjs +69 -0
  39. package/cli/evidence-command-runner.mjs +2 -3
  40. package/cli/evidence-signing.mjs +3 -2
  41. package/cli/external-observation-mapping.mjs +2 -2
  42. package/cli/formatters/terminal-command-formats.mjs +6 -0
  43. package/cli/formatters/terminal-lookup.mjs +6 -0
  44. package/cli/history-arguments.mjs +39 -0
  45. package/cli/history-command-definitions.mjs +88 -0
  46. package/cli/indicator-set-command.mjs +33 -0
  47. package/cli/inline-command-families.mjs +6 -47
  48. package/cli/inline-command-runner.mjs +4 -31
  49. package/cli/input.mjs +2 -2
  50. package/cli/intake-command.mjs +107 -0
  51. package/cli/interchange-report.mjs +4 -2
  52. package/cli/investigation-artifacts.mjs +1 -1
  53. package/cli/investigation-plan.mjs +32 -154
  54. package/cli/investigation-recipes.mjs +177 -0
  55. package/cli/investigation-run.mjs +78 -12
  56. package/cli/local-mmdb-review.mjs +197 -17
  57. package/cli/lookalike-calibration.mjs +1 -1
  58. package/cli/lookup-brief.mjs +2 -2
  59. package/cli/lookup-command-runner.mjs +18 -35
  60. package/cli/lookup-diff.mjs +10 -9
  61. package/cli/lookup-plan.mjs +10 -14
  62. package/cli/lookup-reconcile.mjs +6 -5
  63. package/cli/lookup-timeline.mjs +7 -6
  64. package/cli/mail-header-review.mjs +24 -247
  65. package/cli/mail-review.mjs +9 -7
  66. package/cli/network-arguments.mjs +51 -0
  67. package/cli/network-command-definitions.mjs +130 -0
  68. package/cli/network-command-runner.mjs +43 -31
  69. package/cli/offline-evidence-review.mjs +39 -6
  70. package/cli/posture.mjs +1 -1
  71. package/cli/registry-fixture-scaffold.mjs +1 -1
  72. package/cli/retained-artifact-diff.mjs +8 -7
  73. package/cli/review-arguments.mjs +131 -0
  74. package/cli/review-command-definitions.mjs +273 -0
  75. package/cli/review-command-runner.mjs +68 -158
  76. package/cli/risk-calibration.mjs +8 -8
  77. package/cli/runner.mjs +20 -32
  78. package/cli/saved-lookup.mjs +2 -2
  79. package/cli/selected-input-worker.mjs +63 -0
  80. package/cli/sharing-review.mjs +5 -64
  81. package/cli/source-reliability.mjs +2 -2
  82. package/cli/support-arguments.mjs +49 -0
  83. package/cli/support-command-definitions.mjs +209 -0
  84. package/cli/support-command-runner.mjs +2 -2
  85. package/cli/workflow-arguments.mjs +86 -0
  86. package/cli/workflow-command-definitions.mjs +78 -0
  87. package/cli/workflow-command-runner.mjs +8 -1
  88. package/docs/cli-reference.md +27 -0
  89. package/docs/cli.md +223 -2
  90. package/docs/contextual-reviews.md +155 -0
  91. package/lib/analyst-taxonomy.mjs +2 -40
  92. package/lib/application-version.mjs +1 -1
  93. package/lib/availability.mjs +13 -789
  94. package/lib/bounded-contract-normalizers.mjs +4 -0
  95. package/lib/bounded-json.mjs +2 -330
  96. package/lib/browser-library-profile.mjs +62 -84
  97. package/lib/candidate-provenance-bounds.mjs +2 -0
  98. package/lib/classify.mjs +1 -1
  99. package/lib/ct-query.mjs +2 -27
  100. package/lib/ct-response-bounds.mjs +2 -9
  101. package/lib/ct-search.mjs +83 -47
  102. package/lib/dmarc-discovery.mjs +62 -0
  103. package/lib/dns-convergence-review.mjs +21 -8
  104. package/lib/dns-delegation-health.mjs +12 -8
  105. package/lib/dns-inheritance-review.mjs +53 -63
  106. package/lib/dns-intelligence.mjs +9 -7
  107. package/lib/dns-query-coverage.mjs +40 -0
  108. package/lib/domain-assurance.mjs +1 -1
  109. package/lib/domain-change-packet.mjs +12 -13
  110. package/lib/domain-change-review.mjs +32 -14
  111. package/lib/domain-control-flight-recorder.mjs +103 -27
  112. package/lib/domain-control-manifest.mjs +25 -17
  113. package/lib/domain-posture-analysis.mjs +22 -5
  114. package/lib/domain-posture.mjs +16 -15
  115. package/lib/domain-registration.mjs +392 -0
  116. package/lib/evidence-export-privacy.mjs +22 -15
  117. package/lib/evidence-export.mjs +3 -3
  118. package/lib/evidence-report.mjs +4 -1
  119. package/lib/external-intelligence-risk.mjs +2 -124
  120. package/lib/generated/cisa-kev-catalog.mjs +41 -3
  121. package/lib/generated/registrar-standing-catalogue.mjs +18 -10
  122. package/lib/generated/retire-browser-catalog.mjs +332 -19
  123. package/lib/generated/{unicode-confusables-17.mjs → unicode-confusables.mjs} +33 -33
  124. package/lib/html-signals.mjs +7 -2
  125. package/lib/http-evidence-bounds.mjs +2 -8
  126. package/lib/http-intelligence.mjs +13 -1
  127. package/lib/idn-confusable-policy.mjs +6 -4
  128. package/lib/idn-confusables.mjs +1 -1
  129. package/lib/interchange-fidelity-registry.mjs +10 -0
  130. package/lib/lookup-contract-primitives.mjs +1 -1
  131. package/lib/lookup-diagnostics.mjs +1 -23
  132. package/lib/lookup-network-evidence-bounds.mjs +1 -10
  133. package/lib/lookup-progress.mjs +50 -1
  134. package/lib/lookup-response-contract.mjs +69 -40
  135. package/lib/lookup-response.mjs +5 -15
  136. package/lib/lookup-source-progress.mjs +8 -22
  137. package/lib/lookup.mjs +1 -1
  138. package/lib/opportunity-scoring.mjs +2 -107
  139. package/lib/outbound-identity.mjs +1 -1
  140. package/lib/page-fingerprints.mjs +18 -1
  141. package/lib/perceptual-hash-comparison.mjs +2 -34
  142. package/lib/perceptual-hash.mjs +36 -10
  143. package/lib/portable-generator.mjs +2 -29
  144. package/lib/project-metadata.mjs +2 -8
  145. package/lib/rdap-client.mjs +11 -7
  146. package/lib/registrable-domain.mjs +2 -56
  147. package/lib/registrar-standing-contract.mjs +1 -1
  148. package/lib/registrar-standing.mjs +9 -13
  149. package/lib/registry-capabilities.mjs +2 -2
  150. package/lib/registry-capability-catalogue.mjs +27 -19
  151. package/lib/risk-calibration-summary.mjs +2 -352
  152. package/lib/risk-scoring.mjs +2 -345
  153. package/lib/scoring-evidence-quality.mjs +2 -107
  154. package/lib/semantic-version.mjs +2 -54
  155. package/lib/service-binding-dns.mjs +27 -0
  156. package/lib/smtp-transport-review.mjs +48 -19
  157. package/lib/sslbl-certificates.generated.mjs +141 -139
  158. package/lib/static-html-analysis.mjs +1 -1
  159. package/lib/structured-data-identity.mjs +1 -1
  160. package/lib/threat-intelligence-manifests.mjs +1 -1
  161. package/lib/threat-intelligence-runtime.mjs +1 -1
  162. package/lib/threat-intelligence-targets.mjs +1 -1
  163. package/lib/threat-intelligence-types.mjs +2 -75
  164. package/lib/typosquat-generator.mjs +11 -2
  165. package/lib/website-enrichment.mjs +388 -0
  166. package/lib/whois-authority.mjs +43 -5
  167. package/lib/whois-normalization.mjs +3 -1
  168. package/lib/whois-parser.mjs +5 -3
  169. package/package.json +13 -14
  170. package/packages/analysis/analyst-taxonomy.mjs +40 -0
  171. package/packages/analysis/bounded-json.mjs +330 -0
  172. package/packages/analysis/candidate-provenance-bounds.mjs +4 -0
  173. package/packages/analysis/cli-argument-grammar.mjs +216 -0
  174. package/packages/analysis/ct-query.mjs +27 -0
  175. package/packages/analysis/ct-response-bounds.mjs +9 -0
  176. package/packages/analysis/external-intelligence-risk.mjs +124 -0
  177. package/packages/analysis/http-evidence-bounds.mjs +8 -0
  178. package/packages/analysis/lookup-network-evidence-bounds.mjs +10 -0
  179. package/packages/analysis/opportunity-scoring.mjs +107 -0
  180. package/packages/analysis/perceptual-hash-comparison.mjs +34 -0
  181. package/packages/analysis/portable-generator.mjs +29 -0
  182. package/packages/analysis/project-metadata.mjs +8 -0
  183. package/packages/analysis/registrable-domain.mjs +56 -0
  184. package/packages/analysis/risk-calibration-summary.mjs +352 -0
  185. package/packages/analysis/risk-scoring.mjs +345 -0
  186. package/packages/analysis/scoring-evidence-quality.mjs +107 -0
  187. package/packages/analysis/semantic-version.mjs +54 -0
  188. package/packages/analysis/threat-intelligence-types.mjs +75 -0
  189. package/packages/cases/case-evidence-links.mjs +135 -0
  190. package/packages/cases/case-evidence-model.mjs +78 -12
  191. package/packages/cases/case-evidence-quality.mjs +5 -0
  192. package/packages/cases/case-export-input.mjs +7 -5
  193. package/packages/cases/case-incident-context.mjs +52 -0
  194. package/packages/cases/case-markdown.mjs +11 -0
  195. package/packages/cases/case-migration-model.mjs +40 -24
  196. package/packages/cases/case-recheck-model.mjs +90 -32
  197. package/packages/cases/case-record-contracts.mjs +1 -1
  198. package/packages/cases/case-record-core.mjs +1 -1
  199. package/packages/cases/case-record-operations.mjs +40 -112
  200. package/packages/cases/case-record-projection.mjs +8 -1
  201. package/packages/cases/case-record-version-input.mjs +19 -0
  202. package/packages/cases/case-report.mjs +63 -27
  203. package/packages/cases/case-requested-evidence.mjs +146 -0
  204. package/packages/cases/case-response-actions.mjs +103 -1
  205. package/packages/cases/case-response-model.mjs +9 -1
  206. package/packages/cases/case-response-outcomes.mjs +29 -8
  207. package/packages/cases/case-response-packet-render.mjs +13 -9
  208. package/packages/cases/case-response-packet.mjs +25 -11
  209. package/packages/cases/case-response-records.mjs +0 -1
  210. package/packages/cases/case-response-review-inputs.mjs +14 -3
  211. package/packages/cases/case-storage-model.mjs +1 -1
  212. package/packages/cases/case-workflow-metadata.mjs +225 -0
  213. package/packages/cases/case-workflow-migration.mjs +43 -0
  214. package/packages/cases/http-summary.mjs +6 -1
  215. package/packages/comparison/favicon-similarity.mjs +1 -1
  216. package/packages/comparison/page-similarity.mjs +1 -1
  217. package/packages/comparison/relationship-evidence.mjs +21 -9
  218. package/packages/comparison/relationship-provenance.mjs +7 -1
  219. package/packages/contracts/analyst-interchange.mjs +28 -10
  220. package/packages/contracts/case-portability.mjs +118 -25
  221. package/packages/contracts/cli-command-semantics.mjs +2 -0
  222. package/packages/contracts/cli-grammar.mjs +6 -0
  223. package/packages/contracts/cli-lookup-brief.mjs +2 -0
  224. package/packages/contracts/context-review.mjs +24 -0
  225. package/packages/contracts/document-intake.mjs +11 -0
  226. package/packages/contracts/domain-control-flight-recorder.mjs +30 -14
  227. package/packages/contracts/domain-control-manifest.mjs +1 -1
  228. package/packages/contracts/domain-control-monitor.mjs +13 -2
  229. package/packages/contracts/domain-control-review.mjs +36 -15
  230. package/packages/contracts/har-review.mjs +2 -0
  231. package/packages/contracts/identity-events.mjs +4 -0
  232. package/packages/contracts/investigation-run.mjs +2 -0
  233. package/packages/contracts/ip-address.mjs +10 -0
  234. package/packages/contracts/mail-authentication.mjs +5 -0
  235. package/packages/contracts/message-intake.mjs +31 -0
  236. package/packages/contracts/offline-comparison.mjs +4 -1
  237. package/packages/contracts/passive-mail.mjs +10 -0
  238. package/packages/contracts/web-capture.mjs +2 -2
  239. package/packages/contracts/workspace-portability.mjs +63 -17
  240. package/packages/evidence/collection-quality.mjs +67 -0
  241. package/packages/evidence/dns-query-outcome.mjs +72 -0
  242. package/packages/evidence/email-recipient.mjs +18 -0
  243. package/packages/evidence/lookup-target.mjs +5 -1
  244. package/packages/evidence/observation.mjs +2 -0
  245. package/packages/evidence/png-pixels.mjs +117 -0
  246. package/packages/evidence/public-address-policy.mjs +83 -0
  247. package/packages/interchange/defensive-indicator-export.mjs +264 -0
  248. package/packages/interchange/external-findings-import.mjs +4 -12
  249. package/packages/interchange/managed-indicator-export.mjs +35 -0
  250. package/packages/interchange/managed-indicator-set.mjs +210 -0
  251. package/packages/interchange/misp-indicator-export.mjs +93 -0
  252. package/packages/interchange/sharing-policy.mjs +56 -0
  253. package/packages/interchange/stix-indicator-export.mjs +129 -0
  254. package/packages/interchange/web-capture-import.mjs +22 -15
  255. package/packages/investigation/acquisition-decision-packet.mjs +1 -1
  256. package/packages/investigation/bulk-mail-exposure.mjs +7 -13
  257. package/packages/investigation/capture-coverage.mjs +57 -0
  258. package/packages/investigation/connector-provenance-review.mjs +100 -0
  259. package/packages/investigation/context-review.mjs +35 -0
  260. package/packages/investigation/document-intake.mjs +81 -0
  261. package/packages/investigation/docx-intake.mjs +133 -0
  262. package/packages/investigation/domain-history-review.mjs +100 -0
  263. package/packages/investigation/domain-transition-review.mjs +47 -0
  264. package/packages/investigation/evidence-coverage-ledger.mjs +9 -1
  265. package/packages/investigation/har-intake.mjs +68 -0
  266. package/packages/investigation/identity-event-intake.mjs +131 -0
  267. package/packages/investigation/identity-incident-review.mjs +39 -0
  268. package/packages/investigation/incident-sequence-review.mjs +58 -0
  269. package/packages/investigation/intake-report.mjs +19 -0
  270. package/packages/investigation/investigation-manifest.mjs +2 -2
  271. package/packages/investigation/link-intake.mjs +179 -0
  272. package/packages/investigation/lookup-asset-graph.mjs +1 -1
  273. package/packages/investigation/mail-authentication-review.mjs +222 -0
  274. package/packages/investigation/mail-header-identity.mjs +209 -0
  275. package/packages/investigation/message-intake.mjs +195 -0
  276. package/packages/investigation/page-behaviour.mjs +113 -0
  277. package/packages/investigation/pdf-intake.mjs +155 -0
  278. package/packages/investigation/platform-continuity-review.mjs +60 -0
  279. package/packages/investigation/qr-intake.mjs +56 -0
  280. package/packages/investigation/requested-action-hints.mjs +11 -0
  281. package/packages/investigation/selected-input-review.mjs +17 -0
  282. package/packages/investigation/storefront-review.mjs +70 -0
  283. package/packages/workspace/brand-profile-model.mjs +2 -2
  284. package/packages/workspace/bulk-review-model.mjs +28 -26
  285. package/packages/workspace/bulk-session-model.mjs +23 -13
  286. package/packages/workspace/campaign-model.mjs +1 -1
  287. package/packages/workspace/detection-rule-model.mjs +1 -1
  288. package/packages/workspace/hostile-input.mjs +1 -1
  289. package/packages/workspace/investigation-guide.mjs +2 -2
  290. package/packages/workspace/investigation-template-model.mjs +26 -2
  291. package/packages/workspace/page-baseline.mjs +2 -2
  292. package/packages/workspace/relationship-observation-model.mjs +17 -13
  293. package/packages/workspace/shortlist-model.mjs +14 -5
  294. package/packages/workspace/watchlist-history.mjs +28 -12
  295. package/packages/workspace/watchlist-store.mjs +17 -5
  296. package/packages/workspace/website-snapshot-model.mjs +28 -3
  297. package/packages/workspace/workspace-archive-crypto.mjs +1 -1
  298. package/packages/workspace/workspace-archive.mjs +5 -4
  299. package/third-party-notices.txt +549 -16
  300. package/lib/hostname.mjs +0 -2
@@ -1,10 +1,16 @@
1
1
  // Only the source metadata needed to qualify an observed relationship crosses
2
2
  // this boundary. Collection and retention times are never source-time fallbacks.
3
- import { normalizeDomain } from "../cases/case-model.mjs";
3
+ import { normalizeDomain } from "../evidence/domain-name.mjs";
4
+ import { canonicalPublicIpAddress } from "../evidence/public-address-policy.mjs";
4
5
  import { normalizeExplicitIsoTimestamp, readObservationEnvelope } from "../evidence/observation.mjs";
5
6
  export const RELATIONSHIP_TYPES = Object.freeze([
6
7
  'nameserver_set', 'ip_address', 'certificate', 'tracking_identifier', 'favicon', 'official_asset',
7
8
  ]);
9
+ export const NON_PUBLIC_RELATIONSHIP_LIMITATION = 'This is an equal non-public DNS answer, not evidence of shared public hosting. Private, loopback, reserved and sinkhole-style answers can recur independently.';
10
+ export const FAVICON_RELATIONSHIP_DESCRIPTION = 'Each domain is connected by exact or bounded perceptual favicon matches, directly or through another member. Not every pair necessarily matches.';
11
+ export function isNonPublicAddressRelationship(type, value) {
12
+ return type === 'ip_address' && !canonicalPublicIpAddress(value);
13
+ }
8
14
  const SOURCES = ['dns', 'tls', 'http', 'page_identity', 'favicon', 'registration_or_dns', 'unknown'];
9
15
  const SOURCES_BY_TYPE = {
10
16
  nameserver_set: ['dns', 'registration_or_dns'], ip_address: ['dns'], certificate: ['tls'],
@@ -3,9 +3,9 @@ import { buildExtractedLifecycleFamily } from "./extracted-domain-lifecycle.mjs"
3
3
  import { defineSchemaLifecycleFamily } from "./schema-lifecycle.mjs";
4
4
  export const ANALYST_INTERCHANGE_CONTRACT_OWNER = 'packages/contracts/analyst-interchange.mts';
5
5
  export const INVESTIGATION_CACAO_SPEC_VERSION = 'cacao-2.0';
6
- export const INVESTIGATION_CACAO_PROFILE_VERSION = 2;
7
- export const INVESTIGATION_CACAO_PROFILE_SEMVER = '2.0.0';
8
- export const INVESTIGATION_CACAO_SUPPORTED_PROFILE_VERSIONS = [INVESTIGATION_CACAO_PROFILE_VERSION];
6
+ export const INVESTIGATION_CACAO_PROFILE_VERSION = 3;
7
+ export const INVESTIGATION_CACAO_PROFILE_SEMVER = '3.0.0';
8
+ export const INVESTIGATION_CACAO_SUPPORTED_PROFILE_VERSIONS = [2, INVESTIGATION_CACAO_PROFILE_VERSION];
9
9
  export const MAX_INVESTIGATION_CACAO_IMPORT_BYTES = 384 * 1024;
10
10
  export const BRAND_PROTECTION_OPERATIONS_REPORT_SCHEMA = 'whoisleuth.brand-protection-operations-report';
11
11
  export const BRAND_PROTECTION_OPERATIONS_REPORT_VERSION = 2;
@@ -18,6 +18,10 @@ export const STIX_INDICATOR_LIFECYCLE_SCHEMA = 'whoisleuth.internal.stix-indicat
18
18
  export const STIX_INDICATOR_EXPORT_VERSION = 2;
19
19
  export const MISP_INDICATOR_LIFECYCLE_SCHEMA = 'whoisleuth.internal.misp-indicators';
20
20
  export const MISP_INDICATOR_EXPORT_VERSION = 2;
21
+ export const MANAGED_INDICATOR_SET_SCHEMA = 'whoisleuth.managed-indicator-set';
22
+ export const MANAGED_INDICATOR_SET_VERSION = 1;
23
+ export const MAX_MANAGED_INDICATOR_SET_BYTES = 4 * 1024 * 1024;
24
+ export const MAX_MANAGED_INDICATOR_PLAN_BYTES = 16 * 1024 * 1024;
21
25
  export const DNS_CHANGE_REHEARSAL_VERSION = 2;
22
26
  export const DNS_CHANGE_REHEARSAL_EXPORT_SCHEMA = 'whoisleuth.dns-change-rehearsal';
23
27
  export const MAIL_REPORT_SCHEMA = 'whoisleuth.mail-report-review';
@@ -34,15 +38,16 @@ export const MAX_STATIC_PAGE_PATTERN_PACK_BYTES = 256 * 1024;
34
38
  export const WEB_CAPTURE_SUMMARY_SCHEMA = 'whoisleuth.web-capture-summary';
35
39
  export const WEB_CAPTURE_SUMMARY_VERSION = 1;
36
40
  export const WEB_CAPTURE_MANIFEST_SCHEMA = 'whoisleuth.web-capture-manifest';
37
- export const WEB_CAPTURE_MANIFEST_VERSION = 2;
41
+ export const WEB_CAPTURE_MANIFEST_VERSION = 3;
42
+ export const WEB_CAPTURE_MANIFEST_SUPPORTED_VERSIONS = [2, WEB_CAPTURE_MANIFEST_VERSION];
38
43
  export const MAX_WEB_CAPTURE_MANIFEST_BYTES = 1024 * 1024;
39
44
  const CACAO_COMPATIBILITY = defineSchemaCompatibility({
40
45
  id: 'export.investigation-cacao-profile', kind: 'export', schema: INVESTIGATION_CACAO_SPEC_VERSION,
41
46
  currentVersion: INVESTIGATION_CACAO_PROFILE_VERSION, supportedVersions: INVESTIGATION_CACAO_SUPPORTED_PROFILE_VERSIONS,
42
- acceptsUnversionedLegacy: false, futureVersionBehavior: 'reject', migration: 'exact_current_only',
47
+ acceptsUnversionedLegacy: false, futureVersionBehavior: 'reject', migration: 'normalize_to_current',
43
48
  writeSemantics: 'non_destructive_merge', byteBudget: MAX_INVESTIGATION_CACAO_IMPORT_BYTES,
44
49
  owner: ANALYST_INTERCHANGE_CONTRACT_OWNER,
45
- note: 'Restricted CACAO 2.0 profile with a connected linear sequence of manual analyst steps; version 2 adds fixed response recipe identifiers while executable commands, branches, targets, credentials, and arbitrary operations remain rejected.',
50
+ note: 'Restricted CACAO 2.0 manual profile. Version 3 retains optional lesson-revision provenance; public profile 2 remains readable. Executable commands, branches, targets and credentials remain rejected.',
46
51
  });
47
52
  const OPERATIONS_REPORT_COMPATIBILITY = defineSchemaCompatibility({
48
53
  id: 'export.brand-protection-operations-report', kind: 'export', schema: BRAND_PROTECTION_OPERATIONS_REPORT_SCHEMA,
@@ -72,6 +77,13 @@ const MISP_INDICATORS_COMPATIBILITY = defineSchemaCompatibility({
72
77
  writeSemantics: 'read_only', byteBudget: null, owner: ANALYST_INTERCHANGE_CONTRACT_OWNER,
73
78
  note: 'Unpublished, non-IDS, non-correlating event for reviewed import. Version 2 omits optional seen times when the source observation time is unknown.',
74
79
  });
80
+ export const MANAGED_INDICATOR_SET_COMPATIBILITY = defineSchemaCompatibility({
81
+ id: 'export.managed-indicator-set', kind: 'export', schema: MANAGED_INDICATOR_SET_SCHEMA,
82
+ currentVersion: MANAGED_INDICATOR_SET_VERSION, supportedVersions: [MANAGED_INDICATOR_SET_VERSION],
83
+ acceptsUnversionedLegacy: false, futureVersionBehavior: 'reject', migration: 'exact_current_only',
84
+ writeSemantics: 'read_only', byteBudget: MAX_MANAGED_INDICATOR_SET_BYTES, owner: ANALYST_INTERCHANGE_CONTRACT_OWNER,
85
+ note: 'File-backed managed indicator revisions retain original observations, stable identities, explicit renewals and permanent withdrawals. Known predecessors prevent history replacement; content digests do not authenticate authorship or an unavailable earlier chain.',
86
+ });
75
87
  const WEB_CAPTURE_SUMMARY_COMPATIBILITY = defineSchemaCompatibility({
76
88
  id: 'export.web-capture-summary', kind: 'export', schema: WEB_CAPTURE_SUMMARY_SCHEMA,
77
89
  currentVersion: WEB_CAPTURE_SUMMARY_VERSION, supportedVersions: [WEB_CAPTURE_SUMMARY_VERSION],
@@ -81,11 +93,11 @@ const WEB_CAPTURE_SUMMARY_COMPATIBILITY = defineSchemaCompatibility({
81
93
  });
82
94
  const WEB_CAPTURE_MANIFEST_COMPATIBILITY = defineSchemaCompatibility({
83
95
  id: 'export.web-capture-manifest', kind: 'export', schema: WEB_CAPTURE_MANIFEST_SCHEMA,
84
- currentVersion: WEB_CAPTURE_MANIFEST_VERSION, supportedVersions: [WEB_CAPTURE_MANIFEST_VERSION],
96
+ currentVersion: WEB_CAPTURE_MANIFEST_VERSION, supportedVersions: WEB_CAPTURE_MANIFEST_SUPPORTED_VERSIONS,
85
97
  acceptsUnversionedLegacy: false, futureVersionBehavior: 'reject', migration: 'read_only',
86
98
  writeSemantics: 'read_only', byteBudget: MAX_WEB_CAPTURE_MANIFEST_BYTES,
87
99
  owner: ANALYST_INTERCHANGE_CONTRACT_OWNER,
88
- note: 'Metadata-only capture manifest; the browser and offline artefact comparison accept the v1.47.4 current-writer version 2.',
100
+ note: 'Metadata-only capture manifest. Version 3 adds bounded page and dependency observations; version 2 remains readable with those observations unavailable.',
89
101
  });
90
102
  const DEFENSIVE_MANIFEST_COMPATIBILITY = defineSchemaCompatibility({
91
103
  id: 'export.defensive-indicator-manifest', kind: 'export', schema: DEFENSIVE_INDICATOR_MANIFEST_SCHEMA,
@@ -141,12 +153,17 @@ export const ANALYST_INTERCHANGE_LIFECYCLE_FAMILY = defineSchemaLifecycleFamily(
141
153
  includedCategories: ['reviewed-indicators', 'source-identity', 'provenance', 'completeness', 'omitted-counts', 'limitations'],
142
154
  excludedCategories: ['credentials', 'cookies', 'message-content', 'raw-upstream-responses', 'expanded-contacts'],
143
155
  formats: [
156
+ { descriptor: MANAGED_INDICATOR_SET_COMPATIBILITY, lifecycleSchema: MANAGED_INDICATOR_SET_SCHEMA,
157
+ requiredKeys: ['schema', 'version', 'id', 'producerId', 'revisionId', 'name', 'revision', 'createdAt', 'modifiedAt', 'previous', 'entries', 'integrity'], optionalKeys: [],
158
+ hook: { module: 'packages/interchange/managed-indicator-set.mts', exportName: 'validateManagedIndicatorSet', role: 'structure_validator', runtime: 'shared' },
159
+ fixtures: [{ id: 'managed-indicator-set-v1', path: `${F}managed-indicator-set-v1.json`, bytes: 1_193, sha256: '3a4ffb7256e696d1f1b3bae14743bb8c03ddeb0922186868e935f5a489ba3985', version: MANAGED_INDICATOR_SET_VERSION }] },
144
160
  { descriptor: CACAO_COMPATIBILITY, lifecycleSchema: INVESTIGATION_CACAO_SPEC_VERSION,
145
161
  requiredKeys: ['type', 'spec_version', 'id', 'name', 'created', 'modified', 'workflow_start', 'workflow', 'extension_definitions'],
146
162
  optionalKeys: ['description', 'playbook_types', 'playbook_activities', 'created_by', 'playbook_extensions', 'agent_definitions', 'x_whoisleuth_profile_version'],
147
163
  hook: { module: 'packages/interchange/investigation-playbook-interchange.mts', exportName: 'parseCacaoInvestigationPlaybook', role: 'normaliser', runtime: 'shared' },
148
164
  fixtures: [
149
- { id: 'investigation-cacao-profile-v2', path: `${F}investigation-cacao-profile-v2.json`, bytes: 4_091, sha256: '7e9a3de6e12b83477690c0c58e03e6efbbb8b0c11fd98dc8470bdb6784d42358', version: INVESTIGATION_CACAO_PROFILE_VERSION },
165
+ { id: 'investigation-cacao-profile-v2', path: `${F}investigation-cacao-profile-v2.json`, bytes: 4_091, sha256: '7e9a3de6e12b83477690c0c58e03e6efbbb8b0c11fd98dc8470bdb6784d42358', version: 2 },
166
+ { id: 'investigation-cacao-profile-v3', path: `${F}investigation-cacao-profile-v3.json`, bytes: 4_067, sha256: '608cbb7875c3448ca11f6f8cb2f96ae098e18316532a8f651bbe540ca7f25a2d', version: INVESTIGATION_CACAO_PROFILE_VERSION },
150
167
  ] },
151
168
  { descriptor: OPERATIONS_REPORT_COMPATIBILITY, lifecycleSchema: BRAND_PROTECTION_OPERATIONS_REPORT_SCHEMA,
152
169
  requiredKeys: ['schema', 'version', 'generatedAt', 'window', 'limitations'], optionalKeys: ['sourceState', 'summary', 'counts', 'states', 'actionTypes', 'durations', 'denominators', 'omissions'],
@@ -182,7 +199,8 @@ export const ANALYST_INTERCHANGE_LIFECYCLE_FAMILY = defineSchemaLifecycleFamily(
182
199
  requiredKeys: ['schema', 'schemaVersion', 'source', 'captures'], optionalKeys: [],
183
200
  hook: { module: 'packages/interchange/web-capture-import.mts', exportName: 'parseWebCaptureManifest', role: 'normaliser', runtime: 'shared' },
184
201
  fixtures: [
185
- { id: 'web-capture-manifest-v2', path: `${F}web-capture-manifest-v2.json`, bytes: 494, sha256: 'a2208dc956a832d70e37cd9a5929db81d29148856c485e4fd6ce5b123de5b8e6', version: WEB_CAPTURE_MANIFEST_VERSION },
202
+ { id: 'web-capture-manifest-v2', path: `${F}web-capture-manifest-v2.json`, bytes: 494, sha256: 'a2208dc956a832d70e37cd9a5929db81d29148856c485e4fd6ce5b123de5b8e6', version: 2 },
203
+ { id: 'web-capture-manifest-v3', path: `${F}web-capture-manifest-v3.json`, bytes: 868, sha256: 'bca4e8cb539ac6035dd44586fa7e27774766a14dadbc4fc73cad2b71dace2ac8', version: WEB_CAPTURE_MANIFEST_VERSION },
186
204
  ] },
187
205
  { descriptor: DEFENSIVE_MANIFEST_COMPATIBILITY, lifecycleSchema: DEFENSIVE_INDICATOR_MANIFEST_SCHEMA,
188
206
  requiredKeys: ['schema', 'version', 'generatedAt'], optionalKeys: ['expiresAt', 'reviewRequired', 'explicitSelection', 'includeWildcards', 'entries', 'indicators', 'exclusions', 'limitations'],
@@ -3,14 +3,15 @@ import { defineSchemaCompatibility } from "./schema-compatibility.mjs";
3
3
  import { defineSchemaLifecycleFamily } from "./schema-lifecycle.mjs";
4
4
  export const CASE_CONTRACT_OWNER = 'packages/contracts/case-portability.mts';
5
5
  export const MAX_CASE_OBJECTIVE_LENGTH = 320;
6
- export const LATEST_PUBLIC_APPLICATION_VERSION = '2.3.0';
7
- // Published writer identities are independent of the next writer. Historical
8
- // readers below use fixed release epochs, never a moving "latest" identity.
9
- export const LATEST_PUBLIC_CASE_SCHEMA_VERSION = 15;
10
- export const LATEST_PUBLIC_CASE_REPORT_SCHEMA_VERSION = 11;
11
- export const LATEST_PUBLIC_CASE_RESPONSE_PACKET_VERSION = 9;
12
- export const LATEST_PUBLIC_CASE_RESPONSE_REVIEW_INPUTS_VERSION = 3;
13
- export const LATEST_PUBLIC_WORKSPACE_ARCHIVE_VERSION = 8;
6
+ // These published export names identify a format checkpoint, not the newest
7
+ // application patch. Release verification reads the preceding tag's durable
8
+ // commitments directly. Historical readers use fixed schema epochs below.
9
+ export const LATEST_PUBLIC_APPLICATION_VERSION = '2.4.0';
10
+ export const LATEST_PUBLIC_CASE_SCHEMA_VERSION = 16;
11
+ export const LATEST_PUBLIC_CASE_REPORT_SCHEMA_VERSION = 12;
12
+ export const LATEST_PUBLIC_CASE_RESPONSE_PACKET_VERSION = 10;
13
+ export const LATEST_PUBLIC_CASE_RESPONSE_REVIEW_INPUTS_VERSION = 4;
14
+ export const LATEST_PUBLIC_WORKSPACE_ARCHIVE_VERSION = 9;
14
15
  export const CASE_PORTABILITY_IDENTITY_CONSTANTS = Object.freeze([
15
16
  'LATEST_PUBLIC_APPLICATION_VERSION',
16
17
  'LATEST_PUBLIC_CASE_SCHEMA_VERSION',
@@ -27,6 +28,7 @@ export const CASE_PORTABILITY_IDENTITY_CONSTANTS = Object.freeze([
27
28
  'PUBLISHED_V2_3_CASE_REPORT_SCHEMA_VERSION',
28
29
  'PUBLISHED_V2_3_CASE_RESPONSE_PACKET_VERSION',
29
30
  'PUBLISHED_V2_3_CASE_RESPONSE_REVIEW_INPUTS_VERSION',
31
+ 'PUBLISHED_V2_3_WORKSPACE_ARCHIVE_VERSION',
30
32
  'CASE_BROWSER_STORE_LIFECYCLE_SCHEMA',
31
33
  'CASE_EXPORT_LIFECYCLE_SCHEMA',
32
34
  'CASE_SCHEMA_VERSION',
@@ -158,12 +160,14 @@ export const PUBLISHED_V2_CASE_SCHEMA_VERSION = 13;
158
160
  export const PUBLISHED_V2_2_CASE_SCHEMA_VERSION = 14;
159
161
  export const PUBLISHED_V2_3_CASE_SCHEMA_VERSION = 15;
160
162
  export const INCIDENT_CASE_SCHEMA_VERSION = 16;
161
- export const CASE_SCHEMA_VERSION = INCIDENT_CASE_SCHEMA_VERSION;
163
+ export const EVIDENCE_FOLLOW_UP_CASE_SCHEMA_VERSION = 17;
164
+ export const CASE_SCHEMA_VERSION = EVIDENCE_FOLLOW_UP_CASE_SCHEMA_VERSION;
162
165
  export const CASE_BROWSER_SUPPORTED_VERSIONS = Object.freeze([
163
166
  PUBLIC_CASE_SCHEMA_VERSION,
164
167
  PUBLISHED_V2_CASE_SCHEMA_VERSION,
165
168
  PUBLISHED_V2_2_CASE_SCHEMA_VERSION,
166
169
  PUBLISHED_V2_3_CASE_SCHEMA_VERSION,
170
+ INCIDENT_CASE_SCHEMA_VERSION,
167
171
  CASE_SCHEMA_VERSION,
168
172
  ]);
169
173
  export const CASE_IMPORT_VERSIONS = CASE_BROWSER_SUPPORTED_VERSIONS;
@@ -172,11 +176,12 @@ export const PUBLIC_CASE_REPORT_SCHEMA_VERSION = 8;
172
176
  export const PUBLISHED_V2_CASE_REPORT_SCHEMA_VERSION = 9;
173
177
  export const PUBLISHED_V2_2_CASE_REPORT_SCHEMA_VERSION = 10;
174
178
  export const PUBLISHED_V2_3_CASE_REPORT_SCHEMA_VERSION = 11;
175
- export const CASE_REPORT_SCHEMA_VERSION = 12;
179
+ export const CASE_REPORT_SCHEMA_VERSION = 13;
176
180
  export const CASE_REPORT_OUTPUT_VERSIONS = Object.freeze([
177
181
  PUBLISHED_V2_CASE_REPORT_SCHEMA_VERSION,
178
182
  PUBLISHED_V2_2_CASE_REPORT_SCHEMA_VERSION,
179
183
  PUBLISHED_V2_3_CASE_REPORT_SCHEMA_VERSION,
184
+ LATEST_PUBLIC_CASE_REPORT_SCHEMA_VERSION,
180
185
  CASE_REPORT_SCHEMA_VERSION,
181
186
  ]);
182
187
  export const CASE_RESPONSE_PACKET_SCHEMA = 'whoisleuth.case-response-packet';
@@ -184,12 +189,13 @@ export const PUBLIC_CASE_RESPONSE_PACKET_VERSION = 6;
184
189
  export const PUBLISHED_V2_CASE_RESPONSE_PACKET_VERSION = 7;
185
190
  export const PUBLISHED_V2_2_CASE_RESPONSE_PACKET_VERSION = 8;
186
191
  export const PUBLISHED_V2_3_CASE_RESPONSE_PACKET_VERSION = 9;
187
- export const CASE_RESPONSE_PACKET_VERSION = 10;
192
+ export const CASE_RESPONSE_PACKET_VERSION = 11;
188
193
  export const CASE_RESPONSE_PACKET_OUTPUT_VERSIONS = Object.freeze([
189
194
  PUBLIC_CASE_RESPONSE_PACKET_VERSION,
190
195
  PUBLISHED_V2_CASE_RESPONSE_PACKET_VERSION,
191
196
  PUBLISHED_V2_2_CASE_RESPONSE_PACKET_VERSION,
192
197
  PUBLISHED_V2_3_CASE_RESPONSE_PACKET_VERSION,
198
+ LATEST_PUBLIC_CASE_RESPONSE_PACKET_VERSION,
193
199
  CASE_RESPONSE_PACKET_VERSION,
194
200
  ]);
195
201
  export const SUPPORTED_CASE_RESPONSE_PACKET_VERSIONS = CASE_RESPONSE_PACKET_OUTPUT_VERSIONS;
@@ -197,16 +203,17 @@ export const CASE_RESPONSE_REVIEW_INPUTS_SCHEMA = 'whoisleuth.case-response-revi
197
203
  export const PUBLISHED_V2_CASE_RESPONSE_REVIEW_INPUTS_VERSION = 1;
198
204
  export const PUBLISHED_V2_2_CASE_RESPONSE_REVIEW_INPUTS_VERSION = 2;
199
205
  export const PUBLISHED_V2_3_CASE_RESPONSE_REVIEW_INPUTS_VERSION = 3;
200
- export const CASE_RESPONSE_REVIEW_INPUTS_VERSION = 4;
206
+ export const CASE_RESPONSE_REVIEW_INPUTS_VERSION = 5;
201
207
  export const SUPPORTED_CASE_RESPONSE_REVIEW_INPUTS_VERSIONS = Object.freeze([
202
208
  PUBLISHED_V2_CASE_RESPONSE_REVIEW_INPUTS_VERSION,
203
209
  PUBLISHED_V2_2_CASE_RESPONSE_REVIEW_INPUTS_VERSION,
204
210
  PUBLISHED_V2_3_CASE_RESPONSE_REVIEW_INPUTS_VERSION,
211
+ LATEST_PUBLIC_CASE_RESPONSE_REVIEW_INPUTS_VERSION,
205
212
  CASE_RESPONSE_REVIEW_INPUTS_VERSION,
206
213
  ]);
207
214
  export const CLI_CASE_PACK_SCHEMA = 'whoisleuth.cli.case-pack';
208
215
  export const CLI_CASE_PACK_VERSION = 2;
209
- export const CLI_CASE_PACK_WRITER_FIXTURE_ID = 'cli-case-pack-v2-case-v16-current';
216
+ export const CLI_CASE_PACK_WRITER_FIXTURE_ID = 'cli-case-pack-v2-case-v17-current';
210
217
  export const CLI_CASE_PACK_INPUT_CASE_VERSIONS = Object.freeze(CASE_IMPORT_VERSIONS.filter((version) => version >= PUBLISHED_V2_3_CASE_SCHEMA_VERSION));
211
218
  export const SUPPORTED_CLI_CASE_PACK_VERSIONS = Object.freeze([CLI_CASE_PACK_VERSION]);
212
219
  export const CLI_CASE_PACK_ROOT_KEYS = Object.freeze(['version', 'exportedAt', 'cases', 'packet', 'integrity']);
@@ -225,6 +232,7 @@ export const CLI_CASE_PACK_CASE_REPORT_EPOCHS = Object.freeze([
225
232
  Object.freeze({ caseVersions: Object.freeze([PUBLISHED_V2_CASE_SCHEMA_VERSION]), reportVersions: Object.freeze([PUBLISHED_V2_CASE_REPORT_SCHEMA_VERSION]) }),
226
233
  Object.freeze({ caseVersions: Object.freeze([PUBLISHED_V2_2_CASE_SCHEMA_VERSION]), reportVersions: Object.freeze([PUBLISHED_V2_2_CASE_REPORT_SCHEMA_VERSION]) }),
227
234
  Object.freeze({ caseVersions: Object.freeze([PUBLISHED_V2_3_CASE_SCHEMA_VERSION]), reportVersions: Object.freeze([PUBLISHED_V2_3_CASE_REPORT_SCHEMA_VERSION]) }),
235
+ Object.freeze({ caseVersions: Object.freeze([INCIDENT_CASE_SCHEMA_VERSION]), reportVersions: Object.freeze([LATEST_PUBLIC_CASE_REPORT_SCHEMA_VERSION]) }),
228
236
  Object.freeze({ caseVersions: Object.freeze([CASE_SCHEMA_VERSION]), reportVersions: Object.freeze([CASE_REPORT_SCHEMA_VERSION]) }),
229
237
  ]);
230
238
  export function caseReportVersionMatchesCase(caseVersion, reportVersion) {
@@ -237,12 +245,13 @@ export const WORKSPACE_ARCHIVE_SCHEMA = 'whoisleuth.workspace-archive';
237
245
  export const PUBLIC_WORKSPACE_ARCHIVE_VERSION = 5;
238
246
  export const PUBLISHED_V2_WORKSPACE_ARCHIVE_VERSION = 6;
239
247
  export const PUBLISHED_V2_2_WORKSPACE_ARCHIVE_VERSION = 7;
248
+ export const PUBLISHED_V2_3_WORKSPACE_ARCHIVE_VERSION = 8;
240
249
  export const WORKSPACE_ARCHIVE_VERSION = 9;
241
250
  export const SUPPORTED_WORKSPACE_ARCHIVE_VERSIONS = Object.freeze([
242
251
  PUBLIC_WORKSPACE_ARCHIVE_VERSION,
243
252
  PUBLISHED_V2_WORKSPACE_ARCHIVE_VERSION,
244
253
  PUBLISHED_V2_2_WORKSPACE_ARCHIVE_VERSION,
245
- LATEST_PUBLIC_WORKSPACE_ARCHIVE_VERSION,
254
+ PUBLISHED_V2_3_WORKSPACE_ARCHIVE_VERSION,
246
255
  WORKSPACE_ARCHIVE_VERSION,
247
256
  ]);
248
257
  export function isSupportedWorkspaceArchiveVersion(value) {
@@ -448,7 +457,7 @@ const CASE_LIFECYCLE_FIXTURES = Object.freeze([
448
457
  version: PUBLIC_CASE_SCHEMA_VERSION,
449
458
  role: 'historical',
450
459
  expectation: 'normalises_to_current_output',
451
- expectedOutputFixtureId: 'browser-case-v16',
460
+ expectedOutputFixtureId: 'browser-case-v17',
452
461
  shapeId: `case.browser-store.v${PUBLIC_CASE_SCHEMA_VERSION}`,
453
462
  scope: 'repository',
454
463
  }),
@@ -462,7 +471,7 @@ const CASE_LIFECYCLE_FIXTURES = Object.freeze([
462
471
  version: PUBLISHED_V2_CASE_SCHEMA_VERSION,
463
472
  role: 'historical',
464
473
  expectation: 'normalises_to_current_output',
465
- expectedOutputFixtureId: 'browser-case-v16',
474
+ expectedOutputFixtureId: 'browser-case-v17',
466
475
  shapeId: `case.browser-store.v${PUBLISHED_V2_CASE_SCHEMA_VERSION}`,
467
476
  scope: 'repository',
468
477
  }),
@@ -476,7 +485,7 @@ const CASE_LIFECYCLE_FIXTURES = Object.freeze([
476
485
  version: PUBLISHED_V2_2_CASE_SCHEMA_VERSION,
477
486
  role: 'historical',
478
487
  expectation: 'normalises_to_current_output',
479
- expectedOutputFixtureId: 'browser-case-v16',
488
+ expectedOutputFixtureId: 'browser-case-v17',
480
489
  shapeId: `case.browser-store.v${PUBLISHED_V2_2_CASE_SCHEMA_VERSION}`,
481
490
  scope: 'repository',
482
491
  }),
@@ -490,7 +499,7 @@ const CASE_LIFECYCLE_FIXTURES = Object.freeze([
490
499
  version: PUBLISHED_V2_3_CASE_SCHEMA_VERSION,
491
500
  role: 'historical',
492
501
  expectation: 'normalises_to_current_output',
493
- expectedOutputFixtureId: 'browser-case-v16',
502
+ expectedOutputFixtureId: 'browser-case-v17',
494
503
  shapeId: `case.browser-store.v${PUBLISHED_V2_3_CASE_SCHEMA_VERSION}`,
495
504
  scope: 'repository',
496
505
  }),
@@ -501,6 +510,20 @@ const CASE_LIFECYCLE_FIXTURES = Object.freeze([
501
510
  sha256: 'e0d9dd7d1da29ca31e1b6ccd0c514c44267d84fe9db2eff0ffce23c51fb04ce4',
502
511
  contentDigestSha256: null,
503
512
  schema: CASE_BROWSER_STORE_LIFECYCLE_SCHEMA,
513
+ version: INCIDENT_CASE_SCHEMA_VERSION,
514
+ role: 'historical',
515
+ expectation: 'normalises_to_current_output',
516
+ expectedOutputFixtureId: 'browser-case-v17',
517
+ shapeId: `case.browser-store.v${INCIDENT_CASE_SCHEMA_VERSION}`,
518
+ scope: 'repository',
519
+ }),
520
+ Object.freeze({
521
+ id: 'browser-case-v17',
522
+ path: 'test/fixtures/case-lifecycle/browser-case-v17.json',
523
+ bytes: 3312,
524
+ sha256: '8de3bb904b09ec07994d832738b148321902d91c4351763eebdc0cf908bf5429',
525
+ contentDigestSha256: null,
526
+ schema: CASE_BROWSER_STORE_LIFECYCLE_SCHEMA,
504
527
  version: CASE_SCHEMA_VERSION,
505
528
  role: 'current',
506
529
  expectation: 'accepted_exact',
@@ -518,7 +541,7 @@ const CASE_LIFECYCLE_FIXTURES = Object.freeze([
518
541
  version: PUBLIC_CASE_SCHEMA_VERSION,
519
542
  role: 'historical',
520
543
  expectation: 'normalises_to_current_output',
521
- expectedOutputFixtureId: 'case-export-v16',
544
+ expectedOutputFixtureId: 'case-export-v17',
522
545
  shapeId: `case.export.v${PUBLIC_CASE_SCHEMA_VERSION}`,
523
546
  scope: 'repository',
524
547
  }),
@@ -532,7 +555,7 @@ const CASE_LIFECYCLE_FIXTURES = Object.freeze([
532
555
  version: PUBLISHED_V2_CASE_SCHEMA_VERSION,
533
556
  role: 'historical',
534
557
  expectation: 'normalises_to_current_output',
535
- expectedOutputFixtureId: 'case-export-v16',
558
+ expectedOutputFixtureId: 'case-export-v17',
536
559
  shapeId: `case.export.v${PUBLISHED_V2_CASE_SCHEMA_VERSION}`,
537
560
  scope: 'repository',
538
561
  }),
@@ -546,7 +569,7 @@ const CASE_LIFECYCLE_FIXTURES = Object.freeze([
546
569
  version: PUBLISHED_V2_2_CASE_SCHEMA_VERSION,
547
570
  role: 'historical',
548
571
  expectation: 'normalises_to_current_output',
549
- expectedOutputFixtureId: 'case-export-v16',
572
+ expectedOutputFixtureId: 'case-export-v17',
550
573
  shapeId: `case.export.v${PUBLISHED_V2_2_CASE_SCHEMA_VERSION}`,
551
574
  scope: 'repository',
552
575
  }),
@@ -560,7 +583,7 @@ const CASE_LIFECYCLE_FIXTURES = Object.freeze([
560
583
  version: PUBLISHED_V2_3_CASE_SCHEMA_VERSION,
561
584
  role: 'historical',
562
585
  expectation: 'normalises_to_current_output',
563
- expectedOutputFixtureId: 'case-export-v16',
586
+ expectedOutputFixtureId: 'case-export-v17',
564
587
  shapeId: `case.export.v${PUBLISHED_V2_3_CASE_SCHEMA_VERSION}`,
565
588
  scope: 'repository',
566
589
  }),
@@ -571,6 +594,20 @@ const CASE_LIFECYCLE_FIXTURES = Object.freeze([
571
594
  sha256: 'b0c14fd4d391b1067f101d61c1549c28570999c9cebb58aed21e164d7af56ad8',
572
595
  contentDigestSha256: null,
573
596
  schema: CASE_EXPORT_LIFECYCLE_SCHEMA,
597
+ version: INCIDENT_CASE_SCHEMA_VERSION,
598
+ role: 'historical',
599
+ expectation: 'normalises_to_current_output',
600
+ expectedOutputFixtureId: 'case-export-v17',
601
+ shapeId: `case.export.v${INCIDENT_CASE_SCHEMA_VERSION}`,
602
+ scope: 'repository',
603
+ }),
604
+ Object.freeze({
605
+ id: 'case-export-v17',
606
+ path: 'test/fixtures/case-lifecycle/case-export-v17.json',
607
+ bytes: 3356,
608
+ sha256: 'bbce977c1045fd2b5d576619f07c6cc19388d3d192f671f467ab0ad069f01e8b',
609
+ contentDigestSha256: null,
610
+ schema: CASE_EXPORT_LIFECYCLE_SCHEMA,
574
611
  version: CASE_SCHEMA_VERSION,
575
612
  role: 'current',
576
613
  expectation: 'accepted_exact',
@@ -641,6 +678,20 @@ const CASE_LIFECYCLE_FIXTURES = Object.freeze([
641
678
  sha256: '175b9ab720d95e497eb89a73f437b00c5db9595833b02b033a09bb526a926f36',
642
679
  contentDigestSha256: null,
643
680
  schema: CASE_REPORT_SCHEMA,
681
+ version: LATEST_PUBLIC_CASE_REPORT_SCHEMA_VERSION,
682
+ role: 'historical',
683
+ expectation: 'accepted_exact',
684
+ expectedOutputFixtureId: null,
685
+ shapeId: `case.report.v${LATEST_PUBLIC_CASE_REPORT_SCHEMA_VERSION}`,
686
+ scope: 'repository',
687
+ }),
688
+ Object.freeze({
689
+ id: 'case-report-v13',
690
+ path: 'test/fixtures/case-lifecycle/case-report-v13.json',
691
+ bytes: 4894,
692
+ sha256: '3b0e69f800a2b7a087a0ffcdf81ebbe4237863f42aea7812fd0d0f6b600b8426',
693
+ contentDigestSha256: null,
694
+ schema: CASE_REPORT_SCHEMA,
644
695
  version: CASE_REPORT_SCHEMA_VERSION,
645
696
  role: 'current',
646
697
  expectation: 'accepted_exact',
@@ -697,6 +748,20 @@ const CASE_LIFECYCLE_FIXTURES = Object.freeze([
697
748
  sha256: '991475b878f65d9673e60c776162daf921ec5915e16715080ea0534516cff9c4',
698
749
  contentDigestSha256: 'sha256:97cfe929ace59edb46ee0b84e6135614aba782e170e730c3de3fb24b82413162',
699
750
  schema: CASE_RESPONSE_PACKET_SCHEMA,
751
+ version: LATEST_PUBLIC_CASE_RESPONSE_PACKET_VERSION,
752
+ role: 'historical',
753
+ expectation: 'accepted_exact',
754
+ expectedOutputFixtureId: null,
755
+ shapeId: `case.response-packet.v${LATEST_PUBLIC_CASE_RESPONSE_PACKET_VERSION}`,
756
+ scope: 'repository',
757
+ }),
758
+ Object.freeze({
759
+ id: 'case-response-packet-v11',
760
+ path: 'test/fixtures/case-lifecycle/case-response-packet-v11.json',
761
+ bytes: 12276,
762
+ sha256: '42817b82fbba58930866d9d4f5554732f56c143fd7a64c6e0acab57b66ddfdce',
763
+ contentDigestSha256: "sha256:603558cc7a9112ab759b7dd01f44c53c9408ed6d71db19f562fced2629d5c437",
764
+ schema: CASE_RESPONSE_PACKET_SCHEMA,
700
765
  version: CASE_RESPONSE_PACKET_VERSION,
701
766
  role: 'current',
702
767
  expectation: 'accepted_exact',
@@ -753,6 +818,20 @@ const CASE_LIFECYCLE_FIXTURES = Object.freeze([
753
818
  sha256: 'b054fc8405663992055ec6c07998fd19ab2f12dcc581b87b42f773b1ab4acec7',
754
819
  contentDigestSha256: null,
755
820
  schema: CASE_RESPONSE_REVIEW_INPUTS_SCHEMA,
821
+ version: LATEST_PUBLIC_CASE_RESPONSE_REVIEW_INPUTS_VERSION,
822
+ role: 'historical',
823
+ expectation: 'accepted_exact',
824
+ expectedOutputFixtureId: null,
825
+ shapeId: `case.response-review-inputs.v${LATEST_PUBLIC_CASE_RESPONSE_REVIEW_INPUTS_VERSION}`,
826
+ scope: 'repository',
827
+ }),
828
+ Object.freeze({
829
+ id: 'case-response-review-inputs-v5',
830
+ path: 'test/fixtures/case-lifecycle/case-response-review-inputs-v5.json',
831
+ bytes: 7604,
832
+ sha256: '4a54acbc37ee4384c7a2b5c6168d3fffce6757e259d5314da537c8d423c8bee4',
833
+ contentDigestSha256: null,
834
+ schema: CASE_RESPONSE_REVIEW_INPUTS_SCHEMA,
756
835
  version: CASE_RESPONSE_REVIEW_INPUTS_VERSION,
757
836
  role: 'current',
758
837
  expectation: 'accepted_exact',
@@ -831,8 +910,8 @@ const CASE_LIFECYCLE_FIXTURES = Object.freeze([
831
910
  scope: 'repository',
832
911
  }),
833
912
  Object.freeze({
834
- id: CLI_CASE_PACK_WRITER_FIXTURE_ID,
835
- path: `test/fixtures/case-lifecycle/${CLI_CASE_PACK_WRITER_FIXTURE_ID}.json`,
913
+ id: 'cli-case-pack-v2-case-v16-current',
914
+ path: 'test/fixtures/case-lifecycle/cli-case-pack-v2-case-v16-current.json',
836
915
  bytes: 9602,
837
916
  sha256: '2ac29a7b64b3d8930e92f6ab6b826566061224f281799f9b4b15c0acc98550f0',
838
917
  contentDigestSha256: 'sha256:c6e3cfbe171a85e9318a0a6780d54dc4ad3e662113a75f483fd527c2e36e0143',
@@ -844,6 +923,20 @@ const CASE_LIFECYCLE_FIXTURES = Object.freeze([
844
923
  shapeId: 'case.cli-pack.current',
845
924
  scope: 'repository',
846
925
  }),
926
+ Object.freeze({
927
+ id: CLI_CASE_PACK_WRITER_FIXTURE_ID,
928
+ path: `test/fixtures/case-lifecycle/${CLI_CASE_PACK_WRITER_FIXTURE_ID}.json`,
929
+ bytes: 9874,
930
+ sha256: '3b25440521ddcc1e3ea65a0e56907df4fb725e27b91415c8f1afabab1c4f7e58',
931
+ contentDigestSha256: 'sha256:3cbf9f6dcacdc8efdaa55319e5ce7068a5cf2ea116c80e9ff8ccf6d2dc8741c6',
932
+ schema: CLI_CASE_PACK_SCHEMA,
933
+ version: CLI_CASE_PACK_VERSION,
934
+ role: 'current',
935
+ expectation: 'accepted_exact',
936
+ expectedOutputFixtureId: null,
937
+ shapeId: 'case.cli-pack.current',
938
+ scope: 'repository',
939
+ }),
847
940
  Object.freeze({
848
941
  id: 'workspace-archive-v5-public',
849
942
  path: 'test/fixtures/case-lifecycle/workspace-archive-v5-public.json',
@@ -893,7 +986,7 @@ const CASE_LIFECYCLE_FIXTURES = Object.freeze([
893
986
  sha256: '747062e2dbbcf96724b19718d36bf0dc50b08e96d8adb670115d2315032b6c05',
894
987
  contentDigestSha256: null,
895
988
  schema: WORKSPACE_ARCHIVE_SCHEMA,
896
- version: LATEST_PUBLIC_WORKSPACE_ARCHIVE_VERSION,
989
+ version: PUBLISHED_V2_3_WORKSPACE_ARCHIVE_VERSION,
897
990
  role: 'historical',
898
991
  expectation: 'normalises_to_current_output',
899
992
  expectedOutputFixtureId: `workspace-archive-v${WORKSPACE_ARCHIVE_VERSION}-empty-current`,
@@ -76,9 +76,11 @@ export const CLI_COMMAND_SEMANTICS = Object.freeze({
76
76
  'page-compare': semantic('investigate', CAPABILITY_IDS.OFFLINE_REVIEW),
77
77
  'mail-review': semantic('investigate', CAPABILITY_IDS.OFFLINE_REVIEW),
78
78
  'mail-headers': semantic('investigate', CAPABILITY_IDS.OFFLINE_REVIEW),
79
+ intake: semantic('investigate', CAPABILITY_IDS.OFFLINE_REVIEW),
79
80
  'review-evidence': semantic('investigate', CAPABILITY_IDS.PORTABLE_EVIDENCE),
80
81
  brief: semantic('investigate', CAPABILITY_IDS.OFFLINE_REVIEW),
81
82
  case: semantic('respond', CAPABILITY_IDS.ANALYST_CASES),
83
+ 'indicator-set': semantic('respond', CAPABILITY_IDS.PORTABLE_EVIDENCE),
82
84
  'case-pack': semantic('respond', CAPABILITY_IDS.PORTABLE_EVIDENCE),
83
85
  'domain-control': semantic('assure', CAPABILITY_IDS.PORTABLE_EVIDENCE),
84
86
  'monitor-once': semantic('assure', CAPABILITY_IDS.LOOKUP),
@@ -0,0 +1,6 @@
1
+ import { MAX_INVESTIGATION_MANIFEST_ARTIFACTS } from "./investigation-package-limits.mjs";
2
+ export const MAX_CLI_ARGUMENTS = MAX_INVESTIGATION_MANIFEST_ARTIFACTS + 32;
3
+ export const MAX_CLI_ARGUMENT_LENGTH = 1024;
4
+ export function hasUnsafeCliText(value) {
5
+ return /[\x00-\x1f\x7f-\x9f]|\p{Default_Ignorable_Code_Point}/u.test(value);
6
+ }
@@ -0,0 +1,2 @@
1
+ export const CLI_LOOKUP_BRIEF_SCHEMA = 'whoisleuth.cli.lookup-brief';
2
+ export const CLI_LOOKUP_BRIEF_VERSION = 3;
@@ -0,0 +1,24 @@
1
+ /** Common presentation of bounded, analyst-selected contextual evidence. */
2
+ export const CONTEXT_REVIEW_SCHEMA = 'whoisleuth.context-review';
3
+ export const CONTEXT_REVIEW_VERSION = 1;
4
+ export const DOMAIN_HISTORY_INPUT_SCHEMA = 'whoisleuth.domain-history.input';
5
+ export const DOMAIN_HISTORY_INPUT_VERSION = 1;
6
+ export const PLATFORM_CONTINUITY_INPUT_SCHEMA = 'whoisleuth.platform-continuity.input';
7
+ export const PLATFORM_CONTINUITY_INPUT_VERSION = 1;
8
+ export const STOREFRONT_INPUT_SCHEMA = 'whoisleuth.storefront-review.input';
9
+ export const STOREFRONT_INPUT_VERSION = 1;
10
+ export const CONNECTOR_INPUT_SCHEMA = 'whoisleuth.connector-review.input';
11
+ export const CONNECTOR_INPUT_VERSION = 1;
12
+ export const INCIDENT_SEQUENCE_INPUT_SCHEMA = 'whoisleuth.incident-sequence.input';
13
+ export const INCIDENT_SEQUENCE_INPUT_VERSION = 1;
14
+ export const CONTEXT_INPUT_CONTRACTS = [
15
+ { schema: DOMAIN_HISTORY_INPUT_SCHEMA, version: DOMAIN_HISTORY_INPUT_VERSION },
16
+ { schema: PLATFORM_CONTINUITY_INPUT_SCHEMA, version: PLATFORM_CONTINUITY_INPUT_VERSION },
17
+ { schema: STOREFRONT_INPUT_SCHEMA, version: STOREFRONT_INPUT_VERSION },
18
+ { schema: CONNECTOR_INPUT_SCHEMA, version: CONNECTOR_INPUT_VERSION },
19
+ { schema: INCIDENT_SEQUENCE_INPUT_SCHEMA, version: INCIDENT_SEQUENCE_INPUT_VERSION },
20
+ ];
21
+ export const CONTEXT_INPUT_SCHEMAS = CONTEXT_INPUT_CONTRACTS.map(input => input.schema);
22
+ export const MAX_CONTEXT_INPUT_BYTES = 16 * 1024 * 1024;
23
+ export const MAX_CONTEXT_RECORDS = 200;
24
+ export const CONTEXT_REVIEW_KINDS = ['domain_history', 'platform_continuity', 'storefront', 'connector', 'incident_sequence'];
@@ -0,0 +1,11 @@
1
+ /** Input admission, decoded XML, page work and pixels are independently bounded. */
2
+ export const MAX_DOCUMENT_PARTS = 512;
3
+ export const MAX_DOCUMENT_PAGES = 256;
4
+ export const MAX_DOCUMENT_DECODED_BYTES = 32 * 1024 * 1024;
5
+ export const MAX_DOCUMENT_TEXT_BYTES = 8 * 1024 * 1024;
6
+ export const MAX_DOCUMENT_XML_NODES = 200_000;
7
+ export const MAX_DOCUMENT_PAGE_OPERATIONS = 200_000;
8
+ export const MAX_DOCUMENT_XML_DEPTH = 64;
9
+ export const MAX_DOCUMENT_IMAGE_PIXELS = 16 * 1024 * 1024;
10
+ export const MAX_DOCUMENT_TOTAL_IMAGE_PIXELS = 64 * 1024 * 1024;
11
+ export const DOCUMENT_REVIEW_DEADLINE_MS = 60_000;
@@ -5,8 +5,8 @@ import { MAX_DOMAIN_CONTROL_MONITOR_CONCURRENCY, MAX_DOMAIN_CONTROL_MONITOR_DOMA
5
5
  export const DOMAIN_CONTROL_FLIGHT_RECORDER_INPUT_SCHEMA = 'whoisleuth.domain-control-flight-recorder.input';
6
6
  export const DOMAIN_CONTROL_FLIGHT_RECORDER_SCHEMA = 'whoisleuth.domain-control-flight-recorder';
7
7
  export const PUBLIC_DOMAIN_CONTROL_FLIGHT_RECORDER_VERSION = 1;
8
- export const DOMAIN_CONTROL_FLIGHT_RECORDER_VERSION = 2;
9
- export const SUPPORTED_DOMAIN_CONTROL_FLIGHT_RECORDER_VERSIONS = [PUBLIC_DOMAIN_CONTROL_FLIGHT_RECORDER_VERSION, DOMAIN_CONTROL_FLIGHT_RECORDER_VERSION];
8
+ export const DOMAIN_CONTROL_FLIGHT_RECORDER_VERSION = 3;
9
+ export const SUPPORTED_DOMAIN_CONTROL_FLIGHT_RECORDER_VERSIONS = [PUBLIC_DOMAIN_CONTROL_FLIGHT_RECORDER_VERSION, 2, DOMAIN_CONTROL_FLIGHT_RECORDER_VERSION];
10
10
  export const MIN_FLIGHT_RECORDER_OBSERVATIONS = 1;
11
11
  export const MAX_FLIGHT_RECORDER_OBSERVATIONS = 200;
12
12
  export const MAX_FLIGHT_RECORDER_WINDOWS = 40;
@@ -95,11 +95,14 @@ export const PUBLIC_DOMAIN_CONTROL_FLIGHT_RECORDER_EVENT_KEYS = Object.freeze([
95
95
  'approvedWindow',
96
96
  'explanation',
97
97
  ]);
98
- export const DOMAIN_CONTROL_FLIGHT_RECORDER_EVENT_KEYS = Object.freeze([...PUBLIC_DOMAIN_CONTROL_FLIGHT_RECORDER_EVENT_KEYS, 'capturedAt']);
99
- export const DOMAIN_CONTROL_FLIGHT_RECORDER_APPROVED_WINDOW_KEYS = Object.freeze([
98
+ export const SOURCE_TIMED_FLIGHT_RECORDER_EVENT_KEYS = Object.freeze([...PUBLIC_DOMAIN_CONTROL_FLIGHT_RECORDER_EVENT_KEYS, 'capturedAt']);
99
+ export const DOMAIN_CONTROL_FLIGHT_RECORDER_EVENT_KEYS = Object.freeze([...SOURCE_TIMED_FLIGHT_RECORDER_EVENT_KEYS, 'collectionDepth', 'changeInterval']);
100
+ export const FLIGHT_RECORDER_CHANGE_INTERVAL_KEYS = Object.freeze(['after', 'by', 'incompleteObservations']);
101
+ export const HISTORICAL_FLIGHT_RECORDER_APPROVED_WINDOW_KEYS = Object.freeze([
100
102
  'id',
101
103
  'reason',
102
104
  ]);
105
+ export const DOMAIN_CONTROL_FLIGHT_RECORDER_APPROVED_WINDOW_KEYS = Object.freeze([...HISTORICAL_FLIGHT_RECORDER_APPROVED_WINDOW_KEYS, 'startsAt', 'endsAt']);
103
106
  export const PUBLIC_DOMAIN_CONTROL_FLIGHT_RECORDER_SUMMARY_KEYS = Object.freeze([
104
107
  'firstObservations',
105
108
  'observedChanges',
@@ -115,10 +118,14 @@ export const PUBLIC_DOMAIN_CONTROL_FLIGHT_RECORDER_LIMITATIONS = Object.freeze([
115
118
  'Approved windows label expected timing but do not delete evidence or establish that a change was authorised, successful, safe, or complete.',
116
119
  'Observed changes can reflect publication lag or different collection conditions and require analyst review.',
117
120
  ]);
118
- export const DOMAIN_CONTROL_FLIGHT_RECORDER_LIMITATIONS = Object.freeze([
121
+ export const SOURCE_TIMED_FLIGHT_RECORDER_LIMITATIONS = Object.freeze([
119
122
  ...PUBLIC_DOMAIN_CONTROL_FLIGHT_RECORDER_LIMITATIONS,
120
123
  'Capture time and source observation time remain separate. Unknown, conflicting or non-increasing source times cannot establish an observed change.',
121
124
  ]);
125
+ export const DOMAIN_CONTROL_FLIGHT_RECORDER_LIMITATIONS = Object.freeze([
126
+ ...SOURCE_TIMED_FLIGHT_RECORDER_LIMITATIONS,
127
+ 'A change interval is bounded by comparable complete source observations, not an exact change time. Incomplete observations inside it remain explicit. An approved window must contain the whole interval.',
128
+ ]);
122
129
  export const DOMAIN_CONTROL_FLIGHT_RECORDER_INPUT_COMPATIBILITY = defineSchemaCompatibility({
123
130
  id: 'cli.domain-control-flight-recorder-input',
124
131
  kind: 'cli_document',
@@ -224,14 +231,22 @@ export const DOMAIN_CONTROL_FLIGHT_RECORDER_SCHEMA_LIFECYCLE = defineSchemaLifec
224
231
  scope: 'repository',
225
232
  },
226
233
  ].flatMap((fixture) => {
227
- const version = DOMAIN_CONTROL_FLIGHT_RECORDER_VERSION;
228
- const id = fixture.id.replace(/v\d+$/u, `v${version}`);
229
- const content = fixture.role === 'input'
230
- ? { bytes: 2108, sha256: '6998ea864355dd0802b3b6f2c4905f8c84136ad0d4ac9cad1a0fd63dca523b60' }
231
- : { bytes: 4649, sha256: '8f3168d101291166f34cede351bc176a934e7997920cd8c22ec00d642ec000df' };
232
234
  return [
233
235
  { ...fixture, role: fixture.role === 'input' ? 'input' : 'historical', expectation: 'accepted_exact', expectedOutputFixtureId: null },
234
- { ...fixture, ...content, id, path: `test/fixtures/${id}.json`, version, expectedOutputFixtureId: fixture.expectedOutputFixtureId?.replace(/v\d+$/u, `v${version}`) ?? null },
236
+ ...[2, DOMAIN_CONTROL_FLIGHT_RECORDER_VERSION].map(version => {
237
+ const id = fixture.id.replace(/v\d+$/u, `v${version}`);
238
+ const content = version === 2
239
+ ? fixture.role === 'input'
240
+ ? { bytes: 2108, sha256: '6998ea864355dd0802b3b6f2c4905f8c84136ad0d4ac9cad1a0fd63dca523b60' }
241
+ : { bytes: 4649, sha256: '8f3168d101291166f34cede351bc176a934e7997920cd8c22ec00d642ec000df' }
242
+ : fixture.role === 'input'
243
+ ? { bytes: 2108, sha256: '3d9b91dfd34754482047cde64d6343676ec4747267677fcbcc989a65fbd9a6c6' }
244
+ : { bytes: 5597, sha256: 'ab6a82cfecfb8238a72fbe2dcd4a68a1fd888990e5070eff9d98a337796624d6' };
245
+ return { ...fixture, ...content, id, path: `test/fixtures/${id}.json`, version,
246
+ role: fixture.role === 'input' ? 'input' : version === DOMAIN_CONTROL_FLIGHT_RECORDER_VERSION ? 'current' : 'historical',
247
+ expectation: version === DOMAIN_CONTROL_FLIGHT_RECORDER_VERSION ? fixture.expectation : 'accepted_exact',
248
+ expectedOutputFixtureId: version === DOMAIN_CONTROL_FLIGHT_RECORDER_VERSION ? fixture.expectedOutputFixtureId?.replace(/v\d+$/u, `v${version}`) ?? null : null };
249
+ }),
235
250
  ];
236
251
  }),
237
252
  metadata: {
@@ -257,11 +272,12 @@ export const DOMAIN_CONTROL_FLIGHT_RECORDER_SCHEMA_LIFECYCLE = defineSchemaLifec
257
272
  versions: [version],
258
273
  objects: [
259
274
  { path: '$', requiredKeys: DOMAIN_CONTROL_FLIGHT_RECORDER_ROOT_KEYS, optionalKeys: [], unknownKeys: 'reject' },
260
- { path: '$.events[]', requiredKeys: version === PUBLIC_DOMAIN_CONTROL_FLIGHT_RECORDER_VERSION ? PUBLIC_DOMAIN_CONTROL_FLIGHT_RECORDER_EVENT_KEYS : DOMAIN_CONTROL_FLIGHT_RECORDER_EVENT_KEYS, optionalKeys: [], unknownKeys: 'reject' },
261
- { path: '$.events[].approvedWindow', requiredKeys: DOMAIN_CONTROL_FLIGHT_RECORDER_APPROVED_WINDOW_KEYS, optionalKeys: [], unknownKeys: 'reject' },
275
+ { path: '$.events[]', requiredKeys: version === 1 ? PUBLIC_DOMAIN_CONTROL_FLIGHT_RECORDER_EVENT_KEYS : version === 2 ? SOURCE_TIMED_FLIGHT_RECORDER_EVENT_KEYS : DOMAIN_CONTROL_FLIGHT_RECORDER_EVENT_KEYS, optionalKeys: [], unknownKeys: 'reject' },
276
+ { path: '$.events[].approvedWindow', requiredKeys: version >= 3 ? DOMAIN_CONTROL_FLIGHT_RECORDER_APPROVED_WINDOW_KEYS : HISTORICAL_FLIGHT_RECORDER_APPROVED_WINDOW_KEYS, optionalKeys: [], unknownKeys: 'reject' },
277
+ ...(version >= 3 ? [{ path: '$.events[].changeInterval', requiredKeys: FLIGHT_RECORDER_CHANGE_INTERVAL_KEYS, optionalKeys: [], unknownKeys: 'reject' }] : []),
262
278
  { path: '$.summary', requiredKeys: version === PUBLIC_DOMAIN_CONTROL_FLIGHT_RECORDER_VERSION ? PUBLIC_DOMAIN_CONTROL_FLIGHT_RECORDER_SUMMARY_KEYS : DOMAIN_CONTROL_FLIGHT_RECORDER_SUMMARY_KEYS, optionalKeys: [], unknownKeys: 'reject' },
263
279
  ],
264
- fixedArrays: [{ path: '$.limitations', values: version === PUBLIC_DOMAIN_CONTROL_FLIGHT_RECORDER_VERSION ? PUBLIC_DOMAIN_CONTROL_FLIGHT_RECORDER_LIMITATIONS : DOMAIN_CONTROL_FLIGHT_RECORDER_LIMITATIONS }],
280
+ fixedArrays: [{ path: '$.limitations', values: version === 1 ? PUBLIC_DOMAIN_CONTROL_FLIGHT_RECORDER_LIMITATIONS : version === 2 ? SOURCE_TIMED_FLIGHT_RECORDER_LIMITATIONS : DOMAIN_CONTROL_FLIGHT_RECORDER_LIMITATIONS }],
265
281
  normalisation: 'preserve_document',
266
282
  target: null,
267
283
  },
@@ -410,7 +410,7 @@ export const DOMAIN_CONTROL_SCHEMA_LIFECYCLE = defineSchemaLifecycleFamily({
410
410
  bounds: [
411
411
  { id: 'exported-entries', path: '$.selectedDomains', phase: 'action', unit: 'entries', minimum: 1, maximum: MAX_DOMAIN_CONTROL_BROWSER_PROFILE_ENTRIES, handling: 'cap_operation' },
412
412
  { id: 'previewed-entries', path: '$.verifiedManifest.entries', phase: 'action', unit: 'entries', minimum: 1, maximum: MAX_DOMAIN_CONTROL_MANIFEST_ENTRIES, handling: 'reject' },
413
- { id: 'applied-baselines', path: '$.choices', phase: 'action', unit: 'entries', minimum: 0, maximum: MAX_DOMAIN_CONTROL_BROWSER_PROFILE_ENTRIES, handling: 'cap_operation' },
413
+ { id: 'applied-baselines', path: '$.choices', phase: 'action', unit: 'entries', minimum: 0, maximum: MAX_DOMAIN_CONTROL_BROWSER_PROFILE_ENTRIES, handling: 'reject' },
414
414
  ],
415
415
  },
416
416
  {