@slicedearth/whoisleuth-cli 2.5.1 → 2.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/DISCLOSURE +1 -1
- package/cli/artifact-validation/case-response.mjs +51 -8
- package/cli/artifact-validation/lookup-evidence.mjs +15 -1
- package/cli/artifact-validation/signed-review.mjs +2 -51
- package/cli/artifact-verify.mjs +9 -2
- package/cli/bulk-command-runner.mjs +4 -6
- package/cli/case-command.mjs +79 -3
- package/cli/case-pack.mjs +7 -2
- package/cli/command-definition.mjs +2 -1
- package/cli/discovery-scan-command-runner.mjs +4 -6
- package/cli/domain-feed.mjs +61 -0
- package/cli/formatters/json.mjs +1 -13
- package/cli/indicator-set-command.mjs +1 -2
- package/cli/intake-command.mjs +43 -3
- package/cli/investigation-artifacts.mjs +2 -3
- package/cli/investigation-package-review.mjs +2 -0
- package/cli/local-mmdb-review.mjs +27 -4
- package/cli/lookup-command-runner.mjs +1 -0
- package/cli/offline-evidence-review.mjs +51 -5
- package/cli/review-arguments.mjs +3 -2
- package/cli/review-command-definitions.mjs +14 -8
- package/cli/review-command-runner.mjs +6 -3
- package/cli/risk-calibration.mjs +6 -4
- package/cli/selected-input-worker.mjs +12 -2
- package/cli/support-command-runner.mjs +3 -3
- package/cli/watchlist-review.mjs +43 -0
- package/cli/workflow-arguments.mjs +14 -0
- package/cli/workflow-command-definitions.mjs +26 -1
- package/cli/workflow-command-runner.mjs +32 -0
- package/docs/cli.md +99 -2
- package/docs/contextual-reviews.md +28 -5
- package/docs/infrastructure-observations.md +126 -0
- package/docs/offline-intake-context.md +213 -0
- package/lib/credential-form-attribution.mjs +136 -0
- package/lib/credential-surface-profile.mjs +6 -2
- package/lib/dns-delegation-health.mjs +23 -7
- package/lib/dns-intelligence.mjs +18 -4
- package/lib/domain-posture-analysis.mjs +53 -2
- package/lib/domain-posture-parsers.mjs +9 -0
- package/lib/domain-posture.mjs +29 -10
- package/lib/evidence-export-privacy.mjs +5 -2
- package/lib/evidence-report.mjs +1 -0
- package/lib/favicon.mjs +13 -4
- package/lib/html-signals.mjs +6 -0
- package/lib/lookup-child-profile-contract.mjs +28 -11
- package/lib/lookup-response-contract.mjs +1 -0
- package/lib/operation-budget.mjs +4 -0
- package/lib/service-binding-dns.mjs +19 -2
- package/lib/sslbl-certificates.generated.mjs +138 -137
- package/lib/static-html-analysis.mjs +32 -4
- package/lib/technology-evidence-role.mjs +1 -51
- package/lib/tls-intelligence.mjs +30 -5
- package/lib/website-enrichment.mjs +39 -23
- package/lib/whois-chain.mjs +12 -3
- package/lib/whois-common-formats.mjs +3 -1
- package/lib/whois-contacts.mjs +13 -2
- package/package.json +7 -5
- package/packages/analysis/risk-scoring.mjs +12 -9
- package/packages/cases/case-containment-handoff.mjs +195 -0
- package/packages/cases/case-evidence-links.mjs +16 -0
- package/packages/cases/case-evidence-model.mjs +14 -3
- package/packages/cases/case-object-coverage.mjs +33 -0
- package/packages/cases/case-packet-correction.mjs +106 -0
- package/packages/cases/case-recheck-model.mjs +35 -5
- package/packages/cases/case-record-operations.mjs +74 -9
- package/packages/cases/case-record-projection.mjs +15 -3
- package/packages/cases/case-record-version-input.mjs +23 -1
- package/packages/cases/case-report.mjs +30 -0
- package/packages/cases/case-requested-evidence.mjs +1 -1
- package/packages/cases/case-response-actions.mjs +57 -1
- package/packages/cases/case-response-model.mjs +7 -0
- package/packages/cases/case-response-object.mjs +119 -0
- package/packages/cases/case-response-outcomes.mjs +81 -14
- package/packages/cases/case-response-packet-render.mjs +22 -3
- package/packages/cases/case-response-packet.mjs +30 -2
- package/packages/cases/case-response-review-inputs.mjs +48 -7
- package/packages/cases/case-workflow-metadata.mjs +1 -9
- package/packages/contracts/analyst-interchange.mjs +7 -5
- package/packages/contracts/candidate-watch-review.mjs +54 -0
- package/packages/contracts/case-portability.mjs +260 -42
- package/packages/contracts/cli-command-semantics.mjs +3 -0
- package/packages/contracts/context-review.mjs +2 -1
- package/packages/contracts/external-observation-interchange.mjs +96 -28
- package/packages/contracts/lookup-evidence.mjs +3 -2
- package/packages/contracts/message-intake.mjs +7 -1
- package/packages/contracts/risk-calibration.mjs +44 -29
- package/packages/contracts/schema-lifecycle.mjs +3 -1
- package/packages/contracts/web-capture.mjs +1 -1
- package/packages/contracts/workspace-portability.mjs +86 -16
- package/packages/evidence/collection-quality.mjs +1 -1
- package/packages/evidence/credential-form-attribution.mjs +60 -0
- package/packages/evidence/image-regions.mjs +15 -0
- package/packages/evidence/record-identity.mjs +60 -16
- package/packages/evidence/technology-evidence-role.mjs +51 -0
- package/packages/interchange/external-findings-import.mjs +32 -17
- package/packages/interchange/json-output.mjs +14 -0
- package/packages/interchange/managed-indicator-export.mjs +3 -2
- package/packages/interchange/managed-indicator-set.mjs +13 -0
- package/packages/investigation/acquisition-decision-packet.mjs +78 -0
- package/packages/investigation/context-review.mjs +3 -3
- package/packages/investigation/document-intake.mjs +9 -3
- package/packages/investigation/domain-history-review.mjs +43 -11
- package/packages/investigation/domain-transition-review.mjs +2 -0
- package/packages/investigation/infrastructure-observation.mjs +234 -0
- package/packages/investigation/intake-context.mjs +284 -0
- package/packages/investigation/intake-indicators.mjs +116 -0
- package/packages/investigation/intake-phones.mjs +155 -0
- package/packages/investigation/intake-report.mjs +2 -1
- package/packages/investigation/investigation-manifest.mjs +33 -7
- package/packages/investigation/link-intake.mjs +38 -8
- package/packages/investigation/message-intake.mjs +55 -15
- package/packages/monitoring/domain-feed.mjs +321 -0
- package/packages/workspace/brand-candidate-workflow.mjs +351 -0
- package/packages/workspace/brand-keyword-campaign.mjs +231 -0
- package/packages/workspace/brand-profile-model.mjs +46 -2
- package/packages/workspace/bulk-session-model.mjs +4 -0
- package/packages/workspace/candidate-watch-handoff.mjs +195 -0
- package/packages/workspace/detection-rule-model.mjs +10 -2
- package/packages/workspace/shortlist-model.mjs +10 -2
- package/packages/workspace/watchlist-history.mjs +31 -4
- package/packages/workspace/watchlist-store.mjs +136 -7
- package/third-party-notices.txt +29 -29
|
@@ -1,9 +1,12 @@
|
|
|
1
1
|
// Exact, bounded validation of current and published response-review inputs.
|
|
2
2
|
import { readCaseEvidenceRequest, readCasePacketAmendment, assertEvidenceRequestEvent, assertEvidenceRequestHistory, assertPacketAmendmentSelection } from "./case-requested-evidence.mjs";
|
|
3
|
+
import { readCaseDeliveryPacketReceipt, readCasePacketCorrection, assertDeliveryPacketReceipt, correctionDelivery } from "./case-packet-correction.mjs";
|
|
4
|
+
import { CASE_SELECTED_EVIDENCE_SOURCE_LIMITATION } from "./case-evidence-links.mjs";
|
|
3
5
|
import { CASE_RESPONSE_REVIEW_INPUTS_SCHEMA, MAX_ABUSE_CATEGORY_LENGTH, MAX_ABUSIVE_URLS, MAX_AFFECTED_PARTY_LENGTH, MAX_CASE_ACTIONS, MAX_CASE_ACTION_EVENTS_PER_ACTION, MAX_CASE_ACTION_EVENTS_PER_CASE, MAX_EXACT_URL_LENGTH, MAX_RESPONSE_ACTION_HISTORY, MAX_RESPONSE_ARTEFACT_REFERENCES, MAX_RESPONSE_CONTACTS, MAX_RESPONSE_CONTRADICTIONS, MAX_RESPONSE_HARM_LENGTH, MAX_RESPONSE_LIMITATIONS, MAX_RESPONSE_LIMITATION_LENGTH, MAX_RESPONSE_REFERENCE_LENGTH, MAX_RESPONSE_SELECTED_EVIDENCE, MAX_RESPONSE_VALUE_LENGTH, PUBLISHED_V2_2_CASE_RESPONSE_REVIEW_INPUTS_VERSION, PUBLISHED_V2_3_CASE_RESPONSE_REVIEW_INPUTS_VERSION, SUPPORTED_CASE_RESPONSE_REVIEW_INPUTS_VERSIONS, } from "../contracts/case-portability.mjs";
|
|
4
6
|
import { CASE_ACTION_EVENT_SOURCE_CLASSES, CASE_ACTION_STATES, CASE_ACTION_TYPES, CASE_ASSERTION_STATES, CASE_CLOSURE_REASONS, CASE_OBSERVED_EFFECT_SOURCE_CLASSES, CASE_OBSERVED_EFFECT_STATES, CASE_PIN_COMPLETENESS, CASE_PROVIDER_OUTCOMES, } from "./case-response-records.mjs";
|
|
5
7
|
import { RESPONSE_CONTACT_KINDS, RESPONSE_PACKET_PROFILE_IDS, RESPONSE_READINESS_ROW_IDS, RESPONSE_READINESS_STATES, } from "./case-response-packet-vocabulary.mjs";
|
|
6
8
|
import { isValidAsciiHostname } from "../contracts/domain-name.mjs";
|
|
9
|
+
import { readCaseResponseObject, readCaseResponseObjects, readCaseResponseObjectOutcome, assertCaseObjectOutcome } from "./case-response-object.mjs";
|
|
7
10
|
const CONTACT_KINDS = new Set(RESPONSE_CONTACT_KINDS);
|
|
8
11
|
const PRE_PLATFORM_CONTACT_KINDS = new Set(RESPONSE_CONTACT_KINDS.filter((kind) => kind !== 'application_platform'));
|
|
9
12
|
const CONTROL_RE = /[\u0000-\u001f\u007f]/u;
|
|
@@ -27,6 +30,7 @@ const CASE_RESPONSE_REVIEW_INPUT_KEYS = Object.freeze([
|
|
|
27
30
|
'escalationHistoryOmitted',
|
|
28
31
|
'escalationHistoryLimitations',
|
|
29
32
|
'responseLifecycle',
|
|
33
|
+
'sourceQualifications',
|
|
30
34
|
]);
|
|
31
35
|
const PUBLISHED_V2_CASE_RESPONSE_REVIEW_INPUT_KEYS = Object.freeze(CASE_RESPONSE_REVIEW_INPUT_KEYS.filter((key) => key !== 'recipientRoute' && key !== 'actionBinding'));
|
|
32
36
|
function exactReviewRecord(value, keys, label, optionalKeys = []) {
|
|
@@ -88,7 +92,7 @@ function reviewEnum(value, values, label) {
|
|
|
88
92
|
function reviewNullableEnum(value, values, label) {
|
|
89
93
|
return value === null ? null : reviewEnum(value, values, label);
|
|
90
94
|
}
|
|
91
|
-
function validateReviewLifecycle(value) {
|
|
95
|
+
function validateReviewLifecycle(value, version) {
|
|
92
96
|
const lifecycle = exactReviewRecord(value, [
|
|
93
97
|
'providerOutcomeState', 'latestProviderOutcome', 'observedChangeState',
|
|
94
98
|
'latestObservedEffect', 'latestObservedChangeAt', 'closure', 'limitations',
|
|
@@ -110,7 +114,10 @@ function validateReviewLifecycle(value) {
|
|
|
110
114
|
if (lifecycle.latestObservedEffect !== null) {
|
|
111
115
|
const effect = exactReviewRecord(lifecycle.latestObservedEffect, [
|
|
112
116
|
'reviewId', 'state', 'observedAt', 'sourceClass', 'source',
|
|
113
|
-
], 'Case-response latest observed effect');
|
|
117
|
+
], 'Case-response latest observed effect', version >= 6 ? ['responseObject', 'objectOutcome'] : []);
|
|
118
|
+
const responseObject = readCaseResponseObject(effect.responseObject);
|
|
119
|
+
const objectOutcome = readCaseResponseObjectOutcome(effect.objectOutcome);
|
|
120
|
+
assertCaseObjectOutcome(objectOutcome, responseObject);
|
|
114
121
|
reviewText(effect.reviewId, 64, 'Case-response observed-effect review id');
|
|
115
122
|
reviewEnum(effect.state, CASE_OBSERVED_EFFECT_STATES, 'Case-response observed-effect state');
|
|
116
123
|
reviewText(effect.observedAt, 64, 'Case-response observed-effect time');
|
|
@@ -151,7 +158,12 @@ export function validateCaseResponseReviewInputs(value) {
|
|
|
151
158
|
}
|
|
152
159
|
const hasPlatformRoutes = version >= PUBLISHED_V2_3_CASE_RESPONSE_REVIEW_INPUTS_VERSION;
|
|
153
160
|
const hasActionBinding = version >= PUBLISHED_V2_2_CASE_RESPONSE_REVIEW_INPUTS_VERSION;
|
|
154
|
-
const source = exactReviewRecord(value, hasActionBinding ? CASE_RESPONSE_REVIEW_INPUT_KEYS : PUBLISHED_V2_CASE_RESPONSE_REVIEW_INPUT_KEYS, 'Case-response review inputs');
|
|
161
|
+
const source = exactReviewRecord(value, (hasActionBinding ? CASE_RESPONSE_REVIEW_INPUT_KEYS : PUBLISHED_V2_CASE_RESPONSE_REVIEW_INPUT_KEYS).filter(key => version >= 7 || key !== 'sourceQualifications'), 'Case-response review inputs');
|
|
162
|
+
if (version >= 7)
|
|
163
|
+
for (const qualification of boundedReviewArray(source.sourceQualifications, 1, 'Selected evidence source qualifications')) {
|
|
164
|
+
if (qualification !== CASE_SELECTED_EVIDENCE_SOURCE_LIMITATION)
|
|
165
|
+
throw new TypeError('Unknown selected-source qualification.');
|
|
166
|
+
}
|
|
155
167
|
const profile = exactReviewRecord(source.profile, [
|
|
156
168
|
'id', 'label', 'audience', 'subject', 'checklist', 'includedEvidence',
|
|
157
169
|
'excludedEvidence', 'redactions',
|
|
@@ -236,7 +248,11 @@ export function validateCaseResponseReviewInputs(value) {
|
|
|
236
248
|
for (const candidate of boundedReviewArray(source.selectedEvidence, MAX_RESPONSE_SELECTED_EVIDENCE, 'Case-response selected evidence')) {
|
|
237
249
|
const evidence = exactReviewRecord(candidate, [
|
|
238
250
|
'id', 'label', 'source', 'observedAt', 'completeness', 'limitations',
|
|
239
|
-
], 'Case-response selected evidence item',
|
|
251
|
+
], 'Case-response selected evidence item', [
|
|
252
|
+
...(version > PUBLISHED_V2_3_CASE_RESPONSE_REVIEW_INPUTS_VERSION ? ['observationHostname', 'webObservationMode'] : []),
|
|
253
|
+
...(version >= 6 ? ['responseObject'] : []),
|
|
254
|
+
]);
|
|
255
|
+
readCaseResponseObject(evidence.responseObject);
|
|
240
256
|
reviewText(evidence.id, 64, 'Case-response evidence id');
|
|
241
257
|
reviewText(evidence.label, 80, 'Case-response evidence label');
|
|
242
258
|
reviewText(evidence.source, 120, 'Case-response evidence source');
|
|
@@ -319,8 +335,11 @@ export function validateCaseResponseReviewInputs(value) {
|
|
|
319
335
|
...(hasPlatformRoutes ? ['routeReviewAfter'] : []),
|
|
320
336
|
'providerOutcome', 'outcomeDetail', 'originActionId', 'historyOmitted',
|
|
321
337
|
'historyLimitations', 'transitions', 'createdAt', 'updatedAt',
|
|
322
|
-
], 'Case-response escalation action', version >= 5 ? ['amendment'] : []);
|
|
338
|
+
], 'Case-response escalation action', [...(version >= 5 ? ['amendment'] : []), ...(version >= 6 ? ['responseObjects'] : []), ...(version >= 7 ? ['correction'] : [])]);
|
|
339
|
+
readCaseResponseObjects(action.responseObjects);
|
|
323
340
|
readCasePacketAmendment(action.amendment);
|
|
341
|
+
if (readCasePacketCorrection(action.correction) && action.amendment)
|
|
342
|
+
throw new TypeError('Correction and provider amendment must remain separate.');
|
|
324
343
|
for (const key of ['actionId', 'recipient', 'contactSource', 'createdAt', 'updatedAt']) {
|
|
325
344
|
reviewText(action[key], MAX_RESPONSE_VALUE_LENGTH, `Case-response action ${key}`);
|
|
326
345
|
}
|
|
@@ -346,7 +365,21 @@ export function validateCaseResponseReviewInputs(value) {
|
|
|
346
365
|
'id', 'previousState', 'nextState', 'occurredAt', 'sourceClass', 'provenance',
|
|
347
366
|
'reference', 'evidencePinId', 'limitations', 'providerOutcome', 'outcomeDetail',
|
|
348
367
|
'originActionId', 'applied',
|
|
349
|
-
], 'Case-response transition', version >= 5 ? ['evidenceRequest'] : []);
|
|
368
|
+
], 'Case-response transition', [...(version >= 5 ? ['evidenceRequest'] : []), ...(version >= 6 ? ['responseObjects', 'objectOutcome'] : []), ...(version >= 7 ? ['packetReceipt'] : [])]);
|
|
369
|
+
const receipt = readCaseDeliveryPacketReceipt(transition.packetReceipt);
|
|
370
|
+
assertDeliveryPacketReceipt(receipt, transition, action.actionId);
|
|
371
|
+
if (receipt && (receipt.caseId !== source.case.id || receipt.target !== source.case.domain
|
|
372
|
+
|| receipt.recipient !== action.recipient))
|
|
373
|
+
throw new TypeError('The delivery receipt must retain the same Case, target and recipient.');
|
|
374
|
+
const responseObjects = readCaseResponseObjects(transition.responseObjects);
|
|
375
|
+
const objectOutcome = readCaseResponseObjectOutcome(transition.objectOutcome);
|
|
376
|
+
if (objectOutcome) {
|
|
377
|
+
if (!responseObjects?.length || !['submitted', 'acknowledged', 'terminal'].includes(String(transition.nextState))
|
|
378
|
+
|| !['analyst', 'provider'].includes(String(transition.sourceClass))
|
|
379
|
+
|| transition.previousState === 'authorised')
|
|
380
|
+
throw new TypeError('Case-response object outcome requires an explicit affected subset after submission.');
|
|
381
|
+
responseObjects.forEach(object => assertCaseObjectOutcome(objectOutcome, object));
|
|
382
|
+
}
|
|
350
383
|
const evidenceRequest = readCaseEvidenceRequest(transition.evidenceRequest);
|
|
351
384
|
assertEvidenceRequestEvent(evidenceRequest, transition);
|
|
352
385
|
requestEvents.push({ id: transition.id, ...(evidenceRequest ? { evidenceRequest } : {}) });
|
|
@@ -376,7 +409,15 @@ export function validateCaseResponseReviewInputs(value) {
|
|
|
376
409
|
assertPacketAmendmentSelection(source.escalationHistory.map(action => ({
|
|
377
410
|
id: action.actionId, history: action.transitions, originActionId: action.originActionId, amendment: action.amendment,
|
|
378
411
|
})), currentLineageActionIds?.[0] ?? null, new Set(source.selectedEvidence.map(pin => pin.id)));
|
|
412
|
+
if (version >= 7) {
|
|
413
|
+
const actions = source.escalationHistory.map(action => ({ ...action, id: action.actionId, history: action.transitions }));
|
|
414
|
+
const selected = actions.find(action => action.id === currentLineageActionIds?.[0]);
|
|
415
|
+
if (selected?.correction)
|
|
416
|
+
correctionDelivery(actions, selected, { caseId: source.case.id,
|
|
417
|
+
target: source.case.domain, profile: profile.id,
|
|
418
|
+
selectedPinIds: new Set(source.selectedEvidence.map(pin => pin.id)) });
|
|
419
|
+
}
|
|
379
420
|
reviewStrings(source.escalationHistoryLimitations, MAX_RESPONSE_LIMITATIONS, MAX_RESPONSE_LIMITATION_LENGTH, 'Case-response escalation limitations');
|
|
380
|
-
validateReviewLifecycle(source.responseLifecycle);
|
|
421
|
+
validateReviewLifecycle(source.responseLifecycle, version);
|
|
381
422
|
return recursivelyFreezeReviewValue(structuredClone(source));
|
|
382
423
|
}
|
|
@@ -102,15 +102,7 @@ export function normalizeCaseIncidentTargetUrl(value) {
|
|
|
102
102
|
const normalized = parsed.toString();
|
|
103
103
|
return normalized.length <= MAX_CASE_INCIDENT_TARGET_URL_LENGTH ? normalized : null;
|
|
104
104
|
}
|
|
105
|
-
|
|
106
|
-
export function buildCaseIncidentCoverage(record) {
|
|
107
|
-
return caseIncidentTargets(record, { includeResolved: true }).map(target => ({
|
|
108
|
-
target,
|
|
109
|
-
hostname: new URL(target.url).hostname,
|
|
110
|
-
actionCoverage: 'unknown',
|
|
111
|
-
observationCoverage: 'unknown',
|
|
112
|
-
}));
|
|
113
|
-
}
|
|
105
|
+
export { buildCaseIncidentCoverage } from "./case-object-coverage.mjs";
|
|
114
106
|
export function emptyCaseWorkflowMetadata() {
|
|
115
107
|
return { types: [], incidentTargets: [], investigationContext: null };
|
|
116
108
|
}
|
|
@@ -33,7 +33,8 @@ export const MAIL_REPORT_CANONICALIZATION_ROUTES = Object.freeze([
|
|
|
33
33
|
export const REGISTRATION_DISCLOSURE_PLAN_SCHEMA = 'whoisleuth.registration-disclosure-plan';
|
|
34
34
|
export const REGISTRATION_DISCLOSURE_PLAN_VERSION = 2;
|
|
35
35
|
export const STATIC_PAGE_PATTERN_PACK_SCHEMA = 'whoisleuth.static-page-pattern-pack';
|
|
36
|
-
export const STATIC_PAGE_PATTERN_PACK_VERSION =
|
|
36
|
+
export const STATIC_PAGE_PATTERN_PACK_VERSION = 3;
|
|
37
|
+
export const STATIC_PAGE_PATTERN_PACK_SUPPORTED_VERSIONS = [2, STATIC_PAGE_PATTERN_PACK_VERSION];
|
|
37
38
|
export const MAX_STATIC_PAGE_PATTERN_PACK_BYTES = 256 * 1024;
|
|
38
39
|
export const WEB_CAPTURE_SUMMARY_SCHEMA = 'whoisleuth.web-capture-summary';
|
|
39
40
|
export const WEB_CAPTURE_SUMMARY_VERSION = 1;
|
|
@@ -136,11 +137,11 @@ const REGISTRATION_DISCLOSURE_COMPATIBILITY = defineSchemaCompatibility({
|
|
|
136
137
|
});
|
|
137
138
|
const STATIC_PATTERN_PACK_COMPATIBILITY = defineSchemaCompatibility({
|
|
138
139
|
id: 'export.static-page-pattern-pack', kind: 'export', schema: STATIC_PAGE_PATTERN_PACK_SCHEMA,
|
|
139
|
-
currentVersion: STATIC_PAGE_PATTERN_PACK_VERSION, supportedVersions:
|
|
140
|
-
acceptsUnversionedLegacy: false, futureVersionBehavior: 'reject', migration: '
|
|
140
|
+
currentVersion: STATIC_PAGE_PATTERN_PACK_VERSION, supportedVersions: STATIC_PAGE_PATTERN_PACK_SUPPORTED_VERSIONS,
|
|
141
|
+
acceptsUnversionedLegacy: false, futureVersionBehavior: 'reject', migration: 'read_only',
|
|
141
142
|
writeSemantics: 'non_destructive_merge', byteBudget: MAX_STATIC_PAGE_PATTERN_PACK_BYTES,
|
|
142
143
|
owner: ANALYST_INTERCHANGE_CONTRACT_OWNER,
|
|
143
|
-
note: '
|
|
144
|
+
note: 'Versions 2 and 3 remain readable. Version 3 can use attributed password-form destinations; older rules retain their declared meaning. Import remains explicit and does not execute code or start collection.',
|
|
144
145
|
});
|
|
145
146
|
export function serialiseAnalystInterchangeJson(value) {
|
|
146
147
|
return `${JSON.stringify(value, null, 2)}\n`;
|
|
@@ -229,6 +230,7 @@ export const ANALYST_INTERCHANGE_LIFECYCLE_FAMILY = defineSchemaLifecycleFamily(
|
|
|
229
230
|
{ descriptor: STATIC_PATTERN_PACK_COMPATIBILITY, lifecycleSchema: STATIC_PAGE_PATTERN_PACK_SCHEMA,
|
|
230
231
|
requiredKeys: ['schema', 'version', 'id', 'label', 'description', 'evidenceBoundary', 'relationship', 'confidence', 'rules'], optionalKeys: [],
|
|
231
232
|
hook: { module: 'packages/interchange/static-page-pattern-packs.mts', exportName: 'validateStaticPagePatternPack', role: 'structure_validator', runtime: 'shared' },
|
|
232
|
-
fixtures: [{ id: 'static-page-pattern-pack-v2', path: `${F}static-page-pattern-pack-v2.json`, bytes: 1_094, sha256: 'c409e527850bcb924f5be0708a58c5e2c32d98947c42150f6a79765991c0eb6f', version:
|
|
233
|
+
fixtures: [{ id: 'static-page-pattern-pack-v2', path: `${F}static-page-pattern-pack-v2.json`, bytes: 1_094, sha256: 'c409e527850bcb924f5be0708a58c5e2c32d98947c42150f6a79765991c0eb6f', version: 2 },
|
|
234
|
+
{ id: 'static-page-pattern-pack-v3', path: `${F}static-page-pattern-pack-v3.json`, bytes: 746, sha256: '5be19b621ad604de8c430191a34fcd2d624b22b8bdf9a5cb2653ef7d1b86c71a', version: 3 }] },
|
|
233
235
|
],
|
|
234
236
|
}));
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import { defineSchemaCompatibility } from "./schema-compatibility.mjs";
|
|
2
|
+
import { defineSchemaLifecycleFamily } from "./schema-lifecycle.mjs";
|
|
3
|
+
import { buildExtractedLifecycleFamily } from "./extracted-domain-lifecycle.mjs";
|
|
4
|
+
import { MAX_WATCHLIST_IMPORT_BYTES } from "./workspace-portability.mjs";
|
|
5
|
+
export const CANDIDATE_WATCH_INPUT_SCHEMA = 'whoisleuth.candidate-watch-input';
|
|
6
|
+
export const CANDIDATE_WATCH_INPUT_VERSION = 1;
|
|
7
|
+
export const CANDIDATE_WATCH_INPUT_COMPATIBILITY = defineSchemaCompatibility({
|
|
8
|
+
id: 'cli.candidate-watch-input',
|
|
9
|
+
kind: 'cli_document',
|
|
10
|
+
schema: CANDIDATE_WATCH_INPUT_SCHEMA,
|
|
11
|
+
currentVersion: 1,
|
|
12
|
+
supportedVersions: [1],
|
|
13
|
+
acceptsUnversionedLegacy: false,
|
|
14
|
+
futureVersionBehavior: 'reject',
|
|
15
|
+
migration: 'exact_current_only',
|
|
16
|
+
writeSemantics: 'read_only',
|
|
17
|
+
byteBudget: MAX_WATCHLIST_IMPORT_BYTES,
|
|
18
|
+
owner: 'packages/contracts/candidate-watch-review.mts',
|
|
19
|
+
note: 'Bounded offline analyst selection and optional portable Watchlist document; no collection or schedule authority.',
|
|
20
|
+
});
|
|
21
|
+
export const CANDIDATE_WATCH_REVIEW_LIFECYCLE_FAMILY = defineSchemaLifecycleFamily(buildExtractedLifecycleFamily({
|
|
22
|
+
id: 'candidate-watch-review',
|
|
23
|
+
owner: 'packages/contracts/candidate-watch-review.mts',
|
|
24
|
+
plane: 'node',
|
|
25
|
+
projection: 'review_output',
|
|
26
|
+
retention: 'transient_report',
|
|
27
|
+
serializerModule: 'cli/formatters/json.mts',
|
|
28
|
+
serializerExportName: 'formatJsonDocument',
|
|
29
|
+
includedCategories: ['candidate-provenance', 'analyst-watch-reasons', 'review-priorities'],
|
|
30
|
+
excludedCategories: ['raw-upstream-payloads', 'credentials', 'cookies', 'query-bearing-urls'],
|
|
31
|
+
formats: [
|
|
32
|
+
{
|
|
33
|
+
descriptor: CANDIDATE_WATCH_INPUT_COMPATIBILITY,
|
|
34
|
+
lifecycleSchema: CANDIDATE_WATCH_INPUT_SCHEMA,
|
|
35
|
+
requiredKeys: ['schema', 'version', 'watchlists', 'selection'],
|
|
36
|
+
optionalKeys: [],
|
|
37
|
+
hook: {
|
|
38
|
+
module: 'cli/watchlist-review.mts',
|
|
39
|
+
exportName: 'parseCandidateWatchInput',
|
|
40
|
+
role: 'structure_validator',
|
|
41
|
+
runtime: 'node',
|
|
42
|
+
},
|
|
43
|
+
fixtures: [
|
|
44
|
+
{
|
|
45
|
+
id: 'candidate-watch-input-v1',
|
|
46
|
+
path: 'test/fixtures/brand-candidate-workflow/candidate-watch-input-v1.json',
|
|
47
|
+
bytes: 895,
|
|
48
|
+
sha256: '40a0d73e5aa3afc3b6836bb289c79cf9556dd755a5086a9666506b31468b5d3d',
|
|
49
|
+
version: 1,
|
|
50
|
+
},
|
|
51
|
+
],
|
|
52
|
+
},
|
|
53
|
+
],
|
|
54
|
+
}));
|