arachni 1.0.5 → 1.0.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (80) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +50 -0
  3. data/README.md +9 -2
  4. data/components/checks/active/code_injection.rb +5 -5
  5. data/components/checks/active/code_injection_timing.rb +3 -3
  6. data/components/checks/active/no_sql_injection_differential.rb +3 -2
  7. data/components/checks/active/os_cmd_injection.rb +11 -5
  8. data/components/checks/active/os_cmd_injection_timing.rb +11 -4
  9. data/components/checks/active/path_traversal.rb +2 -2
  10. data/components/checks/active/sql_injection.rb +1 -1
  11. data/components/checks/active/sql_injection/patterns/mssql +1 -0
  12. data/components/checks/active/sql_injection_differential.rb +3 -2
  13. data/components/checks/active/unvalidated_redirect.rb +3 -3
  14. data/components/checks/passive/common_directories/directories.txt +2 -0
  15. data/components/checks/passive/common_files/filenames.txt +1 -0
  16. data/lib/arachni/browser.rb +17 -1
  17. data/lib/arachni/check/auditor.rb +5 -2
  18. data/lib/arachni/check/base.rb +30 -5
  19. data/lib/arachni/element/capabilities/analyzable/differential.rb +2 -5
  20. data/lib/arachni/element/capabilities/auditable.rb +3 -1
  21. data/lib/arachni/element/capabilities/with_dom.rb +1 -0
  22. data/lib/arachni/element/capabilities/with_node.rb +1 -1
  23. data/lib/arachni/element/cookie.rb +2 -2
  24. data/lib/arachni/element/form.rb +1 -1
  25. data/lib/arachni/element/header.rb +2 -2
  26. data/lib/arachni/element/link_template.rb +1 -1
  27. data/lib/arachni/framework.rb +21 -1144
  28. data/lib/arachni/framework/parts/audit.rb +282 -0
  29. data/lib/arachni/framework/parts/browser.rb +132 -0
  30. data/lib/arachni/framework/parts/check.rb +86 -0
  31. data/lib/arachni/framework/parts/data.rb +158 -0
  32. data/lib/arachni/framework/parts/platform.rb +34 -0
  33. data/lib/arachni/framework/parts/plugin.rb +61 -0
  34. data/lib/arachni/framework/parts/report.rb +128 -0
  35. data/lib/arachni/framework/parts/scope.rb +40 -0
  36. data/lib/arachni/framework/parts/state.rb +457 -0
  37. data/lib/arachni/http/client.rb +33 -30
  38. data/lib/arachni/http/request.rb +6 -2
  39. data/lib/arachni/issue.rb +55 -1
  40. data/lib/arachni/platform/manager.rb +25 -21
  41. data/lib/arachni/state/framework.rb +7 -1
  42. data/lib/arachni/utilities.rb +10 -0
  43. data/lib/version +1 -1
  44. data/spec/arachni/browser_spec.rb +13 -0
  45. data/spec/arachni/check/auditor_spec.rb +1 -0
  46. data/spec/arachni/check/base_spec.rb +80 -0
  47. data/spec/arachni/element/cookie_spec.rb +2 -2
  48. data/spec/arachni/framework/parts/audit_spec.rb +391 -0
  49. data/spec/arachni/framework/parts/browser_spec.rb +26 -0
  50. data/spec/arachni/framework/parts/check_spec.rb +24 -0
  51. data/spec/arachni/framework/parts/data_spec.rb +187 -0
  52. data/spec/arachni/framework/parts/platform_spec.rb +62 -0
  53. data/spec/arachni/framework/parts/plugin_spec.rb +41 -0
  54. data/spec/arachni/framework/parts/report_spec.rb +66 -0
  55. data/spec/arachni/framework/parts/scope_spec.rb +86 -0
  56. data/spec/arachni/framework/parts/state_spec.rb +528 -0
  57. data/spec/arachni/framework_spec.rb +17 -1344
  58. data/spec/arachni/http/client_spec.rb +12 -7
  59. data/spec/arachni/issue_spec.rb +35 -0
  60. data/spec/arachni/platform/manager_spec.rb +2 -3
  61. data/spec/arachni/state/framework_spec.rb +15 -0
  62. data/spec/components/checks/active/code_injection_timing_spec.rb +5 -5
  63. data/spec/components/checks/active/no_sql_injection_differential_spec.rb +4 -0
  64. data/spec/components/checks/active/os_cmd_injection_spec.rb +20 -7
  65. data/spec/components/checks/active/os_cmd_injection_timing_spec.rb +5 -5
  66. data/spec/components/checks/active/sql_injection_differential_spec.rb +4 -0
  67. data/spec/components/checks/active/sql_injection_spec.rb +2 -3
  68. data/spec/support/servers/arachni/browser.rb +31 -0
  69. data/spec/support/servers/checks/active/code_injection.rb +1 -1
  70. data/spec/support/servers/checks/active/no_sql_injection_differential.rb +36 -34
  71. data/spec/support/servers/checks/active/os_cmd_injection.rb +6 -12
  72. data/spec/support/servers/checks/active/os_cmd_injection_timing.rb +9 -4
  73. data/spec/support/servers/checks/active/sql_injection.rb +1 -1
  74. data/spec/support/servers/checks/active/sql_injection_differential.rb +37 -34
  75. data/spec/support/shared/element/capabilities/with_node.rb +25 -0
  76. data/spec/support/shared/framework.rb +26 -0
  77. data/ui/cli/output.rb +2 -0
  78. data/ui/cli/rpc/server/dispatcher/option_parser.rb +1 -1
  79. metadata +32 -4
  80. data/components/checks/active/sql_injection/patterns/coldfusion +0 -1
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 113357f2e377065a79df18200488cc13fe744b6d
4
- data.tar.gz: 41739b4e27d567d15f67f9ed86c571c24143c3a0
3
+ metadata.gz: 9c9fcad54f45425bae6cfe2e13128890fe6bd259
4
+ data.tar.gz: 5909dde7398a67e4da8f94fdbb11f40e3fc6d9b7
5
5
  SHA512:
