مخزن، سطح افشاست
وقتی به یک عامل کدنویسی وظیفهای داده میشود، معمولاً به یک working tree دسترسی پیدا میکند. آن tree یک sandbox بیطرف نیست. شامل کل تاریخچهٔ commit، همهٔ branchها، همهٔ tagها، و هر artifact رهاشده یا ignoreشدهای است که تصادفاً آنجا بوده. عامل آنچه را که هست میخواند. اگر مخزن هرگز شامل یک تصویر، یک credential، یک database dump، یا یک binary blob بوده باشد، عامل میتواند آن را بخواند، کپی کند، تبدیل کند و دوباره بنویسد. گزارش اخیر افشای ۱۳٬۰۰۰ تصویر داخلی از طریق عاملهای کدنویسی، داستانی دربارهٔ یک پرامپت زیرکانه نیست. داستانی است دربارهٔ آنچه رخ میدهد وقتی یک عامل مخزنی را بدون مرز مهار به ارث میبرد.
لایهٔ دستور جای اشتباهی برای جستجوی ایمنی است. پرامپتها توصیهای هستند. آنها توسط مدلی تفسیر میشوند که برای تکمیل وظیفه بهینهسازی میشود، نه برای سیاست طبقهبندی دادههای شما. پرامپتی که میگوید «پوشهٔ اسرار را نخوان» یک پیشنهاد است، نه یک کنترل دسترسی. مرز مخزن یک کنترل دسترسی است. توسط filesystem، توسط remote، و توسط credentialی که عامل در اختیار دارد اعمال میشود. مهندسی واقعی آنجاست.
چرا عاملهای کدنویسی یک کلاس افشای متفاوت هستند
بیشتر استقرارهای عامل LLM با APIها تعامل دارند. عاملهای کدنویسی با یک سیستم کنترل نسخه تعامل دارند. این تفاوت، حالتهای شکست را به سه شکل تغییر میدهد.
اول، عامل درون یک working tree با دسترسی خواندن کامل به تاریخچه، branchها و artifactهای رهاشده عمل میکند. یک checkout یک snapshot از وضعیت فعلی نیست؛ یک سابقهٔ کامل از هر چیزی است که تاکنون commit شده. اگر یک secret commit شده و سپس حذف شده باشد، هنوز در object store است. اگر یک تصویر روی یک feature branch اضافه شده باشد، قابل دسترسی است. عامل نیازی به دستور برای یافتن آن ندارد. ابزارهای استاندارد — git log، git grep، git cat-file — آن را بهعنوان بخشی از کاوش معمولی آشکار میکنند.
دوم، عاملهای کدنویسی بهطور معمول دستوراتی اجرا میکنند که artifactهای باینری تولید و commit میکنند. خروجیهای build، وزنهای مدل، اسکرینشاتها، test fixtureها، گزارشهای تولیدشده. اینها بهطور تمیز diff نمیشوند. یک diff متنی از یک فایل باینری یا یک خط «Binary files differ» است یا دیواری از بایتهای ناخوانا. بازبینها از آنها رد میشوند. artifact وارد تاریخچه میشود، و وقتی در تاریخچه باشد، در هر clone بعدی هم هست.
سوم، کانال خروجی یک سیستم کنترل نسخه با یک remote است. هر اشتباه بهطور بالقوه عمومی و دائمی است. یک push به یک remote عمومی یک انتقال فایل نیست؛ یک رویداد انتشار است. بازنویسی تاریخچه پس از واقع ممکن است اما مخرب است، و چیزی را که قبلاً fetch شده از انتشار خارج نمیکند. برگشتناپذیری ساختاری است، نه رویهای.
این سه ویژگی به یک کلاس افشای مشخص ترکیب میشوند: عاملی که میتواند کل تاریخچه را بخواند، artifactهای مبهم بنویسد، و به یک remote منتشر کند. این یک پروفایل ریسک متفاوت از عاملی است که میتواند یک API محدود را فراخوانی کند.

