Detect-Forge/Detect-Forge
GitHub: Detect-Forge/Detect-Forge
一款 AI 原生的检测工程 CLI 工具包,帮助安全团队在 CI 中自动化评估检测规则的 ATT&CK 技术陈旧度、覆盖缺口及对抗回放表现。
Stars: 0 | Forks: 0
# Detect-Forge
AI 原生检测工程工具包。一次安装,一个配置,一个 CI 步骤。
## 概述
Detect-Forge 是一个专为检测工程师设计的可组合 CLI。每一项能力都是一个子命令;它们共享配置、输出格式、缓存以及单一的 CI 门禁。无需平台,无需注册。
首发功能是 `stale` —— 它会从三个维度评估您的 Sigma (YAML) 和 Elastic Detection Rules (TOML — 涵盖 EQL、KQL 和 ESQL) 的 ATT&CK 技术陈旧度:
1. **时间戳偏移** — 将 ATT&CK STIX 的 `modified` 时间戳与规则修改日期进行比较(确定性判断)。
2. **语义对齐** ✅ — 基于嵌入计算规则文本(标题 + 描述)与当前 ATT&CK 技术描述之间的余弦相似度。当对齐度低于可配置阈值(`--semantic-threshold`,默认为 0.65)时,将标记该规则。真正的历史偏移(与过去的 MITRE 定义进行比较)将在 Phase 3.b 中实现。
3. **LLM diff 建议** ✅ — 可选,通过 OpenAI 结构化输出实现 BYOLLM;为 `semantic_drift` 发现提供重写规则的建议。绝不自动应用 —— 每一个建议都需要人工审查。Anthropic Claude 支持推迟至 v0.2。
专为在 GitHub Actions 中作为 CI 门禁运行而设计。任何数据都不会离开您的环境。
## 状态
🚀 2026年6月8日 — `audit` 发布:将 stale + coverage + backtest 组合成单个 CI 步骤,具备严格 AND 门禁语义、三个维度的独立评分,以及统一的 terminal/json/html 报告。
🚀 2026年5月29日 — `backtest` 发布,支持针对内置 Mordor (Security-Datasets) 语料库的对抗性重放、Sigma 匹配器(selections + modifiers + correlations)、Elastic 匹配器(通过 `eql` Python 库实现 EQL + 自定义 KQL 评估器)、包含 ATT&CK Navigator layer JSON 在内的四种输出格式,以及两个 CI 门禁(优先级静默 + 损坏规则)。
🚀 2026年5月23日发布 — `stale` 随所有三个评分维度一同发布:时间戳偏移、语义偏移(Phase 3.a)和 LLM diff 建议(Phase 4)。真正的历史偏移(Phase 3.b)推迟至 v0.2。`coverage` 随完整/浅层/缺口分析、CTID 加权优先级门禁以及 ATT&CK Navigator 导出功能一同发布。其余子命令(`cti ingest`、`audit`)已作为存根注册。
## 环境要求
- Python **3.12** 或更高版本
## 安装
```
python3.12 -m venv .venv
source .venv/bin/activate
pip install -e ".[dev]"
```
## 使用方法
```
detect-forge --help
detect-forge --version
detect-forge stale path/to/rules
```
### 子命令
| 命令 | 状态 | 描述 |
|---|---|---|
| `stale` | ✅ 可用 | 评估检测规则的 ATT&CK 技术陈旧度。 |
| `backtest` | ✅ 可用 | 对抗性重放(Type 3 + 4)。 |
| `coverage` | ✅ 可用 | 覆盖缺口映射(Type 6a 扩展)。 |
| `cti ingest` | 📝 2026年 Q3–Q4 | CTI 到检测的生成。 |
| `audit` | ✅ 可用 | 在一个步骤中运行所有检查(stale + coverage + backtest)。 |
### `stale` 选项
| 选项 | 默认值 | 描述 |
|---|---|---|
| `RULE_DIR` (位置参数) | — | 要扫描的检测规则目录。递归获取 `.yml`/`.yaml` (Sigma) 和 `.toml` (Elastic Detection Rules: EQL/KQL/ESQL)。必须存在。 |
| `--format {terminal,json,html}` | `terminal` | 输出格式。 |
| `-o, --output PATH` | _stdout_ | 将输出写入文件而不是 stdout。 |
| `--min-severity {low,medium,high,critical}` | `low` | 仅显示达到或高于此严重级别的规则。 |
| `--no-cache` | off | 绕过磁盘缓存并获取最新的 ATT&CK bundle。 |
| `--domain {enterprise-attack,ics-attack,mobile-attack}` | `enterprise-attack` | 要获取的 ATT&CK 领域。 |
| `--semantic-threshold FLOAT` | `0.65` | 余弦相似度阈值;低于此值的配对将产生 `semantic_drift` 发现。 |
支持的规则格式按扩展名自动检测。`.yml`/`.yaml` 文件被解析为 Sigma 规则;`.toml` 文件被解析为 Elastic Detection Rules。Elastic 模式涵盖 EQL、KQL (kuery) 和 ESQL —— 它们共享相同的 TOML 结构,仅在 `language` 字段上有所不同。
### 对齐度如何评分
每条规则作为 `title + description` 进行嵌入(自然语言部分 —— 检测查询主体不被嵌入,因为查询语言与通用文本嵌入的对齐效果不佳)。从 STIX bundle 中将每个 ATT&CK 技术作为 `name + description` 进行嵌入。对于规则标记的每个技术,我们计算两个向量之间的余弦相似度;得分严格低于 `--semantic-threshold`(默认为 `0.65`)的配对会产生一个 `medium` 严重级别的 `semantic_drift` 发现,得分显示在报告的 `Similarity` 列中。
嵌入使用 [`fastembed`](https://github.com/qdrant/fastembed) 计算一次(模型为 `BAAI/bge-small-en-v1.5`,约 30MB,首次运行时自动下载)并缓存在 `$CACHE_DIR/embeddings/` 下。后续运行将从缓存中读取。没有 `--no-semantic` 标志:缓存命中时的成本接近于零,而缓存未命中时的工作无论如何都必须至少发生一次。
#### 相似度得分参考
| 相似度 | 含义 |
|---|---|
| < 0.50 | 重大概念分歧 —— 规则和技术描述的是不同的事物 |
| 0.50–0.70 | 显著偏移 —— 技术已经发生了实质性的演变 |
| 0.70–0.85 | 中度偏移 —— 措辞改变,部分行为发生转变 |
| > 0.85 | 轻微或没有偏移 |
默认触发条件(`semantic_threshold = 0.65`)可捕获具有显著或重大偏移的规则 —— 值得关注的实质性分歧,而不仅仅是一个标记。
进度旋转器输出至 **stderr**;报告输出至 **stdout**,以便 JSON 输出可以被安全地管道传输:
```
detect-forge stale path/to/rules --format json | jq '.scores'
detect-forge stale path/to/rules --format json -o report.json
```
### 退出代码
| 代码 | 含义 |
|---|---|
| `0` | 扫描完成;没有门禁发现(CI 通过)。 |
| `1` | 工具错误、存根命令或未实现的功能。 |
| `2` | 满足 CI 门禁条件(例如 `stale` 发现了严重发现)。 |
使用退出代码 `2` 来使您的 CI pipeline 失败:
```
detect-forge stale path/to/rules
code=$?
if [ "$code" -eq 2 ]; then exit 2; fi
```
### 环境变量
所有设置都可以通过带有 `DETECT_FORGE_` 前缀的环境变量(或工作目录中的 `.env` 文件)进行覆盖。将仓库根目录下的 `.env.sample` 复制为 `.env` 即可开始使用。
| 变量 | 默认值 | 用途 |
|---|---|---|
| `DETECT_FORGE_CACHE_DIR` | `$XDG_CACHE_HOME/detect-forge` (或 `~/.cache/detect-forge`) | ATT&CK bundle 的缓存位置。 |
| `DETECT_FORGE_CACHE_TTL_HOURS` | `24` | 缓存寿命(以小时为单位)。 |
| `DETECT_FORGE_ATTACK_DOMAIN` | `enterprise-attack` | 默认的 `--domain` 值。 |
| `DETECT_FORGE_NO_CACHE` | `false` | 如果为真,则始终绕过缓存。 |
| `DETECT_FORGE_SEMANTIC_THRESHOLD` | 未设置 | 覆盖 `.detect-forge.toml` 和 CLI 标志中的 `semantic_threshold`(最高优先级)。 |
| `OPENAI_API_KEY` | 未设置 | 启用 LLM diff 建议的必填项。未设置时,扫描将正常完成并打印跳过横幅。 |
### LLM diff 建议(Phase 4)
当规则产生 `semantic_drift` 发现时,`stale` 可以选择调用 OpenAI 的结构化输出 API,以提议一个与当前 ATT&CK 技术对齐的重写规则。这些提议是 **BYOLLM** 且 **绝不自动应用** —— 从业者需要审查每一个建议并手动决定保留哪些内容。
#### 启用方法
在您的环境中设置 `OPENAI_API_KEY`。如果没有它,扫描将正常完成并在报告末尾打印 `💡 LLM diff proposals skipped`。
```
export OPENAI_API_KEY=sk-...
detect-forge stale ./rules
```
#### 通过 `.detect-forge.toml` 进行配置
LLM 提议设置位于 `.detect-forge.toml` 中(从您的 CWD 向上查找,直到 git 根目录)。这些设置没有 CLI 标志。仓库根目录下附带了一个包含默认值的入门级 `.detect-forge.toml` —— 可直接在原地编辑或将其复制到您自己的项目中。
```
[stale]
semantic_threshold = 0.65 # Cosine similarity floor; pairs below trigger a proposal
llm_model = "gpt-4o-mini" # Any OpenAI chat-completion model that supports structured outputs
max_proposals = 5 # Hard ceiling on LLM calls per scan run (cost guard)
```
`max_proposals` 是您的主要成本控制手段 —— 每次提议尝试(成功、拒绝或验证失败)都会消耗此配额。
#### 成本
在默认设置下(`gpt-4o-mini`,5 个提议):每次扫描远低于 $0.01。每个提议大约 $0.0005。`max_proposals` 设置是您的硬性成本上限。
#### 提议的形式
对于每个候选规则,您将看到一个终端面板,其中包含规则文件名、模型的置信度 (0–1)、它更改的字段列表、简要说明,以及语法高亮的 YAML (Sigma) 或 TOML (Elastic) 格式的重写规则主体。HTML 报告在底部增加了一个带有颜色编码置信度徽章的 "LLM Proposals" 部分。
#### 提议不会做什么
- 它们永远不会修改磁盘上的规则。请在审查后手动应用更改。
- 如果未设置 `OPENAI_API_KEY`,它们将不会运行。
- 它们仅使用规则的自然语言字段和您当前的 ATT&CK 技术描述 —— 除了 OpenAI API 调用之外,没有任何遥测数据会离开您的环境。
- 它们不能代替人工审查。模型的 `confidence` 字段是自报告的,不可靠 —— 请将每个提议视为草稿。
### 覆盖缺口分析
`detect-forge coverage` 将您的检测规则语料库映射到 ATT&CK 矩阵,并报告哪些技术拥有完整、浅层或零覆盖。优先技术(默认为 CTID 风格的前 25 个列表)驱动 CI 门禁。
#### 快速开始
```
detect-forge coverage ./rules
detect-forge coverage ./rules --format html --output coverage.html
detect-forge coverage ./rules --format navigator --output layer.json
```
Navigator JSON 输出可以直接导入 https://mitre-attack.github.io/attack-navigator/ 以获取热力图视图。
#### 覆盖状态
| 状态 | 含义 |
|---|---|
| **full** | 至少有一条规则标记了此确切的技术 ID。 |
| **shallow** | 仅标记了父技术(例如规则标记了 `T1059`;子技术 `T1059.001` 即为浅层)。 |
| **gap** | 没有任何规则在任何层级引用此技术。 |
#### 配置
设置位于 `.detect-forge.toml` 的 `[coverage]` 中。仓库根目录下附带了一个入门级配置节。
```
[coverage]
priority_list = "" # path to custom JSON; empty = built-in CTID default
gate_on_priority_gaps = true # exit 2 when priority techniques have no rules
```
#### 自定义优先级列表
放入一个包含您自己的技术 ID 的 JSON 文件(行业威胁模型、内部红队优先级等):
```
{
"name": "Acme Corp Priorities 2026",
"technique_ids": ["T1078", "T1190", "T1059.001", "T1486"]
}
```
通过 `[coverage] priority_list = "/path/to/list.json"` 或 `--priority-list /path/to/list.json` 指向它以进行一次性扫描。
#### CI 门禁
当任何优先级列表中的技术处于 gap 状态(完全没有规则)时,命令将以退出代码 2 退出。对于信息性扫描,可以使用 `--no-gate` 抑制此行为,或者在配置中设置 `gate_on_priority_gaps = false` 以永久关闭。
#### coverage 在 v0.1 中不会做的事
- 没有随着时间推移的覆盖率差异对比 —— 那属于覆盖率版本控制,推迟至 v0.2。
- 没有来自 `cti ingest` 的威胁情报加权 —— 将在该子命令发布时与其组合使用。
- 没有基于规则状态的过滤(例如仅统计 `status: stable` 的规则)。
- 没有规则质量加权(未经测试的规则 = 与久经沙场的规则权重相同)。
### Backtest (Roberts Types 3+4 重放)
`detect-forge backtest` 将您的检测规则针对内置的 Mordor (Security-Datasets 样本进行重放,以查看哪些规则实际触发,哪些在测试数据集上保持静默,以及哪些针对完全没有数据的技术。
#### 快速开始
```
detect-forge backtest ./rules
detect-forge backtest ./rules --format json -o report.json
detect-forge backtest ./rules --format html -o report.html
detect-forge backtest ./rules --format navigator -o layer.json
```
Navigator JSON 输出可以直接导入 https://mitre-attack.github.io/attack-navigator/ 获取按触发状态着色的热力图视图。
#### 触发状态模型(按数据集)
| 状态 | 含义 |
|---|---|
| **verified** | 来自此数据集的至少一个事件匹配了规则的检测逻辑。 |
| **silent** | 数据集已加载并评估;规则产生零匹配。 |
| **untested** | 在内置的 Mordor 索引中找不到针对此技术的数据集。 |
| **unsupported** | 规则格式或查询语言不受任何匹配器支持。 |
#### 单个规则的状态模型
| 状态 | 含义 |
|---|---|
| **fires** | 规则在针对所有目标技术的至少一个数据集上触发。 |
| **partial** | 规则在某些数据集上触发,但在其他数据集上保持静默。 |
| **silent_on_all** | 规则针对 ≥1 个数据集进行了评估,并在所有数据集上均产生零匹配。 |
| **untested** | 所有目标技术均没有可用的 Mordor 数据。 |
| **unsupported** | 没有匹配器可以处理此规则的格式/语言。 |
#### 配置(`.detect-forge.toml [backtest]`)
```
[backtest]
gate_on_priority_silence = true # exit 2 when priority technique is silent
gate_on_broken_rules = true # exit 2 when any rule is silent on all tested datasets
mordor_source = "" # local Security-Datasets checkout; empty = fetch
platform = "all" # windows | linux | macos | all
```
#### 自定义优先级列表
Backtest 重用与 `coverage` 相同的优先级列表格式。通过 `[coverage] priority_list` 或 `--priority-list` 指向它 —— 请参阅 Coverage 下的 [自定义优先级列表](#custom-priority-list) 部分。
#### CI 门禁语义
有两个独立的门禁可能会导致 CI pipeline 失败(退出代码 `2`):
- **优先级静默门禁** (`gate_on_priority_silence`):当优先级列表中的某个技术至少有一条规则标记,但这些规则都没有在任何 Mordor 数据集上触发时触发。该技术在理论上有覆盖,但在重放中保持静默 —— 这是一个值得调查的信号。
- **损坏规则门禁** (`gate_on_broken_rules`):当任何规则针对至少一个数据集进行评估并在所有这些数据集上产生零匹配(`silent_on_all` 状态)时触发。这些规则是修订或淘汰的候选对象。
对于信息性扫描,可以使用 `--no-gate` 一起抑制这两个门禁,或者通过配置独立抑制。
#### Mordor 源覆盖
默认情况下,数据集在首次使用时从 GitHub (Security-Datasets) 获取并在本地缓存。对于物理隔离环境或本地克隆:
```
detect-forge backtest ./rules --mordor-source /path/to/Security-Datasets
```
或者在 `.detect-forge.toml` 中设置 `mordor_source = "/path/to/Security-Datasets"`。
#### 平台过滤器
将评估限制为匹配特定平台的数据集:
```
detect-forge backtest ./rules --platform windows
detect-forge backtest ./rules --platform linux
```
默认为 `all`。可用值:`windows`、`linux`、`macos`、`all`。
#### 技术过滤器
将扫描限制在特定技术(适用于有针对性的分类甄别):
```
detect-forge backtest ./rules --techniques T1059.001,T1078
```
#### backtest 在 v0.1 中不会做的事
- 没有 ES|QL 匹配器 —— 推迟至 v0.2。
- 没有 Sigma `|cidr`、`|gt`、`|lt`、`|gte`、`|lte` 修饰符。
- 没有 Sigma `keywords`(无字段搜索)。
- 没有 Mordor `compound/` 数据集(多技术链)。
- 没有针对每个规则的 TP/FP/FN 矩阵。
- 没有 backtest → coverage `verified` 状态集成。
- 没有沙箱 / 执行模式(仅重放 —— 没有实时执行)。
- 没有检测延迟 / 触发时间指标。
### Audit (一步式 CI 门禁)
`detect-forge audit` 在单个
Python 会话中按顺序运行 `stale` + `coverage` + `backtest` 并发出统一的报告。遵循“一次安装,一个配置,一个
CI 步骤”的标语:
```
detect-forge audit ./rules
```
#### 快速开始(所有格式)
```
detect-forge audit ./rules # terminal
detect-forge audit ./rules --format json -o audit.json
detect-forge audit ./rules --format html -o audit.html
```
v0.1 不支持 Navigator 输出 —— 直接运行 `coverage` 或 `backtest`
以获取技术热力图。
#### 三个维度的独立评分
页眉显示了三个百分比(0–100,无综合指标):
- **陈旧健康状况** = `100 × (total_rules − critical) / total_rules`
- **覆盖率完整度** = `100 × full / total_techniques`
- **Backtest 验证率** = `100 × rules_fires / (rules_parsed − rules_unsupported)`
当相应的子命令被跳过或出错时,每个得分为 `null`。
#### 严格 AND 门禁语义
audit 门禁仅在**每个已启用的子命令**单独运行时都会触发门禁的情况下才会
触发(退出代码 2)。这是有意为之的宽松策略 —— 运行每个子命令
能提供最精细的信号。Audit 的门禁是“一切都损坏了”的警报。
使用 `--no-gate` 覆盖,或在配置中设置 `[audit].gate_strategy = "never"`
以进入信息模式(始终退出 0/1)。
#### CI 退出代码
| 代码 | 含义 |
|---|---|
| `0` | 清洁 OR audit 门禁未触发 |
| `1` | 至少有一个子命令崩溃(工具错误) |
| `2` | Audit 门禁已触发 |
#### 配置(`.detect-forge.toml [audit]`)
```
[audit]
gate_strategy = "all" # "all" | "never"
subcommands = ["stale", "coverage", "backtest"] # subset to run
include_llm_proposals = false # cost gate
```
现有的 `[stale]`、`[coverage]`、`[backtest]` 部分继续驱动每个
子命令自身的配置(语义阈值、优先级列表路径、mordor 源等)。Audit 只是组合使用 —— 它不会
覆盖单个子命令的配置。
#### 为增量采用而跳过子命令
```
detect-forge audit ./rules --skip stale --skip backtest # only coverage
```
CLI 的 `--skip` 是从 `[audit].subcommands` 中做减法。如果您的配置
禁用了一个子命令并且您 `--skip` 了另一个,有效集合就是它们的交集。
#### LLM 建议默认关闭
`stale` 子命令的 LLM diff 建议在 audit 模式下会被跳过,除非您
通过 `--with-llm-proposals` 或 `[audit].include_llm_proposals = true` 选择启用。这是
一个成本控制门禁 —— 每次提议调用的成本约为 $0.0005(默认)。
#### audit 在 v0.1 中不会做的事
- 不跨子命令合并发现(没有来自 backtest 的 "verified" 覆盖
状态等)
- 不并行运行子命令
- 不发出 Navigator layer
- 不与之前的基线进行差异对比
- 不传播每个子命令的退出代码(audit 仅作汇总;如果您需要它们各自的退出代码,请直接运行子命令)
## Python API
每个子命令都为高级用户提供了可编程的 API:
```
from pathlib import Path
from detect_forge.stale import scan
report = scan(Path("./rules"), domain="enterprise-attack")
for score in report.scores:
if score.worst_severity == "critical":
print(f"{score.title}: {score.worst_days_stale} days stale")
```
## 开发
```
pytest -q # run the test suite
ruff check src/ tests/ # lint
mypy src/ # type-check (strict)
```
包布局:
```
src/detect_forge/
├── cli.py # click root group; registers all subcommands
├── settings.py # DETECT_FORGE_* pydantic-settings config
├── console.py # rich stdout + stderr consoles
├── cache.py # XDG-aware cache (default_cache_dir() factory)
├── common.py # @common_output_options decorator
├── exit_codes.py # CLEAN=0, RESERVED=1, GATED=2
├── _stubs.py # stub_command() helper
├── stale/ # the staleness pipeline (real subcommand)
├── backtest/ # stub
├── coverage/ # stub
├── cti/ # group + ingest stub
└── audit/ # stub
```
## 许可证
MIT
标签:AI原生, AMSI绕过, ATT&CK框架, Petitpotam, Python安全, 威胁检测, 安全检测工程, 安全运营, 扫描框架, 文档结构分析, 聊天机器人, 逆向工具