feat: Enhance lite-skill-generator with single file output and improved validation

This commit is contained in:
catlog22
2026-01-28 22:23:19 +08:00
parent 502c8a09a1
commit a5d9e8ca87
2 changed files with 108 additions and 21 deletions

View File

@@ -286,19 +286,35 @@ export async function handleSystemRoutes(ctx: SystemRouteContext): Promise<boole
return true;
}
let validatedPath: string;
try {
// Validate path is within allowed directories (fix: sec-001-a1b2c3d4)
const validatedPath = await validateAllowedPath(filePath, {
mustExist: true,
allowedDirectories: [process.cwd(), resolvePath('.ccw', 'sessions')]
});
validatedPath = await validateAllowedPath(filePath, { mustExist: true, allowedDirectories: [initialPath] });
} catch (err) {
const message = err instanceof Error ? err.message : String(err);
const status = message.includes('Access denied') ? 403 : (message.includes('File not found') ? 404 : 400);
console.error(`[System] Path validation failed: ${message}`);
res.writeHead(status, { 'Content-Type': 'application/json' });
res.end(JSON.stringify({
error: status === 403 ? 'Access denied' : (status === 404 ? 'File not found' : 'Invalid path')
}));
return true;
}
try {
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));
} catch (err) {
res.writeHead(404, { 'Content-Type': 'application/json' });
res.end(JSON.stringify({ error: 'File not found or invalid JSON' }));
} catch (err: unknown) {
const errno = typeof err === 'object' && err !== null && 'code' in err ? String((err as any).code) : null;
const status = err instanceof SyntaxError ? 400 : (errno === 'EACCES' ? 403 : 404);
const message = err instanceof Error ? err.message : String(err);
console.error(`[System] Failed to read JSON file: ${message}`);
res.writeHead(status, { 'Content-Type': 'application/json' });
res.end(JSON.stringify({
error: status === 403 ? 'Access denied' : (status === 400 ? 'Invalid JSON' : 'File not found')
}));
}
return true;
}
@@ -449,12 +465,16 @@ export async function handleSystemRoutes(ctx: SystemRouteContext): Promise<boole
}
// Validate path is within allowed directories (fix: sec-003-c3d4e5f6)
const initialPath = process.cwd();
if (browsePath) {
try {
targetPath = await validateAllowedPath(targetPath, {
mustExist: true,
allowedDirectories: [initialPath, os.homedir()]
});
} catch (err) {
const message = err instanceof Error ? err.message : String(err);
const status = message.includes('Access denied') ? 403 : 400;
console.error(`[System] Path validation failed: ${message}`);
return { error: status === 403 ? 'Access denied' : 'Invalid path', status };
}
try {
@@ -486,7 +506,9 @@ export async function handleSystemRoutes(ctx: SystemRouteContext): Promise<boole
homePath: os.homedir()
};
} catch (err) {
return { error: 'Cannot access directory: ' + (err as Error).message, status: 400 };
const message = err instanceof Error ? err.message : String(err);
console.error(`[System] Failed to browse directory: ${message}`);
return { error: 'Cannot access directory', status: 400 };
}
});
return true;
@@ -518,11 +540,17 @@ export async function handleSystemRoutes(ctx: SystemRouteContext): Promise<boole
}
// 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 {
targetPath = await validateAllowedPath(targetPath, {
mustExist: true,
allowedDirectories: [initialPath, os.homedir()]
});
} catch (err) {
const message = err instanceof Error ? err.message : String(err);
const status = message.includes('Access denied') ? 403 : 400;
console.error(`[System] Path validation failed: ${message}`);
return { error: status === 403 ? 'Access denied' : 'Invalid path', status };
}
try {
await fs.promises.access(targetPath, fs.constants.R_OK);
@@ -534,8 +562,10 @@ export async function handleSystemRoutes(ctx: SystemRouteContext): Promise<boole
isFile: stat.isFile(),
isDirectory: stat.isDirectory()
};
} catch {
return { error: 'File not accessible', status: 404 };
} catch (err: unknown) {
const errno = typeof err === 'object' && err !== null && 'code' in err ? String((err as any).code) : null;
const status = errno === 'EACCES' ? 403 : 404;
return { error: status === 403 ? 'Access denied' : 'File not accessible', status };
}
});
return true;