כיצד אוכל לשלב ניתוח קוד סטטי ב-CI/CD Pipelines?

כיצד אוכל לשלב ניתוח קוד סטטי ב-CI/CD Pipelines?

IN-COM יוני 11, 2026

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

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

היכן בצנרת להריץ ניתוח סטטי

ניתוח סטטי צריך לרוץ במספר נקודות, כאשר כל אחת משרתת מטרה שונה. הפעלה שלו רק בנקודה אחת יוצרת נקודות מתות; הפעלה שלו בכל מקום יוצרת באותה מידה צינורות איטיים שמפתחים מנתבים סביבם.

שלב הצינורמה לרוץתקציב השהייהמטרה
קדם-התחייבות (מקומי)Linters מהיר בלבד (ESLint, Clippy, Rustfmt)מתחת ל 5 שניותעצרו בעיות ברורות לפני שהן מגיעות למאגר
בקשת משיכה / בקשת מיזוגסריקת סיבים מלאה + SAST + סריקת SonarQube מצטברתפחות משתי דקותמיזוגי בלוקים בנושאים קריטיים חדשים
בניית סניף ראשיניתוח מלא כולל כיסוי, כפילויות, חוב טכניפחות משתי דקותמעקב אחר מגמות איכות, עדכון לוחות מחוונים
מתוכנן מדי לילהסריקות עמוקות, ביקורת תלות, בדיקות תאימותאין תקציבלזהות בעיות של בנייה איטית וסיכון בשרשרת האספקה

השלב החשוב ביותר הוא בקשת המשיכהניתוח שפועל ב-push to main מגיע לאחר שהתקבלה ההחלטה למזג. ניתוח שפועל בבקשת pull ומפרסם הערות בתוך הקוד עם הקשר מדויק של הקובץ והשורה מגיע כאשר המפתח עדיין בקוד ועלות התיקון היא הנמוכה ביותר.

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

פעולות GitHub: השלמת תצורת עבודה

GitHub Actions היא פלטפורמת ה-CI הנפוצה ביותר. תהליך העבודה הבא מפעיל צינור איכות שכבתי: בדיקת עיצוב, עיבוד טקסט, בנייה, סריקת אבטחה SAST ושער איכות SonarCloud.

יאמל

# .github/workflows/quality.yml
name: Code Quality

on:
  push:
    branches: [main, develop]
  pull_request:
    branches: [main, develop]

jobs:
  # ── Layer 1: Fast checks (under 60 seconds) ─────────────────────────────
  lint:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - name: Set up Node.js
        uses: actions/setup-node@v4
        with:
          node-version: "20"
          cache: "npm"

      - run: npm ci

      - name: Lint (fail on warnings)
        run: npx eslint src/ --max-warnings 0

      - name: Format check
        run: npx prettier --check src/

      - name: TypeScript type check
        run: npx tsc --noEmit

  # ── Layer 2: Security SAST (Semgrep) ────────────────────────────────────
  sast:
    runs-on: ubuntu-latest
    needs: lint
    steps:
      - uses: actions/checkout@v4

      - name: Semgrep SAST scan
        uses: semgrep/semgrep-action@v1
        with:
          config: p/javascript p/nodejs p/owasp-top-ten
        env:
          SEMGREP_APP_TOKEN: ${{ secrets.SEMGREP_APP_TOKEN }}

  # ── Layer 3: SonarCloud quality gate ────────────────────────────────────
  sonar:
    runs-on: ubuntu-latest
    needs: lint
    steps:
      - uses: actions/checkout@v4
        with:
          fetch-depth: 0  # full history required for blame data

      - name: Set up Node.js
        uses: actions/setup-node@v4
        with:
          node-version: "20"
          cache: "npm"

      - run: npm ci

      - name: Run tests with coverage
        run: npm test -- --coverage --coverageReporters=lcov

      - name: SonarCloud scan
        uses: SonarSource/sonarcloud-github-action@master
        env:
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
          SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}

נכסים

# sonar-project.properties
sonar.projectKey=my-org_my-project
sonar.organization=my-org
sonar.sources=src
sonar.tests=tests
sonar.javascript.lcov.reportPaths=coverage/lcov.info
sonar.coverage.exclusions=**/*.test.js,**/*.spec.js

החלטות תצורה מרכזיות בתהליך עבודה זה:

  • --max-warnings 0 ב-ESLint: כל אזהרה היא כשל בנייה. צוותים שמאפשרים אזהרות צוברים אותן עד שאף אחד לא קורא אותן.
  • fetch-depth: 0 בקופה: SonarCloud זקוק להיסטוריית git מלאה כדי להקצות אשמה לבעיות שהוכנסו ולחשב נכון את מדדי הקוד החדש.
  • Semgrep פועל לאחר סריקת lint במקביל ל-Sonar: סריקת אבטחה ואיכות הן דאגות עצמאיות; הרצתן במקביל מפחיתה את זמן ה-pipeline הכולל.
  • בדיקות מבוצעות עם lcov פלט כיסוי: SonarCloud קורא זאת כדי להציג כיסוי לכל קובץ ולאכוף ספי כיסוי בשער האיכות.

