Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions cmd/sops/subcommand/exec/exec.go
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,9 @@ func GetFile(dir, filename string) (*os.File, error) {
}

func ExecWithFile(opts ExecOpts) error {
var userEnv []string
if opts.User != "" {
userEnv = UserEnv(opts.User)
SwitchUser(opts.User)
}

Expand Down Expand Up @@ -107,6 +109,7 @@ func ExecWithFile(opts ExecOpts) error {
if !opts.Pristine {
env = os.Environ()
}
env = append(env, userEnv...)
env = append(env, opts.Env...)

placeholdered := strings.Replace(opts.Command, "{}", filename, -1)
Expand All @@ -125,7 +128,9 @@ func ExecWithFile(opts ExecOpts) error {
}

func ExecWithEnv(opts ExecOpts) error {
var userEnv []string
if opts.User != "" {
userEnv = UserEnv(opts.User)
SwitchUser(opts.User)
}

Expand All @@ -150,6 +155,7 @@ func ExecWithEnv(opts ExecOpts) error {
env = append(env, string(line))
}

env = append(env, userEnv...)
env = append(env, opts.Env...)

if opts.SameProcess {
Expand Down
12 changes: 12 additions & 0 deletions cmd/sops/subcommand/exec/exec_unix.go
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,18 @@ func GetPipe(dir, filename string) (string, error) {
return tmpfn, nil
}

func UserEnv(username string) []string {
u, err := user.Lookup(username)
if err != nil {
log.Fatal(err)
}
return []string{
"HOME=" + u.HomeDir,
"USER=" + u.Username,
"LOGNAME=" + u.Username,
}
}

func SwitchUser(username string) {
user, err := user.Lookup(username)
if err != nil {
Expand Down
115 changes: 115 additions & 0 deletions cmd/sops/subcommand/exec/exec_unix_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
//go:build !windows
// +build !windows

package exec

import (
"os"
"os/user"
"testing"

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

func TestUserEnvReturnsCorrectVars(t *testing.T) {
currentUser, err := user.Current()
require.NoError(t, err)

env := UserEnv(currentUser.Username)

assert.Contains(t, env, "HOME="+currentUser.HomeDir)
assert.Contains(t, env, "USER="+currentUser.Username)
assert.Contains(t, env, "LOGNAME="+currentUser.Username)
assert.Len(t, env, 3)
}

func TestUserEnvDoesNotModifyProcessEnv(t *testing.T) {
currentUser, err := user.Current()
require.NoError(t, err)

originalHome := os.Getenv("HOME")

UserEnv(currentUser.Username)

assert.Equal(t, originalHome, os.Getenv("HOME"),
"HOME should not be modified in the current process")
}

func TestExecWithFilePassesUserEnvToChild(t *testing.T) {
if os.Getuid() != 0 {
t.Skip("skipping test that requires root privileges")
}

currentUser, err := user.Current()
require.NoError(t, err)

originalHome := os.Getenv("HOME")

err = ExecWithFile(ExecOpts{
Command: "env | grep ^HOME=",
Plaintext: []byte("hello"),
User: currentUser.Username,
Fifo: false,
})
require.NoError(t, err)

assert.Equal(t, originalHome, os.Getenv("HOME"),
"ExecWithFile should not modify HOME in the current process")
}

func TestExecWithEnvPassesUserEnvToChild(t *testing.T) {
if os.Getuid() != 0 {
t.Skip("skipping test that requires root privileges")
}

currentUser, err := user.Current()
require.NoError(t, err)

originalHome := os.Getenv("HOME")

err = ExecWithEnv(ExecOpts{
Command: "true",
Plaintext: []byte{},
User: currentUser.Username,
})
require.NoError(t, err)

assert.Equal(t, originalHome, os.Getenv("HOME"),
"ExecWithEnv should not modify HOME in the current process")
}

func TestExecWithFilePristineIncludesUserEnv(t *testing.T) {
if os.Getuid() != 0 {
t.Skip("skipping test that requires root privileges")
}

currentUser, err := user.Current()
require.NoError(t, err)

err = ExecWithFile(ExecOpts{
Command: "env | grep -q ^HOME=",
Plaintext: []byte("hello"),
User: currentUser.Username,
Pristine: true,
Fifo: false,
})
require.NoError(t, err, "child should have HOME even with --pristine when --user is set")
}

func TestExecWithEnvPristineIncludesUserEnv(t *testing.T) {
if os.Getuid() != 0 {
t.Skip("skipping test that requires root privileges")
}

currentUser, err := user.Current()
require.NoError(t, err)

err = ExecWithEnv(ExecOpts{
Command: "env | grep -q ^HOME=",
Plaintext: []byte{},
User: currentUser.Username,
Pristine: true,
})
require.NoError(t, err, "child should have HOME even with --pristine when --user is set")
}
4 changes: 4 additions & 0 deletions cmd/sops/subcommand/exec/exec_windows.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,10 @@ func GetPipe(dir, filename string) (string, error) {
return "", fmt.Errorf("fifos are not available on windows")
}

func UserEnv(username string) []string {
return nil
}

func SwitchUser(username string) {
log.Fatal("user switching not available on windows")
}