Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 6 additions & 4 deletions examples/01_email_basic.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,16 @@
Demonstrates basic usage of Cyvest for analyzing a suspicious email.
"""

import logging
import tempfile
from decimal import Decimal
from pathlib import Path

from logurich import logger
from logurich import init_logger

from cyvest import Cyvest

logger.enable("cyvest")
logger = logging.getLogger(__name__)


def main() -> None:
Expand Down Expand Up @@ -89,9 +90,10 @@ def main() -> None:
cv.io_save_json(str(json_path))
cv.io_save_markdown(str(md_path))

logger.info("✓ Investigation saved to {} and {}", json_path, md_path)
logger.info("Temporary output directory: {}", output_dir)
logger.info("✓ Investigation saved to %s and %s", json_path, md_path)
logger.info("Temporary output directory: %s", output_dir)


if __name__ == "__main__":
init_logger("INFO")
main()
10 changes: 6 additions & 4 deletions examples/02_urls_and_ips.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,15 +5,16 @@
with relationship tracking.
"""

import logging
import tempfile
from decimal import Decimal
from pathlib import Path

from logurich import logger
from logurich import init_logger

from cyvest import Cyvest

logger.enable("cyvest")
logger = logging.getLogger(__name__)


def main() -> None:
Expand Down Expand Up @@ -97,9 +98,10 @@ def main() -> None:
output_dir = Path(tempfile.mkdtemp(prefix="cyvest_example_02_"))
json_path = output_dir / "urls_and_ips_investigation.json"
cv.io_save_json(str(json_path))
logger.info("✓ Investigation saved to {}", json_path)
logger.info("Temporary output directory: {}", output_dir)
logger.info("✓ Investigation saved to %s", json_path)
logger.info("Temporary output directory: %s", output_dir)


if __name__ == "__main__":
init_logger("INFO")
main()
13 changes: 7 additions & 6 deletions examples/03_merge_demo.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,16 +5,17 @@
and merge them together.
"""

import logging
import multiprocessing as mp
import tempfile
from decimal import Decimal
from pathlib import Path

from logurich import logger
from logurich import init_logger

from cyvest import Cyvest

logger.enable("cyvest")
logger = logging.getLogger(__name__)


def analyze_network_traffic() -> Cyvest:
Expand Down Expand Up @@ -62,7 +63,6 @@ def analyze_endpoint_logs() -> Cyvest:
endpoint_check = cv.check_create(
"malware_execution",
"endpoint",
"Detected malware execution",
comment="Ransomware executed with SYSTEM privileges",
score=Decimal("9.5"),
level=cv.LVL.MALICIOUS,
Expand Down Expand Up @@ -135,7 +135,7 @@ def main() -> None:
incident_tag = main_investigation.tag_create("incident:findings", "Consolidated findings from all data sources")

# Add all checks to the tag
for check in main_investigation.get_all_checks().values():
for check in main_investigation.check_get_all().values():
main_investigation.tag_add_check(incident_tag.key, check.key)

# Finalize relationships
Expand All @@ -151,11 +151,12 @@ def main() -> None:
output_dir = Path(tempfile.mkdtemp(prefix="cyvest_example_03_"))
json_path = output_dir / "merged_investigation.json"
main_investigation.io_save_json(str(json_path))
logger.info("✓ Merged investigation saved to {}", json_path)
logger.info("Temporary output directory: {}", output_dir)
logger.info("✓ Merged investigation saved to %s", json_path)
logger.info("Temporary output directory: %s", output_dir)


if __name__ == "__main__":
# Required for multiprocessing on some platforms
mp.freeze_support()
init_logger("INFO")
main()
15 changes: 7 additions & 8 deletions examples/04_email.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,20 +5,19 @@
with each task building a Cyvest fragment that merges into the main investigation.
"""

import logging
from abc import ABC, abstractmethod
from concurrent.futures import ThreadPoolExecutor, as_completed
from pathlib import Path
from typing import Any

import click
from logurich import logger
from logurich.opt_click import click_logger_params

from cyvest import Cyvest
from cyvest.shared import SharedInvestigationContext

logger.enable("cyvest")

logger = logging.getLogger(__name__)

# ============================================================================
# Task Framework
Expand Down Expand Up @@ -282,10 +281,10 @@ def run(self, cy: Cyvest) -> None:
.tagged(tag)
)

logger.info("[bold red]Check Score: {}[/bold red]", chk.score)
logger.info("[bold red]Check Score: %s[/bold red]", chk.score)

