esp_sdk 2.4.0 → 2.5.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: eb2adf95c6d86093eac45d8f35aba16bfc4b92fd
4
- data.tar.gz: 866a1a8d25f13d6c948cf2c5763d32733c488706
3
+ metadata.gz: 985f05618a6621f6fd81b455b13b532c91712638
4
+ data.tar.gz: 773b7274d4889449a4264f8f19e869f4b9830798
5
5
  SHA512:
6
- metadata.gz: 8c956e5ead24371596d4b00c95d2d4a60d385a51c6ffc7fb1df4cfac3b3b73eff6ada2ee93bb36741eb54c12ee4a8c2f75d8c6d5e894b1f6b8291c063302d7e0
7
- data.tar.gz: b4bde23260e83f6819e757c86bb10b720a6378895885e2d806058ae9ea68e93519a1ca1d7ff6639d8265717378c3b864268610c5be78c7db877687386f2ee3e2
6
+ metadata.gz: 54b0c9606230136eff0b3c463467be3f9eebf1986b4e603622c1efa59e680496769b7d086ae13913be912df2c12e7ac2f1e5352e39a1ba18628ffc4c9e36828a
7
+ data.tar.gz: c398c98653b66c5adac110c958580662fb1bba451fa0d8db98cc6f435e6d1ed0f5f79c594a768bee60232d9b9799eeff230c65961ab1c4cc43e2917bb344667e
@@ -1,5 +1,10 @@
1
1
  ## Unreleased
2
2
 
3
+ ## 2.5.0 - 2016-07-20
4
+ ### Added
5
+ - Add custom signature definitions and results. Code for a custom signature is now created/updated under a definition. Running a definition for an on demand test creates a result record which will have errors and alerts when completed.
6
+ - This is a backwards incompatible change on the API. If you need to save or run code on a custom signature, make sure to use this version or later.
7
+
3
8
  ## 2.4.0 - 2016-06-30
4
9
  ### Added
5
10
  - Report relationship to ExternalAccount
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- esp_sdk (2.4.0)
4
+ esp_sdk (2.5.0)
5
5
  activeresource (~> 4.0.0)
6
6
  api-auth (~> 2.0.0)
7
7
  rack
data/lib/esp.rb CHANGED
@@ -102,11 +102,6 @@ module ESP
102
102
  autoload :Tag, File.expand_path(File.dirname(__FILE__) + '/esp/resources/tag')
103
103
  autoload :Region, File.expand_path(File.dirname(__FILE__) + '/esp/resources/region')
104
104
  autoload :Suppression, File.expand_path(File.dirname(__FILE__) + '/esp/resources/suppression')
105
- class Suppression
106
- autoload :UniqueIdentifier, File.expand_path(File.dirname(__FILE__) + '/esp/resources/suppression/unique_identifier')
107
- autoload :Signature, File.expand_path(File.dirname(__FILE__) + '/esp/resources/suppression/signature')
108
- autoload :Region, File.expand_path(File.dirname(__FILE__) + '/esp/resources/suppression/region')
109
- end
110
105
  autoload :Stat, File.expand_path(File.dirname(__FILE__) + '/esp/resources/stat')
111
106
  autoload :StatCustomSignature, File.expand_path(File.dirname(__FILE__) + '/esp/resources/stat_custom_signature')
112
107
  autoload :StatSignature, File.expand_path(File.dirname(__FILE__) + '/esp/resources/stat_signature')
@@ -114,9 +109,4 @@ module ESP
114
109
  autoload :StatService, File.expand_path(File.dirname(__FILE__) + '/esp/resources/stat_service')
115
110
  autoload :ExternalAccountCreator, File.expand_path(File.dirname(__FILE__) + '/../lib/esp/external_account_creator')
116
111
  autoload :AWSClients, File.expand_path(File.dirname(__FILE__) + '/../lib/esp/aws_clients')
117
- class Report
118
- module Export
119
- autoload :Integration, File.expand_path(File.dirname(__FILE__) + '/esp/resources/reports/export/integration')
120
- end
121
- end
122
112
  end
@@ -1,8 +1,12 @@
1
1
  module ESP
2
2
  class CustomSignature < ESP::Resource
