1. 准备工作
欢迎参加使用 CodeMender 保护 CI/CD 流水线实操 Codelab。
在现代软件交付中,静态分析工具(例如 CodeQL)对于捕获已知语法模式至关重要,但它们往往会漏掉隐藏在业务逻辑深处的细微漏洞,并生成干扰性强、未经证实的警报。
CodeMender(CLI 可执行文件:cm)可与现有 SAST 工具协同运行,通过部署 Google 的 Gemini 赋能的安全推理智能体来增强 CI/CD 流水线,从而:
发现复杂的业务逻辑缺陷:超越僵化的静态模式规则,推理应用上下文和架构数据流。
通过自我利用验证大幅减少假正例:在提醒开发者之前,先在沙盒环境中动态证明可利用性。
自主修复已验证的发现结果:合成代码补丁,根据测试套件进行验证以防止回归,并提交可供审核的拉取请求。
在此 Codelab 中,您将使用 GitHub Actions 和无密钥 Google Cloud 工作负载身份联合 (WIF) 将 CodeMender 集成到 OWASP Juice Shop 代码库中。
前提条件
- 建议:基本熟悉 CodeMender 概念(在使用 CodeMender 保护应用安全中介绍)
- 基本了解 GitHub Actions 工作流、触发器和 YAML 语法
- 基本熟悉 Git 命令和拉取请求审核工作流
- 基本了解 Google Cloud IAM 概念(服务账号、角色和 API)
- 熟悉常见的 Web 应用漏洞(例如 SQL 注入和 XSS)
学习内容
- 如何通过工作负载身份联合 (WIF) 在 GitHub Actions 和 Google Cloud 之间配置零信任无密钥身份验证
- 如何使用 CodeMender 和
gemini-3.8-flash构建自动化漏洞扫描工作流 - 如何使用 OASIS SARIF v2.1.0 和原生 GitHub Actions 作业摘要发布可执行的安全报告
- 如何构建自主修复工作流,以生成经过测试验证的代码补丁并提交拉取请求供人工审核
- 如何强制执行自动化质量硬性限制 (
--fail-on) 和流水线缓存,以实现高速持续安全
所需条件
- 启用了结算功能且拥有 Project Owner/Editor 权限的 Google Cloud 项目
- 一个 GitHub 账号,具有创建代码库、配置 Secret 和管理 Action 的权限(您将在第 3 步中使用 GitHub CLI 创建 OWASP Juice Shop GitHub 代码库的分支)。
2. 了解 CodeMender CI/CD 架构
在现代企业 DevSecOps 中,AI 赋能的安全防护不能仅仅是手动桌面工具。它必须充当自动化守门人(阻止不安全的拉取请求)和自主共同开发者(直接向 Git 提出经过验证的补丁)。
为此,您将实现由 Google Cloud Workload Identity Federation (WIF) 和 Gemini Enterprise 推理模型提供支持的双工作负载身份联合架构。
双工作流架构

