diff --git a/app/middleware/securities.js b/app/middleware/securities.js index 17d02a7..23d7244 100644 --- a/app/middleware/securities.js +++ b/app/middleware/securities.js @@ -13,9 +13,9 @@ module.exports = (_, app) => { } // format csrf.cookieDomain - const orginalCookieDomain = options.csrf.cookieDomain; - if (orginalCookieDomain && typeof orginalCookieDomain !== 'function') { - options.csrf.cookieDomain = () => orginalCookieDomain; + const originalCookieDomain = options.csrf.cookieDomain; + if (originalCookieDomain && typeof originalCookieDomain !== 'function') { + options.csrf.cookieDomain = () => originalCookieDomain; } defaultMiddleware.forEach(middlewareName => { @@ -46,7 +46,10 @@ module.exports = (_, app) => { app.deprecate('[egg-security] Please use `config.security.xframe.ignore` instead, `config.security.xframe.blackUrls` will be removed very soon'); opt.ignore = opt.blackUrls; } - opt.matching = createMatch(opt); + opt.matching = createMatch({ + ...opt, + pathToRegexpModule: app.options.pathToRegexpModule, + }); const fn = require(path.join(__dirname, '../../lib/middlewares', middlewareName))(opt, app); middlewares.push(fn); diff --git a/package.json b/package.json index eeb9784..2856064 100644 --- a/package.json +++ b/package.json @@ -21,7 +21,7 @@ "@eggjs/ip": "^2.0.2", "csrf": "^3.0.6", "delegates": "^1.0.0", - "egg-path-matching": "^1.0.0", + "egg-path-matching": "^1.2.0", "escape-html": "^1.0.3", "extend": "^3.0.1", "koa-compose": "^4.1.0", @@ -43,7 +43,8 @@ "eslint": "^8.40.0", "eslint-config-egg": "^12.2.1", "spy": "^1.0.0", - "supertest": "^6.3.3" + "supertest": "^6.3.3", + "path-to-regexp-v8": "npm:path-to-regexp@8" }, "scripts": { "lint": "eslint .", diff --git a/test/fixtures/apps/iframe-with-pathToRegexpModule/app/router.js b/test/fixtures/apps/iframe-with-pathToRegexpModule/app/router.js new file mode 100755 index 0000000..2023836 --- /dev/null +++ b/test/fixtures/apps/iframe-with-pathToRegexpModule/app/router.js @@ -0,0 +1,20 @@ +module.exports = function(app) { + app.get('/', controller); + app.get('/foo', controller); + app.get('/hello', controller); + app.get('/hello/other/world', controller); + app.get('/world/12', controller); + + app.get('/options', options, controller); + + async function controller() { + this.body = 'body'; + } + + async function options(ctx, next) { + ctx.securityOptions.xframe = { + value: 'ALLOW-FROM http://www.domain.com', + }; + return next(); + } +}; diff --git a/test/fixtures/apps/iframe-with-pathToRegexpModule/config/config.js b/test/fixtures/apps/iframe-with-pathToRegexpModule/config/config.js new file mode 100755 index 0000000..23fbd47 --- /dev/null +++ b/test/fixtures/apps/iframe-with-pathToRegexpModule/config/config.js @@ -0,0 +1,8 @@ +exports.keys = 'test key'; + +exports.security = { + defaultMiddleware: 'xframe', + xframe: { + ignore: ['/hello', '/world/:id'], + }, +}; diff --git a/test/fixtures/apps/iframe-with-pathToRegexpModule/package.json b/test/fixtures/apps/iframe-with-pathToRegexpModule/package.json new file mode 100755 index 0000000..319468f --- /dev/null +++ b/test/fixtures/apps/iframe-with-pathToRegexpModule/package.json @@ -0,0 +1,3 @@ +{ + "name": "iframe-with-pathToRegexpModule" +} diff --git a/test/xframe-with-pathToRegexpModule.test.js b/test/xframe-with-pathToRegexpModule.test.js new file mode 100644 index 0000000..fa06ff1 --- /dev/null +++ b/test/xframe-with-pathToRegexpModule.test.js @@ -0,0 +1,125 @@ +const { strict: assert } = require('node:assert'); +const mm = require('egg-mock'); + +describe('test/xframe-with-pathToRegexpModule.test.js', () => { + let app; + let app2; + let app3; + let app4; + before(async () => { + app = mm.app({ + baseDir: 'apps/iframe-with-pathToRegexpModule', + plugin: 'security', + pathToRegexpModule: require.resolve('path-to-regexp-v8'), + }); + await app.ready(); + + app2 = mm.app({ + baseDir: 'apps/iframe-novalue', + plugin: 'security', + pathToRegexpModule: require.resolve('path-to-regexp-v8'), + }); + await app2.ready(); + + app3 = mm.app({ + baseDir: 'apps/iframe-allowfrom', + plugin: 'security', + pathToRegexpModule: require.resolve('path-to-regexp-v8'), + }); + await app3.ready(); + + app4 = mm.app({ + baseDir: 'apps/iframe-black-urls', + plugin: 'security', + pathToRegexpModule: require.resolve('path-to-regexp-v8'), + }); + await app4.ready(); + }); + + afterEach(mm.restore); + + it('should contain X-Frame-Options: SAMEORIGIN', async () => { + await app.httpRequest() + .get('/') + .set('accept', 'text/html') + .expect('x-frame-options', 'SAMEORIGIN'); + + await app.httpRequest() + .get('/foo') + .set('accept', 'text/html') + .expect('x-frame-options', 'SAMEORIGIN'); + }); + + it('should contain X-Frame-Options: ALLOW-FROM http://www.domain.com by this.securityOptions', async () => { + const res = await app.httpRequest() + .get('/options') + .set('accept', 'text/html'); + assert.equal(res.status, 200); + assert.equal(res.headers['x-frame-options'], 'ALLOW-FROM http://www.domain.com'); + }); + + it('should contain X-Frame-Options: SAMEORIGIN when dont set value option', function(done) { + app2.httpRequest() + .get('/foo') + .set('accept', 'text/html') + .expect('x-frame-options', 'SAMEORIGIN', done); + }); + + it('should contain X-Frame-Options: ALLOW-FROM with page when set ALLOW-FROM and page option', function(done) { + app3.httpRequest() + .get('/foo') + .set('accept', 'text/html') + .expect('x-frame-options', 'ALLOW-FROM http://www.domain.com', done); + }); + + it('should not contain X-Frame-Options: SAMEORIGIN when use ignore', async () => { + let res = await app.httpRequest() + .get('/hello') + .set('accept', 'text/html') + .expect(200); + assert.equal(res.headers['x-frame-options'], undefined); + + // '/hello' won't match '/hello/other/world' on path-to-regexp@8 + res = await app.httpRequest() + .get('/hello/other/world') + .set('accept', 'text/html') + .expect(200); + assert.equal(res.headers['x-frame-options'], 'SAMEORIGIN'); + + res = await app4.httpRequest() + .get('/hello') + .set('accept', 'text/html') + .expect(200); + assert.equal(res.headers['x-frame-options'], undefined); + + res = await app.httpRequest() + .get('/world/12') + .set('accept', 'text/html') + .expect(200); + assert.equal(res.headers['x-frame-options'], undefined); + + res = await app.httpRequest() + .get('/world/12?xx=xx') + .set('accept', 'text/html') + .expect(200); + assert.equal(res.headers['x-frame-options'], undefined); + + res = await app2.httpRequest() + .get('/hello') + .set('accept', 'text/html') + .expect(200); + assert.equal(res.headers['x-frame-options'], undefined); + + res = await app2.httpRequest() + .get('/world/12') + .set('accept', 'text/html') + .expect(200); + assert.equal(res.headers['x-frame-options'], undefined); + + res = await app2.httpRequest() + .get('/world/12?xx=xx') + .set('accept', 'text/html') + .expect(200); + assert.equal(res.headers['x-frame-options'], undefined); + }); +}); diff --git a/test/xframe.test.js b/test/xframe.test.js index 9055a2a..4a033db 100644 --- a/test/xframe.test.js +++ b/test/xframe.test.js @@ -38,12 +38,12 @@ describe('test/xframe.test.js', () => { await app.httpRequest() .get('/') .set('accept', 'text/html') - .expect('X-Frame-Options', 'SAMEORIGIN'); + .expect('x-frame-options', 'SAMEORIGIN'); await app.httpRequest() .get('/foo') .set('accept', 'text/html') - .expect('X-Frame-Options', 'SAMEORIGIN'); + .expect('x-frame-options', 'SAMEORIGIN'); }); it('should contain X-Frame-Options: ALLOW-FROM http://www.domain.com by this.securityOptions', async () => { @@ -58,14 +58,14 @@ describe('test/xframe.test.js', () => { app2.httpRequest() .get('/foo') .set('accept', 'text/html') - .expect('X-Frame-Options', 'SAMEORIGIN', done); + .expect('x-frame-options', 'SAMEORIGIN', done); }); it('should contain X-Frame-Options: ALLOW-FROM with page when set ALLOW-FROM and page option', function(done) { app3.httpRequest() .get('/foo') .set('accept', 'text/html') - .expect('X-Frame-Options', 'ALLOW-FROM http://www.domain.com', done); + .expect('x-frame-options', 'ALLOW-FROM http://www.domain.com', done); }); it('should not contain X-Frame-Options: SAMEORIGIN when use ignore', async () => { @@ -73,42 +73,48 @@ describe('test/xframe.test.js', () => { .get('/hello') .set('accept', 'text/html') .expect(200); - assert.equal(res.headers['X-Frame-Options'], undefined); + assert.equal(res.headers['x-frame-options'], undefined); + + res = await app.httpRequest() + .get('/hello/other/world') + .set('accept', 'text/html') + .expect(200); + assert.equal(res.headers['x-frame-options'], undefined); res = await app4.httpRequest() .get('/hello') .set('accept', 'text/html') .expect(200); - assert.equal(res.headers['X-Frame-Options'], undefined); + assert.equal(res.headers['x-frame-options'], undefined); res = await app.httpRequest() .get('/world/12') .set('accept', 'text/html') .expect(200); - assert.equal(res.headers['X-Frame-Options'], undefined); + assert.equal(res.headers['x-frame-options'], undefined); res = await app.httpRequest() .get('/world/12?xx=xx') .set('accept', 'text/html') .expect(200); - assert.equal(res.headers['X-Frame-Options'], undefined); + assert.equal(res.headers['x-frame-options'], undefined); res = await app2.httpRequest() .get('/hello') .set('accept', 'text/html') .expect(200); - assert.equal(res.headers['X-Frame-Options'], undefined); + assert.equal(res.headers['x-frame-options'], undefined); res = await app2.httpRequest() .get('/world/12') .set('accept', 'text/html') .expect(200); - assert.equal(res.headers['X-Frame-Options'], undefined); + assert.equal(res.headers['x-frame-options'], undefined); res = await app2.httpRequest() .get('/world/12?xx=xx') .set('accept', 'text/html') .expect(200); - assert.equal(res.headers['X-Frame-Options'], undefined); + assert.equal(res.headers['x-frame-options'], undefined); }); });