fix(secrets): make file keyring filenames Windows-safe (#502)#527
Closed
solomonneas wants to merge 1 commit intoopenclaw:mainfrom
Closed
fix(secrets): make file keyring filenames Windows-safe (#502)#527solomonneas wants to merge 1 commit intoopenclaw:mainfrom
solomonneas wants to merge 1 commit intoopenclaw:mainfrom
Conversation
The 99designs/keyring file backend writes one file per item, escaping only '/' in filenames via percent.Encode. NTFS rejects ':', '<', '>', '"', '|', '?', '*', and '\', so token keys like 'token:default:user@gmail.com' cannot be persisted on Windows: any auth-requiring command exits with 'open ...token:default:<email>: The filename, directory name, or volume label syntax is incorrect.' Wrap the keyring with fileSafeKeyring, which percent-encodes Windows-illegal characters in item keys before they reach the on-disk filename builder and prepends a '_v1_' sentinel so wrapper-written entries are unambiguously distinguishable from legacy raw-form files. Reads fall back to the raw key form so existing macOS/Linux keyring directories keep working without manual migration; writes opportunistically remove the legacy file once the new encoded form lands. The shim is applied unconditionally rather than only when the file backend is explicitly selected, so it also covers the auto-fallback case where 99designs/keyring's opener silently lands on the file backend (e.g. Windows + WinCred broken, or Linux + secret-service+kwallet broken). For non-file backends the shim is effectively a no-op for keys without Windows-illegal characters and transparently migrates legacy items on first read/write. Fixes openclaw#502.
Collaborator
|
Thanks @solomonneas. I landed a narrower maintainer version on main as 8135cc3. What changed from the PR version:
Closing this as landed. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes #502.
Problem
The
99designs/keyringfile backend writes one file per item, escaping only/in filenames viapercent.Encode. NTFS rejects:,<,>,",|,?,*, and\, so token keys liketoken:default:user@gmail.comcannot be persisted on Windows. Any auth-requiring command exits with:Fix
Wrap the keyring with a new
fileSafeKeyringshim that::,<,>,",|,?,*,\, plus%for self-inverse encoding) in item keys before they reach the on-disk filename builder._v1_sentinel to encoded keys soKeys()can unambiguously distinguish wrapper-written entries from legacy raw-form files. Avoids corrupting any pre-existing item whose key happened to contain a literal%XXtriplet.~/.local/share/gogcli/keyring/directories on macOS/Linux keep working without manual migration.Keys()does not return duplicates.ERROR_INVALID_NAMEon Windows, surfaced as a*fs.PathErrorwrappingsyscall.Errno(0x7B)) tokeyring.ErrKeyNotFound. Detection is build-tagged:filename_safe_windows.gofor the real check,filename_safe_other.goreturningfalseeverywhere else. This is what lets the raw-form fallback safely run on every platform without leaking a confusing error from a path the underlying filesystem could never have stored.The shim is applied unconditionally rather than only when the file backend is explicitly selected. That covers two cases at once:
99designs/keyring's opener tries native backends in priority order and silently lands on the file backend when they fail (e.g. Windows + WinCred unavailable, Linux + secret-service+kwallet unavailable). The wrapper now catches those too.Tests
internal/secrets/filename_safe_test.go,_other_test.go,_windows_test.goadd 25 tests:Encoder unit tests
_v1_are still encoded, preserving the round trip.decodeFilenameKeydoes not corrupt legacy%XX-bearing names (no_v1_prefix → returned as-is).End-to-end fixture against a real
99designs/keyringfile backend in a temp dirKeys()returns the original (decoded) key set with duplicates collapsed.Getfalls back to legacy raw-form files (Skipf-guarded for filesystems that reject the legacy filename, so the test stays portable).Setmigrates a legacy raw file to the encoded form.Removecleans both encoded and legacy forms.Get,GetMetadata,Removeof missing keys returnkeyring.ErrKeyNotFound.Cross-platform error-translation behaviour via a
fakeInnerKeyringplus a swappableisInvalidFilenameErrorGet/GetMetadata/Removetranslate the simulated "invalid filename" error tokeyring.ErrKeyNotFound.SetandRemovesurvive the inner backend rejecting the raw-form cleanup attempt with that error.acceptingFakeKeyringcovers the WinCred upgrade scenario: a non-file backend that accepts colon-bearing keys still serves legacy items via the raw-key fallback.Platform-specific defaults
filename_safe_windows_test.go(build-taggedwindows) verifiesdefaultIsInvalidFilenameErrormatches a*fs.PathErrorwrappingsyscall.Errno(0x7B)and rejects unrelated errnos.filename_safe_other_test.go(build-tagged!windows) verifies the default returnsfalse.Verification
make fmtclean,make lint0 issues,make testgreen across all packages includingcmd/gogandinternal/cmd.-raceclean on the 25 new tests. (The pre-existing race inTestOpenKeyringWithTimeout_Timeoutis upstream/main behaviour, untouched here.)GOOS=windows GOARCH=amd64andGOOS=darwin GOARCH=arm64both build the package and tests cleanly.encodeFilenameKey/decodeFilenameKey/needsFilenameEncoding/isNotFound; 79.5% package overall.Notes
Out of scope on purpose, called out in a comment on
safeFilenameMapso a future contributor knows where to extend:CON,PRN,AUX,NUL,COM1-9,LPT1-9).The current gogcli key namespace (
token:<client>:<email>anddefault_account[:<client>]) cannot produce any of those forms.Distinct from #395 (CI test skips on Windows). This is the runtime crash path for real user machines whether they explicitly set
keyring_backend: fileor auto-fall through to it.