Skip to content
Merged
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
16 changes: 15 additions & 1 deletion cli/command/stack/loader/loader.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import (
"io"
"os"
"path/filepath"
"runtime"
"sort"
"strings"

Expand Down Expand Up @@ -104,9 +105,22 @@ func GetConfigDetails(composefiles []string, stdin io.Reader) (composetypes.Conf
func buildEnvironment(env []string) (map[string]string, error) {
result := make(map[string]string, len(env))
for _, s := range env {
if runtime.GOOS == "windows" && len(s) > 0 {
// cmd.exe can have special environment variables which names start with "=".
// They are only there for MS-DOS compatibility and we should ignore them.
// See TestBuildEnvironment for examples.
//
// https://ss64.com/nt/syntax-variables.html
// https://devblogs.microsoft.com/oldnewthing/20100506-00/?p=14133
// https://github.com/docker/cli/issues/4078
if s[0] == '=' {
Copy link
Collaborator Author

@vvoland vvoland Mar 9, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I didn't make this one an explicit blacklist of variables, because:

  1. MS documentation explicitly states that:

The name of an environment variable cannot include an equal sign (=).
https://learn.microsoft.com/en-us/windows/win32/procthread/environment-variables

so user can't deliberately set such variable nor anyone other than cmd.exe should actually set such variables.

  1. I don't think we want to implement the parser for the drive-specific CWD format (or hardcode every possible drive letter).
  2. We don't want to play whack-a-mole if there are some other undocumented variables that can start with =.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  1. MS documentation explicitly states that:

The name of an environment variable cannot include an equal sign (=).
https://learn.microsoft.com/en-us/windows/win32/procthread/environment-variables

Interesting, thanks!

So;

  • Perhaps we should include that link (and description that a user cannot set it) in the comment
  • From the above, I wonder indeed if this is something that os.Environ() should take into account (if that would make sense) and possibly filter out those "env-vars" (might be worth opening a ticket for in Golang, and see if there's opinions on that)

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

continue
}
}

k, v, ok := strings.Cut(s, "=")
if !ok || k == "" {
Comment on lines 121 to 122
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I was wondering if the case above could also be checked as;

if ok && k == "" && runtime.GOOS == "windows" {
    // windows handling here

But not sure if that would make it actually more readable than your implementation, and won't give us a performance benefice, so .. bad suggestion I guess 😂

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think the version as written is better as it makes the intention more obvious.

return result, errors.Errorf("unexpected environment %q", s)
return result, errors.Errorf("unexpected environment variable '%s'", s)
}
// value may be set, but empty if "s" is like "K=", not "K".
result[k] = v
Expand Down
32 changes: 32 additions & 0 deletions cli/command/stack/loader/loader_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package loader
import (
"os"
"path/filepath"
"runtime"
"strings"
"testing"

Expand Down Expand Up @@ -45,3 +46,34 @@ services:
assert.Check(t, is.Equal("3.0", details.ConfigFiles[0].Config["version"]))
assert.Check(t, is.Len(details.Environment, len(os.Environ())))
}

func TestBuildEnvironment(t *testing.T) {
inputEnv := []string{
"LEGIT_VAR=LEGIT_VALUE",
"EMPTY_VARIABLE=",
}

if runtime.GOOS == "windows" {
inputEnv = []string{
"LEGIT_VAR=LEGIT_VALUE",

// cmd.exe has some special environment variables which start with "=".
// These should be ignored as they're only there for MS-DOS compatibility.
"=ExitCode=00000041",
"=ExitCodeAscii=A",
`=C:=C:\some\dir`,
`=D:=D:\some\different\dir`,
`=X:=X:\`,
`=::=::\`,

"EMPTY_VARIABLE=",
}
}

env, err := buildEnvironment(inputEnv)
assert.NilError(t, err)

assert.Check(t, is.Len(env, 2))
assert.Check(t, is.Equal("LEGIT_VALUE", env["LEGIT_VAR"]))
assert.Check(t, is.Equal("", env["EMPTY_VARIABLE"]))
}