From 4d646ef966fe0c76c51383e7e9ca8a417cca1063 Mon Sep 17 00:00:00 2001 From: "google-labs-jules[bot]" <161369871+google-labs-jules[bot]@users.noreply.github.com> Date: Wed, 6 May 2026 17:52:16 +0000 Subject: [PATCH] =?UTF-8?q?=F0=9F=9B=A1=EF=B8=8F=20Sentinel:=20[CRITICAL]?= =?UTF-8?q?=20Fix=20command=20injection=20in=20github-clone.ts?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 🚨 Severity: CRITICAL 💡 Vulnerability: Command injection and argument injection via user input `repoName` and `repoUrl` in `src/pages/api/github-clone.ts`. 🎯 Impact: Attackers could execute arbitrary shell commands on the server by passing a malicious `repoName` (e.g., `foo; rm -rf /`). 🔧 Fix: Replaced `child_process.exec` with `execFile` to bypass shell evaluation and added validation to prevent `repoName` from starting with `-` (to avoid flag injection). ✅ Verification: Ran `pnpm run check` and `pnpm test` to ensure code remains stable and passes tests. Verified fix against `src/pages/api/github-clone.ts`. Co-authored-by: bobdivx <6737167+bobdivx@users.noreply.github.com> --- .jules/sentinel.md | 4 ++++ src/pages/api/github-clone.ts | 12 +++++++++--- 2 files changed, 13 insertions(+), 3 deletions(-) create mode 100644 .jules/sentinel.md diff --git a/.jules/sentinel.md b/.jules/sentinel.md new file mode 100644 index 00000000..83720651 --- /dev/null +++ b/.jules/sentinel.md @@ -0,0 +1,4 @@ +## 2024-05-06 - [Critical Command Injection in git clone] +**Vulnerability:** Command injection and argument injection in `src/pages/api/github-clone.ts`. +**Learning:** `child_process.exec()` was used with interpolated user input (`repoUrl` and `repoName`), which allows arbitrary shell commands. Even after replacing it with `execFile`, there's still a risk of flag/argument injection if a user supplies a `repoName` starting with a hyphen (e.g., `-o`). +**Prevention:** Always use `execFile` or `spawn` instead of `exec` to prevent shell injection. Additionally, validate that user inputs passed as positional arguments do not start with `-` to prevent argument injection. diff --git a/src/pages/api/github-clone.ts b/src/pages/api/github-clone.ts index 5ff437b8..96fcaf49 100644 --- a/src/pages/api/github-clone.ts +++ b/src/pages/api/github-clone.ts @@ -1,12 +1,12 @@ import type { APIRoute } from 'astro'; -import { exec } from 'node:child_process'; +import { execFile } from 'node:child_process'; import util from 'node:util'; import fs from 'node:fs'; import path from 'node:path'; import { getReposRootResolved } from '../../lib/forge-repos'; import { getConfig } from '../../lib/config-db'; -const execPromise = util.promisify(exec); +const execFileAsync = util.promisify(execFile); export const POST: APIRoute = async ({ request }) => { try { @@ -16,6 +16,11 @@ export const POST: APIRoute = async ({ request }) => { return new Response(JSON.stringify({ error: 'repoUrl et repoName requis' }), { status: 400 }); } + // 🛡️ Security: Prevent flag injection by ensuring repoName doesn't start with a hyphen + if (repoName.startsWith('-')) { + return new Response(JSON.stringify({ error: 'Nom de dépôt invalide' }), { status: 400 }); + } + const githubToken = await getConfig('githubToken', true); if (!githubToken || githubToken.trim() === '') { return new Response(JSON.stringify({ error: 'Jeton GitHub manquant' }), { status: 400 }); @@ -33,7 +38,8 @@ export const POST: APIRoute = async ({ request }) => { const authUrl = repoUrl.replace('https://', `https://oauth2:${githubToken}@`); // Clone the repository - const { stdout, stderr } = await execPromise(`git clone ${authUrl} ${repoName}`, { cwd: reposRoot }); + // 🛡️ Security: Use execFile to prevent command injection + const { stdout, stderr } = await execFileAsync('git', ['clone', authUrl, repoName], { cwd: reposRoot }); // Try to auto-sync it into the database try {