fix(security): cache-control on all pages using setupPageRoute or setupApiRoute, and 404 controllers.

This commit also reverts e39cdd490b
isekai-main
Julian Lam 3 years ago
parent e39cdd490b
commit 1f6f389ff2

@ -55,6 +55,7 @@ exports.send404 = async function (req, res) {
}); });
} }
await middleware.inhibitCacheAsync(req, res);
await middleware.buildHeaderAsync(req, res); await middleware.buildHeaderAsync(req, res);
await res.render('404', { await res.render('404', {
path: validator.escape(path), path: validator.escape(path),

@ -3,6 +3,7 @@
const os = require('os'); const os = require('os');
const winston = require('winston'); const winston = require('winston');
const _ = require('lodash'); const _ = require('lodash');
const util = require('util');
const meta = require('../meta'); const meta = require('../meta');
const languages = require('../languages'); const languages = require('../languages');
@ -108,4 +109,13 @@ module.exports = function (middleware) {
return [defaultLang]; return [defaultLang];
} }
} }
middleware.inhibitCache = (req, res, next) => {
if (req.loggedIn) {
res.set('cache-control', 'private');
}
next();
};
middleware.inhibitCacheAsync = util.promisify(middleware.inhibitCache);
}; };

@ -18,6 +18,7 @@ function _handleArgs(middleware, middlewares, controller) {
middleware.authenticateRequest, middleware.authenticateRequest,
middleware.maintenanceMode, middleware.maintenanceMode,
middleware.registrationComplete, middleware.registrationComplete,
middleware.inhibitCache,
middleware.pluginHooks, middleware.pluginHooks,
...middlewares, ...middlewares,
]; ];

@ -1,20 +1,25 @@
'use strict'; 'use strict';
const assert = require('assert'); const assert = require('assert');
const nconf = require('nconf');
const request = require('request-promise-native');
const db = require('./mocks/databasemock'); const db = require('./mocks/databasemock');
const middleware = require('../src/middleware'); const middleware = require('../src/middleware');
const user = require('../src/user'); const user = require('../src/user');
const groups = require('../src/groups'); const groups = require('../src/groups');
const utils = require('../src/utils');
const helpers = require('./helpers');
describe('Middlewares', () => { describe('Middlewares', () => {
let adminUid; describe('expose', () => {
let adminUid;
before(async () => { before(async () => {
adminUid = await user.create({ username: 'admin', password: '123456' }); adminUid = await user.create({ username: 'admin', password: '123456' });
await groups.join('administrators', adminUid); await groups.join('administrators', adminUid);
}); });
describe('expose', () => {
it('should expose res.locals.isAdmin = false', (done) => { it('should expose res.locals.isAdmin = false', (done) => {
const resMock = { locals: {} }; const resMock = { locals: {} };
middleware.exposeAdmin({}, resMock, () => { middleware.exposeAdmin({}, resMock, () => {
@ -94,5 +99,93 @@ describe('Middlewares', () => {
}); });
}); });
}); });
describe('.inhibitCache (cache-control header)', () => {
let uid;
let jar;
before(async () => {
uid = await user.create({ username: 'testuser', password: '123456' });
({ jar } = await helpers.loginUser('testuser', '123456'));
});
it('should be absent on non-existent routes, for guests', async () => {
const res = await request(`${nconf.get('url')}/${utils.generateUUID()}`, {
simple: false,
resolveWithFullResponse: true,
});
assert.strictEqual(res.statusCode, 404);
assert(!Object.keys(res.headers).includes('cache-control'));
});
it('should be set to "private" on non-existent routes, for logged in users', async () => {
const res = await request(`${nconf.get('url')}/${utils.generateUUID()}`, {
simple: false,
resolveWithFullResponse: true,
jar,
});
assert.strictEqual(res.statusCode, 404);
assert(Object.keys(res.headers).includes('cache-control'));
assert.strictEqual(res.headers['cache-control'], 'private');
});
it('should be absent on regular routes, for guests', async () => {
const res = await request(nconf.get('url'), {
simple: false,
resolveWithFullResponse: true,
});
assert.strictEqual(res.statusCode, 200);
assert(!Object.keys(res.headers).includes('cache-control'));
});
it('should be absent on api routes, for guests', async () => {
const res = await request(`${nconf.get('url')}/api`, {
simple: false,
resolveWithFullResponse: true,
});
assert.strictEqual(res.statusCode, 200);
assert(!Object.keys(res.headers).includes('cache-control'));
});
it('should be set to "private" on regular routes, for logged-in users', async () => {
const res = await request(nconf.get('url'), {
simple: false,
resolveWithFullResponse: true,
jar,
});
assert.strictEqual(res.statusCode, 200);
assert(Object.keys(res.headers).includes('cache-control'));
assert.strictEqual(res.headers['cache-control'], 'private');
});
it('should be set to "private" on api routes, for logged-in users', async () => {
const res = await request(`${nconf.get('url')}/api`, {
simple: false,
resolveWithFullResponse: true,
jar,
});
assert.strictEqual(res.statusCode, 200);
assert(Object.keys(res.headers).includes('cache-control'));
assert.strictEqual(res.headers['cache-control'], 'private');
});
it('should be set to "private" on apiv3 routes, for logged-in users', async () => {
const res = await request(`${nconf.get('url')}/api/v3/users/${uid}`, {
simple: false,
resolveWithFullResponse: true,
jar,
});
assert.strictEqual(res.statusCode, 200);
assert(Object.keys(res.headers).includes('cache-control'));
assert.strictEqual(res.headers['cache-control'], 'private');
});
});
}); });

Loading…
Cancel
Save