שילוב עם CI/CD

‫CodeMender יכול לסרוק כל בקשת משיכה בצינור עיבוד הנתונים של האינטגרציה הרציפה (CI) והפיתוח הרציף (CD). הבדיקה נכשלת כש-CodeMender מוצא פגיעות ברמת חומרה CRITICAL או HIGH בקוד שמשתנה בבקשת משיכת הקוד. ממצאים בקוד שלא השתנה בבקשת משיכה מופיעים בדוח, אבל הם לא גורמים לסימון הבדיקה כנכשלת.

במאמר הזה מוסבר איך:

  • סריקת בקשות למשיכת קוד באמצעות GitHub Actions, ופתיחת בקשה למשיכת קוד עם תיקונים על ידי CodeMender.
  • סריקת בקשות משיכה באמצעות Cloud Build.
  • סורקים את השינויים לפני כל commit באמצעות Git pre-commit hook.

איך פועלות סריקות של בקשות משיכה

כדי לסרוק בקשת משיכה, מריצים את הפקודה cm find עם הדגל --diff וענף הבסיס של בקשת המשיכה:

cm find . --diff=origin/main

בתגובה לפקודה הזו, CodeMender מבצע את הפעולות הבאות:

  1. הכלי משווה בין הענף של בקשת משיכת השינויים לבין ענף הבסיס, וסורק את קובצי המקור שעברו שינוי ואת הקבצים שתלויים בהם באופן ישיר, כמו קבצים שקוראים לפונקציות וקבצים שמייבאים פונקציות.
  2. מבדילה בין נקודות חולשה חדשות שנוצרו או נחשפו בבקשת ה-pull לבין ממצאים קיימים, שמסומנים בתווית [LEGACY / UNTOUCHED]. בדיקת ה-CI חוסמת רק פגיעויות שנגרמות כתוצאה מבקשת המשיכה.
  3. הכלי יוצא עם סטטוס 1 אם ממצא שנגרם על ידי בקשת המשיכה תואם לחומרת הבעיה ב---fail-on. ערך ברירת המחדל הוא CRITICAL,HIGH.

במאמר מצבי החיפוש של CodeMender מוסבר איך --diff פועל בהשוואה למצבי סריקה אחרים.

לפני שמתחילים

‫CodeMender זמין בגרסת טרום-השקה ציבורית לקבוצה מוגבלת של לקוחות. כדי לקבל גישה, צריך לפנות לצוות המכירות.

  1. In the Cloud de Confiance console, on the project selector page, select or create a Cloud de Confiance project.

    Roles required to select or create a project

    • Select a project: Selecting a project doesn't require a specific IAM role—you can select any project that you've been granted a role on.
    • Create a project: To create a project, you need the Project Creator role (roles/resourcemanager.projectCreator), which contains the resourcemanager.projects.create permission. Learn how to grant roles.

    Go to project selector

  2. If you're using an existing project for this guide, verify that you have the permissions required to complete this guide. If you created a new project, then you already have the required permissions.

  3. Verify that billing is enabled for your Cloud de Confiance project.

  4. Enable the Agent Platform API and Cloud Resource Manager API APIs, if any are not already enabled.

    Roles required to enable APIs

    To enable APIs, you need the serviceusage.services.enable permission. If you created the project, then you likely already have this permission through the Owner role (roles/owner). Otherwise, you can get this permission through the Service Usage Admin role (roles/serviceusage.serviceUsageAdmin). Learn how to grant roles.

    Enable the APIs

  5. Install the Google Cloud CLI.

  6. Configure the gcloud CLI to use your federated identity.

    For more information, see Sign in to the gcloud CLI with your federated identity.

  7. To initialize the gcloud CLI, run the following command:

    gcloud init

מוודאים שגרסת CodeMender CLI שלכם תומכת בסריקות של בקשות משיכה. הצינורות שמופיעים במסמך הזה מורידים את הגרסה היציבה האחרונה בכל פעם שהם פועלים. כדי לבדוק התקנה מקומית, מריצים את הפקודה cm find --help ומחפשים את הדגל --diff. כדי לעדכן את ה-CLI, מריצים את cm update. מידע נוסף זמין במאמר התקנה והגדרה של ה-CLI.

התפקידים הנדרשים

כדי לקבל את ההרשאות שדרושות להגדרת שילובי CI/CD ל-CodeMender, בקשו מהאדמין להקצות לכם את התפקידים הבאים ב-IAM בפרויקט:

להסבר על מתן תפקידים, ראו איך מנהלים את הגישה ברמת הפרויקט, התיקייה והארגון.

יכול להיות שאפשר לקבל את ההרשאות הנדרשות גם באמצעות תפקידים בהתאמה אישית או תפקידים מוגדרים מראש.

כדי לוודא שלחשבון השירות של CI/CD יש את ההרשאות הנדרשות להפעלת CodeMender בצינור CI/CD, צריך לבקש מהאדמין להקצות לחשבון השירות של CI/CD את תפקידי ה-IAM הבאים:

להסבר על מתן תפקידים, ראו איך מנהלים את הגישה ברמת הפרויקט, התיקייה והארגון.

יכול להיות שהאדמין גם יוכל לתת לחשבון השירות של CI/CD את ההרשאות שנדרשות באמצעות תפקידים בהתאמה אישית או תפקידים מוגדרים מראש אחרים.

שיקולי אבטחה

