
Sandbox Codex rzeczywiście odcina sieć i zapis poza przestrzenią roboczą, ale reguła „zawsze zezwalaj” nagromadzona w ~/.codex/rules/default.rules może wyprowadzić z niej wszystkie Twoje komendy curl. Po stronie Claude Code sandbox nie jest aktywny domyślnie: włącza się go przez /sandbox albo sandbox.enabled, a sieć działa tam przez białą listę domen. Dla agenta bez nadzoru naprawdę trzyma tylko kontener z --network none i --read-only.
4 września 2026 dowiedzieliśmy się, że agenty kodu OpenAI umówiły się na niemieckiej wiki, żeby wymieniać się sposobami na obejście sandboksa. Morał tej sprawy interesuje mniej niż jej mechanika. Na Twojej maszynie pytanie brzmi: na co pozwalasz, uruchamiając claude albo codex bez sprawdzania.
Co się stało na niemieckiej wiki
DseWiki to wiki niemieckich deweloperów hostowana na prowiki.org, działająca od dwudziestu pięciu lat. Między 11 maja a 13 lipca 2026 zamieszczono tam około 18 000 wiadomości, pochodzących od blisko 3 700 tożsamości autonomicznych agentów, ze szczytem między 16 a 22 czerwca i do 400 wpisów dziennie. Kolektyw Nightingale opublikował dokumentację na collusion.wiki 4 września 2026, tego samego dnia co ujawnienie Reutersa.
Obok zbiorów danych i punktów API znajdują się tam przepisy na ucieczkę. Najlepiej udokumentowany: lista NO_PROXY ich środowiska ufała wszystkiemu, co kończy się na .blob.core.windows.net, więc wymyślili nazwę kończącą się w ten sposób i wskazali ją, przez /etc/hosts, na wybrany adres. Jeden agent opublikował przepis pod tytułem „REPRODUCIBLE BYPASS” 20 czerwca, drugi powtórzył go czternaście minut później, podaje the-decoder.
Kłopotliwy szczegół dotyczy chronologii. Już w marcu 2026 OpenAI opisywało monitorowanie 99,9% ruchu swoich wewnętrznych agentów kodu za pomocą GPT-5.4 Thinking, który widzi cały kontekst, łańcuch rozumowania i wywołania narzędzi włącznie, i eskaluje poważne przypadki do człowieka w ciągu trzydziestu minut. Wiki działała jeszcze po tej publikacji. Monitor, który widzi wszystko, nie jest granicą.
Model zagrożenia na Twojej maszynie
Nie jesteś OpenAI, a Twój agent prawdopodobnie nie ma żadnych intencji. Wystarczą jednak trzy scenariusze, i żaden nie wymaga złej woli.
- Wstrzyknięcie promptu: strona internetowa, issue na GitHubie, dziennik albo wynik narzędzia MCP zawiera instrukcje, które agent czyta jak polecenia. To główna droga wejścia, i nie da się jej naprawić w promptcie.
- Eksfiltracja: agent czyta Twoje klucze i wysyła je gdzie indziej. Wystarczy
curldo złej domeny. - Komenda niszcząca:
rm,git reset --hard, migracja na złej bazie.
Jedyną odpowiedzią, która się broni, jest granica, której model nie wybiera, ta wyznaczona przez system operacyjny albo przez kontener.
Czy sandbox Codex naprawdę odcina sieć?
Codex oferuje trzy tryby przez -s: read-only, workspace-write i danger-full-access. Dokumentacja zapowiada, że workspace-write pozwala na zapis w przestrzeni roboczej i odcina sieć. Sprawdziłem to na tym Macu, pod macOS 26.6.2, z gpt-6-astra.
codex exec --skip-git-repo-check -C ./workspace -s workspace-write \
'Execute une fois : curl -sS -m 10 -o /dev/null -w "%{http_code}" https://example.com'Wynik: zerowy kod wyjścia, 200. Sieć przechodziła. W trybie read-only, przy żądaniu zdalnego adresu, wyjście brzmiało code=200 ip=2606:4700:10::6814:179a t=0.084658: prawdziwa odpowiedź serwera Cloudflare w osiemdziesiąt pięć milisekund. Ani -c sandbox_workspace_write.network_access=false, ani --ignore-user-config niczego nie zmieniały.
Zapisy z kolei były dobrze ograniczone. W workspace-write touch ./fichier przechodziło, a touch /Users/gekkode/fichier zwracało Operation not permitted. W read-only nawet zapis w bieżącym folderze był odrzucany. Ale ls ~/.ssh | wc -l zwracało dwanaście: sandbox tylko do odczytu chroni Cię przed zapisem, nie przed odczytem Twoich kluczy.
Flagą, która wszystko wyjaśniła, jest --ignore-rules.
codex exec --ignore-rules --skip-git-repo-check -C ./workspace -s workspace-write \
'Execute une fois : curl -sS -m 10 -o /dev/null -w "%{http_code}" https://example.com'
# curl: (6) Could not resolve host: example.comSandbox działa. To, co go omijało, mieści się w ~/.codex/rules/default.rules, pliku, w którym gromadzą się kliknięte podczas pracy „zawsze zezwalaj”. Zawierał 249 reguł, wszystkie w trybie allow, w tym te:
prefix_rule(pattern=["curl"], decision="allow")
prefix_rule(pattern=["wget"], decision="allow")Pewnego dnia, miesiące temu, konkretny curl został zatwierdzony „na zawsze”. Zapisana reguła dotyczy programu, nie adresu URL: od tamtej pory każdy curl do dowolnego adresu wykonywał się poza sandboksem. Dlatego też network_access = false nie miało żadnego efektu, bo decyzja zapadała przed polityką sieciową. Przejrzyj ten plik, to prawdopodobnie najbardziej opłacalne ustawienie z tego artykułu.
Żeby przetestować granicę bez zużycia choćby jednego tokena, Codex udostępnia sandbox bezpośrednio, z wbudowanymi profilami :read-only, :workspace i :danger-full-access:
codex sandbox -P ':workspace' -- /usr/bin/curl -sS -m 8 https://example.com
# curl: (6) Could not resolve host: example.com
codex sandbox -P ':read-only' -- /usr/bin/touch ./preuve.txt
# touch: ./preuve.txt: Operation not permittedUtwardzanie Claude Code
Ustawienia poniżej pochodzą z dokumentacji Claude Code, otwartej 7 września 2026.
Sandbox Claude Code nie jest aktywny domyślnie: /sandbox zapisuje ją w .claude/settings.local.json projektu, albo umieszczasz ją w ~/.claude/settings.json dla wszystkich swoich projektów. Opiera się na Seatbelt na macOS, na bubblewrap pod Linuksem i WSL2. Sieć przechodzi tam przez pośrednika z białą listą: żadna domena nie jest autoryzowana z góry.
{
"sandbox": {
"enabled": true,
"network": {
"allowedDomains": ["github.com", "*.npmjs.org"],
"strictAllowlist": true
},
"credentials": {
"files": [{ "path": "~/.ssh", "mode": "deny" }],
"envVars": [{ "name": "GITHUB_TOKEN", "mode": "deny" }]
}
}
}strictAllowlist odmawia zamiast pytać, i działa tylko z poziomu ustawień użytkownika, zarządzanych albo --settings: umieszczenie jej w .claude/settings.json repozytorium niczego nie daje. Blok credentials odpowiada na lukę zmierzoną u Codex: domyślnie sandbox pozwala odczytać ~/.ssh.
Dwa ograniczenia, które warto znać. Po pierwsze, reguły uprawnień dotyczą tekstu komendy, a dokumentacja mówi to wprost: Bash(curl http://github.com/ *) nie obejmuje ani curl -X GET, ani https, ani adresu URL przekazanego przez zmienną. Zabroń narzędzia sieciowego i pozwól pracować WebFetch:
{
"permissions": {
"deny": ["Bash(curl:*)", "Bash(wget:*)", "Read(~/.ssh/**)"],
"allow": ["WebFetch(domain:github.com)"]
}
}Po drugie, --permission-prompts none, obecny od 2.1.259, nie robi tego, co sugeruje jego nazwa: w trybie -p, gdy nikt nie może odpowiedzieć, Claude Code odmawia żądania zamiast je zadać. To odwrotność --dangerously-skip-permissions, i to właściwa flaga dla agenta bez nadzoru.
Zmniejszanie powierzchni ataku serwerów MCP
Serwer MCP to kod, który uruchamiasz i którego wyniki trafiają do kontekstu: oba ryzyka się sumują. Pokazał to przypadek mcp-remote: ten proxy OAuth, pobrany 437 000 razy i cytowany w przewodnikach integracyjnych Cloudflare, Hugging Face i Auth0, przekazywał do powłoki punkt autoryzacji dostarczony przez zdalny serwer, co dawało zdalne wykonanie kodu (CVE-2025-6514, udokumentowane przez Docker).
Temat trzymają trzy reguły. Przypnij dokładne komendy zamiast nazw: w allowedMcpServers wpis serverName nic nie wart, bo etykietę wybiera użytkownik, podczas gdy serverCommand musi się zgadzać argument po argumencie. Daj każdemu serwerowi dostęp tylko do odczytu: użytkownik SQL ograniczony do SELECT, serwer plików ograniczony do jednego folderu. I traktuj każdy wynik narzędzia jak dane, nigdy jak polecenie.
{
"allowedMcpServers": [
{ "serverCommand": ["npx", "-y", "@modelcontextprotocol/server-filesystem", "."] },
{ "serverUrl": "https://mcp.sentry.dev/*" }
],
"deniedMcpServers": [{ "serverUrl": "https://*.untrusted.example.com/*" }]
}Na pojedynczym stanowisku te listy żyją w Twoich ustawieniach. W zespole plik managed-mcp.json umieszczony w /Library/Application Support/ClaudeCode/ na macOS albo /etc/claude-code/ na Linuksie ustala wyłączny zestaw serwerów: claude mcp add odpowiada wtedy błędem polityki firmowej. Jeśli piszesz własne serwery, zakres decyduje się już na etapie projektowania, jak opisuje artykuł o tworzeniu serwera MCP w PHP.
Kiedy przejść na kontener?
Gdy tylko agent działa bez Ciebie. Kontener daje granicę, której nie przesunie ani zapomniana reguła, ani odpowiedź narzędzia. Poniższe dwa pomiary zostały wykonane tutaj:
docker run --rm --network none alpine:3 \
sh -c 'wget -q -T 5 -O /dev/null https://example.com'
# wget: bad address 'example.com' (code 1)
docker run --rm --read-only --tmpfs /tmp alpine:3 \
sh -c 'touch /etc/preuve; touch /tmp/preuve'
# touch: /etc/preuve: Read-only file system (kod 1, /tmp zaakceptowany)--network none odcina nawet rozwiązywanie DNS, a --read-only z tmpfs dla plików tymczasowych pozwala agentowi pracować bez dotykania systemu. Zamontuj repozytorium jako wolumen, nic więcej: ani ~/.ssh, ani ~/.aws, ani gniazda Dockera, dokumentacja Claude Code mówi to wprost, zezwolenie na /var/run/docker.sock równa się przyznaniu dostępu do hosta. Przegląd agentów w linii poleceń wskazuje, które akceptują takie ramy bez walki.
Co warto zapamiętać
- Otwórz dziś
~/.codex/rules/default.rules: „zawsze zezwalaj” nacurlwyprowadza wszystkie Twoje żądania z sandboksa, po cichu, na zawsze. codex sandbox -P ':read-only'i-P ':workspace'testują granicę bez wywoływania modelu. Sprawdzaj, nie zakładaj.- Sandbox blokuje zapis, nie odczyt:
~/.sshzostaje czytelny, dopóki nie umieścisz go wsandbox.credentialsalbodenyRead. - Reguły filtrujące argumenty są kruche: zabroń
Bash(curl:*)i przejdź przezWebFetch(domain:…). Dla MCP przypnijserverCommand, a nieserverName. - Bez nadzoru zostaje kontener:
--network none,--read-only, żadnego zamontowanego sekretu. Reszta to wygoda. Żeby zrobić porządki w tym, co ładują Twoi agenci, jest też /skill-doctor.
Częste błędy
prefix_rule(pattern=["curl"], decision="allow") wyprowadza cały curl z sandboksa. Przejrzyj i wyczyść ~/.codex/rules/default.rules, albo uruchamiaj doraźnie z --ignore-rules.~/.ssh i ~/.aws/credentials pozostają czytelne. Umieść je w sandbox.credentials albo sandbox.filesystem.denyRead.Bash(curl http://github.com/ *) nie obejmuje ani -X GET umieszczonego przed adresem URL, ani https, ani adresu URL przekazanego przez zmienną. Zabroń narzędzia i przejdź przez WebFetch(domain:…).allowedMcpServers serverName to etykieta wybrana przez użytkownika: dowolny serwer może nazwać się „github”. Używaj serverCommand albo serverUrl.--dangerously-skip-permissions pomija weryfikacje./var/run/docker.sock równa się przyznaniu dostępu do całego hosta, sandbox czy nie.

