@cyvest/cyvest-js 3.1.0 → 4.0.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/dist/index.d.mts +113 -111
- package/dist/index.d.ts +113 -111
- package/dist/index.js +212 -39
- package/dist/index.mjs +212 -38
- package/package.json +5 -5
- package/src/finders.ts +29 -20
- package/src/types.generated.ts +108 -99
- package/tests/getters-finders.test.ts +38 -18
- package/tests/graph.test.ts +19 -10
- package/vitest.config.ts +8 -0
package/dist/index.mjs
CHANGED
|
@@ -5,6 +5,97 @@ import addFormats from "ajv-formats";
|
|
|
5
5
|
// ../../../schema/cyvest.schema.json
|
|
6
6
|
var cyvest_schema_default = {
|
|
7
7
|
$defs: {
|
|
8
|
+
AuditEvent: {
|
|
9
|
+
additionalProperties: true,
|
|
10
|
+
description: "Centralized audit event for investigation-level changes.",
|
|
11
|
+
properties: {
|
|
12
|
+
event_id: {
|
|
13
|
+
title: "Event Id",
|
|
14
|
+
type: "string"
|
|
15
|
+
},
|
|
16
|
+
timestamp: {
|
|
17
|
+
format: "date-time",
|
|
18
|
+
title: "Timestamp",
|
|
19
|
+
type: "string"
|
|
20
|
+
},
|
|
21
|
+
event_type: {
|
|
22
|
+
title: "Event Type",
|
|
23
|
+
type: "string"
|
|
24
|
+
},
|
|
25
|
+
actor: {
|
|
26
|
+
anyOf: [
|
|
27
|
+
{
|
|
28
|
+
type: "string"
|
|
29
|
+
},
|
|
30
|
+
{
|
|
31
|
+
type: "null"
|
|
32
|
+
}
|
|
33
|
+
],
|
|
34
|
+
default: null,
|
|
35
|
+
title: "Actor"
|
|
36
|
+
},
|
|
37
|
+
reason: {
|
|
38
|
+
anyOf: [
|
|
39
|
+
{
|
|
40
|
+
type: "string"
|
|
41
|
+
},
|
|
42
|
+
{
|
|
43
|
+
type: "null"
|
|
44
|
+
}
|
|
45
|
+
],
|
|
46
|
+
default: null,
|
|
47
|
+
title: "Reason"
|
|
48
|
+
},
|
|
49
|
+
tool: {
|
|
50
|
+
anyOf: [
|
|
51
|
+
{
|
|
52
|
+
type: "string"
|
|
53
|
+
},
|
|
54
|
+
{
|
|
55
|
+
type: "null"
|
|
56
|
+
}
|
|
57
|
+
],
|
|
58
|
+
default: null,
|
|
59
|
+
title: "Tool"
|
|
60
|
+
},
|
|
61
|
+
object_type: {
|
|
62
|
+
anyOf: [
|
|
63
|
+
{
|
|
64
|
+
type: "string"
|
|
65
|
+
},
|
|
66
|
+
{
|
|
67
|
+
type: "null"
|
|
68
|
+
}
|
|
69
|
+
],
|
|
70
|
+
default: null,
|
|
71
|
+
title: "Object Type"
|
|
72
|
+
},
|
|
73
|
+
object_key: {
|
|
74
|
+
anyOf: [
|
|
75
|
+
{
|
|
76
|
+
type: "string"
|
|
77
|
+
},
|
|
78
|
+
{
|
|
79
|
+
type: "null"
|
|
80
|
+
}
|
|
81
|
+
],
|
|
82
|
+
default: null,
|
|
83
|
+
title: "Object Key"
|
|
84
|
+
},
|
|
85
|
+
details: {
|
|
86
|
+
additionalProperties: true,
|
|
87
|
+
title: "Details",
|
|
88
|
+
type: "object"
|
|
89
|
+
}
|
|
90
|
+
},
|
|
91
|
+
required: [
|
|
92
|
+
"event_id",
|
|
93
|
+
"timestamp",
|
|
94
|
+
"event_type"
|
|
95
|
+
],
|
|
96
|
+
title: "AuditEvent",
|
|
97
|
+
type: "object"
|
|
98
|
+
},
|
|
8
99
|
Check: {
|
|
9
100
|
description: "Represents a verification step in the investigation.\n\nA check validates a specific aspect of the data under investigation\nand contributes to the overall investigation score.",
|
|
10
101
|
properties: {
|
|
@@ -36,20 +127,25 @@ var cyvest_schema_default = {
|
|
|
36
127
|
level: {
|
|
37
128
|
$ref: "#/$defs/Level"
|
|
38
129
|
},
|
|
39
|
-
|
|
130
|
+
origin_investigation_id: {
|
|
131
|
+
title: "Origin Investigation Id",
|
|
132
|
+
type: "string"
|
|
133
|
+
},
|
|
134
|
+
observable_links: {
|
|
40
135
|
items: {
|
|
41
|
-
|
|
136
|
+
$ref: "#/$defs/ObservableLink"
|
|
42
137
|
},
|
|
43
|
-
title: "
|
|
138
|
+
title: "Observable Links",
|
|
44
139
|
type: "array"
|
|
45
140
|
},
|
|
46
|
-
score_policy: {
|
|
47
|
-
$ref: "#/$defs/CheckScorePolicy",
|
|
48
|
-
default: "auto"
|
|
49
|
-
},
|
|
50
141
|
key: {
|
|
51
142
|
title: "Key",
|
|
52
143
|
type: "string"
|
|
144
|
+
},
|
|
145
|
+
score_display: {
|
|
146
|
+
readOnly: true,
|
|
147
|
+
title: "Score Display",
|
|
148
|
+
type: "string"
|
|
53
149
|
}
|
|
54
150
|
},
|
|
55
151
|
required: [
|
|
@@ -60,21 +156,14 @@ var cyvest_schema_default = {
|
|
|
60
156
|
"extra",
|
|
61
157
|
"score",
|
|
62
158
|
"level",
|
|
63
|
-
"
|
|
64
|
-
"
|
|
159
|
+
"origin_investigation_id",
|
|
160
|
+
"observable_links",
|
|
161
|
+
"key",
|
|
162
|
+
"score_display"
|
|
65
163
|
],
|
|
66
164
|
title: "Check",
|
|
67
165
|
type: "object"
|
|
68
166
|
},
|
|
69
|
-
CheckScorePolicy: {
|
|
70
|
-
description: "Controls how a check reacts to linked observables.",
|
|
71
|
-
enum: [
|
|
72
|
-
"auto",
|
|
73
|
-
"manual"
|
|
74
|
-
],
|
|
75
|
-
title: "CheckScorePolicy",
|
|
76
|
-
type: "string"
|
|
77
|
-
},
|
|
78
167
|
Container: {
|
|
79
168
|
additionalProperties: false,
|
|
80
169
|
description: "Groups checks and sub-containers for hierarchical organization.\n\nContainers allow structuring the investigation into logical sections\nwith aggregated scores and levels.",
|
|
@@ -284,14 +373,19 @@ var cyvest_schema_default = {
|
|
|
284
373
|
title: "Key",
|
|
285
374
|
type: "string"
|
|
286
375
|
},
|
|
287
|
-
|
|
288
|
-
description: "Checks that
|
|
376
|
+
check_links: {
|
|
377
|
+
description: "Checks that currently link to this observable (navigation-only).",
|
|
289
378
|
items: {
|
|
290
379
|
type: "string"
|
|
291
380
|
},
|
|
292
381
|
readOnly: true,
|
|
293
|
-
title: "
|
|
382
|
+
title: "Check Links",
|
|
294
383
|
type: "array"
|
|
384
|
+
},
|
|
385
|
+
score_display: {
|
|
386
|
+
readOnly: true,
|
|
387
|
+
title: "Score Display",
|
|
388
|
+
type: "string"
|
|
295
389
|
}
|
|
296
390
|
},
|
|
297
391
|
required: [
|
|
@@ -306,11 +400,40 @@ var cyvest_schema_default = {
|
|
|
306
400
|
"threat_intels",
|
|
307
401
|
"relationships",
|
|
308
402
|
"key",
|
|
309
|
-
"
|
|
403
|
+
"check_links",
|
|
404
|
+
"score_display"
|
|
310
405
|
],
|
|
311
406
|
title: "Observable",
|
|
312
407
|
type: "object"
|
|
313
408
|
},
|
|
409
|
+
ObservableLink: {
|
|
410
|
+
additionalProperties: false,
|
|
411
|
+
description: "Edge metadata for a Check\u2194Observable association.",
|
|
412
|
+
properties: {
|
|
413
|
+
observable_key: {
|
|
414
|
+
title: "Observable Key",
|
|
415
|
+
type: "string"
|
|
416
|
+
},
|
|
417
|
+
propagation_mode: {
|
|
418
|
+
$ref: "#/$defs/PropagationMode",
|
|
419
|
+
default: "LOCAL_ONLY"
|
|
420
|
+
}
|
|
421
|
+
},
|
|
422
|
+
required: [
|
|
423
|
+
"observable_key"
|
|
424
|
+
],
|
|
425
|
+
title: "ObservableLink",
|
|
426
|
+
type: "object"
|
|
427
|
+
},
|
|
428
|
+
PropagationMode: {
|
|
429
|
+
description: "Controls how a Check\u2194Observable link propagates across merged investigations.",
|
|
430
|
+
enum: [
|
|
431
|
+
"LOCAL_ONLY",
|
|
432
|
+
"GLOBAL"
|
|
433
|
+
],
|
|
434
|
+
title: "PropagationMode",
|
|
435
|
+
type: "string"
|
|
436
|
+
},
|
|
314
437
|
Relationship: {
|
|
315
438
|
description: "Represents a relationship between observables.",
|
|
316
439
|
properties: {
|
|
@@ -530,6 +653,11 @@ var cyvest_schema_default = {
|
|
|
530
653
|
key: {
|
|
531
654
|
title: "Key",
|
|
532
655
|
type: "string"
|
|
656
|
+
},
|
|
657
|
+
score_display: {
|
|
658
|
+
readOnly: true,
|
|
659
|
+
title: "Score Display",
|
|
660
|
+
type: "string"
|
|
533
661
|
}
|
|
534
662
|
},
|
|
535
663
|
required: [
|
|
@@ -540,7 +668,8 @@ var cyvest_schema_default = {
|
|
|
540
668
|
"score",
|
|
541
669
|
"level",
|
|
542
670
|
"taxonomies",
|
|
543
|
-
"key"
|
|
671
|
+
"key",
|
|
672
|
+
"score_display"
|
|
544
673
|
],
|
|
545
674
|
title: "ThreatIntel",
|
|
546
675
|
type: "object"
|
|
@@ -551,6 +680,24 @@ var cyvest_schema_default = {
|
|
|
551
680
|
additionalProperties: false,
|
|
552
681
|
description: "Schema for a complete serialized investigation.\n\nThis model describes the output of `serialize_investigation()` from\n`cyvest.io_serialization`. It is the top-level schema for exported investigations.\n\nEntity types reference the runtime models directly. When generating schemas with\n`mode='serialization'`, Pydantic respects field_serializer decorators and produces\nschemas matching the actual model_dump() output.",
|
|
553
682
|
properties: {
|
|
683
|
+
investigation_id: {
|
|
684
|
+
description: "Stable investigation identity (ULID).",
|
|
685
|
+
title: "Investigation Id",
|
|
686
|
+
type: "string"
|
|
687
|
+
},
|
|
688
|
+
investigation_name: {
|
|
689
|
+
anyOf: [
|
|
690
|
+
{
|
|
691
|
+
type: "string"
|
|
692
|
+
},
|
|
693
|
+
{
|
|
694
|
+
type: "null"
|
|
695
|
+
}
|
|
696
|
+
],
|
|
697
|
+
default: null,
|
|
698
|
+
description: "Optional human-readable investigation name.",
|
|
699
|
+
title: "Investigation Name"
|
|
700
|
+
},
|
|
554
701
|
started_at: {
|
|
555
702
|
description: "Investigation start time (UTC).",
|
|
556
703
|
format: "date-time",
|
|
@@ -579,6 +726,14 @@ var cyvest_schema_default = {
|
|
|
579
726
|
title: "Whitelists",
|
|
580
727
|
type: "array"
|
|
581
728
|
},
|
|
729
|
+
event_log: {
|
|
730
|
+
description: "Append-only investigation audit log.",
|
|
731
|
+
items: {
|
|
732
|
+
$ref: "#/$defs/AuditEvent"
|
|
733
|
+
},
|
|
734
|
+
title: "Event Log",
|
|
735
|
+
type: "array"
|
|
736
|
+
},
|
|
582
737
|
observables: {
|
|
583
738
|
additionalProperties: {
|
|
584
739
|
$ref: "#/$defs/Observable"
|
|
@@ -644,9 +799,16 @@ var cyvest_schema_default = {
|
|
|
644
799
|
data_extraction: {
|
|
645
800
|
$ref: "#/$defs/DataExtractionSchema",
|
|
646
801
|
description: "Data extraction metadata."
|
|
802
|
+
},
|
|
803
|
+
score_display: {
|
|
804
|
+
description: "Global investigation score formatted as fixed-point x.xx.",
|
|
805
|
+
readOnly: true,
|
|
806
|
+
title: "Score Display",
|
|
807
|
+
type: "string"
|
|
647
808
|
}
|
|
648
809
|
},
|
|
649
810
|
required: [
|
|
811
|
+
"investigation_id",
|
|
650
812
|
"started_at",
|
|
651
813
|
"score",
|
|
652
814
|
"level",
|
|
@@ -660,7 +822,8 @@ var cyvest_schema_default = {
|
|
|
660
822
|
"containers",
|
|
661
823
|
"stats",
|
|
662
824
|
"stats_checks",
|
|
663
|
-
"data_extraction"
|
|
825
|
+
"data_extraction",
|
|
826
|
+
"score_display"
|
|
664
827
|
],
|
|
665
828
|
title: "Cyvest Investigation",
|
|
666
829
|
type: "object"
|
|
@@ -1140,17 +1303,6 @@ function findChecksByCheckId(inv, checkId) {
|
|
|
1140
1303
|
}
|
|
1141
1304
|
return result;
|
|
1142
1305
|
}
|
|
1143
|
-
function findManuallyScored(inv) {
|
|
1144
|
-
const result = [];
|
|
1145
|
-
for (const checks of Object.values(inv.checks)) {
|
|
1146
|
-
for (const check of checks) {
|
|
1147
|
-
if (check.score_policy === "manual") {
|
|
1148
|
-
result.push(check);
|
|
1149
|
-
}
|
|
1150
|
-
}
|
|
1151
|
-
}
|
|
1152
|
-
return result;
|
|
1153
|
-
}
|
|
1154
1306
|
function findThreatIntelBySource(inv, source) {
|
|
1155
1307
|
const normalizedSource = source.trim().toLowerCase();
|
|
1156
1308
|
return Object.values(inv.threat_intels).filter(
|
|
@@ -1193,13 +1345,32 @@ function findContainersAtLeast(inv, minLevel2) {
|
|
|
1193
1345
|
}
|
|
1194
1346
|
function getChecksForObservable(inv, observableKey) {
|
|
1195
1347
|
const result = [];
|
|
1348
|
+
const seen = /* @__PURE__ */ new Set();
|
|
1349
|
+
const checkLookup = /* @__PURE__ */ new Map();
|
|
1196
1350
|
for (const checks of Object.values(inv.checks)) {
|
|
1197
1351
|
for (const check of checks) {
|
|
1198
|
-
|
|
1352
|
+
checkLookup.set(check.key, check);
|
|
1353
|
+
}
|
|
1354
|
+
}
|
|
1355
|
+
const observable = inv.observables[observableKey];
|
|
1356
|
+
if (observable) {
|
|
1357
|
+
for (const checkKey of observable.check_links) {
|
|
1358
|
+
const check = checkLookup.get(checkKey);
|
|
1359
|
+
if (check && !seen.has(check.key)) {
|
|
1199
1360
|
result.push(check);
|
|
1361
|
+
seen.add(check.key);
|
|
1200
1362
|
}
|
|
1201
1363
|
}
|
|
1202
1364
|
}
|
|
1365
|
+
for (const check of checkLookup.values()) {
|
|
1366
|
+
if (seen.has(check.key)) {
|
|
1367
|
+
continue;
|
|
1368
|
+
}
|
|
1369
|
+
if (check.observable_links.some((link) => link.observable_key === observableKey)) {
|
|
1370
|
+
result.push(check);
|
|
1371
|
+
seen.add(check.key);
|
|
1372
|
+
}
|
|
1373
|
+
}
|
|
1203
1374
|
return result;
|
|
1204
1375
|
}
|
|
1205
1376
|
function getThreatIntelsForObservable(inv, observableKey) {
|
|
@@ -1215,7 +1386,11 @@ function getObservablesForCheck(inv, checkKey) {
|
|
|
1215
1386
|
for (const checks of Object.values(inv.checks)) {
|
|
1216
1387
|
for (const check of checks) {
|
|
1217
1388
|
if (check.key === checkKey) {
|
|
1218
|
-
|
|
1389
|
+
const keys = /* @__PURE__ */ new Set();
|
|
1390
|
+
for (const link of check.observable_links) {
|
|
1391
|
+
keys.add(link.observable_key);
|
|
1392
|
+
}
|
|
1393
|
+
return Array.from(keys).map((obsKey) => inv.observables[obsKey]).filter((obs) => obs !== void 0);
|
|
1219
1394
|
}
|
|
1220
1395
|
}
|
|
1221
1396
|
}
|
|
@@ -1587,7 +1762,6 @@ export {
|
|
|
1587
1762
|
findExternalObservables,
|
|
1588
1763
|
findInternalObservables,
|
|
1589
1764
|
findLeafObservables,
|
|
1590
|
-
findManuallyScored,
|
|
1591
1765
|
findObservablesAtLeast,
|
|
1592
1766
|
findObservablesByLevel,
|
|
1593
1767
|
findObservablesByType,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cyvest/cyvest-js",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "4.0.0",
|
|
4
4
|
"main": "dist/index.cjs",
|
|
5
5
|
"module": "dist/index.mjs",
|
|
6
6
|
"types": "dist/index.d.ts",
|
|
@@ -10,10 +10,10 @@
|
|
|
10
10
|
"ajv-formats": "^3.0.1"
|
|
11
11
|
},
|
|
12
12
|
"devDependencies": {
|
|
13
|
-
"json-schema-to-typescript": "^
|
|
14
|
-
"tsup": "^8.
|
|
15
|
-
"typescript": "^5.
|
|
16
|
-
"vitest": "^
|
|
13
|
+
"json-schema-to-typescript": "^15.0.4",
|
|
14
|
+
"tsup": "^8.5.1",
|
|
15
|
+
"typescript": "^5.9.3",
|
|
16
|
+
"vitest": "^4.0.16"
|
|
17
17
|
},
|
|
18
18
|
"engines": {
|
|
19
19
|
"node": ">=20"
|
package/src/finders.ts
CHANGED
|
@@ -288,24 +288,6 @@ export function findChecksByCheckId(
|
|
|
288
288
|
return result;
|
|
289
289
|
}
|
|
290
290
|
|
|
291
|
-
/**
|
|
292
|
-
* Find checks with score policy set to manual.
|
|
293
|
-
*
|
|
294
|
-
* @param inv - The investigation to search
|
|
295
|
-
* @returns Array of manually scored checks
|
|
296
|
-
*/
|
|
297
|
-
export function findManuallyScored(inv: CyvestInvestigation): Check[] {
|
|
298
|
-
const result: Check[] = [];
|
|
299
|
-
for (const checks of Object.values(inv.checks)) {
|
|
300
|
-
for (const check of checks) {
|
|
301
|
-
if (check.score_policy === "manual") {
|
|
302
|
-
result.push(check);
|
|
303
|
-
}
|
|
304
|
-
}
|
|
305
|
-
}
|
|
306
|
-
return result;
|
|
307
|
-
}
|
|
308
|
-
|
|
309
291
|
// ============================================================================
|
|
310
292
|
// Threat Intel Finders
|
|
311
293
|
// ============================================================================
|
|
@@ -434,15 +416,37 @@ export function getChecksForObservable(
|
|
|
434
416
|
observableKey: string
|
|
435
417
|
): Check[] {
|
|
436
418
|
const result: Check[] = [];
|
|
419
|
+
const seen = new Set<string>();
|
|
420
|
+
const checkLookup = new Map<string, Check>();
|
|
437
421
|
|
|
438
422
|
for (const checks of Object.values(inv.checks)) {
|
|
439
423
|
for (const check of checks) {
|
|
440
|
-
|
|
424
|
+
checkLookup.set(check.key, check);
|
|
425
|
+
}
|
|
426
|
+
}
|
|
427
|
+
|
|
428
|
+
const observable = inv.observables[observableKey];
|
|
429
|
+
if (observable) {
|
|
430
|
+
for (const checkKey of observable.check_links) {
|
|
431
|
+
const check = checkLookup.get(checkKey);
|
|
432
|
+
if (check && !seen.has(check.key)) {
|
|
441
433
|
result.push(check);
|
|
434
|
+
seen.add(check.key);
|
|
442
435
|
}
|
|
443
436
|
}
|
|
444
437
|
}
|
|
445
438
|
|
|
439
|
+
for (const check of checkLookup.values()) {
|
|
440
|
+
if (seen.has(check.key)) {
|
|
441
|
+
continue;
|
|
442
|
+
}
|
|
443
|
+
|
|
444
|
+
if (check.observable_links.some((link) => link.observable_key === observableKey)) {
|
|
445
|
+
result.push(check);
|
|
446
|
+
seen.add(check.key);
|
|
447
|
+
}
|
|
448
|
+
}
|
|
449
|
+
|
|
446
450
|
return result;
|
|
447
451
|
}
|
|
448
452
|
|
|
@@ -486,7 +490,12 @@ export function getObservablesForCheck(
|
|
|
486
490
|
for (const checks of Object.values(inv.checks)) {
|
|
487
491
|
for (const check of checks) {
|
|
488
492
|
if (check.key === checkKey) {
|
|
489
|
-
|
|
493
|
+
const keys = new Set<string>();
|
|
494
|
+
for (const link of check.observable_links) {
|
|
495
|
+
keys.add(link.observable_key);
|
|
496
|
+
}
|
|
497
|
+
|
|
498
|
+
return Array.from(keys)
|
|
490
499
|
.map((obsKey) => inv.observables[obsKey])
|
|
491
500
|
.filter((obs): obs is Observable => obs !== undefined);
|
|
492
501
|
}
|