Skip to content

lfs: verify content hash and prevent object overwrite#8166

Merged
unknwon merged 8 commits intomainfrom
fix/lfs-object-overwrite
Feb 8, 2026
Merged

lfs: verify content hash and prevent object overwrite#8166
unknwon merged 8 commits intomainfrom
fix/lfs-object-overwrite

Conversation

@unknwon
Copy link
Member

@unknwon unknwon commented Feb 8, 2026

Summary

  • Prevent cross-repository LFS object overwrite by using O_CREATE|O_EXCL to atomically refuse writing to an existing file path. If the file already exists (same OID uploaded by another repo), the existing size is returned so the caller can still create the DB record linking the new repo to that OID.
  • Compute SHA-256 of uploaded content during write and reject the upload if the hash does not match the claimed OID, preventing an attacker from writing arbitrary content to a known OID path.

Test plan

  • Existing TestLocalStorage_Upload updated with matching OID/content pair
  • New test: OID mismatch rejects upload and cleans up file
  • New test: duplicate upload returns existing size and preserves original content
  • task lint passes
  • task test passes (lfsutil)

References: GHSA-cj4v-437j-jq4c

🤖 Generated with Claude Code

unknwon and others added 3 commits February 8, 2026 12:46
Prevent cross-repository LFS object overwrite by:
1. Using O_CREATE|O_EXCL to never overwrite existing files on disk.
   If the file already exists, it was previously hash-verified, so we
   return the existing size and let the caller create the DB record.
2. Computing SHA-256 of uploaded content and rejecting uploads where
   the hash does not match the claimed OID.

Reported-by: KKC73
References: GHSA-gmf8-978x-2fg2

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit fixes the style issues introduced in 85ebf17 according to the output
from Go fmt and Gofumpt.

Details: #8166
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

This PR hardens the local LFS storage backend to prevent cross-repository object overwrites and to ensure uploaded content matches the claimed SHA-256 OID, addressing GHSA-gmf8-978x-2fg2.

Changes:

  • Add SHA-256 verification during LocalStorage.Upload and reject uploads whose content hash doesn’t match the OID.
  • Prevent overwriting existing LFS objects by using atomic create semantics and returning existing object size on duplicates.
  • Extend LFS storage tests to cover OID mismatch cleanup and duplicate upload behavior, and document the fix in the changelog.

Reviewed changes

Copilot reviewed 3 out of 3 changed files in this pull request and generated 6 comments.

File Description
internal/lfsutil/storage.go Adds OID/content verification and uses exclusive create to prevent overwrites.
internal/lfsutil/storage_test.go Updates/extends tests for hash verification, cleanup on mismatch, and duplicate upload behavior.
CHANGELOG.md Notes the security fix in the development changelog.

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

- Write to a temp file and rename into place after hash verification, so
  the final path always contains a complete file even under concurrent
  uploads. This also avoids the deferred cleanup accidentally deleting a
  pre-existing file it did not create.
- Handle ErrOIDMismatch in the upload route to return 400 instead of 500.
- Check file close error before publishing.
- Use t.TempDir() in tests.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@unknwon unknwon added this to the 0.14.2 milestone Feb 8, 2026
- Lowercase error message for ErrObjectNotExist.
- Use a dedicated .tmp directory under the storage root for temp files
  instead of mixing them with object files.
- Add subdir under t.TempDir() for test clarity.
- Capitalize OID in test names and move comment inline.
- Rename "oid mismatch" test to "valid OID but wrong content".

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Add [lfs] TEMP_PATH config option (default: data/tmp/lfs) for storing
temporary files during upload hash verification, following the existing
pattern of [repository.upload] TEMP_PATH.

Clean up the LFS temp directory on startup.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
If os.Rename fails because the target already exists (concurrent upload
won the race), treat it as success since both uploads have verified
content.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@unknwon unknwon merged commit 81ee883 into main Feb 8, 2026
11 of 12 checks passed
@unknwon unknwon deleted the fix/lfs-object-overwrite branch February 8, 2026 22:14
unknwon added a commit that referenced this pull request Feb 19, 2026
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-authored-by: deepsource-autofix[bot] <62050782+deepsource-autofix[bot]@users.noreply.github.com>
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.

2 participants