Skip to content

fix(security): prevent path traversal in NestedGlob search root#407

Merged
yacosta738 merged 5 commits into
mainfrom
fix/security-nested-glob-traversal-10326664153959606349
May 13, 2026
Merged

fix(security): prevent path traversal in NestedGlob search root#407
yacosta738 merged 5 commits into
mainfrom
fix/security-nested-glob-traversal-10326664153959606349

Conversation

@yacosta738
Copy link
Copy Markdown
Contributor

Identified and fixed a path traversal vulnerability in AgentSync's NestedGlob synchronization logic.

Severity: HIGH
Vulnerability: Path traversal in symlink discovery (search root).
Impact: A malicious agentsync.toml could specify a search root outside the project (e.g., source = "../../"), causing AgentSync to recursively walk sensitive host directories.
Fix: Added validation for the source field in NestedGlob targets using revalidate_destination_path to ensure it resolves within the project root.
Source: src/linker.rs in process_target and clean.
Verification: Added tests/test_security.rs with a reproduction case that verifies the traversal is now rejected with an error. All existing tests pass.


PR created automatically by Jules for task 10326664153959606349 started by @yacosta738

Hardens the NestedGlob sync type in `src/linker.rs` by validating the
search root (`source` field) against the project root. This prevents a
malicious configuration from triggering a recursive directory walk
outside the intended project boundaries.

Validation is implemented using `revalidate_destination_path` which
correctly handles current directory ('.') search roots while rejecting
traversal components or absolute paths that escape the project root.

Security regression tests are added in `tests/test_security.rs` to
verify the fix.

Under 50 lines changed.
@google-labs-jules
Copy link
Copy Markdown
Contributor

👋 Jules, reporting for duty! I'm here to lend a hand with this pull request.

When you start a review, I'll add a 👀 emoji to each comment to let you know I've read it. I'll focus on feedback directed at me and will do my best to stay out of conversations between you and other bots or reviewers to keep the noise down.

I'll push a commit with your requested changes shortly after. Please note there might be a delay between these steps, but rest assured I'm on the job!

For more direct control, you can switch me to Reactive Mode. When this mode is on, I will only act on comments where you specifically mention me with @jules. You can find this option in the Pull Request section of your global Jules UI settings. You can always switch back!

New to Jules? Learn more at jules.google/docs.


For security, I will only act on instructions from the user who triggered this task.

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented May 6, 2026

Review Change Stack
No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: e33b5063-5113-4db0-9b90-e4c2effbcfc1

📥 Commits

Reviewing files that changed from the base of the PR and between 2fe4580 and b20a90f.

📒 Files selected for processing (1)
  • tests/test_security.rs

📝 Walkthrough

Summary by CodeRabbit

  • Bug Fixes

    • Strengthened security checks to revalidate search roots for nested-glob synchronizations; unsafe targets now error out and their cleanup is skipped to avoid traversing outside the project.
  • Tests

    • Added security tests that assert errors for unsafe nested-glob paths and verify no files are created or removed outside the project boundary.

Walkthrough

This change revalidates NestedGlob computed search_root paths with the destination safety check during target processing and cleanup, and adds a test that verifies relative and absolute outside-root search_roots are rejected and do not create or remove files outside the project root.

Changes

Path Traversal Prevention for NestedGlob Targets

Layer / File(s) Summary
Process target revalidation
src/linker.rs
Call revalidate_destination_path on search_root after project_root.join(target.source) in process_target; return a contextual error when validation fails.
Cleanup revalidation
src/linker.rs
Call revalidate_destination_path on search_root in clean for SyncType::NestedGlob and skip cleanup for that target when validation fails.
Test imports
tests/test_security.rs
Add necessary imports and temporary directory/test scaffolding for the new security test.
Security tests
tests/test_security.rs
Add test_nested_glob_search_root_traversal exercising a relative ../outside_dir and an absolute outside path; assert sync reports errors and that no files outside the project root are created or removed.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Poem

🐰 I hopped along the nested-glob way,
Found outside paths that wanted to stray,
A gentle check said "stop" and held the gate,
No leaks, no cleans that wanderate.
Nose twitch, content hop

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly and specifically describes the main security fix: preventing path traversal in NestedGlob search root, which is the primary change in the changeset.
Description check ✅ Passed The description comprehensively documents the vulnerability, its impact, the fix applied, and verification approach, all directly related to the changeset.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

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

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/security-nested-glob-traversal-10326664153959606349

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.

@sentry
Copy link
Copy Markdown

sentry Bot commented May 6, 2026

Codecov Report

✅ All modified and coverable lines are covered by tests.

📢 Thoughts on this report? Let us know!

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
src/linker.rs (1)

384-401: 🧹 Nitpick | 🔵 Trivial | 💤 Low value

NestedGlob search_root validation looks correct.

revalidate_destination_path resolves symlinks and .. via canonicalization on the first existing ancestor and asserts the result is under canonical_project_root, which correctly rejects:

  • relative traversal (e.g. source = "../outside"),
  • absolute escapes (e.g. source = "/etc", since Path::join of an absolute replaces the base), and
  • symlinked roots that resolve outside the project.

It still allows source = "." because canonicalizing project_root/. yields the project root itself. The error path here propagates via ? into Linker::sync, which increments result.errors — exactly what the new regression test in tests/test_security.rs asserts.

