fix(core): DPAPI keyring works when started from PowerShell 7 #18

Merged
james.maes merged 1 commits from fix/dpapi-psmodulepath into main 2026-11-08 14:03:20 +00:00
2 changed files with 11 additions and 1 deletions

No files matched your search

+7
View File
@@ -102,6 +102,13 @@ changes listed under Unreleased above as of this tag.
the Homebrew tap and Scoop bucket. The `cli-v*` tags and GitHub releases are retired; `scripts/install.sh`,
the formula and the Scoop manifest download from `https://git.kof22.com/QRun-IO/qqq-cli/releases`.
### Fixed
- **Windows: the DPAPI keyring works when `qqq` is started from PowerShell 7.** The inherited `PSModulePath`
named PowerShell 7's modules, so Windows PowerShell 5.1 could not load `Microsoft.PowerShell.Security` and every
sign-in failed with "Windows credential protection failed". The keyring now starts PowerShell without it.
Found by the first native Windows release lane.
## [1.0.0] - 2026-11-01
`qqq` 1.0: the command-line client and the full-screen terminal UI (`qqq ui`), shipped together as one
+4 -1
View File
@@ -110,7 +110,10 @@ export class DpapiFileStore implements SecretStore {
+ '[Console]::Write((ConvertTo-Json -InputObject $qqqResult -Compress))'
return new Promise<string>((resolve, reject) => {
const failed = () => reject(new Error('Windows credential protection failed; check PowerShell and your Windows profile'))
const child = execFile(shell, ['-NoProfile', '-NonInteractive', '-Command', command], { timeout: 15000 }, (error, stdout) => {
// Started from PowerShell 7, the inherited PSModulePath names PowerShell 7's modules, and Windows PowerShell 5.1
// then cannot load Microsoft.PowerShell.Security (ConvertTo-SecureString). Without it, each shell uses its own.
const env = Object.fromEntries(Object.entries(process.env).filter(([name]) => name.toUpperCase() !== 'PSMODULEPATH'))
const child = execFile(shell, ['-NoProfile', '-NonInteractive', '-Command', command], { timeout: 15000, env }, (error, stdout) => {
if (error) { failed(); return }
try {
const result: unknown = JSON.parse(stdout.trim())