mirror of
https://github.com/AutoMaker-Org/automaker.git
synced 2026-02-01 08:13:37 +00:00
refactor: integrate secure file system operations across services
This commit replaces direct file system operations with a secure file system adapter to enhance security by enforcing path validation. The changes include:
- Replaced `fs` imports with `secureFs` in various services and utilities.
- Updated file operations in `agent-service`, `auto-mode-service`, `feature-loader`, and `settings-service` to use the secure file system methods.
- Ensured that all file I/O operations are validated against the ALLOWED_ROOT_DIRECTORY.
This refactor aims to prevent unauthorized file access and improve overall security posture.
Tests: All unit tests passing.
🤖 Generated with Claude Code
This commit is contained in:
@@ -5,6 +5,7 @@
|
||||
import { Router } from "express";
|
||||
import { AgentService } from "../../services/agent-service.js";
|
||||
import type { EventEmitter } from "../../lib/events.js";
|
||||
import { validatePathParams } from "../../middleware/validate-paths.js";
|
||||
import { createStartHandler } from "./routes/start.js";
|
||||
import { createSendHandler } from "./routes/send.js";
|
||||
import { createHistoryHandler } from "./routes/history.js";
|
||||
@@ -18,8 +19,8 @@ export function createAgentRoutes(
|
||||
): Router {
|
||||
const router = Router();
|
||||
|
||||
router.post("/start", createStartHandler(agentService));
|
||||
router.post("/send", createSendHandler(agentService));
|
||||
router.post("/start", validatePathParams("workingDirectory?"), createStartHandler(agentService));
|
||||
router.post("/send", validatePathParams("workingDirectory?", "imagePaths[]"), createSendHandler(agentService));
|
||||
router.post("/history", createHistoryHandler(agentService));
|
||||
router.post("/stop", createStopHandler(agentService));
|
||||
router.post("/clear", createClearHandler(agentService));
|
||||
|
||||
@@ -6,8 +6,6 @@ import type { Request, Response } from "express";
|
||||
import { AgentService } from "../../../services/agent-service.js";
|
||||
import { createLogger } from "../../../lib/logger.js";
|
||||
import { getErrorMessage, logError } from "../common.js";
|
||||
import { validatePath, PathNotAllowedError } from "../../../lib/security.js";
|
||||
|
||||
const logger = createLogger("Agent");
|
||||
|
||||
export function createSendHandler(agentService: AgentService) {
|
||||
@@ -30,27 +28,6 @@ export function createSendHandler(agentService: AgentService) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Validate paths are within ALLOWED_ROOT_DIRECTORY
|
||||
try {
|
||||
if (workingDirectory) {
|
||||
validatePath(workingDirectory);
|
||||
}
|
||||
if (imagePaths && imagePaths.length > 0) {
|
||||
for (const imagePath of imagePaths) {
|
||||
validatePath(imagePath);
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
if (error instanceof PathNotAllowedError) {
|
||||
res.status(403).json({
|
||||
success: false,
|
||||
error: error.message,
|
||||
});
|
||||
return;
|
||||
}
|
||||
throw error;
|
||||
}
|
||||
|
||||
// Start the message processing (don't await - it streams via WebSocket)
|
||||
agentService
|
||||
.sendMessage({
|
||||
|
||||
@@ -6,8 +6,6 @@ import type { Request, Response } from "express";
|
||||
import { AgentService } from "../../../services/agent-service.js";
|
||||
import { createLogger } from "../../../lib/logger.js";
|
||||
import { getErrorMessage, logError } from "../common.js";
|
||||
import { validatePath, PathNotAllowedError } from "../../../lib/security.js";
|
||||
|
||||
const logger = createLogger("Agent");
|
||||
|
||||
export function createStartHandler(agentService: AgentService) {
|
||||
@@ -25,22 +23,6 @@ export function createStartHandler(agentService: AgentService) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Validate paths are within ALLOWED_ROOT_DIRECTORY
|
||||
if (workingDirectory) {
|
||||
try {
|
||||
validatePath(workingDirectory);
|
||||
} catch (error) {
|
||||
if (error instanceof PathNotAllowedError) {
|
||||
res.status(403).json({
|
||||
success: false,
|
||||
error: error.message,
|
||||
});
|
||||
return;
|
||||
}
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
const result = await agentService.startConversation({
|
||||
sessionId,
|
||||
workingDirectory,
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
|
||||
import { Router } from "express";
|
||||
import type { AutoModeService } from "../../services/auto-mode-service.js";
|
||||
import { validatePathParams } from "../../middleware/validate-paths.js";
|
||||
import { createStopFeatureHandler } from "./routes/stop-feature.js";
|
||||
import { createStatusHandler } from "./routes/status.js";
|
||||
import { createRunFeatureHandler } from "./routes/run-feature.js";
|
||||
@@ -22,16 +23,16 @@ export function createAutoModeRoutes(autoModeService: AutoModeService): Router {
|
||||
|
||||
router.post("/stop-feature", createStopFeatureHandler(autoModeService));
|
||||
router.post("/status", createStatusHandler(autoModeService));
|
||||
router.post("/run-feature", createRunFeatureHandler(autoModeService));
|
||||
router.post("/run-feature", validatePathParams("projectPath"), createRunFeatureHandler(autoModeService));
|
||||
router.post("/verify-feature", createVerifyFeatureHandler(autoModeService));
|
||||
router.post("/resume-feature", createResumeFeatureHandler(autoModeService));
|
||||
router.post("/context-exists", createContextExistsHandler(autoModeService));
|
||||
router.post("/analyze-project", createAnalyzeProjectHandler(autoModeService));
|
||||
router.post("/analyze-project", validatePathParams("projectPath"), createAnalyzeProjectHandler(autoModeService));
|
||||
router.post(
|
||||
"/follow-up-feature",
|
||||
createFollowUpFeatureHandler(autoModeService)
|
||||
);
|
||||
router.post("/commit-feature", createCommitFeatureHandler(autoModeService));
|
||||
router.post("/commit-feature", validatePathParams("projectPath", "worktreePath?"), createCommitFeatureHandler(autoModeService));
|
||||
router.post("/approve-plan", createApprovePlanHandler(autoModeService));
|
||||
|
||||
return router;
|
||||
|
||||
@@ -6,7 +6,6 @@ import type { Request, Response } from "express";
|
||||
import type { AutoModeService } from "../../../services/auto-mode-service.js";
|
||||
import { createLogger } from "../../../lib/logger.js";
|
||||
import { getErrorMessage, logError } from "../common.js";
|
||||
import { validatePath, PathNotAllowedError } from "../../../lib/security.js";
|
||||
|
||||
const logger = createLogger("AutoMode");
|
||||
|
||||
@@ -22,20 +21,6 @@ export function createAnalyzeProjectHandler(autoModeService: AutoModeService) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Validate paths are within ALLOWED_ROOT_DIRECTORY
|
||||
try {
|
||||
validatePath(projectPath);
|
||||
} catch (error) {
|
||||
if (error instanceof PathNotAllowedError) {
|
||||
res.status(403).json({
|
||||
success: false,
|
||||
error: error.message,
|
||||
});
|
||||
return;
|
||||
}
|
||||
throw error;
|
||||
}
|
||||
|
||||
// Start analysis in background
|
||||
autoModeService.analyzeProject(projectPath).catch((error) => {
|
||||
logger.error(`[AutoMode] Project analysis error:`, error);
|
||||
|
||||
@@ -5,7 +5,6 @@
|
||||
import type { Request, Response } from "express";
|
||||
import type { AutoModeService } from "../../../services/auto-mode-service.js";
|
||||
import { getErrorMessage, logError } from "../common.js";
|
||||
import { validatePath, PathNotAllowedError } from "../../../lib/security.js";
|
||||
|
||||
export function createCommitFeatureHandler(autoModeService: AutoModeService) {
|
||||
return async (req: Request, res: Response): Promise<void> => {
|
||||
@@ -26,23 +25,6 @@ export function createCommitFeatureHandler(autoModeService: AutoModeService) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Validate paths are within ALLOWED_ROOT_DIRECTORY
|
||||
try {
|
||||
validatePath(projectPath);
|
||||
if (worktreePath) {
|
||||
validatePath(worktreePath);
|
||||
}
|
||||
} catch (error) {
|
||||
if (error instanceof PathNotAllowedError) {
|
||||
res.status(403).json({
|
||||
success: false,
|
||||
error: error.message,
|
||||
});
|
||||
return;
|
||||
}
|
||||
throw error;
|
||||
}
|
||||
|
||||
const commitHash = await autoModeService.commitFeature(
|
||||
projectPath,
|
||||
featureId,
|
||||
|
||||
@@ -6,7 +6,6 @@ import type { Request, Response } from "express";
|
||||
import type { AutoModeService } from "../../../services/auto-mode-service.js";
|
||||
import { createLogger } from "../../../lib/logger.js";
|
||||
import { getErrorMessage, logError } from "../common.js";
|
||||
import { validatePath, PathNotAllowedError } from "../../../lib/security.js";
|
||||
|
||||
const logger = createLogger("AutoMode");
|
||||
|
||||
@@ -27,20 +26,6 @@ export function createRunFeatureHandler(autoModeService: AutoModeService) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Validate path is within ALLOWED_ROOT_DIRECTORY
|
||||
try {
|
||||
validatePath(projectPath);
|
||||
} catch (error) {
|
||||
if (error instanceof PathNotAllowedError) {
|
||||
res.status(403).json({
|
||||
success: false,
|
||||
error: error.message,
|
||||
});
|
||||
return;
|
||||
}
|
||||
throw error;
|
||||
}
|
||||
|
||||
// Start execution in background
|
||||
// executeFeature derives workDir from feature.branchName
|
||||
autoModeService
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
|
||||
import { Router } from "express";
|
||||
import { FeatureLoader } from "../../services/feature-loader.js";
|
||||
import { validatePathParams } from "../../middleware/validate-paths.js";
|
||||
import { createListHandler } from "./routes/list.js";
|
||||
import { createGetHandler } from "./routes/get.js";
|
||||
import { createCreateHandler } from "./routes/create.js";
|
||||
@@ -15,11 +16,11 @@ import { createGenerateTitleHandler } from "./routes/generate-title.js";
|
||||
export function createFeaturesRoutes(featureLoader: FeatureLoader): Router {
|
||||
const router = Router();
|
||||
|
||||
router.post("/list", createListHandler(featureLoader));
|
||||
router.post("/get", createGetHandler(featureLoader));
|
||||
router.post("/create", createCreateHandler(featureLoader));
|
||||
router.post("/update", createUpdateHandler(featureLoader));
|
||||
router.post("/delete", createDeleteHandler(featureLoader));
|
||||
router.post("/list", validatePathParams("projectPath"), createListHandler(featureLoader));
|
||||
router.post("/get", validatePathParams("projectPath"), createGetHandler(featureLoader));
|
||||
router.post("/create", validatePathParams("projectPath"), createCreateHandler(featureLoader));
|
||||
router.post("/update", validatePathParams("projectPath"), createUpdateHandler(featureLoader));
|
||||
router.post("/delete", validatePathParams("projectPath"), createDeleteHandler(featureLoader));
|
||||
router.post("/agent-output", createAgentOutputHandler(featureLoader));
|
||||
router.post("/generate-title", createGenerateTitleHandler());
|
||||
|
||||
|
||||
@@ -7,7 +7,6 @@ import {
|
||||
FeatureLoader,
|
||||
type Feature,
|
||||
} from "../../../services/feature-loader.js";
|
||||
import { validatePath, PathNotAllowedError } from "../../../lib/security.js";
|
||||
import { getErrorMessage, logError } from "../common.js";
|
||||
|
||||
export function createCreateHandler(featureLoader: FeatureLoader) {
|
||||
@@ -28,20 +27,6 @@ export function createCreateHandler(featureLoader: FeatureLoader) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Validate path is within ALLOWED_ROOT_DIRECTORY
|
||||
try {
|
||||
validatePath(projectPath);
|
||||
} catch (error) {
|
||||
if (error instanceof PathNotAllowedError) {
|
||||
res.status(403).json({
|
||||
success: false,
|
||||
error: error.message,
|
||||
});
|
||||
return;
|
||||
}
|
||||
throw error;
|
||||
}
|
||||
|
||||
const created = await featureLoader.create(projectPath, feature);
|
||||
res.json({ success: true, feature: created });
|
||||
} catch (error) {
|
||||
|
||||
@@ -5,7 +5,6 @@
|
||||
import type { Request, Response } from "express";
|
||||
import { FeatureLoader } from "../../../services/feature-loader.js";
|
||||
import { getErrorMessage, logError } from "../common.js";
|
||||
import { validatePath, PathNotAllowedError } from "../../../lib/security.js";
|
||||
|
||||
export function createDeleteHandler(featureLoader: FeatureLoader) {
|
||||
return async (req: Request, res: Response): Promise<void> => {
|
||||
@@ -25,20 +24,6 @@ export function createDeleteHandler(featureLoader: FeatureLoader) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Validate path is within ALLOWED_ROOT_DIRECTORY
|
||||
try {
|
||||
validatePath(projectPath);
|
||||
} catch (error) {
|
||||
if (error instanceof PathNotAllowedError) {
|
||||
res.status(403).json({
|
||||
success: false,
|
||||
error: error.message,
|
||||
});
|
||||
return;
|
||||
}
|
||||
throw error;
|
||||
}
|
||||
|
||||
const success = await featureLoader.delete(projectPath, featureId);
|
||||
res.json({ success });
|
||||
} catch (error) {
|
||||
|
||||
@@ -5,7 +5,6 @@
|
||||
import type { Request, Response } from "express";
|
||||
import { FeatureLoader } from "../../../services/feature-loader.js";
|
||||
import { getErrorMessage, logError } from "../common.js";
|
||||
import { validatePath, PathNotAllowedError } from "../../../lib/security.js";
|
||||
|
||||
export function createGetHandler(featureLoader: FeatureLoader) {
|
||||
return async (req: Request, res: Response): Promise<void> => {
|
||||
@@ -25,20 +24,6 @@ export function createGetHandler(featureLoader: FeatureLoader) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Validate path is within ALLOWED_ROOT_DIRECTORY
|
||||
try {
|
||||
validatePath(projectPath);
|
||||
} catch (error) {
|
||||
if (error instanceof PathNotAllowedError) {
|
||||
res.status(403).json({
|
||||
success: false,
|
||||
error: error.message,
|
||||
});
|
||||
return;
|
||||
}
|
||||
throw error;
|
||||
}
|
||||
|
||||
const feature = await featureLoader.get(projectPath, featureId);
|
||||
if (!feature) {
|
||||
res.status(404).json({ success: false, error: "Feature not found" });
|
||||
|
||||
@@ -4,7 +4,6 @@
|
||||
|
||||
import type { Request, Response } from "express";
|
||||
import { FeatureLoader } from "../../../services/feature-loader.js";
|
||||
import { validatePath, PathNotAllowedError } from "../../../lib/security.js";
|
||||
import { getErrorMessage, logError } from "../common.js";
|
||||
|
||||
export function createListHandler(featureLoader: FeatureLoader) {
|
||||
@@ -19,20 +18,6 @@ export function createListHandler(featureLoader: FeatureLoader) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Validate path is within ALLOWED_ROOT_DIRECTORY
|
||||
try {
|
||||
validatePath(projectPath);
|
||||
} catch (error) {
|
||||
if (error instanceof PathNotAllowedError) {
|
||||
res.status(403).json({
|
||||
success: false,
|
||||
error: error.message,
|
||||
});
|
||||
return;
|
||||
}
|
||||
throw error;
|
||||
}
|
||||
|
||||
const features = await featureLoader.getAll(projectPath);
|
||||
res.json({ success: true, features });
|
||||
} catch (error) {
|
||||
|
||||
@@ -8,7 +8,6 @@ import {
|
||||
type Feature,
|
||||
} from "../../../services/feature-loader.js";
|
||||
import { getErrorMessage, logError } from "../common.js";
|
||||
import { validatePath, PathNotAllowedError } from "../../../lib/security.js";
|
||||
|
||||
export function createUpdateHandler(featureLoader: FeatureLoader) {
|
||||
return async (req: Request, res: Response): Promise<void> => {
|
||||
@@ -27,20 +26,6 @@ export function createUpdateHandler(featureLoader: FeatureLoader) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Validate path is within ALLOWED_ROOT_DIRECTORY
|
||||
try {
|
||||
validatePath(projectPath);
|
||||
} catch (error) {
|
||||
if (error instanceof PathNotAllowedError) {
|
||||
res.status(403).json({
|
||||
success: false,
|
||||
error: error.message,
|
||||
});
|
||||
return;
|
||||
}
|
||||
throw error;
|
||||
}
|
||||
|
||||
const updated = await featureLoader.update(
|
||||
projectPath,
|
||||
featureId,
|
||||
|
||||
@@ -3,14 +3,15 @@
|
||||
*/
|
||||
|
||||
import { Router } from "express";
|
||||
import { validatePathParams } from "../../middleware/validate-paths.js";
|
||||
import { createDiffsHandler } from "./routes/diffs.js";
|
||||
import { createFileDiffHandler } from "./routes/file-diff.js";
|
||||
|
||||
export function createGitRoutes(): Router {
|
||||
const router = Router();
|
||||
|
||||
router.post("/diffs", createDiffsHandler());
|
||||
router.post("/file-diff", createFileDiffHandler());
|
||||
router.post("/diffs", validatePathParams("projectPath"), createDiffsHandler());
|
||||
router.post("/file-diff", validatePathParams("projectPath", "filePath"), createFileDiffHandler());
|
||||
|
||||
return router;
|
||||
}
|
||||
|
||||
@@ -5,7 +5,6 @@
|
||||
import type { Request, Response } from "express";
|
||||
import { getErrorMessage, logError } from "../common.js";
|
||||
import { getGitRepositoryDiffs } from "../../common.js";
|
||||
import { validatePath, PathNotAllowedError } from "../../../lib/security.js";
|
||||
|
||||
export function createDiffsHandler() {
|
||||
return async (req: Request, res: Response): Promise<void> => {
|
||||
@@ -17,20 +16,6 @@ export function createDiffsHandler() {
|
||||
return;
|
||||
}
|
||||
|
||||
// Validate paths are within ALLOWED_ROOT_DIRECTORY
|
||||
try {
|
||||
validatePath(projectPath);
|
||||
} catch (error) {
|
||||
if (error instanceof PathNotAllowedError) {
|
||||
res.status(403).json({
|
||||
success: false,
|
||||
error: error.message,
|
||||
});
|
||||
return;
|
||||
}
|
||||
throw error;
|
||||
}
|
||||
|
||||
try {
|
||||
const result = await getGitRepositoryDiffs(projectPath);
|
||||
res.json({
|
||||
|
||||
@@ -7,7 +7,6 @@ import { exec } from "child_process";
|
||||
import { promisify } from "util";
|
||||
import { getErrorMessage, logError } from "../common.js";
|
||||
import { generateSyntheticDiffForNewFile } from "../../common.js";
|
||||
import { validatePath, PathNotAllowedError } from "../../../lib/security.js";
|
||||
|
||||
const execAsync = promisify(exec);
|
||||
|
||||
@@ -26,21 +25,6 @@ export function createFileDiffHandler() {
|
||||
return;
|
||||
}
|
||||
|
||||
// Validate paths are within ALLOWED_ROOT_DIRECTORY
|
||||
try {
|
||||
validatePath(projectPath);
|
||||
validatePath(filePath);
|
||||
} catch (error) {
|
||||
if (error instanceof PathNotAllowedError) {
|
||||
res.status(403).json({
|
||||
success: false,
|
||||
error: error.message,
|
||||
});
|
||||
return;
|
||||
}
|
||||
throw error;
|
||||
}
|
||||
|
||||
try {
|
||||
// First check if the file is untracked
|
||||
const { stdout: status } = await execAsync(
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
|
||||
import { Router } from "express";
|
||||
import type { SettingsService } from "../../services/settings-service.js";
|
||||
import { validatePathParams } from "../../middleware/validate-paths.js";
|
||||
import { createGetGlobalHandler } from "./routes/get-global.js";
|
||||
import { createUpdateGlobalHandler } from "./routes/update-global.js";
|
||||
import { createGetCredentialsHandler } from "./routes/get-credentials.js";
|
||||
@@ -57,8 +58,8 @@ export function createSettingsRoutes(settingsService: SettingsService): Router {
|
||||
router.put("/credentials", createUpdateCredentialsHandler(settingsService));
|
||||
|
||||
// Project settings
|
||||
router.post("/project", createGetProjectHandler(settingsService));
|
||||
router.put("/project", createUpdateProjectHandler(settingsService));
|
||||
router.post("/project", validatePathParams("projectPath"), createGetProjectHandler(settingsService));
|
||||
router.put("/project", validatePathParams("projectPath"), createUpdateProjectHandler(settingsService));
|
||||
|
||||
// Migration from localStorage
|
||||
router.post("/migrate", createMigrateHandler(settingsService));
|
||||
|
||||
@@ -11,7 +11,6 @@
|
||||
import type { Request, Response } from "express";
|
||||
import type { SettingsService } from "../../../services/settings-service.js";
|
||||
import { getErrorMessage, logError } from "../common.js";
|
||||
import { validatePath, PathNotAllowedError } from "../../../lib/security.js";
|
||||
|
||||
/**
|
||||
* Create handler factory for POST /api/settings/project
|
||||
@@ -32,20 +31,6 @@ export function createGetProjectHandler(settingsService: SettingsService) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Validate paths are within ALLOWED_ROOT_DIRECTORY
|
||||
try {
|
||||
validatePath(projectPath);
|
||||
} catch (error) {
|
||||
if (error instanceof PathNotAllowedError) {
|
||||
res.status(403).json({
|
||||
success: false,
|
||||
error: error.message,
|
||||
});
|
||||
return;
|
||||
}
|
||||
throw error;
|
||||
}
|
||||
|
||||
const settings = await settingsService.getProjectSettings(projectPath);
|
||||
|
||||
res.json({
|
||||
|
||||
@@ -12,7 +12,6 @@ import type { Request, Response } from "express";
|
||||
import type { SettingsService } from "../../../services/settings-service.js";
|
||||
import type { ProjectSettings } from "../../../types/settings.js";
|
||||
import { getErrorMessage, logError } from "../common.js";
|
||||
import { validatePath, PathNotAllowedError } from "../../../lib/security.js";
|
||||
|
||||
/**
|
||||
* Create handler factory for PUT /api/settings/project
|
||||
@@ -44,20 +43,6 @@ export function createUpdateProjectHandler(settingsService: SettingsService) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Validate paths are within ALLOWED_ROOT_DIRECTORY
|
||||
try {
|
||||
validatePath(projectPath);
|
||||
} catch (error) {
|
||||
if (error instanceof PathNotAllowedError) {
|
||||
res.status(403).json({
|
||||
success: false,
|
||||
error: error.message,
|
||||
});
|
||||
return;
|
||||
}
|
||||
throw error;
|
||||
}
|
||||
|
||||
const settings = await settingsService.updateProjectSettings(
|
||||
projectPath,
|
||||
updates
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
|
||||
import { Router } from "express";
|
||||
import type { EventEmitter } from "../../lib/events.js";
|
||||
import { validatePathParams } from "../../middleware/validate-paths.js";
|
||||
import { createGenerateHandler } from "./routes/generate.js";
|
||||
import { createStopHandler } from "./routes/stop.js";
|
||||
import { createStatusHandler } from "./routes/status.js";
|
||||
@@ -11,7 +12,7 @@ import { createStatusHandler } from "./routes/status.js";
|
||||
export function createSuggestionsRoutes(events: EventEmitter): Router {
|
||||
const router = Router();
|
||||
|
||||
router.post("/generate", createGenerateHandler(events));
|
||||
router.post("/generate", validatePathParams("projectPath"), createGenerateHandler(events));
|
||||
router.post("/stop", createStopHandler());
|
||||
router.get("/status", createStatusHandler());
|
||||
|
||||
|
||||
@@ -12,7 +12,6 @@ import {
|
||||
logError,
|
||||
} from "../common.js";
|
||||
import { generateSuggestions } from "../generate-suggestions.js";
|
||||
import { validatePath, PathNotAllowedError } from "../../../lib/security.js";
|
||||
|
||||
const logger = createLogger("Suggestions");
|
||||
|
||||
@@ -29,20 +28,6 @@ export function createGenerateHandler(events: EventEmitter) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Validate paths are within ALLOWED_ROOT_DIRECTORY
|
||||
try {
|
||||
validatePath(projectPath);
|
||||
} catch (error) {
|
||||
if (error instanceof PathNotAllowedError) {
|
||||
res.status(403).json({
|
||||
success: false,
|
||||
error: error.message,
|
||||
});
|
||||
return;
|
||||
}
|
||||
throw error;
|
||||
}
|
||||
|
||||
const { isRunning } = getSuggestionsStatus();
|
||||
if (isRunning) {
|
||||
res.json({
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
*/
|
||||
|
||||
import { Router } from "express";
|
||||
import { validatePathParams } from "../../middleware/validate-paths.js";
|
||||
import { createInfoHandler } from "./routes/info.js";
|
||||
import { createStatusHandler } from "./routes/status.js";
|
||||
import { createListHandler } from "./routes/list.js";
|
||||
@@ -32,27 +33,27 @@ import { createListDevServersHandler } from "./routes/list-dev-servers.js";
|
||||
export function createWorktreeRoutes(): Router {
|
||||
const router = Router();
|
||||
|
||||
router.post("/info", createInfoHandler());
|
||||
router.post("/status", createStatusHandler());
|
||||
router.post("/info", validatePathParams("projectPath"), createInfoHandler());
|
||||
router.post("/status", validatePathParams("projectPath"), createStatusHandler());
|
||||
router.post("/list", createListHandler());
|
||||
router.post("/diffs", createDiffsHandler());
|
||||
router.post("/file-diff", createFileDiffHandler());
|
||||
router.post("/merge", createMergeHandler());
|
||||
router.post("/create", createCreateHandler());
|
||||
router.post("/delete", createDeleteHandler());
|
||||
router.post("/diffs", validatePathParams("projectPath"), createDiffsHandler());
|
||||
router.post("/file-diff", validatePathParams("projectPath", "filePath"), createFileDiffHandler());
|
||||
router.post("/merge", validatePathParams("projectPath"), createMergeHandler());
|
||||
router.post("/create", validatePathParams("projectPath"), createCreateHandler());
|
||||
router.post("/delete", validatePathParams("projectPath", "worktreePath"), createDeleteHandler());
|
||||
router.post("/create-pr", createCreatePRHandler());
|
||||
router.post("/pr-info", createPRInfoHandler());
|
||||
router.post("/commit", createCommitHandler());
|
||||
router.post("/push", createPushHandler());
|
||||
router.post("/pull", createPullHandler());
|
||||
router.post("/commit", validatePathParams("worktreePath"), createCommitHandler());
|
||||
router.post("/push", validatePathParams("worktreePath"), createPushHandler());
|
||||
router.post("/pull", validatePathParams("worktreePath"), createPullHandler());
|
||||
router.post("/checkout-branch", createCheckoutBranchHandler());
|
||||
router.post("/list-branches", createListBranchesHandler());
|
||||
router.post("/list-branches", validatePathParams("worktreePath"), createListBranchesHandler());
|
||||
router.post("/switch-branch", createSwitchBranchHandler());
|
||||
router.post("/open-in-editor", createOpenInEditorHandler());
|
||||
router.post("/open-in-editor", validatePathParams("worktreePath"), createOpenInEditorHandler());
|
||||
router.get("/default-editor", createGetDefaultEditorHandler());
|
||||
router.post("/init-git", createInitGitHandler());
|
||||
router.post("/init-git", validatePathParams("projectPath"), createInitGitHandler());
|
||||
router.post("/migrate", createMigrateHandler());
|
||||
router.post("/start-dev", createStartDevHandler());
|
||||
router.post("/start-dev", validatePathParams("projectPath", "worktreePath"), createStartDevHandler());
|
||||
router.post("/stop-dev", createStopDevHandler());
|
||||
router.post("/list-dev-servers", createListDevServersHandler());
|
||||
|
||||
|
||||
@@ -6,7 +6,6 @@ import type { Request, Response } from "express";
|
||||
import { exec } from "child_process";
|
||||
import { promisify } from "util";
|
||||
import { getErrorMessage, logError } from "../common.js";
|
||||
import { validatePath, PathNotAllowedError } from "../../../lib/security.js";
|
||||
|
||||
const execAsync = promisify(exec);
|
||||
|
||||
@@ -26,20 +25,6 @@ export function createCommitHandler() {
|
||||
return;
|
||||
}
|
||||
|
||||
// Validate paths are within ALLOWED_ROOT_DIRECTORY
|
||||
try {
|
||||
validatePath(worktreePath);
|
||||
} catch (error) {
|
||||
if (error instanceof PathNotAllowedError) {
|
||||
res.status(403).json({
|
||||
success: false,
|
||||
error: error.message,
|
||||
});
|
||||
return;
|
||||
}
|
||||
throw error;
|
||||
}
|
||||
|
||||
// Check for uncommitted changes
|
||||
const { stdout: status } = await execAsync("git status --porcelain", {
|
||||
cwd: worktreePath,
|
||||
|
||||
@@ -20,7 +20,6 @@ import {
|
||||
ensureInitialCommit,
|
||||
} from "../common.js";
|
||||
import { trackBranch } from "./branch-tracking.js";
|
||||
import { validatePath, PathNotAllowedError } from "../../../lib/security.js";
|
||||
|
||||
const execAsync = promisify(exec);
|
||||
|
||||
@@ -92,20 +91,6 @@ export function createCreateHandler() {
|
||||
return;
|
||||
}
|
||||
|
||||
// Validate paths are within ALLOWED_ROOT_DIRECTORY
|
||||
try {
|
||||
validatePath(projectPath);
|
||||
} catch (error) {
|
||||
if (error instanceof PathNotAllowedError) {
|
||||
res.status(403).json({
|
||||
success: false,
|
||||
error: error.message,
|
||||
});
|
||||
return;
|
||||
}
|
||||
throw error;
|
||||
}
|
||||
|
||||
if (!(await isGitRepo(projectPath))) {
|
||||
res.status(400).json({
|
||||
success: false,
|
||||
|
||||
@@ -6,7 +6,6 @@ import type { Request, Response } from "express";
|
||||
import { exec } from "child_process";
|
||||
import { promisify } from "util";
|
||||
import { isGitRepo, getErrorMessage, logError } from "../common.js";
|
||||
import { validatePath, PathNotAllowedError } from "../../../lib/security.js";
|
||||
|
||||
const execAsync = promisify(exec);
|
||||
|
||||
@@ -27,21 +26,6 @@ export function createDeleteHandler() {
|
||||
return;
|
||||
}
|
||||
|
||||
// Validate paths are within ALLOWED_ROOT_DIRECTORY
|
||||
try {
|
||||
validatePath(projectPath);
|
||||
validatePath(worktreePath);
|
||||
} catch (error) {
|
||||
if (error instanceof PathNotAllowedError) {
|
||||
res.status(403).json({
|
||||
success: false,
|
||||
error: error.message,
|
||||
});
|
||||
return;
|
||||
}
|
||||
throw error;
|
||||
}
|
||||
|
||||
if (!(await isGitRepo(projectPath))) {
|
||||
res.status(400).json({
|
||||
success: false,
|
||||
|
||||
@@ -7,7 +7,6 @@ import path from "path";
|
||||
import fs from "fs/promises";
|
||||
import { getErrorMessage, logError } from "../common.js";
|
||||
import { getGitRepositoryDiffs } from "../../common.js";
|
||||
import { validatePath, PathNotAllowedError } from "../../../lib/security.js";
|
||||
|
||||
export function createDiffsHandler() {
|
||||
return async (req: Request, res: Response): Promise<void> => {
|
||||
@@ -27,20 +26,6 @@ export function createDiffsHandler() {
|
||||
return;
|
||||
}
|
||||
|
||||
// Validate paths are within ALLOWED_ROOT_DIRECTORY
|
||||
try {
|
||||
validatePath(projectPath);
|
||||
} catch (error) {
|
||||
if (error instanceof PathNotAllowedError) {
|
||||
res.status(403).json({
|
||||
success: false,
|
||||
error: error.message,
|
||||
});
|
||||
return;
|
||||
}
|
||||
throw error;
|
||||
}
|
||||
|
||||
// Git worktrees are stored in project directory
|
||||
const worktreePath = path.join(projectPath, ".worktrees", featureId);
|
||||
|
||||
|
||||
@@ -9,7 +9,6 @@ import path from "path";
|
||||
import fs from "fs/promises";
|
||||
import { getErrorMessage, logError } from "../common.js";
|
||||
import { generateSyntheticDiffForNewFile } from "../../common.js";
|
||||
import { validatePath, PathNotAllowedError } from "../../../lib/security.js";
|
||||
|
||||
const execAsync = promisify(exec);
|
||||
|
||||
@@ -30,21 +29,6 @@ export function createFileDiffHandler() {
|
||||
return;
|
||||
}
|
||||
|
||||
// Validate paths are within ALLOWED_ROOT_DIRECTORY
|
||||
try {
|
||||
validatePath(projectPath);
|
||||
validatePath(filePath);
|
||||
} catch (error) {
|
||||
if (error instanceof PathNotAllowedError) {
|
||||
res.status(403).json({
|
||||
success: false,
|
||||
error: error.message,
|
||||
});
|
||||
return;
|
||||
}
|
||||
throw error;
|
||||
}
|
||||
|
||||
// Git worktrees are stored in project directory
|
||||
const worktreePath = path.join(projectPath, ".worktrees", featureId);
|
||||
|
||||
|
||||
@@ -8,7 +8,6 @@ import { promisify } from "util";
|
||||
import path from "path";
|
||||
import fs from "fs/promises";
|
||||
import { getErrorMessage, logError, normalizePath } from "../common.js";
|
||||
import { validatePath, PathNotAllowedError } from "../../../lib/security.js";
|
||||
|
||||
const execAsync = promisify(exec);
|
||||
|
||||
@@ -30,20 +29,6 @@ export function createInfoHandler() {
|
||||
return;
|
||||
}
|
||||
|
||||
// Validate paths are within ALLOWED_ROOT_DIRECTORY
|
||||
try {
|
||||
validatePath(projectPath);
|
||||
} catch (error) {
|
||||
if (error instanceof PathNotAllowedError) {
|
||||
res.status(403).json({
|
||||
success: false,
|
||||
error: error.message,
|
||||
});
|
||||
return;
|
||||
}
|
||||
throw error;
|
||||
}
|
||||
|
||||
// Check if worktree exists (git worktrees are stored in project directory)
|
||||
const worktreePath = path.join(projectPath, ".worktrees", featureId);
|
||||
try {
|
||||
|
||||
@@ -8,7 +8,6 @@ import { promisify } from "util";
|
||||
import { existsSync } from "fs";
|
||||
import { join } from "path";
|
||||
import { getErrorMessage, logError } from "../common.js";
|
||||
import { validatePath, PathNotAllowedError } from "../../../lib/security.js";
|
||||
|
||||
const execAsync = promisify(exec);
|
||||
|
||||
@@ -27,20 +26,6 @@ export function createInitGitHandler() {
|
||||
return;
|
||||
}
|
||||
|
||||
// Validate paths are within ALLOWED_ROOT_DIRECTORY
|
||||
try {
|
||||
validatePath(projectPath);
|
||||
} catch (error) {
|
||||
if (error instanceof PathNotAllowedError) {
|
||||
res.status(403).json({
|
||||
success: false,
|
||||
error: error.message,
|
||||
});
|
||||
return;
|
||||
}
|
||||
throw error;
|
||||
}
|
||||
|
||||
// Check if .git already exists
|
||||
const gitDirPath = join(projectPath, ".git");
|
||||
if (existsSync(gitDirPath)) {
|
||||
|
||||
@@ -6,7 +6,6 @@ import type { Request, Response } from "express";
|
||||
import { exec } from "child_process";
|
||||
import { promisify } from "util";
|
||||
import { getErrorMessage, logWorktreeError } from "../common.js";
|
||||
import { validatePath, PathNotAllowedError } from "../../../lib/security.js";
|
||||
|
||||
const execAsync = promisify(exec);
|
||||
|
||||
@@ -31,20 +30,6 @@ export function createListBranchesHandler() {
|
||||
return;
|
||||
}
|
||||
|
||||
// Validate paths are within ALLOWED_ROOT_DIRECTORY
|
||||
try {
|
||||
validatePath(worktreePath);
|
||||
} catch (error) {
|
||||
if (error instanceof PathNotAllowedError) {
|
||||
res.status(403).json({
|
||||
success: false,
|
||||
error: error.message,
|
||||
});
|
||||
return;
|
||||
}
|
||||
throw error;
|
||||
}
|
||||
|
||||
// Get current branch
|
||||
const { stdout: currentBranchOutput } = await execAsync(
|
||||
"git rev-parse --abbrev-ref HEAD",
|
||||
|
||||
@@ -7,7 +7,6 @@ import { exec } from "child_process";
|
||||
import { promisify } from "util";
|
||||
import path from "path";
|
||||
import { getErrorMessage, logError } from "../common.js";
|
||||
import { validatePath, PathNotAllowedError } from "../../../lib/security.js";
|
||||
|
||||
const execAsync = promisify(exec);
|
||||
|
||||
@@ -30,20 +29,6 @@ export function createMergeHandler() {
|
||||
return;
|
||||
}
|
||||
|
||||
// Validate paths are within ALLOWED_ROOT_DIRECTORY
|
||||
try {
|
||||
validatePath(projectPath);
|
||||
} catch (error) {
|
||||
if (error instanceof PathNotAllowedError) {
|
||||
res.status(403).json({
|
||||
success: false,
|
||||
error: error.message,
|
||||
});
|
||||
return;
|
||||
}
|
||||
throw error;
|
||||
}
|
||||
|
||||
const branchName = `feature/${featureId}`;
|
||||
// Git worktrees are stored in project directory
|
||||
const worktreePath = path.join(projectPath, ".worktrees", featureId);
|
||||
|
||||
@@ -7,7 +7,6 @@ import type { Request, Response } from "express";
|
||||
import { exec } from "child_process";
|
||||
import { promisify } from "util";
|
||||
import { getErrorMessage, logError } from "../common.js";
|
||||
import { validatePath, PathNotAllowedError } from "../../../lib/security.js";
|
||||
|
||||
const execAsync = promisify(exec);
|
||||
|
||||
@@ -109,20 +108,6 @@ export function createOpenInEditorHandler() {
|
||||
return;
|
||||
}
|
||||
|
||||
// Validate paths are within ALLOWED_ROOT_DIRECTORY
|
||||
try {
|
||||
validatePath(worktreePath);
|
||||
} catch (error) {
|
||||
if (error instanceof PathNotAllowedError) {
|
||||
res.status(403).json({
|
||||
success: false,
|
||||
error: error.message,
|
||||
});
|
||||
return;
|
||||
}
|
||||
throw error;
|
||||
}
|
||||
|
||||
const editor = await detectDefaultEditor();
|
||||
|
||||
try {
|
||||
|
||||
@@ -6,7 +6,6 @@ import type { Request, Response } from "express";
|
||||
import { exec } from "child_process";
|
||||
import { promisify } from "util";
|
||||
import { getErrorMessage, logError } from "../common.js";
|
||||
import { validatePath, PathNotAllowedError } from "../../../lib/security.js";
|
||||
|
||||
const execAsync = promisify(exec);
|
||||
|
||||
@@ -25,20 +24,6 @@ export function createPullHandler() {
|
||||
return;
|
||||
}
|
||||
|
||||
// Validate paths are within ALLOWED_ROOT_DIRECTORY
|
||||
try {
|
||||
validatePath(worktreePath);
|
||||
} catch (error) {
|
||||
if (error instanceof PathNotAllowedError) {
|
||||
res.status(403).json({
|
||||
success: false,
|
||||
error: error.message,
|
||||
});
|
||||
return;
|
||||
}
|
||||
throw error;
|
||||
}
|
||||
|
||||
// Get current branch name
|
||||
const { stdout: branchOutput } = await execAsync(
|
||||
"git rev-parse --abbrev-ref HEAD",
|
||||
|
||||
@@ -6,7 +6,6 @@ import type { Request, Response } from "express";
|
||||
import { exec } from "child_process";
|
||||
import { promisify } from "util";
|
||||
import { getErrorMessage, logError } from "../common.js";
|
||||
import { validatePath, PathNotAllowedError } from "../../../lib/security.js";
|
||||
|
||||
const execAsync = promisify(exec);
|
||||
|
||||
@@ -26,20 +25,6 @@ export function createPushHandler() {
|
||||
return;
|
||||
}
|
||||
|
||||
// Validate paths are within ALLOWED_ROOT_DIRECTORY
|
||||
try {
|
||||
validatePath(worktreePath);
|
||||
} catch (error) {
|
||||
if (error instanceof PathNotAllowedError) {
|
||||
res.status(403).json({
|
||||
success: false,
|
||||
error: error.message,
|
||||
});
|
||||
return;
|
||||
}
|
||||
throw error;
|
||||
}
|
||||
|
||||
// Get branch name
|
||||
const { stdout: branchOutput } = await execAsync(
|
||||
"git rev-parse --abbrev-ref HEAD",
|
||||
|
||||
@@ -9,7 +9,6 @@
|
||||
import type { Request, Response } from "express";
|
||||
import { getDevServerService } from "../../../services/dev-server-service.js";
|
||||
import { getErrorMessage, logError } from "../common.js";
|
||||
import { validatePath, PathNotAllowedError } from "../../../lib/security.js";
|
||||
|
||||
export function createStartDevHandler() {
|
||||
return async (req: Request, res: Response): Promise<void> => {
|
||||
@@ -35,21 +34,6 @@ export function createStartDevHandler() {
|
||||
return;
|
||||
}
|
||||
|
||||
// Validate paths are within ALLOWED_ROOT_DIRECTORY
|
||||
try {
|
||||
validatePath(projectPath);
|
||||
validatePath(worktreePath);
|
||||
} catch (error) {
|
||||
if (error instanceof PathNotAllowedError) {
|
||||
res.status(403).json({
|
||||
success: false,
|
||||
error: error.message,
|
||||
});
|
||||
return;
|
||||
}
|
||||
throw error;
|
||||
}
|
||||
|
||||
const devServerService = getDevServerService();
|
||||
const result = await devServerService.startDevServer(projectPath, worktreePath);
|
||||
|
||||
|
||||
@@ -8,7 +8,6 @@ import { promisify } from "util";
|
||||
import path from "path";
|
||||
import fs from "fs/promises";
|
||||
import { getErrorMessage, logError } from "../common.js";
|
||||
import { validatePath, PathNotAllowedError } from "../../../lib/security.js";
|
||||
|
||||
const execAsync = promisify(exec);
|
||||
|
||||
@@ -30,20 +29,6 @@ export function createStatusHandler() {
|
||||
return;
|
||||
}
|
||||
|
||||
// Validate paths are within ALLOWED_ROOT_DIRECTORY
|
||||
try {
|
||||
validatePath(projectPath);
|
||||
} catch (error) {
|
||||
if (error instanceof PathNotAllowedError) {
|
||||
res.status(403).json({
|
||||
success: false,
|
||||
error: error.message,
|
||||
});
|
||||
return;
|
||||
}
|
||||
throw error;
|
||||
}
|
||||
|
||||
// Git worktrees are stored in project directory
|
||||
const worktreePath = path.join(projectPath, ".worktrees", featureId);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user