Jak zintegrować statyczną analizę kodu z procesami CI/CD?

Jak zintegrować statyczną analizę kodu z procesami CI/CD?

W-COM 11 czerwca 2026 r.

Każda linijka kodu, która trafia do produkcji, została zatwierdzona przez kogoś, kto wierzył w jej poprawność. Statyczna analiza kodu to zautomatyzowany system, który sprawdza, czy to przekonanie było słuszne, nie poprzez uruchomienie kodu, ale poprzez odczytanie jego struktury, śledzenie przepływów danych i porównanie go ze znanymi wzorcami luk w zabezpieczeniach, błędów i naruszeń jakości. Pytanie nie brzmi, czy go uruchomić, ale na jakim etapie procesu go uruchomić, które narzędzie uruchomić, jakie progi egzekwować i jak sprawić, by informacje zwrotne były na tyle szybkie, aby programiści faktycznie na nie reagowali, a nie je ignorowali.

Różnica między analizą statyczną jako aktywnością typu checkbox a analizą statyczną jako prawdziwą bramką jakości polega na konfiguracji. Skaner, który działa i generuje raport, którego nikt nie czyta, to teatr. Bramka jakości, która kończy kompilację niepowodzeniem, gdy pojawiają się nowe krytyczne luki w zabezpieczeniach, blokuje scalanie, gdy pokrycie spadnie poniżej progu, i wyświetla precyzyjne informacje zwrotne dotyczące plików i linii w interfejsie przeglądu pull requestów, to system, który zmienia zachowanie w momencie podejmowania decyzji.

Gdzie w procesie należy uruchomić analizę statyczną

Analiza statyczna powinna być przeprowadzana w wielu punktach, z których każdy służy innemu celowi. Uruchomienie jej tylko w jednym punkcie tworzy martwe punkty; uruchomienie jej wszędzie w równym stopniu tworzy powolne potoki, które programiści omijają.

Etap rurociąguCo biegaćBudżet opóźnieniaCel
Wstępne zatwierdzenie (lokalne)Tylko szybkie lintery (ESLint, Clippy, rustfmt)Poniżej 5 sekundZatrzymaj oczywiste problemy zanim trafią do repozytorium
Żądanie ściągnięcia/żądanie scaleniaPełne skanowanie + SAST + przyrostowe skanowanie SonarQubeMniej niż 3 minutyŁączenie bloków w przypadku nowych krytycznych problemów
Kompilacja głównej gałęziPełna analiza obejmująca zasięg, duplikację i dług technicznyMniej niż 10 minutyŚledź trendy jakościowe, aktualizuj pulpity nawigacyjne
Zaplanowane na nocGłębokie skanowanie, audyt zależności, sprawdzanie zgodnościBrak budżetuWykrywaj problemy związane z powolnym rozwojem i ryzyko związane z łańcuchem dostaw

Najcenniejszym etapem jest żądanie ściągnięciaAnaliza uruchamiana po przesłaniu do serwera głównego pojawia się po podjęciu decyzji o scaleniu. Analiza uruchamiana po żądaniu ściągnięcia i publikująca komentarze w tekście z precyzyjnym kontekstem pliku i wiersza pojawia się, gdy programista wciąż pracuje nad kodem, a koszt naprawy jest najniższy.

Przeprowadzanie intensywnej analizy przed zatwierdzeniem każdego zapisu niszczy doświadczenie programisty. Szybkie lintery, które kończą się w mniej niż 5 sekund, są tu na miejscu. Wszystko inne powinno być w CI.

Akcje GitHub: Kompletna konfiguracja robocza

GitHub Actions to najpopularniejsza platforma CI. Poniższy przepływ pracy uruchamia warstwowy proces kontroli jakości: sprawdzanie formatowania, linting, kompilacja, skanowanie bezpieczeństwa SAST i bramka jakości SonarCloud.

jamla

# .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 }}

niska zabudowa

# 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