GitLab CI/CD: צינור איכות עם שערי איכות

יאמל

# .gitlab-ci.yml
stages:
  - lint
  - test
  - analyze
  - security

variables:
  SONAR_HOST_URL: "https://sonarcloud.io"

lint:
  stage: lint
  image: node:20
  cache:
    paths: [node_modules/]
  script:
    - npm ci
    - npx eslint src/ --max-warnings 0
    - npx prettier --check src/
    - npx tsc --noEmit
  rules:
    - if: $CI_MERGE_REQUEST_IID
    - if: $CI_COMMIT_BRANCH == "main"

test:
  stage: test
  image: node:20
  script:
    - npm ci
    - npm test -- --coverage --coverageReporters=lcov cobertura
  coverage: '/Lines\s*:\s*(\d+\.?\d*)%/'
  artifacts:
    reports:
      coverage_report:
        coverage_format: cobertura
        path: coverage/cobertura-coverage.xml

sonarcloud:
  stage: analyze
  image:
    name: sonarsource/sonar-scanner-cli:latest
    entrypoint: [""]
  variables:
    SONAR_USER_HOME: "${CI_PROJECT_DIR}/.sonar"
    GIT_DEPTH: "0"
  cache:
    key: "${CI_JOB_NAME}"
    paths: [.sonar/cache]
  script:
    - sonar-scanner
  rules:
    - if: $CI_MERGE_REQUEST_IID
    - if: $CI_COMMIT_BRANCH == "main"

semgrep:
  stage: security
  image: returntocorp/semgrep:latest
  script:
    - semgrep scan --config=p/owasp-top-ten --config=p/javascript
      --sarif --output=semgrep.sarif src/
  artifacts:
    reports:
      sast: semgrep.sarif
  rules:
    - if: $CI_MERGE_REQUEST_IID
    - if: $CI_COMMIT_BRANCH == "main"

השמיים artifacts: reports: sast: block משלב את פלט SARIF מ-Semgrep ישירות לתוך לוח המחוונים של האבטחה של GitLab, שם הממצאים מופיעים בדוח האבטחה של בקשת המיזוג ולא כפלט גולמי של יומן CI.

ג'נקינס: צינור הצהרתי עם SonarQube

קצבי

// Jenkinsfile
pipeline {
    agent any

    tools {
        nodejs "NodeJS-20"
    }

    environment {
        SONAR_TOKEN = credentials('sonar-token')
    }

    stages {
        stage('Lint') {
            steps {
                sh 'npm ci'
                sh 'npx eslint src/ --max-warnings 0'
                sh 'npx tsc --noEmit'
            }
        }

        stage('Test') {
            steps {
                sh 'npm test -- --coverage --coverageReporters=lcov'
            }
            post {
                always {
                    publishHTML([
                        allowMissing: false,
                        reportDir: 'coverage/lcov-report',
                        reportFiles: 'index.html',
                        reportName: 'Coverage Report'
                    ])
                }
            }
        }

        stage('SonarQube Analysis') {
            steps {
                withSonarQubeEnv('SonarQube') {
                    sh '''
                        npx sonar-scanner \
                          -Dsonar.projectKey=my-project \
                          -Dsonar.sources=src \
                          -Dsonar.javascript.lcov.reportPaths=coverage/lcov.info
                    '''
                }
            }
        }

        stage('Quality Gate') {
            steps {
                timeout(time: 5, unit: 'MINUTES') {
                    waitForQualityGate abortPipeline: true
                }
            }
        }
    }

    post {
        failure {
            emailext(
                subject: "Quality Gate FAILED: ${env.JOB_NAME} #${env.BUILD_NUMBER}",
                body: "Build failed quality gate. Review: ${env.BUILD_URL}",
                to: "${env.CHANGE_AUTHOR_EMAIL}"
            )
        }
    }
}

waitForQualityGate abortPipeline: true הוא הקו הקריטי. הוא מבצע סריקה של SonarQube עד להשלמת הניתוח ונכשל בבנייה אם שער האיכות אינו עומד. בלעדיו, הצינור מסתיים בזמן שהניתוח עדיין פועל ותוצאת שער האיכות לעולם לא נאכפת.

הגדרת שערי איכות שצוותים באמת מכבדים

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

