@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/README.md +51 -0
- package/dist/index.d.mts +1210 -0
- package/dist/index.d.ts +1210 -0
- package/dist/index.js +1768 -0
- package/dist/index.mjs +1632 -0
- package/package.json +27 -0
- package/src/finders.ts +712 -0
- package/src/getters.ts +409 -0
- package/src/graph.ts +601 -0
- package/src/helpers.ts +31 -0
- package/src/index.ts +28 -0
- package/src/keys.ts +286 -0
- package/src/levels.ts +262 -0
- package/src/types.generated.ts +176 -0
- package/tests/getters-finders.test.ts +461 -0
- package/tests/graph.test.ts +398 -0
- package/tests/keys-levels.test.ts +298 -0
- package/tsconfig.json +4 -0
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
|
+
}
|