package cli import ( "encoding/json" "path/filepath" "strings" "git.eeqj.de/sneak/secret/internal/secret" "git.eeqj.de/sneak/secret/internal/vault" "github.com/spf13/afero" "github.com/spf13/cobra" ) // getSecretNamesCompletionFunc returns a completion function that provides secret names func getSecretNamesCompletionFunc(fs afero.Fs, stateDir string) func( cmd *cobra.Command, args []string, toComplete string, ) ([]string, cobra.ShellCompDirective) { return func(_ *cobra.Command, _ []string, toComplete string) ([]string, cobra.ShellCompDirective) { // Get current vault vlt, err := vault.GetCurrentVault(fs, stateDir) if err != nil { return nil, cobra.ShellCompDirectiveNoFileComp } // Get list of secrets secrets, err := vlt.ListSecrets() if err != nil { return nil, cobra.ShellCompDirectiveNoFileComp } // Filter secrets based on what user has typed var completions []string for _, secret := range secrets { if strings.HasPrefix(secret, toComplete) { completions = append(completions, secret) } } return completions, cobra.ShellCompDirectiveNoFileComp } } // getUnlockerIDsCompletionFunc returns a completion function that provides unlocker IDs func getUnlockerIDsCompletionFunc(fs afero.Fs, stateDir string) func( cmd *cobra.Command, args []string, toComplete string, ) ([]string, cobra.ShellCompDirective) { return func(_ *cobra.Command, _ []string, toComplete string) ([]string, cobra.ShellCompDirective) { // Get current vault vlt, err := vault.GetCurrentVault(fs, stateDir) if err != nil { return nil, cobra.ShellCompDirectiveNoFileComp } // Get unlocker metadata list unlockerMetadataList, err := vlt.ListUnlockers() if err != nil { return nil, cobra.ShellCompDirectiveNoFileComp } // Get vault directory vaultDir, err := vlt.GetDirectory() if err != nil { return nil, cobra.ShellCompDirectiveNoFileComp } // Collect unlocker IDs var completions []string for _, metadata := range unlockerMetadataList { // Get the actual unlocker ID by creating the unlocker instance unlockersDir := filepath.Join(vaultDir, "unlockers.d") files, err := afero.ReadDir(fs, unlockersDir) if err != nil { continue } for _, file := range files { if !file.IsDir() { continue } unlockerDir := filepath.Join(unlockersDir, file.Name()) metadataPath := filepath.Join(unlockerDir, "unlocker-metadata.json") // Check if this is the right unlocker by comparing metadata metadataBytes, err := afero.ReadFile(fs, metadataPath) if err != nil { continue } var diskMetadata secret.UnlockerMetadata if err := json.Unmarshal(metadataBytes, &diskMetadata); err != nil { continue } // Match by type and creation time if diskMetadata.Type == metadata.Type && diskMetadata.CreatedAt.Equal(metadata.CreatedAt) { // Create the appropriate unlocker instance var unlocker secret.Unlocker switch metadata.Type { case "passphrase": unlocker = secret.NewPassphraseUnlocker(fs, unlockerDir, diskMetadata) case "keychain": unlocker = secret.NewKeychainUnlocker(fs, unlockerDir, diskMetadata) case "pgp": unlocker = secret.NewPGPUnlocker(fs, unlockerDir, diskMetadata) } if unlocker != nil { id := unlocker.GetID() if strings.HasPrefix(id, toComplete) { completions = append(completions, id) } } break } } } return completions, cobra.ShellCompDirectiveNoFileComp } } // getVaultNamesCompletionFunc returns a completion function that provides vault names func getVaultNamesCompletionFunc(fs afero.Fs, stateDir string) func( cmd *cobra.Command, args []string, toComplete string, ) ([]string, cobra.ShellCompDirective) { return func(_ *cobra.Command, _ []string, toComplete string) ([]string, cobra.ShellCompDirective) { vaults, err := vault.ListVaults(fs, stateDir) if err != nil { return nil, cobra.ShellCompDirectiveNoFileComp } var completions []string for _, v := range vaults { if strings.HasPrefix(v, toComplete) { completions = append(completions, v) } } return completions, cobra.ShellCompDirectiveNoFileComp } }