Każda linijka kodu trafiająca do produkcji została napisana przez człowieka pracującego pod presją czasu, niepełnym kontekstem, niekompletną dokumentacją i nieodwracalną trudnością w rozumowaniu dużych systemów w czasie rzeczywistym. Statyczna analiza kodu to dyscyplina polegająca na systematycznym badaniu kodu źródłowego bez jego uruchamiania, z wykorzystaniem zautomatyzowanych narzędzi do wykrywania tego, co niezawodnie pomija ludzki przegląd: luk w zabezpieczeniach, błędów logicznych, naruszeń standardów kodowania, martwego kodu i wzorców strukturalnych wskazujących na przyszłe problemy z konserwacją. Nie zastępuje ona testowania, przeglądu projektu ani oceny inżynierskiej. Jest to warstwa zautomatyzowanej kontroli, która działa na każdym pliku, każdym zatwierdzeniu i każdej kompilacji z szybkością i spójnością, jakiej nie dorówna żaden proces ręczny.
SMART TS XL
Najbardziej kompleksowe narzędzie do analizy kodu statycznego dla dużych przedsiębiorstw
ODKRYJ TERAZDefinicja brzmi prosto. W praktyce analiza statyczna obejmuje szerokie spektrum technik, działa na różnych poziomach szczegółowości i dokładności, ma zastosowanie w różnych fazach cyklu życia oprogramowania i znacznie różni się pod względem możliwości wykrywania poszczególnych narzędzi. Narzędzie linter wymuszające reguły formatowania technicznie rzecz biorąc wykonuje analizę statyczną. Narzędzie, które konstruuje pełny graf wywołań, śledzi zanieczyszczone dane do ujść bezpieczeństwa, identyfikuje niedostępne gałęzie i mapuje zależności na poziomie pól w wielojęzycznym systemie korporacyjnym, również wykonuje analizę statyczną, ale oba narzędzia działają na zupełnie różnych poziomach szczegółowości technicznej i praktycznej użyteczności. Zrozumienie tego spektrum jest warunkiem wstępnym do wyboru i efektywnego wykorzystania analizy statycznej.
Czym jest analiza statyczna, a czym nie jest
Analiza statyczna bada kod źródłowy jako ustrukturyzowany artefakt, wykorzystując gramatykę i semantykę języka programowania do zbudowania modelu działania kodu, a następnie odpytując ten model o interesujące właściwości. Analiza jest przeprowadzana bez uruchamiania kodu: nie jest wymagane żadne środowisko wykonawcze, żadne dane wejściowe do testów ani ślad wykonania. Pliki źródłowe stanowią dane wejściowe, a wynik analizy jest w całości uzyskiwany ze struktury, zawartości i relacji kodu.
Ta właściwość braku wykonywania jest zarówno źródłem wartości analizy statycznej, jak i źródłem jej ograniczeń. Ponieważ analiza statyczna nie wykonuje kodu, może ona objąć każdą ścieżkę kodu, w tym ścieżki, do których testowanie nigdy nie dociera: rzadko używane procedury obsługi błędów, gałęzie warunkowe aktywowane tylko przez określone konfiguracje danych oraz ścieżki kodu starszego typu, które nie były testowane od lat. Ponieważ analiza statyczna nie wykonuje kodu, nie może również obserwować zachowania w czasie wykonywania, nie może wnioskować o wartościach, które są ustalane dopiero w czasie wykonywania, i musi używać aproksymacji, gdy zachowanie kodu zależy od kontekstu wykonania, do którego nie ma dostępu.
Praktyczną konsekwencją jest to, że analiza statyczna znajduje konkretną, wartościową i dobrze zdefiniowaną klasę problemów: problemy strukturalne, naruszenia zasad, wzorce powiązane ze znanymi klasami podatności oraz relacje zależności wyrażone w tekście i strukturze kodu. Nie wykrywa problemów, które pojawiają się tylko w określonych warunkach środowiska wykonawczego, wyścigów kodów wymagających współbieżnego wykonania, ani błędów logiki biznesowej, które zależą od semantycznej wiedzy o tym, co kod ma robić. Te ograniczenia nie umniejszają wartości analizy statycznej, lecz definiują jej zakres. Zrozumienie tego zakresu pozwala zespołom na odpowiednią integrację analizy statycznej z testowaniem, przeglądem kodu i monitorowaniem środowiska wykonawczego, zamiast traktować ją jako substytut któregokolwiek z tych elementów.
Analiza statyczna a analiza dynamiczna
Analiza dynamiczna ocenia kod poprzez jego wykonywanie. Narzędzie obserwuje zachowanie w czasie wykonywania: alokację i dealokację pamięci, czas wykonywania dla każdej ścieżki kodu, wartości zmiennych w określonych punktach, wzorce współbieżności oraz wywołania systemowe. Analiza dynamiczna wykrywa problemy, które ujawniają się tylko podczas wykonywania: wycieki pamięci kumulujące się w długich cyklach, wyścigi między jednocześnie wykonywanymi wątkami, regresje wydajności przy określonych obciążeniach oraz awarie spowodowane nieoczekiwanymi wartościami wejściowymi.
Te dwa podejścia raczej się uzupełniają, niż konkurują. Poniższe porównanie obrazuje praktyczny zakres każdego z nich:
| Właściwość | Analiza statyczna | Analiza dynamiczna |
|---|---|---|
| Wymaga wykonania | Nie | Tak |
| Pokrycie ścieżki kodu | Wszystkie ścieżki, łącznie z tymi niećwiczonymi | Tylko wykonane ścieżki |
| Wyszukuje błędy pamięci wykonawczej | Częściowo (tylko wzory) | Tak, bezpośrednio |
| Znajduje luki w zabezpieczeniach w strukturze kodu | Tak | Częściowo |
| Znajduje błędy współbieżności | Częściowo (tylko wzory) | Tak, bezpośrednio |
| Działa na niekompletnym kodzie | Tak | Nie |
| Skalowanie do pełnej bazy kodu w jednym przejściu | Tak | Zależy od pokrycia testowego |
| Wykrywa martwy kod | Tak | Nie |
| Identyfikuje zależności między komponentami | Tak | Częściowo |
Najskuteczniejsze programy zapewnienia jakości wykorzystują oba te podejścia. Analiza statyczna zapewnia wczesne i kompleksowe wykrywanie problemów strukturalnych i naruszeń zasad przed uruchomieniem kodu. Analiza dynamiczna zapewnia weryfikowane w czasie wykonywania potwierdzenie zachowania podczas wykonywania kodu. Żadna z tych metod nie obejmuje pełnego zakresu jakości i bezpieczeństwa.
Gdzie analiza statyczna znajduje się w cyklu życia rozwoju
Analiza statyczna powinna być częścią cyklu rozwoju oprogramowania już na najwcześniejszym praktycznym etapie: w środowisku IDE programisty podczas pisania kodu, w hakach pre-commit, które są uruchamiane przed wprowadzeniem kodu do systemu kontroli wersji, oraz w procesie ciągłej integracji (CI), który weryfikuje każdą zmianę przed jej scaleniem. To właśnie to umiejscowienie sprawia, że analiza statyczna jest mechanizmem zapobiegawczym, a nie detekcyjnym: problemy wykryte w środowisku IDE wymagają kilku minut na naprawę, problemy wykryte w godzinach przed zatwierdzeniem, a problemy wykryte po wdrożeniu kosztują znacznie więcej czasu i ryzyka.
Zasada ta jest czasami nazywana „przesunięciem w lewo”, co oznacza przesunięcie kontroli jakości na wcześniejszym etapie procesu rozwoju, w kierunku lewej strony typowej osi czasu SDLC (od lewej do prawej). Analiza statyczna jest podstawowym mechanizmem technicznym przesunięcia kontroli bezpieczeństwa i jakości w lewo, ponieważ jest to jedyne zautomatyzowane podejście, które może zostać uruchomione na kodzie, zanim będzie on wystarczająco ukończony do wykonania, zanim zostaną dla niego napisane zestawy testów i zanim zostanie sprawdzony przez inną osobę. Jak opisano w kontekście Integracja DevOps dla jakości kodu, wdrażanie automatycznej analizy do codziennych przepływów prac programistycznych to podstawowa praktyka dla organizacji, które chcą utrzymać jakość kodu na dużą skalę, bez zwiększania nakładu pracy związanego z ręcznym przeglądem proporcjonalnie do wielkości zespołu.
Jak działa analiza statyczna: warstwy techniczne
Narzędzia do analizy statycznej działają na kilku różnych poziomach technicznych, z których każdy zapewnia inny rodzaj analizy i wykrywa inną klasę problemów. Zrozumienie tych poziomów jest ważne, ponieważ różne narzędzia działają na różnych poziomach, a poziom ten decyduje zarówno o tym, co narzędzie może znaleźć, jak i o tym, czego nie może.
Analiza leksykalna: warstwa powierzchniowa
Analiza leksykalna to najbardziej podstawowy poziom analizy statycznej. Działa na kodzie źródłowym jako sekwencji znaków, dzieląc go na tokeny: słowa kluczowe, identyfikatory, operatory, literały i ograniczniki. Narzędzia lintingowe, które wymuszają stosowanie konwencji nazewnictwa, reguł dotyczących odstępów między wierszami, maksymalnej długości wiersza i niedozwolonego używania słów kluczowych, działają głównie na poziomie leksykalnym. Są szybkie, wymagają minimalnej konfiguracji i konsekwentnie wykrywają naruszenia zasad na poziomie powierzchniowym.
Analiza leksykalna nie potrafi wnioskować o tym, co robi kod. Wie, że zmienna ma określoną nazwę, ale nie wie, co reprezentuje ani jak jej wartość przepływa przez program. Narzuca formę bez zrozumienia treści. Z tego powodu analiza leksykalna jest niezbędna, ale niewystarczająca jako samodzielny mechanizm jakości: zapewnia czytelność i spójność kodu, ale nie jest w stanie wykryć błędów logicznych, luk w zabezpieczeniach ani problemów strukturalnych.
Analiza składniowa: struktura bez semantyki
Analiza składniowa analizuje kod źródłowy zgodnie z gramatyką języka programowania, generując abstrakcyjne drzewo składniowe, które reprezentuje strukturalne relacje w kodzie: które wyrażenia są podwyrażeniami, które innymi, które instrukcje należą do których bloków, które identyfikatory są deklaracjami, a które referencjami. Wiele narzędzi do analizy statycznej działa głównie na poziomie składniowym, wykorzystując dopasowywanie wzorców AST do wykrywania struktur kodu powiązanych ze znanymi problemami.
Reguła, która sygnalizuje funkcje przekraczające próg złożoności, działa składniowo: zlicza liczbę punktów decyzyjnych w kodzie AST ciała funkcji. Reguła wykrywająca wzorce dereferencji wartości null działa składniowo: znajduje wzorce AST, w których wartość, która może być nullem, jest używana bez sprawdzania nulla. Te detekcje są skuteczniejsze niż analiza leksykalna, ponieważ opierają się na strukturze, ale nadal operują na wzorcach, a nie na semantyce. Dopasowanie wzorca dereferencji wartości null nie wie, czy zmienna może być nullem w kontekście, w którym jest używana; wie tylko, że wzorzec jest obecny.
Analiza semantyczna: znaczenie i typ
Analiza semantyczna opiera się na ustalonym znaczeniu kodu: jaki typ ma każde wyrażenie, do której deklaracji odwołuje się każde odwołanie, która przeciążona metoda jest wywoływana oraz co system typów może udowodnić na temat wartości przepływających przez program. Sprawdzanie typów jest najbardziej znaną formą analizy semantycznej. Moduł sprawdzający typy w kompilatorze przeprowadza analizę statyczną, odrzucając kod, który przekazuje ciąg znaków w miejscu, gdzie oczekiwana jest liczba całkowita.
Bardziej zaawansowana analiza semantyczna obejmuje wnioskowanie typu, które określa typy dla wyrażeń, które nie są jawnie adnotowane, oraz analizę bezpieczeństwa wartości null, która śledzi, czy wartości, które mogą być nullem, są bezpiecznie sprawdzane przed użyciem. Analizy te wymagają pełnego rozwiązania symboli, co oznacza, że są specyficzne dla danego języka i wymagają kompletnego lub prawie kompletnego kodu: nie mogą działać na fragmentach, w których brakuje definicji typów lub które odwołują się do symboli zdefiniowanych w niedostępnych zależnościach. Jak zbadano w szerszej dyskusji na temat planowanie modernizacji dziedzictwaMożliwość przeprowadzenia kompletnej analizy semantycznej w starszych bazach kodu, które mogą mieć niekompletne lub nieudokumentowane zależności, wymaga specjalistycznych narzędzi, które potrafią obsłużyć specyficzne wzorce strukturalne tych środowisk.
Analiza przepływu danych: wartości poprzez realizację
Analiza przepływu danych śledzi, jak wartości przemieszczają się w programie. Działa ona na grafie przepływu sterowania programu, propagując informacje o wartościach zmiennych wzdłuż ścieżek wykonywania i rejestrując, skąd pochodzą wartości, gdzie są modyfikowane i gdzie są wykorzystywane. Analiza przepływu danych umożliwia wykrywanie problemów, takich jak niezainicjowane odczyty zmiennych, użycie po zwolnieniu (use-after-free) w zarządzaniu pamięcią oraz propagacja skażenia z danych wejściowych użytkownika do operacji wrażliwych na bezpieczeństwo.
Analiza skażenia, specyficzna forma analizy przepływu danych, śledzi wartości pochodzące z niezaufanych źródeł (dane wejściowe użytkownika, dane sieciowe, zawartość plików) i identyfikuje, czy wartości te mogą dotrzeć do operacji wrażliwych pod względem bezpieczeństwa (zapytania SQL, wywołania systemowe, operacje wyjściowe) bez ich oczyszczenia. Jeśli skażona wartość dotrze do ujścia zabezpieczenia bez oczyszczenia, analiza sygnalizuje potencjalną lukę w zabezpieczeniach. Jest to zautomatyzowany mechanizm wykrywania leżący u podstaw większości luk w zabezpieczeniach związanych z wstrzykiwaniem kodu SQL, atakami typu cross-site scripting i wstrzykiwaniem poleceń w narzędziach do analizy statycznej.
Różnica między tymi dwiema ścieżkami w kodzie jest niewielka, ale wynik w zakresie bezpieczeństwa jest zupełnie inny:
# Vulnerable: user input reaches SQL query without sanitization (tainted path)
def get_user(username):
query = "SELECT * FROM users WHERE name = '" + username + "'"
return db.execute(query) # sink: tainted value reaches SQL execution
# Safe: sanitization breaks the taint chain before the sink
def get_user_safe(username):
query = "SELECT * FROM users WHERE name = ?"
return db.execute(query, (username,)) # parameterized: taint neutralized
Statyczna analiza skażenia wykrywa podatny na atak wzorzec w pierwszej funkcji bez wykonywania kodu i bez konieczności wprowadzania złośliwych danych testowych do jej uruchomienia. Analiza przepływu danych jest kosztowna obliczeniowo i wiąże się z fundamentalnymi kompromisami między precyzją a wydajnością. Precyzyjna analiza przepływu danych, uwzględniająca wszystkie możliwe ścieżki wykonania, jest często niepraktyczna w przypadku dużych baz kodu. Większość narzędzi korzysta z przybliżeń, które kosztem skalowalności kosztem precyzji, dlatego wyniki dotyczące przepływu danych zazwyczaj zawierają wskaźnik fałszywie dodatnich wyników, który wymaga weryfikacji przez człowieka. wizualizacja kodu ścieżek wykonywania i przepływów danych sprawia, że wyniki analizy są przejrzyste dla programistów, którzy muszą sprawdzić, czy oznaczona ścieżka jest rzeczywiście podatna na atak w kontekście ich aplikacji.
Analiza przepływu sterowania: ścieżki wykonania
Analiza przepływu sterowania tworzy graf wszystkich możliwych ścieżek wykonania w kodzie, identyfikując, które instrukcje są osiągalne, które nie działają, oraz jakie warunki muszą zostać spełnione, aby każda gałąź mogła zostać wykonana. Graf przepływu sterowania stanowi podstawę wielu innych analiz: analiza przepływu danych operuje na grafie przepływu sterowania, analiza osiągalności wykorzystuje go do identyfikacji niedziałającego kodu, a na jego podstawie wyprowadzane są metryki złożoności, takie jak złożoność cyklomatyczna.
Analiza przepływu sterowania umożliwia wykrywanie martwego kodu: kod, który jest zdefiniowany, ale nieosiągalny z żadnego punktu wejścia, nie ma żadnych krawędzi wejściowych w grafie przepływu sterowania i można go zidentyfikować jako nieużywany. Ma to bezpośredni związek z… mapowanie zależności aplikacji których zespoły przedsiębiorstw potrzebują przed modernizacją: wiedza o tym, które ścieżki kodu są aktywne, a które nie, pozwala określić, co można bezpiecznie usunąć, a co musi zostać zachowane podczas migracji.
Analiza grafu połączeń: relacje między komponentami
Analiza grafu wywołań buduje model określający, które funkcje wywołują które inne funkcje w całej bazie kodu. Kompletny graf wywołań obsługuje enumerację wywołujących, enumerację wywoływanych, analizę zależności przechodnich oraz identyfikację funkcji, które nigdy nie są wywoływane z żadnego punktu wejścia. Międzykomponentowa analiza grafu wywołań, obejmująca wiele plików, modułów i pakietów, stanowi techniczną podstawę analizy wpływu: określa, na co wpłynie zmiana danej funkcji lub interfejsu.
W bazach kodu jednojęzycznych i jednorepozycyjnych, konstrukcja grafu wywołań jest dobrze obsługiwana przez większość dojrzałych narzędzi do analizy statycznej. W wielojęzycznych środowiskach korporacyjnych, konstrukcja kompletnego grafu wywołań wymaga zunifikowanej platformy analitycznej, która przetwarza wszystkie języki w systemie i rozwiązuje między nimi relacje wywołań między językami. Bazy kodów JavaScript i Node.js, komplikuje to dynamiczne ładowanie modułów, rozsyłanie oparte na prototypach i wzorce wywołań zwrotnych. W przypadku systemów korporacyjnych łączących COBOL, JCL, SQL i nowoczesne warstwy usług, wyzwanie jest znacznie skalowalne, wymagając parserów specyficznych dla danego języka i modelu grafu międzyjęzykowego do reprezentowania całego systemu.
Co wykrywa analiza statyczna: praktyczna taksonomia
Kategorie problemów wykrywane przez narzędzia do analizy statycznej obejmują szeroki zakres, a różne narzędzia obejmują różne podzbiory tego zakresu. Zrozumienie taksonomii pomaga zespołom dopasować możliwości narzędzi do ich specyficznych wymagań w zakresie wykrywania.
Luki w zabezpieczeniach odkryte poprzez analizę wzorców i skażeń:
- Wstrzyknięcie kodu SQL, atak między witrynami, wstrzykiwanie poleceń poprzez rozprzestrzenianie się skażenia ze źródeł kontrolowanych przez użytkownika do odbiorników bezpieczeństwa
- Niebezpieczne użycie kryptografii: słabe algorytmy, niewystarczająca długość kluczy, przestarzałe tryby szyfrowania
- Zakodowane na stałe dane uwierzytelniające, klucze API i tajne wartości osadzone w kodzie źródłowym
- Niebezpieczne wzorce deserializacji i niebezpieczne konfiguracje analizy XML
- Luki w zabezpieczeniach związane z przemierzaniem ścieżek w operacjach dostępu do plików
Problemy z jakością kodu i łatwością utrzymania wykryte podczas analizy strukturalnej:
- Nadmierna złożoność cyklomatyczna wskazuje na to, że kod jest trudny do bezpiecznego testowania i modyfikowania
- Funkcje i klasy, które są zbyt długie i naruszają zasady pojedynczej odpowiedzialności
- Zduplikowane bloki kodu, które stanowią zagrożenie konserwacyjne, gdy jedna kopia jest aktualizowana, a druga nie
- Nieużywane zmienne, parametry i importy dodają szum, nie wpływając na zachowanie
- Niespójne konwencje nazewnictwa i naruszenia stylu, które zmniejszają czytelność
Problemy z poprawnością wykryte poprzez analizę semantyczną i przepływu danych:
- Dereferencje zerowe w językach bez wymuszania bezpieczeństwa wartości zerowych
- Niezainicjowane odczyty zmiennych, które powodują niezdefiniowane zachowanie
- Przepełnienie i niedomiar całkowity w operacjach arytmetycznych
- Wycieki zasobów, w których pozyskane zasoby nie są udostępniane we wszystkich ścieżkach kodu
- Nieprawidłowa obsługa wyjątków, która po cichu połyka błędy
Problemy strukturalne wykryte za pomocą analizy wykresu wywołań i zależności:
- Martwy kod, do którego nie można się dodzwonić z żadnego punktu wejścia
- Zależności cykliczne między modułami wskazują na słabe oddzielenie architektoniczne
- Przestarzałe użycie funkcji w bazach kodu, które zostały zmigrowane do implementacji zastępczych
- Nieosiągalny kod po bezwarunkowych zwrotach lub wyrzuceniach
- Brak kontroli wartości null przed dereferencją wartości zwracanych przez funkcje, które mogą zwracać wartość null
Dla litu szacuje się Aplikacje Node.js i innych dynamicznych środowiskach wykonawczych, kategorie wykrywania obejmują wzorce asynchroniczne: brakujące procedury odrzucania obietnic, naruszenia wzorców wywołania zwrotnego w pierwszej kolejności oraz wycieki pamięci emiterów zdarzeń. Rust i programowanie systemowe kontekstach analiza skupia się na naruszeniach czasu życia, niebezpiecznym użyciu bloków i właściwościach bezpieczeństwa współbieżności, których kompilator nie może w pełni zweryfikować.
Czego analiza statyczna nie jest w stanie wykryć
Zrozumienie granic analizy statycznej jest równie ważne, jak zrozumienie jej możliwości. Zespoły, które oczekują, że analiza statyczna wykryje wszystkie błędy, będą zawiedzione i mogą nie docenić rzetelnych wyników analizy. Kilka kategorii problemów strukturalnie wykracza poza zakres analizy statycznej.
Zachowanie tylko w czasie wykonywania Z definicji jest poza zasięgiem analizy statycznej. Wycieki pamięci, które ujawniają się dopiero po dłuższym wykonywaniu, regresje wydajności przy określonych obciążeniach, błędy współbieżności zależne od niedeterministycznego harmonogramowania wątków oraz awarie spowodowane nieoczekiwanymi kombinacjami stanu środowiska wykonawczego – wszystkie te problemy wymagają wykrycia przez analizę dynamiczną, profilowanie i testy obciążeniowe.
Błędy logiki biznesowej Błędy zależne od wiedzy o domenie nie są wykrywalne za pomocą analizy statycznej. Funkcja, która niepoprawnie oblicza odsetki z powodu błędnego wzoru, raport agregujący dane z użyciem niewłaściwej granicy czasowej lub kontrola autoryzacji przyznająca dostęp niewłaściwej grupie użytkowników: są to błędy poprawności, które wymagają semantycznej wiedzy o tym, co kod ma robić. Analiza statyczna może zweryfikować zgodność kodu ze wzorcami strukturalnymi, ale nie może zweryfikować, czy kod implementuje poprawne zachowanie biznesowe. Testowanie funkcjonalne i przegląd specyfikacji obejmują ten obszar.
Luki w konfiguracji problemy występujące w artefaktach wdrożeniowych, definicjach infrastruktury i ustawieniach środowiska, a nie w kodzie źródłowym, są częściowo objęte nowoczesną analizą statyczną za pośrednictwem analizy infrastruktury jako kodu, ale wiele problemów z konfiguracją jest widocznych tylko w czasie wykonywania lub w interakcji między kodem a jego środowiskiem wykonawczym.
Złożone luki w uwierzytelnianiu i autoryzacji Analizy statyczne, obejmujące wiele komponentów, uwzględniające stan sesji lub zależne od interakcji wielu kontroli bezpieczeństwa w łańcuchu połączeń, są trudne do prawidłowego wnioskowania. W tej kategorii często występują wyniki fałszywie pozytywne i fałszywie negatywne, a ich ocena wymaga konsultacji z ekspertami.
Ocena i wybór narzędzi do analizy statycznej
Wybór narzędzia do analizy statycznej to problem dopasowania: możliwości którego narzędzia odpowiadają wymaganiom bazy kodu, zespołu i organizacji? Wśród wymiarów, w których narzędzia różnią się znacząco, znajdują się: obsługa języków, głębokość analizy, wskaźnik fałszywych trafień, obsługa integracji i skalowalność.
wsparcie językowe jest ograniczeniem początkowym. Narzędzie, które nie obsługuje języka w bazie kodu, nie wnosi żadnej wartości do tej bazy. W środowiskach wielojęzycznych wybór polega na wyborze między wieloma narzędziami jednojęzycznymi (z których każde dobrze obsługuje swój język, ale nie zapewnia analizy międzyjęzykowej) a ujednoliconą platformą obejmującą wiele języków ze zintegrowanym rozwiązywaniem zależności międzyjęzykowych. W systemach korporacyjnych z dużą ilością starszego kodu i nowoczesnymi komponentami, podejście oparte na ujednoliconej platformie jest zazwyczaj konieczne, ponieważ zależności międzyjęzykowe to właśnie relacje, których narzędzia jednojęzyczne nie są w stanie przedstawić.
Głębokość analizy Określa, jakie kategorie problemów narzędzie może znaleźć. Narzędzie działające wyłącznie na poziomie leksykalnym i składniowym nie znajdzie luk w przepływie danych ani martwego kodu. Narzędzie implementujące pełną międzyproceduralną analizę przepływu danych znajdzie więcej luk, ale będzie również generować więcej fałszywych alarmów i wymagać większych zasobów obliczeniowych. Odpowiednia głębokość zależy od profilu ryzyka bazy kodu: krytyczne dla bezpieczeństwa systemy finansowe lub opieki zdrowotnej zazwyczaj uzasadniają głęboką analizę przepływu danych, podczas gdy wewnętrzne bazy kodu narzędzi mogą być odpowiednio obsługiwane przez lżejszą analizę strukturalną.
Fałszywie pozytywny wskaźnik stanowi praktyczne ograniczenie dla adopcji. Narzędzie, które sygnalizuje dużą liczbę nieistotnych problemów w każdej analizowanej bazie kodu, zostanie skonfigurowane tak, aby ignorować te problemy, co oznacza, że zespół traci korzyści płynące z tych reguł analizy, ponosząc jednocześnie stałe koszty tłumienia wyników. Współczynnik wyników fałszywie dodatnich jest funkcją zarówno jakości analizy narzędzia, jak i specyfiki stosowanych reguł. Zespoły oceniające narzędzia powinny testować je na reprezentatywnej próbce własnego kodu i mierzyć stosunek wyników możliwych do podjęcia działań do wyników tłumionych, a nie polegać na testach porównawczych dostarczanych przez dostawców w syntetycznych bazach kodu.
Integracja CI/CD i IDE Określa, czy narzędzie jest używane w praktyce, czy traktowane jako okazjonalna czynność audytowa. Narzędzie wymagające osobnego, ręcznego uruchomienia i generujące wyniki w osobnym interfejsie będzie używane mniej konsekwentnie niż narzędzie, które prezentuje wyniki w IDE programisty podczas pisania kodu i odrzuca żądania ściągnięcia (pull request), które wprowadzają nowe naruszenia. Jakość integracji jest praktycznym czynnikiem adaptacji, który jest równie ważny jak jakość analizy dla osiągnięcia spójnego pokrycia.
Skalowalność staje się wiążącym ograniczeniem w dużych bazach kodu. Narzędzie, którego analiza bazy kodu liczącej milion wierszy zajmuje godziny, nie może zostać zintegrowane z procesem zatwierdzania zmian (commit) ani żądaniami ściągnięcia (pull request). Analiza przyrostowa, która przy każdym uruchomieniu ponownie analizuje tylko zmienione pliki i ich zależności, a nie całą bazę kodu, to mechanizm techniczny, który umożliwia statyczną analizę poszczególnych zatwierdzeń na dużą skalę. Narzędzia należy oceniać pod kątem ich możliwości analizy przyrostowej, a także wydajności pełnego skanowania.
Analiza statyczna w środowiskach wielojęzycznych przedsiębiorstw
Wyzwania związane z analizą statyczną znacznie rosną w środowiskach korporacyjnych, gdzie baza kodu obejmuje wiele języków, wiele platform i dekady nagromadzonego kodu. Podejścia analityczne, które dobrze sprawdzają się w jednojęzycznej, nowo powstałej bazie kodu, często zawodzą w takich środowiskach, ponieważ narzędzia nie obsługują obecnych języków, nie potrafią modelować zależności międzyjęzykowych lub wzorce strukturalne starszego kodu nie odpowiadają założeniom wbudowanym w narzędzia zaprojektowane dla nowoczesnych baz kodu.
Na przykład programy COBOL mają model strukturalny oparty na podziałach, sekcjach i akapitach, który zasadniczo różni się od modelu funkcji i klas, stosowanego w większości frameworków analizy statycznej. Wspólne definicje oparte na kopiach, zakresy akapitów PERFORM-THRU oraz konwencje nazewnictwa danych wykorzystujące łączniki zamiast znaków camelCase lub podkreśleń to strukturalne cechy COBOL-a, z którymi narzędzia niezależne od języka zazwyczaj radzą sobie słabo lub wcale. Biblioteka JCL, która koordynuje wykonywanie programów wsadowych na komputerach mainframe i definiuje zbiory danych przepływające między nimi, nie jest w ogóle analizowana przez żadną platformę analizy statycznej ogólnego przeznaczenia.
W rezultacie, w organizacjach, które opierają się na komputerach mainframe i starszych platformach, a także na nowoczesnych usługach, powstaje strukturalna luka w pokryciu kodu: narzędzia do analizy statycznej obejmują współczesny kod kompleksowo, a starszy kod wcale, lub obejmują każdy język osobno, nie zapewniając wglądu w relacje między nimi. Ta luka jest najbardziej dotkliwa właśnie tam, gdzie najtrudniej ją załatać: w interfejsach międzyjęzykowych, gdzie zmiana w programie COBOL wpływa na usługę Java odczytującą jego dane wyjściowe, lub gdzie zmiana schematu w bazie danych wpływa jednocześnie na starsze przetwarzanie wsadowe i nowoczesne warstwy API. Jak opisano w kontekście planowanie modernizacji komputerów mainframe oraz Przejścia na platformę IBM i RPG, umiejętność zrozumienia aktualnego stanu całego portfolio aplikacji, w tym starszych komponentów, jest warunkiem koniecznym do zaplanowania jakiegokolwiek programu modernizacji, który nie stwarza nowych ryzyk, a jednocześnie zajmuje się tymi już istniejącymi.
W jaki sposób SMART TS XL Zapewnia statyczną analizę kodu w całym przedsiębiorstwie
SMART TS XL opiera się na założeniu, że bazy kodu przedsiębiorstwa wymagają analizy na poziomie systemu, a nie plików czy repozytorium. Platforma Software Intelligence pobiera kod źródłowy z każdego języka i platformy w środowisku, w tym COBOL, JCL, Java, .NET, Python, JavaScript, TypeScript, SQL i innych, i analizuje każdy z nich, wykorzystując analizę specyficzną dla danego języka, tworząc ujednolicony model odniesień krzyżowych. Model ten reprezentuje strukturalne relacje całego systemu: grafy wywołań obejmujące granice języków, ślady przepływu danych na poziomie pól, które podążają za wartościami z definicji COBOL, przez kolumny bazy danych do usług Java, grafy przepływu sterowania, które pokazują, które ścieżki kodu są aktywne, a które nie, oraz mapy zależności, które identyfikują każdy komponent, na który ma wpływ proponowana zmiana.
rozwiązanie do analizy kodu statycznego że SMART TS XL provides nie jest zbiorem linterów dla poszczególnych języków koordynowanych za pośrednictwem wspólnego pulpitu. Jest to ujednolicona platforma analityczna, która modeluje system jako całość, umożliwiając analizę międzyjęzykową i międzykomponentową, wymaganą przez środowiska korporacyjne. Deweloper pytający „na co wpłynie zmiana tej funkcji?” otrzymuje pełną odpowiedź zaczerpniętą z ujednoliconego grafu zależności, a nie częściową odpowiedź z narzędzia jednojęzycznego obejmującego aktualnie przeglądany plik. Analityk bezpieczeństwa przeprowadzający analizę skażenia śledzi poufne dane w systemie od źródła do ujścia, niezależnie od tego, ile granic językowych dane przekraczają. Zespół modernizacyjny planujący migrację ma pełną widoczność tego, które komponenty od czego zależą, uporządkowaną według warstw, języków i określonego typu relacji, a nie widok ograniczony do komponentów, które akurat korzystają z nowoczesnych narzędzi.
SMART TS XLFunkcja wyszukiwania korporacyjnego stanowi punkt wyjścia do analizy, zwracając wyniki uporządkowane według typu relacji strukturalnej, a nie według wystąpienia ciągu znaków: definicje, wywołania, operacje odczytu i zapisu, inkluzje copybook, odwołania SQL i ekspozycje API są rozróżniane w zestawie wyników, dając programistom konkretne informacje, których potrzebują, bez konieczności filtrowania listy dopasowań tekstowych. Wizualizacja kodu przekłada dogłębną analizę strukturalną na łatwe w nawigacji schematy blokowe i diagramy zależności, które sprawiają, że złożone systemy stają się zrozumiałe bez konieczności sekwencyjnego czytania każdej linijki kodu przez programistów.
Analiza statyczna jako fundament, a nie cel
Analiza statyczna jest najcenniejsza, gdy traktuje się ją jako infrastrukturę, a nie narzędzie: coś, co działa nieprzerwanie na całym kodzie, generuje wyniki, które są systematycznie weryfikowane i których wyniki są powiązane z procesem rozwoju oprogramowania, a nie konsultowane sporadycznie. Organizacje, które osiągają ten poziom integracji, przekonują się, że analiza statyczna stopniowo przesuwa działania związane z jakością i bezpieczeństwem z reaktywnego usuwania problemów, gdzie są one wykrywane po fakcie, na proaktywne zapobieganie, gdzie wzorce związane z problemami są eliminowane, zanim zdążą je spowodować.
Inwestycja w osiągnięcie tego celu nie jest przede wszystkim inwestycją w narzędzia. Cięższa praca jest kulturowa i procesowa: ustalenie oczekiwania, że wyniki analizy statycznej będą uwzględniane, a nie tłumione, skonfigurowanie narzędzia w celu zrównoważenia głębokości z odsetkiem fałszywych trafień dla konkretnej bazy kodu, zintegrowanie wyników z procesem tworzenia i ciągłej integracji (IDE) tak, aby były one uwzględniane w fazie rozwoju, a nie w oddzielnej fazie przeglądu, oraz utrzymanie konfiguracji w miarę rozwoju bazy kodu. Narzędzia to umożliwiają; praktyka organizacyjna to podtrzymuje. W przypadku przedsiębiorstw obsługujących systemy obejmujące wiele języków, wiele platform i wiele dekad nagromadzonego kodu, fundament narzędziowy musi być w stanie objąć ten pełny zakres. Wartość analizy statycznej obejmującej 80% bazy kodu nie stanowi 80% wartości pełnego pokrycia; jest ona ograniczona przez ryzyka, które występują w 20%, które nie są objęte.