0
# Fix Resolution
1
2
Functionality for resolving and applying fixes to vulnerable dependencies. This module handles the process of determining appropriate fix versions and coordinating upgrades.
3
4
## Capabilities
5
6
### Fix Version Classes
7
8
Base classes representing fix versions and their resolution status.
9
10
```python { .api }
11
@dataclass(frozen=True)
12
class FixVersion:
13
"""
14
Represents an abstract dependency fix version.
15
16
This class cannot be constructed directly.
17
"""
18
19
dep: ResolvedDependency
20
"""
21
The dependency that needs to be fixed.
22
"""
23
24
def is_skipped(self) -> bool:
25
"""
26
Check whether the FixVersion was unable to be resolved.
27
28
Returns:
29
True if this is a SkippedFixVersion, False otherwise
30
"""
31
```
32
33
### Resolved Fix Version
34
35
Represents a successfully resolved fix version.
36
37
```python { .api }
38
@dataclass(frozen=True)
39
class ResolvedFixVersion(FixVersion):
40
"""
41
Represents a dependency fix version that was successfully resolved.
42
"""
43
44
version: Version
45
"""
46
The resolved fix version to upgrade to.
47
"""
48
```
49
50
### Skipped Fix Version
51
52
Represents a fix version that could not be resolved.
53
54
```python { .api }
55
@dataclass(frozen=True)
56
class SkippedFixVersion(FixVersion):
57
"""
58
Represents a dependency fix version that was unable to be resolved.
59
"""
60
61
skip_reason: str
62
"""
63
The reason why this fix version could not be resolved.
64
"""
65
```
66
67
### Exceptions
68
69
Exception classes related to fix resolution.
70
71
```python { .api }
72
class FixResolutionImpossible(Exception):
73
"""
74
Raised when resolve_fix_versions fails to find a fix version without known vulnerabilities.
75
"""
76
```
77
78
### Fix Resolution Functions
79
80
Functions for resolving fix versions from vulnerability results.
81
82
```python { .api }
83
def resolve_fix_versions(
84
dependency_results: Iterator[tuple[Dependency, list[VulnerabilityResult]]],
85
service: VulnerabilityService,
86
state: AuditState = AuditState(),
87
) -> Iterator[FixVersion]:
88
"""
89
Resolve fix versions for vulnerabilities.
90
91
Takes dependency audit results and determines appropriate fix versions
92
for each vulnerable dependency.
93
94
Parameters:
95
- dependency_results: Iterator of (dependency, vulnerabilities) tuples
96
- service: VulnerabilityService to query for additional information
97
- state: AuditState for progress tracking
98
99
Returns:
100
Iterator of FixVersion objects (either ResolvedFixVersion or SkippedFixVersion)
101
"""
102
```
103
104
## Usage Examples
105
106
### Basic Fix Resolution
107
108
```python
109
from pip_audit._fix import resolve_fix_versions
110
from pip_audit._audit import Auditor
111
from pip_audit._dependency_source import PipSource
112
from pip_audit._service import PyPIService
113
114
# Perform audit and resolve fixes
115
service = PyPIService()
116
source = PipSource()
117
auditor = Auditor(service=service)
118
119
# Get audit results
120
audit_results = auditor.audit(source)
121
122
# Resolve fix versions
123
fix_versions = resolve_fix_versions(audit_results, service)
124
125
for fix_version in fix_versions:
126
if fix_version.is_skipped():
127
print(f"Cannot fix {fix_version.dep.name}: {fix_version.skip_reason}")
128
else:
129
print(f"Fix {fix_version.dep.name} v{fix_version.dep.version} -> v{fix_version.version}")
130
```
131
132
### Applying Fixes
133
134
```python
135
from pip_audit._fix import resolve_fix_versions
136
from pip_audit._audit import Auditor
137
from pip_audit._dependency_source import RequirementSource
138
from pip_audit._service import PyPIService
139
140
# Setup components
141
service = PyPIService()
142
source = RequirementSource("requirements.txt")
143
auditor = Auditor(service=service)
144
145
# Get vulnerabilities and resolve fixes
146
audit_results = list(auditor.audit(source))
147
vulnerable_results = [(dep, vulns) for dep, vulns in audit_results if vulns]
148
149
if vulnerable_results:
150
print(f"Found {len(vulnerable_results)} packages with vulnerabilities")
151
152
# Resolve fix versions
153
fix_versions = list(resolve_fix_versions(iter(vulnerable_results), service))
154
155
# Apply fixes
156
for fix_version in fix_versions:
157
if not fix_version.is_skipped():
158
try:
159
source.fix(fix_version)
160
print(f"Fixed {fix_version.dep.name} -> v{fix_version.version}")
161
except Exception as e:
162
print(f"Failed to fix {fix_version.dep.name}: {e}")
163
else:
164
print(f"Skipped {fix_version.dep.name}: {fix_version.skip_reason}")
165
```
166
167
### Fix Resolution with State Tracking
168
169
```python
170
from pip_audit._fix import resolve_fix_versions
171
from pip_audit._audit import Auditor
172
from pip_audit._dependency_source import PyProjectSource
173
from pip_audit._service import OsvService
174
from pip_audit._state import AuditState
175
176
# Custom state handler for progress tracking
177
class ProgressTracker:
178
def update_state(self, message: str, logs: str | None = None):
179
print(f"Progress: {message}")
180
181
def initialize(self):
182
print("Starting fix resolution...")
183
184
def finalize(self):
185
print("Fix resolution completed.")
186
187
# Setup with state tracking
188
tracker = ProgressTracker()
189
state = AuditState(members=[tracker])
190
191
service = OsvService()
192
source = PyProjectSource("pyproject.toml")
193
auditor = Auditor(service=service)
194
195
# Perform audit with state tracking
196
state.initialize()
197
audit_results = auditor.audit(source)
198
199
# Resolve fixes with progress updates
200
fix_versions = resolve_fix_versions(audit_results, service, state=state)
201
202
resolved_count = 0
203
skipped_count = 0
204
205
for fix_version in fix_versions:
206
if fix_version.is_skipped():
207
skipped_count += 1
208
else:
209
resolved_count += 1
210
211
state.finalize()
212
print(f"Summary: {resolved_count} fixes resolved, {skipped_count} skipped")
213
```
214
215
### Advanced Fix Strategy
216
217
```python
218
from pip_audit._fix import resolve_fix_versions
219
from pip_audit._audit import Auditor
220
from pip_audit._dependency_source import PipSource
221
from pip_audit._service import PyPIService
222
from packaging.version import Version
223
224
def choose_best_fix_version(vulnerabilities, available_versions):
225
"""
226
Custom logic to choose the best fix version from available options.
227
"""
228
# Get all fix versions from vulnerabilities
229
all_fix_versions = set()
230
for vuln in vulnerabilities:
231
all_fix_versions.update(vuln.fix_versions)
232
233
# Filter to versions that fix all vulnerabilities
234
valid_fixes = []
235
for version in all_fix_versions:
236
fixes_all = True
237
for vuln in vulnerabilities:
238
if version not in vuln.fix_versions:
239
fixes_all = False
240
break
241
if fixes_all:
242
valid_fixes.append(version)
243
244
# Choose the minimum version that fixes all issues
245
return min(valid_fixes) if valid_fixes else None
246
247
# Example usage
248
service = PyPIService()
249
source = PipSource()
250
auditor = Auditor(service=service)
251
252
# Get audit results and apply custom fix resolution
253
for dependency, vulnerabilities in auditor.audit(source):
254
if vulnerabilities:
255
# Use custom logic to determine best fix
256
best_fix = choose_best_fix_version(vulnerabilities, [])
257
if best_fix:
258
print(f"Recommended fix for {dependency.name}: v{best_fix}")
259
else:
260
print(f"No suitable fix found for {dependency.name}")
261
```