const security = require('./index') const express = require('express') const superagent = require("superagent") let app let server const mockReq = { originalUrl: '/', _setUrl: function (url) { this.originalUrl = url }, method: 'GET', _setMethod: function(method) { this.method = method }, app: { _router: { stack: [{ route: { path: '/' } }] } } } const mockRes = { _headers: { 'X-Powered-By': 'my-server' }, set: function(header, value) { this._headers[header] = value }, removeHeader: function(header) { delete this._headers[header] }, _status: 200, status: function(status) { this._status = status return this }, end: function() { return undefined } } describe('Unit Tests', () => { beforeEach(() => { mockRes._headers = { 'X-Powered-By': 'my-server'} mockRes._status = 200 mockReq.originalUrl = '/' mockReq.method = 'GET' }) headerUnitTest('Cache-Control', 'CacheControl', 'no-cache, no-store, must-revalidate') headerUnitTest('Pragma', 'Pragma', 'no-cache') headerUnitTest('Expires', 'Expires', '0') headerUnitTest('Content-Security-Policy', 'ContentSecurityPolicy', 'default-src \'self\'; frame-ancestors \'none\'') headerUnitTest('X-XSS-Protection', 'XXSSProtection', '1; mode=block') headerUnitTest('X-DNS-Prefetch-Control', 'XDNSPrefetchControl', 'off') headerUnitTest('Expect-CT', 'ExpectCT', 'enforce; max-age=30; report-uri="/_report"') headerUnitTest('X-Frame-Options', 'XFrameOptions', 'deny') describe('Header: X-Powered-By', () => { it('should remove Header if not defined', (done) => { let sec = security() sec.setHeaders(mockReq, mockRes, () => { expect(mockRes._headers['X-Powered-By']).toBeUndefined() done() }) }) it('should not remove Header if set to false', (done) => { let options = {} options.XPoweredBy = false let sec = security(options) sec.setHeaders(mockReq, mockRes, () => { expect(mockRes._headers['X-Powered-By']).toBeDefined() done() }) }) }) headerUnitTest('Strict-Transport-Security', 'StrictTransportSecurity', 'max-age=30') headerUnitTest('X-Download-Options', 'XDownloadOptions', 'noopen') headerUnitTest('X-Content-Type-Options', 'XContentTypeOptions', 'nosniff') headerUnitTest('X-Permitted-Cross-Domain-Policies', 'XPermittedCrossDomainPolicies', 'none') headerUnitTest('Referrer-Policy', 'ReferrerPolicy', 'no-referrer') describe('Allowed Methods', () => { it('should only allow GET, POST, PUT, DELETE on default', (done) => { let sec = security() mockReq._setMethod('GET') sec.setHeaders(mockReq, mockRes, () => { expect(mockRes._status).toBe(200) mockReq._setMethod('HEAD') sec.setHeaders(mockReq, mockRes, () => { expect(mockRes._status).toBe(405) done() }) }) }) it('should allow given Methods', (done) => { let sec = security({ allowedMethods: ['POST'] }) mockReq._setMethod('POST') sec.setHeaders(mockReq, mockRes, () => { expect(mockRes._status).toBe(200) mockReq._setMethod('GET') sec.setHeaders(mockReq, mockRes, () => { expect(mockRes._status).toBe(405) done() }) }) }) }) describe('Defined Routes', () => { it('should allow all routes by default', (done) => { let sec = security() mockReq._setUrl('/') sec.setHeaders(mockReq, mockRes, () => { expect(mockRes._status).toBe(200) mockReq._setUrl('/test') sec.setHeaders(mockReq, mockRes, () => { expect(mockRes._status).toBe(200) done() }) }) }) it('should only allow defined routes if set to true', (done) => { let sec = security({ onlyDefinedRoutes: true, definedRoutes: ['/'] }) mockReq._setUrl('/') sec.setHeaders(mockReq, mockRes, () => { expect(mockRes._status).toBe(200) mockReq._setUrl('/test') sec.setHeaders(mockReq, mockRes, () => { expect(mockRes._status).toBe(405) done() }) }) }) }) }) describe('Integration Tests', () => { afterEach(() => { server.close() }) headerIntegrationTest('Cache-Control', 'CacheControl', 'no-cache, no-store, must-revalidate') headerIntegrationTest('Pragma', 'Pragma', 'no-cache') headerIntegrationTest('Expires', 'Expires', '0') headerIntegrationTest('Content-Security-Policy', 'ContentSecurityPolicy', 'default-src \'self\'; frame-ancestors \'none\'') headerIntegrationTest('X-XSS-Protection', 'XXSSProtection', '1; mode=block') headerIntegrationTest('X-DNS-Prefetch-Control', 'XDNSPrefetchControl', 'off') headerIntegrationTest('Expect-CT', 'ExpectCT', 'enforce; max-age=30; report-uri="/_report"') headerIntegrationTest('X-Frame-Options', 'XFrameOptions', 'deny') describe('Header: X-Powered-By', () => { it('should remove Header if not defined', (done) => { startUpServer({}) superagent .get('http://127.0.0.1:7777') .then(res => { expect(res.status).toBe(200) expect(res.headers['x-powered-by']).toBeUndefined() done() }) }) it('should not remove Header if set to false', (done) => { startUpServer({ XPoweredBy: false }) superagent .get('http://127.0.0.1:7777') .then(res => { expect(res.status).toBe(200) expect(res.headers['x-powered-by']).toBeDefined() done() }) }) }) headerIntegrationTest('Strict-Transport-Security', 'StrictTransportSecurity', 'max-age=30') headerIntegrationTest('X-Download-Options', 'XDownloadOptions', 'noopen') headerIntegrationTest('X-Content-Type-Options', 'XContentTypeOptions', 'nosniff') headerIntegrationTest('X-Permitted-Cross-Domain-Policies', 'XPermittedCrossDomainPolicies', 'none') headerIntegrationTest('Referrer-Policy', 'ReferrerPolicy', 'no-referrer') describe('Allowed Methods', () => { it('should only allow GET, POST, PUT, DELETE on default', (done) => { startUpServer({}) superagent .get('http://127.0.0.1:7777') .then(res => { expect(res.status).toBe(200) superagent .head('http://127.0.0.1:7777') .then(res2 => {}) .catch((error) => { expect(error.status).toBe(405) done() }) }) }) it('should allow given Methods', async () => { startUpServer({ allowedMethods: ['POST'] }) const res = await superagent.post('http://127.0.0.1:7777').send({}); expect(res.status).toBe(200) try { const res2 = await superagent.get('http://127.0.0.1:7777'); } catch (error) { expect(error.status).toBe(405) } }) }) describe('Defined Routes', () => { it('should allow all routes by default', (done) => { startUpServer({}) superagent .get('http://127.0.0.1:7777') .then(res => { expect(res.status).toBe(200) superagent .get('http://127.0.0.1:7777/test') .then(res2 => { expect(res2.status).toBe(200) done() }) }) }) it('should only allow defined routes if set to true', (done) => { startUpServer({ onlyDefinedRoutes: true, definedRoutes: ['/'] }) superagent .get('http://127.0.0.1:7777') .then(res => { expect(res.status).toBe(200) superagent .get('http://127.0.0.1:7777/test') .then(res2 => {}) .catch((error) => { expect(error.status).toBe(405) done() }) }) }) it('should not allow any routes if set to true but no routes given', (done) => { startUpServer({ onlyDefinedRoutes: true }) superagent .get('http://127.0.0.1:7777') .then(res => { }) .catch((error) => { expect(error.status).toBe(405) done() }) }) it('should allow regex route if set', (done) => { startUpServer({ onlyDefinedRoutes: true, definedRoutes: ['/', 'REGEX:\\/test\\/\\d{1,}'] }) superagent .get('http://127.0.0.1:7777') .then(res => { expect(res.status).toBe(200) superagent .get('http://127.0.0.1:7777/test') .then(res2 => {}) .catch((error) => { expect(error.status).toBe(405) superagent .get('http://127.0.0.1:7777/test/123') .then(res3 => { expect(res3.status).toBe(200) done() }) }) }) }) }) }) function headerUnitTest (header, headerOption, defaultValue) { describe('Header: ' + header, () => { it('should set "' + defaultValue + '" if not defined', (done) => { let sec = security() sec.setHeaders(mockReq, mockRes, () => { expect(mockRes._headers[header]).toBe(defaultValue) done() }) }) it('should not set Header if set to false', (done) => { let options = {} options[headerOption] = false let sec = security(options) sec.setHeaders(mockReq, mockRes, () => { expect(mockRes._headers[header]).toBeUndefined() done() }) }) it('should set given values', (done) => { let options = {} options[headerOption] = 'somevalue' let sec = security(options) sec.setHeaders(mockReq, mockRes, () => { expect(mockRes._headers[header]).toBe('somevalue') done() }) }) }) } function headerIntegrationTest (header, headerOption, defaultValue) { describe('Header: ' + header, () => { it('should set "' + defaultValue + '" if not defined', (done) => { startUpServer({}) superagent .get('http://127.0.0.1:7777') .then(res => { expect(res.status).toBe(200) expect(res.headers[header.toLowerCase()]).toBe(defaultValue) done() }) }) it('should not set Header if set to false', (done) => { let options = {} options[headerOption] = false startUpServer(options) superagent .get('http://127.0.0.1:7777') .then(res => { expect(res.status).toBe(200) expect(res.headers[header.toLowerCase()]).toBeUndefined() done() }) }) it('should set given values', (done) => { let options = {} options[headerOption] = 'somevalue' startUpServer(options) superagent .get('http://127.0.0.1:7777') .then(res => { expect(res.status).toBe(200) expect(res.headers[header.toLowerCase()]).toBe('somevalue') done() }) }) }) } function startUpServer(options) { app = express() let sec = security(options) app.use(sec.setHeaders) app.get('/', function (req, res) { res.send('Hello World!') }) app.post('/', function (req, res) { res.send('Some post') }) app.get('/test', function (req, res) { res.send('Hello Test!') }) app.get('/test/123', function (req, res) { res.send('Hello 123!') }) server = app.listen(7777) }