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
1 change: 1 addition & 0 deletions cmd/launcher/cli/launcher.go
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ func RunLaunch() error {
ExecD: launch.NewExecDRunner(),
Shell: launch.DefaultShell,
Setenv: os.Setenv,
Logger: cmd.DefaultLogger,
}

if err := launcher.Launch(os.Args[0], os.Args[1:]); err != nil {
Expand Down
11 changes: 11 additions & 0 deletions launch/launcher.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import (

"github.com/buildpacks/lifecycle/api"
"github.com/buildpacks/lifecycle/env"
"github.com/buildpacks/lifecycle/log"
)

var (
Expand All @@ -26,9 +27,11 @@ type Launcher struct {
ExecD ExecD
Shell Shell
LayersDir string
Logger *log.DefaultLogger
PlatformAPI *api.Version
Processes []Process
Setenv func(string, string) error
Setumask func(Env) error
}

type ExecFunc func(argv0 string, argv []string, envv []string) error
Expand Down Expand Up @@ -69,6 +72,14 @@ func (l *Launcher) LaunchProcess(self string, proc Process) error {
}
proc.WorkingDirectory = getProcessWorkingDirectory(proc, l.AppDir)

if l.Setumask == nil {
l.Setumask = SetUmask
}
err := l.Setumask(l.Env)
if err != nil && l.Logger != nil {
l.Logger.Errorf("invalid umask: %s", err)
}

if proc.Direct {
return l.launchDirect(proc)
}
Expand Down
6 changes: 6 additions & 0 deletions launch/launcher_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,9 @@ func testLauncher(t *testing.T, when spec.G, it spec.S) {
// set command to something on the real path so exec.LookPath succeeds
process.Command = launch.NewRawCommand([]string{"sh"})

launcher.Setumask = func(_ launch.Env) error {
return nil
}
mockEnv.EXPECT().Get("PATH").Return("some-path").AnyTimes()
launcher.Setenv = func(k string, v string) error {
if k == "PATH" {
Expand Down Expand Up @@ -373,6 +376,9 @@ func testLauncher(t *testing.T, when spec.G, it spec.S) {
it.Before(func() {
shell = &fakeShell{}
launcher.Shell = shell
launcher.Setumask = func(_ launch.Env) error {
return nil
}
})

it("sets Caller to self", func() {
Expand Down
35 changes: 35 additions & 0 deletions launch/umask_unix.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
//go:build unix

package launch

import (
"fmt"
"strconv"
"syscall"

"github.com/pkg/errors"
)

const (
umaskEnvVar = "CNB_LAUNCH_UMASK"
)

// SetUmask on unix systems from the value in the `UMASK` environment variable
func SetUmask(env Env) error {
return SetUmaskWith(env, syscall.Umask)
}

// SetUmaskWith the injected function umaskFn
func SetUmaskWith(env Env, umaskFn func(int) int) error {
umask := env.Get(umaskEnvVar)
if umask == "" {
return nil
}

u, err := strconv.ParseInt(umask, 8, 0)
if err != nil {
return errors.Wrap(err, fmt.Sprintf("invalid umask value %s", umask))
}
umaskFn(int(u))
return nil
}
77 changes: 77 additions & 0 deletions launch/umask_unix_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
//go:build unix

package launch_test

import (
"testing"

"github.com/sclevine/spec"
"github.com/sclevine/spec/report"

"github.com/buildpacks/lifecycle/env"
"github.com/buildpacks/lifecycle/launch"
h "github.com/buildpacks/lifecycle/testhelpers"
)

func TestUmask(t *testing.T) {
spec.Run(t, "Umask", testUmask, spec.Report(report.Terminal{}))
}

func testUmask(t *testing.T, when spec.G, it spec.S) {
when("UMASK is set", func() {
it("parses octal umask values", func() {
tests := []struct {
name string
umask string
expected int
}{
{"standard user umask", "0002", 2},
{"restrictive umask", "0077", 63},
{"permissive umask", "0000", 0},
{"three digit umask", "022", 18},
}

for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
environ := env.NewLaunchEnv([]string{"CNB_LAUNCH_UMASK=" + tt.umask}, "", "")

var called int
spy := func(m int) int {
called = m
return 0
}

err := launch.SetUmaskWith(environ, spy)

h.AssertNil(t, err)
h.AssertEq(t, called, tt.expected)
})
}
})

it("returns error for invalid umask", func() {
environ := env.NewLaunchEnv([]string{"CNB_LAUNCH_UMASK=invalid"}, "", "")

err := launch.SetUmaskWith(environ, func(int) int { return 0 })

h.AssertNotNil(t, err)
})
})

when("UMASK is unset", func() {
it("does not call umask function", func() {
environ := env.NewLaunchEnv([]string{}, "", "")

called := false
spy := func(_ int) int {
called = true
return 0
}

err := launch.SetUmaskWith(environ, spy)

h.AssertNil(t, err)
h.AssertEq(t, called, false)
})
})
}
5 changes: 5 additions & 0 deletions launch/umask_windows.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
package launch

func SetUmask(env Env) {
// no operation
}