mechanize 2.3 → 2.4

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of mechanize might be problematic. Click here for more details.

data.tar.gz.sig CHANGED
Binary file
@@ -1,8 +1,31 @@
1
1
  = Mechanize CHANGELOG
2
2
 
3
- === 2.3 / 2012-02-20
3
+ === 2.4
4
+
5
+ * Security fix:
6
+
7
+ Mechanize#auth and Mechanize#basic_auth allowed disclosure of passwords to
8
+ malicious servers and have been removed.
9
+
10
+ In prior versions of mechanize only one set of HTTP authentication
11
+ credentials were allowed for all connections. If a mechanize instance
12
+ connected to more than one server then a malicious server detecting
13
+ mechanize could ask for HTTP Basic authentication. This would expose the
14
+ username and password intended only for one server.
15
+
16
+ Mechanize#auth and Mechanize#basic_auth now warn when used.
17
+
18
+ To fix the warning switch to Mechanize#add_auth which requires at the URI
19
+ the credentials are intended for, the username and the password.
20
+ Optionally an HTTP authentication realm or NTLM domain may be provided.
4
21
 
5
22
  * Minor enhancement
23
+ * Improved exception messages for 401 Unauthorized responses. Mechanize now
24
+ tells you if you were missing credentials, had an incorrect password, etc.
25
+
26
+ === 2.3 / 2012-02-20
27
+
28
+ * Minor enhancements
6
29
  * Add support for the Max-Age attribute in the Set-Cookie header.
7
30
  * Added Mechanize::Download#body for compatibility with Mechanize::File when
8
31
  using Mechanize#get_file with Mechanize::Image or other Download-based
@@ -46,6 +46,7 @@ lib/mechanize/http.rb
46
46
  lib/mechanize/http/agent.rb
47
47
  lib/mechanize/http/auth_challenge.rb
48
48
  lib/mechanize/http/auth_realm.rb
49
+ lib/mechanize/http/auth_store.rb
49
50
  lib/mechanize/http/content_disposition_parser.rb
50
51
  lib/mechanize/http/www_authenticate_parser.rb
51
52
  lib/mechanize/image.rb
@@ -145,6 +146,7 @@ test/test_mechanize_history.rb
145
146
  test/test_mechanize_http_agent.rb
146
147
  test/test_mechanize_http_auth_challenge.rb
147
148
  test/test_mechanize_http_auth_realm.rb
149
+ test/test_mechanize_http_auth_store.rb
148
150
  test/test_mechanize_http_content_disposition_parser.rb
149
151
  test/test_mechanize_http_www_authenticate_parser.rb
150
152
  test/test_mechanize_image.rb
@@ -2,6 +2,7 @@ require 'rubygems'
2
2
  require 'mechanize'
3
3
 
4
4
  agent = Mechanize.new
5
+ agent.max_history = nil # unlimited history
5
6
  stack = agent.get(ARGV[0]).links
6
7
 
7
8
  while l = stack.pop
@@ -73,7 +73,7 @@ class Mechanize
73
73
  ##
74
74
  # The version of Mechanize you are using.
75
75
 
76
- VERSION = '2.3'
76
+ VERSION = '2.4'
77
77
 
78
78
  ##
79
79
  # Base mechanize error class
@@ -209,7 +209,7 @@ class Mechanize
209
209
  ##
210
210
  # Maximum number of items allowed in the history. The default setting is 50
211
211
  # pages. Note that the size of the history multiplied by the maximum
212
- # response body size
212
+ # response body size
213
213
 
214
214
  def max_history
215
215
  @agent.history.max_size
@@ -618,17 +618,42 @@ class Mechanize
618
618
  attr_reader :proxy_user
619
619
 
620
620
  ##
621
- # Sets the user and password to be used for HTTP authentication.
622
- # sets the optional domain for NTLM authentication
621
+ # *NOTE*: These credentials will be used as a default for any challenge
622
+ # exposing your password to disclosure to malicious servers. Use of this
623
+ # method will warn. This method is deprecated and will be removed in
624
+ # mechanize 3.
625
+ #
626
+ # Sets the +user+ and +password+ as the default credentials to be used for
627
+ # HTTP authentication for any server. The +domain+ is used for NTLM
628
+ # authentication.
629
+
630
+ def auth user, password, domain = nil
631
+ caller.first =~ /(.*?):(\d+).*?$/
632
+
633
+ warn <<-WARNING
634
+ At #{$1} line #{$2}
635
+
636
+ Use of #auth and #basic_auth are deprecated due to a security vulnerability.
637
+
638
+ WARNING
623
639
 
624
- def auth(user, password, domain = nil)
625
- @agent.user = user
626
- @agent.password = password
627
- @agent.domain = domain
640
+ @agent.add_default_auth user, password, domain
628
641
  end
629
642
 
630
643
  alias basic_auth auth
631
644
 
645
+ ##
646
+ # Adds credentials +user+, +pass+ for +uri+. If +realm+ is set the
647
+ # credentials are used only for that realm. If +realm+ is not set the
648
+ # credentials become the default for any realm on that URI.
649
+ #
650
+ # +domain+ and +realm+ are exclusive as NTLM does not follow RFC 2617. If
651
+ # +domain+ is given it is only used for NTLM authentication.
652
+
653
+ def add_auth uri, user, password, realm = nil, domain = nil
654
+ @agent.add_auth uri, user, password, realm, domain
655
+ end
656
+
632
657
  ##
633
658
  # Are If-Modified-Since conditional requests enabled?
634
659
 
@@ -2,12 +2,15 @@
2
2
  # Fake response for dealing with file:/// requests
3
3
 
4
4
  class Mechanize::FileResponse
5
+
5
6
  def initialize(file_path)
6
7
  @file_path = file_path
8
+ @uri = nil
7
9
  end
8
10
 
9
11
  def read_body
10
- raise Mechanize::ResponseCodeError, self unless File.exist? @file_path
12
+ raise Mechanize::ResponseCodeError.new(self) unless
13
+ File.exist? @file_path
11
14
 
12
15
  if directory?
13
16
  yield dir_body
@@ -30,10 +33,10 @@ class Mechanize::FileResponse
30
33
  def each_header; end
31
34
 
32
35
  def [](key)
33
- return nil unless key.downcase == 'content-type'
36
+ return nil if key.casecmp('Content-Type') != 0
34
37
  return 'text/html' if directory?
35
38
  return 'text/html' if ['.html', '.xhtml'].any? { |extn|
36
- @file_path =~ /#{extn}$/
39
+ @file_path.end_with?(extn)
37
40
  }
38
41
  nil
39
42
  end
@@ -53,6 +56,10 @@ class Mechanize::FileResponse
53
56
  File.exist?(@file_path) ? 'OK' : 'Not Found'
54
57
  end
55
58
 
59
+ def uri
60
+ @uri ||= URI "file://#{@file_path}"
61
+ end
62
+
56
63
  private
57
64
 
58
65
  def dir_body
@@ -70,5 +77,6 @@ class Mechanize::FileResponse
70
77
  def directory?
