diff --git a/docs/commands/audit.md b/docs/commands/audit.md index e8796346ba..118642aae3 100644 --- a/docs/commands/audit.md +++ b/docs/commands/audit.md @@ -8,6 +8,20 @@ The `audit` command will decrypt all secrets and scan for weak passwords or othe $ gopass audit ``` +## Excludes + +You can exclude certain secrets from the audit by adding a `.gopass-audit-exclude` file to the secret. The file should contain a list of RE2 patters to exclude, one per line. For example: + +``` +# Lines starting with # are ignored. Trailing comments are not supported. +# Exclude all secrets in the pin folder. +# Note: These are RE2, not Glob patterns! +pin/.* +# Literal matches are also valid RE2 patterns +test_folder/ignore_this +# Gopass internally uses forward slashes as path separators, even on Windows. So no need to escape backslashes. +``` + ## Password strength backends | Backend | Description | @@ -15,5 +29,3 @@ $ gopass audit | [`zxcvbn`](https://github.com/nbutton23/zxcvbn) | [zxcvbn](https://github.com/dropbox/zxcvbn) password strength checker. | | [`crunchy`](https://github.com/muesli/crunchy) | Crunchy password strength checker | | `name` | Checks if password equals the name of the secret | - - diff --git a/internal/action/audit.go b/internal/action/audit.go index cc393d6c01..d02da8ffa6 100644 --- a/internal/action/audit.go +++ b/internal/action/audit.go @@ -45,8 +45,18 @@ func (s *Action) Audit(c *cli.Context) error { return nil } + var excludes string + st := s.Store.Storage(ctx, c.Args().First()) + if buf, err := st.Get(ctx, ".gopass-audit-ignore"); err == nil && buf != nil { + excludes = string(buf) + } + nList := audit.FilterExcludes(excludes, list) + if len(nList) < len(list) { + out.Warningf(ctx, "Excluding %d secrets based on .gopass-audit-ignore", len(list)-len(nList)) + } + a := audit.New(c.Context, s.Store) - r, err := a.Batch(ctx, list) + r, err := a.Batch(ctx, nList) if err != nil { return exit.Error(exit.Unknown, err, "failed to audit password store: %s", err) } diff --git a/internal/audit/excludes.go b/internal/audit/excludes.go new file mode 100644 index 0000000000..156899e3d0 --- /dev/null +++ b/internal/audit/excludes.go @@ -0,0 +1,62 @@ +package audit + +import ( + "regexp" + "strings" + + "github.com/gopasspw/gopass/pkg/debug" +) + +type res []*regexp.Regexp + +func (r res) Matches(s string) bool { + for _, re := range r { + if re.MatchString(s) { + debug.Log("Matched %s against %s", s, re.String()) + + return true + } + } + + return false +} + +// FilterExcludes filters the given list of secrets against the given exclude patterns (RE2 syntax). +func FilterExcludes(excludes string, in []string) []string { + debug.Log("Filtering %d secrets against %d exclude patterns", len(in), strings.Count(excludes, "\n")) + + res := make(res, 0, 10) + for _, line := range strings.Split(excludes, "\n") { + line = strings.TrimSpace(line) + if line == "" { + continue + } + if strings.HasPrefix(line, "#") { + continue + } + re, err := regexp.Compile(line) + if err != nil { + debug.Log("failed to compile exclude pattern %q: %s", line, err) + + continue + } + debug.Log("Adding exclude pattern %q", re.String()) + res = append(res, re) + } + + // shortcut if we have no excludes + if len(res) < 1 { + return in + } + + // check all secrets against all excludes + out := make([]string, 0, len(in)) + for _, s := range in { + if res.Matches(s) { + continue + } + out = append(out, s) + } + + return out +}