3
+ autoload :Definition, File.expand_path(File.dirname(__FILE__) + '/custom_signature/definition')
4
+ autoload :Result, File.expand_path(File.dirname(__FILE__) + '/custom_signature/result')
5
+
3
6
  ##
4
7
  # The organization this custom signature belongs to.
5
8
  belongs_to :organization, class_name: 'ESP::Organization'
9
+ has_many :definitions, class_name: 'ESP::CustomSignature::Definition'
6
10
 
7
11
  ##
8
12
  # The collection of teams that belong to the custom_signature.
@@ -11,100 +15,6 @@ module ESP
11
15
  Team.where(custom_signatures_id_eq: id)
12
16
  end
13
17
 
14
- # Run a custom signature that has not been saved. Useful for debugging a custom signature.
15
- # Returns a collection of alerts.
16
- # Throws an error if not successful.
17
- #
18
- # ==== Parameters
19
- #
20
- # +arguments+ | Required | A hash of run arguments
21
- #
22
- # ===== Valid Arguments
23
- #
24
- # See {API documentation}[http://api-docs.evident.io?ruby#custom-signature-run-new] for valid arguments
25
- #
26
- # ==== Example
27
- # signature = "# Demo Ruby Signature\r\nconfigure do |c|\r\n # Set regions to run in. Remove this line to run in all regions.\r\n c.valid_regions = [:us_east_1]\r\n # Override region to display as global. Useful when checking resources\r\n # like IAM that do not have a specific region.\r\n c.display_as = :global\r\n # deep_inspection works with set_data to automically collect\r\n # data fields for each alert. Not required.\r\n c.deep_inspection = [:users]\r\nend\r\n\r\n# Required perform method\r\ndef perform(aws)\r\n list_users = aws.iam.list_users\r\n count = list_users[:users].count\r\n\r\n # Set data for deep_inspection to use\r\n set_data(list_users)\r\n\r\n if count == 0\r\n fail(user_count: count, condition: 'count == 0')\r\n else\r\n pass(user_count: count, condition: 'count >= 1')\r\n end\r\nend\r\n"
28
- # alerts = ESP::CustomSignature.run!(external_account_id: 3, regions: ['us_east_1'], language: 'ruby', signature: signature)
29
- def self.run!(arguments = {})
30
- result = run(arguments)
31
- return result if result.is_a?(ActiveResource::Collection)
32
- result.message = result.errors.full_messages.join(' ')
33
- fail(ActiveResource::ResourceInvalid.new(result)) # rubocop:disable Style/RaiseArgs
34
- end
35
-
36
- # Run a custom signature that has not been saved. Useful for debugging a custom signature.
37
- # Returns a collection of alerts.
38
- # If not successful, returns a CustomSignature object with the errors object populated.
39
- #
40
- # ==== Parameters
41
- #
42
- # +arguments+ | Required | A hash of run arguments
43
- #
44
- # ===== Valid Arguments
45
- #
46
- # See {API documentation}[http://api-docs.evident.io?ruby#custom-signature-run-new] for valid arguments
47
- #
48
- # ==== Example
49
- # signature = "# Demo Ruby Signature\r\nconfigure do |c|\r\n # Set regions to run in. Remove this line to run in all regions.\r\n c.valid_regions = [:us_east_1]\r\n # Override region to display as global. Useful when checking resources\r\n # like IAM that do not have a specific region.\r\n c.display_as = :global\r\n # deep_inspection works with set_data to automically collect\r\n # data fields for each alert. Not required.\r\n c.deep_inspection = [:users]\r\nend\r\n\r\n# Required perform method\r\ndef perform(aws)\r\n list_users = aws.iam.list_users\r\n count = list_users[:users].count\r\n\r\n # Set data for deep_inspection to use\r\n set_data(list_users)\r\n\r\n if count == 0\r\n fail(user_count: count, condition: 'count == 0')\r\n else\r\n pass(user_count: count, condition: 'count >= 1')\r\n end\r\nend\r\n"
50
- # alerts = ESP::CustomSignature.run(external_account_id: 3, regions: ['us_east_1'], language: 'ruby', signature: signature)
51
- def self.run(arguments = {})
52
- arguments = arguments.with_indifferent_access
53
- arguments[:regions] = Array(arguments[:regions])
54
- new(arguments).run
55
- end
56
-
57
- # Run this custom signature instance.
58
- # Returns a collection of alerts.
59
- # Throws an error if not successful.
60
- #
61
- # ==== Parameters
62
- #
63
- # +arguments+ | Required | A hash of run arguments
64
- #
65
- # ===== Valid Arguments
66
- #
67
- # See {API documentation}[http://api-docs.evident.io?ruby#custom-signature-run-existing] for valid arguments
68
- #
69
- # ==== Example
70
- # custom_signature = ESP::CustomSignature.find(365)
71
- # alerts = custom_signature.run!(external_account_id: 3, regions: ['us_east_1'])
72
- def run!(arguments = {})
73
- result = run(arguments)
74
- return result if result.is_a?(ActiveResource::Collection)
75
- self.message = errors.full_messages.join(' ')
76
- fail(ActiveResource::ResourceInvalid.new(self)) # rubocop:disable Style/RaiseArgs
77
- end
78
-
79
- # Run this custom signature instance.
80
- # Returns a collection of alerts.
81
- # If not successful, returns a CustomSignature object with the errors object populated.
82
- #
83
- # ==== Parameters
84
- #
85
- # +arguments+ | Required | A hash of run arguments
86
- #
87
- # ===== Valid Arguments
88
- #
89
- # See {API documentation}[http://api-docs.evident.io?ruby#custom-signature-run-existing] for valid arguments
90
- #
91
- # ==== Example
92
- # custom_signature = ESP::CustomSignature.find(365)
93
- # alerts = custom_signature.run(external_account_id: 3, regions: ['us_east_1'])
94
- def run(arguments = {})
95
- arguments = arguments.with_indifferent_access
96
-
97
- attributes['external_account_id'] ||= arguments[:external_account_id]
98
- attributes['regions'] ||= Array(arguments[:regions])
99
-
100
- response = connection.post endpoint, to_json
101
- ESP::Alert.send(:instantiate_collection, self.class.format.decode(response.body))
102
- rescue ActiveResource::BadRequest, ActiveResource::ResourceInvalid, ActiveResource::ResourceNotFound => error
103
- load_remote_errors(error, true)
104
- self.code = error.response.code
105
- self
106
- end
107
-
108
18
  # Create a suppression for this custom signature.
