1. 시작하기 전에
CodeMender로 CI/CD 파이프라인 보호 실습형 Codelab에 오신 것을 환영합니다.
현대적인 소프트웨어 배포에서 정적 분석 도구 (예: CodeQL)는 알려진 구문 패턴을 포착하는 데 필수적이지만, 비즈니스 로직 깊숙이 숨겨진 미묘한 취약점을 놓치고 노이즈가 많고 검증되지 않은 알림을 생성하는 경우가 많습니다.
CodeMender (CLI 실행 파일: cm)는 기존 SAST 도구와 함께 작동하여 CI/CD 파이프라인을 개선합니다. Google의 Gemini 기반 보안 추론 에이전트를 배포하여 다음을 수행합니다.
복잡한 비즈니스 로직 결함 발견: 엄격한 정적 패턴 규칙을 넘어 애플리케이션 컨텍스트와 아키텍처 데이터 흐름을 추론합니다.
자체 익스플로잇 검증을 통해 거짓양성을 대폭 줄임: 개발자에게 알리기 전에 샌드박스 환경에서 익스플로잇 가능성을 동적으로 증명합니다.
검증된 발견 항목 자율 해결: 코드 패치를 합성하고, 테스트 모음을 기준으로 검증하여 회귀를 방지하고, 검토 준비가 완료된 pull 요청을 제출합니다.
이 Codelab에서는 GitHub Actions와 키 없는 Google Cloud 워크로드 아이덴티티 제휴 (WIF)를 사용하여 CodeMender를 OWASP Juice Shop 저장소에 통합합니다.
기본 요건
- 권장사항: CodeMender 개념에 대한 기본 지식(CodeMender로 애플리케이션 보호에서 다룸)
- GitHub Actions 워크플로, 트리거, YAML 문법에 관한 기본적인 이해
- Git 명령어 및 pull 요청 검토 워크플로에 대한 기본적인 지식
- Google Cloud IAM 개념 (서비스 계정, 역할, API)에 관한 기본 지식
- 일반적인 웹 애플리케이션 취약점 (예: SQL 삽입 및 XSS)에 대한 지식
학습할 내용
- 워크로드 아이덴티티 제휴 (WIF)를 통해 GitHub Actions와 Google Cloud 간에 제로 트러스트 키 없는 인증을 구성하는 방법
- CodeMender 및
gemini-3.8-flash를 사용하여 자동화된 취약점 스캔 워크플로를 빌드하는 방법 - OASIS SARIF v2.1.0 및 네이티브 GitHub Actions 작업 요약을 사용하여 실행 가능한 보안 보고서를 게시하는 방법
- 테스트로 검증된 코드 패치를 생성하고 사람이 검토할 수 있도록 pull 요청을 여는 자율적 해결 워크플로를 구성하는 방법
- 고속 연속 보안을 위해 자동화된 품질 하드 게이트 (
--fail-on) 및 파이프라인 캐싱을 적용하는 방법
필요한 항목
- 결제가 사용 설정되어 있고 프로젝트 소유자/편집자 권한이 있는 Google Cloud 프로젝트
- 저장소를 만들고, 보안 비밀을 구성하고, 작업을 관리할 수 있는 권한이 있는 GitHub 계정 (3단계에서 GitHub CLI를 사용하여 OWASP Juice Shop GitHub 저장소를 포크함)
2. CodeMender CI/CD 아키텍처 이해
현대 기업의 DevSecOps에서 AI 기반 보안은 단순히 수동 데스크톱 도구에 그쳐서는 안 됩니다. 자동화된 게이트키퍼 (안전하지 않은 pull 요청 차단) 및 자율 공동 개발자 (검증된 패치를 Git에 직접 제안) 역할을 해야 합니다.
이를 위해 Google Cloud 워크로드 아이덴티티 제휴 (WIF)와 Gemini Enterprise 추론 모델을 기반으로 하는 이중 워크플로 아키텍처를 구현합니다.
이중 워크플로 아키텍처

