commit 5ea5428099e22d0feae96d142c6447572d0ff889
parent 290bb3d47f715ba493c46073e254416ba1f4f494
Author: Sean Enck <sean@ttypty.com>
Date: Wed, 2 Jul 2025 13:04:14 -0400
the url field is not a password and can be shown plaintext
Diffstat:
7 files changed, 46 insertions(+), 18 deletions(-)
diff --git a/internal/app/core.go b/internal/app/core.go
@@ -23,7 +23,7 @@ type (
UserInputOptions interface {
CommandOptions
IsPipe() bool
- Input(bool, string) ([]byte, error)
+ Input(bool, bool, string) ([]byte, error)
}
// DefaultCommand is the default CLI app type for actual execution
@@ -78,6 +78,6 @@ func (a *DefaultCommand) IsPipe() bool {
}
// Input will read user input
-func (a *DefaultCommand) Input(interactive bool, prompt string) ([]byte, error) {
- return platform.GetUserInput(interactive, prompt)
+func (a *DefaultCommand) Input(interactive, isPassword bool, prompt string) ([]byte, error) {
+ return platform.GetUserInput(interactive, isPassword, prompt)
}
diff --git a/internal/app/insert.go b/internal/app/insert.go
@@ -40,7 +40,8 @@ func Insert(cmd UserInputOptions) error {
}
}
}
- password, err := cmd.Input(!isPipe && !strings.EqualFold(base, kdbx.NotesField), base)
+ isPass := !strings.EqualFold(base, kdbx.URLField)
+ password, err := cmd.Input(!isPipe && !strings.EqualFold(base, kdbx.NotesField), isPass, base)
if err != nil {
return fmt.Errorf("invalid input: %w", err)
}
@@ -53,7 +54,9 @@ func Insert(cmd UserInputOptions) error {
return err
}
if !isPipe {
- fmt.Fprintln(cmd.Writer())
+ if isPass {
+ fmt.Fprintln(cmd.Writer())
+ }
}
return nil
}
diff --git a/internal/app/insert_test.go b/internal/app/insert_test.go
@@ -18,6 +18,7 @@ type (
pipe func() bool
token func() string
prompt string
+ isPass bool
interactive bool
}
)
@@ -36,9 +37,10 @@ func (m *mockInsert) IsPipe() bool {
return m.pipe()
}
-func (m *mockInsert) Input(interactive bool, prompt string) ([]byte, error) {
+func (m *mockInsert) Input(interactive, isPass bool, prompt string) ([]byte, error) {
m.interactive = interactive
m.prompt = prompt
+ m.isPass = isPass
return m.input()
}
@@ -121,7 +123,7 @@ func TestInsertDo(t *testing.T) {
if m.command.buf.String() == "" {
t.Error("invalid insert")
}
- if m.prompt != "password" {
+ if m.prompt != "password" || !m.isPass {
t.Error("invalid field prompt")
}
m.command.confirm = false
@@ -152,7 +154,19 @@ func TestInsertDo(t *testing.T) {
if m.command.buf.String() == "" || m.interactive {
t.Errorf("invalid insert %s %v", m.command.buf.String(), m.interactive)
}
- if m.prompt != "notes" {
+ if m.prompt != "notes" || !m.isPass {
+ t.Error("invalid field prompt")
+ }
+ m.interactive = false
+ m.command.buf = bytes.Buffer{}
+ m.command.args = []string{"test/test2/test1/url"}
+ if err := app.Insert(m); err != nil {
+ t.Errorf("invalid error: %v", err)
+ }
+ if m.command.buf.String() != "" || !m.interactive {
+ t.Errorf("invalid insert %s %v", m.command.buf.String(), m.interactive)
+ }
+ if m.prompt != "url" || m.isPass {
t.Error("invalid field prompt")
}
}
diff --git a/internal/app/rekey.go b/internal/app/rekey.go
@@ -24,7 +24,7 @@ func ReKey(cmd UserInputOptions) error {
}
var pass string
if !vars.NoKey {
- p, err := cmd.Input(!piping, "password")
+ p, err := cmd.Input(!piping, true, "password")
if err != nil {
return err
}
diff --git a/internal/app/rekey_test.go b/internal/app/rekey_test.go
@@ -2,6 +2,7 @@ package app_test
import (
"bytes"
+ "errors"
"io"
"testing"
@@ -32,7 +33,10 @@ func (m *mockKeyer) Args() []string {
return m.args
}
-func (m *mockKeyer) Input(bool, string) ([]byte, error) {
+func (m *mockKeyer) Input(_, pass bool, _ string) ([]byte, error) {
+ if !pass {
+ return nil, errors.New("invalid request, always password")
+ }
return []byte(m.pass), nil
}
diff --git a/internal/kdbx/core.go b/internal/kdbx/core.go
@@ -17,7 +17,7 @@ import (
var (
errPath = errors.New("input paths must contain at LEAST 2 components")
// AllowedFields are the same of allowed names for storing in a kdbx entry
- AllowedFields = []string{NotesField, OTPField, "Password", "URL"}
+ AllowedFields = []string{NotesField, OTPField, "Password", URLField}
)
const (
@@ -28,6 +28,8 @@ const (
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/platform/os.go b/internal/platform/os.go
@@ -42,10 +42,10 @@ func termEcho(on bool) {
}
// GetUserInput will read the user's input from stdin via multiple means.
-func GetUserInput(interactive bool, prompt string) ([]byte, error) {
+func GetUserInput(interactive, isPassword bool, prompt string) ([]byte, error) {
var value string
if interactive {
- input, err := confirmInputsMatch(prompt)
+ input, err := confirmInputsMatch(isPassword, prompt)
if err != nil {
return nil, err
}
@@ -63,16 +63,21 @@ func GetUserInput(interactive bool, prompt string) ([]byte, error) {
return []byte(value), nil
}
-func confirmInputsMatch(prompt string) (string, error) {
- termEcho(false)
- defer func() {
- termEcho(true)
- }()
+func confirmInputsMatch(isPassword bool, prompt string) (string, error) {
+ if isPassword {
+ termEcho(false)
+ defer func() {
+ termEcho(true)
+ }()
+ }
fmt.Printf("please enter %s: ", prompt)
first, err := Stdin(true)
if err != nil {
return "", err
}
+ if !isPassword {
+ return first, nil
+ }
fmt.Printf("\nplease re-enter %s: ", prompt)
second, err := Stdin(true)
if err != nil {