TL;DR

  • 11 września 2026 wszedł w życie pierwszy kluczowy obowiązek z CRA: zgłoszenie aktywnie wykorzystywanej podatności w ciągu 24 godzin od uzyskania wiedzy. Większość pozostałych obowiązków zacznie obowiązywać 11 grudnia 2027.
  • Z Annex I zostają cztery obowiązki techniczne, które da się zautomatyzować: SBOM, skan zależności przy każdym pull requeście, cykliczny skan tego, co działa u klientów, i osobny alert dla podatności z exploitem.
  • Na Jenkinsie załatwia to wtyczka vuln-today-jenkins-plugin i jedna linijka w Jenkinsfile: vulnScan file: 'package-lock.json'.
  • Lockfile i sbom.cdx.json w repozytorium to dokumentacja komponentów każdej wersji. Git pamięta historię, więc pytanie „co było w wersji z marca” to jedna komenda.
  • Pipeline nie zastąpi polityki skoordynowanego ujawniania, pliku security.txt ani osoby, która po alercie ocenia, czy to zgłoszenie do CSIRT.

Zanim prawnicy się obudzą

11 września 2026 wszedł w życie pierwszy kluczowy obowiązek z CRA, czyli obowiązek zgłaszania aktywnie wykorzystywanych podatności w ciągu 24 godzin. Prędzej czy później ktoś z działu prawnego przyjdzie do zespołu z trzema pytaniami: jakie komponenty są w wersji produktu, którą klienci mają dziś (i którą mieli rok temu), czy któryś z nich ma znaną, wykorzystywaną podatność, i kto o tym wie.

Dobra wiadomość: na wszystkie trzy pytania może odpowiadać pipeline. Poniżej pokazujemy, jak zrobiliśmy to na Jenkinsie z vuln.today, polskim narzędziem do analizy podatności stworzonym przez naszego inżyniera bezpieczeństwa.

Czego CRA wymaga od twórców oprogramowania

Rozporządzenie weszło w życie 10 grudnia 2024. Większość obowiązków zaczyna obowiązywać 11 grudnia 2027, ale raportowanie podatności i incydentów (art. 14) wystartowało już 11 września 2026. Producent, który dowiaduje się, że podatność w jego produkcie jest aktywnie wykorzystywana, zgłasza to do ENISA i krajowego CSIRT przez nową platformę raportowania.

Zgłoszenie ma trzy etapy:

  • wczesne ostrzeżenie w 24 godziny od uzyskania wiedzy (co i w jakim produkcie),
  • pełniejsze powiadomienie w 72 godziny (charakter podatności i exploita, podjęte lub dostępne środki zaradcze),
  • raport końcowy 14 dni po udostępnieniu poprawki.

Niezależnie od zgłoszenia producent informuje klientów, których ta konkretna podatność dotyczy, o problemie i dostępnych środkach zaradczych. Uspokajamy: nie chodzi o publiczny komunikat o CVE w zależnościach. Obowiązek dotyczy tylko podatności aktywnie wykorzystywanej w Waszym produkcie, a wytyczne Komisji pozwalają ograniczyć szczegóły do dotkniętych klientów, gdy publiczne ujawnienie zwiększałoby ryzyko. Nie chcemy przecież podawać atakującym informacji o podatnościach w naszych produktach. Co istotne, obowiązek obejmuje też produkty wprowadzone na rynek przed 11 grudnia 2027.

24 godziny na wczesne ostrzeżenie od uzyskania wiedzy o aktywnie wykorzystywanej podatności. Potem 72 godziny na powiadomienie i 14 dni po udostępnieniu poprawki na raport końcowy.

Annex I część II opisuje, w jaki sposób producent ma obsługiwać podatności. Po odfiltrowaniu obowiązków czysto procesowych (polityka CVD, kontakt do zgłaszania, publikowanie advisory) zostają cztery, które można i warto zautomatyzować.

Cztery wymagania, które warto zautomatyzować

