Commit Graph

124 Commits

Author SHA1 Message Date
a6f24e9581 Fix --keyid flag scope and implement secret move command
- Restrict --keyid flag to PGP unlocker type only
- Add validation to prevent --keyid usage with non-PGP unlockers
- Implement 'secret move' command with 'mv' and 'rename' aliases
- Add comprehensive tests for move functionality
- Update documentation to reflect optional nature of --keyid for PGP

The move command allows renaming or moving secrets within a vault while
preserving all versions and metadata. It fails if the destination already
exists to prevent accidental overwrites.
2025-07-26 01:26:27 +02:00
a73a409fe4 Refactor unlockers command structure and add quiet flag to list command
- Rename 'unlockers' command to 'unlocker' for consistency
- Move all unlocker subcommands (list, add, remove) under single 'unlocker' command
- Add --quiet/-q flag to 'secret list' for scripting support
- Update documentation and tests to reflect command changes

The quiet flag outputs only secret names without headers or formatting,
making it ideal for shell script usage like: secret get $(secret list -q | head -1)
2025-07-22 16:04:44 +02:00
70d19d09d0 latest 2025-07-22 13:35:19 +02:00
40ea47b2a1 Add missing changes from feature branch
- Update Makefile to run lint and vet before tests
- Add install target to Makefile
- Fix keychainunlocker_stub.go for non-Darwin platforms
2025-07-22 12:51:02 +02:00
7ed3e287ea Merge branch 'add-list-remove-commands' 2025-07-22 12:47:20 +02:00
8e3530a510 Fix use-after-free crash in readSecurePassphrase
The function was using defer to destroy password buffers, which caused
the buffers to be freed before the function returned. This led to a
SIGBUS error when trying to access the destroyed buffer's memory.

Changed to manual memory management to ensure buffers are only destroyed
when no longer needed, and the first buffer is returned directly to the
caller who is responsible for destroying it.
2025-07-22 12:46:16 +02:00
e5d7407c79 Fix mnemonic input to not echo to screen
Changed mnemonic input to use secure non-echoing input like passphrases:
- Use secret.ReadPassphrase() instead of readLineFromStdin()
- Add newline after hidden input for better UX
- Remove unused stdin reading functions from cli.go

This prevents sensitive mnemonic phrases from being displayed on screen
during input, matching the security behavior of passphrase input.
2025-07-22 12:39:32 +02:00
377b51f2db Add Docker support for building and running the CLI tool
- Add DOCKER_HOST export to Makefile for remote Docker daemon
- Create multi-stage Dockerfile:
  - Build stage: golang:1.24-alpine with gcc, make, git
  - Runtime stage: alpine with ca-certificates, gnupg
  - Runs as non-root 'secret' user
- Add Makefile targets:
  - docker: build container as sneak/secret
  - docker-run: run container interactively
- Add .dockerignore to exclude build artifacts but keep .git
  for potential linker flags

Container includes GPG support for PGP unlockers and runs on Linux,
making it suitable for cross-platform testing and deployment.
2025-07-21 22:13:19 +02:00
a09fa89f30 Fix cross-platform build issues and security vulnerabilities
- Add build tags to keychain implementation files (Darwin-only)
- Create stub implementations for non-Darwin platforms that panic
- Conditionally show keychain support in help text based on platform
- Platform check in UnlockersAdd prevents keychain usage on non-Darwin
- Verified GPG operations already protected against command injection
  via validateGPGKeyID() and proper exec.Command argument passing
- Keychain operations use go-keychain library, no shell commands

The application now builds and runs on Linux/non-Darwin platforms with
keychain functionality properly isolated to macOS only.
2025-07-21 22:05:23 +02:00
7af1e6efa8 Improve PGP unlocker ergonomics
- Support 'secret unlockers add pgp [keyid]' positional argument syntax
- Automatically detect and use default GPG key when no key is specified
- Change PGP unlocker ID format from <keyid>-pgp to pgp-<keyid>
- Check if PGP key is already added before creating duplicate unlocker
- Add getDefaultGPGKey() that checks gpgconf first, then falls back to
  first secret key
- Export ResolveGPGKeyFingerprint() for use in CLI
- Add checkUnlockerExists() helper to verify unlocker IDs

The new behavior:
- 'secret unlockers add pgp' uses default GPG key
- 'secret unlockers add pgp KEYID' uses specified key
- 'secret unlockers add pgp --keyid=KEYID' also works
- Errors if key is already added or no default key exists
2025-07-21 18:57:58 +02:00
09b3a1fcdc Remove internal/macse package and fix all linter issues
- Remove internal/macse package (Secure Enclave experiment)
- Fix errcheck: handle keychain.DeleteItem error return
- Fix lll: break long lines in command descriptions
- Fix mnd: add nolint comment for cobra.ExactArgs(2)
- Fix nlreturn: add blank lines before return/break statements
- Fix revive: add nolint comment for KEYCHAIN_APP_IDENTIFIER constant
- Fix nestif: simplify UnlockersRemove by using new NumSecrets method
- Add NumSecrets() method to vault.Vault for counting secrets
- Update golangci.yml to exclude ALL_CAPS warning (attempted various
  configurations but settled on nolint comment)

