mirror of
https://github.com/AutoMaker-Org/automaker.git
synced 2026-03-20 23:13:07 +00:00
fix: Add symlink validation to prevent path traversal attacks
This commit is contained in:
@@ -20,20 +20,39 @@ import type { Request, Response } from 'express';
|
|||||||
import { execFile } from 'child_process';
|
import { execFile } from 'child_process';
|
||||||
import { promisify } from 'util';
|
import { promisify } from 'util';
|
||||||
import * as path from 'path';
|
import * as path from 'path';
|
||||||
|
import * as fs from 'fs';
|
||||||
import { getErrorMessage, logError } from '../common.js';
|
import { getErrorMessage, logError } from '../common.js';
|
||||||
|
|
||||||
const execFileAsync = promisify(execFile);
|
const execFileAsync = promisify(execFile);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Validate that a file path does not escape the worktree directory.
|
* Validate that a file path does not escape the worktree directory.
|
||||||
* Prevents path traversal attacks (e.g., ../../etc/passwd).
|
* Prevents path traversal attacks (e.g., ../../etc/passwd) and
|
||||||
|
* rejects symlinks inside the worktree that point outside of it.
|
||||||
*/
|
*/
|
||||||
function validateFilePath(filePath: string, worktreePath: string): boolean {
|
function validateFilePath(filePath: string, worktreePath: string): boolean {
|
||||||
// Resolve the full path relative to the worktree
|
// Resolve the full path relative to the worktree (lexical resolution)
|
||||||
const resolved = path.resolve(worktreePath, filePath);
|
const resolved = path.resolve(worktreePath, filePath);
|
||||||
const normalizedWorktree = path.resolve(worktreePath);
|
const normalizedWorktree = path.resolve(worktreePath);
|
||||||
// Ensure the resolved path starts with the worktree path
|
|
||||||
return resolved.startsWith(normalizedWorktree + path.sep) || resolved === normalizedWorktree;
|
// First, perform lexical prefix check
|
||||||
|
const lexicalOk =
|
||||||
|
resolved.startsWith(normalizedWorktree + path.sep) || resolved === normalizedWorktree;
|
||||||
|
if (!lexicalOk) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Then, attempt symlink-aware validation using realpath.
|
||||||
|
// This catches symlinks inside the worktree that point outside of it.
|
||||||
|
try {
|
||||||
|
const realResolved = fs.realpathSync(resolved);
|
||||||
|
const realWorktree = fs.realpathSync(normalizedWorktree);
|
||||||
|
return realResolved.startsWith(realWorktree + path.sep) || realResolved === realWorktree;
|
||||||
|
} catch {
|
||||||
|
// If realpath fails (e.g., target doesn't exist yet for untracked files),
|
||||||
|
// fall back to the lexical startsWith check which already passed above.
|
||||||
|
return true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export function createDiscardChangesHandler() {
|
export function createDiscardChangesHandler() {
|
||||||
|
|||||||
Reference in New Issue
Block a user