6
- metadata.gz: 0418e763e4b5577d1d6856cbbf3cdc242ba04525e10de155b4b86484e6208acdf78cea104e0ce9e75a44aab73fb8e7d40c10be0459d58811b8f7a83b6b4971e1
7
- data.tar.gz: 2ab357a8d52233a026374d1f0bd09ba03d8ebffa9d939d8a97f51bf76c638e95362c1f080b6ed78c54e630d1a13a0c4ddd90dbe61555259dddef762d3a3385e3
6
+ metadata.gz: 9adfdde00f808552c7f8387f5bf7bb766672fee202b757dc150eb92eb5f33326ab3fb680ada9b1cf024f26c1a9c503006d47238d5359f9eb9c2f42b27538f0bf
7
+ data.tar.gz: fe7f56f73a56ddc426095e9abd9b54315c0be4ba493fb2a1666521a38cb8a88a4e3992a810ff0b1d936a4d9e4d6a5f23f5c319333241686f8adca5789c891b89
@@ -1,5 +1,55 @@
1
1
  # ChangeLog
2
2
 
3
+ ## 1.0.6 _(December 07, 2014)_
4
+
5
+ - `arachni_rpcd` -- Fixed bug causing the `--nickname` option to not be understood.
6
+ - `UI::Output` -- Flush output stream after each message.
7
+ - `Platform::Manager`
8
+ - Removed 'coldfusion`.
9
+ - Added `sql` and `nosql` parents for DBs.
10
+ - `Check::Auditor#skip?` -- Ignore mutations when checking for redundancies.
11
+ - `Browser` -- Fixed issue causing `select` inputs in forms to not be set.
12
+ - `Element::Cookie.encode` -- Added '&' to the list of reserved characters.
13
+ - `Issue`
14
+ - `#recheck` -- Rechecks the existence of the issue.
15
+ - `Element::Capabilities`
16
+ - `WithNode`
17
+ - `#html=` -- Recode string before storing.
18
+ - `WithDOM`
19
+ - `#dom` -- Return `nil` on `Inputtable::Error`.
20
+ - `Auditable` -- Updated response analysis messages to include vector type,
21
+ name and action URL.
22
+ - `Framework` -- Split into `Parts`:
23
+ - `Audit`
24
+ - If `Options.platforms` are given, checks which don't support them are
25
+ completely skipped.
26
+ - `Browser`
27
+ - `Check`
28
+ - `Data`
29
+ - `#pop_page_from_url_queue` -- Fixed issue causing multiple-choice
30
+ redirections to cause an error.
31
+ - `Platform`
32
+ - `Plugin`
33
+ - `Report`
34
+ - `Scope`
35
+ - `State`
36
+ - `State::Framework`
37
+ - Added `#done?`
38
+ - `#abort` -- Fixed exception message.
39
+ - Checks
40
+ - Active
41
+ - `sql_injection` -- Slight payload update to catch double-quote cases.
42
+ - `code_injection` -- Slight PHP payload update, to ensure it works in more cases.
43
+ - `code_injection_timing` -- Updated payloads to mirror `code_injection`.
44
+ - `os_command_injection` -- Updated payloads to handle chained commands.
45
+ - `os_command_injection_timing` -- Updated payloads to handle chained commands.
46
+ - `path_traversal` -- Fixed MS Windows output pattern.
47
+ - `sql_injection_differential` -- Set platform to generic `sql`.
48
+ - `no_sql_injection_differential` -- Set platform to generic `nosql`.
49
+ - `unvalidated_redirect` -- Disable `follow_location`.
50
+ - Passive
51
+ - `common_files` -- Added `.svn/all-wcprops`.
52
+
3
53
  ## 1.0.5 _(November 14, 2014)_
