Current section
Files
Jump to
Current section
Files
lib/http_cookie/url.ex
defmodule HttpCookie.URL do
@moduledoc false
# 5.1.2. Canonicalized Host Names
#
# A canonicalized host name is the string generated by the following
# algorithm:
#
# 1. Convert the host name to a sequence of individual domain name
# labels.
#
# 2. Convert each label that is not a Non-Reserved LDH (NR-LDH) label,
# to an A-label (see Section 2.3.2.1 of [RFC5890] for the former
# and latter), or to a "punycode label" (a label resulting from the
# "ToASCII" conversion in Section 4 of [RFC3490]), as appropriate
# (see Section 6.3 of this specification).
#
# 3. Concatenate the resulting labels, separated by a %x2E (".")
# character.
def canonicalize_domain(domain) do
domain
|> :idna.to_ascii()
|> to_string()
|> String.downcase()
end
def normalize_path(path) when path in [nil, ""], do: "/"
def normalize_path(path), do: path
# 5.1.4. Paths and Path-Match
#
# The user agent MUST use an algorithm equivalent to the following
# algorithm to compute the default-path of a cookie:
#
# 1. Let uri-path be the path portion of the request-uri if such a
# portion exists (and empty otherwise). For example, if the
# request-uri contains just a path (and optional query string),
# then the uri-path is that path (without the %x3F ("?") character
# or query string), and if the request-uri contains a full
# absoluteURI, the uri-path is the path component of that URI.
#
# 2. If the uri-path is empty or if the first character of the uri-
# path is not a %x2F ("/") character, output %x2F ("/") and skip
# the remaining steps.
#
# 3. If the uri-path contains no more than one %x2F ("/") character,
# output %x2F ("/") and skip the remaining step.
#
# 4. Output the characters of the uri-path from the first character up
# to, but not including, the right-most %x2F ("/").
def default_path(request_url) do
request_path = normalize_path(request_url.path)
cond do
request_path == "/" -> "/"
request_path =~ ~r|^[^/]| -> "/"
request_path =~ ~r|^/[^/]*$| -> "/"
true -> String.split(request_path, ~r|/[^/]*$|) |> hd()
end
end
# 5.1.4. Paths and Path-Match
# [...]
# A request-path path-matches a given cookie-path if at least one of
# the following conditions holds:
#
# o The cookie-path and the request-path are identical.
def path_match?(path, path), do: true
# (continued)
# o The cookie-path is a prefix of the request-path, and the last
# character of the cookie-path is %x2F ("/").
#
# o The cookie-path is a prefix of the request-path, and the first
# character of the request-path that is not included in the cookie-
# path is a %x2F ("/") character.
def path_match?(request_path, cookie_path) do
cond do
String.starts_with?(request_path, cookie_path) and String.ends_with?(cookie_path, "/") ->
true
request_path =~ ~r|^#{Regex.escape(cookie_path)}/| ->
true
true ->
false
end
end
# 5.1.3. Domain Matching
#
# A string domain-matches a given domain string if at least one of the
# following conditions hold:
#
# o The domain string and the string are identical. (Note that both
# the domain string and the string will have been canonicalized to
# lower case at this point.)
def domain_match?(domain, domain), do: true
# (continued)
# o All of the following conditions hold:
#
# * The domain string is a suffix of the string.
#
# * The last character of the string that is not included in the
# domain string is a %x2E (".") character.
#
# * The string is a host name (i.e., not an IP address).
def domain_match?(request_domain, cookie_domain) do
request_domain =~ ~r/\.#{Regex.escape(cookie_domain)}$/ and !ip_address?(request_domain)
end
# NOTE: A "public suffix" is a domain that is controlled by a
# public registry, such as "com", "co.uk", and "pvt.k12.wy.us".
# This step is essential for preventing attacker.com from
# disrupting the integrity of example.com by setting a cookie
# with a Domain attribute of "com". Unfortunately, the set of
# public suffixes (also known as "registry controlled domains")
# changes over time. If feasible, user agents SHOULD use an
# up-to-date public suffix list, such as the one maintained by
# the Mozilla project at <http://publicsuffix.org/>.
def public_suffix?(""), do: false
if Code.ensure_loaded?(PublicSuffix) do
def public_suffix?(domain) do
domain == PublicSuffix.public_suffix(domain)
end
else
def public_suffix?(_domain) do
# work around for typing violation that's reported
# because the function only returns false when the
# dependency is missing - but it should not be called in that
# case anyway because the option velidation should fail before that
if :rand.uniform(10) <= 10 do
raise "Missing :public_suffix dependency"
else
true
end
end
end
def ip_address?(str) do
charlist = String.to_charlist(str)
case :inet.parse_address(charlist) do
{:ok, _ip} -> true
_ -> false
end
end
end