Skip to content

Commit

Permalink
Formatting fixes
Browse files Browse the repository at this point in the history
  • Loading branch information
mdlavin committed Feb 20, 2024
1 parent d0cd009 commit d812412
Show file tree
Hide file tree
Showing 2 changed files with 78 additions and 54 deletions.
43 changes: 26 additions & 17 deletions src/sqs.test.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import { v4 as uuid } from 'uuid';
import { LoggerInterface } from '@lifeomic/logging';
import { SQSMessageAction, SQSMessageHandler } from './sqs';
import { promises as fs } from 'fs'
import { privateDecrypt } from 'crypto'
import { promises as fs } from 'fs';
import { privateDecrypt } from 'crypto';

const logger: jest.Mocked<LoggerInterface> = {
info: jest.fn(),
Expand All @@ -12,8 +12,11 @@ const logger: jest.Mocked<LoggerInterface> = {

let publicKey: string;
beforeAll(async () => {
publicKey = await fs.readFile(__dirname + '/__fixtures__/public-key.pem', 'utf8')
})
publicKey = await fs.readFile(
__dirname + '/__fixtures__/public-key.pem',
'utf8',
);
});

beforeEach(() => {
logger.info.mockReset();
Expand Down Expand Up @@ -158,15 +161,21 @@ describe('SQSMessageHandler', () => {
{
error,
encryptedBody: expect.any(String),
publicKeyDescription: 'test-public-key'
publicKeyDescription: 'test-public-key',
},
'Failed to redact message body',
);

// Verify that the encrypted body can be decrypted.
const privateKey = await fs.readFile(__dirname + '/__fixtures__/private-key.pem', 'utf8')
const encryptedBody = logger.error.mock.calls[0][0].encryptedBody
const decrypted = privateDecrypt(privateKey, Buffer.from(encryptedBody, 'base64')).toString('utf8');
const privateKey = await fs.readFile(
__dirname + '/__fixtures__/private-key.pem',
'utf8',
);
const encryptedBody = logger.error.mock.calls[0][0].encryptedBody;
const decrypted = privateDecrypt(
privateKey,
Buffer.from(encryptedBody, 'base64'),
).toString('utf8');
expect(decrypted).toEqual(body);

// Verify the the body was redacted.
Expand All @@ -176,9 +185,9 @@ describe('SQSMessageHandler', () => {
...event,
Records: event.Records.map((record: any) => ({
...record,
body: "[REDACTION FAILED]"
}))
}
body: '[REDACTION FAILED]',
})),
},
},
'Processing SQS topic message',
);
Expand Down Expand Up @@ -219,16 +228,16 @@ describe('SQSMessageHandler', () => {
expect(logger.error).toHaveBeenCalledWith(
{
error,
encryptedBody: "[ENCRYPTION FAILED]", // Signals that encryption failed
publicKeyDescription: 'test-public-key'
encryptedBody: '[ENCRYPTION FAILED]', // Signals that encryption failed
publicKeyDescription: 'test-public-key',
},
'Failed to redact message body',
);

// When encryption fails, the failure is logged.
expect(logger.error).toHaveBeenCalledWith(
{
error: expect.anything()
error: expect.anything(),
},
'Failed to encrypt message body',
);
Expand All @@ -240,9 +249,9 @@ describe('SQSMessageHandler', () => {
...event,
Records: event.Records.map((record: any) => ({
...record,
body: "[REDACTION FAILED]"
}))
}
body: '[REDACTION FAILED]',
})),
},
},
'Processing SQS topic message',
);
Expand Down
89 changes: 52 additions & 37 deletions src/sqs.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import {
processWithOrdering,
withHealthCheckHandling,
} from './utils';
import { publicEncrypt } from 'crypto'
import { publicEncrypt } from 'crypto';

