diff --git a/daemon/config/config.go b/daemon/config/config.go index acc2411f15..6f0ced06e3 100644 --- a/daemon/config/config.go +++ b/daemon/config/config.go @@ -412,10 +412,14 @@ func getConflictFreeConfiguration(configFile string, flags *pflag.FlagSet) (*Con var config Config + // Strip the UTF-8 BOM if present ([RFC 8259] allows JSON implementations to optionally strip the BOM for + // interoperability; do so here as Notepad on older versions of Windows Server insists on a BOM). + // [RFC 8259]: https://tools.ietf.org/html/rfc8259#section-8.1 + b = bytes.TrimPrefix(b, []byte("\xef\xbb\xbf")) + b = bytes.TrimSpace(b) if len(b) == 0 { - // empty config file - return &config, nil + return &config, nil // early return on empty config } if flags != nil { diff --git a/daemon/config/config_test.go b/daemon/config/config_test.go index 882dcaedff..a8a2b59e2a 100644 --- a/daemon/config/config_test.go +++ b/daemon/config/config_test.go @@ -2,6 +2,7 @@ package config // import "github.com/docker/docker/daemon/config" import ( "os" + "path/filepath" "reflect" "strings" "testing" @@ -35,6 +36,20 @@ func TestDaemonBrokenConfiguration(t *testing.T) { assert.ErrorContains(t, err, `invalid character ' ' in literal true`) } +// TestDaemonConfigurationWithBOM ensures that the UTF-8 byte order mark is ignored when reading the configuration file. +func TestDaemonConfigurationWithBOM(t *testing.T) { + configFile := filepath.Join(t.TempDir(), "daemon.json") + + f, err := os.Create(configFile) + assert.NilError(t, err) + + f.Write([]byte("\xef\xbb\xbf{\"debug\": true}")) + f.Close() + + _, err = MergeDaemonConfigurations(&Config{}, nil, configFile) + assert.NilError(t, err) +} + func TestFindConfigurationConflicts(t *testing.T) { config := map[string]interface{}{"authorization-plugins": "foobar"} flags := pflag.NewFlagSet("test", pflag.ContinueOnError)