0
# Output and Formatters
1
2
Safety CLI provides a comprehensive formatting and output system that supports multiple report formats for vulnerability scanning, license checking, and other analysis results. The system uses a strategy pattern with pluggable formatters for different output types.
3
4
## Core Formatter Architecture
5
6
### FormatterAPI Base Class { .api }
7
8
**Import Statements:**
9
10
```python
11
from safety.formatter import FormatterAPI, SafetyFormatter
12
from safety.formatters.json import JsonReport, build_json_report
13
from safety.formatters.text import TextReport
14
from safety.formatters.screen import ScreenReport
15
from safety.formatters.html import HTMLReport
16
from safety.formatters.bare import BareReport
17
from safety.models import SafetyEncoder
18
```
19
20
#### FormatterAPI Abstract Base { .api }
21
22
**Description**: Abstract base class defining the interface for all output formatters.
23
24
```python
25
class FormatterAPI:
26
"""Strategy pattern base class for report formatters."""
27
28
def __init__(self, **kwargs: Any) -> None:
29
"""
30
Initialize formatter with configuration options.
31
32
Args:
33
**kwargs: Formatter-specific configuration options
34
"""
35
36
@abstractmethod
37
def render_vulnerabilities(
38
self,
39
announcements: List[Dict[str, Any]], # Platform announcements
40
vulnerabilities: List[Dict[str, Any]], # Found vulnerabilities
41
remediations: Dict[str, Any], # Remediation suggestions
42
full: bool, # Include full details
43
packages: List[Dict[str, Any]], # Scanned packages
44
fixes: Tuple = () # Applied fixes
45
) -> Optional[str]:
46
"""
47
Render vulnerability scan results.
48
49
Args:
50
announcements: Platform announcements and notices
51
vulnerabilities: Detected vulnerabilities
52
remediations: Available remediation options
53
full: Whether to include detailed information
54
packages: List of analyzed packages
55
fixes: Applied automatic fixes
56
57
Returns:
58
Optional[str]: Formatted report output
59
"""
60
61
@abstractmethod
62
def render_licenses(
63
self,
64
announcements: List[Dict[str, Any]], # Platform announcements
65
licenses: List[Dict[str, Any]] # License information
66
) -> Optional[str]:
67
"""
68
Render license compliance report.
69
70
Args:
71
announcements: Platform announcements
72
licenses: Package license information
73
74
Returns:
75
Optional[str]: Formatted license report
76
"""
77
78
@abstractmethod
79
def render_announcements(
80
self,
81
announcements: List[Dict[str, Any]] # Platform announcements
82
) -> Optional[str]:
83
"""
84
Render platform announcements.
85
86
Args:
87
announcements: Platform announcements and notices
88
89
Returns:
90
Optional[str]: Formatted announcements
91
"""
92
```
93
94
### SafetyFormatter Factory { .api }
95
96
**Description**: Factory class that creates appropriate formatter instances based on output format specification.
97
98
```python
99
class SafetyFormatter(FormatterAPI):
100
"""Factory formatter that delegates to specific format implementations."""
101
102
def __init__(self, output: str, **kwargs: Any) -> None:
103
"""
104
Initialize formatter factory with target output format.
105
106
Args:
107
output (str): Target output format ('json', 'html', 'bare', 'text', 'screen')
108
**kwargs: Additional configuration passed to specific formatter
109
"""
110
111
# Supported output formats
112
SUPPORTED_FORMATS = [
113
'json', # Machine-readable JSON
114
'html', # Web-viewable HTML report
115
'bare', # Minimal text output
116
'text', # Formatted text report
117
'screen' # Rich terminal output (default)
118
]
119
```
120
121
**Example Usage:**
122
123
```python
124
from safety.formatter import SafetyFormatter
125
126
# Create JSON formatter
127
json_formatter = SafetyFormatter(output='json')
128
129
# Create HTML formatter with custom template
130
html_formatter = SafetyFormatter(
131
output='html',
132
template_dir='/custom/templates'
133
)
134
135
# Create screen formatter for terminal output
136
screen_formatter = SafetyFormatter(output='screen')
137
```
138
139
## Format-Specific Implementations
140
141
### JSON Formatter { .api }
142
143
#### JsonReport Class { .api }
144
145
**Description**: Generates machine-readable JSON reports for vulnerability and license data.
146
147
```python
148
class JsonReport(FormatterAPI):
149
"""JSON formatter for machine-readable output."""
150
151
def __init__(self, version: str = "3.0", **kwargs: Any) -> None:
152
"""
153
Initialize JSON formatter.
154
155
Args:
156
version (str): JSON schema version to use
157
**kwargs: Additional configuration options
158
"""
159
160
def render_vulnerabilities(
161
self,
162
announcements: List[Dict[str, Any]],
163
vulnerabilities: List[Dict[str, Any]],
164
remediations: Dict[str, Any],
165
full: bool,
166
packages: List[Dict[str, Any]],
167
fixes: Tuple = ()
168
) -> str:
169
"""
170
Generate JSON vulnerability report.
171
172
Returns:
173
str: JSON-formatted vulnerability report
174
"""
175
176
def render_licenses(
177
self,
178
announcements: List[Dict[str, Any]],
179
licenses: List[Dict[str, Any]]
180
) -> str:
181
"""
182
Generate JSON license report.
183
184
Returns:
185
str: JSON-formatted license report
186
"""
187
```
188
189
#### JSON Report Builder { .api }
190
191
```python
192
def build_json_report(
193
announcements: List[Dict], # Platform announcements
194
vulnerabilities: List[Dict], # Vulnerability data
195
remediations: Dict[str, Any], # Remediation options
196
packages: List[Any] # Package information
197
) -> Dict[str, Any]:
198
"""
199
Build structured JSON report from scan results.
200
201
Args:
202
announcements: Platform announcements and notices
203
vulnerabilities: Detected vulnerabilities
204
remediations: Available remediation suggestions
205
packages: Scanned package information
206
207
Returns:
208
Dict[str, Any]: Structured report data ready for JSON serialization
209
"""
210
```
211
212
**JSON Report Structure:**
213
214
```json
215
{
216
"report_meta": {
217
"scan_target": "project_directory",
218
"timestamp": "2024-01-15T10:30:00Z",
219
"safety_version": "3.6.1",
220
"vulnerabilities_found": 2,
221
"vulnerabilities_ignored": 1,
222
"remediations_recommended": 1
223
},
224
"vulnerabilities": [
225
{
226
"vulnerability_id": "VULN-12345",
227
"package_name": "requests",
228
"installed_version": "2.20.0",
229
"affected_versions": ">=2.0.0,<2.20.1",
230
"advisory": "Security vulnerability description",
231
"cve": {
232
"name": "CVE-2023-12345",
233
"cvssv3": 7.5
234
},
235
"severity": {
236
"source": "safety",
237
"cvssv3": 7.5
238
},
239
"fixed_versions": ["2.20.1", "2.21.0"],
240
"ignored": false,
241
"more_info_url": "https://safety.com/vuln/12345"
242
}
243
],
244
"ignored_vulnerabilities": [...],
245
"remediations": {...},
246
"packages": [...],
247
"announcements": [...]
248
}
249
```
250
251
### Screen Formatter { .api }
252
253
#### ScreenReport Class { .api }
254
255
**Description**: Rich terminal formatter with colors, formatting, and interactive elements.
256
257
```python
258
class ScreenReport(FormatterAPI):
259
"""Rich terminal formatter for interactive console output."""
260
261
def __init__(self,
262
console=None, # Rich console instance
263
emoji: bool = True, # Enable emoji output
264
**kwargs: Any) -> None:
265
"""
266
Initialize screen formatter.
267
268
Args:
269
console: Rich console instance for output
270
emoji: Enable emoji in output
271
**kwargs: Additional configuration
272
"""
273
274
def render_vulnerabilities(self, ...) -> str:
275
"""
276
Generate rich terminal vulnerability report with:
277
- Color-coded severity levels
278
- Formatted tables and lists
279
- Progress indicators
280
- Interactive elements
281
"""
282
283
def render_licenses(self, ...) -> str:
284
"""Generate formatted license compliance report."""
285
```
286
287
**Screen Output Features:**
288
289
- **Color Coding**: Severity-based color schemes (red=critical, orange=high, etc.)
290
- **Rich Formatting**: Tables, lists, and structured layout
291
- **Emoji Support**: Visual indicators for status and severity
292
- **Progress Indicators**: Real-time scan progress
293
- **Interactive Elements**: Clickable links and expandable sections
294
295
### Text Formatter { .api }
296
297
#### TextReport Class { .api }
298
299
**Description**: Plain text formatter for non-interactive environments and file output.
300
301
```python
302
class TextReport(FormatterAPI):
303
"""Plain text formatter for file output and non-interactive terminals."""
304
305
def render_vulnerabilities(self, ...) -> str:
306
"""
307
Generate plain text vulnerability report suitable for:
308
- File output
309
- Email reports
310
- CI/CD logs
311
- Non-interactive terminals
312
"""
313
314
def render_licenses(self, ...) -> str:
315
"""Generate plain text license report."""
316
```
317
318
### HTML Formatter { .api }
319
320
#### HTMLReport Class { .api }
321
322
**Description**: HTML formatter for web-viewable reports with styling and interactivity.
323
324
```python
325
class HTMLReport(FormatterAPI):
326
"""HTML formatter for web-viewable vulnerability reports."""
327
328
def __init__(self,
329
template_dir: Optional[str] = None, # Custom template directory
330
**kwargs: Any) -> None:
331
"""
332
Initialize HTML formatter.
333
334
Args:
335
template_dir: Custom Jinja2 template directory
336
**kwargs: Additional template variables
337
"""
338
339
def render_vulnerabilities(self, ...) -> str:
340
"""
341
Generate HTML vulnerability report with:
342
- Styled tables and layouts
343
- Interactive filtering
344
- Sortable columns
345
- Embedded CSS and JavaScript
346
"""
347
```
348
349
**HTML Report Features:**
350
351
- **Responsive Design**: Mobile and desktop compatible
352
- **Interactive Tables**: Sortable and filterable vulnerability lists
353
- **Severity Styling**: Color-coded severity indicators
354
- **Embedded Assets**: Self-contained HTML files
355
- **Print Optimized**: Clean printing layouts
356
357
### Bare Formatter { .api }
358
359
#### BareReport Class { .api }
360
361
**Description**: Minimal formatter for machine processing and scripting integration.
362
363
```python
364
class BareReport(FormatterAPI):
365
"""Minimal formatter for machine processing and automation."""
366
367
def render_vulnerabilities(self, ...) -> str:
368
"""
369
Generate minimal vulnerability output:
370
- One vulnerability per line
371
- Tab or delimiter-separated fields
372
- No headers or formatting
373
- Suitable for grep, awk, etc.
374
"""
375
376
def render_licenses(self, ...) -> str:
377
"""Generate minimal license output."""
378
```
379
380
**Bare Output Format:**
381
382
```
383
package_name\tvulnerability_id\tseverity\tinstalled_version\tfixed_version
384
requests\tVULN-12345\thigh\t2.20.0\t2.20.1
385
django\tVULN-67890\tcritical\t3.0.0\t3.0.14
386
```
387
388
## Data Encoding and Serialization
389
390
### SafetyEncoder { .api }
391
392
**Description**: Custom JSON encoder for Safety-specific data types and models.
393
394
```python
395
from safety.models import SafetyEncoder
396
397
class SafetyEncoder(json.JSONEncoder):
398
"""Custom JSON encoder for Safety data structures."""
399
400
def default(self, obj: Any) -> Any:
401
"""
402
Serialize Safety-specific objects to JSON-compatible formats.
403
404
Handles:
405
- Vulnerability objects
406
- Package objects
407
- DateTime objects
408
- Custom data classes
409
410
Args:
411
obj: Object to serialize
412
413
Returns:
414
Any: JSON-serializable representation
415
"""
416
```
417
418
**Example Usage:**
419
420
```python
421
import json
422
from safety.models import SafetyEncoder
423
424
# Serialize vulnerability data with custom encoder
425
vulnerability_data = {...} # Vulnerability objects
426
json_output = json.dumps(
427
vulnerability_data,
428
cls=SafetyEncoder,
429
indent=2
430
)
431
```
432
433
## Output Utilities and Helpers
434
435
### Report Brief Information { .api }
436
437
```python
438
from safety.output_utils import get_report_brief_info, should_add_nl
439
440
def get_report_brief_info(
441
as_dict: bool = False, # Return as dictionary
442
report_type: int = 1, # Report type identifier
443
vulnerabilities_found: int = 0, # Number of vulnerabilities
444
vulnerabilities_ignored: int = 0, # Number ignored
445
remediations_recommended: Dict = None # Remediation suggestions
446
) -> Union[str, Dict[str, Any]]:
447
"""
448
Generate brief report summary information.
449
450
Args:
451
as_dict: Return structured data instead of formatted string
452
report_type: Type of report (1=vulnerability, 2=license, etc.)
453
vulnerabilities_found: Count of active vulnerabilities
454
vulnerabilities_ignored: Count of ignored vulnerabilities
455
remediations_recommended: Available remediation options
456
457
Returns:
458
Union[str, Dict]: Brief report summary
459
"""
460
461
def should_add_nl(console_output: str) -> bool:
462
"""
463
Determine if newline should be added to console output.
464
465
Args:
466
console_output (str): Console output string
467
468
Returns:
469
bool: True if newline should be added
470
"""
471
```
472
473
## Schema Versioning { .api }
474
475
### Report Schemas { .api }
476
477
Safety supports versioned report schemas for backward compatibility:
478
479
```python
480
from safety.formatters.schemas.v3_0 import ScanReportV30
481
482
class ScanReportV30(BaseModel):
483
"""Pydantic model for v3.0 scan report schema."""
484
485
report_meta: ReportMeta # Report metadata
486
vulnerabilities: List[VulnerabilityModel] # Vulnerability findings
487
ignored_vulnerabilities: List[VulnerabilityModel] # Ignored vulnerabilities
488
remediations: Dict[str, Any] # Remediation suggestions
489
packages: List[PackageModel] # Scanned packages
490
announcements: List[AnnouncementModel] # Platform announcements
491
```
492
493
## Configuration and Customization
494
495
### Formatter Configuration { .api }
496
497
Formatters can be configured through various options:
498
499
```python
500
# JSON formatter with specific version
501
json_formatter = SafetyFormatter(
502
output='json',
503
version='3.0',
504
indent=4,
505
sort_keys=True
506
)
507
508
# HTML formatter with custom templates
509
html_formatter = SafetyFormatter(
510
output='html',
511
template_dir='/path/to/custom/templates',
512
theme='dark',
513
include_css=True
514
)
515
516
# Screen formatter with custom console
517
from rich.console import Console
518
custom_console = Console(width=120, theme='github-dark')
519
520
screen_formatter = SafetyFormatter(
521
output='screen',
522
console=custom_console,
523
emoji=False
524
)
525
```
526
527
### Template Customization { .api }
528
529
HTML formatter supports custom Jinja2 templates:
530
531
```html
532
<!-- Custom vulnerability report template -->
533
<!DOCTYPE html>
534
<html>
535
<head>
536
<title>{{ report_title }} - Security Report</title>
537
<style>{{ embedded_css }}</style>
538
</head>
539
<body>
540
<h1>{{ report_title }}</h1>
541
542
{% if vulnerabilities %}
543
<table class="vulnerabilities">
544
<thead>
545
<tr>
546
<th>Package</th>
547
<th>Vulnerability</th>
548
<th>Severity</th>
549
<th>Fixed Version</th>
550
</tr>
551
</thead>
552
<tbody>
553
{% for vuln in vulnerabilities %}
554
<tr class="severity-{{ vuln.severity.lower() }}">
555
<td>{{ vuln.package_name }}</td>
556
<td>{{ vuln.vulnerability_id }}</td>
557
<td>{{ vuln.severity }}</td>
558
<td>{{ vuln.fixed_versions|join(', ') }}</td>
559
</tr>
560
{% endfor %}
561
</tbody>
562
</table>
563
{% endif %}
564
</body>
565
</html>
566
```
567
568
## Usage Examples
569
570
### Basic Report Generation
571
572
```python
573
from safety.formatter import SafetyFormatter
574
575
# Sample data
576
vulnerabilities = [...] # Vulnerability objects
577
packages = [...] # Package objects
578
announcements = [...] # Announcement objects
579
remediations = {...} # Remediation suggestions
580
581
# Generate JSON report
582
json_formatter = SafetyFormatter(output='json')
583
json_report = json_formatter.render_vulnerabilities(
584
announcements=announcements,
585
vulnerabilities=vulnerabilities,
586
remediations=remediations,
587
full=True,
588
packages=packages
589
)
590
591
# Save to file
592
with open('security-report.json', 'w') as f:
593
f.write(json_report)
594
```
595
596
### Multi-Format Reporting
597
598
```python
599
from safety.formatter import SafetyFormatter
600
601
# Generate reports in multiple formats
602
formats = ['json', 'html', 'text']
603
reports = {}
604
605
for format_type in formats:
606
formatter = SafetyFormatter(output=format_type)
607
reports[format_type] = formatter.render_vulnerabilities(
608
announcements=announcements,
609
vulnerabilities=vulnerabilities,
610
remediations=remediations,
611
full=True,
612
packages=packages
613
)
614
615
# Save each format
616
for format_type, content in reports.items():
617
filename = f'security-report.{format_type}'
618
with open(filename, 'w') as f:
619
f.write(content)
620
```
621
622
### License Reporting
623
624
```python
625
# Generate license compliance report
626
license_data = [...] # License information
627
628
formatter = SafetyFormatter(output='html')
629
license_report = formatter.render_licenses(
630
announcements=announcements,
631
licenses=license_data
632
)
633
634
# Display in terminal
635
formatter = SafetyFormatter(output='screen')
636
terminal_output = formatter.render_licenses(
637
announcements=announcements,
638
licenses=license_data
639
)
640
print(terminal_output)
641
```
642
643
### Custom JSON Processing
644
645
```python
646
from safety.formatters.json import build_json_report
647
import json
648
649
# Build structured report data
650
report_data = build_json_report(
651
announcements=announcements,
652
vulnerabilities=vulnerabilities,
653
remediations=remediations,
654
packages=packages
655
)
656
657
# Custom processing
658
report_data['custom_metadata'] = {
659
'scan_time': datetime.now().isoformat(),
660
'project_name': 'my-project',
661
'environment': 'production'
662
}
663
664
# Serialize with custom options
665
json_output = json.dumps(
666
report_data,
667
cls=SafetyEncoder,
668
indent=2,
669
sort_keys=True,
670
ensure_ascii=False
671
)
672
```
673
674
This comprehensive formatter documentation covers all aspects of Safety CLI's output and formatting system, enabling developers to generate reports in their preferred format and integrate security scanning results into their workflows and tooling.