Skip to content

Commit

Permalink
Merge 9d7b051 into b91b541
Browse files Browse the repository at this point in the history
  • Loading branch information
anniel-stripe committed Jan 11, 2023
2 parents b91b541 + 9d7b051 commit 6cad1f5
Show file tree
Hide file tree
Showing 9 changed files with 152 additions and 56 deletions.
26 changes: 17 additions & 9 deletions lib/Webhooks.js

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

17 changes: 10 additions & 7 deletions lib/multipart.js

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

27 changes: 22 additions & 5 deletions lib/utils.js

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

12 changes: 8 additions & 4 deletions src/Error.ts
Original file line number Diff line number Diff line change
Expand Up @@ -128,10 +128,14 @@ class StripeConnectionError extends StripeError {}
* webhook fails
*/
class StripeSignatureVerificationError extends StripeError {
header: string;
payload: string;

constructor(header: string, payload: string, raw: StripeRawError = {}) {
header: string | Buffer;
payload: string | Buffer;

constructor(
header: string | Buffer,
payload: string | Buffer,
raw: StripeRawError = {}
) {
super(raw);
this.header = header;
this.payload = payload;
Expand Down
2 changes: 1 addition & 1 deletion src/Types.d.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
/* eslint-disable camelcase */
type AppInfo = {name?: string} & Record<string, unknown>;
type BufferedFile = {name: string; type: string; file: {data: Buffer}};
type BufferedFile = {name: string; type: string; file: {data: Uint8Array}};
type HttpClientResponseError = {code: number};
type HttpHeaderValue = string | number | string[];
type MethodSpec = {
Expand Down
34 changes: 21 additions & 13 deletions src/Webhooks.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,8 @@ type WebhookParsedHeader = {
};
type WebhookParsedEvent = {
details: WebhookParsedHeader;
decodedPayload: string;
decodedHeader: string;
decodedPayload: WebhookHeader;
decodedHeader: WebhookPayload;
};
type WebhookTestHeaderOptions = {
timestamp: number;
Expand Down Expand Up @@ -78,8 +78,10 @@ const Webhook: WebhookObject = {
cryptoProvider
);

// @ts-ignore
const jsonPayload = JSON.parse(payload);
const jsonPayload =
payload instanceof Uint8Array
? JSON.parse(new TextDecoder().decode(payload))
: JSON.parse(payload);
return jsonPayload;
},

Expand All @@ -99,7 +101,10 @@ const Webhook: WebhookObject = {
);

// @ts-ignore
const jsonPayload = JSON.parse(payload);
const jsonPayload =
payload instanceof Uint8Array
? JSON.parse(new TextDecoder().decode(payload))
: JSON.parse(payload);
return jsonPayload;
},

Expand Down Expand Up @@ -218,9 +223,11 @@ function parseEventDetails(
encodedHeader: WebhookHeader,
expectedScheme: string
): WebhookParsedEvent {
const decodedPayload = Buffer.isBuffer(encodedPayload)
? encodedPayload.toString('utf8')
: encodedPayload;
const textDecoder = new TextDecoder('utf8');
const decodedPayload =
encodedPayload instanceof Uint8Array
? textDecoder.decode(encodedPayload)
: encodedPayload;

// Express's type for `Request#headers` is `string | []string`
// which is because the `set-cookie` header is an array,
Expand All @@ -232,9 +239,10 @@ function parseEventDetails(
);
}

const decodedHeader = Buffer.isBuffer(encodedHeader)
? encodedHeader.toString('utf8')
: encodedHeader;
const decodedHeader =
encodedHeader instanceof Uint8Array
? textDecoder.decode(encodedHeader)
: encodedHeader;

const details = parseHeader(decodedHeader, expectedScheme);

Expand All @@ -259,7 +267,7 @@ function parseEventDetails(

function validateComputedSignature(
payload: WebhookPayload,
header: string,
header: WebhookHeader,
details: WebhookParsedHeader,
expectedSignature: string,
tolerance: number
Expand Down Expand Up @@ -292,7 +300,7 @@ function validateComputedSignature(
}

function parseHeader(
header: string,
header: WebhookHeader,
scheme: string
): WebhookParsedHeader | null {
if (typeof header !== 'string') {
Expand Down
27 changes: 16 additions & 11 deletions src/multipart.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ class StreamProcessingError extends StripeError {}
type MultipartCallbackReturn = any;
type MultipartCallback = (
error: Error | null,
data: Buffer | string | null
data: Uint8Array | string | null
) => MultipartCallbackReturn;
// Method for formatting HTTP body for the multipart/form-data specification
// Mostly taken from Fermata.js
Expand All @@ -15,20 +15,25 @@ const multipartDataGenerator = (
method: string,
data: MultipartRequestData,
headers: RequestHeaders
): Buffer => {
): Uint8Array => {
const segno = (
Math.round(Math.random() * 1e16) + Math.round(Math.random() * 1e16)
).toString();
headers['Content-Type'] = `multipart/form-data; boundary=${segno}`;
let buffer = Buffer.alloc(0);
const textEncoder = new TextEncoder();

let buffer = new Uint8Array(0);
const endBuffer = textEncoder.encode('\r\n');

function push(l: any): void {
const prevBuffer = buffer;
const newBuffer = l instanceof Buffer ? l : Buffer.from(l);
buffer = Buffer.alloc(prevBuffer.length + newBuffer.length + 2);
prevBuffer.copy(buffer);
newBuffer.copy(buffer, prevBuffer.length);
buffer.write('\r\n', buffer.length - 2);
const newBuffer =
l instanceof Uint8Array ? l : new Uint8Array(textEncoder.encode(l));
buffer = new Uint8Array(prevBuffer.length + newBuffer.length + 2);

buffer.set(prevBuffer);
buffer.set(newBuffer, prevBuffer.length);
buffer.set(endBuffer, buffer.length - 2);
}

function q(s: string): string {
Expand Down Expand Up @@ -71,15 +76,15 @@ const streamProcessor = (
headers: RequestHeaders,
callback: MultipartCallback
): void => {
const bufferArray: Array<Buffer> = [];
const bufferArray: Array<Uint8Array> = [];
data.file.data
.on('data', (line: Buffer) => {
.on('data', (line: Uint8Array) => {
bufferArray.push(line);
})
.once('end', () => {
// @ts-ignore
const bufferData: BufferedFile = Object.assign({}, data);
bufferData.file.data = Buffer.concat(bufferArray);
bufferData.file.data = utils.concat(bufferArray);
const buffer = multipartDataGenerator(method, bufferData, headers);
callback(null, buffer);
})
Expand Down
32 changes: 26 additions & 6 deletions src/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -216,9 +216,10 @@ const utils = {
/**
* Secure compare, from https://github.com/freewil/scmp
*/
secureCompare: (a: Uint8Array, b: Uint8Array): boolean => {
a = Buffer.from(a);
b = Buffer.from(b);
secureCompare: (a: string, b: string): boolean => {
if (!a || !b) {
throw new Error('secureCompare must receive two arguments');
}

// return early here if buffer lengths are not equal since timingSafeEqual
// will throw if buffer lengths are not equal
Expand All @@ -229,14 +230,17 @@ const utils = {
// use crypto.timingSafeEqual if available (since Node.js v6.6.0),
// otherwise use our own scmp-internal function.
if (crypto.timingSafeEqual) {
return crypto.timingSafeEqual(a, b);
const textEncoder = new TextEncoder();
const aEncoded: Uint8Array = textEncoder.encode(a);
const bEncoded: Uint8Array = textEncoder.encode(b);
return crypto.timingSafeEqual(aEncoded, bEncoded);
}

const len = a.length;
let result = 0;

for (let i = 0; i < len; ++i) {
result |= a[i] ^ b[i];
result |= a.charCodeAt(i) ^ b.charCodeAt(i);
}
return result === 0;
},
Expand Down Expand Up @@ -381,7 +385,7 @@ const utils = {

if (utils.isObject(value)) {
if (
!Buffer.isBuffer(value) &&
!(value instanceof Uint8Array) &&
!Object.prototype.hasOwnProperty.call(value, 'data')
) {
// Non-buffer non-file Objects are recursively flattened
Expand Down Expand Up @@ -439,6 +443,22 @@ const utils = {
platform: process.platform,
};
},

/**
* Joins an array of Uint8Arrays into a single Uint8Array
*/
concat: (arrays: Array<Uint8Array>): Uint8Array => {
const totalLength = arrays.reduce((len, array) => len + array.length, 0);
const merged = new Uint8Array(totalLength);

let offset = 0;
arrays.forEach((array) => {
merged.set(array, offset);
offset += array.length;
});

return merged;
},
};

function emitWarning(warning: string): void {
Expand Down
Loading

0 comments on commit 6cad1f5

Please sign in to comment.