commit c2cb8ecc5a75752a5c0662492fa3335dc48f189e
parent 46934afcb75a339f3a8681128ad2b74e6dd9923b
Author: Sean Enck <sean@ttypty.com>
Date: Sat, 7 Jun 2025 20:40:10 -0400
filtering should happen at the list/group or conv level
Diffstat:
9 files changed, 69 insertions(+), 42 deletions(-)
diff --git a/cmd/lb/main_test.go b/cmd/lb/main_test.go
@@ -217,6 +217,7 @@ func test(profile string) error {
r.logAppend("echo")
r.run("", "ls")
r.run("", "ls multiline")
+ r.run("", "ls url")
r.run("", "json")
r.run("", "json 'multiline'")
r.logAppend("echo")
diff --git a/cmd/lb/tests/expected.log b/cmd/lb/tests/expected.log
@@ -58,6 +58,7 @@ test4/multiline/notes
test5/multiline/notes
test6/multiline/notes
test6/multiline/password
+test7/deeper/root/url
{
"test1/key1": {
"modtime": "XXXX-XX-XX",
diff --git a/internal/app/conv.go b/internal/app/conv.go
@@ -6,6 +6,7 @@ import (
"errors"
"fmt"
"io"
+ "regexp"
"strings"
"git.sr.ht/~enckse/lockbox/internal/kdbx"
@@ -31,7 +32,16 @@ func Conv(cmd CommandOptions) error {
}
func serialize(w io.Writer, tx *kdbx.Transaction, isJSON bool, filter string) error {
- e, err := tx.QueryCallback(kdbx.QueryOptions{Mode: kdbx.ListMode, Values: kdbx.JSONValue, PathFilter: filter})
+ var re *regexp.Regexp
+ hasFilter := filter != ""
+ if hasFilter {
+ var err error
+ re, err = regexp.Compile(filter)
+ if err != nil {
+ return err
+ }
+ }
+ e, err := tx.QueryCallback(kdbx.QueryOptions{Mode: kdbx.ListMode, Values: kdbx.JSONValue})
if err != nil {
return err
}
@@ -43,6 +53,11 @@ func serialize(w io.Writer, tx *kdbx.Transaction, isJSON bool, filter string) er
if err != nil {
return err
}
+ if hasFilter {
+ if !re.MatchString(item.Path) {
+ continue
+ }
+ }
if printed {
if isJSON {
fmt.Fprint(w, ",")
diff --git a/internal/app/help/core_test.go b/internal/app/help/core_test.go
@@ -13,7 +13,7 @@ func TestUsage(t *testing.T) {
t.Errorf("invalid usage, out of date? %d", len(u))
}
u, _ = help.Usage(true, "lb")
- if len(u) != 108 {
+ if len(u) != 112 {
t.Errorf("invalid verbose usage, out of date? %d", len(u))
}
for _, usage := range u {
diff --git a/internal/app/json_test.go b/internal/app/json_test.go
@@ -16,6 +16,14 @@ func TestJSON(t *testing.T) {
if err := app.JSON(m); err.Error() != "invalid arguments" {
t.Errorf("invalid error: %v", err)
}
+ m.args = []string{}
+ if err := app.JSON(m); err != nil {
+ t.Errorf("invalid error: %v", err)
+ }
+ if m.buf.String() == "" || m.buf.String() == "{}\n" {
+ t.Error("no data")
+ }
+ m.buf = bytes.Buffer{}
m.args = []string{"test2/test1"}
if err := app.JSON(m); err != nil {
t.Errorf("invalid error: %v", err)
diff --git a/internal/app/list.go b/internal/app/list.go
@@ -4,6 +4,7 @@ package app
import (
"errors"
"fmt"
+ "regexp"
"sort"
"strings"
@@ -27,13 +28,27 @@ func List(cmd CommandOptions, groups bool) error {
}
func doList(attr, filter string, cmd CommandOptions, groups bool) error {
+ var re *regexp.Regexp
+ hasFilter := filter != ""
+ if hasFilter {
+ var err error
+ re, err = regexp.Compile(filter)
+ if err != nil {
+ return err
+ }
+ }
opts := kdbx.QueryOptions{}
opts.Mode = kdbx.ListMode
- opts.PathFilter = filter
e, err := cmd.Transaction().QueryCallback(opts)
if err != nil {
return err
}
+ allowed := func(p string) bool {
+ if hasFilter {
+ return re.MatchString(p)
+ }
+ return true
+ }
w := cmd.Writer()
attrFilter := attr != ""
for f, err := range e {
@@ -41,7 +56,9 @@ func doList(attr, filter string, cmd CommandOptions, groups bool) error {
return err
}
if groups {
- fmt.Fprintf(w, "%s\n", f.Path)
+ if allowed(f.Path) {
+ fmt.Fprintf(w, "%s\n", f.Path)
+ }
continue
}
if f.Values == nil {
@@ -54,7 +71,10 @@ func doList(attr, filter string, cmd CommandOptions, groups bool) error {
continue
}
}
- results = append(results, kdbx.NewPath(f.Path, k))
+ path := kdbx.NewPath(f.Path, k)
+ if allowed(path) {
+ results = append(results, path)
+ }
}
if len(results) == 0 {
continue
diff --git a/internal/app/list_test.go b/internal/app/list_test.go
@@ -80,6 +80,22 @@ func TestFind(t *testing.T) {
if m.buf.String() == "" {
t.Error("nothing listed")
}
+ m.buf.Reset()
+ m.args = []string{"test"}
+ if err := app.List(m, true); err != nil {
+ t.Errorf("invalid error: %v", err)
+ }
+ if m.buf.String() == "" {
+ t.Error("nothing listed")
+ }
+ m.buf.Reset()
+ m.args = []string{"[zzzz]"}
+ if err := app.List(m, true); err != nil {
+ t.Errorf("invalid error: %v", err)
+ }
+ if m.buf.String() != "" {
+ t.Error("something listed")
+ }
}
func TestGroups(t *testing.T) {
diff --git a/internal/kdbx/query.go b/internal/kdbx/query.go
@@ -5,7 +5,6 @@ import (
"crypto/sha512"
"errors"
"fmt"
- "regexp"
"slices"
"strings"
@@ -17,10 +16,9 @@ import (
type (
// QueryOptions indicates how to find entities
QueryOptions struct {
- PathFilter string
- Criteria string
- Mode QueryMode
- Values ValueMode
+ Criteria string
+ Mode QueryMode
+ Values ValueMode
}
// QueryMode indicates HOW an entity will be found
QueryMode int
@@ -123,15 +121,6 @@ func (t *Transaction) QueryCallback(args QueryOptions) (QuerySeq2, error) {
var entities []entity
isSort := args.Mode != ExactMode
decrypt := args.Values != BlankValue
- hasPathFilter := args.PathFilter != ""
- var pathFilter *regexp.Regexp
- if hasPathFilter {
- var err error
- pathFilter, err = regexp.Compile(args.PathFilter)
- if err != nil {
- return nil, err
- }
- }
err := t.act(func(ctx Context) error {
forEach("", ctx.db.Content.Root.Groups[0].Groups, ctx.db.Content.Root.Groups[0].Entries, func(offset string, entry gokeepasslib.Entry) {
path := getPathName(entry)
@@ -156,11 +145,6 @@ func (t *Transaction) QueryCallback(args QueryOptions) (QuerySeq2, error) {
}
}
}
- if hasPathFilter {
- if !pathFilter.MatchString(path) {
- return
- }
- }
obj := entity{backing: entry, path: path}
if isSort && len(entities) > 0 {
i, _ := slices.BinarySearchFunc(entities, obj, func(i, j entity) int {
diff --git a/internal/kdbx/query_test.go b/internal/kdbx/query_test.go
@@ -246,24 +246,6 @@ func TestQueryCallback(t *testing.T) {
if res[0].Path != "test/test/abc" {
t.Errorf("invalid results: %v", res)
}
- seq, err = fullSetup(t, true).QueryCallback(kdbx.QueryOptions{Mode: kdbx.ExactMode, Criteria: "test/test/abc", PathFilter: "abc"})
- if err != nil {
- t.Errorf("no error: %v", err)
- }
- res = testCollect(t, 1, seq)
- if res[0].Path != "test/test/abc" {
- t.Errorf("invalid results: %v", res)
- }
- seq, err = fullSetup(t, true).QueryCallback(kdbx.QueryOptions{Mode: kdbx.ExactMode, Criteria: "test/test/abc", PathFilter: "abz"})
- if err != nil {
- t.Errorf("no error: %v", err)
- }
- testCollect(t, 0, seq)
- seq, err = fullSetup(t, true).QueryCallback(kdbx.QueryOptions{Mode: kdbx.ExactMode, Criteria: "abczzz"})
- if err != nil {
- t.Errorf("no error: %v", err)
- }
- testCollect(t, 0, seq)
}
func TestSetModTime(t *testing.T) {