قواعد دامنهبندی که از این نتیجه میشوند
واحد مهار، مرز مخزن است. قواعد زیر مستقیماً از این نتیجه میشوند.
کمعمق و دامنهدار clone کنید. عاملی که به یک package نیاز دارد نباید تاریخچهٔ monorepo را دریافت کند. از یک clone کمعمق (--depth 1) و یک sparse checkout (--filter=blob:none، --sparse) استفاده کنید تا working tree فقط شامل مسیرهایی باشد که وظیفه نیاز دارد. این یک بهینهسازی عملکرد نیست؛ کاهش سطح خواندن است. اگر عامل نتواند object را ببیند، نمیتواند object را نشت دهد.
credential عامل را از credential انسان جدا کنید. عامل نباید یک personal access token داشته باشد که بتواند هر مخزنی را که انسان میتواند بخواند، بخواند. باید یک credential محدود به مخزن مشخص داشته باشد، و آن credential باید فقط حق push به یک remote staging را داشته باشد. remote staging جایی است که commitهای نوشتهشده توسط عامل فرود میآیند. ارتقا به remote متعارف یک مرحلهٔ جداگانه و تحت کنترل انسان است.
مخزن را بهعنوان یک سطح قابلیت در نظر بگیرید. قبل از تصمیمگیری دربارهٔ آنچه عامل میتواند بنویسد، آنچه را که میتواند بخواند فهرست کنید. این همان انضباط امنیت مبتنی بر قابلیت است: مجموعهٔ objectهای قابل خواندن، مجموعهٔ objectهای قابل exfiltrate را تعریف میکند. اگر فهرست شامل تاریخچه، branchها و فایلهای رهاشده باشد، دامنهٔ نوشتن عامل باید بهطور متناظر باریک باشد.
معاوضهٔ صادقانه: cloneهای کمعمق و دامنهدار برخی جریانهای کاری عامل را میشکنند. عاملهایی که نیاز به bisect یک regression، ردیابی یک تغییر در طول تاریخچه، یا استدلال دربارهٔ یک branch دیرینه دارند، شکست میخورند یا نتایج بدتری تولید میکنند. کاهش این مشکل فراهم کردن یک مسیر جداگانه، فقطخواندنی و auditشده برای دسترسی به تاریخچه است — نه گستردهتر کردن clone پیشفرض. پیشفرض باید سطح باریک باشد؛ استثنا باید صریح و ثبتشده باشد.
تشخیصی که به رفتار درست عامل وابسته نیست
تشخیص نباید به پیروی عامل از دستورات متکی
blobهای staged را قبل از commit اسکن کنید، نه بعد از push. یک pre-commit hook که محتوای staged را برای رشتههای با آنتروپی بالا و headerهای تصویر شناختهشده بررسی میکند، این کلاس نشت را قبل از ورود به تاریخچه میگیرد. اسکن آنتروپی بالا credentialها و کلیدها را میگیرد. اسکن header بایتهای جادویی PNG، JPEG، GIF و WebP را میگیرد. هر دو ارزان و قطعی هستند. نکته این است که آنها را روی object staged اجرا کنید، نه روی working tree، زیرا object staged همان چیزی است که commit خواهد شد.

inventory artifact را diff کنید، نه فقط diff منبع را. هر commit نوشتهشده توسط عامل باید یک manifest از فایلهای اضافهشده، تغییریافته و حذفشده، با انواع و اندازههای فایل تولید کند. بازبینها باید آن manifest را در کنار diff منبع ببینند. commitی که یک باینری ۲ مگابایتی به یک مخزن فقط-منبع اضافه میکند یک سیگنال است، صرفنظر از اینکه باینری شامل چه چیزی است. diff inventory سطح بازبینی برای artifactهاست؛ diff منبع سطح بازبینی برای منطق است.
در مورد ایجاد remote هشدار بدهید. مرحلهٔ برگشتناپذیر push به یک remote است، و برگشتناپذیرترین نوع آن ایجاد یک remote جدید است. در مورد آن هشدار بدهید. ایجاد remote را بهعنوان یک عملیات ممتاز که نیاز به تأیید صریح دارد، جدا از commitهای معمولی، در نظر بگیرید.
این برای طراحی ابزار عامل چه معنایی دارد
حالت شکست از بین نمیرود، زیرا پیامد دادن مخازن واقعی به عاملهاست. ابزارها باید مسیر ایمن را مسیر پیشفرض کنند.
یک ابزار commit باید یک manifest artifact اعلامشده را نمایش دهد، نه یک shell escape. بهجای دادن یک shell به عامل و امیدواری به اینکه git add را روی فایلهای درست اجرا کند، ابزار باید از عامل بخواهد اعلام کند چه چیزی را قصد commit دارد: فهرستی از مسیرها، انواعشان و منشأشان. سپس ابزار دقیقاً همان مجموعه را commit میکند. این کلاس commitهای تصادفی ناشی از فراخوانیهای گستردهٔ git add . و از مراحل build که artifactها را در tree رها میکنند حذف میکند.
credentialهای مخزن باید کوتاهعمر و مقید به یک branch باشند. credentialی که در چند دقیقه منقضی میشود و فقط میتواند به یک branch push کند، شعاع انفجار یک عامل بهخطرافتاده را محدود میکند. این آینهٔ نحوهٔ کار credentialهای هزینهٔ دامنهدار برای پرداختهاست: credential دامنه را حمل میکند، نه فراخوان. عامل نیازی به اعتماد ندارد؛ credential محدود است.
ابزار بازبینی نیاز به یک فیلد نویسندگی منتسب به عامل دارد. commitهای نوشتهشده توسط عامل باید در سطح metadata از commitهای نوشتهشده توسط انسان قابل تشخیص باشند. بازبینها میتوانند بررسی متفاوتی اعمال کنند: یک commit نوشتهشده توسط انسان ممکن است بازبینی معمولی بگیرد، در حالی که یک commit نوشتهشده توسط عامل بهطور معمول یک بررسی inventory artifact و یک اسکن secret میگیرد. انتساب دربارهٔ سرزنش نیست؛ دربارهٔ مسیریابی به فرآیند بازبینی درست است.
مرز، نه دستور
افشای تصاویر داخلی از طریق عاملهای کدنویسی یادآوری میکند که مرز امنیتی جالب برای سیستمهای agentic پرامپت نیست. مجموعهٔ منابعی است که عامل میتواند به آنها برسد. برای عاملهای کدنویسی، آن مجموعه توسط مخزن تعریف میشود: تاریخچهاش، branchهایش، artifactهای رهاشدهاش، و remoteاش. مهار یعنی باریک کردن عمدی آن مجموعه، تشخیص نقضها روی artifactها نه روی رفتار، و طراحی ابزارهایی که مسیر باریک را مسیر آسان کنند. دستور کمقابلاجراترین بخش سیستم است. مرز مخزن بخشی است که واقعاً دوام میآورد.