All tests pass, code is formatted and linted.
2025-07-21 17:48:47 +02:00
816f53f819 Replace shell-based keychain implementation with keybase/go-keychain library
- Replaced exec.Command calls to /usr/bin/security with native keybase/go-keychain API
- Added comprehensive test suite for keychain operations
- Fixed binary data storage in tests using hex encoding
- Updated macse tests to skip with explanation about ADE requirements
- All tests passing with CGO_ENABLED=1
2025-07-21 15:58:41 +02:00
bba1fb21e6 docs 2025-07-15 19:01:29 +02:00
d4f557631b prototype secure enclave interface 2025-07-15 09:37:02 +02:00
e53161188c Fix remaining memory security issues
- Fixed gpgDecryptDefault to return *memguard.LockedBuffer instead of []byte
- Updated GPGDecryptFunc signature and all implementations
- Confirmed getSecretValue already returns LockedBuffer (was fixed earlier)
- Improved passphrase string handling by removing intermediate variables
- Note: String conversion for passphrases is unavoidable due to age library API
- All GPG decrypted data is now immediately protected in memory
2025-07-15 09:08:51 +02:00
ff17b9b107 Update TODO.md - DecryptWithPassphrase already fixed
- DecryptWithPassphrase was automatically fixed when we updated DecryptWithIdentity
- It now returns LockedBuffer since it calls DecryptWithIdentity internally
2025-07-15 09:04:59 +02:00
63cc06b93c Fix DecryptWithIdentity to return LockedBuffer
- Changed DecryptWithIdentity to return *memguard.LockedBuffer instead of []byte
- Updated all callers throughout the codebase to handle LockedBuffer
- This ensures decrypted data is protected in memory immediately after decryption
- Fixed all usages in vault, secret, version, and unlocker implementations
- Removed duplicate buffer creation and unnecessary memory clearing
2025-07-15 09:04:34 +02:00
8ec3fc877d Fix GetValue methods to return LockedBuffer internally
- Changed Secret.GetValue and Version.GetValue to return *memguard.LockedBuffer
- Updated all internal callers to handle LockedBuffer properly
- For backward compatibility, vault.GetSecret still returns []byte but makes a copy
- This ensures secret values are protected in memory during decryption
- Updated tests to handle LockedBuffer returns
- Fixed CLI getSecretValue to use LockedBuffer throughout
2025-07-15 08:59:23 +02:00
819902f385 Fix gpgEncryptDefault to accept LockedBuffer for data parameter
- Changed GPGEncryptFunc signature to accept *memguard.LockedBuffer instead of []byte
- Updated gpgEncryptDefault implementation to use LockedBuffer
- Updated all callers including tests to pass LockedBuffer
- This ensures GPG encryption data is protected in memory
- Fixed linter issue with line length
2025-07-15 08:46:33 +02:00
292564c6e7 Fix storeInKeychain to accept LockedBuffer for data parameter
- Changed storeInKeychain to accept *memguard.LockedBuffer instead of []byte
- Updated caller in CreateKeychainUnlocker to create LockedBuffer before storing
- This ensures keychain data is protected in memory before being stored
- Added proper buffer cleanup with defer Destroy()
2025-07-15 08:44:09 +02:00
eef2332823 Fix EncryptWithPassphrase to accept LockedBuffer for data parameter
- Changed EncryptWithPassphrase to accept *memguard.LockedBuffer instead of []byte
- Updated all callers to pass LockedBuffer:
  - CreatePassphraseUnlocker in vault/unlockers.go
  - Keychain unlocker in keychainunlocker.go
  - Tests in passphrase_test.go
