├── .gitignore ├── AWSlack.template.json ├── Makefile ├── README.md ├── index.js ├── package-lock.json └── package.json /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | code.zip 3 | .vscode/ 4 | -------------------------------------------------------------------------------- /AWSlack.template.json: -------------------------------------------------------------------------------- 1 | { 2 | "AWSTemplateFormatVersion": "2010-09-09", 3 | "Description": "AWSlack AWS CloudFormation Template: This template deploys Slack integration into your account Please see https://github.com/doitintl/AWSlack more information. **WARNING** You will be billed for the AWS resources used if you create a stack from this template.", 4 | "Parameters": { 5 | "SlackAPIToken": { 6 | "Type": "String", 7 | "Description": "Slack Bot API Token", 8 | "MinLength": 42, 9 | "MaxLength": 55, 10 | "AllowedPattern": "^[a-z]{4}(?:-\\d{11,12})?-\\d{12}-[a-zA-Z0-9]{24}$", 11 | "NoEcho": true 12 | }, 13 | "DefaultSlackChannel": { 14 | "Type": "String", 15 | "Description": "The bot will publish messages to this channel. You may publish different events to different channels by editing the 'awslack.tests' DynamoDB table for each 'test' Item", 16 | "Default": "aws", 17 | "MinLength": 1, 18 | "MaxLength": 21 19 | } 20 | }, 21 | "Resources": { 22 | "EventRule": { 23 | "Type": "AWS::Events::Rule", 24 | "Properties": { 25 | "Description": "EventRule", 26 | "EventPattern": { 27 | "account": [ 28 | { 29 | "Ref": "AWS::AccountId" 30 | } 31 | ] 32 | }, 33 | "State": "ENABLED", 34 | "Targets": [ 35 | { 36 | "Arn": { 37 | "Fn::GetAtt": [ 38 | "EventHandler", 39 | "Arn" 40 | ] 41 | }, 42 | "Id": "TargetFunctionV1" 43 | } 44 | ] 45 | } 46 | }, 47 | "PermissionForEventsToInvokeLambda": { 48 | "Type": "AWS::Lambda::Permission", 49 | "Properties": { 50 | "FunctionName": { 51 | "Ref": "EventHandler" 52 | }, 53 | "Action": "lambda:InvokeFunction", 54 | "Principal": "events.amazonaws.com", 55 | "SourceArn": { 56 | "Fn::GetAtt": [ 57 | "EventRule", 58 | "Arn" 59 | ] 60 | } 61 | } 62 | }, 63 | "LambdaExecutionRole": { 64 | "Type": "AWS::IAM::Role", 65 | "Properties": { 66 | "AssumeRolePolicyDocument": { 67 | "Version": "2012-10-17", 68 | "Statement": [ 69 | { 70 | "Effect": "Allow", 71 | "Principal": { 72 | "Service": [ 73 | "lambda.amazonaws.com" 74 | ] 75 | }, 76 | "Action": [ 77 | "sts:AssumeRole" 78 | ] 79 | } 80 | ] 81 | }, 82 | "Path": "/", 83 | "Policies": [ 84 | { 85 | "PolicyName": "root", 86 | "PolicyDocument": { 87 | "Version": "2012-10-17", 88 | "Statement": [ 89 | { 90 | "Effect": "Allow", 91 | "Action": [ 92 | "logs:*" 93 | ], 94 | "Resource": "arn:aws:logs:*:*:*" 95 | }, 96 | { 97 | "Effect": "Allow", 98 | "Action": [ 99 | "dynamodb:*" 100 | ], 101 | "Resource": [ 102 | "arn:aws:dynamodb:*:*:table/awslack.configs", 103 | "arn:aws:dynamodb:*:*:table/awslack.tests" 104 | ] 105 | } 106 | ] 107 | } 108 | } 109 | ] 110 | } 111 | }, 112 | "EventHandler": { 113 | "Type": "AWS::Lambda::Function", 114 | "Properties": { 115 | "Code": { 116 | "S3Bucket": "awslack-v2", 117 | "S3Key": "source/code.zip" 118 | }, 119 | "Description": "Handles events by sending Slack notifications.", 120 | "FunctionName": "awslack", 121 | "Handler": "index.handleEvent", 122 | "Environment": { 123 | "Variables": { 124 | "key": "value" 125 | } 126 | }, 127 | "Role": { 128 | "Fn::GetAtt": [ 129 | "LambdaExecutionRole", 130 | "Arn" 131 | ] 132 | }, 133 | "Runtime": "nodejs6.10" 134 | } 135 | }, 136 | "ConfigTable": { 137 | "Type": "AWS::DynamoDB::Table", 138 | "Properties": { 139 | "AttributeDefinitions": [ 140 | { 141 | "AttributeName": "name", 142 | "AttributeType": "S" 143 | } 144 | ], 145 | "KeySchema": [ 146 | { 147 | "AttributeName": "name", 148 | "KeyType": "HASH" 149 | } 150 | ], 151 | "ProvisionedThroughput": { 152 | "ReadCapacityUnits": 10, 153 | "WriteCapacityUnits": 1 154 | }, 155 | "TableName": "awslack.configs" 156 | } 157 | }, 158 | "TestTable": { 159 | "Type": "AWS::DynamoDB::Table", 160 | "Properties": { 161 | "AttributeDefinitions": [ 162 | { 163 | "AttributeName": "name", 164 | "AttributeType": "S" 165 | } 166 | ], 167 | "KeySchema": [ 168 | { 169 | "AttributeName": "name", 170 | "KeyType": "HASH" 171 | } 172 | ], 173 | "ProvisionedThroughput": { 174 | "ReadCapacityUnits": 10, 175 | "WriteCapacityUnits": 1 176 | }, 177 | "TableName": "awslack.tests" 178 | } 179 | }, 180 | "InitializeDynamoDBFunction": { 181 | "Type": "AWS::Lambda::Function", 182 | "Properties": { 183 | "Code": { 184 | "S3Bucket": "awslack-v2", 185 | "S3Key": "source/code.zip" 186 | }, 187 | "Description": "Initializes DynamoDB", 188 | "FunctionName": "awslack-init-db", 189 | "Handler": "index.initDynamoDB", 190 | "Role": { 191 | "Fn::GetAtt": [ 192 | "LambdaExecutionRole", 193 | "Arn" 194 | ] 195 | }, 196 | "Runtime": "nodejs6.10" 197 | } 198 | }, 199 | "TriggerDynamoDBInitialize": { 200 | "Type": "Custom::TriggerDynamoDBInitialize", 201 | "DependsOn": [ 202 | "ConfigTable", 203 | "TestTable", 204 | "InitializeDynamoDBFunction" 205 | ], 206 | "Properties": { 207 | "ServiceToken": { 208 | "Fn::GetAtt": [ 209 | "InitializeDynamoDBFunction", 210 | "Arn" 211 | ] 212 | }, 213 | "SlackAPIToken": { 214 | "Ref": "SlackAPIToken" 215 | }, 216 | "DefaultSlackChannel": { 217 | "Ref": "DefaultSlackChannel" 218 | } 219 | } 220 | } 221 | } 222 | } -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | all: deploy-version 2 | 3 | install-dep: 4 | npm install 5 | 6 | zip-code: install-dep 7 | zip -r code.zip index.js package.json node_modules 8 | 9 | deploy-version: zip-code 10 | aws s3 cp AWSlack.template.json s3://awslack-v2/source/ --acl public-read 11 | aws s3 cp code.zip s3://awslack-v2/source/ --acl public-read 12 | 13 | clean: 14 | rm -f code.zip 15 | rm -rf node_modules -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # AWSlack 2 | 3 | ## Prerequisites 4 | - A valid `AWS` Account. 5 | - Any `AWS` supported browser. ( Chrome / Firefox / Safari etc. ) 6 | 7 | ## Generate Slack API Token 8 | - Go to https://.slack.com/apps/new/A0F7YS25R-bots 9 | - Enter a name for the bot to post with. (i.e. @aws) 10 | - Click `Add bot integration`. 11 | - Wait until the UI displays the `API Token` and copy the string (i.e. xxxx-yyyyyyyyyyyy-zzzzzzzzzzzzzzzzzzzzzzzz). 12 | - Keep this token for using in the next step. 13 | - Don't forget to invite your new bot to a channel by `@` mentioning it. 14 | 15 | ## Deployment 16 | - Hold down the `Ctrl` button and Click the `Launch Stack` button to deploy the stack into your account: 17 | 18 | 19 | - Paste your `API Token` in the `SlackAPIToken` parameter. 20 | - The bot will publish messages to the channel in the `DefaultSlackChannel` parameter. The default is `aws`. 21 | - Click `Next` and Confirm all steps until the stack deploys. 22 | 23 | ## Configure 24 | - Open your browser at [AWS DynamoDB Console](https://console.aws.amazon.com/dynamodb/home) 25 | - Switch to the `Tables` tab. 26 | - Select the `awslack.tests` table and go to the `Items` tab. 27 | - You can add new tests or change/delete existing tests. 28 | - You can change the `slackChannel` attribute of each test to another Slack channel. 29 | - Select the `awslack.configs` table and go to the `Items` tab. 30 | - You can edit the slackAPIToken and set the `value` to another slack API token. 31 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | function runTest(test, data) { 2 | try { 3 | $ = data; 4 | return eval(`'use strict';(${test});`); 5 | } 6 | catch (err) { 7 | console.log(`Rule test error in '${test}': ${err}`); 8 | return false; 9 | } 10 | } 11 | 12 | function evalMessage(message, data) { 13 | try { 14 | $ = data; 15 | return eval(`'use strict';\`${message}\`;`); 16 | } 17 | catch (err) { 18 | console.log(`Rule message error in '${message}': ${err}`); 19 | return ""; 20 | } 21 | } 22 | 23 | const AWS = require('aws-sdk'); 24 | const Slack = require('slack-node'); 25 | const dynamodb = new AWS.DynamoDB(); 26 | 27 | exports.handleEvent = function (event, context, callback) { 28 | console.log(`event data: ${JSON.stringify(event)}`); 29 | Promise.all([ 30 | getConfig(), 31 | getTests() 32 | ]) 33 | .then(([config, tests]) => { 34 | return Promise.all(tests.map(test => { 35 | let clonedData = JSON.parse(JSON.stringify(event)); 36 | if (runTest(test.test, clonedData)) { 37 | let message = evalMessage(test.message, clonedData); 38 | return sendSlack(test.slackChannel, message, config.slackAPIToken); 39 | } 40 | else { 41 | return Promise.resolve(); 42 | } 43 | })); 44 | }) 45 | .then(() => { 46 | callback(null, {}); 47 | }) 48 | .catch(err => { 49 | callback(err); 50 | }); 51 | }; 52 | 53 | function getConfig() { 54 | return readDynamo("awslack.configs").then(data => { 55 | return data.reduce((configs, config) => { 56 | configs[config.name.S] = config.value.S; 57 | return configs; 58 | }, {}); 59 | }); 60 | } 61 | 62 | function getTests() { 63 | return readDynamo("awslack.tests").then(data => { 64 | return data.map(test => ({ 65 | test: test.test.S, 66 | message: test.message.S, 67 | slackChannel: test.slackChannel.S 68 | })); 69 | }); 70 | } 71 | 72 | function readDynamo(tableName) { 73 | return new Promise((resolve, reject) => { 74 | try { 75 | dynamodb.scan({ 76 | TableName: tableName 77 | }, function (err, data) { 78 | if (!!err) { 79 | reject(err); 80 | } 81 | else { 82 | resolve(data.Items); 83 | } 84 | }); 85 | } 86 | catch (e) { 87 | reject(e); 88 | } 89 | }); 90 | } 91 | 92 | function writeDynamo(tableName, item) { 93 | return new Promise((resolve, reject) => { 94 | try { 95 | dynamodb.putItem({ 96 | TableName: tableName, 97 | Item: item 98 | }, function (err, data) { 99 | if (!!err) { 100 | reject(err); 101 | } 102 | else { 103 | resolve(data); 104 | } 105 | }); 106 | } 107 | catch (e) { 108 | reject(e); 109 | } 110 | }); 111 | } 112 | 113 | function sendSlack(channel, message, apiToken) { 114 | return new Promise((resolve, reject) => { 115 | const slack = new Slack(apiToken); 116 | slack.api('chat.postMessage', { 117 | text: message, 118 | channel: channel, 119 | as_user : true 120 | }, function (err, response) { 121 | if (!!err) { 122 | reject(err); 123 | } 124 | else { 125 | resolve(response); 126 | } 127 | }); 128 | }); 129 | } 130 | 131 | exports.initDynamoDB = function (event, context, callback) { 132 | const slackAPIToken = event.ResourceProperties.SlackAPIToken; 133 | const defaultSlackChannel = event.ResourceProperties.DefaultSlackChannel; 134 | 135 | Promise.all([ 136 | writeDynamo("awslack.configs", { name: { S: "slackAPIToken" }, value: { S: slackAPIToken } }), 137 | writeDynamo("awslack.tests", { name: { S: "bucket_create" }, test: { S: "$.source==='aws.s3' && $.detail.eventName==='CreateBucket'" }, message: { S: "Bucket ${$.detail.requestParameters.bucketName} created in region ${$.region} by ${$.detail.userIdentity.arn}" }, slackChannel: { S: defaultSlackChannel } }), 138 | writeDynamo("awslack.tests", { name: { S: "bucket_delete" }, test: { S: "$.source==='aws.s3' && $.detail.eventName==='DeleteBucket'" }, message: { S: "Bucket ${$.detail.requestParameters.bucketName} deleted in region ${$.region} by ${$.detail.userIdentity.arn}" }, slackChannel: { S: defaultSlackChannel } }), 139 | writeDynamo("awslack.tests", { name: { S: "lambda_update" }, test: { S: "$.source==='aws.lambda' && $.detail.eventName.includes('UpdateFunctionCode')" }, message: { S: "The Lambda function ${$.detail.requestParameters.functionName} was updated by ${$.detail.userIdentity.arn}" }, slackChannel: { S: defaultSlackChannel } }), 140 | writeDynamo("awslack.tests", { name: { S: "ec2_start" }, test: { S: "$.source==='aws.ec2' && $['detail-type']==='EC2 Instance State-change Notification' && $.detail.state==='running'" }, message: { S: "EC2 instance ${$.detail['instance-id']} started in region ${$.region}" }, slackChannel: { S: defaultSlackChannel } }), 141 | writeDynamo("awslack.tests", { name: { S: "autoscale" }, test: { S:"$.source==='aws.autoscaling' && ( $['detail-type']==='EC2 Instance Terminate Successful' || $['detail-type']==='EC2 Instance Launch Successful')" }, message: { S: "Autoscaling event of type ${$['detail-type']} on group ${$.detail.AutoScalingGroupName} in region ${$.region} - Cause: ${$.detail.Cause}" }, slackChannel: { S: defaultSlackChannel } }), 142 | writeDynamo("awslack.tests", { name: { S: "health" }, test: { S:"$.source==='aws.health'" }, message: { S: "Health event ${$.detail.eventTypeCode} in region ${$.region}" }, slackChannel: { S: defaultSlackChannel } }), 143 | writeDynamo("awslack.tests", { name: { S: "signin" }, test: { S:"$.source==='aws.signin'" }, message: { S: "Sign-in event by ${$.detail.userIdentity.arn} from ${$.detail.sourceIPAddress} in region ${$.region} at ${$.detail.eventTime} with UserAgent: ${$.detail.userAgent}" }, slackChannel: { S: defaultSlackChannel } }) 144 | ]) 145 | .then(() => { 146 | sendResponse(event, context, "SUCCESS", {}); 147 | callback(null, {}); 148 | }) 149 | .catch(err => { 150 | sendResponse(event, context, "FAILED", err); 151 | callback(err); 152 | }); 153 | }; 154 | 155 | function sendResponse(event, context, responseStatus, responseData) { 156 | var responseBody = JSON.stringify({ 157 | Status: responseStatus, 158 | Reason: JSON.stringify(responseData), 159 | PhysicalResourceId: context.logStreamName, 160 | StackId: event.StackId, 161 | RequestId: event.RequestId, 162 | LogicalResourceId: event.LogicalResourceId, 163 | Data: responseData 164 | }); 165 | 166 | console.log("RESPONSE BODY:\n", responseBody); 167 | 168 | var https = require("https"); 169 | var url = require("url"); 170 | 171 | var parsedUrl = url.parse(event.ResponseURL); 172 | var options = { 173 | hostname: parsedUrl.hostname, 174 | port: 443, 175 | path: parsedUrl.path, 176 | method: "PUT", 177 | headers: { 178 | "content-type": "", 179 | "content-length": responseBody.length 180 | } 181 | }; 182 | 183 | console.log("SENDING RESPONSE...\n"); 184 | 185 | var request = https.request(options, function (response) { 186 | console.log("STATUS: " + response.statusCode); 187 | console.log("HEADERS: " + JSON.stringify(response.headers)); 188 | // Tell AWS Lambda that the function execution is done 189 | context.done(); 190 | }); 191 | 192 | request.on("error", function (error) { 193 | console.log("sendResponse Error:" + error); 194 | // Tell AWS Lambda that the function execution is done 195 | context.done(); 196 | }); 197 | 198 | // write data to request body 199 | request.write(responseBody); 200 | request.end(); 201 | } -------------------------------------------------------------------------------- /package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "gcp-alert-service", 3 | "version": "1.0.0", 4 | "lockfileVersion": 1, 5 | "requires": true, 6 | "dependencies": { 7 | "ajv": { 8 | "version": "4.11.8", 9 | "resolved": "https://registry.npmjs.org/ajv/-/ajv-4.11.8.tgz", 10 | "integrity": "sha1-gv+wKynmYq5TvcIK8VlHcGc5xTY=", 11 | "requires": { 12 | "co": "4.6.0", 13 | "json-stable-stringify": "1.0.1" 14 | } 15 | }, 16 | "asn1": { 17 | "version": "0.2.3", 18 | "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.3.tgz", 19 | "integrity": "sha1-2sh4dxPJlmhJ/IGAd36+nB3fO4Y=" 20 | }, 21 | "assert-plus": { 22 | "version": "0.2.0", 23 | "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-0.2.0.tgz", 24 | "integrity": "sha1-104bh+ev/A24qttwIfP+SBAasjQ=" 25 | }, 26 | "asynckit": { 27 | "version": "0.4.0", 28 | "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", 29 | "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=" 30 | }, 31 | "aws-sdk": { 32 | "version": "2.80.0", 33 | "resolved": "https://registry.npmjs.org/aws-sdk/-/aws-sdk-2.80.0.tgz", 34 | "integrity": "sha1-Yc7XR+uYFglIOuxT6NZU08ydFDU=", 35 | "requires": { 36 | "buffer": "4.9.1", 37 | "crypto-browserify": "1.0.9", 38 | "jmespath": "0.15.0", 39 | "querystring": "0.2.0", 40 | "sax": "1.2.1", 41 | "url": "0.10.3", 42 | "uuid": "3.0.1", 43 | "xml2js": "0.4.17", 44 | "xmlbuilder": "4.2.1" 45 | }, 46 | "dependencies": { 47 | "url": { 48 | "version": "0.10.3", 49 | "resolved": "https://registry.npmjs.org/url/-/url-0.10.3.tgz", 50 | "integrity": "sha1-Ah5NnHcF8hu/N9A861h2dAJ3TGQ=", 51 | "requires": { 52 | "punycode": "1.3.2", 53 | "querystring": "0.2.0" 54 | } 55 | } 56 | } 57 | }, 58 | "aws-sign2": { 59 | "version": "0.6.0", 60 | "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.6.0.tgz", 61 | "integrity": "sha1-FDQt0428yU0OW4fXY81jYSwOeU8=" 62 | }, 63 | "aws4": { 64 | "version": "1.6.0", 65 | "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.6.0.tgz", 66 | "integrity": "sha1-g+9cqGCysy5KDe7e6MdxudtXRx4=" 67 | }, 68 | "base64-js": { 69 | "version": "1.2.1", 70 | "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.2.1.tgz", 71 | "integrity": "sha512-dwVUVIXsBZXwTuwnXI9RK8sBmgq09NDHzyR9SAph9eqk76gKK2JSQmZARC2zRC81JC2QTtxD0ARU5qTS25gIGw==" 72 | }, 73 | "bcrypt-pbkdf": { 74 | "version": "1.0.1", 75 | "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.1.tgz", 76 | "integrity": "sha1-Y7xdy2EzG5K8Bf1SiVPDNGKgb40=", 77 | "optional": true, 78 | "requires": { 79 | "tweetnacl": "0.14.5" 80 | } 81 | }, 82 | "boom": { 83 | "version": "2.10.1", 84 | "resolved": "https://registry.npmjs.org/boom/-/boom-2.10.1.tgz", 85 | "integrity": "sha1-OciRjO/1eZ+D+UkqhI9iWt0Mdm8=", 86 | "requires": { 87 | "hoek": "2.16.3" 88 | } 89 | }, 90 | "buffer": { 91 | "version": "4.9.1", 92 | "resolved": "https://registry.npmjs.org/buffer/-/buffer-4.9.1.tgz", 93 | "integrity": "sha1-bRu2AbB6TvztlwlBMgkwJ8lbwpg=", 94 | "requires": { 95 | "base64-js": "1.2.1", 96 | "ieee754": "1.1.8", 97 | "isarray": "1.0.0" 98 | } 99 | }, 100 | "caseless": { 101 | "version": "0.12.0", 102 | "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz", 103 | "integrity": "sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw=" 104 | }, 105 | "co": { 106 | "version": "4.6.0", 107 | "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz", 108 | "integrity": "sha1-bqa989hTrlTMuOR7+gvz+QMfsYQ=" 109 | }, 110 | "combined-stream": { 111 | "version": "1.0.5", 112 | "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.5.tgz", 113 | "integrity": "sha1-k4NwpXtKUd6ix3wV1cX9+JUWQAk=", 114 | "requires": { 115 | "delayed-stream": "1.0.0" 116 | } 117 | }, 118 | "cryptiles": { 119 | "version": "2.0.5", 120 | "resolved": "https://registry.npmjs.org/cryptiles/-/cryptiles-2.0.5.tgz", 121 | "integrity": "sha1-O9/s3GCBR8HGcgL6KR59ylnqo7g=", 122 | "requires": { 123 | "boom": "2.10.1" 124 | } 125 | }, 126 | "crypto-browserify": { 127 | "version": "1.0.9", 128 | "resolved": "https://registry.npmjs.org/crypto-browserify/-/crypto-browserify-1.0.9.tgz", 129 | "integrity": "sha1-zFRJaF37hesRyYKKzHy4erW7/MA=" 130 | }, 131 | "dashdash": { 132 | "version": "1.14.1", 133 | "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz", 134 | "integrity": "sha1-hTz6D3y+L+1d4gMmuN1YEDX24vA=", 135 | "requires": { 136 | "assert-plus": "1.0.0" 137 | }, 138 | "dependencies": { 139 | "assert-plus": { 140 | "version": "1.0.0", 141 | "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", 142 | "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=" 143 | } 144 | } 145 | }, 146 | "delayed-stream": { 147 | "version": "1.0.0", 148 | "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", 149 | "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=" 150 | }, 151 | "ecc-jsbn": { 152 | "version": "0.1.1", 153 | "resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.1.tgz", 154 | "integrity": "sha1-D8c6ntXw1Tw4GTOYUj735UN3dQU=", 155 | "optional": true, 156 | "requires": { 157 | "jsbn": "0.1.1" 158 | } 159 | }, 160 | "extend": { 161 | "version": "3.0.1", 162 | "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.1.tgz", 163 | "integrity": "sha1-p1Xqe8Gt/MWjHOfnYtuq3F5jZEQ=" 164 | }, 165 | "extsprintf": { 166 | "version": "1.0.2", 167 | "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.0.2.tgz", 168 | "integrity": "sha1-4QgOBljjALBilJkMxw4VAiNf1VA=" 169 | }, 170 | "forever-agent": { 171 | "version": "0.6.1", 172 | "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz", 173 | "integrity": "sha1-+8cfDEGt6zf5bFd60e1C2P2sypE=" 174 | }, 175 | "form-data": { 176 | "version": "2.1.4", 177 | "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.1.4.tgz", 178 | "integrity": "sha1-M8GDrPGTJ27KqYFDpp6Uv+4XUNE=", 179 | "requires": { 180 | "asynckit": "0.4.0", 181 | "combined-stream": "1.0.5", 182 | "mime-types": "2.1.15" 183 | } 184 | }, 185 | "getpass": { 186 | "version": "0.1.7", 187 | "resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz", 188 | "integrity": "sha1-Xv+OPmhNVprkyysSgmBOi6YhSfo=", 189 | "requires": { 190 | "assert-plus": "1.0.0" 191 | }, 192 | "dependencies": { 193 | "assert-plus": { 194 | "version": "1.0.0", 195 | "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", 196 | "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=" 197 | } 198 | } 199 | }, 200 | "har-schema": { 201 | "version": "1.0.5", 202 | "resolved": "https://registry.npmjs.org/har-schema/-/har-schema-1.0.5.tgz", 203 | "integrity": "sha1-0mMTX0MwfALGAq/I/pWXDAFRNp4=" 204 | }, 205 | "har-validator": { 206 | "version": "4.2.1", 207 | "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-4.2.1.tgz", 208 | "integrity": "sha1-M0gdDxu/9gDdID11gSpqX7oALio=", 209 | "requires": { 210 | "ajv": "4.11.8", 211 | "har-schema": "1.0.5" 212 | } 213 | }, 214 | "hawk": { 215 | "version": "3.1.3", 216 | "resolved": "https://registry.npmjs.org/hawk/-/hawk-3.1.3.tgz", 217 | "integrity": "sha1-B4REvXwWQLD+VA0sm3PVlnjo4cQ=", 218 | "requires": { 219 | "boom": "2.10.1", 220 | "cryptiles": "2.0.5", 221 | "hoek": "2.16.3", 222 | "sntp": "1.0.9" 223 | } 224 | }, 225 | "hoek": { 226 | "version": "2.16.3", 227 | "resolved": "https://registry.npmjs.org/hoek/-/hoek-2.16.3.tgz", 228 | "integrity": "sha1-ILt0A9POo5jpHcRxCo/xuCdKJe0=" 229 | }, 230 | "http-signature": { 231 | "version": "1.1.1", 232 | "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.1.1.tgz", 233 | "integrity": "sha1-33LiZwZs0Kxn+3at+OE0qPvPkb8=", 234 | "requires": { 235 | "assert-plus": "0.2.0", 236 | "jsprim": "1.4.0", 237 | "sshpk": "1.13.1" 238 | } 239 | }, 240 | "https": { 241 | "version": "1.0.0", 242 | "resolved": "https://registry.npmjs.org/https/-/https-1.0.0.tgz", 243 | "integrity": "sha1-PDfHrhqO65ZpBKKtHpdaGUt+06Q=" 244 | }, 245 | "ieee754": { 246 | "version": "1.1.8", 247 | "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.1.8.tgz", 248 | "integrity": "sha1-vjPUCsEO8ZJnAfbwii2G+/0a0+Q=" 249 | }, 250 | "is-typedarray": { 251 | "version": "1.0.0", 252 | "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", 253 | "integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=" 254 | }, 255 | "isarray": { 256 | "version": "1.0.0", 257 | "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", 258 | "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=" 259 | }, 260 | "isstream": { 261 | "version": "0.1.2", 262 | "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz", 263 | "integrity": "sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo=" 264 | }, 265 | "jmespath": { 266 | "version": "0.15.0", 267 | "resolved": "https://registry.npmjs.org/jmespath/-/jmespath-0.15.0.tgz", 268 | "integrity": "sha1-o/Iiqarp+Wb10nx5ZRDigJF2Qhc=" 269 | }, 270 | "jsbn": { 271 | "version": "0.1.1", 272 | "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz", 273 | "integrity": "sha1-peZUwuWi3rXyAdls77yoDA7y9RM=", 274 | "optional": true 275 | }, 276 | "json-schema": { 277 | "version": "0.2.3", 278 | "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.2.3.tgz", 279 | "integrity": "sha1-tIDIkuWaLwWVTOcnvT8qTogvnhM=" 280 | }, 281 | "json-stable-stringify": { 282 | "version": "1.0.1", 283 | "resolved": "https://registry.npmjs.org/json-stable-stringify/-/json-stable-stringify-1.0.1.tgz", 284 | "integrity": "sha1-mnWdOcXy/1A/1TAGRu1EX4jE+a8=", 285 | "requires": { 286 | "jsonify": "0.0.0" 287 | } 288 | }, 289 | "json-stringify-safe": { 290 | "version": "5.0.1", 291 | "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", 292 | "integrity": "sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus=" 293 | }, 294 | "jsonify": { 295 | "version": "0.0.0", 296 | "resolved": "https://registry.npmjs.org/jsonify/-/jsonify-0.0.0.tgz", 297 | "integrity": "sha1-LHS27kHZPKUbe1qu6PUDYx0lKnM=" 298 | }, 299 | "jsprim": { 300 | "version": "1.4.0", 301 | "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.0.tgz", 302 | "integrity": "sha1-o7h+QCmNjDgFUtjMdiigu5WiKRg=", 303 | "requires": { 304 | "assert-plus": "1.0.0", 305 | "extsprintf": "1.0.2", 306 | "json-schema": "0.2.3", 307 | "verror": "1.3.6" 308 | }, 309 | "dependencies": { 310 | "assert-plus": { 311 | "version": "1.0.0", 312 | "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", 313 | "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=" 314 | } 315 | } 316 | }, 317 | "lodash": { 318 | "version": "4.17.4", 319 | "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.4.tgz", 320 | "integrity": "sha1-eCA6TRwyiuHYbcpkYONptX9AVa4=" 321 | }, 322 | "mime-db": { 323 | "version": "1.27.0", 324 | "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.27.0.tgz", 325 | "integrity": "sha1-gg9XIpa70g7CXtVeW13oaeVDbrE=" 326 | }, 327 | "mime-types": { 328 | "version": "2.1.15", 329 | "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.15.tgz", 330 | "integrity": "sha1-pOv1BkCUVpI3uM9wBGd20J/JKu0=", 331 | "requires": { 332 | "mime-db": "1.27.0" 333 | } 334 | }, 335 | "oauth-sign": { 336 | "version": "0.8.2", 337 | "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.8.2.tgz", 338 | "integrity": "sha1-Rqarfwrq2N6unsBWV4C31O/rnUM=" 339 | }, 340 | "performance-now": { 341 | "version": "0.2.0", 342 | "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-0.2.0.tgz", 343 | "integrity": "sha1-M+8wxcd9TqIcWlOGnZG1bY8lVeU=" 344 | }, 345 | "punycode": { 346 | "version": "1.3.2", 347 | "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.3.2.tgz", 348 | "integrity": "sha1-llOgNvt8HuQjQvIyXM7v6jkmxI0=" 349 | }, 350 | "qs": { 351 | "version": "6.4.0", 352 | "resolved": "https://registry.npmjs.org/qs/-/qs-6.4.0.tgz", 353 | "integrity": "sha1-E+JtKK1rD/qpExLNO/cI7TUecjM=" 354 | }, 355 | "querystring": { 356 | "version": "0.2.0", 357 | "resolved": "https://registry.npmjs.org/querystring/-/querystring-0.2.0.tgz", 358 | "integrity": "sha1-sgmEkgO7Jd+CDadW50cAWHhSFiA=" 359 | }, 360 | "request": { 361 | "version": "2.81.0", 362 | "resolved": "https://registry.npmjs.org/request/-/request-2.81.0.tgz", 363 | "integrity": "sha1-xpKJRqDgbF+Nb4qTM0af/aRimKA=", 364 | "requires": { 365 | "aws-sign2": "0.6.0", 366 | "aws4": "1.6.0", 367 | "caseless": "0.12.0", 368 | "combined-stream": "1.0.5", 369 | "extend": "3.0.1", 370 | "forever-agent": "0.6.1", 371 | "form-data": "2.1.4", 372 | "har-validator": "4.2.1", 373 | "hawk": "3.1.3", 374 | "http-signature": "1.1.1", 375 | "is-typedarray": "1.0.0", 376 | "isstream": "0.1.2", 377 | "json-stringify-safe": "5.0.1", 378 | "mime-types": "2.1.15", 379 | "oauth-sign": "0.8.2", 380 | "performance-now": "0.2.0", 381 | "qs": "6.4.0", 382 | "safe-buffer": "5.1.1", 383 | "stringstream": "0.0.5", 384 | "tough-cookie": "2.3.2", 385 | "tunnel-agent": "0.6.0", 386 | "uuid": "3.0.1" 387 | } 388 | }, 389 | "requestretry": { 390 | "version": "1.12.0", 391 | "resolved": "https://registry.npmjs.org/requestretry/-/requestretry-1.12.0.tgz", 392 | "integrity": "sha1-fxCizQ7bfkO/motsv+2iAvsyCGA=", 393 | "requires": { 394 | "extend": "3.0.1", 395 | "lodash": "4.17.4", 396 | "request": "2.81.0", 397 | "when": "3.7.8" 398 | } 399 | }, 400 | "safe-buffer": { 401 | "version": "5.1.1", 402 | "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.1.tgz", 403 | "integrity": "sha512-kKvNJn6Mm93gAczWVJg7wH+wGYWNrDHdWvpUmHyEsgCtIwwo3bqPtV4tR5tuPaUhTOo/kvhVwd8XwwOllGYkbg==" 404 | }, 405 | "sax": { 406 | "version": "1.2.1", 407 | "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.1.tgz", 408 | "integrity": "sha1-e45lYZCyKOgaZq6nSEgNgozS03o=" 409 | }, 410 | "slack-node": { 411 | "version": "0.1.8", 412 | "resolved": "https://registry.npmjs.org/slack-node/-/slack-node-0.1.8.tgz", 413 | "integrity": "sha1-zamN6GgUhbMB3GdC3cOJcRf600k=", 414 | "requires": { 415 | "requestretry": "1.12.0" 416 | } 417 | }, 418 | "sntp": { 419 | "version": "1.0.9", 420 | "resolved": "https://registry.npmjs.org/sntp/-/sntp-1.0.9.tgz", 421 | "integrity": "sha1-ZUEYTMkK7qbG57NeJlkIJEPGYZg=", 422 | "requires": { 423 | "hoek": "2.16.3" 424 | } 425 | }, 426 | "sshpk": { 427 | "version": "1.13.1", 428 | "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.13.1.tgz", 429 | "integrity": "sha1-US322mKHFEMW3EwY/hzx2UBzm+M=", 430 | "requires": { 431 | "asn1": "0.2.3", 432 | "assert-plus": "1.0.0", 433 | "bcrypt-pbkdf": "1.0.1", 434 | "dashdash": "1.14.1", 435 | "ecc-jsbn": "0.1.1", 436 | "getpass": "0.1.7", 437 | "jsbn": "0.1.1", 438 | "tweetnacl": "0.14.5" 439 | }, 440 | "dependencies": { 441 | "assert-plus": { 442 | "version": "1.0.0", 443 | "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", 444 | "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=" 445 | } 446 | } 447 | }, 448 | "stringstream": { 449 | "version": "0.0.5", 450 | "resolved": "https://registry.npmjs.org/stringstream/-/stringstream-0.0.5.tgz", 451 | "integrity": "sha1-TkhM1N5aC7vuGORjB3EKioFiGHg=" 452 | }, 453 | "tough-cookie": { 454 | "version": "2.3.2", 455 | "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.3.2.tgz", 456 | "integrity": "sha1-8IH3bkyFcg5sN6X6ztc3FQ2EByo=", 457 | "requires": { 458 | "punycode": "1.4.1" 459 | }, 460 | "dependencies": { 461 | "punycode": { 462 | "version": "1.4.1", 463 | "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz", 464 | "integrity": "sha1-wNWmOycYgArY4esPpSachN1BhF4=" 465 | } 466 | } 467 | }, 468 | "tunnel-agent": { 469 | "version": "0.6.0", 470 | "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", 471 | "integrity": "sha1-J6XeoGs2sEoKmWZ3SykIaPD8QP0=", 472 | "requires": { 473 | "safe-buffer": "5.1.1" 474 | } 475 | }, 476 | "tweetnacl": { 477 | "version": "0.14.5", 478 | "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz", 479 | "integrity": "sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q=", 480 | "optional": true 481 | }, 482 | "url": { 483 | "version": "0.11.0", 484 | "resolved": "https://registry.npmjs.org/url/-/url-0.11.0.tgz", 485 | "integrity": "sha1-ODjpfPxgUh63PFJajlW/3Z4uKPE=", 486 | "requires": { 487 | "punycode": "1.3.2", 488 | "querystring": "0.2.0" 489 | } 490 | }, 491 | "uuid": { 492 | "version": "3.0.1", 493 | "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.0.1.tgz", 494 | "integrity": "sha1-ZUS7ot/ajBzxfmKaOjBeK7H+5sE=" 495 | }, 496 | "verror": { 497 | "version": "1.3.6", 498 | "resolved": "https://registry.npmjs.org/verror/-/verror-1.3.6.tgz", 499 | "integrity": "sha1-z/XfEpRtKX0rqu+qJoniW+AcAFw=", 500 | "requires": { 501 | "extsprintf": "1.0.2" 502 | } 503 | }, 504 | "when": { 505 | "version": "3.7.8", 506 | "resolved": "https://registry.npmjs.org/when/-/when-3.7.8.tgz", 507 | "integrity": "sha1-xxMLan6gRpPoQs3J56Hyqjmjn4I=" 508 | }, 509 | "xml2js": { 510 | "version": "0.4.17", 511 | "resolved": "https://registry.npmjs.org/xml2js/-/xml2js-0.4.17.tgz", 512 | "integrity": "sha1-F76T6q4/O3eTWceVtBlwWogX6Gg=", 513 | "requires": { 514 | "sax": "1.2.1", 515 | "xmlbuilder": "4.2.1" 516 | } 517 | }, 518 | "xmlbuilder": { 519 | "version": "4.2.1", 520 | "resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-4.2.1.tgz", 521 | "integrity": "sha1-qlijBBoGb5DqoWwvU4n/GfP0YaU=", 522 | "requires": { 523 | "lodash": "4.17.4" 524 | } 525 | } 526 | } 527 | } 528 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "gcp-alert-service", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "repository": { 10 | "type": "git", 11 | "url": "git+https://github.com/doitintl/gcp-alert-service.git" 12 | }, 13 | "author": "", 14 | "license": "ISC", 15 | "bugs": { 16 | "url": "https://github.com/doitintl/gcp-alert-service/issues" 17 | }, 18 | "homepage": "https://github.com/doitintl/gcp-alert-service#readme", 19 | "dependencies": { 20 | "aws-sdk": "~2.80.0", 21 | "https": "^1.0.0", 22 | "slack-node": "~0.1.8", 23 | "url": "^0.11.0" 24 | } 25 | } 26 | --------------------------------------------------------------------------------