mirror of
https://github.com/freeCodeCamp/freeCodeCamp.git
synced 2026-02-19 13:00:32 -05:00
107 lines
2.8 KiB
TypeScript
107 lines
2.8 KiB
TypeScript
import Fastify, { type FastifyInstance } from 'fastify';
|
|
|
|
import { COOKIE_DOMAIN } from '../utils/env';
|
|
import cookies from './cookies';
|
|
import csrf, { CSRF_COOKIE, CSRF_SECRET_COOKIE } from './csrf';
|
|
|
|
// eslint-disable-next-line @typescript-eslint/no-unsafe-return
|
|
jest.mock('../utils/env', () => ({
|
|
...jest.requireActual('../utils/env'),
|
|
COOKIE_DOMAIN: 'www.example.com',
|
|
FREECODECAMP_NODE_ENV: 'production'
|
|
}));
|
|
|
|
async function setupServer() {
|
|
const fastify = Fastify({ logger: true, disableRequestLogging: true });
|
|
await fastify.register(cookies);
|
|
await fastify.register(csrf);
|
|
// eslint-disable-next-line @typescript-eslint/unbound-method
|
|
fastify.addHook('onRequest', fastify.csrfProtection);
|
|
|
|
fastify.get('/', (_req, reply) => {
|
|
void reply.send({ foo: 'bar' });
|
|
});
|
|
return fastify;
|
|
}
|
|
|
|
describe('CSRF protection', () => {
|
|
let fastify: FastifyInstance;
|
|
beforeEach(async () => {
|
|
fastify = await setupServer();
|
|
});
|
|
it('should receive a new CSRF token with the expected properties', async () => {
|
|
const response = await fastify.inject({
|
|
method: 'GET',
|
|
url: '/'
|
|
});
|
|
const newCookies = response.cookies;
|
|
const csrfTokenCookie = newCookies.find(
|
|
cookie => cookie.name === CSRF_COOKIE
|
|
);
|
|
|
|
const { value, ...rest } = csrfTokenCookie!;
|
|
|
|
// The value is a random string - it's enough to check that it's not empty
|
|
expect(value).toHaveLength(52);
|
|
|
|
expect(rest).toStrictEqual({
|
|
name: CSRF_COOKIE,
|
|
path: '/',
|
|
sameSite: 'Strict',
|
|
domain: COOKIE_DOMAIN,
|
|
secure: true
|
|
});
|
|
});
|
|
|
|
it('should return 403 if the _csrf secret is missing', async () => {
|
|
const response = await fastify.inject({
|
|
method: 'GET',
|
|
url: '/'
|
|
});
|
|
|
|
expect(response.statusCode).toEqual(403);
|
|
// The response body is determined by the error-handling plugin, so we don't
|
|
// check it here.
|
|
});
|
|
|
|
it('should return 403 if the csrf_token is invalid', async () => {
|
|
const response = await fastify.inject({
|
|
method: 'GET',
|
|
url: '/',
|
|
cookies: {
|
|
_csrf: 'foo',
|
|
csrf_token: 'bar'
|
|
}
|
|
});
|
|
expect(response.statusCode).toEqual(403);
|
|
});
|
|
|
|
it('should allow the request if the csrf_token is valid', async () => {
|
|
const csrfResponse = await fastify.inject({
|
|
method: 'GET',
|
|
url: '/'
|
|
});
|
|
|
|
const csrfTokenCookie = csrfResponse.cookies.find(
|
|
cookie => cookie.name === CSRF_COOKIE
|
|
);
|
|
const csrfSecretCookie = csrfResponse.cookies.find(
|
|
cookie => cookie.name === CSRF_SECRET_COOKIE
|
|
);
|
|
|
|
const res = await fastify.inject({
|
|
method: 'GET',
|
|
url: '/',
|
|
cookies: {
|
|
_csrf: csrfSecretCookie!.value
|
|
},
|
|
headers: {
|
|
'csrf-token': csrfTokenCookie!.value
|
|
}
|
|
});
|
|
|
|
expect(res.json()).toEqual({ foo: 'bar' });
|
|
expect(res.statusCode).toEqual(200);
|
|
});
|
|
});
|