Skip to content

Harden main-process traversal and add stress benchmarks#57

Merged
Mehdi-Bl merged 4 commits into
mainfrom
feat/main-process-symlink-safety-bench
Feb 10, 2026
Merged

Harden main-process traversal and add stress benchmarks#57
Mehdi-Bl merged 4 commits into
mainfrom
feat/main-process-symlink-safety-bench

Conversation

@Mehdi-Bl

@Mehdi-Bl Mehdi-Bl commented Feb 10, 2026

Copy link
Copy Markdown
Contributor

Summary

  • harden traversal by resolving real paths safely, skipping symlinks, and guarding against recursion loops
  • add integration coverage for symlink escape and symlink loop scenarios
  • add stress benchmark suite for IPC latency and event-loop lag sampling ()
  • add

ai-code-fusion@0.2.0 test:stress
jest --config jest.config.js --runInBand --testMatch="/tests/stress//*.{js,jsx,ts,tsx}" --verbose command

  • update with stress suite coverage and fix binary detection target path reference

Why

This implements the first measurable slice of the facts-first backend plan: concrete safety hardening plus benchmark scaffolding so performance decisions are evidence-based.

Validation

ai-code-fusion@0.2.0 lint
cross-env ESLINT_USE_FLAT_CONFIG=false eslint src tests --ext .js,.jsx,.ts,.tsx --cache && npm run lint:md

ai-code-fusion@0.2.0 lint:md
node scripts/lint-markdown-links.js

Markdown docs lint passed: 13 markdown files checked, 8 links/images scanned, no decorative icons found.

ai-code-fusion@0.2.0 test
jest --config jest.config.js --passWithNoTests --runInBand

ai-code-fusion@0.2.0 test:stress
jest --config jest.config.js --runInBand --testMatch="/tests/stress//*.{js,jsx,ts,tsx}" --verbose

All passed locally.

Summary by Sourcery

Harden main-process directory traversal around symlinks and add stress benchmarks for main IPC handlers.

Bug Fixes:

  • Prevent main-process directory traversal from following symlinks outside the authorized root and from recursing into symlink cycles.

Enhancements:

  • Centralize real-path resolution and file stat reading helpers for safer path boundary checks in the main process.
  • Tighten directory-tree walking to enforce root-boundary checks on all traversed paths, including those resolved from symlinks.

Build:

  • Add an npm test:stress script to run the stress benchmark suite via Jest.

Tests:

  • Extend main-process integration tests to cover symlink escape and symlink loop scenarios for the directory tree handler.
  • Introduce a stress benchmark suite measuring fs:getDirectoryTree IPC latency and event-loop lag under large mocked directory trees.
  • Document the new stress tests in the test catalog and ensure they are included in the main-process test coverage map.

Summary by CodeRabbit

  • Bug Fixes

    • Improved symbolic link handling and path boundary verification for safer directory traversal
  • Tests

    • Added stress tests for IPC latency benchmarking, event-loop lag sampling, and related test catalog updates
    • Expanded unit/integration tests to cover symlink edge cases and traversal cycles
  • Chores

    • Added an npm script to run stress tests with verbose output

@chatgpt-codex-connector

Copy link
Copy Markdown

You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard.
To continue using code reviews, you can upgrade your account or add credits to your account and enable them for code reviews in your settings.

@sourcery-ai

sourcery-ai Bot commented Feb 10, 2026

Copy link
Copy Markdown

Reviewer's Guide

Hardens the main-process directory traversal logic against symlink escapes and recursion loops, and introduces a Jest-based stress benchmark suite to measure IPC latency and event-loop lag for the fs:getDirectoryTree handler, along with associated test/mocking updates and documentation alignment.

Sequence diagram for hardened fs_getDirectoryTree traversal

sequenceDiagram
  actor Renderer
  participant MainProcessHandler_fs_getDirectoryTree as MainProcessHandler_fs_getDirectoryTree
  participant FileSystem as FileSystem

  Renderer->>MainProcessHandler_fs_getDirectoryTree: IPC invoke fs_getDirectoryTree(authorizedDirPath)
  activate MainProcessHandler_fs_getDirectoryTree

  MainProcessHandler_fs_getDirectoryTree->>MainProcessHandler_fs_getDirectoryTree: init visitedDirectoryRealPaths
  MainProcessHandler_fs_getDirectoryTree->>MainProcessHandler_fs_getDirectoryTree: walkDirectory(dir)

  loop For each directory
    MainProcessHandler_fs_getDirectoryTree->>MainProcessHandler_fs_getDirectoryTree: resolveRealPath(dir)
    MainProcessHandler_fs_getDirectoryTree->>FileSystem: realpathSync or realpathSync_native
    FileSystem-->>MainProcessHandler_fs_getDirectoryTree: realDirectoryPath
    MainProcessHandler_fs_getDirectoryTree->>MainProcessHandler_fs_getDirectoryTree: check visitedDirectoryRealPaths
    alt Directory already visited
      MainProcessHandler_fs_getDirectoryTree->>MainProcessHandler_fs_getDirectoryTree: skip to avoid recursion loop
    else New directory
      MainProcessHandler_fs_getDirectoryTree->>FileSystem: readdirSync(dir)
      FileSystem-->>MainProcessHandler_fs_getDirectoryTree: items

      loop For each item
        MainProcessHandler_fs_getDirectoryTree->>MainProcessHandler_fs_getDirectoryTree: readPathStats(itemPath)
        MainProcessHandler_fs_getDirectoryTree->>FileSystem: lstatSync(itemPath)
        FileSystem-->>MainProcessHandler_fs_getDirectoryTree: lstatResult
        alt Item is symbolic link
          MainProcessHandler_fs_getDirectoryTree->>MainProcessHandler_fs_getDirectoryTree: resolveRealPath(itemPath)
          MainProcessHandler_fs_getDirectoryTree->>FileSystem: realpathSync
          FileSystem-->>MainProcessHandler_fs_getDirectoryTree: resolvedSymlinkPath
          MainProcessHandler_fs_getDirectoryTree->>MainProcessHandler_fs_getDirectoryTree: isPathWithinRoot(authorizedDirPath, resolvedSymlinkPath)
          alt Symlink escapes root
            MainProcessHandler_fs_getDirectoryTree->>MainProcessHandler_fs_getDirectoryTree: log skip symlink outside root
          else Symlink within root
            MainProcessHandler_fs_getDirectoryTree->>MainProcessHandler_fs_getDirectoryTree: still skip to avoid aliasing and loops
          end
        else Item is not symbolic link
          MainProcessHandler_fs_getDirectoryTree->>MainProcessHandler_fs_getDirectoryTree: isPathWithinRoot(authorizedDirPath, itemPath)
          alt Item outside root
            MainProcessHandler_fs_getDirectoryTree->>MainProcessHandler_fs_getDirectoryTree: log skip path outside root
          else Item within root
            alt stats.isDirectory
              MainProcessHandler_fs_getDirectoryTree->>MainProcessHandler_fs_getDirectoryTree: walkDirectory(subdirectory)
            else stats.isFile
              MainProcessHandler_fs_getDirectoryTree->>MainProcessHandler_fs_getDirectoryTree: apply filters and add to result
            end
          end
        end
      end
    end
  end

  MainProcessHandler_fs_getDirectoryTree-->>Renderer: DirectoryTreeItem[]
  deactivate MainProcessHandler_fs_getDirectoryTree
Loading

Class diagram for hardened traversal helpers and path boundary checks

classDiagram
  class MainProcessHandler_fs_getDirectoryTree {
    +authorizedRootPath string
    +walkDirectory(dir string) DirectoryTreeItem[]
    +shouldExclude(itemPath string, authorizedDirPath string, excludePatterns string[], config any) boolean
  }

  class DirectoryTreeItem {
    +path string
    +name string
    +isDirectory boolean
    +extension string
  }

  class PathSecurityUtils {
    +resolveRealPath(inputPath string) string
    +readPathStats(itemPath string) PathStatsResult
    +isPathWithinRoot(rootPath string, candidatePath string) boolean
  }

  class PathStatsResult {
    +stats Stats
    +isSymbolicLink boolean
  }

  class Stats {
    +isDirectory() boolean
    +isSymbolicLink() boolean
  }

  class VisitedDirectoryTracker {
    +visitedDirectoryRealPaths Set~string~
    +hasVisited(realDirectoryPath string) boolean
    +markVisited(realDirectoryPath string) void
  }

  MainProcessHandler_fs_getDirectoryTree --> DirectoryTreeItem : builds
  MainProcessHandler_fs_getDirectoryTree --> PathSecurityUtils : uses
  PathSecurityUtils --> PathStatsResult : returns
  PathStatsResult --> Stats : wraps
  MainProcessHandler_fs_getDirectoryTree --> VisitedDirectoryTracker : uses
Loading

File-Level Changes

Change Details Files
Harden fs:getDirectoryTree traversal to safely resolve real paths, skip unsafe symlinks, and prevent recursion loops.
  • Introduce resolveRealPath helper that safely resolves real filesystem paths using fs.realpathSync(.native) with error handling.
  • Add readPathStats helper that prefers lstatSync to detect symbolic links and falls back to statSync when unavailable.
  • Track visited directory real paths in walkDirectory to avoid infinite recursion and log when skipping revisits.
  • Skip entries that are symlinks, resolving and boundary-checking them, and log when skipping symlinks or paths outside the authorized root.
  • Refactor isPathWithinRoot to reuse resolveRealPath for consistent boundary checking.