One minor wart: the error surfaced will read Destination path resolves outside project root: … even though the offending value is the NestedGlob source (not a destination). Consider wrapping the error with context that names the field, e.g.:

♻️ Optional clarity tweak
-                let search_root = self.project_root.join(&target.source);
-                // SECURITY: Validate search root to prevent traversal/absolute escapes.
-                self.revalidate_destination_path(&search_root)?;
+                let search_root = self.project_root.join(&target.source);
+                // SECURITY: Validate search root to prevent traversal/absolute escapes.
+                self.revalidate_destination_path(&search_root)
+                    .with_context(|| {
+                        format!(
+                            "NestedGlob source resolves outside project root: {}",
+                            target.source
+                        )
+                    })?;
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/linker.rs` around lines 384 - 401, The validation call for NestedGlob
uses self.revalidate_destination_path(&search_root)? which returns an error
message mentioning "Destination path..." even though the offending input is the
NestedGlob `source`; update the call to attach contextual information naming the
field so the propagated error clearly indicates the NestedGlob source is invalid
(e.g., replace the bare ?-propagation with adding context via .with_context or
map_err to include target.source/display), keeping the same failure semantics
and still returning the original error wrapped with a message like "NestedGlob
source '<…>' failed validation".
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@tests/test_security.rs`:
- Around line 1-47: Add two extra assertions/cases to the existing
test_nested_glob_search_root_traversal: (1) add a run that uses an absolute
source (use outside_dir.display() value in the TOML) to ensure absolute-path
escapes are blocked when creating the config and calling linker.sync(&options);
(2) call linker.clean(&SyncOptions::default()) and assert result.removed == 0 to
ensure malicious config cannot drive clean to touch outside files. Also
strengthen the final filesystem assertion by asserting the entire leaked
directory was not created (e.g., assert !project_root.join("leaked").exists())
in addition to checking the specific leaked AGENTS.md; reference the test
function test_nested_glob_search_root_traversal, the Linker struct and its
methods sync and clean, and SyncOptions to locate where to add these checks.

---

Outside diff comments:
In `@src/linker.rs`:
- Around line 384-401: The validation call for NestedGlob uses
self.revalidate_destination_path(&search_root)? which returns an error message
mentioning "Destination path..." even though the offending input is the
NestedGlob `source`; update the call to attach contextual information naming the
field so the propagated error clearly indicates the NestedGlob source is invalid
(e.g., replace the bare ?-propagation with adding context via .with_context or
map_err to include target.source/display), keeping the same failure semantics
and still returning the original error wrapped with a message like "NestedGlob
source '<…>' failed validation".
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: 62f65b33-c3d1-4a27-9f71-015181ee51e7

📥 Commits

Reviewing files that changed from the base of the PR and between 88fd2bc and cea2ac4.

📒 Files selected for processing (2)
  • src/linker.rs
  • tests/test_security.rs

Comment thread tests/test_security.rs Outdated
@yacosta738 yacosta738 marked this pull request as ready for review May 8, 2026 15:38
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@tests/test_security.rs`:
- Around line 64-80: The test builds a TOML string using outside_dir.display()
which on Windows produces backslashes that become invalid TOML escape sequences
when placed in a basic string; update the test in tests/test_security.rs so the
absolute-path case uses a TOML literal string or properly escapes backslashes
before calling Config::load: modify the absolute_toml construction (referenced
by absolute_toml, Config::load, Linker::new, absolute_linker, absolute_result)
to wrap the interpolated path in single quotes (TOML literal) or replace
backslashes with doubled backslashes (e.g. path_str.replace("\\","\\\\")).
Alternatively, if the test must remain Unix-only, gate the whole absolute-path
block with #[cfg(unix)] to avoid running on Windows.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: 86fae0a1-c7a2-40e7-ba76-8c1d8144a1a7

📥 Commits

Reviewing files that changed from the base of the PR and between cea2ac4 and 2fe4580.

📒 Files selected for processing (2)
  • src/linker.rs
  • tests/test_security.rs

Comment thread tests/test_security.rs
@yacosta738
Copy link
Copy Markdown
Contributor Author

@copilot Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In @tests/test_security.rs:

  • Around line 64-80: The test builds a TOML string using outside_dir.display()
    which on Windows produces backslashes that become invalid TOML escape sequences
    when placed in a basic string; update the test in tests/test_security.rs so the
    absolute-path case uses a TOML literal string or properly escapes backslashes
    before calling Config::load: modify the absolute_toml construction (referenced
    by absolute_toml, Config::load, Linker::new, absolute_linker, absolute_result)
    to wrap the interpolated path in single quotes (TOML literal) or replace
    backslashes with doubled backslashes (e.g. path_str.replace("\","\\")).
    Alternatively, if the test must remain Unix-only, gate the whole absolute-path
    block with #[cfg(unix)] to avoid running on Windows.

Copy link
Copy Markdown
Contributor

Copilot AI commented May 11, 2026

@copilot Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

...

Fixed in b20a90f. Applied .replace('\\', "\\\\") to the path string before embedding it in the TOML basic string, so Windows backslashes are properly escaped rather than being interpreted as TOML escape sequences.

@sonarqubecloud
Copy link
Copy Markdown

@yacosta738 yacosta738 merged commit 4ab7550 into main May 13, 2026
27 checks passed
@yacosta738 yacosta738 deleted the fix/security-nested-glob-traversal-10326664153959606349 branch May 13, 2026 11:30
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants