@cyvest/cyvest-js 5.1.1 → 5.1.3
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.cjs +1 -1
- package/dist/index.d.cts +21 -21
- package/dist/index.d.ts +21 -21
- package/dist/index.js +1 -1
- package/package.json +1 -1
- package/src/finders.ts +3 -3
- package/src/getters.ts +5 -5
- package/src/graph.ts +1 -1
- package/src/helpers.ts +1 -1
- package/src/keys.ts +12 -12
- package/tests/getters-finders.test.ts +28 -28
- package/tests/graph.test.ts +52 -52
- package/tests/keys-levels.test.ts +16 -16
package/dist/index.cjs
CHANGED
|
@@ -141,7 +141,7 @@ __export(index_exports, {
|
|
|
141
141
|
module.exports = __toCommonJS(index_exports);
|
|
142
142
|
|
|
143
143
|
// src/helpers.ts
|
|
144
|
-
var import__ = __toESM(require("ajv/dist/2020"), 1);
|
|
144
|
+
var import__ = __toESM(require("ajv/dist/2020.js"), 1);
|
|
145
145
|
var import_ajv_formats = __toESM(require("ajv-formats"), 1);
|
|
146
146
|
|
|
147
147
|
// ../../../schema/cyvest.schema.json
|
package/dist/index.d.cts
CHANGED
|
@@ -348,8 +348,8 @@ type KeyType = "obs" | "chk" | "ti" | "enr" | "tag";
|
|
|
348
348
|
*
|
|
349
349
|
* @example
|
|
350
350
|
* ```ts
|
|
351
|
-
* generateObservableKey("ipv4
|
|
352
|
-
* // => "obs:ipv4
|
|
351
|
+
* generateObservableKey("ipv4", "192.168.1.1")
|
|
352
|
+
* // => "obs:ipv4:192.168.1.1"
|
|
353
353
|
* ```
|
|
354
354
|
*/
|
|
355
355
|
declare function generateObservableKey(obsType: string, value: string): string;
|
|
@@ -379,8 +379,8 @@ declare function generateCheckKey(checkName: string): string;
|
|
|
379
379
|
*
|
|
380
380
|
* @example
|
|
381
381
|
* ```ts
|
|
382
|
-
* generateThreatIntelKey("virustotal", "obs:ipv4
|
|
383
|
-
* // => "ti:virustotal:obs:ipv4
|
|
382
|
+
* generateThreatIntelKey("virustotal", "obs:ipv4:192.168.1.1")
|
|
383
|
+
* // => "ti:virustotal:obs:ipv4:192.168.1.1"
|
|
384
384
|
* ```
|
|
385
385
|
*/
|
|
386
386
|
declare function generateThreatIntelKey(source: string, observableKey: string): string;
|
|
@@ -467,7 +467,7 @@ declare function isTagDescendantOf(descendantName: string, ancestorName: string)
|
|
|
467
467
|
*
|
|
468
468
|
* @example
|
|
469
469
|
* ```ts
|
|
470
|
-
* parseKeyType("obs:ipv4
|
|
470
|
+
* parseKeyType("obs:ipv4:192.168.1.1") // => "obs"
|
|
471
471
|
* parseKeyType("invalid") // => null
|
|
472
472
|
* ```
|
|
473
473
|
*/
|
|
@@ -481,9 +481,9 @@ declare function parseKeyType(key: string): KeyType | null;
|
|
|
481
481
|
*
|
|
482
482
|
* @example
|
|
483
483
|
* ```ts
|
|
484
|
-
* validateKey("obs:ipv4
|
|
485
|
-
* validateKey("obs:ipv4
|
|
486
|
-
* validateKey("obs:ipv4
|
|
484
|
+
* validateKey("obs:ipv4:192.168.1.1") // => true
|
|
485
|
+
* validateKey("obs:ipv4:192.168.1.1", "obs") // => true
|
|
486
|
+
* validateKey("obs:ipv4:192.168.1.1", "chk") // => false
|
|
487
487
|
* validateKey("invalid") // => false
|
|
488
488
|
* ```
|
|
489
489
|
*/
|
|
@@ -496,8 +496,8 @@ declare function validateKey(key: string, expectedType?: KeyType): boolean;
|
|
|
496
496
|
*
|
|
497
497
|
* @example
|
|
498
498
|
* ```ts
|
|
499
|
-
* parseObservableKey("obs:ipv4
|
|
500
|
-
* // => { type: "ipv4
|
|
499
|
+
* parseObservableKey("obs:ipv4:192.168.1.1")
|
|
500
|
+
* // => { type: "ipv4", value: "192.168.1.1" }
|
|
501
501
|
* ```
|
|
502
502
|
*/
|
|
503
503
|
declare function parseObservableKey(key: string): {
|
|
@@ -527,8 +527,8 @@ declare function parseCheckKey(key: string): {
|
|
|
527
527
|
*
|
|
528
528
|
* @example
|
|
529
529
|
* ```ts
|
|
530
|
-
* parseThreatIntelKey("ti:virustotal:obs:ipv4
|
|
531
|
-
* // => { source: "virustotal", observableKey: "obs:ipv4
|
|
530
|
+
* parseThreatIntelKey("ti:virustotal:obs:ipv4:192.168.1.1")
|
|
531
|
+
* // => { source: "virustotal", observableKey: "obs:ipv4:192.168.1.1" }
|
|
532
532
|
* ```
|
|
533
533
|
*/
|
|
534
534
|
declare function parseThreatIntelKey(key: string): {
|
|
@@ -696,12 +696,12 @@ declare function getEntityLevel(entity: Observable | Check | ThreatIntel | Tag):
|
|
|
696
696
|
* Get an observable by its key.
|
|
697
697
|
*
|
|
698
698
|
* @param inv - The investigation to search
|
|
699
|
-
* @param key - Observable key (e.g., "obs:ipv4
|
|
699
|
+
* @param key - Observable key (e.g., "obs:ipv4:192.168.1.1")
|
|
700
700
|
* @returns The observable or undefined if not found
|
|
701
701
|
*
|
|
702
702
|
* @example
|
|
703
703
|
* ```ts
|
|
704
|
-
* const obs = getObservable(investigation, "obs:ipv4
|
|
704
|
+
* const obs = getObservable(investigation, "obs:ipv4:192.168.1.1");
|
|
705
705
|
* if (obs) {
|
|
706
706
|
* console.log(obs.value, obs.level);
|
|
707
707
|
* }
|
|
@@ -712,13 +712,13 @@ declare function getObservable(inv: CyvestInvestigation, key: string): Observabl
|
|
|
712
712
|
* Get an observable by type and value.
|
|
713
713
|
*
|
|
714
714
|
* @param inv - The investigation to search
|
|
715
|
-
* @param type - Observable type (e.g., "ipv4
|
|
715
|
+
* @param type - Observable type (e.g., "ipv4", "url")
|
|
716
716
|
* @param value - Observable value
|
|
717
717
|
* @returns The observable or undefined if not found
|
|
718
718
|
*
|
|
719
719
|
* @example
|
|
720
720
|
* ```ts
|
|
721
|
-
* const obs = getObservableByTypeValue(investigation, "ipv4
|
|
721
|
+
* const obs = getObservableByTypeValue(investigation, "ipv4", "192.168.1.1");
|
|
722
722
|
* ```
|
|
723
723
|
*/
|
|
724
724
|
declare function getObservableByTypeValue(inv: CyvestInvestigation, type: string, value: string): Observable | undefined;
|
|
@@ -783,7 +783,7 @@ declare function getAllChecks(inv: CyvestInvestigation): Check[];
|
|
|
783
783
|
* Get a threat intel entry by its key.
|
|
784
784
|
*
|
|
785
785
|
* @param inv - The investigation to search
|
|
786
|
-
* @param key - Threat intel key (e.g., "ti:virustotal:obs:ipv4
|
|
786
|
+
* @param key - Threat intel key (e.g., "ti:virustotal:obs:ipv4:192.168.1.1")
|
|
787
787
|
* @returns The threat intel or undefined if not found
|
|
788
788
|
*/
|
|
789
789
|
declare function getThreatIntel(inv: CyvestInvestigation, key: string): ThreatIntel | undefined;
|
|
@@ -1006,12 +1006,12 @@ declare function getTagAggregatedLevel(inv: CyvestInvestigation, tagName: string
|
|
|
1006
1006
|
* Find all observables of a specific type.
|
|
1007
1007
|
*
|
|
1008
1008
|
* @param inv - The investigation to search
|
|
1009
|
-
* @param type - Observable type (e.g., "ipv4
|
|
1009
|
+
* @param type - Observable type (e.g., "ipv4", "url", "domain")
|
|
1010
1010
|
* @returns Array of matching observables
|
|
1011
1011
|
*
|
|
1012
1012
|
* @example
|
|
1013
1013
|
* ```ts
|
|
1014
|
-
* const ips = findObservablesByType(investigation, "ipv4
|
|
1014
|
+
* const ips = findObservablesByType(investigation, "ipv4");
|
|
1015
1015
|
* const urls = findObservablesByType(investigation, "url");
|
|
1016
1016
|
* ```
|
|
1017
1017
|
*/
|
|
@@ -1178,7 +1178,7 @@ declare function findTagsByNamePattern(inv: CyvestInvestigation, pattern: RegExp
|
|
|
1178
1178
|
*
|
|
1179
1179
|
* @example
|
|
1180
1180
|
* ```ts
|
|
1181
|
-
* const checks = findChecksForObservable(investigation, "obs:ipv4
|
|
1181
|
+
* const checks = findChecksForObservable(investigation, "obs:ipv4:192.168.1.1");
|
|
1182
1182
|
* ```
|
|
1183
1183
|
*/
|
|
1184
1184
|
declare function findChecksForObservable(inv: CyvestInvestigation, observableKey: string): Check[];
|
|
@@ -1361,7 +1361,7 @@ interface InvestigationGraph {
|
|
|
1361
1361
|
*
|
|
1362
1362
|
* @example
|
|
1363
1363
|
* ```ts
|
|
1364
|
-
* const related = getRelatedObservables(investigation, "obs:email
|
|
1364
|
+
* const related = getRelatedObservables(investigation, "obs:email:test@example.com");
|
|
1365
1365
|
* ```
|
|
1366
1366
|
*/
|
|
1367
1367
|
declare function getRelatedObservables(inv: CyvestInvestigation, observableKey: string): Observable[];
|
package/dist/index.d.ts
CHANGED
|
@@ -348,8 +348,8 @@ type KeyType = "obs" | "chk" | "ti" | "enr" | "tag";
|
|
|
348
348
|
*
|
|
349
349
|
* @example
|
|
350
350
|
* ```ts
|
|
351
|
-
* generateObservableKey("ipv4
|
|
352
|
-
* // => "obs:ipv4
|
|
351
|
+
* generateObservableKey("ipv4", "192.168.1.1")
|
|
352
|
+
* // => "obs:ipv4:192.168.1.1"
|
|
353
353
|
* ```
|
|
354
354
|
*/
|
|
355
355
|
declare function generateObservableKey(obsType: string, value: string): string;
|
|
@@ -379,8 +379,8 @@ declare function generateCheckKey(checkName: string): string;
|
|
|
379
379
|
*
|
|
380
380
|
* @example
|
|
381
381
|
* ```ts
|
|
382
|
-
* generateThreatIntelKey("virustotal", "obs:ipv4
|
|
383
|
-
* // => "ti:virustotal:obs:ipv4
|
|
382
|
+
* generateThreatIntelKey("virustotal", "obs:ipv4:192.168.1.1")
|
|
383
|
+
* // => "ti:virustotal:obs:ipv4:192.168.1.1"
|
|
384
384
|
* ```
|
|
385
385
|
*/
|
|
386
386
|
declare function generateThreatIntelKey(source: string, observableKey: string): string;
|
|
@@ -467,7 +467,7 @@ declare function isTagDescendantOf(descendantName: string, ancestorName: string)
|
|
|
467
467
|
*
|
|
468
468
|
* @example
|
|
469
469
|
* ```ts
|
|
470
|
-
* parseKeyType("obs:ipv4
|
|
470
|
+
* parseKeyType("obs:ipv4:192.168.1.1") // => "obs"
|
|
471
471
|
* parseKeyType("invalid") // => null
|
|
472
472
|
* ```
|
|
473
473
|
*/
|
|
@@ -481,9 +481,9 @@ declare function parseKeyType(key: string): KeyType | null;
|
|
|
481
481
|
*
|
|
482
482
|
* @example
|
|
483
483
|
* ```ts
|
|
484
|
-
* validateKey("obs:ipv4
|
|
485
|
-
* validateKey("obs:ipv4
|
|
486
|
-
* validateKey("obs:ipv4
|
|
484
|
+
* validateKey("obs:ipv4:192.168.1.1") // => true
|
|
485
|
+
* validateKey("obs:ipv4:192.168.1.1", "obs") // => true
|
|
486
|
+
* validateKey("obs:ipv4:192.168.1.1", "chk") // => false
|
|
487
487
|
* validateKey("invalid") // => false
|
|
488
488
|
* ```
|
|
489
489
|
*/
|
|
@@ -496,8 +496,8 @@ declare function validateKey(key: string, expectedType?: KeyType): boolean;
|
|
|
496
496
|
*
|
|
497
497
|
* @example
|
|
498
498
|
* ```ts
|
|
499
|
-
* parseObservableKey("obs:ipv4
|
|
500
|
-
* // => { type: "ipv4
|
|
499
|
+
* parseObservableKey("obs:ipv4:192.168.1.1")
|
|
500
|
+
* // => { type: "ipv4", value: "192.168.1.1" }
|
|
501
501
|
* ```
|
|
502
502
|
*/
|
|
503
503
|
declare function parseObservableKey(key: string): {
|
|
@@ -527,8 +527,8 @@ declare function parseCheckKey(key: string): {
|
|
|
527
527
|
*
|
|
528
528
|
* @example
|
|
529
529
|
* ```ts
|
|
530
|
-
* parseThreatIntelKey("ti:virustotal:obs:ipv4
|
|
531
|
-
* // => { source: "virustotal", observableKey: "obs:ipv4
|
|
530
|
+
* parseThreatIntelKey("ti:virustotal:obs:ipv4:192.168.1.1")
|
|
531
|
+
* // => { source: "virustotal", observableKey: "obs:ipv4:192.168.1.1" }
|
|
532
532
|
* ```
|
|
533
533
|
*/
|
|
534
534
|
declare function parseThreatIntelKey(key: string): {
|
|
@@ -696,12 +696,12 @@ declare function getEntityLevel(entity: Observable | Check | ThreatIntel | Tag):
|
|
|
696
696
|
* Get an observable by its key.
|
|
697
697
|
*
|
|
698
698
|
* @param inv - The investigation to search
|
|
699
|
-
* @param key - Observable key (e.g., "obs:ipv4
|
|
699
|
+
* @param key - Observable key (e.g., "obs:ipv4:192.168.1.1")
|
|
700
700
|
* @returns The observable or undefined if not found
|
|
701
701
|
*
|
|
702
702
|
* @example
|
|
703
703
|
* ```ts
|
|
704
|
-
* const obs = getObservable(investigation, "obs:ipv4
|
|
704
|
+
* const obs = getObservable(investigation, "obs:ipv4:192.168.1.1");
|
|
705
705
|
* if (obs) {
|
|
706
706
|
* console.log(obs.value, obs.level);
|
|
707
707
|
* }
|
|
@@ -712,13 +712,13 @@ declare function getObservable(inv: CyvestInvestigation, key: string): Observabl
|
|
|
712
712
|
* Get an observable by type and value.
|
|
713
713
|
*
|
|
714
714
|
* @param inv - The investigation to search
|
|
715
|
-
* @param type - Observable type (e.g., "ipv4
|
|
715
|
+
* @param type - Observable type (e.g., "ipv4", "url")
|
|
716
716
|
* @param value - Observable value
|
|
717
717
|
* @returns The observable or undefined if not found
|
|
718
718
|
*
|
|
719
719
|
* @example
|
|
720
720
|
* ```ts
|
|
721
|
-
* const obs = getObservableByTypeValue(investigation, "ipv4
|
|
721
|
+
* const obs = getObservableByTypeValue(investigation, "ipv4", "192.168.1.1");
|
|
722
722
|
* ```
|
|
723
723
|
*/
|
|
724
724
|
declare function getObservableByTypeValue(inv: CyvestInvestigation, type: string, value: string): Observable | undefined;
|
|
@@ -783,7 +783,7 @@ declare function getAllChecks(inv: CyvestInvestigation): Check[];
|
|
|
783
783
|
* Get a threat intel entry by its key.
|
|
784
784
|
*
|
|
785
785
|
* @param inv - The investigation to search
|
|
786
|
-
* @param key - Threat intel key (e.g., "ti:virustotal:obs:ipv4
|
|
786
|
+
* @param key - Threat intel key (e.g., "ti:virustotal:obs:ipv4:192.168.1.1")
|
|
787
787
|
* @returns The threat intel or undefined if not found
|
|
788
788
|
*/
|
|
789
789
|
declare function getThreatIntel(inv: CyvestInvestigation, key: string): ThreatIntel | undefined;
|
|
@@ -1006,12 +1006,12 @@ declare function getTagAggregatedLevel(inv: CyvestInvestigation, tagName: string
|
|
|
1006
1006
|
* Find all observables of a specific type.
|
|
1007
1007
|
*
|
|
1008
1008
|
* @param inv - The investigation to search
|
|
1009
|
-
* @param type - Observable type (e.g., "ipv4
|
|
1009
|
+
* @param type - Observable type (e.g., "ipv4", "url", "domain")
|
|
1010
1010
|
* @returns Array of matching observables
|
|
1011
1011
|
*
|
|
1012
1012
|
* @example
|
|
1013
1013
|
* ```ts
|
|
1014
|
-
* const ips = findObservablesByType(investigation, "ipv4
|
|
1014
|
+
* const ips = findObservablesByType(investigation, "ipv4");
|
|
1015
1015
|
* const urls = findObservablesByType(investigation, "url");
|
|
1016
1016
|
* ```
|
|
1017
1017
|
*/
|
|
@@ -1178,7 +1178,7 @@ declare function findTagsByNamePattern(inv: CyvestInvestigation, pattern: RegExp
|
|
|
1178
1178
|
*
|
|
1179
1179
|
* @example
|
|
1180
1180
|
* ```ts
|
|
1181
|
-
* const checks = findChecksForObservable(investigation, "obs:ipv4
|
|
1181
|
+
* const checks = findChecksForObservable(investigation, "obs:ipv4:192.168.1.1");
|
|
1182
1182
|
* ```
|
|
1183
1183
|
*/
|
|
1184
1184
|
declare function findChecksForObservable(inv: CyvestInvestigation, observableKey: string): Check[];
|
|
@@ -1361,7 +1361,7 @@ interface InvestigationGraph {
|
|
|
1361
1361
|
*
|
|
1362
1362
|
* @example
|
|
1363
1363
|
* ```ts
|
|
1364
|
-
* const related = getRelatedObservables(investigation, "obs:email
|
|
1364
|
+
* const related = getRelatedObservables(investigation, "obs:email:test@example.com");
|
|
1365
1365
|
* ```
|
|
1366
1366
|
*/
|
|
1367
1367
|
declare function getRelatedObservables(inv: CyvestInvestigation, observableKey: string): Observable[];
|
package/dist/index.js
CHANGED
package/package.json
CHANGED
package/src/finders.ts
CHANGED
|
@@ -23,12 +23,12 @@ import { isLevelAtLeast, isLevelHigherThan, LEVEL_VALUES } from "./levels";
|
|
|
23
23
|
* Find all observables of a specific type.
|
|
24
24
|
*
|
|
25
25
|
* @param inv - The investigation to search
|
|
26
|
-
* @param type - Observable type (e.g., "ipv4
|
|
26
|
+
* @param type - Observable type (e.g., "ipv4", "url", "domain")
|
|
27
27
|
* @returns Array of matching observables
|
|
28
28
|
*
|
|
29
29
|
* @example
|
|
30
30
|
* ```ts
|
|
31
|
-
* const ips = findObservablesByType(investigation, "ipv4
|
|
31
|
+
* const ips = findObservablesByType(investigation, "ipv4");
|
|
32
32
|
* const urls = findObservablesByType(investigation, "url");
|
|
33
33
|
* ```
|
|
34
34
|
*/
|
|
@@ -346,7 +346,7 @@ export function findTagsByNamePattern(
|
|
|
346
346
|
*
|
|
347
347
|
* @example
|
|
348
348
|
* ```ts
|
|
349
|
-
* const checks = findChecksForObservable(investigation, "obs:ipv4
|
|
349
|
+
* const checks = findChecksForObservable(investigation, "obs:ipv4:192.168.1.1");
|
|
350
350
|
* ```
|
|
351
351
|
*/
|
|
352
352
|
export function findChecksForObservable(
|
package/src/getters.ts
CHANGED
|
@@ -21,12 +21,12 @@ import { getLevelFromScore } from "./levels";
|
|
|
21
21
|
* Get an observable by its key.
|
|
22
22
|
*
|
|
23
23
|
* @param inv - The investigation to search
|
|
24
|
-
* @param key - Observable key (e.g., "obs:ipv4
|
|
24
|
+
* @param key - Observable key (e.g., "obs:ipv4:192.168.1.1")
|
|
25
25
|
* @returns The observable or undefined if not found
|
|
26
26
|
*
|
|
27
27
|
* @example
|
|
28
28
|
* ```ts
|
|
29
|
-
* const obs = getObservable(investigation, "obs:ipv4
|
|
29
|
+
* const obs = getObservable(investigation, "obs:ipv4:192.168.1.1");
|
|
30
30
|
* if (obs) {
|
|
31
31
|
* console.log(obs.value, obs.level);
|
|
32
32
|
* }
|
|
@@ -43,13 +43,13 @@ export function getObservable(
|
|
|
43
43
|
* Get an observable by type and value.
|
|
44
44
|
*
|
|
45
45
|
* @param inv - The investigation to search
|
|
46
|
-
* @param type - Observable type (e.g., "ipv4
|
|
46
|
+
* @param type - Observable type (e.g., "ipv4", "url")
|
|
47
47
|
* @param value - Observable value
|
|
48
48
|
* @returns The observable or undefined if not found
|
|
49
49
|
*
|
|
50
50
|
* @example
|
|
51
51
|
* ```ts
|
|
52
|
-
* const obs = getObservableByTypeValue(investigation, "ipv4
|
|
52
|
+
* const obs = getObservableByTypeValue(investigation, "ipv4", "192.168.1.1");
|
|
53
53
|
* ```
|
|
54
54
|
*/
|
|
55
55
|
export function getObservableByTypeValue(
|
|
@@ -156,7 +156,7 @@ export function getAllChecks(inv: CyvestInvestigation): Check[] {
|
|
|
156
156
|
* Get a threat intel entry by its key.
|
|
157
157
|
*
|
|
158
158
|
* @param inv - The investigation to search
|
|
159
|
-
* @param key - Threat intel key (e.g., "ti:virustotal:obs:ipv4
|
|
159
|
+
* @param key - Threat intel key (e.g., "ti:virustotal:obs:ipv4:192.168.1.1")
|
|
160
160
|
* @returns The threat intel or undefined if not found
|
|
161
161
|
*/
|
|
162
162
|
export function getThreatIntel(
|
package/src/graph.ts
CHANGED
|
@@ -73,7 +73,7 @@ export interface InvestigationGraph {
|
|
|
73
73
|
*
|
|
74
74
|
* @example
|
|
75
75
|
* ```ts
|
|
76
|
-
* const related = getRelatedObservables(investigation, "obs:email
|
|
76
|
+
* const related = getRelatedObservables(investigation, "obs:email:test@example.com");
|
|
77
77
|
* ```
|
|
78
78
|
*/
|
|
79
79
|
export function getRelatedObservables(
|
package/src/helpers.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import Ajv2020, { type ValidateFunction } from "ajv/dist/2020";
|
|
1
|
+
import Ajv2020, { type ValidateFunction } from "ajv/dist/2020.js";
|
|
2
2
|
import addFormats from "ajv-formats";
|
|
3
3
|
import schema from "../../../../schema/cyvest.schema.json" assert { type: "json" };
|
|
4
4
|
import type { CyvestInvestigation } from "./types.generated";
|
package/src/keys.ts
CHANGED
|
@@ -45,8 +45,8 @@ function hashString(content: string, length: number = 16): string {
|
|
|
45
45
|
*
|
|
46
46
|
* @example
|
|
47
47
|
* ```ts
|
|
48
|
-
* generateObservableKey("ipv4
|
|
49
|
-
* // => "obs:ipv4
|
|
48
|
+
* generateObservableKey("ipv4", "192.168.1.1")
|
|
49
|
+
* // => "obs:ipv4:192.168.1.1"
|
|
50
50
|
* ```
|
|
51
51
|
*/
|
|
52
52
|
export function generateObservableKey(obsType: string, value: string): string {
|
|
@@ -85,8 +85,8 @@ export function generateCheckKey(checkName: string): string {
|
|
|
85
85
|
*
|
|
86
86
|
* @example
|
|
87
87
|
* ```ts
|
|
88
|
-
* generateThreatIntelKey("virustotal", "obs:ipv4
|
|
89
|
-
* // => "ti:virustotal:obs:ipv4
|
|
88
|
+
* generateThreatIntelKey("virustotal", "obs:ipv4:192.168.1.1")
|
|
89
|
+
* // => "ti:virustotal:obs:ipv4:192.168.1.1"
|
|
90
90
|
* ```
|
|
91
91
|
*/
|
|
92
92
|
export function generateThreatIntelKey(
|
|
@@ -210,7 +210,7 @@ export function isTagDescendantOf(descendantName: string, ancestorName: string):
|
|
|
210
210
|
*
|
|
211
211
|
* @example
|
|
212
212
|
* ```ts
|
|
213
|
-
* parseKeyType("obs:ipv4
|
|
213
|
+
* parseKeyType("obs:ipv4:192.168.1.1") // => "obs"
|
|
214
214
|
* parseKeyType("invalid") // => null
|
|
215
215
|
* ```
|
|
216
216
|
*/
|
|
@@ -233,9 +233,9 @@ export function parseKeyType(key: string): KeyType | null {
|
|
|
233
233
|
*
|
|
234
234
|
* @example
|
|
235
235
|
* ```ts
|
|
236
|
-
* validateKey("obs:ipv4
|
|
237
|
-
* validateKey("obs:ipv4
|
|
238
|
-
* validateKey("obs:ipv4
|
|
236
|
+
* validateKey("obs:ipv4:192.168.1.1") // => true
|
|
237
|
+
* validateKey("obs:ipv4:192.168.1.1", "obs") // => true
|
|
238
|
+
* validateKey("obs:ipv4:192.168.1.1", "chk") // => false
|
|
239
239
|
* validateKey("invalid") // => false
|
|
240
240
|
* ```
|
|
241
241
|
*/
|
|
@@ -264,8 +264,8 @@ export function validateKey(key: string, expectedType?: KeyType): boolean {
|
|
|
264
264
|
*
|
|
265
265
|
* @example
|
|
266
266
|
* ```ts
|
|
267
|
-
* parseObservableKey("obs:ipv4
|
|
268
|
-
* // => { type: "ipv4
|
|
267
|
+
* parseObservableKey("obs:ipv4:192.168.1.1")
|
|
268
|
+
* // => { type: "ipv4", value: "192.168.1.1" }
|
|
269
269
|
* ```
|
|
270
270
|
*/
|
|
271
271
|
export function parseObservableKey(
|
|
@@ -319,8 +319,8 @@ export function parseCheckKey(
|
|
|
319
319
|
*
|
|
320
320
|
* @example
|
|
321
321
|
* ```ts
|
|
322
|
-
* parseThreatIntelKey("ti:virustotal:obs:ipv4
|
|
323
|
-
* // => { source: "virustotal", observableKey: "obs:ipv4
|
|
322
|
+
* parseThreatIntelKey("ti:virustotal:obs:ipv4:192.168.1.1")
|
|
323
|
+
* // => { source: "virustotal", observableKey: "obs:ipv4:192.168.1.1" }
|
|
324
324
|
* ```
|
|
325
325
|
*/
|
|
326
326
|
export function parseThreatIntelKey(
|
|
@@ -68,9 +68,9 @@ function createTestInvestigation(): CyvestInvestigation {
|
|
|
68
68
|
},
|
|
69
69
|
],
|
|
70
70
|
observables: {
|
|
71
|
-
"obs:ipv4
|
|
72
|
-
key: "obs:ipv4
|
|
73
|
-
type: "ipv4
|
|
71
|
+
"obs:ipv4:192.168.1.1": {
|
|
72
|
+
key: "obs:ipv4:192.168.1.1",
|
|
73
|
+
type: "ipv4",
|
|
74
74
|
value: "192.168.1.1",
|
|
75
75
|
internal: true,
|
|
76
76
|
whitelisted: false,
|
|
@@ -81,7 +81,7 @@ function createTestInvestigation(): CyvestInvestigation {
|
|
|
81
81
|
level: "INFO",
|
|
82
82
|
relationships: [
|
|
83
83
|
{
|
|
84
|
-
target_key: "obs:domain
|
|
84
|
+
target_key: "obs:domain:example.com",
|
|
85
85
|
relationship_type: "related-to",
|
|
86
86
|
direction: "outbound",
|
|
87
87
|
},
|
|
@@ -89,9 +89,9 @@ function createTestInvestigation(): CyvestInvestigation {
|
|
|
89
89
|
threat_intels: [],
|
|
90
90
|
check_links: ["chk:ip_check:network"],
|
|
91
91
|
},
|
|
92
|
-
"obs:ipv4
|
|
93
|
-
key: "obs:ipv4
|
|
94
|
-
type: "ipv4
|
|
92
|
+
"obs:ipv4:8.8.8.8": {
|
|
93
|
+
key: "obs:ipv4:8.8.8.8",
|
|
94
|
+
type: "ipv4",
|
|
95
95
|
value: "8.8.8.8",
|
|
96
96
|
internal: false,
|
|
97
97
|
whitelisted: true,
|
|
@@ -104,9 +104,9 @@ function createTestInvestigation(): CyvestInvestigation {
|
|
|
104
104
|
threat_intels: [],
|
|
105
105
|
check_links: [],
|
|
106
106
|
},
|
|
107
|
-
"obs:domain
|
|
108
|
-
key: "obs:domain
|
|
109
|
-
type: "domain
|
|
107
|
+
"obs:domain:example.com": {
|
|
108
|
+
key: "obs:domain:example.com",
|
|
109
|
+
type: "domain",
|
|
110
110
|
value: "example.com",
|
|
111
111
|
internal: false,
|
|
112
112
|
whitelisted: false,
|
|
@@ -116,7 +116,7 @@ function createTestInvestigation(): CyvestInvestigation {
|
|
|
116
116
|
score_display: "5.00",
|
|
117
117
|
level: "MALICIOUS",
|
|
118
118
|
relationships: [],
|
|
119
|
-
threat_intels: ["ti:virustotal:obs:domain
|
|
119
|
+
threat_intels: ["ti:virustotal:obs:domain:example.com"],
|
|
120
120
|
check_links: ["chk:domain_check:dns"],
|
|
121
121
|
},
|
|
122
122
|
"obs:url:http://malware.com/bad": {
|
|
@@ -148,7 +148,7 @@ function createTestInvestigation(): CyvestInvestigation {
|
|
|
148
148
|
origin_investigation_id: "01HXYZTESTINVESTIGATION",
|
|
149
149
|
observable_links: [
|
|
150
150
|
{
|
|
151
|
-
observable_key: "obs:ipv4
|
|
151
|
+
observable_key: "obs:ipv4:192.168.1.1",
|
|
152
152
|
},
|
|
153
153
|
],
|
|
154
154
|
},
|
|
@@ -164,7 +164,7 @@ function createTestInvestigation(): CyvestInvestigation {
|
|
|
164
164
|
origin_investigation_id: "01HXYZTESTINVESTIGATION",
|
|
165
165
|
observable_links: [
|
|
166
166
|
{
|
|
167
|
-
observable_key: "obs:domain
|
|
167
|
+
observable_key: "obs:domain:example.com",
|
|
168
168
|
},
|
|
169
169
|
],
|
|
170
170
|
},
|
|
@@ -182,10 +182,10 @@ function createTestInvestigation(): CyvestInvestigation {
|
|
|
182
182
|
},
|
|
183
183
|
},
|
|
184
184
|
threat_intels: {
|
|
185
|
-
"ti:virustotal:obs:domain
|
|
186
|
-
key: "ti:virustotal:obs:domain
|
|
185
|
+
"ti:virustotal:obs:domain:example.com": {
|
|
186
|
+
key: "ti:virustotal:obs:domain:example.com",
|
|
187
187
|
source: "virustotal",
|
|
188
|
-
observable_key: "obs:domain
|
|
188
|
+
observable_key: "obs:domain:example.com",
|
|
189
189
|
comment: "",
|
|
190
190
|
extra: {},
|
|
191
191
|
score: 5,
|
|
@@ -241,7 +241,7 @@ function createTestInvestigation(): CyvestInvestigation {
|
|
|
241
241
|
internal_observables: 1,
|
|
242
242
|
external_observables: 3,
|
|
243
243
|
whitelisted_observables: 1,
|
|
244
|
-
observables_by_type: { "ipv4
|
|
244
|
+
observables_by_type: { "ipv4": 2, "domain": 1, url: 1 },
|
|
245
245
|
observables_by_level: { INFO: 1, TRUSTED: 1, MALICIOUS: 2 },
|
|
246
246
|
observables_by_type_and_level: {},
|
|
247
247
|
total_checks: 3,
|
|
@@ -268,7 +268,7 @@ describe("Getters", () => {
|
|
|
268
268
|
|
|
269
269
|
describe("getObservable", () => {
|
|
270
270
|
it("returns observable by key", () => {
|
|
271
|
-
const obs = getObservable(inv, "obs:ipv4
|
|
271
|
+
const obs = getObservable(inv, "obs:ipv4:192.168.1.1");
|
|
272
272
|
expect(obs).toBeDefined();
|
|
273
273
|
expect(obs?.value).toBe("192.168.1.1");
|
|
274
274
|
});
|
|
@@ -280,13 +280,13 @@ describe("Getters", () => {
|
|
|
280
280
|
|
|
281
281
|
describe("getObservableByTypeValue", () => {
|
|
282
282
|
it("finds observable by type and value", () => {
|
|
283
|
-
const obs = getObservableByTypeValue(inv, "ipv4
|
|
283
|
+
const obs = getObservableByTypeValue(inv, "ipv4", "192.168.1.1");
|
|
284
284
|
expect(obs).toBeDefined();
|
|
285
|
-
expect(obs?.key).toBe("obs:ipv4
|
|
285
|
+
expect(obs?.key).toBe("obs:ipv4:192.168.1.1");
|
|
286
286
|
});
|
|
287
287
|
|
|
288
288
|
it("is case insensitive", () => {
|
|
289
|
-
const obs = getObservableByTypeValue(inv, "IPV4
|
|
289
|
+
const obs = getObservableByTypeValue(inv, "IPV4", "192.168.1.1");
|
|
290
290
|
expect(obs).toBeDefined();
|
|
291
291
|
});
|
|
292
292
|
});
|
|
@@ -318,7 +318,7 @@ describe("Getters", () => {
|
|
|
318
318
|
it("returns threat intel by key", () => {
|
|
319
319
|
const ti = getThreatIntel(
|
|
320
320
|
inv,
|
|
321
|
-
"ti:virustotal:obs:domain
|
|
321
|
+
"ti:virustotal:obs:domain:example.com"
|
|
322
322
|
);
|
|
323
323
|
expect(ti).toBeDefined();
|
|
324
324
|
expect(ti?.source).toBe("virustotal");
|
|
@@ -387,12 +387,12 @@ describe("Finders", () => {
|
|
|
387
387
|
|
|
388
388
|
describe("findObservablesByType", () => {
|
|
389
389
|
it("finds observables of specific type", () => {
|
|
390
|
-
const ips = findObservablesByType(inv, "ipv4
|
|
390
|
+
const ips = findObservablesByType(inv, "ipv4");
|
|
391
391
|
expect(ips).toHaveLength(2);
|
|
392
392
|
});
|
|
393
393
|
|
|
394
394
|
it("is case insensitive", () => {
|
|
395
|
-
const ips = findObservablesByType(inv, "IPV4
|
|
395
|
+
const ips = findObservablesByType(inv, "IPV4");
|
|
396
396
|
expect(ips).toHaveLength(2);
|
|
397
397
|
});
|
|
398
398
|
});
|
|
@@ -443,7 +443,7 @@ describe("Finders", () => {
|
|
|
443
443
|
|
|
444
444
|
describe("findChecksForObservable", () => {
|
|
445
445
|
it("finds checks that reference observable", () => {
|
|
446
|
-
const checks = findChecksForObservable(inv, "obs:ipv4
|
|
446
|
+
const checks = findChecksForObservable(inv, "obs:ipv4:192.168.1.1");
|
|
447
447
|
expect(checks).toHaveLength(1);
|
|
448
448
|
expect(checks[0].check_name).toBe("ip_check");
|
|
449
449
|
});
|
|
@@ -453,7 +453,7 @@ describe("Finders", () => {
|
|
|
453
453
|
it("finds threat intel for observable", () => {
|
|
454
454
|
const tis = findThreatIntelsForObservable(
|
|
455
455
|
inv,
|
|
456
|
-
"obs:domain
|
|
456
|
+
"obs:domain:example.com"
|
|
457
457
|
);
|
|
458
458
|
expect(tis).toHaveLength(1);
|
|
459
459
|
expect(tis[0].source).toBe("virustotal");
|
|
@@ -494,8 +494,8 @@ describe("Finders", () => {
|
|
|
494
494
|
describe("getAllObservableTypes", () => {
|
|
495
495
|
it("returns all observable types", () => {
|
|
496
496
|
const types = getAllObservableTypes(inv);
|
|
497
|
-
expect(types).toContain("ipv4
|
|
498
|
-
expect(types).toContain("domain
|
|
497
|
+
expect(types).toContain("ipv4");
|
|
498
|
+
expect(types).toContain("domain");
|
|
499
499
|
expect(types).toContain("url");
|
|
500
500
|
});
|
|
501
501
|
});
|
package/tests/graph.test.ts
CHANGED
|
@@ -37,9 +37,9 @@ function createGraphTestInvestigation(): CyvestInvestigation {
|
|
|
37
37
|
],
|
|
38
38
|
whitelists: [],
|
|
39
39
|
observables: {
|
|
40
|
-
"obs:
|
|
41
|
-
key: "obs:
|
|
42
|
-
type: "
|
|
40
|
+
"obs:file:msg1": {
|
|
41
|
+
key: "obs:file:msg1",
|
|
42
|
+
type: "file",
|
|
43
43
|
value: "msg1",
|
|
44
44
|
internal: false,
|
|
45
45
|
whitelisted: false,
|
|
@@ -50,12 +50,12 @@ function createGraphTestInvestigation(): CyvestInvestigation {
|
|
|
50
50
|
level: "INFO",
|
|
51
51
|
relationships: [
|
|
52
52
|
{
|
|
53
|
-
target_key: "obs:email
|
|
53
|
+
target_key: "obs:email:sender@example.com",
|
|
54
54
|
relationship_type: "from",
|
|
55
55
|
direction: "outbound",
|
|
56
56
|
},
|
|
57
57
|
{
|
|
58
|
-
target_key: "obs:ipv4
|
|
58
|
+
target_key: "obs:ipv4:192.168.1.1",
|
|
59
59
|
relationship_type: "originated-from",
|
|
60
60
|
direction: "outbound",
|
|
61
61
|
},
|
|
@@ -63,9 +63,9 @@ function createGraphTestInvestigation(): CyvestInvestigation {
|
|
|
63
63
|
threat_intels: [],
|
|
64
64
|
check_links: [],
|
|
65
65
|
},
|
|
66
|
-
"obs:email
|
|
67
|
-
key: "obs:email
|
|
68
|
-
type: "email
|
|
66
|
+
"obs:email:sender@example.com": {
|
|
67
|
+
key: "obs:email:sender@example.com",
|
|
68
|
+
type: "email",
|
|
69
69
|
value: "sender@example.com",
|
|
70
70
|
internal: false,
|
|
71
71
|
whitelisted: false,
|
|
@@ -76,7 +76,7 @@ function createGraphTestInvestigation(): CyvestInvestigation {
|
|
|
76
76
|
level: "INFO",
|
|
77
77
|
relationships: [
|
|
78
78
|
{
|
|
79
|
-
target_key: "obs:domain
|
|
79
|
+
target_key: "obs:domain:example.com",
|
|
80
80
|
relationship_type: "related-to",
|
|
81
81
|
direction: "outbound",
|
|
82
82
|
},
|
|
@@ -84,9 +84,9 @@ function createGraphTestInvestigation(): CyvestInvestigation {
|
|
|
84
84
|
threat_intels: [],
|
|
85
85
|
check_links: [],
|
|
86
86
|
},
|
|
87
|
-
"obs:ipv4
|
|
88
|
-
key: "obs:ipv4
|
|
89
|
-
type: "ipv4
|
|
87
|
+
"obs:ipv4:192.168.1.1": {
|
|
88
|
+
key: "obs:ipv4:192.168.1.1",
|
|
89
|
+
type: "ipv4",
|
|
90
90
|
value: "192.168.1.1",
|
|
91
91
|
internal: true,
|
|
92
92
|
whitelisted: false,
|
|
@@ -99,9 +99,9 @@ function createGraphTestInvestigation(): CyvestInvestigation {
|
|
|
99
99
|
threat_intels: [],
|
|
100
100
|
check_links: [],
|
|
101
101
|
},
|
|
102
|
-
"obs:domain
|
|
103
|
-
key: "obs:domain
|
|
104
|
-
type: "domain
|
|
102
|
+
"obs:domain:example.com": {
|
|
103
|
+
key: "obs:domain:example.com",
|
|
104
|
+
type: "domain",
|
|
105
105
|
value: "example.com",
|
|
106
106
|
internal: false,
|
|
107
107
|
whitelisted: false,
|
|
@@ -114,9 +114,9 @@ function createGraphTestInvestigation(): CyvestInvestigation {
|
|
|
114
114
|
threat_intels: [],
|
|
115
115
|
check_links: [],
|
|
116
116
|
},
|
|
117
|
-
"obs:
|
|
118
|
-
key: "obs:
|
|
119
|
-
type: "
|
|
117
|
+
"obs:hash:abc123": {
|
|
118
|
+
key: "obs:hash:abc123",
|
|
119
|
+
type: "hash",
|
|
120
120
|
value: "abc123",
|
|
121
121
|
internal: false,
|
|
122
122
|
whitelisted: false,
|
|
@@ -166,7 +166,7 @@ describe("Graph Traversal", () => {
|
|
|
166
166
|
|
|
167
167
|
describe("getRelatedObservables", () => {
|
|
168
168
|
it("returns all directly connected observables", () => {
|
|
169
|
-
const related = getRelatedObservables(inv, "obs:
|
|
169
|
+
const related = getRelatedObservables(inv, "obs:file:msg1");
|
|
170
170
|
expect(related).toHaveLength(2);
|
|
171
171
|
const values = related.map((o) => o.value);
|
|
172
172
|
expect(values).toContain("sender@example.com");
|
|
@@ -180,7 +180,7 @@ describe("Graph Traversal", () => {
|
|
|
180
180
|
it("includes inbound relationships", () => {
|
|
181
181
|
const related = getRelatedObservables(
|
|
182
182
|
inv,
|
|
183
|
-
"obs:email
|
|
183
|
+
"obs:email:sender@example.com"
|
|
184
184
|
);
|
|
185
185
|
// Has outbound to domain and inbound from email message
|
|
186
186
|
expect(related.length).toBeGreaterThanOrEqual(2);
|
|
@@ -189,12 +189,12 @@ describe("Graph Traversal", () => {
|
|
|
189
189
|
|
|
190
190
|
describe("getObservableChildren", () => {
|
|
191
191
|
it("returns outbound related observables", () => {
|
|
192
|
-
const children = getObservableChildren(inv, "obs:
|
|
192
|
+
const children = getObservableChildren(inv, "obs:file:msg1");
|
|
193
193
|
expect(children).toHaveLength(2);
|
|
194
194
|
});
|
|
195
195
|
|
|
196
196
|
it("returns empty for leaf nodes", () => {
|
|
197
|
-
const children = getObservableChildren(inv, "obs:ipv4
|
|
197
|
+
const children = getObservableChildren(inv, "obs:ipv4:192.168.1.1");
|
|
198
198
|
expect(children).toHaveLength(0);
|
|
199
199
|
});
|
|
200
200
|
});
|
|
@@ -203,14 +203,14 @@ describe("Graph Traversal", () => {
|
|
|
203
203
|
it("returns observables pointing to this one", () => {
|
|
204
204
|
const parents = getObservableParents(
|
|
205
205
|
inv,
|
|
206
|
-
"obs:email
|
|
206
|
+
"obs:email:sender@example.com"
|
|
207
207
|
);
|
|
208
208
|
expect(parents).toHaveLength(1);
|
|
209
209
|
expect(parents[0].value).toBe("msg1");
|
|
210
210
|
});
|
|
211
211
|
|
|
212
212
|
it("returns empty for root nodes", () => {
|
|
213
|
-
const parents = getObservableParents(inv, "obs:
|
|
213
|
+
const parents = getObservableParents(inv, "obs:file:msg1");
|
|
214
214
|
expect(parents).toHaveLength(0);
|
|
215
215
|
});
|
|
216
216
|
});
|
|
@@ -219,7 +219,7 @@ describe("Graph Traversal", () => {
|
|
|
219
219
|
it("filters by relationship type", () => {
|
|
220
220
|
const fromRelated = getRelatedObservablesByType(
|
|
221
221
|
inv,
|
|
222
|
-
"obs:
|
|
222
|
+
"obs:file:msg1",
|
|
223
223
|
"from"
|
|
224
224
|
);
|
|
225
225
|
expect(fromRelated).toHaveLength(1);
|
|
@@ -241,10 +241,10 @@ describe("Graph Traversal", () => {
|
|
|
241
241
|
it("nodes have correct structure", () => {
|
|
242
242
|
const graph = getObservableGraph(inv);
|
|
243
243
|
const emailNode = graph.nodes.find(
|
|
244
|
-
(n) => n.id === "obs:
|
|
244
|
+
(n) => n.id === "obs:file:msg1"
|
|
245
245
|
);
|
|
246
246
|
expect(emailNode).toBeDefined();
|
|
247
|
-
expect(emailNode?.type).toBe("
|
|
247
|
+
expect(emailNode?.type).toBe("file");
|
|
248
248
|
expect(emailNode?.value).toBe("msg1");
|
|
249
249
|
expect(emailNode?.level).toBe("INFO");
|
|
250
250
|
});
|
|
@@ -253,15 +253,15 @@ describe("Graph Traversal", () => {
|
|
|
253
253
|
const graph = getObservableGraph(inv);
|
|
254
254
|
const fromEdge = graph.edges.find((e) => e.type === "from");
|
|
255
255
|
expect(fromEdge).toBeDefined();
|
|
256
|
-
expect(fromEdge?.source).toBe("obs:
|
|
257
|
-
expect(fromEdge?.target).toBe("obs:email
|
|
256
|
+
expect(fromEdge?.source).toBe("obs:file:msg1");
|
|
257
|
+
expect(fromEdge?.target).toBe("obs:email:sender@example.com");
|
|
258
258
|
});
|
|
259
259
|
});
|
|
260
260
|
|
|
261
261
|
describe("findSourceObservables", () => {
|
|
262
262
|
it("finds observables with no incoming relationships", () => {
|
|
263
263
|
const sources = findSourceObservables(inv);
|
|
264
|
-
//
|
|
264
|
+
// file and hash are sources
|
|
265
265
|
expect(sources.length).toBeGreaterThanOrEqual(2);
|
|
266
266
|
const values = sources.map((o) => o.value);
|
|
267
267
|
expect(values).toContain("msg1");
|
|
@@ -272,7 +272,7 @@ describe("Graph Traversal", () => {
|
|
|
272
272
|
describe("findOrphanObservables", () => {
|
|
273
273
|
it("finds observables with no relationships", () => {
|
|
274
274
|
const orphans = findOrphanObservables(inv);
|
|
275
|
-
//
|
|
275
|
+
// hash has no relationships
|
|
276
276
|
expect(orphans).toHaveLength(1);
|
|
277
277
|
expect(orphans[0].value).toBe("abc123");
|
|
278
278
|
});
|
|
@@ -290,7 +290,7 @@ describe("Graph Traversal", () => {
|
|
|
290
290
|
describe("areConnected", () => {
|
|
291
291
|
it("returns true for directly connected", () => {
|
|
292
292
|
expect(
|
|
293
|
-
areConnected(inv, "obs:
|
|
293
|
+
areConnected(inv, "obs:file:msg1", "obs:ipv4:192.168.1.1")
|
|
294
294
|
).toBe(true);
|
|
295
295
|
});
|
|
296
296
|
|
|
@@ -298,21 +298,21 @@ describe("Graph Traversal", () => {
|
|
|
298
298
|
expect(
|
|
299
299
|
areConnected(
|
|
300
300
|
inv,
|
|
301
|
-
"obs:
|
|
302
|
-
"obs:domain
|
|
301
|
+
"obs:file:msg1",
|
|
302
|
+
"obs:domain:example.com"
|
|
303
303
|
)
|
|
304
304
|
).toBe(true);
|
|
305
305
|
});
|
|
306
306
|
|
|
307
307
|
it("returns false for disconnected", () => {
|
|
308
308
|
expect(
|
|
309
|
-
areConnected(inv, "obs:
|
|
309
|
+
areConnected(inv, "obs:hash:abc123", "obs:file:msg1")
|
|
310
310
|
).toBe(false);
|
|
311
311
|
});
|
|
312
312
|
|
|
313
313
|
it("returns true for same node", () => {
|
|
314
314
|
expect(
|
|
315
|
-
areConnected(inv, "obs:
|
|
315
|
+
areConnected(inv, "obs:file:msg1", "obs:file:msg1")
|
|
316
316
|
).toBe(true);
|
|
317
317
|
});
|
|
318
318
|
});
|
|
@@ -321,32 +321,32 @@ describe("Graph Traversal", () => {
|
|
|
321
321
|
it("finds direct path", () => {
|
|
322
322
|
const path = findPath(
|
|
323
323
|
inv,
|
|
324
|
-
"obs:
|
|
325
|
-
"obs:ipv4
|
|
324
|
+
"obs:file:msg1",
|
|
325
|
+
"obs:ipv4:192.168.1.1"
|
|
326
326
|
);
|
|
327
327
|
expect(path).toEqual([
|
|
328
|
-
"obs:
|
|
329
|
-
"obs:ipv4
|
|
328
|
+
"obs:file:msg1",
|
|
329
|
+
"obs:ipv4:192.168.1.1",
|
|
330
330
|
]);
|
|
331
331
|
});
|
|
332
332
|
|
|
333
333
|
it("finds transitive path", () => {
|
|
334
334
|
const path = findPath(
|
|
335
335
|
inv,
|
|
336
|
-
"obs:
|
|
337
|
-
"obs:domain
|
|
336
|
+
"obs:file:msg1",
|
|
337
|
+
"obs:domain:example.com"
|
|
338
338
|
);
|
|
339
339
|
expect(path).not.toBeNull();
|
|
340
340
|
expect(path?.length).toBe(3);
|
|
341
|
-
expect(path?.[0]).toBe("obs:
|
|
342
|
-
expect(path?.[path.length - 1]).toBe("obs:domain
|
|
341
|
+
expect(path?.[0]).toBe("obs:file:msg1");
|
|
342
|
+
expect(path?.[path.length - 1]).toBe("obs:domain:example.com");
|
|
343
343
|
});
|
|
344
344
|
|
|
345
345
|
it("returns null for no path", () => {
|
|
346
346
|
const path = findPath(
|
|
347
347
|
inv,
|
|
348
|
-
"obs:
|
|
349
|
-
"obs:
|
|
348
|
+
"obs:hash:abc123",
|
|
349
|
+
"obs:file:msg1"
|
|
350
350
|
);
|
|
351
351
|
expect(path).toBeNull();
|
|
352
352
|
});
|
|
@@ -354,23 +354,23 @@ describe("Graph Traversal", () => {
|
|
|
354
354
|
it("returns single node for same source/target", () => {
|
|
355
355
|
const path = findPath(
|
|
356
356
|
inv,
|
|
357
|
-
"obs:
|
|
358
|
-
"obs:
|
|
357
|
+
"obs:file:msg1",
|
|
358
|
+
"obs:file:msg1"
|
|
359
359
|
);
|
|
360
|
-
expect(path).toEqual(["obs:
|
|
360
|
+
expect(path).toEqual(["obs:file:msg1"]);
|
|
361
361
|
});
|
|
362
362
|
});
|
|
363
363
|
|
|
364
364
|
describe("getReachableObservables", () => {
|
|
365
365
|
it("returns all reachable from start", () => {
|
|
366
|
-
const reachable = getReachableObservables(inv, "obs:
|
|
366
|
+
const reachable = getReachableObservables(inv, "obs:file:msg1");
|
|
367
367
|
expect(reachable).toHaveLength(4); // msg1 + sender + ip + domain
|
|
368
368
|
});
|
|
369
369
|
|
|
370
370
|
it("respects max depth", () => {
|
|
371
371
|
const reachable = getReachableObservables(
|
|
372
372
|
inv,
|
|
373
|
-
"obs:
|
|
373
|
+
"obs:file:msg1",
|
|
374
374
|
1
|
|
375
375
|
);
|
|
376
376
|
expect(reachable).toHaveLength(3); // msg1 + sender + ip (depth 1)
|
|
@@ -399,7 +399,7 @@ describe("Graph Traversal", () => {
|
|
|
399
399
|
it("returns outbound and inbound relationships", () => {
|
|
400
400
|
const rels = getRelationshipsForObservable(
|
|
401
401
|
inv,
|
|
402
|
-
"obs:email
|
|
402
|
+
"obs:email:sender@example.com"
|
|
403
403
|
);
|
|
404
404
|
expect(rels.outbound).toHaveLength(1);
|
|
405
405
|
expect(rels.inbound).toHaveLength(1);
|
|
@@ -27,20 +27,20 @@ import {
|
|
|
27
27
|
describe("Key Generation", () => {
|
|
28
28
|
describe("generateObservableKey", () => {
|
|
29
29
|
it("generates correct observable key", () => {
|
|
30
|
-
expect(generateObservableKey("ipv4
|
|
31
|
-
"obs:ipv4
|
|
30
|
+
expect(generateObservableKey("ipv4", "192.168.1.1")).toBe(
|
|
31
|
+
"obs:ipv4:192.168.1.1"
|
|
32
32
|
);
|
|
33
33
|
});
|
|
34
34
|
|
|
35
35
|
it("normalizes to lowercase", () => {
|
|
36
|
-
expect(generateObservableKey("IPV4
|
|
37
|
-
"obs:ipv4
|
|
36
|
+
expect(generateObservableKey("IPV4", "192.168.1.1")).toBe(
|
|
37
|
+
"obs:ipv4:192.168.1.1"
|
|
38
38
|
);
|
|
39
39
|
});
|
|
40
40
|
|
|
41
41
|
it("trims whitespace", () => {
|
|
42
|
-
expect(generateObservableKey(" ipv4
|
|
43
|
-
"obs:ipv4
|
|
42
|
+
expect(generateObservableKey(" ipv4 ", " 192.168.1.1 ")).toBe(
|
|
43
|
+
"obs:ipv4:192.168.1.1"
|
|
44
44
|
);
|
|
45
45
|
});
|
|
46
46
|
});
|
|
@@ -56,8 +56,8 @@ describe("Key Generation", () => {
|
|
|
56
56
|
describe("generateThreatIntelKey", () => {
|
|
57
57
|
it("generates correct threat intel key", () => {
|
|
58
58
|
expect(
|
|
59
|
-
generateThreatIntelKey("virustotal", "obs:ipv4
|
|
60
|
-
).toBe("ti:virustotal:obs:ipv4
|
|
59
|
+
generateThreatIntelKey("virustotal", "obs:ipv4:192.168.1.1")
|
|
60
|
+
).toBe("ti:virustotal:obs:ipv4:192.168.1.1");
|
|
61
61
|
});
|
|
62
62
|
});
|
|
63
63
|
|
|
@@ -88,7 +88,7 @@ describe("Key Generation", () => {
|
|
|
88
88
|
|
|
89
89
|
describe("parseKeyType", () => {
|
|
90
90
|
it("parses observable key type", () => {
|
|
91
|
-
expect(parseKeyType("obs:ipv4
|
|
91
|
+
expect(parseKeyType("obs:ipv4:192.168.1.1")).toBe("obs");
|
|
92
92
|
});
|
|
93
93
|
|
|
94
94
|
it("parses check key type", () => {
|
|
@@ -115,13 +115,13 @@ describe("Key Generation", () => {
|
|
|
115
115
|
|
|
116
116
|
describe("validateKey", () => {
|
|
117
117
|
it("validates correct keys", () => {
|
|
118
|
-
expect(validateKey("obs:ipv4
|
|
118
|
+
expect(validateKey("obs:ipv4:192.168.1.1")).toBe(true);
|
|
119
119
|
expect(validateKey("chk:sender:email")).toBe(true);
|
|
120
120
|
});
|
|
121
121
|
|
|
122
122
|
it("validates key type", () => {
|
|
123
|
-
expect(validateKey("obs:ipv4
|
|
124
|
-
expect(validateKey("obs:ipv4
|
|
123
|
+
expect(validateKey("obs:ipv4:192.168.1.1", "obs")).toBe(true);
|
|
124
|
+
expect(validateKey("obs:ipv4:192.168.1.1", "chk")).toBe(false);
|
|
125
125
|
});
|
|
126
126
|
|
|
127
127
|
it("rejects invalid keys", () => {
|
|
@@ -133,8 +133,8 @@ describe("Key Generation", () => {
|
|
|
133
133
|
|
|
134
134
|
describe("parseObservableKey", () => {
|
|
135
135
|
it("parses observable key components", () => {
|
|
136
|
-
expect(parseObservableKey("obs:ipv4
|
|
137
|
-
type: "ipv4
|
|
136
|
+
expect(parseObservableKey("obs:ipv4:192.168.1.1")).toEqual({
|
|
137
|
+
type: "ipv4",
|
|
138
138
|
value: "192.168.1.1",
|
|
139
139
|
});
|
|
140
140
|
});
|
|
@@ -162,10 +162,10 @@ describe("Key Generation", () => {
|
|
|
162
162
|
describe("parseThreatIntelKey", () => {
|
|
163
163
|
it("parses threat intel key components", () => {
|
|
164
164
|
expect(
|
|
165
|
-
parseThreatIntelKey("ti:virustotal:obs:ipv4
|
|
165
|
+
parseThreatIntelKey("ti:virustotal:obs:ipv4:192.168.1.1")
|
|
166
166
|
).toEqual({
|
|
167
167
|
source: "virustotal",
|
|
168
|
-
observableKey: "obs:ipv4
|
|
168
|
+
observableKey: "obs:ipv4:192.168.1.1",
|
|
169
169
|
});
|
|
170
170
|
});
|
|
171
171
|
});
|