Implement new security manager methods

This commit is contained in:
GarboMuffin committed 2023-05-27 18:12:29 -05:00
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}>
+78 -21
View File
@@ -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
};
+5 -1
View File
@@ -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;