feat: safe mode updates

This commit is contained in:
Anoop M D 2024-08-11 16:11:12 +05:30
parent b2baa1e48d
commit 751c7aa16d
12 changed files with 119 additions and 102 deletions

View File

@ -5,11 +5,10 @@ import EnvironmentSelector from 'components/Environments/EnvironmentSelector';
import { addTab } from 'providers/ReduxStore/slices/tabs';
import { useDispatch } from 'react-redux';
import StyledWrapper from './StyledWrapper';
import SecuritySettingsIcon from 'components/SecuritySettings/SecurityIconWithModal/index';
import JsSandboxMode from 'components/SecuritySettings/JsSandboxMode';
const CollectionToolBar = ({ collection }) => {
const dispatch = useDispatch();
const appMode = collection?.securityConfig?.appMode;
const handleRun = () => {
dispatch(
@ -59,16 +58,8 @@ const CollectionToolBar = ({ collection }) => {
<span className="ml-2 mr-4 font-semibold">{collection?.name}</span>
</div>
<div className="flex flex-1 items-center justify-end">
{appMode && (
<span
className={`mr-4 border border-slate-500 px-2 py-1 rounded-md text-xs cursor-pointer opacity-70 ${appMode}`}
onClick={viewSecuritySettings}
>
{appMode} mode
</span>
)}
<span className="mr-2">
<SecuritySettingsIcon collection={collection} />
<JsSandboxMode collection={collection} />
</span>
<span className="mr-2">
<IconRun className="cursor-pointer" size={20} strokeWidth={1.5} onClick={handleRun} />

View File

@ -0,0 +1,16 @@
import styled from 'styled-components';
const StyledWrapper = styled.div`
.safe-mode {
padding: 0.15rem 0.3rem;
color: ${(props) => props.theme.colors.text.green};
border: solid 1px ${(props) => props.theme.colors.text.green} !important;
}
.developer-mode {
padding: 0.15rem 0.3rem;
color: ${(props) => props.theme.colors.text.yellow};
border: solid 1px ${(props) => props.theme.colors.text.yellow} !important;
}
`;
export default StyledWrapper;

View File

@ -0,0 +1,45 @@
import { useDispatch } from 'react-redux';
import { IconShieldLock } from '@tabler/icons';
import { addTab } from 'providers/ReduxStore/slices/tabs';
import { uuid } from 'utils/common/index';
import JsSandboxModeModal from '../JsSandboxModeModal';
import StyledWrapper from './StyledWrapper';
const JsSandboxMode = ({ collection }) => {
const jsSandboxMode = collection?.securityConfig?.jsSandboxMode;
const dispatch = useDispatch();
const viewSecuritySettings = () => {
dispatch(
addTab({
uid: uuid(),
collectionUid: collection.uid,
type: 'security-settings'
})
);
};
return (
<StyledWrapper className='flex'>
{jsSandboxMode === 'safe' && (
<div
className="flex items-center border rounded-md text-xs cursor-pointer safe-mode"
onClick={viewSecuritySettings}
>
Safe Mode
</div>
)}
{jsSandboxMode === 'developer' && (
<div
className="flex items-center border rounded-md text-xs cursor-pointer developer-mode"
onClick={viewSecuritySettings}
>
Developer Mode
</div>
)}
{!jsSandboxMode ? <JsSandboxModeModal collection={collection} /> : null}
</StyledWrapper>
);
};
export default JsSandboxMode;

View File

@ -6,33 +6,32 @@ import Portal from 'components/Portal';
import Modal from 'components/Modal';
import StyledWrapper from './StyledWrapper';
const AppModeModal = ({ collection, onClose }) => {
const JsSandboxModeModal = ({ collection, onClose }) => {
const dispatch = useDispatch();
const [selectedAppMode, setSelectedAppMode] = useState(collection?.securityConfig?.appMode || 'developer');
const [jsSandboxMode, setJsSandboxMode] = useState(collection?.securityConfig?.jsSandboxMode || 'safe');
const handleAppModeChange = (e) => {
setSelectedAppMode(e.target.value);
const handleChange = (e) => {
setJsSandboxMode(e.target.value);
};
const handleSave = () => {
dispatch(
saveCollectionSecurityConfig(collection?.uid, {
appMode: selectedAppMode,
runtime: selectedAppMode === 'developer' ? 'vm2' : selectedAppMode === 'safe' ? 'isolated-vm' : undefined
jsSandboxMode: jsSandboxMode
})
)
.then(() => {
toast.success('App Mode updated successfully');
toast.success('Sandbox mode updated successfully');
onClose();
})
.catch((err) => console.log(err) && toast.error('Failed to update JS AppMode'));
.catch((err) => console.log(err) && toast.error('Failed to update sandbox mode'));
};
return (
<Portal>
<Modal
size="sm"
title={'Scripting Sandbox'}
title={'JavaScript Sandbox'}
confirmText="Save"
handleConfirm={handleSave}
hideCancel={true}
@ -54,13 +53,13 @@ const AppModeModal = ({ collection, onClose }) => {
<input
type="radio"
id="safe"
name="appMode"
name="jsSandboxMode"
value="safe"
checked={selectedAppMode === 'safe'}
onChange={handleAppModeChange}
checked={jsSandboxMode === 'safe'}
onChange={handleChange}
className="cursor-pointer"
/>
<span className={selectedAppMode === 'safe' ? 'font-medium' : 'font-normal'}>
<span className={jsSandboxMode === 'safe' ? 'font-medium' : 'font-normal'}>
Safe Mode
</span>
<span className='beta-tag'>BETA</span>
@ -73,13 +72,13 @@ const AppModeModal = ({ collection, onClose }) => {
<input
type="radio"
id="developer"
name="appMode"
name="jsSandboxMode"
value="developer"
checked={selectedAppMode === 'developer'}
onChange={handleAppModeChange}
checked={jsSandboxMode === 'developer'}
onChange={handleChange}
className="cursor-pointer"
/>
<span className={selectedAppMode === 'developer' ? 'font-medium' : 'font-normal'}>
<span className={jsSandboxMode === 'developer' ? 'font-medium' : 'font-normal'}>
Developer Mode
<span className='ml-1 developer-mode-warning'>(use only if you trust the collections authors)</span>
</span>
@ -97,4 +96,4 @@ const AppModeModal = ({ collection, onClose }) => {
);
};
export default AppModeModal;
export default JsSandboxModeModal;

View File

@ -1,35 +0,0 @@
import { setShowAppModeModal } from 'providers/ReduxStore/slices/collections/index';
import { useDispatch } from 'react-redux';
import { IconShieldLock } from '@tabler/icons';
import { addTab } from 'providers/ReduxStore/slices/tabs';
import { uuid } from 'utils/common/index';
import AppModeModal from './AppModeModal/index';
const SecuritySettingsIcon = ({ collection }) => {
const showAppModeModel = collection?.showAppModeModal;
const dispatch = useDispatch();
const viewSecuritySettings = () => {
dispatch(
addTab({
uid: uuid(),
collectionUid: collection.uid,
type: 'security-settings'
})
);
};
return (
<>
<IconShieldLock className="cursor-pointer" size={20} strokeWidth={1.5} onClick={viewSecuritySettings} />
{showAppModeModel ? (
<AppModeModal
collection={collection}
onClose={() => dispatch(setShowAppModeModal({ showAppModeModal: false, collectionUid: collection?.uid }))}
/>
) : null}
</>
);
};
export default SecuritySettingsIcon;

View File

@ -1,33 +1,32 @@
import { useState } from 'react';
import { saveCollectionSecurityConfig } from 'providers/ReduxStore/slices/collections/actions';
import classnames from 'classnames';
import toast from 'react-hot-toast';
import StyledWrapper from './StyledWrapper';
import { useDispatch } from 'react-redux';
const SecuritySettings = ({ collection }) => {
const dispatch = useDispatch();
const [selectedAppMode, setSelectedAppMode] = useState(collection?.securityConfig?.appMode || 'developer');
const [jsSandboxMode, setJsSandboxMode] = useState(collection?.securityConfig?.jsSandboxMode || 'safe');
const handleAppModeChange = (e) => {
setSelectedAppMode(e.target.value);
const handleChange = (e) => {
setJsSandboxMode(e.target.value);
};
const handleSave = () => {
dispatch(
saveCollectionSecurityConfig(collection?.uid, {
appMode: selectedAppMode,
runtime: selectedAppMode === 'developer' ? 'vm2' : selectedAppMode === 'safe' ? 'isolated-vm' : undefined
jsSandboxMode: jsSandboxMode
})
)
.then(() => {
toast.success('App Mode updated successfully');
toast.success('Sandbox mode updated successfully');
})
.catch((err) => console.log(err) && toast.error('Failed to update JS AppMode'));
.catch((err) => console.log(err) && toast.error('Failed to update sandbox mode'));
};
return (
<StyledWrapper className="flex flex-col h-full relative px-4 py-4">
<div className='font-semibold mt-2'>Scripting Sandbox</div>
<div className='font-semibold mt-2'>JavaScript Sandbox</div>
<div className='mt-4'>
The collection might include JavaScript code in Variables, Scripts, Tests, and Assertions.
@ -39,13 +38,13 @@ const SecuritySettings = ({ collection }) => {
<input
type="radio"
id="safe"
name="appMode"
name="jsSandboxMode"
value="safe"
checked={selectedAppMode === 'safe'}
onChange={handleAppModeChange}
checked={jsSandboxMode === 'safe'}
onChange={handleChange}
className="cursor-pointer"
/>
<span className={selectedAppMode === 'safe' ? 'font-medium' : 'font-normal'}>
<span className={jsSandboxMode === 'safe' ? 'font-medium' : 'font-normal'}>
Safe Mode
</span>
<span className='beta-tag'>BETA</span>
@ -58,13 +57,13 @@ const SecuritySettings = ({ collection }) => {
<input
type="radio"
id="developer"
name="appMode"
name="jsSandboxMode"
value="developer"
checked={selectedAppMode === 'developer'}
onChange={handleAppModeChange}
checked={jsSandboxMode === 'developer'}
onChange={handleChange}
className="cursor-pointer"
/>
<span className={selectedAppMode === 'developer' ? 'font-medium' : 'font-normal'}>
<span className={jsSandboxMode === 'developer' ? 'font-medium' : 'font-normal'}>
Developer Mode
<span className='ml-1 developer-mode-warning'>(use only if you trust the collections authors)</span>
</span>
@ -76,6 +75,9 @@ const SecuritySettings = ({ collection }) => {
<button onClick={handleSave} className="submit btn btn-sm btn-secondary w-fit mt-6">
Save
</button>
<small className='text-muted mt-6'>
* SAFE mode has been introduced v1.25 onwards and is in beta. Please report any issues on github.
</small>
</div>
</StyledWrapper>
);

View File

@ -33,9 +33,6 @@ export const collectionsSlice = createSlice({
const collection = action.payload;
collection.settingsSelectedTab = 'headers';
collection.showAppModeModal = !collection?.securityConfig?.appMode;
collection.folderLevelSettingsSelectedTab = {};
// TODO: move this to use the nextAction approach
@ -53,10 +50,6 @@ export const collectionsSlice = createSlice({
state.collections.push(collection);
}
},
setShowAppModeModal: (state, action) => {
const collection = findCollectionByUid(state.collections, action.payload.collectionUid);
collection.showAppModeModal = action.payload.showAppModeModal;
},
setCollectionSecurityConfig: (state, action) => {
const collection = findCollectionByUid(state.collections, action.payload.collectionUid);
if (collection) {
@ -1718,8 +1711,7 @@ export const {
runRequestEvent,
runFolderEvent,
resetCollectionRunner,
updateRequestDocs,
setShowAppModeModal
updateRequestDocs
} = collectionsSlice.actions;
export default collectionsSlice.reducer;

View File

@ -670,7 +670,9 @@ const registerRendererEventHandlers = (mainWindow, watcher, lastOpenedCollection
ipcMain.handle('renderer:save-collection-security-config', async (event, collectionPath, securityConfig) => {
try {
collectionSecurityStore.setSecurityConfigForCollection(collectionPath, securityConfig);
collectionSecurityStore.setSecurityConfigForCollection(collectionPath, {
jsSandboxMode: securityConfig.jsSandboxMode
});
} catch (error) {
return Promise.reject(error);
}

View File

@ -81,6 +81,11 @@ const getEnvVars = (environment = {}) => {
};
};
const getJsSandboxRuntime = (collection) => {
const securityConfig = get(collection, 'securityConfig', {});
return securityConfig.jsSandboxMode === 'safe' ? 'isolated-vm' : 'vm2';
};
const protocolRegex = /^([-+\w]{1,25})(:?\/\/|:)/;
const configureRequest = async (
@ -459,7 +464,8 @@ const registerNetworkIpc = (mainWindow) => {
const envVars = getEnvVars(environment);
const processEnvVars = getProcessEnvVars(collectionUid);
const brunoConfig = getBrunoConfig(collectionUid);
const scriptingConfig = { ...get(brunoConfig, 'scripts', {}), ...get(collection, 'securityConfig', {}) };
const scriptingConfig = get(brunoConfig, 'scripts', {});
scriptingConfig.runtime = getJsSandboxRuntime(collection);
try {
const controller = new AbortController();
@ -660,7 +666,8 @@ const registerNetworkIpc = (mainWindow) => {
const envVars = getEnvVars(environment);
const processEnvVars = getProcessEnvVars(collectionUid);
const brunoConfig = getBrunoConfig(collectionUid);
const scriptingConfig = { ...get(brunoConfig, 'scripts', {}), ...get(collection, 'securityConfig', {}) };
const scriptingConfig = get(brunoConfig, 'scripts', {});
scriptingConfig.runtime = getJsSandboxRuntime(collection);
await runPreRequest(
request,
@ -765,7 +772,8 @@ const registerNetworkIpc = (mainWindow) => {
const runtimeVariables = collection.runtimeVariables;
const processEnvVars = getProcessEnvVars(collectionUid);
const brunoConfig = getBrunoConfig(collection.uid);
const scriptingConfig = { ...get(brunoConfig, 'scripts', {}), ...get(collection, 'securityConfig', {}) };
const scriptingConfig = get(brunoConfig, 'scripts', {});
scriptingConfig.runtime = getJsSandboxRuntime(collection);
await runPreRequest(
request,
@ -831,7 +839,8 @@ const registerNetworkIpc = (mainWindow) => {
const folderUid = folder ? folder.uid : null;
const cancelTokenUid = uuid();
const brunoConfig = getBrunoConfig(collectionUid);
const scriptingConfig = { ...get(brunoConfig, 'scripts', {}), ...get(collection, 'securityConfig', {}) };
const scriptingConfig = get(brunoConfig, 'scripts', {});
scriptingConfig.runtime = getJsSandboxRuntime(collection);
const collectionRoot = get(collection, 'root', {});
const abortController = new AbortController();

View File

@ -16,7 +16,9 @@ class CollectionSecurityStore {
if (!collection) {
collections.push({
path: collectionPathname,
securityConfig
securityConfig: {
jsSandboxMode: securityConfig.jsSandboxMode
}
});
this.store.set('collections', collections);

View File

@ -6,9 +6,6 @@ const { terser } = require('rollup-plugin-terser');
const bundleLibraries = async () => {
const codeScript = `
import isNumber from "is-number";
global.isNumber = isNumber;
import { faker } from "@faker-js/faker";
import { expect, assert } from 'chai';
import { Buffer } from "buffer";
import moment from "moment";
@ -16,15 +13,12 @@ const bundleLibraries = async () => {
import atob from "atob";
global.expect = expect;
global.assert = assert;
global.faker = faker;
global.moment = moment;
global.btoa = btoa;
global.atob = atob;
global.Buffer = Buffer;
global.requireObject = {
'chai': { expect, assert },
'faker': faker,
'@faker-js/faker': { faker },
'moment': moment,
'buffer': { Buffer },
'btoa': btoa,