Kluczowe decyzje dotyczące konfiguracji w tym przepływie pracy:

  • --max-warnings 0 w ESLint: każde ostrzeżenie oznacza błąd kompilacji. Zespoły, które dopuszczają ostrzeżenia, gromadzą je, aż nikt ich nie przeczyta.
  • fetch-depth: 0 przy kasie: SonarCloud potrzebuje pełnej historii git, aby przypisać winę za zgłoszone problemy i poprawnie obliczyć metryki nowego kodu.
  • Semgrep uruchamia się po lint równolegle z Sonarem: skanowanie pod kątem bezpieczeństwa i jakości to niezależne kwestie; ich równoległe uruchamianie skraca całkowity czas trwania procesu.
  • Testy przeprowadzane są z lcov dane wyjściowe dotyczące pokrycia: SonarCloud odczytuje te dane, aby pokazać poziom pokrycia dla każdego pliku i wymusić progi pokrycia w bramce jakości.

GitLab CI/CD: Proces jakości z bramkami jakości

jamla

# .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: blok integruje dane wyjściowe SARIF z Semgrep bezpośrednio z Panelem bezpieczeństwa GitLab, gdzie ustalenia pojawiają się w raporcie bezpieczeństwa żądania scalenia, a nie jako surowe dane wyjściowe dziennika CI.

Jenkins: Deklaratywny potok z SonarQube

Groovy

// 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 To linia krytyczna. Odpytuje SonarQube do momentu zakończenia analizy i kończy kompilację niepowodzeniem, jeśli bramka jakości nie zostanie spełniona. Bez tego potok kończy się, podczas gdy analiza jest nadal wykonywana, a wynik bramki jakości nigdy nie jest wymuszany.

Konfigurowanie bramek jakości, które zespoły rzeczywiście szanują

Bramka jakości, która zawodzi przy każdym zatwierdzeniu, ponieważ progi są ustawione zbyt agresywnie, zostaje wyłączona. Bramka, która nigdy nie zawodzi, ponieważ progi są zbyt liberalne, nie przynosi żadnych korzyści. Celem jest bramka skalibrowana do rzeczywistego profilu ryzyka projektu.

Zalecana konfiguracja początkowa dla nowej bramki jakości SonarQube:

metrycznyStanpróg
Nowe błędyLepszy niż0
Nowe luki w zabezpieczeniachLepszy niż0
Przegląd nowych punktów dostępu do zabezpieczeńMniej niż100%
Nowe pokrycie koduMniej niż80%
Nowe zduplikowane linieLepszy niż3%
Nowe zapachy koduLepszy niż10

Zastosuj te progi do tylko nowy kod (okres „nowego kodu” w SonarQube). Nie stosuj ich do całej bazy kodu w starszym projekcie. Nieudana kompilacja z powodu długu technicznego nagromadzonego przez pięć lat powoduje, że zespoły muszą całkowicie wyłączyć bramkę. Podejście oparte na nowym kodzie pozwala zatrzymać wyciek, a istniejące zadłużenie jest rozwiązywane osobno.

Progi zapadkowe na przestrzeni czasu. Zacznij od konserwatywnych progów, a następnie zacieśniaj je co kwartał, w miarę jak zespół nadrabia zaległości. Bramka, która dziś przechodzi z 75% pokryciem, może zostać zaktualizowana do 80%, gdy poziom bazowy będzie już bezpiecznie powyżej tego poziomu. Stopniowe zacieśnianie jest bardziej zrównoważone niż skokowe dążenie do perfekcji.

Zarządzanie wydajnością analizy statycznej w dużych bazach kodu

Najczęstszym powodem, dla którego zespoły wyłączają lub omijają analizę statyczną w CI, jest to, że spowalnia ona potoki. 15-minutowe skanowanie jakości przy każdym przeniesieniu do gałęzi funkcji blokuje przepływ pracy programistów. Kilka strategii utrzymuje szybkość analizy:

Analiza przyrostowa (tylko nowy kod). SonarQube i większość narzędzi do analizy statycznej dla przedsiębiorstw obsługuje analizę tylko zmienionych plików w żądaniu ściągnięcia, a nie całej bazy kodu. Konfiguruj sonar.pullrequest.base, sonar.pullrequest.branch, sonar.pullrequest.key Parametry umożliwiające skanowanie przyrostowe specyficzne dla PR. Pełna analiza nadal działa po scaleniu z bazą główną; skanowanie PR działa w mniej niż 2 minuty w większości baz kodu.

