Skip to content

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

Merged
austinpickett merged 5 commits into
NousResearch:mainfrom
yucode-kk:codex/fix-desktop-file-tree-path-hardening
Jun 11, 2026
Merged

fix(desktop): Harden local file tree paths#43618
austinpickett merged 5 commits into
NousResearch:mainfrom
yucode-kk:codex/fix-desktop-file-tree-path-hardening

Conversation

@yucode-kk

@yucode-kk yucode-kk commented Jun 10, 2026

Copy link
Copy Markdown

Harden Hermes Desktop's local file tree and local-file IPC paths without
adding workspace containment.

The file tree previously depended on Dirent.isDirectory() and direct path
resolution. 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 change extracts directory and git-root handling into testable Electron
helpers, adds stable directory ordering and bounded concurrent per-entry
symlink classification, and keeps unreadable folders visible with an error
placeholder. Local path resolution is 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. openExternal(file://) intentionally applies
syntax and device-path validation only because it delegates opening to the OS
instead of reading content into the renderer or model. Content-reading paths
continue to use the stricter readable-file checks. The remaining
validation-to-read TOCTOU window is left for a separate hardening change.

Updated after merging latest origin/main through 3edd09a46 into this
branch with merge commits. The latest apps/desktop/package.json conflict was
resolved by preserving main's new electron/update-remote.test.cjs platform
test while keeping this PR's electron/fs-read-dir.test.cjs and
electron/git-root.test.cjs entries. Current PR state is Ready for review and
mergeable at head bde879cc4.

Validation on Windows after the final merge:

  • git diff --check: passed.
  • git merge-tree --write-tree HEAD origin/main: passed.
  • node --test electron/hardening.test.cjs electron/fs-read-dir.test.cjs electron/git-root.test.cjs: 23 passed, 2 skipped.
  • npm run test:ui --workspace apps/desktop -- src/app/right-sidebar/files/use-project-tree.test.ts src/app/right-sidebar/files/ipc.test.ts: 13 passed.
  • npm run typecheck --workspace apps/desktop: passed.
  • npm run test:desktop:platforms --workspace apps/desktop: 142 passed, 2 skipped, 1 failed. The only failure remains the known unrelated electron/windows-child-process.test.cjs assertion looking for the formatted execFileSync('reg') call site.

Replaces #43593.

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 comp/gateway Gateway runner, session dispatch, delivery P3 Low — cosmetic, nice to have labels Jun 10, 2026
@liuhao1024

Copy link
Copy Markdown
Contributor

Code Review — Clean ✅

Reviewed the full diff (13 files, +880/-138). The security hardening is well-structured:

  1. rejectUnsafePathSyntax — blocks Windows device paths (\\?\, \\.\, globalroot/device/), null bytes, and non-string inputs. Applied at 3 points: raw input, base dir, and resolved path (defense-in-depth).

  2. resolveDirectoryForIpc — new directory resolver with realpath resolution + stat validation. Prevents symlink-to-directory escalation by resolving through realpathForIpc.

  3. resolveReadableFileForIpc — now checks sensitiveFileBlockReason on both the resolved path AND the real path (symlink attack vector). Previously only checked the resolved path.

  4. fs-read-dir.cjs — clean directory listing with hidden-dir filtering (.git, node_modules, etc.). Uses resolveDirectoryForIpc for path validation.

  5. Error typingipcPathError(code, message) replaces plain Error with structured codes (invalid-path, device-path, sensitive-file, ENOENT, etc.). Callers can now distinguish error types programmatically.

  6. Test coverage — 290 lines of tests covering: hidden dir filtering, symlink/junction resolution, device path rejection, null-byte injection, file URL handling, and broken symlinks.

One minor observation: resolveDirectoryForIpc doesn't restrict the real path to be within the project root — a symlink like /project/link -> /etc would pass. But the existing resolveReadableFileForIpc (which is the actual data exfiltration vector) does check sensitiveFileBlockReason on the real path, so this is an acceptable trade-off for directory listing (which only reveals names, not contents).

@yucode-kk
yucode-kk marked this pull request as ready for review June 10, 2026 15:44
@yucode-kk
yucode-kk requested review from a team and Copilot June 10, 2026 15:44

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 and IPC filesystem helpers by adding explicit UI placeholders for folder read errors and centralizing/hardening path resolution for Electron IPC (with expanded automated tests).

Changes:

  • Show an error placeholder child row in the project tree when readDir fails for a folder.
  • Normalize Windows-style paths in renderer-side caching / gitignore filtering and add renderer IPC tests.
  • Refactor Electron IPC handlers for readDir and gitRoot into dedicated modules backed by hardened path helpers and new platform 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 placeholder node metadata and inserts an error placeholder child on folder read errors.
apps/desktop/src/app/right-sidebar/files/use-project-tree.test.ts Updates project tree test to assert 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 field.
apps/desktop/src/app/right-sidebar/files/ipc.ts Normalizes incoming paths to POSIX-style for consistent caching/filtering (Windows support).
apps/desktop/src/app/right-sidebar/files/ipc.test.ts Adds coverage for Windows-style path handling and gitignore filtering in renderer IPC helper.
apps/desktop/package.json Adds new Electron platform tests for fs readDir and git root.
apps/desktop/electron/main.cjs Routes fs IPC handlers through new modules; uses hardening helpers for file URL/path resolution in preview/external URL flows.
apps/desktop/electron/hardening.test.cjs Expands tests for new hardening helpers and additional path/sensitivity cases.
apps/desktop/electron/hardening.cjs Introduces reusable IPC path validation/resolution helpers with structured error codes.
apps/desktop/electron/git-root.test.cjs Adds tests for gitRootForIpc.
apps/desktop/electron/git-root.cjs Extracts git root discovery into a dedicated module using hardened path resolution.
apps/desktop/electron/fs-read-dir.test.cjs Adds comprehensive tests for readDirForIpc behavior (hidden entries, sorting, symlinks, invalid input).
apps/desktop/electron/fs-read-dir.cjs Extracts readDir IPC implementation with hidden-entry filtering and symlink/unknown dirent handling.

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

Comment thread apps/desktop/electron/hardening.cjs
Comment thread apps/desktop/electron/fs-read-dir.cjs Outdated
Comment thread apps/desktop/src/app/right-sidebar/files/ipc.test.ts
@yucode-kk
yucode-kk marked this pull request as draft June 11, 2026 04:24
@yucode-kk
yucode-kk marked this pull request as ready for review June 11, 2026 04:38
@yucode-kk

Copy link
Copy Markdown
Author

This PR is ready for maintainer review. The branch is mergeable, review threads are resolved, and local validation results are listed in the PR body. Could you please approve the pending workflow run when convenient?

…e-tree-path-hardening

# Conflicts:
#	apps/desktop/package.json

@austinpickett austinpickett left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Reviewed the full diff with focus on the security boundary, traced all IPC call sites, and ran the test suites — approving. This is a careful, well-scoped hardening.

The vulnerabilities fixed

  1. Symlink sensitive-file bypass (the headline) — previously sensitiveFileBlockReason checked only the requested path, so a safe-named symlink (e.g. notes.txt~/.ssh/id_rsa) slipped through. The fix now checks sensitive-file rules against both the resolved requested path and its realpath: cheap reject on resolvedPath before stat, then a second rejectSensitiveFilePath(realPath) after confirming it's a regular file. Correct fix.
  2. Windows device-namespace pathsrejectUnsafePathSyntax blocks \\?\, \\.\, and globalroot/device/ forms, preventing raw-device access (\\.\PhysicalDrive0).
  3. file: URL hardening — now parses via new URL() + explicit protocol === 'file:' check before fileURLToPath, and re-runs syntax rejection on the resolved path.
  4. File-tree robustness — reparse-point classification, stable ordering, bounded concurrent symlink classification, and unreadable folders kept visible with an error placeholder instead of looking empty.

Verified

  • All IPC call sites route through resolveReadableFileForIpc / resolveDirectoryForIpc (Media stream, Image file, Preview target, Preview file, file reads) with no blockSensitive: false anywhere in main.cjs — so sensitive blocking (incl. the realpath check) is active on every entry point. blockSensitive !== false keeps it on by default.
  • Tests: 24/24 pass (1 skipped), including the decisive case resolveReadableFileForIpc blocks symlinks whose realpath is sensitive, plus device-path, NUL, blank, directory-symlink, and file-URL cases.

Design note — agreed

The PR deliberately does not add workspace containment, and that's the right call: desktop attachments/previews legitimately target files outside the active project, and file-tree symlinks/junctions remain expandable. The hardening targets the actual threat (sensitive-file exfiltration via symlink + malformed/device paths) without breaking the feature's purpose. openExternal(file://) applying syntax/device validation only (delegating to the OS handler) is consistent.

MERGEABLE; BLOCKED is branch-protection (needs a review), not red CI. Solid work.

@austinpickett
austinpickett merged commit b1af653 into NousResearch:main Jun 11, 2026
25 checks passed
alt-glitch pushed a commit that referenced this pull request Jun 14, 2026
* fix(desktop): Harden local file tree paths

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.

* fix(desktop): Address file tree review feedback
AIalliAI pushed a commit to AIalliAI/Hermes that referenced this pull request Jun 14, 2026
* fix(desktop): Harden local file tree paths

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.

* fix(desktop): Address file tree review feedback
T02200059 pushed a commit to T02200059/hermes-agent that referenced this pull request Jun 18, 2026
* fix(desktop): Harden local file tree paths

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.

* fix(desktop): Address file tree review feedback
waefrebeorn pushed a commit to waefrebeorn/slermes that referenced this pull request Jul 2, 2026
* fix(desktop): Harden local file tree paths

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.

* fix(desktop): Address file tree review feedback
santhreal pushed a commit to santhreal/hermes-agent that referenced this pull request Jul 13, 2026
* fix(desktop): Harden local file tree paths

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.

* fix(desktop): Address file tree review feedback
donbowman pushed a commit to donbowman/hermes-agent that referenced this pull request Jul 13, 2026
* fix(desktop): Harden local file tree paths

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.

* fix(desktop): Address file tree review feedback
Gravezzz pushed a commit to Gravezzz/hermes-agent that referenced this pull request Jul 21, 2026
* fix(desktop): Harden local file tree paths

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.

* fix(desktop): Address file tree review feedback
leewenjie pushed a commit to leewenjie/hermes-agent that referenced this pull request Aug 7, 2026
* fix(desktop): Harden local file tree paths

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.

* fix(desktop): Address file tree review feedback
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.

5 participants