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ągu | Co biegać | Budżet opóźnienia | Cel |
|---|---|---|---|
| Wstępne zatwierdzenie (lokalne) | Tylko szybkie lintery (ESLint, Clippy, rustfmt) | Poniżej 5 sekund | Zatrzymaj oczywiste problemy zanim trafią do repozytorium |
| Żądanie ściągnięcia/żądanie scalenia | Pełne skanowanie + SAST + przyrostowe skanowanie SonarQube | Mniej niż 3 minuty | Łączenie bloków w przypadku nowych krytycznych problemów |
| Kompilacja głównej gałęzi | Pełna analiza obejmująca zasięg, duplikację i dług techniczny | Mniej niż 10 minuty | Śledź trendy jakościowe, aktualizuj pulpity nawigacyjne |
| Zaplanowane na noc | Głębokie skanowanie, audyt zależności, sprawdzanie zgodności | Brak budżetu | Wykrywaj 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 0w 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: 0przy 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
lcovdane 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:
| metryczny | Stan | próg |
|---|---|---|
| Nowe błędy | Lepszy niż | 0 |
| Nowe luki w zabezpieczeniach | Lepszy niż | 0 |
| Przegląd nowych punktów dostępu do zabezpieczeń | Mniej niż | 100% |
| Nowe pokrycie kodu | Mniej niż | 80% |
| Nowe zduplikowane linie | Lepszy niż | 3% |
| Nowe zapachy kodu | Lepszy 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ć.