כשמפעילים את CodeMender בתהליך עבודה של GitHub Actions, בטריגר לפיתוח גרסת Build של Cloud Build או בGit pre-commit hook, ‏CodeMender פועל בלי לבקש אישור. לפני שמגדירים את השילובים האלה, כדאי לקחת בחשבון את הנקודות הבאות:

  • הודעות לאישור: הדגל --yes משבית את ההודעות שמוצגות למשתמש ומבקשות ממנו לאשר את הפקודות של הסוכן ואת הכתיבה לקובץ. הדגל --bypass-warning מדלג על האזהרה שהסוכן יכול לשנות קבצים ולהריץ פקודות במערכת שלכם. לפני שמשתמשים בדגלים האלה, חשוב לקרוא את התנאים של גרסת טרום ההשקה בתחילת המסמך, כולל התנאים לגבי השבתת האישור האנושי.
  • ארגז חול: צינורות הנתונים משביתים את ארגז החול של CodeMender באמצעות --sandbox=false. לפקודות שהסוכן מריץ, כמו בנייה ובדיקות כשהוא מתקן ממצא, יש את אותה גישה כמו למשימה, כוללCloud de Confiance by S3NS האישורים והגישה לרשת. ארגז החול של Linux משתמש במרחבי שמות של משתמשים, שחלק מסביבות ה-CI, כמו קונטיינרים ללא הרשאות, לא תומכות בהם. השביתו את ארגז החול רק בסביבות מבודדות וחד-פעמיות. אם סביבת ה-CI תומכת בארגז החול, מסירים את --sandbox=false.
  • ‫Runners: הפעלת תהליך העבודה של GitHub Actions ב-Runners באירוח GitHub, כמו ubuntu-24.04, שמפעילים כל משימה במכונה וירטואלית חדשה. אל תשתמשו ב-runners באירוח עצמי שנעשה בהם שימוש חוזר בין משימות.
  • קוד לא מהימן: קוד בבקשת משיכה יכול להכיל טקסט שמנסה להכווין את הסוכן. תהליך העבודה של פעולות GitHub סורק רק בקשות משיכה מענפים באותו מאגר, ורק משתמשים עם הרשאת כתיבה יכולים ליצור אותן. ב-Cloud Build, משתמשים בבקרה על תגובות כדי שבעלים או משתפי פעולה יצטרכו לאשר בנייה של בקשות משיכה ממשתתפים אחרים.
  • גישה: מקצים לכל חשבון שירות רק את התפקידים הנדרשים של ניהול זהויות והרשאות גישה (IAM) בשביל צינור עיבוד הנתונים שלו.

סריקת בקשות משיכה באמצעות GitHub Actions

תהליך העבודה בקטע הזה סורק כל בקשת משיכה במאגר ב-GitHub.com. כש-CodeMender מוצא נקודת חולשה ברמת חומרה CRITICAL או HIGH בשינויים של בקשת המשיכה, תהליך העבודה מבצע את הפעולות הבאות:

  • הבדיקה CodeMender scan נכשלת.
  • מריץ את cm fix לכל ממצא, ופותח בקשת משיכה עם התיקונים לענף של בקשת המשיכה.

תהליך העבודה גם מעלה את הממצאים לסריקת הקוד של GitHub.

הגדרת איחוד שירותי אימות הזהות של עומסי עבודה

תהליך העבודה מאמת את Cloud de Confiance באמצעות איחוד שירותי אימות הזהות של עומסי עבודה, כך שלא צריך לאחסן מפתח של חשבון שירות ב-GitHub. מריצים את הפקודות הבאות באמצעות Google Cloud CLI:

  1. מגדירים משתני Shell לפרויקט ולמאגר:

    PROJECT_ID="PROJECT_ID"
    GITHUB_ORG="GITHUB_ORG"
    REPO="GITHUB_ORG/REPOSITORY"
    

    מחליפים את מה שכתוב בשדות הבאים:

    • ‫PROJECT_ID: מזהה הפרויקט ב- Cloud de Confiance .
    • ‫GITHUB_ORG: שם הארגון ב-GitHub, או שם המשתמש ב-GitHub של מאגר בחשבון אישי.
    • ‫REPOSITORY: השם של המאגר ב-GitHub.
  2. מפעילים את ממשקי ה-API שבהם נעשה שימוש ב-Workload Identity Federation:

    gcloud services enable iam.googleapis.com \
        cloudresourcemanager.googleapis.com iamcredentials.googleapis.com \
        sts.googleapis.com --project="${PROJECT_ID}"
    
  3. יוצרים חשבון שירות לזרימת העבודה ומקצים לו את תפקיד המשתמש ב-Agent Platform ב-IAM‏ (roles/aiplatform.user):

    gcloud iam service-accounts create codemender-ci \
        --project="${PROJECT_ID}" \
        --display-name="CodeMender CI"
    
    gcloud projects add-iam-policy-binding "${PROJECT_ID}" \
        --member="serviceAccount:codemender-ci@${PROJECT_ID}.s3ns.iam.gserviceaccount.com" \
        --role="roles/aiplatform.user"
    
  4. יוצרים מאגר זהויות של עומסי עבודה. אם כבר יש לכם מאגר ל-GitHub Actions, דלגו על השלב הזה והשתמשו במזהה של המאגר במקום github בשלבים הבאים.

    gcloud iam workload-identity-pools create github \
        --project="${PROJECT_ID}" \
        --location="global" \
        --display-name="GitHub Actions"
    
  5. יוצרים ספק עבור GitHub במאגר. תנאי המאפיין מקבל טוקנים רק ממאגרים שבבעלות GITHUB_ORG.

    gcloud iam workload-identity-pools providers create-oidc codemender \
        --project="${PROJECT_ID}" \
        --location="global" \
        --workload-identity-pool="github" \
        --display-name="CodeMender" \
        --attribute-mapping="google.subject=assertion.sub,attribute.repository=assertion.repository,attribute.repository_owner=assertion.repository_owner" \
        --attribute-condition="assertion.repository_owner == '${GITHUB_ORG}'" \
        --issuer-uri="https://token.actions.githubusercontent.com"
    
  6. מאפשרים לתהליכי עבודה במאגר להשתמש בחשבון השירות:

    POOL_ID=$(gcloud iam workload-identity-pools describe github \
        --project="${PROJECT_ID}" \
        --location="global" \
        --format="value(name)")
    
    gcloud iam service-accounts add-iam-policy-binding \
        "codemender-ci@${PROJECT_ID}.s3ns.iam.gserviceaccount.com" \
        --project="${PROJECT_ID}" \
        --role="roles/iam.workloadIdentityUser" \
        --member="principalSet://iam.googleapis.com/${POOL_ID}/attribute.repository/${REPO}"
    
  7. מבקשים את השם המלא של הספק. תצטרכו אותו בקטע הבא.

    gcloud iam workload-identity-pools providers describe codemender \
        --project="${PROJECT_ID}" \
        --location="global" \
        --workload-identity-pool="github" \
        --format="value(name)"
    

    הפלט אמור להיראות כך:

    projects/123456789/locations/global/workloadIdentityPools/github/providers/codemender
    