109
19
  #
110
20
  # ==== Parameter
@@ -175,8 +85,7 @@ module ESP
175
85
  #
176
86
  # ==== Example
177
87
  #
178
- # signature = "# Demo Ruby Signature\r\nconfigure do |c|\r\n # Set regions to run in. Remove this line to run in all regions.\r\n c.valid_regions = [:us_east_1]\r\n # Override region to display as global. Useful when checking resources\r\n # like IAM that do not have a specific region.\r\n c.display_as = :global\r\n # deep_inspection works with set_data to automically collect\r\n # data fields for each alert. Not required.\r\n c.deep_inspection = [:users]\r\nend\r\n\r\n# Required perform method\r\ndef perform(aws)\r\n list_users = aws.iam.list_users\r\n count = list_users[:users].count\r\n\r\n # Set data for deep_inspection to use\r\n set_data(list_users)\r\n\r\n if count == 0\r\n fail(user_count: count, condition: 'count == 0')\r\n else\r\n pass(user_count: count, condition: 'count >= 1')\r\n end\r\nend\r\n"
179
- # custom_signature = ESP::CustomSignature.create(signature: signature, description: "A test custom signature.", identifier: "AWS::IAM::001", language: "ruby", name: "Test Signature", risk_level: "Medium")
88
+ # custom_signature = ESP::CustomSignature.create(description: "A test custom signature.", identifier: "AWS::IAM::001", name: "Test Signature", risk_level: "Medium")
180
89
 
181
90
  # :method: save
182
91
  # Create or update a CustomSignature
@@ -187,21 +96,10 @@ module ESP
187
96
  #
188
97
  # ==== Example
189
98
  #
