Summary
On Windows, ~/.asobi/credentials.json (access token, refresh token, device secret) is stored as plaintext JSON. The 0o600/0o700 modes we pass to os.WriteFile/os.MkdirAll only set the read-only attribute on Windows -- they do not create an ACL. The file is protected only by the ACL it inherits from the user profile (owner + SYSTEM + Administrators).
This is not an exploitable weakness on a default install: other unprivileged users cannot read another user's profile, which matches the Unix 0600 guarantee for that threat (there, root can read it too). It's a defense-in-depth gap -- on Unix the mode is an explicit per-file guarantee, while on Windows we rely on inherited directory ACLs and keep the tokens in the clear.
Proposed hardening
Encrypt the credential blob at rest on Windows with DPAPI (CryptProtectData / CryptUnprotectData, available via golang.org/x/sys/windows), scoped to the current user (CRYPTPROTECT_UI_FORBIDDEN). This ties the ciphertext to the logged-in user account, so an Administrator or SYSTEM read of the raw file yields ciphertext, not tokens.
Notes
- Leave the Unix path unchanged --
0600/0700 is the idiomatic guarantee there.
- Gate the DPAPI code behind
//go:build windows so the dependency and syscalls don't touch other platforms.
- Optional follow-up for parity: macOS Keychain / libsecret on Linux. DPAPI is the highest-value first step since it needs no external daemon or keyring.
- Prior art:
gh, aws, and gcloud all encrypt or OS-keyring their stored credentials.
Follow-up to the per-platform wording fix in SECURITY.md.
Summary
On Windows,
~/.asobi/credentials.json(access token, refresh token, device secret) is stored as plaintext JSON. The0o600/0o700modes we pass toos.WriteFile/os.MkdirAllonly set the read-only attribute on Windows -- they do not create an ACL. The file is protected only by the ACL it inherits from the user profile (owner + SYSTEM + Administrators).This is not an exploitable weakness on a default install: other unprivileged users cannot read another user's profile, which matches the Unix
0600guarantee for that threat (there, root can read it too). It's a defense-in-depth gap -- on Unix the mode is an explicit per-file guarantee, while on Windows we rely on inherited directory ACLs and keep the tokens in the clear.Proposed hardening
Encrypt the credential blob at rest on Windows with DPAPI (
CryptProtectData/CryptUnprotectData, available viagolang.org/x/sys/windows), scoped to the current user (CRYPTPROTECT_UI_FORBIDDEN). This ties the ciphertext to the logged-in user account, so an Administrator or SYSTEM read of the raw file yields ciphertext, not tokens.Notes
0600/0700is the idiomatic guarantee there.//go:build windowsso the dependency and syscalls don't touch other platforms.gh,aws, andgcloudall encrypt or OS-keyring their stored credentials.Follow-up to the per-platform wording fix in
SECURITY.md.