├── .env.example ├── .gitignore ├── .npmignore ├── .travis.yml ├── LICENSE ├── README.md ├── config.js ├── images ├── cloudwatch.png ├── code-deploy.png ├── elastic-beanstalk.png └── elasticache.png ├── index.js ├── package-lock.json ├── package.json ├── scripts ├── deploy.sh └── test.sh └── test ├── context.json ├── sns-autoscaling-event.json ├── sns-cloudwatch-event.json ├── sns-codedeploy-configuration.json ├── sns-codedeploy-event.json ├── sns-codepipeline-event-pipeline-started.json ├── sns-codepipeline-event-stage-failed.json ├── sns-codepipeline-event-stage-started.json ├── sns-codepipeline-event-stage-succeeded.json ├── sns-elastic-beanstalk-event.json ├── sns-elasticache-event.json └── sns-event.json /.env.example: -------------------------------------------------------------------------------- 1 | #KMS_ENCRYPTED_HOOK_URL= you can use ENCRYPTED_HOOK_URL if you want 2 | UNENCRYPTED_HOOK_URL= 3 | AWS_FUNCTION_NAME= 4 | AWS_REGION=eu-west-1 5 | AWS_ROLE="arn:aws:iam::123456789123:role/lambda_exec_role" 6 | 7 | # You can get AWS_ACCESS_KEY_ID and AWS_SECRET_ACCESS_KEY here: https://console.aws.amazon.com/iam/home#/users 8 | # Click on user -> Security credentials -> Access keys -> Create access key 9 | AWS_ACCESS_KEY_ID= 10 | AWS_SECRET_ACCESS_KEY= -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *~ 2 | .env 3 | tmp/deploy.env 4 | npm-debug.log 5 | 6 | build/ 7 | node_modules/ 8 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | *~ 2 | *.png 3 | .env 4 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | 2 | language: node_js 3 | 4 | node_js: 5 | - 6 6 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2016 - Assertible, Inc (Christopher Reichert, Cody Reichert) 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining 4 | a copy of this software and associated documentation files (the 5 | "Software"), to deal in the Software without restriction, including 6 | without limitation the rights to use, copy, modify, merge, publish, 7 | distribute, sublicense, and/or sell copies of the Software, and to 8 | permit persons to whom the Software is furnished to do so, subject to 9 | the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be 12 | included in all copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 15 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 16 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 17 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 18 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 19 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 20 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # lambda-cloudwatch-slack 2 | 3 | An [AWS Lambda](http://aws.amazon.com/lambda/) function for better Slack notifications. 4 | [Check out the blog post](https://assertible.com/blog/npm-package-lambda-cloudwatch-slack). 5 | 6 | [![BuildStatus](https://travis-ci.org/assertible/lambda-cloudwatch-slack.png?branch=master)](https://travis-ci.org/assertible/lambda-cloudwatch-slack) 7 | [![NPM version](https://badge.fury.io/js/lambda-cloudwatch-slack.png)](http://badge.fury.io/js/lambda-cloudwatch-slack) 8 | 9 | 10 | ## Overview 11 | 12 | This function was originally derived from the 13 | [AWS blueprint named `cloudwatch-alarm-to-slack`](https://aws.amazon.com/blogs/aws/new-slack-integration-blueprints-for-aws-lambda/). The 14 | function in this repo improves on the default blueprint in several 15 | ways: 16 | 17 | **Better default formatting for CloudWatch notifications:** 18 | 19 | ![AWS Cloud Notification for Slack](https://github.com/assertible/lambda-cloudwatch-slack/raw/master/images/cloudwatch.png) 20 | 21 | **Support for notifications from Elastic Beanstalk:** 22 | 23 | ![Elastic Beanstalk Slack Notifications](https://github.com/assertible/lambda-cloudwatch-slack/raw/master/images/elastic-beanstalk.png) 24 | 25 | **Support for notifications from Code Deploy:** 26 | 27 | ![AWS CodeDeploy Notifications](https://github.com/assertible/lambda-cloudwatch-slack/raw/master/images/code-deploy.png) 28 | 29 | **Basic support for notifications from ElastiCache:** 30 | 31 | ![AWS ElastiCache Notifications](https://github.com/assertible/lambda-cloudwatch-slack/raw/master/images/elasticache.png) 32 | 33 | **Support for encrypted and unencrypted Slack webhook url:** 34 | 35 | 36 | ## Configuration 37 | 38 | ### 1. Clone this repository 39 | 40 | ### 2. Configure environment variables 41 | 42 | ``` 43 | cp .env.example .env 44 | ``` 45 | 46 | Fill in the variables in the `.env`. 47 | 48 | ### 3. Setup Slack hook 49 | 50 | Follow these steps to configure the webhook in Slack: 51 | 52 | 1. Navigate to 53 | [https://slack.com/services/new](https://slack.com/services/new) 54 | and search for and select "Incoming WebHooks". 55 | 56 | 3. Choose the default channel where messages will be sent and click 57 | "Add Incoming WebHooks Integration". 58 | 59 | 4. Copy the webhook URL from the setup instructions and use it in 60 | the next section. 61 | 62 | 5. Click 'Save Settings' at the bottom of the Slack integration 63 | page. 64 | 65 | #### Encrypted the Slack webhook URL 66 | 67 | If you don't want or need to encrypt your hook URL, you can use the 68 | `UNENCRYPTED_HOOK_URL`. If this variable is specified, the 69 | `KMS_ENCRYPTED_HOOK_URL` is ignored. 70 | 71 | If you **do** want to encrypt your hook URL, follow these steps to 72 | encrypt your Slack hook URL for use in this function: 73 | 74 | 1. Create a KMS key - 75 | http://docs.aws.amazon.com/kms/latest/developerguide/create-keys.html. 76 | 77 | 2. Encrypt the event collector token using the AWS CLI. 78 | $ aws kms encrypt --key-id alias/ --plaintext "" 79 | 80 | Note: You must exclude the protocol from the URL 81 | (e.g. "hooks.slack.com/services/abc123"). 82 | 83 | 3. Copy the base-64 encoded, encrypted key (CiphertextBlob) to the 84 | ENCRYPTED_HOOK_URL variable. 85 | 86 | 4. Give your function's role permission for the kms:Decrypt action. 87 | Example: 88 | 89 | ``` 90 | { 91 | "Version": "2012-10-17", 92 | "Statement": [ 93 | { 94 | "Sid": "Stmt1443036478000", 95 | "Effect": "Allow", 96 | "Action": [ 97 | "kms:Decrypt" 98 | ], 99 | "Resource": [ 100 | "" 101 | ] 102 | } 103 | ] 104 | } 105 | ``` 106 | 107 | 108 | ### 4. Deploy to AWS Lambda 109 | 110 | The final step is to deploy the integration to AWS Lambda: 111 | 112 | npm install 113 | npm run deploy 114 | 115 | ## Tests 116 | 117 | With the variables filled in, you can test the function: 118 | 119 | ``` 120 | npm install 121 | npm test 122 | ``` 123 | 124 | ## License 125 | 126 | MIT License 127 | -------------------------------------------------------------------------------- /config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | kmsEncryptedHookUrl: process.env.KMS_ENCRYPTED_HOOK_URL, // encrypted slack webhook url 3 | unencryptedHookUrl: process.env.UNENCRYPTED_HOOK_URL, // unencrypted slack webhook url 4 | 5 | services: { 6 | elasticbeanstalk: { 7 | // text in the sns message or topicname to match on to process this service type 8 | match_text: "ElasticBeanstalkNotifications" 9 | }, 10 | cloudwatch: { 11 | }, 12 | codepipeline: { 13 | // text in the sns message or topicname to match on to process this service type 14 | match_text: "CodePipelineNotifications" 15 | }, 16 | codedeploy: { 17 | // text in the sns message or topicname to match on to process this service type 18 | match_text: "CodeDeploy" 19 | }, 20 | elasticache: { 21 | // text in the sns message or topicname to match on to process this service type 22 | match_text: "ElastiCache" 23 | }, 24 | autoscaling: { 25 | // text in the sns message or topicname to match on to process this service type 26 | match_text: "AutoScaling" 27 | } 28 | } 29 | 30 | } -------------------------------------------------------------------------------- /images/cloudwatch.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/assertible/lambda-cloudwatch-slack/90dca51f7e67882325601c1f7a62984937f3c92c/images/cloudwatch.png -------------------------------------------------------------------------------- /images/code-deploy.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/assertible/lambda-cloudwatch-slack/90dca51f7e67882325601c1f7a62984937f3c92c/images/code-deploy.png -------------------------------------------------------------------------------- /images/elastic-beanstalk.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/assertible/lambda-cloudwatch-slack/90dca51f7e67882325601c1f7a62984937f3c92c/images/elastic-beanstalk.png -------------------------------------------------------------------------------- /images/elasticache.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/assertible/lambda-cloudwatch-slack/90dca51f7e67882325601c1f7a62984937f3c92c/images/elasticache.png -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | var AWS = require('aws-sdk'); 2 | var url = require('url'); 3 | var https = require('https'); 4 | var config = require('./config'); 5 | var _ = require('lodash'); 6 | var hookUrl; 7 | 8 | var baseSlackMessage = {} 9 | 10 | var postMessage = function(message, callback) { 11 | var body = JSON.stringify(message); 12 | var options = url.parse(hookUrl); 13 | options.method = 'POST'; 14 | options.headers = { 15 | 'Content-Type': 'application/json', 16 | 'Content-Length': Buffer.byteLength(body), 17 | }; 18 | 19 | var postReq = https.request(options, function(res) { 20 | var chunks = []; 21 | res.setEncoding('utf8'); 22 | res.on('data', function(chunk) { 23 | return chunks.push(chunk); 24 | }); 25 | res.on('end', function() { 26 | var body = chunks.join(''); 27 | if (callback) { 28 | callback({ 29 | body: body, 30 | statusCode: res.statusCode, 31 | statusMessage: res.statusMessage 32 | }); 33 | } 34 | }); 35 | return res; 36 | }); 37 | 38 | postReq.write(body); 39 | postReq.end(); 40 | }; 41 | 42 | var handleElasticBeanstalk = function(event, context) { 43 | var timestamp = (new Date(event.Records[0].Sns.Timestamp)).getTime()/1000; 44 | var subject = event.Records[0].Sns.Subject || "AWS Elastic Beanstalk Notification"; 45 | var message = event.Records[0].Sns.Message; 46 | 47 | var stateRed = message.indexOf(" to RED"); 48 | var stateSevere = message.indexOf(" to Severe"); 49 | var butWithErrors = message.indexOf(" but with errors"); 50 | var noPermission = message.indexOf("You do not have permission"); 51 | var failedDeploy = message.indexOf("Failed to deploy application"); 52 | var failedConfig = message.indexOf("Failed to deploy configuration"); 53 | var failedQuota = message.indexOf("Your quota allows for 0 more running instance"); 54 | var unsuccessfulCommand = message.indexOf("Unsuccessful command execution"); 55 | 56 | var stateYellow = message.indexOf(" to YELLOW"); 57 | var stateDegraded = message.indexOf(" to Degraded"); 58 | var stateInfo = message.indexOf(" to Info"); 59 | var removedInstance = message.indexOf("Removed instance "); 60 | var addingInstance = message.indexOf("Adding instance "); 61 | var abortedOperation = message.indexOf(" aborted operation."); 62 | var abortedDeployment = message.indexOf("some instances may have deployed the new application version"); 63 | 64 | var color = "good"; 65 | 66 | if (stateRed != -1 || stateSevere != -1 || butWithErrors != -1 || noPermission != -1 || failedDeploy != -1 || failedConfig != -1 || failedQuota != -1 || unsuccessfulCommand != -1) { 67 | color = "danger"; 68 | } 69 | if (stateYellow != -1 || stateDegraded != -1 || stateInfo != -1 || removedInstance != -1 || addingInstance != -1 || abortedOperation != -1 || abortedDeployment != -1) { 70 | color = "warning"; 71 | } 72 | 73 | var slackMessage = { 74 | text: "*" + subject + "*", 75 | attachments: [ 76 | { 77 | "fields": [ 78 | { "title": "Subject", "value": event.Records[0].Sns.Subject, "short": false}, 79 | { "title": "Message", "value": message, "short": false} 80 | ], 81 | "color": color, 82 | "ts": timestamp 83 | } 84 | ] 85 | }; 86 | 87 | return _.merge(slackMessage, baseSlackMessage); 88 | }; 89 | 90 | var handleCodeDeploy = function(event, context) { 91 | var subject = "AWS CodeDeploy Notification"; 92 | var timestamp = (new Date(event.Records[0].Sns.Timestamp)).getTime()/1000; 93 | var snsSubject = event.Records[0].Sns.Subject; 94 | var message; 95 | var fields = []; 96 | var color = "warning"; 97 | 98 | try { 99 | message = JSON.parse(event.Records[0].Sns.Message); 100 | 101 | if(message.status === "SUCCEEDED"){ 102 | color = "good"; 103 | } else if(message.status === "FAILED"){ 104 | color = "danger"; 105 | } 106 | fields.push({ "title": "Message", "value": snsSubject, "short": false }); 107 | fields.push({ "title": "Deployment Group", "value": message.deploymentGroupName, "short": true }); 108 | fields.push({ "title": "Application", "value": message.applicationName, "short": true }); 109 | fields.push({ 110 | "title": "Status Link", 111 | "value": "https://console.aws.amazon.com/codedeploy/home?region=" + message.region + "#/deployments/" + message.deploymentId, 112 | "short": false 113 | }); 114 | } 115 | catch(e) { 116 | color = "good"; 117 | message = event.Records[0].Sns.Message; 118 | fields.push({ "title": "Message", "value": snsSubject, "short": false }); 119 | fields.push({ "title": "Detail", "value": message, "short": false }); 120 | } 121 | 122 | 123 | var slackMessage = { 124 | text: "*" + subject + "*", 125 | attachments: [ 126 | { 127 | "color": color, 128 | "fields": fields, 129 | "ts": timestamp 130 | } 131 | ] 132 | }; 133 | 134 | return _.merge(slackMessage, baseSlackMessage); 135 | }; 136 | 137 | var handleCodePipeline = function(event, context) { 138 | var subject = "AWS CodePipeline Notification"; 139 | var timestamp = (new Date(event.Records[0].Sns.Timestamp)).getTime()/1000; 140 | var snsSubject = event.Records[0].Sns.Subject; 141 | var message; 142 | var fields = []; 143 | var color = "warning"; 144 | var changeType = ""; 145 | 146 | try { 147 | message = JSON.parse(event.Records[0].Sns.Message); 148 | detailType = message['detail-type']; 149 | 150 | if(detailType === "CodePipeline Pipeline Execution State Change"){ 151 | changeType = ""; 152 | } else if(detailType === "CodePipeline Stage Execution State Change"){ 153 | changeType = "STAGE " + message.detail.stage; 154 | } else if(detailType === "CodePipeline Action Execution State Change"){ 155 | changeType = "ACTION"; 156 | } 157 | 158 | if(message.detail.state === "SUCCEEDED"){ 159 | color = "good"; 160 | } else if(message.detail.state === "FAILED"){ 161 | color = "danger"; 162 | } 163 | header = message.detail.state + ": CodePipeline " + changeType; 164 | fields.push({ "title": "Message", "value": header, "short": false }); 165 | fields.push({ "title": "Pipeline", "value": message.detail.pipeline, "short": true }); 166 | fields.push({ "title": "Region", "value": message.region, "short": true }); 167 | fields.push({ 168 | "title": "Status Link", 169 | "value": "https://console.aws.amazon.com/codepipeline/home?region=" + message.region + "#/view/" + message.detail.pipeline, 170 | "short": false 171 | }); 172 | } 173 | catch(e) { 174 | color = "good"; 175 | message = event.Records[0].Sns.Message; 176 | header = message.detail.state + ": CodePipeline " + message.detail.pipeline; 177 | fields.push({ "title": "Message", "value": header, "short": false }); 178 | fields.push({ "title": "Detail", "value": message, "short": false }); 179 | } 180 | 181 | 182 | var slackMessage = { 183 | text: "*" + subject + "*", 184 | attachments: [ 185 | { 186 | "color": color, 187 | "fields": fields, 188 | "ts": timestamp 189 | } 190 | ] 191 | }; 192 | 193 | return _.merge(slackMessage, baseSlackMessage); 194 | }; 195 | 196 | var handleElasticache = function(event, context) { 197 | var subject = "AWS ElastiCache Notification" 198 | var message = JSON.parse(event.Records[0].Sns.Message); 199 | var timestamp = (new Date(event.Records[0].Sns.Timestamp)).getTime()/1000; 200 | var region = event.Records[0].EventSubscriptionArn.split(":")[3]; 201 | var eventname, nodename; 202 | var color = "good"; 203 | 204 | for(key in message){ 205 | eventname = key; 206 | nodename = message[key]; 207 | break; 208 | } 209 | var slackMessage = { 210 | text: "*" + subject + "*", 211 | attachments: [ 212 | { 213 | "color": color, 214 | "fields": [ 215 | { "title": "Event", "value": eventname.split(":")[1], "short": true }, 216 | { "title": "Node", "value": nodename, "short": true }, 217 | { 218 | "title": "Link to cache node", 219 | "value": "https://console.aws.amazon.com/elasticache/home?region=" + region + "#cache-nodes:id=" + nodename + ";nodes", 220 | "short": false 221 | } 222 | ], 223 | "ts": timestamp 224 | } 225 | ] 226 | }; 227 | return _.merge(slackMessage, baseSlackMessage); 228 | }; 229 | 230 | var handleCloudWatch = function(event, context) { 231 | var timestamp = (new Date(event.Records[0].Sns.Timestamp)).getTime()/1000; 232 | var message = JSON.parse(event.Records[0].Sns.Message); 233 | var region = event.Records[0].EventSubscriptionArn.split(":")[3]; 234 | var subject = "AWS CloudWatch Notification"; 235 | var alarmName = message.AlarmName; 236 | var metricName = message.Trigger.MetricName; 237 | var oldState = message.OldStateValue; 238 | var newState = message.NewStateValue; 239 | var alarmDescription = message.AlarmDescription; 240 | var alarmReason = message.NewStateReason; 241 | var trigger = message.Trigger; 242 | var color = "warning"; 243 | 244 | if (message.NewStateValue === "ALARM") { 245 | color = "danger"; 246 | } else if (message.NewStateValue === "OK") { 247 | color = "good"; 248 | } 249 | 250 | var slackMessage = { 251 | text: "*" + subject + "*", 252 | attachments: [ 253 | { 254 | "color": color, 255 | "fields": [ 256 | { "title": "Alarm Name", "value": alarmName, "short": true }, 257 | { "title": "Alarm Description", "value": alarmDescription, "short": false}, 258 | { 259 | "title": "Trigger", 260 | "value": trigger.Statistic + " " 261 | + metricName + " " 262 | + trigger.ComparisonOperator + " " 263 | + trigger.Threshold + " for " 264 | + trigger.EvaluationPeriods + " period(s) of " 265 | + trigger.Period + " seconds.", 266 | "short": false 267 | }, 268 | { "title": "Old State", "value": oldState, "short": true }, 269 | { "title": "Current State", "value": newState, "short": true }, 270 | { 271 | "title": "Link to Alarm", 272 | "value": "https://console.aws.amazon.com/cloudwatch/home?region=" + region + "#alarm:alarmFilter=ANY;name=" + encodeURIComponent(alarmName), 273 | "short": false 274 | } 275 | ], 276 | "ts": timestamp 277 | } 278 | ] 279 | }; 280 | return _.merge(slackMessage, baseSlackMessage); 281 | }; 282 | 283 | var handleAutoScaling = function(event, context) { 284 | var subject = "AWS AutoScaling Notification" 285 | var message = JSON.parse(event.Records[0].Sns.Message); 286 | var timestamp = (new Date(event.Records[0].Sns.Timestamp)).getTime()/1000; 287 | var eventname, nodename; 288 | var color = "good"; 289 | 290 | for(key in message){ 291 | eventname = key; 292 | nodename = message[key]; 293 | break; 294 | } 295 | var slackMessage = { 296 | text: "*" + subject + "*", 297 | attachments: [ 298 | { 299 | "color": color, 300 | "fields": [ 301 | { "title": "Message", "value": event.Records[0].Sns.Subject, "short": false }, 302 | { "title": "Description", "value": message.Description, "short": false }, 303 | { "title": "Event", "value": message.Event, "short": false }, 304 | { "title": "Cause", "value": message.Cause, "short": false } 305 | 306 | ], 307 | "ts": timestamp 308 | } 309 | ] 310 | }; 311 | return _.merge(slackMessage, baseSlackMessage); 312 | }; 313 | 314 | var handleCatchAll = function(event, context) { 315 | 316 | var record = event.Records[0] 317 | var subject = record.Sns.Subject 318 | var timestamp = new Date(record.Sns.Timestamp).getTime() / 1000; 319 | var message = JSON.parse(record.Sns.Message) 320 | var color = "warning"; 321 | 322 | if (message.NewStateValue === "ALARM") { 323 | color = "danger"; 324 | } else if (message.NewStateValue === "OK") { 325 | color = "good"; 326 | } 327 | 328 | // Add all of the values from the event message to the Slack message description 329 | var description = "" 330 | for(key in message) { 331 | 332 | var renderedMessage = typeof message[key] === 'object' 333 | ? JSON.stringify(message[key]) 334 | : message[key] 335 | 336 | description = description + "\n" + key + ": " + renderedMessage 337 | } 338 | 339 | var slackMessage = { 340 | text: "*" + subject + "*", 341 | attachments: [ 342 | { 343 | "color": color, 344 | "fields": [ 345 | { "title": "Message", "value": record.Sns.Subject, "short": false }, 346 | { "title": "Description", "value": description, "short": false } 347 | ], 348 | "ts": timestamp 349 | } 350 | ] 351 | } 352 | 353 | return _.merge(slackMessage, baseSlackMessage); 354 | } 355 | 356 | var processEvent = function(event, context) { 357 | console.log("sns received:" + JSON.stringify(event, null, 2)); 358 | var slackMessage = null; 359 | var eventSubscriptionArn = event.Records[0].EventSubscriptionArn; 360 | var eventSnsSubject = event.Records[0].Sns.Subject || 'no subject'; 361 | var eventSnsMessageRaw = event.Records[0].Sns.Message; 362 | var eventSnsMessage = null; 363 | 364 | try { 365 | eventSnsMessage = JSON.parse(eventSnsMessageRaw); 366 | } 367 | catch (e) { 368 | } 369 | 370 | if(eventSubscriptionArn.indexOf(config.services.codepipeline.match_text) > -1 || eventSnsSubject.indexOf(config.services.codepipeline.match_text) > -1 || eventSnsMessageRaw.indexOf(config.services.codepipeline.match_text) > -1){ 371 | console.log("processing codepipeline notification"); 372 | slackMessage = handleCodePipeline(event,context) 373 | } 374 | else if(eventSubscriptionArn.indexOf(config.services.elasticbeanstalk.match_text) > -1 || eventSnsSubject.indexOf(config.services.elasticbeanstalk.match_text) > -1 || eventSnsMessageRaw.indexOf(config.services.elasticbeanstalk.match_text) > -1){ 375 | console.log("processing elasticbeanstalk notification"); 376 | slackMessage = handleElasticBeanstalk(event,context) 377 | } 378 | else if(eventSnsMessage && 'AlarmName' in eventSnsMessage && 'AlarmDescription' in eventSnsMessage){ 379 | console.log("processing cloudwatch notification"); 380 | slackMessage = handleCloudWatch(event,context); 381 | } 382 | else if(eventSubscriptionArn.indexOf(config.services.codedeploy.match_text) > -1 || eventSnsSubject.indexOf(config.services.codedeploy.match_text) > -1 || eventSnsMessageRaw.indexOf(config.services.codedeploy.match_text) > -1){ 383 | console.log("processing codedeploy notification"); 384 | slackMessage = handleCodeDeploy(event,context); 385 | } 386 | else if(eventSubscriptionArn.indexOf(config.services.elasticache.match_text) > -1 || eventSnsSubject.indexOf(config.services.elasticache.match_text) > -1 || eventSnsMessageRaw.indexOf(config.services.elasticache.match_text) > -1){ 387 | console.log("processing elasticache notification"); 388 | slackMessage = handleElasticache(event,context); 389 | } 390 | else if(eventSubscriptionArn.indexOf(config.services.autoscaling.match_text) > -1 || eventSnsSubject.indexOf(config.services.autoscaling.match_text) > -1 || eventSnsMessageRaw.indexOf(config.services.autoscaling.match_text) > -1){ 391 | console.log("processing autoscaling notification"); 392 | slackMessage = handleAutoScaling(event, context); 393 | } 394 | else{ 395 | slackMessage = handleCatchAll(event, context); 396 | } 397 | 398 | postMessage(slackMessage, function(response) { 399 | if (response.statusCode < 400) { 400 | console.info('message posted successfully'); 401 | context.succeed(); 402 | } else if (response.statusCode < 500) { 403 | console.error("error posting message to slack API: " + response.statusCode + " - " + response.statusMessage); 404 | // Don't retry because the error is due to a problem with the request 405 | context.succeed(); 406 | } else { 407 | // Let Lambda retry 408 | context.fail("server error when processing message: " + response.statusCode + " - " + response.statusMessage); 409 | } 410 | }); 411 | }; 412 | 413 | exports.handler = function(event, context) { 414 | if (hookUrl) { 415 | processEvent(event, context); 416 | } else if (config.unencryptedHookUrl) { 417 | hookUrl = config.unencryptedHookUrl; 418 | processEvent(event, context); 419 | } else if (config.kmsEncryptedHookUrl && config.kmsEncryptedHookUrl !== '') { 420 | var encryptedBuf = new Buffer(config.kmsEncryptedHookUrl, 'base64'); 421 | var cipherText = { CiphertextBlob: encryptedBuf }; 422 | var kms = new AWS.KMS(); 423 | 424 | kms.decrypt(cipherText, function(err, data) { 425 | if (err) { 426 | console.log("decrypt error: " + err); 427 | processEvent(event, context); 428 | } else { 429 | hookUrl = "https://" + data.Plaintext.toString('ascii'); 430 | processEvent(event, context); 431 | } 432 | }); 433 | } else { 434 | context.fail('hook url has not been set.'); 435 | } 436 | }; 437 | -------------------------------------------------------------------------------- /package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "lambda-cloudwatch-slack", 3 | "version": "0.4.0", 4 | "lockfileVersion": 1, 5 | "requires": true, 6 | "dependencies": { 7 | "@types/continuation-local-storage": { 8 | "version": "3.2.2", 9 | "resolved": "https://registry.npmjs.org/@types/continuation-local-storage/-/continuation-local-storage-3.2.2.tgz", 10 | "integrity": "sha512-aItm+aYPJ4rT1cHmAxO+OdWjSviQ9iB5UKb5f0Uvgln0N4hS2mcDodHtPiqicYBXViUYhqyBjhA5uyOcT+S34Q==", 11 | "dev": true, 12 | "requires": { 13 | "@types/node": "*" 14 | } 15 | }, 16 | "@types/node": { 17 | "version": "13.13.4", 18 | "resolved": "https://registry.npmjs.org/@types/node/-/node-13.13.4.tgz", 19 | "integrity": "sha512-x26ur3dSXgv5AwKS0lNfbjpCakGIduWU1DU91Zz58ONRWrIKGunmZBNv4P7N+e27sJkiGDsw/3fT4AtsqQBrBA==", 20 | "dev": true 21 | }, 22 | "agent-base": { 23 | "version": "4.3.0", 24 | "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-4.3.0.tgz", 25 | "integrity": "sha512-salcGninV0nPrwpGNn4VTXBb1SOuXQBiqbrNXoeizJsHrsL6ERFM2Ne3JUSBWRE6aeNJI2ROP/WEEIDUiDe3cg==", 26 | "dev": true, 27 | "requires": { 28 | "es6-promisify": "^5.0.0" 29 | } 30 | }, 31 | "archiver": { 32 | "version": "3.1.1", 33 | "resolved": "https://registry.npmjs.org/archiver/-/archiver-3.1.1.tgz", 34 | "integrity": "sha512-5Hxxcig7gw5Jod/8Gq0OneVgLYET+oNHcxgWItq4TbhOzRLKNAFUb9edAftiMKXvXfCB0vbGrJdZDNq0dWMsxg==", 35 | "dev": true, 36 | "requires": { 37 | "archiver-utils": "^2.1.0", 38 | "async": "^2.6.3", 39 | "buffer-crc32": "^0.2.1", 40 | "glob": "^7.1.4", 41 | "readable-stream": "^3.4.0", 42 | "tar-stream": "^2.1.0", 43 | "zip-stream": "^2.1.2" 44 | } 45 | }, 46 | "archiver-utils": { 47 | "version": "2.1.0", 48 | "resolved": "https://registry.npmjs.org/archiver-utils/-/archiver-utils-2.1.0.tgz", 49 | "integrity": "sha512-bEL/yUb/fNNiNTuUz979Z0Yg5L+LzLxGJz8x79lYmR54fmTIb6ob/hNQgkQnIUDWIFjZVQwl9Xs356I6BAMHfw==", 50 | "dev": true, 51 | "requires": { 52 | "glob": "^7.1.4", 53 | "graceful-fs": "^4.2.0", 54 | "lazystream": "^1.0.0", 55 | "lodash.defaults": "^4.2.0", 56 | "lodash.difference": "^4.5.0", 57 | "lodash.flatten": "^4.4.0", 58 | "lodash.isplainobject": "^4.0.6", 59 | "lodash.union": "^4.6.0", 60 | "normalize-path": "^3.0.0", 61 | "readable-stream": "^2.0.0" 62 | }, 63 | "dependencies": { 64 | "readable-stream": { 65 | "version": "2.3.7", 66 | "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", 67 | "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", 68 | "dev": true, 69 | "requires": { 70 | "core-util-is": "~1.0.0", 71 | "inherits": "~2.0.3", 72 | "isarray": "~1.0.0", 73 | "process-nextick-args": "~2.0.0", 74 | "safe-buffer": "~5.1.1", 75 | "string_decoder": "~1.1.1", 76 | "util-deprecate": "~1.0.1" 77 | } 78 | } 79 | } 80 | }, 81 | "ast-types": { 82 | "version": "0.13.3", 83 | "resolved": "https://registry.npmjs.org/ast-types/-/ast-types-0.13.3.tgz", 84 | "integrity": "sha512-XTZ7xGML849LkQP86sWdQzfhwbt3YwIO6MqbX9mUNYY98VKaaVZP7YNNm70IpwecbkkxmfC5IYAzOQ/2p29zRA==", 85 | "dev": true 86 | }, 87 | "async": { 88 | "version": "2.6.3", 89 | "resolved": "https://registry.npmjs.org/async/-/async-2.6.3.tgz", 90 | "integrity": "sha512-zflvls11DCy+dQWzTW2dzuilv8Z5X/pjfmZOWba6TNIVDm+2UDaJmXSOXlasHKfNBs8oo3M0aT50fDEWfKZjXg==", 91 | "dev": true, 92 | "requires": { 93 | "lodash": "^4.17.14" 94 | }, 95 | "dependencies": { 96 | "lodash": { 97 | "version": "4.17.15", 98 | "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.15.tgz", 99 | "integrity": "sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A==", 100 | "dev": true 101 | } 102 | } 103 | }, 104 | "async-listener": { 105 | "version": "0.6.10", 106 | "resolved": "https://registry.npmjs.org/async-listener/-/async-listener-0.6.10.tgz", 107 | "integrity": "sha512-gpuo6xOyF4D5DE5WvyqZdPA3NGhiT6Qf07l7DCB0wwDEsLvDIbCr6j9S5aj5Ch96dLace5tXVzWBZkxU/c5ohw==", 108 | "dev": true, 109 | "requires": { 110 | "semver": "^5.3.0", 111 | "shimmer": "^1.1.0" 112 | } 113 | }, 114 | "atomic-batcher": { 115 | "version": "1.0.2", 116 | "resolved": "https://registry.npmjs.org/atomic-batcher/-/atomic-batcher-1.0.2.tgz", 117 | "integrity": "sha1-0WkB0QzOxZUWwZe5zNiTBom4E7Q=", 118 | "dev": true 119 | }, 120 | "aws-sdk": { 121 | "version": "2.331.0", 122 | "resolved": "https://registry.npmjs.org/aws-sdk/-/aws-sdk-2.331.0.tgz", 123 | "integrity": "sha512-+Kd0wUxoLFUOM3+pbKcPLSGIXsXCaFBaneUe6xFr/Nkn8YAwkH05ufSVdK6Q1AjdNnc1sR0HNV8R18dM8riXOQ==", 124 | "requires": { 125 | "buffer": "4.9.1", 126 | "events": "1.1.1", 127 | "ieee754": "1.1.8", 128 | "jmespath": "0.15.0", 129 | "querystring": "0.2.0", 130 | "sax": "1.2.1", 131 | "url": "0.10.3", 132 | "uuid": "3.1.0", 133 | "xml2js": "0.4.19" 134 | }, 135 | "dependencies": { 136 | "url": { 137 | "version": "0.10.3", 138 | "resolved": "https://registry.npmjs.org/url/-/url-0.10.3.tgz", 139 | "integrity": "sha1-Ah5NnHcF8hu/N9A861h2dAJ3TGQ=", 140 | "requires": { 141 | "punycode": "1.3.2", 142 | "querystring": "0.2.0" 143 | } 144 | } 145 | } 146 | }, 147 | "aws-xray-sdk-core": { 148 | "version": "2.5.0", 149 | "resolved": "https://registry.npmjs.org/aws-xray-sdk-core/-/aws-xray-sdk-core-2.5.0.tgz", 150 | "integrity": "sha512-qe60bv0kn5KY6gAIF88TPCOIdu/A3dTmcKISj+kE4OH02eF6kMm1ctL7OgoBAasnsDNSn0VMLhIaA1izgoWuxA==", 151 | "dev": true, 152 | "requires": { 153 | "@types/continuation-local-storage": "*", 154 | "atomic-batcher": "^1.0.2", 155 | "aws-sdk": "^2.304.0", 156 | "continuation-local-storage": "^3.2.0", 157 | "date-fns": "^1.29.0", 158 | "pkginfo": "^0.4.0", 159 | "semver": "^5.3.0", 160 | "winston": "^2.4.4" 161 | } 162 | }, 163 | "balanced-match": { 164 | "version": "1.0.0", 165 | "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", 166 | "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=", 167 | "dev": true 168 | }, 169 | "base64-js": { 170 | "version": "1.3.0", 171 | "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.3.0.tgz", 172 | "integrity": "sha512-ccav/yGvoa80BQDljCxsmmQ3Xvx60/UpBIij5QN21W3wBi/hhIC9OoO+KLpu9IJTS9j4DRVJ3aDDF9cMSoa2lw==" 173 | }, 174 | "bl": { 175 | "version": "4.0.2", 176 | "resolved": "https://registry.npmjs.org/bl/-/bl-4.0.2.tgz", 177 | "integrity": "sha512-j4OH8f6Qg2bGuWfRiltT2HYGx0e1QcBTrK9KAHNMwMZdQnDZFk0ZSYIpADjYCB3U12nicC5tVJwSIhwOWjb4RQ==", 178 | "dev": true, 179 | "requires": { 180 | "buffer": "^5.5.0", 181 | "inherits": "^2.0.4", 182 | "readable-stream": "^3.4.0" 183 | }, 184 | "dependencies": { 185 | "buffer": { 186 | "version": "5.6.0", 187 | "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.6.0.tgz", 188 | "integrity": "sha512-/gDYp/UtU0eA1ys8bOs9J6a+E/KWIY+DZ+Q2WESNUA0jFRsJOc0SNUO6xJ5SGA1xueg3NL65W6s+NY5l9cunuw==", 189 | "dev": true, 190 | "requires": { 191 | "base64-js": "^1.0.2", 192 | "ieee754": "^1.1.4" 193 | } 194 | } 195 | } 196 | }, 197 | "brace-expansion": { 198 | "version": "1.1.11", 199 | "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", 200 | "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", 201 | "dev": true, 202 | "requires": { 203 | "balanced-match": "^1.0.0", 204 | "concat-map": "0.0.1" 205 | } 206 | }, 207 | "buffer": { 208 | "version": "4.9.1", 209 | "resolved": "https://registry.npmjs.org/buffer/-/buffer-4.9.1.tgz", 210 | "integrity": "sha1-bRu2AbB6TvztlwlBMgkwJ8lbwpg=", 211 | "requires": { 212 | "base64-js": "^1.0.2", 213 | "ieee754": "^1.1.4", 214 | "isarray": "^1.0.0" 215 | } 216 | }, 217 | "buffer-crc32": { 218 | "version": "0.2.13", 219 | "resolved": "https://registry.npmjs.org/buffer-crc32/-/buffer-crc32-0.2.13.tgz", 220 | "integrity": "sha1-DTM+PwDqxQqhRUq9MO+MKl2ackI=", 221 | "dev": true 222 | }, 223 | "bytes": { 224 | "version": "3.1.0", 225 | "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.0.tgz", 226 | "integrity": "sha512-zauLjrfCG+xvoyaqLoV8bLVXXNGC4JqlxFCutSDWA6fJrTo2ZuvLYTqZ7aHBLZSMOopbzwv8f+wZcVzfVTI2Dg==", 227 | "dev": true 228 | }, 229 | "co": { 230 | "version": "4.6.0", 231 | "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz", 232 | "integrity": "sha1-bqa989hTrlTMuOR7+gvz+QMfsYQ=", 233 | "dev": true 234 | }, 235 | "colors": { 236 | "version": "1.0.3", 237 | "resolved": "https://registry.npmjs.org/colors/-/colors-1.0.3.tgz", 238 | "integrity": "sha1-BDP0TYCWgP3rYO0mDxsMJi6CpAs=", 239 | "dev": true 240 | }, 241 | "commander": { 242 | "version": "4.1.1", 243 | "resolved": "https://registry.npmjs.org/commander/-/commander-4.1.1.tgz", 244 | "integrity": "sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==", 245 | "dev": true 246 | }, 247 | "compress-commons": { 248 | "version": "2.1.1", 249 | "resolved": "https://registry.npmjs.org/compress-commons/-/compress-commons-2.1.1.tgz", 250 | "integrity": "sha512-eVw6n7CnEMFzc3duyFVrQEuY1BlHR3rYsSztyG32ibGMW722i3C6IizEGMFmfMU+A+fALvBIwxN3czffTcdA+Q==", 251 | "dev": true, 252 | "requires": { 253 | "buffer-crc32": "^0.2.13", 254 | "crc32-stream": "^3.0.1", 255 | "normalize-path": "^3.0.0", 256 | "readable-stream": "^2.3.6" 257 | }, 258 | "dependencies": { 259 | "readable-stream": { 260 | "version": "2.3.7", 261 | "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", 262 | "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", 263 | "dev": true, 264 | "requires": { 265 | "core-util-is": "~1.0.0", 266 | "inherits": "~2.0.3", 267 | "isarray": "~1.0.0", 268 | "process-nextick-args": "~2.0.0", 269 | "safe-buffer": "~5.1.1", 270 | "string_decoder": "~1.1.1", 271 | "util-deprecate": "~1.0.1" 272 | } 273 | } 274 | } 275 | }, 276 | "concat-map": { 277 | "version": "0.0.1", 278 | "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", 279 | "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", 280 | "dev": true 281 | }, 282 | "continuation-local-storage": { 283 | "version": "3.2.1", 284 | "resolved": "https://registry.npmjs.org/continuation-local-storage/-/continuation-local-storage-3.2.1.tgz", 285 | "integrity": "sha512-jx44cconVqkCEEyLSKWwkvUXwO561jXMa3LPjTPsm5QR22PA0/mhe33FT4Xb5y74JDvt/Cq+5lm8S8rskLv9ZA==", 286 | "dev": true, 287 | "requires": { 288 | "async-listener": "^0.6.0", 289 | "emitter-listener": "^1.1.1" 290 | } 291 | }, 292 | "core-util-is": { 293 | "version": "1.0.2", 294 | "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", 295 | "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=", 296 | "dev": true 297 | }, 298 | "crc": { 299 | "version": "3.8.0", 300 | "resolved": "https://registry.npmjs.org/crc/-/crc-3.8.0.tgz", 301 | "integrity": "sha512-iX3mfgcTMIq3ZKLIsVFAbv7+Mc10kxabAGQb8HvjA1o3T1PIYprbakQ65d3I+2HGHt6nSKkM9PYjgoJO2KcFBQ==", 302 | "dev": true, 303 | "requires": { 304 | "buffer": "^5.1.0" 305 | }, 306 | "dependencies": { 307 | "buffer": { 308 | "version": "5.6.0", 309 | "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.6.0.tgz", 310 | "integrity": "sha512-/gDYp/UtU0eA1ys8bOs9J6a+E/KWIY+DZ+Q2WESNUA0jFRsJOc0SNUO6xJ5SGA1xueg3NL65W6s+NY5l9cunuw==", 311 | "dev": true, 312 | "requires": { 313 | "base64-js": "^1.0.2", 314 | "ieee754": "^1.1.4" 315 | } 316 | } 317 | } 318 | }, 319 | "crc32-stream": { 320 | "version": "3.0.1", 321 | "resolved": "https://registry.npmjs.org/crc32-stream/-/crc32-stream-3.0.1.tgz", 322 | "integrity": "sha512-mctvpXlbzsvK+6z8kJwSJ5crm7yBwrQMTybJzMw1O4lLGJqjlDCXY2Zw7KheiA6XBEcBmfLx1D88mjRGVJtY9w==", 323 | "dev": true, 324 | "requires": { 325 | "crc": "^3.4.4", 326 | "readable-stream": "^3.4.0" 327 | } 328 | }, 329 | "cycle": { 330 | "version": "1.0.3", 331 | "resolved": "https://registry.npmjs.org/cycle/-/cycle-1.0.3.tgz", 332 | "integrity": "sha1-IegLK+hYD5i0aPN5QwZisEbDStI=", 333 | "dev": true 334 | }, 335 | "data-uri-to-buffer": { 336 | "version": "1.2.0", 337 | "resolved": "https://registry.npmjs.org/data-uri-to-buffer/-/data-uri-to-buffer-1.2.0.tgz", 338 | "integrity": "sha512-vKQ9DTQPN1FLYiiEEOQ6IBGFqvjCa5rSK3cWMy/Nespm5d/x3dGFT9UBZnkLxCwua/IXBi2TYnwTEpsOvhC4UQ==", 339 | "dev": true 340 | }, 341 | "date-fns": { 342 | "version": "1.30.1", 343 | "resolved": "https://registry.npmjs.org/date-fns/-/date-fns-1.30.1.tgz", 344 | "integrity": "sha512-hBSVCvSmWC+QypYObzwGOd9wqdDpOt+0wl0KbU+R+uuZBS1jN8VsD1ss3irQDknRj5NvxiTF6oj/nDRnN/UQNw==", 345 | "dev": true 346 | }, 347 | "debug": { 348 | "version": "4.1.1", 349 | "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", 350 | "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", 351 | "dev": true, 352 | "requires": { 353 | "ms": "^2.1.1" 354 | } 355 | }, 356 | "deep-is": { 357 | "version": "0.1.3", 358 | "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.3.tgz", 359 | "integrity": "sha1-s2nW+128E+7PUk+RsHD+7cNXzzQ=", 360 | "dev": true 361 | }, 362 | "degenerator": { 363 | "version": "1.0.4", 364 | "resolved": "https://registry.npmjs.org/degenerator/-/degenerator-1.0.4.tgz", 365 | "integrity": "sha1-/PSQo37OJmRk2cxDGrmMWBnO0JU=", 366 | "dev": true, 367 | "requires": { 368 | "ast-types": "0.x.x", 369 | "escodegen": "1.x.x", 370 | "esprima": "3.x.x" 371 | } 372 | }, 373 | "depd": { 374 | "version": "1.1.2", 375 | "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz", 376 | "integrity": "sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak=", 377 | "dev": true 378 | }, 379 | "dotenv": { 380 | "version": "8.2.0", 381 | "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-8.2.0.tgz", 382 | "integrity": "sha512-8sJ78ElpbDJBHNeBzUbUVLsqKdccaa/BXF1uPTw3GrvQTBgrQrtObr2mUrE38vzYd8cEv+m/JBfDLioYcfXoaw==", 383 | "dev": true 384 | }, 385 | "emitter-listener": { 386 | "version": "1.1.2", 387 | "resolved": "https://registry.npmjs.org/emitter-listener/-/emitter-listener-1.1.2.tgz", 388 | "integrity": "sha512-Bt1sBAGFHY9DKY+4/2cV6izcKJUf5T7/gkdmkxzX/qv9CcGH8xSwVRW5mtX03SWJtRTWSOpzCuWN9rBFYZepZQ==", 389 | "dev": true, 390 | "requires": { 391 | "shimmer": "^1.2.0" 392 | } 393 | }, 394 | "end-of-stream": { 395 | "version": "1.4.4", 396 | "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz", 397 | "integrity": "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==", 398 | "dev": true, 399 | "requires": { 400 | "once": "^1.4.0" 401 | } 402 | }, 403 | "es6-promise": { 404 | "version": "4.2.8", 405 | "resolved": "https://registry.npmjs.org/es6-promise/-/es6-promise-4.2.8.tgz", 406 | "integrity": "sha512-HJDGx5daxeIvxdBxvG2cb9g4tEvwIk3i8+nhX0yGrYmZUzbkdg8QbDevheDB8gd0//uPj4c1EQua8Q+MViT0/w==", 407 | "dev": true 408 | }, 409 | "es6-promisify": { 410 | "version": "5.0.0", 411 | "resolved": "https://registry.npmjs.org/es6-promisify/-/es6-promisify-5.0.0.tgz", 412 | "integrity": "sha1-UQnWLz5W6pZ8S2NQWu8IKRyKUgM=", 413 | "dev": true, 414 | "requires": { 415 | "es6-promise": "^4.0.3" 416 | } 417 | }, 418 | "escodegen": { 419 | "version": "1.14.1", 420 | "resolved": "https://registry.npmjs.org/escodegen/-/escodegen-1.14.1.tgz", 421 | "integrity": "sha512-Bmt7NcRySdIfNPfU2ZoXDrrXsG9ZjvDxcAlMfDUgRBjLOWTuIACXPBFJH7Z+cLb40JeQco5toikyc9t9P8E9SQ==", 422 | "dev": true, 423 | "requires": { 424 | "esprima": "^4.0.1", 425 | "estraverse": "^4.2.0", 426 | "esutils": "^2.0.2", 427 | "optionator": "^0.8.1", 428 | "source-map": "~0.6.1" 429 | }, 430 | "dependencies": { 431 | "esprima": { 432 | "version": "4.0.1", 433 | "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", 434 | "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", 435 | "dev": true 436 | } 437 | } 438 | }, 439 | "esprima": { 440 | "version": "3.1.3", 441 | "resolved": "https://registry.npmjs.org/esprima/-/esprima-3.1.3.tgz", 442 | "integrity": "sha1-/cpRzuYTOJXjyI1TXOSdv/YqRjM=", 443 | "dev": true 444 | }, 445 | "estraverse": { 446 | "version": "4.3.0", 447 | "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", 448 | "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", 449 | "dev": true 450 | }, 451 | "esutils": { 452 | "version": "2.0.3", 453 | "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", 454 | "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", 455 | "dev": true 456 | }, 457 | "events": { 458 | "version": "1.1.1", 459 | "resolved": "http://registry.npmjs.org/events/-/events-1.1.1.tgz", 460 | "integrity": "sha1-nr23Y1rQmccNzEwqH1AEKI6L2SQ=" 461 | }, 462 | "extend": { 463 | "version": "3.0.2", 464 | "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", 465 | "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==", 466 | "dev": true 467 | }, 468 | "eyes": { 469 | "version": "0.1.8", 470 | "resolved": "https://registry.npmjs.org/eyes/-/eyes-0.1.8.tgz", 471 | "integrity": "sha1-Ys8SAjTGg3hdkCNIqADvPgzCC8A=", 472 | "dev": true 473 | }, 474 | "fast-levenshtein": { 475 | "version": "2.0.6", 476 | "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", 477 | "integrity": "sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc=", 478 | "dev": true 479 | }, 480 | "file-uri-to-path": { 481 | "version": "1.0.0", 482 | "resolved": "https://registry.npmjs.org/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz", 483 | "integrity": "sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw==", 484 | "dev": true 485 | }, 486 | "fs-constants": { 487 | "version": "1.0.0", 488 | "resolved": "https://registry.npmjs.org/fs-constants/-/fs-constants-1.0.0.tgz", 489 | "integrity": "sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==", 490 | "dev": true 491 | }, 492 | "fs-extra": { 493 | "version": "8.1.0", 494 | "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-8.1.0.tgz", 495 | "integrity": "sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g==", 496 | "dev": true, 497 | "requires": { 498 | "graceful-fs": "^4.2.0", 499 | "jsonfile": "^4.0.0", 500 | "universalify": "^0.1.0" 501 | } 502 | }, 503 | "fs.realpath": { 504 | "version": "1.0.0", 505 | "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", 506 | "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", 507 | "dev": true 508 | }, 509 | "ftp": { 510 | "version": "0.3.10", 511 | "resolved": "https://registry.npmjs.org/ftp/-/ftp-0.3.10.tgz", 512 | "integrity": "sha1-kZfYYa2BQvPmPVqDv+TFn3MwiF0=", 513 | "dev": true, 514 | "requires": { 515 | "readable-stream": "1.1.x", 516 | "xregexp": "2.0.0" 517 | }, 518 | "dependencies": { 519 | "isarray": { 520 | "version": "0.0.1", 521 | "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", 522 | "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=", 523 | "dev": true 524 | }, 525 | "readable-stream": { 526 | "version": "1.1.14", 527 | "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.1.14.tgz", 528 | "integrity": "sha1-fPTFTvZI44EwhMY23SB54WbAgdk=", 529 | "dev": true, 530 | "requires": { 531 | "core-util-is": "~1.0.0", 532 | "inherits": "~2.0.1", 533 | "isarray": "0.0.1", 534 | "string_decoder": "~0.10.x" 535 | } 536 | }, 537 | "string_decoder": { 538 | "version": "0.10.31", 539 | "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz", 540 | "integrity": "sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ=", 541 | "dev": true 542 | } 543 | } 544 | }, 545 | "get-uri": { 546 | "version": "2.0.4", 547 | "resolved": "https://registry.npmjs.org/get-uri/-/get-uri-2.0.4.tgz", 548 | "integrity": "sha512-v7LT/s8kVjs+Tx0ykk1I+H/rbpzkHvuIq87LmeXptcf5sNWm9uQiwjNAt94SJPA1zOlCntmnOlJvVWKmzsxG8Q==", 549 | "dev": true, 550 | "requires": { 551 | "data-uri-to-buffer": "1", 552 | "debug": "2", 553 | "extend": "~3.0.2", 554 | "file-uri-to-path": "1", 555 | "ftp": "~0.3.10", 556 | "readable-stream": "2" 557 | }, 558 | "dependencies": { 559 | "debug": { 560 | "version": "2.6.9", 561 | "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", 562 | "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", 563 | "dev": true, 564 | "requires": { 565 | "ms": "2.0.0" 566 | } 567 | }, 568 | "ms": { 569 | "version": "2.0.0", 570 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", 571 | "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", 572 | "dev": true 573 | }, 574 | "readable-stream": { 575 | "version": "2.3.7", 576 | "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", 577 | "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", 578 | "dev": true, 579 | "requires": { 580 | "core-util-is": "~1.0.0", 581 | "inherits": "~2.0.3", 582 | "isarray": "~1.0.0", 583 | "process-nextick-args": "~2.0.0", 584 | "safe-buffer": "~5.1.1", 585 | "string_decoder": "~1.1.1", 586 | "util-deprecate": "~1.0.1" 587 | } 588 | } 589 | } 590 | }, 591 | "glob": { 592 | "version": "7.1.6", 593 | "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz", 594 | "integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==", 595 | "dev": true, 596 | "requires": { 597 | "fs.realpath": "^1.0.0", 598 | "inflight": "^1.0.4", 599 | "inherits": "2", 600 | "minimatch": "^3.0.4", 601 | "once": "^1.3.0", 602 | "path-is-absolute": "^1.0.0" 603 | } 604 | }, 605 | "graceful-fs": { 606 | "version": "4.2.4", 607 | "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.4.tgz", 608 | "integrity": "sha512-WjKPNJF79dtJAVniUlGGWHYGz2jWxT6VhN/4m1NdkbZ2nOsEF+cI1Edgql5zCRhs/VsQYRvrXctxktVXZUkixw==", 609 | "dev": true 610 | }, 611 | "http-errors": { 612 | "version": "1.7.3", 613 | "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.7.3.tgz", 614 | "integrity": "sha512-ZTTX0MWrsQ2ZAhA1cejAwDLycFsd7I7nVtnkT3Ol0aqodaKW+0CTZDQ1uBv5whptCnc8e8HeRRJxRs0kmm/Qfw==", 615 | "dev": true, 616 | "requires": { 617 | "depd": "~1.1.2", 618 | "inherits": "2.0.4", 619 | "setprototypeof": "1.1.1", 620 | "statuses": ">= 1.5.0 < 2", 621 | "toidentifier": "1.0.0" 622 | } 623 | }, 624 | "http-proxy-agent": { 625 | "version": "2.1.0", 626 | "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-2.1.0.tgz", 627 | "integrity": "sha512-qwHbBLV7WviBl0rQsOzH6o5lwyOIvwp/BdFnvVxXORldu5TmjFfjzBcWUWS5kWAZhmv+JtiDhSuQCp4sBfbIgg==", 628 | "dev": true, 629 | "requires": { 630 | "agent-base": "4", 631 | "debug": "3.1.0" 632 | }, 633 | "dependencies": { 634 | "debug": { 635 | "version": "3.1.0", 636 | "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", 637 | "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", 638 | "dev": true, 639 | "requires": { 640 | "ms": "2.0.0" 641 | } 642 | }, 643 | "ms": { 644 | "version": "2.0.0", 645 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", 646 | "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", 647 | "dev": true 648 | } 649 | } 650 | }, 651 | "https": { 652 | "version": "1.0.0", 653 | "resolved": "https://registry.npmjs.org/https/-/https-1.0.0.tgz", 654 | "integrity": "sha1-PDfHrhqO65ZpBKKtHpdaGUt+06Q=" 655 | }, 656 | "https-proxy-agent": { 657 | "version": "3.0.1", 658 | "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-3.0.1.tgz", 659 | "integrity": "sha512-+ML2Rbh6DAuee7d07tYGEKOEi2voWPUGan+ExdPbPW6Z3svq+JCqr0v8WmKPOkz1vOVykPCBSuobe7G8GJUtVg==", 660 | "dev": true, 661 | "requires": { 662 | "agent-base": "^4.3.0", 663 | "debug": "^3.1.0" 664 | }, 665 | "dependencies": { 666 | "debug": { 667 | "version": "3.2.6", 668 | "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz", 669 | "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==", 670 | "dev": true, 671 | "requires": { 672 | "ms": "^2.1.1" 673 | } 674 | } 675 | } 676 | }, 677 | "iconv-lite": { 678 | "version": "0.4.24", 679 | "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", 680 | "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", 681 | "dev": true, 682 | "requires": { 683 | "safer-buffer": ">= 2.1.2 < 3" 684 | } 685 | }, 686 | "ieee754": { 687 | "version": "1.1.8", 688 | "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.1.8.tgz", 689 | "integrity": "sha1-vjPUCsEO8ZJnAfbwii2G+/0a0+Q=" 690 | }, 691 | "inflight": { 692 | "version": "1.0.6", 693 | "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", 694 | "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", 695 | "dev": true, 696 | "requires": { 697 | "once": "^1.3.0", 698 | "wrappy": "1" 699 | } 700 | }, 701 | "inherits": { 702 | "version": "2.0.4", 703 | "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", 704 | "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", 705 | "dev": true 706 | }, 707 | "ip": { 708 | "version": "1.1.5", 709 | "resolved": "https://registry.npmjs.org/ip/-/ip-1.1.5.tgz", 710 | "integrity": "sha1-vd7XARQpCCjAoDnnLvJfWq7ENUo=", 711 | "dev": true 712 | }, 713 | "isarray": { 714 | "version": "1.0.0", 715 | "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", 716 | "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=" 717 | }, 718 | "isstream": { 719 | "version": "0.1.2", 720 | "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz", 721 | "integrity": "sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo=", 722 | "dev": true 723 | }, 724 | "jmespath": { 725 | "version": "0.15.0", 726 | "resolved": "https://registry.npmjs.org/jmespath/-/jmespath-0.15.0.tgz", 727 | "integrity": "sha1-o/Iiqarp+Wb10nx5ZRDigJF2Qhc=" 728 | }, 729 | "jsonfile": { 730 | "version": "4.0.0", 731 | "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz", 732 | "integrity": "sha1-h3Gq4HmbZAdrdmQPygWPnBDjPss=", 733 | "dev": true, 734 | "requires": { 735 | "graceful-fs": "^4.1.6" 736 | } 737 | }, 738 | "klaw": { 739 | "version": "3.0.0", 740 | "resolved": "https://registry.npmjs.org/klaw/-/klaw-3.0.0.tgz", 741 | "integrity": "sha512-0Fo5oir+O9jnXu5EefYbVK+mHMBeEVEy2cmctR1O1NECcCkPRreJKrS6Qt/j3KC2C148Dfo9i3pCmCMsdqGr0g==", 742 | "dev": true, 743 | "requires": { 744 | "graceful-fs": "^4.1.9" 745 | } 746 | }, 747 | "lazystream": { 748 | "version": "1.0.0", 749 | "resolved": "https://registry.npmjs.org/lazystream/-/lazystream-1.0.0.tgz", 750 | "integrity": "sha1-9plf4PggOS9hOWvolGJAe7dxaOQ=", 751 | "dev": true, 752 | "requires": { 753 | "readable-stream": "^2.0.5" 754 | }, 755 | "dependencies": { 756 | "readable-stream": { 757 | "version": "2.3.7", 758 | "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", 759 | "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", 760 | "dev": true, 761 | "requires": { 762 | "core-util-is": "~1.0.0", 763 | "inherits": "~2.0.3", 764 | "isarray": "~1.0.0", 765 | "process-nextick-args": "~2.0.0", 766 | "safe-buffer": "~5.1.1", 767 | "string_decoder": "~1.1.1", 768 | "util-deprecate": "~1.0.1" 769 | } 770 | } 771 | } 772 | }, 773 | "levn": { 774 | "version": "0.3.0", 775 | "resolved": "https://registry.npmjs.org/levn/-/levn-0.3.0.tgz", 776 | "integrity": "sha1-OwmSTt+fCDwEkP3UwLxEIeBHZO4=", 777 | "dev": true, 778 | "requires": { 779 | "prelude-ls": "~1.1.2", 780 | "type-check": "~0.3.2" 781 | } 782 | }, 783 | "lodash": { 784 | "version": "4.17.21", 785 | "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", 786 | "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==" 787 | }, 788 | "lodash.defaults": { 789 | "version": "4.2.0", 790 | "resolved": "https://registry.npmjs.org/lodash.defaults/-/lodash.defaults-4.2.0.tgz", 791 | "integrity": "sha1-0JF4cW/+pN3p5ft7N/bwgCJ0WAw=", 792 | "dev": true 793 | }, 794 | "lodash.difference": { 795 | "version": "4.5.0", 796 | "resolved": "https://registry.npmjs.org/lodash.difference/-/lodash.difference-4.5.0.tgz", 797 | "integrity": "sha1-nMtOUF1Ia5FlE0V3KIWi3yf9AXw=", 798 | "dev": true 799 | }, 800 | "lodash.flatten": { 801 | "version": "4.4.0", 802 | "resolved": "https://registry.npmjs.org/lodash.flatten/-/lodash.flatten-4.4.0.tgz", 803 | "integrity": "sha1-8xwiIlqWMtK7+OSt2+8kCqdlph8=", 804 | "dev": true 805 | }, 806 | "lodash.isplainobject": { 807 | "version": "4.0.6", 808 | "resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz", 809 | "integrity": "sha1-fFJqUtibRcRcxpC4gWO+BJf1UMs=", 810 | "dev": true 811 | }, 812 | "lodash.union": { 813 | "version": "4.6.0", 814 | "resolved": "https://registry.npmjs.org/lodash.union/-/lodash.union-4.6.0.tgz", 815 | "integrity": "sha1-SLtQiECfFvGCFmZkHETdGqrjzYg=", 816 | "dev": true 817 | }, 818 | "lru-cache": { 819 | "version": "5.1.1", 820 | "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", 821 | "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", 822 | "dev": true, 823 | "requires": { 824 | "yallist": "^3.0.2" 825 | } 826 | }, 827 | "minimatch": { 828 | "version": "3.0.4", 829 | "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", 830 | "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", 831 | "dev": true, 832 | "requires": { 833 | "brace-expansion": "^1.1.7" 834 | } 835 | }, 836 | "ms": { 837 | "version": "2.1.2", 838 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", 839 | "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", 840 | "dev": true 841 | }, 842 | "netmask": { 843 | "version": "1.0.6", 844 | "resolved": "https://registry.npmjs.org/netmask/-/netmask-1.0.6.tgz", 845 | "integrity": "sha1-ICl+idhvb2QA8lDZ9Pa0wZRfzTU=", 846 | "dev": true 847 | }, 848 | "node-lambda": { 849 | "version": "0.16.0", 850 | "resolved": "https://registry.npmjs.org/node-lambda/-/node-lambda-0.16.0.tgz", 851 | "integrity": "sha512-dS0Yo+sGa7tjHlOW82TkOePYgfOzDs7msLIvU/n0rJklDAGbWPtPt5z19KaPDkhnQ7tICbLq5d/IKQ4o0YiANg==", 852 | "dev": true, 853 | "requires": { 854 | "archiver": "^3.1.1", 855 | "aws-sdk": "^2.585.0", 856 | "aws-xray-sdk-core": "^2.5.0", 857 | "commander": "^4.0.1", 858 | "continuation-local-storage": "^3.2.1", 859 | "dotenv": "^8.2.0", 860 | "fs-extra": "^8.1.0", 861 | "klaw": "^3.0.0", 862 | "minimatch": "^3.0.4", 863 | "proxy-agent": "^3.1.1" 864 | }, 865 | "dependencies": { 866 | "aws-sdk": { 867 | "version": "2.667.0", 868 | "resolved": "https://registry.npmjs.org/aws-sdk/-/aws-sdk-2.667.0.tgz", 869 | "integrity": "sha512-CDeMyqblfGlj/xUSp1/G1b9Tb4LDTid5CYrBW9FyBqMg6JIiXkaEN3b4oK2VjS18Xv1eth5LJAbYNMxjfHfKjw==", 870 | "dev": true, 871 | "requires": { 872 | "buffer": "4.9.1", 873 | "events": "1.1.1", 874 | "ieee754": "1.1.13", 875 | "jmespath": "0.15.0", 876 | "querystring": "0.2.0", 877 | "sax": "1.2.1", 878 | "url": "0.10.3", 879 | "uuid": "3.3.2", 880 | "xml2js": "0.4.19" 881 | } 882 | }, 883 | "ieee754": { 884 | "version": "1.1.13", 885 | "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.1.13.tgz", 886 | "integrity": "sha512-4vf7I2LYV/HaWerSo3XmlMkp5eZ83i+/CDluXi/IGTs/O1sejBNhTtnxzmRZfvOUqj7lZjqHkeTvpgSFDlWZTg==", 887 | "dev": true 888 | }, 889 | "url": { 890 | "version": "0.10.3", 891 | "resolved": "https://registry.npmjs.org/url/-/url-0.10.3.tgz", 892 | "integrity": "sha1-Ah5NnHcF8hu/N9A861h2dAJ3TGQ=", 893 | "dev": true, 894 | "requires": { 895 | "punycode": "1.3.2", 896 | "querystring": "0.2.0" 897 | } 898 | }, 899 | "uuid": { 900 | "version": "3.3.2", 901 | "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.3.2.tgz", 902 | "integrity": "sha512-yXJmeNaw3DnnKAOKJE51sL/ZaYfWJRl1pK9dr19YFCu0ObS231AB1/LbqTKRAQ5kw8A90rA6fr4riOUpTZvQZA==", 903 | "dev": true 904 | } 905 | } 906 | }, 907 | "normalize-path": { 908 | "version": "3.0.0", 909 | "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", 910 | "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", 911 | "dev": true 912 | }, 913 | "once": { 914 | "version": "1.4.0", 915 | "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", 916 | "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", 917 | "dev": true, 918 | "requires": { 919 | "wrappy": "1" 920 | } 921 | }, 922 | "optionator": { 923 | "version": "0.8.3", 924 | "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.8.3.tgz", 925 | "integrity": "sha512-+IW9pACdk3XWmmTXG8m3upGUJst5XRGzxMRjXzAuJ1XnIFNvfhjjIuYkDvysnPQ7qzqVzLt78BCruntqRhWQbA==", 926 | "dev": true, 927 | "requires": { 928 | "deep-is": "~0.1.3", 929 | "fast-levenshtein": "~2.0.6", 930 | "levn": "~0.3.0", 931 | "prelude-ls": "~1.1.2", 932 | "type-check": "~0.3.2", 933 | "word-wrap": "~1.2.3" 934 | } 935 | }, 936 | "pac-proxy-agent": { 937 | "version": "3.0.1", 938 | "resolved": "https://registry.npmjs.org/pac-proxy-agent/-/pac-proxy-agent-3.0.1.tgz", 939 | "integrity": "sha512-44DUg21G/liUZ48dJpUSjZnFfZro/0K5JTyFYLBcmh9+T6Ooi4/i4efwUiEy0+4oQusCBqWdhv16XohIj1GqnQ==", 940 | "dev": true, 941 | "requires": { 942 | "agent-base": "^4.2.0", 943 | "debug": "^4.1.1", 944 | "get-uri": "^2.0.0", 945 | "http-proxy-agent": "^2.1.0", 946 | "https-proxy-agent": "^3.0.0", 947 | "pac-resolver": "^3.0.0", 948 | "raw-body": "^2.2.0", 949 | "socks-proxy-agent": "^4.0.1" 950 | } 951 | }, 952 | "pac-resolver": { 953 | "version": "3.0.0", 954 | "resolved": "https://registry.npmjs.org/pac-resolver/-/pac-resolver-3.0.0.tgz", 955 | "integrity": "sha512-tcc38bsjuE3XZ5+4vP96OfhOugrX+JcnpUbhfuc4LuXBLQhoTthOstZeoQJBDnQUDYzYmdImKsbz0xSl1/9qeA==", 956 | "dev": true, 957 | "requires": { 958 | "co": "^4.6.0", 959 | "degenerator": "^1.0.4", 960 | "ip": "^1.1.5", 961 | "netmask": "^1.0.6", 962 | "thunkify": "^2.1.2" 963 | } 964 | }, 965 | "path-is-absolute": { 966 | "version": "1.0.1", 967 | "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", 968 | "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", 969 | "dev": true 970 | }, 971 | "pkginfo": { 972 | "version": "0.4.1", 973 | "resolved": "https://registry.npmjs.org/pkginfo/-/pkginfo-0.4.1.tgz", 974 | "integrity": "sha1-tUGO8EOd5UJfxJlQQtztFPsqhP8=", 975 | "dev": true 976 | }, 977 | "prelude-ls": { 978 | "version": "1.1.2", 979 | "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.1.2.tgz", 980 | "integrity": "sha1-IZMqVJ9eUv/ZqCf1cOBL5iqX2lQ=", 981 | "dev": true 982 | }, 983 | "process-nextick-args": { 984 | "version": "2.0.1", 985 | "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", 986 | "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==", 987 | "dev": true 988 | }, 989 | "proxy-agent": { 990 | "version": "3.1.1", 991 | "resolved": "https://registry.npmjs.org/proxy-agent/-/proxy-agent-3.1.1.tgz", 992 | "integrity": "sha512-WudaR0eTsDx33O3EJE16PjBRZWcX8GqCEeERw1W3hZJgH/F2a46g7jty6UGty6NeJ4CKQy8ds2CJPMiyeqaTvw==", 993 | "dev": true, 994 | "requires": { 995 | "agent-base": "^4.2.0", 996 | "debug": "4", 997 | "http-proxy-agent": "^2.1.0", 998 | "https-proxy-agent": "^3.0.0", 999 | "lru-cache": "^5.1.1", 1000 | "pac-proxy-agent": "^3.0.1", 1001 | "proxy-from-env": "^1.0.0", 1002 | "socks-proxy-agent": "^4.0.1" 1003 | } 1004 | }, 1005 | "proxy-from-env": { 1006 | "version": "1.1.0", 1007 | "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", 1008 | "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==", 1009 | "dev": true 1010 | }, 1011 | "punycode": { 1012 | "version": "1.3.2", 1013 | "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.3.2.tgz", 1014 | "integrity": "sha1-llOgNvt8HuQjQvIyXM7v6jkmxI0=" 1015 | }, 1016 | "querystring": { 1017 | "version": "0.2.0", 1018 | "resolved": "https://registry.npmjs.org/querystring/-/querystring-0.2.0.tgz", 1019 | "integrity": "sha1-sgmEkgO7Jd+CDadW50cAWHhSFiA=" 1020 | }, 1021 | "raw-body": { 1022 | "version": "2.4.1", 1023 | "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.4.1.tgz", 1024 | "integrity": "sha512-9WmIKF6mkvA0SLmA2Knm9+qj89e+j1zqgyn8aXGd7+nAduPoqgI9lO57SAZNn/Byzo5P7JhXTyg9PzaJbH73bA==", 1025 | "dev": true, 1026 | "requires": { 1027 | "bytes": "3.1.0", 1028 | "http-errors": "1.7.3", 1029 | "iconv-lite": "0.4.24", 1030 | "unpipe": "1.0.0" 1031 | } 1032 | }, 1033 | "readable-stream": { 1034 | "version": "3.6.0", 1035 | "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", 1036 | "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", 1037 | "dev": true, 1038 | "requires": { 1039 | "inherits": "^2.0.3", 1040 | "string_decoder": "^1.1.1", 1041 | "util-deprecate": "^1.0.1" 1042 | } 1043 | }, 1044 | "safe-buffer": { 1045 | "version": "5.1.2", 1046 | "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", 1047 | "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", 1048 | "dev": true 1049 | }, 1050 | "safer-buffer": { 1051 | "version": "2.1.2", 1052 | "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", 1053 | "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", 1054 | "dev": true 1055 | }, 1056 | "sax": { 1057 | "version": "1.2.1", 1058 | "resolved": "http://registry.npmjs.org/sax/-/sax-1.2.1.tgz", 1059 | "integrity": "sha1-e45lYZCyKOgaZq6nSEgNgozS03o=" 1060 | }, 1061 | "semver": { 1062 | "version": "5.7.1", 1063 | "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", 1064 | "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", 1065 | "dev": true 1066 | }, 1067 | "setprototypeof": { 1068 | "version": "1.1.1", 1069 | "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.1.tgz", 1070 | "integrity": "sha512-JvdAWfbXeIGaZ9cILp38HntZSFSo3mWg6xGcJJsd+d4aRMOqauag1C63dJfDw7OaMYwEbHMOxEZ1lqVRYP2OAw==", 1071 | "dev": true 1072 | }, 1073 | "shimmer": { 1074 | "version": "1.2.1", 1075 | "resolved": "https://registry.npmjs.org/shimmer/-/shimmer-1.2.1.tgz", 1076 | "integrity": "sha512-sQTKC1Re/rM6XyFM6fIAGHRPVGvyXfgzIDvzoq608vM+jeyVD0Tu1E6Np0Kc2zAIFWIj963V2800iF/9LPieQw==", 1077 | "dev": true 1078 | }, 1079 | "smart-buffer": { 1080 | "version": "4.1.0", 1081 | "resolved": "https://registry.npmjs.org/smart-buffer/-/smart-buffer-4.1.0.tgz", 1082 | "integrity": "sha512-iVICrxOzCynf/SNaBQCw34eM9jROU/s5rzIhpOvzhzuYHfJR/DhZfDkXiZSgKXfgv26HT3Yni3AV/DGw0cGnnw==", 1083 | "dev": true 1084 | }, 1085 | "socks": { 1086 | "version": "2.3.3", 1087 | "resolved": "https://registry.npmjs.org/socks/-/socks-2.3.3.tgz", 1088 | "integrity": "sha512-o5t52PCNtVdiOvzMry7wU4aOqYWL0PeCXRWBEiJow4/i/wr+wpsJQ9awEu1EonLIqsfGd5qSgDdxEOvCdmBEpA==", 1089 | "dev": true, 1090 | "requires": { 1091 | "ip": "1.1.5", 1092 | "smart-buffer": "^4.1.0" 1093 | } 1094 | }, 1095 | "socks-proxy-agent": { 1096 | "version": "4.0.2", 1097 | "resolved": "https://registry.npmjs.org/socks-proxy-agent/-/socks-proxy-agent-4.0.2.tgz", 1098 | "integrity": "sha512-NT6syHhI9LmuEMSK6Kd2V7gNv5KFZoLE7V5udWmn0de+3Mkj3UMA/AJPLyeNUVmElCurSHtUdM3ETpR3z770Wg==", 1099 | "dev": true, 1100 | "requires": { 1101 | "agent-base": "~4.2.1", 1102 | "socks": "~2.3.2" 1103 | }, 1104 | "dependencies": { 1105 | "agent-base": { 1106 | "version": "4.2.1", 1107 | "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-4.2.1.tgz", 1108 | "integrity": "sha512-JVwXMr9nHYTUXsBFKUqhJwvlcYU/blreOEUkhNR2eXZIvwd+c+o5V4MgDPKWnMS/56awN3TRzIP+KoPn+roQtg==", 1109 | "dev": true, 1110 | "requires": { 1111 | "es6-promisify": "^5.0.0" 1112 | } 1113 | } 1114 | } 1115 | }, 1116 | "source-map": { 1117 | "version": "0.6.1", 1118 | "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", 1119 | "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", 1120 | "dev": true, 1121 | "optional": true 1122 | }, 1123 | "stack-trace": { 1124 | "version": "0.0.10", 1125 | "resolved": "https://registry.npmjs.org/stack-trace/-/stack-trace-0.0.10.tgz", 1126 | "integrity": "sha1-VHxws0fo0ytOEI6hoqFZ5f3eGcA=", 1127 | "dev": true 1128 | }, 1129 | "statuses": { 1130 | "version": "1.5.0", 1131 | "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz", 1132 | "integrity": "sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow=", 1133 | "dev": true 1134 | }, 1135 | "string_decoder": { 1136 | "version": "1.1.1", 1137 | "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", 1138 | "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", 1139 | "dev": true, 1140 | "requires": { 1141 | "safe-buffer": "~5.1.0" 1142 | } 1143 | }, 1144 | "tar-stream": { 1145 | "version": "2.1.2", 1146 | "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-2.1.2.tgz", 1147 | "integrity": "sha512-UaF6FoJ32WqALZGOIAApXx+OdxhekNMChu6axLJR85zMMjXKWFGjbIRe+J6P4UnRGg9rAwWvbTT0oI7hD/Un7Q==", 1148 | "dev": true, 1149 | "requires": { 1150 | "bl": "^4.0.1", 1151 | "end-of-stream": "^1.4.1", 1152 | "fs-constants": "^1.0.0", 1153 | "inherits": "^2.0.3", 1154 | "readable-stream": "^3.1.1" 1155 | } 1156 | }, 1157 | "thunkify": { 1158 | "version": "2.1.2", 1159 | "resolved": "https://registry.npmjs.org/thunkify/-/thunkify-2.1.2.tgz", 1160 | "integrity": "sha1-+qDp0jDFGsyVyhOjYawFyn4EVT0=", 1161 | "dev": true 1162 | }, 1163 | "toidentifier": { 1164 | "version": "1.0.0", 1165 | "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.0.tgz", 1166 | "integrity": "sha512-yaOH/Pk/VEhBWWTlhI+qXxDFXlejDGcQipMlyxda9nthulaxLZUNcUqFxokp0vcYnvteJln5FNQDRrxj3YcbVw==", 1167 | "dev": true 1168 | }, 1169 | "type-check": { 1170 | "version": "0.3.2", 1171 | "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.3.2.tgz", 1172 | "integrity": "sha1-WITKtRLPHTVeP7eE8wgEsrUg23I=", 1173 | "dev": true, 1174 | "requires": { 1175 | "prelude-ls": "~1.1.2" 1176 | } 1177 | }, 1178 | "universalify": { 1179 | "version": "0.1.2", 1180 | "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz", 1181 | "integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==", 1182 | "dev": true 1183 | }, 1184 | "unpipe": { 1185 | "version": "1.0.0", 1186 | "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", 1187 | "integrity": "sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw=", 1188 | "dev": true 1189 | }, 1190 | "url": { 1191 | "version": "0.11.0", 1192 | "resolved": "https://registry.npmjs.org/url/-/url-0.11.0.tgz", 1193 | "integrity": "sha1-ODjpfPxgUh63PFJajlW/3Z4uKPE=", 1194 | "requires": { 1195 | "punycode": "1.3.2", 1196 | "querystring": "0.2.0" 1197 | } 1198 | }, 1199 | "util-deprecate": { 1200 | "version": "1.0.2", 1201 | "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", 1202 | "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=", 1203 | "dev": true 1204 | }, 1205 | "uuid": { 1206 | "version": "3.1.0", 1207 | "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.1.0.tgz", 1208 | "integrity": "sha512-DIWtzUkw04M4k3bf1IcpS2tngXEL26YUD2M0tMDUpnUrz2hgzUBlD55a4FjdLGPvfHxS6uluGWvaVEqgBcVa+g==" 1209 | }, 1210 | "winston": { 1211 | "version": "2.4.4", 1212 | "resolved": "https://registry.npmjs.org/winston/-/winston-2.4.4.tgz", 1213 | "integrity": "sha512-NBo2Pepn4hK4V01UfcWcDlmiVTs7VTB1h7bgnB0rgP146bYhMxX0ypCz3lBOfNxCO4Zuek7yeT+y/zM1OfMw4Q==", 1214 | "dev": true, 1215 | "requires": { 1216 | "async": "~1.0.0", 1217 | "colors": "1.0.x", 1218 | "cycle": "1.0.x", 1219 | "eyes": "0.1.x", 1220 | "isstream": "0.1.x", 1221 | "stack-trace": "0.0.x" 1222 | }, 1223 | "dependencies": { 1224 | "async": { 1225 | "version": "1.0.0", 1226 | "resolved": "https://registry.npmjs.org/async/-/async-1.0.0.tgz", 1227 | "integrity": "sha1-+PwEyjoTeErenhZBr5hXjPvWR6k=", 1228 | "dev": true 1229 | } 1230 | } 1231 | }, 1232 | "word-wrap": { 1233 | "version": "1.2.3", 1234 | "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz", 1235 | "integrity": "sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==", 1236 | "dev": true 1237 | }, 1238 | "wrappy": { 1239 | "version": "1.0.2", 1240 | "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", 1241 | "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", 1242 | "dev": true 1243 | }, 1244 | "xml2js": { 1245 | "version": "0.4.19", 1246 | "resolved": "https://registry.npmjs.org/xml2js/-/xml2js-0.4.19.tgz", 1247 | "integrity": "sha512-esZnJZJOiJR9wWKMyuvSE1y6Dq5LCuJanqhxslH2bxM6duahNZ+HMpCLhBQGZkbX6xRf8x1Y2eJlgt2q3qo49Q==", 1248 | "requires": { 1249 | "sax": ">=0.6.0", 1250 | "xmlbuilder": "~9.0.1" 1251 | } 1252 | }, 1253 | "xmlbuilder": { 1254 | "version": "9.0.7", 1255 | "resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-9.0.7.tgz", 1256 | "integrity": "sha1-Ey7mPS7FVlxVfiD0wi35rKaGsQ0=" 1257 | }, 1258 | "xregexp": { 1259 | "version": "2.0.0", 1260 | "resolved": "https://registry.npmjs.org/xregexp/-/xregexp-2.0.0.tgz", 1261 | "integrity": "sha1-UqY+VsoLhKfzpfPWGHLxJq16WUM=", 1262 | "dev": true 1263 | }, 1264 | "yallist": { 1265 | "version": "3.1.1", 1266 | "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", 1267 | "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", 1268 | "dev": true 1269 | }, 1270 | "zip-stream": { 1271 | "version": "2.1.3", 1272 | "resolved": "https://registry.npmjs.org/zip-stream/-/zip-stream-2.1.3.tgz", 1273 | "integrity": "sha512-EkXc2JGcKhO5N5aZ7TmuNo45budRaFGHOmz24wtJR7znbNqDPmdZtUauKX6et8KAVseAMBOyWJqEpXcHTBsh7Q==", 1274 | "dev": true, 1275 | "requires": { 1276 | "archiver-utils": "^2.1.0", 1277 | "compress-commons": "^2.1.1", 1278 | "readable-stream": "^3.4.0" 1279 | } 1280 | } 1281 | } 1282 | } 1283 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "lambda-cloudwatch-slack", 3 | "version": "0.4.0", 4 | "description": "Better Slack notifications for AWS CloudWatch", 5 | "authors": [ 6 | "Christopher Reichert ", 7 | "Cody Reichert ", 8 | "Alexandr Promakh " 9 | ], 10 | "config": { 11 | "progress": "true" 12 | }, 13 | "dependencies": { 14 | "aws-sdk": "^2.4.0", 15 | "https": "^1.0.0", 16 | "lodash": "^4.15.0", 17 | "url": "^0.11.0" 18 | }, 19 | "devDependencies": { 20 | "node-lambda": "0.16.0" 21 | }, 22 | "keywords": [ 23 | "aws", 24 | "slack", 25 | "lambda", 26 | "slackbot", 27 | "cloudwatch", 28 | "notifications" 29 | ], 30 | "license": "MIT", 31 | "repository": { 32 | "type": "git", 33 | "url": "https://github.com/assertible/lambda-cloudwatch-slack.git" 34 | }, 35 | "scripts": { 36 | "deploy": "./scripts/deploy.sh", 37 | "test": "./scripts/test.sh" 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /scripts/deploy.sh: -------------------------------------------------------------------------------- 1 | NODE_LAMBDA=./node_modules/node-lambda/bin/node-lambda 2 | 3 | test -s $NODE_LAMBDA || { echo "node-lambda not installed. Run 'npm install' first."; exit 1; } 4 | mkdir -p tmp 5 | cat .env | grep HOOK_URL > ./tmp/deploy.env 6 | $NODE_LAMBDA deploy --configFile ./tmp/deploy.env -------------------------------------------------------------------------------- /scripts/test.sh: -------------------------------------------------------------------------------- 1 | NODE_LAMBDA=./node_modules/node-lambda/bin/node-lambda 2 | 3 | $NODE_LAMBDA run -x test/context.json -j test/sns-codepipeline-event-pipeline-started.json 4 | $NODE_LAMBDA run -x test/context.json -j test/sns-codepipeline-event-stage-started.json 5 | $NODE_LAMBDA run -x test/context.json -j test/sns-codepipeline-event-stage-succeeded.json 6 | $NODE_LAMBDA run -x test/context.json -j test/sns-codepipeline-event-stage-failed.json 7 | $NODE_LAMBDA run -x test/context.json -j test/sns-cloudwatch-event.json 8 | $NODE_LAMBDA run -x test/context.json -j test/sns-event.json 9 | $NODE_LAMBDA run -x test/context.json -j test/sns-elastic-beanstalk-event.json 10 | $NODE_LAMBDA run -x test/context.json -j test/sns-codedeploy-event.json 11 | $NODE_LAMBDA run -x test/context.json -j test/sns-codedeploy-configuration.json 12 | $NODE_LAMBDA run -x test/context.json -j test/sns-elasticache-event.json 13 | $NODE_LAMBDA run -x test/context.json -j test/sns-autoscaling-event.json -------------------------------------------------------------------------------- /test/context.json: -------------------------------------------------------------------------------- 1 | {} -------------------------------------------------------------------------------- /test/sns-autoscaling-event.json: -------------------------------------------------------------------------------- 1 | { 2 | "Records": [ 3 | { 4 | "EventSource": "aws:sns", 5 | "EventVersion": "1.0", 6 | "EventSubscriptionArn": "arn:aws:sns:us-east-1:123456789123:AutoScalingNotifications:00000000-0000-0000-0000-000000000000", 7 | "Sns": { 8 | "Type": "Notification", 9 | "MessageId": "00000000-0000-0000-0000-000000000000", 10 | "TopicArn": "arn:aws:sns:us-east-1:123456789:AutoScalingNotifications", 11 | "Subject": "Auto Scaling: termination for group \"autoscale-group-name\"", 12 | "Message": "{\"Progress\":50,\"AccountId\":\"123456789123\",\"Description\":\"Terminating EC2 instance: i-00000000\",\"RequestId\":\"00000000-0000-0000-0000-000000000000\",\"EndTime\":\"2016-09-16T12:39:01.604Z\",\"AutoScalingGroupARN\":\"arn:aws:autoscaling:us-east-1:123456789:autoScalingGroup:00000000-0000-0000-0000-000000000000:autoScalingGroupName/autoscale-group-name\",\"ActivityId\":\"00000000-0000-0000-0000-000000000000\",\"StartTime\":\"2016-09-16T12:37:39.004Z\",\"Service\":\"AWS Auto Scaling\",\"Time\":\"2016-09-16T12:39:01.604Z\",\"EC2InstanceId\":\"i-00000000\",\"StatusCode\":\"InProgress\",\"StatusMessage\":\"\",\"Details\":{\"Subnet ID\":\"subnet-00000000\",\"Availability Zone\":\"us-east-1a\"},\"AutoScalingGroupName\":\"autoscale-group-name\",\"Cause\":\"At 2016-09-16T12:37:09Z a user request update of AutoScalingGroup constraints to min: 0, max: 0, desired: 0 changing the desired capacity from 1 to 0. At 2016-09-16T12:37:38Z an instance was taken out of service in response to a difference between desired and actual capacity, shrinking the capacity from 1 to 0. At 2016-09-16T12:37:39Z instance i-00000000 was selected for termination.\",\"Event\":\"autoscaling:EC2_INSTANCE_TERMINATE\"}", 13 | "Timestamp": "2016-09-16T12:39:01.661Z", 14 | "MessageAttributes": {} 15 | } 16 | } 17 | ] 18 | } 19 | -------------------------------------------------------------------------------- /test/sns-cloudwatch-event.json: -------------------------------------------------------------------------------- 1 | { 2 | "Records": [ 3 | { 4 | "EventSource": "aws:sns", 5 | "EventVersion": "1.0", 6 | "EventSubscriptionArn": "arn:aws:sns:us-west-2:123456789123:CloudWatchNotifications:00000000-0000-0000-0000-000000000000", 7 | "Sns": { 8 | "Type": "Notification", 9 | "MessageId": "00000000-0000-0000-0000-000000000000", 10 | "TopicArn": "arn:aws:sns:us-west-2:123456789123:CloudWatchNotifications", 11 | "Timestamp": "2016-08-11T07:24:05.959Z", 12 | "Subject": "ALARM: \"awsrds-app-High-DB-Connections\" in US West - Oregon", 13 | "Message": "{\"AlarmName\":\"awsrds-app-High-DB-Connections\",\"AlarmDescription\":null,\"AWSAccountId\":\"123456789123\",\"NewStateValue\":\"ALARM\",\"NewStateReason\":\"Threshold Crossed: 1 datapoint (10.0) was greater than or equal to the threshold (10.0).\",\"StateChangeTime\":\"2016-07-24T22:05:19.737+0000\",\"Region\":\"US West - Oregon\",\"OldStateValue\":\"OK\",\"Trigger\":{\"MetricName\":\"DatabaseConnections\",\"Namespace\":\"AWS/RDS\",\"Statistic\":\"AVERAGE\",\"Unit\":null,\"Dimensions\":[{\"name\":\"DBInstanceIdentifier\",\"value\":\"app\"}],\"Period\":300,\"EvaluationPeriods\":1,\"ComparisonOperator\":\"GreaterThanOrEqualToThreshold\",\"Threshold\":10.0}}", 14 | "MessageAttributes": {} 15 | } 16 | } 17 | ] 18 | } 19 | -------------------------------------------------------------------------------- /test/sns-codedeploy-configuration.json: -------------------------------------------------------------------------------- 1 | { 2 | "Records": [ 3 | { 4 | "EventSource": "aws:sns", 5 | "EventVersion": "1.0", 6 | "EventSubscriptionArn": "arn:aws:sns:us-east-1:123456789123:CodeDeployNotifications:00000000-0000-0000-0000-000000000000", 7 | "Sns": { 8 | "Type": "Notification", 9 | "MessageId": "00000000-0000-0000-0000-000000000000", 10 | "TopicArn": "arn:aws:sns:us-east-1:123456789:CodeDeployNotifications", 11 | "Subject": "SUCCESS: AWS CodeDeploy notification setup for trigger CodeDeployNotifications", 12 | "Message": "Your AWS CodeDeploy trigger arn:aws:sns:us-east-1:123456789123:CodeDeployNotifications is able to send notifications for the SNS topic you specified.\nAll subscribers to the topic will receive notifications related to the events you specified for this trigger.\nThank you,\nThe AWS CodeDeploy Service Team", 13 | "Timestamp": "2016-09-15T16:46:11.645Z", 14 | "MessageAttributes": {} 15 | } 16 | } 17 | ] 18 | } 19 | -------------------------------------------------------------------------------- /test/sns-codedeploy-event.json: -------------------------------------------------------------------------------- 1 | { 2 | "Records": [ 3 | { 4 | "EventSource": "aws:sns", 5 | "EventVersion": "1.0", 6 | "EventSubscriptionArn": "arn:aws:sns:us-east-1:123456789123:CodeDeployNotifications:00000000-0000-0000-0000-000000000000", 7 | "Sns": { 8 | "Type": "Notification", 9 | "MessageId": "00000000-0000-0000-0000-000000000000", 10 | "TopicArn": "arn:aws:sns:us-east-1:123456789:CodeDeployNotifications", 11 | "Subject": "SUCCEEDED: AWS CodeDeploy d-123ABC456 in us-east-1 to your-application-name", 12 | "Message": "{\"region\":\"us-east-1\",\"accountId\":\"123456789\",\"eventTriggerName\":\"notify team\",\"applicationName\":\"your-application-name\",\"deploymentId\":\"d-123ABC456\",\"deploymentGroupName\":\"your-application-name-group\",\"createTime\":\"Wed Aug 10 18:39:05 UTC 2016\",\"completeTime\":\"Wed Aug 10 18:40:33 UTC 2016\",\"deploymentOverview\":\"{\\\"Succeeded\\\":1,\\\"Failed\\\":0,\\\"Skipped\\\":0,\\\"InProgress\\\":0,\\\"Pending\\\":0}\",\"status\":\"SUCCEEDED\"}", 13 | "Timestamp": "2016-08-10T19:12:12.411Z", 14 | "MessageAttributes": {} 15 | } 16 | } 17 | ] 18 | } 19 | -------------------------------------------------------------------------------- /test/sns-codepipeline-event-pipeline-started.json: -------------------------------------------------------------------------------- 1 | { 2 | "Records": [ 3 | { 4 | "EventSource": "aws:sns", 5 | "EventVersion": "1.0", 6 | "EventSubscriptionArn": "arn:aws:sns:us-east-1:123456789123:CodePipelineNotifications:00000000-0000-0000-0000-000000000000", 7 | "Sns": { 8 | "Type": "Notification", 9 | "MessageId": "00000000-0000-0000-0000-000000000000", 10 | "TopicArn": "arn:aws:sns:us-east-1:123456789:codepipeline-alerts", 11 | "Subject": null, 12 | "Message": "{\"version\":\"0\",\"id\":\"00000000-0000-0000-0000-000000000000\",\"detail-type\":\"CodePipeline Pipeline Execution State Change\",\"source\":\"aws.codepipeline\",\"account\":\"123456789123\",\"time\":\"2018-02-14T07:14:14Z\",\"region\":\"us-east-1\",\"resources\":[\"arn:aws:codepipeline:us-east-1:123456789123:a-pipeline-project\"],\"detail\":{\"pipeline\":\"a-pipeline-project\",\"execution-id\":\"00000000-0000-0000-0000-000000000000\",\"state\":\"STARTED\",\"version\":1.0}}", 13 | "Timestamp": "2018-02-14T07:14:27.208Z", 14 | "MessageAttributes": {} 15 | } 16 | } 17 | ] 18 | } -------------------------------------------------------------------------------- /test/sns-codepipeline-event-stage-failed.json: -------------------------------------------------------------------------------- 1 | { 2 | "Records": [ 3 | { 4 | "EventSource": "aws:sns", 5 | "EventVersion": "1.0", 6 | "EventSubscriptionArn": "arn:aws:sns:us-east-1:123456789123:CodePipelineNotifications:00000000-0000-0000-0000-000000000000", 7 | "Sns": { 8 | "Type": "Notification", 9 | "MessageId": "00000000-0000-0000-0000-000000000000", 10 | "TopicArn": "arn:aws:sns:us-east-1:123456789:codepipeline-alerts", 11 | "Subject": null, 12 | "Message": "{\"version\":\"0\",\"id\":\"00000000-0000-0000-0000-000000000000\",\"detail-type\":\"CodePipeline Stage Execution State Change\",\"source\":\"aws.codepipeline\",\"account\":\"123456789123\",\"time\":\"2018-02-14T07:14:14Z\",\"region\":\"us-east-1\",\"resources\":[\"arn:aws:codepipeline:us-east-1:123456789123:a-pipeline-project\"],\"detail\":{\"pipeline\":\"a-pipeline-project\",\"execution-id\":\"00000000-0000-0000-0000-000000000000\",\"stage\":\"Build\",\"state\":\"FAILED\",\"version\":1.0}}", 13 | "Timestamp": "2018-02-14T07:14:27.208Z", 14 | "MessageAttributes": {} 15 | } 16 | } 17 | ] 18 | } 19 | -------------------------------------------------------------------------------- /test/sns-codepipeline-event-stage-started.json: -------------------------------------------------------------------------------- 1 | { 2 | "Records": [ 3 | { 4 | "EventSource": "aws:sns", 5 | "EventVersion": "1.0", 6 | "EventSubscriptionArn": "arn:aws:sns:us-east-1:123456789123:CodePipelineNotifications:00000000-0000-0000-0000-000000000000", 7 | "Sns": { 8 | "Type": "Notification", 9 | "MessageId": "00000000-0000-0000-0000-000000000000", 10 | "TopicArn": "arn:aws:sns:us-east-1:123456789:codepipeline-alerts", 11 | "Subject": null, 12 | "Message": "{\"version\":\"0\",\"id\":\"00000000-0000-0000-0000-000000000000\",\"detail-type\":\"CodePipeline Stage Execution State Change\",\"source\":\"aws.codepipeline\",\"account\":\"123456789123\",\"time\":\"2018-02-14T07:14:14Z\",\"region\":\"us-east-1\",\"resources\":[\"arn:aws:codepipeline:us-east-1:123456789123:a-pipeline-project\"],\"detail\":{\"pipeline\":\"a-pipeline-project\",\"execution-id\":\"00000000-0000-0000-0000-000000000000\",\"stage\":\"Build\",\"state\":\"STARTED\",\"version\":1.0}}", 13 | "Timestamp": "2018-02-14T07:14:27.208Z", 14 | "MessageAttributes": {} 15 | } 16 | } 17 | ] 18 | } 19 | -------------------------------------------------------------------------------- /test/sns-codepipeline-event-stage-succeeded.json: -------------------------------------------------------------------------------- 1 | { 2 | "Records": [ 3 | { 4 | "EventSource": "aws:sns", 5 | "EventVersion": "1.0", 6 | "EventSubscriptionArn": "arn:aws:sns:us-east-1:123456789123:CodePipelineNotifications:00000000-0000-0000-0000-000000000000", 7 | "Sns": { 8 | "Type": "Notification", 9 | "MessageId": "00000000-0000-0000-0000-000000000000", 10 | "TopicArn": "arn:aws:sns:us-east-1:123456789:codepipeline-alerts", 11 | "Subject": null, 12 | "Message": "{\"version\":\"0\",\"id\":\"00000000-0000-0000-0000-000000000000\",\"detail-type\":\"CodePipeline Stage Execution State Change\",\"source\":\"aws.codepipeline\",\"account\":\"123456789123\",\"time\":\"2018-02-14T07:14:14Z\",\"region\":\"us-east-1\",\"resources\":[\"arn:aws:codepipeline:us-east-1:123456789123:a-pipeline-project\"],\"detail\":{\"pipeline\":\"a-pipeline-project\",\"execution-id\":\"00000000-0000-0000-0000-000000000000\",\"stage\":\"Build\",\"state\":\"SUCCEEDED\",\"version\":1.0}}", 13 | "Timestamp": "2018-02-14T07:14:27.208Z", 14 | "MessageAttributes": {} 15 | } 16 | } 17 | ] 18 | } 19 | -------------------------------------------------------------------------------- /test/sns-elastic-beanstalk-event.json: -------------------------------------------------------------------------------- 1 | { 2 | "Records": [ 3 | { 4 | "Sns": { 5 | "MessageAttributes": null, 6 | "Message": "Timestamp: Sun Jul 24 20:37:10 UTC 2016\nMessage: Environment health has transitioned from Ok to Degraded. 5.0 % of the requests are failing with HTTP 5xx.\n\nEnvironment: retsd-staging-1\nApplication: retsd\n\nEnvironment URL: http:\/\/app-staging.us-west-2.elasticbeanstalk.com\nNotificationProcessId: 00000000-0000-0000-0000-000000000000", 7 | "Subject": "AWS Elastic Beanstalk Notification - Environment health has transitioned from Ok to Degraded. 5.0...", 8 | "TopicArn": "arn:aws:sns:us-west-2:123456789:ElasticBeanstalkNotifications-Environment-app-staging", 9 | "MessageId": "00000000-0000-0000-0000-000000000000", 10 | "Type": "Notification", 11 | "Timestamp": "2016-08-11T07:24:05.959Z" 12 | }, 13 | "EventSubscriptionArn": "arn:aws:sns:us-west-2:123456789123:ElasticBeanstalkNotifications-Environment-app-staging:00000000-0000-0000-0000-000000000000", 14 | "EventVersion": "1.0", 15 | "EventSource": "aws:sns" 16 | } 17 | ] 18 | } 19 | -------------------------------------------------------------------------------- /test/sns-elasticache-event.json: -------------------------------------------------------------------------------- 1 | { 2 | "Records": [ 3 | { 4 | "EventSource": "aws:sns", 5 | "EventVersion": "1.0", 6 | "EventSubscriptionArn": "arn:aws:sns:us-east-1:123456789123:ElasticacheNotifications:00000000-0000-0000-0000-000000000000", 7 | "Sns": { 8 | "Type": "Notification", 9 | "MessageId": "7f660149-f0b8-53a4-83c5-516fdaed6a4b", 10 | "TopicArn": "arn:aws:sns:us-east-1:123456789123:ElasticacheNotifications", 11 | "Subject": null, 12 | "Message": "{\"ElastiCache:ElasticacheEvent\":\"nodename\"}", 13 | "Timestamp": "2016-08-11T07:24:05.959Z", 14 | "MessageAttributes": {} 15 | } 16 | } 17 | ] 18 | } 19 | -------------------------------------------------------------------------------- /test/sns-event.json: -------------------------------------------------------------------------------- 1 | { 2 | "Records": [ 3 | { 4 | "EventSource": "aws:sns", 5 | "EventVersion": "1.0", 6 | "EventSubscriptionArn": "arn:aws:sns:eu-west-1:111111111111111:samlpe-alert:ff1e3698-7f85-4da2-a90e-13ad2afe30e0", 7 | "Sns": { 8 | "Type": "Notification", 9 | "MessageId": "0d7a65e4-435e-5ff1-8e80-37e6fba3ef4f", 10 | "TopicArn": "arn:aws:sns:eu-west-1:111111111111111:Sample-alert", 11 | "Subject": "ALARM: \"Sample Alert\" in EU - Ireland", 12 | "Message": "{\"AlarmName\":\"Sample Alert\",\"AlarmDescription\":null,\"AWSAccountId\":\"111111111111111\",\"NewStateValue\":\"ALARM\",\"NewStateReason\":\"Threshold Crossed: 1 datapoint [1446.0 (07/08/17 18:46:00)] was greater than or equal to the threshold (1000.0).\",\"StateChangeTime\":\"2017-08-07T18:51:41.602+0000\",\"Region\":\"EU - Ireland\",\"OldStateValue\":\"OK\",\"Trigger\":{\"MetricName\":\"RequestCount\",\"Namespace\":\"AWS/ApplicationELB\",\"StatisticType\":\"Statistic\",\"Statistic\":\"SUM\",\"Unit\":null,\"Dimensions\":[{\"name\":\"LoadBalancer\",\"value\":\"app/app/6892f491c9b95d3f\"}],\"Period\":300,\"EvaluationPeriods\":1,\"ComparisonOperator\":\"GreaterThanOrEqualToThreshold\",\"Threshold\":1000.0,\"TreatMissingData\":\"\",\"EvaluateLowSampleCountPercentile\":\"\"}}", 13 | "Timestamp": "2017-08-07T18:51:41.652Z", 14 | "SignatureVersion": "1", 15 | "Signature": "lol", 16 | "SigningCertUrl": "https://sns.eu-west-1.amazonaws.com/SimpleNotificationService-b95095beb82e8f6a0463n12jnkndswea.pem", 17 | "UnsubscribeUrl": "https://sns.eu-west-1.amazonaws.com/?Action=Unsubscribe&SubscriptionArn=arn:aws:sns:eu-west-1:111111111111111:Sample-alert:ff1e3698-7f85-4da2-a90e-13ad2afe30e0", 18 | "MessageAttributes": {} 19 | } 20 | } 21 | ] 22 | } 23 | --------------------------------------------------------------------------------