- Removed intermediate dataBuffer creation since data is now already protected
- This ensures sensitive data is protected in memory throughout encryption
2025-07-15 08:42:46 +02:00
e82d428b05 Remove deprecated Secret.Save function
- Removed unused deprecated Save(value []byte, force bool) function
- This function accepted unprotected secret data which was a security issue
- All code now uses vault.AddSecret directly with LockedBuffer
- Updated TODO.md to reflect completion of this security fix
2025-07-15 08:40:35 +02:00
9cbe055791 fmt 2025-07-15 08:33:16 +02:00
7596049828 uses protected memory buffers now for all secrets in ram 2025-07-15 08:32:33 +02:00
d3ca006886 Merge branch 'main' into fix-memory-security 2025-07-15 07:36:13 +02:00
f91281e991 Merge branch 'fix-test-json-fields' 2025-07-15 07:35:58 +02:00
7c5e78db17 fix: update JSON fields from snake_case to camelCase and make tests quiet by default
- Update all JSON field references in tests from snake_case to camelCase
- Update vault list JSON output to use currentVault instead of current_vault
- Make integration tests quiet by default unless run with -v flag
- Fix tests that were using exec.Command to use in-process execution helpers
- Tests now only show debug output when explicitly requested or on failure
2025-07-15 07:35:48 +02:00
8e374b3d24 add test binaries to gitignore 2025-07-15 07:24:15 +02:00
c9774e89e0 WIP: refactor to use memguard for secure memory handling
- Add memguard dependency
- Update ReadPassphrase to return LockedBuffer
- Update EncryptWithPassphrase/DecryptWithPassphrase to accept LockedBuffer
- Remove string wrapper functions
- Update all callers to create LockedBuffers at entry points
- Update interfaces and mock implementations
2025-07-15 07:23:58 +02:00
f9938135c6 fix: resolve all remaining linter issues (staticcheck, tagliatelle, lll)
- Fix staticcheck QF1011: Remove explicit type declaration for io.Writer variables
- Fix tagliatelle: Change all JSON tags from snake_case to camelCase
  - created_at → createdAt
  - keychain_item_name → keychainItemName
  - age_public_key → agePublicKey
  - age_priv_key_passphrase → agePrivKeyPassphrase
  - encrypted_longterm_key → encryptedLongtermKey
  - derivation_index → derivationIndex
  - public_key_hash → publicKeyHash
  - mnemonic_family_hash → mnemonicFamilyHash
  - gpg_key_id → gpgKeyId
- Fix lll: Break long function signature line to stay under 120 character limit

All linter issues have been resolved. The codebase now passes all linter checks.
2025-07-15 06:33:25 +02:00
386a27c0b6 fix: resolve all revive linter issues
Added missing package comments:
- cmd/secret/main.go
- internal/cli/cli.go
- internal/secret/constants.go
- internal/vault/management.go
- pkg/bip85/bip85.go

Fixed comment format issues for exported items:
- EnvStateDir, EnvMnemonic, EnvUnlockPassphrase, EnvGPGKeyID in constants.go
- Metadata, UnlockerMetadata, SecretMetadata, Configuration in metadata.go
- AppBIP39, AppHDWIF, AppXPRV in bip85.go

Replaced unused parameters with underscore (_):
- generate.go:39 - parameter 'args'
- init.go:30 - parameter 'args'
- unlockers.go:39,77,102 - parameter 'args' or 'cmd'
- vault.go:37 - parameter 'args'
- management.go:34 - parameter 'target'
2025-07-15 06:06:48 +02:00
080a3dc253 fix: resolve all nlreturn linter errors
Add blank lines before return statements in all files to satisfy
the nlreturn linter. This improves code readability by providing
visual separation before return statements.