Buforowanie. Najdroższą częścią wielu analiz statycznych jest pobranie narzędzia i jego definicji reguł. Buforuj plik binarny skanera, bazę danych reguł i wszelkie rozwiązane zależności między przebiegami:

jamla

# 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

Równoległość. Linting, skanowanie bezpieczeństwa i analiza bramek jakościowych to niezależne procesy. Należy je uruchamiać równolegle, a nie sekwencyjnie. Całkowity czas potoku jest określany na podstawie najwolniejszego zadania, a nie sumy wszystkich zadań.

Selektywna aktywacja narzędzi. Nie każde narzędzie musi działać na każdej gałęzi. Pełne skanowanie bezpieczeństwa i sprawdzanie zgodności może być uruchamiane w przypadku żądań ściągnięcia i gałęzi głównej, podczas gdy gałęzie przed scaleniem uruchamiają tylko szybkie lintery. Użyj filtrów gałęzi w konfiguracji CI, aby zastosować różne profile analizy.

Analiza statyczna w potokach, w których priorytetem jest bezpieczeństwo: SAST i skanowanie zależności

Procesy skoncentrowane na bezpieczeństwie dodają dwie warstwy poza standardową analizę jakości: SAST (Static Application Security Testing) pod kątem luk w kodzie aplikacji oraz skanowanie zależności w celu wykrycia znanych luk CVE w bibliotekach innych firm.

Narzędzia SAST (Semgrep, CodeQL, Snyk Code, Checkmarx) analizują, w jaki sposób niezaufane dane przepływają przez kod aplikacji, aby dotrzeć do wrażliwych operacji. Wykrywają one ataki typu SQL injection, XSS, command injection, niebezpieczne deserializacje oraz zakodowane na stałe dane uwierzytelniające, których standardowe narzędzia do analizy nie są w stanie wykryć.

jamla

# 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"

Skanowanie zależności Kontrole package.json, pom.xml, requirements.txti równoważne pliki manifestu w odniesieniu do baz danych luk w zabezpieczeniach:

jamla

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

Warstwowy model bezpieczeństwa uwzględnia szybki, oparty na wzorcach SAST (Semgrep) w każdym żądaniu żądania (PR), co zapewnia natychmiastową informację zwrotną dla programistów, oraz głęboki, semantyczny SAST (CodeQL) w harmonogramie, co zapewnia kompleksową ochronę. Skanowanie zależności jest uruchamiane przy każdej kompilacji, ponieważ nowe luki w zabezpieczeniach (CVE) są publikowane codziennie.

Typowe błędy integracji i jak ich unikać

Analiza jest wykonywana wyłącznie na gałęzi głównej. Wartość analizy statycznej polega na wykrywaniu problemów przed ich scaleniem, a nie po nim. Analiza uruchamiana dopiero po scaleniu służy jako raport, a nie bramka.

Ustawienie bramek jakości tak, aby ostrzegały zamiast sygnalizować awarię. Ostrzeżenie, które nie blokuje przepływu danych, to powiadomienie, które deweloperzy uczą się ignorować. Bramki, które działają prawidłowo, nie budują kultury jakości.

Nie wykluczanie plików testowych z progów pokrycia. Testowanie kodu poprzez testowanie innego kodu generuje zawyżone wskaźniki pokrycia. Wyklucz katalogi testowe z obliczeń pokrycia, aby uzyskać dokładne pomiary pokrycia kodu produkcyjnego.

Ignorowanie zaległości w analizie starszych baz kodów. Włączenie SonarQube po raz pierwszy na bazie kodu liczącej milion wierszy i otrzymanie 40 000 zgłoszeń jest demotywujące i nieproduktywne. Zastosuj podejście oparte na nowym kodzie bazowym: określ datę rozpoczęcia „nowego kodu”, zastosuj bramki tylko do kodu napisanego po tej dacie i potraktuj istniejący backlog jako oddzielny program redukcji długu technicznego.

Niełączenie wyników z komentarzami do żądania ściągnięcia. Wyniki analizy publikowane na oddzielnym pulpicie, który muszą odwiedzić programiści, są ignorowane. Uwzględniono również wyniki analizy publikowane jako komentarze w wierszach, które spowodowały problemy, w interfejsie przeglądu żądań ściągnięcia. Konfiguruj sonar.pullrequest.* parametry integracji GitHub, GitLab lub Bitbucket, dzięki czemu wyniki pojawiają się tam, gdzie przeprowadzany jest przegląd kodu.

W jaki sposób SMART TS XL Integruje się z procesami CI/CD

Większość narzędzi do analizy statycznej w procesach CI/CD rozpoznaje tylko jeden język na raz. ESLint rozpoznaje JavaScript. SonarQube rozpoznaje Javę, Pythona, JavaScript i C#. Żadne z nich nie rozpoznaje COBOL-a, JCL-a, RPG-a ani zależności międzyjęzykowych łączących program wsadowy na komputerze mainframe z mikrousługą Java, która pobiera jego dane wyjściowe, a ta z kolei łączy się z front-endem JavaScript, który je wyświetla.

SMART TS XL Integruje się z procesami CI/CD jako wielojęzykowa warstwa analizy statycznej, obejmująca jednocześnie wszystkie języki w środowisku przedsiębiorstwa. Po modyfikacji usługi Java, SMART TS XL'S analiza wpływu Śledzi graf zależności, aby zidentyfikować programy COBOL, schematy baz danych i usługi podrzędne, których dotyczy dana zmiana, przed jej wdrożeniem. Po zmodyfikowaniu kopii COBOL, identyfikuje ona wszystkie programy w całym portfolio aplikacji, które zawierają tę kopię i wymagają walidacji.

Ta świadomość zależności międzyjęzykowej to zdolność, która sprawia, że SMART TS XLIntegracja CI/CD różni się od dodawania kolejnego języka do SonarQube. Nie chodzi tylko o analizę kodu w zmienionym pliku. Chodzi o zrozumienie, na co jeszcze w systemie wpłynie ta zmiana, czyli o analizę architektoniczną, która określa rzeczywisty zakres zmiany przed jej wprowadzeniem.

Dla zespołów ds. rozwoju przedsiębiorstw działających w oparciu o starsze i nowe stosy, SMART TS XL'S Integracja DevOps Funkcja ta osadza tę analizę strukturalną w procesie przeglądu potoku, zapewniając widoczność architektury, której nie mogą zapewnić narzędzia specyficzne dla danego języka. W rezultacie powstają bramki jakości, które wymuszają przestrzeganie standardów nie tylko w obrębie granic języka, ale w całym systemie, gwarantując, że zmiana w dowolnym komponencie nie spowoduje nieoczekiwanych awarii w komponentach od niego zależnych.

Analiza statyczna jako stały obywatel rurociągu

Zespoły, które poprawnie przeprowadzają analizę statyczną, traktują ją tak samo, jak testowanie: nie jako fazę do przejścia, ale jako ciągłą praktykę wbudowaną w każde zatwierdzenie. Analiza jest przeprowadzana przy każdym żądaniu ściągnięcia (pull request). Wyniki kontroli jakości (Quality Gate) pojawiają się w linii z przeglądem kodu. Progi są zaostrzane w miarę ulepszania bazy kodu. Nowe kategorie analizy są dodawane w miarę wzrostu dojrzałości zespołu w zakresie bezpieczeństwa i jakości.

Szablony konfiguracji potoku przedstawione w tym artykule stanowią punkt wyjścia. Konkretne narzędzia, progi i warunki bramek powinny być skalibrowane do stosu językowego projektu, profilu ryzyka i dojrzałości zespołu. Zasada, która nie powinna ulegać zmianom, to: analiza, która nie stosuje scalania bramek, nie zmienia zachowania, a zachowanie jest tym, co programy jakości kodu próbują zmienić.