From 8c1ae12a0c741aab8e398befadafeed58cb94696 Mon Sep 17 00:00:00 2001 From: John Hildenbiddle Date: Sun, 14 Feb 2021 14:10:46 -0600 Subject: [PATCH] fix: isExternal check with malformed URL + tests Fix #1477. Fix #1126. Follow-up to #1489. --- src/core/fetch/index.js | 2 +- test/e2e/security.test.js | 32 ++++++++++++++++++++++++++++++++ 2 files changed, 33 insertions(+), 1 deletion(-) create mode 100644 test/e2e/security.test.js diff --git a/src/core/fetch/index.js b/src/core/fetch/index.js index 0fac23bee..2d6f813c2 100644 --- a/src/core/fetch/index.js +++ b/src/core/fetch/index.js @@ -22,7 +22,7 @@ function loadNested(path, qs, file, next, vm, first) { function isExternal(url) { let match = url.match( - /^([^:/?#]+:)?(?:\/\/([^/?#]*))?([^?#]+)?(\?[^#]*)?(#.*)?/ + /^([^:/?#]+:)?(?:\/{2,}([^/?#]*))?([^?#]+)?(\?[^#]*)?(#.*)?/ ); if ( typeof match[1] === 'string' && diff --git a/test/e2e/security.test.js b/test/e2e/security.test.js new file mode 100644 index 000000000..caffddad6 --- /dev/null +++ b/test/e2e/security.test.js @@ -0,0 +1,32 @@ +const docsifyInit = require('../helpers/docsify-init'); + +describe(`Security`, function() { + const sharedOptions = { + markdown: { + homepage: '# Hello World', + }, + routes: { + 'test.md': '# Test Page', + }, + }; + + describe(`Cross Site Scripting (XSS)`, function() { + const slashStrings = ['//', '///']; + + for (const slashString of slashStrings) { + const hash = `#${slashString}domain.com/file.md`; + + test(`should not load remote content from hash (${hash})`, async () => { + await docsifyInit(sharedOptions); + await expect(page).toHaveText('#main', 'Hello World'); + await page.evaluate(() => (location.hash = '#/test')); + await expect(page).toHaveText('#main', 'Test Page'); + await page.evaluate(newHash => { + location.hash = newHash; + }, hash); + await expect(page).toHaveText('#main', 'Hello World'); + expect(page.url()).toMatch(/#\/$/); + }); + } + }); +});