71
78
  File.directory?(@file_path)
72
79
  end
80
+
73
81
  end
74
82
 
@@ -43,11 +43,9 @@ class Mechanize::HTTP::Agent
43
43
 
44
44
  # :section: HTTP Authentication
45
45
 
46
+ attr_reader :auth_store # :nodoc:
46
47
  attr_reader :authenticate_methods # :nodoc:
47
48
  attr_reader :digest_challenges # :nodoc:
48
- attr_accessor :user
49
- attr_accessor :password
50
- attr_accessor :domain
51
49
 
52
50
  # :section: Redirection
53
51
 
@@ -139,6 +137,7 @@ class Mechanize::HTTP::Agent
139
137
  @webrobots = nil
140
138
 
141
139
  # HTTP Authentication
140
+ @auth_store = Mechanize::HTTP::AuthStore.new
142
141
  @authenticate_parser = Mechanize::HTTP::WWWAuthenticateParser.new
143
142
  @authenticate_methods = Hash.new do |methods, uri|
144
143
  methods[uri] = Hash.new do |realms, auth_scheme|
@@ -147,9 +146,6 @@ class Mechanize::HTTP::Agent
147
146
  end
148
147
  @digest_auth = Net::HTTP::DigestAuth.new
149
148
  @digest_challenges = {}
150
- @password = nil # HTTP auth password
151
- @user = nil # HTTP auth user
152
- @domain = nil # NTLM HTTP domain
153
149
 
154
150
  # SSL
155
151
  @pass = nil
@@ -170,6 +166,33 @@ class Mechanize::HTTP::Agent
170
166
  @http.keep_alive = 300
171
167
  end
172
168
 
169
+ ##
170
+ # Adds credentials +user+, +pass+ for +uri+. If +realm+ is set the
171
+ # credentials are used only for that realm. If +realm+ is not set the
172
+ # credentials become the default for any realm on that URI.
173
+ #
174
+ # +domain+ and +realm+ are exclusive as NTLM does not follow RFC 2617. If
175
+ # +domain+ is given it is only used for NTLM authentication.
176
+
177
+ def add_auth uri, user, password, realm = nil, domain = nil
178
+ @auth_store.add_auth uri, user, password, realm, domain
179
+ end
180
+
181
+ ##
182
+ # USE OF add_default_auth IS NOT RECOMMENDED AS IT MAY EXPOSE PASSWORDS TO
183
+ # THIRD PARTIES
184
+ #
185
+ # Adds credentials +user+, +pass+ as the default authentication credentials.
186
+ # If no other credentials are available these will be returned from
187
+ # credentials_for.
188
+ #
189
+ # If +domain+ is given it is only used for NTLM authentication.
190
+
191
+ def add_default_auth user, password, domain = nil # :nodoc:
192
+ @auth_store.add_default_auth user, password, domain
193
+ end
194
+
195
+ ##
173
196
  # Retrieves +uri+ and parses it into a page or other object according to
174
197
  # PluggableParser. If the URI is an HTTP or HTTPS scheme URI the given HTTP
175
198
  # +method+ is used to retrieve it, along with the HTTP +headers+, request
@@ -265,7 +288,7 @@ class Mechanize::HTTP::Agent
265
288
  response_authenticate(response, page, uri, request, headers, params,
266
289
  referer)
267
290
  else
268
- raise Mechanize::ResponseCodeError.new(page), "Unhandled response"
291
+ raise Mechanize::ResponseCodeError.new(page, 'unhandled response')
269
292
  end
270
293
  end
271
294
 
@@ -470,16 +493,18 @@ class Mechanize::HTTP::Agent
470
493
  request_auth_digest request, uri, realm, base_uri, false
471
494
  elsif realm = schemes[:iis_digest].find { |r| r.uri == base_uri } then
472
495
  request_auth_digest request, uri, realm, base_uri, true
473
- elsif schemes[:basic].find { |r| r.uri == base_uri } then
474
- request.basic_auth @user, @password
496
+ elsif realm = schemes[:basic].find { |r| r.uri == base_uri } then
497
+ user, password, = @auth_store.credentials_for uri, realm.realm
498
+ request.basic_auth user, password
475
499
  end
476
500
  end
477
501
 
478
502
  def request_auth_digest request, uri, realm, base_uri, iis
479
503
  challenge = @digest_challenges[realm]
480
504
 
481
- uri.user = @user
482
- uri.password = @password
505
+ user, password, = @auth_store.credentials_for uri, realm.realm
506
+ uri.user = user
507
+ uri.password = password
483
508
 
484
509
  auth = @digest_auth.auth_header uri, challenge.to_s, request.method, iis
485
510
  request['Authorization'] = auth
@@ -523,8 +548,8 @@ class Mechanize::HTTP::Agent
523
548
  # major browsers do.
524
549
  def request_referer request, uri, referer
525
550
  return unless referer
526
- return if 'https' == referer.scheme.downcase and
527
- 'https' != uri.scheme.downcase
551
+ return if 'https'.casecmp(referer.scheme) == 0 and
552
+ 'https'.casecmp(uri.scheme) != 0
528
553
  if referer.fragment || referer.user || referer.password
529
554
  referer = referer.dup
530
555
  referer.fragment = referer.user = referer.password = nil
@@ -643,14 +668,20 @@ class Mechanize::HTTP::Agent
643
668
 
644
669
  def response_authenticate(response, page, uri, request, headers, params,
645
670
  referer)
646
- raise Mechanize::UnauthorizedError, page unless @user || @password
647
-
648
671
  www_authenticate = response['www-authenticate']
649
672
 
650
- raise Mechanize::UnauthorizedError, page unless www_authenticate
651
-
673
+ unless www_authenticate = response['www-authenticate'] then
674
+ message = 'WWW-Authenticate header missing in response'
675
+ raise Mechanize::UnauthorizedError.new(page, nil, message)
676
+ end
677
+
652
678
  challenges = @authenticate_parser.parse www_authenticate
653
679
 
680
+ unless @auth_store.credentials? uri, challenges then
681
+ message = "no credentials found, provide some with #add_auth"
682
+ raise Mechanize::UnauthorizedError.new(page, challenges, message)
683
+ end
684
+
654
685
  if challenge = challenges.find { |c| c.scheme =~ /^Digest$/i } then
655
686
  realm = challenge.realm uri
656
687
 
@@ -662,23 +693,30 @@ class Mechanize::HTTP::Agent
662
693
 
663
694
  existing_realms = @authenticate_methods[realm.uri][auth_scheme]
664
695
 
665
- raise Mechanize::UnauthorizedError, page if
666
- existing_realms.include? realm
696
+ if existing_realms.include? realm
697
+ message = 'Digest authentication failed'
698
+ raise Mechanize::UnauthorizedError.new(page, challeges, message)
699
+ end
667
700
 
668
701
  existing_realms << realm
669
702
  @digest_challenges[realm] = challenge
670
703
  elsif challenge = challenges.find { |c| c.scheme == 'NTLM' } then