השינויים באיחוד שירותי אימות הזהות של עומסי העבודה וב-IAM ייכנסו לתוקף תוך 5 דקות לכל היותר. מידע נוסף זמין במאמר הגדרה של איחוד שירותי אימות הזהות של עומסי עבודה עם צינורות עיבוד נתונים לפריסה.

הוספת סודות למאגר

במאגר, עוברים אל הגדרות > Secrets and variables (סודות ומשתנים)> Actions (פעולות), ומוסיפים את הסודות הבאים במאגר:

סוד ערך
GCP_PROJECT_ID מזהה הפרויקט.
GCP_WIF_PROVIDER השם המלא של הספק מהקטע הקודם.
GCP_WIF_SERVICE_ACCOUNT ‫codemender-ci@PROJECT_ID.s3ns.iam.gserviceaccount.com, כאשר PROJECT_ID הוא מזהה הפרויקט.

מידע נוסף זמין במאמר שימוש בסודות ב-GitHub Actions.

קביעת הגדרות המאגר

  1. כדי לאפשר למשימת התיקון האוטומטי לפתוח בקשות משיכה, עוברים אל Settings > Actions > General. בקטע Workflow permissions (הרשאות של תהליך העבודה), בוחרים באפשרות Allow GitHub Actions to create and approve pull requests (מתן הרשאה ל-GitHub Actions ליצור ולאשר בקשות משיכה), ואז לוחצים על Save (שמירה). מאגר בארגון יורש את ההגדרה הזו מהארגון. אם אתם לא יכולים לשנות את זה, בקשו מבעלי הארגון. אם אתם לא משתמשים בתיקון אוטומטי, דלגו על השלב הזה. מידע נוסף מופיע במאמר בנושא ניהול ההגדרות של GitHub Actions במאגר.
  2. מוודאים שסריקת הקוד ב-GitHub זמינה למאגר. במאגרים פרטיים ופנימיים, כדי לסרוק את הקוד צריך להשתמש ב-GitHub Code Security. אם סריקת הקוד לא זמינה, מחקו את השלב Upload results to code scanning ואת ההרשאות security-events ו-actions ממשימת הסריקה. אחרת, ההעלאה נכשלת, וכך גם עבודת הסריקה. מידע נוסף זמין במאמר העלאת קובץ SARIF ל-GitHub.

הוספת תהליך העבודה

שומרים את תהליך העבודה הבא במאגר בתור .github/workflows/codemender.yml:

name: CodeMender

on:
  pull_request:

permissions: {}

defaults:
  run:
    shell: bash

env:
  CM_DOWNLOAD_URL: https://artifactregistry.googleapis.com/download/v1/projects/cmoc-prod/locations/us/repositories/codemender-cli-production/files/cm%3Astable%3Acm-linux-amd64.zip:download?alt=media

