🛡️ Sentinel: [CRITICAL] Fix command injection in docker logs API#65
🛡️ Sentinel: [CRITICAL] Fix command injection in docker logs API#65bobdivx wants to merge 1 commit into
Conversation
🚨 Severity: CRITICAL 💡 Vulnerability: Command injection in docker logs API 🎯 Impact: Allows arbitrary command execution 🔧 Fix: Refactor execSync to execFileAsync with argument arrays and flag validation ✅ Verification: Pass tail and containerId query params Co-authored-by: bobdivx <6737167+bobdivx@users.noreply.github.com>
|
👋 Jules, reporting for duty! I'm here to lend a hand with this pull request. When you start a review, I'll add a 👀 emoji to each comment to let you know I've read it. I'll focus on feedback directed at me and will do my best to stay out of conversations between you and other bots or reviewers to keep the noise down. I'll push a commit with your requested changes shortly after. Please note there might be a delay between these steps, but rest assured I'm on the job! For more direct control, you can switch me to Reactive Mode. When this mode is on, I will only act on comments where you specifically mention me with New to Jules? Learn more at jules.google/docs. For security, I will only act on instructions from the user who triggered this task. |
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
There was a problem hiding this comment.
Code Review
This pull request addresses a critical command injection vulnerability in the Docker logs API endpoint by replacing execSync with execFileAsync, sanitizing error messages, and validating inputs to prevent flag injection. The code review feedback points out two important improvements: first, resolving potential data loss and incorrect error handling where stderr logs could be discarded or incorrectly returned as a successful response; second, ensuring the tail parameter is validated as a non-negative integer to prevent unexpected command behavior.
| let logs: string[] = []; | ||
| try { | ||
| const output = execSync(command, { stdio: ['pipe', 'pipe', 'pipe'] }).toString(); | ||
| logs = output.trim().split('\n'); | ||
| const { stdout, stderr } = await execFileAsync('docker', ['logs', '--tail', tail.toString(), '--', containerId]); | ||
| const output = stdout.trim() || stderr.trim(); | ||
| logs = output ? output.split('\n') : []; | ||
| } catch (err: any) { | ||
| // Certains logs sortent sur stderr, checkons stderr si stdout est vide ou si erreur | ||
| if (err.stderr) { | ||
| logs = err.stderr.toString().trim().split('\n'); | ||
| } else { | ||
| throw err; | ||
| throw new Error("Execution failed"); | ||
| } | ||
| } |
There was a problem hiding this comment.
There are two issues with the current implementation of log retrieval and error handling:
- Data Loss on Success: If a container writes to both
stdoutandstderr,stdout.trim() || stderr.trim()will only returnstdout.trim(), completely discarding allstderrlogs. - Incorrect Error Handling: Since
execFileAsyncresolves when the process exits with0, any logs written tostderrduring a successful run are already present instderrof the resolved promise. If the process exits with a non-zero code (e.g., container not found),execFileAsyncthrows an error, anderr.stderrcontains the CLI error message (e.g.,"Error: No such container: ..."). Returningerr.stderras logs with a200 OKstatus is incorrect and leaks system errors. Instead, we should propagate the error so the outer catch block can return a clean500response.
let logs: string[] = [];
try {
const { stdout, stderr } = await execFileAsync('docker', ['logs', '--tail', tail.toString(), '--', containerId]);
const output = [stdout.trim(), stderr.trim()].filter(Boolean).join('
');
logs = output ? output.split('
') : [];
} catch (err: any) {
throw new Error("Execution failed");
}| const tail = parseInt(tailParam, 10); | ||
| if (isNaN(tail)) { | ||
| return new Response(JSON.stringify({ error: "Paramètre tail invalide" }), { | ||
| status: 400, | ||
| headers: { 'Content-Type': 'application/json' } | ||
| }); | ||
| } |
There was a problem hiding this comment.
Validate that the tail parameter is a non-negative integer. If a negative value is passed, it could lead to unexpected behavior or errors when passed to the docker logs command.
| const tail = parseInt(tailParam, 10); | |
| if (isNaN(tail)) { | |
| return new Response(JSON.stringify({ error: "Paramètre tail invalide" }), { | |
| status: 400, | |
| headers: { 'Content-Type': 'application/json' } | |
| }); | |
| } | |
| const tail = parseInt(tailParam, 10); | |
| if (isNaN(tail) || tail < 0) { | |
| return new Response(JSON.stringify({ error: "Paramètre tail invalide" }), { | |
| status: 400, | |
| headers: { 'Content-Type': 'application/json' } | |
| }); | |
| } |
🚨 Severity: CRITICAL
💡 Vulnerability: Command injection in docker logs API
🎯 Impact: Allows arbitrary command execution
🔧 Fix: Refactor execSync to execFileAsync with argument arrays and flag validation
✅ Verification: Pass tail and containerId query params
PR created automatically by Jules for task 5251450566315646849 started by @bobdivx