Skip to content

Commit

Permalink
Merge pull request getsops#1393 from felixfontein/encrypted-check
Browse files Browse the repository at this point in the history
Make check whether file contains invalid keys for encryption dependent on output store
  • Loading branch information
felixfontein authored Dec 29, 2023
2 parents cc0477c + 1bda828 commit 3ada89e
Show file tree
Hide file tree
Showing 9 changed files with 88 additions and 6 deletions.
9 changes: 4 additions & 5 deletions cmd/sops/encrypt.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,8 @@ func (err *fileAlreadyEncryptedError) Error() string {

func (err *fileAlreadyEncryptedError) UserError() string {
message := "The file you have provided contains a top-level entry called " +
"'sops'. This is generally due to the file already being encrypted. " +
"'sops', or for flat file formats top-level entries starting with " +
"'sops_'. This is generally due to the file already being encrypted. " +
"SOPS uses a top-level entry called 'sops' to store the metadata " +
"required to decrypt the file. For this reason, SOPS can not " +
"encrypt files that already contain such an entry.\n\n" +
Expand All @@ -47,10 +48,8 @@ func (err *fileAlreadyEncryptedError) UserError() string {
}

func ensureNoMetadata(opts encryptOpts, branch sops.TreeBranch) error {
for _, b := range branch {
if b.Key == "sops" {
return &fileAlreadyEncryptedError{}
}
if opts.OutputStore.HasSopsTopLevelKey(branch) {
return &fileAlreadyEncryptedError{}
}
return nil
}
Expand Down
7 changes: 7 additions & 0 deletions sops.go
Original file line number Diff line number Diff line change
Expand Up @@ -567,13 +567,20 @@ type ValueEmitter interface {
EmitValue(interface{}) ([]byte, error)
}

// CheckEncryped is the interface for testing whether a branch contains sops
// metadata. This is used to check whether a file is already encrypted or not.
type CheckEncryped interface {
HasSopsTopLevelKey(TreeBranch) bool
}

// Store is used to interact with files, both encrypted and unencrypted.
type Store interface {
EncryptedFileLoader
PlainFileLoader
EncryptedFileEmitter
PlainFileEmitter
ValueEmitter
CheckEncryped
}

// MasterKeyCount returns the number of master keys available
Expand Down
12 changes: 12 additions & 0 deletions stores/dotenv/store.go
Original file line number Diff line number Diff line change
Expand Up @@ -175,3 +175,15 @@ func isComplexValue(v interface{}) bool {
}
return false
}

// HasSopsTopLevelKey checks whether a top-level "sops" key exists.
func (store *Store) HasSopsTopLevelKey(branch sops.TreeBranch) bool {
for _, b := range branch {
if key, ok := b.Key.(string); ok {
if strings.HasPrefix(key, SopsPrefix) {
return true
}
}
}
return false
}
17 changes: 17 additions & 0 deletions stores/dotenv/store_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -80,3 +80,20 @@ func TestEmitEncryptedFileStability(t *testing.T) {
previous = bytes
}
}

func TestHasSopsTopLevelKey(t *testing.T) {
ok := (&Store{}).HasSopsTopLevelKey(sops.TreeBranch{
sops.TreeItem{
Key: "sops",
Value: "value",
},
})
assert.Equal(t, ok, false)
ok = (&Store{}).HasSopsTopLevelKey(sops.TreeBranch{
sops.TreeItem{
Key: "sops_",
Value: "value",
},
})
assert.Equal(t, ok, true)
}
5 changes: 5 additions & 0 deletions stores/ini/store.go
Original file line number Diff line number Diff line change
Expand Up @@ -274,3 +274,8 @@ func (store *Store) EmitExample() []byte {
}
return bytes
}

// HasSopsTopLevelKey checks whether a top-level "sops" key exists.
func (store *Store) HasSopsTopLevelKey(branch sops.TreeBranch) bool {
return stores.HasSopsTopLevelKey(branch)
}
10 changes: 10 additions & 0 deletions stores/json/store.go
Original file line number Diff line number Diff line change
Expand Up @@ -357,3 +357,13 @@ func (store *Store) EmitExample() []byte {
}
return bytes
}

// HasSopsTopLevelKey checks whether a top-level "sops" key exists.
func (store *Store) HasSopsTopLevelKey(branch sops.TreeBranch) bool {
return stores.HasSopsTopLevelKey(branch)
}

// HasSopsTopLevelKey checks whether a top-level "sops" key exists.
func (store *BinaryStore) HasSopsTopLevelKey(branch sops.TreeBranch) bool {
return stores.HasSopsTopLevelKey(branch)
}
10 changes: 10 additions & 0 deletions stores/stores.go
Original file line number Diff line number Diff line change
Expand Up @@ -506,3 +506,13 @@ var ExampleFlatTree = sops.Tree{
},
},
}

// HasSopsTopLevelKey returns true if the given branch has a top-level key called "sops".
func HasSopsTopLevelKey(branch sops.TreeBranch) bool {
for _, b := range branch {
if b.Key == "sops" {
return true
}
}
return false
}
5 changes: 5 additions & 0 deletions stores/yaml/store.go
Original file line number Diff line number Diff line change
Expand Up @@ -417,3 +417,8 @@ func (store *Store) EmitExample() []byte {
}
return bytes
}

// HasSopsTopLevelKey checks whether a top-level "sops" key exists.
func (store *Store) HasSopsTopLevelKey(branch sops.TreeBranch) bool {
return stores.HasSopsTopLevelKey(branch)
}
19 changes: 18 additions & 1 deletion stores/yaml/store_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -380,4 +380,21 @@ func TestIndent1(t *testing.T) {
assert.Nil(t, err)
assert.Equal(t, string(INDENT_1_OUT), string(bytes))
assert.Equal(t, INDENT_1_OUT, bytes)
}
}

func TestHasSopsTopLevelKey(t *testing.T) {
ok := (&Store{}).HasSopsTopLevelKey(sops.TreeBranch{
sops.TreeItem{
Key: "sops",
Value: "value",
},
})
assert.Equal(t, ok, true)
ok = (&Store{}).HasSopsTopLevelKey(sops.TreeBranch{
sops.TreeItem{
Key: "sops_",
Value: "value",
},
})
assert.Equal(t, ok, false)
}

0 comments on commit 3ada89e

Please sign in to comment.