Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions .jules/sentinel.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
## 2025-02-18 - Fix Command Injection Vulnerability in docker-logs.ts
**Vulnerability:** Un-sanitized `id` and `tail` parameters were directly interpolated into a shell string executed via `execSync` in `src/pages/api/docker-logs.ts`, leading to arbitrary command injection.
**Learning:** Node's `execSync` (and `exec`) executes a shell, allowing attackers to inject shell metacharacters (e.g., `;`, `&`, `|`, `` ` ``) when input is dynamically included.
**Prevention:** Always use `execFile` or `execFileAsync` with explicitly structured argument arrays instead of shell command strings when dealing with user-provided parameters. Furthermore, explicitly validate parameters against flag injection (e.g. `startsWith('-')`).
61 changes: 61 additions & 0 deletions patch_docker_logs.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
import { readFileSync, writeFileSync } from 'fs';
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

This file appears to be a temporary script used to apply changes to the codebase. Such artifacts should not be committed to the repository as they clutter the source tree and are not part of the application's runtime or build process.


const filepath = 'src/pages/api/docker-logs.ts';
let content = readFileSync(filepath, 'utf8');

content = content.replace("import { execSync } from 'child_process';",
`import { execFile } from 'node:child_process';
import { promisify } from 'node:util';

const execFileAsync = promisify(execFile);`);

content = content.replace("const command = `docker logs --tail ${tail} ${containerId}`;", "");

const searchBlock = ` let logs = [];
try {
const output = execSync(command, { stdio: ['pipe', 'pipe', 'pipe'] }).toString();
logs = output.trim().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;
}
}`;

const replaceBlock = ` // 🛡️ Sentinel: Validate container ID to prevent flag injection
if (containerId.startsWith('-')) {
return new Response(JSON.stringify({ error: "ID du conteneur invalide" }), {
status: 400,
headers: { 'Content-Type': 'application/json' }
});
}

// 🛡️ Sentinel: Validate and parse tail parameter to ensure it is a safe integer
const parsedTail = parseInt(tail, 10);
if (isNaN(parsedTail) || parsedTail < 0) {
return new Response(JSON.stringify({ error: "Paramètre tail invalide" }), {
status: 400,
headers: { 'Content-Type': 'application/json' }
});
}

let logs = [];
try {
// 🛡️ Sentinel: Use execFileAsync with an array of arguments to prevent command injection
const { stdout, stderr } = await execFileAsync('docker', ['logs', '--tail', parsedTail.toString(), containerId], { maxBuffer: 10 * 1024 * 1024 });
const output = stdout || stderr; // Docker logs often outputs to stderr
logs = output.trim().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 {
// 🛡️ Sentinel: Sanitize error message
throw new Error("Erreur lors de la récupération des logs Docker");
}
}`;

content = content.replace(searchBlock, replaceBlock);
writeFileSync(filepath, content);
9 changes: 9 additions & 0 deletions patch_test.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import { readFileSync, writeFileSync } from 'fs';
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

This file seems to be a scratchpad or utility script used during development to modify tests. It should be removed from the pull request to maintain a clean repository structure.


const filepath = 'tests/unit/forge-tool-install.test.ts';
let content = readFileSync(filepath, 'utf8');
content = content.replace("it('accepte un nom simple avec un manager explicite', async () => {", "it('accepte un nom simple avec un manager explicite', async () => {",);
// That replace didn't do anything helpful. I'll pass the timeout as the third argument to `it`.
content = content.replace("});\n});", "}, 20000);\n});");

writeFileSync(filepath, content);
Loading