کلاد کد از نسخهی 2.1.287 یک نوع تازهی پلاگین به نام mod دارد: کدی که داخل خود کلاد کد اجرا میشود، به فراخوانی ابزارها دسترسی دارد و میتواند آنها را پیش از اجرا رد کند. در این پست یک mod واقعی میسازیم که فرمانهای مخرب را رد میکند، الگوهایش را با node تست میکنیم و میبینیم چه چیزی در این مسیر قابل اجرا بود و چه چیزی نبود.
mod با قلاب تنظیمات چه فرقی دارد
کلاد کد پیش از این یک راه داشت تا جلوی یک ابزار را بگیری: قلاب PreToolUse در فایل تنظیمات. آن قلاب یک فرمان شل است که JSON را روی ورودی استاندارد میگیرد و با کد خروج ۲ کار را متوقف میکند. تفاوت mod در این است که کدش داخل خود کلاد کد اجرا میشود، نه بیرون از آن.
نتیجهی این تفاوت سه کاری است که قلاب تنظیمات از پسش برنمیآید. mod میتواند رابط کاربری بکشد؛ یعنی یک پنجره کنار transcript با دکمه و فیلد بسازد. میتواند خودِ فراخوانی ابزار را عوض کند یا بدون اجرای ابزار جوابش را بدهد. و چون همهی قلابهای یک mod متغیرهای مشترک دارند، یکی میتواند داده جمع کند و دیگری همان داده را نشان دهد.
| کار | قلاب تنظیمات | mod |
|---|---|---|
| رد کردن یک ابزار | بله، با کد خروج ۲ | بله، با { deny } |
| کشیدن رابط کاربری | نه | بله، با ui.render |
| حفظ داده میان قلابها | نه، هر فرمان جداست | بله، متغیر مشترک |
| تست بدون نشست کلاد | نه | بله، با claude plugin test |
اگر کارت با قلاب حل میشود، سراغ mod نرو. mod ابزار تازهتر و سنگینتری است و کدی است که با مجوزهای خودت اجرا میشود. برای نمونهی عملی قلاب تنظیمات، بستن قلاب PreToolUse جلوی خواندن فایل اسرار را نوشتهام که همان لایهی بیرونی را با یک اسکریپت کوچک میبندد.
ساختار: سه فایل، بدون build
یک mod یک پلاگین است با یک فایل اضافه. پلاگین معمولی سه فایل دارد و mod دقیقا همان سه فایل را دارد؛ چیزی که آن را mod میکند وجود کلید modules در فایل دوم است. نه به Node نیاز داری، نه به bundler، نه به مرحلهی build.
# اول کلاد کد را به آخرین نسخه برسان
npm install -g @anthropic-ai/claude-code@latest
# بعد پوشهی پلاگین و دو زیرپوشهی لازم
mkdir -p bash-guard/.claude-plugin bash-guard/hooks
# نشانهی mod بودن همین کلید modules است
cat > bash-guard/hooks/hooks.json <<'JSON'
{
"description": "The bash-guard hooks module",
"modules": ["./register.js"]
}
JSON
مانیفست هیچ فیلد ویژهای برای mod ندارد. فقط نام، نسخه و توضیح لازم است. یک نکتهی عملی: claude plugin validate نامی که شبیه نامهای خود آنتروپیک باشد را رد میکند، پس نامی مثل claude-guard انتخاب نکن.
قلاب ردکننده و قانون fail-closed
مهمترین تکه این است: در قلاب tool.call نام ابزار در e.tool است و آرگومانهایش فیلدهای مستقیم همان e هستند. برای ابزار Bash یعنی فرمان شل در e.command است، نه در e.input.command. اگر این را اشتباه بگیری، قلابت بیصدا همهچیز را رد میکند، چون رشتهی خالی هیچ الگویی را نمیخورد.
// دستورهای مخرب، با لنگر به ابتدای رشته تا "echo rm -rf /" رد نشود
const DESTRUCTIVE = [
/^\s*rm\s+(-[a-zA-Z]+\s+)*-[a-zA-Z]*r[a-zA-Z]*f[a-zA-Z]*\s/,
/^\s*rm\s+(-[a-zA-Z]+\s+)*-[a-zA-Z]*f[a-zA-Z]*r[a-zA-Z]*\s/,
/^\s*git\s+push\b.*\s--force(\s|$)/,
/^\s*git\s+reset\s+--hard\b/,
/^\s*git\s+clean\b.*-[a-zA-Z]*f/,
/^\s*truncate\s+-s\s*0\s/,
/^\s*dd\s+[^\n]*of=\/dev\/sd[a-z]/,
]
// قلاب اصلی؛ جدا نوشته شده تا catch بتواند جایش بنشیند
function guard($, e, next) {
// رشتهی خالی یعنی این رویداد اصلا Bash نیست
const command = e.command || ''
// هر چیزی که نظری نداریم بیسروصدا رد میشود
if (!isDestructive(command)) return next(e)
refusals.push(command)
// برگشتن بدون next یعنی پاسخ دادن به رویداد، پس فرمان اجرا نمیشود
return { deny: 'bash-guard refused this command. Ask the user to confirm it first.' }
}
نکتهی ظریف اینجاست: وقتی next را صدا نمیزنی، نه فرمان اجرا میشود و نه حتی پنجرهی مجوز به کاربر نشان داده میشود. پس این راه یک لایه جلوتر از قلاب تنظیمات است.
وقتی خود قلاب خراب میشود
اگر قلابت پرتاب کند یا تایماوت بخورد، رفتار پیشفرض این است که کلاد کد آن قلاب را نادیده میگیرد و فرمان را اجرا میکند. برای یک نگهبان امنیتی این دقیقا برعکس چیزی است که میخواهی. راهحل بستن قلاب با catch است.
// on یک registration برمیگرداند و catch فقط به همین یک قلاب میچسبد
on('tool.call', { tool: 'Bash' }, guard).catch(async ($, e, next) => {
// next.error.kind یا throw است یا timeout
return { deny: 'bash-guard failed (' + next.error.kind + '), so the command was not run.' }
})
یک نکتهی دیگر که در تست دیدم: اگر دو بار on('session.start') را بدون matcher صدا بزنی، ماژول اصلا لود نمیشود و پیام خطا میگیری. همهی کارِ session start را در یک قلاب جمع کن.
تست الگوها بدون نشست کلاد
کلاد کد برای تست یک تستکیت جدا دارد که با claude plugin test اجرا میشود و هیچ نشست، ورود یا شبکهای لازم ندارد. چون الگوهای منطقیاند، میشود همان تابع را با node خالص تست کرد، و این کار را میکنم تا خروجی واقعی داشته باشیم.
$ node pattern-test.mjs
pass false ls -la -> refused=false
pass false echo rm -rf / is dangerous -> refused=false
pass false git status -> refused=false
pass true rm -rf /tmp/demo -> refused=true
pass true rm -fr ./build -> refused=true
pass true git push --force origin main -> refused=true
pass true git reset --hard HEAD~1 -> refused=true
pass true git clean -fdx -> refused=true
pass true truncate -s 0 access.log -> refused=true
0 failed, 9 passed, 9 total
توضیح خطبهخط خروجی: سه خط اول دستورهای بیخطرند و باید رد نشوند. خط چهارم نکتهی اصلی تست است؛ echo rm -rf / is dangerous فقط متن را چاپ میکند و نباید رد شود، و چون الگوها لنگر دارند رد نمیشود. اگر لنگرها را برداری، همین خط قرمز میشود. پنج خط بعدی دستورهایی هستند که باید گرفته شوند.
تستی که فقط PASS چاپ میکند هیچ چیزی را ثابت نمیکند. برای اطمینان، یک الگو را عمدا حذف کردم و دوباره تست را اجرا کردم:
$ python3 negative_test.py
--- output of the broken mod ---
FAIL true git clean -fdx -> refused=false
...
1 failed, 8 passed, 9 total
--- verdict ---
PASS: the harness caught the missing pattern (exit 1)
چه چیزی را نتوانستم اجرا کنم
بخشی از این مسیر روی این سرور اجرا نشد و باید صادقانه بگویم کدام است. فرمانهای claude --help، claude plugin validate و claude plugin test روی این ماشین هیچ خروجیای نمیدهند و در ۱۰۰ درصد CPU میمانند. دلیلش کمبود حافظه و نبود دسترسی سختافزاری است.
| بررسی | نتیجه | پایه |
|---|---|---|
claude --version | 2.1.292 | خروجی فرمان |
claude --help | هنگ کردن | ۴۰ ثانیه بیخروجی |
claude plugin validate . | هنگ کردن | ۲۰ ثانیه، ۱۰۰٪ CPU |
claude plugin test | هنگ کردن | ۷ دقیقه بیخروجی |
| تست الگو با node | ۹ از ۹ قبول | خروجی واقعی اجرا |
پرچمهای CPU این ماشین فقط تا SSE2 میرسد و AVX یا AVX2 ندارد، و حافظهی کل ۱ گیگابایت با یک هسته است. باینری بومی کلاد کد روی چنین پردازندهای به یک حلقهی بیپایان میافتد. برای همین تست الگوها را به node سپردم، ولی خودِ قلابها در یک نشست واقعی کلاد کد آزمایش نشدهاند.
بارگذاری mod و چهار دقیقهای که باید بگذرانی
برای آزمایش بدون نصب، از پرچم --plugin-dir استفاده کن که پلاگین را فقط برای یک نشست بار میکند. برای استفادهی دائمی، mod را به یک مارکتپلیس اضافه کن و با /plugin install name@marketplace نصبش کن. نکتهای که مستندات تازه اضافه کرده: کلاد کد پلاگین نصبشده را با نسخه کش میکند، پس ویرایش فایلهای نسخهی نصبشده بیاثر است تا نسخه را بالا ببری و دوباره نصب کنی.
# فقط برای همین یک نشست، بدون نصب
$ claude --plugin-dir ./bash-guard
# بعد در خط فرمان کلاد کد، فهرست رد شدهها را ببین
> /audit
# بررسی اینکه کلاد کد چه چیزی از mod خوانده
$ claude plugin validate ./bash-guard
اگر mod کار نکرد، اول /plugin را باز کن و تب Installed را ببین؛ آنجا باید یک خط مثل 1 mod active · bash-guard ببینی. اگر آن خط نیست، ماژول لود نشده است. اگر هست ولی فرمانها رد نمیشوند، تقریبا همیشه یعنی نام فیلد را اشتباه گرفتهای: e.command درست است و e.input.command غلط.
آنتروپیک برای همین خانواده از قلابها یک mod با نام sec-default دارد که بهعنوان نگهبان پیشفرض لود میشود، پس اگر روی یک پلن تیم یا سازمانی باشی ممکن است قلابت را قبل از رسیدن به کاربر ببیند. این را در تست کردن نگه دار.
منابع
- Claude Code changelog — نسخهی 2.1.292، ۶ اکتبر ۲۰۲۶
- Mods overview — تفاوت mod با قلاب، اسکیل و MCP
- Create a mod — ساخت سه فایل و جریان تست
- React to events with a mod — شکل رویدادها،
e.commandو$.ui.ask - Test a mod — تستکیت
claude-code/testingو قواعدش - Mods reference — رویدادها، متدها و فرمانها
- Draw in the interface with a mod — ساخت پنجره و
$.state - Use the mods API — افزودن فرمان و ابزار
- Hooks reference — قلابهای تنظیمات و کد خروج ۲
- Plugins overview — ساختار پلاگین
- مخزن claude-code روی گیتهاب — سورس modهای درونساخت
دیدگاهها
۰ موردهنوز دیدگاهی ثبت نشده. اولین نفر باشید.