Browse Source

daemon/config: ignore UTF-8 BOM in config JSON

[RFC 8259] allows for JSON implementations to optionally ignore a BOM
when it helps with interoperability; do so in Moby as Notepad (the only
text editor available out of the box in many versions of Windows Server)
insists on writing UTF-8 with a BOM.

  [RFC 8259]: https://tools.ietf.org/html/rfc8259#section-8.1

Signed-off-by: Bjorn Neergaard <bneergaard@mirantis.com>
Bjorn Neergaard 2 years ago
parent
commit
bb19265ba8
2 changed files with 21 additions and 2 deletions
  1. 6 2
      daemon/config/config.go
  2. 15 0
      daemon/config/config_test.go

+ 6 - 2
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 {

+ 15 - 0
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)