Add gitresolver package for git:// skill references#4051
Conversation
There was a problem hiding this comment.
Large PR Detected
This PR exceeds 1000 lines of changes and requires justification before it can be reviewed.
How to unblock this PR:
Add a section to your PR description with the following format:
## Large PR Justification
[Explain why this PR must be large, such as:]
- Generated code that cannot be split
- Large refactoring that must be atomic
- Multiple related changes that would break if separated
- Migration or data transformationAlternative:
Consider splitting this PR into smaller, focused changes (< 1000 lines each) for easier review and reduced risk.
See our Contributing Guidelines for more details.
This review will be automatically dismissed once you add the justification section.
Codecov Report❌ Patch coverage is Additional details and impacted files@@ Coverage Diff @@
## main #4051 +/- ##
==========================================
+ Coverage 68.62% 68.68% +0.06%
==========================================
Files 448 453 +5
Lines 45776 46008 +232
==========================================
+ Hits 31412 31600 +188
- Misses 11946 11968 +22
- Partials 2418 2440 +22 ☔ View full report in Codecov by Sentry. 🚀 New features to boost your workflow:
|
aponcedeleonch
left a comment
There was a problem hiding this comment.
Review of the gitresolver package. Nice self-contained foundation — the interface design and functional options are clean. A few issues to address before merging, ranging from a functional bug to code duplication and test coverage.
See inline comments below.
Fixes all review comments from PR #4051: - BUG-1: Detect semver-like refs (v1.0.0) and use Tag field instead of Branch to fix tag-based clones - DUP-1/DUP-2: Reuse exported ValidatePathNoSymlinks, DirPermissions, and FilePermissionMask from pkg/skills/installer.go - DUP-3: Reuse pkg/networking.IsPrivateIP and IsLocalhost for SSRF validation instead of reimplementing - NEW-1: Move HeadCommitHash to Client interface for mockability - NEW-3: Remove redundant filepath.Clean in writer.go - NEW-5: Add debug log when fallback GIT_TOKEN is used for non-standard hosts - NEW-6: Replace network-dependent context cancellation test with a blocking mock client - MINOR-1: Fix isHex("") returning true - Coverage: Add tests for tag refs, commit refs, branch refs, semver detection, and isHex edge cases Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Fixes all review comments from PR #4051: - BUG-1: Detect semver-like refs (v1.0.0) and use Tag field instead of Branch to fix tag-based clones - DUP-1/DUP-2: Reuse exported ValidatePathNoSymlinks, DirPermissions, and FilePermissionMask from pkg/skills/installer.go - DUP-3: Reuse pkg/networking.IsPrivateIP and IsLocalhost for SSRF validation instead of reimplementing - NEW-1: Move HeadCommitHash to Client interface for mockability - NEW-3: Remove redundant filepath.Clean in writer.go - NEW-5: Add debug log when fallback GIT_TOKEN is used for non-standard hosts - NEW-6: Replace network-dependent context cancellation test with a blocking mock client - MINOR-1: Fix isHex("") returning true - Coverage: Add tests for tag refs, commit refs, branch refs, semver detection, and isHex edge cases Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
f37e048 to
646f27f
Compare
aponcedeleonch
left a comment
There was a problem hiding this comment.
Most previous comments are addressed — nice work. Constants and ValidatePathNoSymlinks are now shared, and the tag/SSRF/auth/test issues are fixed.
One remaining concern: the file-writing loop in writer.go and installer.go still uses raw os.WriteFile without atomic writes or file locking. The codebase already has fileutils.AtomicWriteFile and fileutils.WithFileLock used by config_editor.go, encrypted.go, and file_status.go. Both skill installers should adopt the same pattern. See inline comment for a suggested consolidation.
aponcedeleonch
left a comment
There was a problem hiding this comment.
approved with the above suggestion
Introduces pkg/skills/gitresolver with URL parsing, clone resolution, host-scoped auth, and secure file writing for git-based skill install. This is the foundation package — skillsvc integration follows in the next PR. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Fixes all review comments from PR #4051: - BUG-1: Detect semver-like refs (v1.0.0) and use Tag field instead of Branch to fix tag-based clones - DUP-1/DUP-2: Reuse exported ValidatePathNoSymlinks, DirPermissions, and FilePermissionMask from pkg/skills/installer.go - DUP-3: Reuse pkg/networking.IsPrivateIP and IsLocalhost for SSRF validation instead of reimplementing - NEW-1: Move HeadCommitHash to Client interface for mockability - NEW-3: Remove redundant filepath.Clean in writer.go - NEW-5: Add debug log when fallback GIT_TOKEN is used for non-standard hosts - NEW-6: Replace network-dependent context cancellation test with a blocking mock client - MINOR-1: Fix isHex("") returning true - Coverage: Add tests for tag refs, commit refs, branch refs, semver detection, and isHex edge cases Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Anchor semver regex and require at least one dot (v1.0, not v1) to prevent misclassifying branch names like v1-beta-branch as tags - Document DNS rebinding limitation in validateHost - Add HeadCommitHash unit tests for nil inputs and valid repos - Clarify collectFiles vs WriteFiles directory handling consistency - Add regression tests for branch-like refs with v-prefix Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
f7ab6ed to
03d5dda
Compare
The containment-check + write loop in gitresolver/writer.go was near-identical to installer.go. Extract a shared WriteContainedFile helper in pkg/fileutils that performs path containment validation, parent directory creation, and atomic file writes via AtomicWriteFile. Both callers now delegate to this helper, and gitresolver/WriteFiles wraps its operation in WithFileLock to prevent concurrent installs from corrupting each other. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Summary
thv skill install git://...) needs a package to parse git references, clone repos, resolve auth, and securely write extracted files. This PR adds that foundation as a self-contained package without wiring it into the skill service yet.pkg/skills/gitresolver/package with four components: URL parsing with SSRF prevention, clone resolution with timeout, host-scoped credential resolution, and secure file writing with symlink/containment checks.HeadCommitHashhelper topkg/git/for retrieving the HEAD commit hash after clone (used by the resolver to record provenance).Relates to #4015
Type of change
Large PR Justification
This PR is 1268 lines but ~655 lines (over half) are unit tests. The remaining ~613 lines are a single self-contained package (
pkg/skills/gitresolver/) with four tightly coupled components (URL parsing, clone resolution, auth, file writing) that share types and validation logic. Splitting these into separate PRs would create packages that don't compile independently and add review overhead without reducing risk. The 14-lineHeadCommitHashaddition topkg/git/is the only change outside the new package.Test plan
task test)task lint-fix)Changes
pkg/skills/gitresolver/reference.gogit://URL parsing with SSRF prevention (private IP/localhost rejection, absolute path/backslash rejection)pkg/skills/gitresolver/resolver.goResolverinterface: clones repo, extracts skill files, enforces 2-min clone timeoutpkg/skills/gitresolver/auth.goGITHUB_TOKENonly sent to github.com, etc.)pkg/skills/gitresolver/writer.gopkg/git/client.goHeadCommitHashhelper function*_test.goDoes this introduce a user-facing change?
No — this is a foundation package with no integration yet. The next PR will wire it into skillsvc.
Special notes for reviewers
Security design
GITHUB_TOKENis only sent togithub.com,GITLAB_TOKENonly togitlab.com.GIT_TOKENis an opt-in fallback for other hosts.context.WithTimeoutinResolve()prevents slowloris-style hangs.validateSkillPathrejects absolute paths and backslashes in addition to..traversal.LimitedFscaps clones at 10k files / 100MB.Known limitations for follow-up
http.TransportwithDialContexthook)collectFilesreads only top-level files in the skill directory (flat structure)This is PR 2 of 3 — split from #4042. PR 1 (#4045, merged) moved the git package. PR 3 will integrate gitresolver into skillsvc.
Generated with Claude Code