Langkau ke kandungan

Kotak pasir dan keselamatan

Ejen membaca fail, menjalankan perintah shell dan memanggil API luaran bagi pihak anda. Kegagalan yang anda risaukan jarang berpunca daripada niat jahat — ia ialah ejen yang tersalah baca laluan dan menulis semula checkout kerja anda, atau ejen yang push cawangan yang belum anda selesai semak. Jadi persoalan yang perlu dijawab oleh produk ini bukan “bolehkah ejen dipercayai” tetapi “apakah jejari letupan apabila ia tersilap”.

Control Center menjawabnya secara berlapis dan lapisan-lapisan itu memang sengaja berdikari. Jika satu hilang, yang lain masih bertahan. Itu penting, kerana lapisan paling luar — kotak pasir OS — ialah satu-satunya yang bersyarat: ia bergantung pada hos yang menawarkan bahagian belakang dan pada Windows tiada hos yang berbuat demikian.

Lima perkara membataskan larian ejen pada setiap hos, dengan atau tanpa kotak pasir. Tiada satu pun daripadanya sempadan kernel; semuanya nyata. Apabila hos menawarkan bahagian belakang, kotak pasir OS duduk di atasnya dan “Apa yang ditambah oleh kotak pasir dan di mana ia berhenti” di bawah merangkumi apa yang berubah.

Worktree copy-on-write tersendiri. Setiap perbualan menyediakan checkout sendiri bagi setiap repo yang dipautkan, diklon secara copy-on-write melalui natif rift yang disertakan dan bukan disalin bait demi bait, jadi pengasingan cukup murah untuk dijadikan lalai. Repositori berdaftar yang anda tambahkan pada ruang kerja bukanlah direktori tempat ejen bekerja — dua perbualan yang menyentuh repo yang sama tidak dapat melihat suntingan satu sama lain dan kedua-duanya tidak menyunting checkout anda.

Butirannya wajar diperjelas: worktree adalah bagi setiap perbualan, bukan setiap ejen. Dua ejen dalam perbualan yang sama mendapat direktori kerja overlay yang berbeza yang berkongsi repos/ perbualan itu — itulah maksudnya, kerana mereka bekerjasama pada satu perubahan — sementara direktori ejen peribadi mereka kekal berasingan. Copy-on-write ialah satu-satunya bahagian belakang di mana sahaja rift disertakan: sistem fail yang tidak boleh reflink, atau dir data pada volum berbeza daripada repo, menggagalkan penyediaan dan bukannya berpatah balik. Windows ialah pengecualian — tiada bahagian belakang copy-on-write di sana, jadi git worktree ialah bahagian belakangnya, bukan kemerosotan senyap.

Kekangan laluan dalam alat fail harness terbina. Alat read, write, edit, apply_patch, search, find dan search_files harness menyelesaikan setiap laluan terhadap direktori kerja larian serta akar kongsi — direktori repos/ perbualan, tempat worktree berada, dan dir lampiran ruang apabila wujud — dan menolak apa-apa yang terletak di luar. Laluan mutlak hanya diterima jika ia sudah berada di dalam. Symlink di dalam worktree yang menunjuk ke luar juga diselesaikan dan ditolak. Inilah sebabnya ejen tidak boleh write ke ~/.ssh walaupun tanpa kotak pasir.

Permukaan alat modus. Modus sesuatu perbualan menentukan alat mana yang dizahirkan untuk larian itu sama sekali. Dalam modus baca sahaja (plan, review, orchestrate), alat yang mengubah keadaan langsung tidak ada dalam daftar dan polisi perintah shell bagi modus tersebut menolak setiap perintah yang mengubah keadaan secara terus — git commit, git push, npm install, docker dan seumpamanya — dan bukannya meminta pengesahan untuknya. Alat yang tidak hadir tidak boleh dipanggil, dan itu jaminan yang lebih kukuh daripada alat yang hadir tetapi dipagari.

Guardrail tindakan bersatu. Setiap alat yang mengubah keadaan mengisytiharkan kelas kesan yang boleh dihasilkannya dan enjin polisi menyelesaikan setiap satu kepada benarkan, tanya dahulu, atau tolak sebelum panggilan itu dijalankan. Satu prompt tanpa pelulus yang bersambung diselesaikan kepada ditolak — soalan yang tiada siapa dapat jawab tidak pernah menjadi ya. Lihat Guardrail untuk model penyelesaiannya.

Kelayakan melalui broker dan persekitaran yang disanitasi. Kelayakan ditempa setiap pelancaran dan ditarik balik apabila larian tamat, dan token GitHub yang dipegang larian boleh membaca tetapi tidak boleh push — lihat “Push melalui Control Center” di bawah. ACP, bash harness dan Claude Code di bawah balutan natif mendapat persekitaran yang dilucutkan vektor suntikan pemaut dinamik dan penterjemah (LD_PRELOAD, DYLD_INSERT_LIBRARIES, NODE_OPTIONS, GIT_SSH_COMMAND dan seumpamanya) sebelum proses bermula. Claude Code melalui NoSandboxAdapter mewarisi persekitaran hos.