jobs:
  scan:
    name: CodeMender scan
    # Workflows for fork and Dependabot pull requests can't read this
    # repository's secrets, so they can't authenticate to Google Cloud.
    if: >-
      github.event.pull_request.head.repo.full_name == github.repository &&
      github.actor != 'dependabot[bot]'
    runs-on: ubuntu-24.04
    timeout-minutes: 15
    permissions:
      contents: read
      id-token: write         # Workload Identity Federation
      security-events: write  # Upload SARIF to code scanning
      actions: read           # Upload SARIF from a private repository
    outputs:
      findings: ${{ steps.scan.outputs.findings }}
    steps:
      - uses: actions/checkout@v7
        with:
          fetch-depth: 0  # cm needs the base branch history
          persist-credentials: false

      - uses: google-github-actions/auth@v3
        with:
          project_id: ${{ secrets.GCP_PROJECT_ID }}
          workload_identity_provider: ${{ secrets.GCP_WIF_PROVIDER }}
          service_account: ${{ secrets.GCP_WIF_SERVICE_ACCOUNT }}

      - name: Install the CodeMender CLI
        run: |
          curl -fsSL -o "$RUNNER_TEMP/cm.zip" "$CM_DOWNLOAD_URL"
          unzip -q -o "$RUNNER_TEMP/cm.zip" -d "$RUNNER_TEMP/cm"
          echo "$RUNNER_TEMP/cm" >> "$GITHUB_PATH"

      - name: Scan the pull request
        id: scan
        env:
          BASE_REF: ${{ github.base_ref }}
          FAIL_ON: CRITICAL,HIGH
        run: |
          cm init
          status=0
          cm find . \
            --diff="origin/$BASE_REF" \
            --diff-workers=16 \
            --fail-on="$FAIL_ON" \
            --format=sarif \
            --output=codemender.sarif \
            --sandbox=false \
            --yes \
            --bypass-warning || status=$?

          # Save the findings that failed the scan for the autofix job. Like the
          # CI gate, skip findings labeled [LEGACY / UNTOUCHED]: they're in code
          # that the pull request didn't change.
          echo '[]' > "$RUNNER_TEMP/codemender-findings.json"
          if [ "$status" -ne 0 ]; then
            cm report --format=json --status=OPEN \
              | jq --arg fail_on "$FAIL_ON" '
                  ($fail_on | ascii_upcase | split(",")) as $severities
                  | [.[]
                     | select(.title | startswith("[LEGACY / UNTOUCHED]") | not)
                     | select(.severity | ascii_upcase | IN($severities[]))]' \
              > "$RUNNER_TEMP/codemender-findings.json"
          fi
          echo "findings=$(jq length "$RUNNER_TEMP/codemender-findings.json")" >> "$GITHUB_OUTPUT"
          exit "$status"

      - name: Upload results to code scanning
        if: ${{ !cancelled() && hashFiles('codemender.sarif') != '' }}
        uses: github/codeql-action/upload-sarif@v4
        with:
          sarif_file: codemender.sarif
          category: codemender

      - name: Save findings for autofix
        if: ${{ !cancelled() && steps.scan.outputs.findings > 0 }}
        uses: actions/upload-artifact@v7
        with:
          name: codemender-findings
          path: ${{ runner.temp }}/codemender-findings.json
          retention-days: 1

  autofix:
    name: CodeMender autofix
    needs: scan
    if: ${{ !cancelled() && needs.scan.outputs.findings > 0 }}
    runs-on: ubuntu-24.04
    timeout-minutes: 60
    permissions:
      contents: write        # Push the fix branch
      pull-requests: write   # Open the fix pull request
      id-token: write        # Workload Identity Federation
    steps:
      - uses: actions/checkout@v7
        with:
          ref: ${{ github.head_ref }}
          persist-credentials: false

      - uses: google-github-actions/auth@v3
        with:
          project_id: ${{ secrets.GCP_PROJECT_ID }}
          workload_identity_provider: ${{ secrets.GCP_WIF_PROVIDER }}
          service_account: ${{ secrets.GCP_WIF_SERVICE_ACCOUNT }}

      - name: Install the CodeMender CLI
        run: |
          curl -fsSL -o "$RUNNER_TEMP/cm.zip" "$CM_DOWNLOAD_URL"
          unzip -q -o "$RUNNER_TEMP/cm.zip" -d "$RUNNER_TEMP/cm"
          echo "$RUNNER_TEMP/cm" >> "$GITHUB_PATH"

      - uses: actions/download-artifact@v8
        with:
          name: codemender-findings
          path: ${{ runner.temp }}/codemender

      - name: Fix findings
        run: |
          # Keep CodeMender state and the auth action's credentials file out of
          # the commits, and out of the `git clean` that cm fix runs.
          printf '%s\n' .cm_project 'gha-creds-*.json' >> .git/info/exclude

          cm init
          # Fix in the repository root instead of each file's directory.
          printf 'project_paths:\n  - "%s"\n' "$GITHUB_WORKSPACE" \
            >> ~/.codemender/config.yaml
          cm report import -f "$RUNNER_TEMP/codemender/codemender-findings.json"

          git config user.name "github-actions[bot]"
          git config user.email \
            "41898282+github-actions[bot]@users.noreply.github.com"

          cm report --format=json --status=OPEN \
            | jq -r '.[] | [.finding_id, .title] | @tsv' \
            > "$RUNNER_TEMP/findings.tsv"
          while IFS=$'\t' read -r id title; do
            # cm fix exits 0 when it makes no edits, so also check for changes.
            if cm fix "$id" \
                 --sandbox=false --yes --bypass-warning < /dev/null &&
               ! git diff --quiet; then
              git commit -q -a -m "Fix $title" -m "CodeMender finding $id"
            else
              echo "::warning::CodeMender didn't fix \"$title\" ($id)."
            fi
            # Discard files the fix agent created, such as test output.
            git reset -q --hard
            git clean -q -fd
          done < "$RUNNER_TEMP/findings.tsv"

      - uses: peter-evans/create-pull-request@v8
        with:
          branch: codemender/fix-pr-${{ github.event.pull_request.number }}
          base: ${{ github.head_ref }}
          title: "CodeMender fixes for #${{ github.event.pull_request.number }}"
          body: |
            CodeMender generated these fixes for findings in #${{ github.event.pull_request.number }}.
            Review each commit before you merge this pull request into `${{ github.head_ref }}`.

פרטי תהליך העבודה

התרשים הבא מציג את האינטראקציה בין הסריקה של בקשת המיזוג לבין עבודות התיקון האוטומטי:

מתבצע סריקה של בקשת משיכה. אם אין ממצאים שחוסמים את הבדיקה, היא עוברת. אחרת, הבדיקה נכשלת, CodeMender פותח בקשת מיזוג לתיקון, ומיזוג התיקונים סורק שוב את בקשת המיזוג.

