{"action":"created","comment":{"url":"https://api.github.com/repos/airbnb/streamalert/pulls/comments/180466554","pull_request_review_id":110891121,"id":180466554,"diff_hunk":"@@ -0,0 +1,395 @@\n+\"\"\"\n+Copyright 2017-present, Airbnb Inc.\n+\n+Licensed under the Apache License, Version 2.0 (the \"License\");\n+you may not use this file except in compliance with the License.\n+You may obtain a copy of the License at\n+\n+   http://www.apache.org/licenses/LICENSE-2.0\n+\n+Unless required by applicable law or agreed to in writing, software\n+distributed under the License is distributed on an \"AS IS\" BASIS,\n+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n+See the License for the specific language governing permissions and\n+limitations under the License.\n+\"\"\"\n+import csv\n+import StringIO\n+import re\n+\n+import backoff\n+import requests\n+\n+from app_integrations import LOGGER\n+from app_integrations.apps.app_base import StreamAlertApp, AppIntegration\n+\n+\n+class SalesforceAppError(Exception):\n+    \"\"\"Salesforce App Error class\"\"\"\n+    pass\n+\n+@StreamAlertApp\n+class SalesforceApp(AppIntegration):\n+    \"\"\"Salesforce StreamAlert App\n+\n+    Salesforce will audit 5 types of event logs include 'Console', 'Login',\n+    'LoginAs', 'Report' and 'ReportExport' for user access audit and data loss\n+    prevention, although Salesforce provides 42 types of event logs.\n+\n+    Console events:\n+        contain information about the performance and use of Salesforce Consoles.\n+\n+    Login events:\n+        contain details about your org's user login history.\n+\n+    LoginAs events:\n+        contain details about what a Salesforce admin did while logged in as another user.\n+\n+    Report events:\n+        contain information about what happened when a user ran a report and\n+        captures report's size.\n+\n+    ReportExport:\n+        events contain details about reports that a user exported.\n+    \"\"\"\n+    _SALESFORCE_TOKEN_URL = 'https://login.salesforce.com/services/oauth2/token'\n+    _SALESFORCE_QUERY_URL = ('{instance_url}/services/data/v{api_version}/'\n+                             '{query}{start_time}{event_type}')\n+    # Use the Query resource to retrieve log files.\n+    _SALESFORCE_QUERY_FILTERS = ('query?q=SELECT+Id+,+EventType+,+LogFile+,+LogDate+,'\n+                                 '+LogFileLength+FROM+EventLogFile+')\n+\n+    # Log files filters. These typs of events should be auditted for security purpose.\n+    _SALESFORCE_EVENT_TYPES = {'Console', 'Login', 'LoginAs', 'Report', 'ReportExport'}\n+    # Use \"Where\" and \"LogDate\" clause to filter log files which are generated after\n+    # last_timestamp.\n+    _SALESFORCE_CREATE_AFTER = 'WHERE+LogDate+>+{}+'\n+\n+    _TIMEOUT = 15\n+    EXCEPTIONS_TO_BACKOFF = (SalesforceAppError,)\n+    BACKOFF_MAX_RETRIES = 3\n+\n+    def __init__(self, config):\n+        super(SalesforceApp, self).__init__(config)\n+        self._auth_headers = None\n+        self._instance_url = None\n+        self._latest_api_version = 0\n+\n+    @classmethod\n+    def _type(cls):\n+        return 'events'\n+\n+    @classmethod\n+    def service(cls):\n+        return 'salesforce'\n+\n+    @classmethod\n+    def date_formatter(cls):\n+        \"\"\"Salesforce API date format: YYYY-MM-DDTHH:MM:SSZ\"\"\"\n+        return '%Y-%m-%dT%H:%M:%SZ'\n+\n+    def _request_token(self):\n+        \"\"\"Request OAuth token frome salesforce\n+\n+        Meanwhile, it will also get instance url which will be used in futher\n+        requests. The instance url identifies the Salesforce instance to which\n+        API calls should be sent.\n+\n+        Returns:\n+            bool: Returns True if update auth headers and instance url successfully.\n+        \"\"\"\n+        headers = {'Content-Type': 'application/x-www-form-urlencoded'}\n+        # required credentials when request for a token.\n+        data = {\n+            'grant_type': 'password',\n+            'client_id': self._config.auth['client_id'],\n+            'client_secret': self._config.auth['client_secret'],\n+            'username': self._config.auth['username'],\n+            'password': self._config.auth['password']+self._config.auth['security_token'],\n+            'response_type': 'code',\n+            'redirect_uri': self._SALESFORCE_TOKEN_URL\n+        }\n+        success, response = self._make_post_request(self._SALESFORCE_TOKEN_URL, headers, data)\n+\n+        if not success:\n+            return False\n+\n+        if not response or not response.get('access_token') or not response.get('instance_url'):\n+            LOGGER.error('Response invalid generating headers for service \\'%s\\'',\n+                         self.type())\n+            return False\n+\n+        bearer = 'Bearer {}'.format(response.get('access_token'))\n+        self._auth_headers = {\n+            'Content-Type': 'application/json',\n+            'Authorization': bearer\n+        }\n+        self._instance_url = response.get('instance_url')\n+        LOGGER.debug('Successfully obtain OAuth token and instance URL')\n+        return True\n+\n+    def _sleep_seconds(self):\n+        \"\"\"Salesforce doesn't provide rate limit information. We can default it to 0\"\"\"\n+        return 0","path":"app_integrations/apps/salesforce.py","position":133,"original_position":133,"commit_id":"9ef625a77d3a4315499c4a12bbf42fa884a9fd9b","original_commit_id":"9ef625a77d3a4315499c4a12bbf42fa884a9fd9b","user":{"login":"jacknagz","id":11466941,"avatar_url":"https://avatars1.githubusercontent.com/u/11466941?v=4","gravatar_id":"","url":"https://api.github.com/users/jacknagz","html_url":"https://github.com/jacknagz","followers_url":"https://api.github.com/users/jacknagz/followers","following_url":"https://api.github.com/users/jacknagz/following{/other_user}","gists_url":"https://api.github.com/users/jacknagz/gists{/gist_id}","starred_url":"https://api.github.com/users/jacknagz/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/jacknagz/subscriptions","organizations_url":"https://api.github.com/users/jacknagz/orgs","repos_url":"https://api.github.com/users/jacknagz/repos","events_url":"https://api.github.com/users/jacknagz/events{/privacy}","received_events_url":"https://api.github.com/users/jacknagz/received_events","type":"User","site_admin":false},"body":"@chunyong-lin do you see this as something we'll have to tune in the future?","created_at":"2018-04-10T15:32:07Z","updated_at":"2018-04-10T15:32:07Z","html_url":"https://github.com/airbnb/streamalert/pull/671#discussion_r180466554","pull_request_url":"https://api.github.com/repos/airbnb/streamalert/pulls/671","author_association":"CONTRIBUTOR","_links":{"self":{"href":"https://api.github.com/repos/airbnb/streamalert/pulls/comments/180466554"},"html":{"href":"https://github.com/airbnb/streamalert/pull/671#discussion_r180466554"},"pull_request":{"href":"https://api.github.com/repos/airbnb/streamalert/pulls/671"}}},"pull_request":{"url":"https://api.github.com/repos/airbnb/streamalert/pulls/671","id":180388938,"html_url":"https://github.com/airbnb/streamalert/pull/671","diff_url":"https://github.com/airbnb/streamalert/pull/671.diff","patch_url":"https://github.com/airbnb/streamalert/pull/671.patch","issue_url":"https://api.github.com/repos/airbnb/streamalert/issues/671","number":671,"state":"open","locked":false,"title":"[app] salesforce app integration","user":{"login":"chunyong-lin","id":21151436,"avatar_url":"https://avatars1.githubusercontent.com/u/21151436?v=4","gravatar_id":"","url":"https://api.github.com/users/chunyong-lin","html_url":"https://github.com/chunyong-lin","followers_url":"https://api.github.com/users/chunyong-lin/followers","following_url":"https://api.github.com/users/chunyong-lin/following{/other_user}","gists_url":"https://api.github.com/users/chunyong-lin/gists{/gist_id}","starred_url":"https://api.github.com/users/chunyong-lin/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/chunyong-lin/subscriptions","organizations_url":"https://api.github.com/users/chunyong-lin/orgs","repos_url":"https://api.github.com/users/chunyong-lin/repos","events_url":"https://api.github.com/users/chunyong-lin/events{/privacy}","received_events_url":"https://api.github.com/users/chunyong-lin/received_events","type":"User","site_admin":false},"body":"to: @ryandeivert @jacknagz \r\ncc: @airbnb/streamalert-maintainers\r\nsize: large\r\nresolves N/A\r\n\r\n## Background\r\nMany companies are using Salesforce service to support customer relationship management. It is important to audit user access and prevent data loss from security perspective. Salesforce provides [42 types of event](https://developer.salesforce.com/docs/atlas.en-us.208.0.object_reference.meta/object_reference/sforce_api_objects_eventlogfile.htm) in latest API version.\r\n\r\nNew StreamAlert App for Salesforce will collect *Console*, *Login*, *LoginAs*, *Report* and *ReportAs* event logs and send to StreamAlert Rule Processor for further analysis. Later on, we can write rules to alert on any unexpected behaviors.\r\n\r\nTo collect Salesforce logs, we will use [Event Monitoring](https://developer.salesforce.com/docs/atlas.en-us.api_rest.meta/api_rest/using_resources_event_log_files.htm) which is an API only feature. Each organization's event log files are stored in an API object called EventLogFile. \r\n* The `SalesforceApp` will first retrieve a list of EventLogFiles with filters by `LogDate` and `EventType`. \r\n* Then it will send another GET requests to fetch log events by log file locations. \r\n* `SalesforceApp` will segment and forward log events to the rule processor. \r\n\r\n## prerequisite\r\n* A user account in our Salesforce account.\r\n* The security token associated to your user account.\r\n* Create a connected app, following [Salesforce documentation](https://developer.salesforce.com/docs/atlas.en-us.api_rest.meta/api_rest/intro_defining_remote_access_applications.htmhttps://developer.salesforce.com/docs/atlas.en-us.api_rest.meta/api_rest/intro_defining_remote_access_applications.htm). \r\n \r\n## Changes\r\n\r\n* Create a new app for **Salesforce** following the [Developing a New App](https://streamalert.readthedocs.io/app-development.html) documentation. \r\n* Add unit test cases.\r\n\r\n## Testing\r\n* Unit test coverage is 100%!\r\n```\r\n./tests/scripts/unit_tests.sh\r\n...\r\nName                                                    Stmts   Miss  Cover   Missing\r\n-------------------------------------------------------------------------------------\r\n...\r\napp_integrations/apps/salesforce.py                       131      0   100%\r\n...\r\nRan 618 tests in 9.930s\r\n\r\nOK\r\n```\r\n* Tested in testing accounts.\r\n","created_at":"2018-04-09T18:25:23Z","updated_at":"2018-04-10T15:32:07Z","closed_at":null,"merged_at":null,"merge_commit_sha":"8dbc92118695e38ad51679f005ab43e2fe77a3d0","assignee":null,"assignees":[],"requested_reviewers":[],"requested_teams":[],"labels":[],"milestone":null,"commits_url":"https://api.github.com/repos/airbnb/streamalert/pulls/671/commits","review_comments_url":"https://api.github.com/repos/airbnb/streamalert/pulls/671/comments","review_comment_url":"https://api.github.com/repos/airbnb/streamalert/pulls/comments{/number}","comments_url":"https://api.github.com/repos/airbnb/streamalert/issues/671/comments","statuses_url":"https://api.github.com/repos/airbnb/streamalert/statuses/9ef625a77d3a4315499c4a12bbf42fa884a9fd9b","head":{"label":"airbnb:salesforce_app","ref":"salesforce_app","sha":"9ef625a77d3a4315499c4a12bbf42fa884a9fd9b","user":{"login":"airbnb","id":698437,"avatar_url":"https://avatars3.githubusercontent.com/u/698437?v=4","gravatar_id":"","url":"https://api.github.com/users/airbnb","html_url":"https://github.com/airbnb","followers_url":"https://api.github.com/users/airbnb/followers","following_url":"https://api.github.com/users/airbnb/following{/other_user}","gists_url":"https://api.github.com/users/airbnb/gists{/gist_id}","starred_url":"https://api.github.com/users/airbnb/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/airbnb/subscriptions","organizations_url":"https://api.github.com/users/airbnb/orgs","repos_url":"https://api.github.com/users/airbnb/repos","events_url":"https://api.github.com/users/airbnb/events{/privacy}","received_events_url":"https://api.github.com/users/airbnb/received_events","type":"Organization","site_admin":false},"repo":{"id":79683936,"name":"streamalert","full_name":"airbnb/streamalert","owner":{"login":"airbnb","id":698437,"avatar_url":"https://avatars3.githubusercontent.com/u/698437?v=4","gravatar_id":"","url":"https://api.github.com/users/airbnb","html_url":"https://github.com/airbnb","followers_url":"https://api.github.com/users/airbnb/followers","following_url":"https://api.github.com/users/airbnb/following{/other_user}","gists_url":"https://api.github.com/users/airbnb/gists{/gist_id}","starred_url":"https://api.github.com/users/airbnb/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/airbnb/subscriptions","organizations_url":"https://api.github.com/users/airbnb/orgs","repos_url":"https://api.github.com/users/airbnb/repos","events_url":"https://api.github.com/users/airbnb/events{/privacy}","received_events_url":"https://api.github.com/users/airbnb/received_events","type":"Organization","site_admin":false},"private":false,"html_url":"https://github.com/airbnb/streamalert","description":"StreamAlert is a serverless, realtime data analysis framework which empowers you to ingest, analyze, and alert on data from any environment, using datasources and alerting logic you define.","fork":false,"url":"https://api.github.com/repos/airbnb/streamalert","forks_url":"https://api.github.com/repos/airbnb/streamalert/forks","keys_url":"https://api.github.com/repos/airbnb/streamalert/keys{/key_id}","collaborators_url":"https://api.github.com/repos/airbnb/streamalert/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/airbnb/streamalert/teams","hooks_url":"https://api.github.com/repos/airbnb/streamalert/hooks","issue_events_url":"https://api.github.com/repos/airbnb/streamalert/issues/events{/number}","events_url":"https://api.github.com/repos/airbnb/streamalert/events","assignees_url":"https://api.github.com/repos/airbnb/streamalert/assignees{/user}","branches_url":"https://api.github.com/repos/airbnb/streamalert/branches{/branch}","tags_url":"https://api.github.com/repos/airbnb/streamalert/tags","blobs_url":"https://api.github.com/repos/airbnb/streamalert/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/airbnb/streamalert/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/airbnb/streamalert/git/refs{/sha}","trees_url":"https://api.github.com/repos/airbnb/streamalert/git/trees{/sha}","statuses_url":"https://api.github.com/repos/airbnb/streamalert/statuses/{sha}","languages_url":"https://api.github.com/repos/airbnb/streamalert/languages","stargazers_url":"https://api.github.com/repos/airbnb/streamalert/stargazers","contributors_url":"https://api.github.com/repos/airbnb/streamalert/contributors","subscribers_url":"https://api.github.com/repos/airbnb/streamalert/subscribers","subscription_url":"https://api.github.com/repos/airbnb/streamalert/subscription","commits_url":"https://api.github.com/repos/airbnb/streamalert/commits{/sha}","git_commits_url":"https://api.github.com/repos/airbnb/streamalert/git/commits{/sha}","comments_url":"https://api.github.com/repos/airbnb/streamalert/comments{/number}","issue_comment_url":"https://api.github.com/repos/airbnb/streamalert/issues/comments{/number}","contents_url":"https://api.github.com/repos/airbnb/streamalert/contents/{+path}","compare_url":"https://api.github.com/repos/airbnb/streamalert/compare/{base}...{head}","merges_url":"https://api.github.com/repos/airbnb/streamalert/merges","archive_url":"https://api.github.com/repos/airbnb/streamalert/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/airbnb/streamalert/downloads","issues_url":"https://api.github.com/repos/airbnb/streamalert/issues{/number}","pulls_url":"https://api.github.com/repos/airbnb/streamalert/pulls{/number}","milestones_url":"https://api.github.com/repos/airbnb/streamalert/milestones{/number}","notifications_url":"https://api.github.com/repos/airbnb/streamalert/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/airbnb/streamalert/labels{/name}","releases_url":"https://api.github.com/repos/airbnb/streamalert/releases{/id}","deployments_url":"https://api.github.com/repos/airbnb/streamalert/deployments","created_at":"2017-01-22T01:10:56Z","updated_at":"2018-04-10T12:59:16Z","pushed_at":"2018-04-10T07:25:10Z","git_url":"git://github.com/airbnb/streamalert.git","ssh_url":"git@github.com:airbnb/streamalert.git","clone_url":"https://github.com/airbnb/streamalert.git","svn_url":"https://github.com/airbnb/streamalert","homepage":"","size":7975,"stargazers_count":1215,"watchers_count":1215,"language":"Python","has_issues":true,"has_projects":true,"has_downloads":true,"has_wiki":false,"has_pages":false,"forks_count":125,"mirror_url":null,"archived":false,"open_issues_count":56,"license":{"key":"apache-2.0","name":"Apache License 2.0","spdx_id":"Apache-2.0","url":"https://api.github.com/licenses/apache-2.0"},"forks":125,"open_issues":56,"watchers":1215,"default_branch":"master"}},"base":{"label":"airbnb:master","ref":"master","sha":"bcc4f20aba07310abba68d29f9f8dbb6fc8e33ca","user":{"login":"airbnb","id":698437,"avatar_url":"https://avatars3.githubusercontent.com/u/698437?v=4","gravatar_id":"","url":"https://api.github.com/users/airbnb","html_url":"https://github.com/airbnb","followers_url":"https://api.github.com/users/airbnb/followers","following_url":"https://api.github.com/users/airbnb/following{/other_user}","gists_url":"https://api.github.com/users/airbnb/gists{/gist_id}","starred_url":"https://api.github.com/users/airbnb/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/airbnb/subscriptions","organizations_url":"https://api.github.com/users/airbnb/orgs","repos_url":"https://api.github.com/users/airbnb/repos","events_url":"https://api.github.com/users/airbnb/events{/privacy}","received_events_url":"https://api.github.com/users/airbnb/received_events","type":"Organization","site_admin":false},"repo":{"id":79683936,"name":"streamalert","full_name":"airbnb/streamalert","owner":{"login":"airbnb","id":698437,"avatar_url":"https://avatars3.githubusercontent.com/u/698437?v=4","gravatar_id":"","url":"https://api.github.com/users/airbnb","html_url":"https://github.com/airbnb","followers_url":"https://api.github.com/users/airbnb/followers","following_url":"https://api.github.com/users/airbnb/following{/other_user}","gists_url":"https://api.github.com/users/airbnb/gists{/gist_id}","starred_url":"https://api.github.com/users/airbnb/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/airbnb/subscriptions","organizations_url":"https://api.github.com/users/airbnb/orgs","repos_url":"https://api.github.com/users/airbnb/repos","events_url":"https://api.github.com/users/airbnb/events{/privacy}","received_events_url":"https://api.github.com/users/airbnb/received_events","type":"Organization","site_admin":false},"private":false,"html_url":"https://github.com/airbnb/streamalert","description":"StreamAlert is a serverless, realtime data analysis framework which empowers you to ingest, analyze, and alert on data from any environment, using datasources and alerting logic you define.","fork":false,"url":"https://api.github.com/repos/airbnb/streamalert","forks_url":"https://api.github.com/repos/airbnb/streamalert/forks","keys_url":"https://api.github.com/repos/airbnb/streamalert/keys{/key_id}","collaborators_url":"https://api.github.com/repos/airbnb/streamalert/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/airbnb/streamalert/teams","hooks_url":"https://api.github.com/repos/airbnb/streamalert/hooks","issue_events_url":"https://api.github.com/repos/airbnb/streamalert/issues/events{/number}","events_url":"https://api.github.com/repos/airbnb/streamalert/events","assignees_url":"https://api.github.com/repos/airbnb/streamalert/assignees{/user}","branches_url":"https://api.github.com/repos/airbnb/streamalert/branches{/branch}","tags_url":"https://api.github.com/repos/airbnb/streamalert/tags","blobs_url":"https://api.github.com/repos/airbnb/streamalert/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/airbnb/streamalert/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/airbnb/streamalert/git/refs{/sha}","trees_url":"https://api.github.com/repos/airbnb/streamalert/git/trees{/sha}","statuses_url":"https://api.github.com/repos/airbnb/streamalert/statuses/{sha}","languages_url":"https://api.github.com/repos/airbnb/streamalert/languages","stargazers_url":"https://api.github.com/repos/airbnb/streamalert/stargazers","contributors_url":"https://api.github.com/repos/airbnb/streamalert/contributors","subscribers_url":"https://api.github.com/repos/airbnb/streamalert/subscribers","subscription_url":"https://api.github.com/repos/airbnb/streamalert/subscription","commits_url":"https://api.github.com/repos/airbnb/streamalert/commits{/sha}","git_commits_url":"https://api.github.com/repos/airbnb/streamalert/git/commits{/sha}","comments_url":"https://api.github.com/repos/airbnb/streamalert/comments{/number}","issue_comment_url":"https://api.github.com/repos/airbnb/streamalert/issues/comments{/number}","contents_url":"https://api.github.com/repos/airbnb/streamalert/contents/{+path}","compare_url":"https://api.github.com/repos/airbnb/streamalert/compare/{base}...{head}","merges_url":"https://api.github.com/repos/airbnb/streamalert/merges","archive_url":"https://api.github.com/repos/airbnb/streamalert/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/airbnb/streamalert/downloads","issues_url":"https://api.github.com/repos/airbnb/streamalert/issues{/number}","pulls_url":"https://api.github.com/repos/airbnb/streamalert/pulls{/number}","milestones_url":"https://api.github.com/repos/airbnb/streamalert/milestones{/number}","notifications_url":"https://api.github.com/repos/airbnb/streamalert/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/airbnb/streamalert/labels{/name}","releases_url":"https://api.github.com/repos/airbnb/streamalert/releases{/id}","deployments_url":"https://api.github.com/repos/airbnb/streamalert/deployments","created_at":"2017-01-22T01:10:56Z","updated_at":"2018-04-10T12:59:16Z","pushed_at":"2018-04-10T07:25:10Z","git_url":"git://github.com/airbnb/streamalert.git","ssh_url":"git@github.com:airbnb/streamalert.git","clone_url":"https://github.com/airbnb/streamalert.git","svn_url":"https://github.com/airbnb/streamalert","homepage":"","size":7975,"stargazers_count":1215,"watchers_count":1215,"language":"Python","has_issues":true,"has_projects":true,"has_downloads":true,"has_wiki":false,"has_pages":false,"forks_count":125,"mirror_url":null,"archived":false,"open_issues_count":56,"license":{"key":"apache-2.0","name":"Apache License 2.0","spdx_id":"Apache-2.0","url":"https://api.github.com/licenses/apache-2.0"},"forks":125,"open_issues":56,"watchers":1215,"default_branch":"master"}},"_links":{"self":{"href":"https://api.github.com/repos/airbnb/streamalert/pulls/671"},"html":{"href":"https://github.com/airbnb/streamalert/pull/671"},"issue":{"href":"https://api.github.com/repos/airbnb/streamalert/issues/671"},"comments":{"href":"https://api.github.com/repos/airbnb/streamalert/issues/671/comments"},"review_comments":{"href":"https://api.github.com/repos/airbnb/streamalert/pulls/671/comments"},"review_comment":{"href":"https://api.github.com/repos/airbnb/streamalert/pulls/comments{/number}"},"commits":{"href":"https://api.github.com/repos/airbnb/streamalert/pulls/671/commits"},"statuses":{"href":"https://api.github.com/repos/airbnb/streamalert/statuses/9ef625a77d3a4315499c4a12bbf42fa884a9fd9b"}},"author_association":"CONTRIBUTOR"}}
{"actor":{"display_login":"jacknagz"}}