Skip to content

Commit a4975aa

Browse files
authored
feat(webhookValidator): Add wh signature validator
Add webhook signature checker
1 parent 7c0ce47 commit a4975aa

File tree

6 files changed

+63
-8
lines changed

6 files changed

+63
-8
lines changed

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@
2525
"test": "npm run lint && jest",
2626
"test:watch": "npm t -- --watch",
2727
"test:codecov": "npx codecov",
28-
"docs": "trash build/docs && typedoc src && opn build/docs/index.html",
28+
"docs": "trash build/docs && typedoc src",
2929
"docs:publish": "npm run docs && gh-pages -d build/docs",
3030
"release": "standard-version",
3131
"prepare": "npm run build",

src/index.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515
* limitations under the License.
1616
*/
1717

18-
import { SecurityOptions, getSecurity } from './lib/api/security';
18+
import { SecurityOptions, getSecurity, validateWebhookSignature, WebhookValidatePayload } from './lib/api/security';
1919
import { Client, ClientOptions } from './lib/client';
2020
import { TransformSchema } from './schema/transforms.schema';
2121

@@ -42,4 +42,6 @@ export {
4242
TransformSchema,
4343
SecurityOptions,
4444
getSecurity,
45+
validateWebhookSignature,
46+
WebhookValidatePayload
4547
};

src/lib/api/security.spec.browser.ts

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515
* limitations under the License.
1616
*/
1717

18-
import { getSecurity } from './security';
18+
import { getSecurity, validateWebhookSignature } from './security';
1919
import * as utils from './../utils';
2020

2121
describe('api/security', () => {
@@ -35,4 +35,19 @@ describe('api/security', () => {
3535
expect(() => getSecurity(policy, appSecret)).toThrowError('getSecurity is only supported in nodejs');
3636
});
3737
});
38+
39+
describe('validateWebhookSignature', () => {
40+
it('should throw not supported error', () => {
41+
const testRawData = '{"id": 6844, "action": "fp.upload", "timestamp": 1559199901, "text": {"url": "http://cdn.filestackapi.dev/xK88QArxRiyVvFzo4p33", "client": "Computer", "data": {"filename": "01 (1).png", "type": "image/png", "size": 881855}}}';
42+
43+
const correctSignature = {
44+
signature: '14495b54b246e1352bb69cd91c5c1bfe2221f2d0330414b3df8f5fb2903db730',
45+
timestamp: '1559199901',
46+
};
47+
48+
const secret = 'Y5cWb1rdRDSTSqEjF5pv';
49+
50+
expect(() => validateWebhookSignature(secret, testRawData, correctSignature)).toThrowError('validateWebhookSignature is only supported in nodejs');
51+
});
52+
});
3853
});

src/lib/api/security.spec.node.ts

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515
* limitations under the License.
1616
*/
1717

18-
import { getSecurity } from './security';
18+
import { getSecurity, validateWebhookSignature } from './security';
1919

2020
describe('api:security', () => {
2121
describe('getSecurity', () => {
@@ -28,7 +28,8 @@ describe('api:security', () => {
2828
const appSecret = 'testAppSecret';
2929
const result = getSecurity(policy, appSecret);
3030
const expected = {
31-
policy: 'eyJleHBpcnkiOjE1MjM1OTU2MDAsImNhbGwiOlsicGljayIsInJlYWQiLCJzdGF0Iiwid3JpdGUiLCJ3cml0ZVVybCIsInN0b3JlIiwiY29udmVydCIsInJlbW92ZSIsImV4aWYiLCJydW5Xb3JrZmxvdyJdLCJoYW5kbGUiOiJURVNUX0hBTkRMRSJ9',
31+
policy:
32+
'eyJleHBpcnkiOjE1MjM1OTU2MDAsImNhbGwiOlsicGljayIsInJlYWQiLCJzdGF0Iiwid3JpdGUiLCJ3cml0ZVVybCIsInN0b3JlIiwiY29udmVydCIsInJlbW92ZSIsImV4aWYiLCJydW5Xb3JrZmxvdyJdLCJoYW5kbGUiOiJURVNUX0hBTkRMRSJ9',
3233
signature: '7df0536104cdcc16370ad6494cdbda30c9773a62eec6e5153fa539544db6206e',
3334
};
3435
expect(result).toEqual(expected);
@@ -45,4 +46,19 @@ describe('api:security', () => {
4546
expect(() => getSecurity(policy, appSecret)).toThrowError('Invalid security params');
4647
});
4748
});
49+
50+
describe('validateWebhookSignature', () => {
51+
it('should pass validation on proper signature', () => {
52+
const testRawData = '{"id": 6844, "action": "fp.upload", "timestamp": 1559199901, "text": {"url": "http://cdn.filestackapi.dev/xK88QArxRiyVvFzo4p33", "client": "Computer", "data": {"filename": "01 (1).png", "type": "image/png", "size": 881855}}}';
53+
54+
const correctSignature = {
55+
signature: '14495b54b246e1352bb69cd91c5c1bfe2221f2d0330414b3df8f5fb2903db730',
56+
timestamp: '1559199901',
57+
};
58+
59+
const secret = 'Y5cWb1rdRDSTSqEjF5pv';
60+
61+
expect(validateWebhookSignature(secret, testRawData, correctSignature)).toBeTruthy();
62+
});
63+
});
4864
});

src/lib/api/security.ts

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,3 +68,28 @@ export const getSecurity = (policyOptions: SecurityOptions, appSecret: string):
6868

6969
return { policy, signature };
7070
};
71+
72+
export interface WebhookValidatePayload {
73+
timestamp: string;
74+
signature: string;
75+
}
76+
77+
/**
78+
* Check webhook signature
79+
*
80+
* @param secret - app secred
81+
* @param rawBody - unchanged raw webhook body
82+
* @param toCompare - data from wh response headers
83+
*/
84+
export const validateWebhookSignature = (secret: string, rawBody: string, toCompare: WebhookValidatePayload) => {
85+
if (!isNode()) {
86+
throw new Error('validateWebhookSignature is only supported in nodejs');
87+
}
88+
89+
const hash = requireNode('crypto')
90+
.createHmac('sha256', secret)
91+
.update(`${toCompare.timestamp}.${rawBody}`)
92+
.digest('hex');
93+
94+
return hash === toCompare.signature;
95+
};

src/schema/picker.schema.ts

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -140,7 +140,6 @@ export const PickerParamsSchema = {
140140
{
141141
type: 'integer',
142142
minimum: 1,
143-
maximum: 1000000,
144143
},
145144
],
146145
},
@@ -152,7 +151,6 @@ export const PickerParamsSchema = {
152151
{
153152
type: 'integer',
154153
minimum: 1,
155-
maximum: 1000000,
156154
},
157155
],
158156
},
@@ -164,7 +162,6 @@ export const PickerParamsSchema = {
164162
{
165163
type: 'integer',
166164
minimum: 1,
167-
maximum: 1000000,
168165
},
169166
],
170167
},

0 commit comments

Comments
 (0)