Python依赖安全检查 Python 依赖安全检查 —— 自动扫描项目依赖中的已知漏洞涵盖 safety、pip-audit、Dependabot 模拟和漏洞数据库查询# 安装依赖pip install safety pip-audit# 依赖安全是软件供应链安全的重要环节需定期检查第三方包中的已知漏洞import subprocessimport jsonimport osimport sysimport tempfileimport refrom typing import List, Dict, Optional, Tuplefrom dataclasses import dataclass, field, asdictfrom datetime import datetime# 第一部分数据结构 dataclassclass Vulnerability:漏洞信息的数据结构package_name: strinstalled_version: strvulnerable_versions: stradvisory: str # 漏洞描述cve_id: str # CVE 编号如 CVE-2024-XXXXseverity: str # 严重级别CRITICAL/HIGH/MEDIUM/LOWfixed_version: str # 修复版本more_info_url: str # 更多信息链接def __str__(self) - str:return f[{self.severity}] {self.cve_id}: {self.package_name} \f{self.installed_version} - {self.fixed_version}dataclassclass ScanResult:扫描结果vulnerabilities: List[Vulnerability] field(default_factorylist)scanned_packages: int 0scan_time: str is_clean: bool Truedef summary(self) - str:生成扫描结果摘要critical sum(1 for v in self.vulnerabilities if v.severity CRITICAL)high sum(1 for v in self.vulnerabilities if v.severity HIGH)medium sum(1 for v in self.vulnerabilities if v.severity MEDIUM)if self.is_clean:return f安全扫描了 {self.scanned_packages} 个包未发现已知漏洞return f发现 {len(self.vulnerabilities)} 个漏洞 \f严重{critical}高{high}中{medium}# 第二部分Safety 集成 class SafetyScanner:使用 safety 库扫描 Python 依赖中的已知漏洞。safety 使用 pyup.io 的漏洞数据库免费版有延迟商业版实时更新。staticmethoddef scan_requirements(requirements_file: str) - ScanResult:扫描 requirements.txt 文件中的依赖。result ScanResult(scan_timedatetime.now().isoformat())if not os.path.exists(requirements_file):print(f文件不存在{requirements_file})return resulttry:# 调用 safety check 命令cmd [sys.executable, -m, safety, check,-r, requirements_file,--json, # JSON 格式输出--full-report # 完整报告]output subprocess.check_output(cmd, stderrsubprocess.STDOUT, timeout60).decode()# 解析 safety 输出result SafetyScanner._parse_safety_output(output)result.scan_time datetime.now().isoformat()return resultexcept subprocess.TimeoutExpired:print(Safety 扫描超时)except FileNotFoundError:print(Safety 未安装请运行pip install safety)except subprocess.CalledProcessError as e:# safety 在发现漏洞时返回非零退出码if e.returncode 255:print(Safety 内部错误)else:return SafetyScanner._parse_safety_output(e.output.decode())except Exception as e:print(fSafety 扫描异常{e})return resultstaticmethoddef _parse_safety_output(output: str) - ScanResult:解析 safety 的 JSON 输出为 ScanResult。result ScanResult(scan_timedatetime.now().isoformat())try:data json.loads(output)for vuln in data.get(vulnerabilities, []):vulnerability Vulnerability(package_namevuln.get(package_name, unknown),installed_versionvuln.get(installed_version, ),vulnerable_versionsvuln.get(vulnerable_spec, ),advisoryvuln.get(advisory, ),cve_idvuln.get(cve, N/A),severityvuln.get(severity, UNKNOWN),fixed_versionvuln.get(fixed_version, ),more_info_urlvuln.get(more_info_url, ))result.vulnerabilities.append(vulnerability)result.scanned_packages data.get(scanned_packages, 0)result.is_clean len(result.vulnerabilities) 0except json.JSONDecodeError:print(解析 safety 输出失败)return result# 第三部分pip-audit 集成 class PipAuditScanner:使用 pip-audit 扫描依赖漏洞。pip-audit 是 PyPA 官方推荐的依赖安全检查工具使用 PyPI 的 JSON API 获取漏洞信息。staticmethoddef scan_project(project_dir: str .) - ScanResult:扫描项目的所有依赖。result ScanResult(scan_timedatetime.now().isoformat())try:cmd [sys.executable, -m, pip_audit,--project-dir, project_dir,--format, json,--desc, # 包含漏洞描述]output subprocess.check_output(cmd, stderrsubprocess.STDOUT, timeout120).decode()result PipAuditScanner._parse_output(output)return resultexcept subprocess.TimeoutExpired:print(pip-audit 扫描超时)except FileNotFoundError:print(pip-audit 未安装请运行pip install pip-audit)except subprocess.CalledProcessError as e:return PipAuditScanner._parse_output(e.output.decode())except Exception as e:print(fpip-audit 扫描异常{e})return resultstaticmethoddef scan_package_list(packages: List[str]) - ScanResult:扫描指定的包列表。# 创建一个临时文件包含包列表with tempfile.NamedTemporaryFile(modew, suffix.txt,deleteFalse) as f:f.write(\n.join(packages))tmp_file f.nametry:cmd [sys.executable, -m, pip_audit,-r, tmp_file,--format, json]output subprocess.check_output(cmd, stderrsubprocess.STDOUT, timeout60).decode()return PipAuditScanner._parse_output(output)finally:os.unlink(tmp_file)staticmethoddef _parse_output(output: str) - ScanResult:解析 pip-audit 的 JSON 输出。result ScanResult(scan_timedatetime.now().isoformat())try:data json.loads(output)for pkg in data.get(dependencies, []):vulns pkg.get(vulns, [])for vuln in vulns:vulnerability Vulnerability(package_namepkg.get(name, unknown),installed_versionpkg.get(version, ),vulnerable_versionsvuln.get(vulnerable_versions, ),advisoryvuln.get(description, ),cve_idvuln.get(id, N/A),severityvuln.get(severity, UNKNOWN),fixed_versionvuln.get(fixed_version, ),more_info_urlvuln.get(url, ))result.vulnerabilities.append(vulnerability)result.scanned_packages 1result.is_clean len(result.vulnerabilities) 0except json.JSONDecodeError:print(解析 pip-audit 输出失败)return result# 第四部分Dependabot 模拟 class DependabotSimulator:模拟 GitHub Dependabot 的核心功能1. 解析依赖文件2. 检查版本兼容性3. 生成更新建议staticmethoddef analyze_dependency_file(filepath: str) - Dict:分析依赖文件生成类似 Dependabot 的报告。if not os.path.exists(filepath):return {error: f文件不存在{filepath}}with open(filepath, r) as f:content f.read()# 解析依赖dependencies []for line in content.split(\n):line line.strip()if line and not line.startswith(#):# 处理 、、 等版本约束match re.match(r([\w\-_])\s*([!~])\s*([\w.]), line)if match:dependencies.append({name: match.group(1),operator: match.group(2),version: match.group(3)})return {file: filepath,dependency_count: len(dependencies),dependencies: dependencies,analysis_time: datetime.now().isoformat(),recommendations: DependabotSimulator._generate_recommendations(dependencies)}staticmethoddef _generate_recommendations(deps: List[Dict]) - List[Dict]:生成依赖更新建议。recommendations []for dep in deps:# 检查是否使用了精确版本固定建议使用范围约束if dep.get(operator) :recommendations.append({package: dep[name],current: f{dep[operator]}{dep[version]},suggestion: f{dep[version]},{int(dep[version].split(.)[0]) 1}.0.0,reason: 建议使用范围约束以接收补丁更新})return recommendations# 第五部分综合演示 def demo_dependency_audit():演示依赖安全检查的完整流程。print( 依赖安全检查演示 \n)# 创建示例 requirements 文件req_content # 项目依赖flask2.2.3requests2.28.1django3.2.18cryptography38.0.3pyyaml5.4req_file demo_requirements.txtwith open(req_file, w) as f:f.write(req_content)# 使用 Dependabot 模拟器分析print(--- Dependabot 依赖分析 ---)analysis DependabotSimulator.analyze_dependency_file(req_file)print(f发现 {analysis[dependency_count]} 个依赖)for rec in analysis.get(recommendations, []):print(f 建议{rec[package]} {rec[current]} - {rec[suggestion]})# 扫描结果汇总print(\n--- 安全建议 ---)print(定期执行依赖安全检查的最佳实践1. 每次 CI/CD 构建时自动扫描2. 使用 pip-audit 作为主要的漏洞检测工具3. 启用 GitHub Dependabot 自动创建更新 PR4. 订阅安全公告邮件列表如 oss-security5. 及时更新受影响的依赖包6. 使用锁定文件requirements.txt 或 Pipfile.lock)# 清理临时文件if os.path.exists(req_file):os.unlink(req_file)if __name__ __main__:demo_dependency_audit()