Zaszyfrowana paczka ważyła 313 MB i leżała na dysku laptopa, w katalogu roboczym narzędzia używanego do pisania kodu. Obok niej stał plik ze stanem, czytelny bez żadnego klucza, a w nim licznik nieudanych prób wysyłki: 564. Tak wyglądało odkrycie, które 18 września opisał programista podpisujący się jako ferstar. Narzędzie to ZCode, pulpitowe AI do programowania od chińskiej firmy Z.ai, znanej z otwartych modeli GLM. Firma odniosła się do sprawy tego samego dnia po południu. Trzy dni później opublikowała kod ZCode na licencji Apache 2.0. Dopiero to otwarcie pozwala sprawdzić, czy jej wyjaśnienia trzymają się tego, co narzędzie faktycznie robiło.
Co badacz znalazł w katalogu roboczym ZCode
Badacz znalazł zaszyfrowaną migawkę katalogu roboczego z projektem komercyjnym. Punkt wyjścia był przy tym banalny: robił miejsce na dysku 256-gigabajtowego MacBooka i zauważył, że katalog ZCode zajmuje ponad 700 MB.
Metadane migawki leżały obok, zapisane otwartym tekstem. Katalog roboczy ważył 345,5 MB, a zaszyfrowana paczka 313 MB. Manifest wyliczał 42 411 plików.
Najwięcej mówi rozbicie tej paczki na kategorie. Aż 86,6% jej zawartości stanowił katalog .git: obiekty kolejnych zmian, dzienniki operacji na gałęziach i dane Git LFS, czyli systemu przechowywania dużych plików.
To jest istotniejsze, niż brzmi. Katalog .git nie jest zdjęciem bieżącego stanu projektu. Zawiera całą historię repozytorium: klucze i konfiguracje skasowane w późniejszych commitach, nazwy niewypchniętych gałęzi, adresy wewnętrznych serwerów w ustawieniach.
Po rozpakowaniu aplikacji ferstar odtworzył ścieżkę wysyłki. Klient prosił serwer o poświadczenia, dostawał podpis formularza do magazynu obiektowego i jednorazowy klucz publiczny, pakował dane lokalnie, szyfrował i wysyłał bezpośrednio do magazynu. Zastosowano szyfrowanie kopertowe, w którym klucz symetryczny opakowano kluczem publicznym wydanym przez serwer. Klucz prywatny nigdy nie trafiał na maszynę użytkownika, więc właściciel kodu nie mógł otworzyć paczki leżącej na jego własnym dysku.
Dwa przełączniki w ustawieniach, które wyglądały na wyłączniki tej funkcji, sterowały według jego analizy czym innym: zgodą na trenowanie i indeksowaniem po stronie serwera. Samo pakowanie szło dalej.
Paczka 313 MB nigdy nie wyszła, ale wyszła inna
Paczka 313 MB nie opuściła komputera, ale serwer przyjął mniejszą paczkę z publicznym repozytorium. Tę część relacje prasowe najczęściej skracają i wychodzi z tego nieprawda.
Archiwum z projektem komercyjnym przekraczało limit rozmiaru i dlatego licznik nieudanych prób doszedł do 564. ferstar sprawdził zapisy połączeń na własnym routerze i potwierdził, że te dane nie opuściły sieci lokalnej.
Wyszedł natomiast inny katalog roboczy: publiczne repozytorium, 538 plików, około 15 kB po spakowaniu i zaszyfrowaniu. Serwer je przyjął. Mechanizm więc działał, a jedyną przeszkodą w tym konkretnym przypadku był rozmiar pliku.
Wszystkie te liczby pochodzą od jednego badacza i tak trzeba je czytać. Nie są ustaleniem audytu ani danymi firmy.
Co powiedziała firma i czego to wyjaśnienie nie tłumaczy
Z.ai odniosła się do sprawy 18 września po południu, w swojej społeczności użytkowników. Według relacji ferstara firma wskazała jako źródło problemu indeksowanie bazy kodu, używane do indeksów lokalnych, przywracania punktów kontrolnych sesji i funkcji Repo Wiki. Generowanie stron Wiki w chmurze miało “móc” pociągać za sobą wysyłkę danych repozytorium. Po wygenerowaniu dane miały być natychmiast niszczone. Funkcja była domyślnie włączona we wczesnym okresie, część użytkowników została dotknięta, a problem został naprawiony.
Do tego doszły zapowiedzi: otwarcie kodu, przegląd przez podmioty zewnętrzne i dodatkowy reset limitów dla wszystkich użytkowników jako rekompensata.
Stanowiska nie czytaliśmy u źródła, bo post w społeczności i wpis firmy nie były dla nas dostępne. Powyższy opis jest relacją z drugiej ręki i tak jest tu oznaczony.
Sprawdziliśmy wyjaśnienie w opublikowanym kodzie
W opublikowanym kodzie nie ma potoku wysyłkowego opisanego przez badacza. Repozytorium zai-org/ZCode powstało 20 września, a my sklonowaliśmy je i przeszukaliśmy lokalnie, zamiast oglądać przez przeglądarkę.
Wyszukiwanie ciągów charakterystycznych dla opisanego mechanizmu nie zwraca niczego: szyfrowanie strumieniowe, opakowywanie klucza, migawka repozytorium, wysyłka formularzem do magazynu obiektowego, wyzwalacz przed każdym zapytaniem. Zero trafień w 6973 plikach.
Jedno doprecyzowanie wobec relacji badacza. Ciąg upload-credential w repozytorium jednak występuje, w dwóch plikach, ale dotyczy czegoś innego. To adres do wysyłania załączników przy zgłoszeniu od użytkownika oraz lista dozwolonych członów ścieżki w module telemetrii sieciowej. Adres chińskiego dostawcy chmury pada raz, w konfiguracji dostawców modeli. To nie jest ten sam mechanizm i wniosek badacza zostaje w mocy, ale “zero trafień” jest sformułowaniem zbyt mocnym.
Najważniejsze ustalenie dotyczy jednak samego wyjaśnienia firmy. Jeśli wysyłka całych repozytoriów była potrzebna do przywracania punktów kontrolnych, to w opublikowanym kodzie powinien zostać po tym ślad. Nie ma go. Katalog z obsługą punktów kontrolnych nie zawiera ani jednego wywołania sieciowego. Cała mechanika opiera się na lokalnym poleceniu git diff, a metadane zapisywane są na dysku użytkownika. Punkty kontrolne działają bez wysyłania czegokolwiek i działają tak w kodzie, który firma sama opublikowała.
Frontend Master 2026 · Kodożercy
Wreszcie rozumiesz, dlaczego kod od AI raz działa, a raz nie
Frontend Master 2026 to pakiet sześciu kursów Kodożerców, który daje Ci język do prowadzenia kodu od AI: poprawiania, łączenia, rozszerzania i szybkiego znajdowania linijki, którą trzeba zmienić. Bez teorii dla samej teorii.
Wchodzę w to →