671
704
  existing_realms = @authenticate_methods[uri + '/'][:ntlm]
672
705
 
673
- raise Mechanize::UnauthorizedError, page if
674
- existing_realms.include?(realm) and not challenge.params
706
+ if existing_realms.include?(realm) and not challenge.params then
707
+ message = 'NTLM authentication failed'
708
+ raise Mechanize::UnauthorizedError.new(page, challenges, message)
709
+ end
675
710
 
676
711
  existing_realms << realm
677
712
 
678
713
  if challenge.params then
679
714
  type_2 = Net::NTLM::Message.decode64 challenge.params
680
715
 
681
- type_3 = type_2.response({ :user => @user, :password => @password, :domain => @domain },
716
+ user, password, domain = @auth_store.credentials_for uri, nil
717
+
718
+ type_3 = type_2.response({ :user => user, :password => password,
719
+ :domain => domain },
682
720
  { :ntlmv2 => true }).encode64
683
721
 
684
722
  headers['Authorization'] = "NTLM #{type_3}"
@@ -691,12 +729,15 @@ class Mechanize::HTTP::Agent
691
729
 
692
730
  existing_realms = @authenticate_methods[realm.uri][:basic]
693
731
 
694
- raise Mechanize::UnauthorizedError, page if
695
- existing_realms.include? realm
732
+ if existing_realms.include? realm then
733
+ message = 'Basic authentication failed'
734
+ raise Mechanize::UnauthorizedError.new(page, challenges, message)
735
+ end
696
736
 
697
737
  existing_realms << realm
698
738
  else
699
- raise Mechanize::UnauthorizedError, page
739
+ message = 'unsupported authentication scheme'
740
+ raise Mechanize::UnauthorizedError.new(page, challenges, message)
700
741
  end
701
742
 
702
743
  fetch uri, request.method.downcase.to_sym, headers, params, referer
@@ -840,7 +881,7 @@ class Mechanize::HTTP::Agent
840
881
  body_io.flush
841
882
  body_io.rewind
842
883
 
843
- raise Mechanize::ResponseCodeError, response if
884
+ raise Mechanize::ResponseCodeError.new(response, uri) if
844
885
  Net::HTTPUnknownResponse === response
845
886
 
846
887
  content_length = response.content_length
@@ -1126,3 +1167,5 @@ class Mechanize::HTTP::Agent
1126
1167
 
1127
1168
  end
1128
1169
 
1170
+ require 'mechanize/http/auth_store'
1171
+
@@ -44,6 +44,13 @@ class Mechanize::HTTP
44
44
  end
45
45
  end
46
46
 
47
+ ##
48
+ # The name of the realm for this challenge
49
+
50
+ def realm_name
51
+ params['realm'] if Hash === params # NTLM has a string for params
52
+ end
53
+
47
54
  ##
48
55
  # The reconstructed, normalized challenge
49
56
 
@@ -0,0 +1,115 @@
1
+ ##
2
+ # A credential store for HTTP authentication.
3
+ #
4
+ # uri = URI 'http://example'
5
+ #
6
+ # store = Mechanize::HTTP::AuthStore.new
7
+ # store.add_auth uri, 'user1', 'pass'
8
+ # store.add_auth uri, 'user2', 'pass', 'realm'
9
+ #
10
+ # user, pass = store.credentials_for uri, 'realm' #=> 'user2', 'pass'
11
+ # user, pass = store.credentials_for uri, 'other' #=> 'user1', 'pass'
12
+ #
13
+ # store.remove_auth uri # removes all credentials
14
+
15
+ class Mechanize::HTTP::AuthStore
16
+
17
+ attr_reader :auth_accounts # :nodoc:
18
+
19
+ attr_reader :default_auth # :nodoc:
20
+
21
+ ##
22
+ # Creates a new AuthStore
23
+
24
+ def initialize
25
+ @auth_accounts = Hash.new do |h, uri|
26
+ h[uri] = {}
27
+ end
28
+
29
+ @default_auth = nil
30
+ end
31
+
32
+ ##
33
+ # Adds credentials +user+, +pass+ for the server at +uri+. If +realm+ is
34
+ # set the credentials are used only for that realm. If +realm+ is not set
35
+ # the credentials become the default for any realm on that URI.
36
+ #
37
+ # +domain+ and +realm+ are exclusive as NTLM does not follow RFC
38
+ # 2617. If +domain+ is given it is only used for NTLM authentication.
39
+
40
+ def add_auth uri, user, pass, realm = nil, domain = nil
41
+ raise ArgumentError,
42
+ 'NTLM domain given with realm which NTLM does not use' if
43
+ realm and domain
44
+
45
+ uri += '/'
46
+
47
+ auth_accounts[uri][realm] = [user, pass, domain]
48
+
49
+ self
50
+ end
51
+
52
+ ##
53
+ # USE OF add_default_auth IS NOT RECOMMENDED AS IT MAY EXPOSE PASSWORDS TO
54
+ # THIRD PARTIES
55
+ #
56
+ # Adds credentials +user+, +pass+ as the default authentication credentials.
57
+ # If no other credentials are available these will be returned from
58
+ # credentials_for.
59
+ #
60
+ # If +domain+ is given it is only used for NTLM authentication.
61
+
62
+ def add_default_auth user, pass, domain = nil
63
+ warn <<-WARN
64
+ You have supplied default authentication credentials that apply to ANY SERVER.
65
+ Your username and password can be retrieved by ANY SERVER using Basic
66
+ authentication.
67
+
68
+ THIS EXPOSES YOUR USERNAME AND PASSWORD TO DISCLOSURE WITHOUT YOUR KNOWLEDGE.
69
+
70
+ Use add_auth to set authentication credentials that will only be delivered
71
+ only to a particular server you specify.
72
+ WARN
73
+
74
+ @default_auth = [user, pass, domain]
75
+ end
76
+
77
+ ##
78
+ # Returns true if credentials exist for the +challenges+ from the server at
79
+ # +uri+.
80
+
81
+ def credentials? uri, challenges
82
+ challenges.any? do |challenge|
83
+ credentials_for uri, challenge.realm_name
84
+ end
85
+ end
86
+
87
+ ##
88
+ # Retrieves credentials for +realm+ on the server at +uri+.
89
+
90
+ def credentials_for uri, realm
91
+ uri += '/'
92
+
93
+ realms = @auth_accounts[uri]
94
+
95
+ realms[realm] || realms[nil] || @default_auth
96
+ end
97
+
98
+ ##
99
+ # Removes credentials for +realm+ on the server at +uri+. If +realm+ is not
100
+ # set all credentials for the server at +uri+ are removed.
101
+
102
+ def remove_auth uri, realm = nil
103
+ uri += '/'
104
+
105
+ if realm then
106
+ auth_accounts[uri].delete realm
107
+ else
108
+ auth_accounts.delete uri
109
+ end
110
+
111
+ self
112
+ end
113
+
114
+ end
115
+
@@ -45,6 +45,8 @@ class Mechanize::HTTP::WWWAuthenticateParser
45
45
 
46
46
  challenges << challenge
47
47
  next
48
+ else
49
+ scheme.capitalize!
48
50
  end
49
51
 
50
52
  next unless space
@@ -55,6 +57,8 @@ class Mechanize::HTTP::WWWAuthenticateParser
55
57
  pos = @scanner.pos
56
58
  name, value = auth_param
57
59
 
60
+ name.downcase! if name =~ /^realm$/i
61
+
58
62
  unless name then
59
63
  challenge.params = params
60
64
  challenges << challenge
@@ -84,7 +84,7 @@ class Mechanize::Page < Mechanize::File
84
84
 
85
85
  if @parser
86
86
  parser_encoding = @parser.encoding
87
- if (parser_encoding && parser_encoding.downcase) != (encoding && encoding.downcase)
87
+ if parser_encoding && encoding && parser_encoding.casecmp(encoding) != 0
88
88
  # lazy reinitialize the parser with the new encoding
89
89
  @parser = nil
90
90
  end
@@ -7,13 +7,18 @@ class Mechanize::ResponseCodeError < Mechanize::Error
7
7
  attr_reader :response_code
8
8
  attr_reader :page
9
9
 
10
- def initialize(page)
10
+ def initialize(page, message = nil)
11
+ super message
12
+
11
13
  @page = page
12
14
  @response_code = page.code.to_s
13
15
  end
14
16
 
15
17
  def to_s
16
- "#{@response_code} => #{Net::HTTPResponse::CODE_TO_OBJ[@response_code]}"
18
+ response_class = Net::HTTPResponse::CODE_TO_OBJ[@response_code]
19
+ out = "#{@response_code} => #{response_class} "
20
+ out << "for #{@page.uri} " if @page.respond_to? :uri # may be HTTPResponse
21
+ out << "-- #{super}"
17
22
  end
18
23
 
19
24
  alias inspect to_s
@@ -120,21 +120,24 @@ end
120
120
 
121
121
  class BasicAuthServlet < WEBrick::HTTPServlet::AbstractServlet
122
122
  def do_GET(req,res)
123
- htpd = WEBrick::HTTPAuth::Htpasswd.new('dot.htpasswd')
124
- htpd.set_passwd('Blah', 'user', 'pass')
123
+ htpd = nil
124
+ Tempfile.open 'dot.htpasswd' do |io|
125
+ htpd = WEBrick::HTTPAuth::Htpasswd.new(io.path)
126
+ htpd.set_passwd('Blah', 'user', 'pass')
127
+ end
128
+
125
129
  authenticator = WEBrick::HTTPAuth::BasicAuth.new({
126
130
  :UserDB => htpd,
127
131
  :Realm => 'Blah',
128
132
  :Logger => Logger.new(nil)
129
- }
130
- )
131
- begin
132
- authenticator.authenticate(req,res)
133
- res.body = 'You are authenticated'
134
- rescue WEBrick::HTTPStatus::Unauthorized
135
- res.status = 401
136
- end
137
- FileUtils.rm('dot.htpasswd')
133
+ })
134
+
135
+ begin
136
+ authenticator.authenticate(req,res)
137
+ res.body = 'You are authenticated'
138
+ rescue WEBrick::HTTPStatus::Unauthorized
139
+ res.status = 401
140
+ end
138
141
  end