190
- # signature = "# Demo Ruby Signature\r\nconfigure do |c|\r\n # Set regions to run in. Remove this line to run in all regions.\r\n c.valid_regions = [:us_east_1]\r\n # Override region to display as global. Useful when checking resources\r\n # like IAM that do not have a specific region.\r\n c.display_as = :global\r\n # deep_inspection works with set_data to automically collect\r\n # data fields for each alert. Not required.\r\n c.deep_inspection = [:users]\r\nend\r\n\r\n# Required perform method\r\ndef perform(aws)\r\n list_users = aws.iam.list_users\r\n count = list_users[:users].count\r\n\r\n # Set data for deep_inspection to use\r\n set_data(list_users)\r\n\r\n if count == 0\r\n fail(user_count: count, condition: 'count == 0')\r\n else\r\n pass(user_count: count, condition: 'count >= 1')\r\n end\r\nend\r\n"
191
- # custom_signature = ESP::CustomSignature.new(signature: signature, description: "A test custom signature.", identifier: "AWS::IAM::001", language: "ruby", name: "Test Signature", risk_level: "Medium")
99
+ # custom_signature = ESP::CustomSignature.new(description: "A test custom signature.", identifier: "AWS::IAM::001", name: "Test Signature", risk_level: "Medium")
192
100
  # custom_signature.save
193
101
 
194
102
  # :method: destroy
195
103
  # Delete a CustomSignature
196
-
197
- private
198
-
199
- def endpoint
200
- if id.present?
201
- "#{self.class.prefix}custom_signatures/#{id}/run.json"
202
- else
203
- "#{self.class.prefix}custom_signatures/run.json"
204
- end
205
- end
206
104
  end
207
105
  end
@@ -0,0 +1,100 @@
1
+ module ESP
2
+ class CustomSignature
3
+ class Definition < ESP::Resource
4
+ self.prefix += "custom_signature_"
5
+
6
+ belongs_to :custom_signature, class_name: 'ESP::CustomSignature'
7
+ has_many :results, class_name: 'ESP::CustomSignature::Result'
8
+
9
+ def activate
10
+ patch(:activate).tap do |response|
11
+ load_attributes_from_response(response)
12
+ end
13
+ rescue ActiveResource::BadRequest, ActiveResource::ResourceInvalid, ActiveResource::UnauthorizedAccess => error
14
+ load_remote_errors(error, true)
15
+ self.code = error.response.code
16
+ false
17
+ end
18
+
19
+ def archive
20
+ patch(:archive).tap do |response|
21
+ load_attributes_from_response(response)
22
+ end
23
+ rescue ActiveResource::BadRequest, ActiveResource::ResourceInvalid, ActiveResource::UnauthorizedAccess => error
24
+ load_remote_errors(error, true)
25
+ self.code = error.response.code
26
+ false
27
+ end
28
+
29
+ # :singleton-method: where
30
+ # Return a paginated CustomSignature::Definition list filtered by search parameters
31
+ #
32
+ # ==== Parameters
33
+ #
34
+ # +clauses+ | Hash of attributes with appended predicates to search, sort and include.
35
+ #
36
+ # ===== Valid Clauses
37
+ #
38
+ # See {API documentation}[http://api-docs.evident.io?ruby#custom-signature-definition-attributes] for valid arguments
39
+ #
40
+ # :call-seq:
41
+ # where(clauses = {})
42
+
43
+ ##
44
+ # :singleton-method: find
45
+ # Find a CustomSignature::Definition by id
46
+ #
47
+ # ==== Parameter
48
+ #
49
+ # +id+ | Required | The ID of the custom signature definition to retrieve
50
+ #
51
+ # +options+ | Optional | A hash of options
52
+ #
53
+ # ===== Valid Options
54
+ #
55
+ # +include+ | The list of associated objects to return on the initial request.
56
+ #
57
+ # ===== Valid Includable Associations
58
+ #
59
+ # See {API documentation}[http://api-docs.evident.io?ruby#custom-signature-definition-attributes] for valid arguments
60
+ #
61
+ # :call-seq:
62
+ # find(id, options = {})
63
+
64
+ # :singleton-method: all
65
+ # Return a paginated CustomSignature::Definition list
66
+
67
+ # :singleton-method: create
68
+ # Create a CustomSignature::Definition
69
+ # :call-seq:
70
+ # create(attributes={})
71
+ #
72
+ # ==== Parameter
73
+ #
74
+ # +attributes+ | Required | A hash of custom signature definition attributes
75
+ #
76
+ # ===== Valid Attributes
77
+ #
78
+ # See {API documentation}[http://api-docs.evident.io?ruby#custom-signature-definition-create] for valid arguments
79
+ #
80
+ # ==== Example
81
+ #
82
+ # definition = ESP::CustomSignature::Definition.create(custom_signature_id: 1)
83
+
84
+ # :method: save
85
+ # Create or update a CustomSignature::Definition
86
+ #
87
+ # ===== Valid Attributes
88
+ #
89
+ # See {API documentation}[http://api-docs.evident.io?ruby#custom-signature-definition-create] for valid arguments
90
+ #
91
+ # ==== Example
92
+ #
93
+ # definition = ESP::CustomSignature::Definition.new(custom_signature_id: 1)
94
+ # definition.save
95
+
96
+ # :method: destroy
97
+ # Delete a CustomSignature::Definition
98
+ end
99
+ end
100
+ end
@@ -0,0 +1,97 @@
1
+ module ESP
2
+ class CustomSignature
3
+ class Result < ESP::Resource
4
+ autoload :Alert, File.expand_path(File.dirname(__FILE__) + '/result/alert')
5
+
6
+ self.prefix += "custom_signature_"
7
+
8
+ belongs_to :definition, class_name: 'ESP::CustomSignature::Definition', foreign_key: :definition_id
9
+ belongs_to :region, class_name: 'ESP::Region'
10
+ belongs_to :external_account, class_name: 'ESP::ExternalAccount'
11
+
12
+ def alerts
13
+ return attributes['alerts'] if attributes['alerts'].present?
14
+ CustomSignature::Result::Alert.for_result(id)
15
+ end
16
+
17
+ # Not Implemented. You cannot update a CustomSignature::Result.
18
+ def update
19
+ fail ESP::NotImplementedError
20
+ end
21
+
22
+ # Not Implemented. You cannot destroy a CustomSignature::Result.
23
+ def destroy
24
+ fail ESP::NotImplementedError
25
+ end
26
+
27
+ # :singleton-method: where
28
+ # Return a paginated CustomSignature::Result list filtered by search parameters
29
+ #
30
+ # ==== Parameters
31
+ #
32
+ # +clauses+ | Hash of attributes with appended predicates to search, sort and include.
33
+ #
34
+ # ===== Valid Clauses
35
+ #
36
+ # See {API documentation}[http://api-docs.evident.io?ruby#custom-signature-result-attributes] for valid arguments
37
+ #
38
+ # :call-seq:
39
+ # where(clauses = {})
40
+
41
+ ##
42
+ # :singleton-method: find
43
+ # Find a CustomSignature::Result by id
44
+ #
45
+ # ==== Parameter
46
+ #
47
+ # +id+ | Required | The ID of the custom signature result to retrieve
48
+ #
49
+ # +options+ | Optional | A hash of options
50
+ #
51
+ # ===== Valid Options
52
+ #
53
+ # +include+ | The list of associated objects to return on the initial request.
54
+ #
55
+ # ===== Valid Includable Associations
56
+ #
57
+ # See {API documentation}[http://api-docs.evident.io?ruby#custom-signature-result-attributes] for valid arguments
58
+ #
59
+ # :call-seq:
60
+ # find(id, options = {})
61
+
62
+ # :singleton-method: all
63
+ # Return a paginated CustomSignature::Result list
64
+
65
+ # :singleton-method: create
66
+ # Create a CustomSignature::Result
67
+ # :call-seq:
68
+ # create(attributes={})
69
+ #
70
+ # ==== Parameter
71
+ #
72
+ # +attributes+ | Required | A hash of custom signature result attributes
73
+ #
74
+ # ===== Valid Attributes
75
+ #
76
+ # See {API documentation}[http://api-docs.evident.io?ruby#custom-signature-result-create] for valid arguments
77
+ #
78
+ # ==== Example
79
+ #
80
+ # signature = "# Demo Ruby Signature\r\nconfigure do |c|\r\n # Set regions to run in. Remove this line to run in all regions.\r\n c.valid_regions = [:us_east_1]\r\n # Override region to display as global. Useful when checking resources\r\n # like IAM that do not have a specific region.\r\n c.display_as = :global\r\n # deep_inspection works with set_data to automically collect\r\n # data fields for each alert. Not required.\r\n c.deep_inspection = [:users]\r\nend\r\n\r\n# Required perform method\r\ndef perform(aws)\r\n list_users = aws.iam.list_users\r\n count = list_users[:users].count\r\n\r\n # Set data for deep_inspection to use\r\n set_data(list_users)\r\n\r\n if count == 0\r\n fail(user_count: count, condition: 'count == 0')\r\n else\r\n pass(user_count: count, condition: 'count >= 1')\r\n end\r\nend\r\n"
81
+ # result = ESP::CustomSignature::Result.create(signature: signature, language: "ruby", region_id: 1, external_account_id: 1)
82
+
83
+ # :method: save
84
+ # Create or update a CustomSignature::Result
85
+ #
86
+ # ===== Valid Attributes
87
+ #
88
+ # See {API documentation}[http://api-docs.evident.io?ruby#custom-signature-result-create] for valid arguments
89
+ #
90
+ # ==== Example
91
+ #
92
+ # signature = "# Demo Ruby Signature\r\nconfigure do |c|\r\n # Set regions to run in. Remove this line to run in all regions.\r\n c.valid_regions = [:us_east_1]\r\n # Override region to display as global. Useful when checking resources\r\n # like IAM that do not have a specific region.\r\n c.display_as = :global\r\n # deep_inspection works with set_data to automically collect\r\n # data fields for each alert. Not required.\r\n c.deep_inspection = [:users]\r\nend\r\n\r\n# Required perform method\r\ndef perform(aws)\r\n list_users = aws.iam.list_users\r\n count = list_users[:users].count\r\n\r\n # Set data for deep_inspection to use\r\n set_data(list_users)\r\n\r\n if count == 0\r\n fail(user_count: count, condition: 'count == 0')\r\n else\r\n pass(user_count: count, condition: 'count >= 1')\r\n end\r\nend\r\n"
93
+ # result = ESP::CustomSignature::Result.new(signature: signature, language: "ruby", region_id: 1, external_account_id: 1)
94
+ # result.save
95
+ end
96
+ end
97
+ end
@@ -0,0 +1,53 @@
1
+ module ESP
2
+ class CustomSignature
3
+ class Result
4
+ class Alert < ESP::Resource
5
+ belongs_to :region, class_name: 'ESP::Region'
6
+ belongs_to :external_account, class_name: 'ESP::ExternalAccount'
7
+ belongs_to :custom_signature, class_name: 'ESP::CustomSignature'
8
+
9
+ # Returns all the alerts for a custom signature result identified by the custom_signature_result_id parameter.
10
+ #
11
+ # ==== Parameters
12
+ #
13
+ # +custom_signature_result_id+ | Required | The ID of the custom signature result to retrieve alerts for
14
+ #
15
+ # See {API documentation}[http://api-docs.evident.io?ruby#alert-attributes] for valid arguments
16
+ def self.for_result(custom_signature_result_id = nil)
17
+ fail ArgumentError, "You must supply a custom signature result id." unless custom_signature_result_id.present?
18
+ # call find_every directly since find is overriden/not implemented
19
+ find_every(from: "#{prefix}custom_signature_results/#{custom_signature_result_id}/alerts.json")
20
+ end
21
+
22
+ # Not Implemented. You cannot search for CustomSignature::Result::Alert.
23
+ #
24
+ # Regular ARELlike methods are disabled.
25
+ def self.find(*)
26
+ fail ESP::NotImplementedError, 'Regular ARELlike methods are disabled. Use the .for_result method.'
27
+ end
28
+
29
+ # Not Implemented. You cannot search for CustomSignature::Result::Alert.
30
+ #
31
+ # Regular ARELlike methods are disabled.
32
+ def self.where(*)
33
+ fail ESP::NotImplementedError, 'Regular ARELlike methods are disabled. Use the .for_result method.'
34
+ end
35
+
36
+ # Not Implemented. You cannot create a CustomSignature::Result::Alert.
37
+ def create
38
+ fail ESP::NotImplementedError
39
+ end
40
+
41
+ # Not Implemented. You cannot update a CustomSignature::Result::Alert.
42
+ def update
43
+ fail ESP::NotImplementedError
44
+ end
45
+
46
+ # Not Implemented. You cannot destroy a CustomSignature::Result::Alert.
47
+ def destroy
48
+ fail ESP::NotImplementedError
49
+ end
50
+ end
51
+ end
52
+ end
53
+ end