Czego otwarcie kodu nie rozstrzyga
Otwarcie kodu nie pokazuje, jak działały wcześniejsze wersje ani co stało się z danymi wysłanymi wcześniej. Publikacja źródeł jest realnym ruchem i dzięki niej powstał ten tekst, ale ma granice, które warto nazwać.
Historia rozwoju została spłaszczona do dwóch zapisów: pustego początkowego i jednego zrzutu całego kodu. Nie da się prześledzić, jak powstał usunięty mechanizm ani jak go wycinano. Zgłoszenia są wyłączone, a żądania scalenia zablokowane, więc ruch idzie w jedną stronę.
Audyty, na które powołuje się firma, mówią o stanie po zdarzeniu. Potwierdzenie, że kubełek w chmurze jest pusty i że klient w nowej wersji nie generuje migawek, nie odpowiada na pytanie, co działo się z danymi wysłanymi wcześniej. Tego z badania przeprowadzonego po fakcie odtworzyć się nie da i badacz zwraca na to uwagę wprost.
Dochodzi jeszcze rozróżnienie, które w tej sprawie gubi się najczęściej. Otwarte wagi modelu i otwarty kod narzędzia to dwie różne rzeczy. Z.ai udostępnia wagi GLM, o czym pisaliśmy przy premierze GLM 5.3, ale aplikacja pulpitowa była do 20 września zamknięta. Otwarte wagi same z siebie nie czynią z takiej aplikacji prywatnego wdrożenia, bo o tym decyduje również to, co program wysyła poza komputer.
Co z tego wynika dla pracy z narzędziami AI
Praktyczny wniosek nie dotyczy jednej firmy. Agent kodujący ma z definicji dostęp do katalogu z projektem, a jego uprawnienia ustawia się w warstwie narzędzi. Opisany mechanizm działał poza tą warstwą, w procesie nadrzędnym wobec agenta, więc żadne ograniczenie uprawnień ani potwierdzanie operacji go nie dotyczyło.
Trzy rzeczy da się sprawdzić samodzielnie i żadna nie wymaga inżynierii wstecznej. Pierwsza to rozmiar i zawartość katalogu, w którym narzędzie trzyma swoje dane. Druga to lista połączeń wychodzących przy zalogowanym koncie. Zostaje jeszcze porównanie polityki prywatności z tym, co widać na dysku. Rozbieżność między dokumentem a zachowaniem zawsze rozstrzyga się na korzyść zachowania.
Sprawa dopisuje się do dłuższej listy. Opisywaliśmy niedawno przejęte konta w repozytorium pakietów Rusta, gdzie zagrożenie także przyszło narzędziem, któremu programista ufa z przyzwyczajenia.
Najczęstsze pytania
Czy kod z mojego repozytorium wyciekł, jeśli używałem ZCode?
Z publicznie dostępnych materiałów tego nie da się ustalić dla konkretnego użytkownika. Wiadomo, że mechanizm działał przy zalogowanym koncie i że przynajmniej jedna mała paczka została przez serwer przyjęta. Wiadomo też, że duże archiwa odbijały się od limitu rozmiaru. Firma twierdzi, że dane były niszczone natychmiast po wygenerowaniu strony Wiki. Wskazane przez nią podmioty audytujące potwierdziły co innego, czyli stan późniejszy: pusty, a następnie usunięty kubełek w chmurze oraz brak ścieżki wysyłkowej w nowej wersji klienta. Losu danych wysłanych przed ujawnieniem sprawy żadne z tych ustaleń nie odtwarza.
Czy otwarcie kodu oznacza, że problem został rozwiązany?
W opublikowanej wersji potoku wysyłkowego nie ma i sprawdziliśmy to samodzielnie w sklonowanym repozytorium. To odpowiada na pytanie o kod dostępny dzisiaj. Nie odpowiada na pytanie o wersje wcześniejsze, bo historia commitów została spłaszczona do jednego zrzutu. Nie odpowiada też na pytanie o to, co stało się z danymi, które zdążyły wyjść.
Podsumowanie
Programista podpisujący się jako ferstar opisał 18 września mechanizm w ZCode, pulpitowym narzędziu do programowania z AI od firmy Z.ai. Narzędzie pakowało katalog roboczy razem z całą historią Git, szyfrowało go kluczem wydanym przez serwer i wysyłało do magazynu w chmurze. Duża paczka z projektem komercyjnym nie opuściła komputera, bo przekraczała limit rozmiaru i licznik nieudanych prób zatrzymał się na 564. Mniejszą paczkę z publicznego repozytorium serwer przyjął, więc mechanizm działał. Firma przyznała istnienie funkcji, wskazała jako przyczynę indeksowanie bazy kodu i 21 września opublikowała źródła na licencji Apache 2.0. W udostępnionej wersji potoku wysyłkowego już nie ma, a punkty kontrolne działają na lokalnym Gicie, bez połączeń sieciowych. Nie wyjaśnia to jednak, co stało się z danymi, które zdążyły wyjść wcześniej.
Newsletter · DevstockAcademy & Kodożercy
Bądź na bieżąco ze światem IT, AI i automatyzacji
Co wtorek: newsy z branży, praktyczne tipy i narzędzia które warto znać. Zero spamu.



