lockbox

password manager
Log | Files | Refs | README | LICENSE

commit 6aca128594765319aaba8e45f4b71c8e09ff0d27
parent d69ae80d8b1d166cd2cc85ce295d0a5cdbc70681
Author: Sean Enck <sean@ttypty.com>
Date:   Wed,  4 Feb 2026 16:33:54 -0500

generate fields

Diffstat:
M.gitignore | 1+
MMakefile | 1+
Minternal/app/help/core.go | 8++------
Minternal/app/insert.go | 2+-
Minternal/app/list.go | 5+----
Minternal/kdbx/actions.go | 2+-
Minternal/kdbx/core.go | 12+-----------
Minternal/kdbx/core_test.go | 9++++++---
Minternal/kdbx/hasher.go | 2+-
Atools/kdbx.go | 73+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
10 files changed, 88 insertions(+), 27 deletions(-)

diff --git a/.gitignore b/.gitignore @@ -1,2 +1,3 @@ target/ testdata +internal/kdbx/fields.go diff --git a/Makefile b/Makefile @@ -14,6 +14,7 @@ setup: @test -d $(TARGET) || mkdir -p $(TARGET) $(OBJECT): + go generate ./... GOOS=$(GOOS) GOARCH=$(GOARCH) go build $(GOFLAGS) -ldflags "$(LDFLAGS) -X main.version=$(VERSION)" -o "$(OBJECT)" $(CMD)/main.go unittest: diff --git a/internal/app/help/core.go b/internal/app/help/core.go @@ -127,14 +127,10 @@ func Usage(verbose bool, exe string) ([]string, error) { document.Config.XDG = config.ConfigXDG document.ReKey.KeyFile = setDocFlag(commands.ReKeyFlags.KeyFile) document.ReKey.NoKey = commands.ReKeyFlags.NoKey - var fields []string - for _, field := range kdbx.AllowedFields { - fields = append(fields, strings.ToLower(field)) - } - document.Database.Fields = strings.Join(fields, ", ") + document.Database.Fields = strings.Join(kdbx.AllFieldsLower, ", ") var examples []string for _, example := range []string{commands.Insert, commands.Show} { - for _, field := range fields { + for _, field := range kdbx.AllFieldsLower { examples = append(examples, fmt.Sprintf("%s %s my/path/%s", document.Executable, example, field)) } } diff --git a/internal/app/insert.go b/internal/app/insert.go @@ -21,7 +21,7 @@ func Insert(cmd UserInputOptions) error { } entry := args[0] base := kdbx.Base(entry) - if !slices.ContainsFunc(kdbx.AllowedFields, func(v string) bool { + if !slices.ContainsFunc(kdbx.AllFields, func(v string) bool { return base == strings.ToLower(v) }) { return fmt.Errorf("'%s' is not an allowed field name", base) diff --git a/internal/app/list.go b/internal/app/list.go @@ -57,10 +57,7 @@ func doList(attr, filter string, cmd CommandOptions, mode ListMode) error { isFields := mode == ListFieldsMode var allowedFields []string if isFields { - allowedFields = make([]string, len(kdbx.AllowedFields)) - for idx, item := range kdbx.AllowedFields { - allowedFields[idx] = strings.ToLower(item) - } + allowedFields = kdbx.AllFieldsLower } isGroups := mode == ListGroupsMode || isFields for f, err := range e { diff --git a/internal/kdbx/actions.go b/internal/kdbx/actions.go @@ -208,7 +208,7 @@ func (t *Transaction) Move(moves ...MoveRequest) error { values := make(map[string]string) for k, v := range move.Source.Values { found := false - for _, mapping := range AllowedFields { + for _, mapping := range AllFields { if strings.EqualFold(k, mapping) { values[mapping] = v found = true diff --git a/internal/kdbx/core.go b/internal/kdbx/core.go @@ -14,23 +14,13 @@ import ( "github.com/tobischo/gokeepasslib/v3/wrappers" ) -var ( - errPath = errors.New("input paths must contain at LEAST 2 components (excluding field)") - // AllowedFields are the same of allowed names for storing in a kdbx entry - AllowedFields = []string{NotesField, OTPField, "Password", URLField} -) +var errPath = errors.New("input paths must contain at LEAST 2 components (excluding field)") const ( checksumKey = "checksum" titleKey = "Title" pathSep = "/" modTimeKey = "ModTime" - // OTPField is the totp storage attribute - OTPField = "otp" - // NotesField is the multiline notes key - NotesField = "Notes" - // URLField is a non-secret (in terms of input) entry field - URLField = "URL" ) type ( diff --git a/internal/kdbx/core_test.go b/internal/kdbx/core_test.go @@ -11,13 +11,16 @@ import ( ) func TestAllowedSort(t *testing.T) { - set := fmt.Sprintf("%v", kdbx.AllowedFields) - have := kdbx.AllowedFields + testSorted(kdbx.AllFields, fmt.Sprintf("%v", kdbx.AllFields), t) + testSorted(kdbx.AllFieldsLower, fmt.Sprintf("%v", kdbx.AllFieldsLower), t) +} + +func testSorted(have []string, set string, t *testing.T) { slices.SortFunc(have, func(x, y string) int { return strings.Compare(strings.ToLower(x), strings.ToLower(y)) }) if fmt.Sprintf("%v", have) != set { - t.Error("allowed fields has incorrect sort") + t.Errorf("allowed fields has incorrect sort: %v", set) } } diff --git a/internal/kdbx/hasher.go b/internal/kdbx/hasher.go @@ -58,7 +58,7 @@ func NewHasher(mode ValueMode) (*Hasher, error) { } obj.checksumTo = max(int(length), obj.checksumTo) } - obj.requiredLength = (len(AllowedFields) + 2) * obj.checksumTo + obj.requiredLength = (len(AllFields) + 2) * obj.checksumTo return obj, nil } diff --git a/tools/kdbx.go b/tools/kdbx.go @@ -0,0 +1,73 @@ +//go:generate go run kdbx.go +package main + +import ( + "os" + "path/filepath" + "slices" + "strings" + "text/template" +) + +var items = map[string]string{"Password": "", "OTP": "otp", "Notes": "", "URL": ""} + +const fileTemplate = `// Package kdbx requires fields for kdbx handling +// Code generated by generator; DO NOT EDIT. +package kdbx + +const ( +{{- range . }} + // {{ .Variable }} is the value of '{{ .Value }}' for kdbx files + {{ .Variable }} = "{{ .Value }}" +{{- end }} +) + +var ( + // AllFields are the kdbx fields + AllFields = []string{ + {{- range . }} + {{ .Variable }}, + {{- end }} + } + + // AllFieldsLower are the kdbx fields lowercase + AllFieldsLower = []string{ + {{- range . }} + "{{ .Lower }}", + {{- end }} + } +) +` + +type Item struct { + Variable string + Value string + Lower string +} + +func main() { + var data []Item + for k, v := range items { + value := v + if value == "" { + value = k + } + data = append(data, Item{ + Variable: k + "Field", + Value: value, + Lower: strings.ToLower(value), + }) + } + slices.SortFunc(data, func(x, y Item) int { + return strings.Compare(x.Lower, y.Lower) + }) + + f, err := os.Create(filepath.Join("..", "internal", "kdbx", "fields.go")) + if err != nil { + panic(err) + } + defer f.Close() + + tmpl := template.Must(template.New("gen").Parse(fileTemplate)) + tmpl.Execute(f, data) +}