4
54
 
5
55
  - Executables
data/README.md CHANGED
@@ -1,9 +1,17 @@
1
+ **NOTICE**:
2
+
3
+ * Arachni's license has changed, please see the _LICENSE_ file before working
4
+ with the project.
5
+ * v1.0 is not backwards compatible with v0.4.
6
+
7
+ <hr/>
8
+
1
9
  # Arachni - Web Application Security Scanner Framework
2
10
 
3
11
  <table>
4
12
  <tr>
5
13
  <th>Version</th>
6
- <td>1.0.5</td>
14
+ <td>1.0.6</td>
7
15
  </tr>
8
16
  <tr>
9
17
  <th>Homepage</th>
@@ -348,7 +356,6 @@ Active checks engage the web application via its inputs.
348
356
 
349
357
  - SQL injection (`sql_injection`) -- Error based detection.
350
358
  - Oracle
351
- - ColdFusion
352
359
  - InterBase
353
360
  - PostgreSQL
354
361
  - MySQL
@@ -12,7 +12,7 @@
12
12
  #
13
13
  # @author Tasos "Zapotek" Laskos <tasos.laskos@arachni-scanner.com>
14
14
  #
15
- # @version 0.2
15
+ # @version 0.2.1
16
16
  #
17
17
  # @see http://cwe.mitre.org/data/definitions/94.html
18
18
  # @see http://php.net/manual/en/function.eval.php
@@ -23,11 +23,11 @@
23
23
  class Arachni::Checks::CodeInjection < Arachni::Check::Base
24
24
 
25
25
  def self.rand1
26
- @rand1 ||= '287630581954'
26
+ @rand1 ||= '28763'
27
27
  end
28
28
 
29
29
  def self.rand2
30
- @rand2 ||= '4196403186331128'
30
+ @rand2 ||= '4196403'
31
31
  end
32
32
 
33
33
  def self.options
@@ -41,7 +41,7 @@ class Arachni::Checks::CodeInjection < Arachni::Check::Base
41
41
  def self.code_strings
42
42
  # code strings to be injected to the webapp
43
43
  @code_strings ||= {
44
- php: "echo #{rand1}+#{rand2};",
44
+ php: "print #{rand1}+#{rand2};",
45
45
  perl: "print #{rand1}+#{rand2};",
46
46
  python: "print #{rand1}+#{rand2}",
47
47
  asp: "Response.Write\x28#{rand1}+#{rand2}\x29"
@@ -72,7 +72,7 @@ Injects code snippets and assess whether or not execution was successful.
72
72
  elements: [ Element::Form, Element::Link, Element::Cookie,
73
73
  Element::Header, Element::LinkTemplate ],
74
74
  author: 'Tasos "Zapotek" Laskos <tasos.laskos@arachni-scanner.com>',
75
- version: '0.2',
75
+ version: '0.2.1',
76
76
  platforms: payloads.keys,
77
77
 
78
78
  issue: {
@@ -14,7 +14,7 @@
14
14
  #
15
15
  # @author Tasos "Zapotek" Laskos <tasos.laskos@arachni-scanner.com>
16
16
  #
17
- # @version 0.3
17
+ # @version 0.3.1
18
18
  #
19
19
  # @see http://cwe.mitre.org/data/definitions/94.html
20
20
  # @see http://php.net/manual/en/function.eval.php
@@ -35,7 +35,7 @@ class Arachni::Checks::CodeInjectionTiming < Arachni::Check::Base
35
35
  jsp: 'Thread.sleep(__TIME__);',
36
36
  asp: 'Thread.Sleep(__TIME__);',
37
37
  }.inject({}) do |h, (platform, payload)|
38
- h[platform] = [ ' ', ' && ', ';' ].map { |sep| "#{sep} #{payload}" }
38
+ h[platform] = [ ' %s', ';%s', "\";%s#", "';%s#" ].map { |s| s % payload }
39
39
  h
40
40
  end
41
41
  end
@@ -54,7 +54,7 @@ a time delay.
54
54
  elements: [ Element::Form, Element::Link, Element::Cookie,
55
55
  Element::Header, Element::LinkTemplate ],
56
56
  author: 'Tasos "Zapotek" Laskos <tasos.laskos@arachni-scanner.com>',
57
- version: '0.3',
57
+ version: '0.3.1',
58
58
  platforms: payloads.keys,
59
59
 
60
60
  issue: {
@@ -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::NoSqlInjectionDifferential < Arachni::Check::Base
12
12
 
13
13
  def self.options
@@ -40,7 +40,8 @@ that of a vulnerable application.
40
40
  },
41
41
  elements: [ Element::Link, Element::Form, Element::Cookie ],
42
42
  author: 'Tasos "Zapotek" Laskos <tasos.laskos@arachni-scanner.com>',
43
- version: '0.1',
43
+ version: '0.1.1',
44
+ platforms: [ :nosql ],
44
45
 
45
46
  issue: {
46
47
  name: %q{Blind NoSQL Injection (differential analysis)},
@@ -10,7 +10,7 @@
10
10
  #
11
11
  # @author Tasos "Zapotek" Laskos <tasos.laskos@arachni-scanner.com>
12
12
  #
13
- # @version 0.2.1
13
+ # @version 0.2.2
14
14
  #
15
15
  # @see http://cwe.mitre.org/data/definitions/78.html
16
16
  # @see http://www.owasp.org/index.php/OS_Command_Injection
@@ -23,8 +23,8 @@ class Arachni::Checks::OsCmdInjection < Arachni::Check::Base
23
23
  /(root|mail):.+:\d+:\d+:.+:[0-9a-zA-Z\/]+/im
24
24
  ],
25
25
  windows: [
26
- /\[boot loader\](.*)\[operating systems\]/im,
27
- /\[fonts\](.*)\[extensions\]/im
26
+ /\[boot loader\].*\[operating systems\]/im,
27
+ /\[fonts\].*\[extensions\]/im
28
28
  ]
29
29
  },
30
30
  format: [ Format::STRAIGHT, Format::APPEND ]
@@ -49,7 +49,13 @@ class Arachni::Checks::OsCmdInjection < Arachni::Check::Base
49
49
  }.inject({}) do |h, (platform, payloads)|
50
50
  h[platform] ||= []
51
51
  payloads.each do |payload|
52
- h[platform] |= [ '', '&&', '|', ';' ].map { |sep| "#{sep} #{payload}" }
52
+ h[platform] << "#{payload}"
53
+
54
+ ['', '\'', '"'].each do |q|
55
+ h[platform] |= [ '&&', '|', ';' ].
56
+ map { |sep| "#{q} #{sep} #{payload} #{sep} #{q}" }
57
+ end
58
+
53
59
  h[platform] << "` #{payload}`"
54
60
  end
55
61
  h
@@ -69,7 +75,7 @@ Tries to find Operating System command injections.
69
75
  elements: [ Element::Form, Element::Link, Element::Cookie,
70
76
  Element::Header, Element::LinkTemplate ],
71
77
  author: 'Tasos "Zapotek" Laskos <tasos.laskos@arachni-scanner.com> ',
72
- version: '0.2.1',
78
+ version: '0.2.2',
73
79
  platforms: payloads.keys,
74
80
 
75
81
  issue: {
@@ -10,7 +10,7 @@
10
10
  #
11
11
  # @author Tasos "Zapotek" Laskos <tasos.laskos@arachni-scanner.com>
12
12
  #
13
- # @version 0.3
13
+ # @version 0.3.1
14
14
  #
15
15
  # @see http://cwe.mitre.org/data/definitions/78.html
16
16
  # @see http://www.owasp.org/index.php/OS_Command_Injection
@@ -23,8 +23,15 @@ class Arachni::Checks::OsCmdInjectionTiming < Arachni::Check::Base
23
23
  unix: 'sleep __TIME__',
24
24
  windows: 'ping -n __TIME__ localhost'
25
25
  }.inject({}) do |h, (platform, payload)|
26
- h[platform] = [ '', '&', '&&', '|', ';' ].map { |sep| "#{sep} #{payload}" }
27
- h[platform] << "`#{payload}`"
26
+ h[platform] ||= []
27
+ h[platform] << "#{payload}"
28
+
29
+ ['', '\'', '"'].each do |q|
30
+ h[platform] |= [ '&', '&&', '|', ';' ].
31
+ map { |sep| "#{q} #{sep} #{payload} #{sep} #{q}" }
32
+ end
33
+
34
+ h[platform] << "` #{payload}`"
28
35
  h
29
36
  end
30
37
  end
@@ -46,7 +53,7 @@ Tries to find operating system command injections using timing attacks.
46
53
  elements: [ Element::Form, Element::Link, Element::Cookie,
47
54
  Element::Header, Element::LinkTemplate ],
48
55
  author: 'Tasos "Zapotek" Laskos <tasos.laskos@arachni-scanner.com> ',
49
- version: '0.3',
56
+ version: '0.3.1',
50
57
  platforms: payloads.keys,
51
58
 
52
59
  issue: {
@@ -28,8 +28,8 @@ class Arachni::Checks::PathTraversal < Arachni::Check::Base
28
28
  /(root|mail):.+:\d+:\d+:.+:[0-9a-zA-Z\/]+/im
29
29
  ],
30
30
  windows: [
31
- /\[boot loader\](.*)\[operating systems\]/im,
32
- /\[fonts\](.*)\[extensions\]/im
31
+ /\[boot loader\].*\[operating systems\]/im,
32
+ /\[fonts\].*\[extensions\]/im
33
33
  ],
