metasploit-model 0.28.0-java → 0.29.0-java
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.
- checksums.yaml +4 -4
- data/.travis.yml +3 -1
- data/config/locales/en.yml +0 -171
- data/lib/metasploit/model.rb +0 -2
- data/lib/metasploit/model/spec.rb +0 -130
- data/lib/metasploit/model/version.rb +1 -1
- data/spec/support/shared/examples/metasploit/model/search/operator/help.rb +4 -6
- data/spec/support/shared/examples/metasploit/model/translation.rb +1 -1
- data/spec/support/shared/examples/search_association.rb +1 -1
- data/spec/support/shared/examples/search_with.rb +4 -2
- metadata +2 -348
- data/app/models/metasploit/model/module/ancestor/spec/template.rb +0 -125
- data/app/models/metasploit/model/module/class/spec/template.rb +0 -92
- data/app/models/metasploit/model/module/instance/spec/template.rb +0 -97
- data/app/models/metasploit/model/spec/template.rb +0 -277
- data/app/validators/derivation_validator.rb +0 -17
- data/app/validators/dynamic_length_validator.rb +0 -45
- data/lib/metasploit/model/architecture.rb +0 -342
- data/lib/metasploit/model/author.rb +0 -54
- data/lib/metasploit/model/authority.rb +0 -141
- data/lib/metasploit/model/authority/bid.rb +0 -10
- data/lib/metasploit/model/authority/cve.rb +0 -10
- data/lib/metasploit/model/authority/msb.rb +0 -10
- data/lib/metasploit/model/authority/osvdb.rb +0 -10
- data/lib/metasploit/model/authority/pmasa.rb +0 -10
- data/lib/metasploit/model/authority/secunia.rb +0 -10
- data/lib/metasploit/model/authority/us_cert_vu.rb +0 -10
- data/lib/metasploit/model/authority/waraxe.rb +0 -31
- data/lib/metasploit/model/authority/zdi.rb +0 -10
- data/lib/metasploit/model/derivation.rb +0 -109
- data/lib/metasploit/model/derivation/full_name.rb +0 -19
- data/lib/metasploit/model/email_address.rb +0 -124
- data/lib/metasploit/model/module.rb +0 -20
- data/lib/metasploit/model/module/action.rb +0 -52
- data/lib/metasploit/model/module/ancestor.rb +0 -491
- data/lib/metasploit/model/module/ancestor/spec.rb +0 -6
- data/lib/metasploit/model/module/architecture.rb +0 -34
- data/lib/metasploit/model/module/author.rb +0 -40
- data/lib/metasploit/model/module/class.rb +0 -403
- data/lib/metasploit/model/module/class/spec.rb +0 -6
- data/lib/metasploit/model/module/handler.rb +0 -29
- data/lib/metasploit/model/module/instance.rb +0 -622
- data/lib/metasploit/model/module/instance/spec.rb +0 -6
- data/lib/metasploit/model/module/path.rb +0 -159
- data/lib/metasploit/model/module/platform.rb +0 -32
- data/lib/metasploit/model/module/rank.rb +0 -85
- data/lib/metasploit/model/module/reference.rb +0 -32
- data/lib/metasploit/model/module/stance.rb +0 -16
- data/lib/metasploit/model/module/target.rb +0 -78
- data/lib/metasploit/model/module/target/architecture.rb +0 -34
- data/lib/metasploit/model/module/target/platform.rb +0 -34
- data/lib/metasploit/model/module/type.rb +0 -38
- data/lib/metasploit/model/platform.rb +0 -246
- data/lib/metasploit/model/real_pathname.rb +0 -16
- data/lib/metasploit/model/reference.rb +0 -104
- data/spec/app/models/metasploit/model/module/ancestor/spec/template_spec.rb +0 -174
- data/spec/app/models/metasploit/model/module/class/spec/template_spec.rb +0 -222
- data/spec/app/models/metasploit/model/module/instance/spec/template_spec.rb +0 -201
- data/spec/app/models/metasploit/model/search/operator/deprecated/app_spec.rb +0 -62
- data/spec/app/models/metasploit/model/search/operator/deprecated/author_spec.rb +0 -197
- data/spec/app/models/metasploit/model/search/operator/deprecated/authority_spec.rb +0 -135
- data/spec/app/models/metasploit/model/search/operator/deprecated/platform_spec.rb +0 -124
- data/spec/app/models/metasploit/model/search/operator/deprecated/ref_spec.rb +0 -387
- data/spec/app/models/metasploit/model/search/operator/deprecated/text_spec.rb +0 -399
- data/spec/app/models/metasploit/model/spec/template_spec.rb +0 -23
- data/spec/app/validators/derivation_validator_spec.rb +0 -92
- data/spec/app/validators/dynamic_length_validator_spec.rb +0 -293
- data/spec/dummy/app/models/dummy/architecture.rb +0 -92
- data/spec/dummy/app/models/dummy/author.rb +0 -15
- data/spec/dummy/app/models/dummy/authority.rb +0 -130
- data/spec/dummy/app/models/dummy/email_address.rb +0 -27
- data/spec/dummy/app/models/dummy/module/action.rb +0 -25
- data/spec/dummy/app/models/dummy/module/ancestor.rb +0 -67
- data/spec/dummy/app/models/dummy/module/architecture.rb +0 -19
- data/spec/dummy/app/models/dummy/module/author.rb +0 -28
- data/spec/dummy/app/models/dummy/module/class.rb +0 -69
- data/spec/dummy/app/models/dummy/module/instance.rb +0 -209
- data/spec/dummy/app/models/dummy/module/path.rb +0 -64
- data/spec/dummy/app/models/dummy/module/platform.rb +0 -19
- data/spec/dummy/app/models/dummy/module/rank.rb +0 -44
- data/spec/dummy/app/models/dummy/module/reference.rb +0 -19
- data/spec/dummy/app/models/dummy/module/target.rb +0 -62
- data/spec/dummy/app/models/dummy/module/target/architecture.rb +0 -19
- data/spec/dummy/app/models/dummy/module/target/platform.rb +0 -19
- data/spec/dummy/app/models/dummy/platform.rb +0 -58
- data/spec/dummy/app/models/dummy/reference.rb +0 -31
- data/spec/dummy/spec/factories/dummy/architectures.rb +0 -13
- data/spec/dummy/spec/factories/dummy/authorities.rb +0 -32
- data/spec/dummy/spec/factories/dummy/authors.rb +0 -8
- data/spec/dummy/spec/factories/dummy/email_addresses.rb +0 -8
- data/spec/dummy/spec/factories/dummy/module/actions.rb +0 -13
- data/spec/dummy/spec/factories/dummy/module/ancestors.rb +0 -45
- data/spec/dummy/spec/factories/dummy/module/architectures.rb +0 -35
- data/spec/dummy/spec/factories/dummy/module/authors.rb +0 -10
- data/spec/dummy/spec/factories/dummy/module/classes.rb +0 -40
- data/spec/dummy/spec/factories/dummy/module/instances.rb +0 -86
- data/spec/dummy/spec/factories/dummy/module/paths.rb +0 -16
- data/spec/dummy/spec/factories/dummy/module/platforms.rb +0 -35
- data/spec/dummy/spec/factories/dummy/module/ranks.rb +0 -14
- data/spec/dummy/spec/factories/dummy/module/references.rb +0 -17
- data/spec/dummy/spec/factories/dummy/module/target/architectures.rb +0 -40
- data/spec/dummy/spec/factories/dummy/module/target/platforms.rb +0 -41
- data/spec/dummy/spec/factories/dummy/module/targets.rb +0 -46
- data/spec/dummy/spec/factories/dummy/platforms.rb +0 -3
- data/spec/dummy/spec/factories/dummy/references.rb +0 -26
- data/spec/factories/metasploit/model/architectures.rb +0 -6
- data/spec/factories/metasploit/model/authorities.rb +0 -28
- data/spec/factories/metasploit/model/authors.rb +0 -9
- data/spec/factories/metasploit/model/email_addresses.rb +0 -14
- data/spec/factories/metasploit/model/module/actions.rb +0 -9
- data/spec/factories/metasploit/model/module/ancestors.rb +0 -180
- data/spec/factories/metasploit/model/module/architectures.rb +0 -15
- data/spec/factories/metasploit/model/module/classes.rb +0 -47
- data/spec/factories/metasploit/model/module/handlers.rb +0 -3
- data/spec/factories/metasploit/model/module/instances.rb +0 -83
- data/spec/factories/metasploit/model/module/paths.rb +0 -51
- data/spec/factories/metasploit/model/module/platforms.rb +0 -15
- data/spec/factories/metasploit/model/module/ranks.rb +0 -9
- data/spec/factories/metasploit/model/module/references.rb +0 -11
- data/spec/factories/metasploit/model/module/stances.rb +0 -3
- data/spec/factories/metasploit/model/module/targets.rb +0 -22
- data/spec/factories/metasploit/model/module/types.rb +0 -5
- data/spec/factories/metasploit/model/references.rb +0 -88
- data/spec/lib/metasploit/model/architecture_spec.rb +0 -52
- data/spec/lib/metasploit/model/author_spec.rb +0 -6
- data/spec/lib/metasploit/model/authority/bid_spec.rb +0 -17
- data/spec/lib/metasploit/model/authority/cve_spec.rb +0 -17
- data/spec/lib/metasploit/model/authority/msb_spec.rb +0 -17
- data/spec/lib/metasploit/model/authority/osvdb_spec.rb +0 -17
- data/spec/lib/metasploit/model/authority/pmasa_spec.rb +0 -17
- data/spec/lib/metasploit/model/authority/secunia_spec.rb +0 -17
- data/spec/lib/metasploit/model/authority/us_cert_vu_spec.rb +0 -17
- data/spec/lib/metasploit/model/authority/waraxe_spec.rb +0 -51
- data/spec/lib/metasploit/model/authority/zdi_spec.rb +0 -21
- data/spec/lib/metasploit/model/authority_spec.rb +0 -10
- data/spec/lib/metasploit/model/derivation/full_name_spec.rb +0 -75
- data/spec/lib/metasploit/model/derivation_spec.rb +0 -217
- data/spec/lib/metasploit/model/email_address_spec.rb +0 -19
- data/spec/lib/metasploit/model/module/action_spec.rb +0 -6
- data/spec/lib/metasploit/model/module/ancestor_spec.rb +0 -6
- data/spec/lib/metasploit/model/module/architecture_spec.rb +0 -9
- data/spec/lib/metasploit/model/module/author_spec.rb +0 -6
- data/spec/lib/metasploit/model/module/class_spec.rb +0 -19
- data/spec/lib/metasploit/model/module/handler_spec.rb +0 -61
- data/spec/lib/metasploit/model/module/instance_spec.rb +0 -183
- data/spec/lib/metasploit/model/module/path_spec.rb +0 -6
- data/spec/lib/metasploit/model/module/platform_spec.rb +0 -9
- data/spec/lib/metasploit/model/module/rank_spec.rb +0 -29
- data/spec/lib/metasploit/model/module/reference_spec.rb +0 -9
- data/spec/lib/metasploit/model/module/stance_spec.rb +0 -30
- data/spec/lib/metasploit/model/module/target/architecture_spec.rb +0 -9
- data/spec/lib/metasploit/model/module/target/platform_spec.rb +0 -9
- data/spec/lib/metasploit/model/module/target_spec.rb +0 -9
- data/spec/lib/metasploit/model/module/type_spec.rb +0 -125
- data/spec/lib/metasploit/model/platform_spec.rb +0 -100
- data/spec/lib/metasploit/model/reference_spec.rb +0 -21
- data/spec/support/shared/contexts/metasploit/model/module/ancestor/contents/metasploit_module.rb +0 -12
- data/spec/support/shared/contexts/metasploit/model/module/ancestor/factory/contents.rb +0 -10
- data/spec/support/shared/contexts/metasploit/model/module/ancestor/factory/contents/metasploit_module.rb +0 -15
- data/spec/support/shared/examples/derives.rb +0 -115
- data/spec/support/shared/examples/metasploit/model/architecture.rb +0 -425
- data/spec/support/shared/examples/metasploit/model/architecture/seed.rb +0 -25
- data/spec/support/shared/examples/metasploit/model/author.rb +0 -27
- data/spec/support/shared/examples/metasploit/model/authority.rb +0 -124
- data/spec/support/shared/examples/metasploit/model/authority/seed.rb +0 -49
- data/spec/support/shared/examples/metasploit/model/email_address.rb +0 -220
- data/spec/support/shared/examples/metasploit/model/module/action.rb +0 -26
- data/spec/support/shared/examples/metasploit/model/module/ancestor.rb +0 -2004
- data/spec/support/shared/examples/metasploit/model/module/ancestor/payload_factory.rb +0 -63
- data/spec/support/shared/examples/metasploit/model/module/architecture.rb +0 -36
- data/spec/support/shared/examples/metasploit/model/module/author.rb +0 -29
- data/spec/support/shared/examples/metasploit/model/module/class.rb +0 -1514
- data/spec/support/shared/examples/metasploit/model/module/handler.rb +0 -21
- data/spec/support/shared/examples/metasploit/model/module/instance.rb +0 -1384
- data/spec/support/shared/examples/metasploit/model/module/instance/class_methods.rb +0 -537
- data/spec/support/shared/examples/metasploit/model/module/instance/is_not_stanced_with_module_type.rb +0 -22
- data/spec/support/shared/examples/metasploit/model/module/instance/is_stanced_with_module_type.rb +0 -38
- data/spec/support/shared/examples/metasploit/model/module/instance/validates/dynamic_length_of.rb +0 -131
- data/spec/support/shared/examples/metasploit/model/module/path.rb +0 -493
- data/spec/support/shared/examples/metasploit/model/module/platform.rb +0 -36
- data/spec/support/shared/examples/metasploit/model/module/rank.rb +0 -92
- data/spec/support/shared/examples/metasploit/model/module/reference.rb +0 -16
- data/spec/support/shared/examples/metasploit/model/module/target.rb +0 -29
- data/spec/support/shared/examples/metasploit/model/module/target/architecture.rb +0 -56
- data/spec/support/shared/examples/metasploit/model/module/target/platform.rb +0 -56
- data/spec/support/shared/examples/metasploit/model/platform.rb +0 -213
- data/spec/support/shared/examples/metasploit/model/real_pathname.rb +0 -29
- data/spec/support/shared/examples/metasploit/model/reference.rb +0 -410
- data/spec/support/shared/examples/search/query/metasploit/model/search/operator/deprecated/app.rb +0 -44
- data/spec/support/shared/examples/search/query/metasploit/model/search/operator/deprecated/authority.rb +0 -72
- data/spec/support/templates/metasploit/model/module/ancestors/_attributes.rb.erb +0 -9
- data/spec/support/templates/metasploit/model/module/ancestors/_methods.rb.erb +0 -8
- data/spec/support/templates/metasploit/model/module/ancestors/_validations.rb.erb +0 -6
- data/spec/support/templates/metasploit/model/module/ancestors/base.rb.erb +0 -3
- data/spec/support/templates/metasploit/model/module/ancestors/module_types/_auxiliary.rb.erb +0 -1
- data/spec/support/templates/metasploit/model/module/ancestors/module_types/_encoder.rb.erb +0 -1
- data/spec/support/templates/metasploit/model/module/ancestors/module_types/_exploit.rb.erb +0 -1
- data/spec/support/templates/metasploit/model/module/ancestors/module_types/_non_payload.rb.erb +0 -5
- data/spec/support/templates/metasploit/model/module/ancestors/module_types/_nop.rb.erb +0 -1
- data/spec/support/templates/metasploit/model/module/ancestors/module_types/_payload.rb.erb +0 -4
- data/spec/support/templates/metasploit/model/module/ancestors/module_types/_post.rb.erb +0 -1
- data/spec/support/templates/metasploit/model/module/ancestors/payload_types/_handled.rb.erb +0 -20
- data/spec/support/templates/metasploit/model/module/ancestors/payload_types/_single.rb.erb +0 -1
- data/spec/support/templates/metasploit/model/module/ancestors/payload_types/_stage.rb.erb +0 -0
- data/spec/support/templates/metasploit/model/module/ancestors/payload_types/_stager.rb.erb +0 -1
- data/spec/support/templates/metasploit/model/module/classes/_methods.rb.erb +0 -9
|
@@ -1,29 +0,0 @@
|
|
|
1
|
-
# The handler Module for a {Metasploit::Model::Module::Ancestor#handled?} {Metasploit::Model::Module::Ancestor}.
|
|
2
|
-
module Metasploit::Model::Module::Handler
|
|
3
|
-
#
|
|
4
|
-
# CONSTANTS
|
|
5
|
-
#
|
|
6
|
-
|
|
7
|
-
# Maps {TYPES} to {GENERAL_TYPES} used as connection types for `Msf::Payload`.
|
|
8
|
-
GENERAL_TYPE_BY_TYPE = {
|
|
9
|
-
'bind_tcp' => 'bind',
|
|
10
|
-
'find_port' => 'find',
|
|
11
|
-
'find_shell' => 'find',
|
|
12
|
-
'find_tag' => 'find',
|
|
13
|
-
'none' => 'none',
|
|
14
|
-
'reverse_http' => 'tunnel',
|
|
15
|
-
'reverse_https' => 'tunnel',
|
|
16
|
-
'reverse_https_proxy' => 'tunnel',
|
|
17
|
-
'reverse_ipv6_http' => 'tunnel',
|
|
18
|
-
'reverse_ipv6_https' => 'tunnel',
|
|
19
|
-
'reverse_tcp' => 'reverse',
|
|
20
|
-
'reverse_tcp_allports' => 'reverse',
|
|
21
|
-
'reverse_tcp_double' => 'reverse',
|
|
22
|
-
'reverse_tcp_double_ssl' => 'reverse',
|
|
23
|
-
'reverse_tcp_ssl' => 'reverse'
|
|
24
|
-
}
|
|
25
|
-
# General handler types that are used as connection types for Msf::Payloads.
|
|
26
|
-
GENERAL_TYPES = GENERAL_TYPE_BY_TYPE.values.uniq.sort
|
|
27
|
-
# Types of handlers
|
|
28
|
-
TYPES = GENERAL_TYPE_BY_TYPE.keys.sort
|
|
29
|
-
end
|
|
@@ -1,622 +0,0 @@
|
|
|
1
|
-
# Code shared between `Mdm::Module::Instance` and `Metasploit::Framework::Module::Instance`.
|
|
2
|
-
module Metasploit::Model::Module::Instance
|
|
3
|
-
extend ActiveModel::Naming
|
|
4
|
-
extend ActiveSupport::Autoload
|
|
5
|
-
extend ActiveSupport::Concern
|
|
6
|
-
|
|
7
|
-
include Metasploit::Model::Translation
|
|
8
|
-
|
|
9
|
-
autoload :Spec
|
|
10
|
-
|
|
11
|
-
#
|
|
12
|
-
# CONSTANTS
|
|
13
|
-
#
|
|
14
|
-
|
|
15
|
-
# {#dynamic_length_validation_options} by {#module_type} by attribute.
|
|
16
|
-
DYNAMIC_LENGTH_VALIDATION_OPTIONS_BY_MODULE_TYPE_BY_ATTRIBUTE = {
|
|
17
|
-
actions: {
|
|
18
|
-
Metasploit::Model::Module::Type::AUX => {
|
|
19
|
-
minimum: 0
|
|
20
|
-
},
|
|
21
|
-
Metasploit::Model::Module::Type::ENCODER => {
|
|
22
|
-
is: 0
|
|
23
|
-
},
|
|
24
|
-
Metasploit::Model::Module::Type::EXPLOIT => {
|
|
25
|
-
is: 0
|
|
26
|
-
},
|
|
27
|
-
Metasploit::Model::Module::Type::NOP => {
|
|
28
|
-
is: 0
|
|
29
|
-
},
|
|
30
|
-
Metasploit::Model::Module::Type::PAYLOAD => {
|
|
31
|
-
is: 0
|
|
32
|
-
},
|
|
33
|
-
Metasploit::Model::Module::Type::POST => {
|
|
34
|
-
minimum: 0
|
|
35
|
-
}
|
|
36
|
-
},
|
|
37
|
-
module_architectures: {
|
|
38
|
-
Metasploit::Model::Module::Type::AUX => {
|
|
39
|
-
is: 0
|
|
40
|
-
},
|
|
41
|
-
Metasploit::Model::Module::Type::ENCODER => {
|
|
42
|
-
minimum: 1
|
|
43
|
-
},
|
|
44
|
-
Metasploit::Model::Module::Type::EXPLOIT => {
|
|
45
|
-
minimum: 1
|
|
46
|
-
},
|
|
47
|
-
Metasploit::Model::Module::Type::NOP => {
|
|
48
|
-
minimum: 1
|
|
49
|
-
},
|
|
50
|
-
Metasploit::Model::Module::Type::PAYLOAD => {
|
|
51
|
-
minimum: 1
|
|
52
|
-
},
|
|
53
|
-
Metasploit::Model::Module::Type::POST => {
|
|
54
|
-
minimum: 1
|
|
55
|
-
}
|
|
56
|
-
},
|
|
57
|
-
module_platforms: {
|
|
58
|
-
Metasploit::Model::Module::Type::AUX => {
|
|
59
|
-
is: 0
|
|
60
|
-
},
|
|
61
|
-
Metasploit::Model::Module::Type::ENCODER => {
|
|
62
|
-
is: 0
|
|
63
|
-
},
|
|
64
|
-
Metasploit::Model::Module::Type::EXPLOIT => {
|
|
65
|
-
minimum: 1
|
|
66
|
-
},
|
|
67
|
-
Metasploit::Model::Module::Type::NOP => {
|
|
68
|
-
is: 0
|
|
69
|
-
},
|
|
70
|
-
Metasploit::Model::Module::Type::PAYLOAD => {
|
|
71
|
-
minimum: 1
|
|
72
|
-
},
|
|
73
|
-
Metasploit::Model::Module::Type::POST => {
|
|
74
|
-
minimum: 1
|
|
75
|
-
}
|
|
76
|
-
},
|
|
77
|
-
module_references: {
|
|
78
|
-
Metasploit::Model::Module::Type::AUX => {
|
|
79
|
-
minimum: 0
|
|
80
|
-
},
|
|
81
|
-
Metasploit::Model::Module::Type::ENCODER => {
|
|
82
|
-
is: 0
|
|
83
|
-
},
|
|
84
|
-
Metasploit::Model::Module::Type::EXPLOIT => {
|
|
85
|
-
minimum: 1
|
|
86
|
-
},
|
|
87
|
-
Metasploit::Model::Module::Type::NOP => {
|
|
88
|
-
is: 0
|
|
89
|
-
},
|
|
90
|
-
Metasploit::Model::Module::Type::PAYLOAD => {
|
|
91
|
-
is: 0
|
|
92
|
-
},
|
|
93
|
-
Metasploit::Model::Module::Type::POST => {
|
|
94
|
-
minimum: 0
|
|
95
|
-
}
|
|
96
|
-
},
|
|
97
|
-
targets: {
|
|
98
|
-
Metasploit::Model::Module::Type::AUX => {
|
|
99
|
-
is: 0
|
|
100
|
-
},
|
|
101
|
-
Metasploit::Model::Module::Type::ENCODER => {
|
|
102
|
-
is: 0
|
|
103
|
-
},
|
|
104
|
-
Metasploit::Model::Module::Type::EXPLOIT => {
|
|
105
|
-
minimum: 1
|
|
106
|
-
},
|
|
107
|
-
Metasploit::Model::Module::Type::NOP => {
|
|
108
|
-
is: 0
|
|
109
|
-
},
|
|
110
|
-
Metasploit::Model::Module::Type::PAYLOAD => {
|
|
111
|
-
is: 0
|
|
112
|
-
},
|
|
113
|
-
Metasploit::Model::Module::Type::POST => {
|
|
114
|
-
is: 0
|
|
115
|
-
}
|
|
116
|
-
}
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
# Minimum length of {#module_authors}.
|
|
120
|
-
MINIMUM_MODULE_AUTHORS_LENGTH = 1
|
|
121
|
-
|
|
122
|
-
# {#privileged} is Boolean so, valid values are just `true` and `false`, but since both the validation and
|
|
123
|
-
# factory need an array of valid values, this constant exists.
|
|
124
|
-
PRIVILEGES = [
|
|
125
|
-
false,
|
|
126
|
-
true
|
|
127
|
-
]
|
|
128
|
-
|
|
129
|
-
# Member of {Metasploit::Model::Module::Type::ALL} that require {#stance} to be non-`nil`.
|
|
130
|
-
STANCED_MODULE_TYPES = [
|
|
131
|
-
Metasploit::Model::Module::Type::AUX,
|
|
132
|
-
Metasploit::Model::Module::Type::EXPLOIT
|
|
133
|
-
]
|
|
134
|
-
|
|
135
|
-
included do
|
|
136
|
-
include ActiveModel::Validations
|
|
137
|
-
include Metasploit::Model::Search
|
|
138
|
-
|
|
139
|
-
#
|
|
140
|
-
#
|
|
141
|
-
# Search
|
|
142
|
-
#
|
|
143
|
-
#
|
|
144
|
-
|
|
145
|
-
#
|
|
146
|
-
# Search Associations
|
|
147
|
-
#
|
|
148
|
-
|
|
149
|
-
search_association :actions
|
|
150
|
-
search_association :architectures
|
|
151
|
-
search_association :authorities
|
|
152
|
-
search_association :authors
|
|
153
|
-
search_association :email_addresses
|
|
154
|
-
search_association :module_class
|
|
155
|
-
search_association :platforms
|
|
156
|
-
search_association :rank
|
|
157
|
-
search_association :references
|
|
158
|
-
search_association :targets
|
|
159
|
-
|
|
160
|
-
#
|
|
161
|
-
# Search Attributes
|
|
162
|
-
#
|
|
163
|
-
|
|
164
|
-
search_attribute :description, :type => :string
|
|
165
|
-
search_attribute :disclosed_on, :type => :date
|
|
166
|
-
search_attribute :license, :type => :string
|
|
167
|
-
search_attribute :name, :type => :string
|
|
168
|
-
search_attribute :privileged, :type => :boolean
|
|
169
|
-
search_attribute :stance, :type => :string
|
|
170
|
-
|
|
171
|
-
#
|
|
172
|
-
# Search Withs
|
|
173
|
-
#
|
|
174
|
-
|
|
175
|
-
search_with Metasploit::Model::Search::Operator::Deprecated::App
|
|
176
|
-
search_with Metasploit::Model::Search::Operator::Deprecated::Author
|
|
177
|
-
search_with Metasploit::Model::Search::Operator::Deprecated::Authority,
|
|
178
|
-
:abbreviation => :bid
|
|
179
|
-
search_with Metasploit::Model::Search::Operator::Deprecated::Authority,
|
|
180
|
-
:abbreviation => :cve
|
|
181
|
-
search_with Metasploit::Model::Search::Operator::Deprecated::Authority,
|
|
182
|
-
:abbreviation => :edb
|
|
183
|
-
search_with Metasploit::Model::Search::Operator::Deprecated::Authority,
|
|
184
|
-
:abbreviation => :osvdb
|
|
185
|
-
search_with Metasploit::Model::Search::Operator::Deprecated::Platform,
|
|
186
|
-
:name => :os
|
|
187
|
-
search_with Metasploit::Model::Search::Operator::Deprecated::Platform,
|
|
188
|
-
:name => :platform
|
|
189
|
-
search_with Metasploit::Model::Search::Operator::Deprecated::Ref
|
|
190
|
-
search_with Metasploit::Model::Search::Operator::Deprecated::Text
|
|
191
|
-
|
|
192
|
-
#
|
|
193
|
-
#
|
|
194
|
-
# Validations
|
|
195
|
-
#
|
|
196
|
-
#
|
|
197
|
-
|
|
198
|
-
#
|
|
199
|
-
# Method Validations
|
|
200
|
-
#
|
|
201
|
-
|
|
202
|
-
validate :architectures_from_targets,
|
|
203
|
-
if: 'allows?(:targets)'
|
|
204
|
-
validate :platforms_from_targets,
|
|
205
|
-
if: 'allows?(:targets)'
|
|
206
|
-
|
|
207
|
-
#
|
|
208
|
-
# Attribute Validations
|
|
209
|
-
#
|
|
210
|
-
|
|
211
|
-
validates :actions,
|
|
212
|
-
dynamic_length: true
|
|
213
|
-
validates :description,
|
|
214
|
-
:presence => true
|
|
215
|
-
validates :license,
|
|
216
|
-
:presence => true
|
|
217
|
-
validates :module_architectures,
|
|
218
|
-
dynamic_length: true
|
|
219
|
-
validates :module_authors,
|
|
220
|
-
:length => {
|
|
221
|
-
:minimum => MINIMUM_MODULE_AUTHORS_LENGTH
|
|
222
|
-
}
|
|
223
|
-
validates :module_class,
|
|
224
|
-
:presence => true
|
|
225
|
-
validates :module_platforms,
|
|
226
|
-
dynamic_length: true
|
|
227
|
-
validates :module_references,
|
|
228
|
-
dynamic_length: true
|
|
229
|
-
validates :name,
|
|
230
|
-
:presence => true
|
|
231
|
-
validates :privileged,
|
|
232
|
-
:inclusion => {
|
|
233
|
-
:in => PRIVILEGES
|
|
234
|
-
}
|
|
235
|
-
validates :stance,
|
|
236
|
-
inclusion: {
|
|
237
|
-
if: :stanced?,
|
|
238
|
-
in: Metasploit::Model::Module::Stance::ALL
|
|
239
|
-
},
|
|
240
|
-
nil: {
|
|
241
|
-
unless: :stanced?
|
|
242
|
-
}
|
|
243
|
-
validates :targets,
|
|
244
|
-
dynamic_length: true
|
|
245
|
-
end
|
|
246
|
-
|
|
247
|
-
#
|
|
248
|
-
#
|
|
249
|
-
# Associations
|
|
250
|
-
#
|
|
251
|
-
#
|
|
252
|
-
|
|
253
|
-
# @!attribute [rw] actions
|
|
254
|
-
# Auxiliary actions to perform when this running this module.
|
|
255
|
-
#
|
|
256
|
-
# @return [Array<Metasploit::Model::Module::Action>]
|
|
257
|
-
|
|
258
|
-
# @!attribute [rw] default_action
|
|
259
|
-
# The default action in {#actions}.
|
|
260
|
-
#
|
|
261
|
-
# @return [Metasploit::Model::Module::Action]
|
|
262
|
-
|
|
263
|
-
# @!attribute [rw] default_target
|
|
264
|
-
# The default target in {#targets}.
|
|
265
|
-
#
|
|
266
|
-
# @return [Metasploit::Model::Module::Target]
|
|
267
|
-
|
|
268
|
-
# @!attribute [rw] module_architectures
|
|
269
|
-
# Joins this with {#architectures}.
|
|
270
|
-
#
|
|
271
|
-
# @return [Array<Metasploit::Model::Module::Architecture>]
|
|
272
|
-
|
|
273
|
-
# @!attribute [rw] module_authors
|
|
274
|
-
# Joins this with {#authors} and {#email_addresses} to model the name and email address used for an author
|
|
275
|
-
# entry in the module metadata.
|
|
276
|
-
#
|
|
277
|
-
# @return [Array<Metasploit::Model::Module::Author>]
|
|
278
|
-
|
|
279
|
-
# @!attribute [rw] module_class
|
|
280
|
-
# Class-derived metadata to go along with the instance-derived metadata in this model.
|
|
281
|
-
#
|
|
282
|
-
# @return [Metasploit::Model::Module::Class]
|
|
283
|
-
|
|
284
|
-
# @!attribute [rw] module_platforms
|
|
285
|
-
# Joins this with {#platforms}.
|
|
286
|
-
#
|
|
287
|
-
# @return [Array<Metasploit::Model::Module::Platform>]
|
|
288
|
-
|
|
289
|
-
# @!attribute [rw] targets
|
|
290
|
-
# Names of targets with different configurations that can be exploited by this module.
|
|
291
|
-
#
|
|
292
|
-
# @return [Array<Metasploit::Model::Module::Target>]
|
|
293
|
-
|
|
294
|
-
# @!attribute [r] architectures
|
|
295
|
-
# The {Metasploit::Model::Architecture architectures} supported by this module.
|
|
296
|
-
#
|
|
297
|
-
# @return [Array<Metasploit::Model::Architecture>]
|
|
298
|
-
|
|
299
|
-
# @!attribute [r] authors
|
|
300
|
-
# The names of the authors of this module.
|
|
301
|
-
#
|
|
302
|
-
# @return [Array<Metasploit::Model::Author>]
|
|
303
|
-
|
|
304
|
-
# @!attribute [r] email_addresses
|
|
305
|
-
# The email addresses of the authors of this module.
|
|
306
|
-
#
|
|
307
|
-
# @return [Array<Metasploit::Model::EmailAddress>]
|
|
308
|
-
|
|
309
|
-
# @!attribute [r] platforms
|
|
310
|
-
# Platforms supported by this module.
|
|
311
|
-
#
|
|
312
|
-
# @return [Array<Metasploit::Model::Module::Platform>]
|
|
313
|
-
|
|
314
|
-
# @!attribute [r] references
|
|
315
|
-
# External references to the exploit or proof-of-concept (PoC) code in this module.
|
|
316
|
-
#
|
|
317
|
-
# @return [Array<Metasploit::Model::Reference>]
|
|
318
|
-
|
|
319
|
-
# @!attribute [r] vulns
|
|
320
|
-
# Vulnerabilities with same {Metasploit::Model::Reference reference} as this module.
|
|
321
|
-
#
|
|
322
|
-
# @return [Array<Metasploit::Model::Vuln>]
|
|
323
|
-
|
|
324
|
-
# @!attribute [r] vulnerable_hosts
|
|
325
|
-
# Hosts vulnerable to this module.
|
|
326
|
-
#
|
|
327
|
-
# @return [Array<Metasploit::Model::Host>]
|
|
328
|
-
|
|
329
|
-
# @!attribute [r] vulnerable_services
|
|
330
|
-
# Services vulnerable to this module.
|
|
331
|
-
#
|
|
332
|
-
# @return [Array<Metasploit::Model::Service>]
|
|
333
|
-
|
|
334
|
-
#
|
|
335
|
-
# Attributes
|
|
336
|
-
#
|
|
337
|
-
|
|
338
|
-
# @!attribute [rw] description
|
|
339
|
-
# A long, paragraph description of what the module does.
|
|
340
|
-
#
|
|
341
|
-
# @return [String]
|
|
342
|
-
|
|
343
|
-
# @!attribute [rw] disclosed_on
|
|
344
|
-
# The date the vulnerability exploited by this module was disclosed to the public.
|
|
345
|
-
#
|
|
346
|
-
# @return [Date, nil]
|
|
347
|
-
|
|
348
|
-
# @!attribute [rw] license
|
|
349
|
-
# The name of the software license for the module's code.
|
|
350
|
-
#
|
|
351
|
-
# @return [String]
|
|
352
|
-
|
|
353
|
-
# @!attribute [rw] name
|
|
354
|
-
# The human readable name of the module. It is unrelated to {Metasploit::Model::Module::Class#full_name} or
|
|
355
|
-
# {Metasploit::Model::Module::Class#reference_name} and is better thought of as a short summary of the
|
|
356
|
-
# {#description}.
|
|
357
|
-
#
|
|
358
|
-
# @return [String]
|
|
359
|
-
|
|
360
|
-
# @!attribute [rw] privileged
|
|
361
|
-
# Whether this module requires privileged access to run.
|
|
362
|
-
#
|
|
363
|
-
# @return [Boolean]
|
|
364
|
-
|
|
365
|
-
# @!attribute [rw] stance
|
|
366
|
-
# Whether the module is active or passive. `nil` if the {#module_type} is not {#stanced?}.
|
|
367
|
-
#
|
|
368
|
-
# @return ['active', 'passive', nil]
|
|
369
|
-
|
|
370
|
-
#
|
|
371
|
-
# Module Methods
|
|
372
|
-
#
|
|
373
|
-
|
|
374
|
-
module ClassMethods
|
|
375
|
-
# Whether the given `:attribute` is allowed to be present for the given `:module_type`. An attribute is
|
|
376
|
-
# considered allowed if it allows greatrr than 0 elements for a collection.
|
|
377
|
-
#
|
|
378
|
-
# @raise [KeyError] if `:attribute` is not given in `options`.
|
|
379
|
-
# @raise [KeyError] if `:module_type` is not given in `options`.
|
|
380
|
-
# @return [true] if maximum elements is greater than 0 or value can be non-nil
|
|
381
|
-
def allows?(options={})
|
|
382
|
-
allowed = false
|
|
383
|
-
length_validation_options = dynamic_length_validation_options(options)
|
|
384
|
-
|
|
385
|
-
is = length_validation_options[:is]
|
|
386
|
-
|
|
387
|
-
if is
|
|
388
|
-
if is > 0
|
|
389
|
-
allowed = true
|
|
390
|
-
end
|
|
391
|
-
else
|
|
392
|
-
maximum = length_validation_options[:maximum]
|
|
393
|
-
|
|
394
|
-
if maximum
|
|
395
|
-
if maximum > 0
|
|
396
|
-
allowed = true
|
|
397
|
-
end
|
|
398
|
-
else
|
|
399
|
-
# if there is no maximum, then it's treated as infinite
|
|
400
|
-
allowed = true
|
|
401
|
-
end
|
|
402
|
-
end
|
|
403
|
-
|
|
404
|
-
allowed
|
|
405
|
-
end
|
|
406
|
-
|
|
407
|
-
# The length validation options for the given `:attribute` and `:module_type`.
|
|
408
|
-
# @return [Hash{Symbol => Integer}] Hash containing either `:is` (meaning :maximum and :minimum are the same) or
|
|
409
|
-
# `:minimum` (no attribute has an explicit :maximum currently).
|
|
410
|
-
# @raise [KeyError] if `:attribute` is not given in `options`.
|
|
411
|
-
# @raise [KeyError] if `:module_type` is not given in `options`.
|
|
412
|
-
# @raise [KeyError] if `:attribute` value is not a key in
|
|
413
|
-
# {DYNAMIC_LENGTH_VALIDATION_OPTIONS_BY_MODULE_TYPE_BY_ATTRIBUTE}.
|
|
414
|
-
# @raise [KeyError] if `:module_type` value is a not a {Metasploit::Model::Module::Type::ALL} member.
|
|
415
|
-
def dynamic_length_validation_options(options={})
|
|
416
|
-
options.assert_valid_keys(:attribute, :module_type)
|
|
417
|
-
attribute = options.fetch(:attribute)
|
|
418
|
-
module_type = options.fetch(:module_type)
|
|
419
|
-
|
|
420
|
-
dynamic_length_validation_options_by_module_type = DYNAMIC_LENGTH_VALIDATION_OPTIONS_BY_MODULE_TYPE_BY_ATTRIBUTE.fetch(attribute)
|
|
421
|
-
dynamic_length_validation_options_by_module_type.fetch(module_type)
|
|
422
|
-
end
|
|
423
|
-
|
|
424
|
-
# Whether the `:module_type` requires stance to be in {Metasploit::Model::Module::Stance::ALL} or if it must
|
|
425
|
-
# be `nil`.
|
|
426
|
-
#
|
|
427
|
-
# @param module_type [String] A member of `Metasploit::Model::Module::Type::ALL`.
|
|
428
|
-
# @return [true] if `module_type` is in {STANCED_MODULE_TYPES}.
|
|
429
|
-
# @return [false] otherwise.
|
|
430
|
-
def stanced?(module_type)
|
|
431
|
-
STANCED_MODULE_TYPES.include? module_type
|
|
432
|
-
end
|
|
433
|
-
end
|
|
434
|
-
|
|
435
|
-
# make ClassMethods directly callable on Metasploit::Model::Module::Instance for factories
|
|
436
|
-
extend ClassMethods
|
|
437
|
-
|
|
438
|
-
#
|
|
439
|
-
# Module Methods
|
|
440
|
-
#
|
|
441
|
-
|
|
442
|
-
# Values of {#module_type} (members of {Metasploit::Model::Module::Type::ALL}), which have an exact length
|
|
443
|
-
# (`:is`) or maximum length (`:maximum`) greater than 0 for the given `attribute`.
|
|
444
|
-
#
|
|
445
|
-
# @return [Array<String>] Array with members of {Metasploit::Model::Module::Type::ALL}.
|
|
446
|
-
# @see DYNAMIC_LENGTH_VALIDATION_OPTIONS_BY_MODULE_TYPE_BY_ATTRIBUTE
|
|
447
|
-
def self.module_types_that_allow(attribute)
|
|
448
|
-
dynamic_length_validation_options_by_module_type = DYNAMIC_LENGTH_VALIDATION_OPTIONS_BY_MODULE_TYPE_BY_ATTRIBUTE.fetch(attribute)
|
|
449
|
-
|
|
450
|
-
dynamic_length_validation_options_by_module_type.each_with_object([]) { |(module_type, dynamic_length_validation_options), module_types|
|
|
451
|
-
is = dynamic_length_validation_options[:is]
|
|
452
|
-
|
|
453
|
-
if is
|
|
454
|
-
if is > 0
|
|
455
|
-
module_types << module_type
|
|
456
|
-
end
|
|
457
|
-
else
|
|
458
|
-
maximum = dynamic_length_validation_options[:maximum]
|
|
459
|
-
|
|
460
|
-
if maximum
|
|
461
|
-
if maximum > 0
|
|
462
|
-
module_types << module_type
|
|
463
|
-
end
|
|
464
|
-
else
|
|
465
|
-
module_types << module_type
|
|
466
|
-
end
|
|
467
|
-
end
|
|
468
|
-
|
|
469
|
-
}
|
|
470
|
-
end
|
|
471
|
-
|
|
472
|
-
#
|
|
473
|
-
# Instance Methods
|
|
474
|
-
#
|
|
475
|
-
|
|
476
|
-
# Whether the given `attribute` is allowed to have elements.
|
|
477
|
-
#
|
|
478
|
-
# @param attribute [Symbol] name of attribute to check if {#module_type} allows it to have one or more
|
|
479
|
-
# elements.
|
|
480
|
-
# @return (see Metasploit::Model::Module::Instance::ClassMethods#allows?)
|
|
481
|
-
# @return [false] if {#module_type} is not valid
|
|
482
|
-
def allows?(attribute)
|
|
483
|
-
if Metasploit::Model::Module::Type::ALL.include? module_type
|
|
484
|
-
self.class.allows?(
|
|
485
|
-
attribute: attribute,
|
|
486
|
-
module_type: module_type
|
|
487
|
-
)
|
|
488
|
-
else
|
|
489
|
-
false
|
|
490
|
-
end
|
|
491
|
-
end
|
|
492
|
-
|
|
493
|
-
# The dynamic length valdiations, such as `:is` and `:minimum` for the given attribute for the current
|
|
494
|
-
# {#module_type}.
|
|
495
|
-
#
|
|
496
|
-
# @param attribute [Symbol] name of attribute whose dynamic length validation options to be
|
|
497
|
-
# @return (see Metasploit::Model::Module::Instance::ClassMethods#dynamic_length_validation_options)
|
|
498
|
-
# @return [{}] an empty Hash if {#module_type} is not a member of {Metasploit::Model::Module::Type::ALL}
|
|
499
|
-
def dynamic_length_validation_options(attribute)
|
|
500
|
-
if Metasploit::Model::Module::Type::ALL.include? module_type
|
|
501
|
-
self.class.dynamic_length_validation_options(
|
|
502
|
-
module_type: module_type,
|
|
503
|
-
attribute: attribute
|
|
504
|
-
)
|
|
505
|
-
else
|
|
506
|
-
{}
|
|
507
|
-
end
|
|
508
|
-
end
|
|
509
|
-
|
|
510
|
-
# @!method module_type
|
|
511
|
-
# The {Metasploit::Model::Module::Class#module_type} of the {#module_class}.
|
|
512
|
-
#
|
|
513
|
-
# @return (see Metasploit;:Model::Module::Class#module_type)
|
|
514
|
-
delegate :module_type,
|
|
515
|
-
allow_nil: true,
|
|
516
|
-
to: :module_class
|
|
517
|
-
|
|
518
|
-
# Whether {#module_type} requires {#stance} to be set or to be `nil`.
|
|
519
|
-
#
|
|
520
|
-
# @return (see Metasploit::Model::Module::Instance::ClassMethods#stanced?)
|
|
521
|
-
# @return [false] if {#module_type} is not valid
|
|
522
|
-
def stanced?
|
|
523
|
-
self.class.stanced?(module_type)
|
|
524
|
-
end
|
|
525
|
-
|
|
526
|
-
private
|
|
527
|
-
|
|
528
|
-
# Validates that the {#module_architectures}
|
|
529
|
-
# {Metasploit::Model::Module::Architecture#architecture architectures} match the {#targets}
|
|
530
|
-
# {Metasploit::Model::Module::Target#target_architectures target_architectures}
|
|
531
|
-
# {Metasploit::Model::Module::Target::Architecture#architecture architectures}.
|
|
532
|
-
#
|
|
533
|
-
# @return [void]
|
|
534
|
-
def architectures_from_targets
|
|
535
|
-
actual_architecture_set = Set.new module_architectures.map(&:architecture)
|
|
536
|
-
expected_architecture_set = Set.new
|
|
537
|
-
|
|
538
|
-
targets.each do |module_target|
|
|
539
|
-
module_target.target_architectures.each do |target_architecture|
|
|
540
|
-
expected_architecture_set.add target_architecture.architecture
|
|
541
|
-
end
|
|
542
|
-
end
|
|
543
|
-
|
|
544
|
-
extra_architecture_set = actual_architecture_set - expected_architecture_set
|
|
545
|
-
|
|
546
|
-
unless extra_architecture_set.empty?
|
|
547
|
-
human_extra_architectures = human_architecture_set(extra_architecture_set)
|
|
548
|
-
|
|
549
|
-
errors.add(:architectures, :extra, extra: human_extra_architectures)
|
|
550
|
-
end
|
|
551
|
-
|
|
552
|
-
missing_architecture_set = expected_architecture_set - actual_architecture_set
|
|
553
|
-
|
|
554
|
-
unless missing_architecture_set.empty?
|
|
555
|
-
human_missing_architectures = human_architecture_set(missing_architecture_set)
|
|
556
|
-
|
|
557
|
-
errors.add(:architectures, :missing, missing: human_missing_architectures)
|
|
558
|
-
end
|
|
559
|
-
end
|
|
560
|
-
|
|
561
|
-
# Converts a Set<Metasploit::Model::Architecture> to a human readable representation including the
|
|
562
|
-
# {Metasploit::Model::Architecture#abbreviation}.
|
|
563
|
-
#
|
|
564
|
-
# @return [String]
|
|
565
|
-
def human_architecture_set(architecture_set)
|
|
566
|
-
abbreviations = architecture_set.map(&:abbreviation)
|
|
567
|
-
|
|
568
|
-
human_set(abbreviations)
|
|
569
|
-
end
|
|
570
|
-
|
|
571
|
-
# Converts a Set<Metasploit::Model::Platform> to a human-readable representation including the
|
|
572
|
-
# {Metasploit::Model::Platform#fully_qualified_name}.
|
|
573
|
-
#
|
|
574
|
-
# @return [String]
|
|
575
|
-
def human_platform_set(platform_set)
|
|
576
|
-
fully_qualified_names = platform_set.map(&:fully_qualified_name)
|
|
577
|
-
|
|
578
|
-
human_set(fully_qualified_names)
|
|
579
|
-
end
|
|
580
|
-
|
|
581
|
-
# Converts strings to a human-readable set notation.
|
|
582
|
-
#
|
|
583
|
-
# @return [String]
|
|
584
|
-
def human_set(strings)
|
|
585
|
-
sorted = strings.sort
|
|
586
|
-
comma_separated = sorted.join(', ')
|
|
587
|
-
|
|
588
|
-
"{#{comma_separated}}"
|
|
589
|
-
end
|
|
590
|
-
|
|
591
|
-
# Validates that {#module_platforms} {Metasploit::Model::Module::Platform#platform platforms} match the
|
|
592
|
-
# {#targets} {Metasploit::Model::Module::Target#target_platforms target_platforms}
|
|
593
|
-
# {Metasploit::Model::Module::Target::Platform#platform platforms}.
|
|
594
|
-
#
|
|
595
|
-
# @return [void]
|
|
596
|
-
def platforms_from_targets
|
|
597
|
-
actual_platform_set = Set.new module_platforms.map(&:platform)
|
|
598
|
-
expected_platform_set = Set.new
|
|
599
|
-
|
|
600
|
-
targets.each do |module_target|
|
|
601
|
-
module_target.target_platforms.each do |target_platform|
|
|
602
|
-
expected_platform_set.add target_platform.platform
|
|
603
|
-
end
|
|
604
|
-
end
|
|
605
|
-
|
|
606
|
-
extra_platform_set = actual_platform_set - expected_platform_set
|
|
607
|
-
|
|
608
|
-
unless extra_platform_set.empty?
|
|
609
|
-
human_extra_platforms = human_platform_set(extra_platform_set)
|
|
610
|
-
|
|
611
|
-
errors.add(:platforms, :extra, extra: human_extra_platforms)
|
|
612
|
-
end
|
|
613
|
-
|
|
614
|
-
missing_platform_set = expected_platform_set - actual_platform_set
|
|
615
|
-
|
|
616
|
-
unless missing_platform_set.empty?
|
|
617
|
-
human_missing_platforms = human_platform_set(missing_platform_set)
|
|
618
|
-
|
|
619
|
-
errors.add(:platforms, :missing, missing: human_missing_platforms)
|
|
620
|
-
end
|
|
621
|
-
end
|
|
622
|
-
end
|