Skip to content

Commit

Permalink
Feat/alert notify leaked key (#299)
Browse files Browse the repository at this point in the history
* Adding modifications to SRE bot to handle the leaked api event

* Restoring the subject as it was accidentally deleted

* Formatting
  • Loading branch information
sylviamclaughlin authored Oct 25, 2023
1 parent f093589 commit 4c5a54d
Show file tree
Hide file tree
Showing 2 changed files with 167 additions and 0 deletions.
76 changes: 76 additions & 0 deletions app/server/event_handlers/aws.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
import re
import urllib.parse
from commands.utils import log_ops_message
from integrations import google_drive, opsgenie


def parse(payload, client):
Expand All @@ -19,6 +20,8 @@ def parse(payload, client):
blocks = format_auto_mitigation(payload)
elif isinstance(msg, str) and "IAM User" in msg:
blocks = format_new_iam_user(payload)
elif isinstance(msg, str) and "API Key with value token=" in msg:
blocks = format_api_key_detected(payload, client)
else:
blocks = []
log_ops_message(
Expand All @@ -38,6 +41,39 @@ def nested_get(dictionary, keys):
return dictionary


def alert_on_call(product, client, api_key, github_repo):
# get the list of folders
folders = google_drive.list_folders()
# get the folder id for the Product
for folder in folders:
if folder["name"] == product:
folder = folder["id"]
break
# Get folder metadata
folder_metadata = google_drive.list_metadata(folder).get("appProperties", {})
oncall = []
message = ""
private_message = ""

# Get OpsGenie users on call and construct string
if "genie_schedule" in folder_metadata:
for email in opsgenie.get_on_call_users(folder_metadata["genie_schedule"]):
r = client.users_lookupByEmail(email=email)
if r.get("ok"):
oncall.append(r["user"])
message = f"{product} on-call staff "
for user in oncall:
# send a private message to the people on call.
message += f"<@{user['id']}> "
private_message = f"Hello {user['profile']['first_name']}!\nA Notify API key has been leaked and needs to be revoked. 🙀 \nThe key is *{api_key}* and the file is {github_repo}. You can see the message in #internal-sre-alerts to start an incident."
# send the private message
client.chat_postMessage(
channel=user["id"], text=private_message, as_user=True
)
message += "have been notified."
return message


def format_abuse_notification(payload, msg):
regex = r"arn:aws:sns:\w.*:(\d.*):\w.*"
account = re.search(regex, payload.TopicArn).groups()[0]
Expand Down Expand Up @@ -198,3 +234,43 @@ def format_cloudwatch_alarm(msg):
{"type": "section", "text": {"type": "mrkdwn", "text": link}},
]
return blocks


# If the message contains an api key it will be parsed by the format_api_key_detected function.


def format_api_key_detected(payload, client):
msg = payload.Message
regex = r"API Key with value token='(\w.+)' has been detected in url='(\w.+)'"
# extract the api key and the github repo from the message
api_key = re.search(regex, msg).groups()[0]
github_repo = re.search(regex, msg).groups()[1]

# send a private message with the api-key and github repo to the people on call.
on_call_message = alert_on_call("Notify", client, api_key, github_repo)

# Format the message displayed in Slack
return [
{"type": "section", "text": {"type": "mrkdwn", "text": " "}},
{
"type": "header",
"text": {
"type": "plain_text",
"text": "🙀 Notify API Key has been compromised!🔑",
},
},
{
"type": "section",
"text": {
"type": "mrkdwn",
"text": f"Notify API Key *{api_key}* has been committed in github file {github_repo}. The key needs to be revoked!",
},
},
{
"type": "section",
"text": {
"type": "plain_text",
"text": f"{on_call_message}",
},
},
]
91 changes: 91 additions & 0 deletions app/tests/server/event_handlers/test_aws_handler.py
Original file line number Diff line number Diff line change
Expand Up @@ -178,6 +178,81 @@ def test_format_new_iam_user_extracts_the_user_and_inserts_it_into_blocks():
assert "[email protected]" in response[2]["text"]["text"]


@patch("server.event_handlers.aws.format_api_key_detected")
def test_parse_returns_blocks_if_api_key_detected(format_api_key_detected_mock):
# Test that the parse function returns the blocks returned by format_api_key_detected
client = MagicMock()
format_api_key_detected_mock.return_value = ["foo", "bar"]
payload = mock_api_key_detected()
response = aws.parse(payload, client)
assert response == ["foo", "bar"]
format_api_key_detected_mock.assert_called_once_with(payload, client)


@patch("server.event_handlers.aws.alert_on_call")
def test_format_api_key_detected_extracts_the_api_key_and_inserts_it_into_blocks(
alert_on_call_mock,
):
# Test that the format_api_key_detected function extracts the api key properly
client = MagicMock()
payload = mock_api_key_detected()
response = aws.format_api_key_detected(payload, client)
assert "gcntfy-api-key-blah" in response[2]["text"]["text"]


@patch("server.event_handlers.aws.alert_on_call")
def test_format_api_key_detected_extracts_the_url_and_inserts_it_into_blocks(
alert_on_call_mock,
):
# Test that the format_api_key_detected function extracts the url properly
client = MagicMock()
payload = mock_api_key_detected()
response = aws.format_api_key_detected(payload, client)
assert "https://github.com/blah" in response[2]["text"]["text"]


@patch("server.event_handlers.aws.alert_on_call")
def test_format_api_key_detected_extracts_the_on_call_message_and_inserts_it_into_blocks(
alert_on_call_mock,
):
# Test that the format_api_key_detected function extracts the on call message properly
client = MagicMock()
alert_on_call_mock.return_value = "test message"
payload = mock_api_key_detected()
response = aws.format_api_key_detected(payload, client)
assert "test message" in response[3]["text"]["text"]


@patch("integrations.google_drive.get_google_service")
@patch("commands.incident.google_drive.list_folders")
@patch("commands.incident.google_drive.list_metadata")
@patch("integrations.opsgenie.get_on_call_users")
def test_alert_on_call_returns_message(
get_on_call_users_mock,
list_metadata_mock,
google_list_folders_mock,
get_google_service_mock,
):
# Test that the alert_on_call function returns the proper message
client = MagicMock()
product = "test"
api_key = "test_api_key"
github_repo = "test_repo"
google_list_folders_mock.return_value = [
{
"name": "Notify",
"id": 12345,
"appProperties": {"genie_schedule": "test_schedule"},
}
]
list_metadata_mock.return_value = {
"name": "Notify",
"appProperties": {"genie_schedule": "test_schedule"},
}
response = aws.alert_on_call(product, client, api_key, github_repo)
assert "test on-call staff have been notified" in response


def mock_abuse_alert():
return MagicMock(
Type="Notification",
Expand Down Expand Up @@ -253,3 +328,19 @@ def mock_new_iam_user():
SigningCertURL="https://sns.ca-central-1.amazonaws.com/SimpleNotificationService-56e67fcb41f6fec09b0196692625d385.pem",
UnsubscribeURL="https://sns.ca-central-1.amazonaws.com/?Action=Unsubscribe&SubscriptionArn=arn:aws:sns:ca-central-1:412578375350:test-sre-bot:4636a013-5224-4207-91b2-d6d7c7ab7ea7",
)


# Mock the message returned from AWS when a API key has been compromised
def mock_api_key_detected():
return MagicMock(
Type="Notification",
MessageId="1e5f5647g-adb5-5d6f-ab5e-c2e508881361",
TopicArn="arn:aws:sns:ca-central-1:412578375350:test-sre-bot",
Subject="API Key detected",
Message="API Key with value token='gcntfy-api-key-blah' has been detected in url='https://github.com/blah'! This key needs to be revoked asap.",
Timestamp="2023-09-25T20:50:37.868Z",
SignatureVersion="1",
Signature="EXAMPLEO0OA1HN4MIHrtym3N6SWqvotsY4EcG+Ty/wrfZcxpQ3mximWM7ZfoYlzZ8NBh4s1XTPuqbl5efK64TEuPgNWBMKsm5Gc2d8H6hoDpLqAOELGl2/xlvWf2CovLH/KPj8xrSwAgOS9jL4r/EEMdXYb705YMMBudu78gooatU9EpVl+1I2MCP2AW0ZJWrcSwYMqxo9yo7H6coyBRlmTxP97PlELXoqXLfufsfFBjZ0eFycndG5A0YHeue82uLF5fIHGpcTjqNzLF0PXuJoS9xVkGx3X7p+dzmRE4rp/swGyKCqbXvgldPRycuj7GSk3r8HLSfzjqHyThnDqMECA==",
SigningCertURL="https://sns.ca-central-1.amazonaws.com/SimpleNotificationService-56e67fcb41f6fec09b0196692625d385.pem",
UnsubscribeURL="https://sns.ca-central-1.amazonaws.com/?Action=Unsubscribe&SubscriptionArn=arn:aws:sns:ca-central-1:412578375350:test-sre-bot:4636a013-5224-4207-91b2-d6d7c7ab7ea7",
)

0 comments on commit 4c5a54d

Please sign in to comment.