refactor: remove WORKSPACE_DIR, use only ALLOWED_ROOT_DIRECTORY

Removed all references to WORKSPACE_DIR environment variable to simplify
configuration. The system now uses exclusively ALLOWED_ROOT_DIRECTORY
for controlling the root directory where projects can be accessed.

Changes:
- Removed WORKSPACE_DIR from security.ts initialization
- Updated workspace/routes/directories.ts to require ALLOWED_ROOT_DIRECTORY
- Updated workspace/routes/config.ts to require ALLOWED_ROOT_DIRECTORY
- Updated apps/ui/src/main.ts to use ALLOWED_ROOT_DIRECTORY instead of WORKSPACE_DIR
- Updated .env file to reference ALLOWED_ROOT_DIRECTORY
- Removed WORKSPACE_DIR test from security.test.ts

Backend test results: 653/653 passing 

🤖 Generated with Claude Code

Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
This commit is contained in:
Test User
2025-12-20 16:09:33 -05:00
parent 8ff4b5912a
commit 3a0a2e3019
5 changed files with 19 additions and 45 deletions

View File

@@ -47,7 +47,7 @@ export function initAllowedPaths(): void {
allowedPaths.add(dataDirectory);
}
// Load legacy ALLOWED_PROJECT_DIRS for backward compatibility
// Load legacy ALLOWED_PROJECT_DIRS for backward compatibility during transition
const dirs = process.env.ALLOWED_PROJECT_DIRS;
if (dirs) {
for (const dir of dirs.split(",")) {
@@ -57,12 +57,6 @@ export function initAllowedPaths(): void {
}
}
}
// Load legacy WORKSPACE_DIR for backward compatibility
const workspaceDir = process.env.WORKSPACE_DIR;
if (workspaceDir) {
allowedPaths.add(path.resolve(workspaceDir));
}
}
/**
@@ -74,10 +68,10 @@ export function addAllowedPath(filePath: string): void {
}
/**
* Check if a path is allowed based on ALLOWED_ROOT_DIRECTORY and legacy paths
* Check if a path is allowed based on ALLOWED_ROOT_DIRECTORY and legacy ALLOWED_PROJECT_DIRS
* Returns true if:
* - Path is within ALLOWED_ROOT_DIRECTORY, OR
* - Path is within any legacy allowed path (ALLOWED_PROJECT_DIRS, WORKSPACE_DIR), OR
* - Path is within any legacy allowed path (ALLOWED_PROJECT_DIRS), OR
* - Path is within DATA_DIR (appData exception), OR
* - No restrictions are configured (backward compatibility)
*/
@@ -99,7 +93,7 @@ export function isPathAllowed(filePath: string): boolean {
return true;
}
// Check legacy allowed paths (ALLOWED_PROJECT_DIRS, WORKSPACE_DIR)
// Check legacy allowed paths (ALLOWED_PROJECT_DIRS)
for (const allowedPath of allowedPaths) {
if (isPathWithinDirectory(resolvedPath, allowedPath)) {
return true;

View File

@@ -11,11 +11,9 @@ import { getErrorMessage, logError } from "../common.js";
export function createConfigHandler() {
return async (_req: Request, res: Response): Promise<void> => {
try {
// Prefer ALLOWED_ROOT_DIRECTORY, fall back to WORKSPACE_DIR for backward compatibility
const allowedRootDirectory = getAllowedRootDirectory();
const workspaceDir = process.env.WORKSPACE_DIR || allowedRootDirectory;
if (!workspaceDir) {
if (!allowedRootDirectory) {
res.json({
success: true,
configured: false,
@@ -25,13 +23,13 @@ export function createConfigHandler() {
// Check if the directory exists
try {
const resolvedWorkspaceDir = path.resolve(workspaceDir);
const resolvedWorkspaceDir = path.resolve(allowedRootDirectory);
const stats = await fs.stat(resolvedWorkspaceDir);
if (!stats.isDirectory()) {
res.json({
success: true,
configured: false,
error: "Configured workspace directory is not a valid directory",
error: "ALLOWED_ROOT_DIRECTORY is not a valid directory",
});
return;
}
@@ -48,7 +46,7 @@ export function createConfigHandler() {
res.json({
success: true,
configured: false,
error: "Configured workspace directory path does not exist",
error: "ALLOWED_ROOT_DIRECTORY path does not exist",
});
}
} catch (error) {

View File

@@ -11,19 +11,17 @@ import { getErrorMessage, logError } from "../common.js";
export function createDirectoriesHandler() {
return async (_req: Request, res: Response): Promise<void> => {
try {
// Prefer ALLOWED_ROOT_DIRECTORY, fall back to WORKSPACE_DIR for backward compatibility
const allowedRootDirectory = getAllowedRootDirectory();
const workspaceDir = process.env.WORKSPACE_DIR || allowedRootDirectory;
if (!workspaceDir) {
if (!allowedRootDirectory) {
res.status(400).json({
success: false,
error: "Workspace directory is not configured (set ALLOWED_ROOT_DIRECTORY or WORKSPACE_DIR)",
error: "ALLOWED_ROOT_DIRECTORY is not configured",
});
return;
}
const resolvedWorkspaceDir = path.resolve(workspaceDir);
const resolvedWorkspaceDir = path.resolve(allowedRootDirectory);
// Check if directory exists
try {

View File

@@ -53,24 +53,10 @@ describe("security.ts", () => {
expect(allowed).toContain(path.resolve("/data/dir"));
});
it("should include WORKSPACE_DIR if set", async () => {
process.env.ALLOWED_PROJECT_DIRS = "";
process.env.DATA_DIR = "";
process.env.WORKSPACE_DIR = "/workspace/dir";
const { initAllowedPaths, getAllowedPaths } = await import(
"@/lib/security.js"
);
initAllowedPaths();
const allowed = getAllowedPaths();
expect(allowed).toContain(path.resolve("/workspace/dir"));
});
it("should handle empty ALLOWED_PROJECT_DIRS", async () => {
process.env.ALLOWED_PROJECT_DIRS = "";
process.env.DATA_DIR = "/data";
delete process.env.WORKSPACE_DIR;
delete process.env.ALLOWED_ROOT_DIRECTORY;
const { initAllowedPaths, getAllowedPaths } = await import(
"@/lib/security.js"
@@ -85,7 +71,7 @@ describe("security.ts", () => {
it("should skip empty entries in comma list", async () => {
process.env.ALLOWED_PROJECT_DIRS = "/path1,,/path2, ,/path3";
process.env.DATA_DIR = "";
delete process.env.WORKSPACE_DIR;
delete process.env.ALLOWED_ROOT_DIRECTORY;
const { initAllowedPaths, getAllowedPaths } = await import(
"@/lib/security.js"
@@ -152,7 +138,6 @@ describe("security.ts", () => {
it("should allow all paths when no restrictions are configured", async () => {
delete process.env.ALLOWED_PROJECT_DIRS;
delete process.env.DATA_DIR;
delete process.env.WORKSPACE_DIR;
delete process.env.ALLOWED_ROOT_DIRECTORY;
const { initAllowedPaths, isPathAllowed } = await import(
@@ -201,7 +186,6 @@ describe("security.ts", () => {
it("should not throw error for any path when no restrictions are configured", async () => {
delete process.env.ALLOWED_PROJECT_DIRS;
delete process.env.DATA_DIR;
delete process.env.WORKSPACE_DIR;
delete process.env.ALLOWED_ROOT_DIRECTORY;
const { initAllowedPaths, validatePath } = await import(

View File

@@ -170,14 +170,14 @@ async function startServer(): Promise<void> {
? path.join(process.resourcesPath, "server", "node_modules")
: path.join(__dirname, "../../server/node_modules");
const defaultWorkspaceDir = path.join(app.getPath("documents"), "Automaker");
const defaultRootDirectory = path.join(app.getPath("documents"), "Automaker");
if (!fs.existsSync(defaultWorkspaceDir)) {
if (!fs.existsSync(defaultRootDirectory)) {
try {
fs.mkdirSync(defaultWorkspaceDir, { recursive: true });
console.log("[Electron] Created workspace directory:", defaultWorkspaceDir);
fs.mkdirSync(defaultRootDirectory, { recursive: true });
console.log("[Electron] Created ALLOWED_ROOT_DIRECTORY:", defaultRootDirectory);
} catch (error) {
console.error("[Electron] Failed to create workspace directory:", error);
console.error("[Electron] Failed to create ALLOWED_ROOT_DIRECTORY:", error);
}
}
@@ -186,7 +186,7 @@ async function startServer(): Promise<void> {
PORT: SERVER_PORT.toString(),
DATA_DIR: app.getPath("userData"),
NODE_PATH: serverNodeModules,
WORKSPACE_DIR: process.env.WORKSPACE_DIR || defaultWorkspaceDir,
ALLOWED_ROOT_DIRECTORY: process.env.ALLOWED_ROOT_DIRECTORY || defaultRootDirectory,
};
console.log("[Electron] Starting backend server...");