139
142
  alias :do_POST :do_GET
140
143
  end
@@ -148,29 +151,34 @@ class ContentTypeServlet < WEBrick::HTTPServlet::AbstractServlet
148
151
  end
149
152
 
150
153
  class DigestAuthServlet < WEBrick::HTTPServlet::AbstractServlet
151
- htpd = WEBrick::HTTPAuth::Htdigest.new('digest.htpasswd')
152
- htpd.set_passwd('Blah', 'user', 'pass')
154
+ htpd = nil
155
+
156
+ Tempfile.open 'digest.htpasswd' do |io|
157
+ htpd = WEBrick::HTTPAuth::Htdigest.new(io.path)
158
+ htpd.set_passwd('Blah', 'user', 'pass')
159
+ end
160
+
153
161
  @@authenticator = WEBrick::HTTPAuth::DigestAuth.new({
154
162
  :UserDB => htpd,
155
163
  :Realm => 'Blah',
156
164
  :Algorithm => 'MD5',
157
165
  :Logger => Logger.new(nil)
158
- }
159
- )
160
- def do_GET(req,res)
161
- def req.request_time; Time.now; end
162
- def req.request_uri; '/digest_auth'; end
163
- def req.request_method; "GET"; end
164
-
165
- begin
166
- @@authenticator.authenticate(req,res)
167
- res.body = 'You are authenticated'
168
- rescue WEBrick::HTTPStatus::Unauthorized
169
- res.status = 401
170
- end
171
- FileUtils.rm('digest.htpasswd') if File.exists?('digest.htpasswd')
172
- end
173
- alias :do_POST :do_GET
166
+ })
167
+
168
+ def do_GET(req,res)
169
+ def req.request_time; Time.now; end
170
+ def req.request_uri; '/digest_auth'; end
171
+ def req.request_method; "GET"; end
172
+
173
+ begin
174
+ @@authenticator.authenticate(req,res)
175
+ res.body = 'You are authenticated'
176
+ rescue WEBrick::HTTPStatus::Unauthorized
177
+ res.status = 401
178
+ end
179
+ FileUtils.rm('digest.htpasswd') if File.exists?('digest.htpasswd')
180
+ end
181
+ alias :do_POST :do_GET
174
182
  end
175
183
 
176
184
  class FileUploadServlet < WEBrick::HTTPServlet::AbstractServlet
@@ -1,3 +1,22 @@
1
1
  class Mechanize::UnauthorizedError < Mechanize::ResponseCodeError
2
+
3
+ attr_reader :challenges
4
+
5
+ def initialize page, challenges, message
6
+ super page, message
7
+ @challenges = challenges
8
+ end
9
+
10
+ def to_s
11
+ out = super
12
+
13
+ if @challenges then
14
+ realms = @challenges.map { |challenge| challenge.realm_name }.join ', '
15
+ out << " -- available realms: #{realms}"
16
+ end
17
+
18
+ out
19
+ end
20
+
2
21
  end
3
22
 
@@ -41,9 +41,17 @@ class TestMechanize < Mechanize::TestCase
41
41
  end
42
42
 
43
43
  def test_basic_auth
44
- @mech.basic_auth('user', 'pass')
45
- page = @mech.get("http://localhost/basic_auth")
46
- assert_equal('You are authenticated', page.body)
44
+ _, err = capture_io do
45
+ @mech.basic_auth 'user', 'pass' # warns
46
+ end
47
+
48
+ line = __LINE__ - 3
49
+ file = File.basename __FILE__
50
+
51
+ assert_match "#{file} line #{line}", err
52
+
53
+ page = @mech.get @uri + '/basic_auth'
54
+ assert_equal 'You are authenticated', page.body
47
55
  end
48
56
 
49
57
  def test_cert_key_file
@@ -326,7 +334,11 @@ but not <a href="/" rel="me nofollow">this</a>!
326
334
 
