cyvest 1.0.1__tar.gz
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.
- cyvest-1.0.1/PKG-INFO +548 -0
- cyvest-1.0.1/README.md +521 -0
- cyvest-1.0.1/pyproject.toml +74 -0
- cyvest-1.0.1/src/cyvest/__init__.py +33 -0
- cyvest-1.0.1/src/cyvest/cli.py +327 -0
- cyvest-1.0.1/src/cyvest/cyvest.py +825 -0
- cyvest-1.0.1/src/cyvest/investigation.py +1580 -0
- cyvest-1.0.1/src/cyvest/io_rich.py +335 -0
- cyvest-1.0.1/src/cyvest/io_serialization.py +514 -0
- cyvest-1.0.1/src/cyvest/io_visualization.py +361 -0
- cyvest-1.0.1/src/cyvest/keys.py +168 -0
- cyvest-1.0.1/src/cyvest/levels.py +149 -0
- cyvest-1.0.1/src/cyvest/model.py +625 -0
- cyvest-1.0.1/src/cyvest/proxies.py +660 -0
- cyvest-1.0.1/src/cyvest/score.py +440 -0
- cyvest-1.0.1/src/cyvest/stats.py +291 -0
cyvest-1.0.1/PKG-INFO
ADDED
|
@@ -0,0 +1,548 @@
|
|
|
1
|
+
Metadata-Version: 2.3
|
|
2
|
+
Name: cyvest
|
|
3
|
+
Version: 1.0.1
|
|
4
|
+
Summary: Cybersecurity investigation model
|
|
5
|
+
Keywords: cybersecurity,investigation,threat-intel,security-analysis
|
|
6
|
+
Author: PakitoSec
|
|
7
|
+
Author-email: PakitoSec <jeromep83@gmail.com>
|
|
8
|
+
License: MIT
|
|
9
|
+
Classifier: Development Status :: 4 - Beta
|
|
10
|
+
Classifier: Intended Audience :: Developers
|
|
11
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
12
|
+
Classifier: Programming Language :: Python :: 3
|
|
13
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
14
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
16
|
+
Classifier: Topic :: Security
|
|
17
|
+
Requires-Dist: click>=8
|
|
18
|
+
Requires-Dist: logurich[click]>=0.1
|
|
19
|
+
Requires-Dist: rich>=13
|
|
20
|
+
Requires-Dist: pyvis>=0.3.2 ; extra == 'visualization'
|
|
21
|
+
Requires-Python: >=3.10
|
|
22
|
+
Project-URL: Homepage, https://github.com/PakitoSec/cyvest
|
|
23
|
+
Project-URL: Issues, https://github.com/PakitoSec/cyvest/issues
|
|
24
|
+
Project-URL: Repository, https://github.com/PakitoSec/cyvest
|
|
25
|
+
Provides-Extra: visualization
|
|
26
|
+
Description-Content-Type: text/markdown
|
|
27
|
+
|
|
28
|
+
# Cyvest - Cybersecurity Investigation Framework
|
|
29
|
+
|
|
30
|
+
[](https://www.python.org/downloads/)
|
|
31
|
+
[](https://opensource.org/licenses/MIT)
|
|
32
|
+
|
|
33
|
+
**Cyvest** is a Python framework for building, analyzing, and structuring cybersecurity investigations programmatically. It provides automatic scoring, level calculation, relationship tracking, and rich reporting capabilities.
|
|
34
|
+
|
|
35
|
+
## Features
|
|
36
|
+
|
|
37
|
+
- ๐ **Structured Investigation Modeling**: Model investigations with observables, checks, threat intelligence, and enrichments
|
|
38
|
+
- ๐ **Automatic Scoring**: Dynamic score calculation and propagation through investigation hierarchy
|
|
39
|
+
- ๐ฏ **Level Classification**: Automatic security level assignment (TRUSTED, INFO, SAFE, NOTABLE, SUSPICIOUS, MALICIOUS)
|
|
40
|
+
- ๐ **Relationship Tracking**: STIX2-compliant relationship modeling between observables
|
|
41
|
+
- ๐ท๏ธ **STIX2 Type Support**: Built-in enums for STIX2 Observable and Relationship types with autocomplete
|
|
42
|
+
- ๐ **Real-time Statistics**: Live metrics and aggregations throughout the investigation
|
|
43
|
+
- ๐ **Investigation Merging**: Combine investigations from multiple threads or processes
|
|
44
|
+
- ๐งต **Multi-Threading Support**: Advanced thread-safe shared context available via `cyvest.investigation` module
|
|
45
|
+
- ๐พ **Multiple Export Formats**: JSON and Markdown output for reporting and LLM consumption
|
|
46
|
+
- ๐จ **Rich Console Output**: Beautiful terminal displays with the Rich library
|
|
47
|
+
- ๐งฉ **Fluent helpers**: Convenient API with method chaining for rapid development
|
|
48
|
+
|
|
49
|
+
## Installation
|
|
50
|
+
|
|
51
|
+
### Using uv (recommended)
|
|
52
|
+
|
|
53
|
+
```bash
|
|
54
|
+
# Install uv if not already installed
|
|
55
|
+
curl -LsSf https://astral.sh/uv/install.sh | sh
|
|
56
|
+
|
|
57
|
+
# Clone the repository
|
|
58
|
+
git clone https://github.com/PakitoSec/cyvest.git
|
|
59
|
+
cd cyvest
|
|
60
|
+
|
|
61
|
+
# Install dependencies
|
|
62
|
+
uv sync
|
|
63
|
+
|
|
64
|
+
# Install in development mode
|
|
65
|
+
uv pip install -e .
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
### Using pip
|
|
69
|
+
|
|
70
|
+
```bash
|
|
71
|
+
pip install -e .
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
> Install the optional visualization extra with\
|
|
75
|
+
> `pip install "cyvest[visualization]"` (or `uv pip install -e ".[visualization]"`).
|
|
76
|
+
|
|
77
|
+
## Quick Start
|
|
78
|
+
|
|
79
|
+
```python
|
|
80
|
+
from decimal import Decimal
|
|
81
|
+
from cyvest import Cyvest, Level, ObservableType, RelationshipType
|
|
82
|
+
|
|
83
|
+
# Create an investigation
|
|
84
|
+
with Cyvest(data={"type": "email"}) as cv:
|
|
85
|
+
# Create observables with STIX2 types
|
|
86
|
+
url = (
|
|
87
|
+
cv.observable(ObservableType.URL, "https://phishing-site.com", internal=False)
|
|
88
|
+
.with_ti("virustotal", score=Decimal("8.5"), level=Level.MALICIOUS)
|
|
89
|
+
.relate_to(cv.root(), RelationshipType.RELATED_TO)
|
|
90
|
+
)
|
|
91
|
+
|
|
92
|
+
# Create checks
|
|
93
|
+
check = cv.check("url_analysis", "email_body", "Analyze suspicious URL")
|
|
94
|
+
check.link_observable(url)
|
|
95
|
+
check.with_score(Decimal("8.5"), "Malicious URL detected")
|
|
96
|
+
|
|
97
|
+
# Display results
|
|
98
|
+
print(f"Global Score: {cv.get_global_score()}")
|
|
99
|
+
print(f"Global Level: {cv.get_global_level()}")
|
|
100
|
+
|
|
101
|
+
# Export
|
|
102
|
+
from cyvest.io_serialization import save_investigation_json
|
|
103
|
+
save_investigation_json(cv, "investigation.json")
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
### Model Proxies
|
|
107
|
+
|
|
108
|
+
Cyvest only exposes immutable model proxies. Helpers like `observable_create`, `check_create`, and the
|
|
109
|
+
fluent `cv.observable()`/`cv.check()` convenience methods return `ObservableProxy`, `CheckProxy`, `ContainerProxy`, etc.
|
|
110
|
+
These proxies reflect the live investigation state but raise `AttributeError` if you try to assign to their attributes.
|
|
111
|
+
Use the facade helpers (`cv.observable_set_level`, `cv.check_update_score`, `cv.observable_add_threat_intel`) or the
|
|
112
|
+
built-in fluent methods on the proxies themselves (`with_ti`, `relate_to`, `link_observable`, `with_score`, โฆ) so the
|
|
113
|
+
score engine runs automatically.
|
|
114
|
+
|
|
115
|
+
Safe metadata fields like `comment`, `extra`, or `internal` can be updated through the proxies without breaking score
|
|
116
|
+
consistency:
|
|
117
|
+
|
|
118
|
+
```python
|
|
119
|
+
url_obs.update_metadata(comment="triaged", internal=False, extra={"ticket": "INC-4242"})
|
|
120
|
+
check.update_metadata(description="New scope", extra={"playbook": "url-analysis"})
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
Dictionary fields merge by default; pass `merge_extra=False` (or `merge_data=False` for enrichments) to overwrite them.
|
|
124
|
+
|
|
125
|
+
## Core Concepts
|
|
126
|
+
|
|
127
|
+
### Observables
|
|
128
|
+
|
|
129
|
+
Observables represent cyber artifacts (URLs, IPs, domains, hashes, files, etc.). Use STIX2-compliant types for standardization:
|
|
130
|
+
|
|
131
|
+
```python
|
|
132
|
+
from cyvest import ObservableType, RelationshipType
|
|
133
|
+
|
|
134
|
+
# Create observable with STIX2 type enum
|
|
135
|
+
url_obs = cv.observable_create(
|
|
136
|
+
ObservableType.URL,
|
|
137
|
+
"https://malicious.com",
|
|
138
|
+
internal=False
|
|
139
|
+
)
|
|
140
|
+
|
|
141
|
+
# Or use strings (backward compatible)
|
|
142
|
+
ip_obs = cv.observable_create("ipv4-addr", "192.0.2.1", internal=False)
|
|
143
|
+
|
|
144
|
+
# Add threat intelligence
|
|
145
|
+
cv.observable_add_threat_intel(
|
|
146
|
+
url_obs.key,
|
|
147
|
+
source="virustotal",
|
|
148
|
+
score=Decimal("9.0"),
|
|
149
|
+
level=Level.MALICIOUS,
|
|
150
|
+
comment="Detected as malware distribution site"
|
|
151
|
+
)
|
|
152
|
+
|
|
153
|
+
# Create relationships with STIX2 relationship types
|
|
154
|
+
# Accepts observable proxies or string keys
|
|
155
|
+
cv.observable_add_relationship(
|
|
156
|
+
url_obs, # Can pass ObservableProxy directly
|
|
157
|
+
ip_obs, # Or use .key for string keys
|
|
158
|
+
RelationshipType.RESOLVES_TO
|
|
159
|
+
)
|
|
160
|
+
```
|
|
161
|
+
|
|
162
|
+
**Available STIX2 Observable Types:**
|
|
163
|
+
|
|
164
|
+
- Network: `IPV4_ADDR`, `IPV6_ADDR`, `DOMAIN_NAME`, `URL`, `MAC_ADDR`, `NETWORK_TRAFFIC`
|
|
165
|
+
- Email: `EMAIL_ADDR`, `EMAIL_MESSAGE`, `EMAIL_MIME_PART`
|
|
166
|
+
- File: `FILE`, `DIRECTORY`, `ARTIFACT`
|
|
167
|
+
- System: `PROCESS`, `SOFTWARE`, `USER_ACCOUNT`, `WINDOWS_REGISTRY_KEY`
|
|
168
|
+
- Other: `AUTONOMOUS_SYSTEM`, `MUTEX`, `X509_CERTIFICATE`
|
|
169
|
+
|
|
170
|
+
**Available STIX2 Relationship Types:**
|
|
171
|
+
|
|
172
|
+
- Network: `RESOLVES_TO`, `BELONGS_TO`, `COMMUNICATES_WITH`
|
|
173
|
+
- File: `CONTAINS`, `DOWNLOADED`, `DROPPED`
|
|
174
|
+
- Email: `FROM`, `SENDER`, `TO`, `CC`, `BCC`
|
|
175
|
+
- Process: `CREATED`, `OPENED`, `PARENT`, `CHILD`
|
|
176
|
+
- General: `RELATED_TO`, `DERIVED_FROM`, `DUPLICATE_OF`
|
|
177
|
+
|
|
178
|
+
**Relationship Direction:**
|
|
179
|
+
|
|
180
|
+
Relationships support directional semantics with **automatic semantic defaults** that determine **hierarchical score propagation**:
|
|
181
|
+
|
|
182
|
+
```python
|
|
183
|
+
from cyvest import RelationshipType, RelationshipDirection
|
|
184
|
+
|
|
185
|
+
# Automatically gets OUTBOUND (domain โ IP)
|
|
186
|
+
# IP is a child of domain, IP's score propagates UP to domain
|
|
187
|
+
# Accepts observable proxies directly
|
|
188
|
+
cv.observable_add_relationship(domain, ip, RelationshipType.RESOLVES_TO)
|
|
189
|
+
|
|
190
|
+
# Automatically gets INBOUND (file โ URL)
|
|
191
|
+
# URL is parent of file, file's score propagates UP to URL
|
|
192
|
+
cv.observable_add_relationship(malware, url, RelationshipType.DOWNLOADED)
|
|
193
|
+
|
|
194
|
+
# Automatically gets BIDIRECTIONAL (host โ host)
|
|
195
|
+
# No hierarchical propagation - scores remain independent
|
|
196
|
+
cv.observable_add_relationship(host1, host2, RelationshipType.COMMUNICATES_WITH)
|
|
197
|
+
|
|
198
|
+
# Can override semantic defaults if needed
|
|
199
|
+
cv.observable_add_relationship(
|
|
200
|
+
domain, ip, # Accepts observable proxies
|
|
201
|
+
RelationshipType.RESOLVES_TO,
|
|
202
|
+
RelationshipDirection.INBOUND # explicit override
|
|
203
|
+
)
|
|
204
|
+
```
|
|
205
|
+
|
|
206
|
+
**Direction-Based Hierarchical Scoring:**
|
|
207
|
+
|
|
208
|
+
Relationship directions define parent-child hierarchies for score propagation:
|
|
209
|
+
|
|
210
|
+
- **OUTBOUND (โ)**: `source โ target` โ Target is a **child** of source
|
|
211
|
+
- Source's score includes child's score: `score = max(TI_scores, child_scores)`
|
|
212
|
+
- Example: `domain โ IP` means IP score flows up to domain
|
|
213
|
+
|
|
214
|
+
- **INBOUND (โ)**: `source โ target` โ Target is a **parent** of source
|
|
215
|
+
- Target's score includes source's score
|
|
216
|
+
- Example: `file โ URL` means file score flows up to URL
|
|
217
|
+
|
|
218
|
+
- **BIDIRECTIONAL (โ)**: `source โ target` โ **No hierarchy**
|
|
219
|
+
- Scores do NOT propagate between observables
|
|
220
|
+
- Each maintains independent score from its own threat intel
|
|
221
|
+
- Example: `host1 โ host2` keeps separate scores
|
|
222
|
+
|
|
223
|
+
**Semantic Default Directions:**
|
|
224
|
+
|
|
225
|
+
Each relationship type has an intuitive default direction:
|
|
226
|
+
- **OUTBOUND (โ)**: `RESOLVES_TO`, `BELONGS_TO`, `CONTAINS`, `TO`, `CC`, `BCC`, `CREATED`, `OPENED`, `PARENT`, `VALUES`
|
|
227
|
+
- **INBOUND (โ)**: `DOWNLOADED`, `DROPPED`, `FROM`, `SENDER`, `CHILD`, `DERIVED_FROM`
|
|
228
|
+
- **BIDIRECTIONAL (โ)**: `COMMUNICATES_WITH`, `RELATED_TO`, `DUPLICATE_OF`
|
|
229
|
+
|
|
230
|
+
Direction symbols appear in visualizations, markdown exports, and determine score flow.
|
|
231
|
+
|
|
232
|
+
### Checks
|
|
233
|
+
|
|
234
|
+
Checks represent verification steps in your investigation:
|
|
235
|
+
|
|
236
|
+
```python
|
|
237
|
+
check = cv.check_create(
|
|
238
|
+
check_id="malware_detection",
|
|
239
|
+
scope="endpoint",
|
|
240
|
+
description="Verify file hash against threat intel",
|
|
241
|
+
score=Decimal("8.0"),
|
|
242
|
+
level=Level.MALICIOUS
|
|
243
|
+
)
|
|
244
|
+
|
|
245
|
+
# Link observables to checks
|
|
246
|
+
cv.check_link_observable(check.key, file_hash_obs.key)
|
|
247
|
+
```
|
|
248
|
+
|
|
249
|
+
### Threat Intelligence
|
|
250
|
+
|
|
251
|
+
Threat intelligence provides verdicts from external sources:
|
|
252
|
+
|
|
253
|
+
```python
|
|
254
|
+
cv.observable_add_threat_intel(
|
|
255
|
+
observable.key,
|
|
256
|
+
source="virustotal",
|
|
257
|
+
score=Decimal("7.5"),
|
|
258
|
+
level=Level.SUSPICIOUS,
|
|
259
|
+
comment="15/70 vendors flagged as malicious",
|
|
260
|
+
taxonomies=[{"malware-type": "trojan"}]
|
|
261
|
+
)
|
|
262
|
+
```
|
|
263
|
+
|
|
264
|
+
### Containers
|
|
265
|
+
|
|
266
|
+
Containers organize checks hierarchically:
|
|
267
|
+
|
|
268
|
+
```python
|
|
269
|
+
with cv.container("network_analysis") as network:
|
|
270
|
+
with network.sub_container("c2_detection") as c2:
|
|
271
|
+
check = cv.check("beacon_detection", "network", "Detect C2 beacons")
|
|
272
|
+
c2.add_check(check.get())
|
|
273
|
+
```
|
|
274
|
+
|
|
275
|
+
### Multi-Threaded Investigations
|
|
276
|
+
|
|
277
|
+
**Advanced Feature**: Use `SharedInvestigationContext` (imported directly from `cyvest.investigation`) for thread-safe parallel task execution with automatic observable sharing:
|
|
278
|
+
|
|
279
|
+
```python
|
|
280
|
+
from cyvest import Cyvest
|
|
281
|
+
from cyvest.investigation import SharedInvestigationContext, InvestigationTask, Investigation
|
|
282
|
+
from concurrent.futures import ThreadPoolExecutor
|
|
283
|
+
|
|
284
|
+
class EmailAnalysisTask(InvestigationTask):
|
|
285
|
+
def run(self, shared_context):
|
|
286
|
+
# SharedInvestigationContext.create_cyvest() creates a Cyvest instance
|
|
287
|
+
# that auto-merges results when the context exits
|
|
288
|
+
with shared_context.create_cyvest() as cy:
|
|
289
|
+
# Access data from root observable
|
|
290
|
+
data = cy.root().extra
|
|
291
|
+
|
|
292
|
+
# Build investigation fragment
|
|
293
|
+
domain = cy.observable(ObservableType.DOMAIN_NAME, data.get("domain"))
|
|
294
|
+
|
|
295
|
+
# Auto-reconciles on exit
|
|
296
|
+
return cy
|
|
297
|
+
|
|
298
|
+
# Create shared context
|
|
299
|
+
main_inv = Investigation(email_data, root_type="artifact")
|
|
300
|
+
shared = SharedInvestigationContext(main_inv)
|
|
301
|
+
|
|
302
|
+
# Run tasks in parallel - they can reference each other's observables
|
|
303
|
+
with ThreadPoolExecutor(max_workers=4) as executor:
|
|
304
|
+
futures = [executor.submit(task.run, shared) for task in tasks]
|
|
305
|
+
for future in as_completed(futures):
|
|
306
|
+
future.result() # Auto-reconciled
|
|
307
|
+
|
|
308
|
+
# Get merged investigation (same object passed to SharedInvestigationContext)
|
|
309
|
+
final_investigation = main_inv
|
|
310
|
+
```
|
|
311
|
+
|
|
312
|
+
See `examples/04_email.py` for a complete multi-threaded investigation example.
|
|
313
|
+
|
|
314
|
+
### Scoring & Levels
|
|
315
|
+
|
|
316
|
+
Scores and levels are automatically calculated and propagated:
|
|
317
|
+
|
|
318
|
+
- **Threat Intel โ Observable**: Observable score = **max** of all threat intel scores (not sum)
|
|
319
|
+
- **Observable Hierarchy**: Parent observable scores include child observable scores based on relationship direction:
|
|
320
|
+
- **OUTBOUND relationships**: target scores propagate to source (source is parent)
|
|
321
|
+
- **INBOUND relationships**: source scores propagate to target (target is parent)
|
|
322
|
+
- **BIDIRECTIONAL relationships**: no hierarchical propagation
|
|
323
|
+
- **Observable โ Check**: Check score = **max** of all linked observables' scores and check's current score
|
|
324
|
+
- **Manual checks**: Set `score_policy=CheckScorePolicy.MANUAL` (or `check.disable_auto_score()`) to prevent observable-driven score/level changes
|
|
325
|
+
- **Check โ Global**: All check scores sum to global investigation score
|
|
326
|
+
|
|
327
|
+
Score to Level mapping:
|
|
328
|
+
|
|
329
|
+
- `< 0.0` โ TRUSTED
|
|
330
|
+
- `== 0.0` โ INFO
|
|
331
|
+
- `< 3.0` โ NOTABLE
|
|
332
|
+
- `< 5.0` โ SUSPICIOUS
|
|
333
|
+
- `>= 5.0` โ MALICIOUS
|
|
334
|
+
|
|
335
|
+
**SAFE Level Protection:**
|
|
336
|
+
|
|
337
|
+
The SAFE level has special protection for trusted/whitelisted observables:
|
|
338
|
+
|
|
339
|
+
```python
|
|
340
|
+
# Mark a known-good domain as SAFE
|
|
341
|
+
trusted = cv.observable_create(
|
|
342
|
+
"domain",
|
|
343
|
+
"trusted.example.com",
|
|
344
|
+
level=Level.SAFE
|
|
345
|
+
)
|
|
346
|
+
|
|
347
|
+
# Adding low-score threat intel won't downgrade to TRUSTED or INFO
|
|
348
|
+
cv.observable_add_threat_intel(trusted.key, "source1", score=Decimal("0"))
|
|
349
|
+
# Level stays SAFE, score updates to 0
|
|
350
|
+
|
|
351
|
+
# But high-score threat intel can still upgrade to MALICIOUS if warranted
|
|
352
|
+
cv.observable_add_threat_intel(trusted.key, "source2", score=Decimal("6.0"))
|
|
353
|
+
# Level upgrades to MALICIOUS, score updates to 6.0
|
|
354
|
+
|
|
355
|
+
# Threat intel with SAFE level can also mark observables as SAFE
|
|
356
|
+
uncertain = cv.observable_create("domain", "example.com")
|
|
357
|
+
cv.observable_add_threat_intel(
|
|
358
|
+
uncertain.key,
|
|
359
|
+
"whitelist_service",
|
|
360
|
+
score=Decimal("0"),
|
|
361
|
+
level=Level.SAFE
|
|
362
|
+
)
|
|
363
|
+
# Observable upgraded to SAFE level with automatic downgrade protection
|
|
364
|
+
```
|
|
365
|
+
|
|
366
|
+
SAFE observables:
|
|
367
|
+
- Cannot be downgraded to lower levels (NONE, TRUSTED, INFO)
|
|
368
|
+
- Can be upgraded to higher levels (NOTABLE, SUSPICIOUS, MALICIOUS)
|
|
369
|
+
- Score values still update based on threat intelligence
|
|
370
|
+
- Protection is preserved during investigation merges
|
|
371
|
+
- Can be marked SAFE by threat intel sources (e.g., whitelists, reputation databases)
|
|
372
|
+
|
|
373
|
+
SAFE checks:
|
|
374
|
+
- Automatically inherit SAFE level when linked to SAFE observables (if all other observables are โค SAFE)
|
|
375
|
+
- Can still upgrade to higher levels when NOTABLE/SUSPICIOUS/MALICIOUS observables are linked
|
|
376
|
+
|
|
377
|
+
**Root Observable Barrier:**
|
|
378
|
+
|
|
379
|
+
The root observable (the investigation's entry point with `value="input-data"`) acts as a special barrier to prevent cross-contamination:
|
|
380
|
+
|
|
381
|
+
**Barrier as Child** - When root appears as a child of other observables, it is **skipped** in their score calculations.
|
|
382
|
+
|
|
383
|
+
**Barrier as Parent** - Root's propagation is asymmetric:
|
|
384
|
+
- Root **CAN** be updated when children change (aggregates child scores)
|
|
385
|
+
- Root **does NOT** propagate upward beyond itself (stops recursive propagation)
|
|
386
|
+
- Root **DOES** propagate to checks normally
|
|
387
|
+
|
|
388
|
+
This design enables flexible investigation structures while preventing unintended score contamination.
|
|
389
|
+
|
|
390
|
+
## Examples
|
|
391
|
+
|
|
392
|
+
See the `examples/` directory for complete examples:
|
|
393
|
+
|
|
394
|
+
- **01_email_basic.py**: Basic email phishing investigation
|
|
395
|
+
- **02_urls_and_ips.py**: Network investigation with URLs and IPs
|
|
396
|
+
- **03_merge_demo.py**: Multi-process investigation merging
|
|
397
|
+
- **04_email.py**: Multi-threaded investigation with SharedInvestigationContext
|
|
398
|
+
- **05_visualization.py**: Interactive HTML visualization showcasing scores, levels, and relationship flows
|
|
399
|
+
|
|
400
|
+
Run an example:
|
|
401
|
+
|
|
402
|
+
```bash
|
|
403
|
+
python examples/01_email_basic.py
|
|
404
|
+
python examples/04_email.py
|
|
405
|
+
python examples/05_visualization.py
|
|
406
|
+
```
|
|
407
|
+
|
|
408
|
+
## CLI Usage
|
|
409
|
+
|
|
410
|
+
Cyvest includes a command-line interface for working with investigation files:
|
|
411
|
+
|
|
412
|
+
```bash
|
|
413
|
+
# Display investigation
|
|
414
|
+
cyvest show investigation.json --graph
|
|
415
|
+
|
|
416
|
+
# Show statistics
|
|
417
|
+
cyvest stats investigation.json --detailed
|
|
418
|
+
|
|
419
|
+
# Export to markdown
|
|
420
|
+
cyvest export investigation.json -o report.md -f markdown
|
|
421
|
+
|
|
422
|
+
# Merge investigations with automatic deduplication
|
|
423
|
+
cyvest merge inv1.json inv2.json inv3.json -o merged.json
|
|
424
|
+
|
|
425
|
+
# Merge with statistics display
|
|
426
|
+
cyvest merge inv1.json inv2.json -o merged.json --stats
|
|
427
|
+
|
|
428
|
+
# Merge and display rich summary
|
|
429
|
+
cyvest merge inv1.json inv2.json -o merged.json -f rich --stats
|
|
430
|
+
|
|
431
|
+
# Generate an interactive visualization (requires visualization extra)
|
|
432
|
+
cyvest visualize investigation.json --min-level SUSPICIOUS --group-by-type
|
|
433
|
+
```
|
|
434
|
+
|
|
435
|
+
## Development
|
|
436
|
+
|
|
437
|
+
### Setup Development Environment
|
|
438
|
+
|
|
439
|
+
```bash
|
|
440
|
+
# Install development dependencies
|
|
441
|
+
uv sync --all-extras
|
|
442
|
+
|
|
443
|
+
# Run tests
|
|
444
|
+
pytest
|
|
445
|
+
|
|
446
|
+
# Run tests with coverage
|
|
447
|
+
pytest --cov=cyvest --cov-report=html
|
|
448
|
+
|
|
449
|
+
# Format code
|
|
450
|
+
ruff format .
|
|
451
|
+
|
|
452
|
+
# Lint code
|
|
453
|
+
ruff check .
|
|
454
|
+
```
|
|
455
|
+
|
|
456
|
+
### Running Tests
|
|
457
|
+
|
|
458
|
+
```bash
|
|
459
|
+
# Run all tests
|
|
460
|
+
pytest
|
|
461
|
+
|
|
462
|
+
# Run specific test file
|
|
463
|
+
pytest tests/test_score.py
|
|
464
|
+
|
|
465
|
+
# Run with verbose output
|
|
466
|
+
pytest -v
|
|
467
|
+
|
|
468
|
+
# Run with coverage
|
|
469
|
+
pytest --cov=cyvest
|
|
470
|
+
```
|
|
471
|
+
|
|
472
|
+
## Documentation
|
|
473
|
+
|
|
474
|
+
Build the documentation with MkDocs:
|
|
475
|
+
|
|
476
|
+
```bash
|
|
477
|
+
# Install docs dependencies
|
|
478
|
+
uv sync --all-extras
|
|
479
|
+
|
|
480
|
+
# Serve documentation locally
|
|
481
|
+
mkdocs serve
|
|
482
|
+
|
|
483
|
+
# Build documentation
|
|
484
|
+
mkdocs build
|
|
485
|
+
```
|
|
486
|
+
|
|
487
|
+
## Project Structure
|
|
488
|
+
|
|
489
|
+
```
|
|
490
|
+
cyvest/
|
|
491
|
+
โโโ src/cyvest/
|
|
492
|
+
โ โโโ __init__.py # Package initialization
|
|
493
|
+
โ โโโ cyvest.py # High-level API facade
|
|
494
|
+
โ โโโ investigation.py # Core state management with merge-on-create
|
|
495
|
+
โ โโโ proxies.py # Read-only proxies + fluent helper methods
|
|
496
|
+
โ โโโ levels.py # Level enum and scoring logic
|
|
497
|
+
โ โโโ keys.py # Key generation utilities
|
|
498
|
+
โ โโโ model.py # Core data models
|
|
499
|
+
โ โโโ score.py # Scoring and propagation engine
|
|
500
|
+
โ โโโ stats.py # Statistics and aggregations
|
|
501
|
+
โ โโโ io_serialization.py # JSON and Markdown export
|
|
502
|
+
โ โโโ io_rich.py # Rich console output
|
|
503
|
+
โ โโโ cli.py # CLI interface
|
|
504
|
+
โโโ examples/ # Example scripts
|
|
505
|
+
โโโ tests/ # Test suite
|
|
506
|
+
โโโ docs/ # Documentation
|
|
507
|
+
โโโ pyproject.toml # Project configuration
|
|
508
|
+
โโโ README.md # This file
|
|
509
|
+
```
|
|
510
|
+
|
|
511
|
+
## Contributing
|
|
512
|
+
|
|
513
|
+
Contributions are welcome! Please:
|
|
514
|
+
|
|
515
|
+
1. Fork the repository
|
|
516
|
+
2. Create a feature branch
|
|
517
|
+
3. Make your changes with tests
|
|
518
|
+
4. Run the test suite
|
|
519
|
+
5. Submit a pull request
|
|
520
|
+
|
|
521
|
+
## License
|
|
522
|
+
|
|
523
|
+
This project is licensed under the MIT License - see the LICENSE file for details.
|
|
524
|
+
|
|
525
|
+
## Use Cases
|
|
526
|
+
|
|
527
|
+
Cyvest is designed for:
|
|
528
|
+
|
|
529
|
+
- **Security Operations Centers (SOCs)**: Automate investigation workflows
|
|
530
|
+
- **Incident Response**: Structure and document incident investigations
|
|
531
|
+
- **Threat Hunting**: Build repeatable hunting methodologies
|
|
532
|
+
- **Malware Analysis**: Track relationships between artifacts
|
|
533
|
+
- **Phishing Analysis**: Analyze emails and linked resources
|
|
534
|
+
- **Integration**: Combine results from multiple security tools
|
|
535
|
+
|
|
536
|
+
## Architecture Highlights
|
|
537
|
+
|
|
538
|
+
- **Thread-Safe**: Advanced `SharedInvestigationContext` (via `cyvest.investigation`) provides thread-safe parallel task execution
|
|
539
|
+
- **Deterministic Keys**: Same objects always generate same keys for merging
|
|
540
|
+
- **Score Propagation**: Automatic hierarchical score calculation
|
|
541
|
+
- **Flexible Export**: JSON for storage, Markdown for LLM analysis
|
|
542
|
+
- **STIX2 Relationships**: Industry-standard relationship modeling
|
|
543
|
+
- **Audit Trail**: Score change history for debugging
|
|
544
|
+
|
|
545
|
+
## Future Enhancements
|
|
546
|
+
|
|
547
|
+
- Database persistence layer
|
|
548
|
+
- Additional export formats (PDF, HTML)
|