mirror of
https://github.com/samsonjs/http-cookie.git
synced 2026-03-25 08:55:53 +00:00
Introduce a new cookie object attribute "origin".
Change the signature of HTTP::Cookie.parse() so that it only optionally takes an origin URI. When one is given, the method checks if each piece of cookie in the header value is valid and acceptable from the origin to ignore unacceptable cookies.
This commit is contained in:
parent
a1e5e1628a
commit
532101a102
2 changed files with 88 additions and 35 deletions
|
|
@ -31,6 +31,8 @@ class HTTP::Cookie
|
|||
attr_accessor :created_at
|
||||
attr_accessor :accessed_at
|
||||
|
||||
attr_accessor :origin
|
||||
|
||||
# :call-seq:
|
||||
# new(name, value)
|
||||
# new(name, value, attr_hash)
|
||||
|
|
@ -95,12 +97,21 @@ class HTTP::Cookie
|
|||
class << self
|
||||
include URIFix if defined?(URIFix)
|
||||
|
||||
# Parses a Set-Cookie header value +set_cookie+ sent from +origin+
|
||||
# into an array of Cookie objects. Parts (separated by commas)
|
||||
# that are malformed are ignored.
|
||||
# Parses a Set-Cookie header value +set_cookie+ into an array of
|
||||
# Cookie objects. Parts (separated by commas) that are malformed
|
||||
# are ignored.
|
||||
#
|
||||
# If a block is given, each cookie object is passed to the block.
|
||||
def parse(origin, set_cookie, logger = nil)
|
||||
#
|
||||
# The cookie's origin URI/URL and a logger object can be passed in
|
||||
# +options+ with the keywords +:origin+ and +:logger+,
|
||||
# respectively.
|
||||
def parse(set_cookie, options = nil, &block)
|
||||
if options
|
||||
logger = options[:logger]
|
||||
origin = options[:origin] and origin = URI(origin)
|
||||
end
|
||||
|
||||
[].tap { |cookies|
|
||||
set_cookie.split(/,(?=[^;,]*=)|,$/).each { |c|
|
||||
cookie_elem = c.split(/;+/)
|
||||
|
|
@ -163,15 +174,21 @@ class HTTP::Cookie
|
|||
end
|
||||
end
|
||||
|
||||
cookie.path ||= (origin + './').path
|
||||
cookie.secure ||= false
|
||||
cookie.domain ||= origin.host
|
||||
|
||||
# RFC 6265 4.1.2.2
|
||||
cookie.expires = Time.now + cookie.max_age if cookie.max_age
|
||||
cookie.session = !cookie.expires
|
||||
|
||||
# Move this in to the cookie jar
|
||||
if origin
|
||||
begin
|
||||
cookie.origin = origin
|
||||
rescue => e
|
||||
logger.warn("Invalid cookie for the origin: #{origin} (#{e})") if logger
|
||||
next
|
||||
end
|
||||
end
|
||||
|
||||
yield cookie if block_given?
|
||||
|
||||
cookies << cookie
|
||||
|
|
@ -202,6 +219,15 @@ class HTTP::Cookie
|
|||
@domain = @domain_name.hostname
|
||||
end
|
||||
|
||||
def origin=(origin)
|
||||
origin = URI(origin)
|
||||
acceptable_from_uri?(origin) or
|
||||
raise ArgumentError, "unacceptable cookie sent from URI #{origin}"
|
||||
self.domain ||= origin.host
|
||||
self.path ||= (origin + './').path
|
||||
@origin = origin
|
||||
end
|
||||
|
||||
def expires=(t)
|
||||
@expires = t && (t.is_a?(Time) ? t.httpdate : t.to_s)
|
||||
end
|
||||
|
|
@ -222,7 +248,7 @@ class HTTP::Cookie
|
|||
|
||||
# RFC 6265 5.3
|
||||
# When the user agent "receives a cookie":
|
||||
return host.hostname == domain unless @for_domain
|
||||
return domain.nil? || host.hostname == domain unless @for_domain
|
||||
|
||||
if host.cookie_domain?(@domain_name)
|
||||
true
|
||||
|
|
@ -236,7 +262,7 @@ class HTTP::Cookie
|
|||
|
||||
def valid_for_uri?(uri)
|
||||
return false if secure? && uri.scheme != 'https'
|
||||
acceptable_from_uri?(uri) && uri.path.start_with?(path)
|
||||
acceptable_from_uri?(uri) && (@path.nil? || uri.path.start_with?(@path))
|
||||
end
|
||||
|
||||
def to_s
|
||||
|
|
|
|||
|
|
@ -34,7 +34,7 @@ class TestHTTPCookie < Test::Unit::TestCase
|
|||
dates.each do |date|
|
||||
cookie = "PREF=1; expires=#{date}"
|
||||
silently do
|
||||
HTTP::Cookie.parse(url, cookie) { |c|
|
||||
HTTP::Cookie.parse(cookie, :origin => url) { |c|
|
||||
assert c.expires, "Tried parsing: #{date}"
|
||||
assert_equal(true, c.expires < yesterday)
|
||||
}
|
||||
|
|
@ -47,7 +47,7 @@ class TestHTTPCookie < Test::Unit::TestCase
|
|||
|
||||
uri = URI.parse 'http://example'
|
||||
|
||||
HTTP::Cookie.parse uri, cookie_str do |cookie|
|
||||
HTTP::Cookie.parse cookie_str, :origin => uri do |cookie|
|
||||
assert_equal 'a', cookie.name
|
||||
assert_equal 'b', cookie.value
|
||||
end
|
||||
|
|
@ -58,7 +58,7 @@ class TestHTTPCookie < Test::Unit::TestCase
|
|||
|
||||
uri = URI.parse 'http://example'
|
||||
|
||||
HTTP::Cookie.parse uri, cookie_str do |cookie|
|
||||
HTTP::Cookie.parse cookie_str, :origin => uri do |cookie|
|
||||
assert_equal 'foo', cookie.name
|
||||
assert_equal 'bar', cookie.value
|
||||
assert_equal '/', cookie.path
|
||||
|
|
@ -72,7 +72,7 @@ class TestHTTPCookie < Test::Unit::TestCase
|
|||
|
||||
uri = URI.parse 'http://example'
|
||||
|
||||
HTTP::Cookie.parse uri, cookie_str do |cookie|
|
||||
HTTP::Cookie.parse cookie_str, :origin => uri do |cookie|
|
||||
assert_equal 'quoted', cookie.name
|
||||
assert_equal '"value"', cookie.value
|
||||
end
|
||||
|
|
@ -81,7 +81,7 @@ class TestHTTPCookie < Test::Unit::TestCase
|
|||
def test_parse_weird_cookie
|
||||
cookie = 'n/a, ASPSESSIONIDCSRRQDQR=FBLDGHPBNDJCPCGNCPAENELB; path=/'
|
||||
url = URI.parse('http://www.searchinnovation.com/')
|
||||
HTTP::Cookie.parse(url, cookie) { |c|
|
||||
HTTP::Cookie.parse(cookie, :origin => url) { |c|
|
||||
assert_equal('ASPSESSIONIDCSRRQDQR', c.name)
|
||||
assert_equal('FBLDGHPBNDJCPCGNCPAENELB', c.value)
|
||||
}
|
||||
|
|
@ -90,7 +90,7 @@ class TestHTTPCookie < Test::Unit::TestCase
|
|||
def test_double_semicolon
|
||||
double_semi = 'WSIDC=WEST;; domain=.williams-sonoma.com; path=/'
|
||||
url = URI.parse('http://williams-sonoma.com/')
|
||||
HTTP::Cookie.parse(url, double_semi) { |cookie|
|
||||
HTTP::Cookie.parse(double_semi, :origin => url) { |cookie|
|
||||
assert_equal('WSIDC', cookie.name)
|
||||
assert_equal('WEST', cookie.value)
|
||||
}
|
||||
|
|
@ -99,7 +99,7 @@ class TestHTTPCookie < Test::Unit::TestCase
|
|||
def test_parse_bad_version
|
||||
bad_cookie = 'PRETANET=TGIAqbFXtt; Name=/PRETANET; Path=/; Version=1.2; Content-type=text/html; Domain=192.168.6.196; expires=Friday, 13-November-2026 23:01:46 GMT;'
|
||||
url = URI.parse('http://localhost/')
|
||||
HTTP::Cookie.parse(url, bad_cookie) { |cookie|
|
||||
HTTP::Cookie.parse(bad_cookie, :origin => url) { |cookie|
|
||||
assert_nil(cookie.version)
|
||||
}
|
||||
end
|
||||
|
|
@ -107,7 +107,7 @@ class TestHTTPCookie < Test::Unit::TestCase
|
|||
def test_parse_bad_max_age
|
||||
bad_cookie = 'PRETANET=TGIAqbFXtt; Name=/PRETANET; Path=/; Max-Age=1.2; Content-type=text/html; Domain=192.168.6.196; expires=Friday, 13-November-2026 23:01:46 GMT;'
|
||||
url = URI.parse('http://localhost/')
|
||||
HTTP::Cookie.parse(url, bad_cookie) { |cookie|
|
||||
HTTP::Cookie.parse(bad_cookie, :origin => url) { |cookie|
|
||||
assert_nil(cookie.max_age)
|
||||
}
|
||||
end
|
||||
|
|
@ -122,7 +122,7 @@ class TestHTTPCookie < Test::Unit::TestCase
|
|||
silently do
|
||||
dates.each do |date|
|
||||
cookie = "PREF=1; expires=#{date}"
|
||||
HTTP::Cookie.parse(url, cookie) { |c|
|
||||
HTTP::Cookie.parse(cookie, :origin => url) { |c|
|
||||
assert_equal(true, c.expires.nil?)
|
||||
}
|
||||
end
|
||||
|
|
@ -134,7 +134,7 @@ class TestHTTPCookie < Test::Unit::TestCase
|
|||
|
||||
cookie_str = 'a=b; domain=.example.com'
|
||||
|
||||
cookie = HTTP::Cookie.parse(url, cookie_str).first
|
||||
cookie = HTTP::Cookie.parse(cookie_str, :origin => url).first
|
||||
|
||||
assert_equal 'example.com', cookie.domain
|
||||
assert cookie.for_domain?
|
||||
|
|
@ -145,7 +145,7 @@ class TestHTTPCookie < Test::Unit::TestCase
|
|||
|
||||
cookie_str = 'a=b; domain=example.com'
|
||||
|
||||
cookie = HTTP::Cookie.parse(url, cookie_str).first
|
||||
cookie = HTTP::Cookie.parse(cookie_str, :origin => url).first
|
||||
|
||||
assert_equal 'example.com', cookie.domain
|
||||
assert cookie.for_domain?
|
||||
|
|
@ -156,7 +156,7 @@ class TestHTTPCookie < Test::Unit::TestCase
|
|||
|
||||
cookie_str = 'a=b;'
|
||||
|
||||
cookie = HTTP::Cookie.parse(url, cookie_str).first
|
||||
cookie = HTTP::Cookie.parse(cookie_str, :origin => url).first
|
||||
|
||||
assert_equal 'example.com', cookie.domain
|
||||
assert !cookie.for_domain?
|
||||
|
|
@ -167,17 +167,17 @@ class TestHTTPCookie < Test::Unit::TestCase
|
|||
|
||||
date = 'Mon, 19 Feb 2012 19:26:04 GMT'
|
||||
|
||||
cookie = HTTP::Cookie.parse(url, "name=Akinori; expires=#{date}").first
|
||||
cookie = HTTP::Cookie.parse("name=Akinori; expires=#{date}", :origin => url).first
|
||||
assert_equal Time.at(1329679564), cookie.expires
|
||||
|
||||
cookie = HTTP::Cookie.parse(url, 'name=Akinori; max-age=3600').first
|
||||
cookie = HTTP::Cookie.parse('name=Akinori; max-age=3600', :origin => url).first
|
||||
assert_in_delta Time.now + 3600, cookie.expires, 1
|
||||
|
||||
# Max-Age has precedence over Expires
|
||||
cookie = HTTP::Cookie.parse(url, "name=Akinori; max-age=3600; expires=#{date}").first
|
||||
cookie = HTTP::Cookie.parse("name=Akinori; max-age=3600; expires=#{date}", :origin => url).first
|
||||
assert_in_delta Time.now + 3600, cookie.expires, 1
|
||||
|
||||
cookie = HTTP::Cookie.parse(url, "name=Akinori; expires=#{date}; max-age=3600").first
|
||||
cookie = HTTP::Cookie.parse("name=Akinori; expires=#{date}; max-age=3600", :origin => url).first
|
||||
assert_in_delta Time.now + 3600, cookie.expires, 1
|
||||
end
|
||||
|
||||
|
|
@ -191,7 +191,7 @@ class TestHTTPCookie < Test::Unit::TestCase
|
|||
'name=Akinori; expires=',
|
||||
'name=Akinori; max-age=',
|
||||
].each { |str|
|
||||
cookie = HTTP::Cookie.parse(url, str).first
|
||||
cookie = HTTP::Cookie.parse(str, :origin => url).first
|
||||
assert cookie.session, str
|
||||
}
|
||||
|
||||
|
|
@ -199,13 +199,13 @@ class TestHTTPCookie < Test::Unit::TestCase
|
|||
'name=Akinori; expires=Mon, 19 Feb 2012 19:26:04 GMT',
|
||||
'name=Akinori; max-age=3600',
|
||||
].each { |str|
|
||||
cookie = HTTP::Cookie.parse(url, str).first
|
||||
cookie = HTTP::Cookie.parse(str, :origin => url).first
|
||||
assert !cookie.session, str
|
||||
}
|
||||
end
|
||||
|
||||
def test_parse_many
|
||||
url = URI 'http://example/'
|
||||
url = URI 'http://localhost/'
|
||||
cookie_str =
|
||||
"abc, " \
|
||||
"name=Aaron; Domain=localhost; Expires=Sun, 06 Nov 2011 00:29:51 GMT; Path=/, " \
|
||||
|
|
@ -221,7 +221,7 @@ class TestHTTPCookie < Test::Unit::TestCase
|
|||
"no_domain2=no_domain; Expires=Sun, 06 Nov 2011 00:29:53 GMT; no_expires=nope; Domain, " \
|
||||
"no_domain3=no_domain; Expires=Sun, 06 Nov 2011 00:29:53 GMT; no_expires=nope; Domain="
|
||||
|
||||
cookies = HTTP::Cookie.parse url, cookie_str
|
||||
cookies = HTTP::Cookie.parse cookie_str, :origin => url
|
||||
assert_equal 13, cookies.length
|
||||
|
||||
name = cookies.find { |c| c.name == 'name' }
|
||||
|
|
@ -278,7 +278,7 @@ class TestHTTPCookie < Test::Unit::TestCase
|
|||
end
|
||||
end
|
||||
cookie = nil
|
||||
HTTP::Cookie.parse(url, cookie_text) { |p_cookie| cookie = p_cookie }
|
||||
HTTP::Cookie.parse(cookie_text, :origin => url) { |p_cookie| cookie = p_cookie }
|
||||
|
||||
assert_equal('12345%7D=ASDFWEE345%3DASda', cookie.to_s)
|
||||
assert_equal('/', cookie.path)
|
||||
|
|
@ -313,7 +313,7 @@ class TestHTTPCookie < Test::Unit::TestCase
|
|||
end
|
||||
end
|
||||
cookie = nil
|
||||
HTTP::Cookie.parse(url, cookie_text) { |p_cookie| cookie = p_cookie }
|
||||
HTTP::Cookie.parse(cookie_text, :origin => url) { |p_cookie| cookie = p_cookie }
|
||||
|
||||
assert_equal('12345%7D=', cookie.to_s)
|
||||
assert_equal('', cookie.value)
|
||||
|
|
@ -351,7 +351,7 @@ class TestHTTPCookie < Test::Unit::TestCase
|
|||
end
|
||||
end
|
||||
cookie = nil
|
||||
HTTP::Cookie.parse(url, cookie_text) { |p_cookie| cookie = p_cookie }
|
||||
HTTP::Cookie.parse(cookie_text, :origin => url) { |p_cookie| cookie = p_cookie }
|
||||
|
||||
assert_equal('12345%7D=ASDFWEE345%3DASda', cookie.to_s)
|
||||
assert_equal('/', cookie.path)
|
||||
|
|
@ -388,7 +388,7 @@ class TestHTTPCookie < Test::Unit::TestCase
|
|||
end
|
||||
end
|
||||
cookie = nil
|
||||
HTTP::Cookie.parse(url, cookie_text) { |p_cookie| cookie = p_cookie }
|
||||
HTTP::Cookie.parse(cookie_text, :origin => url) { |p_cookie| cookie = p_cookie }
|
||||
|
||||
assert_equal('12345%7D=ASDFWEE345%3DASda', cookie.to_s)
|
||||
assert_equal('/', cookie.path)
|
||||
|
|
@ -424,7 +424,7 @@ class TestHTTPCookie < Test::Unit::TestCase
|
|||
end
|
||||
end
|
||||
cookie = nil
|
||||
HTTP::Cookie.parse(url, cookie_text) { |p_cookie| cookie = p_cookie }
|
||||
HTTP::Cookie.parse(cookie_text, :origin => url) { |p_cookie| cookie = p_cookie }
|
||||
|
||||
assert_equal('12345%7D=ASDFWEE345%3DASda', cookie.to_s)
|
||||
assert_equal('/', cookie.path)
|
||||
|
|
@ -469,7 +469,7 @@ class TestHTTPCookie < Test::Unit::TestCase
|
|||
url = URI.parse('http://host.dom.example.com:8080/')
|
||||
|
||||
cookie_str = 'a=b; domain=Example.Com'
|
||||
cookie = HTTP::Cookie.parse(url, cookie_str).first
|
||||
cookie = HTTP::Cookie.parse(cookie_str, :origin => url).first
|
||||
assert 'example.com', cookie.domain
|
||||
|
||||
cookie.domain = DomainName(url.host)
|
||||
|
|
@ -485,5 +485,32 @@ class TestHTTPCookie < Test::Unit::TestCase
|
|||
}
|
||||
assert 'example.com', cookie.domain
|
||||
end
|
||||
|
||||
def test_origin=
|
||||
url = URI.parse('http://example.com/path/')
|
||||
|
||||
cookie_str = 'a=b'
|
||||
cookie = HTTP::Cookie.parse(cookie_str).first
|
||||
cookie.origin = url
|
||||
assert_equal '/path/', cookie.path
|
||||
assert_equal 'example.com', cookie.domain
|
||||
assert_equal false, cookie.for_domain
|
||||
assert_raises(ArgumentError) {
|
||||
cookie.origin = URI.parse('http://www.example.com/')
|
||||
}
|
||||
|
||||
cookie_str = 'a=b; domain=.example.com; path=/'
|
||||
cookie = HTTP::Cookie.parse(cookie_str).first
|
||||
cookie.origin = url
|
||||
assert_equal '/', cookie.path
|
||||
assert_equal 'example.com', cookie.domain
|
||||
assert_equal true, cookie.for_domain
|
||||
|
||||
cookie_str = 'a=b; domain=example.com'
|
||||
cookie = HTTP::Cookie.parse(cookie_str).first
|
||||
assert_raises(ArgumentError) {
|
||||
cookie.origin = URI.parse('http://example.org/')
|
||||
}
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue