diff --git a/go.mod b/go.mod index 6f039a7..1bbb386 100644 --- a/go.mod +++ b/go.mod @@ -1,12 +1,12 @@ module github.com/rwinkhart/MUTN -go 1.25.6 +go 1.26.0 require ( github.com/charmbracelet/glamour v0.7.0 - github.com/rwinkhart/go-boilerplate v0.2.2 - github.com/rwinkhart/libmutton v0.4.3-0.20260125215941-ec9c6b506232 - golang.org/x/term v0.39.0 + github.com/rwinkhart/go-boilerplate v0.3.0 + github.com/rwinkhart/libmutton v0.4.3-0.20260213233823-3fd2b13712ef + golang.org/x/term v0.40.0 ) require ( @@ -29,18 +29,18 @@ require ( github.com/pquerna/otp v1.5.0 // indirect github.com/rivo/uniseg v0.4.7 // indirect github.com/rwinkhart/go-highlite v0.1.1 // indirect - github.com/rwinkhart/peercred-mini v0.1.2 // indirect - github.com/rwinkhart/rcw v0.2.5 // indirect + github.com/rwinkhart/peercred-mini v0.1.4 // indirect + github.com/rwinkhart/rcw v0.3.0 // indirect github.com/yuin/goldmark v1.7.16 // indirect github.com/yuin/goldmark-emoji v1.0.6 // indirect - golang.org/x/crypto v0.47.0 // indirect + golang.org/x/crypto v0.48.0 // indirect golang.org/x/net v0.49.0 // indirect - golang.org/x/sys v0.40.0 // indirect + golang.org/x/sys v0.41.0 // indirect gopkg.in/yaml.v2 v2.4.0 // indirect ) -replace golang.org/x/sys => github.com/rwinkhart/sys v0.40.0 +replace golang.org/x/sys => github.com/rwinkhart/sys v0.41.0 -replace github.com/Microsoft/go-winio => github.com/rwinkhart/go-winio v0.1.0 +replace github.com/Microsoft/go-winio => github.com/rwinkhart/go-winio v0.1.1 replace github.com/charmbracelet/glamour => github.com/rwinkhart/glamour-temp-MUTN v0.7.2 diff --git a/go.sum b/go.sum index 52eb338..0b27a32 100644 --- a/go.sum +++ b/go.sum @@ -44,20 +44,20 @@ github.com/rivo/uniseg v0.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ= github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88= github.com/rwinkhart/glamour-temp-MUTN v0.7.2 h1:/CBexwkGdL8HuZjO7M0zzjGHaHCr9/r4XTOtUJa+pPE= github.com/rwinkhart/glamour-temp-MUTN v0.7.2/go.mod h1:DN/eJaNQ5h7dgxzfmWFo7By+b82CKEUdNaY6z4R4v3c= -github.com/rwinkhart/go-boilerplate v0.2.2 h1:SVHTAQU+HWFivtUnDBcfrgClJV5ZmHyFS7/uERh7NKU= -github.com/rwinkhart/go-boilerplate v0.2.2/go.mod h1:/NVRKGslU20E5xU5YOgXzWxA6aa94BMtv5MtHRTb5Ek= +github.com/rwinkhart/go-boilerplate v0.3.0 h1:dwlm1mZya1xrATkvm2pHbTIFNb5WRjIc7A3a4Ep2aAM= +github.com/rwinkhart/go-boilerplate v0.3.0/go.mod h1:ES13A2r9fnCVfyezwMBgY/RgA4pOIudOUXz3Jk/ikes= github.com/rwinkhart/go-highlite v0.1.1 h1:9TxbRhYVfD/3YaEgNk1BtEiZ0t8/5mxDUZqNMXHodT0= github.com/rwinkhart/go-highlite v0.1.1/go.mod h1:mWLMtCWcyV0BG4NeyPyAUGMjPvI0ds6vXmUq89QwBFA= -github.com/rwinkhart/go-winio v0.1.0 h1:b72agLW+dETGmhR3VbcbwnStfgKfc5AfgJOXBJDkaHg= -github.com/rwinkhart/go-winio v0.1.0/go.mod h1:ZWa7ssZJT30CCDGJ7fk/2SBTq9BIQrrVjrcss0UW2s0= -github.com/rwinkhart/libmutton v0.4.3-0.20260125215941-ec9c6b506232 h1:NzUoqkjBy9u9a3wV+D1DR2DemVpYi0Hxug800ybarZE= -github.com/rwinkhart/libmutton v0.4.3-0.20260125215941-ec9c6b506232/go.mod h1:63SCVQaWcg4JiEBxYEtLwAiRxXQvlpyFsmWUxxfdIpU= -github.com/rwinkhart/peercred-mini v0.1.2 h1:4cGWDbv0whvLeVvbUdx84V/9p+2fS+DEXgrA1KxlRFo= -github.com/rwinkhart/peercred-mini v0.1.2/go.mod h1:LLHG7YshHEpbpJJP+Il9nx2dnGj5O3VGE32rWmflj0c= -github.com/rwinkhart/rcw v0.2.5 h1:3GJeii9sDZsZNL+HRkfHrnbgxnaB8a9kejiMIfPXnpE= -github.com/rwinkhart/rcw v0.2.5/go.mod h1:qVw8Yp/SKtsuVZYOzzluVEVkoRJ1G7UEh2aR1CXso/U= -github.com/rwinkhart/sys v0.40.0 h1:ZPBbXb+27vLL518sZhhNV89jXK1KbCnFGmYJ2juOQ/c= -github.com/rwinkhart/sys v0.40.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks= +github.com/rwinkhart/go-winio v0.1.1 h1:kAJKiqneR7cUR01Wn5/doAAV4kOGTEGPug4oinXc5N4= +github.com/rwinkhart/go-winio v0.1.1/go.mod h1:ZWa7ssZJT30CCDGJ7fk/2SBTq9BIQrrVjrcss0UW2s0= +github.com/rwinkhart/libmutton v0.4.3-0.20260213233823-3fd2b13712ef h1:XDjR//iPsxytW/nRMCS6re+fwY+ljddRA/7Tk52hAT4= +github.com/rwinkhart/libmutton v0.4.3-0.20260213233823-3fd2b13712ef/go.mod h1:XkT0W9PW8ouGUwrGKhNCSDgaEY25n64+/HxJUQcQ0FU= +github.com/rwinkhart/peercred-mini v0.1.4 h1:93+phjLknvJadEd2cu/ZPPWdfRSPOwFJzDBEn4ZtWVc= +github.com/rwinkhart/peercred-mini v0.1.4/go.mod h1:E8eApo/izzmq4nGGR+kpJ0ZLAXjofnVRMGCeVMPQ/Ik= +github.com/rwinkhart/rcw v0.3.0 h1:OdgA++IGcDrZqN6FzyYnzbz04fL8Q1MHvlgPjhtgJM4= +github.com/rwinkhart/rcw v0.3.0/go.mod h1:UUY7kSku6kV2X1RWPUcK0Re+rQ6RvngWI8DVZJ11cqk= +github.com/rwinkhart/sys v0.41.0 h1:pHB6HphVC132UXYZ6yeOk62abqgl+pyl5ZZnsk4iUKg= +github.com/rwinkhart/sys v0.41.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA= @@ -66,12 +66,12 @@ github.com/yuin/goldmark v1.7.16 h1:n+CJdUxaFMiDUNnWC3dMWCIQJSkxH4uz3ZwQBkAlVNE= github.com/yuin/goldmark v1.7.16/go.mod h1:ip/1k0VRfGynBgxOz0yCqHrbZXhcjxyuS66Brc7iBKg= github.com/yuin/goldmark-emoji v1.0.6 h1:QWfF2FYaXwL74tfGOW5izeiZepUDroDJfWubQI9HTHs= github.com/yuin/goldmark-emoji v1.0.6/go.mod h1:ukxJDKFpdFb5x0a5HqbdlcKtebh086iJpI31LTKmWuA= -golang.org/x/crypto v0.47.0 h1:V6e3FRj+n4dbpw86FJ8Fv7XVOql7TEwpHapKoMJ/GO8= -golang.org/x/crypto v0.47.0/go.mod h1:ff3Y9VzzKbwSSEzWqJsJVBnWmRwRSHt/6Op5n9bQc4A= +golang.org/x/crypto v0.48.0 h1:/VRzVqiRSggnhY7gNRxPauEQ5Drw9haKdM0jqfcCFts= +golang.org/x/crypto v0.48.0/go.mod h1:r0kV5h3qnFPlQnBSrULhlsRfryS2pmewsg+XfMgkVos= golang.org/x/net v0.49.0 h1:eeHFmOGUTtaaPSGNmjBKpbng9MulQsJURQUAfUwY++o= golang.org/x/net v0.49.0/go.mod h1:/ysNB2EvaqvesRkuLAyjI1ycPZlQHM3q01F02UY/MV8= -golang.org/x/term v0.39.0 h1:RclSuaJf32jOqZz74CkPA9qFuVTX7vhLlpfj/IGWlqY= -golang.org/x/term v0.39.0/go.mod h1:yxzUCTP/U+FzoxfdKmLaA0RV1WgE0VY7hXBwKtY/4ww= +golang.org/x/term v0.40.0 h1:36e4zGLqU4yhjlmxEaagx2KuYbJq3EwY8K943ZsHcvg= +golang.org/x/term v0.40.0/go.mod h1:w2P8uVp06p2iyKKuvXIm7N/y0UCRt3UfJTfZ7oOpglM= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= diff --git a/mutn.go b/mutn.go index 52f1544..62fb45e 100644 --- a/mutn.go +++ b/mutn.go @@ -94,7 +94,7 @@ func main() { case "note", "-n": field = 4 case "menu", "-m": - cli.CopyMenu(args[1], nil, "") + cli.CopyMenu(args[1], nil, nil) default: cli.HelpCopy() } @@ -230,7 +230,7 @@ func main() { other.PrintError("Failed to age entries: "+err.Error(), 1) } case 4: - verifyEntries(front.InputHidden("RCW Password:")) + verifyEntries(front.InputSecret("RCW Password:")) case 5: oldPassword := confirmRCWPassword("old") newPassword := confirmRCWPassword("new") @@ -264,8 +264,8 @@ func main() { func confirmRCWPassword(lowercasePrefix string) []byte { for { // get master password - rcwPass := front.InputHidden(strings.ToUpper(string(lowercasePrefix[0])) + lowercasePrefix[1:] + " master password:") - if !bytes.Equal(rcwPass, front.InputHidden("Confirm "+lowercasePrefix+" master password:")) || len(rcwPass) == 0 { + rcwPass := front.InputSecret(strings.ToUpper(string(lowercasePrefix[0])) + lowercasePrefix[1:] + " master password:") + if !bytes.Equal(rcwPass, front.InputSecret("Confirm "+lowercasePrefix+" master password:")) || len(rcwPass) == 0 { fmt.Println(back.AnsiError + "Passwords do not match or password is invalid" + back.AnsiReset) continue } diff --git a/src/cli/1globals.go b/src/cli/1globals.go index c0f25aa..e73450a 100644 --- a/src/cli/1globals.go +++ b/src/cli/1globals.go @@ -19,5 +19,5 @@ const ( ) func init() { - global.GetPassword = front.InputHidden + global.GetPassword = front.InputSecret } diff --git a/src/cli/add.go b/src/cli/add.go index 6f96dd8..514be8f 100644 --- a/src/cli/add.go +++ b/src/cli/add.go @@ -18,24 +18,24 @@ func AddEntry(realPath string, entryType uint8) { var decSlice []string - var password string + var password []byte if entryType < 2 { username := front.Input("Username:") // determine whether to generate the password if entryType == 0 { - password = string(front.InputHidden("Password:")) + password = front.InputSecret("Password:") } else { password = inputPasswordGen() } - totp := string(front.InputHidden("TOTP secret:")) + totp := string(front.InputSecret("TOTP secret:")) url := front.Input("URL:") if front.InputBinary("Add a note to this entry?") { note, _ := editNote([]string{}) - decSlice = append([]string{password, username, totp, url}, note...) + decSlice = append([]string{string(password), username, totp, url}, note...) } else { - decSlice = []string{password, username, totp, url} + decSlice = []string{string(password), username, totp, url} } } else { note, _ := editNote([]string{}) @@ -43,9 +43,9 @@ func AddEntry(realPath string, entryType uint8) { } // write and preview the new entry - if password != "" { - writeEntryCLI(realPath, decSlice, true, "") + if password != nil { + writeEntryCLI(realPath, decSlice, true, nil) } else { - writeEntryCLI(realPath, decSlice, false, "") + writeEntryCLI(realPath, decSlice, false, nil) } } diff --git a/src/cli/copyMenu.go b/src/cli/copyMenu.go index d0402c1..1121bac 100644 --- a/src/cli/copyMenu.go +++ b/src/cli/copyMenu.go @@ -21,7 +21,7 @@ import ( // fields without having to re-decrypt each time. decSlice can be left nil // to decrypt the entry specified by vanityPath. If the entry was provided // decrypted, CopyMenu assumes it was edited and triggers a sync. -func CopyMenu(vanityPath string, decSlice []string, oldPassword string) { +func CopyMenu(vanityPath string, decSlice []string, oldPassword []byte) { realPath := global.GetRealPath(vanityPath) var err error if decSlice == nil { @@ -45,7 +45,7 @@ func CopyMenu(vanityPath string, decSlice []string, oldPassword string) { for i := range indices { if len(decSlice) > indices[i] && decSlice[indices[i]] != "" { fieldOptions = append(fieldOptions, fieldStrings[i]) - if indices[i] == 0 && oldPassword != "" { + if indices[i] == 0 && oldPassword != nil { fieldOptions = append(fieldOptions, "Old Password") } } @@ -65,7 +65,7 @@ func CopyMenu(vanityPath string, decSlice []string, oldPassword string) { signal.Notify(sigChan, os.Interrupt, syscall.SIGINT, syscall.SIGTERM) go func() { <-sigChan - if err = clip.ClearProcess(""); err != nil { + if err = clip.ClearProcess(nil); err != nil { other.PrintError("Failed to clear clipboard on exit: "+err.Error(), global.ErrorClipboard) } os.Exit(0) @@ -105,7 +105,7 @@ func CopyMenu(vanityPath string, decSlice []string, oldPassword string) { } selectedField = fieldOptions[choice-1] if selectedField == "Old Password" { - if err = clip.CopyString(false, oldPassword); err != nil { + if err = clip.CopyBytes(false, oldPassword); err != nil { other.PrintError("Failed to copy old password to clipboard: "+err.Error(), global.ErrorClipboard) } continue @@ -113,14 +113,13 @@ func CopyMenu(vanityPath string, decSlice []string, oldPassword string) { choice = indices[slices.Index(fieldStrings, selectedField)] if choice == 2 { fmt.Println(back.AnsiWarning + "[Starting]" + back.AnsiReset + " TOTP clipboard refresher") - errorChan := make(chan error) + errorChan := make(chan error, 1) done := make(chan bool) go clip.TOTPCopier(decSlice[2], errorChan, done) // block until first successful copy if err = <-errorChan; err != nil { other.PrintError("Failed to copy TOTP token: "+err.Error(), global.ErrorClipboard) } - fmt.Println(back.AnsiGreen + "[Started]" + back.AnsiReset + " TOTP clipboard refresher") // block until the user sends "q" for { input := front.Input("Service will run until 'q' is entered:") @@ -129,9 +128,9 @@ func CopyMenu(vanityPath string, decSlice []string, oldPassword string) { } } close(done) - fmt.Println(back.AnsiBlue + "\n[Stopped]" + back.AnsiReset + " TOTP clipboard refresher") + fmt.Println(back.AnsiBlue + "[Stopped]" + back.AnsiReset + " TOTP clipboard refresher") } else { - if err = clip.CopyString(false, decSlice[choice]); err != nil { + if err = clip.CopyBytes(false, []byte(decSlice[choice])); err != nil { other.PrintError("Failed to copy field to clipboard: "+err.Error(), global.ErrorClipboard) } } diff --git a/src/cli/edit.go b/src/cli/edit.go index 7d22a4b..ea0db07 100644 --- a/src/cli/edit.go +++ b/src/cli/edit.go @@ -34,15 +34,15 @@ func EditEntryField(realPath string, field int) { } // edit the field - var oldPassword string + var oldPassword []byte switch field { case 0: - oldPassword = decSlice[field] - decSlice[field] = string(front.InputHidden("Password:")) + oldPassword = []byte(decSlice[field]) + decSlice[field] = string(front.InputSecret("Password:")) case 1: decSlice[field] = front.Input("Username:") case 2: - decSlice[field] = string(front.InputHidden("TOTP secret:")) + decSlice[field] = string(front.InputSecret("TOTP secret:")) case 3: decSlice[field] = front.Input("URL:") case 4: // edit notes fields @@ -75,8 +75,8 @@ func GenUpdate(realPath string) { } // generate a new password - oldPassword := decSlice[0] - decSlice[0] = inputPasswordGen() + oldPassword := []byte(decSlice[0]) + decSlice[0] = string(inputPasswordGen()) // write and preview the modified entry writeEntryCLI(realPath, decSlice, true, oldPassword) diff --git a/src/cli/printInfo.go b/src/cli/printInfo.go index 0fc676c..b782039 100644 --- a/src/cli/printInfo.go +++ b/src/cli/printInfo.go @@ -152,7 +152,7 @@ func Version() { ansiVersionOutline + "<><><><><><><><><><><><><><>-<><><><><><><><><><><><><><>\n" + "\\" + ansiBlackOnWhite + " " + back.AnsiReset + ansiVersionOutline + "/\n" + "\\" + ansiBlackOnWhite + " MUTN v" + MUTNVersion + " " + back.AnsiReset + ansiVersionOutline + "/\n" + - "\\" + ansiBlackOnWhite + " The Cryptic Cuts Update " + back.AnsiReset + ansiVersionOutline + "/\n" + + "\\" + ansiBlackOnWhite + " The \"Hardy Herd\" Update " + back.AnsiReset + ansiVersionOutline + "/\n" + "\\" + ansiBlackOnWhite + " " + back.AnsiReset + ansiVersionOutline + "/\n" + "\\" + ansiBlackOnWhite + " Built with libmutton v" + global.LibmuttonVersion + " " + back.AnsiReset + ansiVersionOutline + "/\n" + "\\" + ansiBlackOnWhite + " " + back.AnsiReset + ansiVersionOutline + "/\n" + diff --git a/src/cli/utilitiesMisc.go b/src/cli/utilitiesMisc.go index 1a33436..8fca326 100644 --- a/src/cli/utilitiesMisc.go +++ b/src/cli/utilitiesMisc.go @@ -6,12 +6,12 @@ import ( "github.com/rwinkhart/go-boilerplate/back" "github.com/rwinkhart/go-boilerplate/front" "github.com/rwinkhart/go-boilerplate/other" - "github.com/rwinkhart/go-boilerplate/stringy" + "github.com/rwinkhart/go-boilerplate/security" "github.com/rwinkhart/libmutton/core" ) // inputPasswordGen prompts the user for password generation parameters and returns a generated password as a string. -func inputPasswordGen() string { +func inputPasswordGen() []byte { passLength := front.InputInt("Password length:", 1, -1) fmt.Println() passCharset := uint8(front.InputMenuGen("Password complexity:", []string{"Simple", "Complex", "Ultra Complex (not compatible with many services)"})) @@ -24,11 +24,11 @@ func inputPasswordGen() string { complexity = 0.2 // (ultra) complex // 2 and 3 indicate complex and ultra complex charsets, respectively } - return stringy.StringGen(passLength, complexity, passCharset) + return security.BytesGen(passLength, complexity, passCharset) } // writeEntryCLI writes decSlice to realPath and previews it (errors if no data is supplied). -func writeEntryCLI(realPath string, decSlice []string, passwordIsNew bool, oldPassword string) { +func writeEntryCLI(realPath string, decSlice []string, passwordIsNew bool, oldPassword []byte) { if core.EntryIsNotEmpty(decSlice) { if err := core.WriteEntry(realPath, decSlice, passwordIsNew, nil); err != nil { other.PrintError("Failed to write entry: "+err.Error(), back.ErrorWrite)