feat(api): add CORS headers (#50120)

* test: allow mocking of env vars

Since utils/env is a module, we can mock it to control env vars in
tests. However, it's not compatible with building the server in
setupFilesAfterEnv, so, instead, we can use a utility function to keep
things DRY.

* fix: update type of fastifyTestInstance

* chore: add comment about sts preload

* chore: rename header plugin

* test: add get util + provide origin on request

* feat: add cors headers

* chore: add TODO
This commit is contained in:
Oliver Eyton-Williams
2023-04-26 09:02:12 +02:00
committed by GitHub
parent 293fb65063
commit 46cdfd7802
8 changed files with 147 additions and 43 deletions

41
api/src/plugins/cors.ts Normal file
View File

@@ -0,0 +1,41 @@
import { FastifyPluginCallback } from 'fastify';
import fp from 'fastify-plugin';
import { HOME_LOCATION } from '../utils/env';
// import { FREECODECAMP_NODE_ENV } from '../utils/env';
const allowedOrigins = [
'https://www.freecodecamp.dev',
'https://www.freecodecamp.org',
'https://beta.freecodecamp.dev',
'https://beta.freecodecamp.org',
'https://chinese.freecodecamp.dev',
'https://chinese.freecodecamp.org'
];
const cors: FastifyPluginCallback = (fastify, _options, done) => {
fastify.addHook('onRequest', async (req, reply) => {
const origin = req.headers.origin;
if (origin && allowedOrigins.includes(origin)) {
void reply.header('Access-Control-Allow-Origin', origin);
} else {
// TODO: Discuss if this is the correct approach. Standard practice is to
// reflect one of a list of allowed origins and handle development
// separately. If we switch to that approach we can replace use
// @fastify/cors instead.
void reply.header('Access-Control-Allow-Origin', HOME_LOCATION);
}
void reply
.header(
'Access-Control-Allow-Headers',
'Origin, X-Requested-With, Content-Type, Accept'
)
.header('Access-Control-Allow-Credentials', true);
});
done();
};
export default fp(cors);

View File

@@ -4,7 +4,7 @@ import fp from 'fastify-plugin';
import { FREECODECAMP_NODE_ENV } from '../utils/env';
const fastifySentry: FastifyPluginCallback = (fastify, _options, done) => {
const securityHeaders: FastifyPluginCallback = (fastify, _options, done) => {
// OWASP recommended headers
fastify.addHook('onRequest', async (_request, reply) => {
void reply
@@ -13,7 +13,8 @@ const fastifySentry: FastifyPluginCallback = (fastify, _options, done) => {
.header('Content-Type', 'application/json; charset=utf-8')
.header('X-Content-Type-Options', 'nosniff')
.header('X-Frame-Options', 'DENY');
// TODO: Increase this gradually to 2 years.
// TODO: Increase this gradually to 2 years. Include preload once it is
// at least 1 year.
if (FREECODECAMP_NODE_ENV === 'production') {
void reply.header(
'Strict-Transport-Security',
@@ -25,4 +26,4 @@ const fastifySentry: FastifyPluginCallback = (fastify, _options, done) => {
done();
};
export default fp(fastifySentry);
export default fp(securityHeaders);