Wymóg CRACo to technicznie znaczyJak to robimy
SBOM w formacie maszynowo czytelnym, co najmniej zależności najwyższego poziomu, przechowywany z dokumentacją technicznąSpis komponentów dla każdej wersji, która była u klientów, dostępny na żądanie przez lataLockfile i SBOM w repozytorium, historia w git
Identyfikować i dokumentować podatności w komponentachSkan drzewa zależnościSkan lockfile przy każdym pull requeście
Regularnie testować i przeglądać bezpieczeństwo produktuSkan tego, co jest wdrożone, bez czekania na zmianę w kodzieSkan dzienny gałęzi lub tagu produkcyjnego
Zgłaszać aktywnie wykorzystywane podatności w 24 godziny (art. 14)Sygnał KEV lub exploit musi natychmiast trafić do konkretnej osobyOsobny kanał Slack dla podatności z exploitem

Zanim ktoś zamknie tekst z myślą „ale my przecież tworzymy SaaS, to nas nie dotyczy”: CRA obejmuje produkty udostępniane na rynku, czyli oprogramowanie dostarczane klientowi. Czysty SaaS co do zasady podlega NIS2, a nie CRA. Ale aplikacja mobilna, biblioteka, wtyczka, agent czy instalacja on-prem dla takiego produktu - już tak. Wiele firm ma w portfolio przynajmniej jedną taką rzecz, a wymagania wobec zależności są pod NIS2 identyczne.

Wdrożenie na Jenkinsie

Całość opiera się na naszej wtyczce do Jenkinsa vuln-today-jenkins-plugin (kod na GitHubie) i API vuln.today. Wtyczka dodaje krok vulnScan, który wysyła wskazany lockfile do API, odbiera listę podatności i wysyła powiadomienia. Sam skan wykonuje się na agencie, na którym działa build. Wtyczka powstała w duchu minimalizmu: na agentach nie trzeba instalować żadnych skanerów, curl ani jq, a kontroler Jenkinsa nie przetwarza plików z repozytoriów. Na kontrolerze zostaje tylko to, co powinno się tam znajdować: credentiale i stan powiadomień, czyli które CVE zostały już zgłoszone dla której aplikacji i wersji, żeby nie generować duplikatów.

Jednorazowa konfiguracja:

  1. Instalacja wtyczki (.hpi) w Manage Jenkins, Plugins.
  2. Dwa credentiale typu Secret text: vuln-today-api-key i vuln-today-slack-webhook. Opcjonalnie trzeci, vuln-today-slack-webhook-critical, dla kanału, na który trafiają podatności z exploitem.

Potem w każdym projekcie jedna linijka:

pipeline {
  agent any
  stages {
    stage('Dependency Scan') {
      when { changeRequest() }
      steps {
        vulnScan file: 'package-lock.json'
      }
    }
  }
}

Podajemy ścieżkę do lockfile i nic więcej. vuln.today samo rozpoznaje format (npm, Composer, pip, Go, Maven, Gradle, Cargo, Bundler, a także SBOM w CycloneDX i SPDX). Wynik wraca w kilka sekund, wtyczka agreguje go per paczka i wysyła powiadomienie. To pokrywa drugi wymóg: każda zmiana zależności jest sprawdzona, zanim trafi do gałęzi głównej.

SBOM: jedno źródło prawdy, czyli repozytorium

Pierwszy wymóg jest w większości spełniony, zanim cokolwiek zrobimy. Plik package-lock.json to maszynowo czytelny spis wszystkich komponentów, łącznie z zależnościami przechodnimi, wersjami i sumami kontrolnymi. Leży w repozytorium, a git pamięta każdą jego wersję. Pytanie organu nadzoru „co było w wersji wdrożonej w marcu 2027” to zwykła komenda git show <commit>:package-lock.json. Nie potrzeba do tego tagów, osobnego artefaktu ani zewnętrznego narzędzia. Wystarczy git.

