Baneado98/ci-sentinel

GitHub: Baneado98/ci-sentinel

针对七大主流 CI/CD 平台的深度安全审计工具,结合实时威胁情报与跨文件污点分析,自动检测并修复流水线中的供应链漏洞与注入风险。

Stars: 0 | Forks: 0

# ci-sentinel 🛡️ **针对七大 CI 生态系统的安全审计工具 —— GitHub Actions、GitLab CI/CD、Jenkins、CircleCI、Azure Pipelines、Bitbucket Pipelines 和 Travis CI** —— 能在攻击者的拉取/合并请求运行带有你仓库机密的代码*之前*,找出 YAML linter 无法察觉的供应链和注入缺陷。 以 **MCP 服务器**(适用于 Claude / Cursor / 任何 agent)和**按次付费的 HTTP API**(x402 USDC 或预付卡密钥)形式提供。 只需提供你的 `.github/workflows/*.yml`、`.gitlab-ci.yml`、`Jenkinsfile`、`.circleci/config.yml`、`azure-pipelines.yml`、`bitbucket-pipelines.yml` 和/或 `.travis.yml`(每个文件都会自动路由到正确的分析器),即可获得包含具体缺陷的 **CRITICAL / VULNERABLE / RISKY / HARDENED**(严重 / 易受攻击 / 有风险 / 已加固)判定结果。 ## 它能捕捉到 linter 无法发现的缺陷 | 类别 | ci-sentinel 的作为 | |---|---| | 💉 **表达式注入(步骤间 / 作业间污染)** | 将不受信任的 `${{ github.event.* }}`(issue/PR 标题、正文、评论、审查、分支名、提交信息、**标签名、fork 仓库身份、编辑前值**)溯源到 `run:` shell **或** `actions/github-script` 接收端 —— 并**追踪其跨越 `steps..outputs.*`、`needs..outputs.*`、`env:` 变量、`matrix` 值和可复用工作流 `inputs.*` 的过程**。隐藏在中间输出背后的不受信任值依然无所遁形;而针对单文件的 grep 做不到这点。 | | 🎭 **Pwn 请求** | `pull_request_target` / `workflow_run` 在拥有特权和机密的环境中**检出不受信任的 fork 代码** = 利用你的机密执行任意代码。 | | 🔑 **Token 权限** | 过大的 `GITHUB_TOKEN` 权限范围(`write-all`、`contents:write`、`id-token:write`),缺失 `permissions:` 块,以及**向不受信任的触发器暴露机密**。 | | 🔁 **可复用工作流** | `workflow_call` 调用者将不受信任的数据作为 `inputs.*` 跨越边界传递,以及使用 **`secrets: inherit`** 将完整的机密集交给处于不受信任触发器下的可复用工作流。 | | 📌 **Action 锁定** | 第三方 actions(以及**可复用工作流的 `uses:`**)使用**可变标签/分支**而非提交 SHA(类似 `tj-actions/changed-files` 的劫持方式),并通过**传递性 action 图谱**进行解析。 | | 🖥️ **自托管 RCE** | 可从**公开仓库 PR** 访问的自托管 runner(在你自己的基础设施上实现 RCE),以及 **OIDC `id-token` 信任**的滥用。 | ### ……以及对 **GitLab CI/CD** (`.gitlab-ci.yml`) 的同等深度检测 | 类别 | ci-sentinel 的作为 | |---|---| | 💉 **CI 变量注入** | 将不受信任的 GitLab 变量(`CI_COMMIT_REF_NAME/BRANCH/TAG`、`CI_MERGE_REQUEST_TITLE/DESCRIPTION/SOURCE_BRANCH_NAME`、提交信息/作者、触发用户字段)溯源到插入 `script:` 的部分 —— **追踪其在 `variables:` 和 `extends:` 模板中的污染传播**。它了解安全的变量(`CI_COMMIT_SHA`、`CI_MERGE_REQUEST_IID`、`CI_PROJECT_ID` 等),因此不会盲目标记每一个 `$CI_`。 | | 🎭 **Fork 合并请求 pwn** | 可从 **fork MR 流水线**(`merge_request_event`、`only: merge_requests`)访问的作业,这些作业携带**机密/宽泛的 `CI_JOB_TOKEN`/`id_tokens` (OIDC)**,或者在没有 `when: manual` 门禁的情况下运行**特权操作(部署/发布/`kubectl`/`terraform`)**。 | | 📦 **`include:` 供应链** | `include:` 来自**远程 URL** 或另一个**处于移动(非 SHA)引用上的项目/组件** —— 外部流水线代码被合并到你的代码中。 | | ☣️ **制品/缓存中毒** | **不受信任的作业**写入制品/缓存,而**具有特权的下游作业**消费并执行它(`needs:`/`dependencies:` 或共享缓存路径) —— 一种跨作业**及跨流水线**的提权。 | ### ……以及另外五个 CI 系统,使用相同的污染模型 | 生态系统 | ci-sentinel 捕捉的缺陷 | |---|---| | 🔧 **Jenkins** (`Jenkinsfile`) | 从 `params.*` / `env.CHANGE_*` / `ghprb*` / SCM 数据注入到 `sh`/`bat`/`powershell`(通过 `environment{}` 绑定)的命令注入;凭据泄露(`credentials()`/`withCredentials` 被打印或嵌入 GString);Groovy `evaluate()`/`load` RCE;缺失 `input()` 批准门禁;不安全的 `agent any`。 | | ⭕ **CircleCI** (`.circleci/config.yml`) | 从 `<< pipeline.git.branch/tag >>` / 流水线参数注入到 `run:` 的 shell 注入;未锁定的 **orbs**(`@volatile` 或仅有主版本号);在没有 `type: approval` 门禁的情况下暴露 fork PR 的 **context secret**;没有批准的特权部署。 | | 🟦 **Azure Pipelines** (`azure-pipelines.yml`) | 从 `$(Build.SourceBranch)` / `$(System.PullRequest.SourceBranch)` / 提交信息注入到 `script:`/`bash:`/`pwsh:` 的宏注入;来自外部仓库资源的不可信**模板**;fork 的**变量组**机密暴露;未锁定的仓库资源。 | | 🪣 **Bitbucket Pipelines** (`bitbucket-pipelines.yml`) | 从 `$BITBUCKET_BRANCH` / `$BITBUCKET_TAG` / `$BITBUCKET_PR_DESTINATION_BRANCH` / 提交信息在未加引号的情况下扩展到 `script:` 的 shell 注入;在外部可触发的 PR 流水线上暴露**安全/部署变量**;未锁定的 **pipes**(`:latest`);没有 `trigger: manual` 门禁的部署。 | | 🚦 **Travis CI** (`.travis.yml`) | 从 `$TRAVIS_BRANCH` / `$TRAVIS_PULL_REQUEST_BRANCH` / `$TRAVIS_COMMIT_MESSAGE` 在未加引号的情况下扩展到生命周期钩子的 shell 注入;**安全环境变量**在 PR/fork 的机密暴露;没有 `on:` 分支/条件门禁的 `deploy:` 阶段。 | ### ……加上两个一次性的 agent 无法复制的深度跨域检测器 | 检测器 | ci-sentinel 捕捉的缺陷 | |---|---| | ☁️ **OIDC 云信任配置错误**(Terraform / CloudFormation / GCP workload-identity / Azure federated-credential) | 对 CI OIDC 的**云端部分**进行建模:IAM 角色 / WIF 池 / 联合应用的信任策略。标记具有宽泛通配符的 `sub` 条件(`repo:org/*`、`repo:*`)、**完全没有 `sub` 条件**(Issuer 上的任何工作流都可以扮演角色)、仓库已锁定但引用/环境**未**锁定(任何分支都可以扮演角色)、纯粹的 `pull_request` 主体(可从 fork 访问),或未锁定的 `aud`。然后**将 IaC 信任与 CI 端关联**(一个从不受信任的触发器生成 `id-token` 的工作流),并在链条端到端可达时**将其升级为严重**。单文件 linter 无法捕捉到这一点 —— 它跨越了 CI 声明和云信任策略。 | | 🔧 **Jenkins 共享库**(`@Library` → `vars/*.groovy`) | 将不受信任的流水线值(PR 标题 / 分支 / 构建参数)溯源到通过库的 `call()` 内部传递给共享库全局变量步骤的、最终到达内部 `sh`/`bat` 接收端的过程 —— 这是 Jenkins 中等同于 orb/template/composite-action 的跨文件污染,**仅在阅读 `Jenkinsfile` 时是不可见的**。同时还会将处于**可变引用**(分支 / 默认版本)上的 `@Library` 导入标记为供应链风险。 | 每个分析器都了解**安全**的变量(SHA、数字 ID、受信任的 slug —— `BITBUCKET_COMMIT`、`TRAVIS_PULL_REQUEST`、`pipeline.git.revision` 等)以及安全的 OIDC 信任(锁定到 `repo:org/repo:ref:refs/heads/main` / `:environment:prod` 并设置了 `aud` 的 `sub`),因此它不会盲目标记每一个 `$VAR` 或每一个信任。对加固的配置零误报。 它是一个真正的静态分析器:具备行感知能力的工作流 YAML 解析器 → 构建 triggers/jobs/steps/permissions/actions/outputs/needs 模型 → 跨步骤/跨作业的污点解析器 + 权限及供应链检测器 → 带有每个发现对应的 **file:line**、**完整污点路径**、具体**修复建议**以及可上传至 **GitHub code scanning** 的 **SARIF 2.1.0** 报告(包含 `codeFlows`)的评分判定。 ### ……而且它不仅进行标记 —— 它还能修复,并进行评级 | 高级功能 | 你将获得 | |---|---| | 🔧 **自动修复(精确的解决方案)** | 对于每个发现,ci-sentinel 会生成**具体的更改**:修正后的 YAML/Groovy 代码片段**以及你可以应用的统一差异** —— 将 action/orb/pipe 锁定到不可变的 SHA/版本,绑定环境变量并为注入的表达式加引号,将 write-all 降级为最小权限,插入手动/批准门禁,锁定 OIDC `sub`/`aud`,将硬编码的机密移至存储中(并进行轮换)。不是提示“你有一个 bug”,而是提供**“将此处更改为这”**的指导,并附带置信度标签(`exact` / `template` / `guided`),让你了解哪些是可以直接替换的,哪些需要填充具体值。发现结果携带稳定的 `fingerprint` 以便去重,且 SARIF 结果包含内联的 `fixes`。 | | 📋 **合规记分卡 (A–F)** | 采用类似 CIS 的 CI 安全基准测试,给出 **A–F** 的评分,并对每个控制项进行 **PASS / FAIL / WARN / N/A 细分**(最小权限、组件锁定、不对 fork 暴露机密、不在公开 PR 上使用自托管 runner、OIDC 已锁定、无硬编码机密、无 pwn checkout、受控部署、无注入、无跨作业中毒)。每个控制项都列出了决定其判定结果的**发现 ID** —— 能够向安全团队提供有力证明。一个直接可利用的**严重**缺陷会直接限制最高评分(一个开放的 RCE/机密窃取路径足以主导整体的安全态势)。 | | 🛰️ **实时威胁情报源(数据护城河)** | 配置中的每一个 `uses:` / orb / pipe 都会与**实时的、服务器端的已知受损 CI 组件情报源**进行比对,该源**通过定时任务从公开渠道摄入 —— [OSV.dev](https://osv.dev) 和 [GitHub Security Advisory](https://github.com/advisories) 数据库 (`ecosystem=actions`)** —— 并合并到精选的标志性事件种子库中(`tj-actions/changed-files` CVE-2025-30066、`reviewdog/action-setup` CVE-2025-30154 及其传递依赖项、`aquasecurity/trivy-action` 标签强制推送事件、Ultralytics)。它知道 `tj-actions/changed-files@v45` 是**有毒的**,而同一个 action **如果 SHA 锁定到干净的提交**或**升级到 `@v46` 则是安全的** —— 这是静态 YAML 扫描或本地 linter **无法**做到的区别,因为它们没有数据源。该数据源**仅存在于托管服务器上**(轻量级客户端从不携带它);`GET /feed/status` 会展示其新鲜度。**实时信誉信号**(Star 数 / 最后提交时间 / 是否归档 / 仓库是否仍然解析)还会额外标记**已归档/废弃**的组件以及作为接管诱饵的**悬空**引用。**这是证明托管付费模式合理性的访问护城河:**其价值在于实时的数据,而非计算能力。 | | 🪤 **标签重写与冒名顶替者检测(实时引用→提交)** |提供你*预期*可变引用所对应的提交 SHA(来自你的 lockfile/SBOM),它会解析该标签在上游**当前**指向的内容 —— 从而标记**标签重写 / 强制推送**(`@v2` 现在运行的是与你审查时*不同*的提交 —— 精确的 tj-actions 攻击手法)、**已删除/悬空的引用**,或**可疑的近期移动**。它还将**域名抢注检测与实时信誉信号**结合,以确认**冒名行为** —— 例如 `actons/checkout`(极其接近 `actions` 的易混淆名称),如果其仓库是一个刚建立、Star 数少或悬空的冒名者,就会被标记;相对地,如果是合法命名的小型 fork 则不会被标记(零误报)。并且由于数据源是以**组件身份**作为键的,因此无论哪个 CI 系统引用了受损的 action,它都会被捕捉到(跨 CI) —— 这是单一 CI 扫描器在结构上根本无法具备的能力。 | ## MCP 服务器(免费) ``` { "mcpServers": { "ci-sentinel": { "command": "npx", "args": ["-y", "ci-sentinel-mcp"] } } } ``` 工具:**`audit_ci_security`**。传入 `files`(工作流文件名 → YAML 的映射)或 `source`(单个工作流)。免费层级返回判定结果以及发现了**多少**种类型的问题。 ## 深度审计 (`deep: true`) —— 两种支付方式 深度审计返回**每一个发现及其 file:line、完整的跨步骤/跨作业注入污点路径、传递性 action 供应链图谱、针对每个发现的具体修复建议(修正的代码片段 + 统一差异)、带有各项控制细分的 A–F 合规记分卡,以及包含内联 `fixes` 的 SARIF 2.1.0 文档**(可通过 `github/codeql-action/upload-sarif` 上传至 GitHub code scanning)。 ## HTTP API ``` POST /audit # free, rate-limited — verdict + per-severity counts POST /pro/audit # deep — pay-per-call (x402) or Authorization: Bearer POST /mcp # MCP-over-HTTP (free) GET /presence # live active sessions (last 5 min) GET / # this landing page ``` ``` curl -X POST https://ci-sentinel.vercel.app/audit \ -H 'Content-Type: application/json' \ -d '{"files":{".github/workflows/ci.yml":"name: CI\non: pull_request_target\n..."}}' ``` ## 为什么采用托管模式?(护城河) npm 包是一个**轻量级客户端**:它不在本地运行任何分析。解析器、污点引擎、action 图谱解析器和检测器知识库**仅在服务器端**、付费墙之后运行 —— 因此高价值的内容(发现、证据、污点路径、修复建议)永远不会出现在调用者的机器上。`npm pack` 仅发布 `dist/mcp.js`、`dist/mcpServer.js` 和 `dist/types.js`;引擎绝不会出现在 tarball 中(已通过 `npm run test:moat` 自动验证)。 ## 实时数据源如何保持更新(更新流水线) 威胁情报源是**动态摄入的,而非硬编码**。服务器端会运行一个周期性任务: 1. **获取**来自两个公开来源的安全公告(无需付费密钥): - **GitHub Security Advisory DB** —— `GET /advisories?ecosystem=actions`(公开;`GITHUB_TOKEN` 仅用于提高速率限制,**并非必需**)。 - **OSV.dev** —— 针对 `GitHub Actions` 生态系统,查询我们已追踪的组件以及 GHSA 刚刚披露的所有内容(以便 OSV 对其进行丰富补充)。 2. **将**每个公告**规范化**为 `ThreatRecord` —— 拆分所有者/名称,根据公告文本推断事件类别(标签重写 / 维护者受损 / 恶意 / CVE……),划分严重性级别,将受影响的版本范围解析为选择器,捕获**已修复版本**(因此升级后的锁定版本**不会**触发误报)。 3. 将行**合并**到精选种子上(`ingestFeed`:联合引用,保留最严重的级别,扩大受影响范围,按身份去重)。 4. 将快照**存储**在 Cloudflare KV 中。每次 `/pro/audit` 都会通过 `kvFeedProvider` 将其读回,如果 KV 处于冷状态,则**回退到编译好的种子库** —— 从而保证检测器永远不会被阻塞,且永远不会差于静态基准。 ``` # 运行一次刷新(live:调用真实的公共 API) node scripts/refreshFeed.mjs # 无网络的 dry run(生成仅 seed 的快照) CI_SENTINEL_NO_NET=1 node scripts/refreshFeed.mjs ``` 使用 **Vercel Cron**(`{ "path": "/internal/refresh-feed", "schedule": "0 */6 * * *" }`,受 `FEED_REFRESH_TOKEN` 保护)或通过计划任务 GitHub Action 来调度它,执行 `node scripts/refreshFeed.mjs`。`GET /feed/status` 报告实时快照的版本、`generatedAt` 和记录数 —— 公开的新鲜度证明。这些内容都不会包含在 npm 客户端中。 ## 开发 ``` npm install npm run build # tsc npm test # engine + parser + taint + SARIF + autofix + compliance + corpus tests (851 checks) npm run test:moat # lock-proof: tarball ships no engine, deep degrades w/o pay npm run dev:http # local HTTP server (FORCE_LISTEN) ``` 对你提供的工作流 YAML 进行启发式静态分析 —— 它无法查看仓库级别的默认 token 权限、分支保护或组织策略,也不会执行流水线。请将发现结果视为需要验证的线索。 源码与文档:https://github.com/Baneado98/ci-sentinel · MIT
标签:DevSecOps, MCP, MITM代理, Python脚本, StruQ, 上游代理, 图数据库, 安全扫描, 文档安全, 时序注入