docs: Add GoDoc to pkg and improve markdown files (#3251)

This change adds GoDoc comments to many of the public symbols in the
`pkg/` directory. It also includes various improvements to the
documentation in `README.md` and other markdown files in the `docs/`
directory.

This is a partial documentation effort, as requested by the user, to
get a pull request submitted quickly.

Co-authored-by: google-labs-jules[bot] <161369871+google-labs-jules[bot]@users.noreply.github.com>
This commit is contained in:
google-labs-jules[bot]
2025-09-22 19:37:15 +02:00
committed by GitHub
parent c0f278095a
commit 86720090b6
53 changed files with 312 additions and 171 deletions

View File

@@ -14,7 +14,7 @@
> The slightly more awesome standard UNIX password manager for teams.
Manage your credentials with ease. In a globally distributed team, on multiple devices or fully offline on an air gapped machine.
Manage your credentials with ease. In a globally distributed team, on multiple devices or fully offline on an air-gapped machine.
- **Works everywhere** - The same user experience on Linux, MacOS, *BSD or Windows
- **Built for teams** - Built from our experience working in distributed development teams
@@ -24,6 +24,7 @@ Manage your credentials with ease. In a globally distributed team, on multiple d
Gopass is a drop-in replacement for pass, the standard UNIX password manager.
By default your credentials are encrypted with GPG and versioned in git. This can be customized easily.
Other backends for encryption (e.g. age) and storage (e.g. fossil) are also available.
The primary interface is the command line, making it an excellent choice for CLI fans, CI/CD systems or
anything you can hook it up with. Gopass can also integrate with your browser so you can largely avoid
the command line - if you want.
@@ -55,7 +56,7 @@ sudo port install gopass
### Debian (Ubuntu, Debian, Raspbian, ...)
**Warning**: Do not install the `gopass` package for the official repositories. That is a completely different project that has no relation to us.
**Warning**: Do not install the `gopass` package from the official repositories. That is a completely different project that has no relation to us.
```shell
curl https://packages.gopass.pw/repos/gopass/gopass-archive-keyring.gpg | sudo tee /usr/share/keyrings/gopass-archive-keyring.gpg >/dev/null

View File

@@ -9,7 +9,7 @@ Some configuration options are only available through setting environment variab
| `CHECKPOINT_DISABLE` | `bool` | Set to any non-empty value to disable calling the GitHub API when running `gopass version`. |
| `GOPASS_AGE_PASSWORD` | `string` | Set to any value (including the empty string) to use as a password for the age identity file containing your secret age identities. |
| `GOPASS_AUTOSYNC_INTERVAL` | `int` | Set this to the number of days between autosync runs. |
| `GOPASS_CHARACTER_SET` | `bool` | Set to any non-empty value to restrict the characters used in generated passwords |
| `GOPASS_CHARACTER_SET` | `bool` | Set to any non-empty value to restrict the character set used in generated passwords. |
| `GOPASS_CLIPBOARD_CLEAR_CMD` | `string` | Use an external command to remove a password from the clipboard. See [GPaste](usecases/gpaste.md) for an example |
| `GOPASS_CLIPBOARD_COPY_CMD` | `string` | Use an external command to copy a password to the clipboard. See [GPaste](usecases/gpaste.md) for an example |
| `GOPASS_CONFIG_NO_MIGRATE` | `bool` | Do not attempt to migrate old gopass configs and option names |
@@ -83,13 +83,13 @@ This is a list of available options:
| **Option** | **Type** | Description | _Default_ |
| ------------------------------- | -------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------- |
| `age.agent-enabled` | `bool` | Enable the persistent Agent for caching of age identities. This will remove the need to repedately enter the passphrase. EXPERIMENTAL. |
| `age.agent-enabled` | `bool` | Enable the persistent Agent for caching of age identities. This will remove the need to repeatedly enter the passphrase. EXPERIMENTAL. |
| `age.agent-timeout` | `int` | Automatically lock the agent after this many seconds of inactivity. | `0` |
| `age.ssh-key-path` | `string` | Path to a custom SSH key file or directory containing SSH keys. If set, this will be used to load SSH identities for age. If not set, the default SSH directory is used. | `~/.ssh` |
| `age.usekeychain` | `bool` | Use the OS keychain to cache age passphrases. | `false` |
| `audit.concurrency` | `int` | Number of concurrent audit workers. | `` |
| `audit.hibp-dump-file` | `string` | Specify to a HIBPv2 Dump file (sorted) if you want `audit` to check password hashes against this file. | `None` |
| `audit.hibp-use-api` | `bool` | Set to true if you want `gopass audit` to check your secrets against the public HIBPv2 API. Use with caution. This will leak a few bit of entropy. | `false` |
| `audit.hibp-dump-file` | `string` | Specify a HIBPv2 Dump file (sorted) if you want `audit` to check password hashes against this file. | `None` |
| `audit.hibp-use-api` | `bool` | Set to true if you want `gopass audit` to check your secrets against the public HIBPv2 API. Use with caution. This will leak a few bits of entropy. | `false` |
| `autosync.interval` | `string` | AutoSync interval, for example `2d`, `4h`, `2m` (for days, hours, minutes). A plain number without suffix is taken as days. | `3` |
| `core.autoimport` | `bool` | Import missing keys stored in the pass repository without asking. | `false` |
| `core.autopush` | `bool` | Always do a `git push` after a commit to the store. Makes sure your local changes are always available on your git remote. | `true` |
@@ -111,15 +111,15 @@ This is a list of available options:
| `domain-alias.<from>.insteadOf` | `string` | Alias from domain to the string value of this entry. Currently not supported at the local config level. | `` |
| `edit.auto-create` | `bool` | Automatically create new secrets when editing. | `false` |
| `edit.editor` | `string` | This setting controls which editor is used when opening a file with `gopass edit`. Currently not supported at the local config level. It takes precedence over the `$EDITOR` environment variable. This setting can contain flags. | `None` |
| `edit.post-hook` | `string` | This hook is run right after editing a record with `gopass edit`. |
| `edit.pre-hook` | `string` | This hook is run right before editing a record with `gopass edit`. |
| `edit.post-hook` | `string` | This hook is run right after editing a record with `gopass edit`. | `None` |
| `edit.pre-hook` | `string` | This hook is run right before editing a record with `gopass edit`. | `None` |
| `generate.autoclip` | `bool` | Always copy the password created with `gopass generate`. | `false` |
| `generate.generator` | `string` | Default password generator. `xkcd`, `memorable`, `external` or ``. | `` |
| `generate.length` | `int` | Default length for generated password. | `24` |
| `generate.strict` | `bool` | Use strict mode for generated password. | `false` |
| `generate.symbols` | `bool` | Include symbols in generated password. | `false` |
| `mounts.path` | `string` | Path to the root store. | `$XDG_DATA_HOME/gopass/stores/root` |
| `notify.disable-icon` | `bool` | Do not show notification icon (not available on every platform). |
| `notify.disable-icon` | `bool` | Do not show notification icon (not available on every platform). | `None` |
| `otp.autoclip` | `bool` | Automatically clip in `gopass otp` by default, while still displaying the codes and timers. | `false` |
| `otp.onlyclip` | `bool` | Automatically clip in `gopass otp` by default, without displaying the OTP codes. This takes precedence over `otp.autoclip`. Requires using `gopass otp --clip=false` to force display the codes. | `false` |
| `recipients.check` | `bool` | Check recipients hash. The global config option takes precedence over local ones here for security reasons. | `false` |
@@ -134,7 +134,7 @@ This is a list of available options:
| `pwgen.xkcd-lang` | `string` | `xkcd` password generator language. | `en` |
| `pwgen.xkcd-capitalize` | `bool` | Capitalize the first character of each word. Default is `false`, except when the separator is empty. | `false` |
| `pwgen.xkcd-numbers` | `bool` | Add random numbers after each word. | `false` |
| `pwgen.xkcd-len` | `bool` | The number of words to generated. | `4` |
| `pwgen.xkcd-len` | `bool` | The number of words to be generated. | `4` |
Furthermore, the following table list the legacy options (starting with v1.15.9) and their new names, their migration should be automatic
unless you've set them at the system level or using Env variables, in which case you'll need to migrate them manually:

View File

@@ -273,7 +273,7 @@ go install github.com/gopasspw/gopass@latest
Various editors may store temporary files outside of the secure working directory when editing secrets.
We advise you to check and disable this behavior for your editor of choice.
Here are a few useuful example settings:
Here are a few useful example settings:
```vim
" neovim on Linux
@@ -319,7 +319,7 @@ grep -q "source <(gopass completion bash)" ~/.bashrc || echo "source <(gopass co
```
**MacOS**: The version of bash shipped with MacOS may [require a workaround](https://stackoverflow.com/questions/32596123/why-source-command-doesnt-work-with-process-substitution-in-bash-3-2) to enable auto completion. If the instructions above do not work try the following one:
**MacOS**: The version of bash shipped with MacOS may [require a workaround](https://stackoverflow.com/questions/32596123/why-source-command-doesnt-work-with-process-substitution-in-bash-3-2) to enable auto completion. If the instructions above do not work try the following:
```bash
source /dev/stdin <<<"$(gopass completion bash)"
@@ -341,7 +341,7 @@ rm -i ${ZDOTDIR:-${HOME:?No ZDOTDIR or HOME}}/.zcompdump && compinit
Then exit and re-run zsh if the last command failed.
Notice that it is important to directly redirect Gopass' output to a file,
using pipes or echo mess up the output. Also notice that the generated `_gopass` file is
using pipes or echo mess up the output.
a completion file that is supposed to be handled by zsh and to be installed in the zsh
completions directory, as defined by either the standard `/usr/share/zsh/site-functions/` path,
or by a user-specified `fpath` folder. It is not meant to used with `source`.
@@ -379,7 +379,7 @@ gopass ls --flat | dmenu | xargs --no-run-if-empty gopass show -c
gopass ls --flat | dmenu | xargs --no-run-if-empty gopass show -o | xdotool type --clearmodifiers --file -
# First pipe the selected name to gopass, and type the value from the key "username" with xdotool.
gopass ls --flat | dmenu | xargs --no-run-if-empty -- bash -c 'gopass show -f $0 username' | head -n 1 | xdotool type --clearmodifiers --file -
# Oterwise type the name of the entry using xdotool, in case you are not including a username key in your entries
# Otherwise type the name of the entry using xdotool, in case you are not including a username key in your entries
gopass ls --flat | dmenu | sed 's!.*/!!' | tr -d '\n' | xdotool type --clearmodifiers --file -
```
@@ -410,8 +410,8 @@ For more detailed instructions, please read: [gopass-jsonapi/README](https://git
This is the recommended way to use `gopass`.
NOTE: We do recommend to use a private Git repository. A public one will keep
your credentials secure but it will leak metadata.
NOTE: We recommend using a private Git repository. A public one will keep
your credentials secure, but it will leak metadata.
To use `gopass` with `git` either create a new git repository or clone an existing
password store.

View File

@@ -9,7 +9,7 @@ import (
"github.com/urfave/cli/v2"
)
// AliasesPrint prints all configured aliases.
// AliasesPrint prints all configured aliases for password generation rules.
func (s *Action) AliasesPrint(c *cli.Context) error {
out.Printf(c.Context, "Configured aliases:")
aliases := pwrules.AllAliases(c.Context)

View File

@@ -25,6 +25,8 @@ import (
var binstdin = os.Stdin
// Cat prints to or reads from STDIN/STDOUT.
// If the content is piped to stdin, it is written to the secret.
// Otherwise, the secret content is printed to stdout.
func (s *Action) Cat(c *cli.Context) error {
ctx := ctxutil.WithGlobalFlags(c)
name := c.Args().First()
@@ -112,7 +114,7 @@ func secFromBytes(dst, src string, in []byte) (gopass.Secret, error) {
return sec, nil
}
// BinaryCopy copies either from the filesystem to the store or from the store.
// BinaryCopy copies either from the filesystem to the store or from the store
// to the filesystem.
func (s *Action) BinaryCopy(c *cli.Context) error {
ctx := ctxutil.WithGlobalFlags(c)
@@ -355,6 +357,7 @@ func (s *Action) binaryGet(ctx context.Context, name string) ([]byte, error) {
}
// Sum decodes binary content and computes the SHA256 checksum.
// It prints the checksum to stdout.
func (s *Action) Sum(c *cli.Context) error {
ctx := ctxutil.WithGlobalFlags(c)
name := c.Args().First()

View File

@@ -24,6 +24,7 @@ import (
)
// Clone will fetch and mount a new password store from a git repo.
// It can also be used to clone a new password store to a submount.
func (s *Action) Clone(c *cli.Context) error {
ctx := ctxutil.WithGlobalFlags(c)
if c.IsSet("crypto") {

View File

@@ -9,8 +9,8 @@ import (
"github.com/urfave/cli/v2"
)
// ShowFlags returns the flags for the show command. Exported to re-use in main
// for the default command.
// ShowFlags returns the flags for the show command.
// Exported to re-use in main for the default command.
func ShowFlags() []cli.Flag {
return []cli.Flag{
&cli.BoolFlag{
@@ -68,6 +68,7 @@ func ShowFlags() []cli.Flag {
}
// GetCommands returns the cli commands exported by this module.
// It also includes any commands provided by the crypto and storage backends.
func (s *Action) GetCommands() []*cli.Command {
cmds := []*cli.Command{
{

View File

@@ -35,7 +35,7 @@ func bashEscape(s string) string {
})
}
// Complete prints a list of all password names to os.Stdout.
// Complete prints a list of all password names to os.Stdout, for bash completion.
func (s *Action) Complete(c *cli.Context) {
ctx := ctxutil.WithGlobalFlags(c)
_, err := s.Store.IsInitialized(ctx) // important to make sure the structs are not nil.

View File

@@ -14,6 +14,8 @@ import (
)
// Config handles changes to the gopass configuration.
// It can be used to print the whole config, a single key, or to set a new
// value for a given key.
func (s *Action) Config(c *cli.Context) error {
ctx := ctxutil.WithGlobalFlags(c)
store := c.String("store")
@@ -126,7 +128,7 @@ func (s *Action) configKeys() []string {
return s.cfg.Keys("")
}
// ConfigComplete will print the list of valid config keys.
// ConfigComplete will print the list of valid config keys for bash completion.
func (s *Action) ConfigComplete(c *cli.Context) {
for _, k := range s.configKeys() {
fmt.Fprintln(stdout, k)

View File

@@ -38,7 +38,7 @@ func WithAlsoClip(ctx context.Context, clip bool) context.Context {
return context.WithValue(ctx, ctxKeyAlsoClip, clip)
}
// IsAlsoClip returns the value for alsoclip of the dfeault (false).
// IsAlsoClip returns the value for alsoclip or the default (false).
func IsAlsoClip(ctx context.Context) bool {
bv, ok := ctx.Value(ctxKeyAlsoClip).(bool)
if !ok {

View File

@@ -9,6 +9,7 @@ import (
"github.com/gopasspw/gopass/pkg/debug"
)
// DefaultAppdir is the default appdir for gopass.
var DefaultAppdir = New("gopass")
// Appdir is a helper struct to generate paths for config, cache and data dirs.
@@ -17,7 +18,9 @@ type Appdir struct {
name string
}
// New returns a new Appdir.
// New returns a new Appdir for the given application name.
// The name is used to construct the paths to the application's
// directories.
func New(name string) *Appdir {
return &Appdir{
name: name,
@@ -29,22 +32,26 @@ func (a *Appdir) Name() string {
return a.name
}
// UserConfig returns the users config dir.
// UserConfig returns the user's config dir for gopass.
// See a.UserConfig() for more details.
func UserConfig() string {
return DefaultAppdir.UserConfig()
}
// UserCache returns the users cache dir.
// UserCache returns the user's cache dir for gopass.
// See a.UserCache() for more details.
func UserCache() string {
return DefaultAppdir.UserCache()
}
// UserData returns the users data dir.
// UserData returns the user's data dir for gopass.
// See a.UserData() for more details.
func UserData() string {
return DefaultAppdir.UserData()
}
// UserHome returns the users home dir.
// UserHome returns the user's home dir.
// It respects the GOPASS_HOMEDIR environment variable.
func UserHome() string {
if hd := os.Getenv("GOPASS_HOMEDIR"); hd != "" {
return hd

View File

@@ -5,7 +5,9 @@ import (
"path/filepath"
)
// UserConfig returns the users config dir
// UserConfig returns the user's config directory.
// It uses the APPDATA environment variable on Windows.
// The GOPASS_HOMEDIR environment variable can be used to override the base path.
func (a *Appdir) UserConfig() string {
if hd := os.Getenv("GOPASS_HOMEDIR"); hd != "" {
return filepath.Join(hd, ".config", a.name)
@@ -14,7 +16,9 @@ func (a *Appdir) UserConfig() string {
return filepath.Join(os.Getenv("APPDATA"), a.name)
}
// UserCache returns the users cache dir
// UserCache returns the user's cache directory.
// It uses the LOCALAPPDATA environment variable on Windows.
// The GOPASS_HOMEDIR environment variable can be used to override the base path.
func (a *Appdir) UserCache() string {
if hd := os.Getenv("GOPASS_HOMEDIR"); hd != "" {
return filepath.Join(hd, ".cache", a.name)
@@ -23,7 +27,9 @@ func (a *Appdir) UserCache() string {
return filepath.Join(os.Getenv("LOCALAPPDATA"), a.name)
}
// UserData returns the users data dir
// UserData returns the user's data directory.
// It uses the LOCALAPPDATA environment variable on Windows.
// The GOPASS_HOMEDIR environment variable can be used to override the base path.
func (a *Appdir) UserData() string {
if hd := os.Getenv("GOPASS_HOMEDIR"); hd != "" {
return filepath.Join(hd, ".local", "share", a.name)

View File

@@ -10,7 +10,10 @@ import (
"github.com/gopasspw/gopass/pkg/debug"
)
// UserConfig returns the users config dir.
// UserConfig returns the user's config directory.
// It follows the XDG Base Directory Specification.
// The GOPASS_HOMEDIR environment variable can be used to override the base path.
// See: https://specifications.freedesktop.org/basedir-spec/basedir-spec-latest.html
func (a *Appdir) UserConfig() string {
if hd := os.Getenv("GOPASS_HOMEDIR"); hd != "" {
debug.V(3).Log("GOPASS_HOMEDIR is set to %s", hd)
@@ -26,7 +29,10 @@ func (a *Appdir) UserConfig() string {
return filepath.Join(base, a.name)
}
// UserCache returns the users cache dir.
// UserCache returns the user's cache directory.
// It follows the XDG Base Directory Specification.
// The GOPASS_HOMEDIR environment variable can be used to override the base path.
// See: https://specifications.freedesktop.org/basedir-spec/basedir-spec-latest.html
func (a *Appdir) UserCache() string {
if hd := os.Getenv("GOPASS_HOMEDIR"); hd != "" {
return filepath.Join(hd, ".cache", a.name)
@@ -40,7 +46,10 @@ func (a *Appdir) UserCache() string {
return filepath.Join(base, a.name)
}
// UserData returns the users data dir.
// UserData returns the user's data directory.
// It follows the XDG Base Directory Specification.
// The GOPASS_HOMEDIR environment variable can be used to override the base path.
// See: https://specifications.freedesktop.org/basedir-spec/basedir-spec-latest.html
func (a *Appdir) UserData() string {
if hd := os.Getenv("GOPASS_HOMEDIR"); hd != "" {
return filepath.Join(hd, ".local", "share", a.name)

View File

@@ -24,7 +24,8 @@ var (
)
// CopyTo copies the given data to the clipboard and enqueues automatic
// clearing of the clipboard.
// clearing of the clipboard. The name of the secret is passed for logging
// and notifications. The timeout is in seconds.
func CopyTo(ctx context.Context, name string, content []byte, timeout int) error {
debug.Log("Copying to clipboard: %s for %ds", name, timeout)

View File

@@ -40,7 +40,8 @@ const (
var ErrNoCallback = fmt.Errorf("no callback")
// WithGlobalFlags parses any global flags from the cli context and returns
// a regular context.
// a regular context. It handles the --yes flag and sets the appropriate
// context value.
func WithGlobalFlags(c *cli.Context) context.Context {
if c.Bool("yes") {
return WithAlwaysYes(c.Context, true)
@@ -49,10 +50,11 @@ func WithGlobalFlags(c *cli.Context) context.Context {
return c.Context
}
// ProgressCallback is a callback for updateing progress.
// ProgressCallback is a callback for updating progress.
type ProgressCallback func()
// WithTerminal returns a context with an explicit value for terminal.
// WithTerminal returns a context with an explicit value for whether or not we are
// in a terminal.
func WithTerminal(ctx context.Context, isTerm bool) context.Context {
return context.WithValue(ctx, ctxKeyTerminal, isTerm)
}
@@ -74,7 +76,8 @@ func IsTerminal(ctx context.Context) bool {
return bv
}
// WithInteractive returns a context with an explicit value for interactive.
// WithInteractive returns a context with an explicit value for whether or not we are
// in an interactive session.
func WithInteractive(ctx context.Context, isInteractive bool) context.Context {
return context.WithValue(ctx, ctxKeyInteractive, isInteractive)
}
@@ -121,6 +124,7 @@ func IsStdin(ctx context.Context) bool {
}
// WithShowParsing returns a context with the value for ShowParsing set.
// This is used to control whether to show parsing errors.
func WithShowParsing(ctx context.Context, bv bool) context.Context {
return context.WithValue(ctx, ctxKeyShowParsing, bv)
}
@@ -143,6 +147,7 @@ func IsShowParsing(ctx context.Context) bool {
}
// WithGitCommit returns a context with the value of git commit set.
// If true, changes will be committed to git.
func WithGitCommit(ctx context.Context, bv bool) context.Context {
return context.WithValue(ctx, ctxKeyGitCommit, bv)
}
@@ -159,7 +164,8 @@ func IsGitCommit(ctx context.Context) bool {
return is(ctx, ctxKeyGitCommit, true)
}
// IsFollowRef returns the value of follow-ref or the default (true).
// IsFollowRef returns the value of follow-ref or the default (false).
// If true, symlinks will be followed.
func IsFollowRef(ctx context.Context) bool {
return is(ctx, ctxFollowRef, false)
}
@@ -177,6 +183,7 @@ func WithFollowRef(ctx context.Context, bv bool) context.Context {
}
// WithAlwaysYes returns a context with the value of always yes set.
// If true, any prompts will be answered with yes.
func WithAlwaysYes(ctx context.Context, bv bool) context.Context {
return context.WithValue(ctx, ctxKeyAlwaysYes, bv)
}
@@ -221,7 +228,7 @@ func GetProgressCallback(ctx context.Context) ProgressCallback {
return cb
}
// WithAlias returns an context with the alias set.
// WithAlias returns a context with the alias set.
func WithAlias(ctx context.Context, alias string) context.Context {
return context.WithValue(ctx, ctxKeyAlias, alias)
}
@@ -242,6 +249,7 @@ func GetAlias(ctx context.Context) string {
}
// WithGitInit returns a context with the value for the git init flag set.
// If true, a git repository will be initialized.
func WithGitInit(ctx context.Context, bv bool) context.Context {
return context.WithValue(ctx, ctxKeyGitInit, bv)
}
@@ -251,12 +259,13 @@ func HasGitInit(ctx context.Context) bool {
return hasBool(ctx, ctxKeyGitInit)
}
// IsGitInit returns the value of the git init flag or ture if none was set.
// IsGitInit returns the value of the git init flag or true if none was set.
func IsGitInit(ctx context.Context) bool {
return is(ctx, ctxKeyGitInit, true)
}
// WithForce returns a context with the force flag set.
// If true, operations that would otherwise fail will be forced to succeed.
func WithForce(ctx context.Context, bv bool) context.Context {
return context.WithValue(ctx, ctxKeyForce, bv)
}
@@ -335,7 +344,7 @@ func GetCommitMessage(ctx context.Context) string {
return ht.head
}
// GetCommitMessageFull returns the set commit message (head+body, of either are defined) or an empty string.
// GetCommitMessageFull returns the set commit message (head+body, if either are defined) or an empty string.
func GetCommitMessageFull(ctx context.Context) string {
ht, ok := ctx.Value(ctxKeyCommitMessage).(*HeadedText)
if !ok {
@@ -346,6 +355,7 @@ func GetCommitMessageFull(ctx context.Context) string {
}
// WithNoNetwork returns a context with the value of no network set.
// If true, no network operations will be performed.
func WithNoNetwork(ctx context.Context, bv bool) context.Context {
return context.WithValue(ctx, ctxKeyNoNetwork, bv)
}
@@ -391,6 +401,7 @@ func GetEmail(ctx context.Context) string {
}
// WithImportFunc will return a context with the import callback set.
// The callback is used to ask the user for confirmation before importing a key.
func WithImportFunc(ctx context.Context, imf store.ImportCallback) context.Context {
return context.WithValue(ctx, ctxKeyImportFunc, imf)
}
@@ -435,6 +446,7 @@ func HasPasswordCallback(ctx context.Context) bool {
}
// GetPasswordCallback returns the password callback or a default (which always fails).
// The default callback returns ErrNoCallback.
func GetPasswordCallback(ctx context.Context) PasswordCallback {
pwcb, ok := ctx.Value(ctxKeyPasswordCallback).(PasswordCallback)
if !ok || pwcb == nil {
@@ -462,6 +474,7 @@ func HasPasswordPurgeCallback(ctx context.Context) bool {
}
// GetPasswordPurgeCallback returns the password purge callback or a default (which is a no-op).
// The default callback does nothing.
func GetPasswordPurgeCallback(ctx context.Context) PasswordPurgeCallback {
ppcb, ok := ctx.Value(ctxKeyPasswordPurgeCallback).(PasswordPurgeCallback)
if !ok || ppcb == nil {
@@ -473,6 +486,7 @@ func GetPasswordPurgeCallback(ctx context.Context) PasswordPurgeCallback {
// WithCommitTimestamp returns a context with the value for the commit
// timestamp set.
// This is used to allow for reproducible builds.
func WithCommitTimestamp(ctx context.Context, ts time.Time) context.Context {
return context.WithValue(ctx, ctxKeyCommitTimestamp, ts)
}
@@ -496,6 +510,7 @@ func GetCommitTimestamp(ctx context.Context) time.Time {
}
// WithHidden returns a context with the flag value for hidden set.
// This is used to hide secrets from the output.
func WithHidden(ctx context.Context, hidden bool) context.Context {
return context.WithValue(ctx, ctxKeyHidden, hidden)
}

View File

@@ -32,19 +32,23 @@ func is(ctx context.Context, key contextKey, def bool) bool {
return bv
}
// HeadedText is a helper struct for storing a commit message with a subject and body.
type HeadedText struct {
head string
body *strings.Builder
}
// SetHead sets the head of the text.
func (h *HeadedText) SetHead(s string) {
h.head = s
}
// GetHead returns the head of the text.
func (h *HeadedText) GetHead() string {
return h.head
}
// AddToBody adds a string to the body of the text.
func (h *HeadedText) AddToBody(s string) {
if h.body == nil {
var realBody strings.Builder
@@ -56,10 +60,12 @@ func (h *HeadedText) AddToBody(s string) {
(*h.body).WriteString("\n" + s)
}
// ClearBody clears the body of the text.
func (h *HeadedText) ClearBody() {
h.body = nil
}
// GetBody returns the body of the text.
func (h *HeadedText) GetBody() string {
if h.body == nil {
return ""
@@ -68,12 +74,14 @@ func (h *HeadedText) GetBody() string {
return (*h.body).String()
}
// HasBody returns true if the body of the text is not empty.
func (h *HeadedText) HasBody() bool {
ok := h.body != nil
return ok && (*h.body).Len() > 0
}
// GetText returns the full text, including the head and body.
func (h *HeadedText) GetText() string {
body := h.GetBody()
if body == "" && h.head == "" {

View File

@@ -34,6 +34,7 @@ var opts struct {
type v int
// V returns a logger at the given verbosity level.
// The higher the number, the more verbose the logging.
func V(n int) v {
return v(n)
}
@@ -211,12 +212,16 @@ func checkFilter(filter map[string]bool, key string) bool {
// Log logs a statement to Stderr (unless filtered) and the
// debug log file (if enabled), but only if the verbosity
// level is greater or equal to the given level.
//
// This is a no-op if the verbosity level is not high enough.
func (n v) Log(f string, args ...any) {
logFn(int(n), 0, f, args...)
}
// Log logs a statement to Stderr (unless filtered) and the
// debug log file (if enabled).
//
// This is a no-op if debugging is not enabled.
func Log(f string, args ...any) {
logFn(0, 0, f, args...)
}
@@ -224,6 +229,8 @@ func Log(f string, args ...any) {
// LogN logs a statement to Stderr (unless filtered) and the
// debug log file (if enabled). The offset will be applied to
// the runtime position.
//
// This is a no-op if debugging is not enabled.
func LogN(offset int, f string, args ...any) {
logFn(0, offset, f, args...)
}
@@ -289,6 +296,7 @@ func doLog(verbosity, offset int, f string, args ...any) {
}
// IsEnabled returns true if debug logging was enabled.
// This is useful to avoid expensive computations if debugging is not enabled.
func IsEnabled() bool {
return enabled
}

View File

@@ -9,7 +9,8 @@ import (
var biFunc func() (*rdebug.BuildInfo, bool) = rdebug.ReadBuildInfo
// ModuleVersion the version of the named import.
// ModuleVersion returns the version of the named module.
// It reads the build info and parses the version of the requested module.
func ModuleVersion(m string) semver.Version {
bi, ok := biFunc()
if !ok || bi == nil {

View File

@@ -19,8 +19,8 @@ import (
var reCleanFilename = regexp.MustCompile(`[^\w\d@.-]`)
// CleanFilename strips all possibly suspicious characters from a filename
// WARNING: NOT suiteable for pathnames as slashes will be stripped as well!
// CleanFilename strips all possibly suspicious characters from a filename.
// WARNING: NOT suitable for pathnames as slashes will be stripped as well!
func CleanFilename(in string) string {
return strings.Trim(reCleanFilename.ReplaceAllString(in, "_"), "_ ")
}
@@ -40,6 +40,7 @@ func ExpandHomedir(path string) string {
}
// CleanPath resolves common aliases in a path and cleans it as much as possible.
// It expands the tilde to the user's home directory and resolves relative paths.
func CleanPath(path string) string {
// Replace ~ with GOPASS_HOMEDIR if set (mainly for testing and experiments),
// otherwise replace ~ with user's homedir if set. We expect any reference
@@ -62,7 +63,6 @@ func CleanPath(path string) string {
}
// IsDir checks if a certain path exists and is a directory.
// https://stackoverflow.com/questions/10510691/how-to-check-whether-a-file-or-directory-denoted-by-a-path-exists-in-golang
func IsDir(path string) bool {
fi, err := os.Stat(path)
if err != nil {
@@ -141,7 +141,8 @@ func IsEmptyDir(path string) (bool, error) {
return empty, nil
}
// Shred overwrite the given file any number of times.
// Shred overwrites the given file with random data and deletes it.
// The file is overwritten `runs` times. The last run is with zeros.
func Shred(path string, runs int) error {
fh, err := os.OpenFile(path, os.O_WRONLY, 0o600)
if err != nil {
@@ -278,7 +279,7 @@ func CopyFile(from, to string) error {
}
// CopyFileForce copies a file from src to dst. Permissions will be preserved. The destination
// if removed before copying to avoid permission issues.
// is removed before copying to avoid permission issues.
func CopyFileForce(from, to string) error {
if IsFile(to) {
if err := os.Remove(to); err != nil {

View File

@@ -5,7 +5,9 @@ import (
"strconv"
)
// Umask extracts the umask from env.
// Umask extracts the umask from the environment variables.
// It checks for GOPASS_UMASK and PASSWORD_STORE_UMASK.
// If neither is set, it returns the default umask of 0o77.
func Umask() int {
for _, en := range []string{"GOPASS_UMASK", "PASSWORD_STORE_UMASK"} {
um := os.Getenv(en)

View File

@@ -18,7 +18,7 @@ import (
"github.com/gopasspw/gopass/pkg/gopass"
)
// Gopass is a secret store implementation.
// Gopass is a secret store implementation. It is the main entry point for the gopass API.
type Gopass struct {
rs *root.Store
}
@@ -26,15 +26,17 @@ type Gopass struct {
// make sure that *Gopass implements Store.
var _ gopass.Store = &Gopass{}
// ErrNotImplemented is returned when a method is not implemented.
// ErrNotImplemented is returned when a method is not yet implemented.
var ErrNotImplemented = fmt.Errorf("not yet implemented")
// ErrNotInitialized is returned when the store is not initialized.
// This happens when the gopass CLI has not been used to set up a password store yet.
var ErrNotInitialized = fmt.Errorf("password store not initialized. run 'gopass setup' first")
// New initializes an existing password store. It will attempt to load an existing
// configuration or use the built-in defaults. If no password store is found and
// the user will need to initialize it with the gopass CLI (`gopass setup`) first.
// New initializes an existing password store and returns a new Gopass instance.
// It will attempt to load an existing configuration or use the built-in defaults.
// If no password store is found the user will need to initialize it with the gopass
// CLI (`gopass setup`) first.
//
// WARNING: This will need to change to accommodate for runtime configuration.
func New(ctx context.Context) (*Gopass, error) {
@@ -55,19 +57,21 @@ func New(ctx context.Context) (*Gopass, error) {
}, nil
}
// List returns a list of all secrets.
// List returns a list of all secret names.
func (g *Gopass) List(ctx context.Context) ([]string, error) {
return g.rs.List(ctx, tree.INF) //nolint:wrapcheck
}
// Get returns a single, encrypted secret. It must be unwrapped before use.
// Use "latest" to get the latest revision.
// The revision parameter is not yet implemented.
func (g *Gopass) Get(ctx context.Context, name, revision string) (gopass.Secret, error) {
return g.rs.Get(ctx, name) //nolint:wrapcheck
}
// Set adds a new revision to an existing secret or creates a new one.
// Set adds a new revision to an existing secret or creates a new one if it doesn't exist.
// Create new secrets with secrets.New().
// The secret is passed as a gopass.Byter, which is an interface that can be satisfied by a []byte.
func (g *Gopass) Set(ctx context.Context, name string, sec gopass.Byter) error {
return g.rs.Set(ctx, name, sec) //nolint:wrapcheck
}
@@ -82,28 +86,31 @@ func (g *Gopass) RemoveAll(ctx context.Context, prefix string) error {
return g.rs.Prune(ctx, prefix) //nolint:wrapcheck
}
// Rename move a prefix to another.
// Rename moves a secret from one path to another.
func (g *Gopass) Rename(ctx context.Context, src, dest string) error {
return g.rs.Move(ctx, src, dest) //nolint:wrapcheck
}
// Sync synchronizes a secret with a remote.
// Sync synchronizes the store with a remote.
// Not yet implemented.
func (g *Gopass) Sync(ctx context.Context) error {
return ErrNotImplemented
}
// Revisions lists all revisions of this secret.
// Not yet implemented.
func (g *Gopass) Revisions(ctx context.Context, name string) ([]string, error) {
return nil, ErrNotImplemented
}
// String returns the name of this store.
func (g *Gopass) String() string {
return "gopass"
}
// Close shuts down all background processes.
// Close shuts down all background processes and closes the store.
//
// MUST be called before existing to make sure any background processing
// MUST be called before exiting to make sure any background processing
// (e.g. pending commits or pushes) are complete. Failing to do so might
// result in an invalid password store state.
func (g *Gopass) Close(ctx context.Context) error {

View File

@@ -30,28 +30,29 @@ type MockAPI struct {
}
// New creates a new gopass API mock.
// It uses an in-memory store.
func New() *MockAPI {
return &MockAPI{
store: mockstore.New(""),
}
}
// String returns mockapi.
// String returns the name of the mock API.
func (a *MockAPI) String() string {
return "mockapi"
}
// List does nothing.
// List returns a list of all secrets in the mock store.
func (a *MockAPI) List(ctx context.Context) ([]string, error) {
return a.store.List(ctx, "") //nolint:wrapcheck
}
// Get does nothing.
// Get returns a secret from the mock store.
func (a *MockAPI) Get(ctx context.Context, name, _ string) (gopass.Secret, error) {
return a.store.Get(ctx, name) //nolint:wrapcheck
}
// Revisions does nothing.
// Revisions returns a list of all revisions of a secret in the mock store.
func (a *MockAPI) Revisions(ctx context.Context, name string) ([]string, error) {
rs, err := a.store.ListRevisions(ctx, name)
if err != nil {
@@ -66,22 +67,22 @@ func (a *MockAPI) Revisions(ctx context.Context, name string) ([]string, error)
return revs, nil
}
// Set does nothing.
// Set sets a secret in the mock store.
func (a *MockAPI) Set(ctx context.Context, name string, sec gopass.Byter) error {
return a.store.Set(ctx, name, sec) //nolint:wrapcheck
}
// Remove does nothing.
// Remove removes a secret from the mock store.
func (a *MockAPI) Remove(ctx context.Context, name string) error {
return a.store.Delete(ctx, name) //nolint:wrapcheck
}
// RemoveAll does nothing.
// RemoveAll removes all secrets with a given prefix from the mock store.
func (a *MockAPI) RemoveAll(ctx context.Context, prefix string) error {
return a.store.Prune(ctx, prefix) //nolint:wrapcheck
}
// Rename does nothing.
// Rename moves a secret in the mock store.
func (a *MockAPI) Rename(ctx context.Context, src, dest string) error {
return a.store.Move(ctx, src, dest) //nolint:wrapcheck
}

View File

@@ -17,6 +17,9 @@ var (
)
// AKV is the new Key-Value implementation that will replace KV.
// It stores a password and a set of key-value pairs.
// The raw content is stored in a strings.Builder, which is a more
// efficient way to build strings.
type AKV struct {
password string
kvp map[string][]string
@@ -26,7 +29,7 @@ type AKV struct {
fromMime bool
}
// NewAKV creates a new AKV instances.
// NewAKV creates a new, empty AKV instance.
func NewAKV() *AKV {
a := &AKV{
kvp: make(map[string][]string),
@@ -36,7 +39,7 @@ func NewAKV() *AKV {
return a
}
// NewAKVWithData returns a new KV secret populated with data.
// NewAKVWithData returns a new AKV secret populated with data.
func NewAKVWithData(pw string, kvps map[string][]string, body string, converted bool) *AKV {
kv := NewAKV()
kv.SetPassword(pw)
@@ -54,17 +57,17 @@ func NewAKVWithData(pw string, kvps map[string][]string, body string, converted
return kv
}
// Bytes returns the raw string as bytes.
// Bytes returns the raw content of the secret as a byte slice.
func (a *AKV) Bytes() []byte {
return []byte(a.raw.String())
}
// Keys returns all the parsed keys.
// Keys returns a sorted list of all keys in the secret.
func (a *AKV) Keys() []string {
return set.SortedKeys(a.kvp)
}
// Get returns the value of the requested key, if found.
// Get returns the first value for a given key, if found.
func (a *AKV) Get(key string) (string, bool) {
if v, found := a.kvp[key]; found {
return v[0], true
@@ -73,16 +76,15 @@ func (a *AKV) Get(key string) (string, bool) {
return "", false
}
// Values returns all values for that key.
// Values returns all values for a given key.
func (a *AKV) Values(key string) ([]string, bool) {
v, found := a.kvp[key]
return v, found
}
// Ref returns reference in case of having password of the
// gopass://ref
// which references another secret in the store.
// Ref returns a reference to another secret if the password is a gopass URI.
// A gopass URI is a string that starts with "gopass://".
func (a *AKV) Ref() (string, bool) {
if strings.HasPrefix(a.password, gopassRef) {
return strings.TrimPrefix(a.password, gopassRef), true
@@ -91,7 +93,8 @@ func (a *AKV) Ref() (string, bool) {
return "", false
}
// Set writes a single key.
// Set sets the value for a given key, replacing the first instance if it exists.
// If the key does not exist, it is added to the end of the secret.
func (a *AKV) Set(key string, value any) error {
// if it's new key we can just append it at the end
if _, found := a.kvp[key]; !found {
@@ -155,7 +158,7 @@ func (a *AKV) Set(key string, value any) error {
return nil
}
// Add appends data to a given key.
// Add adds a new value for a given key.
func (a *AKV) Add(key string, value any) error {
sv := fmt.Sprintf("%s", value)
a.kvp[key] = append(a.kvp[key], sv)
@@ -169,7 +172,7 @@ func (a *AKV) Add(key string, value any) error {
return nil
}
// Del removes a given key and all of its values.
// Del removes a key and all of its values.
func (a *AKV) Del(key string) bool {
_, found := a.kvp[key]
if !found {
@@ -218,12 +221,12 @@ func (a *AKV) Del(key string) bool {
return true
}
// Password returns the password.
// Password returns the password of the secret.
func (a *AKV) Password() string {
return a.password
}
// SetPassword updates the password.
// SetPassword updates the password of the secret.
func (a *AKV) SetPassword(p string) {
s := newScanner(strings.NewReader(a.raw.String()), a.raw.Len())
a.raw = strings.Builder{}
@@ -247,7 +250,7 @@ func (a *AKV) SetPassword(p string) {
}
}
// ParseAKV tries to parse an AKV secret.
// ParseAKV parses a byte slice and returns a new AKV instance.
func ParseAKV(in []byte) *AKV {
a := NewAKV()
a.raw = strings.Builder{}
@@ -291,7 +294,8 @@ func ParseAKV(in []byte) *AKV {
return a
}
// Body returns the body.
// Body returns the body of the secret, which is the content of the secret
// without the password and the key-value pairs.
func (a *AKV) Body() string {
out := strings.Builder{}
@@ -346,7 +350,7 @@ func newScanner(in io.Reader, inSize int) *bufio.Scanner {
return s
}
// Write appends the buffer to the secret's body.
// Write appends data to the secret's body.
func (a *AKV) Write(buf []byte) (int, error) {
var w io.Writer
w = &a.raw
@@ -365,26 +369,27 @@ func (a *AKV) Write(buf []byte) (int, error) {
return w.Write(buf)
}
// FromMime returns whether this secret was converted from a Mime secret of not.
// FromMime returns whether this secret was converted from a MIME secret.
func (a *AKV) FromMime() bool {
return a.fromMime
}
// SafeStr always returnes "(elided)".
// SafeStr always returns "(elided)" to avoid leaking sensitive information.
func (a *AKV) SafeStr() string {
return "(elided)"
}
// pwWriter is a io.Writer that will extract the first line of the input stream and
// then write it to the password field of the provided callback. The first line can stretch
// multiple chunks but once the first line has been completed any writes to this
// writer will be silently discarded.
// pwWriter is an io.Writer that extracts the first line of the input stream
// and writes it to the password field of the provided callback. The first
// line can stretch multiple chunks, but once the first line has been
// completed, any subsequent writes to this writer will be silently discarded.
type pwWriter struct {
w io.Writer
cb func(string)
buf strings.Builder
}
// Write implements the io.Writer interface for pwWriter.
func (p *pwWriter) Write(buf []byte) (int, error) {
n, err := p.w.Write(buf)
if p.cb == nil {

View File

@@ -1,10 +1,11 @@
package secrets
// PermanentError signal that parsing should not attempt other formats.
// PermanentError signals that parsing should not attempt other formats.
type PermanentError struct {
Err error
}
// Error returns the underlying error.
func (p *PermanentError) Error() string {
return p.Err.Error()
}

View File

@@ -2,5 +2,6 @@ package secrets
const (
// Ident is the header of the deprecated Gopass MIME secret.
// It is used to identify a secret as a gopass secret.
Ident = "GOPASS-SECRET-1.0"
)

View File

@@ -5,6 +5,7 @@ import (
)
// New creates a new secret.
// It returns a new AKV secret.
func New() gopass.Secret { //nolint:ireturn
return NewAKV()
}

View File

@@ -13,6 +13,7 @@ import (
)
// ErrUnknown is returned when the secret is not recognized.
// This is a sentinel error.
var ErrUnknown = fmt.Errorf("unknown secrets type")
// parseLegacyMIME is a fallback parser for the transient MIME format.

View File

@@ -11,8 +11,11 @@ import (
"github.com/gopasspw/gopass/pkg/gopass/secrets"
)
// Parse tries to parse a secret. It will start with the most specific
// secrets type.
// Parse tries to parse a secret from a byte slice. It attempts to parse the
// secret in the following order: legacy MIME, YAML, and finally AKV.
// If parsing as legacy MIME or YAML fails, it falls back to the next format.
// If a permanent error is encountered while parsing as legacy MIME, it returns
// the error immediately.
//
//nolint:ireturn
func Parse(in []byte) (gopass.Secret, error) {
@@ -49,7 +52,8 @@ func Parse(in []byte) (gopass.Secret, error) {
return s, nil
}
// MustParse parses a secret or panics. Should only be used for tests.
// MustParse parses a secret from a string or panics if an error occurs.
// This function should only be used for testing purposes.
func MustParse(in string) gopass.Secret {
sec, err := Parse([]byte(in))
if err != nil {

View File

@@ -31,7 +31,7 @@ var ErrNotSupported = fmt.Errorf("not supported")
// is neither trivial nor intuitive for users manually editing secrets (e.g.
// unquoted phone numbers being parsed as octal and such).
//
// Format
// Format:
// ------
// Line | Description
//
@@ -45,12 +45,13 @@ type YAML struct {
body string
}
// Keys returns all keys.
// Keys returns all keys from the YAML data.
func (y *YAML) Keys() []string {
return set.SortedKeys(y.data)
}
// Get returns the first value of a single key.
// It supports yamlpath expressions.
func (y *YAML) Get(key string) (string, bool) {
if y.data == nil {
y.data = make(map[string]any)
@@ -67,7 +68,8 @@ func (y *YAML) Get(key string) (string, bool) {
return "", false
}
// Values returns Get since as per YAML specification keys must be unique.
// Values returns all values for a given key.
// Since YAML keys are unique, this is equivalent to Get.
func (y *YAML) Values(key string) ([]string, bool) {
data, found := y.Get(key)
@@ -85,9 +87,8 @@ func (y *YAML) Set(key string, value any) error {
return nil
}
// Ref returns reference in case of having password of the
// gopass://ref
// which references another secret in the store.
// Ref returns a reference in case the password is of the form
// gopass://ref, which references another secret in the store.
func (y *YAML) Ref() (string, bool) {
if strings.HasPrefix(y.password, gopassRef) {
return strings.TrimPrefix(y.password, gopassRef), true
@@ -96,12 +97,12 @@ func (y *YAML) Ref() (string, bool) {
return "", false
}
// Add doesn't work since as per YAML specification keys must be unique.
// Add is not supported for YAML secrets, as keys must be unique.
func (y *YAML) Add(key string, value any) error {
return ErrNotSupported
}
// Del removes a single key.
// Del removes a single key from the YAML data.
func (y *YAML) Del(key string) bool {
_, found := y.data[key]
@@ -110,7 +111,7 @@ func (y *YAML) Del(key string) bool {
return found
}
// ParseYAML will try to parse a YAML secret.
// ParseYAML will try to parse a YAML secret from a byte slice.
func ParseYAML(in []byte) (*YAML, error) {
y := &YAML{
data: make(map[string]any, 10),
@@ -145,7 +146,7 @@ func ParseYAML(in []byte) (*YAML, error) {
return y, nil
}
// Body returns the body.
// Body returns the body of the secret.
func (y *YAML) Body() string {
return y.body
}
@@ -194,7 +195,7 @@ func parseBody(r *bufio.Reader) (string, error) {
return "", ErrNoYAML
}
// Bytes serialized this secret.
// Bytes serializes this secret to a byte slice.
func (y *YAML) Bytes() []byte {
defer func() {
if r := recover(); r != nil {
@@ -232,7 +233,7 @@ func (y *YAML) Write(buf []byte) (int, error) {
return len(buf), nil
}
// SafeStr always returnes "(elided)".
// SafeStr always returns "(elided)".
func (y *YAML) SafeStr() string {
return "(elided)"
}

View File

@@ -1,3 +1,4 @@
// Package gopass provides the interfaces for the gopass password store.
package gopass
import (
@@ -5,60 +6,64 @@ import (
"fmt"
)
// Byter is a minimal secrets write interface.
// Byter is a minimal secrets write interface. It is used for writing secrets to the store.
type Byter interface {
Bytes() []byte
}
// Secret is a secret type.
// Secret is a secret type that represents a single secret in the store.
type Secret interface {
Byter
// Keys returns a list of all keys in the secret's metadata.
Keys() []string
// Get returns a single value for that key, use Password() to get the password value.
// Get returns a single value for a given key from the secret's metadata.
Get(key string) (string, bool)
// Values returns all values for that key, use Password() to get the password value.
// Values returns all values for a given key from the secret's metadata.
Values(key string) ([]string, bool)
// Set sets a single header value, use SetPassword() to set the password value.
// Set sets a single value for a given key in the secret's metadata.
Set(key string, value any) error
// Add appends the value to that key, use SetPassword() to set the password value.
// Add adds a new value for a given key in the secret's metadata.
Add(key string, value any) error
// Del removes a single header value
// Del removes a key from the secret's metadata.
Del(key string) bool
// Ref returns reference in case of having password of the
// gopass://ref
// which references another secret in the store.
// Ref returns a reference to another secret if the secret is a reference.
// A reference is a secret that contains a gopass:// URI.
Ref() (string, bool)
// GetBody returns everything except the header. Use Bytes to get everything.
// Body returns the body of the secret, which is everything except the password and metadata.
Body() string
// Password returns the password of the secret.
Password() string
// SetPassword sets the password of the secret.
SetPassword(string)
}
// Store is a secret store.
// Store is a secret store that provides methods for interacting with the password store.
type Store interface {
fmt.Stringer
// List all secrets.
// List lists all secrets in the store.
List(context.Context) ([]string, error)
// Get an decrypted secret. Revision defaults to "latest".
// Get returns a decrypted secret from the store.
// The revision parameter defaults to "latest".
Get(ctx context.Context, name, revision string) (Secret, error)
// Set (add) a new revision of an secret
// Set creates or updates a secret in the store.
Set(ctx context.Context, name string, sec Byter) error
// Revisions return a list of revisions for a secret.
// Revisions returns a list of all revisions of a secret.
Revisions(ctx context.Context, name string) ([]string, error)
// Remove a single secret.
// Remove removes a single secret from the store.
Remove(ctx context.Context, name string) error
// RemoveAll secrets with a common prefix.
// RemoveAll removes all secrets with a common prefix from the store.
RemoveAll(ctx context.Context, prefix string) error
// Rename a path (secret of prefix) without decrypting.
// Rename renames a secret or a folder of secrets.
Rename(ctx context.Context, src, dest string) error
// Sync with a remote (if configured)
// NOTE: We will always auto-sync when mutating the store. Use this to
// manually pull in changes.
// Sync synchronizes the store with a remote.
// NOTE: The store is automatically synchronized when mutated.
// This method can be used to manually trigger a synchronization.
Sync(ctx context.Context) error
// Clean up any resources. MUST be called before the process exists.
// Close closes the store and cleans up any resources.
// It MUST be called before the program exits.
Close(ctx context.Context) error
}

View File

@@ -15,7 +15,9 @@ import (
"github.com/pquerna/otp"
)
// Calculate will compute a OTP code from a given secret.
// Calculate will compute an OTP code from a given secret.
// It will look for a field named "otpauth" or "totp" or "hotp".
// If none is found it will fall back to the password.
//
//nolint:ireturn
func Calculate(name string, sec gopass.Secret) (*otp.Key, error) {
@@ -89,7 +91,7 @@ func parseOTP(typ string, secKey string) (*otp.Key, error) {
return key, nil
}
// WriteQRFile writes the given OTP code as a QR image to disk.
// WriteQRFile writes the given OTP key as a QR image to disk.
func WriteQRFile(key *otp.Key, file string) error {
// Convert TOTP key into a QR code encoded as a PNG image.
var buf bytes.Buffer

View File

@@ -14,7 +14,8 @@ import (
"fmt"
)
// CredentialFlags for the credential parameters: https://www.w3.org/TR/webauthn-2/#flags
// CredentialFlags for the credential parameters.
// See: https://www.w3.org/TR/webauthn-2/#flags
type CredentialFlags struct {
UserPresent bool
UserVerified bool
@@ -33,14 +34,16 @@ type Credential struct {
Flags CredentialFlags
}
// ClientData for signature: https://www.w3.org/TR/webauthn-1/#sec-client-data
// ClientData for signature.
// See: https://www.w3.org/TR/webauthn-1/#sec-client-data
type ClientData struct {
Challenge string `json:"challenge"`
Origin string `json:"origin"`
CredType string `json:"type"`
}
// Response from a GetAssertion: https://www.w3.org/TR/webauthn-1/#authenticatorGetAssertion-return-values
// Response from a GetAssertion.
// See: https://www.w3.org/TR/webauthn-1/#authenticatorGetAssertion-return-values
type Response struct {
AuthenticatorData []byte `json:"authdata"`
ClientDataJSON []byte `json:"client_data_json"`
@@ -70,7 +73,8 @@ func authDataFlags(options CredentialFlags) uint8 {
return flags
}
// CreateCredential is an implementation of the authenticatorMakeCredential Operation: https://www.w3.org/TR/webauthn-2/#sctn-op-make-cred
// CreateCredential is an implementation of the authenticatorMakeCredential Operation.
// See: https://www.w3.org/TR/webauthn-2/#sctn-op-make-cred
func CreateCredential(rp string, user string, flags CredentialFlags) (*Credential, error) {
rawID := make([]byte, 32)
_, err := rand.Read(rawID)
@@ -90,7 +94,8 @@ func CreateCredential(rp string, user string, flags CredentialFlags) (*Credentia
}, nil
}
// GetAssertion is an implementation of the authenticatorGetAssertion Operation: https://www.w3.org/TR/webauthn-2/#authenticatorgetassertion
// GetAssertion is an implementation of the authenticatorGetAssertion Operation.
// See: https://www.w3.org/TR/webauthn-2/#authenticatorgetassertion
func (cred *Credential) GetAssertion(challenge string, origin string) (*Response, error) {
credType := "webauthn.get"
clientData := ClientData{

View File

@@ -11,7 +11,7 @@ import (
"github.com/gopasspw/gopass/pkg/termio"
)
// Client is pinentry CLI drop-in.
// Client is a pinentry CLI drop-in.
type Client struct {
repeat bool
}
@@ -35,7 +35,7 @@ func (c *Client) Option(string) error {
return nil
}
// GetPINContext prompts for the pin in the termnial and returns the output.
// GetPINContext prompts for the pin in the terminal and returns the output.
// The context is only used for tests.
func (c *Client) GetPINContext(ctx context.Context) (string, error) {
pw, err := termio.AskForPassword(ctx, "your PIN", c.repeat)
@@ -46,7 +46,7 @@ func (c *Client) GetPINContext(ctx context.Context) (string, error) {
return pw, nil
}
// GetPIN prompts for the pin in the termnial and returns the output.
// GetPIN prompts for the pin in the terminal and returns the output.
func (c *Client) GetPIN() (string, error) {
return c.GetPINContext(context.TODO())
}

View File

@@ -10,6 +10,7 @@
package protect
// ProtectEnabled lets us know if we have protection or not.
// It is false on all systems except OpenBSD.
var ProtectEnabled = false
// Pledge on any other system than OpenBSD doesn't do anything.

View File

@@ -5,7 +5,8 @@ package protect
import "golang.org/x/sys/unix"
// ProtectEnabled lets us know if we have protection or not
// ProtectEnabled lets us know if we have protection or not.
// It is true on OpenBSD.
var ProtectEnabled = true
// Pledge on OpenBSD lets us "promise" to only run a subset of

View File

@@ -17,6 +17,9 @@ var ErrCrypticInvalid = fmt.Errorf("password does not satisfy all validators")
// Cryptic is a generator for hard-to-remember passwords as required by (too)
// many sites. Prefer memorable or xkcd-style passwords, if possible.
//
// The generator can be configured with a character set, a length, a maximum
// number of tries, and a list of validators.
type Cryptic struct {
Chars string
Length int
@@ -25,6 +28,8 @@ type Cryptic struct {
}
// NewCryptic creates a new generator with sane defaults.
// The default length is 16, and the default character set is digits, upper and lower case letters.
// If symbols is true, the symbol character set is added.
func NewCryptic(length int, symbols bool) *Cryptic {
if length < 1 {
length = 16
@@ -45,6 +50,7 @@ func NewCryptic(length int, symbols bool) *Cryptic {
// NewCrypticForDomain tries to look up password rules for the given domain
// or uses the default generator.
// It will adjust the length and character set of the generator based on the rules.
func NewCrypticForDomain(ctx context.Context, length int, domain string) *Cryptic {
c := NewCryptic(length, true)
r, found := pwrules.LookupRule(ctx, domain)
@@ -149,6 +155,7 @@ func uniqueChars(in string) string {
// NewCrypticWithAllClasses returns a password generator that generates passwords
// containing all available character classes.
// This is useful for password policies that require a mix of character types.
func NewCrypticWithAllClasses(length int, symbols bool) *Cryptic {
c := NewCryptic(length, symbols)
c.Validators = append(c.Validators, func(pw string) error {
@@ -162,7 +169,7 @@ func NewCrypticWithAllClasses(length int, symbols bool) *Cryptic {
return c
}
// NewCrypticWithCrunchy returns a password generators that only returns a
// NewCrypticWithCrunchy returns a password generator that only returns a
// password if it's successfully validated with crunchy.
func NewCrypticWithCrunchy(length int, symbols bool) *Cryptic {
c := NewCryptic(length, symbols)
@@ -174,6 +181,8 @@ func NewCrypticWithCrunchy(length int, symbols bool) *Cryptic {
}
// Password returns a single password from the generator.
// It will try to generate a password that satisfies all validators.
// If it fails after MaxTries, it will return an empty string.
func (c *Cryptic) Password() string {
round := 0
maxFn := func() bool {

View File

@@ -18,7 +18,8 @@ var (
)
// GenerateExternal will invoke an external password generator,
// if set, and return it's output.
// if set, and return its output.
// The external generator is configured via the GOPASS_EXTERNAL_PWGEN environment variable.
func GenerateExternal(pwlen int) (string, error) {
c := os.Getenv("GOPASS_EXTERNAL_PWGEN")
if c == "" {

View File

@@ -4,6 +4,9 @@ import "strings"
// GenerateMemorablePassword will generate a memorable password
// with a minimum length.
// It will use a wordlist to generate the password.
// If symbols is true, it will add symbols to the password.
// If capitals is true, it will capitalize some words.
func GenerateMemorablePassword(minLength int, symbols bool, capitals bool) string {
var sb strings.Builder

View File

@@ -16,11 +16,16 @@ var ErrMaxTries = fmt.Errorf("maximum tries exceeded")
// Character classes.
const (
// Digits is the class of digits.
Digits = "0123456789"
Upper = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
Lower = "abcdefghijklmnopqrstuvwxyz"
Syms = "!\"#$%&'()*+,-./:;<=>?@[\\]^_`{|}~"
Ambiq = "0ODQ1IlB8G6S5Z2"
// Upper is the class of upper case letters.
Upper = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
// Lower is the class of lower case letters.
Lower = "abcdefghijklmnopqrstuvwxyz"
// Syms is the class of symbols.
Syms = "!\"#$%&'()*+,-./:;<=>?@[\\]^_`{|}~"
// Ambiq is the class of ambiguous characters.
Ambiq = "0ODQ1IlB8G6S5Z2"
// CharAlpha is the class of letters.
CharAlpha = Upper + Lower
// CharAlphaNum is the class of alpha-numeric characters.
@@ -29,7 +34,7 @@ const (
CharAll = Digits + Upper + Lower + Syms
)
// GeneratePassword generates a random, hard to remember password.
// GeneratePassword generates a random, hard-to-remember password.
func GeneratePassword(length int, symbols bool) string {
chars := Digits + Upper + Lower
if symbols {
@@ -45,6 +50,7 @@ func GeneratePassword(length int, symbols bool) string {
// GeneratePasswordCharset generates a random password from a given
// set of characters.
// It does not perform any checks on the generated password.
func GeneratePasswordCharset(length int, chars string) string {
c := NewCryptic(length, false)
c.Chars = chars
@@ -56,6 +62,8 @@ func GeneratePasswordCharset(length int, chars string) string {
// contains all character classes instead of only enabling them.
// This is especially useful for broken (corporate) password policies
// that mandate the use of certain character classes for no good reason.
// It will try to generate a password that contains at least one character from each of the
// enabled character classes (digits, upper, lower, symbols).
func GeneratePasswordWithAllClasses(length int, symbols bool) (string, error) {
c := NewCrypticWithAllClasses(length, symbols)
if pw := c.Password(); pw != "" {
@@ -67,6 +75,7 @@ func GeneratePasswordWithAllClasses(length int, symbols bool) (string, error) {
// GeneratePasswordCharsetCheck generates a random password from a given
// set of characters and validates the generated password with crunchy.
// It will try to generate a password that passes the crunchy check.
func GeneratePasswordCharsetCheck(length int, chars string) string {
c := NewCrypticWithCrunchy(length, false)
c.Chars = chars
@@ -74,7 +83,7 @@ func GeneratePasswordCharsetCheck(length int, chars string) string {
return c.Password()
}
// Prune removes all characters in cutset from the input.
// Prune removes all characters in cutset from the input string.
func Prune(in string, cutset string) string {
out := make([]rune, 0, len(in))

View File

@@ -11,6 +11,7 @@ import (
)
// LookupAliases looks up known aliases for the given domain.
// It will check for both built-in and custom aliases.
func LookupAliases(ctx context.Context, domain string) []string {
customAliases := loadCustomAliases(ctx)
aliases := make([]string, 0, len(genAliases[domain])+len(customAliases[domain]))
@@ -21,7 +22,7 @@ func LookupAliases(ctx context.Context, domain string) []string {
return aliases
}
// AllAliases returns all aliases.
// AllAliases returns all aliases, including built-in and custom aliases.
func AllAliases(ctx context.Context) map[string][]string {
customAliases := loadCustomAliases(ctx)
all := make(map[string][]string, len(genAliases)+len(customAliases))

View File

@@ -16,7 +16,7 @@ func init() {
}
// LookupChangeURL looks up a change URL, either directly or through
// one of it's know aliases.
// one of its known aliases.
func LookupChangeURL(ctx context.Context, domain string) string {
if u, found := changeURLs[domain]; found {
return u

View File

@@ -14,12 +14,12 @@ import (
var reChars = regexp.MustCompile(`(allowed|required):\s*\[(.*)\](?:;|,)`)
// AllRules returns all rules.
// AllRules returns all password rules.
func AllRules() map[string]Rule {
return genRules
}
// LookupRule looks up a rule either directly or through one of it's know
// LookupRule looks up a rule either directly or through one of its known
// aliases.
func LookupRule(ctx context.Context, domain string) (Rule, bool) {
r, found := genRules[domain]
@@ -36,7 +36,8 @@ func LookupRule(ctx context.Context, domain string) (Rule, bool) {
return Rule{}, false
}
// Rule is a password rule as defined by Apple at https://developer.apple.com/password-rules/
// Rule is a password rule as defined by Apple.
// See: https://developer.apple.com/password-rules/
type Rule struct {
Minlen int
Maxlen int

View File

@@ -15,7 +15,7 @@ func Random() string {
return password
}
// RandomLength returns a random passphrase combined from the desired number.
// RandomLength returns a random passphrase combined from the desired number
// of words. Words are drawn from lang.
func RandomLength(length int, lang string) (string, error) {
return RandomLengthDelim(length, " ", lang, false, false)

View File

@@ -18,8 +18,8 @@ const (
// ErrUnknowColor is returned when the color is unknown.
var ErrUnknowColor = fmt.Errorf("unknown color")
// QRCode returns a string containing an ANSI encoded
// QR Code.
// QRCode returns a string containing an ANSI encoded QR Code.
// This can be printed to a terminal that supports ANSI escape codes.
func QRCode(content string) (string, error) {
q, err := qrcode.New(content, qrcode.Medium)
if err != nil {

View File

@@ -1,6 +1,6 @@
package set
// Filter filters all r's from the input list.
// Filter filters all elements in r from the input list.
func Filter[K comparable](in []K, r ...K) []K {
rs := Map(r)
var out []K

View File

@@ -1,6 +1,6 @@
package set
// Map takes a slice of a given type and create a boolean map with keys
// Map takes a slice of a given type and creates a boolean map with keys
// of that type.
func Map[K comparable](in []K) map[K]bool {
m := make(map[K]bool, len(in))

View File

@@ -8,7 +8,7 @@ import (
"golang.org/x/exp/constraints"
)
// Set is a generic set type.
// Set is a generic set type, implemented as a map of keys to booleans.
type Set[K constraints.Ordered] map[K]bool
// New initializes a new Set with the given elements.
@@ -78,7 +78,7 @@ func (s Set[K]) Equals(s2 Set[K]) bool {
return len(s) == len(s2) && s.IsSubset(s2)
}
// Contains returs true if the set contains the presented
// Contains returns true if the set contains the given
// element.
func (s Set[K]) Contains(e K) bool {
for k := range s {
@@ -194,7 +194,7 @@ func (s *Set[K]) Add(elems ...K) bool {
return len(*s) != il
}
// Remove deletes the given element from the set.
// Remove deletes the given elements from the set.
func (s Set[K]) Remove(s2 Set[K]) bool {
if s.Empty() {
return false
@@ -220,8 +220,8 @@ func (s Set[K]) Discard(elems ...K) bool {
return len(s) != il
}
// Map returns a new set by applied the function f
// to all it's elements.
// Map returns a new set by applying the function f
// to all its elements.
func (s Set[K]) Map(f func(K) K) Set[K] {
out := make(Set[K], len(s))
for k := range s {
@@ -231,7 +231,7 @@ func (s Set[K]) Map(f func(K) K) Set[K] {
return out
}
// Each applies the function f to all it's elements.
// Each applies the function f to all its elements.
func (s Set[K]) Each(f func(K)) {
for k := range s {
f(k)
@@ -239,7 +239,7 @@ func (s Set[K]) Each(f func(K)) {
}
// Select returns a new set with all the elements for
// that f returns true.
// which f returns true.
func (s Set[K]) Select(f func(K) bool) Set[K] {
out := make(Set[K], len(s))
for k := range s {
@@ -252,7 +252,7 @@ func (s Set[K]) Select(f func(K) bool) Set[K] {
}
// Partition returns two new sets: the first contains all
// the elements for which f returns true. The seconds the others.
// the elements for which f returns true. The second contains the others.
func (s Set[K]) Partition(f func(K) bool) (Set[K], Set[K]) {
yes := make(Set[K], len(s))
no := make(Set[K], len(s))

View File

@@ -8,6 +8,7 @@ import (
)
// SortedKeys returns the sorted keys of the map.
// The keys are sorted in ascending order.
func SortedKeys[K constraints.Ordered, V any](m map[K]V) []K {
// sort
keys := maps.Keys(m)
@@ -16,6 +17,7 @@ func SortedKeys[K constraints.Ordered, V any](m map[K]V) []K {
}
// Sorted returns a sorted set of the input.
// Duplicates are removed.
func Sorted[K constraints.Ordered](l []K) []K {
return SortedFiltered(l, func(k K) bool {
return true
@@ -23,6 +25,7 @@ func Sorted[K constraints.Ordered](l []K) []K {
}
// SortedFiltered returns a sorted set of the input, filtered by the predicate.
// Duplicates are removed.
func SortedFiltered[K constraints.Ordered](l []K, want func(K) bool) []K {
if len(l) == 0 {
return l

View File

@@ -15,7 +15,7 @@ var ErrNotInit = fmt.Errorf("not initialized")
// globalPrefix is prefixed to all temporary dirs.
var globalPrefix string
// File is a temporary file.
// File is a temporary file that is stored on a ramdisk if possible.
type File struct {
dir string
dev string
@@ -23,6 +23,8 @@ type File struct {
}
// New returns a new tempfile wrapper.
// It will create a temporary directory and a file inside it.
// If possible, it will mount a ramdisk to the temporary directory.
func New(ctx context.Context, prefix string) (*File, error) {
td, err := os.MkdirTemp(tempdirBase(), globalPrefix+prefix)
if err != nil {

View File

@@ -95,7 +95,7 @@ func AskForBool(ctx context.Context, text string, def bool) (bool, error) {
}
}
// AskForInt asks for an valid interger once. If the input
// AskForInt asks for a valid integer once. If the input
// can not be converted to an int it returns an error.
func AskForInt(ctx context.Context, text string, def int) (int, error) {
if ctxutil.IsAlwaysYes(ctx) {
@@ -121,6 +121,7 @@ func AskForInt(ctx context.Context, text string, def int) (int, error) {
// AskForConfirmation asks a yes/no question until the user
// replies yes or no.
// It will retry until a valid answer is given or the user aborts.
func AskForConfirmation(ctx context.Context, text string) bool {
if ctxutil.IsAlwaysYes(ctx) {
return true
@@ -141,6 +142,7 @@ func AskForConfirmation(ctx context.Context, text string) bool {
}
// AskForKeyImport asks for permissions to import the named key.
// It will ask the user for confirmation.
func AskForKeyImport(ctx context.Context, key string, names []string) bool {
if ctxutil.IsAlwaysYes(ctx) {
return true
@@ -159,6 +161,7 @@ func AskForKeyImport(ctx context.Context, key string, names []string) bool {
}
// AskForPassword prompts for a password, optionally prompting twice until both match.
// It will retry until the passwords match or the user aborts.
func AskForPassword(ctx context.Context, name string, repeat bool) (string, error) {
if ctxutil.IsAlwaysYes(ctx) {
return "", nil

View File

@@ -25,7 +25,7 @@ func HasPassPromptFunc(ctx context.Context) bool {
return ok && ppf != nil
}
// GetPassPromptFunc will return the password prompt func or a default one
// GetPassPromptFunc will return the password prompt func or a default one.
// Note: will never return nil.
func GetPassPromptFunc(ctx context.Context) PassPromptFunc {
ppf, ok := ctx.Value(ctxKeyPassPromptFunc).(PassPromptFunc)
@@ -37,12 +37,14 @@ func GetPassPromptFunc(ctx context.Context) PassPromptFunc {
}
// WithWorkdir returns a context with the working directory option set.
// The working directory is used to resolve relative paths.
func WithWorkdir(ctx context.Context, dir string) context.Context {
return context.WithValue(ctx, ctxKeyWorkdir, dir)
}
// GetWorkdir returns the working directory from the context or an empty
// string if it is not set.
// The working directory is used to resolve relative paths.
func GetWorkdir(ctx context.Context) string {
sv, ok := ctx.Value(ctxKeyWorkdir).(string)
if !ok {

View File

@@ -25,6 +25,8 @@ var (
)
// DetectName tries to guess the name of the logged in user.
// It checks the context, the command line flags, environment variables,
// and the git config.
func DetectName(ctx context.Context, c *cli.Context) string {
cand := make([]string, 0, 10)
cand = append(cand, ctxutil.GetUsername(ctx))
@@ -50,6 +52,8 @@ func DetectName(ctx context.Context, c *cli.Context) string {
}
// DetectEmail tries to guess the email of the logged in user.
// It checks the context, the command line flags, environment variables,
// and the git config.
func DetectEmail(ctx context.Context, c *cli.Context) string {
cand := make([]string, 0, 10)
cand = append(cand, ctxutil.GetEmail(ctx))

View File

@@ -20,6 +20,7 @@ const (
var now = time.Now
// ProgressBar is a gopass progress bar.
// It is used to display progress for long-running operations.
type ProgressBar struct {
// keep both int64 fields at the top to ensure correct
// 8-byte alignment on 32 bit systems. See https://golang.org/pkg/sync/atomic/#pkg-note-BUG