diff options
author | 2019-03-05 16:20:40 +0100 | |
---|---|---|
committer | 2019-03-05 16:20:40 +0100 | |
commit | 6e5531ae16d69087856ce7f174ba465bc759394c (patch) | |
tree | 510ab3208f045dbe574b53123c752b9d9349d5a3 | |
parent | Merge branch 'develop' of https://github.com/meetfranz/franz into develop (diff) | |
download | ferdium-app-6e5531ae16d69087856ce7f174ba465bc759394c.tar.gz ferdium-app-6e5531ae16d69087856ce7f174ba465bc759394c.tar.zst ferdium-app-6e5531ae16d69087856ce7f174ba465bc759394c.zip |
feat(App): Add security checks for external URLs
-rw-r--r-- | src/config.js | 6 | ||||
-rw-r--r-- | src/helpers/url-helpers.js | 15 | ||||
-rw-r--r-- | src/index.js | 6 | ||||
-rw-r--r-- | src/stores/AppStore.js | 11 |
4 files changed, 36 insertions, 2 deletions
diff --git a/src/config.js b/src/config.js index a782ad667..479572edb 100644 --- a/src/config.js +++ b/src/config.js | |||
@@ -62,3 +62,9 @@ export const SETTINGS_PATH = path.join(app.getPath('userData'), 'config'); | |||
62 | 62 | ||
63 | // Replacing app.asar is not beautiful but unforunately necessary | 63 | // Replacing app.asar is not beautiful but unforunately necessary |
64 | export const DICTIONARY_PATH = asarPath(path.join(__dirname, 'dictionaries')); | 64 | export const DICTIONARY_PATH = asarPath(path.join(__dirname, 'dictionaries')); |
65 | |||
66 | export const ALLOWED_PROTOCOLS = [ | ||
67 | 'https:', | ||
68 | 'http:', | ||
69 | 'ftp:', | ||
70 | ]; | ||
diff --git a/src/helpers/url-helpers.js b/src/helpers/url-helpers.js new file mode 100644 index 000000000..750d1f00c --- /dev/null +++ b/src/helpers/url-helpers.js | |||
@@ -0,0 +1,15 @@ | |||
1 | import { URL } from 'url'; | ||
2 | |||
3 | import { ALLOWED_PROTOCOLS } from '../config'; | ||
4 | |||
5 | const debug = require('debug')('Franz:Helpers:url'); | ||
6 | |||
7 | export function isValidExternalURL(url) { | ||
8 | const parsedUrl = new URL(url); | ||
9 | |||
10 | const isAllowed = ALLOWED_PROTOCOLS.includes(parsedUrl.protocol); | ||
11 | |||
12 | debug('protocol check is', isAllowed, 'for:', url); | ||
13 | |||
14 | return isAllowed; | ||
15 | } | ||
diff --git a/src/index.js b/src/index.js index 0614197a2..0e222c3d6 100644 --- a/src/index.js +++ b/src/index.js | |||
@@ -34,6 +34,7 @@ import { | |||
34 | DEFAULT_WINDOW_OPTIONS, | 34 | DEFAULT_WINDOW_OPTIONS, |
35 | } from './config'; | 35 | } from './config'; |
36 | import { asarPath } from './helpers/asar-helpers'; | 36 | import { asarPath } from './helpers/asar-helpers'; |
37 | import { isValidExternalURL } from './helpers/url-helpers'; | ||
37 | /* eslint-enable import/first */ | 38 | /* eslint-enable import/first */ |
38 | 39 | ||
39 | const debug = require('debug')('Franz:App'); | 40 | const debug = require('debug')('Franz:App'); |
@@ -294,7 +295,10 @@ const createWindow = () => { | |||
294 | mainWindow.webContents.on('new-window', (e, url) => { | 295 | mainWindow.webContents.on('new-window', (e, url) => { |
295 | debug('Open url', url); | 296 | debug('Open url', url); |
296 | e.preventDefault(); | 297 | e.preventDefault(); |
297 | shell.openExternal(url); | 298 | |
299 | if (isValidExternalURL(url)) { | ||
300 | shell.openExternal(url); | ||
301 | } | ||
298 | }); | 302 | }); |
299 | }; | 303 | }; |
300 | 304 | ||
diff --git a/src/stores/AppStore.js b/src/stores/AppStore.js index 168aa7e48..f9009af5a 100644 --- a/src/stores/AppStore.js +++ b/src/stores/AppStore.js | |||
@@ -8,6 +8,7 @@ import { getDoNotDisturb } from '@meetfranz/electron-notification-state'; | |||
8 | import AutoLaunch from 'auto-launch'; | 8 | import AutoLaunch from 'auto-launch'; |
9 | import prettyBytes from 'pretty-bytes'; | 9 | import prettyBytes from 'pretty-bytes'; |
10 | import ms from 'ms'; | 10 | import ms from 'ms'; |
11 | import { URL } from 'url'; | ||
11 | 12 | ||
12 | import Store from './lib/Store'; | 13 | import Store from './lib/Store'; |
13 | import Request from './lib/Request'; | 14 | import Request from './lib/Request'; |
@@ -19,6 +20,7 @@ import { onVisibilityChange } from '../helpers/visibility-helper'; | |||
19 | import { getLocale } from '../helpers/i18n-helpers'; | 20 | import { getLocale } from '../helpers/i18n-helpers'; |
20 | 21 | ||
21 | import { getServiceIdsFromPartitions, removeServicePartitionDirectory } from '../helpers/service-helpers.js'; | 22 | import { getServiceIdsFromPartitions, removeServicePartitionDirectory } from '../helpers/service-helpers.js'; |
23 | import { isValidExternalURL } from '../helpers/url-helpers'; | ||
22 | 24 | ||
23 | const debug = require('debug')('Franz:AppStore'); | 25 | const debug = require('debug')('Franz:AppStore'); |
24 | 26 | ||
@@ -256,7 +258,14 @@ export default class AppStore extends Store { | |||
256 | } | 258 | } |
257 | 259 | ||
258 | @action _openExternalUrl({ url }) { | 260 | @action _openExternalUrl({ url }) { |
259 | shell.openExternal(url); | 261 | const parsedUrl = new URL(url); |
262 | debug('open external url', parsedUrl); | ||
263 | |||
264 | if (isValidExternalURL(url)) { | ||
265 | shell.openExternal(url); | ||
266 | } | ||
267 | |||
268 | gaEvent('External URL', 'open', parsedUrl.host); | ||
260 | } | 269 | } |
261 | 270 | ||
262 | @action _checkForUpdates() { | 271 | @action _checkForUpdates() { |