Skip to content

fix(desktop): Harden local file tree paths - #43593

Closed
yucode-kk wants to merge 1 commit into
NousResearch:mainfrom
yucode-kk:codex/fix-desktop-file-tree-path-hardening
Closed

fix(desktop): Harden local file tree paths#43593
yucode-kk wants to merge 1 commit into
NousResearch:mainfrom
yucode-kk:codex/fix-desktop-file-tree-path-hardening

Conversation

@yucode-kk

Copy link
Copy Markdown

Harden Hermes Desktop's local file tree and file-reading IPC paths without
restricting symlink or junction traversal outside the selected project.

The file tree previously depended on Dirent.isDirectory() and direct
path.resolve() / fileURLToPath() calls. Windows reparse points could be
classified as neither files nor directories, unreadable child directories
looked like empty folders, and sensitive-file checks could be bypassed through
a safe-named symlink.

This extracts directory and git-root handling into testable Electron helpers,
adds stable directory ordering and per-entry symlink classification, and keeps
unreadable folders visible with an error placeholder. Local path resolution is
now shared across file reads, previews, media, git-root discovery, and
file: URL handling. It rejects blank, NUL, malformed file URL, and Windows
device namespace paths, then checks sensitive-file rules against both the
requested path and its realpath.

Workspace containment is intentionally not included. Desktop attachments and
previews may target files outside the active project, and file-tree symlinks or
junctions remain expandable. The remaining validation-to-read TOCTOU window is
left for a separate deeper hardening change.

Validation covered the focused Electron tests (21 passed, 2 skipped because
ordinary symlink creation requires additional Windows privileges), the
renderer file-tree tests (13 passed), TypeScript compilation, and the desktop
production build. The complete desktop platform test command reaches 134
passed and 2 skipped tests, with the existing unrelated
electron/windows-child-process.test.cjs assertion still failing because it
cannot find the formatted execFileSync('reg') call site.

Normalize Electron local path handling across file tree, preview, media, and git-root flows. Reject malformed and Windows device paths, recheck sensitive files after realpath resolution, and preserve external symlink traversal with stable renderer errors.
@alt-glitch alt-glitch added type/security Security vulnerability or hardening P3 Low — cosmetic, nice to have comp/gateway Gateway runner, session dispatch, delivery labels Jun 10, 2026
@yucode-kk
yucode-kk marked this pull request as ready for review June 10, 2026 15:09
@yucode-kk
yucode-kk requested review from a team and Copilot June 10, 2026 15:09
@yucode-kk yucode-kk closed this Jun 10, 2026

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Note

Copilot was unable to run its full agentic suite in this review.

This PR improves the desktop project tree UX and hardens filesystem IPC by adding explicit error placeholders, normalizing Windows paths, and extracting readDir/gitRoot IPC logic into dedicated, tested modules.

Changes:

  • Show an inline “error placeholder” child row when a folder read fails, and update the tree row iconography accordingly.
  • Normalize Windows-style paths in the renderer IPC helper and add new renderer tests for Windows-path gitignore filtering.
  • Refactor Electron main-process readDir/gitRoot IPC handlers into fs-read-dir.cjs / git-root.cjs, and expand hardening.cjs path validation with comprehensive tests.

Reviewed changes

Copilot reviewed 13 out of 13 changed files in this pull request and generated 3 comments.

Show a summary per file
File Description
apps/desktop/src/app/right-sidebar/files/use-project-tree.ts Adds synthetic error/loading node typing and creates error placeholder children on read failures.
apps/desktop/src/app/right-sidebar/files/use-project-tree.test.ts Updates expectations to validate the new error placeholder child behavior.
apps/desktop/src/app/right-sidebar/files/tree.tsx Renders warning vs loading icons based on the new placeholder flag.
apps/desktop/src/app/right-sidebar/files/ipc.ts Normalizes backslashes to forward slashes when cleaning cache keys/paths.
apps/desktop/src/app/right-sidebar/files/ipc.test.ts Adds coverage for Windows-style paths when filtering gitignored entries.
apps/desktop/package.json Extends platform test script to include new Electron tests.
apps/desktop/electron/main.cjs Switches file path handling to hardening helpers and delegates IPC handlers to new modules.
apps/desktop/electron/hardening.test.cjs Adds tests for invalid/device paths, sensitive files, symlinks, and directory resolution.
apps/desktop/electron/hardening.cjs Introduces structured path validation/errors and new helpers for requested/readable/directory paths.
apps/desktop/electron/git-root.test.cjs Adds tests for the extracted git root resolver.
apps/desktop/electron/git-root.cjs New module implementing gitRootForIpc using hardened path resolution.
apps/desktop/electron/fs-read-dir.test.cjs Adds tests for hidden filtering, sorting, URL inputs, device-path rejection, and symlink handling.
apps/desktop/electron/fs-read-dir.cjs New module implementing hardened directory listing with filtering/statting of uncertain dirents.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines 729 to 734
if (parsed.protocol === 'file:') {
let localPath
try {
localPath = fileURLToPath(parsed.toString())
localPath = resolveRequestedPathForIpc(parsed.toString(), { purpose: 'Open external file' })
} catch {
return false
Comment on lines +75 to +81
for (const dirent of dirents) {
if (FS_READDIR_HIDDEN.has(dirent.name)) {
continue
}

entries.push(await entryForDirent(dirent, resolved, fsImpl))
}
Comment on lines +52 to +58
function errorChild(parentId: string, error: string | undefined): TreeNode {
return {
id: `${parentId}::${ERROR_PLACEHOLDER_ID}`,
isDirectory: false,
name: `Unable to read (${error || 'read-error'})`,
placeholder: 'error'
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

comp/gateway Gateway runner, session dispatch, delivery P3 Low — cosmetic, nice to have type/security Security vulnerability or hardening

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants