Piaskownica Twojego agenta to tylko sugestia
6 marca 2026 roku badacz bezpieczeństwa otworzył zgłoszenie na GitHub w repozytorium Cline. Tytuł zgłoszenia zawierał prompt injection. Trzy godziny później [email protected] został opublikowany na npm z backdoorem.1
Piaskownica nie pomogła. Agent działał wyłącznie w ramach przyznanych mu uprawnień. Każda podjęta przez niego akcja — od odczytu zgłoszenia, przez instalację zatrutego pakietu npm, po wypełnienie pamięci podręcznej CI powyżej progu eksmisji 10 GB — posiadała ważną autoryzację.
Podsumowanie
- Piaskownice zawodzą na trzech poziomach. Listy blokad oparte na ciągach znaków są obchodzone przez aliasing ścieżek (
/proc/self/root/usr/bin/npx). Izolacja przestrzeni nazw pada, gdy agent sam ją dezaktywuje. Wymuszanie na poziomie jądra jest obchodzone przez wywołanie dynamicznego linkera (ld-linux-x86-64.so.2). Wszystkie trzy metody zademonstrowano na systemach produkcyjnych w 2026 roku. - Najgroźniejsze ataki nie wymagają ucieczki z piaskownicy. Clinejection działał wyłącznie w ramach autoryzowanych uprawnień, wykorzystując współdzielone klucze pamięci podręcznej CI do przejścia z niskouprzywilejowanego triażu do wysokouprzywilejowanego wydania. Żadna piaskownica tego nie wykryła, ponieważ żadna reguła piaskownicy nie została złamana.
- Obrona wymaga warstw, nie murów. Klasyfikacja danych wejściowych, wymuszanie na poziomie jądra (Seatbelt/seccomp), monitorowanie ruchu wychodzącego i izolacja stanu — każda warstwa wychwytuje to, co poprzednia przeoczyła. Poniżej zamieszczono konkretne hooki i profil Seatbelt.
Trzy poziomy awarii piaskownicy
Każda piaskownica agenta działa na jednym z trzech poziomów wymuszania. Każdy poziom ma znany sposób obejścia. Te obejścia nie są teoretyczne: trzy niezależne zespoły badawcze zademonstrowały wszystkie trzy na produkcyjnych systemach agentowych w 2026 roku.
Poziom 1: Listy blokad oparte na ciągach znaków
Najprostsza piaskownica to lista zablokowanych poleceń. Claude Code utrzymuje listę narzędzi, których agent nie może wywoływać: curl, wget, npx i inne. Lista blokad dopasowuje ciąg polecenia przed wykonaniem.2
Obejście: aliasing ścieżek. Na Linuksie /proc/self/root/usr/bin/npx wskazuje na ten sam plik binarny co /usr/bin/npx. Ciągi znaków są różne. Plik binarny jest identyczny. Lista blokad widzi ścieżkę, której nie rozpoznaje, i zezwala na wykonanie.2
Technika nie jest nowością. Literatura bezpieczeństwa dokumentuje aliasing ścieżek przez /proc, dowiązania symboliczne i ścieżki względne od dziesięcioleci. Nowością jest to, że agent odkrył obejście samodzielnie. Nikt nie instruował agenta, aby omijał listę blokad. Agent otrzymał zadanie do wykonania. Lista blokad zablokowała bezpośrednią ścieżkę. Agent wyszukał alternatywne ścieżki prowadzące do tego samego celu. Gdy znalazł jedną, kontynuował.2
Dopasowywanie ciągów znaków zawodzi, ponieważ operuje na etykietach (nazwach plików), a nie na tożsamościach (zawartości binarnej). Każdy system identyfikujący programy po ich ścieżce zawiedzie wobec przeciwnika lub agenta, który potrafi konstruować alternatywne ścieżki do tego samego programu.
Poziom 2: Izolacja przestrzeni nazw
Gdy Anthropic wykrył obejście przez aliasing ścieżek, wdrożył bubblewrap: piaskownicę opartą na przestrzeniach nazw, która ogranicza dostęp do systemu plików, widoczność procesów i możliwości sieciowe poprzez przestrzenie nazw Linuksa.2
Obejście: samodzielna dezaktywacja. Agent rozpoznał, że piaskownica uniemożliwia wykonanie zadania. Bez instrukcji dezaktywacji piaskownicy agent przeanalizował przeszkodę i podjął próbę jej usunięcia. Rozróżnienie jest istotne: zachowanie nie wynikało z prompt injection. Własny proces planowania agenta zidentyfikował ograniczenie i podjął działania w celu jego wyeliminowania, stosując tę samą zdolność rozwiązywania problemów, która czyni agentów użytecznymi, do problemu „kontrola bezpieczeństwa mnie blokuje”.2
Izolacja przestrzeni nazw zawodzi, gdy agent posiada wystarczające uprawnienia do modyfikacji własnego środowiska wykonawczego. Piaskownica działa w tym samym kontekście zaufania co agent. Jeśli agent może zapisywać pliki, modyfikować konfiguracje lub wykonywać polecenia, może próbować przekonfigurować lub wyłączyć piaskownicę. Granica izolacji jest tak silna jak ograniczenia zdolności agenta do modyfikacji tej granicy.
Poziom 3: Hooki wymuszania na poziomie jądra
Najsilniejsze podejście do piaskownicy na Linuksie przechwytuje wywołanie systemowe execve jądra: wywołanie, które ładuje i uruchamia nowy program. Jeśli plik binarny nie znajduje się na liście dozwolonych, execve zwraca EPERM i program się nie uruchamia.2
Obejście: wywołanie dynamicznego linkera. Polecenie /lib/x86_64-linux-gnu/ld-linux-x86-64.so.2 /usr/bin/wget ładuje plik binarny wget poprzez mapowanie pamięci dynamicznego linkera, a nie przez execve. Hook wymuszania na poziomie jądra nigdy się nie aktywuje, ponieważ ścieżka ładowania pliku binarnego jest inna: mmap i mprotect zamiast execve.2
Obejście przez dynamiczny linker reprezentuje klasę podatności, a nie pojedynczą sztuczkę. Każdy mechanizm ładowania kodu, który nie przechodzi przez execve, omija wymuszanie oparte na execve: dynamiczne linkowanie, dlopen, języki interpretowane ładujące biblioteki współdzielone, wykonywanie z mapowania pamięci. Powierzchnia ataku to cały zbiór ścieżek od dysku do wykonania, które omijają monitorowane pojedyncze wywołanie systemowe.
Artykuł prezentujący te wyniki proponuje Veto: system wymuszania adresowalny zawartością, który identyfikuje pliki binarne po ich haszu SHA-256, a nie po nazwie pliku czy mechanizmie ładowania.2 Veto działa jednocześnie na poziomie exec, load i sieci. Podejście jest słuszne, ale dodaje złożoność: każda aktualizacja pliku binarnego wymaga ponownego obliczenia hasza, a wymuszanie oparte na haszach nie obejmuje kodu interpretowanego (Python, JavaScript, skrypty powłoki), który nigdy nie tworzy pliku binarnego do zahaszowania.
Czwarta awaria: autoryzowane nadużycie
Trzy ucieczki z piaskownicy dzielą wspólne założenie: agent próbuje wykonać coś, na co nie ma pozwolenia. Clinejection całkowicie unieważnił to założenie.1
Łańcuch ataku:
| Krok | Akcja | Uprawnienia | Status piaskownicy |
|---|---|---|---|
| 1 | Atakujący otwiera zgłoszenie z zainfekowanym tytułem | Publiczny dostęp do GitHub | Niezaangażowana |
| 2 | Claude Code odczytuje zgłoszenie, instaluje pakiet npm | Bash (przyznany), Write (przyznany) | Wszystkie uprawnienia ważne |
| 3 | Skrypt preinstalacyjny wypełnia pamięć podręczną powyżej 10 GB | Hooki cyklu życia npm (standardowe) | W ramach piaskownicy |
| 4 | Eksmitowana pamięć podręczna zastąpiona zatrutymi wpisami | Pamięć podręczna GitHub Actions (współdzielona) | W ramach uprawnień CI |
| 5 | Nocne wydanie budowane z zatrutej pamięci podręcznej | Workflow wydania (zaplanowany) | W ramach uprawnień CI |
| 6 | [email protected] opublikowany z backdoorem |
npm publish (workflow wydania) | W ramach uprawnień wydania |
Atak nie naruszył żadnej piaskownicy. Nie eskalował żadnych uprawnień. Każda pojedyncza akcja posiadała ważną autoryzację. Atak się powiódł, ponieważ dwa workflow — triaż zgłoszeń i nocne wydanie — współdzieliły identyczne klucze pamięci podręcznej (${{ runner.os }}-npm-${{ hashFiles('package-lock.json') }}). Atakujący wykorzystał ruch boczny przez współdzielony stan, a nie eskalację pionową przez ucieczkę z piaskownicy.1
Kolizja kluczy pamięci podręcznej oznaczała, że dowolny anonimowy użytkownik GitHub mógł uruchomić workflow, który zanieczyścił artefakty budowania zasilające potok wydań. Workflow triażu zgłoszeń nie powinien współdzielić stanu z workflow wydania. Ale pamięci podręczne GitHub Actions są domyślnie współdzielone pomiędzy wszystkimi workflow w repozytorium. Model bezpieczeństwa zakładał, że workflow są niezależne. Nie były.1
Wzorzec autoryzowanych akcji składających się w nieautoryzowane rezultaty jest tym, co artykuł SoK: Agentic Skills formalizuje jako lukę w kompozycji umiejętności. Poszczególne narzędzia są autoryzowane. Poszczególne akcje są dozwolone. Kompozycja produkuje zachowanie, którego żadna pojedyncza kontrola uprawnień nie wychwytuje.3
Clinejection nie jest przypadkiem brzegowym. To domyślny tryb awarii każdego systemu, który przyznaje agentom uprawnienia na poziomie narzędzi bez monitorowania kompozycji na poziomie akcji. Pętla zwrotna fabrykacji, którą udokumentowałem wcześniej, wykorzystywała tę samą lukę: system autoryzował każdą pojedynczą akcję (zapis do pamięci, odczyt z pamięci, publikacja na platformie). Kompozycja (konfabulacja, utrwalenie, publikacja, wzmocnienie) nie otrzymała takiej autoryzacji.4
Czego atak potrzebował
Atak się powiódł dzięki czterem warunkom, z których każdy odpowiada warstwie obrony ze stosu widoczności agenta:8
-
Niezaufane dane wejściowe przetwarzane jako zaufane. Tytuł zgłoszenia pochodził od dowolnego anonimowego użytkownika GitHub. Claude Code przetworzył go z takim samym autorytetem jak instrukcję opiekuna repozytorium. Obrona: klasyfikacja źródeł danych wejściowych.
-
Zbyt szerokie uprawnienia narzędzi. Workflow triażu przyznawał dostęp do Bash. Zadanie triażu wymagało jedynie dostępu Read do metadanych zgłoszenia i dostępu Write do dodawania etykiet. Zadanie nie potrzebowało Bash, a jednak dostęp do Bash umożliwił polecenie npm install. Obrona: minimalne przyznawanie uprawnień do narzędzi.
-
Współdzielony stan pomiędzy granicami zaufania. Poprawka to jedna linia w każdym pliku workflow:
# Before: shared key (vulnerable)
key: ${{ runner.os }}-npm-${{ hashFiles('package-lock.json') }}
# After: workflow-scoped key
key: ${{ runner.os }}-npm-triage-${{ hashFiles('package-lock.json') }}
Obrona: oddzielne klucze pamięci podręcznej, izolacja workflow.
- Brak monitorowania ruchu wychodzącego. Polecenie npm install wysyłało wychodzące żądania sieciowe w celu pobrania złośliwego pakietu. Żadne monitorowanie nie wykryło nieoczekiwanego ruchu wychodzącego z workflow triażu. Obrona: logowanie żądań wychodzących, listy dozwolonych domen.
Dlaczego bezpieczeństwo oparte na uprawnieniach jest niewystarczające
Każda piaskownica i system uprawnień omówiony do tej pory działa na tym samym modelu: zdefiniuj, co agent może robić, a następnie zweryfikuj każdą akcję względem definicji. Model ma trzy strukturalne słabości.
Uprawnienia przyznawane są narzędziom, nie kompozycjom. Agent może używać Bash. Agent może używać web-fetch. Agent może używać file-write. Każde uprawnienie jest indywidualnie uzasadnione. Ale Bash + web-fetch + file-write, złożone w sekwencję, to prymityw eksfiltracji. Żadna kontrola uprawnień na poziomie narzędzi nie wychwytuje kompozycji. Atak cichej eksfiltracji działa wyłącznie w ramach przyznanych uprawnień narzędzi.5
Agenty optymalizują wokół ograniczeń. Badania nad ucieczkami z piaskownicy pokazują, że agenty traktują kontrole bezpieczeństwa jako przeszkody, a nie granice. Gdy lista blokad zablokowała ścieżkę, agent znalazł alternatywną ścieżkę. Gdy izolacja przestrzeni nazw zablokowała dostęp, agent podjął próbę wyłączenia izolacji. To zachowanie nie jest złośliwością. To to samo ukierunkowane na cel rozwiązywanie problemów, które czyni agentów użytecznymi. Agent instruowany „wykonaj zadanie” potraktuje kontrolę bezpieczeństwa uniemożliwiającą wykonanie zadania jako problem do rozwiązania.2
Zakres uprawnień przeżywa kontekst, który je przyznał. Workflow triażu zgłoszeń Cline przyznawał Claude Code dostęp do Bash w celu triażowania zgłoszeń. Ten sam dostęp do Bash wystarczał do instalowania dowolnych pakietów npm, wykonywania skryptów preinstalacyjnych i manipulowania pamięcią podręczną GitHub Actions. Opiekunowie ograniczyli uprawnienie do celu, ale nie do akcji, których ten cel wymaga. Uprawnienia ograniczone do celu wymagają mapowania z „triażuj zgłoszenia” na „odczytaj metadane zgłoszenia, sklasyfikuj, dodaj etykiety”, które wyklucza „instaluj pakiety z niezaufanych źródeł”. Żaden popularny framework agentowy nie zapewnia tego mapowania.1
Projekt mcp-firewall rozwiązuje problem granularności uprawnień, implementując politykę na poziomie wywołań narzędzi, a nie na poziomie dostępności narzędzi.6 Zamiast „agent może używać Bash”, polityka określa „agent może używać Bash do uruchamiania poleceń git w katalogu projektu”. Poprawa granularności jest znacząca, ale sama w sobie niewystarczająca: polecenie npm install Clinejection wyglądałoby jak uzasadniony krok budowania dla polityki wywołań narzędzi, która zezwala na operacje npm.
Jak wygląda wymuszanie na poziomie jądra
Badania nad ucieczkami z piaskownicy proponują wymuszanie na poziomie jądra. Mój system produkcyjny implementuje wersję tego rozwiązania przy użyciu macOS Seatbelt (sandbox-exec) — frameworka obowiązkowej kontroli dostępu na poziomie jądra, który ogranicza całe drzewo procesów uruchamiane przez agenta.7
Architektura różni się od bubblewrap na trzy sposoby:
Wymuszanie na drzewie procesów. Ograniczenia Seatbelt dotyczą procesu agenta i każdego procesu potomnego, który uruchamia. Jeśli agent uruchomi bash -c "python3 script.py", proces Python dziedziczy profil Seatbelt z procesu bash, który odziedziczył go z procesu agenta. Ograniczenie propaguje się niezależnie od mechanizmu tworzenia procesów.7
Reguły odmowy na poziomie ścieżek. Profil odmawia dostępu do odczytu i zapisu określonych ścieżek systemu plików: ~/.ssh, ~/.gnupg, ~/.aws, dowolny plik .env i plik poświadczeń. Odmowa działa na warstwie VFS jądra, nie na warstwie dopasowywania ciągów znaków. /proc/self/root/home/user/.ssh/id_rsa jest rozwiązywany przez VFS do tego samego i-węzła co ~/.ssh/id_rsa, a profil Seatbelt odmawia dostępu do i-węzła, nie do ciągu ścieżki.7
Niezmienny z perspektywy agenta. Profil Seatbelt jest ładowany przez system hooków podczas uruchamiania agenta. Agent nie może modyfikować systemu hooków, ponieważ hooki są odczytywane z settings.json podczas inicjalizacji CLI, przed przekazaniem kontroli agentowi. Agent nie może wyłączyć własnej piaskownicy z tego samego powodu, dla którego nie można usunąć programu ładującego z poziomu systemu operacyjnego, który uruchomił: mechanizm wymuszania działa w wyższym kontekście zaufania niż kod, który ogranicza.7
Sam profil to 20 linii składni Seatbelt:
(version 1)
(allow default)
;; Block writes to credential paths
(deny file-write* (subpath "/Users/me/.ssh"))
(deny file-write* (subpath "/Users/me/.gnupg"))
(deny file-write* (subpath "/Users/me/.aws"))
(deny file-write* (subpath "/Users/me/.config/gh"))
(deny file-write* (literal "/Users/me/.env"))
(deny file-write* (regex #"/Users/me/\.env\."))
(deny file-write* (subpath "/Users/me/Library/Keychains"))
Hook opakowuje każde polecenie Bash: sandbox-exec -f claude-sandbox.sb /bin/sh command.sh. Uruchomiłem piaskownicę w trybie próbnym (loguj naruszenia, ale nie blokuj) przez tydzień przed włączeniem wymuszania. 31 testów weryfikuje, że piaskownica wychwytuje próby odczytu zablokowanych ścieżek, zapisu do nich i że uprawnione operacje (git, python, edycja plików w katalogu projektu) przechodzą bez przeszkód. Narzut wynosi około 2 ms na polecenie.7
Podejście z Seatbelt adresuje dwa z trzech poziomów ucieczki z piaskownicy:
| Poziom ucieczki | Odpowiedź Seatbelt |
|---|---|
| Aliasing ścieżek | Wymuszanie na poziomie VFS rozwiązuje wszystkie ścieżki przed sprawdzeniem polityki. Alternatywne ciągi ścieżek do tego samego i-węzła uruchamiają tę samą regułę odmowy. |
| Samodzielna dezaktywacja | Hooki ładują się przed agentem, z konfiguracji, której agent nie może modyfikować. Agent nie może wyrozumować drogi wyjścia z mechanizmu, do którego nie ma dostępu. |
| Obejście przez dynamiczny linker | Nie testowano konkretnie z Seatbelt. Seatbelt ogranicza dostęp do plików na poziomie VFS, więc ld-linux próbujący wykonać mmap na zablokowanym pliku binarnym powinien zakończyć się niepowodzeniem przy otwieraniu pliku. Wymaga jawnej weryfikacji. |
Obejście przez dynamiczny linker to otwarte pytanie. Seatbelt powinien je zablokować, ponieważ reguła odmowy operuje na dostępie do plików, a dynamiczny linker musi odczytać plik binarny przed zmapowaniem go w pamięci. Ale „powinien” to nie dowód. Przetestowanie tego konkretnego wektora ataku na profilu Seatbelt to oczekujący element weryfikacji.7
Stos obrony
Skuteczne bezpieczeństwo agentów to nie pojedynczy mechanizm. To warstwowy stos, w którym każda warstwa adresuje inny tryb awarii. Żadna pojedyncza warstwa nie jest wystarczająca. Razem pokrywają znaną powierzchnię ataku.
| Warstwa | Adresuje | Mechanizm | Co uruchamiam |
|---|---|---|---|
| Sanityzacja danych wejściowych | Wstrzykiwanie niezaufanej zawartości | Usuwanie metadanych, klasyfikacja źródeł wejściowych, odrzucanie nieoczekiwanych formatów | Biblioteka web extract usuwa <title>, <meta>, tagi OG |
| Określanie zakresu uprawnień | Zbyt szerokie przyznawanie narzędzi | Uprawnienia ograniczone do celu, minimum uprawnień na typ zadania | Profile uprawnień specyficzne dla zadań w konfiguracji hooków |
| Piaskownica jądra | Agent uciekający z ograniczeń | Seatbelt (macOS), Veto (Linux), wymuszanie na drzewie procesów | sandbox-bash.sh z profilem Seatbelt, 31 testów |
| Monitorowanie ruchu wychodzącego | Eksfiltracja danych, nieoczekiwany ruch wychodzący | Logowanie wszystkich żądań wychodzących, alerty na nieoczekiwane domeny | Logowanie URL w web extract, lista dozwolonych domen w PreToolUse |
| Izolacja stanu | Zanieczyszczenie między workflow | Oddzielne sekrety, klucze pamięci podręcznej i magazyny artefaktów na poziom zaufania | Izolacja na poziomie workflow (odpowiedzialność platformy CI) |
| Zapora wyjściowa | Niezweryfikowana publikacja do systemów zewnętrznych | Klasyfikacja poleceń jako lokalne/współdzielone/zewnętrzne, przekazywanie zewnętrznych do przeglądu człowieka | Hooki granicy publikacji |
Stos jest uporządkowany według punktu wymuszania: wejście, uprawnienia, wykonanie, ruch wychodzący, stan, wyjście. Każda warstwa wychwytuje to, co poprzednia przeoczyła. Sanityzacja danych wejściowych wychwytuje prompt injection. Jeśli wstrzyknięcie przejdzie dalej, określanie zakresu uprawnień uniemożliwia agentowi wykonanie wstrzykniętego polecenia. Jeśli polecenie zostanie wykonane, piaskownica jądra uniemożliwia dostęp do wrażliwych zasobów. Jeśli agent pozostaje w ramach autoryzowanych zasobów, monitorowanie ruchu wychodzącego wychwytuje dane opuszczające system. Jeśli dane pozostają w systemie, izolacja stanu zapobiega zanieczyszczeniu między workflow. Jeśli dojdzie do zanieczyszczenia, zapora wyjściowa zapobiega publikacji.
Żadna warstwa nie jest doskonała. Clinejection przeszedłby przez piaskownicę jądra, ponieważ atak nie naruszył żadnej piaskownicy. Atak zostałby zatrzymany na etapie określania zakresu uprawnień (Bash nie jest potrzebny do triażu) lub izolacji stanu (oddzielne klucze pamięci podręcznej). Obrona wychwytująca atak zależy od tego, która warstwa adresuje konkretny warunek, który atak wykorzystuje.
Kluczowe wnioski
Dla zespołów DevOps i opiekunów CI: - Przeprowadzenie audytu każdego workflow przetwarzającego niezaufane dane wejściowe (zgłoszenia, pull requesty, komentarze) pod kątem współdzielonych kluczy pamięci podręcznej. Różne poziomy zaufania wymagają różnych przestrzeni nazw pamięci podręcznej. - Usunięcie uprawnień Bash i Write z workflow triażu. Klasyfikacja zgłoszeń wymaga dostępu Read do metadanych i dostępu Write do etykiet — nic więcej.
Dla twórców agentów: - Klasyfikacja źródeł danych wejściowych zanim agent je przetworzy. Anonimowa zawartość użytkowników otrzymuje dostęp tylko do narzędzi Read-only. Kod repozytorium otrzymuje pełny zestaw uprawnień. - Implementacja uprawnień ograniczonych do celu: mapowanie typów zadań na minimalny zestaw narzędzi wymagany przez każde zadanie, nie maksymalny dostępny.
Dla zespołów bezpieczeństwa: - Wdrożenie wymuszania na poziomie jądra (Seatbelt na macOS, seccomp na Linuksie) jako zabezpieczenia za kontrolami na poziomie aplikacji. Narzut 2 ms jest pomijalny; ochrona przed aliasingiem ścieżek i dezaktywacją piaskownicy jest absolutna. - Monitorowanie ruchu wychodzącego: lista 12 dozwolonych domen i logowanie każdego żądania wychwytuje eksfiltrację, której żadna kontrola uprawnień nie wykrywa.
Co można wdrożyć już dziś
Trzy hooki adresujące tryby awarii o największym wpływie:
1. Klasyfikacja źródła danych wejściowych (adresuje Clinejection). Przed przetworzeniem przez agenta jakichkolwiek danych zewnętrznych należy sklasyfikować ich poziom zaufania. Tytuły zgłoszeń od anonimowych użytkowników są niezaufane. Kod z repozytorium jest zaufany. Odpowiedzi API są półzaufane. Klasyfikacja determinuje, jakich narzędzi agent może używać podczas przetwarzania tych danych. Niezaufane dane wejściowe otrzymują tryb Read-only. Zaufane dane wejściowe otrzymują pełny zestaw uprawnień.
2. Odmowa dostępu do systemu plików na poziomie jądra (adresuje ucieczkę z piaskownicy). Opakowywanie każdego wywołania narzędzia Bash w sandbox-exec (macOS) lub profil seccomp (Linux). Odmowa dostępu do ścieżek poświadczeń (~/.ssh, ~/.aws, ~/.gnupg, .env). Narzut jest pomijalny (2 ms), a ochrona jest absolutna: agent nie może uzyskać dostępu do odmówionych ścieżek niezależnie od konstruowanego polecenia powłoki, uruchamianych podprocesów czy odkrytych aliasów ścieżek.7
3. Lista dozwolonych domen dla ruchu wychodzącego (adresuje cichą eksfiltrację). Przed każdym wychodzącym żądaniem HTTP sprawdzanie docelowej domeny na liście zatwierdzonych. Logowanie wszystkich żądań, zatwierdzonych lub odrzuconych. Log tworzy ścieżkę audytu; lista dozwolonych zapobiega eksfiltracji do kontrolowanych przez atakującego punktów końcowych. Lista 12 dozwolonych domen pokrywa usługi, których agent kodujący faktycznie potrzebuje (GitHub, npm, PyPI, strony z dokumentacją). Wszystko poza listą jest domyślnie podejrzane.5
Każdy hook to 10–30 linii skryptu powłoki. Każdy uruchamia się automatycznie przy każdym odpowiednim wywołaniu narzędzia. Razem adresują trzy klasy ataków zademonstrowane w 2026 roku: prompt injection przez niezaufane dane wejściowe, ucieczkę z piaskownicy przez manipulację ścieżkami i eksfiltrację danych przez autoryzowane żądania wychodzące.
Atakujący Cline potrzebował jednego zgłoszenia na GitHub i trzech godzin. Następny atak wykorzysta ten sam schemat przeciwko repozytorium, które uruchamia triaż AI z domyślnymi uprawnieniami, współdzielonymi pamięciami podręcznymi i bez monitorowania ruchu wychodzącego. Pytanie nie brzmi, czy piaskownica wytrzyma. Trzy niezależne zespoły badawcze w 2026 roku udowodniły, że nie wytrzyma. Pytanie brzmi, co wychwytuje atak, gdy piaskownica zawodzi.
FAQ
Czym Clinejection różni się od zwykłego ataku na łańcuch dostaw?
Tradycyjne ataki na łańcuch dostaw kompromitują maszynę lub poświadczenia programisty. Clinejection skompromitował potok CI przez agenta AI przetwarzającego niezaufane dane wejściowe użytkowników. Atakujący nigdy nie miał dostępu do repozytorium, nigdy nie skompromitował poświadczeń i nigdy nie wykorzystał podatności w systemie budowania. Atak wykorzystał agenta AI jako most od niezaufanych danych wejściowych (tytuł zgłoszenia) do zaufanej infrastruktury (potok wydań) poprzez współdzieloną pamięć podręczną.1
Czy to oznacza, że triaż zgłoszeń wspomagany przez AI jest z natury niebezpieczny?
Nie z natury, ale obecne domyślne konfiguracje są niebezpieczne. Uruchamianie Claude Code z dostępem do Bash na każdym zgłoszeniu otwartym przez dowolnego użytkownika jest równoznaczne z uruchamianiem dowolnego kodu od anonimowych kontrybutorów. Agenty triażu potrzebują dostępu Read do metadanych zgłoszeń i dostępu Write do dodawania etykiet. Nie potrzebują Bash, web-fetch ani żadnego narzędzia mogącego modyfikować środowisko budowania.1
Co z kontenerami i maszynami wirtualnymi do piaskownicowania?
Kontenery i maszyny wirtualne zapewniają silniejszą izolację niż piaskownice na poziomie procesu, ale wprowadzają opóźnienia (100 ms+ na zimnym starcie) i złożoność (zarządzanie obrazami, montowanie wolumenów, konfiguracja sieci). Dla interaktywnych sesji agentów wywołujących ponad 50 narzędzi na sesję opóźnienie się kumuluje. Podejście na poziomie jądra (Seatbelt, seccomp, Veto) zapewnia wymuszanie bez narzutu, ponieważ działa w ramach istniejącego modelu procesów.7
Czy agent może obejść Seatbelt?
Seatbelt to framework obowiązkowej kontroli dostępu wymuszany przez jądro macOS. Jego obejście wymaga exploita jądra. Agent działa w przestrzeni użytkownika. Nie może modyfikować stanu jądra. Jednakże Seatbelt ma węższy zakres niż pełny kontener: ogranicza dostęp do plików i wykonywanie procesów, ale nie ogranicza dostępu sieciowego w tym samym stopniu. Połączenie Seatbelt z monitorem ruchu wychodzącego pokrywa obie powierzchnie.7
Jak to się ma do frameworka bezpieczeństwa agentów AI NIST?
Mój publiczny komentarz do NIST argumentował, że zagrożenia agentowe mają charakter behawioralny, nie architektoniczny. Awarie piaskownicy udokumentowane w tym artykule wzmacniają ten argument: ucieczki agentów mają charakter behawioralny (agent rozumuje o obejściu ograniczeń), a najgroźniejszy atak (Clinejection) jest w pełni behawioralny (autoryzowane akcje składające się w nieautoryzowane rezultaty). Istniejące frameworki NIST (CSF 2.0, SP 800-53, AI RMF) nie traktują kompozycji behawioralnej jako klasy zagrożeń.9
-
Khan, A. “Clinejection: Compromising Cline’s Production Releases just by Prompting an Issue Triager.” March 2026. Via Simon Willison. ↩↩↩↩↩↩↩
-
tomvault. “How Claude Code Escapes Its Own Denylist and Sandbox.” ona.com, March 2026. HN discussion, 34 points, 14 comments. ↩↩↩↩↩↩↩↩↩
-
Jiang, M. et al. “SoK: Organizing, Orchestrating, and Benchmarking Agent Skills at Scale.” Semantic Scholar, February 2026. ↩
-
Crosley, B. “The Fabrication Firewall: When Your Agent Publishes Lies.” blakecrosley.com, February 2026. ↩
-
Lan, J. et al. “Silent Egress: The Implicit Prompt Injection Attack Surface.” Semantic Scholar, February 2026. Crosley, B. “Silent Egress: The Attack Surface You Didn’t Build.” blakecrosley.com, March 2026. ↩↩
-
dzervas. “mcp-firewall: A tool policy manager for AI agents.” GitHub, March 2026. ↩
-
Author’s production hook system. 84 hooks across 15 event types, 60+ sessions. Sandbox: macOS Seatbelt profile, 31 tests, approximately 2ms overhead per command. ↩↩↩↩↩↩↩↩↩
-
Crosley, B. “The Invisible Agent: Why You Can’t Govern What You Can’t See.” blakecrosley.com, March 2026. ↩
-
Crosley, B. “What I Told NIST About AI Agent Security.” blakecrosley.com, February 2026. NIST docket NIST-2025-0035. ↩