logger.info(f"Bodies URLs analysis complete: {len(urls_with_scores)} URLs processed")
logger.info("[bold red]Tag Score: {}[/bold red]", tag.get_aggregated_score())
logger.info("[bold red]Tag Score: %s[/bold red]", tag.get_aggregated_score())


class BodiesDomainTask(BaseRule):
Expand Down Expand Up @@ -337,10 +336,10 @@ def run(self, cy: Cyvest) -> None:
.tagged(tag)
)

logger.info("[bold red]Check Score: {}[/bold red]", chk.score)
logger.info("[bold red]Check Score: %s[/bold red]", chk.score)

logger.info(f"Bodies DOMAINS analysis complete: {len(domains_with_scores)} DOMAINS processed")
logger.info("[bold red]Tag Score: {}[/bold red]", tag.get_aggregated_score())
logger.info("[bold red]Tag Score: %s[/bold red]", tag.get_aggregated_score())


class AttachmentTask(BaseRule):
Expand Down Expand Up @@ -580,7 +579,7 @@ def main(workers, browser, stats, audit, no_audit_log, output):
logger.info("[bold cyan]Generating json...[/bold cyan]")
json_path = cy.io_save_json(output, include_audit_log=not no_audit_log)
size_kb = Path(json_path).stat().st_size / 1024
logger.info("[green]✓ Full json saved to: {} ({:.2f} KB)[/green]", json_path, size_kb)
logger.info("[green]✓ Full json saved to: %s (%.2f KB)[/green]", json_path, size_kb)


if __name__ == "__main__":
Expand Down
6 changes: 3 additions & 3 deletions examples/05_visualization.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,16 +12,16 @@
- Edge labels show the relationship type
"""

import logging
import tempfile
from pathlib import Path

import click
from logurich import logger
from logurich.opt_click import click_logger_params

from cyvest import Cyvest

logger.enable("cyvest")
logger = logging.getLogger(__name__)


@click.command()
Expand Down Expand Up @@ -160,7 +160,7 @@ def main(no_audit_log: bool = False, output: Path | None = None) -> None:
output_path = output or (Path(tempfile.gettempdir()) / "cyvest_investigation.json")
json_path = cv.io_save_json(output_path, include_audit_log=not no_audit_log)
size_kb = Path(json_path).stat().st_size / 1024
logger.info("[green]✓ Full json saved to: {} ({:.2f} KB)[/green]", json_path, size_kb)
logger.info("[green]✓ Full json saved to: %s (%.2f KB)[/green]", json_path, size_kb)

# Generate and open network visualization
logger.info("[bold cyan]Generating Network Visualization...[/bold cyan]")
Expand Down
7 changes: 4 additions & 3 deletions examples/06_compare_investigations.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,15 +5,15 @@
Shows differences in checks, observables, and threat intelligence between investigations.
"""

import logging
from decimal import Decimal

from logurich import init_logger, logger
from logurich import init_logger

from cyvest import Cyvest, ExpectedResult, Level, compare_investigations
from cyvest.io_rich import display_diff

init_logger("INFO")
logger.enable("cyvest")
logger = logging.getLogger(__name__)


def create_expected_investigation() -> Cyvest:
Expand Down Expand Up @@ -163,4 +163,5 @@ def main() -> None:


if __name__ == "__main__":
init_logger("INFO")
main()
2 changes: 1 addition & 1 deletion js/packages/cyvest-app/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@cyvest/cyvest-app",
"version": "5.3.3",
"version": "5.4.0",
"private": true,
"scripts": {
"dev": "vite",
Expand Down
2 changes: 1 addition & 1 deletion js/packages/cyvest-js/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@cyvest/cyvest-js",
"version": "5.3.3",
"version": "5.4.0",
"type": "module",
"files": [
"dist"
Expand Down
2 changes: 1 addition & 1 deletion js/packages/cyvest-vis/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@cyvest/cyvest-vis",
"version": "5.3.3",
"version": "5.4.0",
"type": "module",
"files": [
"dist"
Expand Down
4 changes: 2 additions & 2 deletions pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[project]
name = "cyvest"
version = "5.3.3"
version = "5.4.0"
description = "Cybersecurity investigation model"
readme = {file = "README.md", content-type = "text/markdown"}
requires-python = ">=3.10"
Expand All @@ -10,7 +10,7 @@ authors = [
]
dependencies = [
"click>=8",
"logurich[click]>=0.6.0",
"logurich[click]>=0.9.0",
"pydantic>=2.12.5",
"rich>=13",
"typing-extensions>=4.15",
Expand Down
6 changes: 1 addition & 5 deletions src/cyvest/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,6 @@
programmatically with automatic scoring, level calculation, and rich reporting capabilities.
"""

from logurich import logger

from cyvest.compare import (
DiffItem,
DiffStatus,
Expand All @@ -21,9 +19,7 @@
from cyvest.model_enums import ObservableType, RelationshipDirection, RelationshipType
from cyvest.proxies import CheckProxy, EnrichmentProxy, ObservableProxy, TagProxy, ThreatIntelProxy

__version__ = "5.3.3"

logger.disable("cyvest")
__version__ = "5.4.0"

__all__ = [
# Core class
Expand Down
10 changes: 5 additions & 5 deletions src/cyvest/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,11 @@
from __future__ import annotations

import json
import logging
from pathlib import Path
from typing import Any

import click
from logurich import logger
from logurich.opt_click import click_logger_params
from rich.console import Console

Expand All @@ -34,6 +34,7 @@

CONTEXT_SETTINGS = {"help_option_names": ["-h", "--help"]}
console = Console()
logger = logging.getLogger(__name__)


def _load_investigation(input_path: Path) -> dict[str, Any]:
Expand All @@ -51,7 +52,7 @@ def _print_stats_overview(stats: dict[str, Any]) -> None:
for key, value in stats.items():
if isinstance(value, dict):
continue
logger.info(" {}: {}", key, value)
logger.info(" %s: %s", key, value)


def _write_markdown(data: dict[str, Any], output_path: Path) -> None:
Expand Down Expand Up @@ -86,7 +87,6 @@ def _write_markdown(data: dict[str, Any], output_path: Path) -> None:
@click.version_option(__version__, prog_name="Cyvest")
def cli() -> None:
"""Cyvest - Cybersecurity Investigation Framework."""
logger.enable("cyvest")
logger.info("> [green bold]CYVEST[/green bold]")


Expand Down Expand Up @@ -122,8 +122,8 @@ def stats(input: Path, detailed: bool) -> None:
cv = load_investigation_json(input)
logger.info(f"[cyan]Statistics for: {input}[/cyan]")
logger.info("[bold]Overview:[/bold]")
logger.info(" Global Score: {}", f"{cv.get_global_score():.2f}")
logger.info(" Global Level: {}", cv.get_global_level())
logger.info(" Global Score: %s", f"{cv.get_global_score():.2f}")
logger.info(" Global Level: %s", cv.get_global_level())

if detailed:
logger.info("")
Expand Down
5 changes: 4 additions & 1 deletion src/cyvest/cyvest.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,14 @@

from __future__ import annotations

import logging
import threading
from collections.abc import Callable, Iterable
from decimal import Decimal
from pathlib import Path
from typing import TYPE_CHECKING, Any, Final, Literal, overload

from logurich import logger
import logurich # noqa: F401

from cyvest import keys
from cyvest.compare import compare_investigations
Expand Down Expand Up @@ -48,6 +49,8 @@
if TYPE_CHECKING:
from cyvest.shared import SharedInvestigationContext

logger = logging.getLogger(__name__)


class Cyvest:
"""
Expand Down
7 changes: 4 additions & 3 deletions src/cyvest/investigation.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,12 @@

from __future__ import annotations

import logging
from copy import deepcopy
from datetime import datetime, timezone
from decimal import Decimal
from typing import TYPE_CHECKING, Any, Literal

from logurich import logger

from cyvest import keys
from cyvest.level_score_rules import recalculate_level_for_score
from cyvest.levels import Level, normalize_level
Expand All @@ -38,6 +37,8 @@
if TYPE_CHECKING:
from cyvest.model_schema import StatisticsSchema

logger = logging.getLogger(__name__)


class Investigation:
"""
Expand Down Expand Up @@ -1561,7 +1562,7 @@ def _snapshot_tag(tag: Tag) -> dict[str, Any]:
else:
# Genuine error - target still doesn't exist after Pass 2
logger.critical(
"Relationship target '{}' not found after merge completion for observable '{}'. "
"Relationship target '%s' not found after merge completion for observable '%s'. "
"This indicates corrupted data or a bug in the merge logic.",
rel.target_key,
source_key,
Expand Down
Loading
Loading