הפעולה CodeMender scan מבצעת את הפעולות הבאות:

  1. הפונקציה מאחזרת את בקשת המיזוג עם ההיסטוריה המלאה שלה. בבקשות משיכה, GitHub בודק את ה-commit של המיזוג של הסתעפות בקשת המשיכה לתוך הסתעפות הבסיס.
  2. מבצע אימות ב- Cloud de Confiance ומוריד את הגרסה היציבה האחרונה של CodeMender CLI.
  3. מריצים את הפקודה cm find --diff מול ענף הבסיס. המשימה תיכשל אם CodeMender ימצא חולשה ברמת החומרה שצוינה ב-FAIL_ON בשינויים של בקשת ה-Pull.
  4. הדוח מועלה לסריקת הקוד ב-GitHub, גם אם הסריקה נכשלת. הדוח כולל ממצאים עם התווית [LEGACY / UNTOUCHED]. בנוסף, סריקת הקוד מציגה ממצא בבדיקות של בקשת המשיכה כשכל שורות הממצא נמצאות בהבדלים של בקשת המשיכה.
  5. שומר את הממצאים שנכשלו בעבודה לצורך תיקון אוטומטי.

משימת הסריקה לא פועלת בבקשות משיכה מפיצולים או מ-Dependabot, כי תהליכי העבודה של בקשות המשיכה האלה לא יכולים לקרוא את הסודות של המאגר. ב-GitHub, משימה שדילגו עליה מדווחת כמשימה שהסתיימה בהצלחה, כך שהבדיקה לא חוסמת את בקשות המשיכה האלה, גם אם מגדירים אותה כבדיקה נדרשת. אפשר לבדוק את בקשות המשיכה (pull request) האלה בעצמך, או לסרוק אותן באופן מקומי באמצעות cm find --diff.

משימת התיקון האוטומטי של CodeMender פועלת כשמשימת הסריקה שומרת את הממצאים. הוא מבצע את הפעולות הבאות:

  1. הפונקציה מאחזרת את הענף של בקשת המיזוג.
  2. הכלי מריץ cm fix לכל ממצא ומבצע כל תיקון בנפרד. אם CodeMender לא מתקן ממצא, המשימה מתעדת אזהרה ביומן. העבודה שומרת רק שינויים בקבצים שכבר נמצאים במאגר, ומוחקת קבצים שסוכן התיקון יוצר, כמו פלט של בדיקות.
  3. פותח בקשת משיכה מהענף codemender/fix-pr-NUMBER לענף בקשת המשיכה, כאשר NUMBER הוא מספר בקשת המשיכה. אם בקשת המיזוג לתיקון כבר קיימת, העדכון מתבצע במשימה. אם CodeMender לא מתקן ממצאים, או אם המשימה מגיעה למגבלת הזמן של 60 דקות, המשימה לא פותחת בקשת משיכה.

בדיקה ומיזוג של תיקונים

‫GitHub לא מפעיל באופן אוטומטי תהליכי עבודה לבקשות משיכה שתהליך עבודה יוצר באמצעות GITHUB_TOKEN. כדי לסרוק את בקשת המיזוג של התיקון, משתמש עם גישת כתיבה למאגר לוחץ על Approve workflows to run (אישור הפעלת תהליכי עבודה) בבקשת המיזוג של התיקון.

כשממזגים את בקשת משיכה (pull request) לתיקון, הענף של בקשת משיכה (pull request) משתנה, ותהליך העבודה סורק שוב את בקשת משיכה (pull request) המקורית.

התאמה אישית של תהליך העבודה

אפשר להתאים אישית את תהליך העבודה בדרכים הבאות:

  • כדי לשנות את רמות החומרה שגורמות לבדיקה להיכשל, בשלב Scan the pull request, מגדירים את FAIL_ON לרשימה של רמות חומרה מופרדות בפסיקים בלי רווחים, כמו CRITICAL,HIGH,MEDIUM. הערכים התקינים הם CRITICAL,‏ HIGH,‏ MEDIUM ו-LOW.
  • כדי לדווח על ממצאים בלי שהבדיקה תיכשל, צריך להגדיר את FAIL_ON: "". תהליך העבודה עדיין מעלה ממצאים לסריקת הקוד, אבל הוא לא מפעיל את משימת התיקון האוטומטי.
  • כדי לסרוק בקשות למשיכת קוד רק בענפים מסוימים, מוסיפים branches פילטר לטריגר pull_request. לאחר מכן, תהליך העבודה לא סורק בקשות משיכה לתיקון, כי הבסיס שלהן הוא ענף של בקשת משיכה:

    on:
      pull_request:
        branches: [main]
    
  • כדי להשבית את התיקון האוטומטי, מוחקים את המשימה autofix ואת השלב Save findings for autofix.

סריקת בקשות משיכה באמצעות Cloud Build

ה-build בחלק הזה סורק כל בקשת משיכה במאגר ב-GitHub שמחובר ל-Cloud Build. ה-build והבדיקה של GitHub נכשלים כש-CodeMender מוצא פרצת אבטחה ברמת חומרה CRITICAL או HIGH בשינויים של בקשת ה-pull. ה-build לא מתקן את הממצאים.

לפני שמתחילים, צריך לבצע את הפעולות הבאות:

  1. מקשרים את המאגר ל-Cloud Build כמאגר מהדור השני. מידע נוסף זמין במאמר בנושא חיבור למאגר GitHub.
  2. מפעילים את Cloud Build API ואת IAM API בפרויקט שיש לו גישה ל-CodeMender. הגרסה משתמשת ב-CodeMender בפרויקט שמריץ את הגרסה.

    PROJECT_ID="PROJECT_ID"
    gcloud services enable cloudbuild.googleapis.com iam.googleapis.com \
        --project="${PROJECT_ID}"
    

    מחליפים את PROJECT_ID במזהה הפרויקט ב- Cloud de Confiance.

