技术教程 · 阅读约 10 分钟

AI 安全漏洞分析自动化:用 Claude 写一个能读懂你整个代码库的安全审计工具

AI 安全漏洞分析自动化:用 Claude 写一个能读懂你整个代码库的安全审计工具

很多人用 AI 审代码,都是把文件一段段粘贴进去,然后问"这里有没有安全问题"。

这种方式有个根本缺陷:AI 看不到上下文。一个函数里的 SQL 拼接是不是漏洞,取决于调用方有没有做输入校验。割裂地看,误报率极高,真正的问题反而容易漏掉。

HN 今天有篇讨论:"AI is breaking two vulnerability cultures",核心观点是 AI 正在同时冲击两种安全文化——一种是"靠人工慢慢审",另一种是"靠规则引擎扫描"。AI 的优势在于能理解语义,但前提是你得把足够的上下文喂给它。

下面这个工具就是解决这个问题的:把整个 repo 的结构和关键文件一次性组织好,交给 Claude 做系统性安全审计


思路

1. 递归遍历项目,过滤掉无关文件(node_modules、.git 等)

2. 优先读取高风险文件:路由定义、数据库操作、认证逻辑、配置文件

3. 构建一个结构化的 prompt,包含文件树 + 关键文件内容

4. 调用 Claude API,要求输出结构化的漏洞报告

整个流程大概 50 行 Python,跑一次能覆盖中小型项目的主要风险面。


完整代码


import os

import json

from pathlib import Path

from openai import OpenAI



# 无量Api 兼容 OpenAI 格式,改一行 base_url 即可

client = OpenAI(

    api_key="your-api-key",  # 在 api2everything.xyz 注册获取

    base_url="https://api2everything.xyz/v1"

)



# 高风险文件关键词,优先读取

HIGH_RISK_PATTERNS = [

    "route", "router", "controller",

    "auth", "login", "token", "jwt",

    "db", "database", "query", "model",

    "config", "env", "setting",

    "upload", "file", "exec", "shell"

]



SKIP_DIRS = {

    "node_modules", ".git", "__pycache__", ".venv",

    "venv", "dist", "build", ".next", "coverage"

}



SKIP_EXTS = {

    ".png", ".jpg", ".jpeg", ".gif", ".svg", ".ico",

    ".lock", ".sum", ".min.js", ".map"

}



def should_skip(path: Path) -> bool:

    for part in path.parts:

        if part in SKIP_DIRS:

            return True

    if path.suffix in SKIP_EXTS:

        return True

    return False



def is_high_risk(path: Path) -> bool:

    name = path.stem.lower()

    return any(pattern in name for pattern in HIGH_RISK_PATTERNS)



def collect_files(root: str, max_files: int = 60):

    root_path = Path(root)

    all_files = []

    high_risk_files = []



    for p in root_path.rglob("*"):

        if p.is_file() and not should_skip(p):

            rel = p.relative_to(root_path)

            if is_high_risk(p):

                high_risk_files.append(rel)

            else:

                all_files.append(rel)



    # 高风险文件优先,总数控制在 max_files 以内

    selected = high_risk_files[:max_files // 2]

    remaining = max_files - len(selected)

    selected += all_files[:remaining]

    return sorted(selected), high_risk_files



def build_file_tree(root: str, files: list) -> str:

    lines = [f"项目根目录: {Path(root).name}/"]

    for f in files:

        lines.append(f"  {f}")

    return "\n".join(lines)



def read_file_safe(path: Path, max_chars: int = 3000) -> str:

    try:

        content = path.read_text(encoding="utf-8", errors="ignore")

        if len(content) > max_chars:

            content = content[:max_chars] + "\n... [文件过长,已截断]"

        return content

    except Exception as e:

        return f"[读取失败: {e}]"



def build_prompt(root: str, files: list, high_risk: list) -> str:

    root_path = Path(root)

    tree = build_file_tree(root, files)



    sections = [

        "你是一名资深安全工程师,请对以下代码库进行安全审计。",

        "",

        "## 项目文件结构",

        "```",

        tree,

        "```",

        "",

        "## 高风险文件内容(重点审查)",

    ]



    for rel in high_risk[:20]:  # 最多读 20 个高风险文件

        full_path = root_path / rel

        content = read_file_safe(full_path)

        sections += [

            f"\n### {rel}",

            "```",

            content,

            "```"

        ]



    sections += [

        "",

        "## 审计要求",

        "请按以下格式输出报告,每个问题单独列出:",

        "",

        "**[严重程度: 高/中/低]** 漏洞类型",

        "- 位置: 文件名:行号(如能判断)",

        "- 描述: 具体问题",

        "- 利用方式: 攻击者如何利用",

        "- 修复建议: 具体代码级别的修复方案",

        "",

        "重点关注:SQL注入、XSS、CSRF、不安全的反序列化、",

        "硬编码密钥、路径遍历、命令注入、不当的权限校验、",

        "敏感信息泄露、不安全的依赖。",

        "",

        "最后给出一个总体安全评分(0-100)和优先修复建议。"

    ]



    return "\n".join(sections)



def audit_repo(repo_path: str, model: str = "claude-sonnet-4-5"):

    print(f"正在扫描项目: {repo_path}")

    files, high_risk = collect_files(repo_path)

    print(f"共发现 {len(files)} 个文件,其中高风险文件 {len(high_risk)} 个")



    prompt = build_prompt(repo_path, files, high_risk)

    print(f"Prompt 长度: {len(prompt)} 字符,正在调用 Claude...")



    response = client.chat.completions.create(

        model=model,

        messages=[

            {"role": "user", "content": prompt}

        ],

        max_tokens=4096,

        temperature=0.2  # 安全审计需要确定性输出

    )



    report = response.choices[0].message.content



    # 保存报告

    output_file = Path(repo_path) / "security_audit_report.md"

    output_file.write_text(f"# 安全审计报告\n\n{report}", encoding="utf-8")

    print(f"\n报告已保存至: {output_file}")

    print("\n" + "="*60)

    print(report)



if __name__ == "__main__":

    import sys

    path = sys.argv[1] if len(sys.argv) > 1 else "."

    audit_repo(path)


使用方式


pip install openai

python audit.py /path/to/your/project

报告会直接生成在项目根目录下的 security_audit_report.md


几个实际跑出来的发现

在一个 Express + MongoDB 的项目上跑了一次,Claude 找到了:

  • 一个 find() 查询直接把 req.query 传进去,NoSQL 注入风险
  • JWT secret 写在代码里而不是环境变量
  • 文件上传接口没有校验 MIME 类型,只校验了扩展名

这三个问题,传统的静态扫描工具(ESLint security 插件)只发现了第三个。


关于模型选择和成本

这类任务 prompt 比较长(通常 8k-20k token),用官方 Claude API 跑一次大概要 $0.5-1。

我现在用的是 无量Api,同样的 claude-sonnet-4-5,价格是官方的 35% 左右,注册还送 ¥1 余额可以直接试。接入方式就是改一行 base_url,代码里已经写好了,直接用就行。

对于这种批量跑、频繁迭代的工具脚本,省下来的成本还是很可观的。


扩展方向

  • 接入 GitHub Actions,每次 PR 自动触发审计
  • 对比两次审计结果,只报告新增漏洞
  • 针对特定框架(Django、Spring)定制高风险文件识别规则

代码逻辑不复杂,核心是上下文组织的方式。AI 能不能给出有价值的结论,80% 取决于你怎么喂数据。

有问题或者跑出来有意思的发现,评论区聊聊。觉得有用的话点个赞,后续还会写更多这类可以直接跑的工具。