SBOM jest wymieniony wprost w Annex VII jako element dokumentacji technicznej, a tę producent powinien przechowywać przez 10 lat od wprowadzenia produktu na rynek albo przez okres wsparcia, jeśli jest dłuższy. W praktyce sprowadza się to do nieusuwania repozytorium.

Jedna rzecz jest na ten moment niepewna: rozporządzenie mówi o „powszechnie używanym formacie”, a Komisja może go doprecyzować aktem wykonawczym. Takiego aktu na dziś nie ma. Najczęściej przywoływanym punktem odniesienia jest niemiecka wytyczna BSI TR-03183-2 wskazująca CycloneDX lub SPDX i nie wiadomo, czy lockfile npm zostanie uznany za wystarczający. Dlatego obok lockfile trzymamy w repozytorium sbom.cdx.json, generowany w pipeline na gałęzi głównej i commitowany, gdy się zmienił:

stage('SBOM') {
  when { branch 'master' }
  steps {
    sh '''
      npm sbom --sbom-format cyclonedx --omit dev > sbom.cdx.json
      git diff --quiet sbom.cdx.json || {
        git commit -m "chore: update SBOM [skip ci]" sbom.cdx.json
        git push origin HEAD:master
      }
    '''
  }
}

Komenda npm sbom jest wbudowana w npm od wersji 9.5, a dla Composera (PHP) jest cyclonedx-php-composer. SBOM żyje obok lockfile, ma tę samą historię i ten sam commit co kod, który opisuje. Deweloper nie musi pamiętać ani o nim, ani o jego generowaniu.

Skan dzienny tego, co jest wdrożone

To trzeci wymóg. Nowe CVE nie czekają na pull request: produkt, którego nikt nie dotykał od pół roku, może dostać CVE z exploitem dziś. Dlatego skan dzienny musi dotyczyć tego, co faktycznie działa u klientów, a nie tego, co dopiero wydamy albo właśnie wydaliśmy. Innymi słowy nie wystarczy zeskanować oprogramowania raz przy wydaniu i zapomnieć. Trzeba je skanować cyklicznie.

W większości projektów produkcja to ostatni build gałęzi głównej. W takim wypadku wystarczy job z cronem na tej gałęzi:

pipeline {
  agent any
  triggers { cron('H 6 * * *') }
  stages {
    stage('Scan deployed release') {
      steps {
        vulnScan file: 'package-lock.json'
      }
    }
  }
}

Jeśli klienci on-prem siedzą na starszej wersji albo utrzymujecie kilka linii wsparcia, ten sam job robi checkout odpowiedniego tagu i wywołuje vulnScan file: 'package-lock.json', version: TAG. Parametr version daje osobny stan każdej wersji: to samo CVE naprawione w 3.0.0 nadal wraca jako otwarte dla 2.3.1, zamiast zniknąć w deduplikacji.

Alert, który rozróżnia „zaktualizować” od „zgłosić”

Czwarty wymóg, najważniejszy od 11 września. Obowiązek zgłoszenia dotyczy podatności aktywnie wykorzystywanej, a nie każdej z wysokim CVSS. CVE 9.8 bez znanego exploita to pilna aktualizacja. CVE 6.5 z katalogu CISA KEV, w wersji, którą klient ma na produkcji, to potencjalnie start 24-godzinnego zegara. Gdyby oba wyglądały na Slacku tak samo, to drugie utonęłoby w środku listy, bo ma niższy wynik, a w praktyce jest ważniejsze pod względem prawnym.

vuln.today zwraca przy każdej podatności nie tylko CVSS, ale też epss_score, flagę kev (CISA Known Exploited Vulnerabilities) i flagę poc (publicznie dostępny exploit). Wtyczka rozdziela na tej podstawie powiadomienia na dwie klasy:

  • Nowe CVE w zależnościach: kanał zespołu, paczki posortowane po CVSS, wersja naprawiona, gotowa komenda npm install, linki. To samo CVE może wrócić po 7 dniach jako przypomnienie.
  • Podatność wykorzystywana lub z exploitem (kev == true albo poc == true): osobny kanał, bez deduplikacji, z nazwą wersji i wzmianką osoby odpowiedzialnej za ocenę.

