fix(csrf): remove all csrf bypass
This commit is contained in:
committed by
mrugesh
parent
f183df0d88
commit
23b899f50f
@ -37,7 +37,7 @@ export default function bootCertificate(app) {
|
||||
api.put('/certificate/verify', ifNoUser401, ifNoSuperBlock404, verifyCert);
|
||||
api.get('/certificate/showCert/:username/:cert', showCert);
|
||||
|
||||
app.use('/internal', api);
|
||||
app.use(api);
|
||||
}
|
||||
|
||||
const noNameMessage = dedent`
|
||||
|
@ -61,7 +61,6 @@ export default async function bootChallenge(app, done) {
|
||||
router.get('/map', redirectToLearn);
|
||||
|
||||
app.use(api);
|
||||
app.use('/internal', api);
|
||||
app.use(router);
|
||||
done();
|
||||
}
|
||||
|
@ -319,7 +319,6 @@ export default function donateBoot(app, done) {
|
||||
api.post('/update-paypal', updatePaypal);
|
||||
donateRouter.use('/donate', api);
|
||||
app.use(donateRouter);
|
||||
app.use('/internal', donateRouter);
|
||||
connectToStripe().then(done);
|
||||
}
|
||||
}
|
||||
|
@ -9,7 +9,6 @@ const githubSecret = process.env.GITHUB_SECRET;
|
||||
|
||||
module.exports = function(app) {
|
||||
const router = app.loopback.Router();
|
||||
const api = app.loopback.Router();
|
||||
const User = app.models.User;
|
||||
|
||||
router.get('/api/github', githubCalls);
|
||||
@ -22,14 +21,12 @@ module.exports = function(app) {
|
||||
);
|
||||
router.get('/unsubscribed/:unsubscribeId', unsubscribedWithId);
|
||||
router.get('/unsubscribed', unsubscribed);
|
||||
api.get('/resubscribe/:unsubscribeId', resubscribe);
|
||||
router.get('/resubscribe/:unsubscribeId', resubscribe);
|
||||
router.get('/nonprofits', nonprofits);
|
||||
router.get('/coding-bootcamp-cost-calculator', bootcampCalculator);
|
||||
|
||||
app.use(router);
|
||||
|
||||
app.use('/internal', api);
|
||||
|
||||
function theFastestWebPageOnTheInternet(req, res) {
|
||||
res.render('resources/the-fastest-web-page-on-the-internet', {
|
||||
title: 'This is the fastest web page on the internet'
|
||||
|
@ -2,5 +2,4 @@ module.exports = function mountRestApi(app) {
|
||||
const restApi = app.loopback.rest();
|
||||
const restApiRoot = app.get('restApiRoot');
|
||||
app.use(restApiRoot, restApi);
|
||||
app.use(`/internal${restApiRoot}`, restApi);
|
||||
};
|
||||
|
@ -48,7 +48,6 @@ export default function settingsController(app) {
|
||||
api.put('/update-my-username', ifNoUser401, updateMyUsername);
|
||||
api.put('/update-user-flag', ifNoUser401, updateUserFlag);
|
||||
|
||||
app.use('/internal', api);
|
||||
app.use(api);
|
||||
}
|
||||
|
||||
|
@ -31,7 +31,7 @@ function bootUser(app) {
|
||||
api.post('/account/reset-progress', ifNoUser401, postResetProgress);
|
||||
api.post('/user/report-user/', ifNoUser401, postReportUserProfile);
|
||||
|
||||
app.use('/internal', api);
|
||||
app.use(api);
|
||||
}
|
||||
|
||||
function createReadSessionUser(app) {
|
||||
|
@ -6,10 +6,9 @@ export default function() {
|
||||
domain: process.env.COOKIE_DOMAIN || 'localhost'
|
||||
}
|
||||
});
|
||||
// Note: paypal webhook goes through /internal
|
||||
return function csrf(req, res, next) {
|
||||
const path = req.path.split('/')[1];
|
||||
if (/(^api$|^unauthenticated$|^internal$|^p$)/.test(path)) {
|
||||
if (/^donate\/update-paypal$/.test(path)) {
|
||||
return next();
|
||||
}
|
||||
return protection(req, res, next);
|
||||
|
@ -11,19 +11,23 @@ import { jwtSecret as _jwtSecret } from '../../../config/secrets';
|
||||
|
||||
import { wrapHandledError } from '../utils/create-handled-error';
|
||||
|
||||
// We need to tunnel through a proxy path set up within
|
||||
// the gatsby app, at this time, that path is /internal
|
||||
const apiProxyRE = /^\/internal\/|^\/external\//;
|
||||
const newsShortLinksRE = /^\/internal\/n\/|^\/internal\/p\?/;
|
||||
const loopbackAPIPathRE = /^\/internal\/api\//;
|
||||
const showCertRe = /^\/internal\/certificate\/showCert\//;
|
||||
const updatePaypalRe = /^\/internal\/donate\/update-paypal/;
|
||||
const newsShortLinksRE = /^\/n\/|^\/p\//;
|
||||
const showCertRE = /^\/certificate\/showCert\//;
|
||||
const updatePaypalRE = /^\/donate\/update-paypal/;
|
||||
// signin may not have a trailing slash
|
||||
const signinRE = /^\/signin/;
|
||||
const unsubscribeRE = /^\/u\/|^\/unsubscribe\/|^\/ue\//;
|
||||
const unsubscribedRE = /^\/unsubscribed\//;
|
||||
const resubscribeRE = /^\/resubscribe\//;
|
||||
|
||||
const _whiteListREs = [
|
||||
newsShortLinksRE,
|
||||
loopbackAPIPathRE,
|
||||
showCertRe,
|
||||
updatePaypalRe
|
||||
showCertRE,
|
||||
updatePaypalRE,
|
||||
signinRE,
|
||||
unsubscribeRE,
|
||||
unsubscribedRE,
|
||||
resubscribeRE
|
||||
];
|
||||
|
||||
export function isWhiteListedPath(path, whiteListREs = _whiteListREs) {
|
||||
@ -33,7 +37,7 @@ export function isWhiteListedPath(path, whiteListREs = _whiteListREs) {
|
||||
export default ({ jwtSecret = _jwtSecret, getUserById = _getUserById } = {}) =>
|
||||
function requestAuthorisation(req, res, next) {
|
||||
const { path } = req;
|
||||
if (apiProxyRE.test(path) && !isWhiteListedPath(path)) {
|
||||
if (!isWhiteListedPath(path)) {
|
||||
const { accessToken, error, jwt } = getAccessTokenFromRequest(
|
||||
req,
|
||||
jwtSecret
|
||||
|
@ -64,7 +64,7 @@ describe('request-authorization', () => {
|
||||
describe('cookies', () => {
|
||||
it('throws when no access token is present', () => {
|
||||
expect.assertions(2);
|
||||
const req = mockReq({ path: '/internal/some-path/that-needs/auth' });
|
||||
const req = mockReq({ path: '/some-path/that-needs/auth' });
|
||||
const res = mockRes();
|
||||
const next = sinon.spy();
|
||||
expect(() => requestAuthorization(req, res, next)).toThrowError(
|
||||
@ -77,7 +77,7 @@ describe('request-authorization', () => {
|
||||
expect.assertions(2);
|
||||
const invalidJWT = jwt.sign({ accessToken }, invalidJWTSecret);
|
||||
const req = mockReq({
|
||||
path: '/internal/some-path/that-needs/auth',
|
||||
path: '/some-path/that-needs/auth',
|
||||
// eslint-disable-next-line camelcase
|
||||
cookie: { jwt_access_token: invalidJWT }
|
||||
});
|
||||
@ -97,7 +97,7 @@ describe('request-authorization', () => {
|
||||
validJWTSecret
|
||||
);
|
||||
const req = mockReq({
|
||||
path: '/internal/some-path/that-needs/auth',
|
||||
path: '/some-path/that-needs/auth',
|
||||
// eslint-disable-next-line camelcase
|
||||
cookie: { jwt_access_token: invalidJWT }
|
||||
});
|
||||
@ -114,7 +114,7 @@ describe('request-authorization', () => {
|
||||
expect.assertions(3);
|
||||
const validJWT = jwt.sign({ accessToken }, validJWTSecret);
|
||||
const req = mockReq({
|
||||
path: '/internal/some-path/that-needs/auth',
|
||||
path: '/some-path/that-needs/auth',
|
||||
// eslint-disable-next-line camelcase
|
||||
cookie: { jwt_access_token: validJWT }
|
||||
});
|
||||
@ -130,7 +130,7 @@ describe('request-authorization', () => {
|
||||
it('adds the jwt to the headers', async done => {
|
||||
const validJWT = jwt.sign({ accessToken }, validJWTSecret);
|
||||
const req = mockReq({
|
||||
path: '/internal/some-path/that-needs/auth',
|
||||
path: '/some-path/that-needs/auth',
|
||||
// eslint-disable-next-line camelcase
|
||||
cookie: { jwt_access_token: validJWT }
|
||||
});
|
||||
@ -142,7 +142,8 @@ describe('request-authorization', () => {
|
||||
});
|
||||
|
||||
it('calls next if request does not require authorization', async () => {
|
||||
const req = mockReq({ path: '/unauthenticated/another/route' });
|
||||
// currently /unsubscribe does not require authorization
|
||||
const req = mockReq({ path: '/unsubscribe/another/route' });
|
||||
const res = mockRes();
|
||||
const next = sinon.spy();
|
||||
await requestAuthorization(req, res, next);
|
||||
@ -153,7 +154,7 @@ describe('request-authorization', () => {
|
||||
describe('Auth header', () => {
|
||||
it('throws when no access token is present', () => {
|
||||
expect.assertions(2);
|
||||
const req = mockReq({ path: '/internal/some-path/that-needs/auth' });
|
||||
const req = mockReq({ path: '/some-path/that-needs/auth' });
|
||||
const res = mockRes();
|
||||
const next = sinon.spy();
|
||||
expect(() => requestAuthorization(req, res, next)).toThrowError(
|
||||
@ -166,7 +167,7 @@ describe('request-authorization', () => {
|
||||
expect.assertions(2);
|
||||
const invalidJWT = jwt.sign({ accessToken }, invalidJWTSecret);
|
||||
const req = mockReq({
|
||||
path: '/internal/some-path/that-needs/auth',
|
||||
path: '/some-path/that-needs/auth',
|
||||
headers: { 'X-fcc-access-token': invalidJWT }
|
||||
});
|
||||
const res = mockRes();
|
||||
@ -185,7 +186,7 @@ describe('request-authorization', () => {
|
||||
validJWTSecret
|
||||
);
|
||||
const req = mockReq({
|
||||
path: '/internal/some-path/that-needs/auth',
|
||||
path: '/some-path/that-needs/auth',
|
||||
headers: { 'X-fcc-access-token': invalidJWT }
|
||||
});
|
||||
const res = mockRes();
|
||||
@ -201,7 +202,7 @@ describe('request-authorization', () => {
|
||||
expect.assertions(3);
|
||||
const validJWT = jwt.sign({ accessToken }, validJWTSecret);
|
||||
const req = mockReq({
|
||||
path: '/internal/some-path/that-needs/auth',
|
||||
path: '/some-path/that-needs/auth',
|
||||
headers: { 'X-fcc-access-token': validJWT }
|
||||
});
|
||||
const res = mockRes();
|
||||
@ -216,7 +217,7 @@ describe('request-authorization', () => {
|
||||
it('adds the jwt to the headers', async done => {
|
||||
const validJWT = jwt.sign({ accessToken }, validJWTSecret);
|
||||
const req = mockReq({
|
||||
path: '/internal/some-path/that-needs/auth',
|
||||
path: '/some-path/that-needs/auth',
|
||||
// eslint-disable-next-line camelcase
|
||||
cookie: { jwt_access_token: validJWT }
|
||||
});
|
||||
@ -228,7 +229,8 @@ describe('request-authorization', () => {
|
||||
});
|
||||
|
||||
it('calls next if request does not require authorization', async () => {
|
||||
const req = mockReq({ path: '/unauthenticated/another/route' });
|
||||
// currently /unsubscribe does not require authorization
|
||||
const req = mockReq({ path: '/unsubscribe/another/route' });
|
||||
const res = mockRes();
|
||||
const next = sinon.spy();
|
||||
await requestAuthorization(req, res, next);
|
||||
|
@ -1,9 +1,9 @@
|
||||
Here's your sign in link. It will instantly sign you into freeCodeCamp.org - no password necessary:
|
||||
|
||||
<%= host %>/internal/passwordless-auth/?email=<%= loginEmail %>&token=<%= loginToken %>
|
||||
<%= host %>/passwordless-auth/?email=<%= loginEmail %>&token=<%= loginToken %>
|
||||
|
||||
Note: this sign in link will expire after 15 minutes. If you need a new sign in link, go to https://www.freecodecamp.org/signin
|
||||
|
||||
See you soon!
|
||||
|
||||
- The freeCodeCamp.org Team
|
||||
- The freeCodeCamp.org Team
|
||||
|
@ -4,10 +4,10 @@ We have created a new account for you.
|
||||
|
||||
Here's your sign in link. It will instantly sign you into freeCodeCamp.org - no password necessary:
|
||||
|
||||
<%= host %>/internal/passwordless-auth/?email=<%= loginEmail %>&token=<%= loginToken %>
|
||||
<%= host %>/passwordless-auth/?email=<%= loginEmail %>&token=<%= loginToken %>
|
||||
|
||||
Note: this sign in link will expire after 15 minutes. If you need a new sign in link, go to https://www.freecodecamp.org/signin
|
||||
|
||||
See you soon!
|
||||
|
||||
- The freeCodeCamp.org Team
|
||||
- The freeCodeCamp.org Team
|
||||
|
Reference in New Issue
Block a user