1
0
mirror of https://github.com/getsops/sops.git synced 2026-02-05 12:45:21 +01:00

Add dedicated functions for \n and non string encoding

Signed-off-by: Lance Rushing <lance@lancerushing.com>
This commit is contained in:
Lance Rushing
2023-11-27 23:03:14 -08:00
committed by Felix Fontein
parent 1161ec17a1
commit 32dbc6be13
4 changed files with 121 additions and 79 deletions

View File

@@ -48,7 +48,7 @@ func (store *Store) LoadEncryptedFile(in []byte) (sops.Tree, error) {
}
}
metadata, err := stores.Unflatten(mdMap)
metadata, err := stores.UnflattenMetadata(mdMap)
if err != nil {
return sops.Tree{}, err
}
@@ -100,7 +100,7 @@ func (store *Store) LoadPlainFile(in []byte) (sops.TreeBranches, error) {
// runtime object
func (store *Store) EmitEncryptedFile(in sops.Tree) ([]byte, error) {
metadata := stores.MetadataFromInternal(in.Metadata)
mdItems, err := stores.Flatten(metadata)
mdItems, err := stores.FlattenMetadata(metadata)
if err != nil {
return nil, err
}

View File

@@ -44,10 +44,10 @@ func flattenValue(value interface{}) interface{} {
return output
}
// flattenMap flattens a map with potentially nested maps into a flat
// Flatten flattens a map with potentially nested maps into a flat
// map. Only string keys are allowed on both the top-level map and
// child maps.
func flattenMap(in map[string]interface{}) map[string]interface{} {
func Flatten(in map[string]interface{}) map[string]interface{} {
newMap := make(map[string]interface{})
for k, v := range in {
if flat, ok := flattenValue(v).(map[string]interface{}); ok {
@@ -61,7 +61,7 @@ func flattenMap(in map[string]interface{}) map[string]interface{} {
return newMap
}
func Flatten(md Metadata) (map[string]interface{}, error) {
func FlattenMetadata(md Metadata) (map[string]interface{}, error) {
var mdMap map[string]interface{}
jsonBytes, err := json.Marshal(md)
if err != nil {
@@ -72,14 +72,9 @@ func Flatten(md Metadata) (map[string]interface{}, error) {
return nil, err
}
flat := flattenMap(mdMap)
macOnlyEncryptedToString(flat)
for k, v := range flat {
if s, ok := v.(string); ok {
flat[k] = strings.Replace(s, "\n", "\\n", -1)
}
}
flat := Flatten(mdMap)
encodeNonStrings(flat)
encodeNewLines(flat)
return flat, nil
}
@@ -93,7 +88,7 @@ type listToken struct {
position int
}
// tokenize converts a path generated by flattenMap to be used as a key
// tokenize converts a path generated by Flatten to be used as a key
// in the flattened map, and converts it to a slice of tokens
func tokenize(path string) []token {
const (
@@ -196,8 +191,8 @@ func unflatten(currentNode interface{}, currentToken, nextToken token, value int
return nil
}
// unflattenMap unflattens a map flattened by flattenMap
func unflattenMap(in map[string]interface{}) map[string]interface{} {
// Unflatten unflattens a map flattened by Flatten
func Unflatten(in map[string]interface{}) map[string]interface{} {
newMap := make(map[string]interface{})
for k, v := range in {
var current interface{} = newMap
@@ -209,16 +204,11 @@ func unflattenMap(in map[string]interface{}) map[string]interface{} {
return newMap
}
// Unflatten unflattens a map flattened by Flatten into Metadata
func Unflatten(in map[string]interface{}) (Metadata, error) {
for k, v := range in {
if s, ok := v.(string); ok {
in[k] = strings.Replace(s, "\\n", "\n", -1)
}
}
m := unflattenMap(in)
macOnlyEncryptedToBool(m)
// UnflattenMetadata unflattens a map flattened by FlattenMetadata into Metadata
func UnflattenMetadata(in map[string]interface{}) (Metadata, error) {
decodeNewLines(in)
decodeNonStrings(in)
m := Unflatten(in)
var md Metadata
jsonBytes, err := json.Marshal(m)
if err != nil {
@@ -228,7 +218,24 @@ func Unflatten(in map[string]interface{}) (Metadata, error) {
return md, err
}
func macOnlyEncryptedToBool(m map[string]interface{}) {
func decodeNewLines(m map[string]interface{}) {
for k, v := range m {
if s, ok := v.(string); ok {
m[k] = strings.Replace(s, "\\n", "\n", -1)
}
}
}
func encodeNewLines(m map[string]interface{}) {
for k, v := range m {
if s, ok := v.(string); ok {
m[k] = strings.Replace(s, "\n", "\\n", -1)
}
}
}
// decodeNonStrings will look for known keys that are not strings and decode to the appropriate type
func decodeNonStrings(m map[string]interface{}) {
if v, ok := m["mac_only_encrypted"]; ok {
m["mac_only_encrypted"] = false
if v == "true" {
@@ -237,7 +244,8 @@ func macOnlyEncryptedToBool(m map[string]interface{}) {
}
}
func macOnlyEncryptedToString(m map[string]interface{}) {
// encodeNonStrings will look for known keys that are not strings and will encode it to strings
func encodeNonStrings(m map[string]interface{}) {
if v, found := m["mac_only_encrypted"]; found {
if vBool, ok := v.(bool); ok {
m["mac_only_encrypted"] = "false"

View File

@@ -13,9 +13,9 @@ func TestFlat(t *testing.T) {
expected := map[string]interface{}{
"foo": "bar",
}
flattened := flattenMap(input)
flattened := Flatten(input)
assert.Equal(t, expected, flattened)
unflattened := unflattenMap(flattened)
unflattened := Unflatten(flattened)
assert.Equal(t, input, unflattened)
}
@@ -30,9 +30,9 @@ func TestMap(t *testing.T) {
"foo" + mapSeparator + "bar": 0,
"foo" + mapSeparator + "baz": 0,
}
flattened := flattenMap(input)
flattened := Flatten(input)
assert.Equal(t, expected, flattened)
unflattened := unflattenMap(flattened)
unflattened := Unflatten(flattened)
assert.Equal(t, input, unflattened)
}
@@ -47,9 +47,9 @@ func TestFlattenMapMoreNesting(t *testing.T) {
expected := map[string]interface{}{
"foo" + mapSeparator + "bar" + mapSeparator + "baz": 0,
}
flattened := flattenMap(input)
flattened := Flatten(input)
assert.Equal(t, expected, flattened)
unflattened := unflattenMap(flattened)
unflattened := Unflatten(flattened)
assert.Equal(t, input, unflattened)
}
@@ -62,9 +62,9 @@ func TestFlattenList(t *testing.T) {
expected := map[string]interface{}{
"foo" + listSeparator + "0": 0,
}
flattened := flattenMap(input)
flattened := Flatten(input)
assert.Equal(t, expected, flattened)
unflattened := unflattenMap(flattened)
unflattened := Unflatten(flattened)
assert.Equal(t, input, unflattened)
}
@@ -79,13 +79,13 @@ func TestFlattenListWithMap(t *testing.T) {
expected := map[string]interface{}{
"foo" + listSeparator + "0" + mapSeparator + "bar": 0,
}
flattened := flattenMap(input)
flattened := Flatten(input)
assert.Equal(t, expected, flattened)
unflattened := unflattenMap(flattened)
unflattened := Unflatten(flattened)
assert.Equal(t, input, unflattened)
}
func TestFlattenMap(t *testing.T) {
func TestFlatten(t *testing.T) {
input := map[string]interface{}{
"foo": "bar",
"baz": map[string]interface{}{
@@ -106,9 +106,9 @@ func TestFlattenMap(t *testing.T) {
"qux" + listSeparator + "1": 1,
"qux" + listSeparator + "2": 2,
}
flattened := flattenMap(input)
flattened := Flatten(input)
assert.Equal(t, expected, flattened)
unflattened := unflattenMap(flattened)
unflattened := Unflatten(flattened)
assert.Equal(t, input, unflattened)
}
@@ -140,38 +140,7 @@ func TestTokenizeNested(t *testing.T) {
assert.Equal(t, expected, tokenized)
}
func TestMacOnlyEncryptedToBool(t *testing.T) {
tests := []struct {
input map[string]interface{}
want map[string]interface{}
}{
{map[string]interface{}{"mac_only_encrypted": "false"}, map[string]interface{}{"mac_only_encrypted": false}},
{map[string]interface{}{"mac_only_encrypted": "true"}, map[string]interface{}{"mac_only_encrypted": true}},
{map[string]interface{}{"mac_only_encrypted": "something-else"}, map[string]interface{}{"mac_only_encrypted": false}},
}
for _, tt := range tests {
macOnlyEncryptedToBool(tt.input)
assert.Equal(t, tt.want, tt.input)
}
}
func TestMacOnlyEncryptedToString(t *testing.T) {
tests := []struct {
input map[string]interface{}
want map[string]interface{}
}{
{map[string]interface{}{"mac_only_encrypted": false}, map[string]interface{}{"mac_only_encrypted": "false"}},
{map[string]interface{}{"mac_only_encrypted": true}, map[string]interface{}{"mac_only_encrypted": "true"}},
}
for _, tt := range tests {
macOnlyEncryptedToString(tt.input)
assert.Equal(t, tt.want, tt.input)
}
}
func TestFlatten(t *testing.T) {
func TestFlattenMetadata(t *testing.T) {
tests := []struct {
input Metadata
want map[string]interface{}
@@ -183,7 +152,7 @@ func TestFlatten(t *testing.T) {
}
for _, tt := range tests {
got, err := Flatten(tt.input)
got, err := FlattenMetadata(tt.input)
assert.NoError(t, err)
for k, v := range tt.want {
assert.Equal(t, v, got[k])
@@ -191,7 +160,7 @@ func TestFlatten(t *testing.T) {
}
}
func TestFlattenToUnflatten(t *testing.T) {
func TestFlattenMetadataToUnflattenMetadata(t *testing.T) {
tests := []struct {
input Metadata
}{
@@ -203,10 +172,75 @@ func TestFlattenToUnflatten(t *testing.T) {
}
for _, tt := range tests {
flat, err := Flatten(tt.input)
flat, err := FlattenMetadata(tt.input)
assert.NoError(t, err)
md, err := Unflatten(flat)
md, err := UnflattenMetadata(flat)
assert.NoError(t, err)
assert.Equal(t, tt.input, md)
}
}
func TestDecodeNewLines(t *testing.T) {
tests := []struct {
input map[string]interface{}
want map[string]interface{}
}{
{map[string]interface{}{"mac": "line1\\nline2"}, map[string]interface{}{"mac": "line1\nline2"}},
{map[string]interface{}{"mac": "line1\\n\\n\\nline2\\n\\nline3"}, map[string]interface{}{"mac": "line1\n\n\nline2\n\nline3"}},
}
for _, tt := range tests {
decodeNewLines(tt.input)
for k, v := range tt.want {
assert.Equal(t, v, tt.input[k])
}
}
}
func TestEncodeNewLines(t *testing.T) {
tests := []struct {
input map[string]interface{}
want map[string]interface{}
}{
{map[string]interface{}{"mac": "line1\nline2"}, map[string]interface{}{"mac": "line1\\nline2"}},
{map[string]interface{}{"mac": "line1\n\n\nline2\n\nline3"}, map[string]interface{}{"mac": "line1\\n\\n\\nline2\\n\\nline3"}},
}
for _, tt := range tests {
encodeNewLines(tt.input)
for k, v := range tt.want {
assert.Equal(t, v, tt.input[k])
}
}
}
func TestDecodeNonStrings(t *testing.T) {
tests := []struct {
input map[string]interface{}
want map[string]interface{}
}{
{map[string]interface{}{"mac_only_encrypted": "false"}, map[string]interface{}{"mac_only_encrypted": false}},
{map[string]interface{}{"mac_only_encrypted": "true"}, map[string]interface{}{"mac_only_encrypted": true}},
{map[string]interface{}{"mac_only_encrypted": "something-else"}, map[string]interface{}{"mac_only_encrypted": false}},
}
for _, tt := range tests {
decodeNonStrings(tt.input)
assert.Equal(t, tt.want, tt.input)
}
}
func TestEncodeNonStrings(t *testing.T) {
tests := []struct {
input map[string]interface{}
want map[string]interface{}
}{
{map[string]interface{}{"mac_only_encrypted": false}, map[string]interface{}{"mac_only_encrypted": "false"}},
{map[string]interface{}{"mac_only_encrypted": true}, map[string]interface{}{"mac_only_encrypted": "true"}},
}
for _, tt := range tests {
encodeNonStrings(tt.input)
assert.Equal(t, tt.want, tt.input)
}
}

View File

@@ -187,7 +187,7 @@ func (store *Store) iniSectionToMetadata(sopsSection *ini.Section) (stores.Metad
for k, v := range sopsSection.KeysHash() {
metadataHash[k] = v
}
return stores.Unflatten(metadataHash)
return stores.UnflattenMetadata(metadataHash)
}
// LoadPlainFile loads a plaintext INI file's bytes onto a sops.TreeBranches runtime object
@@ -221,7 +221,7 @@ func (store *Store) EmitEncryptedFile(in sops.Tree) ([]byte, error) {
}
func (store *Store) encodeMetadataToIniBranch(md stores.Metadata) (sops.TreeBranch, error) {
flat, err := stores.Flatten(md)
flat, err := stores.FlattenMetadata(md)
if err != nil {
return nil, err
}