@slicedearth/whoisleuth-cli 2.5.1 → 2.7.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 (170) hide show
  1. package/DISCLOSURE +9 -6
  2. package/NOTICE +3 -2
  3. package/README.md +60 -67
  4. package/cli/artifact-validation/case-response.mjs +6 -814
  5. package/cli/artifact-validation/lookup-evidence.mjs +30 -3
  6. package/cli/artifact-validation/signed-review.mjs +2 -51
  7. package/cli/artifact-verify.mjs +41 -3
  8. package/cli/bulk-command-runner.mjs +4 -6
  9. package/cli/case-command.mjs +92 -16
  10. package/cli/case-pack.mjs +14 -3
  11. package/cli/ci-report.mjs +17 -5
  12. package/cli/command-definition.mjs +7 -6
  13. package/cli/discovery-scan-command-runner.mjs +4 -6
  14. package/cli/doctor.mjs +37 -3
  15. package/cli/domain-feed.mjs +61 -0
  16. package/cli/formatters/html.mjs +3 -0
  17. package/cli/formatters/json.mjs +9 -14
  18. package/cli/formatters/terminal-lookup.mjs +24 -1
  19. package/cli/history-command-definitions.mjs +1 -1
  20. package/cli/indicator-set-command.mjs +1 -2
  21. package/cli/intake-command.mjs +43 -3
  22. package/cli/investigation-artifacts.mjs +2 -3
  23. package/cli/investigation-package-review.mjs +2 -0
  24. package/cli/investigation-plan.mjs +15 -0
  25. package/cli/local-mmdb-review.mjs +27 -4
  26. package/cli/lookup-brief.mjs +11 -5
  27. package/cli/lookup-command-runner.mjs +21 -13
  28. package/cli/offline-evidence-review.mjs +51 -5
  29. package/cli/review-arguments.mjs +3 -2
  30. package/cli/review-command-definitions.mjs +23 -17
  31. package/cli/review-command-runner.mjs +6 -3
  32. package/cli/risk-calibration.mjs +6 -4
  33. package/cli/saved-lookup.mjs +22 -7
  34. package/cli/selected-input-worker.mjs +12 -2
  35. package/cli/source-reliability.mjs +12 -10
  36. package/cli/support-arguments.mjs +1 -1
  37. package/cli/support-command-definitions.mjs +5 -4
  38. package/cli/support-command-runner.mjs +9 -7
  39. package/cli/watchlist-review.mjs +43 -0
  40. package/cli/workflow-arguments.mjs +14 -0
  41. package/cli/workflow-command-definitions.mjs +27 -1
  42. package/cli/workflow-command-runner.mjs +32 -0
  43. package/docs/cli-reference.md +49 -11
  44. package/docs/cli.md +200 -26
  45. package/docs/contextual-reviews.md +38 -5
  46. package/docs/infrastructure-observations.md +135 -0
  47. package/docs/offline-intake-context.md +213 -0
  48. package/lib/credential-form-attribution.mjs +136 -0
  49. package/lib/credential-surface-profile.mjs +6 -2
  50. package/lib/dmarc-discovery.mjs +4 -2
  51. package/lib/dns-delegation-health.mjs +23 -7
  52. package/lib/dns-inheritance-review.mjs +14 -1
  53. package/lib/dns-intelligence.mjs +18 -4
  54. package/lib/domain-posture-analysis.mjs +53 -2
  55. package/lib/domain-posture-parsers.mjs +9 -0
  56. package/lib/domain-posture.mjs +29 -10
  57. package/lib/evidence-export-privacy.mjs +5 -2
  58. package/lib/evidence-export.mjs +10 -4
  59. package/lib/evidence-report.mjs +3 -1
  60. package/lib/favicon.mjs +13 -4
  61. package/lib/generated/cisa-kev-catalog.mjs +17 -4
  62. package/lib/generated/registrar-standing-catalogue.mjs +7 -7
  63. package/lib/generated/retire-browser-catalog.mjs +178 -5
  64. package/lib/html-signals.mjs +6 -0
  65. package/lib/idn-confusables.mjs +5 -1
  66. package/lib/lookup-child-profile-contract.mjs +28 -11
  67. package/lib/lookup-collection-receipt.mjs +52 -0
  68. package/lib/lookup-diagnostics.mjs +2 -12
  69. package/lib/lookup-progress.mjs +29 -31
  70. package/lib/lookup-response-contract.mjs +10 -0
  71. package/lib/lookup-response.mjs +15 -14
  72. package/lib/lookup-source-progress.mjs +4 -2
  73. package/lib/lookup.mjs +33 -2
  74. package/lib/operation-budget.mjs +4 -0
  75. package/lib/rdap-capabilities.mjs +43 -9
  76. package/lib/registry-insights.mjs +17 -6
  77. package/lib/service-binding-dns.mjs +19 -2
  78. package/lib/sslbl-certificates.generated.mjs +142 -140
  79. package/lib/static-html-analysis.mjs +32 -4
  80. package/lib/technology-evidence-role.mjs +1 -51
  81. package/lib/tls-intelligence.mjs +30 -5
  82. package/lib/website-enrichment.mjs +39 -23
  83. package/lib/whois-authority.mjs +56 -1
  84. package/lib/whois-chain.mjs +14 -4
  85. package/lib/whois-common-formats.mjs +3 -1
  86. package/lib/whois-contacts.mjs +13 -2
  87. package/lib/whois-contracts.mjs +2 -0
  88. package/lib/whois-normalization.mjs +4 -4
  89. package/package.json +9 -7
  90. package/packages/analysis/bounded-json.mjs +12 -2
  91. package/packages/analysis/risk-scoring.mjs +12 -9
  92. package/packages/cases/case-attachment-model.mjs +36 -1
  93. package/packages/cases/case-checkpoint-receipts.mjs +68 -0
  94. package/packages/cases/case-containment-handoff.mjs +195 -0
  95. package/packages/cases/case-evidence-links.mjs +44 -0
  96. package/packages/cases/case-evidence-model.mjs +51 -7
  97. package/packages/cases/case-migration-model.mjs +9 -1
  98. package/packages/cases/case-object-coverage.mjs +119 -0
  99. package/packages/cases/case-packet-correction.mjs +106 -0
  100. package/packages/cases/case-recheck-model.mjs +35 -5
  101. package/packages/cases/case-record-operations.mjs +87 -9
  102. package/packages/cases/case-record-projection.mjs +43 -5
  103. package/packages/cases/case-record-version-input.mjs +23 -1
  104. package/packages/cases/case-report.mjs +35 -4
  105. package/packages/cases/case-requested-evidence.mjs +1 -1
  106. package/packages/cases/case-response-actions.mjs +57 -1
  107. package/packages/cases/case-response-model.mjs +7 -0
  108. package/packages/cases/case-response-object.mjs +119 -0
  109. package/packages/cases/case-response-outcomes.mjs +81 -14
  110. package/packages/cases/case-response-packet-render.mjs +22 -3
  111. package/packages/cases/case-response-packet-validation.mjs +880 -0
  112. package/packages/cases/case-response-packet-vocabulary.mjs +12 -1
  113. package/packages/cases/case-response-packet.mjs +32 -17
  114. package/packages/cases/case-response-review-inputs.mjs +66 -8
  115. package/packages/cases/case-type-presentation.mjs +10 -0
  116. package/packages/cases/case-workflow-metadata.mjs +1 -17
  117. package/packages/comparison/capture-context.mjs +3 -2
  118. package/packages/contracts/analyst-interchange.mjs +7 -5
  119. package/packages/contracts/analyst-review-state-contract.mjs +1 -1
  120. package/packages/contracts/candidate-watch-review.mjs +54 -0
  121. package/packages/contracts/case-portability.mjs +292 -43
  122. package/packages/contracts/cli-command-semantics.mjs +3 -0
  123. package/packages/contracts/cli-doctor.mjs +4 -0
  124. package/packages/contracts/cli-lookup.mjs +57 -46
  125. package/packages/contracts/context-review.mjs +2 -1
  126. package/packages/contracts/external-observation-interchange.mjs +96 -28
  127. package/packages/contracts/lookup-evidence.mjs +13 -2
  128. package/packages/contracts/message-intake.mjs +7 -1
  129. package/packages/contracts/registry-insights.mjs +6 -0
  130. package/packages/contracts/risk-calibration.mjs +44 -29
  131. package/packages/contracts/schema-lifecycle.mjs +3 -1
  132. package/packages/contracts/web-capture.mjs +1 -1
  133. package/packages/contracts/workspace-portability.mjs +86 -16
  134. package/packages/evidence/collection-quality.mjs +1 -1
  135. package/packages/evidence/credential-form-attribution.mjs +60 -0
  136. package/packages/evidence/dmarc-policy-context.mjs +80 -0
  137. package/packages/evidence/image-regions.mjs +15 -0
  138. package/packages/evidence/lookup-collection-receipt.mjs +140 -0
  139. package/packages/evidence/lookup-collection-review.mjs +74 -0
  140. package/packages/evidence/record-identity.mjs +60 -16
  141. package/packages/evidence/technology-evidence-role.mjs +51 -0
  142. package/packages/interchange/external-findings-import.mjs +40 -21
  143. package/packages/interchange/json-output.mjs +14 -0
  144. package/packages/interchange/managed-indicator-export.mjs +3 -2
  145. package/packages/interchange/managed-indicator-set.mjs +13 -0
  146. package/packages/investigation/acquisition-decision-packet.mjs +78 -0
  147. package/packages/investigation/bulk-mail-exposure.mjs +2 -2
  148. package/packages/investigation/context-review.mjs +3 -3
  149. package/packages/investigation/document-intake.mjs +9 -3
  150. package/packages/investigation/domain-history-review.mjs +50 -12
  151. package/packages/investigation/domain-transition-review.mjs +2 -0
  152. package/packages/investigation/infrastructure-observation.mjs +234 -0
  153. package/packages/investigation/intake-context.mjs +284 -0
  154. package/packages/investigation/intake-indicators.mjs +116 -0
  155. package/packages/investigation/intake-phones.mjs +155 -0
  156. package/packages/investigation/intake-report.mjs +2 -1
  157. package/packages/investigation/investigation-manifest.mjs +33 -7
  158. package/packages/investigation/link-intake.mjs +38 -8
  159. package/packages/investigation/message-intake.mjs +55 -15
  160. package/packages/monitoring/domain-feed.mjs +321 -0
  161. package/packages/workspace/brand-candidate-workflow.mjs +351 -0
  162. package/packages/workspace/brand-keyword-campaign.mjs +241 -0
  163. package/packages/workspace/brand-profile-model.mjs +46 -2
  164. package/packages/workspace/bulk-session-model.mjs +12 -0
  165. package/packages/workspace/candidate-watch-handoff.mjs +195 -0
  166. package/packages/workspace/detection-rule-model.mjs +10 -2
  167. package/packages/workspace/shortlist-model.mjs +10 -2
  168. package/packages/workspace/watchlist-history.mjs +31 -4
  169. package/packages/workspace/watchlist-store.mjs +136 -7
  170. package/third-party-notices.txt +30 -30
