From bcb3660017fb33c5f1ee6579c3c1218b7843a6b7 Mon Sep 17 00:00:00 2001 From: Richard Marmorstein Date: Wed, 1 Jul 2020 14:50:35 -0400 Subject: [PATCH] Document but discourage protocol --- README.md | 1 + lib/stripe.js | 10 ++++++++++ test/stripe.spec.js | 41 +++++++++++++++++++++++++++++++++++++++++ 3 files changed, 52 insertions(+) diff --git a/README.md b/README.md index 9775f781c7..a18668d656 100644 --- a/README.md +++ b/README.md @@ -175,6 +175,7 @@ const stripe = Stripe('sk_test_...', { | `timeout` | 80000 | [Maximum time each request can take in ms.](#configuring-timeout) | | `host` | `'api.stripe.com'` | Host that requests are made to. | | `port` | 443 | Port that requests are made to. | +| `protocol` | `'https'` | `'https'` or `'http'`. `http` is never appropriate for sending requests to Stripe servers, and we strongly discourage `http`, even in local testing scenarios, as this can result in your credentials being transmitted over an insecure channel. | `telemetry` | `true` | Allow Stripe to send latency [telemetry](#request-latency-telemetry). | Note: Both `maxNetworkRetries` and `timeout` can be overridden on a per-request basis. diff --git a/lib/stripe.js b/lib/stripe.js index 929a7915bf..2abeb88cbc 100644 --- a/lib/stripe.js +++ b/lib/stripe.js @@ -65,6 +65,16 @@ function Stripe(key, config = {}) { this.once = this._emitter.once.bind(this._emitter); this.off = this._emitter.removeListener.bind(this._emitter); + if ( + props.protocol && + props.protocol !== 'https' && + (!props.host || /\.stripe\.com$/.test(props.host)) + ) { + throw new Error( + 'The `https` protocol must be used when sending requests to `*.stripe.com`' + ); + } + this._api = { auth: null, host: props.host || DEFAULT_HOST, diff --git a/test/stripe.spec.js b/test/stripe.spec.js index dc927c0e85..08e27ffddd 100644 --- a/test/stripe.spec.js +++ b/test/stripe.spec.js @@ -58,6 +58,47 @@ describe('Stripe Module', function() { }); }).to.not.throw(); }); + it('should forbid sending http to *.stripe.com', () => { + expect(() => { + Stripe(testUtils.getUserStripeKey(), { + host: 'foo.stripe.com', + protocol: 'http', + }); + }).to.throw(/The `https` protocol must be used/); + + expect(() => { + Stripe(testUtils.getUserStripeKey(), { + protocol: 'http', + }); + }).to.throw(/The `https` protocol must be used/); + + expect(() => { + Stripe(testUtils.getUserStripeKey(), { + protocol: 'http', + host: 'api.stripe.com', + }); + }).to.throw(/The `https` protocol must be used/); + + expect(() => { + Stripe(testUtils.getUserStripeKey(), { + protocol: 'https', + host: 'api.stripe.com', + }); + }).not.to.throw(); + + expect(() => { + Stripe(testUtils.getUserStripeKey(), { + host: 'api.stripe.com', + }); + }).not.to.throw(); + + expect(() => { + Stripe(testUtils.getUserStripeKey(), { + protocol: 'http', + host: 'localhost', + }); + }).not.to.throw(); + }); it('should perform a no-op if null, undefined or empty values are passed', () => { const cases = [null, undefined, '', {}];