transilienceai/whitney

GitHub: transilienceai/whitney

开源静态 AI 安全扫描器,填补通用规则在间接提示注入上的检测空白。

Stars: 1 | Forks: 0

# Whitney **开源静态 AI 安全扫描器 — 发现传统扫描器遗漏的提示注入模式,且不在每次运行时消耗 LLM API 信用。** Whitney 是精选的 [Semgrep](https://semgrep.dev) 规则集、轻量 Python 包装器、可选 LLM-as-judge 分类层以及 AI 依赖 SBOM 提取器的组合。零自定义 SAST。默认路径零 LLM 调用。目前仅支持 Python(路线图包含 TS/JS/Go)。 ``` pip install whitney whitney scan ./your-repo ``` ``` SEVERITY FILE :LINE TITLE critical app/handlers/chat.py :42 LangChain SQLDatabaseChain with user-controlled question (P2SQL) critical app/main.py :89 pandas_dataframe_agent.run with user input — arbitrary code exec high app/api.py :17 Flask handler interpolates request.json into LLM prompt high app/rag.py :55 WebBaseLoader fetched content flows into LLM chain unfiltered ``` ## 检测范围 **15 种源类型**的提示注入: | 类别 | 覆盖的源类型 | |---|---| | **直接注入** | `direct_http`(Flask/FastAPI/Django)、`direct_cli`(argparse/click/stdin)、`direct_voice`(Whisper/Twilio SpeechResult) | | **间接获取** | `indirect_rag`(Chroma/Pinecone/Weaviate/pgvector)、`indirect_web_fetch`(requests/WebBaseLoader/SeleniumURLLoader)、`indirect_file_upload`(PyPDFLoader/UnstructuredFileLoader)、`indirect_email`(SES/SNS)、`indirect_search`(Tavily/SerpAPI/Brave/Google CSE) | | **间接代理** | `indirect_tool_response`(LangChain 工具返回值)、`indirect_mcp`(MCP call_tool 响应)、`indirect_a2a`(CrewAI/LangGraph 代理间上下文交接) | | **间接存储** | `indirect_db_stored`(数据库查询结果进入提示)、`indirect_memory_stored`(Mem0/Zep/LangChain 内存回放) | | **跨模态** | `cross_modal_image_ocr`(pytesseract/easyocr)、`cross_modal_unicode`(标签块/ZWJ/同形异义字) | 此外,**仅凭存在性触发的关键汇点**:LangChain `PALChain` / `PythonAstREPLTool`(CVE-2023-36258 类别)以及 `SQLDatabaseChain` / `create_sql_agent` / `NLSQLTableQueryEngine`(P2SQL 类别)。 并提供一个 **AI 依赖 SBOM**(`whitney sbom`)—— 针对 `requirements.txt`、`pyproject.toml`、`package.json` 以及 `model=...` 赋值中发现的 AI SDK 与模型的 CycloneDX 1.5 清单,包含一个已知易受攻击的 SDK 版本对照表。 ## 存在原因 通用静态 AI 规则集(Semgrep `p/ai-best-practices`、Bandit、Agent Audit)仅能捕获约 30–50% 的真实提示注入模式,且对间接注入(RAG 检索、Web 获取、工具响应、代理间上下文交接)完全不可见。Whitney 旨在填补这一空白。 | 扫描器 | 语料召回 | 语料精确率 | 语料 F1 | 在 6 个真实 AI 仓库上的发现 | |---|---|---|---|---| | **Whitney 默认(无 LLM)** | **1.000** | 0.897 | 0.945 | **47** | | **Whitney 分类(可选)** | **1.000** | **1.000** | **1.000** | **47** | | Semgrep `p/ai-best-practices` | 0.500 | 0.867 | 0.634 | 14 | | Agent Audit 0.18.2 | 0.308 | 0.571 | 0.400 | — | | Bandit / Semgrep `p/security-audit` | 0.000 | — | — | 0 | 语料库包含 **35 个手工标注的测试用例**(26 个正例 + 9 个负例),覆盖全部 15 种源类型,每个用例附带包含 `source_url`、`source_commit`、`vuln_subtype` 与标注理由的 YAML 侧车文件。可通过 `python -m tests.corpus.eval` 在本地复现。 在 **5 个从未用于开发规则的盲测仓库** — `aimaster-dev/chatbot-using-rag-and-langchain`、`Lizhecheng02/RAG-ChatBot`、`SachinSamuel01/rag-langchain-streamlit`、`streamlit/example-app-langchain-rag`、`Vigneshmaradiya/ai-agent-comparison` — 中,Whitney 产生 11 个发现,其中 9 个为真阳性、2 个为开发者在 `main()` 测试脚手架中的假阳性。**精确率达 81.8%,经人工审计。** 完整审计表位于 `tests/corpus/DIFFERENTIAL.md`。 ## 已知防御机制 仅当在不可信内容到达 LLM **之前** 调用了 **供应商防护栏** 或 **正确的 LLM-as-judge 分类** 时,Whitney 才会抑制告警: - AWS Bedrock Guardrails(`apply_guardrail`、`GuardrailIdentifier=` 在 `invoke_model` 上) - Azure AI 内容安全 / 提示防护(`ContentSafetyClient.detect_jailbreak`) - Lakera Guard(`api.lakera.ai` 或 SDK 调用) - NeMo Guardrails(`LLMRails.generate`、包装式调用) - DeepKeep AI 防火墙(`dk_request_filter`) - OpenAI 审核(`client.moderations.create`) - 正确的 LLM-as-judge(通过可选分类层判定;详见 [`docs/TRIAGE.md`](docs/TRIAGE.md)) **明确不计入的弱防御**:正则/Pydantic 字符串校验、长度限制、关键词黑名单、系统提示警告。均可通过 Unicode、同形异义字、Base64、语言切换或改写绕过。Whitney 仍会在 `details["defense_present"]` 中记录其存在,以便修复建议指向更强的替代方案。 ## 架构 ``` whitney/ ├── scanner.py # public scan_repository(path) entry point ├── semgrep_runner.py # subprocess wrapper around Semgrep CLI ├── llm_triage.py # opt-in LLM-as-judge classifier (Claude Opus or mock) ├── sbom.py # AI dependency SBOM scanner (CycloneDX 1.5) ├── models.py # stdlib @dataclass Finding (no pydantic) ├── cli.py # `whitney scan|sbom|version` command-line interface └── rules/ ├── prompt_injection_taint.yaml # Semgrep taint mode — flow detection ├── prompt_injection_critical_sinks.yaml # PAL chain + SQL chain — presence alone └── prompt_injection_structural.yaml # CrewAI / LLMChain / WebBaseLoader idioms ``` 三个 Semgrep 规则文件,各具不同的检测理念: 1. **`prompt_injection_taint.yaml`** — 单一整合污点规则。50+ 模式源、25+ 模式净化、40+ 模式汇点,配合针对 UI/存储形态的精确 `pattern-not` 排除。仅在实际数据流存在漏洞时捕获直接与间接注入。 2. **`prompt_injection_critical_sinks.yaml`** — AST 模式规则,针对**仅凭存在即关键**的汇点(无需污点流):PAL 链、SQL 链、任意代码路径的工具调用执行器。 3. **`prompt_injection_structural.yaml`** — 针对**代码结构层面**的漏洞 AST 规则(而非数据流):CrewAI `Task(..., context=[upstream_task])` 代理交接、LangChain `LLMChain` 惯用写法、`WebBaseLoader` + 链、`PdfReader` + LLM。 每条规则均通过 `pattern-not-inside: def $F(...): ... $BEDROCK.apply_guardrail(...) ...` 对已识别防御机制实现函数级抑制。 总计 Python 代码约 800 行,分布在 `scanner.py`、`semgrep_runner.py`、`llm_triage.py`、`sbom.py`、`models.py`、`cli.py` 中。无自定义污点引擎、无 Tree-sitter 遍历器、无自定义 AST 分析。全部工作由 Semgrep 完成。 ## 零 LLM 默认,可选分类 默认扫描路径(`whitney scan ./repo` 或未设置环境变量时的 `scan_repository(path)`)**零 LLM 调用**,并在各次运行间产生字节级一致的输出。可选模式在此基础上叠加: ``` # 默认 — 仅 Semgrep,无 API 调用,完全可复现 whitney scan ./my-repo # 模拟分诊 — 结构启发式用于 LLM-as-judge 正确性,无 API 调用 WHITNEY_STRICT_JUDGE_PROMPTS=1 WHITNEY_TRIAGE_MOCK=1 whitney scan ./my-repo # 实际分诊 — 调用 Claude Opus 对 LLM-as-judge 提示进行分类 export ANTHROPIC_API_KEY=sk-ant-... WHITNEY_STRICT_JUDGE_PROMPTS=1 whitney scan ./my-repo ``` 真实模式分类使用 `claude-opus-4-6` 在 `temperature=0` 下进行,判决结果以 `(model_id, prompt_version, code_hash)` 为键缓存,因此对未变更代码的重复扫描无需消耗成本。分类调用上限为每次扫描 50 次,错误时 fail-open。完整操作说明、成本估算与故障排查请参见 [`docs/TRIAGE.md`](docs/TRIAGE.md)。 ## 路径排除 路径排除自动应用于:`venv`、`.venv`、`env``__pycache__`、`node_modules`、`tests`、`test_*`、`fixtures`、`examples`、`docs`、`dist`、`build`、`site-packages`。测试固件内部的发现视为开发者视角的假阳性,无论其技术正确性如何。 ## 输出内容 ``` from whitney import scan_repository findings = scan_repository("./my-repo") for f in findings: print(f.severity.value, f.check_id, f.details["file_path"], f.details["line_number"]) print(" CWE:", f.details["cwe"]) print(" OWASP LLM Top 10:", f.details["owasp"]) print(" OWASP Agentic:", f.details["owasp_agentic"]) ``` 每个发现包含: - `check_id`、`title`、`description`、`severity`、`remediation` - `details.file_path`、`details.line_number`、`details.end_line`、`details.code_snippet` - `details.cwe` — 例如 `["CWE-94"]` - `details.owasp` — OWASP LLM Top 10 年份特定标签,例如 `["LLM01:2025"]` - `details.owasp_agentic` — OWASP 代理 Top 10,例如 `["AA01:2026"]` - `details.confidence` — `HIGH` / `MEDIUM` / `LOW` - `details.technology` — 规则中识别的框架 CWE 与两项 OWASP 家族直接在规则 YAML 元数据中提供;监管框架增强(ISO 42001、EU AI Act、NIST AI RMF、MITRE ATLAS)由消费 Whitney 输出的下游工具负责;[Shasta](https://github.com/transilienceai/shasta) 合规包是其中一种消费者。 ## 已知限制 - **仅限单文件。** Semgrep OSS 污点分析仅限于过程内与单文件。跨文件流程(污点源在 `handlers/chat.py`、LLM 汇点在 `services/llm.py`)无法追踪。经验证在 3 个真实仓库中所有漏洞路径均为单文件,但大型单体仓库可能需要 Semgrep Pro 或未来的结构规则扩展。 - **仅 Python。** TypeScript / JavaScript / Go 支持在路线图上;一旦补全源/汇分类体系,规则编写方式可直接迁移。 - **防护策略验证不在范围内。** 若开发者调用 `bedrock.apply_guardrail(GuardrailIdentifier="xxx")`,Whitney 假定策略 `"xxx"` 确实覆盖提示注入。验证策略内容需在扫描时拉取 AWS 策略定义。 - **盲测中 2 个已知 FP 模式。** 若开发者 `main()` 测试脚手架中包含硬编码查询且辅助文件导入 RAG 检索器,可能产生假阳性。在从未扫描的真实代码上精确率达 81.8%,保留 `def main():` 入口点以确保合法 CLI 应用仍被捕获的代价。 ## 参见 - [**docs/TRIAGE.md**](docs/TRIAGE.md) — 可选 LLM-as-judge 分类层的操作指南 - [**docs/SCANNER.md**](docs/SCANNER.md) — 规则文件的深层架构说明 - [**tests/corpus/DIFFERENTIAL.md**](tests/corpus/DIFFERENTIAL.md) — 与所有通用扫描器的完整基准对比 - [**tests/corpus/README.md**](tests/corpus/README.md) — 标注语料库的结构说明与新增用例方法 - [**Shasta**](https://github.com/transilienceai/shasta) — 云端 AI 治理姊妹项目(涵盖 AWS Bedrock / SageMaker、Azure OpenAI / ML)、监管框架映射(ISO 42001、EU AI Act、NIST AI RMF、MITRE ATLAS)、策略生成、仪表板与报告 ## 许可证 Apache-2.0。参见 [LICENSE](LICENSE)。
标签:AI依赖SBOM, AI安全, AI安全扫描, Chat Copilot, GraphQL安全矩阵, LLM-as-judge, PB级数据处理, prompt注入检测, Python安全工具, SAST, Semgrep规则集, URL发现, XML 请求, 云安全监控, 代码安全, 安全专业人员, 安全合规, 安全开发, 安全扫描器, 安全扫描工具, 安全检测, 安全漏洞检测, 安全运维, 开源安全扫描, 提示注入, 漏洞枚举, 盲注攻击, 网络代理, 逆向工具, 集群管理, 静态分析