יצירה של חשבון שירות

יוצרים חשבון שירות ל-build. כדי להשתמש ב-CodeMender, מקצים לו את תפקיד ה-IAM "משתמש בפלטפורמת הסוכנים" (roles/aiplatform.user), וכדי לכתוב יומני build, מקצים לו את התפקיד "כתיבת יומנים" (roles/logging.logWriter):

SA="codemender-build@${PROJECT_ID}.s3ns.iam.gserviceaccount.com"

gcloud iam service-accounts create codemender-build \
    --project="${PROJECT_ID}" \
    --display-name="CodeMender Cloud Build"

gcloud projects add-iam-policy-binding "${PROJECT_ID}" \
    --member="serviceAccount:${SA}" \
    --role="roles/aiplatform.user"

gcloud projects add-iam-policy-binding "${PROJECT_ID}" \
    --member="serviceAccount:${SA}" \
    --role="roles/logging.logWriter"

כדי לשמור דוחות SARIF בקטגוריה של Cloud Storage, צריך לתת לחשבון השירות גם גישה לקטגוריה:

BUCKET="BUCKET"

gcloud storage buckets add-iam-policy-binding "gs://${BUCKET}" \
    --member="serviceAccount:${SA}" \
    --role="roles/storage.objectUser"

מחליפים את BUCKET בשם של הקטגוריה שלכם ב-Cloud Storage.

הוספת הגדרות ה-build

מבצעים commit של תצורת ה-build הבאה לשורש של המאגר בתור cloudbuild.yaml:

# Scans a pull request with CodeMender. The build fails when CodeMender finds
# a CRITICAL or HIGH vulnerability in the pull request's changes.
steps:
  - id: install-codemender
    name: gcr.io/google.com/cloudsdktool/google-cloud-cli:slim
    script: |
      #!/usr/bin/env bash
      set -euo pipefail
      curl -fsSL -o /tmp/cm.zip "$_CM_DOWNLOAD_URL"
      python3 -m zipfile -e /tmp/cm.zip /workspace/.codemender-cli
      chmod +x /workspace/.codemender-cli/cm

  - id: fetch-history
    name: gcr.io/google.com/cloudsdktool/google-cloud-cli:slim
    script: |
      #!/usr/bin/env bash
      set -euo pipefail
      : "${_BASE_BRANCH:?Run this build from a pull request trigger.}"
      # Triggers check out only the pull request's commit. CodeMender needs the
      # history back to where the pull request branched from the base branch.
      # Fetch from origin, or from GitHub if origin isn't configured.
      refspec="+refs/heads/$_BASE_BRANCH:refs/remotes/origin/$_BASE_BRANCH"
      git fetch --quiet --unshallow origin "$refspec" ||
        git fetch --quiet --unshallow \
          "https://github.com/$REPO_FULL_NAME.git" "$refspec"
      merge_base=$(git merge-base HEAD "origin/$_BASE_BRANCH")
      echo "Merge base with $_BASE_BRANCH: $merge_base"

  - id: scan
    name: gcr.io/google.com/cloudsdktool/google-cloud-cli:slim
    script: |
      #!/usr/bin/env bash
      set -uo pipefail
      export PATH="/workspace/.codemender-cli:$PATH"
      # The project that has access to CodeMender.
      export GOOGLE_CLOUD_PROJECT="$PROJECT_ID"
      cm init || exit 1
      status=0
      cm find . \
        --diff="origin/$_BASE_BRANCH" \
        --diff-workers=16 \
        --fail-on=CRITICAL,HIGH \
        --format=sarif \
        --output=codemender.sarif \
        --sandbox=false \
        --yes \
        --bypass-warning || status=$?

      # cm find writes the SARIF report when the scan completes.
      if [ -f codemender.sarif ]; then
        # cm find doesn't print finding details, so print them here.
        cm report --status=OPEN --format=md
        if [ -n "${_REPORT_BUCKET:-}" ]; then
          gcloud storage cp codemender.sarif \
            "gs://$_REPORT_BUCKET/codemender/$BUILD_ID.sarif" || status=1
        fi
      fi
      exit "$status"

substitutions:
  _CM_DOWNLOAD_URL: https://artifactregistry.googleapis.com/download/v1/projects/cmoc-prod/locations/us/repositories/codemender-cli-production/files/cm%3Astable%3Acm-linux-amd64.zip:download?alt=media
  _REPORT_BUCKET: ''  # Optional: a bucket to save the SARIF report in.

timeout: 1200s
options:
  automapSubstitutions: true
  logging: CLOUD_LOGGING_ONLY

יצירת הטריגר

יוצרים טריגר שמריץ את ה-build לכל בקשת משיכה (pull request) אל main:

REGION="REGION"
CONNECTION="CONNECTION"
REPOSITORY="REPOSITORY"

gcloud builds triggers create github \
    --project="${PROJECT_ID}" \
    --region="${REGION}" \
    --name="codemender-scan" \
    --repository="projects/${PROJECT_ID}/locations/${REGION}/connections/${CONNECTION}/repositories/${REPOSITORY}" \
    --pull-request-pattern='^main$' \
    --comment-control=COMMENTS_ENABLED_FOR_EXTERNAL_CONTRIBUTORS_ONLY \
    --build-config=cloudbuild.yaml \
    --service-account="projects/${PROJECT_ID}/serviceAccounts/${SA}"