Alat fail harness berjalan dalam proses pelayan, bukan dalam proses anak, jadi tiada profil kotak pasir dapat membataskannya walaupun satu dilekatkan — hanya bash melalui pelari perintah berkotak pasir. Kekangan mereka ialah semakan laluan yang dihuraikan di atas, dan oleh itu semakan itu menanggung beban utama dan bukan sekadar barisan pertahanan kedua.

Alat bash ialah tempat lain yang perlu diperjelas. Perintahnya dinilai terhadap polisi perintah modus dan kemudian dilancarkan dengan persekitaran yang disanitasi serta worktree sebagai direktori kerjanya. Apabila bahagian belakang wujud, pelancaran itu dibalut, jadi kotak pasirlah yang menghalang perintah shell daripada menulis di luar worktree; apabila tiada — Windows, atau Linux tanpa bwrap dan socat — polisi perintah ialah satu-satunya perkara antara perintah itu dan seluruh cakera yang lain. Dalam modus chat, polisi itu menolak secara mutlak senarai tetap (peningkatan keistimewaan, pemusnahan sistem dan cakera, pelarian kontena dan namespace) dan meminta pengesahan bagi mutasi keadaan jauh (git push, gh pr create, penerbitan dan pemasangan pakej). Apa-apa yang tiada dalam kedua-dua senarai akan berjalan. Saluran paip, rantaian dan subshell dihuraikan dan setiap sub-perintah dinilai secara berasingan, dengan keputusan paling ketat yang menang.

Claude Code, apabila digunakan sebagai penyesuai, ialah kes yang kotak pasir memikul beban paling berat. Control Center melancarkannya dengan --dangerously-skip-permissions, kerana claude -p bukan interaktif akan tersekat selama-lamanya pada gesaan kelulusannya sendiri. Panggilan baca, tulis dan edit-nya berlaku di dalam prosesnya sendiri dan Control Center tidak pernah melihatnya. Panggilan Bash-nya dihantar, sebelum dijalankan, kepada hook pada pelayan yang mengelaskan baris perintah dan bertanya kepada peraturan yang sepadan, dan alat mcp__* yang disediakan Control Center melalui pintu pagar seperti biasa. Apabila bahagian belakang wujud, panggilan yang tidak kelihatan itu masih berlaku di dalam balutan kotak pasir, jadi OS ialah sempadan di sekelilingnya. Apabila tiada, tiada apa-apa. Setiap penyesuai menyatakan hal ini dalam matriks penguatkuasaannya — lihat matriks kejujuran penyesuai.

Tiada bendera kebenaran bagi setiap ejen. Sama ada ejen boleh mencipta commit, push, membuka pull request, menulis ke penjejak atau mencapai rangkaian ditentukan oleh guardrail — benarkan, tanya dahulu atau tolak — dan peraturan yang sama terpakai tanpa mengira penyesuai yang menjalankan ejen.

