From 5140e317b75d0467bfbbabb5d12404568fee1bb7 Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Thu, 17 Jul 2025 11:14:45 -0700 Subject: [PATCH] url: %-encode literal % characters when building query strings When building a query string via `url-build-query-string', %-encode literal % characters appearing in both the keys and the values. * lisp/url/url-util.el (url--query-key-value-preserved-chars): Define a new constant based on `url-query-key-value-allowed-chars' specifying the characters that should be preserved when %-encoding query-string keys and values. (url-build-query-string): Use the new constant (fixes Bug#78984). * test/lisp/url/url-util-tests.el (url-util-tests): Add a test. --- lisp/url/url-util.el | 9 ++++++++- test/lisp/url/url-util-tests.el | 8 ++++---- 2 files changed, 12 insertions(+), 5 deletions(-) diff --git a/lisp/url/url-util.el b/lisp/url/url-util.el index 8435114bbc2..47ef8811c5e 100644 --- a/lisp/url/url-util.el +++ b/lisp/url/url-util.el @@ -409,6 +409,13 @@ should return it unchanged." (url-hexify-string frag url-query-allowed-chars))) (url-recreate-url obj))) +(defconst url--query-key-value-preserved-chars + (let ((vec (copy-sequence url-query-key-value-allowed-chars))) + (aset vec ?% nil) + vec) + "Byte mask used for %-encoding keys and values in the query segment of a URI. +`url-query-key-value-allowed-chars' minus '%'.") + ;;;###autoload (defun url-build-query-string (query &optional semicolons keep-empty) "Build a query-string. @@ -436,7 +443,7 @@ instead of just \"key\" as in the example above." (let ((escaped (mapcar (lambda (sym) (url-hexify-string (format "%s" sym) - url-query-key-value-allowed-chars)) + url--query-key-value-preserved-chars)) key-vals))) (mapconcat (lambda (val) (let ((vprint (format "%s" val)) diff --git a/test/lisp/url/url-util-tests.el b/test/lisp/url/url-util-tests.el index d34bfc8509c..6e79a798333 100644 --- a/test/lisp/url/url-util-tests.el +++ b/test/lisp/url/url-util-tests.el @@ -33,10 +33,10 @@ ((key1 "val1") (key2 val2) (key3 val1 val2) ("key4") (key5 "")) t) ("key1=val1;key2=val2;key3=val1;key3=val2;key4=;key5=" ((key1 val1) (key2 val2) ("key3" val1 val2) (key4) (key5 "")) t t) - ("key1=val/slash;key2=val%3Bsemi;key3=val%26amp;key4=val%3Deq" - ((key1 "val/slash") (key2 "val;semi") (key3 "val&") (key4 "val=eq")) t) - ("key%3Deq=val1;key%3Bsemi=val2;key%26amp=val3" - (("key=eq" val1) ("key;semi" val2) ("key&" val3)) t))) + ("key1=val/slash;key2=val%3Bsemi;key3=val%26amp;key4=val%3Deq;key5=val%25perc" + ((key1 "val/slash") (key2 "val;semi") (key3 "val&") (key4 "val=eq") (key5 "val%perc")) t) + ("key%3Deq=val1;key%3Bsemi=val2;key%26amp=val3;key%25perc=val4" + (("key=eq" val1) ("key;semi" val2) ("key&" val3) ("key%perc" val4)) t))) test) (while tests (setq test (car tests)