Repository: jekyll/jekyll (Ruby)

Issue:
[Bug]: Jekyll::URL.escape_path interprets colons as protocol scheme
### Operating System

Alpine 3.13

### Ruby Version

ruby 3.1.5p252 (2024-04-23 revision 1945f8dc0e) [x86_64-linux-musl]

### Jekyll Version

jekyll 4.2.2

### GitHub Pages Version

_No response_

### Expected Behavior

I'm migrating a site and while generating tag pages, Jekyll throws an exception `#<Addressable::URI::InvalidURIError: Invalid scheme format: 'two words'>` for a tag with the format "two words: something else". I need to keep the original URLs, so I can't slugify the tag names, they should be escaped, so the page URL should be something like `/tag/two words: something else/`. I wrote a plugin that uses `Jekyll::URL` to generate them.

### Current Behavior

This will correctly escape the URL to `/tag/word:%20something%20else/`

```ruby
::Jekyll::URL.new(template: '/tag/:tag/', placeholders: { tag: 'word: something else' }).to_s
```

But

```ruby
::Jekyll::URL.new(template: '/tag/:tag/', placeholders: { tag: 'two words: something else' }).to_s
```

Will throw:

```
Addressable::URI::InvalidURIError: Invalid scheme format: 'two words'
from /usr/lib/ruby/gems/3.1.0/gems/addressable-2.8.7/lib/addressable/uri.rb:924:in `scheme='
```

Because `Jekyll::URL.escape_path` uses `Addressable::URI.encode`, that parses the placeholder value to an `Addressable::URI` before escaping it.

The escaping works correctly to `two%20words:%20something%20else` with `Addressable::URL.encode_component`:

```ruby
Addressable::URI.encode_component 'two words: something else'
```

So I'm wondering if patching `Jekyll::URL` to use `encode_component` instead of `encode` would be a good PR. Otherwise, change `Jekyll::URL#generate_url_from_drop` and `Jekyll::URL#generate_url_from_hash` to use a new method that calls `encode_component` instead of `encode`, by adding an `escape_component` method.  I can prepare such PR if that's fix.

### Relevant log output

```shell

```

### Code Sample

_No response_


Discussion:
Hello @fauno
I am onboard with the idea of patching `Jekyll::URL` to use `Addressable::URI::encode_component` instead of `Addressable::URI::encode` since relying only on `:encode` is a deficiency as per this bug report.




Patch:
diff --git a/lib/jekyll/url.rb b/lib/jekyll/url.rb
index aeb2ac0d867..d57352c4361 100644
--- a/lib/jekyll/url.rb
+++ b/lib/jekyll/url.rb
@@ -144,7 +144,13 @@ def self.escape_path(path)
       #   pct-encoded   = "%" HEXDIG HEXDIG
       #   sub-delims    = "!" / "$" / "&" / "'" / "(" / ")"
       #                 / "*" / "+" / "," / ";" / "="
-      Addressable::URI.encode(path).encode("utf-8").sub("#", "%23")
+      #
+      # `Addressable::URI::CharacterClassesRegexps::PATH` is used to encode
+      # non-alphanumeric characters such as "[", "]", etc.
+      Addressable::URI.encode_component(
+        path,
+        Addressable::URI::CharacterClassesRegexps::PATH
+      ).encode("utf-8").sub("#", "%23")
     end
 
     # Unescapes a URL path segment


Test patch:
diff --git a/test/test_url.rb b/test/test_url.rb
index 859babd4574..430789aa4a2 100644
--- a/test/test_url.rb
+++ b/test/test_url.rb
@@ -80,5 +80,16 @@ class TestURL < JekyllUnitTest
         ).to_s
       end
     end
+
+    should "not treat colons in placeholders as uri delimiters" do
+      assert_equal "/foo/foo%20bar:foobar/", URL.new(
+        :template     => "/:x/:y/",
+        :placeholders => { :x => "foo", :y => "foo bar:foobar" }
+      ).to_s
+    end
+
+    should "unescape urls with colons" do
+      assert_equal "/foo/foo bar:foobar/", Jekyll::URL.unescape_path("/foo/foo%20bar:foobar/")
+    end
   end
 end