arachni 1.0.4 → 1.0.5
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +42 -0
- data/README.md +8 -4
- data/bin/arachni_console +1 -1
- data/components/checks/active/no_sql_injection.rb +4 -4
- data/components/checks/passive/common_directories/directories.txt +1 -0
- data/components/checks/passive/common_files/filenames.txt +1 -0
- data/components/plugins/login_script.rb +156 -0
- data/components/reporters/plugin_formatters/html/login_script.rb +48 -0
- data/components/reporters/plugin_formatters/stdout/login_script.rb +23 -0
- data/components/reporters/plugin_formatters/xml/login_script.rb +26 -0
- data/components/reporters/xml/schema.xsd +17 -0
- data/lib/arachni/browser.rb +7 -4
- data/lib/arachni/browser/javascript.rb +40 -4
- data/lib/arachni/browser/javascript/proxy.rb +1 -1
- data/lib/arachni/browser_cluster/worker.rb +14 -4
- data/lib/arachni/check/auditor.rb +24 -7
- data/lib/arachni/check/manager.rb +6 -0
- data/lib/arachni/framework.rb +54 -6
- data/lib/arachni/http/client.rb +41 -23
- data/lib/arachni/http/headers.rb +5 -1
- data/lib/arachni/http/message.rb +0 -7
- data/lib/arachni/http/request.rb +40 -32
- data/lib/arachni/http/response.rb +8 -1
- data/lib/arachni/platform/manager.rb +7 -0
- data/lib/arachni/rpc/server/framework/multi_instance.rb +1 -1
- data/lib/arachni/session.rb +88 -58
- data/lib/arachni/state/framework.rb +34 -5
- data/lib/arachni/support/profiler.rb +2 -0
- data/lib/arachni/uri.rb +2 -1
- data/lib/version +1 -1
- data/spec/arachni/browser/javascript_spec.rb +15 -0
- data/spec/arachni/check/manager_spec.rb +17 -0
- data/spec/arachni/framework_spec.rb +4 -2
- data/spec/arachni/http/client_spec.rb +1 -1
- data/spec/arachni/session_spec.rb +80 -37
- data/spec/arachni/state/framework_spec.rb +34 -1
- data/spec/arachni/uri_spec.rb +7 -0
- data/spec/components/plugins/login_script_spec.rb +157 -0
- data/spec/support/servers/plugins/login_script.rb +13 -0
- data/ui/cli/output.rb +26 -9
- metadata +11 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 113357f2e377065a79df18200488cc13fe744b6d
|
4
|
+
data.tar.gz: 41739b4e27d567d15f67f9ed86c571c24143c3a0
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 0418e763e4b5577d1d6856cbbf3cdc242ba04525e10de155b4b86484e6208acdf78cea104e0ce9e75a44aab73fb8e7d40c10be0459d58811b8f7a83b6b4971e1
|
7
|
+
data.tar.gz: 2ab357a8d52233a026374d1f0bd09ba03d8ebffa9d939d8a97f51bf76c638e95362c1f080b6ed78c54e630d1a13a0c4ddd90dbe61555259dddef762d3a3385e3
|
data/CHANGELOG.md
CHANGED
@@ -1,5 +1,47 @@
|
|
1
1
|
# ChangeLog
|
2
2
|
|
3
|
+
## 1.0.5 _(November 14, 2014)_
|
4
|
+
|
5
|
+
- Executables
|
6
|
+
- `arachni_console` -- Require the UI::Output interface after Arachni.
|
7
|
+
- Error log
|
8
|
+
- Redacted HTTP authentication credentials.
|
9
|
+
- `Session`
|
10
|
+
- Added `#record_login_sequence`, allowing for arbitrary login sequences to
|
11
|
+
be stored and replayed.
|
12
|
+
- `URI`
|
13
|
+
- `#domain` -- Fixed `nil` error on missing host.
|
14
|
+
- `.query_parameters` -- Recode query string before parsing to fix encoding errors.
|
15
|
+
- `UI::Output`
|
16
|
+
- `#log_error` -- Store errors in memory, as well as in logfile.
|
17
|
+
- `RPC::Server::Framework::MultiInstance`
|
18
|
+
- `#errors` -- Return errors from memory buffer instead of logfile, to
|
19
|
+
prevent "Too many open file" exceptions.
|
20
|
+
- ` Framework`
|
21
|
+
- `#audit_page` -- Keep track of checked elements at the `Framework` level
|
22
|
+
too and remove them from pages.
|
23
|
+
- `Browser`
|
24
|
+
- Fixed `nil` error on failed process spawn.
|
25
|
+
- `Javascript` -- Updated to preload and cache script sources to avoid
|
26
|
+
hitting the disk in order to prevent "Too many open file" exceptions.
|
27
|
+
- `#run_without_elements` -- Runs a script but unwraps `Watir` elements.
|
28
|
+
- `Proxy` -- Updated to use `#run_without_elements`.
|
29
|
+
- `BrowserCluster::Worker`
|
30
|
+
- Print error message on failure to respawn.
|
31
|
+
- `Check::Auditor` -- Updated audit helpers to mark elements as audited at the
|
32
|
+
check component level, to avoid sending redundant workload to the analysis
|
33
|
+
classes only to be ignored there.
|
34
|
+
- `#skip?` -- Optimized redundant issue checks.
|
35
|
+
- Checks
|
36
|
+
- Active
|
37
|
+
- `no_sql_injection` -- Updated payloads to be per platform.
|
38
|
+
- Passive
|
39
|
+
- `common_files` -- Added more filenames. [PR #504]
|
40
|
+
- Plugins
|
41
|
+
- Added
|
42
|
+
- `login_script` -- Exposes a Watir WebDriver interface to an external
|
43
|
+
script in order to allow for arbitrary login sequences.
|
44
|
+
|
3
45
|
## 1.0.4 _(October 25, 2014)_
|
4
46
|
|
5
47
|
- CLI options
|
data/README.md
CHANGED
@@ -3,7 +3,7 @@
|
|
3
3
|
<table>
|
4
4
|
<tr>
|
5
5
|
<th>Version</th>
|
6
|
-
<td>1.0.
|
6
|
+
<td>1.0.5</td>
|
7
7
|
</tr>
|
8
8
|
<tr>
|
9
9
|
<th>Homepage</th>
|
@@ -121,7 +121,7 @@ you with its findings.
|
|
121
121
|
- Proxy authentication.
|
122
122
|
- Site authentication (Automated form-based, Cookie-Jar, Basic-Digest, NTLMv1 and others).
|
123
123
|
- Automatic log-out detection and re-login during the scan (when the initial
|
124
|
-
login was performed via the `autologin` or `proxy` plugins).
|
124
|
+
login was performed via the `autologin`, `login_script` or `proxy` plugins).
|
125
125
|
- Custom 404 page detection.
|
126
126
|
- UI abstraction:
|
127
127
|
- [Command-line Interface](https://github.com/Arachni/arachni/wiki/Executables).
|
@@ -419,7 +419,10 @@ Active checks engage the web application via its inputs.
|
|
419
419
|
- Path XSS (`xss_path`).
|
420
420
|
- XSS in event attributes of HTML elements (`xss_event`).
|
421
421
|
- XSS in HTML tags (`xss_tag`).
|
422
|
-
- XSS in
|
422
|
+
- XSS in script context (`xss_script_context`).
|
423
|
+
- DOM XSS (`xss_dom`).
|
424
|
+
- DOM XSS inputs (`xss_dom_inputs`).
|
425
|
+
- DOM XSS script context (`xss_dom_script_context`).
|
423
426
|
- Source code disclosure (`source_code_disclosure`)
|
424
427
|
|
425
428
|
##### Passive
|
@@ -475,7 +478,8 @@ core remains lean and makes it easy for anyone to add arbitrary functionality.
|
|
475
478
|
|
476
479
|
- Passive Proxy (`proxy`) -- Analyzes requests and responses between the web app and
|
477
480
|
the browser assisting in AJAX audits, logging-in and/or restricting the scope of the audit.
|
478
|
-
- Form based
|
481
|
+
- Form based login (`autologin`).
|
482
|
+
- Script based login (`login_script`).
|
479
483
|
- Dictionary attacker for HTTP Auth (`http_dicattack`).
|
480
484
|
- Dictionary attacker for form based authentication (`form_dicattack`).
|
481
485
|
- Cookie collector (`cookie_collector`) -- Keeps track of cookies while establishing a timeline of changes.
|
data/bin/arachni_console
CHANGED
@@ -7,7 +7,7 @@
|
|
7
7
|
=end
|
8
8
|
|
9
9
|
# @author Tasos "Zapotek" Laskos <tasos.laskos@arachni-scanner.com>
|
10
|
-
# @version 0.1
|
10
|
+
# @version 0.1.1
|
11
11
|
class Arachni::Checks::NoSqlInjection < Arachni::Check::Base
|
12
12
|
|
13
13
|
def self.error_patterns
|
@@ -31,7 +31,7 @@ class Arachni::Checks::NoSqlInjection < Arachni::Check::Base
|
|
31
31
|
# Prepares the payloads that will hopefully cause the webapp to output SQL
|
32
32
|
# error messages if included as part of an SQL query.
|
33
33
|
def self.payloads
|
34
|
-
@payloads ||=
|
34
|
+
@payloads ||= { mongodb: '\';.")' }
|
35
35
|
end
|
36
36
|
|
37
37
|
def self.options
|
@@ -57,8 +57,8 @@ NoSQL injection check, uses known DB errors to identify vulnerabilities.
|
|
57
57
|
elements: [Element::Link, Element::Form, Element::Cookie,
|
58
58
|
Element::Header, Element::LinkTemplate ],
|
59
59
|
author: 'Tasos "Zapotek" Laskos <tasos.laskos@arachni-scanner.com>',
|
60
|
-
version: '0.1',
|
61
|
-
platforms:
|
60
|
+
version: '0.1.1',
|
61
|
+
platforms: payloads.keys,
|
62
62
|
|
63
63
|
issue: {
|
64
64
|
name: %q{NoSQL Injection},
|
@@ -0,0 +1,156 @@
|
|
1
|
+
=begin
|
2
|
+
Copyright 2010-2014 Tasos Laskos <tasos.laskos@arachni-scanner.com>
|
3
|
+
|
4
|
+
This file is part of the Arachni Framework project and is subject to
|
5
|
+
redistribution and commercial restrictions. Please see the Arachni Framework
|
6
|
+
web site for more information on licensing and terms of use.
|
7
|
+
=end
|
8
|
+
|
9
|
+
# Automated login plugin using a custom login script.
|
10
|
+
#
|
11
|
+
# @author Tasos "Zapotek" Laskos <tasos.laskos@arachni-scanner.com>
|
12
|
+
class Arachni::Plugins::LoginScript < Arachni::Plugin::Base
|
13
|
+
|
14
|
+
STATUSES = {
|
15
|
+
success: 'Login was successful.',
|
16
|
+
failure: 'The script was executed successfully, but the login check failed.',
|
17
|
+
error: 'A runtime error was encountered while executing the login script.',
|
18
|
+
missing_check: 'No session check was provided, either via interface options or the script.'
|
19
|
+
}
|
20
|
+
|
21
|
+
def prepare
|
22
|
+
script = IO.read( @options[:script] )
|
23
|
+
@script = proc { |browser| eval script }
|
24
|
+
|
25
|
+
framework_pause
|
26
|
+
print_info 'System paused.'
|
27
|
+
end
|
28
|
+
|
29
|
+
def run
|
30
|
+
session.record_login_sequence do |browser|
|
31
|
+
print_info 'Running the script.'
|
32
|
+
@script.call browser ? browser.watir : nil
|
33
|
+
print_info 'Execution completed.'
|
34
|
+
end
|
35
|
+
|
36
|
+
begin
|
37
|
+
session.login
|
38
|
+
rescue => e
|
39
|
+
set_status :error
|
40
|
+
print_exception e
|
41
|
+
return
|
42
|
+
end
|
43
|
+
|
44
|
+
if !session.logged_in?
|
45
|
+
set_status :failure, :error
|
46
|
+
return
|
47
|
+
end
|
48
|
+
|
49
|
+
cookies = http.cookies.inject({}){ |h, c| h.merge!( c.simple ) }
|
50
|
+
|
51
|
+
set_status :success, :ok, { 'cookies' => cookies }
|
52
|
+
|
53
|
+
print_info 'Cookies set to:'
|
54
|
+
cookies.each do |name, val|
|
55
|
+
print_info " * #{name.inspect} = #{val.inspect}"
|
56
|
+
end
|
57
|
+
|
58
|
+
rescue Arachni::Session::Error::NoLoginCheck
|
59
|
+
set_status :missing_check, :error
|
60
|
+
rescue => e
|
61
|
+
set_status :error
|
62
|
+
print_exception e
|
63
|
+
end
|
64
|
+
|
65
|
+
def clean_up
|
66
|
+
if @failed
|
67
|
+
print_info 'Aborting the scan.'
|
68
|
+
framework_abort
|
69
|
+
return
|
70
|
+
end
|
71
|
+
|
72
|
+
framework_resume
|
73
|
+
end
|
74
|
+
|
75
|
+
def set_status( status, type = nil, extra = {} )
|
76
|
+
type ||= status
|
77
|
+
|
78
|
+
register_results(
|
79
|
+
{
|
80
|
+
'status' => status.to_s,
|
81
|
+
'message' => STATUSES[status]
|
82
|
+
}.merge( extra )
|
83
|
+
)
|
84
|
+
|
85
|
+
@failed = true if type == :error
|
86
|
+
send "print_#{type}", STATUSES[status]
|
87
|
+
end
|
88
|
+
|
89
|
+
def self.info
|
90
|
+
{
|
91
|
+
name: 'Login script',
|
92
|
+
description: %q{
|
93
|
+
Loads and sets an external script as the system's login sequence, to be executed
|
94
|
+
prior to the scan and whenever a log-out is detected.
|
95
|
+
|
96
|
+
The script needn't necessarily perform an actual login operation. If another
|
97
|
+
process is used to manage sessions, the script can be used to communicate with
|
98
|
+
that process and, for example, load and set cookies from a shared cookie-jar.
|
99
|
+
|
100
|
+
**With browser (slow):**
|
101
|
+
|
102
|
+
If a [browser](http://watirwebdriver.com/) is available, it will be exposed to
|
103
|
+
the script via the `browser` variable. Otherwise, that variable will have a
|
104
|
+
value of `nil`.
|
105
|
+
|
106
|
+
browser.goto 'http://testfire.net/bank/login.aspx'
|
107
|
+
|
108
|
+
form = browser.form( id: 'login' )
|
109
|
+
form.text_field( name: 'uid' ).set 'jsmith'
|
110
|
+
form.text_field( name: 'passw' ).set 'Demo1234'
|
111
|
+
|
112
|
+
form.submit
|
113
|
+
|
114
|
+
# You can also configure the session check from the script, dynamically,
|
115
|
+
# if you don't want to set static options via the user interface.
|
116
|
+
framework.options.session.check_url = browser.url
|
117
|
+
framework.options.session.check_pattern = /Sign Off|MY ACCOUNT/
|
118
|
+
|
119
|
+
**Without browser (fast):**
|
120
|
+
|
121
|
+
If a real browser environment is not required for the login operation, then
|
122
|
+
using the system-wide HTTP interface is preferable, as it will be much faster
|
123
|
+
and consume much less resources.
|
124
|
+
|
125
|
+
response = http.post( 'http://testfire.net/bank/login.aspx',
|
126
|
+
parameters: {
|
127
|
+
'uid' => 'jsmith',
|
128
|
+
'passw' => 'Demo1234'
|
129
|
+
},
|
130
|
+
mode: :sync,
|
131
|
+
update_cookies: true
|
132
|
+
)
|
133
|
+
|
134
|
+
framework.options.session.check_url = to_absolute( response.headers.location, response.url )
|
135
|
+
framework.options.session.check_pattern = /Sign Off|MY ACCOUNT/
|
136
|
+
|
137
|
+
**From cookie-jar:**
|
138
|
+
|
139
|
+
If an external process is used to manage sessions, you can keep Arachni in sync
|
140
|
+
by loading cookies from a shared Netscape-style cookie-jar file.
|
141
|
+
|
142
|
+
http.cookie_jar.load 'cookies.txt'
|
143
|
+
},
|
144
|
+
author: 'Tasos "Zapotek" Laskos <tasos.laskos@arachni-scanner.com>',
|
145
|
+
version: '0.1',
|
146
|
+
options: [
|
147
|
+
Options::Path.new( :script,
|
148
|
+
required: true,
|
149
|
+
description: 'Script that includes the login sequence.'
|
150
|
+
),
|
151
|
+
],
|
152
|
+
priority: 0 # run before any other plugin
|
153
|
+
}
|
154
|
+
end
|
155
|
+
|
156
|
+
end
|
@@ -0,0 +1,48 @@
|
|
1
|
+
=begin
|
2
|
+
Copyright 2010-2014 Tasos Laskos <tasos.laskos@arachni-scanner.com>
|
3
|
+
|
4
|
+
This file is part of the Arachni Framework project and is subject to
|
5
|
+
redistribution and commercial restrictions. Please see the Arachni Framework
|
6
|
+
web site for more information on licensing and terms of use.
|
7
|
+
=end
|
8
|
+
|
9
|
+
class Arachni::Reporters::HTML
|
10
|
+
|
11
|
+
# @author Tasos "Zapotek" Laskos <tasos.laskos@arachni-scanner.com>
|
12
|
+
class PluginFormatters::LoginScript < Arachni::Plugin::Formatter
|
13
|
+
include TemplateUtilities
|
14
|
+
|
15
|
+
def run
|
16
|
+
ERB.new( tpl ).result( binding )
|
17
|
+
end
|
18
|
+
|
19
|
+
def tpl
|
20
|
+
<<-HTML
|
21
|
+
<% if results['status'] == 'success' %>
|
22
|
+
<p class="alert alert-success">
|
23
|
+
<%= results['message'] %>
|
24
|
+
</p>
|
25
|
+
|
26
|
+
<h3>Cookies set to:</h3>
|
27
|
+
|
28
|
+
<dl class="dl-horizontal">
|
29
|
+
<% results['cookies'].each do |k, v| %>
|
30
|
+
<dt>
|
31
|
+
<code><%= escapeHTML( k ) %></code>
|
32
|
+
</dt>
|
33
|
+
<dd>
|
34
|
+
<code><%= escapeHTML( v ) %></code>
|
35
|
+
</dd>
|
36
|
+
<% end %>
|
37
|
+
</dl>
|
38
|
+
<% else %>
|
39
|
+
<p class="alert alert-danger">
|
40
|
+
<%= results['message'] %>
|
41
|
+
</p>
|
42
|
+
<% end %>
|
43
|
+
HTML
|
44
|
+
end
|
45
|
+
|
46
|
+
end
|
47
|
+
|
48
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
=begin
|
2
|
+
Copyright 2010-2014 Tasos Laskos <tasos.laskos@arachni-scanner.com>
|
3
|
+
|
4
|
+
This file is part of the Arachni Framework project and is subject to
|
5
|
+
redistribution and commercial restrictions. Please see the Arachni Framework
|
6
|
+
web site for more information on licensing and terms of use.
|
7
|
+
=end
|
8
|
+
|
9
|
+
class Arachni::Reporters::Stdout
|
10
|
+
|
11
|
+
# @author Tasos "Zapotek" Laskos <tasos.laskos@arachni-scanner.com>
|
12
|
+
class PluginFormatters::LoginScript < Arachni::Plugin::Formatter
|
13
|
+
|
14
|
+
def run
|
15
|
+
print_ok results['message']
|
16
|
+
|
17
|
+
return if !results['cookies']
|
18
|
+
print_info 'Cookies set to:'
|
19
|
+
results['cookies'].each_pair { |name, val| print_info " * #{name} = #{val}" }
|
20
|
+
end
|
21
|
+
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
=begin
|
2
|
+
Copyright 2010-2014 Tasos Laskos <tasos.laskos@arachni-scanner.com>
|
3
|
+
|
4
|
+
This file is part of the Arachni Framework project and is subject to
|
5
|
+
redistribution and commercial restrictions. Please see the Arachni Framework
|
6
|
+
web site for more information on licensing and terms of use.
|
7
|
+
=end
|
8
|
+
|
9
|
+
class Arachni::Reporters::XML
|
10
|
+
|
11
|
+
# @author Tasos "Zapotek" Laskos <tasos.laskos@arachni-scanner.com>
|
12
|
+
class PluginFormatters::LoginScript < Arachni::Plugin::Formatter
|
13
|
+
|
14
|
+
def run( xml )
|
15
|
+
xml.message results['message']
|
16
|
+
xml.status results['status']
|
17
|
+
|
18
|
+
if results['cookies']
|
19
|
+
xml.cookies {
|
20
|
+
results['cookies'].each { |name, value| xml.cookie name: name, value: value }
|
21
|
+
}
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
end
|
26
|
+
end
|
@@ -25,6 +25,7 @@
|
|
25
25
|
<xs:choice minOccurs="0" maxOccurs="9">
|
26
26
|
<xs:element name="healthmap" type="plugin_healthmap" maxOccurs="1"/>
|
27
27
|
<xs:element name="autologin" type="plugin_autologin" maxOccurs="1"/>
|
28
|
+
<xs:element name="login_script" type="plugin_login_script" maxOccurs="1"/>
|
28
29
|
<xs:element name="content_types" type="plugin_content_types" maxOccurs="1"/>
|
29
30
|
<xs:element name="cookie_collector" type="plugin_cookie_collector" maxOccurs="1"/>
|
30
31
|
<xs:element name="form_dicattack" type="plugin_form_dicattack" maxOccurs="1"/>
|
@@ -187,6 +188,22 @@
|
|
187
188
|
</xs:all>
|
188
189
|
</xs:complexType>
|
189
190
|
|
191
|
+
<xs:complexType name="plugin_login_script">
|
192
|
+
<xs:all>
|
193
|
+
<xs:element name="name" type="xs:string"/>
|
194
|
+
<xs:element name="description" type="xs:string"/>
|
195
|
+
<xs:element name="results" type="plugin_login_script_results"/>
|
196
|
+
</xs:all>
|
197
|
+
</xs:complexType>
|
198
|
+
|
199
|
+
<xs:complexType name="plugin_login_script_results">
|
200
|
+
<xs:all>
|
201
|
+
<xs:element name="status" type="xs:string"/>
|
202
|
+
<xs:element name="message" type="xs:string"/>
|
203
|
+
<xs:element name="cookies" type="cookies" minOccurs="0"/>
|
204
|
+
</xs:all>
|
205
|
+
</xs:complexType>
|
206
|
+
|
190
207
|
<xs:complexType name="plugin_healthmap">
|
191
208
|
<xs:all>
|
192
209
|
<xs:element name="name" type="xs:string"/>
|
data/lib/arachni/browser.rb
CHANGED
@@ -820,7 +820,7 @@ class Browser
|
|
820
820
|
c[:value] = Cookie.decode( c[:value].to_s )
|
821
821
|
c[:httponly] = !js_cookies.include?( original_name )
|
822
822
|
|
823
|
-
Cookie.new c.merge( url: @last_url )
|
823
|
+
Cookie.new c.merge( url: @last_url || url )
|
824
824
|
end
|
825
825
|
end
|
826
826
|
|
@@ -1007,8 +1007,10 @@ class Browser
|
|
1007
1007
|
print_debug 'Spawn timed-out.'
|
1008
1008
|
end
|
1009
1009
|
|
1010
|
-
|
1011
|
-
|
1010
|
+
if @process.io.stdout
|
1011
|
+
last_attempt_output = IO.read( @process.io.stdout )
|
1012
|
+
print_debug last_attempt_output
|
1013
|
+
end
|
1012
1014
|
|
1013
1015
|
if done
|
1014
1016
|
print_debug 'PhantomJS is ready.'
|
@@ -1046,12 +1048,13 @@ class Browser
|
|
1046
1048
|
end
|
1047
1049
|
|
1048
1050
|
@process = nil
|
1051
|
+
@watir = nil
|
1049
1052
|
@pid = nil
|
1050
1053
|
@browser_url = nil
|
1051
1054
|
end
|
1052
1055
|
|
1053
1056
|
def browser_alive?
|
1054
|
-
@process && @process.alive?
|
1057
|
+
@watir && @process && @process.alive?
|
1055
1058
|
rescue Errno::ECHILD
|
1056
1059
|
false
|
1057
1060
|
end
|