19088: base64-encode non-ascii property values in response headers.
authorTom Clegg <tom@curii.com>
Mon, 27 Jun 2022 19:05:58 +0000 (15:05 -0400)
committerTom Clegg <tom@curii.com>
Mon, 27 Jun 2022 19:05:58 +0000 (15:05 -0400)
Arvados-DCO-1.1-Signed-off-by: Tom Clegg <tom@curii.com>

services/keep-web/s3.go
services/keep-web/s3_test.go

index d92828e066eeb9b61c9b25e38be543989ca540a1..90b75f8a306019c2b646d15228da0c1c54a62956 100644 (file)
@@ -14,6 +14,7 @@ import (
        "fmt"
        "hash"
        "io"
+       "mime"
        "net/http"
        "net/textproto"
        "net/url"
@@ -604,6 +605,14 @@ func (h *handler) serveS3(w http.ResponseWriter, r *http.Request) bool {
 }
 
 func setFileInfoHeaders(header http.Header, fs arvados.CustomFileSystem, path string) error {
+       maybeEncode := func(s string) string {
+               for _, c := range s {
+                       if c > '\u007f' {
+                               return mime.BEncoding.Encode("UTF-8", s)
+                       }
+               }
+               return s
+       }
        path = strings.TrimSuffix(path, "/")
        var props map[string]interface{}
        for {
@@ -636,9 +645,9 @@ func setFileInfoHeaders(header http.Header, fs arvados.CustomFileSystem, path st
                }
                k = "x-amz-meta-" + k
                if s, ok := v.(string); ok {
-                       header.Set(k, s)
+                       header.Set(k, maybeEncode(s))
                } else if j, err := json.Marshal(v); err == nil {
-                       header.Set(k, string(j))
+                       header.Set(k, maybeEncode(string(j)))
                }
        }
        return nil
index b25ef972dc1805a898da87fa2e2aa8512bcfb6a7..a99f3c278f6214b5764f853920c10539f7757ffe 100644 (file)
@@ -11,6 +11,7 @@ import (
        "crypto/sha256"
        "fmt"
        "io/ioutil"
+       "mime"
        "net/http"
        "net/http/httptest"
        "net/url"
@@ -77,9 +78,10 @@ func (s *IntegrationSuite) s3setup(c *check.C) s3stage {
                "name":          "keep-web s3 test collection",
                "manifest_text": ". d41d8cd98f00b204e9800998ecf8427e+0 0:0:emptyfile\n./emptydir d41d8cd98f00b204e9800998ecf8427e+0 0:0:.\n",
                "properties": map[string]interface{}{
-                       "string": "string value",
-                       "array":  []string{"element1", "element2"},
-                       "object": map[string]interface{}{"key": map[string]interface{}{"key2": "value"}},
+                       "string":   "string value",
+                       "array":    []string{"element1", "element2"},
+                       "object":   map[string]interface{}{"key": map[string]interface{}{"key2": "value⛵"}},
+                       "nonascii": "⛵",
                },
        }})
        c.Assert(err, check.IsNil)
@@ -252,9 +254,10 @@ func (s *IntegrationSuite) TestS3PropertiesAsMetadata(c *check.C) {
        defer stage.teardown(c)
 
        expectCollectionTags := map[string]string{
-               "String": "string value",
-               "Array":  `["element1","element2"]`,
-               "Object": `{"key":{"key2":"value"}}`,
+               "String":   "string value",
+               "Array":    `["element1","element2"]`,
+               "Object":   mime.BEncoding.Encode("UTF-8", `{"key":{"key2":"value⛵"}}`),
+               "Nonascii": "=?UTF-8?b?4pu1?=",
        }
        expectSubprojectTags := map[string]string{
                "Subproject_properties_key": "subproject properties value",