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` 将状态内联到 `