이 파이프라인은 감지와 해결을 하나의 거대한 스크립트에 몰아넣는 대신 보안을 두 개의 분리된 목적별 워크플로로 나눕니다.
워크플로 | 기본 트리거 | 핵심 책임 | 품질 하드 게이트 |
1. 취약점 스캐너( | pull 요청, 기본 푸시, 주간 크론, 수동 디스패치 | • PR에 대한 빠른 차등 AST 스캔 (~15초), 푸시에 대한 빠른 기준 스캔 ( | PR 병합 차단: 새로 도입된 높음 또는 심각 취약점이 감지된 경우 ( |
2. Autonomous Remediator( | 수동 트리거 ( | • 스캔 아티팩트 ( | PR을 열기 전에 깨끗한 프로덕션 diff를 보장합니다. |
세 가지 핵심 엔터프라이즈 설계 원칙
다음 섹션에서 이러한 워크플로를 빌드할 때 다음 세 가지 기본 아키텍처 핵심 요소를 염두에 두세요.
🔑 제로 트러스트 키 없는 인증 및 프리플라이트 검증:
장기 Google Cloud 서비스 계정 JSON 키는 GitHub Secrets에 저장되지 않습니다. 실행기는 Google Cloud STS를 통해 교환된 단기 OIDC 토큰을 생성하여 1시간 동안 유효한 임시 사용자 인증 정보를 만들고, Git 체크아웃에 persist-credentials: false를 설정하고, cm health --strict를 실행하여 모델 할당량을 사용하기 전에 사용자 인증 정보, 네트워크 도달 가능성, IAM 권한을 확인합니다.
⚡ 적응형 스캔 라우팅 및 기존 노이즈 제거:
CodeMender는 모든 pull 요청에 대해 느린 전체 저장소 감사를 실행하는 대신 각 CI 이벤트를 최적의 스캔 모드로 라우팅합니다. pull 요청은 수정된 파일과 1홉 호출자에 대해 15초 이내에 차등 AST 분석 (--diff="origin/$BASE_REF")을 트리거하고, 기본 브랜치 푸시는 단일 세션 빠른 기준 스캔 (--quick)을 실행하며, 주간 일정은 동시 파일별 심층 감사 (기본값 cm find .)를 실행합니다. PR에서 건드리지 않은 기존 코드베이스 취약점은 [LEGACY / UNTOUCHED]로 라벨이 지정됩니다. 이러한 취약점은 가시성을 위해 SARIF 보고서에 표시되지만 PR 병합 게이트를 차단하지 않으며 자동 수정을 트리거하지 않습니다.
🛡️ 복원력이 우수한 비차단 보안 모니터링 가능성:
--fail-on가 코드 1로 종료되어 안전하지 않은 PR을 차단하는 경우에도 steps.scan.outputs.completed == 'true' 및 hashFiles('results.sarif') != ''를 사용하여 보고서 내보내기 및 SARIF 업로드 단계를 보호하면 OASIS SARIF 알림 및 작업 요약이 계속 게시됩니다. 실행이 취소되거나 설정 또는 용량 오류 (10~60)로 인해 중단되는 경우 내보내기 및 업로드가 깔끔하게 건너뛰어집니다.
3. Cloud Shell에서 대상 저장소 인증 및 준비
키 없는 인증을 설정하고 자동화된 보안 파이프라인을 실행하려면 소유하고 있는 액세스 가능한 대상 GitHub 저장소가 필요합니다. 이 Codelab에서는 의도적으로 취약하게 만든 Node.js 웹 애플리케이션인 OWASP Juice Shop을 턴키 보안 플레이그라운드로 사용합니다.
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를 엽니다.
코드를 붙여넣고 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 권한 부여
Gemini Enterprise Agent Platform과 통신할 수 있도록 서비스 계정에 roles/aiplatform.user (Agent Platform 사용자) 역할을 부여합니다.
gcloud projects add-iam-policy-binding ${PROJECT_ID} \
--member="serviceAccount:${SERVICE_ACCOUNT_EMAIL}" \
--role="roles/aiplatform.user"
5단계: 워크로드 아이덴티티 풀 만들기
GitHub Actions ID 어설션을 관리하기 위해 워크로드 아이덴티티 풀을 만듭니다 (또는 프로젝트에 이미 있는 경우 자동으로 복원하고 사용 설정).
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 제공업체를 만들거나 업데이트하여 발급기관 URL 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 저장소 보안 비밀 및 워크플로 권한 구성
이제 Google Cloud WIF가 프로비저닝되었으므로 GitHub 저장소 보안 비밀을 구성하고 자동 보안 스캔 및 자동 해결 pull 요청에 필요한 워크플로 권한을 부여합니다.
GitHub CLI (gh)를 사용하여 Cloud Shell에서 직접 이러한 설정을 구성할 수 있으므로 긴 리소스 이름을 수동으로 복사하여 붙여넣을 필요가 없습니다.
1단계: GitHub CLI를 사용하여 저장소 보안 비밀 설정
Cloud Shell 터미널에서 다음 명령어를 실행하여 활성 환경 변수를 사용해 필요한 저장소 보안 비밀 두 개를 안전하게 설정합니다.
# 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 워크플로는 읽기 전용 권한으로 실행되며 pull 요청을 만들 수 없습니다. 9단계에서 CodeMender는 검증된 해결 pull 요청을 자율적으로 생성하고 제출합니다.
GitHub API를 통해 직접 쓰기 권한을 부여하고 pull 요청 생성을 사용 설정합니다.
[ -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)와 함께 master를 타겟팅하는 PR을 자동으로 종료하는 업스트림 기여자 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를 사용하여 자동 취약점 스캔 워크플로 빌드
이 단계에서는 브랜치 푸시 시 기준 스캔을 실행하고, pull 요청 시 차등 게이트를 실행하고, GitHub 코드 스캔으로 SARIF 내보내기를 실행하는 기본 지속적 보안 스캔 워크플로 (.github/workflows/codemender-scan.yml)를 작성합니다.
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)로 이동합니다.
상단 탐색 메뉴에서 작업 탭을 클릭합니다.
왼쪽 사이드바의 워크플로에서 CodeMender 보안 스캔 및 SARIF 내보내기를 선택합니다.
워크플로 실행을 트리거하거나 확인합니다.
자동 트리거: 푸시로 트리거된 자동 실행이 나열되면 직접 클릭하여 실시간 실행을 모니터링합니다.
수동 주문형 트리거 (workflow_dispatch): 아직 실행이 시작되지 않은 경우 오른쪽에 있는 워크플로 실행 ▾ 버튼을 클릭하고 Branch: master가 선택되어 있는지 확인한 후 (빠른 --quick 기준 스캔이 실행되도록 deep_scan 체크박스는 선택 해제된 상태로 유지) 녹색 워크플로 실행 버튼을 클릭합니다.
실행 중인 실행을 클릭하고 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
강화된 실행기 설정 ( 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 보고서(CVSS 심각도 점수가 포함된 cm report -f sarif), $GITHUB_STEP_SUMMARY 테이블 (cm report -f md), codemender-findings.json 아티팩트가 계속 게시됩니다. 설정 또는 용량 오류 (10~60) 시 내보내기를 건너뛰므로 빈 SARIF 파일이 기존 알림을 닫지 않습니다.
7. GitHub 코드 스캔 알림 및 보안 대시보드 검사
기준 푸시 워크플로가 완료되면 GitHub는 업로드된 SARIF 파일을 처리하고 작업 실행 요약과 저장소 보안 탭을 모두 채웁니다.
1단계: GitHub Actions에서 네이티브 작업 요약 검사하기
워크플로가 실행될 때마다 마크다운 요약 표 (cm report -f md)가 GitHub Actions 실행 개요에 직접 추가됩니다.
GitHub 저장소에서 작업 탭을 클릭합니다.
완료된 CodeMender Security Scan & SARIF Export 실행을 선택합니다 (기준 브랜치 푸시 감사는 SARIF 발견 항목을 게시한 후 녹색 ✅ 체크표시로 완료됨).
🛡️ CodeMender Security Scan Summary 섹션까지 아래로 스크롤하여 발견 사항 분석을 검사합니다.
🔒 CodeMender 보안 보고서
요약
총 발견 항목: 28개
심각도 | 개수 |
🔴 심각 | 6 |
🟠 높음 | 16 |
🟡 보통 | 6 |
상태 | 개수 |
🔴 미해결 | 28 |
발견 사항
ID | 심각도 | 신뢰도 | 상태 | 수정 | 파일 | 제목 |
| 🔴 심각 | 100% | 열기 | — |
| 사용자 프로필의 서버 측 코드 삽입을 통한 원격 코드 실행 |
| 🔴 심각 | 100% | 열기 | — |
| 사용자 인증 경로의 SQL 삽입 |
| 🔴 심각 | 100% | 열기 | — |
| B2B 주문 처리에서 샌드박스 이스케이프를 통한 원격 코드 실행 |
2단계: 중앙 집중식 코드 스캔 대시보드 살펴보기
GitHub 저장소 상단 탐색 메뉴에서 보안 및 품질 (또는 보안) 탭을 클릭합니다.
왼쪽 사이드바의 발견 항목에서 코드 스캔을 선택합니다.
여러 도구가 활성 상태인 경우 도구를 CodeMender (is:open branch:master tool:CodeMender)로 필터링하여 네이티브 CVSS 심각도 배지와 매핑된 알림을 검사합니다.
심각도 배지 | 취약점 제목 | 감지된 위치 | 브랜치 |
🔴 심각 | 사용자 프로필의 서버 측 코드 삽입을 통한 원격 코드 실행 |
|
|
🔴 심각 | 사용자 인증 경로의 SQL 삽입 |
|
|
🔴 심각 | B2B 주문 처리에서 샌드박스 이스케이프를 통한 원격 코드 실행 |
|
|
사용자 인증 경로의 SQL 삽입 (routes/login.ts:34)을 클릭하여 Gemini에서 합성한 오염된 데이터 흐름과 근본 원인 분석을 검토합니다.
8. 차등 스캔으로 안전하지 않은 pull 요청 차단
기준 스캔을 통해 OWASP Juice Shop에서 28개의 기존 취약점이 발견되었습니다. 기존의 정적 분석기를 사용하면 전체 백로그가 수정될 때까지 28개의 레거시 발견 항목이 향후 모든 pull 요청을 실패하게 하므로 팀은 병합 차단을 사용 중지해야 합니다.
이제 CodeMender의 PR Differential AST Gate (--diff + --fail-on="CRITICAL,HIGH")를 실제로 테스트해 보겠습니다. 새로운 취약점을 도입하는 pull 요청을 열고 CodeMender가 델타를 격리하고, 약 15~25초 내에 병합을 차단하고, 문제가 되는 정확한 줄에 주석을 추가하는 것을 확인합니다.
1단계: 명령어 삽입 결함을 도입하는 pull 요청 열기
Cloud Shell (클론된 저장소 작업공간 내)에서 기능 브랜치를 만들고, OS 명령어 삽입 취약점이 포함된 새 Express 경로를 추가하고, 포크에 대한 pull 요청을 엽니다.
[ -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 주석 확인
터미널 출력 끝에 표시된 pull 요청 URL을 엽니다.
Pull 요청 대화 페이지 하단의 검사 체크박스로 스크롤하여 자율 코드 취약점 스캔을 확인합니다.
델타 속도: 저장소에 있는 680개 이상의 파일을 모두 검사하는 대신 (약 3~5분) cm find . --diff="origin/${PRIMARY_BRANCH}"는 분석 범위를 routes/diagnostics.ts (및 1홉 호출자)로 엄격하게 제한하여 약 15~25초 만에 검사를 완료합니다.
기존 부채 격리 및 병합 차단: Juice Shop의 수정되지 않은 기존 취약점 28개 중 어느 것도 검사에 실패하지 않습니다. 새로 도입된 CRITICAL 명령어 삽입만 --fail-on="CRITICAL,HIGH"를 트리거하여 상태 코드 1 (빨간색 ❌)로 종료되어 PR 병합을 차단합니다.
풀 요청 상단의 Files changed 탭을 클릭합니다.
--fail-on가 코드 1로 종료될 때 워크플로의 종료 코드 가드 (steps.scan.outputs.completed == 'true')가 여전히 results.sarif를 내보내고 업로드하므로 GitHub 코드 스캔은 routes/diagnostics.ts의 execSync 바로 아래에 인라인 CodeMender — 심각 보안 주석을 렌더링합니다.
3단계: 테스트 PR을 삭제하고 기본 브랜치로 돌아가기
자율적 해결로 진행하기 전에 Cloud Shell 작업공간을 기본 브랜치로 다시 전환하고 안전하지 않은 테스트 pull 요청을 닫습니다.
[ -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 워크플로 빌드
이제 pull 요청 게이트가 새로운 취약점이 코드베이스에 유입되는 것을 방지하므로 Autonomous Remediation (.github/workflows/codemender-remediate.yml)을 배포하여 기존 취약점을 적극적으로 수정하고 검토 준비가 완료된 pull 요청을 열 수 있습니다.
1단계: 해결 워크플로 파일 다운로드하기
CodeMender는 저장소 공개 상태에 따라 두 가지 해결 템플릿을 제공합니다.
공개/오픈소스 저장소 (codemender-remediate.yml): 책임 있는 공개 모드 - 원시 익스플로잇 벡터를 공개적으로 노출하지 않고 GitHub 코드 스캔 SARIF 알림에 연결합니다.
비공개/엔터프라이즈 저장소 (codemender-remediate-private-repo.yml): 내부 감사 증거 모드 - 접을 수 있는 PR 섹션 내에 확인 아티팩트 (PLAN.md, REPORT.md, exploit.sh)를 삽입하고 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 저장소의 작업 탭으로 이동합니다.
왼쪽 사이드바의 워크플로에서 CodeMender Autonomous Security Remediation & Auto-PR을 선택합니다.
오른쪽에 있는 녹색 워크플로 실행 드롭다운을 클릭합니다.
워크플로 사용 위치: 기본 브랜치 (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
Clean Production Commits ( 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 요청 검토하기
저장소의 pull 요청 탭으로 이동하여 새 pull 요청 (예: 🛡️ [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)는 심각도 측정항목 카드, 줄 번호가 있는 구문 강조 코드 스니펫, 외부 웹 종속 항목이 없는 인라인 패치 차이 뷰어가 포함된 독립형 대화형 보안 대시보드를 만듭니다.
GHAS 라이선스가 없는 비공개 저장소를 지원하려면 SARIF를 업로드하는 대신 HTML 보고서 아티팩트를 보관처리하도록 .github/workflows/codemender-scan.yml를 업데이트합니다.
독립형 HTML 보고서 생성: Export Security Reports & Publish Job Summary 단계에서 cm report -f html > codemender-audit-report.html를 사용하여 마크다운 요약과 함께 HTML 대시보드를 내보냅니다.
보고서를 워크플로 아티팩트로 보관처리: 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) 또는 내부 마크다운 파일 (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> 접기 블록 내의 pull 요청 설명에 직접 형식을 지정합니다.
아티팩트 보관처리: actions/upload-artifact@v4를 통해 전체 증거 폴더를 보관처리합니다.
새 Git 커밋: 모든 런타임 메타데이터와 .exploit 디렉터리를 삭제하여 pull 요청 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 서비스 계정의 실시간 워크로드 아이덴티티 바인딩을 검사하려면 다음을 실행합니다.
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 pull 요청 생성 권한과 같은 추가 런타임 오류는 공식 CodeMender CI/CD 문제 해결 가이드를 참고하세요.
11. 리소스 삭제
Google Cloud 및 GitHub 환경에서 지속적인 리소스 소비를 방지하거나 테스트 구성을 삭제하려면 다음 삭제 단계를 수행합니다.
1단계: GitHub 저장소 보안 비밀 삭제 및 gh CLI를 통해 워크플로 사용 중지
GitHub CLI (gh)를 사용하여 저장소 작업 보안 비밀을 완전히 삭제하고 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 — 액세스 일시중지 (이 프로젝트에서 다시 실행할 계획인 경우 권장): Google Cloud IAM의 30일 소프트 삭제 보관 또는 서비스 계정 uniqueId 전파 지연을 트리거하지 않고 OIDC 토큰 교환을 즉시 사용 중지합니다.
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 공급자, 풀 (30일 동안 DELETED 상태로 유지됨), 전용 서비스 계정을 삭제합니다.
# 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초 미만으로 유지합니다.
다양한 형식의 보고 및 기본 작업 요약: 중앙 집중식 GitHub 코드 스캔을 위한 OASIS SARIF v2.1.0과 CI 워크플로 실행 페이지에서 바로 임원 가시성을 제공하는 기본 Markdown (cm report -f md >> $GITHUB_STEP_SUMMARY)을 결합했습니다.
휴먼인더루프 검토를 통한 자율적 해결: 수동적인 알림에서 벗어나 제안된 코드 패치를 자율적으로 합성하고, 회귀 테스트 모음을 기준으로 검증하며, 엔지니어 승인을 위해 감사 아티팩트가 포함된 검토 pull 요청을 여는 능동적인 해결 파이프라인으로 전환했습니다.
자동화된 품질 하드 게이트: 엄격한 브랜치 보호 및 병합 게이팅 (--fail-on)을 적용하여 위험한 취약점이 프로덕션에 도달하지 못하도록 차단했습니다.
아티팩트 기반 상태 전송 및 작업공간 격리: cm report import를 사용하여 CI 작업 간에 발견 항목을 원활하게 전송하는 동시에 .git/info/exclude를 사용하여 원래 Git 기록을 보존하고 사용자 인증 정보 유출을 방지했습니다.
개발자 사고방식의 변화
기존 DevSecOps 관행 | CodeMender Autonomous CI/CD Pipeline |
수동 알림 덤프: 스캐너가 원시 터미널 로그 또는 정적 오류 목록을 출력하여 분류 부담을 개발자에게 전가합니다. | Active Co-developer: 검증된 코드 패치를 합성하고 엔지니어 승인을 위해 병합 준비가 완료된 pull 요청을 엽니다. |
높은 거짓양성: 정적 AST 휴리스틱은 악용 가능성에 대한 증거 없이 수천 개의 이론적인 취약점을 표시합니다. | 2단계 익스플로잇 검증: 동적 샌드박스 테스트 생성 ( |
위험한 보안 비밀 저장소: CI 보안 비밀에 저장된 장기 서비스 계정 JSON 키는 실수로 유출되거나 사용자 인증 정보가 무분별하게 확산될 위험이 있습니다. | 제로 트러스트 WIF: 필요에 따라 Google Cloud STS와 교환되는 임시 OIDC 토큰을 통한 키 없는 인증입니다. |
모놀리식 재검사: 커밋할 때마다 전체 저장소 감사가 트리거되어 CI 워크플로가 지연되고 비용이 많이 드는 토큰이 낭비됩니다. | 이벤트 인식 AST 라우팅: pull 요청은 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 시작 템플릿 저장소
💡 자체 저장소에 적용:
시작 워크플로를 포크하고 CodeMender를 사용하여 조직의 Python, Go, Java, Rust, Node.js 저장소를 보호하세요.