src/main/index.ts
Expand main-process handler integration tests to cover symlink escape and symlink loop scenarios.
  • Refactor fs stat mocking into a reusable createMockStats helper supporting isSymbolicLink.
  • Mock fs.lstatSync and fs.realpathSync in integration tests to reflect new traversal behavior.
  • Add test ensuring symlinks resolving outside the selected root are skipped from directory tree results.
  • Add test ensuring symlink cycles do not cause traversal loops and that loop-inducing symlinks are excluded.
tests/integration/main-process/handlers.test.ts
Add IPC stress/benchmark suite for fs:getDirectoryTree, capturing latency distribution and event-loop lag under large mocked trees.
  • Create Jest stress test that repeatedly invokes fs:getDirectoryTree over a large flat tree, records per-run durations, computes p50/p95/p99 metrics, and writes JSON artifacts.
  • Create stress test that samples event-loop lag while repeatedly invoking fs:getDirectoryTree, computing percentile metrics and emitting artifacts.
  • Mock Electron, fs, yaml, token-counter, gitignore-parser, file-analyzer, and auto-updater to bootstrap the main process in a controlled environment.
  • Introduce helper utilities for percentile computation and benchmark artifact writing to dist/benchmarks.
tests/stress/main-process/ipc-latency.stress.test.ts
Wire up new stress tests into the tooling and update test catalog documentation.
  • Add npm script test:stress that runs Jest against tests/stress/** in-band and verbose mode.
  • Document the new stress test in tests/catalog.md under a dedicated Stress / Benchmark Tests section, and link it from the main-process changes checklist.
  • Fix the binary-detection test catalog entry to point to the correct file-analyzer target.
package.json
tests/catalog.md

Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

@qodo-free-for-open-source-projects

Copy link
Copy Markdown

Review Summary by Qodo

Harden directory traversal and add IPC stress benchmarks

✨ Enhancement 🧪 Tests

Grey Divider

Walkthroughs

Description
• Harden directory traversal with real path resolution and symlink detection
• Skip symlinks and prevent recursion loops via visited directory tracking
• Add stress benchmarks for IPC latency and event-loop lag sampling
• Refactor path validation into reusable helper functions
• Update test catalog and add npm script for stress test suite
Diagram
flowchart LR
  A["Directory Traversal"] --> B["resolveRealPath"]
  A --> C["readPathStats"]
  B --> D["Symlink Detection"]
  C --> D
  D --> E["Skip Symlinks"]
  D --> F["Prevent Loops"]
  E --> G["Safe Tree Walk"]
  F --> G
  H["Stress Tests"] --> I["IPC Latency"]
  H --> J["Event-Loop Lag"]
  I --> K["Benchmark Artifacts"]
  J --> K
Loading

Grey Divider

File Changes

1. src/main/index.ts ✨ Enhancement +62/-18

Add path resolution and symlink safety guards

• Added resolveRealPath() helper to safely resolve paths using native realpath with fallback
• Added readPathStats() helper to detect symbolic links via lstat with proper type checking
• Introduced visitedDirectoryRealPaths Set to track visited directories and prevent recursion
 loops
• Enhanced directory walk to skip symlinks and validate paths stay within authorized root
• Refactored isPathWithinRoot() to use new resolveRealPath() helper, removing duplicate logic

src/main/index.ts


2. tests/integration/main-process/handlers.test.ts 🧪 Tests +97/-2

Add symlink escape and loop detection tests

• Refactored mock stats creation into createMockStats() helper with symlink support
• Added mocks for fs.lstatSync and fs.realpathSync with native variant
• Added test for skipping symlinks that resolve outside authorized root directory
• Added test for preventing infinite loops caused by symlink cycles with timeout guard

tests/integration/main-process/handlers.test.ts


3. tests/stress/main-process/ipc-latency.stress.test.ts 🧪 Tests +246/-0

Add IPC latency and event-loop lag benchmarks

• New stress test file with 246 lines of benchmark infrastructure
• Implements percentile() utility for latency distribution analysis
• Implements writeBenchmarkArtifact() to persist results to dist/benchmarks/ directory
• Benchmark 1: Measures latency distribution for fs:getDirectoryTree on 5000-file flat tree
• Benchmark 2: Samples event-loop lag during 20 repeated directory walks with 2000 files
• Captures p50, p95, p99 percentiles and writes JSON artifacts for analysis

tests/stress/main-process/ipc-latency.stress.test.ts


View more (2)
4. package.json ⚙️ Configuration changes +1/-0

Add stress test npm script

• Added test:stress npm script to run stress tests with --runInBand and --verbose flags
• Script targets tests/stress/**/*.{js,jsx,ts,tsx} test files

package.json


5. tests/catalog.md 📝 Documentation +8/-1

Update test catalog with stress tests

