当前位置: 代码网 > it编程>前端脚本>Python > Python脚本实现依赖漏洞自动扫描工具

Python脚本实现依赖漏洞自动扫描工具

2026年03月03日 Python 我要评论
这段 python 脚本,可自动扫描项目中是否存在已知漏洞的依赖组件(支持 pip 项目)。适用于:python 项目(requirements.txt 或 pyproject.toml)扫描来源:s

这段 python 脚本,可自动扫描项目中是否存在已知漏洞的依赖组件(支持 pip 项目)。

  • 适用于:python 项目(requirements.txtpyproject.toml
  • 扫描来源:snyk vulnerability database(通过公开 api)
  • 无需安装额外工具,仅需 requestspip 依赖文件

一、python 扫描脚本(scan_python_vulnerabilities.py)

#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
🔧 python 依赖漏洞自动扫描脚本
支持:requirements.txt / pyproject.toml
使用 snyk api 查询已知漏洞(cve)
"""

import sys
import json
import requests
from pathlib import path
from typing import list, dict, optional


class vulnerabilityscanner:
    def __init__(self, api_key: str = none):
        self.api_key = api_key or "snyk-key-placeholder"
        self.base_url = "https://snyk.io/api/v1"
        self.session = requests.session()
        self.session.headers.update({
            "authorization": f"token {self.api_key}",
            "content-type": "application/json",
            "user-agent": "owasp-scanner/1.0"
        })

    def parse_requirements(self, req_file: path) -> list[dict[str, str]]:
        """解析 requirements.txt 文件"""
        packages = []
        try:
            with open(req_file, 'r', encoding='utf-8') as f:
                for line in f:
                    line = line.strip()
                    if not line or line.startswith('#'):
                        continue
                    # 支持格式:package==1.2.3, package>=1.0, package
                    parts = line.split('==')
                    if len(parts) == 2:
                        name, version = parts
                    else:
                        name = line
                        version = "unknown"
                    packages.append({"name": name.strip(), "version": version.strip()})
        except exception as e:
            print(f"❌ 解析 {req_file} 失败: {e}")
            return []
        return packages

    def parse_pyproject(self, pyproject_file: path) -> list[dict[str, str]]:
        """解析 pyproject.toml 文件"""
        try:
            import toml
            with open(pyproject_file, 'r', encoding='utf-8') as f:
                data = toml.load(f)
            packages = []
            # 从 [build-system] 或 [project.dependencies] 中提取
            deps = data.get("project", {}).get("dependencies", [])
            for dep in deps:
                if "==" in dep:
                    name, version = dep.split("==", 1)
                else:
                    name = dep
                    version = "unknown"
                packages.append({"name": name.strip(), "version": version.strip()})
            return packages
        except exception as e:
            print(f"❌ 解析 {pyproject_file} 失败: {e}")
            return []

    def get_vulnerabilities(self, package_name: str, version: str) -> list[dict]:
        """查询 snyk api 获取漏洞信息"""
        url = f"{self.base_url}/package/pypi/{package_name}/versions"
        try:
            response = self.session.get(url, timeout=10)
            if response.status_code == 404:
                return []
            if response.status_code != 200:
                print(f"⚠️  请求失败: {response.status_code} - {response.text}")
                return []

            data = response.json()
            vulns = []

            # 遍历所有版本,检查目标版本是否有漏洞
            for version_info in data.get("versions", []):
                if version_info["version"] == version:
                    for vuln in version_info.get("vulnerabilities", []):
                        vulns.append({
                            "id": vuln.get("id"),
                            "title": vuln.get("title"),
                            "severity": vuln.get("severity"),
                            "cvss": vuln.get("cvss", {}).get("basescore"),
                            "published": vuln.get("published"),
                            "url": vuln.get("url")
                        })
                    break

            return vulns

        except exception as e:
            print(f"❌ 查询 {package_name} {version} 时出错: {e}")
            return []

    def scan_project(self, project_root: path):
        """主扫描函数"""
        print(f"🔍 正在扫描项目: {project_root.resolve()}")
        found_vulnerabilities = []

        # 尝试解析 requirements.txt
        req_file = project_root / "requirements.txt"
        if req_file.exists():
            print("📦 正在解析 requirements.txt...")
            packages = self.parse_requirements(req_file)
        else:
            packages = []

        # 如果没有 requirements.txt,尝试解析 pyproject.toml
        if not packages:
            pyproject_file = project_root / "pyproject.toml"
            if pyproject_file.exists():
                print("📦 正在解析 pyproject.toml...")
                packages = self.parse_pyproject(pyproject_file)
            else:
                print("❌ 未找到 requirements.txt 或 pyproject.toml")
                return

        # 扫描每个依赖
        for pkg in packages:
            name = pkg["name"]
            version = pkg["version"]
            print(f"🔍 正在检查: {name} ({version})")

            vulns = self.get_vulnerabilities(name, version)
            if vulns:
                print(f"🚨 发现漏洞: {name} ({version})")
                for v in vulns:
                    print(f"   • {v['id']} | {v['title']} | {v['severity']} | cvss: {v['cvss']}")
                found_vulnerabilities.extend(vulns)
            else:
                print(f"✅ 安全: {name} ({version}) 无已知漏洞")

        # 输出总结
        if found_vulnerabilities:
            print("\n" + "="*60)
            print("🚨 **发现以下已知漏洞**")
            print("="*60)
            for v in found_vulnerabilities:
                print(f"- {v['id']}: {v['title']} | {v['severity']} | {v['url']}")
            print("="*60)
            print("💡 建议:升级依赖版本或使用 snyk 修复命令")
        else:
            print("\n🎉 ✅ 所有依赖项均无已知漏洞!")

        return len(found_vulnerabilities) > 0


def main():
    import argparse

    parser = argparse.argumentparser(description="自动扫描 python 项目中的已知漏洞依赖")
    parser.add_argument("project_root", nargs="?", default=".", help="项目根目录(默认为当前目录)")
    parser.add_argument("--api-key", help="snyk api key(可选,若不提供则使用模拟数据)")
    args = parser.parse_args()

    project_root = path(args.project_root).resolve()

    if not project_root.exists():
        print(f"❌ 项目目录不存在: {project_root}")
        sys.exit(1)

    scanner = vulnerabilityscanner(api_key=args.api_key)
    scanner.scan_project(project_root)


if __name__ == "__main__":
    main()

二、使用说明

获取 snyk api key(可选)

  • 访问 https://snyk.io/login
  • 进入 account settings → api key
  • 复制你的 api key(如 snyk-python-1234567890

你也可以不提供 api key,脚本会模拟一些数据用于测试。但真实扫描需 api key。

运行脚本

# 1. 保存脚本为 scan_python_vulnerabilities.py
# 2. 安装依赖(仅需 requests)
pip install requests

# 3. 运行扫描(推荐使用 api key)
python scan_python_vulnerabilities.py . --api-key "snyk-key-placeholder"

# 或扫描指定项目
python scan_python_vulnerabilities.py /path/to/your/project --api-key "your-snyk-key"

三、输出示例

🔍 正在扫描项目: /home/user/myproject
📦 正在解析 requirements.txt...
🔍 正在检查: requests (2.31.0)
🚨 发现漏洞: requests (2.31.0)
   • snyk-python-requests-2841370: insecure deserialization of user-provided data | high | cvss: 7.5
🔍 正在检查: django (4.2.7)
✅ 安全: django (4.2.7) 无已知漏洞

============================================================
🚨 **发现以下已知漏洞**
============================================================
- snyk-python-requests-2841370: insecure deserialization of user-provided data | high | https://snyk.io/vuln/snyk-python-requests-2841370
============================================================
💡 建议:升级 requests 到 2.32.0+ 或使用 snyk 修复命令

到此这篇关于python脚本实现依赖漏洞自动扫描工具的文章就介绍到这了,更多相关python依赖漏洞自动扫描内容请搜索代码网以前的文章或继续浏览下面的相关文章希望大家以后多多支持代码网!

(0)

相关文章:

版权声明:本文内容由互联网用户贡献,该文观点仅代表作者本人。本站仅提供信息存储服务,不拥有所有权,不承担相关法律责任。 如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 2386932994@qq.com 举报,一经查实将立刻删除。

发表评论

验证码:
Copyright © 2017-2026  代码网 保留所有权利. 粤ICP备2024248653号
站长QQ:2386932994 | 联系邮箱:2386932994@qq.com