Przeprowadziliśmy audyt google/zx z i bez umiejętności recenzowania

Przeprowadziliśmy audyt google/zx z i bez umiejętności recenzowania

Ławka Umiejętności, część 4 z 4. Ten sam model, to samo zapytanie, jedna umiejętność zainstalowana lub nie. Również w serii: strona docelowa, bot Telegram i debugowanie gry w węża.

Trzy wpisy w tej serii, wzorzec był spójny: ramię umiejętności robi więcej, sprawdza więcej, zużywa więcej tokenów i daje bardziej przemyślany wynik. Ten wpis łamie ten wzorzec. To jedyny raz w całym teście, kiedy uruchomienie z umiejętnością było tańsze niż bazowe, a także jedyny raz, kiedy ramię umiejętności przeoczyło największe pojedyncze odkrycie w serii. Obie te rzeczy są prawdziwe jednocześnie, a napięcie między nimi jest najbardziej użytecznym wynikiem, jaki uzyskaliśmy z tego projektu.

Dlaczego google/zx

Chcieliśmy zadania audytu kodu o trzech właściwościach: prawdziwy kod, a nie syntetyczny fragment z wstrzykniętymi błędami; coś, co deweloper średniego poziomu mógłby prawdopodobnie zostać poproszony o przejrzenie we wtorek; oraz baza kodu wystarczająco znana, aby czytelnicy mogli sprawdzić nasze twierdzenia w stosunku do rzeczywistego źródła, zamiast ufać nam w ciemno.

google/zx spełniało wszystkie trzy. To biblioteka Google do pisania skryptów shellowych w JavaScript, wystarczająco popularna, że znacząca część narzędzi Node na niej polega, i wystarczająco mała, że audyt pojedynczego pliku jest uczciwym zadaniem, a nie projektem badawczym. Wybraliśmy src/core.ts, plik, który faktycznie zajmuje się uruchamianiem procesów i obsługą środowiska, pobrany świeżo z repozytorium. Nic tutaj nie jest wymyśloną luką. To prawdziwy, aktywnie utrzymywany plik, a zx jest ogólnie dobrze zbudowany: czysta struktura, rozsądne wartości domyślne w większości miejsc, rodzaj kodu, który przechodzi swobodne czytanie. To jest dokładnie takie ustawienie, w którym audyt albo się opłaca, znajdując jedną rzecz, którą swobodne czytanie przeoczy, albo nie.

Metodologia

Dwa uruchomienia, ten sam model, ta sama instrukcja: przeprowadź audyt src/core.ts z google/zx i przedstaw wyniki sklasyfikowane od P0 do P3, każdy z lokalizacją, opisem co się psuje, kiedy się psuje i minimalną poprawką. Uruchomienie pierwsze otrzymało tę instrukcję i nic więcej. Uruchomienie drugie otrzymało identyczną instrukcję plus naszą własną listę kontrolną przeglądu bezpieczeństwa i kodu, tę, która jest dostarczana w naszych Security Pack i Developer Toolkit, przeczytaną w całości przed rozpoczęciem audytu.

To samo zastrzeżenie, co w każdym innym wpisie w tej serii: to jedno uruchomienie na ramię, jeden model, jeden plik. Nie twierdzimy, że konkretne liczby odkryć powtórzą się przy ponownym uruchomieniu. Raportujemy to, co się wydarzyło, z prawdziwą telemetrią uprzęży testowej i wzorcem w rodzaju rzeczy, które każde ramię wychwytuje, co naszym zdaniem uogólnia się lepiej niż surowe liczby.

Co znalazła linia bazowa

Pozostawiony sam sobie, model przeczytał core.ts i z własnej inicjatywy pobrał również util.ts i error.ts dla kontekstu, zanim cokolwiek napisał. Nikt mu tego nie kazał. Zdecydował, że plik nie ma sensu w izolacji i poszukał w sąsiednich plikach, co okazało się ważne.

