mirror of
https://github.com/catlog22/Claude-Code-Workflow.git
synced 2026-02-05 01:50:27 +08:00
fix(security): Apply 3 critical security fixes
- sec-001: Add validateAllowedPath to /api/file endpoint (path traversal) - sec-002: Enable CSRF by default with CCW_DISABLE_CSRF opt-out - sec-003: Add validateAllowedPath to /api/dialog/browse and /api/dialog/open-file (path traversal) Ref: fix-1738072800000
This commit is contained in:
@@ -113,9 +113,9 @@ export async function csrfValidation(ctx: CsrfMiddlewareContext): Promise<boolea
|
||||
const { pathname, req, res } = ctx;
|
||||
|
||||
if (!pathname.startsWith('/api/')) return true;
|
||||
// CSRF is disabled by default for local deployment scenarios.
|
||||
// Set CCW_ENABLE_CSRF=1 to enable CSRF protection.
|
||||
if (!envFlagEnabled('CCW_ENABLE_CSRF')) return true;
|
||||
// CSRF is enabled by default for security.
|
||||
// Set CCW_DISABLE_CSRF=1 to disable CSRF protection for local development.
|
||||
if (envFlagEnabled('CCW_DISABLE_CSRF')) return true;
|
||||
|
||||
const method = (req.method || 'GET').toUpperCase();
|
||||
if (!['POST', 'PUT', 'PATCH', 'DELETE'].includes(method)) return true;
|
||||
|
||||
@@ -6,6 +6,7 @@ import type { Server } from 'http';
|
||||
import { readFileSync, existsSync, promises as fsPromises } from 'fs';
|
||||
import { join } from 'path';
|
||||
import { resolvePath, getRecentPaths, trackRecentPath, removeRecentPath, normalizePathForDisplay } from '../../utils/path-resolver.js';
|
||||
import { validatePath as validateAllowedPath } from '../../utils/path-validator.js';
|
||||
import { scanSessions } from '../session-scanner.js';
|
||||
import { aggregateData } from '../data-aggregator.js';
|
||||
import {
|
||||
@@ -286,7 +287,12 @@ export async function handleSystemRoutes(ctx: SystemRouteContext): Promise<boole
|
||||
}
|
||||
|
||||
try {
|
||||
const content = await fsPromises.readFile(filePath, 'utf-8');
|
||||
// Validate path is within allowed directories (fix: sec-001-a1b2c3d4)
|
||||
const validatedPath = await validateAllowedPath(filePath, {
|
||||
mustExist: true,
|
||||
allowedDirectories: [process.cwd(), resolvePath('.ccw', 'sessions')]
|
||||
});
|
||||
const content = await fsPromises.readFile(validatedPath, 'utf-8');
|
||||
const json = JSON.parse(content);
|
||||
res.writeHead(200, { 'Content-Type': 'application/json' });
|
||||
res.end(JSON.stringify(json));
|
||||
@@ -442,6 +448,15 @@ export async function handleSystemRoutes(ctx: SystemRouteContext): Promise<boole
|
||||
targetPath = path.resolve(targetPath);
|
||||
}
|
||||
|
||||
// Validate path is within allowed directories (fix: sec-003-c3d4e5f6)
|
||||
const initialPath = process.cwd();
|
||||
if (browsePath) {
|
||||
targetPath = await validateAllowedPath(targetPath, {
|
||||
mustExist: true,
|
||||
allowedDirectories: [initialPath, os.homedir()]
|
||||
});
|
||||
}
|
||||
|
||||
try {
|
||||
const stat = await fs.promises.stat(targetPath);
|
||||
if (!stat.isDirectory()) {
|
||||
@@ -502,6 +517,13 @@ export async function handleSystemRoutes(ctx: SystemRouteContext): Promise<boole
|
||||
targetPath = path.resolve(targetPath);
|
||||
}
|
||||
|
||||
// Validate path is within allowed directories (fix: sec-003-c3d4e5f6)
|
||||
const initialPath = process.cwd();
|
||||
targetPath = await validateAllowedPath(targetPath, {
|
||||
mustExist: true,
|
||||
allowedDirectories: [initialPath, os.homedir()]
|
||||
});
|
||||
|
||||
try {
|
||||
await fs.promises.access(targetPath, fs.constants.R_OK);
|
||||
const stat = await fs.promises.stat(targetPath);
|
||||
|
||||
Reference in New Issue
Block a user