Changes made across 24 files:
- internal/cli/*.go
- internal/secret/*.go
- internal/vault/*.go
- pkg/agehd/agehd.go
- pkg/bip85/bip85.go

All 143 nlreturn issues have been resolved.
2025-07-15 06:00:32 +02:00
811ddee3b7 fix: resolve all nestif linter errors
- Extract getLongTermPrivateKey helper function to reduce nesting in keychainunlocker.go and pgpunlocker.go
- Add getPassphrase helper method to reduce nesting in passphraseunlocker.go
- Refactor version serial extraction to use early returns in version.go
- Extract resolveRelativeSymlink and tryResolveOsSymlink helpers in management.go
- Add processMnemonicForVault helper to reduce nesting in vault creation
- Extract resolveUnlockerDirectory and readUnlockerPathFromFile helpers in unlockers.go
- Add findUnlockerByID helper to reduce duplicate code in RemoveUnlocker and SelectUnlocker

All tests pass after refactoring.
2025-07-15 05:47:16 +02:00
4e242c3491 go 1.24 2025-07-09 16:09:59 -07:00
54fce0f187 fix: resolve mnd and nestif linter errors
- Define base85 constants (base85ChunkSize, base85DigitCount, base85Base)
- Replace magic numbers in base85 encoding logic with named constants
- Add nolint:nestif comments for legitimate nested conditionals:
  - crypto.go: Clear separation between secret generation vs retrieval
  - secrets.go: Separate JSON and table output formatting logic
  - vault.go: Separate JSON and text output formatting logic
2025-07-09 12:54:59 -07:00
93a32217e0 fix: resolve mnd (magic number) linter errors in agehd and bip85 packages
- Define x25519KeySize constant (32) at package level in agehd
- Replace all magic number 32 uses with x25519KeySize constant
- Define bech32BitSize8 and bech32BitSize5 constants for bit conversions
- Define bip85EntropySize constant (64) for entropy validation
- Define BIP39 word count constants (words12-24) with descriptive names
2025-07-09 12:52:46 -07:00
95ba80f618 fix: resolve gochecknoglobals, gosec, lll, and mnd linter errors
- Add nolint comments for BIP85 standard constants (MainNetPrivateKey, TestNetPrivateKey)
- Handle error return from shake.Write() in NewBIP85DRNG
- Fix line length issue by moving nolint comment to separate line
- Add nolint comment for cobra.ExactArgs(2) magic number
- Replace magic number 32 with named constant x25519KeySize in agehd package
2025-07-09 12:49:59 -07:00
d710323bd0 fix: add nolint comments for necessary global variables in internal/secret
Add nolint:gochecknoglobals comments for legitimate global variables:
- debugEnabled and debugLogger: Package-wide debug state management
- GPGEncryptFunc and GPGDecryptFunc: Required for test mocking
- getCurrentVaultFunc: Required to break import cycle between packages
2025-07-09 12:47:51 -07:00
38b450cbcf fix: resolve mnd and nestif linter errors
- Added constants to replace magic numbers:
  - agePrivKeyPassphraseLength = 64
  - versionNameParts = 2
  - maxVersionsPerDay = 999
- Refactored crypto.go to reduce nesting complexity:
  - Inverted if condition to handle non-existent secret first
  - Extracted getSecretValue helper method
2025-07-09 07:05:07 -07:00
6fe49344e2 fix: resolve errcheck, gosec, and mnd linter errors
- Fixed unhandled errors in init.go (os.Setenv/Unsetenv)
- Fixed unhandled errors in test_helpers.go (os.Setenv/Unsetenv)
- Replaced magic numbers with named constants:
  - defaultSecretLength = 16
  - mnemonicEntropyBits = 128
  - tabWriterPadding = 2
2025-07-09 06:59:01 -07:00
6e01ae6002 chore: exclude errcheck linter from test files
Test files often ignore error returns for brevity and clarity,
especially for cleanup operations that don't affect test outcomes.
2025-07-09 06:18:52 -07:00
11e43542cf fix: handle error returns from os.Unsetenv and file.Close (errcheck)
Fixed the first five errcheck linter errors:
- Added error handling for os.Unsetenv in cli_test.go
- Added error handling for file.Close() in crypto.go (4 instances)
2025-07-09 06:16:13 -07:00
2256a37b72 Update golangci-lint configuration to v2.2.1 format
- Add version field set to "2" for golangci-lint v2.2.1
- Remove formatters (gofmt, gofumpt, goimports) from linters list
- Remove unsupported linters (gosimple, typecheck)
- Simplify usetesting configuration
- Move max-issues settings to proper location in issues section
- Remove timeout field from run section
2025-07-09 06:11:48 -07:00
533133486c fix: remove unnecessary type conversions (unconvert)
- Remove unnecessary conversions of UnlockerMetadata in vault/unlockers.go
- The metadata variable is already of the correct type due to type aliasing
2025-06-20 12:52:19 -07:00
eb19fa4b97 fix: replace unused parameters with underscores (revive)
- Replace unused function parameters with _ in test files
- Affects version_test.go, debug_test.go, and pgpunlock_test.go
2025-06-20 12:50:16 -07:00
5ed850196b fix: convert ALL_CAPS constants to CamelCase (revive)
- Rename APP_BIP39 to AppBIP39
- Rename APP_HD_WIF to AppHDWIF
- Rename APP_XPRV to AppXPRV
- Rename APP_PWD85 to AppPWD85
- Update all references in the code
2025-06-20 12:49:01 -07:00
be1f323a09 fix: remove unnecessary zero value initialization (revive)
- Remove explicit = 0 from uint32 declaration as it's the default zero value
2025-06-20 12:47:58 -07:00
bdcddadf90 fix: resolve exported type stuttering issues (revive)
- Rename VaultMetadata to Metadata in internal/vault package to avoid stuttering
- Rename BIP85DRNG to DRNG in pkg/bip85 package to avoid stuttering
- Update all references in code and tests
2025-06-20 12:47:06 -07:00
4062242063 fix: break long error messages to meet line length limits
Split long error messages at logical points to comply with 120 character
line length limit while maintaining readability.
2025-06-20 09:51:26 -07:00
abcc7b6c3a fix: resolve gosec integer overflow and unconvert issues
- Fix G115 integer overflow by converting uint32 to int comparison
- Remove unnecessary int() conversions for syscall constants
- syscall.Stdin/Stderr/Stdout are already int type
2025-06-20 09:50:00 -07:00