Najważniejsze odkrycie linii bazowej i odkrycie o najwyższej ważności w całym teście, to właśnie to. W linii 139, core.ts domyślnie ustawia opcję środowiska na env: process.env. To nie jest kopia środowiska, to jest do niego żywe odniesienie. Kiedy już to wiesz, konsekwencja jest prosta: jeśli jakakolwiek ścieżka kodu robi coś takiego jak $.env.FOO = 'x', nie ustawia zmiennej ograniczonej do tego jednego wywołania shella. Mutuje process.env dla całej działającej aplikacji, co oznacza, że każda inna część programu, każda inna biblioteka, wszystko, co odczytuje zmienne środowiskowe po tym punkcie, widzi zmianę. Modyfikacja konfiguracji przeznaczona dla jednego wywołania podprocesu wycieka do stanu globalnego. W długo działającym procesie serwerowym, lub w jakimkolwiek skrypcie, który rozgałęzia wiele wywołań zx z nieco innymi środowiskami, jest to rodzaj błędu, który nie pojawia się w szybkim teście, a następnie korumpuje całkowicie niepowiązaną część systemu dni później. Zweryfikowaliśmy to bezpośrednio w stosunku do źródła, zamiast wierzyć modelowi na słowo, a linia robi dokładnie to, co mówi odkrycie.

Reszta listy linii bazowej, oceniona jako P2 i P3, była solidna, choć nie spektakularna: błąd wykrywania basha, który jest cicho ignorowany i później objawia się jako mylący, niepowiązany komunikat o błędzie zamiast prawdziwej przyczyny, oraz przypadek, w którym break() rzuca wyjątek synchronicznie z wnętrza handlera .catch(), co jest rodzajem błędu przepływu sterowania, który łatwo napisać i irytująco debugować, ponieważ ślad stosu wskazuje na niepomocne miejsce.

Pełny bilans linii bazowej: jeden P1, dwa P2, cztery P3.

DARMOWY PAKIET STARTOWY

Ciekawi Cię, co niekierowany audyt Claude wykryje w Twoim kodzie, zanim dodasz listę kontrolną? Nasz darmowy pakiet startowy to szybki sposób na przeprowadzenie drugiej weryfikacji.

Pobierz darmowy pakiet startowy

Co znalazło ramię z listą kontrolną

Uruchomienie z umiejętnością podążało za naszą listą kontrolną przeglądu kodu, tą samą, która została omówiona w naszym przeglądzie umiejętności bezpieczeństwa, i wyglądało to tak, jakby recenzował to zupełnie inny recenzent: ten sam plik, ten sam dostęp, ale pojawił się zupełnie inny zestaw problemów.

Jego najlepszym odkryciem było nieobsłużone odrzucenie obietnicy: miejsca wywołań dla break() i timeout() wywołują kill() bez oczekiwania na zakończenie, więc odrzucenie z tego wywołania nie ma gdzie wylądować i może spowodować awarię procesu poza jakimkolwiek blokiem try/catch napisanym przez wywołującego. Wykryło również TypeError, który pojawia się, gdy coś próbuje asynchronicznie iterować ProcessPromise po jej zatrzymaniu, co jest prawdziwym przypadkiem brzegowym, który pojawia się tylko w określonej sekwencji wywołań. Zaznaczyło również, że ZX_PREFIX i ZX_POSTFIX są wstawiane bezpośrednio do każdego wywołania shella bez sanitacji, co zasługuje na uwagę utrzymującego, jeśli którakolwiek z tych wartości może pochodzić spoza zaufanego autora skryptu.

To są prawdziwe, dobrze sformułowane odkrycia, a nie wypełniacze. Raport ramienia z listą kontrolną był również bardziej użyteczny z dwóch dokumentów na własnych warunkach: jednolita struktura, spójny język ważności, każdy wpis zgodny z tym samym formatem lokalizacja-wpływ-poprawka, bez konieczności wymyślania tego formatu przez model na bieżąco.

Pełny bilans listy kontrolnej: dwa P2, pięć P3. Brak P1, i co ważne, w ogóle brak odkrycia dotyczącego process.env.

Nakładanie się, i jak małe było

Ustaw obie listy obok siebie, a nakładanie się jest bliskie zeru. Sześć odkryć linii bazowej, siedem odkryć z listy kontrolnej, trzynaście łącznie, i żadne z nich nie wskazuje tej samej przyczyny źródłowej. Mutacja process.env. Połknięcie błędu wykrywania basha. Synchroniczne rzucenie wyjątku w catch. Nieoczekiwane kill(). TypeError zatrzymanego iteratora. Niesanitarny splice prefix/postfix. Każde z nich to inna ścieżka kodu.

To większe zaskoczenie niż którakolwiek z indywidualnych list. Dwóch kompetentnych recenzentów, patrzących na te same około 300 linii TypeScript, jeden kierowany przez ustrukturyzowaną listę kontrolną, a drugi nie, i lądują na prawie całkowicie rozłącznych zestawach problemów. Gdybyś nam powiedział na początku, że uruchomienie z listą kontrolną zasadniczo podwójnie sprawdzi listę bazową i doda szlifu, uwierzylibyśmy. Tak się nie stało. Lista kontrolna nie udoskonaliła tego samego wyszukiwania, przeprowadziła inne wyszukiwanie.

Liczby

Linia bazowa (bez umiejętności) Ramię umiejętności (lista kontrolna przeglądu kodu) Delta
Zużyte tokeny 87,899 81,743 −7%
Odkrycia P1 1 0 −1
Odkrycia P2 2 2 0
Odkrycia P3 4 5 +1
Łącznie odkryć 7 7 0
Proaktywnie czytało sąsiednie pliki Tak (util.ts, error.ts) Nie
Odkrycie o najwyższej ważności mutacja process.env (zweryfikowana)

Każdy inny wpis w tej serii pokazywał, że ramię umiejętności kosztuje więcej tokenów dla bardziej zdyscyplinowanego wyniku. To jest jeden wyjątek: ramię z listą kontrolną było o 7% tańsze. Nasza interpretacja jest taka, że lista kontrolna zawęża przestrzeń wyszukiwania, a węższa przestrzeń wyszukiwania jest tańsza do wykonania, nawet jeśli jest to również taka, która zamyka niektóre ścieżki, którymi swobodny przebieg by podążał.

Dół vs. góra

To jest najostrzejsza rzecz, której nauczyliśmy się we wszystkich czterech wpisach, i najbardziej wyraźnie widać ją tutaj. Lista kontrolna sprawiła, że audyt był tańszy i nadała mu bardziej rygorystyczny, spójniejszy format. Czego nie zrobiła, to nie znalazła błędu process.env, ponieważ to odkrycie nie pochodziło z żadnej kategorii na liście kontrolnej. Pochodziło z linii bazowej, która zauważyła, że sam core.ts był trudny do zrozumienia, samodzielnie zdecydowała się pobrać util.ts i error.ts, i podążyła za tym przeczuciem w miejsce, na które struktura listy kontrolnej nigdy nie wskazała.

Lista kontrolna podnosi poziom bazowy. Gwarantuje minimalny standard pokrycia: każda kategoria jest sprawdzana, każde odkrycie jest zapisywane w ten sam sposób, i nie tracisz łatwego wykrycia z powodu gorszego dnia. Nie podnosi pułapu. Najlepsze możliwe odkrycie w danym pliku może znajdować się poza każdą kategorią wymienioną na liście kontrolnej, a proces, który tylko przechodzi przez listę kontrolną, przejdzie obok niego, pewnie, w ładnie sformatowanym raporcie.

To nie jest argument przeciwko listom kontrolnym. Żadne z siedmiu odkryć listy kontrolnej nie było złe, a dwa z nich były rodzajem rzeczy, które zapracowany ludzki recenzent prawdopodobnie przeoczy pod presją czasu. To argument za wiedzą, do czego służy lista kontrolna. Jest to narzędzie podnoszące poziom bazowy, a nie pułap, a traktowanie jej jako obu to sposób, w jaki prawdziwy P1 prześlizguje się przez recenzję, która w innym przypadku wygląda na dokładną.

PAKIET SKILLPROOF

Dokładna lista kontrolna, która została użyta w tym teście, ta, która znalazła nieoczekiwane kill() i niesanitarny splice prefiksu, jest dostarczana w naszym Security Pack wraz z resztą naszych najwyżej ocenianych umiejętności recenzowania.

Pobierz Security Pack — $10

Jak samodzielnie przeprowadzić audyt dwuetapowy

Biorąc pod uwagę to, co znaleźliśmy, nasza rzeczywista rekomendacja nie brzmi „użyj listy kontrolnej” ani „pominięcie listy kontrolnej”. Chodzi o uruchomienie obu, w przypadku wszystkiego, co ma znaczenie.

Zacznij od swobodnego przebiegu. Skieruj model na plik, daj mu instrukcję audytu i pozwól mu czytać wszystko, co chce. Nie dawaj mu rubryki. To jest przebieg, który ma największe szanse na wykrycie czegoś, czego nikt nie pomyślał, aby umieścić na liście, ponieważ nie jest ograniczony do listy.

Następnie przeprowadź drugi, oddzielny przebieg z ustrukturyzowaną listą kontrolną, naszą lub własną. To jest przebieg, który gwarantuje pokrycie: kategorie, które są nudne do ręcznego sprawdzania, ale łatwe do pominięcia, gdy podążasz za przeczuciem, sanitacja, obsługa błędów, czyszczenie zasobów, są sprawdzane za każdym razem.

