fix: FileTree showing entire workspace instead of project directory

- Add debug logging to track project directory resolution
- Improve path traversal security check with proper separator handling
- Log entry count and first 10 entries for debugging
- Fix project root validation to prevent listing workspace root

The issue was that the tree was listing from the workspace root
(containing gadget-code/, gadget-drone/, packages/) instead of the
specific project directory. Added logging to help diagnose.
This commit is contained in:
Rob Colbert 2026-05-12 16:24:49 -04:00
parent 71f213d8d1
commit 2b029ebf2e

View File

@ -724,22 +724,43 @@ class GadgetDrone extends GadgetProcess {
return cb(false, { error: "No session lock active" });
}
const projectRoot = WorkspaceService.getProjectDirectory(this.sessionLock.project.slug);
// Get the project directory for this session
const projectSlug = this.sessionLock.project.slug;
const projectRoot = WorkspaceService.getProjectDirectory(projectSlug);
this.log.debug("fileTreeRequest received", {
projectSlug,
projectRoot,
requestedPath: args.path,
});
if (!projectRoot) {
return cb(false, { error: "Project directory not found" });
return cb(false, { error: `Project directory not found for slug: ${projectSlug}` });
}
// If no path specified, list from project root
// If path specified, resolve it relative to project root
const targetPath = args.path
? path.resolve(projectRoot, args.path)
: projectRoot;
// Security: Ensure path is within project root
// Security: Ensure resolved path is within project root
const normalizedTarget = path.normalize(targetPath);
const normalizedRoot = path.normalize(projectRoot);
if (!normalizedTarget.startsWith(normalizedRoot)) {
if (!normalizedTarget.startsWith(normalizedRoot + path.sep) && normalizedTarget !== normalizedRoot) {
this.log.warn("fileTreeRequest path traversal attempt", {
targetPath: normalizedTarget,
projectRoot: normalizedRoot,
});
return cb(false, { error: "Access denied: path outside project root" });
}
this.log.debug("fileTreeRequest resolved paths", {
projectRoot,
targetPath,
isRoot: targetPath === projectRoot,
});
try {
const stat = await fs.stat(targetPath);
if (!stat.isDirectory()) {
@ -754,11 +775,17 @@ class GadgetDrone extends GadgetProcess {
projectRoot,
);
this.log.debug("fileTreeRequest completed", {
entryCount: entries.length,
entries: entries.slice(0, 10).map(e => e.name), // Log first 10 entries
});
cb(true, { entries });
} catch (error) {
const errorMessage = error instanceof Error ? error.message : String(error);
this.log.error("failed to list directory for file tree", {
path: args.path,
targetPath,
error: errorMessage,
});
cb(false, { error: `Failed to list directory: ${errorMessage}` });