מחליפים את מה שכתוב בשדות הבאים:

  • ‫REGION: האזור של החיבור למאגר ב-Cloud Build.
  • ‫CONNECTION: השם של חיבור המאגר של Cloud Build.
  • ‫REPOSITORY: השם של המאגר המחובר ב-Cloud Build.

שימו לב לאפשרויות הבאות:

  • ‫--pull-request-pattern הוא ביטוי רגולרי לענף הבסיס של בקשת משיכת השינויים. משתמשים בתווים ^ ו-$ כדי להתאים את כל שם הענף.
  • עם --comment-control=COMMENTS_ENABLED_FOR_EXTERNAL_CONTRIBUTORS_ONLY, ה-build מופעל באופן אוטומטי לבקשות משיכה מבעלי המאגר וממשתפי פעולה. בבקשות משיכה של משתתפים אחרים, ה-build פועל אחרי שהבעלים או שותף העריכה מגיבים /gcbrun על בקשת המשיכה.
  • כדי לשמור דוחות SARIF בדלי, מוסיפים את --substitutions=_REPORT_BUCKET="${BUCKET}".

אפשר גם ליצור את הטריגר במסוף Cloud de Confiance . מידע נוסף זמין במאמר בנושא יצירת מאגרים מ-GitHub.

כדי להציג את יומני ה-build ב-GitHub, מוסיפים את --include-logs-with-status כשיוצרים את הטריגר, ונותנים לחשבון השירות את התפקיד "צפייה ביומנים" (roles/logging.viewer). כל מי שיכול לראות את הבדיקה ב-GitHub יכול לקרוא את יומני ה-build, כולל הממצאים.

איך מתבצעת הבנייה

התהליך כולל שלושה שלבים:

  1. ‫install-codemender מוריד את הגרסה היציבה האחרונה של CodeMender CLI.
  2. ‫fetch-history מאחזר את ההסתעפות הבסיסית ואת ההיסטוריה ש-CodeMender צריך. ‫Cloud Build בודק רק את הקומיט שהתחיל את הבנייה. השלב מאחזר מהמאגר המרוחק origin. אם לא מגדירים את origin, השלב מאחזר מ-GitHub בלי פרטי כניסה, וזה עובד רק במאגרים ציבוריים. במקרה כזה, מאגר פרטי צריך פרטי כניסה, כמו מפתח SSH. מידע נוסף זמין במאמר גישה ל-GitHub מ-build באמצעות מפתחות SSH.
  3. ‫scan מריץ את cm find --diff מול ענף הבסיס. בסיום הסריקה, כל הממצאים מודפסים ביומן הבנייה. אם מגדירים את _REPORT_BUCKET, השלב גם מעתיק את דוח ה-SARIF אל gs://BUCKET/codemender/BUILD_ID.sarif.

כדי לשנות את רמות החומרה שגורמות לכך שה-build ייכשל, עורכים את --fail-on בשלב scan. אם מריצים את ה-build בלי טריגר של בקשת משיכה, הוא נכשל עם ההודעה Run this build from a pull request trigger.

סריקה לפני כל קומיט

הוק pre-commit של Git יכול לסרוק את השינויים שהכנתם ולחסום את ה-commit אם CodeMender מוצא חולשה ברמת חומרה CRITICAL או HIGH. מפתחים יכולים לדלג על ה-hook, אז כדאי להשתמש בו בנוסף לסריקה ב-CI, ולא במקומה.

כל מפתח שמשתמש ב-hook צריך:

  • ה-CLI של CodeMender, מותקן ומוגדר כמו שמתואר בקטע התקנה והגדרה של ה-CLI, כולל gcloud auth application-default login ו-cm init. הוו צריך cm בPATH.
  • התפקיד Agent Platform User (משתמש ב-Agent Platform) (roles/aiplatform.user) בפרויקט עם גישה ל-CodeMender. ‫CodeMender משתמש בפרויקט שבמשתנה הסביבה GOOGLE_CLOUD_PROJECT. אם המשתנה הזה לא מוגדר, CodeMender משתמש בפרויקט מ-Application Default Credentials או בפרויקט שמוגדר כברירת מחדל ב-CLI של gcloud.

כדי להוסיף את ה-hook, שומרים את הסקריפט הבא בשם .git/hooks/pre-commit במאגר:

#!/bin/sh
# Scans staged changes with CodeMender before each commit.
if ! cm find . --diff --staged --fail-on=CRITICAL,HIGH \
    --yes --bypass-warning; then
  echo "CodeMender blocked this commit. To see the findings, run:" >&2
  echo "  cm report --status=OPEN" >&2
  echo "To commit anyway, run git commit --no-verify." >&2
  exit 1
fi

הופכים את הסקריפט לקובץ הפעלה, ואז מוסיפים את .cm_project לקובץ .gitignore, כדי לא לבצע commit לקובץ שנוצר על ידי CodeMender למעקב אחרי המאגר:

chmod +x .git/hooks/pre-commit
echo .cm_project >> .gitignore

כשמריצים את git commit, ה-hook סורק את הקבצים עם השינויים שהועברו ל-stage. הסריקה עשויה להימשך כמה דקות. ה-hook לא משבית את ארגז החול של CodeMender.

כדי לבצע את ה-commit בלי הסריקה, מריצים את git commit --no-verify.

סריקת דגלים

הדגלים הבאים של cm find שולטים בסריקות של בקשות משיכה. כדי לראות את כל הדגלים, מריצים את הפקודה cm find --help:

