Implement new security manager methods
This commit is contained in:
1 parent
0154858cfb
commit
32aae68907
10 files changed
+211
-31
No files matched your search
@@ -7,7 +7,7 @@ const FetchModal = props => (
|
||||
<div>
|
||||
<FormattedMessage
|
||||
defaultMessage="The project wants to connect to the website:"
|
||||
description="Part of modal when a project asks permission to fetch a URL"
|
||||
description="Part of modal shown when a project asks for permission to fetch a URL using an extension"
|
||||
id="tw.fetch.title"
|
||||
/>
|
||||
<URL url={props.url} />
|
||||
@@ -15,15 +15,14 @@ const FetchModal = props => (
|
||||
<FormattedMessage
|
||||
// eslint-disable-next-line max-len
|
||||
defaultMessage="This could be used to download images or sounds, implement multiplayer, access an API, or for malicious purposes. This will share your IP address, general location, and possibly other data with the website."
|
||||
description="Part of modal shown when a project asks permission to fetch a URL"
|
||||
description="Part of modal shown when a project asks for permission to fetch a URL using an extension"
|
||||
id="tw.securityManager.why"
|
||||
/>
|
||||
</p>
|
||||
<p>
|
||||
<FormattedMessage
|
||||
// eslint-disable-next-line max-len
|
||||
defaultMessage="If allowed, further requests to the same website will be automatically allowed."
|
||||
description="Part of modal shown when asking for permission to automatically load custom extension"
|
||||
description="Part of modal shown when a project asks for permission to fetch a URL using an extension"
|
||||
id="tw.securityManager.trust"
|
||||
/>
|
||||
</p>
|
||||
|
||||
@@ -0,0 +1,26 @@
|
||||
import React from 'react';
|
||||
import {FormattedMessage} from 'react-intl';
|
||||
|
||||
const Notify = () => (
|
||||
<div>
|
||||
<p>
|
||||
<FormattedMessage
|
||||
defaultMessage="The project wants to display notifications."
|
||||
// eslint-disable-next-line max-len
|
||||
description="Part of modal that appears when a project tries to display notifications using an extension"
|
||||
id="tw.notify.title"
|
||||
/>
|
||||
</p>
|
||||
<p>
|
||||
<FormattedMessage
|
||||
// eslint-disable-next-line max-len
|
||||
defaultMessage="If allowed, you may be prompted to enable notifications by your browser, and further notifications will be automatically allowed."
|
||||
// eslint-disable-next-line max-len
|
||||
description="Part of modal that appears when a project tries to display notifications using an extension"
|
||||
id="tw.notify.permission"
|
||||
/>
|
||||
</p>
|
||||
</div>
|
||||
);
|
||||
|
||||
export default Notify;
|
||||
@@ -8,7 +8,7 @@ const OpenWindowModal = props => (
|
||||
<div>
|
||||
<FormattedMessage
|
||||
defaultMessage="The project wants to open a new window or tab with the URL:"
|
||||
description="Part of modal when a project attempts to open a window"
|
||||
description="Part of modal when a project attempts to open a window using an extension"
|
||||
id="tw.openWindow.title"
|
||||
/>
|
||||
<URL url={props.url} />
|
||||
@@ -16,7 +16,7 @@ const OpenWindowModal = props => (
|
||||
<FormattedMessage
|
||||
// eslint-disable-next-line max-len
|
||||
defaultMessage="This website has not been reviewed by the {APP_NAME} developers. It may contain dangerous or malicious code."
|
||||
description="Part of modal when a project attempts to open a window"
|
||||
description="Part of modal when a project attempts to open a window using an extension"
|
||||
id="tw.openWindow.dangerous"
|
||||
values={{
|
||||
APP_NAME
|
||||
|
||||
@@ -0,0 +1,33 @@
|
||||
import React from 'react';
|
||||
import {FormattedMessage} from 'react-intl';
|
||||
|
||||
const ReadClipboard = () => (
|
||||
<div>
|
||||
<p>
|
||||
<FormattedMessage
|
||||
defaultMessage="The project wants to read data from your clipboard."
|
||||
description="Part of modal that appears when a project tries to access the clipboard using an extension"
|
||||
id="tw.clipboard.title"
|
||||
/>
|
||||
</p>
|
||||
<p>
|
||||
<FormattedMessage
|
||||
// eslint-disable-next-line max-len
|
||||
defaultMessage="If your clipboard contains things like passwords, the project may be able to share those with other users or servers."
|
||||
description="Part of modal that appears when a project tries to access the clipboard using an extension"
|
||||
id="tw.clipboard.danger"
|
||||
/>
|
||||
</p>
|
||||
<p>
|
||||
<FormattedMessage
|
||||
// eslint-disable-next-line max-len
|
||||
defaultMessage="Clipboard access may not work in some browsers. If allowed, further clipboard reads will be automatically allowed."
|
||||
// eslint-disable-next-line max-len
|
||||
description="Part of modal that appears when a project tries to access the clipboard using an extension"
|
||||
id="tw.clipboard.permission"
|
||||
/>
|
||||
</p>
|
||||
</div>
|
||||
);
|
||||
|
||||
export default ReadClipboard;
|
||||
@@ -0,0 +1,25 @@
|
||||
import React from 'react';
|
||||
import {FormattedMessage} from 'react-intl';
|
||||
|
||||
const RecordAudio = () => (
|
||||
<div>
|
||||
<p>
|
||||
<FormattedMessage
|
||||
// eslint-disable-next-line max-len
|
||||
defaultMessage="The project wants to to record audio from your microphone. This could include a text transcript or raw audio data. The project may be able to share audio with other users or servers."
|
||||
description="Part of modal that appears when a project tries to record audio using an extension"
|
||||
id="tw.recordAudio.title"
|
||||
/>
|
||||
</p>
|
||||
<p>
|
||||
<FormattedMessage
|
||||
// eslint-disable-next-line max-len
|
||||
defaultMessage="If allowed, you may be prompted to enable microphone access by your browser, and further microphone access will be automatically allowed."
|
||||
description="Part of modal that appears when a project tries to record audio using an extension"
|
||||
id="tw.recordAudio.permission"
|
||||
/>
|
||||
</p>
|
||||
</div>
|
||||
);
|
||||
|
||||
export default RecordAudio;
|
||||
@@ -0,0 +1,25 @@
|
||||
import React from 'react';
|
||||
import {FormattedMessage} from 'react-intl';
|
||||
|
||||
const RecordVideo = () => (
|
||||
<div>
|
||||
<p>
|
||||
<FormattedMessage
|
||||
// eslint-disable-next-line max-len
|
||||
defaultMessage="The project wants to record video from your camera. The project may be able to share images with other users or servers."
|
||||
description="Part of modal that appears when a project tries to record video using an extension"
|
||||
id="tw.recordVideo.title"
|
||||
/>
|
||||
</p>
|
||||
<p>
|
||||
<FormattedMessage
|
||||
// eslint-disable-next-line max-len
|
||||
defaultMessage="If allowed, you may be prompted to enable camera access by your browser, and further camera access will be automatically allowed."
|
||||
description="Part of modal that appears when a project tries to record audio using an extension"
|
||||
id="tw.recordVideo.permission"
|
||||
/>
|
||||
</p>
|
||||
</div>
|
||||
);
|
||||
|
||||
export default RecordVideo;
|
||||
@@ -8,7 +8,7 @@ const RedirectModal = props => (
|
||||
<div>
|
||||
<FormattedMessage
|
||||
defaultMessage="The project wants to navigate this tab to the URL:"
|
||||
description="Part of modal when a project attempts to navigate the current tab"
|
||||
description="Part of modal when a project attempts to navigate the current tab using an extension"
|
||||
id="tw.redirect.title"
|
||||
/>
|
||||
<URL url={props.url} />
|
||||
@@ -16,7 +16,7 @@ const RedirectModal = props => (
|
||||
<FormattedMessage
|
||||
// eslint-disable-next-line max-len
|
||||
defaultMessage="This website has not been reviewed by the {APP_NAME} developers. It may contain dangerous or malicious code."
|
||||
description="Part of modal when a project attempts to navigate the current tab"
|
||||
description="Part of modal when a project attempts to navigate the current tab using an extension"
|
||||
id="tw.redirect.dangerous"
|
||||
values={{
|
||||
APP_NAME
|
||||
|
||||
@@ -8,8 +8,11 @@ import LoadExtensionModal from './load-extension.jsx';
|
||||
import FetchModal from './fetch.jsx';
|
||||
import OpenWindowModal from './open-window.jsx';
|
||||
import RedirectModal from './redirect.jsx';
|
||||
import RecordAudio from './record-audio.jsx';
|
||||
import RecordVideo from './record-video.jsx';
|
||||
import ReadClipboard from './read-clipboard.jsx';
|
||||
import Notify from './notify.jsx';
|
||||
import DelayedMountPropertyHOC from './delayed-mount-property-hoc.jsx';
|
||||
|
||||
import styles from './security-manager-modal.css';
|
||||
|
||||
const messages = defineMessages({
|
||||
@@ -39,6 +42,14 @@ const SecurityManagerModalComponent = props => (
|
||||
<OpenWindowModal {...props.data} />
|
||||
) : props.type === SecurityModals.Redirect ? (
|
||||
<RedirectModal {...props.data} />
|
||||
) : props.type === SecurityModals.RecordAudio ? (
|
||||
<RecordAudio {...props.data} />
|
||||
) : props.type === SecurityModals.RecordVideo ? (
|
||||
<RecordVideo {...props.data} />
|
||||
) : props.type === SecurityModals.ReadClipboard ? (
|
||||
<ReadClipboard {...props.data} />
|
||||
) : props.type === SecurityModals.Notify ? (
|
||||
<Notify {...props.data} />
|
||||
) : null}
|
||||
|
||||
<Box className={styles.buttons}>
|
||||
|
||||
@@ -98,34 +98,46 @@ const parseURL = url => {
|
||||
return parsed;
|
||||
};
|
||||
|
||||
let allowedAudio = false;
|
||||
let allowedVideo = false;
|
||||
let allowedReadClipboard = false;
|
||||
let allowedNotify = false;
|
||||
|
||||
const SECURITY_MANAGER_METHODS = [
|
||||
'getSandboxMode',
|
||||
'canLoadExtensionFromProject',
|
||||
'canFetch',
|
||||
'canOpenWindow',
|
||||
'canRedirect',
|
||||
'canRecordAudio',
|
||||
'canRecordVideo',
|
||||
'canReadClipboard',
|
||||
'canNotify'
|
||||
];
|
||||
|
||||
class TWSecurityManagerComponent extends React.Component {
|
||||
constructor (props) {
|
||||
super(props);
|
||||
bindAll(this, [
|
||||
'getSandboxMode',
|
||||
'canLoadExtensionFromProject',
|
||||
'canFetch',
|
||||
'canOpenWindow',
|
||||
'canRedirect',
|
||||
'handleAllowed',
|
||||
'handleDenied'
|
||||
]);
|
||||
bindAll(this, SECURITY_MANAGER_METHODS);
|
||||
this.nextModalCallbacks = [];
|
||||
this.modalLocked = false;
|
||||
this.state = {
|
||||
type: null,
|
||||
data: null,
|
||||
callback: null
|
||||
callback: null,
|
||||
modalCount: 0
|
||||
};
|
||||
}
|
||||
|
||||
componentDidMount () {
|
||||
const securityManager = this.props.vm.extensionManager.securityManager;
|
||||
securityManager.getSandboxMode = this.getSandboxMode;
|
||||
securityManager.canLoadExtensionFromProject = this.canLoadExtensionFromProject;
|
||||
securityManager.canFetch = this.canFetch;
|
||||
securityManager.canOpenWindow = this.canOpenWindow;
|
||||
securityManager.canRedirect = this.canRedirect;
|
||||
for (const method of SECURITY_MANAGER_METHODS) {
|
||||
securityManager[method] = this[method];
|
||||
}
|
||||
}
|
||||
|
||||
// eslint-disable-next-line valid-jsdoc
|
||||
@@ -162,11 +174,12 @@ class TWSecurityManagerComponent extends React.Component {
|
||||
|
||||
const showModal = async (type, data) => {
|
||||
const result = await new Promise(resolve => {
|
||||
this.setState({
|
||||
this.setState(oldState => ({
|
||||
type,
|
||||
data,
|
||||
callback: resolve
|
||||
});
|
||||
callback: resolve,
|
||||
modalCount: oldState.modalCount + 1
|
||||
}));
|
||||
});
|
||||
releaseLock();
|
||||
return result;
|
||||
@@ -291,6 +304,50 @@ class TWSecurityManagerComponent extends React.Component {
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @returns {Promise<boolean>} True if audio can be recorded
|
||||
*/
|
||||
async canRecordAudio () {
|
||||
if (!allowedAudio) {
|
||||
const {showModal} = await this.acquireModalLock();
|
||||
allowedAudio = await showModal(SecurityModals.RecordAudio);
|
||||
}
|
||||
return allowedAudio;
|
||||
}
|
||||
|
||||
/**
|
||||
* @returns {Promise<boolean>} True if video can be recorded
|
||||
*/
|
||||
async canRecordVideo () {
|
||||
if (!allowedVideo) {
|
||||
const {showModal} = await this.acquireModalLock();
|
||||
allowedVideo = await showModal(SecurityModals.RecordVideo);
|
||||
}
|
||||
return allowedVideo;
|
||||
}
|
||||
|
||||
/**
|
||||
* @returns {Promise<boolean>} True if the clipboard can be read
|
||||
*/
|
||||
async canReadClipboard () {
|
||||
if (!allowedReadClipboard) {
|
||||
const {showModal} = await this.acquireModalLock();
|
||||
allowedReadClipboard = await showModal(SecurityModals.ReadClipboard);
|
||||
}
|
||||
return allowedReadClipboard;
|
||||
}
|
||||
|
||||
/**
|
||||
* @returns {Promise<boolean>} True if the notifications are allowed
|
||||
*/
|
||||
async canNotify () {
|
||||
if (!allowedNotify) {
|
||||
const {showModal} = await this.acquireModalLock();
|
||||
allowedNotify = await showModal(SecurityModals.Notify);
|
||||
}
|
||||
return allowedNotify;
|
||||
}
|
||||
|
||||
render () {
|
||||
if (this.state.type) {
|
||||
return (
|
||||
@@ -299,6 +356,7 @@ class TWSecurityManagerComponent extends React.Component {
|
||||
data={this.state.data}
|
||||
onAllowed={this.handleAllowed}
|
||||
onDenied={this.handleDenied}
|
||||
key={this.state.modalCount}
|
||||
/>
|
||||
);
|
||||
}
|
||||
@@ -309,13 +367,12 @@ class TWSecurityManagerComponent extends React.Component {
|
||||
TWSecurityManagerComponent.propTypes = {
|
||||
vm: PropTypes.shape({
|
||||
extensionManager: PropTypes.shape({
|
||||
securityManager: PropTypes.shape({
|
||||
getSandboxMode: PropTypes.func.isRequired,
|
||||
canLoadExtensionFromProject: PropTypes.func.isRequired,
|
||||
canFetch: PropTypes.func.isRequired,
|
||||
canOpenWindow: PropTypes.func.isRequired,
|
||||
canRedirect: PropTypes.func.isRequired
|
||||
}).isRequired
|
||||
securityManager: PropTypes.shape(
|
||||
SECURITY_MANAGER_METHODS.reduce((obj, method) => {
|
||||
obj[method] = PropTypes.func.isRequired;
|
||||
return obj;
|
||||
}, {})
|
||||
).isRequired
|
||||
}).isRequired
|
||||
}).isRequired
|
||||
};
|
||||
|
||||
@@ -2,7 +2,11 @@ const SecurityModals = {
|
||||
LoadExtension: 'LoadExtension',
|
||||
Fetch: 'Fetch',
|
||||
OpenWindow: 'OpenWindow',
|
||||
Redirect: 'Redirect'
|
||||
Redirect: 'Redirect',
|
||||
RecordAudio: 'RecordAudio',
|
||||
RecordVideo: 'RecordVideo',
|
||||
ReadClipboard: 'ReadClipboard',
|
||||
Notify: 'Notify'
|
||||
};
|
||||
|
||||
export default SecurityModals;
|
||||
Reference in new issue
Block a user