Skip to content

Commit

Permalink
Implement a simple, robust and predictable KV parser (gopasspw#659)
Browse files Browse the repository at this point in the history
  • Loading branch information
dominikschulz authored Feb 21, 2018
1 parent 5437118 commit c63520c
Show file tree
Hide file tree
Showing 10 changed files with 274 additions and 291 deletions.
72 changes: 0 additions & 72 deletions action/fix.go

This file was deleted.

68 changes: 0 additions & 68 deletions action/fix_test.go

This file was deleted.

18 changes: 0 additions & 18 deletions commands.go
Original file line number Diff line number Diff line change
Expand Up @@ -339,24 +339,6 @@ func getCommands(ctx context.Context, action *ap.Action, app *cli.App) []cli.Com
},
},
},
{
Name: "fix",
Usage: "Upgrade secrets",
Before: func(c *cli.Context) error { return action.Initialized(withGlobalFlags(ctx, c), c) },
Action: func(c *cli.Context) error {
return action.Fix(withGlobalFlags(ctx, c), c)
},
Flags: []cli.Flag{
cli.BoolFlag{
Name: "check, c",
Usage: "Only report",
},
cli.BoolFlag{
Name: "force, f",
Usage: "Auto-correct any errors, do not ask",
},
},
},
{
Name: "generate",
Usage: "Generate a new password",
Expand Down
2 changes: 1 addition & 1 deletion commands_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,7 @@ func TestGetCommands(t *testing.T) {
c := cli.NewContext(app, fs, nil)

commands := getCommands(ctx, act, app)
assert.Equal(t, 31, len(commands))
assert.Equal(t, 30, len(commands))

prefix := ""
testCommands(t, c, commands, prefix)
Expand Down
93 changes: 93 additions & 0 deletions store/secret/kv.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
package secret

import (
"bufio"
"fmt"
"sort"
"strings"
)

func (s *Secret) decodeKV() error {
mayBeYAML := false
scanner := bufio.NewScanner(strings.NewReader(s.body))
data := make(map[string]interface{}, strings.Count(s.body, "\n"))
for scanner.Scan() {
line := scanner.Text()
if strings.HasPrefix(line, "---") {
mayBeYAML = true
}
parts := strings.SplitN(line, ":", 2)
if len(parts) < 1 {
continue
}
if len(parts) == 1 && strings.HasPrefix(parts[0], " ") {
mayBeYAML = true
}
for i, part := range parts {
parts[i] = strings.TrimSpace(part)
}
// preserve key only entries
if len(parts) < 2 {
data[parts[0]] = ""
continue
}
if strings.HasPrefix(parts[1], "|") {
mayBeYAML = true
}
data[parts[0]] = parts[1]
}
if mayBeYAML {
docSep, err := s.decodeYAML()
if debug {
fmt.Printf("[DEBUG] decodeKV() - mayBeYAML - err: %s\n", err)
}
if docSep && err == nil && s.data != nil {
return nil
}
}
if debug {
fmt.Printf("[DEBUG] decodeKV() - simple KV\n")
}
s.data = data
return nil
}

func (s *Secret) encodeKV() error {
if s.data == nil {
return nil
}
keys := make([]string, 0, len(s.data))
for key := range s.data {
keys = append(keys, key)
}
sort.Strings(keys)
var buf strings.Builder
mayBeYAML := false
for _, key := range keys {
sv, ok := s.data[key].(string)
if !ok {
mayBeYAML = true
continue
}
_, _ = buf.WriteString(key)
_, _ = buf.WriteString(": ")
_, _ = buf.WriteString(sv)
_, _ = buf.WriteString("\n")
if strings.Contains(sv, "\n") {
mayBeYAML = true
}
}
if mayBeYAML {
if err := s.encodeYAML(); err == nil {
if debug {
fmt.Printf("[DEBUG] encodeKV() - mayBeYAML - OK\n")
}
return nil
}
}
if debug {
fmt.Printf("[DEBUG] encodeKV() - simple KV\n")
}
s.body = buf.String()
return nil
}
43 changes: 43 additions & 0 deletions store/secret/kv_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
package secret

import (
"testing"

"github.com/stretchr/testify/assert"
)

func TestYAMLFromHereDoc(t *testing.T) {
t.Logf("Parse K/V w/ HereDoc as YAML, not K/V")
mlValue := `somepw
---
foo: |
bar
baz
key: value
`
s, err := Parse([]byte(mlValue))
assert.NoError(t, err)
assert.NotNil(t, s)
v, err := s.Value("foo")
assert.NoError(t, err)
assert.Equal(t, "bar\nbaz\n", v)
}

func TestKVContentFromInvalidYAML(t *testing.T) {
t.Logf("Retrieve content from invalid YAML (#375)")
mlValue := `somepasswd
---
Test / test.com
username: [email protected]
password: somepasswd
url: http://www.test.com/`
s, err := Parse([]byte(mlValue))
assert.NoError(t, err)
assert.NotNil(t, s)
v, err := s.Value("Test / test.com")
assert.NoError(t, err)
assert.Equal(t, "", v)

// read back key
assert.Equal(t, mlValue, s.String())
}
Loading

0 comments on commit c63520c

Please sign in to comment.