• Fixed binary-detection test target reference from src/utils/binary-detection.ts to
 src/utils/file-analyzer.ts
• Added new "Stress / Benchmark Tests" section documenting ipc-latency.stress.test.ts
• Added stress test file to main-process coverage list in "Coverage by Component" section

tests/catalog.md


Grey Divider

Qodo Logo

@gemini-code-assist

Copy link
Copy Markdown

Summary of Changes

Hello @Mehdi-Bl, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

This pull request significantly enhances the stability and performance measurement capabilities of the application. It addresses potential vulnerabilities and inefficiencies in file system traversal by implementing safer path resolution and preventing infinite loops caused by symbolic links. Concurrently, it establishes a critical benchmarking framework to gather performance metrics for inter-process communication, enabling data-driven optimization decisions for future development.

Highlights

  • Hardened Main-Process Traversal: Implemented robust directory traversal logic by safely resolving real paths, skipping symbolic links that point outside the authorized root, and preventing recursion loops using a visited paths tracking mechanism.
  • Added Integration Coverage for Symlink Scenarios: Introduced new integration tests to specifically cover scenarios involving symlink escapes and symlink-induced recursion loops, ensuring the new traversal logic behaves as expected.
  • Introduced Stress Benchmark Suite: Added a new stress benchmark suite to measure IPC latency and event-loop lag when handling large directory tree requests, providing a foundation for performance-driven development.
  • Updated Test Catalog and Scripts: The package.json was updated with a new test:stress script, and the tests/catalog.md documentation was expanded to include the new stress tests and corrected a binary detection target path reference.

🧠 New Feature in Public Preview: You can now enable Memory to help Gemini Code Assist learn from your team's feedback. This makes future code reviews more consistent and personalized to your project's style. Click here to enable Memory in your admin console.

Changelog
  • package.json
    • Added a new test:stress script to run the newly introduced stress benchmarks.
  • src/main/index.ts
    • Introduced resolveRealPath to safely resolve file system paths, handling native realpath functions and potential errors.
    • Added readPathStats to retrieve file system statistics, specifically checking for symbolic links.
    • Modified the walkDirectory function to utilize resolveRealPath and readPathStats.
    • Implemented a mechanism to track visited directory real paths to prevent infinite recursion loops during traversal.
    • Added logic to skip symbolic links that resolve to paths outside the authorized root directory.
    • Ensured all traversed paths are strictly within the authorized root directory.
    • Refactored isPathWithinRoot to leverage the new resolveRealPath utility for consistent path resolution.
  • tests/catalog.md
    • Updated the entry for tests/unit/binary-detection.test.ts to correctly reference src/utils/file-analyzer.ts as its primary target.
    • Added a new section for 'Stress / Benchmark Tests' to document the ipc-latency.stress.test.ts file.
    • Included tests/stress/main-process/ipc-latency.stress.test.ts in the list of files related to main process changes.
  • tests/integration/main-process/handlers.test.ts
    • Refactored fs.statSync mock into a reusable createMockStats function, now also mocking isSymbolicLink.
    • Added mocks for fs.lstatSync and fs.realpathSync to simulate symbolic link behavior for testing.
    • Introduced a new test case to verify that symlinks resolving outside the selected root are skipped.
    • Added a new test case to confirm that directory traversal loops caused by symlink cycles are avoided.
  • tests/stress/main-process/ipc-latency.stress.test.ts
    • Added a new stress test file to benchmark main process IPC handlers.
    • Implemented mocks for Electron, Node.js fs module, and other dependencies to create a controlled test environment.
    • Included utility functions for calculating percentiles and writing benchmark artifacts to disk.
    • Defined a benchmark to collect latency distribution for fs:getDirectoryTree when processing large flat directory structures.
    • Defined a benchmark to collect event-loop lag samples during repeated directory walking operations.
Activity
  • The author, Mehdi-Bl, has implemented significant changes to harden main-process traversal and add stress benchmarks.
  • The pull request description indicates that all linting and testing, including the new stress tests, passed locally for the author.
  • Validation steps included running npm run lint, npm run lint:md, npm run test, and npm run test:stress.
Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here.

You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

@coderabbitai

coderabbitai Bot commented Feb 10, 2026

Copy link
Copy Markdown

Warning

Rate limit exceeded

@Mehdi-Bl has exceeded the limit for the number of commits that can be reviewed per hour. Please wait 15 minutes and 36 seconds before requesting another review.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

📝 Walkthrough

Walkthrough

Adds symlink detection, real-path resolution, and recursion-guarding to main-process directory traversal; introduces IPC stress-test scaffolding and a new npm script to run stress tests; updates unit/integration test catalog and adds tests covering symlink edge cases and traversal loops.

Changes

