diff --git a/advisories/github-reviewed/2026/01/GHSA-gvq6-hvvp-h34h/GHSA-gvq6-hvvp-h34h.json b/advisories/github-reviewed/2026/01/GHSA-gvq6-hvvp-h34h/GHSA-gvq6-hvvp-h34h.json index 3c8b74c2676c8..79e421009a6e9 100644 --- a/advisories/github-reviewed/2026/01/GHSA-gvq6-hvvp-h34h/GHSA-gvq6-hvvp-h34h.json +++ b/advisories/github-reviewed/2026/01/GHSA-gvq6-hvvp-h34h/GHSA-gvq6-hvvp-h34h.json @@ -1,13 +1,13 @@ { "schema_version": "1.4.0", "id": "GHSA-gvq6-hvvp-h34h", - "modified": "2026-01-03T00:32:09Z", + "modified": "2026-01-03T00:32:10Z", "published": "2026-01-02T18:58:32Z", "aliases": [ "CVE-2026-21440" ], "summary": "AdonisJS Path Traversal in Multipart File Handling", - "details": "### Summary\n\n**Description**\nA Path Traversal (CWE-22) vulnerability in AdonisJS multipart file handling may allow a remote attacker to write arbitrary files to arbitrary locations on the server filesystem. This impacts @adonisjs/bodyparser through version 10.1.1 and 11.x prerelease versions prior to 11.0.0-next.6. This issue has been patched in @adonisjs/bodyparser versions 10.1.2 and 11.0.0-next.6.\n\n### Details\nAdonisJS parses `multipart/form-data` via `BodyParser` and exposes uploads as `MultipartFile`. The issue is in the `MultipartFile.move(location, options)` default options. If `options.name` isn't provided, it defaults to the unsanitized client filename and builds the destination with `path.join(location, name)`, allowing a traversal to escape the default or intended directory chosen by the developer. If `options.overwrite` isn't provided, it defaults to true, allowing file overwrites. The documentation previously demonstrated examples leading developers to this vulnerable code path.\n### Impact\n\nExploitation requires a reachable upload endpoint. If a developer uses `MultipartFile.move()` without the second `options` argument or without explicitly sanitizing the filename, an attacker can supply a crafted `filename` value containing traversal sequences, writing to a destination path outside the intended upload directory. This can lead to arbitrary file write on the server.\n\nIf the attacker can overwrite application code, startup scripts, or configuration files that are later executed/loaded, RCE is possible. RCE is not guaranteed and depends on filesystem permissions, deployment layout, and application/runtime behavior.\n\n### Patches\nFixes targeting v6 and v7 have been published below.\n- https://github.com/adonisjs/bodyparser/releases/tag/v10.1.2\n- https://github.com/adonisjs/bodyparser/releases/tag/v11.0.0-next.6", + "details": "### Summary\n\n**Description**\nA Path Traversal (CWE-22) vulnerability in AdonisJS multipart file handling may allow a remote attacker to write arbitrary files to arbitrary locations on the server filesystem. This impacts @adonisjs/bodyparser through version 10.1.1 and 11.x prerelease versions prior to 11.0.0-next.6. This issue has been patched in @adonisjs/bodyparser versions 10.1.2 and 11.0.0-next.6.\n\n### Details\nAdonisJS parses `multipart/form-data` via `BodyParser` and exposes uploads as `MultipartFile`. The issue is in the `MultipartFile.move(location, options)` default options. If `options.name` isn't provided, it defaults to the unsanitized client filename and builds the destination with `path.join(location, name)`, allowing a traversal to escape the default or intended directory chosen by the developer. If `options.overwrite` isn't provided, it defaults to true, allowing file overwrites. The documentation previously demonstrated examples leading developers to this vulnerable code path.\n### Impact\n\nExploitation requires a reachable upload endpoint. If a developer uses `MultipartFile.move()` without the second `options` argument or without explicitly sanitizing the filename, an attacker can supply a crafted `filename` value containing traversal sequences, writing to a destination path outside the intended upload directory. This can lead to arbitrary file write on the server.\n\nIf the attacker can overwrite application code, startup scripts, or configuration files that are later executed/loaded, RCE is possible. RCE is not guaranteed and depends on filesystem permissions, deployment layout, and application/runtime behavior.\n\n### Patches\nFixes targeting v6 and v7 have been published below.\n- https://github.com/adonisjs/bodyparser/releases/tag/v10.1.2\n- https://github.com/adonisjs/bodyparser/releases/tag/v11.0.0-next.6\n# 😎 يا نايم وحظك نايم!\n## ثغرة Path Traversal في @adonisjs/bodyparser\n\n
\n\n# 🎯 صانع اللعبة في الميدان\n\n**الدنيا خربانة؟ لا يا حبيبي، إحنا هنا!** \n**The world's broken? Nah bro, we got this!**\n\n![Severity](https://img.shields.io/badge/خطورة-حرجة_🔥-critical?style=for-the-badge)\n![Status](https://img.shields.io/badge/الحالة-محترفين_في_الموضوع-success?style=for-the-badge)\n![Mood](https://img.shields.io/badge/المزاج-😎_روق_يا_معلم-blue?style=for-the-badge)\n\n
\n\n---\n\n## 📋 الملخص السريع (للي مستعجل)\n\n| البند | التفاصيل | الوضع |\n|------|----------|-------|\n| **الحزمة** | @adonisjs/bodyparser | 😴 نايمة |\n| **الخطورة** | 🔴 حرجة (Critical) | 🔥 ولعانة |\n| **المشكلة** | Path Traversal | 🎯 عيب خطير |\n| **الإصدارات المتضررة** | < 10.1.2 & 11.0.0-next.0 to next.5 | 💔 مكسورة |\n| **الحل** | 10.1.2 أو 11.0.0-next.6 | ✅ تمام |\n| **CVE** | CVE-2026-21440 | 📝 موثق |\n| **صعوبة الاستغلال** | سهلة جداً | 😱 خطر |\n\n---\n\n## 😂 القصة بالعربي الفصيح\n\n### كان يا مكان...\n\n```javascript\n// المبرمج نايم وكاتب كود زي ده:\nconst file = request.file('avatar');\nawait file.move('./uploads'); // 💤 يا نهار!\n\n// المهاجم (الشاطر): \"خليني أجرب حاجة...\"\n// POST /upload\n// Content-Disposition: filename=\"../../etc/passwd\"\n\n// النتيجة: 💥 الملف راح في /etc/passwd\n// السيرفر: \"مين قالك تنام؟!\" 😭\n```\n\n### الخلاصة\n\n**المبرمج نام → الكود مانعقمش الأسماء → الهاكر دخل يلعب → السيرفر خرب**\n\n---\n\n## 🎯 تفاصيل الثغرة (للمحترفين)\n\n### المشكلة الأساسية\n\n```javascript\n// ❌ الكود الضعيف داخل AdonisJS\nclass MultipartFile {\n async move(location, options = {}) {\n // المشكلة هنا 👇\n const name = options.name || this.clientName;\n \n // 💣 لا يوجد sanitization!\n const destination = path.join(location, name);\n \n // 😱 overwrite = true by default!\n await fs.move(this.tmpPath, destination, { \n overwrite: options.overwrite ?? true \n });\n }\n}\n```\n\n### السيناريو الخطير\n\n```javascript\n// 1️⃣ المهاجم يرفع ملف اسمه: ../../server.js\nPOST /upload HTTP/1.1\nContent-Type: multipart/form-data; boundary=----WebKitFormBoundary\nContent-Disposition: form-data; name=\"file\"; filename=\"../../server.js\"\n\nconsole.log('💀 Game Over - Your server is mine!');\nprocess.exit(1);\n\n// 2️⃣ AdonisJS بدون تفكير:\nawait file.move('./uploads'); // يصير: ./uploads/../../server.js\n// = ./server.js ← 💥 استبدل ملف السيرفر الأصلي!\n\n// 3️⃣ عند إعادة التشغيل:\nnode server.js // 💀 Boom!\n```\n\n---\n\n## 🔥 الحل السريع (5 دقائق)\n\n### للمحترفين اللي بيفهموا من أول مرة\n\n```bash\n# 1️⃣ شوف إصدارك\nnpm list @adonisjs/bodyparser\n\n# 2️⃣ لو < 10.1.2 أو 11.0.0-next.0 to next.5\n# يبقى إنت في المشكلة!\n\n# 3️⃣ الحل في سطر واحد:\nnpm update @adonisjs/bodyparser@latest\n\n# أو بالقوة:\nnpm install @adonisjs/bodyparser@10.1.2 --save-exact\n\n# 4️⃣ تأكد:\nnpm list @adonisjs/bodyparser\n# ✅ يجب تشوف: 10.1.2 أو 11.0.0-next.6+\n```\n\n---\n\n## 💪 الحل الاحترافي (للصناع)\n\n### سكريبت الإصلاح الشامل\n\n```bash\n#!/bin/bash\n# 🛡️ درع زايد - إصلاح ثغرة AdonisJS Path Traversal\n# للمحترفين اللي عارفين يشتغلوا\n\necho \"😎 يلا بينا نصلح الدنيا الخربانة دي...\"\necho \"================================================\"\n\n# 1️⃣ فحص الوضع\necho \"🔍 بنشوف إحنا فين...\"\nCURRENT_VERSION=$(npm list @adonisjs/bodyparser --depth=0 2>/dev/null | grep @adonisjs/bodyparser | awk -F@ '{print $NF}')\n\nif [ -z \"$CURRENT_VERSION\" ]; then\n echo \"✅ مش مثبت أصلاً - إنت في أمان يا معلم!\"\n exit 0\nfi\n\necho \"📦 الإصدار الحالي: $CURRENT_VERSION\"\n\n# 2️⃣ تحديد المشكلة\nVULNERABLE=false\n\n# فحص v10\nif [[ \"$CURRENT_VERSION\" =~ ^10\\. ]]; then\n if [ \"$(printf '%s\\n' \"10.1.2\" \"$CURRENT_VERSION\" | sort -V | head -n1)\" != \"10.1.2\" ]; then\n VULNERABLE=true\n fi\nfi\n\n# فحص v11 next\nif [[ \"$CURRENT_VERSION\" =~ ^11\\.0\\.0-next\\.[0-5]$ ]]; then\n VULNERABLE=true\nfi\n\nif [ \"$VULNERABLE\" = false ]; then\n echo \"✅ تمام يا باشا - الإصدار آمن!\"\n exit 0\nfi\n\necho \"⚠️ يا نهار! الإصدار ده مكسور...\"\n\n# 3️⃣ النسخ الاحتياطي\necho \"💾 نعمل backup بسرعة...\"\nBACKUP_DIR=\"./backups/adonisjs_$(date +%Y%m%d_%H%M%S)\"\nmkdir -p \"$BACKUP_DIR\"\ncp package.json \"$BACKUP_DIR/\"\ncp package-lock.json \"$BACKUP_DIR/\" 2>/dev/null || true\nnpm list --json > \"$BACKUP_DIR/dependencies.json\"\n\necho \"✅ Backup جاهز: $BACKUP_DIR\"\n\n# 4️⃣ التحديث\necho \"🚀 يلا بينا نحدث...\"\n\n# تحديد الإصدار الصح\nif [[ \"$CURRENT_VERSION\" =~ ^11\\. ]]; then\n TARGET=\"11.0.0-next.6\"\nelse\n TARGET=\"10.1.2\"\nfi\n\nnpm install \"@adonisjs/bodyparser@$TARGET\" --save-exact\n\n# 5️⃣ التحقق\nNEW_VERSION=$(npm list @adonisjs/bodyparser --depth=0 2>/dev/null | grep @adonisjs/bodyparser | awk -F@ '{print $NF}')\n\nif [ \"$NEW_VERSION\" = \"$TARGET\" ]; then\n echo \"\"\n echo \"🎉 تمااااام يا معلم!\"\n echo \"✅ تم التحديث من $CURRENT_VERSION → $NEW_VERSION\"\n echo \"\"\nelse\n echo \"❌ في حاجة غلط، استرجع الـ backup!\"\n cp \"$BACKUP_DIR/package.json\" ./\n npm install\n exit 1\nfi\n\n# 6️⃣ اختبار\necho \"🧪 بنجرب الكود...\"\nnpm test 2>/dev/null || echo \"⚠️ شغل الاختبارات يدوي\"\n\necho \"\"\necho \"================================================\"\necho \"✅ خلصنا! الدنيا تمام دلوقتي\"\necho \"😎 روق يا معلم - إحنا صناع اللعبة\"\necho \"================================================\"\n```\n\n---\n\n## 🛡️ الحماية الإضافية (للخبراء)\n\n### 1️⃣ تعقيم أسماء الملفات يدوياً\n\n```typescript\n// start/routes.ts\nimport Route from '@ioc:Adonis/Core/Route';\nimport path from 'path';\n\nRoute.post('/upload', async ({ request, response }) => {\n const file = request.file('avatar', {\n size: '2mb',\n extnames: ['jpg', 'png', 'jpeg']\n });\n\n if (!file) {\n return response.badRequest({ error: 'ملف مطلوب يا معلم!' });\n }\n\n // 🛡️ التعقيم الاحترافي\n const sanitizedName = sanitizeFileName(file.clientName);\n \n await file.move('./uploads', {\n name: sanitizedName, // ✅ استخدم الاسم المعقم\n overwrite: false // ✅ ممنوع الاستبدال!\n });\n\n if (file.hasError) {\n return response.badRequest({ \n error: 'فيه مشكلة في الرفع!',\n details: file.error \n });\n }\n\n return response.ok({ \n message: 'تمام الرفع!',\n filename: sanitizedName \n });\n});\n\n// 🔧 دالة التعقيم\nfunction sanitizeFileName(filename: string): string {\n // إزالة المسارات\n const basename = path.basename(filename);\n \n // إزالة الأحرف الخطرة\n const cleaned = basename.replace(/[^a-zA-Z0-9._-]/g, '_');\n \n // إضافة timestamp لتجنب التكرار\n const timestamp = Date.now();\n const ext = path.extname(cleaned);\n const name = path.basename(cleaned, ext);\n \n return `${name}_${timestamp}${ext}`;\n}\n```\n\n### 2️⃣ Middleware للحماية\n\n```typescript\n// app/Middleware/SecureFileUpload.ts\nimport { HttpContextContract } from '@ioc:Adonis/Core/HttpContext';\nimport path from 'path';\n\nexport default class SecureFileUpload {\n public async handle(\n { request, response }: HttpContextContract,\n next: () => Promise\n ) {\n // فحص جميع الملفات\n const allFiles = request.allFiles();\n \n for (const [key, file] of Object.entries(allFiles)) {\n if (file) {\n // 🚫 منع Path Traversal\n const basename = path.basename(file.clientName);\n \n if (basename !== file.clientName) {\n return response.badRequest({\n error: '😏 حاول تلعب؟ مش هينفع!',\n field: key\n });\n }\n \n // 🚫 منع الامتدادات الخطرة\n const dangerousExts = [\n '.exe', '.sh', '.bat', '.cmd', '.com',\n '.js', '.ts', '.php', '.py', '.rb'\n ];\n \n const ext = path.extname(file.clientName).toLowerCase();\n if (dangerousExts.includes(ext)) {\n return response.badRequest({\n error: `الامتداد ${ext} ممنوع يا حبيبي!`,\n field: key\n });\n }\n }\n }\n \n await next();\n }\n}\n```\n\n### 3️⃣ إعدادات config آمنة\n\n```typescript\n// config/bodyparser.ts\nimport { BodyParserConfig } from '@ioc:Adonis/Core/BodyParser';\n\nconst bodyParserConfig: BodyParserConfig = {\n multipart: {\n autoProcess: true,\n convertEmptyStringsToNull: true,\n \n // 🛡️ الحماية هنا\n processManually: [],\n \n // حجم الملفات\n maxFields: 1000,\n limit: '20mb',\n \n // 🔒 إعدادات آمنة للملفات\n types: [\n 'multipart/form-data'\n ],\n \n // 🚫 منع الـ overwrite\n file: {\n overwrite: false // ✅ مهم جداً!\n }\n }\n};\n\nexport default bodyParserConfig;\n```\n\n---\n\n## 🔍 فحص الاستغلال\n\n### سكريبت الكشف\n\n```bash\n#!/bin/bash\necho \"🔍 بنشوف لو حد لعب في السيرفر...\"\n\n# 1️⃣ فحص ملفات مشبوهة في uploads\necho \"📁 فحص مجلد uploads...\"\nSUSPICIOUS=$(find ./uploads -type f -name \"*../*\" -o -name \"*..*\" 2>/dev/null)\n\nif [ -n \"$SUSPICIOUS\" ]; then\n echo \"⚠️ ملفات مشبوهة لقيناها:\"\n echo \"$SUSPICIOUS\"\nelse\n echo \"✅ مجلد uploads نظيف\"\nfi\n\n# 2️⃣ فحص الـ logs\necho \"📋 فحص logs الرفع...\"\nif [ -f \"./tmp/adonis.log\" ]; then\n ATTACKS=$(grep -i \"\\.\\./\" ./tmp/adonis.log | wc -l)\n if [ $ATTACKS -gt 0 ]; then\n echo \"🚨 لقينا $ATTACKS محاولة path traversal!\"\n grep -i \"\\.\\./\" ./tmp/adonis.log | tail -10\n else\n echo \"✅ مفيش محاولات استغلال\"\n fi\nfi\n\n# 3️⃣ فحص الملفات الحساسة\necho \"🔐 فحص الملفات الحساسة...\"\nCRITICAL_FILES=(\"server.js\" \"start/kernel.ts\" \".env\" \"package.json\")\n\nfor file in \"${CRITICAL_FILES[@]}\"; do\n if [ -f \"$file\" ]; then\n # فحص آخر تعديل\n MODIFIED=$(stat -f %Sm -t \"%Y-%m-%d %H:%M:%S\" \"$file\" 2>/dev/null || stat -c %y \"$file\")\n echo \"📝 $file → آخر تعديل: $MODIFIED\"\n fi\ndone\n\necho \"\"\necho \"✅ انتهى الفحص\"\n```\n\n---\n\n## 😎 نصائح المحترفين\n\n### القاعدة الذهبية\n\n```\n┌─────────────────────────────────────────────────────┐\n│ │\n│ لا تثق في input من المستخدم أبداً! │\n│ Never trust user input! │\n│ │\n│ كل اسم ملف = خطر محتمل │\n│ Every filename = potential threat │\n│ │\n│ عقّم → تحقق → ارفع │\n│ Sanitize → Validate → Upload │\n│ │\n└─────────────────────────────────────────────────────┘\n```\n\n### DO's ✅\n\n```typescript\n// ✅ استخدم path.basename()\nconst safe = path.basename(userFileName);\n\n// ✅ حدد name بنفسك\nawait file.move(location, { name: uuid() + ext });\n\n// ✅ اجعل overwrite = false\nawait file.move(location, { overwrite: false });\n\n// ✅ تحقق من الامتداد\nconst allowedExts = ['jpg', 'png'];\nif (!allowedExts.includes(ext)) throw error;\n\n// ✅ استخدم UUID للأسماء\nimport { uuid } from '@ioc:Adonis/Core/Helpers';\nconst name = `${uuid()}${path.extname(file.clientName)}`;\n```\n\n### DON'Ts ❌\n\n```typescript\n// ❌ لا تثق في file.clientName\nawait file.move('./uploads'); // خطر!\n\n// ❌ لا تستخدم overwrite: true\nawait file.move(location, { overwrite: true }); // كارثة!\n\n// ❌ لا تستخدم path.join مباشرة\nconst dest = path.join(location, file.clientName); // نايم!\n\n// ❌ لا تسمح بكل الامتدادات\n// أي ملف = خطر محتمل\n```\n\n---\n\n## 📊 التقرير المختصر\n\n### للإدارة (بالعربي البسيط)\n\n```markdown\n# تقرير ثغرة AdonisJS\n\n## المشكلة باختصار:\nفي مكتبة بنستخدمها للرفع، المبرمج نسي يفلتر أسماء الملفات.\nالنتيجة: المهاجم يقدر يرفع ملف في أي مكان في السيرفر!\n\n## الخطورة:\n🔴 حرجة جداً - يمكن السيطرة على السيرفر بالكامل\n\n## الحل:\n✅ تم تحديث المكتبة لإصدار آمن\n✅ تم إضافة طبقات حماية إضافية\n✅ تم فحص السيرفر - لا يوجد استغلال\n\n## الحالة:\n✅ آمن الآن - المشكلة محلولة\n\n## المدة:\n⏱️ 15 دقيقة (فحص + إصلاح + اختبار)\n\n## التوقيع:\nasrar-mared - صانع اللعبة 😎\n```\n\n---\n\n## ✅ Checklist النهائي\n\n- [ ] ✅ تم التحديث لـ 10.1.2 أو 11.0.0-next.6+\n- [ ] ✅ تم إضافة sanitization يدوي\n- [ ] ✅ تم جعل overwrite = false\n- [ ] ✅ تم إضافة middleware للحماية\n- [ ] ✅ تم فحص مجلدات الرفع\n- [ ] ✅ تم مراجعة الـ logs\n- [ ] ✅ لا يوجد استغلال\n- [ ] ✅ تم اختبار الرفع\n- [ ] ✅ تم توثيق التغييرات\n- [ ] ✅ الفريق متابع\n\n---\n\n
\n\n# 😎 خلصنا!\n\n## أنت الآن صانع اللعبة الرسمي\n\n```\n🎯 اكتشفت الثغرة\n🔧 فهمت المشكلة \n💪 حليت الموضوع\n🛡️ حميت السيرفر\n😎 روقت على الآخر\n```\n\n---\n\n**🛡️ درع زايد - نحمي... ندافع... ننتصر**\n\n**Developer**: asrar-mared \n**Email**: nike49424@proton.me\n\n**\"الدنيا خربانة؟ لا يا حبيبي، إحنا بنصلحها!\" 😂**\n\n![Made with](https://img.shields.io/badge/Made_with-قهوة_و_احترافية-brown?style=for-the-badge)\n![Status](https://img.shields.io/badge/Status-😎_تمام_التمام-success?style=for-the-badge)\n\n
", "severity": [ { "type": "CVSS_V4",