jpr5/gh-workflow-scanner

GitHub: jpr5/gh-workflow-scanner

确定性 GitHub Actions 工作流安全扫描器,覆盖 21 条规则并支持自动修复,帮助团队在 CI/CD 流程中持续发现和修复供应链安全隐患。

Stars: 0 | Forks: 0

# Sentinel **专为 GitHub Actions 工作流设计的确定性安全扫描器** ![Build](https://img.shields.io/badge/build-passing-brightgreen) ![Ruby](https://img.shields.io/badge/ruby-3.2%2B-red) ![License](https://img.shields.io/badge/license-MIT-blue) 扫描 GitHub Actions 工作流中的 21 个安全漏洞。无需 AI,无需 gems —— 纯 Ruby stdlib 实现。 文档:https://sentinel.copilotkit.dev ## 安装说明 ``` # 一次性执行 (类似 npx — Ruby 3.2+) gem exec sentinel-ci scan owner/repo # 或者全局安装 gem install sentinel-ci sentinel scan owner/repo # 或者 clone 并直接运行 git clone https://github.com/CopilotKit/sentinel.git cd sentinel export GITHUB_TOKEN=$(gh auth token) bin/sentinel scan owner/repo ``` 要求 Ruby 3.2+。除 stdlib(`yaml`、`net/http`、`optparse`、`json`)外无其他依赖。 ## 用法 ``` # 扫描单个 repo bin/gh-workflow-scanner owner/repo # 扫描本地 checkout bin/gh-workflow-scanner --local /path/to/repo # 扫描整个 GitHub org bin/gh-workflow-scanner --org my-org # JSON 输出,筛选 high 及以上 severity bin/gh-workflow-scanner --format json --severity high owner/repo ``` ## GitHub Action 用作 GitHub Action 以在每个 PR 上自动扫描工作流: ``` - uses: jpr5/gh-workflow-scanner-action@v1 with: severity: high ``` 完整的工作流示例: ``` name: Workflow Security Scan on: pull_request: paths: ['.github/workflows/**'] permissions: contents: read jobs: scan: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - uses: jpr5/gh-workflow-scanner-action@v1 id: scan with: severity: high fail-on-findings: true ``` **输入参数:** | 名称 | 默认值 | 描述 | |------|---------|-------------| | `severity` | `high` | 最低严重级别:`critical`、`high`、`medium`、`low` | | `fail-on-findings` | `true` | 如果存在超过阈值的发现则使检查失败 | **输出结果:** | 名称 | 描述 | |------|-------------| | `findings-count` | 达到或超过严重级别的发现总数 | | `critical-count` | 严重 级别发现数量 | | `high-count` | 高危 级别发现数量 | 发现结果将以行内注释的形式显示在 PR diff 中 —— critical/high 显示为错误,medium 显示为警告,low 显示为通知。 ## 检查内容 | # | 规则 | 严重性 | 内容 | |---|------|----------|------| | 1 | `unpinned-actions` | critical/medium | 标签锁定 actions(第三方为 critical,`actions/*` 为 medium) | | 2 | `shell-injection-expr` | critical | `run:` 块中存在攻击者可控的 `${{ }}` | | 3 | `shell-injection-jq` | critical | 双引号 jq/curl 字符串中的 `${VAR}` | | 4 | `dangerous-triggers` | critical | `pull_request_target` + 拉取 fork 代码 | | 5 | `missing-persist-credentials` | high | `actions/checkout` 未设置 `persist-credentials: false` | | 6 | `credential-window` | high | Git 凭证配置位置远离 push 步骤 | | 7 | `static-aws-credentials` | high | 使用静态 AWS 密钥而非 OIDC 联合身份验证 | | 8 | `unscoped-app-token` | high | `create-github-app-token` 未进行 `permission-*` 范围限定 | | 9 | `docker-build-arg-secrets` | high | Docker build-args 中包含 Secrets(在 image 层中可见) | | 10 | `build-publish-same-job` | high | 在包含发布 Secrets 的同一 job 中进行构建和发布 | | 11 | `curl-pipe-shell` | high | 无完整性校验的 `curl \| sh` | | 12 | `missing-permissions` | medium | 缺少顶层 permissions 块 | | 13 | `git-config-global` | medium | 带有凭证的 `git config --global` | | 14 | `missing-timeouts` | medium | 缺少 `timeout-minutes` 的 Jobs | | 15 | `missing-env-protection` | medium | 缺少环境保护的 Publish/deploy jobs | | 16 | `allow-forks-artifact` | medium | 在特权上下文中下载由 Fork 生成的 artifact | | 17 | `missing-frozen-lockfile` | medium | 未使用 `--frozen-lockfile` / `npm ci` 进行包安装 | | 18 | `unpinned-docker-image` | low | 使用 `:latest` 标签的 Docker 镜像 | | 19 | `overly-broad-triggers` | low | 无 branch/path 过滤器的 Push/PR 触发器 | | 20 | `missing-dependabot` | low | 缺少针对 github-actions 生态系统的 Dependabot 配置 | | 21 | `missing-zizmor` | low | 无 zizmor 静态分析工作流 | ## 自动修复 Sentinel 可以自动生成三类规则的修复方案: ``` bin/gh-workflow-scanner --fix owner/repo # future CLI flag ``` 或者直接使用 Ruby API: ``` require_relative "lib/auto_fix" require_relative "lib/sha_resolver" resolver = ShaResolver.new patched = AutoFix.apply(finding, raw_yaml, sha_resolver: resolver) ``` **可修复的规则:** | 规则 | 修复策略 | |------|-------------| | `unpinned-actions` | 通过 GitHub API 将 tag 解析为 SHA | | `shell-injection-expr` | 将表达式移动到步骤级 `env:` 块中 | | `missing-persist-credentials` | 为 checkout 添加 `persist-credentials: false` | ## PR 机器人 主动扫描热门公共仓库,并为 critical 发现开启修复 PR。 ``` ruby bot/scanner_bot.rb --pattern shell-injection --dry-run ``` **特性:** - GitHub Code Search 查找易受攻击的仓库 - 为可机械化修复的规则自动生成修复 PR - 频率限制(50 PRs/天),stars 阈值(>100) - 支持退出,机器人身份清晰明确 - 作为每日 cron 通过 GitHub Actions 运行 ## 选项 ``` --format FORMAT terminal (default) or json --severity LEVEL minimum severity: critical, high, medium, low (default: low) --local PATH scan local directory --org ORG scan all repos in a GitHub org --token TOKEN GitHub API token (default: GITHUB_TOKEN env var) ``` ## 退出代码 - `0` -- 无 critical 或 high 发现 - `1` -- 存在 critical 或 high 发现 - `2` -- 用法错误 ## 架构 ``` bin/gh-workflow-scanner # CLI entry point (optparse) action/ annotate.rb # GitHub Action annotation emitter lib/ scanner.rb # orchestrator rule_engine.rb # loads + runs all rules workflow.rb # YAML parser + helpers finding.rb # finding data struct github_client.rb # GitHub API client local_client.rb # filesystem client auto_fix.rb # auto-fix engine sha_resolver.rb # GitHub tag -> SHA resolver formatter/ terminal.rb # colored terminal output json.rb # JSON output rules/ base.rb # abstract rule interface *.rb # one file per rule (19 rules) bot/ scanner_bot.rb # PR bot orchestrator search.rb # GitHub Code Search client state.rb # JSON-file state tracking pr_writer.rb # cross-fork PR creation config.rb # bot configuration ``` ## 添加规则 创建 `lib/rules/my_rule.rb`: ``` module Rules class MyRule < Base def name = "my-rule" def description = "What this detects" def severity = :high # :critical, :high, :medium, :low def check(workflow) findings = [] # workflow.uses_actions, workflow.run_blocks, workflow.raw_lines, etc. # Use finding() helper or construct Finding.new() directly findings end end end ``` 规则会从 `lib/rules/` 自动发现。 ## 许可证 MIT
标签:CI/CD安全, DevSecOps, GitHub Actions, GitHub Action插件, Llama, Ruby, StruQ, YAML解析, 上游代理, 动态分析, 安全合规, 工作流安全, 无依赖, 知识库, 确定性扫描, 纯Ruby, 组织安全扫描, 网络代理, 自动化安全检查, 自动笔记, 跨仓库扫描, 软件开发工具包, 错误基检测, 静态代码分析