327
335
  def test_get_HTTP
328
336
  page = @mech.get('HTTP://localhost/', { :q => 'hello' })
329
- assert_equal('HTTP://localhost/?q=hello', page.uri.to_s)
337
+
338
+ assert_kind_of URI::HTTP, page.uri
339
+ assert_equal 'localhost', page.uri.host
340
+ assert_equal 80, page.uri.port
341
+ assert_equal '/?q=hello', page.uri.request_uri
330
342
  end
331
343
 
332
344
  def test_get_anchor
@@ -340,19 +352,19 @@ but not <a href="/" rel="me nofollow">this</a>!
340
352
  end
341
353
  end
342
354
 
343
- def test_get_basic_auth_bad
344
- @mech.basic_auth('aaron', 'aaron')
355
+ def test_get_auth_bad
356
+ @mech.add_auth(@uri, 'aaron', 'aaron')
345
357
 
346
358
  e = assert_raises Mechanize::UnauthorizedError do
347
- @mech.get("http://localhost/basic_auth")
359
+ @mech.get(@uri + '/basic_auth')
348
360
  end
349
361
 
350
362
  assert_equal("401", e.response_code)
351
363
  end
352
364
 
353
- def test_get_basic_auth_none
365
+ def test_get_auth_none
354
366
  e = assert_raises Mechanize::UnauthorizedError do
355
- @mech.get("http://localhost/basic_auth")
367
+ @mech.get(@uri + '/basic_auth')
356
368
  end
357
369
 
358
370
  assert_equal("401", e.response_code)
@@ -374,7 +386,7 @@ but not <a href="/" rel="me nofollow">this</a>!
374
386
  def test_get_digest_auth
375
387
  block_called = false
376
388
 
377
- @mech.basic_auth('user', 'pass')
389
+ @mech.add_auth(@uri, 'user', 'pass')
378
390
 
379
391
  @mech.pre_connect_hooks << lambda { |_, request|
380
392
  block_called = true
@@ -383,7 +395,7 @@ but not <a href="/" rel="me nofollow">this</a>!
383
395
  end
384
396
  }
385
397
 
386
- page = @mech.get("http://localhost/digest_auth")
398
+ page = @mech.get(@uri + '/digest_auth')
387
399
  assert_equal('You are authenticated', page.body)
388
400
  assert block_called
389
401
  end
@@ -869,15 +881,15 @@ but not <a href="/" rel="me nofollow">this</a>!
869
881
  assert_equal "gender=female", requests.first.body
870
882
  end
871
883
 
872
- def test_post_basic_auth
884
+ def test_post_auth
873
885
  requests = []
874
886
 
875
887
  @mech.pre_connect_hooks << proc { |agent, request|
876
888
  requests << request.class
877
889
  }
878
890
 
879
- @mech.basic_auth('user', 'pass')
880
- page = @mech.post("http://localhost/basic_auth")
891
+ @mech.add_auth(@uri, 'user', 'pass')
892
+ page = @mech.post(@uri + '/basic_auth')
881
893
  assert_equal('You are authenticated', page.body)
882
894
  assert_equal(2, requests.length)
883
895
  r1 = requests[0]
@@ -164,7 +164,7 @@ class TestMechanizeHttpAgent < Mechanize::TestCase
164
164
  @agent.fetch uri
165
165
  end
166
166
 
167
- assert_equal '404 => Net::HTTPNotFound', e.message
167
+ assert_match "404 => Net::HTTPNotFound for #{uri}", e.message
168
168
  end
169
169
  end
170
170
 
@@ -436,8 +436,7 @@ class TestMechanizeHttpAgent < Mechanize::TestCase
436
436
  end
437
437
 
438
438
  def test_request_auth_basic
439
- @agent.user = 'user'
440
- @agent.password = 'password'
439
+ @agent.add_auth @uri, 'user', 'password'
441
440
 
442
441
  auth_realm @uri, 'Basic', :basic
443
442
 
@@ -447,8 +446,7 @@ class TestMechanizeHttpAgent < Mechanize::TestCase
447
446
  end
448
447
 
449
448
  def test_request_auth_digest
450
- @agent.user = 'user'
451
- @agent.password = 'password'
449
+ @agent.add_auth @uri, 'user', 'password'
452
450
 
453
451
  realm = auth_realm @uri, 'Digest', :digest
454
452
  @agent.digest_challenges[realm] = 'Digest realm=r, qop="auth"'
@@ -460,8 +458,7 @@ class TestMechanizeHttpAgent < Mechanize::TestCase
460
458
  end
461
459
 
462
460
  def test_request_auth_iis_digest
463
- @agent.user = 'user'
464
- @agent.password = 'password'
461
+ @agent.add_auth @uri, 'user', 'password'
465
462
 
466
463
  realm = auth_realm @uri, 'Digest', :digest
467
464
  @agent.digest_challenges[realm] = 'Digest realm=r, qop="auth"'
@@ -672,9 +669,9 @@ class TestMechanizeHttpAgent < Mechanize::TestCase
672
669
  end
673
670
 
674
671
  def test_response_authenticate
672
+ @agent.add_auth @uri, 'user', 'password'
673
+
675
674
  @res.instance_variable_set :@header, 'www-authenticate' => ['Basic realm=r']
676
- @agent.user = 'user'
677
- @agent.password = 'password'
678
675
 
679
676
  @agent.response_authenticate @res, nil, @uri, @req, {}, nil, nil
680
677
 
@@ -684,10 +681,10 @@ class TestMechanizeHttpAgent < Mechanize::TestCase
684
681
  end
685
682
 
686
683
  def test_response_authenticate_digest
684
+ @agent.add_auth @uri, 'user', 'password'
685
+
687
686
  @res.instance_variable_set(:@header,
688
687
  'www-authenticate' => ['Digest realm=r'])
689
- @agent.user = 'user'
690
- @agent.password = 'password'
691
688
 
692
689
  @agent.response_authenticate @res, nil, @uri, @req, {}, nil, nil
693
690
 
@@ -700,12 +697,11 @@ class TestMechanizeHttpAgent < Mechanize::TestCase
700
697
  end
701
698
 
702
699
  def test_response_authenticate_digest_iis
700
+ @agent.add_auth @uri, 'user', 'password'
701
+
703
702
  @res.instance_variable_set(:@header,
704
703
  'www-authenticate' => ['Digest realm=r'],
705
704
  'server' => ['Microsoft-IIS'])
706
- @agent.user = 'user'
707
- @agent.password = 'password'
708
-
709
705
  @agent.response_authenticate @res, nil, @uri, @req, {}, nil, nil
710
706
 
711
707
  base_uri = @uri + '/'
@@ -714,11 +710,11 @@ class TestMechanizeHttpAgent < Mechanize::TestCase
714
710
  end
715
711
 
716
712
  def test_response_authenticate_multiple
713
+ @agent.add_auth @uri, 'user', 'password'
714
+
717
715
  @res.instance_variable_set(:@header,
718
716
  'www-authenticate' =>
719
717
  ['Basic realm=r, Digest realm=r'])
720
- @agent.user = 'user'
721
- @agent.password = 'password'
722
718
 
723
719
  @agent.response_authenticate @res, nil, @uri, @req, {}, nil, nil
724
720
 
@@ -729,25 +725,39 @@ class TestMechanizeHttpAgent < Mechanize::TestCase
729
725
  assert_empty @agent.authenticate_methods[base_uri][:basic]
730
726
  end
731
727
 
728
+ def test_response_authenticate_no_credentials
729
+ @res.instance_variable_set :@header, 'www-authenticate' => ['Basic realm=r']
730
+
731
+ e = assert_raises Mechanize::UnauthorizedError do
732
+ @agent.response_authenticate @res, fake_page, @uri, @req, {}, nil, nil
733
+ end
734
+
735
+ assert_match 'no credentials', e.message
736
+ assert_match 'available realms: r', e.message
737
+ end
738
+
732
739
  def test_response_authenticate_no_www_authenticate
733
- denied = page URI('http://example/denied'), 'text/html', '', 403
734
- @agent.user = 'user'
735
- @agent.password = 'password'
740
+ @agent.add_auth @uri, 'user', 'password'
741
+
742
+ denied_uri = URI('http://example/denied')
743
+
744
+ denied = page denied_uri, 'text/html', '', 401
736
745
 
737
746
  e = assert_raises Mechanize::UnauthorizedError do
738
747
  @agent.response_authenticate @res, denied, @uri, @req, {}, nil, nil
739
748
  end
740
749
 
741
- assert_equal '403 => Net::HTTPForbidden', e.message
750
+ assert_equal "401 => Net::HTTPUnauthorized for #{denied_uri} -- " \
751
+ "WWW-Authenticate header missing in response",
752
+ e.message
742
753
  end
743
754
 
744
755
  def test_response_authenticate_ntlm
745
756
  @uri += '/ntlm'
757
+ @agent.add_auth @uri, 'user', 'password'
758
+
746
759
  @res.instance_variable_set(:@header,
747
760
  'www-authenticate' => ['Negotiate, NTLM'])
748
- @agent.user = 'user'
749
- @agent.password = 'password'
750
- @agent.domain = 'domain'
751
761
 
752
762
  page = @agent.response_authenticate @res, nil, @uri, @req, {}, nil, nil
753
763
 
@@ -755,8 +765,8 @@ class TestMechanizeHttpAgent < Mechanize::TestCase
755
765
  end
756
766
 
757
767
  def test_response_authenticate_unknown
758
- @agent.user = 'user'
759
- @agent.password = 'password'
768
+ @agent.add_auth @uri, 'user', 'password'
769
+
760
770
  page = Mechanize::File.new nil, nil, nil, 401
761
771
  @res.instance_variable_set(:@header,
762
772
  'www-authenticate' => ['Unknown realm=r'])
@@ -1229,6 +1239,21 @@ class TestMechanizeHttpAgent < Mechanize::TestCase
1229
1239
  end
1230
1240
  end
1231
1241
 
1242
+ def test_file_response_content_type
1243
+ Tempfile.open ['pi','.nothtml'] do |tempfile|
1244
+ res = Mechanize::FileResponse.new tempfile.path
1245
+ assert_equal nil, res['content-type']
1246
+ end
1247
+ Tempfile.open ['pi','.xhtml'] do |tempfile|
1248
+ res = Mechanize::FileResponse.new tempfile.path
1249
+ assert_equal 'text/html', res['content-type']
1250
+ end
1251
+ Tempfile.open ['pi','.html'] do |tempfile|
1252
+ res = Mechanize::FileResponse.new tempfile.path
1253
+ assert_equal 'text/html', res['Content-Type']
1254
+ end
1255
+ end
1256
+
1232
1257
  def test_response_read_large
1233
1258
  @agent.max_file_buffer = 10240
1234
1259
 
@@ -35,5 +35,15 @@ class TestMechanizeHttpAuthChallenge < Mechanize::TestCase
35
35
  assert_equal 'unknown HTTP authentication scheme Unknown', e.message
36
36
  end
37
37
 
38
+ def test_realm_name
39
+ assert_equal 'r', @challenge.realm_name
40
+ end
41
+
42
+ def test_realm_name_ntlm
43
+ challenge = @AC.new 'Negotiate, NTLM'
44
+
45
+ assert_nil challenge.realm_name
46
+ end
47
+
38
48
  end
39
49
 
