diff --git a/e2e/session-luancher.test.ts b/e2e/session-luancher.test.ts new file mode 100644 index 0000000000..d41ae993de --- /dev/null +++ b/e2e/session-luancher.test.ts @@ -0,0 +1,54 @@ +import { + createSession, + deleteSession, + loginAsUser, + navigateTo, +} from './test-util'; +import { test, expect } from '@playwright/test'; + +test.describe('NEO Sessions Launcher', () => { + test.beforeEach(async ({ page }) => { + await loginAsUser(page); + }); + + const sessionName = 'e2e-test-session'; + test('User can create session in NEO', async ({ page }) => { + await createSession(page, sessionName); + await deleteSession(page, sessionName); + }); + + test('Sensitive environment variables are cleared when the browser is reloaded.', async ({ + page, + }) => { + await navigateTo(page, 'session/start'); + await page + .getByRole('button', { name: '2 Environments & Resource' }) + .click(); + await page + .getByRole('button', { name: 'plus Add environment variables' }) + .click(); + await page.getByPlaceholder('Variable').fill('abc'); + await page.getByPlaceholder('Variable').press('Tab'); + await page.getByPlaceholder('Value').fill('123'); + await page + .getByRole('button', { name: 'plus Add environment variables' }) + .click(); + await page.locator('#envvars_1_variable').fill('password'); + await page.locator('#envvars_1_variable').press('Tab'); + await page.locator('#envvars_1_value').fill('hello'); + await page + .getByRole('button', { name: 'plus Add environment variables' }) + .click(); + await page.locator('#envvars_2_variable').fill('api_key'); + await page.locator('#envvars_2_variable').press('Tab'); + await page.locator('#envvars_2_value').fill('secret'); + await page.waitForTimeout(1000); // Wait for the form state to be saved as query param. + await page.reload(); + await expect( + page.locator('#envvars_1_value_help').getByText('Please enter a value.'), + ).toBeVisible(); + await expect( + page.locator('#envvars_2_value_help').getByText('Please enter a value.'), + ).toBeVisible(); + }); +}); diff --git a/e2e/session.test.ts b/e2e/session.test.ts deleted file mode 100644 index 1f9ab8ca08..0000000000 --- a/e2e/session.test.ts +++ /dev/null @@ -1,11 +0,0 @@ -import { createSession, deleteSession, loginAsUser } from './test-util'; -import { test, expect } from '@playwright/test'; - -test.describe('Sessions ', () => { - const sessionName = 'e2e-test-session'; - test('User can create session in NEO', async ({ page }) => { - await loginAsUser(page); - await createSession(page, sessionName); - await deleteSession(page, sessionName); - }); -}); diff --git a/react/src/components/EnvVarFormList.test.tsx b/react/src/components/EnvVarFormList.test.tsx new file mode 100644 index 0000000000..408dbe2d13 --- /dev/null +++ b/react/src/components/EnvVarFormList.test.tsx @@ -0,0 +1,35 @@ +// EnvVarFormList.test.tsx +import { sanitizeSensitiveEnv } from './EnvVarFormList'; + +describe('emptySensitiveEnv', () => { + it('should empty the value of sensitive environment variables', () => { + const envs = [ + { variable: 'SECRET_KEY', value: '12345' }, + { variable: 'API_KEY', value: 'abcdef' }, + { variable: 'NON_SENSITIVE', value: 'value' }, + ]; + + const result = sanitizeSensitiveEnv(envs); + + expect(result).toEqual([ + { variable: 'SECRET_KEY', value: '' }, + { variable: 'API_KEY', value: '' }, + { variable: 'NON_SENSITIVE', value: 'value' }, + ]); + }); + + it('should not change non-sensitive environment variables', () => { + const envs = [{ variable: 'NON_SENSITIVE', value: 'value' }]; + const result = sanitizeSensitiveEnv(envs); + + expect(result).toEqual([{ variable: 'NON_SENSITIVE', value: 'value' }]); + }); + + it('should handle an empty array', () => { + const envs: any[] = []; + + const result = sanitizeSensitiveEnv(envs); + + expect(result).toEqual([]); + }); +}); diff --git a/react/src/components/EnvVarFormList.tsx b/react/src/components/EnvVarFormList.tsx index e651a9a54f..0286a3f8ce 100644 --- a/react/src/components/EnvVarFormList.tsx +++ b/react/src/components/EnvVarFormList.tsx @@ -137,4 +137,40 @@ const EnvVarFormList: React.FC = ({ ); }; +const sensitivePatterns = [ + /AUTH/i, + /ACCESS/i, + /SECRET/i, + /_KEY/i, + /PASSWORD/i, + /PASSWD/i, + /PWD/i, + /TOKEN/i, + /PRIVATE/i, + /CREDENTIAL/i, + /JWT/i, + /KEYPAIR/i, + /CERTIFICATE/i, + /SSH/i, + /ENCRYPT/i, + /SIGNATURE/i, + /SALT/i, + /PIN/i, + /PASSPHRASE/i, + /OAUTH/i, +]; + +export function isSensitiveEnv(key: string) { + return sensitivePatterns.some((pattern) => pattern.test(key)); +} + +export function sanitizeSensitiveEnv(envs: EnvVarFormListValue[]) { + return envs.map((env) => { + if (env && isSensitiveEnv(env.variable)) { + return { ...env, value: '' }; + } + return env; + }); +} + export default EnvVarFormList; diff --git a/react/src/components/VFolderTableFormItem.tsx b/react/src/components/VFolderTableFormItem.tsx index 488bb169cf..56cb3060ff 100644 --- a/react/src/components/VFolderTableFormItem.tsx +++ b/react/src/components/VFolderTableFormItem.tsx @@ -19,6 +19,7 @@ interface VFolderTableFormItemProps extends Omit { export interface VFolderTableFormValues { mounts: string[]; vfoldersAliasMap: AliasMap; + autoMountedFolderNames?: string[]; } const VFolderTableFormItem: React.FC = ({ diff --git a/react/src/pages/SessionLauncherPage.tsx b/react/src/pages/SessionLauncherPage.tsx index 651873a141..4ae5b25ff8 100644 --- a/react/src/pages/SessionLauncherPage.tsx +++ b/react/src/pages/SessionLauncherPage.tsx @@ -3,6 +3,7 @@ import BAIIntervalText from '../components/BAIIntervalText'; import DatePickerISO from '../components/DatePickerISO'; import DoubleTag from '../components/DoubleTag'; import EnvVarFormList, { + sanitizeSensitiveEnv, EnvVarFormListValue, } from '../components/EnvVarFormList'; import Flex from '../components/Flex'; @@ -229,14 +230,21 @@ const SessionLauncherPage = () => { // console.log('syncFormToURLWithDebounce', form.getFieldsValue()); // To sync the latest form values to URL, // 'trailing' is set to true, and get the form values here." + const currentValue = form.getFieldsValue(); setQuery( { // formValues: form.getFieldsValue(), - formValues: _.omit( - form.getFieldsValue(), - ['environments.image'], - ['environments.customizedTag'], - ['autoMountedFolderNames'], + formValues: _.extend( + _.omit( + form.getFieldsValue(), + ['environments.image'], + ['environments.customizedTag'], + ['autoMountedFolderNames'], + ['envvars'], + ), + { + envvars: sanitizeSensitiveEnv(currentValue.envvars), + }, ), }, 'replaceIn',