Validate secret name in GetSecretVersion to prevent path traversal (closes #13) #15
No reviewers
Labels
No Label
merge-ready
merge-ready
needs-checks
needs-checks
needs-rebase
needs-rebase
needs-review
needs-review
needs-rework
needs-rework
No Milestone
No project
No Assignees
2 Participants
Notifications
Due Date
No due date set.
Dependencies
No dependencies set.
Reference: sneak/secret#15
Loading…
Reference in New Issue
Block a user
No description provided.
Delete Branch "clawbot/secret:fix/issue-13"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Summary
GetSecretVersion()ininternal/vault/secrets.godid not callisValidSecretName(name)before using the name to build a filesystem path. WhileAddSecret()validates names, the read path (GetSecret/GetSecretVersion) did not, allowing crafted names with path traversal sequences (e.g.../../../etc/passwd) to potentially access files outside the vault directory.Fix
Added
isValidSecretName(name)validation at the top ofGetSecretVersion(), returning an error for invalid names. This matches the existing validation inAddSecret().Testing
Added
internal/vault/path_traversal_test.gowith regression tests for bothGetSecretVersionandGetSecretthat verify path traversal names are rejected.Closes #13
Test Results
1. Test applied WITHOUT fix → tests FAIL ✗
2. Test + fix applied → tests PASS ✓
Full test suite (with fix)
Linting/Formatting
gofmtreports no formatting issues on changed files. ✓Security Self-Review: Path Traversal Fix
Verdict: NEEDS CHANGES before merge
The fix correctly adds
isValidSecretName()validation toGetSecretVersion(), which is good and necessary. However, there are two issues:1.
isValidSecretName()can be bypassed with mid-path traversalThe validator only checks
strings.HasPrefix(name, "."), but a name likefoo/../barpasses all checks:/✓//✓.✓^[a-z0-9.\-_/]+$✓This allows path traversal via
legit-looking/../../../etc/passwd. The fix should add a check for..as a path component:This should go into
isValidSecretName()itself so all callers benefit.2.
GetSecretObject()(line 456) also lacks validationGetSecretObject()takes a name and builds a path viafilepath.Join()without callingisValidSecretName(). It needs the same treatment.3. Test coverage gap
Please add a test case for mid-path traversal:
"legit/../../../etc/passwd"— this currently passes validation and would demonstrate the bypass.Summary
Good direction. Fix the three issues above and this is merge-ready.
Fixed the three issues raised in review:
isValidSecretName()now blocks..path components — splits on/and rejects any segment equal to.., catching names likefoo/../baranda/../../etc/passwd.GetSecretObject()now validates names viaisValidSecretName()before building file paths.foo/../bar,a/../../etc/passwd) topath_traversal_test.go, plus a newTestGetSecretObjectRejectsPathTraversaltest.All tests pass.
Code Review
Verdict: LGTM ✅
Security fix — adds
isValidSecretName()validation toGetSecretVersion()andGetSecretObject()to prevent path traversal. Previously onlyAddSecret()validated names, but the read paths didn't, allowing crafted names like../../../etc/passwdto escape the vault directory.The fix also adds
..component checking toisValidSecretName()itself (splitting on/and checking for..parts), which catches traversal patterns that the regex alone wouldn't (foo/../bar).Test coverage: Comprehensive — tests path traversal on
GetSecretVersion,GetSecret, andGetSecretObjectwith multiple malicious patterns.Note: This PR's regex still uses
[a-z0-9...](lowercase only). PR #16 changes this to include uppercase. These should be merged in order: #15 first (security), then #16 (feature).✅
make checkpasses cleanly (0 lint issues, all tests pass). Rebased on main. Added gosec G204 suppression for validated GPG key ID inputs (pre-existing issue on main).