BBest24
PLEN

SBOM to dopiero początek: czy podatna biblioteka oznacza podatny produkt?

Od trafienia skanera do uzasadnionej oceny konkretnego firmware: SBOM, warunki podatności, dowody, CycloneDX VEX i zmiany unieważniające wcześniejszy wniosek.

Przekrój modułu z zależnościami i wyróżnioną drogą jednego komponentu do interfejsu.

Skaner zgłasza podatną bibliotekę w dwóch wariantach firmware. Oba używają tej samej wersji parsera, więc na liście wyników wyglądają identycznie. Jeden przyjmuje dane przez interfejs sieciowy. Drugi ma wyłączoną obsługę skompresowanych danych. Zespół chciałby zamknąć drugi alarm jako nieistotny, ale na razie dysponuje tylko ustawieniem w pliku konfiguracyjnym.

To za mało do rzetelnego rozstrzygnięcia. Flaga może nie dotyczyć wydanego obrazu, kod może pozostać w innej bibliotece, a funkcja może być dostępna przez narzędzie serwisowe. Z drugiej strony sama obecność nazwy komponentu nie dowodzi, że każdy produkt zawierający tę bibliotekę jest podatny w ten sam sposób.

Przejdziemy od składu oprogramowania do oceny konkretnego wydania i komunikatu VEX. Przykład używa fikcyjnego parsera example-parser w wersji 2.3.0 oraz umownej podatności DEMO-PARSER-001. Nie jest to identyfikator CVE, zgłoszenie rzeczywistego błędu ani audyt produktu BBest24. Dowody w scenariuszu są założonym materiałem dydaktycznym; lokalnie sprawdzamy logikę decyzji i poprawność dokumentów przykładowych.

Trzy artefakty odpowiadają na różne pytania

SBOM, software bill of materials, opisuje skład oprogramowania w określonym zakresie. Komunikat o podatności opisuje błąd komponentu i znane warunki jego wystąpienia. VEX, vulnerability exploitability exchange, przekazuje ocenę odnoszącą tę podatność do określonego produktu lub konfiguracji. Potrzebujemy wszystkich trzech, ale żaden nie zastępuje pozostałych.

Model CycloneDX pozwala opisywać komponenty, zależności i metadane. Spis nie staje się automatycznie pełny dlatego, że zapisano go w poprawnym JSON. Lista z menedżera pakietów może pominąć bibliotekę skopiowaną ręcznie, system bazowy, kod dostawcy albo drugi obraz uruchamiany przez urządzenie. Zakres i kompletność muszą być świadomą częścią wyniku.

VEX w CycloneDX dotyczy znaczenia podatności w kontekście użycia komponentu. Nie jest mechanizmem naprawczym i nie zmienia zawartości firmware. Status powinien wskazywać, co wiemy, dla jakiego wydania i na podstawie jakiego uzasadnienia. W przeciwnym razie łatwo zamienić go w trwały filtr ukrywający alarm, który po zmianie produktu znów jest istotny.

Rozdzielenie artefaktów pomaga również podczas rozmowy z klientem. Pytanie „czy używacie parsera 2.3.0?” dotyczy składu. Pytanie „czy ten błąd może dotknąć mojego wydania?” wymaga analizy. Odpowiedź „mamy SBOM” nie rozstrzyga drugiego pytania, a odpowiedź „nie dotyczy” bez wskazania wydania nie daje odbiorcy możliwości dopasowania oceny.

Jedna biblioteka, dwa obrazy

Fikcyjna podatność dotyczy obsługi skompresowanego wejścia i może doprowadzić do odmowy usługi. Nie opisujemy konkretnego formatu wyzwalającego błąd; wystarczy wiedzieć, że potrzebne są podatny kod oraz droga doprowadzająca do niego odpowiednie dane. Oba warianty zawierają pozostałe funkcje biblioteki parsera.

W razie potrzeby przewiń tabelę poziomo.

