]> git.ipfire.org Git - thirdparty/curl.git/commitdiff
http_aws_sigv4: fix query normalization of %2b
authorDaniel Stenberg <daniel@haxx.se>
Mon, 9 Feb 2026 13:59:39 +0000 (14:59 +0100)
committerDaniel Stenberg <daniel@haxx.se>
Tue, 10 Feb 2026 06:52:18 +0000 (07:52 +0100)
Reported-by: Nuno Goncalves
Fixes #20543
Closes #20550

lib/http_aws_sigv4.c
tests/unit/unit1979.c
tests/unit/unit1980.c

index f1d0556fcf87213e9427985fb3a23443db179cd0..ea1a7ff758b2e51a0ccefdc23e8c38adf5403f56 100644 (file)
@@ -222,44 +222,44 @@ static CURLcode uri_encode_path(struct Curl_str *original_path,
   return CURLE_OK;
 }
 
-static CURLcode encode_query_component(char *component, size_t len,
-                                       struct dynbuf *db)
+/* Normalize the query part. Make sure %2B is left percent encoded, and not
+   decoded to plus, then encoded to space.
+*/
+static CURLcode normalize_query(const char *string, size_t len,
+                                struct dynbuf *db)
 {
-  size_t i;
-  for(i = 0; i < len; i++) {
-    CURLcode result = CURLE_OK;
-    unsigned char this_char = component[i];
+  CURLcode result = CURLE_OK;
+
+  while(len && !result) {
+    unsigned char in = (unsigned char)*string;
+    if(('%' == in) && (len > 2) &&
+       ISXDIGIT(string[1]) && ISXDIGIT(string[2])) {
+      /* this is two hexadecimal digits following a '%' */
+      in = (unsigned char)((curlx_hexval(string[1]) << 4) |
+                           curlx_hexval(string[2]));
+      string += 3;
+      len -= 3;
+      if(in == '+') {
+        /* decodes to plus, so leave this encoded */
+        result = curlx_dyn_addn(db, "%2B", 3);
+        continue;
+      }
+    }
+    else {
+      string++;
+      len--;
+    }
 
-    if(is_reserved_char(this_char))
+    if(is_reserved_char(in))
       /* Escape unreserved chars from RFC 3986 */
-      result = curlx_dyn_addn(db, &this_char, 1);
-    else if(this_char == '+')
+      result = curlx_dyn_addn(db, &in, 1);
+    else if(in == '+')
       /* Encode '+' as space */
       result = curlx_dyn_add(db, "%20");
     else
-      result = curlx_dyn_addf(db, "%%%02X", this_char);
-    if(result)
-      return result;
+      result = curlx_dyn_addf(db, "%%%02X", in);
   }
 
-  return CURLE_OK;
-}
-
-/*
- * Populates a dynbuf containing url_encode(url_decode(in))
- */
-static CURLcode http_aws_decode_encode(const char *in, size_t in_len,
-                                       struct dynbuf *out)
-{
-  char *out_s;
-  size_t out_s_len;
-  CURLcode result =
-    Curl_urldecode(in, in_len, &out_s, &out_s_len, REJECT_NADA);
-
-  if(!result) {
-    result = encode_query_component(out_s, out_s_len, out);
-    Curl_safefree(out_s);
-  }
   return result;
 }
 
@@ -750,8 +750,8 @@ UNITTEST CURLcode canon_query(const char *query, struct dynbuf *dq)
     counted_query_components++;
 
     /* Decode/encode the key */
-    result = http_aws_decode_encode(in_key, in_key_len,
-                                    &encoded_query_array[index].key);
+    result = normalize_query(in_key, in_key_len,
+                             &encoded_query_array[index].key);
     if(result) {
       goto fail;
     }
@@ -761,8 +761,8 @@ UNITTEST CURLcode canon_query(const char *query, struct dynbuf *dq)
       size_t in_value_len;
       const char *in_value = offset + 1;
       in_value_len = query_part + query_part_len - (offset + 1);
-      result = http_aws_decode_encode(in_value, in_value_len,
-                                      &encoded_query_array[index].value);
+      result = normalize_query(in_value, in_value_len,
+                               &encoded_query_array[index].value);
       if(result) {
         goto fail;
       }
index 943abfed65a8fe03c5e6956538267d55631c7251..821ec5608d008ef712f9761aaf811417235ddf2d 100644 (file)
@@ -83,6 +83,12 @@ static CURLcode test_unit1979(const char *arg)
       "/example space/",
       "/example%20space/"
     },
+    {
+      "get-plus-normalized",
+      true,
+      "/example+space/",
+      "/example%2Bspace/"
+    },
     {
       "get-slash-dot-slash-unnormalized",
       false,
index aa08c44e6aa82497023a158604442d74c115d27c..0c2977a11fa04320a7585512d26d6eca88a4874e 100644 (file)
@@ -79,6 +79,11 @@ static CURLcode test_unit1980(const char *arg)
       "p3= &p1=+&p2=%20",
       "p1=%20&p2=%20&p3=%20"
     },
+    {
+      "2b-incoming",
+      "p3=%2b&p1=+",
+      "p1=%20&p3=%2B"
+    },
   };
 
   size_t i;