fabiocicerchia/gandalf

GitHub: fabiocicerchia/gandalf

一款纯标准库实现的 Git 仓库安全质量门评估器,通过可插拔门机制整合数十种扫描工具,在一遍扫描中完成代码质量、密钥泄露、依赖漏洞和供应链风险的审计。

Stars: 0 | Forks: 0

# 🧙 Gandalf — 代码库质量门评估器 [![code-quality](https://static.pigsec.cn/wp-content/uploads/repos/cas/bc/bc38865df091bbecbad136d0d706024aa6a353665be2ed553693d3a0227c88c2.svg)](https://github.com/fabiocicerchia/gandalf/actions/workflows/code-quality.yml) [![security](https://static.pigsec.cn/wp-content/uploads/repos/cas/51/5138cdb0145abb48ac3230567bd99162785aadb0d5aba2c0be62d49091422d8b.svg)](https://github.com/fabiocicerchia/gandalf/actions/workflows/security.yml) [![License](https://img.shields.io/badge/license-Apache--2.0-blue.svg)](LICENSE) [![OpenSSF Scorecard](https://api.securityscorecards.dev/projects/github.com/fabiocicerchia/gandalf/badge)](https://securityscorecards.dev/viewer/?uri=github.com/fabiocicerchia/gandalf) [![FOSSA Status](https://app.fossa.com/api/projects/git%2Bgithub.com%2Ffabiocicerchia%2Fgandalf.svg?type=shield)](https://app.fossa.com/projects/git%2Bgithub.com%2Ffabiocicerchia%2Fgandalf?ref=badge_shield) [![Release](https://img.shields.io/github/v/release/fabiocicerchia/gandalf)](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 指向它: ``` ![gandalf score](https://img.shields.io/endpoint?url=) ``` ## 门是插件 门是任何具有 `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, 代码安全审计, 安全专业人员, 无后门, 机密信息扫描, 请求拦截, 逆向工具