Wydanie Założona funkcja produktu Pierwsza obserwacja
1.0-network Import danych przez interfejs sieciowy Obsługa kompresji jest włączona
1.0-local Praca z lokalnym, prostym formatem Konfiguracja deklaruje wyłączenie kompresji

Dla 1.0-network zakładamy następnie dowód E-NET: analiza wskazuje wywołanie podatnej funkcji z wejścia sieciowego, a kontrolowany test potwierdza warunki błędu w tym obrazie. W granicach przykładu uzasadnia to ocenę exploitable. Nie potrzebujemy udowadniać, że ktoś już zaatakował urządzenie, aby potraktować potwierdzoną drogę wykorzystania jako problem wymagający działania.

Dla 1.0-local początkowo nie mamy mapy linkera ani wyniku inspekcji wydanego artefaktu. Pozostaje in_triage, czyli w trakcie analizy. Dopiero hipotetyczny pakiet E-LOCAL, wiążący konfigurację budowania z obrazem i potwierdzający brak podatnego kodu, uzasadnia not_affected z przyczyną code_not_present. Komponent parsera nadal pozostaje w SBOM, ponieważ jego pozostały kod jest obecny.

To rozróżnienie jest istotne. Nie nazywamy trafienia skanera fałszywym tylko dlatego, że produkt nie zawiera jednej podatnej funkcji. Dopasowanie biblioteki mogło być poprawne; dopiero analiza produktu wykazała ograniczony wpływ. false_positive ma inne znaczenie niż uzasadniony brak wpływu w konkretnej konfiguracji.

Identyfikacja wydania jest częścią dowodu

Nazwa „wersja 1.0” może obejmować kilka wariantów kompilacji, architektur procesora i obrazów dostawcy. Do oceny potrzebujemy dokładniejszej tożsamości: produktu, wariantu, identyfikatora wydania oraz powiązania z artefaktem. W rzeczywistym procesie przydatny jest skrót kryptograficzny obrazu i identyfikator procesu budowania. Sam numer z ekranu ustawień może nie rozróżniać dwóch różnych plików.

Dla przykładowych dokumentów używamy jednoznacznych identyfikatorów 1.0-network i 1.0-local. Nie dołączamy fikcyjnego skrótu udającego wynik pomiaru prawdziwego firmware. W lokalnych plikach wyraźnie zapisano, że są materiałem szkoleniowym, a dowody E-NET i E-LOCAL stanowią założenia scenariusza.

Trzeba też ustalić granice obrazu. Czy ocena obejmuje główny firmware, bootloader, osobny procesor komunikacyjny i partycję odzyskiwania? Czy moduł pobiera dodatkowy kod po pierwszym uruchomieniu? Czy aktualizacja zmienia tylko aplikację, pozostawiając system bazowy? Jeżeli odpowiedź nie jest znana, brak komponentu w jednym pliku nie uzasadnia wniosku o całym produkcie.

Warto zachować również tożsamość informacji o podatności. Opis błędu może zostać rozszerzony o kolejną funkcję lub inne warunki wejścia. Ocena oparta na poprzednim opisie może wymagać ponowienia nawet wtedy, gdy firmware nie zmienił się ani o bajt. Dowód ma zatem związek zarówno z wydaniem produktu, jak i ze stanem wiedzy o błędzie.

Dwa skanery mogą porównywać różne rzeczy

Przed analizą wpływu trzeba potwierdzić samo dopasowanie. Ta sama krótka nazwa może należeć do różnych pakietów, a jeden komponent może występować pod nazwą dostawcy i nazwą projektu źródłowego. Identyfikator pakietu, wersja, pochodzenie i informacje o wprowadzonych poprawkach pomagają ustalić, czy komunikat rzeczywiście dotyczy ocenianego kodu. Nie należy dopasowywać wyłącznie po podobnym napisie.

Różnica między wynikami narzędzi może wynikać z ich zakresu. Jedno skanuje plik zależności źródłowych, drugie gotowy obraz. Pierwsze zobaczy bibliotekę używaną tylko przez narzędzie budowania; drugie może wykryć kopię statycznie dołączoną do aplikacji. Tych wyników nie porównujemy przez samą liczbę alarmów. Najpierw ustalamy, jakie artefakty i rodzaje zależności każde narzędzie obejmuje.

W naszym scenariuszu oba obrazy rzeczywiście zawierają parser 2.3.0, więc dalsza analiza jest uzasadniona. Gdyby dopasowanie dotyczyło innego projektu o podobnej nazwie, problemem byłaby identyfikacja, a nie niedostępność podatnej funkcji. To inna przyczyna i powinna pozostawić inny ślad w ocenie. Precyzyjny zapis ułatwia później automatyczną korektę podobnych zgłoszeń bez wyciszania prawdziwych trafień.

Warto przechować raport wejściowy skanera, wersję jego bazy i regułę dopasowania. Jeśli po aktualizacji bazy pojawia się nowy alarm dla niezmienionego obrazu, można sprawdzić, czy doszła nowa wiedza o błędzie, poprawiono zakres wersji czy tylko zmieniła się identyfikacja komponentu. Każda z tych sytuacji może wymagać innego działania. Sam fakt zmiany liczby ostrzeżeń nie mówi jeszcze, że produkt stał się bardziej albo mniej bezpieczny.

Drzewo powinno mieć gałąź „nie wiemy”

Dla dwóch niepoprawionych obrazów z przykładu zaczynamy od pytania, czy znamy ich tożsamość i skład. Następnie sprawdzamy, czy podatny kod został rzeczywiście wykluczony. Jeżeli jest obecny albo jego brak nie został udowodniony, analizujemy drogę wejścia i warunki wywołania błędu.

Uproszczone drzewo oceny niepoprawionego obrazu z wynikami in_triage, not_affected i exploitable oraz jawną ścieżką braku dowodów.
Uproszczone drzewo dla dwóch niepoprawionych obrazów z przykładu. Po lewej biegnie dalsza analiza, po prawej wynik danej gałęzi. Nieudowodnienie drogi do błędu nie wystarcza do not_affected. Diagram nie obejmuje wszystkich uzasadnień VEX, w tym pełnego dowodu nieosiągalności oraz środków ograniczających ryzyko.

Diagram celowo nie obejmuje wszystkich możliwych uzasadnień VEX. Można także wykazać nieosiągalność kodu albo skuteczność konkretnego zabezpieczenia, ale potrzebny jest osobny argument obejmujący warunki, w których produkt działa. Nie znaleziono wywołania podczas jednej próby to słabsze stwierdzenie niż wykazano, że w analizowanym zakresie nie istnieje droga do tej funkcji, również w stanach błędu i serwisu.

Brak odtworzenia błędu również nie rozstrzyga sprawy. Test mógł używać niewłaściwej konfiguracji, innej architektury, niepełnych danych albo nie obejmować ścieżki serwisowej. Ujemny wynik jednej próby może być wartościowym fragmentem analizy, ale nie powinien automatycznie zmieniać statusu na not_affected.

Stan nierozstrzygnięty potrzebuje właściciela i następnego działania. „Czekamy na dostawcę” powinno wskazywać, jakiej informacji brakuje: składu obrazu, zakresu podatnych wersji, potwierdzenia poprawki czy opisu warunków. Bez tego status może pozostać niezmieniony przez miesiące, choć zespół nie wie już, co miałoby zakończyć analizę.

Jak mocny jest dowód braku kodu

Flaga kompilacji opisuje intencję. Zapis procesu budowania pokazuje, czy użyto jej dla badanego wydania. Mapa linkera i inspekcja artefaktu pomagają potwierdzić, co rzeczywiście trafiło do obrazu. Trzeba jeszcze sprawdzić, czy ta sama funkcja nie została skopiowana lub dostarczona w innym komponencie. Żaden pojedynczy plik nie zastępuje całego powiązania.