@@ -0,0 +1,172 @@
1
+ # coding: utf-8
2
+
3
+ require 'mechanize/test_case'
4
+
5
+ class TestMechanizeHttpAuthStore < Mechanize::TestCase
6
+
7
+ def setup
8
+ super
9
+
10
+ @store = Mechanize::HTTP::AuthStore.new
11
+
12
+ @uri = URI.parse 'http://example/'
13
+ end
14
+
15
+ def test_add_auth
16
+ @store.add_auth @uri + '/path', 'user', 'pass'
17
+
18
+ expected = {
19
+ @uri => {
20
+ nil => ['user', 'pass', nil],
21
+ }
22
+ }
23
+
24
+ assert_equal expected, @store.auth_accounts
25
+ end
26
+
27
+ def test_add_auth_domain
28
+ @store.add_auth @uri + '/path', 'user1', 'pass', nil, 'domain'
29
+
30
+ expected = {
31
+ @uri => {
32
+ nil => %w[user1 pass domain],
33
+ }
34
+ }
35
+
36
+ assert_equal expected, @store.auth_accounts
37
+
38
+ e = assert_raises ArgumentError do
39
+ @store.add_auth @uri, 'user3', 'pass', 'realm', 'domain'
40
+ end
41
+
42
+ assert_equal 'NTLM domain given with realm which NTLM does not use',
43
+ e.message
44
+ end
45
+
46
+ def test_add_auth_realm
47
+ @store.add_auth @uri, 'user1', 'pass'
48
+ @store.add_auth @uri, 'user2', 'pass', 'realm'
49
+
50
+ expected = {
51
+ @uri => {
52
+ nil => ['user1', 'pass', nil],
53
+ 'realm' => ['user2', 'pass', nil],
54
+ }
55
+ }
56
+
57
+ assert_equal expected, @store.auth_accounts
58
+ end
59
+
60
+ def test_add_default_auth
61
+ _, err = capture_io do
62
+ @store.add_default_auth 'user', 'pass'
63
+ end
64
+
65
+ expected = ['user', 'pass', nil]
66
+
67
+ assert_equal expected, @store.default_auth
68
+
69
+ assert_match 'DISCLOSURE WITHOUT YOUR KNOWLEDGE', err
70
+
71
+ capture_io do
72
+ @store.add_default_auth 'user', 'pass', 'realm'
73
+ end
74
+
75
+ expected = %w[user pass realm]
76
+
77
+ assert_equal expected, @store.default_auth
78
+ end
79
+
80
+ def test_credentials_eh
81
+ challenges = [
82
+ Mechanize::HTTP::AuthChallenge.new('Basic', 'realm' => 'r'),
83
+ Mechanize::HTTP::AuthChallenge.new('Digest', 'realm' => 'r'),
84
+ ]
85
+
86
+ refute @store.credentials? @uri, challenges
87
+
88
+ @store.add_auth @uri, 'user', 'pass'
89
+
90
+ assert @store.credentials? @uri, challenges
91
+ end
92
+
93
+ def test_credentials_for
94
+ assert_nil @store.credentials_for(@uri, 'realm')
95
+
96
+ @store.add_auth @uri, 'user', 'pass', 'realm'
97
+
98
+ assert_equal ['user', 'pass', nil], @store.credentials_for(@uri, 'realm')
99
+ assert_nil @store.credentials_for(@uri, 'other')
100
+ end
101
+
102
+ def test_credentials_for_default
103
+ assert_nil @store.credentials_for(@uri, 'realm')
104
+
105
+ capture_io do
106
+ @store.add_default_auth 'user1', 'pass'
107
+ end
108
+
109
+ assert_equal ['user1', 'pass', nil], @store.credentials_for(@uri, 'realm')
110
+
111
+ @store.add_auth @uri, 'user2', 'pass'
112
+
113
+ assert_equal ['user2', 'pass', nil], @store.credentials_for(@uri, 'realm')
114
+ assert_equal ['user2', 'pass', nil], @store.credentials_for(@uri, 'other')
115
+ end
116
+
117
+ def test_credentials_for_no_realm
118
+ @store.add_auth @uri, 'user', 'pass' # no realm set
119
+
120
+ assert_equal ['user', 'pass', nil], @store.credentials_for(@uri, 'realm')
121
+ end
122
+
123
+ def test_credentials_for_realm
124
+ @store.add_auth @uri, 'user1', 'pass'
125
+ @store.add_auth @uri, 'user2', 'pass', 'realm'
126
+
127
+ assert_equal ['user2', 'pass', nil], @store.credentials_for(@uri, 'realm')
128
+ assert_equal ['user1', 'pass', nil], @store.credentials_for(@uri, 'other')
129
+ end
130
+
131
+ def test_credentials_for_path
132
+ @store.add_auth @uri, 'user', 'pass', 'realm'
133
+
134
+ uri = @uri + '/path'
135
+
136
+ assert_equal ['user', 'pass', nil], @store.credentials_for(uri, 'realm')
137
+ end
138
+
139
+ def test_remove_auth
140
+ @store.remove_auth @uri
141
+
142
+ assert_empty @store.auth_accounts
143
+ end
144
+
145
+ def test_remove_auth_both
146
+ @store.add_auth @uri, 'user1', 'pass'
147
+ @store.add_auth @uri, 'user2', 'pass', 'realm'
148
+
149
+ uri = @uri + '/path'
150
+
151
+ @store.remove_auth uri
152
+
153
+ assert_empty @store.auth_accounts
154
+ end
155
+
156
+ def test_remove_auth_realm
157
+ @store.add_auth @uri, 'user1', 'pass'
158
+ @store.add_auth @uri, 'user2', 'pass', 'realm'
159
+
160
+ @store.remove_auth @uri, 'realm'
161
+
162
+ expected = {
163
+ @uri => {
164
+ nil => ['user1', 'pass', nil]
165
+ }
166
+ }
167
+
168
+ assert_equal expected, @store.auth_accounts
169
+ end
170
+
171
+ end
172
+
@@ -52,20 +52,20 @@ class TestMechanizeHttpWwwAuthenticateParser < Mechanize::TestCase
52
52
 
53
53
  def test_parse_multiple
54
54
  expected = [
55
- challenge('Basic', { 'realm' => 'foo' }),
56
- challenge('Digest', { 'realm' => 'foo' }),
55
+ challenge('Basic', { 'realm' => 'foo' }),
56
+ challenge('Digest', { 'realm' => 'bar' }),
57
57
  ]
58
58
 
59
- assert_equal expected, @parser.parse('Basic realm=foo, Digest realm=foo')
59
+ assert_equal expected, @parser.parse('Basic realm=foo, Digest realm=bar')
60
60
  end
61
61
 
62
62
  def test_parse_multiple_blank
63
63
  expected = [
64
- challenge('Basic', { 'realm' => 'foo' }),
65
- challenge('Digest', { 'realm' => 'foo' }),
64
+ challenge('Basic', { 'realm' => 'foo' }),
65
+ challenge('Digest', { 'realm' => 'bar' }),
66
66
  ]
67
67
 
68
- assert_equal expected, @parser.parse('Basic realm=foo,, Digest realm=foo')
68
+ assert_equal expected, @parser.parse('Basic realm=foo,, Digest realm=bar')
69
69
  end
70
70
 
71
71
  def test_parse_ntlm_init
@@ -84,6 +84,22 @@ class TestMechanizeHttpWwwAuthenticateParser < Mechanize::TestCase
84
84
  assert_equal expected, @parser.parse('NTLM foo=')
85
85
  end
86
86
 
87
+ def test_parse_realm_uppercase
88
+ expected = [
89
+ challenge('Basic', { 'realm' => 'foo' }),
90
+ ]
91
+
92
+ assert_equal expected, @parser.parse('Basic ReAlM=foo')
93
+ end
94
+
95
+ def test_parse_scheme_uppercase
96
+ expected = [
97
+ challenge('Basic', { 'realm' => 'foo' }),
98
+ ]
99
+
100
+ assert_equal expected, @parser.parse('BaSiC realm=foo')
101
+ end
102
+
87
103
  def test_quoted_string
88
104
  @parser.scanner = StringScanner.new '"text"'
89
105
 
metadata CHANGED
@@ -1,12 +1,12 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: mechanize
3
3
  version: !ruby/object:Gem::Version
4
- hash: 5
4
+ hash: 11
5
5
  prerelease:
6
6
  segments:
7
7
  - 2
8
- - 3
9
- version: "2.3"
8
+ - 4
9
+ version: "2.4"
10
10
  platform: ruby
11
11
  authors:
12
12
  - Eric Hodel
@@ -18,9 +18,9 @@ bindir: bin
18
18
  cert_chain:
19
19
  - |
20
20
  -----BEGIN CERTIFICATE-----
21
- MIIDVzCCAj+gAwIBAgIBATANBgkqhkiG9w0BAQUFADBBMRAwDgYDVQQDDAdkcmJy
21
+ MIIDeDCCAmCgAwIBAgIBATANBgkqhkiG9w0BAQUFADBBMRAwDgYDVQQDDAdkcmJy
22
22
  YWluMRgwFgYKCZImiZPyLGQBGRYIc2VnbWVudDcxEzARBgoJkiaJk/IsZAEZFgNu
23
- ZXQwHhcNMTIwMTMxMDEwMzUyWhcNMTMwMTMwMDEwMzUyWjBBMRAwDgYDVQQDDAdk
23
+ ZXQwHhcNMTIwMjI4MTc1NDI1WhcNMTMwMjI3MTc1NDI1WjBBMRAwDgYDVQQDDAdk
24
24
  cmJyYWluMRgwFgYKCZImiZPyLGQBGRYIc2VnbWVudDcxEzARBgoJkiaJk/IsZAEZ
