Skip to content

Comments

fix: prevent sync from overwriting existing files (#027)#27

Closed
laggu wants to merge 1 commit intodevelopfrom
gv-027
Closed

fix: prevent sync from overwriting existing files (#027)#27
laggu wants to merge 1 commit intodevelopfrom
gv-027

Conversation

@laggu
Copy link
Owner

@laggu laggu commented Feb 18, 2026

Summary

Prevents git volume sync from overwriting existing files or directories that are not symlinks.

Description

Currently, sync command removes the target path blindly before linking/copying. This PR adds a check to ensure we only overwrite if the target does not exist or is a symlink. If a regular file or directory exists at the target path, we skip it and return an error to prevent data loss.

Issue

Closes #27

Type of Change

  • Bug fix (non-breaking change which fixes an issue)
  • New feature (non-breaking change which adds functionality)
  • Breaking change (fix or feature that would cause existing functionality to not work as expected)
  • Documentation update

Testing

  • Added regression tests for overwrite protection in internal/gitvolume/sync_test.go
  • Verified existing tests pass

* fix: prevent sync from overwriting existing non-symlink files
* test: add regression tests for sync overwrite protection
@gemini-code-assist
Copy link

Summary of Changes

Hello @laggu, 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 addresses a potential data loss scenario in the git volume sync command. Previously, the command would blindly remove target paths before syncing, which could lead to the accidental deletion of user-managed files or directories. The changes introduce a protective mechanism that checks if a target path already exists and is not a symlink, preventing its overwrite and returning an error to the user. This ensures that only symlinks or non-existent paths are affected by the sync operation, significantly improving the safety and reliability of the synchronization process.

Highlights

  • Data Loss Prevention: Implemented a check in the sync command to prevent it from overwriting existing regular files or directories, thereby safeguarding against unintended data loss.
  • Symlink Overwrite Policy: The sync command will still overwrite existing symlinks, as they are generally considered safe to replace.
  • Error Handling: If a target path already exists and is not a symlink, the sync operation for that volume will now return an error, indicating that it was skipped to prevent data loss.
  • Regression Tests: New regression tests were added to internal/gitvolume/sync_test.go to specifically verify the overwrite protection for both files and directories.

🧠 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
  • internal/gitvolume/sync.go
    • Added logic to check if a target path exists and is not a symlink before proceeding with synchronization.
    • Introduced an error return if a non-symlink target exists, preventing its overwrite.
  • internal/gitvolume/sync_test.go
    • Renamed and updated existing tests to reflect the new overwrite protection behavior.
    • Added new test cases to specifically verify that existing regular files and directories are protected from being overwritten by sync operations.
Activity
  • A bug fix was introduced to prevent data loss during the git volume sync operation.
  • New code was added to internal/gitvolume/sync.go to implement the overwrite protection.
  • Comprehensive regression tests were added and modified in internal/gitvolume/sync_test.go to validate the new protection mechanism.
  • Existing tests were verified to ensure no regressions were introduced.
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.

@laggu
Copy link
Owner Author

laggu commented Feb 18, 2026

✅ Automated Review: Checks Passed

  • CI Status: All checks passed (Build, Lint, Test, Integration)
  • Code Coverage: Regression tests added and verified.
  • Functionality: Overwrite protection verified against requirements.

Ready for merge.

Copy link

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

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 aims to introduce a crucial safety check to prevent git volume sync from overwriting existing files or directories that are not symlinks, thereby preventing data loss. However, this implementation introduces a significant Time-of-Check to Time-of-Use (TOCTOU) race condition, which could still lead to data loss in shared environments. Furthermore, it causes a regression that breaks the idempotency of the sync command for directories in ModeCopy, impacting the tool's ability to ensure a consistent state over multiple runs.

Comment on lines +101 to +115
if _, err := os.Lstat(vol.TargetPath); err == nil {
// Target exists, check if it's a symlink (safe to remove)
// We allow overwriting symlinks because they are likely created by us (or user wants to replace them)
// But we DO NOT overwrite regular files or directories to prevent data loss.
targetInfo, err := os.Lstat(vol.TargetPath)
if err != nil {
return fmt.Errorf("failed to check target %s: %w", vol.Target, err)
}

if targetInfo.Mode()&os.ModeSymlink == 0 {
// It is NOT a symlink (regular file or directory)
// For safety, we skip this volume and report an error.
return fmt.Errorf("target %s already exists and is not a symlink (skipping to prevent data loss)", vol.Target)
}
}

Choose a reason for hiding this comment

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

security-medium medium

This section introduces a critical Time-of-Check to Time-of-Use (TOCTOU) race condition. The os.Lstat check (lines 101-115) for existing files/directories, followed by os.RemoveAll (line 124), creates a window where an attacker could replace a symlink with a regular directory or file. This could lead to os.RemoveAll recursively deleting attacker-supplied content, potentially causing significant data loss and undermining the intended overwrite protection. Additionally, consider defining a specific error type for this scenario to allow callers to handle it programmatically, rather than relying on string matching of the error message "target %s already exists and is not a symlink (skipping to prevent data loss)".

Comment on lines +110 to +113
if targetInfo.Mode()&os.ModeSymlink == 0 {
// It is NOT a symlink (regular file or directory)
// For safety, we skip this volume and report an error.
return fmt.Errorf("target %s already exists and is not a symlink (skipping to prevent data loss)", vol.Target)

Choose a reason for hiding this comment

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

security-medium medium

Regression: Broken Idempotency for Directory Sync

The check targetInfo.Mode()&os.ModeSymlink == 0 prevents overwriting any existing directory that is not a symlink. However, when git-volume is used in ModeCopy for a directory, it creates a real directory at the target path.

On subsequent runs of the sync command, this check will identify the previously created directory as "not a symlink" and fail with an error. This breaks the idempotency of the sync operation, which is a core design principle of the tool (as noted in the comments on line 117). This can lead to partial sync states where some volumes are updated while others are skipped due to this error, potentially leaving the workspace in an inconsistent or insecure configuration.

Severity: Medium
Vulnerability Type: Logic Error

@laggu
Copy link
Owner Author

laggu commented Feb 19, 2026

Closing: sync의 무조건 삭제/재생성(Delete-and-Recreate) 동작이 올바른 설계라는 결론.

  • link 모드: target은 symlink이므로 삭제/재생성해도 원본(source)에 영향 없음
  • copy 모드: target은 source의 파생물이므로 원본 상태로 맞추는 것이 sync의 목적

overwrite protection은 불필요하며, 오히려 idempotency를 깨뜨리는 regression이 발생함 (ModeCopy 디렉토리 재sync 시 에러).

@laggu laggu closed this Feb 19, 2026
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