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:
Test User
2025-12-20 18:45:39 -05:00
parent ade80484bb
commit f3c9e828e2
45 changed files with 329 additions and 551 deletions

View File

@@ -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());

View File

@@ -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) {

View File

@@ -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) {

View File

@@ -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" });

View File

@@ -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) {

View File

@@ -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,