export type SQSMessageHandlerConfig<Message, Context> =
BaseHandlerConfig<Context> & {
Expand Down Expand Up @@ -43,7 +43,7 @@ export type SQSMessageHandlerConfig<Message, Context> =
* example, this could explain who has access to the key or how to get it.
*/
publicKeyDescription: string;
}
};
};

export type SQSMessageAction<Message, Context> = (
Expand Down Expand Up @@ -80,38 +80,53 @@ export type SQSPartialBatchResponse = {
};

const safeRedactor =
(logger: LoggerInterface, redactionConfig: NonNullable<SQSMessageHandlerConfig<any, any>['redactionConfig']>) =>
(body: string) => {
(
logger: LoggerInterface,
redactionConfig: NonNullable<
SQSMessageHandlerConfig<any, any>['redactionConfig']
>,
) =>
(body: string) => {
try {
return redactionConfig.redactMessageBody(body);
} catch (error) {
let encryptedBody;

// If redaction fails, then encrypt the message body and log it.
// Encryption allows for developers to decrypt the message if needed
// but does not log sensitive inforation the the log stream.
try {
return redactionConfig.redactMessageBody(body);
encryptedBody = publicEncrypt(
redactionConfig.publicEncryptionKey,
Buffer.from(body),
).toString('base64');
} catch (error) {
let encryptedBody;

// If redaction fails, then encrypt the message body and log it.
// Encryption allows for developers to decrypt the message if needed
// but does not log sensitive inforation the the log stream.
try {
encryptedBody = publicEncrypt(redactionConfig.publicEncryptionKey, Buffer.from(body)).toString('base64');
} catch (error) {
// If encryption fails, then log the encryption error and replace
// the body with dummy text.
logger.error({ error }, 'Failed to encrypt message body');
encryptedBody = '[ENCRYPTION FAILED]';
}

// Log the redaction error
logger.error({ error, encryptedBody, publicKeyDescription: redactionConfig.publicKeyDescription }, 'Failed to redact message body');
return '[REDACTION FAILED]';
// If encryption fails, then log the encryption error and replace
// the body with dummy text.
logger.error({ error }, 'Failed to encrypt message body');
encryptedBody = '[ENCRYPTION FAILED]';
}
};

// Log the redaction error
logger.error(
{
error,
encryptedBody,
publicKeyDescription: redactionConfig.publicKeyDescription,
},
'Failed to redact message body',
);
return '[REDACTION FAILED]';
}
};

/**
* An abstraction for an SQS message handler.
*/
export class SQSMessageHandler<Message, Context> {
private messageActions: SQSMessageAction<Message, Context>[] = [];

constructor(readonly config: SQSMessageHandlerConfig<Message, Context>) { }
constructor(readonly config: SQSMessageHandlerConfig<Message, Context>) {}

/**
* Adds a message action to the handler.
Expand Down Expand Up @@ -148,12 +163,12 @@ export class SQSMessageHandler<Message, Context> {
: undefined;
const redactedEvent = redactor
? {
...event,
Records: event.Records.map((record) => ({
...record,
body: redactor(record.body),
})),
}
...event,
Records: event.Records.map((record) => ({
...record,
body: redactor(record.body),
})),
}
: event;
context.logger.info(
{ event: redactedEvent },
Expand Down Expand Up @@ -245,13 +260,13 @@ export class SQSMessageHandler<Message, Context> {
const event: SQSEvent = {
Records: messages.map(
(msg) =>
// We don't need to mock every field on this event -- there are lots.
// eslint-disable-next-line @typescript-eslint/no-unsafe-argument
({
attributes: {},
messageId: uuid(),
body: stringifyMessage(msg),
} as any),
// We don't need to mock every field on this event -- there are lots.
// eslint-disable-next-line @typescript-eslint/no-unsafe-argument
({
attributes: {},
messageId: uuid(),
body: stringifyMessage(msg),
} as any),
),
};

Expand Down

0 comments on commit d812412

Please sign in to comment.