@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.
@@ -0,0 +1,461 @@
1
+ import { describe, it, expect, beforeEach } from "vitest";
2
+ import type { CyvestInvestigation } from "../src";
3
+ import {
4
+ // Getters
5
+ getObservable,
6
+ getObservableByTypeValue,
7
+ getCheck,
8
+ getCheckByIdScope,
9
+ getAllChecks,
10
+ getThreatIntel,
11
+ getThreatIntelBySourceObservable,
12
+ getAllThreatIntels,
13
+ getEnrichment,
14
+ getEnrichmentByName,
15
+ getAllEnrichments,
16
+ getContainer,
17
+ getContainerByPath,
18
+ getAllContainers,
19
+ getAllObservables,
20
+ getCounts,
21
+ // Finders
22
+ findObservablesByType,
23
+ findObservablesByLevel,
24
+ findObservablesAtLeast,
25
+ findObservablesByValue,
26
+ findInternalObservables,
27
+ findWhitelistedObservables,
28
+ findChecksByScope,
29
+ findChecksByLevel,
30
+ findChecksAtLeast,
31
+ findThreatIntelBySource,
32
+ getChecksForObservable,
33
+ getThreatIntelsForObservable,
34
+ getObservablesForCheck,
35
+ getHighestScoringObservables,
36
+ getMaliciousObservables,
37
+ getAllScopes,
38
+ getAllObservableTypes,
39
+ } from "../src";
40
+
41
+ // Test fixture
42
+ function createTestInvestigation(): CyvestInvestigation {
43
+ return {
44
+ score: 7.5,
45
+ level: "MALICIOUS",
46
+ whitelisted: false,
47
+ whitelists: [
48
+ {
49
+ identifier: "wl-1",
50
+ name: "Test Whitelist",
51
+ justification: "Testing",
52
+ },
53
+ ],
54
+ observables: {
55
+ "obs:ipv4-addr:192.168.1.1": {
56
+ key: "obs:ipv4-addr:192.168.1.1",
57
+ type: "ipv4-addr",
58
+ value: "192.168.1.1",
59
+ internal: true,
60
+ whitelisted: false,
61
+ comment: "",
62
+ extra: null,
63
+ score: 0,
64
+ level: "INFO",
65
+ relationships: [
66
+ {
67
+ target_key: "obs:domain-name:example.com",
68
+ relationship_type: "related-to",
69
+ direction: "outbound",
70
+ },
71
+ ],
72
+ threat_intels: [],
73
+ generated_by_checks: ["chk:ip_check:network"],
74
+ },
75
+ "obs:ipv4-addr:8.8.8.8": {
76
+ key: "obs:ipv4-addr:8.8.8.8",
77
+ type: "ipv4-addr",
78
+ value: "8.8.8.8",
79
+ internal: false,
80
+ whitelisted: true,
81
+ comment: "Google DNS",
82
+ extra: null,
83
+ score: -1,
84
+ level: "TRUSTED",
85
+ relationships: [],
86
+ threat_intels: [],
87
+ generated_by_checks: [],
88
+ },
89
+ "obs:domain-name:example.com": {
90
+ key: "obs:domain-name:example.com",
91
+ type: "domain-name",
92
+ value: "example.com",
93
+ internal: false,
94
+ whitelisted: false,
95
+ comment: "",
96
+ extra: null,
97
+ score: 5,
98
+ level: "MALICIOUS",
99
+ relationships: [],
100
+ threat_intels: ["ti:virustotal:obs:domain-name:example.com"],
101
+ generated_by_checks: ["chk:domain_check:dns"],
102
+ },
103
+ "obs:url:http://malware.com/bad": {
104
+ key: "obs:url:http://malware.com/bad",
105
+ type: "url",
106
+ value: "http://malware.com/bad",
107
+ internal: false,
108
+ whitelisted: false,
109
+ comment: "",
110
+ extra: null,
111
+ score: 7.5,
112
+ level: "MALICIOUS",
113
+ relationships: [],
114
+ threat_intels: [],
115
+ generated_by_checks: [],
116
+ },
117
+ },
118
+ checks: {
119
+ network: [
120
+ {
121
+ key: "chk:ip_check:network",
122
+ check_id: "ip_check",
123
+ scope: "network",
124
+ description: "IP address check",
125
+ comment: "",
126
+ extra: null,
127
+ score: 0,
128
+ level: "INFO",
129
+ score_policy: "auto",
130
+ observables: ["obs:ipv4-addr:192.168.1.1"],
131
+ },
132
+ ],
133
+ dns: [
134
+ {
135
+ key: "chk:domain_check:dns",
136
+ check_id: "domain_check",
137
+ scope: "dns",
138
+ description: "Domain reputation check",
139
+ comment: "",
140
+ extra: null,
141
+ score: 5,
142
+ level: "MALICIOUS",
143
+ score_policy: "auto",
144
+ observables: ["obs:domain-name:example.com"],
145
+ },
146
+ {
147
+ key: "chk:dns_lookup:dns",
148
+ check_id: "dns_lookup",
149
+ scope: "dns",
150
+ description: "DNS lookup",
151
+ comment: "",
152
+ extra: null,
153
+ score: 0,
154
+ level: "INFO",
155
+ score_policy: "manual",
156
+ observables: [],
157
+ },
158
+ ],
159
+ },
160
+ checks_by_level: {
161
+ INFO: ["chk:ip_check:network", "chk:dns_lookup:dns"],
162
+ MALICIOUS: ["chk:domain_check:dns"],
163
+ },
164
+ threat_intels: {
165
+ "ti:virustotal:obs:domain-name:example.com": {
166
+ key: "ti:virustotal:obs:domain-name:example.com",
167
+ source: "virustotal",
168
+ observable_key: "obs:domain-name:example.com",
169
+ comment: "",
170
+ extra: null,
171
+ score: 5,
172
+ level: "MALICIOUS",
173
+ taxonomies: [{ verdict: "malicious" }],
174
+ },
175
+ },
176
+ enrichments: {
177
+ "enr:whois": {
178
+ key: "enr:whois",
179
+ name: "whois",
180
+ data: { registrar: "Test Registrar" },
181
+ context: "example.com",
182
+ },
183
+ },
184
+ containers: {
185
+ "ctr:email": {
186
+ key: "ctr:email",
187
+ path: "email",
188
+ description: "Email container",
189
+ checks: [],
190
+ sub_containers: {
191
+ "ctr:email/headers": {
192
+ key: "ctr:email/headers",
193
+ path: "email/headers",
194
+ description: "Email headers",
195
+ checks: ["chk:ip_check:network"],
196
+ sub_containers: {},
197
+ aggregated_score: 0,
198
+ aggregated_level: "INFO",
199
+ },
200
+ },
201
+ aggregated_score: 0,
202
+ aggregated_level: "INFO",
203
+ },
204
+ },
205
+ stats: {
206
+ total_observables: 4,
207
+ internal_observables: 1,
208
+ external_observables: 3,
209
+ whitelisted_observables: 1,
210
+ observables_by_type: { "ipv4-addr": 2, "domain-name": 1, url: 1 },
211
+ observables_by_level: { INFO: 1, TRUSTED: 1, MALICIOUS: 2 },
212
+ observables_by_type_and_level: {},
213
+ total_checks: 3,
214
+ applied_checks: 2,
215
+ checks_by_scope: { network: 1, dns: 2 },
216
+ checks_by_level: { INFO: 2, MALICIOUS: 1 },
217
+ total_threat_intel: 1,
218
+ threat_intel_by_source: { virustotal: 1 },
219
+ threat_intel_by_level: { MALICIOUS: 1 },
220
+ total_containers: 2,
221
+ },
222
+ stats_checks: {
223
+ checks: 3,
224
+ applied: 2,
225
+ },
226
+ data_extraction: {
227
+ root_type: "email-message",
228
+ score_mode: "max",
229
+ },
230
+ };
231
+ }
232
+
233
+ describe("Getters", () => {
234
+ let inv: CyvestInvestigation;
235
+
236
+ beforeEach(() => {
237
+ inv = createTestInvestigation();
238
+ });
239
+
240
+ describe("getObservable", () => {
241
+ it("returns observable by key", () => {
242
+ const obs = getObservable(inv, "obs:ipv4-addr:192.168.1.1");
243
+ expect(obs).toBeDefined();
244
+ expect(obs?.value).toBe("192.168.1.1");
245
+ });
246
+
247
+ it("returns undefined for missing key", () => {
248
+ expect(getObservable(inv, "obs:missing:key")).toBeUndefined();
249
+ });
250
+ });
251
+
252
+ describe("getObservableByTypeValue", () => {
253
+ it("finds observable by type and value", () => {
254
+ const obs = getObservableByTypeValue(inv, "ipv4-addr", "192.168.1.1");
255
+ expect(obs).toBeDefined();
256
+ expect(obs?.key).toBe("obs:ipv4-addr:192.168.1.1");
257
+ });
258
+
259
+ it("is case insensitive", () => {
260
+ const obs = getObservableByTypeValue(inv, "IPV4-ADDR", "192.168.1.1");
261
+ expect(obs).toBeDefined();
262
+ });
263
+ });
264
+
265
+ describe("getCheck", () => {
266
+ it("returns check by key", () => {
267
+ const check = getCheck(inv, "chk:domain_check:dns");
268
+ expect(check).toBeDefined();
269
+ expect(check?.check_id).toBe("domain_check");
270
+ });
271
+ });
272
+
273
+ describe("getCheckByIdScope", () => {
274
+ it("finds check by id and scope", () => {
275
+ const check = getCheckByIdScope(inv, "domain_check", "dns");
276
+ expect(check).toBeDefined();
277
+ expect(check?.key).toBe("chk:domain_check:dns");
278
+ });
279
+ });
280
+
281
+ describe("getAllChecks", () => {
282
+ it("returns all checks as flat array", () => {
283
+ const checks = getAllChecks(inv);
284
+ expect(checks).toHaveLength(3);
285
+ });
286
+ });
287
+
288
+ describe("getThreatIntel", () => {
289
+ it("returns threat intel by key", () => {
290
+ const ti = getThreatIntel(
291
+ inv,
292
+ "ti:virustotal:obs:domain-name:example.com"
293
+ );
294
+ expect(ti).toBeDefined();
295
+ expect(ti?.source).toBe("virustotal");
296
+ });
297
+ });
298
+
299
+ describe("getContainer", () => {
300
+ it("returns top-level container", () => {
301
+ const container = getContainer(inv, "ctr:email");
302
+ expect(container).toBeDefined();
303
+ expect(container?.path).toBe("email");
304
+ });
305
+
306
+ it("returns nested container", () => {
307
+ const container = getContainer(inv, "ctr:email/headers");
308
+ expect(container).toBeDefined();
309
+ expect(container?.path).toBe("email/headers");
310
+ });
311
+ });
312
+
313
+ describe("getAllContainers", () => {
314
+ it("returns all containers including nested", () => {
315
+ const containers = getAllContainers(inv);
316
+ expect(containers).toHaveLength(2);
317
+ });
318
+ });
319
+
320
+ describe("getCounts", () => {
321
+ it("returns correct counts", () => {
322
+ const counts = getCounts(inv);
323
+ expect(counts.observables).toBe(4);
324
+ expect(counts.checks).toBe(3);
325
+ expect(counts.threatIntels).toBe(1);
326
+ expect(counts.enrichments).toBe(1);
327
+ expect(counts.containers).toBe(2);
328
+ expect(counts.whitelists).toBe(1);
329
+ });
330
+ });
331
+ });
332
+
333
+ describe("Finders", () => {
334
+ let inv: CyvestInvestigation;
335
+
336
+ beforeEach(() => {
337
+ inv = createTestInvestigation();
338
+ });
339
+
340
+ describe("findObservablesByType", () => {
341
+ it("finds observables of specific type", () => {
342
+ const ips = findObservablesByType(inv, "ipv4-addr");
343
+ expect(ips).toHaveLength(2);
344
+ });
345
+
346
+ it("is case insensitive", () => {
347
+ const ips = findObservablesByType(inv, "IPV4-ADDR");
348
+ expect(ips).toHaveLength(2);
349
+ });
350
+ });
351
+
352
+ describe("findObservablesByLevel", () => {
353
+ it("finds observables at specific level", () => {
354
+ const malicious = findObservablesByLevel(inv, "MALICIOUS");
355
+ expect(malicious).toHaveLength(2);
356
+ });
357
+ });
358
+
359
+ describe("findObservablesAtLeast", () => {
360
+ it("finds observables at or above level", () => {
361
+ const suspicious = findObservablesAtLeast(inv, "SUSPICIOUS");
362
+ expect(suspicious).toHaveLength(2); // 2 malicious
363
+ });
364
+ });
365
+
366
+ describe("findInternalObservables", () => {
367
+ it("finds internal observables", () => {
368
+ const internal = findInternalObservables(inv);
369
+ expect(internal).toHaveLength(1);
370
+ expect(internal[0].internal).toBe(true);
371
+ });
372
+ });
373
+
374
+ describe("findWhitelistedObservables", () => {
375
+ it("finds whitelisted observables", () => {
376
+ const whitelisted = findWhitelistedObservables(inv);
377
+ expect(whitelisted).toHaveLength(1);
378
+ expect(whitelisted[0].value).toBe("8.8.8.8");
379
+ });
380
+ });
381
+
382
+ describe("findChecksByScope", () => {
383
+ it("finds checks in scope", () => {
384
+ const dnsChecks = findChecksByScope(inv, "dns");
385
+ expect(dnsChecks).toHaveLength(2);
386
+ });
387
+ });
388
+
389
+ describe("findChecksByLevel", () => {
390
+ it("finds checks at level", () => {
391
+ const malicious = findChecksByLevel(inv, "MALICIOUS");
392
+ expect(malicious).toHaveLength(1);
393
+ });
394
+ });
395
+
396
+ describe("findThreatIntelBySource", () => {
397
+ it("finds threat intel from source", () => {
398
+ const vt = findThreatIntelBySource(inv, "virustotal");
399
+ expect(vt).toHaveLength(1);
400
+ });
401
+ });
402
+
403
+ describe("getChecksForObservable", () => {
404
+ it("finds checks that reference observable", () => {
405
+ const checks = getChecksForObservable(inv, "obs:ipv4-addr:192.168.1.1");
406
+ expect(checks).toHaveLength(1);
407
+ expect(checks[0].check_id).toBe("ip_check");
408
+ });
409
+ });
410
+
411
+ describe("getThreatIntelsForObservable", () => {
412
+ it("finds threat intel for observable", () => {
413
+ const tis = getThreatIntelsForObservable(
414
+ inv,
415
+ "obs:domain-name:example.com"
416
+ );
417
+ expect(tis).toHaveLength(1);
418
+ expect(tis[0].source).toBe("virustotal");
419
+ });
420
+ });
421
+
422
+ describe("getObservablesForCheck", () => {
423
+ it("finds observables referenced by check", () => {
424
+ const obs = getObservablesForCheck(inv, "chk:ip_check:network");
425
+ expect(obs).toHaveLength(1);
426
+ expect(obs[0].value).toBe("192.168.1.1");
427
+ });
428
+ });
429
+
430
+ describe("getHighestScoringObservables", () => {
431
+ it("returns top scoring observables", () => {
432
+ const top = getHighestScoringObservables(inv, 2);
433
+ expect(top).toHaveLength(2);
434
+ expect(top[0].score).toBeGreaterThanOrEqual(top[1].score);
435
+ });
436
+ });
437
+
438
+ describe("getMaliciousObservables", () => {
439
+ it("returns malicious observables", () => {
440
+ const mal = getMaliciousObservables(inv);
441
+ expect(mal).toHaveLength(2);
442
+ });
443
+ });
444
+
445
+ describe("getAllScopes", () => {
446
+ it("returns all scopes", () => {
447
+ const scopes = getAllScopes(inv);
448
+ expect(scopes).toContain("network");
449
+ expect(scopes).toContain("dns");
450
+ });
451
+ });
452
+
453
+ describe("getAllObservableTypes", () => {
454
+ it("returns all observable types", () => {
455
+ const types = getAllObservableTypes(inv);
456
+ expect(types).toContain("ipv4-addr");
457
+ expect(types).toContain("domain-name");
458
+ expect(types).toContain("url");
459
+ });
460
+ });
461
+ });