v0lka/ipi-check
GitHub: v0lka/ipi-check
ipi-check 是一款检测 AI 编程 Agent 指令文件中间接提示注入风险并对 Agent 技能进行安全审计的 SAST 扫描器。
Stars: 0 | Forks: 0
# ipi-check — 间接提示注入与技能安全扫描器
[](https://github.com/v0lka/ipi-check/actions/workflows/ci.yml)
[](./LICENSE)
[](https://www.python.org/)
[](https://sarifweb.azurewebsites.net/)
一款 SAST 扫描器,可检测 AI agent 指令文件和源代码中的**间接提示注入**([OWASP LLM01](https://genai.owasp.org/llmrisk/llm01-prompt-injection/)),并执行**agent 技能安全审计**——检测 AI agent 技能目录(基于 `SKILL.md`)中的恶意行为(凭证窃取、远程执行、权限滥用、隐蔽性)。它将确定性静态分析与可选的 LLM 分类器相结合,并以 SARIF v2.1.0 格式输出结果——可随时上传至 GitHub Code Scanning、GitLab SAST 或任何支持 SARIF 的查看器。

## 概述
基于 LLM 的编程 agent 会读取项目本地的指令文件,如 `AGENTS.md`、`.cursorrules`、`CLAUDE.md` 和 `.windsurfrules`。控制这些文件(或将内容偷渡到源代码注释中)的攻击者可以劫持 agent——窃取机密、运行破坏性命令或篡改其行为。`ipi-check` 可扫描代码库,检测此类攻击在字节级和语言级的信号。
该扫描器围绕一个**三阶段流水线**构建:
1. **阶段 A — 静态分析(始终开启)。** 字节级检查、正则表达式模式匹配和语义启发式分析生成带有确定性置信度评分的单文件静态结果。
2. **阶段 B — LLM 分类(可选)。** 当配置了 LLM provider 时,经过净化处理的文件内容将被转发至模型,以获取结构化的分类结论。
3. **阶段 C — 技能安全审计(自动)。** 当发现 `SKILL.md` 文件时,将使用专用的聚焦恶意软件的静态模式(IPI401–411)分析技能单元,并进行针对恶意行为的可选 LLM 分类。
确定性的置信度融合矩阵将所有阶段结合起来,得出最终结论:`PASS`、`REVIEW_REQUIRED` 或 `BLOCK`。
## 功能
- **字节级隐藏内容检测** — ANSI 转义序列、Unicode 标签字符(E0000–E007F)、变体选择符、bidi 覆盖、零宽字符、私有区码位、同形异义字。
- **正则表达式注入模式** — 指令覆盖(多语言:EN/RU/CN/FR/ES/DE/JP/KR)、权限声明(包括 CVE-2025-53773 `chat.tools.autoApprove`)、破坏性 shell 命令、数据渗出(包括会话泄露)、shell 注入、越狱人格(STAN/DUDE/token system/角色扮演)、社会工程学、混淆(base64 解码 / payload 分割)以及指令矛盾(文件内规则否定)。
- **语义启发式分析** — Shannon 熵、不可见内容比率、指令密度、极性矛盾检测。
- **通过 [LiteLLM](https://github.com/BerriAI/litellm) 进行可选的 LLM 分类**
— 兼容 OpenAI、Anthropic、Azure、Bedrock、Ollama 或任何 LiteLLM
provider。
- **SARIF v2.1.0 输出** — 兼容 GitHub Code Scanning、GitLab、
VS Code SARIF Viewer 和 Sonar。
- **AI agent 文件感知** — 自动发现 `.cursorrules`、
`.windsurfrules`、`.clinerules`、`AGENTS.md`、`CLAUDE.md`、
`copilot-instructions.md`、`.cursor/**/*.mdc` 等文件。
- **SVG 文件扫描** — 将 SVG 文件纳入扫描目标,检测 ``、`` 和 `` 元素中的提示注入。
- **LLM 预处理净化** — 在 LLM 处理之前中和不可见字符、ANSI 转义序列、base64 编码块和 ROT13 混淆文本;防止分类器本身受到提示注入。
- **批量 LLM 分类** — 针对源代码文件,自动对超大文件进行分块,并在出现部分失败时使用指数退避重试。
- **Agent 技能安全审计** — 自动检测基于 `SKILL.md` 的技能目录,并扫描其中的凭证收集、远程代码执行、权限提升、保密性指令及其他恶意行为。
- **特定技能检测模式(IPI401–411)** — 用于混淆代码、动态上下文滥用、隐藏 HTML 指令和过度权限的专用正则表达式模式。
- **技能 LLM 分类** — 专用的技能分类器 prompt 专注于阴影特性检测(无法从技能描述中推断出的行为)。
- **针对符号链接的路径遍历保护**。
- **基于 Pygments 的代码提取** — 将注释和字符串字面量从源文件中分离出来进行针对性扫描。
## 安装说明
### 从 PyPI 安装
```
# 待办:pip install ipi-check
```
### 从源码安装
```
git clone https://github.com/v0lka/ipi-check.git
cd ipi-check
pip install -e .
```
### Docker
```
# 待办:docker pull ghcr.io/v0lka/ipi-check:latest
# 或在本地构建:
docker build -t ipi-check .
```
需要 Python 3.12+。
## 快速开始
### 案例 1 — 仅静态分析
无 LLM provider;使用字节、模式和启发式阶段。
```
ipi-check scan ./my-repo
```
### 案例 2 — 静态分析 + LLM 分类
```
ipi-check scan ./my-repo \
--llm-model gpt-4o-mini \
--llm-api-token "${OPENAI_API_KEY}"
```
将 SARIF 写入文件而不是标准输出:
```
ipi-check scan ./my-repo --output results.sarif
```
`--llm-api-token` 的值支持 `${VAR_NAME}` 展开,因此凭证无需在命令行中直接出现。
## CLI 参考
```
ipi-check scan REPO_PATH [OPTIONS]
```
| 参数 / 选项 | 类型 | 默认值 | 描述 |
| ----------------------- | ------ | ------------ | ---------------------------------------------------------------------------------- |
| `REPO_PATH` | path | _(必填)_ | 要扫描的代码库目录。支持 `${VAR}` 展开。 |
| `--llm-base-url URL` | string | `None` | LiteLLM base URL(用于自托管 / 代理 endpoint)。 |
| `--llm-model NAME` | string | `None` | LLM 模型名称,例如 `gpt-4o-mini`、`claude-3-5-sonnet`。省略则禁用阶段 2。 |
| `--llm-api-token TOKEN` | string | `None` | LLM provider 的 API token。支持 `${VAR}` 展开。 |
| `--output PATH` | path | stdout | 将 SARIF 写入文件(紧凑型 JSON)。如果扩展名不是 `.sarif` 则会发出警告。 |
| `--quiet` | flag | `false` | 隐藏 stderr 上的横幅、进度和摘要行。 |
| `--no-gitignore` | flag | `false` | 禁用感知 `.gitignore` 的文件排除功能。 |
| `--exclude PAT` | string | _(无)_ | 要排除的 Glob 模式(gitignore 语法)。可重复使用。 |
| `--version` | flag | — | 打印版本号并退出。 |
| `-h`, `--help` | flag | — | 显示帮助信息并退出。 |
## 退出代码
| 代码 | 含义 |
| ---- | ----------------------------------------------------------------------------- |
| `0` | 扫描成功完成。SARIF 已输出。发现的问题(如果有)包含在 SARIF 中。 |
| `1` | 运行时错误(I/O 故障、流水线崩溃等)。请查看 stderr。 |
| `2` | 使用错误(路径无效、缺少参数、输出目录错误)。 |
`ipi-check` 在干净的扫描中始终以 `0` 退出,无论结论如何——
SARIF 报告会携带发现的问题,将门禁决策留给您的 CI。
## 输出格式
输出格式为 [SARIF v2.1.0](https://docs.oasis-open.org/sarif/sarif/v2.1.0/os/sarif-v2.1.0-os.html)。
每个发现的问题都包含文件 URI、违规规则 ID、静态置信度、LLM 结论(如果有)以及人类可读的消息。
```
{
"$schema": "https://json.schemastore.org/sarif-2.1.0.json",
"version": "2.1.0",
"runs": [
{
"tool": {
"driver": {
"name": "ipi-check",
"version": "0.1.0",
"semanticVersion": "0.1.0",
"informationUri": "https://github.com/v0lka/ipi-check",
"rules": [
{
"id": "IPI101",
"name": "IPI101",
"shortDescription": {
"text": "Instruction override pattern — attempts to bypass rules"
},
"fullDescription": {
"text": "Instruction override pattern — attempts to bypass rules"
},
"defaultConfiguration": { "level": "warning" },
"helpUri": "https://github.com/v0lka/ipi-check"
}
]
}
},
"invocations": [
{
"executionSuccessful": true,
"startTimeUtc": "2026-06-06T12:00:00Z",
"endTimeUtc": "2026-06-06T12:00:05Z"
}
],
"results": [
{
"ruleId": "IPI101",
"level": "error",
"message": {
"text": "Instruction override pattern — attempts to bypass rules",
"markdown": "**IPI101** at line 3, column 1: Instruction override pattern — attempts to bypass rules (matched: `ignore all previous instructions`)"
},
"locations": [
{
"physicalLocation": {
"artifactLocation": { "uri": ".cursorrules" },
"region": {
"startLine": 3,
"startColumn": 1
}
}
}
]
}
]
}
]
}
```
## 本地 Git Hook(阻断式 `post-checkout`)
`ipi-check` 本身始终以 `0` 退出,以便其 SARIF 能被 CI 门禁使用。
对于本地开发,您可以将其包装在一个小的 bash 脚本中,该脚本会解析结论摘要,并在遇到 `BLOCK` 时以非零值退出。该包装脚本位于
[`scripts/ipi-check-hook.sh`](./scripts/ipi-check-hook.sh),适用于
任何客户端 hook(`post-checkout`、`post-merge`、`post-rewrite`、
`pre-commit` 等)。
**关于阻断语义。** `post-checkout` 在工作树更新_之后_运行,因此非零退出无法回滚检出操作。然而,它会作为 `git checkout` 的退出状态传播,这足以让 shell、IDE 集成和链式自动化将此次检出视为失败并中止后续步骤。如果您需要_阻止_状态更改(而不是事后标记),请将相同的包装脚本安装为
`pre-commit` 或 `pre-push` hook——它们才是真正的预防性 hook。
### 全局安装(每台机器一次)
使用 Git 的 `core.hooksPath` 将机器上的所有代码库指向一个统一的 hooks 目录:
```
# 1. 为你的 global hooks 选择一个目录。
mkdir -p ~/.githooks
# 2. 将 wrapper 作为 post-checkout hook 放入(必须确切命名为
# 'post-checkout' 并且是可执行的)。
curl -fsSL https://raw.githubusercontent.com/v0lka/ipi-check/main/scripts/ipi-check-hook.sh \
-o ~/.githooks/post-checkout
chmod +x ~/.githooks/post-checkout
# 3. 告诉 Git 在这台机器上的所有 repository 中使用它。
git config --global core.hooksPath ~/.githooks
```
如果您拥有此代码库的本地克隆,请直接复制脚本:
```
cp scripts/ipi-check-hook.sh ~/.githooks/post-checkout
chmod +x ~/.githooks/post-checkout
```
### Hook 的作用
在每次分支检出(`branch_flag == 1`)时,包装脚本会:
1. 如果不在 git 工作树中、设置了 `IPI_CHECK_HOOK_DISABLE=1` 或 `ipi-check` 二进制文件不可用,则跳过(失败放行)。
2. 运行 `ipi-check scan "$REPO_ROOT" --output .git/ipi-check-last.sarif`。
3. 向您的终端重新输出扫描器横幅和摘要。
4. 解析 `BLOCK: N, REVIEW_REQUIRED: M, PASS: K` 摘要行。
5. **当 `BLOCK > 0` 时退出并返回 `1`**,将 SARIF 报告留在 `.git/ipi-check-last.sarif` 以供检查。
### 调整 Hook
包装脚本会遵循以下环境变量:
| 变量 | 默认值 | 效果 |
| --------------------------- | ----------- | ----------------------------------------------------------------- |
| `IPI_CHECK_HOOK_DISABLE` | `0` | 设置为 `1` 可完全跳过扫描(一次性禁用)。 |
| `IPI_CHECK_BLOCK_ON_REVIEW` | `0` | 设置为 `1` 可在 `REVIEW_REQUIRED > 0` 时同样判定为失败。 |
| `IPI_CHECK_BIN` | `ipi-check` | 覆盖二进制文件路径,例如指向虚拟环境或固定版本。 |
示例 — 在一次检出中临时绕过 hook:
```
IPI_CHECK_HOOK_DISABLE=1 git checkout some-branch
```
示例 — 使 `REVIEW_REQUIRED` 也具有阻断性,以实现更严格的本地门禁:
```
git config --global hook.ipi-check.blockOnReview true # for documentation
export IPI_CHECK_BLOCK_ON_REVIEW=1 # actually used by the hook
```
### 对单个代码库禁用
每个代码库的 `core.hooksPath` 会覆盖全局设置:
```
cd /path/to/repo
git config --local core.hooksPath .git/hooks # restore the default
```
## CI/CD 集成
### GitHub — Code Scanning 上传
```
name: ipi-check
on: [push, pull_request]
jobs:
scan:
runs-on: ubuntu-latest
permissions:
security-events: write
contents: read
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
python-version: "3.12"
- run: pip install ipi-check
- run: ipi-check scan . --output ipi-check.sarif
- uses: github/codeql-action/upload-sarif@v3
with:
sarif_file: ipi-check.sarif
```
### GitLab — SAST 报告
```
ipi-check:
image: python:3.12-slim
stage: test
script:
- pip install ipi-check
- ipi-check scan . --output gl-sast-report.sarif
artifacts:
reports:
sast: gl-sast-report.sarif
```
## Docker 使用方法
将要扫描的代码库挂载为数据卷,并让容器在标准输出产生 SARIF:
```
docker run --rm -v "$(pwd):/repo" ipi-check scan /repo
```
通过环境变量持久化报告并传递 API token:
```
docker run --rm \
-v "$(pwd):/repo" \
-e OPENAI_API_KEY \
ipi-check scan /repo \
--llm-model gpt-4o-mini \
--llm-api-token '${OPENAI_API_KEY}' \
--output /repo/results.sarif
```
## 开发
```
git clone https://github.com/v0lka/ipi-check.git
cd ipi-check
python -m venv .venv
source .venv/bin/activate
pip install -e ".[dev]"
```
运行测试套件、linter 和类型检查器:
```
pytest tests/ -v --tb=short
ruff check src/ tests/
mypy src/
```
## 架构
`ipi-check` 由一小组专注的模块组成:
- `core/types.py` — 共享数据类型、枚举和 dataclass。
- `scanner/file_discovery.py` — 定位 AI agent 文件和源文件。
- `scanner/byte_analysis.py` — 检测隐藏/恶意字节。
- `scanner/pattern_matching.py` — 基于正则表达式的注入签名 + 技能特定模式。
- `scanner/semantic_heuristics.py` — 熵、密度、不可见性比率。
- `scanner/static_result.py` — 汇编静态结果并计算严重程度(按文件和按技能)。
- `scanner/code_extractor.py` — Pygments 驱动的注释/字符串提取。
- `scanner/llm_sanitizer.py` — 在 LLM 调用之前中和对抗性内容(不可见字符、ANSI、base64、ROT13)。
- `scanner/token_counter.py` — 基于 tiktoken 的 token 计数,用于批处理组装。
- `scanner/llm_classifier.py` — 基于 LiteLLM 的分类器(单文件、批处理和按技能)。
- `/confidence_fusion.py` — 确定性结论矩阵(按文件和按技能)。
- `scanner/pipeline.py` — 端到端编排(非技能文件 + 技能审计)。
- `reporter/sarif_reporter.py` — SARIF v2.1.0 输出。
- `cli/main.py` — argparse CLI 入口点。
完整的架构和决策记录位于 [`specs/`](./specs/):
[`system-overview.md`](./specs/architecture/system-overview.md)、
[`security-model.md`](./specs/architecture/security-model.md)、
以及 [`specs/decisions/`](./specs/decisions/) 中的 ADR。
## 许可证
[MIT](./LICENSE) © 2026 Vladimir Kochetkov。
标签:AI安全, Chat Copilot, DLL 劫持, Python, SAST, StruQ, 大语言模型, 无后门, 盲注攻击, 请求拦截, 逆向工具, 错误基检测, 静态代码分析