@@ -1,13 +1,103 @@
1
1
  // Pure browser-local watchlist collection model. Watchlist history owns the
2
2
  // evidence shape and diff semantics; this module owns collection names, schema
3
3
  // migration, import merging, and exact serialized-byte accounting.
4
- import { MAX_WATCHLIST_DOMAINS, normalizeWatchlistEntry } from "./watchlist-history.mjs";
4
+ import { MAX_WATCHLIST_DOMAINS, normalizeWatchlistEntry, compactWatchlistResults, appendWatchlistScan, mergeWatchlistBaseline, watchlistActiveDomains, assertWatchlistEditable } from "./watchlist-history.mjs";
5
+ import { mergeWatchDomainMetadata, normalizeWatchDomainMetadata } from "./brand-candidate-workflow.mjs";
5
6
  import { normalizeExplicitIsoTimestamp } from "../evidence/observation.mjs";
6
7
  import { assertWorkspaceDeclaredVersion, assertWorkspaceInputGraph, assertWorkspacePortableVersion, ordinaryWorkspaceRecord } from "./hostile-input.mjs";
7
- import { MAX_WATCHLIST_INPUTS, MAX_WATCHLIST_NAME_LENGTH, MAX_WATCHLISTS, MAX_WATCHLIST_STORE_BYTES, WATCHLIST_SCHEMA, WATCHLIST_SCHEMA_VERSION, WATCHLIST_BROWSER_SUPPORTED_VERSIONS, WATCHLIST_EXPORT_SUPPORTED_VERSIONS, } from "../contracts/workspace-portability.mjs";
8
+ import { boundedJsonLimitsForBytes, parseBoundedJson } from "../analysis/bounded-json.mjs";
9
+ import { MAX_WATCHLIST_INPUTS, MAX_WATCHLIST_NAME_LENGTH, MAX_WATCHLISTS, MAX_WATCHLIST_STORE_BYTES, MAX_WATCHLIST_PORTABLE_BYTES, WATCHLIST_RECOVERY_METADATA_BYTES, serialiseWorkspacePortableJson, WATCHLIST_SCHEMA, WATCHLIST_SCHEMA_VERSION, WATCHLIST_BROWSER_SUPPORTED_VERSIONS, WATCHLIST_EXPORT_SUPPORTED_VERSIONS, } from "../contracts/workspace-portability.mjs";
8
10
  export { MAX_WATCHLIST_INPUTS, MAX_WATCHLIST_NAME_LENGTH, MAX_WATCHLISTS, MAX_WATCHLIST_STORE_BYTES, WATCHLIST_SCHEMA, WATCHLIST_SCHEMA_VERSION, } from "../contracts/workspace-portability.mjs";
