Skip to content

Commit

Permalink
Make check whether file contains invalid keys for encryption dependen…
Browse files Browse the repository at this point in the history
…t on output store.

Signed-off-by: Felix Fontein <[email protected]>
  • Loading branch information
felixfontein committed Dec 28, 2023
1 parent 2a70c24 commit 8d6d70e
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 @@ -172,3 +172,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 @@ -271,3 +271,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 8d6d70e

Please sign in to comment.