samsepassi1/aiscan

GitHub: samsepassi1/aiscan

一款混合 AST 与 LLM 的安全扫描器,专门检测 AI 编程助手高频引入的代码漏洞模式并提供 AI 与人类缺陷率归因分析。

Stars: 2 | Forks: 0

# aiscan — AI 代码安全扫描器 **作者:** Sam Sepassi 一款结合了 AST 与 LLM 的混合型安全扫描器,专为 AI 编程助手(如 GitHub Copilot、Cursor、ChatGPT 和 Claude)在统计上极易引入的漏洞模式而构建。 ## 为什么选择 aiscan? 研究表明,AI 生成的代码存在以下问题: - **高出 322% 的概率** 缺少授权检查 (CWE-639) - **88% 的失败率** 出现在日志注入/输出中和 (CWE-117) - **86% 的失败率** 出现在 XSS/不当输出编码 (CWE-79) - 大量存在硬编码机密信息、弱加密和不安全反序列化问题 传统的 SAST 工具并未针对这些模式进行优化。而 aiscan 正是为此而生。 ## 安装 由于 PyPI 上的 `aiscan` 名称已被一个不相关的项目占用,因此请直接 从本代码仓库进行安装: ``` pip install git+https://github.com/samsepassi1/aiscan@v0.2.3 ``` 或者通过替换 `@v0.2.3` 来指定特定的 commit 或分支。本地 开发安装: ``` git clone https://github.com/samsepassi1/aiscan && cd aiscan pip install -e ".[dev]" ``` ## 快速开始 ``` # 扫描当前目录(仅 AST 规则) aiscan scan . # 使用 LLM 分析层级进行扫描 (Anthropic) export ANTHROPIC_API_KEY="your-key" aiscan scan . --llm # 使用 OpenAI 进行扫描 export OPENAI_API_KEY="your-key" aiscan scan . --llm --llm-provider openai --llm-model gpt-4o # 使用本地 Ollama 的 Zero-egress 模式 aiscan scan . --llm --llm-provider local --llm-model codellama # Self-hosted Ollama endpoint aiscan scan . --llm --llm-provider local \ --llm-base-url http://localhost:11434/v1 \ --llm-model codellama # 输出 SARIF 用于 GitHub Security 标签页 aiscan scan . --format sarif --output results.sarif # 如果存在 HIGH+ 及以上级别的发现则 Exit code 1(适用于 CI/CD) aiscan scan . --severity HIGH --exit-code # 仅扫描 git 变更文件(pre-commit hook) aiscan scan . --diff-only --exit-code # LLM 审查每个文件,而不仅仅是具有 AST 发现的文件(更深度的扫描,成本更高) aiscan scan . --llm --llm-scan-all # 配置 LLM 请求超时和单文件行数预算 aiscan scan . --llm --llm-timeout 90 --llm-max-lines 800 # 覆盖 LLM response 缓存位置(默认:平台用户缓存目录) aiscan scan . --llm --cache-dir ./.aiscan_cache ``` ## 行内抑制 在代码行添加 `aiscan: suppress` 注释即可抑制该行的所有发现。 该注释语法会根据文件所使用的语言自动匹配: ``` # Python API_KEY = os.environ["API_KEY"] # aiscan: suppress not a real secret ``` ``` // JavaScript / TypeScript const token = process.env.TOKEN; // aiscan: suppress already env-sourced /* C-style block comments also work */ const key = process.env.KEY; /* aiscan: suppress */ ``` 在 `suppress` 后可以附带一个可选的理由,该理由将被记录在发现的 `suppression_reason` 字段中,并显示在 SARIF 和 JSON 输出中。 抑制功能是 **语言感知** 的:`#` 仅在 Python 中被识别, 而 `//`/`/* */` 在 JavaScript、TypeScript、Go 和 Java 中被识别。 Python 文件中的 `//` 是整数除法,而非注释,因此不会 触发任何抑制——同理,JS/TS 文件中的 `#` 也是如此。 ## 路径级别排除 在扫描目标根目录下放置一个 `.aiscanignore` 文件,即可声明式地 排除特定路径(其前缀语义与 `--exclude` 相同,每行一个条目)。 以 `#` 开头的行被视为注释。 ``` # .aiscanignore tests/fixtures vendor generated ``` `.aiscanignore` 中的条目将与任何 `--exclude` 参数合并生效。 ## 指标 — AI 与人类缺陷率 `aiscan metrics` 会运行一次扫描,使用 `git blame` 将每个发现映射到 最近修改过该代码的 commit,并根据 `Co-Authored-By` 尾部信息、commit 消息标记 (例如 `Generated with [Claude Code]`)以及作者邮箱域名, 将该 commit 归类为 AI 生成或人类编写。发现结果将按严重程度和 规则分类汇总到 `ai` / `human` / `unknown` 三个统计桶中。 ``` # 整个 repo 的 Terminal 报告 aiscan metrics . --severity HIGH # 用于 dashboards / CI 的 JSON 输出 aiscan metrics . --format json --output metrics.json # 仅归因于当前变更文件中的发现 aiscan metrics . --diff-only ``` 可识别的 AI 智能体包括:Claude、Copilot、Cursor、ChatGPT、Gemini、Devin、 aider。LLM 层级在 `metrics` 模式下会被禁用,因此在 CI 中运行是完全免费的。 被抑制的发现不会计入统计桶中。未提交的代码行(已暂存或已修改) 将归因于 `unknown`,并注明原因为 `uncommitted`。 输出示例: ``` Findings by origin ╭────────────┬────────┬────────┬───────┬───────┬───────┬───────┬───────╮ │ Origin │ Count │ % │ Crit │ High │ Medi │ Low │ Info │ ├────────────┼────────┼────────┼───────┼───────┼───────┼───────┼───────┤ │ AI │ 30 │ 60% │ 18 │ 12 │ 0 │ 0 │ 0 │ │ HUMAN │ 20 │ 40% │ 7 │ 13 │ 0 │ 0 │ 0 │ │ UNKNOWN │ 0 │ 0% │ 0 │ 0 │ 0 │ 0 │ 0 │ ╰────────────┴────────┴────────┴───────┴───────┴───────┴───────┴───────╯ ``` ### 检测覆盖率及其对您仓库的意义 归因分析仅在 commit 包含 aiscan 可识别的 AI 标记时才会触发: 一个指名已知智能体的 `Co-Authored-By:` 尾部信息,类似 `Generated with [Claude Code]` 的正文标记,或者作者邮箱匹配 AI 供应商的域名(例如 `noreply@anthropic.com`, `@copilot.github.com`,`@cursor.sh`)。这在此实践中的意义在于: - **Claude Code** 会自动在 commit 中添加 `Co-Authored-By: Claude`, 并且开箱即用。 - **GitHub Copilot、Cursor、ChatGPT、aider 以及大多数其他工具** 在 默认情况下 *不会* 为 commit 打标签。在团队使用这些工具 但未添加尾部信息的代码库中,AI 编写的代码大部分会被归入 `human` 统计桶中,因为缺乏可以区分的信号。 如果您希望在没有 AI 工具默认打标签的代码库中获得有意义的覆盖率, 最简单的解决方法是使用单行的共享 commit 模板 或 git hook,以便在使用了 AI 时附加 `Co-Authored-By:` 尾部信息。 一旦您的团队采用了这种方法,`aiscan metrics` 就会成为一个随时间推移 真实反映人类与 AI 缺陷率趋势的曲线。 在没有明确标记的情况下的启发式检测(commit 消息的语言 模式、PR 作者分析等)已在开发路线图上,但尚未包含在 v0.2.3 中 ——这种方法虽然提高了覆盖率,但也带来了更高的误报率, 因此 v1 版本的实现将优先采用基于标记的高精度方案。 ## 检测规则 | 规则 ID | 名称 | 严重程度 | CWE | |---------|------|----------|-----| | AI-SEC-001 | 硬编码机密信息 | CRITICAL | CWE-259, CWE-321, CWE-798 | | AI-SEC-002 | 缺少授权检查 | CRITICAL | CWE-862, CWE-306 | | AI-SEC-003 | 弱加密算法 | HIGH | CWE-327, CWE-328 | | AI-SEC-004 | 不安全的随机数生成器 | HIGH | CWE-330, CWE-338 | | AI-SEC-008 | 不安全的反序列化 | CRITICAL | CWE-502 | | AI-SEC-009 | 动态代码执行 | CRITICAL | CWE-78, CWE-94 | | AI-SEC-011 | 路径遍历 | HIGH | CWE-22 | | AI-SEC-012 | 宽松的 CORS | HIGH | CWE-942 | | AI-SEC-013 | SSR 状态注水注入 | CRITICAL | CWE-79, CWE-116 | | AI-SEC-014 | 危险的内部 HTML | HIGH | CWE-79, CWE-80 | | AI-SEC-015 | 服务端 Fetch 中的 SSRF | HIGH | CWE-918 | | AI-SEC-016 | 不安全的 Cookie 标志 | HIGH | CWE-614, CWE-1004, CWE-1275 | | AI-SEC-017 | 弱内容安全策略 | HIGH | CWE-693, CWE-1021 | | AI-SEC-018 | 通过不可信输入的 Prompt 注入 | HIGH | CWE-20, CWE-94, CWE-77 | ### SSR / Electrode 覆盖范围 规则 **AI-SEC-013 至 AI-SEC-017** 专门针对在服务端渲染代码库 (Electrode、Next.js、自定义 Node/React SSR)中常见且 AI 编程助手经常引入的漏洞模式: - **AI-SEC-013** 捕获最危险的 SSR 特定 XSS:使用 `JSON.stringify` 将状态内联到 `