תצורת התחלה מומלצת עבור שער איכותי חדש של SonarQube:

מטרימַצָבסף
באגים חדשיםגדול מ0
פגיעויות חדשותגדול מ0
נבדקו נקודות אבטחה חדשותפחות מ100%
כיסוי קוד חדשפחות מ80%
שורות משוכפלות חדשותגדול מ3%
קוד חדש מריחגדול מ10

החל ספים אלה על קוד חדש בלבד (תקופת "הקוד החדש" ב-SonarQube). אין להחיל אותם על בסיס הקוד המלא בפרויקט מדור קודם, כישלון בכל בנייה עקב חוב טכני שנצבר במשך חמש שנים יגרום לצוותים להשבית לחלוטין את השער. גישת הקוד החדש מאפשרת לכם לעצור את הדימום בזמן שהחוב הקיים מטופל בנפרד.

ספי ראצ'ט לאורך זמן. התחילו עם ספים שמרניים, ולאחר מכן הדקו אותם בכל רבעון ככל שהצוות מסיים את צבר ההזמנות שלו. שער שעובר היום עם כיסוי של 75% יכול להיות מעודכן ל-80% ברגע שקו הבסיס יהיה בנוחות מעליו. הידוק הדרגתי הוא בר קיימא יותר מקפיצה לשלמות.

טיפול בביצועי ניתוח סטטי בבסיסי קוד גדולים

הסיבה הנפוצה ביותר לכך שצוותים משביתים או עוקפים ניתוח סטטי ב-CI היא שזה הופך את הצינורות לאיטיים מדי. סריקת איכות של 15 דקות בכל דחיפה לענף תכונות הורגת את זרימת המפתחים. מספר אסטרטגיות שומרות על מהירות הניתוח:

ניתוח מצטבר (קוד חדש בלבד). SonarQube ורוב כלי הניתוח הסטטי הארגוני תומכים בניתוח רק של הקבצים שהשתנו בבקשת משיכה ולא של בסיס הקוד המלא. sonar.pullrequest.base, sonar.pullrequest.branch, ו sonar.pullrequest.key פרמטרים כדי לאפשר סריקה מצטברת ספציפית ל-PR. ניתוח מלא עדיין פועל בעת מיזוג לקוד הראשי; סריקת PR פועלת בפחות מ-2 דקות ברוב בסיסי הקוד.

מטמון. החלק היקר ביותר בהרצות רבות של ניתוח סטטי הוא הורדת הכלי והגדרות הכללים שלו. שמור במטמון את קובץ הסורק הבינארי, מסד הנתונים של הכללים וכל תלות שנפתרה בין ההרצות:

יאמל

# GitHub Actions: cache SonarCloud scanner
- name: Cache SonarCloud packages
  uses: actions/cache@v4
  with:
    path: ~/.sonar/cache
    key: ${{ runner.os }}-sonar
    restore-keys: ${{ runner.os }}-sonar

מַקבִּילוּת. סריקת סיבים, סריקת אבטחה וניתוח שערי איכות הם עניינים עצמאיים. הפעל אותם במשימות מקבילות ולא ברצף. הזמן הכולל של הצינור נקבע על ידי המשימה האיטית ביותר, ולא על ידי סכום כל המשימות.

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

ניתוח סטטי בצינורות בעלי חשיבות אבטחה: SAST וסריקת תלויות

צינורות אבטחה המתמקדים באבטחה מוסיפים שתי שכבות מעבר לניתוח איכות סטנדרטי: SAST (בדיקות אבטחה סטטיות של יישומים) עבור פגיעויות בקוד יישומים, וסריקת תלויות עבור CVEs ידועים בספריות של צד שלישי.

כלי SAST (Semgrep, CodeQL, Snyk Code, Checkmarx) מנתחים כיצד נתונים לא מהימנים זורמים דרך קוד אפליקציה כדי להגיע לפעולות רגישות. הם מוצאים הזרקת SQL, XSS, הזרקת פקודות, ביטול סריאליזציה לא מאובטח, ופרטי גישה מקודדים ש-linters באיכות סטנדרטית אינם יכולים לזהות.

יאמל

# GitHub Actions: CodeQL analysis for deep vulnerability detection
- name: Initialize CodeQL
  uses: github/codeql-action/init@v3
  with:
    languages: javascript-typescript

- name: Perform CodeQL Analysis
  uses: github/codeql-action/analyze@v3
  with:
    category: "/language:javascript-typescript"

סריקת תלות בדיקות package.json, pom.xml, requirements.txtוקבצי מניפסט מקבילים כנגד מסדי נתונים של פגיעויות:

יאמל

# Dependency audit in the pipeline
- name: Dependency audit
  run: |
    npm audit --audit-level=high
    # Fail if high or critical vulnerabilities are found