Push ialah tindakan yang mesti bertahan walaupun terhadap ejen yang cuba memintas sesuatu peraturan, jadi ia dikuatkuasakan melalui apa yang dipegang ejen, bukan melalui apa yang dijalankannya. Dengan GitHub App dikonfigurasikan, larian dalam worktree GitHub mendapat token pemasangan berskop repo yang boleh membaca kandungan dan bekerja dengan pull request tetapi tidak boleh push. Konfigurasi git larian menulis semula setiap URL push GitHub (https://github.com/, git@github.com:, ssh://git@github.com/) kepada gerbang larian ejen, titik akhir loopback pada cc_server. Gerbang itu membaca ref mana yang dikemas kini oleh push, bertanya kepada peraturan Push ke remote — jadi peraturan yang dihadkan kepada cabang, seperti “tolak push ke main”, turut terpakai — dan barulah memajukan push itu ke GitHub dengan token tulis yang ditempanya untuk tujuan itu dan tidak pernah diserahkan kepada ejen. Push yang ditolak muncul dalam output git sendiri:

! [remote rejected] main -> main (Control Center policy: …)

Gerbang itu hanya memajukan push ke repo worktree itu sendiri atau ke repo GitHub yang dipautkan kepada ruang kerja. Fetch terus ke GitHub dengan token baca.

Perintah shell lain menemui pintu pagar yang lebih lembut. Alat bash harness dan, melalui hook, alat Bash Claude Code mengelaskan setiap baris perintah dan bertanya kepada peraturan yang menaunginya: git commit, merge, rebase, cherry-pick, revert dan am ialah Cipta commit; gh pr create ialah Buka pull request; gh pr merge, review, comment dan close ialah Terbitkan semakan atau cantuman; gh issue create, edit, comment dan close ialah Tulis ke penjejak luaran; pemasangan pakej ialah Pasang pakej; curl dan wget ialah Akses rangkaian. Ini mengenali cara lazim menulis sesuatu tindakan, dan ejen yang bertekad menyembunyikannya (eval, fail skrip, panggilan API terus) boleh berbuat demikian. Push tidak bergantung padanya.

Tiada kunci API tiket diletakkan dalam kotak pasir. Ejen menulis tiket melalui alat MCP Control Center, yang bertanya kepada Tulis ke penjejak luaran.

Peraturan Akses rangkaian dibaca sekali, apabila larian bermula. Rangkaian kotak pasir sama ada hidup atau mati, jadi tolak mematikannya manakala benarkan dan tanya dahulu membiarkannya hidup; alat web_fetch dan web_search harness masih bertanya pada setiap panggilan. Kelayakan ditarik balik apabila larian dimusnahkan, bersama setiap token tulis yang ditempa oleh gerbang, jadi sesuatu pemegang tidak hidup lebih lama daripada larian.

Apa yang ditambah oleh kotak pasir dan di mana ia berhenti

Bahagian bertajuk “Apa yang ditambah oleh kotak pasir dan di mana ia berhenti”

Enum SandboxBackend mempunyai tiga nilai — native (Seatbelt pada macOS, bubblewrap pada Linux dan WSL2), none, dan microvm. Pembalutan perintah ejen hanya menggunakan dua yang pertama: microvm ialah bahagian belakang VM terasing untuk terminal interaktif dan stesen, bukan peningkatan senyap bagi balutan bash. Memahami bentuk natif menjelaskan apa yang anda peroleh apabila satu dilekatkan dan apa yang ia tidak akan pernah berikan.

cc_server menyiasat hos sekali semasa permulaan dan apabila bahagian belakang menjawab, ia menyuap kedua-dua sambungan yang ada pada laluan penghantaran, kerana pengangkutan itu tidak berkongsi satu. Yang pertama (SandboxPort) membalut CLI Claude Code. Yang kedua (SandboxManager) membalut penyesuai ACP dan alat bash harness terbina. Menyuap hanya satu akan menyebabkan yang lain dilancarkan tanpa balutan, yang memang pernah berlaku. Pada hos tanpa bahagian belakang, atau di bawah --sandbox off, semuanya dilancarkan tanpa balutan dan pelayan menyatakan kes mana yang anda alami pada baris permulaannya. ACP dan bash harness masih menyanitasi persekitaran; Claude Code melalui NoSandboxAdapter mewarisi persekitaran hos.

Satu permukaan sengaja mengecualikan diri: terminal yang dihoskan pelayan menerima argumen bahagian belakang bagi setiap sesi dan cc_server membinanya dengan lalai kepada shell hos, jadi terminal yang anda buka dari aplikasi berjalan tanpa kotak pasir melainkan klien menamakan native secara eksplisit. Itu shell sebenar pada hos pelayan, bukan larian ejen.

Pada Linux, bubblewrap membina namespace mount, jadi ejen benar-benar melihat hanya apa yang diikat masuk ke dalamnya. Pada macOS, sandbox-exec mengenakan profil Seatbelt yang permisif secara lalai — profil yang menolak secara lalai sepenuhnya menjadikan shell interaktif tidak boleh digunakan, kerana bilangan panggilan dyld, XPC dan Mach untuk disenaraikan tidak praktikal. Oleh itu profil macOS bermula daripada (allow default) dan mengukir penolakan: bacaan laluan rahsia, polisi tulis yang ditetapkan semula kepada tolak dengan benarkan eksplisit di atasnya, penyekatan unlink pada setiap laluan yang ditolak dan nenek moyangnya supaya penamaan semula tidak dapat memintas penolakan tulis, penolakan exec untuk binari yang sentiasa berbahaya dan untuk apa-apa yang disalin ke direktori boleh tulis, serta rangkaian yang dihadkan kepada port proksi loopback dan DNS.

Akibat praktikalnya: pada Linux, pandangan ejen terhadap sistem fail disempitkan; pada macOS, ejen masih boleh membaca sebahagian besar hos dan checkout berdaftar anda dilindungi oleh penolakan tulis dan penamaan semula yang eksplisit dan bukannya oleh ketaknampakan. Kedua-duanya ialah pengasingan peringkat namespace, bukan sempadan keselamatan kernel — bahagian belakang sendiri menyatakannya dalam nota keupayaan masing-masing.

Set bind-mount ialah tempat sempadan antara ejen dilukis. Penghantaran overlay (Claude Code dan ACP) melekatkan sehingga empat laluan, diselesaikan bagi setiap larian: direktori kerja overlay ejen sebagai baca-tulis, direktori konfigurasi ejen sebagai baca sahaja, direktori repos/ kongsi perbualan sebagai baca-tulis apabila wujud, dan direktori lampiran ruang sebagai baca sahaja apabila wujud. Direktori ejen rakan sebaya tidak pernah dilekatkan. Balutan bash harness lebih sempit: ia hanya melekatkan direktori kerja larian. Ini bentuk yang sama yang dikuatkuasakan oleh semakan laluan dalam proses harness melalui perisian, jadi kedua-duanya sependapat — tetapi hanya semakan laluan yang meliputi alat fail harness sendiri, yang langsung tidak sampai ke namespace mount.

Checkout berdaftar anda menyertai set tolak-tulis di atas itu, dalam setiap modus dan pada ketiga-tiga laluan yang dibalut (Claude Code, ACP, bash harness), dan setiap peraturan dikeluarkan dalam ejaan literal serta ejaan yang diselesaikan symlink. Ejaan kedua bukan sekadar hal kecil: kernel memadankan peraturan penolakan terhadap laluan yang diselesaikan dan macOS menjadikan penyelesaian itu kes biasa dan bukan kes eksotik — /tmp sebenarnya /private/tmp, /var/folders/… sebenarnya /private/var/folders/… — jadi checkout yang dicapai melalui mana-mana satu pun akan boleh ditulis sedangkan profil itu kelihatan melarangnya.

Rangkaian, di bawah kotak pasir, ialah senarai benarkan domain bagi setiap sesi yang dikuatkuasakan oleh proksi loopback dalam proses. Keluar adalah tolak secara lalai: apabila rangkaian dimatikan, proksi tetap bermula tetapi senarai benarkannya kosong, jadi setiap sambungan keluar ditolak. Apabila ia dihidupkan, penyelesai membina senarai benarkan daripada dasar yang dikurasi — penyedia API LLM, hos git, daftar pakej, katalog model — serta domain GitHub, dan memadankan hos sasaran setiap sambungan dengannya. Kad liar berfungsi (*.example.com memadankan subdomain, bukan puncak domain). Senarai yang sentiasa ditolak secara berasingan menang ke atas senarai benarkan dan meliputi titik akhir metadata awan serta pengumpul telemetri. Apa yang tidak boleh dikonfigurasi ialah senarai itu sendiri: spesifikasi membawa medan egressAllowlist untuk domain tambahan, tetapi tiada apa-apa dalam laluan penghantaran yang mengisinya, jadi dalam praktiknya setiap larian mendapat dasar itu serta GitHub dan tiada lagi.

Penolakan, pada macOS, boleh diperhatikan: pemantau menyadap log sistem untuk penolakan Sandbox, menapis bunyi yang diketahui, menyahduplikasi badai cubaan semula dan memunculkan setiap satu sebagai SandboxViolation yang menamakan apa yang dicuba dan apa yang disekat. Pada Linux, laluan balutan menghurai stderr untuk EPERM, Operation not permitted dan Permission denied dan mengeluarkan jenis peristiwa yang sama — tiada penyadapan log sistem. Semuanya tiba pada aliran peristiwa larian itu sendiri, jadi panggilan sistem yang ditolak muncul dalam transkrip dan bukan hanya dalam log. Telemetri itu hanya wujud semasa kotak pasir natif benar-benar membalut proses itu — pada hos tanpa bahagian belakang tiada apa-apa untuk diperhatikan. Ia juga tidak berkaitan dengan guardrail: pelanggaran datang daripada panggilan sistem yang ditolak, tidak pernah daripada carian peraturan.

Pengesahan datang daripada guardrail, bukan kotak pasir

Bahagian bertajuk “Pengesahan datang daripada guardrail, bukan kotak pasir”

Kotak pasir tiada laluan kelulusan. Ia melancarkan, melaksanakan, mengeluarkan peristiwa dan memusnahkan; tiada tempat di dalamnya untuk manusia menjawab soalan. Apabila anda melihat pengesahan dalam sesuatu ruang — luluskan push ini, luluskan pemadaman ini — ia dibangkitkan oleh enjin guardrail (atau polisi perintah harness) sebelum tindakan itu sampai ke masa jalan sama sekali.

Susunan ini memang disengajakan. Kelulusan yang berlaku selepas panggilan sistem hanyalah rekod audit, bukan pintu pagar. Itulah juga sebab peraturan gagal-tertutup terletak dalam guardrail: tanpa pelulus yang bersambung, keputusan dibuat sebelum apa-apa berjalan dan keputusan itu ialah penolakan.

Jalankan diagnosis ejen ialah satu-satunya tempat yang menyatakan apa yang boleh diperiksa secara manual.