דגל ברירת מחדל תיאור
--diff[=REF] מושבת סורק את השינויים בהשוואה ל-REF, כמו origin/main. בלי REF, הסריקות לא כוללות שינויים שלא נשמרו בקבצים שעוקבים אחריהם (בהשוואה ל-HEAD). אי אפשר להשתמש באפשרות הזו עם --deep.
--staged מושבת ב---diff, הסריקות מתבצעות רק על שינויים שהועברו לשלב ההכנה.
--diff-depth DEPTH 1 עומק המעבר לניתוח ההשפעה (1 עד 3, ברירת מחדל: 1-hop).
--diff-workers COUNT 4 מספר הקבצים לסריקה בו-זמנית, מ-1 עד 16.
--diff-max-neighbors COUNT 10 המספר המקסימלי של קבצים תלויים לסריקה.
--fail-on SEVERITIES CRITICAL,HIGH במקרה של --diff, רמות החומרה שגורמות ל-cm find לצאת עם סטטוס 1. אם הערך ריק, הממצאים לא משפיעים על סטטוס היציאה.
--fail-on-truncation מושבת עם --diff, יוצא עם סטטוס 1 אם CodeMender מוצא יותר קבצים תלויים ממה ש---diff-max-neighbors מאפשר.
--format FORMAT table פורמט הדוח: table,‏ json או sarif.
--output FILE פלט רגיל הקובץ שאליו ייכתב הדוח.

פתרון בעיות

בקטע הזה מוסבר איך לפתור בעיות נפוצות שקשורות להפעלת CodeMender בצינורות עיבוד נתונים של CI/CD.

ההרשאה נדחתה

ביומן מופיעה שורה כמו זו:

[1/1] ❌ src/app.py: start session failed: StartSession failed with HTTP 403 Forbidden.

ההודעה יכולה לכלול גם את Permission 'aiplatform.interactions.create' denied. זה מה שכדאי לבדוק:

  • לפרויקט יש גישה ל-CodeMender.
  • לחשבון השירות או למשתמש יש את התפקיד "משתמש בפלטפורמת הסוכנים" (roles/aiplatform.user) בפרויקט.
  • ‫Agent Platform API מופעל בפרויקט.
  • הכלי CodeMender משתמש בפרויקט הנכון. ב-GitHub Actions, בודקים את הסוד GCP_PROJECT_ID. ‫Cloud Build משתמש בפרויקט שבו מופעלת הבנייה. באופן מקומי, בודקים את משתנה הסביבה GOOGLE_CLOUD_PROJECT, או את פרויקט ברירת המחדל אם המשתנה לא מוגדר.

הגדרת המשאב בתהליך

אם ביומן מופיע Resource setup has just started. Please try again shortly. או Resource setup is in progress. Please try again shortly., סימן ש-CodeMender מגדיר משאבים לפרויקט. בדרך כלל זה לוקח כמה דקות. מריצים את המשימה שוב מאוחר יותר.

סימון לא ידוע: ‎--diff

אם הפקודה cm find נכשלת עם Error: unknown flag: --diff, הגרסה של ה-CLI לא תומכת בסריקות של בקשות משיכה. מריצים את cm update או מורידים את הגרסה האחרונה.

לא נמצא ענף בסיס

אם הפקודה cm find נכשלת עם שגיאה כמו השגיאה הבאה, הענף הבסיסי לא נמצא ב-checkout:

Error: retrieving VCS diff: git diff failed: exit status 128: fatal: ambiguous argument 'origin/main': unknown revision or path not in the working tree.

לפני שמריצים את הפקודה cm find, צריך לאחזר את ענף הבסיס ואת ההיסטוריה שלו. ב-GitHub Actions, מגדירים את fetch-depth: 0 ב-actions/checkout.

קבצים שלא נסרקו

שורה עם ❌, כמו [1/1] ❌ src/app.py: start session failed, מציינת ש-CodeMender לא הצליח לסרוק את הקובץ הזה. צריך לבדוק את השגיאה בשורה ואז להריץ את העבודה שוב.

אין בקשת משיכה לתיקון

אם תהליך העבודה לא פותח בקשת משיכה לתיקון, צריך לבדוק את הדברים הבאים:

  • אם משימת התיקון האוטומטי נכשלת כשיוצרים את בקשת משיכת השינויים, מפעילים את האפשרות Allow GitHub Actions to create and approve pull requests (מתן הרשאה ל-GitHub Actions ליצור ולאשר בקשות משיכת שינויים). מידע נוסף זמין במאמר קביעת הגדרות למאגר.
  • אם עבודת התיקון האוטומטי מצליחה בלי לפתוח בקשת משיכה, CodeMender לא תיקן אף ממצא. ביומן המשימה מוצגת אזהרה לכל ממצא ש-CodeMender לא תיקן.

העלאת סריקת הקוד נכשלה

אם שלב ההעלאה נכשל עם GitHub Code Security or GitHub Advanced Security must be enabled for this repository to use code scanning, הפעילו את GitHub Code Security למאגר או מחקו את שלב ההעלאה. מידע נוסף זמין במאמר קביעת הגדרות למאגר.

האזהרה לא אושרה

אם הפקודה cm נכשלת עם השגיאה warning not acknowledged. Please run interactively once to acknowledge or pass --bypass-warning, CodeMender צריך אישור שהוא לא יכול לבקש בסביבה לא אינטראקטיבית. מוסיפים את --bypass-warning לפקודה. לפני שתעשו זאת, קראו את תנאי התצוגה המקדימה בחלק העליון של הדף הזה.

לקבלת עזרה נוספת, אפשר לפנות לצוות המכירות.