9
11
  const BLOCKED_NAMES = new Set(['__proto__', 'prototype', 'constructor']);
10
12
  const CONTROL_RE = /[\x00-\x1f\x7f]/;
13
+ /** Restore evidence while retaining every local active member and its context. */
14
+ export function planHostedWatchlistRestore(current, name, input) {
15
+ const normalizedName = normalizeWatchlistName(name);
16
+ if (!normalizedName)
17
+ throw new Error('Hosted watchlist name is invalid.');
18
+ const target = resolveWatchlistMutationTarget(normalizeWatchlistStore(current).watchlists, normalizedName);
19
+ const snapshot = normalizeWatchlistEntry(input);
20
+ assertWatchlistEditable(snapshot);
21
+ const before = new Set(target.previous ? watchlistActiveDomains(target.previous) : []);
22
+ const metadata = normalizeWatchDomainMetadata(target.previous?.domainMetadata, snapshot.results.map(record => record.domain));
23
+ const after = new Set(metadata.map(record => record.domain));
24
+ // Check bytes as well as membership before requesting consent.
25
+ assertWatchlistStoreBudget({ ...current, [target.name]: { ...snapshot, domainMetadata: metadata } });
26
+ return freezePreview(structuredClone({ name: target.name, previous: target.previous, snapshot,
27
+ retained: [...before].sort(), added: [...after].filter(domain => !before.has(domain)).sort(),
28
+ removed: [], capacity: MAX_WATCHLIST_DOMAINS }));
29
+ }
30
+ export function applyReviewedHostedWatchlistRestore(current, reviewed) {
31
+ const fresh = planHostedWatchlistRestore(current, reviewed.name, reviewed.snapshot);
32
+ if (JSON.stringify(fresh) !== JSON.stringify(reviewed))
33
+ throw new Error('The watchlist changed after review. Preview its membership again; nothing was overwritten.');
34
+ return assertWatchlistStoreBudget({ ...current, [fresh.name]: { ...fresh.snapshot,
35
+ domainMetadata: normalizeWatchDomainMetadata(fresh.previous?.domainMetadata, fresh.snapshot.results.map(record => record.domain)) } }).watchlists;
36
+ }
37
+ export function resolveWatchlistMutationTarget(current, requestedName) {
38
+ const existingName = Object.keys(current).find(candidate => candidate.toLowerCase() === requestedName.toLowerCase());
39
+ const previous = existingName ? current[existingName] ?? null : null;
40
+ assertWatchlistEditable(previous);
41
+ if (!previous && Object.keys(current).length >= MAX_WATCHLISTS)
42
+ throw new Error('Watchlist storage is full. Export and remove a watchlist before saving more.');
43
+ return { name: existingName || requestedName, previous };
44
+ }
45
+ function freezePreview(value) {
46
+ if (value && typeof value === 'object') {
47
+ for (const child of Object.values(value))
48
+ freezePreview(child);
49
+ Object.freeze(value);
50
+ }
51
+ return value;
52
+ }
53
+ /** Review current membership and the exact compact submitted evidence together. */
54
+ export function planWatchlistUpdate(current, name, input, mode, operation) {
55
+ const normalizedName = normalizeWatchlistName(name);
56
+ if (!normalizedName)
57
+ throw new Error('Watchlist names must be 1–100 characters and use a safe name.');
58
+ if (!Array.isArray(input) || input.length > MAX_WATCHLIST_DOMAINS)
59
+ throw new Error(`Watchlists are limited to ${MAX_WATCHLIST_DOMAINS} domains.`);
60
+ if (!['fast', 'deep'].includes(mode) || !['merge', 'replace'].includes(operation))
61
+ throw new Error('Choose a supported Monitor update operation.');
62
+ const target = resolveWatchlistMutationTarget(normalizeWatchlistStore(current).watchlists, normalizedName);
63
+ const submitted = compactWatchlistResults(input);
64
+ if (!submitted.length || submitted.length !== input.length)
65
+ throw new Error('The selected Monitor results contain invalid or repeated domains. Nothing was saved.');
66
+ const before = new Set(target.previous ? watchlistActiveDomains(target.previous) : []);
67
+ const after = new Set([...(operation === 'merge' ? before : []), ...submitted.map(record => record.domain)]);
68
+ if (after.size > MAX_WATCHLIST_DOMAINS)
69
+ throw new Error(`The merged watchlist exceeds its ${MAX_WATCHLIST_DOMAINS}-domain limit. Existing members were preserved.`);
70
+ return freezePreview(structuredClone({ name: target.name, operation, mode, previous: target.previous, input: submitted,
71
+ retained: [...before].filter(domain => after.has(domain)).sort(),
72
+ added: [...after].filter(domain => !before.has(domain)).sort(),
73
+ removed: [...before].filter(domain => !after.has(domain)).sort() }));
74
+ }
75
+ /** Reconcile consent inside the transaction, then retain only bounded history. */
76
+ export function applyReviewedWatchlistUpdate(current, reviewed, checkedAt = new Date().toISOString()) {
77
+ const fresh = planWatchlistUpdate(current, reviewed.name, reviewed.input, reviewed.mode, reviewed.operation);
78
+ if (JSON.stringify(fresh) !== JSON.stringify(reviewed))
79
+ throw new Error('The watchlist changed after review. Preview its membership again; nothing was overwritten.');
80
+ // An explicit replacement removes active membership, not retained history.
81
+ const selected = new Set(fresh.input.map(record => record.domain));
82
+ const previous = fresh.operation === 'replace' && fresh.previous
83
+ ? { ...fresh.previous, results: fresh.previous.results.filter(record => selected.has(record.domain)),
84
+ domainMetadata: fresh.previous.domainMetadata.filter(record => selected.has(record.domain)) }
85
+ : fresh.previous;
86
+ const appended = appendWatchlistScan(previous, fresh.input, { mode: fresh.mode, checkedAt });
87
+ let entry = appended.entry;
88
+ if (fresh.operation === 'merge' && fresh.previous) {
89
+ const records = new Map(fresh.previous.results.map(record => [record.domain, record]));
90
+ for (const record of fresh.input)
91
+ records.set(record.domain, record);
92
+ const results = [...records.values()];
93
+ entry = normalizeWatchlistEntry({ ...entry, results,
94
+ baseline: mergeWatchlistBaseline(fresh.previous.baseline, results),
95
+ domainMetadata: fresh.previous.domainMetadata });
96
+ }
97
+ const all = { ...current };
98
+ Object.defineProperty(all, fresh.name, { value: entry, enumerable: true, writable: true, configurable: true });
99
+ return { watchlists: assertWatchlistStoreBudget(all).watchlists, changes: appended.changes };
100
+ }
11
101
  function plainRecord(value) {
12
102
  return ordinaryWorkspaceRecord(value, 'Watchlist input');
13
103
  }
