mirror of
https://github.com/roleypoly/roleypoly.git
synced 2025-04-24 19:39:11 +00:00
123 lines
3.6 KiB
TypeScript
123 lines
3.6 KiB
TypeScript
import { InteractionRequest, InteractionType } from '@roleypoly/types';
|
|
import nacl from 'tweetnacl';
|
|
import { configContext } from '../../utils/testHelpers';
|
|
import { verifyRequest } from './helpers';
|
|
|
|
describe('verifyRequest', () => {
|
|
it('validates a successful Discord interactions request', () => {
|
|
const [config, context] = configContext();
|
|
|
|
const timestamp = String(Date.now());
|
|
const body: InteractionRequest = {
|
|
id: '123',
|
|
type: InteractionType.APPLICATION_COMMAND,
|
|
application_id: '123',
|
|
token: '123',
|
|
version: 1,
|
|
};
|
|
|
|
const { publicKey, secretKey } = nacl.sign.keyPair();
|
|
const signature = nacl.sign.detached(
|
|
Buffer.from(timestamp + JSON.stringify(body)),
|
|
secretKey
|
|
);
|
|
config.publicKey = Buffer.from(publicKey).toString('hex');
|
|
|
|
const request = new Request('http://local.test', {
|
|
method: 'POST',
|
|
body: JSON.stringify(body),
|
|
headers: {
|
|
'x-signature-timestamp': timestamp,
|
|
'x-signature-ed25519': Buffer.from(signature).toString('hex'),
|
|
},
|
|
});
|
|
|
|
expect(verifyRequest(context.config, request, body)).toBe(true);
|
|
});
|
|
|
|
it('fails to validate a headerless Discord interactions request', () => {
|
|
const [config, context] = configContext();
|
|
|
|
const body: InteractionRequest = {
|
|
id: '123',
|
|
type: InteractionType.APPLICATION_COMMAND,
|
|
application_id: '123',
|
|
token: '123',
|
|
version: 1,
|
|
};
|
|
|
|
const { publicKey, secretKey } = nacl.sign.keyPair();
|
|
config.publicKey = Buffer.from(publicKey).toString('hex');
|
|
|
|
const request = new Request('http://local.test', {
|
|
method: 'POST',
|
|
body: JSON.stringify(body),
|
|
headers: {},
|
|
});
|
|
|
|
expect(verifyRequest(context.config, request, body)).toBe(false);
|
|
});
|
|
|
|
it('fails to validate a bad signature from Discord', () => {
|
|
const [config, context] = configContext();
|
|
|
|
const timestamp = String(Date.now());
|
|
const body: InteractionRequest = {
|
|
id: '123',
|
|
type: InteractionType.APPLICATION_COMMAND,
|
|
application_id: '123',
|
|
token: '123',
|
|
version: 1,
|
|
};
|
|
|
|
const { publicKey } = nacl.sign.keyPair();
|
|
const { secretKey: otherKey } = nacl.sign.keyPair();
|
|
const signature = nacl.sign.detached(
|
|
Buffer.from(timestamp + JSON.stringify(body)),
|
|
otherKey
|
|
);
|
|
config.publicKey = Buffer.from(publicKey).toString('hex');
|
|
|
|
const request = new Request('http://local.test', {
|
|
method: 'POST',
|
|
body: JSON.stringify(body),
|
|
headers: {
|
|
'x-signature-timestamp': timestamp,
|
|
'x-signature-ed25519': Buffer.from(signature).toString('hex'),
|
|
},
|
|
});
|
|
|
|
expect(verifyRequest(context.config, request, body)).toBe(false);
|
|
});
|
|
|
|
it('fails to validate when signature differs from data', () => {
|
|
const [config, context] = configContext();
|
|
|
|
const timestamp = String(Date.now());
|
|
const body: InteractionRequest = {
|
|
id: '123',
|
|
type: InteractionType.APPLICATION_COMMAND,
|
|
application_id: '123',
|
|
token: '123',
|
|
version: 1,
|
|
};
|
|
|
|
const { publicKey, secretKey } = nacl.sign.keyPair();
|
|
const signature = nacl.sign.detached(
|
|
Buffer.from(timestamp + JSON.stringify({ ...body, id: '456' })),
|
|
secretKey
|
|
);
|
|
config.publicKey = Buffer.from(publicKey).toString('hex');
|
|
|
|
const request = new Request('http://local.test', {
|
|
method: 'POST',
|
|
body: JSON.stringify(body),
|
|
headers: {
|
|
'x-signature-timestamp': timestamp,
|
|
'x-signature-ed25519': Buffer.from(signature).toString('hex'),
|
|
},
|
|
});
|
|
|
|
expect(verifyRequest(context.config, request, body)).toBe(false);
|
|
});
|
|
});
|