Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[outputs] Komand support added (contribution from @0xdabbad00) #608

Merged
merged 5 commits into from
Feb 27, 2018
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions conf/outputs.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,9 @@
"aws-s3": {
"sample-bucket": "sample.bucket.name"
},
"komand": [
"sample-integration"
],
"pagerduty": [
"sample-integration"
],
Expand Down
3 changes: 2 additions & 1 deletion docs/source/outputs.rst
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ Out of the box, StreamAlert supports:

* **AWS Lambda**
* **AWS S3**
* **Komand**
* **PagerDuty**
* **Phantom**
* **Slack**
Expand All @@ -29,7 +30,7 @@ Configuration
Adding a new configuration for a currently supported service is handled using ``manage.py``:

- ``python manage.py output new --service <SERVICE_NAME>``
- ``<SERVICE_NAME>`` above should be one of the following supported service identifiers: ``aws-lambda``, ``aws-s3``, ``pagerduty``, ``phantom``, or ``slack``
- ``<SERVICE_NAME>`` above should be one of the following supported service identifiers: ``aws-lambda``, ``aws-s3``, ``komand``, ``pagerduty``, ``phantom``, or ``slack``

For example:
- ``python manage.py output new --service slack``
Expand Down
2 changes: 1 addition & 1 deletion docs/source/rules.rst
Original file line number Diff line number Diff line change
Expand Up @@ -122,7 +122,7 @@ outputs

``outputs`` define where the alert should be sent to if the return value of a rule is ``True``. Your rule(s) must define at least one output.

StreamAlert supports sending alerts to PagerDuty, Slack, Amazon S3 and Phantom.
StreamAlert supports sending alerts to PagerDuty, Slack, Amazon S3, Komand and Phantom.

An alert can be sent to multiple destinations.

Expand Down
2 changes: 1 addition & 1 deletion manage.py
Original file line number Diff line number Diff line change
Expand Up @@ -108,7 +108,7 @@ def _add_output_subparser(subparsers):
output_parser.add_argument(
'--service',
choices=[
'aws-firehose', 'aws-lambda', 'aws-s3', 'jira', 'pagerduty', 'pagerduty-v2',
'aws-firehose', 'aws-lambda', 'aws-s3', 'jira', 'komand', 'pagerduty', 'pagerduty-v2',
'pagerduty-incident', 'phantom', 'slack'
],
required=True,
Expand Down
82 changes: 82 additions & 0 deletions stream_alert/alert_processor/outputs/komand.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
"""
Copyright 2017-present, Airbnb Inc.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
"""
from collections import OrderedDict
import os

from stream_alert.alert_processor import LOGGER
from stream_alert.alert_processor.outputs.output_base import (
OutputDispatcher,
OutputProperty,
OutputRequestFailure,
StreamAlertOutput
)


@StreamAlertOutput
class KomandOutput(OutputDispatcher):
"""KomandOutput handles all alert dispatching for Komand"""
__service__ = 'komand'

@classmethod
def get_user_defined_properties(cls):
"""Get properties that must be asssigned by the user when configuring a new Komand
output. This should be sensitive or unique information for this use-case that needs
to come from the user.

Every output should return a dict that contains a 'descriptor' with a description of the
integration being configured.

Returns:
OrderedDict: Contains various OutputProperty items
"""
return OrderedDict([
('descriptor',
OutputProperty(description='a short and unique descriptor for this '
'Komand integration')),
('komand_auth_token',
OutputProperty(description='the auth token for this Komand integration. '
'Example: 00000000-0000-0000-0000-000000000000',
mask_input=True,
cred_requirement=True)),
('url',
OutputProperty(description='the endpoint url for this Komand integration. '
'Example: https://YOUR-KOMAND-HOST.com/v2/triggers/00000000-0000-0000-0000-000000000000/events',
mask_input=True,
cred_requirement=True))
])

def dispatch(self, **kwargs):
"""Send alert to Komand

Args:
**kwargs: consists of any combination of the following items:
descriptor (str): Service descriptor (ie: slack channel, pd integration)
alert (dict): Alert relevant to the triggered rule
"""
creds = self._load_creds(kwargs['descriptor'])
if not creds:
return self._log_status(False)

headers = {'Authorization': creds['komand_auth_token']}

LOGGER.debug('sending alert to Komand')

success = False
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

remove this declaration as it's unnecessary

resp = self._post_request(creds['url'], {'data': kwargs['alert']}, headers, False)

success = self._check_http_response(resp)

return self._log_status(success)
7 changes: 7 additions & 0 deletions stream_alert_cli/test.py
Original file line number Diff line number Diff line change
Expand Up @@ -771,6 +771,13 @@ def setup_outputs(self, alert):
lambda_function = parts[-1]
helpers.create_lambda_function(lambda_function,
self.region)
elif service == 'komand':
output_name = '{}/{}'.format(service, descriptor)
creds = {'komand_auth_token': '00000000-0000-0000-0000-000000000000',
'url': 'komand.foo.bar'}
helpers.put_mock_creds(output_name, creds, self.secrets_bucket,
self.region, self.kms_alias)

elif service == 'pagerduty':
output_name = '{}/{}'.format(service, descriptor)
creds = {'service_key': '247b97499078a015cc6c586bc0a92de6'}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
"""
Copyright 2017-present, Airbnb Inc.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
"""
# pylint: disable=protected-access,attribute-defined-outside-init
from mock import call, patch, PropertyMock
from moto import mock_s3, mock_kms
from nose.tools import assert_false, assert_true

from stream_alert.alert_processor.outputs.komand import KomandOutput
from stream_alert_cli.helpers import put_mock_creds
from tests.unit.stream_alert_alert_processor import CONFIG, FUNCTION_NAME, KMS_ALIAS, REGION
from tests.unit.stream_alert_alert_processor.helpers import get_alert, remove_temp_secrets


@mock_s3
@mock_kms
@patch('stream_alert.alert_processor.outputs.output_base.OutputDispatcher.MAX_RETRY_ATTEMPTS', 1)
class TestKomandutput(object):
"""Test class for KomandOutput"""
DESCRIPTOR = 'unit_test_komand'
SERVICE = 'komand'
CREDS = {'url': 'http://komand.foo.bar',
'komand_auth_token': 'mocked_auth_token'}

def setup(self):
"""Setup before each method"""
self._dispatcher = KomandOutput(REGION, FUNCTION_NAME, CONFIG)
remove_temp_secrets()
output_name = self._dispatcher.output_cred_name(self.DESCRIPTOR)
put_mock_creds(output_name, self.CREDS, self._dispatcher.secrets_bucket, REGION, KMS_ALIAS)

@patch('logging.Logger.info')
@patch('requests.get')
@patch('requests.post')
def test_dispatch_existing_container(self, post_mock, get_mock, log_mock):
"""KomandOutput - Dispatch Success"""
post_mock.return_value.status_code = 200

assert_true(self._dispatcher.dispatch(descriptor=self.DESCRIPTOR,
alert=get_alert()))

log_mock.assert_called_with('Successfully sent alert to %s', self.SERVICE)

@patch('logging.Logger.error')
@patch('requests.get')
@patch('requests.post')
def test_dispatch_container_failure(self, post_mock, get_mock, log_mock):
"""KomandOutput - Dispatch Failure"""
post_mock.return_value.status_code = 400
json_error = {'message': 'error message', 'errors': ['error1']}
post_mock.return_value.json.return_value = json_error

assert_false(self._dispatcher.dispatch(descriptor=self.DESCRIPTOR,
alert=get_alert()))

log_mock.assert_called_with('Failed to send alert to %s', self.SERVICE)

@patch('logging.Logger.error')
def test_dispatch_bad_descriptor(self, log_error_mock):
"""KomandOutput - Dispatch Failure, Bad Descriptor"""
assert_false(self._dispatcher.dispatch(descriptor='bad_descriptor',
alert=get_alert()))

log_error_mock.assert_called_with('Failed to send alert to %s', self.SERVICE)
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,7 @@ def test_output_loading():
'aws-lambda',
'aws-s3',
'jira',
'komand',
'pagerduty',
'pagerduty-v2',
'pagerduty-incident',
Expand Down