W E-LOCAL zakładamy właśnie taki spójny pakiet. Konfiguracja wyłącza obsługę kompresji, proces budowania dotyczy 1.0-local, a analiza obrazu potwierdza wykluczenie podatnej implementacji. To mocniejsza podstawa niż wyszukanie nazwy funkcji w kodzie źródłowym. Optymalizacja, linkowanie statyczne i zmiana nazw symboli sprawiają, że sama obecność lub nieobecność napisu w pliku nie jest uniwersalnym testem.

Dowód powinien być czytelny dla drugiej osoby. Musi wskazywać narzędzia i ich wersje, sprawdzony artefakt, warunki, wynik oraz ograniczenia. „Sprawdziłem, jest dobrze” nie pozwala powtórzyć oceny po zmianie kompilatora. Zrzut pustej listy symboli nie wyjaśnia, czy lista obejmowała cały obraz i czy badana funkcja mogła zostać zintegrowana z inną.

Przy zamkniętym komponencie dostawcy nie zawsze da się przeprowadzić taką analizę samodzielnie. Można potrzebować jego oświadczenia odnoszącego się do dokładnego wydania oraz opisu podstaw. Własny dokument powinien zachować pochodzenie tego dowodu i jego ograniczenia. Nie przekształcamy ogólnej odpowiedzi handlowej w techniczne potwierdzenie braku podatności.

Słownik formatu ma znaczenie

W przykładach używamy CycloneDX 1.7. Schemat tej wersji definiuje między innymi stany in_triage, exploitable, not_affected, resolved i false_positive. Uzasadnienie code_not_present dotyczy usuniętego lub wyeliminowanego kodu. Nazw nie należy przenosić dowolnie z innego formatu.

OpenVEX 0.2.0 używa między innymi under_investigation, affected, fixed i not_affected, a jego uzasadnienia mają własne nazwy, na przykład vulnerable_code_not_present. Podobna intencja nie oznacza identycznego pola ani pełnej równoważności każdego przypadku. Konwersja musi zachować zakres, czas i uzasadnienie, a nie tylko podmienić napis.

Dla lokalnego wariantu istotny fragment przykładowej oceny CycloneDX wygląda tak:

{
  "id": "DEMO-PARSER-001",
  "analysis": {
    "state": "not_affected",
    "justification": "code_not_present",
    "detail": "Fictional E-LOCAL: vulnerable code absent in 1.0-local."
  },
  "affects": [
    {"ref": "urn:example:firmware:1.0-local"}
  ]
}

To fragment, nie samodzielny pełny dokument. Pełny dokument wymaga również metadanych, identyfikatorów, zależności od parsera i czasu oceny. Podczas przygotowania przykładu sprawdzono lokalnie strukturę kompletnych dokumentów względem schematu 1.7 oraz spójność ich odwołań.

Walidacja formatu nie potwierdza prawdziwości uzasadnienia. Może wykryć niedozwolony status albo błędne pole, ale nie dowiedzie, że E-LOCAL rzeczywiście wyklucza podatny kod. W przykładzie osobny test celowo wpisuje under_investigation do dokumentu CycloneDX i oczekuje odrzucenia. To kontrola interoperacyjności dokumentu, nie test bezpieczeństwa urządzenia.

Status musi prowadzić do działania

Dla 1.0-network ocena exploitable otwiera pracę nad poprawką lub innym udokumentowanym ograniczeniem ryzyka. Potrzebne są odpowiedzialna osoba, priorytet, sposób identyfikacji urządzeń i plan weryfikacji zmiany. Warto oddzielić rozwiązanie docelowe od tymczasowego: wyłączenie interfejsu na czas przygotowania aktualizacji nie jest tym samym co usunięcie błędu z kodu.