Cohort / File(s) Summary
Path Resolution & Symlink Handling
src/main/index.ts
Adds resolveRealPath() and readPathStats() utilities, replaces direct stat usage with symlink-aware checks, introduces visitedDirectoryRealPaths recursion guards, and enforces root-bound checks with warning logs when symlinks resolve outside the root.
Traversal Tests & Catalog
tests/integration/main-process/handlers.test.ts, tests/catalog.md
Adds createMockStats() and mocks for lstatSync/realpathSync. Adds tests ensuring symlinks outside the root are skipped and symlink cycles do not cause infinite traversal. Updates test catalog to list new stress tests.
Stress Tests & Script
package.json, tests/stress/main-process/ipc-latency.stress.test.ts
Adds test:stress npm script (Jest runInBand + stress test pattern) and introduces an IPC latency/event-loop-lag stress test that mocks Electron, bootstraps IPC handlers, collects latency distributions (p50/p95/p99), and emits JSON benchmark artifacts.

Sequence Diagram

sequenceDiagram
    participant App as Caller
    participant Traverse as DirectoryTraversal
    participant FS as Filesystem
    participant Resolve as PathResolver
    participant Guard as RecursionGuard

    App->>Traverse: request directory tree for path
    Traverse->>Guard: is path's realpath visited?
    alt not visited
        Traverse->>FS: readPathStats(path)
        FS-->>Traverse: stats + isSymbolicLink
        alt isSymbolicLink
            Traverse->>Resolve: resolveRealPath(path)
            Resolve->>FS: realpath lookup
            FS-->>Resolve: real path (or fallback)
            Resolve-->>Traverse: resolved real path
            Traverse->>Guard: is resolved path within root?
            alt within root
                Traverse->>Guard: mark resolved realpath visited
                Traverse->>FS: read children / recurse
            else outside root
                Traverse->>App: log warning, skip path
            end
        else regular file/dir
            Traverse->>Guard: mark realpath visited
            Traverse->>FS: read children / recurse
        end
    else visited
        Traverse->>App: log warning, skip to avoid loop
    end
    FS-->>Traverse: children entries
    Traverse-->>App: return assembled tree
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~50 minutes

Poem

🐰 I hopped through paths both real and sly,
Resolved each link that winked an eye.
Guards held tight where loops once played,
Benchmarks raced while metrics stayed—
A rabbit's stamp: traversal's spry! 🥕

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'Harden main-process traversal and add stress benchmarks' directly and accurately summarizes the main changes: security hardening of directory traversal logic (symlink safety, recursion guards, path boundary checks) and addition of stress benchmarks for IPC latency.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch feat/main-process-symlink-safety-bench

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@github-actions

github-actions Bot commented Feb 10, 2026

Copy link
Copy Markdown

Dependency Review

✅ No vulnerabilities or license issues or OpenSSF Scorecard issues found.

Scanned Files

None

@sourcery-ai sourcery-ai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Hey - I've found 1 issue, and left some high level feedback:

  • The new readPathStats helper assumes fs.lstatSync will always succeed when present; consider wrapping the lstatSync call in a try/catch and falling back to fs.statSync on errors (e.g., ENOENT or unusual mocks) to keep behavior resilient and consistent with resolveRealPath’s defensive style.
  • The test setups for fs.readdirSync/statSync/lstatSync/realpathSync in handlers.test.ts and the new stress suite duplicate a fair amount of mocking boilerplate; extracting a shared helper or factory for the mocked filesystem behavior would reduce drift and make future traversal changes easier to test consistently.
Prompt for AI Agents
Please address the comments from this code review:

## Overall Comments
- The new `readPathStats` helper assumes `fs.lstatSync` will always succeed when present; consider wrapping the `lstatSync` call in a try/catch and falling back to `fs.statSync` on errors (e.g., ENOENT or unusual mocks) to keep behavior resilient and consistent with `resolveRealPath`’s defensive style.
- The test setups for `fs.readdirSync`/`statSync`/`lstatSync`/`realpathSync` in `handlers.test.ts` and the new stress suite duplicate a fair amount of mocking boilerplate; extracting a shared helper or factory for the mocked filesystem behavior would reduce drift and make future traversal changes easier to test consistently.

## Individual Comments

