Dlaczego testy bezpieczeństwa muszą trafić do CI/CD
Od „testów na końcu” do ciągłego bezpieczeństwa
Przez lata bezpieczeństwo traktowane było jak kontrola jakości przed wyjazdem samochodu z fabryki: na końcu procesu, raz na jakiś czas, najlepiej kiedy wszystko jest już „gotowe”. Efekt? Gdy zespół bezpieczeństwa znajdował krytyczną podatność, produkt był już praktycznie sprzedany, kampania marketingowa zaplanowana, a klienci czekali. Naprawa oznaczała cofanie zmian, opóźnienia, napięcia między zespołami i gorące maile w stylu „dlaczego dopiero teraz?”.
Model DevSecOps odwraca ten schemat. Bezpieczeństwo staje się elementem codziennego procesu, a nie jednorazowym audytem. Automatyczne testy bezpieczeństwa są uruchamiane przy każdym commicie, merge requeście i przed wdrożeniem. Zamiast jednego wielkiego raportu po kwartale, zespół dostaje małe, konkretne informacje zwrotne na bieżąco. To dokładnie ta sama ewolucja, jaką kilka lat wcześniej przeszły testy jednostkowe i integracyjne.
Celem nie jest „maksymalne bezpieczeństwo za wszelką cenę”, tylko bezpieczeństwo wbudowane w proces wytwórczy. Tak, aby programiści dostawali szybkie komentarze do kodu, zespoły produktowe mogły nadal dostarczać funkcje w swoim tempie, a dział bezpieczeństwa przestał być „tym, który wszystko blokuje”.
Koszt łatania podatności na różnych etapach cyklu życia
Każda luka wykryta później kosztuje wykładniczo więcej. Zespół musi wrócić do dawno zmerge’owanego fragmentu, przypomnieć sobie kontekst, poprawić, przetestować i ponownie wdrożyć. Gdy podatność wychodzi na jaw już po wdrożeniu produkcyjnym, dochodzą koszty incydentu: przestoje, panika użytkowników, obsługa PR, potencjalne kary regulacyjne.
Automatyzacja testów bezpieczeństwa w CI/CD działa jak filtr montowany jak najbliżej źródła problemów – czyli kodu. Wykrycie niewłaściwego użycia funkcji szyfrującej w pull requeście oznacza kilka minut pracy. Wykrycie tego samego problemu po wycieku danych klientów to już tygodnie gaszenia pożaru.
W praktyce można przyjąć prostą zasadę: im później znajdziesz błąd bezpieczeństwa, tym więcej osób musi go dotknąć, aby go naprawić. Automatyzacja skraca tę ścieżkę do relacji: jedna zmiana – jeden autor – jedno poprawienie.
Brak skanów, ręczny audyt czy ciągłe testy – trzy scenariusze
W realnych organizacjach można najczęściej zobaczyć jeden z trzech modeli:
- Brak skanów bezpieczeństwa – poleganie na „doświadczonych programistach” oraz manualnych code review. Działa, dopóki nie pojawi się pierwszy większy incydent.
- Ręczne audyty raz na rok lub przed dużym wydaniem – zewnętrzna firma robi testy penetracyjne, generuje obszerny raport, a zespół przez miesiąc wykonuje zadania z Jiry. Między audytami poziom bezpieczeństwa jest bardziej kwestią wiary niż danych.
- Ciągłe, zautomatyzowane testy – SAST, SCA, skanery tajemnic, częściowo zautomatyzowane DAST, a do tego okresowe manualne testy penetracyjne jako uzupełnienie, a nie główne narzędzie.
Model trzeci nie eliminuje potrzeby eksperckich audytów, ale zmienia ich charakter: zamiast szukać oczywistych błędów, testerzy skupiają się na złożonych scenariuszach, logice biznesowej i łańcuchach ataków trudnych do automatyzacji. Zautomatyzowane testy bezpieczeństwa „odkurzają” codzienne, powtarzalne problemy.
Jak automatyzacja wpływa na relacje Dev–Ops–Security
Największą zmianą nie jest sam fakt uruchamiania skanerów, lecz sposób, w jaki komunikowane i rozwiązywane są problemy. Zamiast bezosobowego maila z działu bezpieczeństwa „blokujemy wdrożenie, bo…”, programista widzi konkretny komentarz w merge requeście z opisem podatności i wskazaniem linii kodu. To inny poziom rozmowy.
Automatyzacja testów bezpieczeństwa w procesie CI/CD wspiera kilka kluczowych zjawisk:
- Odpowiedzialność przesunięta do zespołu produktowego – to zespół deweloperski akceptuje ryzyko, odrzucając lub naprawiając alert.
- Przewidywalność – reguły są spisane w konfiguracji pipeline’ów, a nie w mailach, które każdy interpretuje po swojemu.
- Zaufanie – zespół bezpieczeństwa przestaje być „tajemniczym ministrem”, a staje się dostawcą narzędzi, polityk i wiedzy.
Paradoksalnie im więcej bezpieczeństwa jest zautomatyzowane, tym mniej konfliktów na linii Dev–Sec–Ops. Maszyny są brutalnie konsekwentne i nie obrażają się za odrzucony patch, a człowiek może skupić się na rozmowie o ryzyku, a nie na ręcznym przeklejaniu wyników skanów.

Fundamenty – co znaczy zautomatyzować testy bezpieczeństwa w pipeline’ach
Testy funkcjonalne a testy bezpieczeństwa – co da się zautomatyzować
Testy funkcjonalne odpowiadają na pytanie: czy system robi to, co powinien?. Testy bezpieczeństwa – na inne: czy system robi również to, czego robić nie powinien?. W obu przypadkach część zadań da się w pełni zautomatyzować, a część wymaga pracy eksperta.
Do automatyzacji dobrze nadają się:
- Skanowanie kodu źródłowego (SAST) pod kątem znanych wzorców błędów: SQL injection, XSS, niebezpieczna deserializacja, błędne użycie bibliotek kryptograficznych.
- Skanowanie komponentów (SCA) w poszukiwaniu znanych podatności w bibliotekach open source i komponentach stron trzecich.
- Skanowanie tajemnic – tokeny, klucze API, hasła przypadkowo wrzucone do repozytorium lub logów builda.
- Podstawowe DAST – automatyczne próby wywołania typowych wektorów ataku na działającą aplikację.
- Skanowanie kontenerów i konfiguracji infrastruktury – obrazy Docker, manifesty Kubernetes, pliki Terraform, Ansible.
Manualne testy nadal mają swoje miejsce: złożone testy penetracyjne, ocena specyficznej logiki biznesowej, analiza łańcuchów ataku (np. znalezienie kombinacji drobnych podatności, które razem tworzą poważne zagrożenie). Jednak wszystko, co powtarzalne, deterministyczne i opisane wzorcami ataków, powinno trafić do pipeline’u CI/CD.
Główne kategorie automatycznych testów bezpieczeństwa
Aby sensownie zaplanować automatyzację, trzeba znać podstawowe kategorie narzędzi:
- SAST (Static Application Security Testing) – analizuje kod źródłowy lub artefakty skompilowane bez uruchamiania aplikacji. Dobre do wykrywania błędów, które widać w samej strukturze kodu.
- DAST (Dynamic Application Security Testing) – analizuje działającą aplikację „od zewnątrz”, jak atakujący. Nie widzi kodu źródłowego, ale analizuje odpowiedzi na wysyłane zapytania.
- SCA (Software Composition Analysis) – skupia się na zależnościach: bibliotekach, frameworkach, modułach. Sprawdza wersje pod kątem znanych podatności (CVE), licencji i aktualności.
- Skany tajemnic (secrets scanning) – przeszukują historię Git, aktualne pliki, a czasem także logi i artefakty, w poszukiwaniu kluczy i haseł.
- Skany bezpieczeństwa kontenerów – analizują obrazy pod kątem znanych podatności w pakietach systemowych, błędnych konfiguracji, nadmiernych uprawnień.
- Skany infrastruktury jako kod (IaC scanning) – Terrafrom, CloudFormation, YAML-e Kubernetesa: szukają niebezpiecznych wzorców (np. publiczne S3, otwarte porty, brak szyfrowania).
Każda z tych kategorii ma inne wymagania co do etapu pipeline’u, czasu wykonania, zakresu i poziomu zaufania do wyników. Dobrze zaprojektowany proces CI/CD łączy je w spójną całość, zamiast odpalać wszystkie „jak leci” przy każdym pushu.
Miejsce bezpieczeństwa w typowym pipeline CI/CD
Kiedy spojrzeć na standardowy pipeline, najczęściej pojawiają się w nim etapy: build, test, package, deploy. Automatyzacja testów bezpieczeństwa dokleja do nich dodatkowe kroki:
- Na etapie commit/push – szybkie skany SAST i secrets scanning, często uruchamiane lokalnie (pre-commit hooks) lub przy tworzeniu merge requestu.
- Podczas builda – SCA przy instalowaniu zależności, skanowanie obrazów kontenerów po ich zbudowaniu, skany IaC przed utworzeniem środowiska.
- Po wdrożeniu do środowiska testowego – DAST, skany API, dodatkowe testy integracyjne z perspektywy bezpieczeństwa.
- Po wdrożeniu na produkcję – monitorowanie anomalii, okresowe skany, mechanizmy wykrywania naruszeń.
Nie chodzi o to, aby każdy pipeline zawierał wszystkie możliwe testy. Kluczem jest dopasowanie intensywności i zakresu testów do etapu. Na pre-commit możesz zaakceptować tylko bardzo szybkie kontrole, za to nocny pipeline połączony z pełnymi testami regresyjnymi może odpalić pełny zestaw skanerów.
Shift left i shift right – bezpieczeństwo przed i po wdrożeniu
Często mówi się o shift left, czyli przenoszeniu bezpieczeństwa jak najbliżej fazy developmentu. To prawidłowy kierunek: programista dostaje feedback tuż po napisaniu kodu. Jednak równie istotny jest shift right – włączenie bezpieczeństwa w fazę eksploatacji.
Shift left realizują przede wszystkim:
- pre-commit i pre-push hooks z podstawowymi skanami,
- automatyczny SAST i SCA w pipeline’ach merge requestów,
- lintowanie i skanowanie IaC na gałęzi feature.
Shift right to między innymi:
- regularne DAST i skany API na środowiskach testowych i staging,
- monitorowanie logów i anomalii (np. WAF, SIEM),
- ciągłe skanowanie kontenerów w registry (wykrywanie nowych CVE w już zbudowanych obrazach).
Dojrzały proces DevSecOps łączy te dwa kierunki. Kod jest sprawdzany przed zmerge’owaniem, obrazy są weryfikowane przed wdrożeniem, a działająca aplikacja jest monitorowana po wdrożeniu. Bezpieczeństwo nie jest „punktem kontrolnym”, lecz pętlą zwrotną obejmującą cały cykl życia.
Krok 1 – Zmapowanie ryzyka i wybór krytycznych punktów w procesie CI/CD
Od ogólnego „dbajmy o bezpieczeństwo” do listy konkretnych ryzyk
Hasło „dbajmy o bezpieczeństwo” brzmi pięknie, ale nie da się na jego podstawie zbudować pipeline’u. Potrzebna jest lista konkretnych zagrożeń, wynikająca z charakteru systemu:
- Aplikacja webowa dla klientów końcowych – ryzyko XSS, CSRF, SQLi, wycieku danych osobowych, przejęcia kont.
- API dla partnerów – błędy w autoryzacji, IDOR, nadużycia limitów, brak rate limiting, niepoprawne podpisy.
- Mikroserwisy – komunikacja między usługami, bezpieczeństwo komunikatów, uprawnienia serwis-serwis.
- Monolit on-premise – słabe punkty w integracji z systemami wewnętrznymi, zbyt szerokie uprawnienia, brak izolacji.
- IoT i urządzenia brzegowe – aktualizacje OTA, brak szyfrowania, twardo zakodowane hasła, brak weryfikacji firmware.
Prosty warsztat z zespołem produktowym, architektem i przedstawicielem bezpieczeństwa pozwala zidentyfikować listę „top 10” realnych zagrożeń dla danego systemu. To one powinny determinować, jakie testy bezpieczeństwa trafią do CI/CD w pierwszej kolejności.
Identyfikacja miejsc w pipeline, gdzie wprowadzić testy bezpieczeństwa
Kolejny krok to zmapowanie samego procesu CI/CD. Gdzie kod trafia po commicie? Jak wygląda flow merge requestów? W którym momencie budowane są obrazy, tworzone środowiska, wykonywane testy? Dopiero wtedy można sensownie odpowiedzieć na pytanie, gdzie wpiąć poszczególne skanery.
Typowe punkty integracji to:
- Pre-commit / pre-push – lokalne skrypty, które np. blokują commit z kluczami w plikach konfiguracyjnych.
- Merge Request / Pull Request – SAST, SCA, secrets scanning i podstawowe skany IaC uruchamiane jako joby w pipeline’ach MR.
- Build artefaktów – skanowanie zależności (SCA), skan obrazu kontenera, weryfikacja podpisów komponentów.
- Tworzenie środowiska testowego – skany IaC i konfiguracji, walidacja polityk bezpieczeństwa Kubernetesa.
- Deploy na staging / środowisko efemeryczne – DAST, testy API, testy regresyjne z perspektywy bezpieczeństwa (np. role i uprawnienia).
Definicja poziomów krytyczności i zasad „kiedy blokujemy pipeline”
Sam wybór miejsc w pipeline to za mało. Trzeba jeszcze podjąć kilka niewygodnych decyzji: kiedy build ma się wywalić, a kiedy dopuścić wyjątek i pozwolić zespołowi iść dalej.
Praktyczne podejście to zdefiniowanie prostych, zrozumiałych zasad:
- P1 – krytyczne: zdalne wykonanie kodu, pełny odczyt bazy danych, obejście autoryzacji. Zawsze blokują pipeline, niezależnie od branchy.
- P2 – wysokie: łatwe do wykorzystania luki z istotnym wpływem (np. trwały XSS na panelu admina). Blokują merge do
main/masteri releasów, ale można je warunkowo przepuścić na gałęziach feature z planem naprawy. - P3 – średnie: luki wymagające specyficznych warunków lub o ograniczonym wpływie. Nie blokują pipeline, ale tworzą zadania w backlogu i podpinają się do release notes.
- P4 – niskie / informacyjne: nice to fix, użyte głównie do trendów i edukacji (np. mało istotne „best practices”).
Takie progi trzeba ustalić wspólnie: security, liderzy techniczni, product ownerzy. Inaczej łatwo wpaść w pułapkę: albo pipeline blokowany przez każdą pierdołę, albo zupełnie ignorujący poważne znaleziska.
Mapa ryzyka vs. mapa pipeline’u – priorytety na start
Kiedy lista ryzyk i punkty integracji są już zebrane, dobrze jest je połączyć. Pomaga prosta tabela: w wierszach konkretne ryzyka, w kolumnach etapy CI/CD i typy testów. W każdej komórce pytanie: czy to ryzyko możemy wykryć na tym etapie, tym narzędziem?
Efekt uboczny jest bardzo pożyteczny: wychodzą na jaw ślepe plamki. Przykład: system API z krytyczną autoryzacją, a nigdzie nie ma zaplanowanych testów bezpieczeństwa endpointów na środowisku efemerycznym. Albo intensywne skany DAST, a zupełny brak SCA w czasie builda.
Docelowo powstaje „minimalny zestaw bezpieczeństwa” dla każdego typu serwisu, np.:
- frontend SPA,
- backend REST,
- zadania batch / worker,
- infra / IaC moduły.
Każdy nowy projekt tego typu z automatu dziedziczy gotową listę testów bezpieczeństwa w pipeline’ach. Mniej dyskusji, więcej działania.

Krok 2 – Dobór narzędzi SAST, SCA i skanerów tajemnic pod istniejący stack
Od „co jest najpopularniejsze” do „co faktycznie zadziała u nas”
Łatwo wrzucić do wyszukiwarki frazę „najlepszy SAST” i skończyć z zestawem narzędzi, które są świetne – tyle że dla kogoś innego. Kluczowe pytanie brzmi: jak wygląda Twój stack i proces w praktyce?
Kilka filtrów, które bardzo upraszczają wybór:
- Języki i frameworki – czy narzędzie realnie wspiera np. Spring Boot, NestJS, Django, a nie tylko „Java/JavaScript w ogólności”.
- Integracja z CI – natywne pluginy do GitLab CI, GitHub Actions, Azure DevOps, Jenkins, a może chociaż przyzwoite CLI i gotowe przykłady pipeline’ów.
- Jakość reguł – lepiej mieć mniej języków, ale z dobrą jakością detekcji i mniejszym szumem, niż „obsługę wszystkiego” kosztem setek false positive’ów.
- Model licencjonowania – per-user, per-repo, per-linia kodu, per-agent. Bez tego łatwo utknąć z licencją, której rozliczania nikt nie rozumie.
- Możliwość customizacji – dodawanie własnych reguł, ignorowanie fragmentów kodu, integracja z własnym systemem ticketowym.
Warto też spojrzeć na to, co już jest na rynku wewnątrz firmy. Czasem dział bezpieczeństwa ma narzędzie SAST w innej jednostce – i wystarczy dorzucić kilka pipeline’ów, zamiast organizować kolejne POC.
SAST – jak wybrać i nie utopić się w false positive’ach
Static Application Security Testing bywa pierwszym skanerem, który zespół poznaje… i pierwszym, który wyłącza po tygodniu, gdy po każdym buildzie dostaje 200 ostrzeżeń. Da się tego uniknąć, jeśli odpowiednio zaplanuje się wdrożenie.
Dobre praktyki przy wyborze SAST:
- Test na próbkach własnego kodu – krótkie POC na 2–3 reprezentatywnych repozytoriach. Nie na „hello world”, tylko na realnym, trochę nieidealnym projekcie.
- Sprawdzenie integracji z IDE – feedback w edytorze kodu (VS Code, IntelliJ) bywa ważniejszy niż wyniki w CI. Programiści szybciej poprawią błąd, który widzą „pod ręką”.
- Wsparcie dla frameworków bezpieczeństwa – np. rozpoznawanie własnych wrapperów do sanitizacji, własnych helperów do autoryzacji. Bez tego SAST może traktować bezpieczny kod jako podatny.
- Mechanizm baseline – możliwość uznania istniejących problemów za „stan wyjściowy” i skupienia się w pierwszej fazie na tym, by nie wprowadzać nowych.
Dobrym kompromisem na start jest uruchomienie SAST w trybie nieblokującym na głównym branchu, z wyraźnym progiem dla nowych zmian. Gdy zespół ogarnie istniejący dług bezpieczeństwa, reguły można zaostrzyć.
SCA – zależności, CVE i zarządzanie „szumem aktualizacji”
SCA wydaje się proste: narzędzie skanuje zależności, dopasowuje je do bazy CVE i zgłasza „zaktualizuj do wersji X”. Schody zaczynają się, gdy narzędzie sugeruje upgrade połowy ekosystemu, a deadline na release jest „na wczoraj”.
Kilka praktycznych zasad przy wdrażaniu SCA w CI/CD:
- Priorytet dla komponentów „na brzegu” – biblioteki parsujące wejście użytkownika, warstwa HTTP, ORM, biblioteki JWT. Ich podatności są zazwyczaj groźniejsze niż błąd w pomocniczym module do logowania.
- Polityka minimalnego wsparcia – np. nie dopuszczamy nowych zależności bez wsparcia bezpieczeństwa lub z EOL < X miesięcy.
- Okna serwisowe na aktualizacje – ustalone sprinty lub wydania, w których świadomie „sprzątamy” zależności, zamiast reagować chaotycznie na każde ostrzeżenie z SCA.
- Integracja z repozytorium artefaktów – skanowanie pakietów w Nexus/Artifactory/GitHub Packages, a nie tylko przy okazji builda.
Duże zespoły często dodają jeszcze poziom pośredni: approved dependencies list. Nowa biblioteka trafia najpierw na krótką „kwarantannę” i dopiero po akceptacji jest używana szerzej.
Skanery tajemnic – jak zmniejszyć liczbę „fałszywych alarmów”
Secrets scanning ma tendencję do krzyczenia za każdym razem, gdy zobaczy coś, co przypomina klucz – nawet jeśli to tylko testowy hash w dokumentacji. Po kilku takich sytuacjach łatwo wpaść w tryb ignorowania alertów.
Sensowne wdrożenie skanerów tajemnic opiera się na kilku filarach:
- Pre-commit + CI – lokalny hook chroni przed wrzuceniem tajemnicy w ogóle, CI wyłapuje pozostałe przypadki (np. commit zrobiony bez hooka).
- Whitelist i customowe regexy – możliwość oznaczenia konkretnych wzorców jako bezpieczne, a jednocześnie dodania własnych (np. format kluczy do systemów wewnętrznych).
- Plan rotacji tajemnic – znalezienie klucza w repo to nie tylko „usuń z Gita”. Trzeba mieć prosty sposób na szybką wymianę danego sekretu.
- Skany historii repozytoriów – jednorazowa akcja na start, która może ujawnić „pamiątki z przeszłości” sprzed epoki automatyzacji.
Połączenie tych elementów daje sensowny kompromis: mniej fałszywych alarmów, ale realna ochrona przed sytuacją, w której klucz produkcyjny ląduje jako przykład w README (tak, to się zdarza, i to częściej, niż ktokolwiek by chciał).
Integracja z istniejącym ekosystemem narzędzi
Narzędzia bezpieczeństwa, które żyją w osobnym świecie, szybko lądują w szufladzie. Programista nie będzie regularnie logował się do kolejnego panelu tylko po to, żeby sprawdzić, czy SAST czegoś nie znalazł.
Przy wyborze narzędzi warto sprawdzić:
- Integrację z systemem ticketowym – automatyczne zakładanie zadań w Jira, Azure Boards, YouTrack. Z linkiem do konkretnej linii kodu, a nie ogólnym „w projekcie X jest problem”.
- Integrację z systemem przeglądu kodu – komentarze botów w MR/PR, status checki blokujące merge, raporty w widoku diffu.
- Webhooki i API – do własnych automatyzacji, generowania raportów zbiorczych, integracji z SIEM.
- Ujednolicone raportowanie – formaty takie jak SARIF ułatwiają łączenie wyników z wielu narzędzi w jedno miejsce.
Kierunek jest prosty: wyniki testów bezpieczeństwa powinny pojawiać się tam, gdzie zespół już spędza czas – w MR-ach, w backlogu, w dashboardach CI – zamiast tworzyć kolejny silos.