Zabezpieczenie kompensujące ma warunki. Reguła sieciowa może zmniejszyć dostępność wejścia w jednej instalacji, ale nie istnieć w drugiej. Funkcja wymagająca logowania może nadal przyjmować dane od użytkownika, którego uprawnienia nie powinny pozwalać na zatrzymanie całej usługi. Samo uwierzytelnienie nie dowodzi, że warunki podatności są niemożliwe.

Jeżeli stosujemy uzasadnienie związane z konfiguracją lub ochroną, trzeba wskazać, kto utrzymuje ten warunek i co się stanie po jego zmianie. Odbiorca VEX musi umieć ustalić, czy jego instalacja mieści się w opisanym zakresie. Ogólny status dla całej rodziny produktu może być zbyt szeroki, gdy wynik zależy od lokalnej reguły zapory lub aktywnego trybu serwisowego.

Po zastosowaniu poprawki nowe wydanie wymaga testu regresji i nowej oceny. Stare urządzenia nie stają się naprawione w chwili opublikowania pliku. Flota może jednocześnie zawierać wydania podatne, poprawione i nierozstrzygnięte. Raport powinien zachować to rozróżnienie, zamiast pokazywać jeden status produktu na podstawie najnowszej dostępnej wersji.

Wariant, który unieważnia wcześniejszy wniosek

Załóżmy, że 1.1-local dodaje wygodny import skompresowanych plików przez narzędzie serwisowe. W tym celu kod wcześniej wykluczony wraca do obrazu. Dokument E-LOCAL dotyczący 1.0-local nie jest już dowodem braku kodu w nowym wydaniu, nawet jeśli menedżer pakietów nadal pokazuje parser 2.3.0.

Ocena nowego wydania wraca do in_triage do czasu zbadania zmienionej ścieżki. Jeżeli dane z pliku mogą dotrzeć do podatnej funkcji i spełniają warunki błędu, otrzymujemy podstawę do exploitable. Wektor wejścia może różnić się od sieciowego, co wpływa na analizę ryzyka, ale nie przywraca automatycznie wcześniejszego not_affected.

Historia oceny 1.0-local pozostaje potrzebna. Nie nadpisujemy jej tak, jakby dawna decyzja zawsze była błędna. Mogła być poprawna dla starego obrazu. Nowa informacja pokazuje granicę jej obowiązywania. Dzięki temu odbiorca może rozróżnić zmianę produktu od korekty błędu w poprzedniej analizie.

Powiązanie wydania firmware, komponentu parsera, dowodu technicznego i komunikatu VEX.
Łańcuch oceny konkretnego wydania: identyfikator obrazu prowadzi do składu i komponentu, następnie do dowodu, a dopiero potem do komunikatu VEX. Zmiana obrazu, konfiguracji lub warunków podatności wymaga sprawdzenia, czy dowód nadal obowiązuje.

Ten sam mechanizm dotyczy nowej informacji od dostawcy. Jeśli okazuje się, że błąd występuje także w innym trybie parsera, wcześniejsze wykluczenie jednej funkcji może nie wystarczyć. Wtedy ponownie otwieramy ocenę również dla niezmienionego obrazu. Powodem jest zmiana zakresu wiedzy o podatności, a nie zmiana listy komponentów.

Poprawka bez zmiany numeru biblioteki

Dostawca może przenieść poprawkę do własnej gałęzi, zachowując główny numer wersji. Skaner oparty na nazwie i numerze nadal zgłosi dopasowanie. Nie należy ani ignorować go automatycznie, ani zakładać, że numer rozstrzyga wszystko. Trzeba powiązać zmianę kodu z konkretnym artefaktem i sprawdzić, czy obejmuje warunki badanego błędu.

W takim scenariuszu warto zachować identyfikator poprawki, jej pochodzenie, różnicę kodu i wynik testu regresji. Status resolved opisuje usunięcie problemu w ocenianym zakresie. CycloneDX przewiduje także resolved_with_pedigree, gdy wraz z oceną udostępnia się odpowiedni zapis zmian komponentu. Nie należy wybierać bardziej szczegółowego statusu bez wymaganego materiału tylko dlatego, że brzmi mocniej.

