@cyvest/cyvest-js 2.0.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.
package/src/getters.ts ADDED
@@ -0,0 +1,409 @@
1
+ /**
2
+ * Getter utilities for retrieving entities from a Cyvest Investigation.
3
+ *
4
+ * These functions provide type-safe access to observables, checks, threat intel,
5
+ * enrichments, and containers by their keys.
6
+ */
7
+
8
+ import type {
9
+ CyvestInvestigation,
10
+ Observable,
11
+ Check,
12
+ ThreatIntel,
13
+ Enrichment,
14
+ Container,
15
+ } from "./types.generated";
16
+
17
+ /**
18
+ * Get an observable by its key.
19
+ *
20
+ * @param inv - The investigation to search
21
+ * @param key - Observable key (e.g., "obs:ipv4-addr:192.168.1.1")
22
+ * @returns The observable or undefined if not found
23
+ *
24
+ * @example
25
+ * ```ts
26
+ * const obs = getObservable(investigation, "obs:ipv4-addr:192.168.1.1");
27
+ * if (obs) {
28
+ * console.log(obs.value, obs.level);
29
+ * }
30
+ * ```
31
+ */
32
+ export function getObservable(
33
+ inv: CyvestInvestigation,
34
+ key: string
35
+ ): Observable | undefined {
36
+ return inv.observables[key];
37
+ }
38
+
39
+ /**
40
+ * Get an observable by type and value.
41
+ *
42
+ * @param inv - The investigation to search
43
+ * @param type - Observable type (e.g., "ipv4-addr", "url")
44
+ * @param value - Observable value
45
+ * @returns The observable or undefined if not found
46
+ *
47
+ * @example
48
+ * ```ts
49
+ * const obs = getObservableByTypeValue(investigation, "ipv4-addr", "192.168.1.1");
50
+ * ```
51
+ */
52
+ export function getObservableByTypeValue(
53
+ inv: CyvestInvestigation,
54
+ type: string,
55
+ value: string
56
+ ): Observable | undefined {
57
+ const normalizedType = type.trim().toLowerCase();
58
+ const normalizedValue = value.trim().toLowerCase();
59
+
60
+ for (const obs of Object.values(inv.observables)) {
61
+ if (
62
+ obs.type.toLowerCase() === normalizedType &&
63
+ obs.value.toLowerCase() === normalizedValue
64
+ ) {
65
+ return obs;
66
+ }
67
+ }
68
+ return undefined;
69
+ }
70
+
71
+ /**
72
+ * Get a check by its key.
73
+ *
74
+ * @param inv - The investigation to search
75
+ * @param key - Check key (e.g., "chk:sender_verification:email_headers")
76
+ * @returns The check or undefined if not found
77
+ *
78
+ * @example
79
+ * ```ts
80
+ * const check = getCheck(investigation, "chk:sender_verification:email_headers");
81
+ * ```
82
+ */
83
+ export function getCheck(
84
+ inv: CyvestInvestigation,
85
+ key: string
86
+ ): Check | undefined {
87
+ for (const checks of Object.values(inv.checks)) {
88
+ for (const check of checks) {
89
+ if (check.key === key) {
90
+ return check;
91
+ }
92
+ }
93
+ }
94
+ return undefined;
95
+ }
96
+
97
+ /**
98
+ * Get a check by its ID and scope.
99
+ *
100
+ * @param inv - The investigation to search
101
+ * @param checkId - Check identifier
102
+ * @param scope - Check scope
103
+ * @returns The check or undefined if not found
104
+ *
105
+ * @example
106
+ * ```ts
107
+ * const check = getCheckByIdScope(investigation, "sender_verification", "email_headers");
108
+ * ```
109
+ */
110
+ export function getCheckByIdScope(
111
+ inv: CyvestInvestigation,
112
+ checkId: string,
113
+ scope: string
114
+ ): Check | undefined {
115
+ const normalizedId = checkId.trim().toLowerCase();
116
+ const normalizedScope = scope.trim().toLowerCase();
117
+
118
+ const scopeChecks = inv.checks[normalizedScope] || inv.checks[scope];
119
+ if (scopeChecks) {
120
+ return scopeChecks.find(
121
+ (c) => c.check_id.toLowerCase() === normalizedId
122
+ );
123
+ }
124
+
125
+ // Fallback: search all scopes
126
+ for (const checks of Object.values(inv.checks)) {
127
+ for (const check of checks) {
128
+ if (
129
+ check.check_id.toLowerCase() === normalizedId &&
130
+ check.scope.toLowerCase() === normalizedScope
131
+ ) {
132
+ return check;
133
+ }
134
+ }
135
+ }
136
+ return undefined;
137
+ }
138
+
139
+ /**
140
+ * Get all checks as a flat array (not grouped by scope).
141
+ *
142
+ * @param inv - The investigation
143
+ * @returns Array of all checks
144
+ *
145
+ * @example
146
+ * ```ts
147
+ * const allChecks = getAllChecks(investigation);
148
+ * console.log(`Total checks: ${allChecks.length}`);
149
+ * ```
150
+ */
151
+ export function getAllChecks(inv: CyvestInvestigation): Check[] {
152
+ const result: Check[] = [];
153
+ for (const checks of Object.values(inv.checks)) {
154
+ result.push(...checks);
155
+ }
156
+ return result;
157
+ }
158
+
159
+ /**
160
+ * Get a threat intel entry by its key.
161
+ *
162
+ * @param inv - The investigation to search
163
+ * @param key - Threat intel key (e.g., "ti:virustotal:obs:ipv4-addr:192.168.1.1")
164
+ * @returns The threat intel or undefined if not found
165
+ */
166
+ export function getThreatIntel(
167
+ inv: CyvestInvestigation,
168
+ key: string
169
+ ): ThreatIntel | undefined {
170
+ return inv.threat_intels[key];
171
+ }
172
+
173
+ /**
174
+ * Get a threat intel entry by source and observable key.
175
+ *
176
+ * @param inv - The investigation to search
177
+ * @param source - Threat intel source name
178
+ * @param observableKey - Key of the related observable
179
+ * @returns The threat intel or undefined if not found
180
+ */
181
+ export function getThreatIntelBySourceObservable(
182
+ inv: CyvestInvestigation,
183
+ source: string,
184
+ observableKey: string
185
+ ): ThreatIntel | undefined {
186
+ const normalizedSource = source.trim().toLowerCase();
187
+
188
+ for (const ti of Object.values(inv.threat_intels)) {
189
+ if (
190
+ ti.source.toLowerCase() === normalizedSource &&
191
+ ti.observable_key === observableKey
192
+ ) {
193
+ return ti;
194
+ }
195
+ }
196
+ return undefined;
197
+ }
198
+
199
+ /**
200
+ * Get all threat intel entries as an array.
201
+ *
202
+ * @param inv - The investigation
203
+ * @returns Array of all threat intel entries
204
+ */
205
+ export function getAllThreatIntels(inv: CyvestInvestigation): ThreatIntel[] {
206
+ return Object.values(inv.threat_intels);
207
+ }
208
+
209
+ /**
210
+ * Get an enrichment by its key.
211
+ *
212
+ * @param inv - The investigation to search
213
+ * @param key - Enrichment key (e.g., "enr:whois_data")
214
+ * @returns The enrichment or undefined if not found
215
+ */
216
+ export function getEnrichment(
217
+ inv: CyvestInvestigation,
218
+ key: string
219
+ ): Enrichment | undefined {
220
+ return inv.enrichments[key];
221
+ }
222
+
223
+ /**
224
+ * Get an enrichment by name.
225
+ *
226
+ * @param inv - The investigation to search
227
+ * @param name - Enrichment name
228
+ * @returns The first matching enrichment or undefined if not found
229
+ */
230
+ export function getEnrichmentByName(
231
+ inv: CyvestInvestigation,
232
+ name: string
233
+ ): Enrichment | undefined {
234
+ const normalizedName = name.trim().toLowerCase();
235
+
236
+ for (const enr of Object.values(inv.enrichments)) {
237
+ if (enr.name.toLowerCase() === normalizedName) {
238
+ return enr;
239
+ }
240
+ }
241
+ return undefined;
242
+ }
243
+
244
+ /**
245
+ * Get all enrichments as an array.
246
+ *
247
+ * @param inv - The investigation
248
+ * @returns Array of all enrichments
249
+ */
250
+ export function getAllEnrichments(inv: CyvestInvestigation): Enrichment[] {
251
+ return Object.values(inv.enrichments);
252
+ }
253
+
254
+ /**
255
+ * Get a container by its key.
256
+ *
257
+ * @param inv - The investigation to search
258
+ * @param key - Container key (e.g., "ctr:email/headers")
259
+ * @returns The container or undefined if not found
260
+ */
261
+ export function getContainer(
262
+ inv: CyvestInvestigation,
263
+ key: string
264
+ ): Container | undefined {
265
+ // First check top-level containers
266
+ if (inv.containers[key]) {
267
+ return inv.containers[key];
268
+ }
269
+
270
+ // Search recursively in sub-containers
271
+ function searchSubContainers(containers: Record<string, Container>): Container | undefined {
272
+ for (const container of Object.values(containers)) {
273
+ if (container.key === key) {
274
+ return container;
275
+ }
276
+ const found = searchSubContainers(container.sub_containers);
277
+ if (found) return found;
278
+ }
279
+ return undefined;
280
+ }
281
+
282
+ return searchSubContainers(inv.containers);
283
+ }
284
+
285
+ /**
286
+ * Get a container by its path.
287
+ *
288
+ * @param inv - The investigation to search
289
+ * @param path - Container path
290
+ * @returns The container or undefined if not found
291
+ */
292
+ export function getContainerByPath(
293
+ inv: CyvestInvestigation,
294
+ path: string
295
+ ): Container | undefined {
296
+ const normalizedPath = path.replace(/\\/g, "/").replace(/^\/+|\/+$/g, "").toLowerCase();
297
+
298
+ function searchContainers(containers: Record<string, Container>): Container | undefined {
299
+ for (const container of Object.values(containers)) {
300
+ if (container.path.toLowerCase() === normalizedPath) {
301
+ return container;
302
+ }
303
+ const found = searchContainers(container.sub_containers);
304
+ if (found) return found;
305
+ }
306
+ return undefined;
307
+ }
308
+
309
+ return searchContainers(inv.containers);
310
+ }
311
+
312
+ /**
313
+ * Get all containers as a flat array (including sub-containers).
314
+ *
315
+ * @param inv - The investigation
316
+ * @returns Array of all containers
317
+ */
318
+ export function getAllContainers(inv: CyvestInvestigation): Container[] {
319
+ const result: Container[] = [];
320
+
321
+ function collectContainers(containers: Record<string, Container>): void {
322
+ for (const container of Object.values(containers)) {
323
+ result.push(container);
324
+ collectContainers(container.sub_containers);
325
+ }
326
+ }
327
+
328
+ collectContainers(inv.containers);
329
+ return result;
330
+ }
331
+
332
+ /**
333
+ * Get all observables as an array.
334
+ *
335
+ * @param inv - The investigation
336
+ * @returns Array of all observables
337
+ */
338
+ export function getAllObservables(inv: CyvestInvestigation): Observable[] {
339
+ return Object.values(inv.observables);
340
+ }
341
+
342
+ /**
343
+ * Get all whitelists from the investigation.
344
+ *
345
+ * @param inv - The investigation
346
+ * @returns Array of all whitelists
347
+ */
348
+ export function getWhitelists(inv: CyvestInvestigation) {
349
+ return inv.whitelists;
350
+ }
351
+
352
+ /**
353
+ * Get the investigation statistics.
354
+ *
355
+ * @param inv - The investigation
356
+ * @returns Statistics object
357
+ */
358
+ export function getStats(inv: CyvestInvestigation) {
359
+ return inv.stats;
360
+ }
361
+
362
+ /**
363
+ * Get the investigation check statistics.
364
+ *
365
+ * @param inv - The investigation
366
+ * @returns Check statistics object
367
+ */
368
+ export function getStatsChecks(inv: CyvestInvestigation) {
369
+ return inv.stats_checks;
370
+ }
371
+
372
+ /**
373
+ * Get the data extraction configuration.
374
+ *
375
+ * @param inv - The investigation
376
+ * @returns Data extraction config
377
+ */
378
+ export function getDataExtraction(inv: CyvestInvestigation) {
379
+ return inv.data_extraction;
380
+ }
381
+
382
+ /**
383
+ * Count entities in the investigation.
384
+ */
385
+ export interface InvestigationCounts {
386
+ observables: number;
387
+ checks: number;
388
+ threatIntels: number;
389
+ enrichments: number;
390
+ containers: number;
391
+ whitelists: number;
392
+ }
393
+
394
+ /**
395
+ * Get counts of all entities in the investigation.
396
+ *
397
+ * @param inv - The investigation
398
+ * @returns Object with counts for each entity type
399
+ */
400
+ export function getCounts(inv: CyvestInvestigation): InvestigationCounts {
401
+ return {
402
+ observables: Object.keys(inv.observables).length,
403
+ checks: getAllChecks(inv).length,
404
+ threatIntels: Object.keys(inv.threat_intels).length,
405
+ enrichments: Object.keys(inv.enrichments).length,
406
+ containers: getAllContainers(inv).length,
407
+ whitelists: inv.whitelists.length,
408
+ };
409
+ }