Krok 3 – Wpięcie testów bezpieczeństwa w pipeline CI (na etapie build i pre-merge)
Strategia „szybko vs. dokładnie” – dwa poziomy pipeline’ów
Jeden pipeline, który „robi wszystko”, zazwyczaj nie działa. Szybko staje się zbyt wolny jak na codzienną pracę i zbyt płytki jak na porządną analizę. Rozsądne podejście to wyraźny podział na:
- pipeline „na bieżąco” (MR/PR) – szybki feedback dla programisty, maksymalny czas trwania: kilka–kilkanaście minut,
- pipeline „głęboki” (nocny / release’owy) – pełniejsze skany, dopuszczalny dłuższy czas trwania, odpalany rzadziej.
Do pierwszego trafiają lekkie skany SAST, SCA, secrets scanning, podstawowe skany IaC. Do drugiego – pełniejsze SAST (z bardziej agresywnymi regułami), skany kontenerów, rozszerzone raporty SCA.
Pre-merge: minimalny zestaw kontroli, który ma realny wpływ
Pipeline dla merge requestów to miejsce, gdzie decyzje są najbardziej „bolesne” – każde dodatkowe 5 minut w pipeline’ie to realny czas oczekiwania na review. Zestaw testów musi być zatem krótki, ale konkretny.
Praktyczny minimalny pakiet bezpieczeństwa na MR/PR obejmuje zwykle:
- lekki SAST ograniczony do zmienionych plików (diff-based scanning),
- SCA wywoływane przy zmianie zależności (np. modyfikacja
package.json,pom.xml,requirements.txt), - secrets scanning na diffie,
- skan IaC dla zmienionych manifestów Kubernetes, Terraform itp.
Dobrze działa też podejście „progressive hardening”: na początku nie blokujemy merge requestów, tylko oznaczamy je jako „warning” i budujemy świadomość. Po kilku sprintach, gdy zespół się przyzwyczai, włączamy twardsze zasady blokowania przy krytycznych znalezieniach.
Build: gdzie wcisnąć skany, żeby nie zabić prędkości
Etap build to naturalne miejsce na SCA, skany obrazów kontenerów i część SAST. Zwykle i tak instalujemy zależności, kompilujemy kod i budujemy artefakty – dzięki temu można wykorzystać już pobrane pakiety i metadane.
Typowa sekwencja w pipeline buildowym może wyglądać tak:
- Instalacja zależności / kompilacja.
- Uruchomienie SCA na katalogu z zależnościami.
- Budowa obrazu kontenera.
- Skany obrazu (bazowe pakiety, konfiguracja, uprawnienia użytkownika w kontenerze).
- Opcjonalny SAST na całym projekcie, jeśli czas wykonania jest akceptowalny.
Skany, które trwają dłużej, można zrównoleglić lub uruchamiać warunkowo, np. tylko dla głównego branchu, tagów releasowych lub commitów oznaczonych konkretnym tagiem CI.
Przykładowa konfiguracja dla GitLab CI
Jako prosty przykład – fragment .gitlab-ci.yml, który dodaje podstawowe testy do pipeline’ów MR i buildów:
stages:
- test
- security
sast:
stage: security
image: registry.example.com/security/sast:latest
script:
- sast-scan --path . --changed-only
rules:
- if: '$CI_MERGE_REQUEST_ID'
sca:
stage: security
image: registry.example.com/security/sca:latest
script:
- sca-scan --manifest-path=.
rules:
- changes:
- package.json
- package-lock.json
- pom.xml
- requirements.txt
secrets_scan:
stage: security
image: registry.example.
secrets_scan:
stage: security
image: registry.example.com/security/secrets:latest
script:
- secrets-scan --diff
rules:
- if: '$CI_MERGE_REQUEST_ID'
container_scan:
stage: security
image: registry.example.com/security/container-scanner:latest
script:
- container-scan --image "$CI_REGISTRY_IMAGE:$CI_COMMIT_SHA"
needs: ["build"]
rules:
- if: '$CI_COMMIT_BRANCH == "main"'
Takie podejście nie jest idealne, ale daje sensowny balans: szybkie MR-y, a jednocześnie stały strumień informacji o bezpieczeństwie przy buildach głównego branchu i release’ach.
Krok 4 – Automatyzacja testów DAST, API i bezpieczeństwa środowisk testowych
Dlaczego DAST i testy API nie lubią się z „gołym” CI
SAST i SCA obejdą się bez działającej aplikacji, ale DAST i testy API już nie. Narzędzie musi mieć gdzie uderzyć – aplikacja musi działać, mieć stabilny adres i znany stan. Stąd klasyczny problem: „u mnie na pipeline’ie działało, ale skaner DAST nie widzi aplikacji”.
Żeby to miało sens, trzeba połączyć trzy elementy:
- środowisko, na którym da się bezpiecznie „atakować” aplikację,
- spójny sposób uruchamiania aplikacji wraz z zależnościami (baza, kolejki, cache),
- automatyczny „cleanup”, żeby testy bezpieczeństwa nie zaśmiecały środowisk.
Bez tego DAST kończy jako ręczne ćwiczenie raz na kwartał, a nie integralny etap pipeline’u.
Ephemeral environments – sandbox do testów bezpieczeństwa
Najwygodniejszym rozwiązaniem są tzw. ephemeral environments – tymczasowe środowiska tworzone na potrzeby konkretnego MR-a lub builda. Pipeline je podnosi, skanuje, a potem usuwa. Do tego nie trzeba osobnego klastra za milion monet; często wystarczy rozsądnie skonfigurowany Kubernetes albo docker-compose na dedykowanych runnerach.
Typowy przepływ wygląda następująco:
- Pipeline buduje obraz(y) aplikacji.
- Tworzy tymczasowe środowisko (namespace w Kubernetes, stack docker-compose, środowisko w platformie preview).
- Wykonuje sanity check (czy endpoint health-check odpowiada).
- Odpala skaner DAST / API przeciwko temu środowisku.
- Zbiera raport, publikuje go w CI i/lub jako artefakt.
- Usuwa środowisko.
Jeżeli przy tym punkcie pojawia się w głowie myśl „ale to skomplikowane”, to dobra wiadomość jest taka, że wiele systemów CI/CD (np. GitLab, GitHub + Argo CD, Jenkins + Helm) ma gotowe wzorce na środowiska tymczasowe. Trzeba je tylko lekko „podkręcić” pod testy bezpieczeństwa.
DAST – jak nie zalać zespołu fałszywymi pozytywami
Klasyczny DAST ma opinię hałaśliwego i wolnego. Zamiast odpalać pełny „crawl całej aplikacji na 1000 wątków”, praktyczniej jest podejść do tematu warstwowo.
Sprawdza się m.in. taki podział:
- krótkie skany smoke – dla każdego builda głównego branchu; kilka krytycznych testów (np. proste XSS, SQLi na głównych endpointach), czas wykonania w minutach,
- pełne skany – nocne lub tygodniowe; włączony intensywny crawling, rozszerzone reguły, więcej wektorów ataku.
Oprócz samego podziału czasowego, istotna jest konfiguracja zakresu:
- jasne ograniczenie domen/hostów (żeby skaner nie „podróżował” po wszystkich linkach z aplikacji),
- lista ścieżek do wykluczenia (logout, zmiana hasła, krytyczne operacje administracyjne),
- bezpieczna liczba równoległych requestów, żeby nie DoS-ować sobie testowych środowisk.
Jeśli zespół dev ma w pamięci obraz, jak skaner testowy przypadkowo zaspamował system mailingowy tysiącami powiadomień, chęć do automatyzacji drastycznie maleje. Dlatego lepiej zacząć ostrożnie, a dopiero z czasem „odkręcać” intensywność skanów.
Testy API – kontrakty, specyfikacje i bezpieczeństwo
API jest wygodnym celem ataków: stabilne, dobrze udokumentowane, przewidywalne adresy. Z punktu widzenia automatyzacji to akurat zaleta. Mając specyfikację OpenAPI/Swagger, można w dużej mierze wygenerować scenariusze testowe dla narzędzi bezpieczeństwa.
Praktyczny przepis na start:
- Utrzymuj aktualną specyfikację OpenAPI jako część repo (wersjonowaną wraz z kodem).
- W pipeline CI uruchamiaj testy kontraktowe (czy API nie łamie własnej specyfikacji).
- Na tym samym etapie generuj scenariusze dla testów bezpieczeństwa API (fuzzing, autoryzacja, brak walidacji).
Kilka rodzajów testów API, które dobrze się automatyzują:
- testy autoryzacji poziomej i pionowej – próba wykonania requestów jednym typem tokena do endpointów dla innej roli,
- fuzzing parametrów – podstawianie nietypowych wartości, znaków specjalnych, payloadów XSS/SQLi do parametrów i ciał requestów,
- sprawdzenie „open endpoints” – porównanie listy endpointów z wymaganym uwierzytelnianiem kontra te w rzeczywistości dostępne bez tokena.
Nawet proste narzędzia bazujące na OpenAPI potrafią wychwycić popularne błędy, np. brak walidacji w inputach przy krytycznych operacjach (reset hasła, zarządzanie uprawnieniami).
Konfiguracja DAST/API w pipeline – przykład struktury
Jako uproszczony przykład, fragment konfiguracji w stylu GitLab CI (analogiczną logikę da się przenieść do GitHub Actions, Azure Pipelines czy Jenkinsfile):
stages:
- build
- deploy_review
- security_dynamic
deploy_review:
stage: deploy_review
script:
- ./deploy-preview.sh # tworzy środowisko tymczasowe
environment:
name: review/$CI_COMMIT_SHORT_SHA
url: https://review-$CI_COMMIT_SHORT_SHA.apps.example.com
rules:
- if: '$CI_COMMIT_BRANCH == "main"'
dast_smoke:
stage: security_dynamic
image: registry.example.com/security/dast:latest
script:
- dast-scan --target "$CI_ENVIRONMENT_URL" --profile smoke
needs: ["deploy_review"]
rules:
- if: '$CI_COMMIT_BRANCH == "main"'
api_security:
stage: security_dynamic
image: registry.example.com/security/api-scanner:latest
script:
- api-scan --target "$CI_ENVIRONMENT_URL" --openapi ./openapi.yaml
needs: ["deploy_review"]
rules:
- if: '$CI_COMMIT_BRANCH == "main"'
W bardziej rozbudowanej wersji można dodać osobny job na pełny DAST raz dziennie, powiązany z tym samym mechanizmem tworzenia środowisk, ale odpalany np. z harmonogramu w systemie CI.
Bezpieczeństwo środowisk testowych – nie tylko aplikacja
Automatyzacja testów bezpieczeństwa często skupia się na samej aplikacji, a pomija otoczenie: konfigurację serwera, nagłówki bezpieczeństwa, TLS, dostępność paneli administracyjnych. Tymczasem to właśnie na środowiskach testowych najczęściej obowiązuje luźniejsze podejście („przecież to tylko staging”).
Do pipeline’ów można wpiąć kilka prostych, ale skutecznych kontroli:
- skany konfiguracji serwera – narzędzia sprawdzające nagłówki bezpieczeństwa (CSP, HSTS, X-Frame-Options), przekierowania HTTP→HTTPS,
- sprawdzenie certyfikatów TLS – długość klucza, czas ważności, łańcuch zaufania,
- kontrola otwartych portów przy użyciu lekkich skanerów sieciowych uruchamianych z CI przeciwko środowisku testowemu,
- weryfikacja braku „debug mode” – flagi debugowe, verbose logging, endpointy diagnostyczne dostępne z zewnątrz.
Pierwszy automatyczny test, który wykryje włączony stack trace z pełnym logiem zapytania SQL na stagingu, zazwyczaj skuteczniej przekonuje zespół niż godzinny wykład o „best practices OWASP”.
Plan eskalacji i reagowania na wyniki DAST/API
DAST i testy API uwielbiają produkować raporty. Kluczowe jest z góry ustalone, co z tymi raportami się dzieje, żeby nie skończyły jako „kiedyś to przejrzymy”.
Sprawdza się prosta siatka decyzyjna:
- krytyczne znalezienia (np. remote code execution, pełny SQL injection, brak autoryzacji przy modyfikacji danych) – pipeline fail, automatyczne utworzenie ticketu z najwyższym priorytetem,
- wysokie ryzyko – ticket + wymagany przegląd w następnym sprincie; możliwość warunkowego obejścia z akceptacją właściciela systemu,
- średnie i niskie – raport zbiorczy, cykliczne „sprzątanie” w dedykowanym sprincie bezpieczeństwa.
Dobrym nawykiem jest też oznaczanie znanych, zaakceptowanych ryzyk w narzędziu DAST/API jako „baseline” – żeby kolejne skany nie generowały ponownie tych samych alertów. Inaczej po kilku tygodniach raport będzie wyglądał jak powtórka starego odcinka serialu: niby nowy dzień, ale fabuła ta sama.
Automatyzacja bezpieczeństwa a dane testowe
Testy bezpieczeństwa, szczególnie DAST i fuzzing API, potrafią generować nietypowe dane: długie ciągi znaków, nietypowe encje, losowe wartości. Jeśli w środowisku testowym znajdują się prawdziwe dane klientów, problem zaczyna się robić poważny.
Kilka zasad, które chronią przed kłopotami:
- brak danych produkcyjnych w środowiskach, na których odpalany jest DAST/fuzzing,
- proces anonimizacji/masking danych, jeśli dane są kopiowane z produkcji,
- czytelne tagowanie baz (prod/stage/test) i mechanizmy uniemożliwiające przypadkowe skierowanie skanów na produkcję.
W praktyce często pojawia się sytuacja, w której staging to kopia produkcji „z wczoraj”. W takim scenariuszu testy dynamiczne należy ograniczyć, a dane najpierw zanonimizować, zamiast liczyć, że „jakoś to będzie”.
Łączenie wyników DAST i testów API z resztą ekosystemu
Wyniki DAST i testów API powinny trafić w to samo miejsce, gdzie lądują raporty SAST/SCA. Im mniej osobnych dashboardów, tym większa szansa, że ktoś faktycznie będzie je przeglądał.
Kilka prostych sposobów na integrację:
- eksport raportów do ujednoliconego formatu (np. SARIF) i agregacja w jednym narzędziu,
- automatyczne dodawanie komentarzy w MR/PR, jeśli DAST/API wykryły regresję względem poprzedniego skanu,
- widok trendów (np. liczba krytycznych znalezisk w czasie), który pokazuje, czy automatyzacja faktycznie poprawia sytuację, czy tylko produkuje ładne raporty.
Dobre narzędzie do agregacji wyników potrafi też wychwycić korelacje: np. nowa wersja biblioteki uwierzytelniania → nagły wzrost błędów autoryzacji w testach API. Takie „łączenie kropek” to moment, kiedy automatyzacja zaczyna realnie pomagać, a nie tylko generować artefakty w CI.
Najczęściej zadawane pytania (FAQ)
Jak zacząć automatyzować testy bezpieczeństwa w CI/CD bez rozwalania pipeline’ów?
Podejdź do tego iteracyjnie. Najpierw dodaj najszybsze i najmniej inwazyjne skany: SAST i skanowanie tajemnic uruchamiane przy każdym commicie lub merge requeście. Na początku ustaw je w trybie „report only” – nie blokują builda, tylko dodają komentarze do MR-a lub generują raport.
Kolejny krok to dołożenie SCA (analiza zależności) oraz skanów kontenerów na etapie build/package. Dopiero gdy zespół oswoi się z wynikami i fałszywymi alarmami, włącz twardsze zasady: np. blokowanie pipeline’u przy krytycznych podatnościach. Dzięki temu nie ma efektu „bezpieczeństwo wyłączyło nam dostarczanie”.
Jakie testy bezpieczeństwa najbardziej opłaca się zautomatyzować w CI/CD?
Najwięcej zwrotu z inwestycji dają testy, które są powtarzalne i oparte na znanych wzorcach błędów. W praktyce oznacza to przede wszystkim:
- SAST – skanowanie kodu pod kątem typowych błędów (SQLi, XSS, niebezpieczna deserializacja, złe użycie kryptografii),
- SCA – analiza bibliotek i frameworków pod kątem znanych CVE i przestarzałych wersji,
- skanowanie tajemnic – wykrywanie kluczy API, haseł i tokenów w repozytorium oraz logach builda,
- skany kontenerów i IaC – obrazy Docker, manifesty K8s, Terraform, CloudFormation.
DAST również ma sens, ale zwykle częściowo zautomatyzowany i z mniejszą częstotliwością (np. na środowisku testowym po udanym deployu). Ręczne testy penetracyjne zostają jako uzupełnienie, a nie główne „narzędzie do wszystkiego”.
Na jakim etapie pipeline’u CI/CD uruchamiać testy bezpieczeństwa?
Najprostsza praktyka to rozłożyć testy w zależności od czasu wykonania i wpływu na build:
- Commit / push / merge request – szybkie SAST i skanowanie tajemnic, aby złapać oczywiste błędy jak najbliżej kodu.
- Build / test – SCA, skan obrazów kontenerów, podstawowe DAST na środowisku testowym.
- Przed deployem na produkcję – pełniejsze skany kontenerów, IaC, polityki bezpieczeństwa dla chmury.
Nie ma sensu odpalać wszystkich możliwych skanerów przy każdym pushu. Lepiej podzielić je na „szybkie i częste” oraz „wolne i okresowe” (np. nocne joby lub joby on demand).
Jak uniknąć zalania zespołu fałszywymi alarmami z narzędzi bezpieczeństwa?
Po wdrożeniu pierwszych skanerów zwykle pojawia się efekt „choinki” – wszystko świeci na czerwono. Kluczem jest kalibracja: konfiguracja reguł, wyciszenie oczywiście fałszywych trafień oraz ustalenie progów ryzyka, które faktycznie blokują pipeline.
Praktyczne podejście to: na początku traktować wyniki jako rekomendacje, tagować typowe false positive’y, a dopiero po kilku tygodniach analizy włączyć „twarde” blokowanie tylko dla wybranych kategorii (np. krytyczne i wysokie podatności z potwierdzonym exploitem). Dzięki temu bezpieczeństwo nie staje się wrogiem zespołu, tylko kolejnym automatycznym code review.
Czy automatyzacja testów bezpieczeństwa w CI/CD może zastąpić audyty i testy penetracyjne?
Automatyzacja nie zastępuje audytów, tylko zmienia ich rolę. Skanery bardzo dobrze radzą sobie z powtarzalnymi, znanymi problemami. Dzięki temu na okresowych testach penetracyjnych eksperci nie tracą czasu na „klasyki”, tylko skupiają się na logice biznesowej, łańcuchach ataków i mniej oczywistych scenariuszach.
W praktyce dojrzałe organizacje działają w modelu mieszanym: ciągłe zautomatyzowane testy (SAST, SCA, DAST, skany kontenerów, IaC) + regularne, ale krótsze i bardziej precyzyjne testy manualne. Efekt uboczny: mniej grubych raportów „do Jiry na miesiąc” i więcej konkretnych, priorytetyzowanych zadań.
Jak przekonać zespół deweloperski, że automatyczne testy bezpieczeństwa w CI/CD im pomagają?
Deweloperów przekonują głównie dwie rzeczy: mniejsza liczba „pożarów” na produkcji i szybki, konkretny feedback. Zamiast maila z działu bezpieczeństwa „blokujemy release”, programista widzi komentarz w merge requeście z linią kodu i opisem podatności. To oszczędza czas i nerwy obu stron.
Dobrym argumentem jest też koszt naprawy: błąd złapany w MR to kilka minut pracy, ta sama luka po incydencie bezpieczeństwa oznacza tygodnie gaszenia pożaru, stres, PR i możliwe kary. Automatyzacja skraca ścieżkę: jedna zmiana – jeden autor – jedno poprawienie. Mniej polityki, więcej techniki.






