startmatter/cerberus
GitHub: startmatter/cerberus
面向 CI/CD 的安全扫描编排与合并门禁工具,将多种安全扫描器结果统一为 SARIF 并仅对新增发现阻断流水线。
Stars: 0 | Forks: 0
# Cerberus
**Cerberus** 是你 CI 的看门狗:在一个容器中集成 SAST (Semgrep)、密钥 (Gitleaks)、依赖 (Trivy)、IaC 配置不当 (Checkov) 和 Dockerfile lint (Hadolint)。它将所有结果合并为单一的 SARIF 报告,发送至你的追踪器,并且只有在出现*新*的发现时才会使流水线失败——你旧的积压记录永远不会阻碍合并。
## 为什么还需要另一个扫描器封装
在 CI 中运行扫描器很容易。但应对结果却并非如此:首次扫描会倾泻出数百个发现,导致每个流水线都报错变红,一周后就会有人关掉这个门禁。Cerberus 保持无状态,并将记忆功能委托给后端(追踪器)。后端会跨扫描对发现进行去重,并返回一个 **delta**(增量)——因此只会为新发现创建任务,已修复的任务会自动关闭,已分类的误报保持静默,并且合并门禁只会针对*新*问题做出反应。
## 工作原理
```
CI job ──▶ cerberus scan
├─ Semgrep (SAST)
├─ Gitleaks (secrets, whole history)
├─ Trivy (dependencies, secrets, misconfig)
├─ Checkov (IaC misconfig)
├─ Hadolint (Dockerfiles)
└─ custom (anything that writes SARIF)
│ merge → one SARIF
▼
POST to backend ──▶ dedup, lifecycle, tasks
◀── { new: 1, known: 51, fixed: 3 }
│
exit code by gate policy (fail_on: new-critical)
```
## 用法
**GitLab** — 包含该模板:
```
include:
- remote: https://raw.githubusercontent.com/startmatter/cerberus/main/templates/gitlab-ci.yml
```
**GitHub** — 调用可复用工作流:
```
jobs:
security:
uses: startmatter/cerberus/.github/workflows/scan.yml@main
secrets:
K_SARIF_URL: ${{ secrets.K_SARIF_URL }}
K_SARIF_SECRET: ${{ secrets.K_SARIF_SECRET }}
```
或者当你需要对周围作业进行控制时,直接驱动该 action:
```
- uses: actions/checkout@v4
with:
fetch-depth: 0 # Gitleaks scans the whole history
- uses: startmatter/cerberus@main
with:
url: ${{ secrets.K_SARIF_URL }}
secret: ${{ secrets.K_SARIF_SECRET }}
# dns: 1.1.1.1,8.8.8.8 # self-hosted runners whose resolver containers can't reach
```
CI 上下文(repo、分支、commit、作者、已更改的文件)是从 GitLab CI / GitHub Actions 的环境变量中检测到的。`K_SARIF_URL` 和 `K_SARIF_SECRET` 来自于追踪器的集成设置——请在组级别 (GitLab) 设置它们,或将其作为组织 secrets (GitHub) 设置,以便每个 repo 都能继承它们。在 `auto` 模式下,默认分支会进行报告(创建/关闭任务),而其他所有分支则进行检查(只读门禁)——因此一行作业配置即可覆盖推送和合并请求。
为依赖扫描添加一个夜间定时流水线:新的 CVE 会出现在从未更改过的代码中,因此仅靠推送无法浮现它们。
### 自托管 runner
如果 runner 位于其容器无法访问的 VPN 解析器之后,Semgrep 将无法获取其规则,Trivy 也无法获取其漏洞数据库——并且这两者都会悄无声息地生成一份空报告。请传入 `dns: 1.1.1.1,8.8.8.8` (GitHub) 或者在 runner 的 docker 配置中设置 `--dns` (GitLab)。
### 拉取私有镜像
容器包默认是私有的,并且某些组织不允许公开包。在这种情况下,runner 必须进行身份验证:
- **GitLab** — 添加一个 `DOCKER_AUTH_CONFIG` CI 变量(在组级别):
`{"auths":{"ghcr.io":{"auth":""}}}`,其中 token 是具有 `read:packages` 权限的 GitHub PAT。
- **GitHub,同一组织** — 无需任何操作:该 action 会使用作业的 `GITHUB_TOKEN` 进行登录。请赋予该作业 `permissions: packages: read` 权限。
- **GitHub,另一个组织** — 某个 repo 自己的 token 无法读取另一个组织的私有包。请传入一个具有 `read:packages` 权限的 PAT,作为 `REGISTRY_TOKEN` secret(用于可复用工作流)或 `registry-token` 输入(用于 action)。
配置位于 repo 根目录的 [`cerberus.yml`](cerberus.example.yml) 中——包括扫描器、额外参数、自定义发出 SARIF 的检查以及门禁策略。
本地运行:`docker run -v $(pwd):/src ghcr.io/startmatter/cerberus scan` ——打印发现结果表格,除非你传入 `--upload`,否则不会上传任何内容。退出码:0 为正常,1 为门禁失败,2 为运行时错误。
## 设计原则
- **无状态。** Cerberus 扫描所有内容并发送所有内容。历史记录、去重、基线和抑制状态都存在于后端。
- **增量门禁。** 流水线仅在由变更引入的发现上失败。已存在的积压记录永远不会阻碍合并。
- **无需第二个 UI。** 分类在你的追踪器中进行:将一个任务作为 *declined*(已拒绝)关闭,该发现将被永久抑制;将其作为 *done*(已完成)关闭,下一次扫描将验证该修复。
- **自带后端。** 上传契约是围绕 SARIF 的一种有文档记录的 JSON 封装;任何实现了该契约的后端都可以工作。
## 路线图
- [x] v0:CLI —— 运行扫描器、合并 SARIF、上传、根据响应设置门禁
- [x] 带有固定扫描器版本的 Dockerfile
- [x] 用于合并请求的 `check` 模式(与基线比对进行分类,不进行写入)
- [x] GitLab CI 模板和 GitHub composite action
- [x] 每次推送到 main 分支时将镜像发布到 ghcr.io
- [ ] 带有增量表格的 MR/PR 评论
- [ ] 更多检测核心:Hadolint、Checkov、许可证审计、Zizmor
扫描器规则(例如 Semgrep registry)是在运行时获取的,并由其各自的所有者授权;Cerberus 不会捆绑它们。
## 许可证
[Apache-2.0](LICENSE)
标签:DevSecOps, SARIF, SAST, 上游代理, 代码安全扫描, 盲注攻击, 自动化攻击, 请求拦截