EricCogen/GauntletCI-Demo
GitHub: EricCogen/GauntletCI-Demo
GauntletCI演示仓库,展示如何在实际PR中通过GitHub Actions检测代码变更的行为风险。
Stars: 1 | Forks: 0
# GauntletCI 演示
## 什么是此仓库
此仓库 **并非** 一个可运行的应用程序。它是一个受控的演示环境,其唯一目的是让您无需自行安装任何东西,就能查看 GauntletCI 在真实代码变更上的输出。
它包含:
1. 一个 **小型但真实的 .NET 8 示例应用**(`OrderService` - 一个支付处理服务,包含支付客户端和订单处理器),这样被分析的差异看起来就像您实际会编写的代码。
2. 一个 **GitHub Actions 工作流**(`.github/workflows/gauntlet.yml`),它在每个 PR 上安装从 NuGet 发布的 GauntletCI 工具,并针对 PR 差异运行它,将发现作为内联注释、PR 审阅评论和检查 API 判定发布。
3. 一个位于 `scenarios/` 下的 **标准演示场景库**。每个场景都是一次刻意的代码更改(静默异常吞没、硬编码密钥、破坏性 API 变更、日志中的 PII、并发竞争和一个无操作对照),用于测试不同的 GauntletCI 规则。
4. 一个 **`workflow_dispatch` 操作**(`.github/workflows/reopen-scenarios.yml`),用于按需重建每个场景分支并重新打开其 PR。这使得演示能够针对最新发布的工具版本自我更新,无需手动 git 操作。
## 这个仓库 *不是* 什么
- ❌ 并非 `OrderService` 的生产级参考架构。
- ❌ 并非提交 GauntletCI 错误或功能请求的地方 - 请使用[主仓库的问题跟踪](https://github.com/EricCogen/GauntletCI/issues)。
- ❌ 并非在您自己的代码库上进行真实测试的替代品。请在您自己的差异上运行 `gauntletci analyze`,以查看针对您代码的发现。
## 理解 GauntletCI 的价值主张
GauntletCI 在预提交分析期间检测 git 差异中的**行为变更风险**。这与全项目快照 SAST 工具在本质上是不同的。
### 这意味着什么
**SAST 工具(CodeQL, Semgrep, SonarQube 等):**
- 在 CI 期间扫描整个代码库(需数分钟过程)
- 查找已知漏洞特征码和代码质量模式
- 非常擅长发现硬编码密钥、SQL 注入模式、标准反模式(如 `.Result` 死锁)
- 作为 CI 门控,在编译/打包阶段运行
**GauntletCI:**
- 在预提交期间仅分析 git 差异(亚秒级)
- 检测特定变更增量内的结构变异、执行序列变更、边界漂移
- 捕获那些编译通过、所有测试通过、但会破坏生产系统的行为回归
- 在您提交代码之前、代码审查之前、CI 之前运行
### 18 个场景:它们展示了什么
这 18 个行为场景展示了 GauntletCI 能检测到而全项目快照工具无法看到的内容:
| 类别 | 场景 | 展示内容 |
|------|------|----------|
| **架构访问控制** | S19, S23, S24 | 差异中访问边界的移除/修改,而没有相应的验证更改 |
| **执行序列变更** | S20, S28-S30 | 状态变异或外部调用被重新排序,其语法正确但依赖执行顺序 |
| **异步传播丢失** | S21, S25-S27 | 差异中 CancellationToken 上下文的丢失、即发即忘任务模式、跨方法边界的传播失败 |
| **公共契约漂移** | S22, S31-S32 | 方法签名、默认参数或 API 契约更改,能够编译但在特定更改中破坏调用者 |
| **性能与资源** | S33-S34 | 配置更改、禁用连接池、移除缓存查找,这些对样式检查器不可见 |
| **依赖注入作用域** | S35-S36 | 特定更改中 DI 配置的作用域边界不匹配 |
每个场景:
- 编译成功并通过单元测试
- 能通过 SAST 和代码风格检查
- 引入了只有差异级分析才能在进入生产前捕获的行为风险
### 现场演示
所有工具通过 GitHub Actions 现场运行:
- **DEMO_FINDINGS.md** - 所有 18 个场景的详细工具间比较
- **实时 PR** - 每个场景都运行 CodeQL、Semgrep、SonarQube、StyleCop、Snyk 和 GauntletCI
- **判定结果** - 查看 18/18 vs 0/18 的计分卡
- **总计 36 个场景** - 第 1 层(6),第 2 层(12),第 3 层(18)
## 如何自行验证
**最快的方法:** Fork 此仓库,启用 Actions,运行工作流 `Reopen demo scenarios`
所有分析工具都通过 `.github/workflows/` 中的 GitHub Actions 在每个 PR 上现场运行:
- `codeql.yml` - 在每个 PR 上运行 CodeQL
- `semgrep.yml` - 在每个 PR 上运行 Semgrep
- `stylecop.yml` - 在每个 PR 上运行 StyleCop 检查
- `snyk.yml` - 在每个 PR 上运行 Snyk
- `gauntletci.yml` - 在每个 PR 上运行 GauntletCI
您将实时看到发现(或无发现)。无需下载,无需本地设置——只需 fork,启用工作流,然后观察 PR。
## 如何使用此仓库
### 自行运行(推荐)
这是主打体验:您拥有仓库状态,控制运行,可以在不破坏演示的情况下随意操作。
#### 选项 1 - Fork 并使用 GitHub Actions
1. 将 [`EricCogen/GauntletCI-Demo`](https://github.com/EricCogen/GauntletCI-Demo) **Fork** 到您的账户。
2. **⚠️ 在您的 Fork 上启用 Actions。** GitHub 默认会在新 fork 上禁用工作流。在您的 fork 中,点击 **Actions** 标签页。如果您看到横幅 *"Workflows aren't being run on this forked repository"*(*工作流未在此 fork 仓库上运行*),请点击 **"I understand my workflows, go ahead and enable them"**(*我理解我的工作流,请继续启用它们*)。在您执行此操作之前,reopen-scenarios 工作流不会出现。
3. 前往 **Actions → Reopen demo scenarios → Run workflow**(**操作 → 重新打开演示场景 → 运行工作流**)。
4. 在输入框中键入 `all`(或单个场景文件夹名,如 `03-hardcoded-secret`),然后点击 **Run workflow**(**运行工作流**)。
5. **预计首次运行需约 2 分钟**:工作流重建 `demo/*` 分支并为每个场景打开一个 PR。每个 PR 随后触发 `gauntlet.yml`,该工作流从 NuGet 安装已发布的 GauntletCI 工具(约 30 秒)并在差异上运行(约 5 秒)。
6. 在您的 fork 中打开任何一个新 PR,查看 **Files Changed**(**文件更改**)注释、**Conversation**(**对话**)审阅摘要和 **Checks**(**检查**)判定。
**成功了吗?**
- ✅ 预期:一批标题为 `demo: ` 的新 PR 会出现在您 fork 的 **Pull requests**(**拉取请求**)标签页中,每个 PR 都带有绿色或红色的 **GauntletCI** 检查(与 [`scenarios//README.md`](scenarios/) 中的判定一致)。
- ❌ *没有出现 PR* - 最常见的情况是 Actions 标签页仍然显示禁用横幅。请重新检查步骤 2。
- ❌ *工作流在 `Install GauntletCI` 步骤失败* - 通常是临时的 NuGet 中断。从 **Actions** 标签页重新运行。
- ❌ *工作流在 `Open PR` 步骤因 403 失败* - 您的 fork 在 `main` 分支上设置了阻止机器人的分支保护规则。要么移除该规则,要么将 `DEMO_PR_TOKEN` 设置为可以绕过它的 PAT。
#### 选项 2 - 克隆并在本地运行
如果您的机器上已安装 .NET 8 SDK,此路径最快。
**bash / macOS / Linux:**
```
git clone https://github.com/EricCogen/GauntletCI-Demo.git
cd GauntletCI-Demo
# 安装已发布的工具
dotnet tool install -g GauntletCI
# 构建示例应用
dotnet build
# 在本地应用场景并分析已暂存的差异
cp -r scenarios/02-silent-catch/files/. .
git add -A
gauntletci analyze --staged
```
**PowerShell / Windows:**
```
git clone https://github.com/EricCogen/GauntletCI-Demo.git
Set-Location GauntletCI-Demo
# 安装已发布的工具
dotnet tool install -g GauntletCI
# 构建示例应用
dotnet build
# 在本地应用场景并分析已暂存的差异
Copy-Item -Recurse -Force scenarios/02-silent-catch/files/* .
git add -A
gauntletci analyze --staged
```
您将在自己的机器上,在不到一秒钟内获得与 GauntletCI 在 CI 中产生的相同发现。
**成功了吗?**
- ✅ 预期:控制台输出以 `🛑 Block` 结束,并带有指向场景引入的静默 `catch { }` 块的 `[GCI0007] Error Handling Integrity`(`[GCI0007] 错误处理完整性`)发现。
- ❌ *`gauntletci: command not found`*(*`gauntletci: 命令未找到`*)- dotnet 全局工具文件夹不在您的 `PATH` 中。重启您的 shell 或将 `$HOME/.dotnet/tools`(Unix)/ `%USERPROFILE%\.dotnet\tools`(Windows)添加到 `PATH`。
- ❌ *`error: pathspec 'scenarios/02-silent-catch/files/.' did not match any file(s)`*(*错误:路径规格 'scenarios/02-silent-catch/files/.' 未匹配任何文件*)- 您不在仓库根目录。请先运行 `cd GauntletCI-Demo`。
- ❌ *工具已安装但 `analyze --staged` 报告 `0 findings`*(*0 个发现*)- 场景文件实际上未被暂存。检查 `git status` 并重新运行 `git add -A`。
### 快速查看(无需安装,无需 fork)
如果您只是想*看看*该工具的输出,而无需设置任何东西:
1. 打开 **[Pull Requests 标签页](https://github.com/EricCogen/GauntletCI-Demo/pulls)**。
2. 选择任何带有 `demo:*` 标签的打开 PR。
3. 查看:
- **Files Changed**(**文件更改**)标签页 - GauntletCI 的内联注释出现在触发它们的差异行旁。
- **Conversation**(**对话**)标签页 - GauntletCI 发布一份 PR 审阅,总结发现、严重性和理由。
- **Checks**(**检查**)标签页 - 一个 GauntletCI 检查运行显示整体通过/失败判定。
每个场景的预期判定都记录在其 [`scenarios//README.md`](scenarios/) 中,因此您可以将所见与工具本应捕获的内容进行比较。
### 维护者说明(重新生成规范 PR)
此仓库中的规范 PR 具有自动恢复功能:`reopen-scenarios.yml` 按每周计划以及在每次推送到 `main` 分支时运行,因此展示内容与最新发布的 GauntletCI 版本保持同步。要强制手动重建,请前往 **Actions → Reopen demo scenarios → Run workflow**(**操作 → 重新打开演示场景 → 运行工作流**)。
## 场景
### 第 1 层 - 主打场景
| # | 场景 | 预期判定 | 演示的规则 |
|---|------|----------|------------|
| 01 | [safe-typo-fix](scenarios/01-safe-typo-fix/README.md) | ✅ 无问题 | (无 - 低噪声对照) |
| 02 | [silent-catch](scenarios/02-silent-catch/README.md) | 🛑 阻止 | `GCI0007` 错误处理完整性 |
| 03 | [hardcoded-secret](scenarios/03-hardcoded-secret/README.md) | 🛑 阻止 | `GCI0012` 安全风险 |
| 04 | [breaking-api-change](scenarios/04-breaking-api-change/README.md) | 🛑 阻止 | `GCI0004` 破坏性变更风险 |
| 05 | [pii-logging](scenarios/05-pii-logging/README.md) | ⚠️ 警告 | `GCI0029` PII 日志泄漏 |
| 06 | [concurrency-race](scenarios/06-concurrency-race/README.md) | 🛑 阻止 | `GCI0016` 并发与状态风险 |
### 第 2 层 - 每个规则一个场景
第二波场景,每个在相同的 `OrderService` 示例应用上隔离单个 GauntletCI 规则。每个第 2 层条目的判定都是 ❌ 失败(更改的存在正是为了触发一个规则)。
| # | 场景 | 演示的规则 |
|---|------|------------|
| 07 | [magic-connection-string](scenarios/07-magic-connection-string/README.md) | `GCI0010` 硬编码与配置 |
| 08 | [undisposed-httpclient](scenarios/08-undisposed-httpclient/README.md) | `GCI0024` 资源生命周期 |
| 09 | [insecure-random-token](scenarios/09-insecure-random-token/README.md) | `GCI0048` 安全上下文中的不安全随机数 |
| 10 | [sql-column-truncation](scenarios/10-sql-column-truncation/README.md) | `GCI0050` SQL 列截断风险 |
| 11 | [float-money-equality](scenarios/11-float-money-equality/README.md) | `GCI0049` 浮点/双精度相等比较 |
| 12 | [missing-null-guard](scenarios/12-missing-null-guard/README.md) | `GCI0006` 边界情况处理 |
| 13 | [throw-bare-exception](scenarios/13-throw-bare-exception/README.md) | `GCI0032` 未捕获的异常路径 |
| 14 | [todo-in-payment-flow](scenarios/14-todo-in-payment-flow/README.md) | `GCI0042` TODO/桩代码检测 |
| 15 | [non-idempotent-retry](scenarios/15-non-idempotent-retry/README.md) | `GCI0022` 幂等性与重试安全性 |
| 16 | [tolist-in-loop](scenarios/16-tolist-in-loop/README.md) | `GCI0044` 性能热路径风险 |
| 17 | [captive-dependency](scenarios/17-captive-dependency/README.md) | `GCI0038` 依赖注入安全性 |
| 18 | [dependabot-api-drift](scenarios/18-dependabot-api-drift/README.md) | `GCI0052` 依赖机器人 API 漂移 |
### 第 3 层 - 行为回归场景(扩展)
**18 个高级行为回归场景**,旨在展示 GauntletCI **检测那些能通过传统分析工具(SonarQube, CodeQL, Semgrep, StyleCop, Snyk)的变更的独特能力**。每个场景展示了一个真实的生产错误,它能成功编译,但代表了一个关键回归。
**关键发现:** GauntletCI 检测到了全部 18 个第 3 层场景;竞争对手平均检测到 0/18。
完整比较请参见 [DEMO_FINDINGS.md](DEMO_FINDINGS.md)。
#### 安全与访问控制(S19, S23, S24)
| # | 场景 | 生产影响 |
|---|------|----------|
| 19 | [access-control-drop](scenarios/19-access-control-drop/README.md) | 重构期间剥离安全属性 |
| 23 | [role-based-bypass](scenarios/23-role-based-bypass/README.md) | 授权检查移至条件分支内部 |
| 24 | [encryption-key-rotation-removal](scenarios/24-encryption-key-rotation-removal/README.md) | 简化了解密逻辑,破坏旧的加密数据 |
#### 并发与异步(S21, S25, S26, S27)
| # | 场景 | 生产影响 |
|---|------|----------|
| 21 | [static-mutation-async](scenarios/21-static-mutation-async/README.md) | 异步上下文中的未同步静态变异 |
| 25 | [async-without-await](scenarios/25-async-without-await/README.md) | 调用异步方法而没有 await,导致异常丢失 |
| 26 | [lock-scope-reduction](scenarios/26-lock-scope-reduction/README.md) | 缩小了临界区范围,暴露了竞态条件 |
| 27 | [task-result-deadlock](scenarios/27-task-result-deadlock/README.md) | 同步覆盖异步模式导致挂起 |
#### 数据完整性与业务逻辑(S20, S28, S29, S30)
| # | 场景 | 生产影响 |
|---|------|----------|
| 20 | [audit-log-inversion](scenarios/20-audit-log-inversion/README.md) | 执行顺序变异破坏了合规性日志 |
| 28 | [transaction-rollback-repositioning](scenarios/28-transaction-rollback-repositioning/README.md) | 移动了回滚点,提交了部分更改 |
| 29 | [idempotency-key-removed](scenarios/29-idempotency-key-removed/README.md) | 移除了重复检测,导致重复扣款 |
| 30 | [cascade-delete-to-restrict](scenarios/30-cascade-delete-to-restrict/README.md) | 更改了删除行为,导致关联记录孤立 |
#### API 契约与版本控制(S22, S31, S32)
| # | 场景 | 生产影响 |
|---|------|----------|
| 22 | [breaking-api-contract](scenarios/22-breaking-api-contract/README.md) | 移除了公共 API 参数而没有版本号变更 |
| 31 | [exception-contract-violation](scenarios/31-exception-contract-violation/README.md) | 文档化的异常不再被抛出,破坏了消费者 |
| 32 | [implicit-type-coercion-change](scenarios/32-implicit-type-coercion-change/README.md) | 简化了转换逻辑,改变了边界情况行为 |
#### 性能与资源管理(S33, S34)
| # | 场景 | 生产影响 |
|---|------|----------|
| 33 | [cache-lookup-removed](scenarios/33-cache-lookup-removed/README.md) | 添加了缓存绕过,导致数据库负载激增 |
| 34 | [connection-pooling-disabled](scenarios/34-connection-pooling-disabled/README.md) | 禁用了连接池,引发连接风暴 |
#### 依赖注入与作用域(S35, S36)
| # | 场景 | 生产影响 |
|---|------|----------|
| 35 | [service-locator-anti-pattern](scenarios/35-service-locator-anti-pattern/README.md) | 从服务定位器解析依赖,导致不可测试 |
| 36 | [singleton-captures-scoped](scenarios/36-singleton-captures-scoped/README.md) | 作用域依赖被单例捕获,导致数据泄漏 |
每个场景文件夹包含:
- `README.md` - 变更内容以及预期的判定
- `files/` - 覆盖文件,将其复制到 `main` 以构建演示分支
## 竞争分析:多工具 CI/CD 管道
该演示现在包括自动化的 CI/CD 工作流,在每个 PR 上运行 **5 种互补的分析工具**。这种混合方法让您可以看到免费工具的真实发现,并将其与 GauntletCI 的行为检测进行比较。
**第 3 层扩展:** 现在针对所有 5 个工具测试 18 个行为回归场景,以最大化 GauntletCI 竞争优势的证据。
### 包含的工具
| 工具 | 类型 | 目的 | 免费 | 在 CI 中运行 |
|------|------|------|------|--------------|
| **CodeQL** | 数据流 | 安全污点跟踪 | ✅ | ✅ |
| **Semgrep** | 基于模式 | 自定义规则匹配 | ✅ | ✅ |
| **StyleCop** | 执行 | C# 样式规则 | ✅ | ✅ |
| **Snyk** | 依赖 | 漏洞扫描 | ✅ | ✅ |
| **GauntletCI** | 行为 | 回归检测 | ✅ | ✅ |
### 发现比较
完整分解每个工具在第 3 层场景上的发现(或遗漏),请参见 [**DEMO_FINDINGS.md**](DEMO_FINDINGS.md)。
**快速总结:** 在行为回归(第 3 层场景 - 现在 6 个类别共 18 个场景)上:
- GauntletCI: ✅ 在所有场景中检测到行为变更
- CodeQL, Semgrep, SonarQube, Snyk, StyleCop: ❌ 一贯遗漏行为回归
**覆盖范围:**
- 安全与访问控制:3 个场景
- 并发与异步:4 个场景
- 数据完整性与业务逻辑:4 个场景
- API 契约与版本控制:3 个场景
- 性能与资源管理:2 个场景
- 依赖注入与作用域:2 个场景
这说明了为什么团队在统一的 CI/CD 管道中使用多种工具:每种工具专攻不同的风险类别,而 GauntletCI 填补了行为回归检测的关键空白。
### 自行运行多工具管道
`.github/workflows/` 中的工作流在每个 PR 上自动运行:
```
# 创建测试 PR
git checkout -b test/try-scenarios main
cp -r scenarios/19-access-control-drop/files/. .
git add -A && git commit -m "test: behavioral regression scenario"
git push origin test/try-scenarios
```
然后向 `main` 提交一个 PR。GitHub Actions 将运行所有 5 个工具,并将发现发布在 **Checks** 标签页中。将结果与 [DEMO_FINDINGS.md](DEMO_FINDINGS.md) 进行比较。
## CI 安装如何工作
CI 工作流使用 **真实用户遵循的相同安装路径**,因此该演示也充当了已发布工具的冒烟测试:
```
- run: dotnet tool install -g GauntletCI
- run: |
gauntletci analyze \
--commit ${{ github.event.pull_request.head.sha }} \
--no-banner \
--github-annotations \
--github-pr-comments \
--github-checks
```
没有从源代码构建,没有预发布源 - 只是来自 NuGet 的 `dotnet tool install`。
## 仓库布局
```
GauntletCI-Demo/
├── src/OrderService/ # sample .NET 8 app
├── tests/OrderService.Tests/ # xUnit tests for the sample app
├── scenarios/ # canonical demo scenarios (22 total)
│ ├── 01-safe-typo-fix/ # tier 1 - control + 5 headline rules
│ ├── 02-silent-catch/
│ ├── 03-hardcoded-secret/
│ ├── 04-breaking-api-change/
│ ├── 05-pii-logging/
│ ├── 06-concurrency-race/
│ ├── 07-magic-connection-string/ # tier 2 - one rule per scenario
│ ├── 08-undisposed-httpclient/
│ ├── 09-insecure-random-token/
│ ├── 10-sql-column-truncation/
│ ├── 11-float-money-equality/
│ ├── 12-missing-null-guard/
│ ├── 13-throw-bare-exception/
│ ├── 14-todo-in-payment-flow/
│ ├── 15-non-idempotent-retry/
│ ├── 16-tolist-in-loop/
│ ├── 17-captive-dependency/
│ ├── 18-dependabot-api-drift/
│ ├── 19-access-control-drop/ # tier 3 - behavioral regressions
│ ├── 20-audit-log-inversion/
│ ├── 21-static-mutation-async/
│ └── 22-breaking-api-contract/
├── .github/workflows/
│ ├── gauntlet.yml # PR check that runs GauntletCI
│ ├── reopen-scenarios.yml # rebuilds scenario branches on demand
│ ├── codeql.yml # CodeQL security analysis
│ ├── semgrep.yml # Semgrep pattern scanning
│ ├── stylecop.yml # StyleCop enforcement
│ ├── snyk.yml # Snyk dependency scanning
│ └── gauntletci.yml # GauntletCI behavioral analysis
├── scripts/reopen-scenarios.sh # logic for the rebuild workflow
├── DEMO_FINDINGS.md # multi-tool findings comparison
├── COMPETITOR_COMPARISON.md # detailed tool analysis
├── HYBRID_DEMO_IMPLEMENTATION.md # CI/CD pipeline documentation
├── .gauntletci.json # GauntletCI rule configuration
├── .gauntletci-ignore # path-scoped rule suppressions
└── OrderService.sln
```
## 了解更多
- 🌐 **网站:** https://gauntletci.com
- 📦 **源代码:** https://github.com/EricCogen/GauntletCI
- 📚 **文档:** https://gauntletci.com/docs
- 💬 **问题/疑问:** https://github.com/EricCogen/GauntletCI/issues
## 许可证
MIT - 详见 [LICENSE](LICENSE)。
标签:GauntletCI, GitHub Actions, .NET 8, NuGet, PR分析, SAST, SOC Prime, 云安全监控, 代码分析, 代码安全, 内联注释, 凭证管理, 安全测试, 开发工具, 开源框架, 持续集成, 攻击性安全, 漏洞枚举, 演示, 盲注攻击, 自动笔记, 行为风险, 静态分析, 风险检测, 风险裁决