Skip to content

Commit 3e2268d

Browse files
fix: align path encoding with RFC 3986 section 3.3
1 parent cde1131 commit 3e2268d

File tree

3 files changed

+29
-3
lines changed

3 files changed

+29
-3
lines changed

lib/knockapi/internal/util.rb

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -237,6 +237,11 @@ def dig(data, pick, &blk)
237237
end
238238
end
239239

240+
# @type [Regexp]
241+
#
242+
# https://www.rfc-editor.org/rfc/rfc3986.html#section-3.3
243+
RFC_3986_NOT_PCHARS = /[^A-Za-z0-9\-._~!$&'()*+,;=:@]+/
244+
240245
class << self
241246
# @api private
242247
#
@@ -247,6 +252,15 @@ def uri_origin(uri)
247252
"#{uri.scheme}://#{uri.host}#{":#{uri.port}" unless uri.port == uri.default_port}"
248253
end
249254

255+
# @api private
256+
#
257+
# @param path [String, Integer]
258+
#
259+
# @return [String]
260+
def encode_path(path)
261+
path.to_s.gsub(Knockapi::Internal::Util::RFC_3986_NOT_PCHARS) { ERB::Util.url_encode(_1) }
262+
end
263+
250264
# @api private
251265
#
252266
# @param path [String, Array<String>]
@@ -259,7 +273,7 @@ def interpolate_path(path)
259273
in []
260274
""
261275
in [String => p, *interpolations]
262-
encoded = interpolations.map { ERB::Util.url_encode(_1) }
276+
encoded = interpolations.map { encode_path(_1) }
263277
format(p, *encoded)
264278
end
265279
end
@@ -576,10 +590,10 @@ def encode_query_params(query)
576590

577591
case val
578592
in Knockapi::FilePart unless val.filename.nil?
579-
filename = ERB::Util.url_encode(val.filename)
593+
filename = encode_path(val.filename)
580594
y << "; filename=\"#{filename}\""
581595
in Pathname | IO
582-
filename = ERB::Util.url_encode(::File.basename(val.to_path))
596+
filename = encode_path(::File.basename(val.to_path))
583597
y << "; filename=\"#{filename}\""
584598
else
585599
end

rbi/knockapi/internal/util.rbi

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -148,12 +148,20 @@ module Knockapi
148148
end
149149
end
150150

151+
# https://www.rfc-editor.org/rfc/rfc3986.html#section-3.3
152+
RFC_3986_NOT_PCHARS = T.let(/[^A-Za-z0-9\-._~!$&'()*+,;=:@]+/, Regexp)
153+
151154
class << self
152155
# @api private
153156
sig { params(uri: URI::Generic).returns(String) }
154157
def uri_origin(uri)
155158
end
156159

160+
# @api private
161+
sig { params(path: T.any(String, Integer)).returns(String) }
162+
def encode_path(path)
163+
end
164+
157165
# @api private
158166
sig { params(path: T.any(String, T::Array[String])).returns(String) }
159167
def interpolate_path(path)

sig/knockapi/internal/util.rbs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,8 +45,12 @@ module Knockapi
4545
-> top?
4646
} -> top?
4747

48+
RFC_3986_NOT_PCHARS: Regexp
49+
4850
def self?.uri_origin: (URI::Generic uri) -> String
4951

52+
def self?.encode_path: (String | Integer path) -> String
53+
5054
def self?.interpolate_path: (String | ::Array[String] path) -> String
5155

5256
def self?.decode_query: (String? query) -> ::Hash[String, ::Array[String]]

0 commit comments

Comments
 (0)