Porównaj oba raporty, zanim uznasz którykolwiek za ostateczny. Jeśli nasza liczba nakładania się utrzyma się w Twoim kodzie tak, jak w przypadku zx, spodziewaj się, że obie listy będą miały mniej niż połowę wspólnych odkryć. Traktuj to jako oczekiwany wynik, a nie znak, że którykolwiek przebieg się nie powiódł. Pełny protokół testowania opisuje bardziej szczegółowo, jak strukturyzujemy tego rodzaju sparowane uruchomienia, w tym jak kontrolujemy, aby model nie czytał własnych wcześniejszych wyników.

Jeśli masz budżet tylko na jeden przebieg, nasza szczera rada oparta na tym wyniku brzmi: najpierw uruchom swobodny przebieg. To ten, który ma większe szanse na znalezienie odkrycia, którego nie wiedziałeś, że szukasz. Następnie, jeśli czas pozwoli, uzupełnij go listą kontrolną, aby upewnić się, że nic nudnego nie zostało pominięte. Aby uzyskać szerszy przegląd, które umiejętności dobrze radzą sobie z tego rodzaju przeglądem, zobacz nasz przegląd najlepszych umiejętności kodowania.

FAQ

Czy odkrycie process.env to prawdziwa luka w zx? To prawdziwe zachowanie warte uwagi utrzymującego, nie ujawnione CVE i nie coś, co przedstawiamy jako aktywny exploit. zx to ogólnie dobrze zbudowana, aktywnie utrzymywana biblioteka, a to jest wybór projektowy, żywe odniesienie zamiast kopii, które ma prawdziwą konsekwencję mutacji dla każdej ścieżki kodu, która zapisuje do $.env. Zweryfikowaliśmy tę linię sami w stosunku do źródła, zamiast ufać raportowi modelu, i właśnie dlatego czujemy się komfortowo, nazywając to najważniejszym odkryciem całego testu.

Dlaczego ramię umiejętności kosztowało tutaj mniej, skoro wszędzie indziej w tej serii kosztowało więcej? Nasze najlepsze wyjaśnienie to zakres. Umiejętność projektowania w wpisie o stronie docelowej zachęca do iteracji: sprawdź wynik, popraw, sprawdź ponownie. Lista kontrolna przeglądu działa inaczej. Definiuje stały zestaw kategorii do jednorazowego przejścia, co zawęża wyszukiwanie, zamiast je rozszerzać. Węższe wyszukiwanie, mniej tokenów. To wiarygodny mechanizm, ale nie udowodniony, ponieważ jest to pojedyncze uruchomienie.

Czy powinienem ufać recenzji kodu AI kierowanej listą kontrolną, że wychwyci wszystko? Nie, i to jest tutaj najważniejsze odkrycie. Lista kontrolna to narzędzie podnoszące poziom bazowy: gwarantuje spójne minimalne przeszukanie znanych kategorii. Nie jest to narzędzie podnoszące pułap, a największe odkrycie w całym tym teście pochodziło z przebiegu, który nie podążał za listą. Używaj listy kontrolnej dla pokrycia i spójności. Nie używaj jej jako jedynego przebiegu dla kodu, który naprawdę ma znaczenie.

Co to oznacza dla wyboru umiejętności recenzowania kodu Claude w praktyce? Nie traktuj „która umiejętność” jako jedynej decyzji. Traktuj „ile przebiegów” jako ważniejszą. Dobra umiejętność listy kontrolnej, taka jak ta w naszym wykazie listy kontrolnej przeglądu kodu, jest warta zainstalowania ze względu na spójność i kategorie, które gwarantuje. Połącz ją z co najmniej jednym niekierowanym przebiegiem na wszystkim, co faktycznie byś wdrożył, i przeczytaj nasz przegląd umiejętności bezpieczeństwa, aby dowiedzieć się, jak ważymy umiejętności recenzowania względem siebie w katalogu.

To tyle z serii. Cztery zadania, cztery uczciwe werdykty, a wspólny wątek we wszystkich jest ten sam: umiejętność zmienia to, co model sprawdza, a nie to, czy jest zdolny do wykonania pracy. To, czy ta wymiana jest warta zachodu, zależy całkowicie od tego, co budujesz i jak dokładnie ktoś będzie to później sprawdzał.

★ 9.6/10 × 3

Darmowy pakiet startowy

3 skille z naszymi najwyższymi ocenami z testów plus checklista instalacji — zestaw, który sami wgralibyśmy na świeżą maszynę. Za darmo, na e-mail.

Jeden e-mail z pakietem + krótki cotygodniowy przegląd nowych wyników testów. Wypiszesz się, kiedy chcesz.