Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,6 @@ export class AlchemyWebhookService implements OnModuleInit {
const signingKey = this.webhookCache.get(webhookId);
if (!signingKey) {
this.logger.warn(`Webhook Id ${webhookId} has no signing key`);
this.logger.warn(`Webhook cache: ${JSON.stringify(this.webhookCache)}`);
return false;
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
import { ForbiddenException } from '@nestjs/common';
import { Config, ConfigService } from 'src/config/config';
import { YapealWebhookService } from '../../services/yapeal-webhook.service';
import { YapealWebhookController } from '../yapeal-webhook.controller';

describe('YapealWebhookController', () => {
let controller: YapealWebhookController;
let yapealWebhookService: jest.Mocked<Partial<YapealWebhookService>>;

const setExpectedKey = (key: string | undefined): void => {
(Config.bank.yapeal as { webhookApiKey?: string }).webhookApiKey = key;
};

beforeAll(() => {
new ConfigService();
});

beforeEach(() => {
yapealWebhookService = { processWebhook: jest.fn() };
controller = new YapealWebhookController(yapealWebhookService as unknown as YapealWebhookService);
});

it('processes the webhook when the api key matches', async () => {
setExpectedKey('secret');

await expect(controller.handleYapealWebhook('secret', { foo: 'bar' })).resolves.toEqual({ received: true });
expect(yapealWebhookService.processWebhook).toHaveBeenCalledWith({ foo: 'bar' });
});

it('rejects a wrong api key', async () => {
setExpectedKey('secret');

await expect(controller.handleYapealWebhook('wrong', {})).rejects.toBeInstanceOf(ForbiddenException);
expect(yapealWebhookService.processWebhook).not.toHaveBeenCalled();
});

it('fails closed: rejects every request when no expected key is configured', async () => {
setExpectedKey(undefined);

await expect(controller.handleYapealWebhook('anything', {})).rejects.toBeInstanceOf(ForbiddenException);
await expect(controller.handleYapealWebhook(undefined, {})).rejects.toBeInstanceOf(ForbiddenException);
expect(yapealWebhookService.processWebhook).not.toHaveBeenCalled();
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -23,9 +23,10 @@ export class YapealWebhookController {

private validateApiKey(apiKey: string): void {
const expectedKey = Config.bank.yapeal.webhookApiKey;
if (!expectedKey) return;

if (!apiKey || apiKey !== expectedKey) {
// fail closed: a missing expected key must reject every request, not wave them all through —
// this endpoint marks bank payments as received, so any unauthenticated path is forgeable
if (!expectedKey || !apiKey || apiKey !== expectedKey) {
throw new ForbiddenException('Invalid API key');
}
}
Expand Down