satisfy linter

This commit is contained in:
2026-08-31 22:02:07 +02:00
parent cafc65aea7
commit 49eb1033ef
23 changed files with 327 additions and 220 deletions

View File

@@ -1,3 +1,5 @@
package app
/*
Copyright © 2024-2025 Thomas von Dein
@@ -14,7 +16,6 @@ GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package app
import (
"fmt"
@@ -125,17 +126,24 @@ func (attr *DbAttr) GetFileValue() error {
}
} else {
// read from console stdin
var input string
var data string
var (
input string
data string
buf strings.Builder
)
for {
_, err := fmt.Scanln(&input)
if err != nil {
break
}
data += input + "\n"
buf.WriteString(input)
buf.WriteString("\n")
}
data += buf.String()
attr.Val = []byte(data)
}

View File

@@ -1,3 +1,5 @@
package app
/*
Copyright © 2024 Thomas von Dein
@@ -14,7 +16,6 @@ GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package app
import (
"crypto/rand"
@@ -35,7 +36,7 @@ const (
ArgonParallel uint8 = 2
ArgonSaltLen int = 16
ArgonKeyLen uint32 = 32
B64SaltLen int = 16 //22
B64SaltLen int = 16 // 22
)
type Key struct {
@@ -47,7 +48,8 @@ type Key struct {
// password
func AskForPassword() ([]byte, error) {
fmt.Fprint(os.Stderr, "Password: ")
pass, err := term.ReadPassword(int(syscall.Stdin))
pass, err := term.ReadPassword(syscall.Stdin)
if err != nil {
return nil, fmt.Errorf("failed to read password: %w", err)
}
@@ -57,6 +59,8 @@ func AskForPassword() ([]byte, error) {
return pass, nil
}
// DeriveKey derives a key from password.
//
// We're using the Argon2id key derivation algorithm to derive a
// secure key from the given password. This is important, because
// users might use unsecure passwords. The resulting encrypted data
@@ -78,7 +82,7 @@ func DeriveKey(password []byte, salt []byte) (*Key, error) {
}
hash := argon2.IDKey(
[]byte(password), salt,
password, salt,
ArgonIter,
ArgonMem,
ArgonParallel,
@@ -95,6 +99,7 @@ func DeriveKey(password []byte, salt []byte) (*Key, error) {
// Retrieve a random chunk of given size
func GetRandom(size int, capacity int) ([]byte, error) {
buf := make([]byte, size, capacity)
_, err := rand.Read(buf)
if err != nil {
return nil, fmt.Errorf("failed to retrieve random bytes: %w", err)
@@ -103,7 +108,7 @@ func GetRandom(size int, capacity int) ([]byte, error) {
return buf, nil
}
// Encrypt clear text given in attr using ChaCha20 and auhtenticate
// Encrypt clear text given in attr using ChaCha20 and authenticate
// using the mac Poly1305. The cipher text will be put into attr, thus
// modifying it.
//
@@ -143,7 +148,7 @@ func Encrypt(pass []byte, attr *DbAttr) error {
// Do the reverse
func Decrypt(pass []byte, cipherb []byte) ([]byte, error) {
if len(cipherb) < B64SaltLen {
return nil, fmt.Errorf("encrypted cipher block too small")
return nil, errors.New("encrypted cipher block too small")
}
key, err := DeriveKey(pass, cipherb[0:B64SaltLen])

182
app/db.go
View File

@@ -25,6 +25,7 @@ import (
"os"
"path/filepath"
"regexp"
"slices"
"strings"
"time"
@@ -64,7 +65,7 @@ type DbTag struct {
}
func (entry *DbEntry) Taglist() string {
return strings.Join(entry.Tags, ",")
return strings.Join(entry.GetTags(), ",")
}
const BucketData string = "data"
@@ -113,6 +114,7 @@ func (db *DB) Open() error {
}
db.DB = b
return nil
}
@@ -128,8 +130,10 @@ func (db *DB) List(attr *DbAttr, fulltext bool) (DbEntries, error) {
}
defer db.Close()
var entries DbEntries
var filter *regexp.Regexp
var (
entries DbEntries
filter *regexp.Regexp
)
if len(attr.Args) > 0 {
// via cli
@@ -158,7 +162,7 @@ func (db *DB) List(attr *DbAttr, fulltext bool) (DbEntries, error) {
databucket := root.Bucket([]byte("data"))
if databucket == nil {
return fmt.Errorf("failed to retrieve data sub bucket")
return errors.New("failed to retrieve data sub bucket")
}
err := bucket.ForEach(func(key, pbentry []byte) error {
@@ -169,7 +173,7 @@ func (db *DB) List(attr *DbAttr, fulltext bool) (DbEntries, error) {
if fulltext {
// avoid crash due to access fault
value := databucket.Get([]byte(entry.Key)) // empty is ok
value := databucket.Get([]byte(entry.GetKey())) // empty is ok
vc := make([]byte, len(value))
copy(vc, value)
entry.Value = string(vc)
@@ -179,26 +183,21 @@ func (db *DB) List(attr *DbAttr, fulltext bool) (DbEntries, error) {
switch {
case filter != nil:
if filter.MatchString(entry.Key) ||
filter.MatchString(strings.Join(entry.Tags, " ")) {
if filter.MatchString(entry.GetKey()) ||
filter.MatchString(strings.Join(entry.GetTags(), " ")) {
include = true
}
if !entry.Binary && !include && fulltext {
if filter.MatchString(string(entry.Value)) {
if !entry.GetBinary() && !include && fulltext {
if filter.MatchString(entry.GetValue()) {
include = true
}
}
case len(attr.Tags) > 0:
for _, search := range attr.Tags {
for _, tag := range entry.Tags {
if tag == search {
include = true
break
}
}
if slices.Contains(entry.GetTags(), search) {
include = true
if include {
break
}
}
@@ -239,6 +238,7 @@ func (db *DB) Set(attr *DbAttr) error {
// any tags. if so, we initialize our update struct with these
// tags unless it has new tags configured.
slog.Debug("+++ GET")
oldentry, err := db.txGet(attr)
if err != nil {
if !strings.Contains(err.Error(), "no such key") {
@@ -247,9 +247,9 @@ func (db *DB) Set(attr *DbAttr) error {
}
if oldentry != nil {
if len(oldentry.Tags) > 0 && len(entry.Tags) == 0 {
if len(oldentry.GetTags()) > 0 && len(entry.GetTags()) == 0 {
// initialize update entry with tags from old entry
entry.Tags = oldentry.Tags
entry.Tags = oldentry.GetTags()
}
}
@@ -261,6 +261,7 @@ func (db *DB) Set(attr *DbAttr) error {
}
slog.Debug("+++ UPDATE")
err = db.DB.Update(func(tx *bolt.Tx) error {
// create root bucket
root, err := tx.CreateBucketIfNotExists([]byte(db.Bucket))
@@ -277,7 +278,7 @@ func (db *DB) Set(attr *DbAttr) error {
slog.Debug("opened/created buckets", "root", root, "data", bucket)
// write meta data
err = bucket.Put([]byte(entry.Key), []byte(pbentry))
err = bucket.Put([]byte(entry.GetKey()), pbentry)
if err != nil {
return fmt.Errorf("failed to insert data: %w", err)
}
@@ -289,14 +290,13 @@ func (db *DB) Set(attr *DbAttr) error {
}
// write value
err = databucket.Put([]byte(entry.Key), attr.Val)
err = databucket.Put([]byte(entry.GetKey()), attr.Val)
if err != nil {
return fmt.Errorf("failed to insert data: %w", err)
}
return nil
})
if err != nil {
return err
}
@@ -304,67 +304,6 @@ func (db *DB) Set(attr *DbAttr) error {
return nil
}
// internal DB getter, assumes db.DB has already been
// opened successfully. Do NOT call this w/o valid
// DB handle!
func (db *DB) txGet(attr *DbAttr) (*DbEntry, error) {
entry := DbEntry{}
err := db.DB.View(func(tx *bolt.Tx) error {
// root bucket
root := tx.Bucket([]byte(db.Bucket))
if root == nil {
return nil
}
// get meta sub bucket
bucket := root.Bucket([]byte("meta"))
if bucket == nil {
return nil
}
slog.Debug("opened buckets", "root", root, "data", bucket)
// retrieve meta data
pbentry := bucket.Get([]byte(attr.Key))
if pbentry == nil {
return fmt.Errorf("no such key: %s", attr.Key)
}
// put into struct
if err := proto.Unmarshal(pbentry, &entry); err != nil {
return fmt.Errorf("failed to unmarshal from protobuf: %w", err)
}
// get data sub bucket
databucket := root.Bucket([]byte("data"))
if databucket == nil {
return fmt.Errorf("failed to retrieve data sub bucket")
}
// retrieve actual data value
value := databucket.Get([]byte(attr.Key))
if len(value) == 0 {
return fmt.Errorf("no such key: %s", attr.Key)
}
// we need to make a copy of it, otherwise we'll get an
// "unexpected fault address" error
vc := make([]byte, len(value))
copy(vc, value)
entry.Value = string(vc)
return nil
})
if err != nil {
return nil, err
}
return &entry, nil
}
func (db *DB) Get(attr *DbAttr) (*DbEntry, error) {
if err := db.Open(); err != nil {
return nil, err
@@ -372,7 +311,6 @@ func (db *DB) Get(attr *DbAttr) (*DbEntry, error) {
defer db.Close()
entry, err := db.txGet(attr)
if err != nil {
return nil, fmt.Errorf("failed to read from DB: %w", err)
}
@@ -418,10 +356,11 @@ func (db *DB) Import(attr *DbAttr) (string, error) {
}
var entries DbEntries
now := time.Now()
newfile := db.Dbfile + now.Format("-02.01.2006T03:04.05")
if err := json.Unmarshal([]byte(attr.Val), &entries); err != nil {
if err := json.Unmarshal(attr.Val, &entries); err != nil {
return "", cleanError(newfile, fmt.Errorf("failed to unmarshal json: %w", err))
}
@@ -431,7 +370,6 @@ func (db *DB) Import(attr *DbAttr) (string, error) {
if err != nil {
return "", fmt.Errorf("failed to rename file %s to %s: %w", db.Dbfile, newfile, err)
}
}
// should now be a new db file
@@ -462,7 +400,7 @@ func (db *DB) Import(attr *DbAttr) (string, error) {
}
// write meta data
err = bucket.Put([]byte(entry.Key), []byte(pbentry))
err = bucket.Put([]byte(entry.GetKey()), pbentry)
if err != nil {
return fmt.Errorf("failed to insert data into DB: %w", err)
}
@@ -474,7 +412,7 @@ func (db *DB) Import(attr *DbAttr) (string, error) {
}
// write value
err = databucket.Put([]byte(entry.Key), []byte(entry.Value))
err = databucket.Put([]byte(entry.GetKey()), []byte(entry.GetValue()))
if err != nil {
return fmt.Errorf("failed to insert data: %w", err)
}
@@ -482,7 +420,6 @@ func (db *DB) Import(attr *DbAttr) (string, error) {
return nil
})
if err != nil {
return "", cleanError(newfile, err)
}
@@ -520,16 +457,14 @@ func (db *DB) Info() (*DbInfo, error) {
}
info.Buckets = append(info.Buckets, binfo)
return nil
})
if err != nil {
return fmt.Errorf("failed to read from DB: %w", err)
}
return nil
})
return info, err
@@ -559,7 +494,7 @@ func (db *DB) Getall(attr *DbAttr) (DbEntries, error) {
// get data sub bucket
databucket := root.Bucket([]byte("data"))
if databucket == nil {
return fmt.Errorf("failed to retrieve data sub bucket")
return errors.New("failed to retrieve data sub bucket")
}
slog.Debug("opened buckets", "root", root, "data", bucket)
@@ -572,7 +507,7 @@ func (db *DB) Getall(attr *DbAttr) (DbEntries, error) {
}
// retrieve the value from the data sub bucket
value := databucket.Get([]byte(entry.Key))
value := databucket.Get([]byte(entry.GetKey()))
// we need to make a copy of it, otherwise we'll get an
// "unexpected fault address" error
@@ -587,5 +522,66 @@ func (db *DB) Getall(attr *DbAttr) (DbEntries, error) {
return err
})
return entries, err
}
// internal DB getter, assumes db.DB has already been
// opened successfully. Do NOT call this w/o valid
// DB handle!
func (db *DB) txGet(attr *DbAttr) (*DbEntry, error) {
entry := DbEntry{}
err := db.DB.View(func(tx *bolt.Tx) error {
// root bucket
root := tx.Bucket([]byte(db.Bucket))
if root == nil {
return nil
}
// get meta sub bucket
bucket := root.Bucket([]byte("meta"))
if bucket == nil {
return nil
}
slog.Debug("opened buckets", "root", root, "data", bucket)
// retrieve meta data
pbentry := bucket.Get([]byte(attr.Key))
if pbentry == nil {
return fmt.Errorf("no such key: %s", attr.Key)
}
// put into struct
if err := proto.Unmarshal(pbentry, &entry); err != nil {
return fmt.Errorf("failed to unmarshal from protobuf: %w", err)
}
// get data sub bucket
databucket := root.Bucket([]byte("data"))
if databucket == nil {
return errors.New("failed to retrieve data sub bucket")
}
// retrieve actual data value
value := databucket.Get([]byte(attr.Key))
if len(value) == 0 {
return fmt.Errorf("no such key: %s", attr.Key)
}
// we need to make a copy of it, otherwise we'll get an
// "unexpected fault address" error
vc := make([]byte, len(value))
copy(vc, value)
entry.Value = string(vc)
return nil
})
if err != nil {
return nil, err
}
return &entry, nil
}

View File

@@ -1,3 +1,5 @@
package app
/*
Copyright © 2024 Thomas von Dein
@@ -14,18 +16,24 @@ GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package app
import "os"
import (
"errors"
"os"
)
func cleanError(file string, err error) error {
// remove given [backup] file and forward the given error
return os.Remove(file)
oserr := os.Remove(file)
if oserr != nil {
return errors.Join(err, oserr)
}
return err
}
func fileExists(filename string) bool {
info, err := os.Stat(filename)
if err != nil {
// return false on any error
return false