34
34
  tomcat: [
35
35
  /<web\-app/im
@@ -40,7 +40,7 @@ class Arachni::Checks::SqlInjection < Arachni::Check::Base
40
40
  # Prepares the payloads that will hopefully cause the webapp to output SQL
41
41
  # error messages if included as part of an SQL query.
42
42
  def self.payloads
43
- @payloads ||= [ '\'`--', ')' ]
43
+ @payloads ||= [ '"\'`--', ')' ]
44
44
  end
45
45
 
46
46
  def self.options
@@ -1,5 +1,6 @@
1
1
  System\.Data\.OleDb\.OleDbException
2
2
  \[Microsoft\]\[ODBC SQL Server Driver\]
3
+ \[Macromedia\]\[SQLServer JDBC Driver\]
3
4
  \[SqlException
4
5
  System\.Data\.SqlClient\.SqlException
5
6
  Unclosed quotation mark after the character string
@@ -14,7 +14,7 @@
14
14
  #
15
15
  # @author Tasos "Zapotek" Laskos <tasos.laskos@arachni-scanner.com>
16
16
  #
17
- # @version 0.4.2
17
+ # @version 0.4.3
18
18
  #
19
19
  # @see http://cwe.mitre.org/data/definitions/89.html
20
20
  # @see http://capec.mitre.org/data/definitions/7.html
@@ -59,7 +59,8 @@ that of a vulnerable application.
59
59
  },
60
60
  elements: [ Element::Link, Element::Form, Element::Cookie ],
61
61
  author: 'Tasos "Zapotek" Laskos <tasos.laskos@arachni-scanner.com>',
62
- version: '0.4.2',
62
+ version: '0.4.3',
63
+ platforms: [ :sql ],
63
64
 
64
65
  issue: {
65
66
  name: %q{Blind SQL Injection (differential analysis)},
@@ -12,7 +12,7 @@
12
12
  # header field to determine whether the attack was successful.
13
13
  #
14
14
  # @author Tasos "Zapotek" Laskos <tasos.laskos@arachni-scanner.com>
15
- # @version 0.2
15
+ # @version 0.2.1
16
16
  # @see http://www.owasp.org/index.php/Top_10_2010-A10-Unvalidated_Redirects_and_Forwards
17
17
  class Arachni::Checks::UnvalidatedRedirect < Arachni::Check::Base
18
18
 
@@ -32,7 +32,7 @@ class Arachni::Checks::UnvalidatedRedirect < Arachni::Check::Base
32
32
  end
33
33
 
34
34
  def run
35
- audit( self.class.payloads ) do |response, element|
35
+ audit( self.class.payloads, submit: { follow_location: false } ) do |response, element|
36
36
  # If this was a sample/default value submission ignore it, we only
37
37
  # care about our payloads.
38
38
  next if !payload? element.seed
@@ -67,7 +67,7 @@ URL to determine whether the attack was successful.
67
67
  },
68
68
  elements: [Element::Form, Element::Link, Element::Cookie, Element::Header],
69
69
  author: 'Tasos "Zapotek" Laskos <tasos.laskos@arachni-scanner.com>',
70
- version: '0.2',
70
+ version: '0.2.1',
71
71
 
72
72
  issue: {
73
73
  name: %q{Unvalidated redirect},
@@ -6,6 +6,7 @@ CVS/Repository
6
6
  CVS/Root
7
7
  CVS/Entries
8
8
  .svn/wc.db
9
+ .svn/all-wcprops
9
10
  .git/HEAD
10
11
  _mmServerScripts/MMHTTPDB.php
11
12
  _mmServerScripts/MMHTTPDB.asp
@@ -896,7 +896,7 @@ class Browser
896
896
 
897
897
  begin
898
898
  input.set( value.to_s )
899
- # Disabled inputs and such...
899
+ # Disabled inputs and such...
900
900
  rescue Watir::Exception::ObjectDisabledException,
901
901
  Watir::Exception::ObjectReadOnlyException,
902
902
  Selenium::WebDriver::Error::InvalidElementStateError => e
@@ -904,6 +904,22 @@ class Browser
904
904
  " because: #{e} [#{e.class}"
905
905
  end
906
906
  end
907
+
908
+ form.selects.each do |input|
909
+ name_or_id = name_or_id_for( input )
910
+ value = inputs ? inputs[name_or_id] : value_for( input )
911
+
912
+ begin
913
+ input.select_value( value.to_s )
914
+ # Disabled inputs and such...
915
+ rescue Watir::Exception::ObjectDisabledException,
916
+ Watir::Exception::ObjectReadOnlyException,
917
+ Watir::Exception::NoValueFoundException,
918
+ Selenium::WebDriver::Error::InvalidElementStateError => e
919
+ print_debug_level_2 "Could not fill in form select '#{name_or_id}'" <<
920
+ " because: #{e} [#{e.class}"
921
+ end
922
+ end
907
923
  end
908
924
 
909
925
  def skip_state?( state )
@@ -380,8 +380,11 @@ module Auditor
380
380
  #
381
381
  # @see Page#audit?
382
382
  def skip?( element )
383
- return true if audited?( element.coverage_id ) ||
384
- !page.audit_element?( element )
383
+ # This method also gets called from Auditable#audit to check mutations,
384
+ # don't touch these, we're filtering at a higher level here, otherwise
385
+ # we might mess up the audit.
386
+ return true if !element.mutation? && audited?( element.coverage_id )
387
+ return true if !page.audit_element?( element )
385
388
 
386
389
  # Don't audit elements which have been already logged as vulnerable
387
390
  # either by us or preferred checks.
@@ -138,27 +138,47 @@ class Base < Component::Base
138
138
  # @return [Bool]
139
139
  # `true` if the check can benefit from knowing the platform beforehand,
140
140
  # `false` otherwise.
141
+ #
141
142
  # @see .platforms
142
143
  def has_platforms?
143
144
  platforms.any?
144
145
  end
145
146
 
146
- # @return [Array<Symbol>] Targeted platforms.
147
+ # @return [Array<Symbol>]
148
+ # Targeted platforms.
149
+ #
147
150
  # @see .info
148
151
  def platforms
149
- [info[:platforms]].flatten.compact
152
+ @platforms ||= [info[:platforms]].flatten.compact
153
+ end
154
+
155
+ # @param [Array<Symbol, String>] platforms
156
+ # List of platforms to check for support.
157
+ #
158
+ # @return [Boolean]
159
+ # `true` if any of the given platforms are supported, `false` otherwise.
160
+ def supports_platforms?( platforms )
161
+ return true if platforms.empty? || !has_platforms?
162
+
163
+ # Determine if we've got anything for the given platforms, the same
164
+ # way payloads are picked.
165
+ foo_data = self.platforms.inject({}) { |h, platform| h.merge!( platform => true ) }
166
+ Platform::Manager.new( platforms ).pick( foo_data ).any?
150
167
  end
151
168
 
152
- # @return [Array<Symbol>] Targeted element types.
169
+ # @return [Array<Symbol>]
170
+ # Targeted element types.
171
+ #
153
172
  # @see .info
154
173
  def elements
155
- [info[:elements]].flatten.compact
174
+ @elements ||= [info[:elements]].flatten.compact
156
175
  end
157
176
 
158
177
  # Schedules self to be run *after* the specified checks and prevents
159
178
  # auditing elements that have been previously logged by any of these checks.
160
179
  #
161
- # @return [Array] Check names.
180
+ # @return [Array]
181
+ # Check names.
162
182
  def prefer( *args )
163
183
  @preferred = args.flatten.compact
164
184
  end
@@ -170,6 +190,11 @@ class Base < Component::Base
170
190
  def preferred
171
191
  @preferred ||= []
172
192
  end
193
+
194
+ # @private
195
+ def clear_info_cache
196
+ @elements = @platforms = nil
197
+ end
173
198
  end
174
199
 
175
200
  end