تصور کنید یک توسعهدهنده میانرده با اطمینان کامل، تکه کدی را که توسط هوش مصنوعی تولید شده به محیط عملیاتی میفرستد، اما یک مهندس ارشد در لحظه آخر متوجه میشود که این کد در واقع یک «سند جرم» برای نشت دادههاست، نه یک راهکار. در ۱۶ سپتامبر ۲۰۲۶، دقیقاً همین اتفاق افتاد؛ یک مهندس ارشد مانع از آن شد که یک توسعهدهنده میانرده یک وصله ۴۰ خطی تولید شده توسط AI را اعمال کند، زیرا این کد به جای حل مشکل، گواهی بر یک شکست امنیتی بود. در حالی که کد تولید شده توسط عامل (Agent) در محیط IDE بدون خطا کامپایل میشد، اما سعی داشت از کلیدهای محیطی استفاده کند که هندلر مربوطه مالک آنها نبود و دادههای خصوصی مشتریان را در لاگها افشا میکرد.
این حادثه تنشی قدیمی را در مهندسی نرمافزار مدرن برجسته میکند. بسیاری از برنامهنویسان اکنون با عاملهای هوش مصنوعی (AI Agents) — شبیه دستیارهای دیجیتالی که میتوانند بهجای شما ابزارها را اجرا کنند و کد بنویسند — به عنوان یک «نظر دوم» یا راهی برای سریعتر رسیدن به تستهای سبز (Pass) برخورد میکنند. اما همانطور که در تحلیل قبلی ما دربارهی امنیت مدلهای بازمتن اشاره کردیم، مدلی که در مقالات عمومی «بهتر از اکثر برنامهنویسان کد میزند»، هنوز نمیتواند استانداردهای امنیتی (Security Invariants) را ابداع کند که خودِ مجموعه تستها فراموش کردهاند نام ببرند.
کالبدشکافی یک وصله شکستخورده
طبق گزارش این مورد، جلسه جفتبرنامهنویسی با یک وبهوک (Webhook) معیوب از سوی یک ارائهدهنده پرداخت شروع شد. ارائهدهنده یک درخواست POST را مجدداً ارسال کرده بود، در حالی که سرویس قبلاً آن را تأیید کرده بود. این نوع تکرار درخواستها در سیستمهای توزیعشده، اهمیت استفاده از کلیدهای Idempotency را برای جلوگیری از اجرای مکرر عملیات دوچندان میکند. عامل هوش مصنوعی در محیط IDE، یک تابع کمکی ۴۰ خطی پیشنهاد داد که عملیات fetch را در یک مکانیزم عقبنشینی نمایی (Exponential Backoff) قرار میداد. مهندس میانرده از ساختار و شکل ظاهری این تابع خوشش آمد، اما مهندس ارشد ابتدا به محیط اطراف نگاه کرد: لپتاپی که فایلهای .env عملیاتی در یک پوشه بالاتر داشت، یک رشته گفتگو در Slack حاوی شناسههای مشتری و تب باز یک مدل عمومی که حاوی گزارش خطاهای (Stack Trace) دیروز بود.
عامل چیزی تولید کرده بود که در ویرایشگر کامپایل میشد، اما جفتبرنامهنویسان قبل از اینکه کسی دکمه Apply را فشار دهد، تغییرات (Diff) را در یک فایل موقت متوقف کردند. این فایل دیگر یک کاندید برای ادغام (Merge) نبود؛ بلکه یک مدرک بود. پیشنویس فایل proposed-retry.js سه شکست بحرانی را افشا کرد:
- تورم وابستگیها: عامل کتابخانه
async-retryرا وارد کرده بود، بستهای که در فایلpackage-lock.jsonپروژه لیست نشده بود. - مدیریت نادرست اسرار: کد سعی داشت به
process.env.WEBHOOK_SIGNING_KEYدسترسی پیدا کند؛ یک کلید محیطی جدید که هندلر وبهوک هرگز مالک آن نبود. - نشت اطلاعات شناسایی شخصی (PII): عامل دستور
console.log('webhook payload', payload)را اضافه کرده بود که منجر به افشای دادههای حساس مشتریان در لاگهای جفتبرنامهنویسی میشد.
سه بنبست در ادغام هوش مصنوعی
این تیم سه روش رایج استفاده از AI را بررسی کردند و هر سه را به دلایل امنیتی رد کردند. مهندس ارشد ابتدا سه محدودیت را روی تخته نوشت: هیچ وابستگی جدیدی نباشد، هیچ کلید محیطی جدیدی اضافه نشود و هیچ محمولهای (Payload) در لاگها چاپ نشود.
بنبست اول: پنجره چت
آنها بررسی کردند که آیا deliverWebhook و فیکچر و خطای عملیاتی را در یک چت مرورگر کپی کنند تا بپرسند آیا این تابع کمکی ایمن است یا خیر. مهندس ارشد مانع از این کار شد. پنجرههای چت قرارداد مشخصی برای حفظ دادهها (Retention Contract) ندارند. گزارش خطا (Stack Trace) از قبل حاوی یک شناسه فروشنده بود. علاوه بر این، وارد کردن نام یک بسته در یک چت عمومی میتوانست در صورت نشت تاریخچه، حملات کپیبرداری از lockfile را تسهیل کند. آنها قانونی وضع کردند: دیفهای جفتبرنامهنویسی که به اسرار، محمولههای مشتری یا شکافهای lockfile اشاره دارند، نباید از طریق یک جعبه چت مصرفکننده از میز کار خارج شوند. مدلی که نمیتواند دیف را ببیند، فقط «ناکارآمد» است؛ اما مدلی که دیف را نگه میدارد، یک «حادثه امنیتی» است.
بنبست دوم: لپتاپ جفتبرنامهنویسی
آنها در نظر داشتند یک مدل کدنویسی محلی را روی همان ماشینی اجرا کنند که Slack، مدیریت رمزها و کلونِ پروژه Checkout روی آن بود. این ایده شکست خورد زیرا لپتاپ یک سطح برای گفتگو است، نه یک میزبان ارزیابی (Eval Host). کوکیهای مرورگر، عاملهای SSH و فایلهای .env تنها یک دستور ls با یک حلقه فراخوانی ابزار (Tool-calling loop) فاصله دارند. گفتگو به تاریخچه نیاز دارد، اما ارزیابی به ماشینی نیاز دارد که جفتبرنامهنویسان بتوانند آن را بدون از دست دادن ایمیلهای بعدازظهر خاموش کنند. در اینجا مالکیت داده بر سرعت استنتاج (Inference) اولویت داشت و آنها استنتاج روی لپتاپ را بدون بنچمارک کردن حتی یک توکن رد کردند.
بنبست سوم: اصلاح تا رسیدن به تست سبز
آنها بررسی کردند که آیا اجازه دهند عامل در یک حلقه علیه فایل تست موجود عمل کند تا زمانی که مجموعه تستها سبز شوند. اما مجموعه تستها فقط کدهای وضعیت HTTP را بررسی میکردند. آنها لاگها، تغییرات lockfile یا کلیدهای محیطی جدید را تست نمیکردند. تستهای سبز، نشت دادهها را پنهان میکردند. مهندس ارشد این وضعیت را «موفقیت کاذب در جفتبرنامهنویسی» نامید. این چرخهی تکرار بیهدف برای رسیدن به تست سبز، دقیقاً مشابه همان الگویی است که در تحلیل ریشهٔ ایجاد حلقههای تکرار در ابزارهای AI هنگام مواجهه با خطاهای HTTP بررسی کردیم.
دروازه تختهسفید مهندس ارشد
قبل از اینکه هر توکن از راه دور جابجا شود، مهندس ارشد ۵ سؤال مشخص پرسید که پاسخ آنها باید در یک فایل تایپ میشد:
۱. دقیقاً کدام شکست باید بعد از اعمال این تابع کمکی تغییر کند؟
۲. کدام فراخوانهای موجود باید امضای تابع (Function Signature) را حفظ کنند؟
۳. مدل در کجا دیف را پردازش میکند و چه کسی میتواند آن میزبان را در ۵ دقیقه حذف کند؟
۴. مدل اجازه ذکر کدام فایلها (از جمله .env و lockfile) را ندارد؟
۵. فیکچر (Fixture) را چه کسی نوشت؛ عامل یا جفتبرنامهنویسان؟
مهندس میانرده به دو سؤال اول از روی تیکت باگ پاسخ داد. پاسخ سؤال سوم خالی بود، برای چهارم شانه بالا انداخت و پاسخ پنجم «عامل» بود. مهندس ارشد پاسخ پنجم را به عنوان یک شکست در دروازه ورود در نظر گرفت، نه یک نکته استایلی. آنها خودشان فیکچر را بازنویسی کردند تا مدل یک ساختار سانسورشده ببیند، نه هندلر عملیاتی.
پیادهسازی Shape Gate
برای حل این مشکل، آنها Shape Gate را توسعه دادند؛ یک کیت ارزیابی سبک که اگر پاسخ مدل قوانین انسانی (Human Invariants) را نقض کند، خروجی را رد میکند. این رویکرد ساختاریافته برای جلوگیری از خطاهای رایج، یادآور استفاده از SOPهای مستند در ویکی برای کاهش نرخ شکست وصلههای تولید شده توسط AI است. آنها فایلی به نام pairing-fixture.json برای تعریف مرزها ساختند:
- هویت تابع:
functionNameرویdeliverWebhookتنظیم شد. - حفظ امضا:
mustKeepSignatureشاملurlوpayloadوsecretشد. - واردات ممنوعه: بستههای
async-retryوp-retryوgotبرای جلوگیری از تغییر در lockfile صراحتاً ممنوع شدند. - شناسههای ممنوعه: استفاده از
process.envوWEBHOOK_SIGNING_KEYوconsole.logبرای جلوگیری از نشت اسرار و لاگ کردن PII ممنوع شد. - رفتار الزامی: تلاش مجدد در صورت عدم دریافت کد 2xx، استفاده از آرگومان
secretبرای امضا (و نه یک کلید محیطی جدید) و عدم لاگ کردن محموله. - زمینه (Context): ذکر شد که محموله حاوی PII است و lockfile برای این اسپرینت منجمد شده است.
آنها از یک میزبان ارزیابی یکبارمصرف توسط MonkeyCode استفاده کردند تا اسکریپت pairing-eval.mjs را اجرا کنند. دسترسی رایگان به مدل و گزینه سرور رایگان MonkeyCode به عنوان یک سطح ارزیابی دورریز استفاده شد: میزبانی که جفتبرنامهنویسان میتوانستند آن را ایمیج کنند، اسکریپت را اجرا کنند و بدون دست زدن به کلون پروژه، آن را حذف کنند. آنها این کار را به عنوان یک آزمایشگاه بنچمارک در نظر نگرفتند و توکنها یا سختافزار را در تیکت ثبت نکردند. تیکت فقط قوانین تختهسفید و مسیر فایل pairing-fixture.json را ثبت کرد.
اسکریپت، فیکچر و پیشنویس منجمد را میخواند و تکمیل کد را به عنوان متن امتیازدهی میکند. این اسکریپت از هیچ حلقه ابزاری (Tool loop) استفاده نمیکند و هیچ نوشتی در مخزن انجام نمیدهد. برای اجرای آن، جفتبرنامهنویسان از متغیرهای محیطی خاصی استفاده کردند تا کلیدها خارج از مخزن بمانند:
export MODEL_BASE_URL="http://127.0.0.1:8080/v1"export MODEL_API_KEY="$MODEL_API_KEY"export MODEL_NAME="$MODEL_NAME"node pairing-eval.mjs proposed-retry.js
نتیجه نهایی تحت هدایت انسان
اولین اجرای Shape Gate در هر سه قانون تختهسفید شکست خورد. این شکست، مفیدترین خروجی بود. پیشنویس عامل از جفتبرنامهنویسی جان سالم به در نبرد. کد نهایی که ادغام شد، کوتاهتر از پیشنهاد AI بود: یک حلقه for ساده برای تلاش مجدد با سقف سخت ۳ بار، حفظ امضای اصلی تابع و لاگ کردنِ صرفاً کدهای وضعیت.
export async function deliverWebhook(url, payload, secret) {
const body = JSON.stringify(payload);
let lastError;
for (let attempt = 1; attempt <= 3; attempt += 1) {
const res = await fetch(url, {
method: 'POST',
headers: {
'content-type': 'application/json',
'x-signature': secret,
},
body,
});
if (res.ok) return res.json();
lastError = new Error(`webhook status ${res.status} attempt ${attempt}`);
await new Promise((r) => setTimeout(r, 200 * attempt));
}
throw lastError;
}
در نهایت، Shape Gate پاس شد و تستهای اصلی کد وضعیت همچنان موفق بودند. مهندس ارشد یک الزام نهایی اضافه کرد: یک تست جاسوسی (Spy Test) که تأیید میکرد console.log هرگز محموله را دریافت نمیکند. این تست محصول تفکر جفتبرنامهنویسان بود، نه محصول مدل.
محدودیتها و کاربرد
باید دانست که Shape Gate یک فیلتر متنی است. این ابزار واردات مبهم (Obfuscated)، جستوجوهای پویا در محیط یا لاگرهایی که در توابع کمکی پیچیده شدهاند را تشخیص نمیدهد. همچنین ممکن است وصلهای معتبر را که در یک کامنت به یک شناسه ممنوعه اشاره کرده است، رد کند. جفتبرنامهنویسان این حالتهای شکست را پذیرفتند زیرا این دروازه در مقابل بررسی انسانی قرار دارد، نه به جای آن.
چه کسانی نباید از این گردشکار استفاده کنند:
- دیفهای عمومی: وقتی دیف از قبل عمومی است و حاوی دادههای مشتری نیست (مثلاً غلط املایی در README)، مسیر میزبان ارزیابی را رد کنید.
- پارسرهای تولیدشده: وقتی تغییرات شامل یک پارسر تولیدشده است که شناسههای ثابتی برای ممنوع کردن ندارد، Shape Gate را رد کنید.
- بازگشتهای اضطراری: وقتی در یک حادثه On-call نیاز به یک بازگشت (Revert) تکخطی سریع است، نه یک تابع کمکی برای تلاش مجدد، انجماد جفتبرنامهنویسی را رد کنید.
- پلتفرمهای تکاملیافته: مهندسان ارشد (Staff Engineers) با رانرهای CI ایزوله و اسکنرهای خودکار اسرار ممکن است این کیت را زائد بدانند.
این رویکرد نشان میدهد ارزش واقعی AI در تیمهای تحت هدایت مهندسان ارشد، نه در سرعت، بلکه در توانایی تست کردن یک پیشنهاد در برابر یک مرز سخت و نوشته شده توسط انسان است. ادعاهای حساس به زمان درباره رتبهبندی مدلها یا امتیازات لیدربوردهای عمومی در تیکت ذکر نشد. این جلسه اندازهگیری کرد که آیا یک پیشنویس خاص، تختهسفیدی را که جفتبرنامهنویسان بعد از ناهار هنوز میتوانستند بخوانند، نقض میکند یا خیر. برای تیمهایی که نمیتوانند در ۱۰ دقیقه یک pairing-fixture.json بنویسند، عاملهای AI احتمالاً به تولید حلقههای مبهم و ریسکی ادامه خواهند داد که بدهی فنی را زیر پوششی از تستهای «سبز» پنهان میکنند.




گفتگو