Drugi alert nie jest jeszcze zgłoszeniem. Fakt, że biblioteka ma publiczny exploit, nie znaczy automatycznie, że nasz produkt jest podatny: może nie używać tej funkcji, może mieć mitygację. Ale to jest moment, w którym ktoś musi tej oceny dokonać i ją udokumentować. Wytyczne Komisji mówią wprost: producent „uzyskuje wiedzę” dopiero wtedy, gdy po wstępnej ocenie ma uzasadnioną pewność, że podatność w jego produkcie jest aktywnie wykorzystywana. Tę wstępną ocenę ma jednak wykonać niezwłocznie.

Czego pipeline nie załatwi

Pipeline odpowiada na pytania „co mamy” i „co jest podatne”. Trzy rzeczy z Annex I są poza zasięgiem skanera:

  • publiczny kontakt do zgłaszania podatności (/.well-known/security.txt) i polityka skoordynowanego ujawniania,
  • szablon advisory i miejsce, w którym publikujecie informacje o usuniętych podatnościach (Annex I cz. II pkt 4 wymaga tego dopiero po udostępnieniu poprawki, więc to zwykłe security release notes, a nie ostrzeżenie przed łatką),
  • wyznaczona osoba, która po alercie z kanału krytycznego ocenia, czy to zgłoszenie do CSIRT, i miejsce, w którym zapisuje uzasadnienie.

Wyznaczona osoba jest ogniwem między pipeline a prawnikami. Wskazujemy ją po to, aby w dniu, w którym zegar ruszy naprawdę, nikt nie pytał „kto się tym zajmuje?”.

Podsumowanie

Cztery obowiązki techniczne z CRA to jedna wtyczka i kilka linijek w Jenkinsfile:

  • vulnScan na pull requestach: żadna podatna zależność nie wchodzi do gałęzi głównej niezauważona.
  • vulnScan cyklicznie na gałęzi lub tagu produkcyjnym: wdrożone wersje są sprawdzane wobec CVE opublikowanych wczoraj.
  • Lockfile i sbom.cdx.json w repozytorium: dokumentacja komponentów każdej wersji, z historią w git, u samego źródła.
  • Oddzielne kanały Slack: aktualizacje osobno, potencjalne zgłoszenia osobno, z odpowiedzialną osobą.

Niezależnie od tego, czy skanujecie przez vuln.today, Trivy, Grype czy OSV-Scanner i czy raportujecie na Slacku czy Mattermost, warto sprawdzić u siebie te same cztery rzeczy: czy skan widzi lockfile, a nie manifest; czy uruchamia się bez zmiany w kodzie; czy dotyczy wersji, które klienci mają; i czy sygnał „exploit dostępny” wygląda inaczej niż zwykłe CVE. Jeśli tak, prawnicy mogą się obudzić, kiedy tylko chcą. Odpowiedzi będą na nich czekać w repozytorium i w Jenkinsie.

Źródła

  • Rozporządzenie (UE) 2024/2847 (Cyber Resilience Act), tekst w EUR-Lex: eur-lex.europa.eu
  • CRA, art. 13: obowiązki producentów, w tym SBOM i dokumentacja techniczna
  • CRA, art. 14: obowiązki raportowe producentów
  • CRA, art. 71: wejście w życie i stosowanie
  • CRA, Annex VII: zawartość dokumentacji technicznej
  • Komisja Europejska: wytyczne do stosowania CRA, C(2026) 5252, 27 lipca 2026
  • ENISA: Single Reporting Platform: enisa.europa.eu
  • BSI TR-03183-2: wymagania dla SBOM
  • npm sbom, dokumentacja: docs.npmjs.com
  • cyclonedx-php-composer: github.com
  • Wtyczka vuln-today-jenkins-plugin, kod źródłowy: github.com/radeksh/vuln-today-jenkins-plugin
  • vuln.today: vuln.today