@slicedearth/whoisleuth-cli 1.32.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/LICENSE +661 -0
- package/LICENSES/Retire.js-Apache-2.0.txt +199 -0
- package/NOTICE +76 -0
- package/README.md +84 -0
- package/TRADEMARKS.md +41 -0
- package/bin/whoisleuth.mjs +35 -0
- package/cli/archive-inspect.mjs +198 -0
- package/cli/arguments.mjs +1022 -0
- package/cli/artifact-verify.mjs +210 -0
- package/cli/bulk-checkpoint.mjs +218 -0
- package/cli/bulk-command-runner.mjs +109 -0
- package/cli/bulk-output.mjs +112 -0
- package/cli/bulk.mjs +133 -0
- package/cli/compare.mjs +238 -0
- package/cli/completion.mjs +175 -0
- package/cli/csv.mjs +1 -0
- package/cli/discover.mjs +50 -0
- package/cli/discovery-command-runner.mjs +36 -0
- package/cli/discovery-observation-snapshot.mjs +315 -0
- package/cli/discovery-scan-command-runner.mjs +141 -0
- package/cli/discovery-scan.mjs +325 -0
- package/cli/discovery-snapshot.mjs +133 -0
- package/cli/discovery-workflow.mjs +73 -0
- package/cli/doctor.mjs +137 -0
- package/cli/errors.mjs +21 -0
- package/cli/evidence-command-arguments.mjs +145 -0
- package/cli/evidence-command-runner.mjs +106 -0
- package/cli/evidence-signing.mjs +215 -0
- package/cli/exit-codes.mjs +9 -0
- package/cli/export-evidence.mjs +28 -0
- package/cli/formatters/evidence-report.mjs +3 -0
- package/cli/formatters/html.mjs +69 -0
- package/cli/formatters/json.mjs +135 -0
- package/cli/formatters/markdown.mjs +3 -0
- package/cli/formatters/terminal.mjs +658 -0
- package/cli/http.mjs +27 -0
- package/cli/lookup-command-runner.mjs +87 -0
- package/cli/lookup-diff.mjs +171 -0
- package/cli/mail-review.mjs +204 -0
- package/cli/manual.mjs +34 -0
- package/cli/network-command-runner.mjs +88 -0
- package/cli/output-file.mjs +97 -0
- package/cli/page-compare.mjs +123 -0
- package/cli/posture.mjs +20 -0
- package/cli/progress-events.mjs +53 -0
- package/cli/progress.mjs +56 -0
- package/cli/registry-access.mjs +3 -0
- package/cli/registry-support.mjs +120 -0
- package/cli/risk-calibration.mjs +294 -0
- package/cli/runner-types.mjs +1 -0
- package/cli/runner.mjs +726 -0
- package/cli/saved-lookup.mjs +87 -0
- package/cli/source-reliability.mjs +727 -0
- package/cli/strict-exit.mjs +55 -0
- package/cli/terminal-presentation.mjs +147 -0
- package/docs/cli.md +827 -0
- package/frontend/src/lib/analysis/acquisition-decision-packet.js +93 -0
- package/frontend/src/lib/analysis/acquisition-due-diligence.js +391 -0
- package/frontend/src/lib/analysis/artifact-integrity.js +21 -0
- package/frontend/src/lib/analysis/brand-profile-model.js +421 -0
- package/frontend/src/lib/analysis/bulk-domain-comparison.js +246 -0
- package/frontend/src/lib/analysis/bulk-mail-exposure.js +192 -0
- package/frontend/src/lib/analysis/bulk-retry-plan.js +86 -0
- package/frontend/src/lib/analysis/bulk-review-export.js +62 -0
- package/frontend/src/lib/analysis/bulk-review-model.js +266 -0
- package/frontend/src/lib/analysis/bulk-session-model.js +515 -0
- package/frontend/src/lib/analysis/bulk-sort.js +49 -0
- package/frontend/src/lib/analysis/campaign-model.js +265 -0
- package/frontend/src/lib/analysis/case-evidence-model.js +642 -0
- package/frontend/src/lib/analysis/case-migration-model.js +248 -0
- package/frontend/src/lib/analysis/case-model.js +5 -0
- package/frontend/src/lib/analysis/case-record-contracts.js +44 -0
- package/frontend/src/lib/analysis/case-record-core.js +232 -0
- package/frontend/src/lib/analysis/case-record-model.js +7 -0
- package/frontend/src/lib/analysis/case-record-operations.js +224 -0
- package/frontend/src/lib/analysis/case-response-model.js +640 -0
- package/frontend/src/lib/analysis/case-response-packet.js +573 -0
- package/frontend/src/lib/analysis/case-storage-model.js +93 -0
- package/frontend/src/lib/analysis/detection-rule-model.js +306 -0
- package/frontend/src/lib/analysis/dns-record-normalization.js +11 -0
- package/frontend/src/lib/analysis/http-summary.js +165 -0
- package/frontend/src/lib/analysis/investigation-guide.js +500 -0
- package/frontend/src/lib/analysis/investigation-template-model.js +144 -0
- package/frontend/src/lib/analysis/page-baseline.js +272 -0
- package/frontend/src/lib/analysis/page-similarity.js +179 -0
- package/frontend/src/lib/analysis/relationship-evidence.js +265 -0
- package/frontend/src/lib/analysis/relationship-observation-model.js +423 -0
- package/frontend/src/lib/analysis/scoring.js +135 -0
- package/frontend/src/lib/analysis/shortlist-model.js +209 -0
- package/frontend/src/lib/analysis/utils.js +265 -0
- package/frontend/src/lib/analysis/watchlist-history.js +643 -0
- package/frontend/src/lib/analysis/watchlist-store.js +127 -0
- package/frontend/src/lib/analysis/website-snapshot-model.js +302 -0
- package/frontend/src/lib/analysis/workspace-archive-crypto.js +282 -0
- package/frontend/src/lib/analysis/workspace-archive.js +472 -0
- package/lib/abort.mjs +26 -0
- package/lib/abusech-auth.mjs +19 -0
- package/lib/availability.mjs +716 -0
- package/lib/bounded-contract-normalizers.mjs +61 -0
- package/lib/browser-library-profile.mjs +310 -0
- package/lib/bulk-comparison-evidence.mjs +83 -0
- package/lib/classify.mjs +93 -0
- package/lib/client-behavior-profile.mjs +83 -0
- package/lib/credential-surface-profile.mjs +66 -0
- package/lib/csv.mjs +23 -0
- package/lib/ct-query.mjs +27 -0
- package/lib/ct-search.mjs +393 -0
- package/lib/distributed-operation-budget.mjs +461 -0
- package/lib/dns-delegation-health.mjs +344 -0
- package/lib/dns-intelligence.mjs +739 -0
- package/lib/dns-mx.mjs +24 -0
- package/lib/dns-resolver-selection.mjs +33 -0
- package/lib/domain-posture-analysis.mjs +254 -0
- package/lib/domain-posture-parsers.mjs +358 -0
- package/lib/domain-posture.mjs +745 -0
- package/lib/error-detail.mjs +10 -0
- package/lib/evidence-export.mjs +431 -0
- package/lib/evidence-report-markdown.mjs +71 -0
- package/lib/evidence-report.mjs +391 -0
- package/lib/external-intelligence-risk.mjs +108 -0
- package/lib/favicon.mjs +146 -0
- package/lib/feature-policy.mjs +70 -0
- package/lib/generated/retire-browser-catalog.mjs +8724 -0
- package/lib/generated/unicode-confusables-17.mjs +93 -0
- package/lib/hostname.mjs +38 -0
- package/lib/html-signals.mjs +680 -0
- package/lib/http-intelligence.mjs +232 -0
- package/lib/idn-confusable-policy.mjs +86 -0
- package/lib/idn-confusables.mjs +465 -0
- package/lib/lookup-cache.mjs +130 -0
- package/lib/lookup-progress.mjs +291 -0
- package/lib/lookup-source-progress.mjs +109 -0
- package/lib/lookup-threat-provider-inventory.mjs +13 -0
- package/lib/lookup.mjs +592 -0
- package/lib/observation.mjs +114 -0
- package/lib/observed-network-context.mjs +355 -0
- package/lib/operation-budget.mjs +489 -0
- package/lib/page-fingerprints.mjs +481 -0
- package/lib/page-role-profile.mjs +117 -0
- package/lib/perceptual-hash.mjs +451 -0
- package/lib/rdap-attempts.mjs +34 -0
- package/lib/rdap-bootstrap.mjs +173 -0
- package/lib/rdap-capabilities.mjs +292 -0
- package/lib/rdap-client.mjs +125 -0
- package/lib/rdap-normalization.mjs +691 -0
- package/lib/rdap-registrar.mjs +196 -0
- package/lib/rdap-transport.mjs +39 -0
- package/lib/rdap-types.mjs +1 -0
- package/lib/rdap-validation.mjs +94 -0
- package/lib/rdap.mjs +16 -0
- package/lib/registry-access.mjs +32 -0
- package/lib/registry-capabilities.mjs +117 -0
- package/lib/registry-capability-catalogue.mjs +1206 -0
- package/lib/registry-comparison.mjs +318 -0
- package/lib/registry-dates.mjs +190 -0
- package/lib/registry-insights.mjs +325 -0
- package/lib/response-policy.mjs +256 -0
- package/lib/risk-scoring.mjs +164 -0
- package/lib/safe-fetch.mjs +446 -0
- package/lib/security-txt.mjs +359 -0
- package/lib/service-binding-dns.mjs +608 -0
- package/lib/sslbl-certificates.generated.mjs +143 -0
- package/lib/sslbl-intelligence.mjs +241 -0
- package/lib/static-html-analysis.mjs +340 -0
- package/lib/structured-data-identity.mjs +323 -0
- package/lib/threat-intelligence-contract.mjs +5 -0
- package/lib/threat-intelligence-definition-registry.mjs +34 -0
- package/lib/threat-intelligence-fixtures.mjs +82 -0
- package/lib/threat-intelligence-manifests.mjs +512 -0
- package/lib/threat-intelligence-runtime.mjs +662 -0
- package/lib/threat-intelligence-targets.mjs +168 -0
- package/lib/threat-intelligence-types.mjs +3 -0
- package/lib/threatfox-intelligence.mjs +307 -0
- package/lib/tls-intelligence.mjs +814 -0
- package/lib/typosquat-generator.mjs +884 -0
- package/lib/urlhaus-intelligence.mjs +361 -0
- package/lib/urlscan-intelligence.mjs +349 -0
- package/lib/website-security-posture.mjs +325 -0
- package/lib/website-technology.mjs +437 -0
- package/lib/whois-authority.mjs +98 -0
- package/lib/whois-chain.mjs +261 -0
- package/lib/whois-contacts.mjs +163 -0
- package/lib/whois-contracts.mjs +3 -0
- package/lib/whois-normalization.mjs +27 -0
- package/lib/whois-transport.mjs +100 -0
- package/lib/whois-values.mjs +92 -0
- package/lib/whois.mjs +1187 -0
- package/package.json +55 -0
- package/third-party-notices.txt +5571 -0
|
@@ -0,0 +1,224 @@
|
|
|
1
|
+
// Pure, framework-neutral analyst-case records, evidence histories, bounded
|
|
2
|
+
// record normalization, and analyst updates.
|
|
3
|
+
import { appendCaseAction, appendCaseAssertion, appendCaseDecision, appendCaseEvidencePin, appendCaseEvidencePins, appendCaseManualTrailEvent, appendCaseSighting, normalizeCaseActions, normalizeCaseAssertions, normalizeCaseDecisions, normalizeCaseEvidencePins, normalizeCaseManualTrail, normalizeCaseSightings, updateCaseAction, updateCaseAssertion, } from "./case-response-model.js";
|
|
4
|
+
import { CASE_DISPOSITIONS, CASE_IMPORT_VERSIONS, CASE_SCHEMA_VERSION, CASE_SOURCES, CASE_STATUSES, DEFAULT_DISPOSITION, DEFAULT_SOURCE, DEFAULT_STATUS, EVIDENCE_SOURCES, MAX_CASES, MAX_CASE_IMPORT_BYTES, MAX_CASE_STORE_BYTES, MAX_DOMAIN_LENGTH, MAX_EVIDENCE_CHANGES, MAX_EVIDENCE_DETAIL_LENGTH, MAX_EVIDENCE_FACTORS, MAX_EVIDENCE_MUTATIONS, MAX_EVIDENCE_NAMESERVERS, MAX_EVIDENCE_SNAPSHOTS_PER_CASE, MAX_EVIDENCE_STRING_LENGTH, MAX_EVIDENCE_TITLE_LENGTH, MAX_NOTES_PER_CASE, MAX_NOTE_LENGTH, MAX_TAGS_PER_CASE, MAX_TAG_LENGTH, } from "./case-record-contracts.js";
|
|
5
|
+
import { DEFAULT_EVIDENCE_SOURCE, EVIDENCE_SOURCE_SET, deterministicId, isoOrNow, makeId, normalizeDisposition, normalizeDomain, normalizeNoteBody, normalizeNotes, normalizeSource, normalizeStatus, normalizeTags, objectRecord, safeId, } from "./case-record-core.js";
|
|
6
|
+
import { normalizeEvidenceHistory, } from "./case-evidence-model.js";
|
|
7
|
+
// ---------------------------------------------------------------------------
|
|
8
|
+
// Case normalization
|
|
9
|
+
// ---------------------------------------------------------------------------
|
|
10
|
+
// A case's own source maps onto snapshot provenance for newly captured
|
|
11
|
+
// evidence. A hand-opened ('manual') case has no scan provenance.
|
|
12
|
+
function inferCaptureSource(caseSource) {
|
|
13
|
+
return typeof caseSource === 'string' && EVIDENCE_SOURCE_SET.has(caseSource) && caseSource !== 'import'
|
|
14
|
+
? caseSource
|
|
15
|
+
: DEFAULT_EVIDENCE_SOURCE;
|
|
16
|
+
}
|
|
17
|
+
// Builds a case's bounded current-schema evidence history. Uses a lenient
|
|
18
|
+
// local fallback timestamp so recoverable current data always loads.
|
|
19
|
+
function normalizeCaseEvidence(record, createdAt, updatedAt, now) {
|
|
20
|
+
const localFallback = updatedAt || createdAt || now;
|
|
21
|
+
if (Array.isArray(record.evidenceHistory)) {
|
|
22
|
+
return normalizeEvidenceHistory(record.evidenceHistory, { source: DEFAULT_EVIDENCE_SOURCE, fallback: localFallback });
|
|
23
|
+
}
|
|
24
|
+
return [];
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* Validates a single case field-by-field for LOCAL recovery: missing scalars
|
|
28
|
+
* and timestamps are defaulted so our own stored data always loads. Returns
|
|
29
|
+
* null when the record has no usable domain. An `existing` record preserves
|
|
30
|
+
* stable identity/timestamps across an update. Import validation is separate
|
|
31
|
+
* (see mergeCases) so a defaulted value can never win over local data.
|
|
32
|
+
* @param {unknown} raw
|
|
33
|
+
* @param {CaseRecord} [existing]
|
|
34
|
+
* @param {string} [nowIso]
|
|
35
|
+
* @returns {CaseRecord | null}
|
|
36
|
+
*/
|
|
37
|
+
export function normalizeCase(raw, existing, nowIso) {
|
|
38
|
+
const now = nowIso || new Date().toISOString();
|
|
39
|
+
const record = objectRecord(raw);
|
|
40
|
+
const domain = normalizeDomain(existing ? existing.domain : record.domain);
|
|
41
|
+
if (!domain)
|
|
42
|
+
return null;
|
|
43
|
+
const createdAt = existing ? existing.createdAt : isoOrNow(record.createdAt, now);
|
|
44
|
+
const updatedAt = isoOrNow(record.updatedAt, now);
|
|
45
|
+
const evidencePins = normalizeCaseEvidencePins(record.evidencePins, updatedAt);
|
|
46
|
+
const pinIds = new Set(evidencePins.map((item) => item.id));
|
|
47
|
+
return {
|
|
48
|
+
id: existing ? existing.id : safeId(record.id) || deterministicId(domain),
|
|
49
|
+
domain,
|
|
50
|
+
status: normalizeStatus(record.status),
|
|
51
|
+
disposition: normalizeDisposition(record.disposition),
|
|
52
|
+
tags: normalizeTags(record.tags),
|
|
53
|
+
notes: normalizeNotes(record.notes, now),
|
|
54
|
+
source: normalizeSource(record.source),
|
|
55
|
+
evidenceHistory: normalizeCaseEvidence(record, createdAt, updatedAt, now),
|
|
56
|
+
evidencePins,
|
|
57
|
+
decisions: normalizeCaseDecisions(record.decisions, updatedAt, pinIds),
|
|
58
|
+
actions: normalizeCaseActions(record.actions, updatedAt),
|
|
59
|
+
assertions: normalizeCaseAssertions(record.assertions, updatedAt, pinIds),
|
|
60
|
+
manualTrail: normalizeCaseManualTrail(record.manualTrail, updatedAt),
|
|
61
|
+
sightings: normalizeCaseSightings(record.sightings, updatedAt, pinIds),
|
|
62
|
+
createdAt,
|
|
63
|
+
updatedAt,
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* @param {{ domain: unknown, status?: unknown, disposition?: unknown, source?: unknown, tags?: unknown, evidence?: unknown, note?: unknown }} input
|
|
68
|
+
* @param {string} [nowIso]
|
|
69
|
+
* @returns {CaseRecord}
|
|
70
|
+
*/
|
|
71
|
+
export function createCase(input, nowIso) {
|
|
72
|
+
const now = nowIso || new Date().toISOString();
|
|
73
|
+
const domain = normalizeDomain(input.domain);
|
|
74
|
+
if (!domain)
|
|
75
|
+
throw new Error('A valid domain is required to open a case.');
|
|
76
|
+
const noteBody = normalizeNoteBody(input.note);
|
|
77
|
+
const source = normalizeSource(input.source);
|
|
78
|
+
const evidencePins = input.evidencePins !== undefined
|
|
79
|
+
? appendCaseEvidencePins([], input.evidencePins, now)
|
|
80
|
+
: input.evidencePin !== undefined
|
|
81
|
+
? appendCaseEvidencePin([], input.evidencePin, now)
|
|
82
|
+
: [];
|
|
83
|
+
const pinIds = new Set(evidencePins.map((item) => item.id));
|
|
84
|
+
return {
|
|
85
|
+
id: makeId(),
|
|
86
|
+
domain,
|
|
87
|
+
status: normalizeStatus(input.status),
|
|
88
|
+
disposition: normalizeDisposition(input.disposition),
|
|
89
|
+
tags: normalizeTags(input.tags),
|
|
90
|
+
notes: noteBody ? [{ id: makeId(), body: noteBody, createdAt: now }] : [],
|
|
91
|
+
source,
|
|
92
|
+
evidenceHistory: normalizeEvidenceHistory(input.evidence ? [input.evidence] : [], {
|
|
93
|
+
source: inferCaptureSource(source),
|
|
94
|
+
fallback: now,
|
|
95
|
+
}),
|
|
96
|
+
evidencePins,
|
|
97
|
+
decisions: input.decision !== undefined
|
|
98
|
+
? appendCaseDecision([], input.decision, now)
|
|
99
|
+
: [],
|
|
100
|
+
actions: input.action !== undefined
|
|
101
|
+
? appendCaseAction([], input.action, now)
|
|
102
|
+
: [],
|
|
103
|
+
assertions: input.assertion !== undefined
|
|
104
|
+
? appendCaseAssertion([], input.assertion, now)
|
|
105
|
+
: [],
|
|
106
|
+
manualTrail: input.trailEvent !== undefined
|
|
107
|
+
? appendCaseManualTrailEvent([], input.trailEvent, now)
|
|
108
|
+
: [],
|
|
109
|
+
sightings: input.sighting !== undefined
|
|
110
|
+
? appendCaseSighting([], input.sighting, now, pinIds)
|
|
111
|
+
: [],
|
|
112
|
+
createdAt: now,
|
|
113
|
+
updatedAt: now,
|
|
114
|
+
};
|
|
115
|
+
}
|
|
116
|
+
/**
|
|
117
|
+
* Opens the existing case for a domain, or creates one. Returns a new array
|
|
118
|
+
* (callers persist it) plus the resolved record and whether it was created.
|
|
119
|
+
* @param {CaseRecord[]} cases
|
|
120
|
+
* @param {{ domain: unknown, status?: unknown, disposition?: unknown, source?: unknown, tags?: unknown, evidence?: unknown, note?: unknown }} input
|
|
121
|
+
* @param {string} [nowIso]
|
|
122
|
+
* @returns {{ cases: CaseRecord[], record: CaseRecord, created: boolean }}
|
|
123
|
+
*/
|
|
124
|
+
export function openOrCreateCase(cases, input, nowIso) {
|
|
125
|
+
const domain = normalizeDomain(input.domain);
|
|
126
|
+
if (!domain)
|
|
127
|
+
throw new Error('A valid domain is required to open a case.');
|
|
128
|
+
const existing = cases.find((item) => item.domain === domain);
|
|
129
|
+
if (existing)
|
|
130
|
+
return { cases, record: existing, created: false };
|
|
131
|
+
if (cases.length >= MAX_CASES)
|
|
132
|
+
throw new Error(`Cases are limited to ${MAX_CASES}. Delete or export some first.`);
|
|
133
|
+
const record = createCase({ ...input, domain }, nowIso);
|
|
134
|
+
return { cases: [record, ...cases], record, created: true };
|
|
135
|
+
}
|
|
136
|
+
/**
|
|
137
|
+
* Applies a partial update to one case by id, bumping updatedAt. A `note` in
|
|
138
|
+
* the patch is appended (respecting the per-case note bound); a `tags` array
|
|
139
|
+
* replaces the tag set; an `evidence` value appends a new snapshot to the
|
|
140
|
+
* history (deduplicated, so a materially identical re-capture only advances the
|
|
141
|
+
* latest observation time). Returns a new array and the updated record.
|
|
142
|
+
* @param {CaseRecord[]} cases
|
|
143
|
+
* @param {string} id
|
|
144
|
+
* @param {{ status?: unknown, disposition?: unknown, tags?: unknown, source?: unknown, evidence?: unknown, note?: unknown }} patch
|
|
145
|
+
* @param {string} [nowIso]
|
|
146
|
+
* @returns {{ cases: CaseRecord[], record: CaseRecord }}
|
|
147
|
+
*/
|
|
148
|
+
export function updateCase(cases, id, patch, nowIso) {
|
|
149
|
+
const now = nowIso || new Date().toISOString();
|
|
150
|
+
const index = cases.findIndex((item) => item.id === id);
|
|
151
|
+
if (index < 0)
|
|
152
|
+
throw new Error('That case no longer exists.');
|
|
153
|
+
const current = cases[index];
|
|
154
|
+
if (!current)
|
|
155
|
+
throw new Error('That case no longer exists.');
|
|
156
|
+
let notes = current.notes;
|
|
157
|
+
if (patch.note !== undefined) {
|
|
158
|
+
const body = normalizeNoteBody(patch.note);
|
|
159
|
+
if (!body)
|
|
160
|
+
throw new Error('A note cannot be empty.');
|
|
161
|
+
if (notes.length >= MAX_NOTES_PER_CASE) {
|
|
162
|
+
throw new Error(`Each case is limited to ${MAX_NOTES_PER_CASE} notes.`);
|
|
163
|
+
}
|
|
164
|
+
notes = [...notes, { id: makeId(), body, createdAt: now }];
|
|
165
|
+
}
|
|
166
|
+
const source = patch.source !== undefined ? normalizeSource(patch.source) : current.source;
|
|
167
|
+
let evidenceHistory = current.evidenceHistory;
|
|
168
|
+
if (patch.evidence !== undefined) {
|
|
169
|
+
evidenceHistory = normalizeEvidenceHistory([...current.evidenceHistory, ...(patch.evidence ? [patch.evidence] : [])], { source: inferCaptureSource(source), fallback: now });
|
|
170
|
+
}
|
|
171
|
+
let evidencePins = current.evidencePins;
|
|
172
|
+
if (patch.evidencePins !== undefined) {
|
|
173
|
+
evidencePins = appendCaseEvidencePins(evidencePins, patch.evidencePins, now);
|
|
174
|
+
}
|
|
175
|
+
if (patch.evidencePin !== undefined) {
|
|
176
|
+
evidencePins = appendCaseEvidencePin(evidencePins, patch.evidencePin, now);
|
|
177
|
+
}
|
|
178
|
+
const pinIds = new Set(evidencePins.map((item) => item.id));
|
|
179
|
+
let decisions = current.decisions;
|
|
180
|
+
if (patch.decision !== undefined) {
|
|
181
|
+
decisions = appendCaseDecision(current.decisions, patch.decision, now, pinIds);
|
|
182
|
+
}
|
|
183
|
+
let actions = current.actions;
|
|
184
|
+
if (patch.action !== undefined) {
|
|
185
|
+
actions = appendCaseAction(current.actions, patch.action, now);
|
|
186
|
+
}
|
|
187
|
+
if (patch.actionUpdate !== undefined) {
|
|
188
|
+
actions = updateCaseAction(actions, patch.actionUpdate, now);
|
|
189
|
+
}
|
|
190
|
+
let assertions = current.assertions;
|
|
191
|
+
if (patch.assertion !== undefined) {
|
|
192
|
+
assertions = appendCaseAssertion(current.assertions, patch.assertion, now, pinIds);
|
|
193
|
+
}
|
|
194
|
+
if (patch.assertionUpdate !== undefined) {
|
|
195
|
+
assertions = updateCaseAssertion(assertions, patch.assertionUpdate, now, pinIds);
|
|
196
|
+
}
|
|
197
|
+
let manualTrail = current.manualTrail;
|
|
198
|
+
if (patch.trailEvent !== undefined) {
|
|
199
|
+
manualTrail = appendCaseManualTrailEvent(current.manualTrail, patch.trailEvent, now);
|
|
200
|
+
}
|
|
201
|
+
let sightings = current.sightings;
|
|
202
|
+
if (patch.sighting !== undefined) {
|
|
203
|
+
sightings = appendCaseSighting(current.sightings, patch.sighting, now, pinIds);
|
|
204
|
+
}
|
|
205
|
+
const record = {
|
|
206
|
+
...current,
|
|
207
|
+
status: patch.status !== undefined ? normalizeStatus(patch.status) : current.status,
|
|
208
|
+
disposition: patch.disposition !== undefined ? normalizeDisposition(patch.disposition) : current.disposition,
|
|
209
|
+
tags: patch.tags !== undefined ? normalizeTags(patch.tags) : current.tags,
|
|
210
|
+
source,
|
|
211
|
+
evidenceHistory,
|
|
212
|
+
evidencePins,
|
|
213
|
+
decisions,
|
|
214
|
+
actions,
|
|
215
|
+
assertions,
|
|
216
|
+
manualTrail,
|
|
217
|
+
sightings,
|
|
218
|
+
notes,
|
|
219
|
+
updatedAt: now,
|
|
220
|
+
};
|
|
221
|
+
const next = [...cases];
|
|
222
|
+
next[index] = record;
|
|
223
|
+
return { cases: next, record };
|
|
224
|
+
}
|