25
25
  FgNuZXQwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCbbgLrGLGIDE76
26
26
  LV/cvxdEzCuYuS3oG9PrSZnuDweySUfdp/so0cDq+j8bqy6OzZSw07gdjwFMSd6J
@@ -28,17 +28,18 @@ cert_chain:
28
28
  Gj/okWrQl0NjYOYBpDi+9PPmaH2RmLJu0dB/NylsDnW5j6yN1BEI8MfJRR+HRKZY
29
29
  mUtgzBwF1V4KIZQ8EuL6I/nHVu07i6IkrpAgxpXUfdJQJi0oZAqXurAV3yTxkFwd
30
30
  g62YrrW26mDe+pZBzR6bpLE+PmXCzz7UxUq3AE0gPHbiMXie3EFE0oxnsU3lIduh
31
- sCANiQ8BAgMBAAGjWjBYMAkGA1UdEwQCMAAwCwYDVR0PBAQDAgSwMB0GA1UdDgQW
31
+ sCANiQ8BAgMBAAGjezB5MAkGA1UdEwQCMAAwCwYDVR0PBAQDAgSwMB0GA1UdDgQW
32
32
  BBS5k4Z75VSpdM0AclG2UvzFA/VW5DAfBgNVHREEGDAWgRRkcmJyYWluQHNlZ21l
33
- bnQ3Lm5ldDANBgkqhkiG9w0BAQUFAAOCAQEAge3LmAU2QbrS2/grAEmRu3bCCHrQ
34
- NSc6j+p53VJ1DraNWEMY3D90F/SKzsI0SYgZb71i49k+pNA2CVXzEJAY7agZbjWJ
35
- UbgGKN8u9SGbIoQPBPIl97JPIGlR7AoEdmlWyFySaZD4o6+Q0onUXIV+P/KrYTVb
36
- Zj/NEjHGrvskpDzlYI2LvG71DFp1o0hfIZzdvfWLAMVqtuEjJ6QrUm9FttR06rNo
37
- itgEKl/tNI4M9oKJT0faQ5PvJ70ualcLnwkBLyJVd2r8qwxfjUAjKF8iMpBSb98s
38
- YJY7T/W2n+eWy8WuPhzVUkyzguj0bQe27NDeabgCh2mHd4Hynk2AkYh8MQ==
33
+ bnQ3Lm5ldDAfBgNVHRIEGDAWgRRkcmJyYWluQHNlZ21lbnQ3Lm5ldDANBgkqhkiG
34
+ 9w0BAQUFAAOCAQEAPeWzFnrcvC6eVzdlhmjUub2s6qieBkongKRDHQz5MEeQv4LS
35
+ SARnoHY+uCAVL/1xGAhmpzqQ3fJGWK9eBacW/e8E5GF9xQcV3mE1bA0WNaiDlX5j
36
+ U2aI+ZGSblqvHUCxKBHR1s7UMHsbz1saOmgdRTyPx0juJs68ocbUTeYBLWu9V4KP
37
+ zdGAG2JXO2gONg3b4tYDvpBLbry+KOX27iAJulUaH9TiTOULL4ITJVFsK0mYVqmR
38
+ Q8Tno9S3e4XGGP1ZWfLrTWEJbavFfhGHut2iMRwfC7s/YILAHNATopaJdH9DNpd1
39
+ U81zGHMUBOvz/VGT6wJwYJ3emS2nfA2NOHFfgA==
39
40
  -----END CERTIFICATE-----
40
41
 
41
- date: 2012-02-21 00:00:00 Z
42
+ date: 2012-04-21 00:00:00 Z
42
43
  dependencies:
43
44
  - !ruby/object:Gem::Dependency
44
45
  name: net-http-digest_auth
@@ -201,11 +202,11 @@ dependencies:
201
202
  requirements:
202
203
  - - ~>
203
204
  - !ruby/object:Gem::Version
204
- hash: 23
205
+ hash: 21
205
206
  segments:
206
207
  - 2
207
- - 10
208
- version: "2.10"
208
+ - 11
209
+ version: "2.11"
209
210
  type: :development
210
211
  version_requirements: *id008
211
212
  - !ruby/object:Gem::Dependency
@@ -231,11 +232,11 @@ dependencies:
231
232
  requirements:
232
233
  - - ~>
233
234
  - !ruby/object:Gem::Version
234
- hash: 25
235
+ hash: 35
235
236
  segments:
236
237
  - 2
237
- - 13
238
- version: "2.13"
238
+ - 16
239
+ version: "2.16"
239
240
  type: :development
240
241
  version_requirements: *id010
241
242
  description: |-
@@ -254,11 +255,11 @@ executables: []
254
255
  extensions: []
255
256
 
256
257
  extra_rdoc_files:
257
- - Manifest.txt
258
258
  - CHANGELOG.rdoc
259
259
  - EXAMPLES.rdoc
260
260
  - GUIDE.rdoc
261
261
  - LICENSE.rdoc
262
+ - Manifest.txt
262
263
  - README.rdoc
263
264
  files:
264
265
  - .autotest
@@ -309,6 +310,7 @@ files:
309
310
  - lib/mechanize/http/agent.rb
310
311
  - lib/mechanize/http/auth_challenge.rb
311
312
  - lib/mechanize/http/auth_realm.rb
313
+ - lib/mechanize/http/auth_store.rb
312
314
  - lib/mechanize/http/content_disposition_parser.rb
313
315
  - lib/mechanize/http/www_authenticate_parser.rb
314
316
  - lib/mechanize/image.rb
@@ -408,6 +410,7 @@ files:
408
410
  - test/test_mechanize_http_agent.rb
409
411
  - test/test_mechanize_http_auth_challenge.rb
410
412
  - test/test_mechanize_http_auth_realm.rb
413
+ - test/test_mechanize_http_auth_store.rb
411
414
  - test/test_mechanize_http_content_disposition_parser.rb
412
415
  - test/test_mechanize_http_www_authenticate_parser.rb
413
416
  - test/test_mechanize_image.rb
@@ -459,7 +462,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
459
462
  requirements: []
460
463
 
461
464
  rubyforge_project: mechanize
462
- rubygems_version: 1.8.12
465
+ rubygems_version: 1.8.21
463
466
  signing_key:
464
467
  specification_version: 3
465
468
  summary: The Mechanize library is used for automating interaction with websites
@@ -490,6 +493,7 @@ test_files:
490
493
  - test/test_mechanize_http_agent.rb
491
494
  - test/test_mechanize_http_auth_challenge.rb
492
495
  - test/test_mechanize_http_auth_realm.rb
496
+ - test/test_mechanize_http_auth_store.rb
493
497
  - test/test_mechanize_http_content_disposition_parser.rb
494
498
  - test/test_mechanize_http_www_authenticate_parser.rb
495
499
  - test/test_mechanize_image.rb
metadata.gz.sig CHANGED
Binary file