### Comment 1
<location> `tests/integration/main-process/handlers.test.ts:414-423` </location>
<code_context>
+    test('should avoid directory traversal loops caused by symlink cycles', async () => {
</code_context>

<issue_to_address>
**issue (testing):** The traversal-loop detection test may be flaky due to a hard-coded 200ms timeout in Promise.race.

A 200ms `Promise.race` timeout makes this test timing-sensitive and likely flaky on slower or loaded CI runners, even when the implementation is correct. Either increase the timeout substantially (e.g. 2–5s) or, preferably, replace the time-based check with a structural one—such as mocking `fs.readdirSync` / `resolveRealPath` and enforcing a max call count, failing if that limit is exceeded. That way the test validates loop prevention without depending on wall-clock timing.
</issue_to_address>

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

Comment on lines +414 to +423
test('should avoid directory traversal loops caused by symlink cycles', async () => {
fs.readdirSync.mockImplementation((dir) => {
if (dir === '/mock/repo') {
return ['src', 'loop-link'];
}
if (dir === '/mock/repo/src') {
return ['index.js'];
}
return [];
});

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

issue (testing): The traversal-loop detection test may be flaky due to a hard-coded 200ms timeout in Promise.race.

A 200ms Promise.race timeout makes this test timing-sensitive and likely flaky on slower or loaded CI runners, even when the implementation is correct. Either increase the timeout substantially (e.g. 2–5s) or, preferably, replace the time-based check with a structural one—such as mocking fs.readdirSync / resolveRealPath and enforcing a max call count, failing if that limit is exceeded. That way the test validates loop prevention without depending on wall-clock timing.

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Code Review

This pull request significantly hardens the file system traversal logic in fs:getDirectoryTree by adding protection against symlink-based attacks and introduces resolveRealPath and readPathStats helpers. However, a medium-severity path traversal vulnerability was identified in the fallback logic of the readPathStats function. If lstatSync is unavailable, the code falls back to the insecure statSync function, which follows symlinks and could bypass the introduced protections. The PR also includes new integration tests and a stress benchmark suite, which are positive additions for security and performance observability.

Comment thread src/main/index.ts
Comment on lines +250 to +252
stats: fs.statSync(itemPath),
isSymbolicLink: false,
};

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

security-medium medium

The readPathStats function has a medium-severity path traversal vulnerability due to an insecure fallback to fs.statSync if fs.lstatSync is not available. fs.statSync follows symbolic links, which would bypass the symbolic link protection mechanism this PR introduces, potentially allowing an attacker to access files outside the authorized root path. It is safer to explicitly fail if lstatSync is unavailable rather than falling back to an insecure method.

  throw new Error(
    'fs.lstatSync is not available or returned an invalid result. Cannot safely determine file stats.'
  );

@qodo-free-for-open-source-projects

Copy link
Copy Markdown

Code Review by Qodo

🐞 Bugs (3) 📘 Rule violations (2) 📎 Requirement gaps (0)

Grey Divider


Action required

1. Stress tests run by default 🐞 Bug ⛯ Reliability
Description
The newly-added stress/benchmark test is included by the default Jest testMatch, so it will run in
normal npm test (including CI) and will write benchmark artifacts into dist/benchmarks during
those runs.
Code

tests/stress/main-process/ipc-latency.stress.test.ts[R88-95]

+const writeBenchmarkArtifact = (fileNamePrefix: string, payload: Record<string, unknown>) => {
+  const actualFs = jest.requireActual('fs');
+  const actualPath = jest.requireActual('path');
+  const outputDir = actualPath.join(process.cwd(), 'dist', 'benchmarks');
+  actualFs.mkdirSync(outputDir, { recursive: true });
+  const outputPath = actualPath.join(outputDir, `${fileNamePrefix}-${Date.now()}.json`);
+  actualFs.writeFileSync(outputPath, JSON.stringify(payload, null, 2), 'utf8');
+};
Evidence
Jest is configured to run *all* JS/TS files under tests/, and CI runs npm test, so the new
tests/stress/... test will execute in CI. That stress test writes real files to dist/benchmarks,
creating side effects during a standard test run.

jest.config.js[1-19]
package.json[23-27]
.github/workflows/qa-matrix.yml[37-42]
tests/stress/main-process/ipc-latency.stress.test.ts[88-95]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
A new stress/benchmark test under `tests/stress/` will run as part of the default `npm test` due to the broad Jest `testMatch`. It also writes benchmark artifacts to `dist/benchmarks`, adding side effects and extra time to CI and normal developer test runs.

## Issue Context
- CI runs `npm test`, which uses `jest.config.js`.
- `jest.config.js` currently matches all files under `tests/**`.
- The stress test writes JSON artifacts to `dist/benchmarks`.

## Fix Focus Areas
- jest.config.js[15-18]
- package.json[24-27]
- tests/stress/main-process/ipc-latency.stress.test.ts[88-95]

## Suggested direction
- Narrow default `testMatch` to unit/integration patterns (e.g. `tests/unit/**/*.test.*`, `tests/integration/**/*.test.*`).
- Keep `npm run test:stress` using `--testMatch` to run only `tests/stress/**`.
- Optional: add an env gate in `writeBenchmarkArtifact` (e.g. only write when `process.env.WRITE_BENCHMARKS === &#x27;1&#x27;`).

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools



Remediation recommended

2. resolveRealPath() ignores realpath errors 📘 Rule violation ⛯ Reliability
Description
resolveRealPath() catches realpathSync failures and silently falls back to path.resolve(),
which can mask filesystem errors and make troubleshooting path-boundary issues harder. This violates
the requirement to handle failure points with meaningful context rather than swallowing exceptions.
Code

src/main/index.ts[R222-230]

+  if (typeof realpathFn === 'function') {
+    try {
+      const realPathResult = realpathFn(resolvedPath);
+      return typeof realPathResult === 'string' && realPathResult.length > 0
+        ? realPathResult
+        : resolvedPath;
+    } catch {
+      return resolvedPath;
+    }
Evidence
The robust error-handling rule forbids swallowed exceptions without logging/context. The added
catch { return resolvedPath; } discards the error entirely, providing no actionable context if
realpathSync fails.

Rule 3: Generic: Robust Error Handling and Edge Case Management
src/main/index.ts[222-230]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
`resolveRealPath()` swallows `fs.realpathSync` errors via a bare `catch` and silently falls back to `path.resolve()`, which makes failures non-actionable and harder to debug.

## Issue Context
The code is part of main-process path safety and boundary checks; when `realpathSync` fails (permissions, broken symlink, transient FS error), we should retain meaningful context while still avoiding sensitive-data leakage.

## Fix Focus Areas
- src/main/index.ts[218-234]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


3. console.warn logs raw paths 📘 Rule violation ⛨ Security
Description
The traversal hardening adds warning logs that include full filesystem paths (dir/itemPath),
which may embed sensitive user information (e.g., usernames in home paths). This violates the secure
logging requirement to avoid sensitive data in logs and to keep logs structured/useful without
leaking details.
Code

src/main/index.ts[R356-365]

+      const realDirectoryPath = resolveRealPath(dir);
+      if (visitedDirectoryRealPaths.has(realDirectoryPath)) {
+        console.warn(`Skipping previously visited directory to avoid recursion loops: ${dir}`);
+        return [];
+      }
+      visitedDirectoryRealPaths.add(realDirectoryPath);
+
      const items = fs.readdirSync(dir);
      const result: DirectoryTreeItem[] = [];
Evidence
The secure logging rule requires avoiding sensitive data in logs. The added warnings interpolate raw
filesystem paths directly into log strings, which can expose sensitive directory
structure/identifiers when logs are collected.

Rule 5: Generic: Secure Logging Practices
src/main/index.ts[356-360]
src/main/index.ts[375-387]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
New `console.warn` messages log full filesystem paths, which can contain sensitive user-identifying information (e.g., `/Users/alice/...`). This risks leaking sensitive data into logs.

## Issue Context
These warnings may be shipped to log aggregators in production Electron environments. Keep logs useful while avoiding raw path leakage by redacting, hashing, or logging only relative paths within the authorized root.

## Fix Focus Areas
- src/main/index.ts[354-361]
- src/main/index.ts[375-388]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


4. Realpath overhead in walk 🐞 Bug ➹ Performance
Description
fs:getDirectoryTree now calls isPathWithinRoot for every directory entry; isPathWithinRoot
resolves real paths via synchronous fs.realpathSync, adding significant per-file synchronous
filesystem overhead in a hot path.
Code

src/main/index.ts[R375-388]

+          const { stats, isSymbolicLink } = readPathStats(itemPath);
+          if (isSymbolicLink) {
+            const resolvedSymlinkPath = resolveRealPath(itemPath);
+            if (!isPathWithinRoot(authorizedDirPath, resolvedSymlinkPath)) {
+              console.warn(`Skipping symlink outside current root directory: ${itemPath}`);
+            }
+            // Skip symlinks to prevent traversal aliasing and recursion loops.
+            continue;
+          }
+
+          if (!isPathWithinRoot(authorizedDirPath, itemPath)) {
+            console.warn(`Skipping path outside current root directory: ${itemPath}`);
+            continue;
+          }
Evidence
The directory walker now performs a boundary check per item, and the boundary check resolves the
*root* and *candidate* paths using resolveRealPath, which calls
fs.realpathSync/fs.realpathSync.native. This multiplies synchronous filesystem calls by the
number of entries walked, which is explicitly a performance-sensitive scenario (the PR adds a stress
benchmark with 5000 files).

src/main/index.ts[354-388]
src/main/index.ts[439-448]
src/main/index.ts[218-234]
tests/stress/main-process/ipc-latency.stress.test.ts[140-170]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
`fs:getDirectoryTree` now performs `isPathWithinRoot()` for every entry. `isPathWithinRoot()` calls `resolveRealPath()` for both root and candidate, which uses synchronous `fs.realpathSync`, increasing directory-walk latency and blocking the main process event loop on large repos.

## Issue Context
- Symlinks are already detected and skipped via `lstatSync`.
- For non-symlink entries produced by `readdirSync` + `path.join(dir,item)`, the per-item boundary check is typically redundant.

## Fix Focus Areas
- src/main/index.ts[218-234]
- src/main/index.ts[354-388]
- src/main/index.ts[439-448]

## Suggested direction
- Cache `const resolvedAuthorizedDirRealPath = resolveRealPath(authorizedDirPath);` once per request.
- Introduce a helper like `isPathWithinResolvedRoot(resolvedRootRealPath: string, candidatePath: string, {resolveCandidateSymlinks?: boolean})`.
- For non-symlink entries, consider using a pure-path check (`path.resolve` + `path.relative`) without `realpathSync`, since symlinks are skipped.
- In the symlink branch, avoid re-resolving the root/candidate multiple times (currently `resolveRealPath(itemPath)` + `isPathWithinRoot(...)` triggers another `resolveRealPath`).

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


View more (1)
5. Overbroad '..' check 🐞 Bug ✓ Correctness
Description
isPathWithinRoot treats any relative path starting with '..' as outside the root; this can
incorrectly reject legitimate in-root paths whose first segment begins with '..' (e.g. a directory
literally named ..cache).
Code

src/main/index.ts[R444-448]

+  const resolvedRootPath = resolveRealPath(rootPath);
+  const resolvedCandidatePath = resolveRealPath(candidatePath);
  const relativePath = path.relative(resolvedRootPath, resolvedCandidatePath);

  return relativePath === '' || (!relativePath.startsWith('..') && !path.isAbsolute(relativePath));
Evidence
The boundary logic uses a string prefix check (startsWith('..')) on path.relative(...). If the
relative path is a normal in-root name like ..cache (a valid directory name), it will still start
with '..' and be rejected even though it doesn’t traverse upward. This matters more now that
directory walking applies isPathWithinRoot to each entry.

src/main/index.ts[448-448]
src/main/index.ts[385-388]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
`isPathWithinRoot()` uses `relativePath.startsWith(&#x27;..&#x27;)`, which can incorrectly flag legitimate in-root paths whose directory name starts with `&#x27;..&#x27;` (e.g. `..cache`) as &quot;outside root&quot;.

## Issue Context
The correct intent is to reject paths that traverse upward (i.e., the first path segment is exactly `..`), not paths whose names merely begin with two dots.

## Fix Focus Areas
- src/main/index.ts[439-449]

## Suggested direction
Replace:
- `relativePath.startsWith(&#x27;..&#x27;)`

With a stricter condition:
- `relativePath === &#x27;..&#x27; || relativePath.startsWith(`..${path.sep}`)`

Keep the `path.isAbsolute(relativePath)` check as-is.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


Grey Divider

ⓘ The new review experience is currently in Beta. Learn more

Grey Divider

Qodo Logo

Comment on lines +88 to +95
const writeBenchmarkArtifact = (fileNamePrefix: string, payload: Record<string, unknown>) => {
const actualFs = jest.requireActual('fs');
const actualPath = jest.requireActual('path');
const outputDir = actualPath.join(process.cwd(), 'dist', 'benchmarks');
actualFs.mkdirSync(outputDir, { recursive: true });
const outputPath = actualPath.join(outputDir, `${fileNamePrefix}-${Date.now()}.json`);
actualFs.writeFileSync(outputPath, JSON.stringify(payload, null, 2), 'utf8');
};

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Action required

1. Stress tests run by default 🐞 Bug ⛯ Reliability

The newly-added stress/benchmark test is included by the default Jest testMatch, so it will run in
normal npm test (including CI) and will write benchmark artifacts into dist/benchmarks during
those runs.
Agent Prompt
## Issue description
A new stress/benchmark test under `tests/stress/` will run as part of the default `npm test` due to the broad Jest `testMatch`. It also writes benchmark artifacts to `dist/benchmarks`, adding side effects and extra time to CI and normal developer test runs.

## Issue Context
- CI runs `npm test`, which uses `jest.config.js`.
- `jest.config.js` currently matches all files under `tests/**`.
- The stress test writes JSON artifacts to `dist/benchmarks`.

## Fix Focus Areas
- jest.config.js[15-18]
- package.json[24-27]
- tests/stress/main-process/ipc-latency.stress.test.ts[88-95]

## Suggested direction
- Narrow default `testMatch` to unit/integration patterns (e.g. `tests/unit/**/*.test.*`, `tests/integration/**/*.test.*`).
- Keep `npm run test:stress` using `--testMatch` to run only `tests/stress/**`.
- Optional: add an env gate in `writeBenchmarkArtifact` (e.g. only write when `process.env.WRITE_BENCHMARKS === '1'`).

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools

@sonarqubecloud

Copy link
Copy Markdown

Quality Gate Failed Quality Gate failed

Failed conditions
23.9% Duplication on New Code (required ≤ 3%)

See analysis details on SonarQube Cloud

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant