Skip to content

feat: add secret_service, pass, and credential_manager resolvers#152

Merged
JoshMock merged 16 commits into
mainfrom
feat/extend-external-secrets
Apr 15, 2026
Merged

feat: add secret_service, pass, and credential_manager resolvers#152
JoshMock merged 16 commits into
mainfrom
feat/extend-external-secrets

Conversation

@MattDevy
Copy link
Copy Markdown
Contributor

@MattDevy MattDevy commented Apr 14, 2026

Summary

  • Add three new expression resolvers for native OS secret stores, eliminating the need to hand-roll $(cmd:...) shell commands:
    • secret_service (Linux): freedesktop.org Secret Service API via secret-tool lookup
    • pass (cross-platform): standard Unix password manager via pass show, returns first line only
    • credential_manager (Windows): Windows Credential Manager via PowerShell Get-StoredCredential with -EncodedCommand to avoid shell injection
  • Extract shared parseServiceAccount helper from keychainResolver to DRY service/account validation across keychain, secret_service, and credential_manager
  • Fix MegaLinter config so ESLint actually runs on TypeScript files (was silently skipped before this PR)

Usage

# Linux (GNOME Keyring / KWallet)
api_key: $(secret_service:elastic-cli/api-key)

# macOS (existing)
api_key: $(keychain:elastic-cli/api-key)

# Windows (Credential Manager)
api_key: $(credential_manager:elastic-cli/api-key)

# Cross-platform (pass)
api_key: $(pass:elastic/api-key)

Design decisions

Decision Rationale
pass first-line only Pass puts password on line 1, metadata below. Full output would leak metadata.
credential_manager uses -EncodedCommand Avoids layered cmd.exe/PowerShell quoting vulnerabilities vs raw -Command
credential_manager 10s timeout PowerShell startup overhead vs 5s for native CLI tools
pass no platform restriction Works on Linux, macOS, and Windows (WSL)
Empty-result guard on credential_manager and pass Get-StoredCredential can exit 0 with empty output on missing targets

MegaLinter ESLint fixes

The existing MegaLinter config had several issues that prevented ESLint from ever running on CI:

  • Wrong descriptor name: TYPESCRIPT_ESLINT (not a real descriptor) changed to TYPESCRIPT_ES
  • Missing devDependencies: container sets NODE_ENV=production, so npm ci skipped eslint/typescript-eslint. Fixed with NODE_ENV=development
  • Stale CLI flag: MegaLinter passes --no-eslintrc which was removed in ESLint 9+. Fixed with COMMAND_REMOVE_ARGUMENTS
  • Binary path: use project's ESLint 10 binary at /tmp/lint/node_modules/.bin/eslint (built-in is too old for flat config)

Test plan

  • 30 new unit tests across 3 describe blocks (10 secret_service, 9 pass, 11 credential_manager)
  • All existing resolver tests pass unchanged (keychain refactor is behavior-preserving)
  • Full test suite green (120 resolver tests, 0 failures)
  • MegaLinter ESLint runs successfully on 119 .ts files (verified locally)
  • Manual verification on Linux with secret-tool installed
  • Manual verification on Windows with CredentialManager PowerShell module
  • Manual verification with pass installed

@github-actions
Copy link
Copy Markdown

github-actions Bot commented Apr 14, 2026

MegaLinter analysis: Success

Descriptor Linter Files Fixed Errors Warnings Elapsed time
✅ COPYPASTE jscpd yes no no 2.77s
✅ REPOSITORY gitleaks yes no no 8.59s
✅ REPOSITORY git_diff yes no no 0.07s
✅ REPOSITORY secretlint yes no no 2.14s
✅ REPOSITORY trivy yes no no 14.8s
✅ TYPESCRIPT eslint 2 0 0 3.26s
✅ YAML yamllint 1 0 0 0.68s

See detailed reports in MegaLinter artifacts
Set VALIDATE_ALL_CODEBASE: true in mega-linter.yml to validate all sources, not only the diff

MegaLinter is graciously provided by OX Security
Show us your support by starring ⭐ the repository

@MattDevy MattDevy marked this pull request as draft April 14, 2026 20:40
@MattDevy MattDevy marked this pull request as ready for review April 14, 2026 20:43
@MattDevy
Copy link
Copy Markdown
Contributor Author

Apologies for the spam, I realised megalinter wasn't running eslint (fixed in this PR)

Copy link
Copy Markdown
Member

@JoshMock JoshMock left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔒

})

it('throws on Windows', async () => {
const restorePlatform = _testSetPlatform('win32')
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

future nice-to-have: OS-specific integration tests that actually hit their respective secret services.

@JoshMock JoshMock merged commit 92e6982 into main Apr 15, 2026
19 checks passed
@JoshMock JoshMock deleted the feat/extend-external-secrets branch April 15, 2026 17:21
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants