diff --git a/.gitignore b/.gitignore index 3475a11bb..1f23b2ba2 100644 --- a/.gitignore +++ b/.gitignore @@ -11,3 +11,4 @@ crowdin.yml add-on/dist coverage .nyc_output +add-on/ui-kit/**/* diff --git a/add-on/_locales/en/messages.json b/add-on/_locales/en/messages.json index 8bd0d46bc..c3a696c24 100644 --- a/add-on/_locales/en/messages.json +++ b/add-on/_locales/en/messages.json @@ -400,8 +400,37 @@ } } }, + "page_proxyAccessDialog_title": { + "message": "Allow $ORIGIN$ to access ipfs.$PERMISSION$?", + "description": "Main title of the access permission dialog (page_proxyAccessDialog_title)", + "placeholders": { + "origin": { + "content": "$1" + }, + "permission": { + "content": "$2" + } + } + }, + "page_proxyAccessDialog_wildcardCheckbox_label": { + "message": "Apply to all permissions for $ORIGIN$", + "description": "Label for the apply permissions to all checkbox (page_proxyAccessDialog_wildcardCheckbox_label)", + "placeholders": { + "origin": { + "content": "$1" + } + } + }, "page_proxyAcl_revoke_all_button_title": { "message": "Revoke all permissions", "description": "Button title for revoking all permissions (page_proxyAcl_revoke_all_button_title)" + }, + "page_proxyAccessDialog_allowButton_text": { + "message": "Allow", + "description": "Button text for allowing a permission (page_proxyAccessDialog_allowButton_text)" + }, + "page_proxyAccessDialog_denyButton_text": { + "message": "Deny", + "description": "Button text for denying a permission (page_proxyAccessDialog_allowButton_text)" } } diff --git a/add-on/manifest.json b/add-on/manifest.json index e975253da..84819e3a4 100644 --- a/add-on/manifest.json +++ b/add-on/manifest.json @@ -90,7 +90,7 @@ } ], - "content_security_policy": "script-src 'self'; object-src 'self'; child-src 'self';", + "content_security_policy": "script-src 'self'; object-src 'self'; frame-src 'self';", "default_locale": "en" } diff --git a/add-on/src/lib/ipfs-proxy/access-control.js b/add-on/src/lib/ipfs-proxy/access-control.js index d6fc06510..08a4959e0 100644 --- a/add-on/src/lib/ipfs-proxy/access-control.js +++ b/add-on/src/lib/ipfs-proxy/access-control.js @@ -53,6 +53,10 @@ class AccessControl extends EventEmitter { const grants = await this._getGrants(origin) + if (grants.has('*')) { + return { origin, permission, allow: grants.get('*') } + } + return grants.has(permission) ? { origin, permission, allow: grants.get(permission) } : null @@ -67,6 +71,23 @@ class AccessControl extends EventEmitter { const access = { origin, permission, allow } const grants = await this._getGrants(origin) + // Trying to set access for non-wildcard permission, when wildcard + // permission is already granted? + if (grants.has('*') && permission !== '*') { + if (grants.get('*') === allow) { + // Noop if requested access is the same as access for wildcard grant + return access + } else { + // Fail if requested access is the different to access for wildcard grant + throw new Error(`Illegal set access for ${permission} when wildcard exists`) + } + } + + // If setting a wildcard permission, remove existing grants + if (permission === '*') { + grants.clear() + } + grants.set(permission, allow) await this._setGrants(origin, grants) diff --git a/add-on/src/lib/ipfs-proxy/index.js b/add-on/src/lib/ipfs-proxy/index.js index 7dabd3c2f..f58e9bacf 100644 --- a/add-on/src/lib/ipfs-proxy/index.js +++ b/add-on/src/lib/ipfs-proxy/index.js @@ -5,11 +5,13 @@ const browser = require('webextension-polyfill') const { createProxyServer, closeProxyServer } = require('ipfs-postmsg-proxy') const AccessControl = require('./access-control') const createPreAcl = require('./pre-acl') +const createRequestAccess = require('./request-access') // Creates an object that manages the "server side" of the IPFS proxy function createIpfsProxy (getIpfs, getState) { let connections = [] const accessControl = new AccessControl(browser.storage) + const requestAccess = createRequestAccess(browser, screen) // Port connection events are emitted when a content script attempts to // communicate with us. Each new URL visited by the user will open a port. @@ -25,7 +27,7 @@ function createIpfsProxy (getIpfs, getState) { removeListener: (_, handler) => port.onMessage.removeListener(handler), postMessage: (data) => port.postMessage(data), getMessageData: (d) => d, - pre: (fnName) => createPreAcl(getState, accessControl, origin, fnName) + pre: (fnName) => createPreAcl(getState, accessControl, origin, fnName, requestAccess) }) const close = () => { diff --git a/add-on/src/lib/ipfs-proxy/pre-acl.js b/add-on/src/lib/ipfs-proxy/pre-acl.js index dac18cde7..60d9015a7 100644 --- a/add-on/src/lib/ipfs-proxy/pre-acl.js +++ b/add-on/src/lib/ipfs-proxy/pre-acl.js @@ -1,13 +1,11 @@ -const defaultRequestAccess = require('./request-access') - -// This are the function that DO NOT require an allow/deny decision by the user. +// This are the functions that DO NOT require an allow/deny decision by the user. // All other IPFS functions require authorization. const ACL_WHITELIST = Object.freeze(require('./acl-whitelist.json')) // Creates a "pre" function that is called prior to calling a real function // on the IPFS instance. It will throw if access is denied, and ask the user if // no access decision has been made yet. -function createPreAcl (getState, accessControl, origin, permission, requestAccess = defaultRequestAccess) { +function createPreAcl (getState, accessControl, origin, permission, requestAccess) { return async (...args) => { // Check if all access to the IPFS node is disabled if (!getState().ipfsProxy) throw new Error('User disabled access to IPFS') @@ -18,8 +16,8 @@ function createPreAcl (getState, accessControl, origin, permission, requestAcces let access = await accessControl.getAccess(origin, permission) if (!access) { - const { allow } = await requestAccess(origin, permission) - access = await accessControl.setAccess(origin, permission, allow) + const { allow, wildcard } = await requestAccess(origin, permission) + access = await accessControl.setAccess(origin, wildcard ? '*' : permission, allow) } if (!access.allow) throw new Error(`User denied access to ${permission}`) diff --git a/add-on/src/lib/ipfs-proxy/request-access.js b/add-on/src/lib/ipfs-proxy/request-access.js index d7b9b5cdf..59f27dc30 100644 --- a/add-on/src/lib/ipfs-proxy/request-access.js +++ b/add-on/src/lib/ipfs-proxy/request-access.js @@ -1,17 +1,92 @@ -async function requestAccess (origin, permission) { - const msg = `Allow ${origin} to access ipfs.${permission}?` +'use strict' - // TODO: add checkbox to allow all for this origin - let allow +const DIALOG_WIDTH = 540 +const DIALOG_HEIGHT = 200 +const DIALOG_PATH = 'dist/pages/proxy-access-dialog/index.html' +const DIALOG_PORT_NAME = 'proxy-access-dialog' - try { - allow = window.confirm(msg) - } catch (err) { - console.warn('Failed to confirm, possibly not supported in this environment', err) - allow = false +function createRequestAccess (browser, screen) { + return async function requestAccess (origin, permission, opts) { + opts = opts || {} + + const width = opts.dialogWidth || DIALOG_WIDTH + const height = opts.dialogHeight || DIALOG_HEIGHT + + const url = browser.extension.getURL(opts.dialogPath || DIALOG_PATH) + const currentWin = await browser.windows.getCurrent() + + const top = Math.round(((screen.width / 2) - (width / 2)) + currentWin.left) + const left = Math.round(((screen.height / 2) - (height / 2)) + currentWin.top) + + const { tabs } = await browser.windows.create({ url, width, height, top, left, type: 'popup' }) + + // Resolves with { allow, wildcard } + const userResponse = getUserResponse(tabs[0].id, origin, permission, opts) + // Never resolves, might reject if user closes the tab + const userTabRemoved = getUserTabRemoved(tabs[0].id, origin, permission) + + let response + + try { + // Will the user respond to or close the dialog? + response = await Promise.race([userTabRemoved, userResponse]) + } finally { + userTabRemoved.destroy() + userResponse.destroy() + } + + await browser.tabs.remove(tabs[0].id) + + return response + } + + function getUserResponse (tabId, origin, permission, opts) { + opts = opts || {} + + const dialogPortName = opts.dialogPortName || DIALOG_PORT_NAME + let onPortConnect + + const userResponse = new Promise((resolve, reject) => { + onPortConnect = port => { + if (port.name !== dialogPortName) return + + browser.runtime.onConnect.removeListener(onPortConnect) + + // Tell the dialog what origin/permission it is about + port.postMessage({ origin, permission }) + + // Wait for the user response + const onMessage = ({ allow, wildcard }) => { + port.onMessage.removeListener(onMessage) + resolve({ allow, wildcard }) + } + + port.onMessage.addListener(onMessage) + } + + browser.runtime.onConnect.addListener(onPortConnect) + }) + + userResponse.destroy = () => browser.runtime.onConnect.removeListener(onPortConnect) + + return userResponse } - return { allow } + // Since the dialog is a tab not a real dialog it can be closed by the user + // with no response, this function creates a promise that will reject if the tab + // is removed. + function getUserTabRemoved (tabId, origin, permission) { + let onTabRemoved + + const userTabRemoved = new Promise((resolve, reject) => { + onTabRemoved = () => reject(new Error(`Failed to obtain access response for ${permission} at ${origin}`)) + browser.tabs.onRemoved.addListener(onTabRemoved) + }) + + userTabRemoved.destroy = () => browser.tabs.onRemoved.removeListener(onTabRemoved) + + return userTabRemoved + } } -module.exports = requestAccess +module.exports = createRequestAccess diff --git a/add-on/src/lib/state.js b/add-on/src/lib/state.js index b802c644d..b902e819a 100644 --- a/add-on/src/lib/state.js +++ b/add-on/src/lib/state.js @@ -19,6 +19,7 @@ function initState (options) { state.preloadAtPublicGateway = options.preloadAtPublicGateway state.catchUnhandledProtocols = options.catchUnhandledProtocols state.displayNotifications = options.displayNotifications + state.ipfsProxy = options.ipfsProxy return state } diff --git a/add-on/src/options/forms/experiments-form.js b/add-on/src/options/forms/experiments-form.js index 6142b279c..ac76b2469 100644 --- a/add-on/src/options/forms/experiments-form.js +++ b/add-on/src/options/forms/experiments-form.js @@ -77,11 +77,11 @@ function experimentsForm ({
${browser.i18n.getMessage('option_ipfsProxy_title')}
${browser.i18n.getMessage('option_ipfsProxy_description')} - ${ipfsProxy ? html` - - ${browser.i18n.getMessage('option_ipfsProxy_link_manage_permissions')} - - ` : null} +

${ipfsProxy ? html` + + ${browser.i18n.getMessage('option_ipfsProxy_link_manage_permissions')} + ` : html`${browser.i18n.getMessage('option_ipfsProxy_link_manage_permissions')}`} +

diff --git a/add-on/src/options/page.css b/add-on/src/options/options.css similarity index 100% rename from add-on/src/options/page.css rename to add-on/src/options/options.css diff --git a/add-on/src/options/options.html b/add-on/src/options/options.html index 3bff95bf9..9ebbf23e7 100644 --- a/add-on/src/options/options.html +++ b/add-on/src/options/options.html @@ -4,6 +4,7 @@ Options +
diff --git a/add-on/src/options/page.js b/add-on/src/options/page.js index e3c57f120..6dbb237f2 100644 --- a/add-on/src/options/page.js +++ b/add-on/src/options/page.js @@ -7,8 +7,6 @@ const gatewaysForm = require('./forms/gateways-form') const apiForm = require('./forms/api-form') const experimentsForm = require('./forms/experiments-form') -require('./page.css') - // Render the options page: // Passed current app `state` from the store and `emit`, a function to create // events, allowing views to signal back to the store that something happened. diff --git a/add-on/src/pages/proxy-access-dialog/index.html b/add-on/src/pages/proxy-access-dialog/index.html new file mode 100644 index 000000000..40855149b --- /dev/null +++ b/add-on/src/pages/proxy-access-dialog/index.html @@ -0,0 +1,17 @@ + + + + + IPFS Companion + + + + + + +
+ + + + + diff --git a/add-on/src/pages/proxy-access-dialog/index.js b/add-on/src/pages/proxy-access-dialog/index.js new file mode 100644 index 000000000..c38b61a2f --- /dev/null +++ b/add-on/src/pages/proxy-access-dialog/index.js @@ -0,0 +1,12 @@ +'use strict' + +const browser = require('webextension-polyfill') +const choo = require('choo') +const createProxyAccessDialogStore = require('./store') +const createProxyAccessDialogPage = require('./page') + +const app = choo() + +app.use(createProxyAccessDialogStore(browser.i18n, browser.runtime)) +app.route('*', createProxyAccessDialogPage(browser.i18n)) +app.mount('#root') diff --git a/add-on/src/pages/proxy-access-dialog/page.js b/add-on/src/pages/proxy-access-dialog/page.js new file mode 100644 index 000000000..12985cda6 --- /dev/null +++ b/add-on/src/pages/proxy-access-dialog/page.js @@ -0,0 +1,78 @@ +'use strict' + +const html = require('choo/html') + +function createProxyAccessDialogPage (i18n) { + return function proxyAccessDialogPage (state, emit) { + const onAllow = () => emit('allow') + const onDeny = () => emit('deny') + const onWildcardToggle = () => emit('wildcardToggle') + + const { loading, origin, permission } = state + + return html` +
+
+ ${loading ? null : html` +
+

+ ${i18n.getMessage('page_proxyAccessDialog_title', [origin, permission])} +

+

+ +

+
+ `} +
+
+ ${loading ? html` +
+ + ${button({ + text: i18n.getMessage('page_proxyAccessDialog_denyButton_text'), + disabled: true + })} + + ${button({ + text: i18n.getMessage('page_proxyAccessDialog_allowButton_text'), + disabled: true + })} +
+ ` : html` +
+ + ${button({ + text: i18n.getMessage('page_proxyAccessDialog_denyButton_text'), + onClick: onDeny, + color: 'red' + })} + + ${button({ + text: i18n.getMessage('page_proxyAccessDialog_allowButton_text'), + onClick: onAllow, + color: 'aqua' + })} +
+ `} +
+
+ ` + } +} + +function button ({ onClick, color, text, disabled }) { + if (disabled) { + return html` + + ` + } + + return html` + + ` +} + +module.exports = createProxyAccessDialogPage diff --git a/add-on/src/pages/proxy-access-dialog/proxy-access-dialog.css b/add-on/src/pages/proxy-access-dialog/proxy-access-dialog.css new file mode 100644 index 000000000..918eecbc9 --- /dev/null +++ b/add-on/src/pages/proxy-access-dialog/proxy-access-dialog.css @@ -0,0 +1,10 @@ +@import url('../../../ui-kit/tachyons.css'); +@import url('../../../ui-kit/ipfs.css'); + +.hover-bg-aqua-muted:hover { + background-color: #9ad4db; +} + +.hover-bg-red-muted:hover { + background-color: #f36149; +} diff --git a/add-on/src/pages/proxy-access-dialog/store.js b/add-on/src/pages/proxy-access-dialog/store.js new file mode 100644 index 000000000..78daebf84 --- /dev/null +++ b/add-on/src/pages/proxy-access-dialog/store.js @@ -0,0 +1,35 @@ +'use strict' + +function createProxyAccessDialogStore (i18n, runtime) { + return function proxyAccessDialogStore (state, emitter) { + state.origin = null + state.permission = null + state.loading = true + state.wildcard = false + + const port = runtime.connect({ name: 'proxy-access-dialog' }) + + const onMessage = (data) => { + if (!data || !data.origin || !data.permission) return + port.onMessage.removeListener(onMessage) + + state.origin = data.origin + state.permission = data.permission + state.loading = false + + emitter.emit('render') + } + + port.onMessage.addListener(onMessage) + + emitter.on('allow', () => port.postMessage({ allow: true, wildcard: state.wildcard })) + emitter.on('deny', () => port.postMessage({ allow: false, wildcard: state.wildcard })) + + emitter.on('wildcardToggle', () => { + state.wildcard = !state.wildcard + emitter.emit('render') + }) + } +} + +module.exports = createProxyAccessDialogStore diff --git a/add-on/src/pages/proxy-acl/index.html b/add-on/src/pages/proxy-acl/index.html index bd0b7b9b2..40386161e 100644 --- a/add-on/src/pages/proxy-acl/index.html +++ b/add-on/src/pages/proxy-acl/index.html @@ -5,6 +5,7 @@ Manage Permissions + diff --git a/add-on/src/pages/proxy-acl/page.js b/add-on/src/pages/proxy-acl/page.js index a38f4ccb8..cdeb95710 100644 --- a/add-on/src/pages/proxy-acl/page.js +++ b/add-on/src/pages/proxy-acl/page.js @@ -3,8 +3,6 @@ const html = require('choo/html') const logo = require('../../popup/logo') -require('./page.css') - function createProxyAclPage (i18n) { return function proxyAclPage (state, emit) { const onRevoke = (e) => emit('revoke', e) diff --git a/add-on/src/pages/proxy-acl/page.css b/add-on/src/pages/proxy-acl/proxy-acl.css similarity index 66% rename from add-on/src/pages/proxy-acl/page.css rename to add-on/src/pages/proxy-acl/proxy-acl.css index aaca1a7a1..5bbc23846 100644 --- a/add-on/src/pages/proxy-acl/page.css +++ b/add-on/src/pages/proxy-acl/proxy-acl.css @@ -1,4 +1,5 @@ -@import url('node_modules/tachyons/css/tachyons.css'); +@import url('../../../ui-kit/tachyons.css'); +@import url('../../popup/heartbeat.css'); html, body { height: 100%; diff --git a/add-on/src/popup/browser-action/page.css b/add-on/src/popup/browser-action/browser-action.css similarity index 54% rename from add-on/src/popup/browser-action/page.css rename to add-on/src/popup/browser-action/browser-action.css index 401e3daa9..17d77791d 100644 --- a/add-on/src/popup/browser-action/page.css +++ b/add-on/src/popup/browser-action/browser-action.css @@ -1,5 +1,5 @@ -@import url('node_modules/tachyons/css/tachyons.css'); -@import url('./mdc.switch.css'); +@import url('../../../ui-kit/tachyons.css'); +@import url('mdc.switch.css'); .bg-near-white--hover:hover { background-color: #F4F4F4; diff --git a/add-on/src/popup/browser-action/index.html b/add-on/src/popup/browser-action/index.html index 8ba9fe11a..286fe1f5e 100644 --- a/add-on/src/popup/browser-action/index.html +++ b/add-on/src/popup/browser-action/index.html @@ -3,6 +3,7 @@ +
diff --git a/add-on/src/popup/browser-action/page.js b/add-on/src/popup/browser-action/page.js index 7d03d411a..658929d4f 100644 --- a/add-on/src/popup/browser-action/page.js +++ b/add-on/src/popup/browser-action/page.js @@ -7,8 +7,6 @@ const contextActions = require('./context-actions') const operations = require('./operations') const gatewayStatus = require('./gateway-status') -require('./page.css') - // Render the browser action page: // Passed current app `state` from the store and `emit`, a function to create // events, allowing views to signal back to the store that something happened. diff --git a/add-on/src/popup/logo.js b/add-on/src/popup/logo.js index cc7f9b257..de7422d17 100644 --- a/add-on/src/popup/logo.js +++ b/add-on/src/popup/logo.js @@ -2,7 +2,6 @@ /* eslint-env browser, webextensions */ const html = require('choo/html') -require('./heartbeat.css') function logo ({ path, size = 52, ipfsNodeType = 'external', isIpfsOnline = true, heartbeat = true }) { const logoTypePrefix = ipfsNodeType === 'embedded' ? 'js-' : '' diff --git a/add-on/src/popup/quick-upload.css b/add-on/src/popup/quick-upload.css index 9c7703a93..c34cad974 100644 --- a/add-on/src/popup/quick-upload.css +++ b/add-on/src/popup/quick-upload.css @@ -1,4 +1,5 @@ -@import url('node_modules/tachyons/css/tachyons.css'); +@import url('../../ui-kit/tachyons.css'); +@import url('heartbeat.css'); html, body, #root { height: 100%; diff --git a/add-on/src/popup/quick-upload.html b/add-on/src/popup/quick-upload.html index 24dfc8ae4..b21e81b7b 100644 --- a/add-on/src/popup/quick-upload.html +++ b/add-on/src/popup/quick-upload.html @@ -5,6 +5,7 @@ IPFS Upload + diff --git a/add-on/src/popup/quick-upload.js b/add-on/src/popup/quick-upload.js index b28f76ded..2260c121a 100644 --- a/add-on/src/popup/quick-upload.js +++ b/add-on/src/popup/quick-upload.js @@ -6,8 +6,6 @@ const choo = require('choo') const html = require('choo/html') const logo = require('./logo') -require('./quick-upload.css') - const app = choo() app.use(quickUploadStore) diff --git a/package.json b/package.json index f4d5014d1..1f1038f23 100644 --- a/package.json +++ b/package.json @@ -14,8 +14,13 @@ "build": "run-s clean build:*", "build:copy": "run-s build:copy:*", "build:copy:src": "shx mkdir -p add-on/dist && shx cp -R add-on/src/* add-on/dist", + "build:copy:ui-kit": "run-p build:copy:ui-kit:*", + "build:copy:ui-kit:ipfs-css": "run-p build:copy:ui-kit:ipfs-css:*", + "build:copy:ui-kit:ipfs-css:css": "shx mkdir -p add-on/ui-kit && shx cp node_modules/ipfs-css/ipfs.css add-on/ui-kit", + "build:copy:ui-kit:ipfs-css:fonts": "shx mkdir -p add-on/ui-kit/fonts && shx cp node_modules/ipfs-css/fonts/* add-on/ui-kit/fonts", + "build:copy:ui-kit:tachyons": "shx mkdir -p add-on/ui-kit && shx cp node_modules/tachyons/css/tachyons.css add-on/ui-kit", "build:copy:wx-polyfill-lib": "shx cp node_modules/webextension-polyfill/dist/browser-polyfill.min.js add-on/dist/contentScripts/browser-polyfill.min.js", - "build:js": "browserify -p prundupify -t browserify-css -t [ browserify-package-json --global ] add-on/src/background/background.js add-on/src/options/options.js add-on/src/popup/browser-action/index.js add-on/src/popup/quick-upload.js add-on/src/pages/proxy-acl/index.js -p [ factor-bundle -o add-on/dist/background/background.js -o add-on/dist/options/options.js -o add-on/dist/popup/browser-action/browser-action.js -o add-on/dist/popup/quick-upload.js -o add-on/dist/pages/proxy-acl/proxy-acl.js ] -o add-on/dist/ipfs-companion-common.js", + "build:js": "browserify -p prundupify -t [ browserify-package-json --global ] add-on/src/background/background.js add-on/src/options/options.js add-on/src/popup/browser-action/index.js add-on/src/popup/quick-upload.js add-on/src/pages/proxy-acl/index.js add-on/src/pages/proxy-access-dialog/index.js -p [ factor-bundle -o add-on/dist/background/background.js -o add-on/dist/options/options.js -o add-on/dist/popup/browser-action/browser-action.js -o add-on/dist/popup/quick-upload.js -o add-on/dist/pages/proxy-acl/proxy-acl.js -o add-on/dist/pages/proxy-access-dialog/proxy-access-dialog.js ] -o add-on/dist/ipfs-companion-common.js", "build:content-scripts": "run-p build:content-scripts:*", "build:content-scripts:ipfs-proxy": "run-s build:content-scripts:ipfs-proxy:*", "build:content-scripts:ipfs-proxy:page": "browserify -p prundupify -g uglifyify -t [ browserify-package-json --global ] add-on/src/contentScripts/ipfs-proxy/page.js -o add-on/dist/contentScripts/ipfs-proxy/page.js", @@ -24,9 +29,9 @@ "build:minimize-dist": "shx rm -rf add-on/dist/lib", "build:bundle-extension": "web-ext build -s add-on/ -i src/ -a build/", "watch": "npm-run-all build:copy --parallel watch:*", - "watch:js": "watchify -p prundupify -t browserify-css -t [ browserify-package-json --global ] add-on/src/background/background.js add-on/src/options/options.js add-on/src/popup/browser-action/index.js add-on/src/popup/quick-upload.js add-on/src/pages/proxy-acl/index.js -p [ factor-bundle -o add-on/dist/background/background.js -o add-on/dist/options/options.js -o add-on/dist/popup/browser-action/browser-action.js -o add-on/dist/popup/quick-upload.js -o add-on/dist/pages/proxy-acl/proxy-acl.js ] -o add-on/dist/ipfs-companion-common.js -v", + "watch:js": "watchify -p prundupify -t [ browserify-package-json --global ] add-on/src/background/background.js add-on/src/options/options.js add-on/src/popup/browser-action/index.js add-on/src/popup/quick-upload.js add-on/src/pages/proxy-acl/index.js add-on/src/pages/proxy-access-dialog/index.js -p [ factor-bundle -o add-on/dist/background/background.js -o add-on/dist/options/options.js -o add-on/dist/popup/browser-action/browser-action.js -o add-on/dist/popup/quick-upload.js -o add-on/dist/pages/proxy-acl/proxy-acl.js -o add-on/dist/pages/proxy-access-dialog/proxy-access-dialog.js ] -o add-on/dist/ipfs-companion-common.js -v", "watch:content-scripts": "run-p watch:content-scripts:*", - "watch:content-scripts:ipfs-proxy": "run-s watch:content-scripts:ipfs-proxy:*", + "watch:content-scripts:ipfs-proxy": "run-p watch:content-scripts:ipfs-proxy:*", "watch:content-scripts:ipfs-proxy:page": "watchify -p prundupify -t [ browserify-package-json --global ] add-on/src/contentScripts/ipfs-proxy/page.js -o add-on/dist/contentScripts/ipfs-proxy/page.js -v", "watch:content-scripts:ipfs-proxy:content": "nodemon --exec \"browserify -p prundupify -t brfs -t [ browserify-package-json --global ] -s IpfsProxyContent add-on/src/contentScripts/ipfs-proxy/content.js -o add-on/dist/contentScripts/ipfs-proxy/content.js -v\" --watch add-on/src/contentScripts/ipfs-proxy/content.js --watch add-on/dist/contentScripts/ipfs-proxy/page.js", "test": "run-s test:*", @@ -52,7 +57,6 @@ "babelify": "8.0.0", "brfs": "1.4.4", "browserify": "14.5.0", - "browserify-css": "0.14.0", "browserify-package-json": "1.0.1", "chai": "4.1.2", "cross-env": "5.1.3", @@ -72,7 +76,7 @@ "sinon": "4.1.2", "sinon-chrome": "2.2.1", "standard": "10.0.3", - "uglifyify": "^4.0.5", + "uglifyify": "4.0.5", "watchify": "3.9.0", "web-ext": "2.3.2" }, @@ -82,6 +86,7 @@ "file-type": "7.3.0", "ipfs": "0.27.6", "ipfs-api": "17.2.7", + "ipfs-css": "0.1.0", "ipfs-postmsg-proxy": "2.7.0", "is-ipfs": "0.3.2", "is-svg": "2.1.0", diff --git a/test/functional/lib/ipfs-companion.test.js b/test/functional/lib/ipfs-companion.test.js index 558480063..e88897cfa 100644 --- a/test/functional/lib/ipfs-companion.test.js +++ b/test/functional/lib/ipfs-companion.test.js @@ -11,6 +11,7 @@ describe('init', function () { global.window = {} global.browser = browser global.URL = URL + global.screen = {} init = require('../../../add-on/src/lib/ipfs-companion') }) @@ -74,6 +75,7 @@ describe.skip('onStorageChange()', function () { delete global.window delete global.browser delete global.URL + delete global.screen browser.flush() }) }) diff --git a/test/functional/lib/ipfs-proxy/access-control.test.js b/test/functional/lib/ipfs-proxy/access-control.test.js index 6522744ea..3f9aa0c52 100644 --- a/test/functional/lib/ipfs-proxy/access-control.test.js +++ b/test/functional/lib/ipfs-proxy/access-control.test.js @@ -46,6 +46,105 @@ describe('lib/ipfs-proxy/access-control', () => { expect(acl).to.deep.equal(expectedAcl) }) + it('should allow access for wildcard allow', async () => { + const accessControl = new AccessControl(new Storage()) + let access = await accessControl.getAccess('https://ipfs.io', 'files.add') + + expect(access).to.equal(null) + + // Add wildcard + await accessControl.setAccess('https://ipfs.io', '*', true) + + access = await accessControl.getAccess('https://ipfs.io', 'files.add') + + const expectedAccess = { origin: 'https://ipfs.io', permission: 'files.add', allow: true } + + expect(access).to.deep.equal(expectedAccess) + }) + + it('should deny access for wildcard deny', async () => { + const accessControl = new AccessControl(new Storage()) + let access = await accessControl.getAccess('https://ipfs.io', 'files.add') + + expect(access).to.equal(null) + + // Add wildcard + await accessControl.setAccess('https://ipfs.io', '*', false) + + access = await accessControl.getAccess('https://ipfs.io', 'files.add') + + const expectedAccess = { origin: 'https://ipfs.io', permission: 'files.add', allow: false } + + expect(access).to.deep.equal(expectedAccess) + }) + + it('should clear existing grants when setting wildcard access', async () => { + const accessControl = new AccessControl(new Storage()) + + await accessControl.setAccess('https://ipfs.io', 'files.add', false) + await accessControl.setAccess('https://ipfs.io', 'object.new', true) + await accessControl.setAccess('https://ipfs.io', 'config.set', false) + + let acl = await accessControl.getAcl() + + let expectedAcl = objToAcl({ + 'https://ipfs.io': { + 'files.add': false, + 'object.new': true, + 'config.set': false + } + }) + + expect(acl).to.deep.equal(expectedAcl) + + // Add wildcard + await accessControl.setAccess('https://ipfs.io', '*', false) + + acl = await accessControl.getAcl() + + expectedAcl = objToAcl({ + 'https://ipfs.io': { + '*': false + } + }) + + expect(acl).to.deep.equal(expectedAcl) + }) + + it('should not be able to set different access to specific permission if wildcard access grant exists', async () => { + const accessControl = new AccessControl(new Storage()) + + // Add wildcard + await accessControl.setAccess('https://ipfs.io', '*', false) + + let error + + try { + await accessControl.setAccess('https://ipfs.io', 'files.add', true) + } catch (err) { + error = err + } + + expect(() => { if (error) throw error }).to.throw('Illegal set access for files.add when wildcard exists') + }) + + it('should be able set same access to specific permission if wildcard access grant exists', async () => { + const accessControl = new AccessControl(new Storage()) + + // Add wildcard + await accessControl.setAccess('https://ipfs.io', '*', false) + + let error + + try { + await accessControl.setAccess('https://ipfs.io', 'files.add', false) + } catch (err) { + error = err + } + + expect(() => { if (error) throw error }).to.not.throw() + }) + it('should get granted access for origin and permission', async () => { const accessControl = new AccessControl(new Storage()) diff --git a/test/functional/pages/proxy-access-dialog/page.test.js b/test/functional/pages/proxy-access-dialog/page.test.js new file mode 100644 index 000000000..e2ddace9e --- /dev/null +++ b/test/functional/pages/proxy-access-dialog/page.test.js @@ -0,0 +1,20 @@ +'use strict' +const { describe, it } = require('mocha') +const { expect } = require('chai') +const createProxyAccessDialogPage = require('../../../../add-on/src/pages/proxy-access-dialog/page') +const createMockI18n = require('../../../helpers/mock-i18n') + +describe('pages/proxy-access-dialog/page', () => { + it('should display title, wildcard checkbox and allow/deny buttons', async () => { + const i18n = createMockI18n() + const state = { origin: 'http://ipfs.io', permission: 'files.add' } + + let res + + expect(() => { res = createProxyAccessDialogPage(i18n)(state).toString() }).to.not.throw() + expect(res).to.have.string(`page_proxyAccessDialog_title[${state.origin},${state.permission}]`) + expect(res).to.have.string(`page_proxyAccessDialog_wildcardCheckbox_label[${state.origin}]`) + expect(res).to.have.string(`page_proxyAccessDialog_denyButton_text`) + expect(res).to.have.string(`page_proxyAccessDialog_allowButton_text`) + }) +}) diff --git a/test/functional/pages/proxy-acl/page.test.js b/test/functional/pages/proxy-acl/page.test.js index 7ac519032..6ab913ee4 100644 --- a/test/functional/pages/proxy-acl/page.test.js +++ b/test/functional/pages/proxy-acl/page.test.js @@ -3,6 +3,7 @@ const { describe, it } = require('mocha') const { expect } = require('chai') const createProxyAclPage = require('../../../../add-on/src/pages/proxy-acl/page') const { objToAcl } = require('../../../helpers/acl') +const createMockI18n = require('../../../helpers/mock-i18n') describe('pages/proxy-acl/page', () => { it('should render with empty ACL', async () => { @@ -102,5 +103,3 @@ describe('pages/proxy-acl/page', () => { expect(res).to.have.string('ipfs.block.put') }) }) - -const createMockI18n = () => ({ getMessage: key => key }) diff --git a/test/functional/pages/proxy-acl/store.test.js b/test/functional/pages/proxy-acl/store.test.js index 0b1ccd846..4cb7e5c11 100644 --- a/test/functional/pages/proxy-acl/store.test.js +++ b/test/functional/pages/proxy-acl/store.test.js @@ -8,6 +8,7 @@ const { URL } = require('url') const AccessControl = require('../../../../add-on/src/lib/ipfs-proxy/access-control') const createProxyAclStore = require('../../../../add-on/src/pages/proxy-acl/store') const { objToAcl } = require('../../../helpers/acl') +const createMockI18n = require('../../../helpers/mock-i18n') describe('pages/proxy-acl/store', () => { before(() => { @@ -316,8 +317,6 @@ describe('pages/proxy-acl/store', () => { }) }) -const createMockI18n = () => ({ getMessage: key => key }) - const createMockConfirm = (res = true) => () => res const createMockEvent = (targetAttributes = {}) => { diff --git a/test/helpers/mock-i18n.js b/test/helpers/mock-i18n.js new file mode 100644 index 000000000..b7fad1351 --- /dev/null +++ b/test/helpers/mock-i18n.js @@ -0,0 +1,8 @@ +module.exports = () => ({ + getMessage (key, substititions) { + if (!substititions) return key + substititions = Array.isArray(substititions) ? substititions : [substititions] + if (!substititions.length) return key + return `${key}[${substititions}]` + } +}) diff --git a/yarn.lock b/yarn.lock index 307cc2081..4ced0246b 100644 --- a/yarn.lock +++ b/yarn.lock @@ -472,10 +472,6 @@ atob@^2.0.0: version "2.0.3" resolved "https://registry.yarnpkg.com/atob/-/atob-2.0.3.tgz#19c7a760473774468f20b2d2d03372ad7d4cbf5d" -atob@~1.1.0: - version "1.1.3" - resolved "https://registry.yarnpkg.com/atob/-/atob-1.1.3.tgz#95f13629b12c3a51a5d215abdce2aa9f32f80773" - aws-sign2@~0.6.0: version "0.6.0" resolved "https://registry.yarnpkg.com/aws-sign2/-/aws-sign2-0.6.0.tgz#14342dd38dbcc94d0e5b87d763cd63612c0e794f" @@ -1293,19 +1289,6 @@ browserify-cipher@^1.0.0: browserify-des "^1.0.0" evp_bytestokey "^1.0.0" -browserify-css@0.14.0: - version "0.14.0" - resolved "https://registry.yarnpkg.com/browserify-css/-/browserify-css-0.14.0.tgz#5ece581aa6f8c9aab262956fd06d57c526c9a334" - dependencies: - clean-css "^4.1.5" - concat-stream "^1.6.0" - css "^2.2.1" - find-node-modules "^1.0.4" - lodash "^4.17.4" - mime "^1.3.6" - strip-css-comments "^3.0.0" - through2 "2.0.x" - browserify-des@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/browserify-des/-/browserify-des-1.0.0.tgz#daa277717470922ed2fe18594118a175439721dd" @@ -1737,12 +1720,6 @@ class-utils@^0.3.5: isobject "^3.0.0" static-extend "^0.1.1" -clean-css@^4.1.5: - version "4.1.9" - resolved "https://registry.yarnpkg.com/clean-css/-/clean-css-4.1.9.tgz#35cee8ae7687a49b98034f70de00c4edd3826301" - dependencies: - source-map "0.5.x" - cli-boxes@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/cli-boxes/-/cli-boxes-1.0.0.tgz#4fa917c3e59c94a004cd61f8ee509da651687143" @@ -2107,15 +2084,6 @@ css-what@2.1: version "2.1.0" resolved "https://registry.yarnpkg.com/css-what/-/css-what-2.1.0.tgz#9467d032c38cfaefb9f2d79501253062f87fa1bd" -css@^2.2.1: - version "2.2.1" - resolved "https://registry.yarnpkg.com/css/-/css-2.2.1.tgz#73a4c81de85db664d4ee674f7d47085e3b2d55dc" - dependencies: - inherits "^2.0.1" - source-map "^0.1.38" - source-map-resolve "^0.3.0" - urix "^0.1.0" - d@1: version "1.0.0" resolved "https://registry.yarnpkg.com/d/-/d-1.0.0.tgz#754bb5bfe55451da69a58b94d45f4c5b0462d58f" @@ -2340,12 +2308,6 @@ des.js@^1.0.0: inherits "^2.0.1" minimalistic-assert "^1.0.0" -detect-file@^0.1.0: - version "0.1.0" - resolved "https://registry.yarnpkg.com/detect-file/-/detect-file-0.1.0.tgz#4935dedfd9488648e006b0129566e9386711ea63" - dependencies: - fs-exists-sync "^0.1.0" - detect-indent@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/detect-indent/-/detect-indent-4.0.0.tgz#f76d064352cdf43a1cb6ce619c4ee3a9475de208" @@ -3105,12 +3067,6 @@ expand-template@^1.0.2: version "1.1.0" resolved "https://registry.yarnpkg.com/expand-template/-/expand-template-1.1.0.tgz#e09efba977bf98f9ee0ed25abd0c692e02aec3fc" -expand-tilde@^1.2.2: - version "1.2.2" - resolved "https://registry.yarnpkg.com/expand-tilde/-/expand-tilde-1.2.2.tgz#0b81eba897e5a3d31d1c3d102f8f01441e559449" - dependencies: - os-homedir "^1.0.1" - extend-shallow@^2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/extend-shallow/-/extend-shallow-2.0.1.tgz#51af7d614ad9a9f610ea1bafbb989d6b1c56890f" @@ -3294,13 +3250,6 @@ find-cache-dir@^0.1.1: mkdirp "^0.5.1" pkg-dir "^1.0.0" -find-node-modules@^1.0.4: - version "1.0.4" - resolved "https://registry.yarnpkg.com/find-node-modules/-/find-node-modules-1.0.4.tgz#b6deb3cccb699c87037677bcede2c5f5862b2550" - dependencies: - findup-sync "0.4.2" - merge "^1.2.0" - find-process@^1.0.5: version "1.1.0" resolved "https://registry.yarnpkg.com/find-process/-/find-process-1.1.0.tgz#f21fa08220fec972b471d92ae3cf0c62bebcd5bb" @@ -3326,15 +3275,6 @@ find-up@^2.0.0, find-up@^2.1.0: dependencies: locate-path "^2.0.0" -findup-sync@0.4.2: - version "0.4.2" - resolved "https://registry.yarnpkg.com/findup-sync/-/findup-sync-0.4.2.tgz#a8117d0f73124f5a4546839579fe52d7129fb5e5" - dependencies: - detect-file "^0.1.0" - is-glob "^2.0.1" - micromatch "^2.3.7" - resolve-dir "^0.1.0" - firefox-client@^0.3.0: version "0.3.0" resolved "https://registry.yarnpkg.com/firefox-client/-/firefox-client-0.3.0.tgz#3794460f6eb6afcf41376addcbc7462e24a4cd8b" @@ -3442,10 +3382,6 @@ from@~0: version "0.1.7" resolved "https://registry.yarnpkg.com/from/-/from-0.1.7.tgz#83c60afc58b9c56997007ed1a768b3ab303a44fe" -fs-exists-sync@^0.1.0: - version "0.1.0" - resolved "https://registry.yarnpkg.com/fs-exists-sync/-/fs-exists-sync-0.1.0.tgz#982d6893af918e72d08dec9e8673ff2b5a8d6add" - "fs-ext@github:baudehlo/node-fs-ext#master": version "1.0.0" resolved "https://codeload.github.com/baudehlo/node-fs-ext/tar.gz/500be8514729c194ac7ca2b30b5bc7eaf812670d" @@ -3702,22 +3638,6 @@ global-dirs@^0.1.0: dependencies: ini "^1.3.4" -global-modules@^0.2.3: - version "0.2.3" - resolved "https://registry.yarnpkg.com/global-modules/-/global-modules-0.2.3.tgz#ea5a3bed42c6d6ce995a4f8a1269b5dae223828d" - dependencies: - global-prefix "^0.1.4" - is-windows "^0.2.0" - -global-prefix@^0.1.4: - version "0.1.5" - resolved "https://registry.yarnpkg.com/global-prefix/-/global-prefix-0.1.5.tgz#8d3bc6b8da3ca8112a160d8d496ff0462bfef78f" - dependencies: - homedir-polyfill "^1.0.0" - ini "^1.3.4" - is-windows "^0.2.0" - which "^1.2.12" - globals@^11.0.1: version "11.3.0" resolved "https://registry.yarnpkg.com/globals/-/globals-11.3.0.tgz#e04fdb7b9796d8adac9c8f64c14837b2313378b0" @@ -4016,12 +3936,6 @@ home-or-tmp@^2.0.0: os-homedir "^1.0.0" os-tmpdir "^1.0.1" -homedir-polyfill@^1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/homedir-polyfill/-/homedir-polyfill-1.0.1.tgz#4c2bbc8a758998feebf5ed68580f76d46768b4bc" - dependencies: - parse-passwd "^1.0.0" - hosted-git-info@^2.1.4: version "2.5.0" resolved "https://registry.yarnpkg.com/hosted-git-info/-/hosted-git-info-2.5.0.tgz#6d60e34b3abbc8313062c3b798ef8d901a07af3c" @@ -4374,6 +4288,10 @@ ipfs-block@^0.6.1, ipfs-block@~0.6.1: dependencies: cids "^0.5.2" +ipfs-css@0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/ipfs-css/-/ipfs-css-0.1.0.tgz#0337e7e3aeddf391bbd1b7b7fb427f98b6ec5ca3" + ipfs-multipart@~0.1.0: version "0.1.0" resolved "https://registry.yarnpkg.com/ipfs-multipart/-/ipfs-multipart-0.1.0.tgz#5a8ed13f42e82d8bef7d2e151d8eaf5e3a30e3ea" @@ -4920,10 +4838,6 @@ is-regex@^1.0.4: dependencies: has "^1.0.1" -is-regexp@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/is-regexp/-/is-regexp-1.0.0.tgz#fd2d883545c46bac5a633e7b9a09e87fa2cb5069" - is-relative@^0.1.0: version "0.1.3" resolved "https://registry.yarnpkg.com/is-relative/-/is-relative-0.1.3.tgz#905fee8ae86f45b3ec614bc3c15c869df0876e82" @@ -4958,10 +4872,6 @@ is-utf8@^0.2.0, is-utf8@^0.2.1: version "0.2.1" resolved "https://registry.yarnpkg.com/is-utf8/-/is-utf8-0.2.1.tgz#4b0da1442104d1b336340e80797e865cf39f7d72" -is-windows@^0.2.0: - version "0.2.0" - resolved "https://registry.yarnpkg.com/is-windows/-/is-windows-0.2.0.tgz#de1aa6d63ea29dd248737b69f1ff8b8002d2108c" - is-windows@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/is-windows/-/is-windows-1.0.1.tgz#310db70f742d259a16a369202b51af84233310d9" @@ -6112,10 +6022,6 @@ merge-source-map@^1.0.2: dependencies: source-map "^0.6.1" -merge@^1.2.0: - version "1.2.0" - resolved "https://registry.yarnpkg.com/merge/-/merge-1.2.0.tgz#7531e39d4949c281a66b8c5a6e0265e8b05894da" - merkle-patricia-tree@^2.1.2, merkle-patricia-tree@^2.2.0: version "2.3.0" resolved "https://registry.yarnpkg.com/merkle-patricia-tree/-/merkle-patricia-tree-2.3.0.tgz#84c606232ef343f1b96fc972e697708754f08573" @@ -6129,7 +6035,7 @@ merkle-patricia-tree@^2.1.2, merkle-patricia-tree@^2.2.0: rlp "^2.0.0" semaphore ">=1.0.1" -micromatch@^2.1.5, micromatch@^2.3.11, micromatch@^2.3.7: +micromatch@^2.1.5, micromatch@^2.3.11: version "2.3.11" resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-2.3.11.tgz#86677c97d1720b363431d04d0d15293bd38c1565" dependencies: @@ -6186,10 +6092,6 @@ mime-types@2.1.17, mime-types@^2.1.12, mime-types@^2.1.17, mime-types@~2.1.11, m dependencies: mime-db "~1.30.0" -mime@^1.3.6: - version "1.6.0" - resolved "https://registry.yarnpkg.com/mime/-/mime-1.6.0.tgz#32cd9e5c64553bd58d19a568af452acff04981b1" - mimic-fn@^1.0.0: version "1.2.0" resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-1.2.0.tgz#820c86a39334640e99516928bd03fca88057d022" @@ -7001,10 +6903,6 @@ parse-json@^2.1.0, parse-json@^2.2.0: dependencies: error-ex "^1.2.0" -parse-passwd@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/parse-passwd/-/parse-passwd-1.0.0.tgz#6d5b934a456993b23d37f40a382d6f1666a8e5c6" - parse5@^3.0.1: version "3.0.3" resolved "https://registry.yarnpkg.com/parse5/-/parse5-3.0.3.tgz#042f792ffdd36851551cf4e9e066b3874ab45b5c" @@ -8001,13 +7899,6 @@ require-uncached@1.0.3, require-uncached@^1.0.2, require-uncached@^1.0.3: caller-path "^0.1.0" resolve-from "^1.0.0" -resolve-dir@^0.1.0: - version "0.1.1" - resolved "https://registry.yarnpkg.com/resolve-dir/-/resolve-dir-0.1.1.tgz#b219259a5602fac5c5c496ad894a6e8cc430261e" - dependencies: - expand-tilde "^1.2.2" - global-modules "^0.2.3" - resolve-from@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-1.0.1.tgz#26cbfe935d1aeeeabb29bc3fe5aeb01e93d44226" @@ -8016,7 +7907,7 @@ resolve-from@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-2.0.0.tgz#9480ab20e94ffa1d9e80a804c7ea147611966b57" -resolve-url@^0.2.1, resolve-url@~0.2.1: +resolve-url@^0.2.1: version "0.2.1" resolved "https://registry.yarnpkg.com/resolve-url/-/resolve-url-0.2.1.tgz#2c637fe77c893afd2a663fe21aa9080068e2052a" @@ -8492,15 +8383,6 @@ socket.io@^2.0.4: socket.io-client "2.0.4" socket.io-parser "~3.1.1" -source-map-resolve@^0.3.0: - version "0.3.1" - resolved "https://registry.yarnpkg.com/source-map-resolve/-/source-map-resolve-0.3.1.tgz#610f6122a445b8dd51535a2a71b783dfc1248761" - dependencies: - atob "~1.1.0" - resolve-url "~0.2.1" - source-map-url "~0.3.0" - urix "~0.1.0" - source-map-resolve@^0.5.0: version "0.5.1" resolved "https://registry.yarnpkg.com/source-map-resolve/-/source-map-resolve-0.5.1.tgz#7ad0f593f2281598e854df80f19aae4b92d7a11a" @@ -8545,26 +8427,16 @@ source-map-url@^0.4.0: version "0.4.0" resolved "https://registry.yarnpkg.com/source-map-url/-/source-map-url-0.4.0.tgz#3e935d7ddd73631b97659956d55128e87b5084a3" -source-map-url@~0.3.0: - version "0.3.0" - resolved "https://registry.yarnpkg.com/source-map-url/-/source-map-url-0.3.0.tgz#7ecaf13b57bcd09da8a40c5d269db33799d4aaf9" - -source-map@0.5.x, source-map@^0.5.3, source-map@^0.5.6, source-map@^0.5.7, source-map@~0.5.1, source-map@~0.5.3, source-map@~0.5.6: - version "0.5.7" - resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.5.7.tgz#8a039d2d1021d22d1ea14c80d8ea468ba2ef3fcc" - -source-map@^0.1.38: - version "0.1.43" - resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.1.43.tgz#c24bc146ca517c1471f5dacbe2571b2b7f9e3346" - dependencies: - amdefine ">=0.0.4" - source-map@^0.4.4, source-map@~0.4.0, source-map@~0.4.2: version "0.4.4" resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.4.4.tgz#eba4f5da9c0dc999de68032d8b4f76173652036b" dependencies: amdefine ">=0.0.4" +source-map@^0.5.3, source-map@^0.5.6, source-map@^0.5.7, source-map@~0.5.1, source-map@~0.5.3, source-map@~0.5.6: + version "0.5.7" + resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.5.7.tgz#8a039d2d1021d22d1ea14c80d8ea468ba2ef3fcc" + source-map@^0.6.0, source-map@^0.6.1, source-map@~0.6.1: version "0.6.1" resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.6.1.tgz#74722af32e9614e9c287a8d0bbde48b5e2f1a263" @@ -8891,12 +8763,6 @@ strip-bom@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/strip-bom/-/strip-bom-3.0.0.tgz#2334c18e9c759f7bdd56fdef7e9ae3d588e68ed3" -strip-css-comments@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/strip-css-comments/-/strip-css-comments-3.0.0.tgz#7a5625eff8a2b226cf8947a11254da96e13dae89" - dependencies: - is-regexp "^1.0.0" - strip-eof@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/strip-eof/-/strip-eof-1.0.0.tgz#bb43ff5598a6eb05d89b59fcd129c983313606bf" @@ -9095,13 +8961,6 @@ thenify-all@^1.0.0, thenify-all@^1.6.0: dependencies: any-promise "^1.0.0" -through2@2.0.x, through2@^2.0.0, through2@^2.0.1, through2@^2.0.2, through2@^2.0.3, through2@~2.0.3: - version "2.0.3" - resolved "https://registry.yarnpkg.com/through2/-/through2-2.0.3.tgz#0004569b37c7c74ba39c43f3ced78d1ad94140be" - dependencies: - readable-stream "^2.1.5" - xtend "~4.0.1" - through2@^0.5.1: version "0.5.1" resolved "https://registry.yarnpkg.com/through2/-/through2-0.5.1.tgz#dfdd012eb9c700e2323fd334f38ac622ab372da7" @@ -9116,6 +8975,13 @@ through2@^1.0.0: readable-stream ">=1.1.13-1 <1.2.0-0" xtend ">=4.0.0 <4.1.0-0" +through2@^2.0.0, through2@^2.0.1, through2@^2.0.2, through2@^2.0.3, through2@~2.0.3: + version "2.0.3" + resolved "https://registry.yarnpkg.com/through2/-/through2-2.0.3.tgz#0004569b37c7c74ba39c43f3ced78d1ad94140be" + dependencies: + readable-stream "^2.1.5" + xtend "~4.0.1" + through@2, "through@>=2.2.7 <3", through@^2.3.6, through@~2.3, through@~2.3.1, through@~2.3.4: version "2.3.8" resolved "https://registry.yarnpkg.com/through/-/through-2.3.8.tgz#0dd4c9ffaabc357960b1b724115d7e0e86a2e1f5" @@ -9311,7 +9177,7 @@ uglify-to-browserify@~1.0.0: version "1.0.2" resolved "https://registry.yarnpkg.com/uglify-to-browserify/-/uglify-to-browserify-1.0.2.tgz#6e0924d6bda6b5afe349e39a6d632850a0f882b7" -uglifyify@^4.0.5: +uglifyify@4.0.5: version "4.0.5" resolved "https://registry.yarnpkg.com/uglifyify/-/uglifyify-4.0.5.tgz#49c1fca9828c10a5a8e8d70f191a95f7ab475911" dependencies: @@ -9454,7 +9320,7 @@ urijs@^1.18.2: version "1.19.0" resolved "https://registry.yarnpkg.com/urijs/-/urijs-1.19.0.tgz#d8aa284d0e7469703a6988ad045c4cbfdf08ada0" -urix@^0.1.0, urix@~0.1.0: +urix@^0.1.0: version "0.1.0" resolved "https://registry.yarnpkg.com/urix/-/urix-0.1.0.tgz#da937f7a62e21fec1fd18d49b35c2935067a6c72" @@ -9663,7 +9529,7 @@ which@1.2.4: is-absolute "^0.1.7" isexe "^1.1.1" -which@^1.2.12, which@^1.2.9, which@^1.3.0: +which@^1.2.9, which@^1.3.0: version "1.3.0" resolved "https://registry.yarnpkg.com/which/-/which-1.3.0.tgz#ff04bdfc010ee547d780bec38e1ac1c2777d253a" dependencies: