cyvest 4.4.0__py3-none-any.whl → 5.1.0__py3-none-any.whl
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.
Potentially problematic release.
This version of cyvest might be problematic. Click here for more details.
- cyvest/__init__.py +24 -5
- cyvest/cli.py +63 -1
- cyvest/compare.py +310 -0
- cyvest/cyvest.py +253 -181
- cyvest/investigation.py +276 -243
- cyvest/io_rich.py +141 -54
- cyvest/io_schema.py +1 -1
- cyvest/io_serialization.py +90 -91
- cyvest/keys.py +61 -18
- cyvest/model.py +55 -43
- cyvest/model_schema.py +9 -9
- cyvest/proxies.py +48 -50
- cyvest/shared.py +19 -19
- cyvest/stats.py +11 -36
- {cyvest-4.4.0.dist-info → cyvest-5.1.0.dist-info}/METADATA +105 -12
- cyvest-5.1.0.dist-info/RECORD +24 -0
- {cyvest-4.4.0.dist-info → cyvest-5.1.0.dist-info}/WHEEL +1 -1
- cyvest-4.4.0.dist-info/RECORD +0 -23
- {cyvest-4.4.0.dist-info → cyvest-5.1.0.dist-info}/entry_points.txt +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.3
|
|
2
2
|
Name: cyvest
|
|
3
|
-
Version:
|
|
3
|
+
Version: 5.1.0
|
|
4
4
|
Summary: Cybersecurity investigation model
|
|
5
5
|
Keywords: cybersecurity,investigation,threat-intel,security-analysis
|
|
6
6
|
Author: PakitoSec
|
|
@@ -47,6 +47,7 @@ Description-Content-Type: text/markdown
|
|
|
47
47
|
- 💾 **Multiple Export Formats**: JSON and Markdown output for reporting and LLM consumption
|
|
48
48
|
- 🎨 **Rich Console Output**: Beautiful terminal displays with the Rich library
|
|
49
49
|
- 🧩 **Fluent helpers**: Convenient API with method chaining for rapid development
|
|
50
|
+
- 🔬 **Investigation Comparison**: Compare investigations with tolerance rules and visual diff output
|
|
50
51
|
|
|
51
52
|
## Installation
|
|
52
53
|
|
|
@@ -111,14 +112,14 @@ cv.io_save_json("investigation.json")
|
|
|
111
112
|
### Model Proxies
|
|
112
113
|
|
|
113
114
|
Cyvest only exposes immutable model proxies. Helpers like `observable_create`, `check_create`, and the
|
|
114
|
-
fluent `cv.observable()`/`cv.check()` convenience methods return `ObservableProxy`, `CheckProxy`, `
|
|
115
|
+
fluent `cv.observable()`/`cv.check()` convenience methods return `ObservableProxy`, `CheckProxy`, `TagProxy`, etc.
|
|
115
116
|
These proxies reflect the live investigation state but raise `AttributeError` if you try to assign to their attributes.
|
|
116
117
|
All mutations are routed through the Investigation layer, so use the facade helpers (`cv.observable_set_level`,
|
|
117
118
|
`cv.check_update_score`, `cv.observable_add_threat_intel`) or the built-in fluent methods on the proxies themselves
|
|
118
119
|
(`with_ti`, `relate_to`, `link_observable`, `with_score`, …) so the score engine and audit log stay consistent.
|
|
119
120
|
|
|
120
121
|
Mutation helpers that reference existing objects (for example, `cv.observable_add_relationship`,
|
|
121
|
-
`cv.check_link_observable`, `cv.
|
|
122
|
+
`cv.check_link_observable`, `cv.tag_add_check`) raise `KeyError` when a key is missing.
|
|
122
123
|
|
|
123
124
|
Safe metadata fields like `comment`, `extra`, or `internal` can be updated through the proxies without breaking score
|
|
124
125
|
consistency:
|
|
@@ -208,15 +209,22 @@ ti.add_taxonomy(level=cv.LVL.SUSPICIOUS, name="confidence", value="medium")
|
|
|
208
209
|
ti.remove_taxonomy("confidence")
|
|
209
210
|
```
|
|
210
211
|
|
|
211
|
-
###
|
|
212
|
+
### Tags
|
|
212
213
|
|
|
213
|
-
|
|
214
|
+
Tags organize checks with automatic hierarchy based on `:` delimiter:
|
|
214
215
|
|
|
215
216
|
```python
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
217
|
+
# Simple: pass tag names directly (auto-creates tags)
|
|
218
|
+
check = cv.check("beacon_detection", "Detect C2 beacons")
|
|
219
|
+
check.tagged("network", "c2:detection", "suspicious")
|
|
220
|
+
|
|
221
|
+
# With description: create tag first, then reference it
|
|
222
|
+
tag = cv.tag("network:c2:detection", "C2 Detection Checks")
|
|
223
|
+
check.tagged(tag)
|
|
224
|
+
|
|
225
|
+
# Query hierarchy
|
|
226
|
+
children = cv.tag_get_children("network") # ["network:c2"]
|
|
227
|
+
descendants = cv.tag_get_descendants("network") # ["network:c2", "network:c2:detection"]
|
|
220
228
|
```
|
|
221
229
|
|
|
222
230
|
### Lookup Helpers
|
|
@@ -232,9 +240,9 @@ check = cv.check_create("malware_detection", "endpoint", "Verify file hash")
|
|
|
232
240
|
same_check = cv.check_get("malware_detection", "endpoint")
|
|
233
241
|
same_check_by_key = cv.check_get(check.key)
|
|
234
242
|
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
243
|
+
tag = cv.tag_create("network:analysis")
|
|
244
|
+
same_tag = cv.tag_get("network:analysis")
|
|
245
|
+
same_tag_by_key = cv.tag_get(tag.key)
|
|
238
246
|
|
|
239
247
|
enrichment = cv.enrichment_create("whois", {"registrar": "Example Inc"})
|
|
240
248
|
same_enrichment = cv.enrichment_get("whois")
|
|
@@ -383,6 +391,88 @@ Its key is derived from type + value (e.g. `obs:file:root` or `obs:artifact:root
|
|
|
383
391
|
|
|
384
392
|
This design enables flexible investigation structures while preventing unintended score contamination.
|
|
385
393
|
|
|
394
|
+
### Comparing Investigations
|
|
395
|
+
|
|
396
|
+
Compare two investigations to identify differences in checks, observables, and threat intelligence:
|
|
397
|
+
|
|
398
|
+
```python
|
|
399
|
+
from decimal import Decimal
|
|
400
|
+
from cyvest import Cyvest, ExpectedResult, Level, compare_investigations
|
|
401
|
+
from cyvest.io_rich import display_diff
|
|
402
|
+
|
|
403
|
+
# Create expected and actual investigations
|
|
404
|
+
expected = Cyvest(investigation_name="expected")
|
|
405
|
+
expected.check_create("domain-check", "Verify domain", score=Decimal("1.0"))
|
|
406
|
+
|
|
407
|
+
actual = Cyvest(investigation_name="actual")
|
|
408
|
+
actual.check_create("domain-check", "Verify domain", score=Decimal("2.0"))
|
|
409
|
+
actual.check_create("new-check", "New detection", score=Decimal("1.5"))
|
|
410
|
+
|
|
411
|
+
# Compare investigations
|
|
412
|
+
diffs = compare_investigations(actual, expected)
|
|
413
|
+
# diffs contains:
|
|
414
|
+
# - MISMATCH for domain-check (score changed 1.0 -> 2.0)
|
|
415
|
+
# - ADDED for new-check
|
|
416
|
+
```
|
|
417
|
+
|
|
418
|
+
**Tolerance Rules**
|
|
419
|
+
|
|
420
|
+
Use `result_expected` rules to define acceptable score variations:
|
|
421
|
+
|
|
422
|
+
```python
|
|
423
|
+
# Define tolerance rules
|
|
424
|
+
rules = [
|
|
425
|
+
# Accept any score >= 1.0 for this check
|
|
426
|
+
ExpectedResult(check_name="domain-check", score=">= 1.0"),
|
|
427
|
+
# Accept any score < 3.0 for roger-ai
|
|
428
|
+
ExpectedResult(key="chk:roger-ai", level=Level.SUSPICIOUS, score="< 3.0"),
|
|
429
|
+
]
|
|
430
|
+
|
|
431
|
+
# Compare with tolerance - checks satisfying rules are not flagged as diffs
|
|
432
|
+
diffs = compare_investigations(actual, expected, result_expected=rules)
|
|
433
|
+
```
|
|
434
|
+
|
|
435
|
+
Supported operators: `>=`, `<=`, `>`, `<`, `==`, `!=`
|
|
436
|
+
|
|
437
|
+
**Visual Diff Display**
|
|
438
|
+
|
|
439
|
+
Display differences in a rich table format:
|
|
440
|
+
|
|
441
|
+
```python
|
|
442
|
+
from cyvest.io_rich import display_diff
|
|
443
|
+
from logurich import logger
|
|
444
|
+
|
|
445
|
+
# Display diff table with tree structure showing observables and threat intel
|
|
446
|
+
display_diff(diffs, lambda r: logger.rich("INFO", r), title="Investigation Diff")
|
|
447
|
+
```
|
|
448
|
+
|
|
449
|
+
Output:
|
|
450
|
+
```
|
|
451
|
+
╭────────────────────────────────────────────────┬────────────────────┬─────────────────┬────────╮
|
|
452
|
+
│ Key │ Expected │ Actual │ Status │
|
|
453
|
+
├────────────────────────────────────────────────┼────────────────────┼─────────────────┼────────┤
|
|
454
|
+
│ chk:new-check │ - │ NOTABLE 1.50 │ + │
|
|
455
|
+
│ └── domain: example.com │ - │ INFO 0.00 │ │
|
|
456
|
+
│ └── VirusTotal │ - │ INFO 0.00 │ │
|
|
457
|
+
├────────────────────────────────────────────────┼────────────────────┼─────────────────┼────────┤
|
|
458
|
+
│ chk:domain-check │ NOTABLE 1.00 │ NOTABLE 2.00 │ ✗ │
|
|
459
|
+
╰────────────────────────────────────────────────┴────────────────────┴─────────────────┴────────╯
|
|
460
|
+
```
|
|
461
|
+
|
|
462
|
+
Status symbols: `+` (added), `-` (removed), `✗` (mismatch)
|
|
463
|
+
|
|
464
|
+
**Convenience Methods**
|
|
465
|
+
|
|
466
|
+
Use methods directly on Cyvest objects:
|
|
467
|
+
|
|
468
|
+
```python
|
|
469
|
+
# Compare and get diff items
|
|
470
|
+
diffs = actual.compare(expected=expected, result_expected=rules)
|
|
471
|
+
|
|
472
|
+
# Compare and display in one call
|
|
473
|
+
actual.display_diff(expected=expected, title="My Investigation Diff")
|
|
474
|
+
```
|
|
475
|
+
|
|
386
476
|
## Examples
|
|
387
477
|
|
|
388
478
|
See the `examples/` directory for complete examples:
|
|
@@ -392,6 +482,7 @@ See the `examples/` directory for complete examples:
|
|
|
392
482
|
- **03_merge_demo.py**: Multi-process investigation merging
|
|
393
483
|
- **04_email.py**: Multi-threaded investigation with SharedInvestigationContext
|
|
394
484
|
- **05_visualization.py**: Interactive HTML visualization showcasing scores, levels, and relationship flows
|
|
485
|
+
- **06_compare_investigations.py**: Compare investigations with tolerance rules and visual diff output
|
|
395
486
|
|
|
396
487
|
Run an example:
|
|
397
488
|
|
|
@@ -522,6 +613,7 @@ Cyvest is designed for:
|
|
|
522
613
|
- **Malware Analysis**: Track relationships between artifacts
|
|
523
614
|
- **Phishing Analysis**: Analyze emails and linked resources
|
|
524
615
|
- **Integration**: Combine results from multiple security tools
|
|
616
|
+
- **Regression Testing**: Compare investigation outputs across rule or detection updates
|
|
525
617
|
|
|
526
618
|
## Architecture Highlights
|
|
527
619
|
|
|
@@ -531,6 +623,7 @@ Cyvest is designed for:
|
|
|
531
623
|
- **Score Propagation**: Automatic hierarchical score calculation
|
|
532
624
|
- **Flexible Export**: JSON for storage, Markdown for LLM analysis
|
|
533
625
|
- **Audit Trail**: Score change history for debugging
|
|
626
|
+
- **Investigation Comparison**: Compare investigations with tolerance rules for regression testing
|
|
534
627
|
|
|
535
628
|
## Future Enhancements
|
|
536
629
|
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
cyvest/__init__.py,sha256=cqJ-Sbwopy3acHEn1UO4C7wkqde8rE_iuD3SByy5cmU,1383
|
|
2
|
+
cyvest/cli.py,sha256=K_BgHhBEt_lw7CehM3wpDb03BOA2uzPRD2XNRGld0Pc,14321
|
|
3
|
+
cyvest/compare.py,sha256=CcBGP6pF1fp4tYl3mdTJke8dgzr8C4YTJDA9eOvpVKQ,10119
|
|
4
|
+
cyvest/cyvest.py,sha256=vAeh7q9_HQ87vMmJmpSkAwi4zTlRR55-KhjKP7MwJok,47459
|
|
5
|
+
cyvest/investigation.py,sha256=dt4jzx5TjdWCXXvKUdX0eiri10mCS87EKuPPB1dt-Bw,61447
|
|
6
|
+
cyvest/io_rich.py,sha256=fLg-1-SiHbUTMk7rhic425WBdqg06191O9Tu05LEHOg,24813
|
|
7
|
+
cyvest/io_schema.py,sha256=Z-vlhrbWnGJcIm8k-XVSmzv1vbSvl7I6Z2GV55iU1fc,1296
|
|
8
|
+
cyvest/io_serialization.py,sha256=ZNrjY29SvJoeGZHOTajRbdKUqmsayNw1cP-5HeOTvik,17755
|
|
9
|
+
cyvest/io_visualization.py,sha256=kPT8cAqZJ3liSA2BzwvQXMaejnk496m0Ck11QXJCQLc,11010
|
|
10
|
+
cyvest/keys.py,sha256=xNpSZW5VDzeqc5F0-66-jHM7_v00wt2A70kvbqRSxwA,5678
|
|
11
|
+
cyvest/level_score_rules.py,sha256=MoCCYCLsTDCM-OqLV4Ln_GEH7H_tjNpK4tte89IEQeU,2391
|
|
12
|
+
cyvest/levels.py,sha256=1d3YHCbP2aptSGyIwVzLF4rJjAYdpSzmOnzjs7ZLsdg,4556
|
|
13
|
+
cyvest/model.py,sha256=Em3D5kXS5HCUrehjEEOPWXbX1-UVaEf4ebOs5Z1DBAw,18600
|
|
14
|
+
cyvest/model_enums.py,sha256=t7uFDnGcLXLMZdwgHMxk3HU5KHQgqUvBNZ5i_H2_Jvc,2050
|
|
15
|
+
cyvest/model_schema.py,sha256=Xrd10_dRPcXyiotAF6LJHi7iioGqPBvQlCE87vUVvaM,6037
|
|
16
|
+
cyvest/proxies.py,sha256=74FQvC3gDRpCjv7_-G6x90eJnQmcSVom_3jwS_3EGTY,19525
|
|
17
|
+
cyvest/score.py,sha256=CiD-dcjEVr2oLHPJ4MlcQWv5338Tq-Zn4Q7lXIEq9fY,19807
|
|
18
|
+
cyvest/shared.py,sha256=PidiXY3bfB1ph5B0Ah_etyZKkiUa5S7FoNk5eRjnOLw,19375
|
|
19
|
+
cyvest/stats.py,sha256=X2e9BHqJrfaOHNUt6jShQqy2Pdw4DMdGheRD9WGPmS0,9035
|
|
20
|
+
cyvest/ulid.py,sha256=NA3k6hlgMZyfzLEMJEIMqt73CY2V07Bupqk7gemshDM,1069
|
|
21
|
+
cyvest-5.1.0.dist-info/WHEEL,sha256=eycQt0QpYmJMLKpE3X9iDk8R04v2ZF0x82ogq-zP6bQ,79
|
|
22
|
+
cyvest-5.1.0.dist-info/entry_points.txt,sha256=bwtGzs4Eh9i3WEhW4dhxHaYP8qf8qUN4sDnt4xXywUk,44
|
|
23
|
+
cyvest-5.1.0.dist-info/METADATA,sha256=efWG28fBJ9CRGtXOyLF2l34Q9-wF0Z-juNhW9OgNwUw,23156
|
|
24
|
+
cyvest-5.1.0.dist-info/RECORD,,
|
cyvest-4.4.0.dist-info/RECORD
DELETED
|
@@ -1,23 +0,0 @@
|
|
|
1
|
-
cyvest/__init__.py,sha256=ISyIqEKzFg5WvCKWyAa1k6B3-bSdC8mxhWXkIhuiIYA,1046
|
|
2
|
-
cyvest/cli.py,sha256=Zj8RVCG4BXFTNQt0Oo2GgwviF1aY5by_69eZE2XOgkg,12271
|
|
3
|
-
cyvest/cyvest.py,sha256=82DSGeog6YjEyZagkD5E2e5Vmxk7hSERfhOA_5YCF0I,45041
|
|
4
|
-
cyvest/investigation.py,sha256=LKiP3p74m5UnC1QnCZ5WEX6xtxIyrzI-chdd9nE8YLE,61074
|
|
5
|
-
cyvest/io_rich.py,sha256=46mPidDNzqo1hgcWxVrEFRNMdto_nEe8vaaxaOwGqFk,21932
|
|
6
|
-
cyvest/io_schema.py,sha256=vhreQyKQbVrXFVj1ddIoAuEXc6zGhTVndHZZdDxTd0c,1302
|
|
7
|
-
cyvest/io_serialization.py,sha256=V0KAxGZu11t7LA413L7QlJyh5o2lwytQCLllDflH6I4,18478
|
|
8
|
-
cyvest/io_visualization.py,sha256=kPT8cAqZJ3liSA2BzwvQXMaejnk496m0Ck11QXJCQLc,11010
|
|
9
|
-
cyvest/keys.py,sha256=9S7ELhloRzKrLPlpfRnwYtL7WXbKIsyhteh62mn3j2E,4614
|
|
10
|
-
cyvest/level_score_rules.py,sha256=MoCCYCLsTDCM-OqLV4Ln_GEH7H_tjNpK4tte89IEQeU,2391
|
|
11
|
-
cyvest/levels.py,sha256=1d3YHCbP2aptSGyIwVzLF4rJjAYdpSzmOnzjs7ZLsdg,4556
|
|
12
|
-
cyvest/model.py,sha256=Bi1IRXM2Ca47t5KpJ1vtSnPyeJhHZ1ZnezE4-JkSwv0,18184
|
|
13
|
-
cyvest/model_enums.py,sha256=t7uFDnGcLXLMZdwgHMxk3HU5KHQgqUvBNZ5i_H2_Jvc,2050
|
|
14
|
-
cyvest/model_schema.py,sha256=LVJ99i-_T6cSJ3N7OJO2F0k1LjpA23S-PKSk6RYzwks,6119
|
|
15
|
-
cyvest/proxies.py,sha256=s_vc4KPWvta7saNNR92WHgzn73V-BdOYhZoRJ_0evHw,19581
|
|
16
|
-
cyvest/score.py,sha256=CiD-dcjEVr2oLHPJ4MlcQWv5338Tq-Zn4Q7lXIEq9fY,19807
|
|
17
|
-
cyvest/shared.py,sha256=217ufZ-sBAMr0CMflBEpfeTSXs2eRlgNIul1fm0l_Qw,19505
|
|
18
|
-
cyvest/stats.py,sha256=mBHgLaRPs1R9l775_K3zQKN6ujup5sGzD5o5CQCPSK8,9926
|
|
19
|
-
cyvest/ulid.py,sha256=NA3k6hlgMZyfzLEMJEIMqt73CY2V07Bupqk7gemshDM,1069
|
|
20
|
-
cyvest-4.4.0.dist-info/WHEEL,sha256=RRVLqVugUmFOqBedBFAmA4bsgFcROUBiSUKlERi0Hcg,79
|
|
21
|
-
cyvest-4.4.0.dist-info/entry_points.txt,sha256=bwtGzs4Eh9i3WEhW4dhxHaYP8qf8qUN4sDnt4xXywUk,44
|
|
22
|
-
cyvest-4.4.0.dist-info/METADATA,sha256=8PLZvgm-LAEV_fxfDLupzWHcPa-_mqqCT2rcG0gzDps,18718
|
|
23
|
-
cyvest-4.4.0.dist-info/RECORD,,
|
|
File without changes
|