fabiocicerchia/gandalf
GitHub: fabiocicerchia/gandalf
一款纯标准库实现的 Git 仓库安全质量门评估器,通过可插拔门机制整合数十种扫描工具,在一遍扫描中完成代码质量、密钥泄露、依赖漏洞和供应链风险的审计。
Stars: 0 | Forks: 0
# 🧙 Gandalf — 代码库质量门评估器
[](https://github.com/fabiocicerchia/gandalf/actions/workflows/code-quality.yml)
[](https://github.com/fabiocicerchia/gandalf/actions/workflows/security.yml)
[](LICENSE)
[](https://securityscorecards.dev/viewer/?uri=github.com/fabiocicerchia/gandalf)
[](https://app.fossa.com/projects/git%2Bgithub.com%2Ffabiocicerchia%2Fgandalf?ref=badge_shield)
[](https://github.com/fabiocicerchia/gandalf/releases)
按原样评估代码库,获取 LLM 摘要以及来自可插拔门 (gates) 的红/黄/绿
红绿灯记分卡。仅使用标准库,无外部依赖。
```
PYTHONPATH=src python -m gandalf # whole working tree, as-is (default)
PYTHONPATH=src python -m gandalf --staged # staged changes only
PYTHONPATH=src python -m gandalf --commit # a specific commit (in a throwaway git worktree)
PYTHONPATH=src python -m gandalf --path # limit scanning to a folder
```
该包位于 `src/` 下,因此请将 `src` 放入 `PYTHONPATH` 中(下面的包装脚本会为您完成此操作)。
或者使用 `make analyze`。当判定结果为红色时退出代码为 `1`,否则为 `0` —— 可用于 CI。
## 安装 `gandalf` 命令
```
make install # drops a wrapper in ~/.local/bin (on your PATH)
make install BINDIR=/usr/local/bin # …or anywhere else
```
或使用单行安装程序(在 `~/.local/share/gandalf` 下克隆/更新检出版本并运行 `make install`):
```
curl -fsSL https://raw.githubusercontent.com/fabiocicerchia/gandalf/main/install.sh | bash
```
纯标准库实现,因此“安装”只是一个单行包装脚本,它会针对您当前所在的任何仓库运行此检出(`python -m gandalf`)。等效的单行命令:
```
printf '#!/bin/sh\nexport PYTHONPATH="%s/src:$PYTHONPATH"\nexec python3 -m gandalf "$@"\n' "$PWD" > ~/.local/bin/gandalf && chmod +x ~/.local/bin/gandalf
```
### `.gandalfignore` — 在树扫描门中跳过路径
容器/依赖门 (`trivy`, `checkov`, `kics`) 会扫描整个文件树。
在仓库根目录下添加一个 `.gandalfignore` 文件(gitignore 风格:每行一个 glob 匹配模式,支持 `#` 注释),以跳过未提交的本地机密/状态——例如 `.env` 或 `data/` 目录——这样它们就不会被报告为误报的泄露。内置的默认值(`reports`, `node_modules`, `llama.cpp`, `.venv`, `.git`)始终生效。
## 扫描程序在 Docker 中运行 — 保持主机环境整洁
gandalf 本身是纯标准库 Python(无需安装任何东西)。扫描工具位于同一个镜像中,因此它们永远不会接触主机:
```
make tools # builds the gandalf-tools image (docker build -f gandalf/tools.Dockerfile)
```
构建完成后,任何其二进制文件不在主机 `PATH` 中的门,都会以 `docker run --rm -v :/src gandalf-tools …` 的形式原子化运行其命令。一个名为 `gandalf-cache` 的命名卷会在多次运行之间保持工具数据库(trivy、semgrep 规则)处于预热状态,并远离主机。每个工具的解析顺序如下:
1. `PATH` 中有主机二进制文件 → 直接运行它(不使用 Docker);
2. 否则,如果存在 `gandalf-tools` 镜像 → 在一次性容器中运行该工具;
3. 否则 → 该门降级为 🟡 WARN。
因此,您可以通过 `make tools` 实现主机零安装,*或者*在主机上安装任何工具子集并跳过镜像——两者均有效,并且可以自由混合使用。使用 `GANDALF_TOOLS_IMAGE` 覆盖镜像名称。
该镜像涵盖了与语言无关的工具:`ruff`、`semgrep`、`bandit`、`pip-audit` (osv)、`osv-scanner`、`trivy`、`gitleaks`、`checkov`、`hadolint`、`scorecard`、`mypy`、`vulture`、`codespell`、`yamllint`、`shellcheck`、`actionlint`。
Go 和 Node 门使用主机工具链(`go`/`npx`/`npm`),而 `ci_act`(主机 Docker 守护进程)、`tests`(项目环境)、`dynamic` DAST 工具以及 `atheris` 也在主机上运行。
## 它的功能
1. **LLM 分析** — 一次调用 headroom endpoint 会返回三个 markdown 部分:通用的**摘要**、**修复建议**(基于实际的发现,引用 file:line / package / rule id,以提高分数的具体修复方法),以及**改进建议**(超越及格线进一步提升的方法)。
2. **门 → RAG** — 并发运行每个发现的门,将每个结果映射为 🟢 PASS / 🟡 WARN / 🔴 FAIL,加上整体判定和 0–100 的分数。
3. **输出** — 彩色终端记分卡、独立的 HTML 报告,以及可供 CI 解析的 JSON 文件(两者均写入 `reports/`)。HTML 报告占据页宽的 3/4,具有浅色/深色主题切换、RAG 着色的行、Markdown 渲染的摘要 + 修复建议 + 改进建议、可点击排序的门 / RAG 列、可展开的每门发现,以及显示提交 ref 和 UTC 生成时间的页眉。
整体判定:🔴 如果任何门失败 · 🟡 如果任何门警告 · 🟢 如果全部通过。
## 标志
| 标志 | 效果 |
|------|--------|
| `--commit ` | 评估该提交(在临时工作树中检出,自动删除)。 |
| `--staged` | 仅评估暂存的更改。 |
| `--path ` | 将扫描限制在文件夹下的 git 跟踪文件。单独使用时扫描整个文件夹;与 `--staged`/`--commit` 一起使用时,会将更改集缩小到该文件夹。 |
| `--no-llm` | 跳过 LLM 摘要。 |
| `--debug` | 详细的 stderr 日志:每个门的耗时以及运行的每个外部命令(也可以通过 `GANDALF_DEBUG=1` 设置)。将进度条移开。门持续时间始终记录在 JSON 的 `duration` 中。 |
| `--fix` | 在评分前将门自动修复(`ruff --fix`, `ruff format`, `eslint --fix`)应用到工作树,以便记分卡反映修复后的状态。对 `--commit` 忽略(一次性工作树)。 |
| `--no-html` | 跳过 HTML 报告(JSON 始终会写入)。 |
| `--json` | 同时将 JSON payload 转储到 stdout。 |
| `--target ` | 动态门 (nikto/sqlmap/dalfox) 的实时 URL。没有它,它们将跳过。 |
| `--allow-remote` | 允许针对非 localhost 的 `--target` 进行动态扫描。 |
| `--title` / `--body` | `compliance` 门的需求标题 / 验收标准。没有它们,它将跳过。 |
| `--sarif [PATH]` | 同时写入 SARIF 2.1.0 报告(默认 `reports/.sarif`),用于 GitHub 代码扫描 / CI 仪表板。 |
| `--pr-comments [PATH]` | 将 GitHub PR 评审评论(按发现逐条列出,锚定到 `file:line`)写入为 JSON,随时可 POST 到“创建评审”API(默认 `reports/-pr-comments.json`)。 |
| `--pr N` | 同时通过 REST API 将这些评论发布到 PR #N(需要 `GITHUB_TOKEN` 和 `--pr-repo` / `$GITHUB_REPOSITORY`)。 |
| `--baseline ` | 抑制基线文件中列出的发现(如果存在,默认为 `.gandalf-baseline.json`)。 |
| `--write-baseline [PATH]` | 将当前发现的快照保存到基线文件(默认 `.gandalf-baseline.json`)。 |
| `--config ` | `.gandalf.toml` 的路径(默认:仓库根目录)。 |
| `--cache [PATH]` | 当扫描的文件未更改时,重用门之前的运行结果(默认 `.gandalf-cache.json`)。使用 `--target`/`--title`/`--body` 时忽略。 |
## 配置 (环境变量)
| 变量 | 默认值 | 用途 |
|-----|---------|---------|
| `GANDALF_LLM_URL` | `http://127.0.0.1:8787/v1` | headroom OpenAI 兼容的 base URL |
| `GANDALF_MODEL` | `gpt-oss-120b` | 用于摘要 + 修复建议 + LLM 评判门 (compliance + 技能支持的门) 的模型 id |
| `GANDALF_MAX_TOKENS` | `8000` | 最大补全 token 数(推理模型需要预留空间,否则最后一部分会被截断) |
| `GANDALF_API_KEY` | `sk-no-key-required` | bearer token |
| `GANDALF_LLM_RETRIES` | `2` | LLM 调用的瞬时故障重试次数(尝试次数 = 重试次数 + 1);重试网络错误、超时以及 429/5xx(带指数退避)——不重试 4xx |
| `GANDALF_LLM_BACKOFF` | `1.0` | 基础退避秒数(延迟 = 基础值 × 2^尝试次数) |
| `GANDALF_GATE_TIMEOUT` | `120` | 每个门子进程超时时间(秒) |
| `GANDALF_GATES_PATH` | — | 用于加载门的额外 `:` 分隔的目录 |
| `GANDALF_PROGRESS` | auto | 当 stderr 不是 TTY 时,设置为 `1` 以强制显示 stderr 进度条 |
| `GANDALF_TOOLS_IMAGE` | `gandalf-tools` | 不在主机 PATH 上时,扫描器在其中运行的 Docker 镜像 |
| `GANDALF_KICS_IMAGE` | `checkmarx/kics:latest` | `kics` 门运行的镜像(它自带查询资源) |
| `GANDALF_CODEQL_IMAGE` | `mcr.microsoft.com/cstsectools/codeql-container:latest` | 当没有主机 `codeql` 二进制文件时,`codeql` 门运行的镜像 |
| `GANDALF_CODEQL_TIMEOUT` | `600` | codeql DB 构建 + 分析的每步超时时间 (s)(比其他门慢) |
| `GANDALF_ACT` | `1` | 设置为 `0` 以禁用 `ci_act` 门 |
| `GANDALF_ACT_EVENT` / `GANDALF_ACT_PLATFORM` / `GANDALF_ACT_TIMEOUT` | `pull_request` / `ubuntu-latest=…` / `900` | `act` 运行器配置 |
如果 headroom 无法访问,摘要会显示一行说明,并且门仍会运行。
## 配置 (`.gandalf.toml`)
每个仓库的设置位于仓库根目录下受版本控制的 `.gandalf.toml` 中
(使用 `--config` 或 `GANDALF_CONFIG` 覆盖路径)。它与代码一起评审,
而不是散落在各处的环境变量中。环境变量优先级仍高于文件,
文件优先级高于内置默认值。所有键均为可选。
```
[gandalf]
only = ["ruff", "gitleaks"] # allowlist: run ONLY these gates
skip = ["atheris"] # denylist: never run these
concurrency = 8 # max gates running at once (<=0 = unbounded)
```
`concurrency` 限制了同时运行的门的数量 —— 这很重要,因为大约 35 个门可能各自生成一个 `docker run`,如果它们同时启动,可能会使笔记本电脑或 CI 运行器不堪重负。优先级:`--concurrency N` → `GANDALF_CONCURRENCY` → 配置 → CPU 核心数。
### 单门超时
全局单门子进程超时时间为 `GANDALF_GATE_TIMEOUT`(默认 120 秒)。
按门覆盖它 —— 重量级扫描器(semgrep, trivy, kics)通常需要更多时间,而轻量级扫描器则更少:
```
[gandalf.timeouts]
default = 120 # overrides the global default for all gates
semgrep = 300 # per-gate override, keyed by gate name
trivy = 300
```
以门命名的键优先于 `default`,后者优先于 `GANDALF_GATE_TIMEOUT`。
超出其时间预算的门会降级为 🟡 WARN,如前所述。
### 严重性加权评分
大多数门通过发现 *count* 来评分 —— 一个严重的漏洞与一个样式挑剔的权重相同。
启用严重性加权,以便门的分数能反映其发现的严重程度:
```
[gandalf.severity]
weight = true
```
或 `--severity-weight`。只有报告严重性的发现(安全 / 依赖 / IaC 门 —— bandit, trivy, semgrep, licenses, osv…)才会被加权;按计数评分的门(ruff, mypy…)不受影响,且门的 RAG 结果永远不会改变 —— 只有输入到综合评分中的 0–100 分会改变。单个 CRITICAL 拉分数的程度远超少数几个 LOW。
`only` 是一个允许列表(空 = 允许所有),`skip` 始终生效用于移除;两者均在语言过滤之前应用。被配置移除的门在终端输出和 JSON 的 `disabled_gates` 中列为“disabled by config”。后续的表格(`[gandalf.verdict]`, `[gandalf.timeouts]`, `[gandalf.severity]`, `[gandalf.suppress]`)在其下方各自的章节中有记录。
## 判定策略 (何时运行失败)
默认情况下,只有 **红色** 判定会导致运行失败(退出 1);琥珀色(黄色)通过。可以针对每个仓库或每次调用调整此设置:
```
[gandalf.verdict]
fail_on = "warn" # "fail" (default) | "warn" — treat warnings as failures too
min_score = 85 # also fail if the composite score drops below this (0-100)
```
CLI `--fail-on {fail,warn}` 和 `--min-score N` 会覆盖文件设置。显示的 RAG 红绿灯保持不变(它始终反映门的结果);该策略仅更改通过/失败决定、退出代码以及 JSON 中的 `passed` / `policy`。当 RAG 不是红色但策略仍然使运行失败时,终端会打印一条明确的 `Policy: run FAILED — ` 行。
## 抑制已知发现 (基线)
要阻止 *已知* 发现导致门失败 —— 而不禁用整个门(那是 `skip` 的功能)—— 可以将其静音。有两种机制:
**规则** (`[gandalf.suppress]`) — `gate:rule:pathglob`,任何字段留空即代表通配符:
```
[gandalf.suppress]
rules = [
"ruff:E501", # mute that code everywhere
"gitleaks::tests/*", # mute gitleaks under tests/
"vulture", # mute the whole gate (still runs, just no findings)
]
```
**基线** — 对今天存在的发现建立快照,这样只有 *新* 发现才会导致失败(在遗留仓库中采用 gandalf 的经典方法):
```
python -m gandalf --write-baseline # writes .gandalf-baseline.json
python -m gandalf # auto-loads it; baselined findings are muted
```
`--baseline ` 指向特定文件;`[gandalf.suppress] baseline` 设置一个默认值。指纹对行不敏感(门 + 路径 + 规则 + 消息),因此建立了基线的发现在其上方的编辑后依然有效。静音只能让门表现更好:如果每个发现都被静音,则该门通过;部分静音会保持结果不变,但会提高分数并隐藏被静音的发现。
## JSON 报告结构
```
{
"scope": "staged",
"generated_at": "2026-07-03 13:10:42 UTC",
"commit": {"sha": "…", "short": "ccfc3ec", "subject": "fix: …", "date": "2026-07-02T23:17:13+02:00"},
"languages": ["python", "shell"],
"verdict": "fail",
"passed": false,
"score": 60,
"summary": "…",
"remediation": "…markdown…",
"improvement": "…markdown…",
"skipped_gates": ["eslint", "go_build"],
"gates": [
{"name": "build", "outcome": "fail", "score": 0.0,
"summary": "1 file(s) fail to compile — …", "findings": [...], "blocking": true}
]
}
```
`commit` 是 `--commit` 时的评估提交,否则即使对于 `--staged` / 工作树范围,也是最新的提交 (HEAD)。
## 分数徽章
`--badge` 写入一个 [shields.io endpoint badge](https://shields.io/badges/endpoint-badge)
JSON(默认:`reports/-badge.json`) —— 包含分数和 RAG 颜色,gandalf 端不进行 SVG 渲染。将其提交到具有稳定原始 URL 的地方(一个 `badge` 分支、gh-pages 部署……),并让 README 指向它:
```

```
## 门是插件
门是任何具有 `name: str`、`blocking: bool` 和 `async def run(self, ctx) -> GateResult` 的类。
要添加一个门,只需将导出此类的一个 `.py` 文件放入 `gandalf/gates/`(或 `GANDALF_GATES_PATH` 上的任何目录)—— 它会被自动发现,无需编辑注册表。名称冲突允许插件覆盖内置项。
```
# gandalf/gates/mygate.py
from gandalf.base import GateContext, GateOutcome, GateResult
class MyGate:
name = "mygate"
blocking = False
async def run(self, ctx: GateContext) -> GateResult:
return GateResult(self.name, GateOutcome.PASS, 1.0, "all good")
```
`ctx.changed_files` 在全树模式下为空(扫描整个仓库),而在 `--staged`/`--commit` 模式下会被填充(仅扫描这些文件)。
`ctx.workdir` 是运行工作目录。
门还可以通过公开 `async def fix(self, ctx) -> tuple[bool, str]` 来选择加入 `--fix`,
该方法会在原处应用其自动修复并返回 `(changed, message)`。修复器在评分前按顺序运行;
`ruff`、`format` 和 `eslint` 内置了修复器。
### 语言相关性
gandalf 会检测范围内的语言,并且 **只运行与其相关的门,以及与语言无关的门** —— 因此 Go 更改永远不会触发 eslint 或 mypy。
检测是通过文件扩展名 + 标记文件(`go.mod`, `package.json`, `tsconfig.json`, `pyproject.toml`, `Dockerfile`, …)进行的:
从 `--staged`/`--commit` 模式下的 **更改文件** 中检测,或默认从整个被跟踪的树中检测。不相关的语言门会完全从运行中剔除(在输出和 JSON 中列在“skipped”下),而不仅仅是按绿色跳过。
门可以通过设置 `langs` 类属性来选择加入此功能(例如 `langs = frozenset({"go"})`);没有该属性的门是通用的,并始终运行。
标签 → 语言映射位于 `scope.py` (`_EXT_LANG` / `_MARKER_LANG`) 中。
### 内置门 (36)
每个门都需要其外部工具 —— 位于主机的 `PATH` 中,或者(对于扫描器)位于 `gandalf-tools` 镜像中。
当工具不可用,或者动态门没有 `--target`,或者 `compliance` 没有 `--title`/`--body` 时,该门会降级为 🟡 WARN —— 这样任何仓库仍然可以生成完整的记分卡。带有语言标签的门(Python、Go、Node/TS 组,加上 `shellcheck`=shell, `yamllint`=yaml, `hadolint`=docker)仅在该语言在范围内时运行;其他所有门都是通用的,并始终运行。
**镜像 = 主机整洁。** `gandalf-tools` 提供了与语言无关的扫描器(ruff, semgrep, bandit, pip-audit, osv-scanner, trivy, gitleaks, checkov, hadolint, scorecard, mypy, vulture, codespell, yamllint, shellcheck, actionlint, mdl)。
`kics` 门运行自官方 `checkmarx/kics` 镜像(它自带查询资源),而 `codeql` 运行自主机 `codeql` 二进制文件或 bundle 镜像 (`GANDALF_CODEQL_IMAGE`) —— CodeQL CLI 是一个大型 bundle,而不是 pip/apt 包,因此它没有被内置到 `gandalf-tools` 中。Go 和 Node 门使用您的 **主机** 工具链(`go`, `npx`, `npm`) —— 您已经有了这些 —— 所以它们永远不会被容器化。
如果镜像已过期或不完整(它声称提供的工具实际上并不存在),该门会降级为 🟡 WARN —— 缺失的 Docker 化工具绝不会显示为干净的通过。
Python:
| 门 | 阻断 | 工具 | 检查内容 |
|------|----------|------|--------|
| `build` | yes | — (标准库) | 每个 Python 文件都能编译(语法) |
| `ruff` | no | ruff | lint |
| `format` | no | ruff format | 格式化漂移 (`--check`) |
| `mypy` | no | mypy | 静态类型错误 |
| `bandit` | no | bandit | 安全 lint |
| `vulture` | no | vulture | 死代码 / 未使用的代码 |
跨语言 SAST / deps / secrets / IaC:
| 门 | 阻断 | 工具 | 检查内容 |
|------|----------|------|--------|
| `semgrep` | no | semgrep | SAST — python **+ go + js + ts** + owasp + secrets |
| `codeql` | no | codeql (bundle / own image) | 语义 SAST (数据流/污点) — python, js/ts, go |
| `gitleaks` | yes | gitleaks | 树中的 secrets |
| `osv` | no | pip-audit | Python 依赖漏洞 |
| `osv_scanner` | no | osv-scanner | 依赖漏洞,**所有生态系统** (go.mod, package-lock, …) |
| `trivy` | no | trivy | 文件系统漏洞 + secrets + **配置错误 + license** |
| `checkov` | no | checkov | IaC 配置错误 |
| `kics` | no | checkmarx/kics (own image) | IaC 配置错误 (Terraform/k8s/Docker/Ansible/…) |
| `hadolint` | no | hadolint | Dockerfile lint |
| `scorecard` | no | scorecard | OSSF 安全态势分数 (0–10, 基于本地文件的检查) |
Shell / CI / config / prose:
| 门 | 阻断 | 工具 | 检查内容 |
|------|----------|------|--------|
| `shellcheck` | no | shellcheck | shell 脚本错误 (`*.sh`/`*.bash`) |
| `actionlint` | no | actionlint | GitHub Actions 工作流 lint |
| `yamllint` | no | yamllint | YAML lint |
| `codespell` | no | codespell | 源代码/文档拼写错误 |
| `mdl` | no | mdl | Markdown lint (`*.md`) |
| `ci_act` | no | act + Docker | 在本地运行 `.github/workflows/*` |
数据库 (无 `*.sql` 时自动跳过;方言通过 `GANDALF_SQL_DIALECT` 设置,默认为 `ansi`):
| 门 | 阻断 | 工具 | 检查内容 |
|------|----------|------|--------|
| `sqlfluff` | no | sqlfluff | SQL lint / 样式 |
| `squawk` | no | squawk | Postgres 迁移安全性 (不安全的 DDL, 锁, …) |
Go (主机工具链;无 `go.mod` 时自动跳过):
| 门 | 阻断 | 工具 | 检查内容 |
|------|----------|------|--------|
| `go_build` | yes | go | `go build ./...` 编译 |
| `golangci_lint` | no | golangci-lint | meta-linter (govet, staticcheck, errcheck, unused, …) |
| `govulncheck` | no | govulncheck | Go 漏洞 (可达性感知) |
| `go_test` | no | go | `go test ./...` |
Node / TypeScript (主机工具链;无 `package.json` 时自动跳过):
| 门 | 阻断 | 工具 | 检查内容 |
|------|----------|------|--------|
| `eslint` | no | npx eslint | JS/TS lint (项目本地配置) |
| `tsc` | no | npx tsc | TypeScript 类型检查 (需要 `tsconfig.json`) |
| `node_test` | no | npm test | 运行 `test` 脚本 |
测试 + compliance + 动态:
| 门 | 阻断 | 工具 | 检查内容 |
|------|----------|------|--------|
| `tests` | no | pytest | 运行 Python 测试套件 |
| `compliance` | no | LLM (headroom) | diff 是否满足 `--title`/`--body`? (≥85% = 通过) |
| `atheris` | no | atheris | 覆盖率引导的模糊测试 (需要 harness) |
| `nikto` | no | nikto + `--target` | 服务器配置错误扫描 |
| `sqlmap` | no | sqlmap + `--target` | SQL 注入探测 |
| `dalfox` | no | dalfox + `--target` | XSS 探测 |
除非您传递 `--allow-remote`,否则动态门 (`nikto`/`sqlmap`/`dalfox`) 会拒绝非 localhost 目标。
`golangci-lint`/`govulncheck` 不在镜像中(它们需要 Go 工具链)—— 在主机上 `go install` 它们以激活。
### 技能驱动的评审门 (4)
这些门没有外部工具。每个门都包装了一个评审 **技能** —— 即位于仓库顶级 [`skills/`](../skills) 目录下的文本 playbook —— 并将其作为 LLM 评判器在与 `compliance` 门使用的同一个 headroom endpoint 上运行。
技能的 `SKILL.md` 成为评分准则;模型返回一个严格的 JSON 判定 (`outcome` + `score` + `findings`),并映射到该门。这些技能被内嵌在此仓库中,因此这些门是自包含的,并随代码一起进行版本控制。像每个 LLM 门一样,当 endpoint 无法访问时,每个门都会降级为 🟡 WARN(永远不会导致运行崩溃)—— 因此仅静态运行不会被缺失的模型所阻挡。
| 门 | 阻断 | 技能 | 评判 |
|------|----------|-------|--------|
| `quality_gate_review` | yes | [`quality-gate-review`](../skills/quality-gate-review) | 六个加权质量门 → GO / REVIEW / NO-GO (通过 / 警告 / 失败) |
| `ruthless_refactor` | no | [`ruthless-refactor`](../skills/ruthless-refactor) | 简化:重复、死代码、不必要的间接调用、自定义与库的对比 |
| `pr_code_summary` | no | [`pr-code-summarizer`](../skills/pr-code-summarizer) | 技术主管 60 秒的阅读;在复杂度/风险很高时发出警告 |
| `security_assessment` | no | [`security-assessment`](../skills/security-assessment) | CNCF TAG 安全态势:SBOM、签名、分支保护、披露、事件响应 |
`quality_gate_review` 是唯一的阻断性技能门 —— NO-GO 判定会使运行变红 —— 但来自无法访问的模型的 WARN 永远不会这样。技能是从 gandalf 自己的源代码树(包旁边)读取的,而不是在评审下的工作树中读取的,因此 `--commit`/`--staged` 运行会使用相同的准则进行评判。要在快速、无 LLM 的运行中禁用这些门,请将 `GANDALF_LLM_URL` 指向空(它们会 WARN 并退出判定) —— 基于工具的门不受影响。
### 技能支持的咨询门 (3)
第二类技能门,建立在 `gandalf/skillgate.py` 中共享的 `SkillGate` 基类之上。
每个门都原样内嵌了一个技能(位于 `skills/` 下),将其 `SKILL.md` 包装在一个非交互式的评分契约中,并将模型的 0–100 分映射到 RAG。
技能文件是评分准则和唯一的真实来源 —— 编辑技能,门就会随之改变,就像人类运行 `/` 一样。
与上面的评审门不同,这些是 **咨询性的:它们仅发出 🟢 PASS 或 🟡 WARN,永远不会是 🔴 FAIL。** LLM 判断是主观的,因此它们在不硬性阻断构建的情况下突显阻力 —— 确定性的工具门掌握着红线。当 headroom 无法访问、未内嵌技能或范围内没有任何内容时,WARN 也是合理的降级方式:绝不会是误报的通过。这三个都是通用的(在每次更改时运行)。
| 门 | 类别 | 内嵌技能 | 评判 |
|------|----------|-------------------|--------|
| `grill_me` | 设计就绪度 | [`grill-me`](../skills/grill-me) → `grilling` | 更改遗留未解决或模棱两可的关键决策 (PASS ≥ 80)。 |
| `codebase_architecture` | 架构 | [`improve-codebase-architecture`](../skills/improve-codebase-architecture) → `codebase-design` | 深模块健康度 —— 浅模块、局部性差、泄漏的接缝、难以测试的接口 (PASS ≥ 75)。 |
| `well_architected` | Well-Architected | [`well-architected`](../skills/well-architected) | 针对所有六大 Well-Architected 支柱的更改,HRIs/MRIs 按严重性标记 (PASS ≥ 75)。 |
`grill-me` 和 `improve-codebase-architecture` 来自
[mattpocock/skills](https://github.com/mattpocock/skills);`well-architected` 改编自
AWS 的 [sample-wall-architected-skills-and-steering](https://github.com/aws-samples/sample-well-architected-skills-and-steering)。
每个都与其调用的依赖技能(grilling, codebase-design)一起内嵌,因此门可以离线携带完整的评分准则。
## 与 ai-harness 的契约兼容性
`gandalf/base.py` 有意保持与 `ai-harness/app/gates/base.py` 相同的形状(只是内联了 `GateOutcome` 而不是导入),因此门文件可以在两个项目之间保持不变地移动。所有 17 个 ai-harness 门都被移植到了 `gandalf/gates/` 中(替换了耦合:`app.config.settings` → 环境变量 / 模块常量;`compliance` 评判器的 ai-harness 路由器 → `gandalf.llm`),并在此基础上又添加了 14 个 —— Python 质量门 (mypy/vulture/format)、多语言 linter (shellcheck/actionlint/yamllint/codespell) 以及完整的 Go 和 Node/TS 套件 —— 总计 31 个。
## 测试
```
pytest # the whole suite (tests/ + src/ wired via pyproject.toml)
```
每个测试模块也可以在没有 pytest 的情况下独立运行(在裸环境中很方便):
```
for m in tests/test_gandalf.py tests/test_config.py tests/test_suppress.py \
tests/test_sarif.py tests/test_report.py tests/test_run.py; do
PYTHONPATH=src python "$m"
done
```
覆盖率:RAG 聚合 + 插件发现 + 语言过滤 (`test_gandalf`),配置加载和门选择 (`test_config`),抑制与基线 (`test_suppress`),SARIF 渲染 (`test_sarif`),判定策略 + 终端/HTML 渲染 (`test_report`),以及门运行器的有界并发 + 错误隔离 (`test_run`)。
## 项目布局
```
src/gandalf/ the package (CLI + gates/)
tests/ pytest suite
docs/ mkdocs documentation
examples/ runnable examples
.github/ CI workflows, issue/PR templates, dependabot
```
## 文档
完整文档位于 [`docs/`](docs/) (mkdocs)。可运行的示例位于 [`examples/`](examples/)。
## 贡献
请参阅 [CONTRIBUTING.md](CONTRIBUTING.md)。参与即表示您同意 [行为准则](CODE_OF_CONDUCT.md)。
## 安全
发现漏洞?请参阅 [SECURITY.md](SECURITY.md) —— 请不要公开提 issue。
## 支持
需要实施方面的帮助?[联系我们](https://fabiocicerchia.it/contact)。
## 许可证
[Apache-2.0](LICENSE) © Fabio Cicerchia
标签:Git工具, Python, 代码安全审计, 安全专业人员, 无后门, 机密信息扫描, 请求拦截, 逆向工具