Skip to content

Commit c502632

Browse files
johnstcnmafredri
andauthored
fix: avoid deleting root filesystem when KANIKO_DIR not set (#160)
Co-authored-by: Mathias Fredriksson <[email protected]>
1 parent 6218976 commit c502632

File tree

3 files changed

+67
-2
lines changed

3 files changed

+67
-2
lines changed

README.md

+6
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,12 @@ $ vim .devcontainer/Dockerfile
4747

4848
Exit the container, and re-run the `docker run` command... after the build completes, `htop` should exist in the container! 🥳
4949

50+
> [!NOTE]
51+
> Envbuilder performs destructive filesystem operations! To guard against accidental data
52+
> loss, it will refuse to run if it detects that KANIKO_DIR is not set to a specific value.
53+
> If you need to bypass this behavior for any reason, you can bypass this safety check by setting
54+
> `FORCE_SAFE=true`.
55+
5056
### Git Branch Selection
5157

5258
Choose a branch using `GIT_URL` with a _ref/heads_ reference. For instance:

envbuilder.go

+25-2
Original file line numberDiff line numberDiff line change
@@ -427,8 +427,7 @@ func Run(ctx context.Context, options Options) error {
427427

428428
// It's possible that the container will already have files in it, and
429429
// we don't want to merge a new container with the old one.
430-
err = util.DeleteFilesystem()
431-
if err != nil {
430+
if err := maybeDeleteFilesystem(options.ForceSafe); err != nil {
432431
return nil, fmt.Errorf("delete filesystem: %w", err)
433432
}
434433

@@ -1063,3 +1062,27 @@ func findDevcontainerJSON(options Options) (string, string, error) {
10631062

10641063
return "", "", errors.New("can't find devcontainer.json, is it a correct spec?")
10651064
}
1065+
1066+
// maybeDeleteFilesystem wraps util.DeleteFilesystem with a guard to hopefully stop
1067+
// folks from unwittingly deleting their entire root directory.
1068+
func maybeDeleteFilesystem(force bool) error {
1069+
kanikoDir, ok := os.LookupEnv("KANIKO_DIR")
1070+
if !ok || strings.TrimSpace(kanikoDir) != MagicDir {
1071+
if force {
1072+
bailoutSecs := 10
1073+
_, _ = fmt.Fprintln(os.Stderr, "WARNING! BYPASSING SAFETY CHECK! THIS WILL DELETE YOUR ROOT FILESYSTEM!")
1074+
_, _ = fmt.Fprintf(os.Stderr, "You have %d seconds to bail out", bailoutSecs)
1075+
for i := 0; i < bailoutSecs; i++ {
1076+
_, _ = fmt.Fprintf(os.Stderr, ".")
1077+
<-time.After(time.Second)
1078+
}
1079+
_, _ = fmt.Fprintf(os.Stderr, "\n")
1080+
} else {
1081+
_, _ = fmt.Fprintf(os.Stderr, "KANIKO_DIR is not set to %s. Bailing!\n", MagicDir)
1082+
_, _ = fmt.Fprintln(os.Stderr, "To bypass this check, set FORCE_SAFE=true.")
1083+
return errors.New("safety check failed")
1084+
}
1085+
}
1086+
1087+
return util.DeleteFilesystem()
1088+
}

integration/integration_test.go

+36
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,42 @@ const (
4444
testImageUbuntu = "localhost:5000/envbuilder-test-ubuntu:latest"
4545
)
4646

47+
func TestForceSafe(t *testing.T) {
48+
t.Parallel()
49+
50+
t.Run("Safe", func(t *testing.T) {
51+
t.Parallel()
52+
srv := createGitServer(t, gitServerOptions{
53+
files: map[string]string{
54+
"Dockerfile": "FROM " + testImageAlpine,
55+
},
56+
})
57+
_, err := runEnvbuilder(t, options{env: []string{
58+
"GIT_URL=" + srv.URL,
59+
"KANIKO_DIR=/not/envbuilder",
60+
"DOCKERFILE_PATH=Dockerfile",
61+
}})
62+
require.ErrorContains(t, err, "delete filesystem: safety check failed")
63+
})
64+
65+
// Careful with this one!
66+
t.Run("Unsafe", func(t *testing.T) {
67+
t.Parallel()
68+
srv := createGitServer(t, gitServerOptions{
69+
files: map[string]string{
70+
"Dockerfile": "FROM " + testImageAlpine,
71+
},
72+
})
73+
_, err := runEnvbuilder(t, options{env: []string{
74+
"GIT_URL=" + srv.URL,
75+
"KANIKO_DIR=/not/envbuilder",
76+
"FORCE_SAFE=true",
77+
"DOCKERFILE_PATH=Dockerfile",
78+
}})
79+
require.NoError(t, err)
80+
})
81+
}
82+
4783
func TestFailsGitAuth(t *testing.T) {
4884
t.Parallel()
4985
srv := createGitServer(t, gitServerOptions{

0 commit comments

Comments
 (0)