Skip to content

Commit

Permalink
Merge pull request #4009 from jedevc/filesync-ascii-special-chars
Browse files Browse the repository at this point in the history
filesync: escape special query characters
  • Loading branch information
tonistiigi committed Jul 12, 2023
2 parents b623322 + 48b6240 commit 6c3bdd4
Show file tree
Hide file tree
Showing 2 changed files with 48 additions and 34 deletions.
16 changes: 14 additions & 2 deletions frontend/dockerfile/dockerfile_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -6766,12 +6766,16 @@ func testCopyUnicodePath(t *testing.T, sb integration.Sandbox) {
dockerfile := []byte(`
FROM alpine
COPY test-äöü.txt /
COPY test-%C3%A4%C3%B6%C3%BC.txt /
COPY test+aou.txt /
`)

dir, err := integration.Tmpdir(
t,
fstest.CreateFile("Dockerfile", dockerfile, 0600),
fstest.CreateFile("test-äöü.txt", []byte("test"), 0644),
fstest.CreateFile("test-äöü.txt", []byte("foo"), 0644),
fstest.CreateFile("test-%C3%A4%C3%B6%C3%BC.txt", []byte("bar"), 0644),
fstest.CreateFile("test+aou.txt", []byte("baz"), 0644),
)
require.NoError(t, err)

Expand All @@ -6794,7 +6798,15 @@ COPY test-äöü.txt /

dt, err := os.ReadFile(filepath.Join(destDir, "test-äöü.txt"))
require.NoError(t, err)
require.Equal(t, "test", string(dt))
require.Equal(t, "foo", string(dt))

dt, err = os.ReadFile(filepath.Join(destDir, "test-%C3%A4%C3%B6%C3%BC.txt"))
require.NoError(t, err)
require.Equal(t, "bar", string(dt))

dt, err = os.ReadFile(filepath.Join(destDir, "test+aou.txt"))
require.NoError(t, err)
require.Equal(t, "baz", string(dt))
}

func runShell(dir string, cmds ...string) error {
Expand Down
66 changes: 34 additions & 32 deletions session/filesync/filesync.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,6 @@ const (
keyFollowPaths = "followpaths"
keyDirName = "dir-name"
keyExporterMetaPrefix = "exporter-md-"
keyOptsEncoded = "opts-encoded"
)

type fsSyncProvider struct {
Expand Down Expand Up @@ -86,17 +85,7 @@ func (sp *fsSyncProvider) handle(method string, stream grpc.ServerStream) (retEr
}

opts, _ := metadata.FromIncomingContext(stream.Context()) // if no metadata continue with empty object

isDecoded := false
if v, ok := opts[keyOptsEncoded]; ok && len(v) > 0 {
if b, _ := strconv.ParseBool(v[0]); b {
isDecoded = true
}
}

if isDecoded {
opts = decodeOpts(opts)
}
opts = decodeOpts(opts)

dirName := ""
name, ok := opts[keyDirName]
Expand Down Expand Up @@ -224,9 +213,6 @@ func FSSync(ctx context.Context, c session.Caller, opt FSSendRequestOpt) error {

var stream grpc.ClientStream

// mark that we have encoded options so older versions with raw values can be detected on client side
opts[keyOptsEncoded] = []string{"1"}

opts = encodeOpts(opts)

ctx = metadata.NewOutgoingContext(ctx, opts)
Expand Down Expand Up @@ -361,11 +347,11 @@ func (e InvalidSessionError) Unwrap() error {
func encodeOpts(opts map[string][]string) map[string][]string {
md := make(map[string][]string, len(opts))
for k, v := range opts {
out := make([]string, len(v))
for i, s := range v {
out[i] = encodeStringForHeader(s)
}
out, encoded := encodeStringForHeader(v)
md[k] = out
if encoded {
md[k+"-encoded"] = []string{"1"}
}
}
return md
}
Expand All @@ -374,8 +360,18 @@ func decodeOpts(opts map[string][]string) map[string][]string {
md := make(map[string][]string, len(opts))
for k, v := range opts {
out := make([]string, len(v))
for i, s := range v {
out[i], _ = url.QueryUnescape(s)
var isDecoded bool
if v, ok := opts[k+"-encoded"]; ok && len(v) > 0 {
if b, _ := strconv.ParseBool(v[0]); b {
isDecoded = true
}
}
if isDecoded {
for i, s := range v {
out[i], _ = url.QueryUnescape(s)
}
} else {
copy(out, v)
}
md[k] = out
}
Expand All @@ -384,17 +380,23 @@ func decodeOpts(opts map[string][]string) map[string][]string {

// encodeStringForHeader encodes a string value so it can be used in grpc header. This encoding
// is backwards compatible and avoids encoding ASCII characters.
func encodeStringForHeader(input string) string {
var output strings.Builder
for _, runeVal := range input {
// Only encode non-ASCII characters.
if runeVal > unicode.MaxASCII {
// Encode each non-ASCII character individually.
output.WriteString(url.QueryEscape(string(runeVal)))
} else {
// Directly append ASCII characters and '*' to the output.
output.WriteRune(runeVal)
func encodeStringForHeader(inputs []string) ([]string, bool) {
var encode bool
for _, input := range inputs {
for _, runeVal := range input {
// Only encode non-ASCII characters, and characters that have special
// meaning during decoding.
if runeVal > unicode.MaxASCII {
encode = true
break
}
}
}
return output.String()
if !encode {
return inputs, false
}
for i, input := range inputs {
inputs[i] = url.QueryEscape(input)
}
return inputs, true
}

0 comments on commit 6c3bdd4

Please sign in to comment.