Skip to content

Validate and restrict git skill sync paths to prevent arbitrary overwrite#211

Open
AkaraChen wants to merge 1 commit into
mainfrom
codex/fix-arbitrary-directory-overwrite-in-git-sync-tsd024
Open

Validate and restrict git skill sync paths to prevent arbitrary overwrite#211
AkaraChen wants to merge 1 commit into
mainfrom
codex/fix-arbitrary-directory-overwrite-in-git-sync-tsd024

Conversation

@AkaraChen

Copy link
Copy Markdown
Owner

Motivation

  • Prevent unsafe git-sync behavior that accepted attacker-controlled cloned skill paths and arbitrary destination source_paths, which allowed deletion/replacement of arbitrary user directories.\
  • Require repository-relative skill path validation and ensure sync targets correspond to installed skills within the requested scope.\

Description

  • Added scope and optional project_root to GitSyncRequest so the server can validate which installation locations are in-scope before mutating them (crates/api/src/dto/skill.rs).\
  • Implemented repo-relative cloned-skill validation rejecting absolute paths and .. traversal and requiring the cloned path to parse as a skill and remain under the repo temp dir (resolve_cloned_skill_dir, reject_unsafe_relative_path).\
  • Built an allow-list of canonical installed skill directories for the requested scope and validated each client-supplied source_path against that set before any remove_dir_all or copy occurs (allowed_git_sync_target_dirs, validate_git_sync_target_dir).\
  • Updated the git_sync_skill route to use these helpers and only perform delete-and-copy on validated target directories; parsing of the cloned skill is now required.\
  • Added all handling for sync scope (global/project/both) so clients can request validation across both scopes when appropriate.\
  • Updated the desktop flow and generated DTO to include sync scope metadata (desktop sends scope: "all" and project_root where applicable) so the backend can perform validation.\
  • Added focused route unit tests for path traversal rejection, repo-containment/skill-parsing, and allow-list enforcement (crates/api/src/routes/skills.rs tests).\

Testing

  • Ran formatting checks: cargo fmt --all --check — succeeded.\
  • Ran frontend formatting checks: cd crates/desktop && bunx prettier --check src/components/sync-github-skill-dialog.tsx src/generated/dto/GitSyncRequest.ts — succeeded.\
  • Added unit tests covering the new validation helpers, but running cargo test -p aghub-api in this environment hit network/caching limitations and could not complete (crate downloads blocked), so backend tests were not executed here.\
  • Attempted frontend typecheck (cd crates/desktop && bun run typecheck) which failed in this environment because node dependencies are not installed, so full TS typechecking was not run here.

Codex Task

@coderabbitai

coderabbitai Bot commented Jun 3, 2026

Copy link
Copy Markdown

Warning

Review limit reached

@AkaraChen, we couldn't start this review because you've reached your PR review rate limit.

More reviews will be available in 28 minutes and 25 seconds. Learn how PR review limits work.

Your organization has run out of usage credits. Purchase more in the billing tab.

⌛ How to resolve this issue?

After more reviews become available, 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 include higher PR review limits than trial, open-source, and free plans. In all cases, reviews become available again over time. During sustained high-volume PR review activity, CodeRabbit may temporarily slow when the next review becomes available.

Please see our Fair Usage Limits Policy for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 066a2108-20be-489e-b9f8-5731c78107a5

📥 Commits

Reviewing files that changed from the base of the PR and between 88dc33e and e5bb29f.

⛔ Files ignored due to path filters (1)
  • crates/desktop/src/generated/dto/GitSyncRequest.ts is excluded by !**/generated/**
📒 Files selected for processing (3)
  • crates/api/src/dto/skill.rs
  • crates/api/src/routes/skills.rs
  • crates/desktop/src/components/sync-github-skill-dialog.tsx
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch codex/fix-arbitrary-directory-overwrite-in-git-sync-tsd024

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.

@chatgpt-codex-connector chatgpt-codex-connector 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.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: e5bb29f88b

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

));
}

Ok(target_dir)

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Mutate the canonical sync target, not the supplied path

When source_paths contains a symlink that points at an allowed installed skill directory, canonical_target passes the allow-list check, but the function returns the original target_dir; the later remove_dir_all/copy therefore operates on the symlink path outside the allow-list rather than on the installed skill. Since source_paths is client supplied, a caller can create a symlink to any installed skill and cause sync to replace that symlink path with cloned repo contents, bypassing the intended restriction to installed skill directories. Returning the canonical target, or requiring the supplied path itself to be an allowed installed path, keeps the mutation in-scope.

Useful? React with 👍 / 👎.

Comment on lines +415 to +416
for resources in load_all_agents(resource_scope, project_root) {
for skill in resources.skills {

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Build the sync allow-list without Both-scope dedupe

When scope is all with a project root and the same skill name is installed for the same agent in both project and global scopes, this allow-list misses the global installation because load_all_agents(ResourceScope::Both, ...) delegates to load_both_annotated, which records project skill names first and then skips matching global names in crates/core/src/manager/mod.rs:128-159. A request that is meant to sync both installations will therefore validate the project path but reject the global source_path as INVALID_SYNC_TARGET; build the allow-list from each scope separately so all really includes both directories.

Useful? React with 👍 / 👎.

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.

1 participant