diff --git a/src/receivers/AwsLambdaReceiver.spec.ts b/src/receivers/AwsLambdaReceiver.spec.ts index b6035147a..da6b261d4 100644 --- a/src/receivers/AwsLambdaReceiver.spec.ts +++ b/src/receivers/AwsLambdaReceiver.spec.ts @@ -470,9 +470,11 @@ describe('AwsLambdaReceiver', function () { }); it('should detect invalid signature', async (): Promise => { + const spy = sinon.spy(); const awsReceiver = new AwsLambdaReceiver({ signingSecret: 'my-secret', logger: noopLogger, + invalidRequestSignatureHandler: spy, }); const handler = awsReceiver.toHandler(); const timestamp = Math.floor(Date.now() / 1000); @@ -504,6 +506,7 @@ describe('AwsLambdaReceiver', function () { (_error, _result) => {}, ); assert.equal(response.statusCode, 401); + assert(spy.calledOnce); }); it('should detect too old request timestamp', async (): Promise => { diff --git a/src/receivers/AwsLambdaReceiver.ts b/src/receivers/AwsLambdaReceiver.ts index 5fe9a6d85..00f59fa9c 100644 --- a/src/receivers/AwsLambdaReceiver.ts +++ b/src/receivers/AwsLambdaReceiver.ts @@ -25,6 +25,12 @@ export interface AwsEvent { export type AwsCallback = (error?: Error | string | null, result?: any) => void; +export interface ReceiverInvalidRequestSignatureHandlerArgs { + rawBody: string; + signature: string; + ts: number; +} + export interface AwsResponse { statusCode: number; headers?: { @@ -76,6 +82,7 @@ export interface AwsLambdaReceiverOptions { * @default noop */ customPropertiesExtractor?: (request: AwsEvent) => StringIndexed; + invalidRequestSignatureHandler?: (args: ReceiverInvalidRequestSignatureHandlerArgs) => void; } /* @@ -95,12 +102,15 @@ export default class AwsLambdaReceiver implements Receiver { private customPropertiesExtractor: (request: AwsEvent) => StringIndexed; + private invalidRequestSignatureHandler: (args: ReceiverInvalidRequestSignatureHandlerArgs) => void; + public constructor({ signingSecret, logger = undefined, logLevel = LogLevel.INFO, signatureVerification = true, customPropertiesExtractor = (_) => ({}), + invalidRequestSignatureHandler, }: AwsLambdaReceiverOptions) { // Initialize instance variables, substituting defaults for each value this.signingSecret = signingSecret; @@ -112,6 +122,11 @@ export default class AwsLambdaReceiver implements Receiver { return defaultLogger; })(); this.customPropertiesExtractor = customPropertiesExtractor; + if (invalidRequestSignatureHandler) { + this.invalidRequestSignatureHandler = invalidRequestSignatureHandler; + } else { + this.invalidRequestSignatureHandler = this.defaultInvalidRequestSignatureHandler; + } } public init(app: App): void { @@ -171,7 +186,7 @@ export default class AwsLambdaReceiver implements Receiver { const signature = this.getHeaderValue(awsEvent.headers, 'X-Slack-Signature') as string; const ts = Number(this.getHeaderValue(awsEvent.headers, 'X-Slack-Request-Timestamp')); if (!this.isValidRequestSignature(this.signingSecret, rawBody, signature, ts)) { - this.logger.info(`Invalid request signature detected (X-Slack-Signature: ${signature}, X-Slack-Request-Timestamp: ${ts})`); + this.invalidRequestSignatureHandler({ rawBody, signature, ts }); return Promise.resolve({ statusCode: 401, body: '' }); } } @@ -313,4 +328,10 @@ export default class AwsLambdaReceiver implements Receiver { const caseInsensitiveKey = Object.keys(headers).find((it) => key.toLowerCase() === it.toLowerCase()); return caseInsensitiveKey !== undefined ? headers[caseInsensitiveKey] : undefined; } + + private defaultInvalidRequestSignatureHandler(args: ReceiverInvalidRequestSignatureHandlerArgs): void { + const { signature, ts } = args; + + this.logger.info(`Invalid request signature detected (X-Slack-Signature: ${signature}, X-Slack-Request-Timestamp: ${ts})`); + } }