http-cookie 1.0.0.pre12 → 1.0.4
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +5 -5
- data/.travis.yml +12 -8
- data/CHANGELOG.md +29 -0
- data/README.md +28 -0
- data/http-cookie.gemspec +7 -5
- data/lib/http/cookie.rb +37 -32
- data/lib/http/cookie/ruby_compat.rb +5 -1
- data/lib/http/cookie/scanner.rb +12 -25
- data/lib/http/cookie/version.rb +1 -1
- data/lib/http/cookie_jar.rb +57 -35
- data/lib/http/cookie_jar/abstract_saver.rb +16 -2
- data/lib/http/cookie_jar/abstract_store.rb +30 -6
- data/lib/http/cookie_jar/cookiestxt_saver.rb +23 -2
- data/lib/http/cookie_jar/hash_store.rb +6 -15
- data/lib/http/cookie_jar/mozilla_store.rb +147 -33
- data/lib/http/cookie_jar/yaml_saver.rb +29 -4
- data/test/helper.rb +30 -0
- data/test/test_http_cookie.rb +76 -23
- data/test/test_http_cookie_jar.rb +130 -14
- metadata +34 -29
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
|
-
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 6e3fc7cc6e374fa6bdd53c2a32cae8b8856615df4aad76fe496ab6375738652e
|
4
|
+
data.tar.gz: 0132bb41158fa3bf84a0adf2fcf7226115a1126efbc12c879f465a8c2114215b
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 341c0b9947ed005f8c73030ad7b5380aa19d76cbfc2f3f09ac69207a9a5b33e62f0094f6b68b98ad839f110c565f7afc08a9ff504c82affe736a8bcb55908e05
|
7
|
+
data.tar.gz: c4346ce6bae86a394c72b38f079c3c272205df3b7453c630e1dc3fc1ae7dec7ee0f652c9b5f2b12151fb6b7b402a5fbc41b832c051cf17102bd1cd584f72b461
|
data/.travis.yml
CHANGED
@@ -1,17 +1,21 @@
|
|
1
|
+
sudo: false
|
1
2
|
language: ruby
|
3
|
+
cache: bundler
|
2
4
|
rvm:
|
3
5
|
- 1.8.7
|
4
6
|
- ree
|
5
|
-
- 1.9.2
|
6
7
|
- 1.9.3
|
7
8
|
- 2.0.0
|
9
|
+
- 2.1
|
10
|
+
- 2.2
|
11
|
+
- 2.3.0
|
8
12
|
- ruby-head
|
9
|
-
- jruby-
|
10
|
-
- jruby-
|
11
|
-
-
|
12
|
-
- rbx-18mode
|
13
|
-
- rbx-19mode
|
13
|
+
- jruby-1.7
|
14
|
+
- jruby-9
|
15
|
+
- rbx-2
|
14
16
|
matrix:
|
15
17
|
allow_failures:
|
16
|
-
- rvm:
|
17
|
-
- rvm: rbx-
|
18
|
+
- rvm: ruby-head
|
19
|
+
- rvm: rbx-2
|
20
|
+
before_install:
|
21
|
+
- gem update bundler
|
data/CHANGELOG.md
ADDED
@@ -0,0 +1,29 @@
|
|
1
|
+
## Unreleased
|
2
|
+
|
3
|
+
- Support Mozilla's cookie storage format up to version 7.
|
4
|
+
|
5
|
+
- Fix the time representation with creationTime and lastAccessed in
|
6
|
+
MozillaStore. (#8)
|
7
|
+
|
8
|
+
## 1.0.3 (2016-09-30)
|
9
|
+
|
10
|
+
- Treat comma as normal character in HTTP::Cookie.cookie_value_to_hash
|
11
|
+
instead of key-value pair separator. This should fix the problem
|
12
|
+
described in CVE-2016-7401.
|
13
|
+
|
14
|
+
## 1.0.2 (2013-09-10)
|
15
|
+
|
16
|
+
- Fix HTTP::Cookie.parse so that it does not raise ArgumentError
|
17
|
+
when it finds a bad name or value that is parsable but considered
|
18
|
+
invalid.
|
19
|
+
|
20
|
+
## 1.0.1 (2013-04-21)
|
21
|
+
|
22
|
+
- Minor error handling improvements and documentation updates.
|
23
|
+
|
24
|
+
- Argument error regarding specifying store/saver classes no longer
|
25
|
+
raises IndexError, but either ArgumentError or TypeError.
|
26
|
+
|
27
|
+
## 1.0.0 (2013-04-17)
|
28
|
+
|
29
|
+
- Initial Release.
|
data/README.md
CHANGED
@@ -8,6 +8,34 @@ It was originally a part of the
|
|
8
8
|
separated as an independent library in the hope of serving as a common
|
9
9
|
component that is reusable from any HTTP related piece of software.
|
10
10
|
|
11
|
+
The following is an incomplete list of its features:
|
12
|
+
|
13
|
+
* Its behavior is highly compatible with that of today's major web
|
14
|
+
browsers.
|
15
|
+
|
16
|
+
* It is based on and conforms to RFC 6265 (the latest standard for the
|
17
|
+
HTTP cookie mechanism) to a high extent, with real world conventions
|
18
|
+
deeply in mind.
|
19
|
+
|
20
|
+
* It takes eTLD (effective TLD, also known as "Public Suffix") into
|
21
|
+
account just as major browsers do, to reject cookies with an eTLD
|
22
|
+
domain like "org", "co.jp", or "appspot.com". This feature is
|
23
|
+
brought to you by the domain_name gem.
|
24
|
+
|
25
|
+
* The number of cookies and the size are properly capped so that a
|
26
|
+
cookie store does not get flooded.
|
27
|
+
|
28
|
+
* It supports the legacy Netscape cookies.txt format for
|
29
|
+
serialization, maximizing the interoperability with other
|
30
|
+
implementations.
|
31
|
+
|
32
|
+
* It supports the cookies.sqlite format adopted by Mozilla Firefox for
|
33
|
+
backend store database which can be shared among multiple program
|
34
|
+
instances.
|
35
|
+
|
36
|
+
* It is relatively easy to add a new serialization format or a backend
|
37
|
+
store because of its modular API.
|
38
|
+
|
11
39
|
## Installation
|
12
40
|
|
13
41
|
Add this line to your application's `Gemfile`:
|
data/http-cookie.gemspec
CHANGED
@@ -13,9 +13,10 @@ Gem::Specification.new do |gem|
|
|
13
13
|
'Mike Dalessio' => 'mike.dalessio@gmail.com',
|
14
14
|
}.instance_eval { [keys, values] }
|
15
15
|
|
16
|
-
gem.description = %q{
|
17
|
-
gem.summary = %q{A Ruby library to handle HTTP Cookies}
|
16
|
+
gem.description = %q{HTTP::Cookie is a Ruby library to handle HTTP Cookies based on RFC 6265. It has with security, standards compliance and compatibility in mind, to behave just the same as today's major web browsers. It has builtin support for the legacy cookies.txt and the latest cookies.sqlite formats of Mozilla Firefox, and its modular API makes it easy to add support for a new backend store.}
|
17
|
+
gem.summary = %q{A Ruby library to handle HTTP Cookies based on RFC 6265}
|
18
18
|
gem.homepage = "https://github.com/sparklemotion/http-cookie"
|
19
|
+
gem.license = "MIT"
|
19
20
|
|
20
21
|
gem.files = `git ls-files`.split($/)
|
21
22
|
gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
|
@@ -26,8 +27,9 @@ Gem::Specification.new do |gem|
|
|
26
27
|
gem.add_runtime_dependency("domain_name", ["~> 0.5"])
|
27
28
|
gem.add_development_dependency("sqlite3", ["~> 1.3.3"]) unless defined?(JRUBY_VERSION)
|
28
29
|
gem.add_development_dependency("bundler", [">= 1.2.0"])
|
29
|
-
gem.add_development_dependency("test-unit", [">= 2.4.3"])
|
30
|
-
gem.add_development_dependency("rake", [">= 0.9.2.2"])
|
31
|
-
gem.add_development_dependency("rdoc",
|
30
|
+
gem.add_development_dependency("test-unit", [">= 2.4.3", *("< 3" if RUBY_VERSION < "1.9")])
|
31
|
+
gem.add_development_dependency("rake", [">= 0.9.2.2", *("< 11" if RUBY_VERSION < "1.9")])
|
32
|
+
gem.add_development_dependency("rdoc", RUBY_VERSION > "1.9" ? "> 2.4.2" : "~> 2.4.2")
|
32
33
|
gem.add_development_dependency("simplecov", [">= 0"])
|
34
|
+
gem.add_development_dependency("json", ["< 2"]) if RUBY_VERSION < "2.0"
|
33
35
|
end
|
data/lib/http/cookie.rb
CHANGED
@@ -87,7 +87,7 @@ class HTTP::Cookie
|
|
87
87
|
# The Expires attribute value as a Time object.
|
88
88
|
#
|
89
89
|
# The setter method accepts a Time object, a string representation
|
90
|
-
# of date/time, or `nil`.
|
90
|
+
# of date/time that Time.parse can understand, or `nil`.
|
91
91
|
#
|
92
92
|
# Setting this value resets #max_age to nil. When #max_age is
|
93
93
|
# non-nil, #expires returns `created_at + max_age`.
|
@@ -111,12 +111,11 @@ class HTTP::Cookie
|
|
111
111
|
# new(**attr_hash)
|
112
112
|
#
|
113
113
|
# Creates a cookie object. For each key of `attr_hash`, the setter
|
114
|
-
# is called if defined
|
115
|
-
#
|
116
|
-
#
|
117
|
-
#
|
118
|
-
#
|
119
|
-
# a setter method raises will be passed through.
|
114
|
+
# is called if defined and any error (typically ArgumentError or
|
115
|
+
# TypeError) that is raised will be passed through. Each key can be
|
116
|
+
# either a downcased symbol or a string that may be mixed case.
|
117
|
+
# Support for the latter may, however, be obsoleted in future when
|
118
|
+
# Ruby 2.0's keyword syntax is adopted.
|
120
119
|
#
|
121
120
|
# If `value` is omitted or it is nil, an expiration cookie is
|
122
121
|
# created unless `max_age` or `expires` (`expires_at`) is given.
|
@@ -129,7 +128,7 @@ class HTTP::Cookie
|
|
129
128
|
# new("name" => "uid", "value" => "a12345", "Domain" => 'www.example.org')
|
130
129
|
#
|
131
130
|
def initialize(*args)
|
132
|
-
@origin = @domain = @path =
|
131
|
+
@name = @origin = @domain = @path =
|
133
132
|
@expires = @max_age = nil
|
134
133
|
@for_domain = @secure = @httponly = false
|
135
134
|
@session = true
|
@@ -157,8 +156,8 @@ class HTTP::Cookie
|
|
157
156
|
end
|
158
157
|
for_domain = false
|
159
158
|
domain = max_age = origin = nil
|
160
|
-
attr_hash.each_pair { |
|
161
|
-
case key
|
159
|
+
attr_hash.each_pair { |okey, val|
|
160
|
+
case key ||= okey
|
162
161
|
when :name
|
163
162
|
self.name = val
|
164
163
|
when :value
|
@@ -175,28 +174,26 @@ class HTTP::Cookie
|
|
175
174
|
# Let max_age take precedence over expires
|
176
175
|
max_age = val
|
177
176
|
when :expires, :expires_at
|
178
|
-
self.expires = val
|
177
|
+
self.expires = val unless max_age
|
179
178
|
when :httponly, :httponly?
|
180
179
|
@httponly = val
|
181
180
|
when :secure, :secure?
|
182
181
|
@secure = val
|
183
|
-
when /[A-Z]/
|
184
|
-
warn "keyword should be downcased: #{key}" if $VERBOSE
|
185
|
-
key = key.downcase
|
186
|
-
redo
|
187
182
|
when Symbol
|
188
183
|
setter = :"#{key}="
|
189
184
|
if respond_to?(setter)
|
190
185
|
__send__(setter, val)
|
191
186
|
else
|
192
|
-
warn "unknown
|
187
|
+
warn "unknown attribute name: #{okey.inspect}" if $VERBOSE
|
188
|
+
next
|
193
189
|
end
|
194
190
|
when String
|
195
|
-
|
191
|
+
warn "use downcased symbol for keyword: #{okey.inspect}" if $VERBOSE
|
192
|
+
key = key.downcase.to_sym
|
196
193
|
redo
|
197
194
|
else
|
198
|
-
|
199
|
-
|
195
|
+
warn "invalid keyword ignored: #{okey.inspect}" if $VERBOSE
|
196
|
+
next
|
200
197
|
end
|
201
198
|
}
|
202
199
|
if @name.nil?
|
@@ -284,14 +281,21 @@ class HTTP::Cookie
|
|
284
281
|
Scanner.new(set_cookie, logger).scan_set_cookie { |name, value, attrs|
|
285
282
|
break if name.nil? || name.empty?
|
286
283
|
|
287
|
-
|
284
|
+
begin
|
285
|
+
cookie = new(name, value)
|
286
|
+
rescue => e
|
287
|
+
logger.warn("Invalid name or value: #{e}") if logger
|
288
|
+
next
|
289
|
+
end
|
288
290
|
cookie.created_at = created_at if created_at
|
289
291
|
attrs.each { |aname, avalue|
|
290
292
|
begin
|
291
293
|
case aname
|
292
294
|
when 'domain'
|
293
295
|
cookie.for_domain = true
|
294
|
-
|
296
|
+
# The following may negate @for_domain if the value is
|
297
|
+
# an eTLD or IP address, hence this order.
|
298
|
+
cookie.domain = avalue
|
295
299
|
when 'path'
|
296
300
|
cookie.path = avalue
|
297
301
|
when 'expires'
|
@@ -343,7 +347,7 @@ class HTTP::Cookie
|
|
343
347
|
attr_reader :name
|
344
348
|
|
345
349
|
# See #name.
|
346
|
-
def name=
|
350
|
+
def name= name
|
347
351
|
name = (String.try_convert(name) or
|
348
352
|
raise TypeError, "#{name.class} is not a String")
|
349
353
|
if name.empty?
|
@@ -360,7 +364,7 @@ class HTTP::Cookie
|
|
360
364
|
attr_reader :value
|
361
365
|
|
362
366
|
# See #value.
|
363
|
-
def value=
|
367
|
+
def value= value
|
364
368
|
if value.nil?
|
365
369
|
self.expires = UNIX_EPOCH
|
366
370
|
return @value = ''
|
@@ -379,7 +383,7 @@ class HTTP::Cookie
|
|
379
383
|
attr_reader :domain
|
380
384
|
|
381
385
|
# See #domain.
|
382
|
-
def domain=
|
386
|
+
def domain= domain
|
383
387
|
case domain
|
384
388
|
when nil
|
385
389
|
@for_domain = false
|
@@ -437,7 +441,7 @@ class HTTP::Cookie
|
|
437
441
|
attr_reader :path
|
438
442
|
|
439
443
|
# See #path.
|
440
|
-
def path=
|
444
|
+
def path= path
|
441
445
|
path = (String.try_convert(path) or
|
442
446
|
raise TypeError, "#{path.class} is not a String")
|
443
447
|
@path = path.start_with?('/') ? path : '/'
|
@@ -446,10 +450,11 @@ class HTTP::Cookie
|
|
446
450
|
attr_reader :origin
|
447
451
|
|
448
452
|
# See #origin.
|
449
|
-
def origin=
|
453
|
+
def origin= origin
|
450
454
|
return origin if origin == @origin
|
451
455
|
@origin.nil? or
|
452
456
|
raise ArgumentError, "origin cannot be changed once it is set"
|
457
|
+
# Delay setting @origin because #domain= or #path= may fail
|
453
458
|
origin = URI(origin)
|
454
459
|
if URI::HTTP === origin
|
455
460
|
self.domain ||= origin.host
|
@@ -482,7 +487,7 @@ class HTTP::Cookie
|
|
482
487
|
end
|
483
488
|
|
484
489
|
# See #expires.
|
485
|
-
def expires=
|
490
|
+
def expires= t
|
486
491
|
case t
|
487
492
|
when nil, Time
|
488
493
|
else
|
@@ -499,8 +504,7 @@ class HTTP::Cookie
|
|
499
504
|
attr_reader :max_age
|
500
505
|
|
501
506
|
# See #max_age.
|
502
|
-
def max_age=
|
503
|
-
@expires = nil
|
507
|
+
def max_age= sec
|
504
508
|
case sec
|
505
509
|
when Integer, nil
|
506
510
|
else
|
@@ -510,6 +514,7 @@ class HTTP::Cookie
|
|
510
514
|
raise ArgumentError, "invalid Max-Age: #{sec.inspect}"
|
511
515
|
sec = str.to_i
|
512
516
|
end
|
517
|
+
@expires = nil
|
513
518
|
if @session = sec.nil?
|
514
519
|
@max_age = nil
|
515
520
|
else
|
@@ -603,7 +608,7 @@ class HTTP::Cookie
|
|
603
608
|
if @domain
|
604
609
|
string << "; Domain=#{@domain}"
|
605
610
|
else
|
606
|
-
raise "for_domain is specified but domain is
|
611
|
+
raise "for_domain is specified but domain is unknown"
|
607
612
|
end
|
608
613
|
end
|
609
614
|
if @path
|
@@ -611,7 +616,7 @@ class HTTP::Cookie
|
|
611
616
|
string << "; Path=#{@path}"
|
612
617
|
end
|
613
618
|
else
|
614
|
-
raise "path is
|
619
|
+
raise "path is unknown"
|
615
620
|
end
|
616
621
|
if @max_age
|
617
622
|
string << "; Max-Age=#{@max_age}"
|
@@ -635,7 +640,7 @@ class HTTP::Cookie
|
|
635
640
|
|
636
641
|
# Compares the cookie with another. When there are many cookies with
|
637
642
|
# the same name for a URL, the value of the smallest must be used.
|
638
|
-
def <=>
|
643
|
+
def <=> other
|
639
644
|
# RFC 6265 5.4
|
640
645
|
# Precedence: 1. longer path 2. older creation
|
641
646
|
(@name <=> other.name).nonzero? ||
|
@@ -1,5 +1,5 @@
|
|
1
1
|
class Array
|
2
|
-
def select!
|
2
|
+
def select! # :yield: x
|
3
3
|
i = 0
|
4
4
|
each_with_index { |x, j|
|
5
5
|
yield x or next
|
@@ -10,6 +10,10 @@ class Array
|
|
10
10
|
self[i..-1] = []
|
11
11
|
self
|
12
12
|
end unless method_defined?(:select!)
|
13
|
+
|
14
|
+
def sort_by!(&block) # :yield: x
|
15
|
+
replace(sort_by(&block))
|
16
|
+
end unless method_defined?(:sort_by!)
|
13
17
|
end
|
14
18
|
|
15
19
|
class Hash
|
data/lib/http/cookie/scanner.rb
CHANGED
@@ -50,7 +50,7 @@ class HTTP::Cookie::Scanner < StringScanner
|
|
50
50
|
}
|
51
51
|
end
|
52
52
|
|
53
|
-
def scan_value
|
53
|
+
def scan_value(comma_as_separator = false)
|
54
54
|
''.tap { |s|
|
55
55
|
case
|
56
56
|
when scan(/[^,;"]+/)
|
@@ -59,7 +59,9 @@ class HTTP::Cookie::Scanner < StringScanner
|
|
59
59
|
# RFC 6265 2.2
|
60
60
|
# A cookie-value may be DQUOTE'd.
|
61
61
|
s << scan_dquoted
|
62
|
-
when check(
|
62
|
+
when check(/;/)
|
63
|
+
break
|
64
|
+
when comma_as_separator && check(RE_COOKIE_COMMA)
|
63
65
|
break
|
64
66
|
else
|
65
67
|
s << getch
|
@@ -68,12 +70,12 @@ class HTTP::Cookie::Scanner < StringScanner
|
|
68
70
|
}
|
69
71
|
end
|
70
72
|
|
71
|
-
def scan_name_value
|
73
|
+
def scan_name_value(comma_as_separator = false)
|
72
74
|
name = scan_name
|
73
75
|
if skip(/\=/)
|
74
|
-
value = scan_value
|
76
|
+
value = scan_value(comma_as_separator)
|
75
77
|
else
|
76
|
-
scan_value
|
78
|
+
scan_value(comma_as_separator)
|
77
79
|
value = nil
|
78
80
|
end
|
79
81
|
[name, value]
|
@@ -152,13 +154,6 @@ class HTTP::Cookie::Scanner < StringScanner
|
|
152
154
|
end
|
153
155
|
|
154
156
|
def scan_set_cookie
|
155
|
-
unless block_given?
|
156
|
-
scan_set_cookie { |*values|
|
157
|
-
return values
|
158
|
-
}
|
159
|
-
return
|
160
|
-
end
|
161
|
-
|
162
157
|
# RFC 6265 4.1.1 & 5.2
|
163
158
|
until eos?
|
164
159
|
start = pos
|
@@ -166,7 +161,7 @@ class HTTP::Cookie::Scanner < StringScanner
|
|
166
161
|
|
167
162
|
skip_wsp
|
168
163
|
|
169
|
-
name, value = scan_name_value
|
164
|
+
name, value = scan_name_value(true)
|
170
165
|
if value.nil?
|
171
166
|
@logger.warn("Cookie definition lacks a name-value pair.") if @logger
|
172
167
|
elsif name.empty?
|
@@ -183,7 +178,7 @@ class HTTP::Cookie::Scanner < StringScanner
|
|
183
178
|
break
|
184
179
|
when skip(/;/)
|
185
180
|
skip_wsp
|
186
|
-
aname, avalue = scan_name_value
|
181
|
+
aname, avalue = scan_name_value(true)
|
187
182
|
next if aname.empty? || value.nil?
|
188
183
|
aname.downcase!
|
189
184
|
case aname
|
@@ -221,24 +216,16 @@ class HTTP::Cookie::Scanner < StringScanner
|
|
221
216
|
end
|
222
217
|
|
223
218
|
def scan_cookie
|
224
|
-
unless block_given?
|
225
|
-
scan_cookie { |*values|
|
226
|
-
return values
|
227
|
-
}
|
228
|
-
return
|
229
|
-
end
|
230
|
-
|
231
219
|
# RFC 6265 4.1.1 & 5.4
|
232
220
|
until eos?
|
233
221
|
skip_wsp
|
234
222
|
|
235
|
-
|
223
|
+
# Do not treat comma in a Cookie header value as separator; see CVE-2016-7401
|
224
|
+
name, value = scan_name_value(false)
|
236
225
|
|
237
226
|
yield name, value if value
|
238
227
|
|
239
|
-
|
240
|
-
# values of a header.
|
241
|
-
skip(/[;,]/)
|
228
|
+
skip(/;/)
|
242
229
|
end
|
243
230
|
end
|
244
231
|
end
|