shisa-ai/textguard
GitHub: shisa-ai/textguard
面向LLM系统的恶意文本防御库,专注于Unicode规范化、编码走私检测及多语言文本清洗。
Stars: 1 | Forks: 0
# textguard
面向 LLM 相关系统的恶意文本规范化、检查与清洗。
`textguard` 将可复用的文本防御工作从 [`shisad`](https://github.com/shisa-ai/shisad) 中提取出来,成为一个独立的 Python 包,可以扫描和清洗不受信任的文本输入——包括提示词、Markdown、技能文件和其他内容——而无需引入守护进程或框架依赖。
- 合法多语言 Unicode 文本是一等用例,因此我们**不会**默认将所有内容转换为 ASCII。
- 有损转换始终显式选择加入。
- 所有解码路径都有深度限制和扩展限制。
- 发现结果带有严重性级别(`info`、`warn`、`error`)——没有不透明的聚合风险评分。
- 该包不会静默下载模型或发起网络请求。
## 安装
```
pip install textguard # core only, zero dependencies
pip install 'textguard[yara]' # + pattern-based detection
pip install 'textguard[all]' # everything
```
或使用 uv:
```
uv pip install textguard
uv tool install textguard # CLI-only, isolated
uvx textguard scan SKILL.md # one-shot, no install
```
## CLI
CLI 的行为类似于 UNIX 文本工具:通过管道传递文本,在 Shell 脚本中使用,或指向文件。默认用法与 `cat` 或 `sed` 相当:从 stdin 或文件读取,将清洗后的输出写入 stdout。
CLI 和 [Python API](#python-api) 均专为代理工作流设计。在不受信任的输入到达模型之前对其进行扫描,或将其作为流水线的一部分进行内联清洗。
```
# 扫描:只读,报告发现
textguard scan SKILL.md
textguard scan SKILL.md --json
textguard scan docs/*.md --json > report.json
# 清理:输出清理后的文本
textguard clean SKILL.md # cleaned text to stdout
textguard clean SKILL.md -i # overwrite in place
textguard clean SKILL.md -o out.md # write to file
textguard clean SKILL.md -i --report # overwrite, human-readable report to stderr
cat untrusted.txt | textguard clean - # pipe from stdin
# 预设
textguard clean SKILL.md --preset strict
textguard clean SKILL.md --preset ascii
# 可选后端
textguard scan --yara-rules ./rules/ SKILL.md
textguard scan --no-yara-bundled SKILL.md
textguard scan --split-tokens SKILL.md
textguard scan --promptguard ~/.local/share/textguard/models/promptguard2/ SKILL.md
```
### 预设
预设控制清洗的激进程度:
| 预设 | 规范化 | 剥离 | 解码 | 用例 |
|--------|--------------|--------|---------|----------|
| **default** | NFC | 标签字符、软连字符、空白折叠、组合标记上限 | 否 | 对包括中日韩(CJK)在内的所有多语言文本均安全 |
| **strict** | NFKC | 所有不可见字符、双向控制字符、变体选择器、标签字符、软连字符 | 全部七层 | 技能文件、提示词、隐藏内容可疑的上下文 |
| **ascii** | NFKC + ASCII 音译 | 所有非 ASCII 字符 | 全部七层 | 当您明确想要仅 ASCII 输出时 |
默认预设保留了合法的多语言文本。NFKC 不是默认选项,因为它会破坏日语和其他文字中的语义内容。Strict 和 ascii 预设选择加入 progressively 更激进的清洗。
扫描时的分析有意比重写时的清洗更激进。`scan()` 始终剥离恶意格式并展开有界编码进行分析;预设控制 `clean()` 将哪些内容重写为返回的输出。
分词(split-token)走私检测是可选的。通过 `TextGuard(split_tokens=True)`、`TEXTGUARD_SPLIT_TOKENS=1`、配置文件 `split_tokens = true` 或 CLI `--split-tokens` 启用它。
`scan` 的退出代码反映结果中的最强信号:结构发现映射为 `0` 无、`1` 信息、`2` 警告、`3` 错误;语义层级映射为 `0` 无、`1` 中、`2` 高、`3` 严重。跨子命令的运行时故障返回 `4`。
## 防护层级
textguard 组织为三个层级。每一层都在前一层的基础上增加检测能力。
| 层级 | 安装 | 检测内容 | 占用 |
|------|---------|----------------|-----------|
| **Core** | `textguard` | 不可见字符、双向控制滥用、标签字符、软连字符、变体选择器、Zalgo、同形异义字/混合脚本、编码层滥用(URL、HTML 实体、ROT13、base64、Unicode 转义、十六进制转义、Punycode) | 仅 stdlib,体积小 |
| **YARA** | `textguard[yara]` | 基于模式的检测:提示词注入短语、工具欺骗标签、自定义签名。对原始文本和解码后的文本均运行。 | +~6 MB (`yara-python`) |
| **PromptGuard** | `textguard[promptguard]` | 通过 ONNX 模型进行语义提示词注入/越狱分类。 | +~27 MB wheels (`onnxruntime`, `transformers`) + 首次获取时约 295 MB 模型 |
Core 没有运行时依赖。重型后端始终是可选的额外项。
## 模型管理
PromptGuard 需要约 295 MB 的 ONNX 模型包 ([shisa-ai/promptguard2-onnx](https://huggingface.co/shisa-ai/promptguard2-onnx))。textguard 绝不会静默下载模型。
```
# 获取、验证并安装到 XDG data dir
textguard models fetch promptguard2
# 将 textguard 指向它
export TEXTGUARD_PROMPTGUARD_MODEL=~/.local/share/textguard/models/promptguard2
# 或直接传递
textguard scan --promptguard ~/.local/share/textguard/models/promptguard2 SKILL.md
```
`textguard models fetch promptguard2` 通过 stdlib HTTP 从 Hugging Face 下载,使用捆绑的公钥验证 SSH ed25519 签名,并在根据清单检查 SHA-256 文件哈希后,将其安装在 `~/.local/share/textguard/models/promptguard2/` 下(如果设置了 `XDG_DATA_HOME`,则为该路径)。
## Python API
两个主要调用是 `scan()` 和 `clean()`——它们的作用正如您所料。`scan()` 检查文本并返回发现结果;`clean()` 重写文本并移除恶意内容。
```
from textguard import scan, clean, TextGuard
# 快速函数式 API — 使用默认值,零配置
result = scan(text) # ScanResult
cleaned = clean(text) # CleanResult
# 配置好的实例 — 可复用,携带后端状态
guard = TextGuard(
preset="strict",
split_tokens=True,
yara_rules_dir="./rules/",
promptguard_model_path="~/.local/share/textguard/models/promptguard2/",
)
result = guard.scan(text) # ScanResult
cleaned = guard.clean(text) # CleanResult
semantic = guard.score_semantic(text) # SemanticResult
```
顶层的 `scan()` 和 `clean()` 是使用默认设置的 `TextGuard` 的薄包装器。对于重复调用或启用后端的扫描,请创建 `TextGuard` 实例。
### 结果
`scan()` 返回 `ScanResult`——发现结果、解码元数据和可选的语义分类:
```
result = scan(text)
for f in result.findings:
print(f"{f.severity}: {f.kind} at offset {f.offset} — {f.detail}")
```
`clean()` 返回 `CleanResult`——清洗后的文本以及变更报告:
```
cleaned = clean(text)
print(cleaned.text) # the safe output
for c in cleaned.changes:
print(f" {c.kind}: {c.detail}")
```
## 依赖与供应链
- **Core runtime**:核心仅依赖 stdlib,且**没有运行时依赖**。
- **Unicode 数据**:用于脚本范围和混淆字符的 vendored Unicode 数据通过手动脚本运行生成。
- **可选 YARA**:`yara-python>=4.5.4`
- **可选 PromptGuard**:`onnxruntime>=1.24.4`,`transformers>=5.5.3`
- 通过 `pyproject.toml` 中的最低版本进行固定。通过提交的带有哈希的 `uv.lock` 进行精确解析。
[pypi 包](https://pypi.org/project/textguard) 通过带有 OIDC 和 SBOM 的 Github CI 发布。
## 相关项目
我们的工作主要依赖于这些公开贡献:
- [Unicode 17.0 Scripts](https://www.unicode.org/Public/17.0.0/ucd/Scripts.txt)
- [Unicode 17.0 Confusables](https://www.unicode.org/Public/security/17.0.0/confusables.txt)
- [YARA](https://virustotal.github.io/yara/) / [yara-python](https://github.com/VirusTotal/yara-python)
- [Llama Prompt Guard 2](https://github.com/meta-llama/PurpleLlama/tree/main/Llama-Prompt-Guard-2)
虽然 textguard 填补了一个独特的利基市场(面向 LLM 输入的恶意 Unicode 和编码走私),但也有一些基础和相关项目:
- Unicode 安全参考:
- https://www.unicode.org/reports/tr36/ — 关于 Unicode 攻击面的基础文档
- https://www.unicode.org/reports/tr39/ — 正式规定混淆字符和混合脚本检测的地方
- Unicode / 文本规范化:
- https://github.com/rspeer/python-ftfy — 修复 Mojibake 和编码问题
- https://github.com/vhf/confusable_homoglyphs — 基于相同 Unicode 混淆字符数据构建的 Python 库
- https://github.com/avian2/unidecode — ASCII 音译
标签:Agent工作流, AI基础设施, API密钥检测, CNCF毕业项目, DLL 劫持, DNS信息、DNS暴力破解, DNS枚举, Naabu, Unicode处理, YARA规则, 内容安全, 大语言模型, 对抗性文本, 恶意文本检测, 提示词注入防御, 数据预处理, 文本归一化, 文本清洗, 无依赖, 模式匹配, 系统调用监控, 网络安全工具, 自动化资产收集, 输入验证, 逆向工具