מודל האבטחה השכבתי מציב SAST מהיר מבוסס תבניות (Semgrep) על כל PR לקבלת משוב מיידי למפתחים ו-SAST סמנטי עמוק (CodeQL) על בסיס מתוזמן לכיסוי יסודי. סריקת תלויות פועלת על כל בנייה מכיוון ש-CVEs חדשים מתפרסמים מדי יום.

טעויות נפוצות באינטגרציה וכיצד להימנע מהן

ניתוח מריץ רק על הענף הראשי. הערך של ניתוח סטטי הוא לזהות בעיות לפני שהן מתמזגות, לא לאחר מכן. ניתוח שפועל רק לאחר המיזוג משמש כדוח, לא כשער.

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

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

התעלמות מצבר הניתוח בבסיסי קוד מדור קודם. הפעלת SonarQube בפעם הראשונה על בסיס קוד מדור קודם של מיליון שורות וקבלת 40,000 בעיות היא מרתיעה ואינה יעילה. השתמשו בגישת הבסיס של קוד חדש: הגדירו תאריך התחלה של "קוד חדש", החלו שערים רק על קוד שנכתב לאחר תאריך זה, והתייחסו לצבר הבעיות הקיים כתוכנית נפרדת להפחתת חוב טכני.

לא מקשר תוצאות לתגובות של בקשת משיכה. תוצאות ניתוח המתפרסמות בלוח מחוונים נפרד שמפתחים חייבים לבקר בו מתעלמות. תוצאות ניתוח המתפרסמות כהערות מוטבעות בשורות הספציפיות שהציגו בעיות, בתוך ממשק סקירת בקשות המשיכה, מטופלות. sonar.pullrequest.* פרמטרים עבור אינטגרציה של GitHub, GitLab או Bitbucket כך שממצאים יופיעו במקום בו מתרחשת סקירת קוד.

איך SMART TS XL משתלב עם צינורות CI/CD

רוב כלי הניתוח הסטטי בצינורות CI/CD רואים שפה אחת בכל פעם. ESLint רואה JavaScript. SonarQube רואה Java, Python, JavaScript ו-C#. אף אחד מהם לא רואה COBOL, JCL, RPG או את התלויות בין-שפות המחברות תוכנית אצווה של מיינפריים למיקרו-שירות Java שצורך את הפלט שלה, שמתחבר לממשק ה-JavaScript שמציג אותו.

SMART TS XL משתלב בצינורות CI/CD כשכבת ניתוח סטטית חוצת שפות המכסה כל שפה בסביבת הארגון בו זמנית. כאשר שירות Java משתנה, SMART TS XL"S ניתוח השפעות עוקב אחר גרף התלות כדי לזהות אילו תוכניות COBOL, סכמות מסד נתונים ושירותים במורד הזרם מושפעים משינוי זה, לפני פריסת השינוי. כאשר עותקי COBOL משתנים, הוא מזהה כל תוכנית בכל תיק היישומים הכולל את העותקיים הללו ותזדקק לאימות.

מודעות זו לתלות בין-לשונית היא היכולת שהופכת SMART TS XLשילוב CI/CD שונה מהוספת שפה נוספת ל-SonarQube. מדובר לא רק בניתוח הקוד בקובץ שהשתנה. מדובר בהבנת מה עוד במערכת ישפיע השינוי, שהוא הניתוח הארכיטקטוני שקובע את ההיקף האמיתי של השינוי לפני שהוא מתבצע.

עבור צוותי פיתוח ארגוני הפועלים על פני מערכות מדור קודם ומודרניות, SMART TS XL"S אינטגרציה של DevOps היכולת מטמיעה את הניתוח המבני הזה בתהליך העבודה של סקירת הצינור, ומספקת את הנראות הארכיטקטונית שכלים ספציפיים לשפה אינם יכולים לספק. התוצאה היא שערי איכות האוכפים סטנדרטים לא רק בתוך גבול שפה אלא על פני המערכת כולה, ומבטיחים ששינוי בכל רכיב לא יביא לכשלים בלתי צפויים ברכיבים התלויים בו.

ניתוח סטטי כאזרח קבוע בצינורות

הצוותים שעושים ניתוח סטטי נכון מתייחסים אליו באותו אופן שהם מתייחסים לבדיקות: לא כשלב שיש לעבור, אלא כתרגול מתמשך המוטמע בכל commit. הניתוח פועל על כל בקשת pull. תוצאות שער האיכות מופיעות בהתאם לסקירת הקוד. ספים מתהדקים ככל שבסיס הקוד משתפר. קטגוריות חדשות של ניתוח מתווספות ככל שבשלות האבטחה והאיכות של הצוות גדלה.

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