0
# Data Models and Types
1
2
Safety CLI uses a comprehensive set of data models and types to represent vulnerabilities, packages, requirements, and other security-related information. These models provide structured data access and serialization capabilities.
3
4
## Core Data Models
5
6
### Import Statements { .api }
7
8
```python
9
from safety.models import (
10
# Core models
11
Vulnerability, CVE, Severity, Fix, Package,
12
SafetyRequirement, RequirementFile,
13
14
# Utilities and encoders
15
SafetyEncoder, is_pinned_requirement, SafetyCLI, ToolResult
16
)
17
18
# Note: Announcement and Remediation must be imported directly from vulnerabilities module
19
from safety.models.vulnerabilities import Announcement, Remediation
20
from safety.errors import (
21
SafetyError, SafetyException, InvalidRequirementError,
22
InvalidCredentialError, NetworkConnectionError
23
)
24
from safety_schemas.models import (
25
ConfigModel, Ecosystem, FileType, Stage,
26
PolicyFileModel, DependencyResultModel
27
)
28
```
29
30
## Vulnerability Models
31
32
### Vulnerability Class { .api }
33
34
**Description**: Represents a security vulnerability found in a package dependency.
35
36
```python
37
# Note: Vulnerability is a named tuple with these fields (in order)
38
Vulnerability = namedtuple('Vulnerability', [
39
'vulnerability_id', # str: Unique vulnerability identifier
40
'package_name', # str: Affected package name
41
'pkg', # Package: Package object reference
42
'ignored', # bool: Whether vulnerability is ignored
43
'ignored_reason', # Optional[str]: Reason for ignoring
44
'ignored_expires', # Optional[datetime]: Ignore expiration date
45
'vulnerable_spec', # SpecifierSet: Vulnerable version specifiers
46
'all_vulnerable_specs', # List[str]: All vulnerable version patterns
47
'analyzed_version', # str: Currently installed version
48
'analyzed_requirement', # SafetyRequirement: Analyzed requirement object
49
'advisory', # str: Vulnerability description
50
'is_transitive', # bool: Is transitive dependency
51
'published_date', # datetime: Publication date
52
'fixed_versions', # List[str]: Versions with fixes
53
'closest_versions_without_known_vulnerabilities', # List[str]: Safe alternatives
54
'resources', # List[str]: Additional resources
55
'CVE', # CVE: CVE information
56
'severity', # Severity: Severity assessment
57
'affected_versions', # str: Affected version range
58
'more_info_url' # str: Additional information URL
59
])
60
61
class Vulnerability(vulnerability_nmt):
62
"""Core vulnerability information with comprehensive metadata."""
63
```
64
65
**Methods:**
66
67
```python
68
def to_dict(self) -> Dict[str, Any]:
69
"""
70
Convert vulnerability to dictionary format.
71
72
Returns:
73
Dict[str, Any]: Dictionary representation suitable for JSON serialization
74
"""
75
76
def get_advisory(self) -> str:
77
"""
78
Get cleaned advisory text.
79
80
Returns:
81
str: Advisory text with formatting cleaned
82
"""
83
84
def to_model_dict(self) -> Dict[str, Any]:
85
"""
86
Convert to model dictionary format for API communication.
87
88
Returns:
89
Dict[str, Any]: Structured model representation
90
"""
91
```
92
93
**Example Usage:**
94
95
```python
96
from safety.models import Vulnerability, CVE, Severity
97
from datetime import datetime
98
99
# Access vulnerability properties
100
vuln = Vulnerability(...) # From scan results
101
102
print(f"Vulnerability: {vuln.vulnerability_id}")
103
print(f"Package: {vuln.package_name} v{vuln.analyzed_version}")
104
print(f"Severity: {vuln.severity.cvssv3}")
105
print(f"Advisory: {vuln.get_advisory()}")
106
107
# Check if ignored
108
if vuln.ignored:
109
print(f"Ignored: {vuln.ignored_reason}")
110
if vuln.ignored_expires:
111
print(f"Expires: {vuln.ignored_expires}")
112
113
# Get remediation options
114
if vuln.fixed_versions:
115
print(f"Fixed in: {', '.join(vuln.fixed_versions)}")
116
117
# Convert to dictionary for serialization
118
vuln_dict = vuln.to_dict()
119
```
120
121
### CVE Class { .api }
122
123
**Description**: Common Vulnerabilities and Exposures (CVE) information.
124
125
```python
126
class CVE(NamedTuple):
127
"""CVE identification and scoring information."""
128
129
name: str # CVE identifier (e.g., "CVE-2023-12345")
130
cvssv2: Optional[float] # CVSS v2.0 score
131
cvssv3: Optional[float] # CVSS v3.x score
132
133
def to_dict(self) -> Dict[str, Any]:
134
"""
135
Convert CVE to dictionary format.
136
137
Returns:
138
Dict[str, Any]: CVE information as dictionary
139
"""
140
```
141
142
**Example Usage:**
143
144
```python
145
from safety.models import CVE
146
147
# CVE with scoring information
148
cve = CVE(
149
name="CVE-2023-12345",
150
cvssv2=7.5,
151
cvssv3=8.1
152
)
153
154
# Access properties
155
print(f"CVE: {cve.name}")
156
print(f"CVSS v3 Score: {cve.cvssv3}")
157
158
# Severity classification based on CVSS score
159
if cve.cvssv3 >= 9.0:
160
severity_level = "Critical"
161
elif cve.cvssv3 >= 7.0:
162
severity_level = "High"
163
elif cve.cvssv3 >= 4.0:
164
severity_level = "Medium"
165
else:
166
severity_level = "Low"
167
```
168
169
### Severity Class { .api }
170
171
**Description**: Vulnerability severity assessment from multiple sources.
172
173
```python
174
class Severity(NamedTuple):
175
"""Vulnerability severity with source attribution."""
176
177
source: str # Severity source (e.g., "safety", "nvd")
178
cvssv2: Optional[float] # CVSS v2.0 score
179
cvssv3: Optional[float] # CVSS v3.x score
180
181
def to_dict(self) -> Dict[str, Any]:
182
"""
183
Convert severity to dictionary format.
184
185
Returns:
186
Dict[str, Any]: Structured severity information
187
"""
188
```
189
190
## Package and Requirement Models
191
192
### Package Class { .api }
193
194
**Description**: Represents a Python package with version and dependency information.
195
196
```python
197
@dataclass
198
class Package:
199
"""Python package with comprehensive metadata and version information."""
200
201
# Basic package information
202
name: str # Package name (normalized)
203
version: Optional[str] # Installed version
204
requirements: List[SafetyRequirement] # Package requirements
205
206
# Location information
207
found: Optional[str] = None # Where package was found
208
absolute_path: Optional[str] = None # Absolute path to package
209
210
# Version metadata
211
insecure_versions: List[str] = field(default_factory=list) # Known insecure versions
212
secure_versions: List[str] = field(default_factory=list) # Known secure versions
213
latest_version_without_known_vulnerabilities: Optional[str] = None # Latest safe version
214
latest_version: Optional[str] = None # Latest available version
215
more_info_url: Optional[str] = None # Package information URL
216
```
217
218
**Methods:**
219
220
```python
221
def has_unpinned_req(self) -> bool:
222
"""
223
Check if package has unpinned requirements.
224
225
Returns:
226
bool: True if any requirements are unpinned
227
"""
228
229
def get_unpinned_req(self) -> filter:
230
"""
231
Get unpinned requirements.
232
233
Returns:
234
filter: Filter object containing unpinned requirements
235
"""
236
237
def filter_by_supported_versions(self, versions: List[str]) -> List[str]:
238
"""
239
Filter versions by parsing support.
240
241
Args:
242
versions (List[str]): Version strings to filter
243
244
Returns:
245
List[str]: Valid, parseable version strings
246
"""
247
248
def get_versions(self, db_full: Dict) -> Set[str]:
249
"""
250
Get all versions from vulnerability database.
251
252
Args:
253
db_full (Dict): Complete vulnerability database
254
255
Returns:
256
Set[str]: All known versions for package
257
"""
258
259
def refresh_from(self, db_full: Dict) -> None:
260
"""
261
Refresh package metadata from database.
262
263
Args:
264
db_full (Dict): Complete vulnerability database
265
"""
266
267
def to_dict(self) -> Dict[str, Any]:
268
"""
269
Convert package to dictionary format.
270
271
Returns:
272
Dict[str, Any]: Package information as dictionary
273
"""
274
```
275
276
**Example Usage:**
277
278
```python
279
from safety.models import Package, SafetyRequirement
280
281
# Create package instance
282
package = Package(
283
name="requests",
284
version="2.28.1",
285
requirements=[
286
SafetyRequirement("urllib3>=1.21.1,<1.27"),
287
SafetyRequirement("certifi>=2017.4.17")
288
],
289
found="requirements.txt"
290
)
291
292
# Check for unpinned requirements
293
if package.has_unpinned_req():
294
unpinned = list(package.get_unpinned_req())
295
print(f"Unpinned requirements: {[req.name for req in unpinned]}")
296
297
# Package metadata access
298
print(f"Package: {package.name} v{package.version}")
299
print(f"Found in: {package.found}")
300
print(f"Latest safe version: {package.latest_version_without_known_vulnerabilities}")
301
```
302
303
### SafetyRequirement Class { .api }
304
305
**Description**: Enhanced version of packaging.requirements.Requirement with Safety-specific features.
306
307
```python
308
class SafetyRequirement(Requirement):
309
"""Enhanced requirement with Safety-specific metadata and methods."""
310
311
def __init__(self,
312
requirement: Union[str, Dependency],
313
found: Optional[str] = None) -> None:
314
"""
315
Initialize Safety requirement.
316
317
Args:
318
requirement: Requirement string or Dependency object
319
found: Location where requirement was found
320
321
Raises:
322
InvalidRequirementError: If requirement cannot be parsed
323
"""
324
325
# Additional properties
326
raw: str # Original requirement line
327
found: Optional[str] # Source location
328
```
329
330
**Methods:**
331
332
```python
333
def to_dict(self, **kwargs: Any) -> Dict[str, Any]:
334
"""
335
Convert requirement to dictionary format.
336
337
Args:
338
**kwargs: Additional options (e.g., specifier_obj format)
339
340
Returns:
341
Dict[str, Any]: Requirement information as dictionary
342
"""
343
344
def __eq__(self, other: Any) -> bool:
345
"""
346
Compare requirements for equality.
347
348
Args:
349
other: Other requirement to compare
350
351
Returns:
352
bool: True if requirements are equal
353
"""
354
```
355
356
**Dictionary Format:**
357
358
```python
359
requirement_dict = {
360
'raw': 'requests>=2.20.0,<3.0.0', # Original requirement line
361
'name': 'requests', # Package name
362
'specifier': '>=2.20.0,<3.0.0', # Version specifiers
363
'extras': ['security'], # Optional extras
364
'marker': 'python_version >= "3.8"', # Environment markers
365
'url': None, # Direct URL (if any)
366
'found': 'requirements.txt' # Source location
367
}
368
```
369
370
### RequirementFile { .api }
371
372
**Description**: Named tuple representing a requirements file location.
373
374
```python
375
RequirementFile = namedtuple('RequirementFile', ['path'])
376
377
# Usage
378
req_file = RequirementFile(path="requirements.txt")
379
print(f"Requirements file: {req_file.path}")
380
```
381
382
## Remediation and Fix Models
383
384
### Fix Class { .api }
385
386
**Description**: Represents an applied or available security fix for a vulnerability.
387
388
```python
389
@dataclass
390
class Fix:
391
"""Security fix information and metadata."""
392
393
# Dependency information
394
dependency: Any = None # Dependency object
395
package: str = "" # Package name
396
397
# Version changes
398
previous_version: Any = None # Version before fix
399
updated_version: Any = None # Version after fix
400
previous_spec: Optional[str] = None # Previous version specifier
401
other_options: List[str] = field(default_factory=list) # Alternative versions
402
403
# Fix metadata
404
update_type: str = "" # Type of update (major, minor, patch)
405
status: str = "" # Fix application status
406
applied_at: str = "" # When fix was applied
407
fix_type: str = "" # Fix type classification
408
more_info_url: str = "" # Additional information URL
409
```
410
411
**Example Usage:**
412
413
```python
414
from safety.models import Fix
415
416
# Applied security fix
417
fix = Fix(
418
package="requests",
419
previous_version="2.20.0",
420
updated_version="2.28.1",
421
update_type="minor",
422
status="applied",
423
applied_at="2024-01-15T10:30:00Z",
424
fix_type="security_update"
425
)
426
427
print(f"Fixed {fix.package}: {fix.previous_version} → {fix.updated_version}")
428
print(f"Update type: {fix.update_type}")
429
print(f"Applied: {fix.applied_at}")
430
```
431
432
### Remediation Class { .api }
433
434
**Description**: Remediation suggestion for vulnerable packages.
435
436
```python
437
class Remediation(NamedTuple):
438
"""Remediation suggestion with version recommendations."""
439
440
Package: Package # Affected package
441
closest_secure_version: str # Nearest secure version
442
secure_versions: List[str] # All secure versions
443
latest_package_version: str # Latest available version
444
445
def to_dict(self) -> Dict[str, Any]:
446
"""
447
Convert remediation to dictionary format.
448
449
Returns:
450
Dict[str, Any]: Remediation information
451
"""
452
```
453
454
## Utility Models and Types
455
456
### Announcement Class { .api }
457
458
**Description**: Platform announcements and notices.
459
460
```python
461
class Announcement(NamedTuple):
462
"""Platform announcement information."""
463
464
type: str # Announcement type
465
message: str # Announcement content
466
```
467
468
### DictConverter Base Class { .api }
469
470
**Description**: Abstract base class for objects that can be converted to dictionaries.
471
472
```python
473
class DictConverter:
474
"""Base class for dictionary serialization."""
475
476
@abstractmethod
477
def to_dict(self, **kwargs: Any) -> Dict[str, Any]:
478
"""
479
Convert object to dictionary format.
480
481
Args:
482
**kwargs: Additional conversion options
483
484
Returns:
485
Dict[str, Any]: Dictionary representation
486
"""
487
```
488
489
### SafetyCLI Model { .api }
490
491
**Description**: Represents Safety CLI instance metadata.
492
493
```python
494
from safety.models import SafetyCLI
495
496
class SafetyCLI:
497
"""Safety CLI instance information and metadata."""
498
499
# CLI version and environment information
500
version: str # Safety CLI version
501
python_version: str # Python version
502
platform: str # Operating system platform
503
504
# Usage and telemetry data
505
command: str # Executed command
506
options: Dict[str, Any] # Command options
507
execution_time: float # Execution duration
508
```
509
510
### ToolResult Model { .api }
511
512
**Description**: Results from integrated security tools.
513
514
```python
515
from safety.models import ToolResult
516
517
class ToolResult:
518
"""Results from integrated security analysis tools."""
519
520
tool_name: str # Tool identifier
521
exit_code: int # Tool exit code
522
output: str # Tool output
523
errors: List[str] # Error messages
524
metadata: Dict[str, Any] # Additional metadata
525
```
526
527
## Serialization and Encoding
528
529
### SafetyEncoder { .api }
530
531
**Description**: Custom JSON encoder for Safety-specific data types.
532
533
```python
534
class SafetyEncoder(json.JSONEncoder):
535
"""Custom JSON encoder for Safety objects."""
536
537
def default(self, value: Any) -> Any:
538
"""
539
Serialize Safety objects to JSON-compatible formats.
540
541
Handles:
542
- SafetyRequirement objects
543
- packaging.version.Version objects
544
- packaging.version.LegacyVersion objects
545
- Custom data classes
546
547
Args:
548
value: Object to serialize
549
550
Returns:
551
Any: JSON-serializable representation
552
553
Raises:
554
TypeError: If object type is not supported
555
"""
556
```
557
558
**Example Usage:**
559
560
```python
561
import json
562
from safety.models import SafetyEncoder
563
564
# Serialize complex Safety data structures
565
data = {
566
'vulnerabilities': vulnerability_list, # Contains Vulnerability objects
567
'packages': package_list, # Contains Package objects
568
'requirements': requirement_list # Contains SafetyRequirement objects
569
}
570
571
json_output = json.dumps(data, cls=SafetyEncoder, indent=2)
572
```
573
574
## Configuration Models
575
576
### Schema Models { .api }
577
578
These models are imported from `safety_schemas.models`:
579
580
```python
581
from safety_schemas.models import (
582
ConfigModel, Ecosystem, FileType, Stage,
583
PolicyFileModel, DependencyResultModel
584
)
585
586
class ConfigModel:
587
"""Central configuration for Safety CLI operations."""
588
589
telemetry_enabled: bool # Enable telemetry collection
590
591
class Ecosystem(Enum):
592
"""Supported package ecosystems."""
593
PYTHON = "python"
594
595
class FileType(Enum):
596
"""Supported dependency file types."""
597
REQUIREMENTS_TXT = "requirements.txt"
598
PIPFILE = "Pipfile"
599
PYPROJECT_TOML = "pyproject.toml"
600
# ... additional file types
601
602
class Stage(Enum):
603
"""Development lifecycle stages."""
604
DEVELOPMENT = "development"
605
TESTING = "testing"
606
STAGING = "staging"
607
PRODUCTION = "production"
608
```
609
610
## Error and Exception Models
611
612
### Core Exception Classes { .api }
613
614
```python
615
from safety.errors import (
616
SafetyError, SafetyException, InvalidRequirementError,
617
InvalidCredentialError, NetworkConnectionError
618
)
619
620
class SafetyException(Exception):
621
"""Base exception for Safety CLI errors."""
622
623
def __init__(self, message: str = "Unhandled exception: {info}", info: str = ""):
624
"""Initialize with formatted message."""
625
626
def get_exit_code(self) -> int:
627
"""Get associated exit code."""
628
629
class SafetyError(Exception):
630
"""Generic Safety CLI error."""
631
632
def __init__(self, message: str = "Unhandled Safety error",
633
error_code: Optional[int] = None):
634
"""Initialize with message and optional error code."""
635
636
class InvalidRequirementError(SafetyError):
637
"""Error parsing requirement specifications."""
638
639
class InvalidCredentialError(SafetyError):
640
"""Authentication credential validation error."""
641
642
class NetworkConnectionError(SafetyError):
643
"""Network connectivity error."""
644
```
645
646
## Usage Examples
647
648
### Working with Vulnerabilities
649
650
```python
651
from safety.models import Vulnerability, Package, SafetyRequirement
652
653
# Process scan results
654
for vulnerability in scan_results.vulnerabilities:
655
print(f"🚨 {vulnerability.vulnerability_id}")
656
print(f" Package: {vulnerability.package_name}")
657
print(f" Version: {vulnerability.analyzed_version}")
658
print(f" Severity: {vulnerability.severity.cvssv3}")
659
660
if vulnerability.fixed_versions:
661
print(f" Fixed in: {', '.join(vulnerability.fixed_versions[:3])}")
662
663
# Check if vulnerability is ignored
664
if vulnerability.ignored:
665
print(f" ⚠️ Ignored: {vulnerability.ignored_reason}")
666
```
667
668
### Package Analysis
669
670
```python
671
from safety.models import Package, is_pinned_requirement
672
673
# Analyze package requirements
674
for package in discovered_packages:
675
print(f"📦 {package.name} v{package.version}")
676
677
# Check for unpinned requirements
678
if package.has_unpinned_req():
679
unpinned = list(package.get_unpinned_req())
680
for req in unpinned:
681
print(f" ⚠️ Unpinned: {req.name} {req.specifier}")
682
683
# Show latest safe version
684
if package.latest_version_without_known_vulnerabilities:
685
print(f" ✅ Latest safe: {package.latest_version_without_known_vulnerabilities}")
686
```
687
688
### Custom Serialization
689
690
```python
691
import json
692
from safety.models import SafetyEncoder
693
694
# Create custom report format
695
report = {
696
'scan_metadata': {
697
'timestamp': datetime.now(),
698
'target': '/path/to/project'
699
},
700
'findings': {
701
'vulnerabilities': vulnerability_list,
702
'packages': package_list
703
}
704
}
705
706
# Serialize with Safety encoder
707
json_report = json.dumps(report, cls=SafetyEncoder, indent=2)
708
709
# Save to file
710
with open('security_report.json', 'w') as f:
711
f.write(json_report)
712
```
713
714
This comprehensive data models documentation covers all core types and structures used throughout Safety CLI, enabling developers to understand and work with vulnerability data, package information, and security analysis results programmatically.