From 7d4c6e926ee51cee2914c9e651b4eea43ffcfcf7 Mon Sep 17 00:00:00 2001 From: Manoj Vivek Date: Fri, 9 Aug 2019 15:06:14 +0530 Subject: [PATCH] Modifying content security policy --- browser-extension/package-lock.json | 9 +++++++ browser-extension/package.json | 1 + .../{background.js => background/index.js} | 24 +++++++++++++++-- browser-extension/src/background/utils.js | 15 +++++++++++ .../src/background/utils.test.js | 27 +++++++++++++++++++ browser-extension/webpack.config.js | 4 +-- 6 files changed, 76 insertions(+), 4 deletions(-) rename browser-extension/src/{background.js => background/index.js} (56%) create mode 100644 browser-extension/src/background/utils.js create mode 100644 browser-extension/src/background/utils.test.js diff --git a/browser-extension/package-lock.json b/browser-extension/package-lock.json index 1415ca2f..3f02dcaf 100644 --- a/browser-extension/package-lock.json +++ b/browser-extension/package-lock.json @@ -15918,6 +15918,15 @@ "resolved": "https://registry.npmjs.org/ret/-/ret-0.1.15.tgz", "integrity": "sha512-TTlYpa+OL+vMMNG24xSlQGEJ3B/RzEfUlLct7b5G/ytav+wPrplCpVMFuwzXbkecJrb6IYo1iFb0S9v37754mg==" }, + "rewire": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/rewire/-/rewire-4.0.1.tgz", + "integrity": "sha512-+7RQ/BYwTieHVXetpKhT11UbfF6v1kGhKFrtZN7UDL2PybMsSt/rpLWeEUGF5Ndsl1D5BxiCB14VDJyoX+noYw==", + "dev": true, + "requires": { + "eslint": "^4.19.1" + } + }, "rgb-regex": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/rgb-regex/-/rgb-regex-1.0.1.tgz", diff --git a/browser-extension/package.json b/browser-extension/package.json index 7c9e2beb..0a825cfc 100644 --- a/browser-extension/package.json +++ b/browser-extension/package.json @@ -40,6 +40,7 @@ "enzyme-adapter-react-16": "^1.14.0", "identity-obj-proxy": "^3.0.0", "jest": "^24.8.0", + "rewire": "^4.0.1", "sinon": "^7.3.2", "style-loader": "^0.23.1", "web-ext-webpack-plugin": "github:hiikezoe/web-ext-webpack-plugin", diff --git a/browser-extension/src/background.js b/browser-extension/src/background/index.js similarity index 56% rename from browser-extension/src/background.js rename to browser-extension/src/background/index.js index 2c038bad..93e7ab27 100644 --- a/browser-extension/src/background.js +++ b/browser-extension/src/background/index.js @@ -1,6 +1,7 @@ import 'webextension-polyfill'; -import {MESSAGE_TYPES} from './app/commons/postMan'; +import {MESSAGE_TYPES} from '../app/commons/postMan'; import normalizeUrl from 'normalize-url'; +import {processCSPHeader} from './utils'; chrome.browserAction.onClicked.addListener(activeTab => { const newURL = chrome.runtime.getURL(`/app/index.html?url=${activeTab.url}`); @@ -13,7 +14,15 @@ function onHeadersReceived(details) { console.log('currentUrls', currentUrls, details.url, details.responseHeaders && currentUrls[details.url], details.responseHeaders, currentUrls[details.url]); const normalizedUrl = normalizeUrl(details.url); if (details.responseHeaders && currentUrls[normalizedUrl]) { - const newHeaders = removeHeader(details.responseHeaders, 'X-Frame-Options'); + const extensionURL = chrome.runtime.getURL('/'); + const CSPHeader = getHeader(details.responseHeaders, 'Content-Security-Policy'); + const newCSPHeaderValue = processCSPHeader(CSPHeader, extensionURL); + let newHeaders = removeHeader(details.responseHeaders, 'Content-Security-Policy'); + newHeaders = addHeader(newHeaders, 'Content-Security-Policy', newCSPHeaderValue); + newHeaders = removeHeader(newHeaders, 'X-Frame-Options'); + /*console.log('After X-frameOptions removal', newHeaders) + newHeaders = addHeader(newHeaders, 'x-frame-options', `allow-from ${extensionURL}`) + */ console.log('newHeaders', newHeaders); return { responseHeaders: newHeaders }; } @@ -25,10 +34,21 @@ function removeHeader(headers, headerToRemove) { return headers.filter(({ name }) => name.toLowerCase() != headerToRemove); } +function addHeader(headers, name, value) { + headers.push({name, value}); + return headers; +} + +function getHeader(headers, headerToFind) { + headerToFind = headerToFind.toLowerCase(); + return headers.filter(({name}) => name.toLowerCase() === headerToFind)[0]; +} + chrome.webRequest.onHeadersReceived.addListener( onHeadersReceived, { urls: [""], + types: ["main_frame", "sub_frame"] }, ['blocking', 'responseHeaders'] ); diff --git a/browser-extension/src/background/utils.js b/browser-extension/src/background/utils.js new file mode 100644 index 00000000..3b5ddaee --- /dev/null +++ b/browser-extension/src/background/utils.js @@ -0,0 +1,15 @@ +export function processCSPHeader(header, extensionURL) { + const defaultFrameAccessor = `frame-ancestors ${extensionURL}`; + if (!header || !header.value) { + return defaultFrameAccessor; + } + const directives = header.value.split(';').map(value => value.trim()).filter(value => value.length); + const frameAccessorDirective = directives.filter(directive => directive.indexOf('frame-ancestors') === 0)[0]; + if (!frameAccessorDirective) { + return [...directives, defaultFrameAccessor].join('; '); + } + const frameAccessorDirectiveValues = frameAccessorDirective.split(' ').map(value => value.trim()).filter(value => value.length); + frameAccessorDirectiveValues.push(extensionURL); + const nonFrameAccessorDirectives = directives.filter(directive => directive.indexOf('frame-ancestors') === -1); + return [...nonFrameAccessorDirectives, frameAccessorDirectiveValues.join(' ')].join('; '); +} \ No newline at end of file diff --git a/browser-extension/src/background/utils.test.js b/browser-extension/src/background/utils.test.js new file mode 100644 index 00000000..4a2d0504 --- /dev/null +++ b/browser-extension/src/background/utils.test.js @@ -0,0 +1,27 @@ +import {expect} from 'chai'; +import {processCSPHeader} from './utils'; + +describe('background/utils', () => { + describe('processCSPHeader', () => { + const extensionURL = 'chrome-extension://ahichoemcjmdgdojboecnokgfolkmfmo/'; + + it('should return the frame-accessor director if the response header is null', () => { + const result = processCSPHeader(null, extensionURL); + expect(result).to.be.equals(`frame-ancestors ${extensionURL}`); + }); + + it('should return the frame-accessor directive in addition to the existing directives', () => { + const oldValue = "default-src 'self';" + const result = processCSPHeader({name: 'Content-Security-Policy', value: oldValue}, extensionURL); + expect(result).to.be.equals(`${oldValue} frame-ancestors ${extensionURL}`); + }); + + it('should append the current source the existing frame-accessor directive', () => { + const oldValue = "upgrade-insecure-requests; frame-ancestors 'self' https://stackexchange.com" + const result = processCSPHeader({name: 'Content-Security-Policy', value: oldValue}, extensionURL); + expect(result).to.be.equals(`${oldValue} ${extensionURL}`); + }); + + }); +}); + diff --git a/browser-extension/webpack.config.js b/browser-extension/webpack.config.js index 15c7eef3..7a032fd6 100755 --- a/browser-extension/webpack.config.js +++ b/browser-extension/webpack.config.js @@ -4,7 +4,7 @@ const path = require('path'); module.exports = { entry: { - background: ['./src/background.js'], + background: ['./src/background'], app: ['./src/app/index.js'] }, output: { @@ -32,7 +32,7 @@ module.exports = { options: { modules: { mode: 'local', - localIdentName: '[path][name]__[local]--[hash:base64:5]', + localIdentName: '[name]__[local]--[hash:base64:5]', }, /* importLoaders: 1, sourceMap: true,