Oddzielną sprawą jest zgodność funkcjonalna aktualizacji. Poprawiony parser może zmienić obsługę uszkodzonych plików albo odrzucać dane, które wcześniej były tolerowane. Test bezpieczeństwa powinien iść w parze z testami prawidłowych wejść oraz odzyskania działania po błędzie. Zamknięcie jednego alarmu nie usprawiedliwia niewidocznej regresji podstawowej funkcji produktu.

Jak nie zmienić VEX w plik wyciszeń

Pierwszym sygnałem problemu jest dokument pozbawiony dokładnego zakresu. Drugim jest uzasadnienie oparte wyłącznie na pamięci autora: „tej funkcji nie używamy”. Trzecim jest brak zdarzenia, które wymusi ponowne sprawdzenie. Jeżeli żaden przegląd wydania nie pyta o zmianę konfiguracji, status może przetrwać dłużej niż warunek, na którym go oparto.

W procesie wydawniczym warto powiązać ocenę ze zmianami zależności, flag budowania, interfejsów wejściowych i funkcji serwisowych. Nie każda zmiana unieważnia wszystkie analizy. Potrzebna jest jednak możliwość ustalenia, które dowody odwołują się do zmienionego elementu. To ta sama zasada identyfikowalności, która pomaga w testach funkcjonalnych.

Dokument powinien mieć autora lub odpowiedzialny zespół, datę, wersję i ustaloną drogę aktualizacji. Podpis lub zaufany kanał dystrybucji pomaga ustalić pochodzenie, ale nie zastępuje analizy technicznej. Również odbiorca powinien sprawdzać, czy komunikat dotyczy jego wersji i czy nie został zastąpiony nowszą oceną. Stary, poprawnie podpisany dokument może być nieaktualny.

Karta rozstrzygnięcia pojedynczego alarmu

Praktyczny odbiór zaczyna się od poniższej karty. Puste pole powinno prowadzić do jawnej niewiadomej i działania, a nie do domyślnego zamknięcia alarmu.

W razie potrzeby przewiń tabelę poziomo.

Pole Minimalna treść
Przedmiot Produkt, wariant, wydanie, zakres obrazu i identyfikacja artefaktu
Dopasowanie Komponent, wersja, pochodzenie i powód powiązania z podatnością
Warunki błędu Funkcja, wejście, konfiguracja oraz wymagane uprawnienia
Dowód Materiał, metoda, wersje narzędzi, wynik i ograniczenia
Wniosek Status oraz uzasadnienie właściwe dla wybranego formatu
Działanie Poprawka, ograniczenie, brakujące badanie i osoba odpowiedzialna
Ponowna ocena Zmiany produktu lub informacji, które unieważniają argument

W naszym przykładzie wynik nie brzmi „parser jest bezpieczny”. Brzmi: 1.0-network ma potwierdzoną drogę do fikcyjnego błędu; 1.0-local można uznać za niedotknięty tym błędem dopiero po uzyskaniu właściwego dowodu braku kodu. Dla następnego wydania trzeba sprawdzić te warunki ponownie. Powiązanie wniosku z zakresem i dowodem pozwala podjąć konkretną decyzję i później ją obronić.

Warto również rozdzielić materiał wewnętrzny od komunikatu dla odbiorcy. Dokument VEX powinien przekazywać wystarczający zakres i uzasadnienie, ale nie musi zawierać sekretów budowania ani danych dostępowych. Pełny pakiet dowodowy może pozostać w kontrolowanym repozytorium z jednoznacznym odwołaniem.

Źródła

Źródła sprawdzono 21 października 2026 r. Przykładowe dokumenty CycloneDX przeszły walidację schematu i odwołań; nie stanowi to potwierdzenia bezpieczeństwa rzeczywistego produktu. Wszystkie komponenty, wydania, podatność i pakiety dowodowe w scenariuszu są fikcyjne.

Wszystkie notatki →