feat: share.keyRules, how long and varied a share key must be
Sharing refuses a key shorter than minLength characters (default 32, never under 16) or of fewer than minClasses kinds of lowercase, uppercase, digits and symbols (default 2, so random hex passes), saying so without showing it. Opening doesn't check. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This commit is contained in:
13 files changed
+156
-21
No files matched your search
@@ -24,6 +24,7 @@ import (
|
||||
"git.kebcloud.no/keb/lazysecrets/internal/commands"
|
||||
"git.kebcloud.no/keb/lazysecrets/internal/provider"
|
||||
"git.kebcloud.no/keb/lazysecrets/internal/ref"
|
||||
"git.kebcloud.no/keb/lazysecrets/internal/share"
|
||||
"git.kebcloud.no/keb/lazysecrets/internal/store"
|
||||
"git.kebcloud.no/keb/lazysecrets/internal/ui/icons"
|
||||
"git.kebcloud.no/keb/lazysecrets/internal/ui/keys"
|
||||
@@ -84,6 +85,8 @@ type Options struct {
|
||||
// ShareKey is the config's share.key: the secret that encrypts shared
|
||||
// env files, for repos whose .lazysecrets.yaml names none.
|
||||
ShareKey string
|
||||
// ShareRules are what a key's value must be to share with it.
|
||||
ShareRules share.Rules
|
||||
// Dir is the folder lazysecrets runs in: it goes back to where it was
|
||||
// left there, unless it was started on a link. "" doesn't.
|
||||
Dir string
|
||||
@@ -273,7 +276,8 @@ type Model struct {
|
||||
linked []provider.Container // vaults opened from links, this session
|
||||
open *ref.Ref // reference to open at startup
|
||||
|
||||
shareKey string // the config's share.key
|
||||
shareKey string // the config's share.key
|
||||
shareRules share.Rules // and share.keyRules
|
||||
|
||||
// A lazysecrets:// link found on the clipboard, offered in the footer.
|
||||
dismissedLink string // the last link offered: not offered again
|
||||
@@ -460,7 +464,7 @@ func New(providers []provider.Provider, opts Options) Model {
|
||||
m.spinning = true
|
||||
|
||||
m.cachePath, m.placeDir, m.sourcesAt = opts.CacheFile, opts.Dir, map[string]time.Time{}
|
||||
m.shareKey = opts.ShareKey
|
||||
m.shareKey, m.shareRules = opts.ShareKey, opts.ShareRules
|
||||
m.configFiles, m.statePath, m.changelog = opts.ConfigFiles, opts.StatePath, changelogFields(opts.Changelog)
|
||||
if m.cachePath != "" {
|
||||
m.loadCacheFile()
|
||||
|
||||
@@ -158,14 +158,14 @@ func (m *Model) pickPasted(text string) tea.Cmd {
|
||||
|
||||
// seal reads the key and encrypts the files with it.
|
||||
func (m *Model) seal(s sharing) tea.Cmd {
|
||||
read := m.keyReader()
|
||||
read, rules := m.keyReader(), m.shareRules
|
||||
m.flash = flash{text: "encrypting with " + s.key.Name + "…"}
|
||||
return func() tea.Msg {
|
||||
v, version, err := read(s.key)
|
||||
if err != nil {
|
||||
return sealedMsg{err: fmt.Errorf("reading the key %s: %w", s.key.Name, err)}
|
||||
}
|
||||
link, err := share.Seal(share.Header{Key: share.Pin(s.key, version), Repo: s.repo.Detail}, []byte(v.Reveal()), s.files)
|
||||
link, err := share.Seal(share.Header{Key: share.Pin(s.key, version), Repo: s.repo.Detail}, []byte(v.Reveal()), s.files, rules)
|
||||
return sealedMsg{link: link, n: len(s.files), err: err}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,12 +12,16 @@ import (
|
||||
|
||||
"git.kebcloud.no/keb/lazysecrets/internal/provider/fake"
|
||||
"git.kebcloud.no/keb/lazysecrets/internal/provider/repo"
|
||||
"git.kebcloud.no/keb/lazysecrets/internal/share"
|
||||
)
|
||||
|
||||
// scryptWait is how long the driver waits for encrypting and decrypting,
|
||||
// which take about a second on purpose.
|
||||
const scryptWait = 20 * time.Second
|
||||
|
||||
// demoRules take the demo key, made up to be remembered.
|
||||
var demoRules = share.Rules{MinLength: 16, MinClasses: 1}
|
||||
|
||||
// cloneWith makes a clone of git.example.com/team/app holding files.
|
||||
func cloneWith(t *testing.T, files map[string]string) string {
|
||||
t.Helper()
|
||||
@@ -35,7 +39,7 @@ func cloneWith(t *testing.T, files map[string]string) string {
|
||||
func shareFrom(t *testing.T, root string, paste bool) string {
|
||||
t.Helper()
|
||||
clip := &fakeClipboard{}
|
||||
d := newDriverOpts(t, Options{Clipboard: clip}, fake.Azure(0), repo.New(repo.Settings{Dir: root}))
|
||||
d := newDriverOpts(t, Options{Clipboard: clip, ShareRules: demoRules}, fake.Azure(0), repo.New(repo.Settings{Dir: root}))
|
||||
d.wait = scryptWait
|
||||
if src, ok := d.m.selectedSource(); !ok || src.ID != root {
|
||||
t.Fatalf("selected %v, want the repo", src.ID)
|
||||
|
||||
Reference in new issue
Block a user