@@ -40,6 +130,19 @@ function defineEntry(target, name, entry) {
40
130
  Object.defineProperty(target, name, { value: entry, writable: true, enumerable: true, configurable: true });
41
131
  }
42
132
  function assertHistoricalQuality(entry, version) {
133
+ if (version !== null && version < 6 && Object.hasOwn(entry, 'membershipRecovery'))
134
+ throw new TypeError('Watchlist membership recovery requires schema 6.');
135
+ if (version !== null && version < 5 && Object.hasOwn(entry, 'domainMetadata')) {
136
+ throw new TypeError('Domain watch metadata requires Watchlist schema 5; historical evidence was not reinterpreted.');
137
+ }
138
+ if (version !== null && version < 5) {
139
+ const values = [entry.results, entry.baseline, ...((Array.isArray(entry.history) ? entry.history : []).map(event => plainRecord(event)?.changes))];
140
+ if (values.some(rows => Array.isArray(rows) && rows.some(value => {
141
+ const row = plainRecord(value);
142
+ return row && (Object.hasOwn(row, 'hasExternalPasswordForm') || row.field === 'hasExternalPasswordForm');
143
+ })))
144
+ throw new TypeError('Password-form attribution requires Watchlist schema 5; historical evidence was not reinterpreted.');
145
+ }
43
146
  if (version !== 2)
44
147
  return;
45
148
  for (const values of [entry.results, entry.baseline]) {
@@ -66,7 +169,7 @@ export function normalizeWatchlistStore(raw) {
66
169
  if (!name || !entry || !Array.isArray(entry.results) || entry.results.length > MAX_WATCHLIST_DOMAINS)
67
170
  continue;
68
171
  assertHistoricalQuality(entry, root?.schema === WATCHLIST_SCHEMA ? watchlistStoreVersion(root) : null);
69
- defineEntry(watchlists, name, normalizeWatchlistEntry(entry));
172
+ defineEntry(watchlists, name, normalizeWatchlistEntry(entry, { recoverLegacyMembership: root?.schema === WATCHLIST_SCHEMA && root.version === 5 }));
70
173
  if (Object.keys(watchlists).length >= MAX_WATCHLISTS)
71
174
  break;
72
175
  }
@@ -77,7 +180,8 @@ function byteLength(value) {
77
180
  }
78
181
  export function assertWatchlistStoreBudget(watchlists) {
79
182
  const store = normalizeWatchlistStore(watchlists);
80
- if (byteLength(JSON.stringify(store)) > MAX_WATCHLIST_STORE_BYTES) {
183
+ const recoveryBytes = Object.values(store.watchlists).filter(entry => entry.membershipRecovery).length * WATCHLIST_RECOVERY_METADATA_BYTES;
184
+ if (byteLength(JSON.stringify(store)) - recoveryBytes > MAX_WATCHLIST_STORE_BYTES) {
81
185
  throw new Error('Watchlist storage is full. Export and remove a watchlist before saving more.');
82
186
  }
83
187
  return store;
@@ -120,13 +224,28 @@ export function mergeWatchlistStores(localRaw, importedRaw) {
120
224
  continue;
121
225
  }
122
226
  assertHistoricalQuality(entry, importedVersion);
123
- const normalized = normalizeWatchlistEntry(entry);
227
+ const normalized = normalizeWatchlistEntry(entry, { recoverLegacyMembership: importedVersion === 5 });
124
228
  if (Object.prototype.hasOwnProperty.call(local, name)) {
229
+ if (normalized.membershipRecovery || local[name].membershipRecovery) {
230
+ if (JSON.stringify(normalized) === JSON.stringify(local[name])) {
231
+ skipped++;
232
+ continue;
233
+ }
234
+ throw new Error('A watchlist awaiting membership recovery cannot be merged or replaced. Import it under a separate name to preserve both records.');
235
+ }
125
236
  const localTime = local[name].updatedAt;
237
+ const previous = local[name];
238
+ const domainMetadata = mergeWatchDomainMetadata(previous.domainMetadata, Object.hasOwn(entry, 'domainMetadata') ? normalized.domainMetadata : []);
126
239
  if (!normalized.updatedAt || !localTime || normalized.updatedAt <= localTime) {
127
- skipped++;
240
+ if (JSON.stringify(domainMetadata) !== JSON.stringify(previous.domainMetadata)) {
241
+ defineEntry(local, name, { ...previous, domainMetadata });
242
+ updated++;
243
+ }
244
+ else
245
+ skipped++;
128
246
  continue;
129
247
  }
248
+ normalized.domainMetadata = domainMetadata;
130
249
  updated++;
131
250
  }
132
251
  else if (Object.keys(local).length >= MAX_WATCHLISTS) {
@@ -147,6 +266,16 @@ export function buildWatchlistExport(watchlists, nowIso = new Date().toISOString
147
266
  schema: WATCHLIST_SCHEMA,
148
267
  version: WATCHLIST_SCHEMA_VERSION,
149
268
  exportedAt,
150
- watchlists: normalizeWatchlistStore(watchlists).watchlists,
269
+ watchlists: assertWatchlistStoreBudget(watchlists).watchlists,
151
270
  };
152
271
  }
272
+ /** Preserve readable formatting when it fits; maximum stores need compact JSON. */
273
+ export function serializeWatchlistExport(watchlists, nowIso) {
274
+ const value = buildWatchlistExport(watchlists, nowIso);
275
+ const formatted = serialiseWorkspacePortableJson(value);
276
+ return byteLength(formatted) <= MAX_WATCHLIST_PORTABLE_BYTES ? formatted : JSON.stringify(value);
277
+ }
278
+ export function parseWatchlistExport(json) {
279
+ return parseBoundedJson(json, { label: 'Watchlist import', maximumBytes: MAX_WATCHLIST_PORTABLE_BYTES,
280
+ limits: boundedJsonLimitsForBytes(MAX_WATCHLIST_PORTABLE_BYTES) });
281
+ }
@@ -7,7 +7,7 @@ The inventory is the exact transitive closure of the packaged runtime dependenci
7
7
  Package count: 49
8
8
 
9
9
  ================================================================================
10
- @napi-rs/canvas@1.0.9
10
+ @napi-rs/canvas@1.0.11
11
11
  Relationship: transitive production dependency
12
12
  Declared licence: MIT
13
13
  Licence source: LICENSE
@@ -34,77 +34,77 @@ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
34
34
  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
35
35
  SOFTWARE.
36
36
  ================================================================================
37
- @napi-rs/canvas-android-arm64@1.0.9
37
+ @napi-rs/canvas-android-arm64@1.0.11
38
38
  Relationship: transitive production dependency
39
39
  Declared licence: MIT
40
40
  Licence source: locked optional platform package metadata
41
41
 
42
42
  This optional platform package declares MIT. It is installed separately on supported platforms, not included in this artefact. Its installed package provides its own licence notices.
43
43
  ================================================================================
44
- @napi-rs/canvas-darwin-arm64@1.0.9
44
+ @napi-rs/canvas-darwin-arm64@1.0.11
45
45
  Relationship: transitive production dependency
46
46
  Declared licence: MIT
47
47
  Licence source: locked optional platform package metadata
48
48
 
49
49
  This optional platform package declares MIT. It is installed separately on supported platforms, not included in this artefact. Its installed package provides its own licence notices.
50
50
  ================================================================================
51
- @napi-rs/canvas-darwin-x64@1.0.9
51
+ @napi-rs/canvas-darwin-x64@1.0.11
52
52
  Relationship: transitive production dependency
53
53
  Declared licence: MIT
54
54
  Licence source: locked optional platform package metadata
55
55
 
56
56
  This optional platform package declares MIT. It is installed separately on supported platforms, not included in this artefact. Its installed package provides its own licence notices.
57
57
  ================================================================================
58
- @napi-rs/canvas-linux-arm-gnueabihf@1.0.9
58
+ @napi-rs/canvas-linux-arm-gnueabihf@1.0.11
59
59
  Relationship: transitive production dependency
60
60
  Declared licence: MIT
61
61
  Licence source: locked optional platform package metadata
62
62
 
63
63
  This optional platform package declares MIT. It is installed separately on supported platforms, not included in this artefact. Its installed package provides its own licence notices.
64
64
  ================================================================================
65
- @napi-rs/canvas-linux-arm64-gnu@1.0.9
65
+ @napi-rs/canvas-linux-arm64-gnu@1.0.11
66
66
  Relationship: transitive production dependency
67
67
  Declared licence: MIT
68
68
  Licence source: locked optional platform package metadata
69
69
 
70
70
  This optional platform package declares MIT. It is installed separately on supported platforms, not included in this artefact. Its installed package provides its own licence notices.
71
71
  ================================================================================
72
- @napi-rs/canvas-linux-arm64-musl@1.0.9
72
+ @napi-rs/canvas-linux-arm64-musl@1.0.11
73
73
  Relationship: transitive production dependency
74
74
  Declared licence: MIT
75
75
  Licence source: locked optional platform package metadata
76
76
 
77
77
  This optional platform package declares MIT. It is installed separately on supported platforms, not included in this artefact. Its installed package provides its own licence notices.
78
78
  ================================================================================
79
- @napi-rs/canvas-linux-riscv64-gnu@1.0.9
79
+ @napi-rs/canvas-linux-riscv64-gnu@1.0.11
80
80
  Relationship: transitive production dependency
81
81
  Declared licence: MIT
82
82
  Licence source: locked optional platform package metadata
83
83
 
84
84
  This optional platform package declares MIT. It is installed separately on supported platforms, not included in this artefact. Its installed package provides its own licence notices.
85
85
  ================================================================================
86
- @napi-rs/canvas-linux-x64-gnu@1.0.9
86
+ @napi-rs/canvas-linux-x64-gnu@1.0.11
87
87
  Relationship: transitive production dependency
88
88
  Declared licence: MIT
89
89
  Licence source: locked optional platform package metadata
90
90
 
91
91
  This optional platform package declares MIT. It is installed separately on supported platforms, not included in this artefact. Its installed package provides its own licence notices.
92
92
  ================================================================================
93
- @napi-rs/canvas-linux-x64-musl@1.0.9
93
+ @napi-rs/canvas-linux-x64-musl@1.0.11
94
94
  Relationship: transitive production dependency
95
95
  Declared licence: MIT
96
96
  Licence source: locked optional platform package metadata
97
97
 
98
98
  This optional platform package declares MIT. It is installed separately on supported platforms, not included in this artefact. Its installed package provides its own licence notices.
99
99
  ================================================================================
100
- @napi-rs/canvas-win32-arm64-msvc@1.0.9
100
+ @napi-rs/canvas-win32-arm64-msvc@1.0.11
101
101
  Relationship: transitive production dependency
102
102
  Declared licence: MIT
103
103
  Licence source: locked optional platform package metadata
104
104
 
105
105
  This optional platform package declares MIT. It is installed separately on supported platforms, not included in this artefact. Its installed package provides its own licence notices.
106
106
  ================================================================================
107
- @napi-rs/canvas-win32-x64-msvc@1.0.9
107
+ @napi-rs/canvas-win32-x64-msvc@1.0.11
108
108
  Relationship: transitive production dependency
109
109
  Declared licence: MIT
110
110
  Licence source: locked optional platform package metadata
@@ -138,7 +138,7 @@ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
138
138
  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
139
139
  SOFTWARE.
140
140
  ================================================================================
141
- @peculiar/asn1-asym-key@2.9.5
141
+ @peculiar/asn1-asym-key@2.11.0
142
142
  Relationship: transitive production dependency
143
143
  Declared licence: MIT
144
144
  Licence source: LICENSE
@@ -165,7 +165,7 @@ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
165
165
  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
166
166
  SOFTWARE.
167
167
  ================================================================================
168
- @peculiar/asn1-cms@2.9.5
168
+ @peculiar/asn1-cms@2.11.0
169
169
  Relationship: transitive production dependency
170
170
  Declared licence: MIT
171
171
  Licence source: LICENSE
@@ -192,7 +192,7 @@ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
192
192
  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
193
193
  SOFTWARE.
194
194
  ================================================================================
195
- @peculiar/asn1-csr@2.9.5
195
+ @peculiar/asn1-csr@2.11.0
196
196
  Relationship: transitive production dependency
197
197
  Declared licence: MIT
198
198
  Licence source: LICENSE
@@ -219,7 +219,7 @@ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
219
219
  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
220
220
  SOFTWARE.
221
221
  ================================================================================
222
- @peculiar/asn1-ecc@2.9.5
222
+ @peculiar/asn1-ecc@2.11.0
223
223
  Relationship: transitive production dependency
224
224
  Declared licence: MIT
225
225
  Licence source: LICENSE
@@ -246,7 +246,7 @@ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
246
246
  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
247
247
  SOFTWARE.
248
248
  ================================================================================
249
- @peculiar/asn1-pfx@2.9.5
249
+ @peculiar/asn1-pfx@2.11.0
250
250
  Relationship: transitive production dependency
251
251
  Declared licence: MIT
252
252
  Licence source: LICENSE
@@ -273,7 +273,7 @@ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
273
273
  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
274
274
  SOFTWARE.
275
275
  ================================================================================
276
- @peculiar/asn1-pkcs8@2.9.5
276
+ @peculiar/asn1-pkcs8@2.11.0
277
277
  Relationship: transitive production dependency
278
278
  Declared licence: MIT
279
279
  Licence source: LICENSE
@@ -300,7 +300,7 @@ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
300
300
  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
301
301
  SOFTWARE.
302
302
  ================================================================================
303
- @peculiar/asn1-pkcs9@2.9.5
303
+ @peculiar/asn1-pkcs9@2.11.0
304
304
  Relationship: transitive production dependency
305
305
  Declared licence: MIT
306
306
  Licence source: LICENSE
@@ -327,7 +327,7 @@ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
327
327
  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
328
328
  SOFTWARE.
329
329
  ================================================================================
330
- @peculiar/asn1-rsa@2.9.5
330
+ @peculiar/asn1-rsa@2.11.0
331
331
  Relationship: transitive production dependency
332
332
  Declared licence: MIT
333
333
  Licence source: LICENSE
@@ -354,7 +354,7 @@ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
354
354
  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
355
355
  SOFTWARE.
356
356
  ================================================================================
357
- @peculiar/asn1-schema@2.9.5
357
+ @peculiar/asn1-schema@2.11.0
358
358
  Relationship: transitive production dependency
359
359
  Declared licence: MIT
360
360
  Licence source: LICENSE
@@ -381,7 +381,7 @@ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
381
381
  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
382
382
  SOFTWARE.
383
383
  ================================================================================
384
- @peculiar/asn1-x509@2.9.5
384
+ @peculiar/asn1-x509@2.11.0
385
385
  Relationship: transitive production dependency
386
386
  Declared licence: MIT
387
387
  Licence source: LICENSE
@@ -408,7 +408,7 @@ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
408
408
  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
409
409
  SOFTWARE.
410
410
  ================================================================================
411
- @peculiar/asn1-x509-attr@2.9.5
411
+ @peculiar/asn1-x509-attr@2.11.0
412
412
  Relationship: transitive production dependency
413
413
  Declared licence: MIT
414
414
  Licence source: LICENSE
@@ -435,7 +435,7 @@ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
435
435
  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
436
436
  SOFTWARE.
437
437
  ================================================================================
438
- @peculiar/asn1-x509-post-quantum@2.9.5
438
+ @peculiar/asn1-x509-post-quantum@2.11.0
439
439
  Relationship: transitive production dependency
440
440
  Declared licence: MIT
441
441
  Licence source: LICENSE
@@ -489,7 +489,7 @@ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
489
489
  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
490
490
  SOFTWARE.
491
491
  ================================================================================
492
- @peculiar/x509@2.1.0
492
+ @peculiar/x509@2.2.0
493
493
  Relationship: direct production dependency
494
494
  Declared licence: MIT
495
495
  Licence source: LICENSE
@@ -727,7 +727,7 @@ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
727
727
  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
728
728
  THE SOFTWARE.
729
729
  ================================================================================
730
- pdfjs-dist@6.3.289
730
+ pdfjs-dist@6.4.299
731
731
  Relationship: direct production dependency
732
732
  Declared licence: Apache-2.0
733
733
  Licence source: LICENSE
@@ -909,7 +909,7 @@ Apache License
909
909
 
910
910
  END OF TERMS AND CONDITIONS
911
911
  ================================================================================
912
- postal-mime@3.0.0
912
+ postal-mime@4.0.7
913
913
  Relationship: direct production dependency
914
914
  Declared licence: MIT-0
915
915
  Licence source: LICENSE.txt
@@ -1148,7 +1148,7 @@ CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
1148
1148
  OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
1149
1149
  OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
1150
1150
  ================================================================================
1151
- tldts@7.4.14
1151
+ tldts@7.4.18
1152
1152
  Relationship: direct production dependency
1153
1153
  Declared licence: MIT
1154
1154
  Licence source: LICENSE
@@ -1167,7 +1167,7 @@ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLI
1167
1167
  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
1168
1168
  WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
1169
1169
  ================================================================================
1170
- tldts-core@7.4.14
1170
+ tldts-core@7.4.18
1171
1171
  Relationship: transitive production dependency
1172
1172
  Declared licence: MIT
1173
1173
  Licence source: LICENSE
@@ -1249,7 +1249,7 @@ MIT License
1249
1249
  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
1250
1250
  SOFTWARE
1251
1251
  ================================================================================
1252
- undici@8.11.0
1252
+ undici@8.12.2
1253
1253
  Relationship: direct production dependency
1254
1254
  Declared licence: MIT
1255
1255
  Licence source: LICENSE