From 10cd1474018027a7a758ec28dac2e33d0e9ce7f8 Mon Sep 17 00:00:00 2001 From: Barry Date: Wed, 25 Sep 2019 17:11:39 -0400 Subject: [PATCH 1/4] Added the ability for users to specify a CA bundle for HTTPS requests --- lib/base/RequestClient.js | 6 ++++ lib/http/request.js | 1 + package-lock.json | 6 ++++ package.json | 1 + spec/unit/base/RequestClient.spec.js | 46 ++++++++++++++++++++++++++++ 5 files changed, 60 insertions(+) diff --git a/lib/base/RequestClient.js b/lib/base/RequestClient.js index e3a630380f..bd487643eb 100644 --- a/lib/base/RequestClient.js +++ b/lib/base/RequestClient.js @@ -2,6 +2,7 @@ var _ = require('lodash'); var http = require('request'); +var fs = require('fs'); var Q = require('q'); var Response = require('../http/response'); var Request = require('../http/request'); @@ -53,6 +54,10 @@ RequestClient.prototype.request = function(opts) { forever: opts.forever === false ? false : true, }; + if (process.env.TWILIO_CA_BUNDLE !== undefined) { + options.ca = fs.readFileSync(process.env.TWILIO_CA_BUNDLE); + } + if (!_.isNull(opts.data)) { options.formData = opts.data; } @@ -69,6 +74,7 @@ RequestClient.prototype.request = function(opts) { params: options.qs, data: options.formData, headers: options.headers, + ca: options.ca }; var _this = this; diff --git a/lib/http/request.js b/lib/http/request.js index 90e038a748..5e5eac0f4e 100644 --- a/lib/http/request.js +++ b/lib/http/request.js @@ -11,6 +11,7 @@ var Request = function(opts) { this.params = opts.params || this.ANY; this.data = opts.data || this.ANY; this.headers = opts.headers || this.ANY; + this.ca = opts.ca; }; Request.prototype.ANY = '*'; diff --git a/package-lock.json b/package-lock.json index a5c8b1341f..bdbbf39fea 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1535,6 +1535,12 @@ "minimist": "0.0.8" } }, + "mock-fs": { + "version": "4.10.1", + "resolved": "https://registry.npmjs.org/mock-fs/-/mock-fs-4.10.1.tgz", + "integrity": "sha512-w22rOL5ZYu6HbUehB5deurghGM0hS/xBVyHMGKOuQctkk93J9z9VEOhDsiWrXOprVNQpP9uzGKdl8v9mFspKuw==", + "dev": true + }, "module-not-found-error": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/module-not-found-error/-/module-not-found-error-1.0.1.tgz", diff --git a/package.json b/package.json index 73afd9a6b2..30836613f2 100644 --- a/package.json +++ b/package.json @@ -39,6 +39,7 @@ "jasmine": "^3.4.0", "jsdoc": "^3.6.3", "jshint": "^2.10.2", + "mock-fs": "^4.10.1", "node-mocks-http": "^1.8.0", "proxyquire": "1.8.0", "typescript": "^2.8.3" diff --git a/spec/unit/base/RequestClient.spec.js b/spec/unit/base/RequestClient.spec.js index 61524375e0..70aa696b8b 100644 --- a/spec/unit/base/RequestClient.spec.js +++ b/spec/unit/base/RequestClient.spec.js @@ -1,3 +1,4 @@ +var mockfs = require('mock-fs'); var proxyquire = require('proxyquire'); describe('lastResponse and lastRequest defined', function() { @@ -85,3 +86,48 @@ describe('lastRequest defined, lastResponse undefined', function() { }); }); + +describe('User specified CA bundle', function() { + var client; + beforeEach(function() { + RequestClientMock = proxyquire('../../../lib/base/RequestClient', { + request: function (options, callback) { + callback('failed', null); + }, + }); + + client = new RequestClientMock(); + + options = { + method: 'GET', + uri: 'test-uri', + username: 'test-username', + password: 'test-password', + headers: {'test-header-key': 'test-header-value'}, + params: {'test-param-key': 'test-param-value'}, + data: {'test-data-key': 'test-data-value'} + }; + + mockfs({ + '/path/to/ca': { + 'test-ca.pem': 'test ca data' + } + }); + }); + + afterEach(function () { + mockfs.restore(); + }); + + it('should not modify CA if not specified', function() { + client.request(options); + expect(client.lastRequest.ca).toBeUndefined(); + }); + + it('should use CA if it is specified', function() { + process.env.TWILIO_CA_BUNDLE = '/path/to/ca/test-ca.pem'; + client.request(options); + expect(client.lastRequest.ca.toString()).toEqual('test ca data'); + delete process.env.TWILIO_CA_BUNDLE; + }); +}); From 9fed9aa80140ed945a4f3fc4a40dbfe8933229f1 Mon Sep 17 00:00:00 2001 From: Barry Date: Wed, 25 Sep 2019 18:21:51 -0400 Subject: [PATCH 2/4] Added example code and environment variable documentation --- README.md | 7 +++++++ examples/example.js | 4 ++++ 2 files changed, 11 insertions(+) diff --git a/README.md b/README.md index b8143e00fd..9667b7875e 100644 --- a/README.md +++ b/README.md @@ -68,5 +68,12 @@ To run just one specific test file instead of the whole suite, provide a JavaScr npm run test -- -m .\*client.\* ``` +####Environment Variables + +* `TWILIO_ACCOUNT_SID` - TODO +* `TWILIO_AUTH_TOKEN` - TODO +* `TWILIO_CA_BUNDLE` - Optional, if set will use the specified path to a CA bundle. Useful for environments using SSL decryption + + [apidocs]: https://www.twilio.com/docs/api [libdocs]: https://twilio.github.io/twilio-node diff --git a/examples/example.js b/examples/example.js index 6a3f904ba6..be2c7b5ef7 100644 --- a/examples/example.js +++ b/examples/example.js @@ -5,6 +5,10 @@ var Twilio = require('../lib'); var accountSid = process.env.TWILIO_ACCOUNT_SID; var token = process.env.TWILIO_AUTH_TOKEN; +// Uncomment the following line to specify a custom CA bundle for HTTPS requests: +// process.env.TWILIO_CA_BUNDLE = '/path/to/cert.pem'; +// You can also set this as a regular environment variable outside of the code + var twilio = new Twilio(accountSid, token); var i = 0; From 6067b94a4553729a794568b1322f7e11e8d836c7 Mon Sep 17 00:00:00 2001 From: childish-sambino Date: Thu, 26 Sep 2019 09:26:03 -0500 Subject: [PATCH 3/4] Adding details about env vars to the README --- README.md | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index 9667b7875e..4bed677adc 100644 --- a/README.md +++ b/README.md @@ -28,6 +28,12 @@ TypeScript is supported for TypeScript version 2.9 and above. Check out these [code examples](examples) in JavaScript and TypeScript to get up and running quickly. +### Environment Variables + +`twilio-node` supports credential storage in environment variables. If no credentials are provided when instantiating the Twilio client (e.g., `const client = require('twilio')();`), the values in following env vars will be used: `TWILIO_ACCOUNT_SID` and `TWILIO_AUTH_TOKEN`. + +If your environment requires SSL decryption, you can set the path to CA bundle in the env var `TWILIO_CA_BUNDLE`. + ## Docker Image The `Dockerfile` present in this repository and its respective `twilio/twilio-node` Docker image are currently used by Twilio for testing purposes only. @@ -68,12 +74,5 @@ To run just one specific test file instead of the whole suite, provide a JavaScr npm run test -- -m .\*client.\* ``` -####Environment Variables - -* `TWILIO_ACCOUNT_SID` - TODO -* `TWILIO_AUTH_TOKEN` - TODO -* `TWILIO_CA_BUNDLE` - Optional, if set will use the specified path to a CA bundle. Useful for environments using SSL decryption - - [apidocs]: https://www.twilio.com/docs/api [libdocs]: https://twilio.github.io/twilio-node From 5c1e6cbce04e5c54c1b0274e7a7db1794dc3ebcd Mon Sep 17 00:00:00 2001 From: Barry Date: Thu, 26 Sep 2019 10:55:48 -0400 Subject: [PATCH 4/4] Added file caching for CA bundle --- lib/base/RequestClient.js | 5 ++++- spec/unit/base/RequestClient.spec.js | 14 ++++++++++++++ 2 files changed, 18 insertions(+), 1 deletion(-) diff --git a/lib/base/RequestClient.js b/lib/base/RequestClient.js index bd487643eb..9d6410cddd 100644 --- a/lib/base/RequestClient.js +++ b/lib/base/RequestClient.js @@ -55,7 +55,10 @@ RequestClient.prototype.request = function(opts) { }; if (process.env.TWILIO_CA_BUNDLE !== undefined) { - options.ca = fs.readFileSync(process.env.TWILIO_CA_BUNDLE); + if (this.ca === undefined) { + this.ca = fs.readFileSync(process.env.TWILIO_CA_BUNDLE); + } + options.ca = this.ca; } if (!_.isNull(opts.data)) { diff --git a/spec/unit/base/RequestClient.spec.js b/spec/unit/base/RequestClient.spec.js index 70aa696b8b..2b5ada1382 100644 --- a/spec/unit/base/RequestClient.spec.js +++ b/spec/unit/base/RequestClient.spec.js @@ -130,4 +130,18 @@ describe('User specified CA bundle', function() { expect(client.lastRequest.ca.toString()).toEqual('test ca data'); delete process.env.TWILIO_CA_BUNDLE; }); + + it('should cache the CA after loading it for the first time', function () { + process.env.TWILIO_CA_BUNDLE = '/path/to/ca/test-ca.pem'; + client.request(options); + mockfs({ + '/path/to/ca': { + 'test-ca.pem': null + } + }); + client.request(options); + expect(client.lastRequest.ca.toString()).toEqual('test ca data'); + delete process.env.TWILIO_CA_BUNDLE; + }) + });