stratagem 0.1.7 → 0.1.8
Sign up to get free protection for your applications and to get access to all the features.
- data/Manifest +16 -4
- data/Rakefile +2 -2
- data/lib/bootstrap.rb +1 -0
- data/lib/stratagem/auto_mock/aquifer.rb +15 -7
- data/lib/stratagem/auto_mock/factory.rb +12 -2
- data/lib/stratagem/auto_mock/value_generator.rb +1 -1
- data/lib/stratagem/commands.rb +0 -1
- data/lib/stratagem/crawler/authentication.rb +116 -54
- data/lib/stratagem/crawler/form.rb +12 -0
- data/lib/stratagem/crawler/html_utils.rb +19 -7
- data/lib/stratagem/crawler/session.rb +156 -68
- data/lib/stratagem/crawler/site_model.rb +21 -7
- data/lib/stratagem/crawler/trace_utils.rb +3 -1
- data/lib/stratagem/extensions/trace_compression.rb +52 -0
- data/lib/stratagem/extensions.rb +1 -0
- data/lib/stratagem/framework_extensions/models/adapters/active_model/metadata.rb +3 -8
- data/lib/stratagem/framework_extensions/models/adapters/active_model/tracing.rb +21 -2
- data/lib/stratagem/framework_extensions/models/adapters/common/detect.rb +7 -0
- data/lib/stratagem/framework_extensions/models/adapters/common/extensions.rb +0 -0
- data/lib/stratagem/framework_extensions/models/adapters/common/metadata.rb +36 -0
- data/lib/stratagem/framework_extensions/models/adapters/common/tracing.rb +4 -0
- data/lib/stratagem/framework_extensions/models/adapters/{common → util}/authentication_metadata.rb +0 -0
- data/lib/stratagem/framework_extensions/models/annotations.rb +23 -1
- data/lib/stratagem/framework_extensions/models/metadata.rb +3 -3
- data/lib/stratagem/framework_extensions/models/tracing.rb +32 -10
- data/lib/stratagem/framework_extensions/models.rb +2 -2
- data/lib/stratagem/model/application.rb +8 -4
- data/lib/stratagem/model/components/base.rb +3 -0
- data/lib/stratagem/model/components/controller.rb +22 -23
- data/lib/stratagem/model/components/model.rb +3 -2
- data/lib/stratagem/model/components/reference.rb +24 -13
- data/lib/stratagem/model/components/route.rb +0 -3
- data/lib/stratagem/model/components/view.rb +1 -0
- data/lib/stratagem/model_builder.rb +9 -11
- data/lib/stratagem/site_crawler.rb +14 -19
- data/lib/stratagem.rb +1 -1
- data/spec/model/component_spec.rb +43 -0
- data/spec/model/components/view_spec.rb +43 -0
- data/spec/model/test_spec.rb +10 -0
- data/spec/samples/404.html.erb +30 -0
- data/spec/samples/_form.html.erb +8 -0
- data/spec/samples/index.html.erb +77 -0
- data/spec/samples/sample_model.rb +5 -0
- data/spec/samples/signup.html.erb +14 -0
- data/spec/scan/checks/email_address_spec.rb +24 -0
- data/spec/scan/checks/error_pages_spec.rb +22 -0
- data/stratagem.gemspec +7 -4
- metadata +50 -21
- data/lib/stratagem/commands/devel_crawl.rb +0 -27
- data/lib/stratagem/scan/checks/ssl/secure_login_page.rb +0 -19
- data/lib/stratagem/scan/checks/ssl/secure_login_submit.rb +0 -18
data/Manifest
CHANGED
@@ -15,7 +15,6 @@ lib/stratagem/command.rb
|
|
15
15
|
lib/stratagem/commands.rb
|
16
16
|
lib/stratagem/commands/analyze.rb
|
17
17
|
lib/stratagem/commands/base.rb
|
18
|
-
lib/stratagem/commands/devel_crawl.rb
|
19
18
|
lib/stratagem/commands/devel_mock.rb
|
20
19
|
lib/stratagem/crawler.rb
|
21
20
|
lib/stratagem/crawler/authentication.rb
|
@@ -31,6 +30,7 @@ lib/stratagem/extensions/module.rb
|
|
31
30
|
lib/stratagem/extensions/object.rb
|
32
31
|
lib/stratagem/extensions/red_parse.rb
|
33
32
|
lib/stratagem/extensions/string.rb
|
33
|
+
lib/stratagem/extensions/trace_compression.rb
|
34
34
|
lib/stratagem/framework_extensions.rb
|
35
35
|
lib/stratagem/framework_extensions/controllers.rb
|
36
36
|
lib/stratagem/framework_extensions/controllers/action_controller.rb
|
@@ -44,11 +44,15 @@ lib/stratagem/framework_extensions/models/adapters/authlogic/detect.rb
|
|
44
44
|
lib/stratagem/framework_extensions/models/adapters/authlogic/extensions.rb
|
45
45
|
lib/stratagem/framework_extensions/models/adapters/authlogic/metadata.rb
|
46
46
|
lib/stratagem/framework_extensions/models/adapters/authlogic/tracing.rb
|
47
|
-
lib/stratagem/framework_extensions/models/adapters/common/
|
47
|
+
lib/stratagem/framework_extensions/models/adapters/common/detect.rb
|
48
|
+
lib/stratagem/framework_extensions/models/adapters/common/extensions.rb
|
49
|
+
lib/stratagem/framework_extensions/models/adapters/common/metadata.rb
|
50
|
+
lib/stratagem/framework_extensions/models/adapters/common/tracing.rb
|
48
51
|
lib/stratagem/framework_extensions/models/adapters/restful_authentication/detect.rb
|
49
52
|
lib/stratagem/framework_extensions/models/adapters/restful_authentication/extensions.rb
|
50
53
|
lib/stratagem/framework_extensions/models/adapters/restful_authentication/metadata.rb
|
51
54
|
lib/stratagem/framework_extensions/models/adapters/restful_authentication/tracing.rb
|
55
|
+
lib/stratagem/framework_extensions/models/adapters/util/authentication_metadata.rb
|
52
56
|
lib/stratagem/framework_extensions/models/annotations.rb
|
53
57
|
lib/stratagem/framework_extensions/models/detect.rb
|
54
58
|
lib/stratagem/framework_extensions/models/metadata.rb
|
@@ -90,10 +94,18 @@ lib/stratagem/scan/checks/filter_parameter_logging.rb
|
|
90
94
|
lib/stratagem/scan/checks/mongo_mapper/base.rb
|
91
95
|
lib/stratagem/scan/checks/mongo_mapper/foreign_keys_exposed.rb
|
92
96
|
lib/stratagem/scan/checks/routes.rb
|
93
|
-
lib/stratagem/scan/checks/ssl/secure_login_page.rb
|
94
|
-
lib/stratagem/scan/checks/ssl/secure_login_submit.rb
|
95
97
|
lib/stratagem/scan/result.rb
|
96
98
|
lib/stratagem/scanner.rb
|
97
99
|
lib/stratagem/site_crawler.rb
|
98
100
|
lib/stratagem/snapshot.rb
|
99
101
|
lib/tasks/_old_stratagem.rake
|
102
|
+
spec/model/component_spec.rb
|
103
|
+
spec/model/components/view_spec.rb
|
104
|
+
spec/model/test_spec.rb
|
105
|
+
spec/samples/404.html.erb
|
106
|
+
spec/samples/_form.html.erb
|
107
|
+
spec/samples/index.html.erb
|
108
|
+
spec/samples/sample_model.rb
|
109
|
+
spec/samples/signup.html.erb
|
110
|
+
spec/scan/checks/email_address_spec.rb
|
111
|
+
spec/scan/checks/error_pages_spec.rb
|
data/Rakefile
CHANGED
@@ -2,14 +2,14 @@ require 'rubygems'
|
|
2
2
|
require 'rake'
|
3
3
|
require 'echoe'
|
4
4
|
|
5
|
-
Echoe.new('stratagem', '0.1.
|
5
|
+
Echoe.new('stratagem', '0.1.8') do |p|
|
6
6
|
p.description = "Intuitive security analysis of your Rails applications"
|
7
7
|
p.url = "http://github.com/stratagem/stratagem"
|
8
8
|
p.author = "Charles Grimes"
|
9
9
|
p.email = "cj@stratagemapp.com"
|
10
10
|
p.executable_pattern = ['bin/*']
|
11
11
|
p.ignore_pattern = ["tmp/*", "script/*", "spec/*", "webapp/*"]
|
12
|
-
p.runtime_dependencies = ["launchy >=0.3.5", "redparse >=0.8.4", "haml >=3.0.0"]
|
12
|
+
p.runtime_dependencies = ["launchy >=0.3.5", "redparse >=0.8.4", "haml >=3.0.0", "nokogiri >=1.4.3"]
|
13
13
|
p.development_dependencies = ["launchy >=0.3.5", "redparse >=0.8.4", "sinatra =1.0", "haml >=3.0.0", "webrat >=0.4.3"]
|
14
14
|
# p.requirements ["Install the stratagem-ui gem for the web browser interface."]
|
15
15
|
end
|
data/lib/bootstrap.rb
CHANGED
@@ -35,31 +35,39 @@ module Stratagem::AutoMock
|
|
35
35
|
end
|
36
36
|
|
37
37
|
def instances_of(model_klass)
|
38
|
-
repo[model_klass].clone
|
38
|
+
objects = (repo[model_klass.name] || []).clone
|
39
|
+
puts "found #{objects.size} instances in well"
|
40
|
+
objects
|
39
41
|
end
|
40
42
|
|
41
43
|
def random_instance(model_klass)
|
42
|
-
objects = repo[model_klass]
|
44
|
+
objects = repo[model_klass.name]
|
43
45
|
puts "found #{objects.size} instances in well"
|
44
46
|
instance = objects[rand objects.size]
|
45
47
|
instance
|
46
48
|
end
|
47
49
|
|
48
|
-
def fill
|
50
|
+
def fill(model_count=nil)
|
49
51
|
Stratagem.logger.phase "mocking_models"
|
50
52
|
application.models.each do |meta_model|
|
51
|
-
models = mock_model(meta_model.klass) if (meta_model.stratagem?)
|
53
|
+
models = mock_model(meta_model.klass, model_count) if (meta_model.stratagem?)
|
52
54
|
end
|
53
55
|
puts "aquifer full"
|
56
|
+
print
|
57
|
+
self
|
58
|
+
end
|
59
|
+
|
60
|
+
def print
|
54
61
|
application.models.each do |meta_model|
|
55
62
|
puts "#{meta_model.klass.name}"
|
56
|
-
(repo[meta_model.klass] || []).each do |instance|
|
57
|
-
puts "\t#{instance.
|
63
|
+
(repo[meta_model.klass.name] || []).each do |instance|
|
64
|
+
puts "\t#{instance.class} - #{instance}"
|
58
65
|
end
|
59
66
|
end
|
60
67
|
end
|
61
68
|
|
62
|
-
def mock_model(klass, count=
|
69
|
+
def mock_model(klass, count=nil)
|
70
|
+
count ||= 6
|
63
71
|
count.times do |i|
|
64
72
|
instance,valid = mock(klass)
|
65
73
|
end
|
@@ -11,6 +11,13 @@ module Stratagem::AutoMock
|
|
11
11
|
module Factory
|
12
12
|
include ValueGenerator
|
13
13
|
|
14
|
+
def mock_attributes(model)
|
15
|
+
object = model.new
|
16
|
+
populate_attributes(object, [])
|
17
|
+
correct_invalid_columns(object, [])
|
18
|
+
object.stratagem.mock_attributes
|
19
|
+
end
|
20
|
+
|
14
21
|
def mock(model,mock_chain=[], belongs_to=nil)
|
15
22
|
return if mock_chain.select {|m| m == model }.size > 1
|
16
23
|
mock_chain << model
|
@@ -47,13 +54,13 @@ module Stratagem::AutoMock
|
|
47
54
|
protected
|
48
55
|
|
49
56
|
def add_mocked(instance)
|
50
|
-
(mocked
|
57
|
+
(mocked(instance.class)) << instance
|
51
58
|
end
|
52
59
|
|
53
60
|
def mocked(model=nil)
|
54
61
|
@mocked ||= {}
|
55
62
|
if (model)
|
56
|
-
@mocked[model] ||= []
|
63
|
+
@mocked[model.name] ||= []
|
57
64
|
else
|
58
65
|
@mocked
|
59
66
|
end
|
@@ -174,6 +181,9 @@ module Stratagem::AutoMock
|
|
174
181
|
end
|
175
182
|
puts $!.backtrace unless valid
|
176
183
|
end
|
184
|
+
|
185
|
+
puts "\t#{object.stratagem.mock_attributes.inspect}" if (valid)
|
186
|
+
|
177
187
|
valid
|
178
188
|
end
|
179
189
|
|
@@ -51,7 +51,7 @@ module Stratagem::AutoMock
|
|
51
51
|
else
|
52
52
|
raise Stratagem::AutoMock::UnsupportedColumnTypeError.new("Attribute #{attribute_name} of type #{attribute_type} is not supported")
|
53
53
|
end
|
54
|
-
value = nil if (rand(20) == 1) && !NUMERIC_TYPES.include?(attribute_type)
|
54
|
+
value = nil if (rand(20) == 1) && (!NUMERIC_TYPES.include?(attribute_type)) && (attribute_name.to_s !~ /password/)
|
55
55
|
value
|
56
56
|
end
|
57
57
|
|
data/lib/stratagem/commands.rb
CHANGED
@@ -7,73 +7,104 @@ module Stratagem::Crawler
|
|
7
7
|
module Authentication
|
8
8
|
include Stratagem::Crawler::TraceUtils
|
9
9
|
|
10
|
+
def users
|
11
|
+
page = find_login_form
|
12
|
+
users = []
|
13
|
+
if (page)
|
14
|
+
form = page.login_form
|
15
|
+
attr_names = form.inputs.map {|input| input.guess_attribute.to_sym }
|
16
|
+
model = guess_login_model(attr_names)
|
17
|
+
if (model)
|
18
|
+
users = aquifer.instances_of(model.klass)
|
19
|
+
else
|
20
|
+
log "ERROR: Unable to determine user model"
|
21
|
+
end
|
22
|
+
else
|
23
|
+
log "ERROR: Could not find login form"
|
24
|
+
end
|
25
|
+
users
|
26
|
+
end
|
27
|
+
|
28
|
+
def reset_authentication
|
29
|
+
@authentication_data = nil
|
30
|
+
end
|
31
|
+
|
10
32
|
def authentication
|
11
|
-
@authentication_data
|
33
|
+
unless @authentication_data
|
34
|
+
@authentication_data = AuthenticationData.new()
|
35
|
+
site_model.authentication = @authentication_data
|
36
|
+
end
|
37
|
+
@authentication_data
|
12
38
|
end
|
13
39
|
|
14
|
-
def authenticate
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
begin
|
40
|
+
def authenticate(user, recursion_count=0)
|
41
|
+
reset_authentication
|
42
|
+
|
43
|
+
login(user)
|
44
|
+
route = application_model.routes.recognize(response.request.path, :post)
|
45
|
+
|
46
|
+
redirected_to = nil
|
47
|
+
page = site_model.add(route, response) {|redirect_url| redirected_to = redirect_url }
|
48
|
+
authentication.response_page = page
|
49
|
+
|
50
|
+
begin
|
51
|
+
if (response.request.url == (redirected_to || '')) || (![200,302].include?(response.code.to_i))
|
52
|
+
authentication.success = false
|
53
|
+
else
|
29
54
|
authentication.success = authentication.response_page.login_form.nil?
|
30
|
-
rescue
|
31
|
-
puts $!.message
|
32
|
-
puts $!.backtrace
|
33
55
|
end
|
34
|
-
|
35
|
-
|
36
|
-
puts
|
56
|
+
rescue
|
57
|
+
puts $!.message
|
58
|
+
puts $!.backtrace
|
37
59
|
end
|
38
60
|
|
39
|
-
|
61
|
+
puts "authenticated? #{authentication.success}"
|
62
|
+
if (response && authentication.success)
|
40
63
|
authentication.ssl = response.request.ssl?
|
41
|
-
|
64
|
+
yield
|
65
|
+
logout
|
42
66
|
else
|
43
67
|
false
|
44
68
|
end
|
45
69
|
end
|
46
70
|
|
47
71
|
def find_login_form
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
72
|
+
puts "finding login form"
|
73
|
+
if authentication.login_page.nil?
|
74
|
+
puts "locating login page"
|
75
|
+
puts "testing #{site_models.first.pages.size} pages"
|
76
|
+
site_models.first.pages.sort {|a,b| b.inbound_edges(:redirect).size <=> a.inbound_edges(:redirect).size }.each do |page|
|
77
|
+
puts "Testing page #{page.url} for sign in form"
|
78
|
+
# page.reload {|url| get url; response }
|
79
|
+
# form = page.login_form
|
80
|
+
if (page.login_form)
|
81
|
+
puts "FOUND! - #{page.login_form}"
|
82
|
+
authentication.login_page = page
|
83
|
+
return page
|
84
|
+
end
|
85
|
+
end
|
86
|
+
else
|
87
|
+
return authentication.login_page
|
53
88
|
end
|
54
|
-
|
89
|
+
nil
|
55
90
|
end
|
56
91
|
|
57
|
-
def
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
record = Stratagem::AutoMock::Aquifer.instance.random_instance(model.klass)
|
92
|
+
def logout
|
93
|
+
get "/signout"
|
94
|
+
get "/logout"
|
95
|
+
delete "/user_sessions/1"
|
96
|
+
end
|
63
97
|
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
end
|
70
|
-
else
|
71
|
-
raise "Unable to infer model from inputs"
|
72
|
-
end
|
98
|
+
def login(user)
|
99
|
+
populate_login_form(user).submit {|action,params|
|
100
|
+
post(action, params)
|
101
|
+
puts response.body
|
102
|
+
}
|
73
103
|
end
|
74
104
|
|
75
105
|
def guess_login_model(attr_names)
|
76
106
|
selections = application_model.models.select {|model|
|
107
|
+
puts "#{model.klass.name} - #{model.model_attributes.keys.inspect}"
|
77
108
|
intersect = (model.model_attributes.keys & attr_names)
|
78
109
|
intersect.size > 0
|
79
110
|
}.sort {|a,b|
|
@@ -81,28 +112,59 @@ module Stratagem::Crawler
|
|
81
112
|
b_intersect = (b.model_attributes.keys & attr_names)
|
82
113
|
b_intersect.size <=> a_intersect.size
|
83
114
|
}
|
84
|
-
|
115
|
+
|
116
|
+
explicit_model = application_model.models.find {|model| model.klass.name == 'User' }
|
117
|
+
selections.unshift explicit_model if explicit_model
|
118
|
+
|
119
|
+
puts "selecting model #{selections.first.klass.name} for authentication" if (selections.size > 0)
|
85
120
|
selections.first
|
86
121
|
end
|
87
122
|
|
88
123
|
|
89
|
-
def populate_login_form(
|
124
|
+
def populate_login_form(user)
|
125
|
+
# set up the form
|
126
|
+
page = find_login_form
|
127
|
+
page.reload {|url| get url; response }
|
128
|
+
form = page.login_form
|
129
|
+
|
130
|
+
# map the input values
|
90
131
|
form.inputs.each do |input|
|
132
|
+
# try the expected
|
91
133
|
attribute_name = input.guess_attribute.to_sym
|
92
|
-
attribute_value =
|
134
|
+
attribute_value = user.stratagem.read_mock_attribute(attribute_name) || input.value
|
93
135
|
|
94
|
-
|
136
|
+
# try again
|
137
|
+
if (attribute_value.nil? || attribute_value == '')
|
138
|
+
attribute_name = input.guess_alternate_attribute.to_sym
|
139
|
+
attribute_value = user.stratagem.read_mock_attribute(attribute_name) || input.value
|
140
|
+
end
|
141
|
+
|
142
|
+
# if it still failed is it a confirmation value? if so, try the original
|
143
|
+
if (attribute_value.nil? || attribute_value == '')
|
144
|
+
if (attribute_name.to_s =~ /confirm/)
|
145
|
+
possible_match = attribute_name.to_s.split('_').select {|a| a !~ /confirm/ }.join('_')
|
146
|
+
if user.stratagem.mock_attributes.keys.include?(possible_match)
|
147
|
+
attribute_value = user.stratagem.read_mock_attribute(possible_match) || input.value
|
148
|
+
end
|
149
|
+
end
|
150
|
+
end
|
151
|
+
|
95
152
|
|
96
153
|
if (input.kind_of? Stratagem::Crawler::Toggle)
|
97
154
|
input.check
|
98
|
-
elsif (
|
99
|
-
input.value =
|
155
|
+
elsif (user.stratagem.mock_attributes.keys.include?(attribute_name))
|
156
|
+
input.value = user.stratagem.read_mock_attribute(attribute_name) unless input.hidden?
|
157
|
+
elsif (attribute_name.to_s == 'authenticity_token')
|
158
|
+
puts input.value
|
100
159
|
else
|
101
|
-
puts
|
102
|
-
puts "ERROR: Cannot find attribute #{attribute_name} in model #{
|
160
|
+
puts user.stratagem.mock_attributes.inspect
|
161
|
+
puts "ERROR: Cannot find attribute #{attribute_name} in model #{user.class.name}"
|
103
162
|
end
|
163
|
+
|
164
|
+
puts "3 authentication field: #{input.name} -> #{input.value}"
|
165
|
+
|
104
166
|
end
|
105
|
-
form
|
167
|
+
form
|
106
168
|
end
|
107
169
|
|
108
170
|
end
|
@@ -58,6 +58,14 @@ module Stratagem::Crawler
|
|
58
58
|
end
|
59
59
|
end
|
60
60
|
|
61
|
+
def guess_alternate_attribute
|
62
|
+
if (name =~ /(.*)\[(.*)\]/)
|
63
|
+
$1.to_sym
|
64
|
+
else
|
65
|
+
name.to_sym
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
61
69
|
def guess_model
|
62
70
|
return $1.camelize if (name =~ /(.*)?\[/)
|
63
71
|
return nil
|
@@ -96,6 +104,10 @@ module Stratagem::Crawler
|
|
96
104
|
|
97
105
|
def <<(value)
|
98
106
|
@options << value
|
107
|
+
self.value = value if self.value.nil?
|
99
108
|
end
|
100
109
|
end
|
110
|
+
|
111
|
+
class Radio < Select
|
112
|
+
end
|
101
113
|
end
|
@@ -6,6 +6,7 @@ module Stratagem::Crawler
|
|
6
6
|
INPUT_TEXT = ['text', 'password', 'hidden']
|
7
7
|
INPUT_BUTTON = ['button', 'submit', 'reset', 'image', 'src']
|
8
8
|
INPUT_TOGGLE = ['checkbox']
|
9
|
+
INPUT_RADIO = ['radio']
|
9
10
|
|
10
11
|
def find_login_form(document)
|
11
12
|
possibilities = parse_forms(document).select {|form|
|
@@ -65,16 +66,27 @@ module Stratagem::Crawler
|
|
65
66
|
input = Button.new()
|
66
67
|
elsif (INPUT_TOGGLE.include?(type))
|
67
68
|
input = Toggle.new()
|
69
|
+
elsif (INPUT_RADIO.include?(type))
|
70
|
+
name = form_load_attribute(input_tag, 'name', false)
|
71
|
+
input = form.inputs.find {|i| i.name == name } || Radio.new()
|
68
72
|
else
|
69
|
-
raise FormParseError.new("Unsupported <input> type: '#{type}'", :html => input_tag.to_html)
|
73
|
+
#raise FormParseError.new("Unsupported <input> type: '#{type}'", :html => input_tag.to_html)
|
74
|
+
puts "ERROR: Unsupported input type: #{type}"
|
70
75
|
end
|
71
76
|
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
77
|
+
if (input)
|
78
|
+
input.id = form_load_attribute(input_tag, 'id', false)
|
79
|
+
input.type = form_load_attribute(input_tag, 'type', false) || 'text'
|
80
|
+
input.name = form_load_attribute(input_tag, 'name', false)
|
81
|
+
if (input.class.ancestors.include?(Select))
|
82
|
+
input << form_load_attribute(input_tag, 'value', false)
|
83
|
+
else
|
84
|
+
input.value = form_load_attribute(input_tag, 'value', false)
|
85
|
+
end
|
76
86
|
|
77
|
-
|
87
|
+
form << input
|
88
|
+
end
|
89
|
+
form
|
78
90
|
end
|
79
91
|
|
80
92
|
def form_load_attribute(node, attribute_name, raise_error = true)
|
@@ -82,7 +94,7 @@ module Stratagem::Crawler
|
|
82
94
|
attr = node.attributes[attribute_name]
|
83
95
|
value = nil
|
84
96
|
if (attr)
|
85
|
-
value = attr.value.strip
|
97
|
+
value = attr.value.strip
|
86
98
|
elsif (raise_error)
|
87
99
|
raise FormParseError.new("#{attribute_name} attribute not found in tag - #{node.to_html}")
|
88
100
|
end
|