Module: Pikuri::Thunderbird::MailtoUri
- Defined in:
- lib/pikuri/thunderbird/mailto_uri.rb
Overview
Builds the percent-encoded mailto: URI that MailCompose hands to
Thunderbird. The URI is the injection surface — a naive concat lets an
attacker-authored body smuggle its own &bcc=exfil@evil.com — so every
field value is percent-encoded before it joins the URI:
MailtoUri.build(to: ['a@x.com'], subject: 'Re: hi',
body: "Line 1\nLine 2 & more")
# => "mailto:a@x.com?subject=Re:%20hi&body=Line%201%0ALine%202%20%26%20more"
Multiple recipients ride comma-separated (the commas stay literal — they delimit addresses; each address is encoded individually). The caller (ComposeGuard) has already split and validated the addresses, so a comma here only ever separates two vetted recipients.
Immutable.
Constant Summary collapse
- UNRESERVED =
Bytes kept literal; everything else — the structural
& ? = # %, spaces, newlines, and every non-ASCII byte — is percent-encoded.@stays literal so an address reads asa@x.com(the confirmed-working shape), nota%40x.com. Matched byte-wise (+/n+) so a multibyte body encodes cleanly. %r{[^A-Za-z0-9\-._~@]}n
Class Method Summary collapse
-
.build(to:, cc: [], bcc: [], subject: nil, body: nil) ⇒ String
The
mailto:URI. -
.encode(value) ⇒ String
Percent-encode one value byte-wise (uppercase hex).
-
.encode_list(addrs) ⇒ String
Each address percent-encoded, joined by literal commas.
Class Method Details
.build(to:, cc: [], bcc: [], subject: nil, body: nil) ⇒ String
Returns the mailto: URI.
34 35 36 37 38 39 40 41 42 43 |
# File 'lib/pikuri/thunderbird/mailto_uri.rb', line 34 def self.build(to:, cc: [], bcc: [], subject: nil, body: nil) params = [] params << "cc=#{encode_list(cc)}" unless cc.empty? params << "bcc=#{encode_list(bcc)}" unless bcc.empty? params << "subject=#{encode(subject)}" if subject && !subject.empty? params << "body=#{encode(body)}" if body && !body.empty? uri = "mailto:#{encode_list(to)}" params.empty? ? uri : "#{uri}?#{params.join('&')}" end |
.encode(value) ⇒ String
Percent-encode one value byte-wise (uppercase hex).
53 54 55 |
# File 'lib/pikuri/thunderbird/mailto_uri.rb', line 53 def self.encode(value) value.to_s.b.gsub(UNRESERVED) { |byte| format('%%%02X', byte.ord) } end |
.encode_list(addrs) ⇒ String
Returns each address percent-encoded, joined by literal commas.
47 |
# File 'lib/pikuri/thunderbird/mailto_uri.rb', line 47 def self.encode_list(addrs) = addrs.map { |a| encode(a) }.join(',') |