该流水线并未将检测和修复功能塞进一个庞大的脚本中,而是将安全防护分为两个解耦的专用工作流:
工作流 | 主要触发器 | 核心责任 | 质量硬性限制 |
1. 漏洞扫描( | 拉取请求、主分支推送、每周 Cron、手动分派 | • 针对 PR 运行快速差异化 AST 扫描(约 15 秒),针对推送运行快速基准扫描 ( | 如果检测到新引入的高危或严重漏洞,则阻止 PR 合并 ( |
2. Autonomous Remediator( | 手动触发器 ( | • 从扫描工件 ( | 在提交 PR 之前确保生产环境差异清晰明了 |
三大核心企业设计原则
在后续部分中构建这些工作流时,请牢记以下三大基础架构支柱:
🔑 零信任无密钥身份验证和预检验证:
GitHub Secret 中不会存储任何长期有效的 Google Cloud 服务账号 JSON 密钥。运行器会生成一个短期有效的 OIDC 令牌,该令牌通过 Google Cloud STS 交换为有效期为 1 小时的临时凭证,在 Git 检出时设置 persist-credentials: false,并运行 cm health --strict 以验证凭证、网络可达性和 IAM 权限,然后再使用模型使用模式。
⚡ 自适应扫描路由和旧版噪音抑制:
CodeMender 不会在每个拉取请求上运行缓慢的完整代码库审核,而是将每个 CI 事件路由到最佳扫描模式:拉取请求会在 15 秒内触发对修改后的文件和 1 跳调用方的差异 AST 分析 (--diff="origin/$BASE_REF");主分支推送会运行单会话快速基准扫描 (--quick);每周计划会运行并发逐文件深度审核(默认 cm find .)。拉取请求未触及的预先存在的代码库漏洞会被标记为 [LEGACY / UNTOUCHED];它们会显示在 SARIF 报告中以供查看,但不会阻止拉取请求合并,也不会触发自动修复。
🛡️ 弹性非阻塞安全可观测性:
即使 --fail-on 以代码 1 退出以阻止不安全的 PR,使用 steps.scan.outputs.completed == 'true' 和 hashFiles('results.sarif') != '' 保护报告导出和 SARIF 上传步骤,也能确保 OASIS SARIF 提醒和作业摘要仍然发布,同时在运行被取消或因设置或容量错误(10–60)而中止时,干净利落地跳过导出和上传。
3. 在 Cloud Shell 中进行身份验证并准备目标代码库
如需建立无密钥身份验证并运行自动化安全流水线,您需要拥有一个可访问的目标 GitHub 代码库。在此 Codelab 中,您将使用 OWASP Juice Shop(一个故意留有漏洞的 Node.js Web 应用)作为开箱即用的安全演练场。
您将使用 Google Cloud Shell 中预安装的 GitHub CLI (gh) 向 GitHub 进行身份验证、自动捕获用户详细信息,并通过一个简化的流程准备代码库。
第 1 步:打开 Cloud Shell 并对 GitHub CLI 进行身份验证
打开 Google Cloud 控制台并激活 Cloud Shell(顶部导航栏中的图标 >_)。
对 GitHub CLI (gh) 进行身份验证,以配置 Git 凭证管理:
gh auth login --hostname github.com --git-protocol https --web
按照屏幕上的说明操作:
复制 Cloud Shell 中输出的 8 字符一次性代码(例如 1234-ABCD)。
按 Enter 键即可在浏览器中打开 https://github.com/login/device。
粘贴代码,然后点击 Authorize github(授权 GitHub)。
第 2 步:动态导出变量并创建 OWASP Juice Shop 的分支
在 Cloud Shell 中运行以下命令,以自动捕获您的 GitHub 用户名并准备代码库:
# 1. Automatically capture your GitHub username (customize GITHUB_REPO if desired)
export GITHUB_ORG=$(gh api user -q .login)
export GITHUB_REPO="${GITHUB_REPO:-juice-shop}"
export GITHUB_REPOSITORY="${GITHUB_ORG:+${GITHUB_ORG}/${GITHUB_REPO}}"
echo "Authenticated as: ${GITHUB_REPOSITORY}"
# 2. Fork on GitHub server using GITHUB_REPO as the fork name
gh repo fork juice-shop/juice-shop --fork-name "${GITHUB_REPO}" --default-branch-only --clone=false 2>/dev/null || true
# 3. Shallow clone your fork (downloads only ~15 MB in 3-5 seconds, skipping heavy upstream git history)
[ -d ~/"${GITHUB_REPO}" ] || git clone --depth=1 "https://github.com/${GITHUB_REPOSITORY}.git" ~/"${GITHUB_REPO}" || \
(sleep 2 && git clone --depth=1 "https://github.com/${GITHUB_REPOSITORY}.git" ~/"${GITHUB_REPO}")
cd ~/"${GITHUB_REPO}"
# 4. Confirm you are in the repository directory
git status
4. 通过 gcloud 配置 Google Cloud 工作负载身份联合
在此步骤中,您将在 Google Cloud Shell 中使用 gcloud CLI 启用所需的 API、创建专用的 CI/CD 服务账号、设置工作负载身份池,并与您的 GitHub 代码库 (${GITHUB_REPOSITORY}) 建立 OIDC 信任。
第 1 步:设置 Google Cloud 环境变量
在 Cloud Shell 终端(克隆的代码库工作区内)中,捕获有效的 Google Cloud 项目 ID,验证 GitHub 代码库变量,并定义 WIF 资源名称:
# Capture active Google Cloud Project ID and verify GitHub repository
export PROJECT_ID=$(gcloud config get-value project)
export GITHUB_USER=$(gh api user -q ".login")
export GITHUB_REPO="${GITHUB_REPO:-$(git remote get-url origin 2>/dev/null | sed -E 's#^.*/##; s#\.git$##')}"
export GITHUB_REPO="${GITHUB_REPO:-juice-shop}"
export GITHUB_REPOSITORY="${GITHUB_USER:+${GITHUB_USER}/${GITHUB_REPO}}"
# Define WIF and Service Account resource names
export WORKLOAD_IDENTITY_POOL="codemender-github-pool"
export WORKLOAD_IDENTITY_PROVIDER="codemender-github-provider"
export SERVICE_ACCOUNT_NAME="codemender-ci-runner"
if [ -z "${PROJECT_ID}" ] || [ -z "${GITHUB_REPOSITORY}" ]; then
echo "ERROR: PROJECT_ID ('${PROJECT_ID}') or GITHUB_REPOSITORY ('${GITHUB_REPOSITORY}') is empty. Check 'gcloud config get-value project' and 'gh auth status'."
else
echo "Project ID: ${PROJECT_ID}"
echo "GitHub Repository: ${GITHUB_REPOSITORY}"
fi
第 2 步:启用所需的 Google Cloud API
启用 IAM API、Security Token Service API、IAM Credentials API、Agent Platform API 和 Resource Manager API:
gcloud services enable \
iam.googleapis.com \
iamcredentials.googleapis.com \
sts.googleapis.com \
aiplatform.googleapis.com \
cloudresourcemanager.googleapis.com \
--project=${PROJECT_ID}
第 3 步:创建专用 CI/CD 服务账号
创建一个专用于 CI/CD 流水线执行的最小权限 Google Cloud 服务账号(如果已存在,则安全地重复使用):
export SERVICE_ACCOUNT_EMAIL="${SERVICE_ACCOUNT_NAME}@${PROJECT_ID}.iam.gserviceaccount.com"
gcloud iam service-accounts describe "${SERVICE_ACCOUNT_EMAIL}" \
--project="${PROJECT_ID}" >/dev/null 2>&1 || \
gcloud iam service-accounts create "${SERVICE_ACCOUNT_NAME}" \
--display-name="CodeMender CI/CD Pipeline Runner" \
--project="${PROJECT_ID}"
echo "Service Account Email: ${SERVICE_ACCOUNT_EMAIL}"
第 4 步:授予 Agent Platform IAM 权限
向服务账号授予 roles/aiplatform.user (Agent Platform User) 角色,以便其能够与 Gemini Enterprise Agent Platform 通信:
gcloud projects add-iam-policy-binding ${PROJECT_ID} \
--member="serviceAccount:${SERVICE_ACCOUNT_EMAIL}" \
--role="roles/aiplatform.user"
第 5 步:创建工作负载身份池
创建工作负载身份池,以管理 GitHub Actions 身份断言(如果该池已存在于您的项目中,则会自动恢复并启用它):
POOL_STATE=$(gcloud iam workload-identity-pools list \
--location="global" \
--show-deleted \
--project="${PROJECT_ID}" \
--filter="name ~ /${WORKLOAD_IDENTITY_POOL}$" \
--format="value(state)")
if [ -z "${POOL_STATE}" ]; then
gcloud iam workload-identity-pools create "${WORKLOAD_IDENTITY_POOL}" \
--location="global" \
--display-name="CodeMender GitHub Pool" \
--project="${PROJECT_ID}"
else
[ "${POOL_STATE}" = "DELETED" ] && gcloud iam workload-identity-pools undelete "${WORKLOAD_IDENTITY_POOL}" \
--location="global" \
--project="${PROJECT_ID}"
gcloud iam workload-identity-pools update "${WORKLOAD_IDENTITY_POOL}" \
--location="global" \
--no-disabled \
--project="${PROJECT_ID}"
fi
第 6 步:使用 GitHub OIDC 创建工作负载身份提供方
在池中创建(或更新)OIDC 提供方,绑定颁发者网址 https://token.actions.githubusercontent.com,并将 GitHub 令牌断言映射到 Google STS 属性:
PROVIDER_STATE=$(gcloud iam workload-identity-pools providers list \
--workload-identity-pool="${WORKLOAD_IDENTITY_POOL}" \
--location="global" \
--show-deleted \
--project="${PROJECT_ID}" \
--filter="name ~ /${WORKLOAD_IDENTITY_PROVIDER}$" \
--format="value(state)")
if [ -z "${GITHUB_REPOSITORY}" ]; then
echo "ERROR: GITHUB_REPOSITORY is empty. Re-run Step 1 before configuring the OIDC provider."
elif [ -z "${PROVIDER_STATE}" ]; then
gcloud iam workload-identity-pools providers create-oidc "${WORKLOAD_IDENTITY_PROVIDER}" \
--workload-identity-pool="${WORKLOAD_IDENTITY_POOL}" \
--location="global" \
--display-name="CodeMender GitHub Provider" \
--issuer-uri="https://token.actions.githubusercontent.com" \
--attribute-mapping="google.subject=assertion.sub,attribute.actor=assertion.actor,attribute.repository=assertion.repository,attribute.repository_owner=assertion.repository_owner" \
--attribute-condition="assertion.repository == '${GITHUB_REPOSITORY}'" \
--project="${PROJECT_ID}"
else
[ "${PROVIDER_STATE}" = "DELETED" ] && gcloud iam workload-identity-pools providers undelete "${WORKLOAD_IDENTITY_PROVIDER}" \
--workload-identity-pool="${WORKLOAD_IDENTITY_POOL}" \
--location="global" \
--project="${PROJECT_ID}"
gcloud iam workload-identity-pools providers update-oidc "${WORKLOAD_IDENTITY_PROVIDER}" \
--workload-identity-pool="${WORKLOAD_IDENTITY_POOL}" \
--location="global" \
--issuer-uri="https://token.actions.githubusercontent.com" \
--attribute-mapping="google.subject=assertion.sub,attribute.actor=assertion.actor,attribute.repository=assertion.repository,attribute.repository_owner=assertion.repository_owner" \
--attribute-condition="assertion.repository == '${GITHUB_REPOSITORY}'" \
--no-disabled \
--project="${PROJECT_ID}"
fi
第 7 步:允许 GitHub 代码库模拟服务账号
绑定 roles/iam.workloadIdentityUser 角色,以允许源自代码库的令牌模拟 CI 运行器服务账号,并验证政策绑定:
# Retrieve the canonical Workload Identity Pool resource ID
export POOL_ID=$(gcloud iam workload-identity-pools describe "${WORKLOAD_IDENTITY_POOL}" \
--project="${PROJECT_ID}" \
--location="global" \
--format="value(name)")
if [ -z "${POOL_ID}" ] || [ -z "${GITHUB_REPOSITORY}" ]; then
echo "ERROR: POOL_ID ('${POOL_ID}') or GITHUB_REPOSITORY ('${GITHUB_REPOSITORY}') is empty. Re-run Step 1 and Step 5."
else
# Bind Workload Identity User role to the principalSet of your repository
gcloud iam service-accounts add-iam-policy-binding "${SERVICE_ACCOUNT_EMAIL}" \
--project="${PROJECT_ID}" \
--role="roles/iam.workloadIdentityUser" \
--member="principalSet://iam.googleapis.com/${POOL_ID}/attribute.repository/${GITHUB_REPOSITORY}"
# Verify the principalSet binding on the Service Account
gcloud iam service-accounts get-iam-policy "${SERVICE_ACCOUNT_EMAIL}" \
--project="${PROJECT_ID}"
fi
第 8 步:检索 GitHub 密钥的配置值
检索工作负载身份提供方的完整资源名称,并将其存储在环境变量中,以便在下一步中进行自动注册:
# Retrieve full Workload Identity Provider name
export WORKLOAD_PROVIDER_NAME=$(gcloud iam workload-identity-pools providers describe ${WORKLOAD_IDENTITY_PROVIDER} \
--workload-identity-pool=${WORKLOAD_IDENTITY_POOL} \
--location="global" \
--project=${PROJECT_ID} \
--format="value(name)")
echo "=========================================================="
echo "WIF CONFIGURATION READY:"
echo "=========================================================="
echo "GCP_WORKLOAD_IDENTITY_PROVIDER: ${WORKLOAD_PROVIDER_NAME}"
echo "GCP_SERVICE_ACCOUNT: ${SERVICE_ACCOUNT_EMAIL}"
echo "=========================================================="
5. 配置 GitHub 代码库 Secret 和工作流权限
现在,Google Cloud WIF 已预配完毕,您将配置 GitHub 代码库密钥,并授予必要的自动化工作流权限,以便进行自动安全扫描和自动修复拉取请求。
您可以使用 GitHub CLI (gh) 直接从 Cloud Shell 配置这些设置,从而完全避免手动复制和粘贴长资源名称。
第 1 步:使用 GitHub CLI 设置代码库密钥
在 Cloud Shell 终端中,运行以下命令,使用活跃环境变量安全地设置两个必需的代码库 Secret:
# 1. Set the Workload Identity Provider resource name
gh secret set GCP_WORKLOAD_IDENTITY_PROVIDER \
--body "${WORKLOAD_PROVIDER_NAME}" \
--repo "${GITHUB_REPOSITORY}"
# 2. Set the CI/CD Service Account email
gh secret set GCP_SERVICE_ACCOUNT \
--body "${SERVICE_ACCOUNT_EMAIL}" \
--repo "${GITHUB_REPOSITORY}"
# 3. Verify the secrets are registered
gh secret list --repo "${GITHUB_REPOSITORY}"
第 2 步:为自动 PR 授予工作流权限
默认情况下,GitHub Actions 工作流以只读权限运行,无法创建拉取请求。在第 9 步中,CodeMender 将自主生成并提交经过验证的修复拉取请求。
授予写入权限,并直接通过 GitHub API 启用拉取请求创建功能:
[ -d .git ] || cd ~/"${GITHUB_REPO:-juice-shop}"
export GITHUB_REPOSITORY="${GITHUB_REPOSITORY:-$(git remote get-url origin 2>/dev/null | sed -E 's#^.*github\.com[:/]##; s#\.git$##')}"
gh api \
--method PUT \
-H "Accept: application/vnd.github+json" \
/repos/${GITHUB_REPOSITORY}/actions/permissions/workflow \
-f default_workflow_permissions="write" \
-F can_approve_pull_request_reviews=true
第 3 步:停用继承的上游 Juice Shop 工作流
复刻 OWASP Juice Shop 会带入 16 个上游 CI 工作流(例如 PR Compliance Check、CI/CD Pipeline 和 CodeQL Scan),以及一个上游贡献者 PR 模板,该模板会自动关闭针对 master 的 PR。通过 GitHub CLI 停用所有非 CodeMender 工作流,并在本地移除上游文件,以便您的派生项目严格运行 CodeMender 工作流:
[ -d .git ] || cd ~/"${GITHUB_REPO:-juice-shop}"
export GITHUB_REPOSITORY="${GITHUB_REPOSITORY:-$(git remote get-url origin 2>/dev/null | sed -E 's#^.*github\.com[:/]##; s#\.git$##')}"
# 1. Disable all inherited non-CodeMender workflows in GitHub Actions (and CodeQL default setup if active)
for WF_ID in $(gh workflow list --all --limit 100 --repo "${GITHUB_REPOSITORY}" --json id,path \
-q '.[] | select((.path | startswith(".github/workflows/")) and (.path | test("codemender-") | not)) | .id'); do
gh workflow disable "${WF_ID}" --repo "${GITHUB_REPOSITORY}" 2>/dev/null || true
done
gh api -X PATCH "/repos/${GITHUB_REPOSITORY}/code-scanning/default-setup" \
-f state=not-configured >/dev/null 2>&1 || true
# 2. Remove upstream workflow files and PR template from the local workspace
for f in .github/workflows/*; do
case "$f" in *codemender-*) ;; *) rm -f "$f" ;; esac
done
rm -f .github/PULL_REQUEST_TEMPLATE.md
6. 使用 SARIF 构建自动漏洞扫描工作流
在此步骤中,您将编写主要持续安全扫描工作流 (.github/workflows/codemender-scan.yml),该工作流会在分支推送时运行基准扫描,在拉取请求时运行差异门控,并将 SARIF 导出到 GitHub 代码扫描。
第 1 步:下载 CodeMender 扫描工作流
将 codemender-scan.yml 从起始代码库下载到 .github/workflows/:
[ -d .git ] || cd ~/"${GITHUB_REPO:-juice-shop}"
mkdir -p .github/workflows
curl -fsSL https://raw.githubusercontent.com/edwardc-gcp/codemender-github-actions/main/.github/workflows/codemender-scan.yml \
-o .github/workflows/codemender-scan.yml
第 2 步:提交、推送并触发 CI 扫描
提交工作流更改,并直接推送到代码库的主分支(Juice Shop 为 master,或 main):
# Stage workflow changes, commit, and push to primary branch
git add -A .github
git commit -m "ci: add CodeMender SARIF security scanning workflow"
git push origin $(git branch --show-current)
在浏览器中:
前往您复刻的 GitHub 代码库:https://github.com/YOUR_GITHUB_USER/juice-shop(例如 https://github.com/octocat/juice-shop)。
点击顶部导航栏中的操作标签页。
在左侧边栏的 Workflows(工作流)下,选择 CodeMender Security Scan & SARIF Export(CodeMender 安全扫描和 SARIF 导出)。
触发或查看工作流执行:
自动触发:如果列出了由推送触发的自动运行,请直接点击该运行以监控实时执行情况。
手动按需触发器 (workflow_dispatch):如果尚未开始任何运行,请点击右侧的 Run workflow ▾(运行工作流 ▾)按钮,确保已选择 Branch: master(不勾选 deep_scan 复选框,以便运行快速 --quick 基准扫描),然后点击绿色的 Run workflow(运行工作流)按钮。
点击进入正在执行的运行作业,然后选择 Autonomous Code Vulnerability Scan,实时查看工作流的启动过程。
codemender-scan.yml 内部:事件感知型扫描路由
在云端运行 --quick 基准扫描(约 3-5 分钟)期间,请检查 codemender-scan.yml(第 118-155 行)如何将每个 GitHub 事件路由到运行器内的专用扫描模式:
# Excerpt from .github/workflows/codemender-scan.yml
if [ "${{ github.event_name }}" = "pull_request" ]; then
cm find . \
--diff="origin/${{ github.base_ref }}" \
--diff-workers=16 \
--fail-on="CRITICAL,HIGH" \
--sandbox=false --yes --bypass-warning || status=$?
elif [ "${{ github.event_name }}" = "schedule" ] || [ "${{ github.event.inputs.deep_scan }}" = "true" ]; then
cm find . \
--deep-workers=8 \
--sandbox=false --yes --bypass-warning || status=$?
else
cm find . \
--quick \
--sandbox=false --yes --bypass-warning || status=$?
fi
Hardened Runner Setup(第 27-117 行):跳过不受信任的外部复刻和 dependabot[bot] PR,使用 persist-credentials: false 检出,缓存 ~/.local/bin/cm 和 ~/.codemender/state.db,并在消耗使用模式配额之前运行 cm health --strict。
无头沙盒边界 (--sandbox=false --yes --bypass-warning):停用 Linux 用户命名空间隔离(在无特权的 GitHub Actions 容器内受限),同时强制执行工作区路径边界 (project_paths) 和 command_policy.yaml。
退出代码保护的 SARIF 和工件导出(第 147-194 行):仅当 cm find 以 0(干净)或 1(达到 --fail-on 阈值)退出时,才会设置 steps.scan.outputs.completed=true。这可确保在 --fail-on 阻止 PR 时,SARIF 报告(cm report -f sarif,包含 CVSS 严重程度评分)、$GITHUB_STEP_SUMMARY 表 (cm report -f md) 和 codemender-findings.json 工件仍会发布,同时在出现设置或容量错误 (10–60) 时跳过导出,从而避免空 SARIF 文件关闭现有提醒。
7. 检查 GitHub 代码扫描提醒和安全信息中心
基准推送工作流完成后,GitHub 会处理上传的 SARIF 文件,并填充 Actions 运行摘要和代码库“Security”(安全)标签页。
第 1 步:在 GitHub Actions 上检查原生作业摘要
每次工作流运行都会直接在 GitHub Actions 运行概览中附加一个 Markdown 摘要表 (cm report -f md):
在 GitHub 代码库中,点击操作标签页。
选择已完成的 CodeMender Security Scan & SARIF Export 运行(基准分支推送审核在发布 SARIF 发现结果后会显示绿色 ✅ 勾号)。
向下滚动到 🛡️ CodeMender Security Scan Summary 部分,查看发现结果明细:
🔒 CodeMender 安全报告
摘要
发现结果总数:28
严重程度 | 计数 |
🔴 严重 | 6 |
🟠 高 | 16 |
🟡 中 | 6 |
状态 | 计数 |
🔴 未解决 | 28 |
发现结果
ID | 严重程度 | Confidence | 状态 | 修复 | 文件 | 标题 |
| 🔴 严重 | 100% | 未解决 | — |
| 通过在用户个人资料中注入服务器端代码来远程执行代码 |
| 🔴 严重 | 100% | 未解决 | — |
| 用户身份验证路由中的 SQL 注入 |
| 🔴 严重 | 100% | 未解决 | — |
| B2B 订单处理中通过沙盒逃逸实现远程代码执行 |
第 2 步:探索集中式代码扫描信息中心
在 GitHub 代码库的顶部导航栏中,点击安全性和质量(或安全性)标签页。
在左侧边栏的 Findings(发现结果)下,选择 Code scanning(代码扫描)。
如果多个工具处于活跃状态,请将工具过滤为 CodeMender (is:open branch:master tool:CodeMender),以检查与原生 CVSS 严重级别标记映射的提醒:
严重程度徽章 | 漏洞标题 | 检测到的位置 | 分支 |
🔴 严重 | 通过在用户个人资料中注入服务器端代码来远程执行代码 |
|
|
🔴 严重 | 用户身份验证路由中的 SQL 注入 |
|
|
🔴 严重 | B2B 订单处理中通过沙盒逃逸实现远程代码执行 |
|
|
点击 SQL Injection in User Authentication Route(用户身份验证路由中的 SQL 注入)(routes/login.ts:34),查看 Gemini 合成的受污染数据流和根本原因分析。
8. 使用差异扫描功能阻止不安全的拉取请求
基准扫描在 OWASP Juice Shop 中发现了 28 个既有漏洞。如果使用传统的静态分析器,这 28 个旧发现结果会导致未来的每个拉取请求都失败,直到整个积压问题得到修复,这会迫使团队停用合并屏蔽功能。
现在,您将测试 CodeMender 的 PR 差异 AST 门控(--diff + --fail-on="CRITICAL,HIGH")的实际效果:打开一个引入新漏洞的拉取请求,观察 CodeMender 如何隔离增量、在约 15-25 秒内阻止合并,并对确切的违规代码行进行注释。
第 1 步:打开一个引入命令注入漏洞的拉取请求
在 Cloud Shell(克隆的代码库工作区内)中,创建一个功能分支,添加包含操作系统命令注入漏洞的新 Express 路由,然后针对您的分支代码库发起拉取请求:
[ -d .git ] || cd ~/"${GITHUB_REPO:-juice-shop}"
export GITHUB_REPOSITORY="${GITHUB_REPOSITORY:-$(git remote get-url origin 2>/dev/null | sed -E 's#^.*github\.com[:/]##; s#\.git$##')}"
PRIMARY_BRANCH=$(git symbolic-ref --short refs/remotes/origin/HEAD | sed 's@^origin/@@')
# Reset any previous demo PR/branch so this step can be re-run cleanly
gh pr close feat/diagnostics --repo "${GITHUB_REPOSITORY}" --delete-branch 2>/dev/null || true
git checkout "${PRIMARY_BRANCH}"
git checkout -B feat/diagnostics
# Introduce an Express endpoint vulnerable to OS Command Injection (CWE-78)
cat << 'EOF' > routes/diagnostics.ts
import { execSync } from 'node:child_process'
import { type Request, type Response } from 'express'
export function systemDiagnostics () {
return (req: Request, res: Response) => {
const host = String(req.query.host ?? '127.0.0.1')
const output = execSync(`ping -c 1 ${host}`).toString()
res.status(200).json({ status: 'ok', output })
}
}
EOF
git add routes/diagnostics.ts
git commit -m "feat: add network diagnostics endpoint"
git push -f -u origin feat/diagnostics
gh pr create \
--repo "${GITHUB_REPOSITORY}" \
--base "${PRIMARY_BRANCH}" \
--head feat/diagnostics \
--title "feat: add network diagnostics endpoint" \
--body "Adds a ping diagnostics handler."
第 2 步:验证差异化合并门控和内嵌 PR 注解
打开终端输出末尾显示的拉取请求网址。
滚动到拉取请求会话页面底部的检查复选框,然后查看自主代码漏洞扫描:
增量速度:cm find . --diff="origin/${PRIMARY_BRANCH}" 不会扫描代码库中的所有 680 多个文件(约 3-5 分钟),而是将分析范围严格限定在 routes/diagnostics.ts(以及任何 1 跳调用方),在约 15-25 秒内完成扫描。
旧有技术债务隔离和合并阻止:Juice Shop 的 28 个未改动的旧有漏洞均未导致检查失败,只有新引入的 CRITICAL 命令注入触发了 --fail-on="CRITICAL,HIGH",并以状态代码 1(红色 ❌)退出,从而阻止了 PR 合并!
点击拉取请求顶部的 Files changed(已更改的文件)标签页:
由于工作流的退出代码保护机制 (steps.scan.outputs.completed == 'true') 在 --fail-on 以代码 1 退出时仍会导出并上传 results.sarif,因此 GitHub 代码扫描会在 routes/diagnostics.ts 中的 execSync 下方直接呈现内嵌的 CodeMender — Critical 安全注释!
第 3 步:清理测试 PR 并返回到主分支
将 Cloud Shell 工作区切换回主分支,并关闭不安全的测试拉取请求,然后再继续进行自主修复:
[ -d .git ] || cd ~/"${GITHUB_REPO:-juice-shop}"
export GITHUB_REPOSITORY="${GITHUB_REPOSITORY:-$(git remote get-url origin 2>/dev/null | sed -E 's#^.*github\.com[:/]##; s#\.git$##')}"
PRIMARY_BRANCH=$(git symbolic-ref --short refs/remotes/origin/HEAD | sed 's@^origin/@@')
git checkout "${PRIMARY_BRANCH}"
gh pr close feat/diagnostics --repo "${GITHUB_REPOSITORY}" --delete-branch
git branch -D feat/diagnostics 2>/dev/null || true
9. 构建自主修复和自动 PR 工作流
现在,拉取请求关卡可防止新的漏洞进入代码库,您将部署自主修复 (.github/workflows/codemender-remediate.yml) 来主动修复现有漏洞并打开可供审核的拉取请求。
第 1 步:下载修复工作流文件
CodeMender 提供两种修复模板,具体取决于代码库的公开范围:
公共/开源代码库 (codemender-remediate.yml):负责任披露模式 - 链接到 GitHub 代码扫描 SARIF 提醒,而不会公开披露原始漏洞利用向量。
私有/企业代码库 (codemender-remediate-private-repo.yml):内部审核证据模式 - 将验证工件(PLAN.md、REPORT.md、exploit.sh)嵌入可收起的 PR 部分,同时将其从 Git 提交中排除。
将公开安全修复工作流下载到您的代码库中:
mkdir -p .github/workflows
curl -fsSL https://raw.githubusercontent.com/edwardc-gcp/codemender-github-actions/main/.github/workflows/codemender-remediate.yml \
-o .github/workflows/codemender-remediate.yml
第 2 步:提交并触发自主修复工作流
提交修复工作流并将其推送到主分支:
git add .github/workflows/codemender-remediate.yml
git commit -m "ci: add CodeMender autonomous remediation workflow"
git push origin $(git branch --show-current)
在浏览器中,前往 GitHub 代码库的 Actions(操作)标签页。
在左侧边栏的工作流下,选择 CodeMender Autonomous Security Remediation & Auto-PR。
点击右侧绿色的 Run workflow(运行工作流)下拉菜单:
Use workflow from(使用以下分支的工作流):确保已选择默认分支 (master)。
Specific Finding ID or prefix to fix(要修复的具体发现结果 ID 或前缀):输入在第 7 步中记下的 routes/login.ts 的 8 字符前缀(例如 cd47b38a)。如果将此字段留空,系统会对所有未解决的“严重”和“高”发现结果运行批量修复(约 10-15 分钟);如果针对单个 ID,则会在约 45-60 秒内完成。
点击弹出式窗口中的绿色运行工作流按钮,然后打开正在执行的运行。

codemender-remediate.yml 内部:自主补丁合成
在 CodeMender 于云端合成补丁期间(约 30-45 秒),检查 codemender-remediate.yml(第 111-184 行)如何在运行器中编排修复流程:
零扫描恢复 (cm report import):从扫描工作流下载 codemender-findings.json 工件并将其导入工作区 (cm report import -f codemender-findings.json),仅在没有先前工件时才回退到 cm find . --quick。
无头补丁合成 (cm fix):将 project_paths 固定到 $GITHUB_WORKSPACE,通过管道传输 < /dev/null 以防止交互式挂起,并在批处理循环期间传递 --no-reset,以便在发现结果之间保留累积修复:
# Executed inside the GitHub Actions runner (codemender-remediate.yml)
cm fix "${SPECIFIC_TARGET}" --sandbox=false --yes --bypass-warning < /dev/null
清理生产提交(第 155-252 行):在 peter-evans/create-pull-request@v8 打开分支 (codemender/auto-remediation-${{ github.run_id }}) 之前,通过 .git/info/exclude 清除 .cm_project、.exploit/ 和临时文件,严格提交生产代码更改:
--- a/routes/login.ts
+++ b/routes/login.ts
@@ -28,8 +28,8 @@ module.exports = function login () {
- models.sequelize.query(`SELECT * FROM Users WHERE email = '${req.body.email || ''}' AND password = '${security.hash(req.body.password || '')}' AND deletedAt IS NULL`, { model: models.User })
+ models.sequelize.query('SELECT * FROM Users WHERE email = :email AND password = :password AND deletedAt IS NULL', {
+ replacements: { email: req.body.email || '', password: security.hash(req.body.password || '') },
+ model: models.User
+ })
第 3 步:在 GitHub 上查看新打开的拉取请求
前往代码库中的 Pull requests(拉取请求)标签页,然后打开新的拉取请求(例如 🛡️ [CodeMender] Fix: SQL Injection in User Authentication Route)。
点击 Files changed(文件已更改),确认仅修改了 routes/login.ts,并排除了所有内部运行时元数据。
查看直接链接到 GitHub 代码扫描 (SARIF) 提醒的 PR 说明,以便维护者签核。
10. 排查极端情况和私有代码库问题
未启用 GitHub Advanced Security (GHAS) 的私有代码库
公共 GitHub 代码库可免费、无限制地使用 GitHub 代码扫描 SARIF 提取功能。但是,如果您将此工作流部署到没有有效 GitHub Advanced Security (GHAS) 许可的私有企业代码库,则 upload-sarif 步骤将失败,并显示以下错误:
HTTP 403: Advanced Security must be enabled for this repository to use code scanning.
解决方法:回退到独立的互动式 HTML 报告
CodeMender 的内置 HTML 报告生成器 (cm report -f html) 可创建独立的交互式安全信息中心,其中包含严重程度指标卡片、带有行号的语法突出显示代码段,以及不依赖任何外部 Web 的内嵌补丁差异查看器。
如需在没有 GHAS 许可的情况下支持私有代码库,请更新 .github/workflows/codemender-scan.yml 以归档 HTML 报告工件,而不是上传 SARIF:
生成独立的 HTML 报告:在 Export Security Reports & Publish Job Summary 步骤中,使用 cm report -f html > codemender-audit-report.html 导出 HTML 信息中心以及 Markdown 摘要。
将报告归档为工作流制品:将 Upload SARIF Report to GitHub Code Scanning (github/codeql-action/upload-sarif@v4) 步骤替换为配置为上传 codemender-audit-report.html 的 actions/upload-artifact@v4(例如,在制品名称 codemender-security-report 下)。
在任何浏览器中本地检查:直接从 GitHub Actions 工作流运行摘要页面下载 codemender-security-report.zip,将其解压缩,然后在任何网络浏览器中打开 codemender-audit-report.html 进行可视化分类。
私有代码库自主修复,并嵌入审核证据
在私有企业代码库中,工程和安全团队需要全面了解根本原因分析、漏洞利用概念验证和修复计划,而无需使用外部工具信息中心。不过,将漏洞利用脚本 (exploit.sh) 或内部 Markdown 文件(PLAN.md、LOG.md)提交到代码库分支会污染 Git 历史记录,并可能触发其他安全扫描器的误报。
为解决此问题,请部署专用的私有代码库修复工作流 (codemender-remediate-private-repo.yml):
curl -fsSL https://raw.githubusercontent.com/edwardc-gcp/codemender-github-actions/main/.github/workflows/codemender-remediate-private-repo.yml \
-o .github/workflows/codemender-remediate.yml
git add .github/workflows/codemender-remediate.yml
git commit -m "ci: switch to private repository remediation workflow with embedded audit evidence"
git push origin $(git branch --show-current)
私有代码库修复的工作原理
动态 PoC 验证 (cm verify):在生成修复方案之前,合成并测试可行的漏洞利用,以确认可利用性。如果验证将不可利用的发现结果标记为DISMISSED,则会自动跳过 cm fix。
嵌入式 PR 审核可折叠块:将 REPORT.md、PLAN.md 和 exploit.sh 提取到 /tmp/codemender-evidence/ 中,并直接在 <details open> 可折叠块内将其格式化为拉取请求说明,以便审核者一键检查。
工件归档:通过 actions/upload-artifact@v4 归档完整的证据文件夹。
原始 Git 提交:清除所有运行时元数据和 .exploit 目录,以便拉取请求 Git 提交严格包含生产代码更改。
工作负载身份联合 (WIF) 问题排查核对清单
默认情况下,google-github-actions/auth@v2 会在设置期间写入本地联合凭证配置文件,而实际的 OAuth 2.0 令牌交换会在 cm health --strict 或 cm find 启动时发生。使用以下核对清单诊断 HTTP 400 或 HTTP 403 身份验证错误:
错误症状 | 常见根本原因 | 验证和修复 |
HTTP 403( | 服务账号上缺少 | 确认 |
HTTP 400( | OIDC | 重新运行第 4 步(第 1 步和第 6 步),确保 |
HTTP 400(令牌交换时出现 | OIDC 提供方上的 | 确认 |
设置后立即出现暂时性 HTTP 403 | 对于新创建的绑定,全局 IAM 和工作负载身份联合传播可能需要 1-5 分钟。 | 等待 1-2 分钟,然后在 GitHub Actions 中点击重新运行失败的作业。 |
如需在 Cloud Shell 中检查 CI/CD 服务账号上的实时 Workload Identity 绑定,请运行以下命令:
gcloud iam service-accounts get-iam-policy "${SERVICE_ACCOUNT_EMAIL}" \
--project="${PROJECT_ID}" \
--flatten="bindings[].members" \
--filter="bindings.role=roles/iam.workloadIdentityUser"
确认输出将 roles/iam.workloadIdentityUser 绑定到 principalSet://iam.googleapis.com/<POOL_ID>/attribute.repository/<GITHUB_USER>/<GITHUB_REPO>:
--- bindings: members: principalSet://iam.googleapis.com/.../attribute.repository/<OWNER>/<REPO> role: roles/iam.workloadIdentityUser
CI/CD 预检和 CLI 退出代码问题排查
CodeMender 命令(cm health --strict、cm find、cm verify 和 cm fix)会返回确定性退出代码,以便 CI/CD 流水线能够立即区分安全发现结果与设置或服务错误:
退出代码 | 类别 | 常见根本原因和解决方法 |
| 成功 | 健康检查通过或扫描完成,未发现任何阻碍性问题。 |
| 发现结果/一般 |
|
|
| WIF OAuth/ADC 令牌交换失败( |
|
|
|
|
| 运行器无法访问 |
|
| 服务账号在目标项目中缺少 |
|
| 预检 |
|
| 代理已达到其并发活跃任务限制;请稍等片刻后重新运行工作流。 |
如需了解其他运行时错误(例如 Git diff 基础分支解析 [fetch-depth: 0]、资源传播延迟或 GitHub Actions 拉取请求创建权限),请参阅官方 CodeMender CI/CD 问题排查指南。
11. 清理资源
为防止 Google Cloud 和 GitHub 环境中持续消耗资源或清理测试配置,请执行以下清理步骤。
第 1 步:通过 gh CLI 移除 GitHub 代码库 Secret 并停用工作流
使用 GitHub CLI (gh) 彻底删除代码库 Actions 密钥并停用 CodeMender 工作流:
# Delete the repository Actions secrets
gh secret delete GCP_WORKLOAD_IDENTITY_PROVIDER --repo "${GITHUB_REPOSITORY}" 2>/dev/null || true
gh secret delete GCP_SERVICE_ACCOUNT --repo "${GITHUB_REPOSITORY}" 2>/dev/null || true
# Disable the CodeMender workflows to prevent unwanted automatic triggers
gh workflow disable codemender-scan.yml --repo "${GITHUB_REPOSITORY}" 2>/dev/null || true
gh workflow disable codemender-remediate.yml --repo "${GITHUB_REPOSITORY}" 2>/dev/null || true
第 2 步:停用或删除 Google Cloud 工作负载身份联合资源
工作负载身份池、提供方和服务账号在不交换令牌时不会产生闲置费用。选择与您的工作流匹配的清理模式:
选项 A - 暂停访问权限(如果您计划在此项目中重新运行,建议使用此选项):立即停用 OIDC 令牌交换,而不会触发 Google Cloud IAM 的 30 天软删除保留期或服务账号 uniqueId 传播延迟:
gcloud iam workload-identity-pools providers update-oidc "${WORKLOAD_IDENTITY_PROVIDER}" \
--workload-identity-pool="${WORKLOAD_IDENTITY_POOL}" \
--location="global" \
--disabled \
--project="${PROJECT_ID}"
选项 B - 完全拆解:删除 WIF 提供方、池(在 DELETED 状态下保留 30 天)和专用服务账号:
# Delete Workload Identity Provider
gcloud iam workload-identity-pools providers delete "${WORKLOAD_IDENTITY_PROVIDER}" \
--workload-identity-pool="${WORKLOAD_IDENTITY_POOL}" \
--location="global" \
--project="${PROJECT_ID}" \
--quiet
# Delete Workload Identity Pool
gcloud iam workload-identity-pools delete "${WORKLOAD_IDENTITY_POOL}" \
--location="global" \
--project="${PROJECT_ID}" \
--quiet
# Delete CI/CD Service Account
gcloud iam service-accounts delete "${SERVICE_ACCOUNT_EMAIL}" \
--project="${PROJECT_ID}" \
--quiet
12. 恭喜
恭喜!您已完成使用 CodeMender 保护 CI/CD 流水线 Codelab!
您已成功使用 Google Cloud 工作负载身份联合、CodeMender 和 GitHub Actions 设计、构建并自动化了一个生产级零信任 DevSecOps 流水线。
所学内容
零信任无密钥身份验证:通过工作负载身份联合 (WIF) 将临时 GitHub OIDC 令牌绑定到 Google Cloud IAM,从而消除了存在风险的长期服务账号 JSON 密钥。
PR 差异化 AST 扫描和旧版降噪:将持续扫描范围限定为修改后的文件和依赖项 (--diff="origin/$BASE_REF"),隔离旧版技术债务 ([LEGACY / UNTOUCHED]),使开发者反馈环保持在 15 秒以内。
多格式报告和原生作业摘要:结合使用 OASIS SARIF v2.1.0(用于集中式 GitHub 代码扫描)和原生 Markdown (cm report -f md >> $GITHUB_STEP_SUMMARY)(用于在 CI 工作流运行页面上直接提供即时执行可见性)。
人机协同审核的自主修复:从被动提醒通知转变为主动修复流水线,可自主合成建议的代码补丁、根据回归测试套件进行验证,并打开包含审核工件的审核拉取请求,供工程师签核。
自动化质量硬性控制:实施严格的分支保护和合并控制 (--fail-on),防止危险漏洞进入生产环境。
制品驱动的状态转移和工作区隔离:使用 cm report import 在 CI 作业之间无缝转移发现结果,同时使用 .git/info/exclude 保留原始 Git 历史记录并防止凭证泄露。
开发者思维方式转变
传统 DevSecOps 实践 | CodeMender 自主 CI/CD 流水线 |
被动警报转储:扫描器会输出原始终端日志或静态错误列表,将分类负担完全转移给开发者。 | Active Co-developer:合成经过验证的代码补丁,并打开可供合并的拉取请求,供工程师签核。 |
假正例率高:静态 AST 启发式方法会标记数千个理论上的漏洞,但无法证明其可利用性。 | 2 层级漏洞利用验证:动态沙盒测试生成 ( |
Secret 存储风险:存储在 CI Secret 中的长期服务账号 JSON 密钥存在意外泄露和凭证蔓延的风险。 | 零信任 WIF:通过按需与 Google Cloud STS 交换的临时 OIDC 令牌进行无密钥身份验证。 |
单体式重新扫描:每次提交都会触发完整代码库审核,从而延迟 CI 工作流并浪费昂贵的 token。 | 事件感知型 AST 路由:拉取请求会执行 15 秒的差异化 AST 扫描 ( |
代码库历史记录被污染:临时漏洞利用脚本和攻击日志直接提交到代码库分支。 | 企业级工作区健全度:攻击证据会清晰地嵌入到 PR 说明和工作流工件中,从而保持代码提交的纯净度。 |
后续步骤
🚀 继续 CodeMender 学习之旅:
第 1 部分:使用 CodeMender 保护应用安全 - 掌握自主代码库漏洞发现、PoC 漏洞利用验证和本地补丁合成。
本地左移和无服务器云原生 CI/CD:探索官方 CI/CD 指南中有关 Google Cloud Build (cloudbuild.yaml) 和本地 Git 预提交钩子 (cm find . --diff --staged) 的参考实现。
📖 探索文档和资源:
Google Cloud CodeMender CI/CD 集成官方指南
CodeMender GitHub Actions Starter Template Repository
💡 应用于您自己的代码库:
复刻入门工作流,使用 CodeMender 保护组织的 Python、Go、Java、Rust 和 Node.js 代码库!