diff --git a/src/depcheck/models.py b/src/depcheck/models.py new file mode 100644 index 0000000..8f69477 --- /dev/null +++ b/src/depcheck/models.py @@ -0,0 +1,91 @@ +"""Data models for depcheck.""" + +from dataclasses import dataclass, field +from enum import Enum +from typing import Optional + + +class Severity(str, Enum): + """Severity levels for vulnerabilities and updates.""" + + CRITICAL = "critical" + HIGH = "high" + MEDIUM = "medium" + LOW = "low" + INFO = "info" + + +class PackageManager(str, Enum): + """Supported package managers.""" + + NPM = "npm" + PIP = "pip" + GO = "go" + CARGO = "cargo" + + +@dataclass +class Dependency: + """Represents a single dependency.""" + + name: str + current_version: str + package_manager: PackageManager + latest_version: Optional[str] = None + is_outdated: bool = False + vulnerabilities: list = field(default_factory=list) + category: str = "dependencies" + source_file: Optional[str] = None + + def __hash__(self): + return hash((self.name, self.package_manager.value, self.current_version)) + + def __eq__(self, other): + if not isinstance(other, Dependency): + return False + return ( + self.name == other.name + and self.package_manager == other.package_manager + and self.current_version == other.current_version + ) + + +@dataclass +class Vulnerability: + """Represents a security vulnerability.""" + + cve_id: str + severity: Severity + description: str + affected_versions: str + fixed_version: Optional[str] = None + references: list[str] = field(default_factory=list) + + +@dataclass +class ScanResult: + """Result of a dependency scan.""" + + dependencies: list[Dependency] = field(default_factory=list) + vulnerabilities: list[tuple[Dependency, Vulnerability]] = field(default_factory=list) + scan_errors: list[str] = field(default_factory=list) + package_manager: Optional[PackageManager] = None + source_file: Optional[str] = None + + @property + def outdated_count(self) -> int: + return sum(1 for d in self.dependencies if d.is_outdated) + + @property + def vulnerable_count(self) -> int: + return len(self.vulnerabilities) + + def get_highest_severity(self) -> Optional[Severity]: + if not self.vulnerabilities: + return None + severity_order = [Severity.CRITICAL, Severity.HIGH, Severity.MEDIUM, Severity.LOW] + for severity in severity_order: + for dep, vuln in self.vulnerabilities: + if vuln.severity == severity: + return severity + return None