diff --git a/e2e/tests/variables.spec.ts b/e2e/tests/variables.spec.ts index 69d9f200..14cc284e 100644 --- a/e2e/tests/variables.spec.ts +++ b/e2e/tests/variables.spec.ts @@ -63,6 +63,27 @@ test.describe('Variables', () => { await expect(row.locator('text=***')).toBeVisible(); }); + test('sensitive variable value is masked on entry, revealable', async ({ page }) => { + await page.goto(`/workspaces/${workspaceId}?tab=variables`); + + await page.click('button:has-text("Add Variable")'); + const valField = page.locator('#var-val'); + await valField.fill('shoulder-surf-me'); + + // Not sensitive yet -> value shown normally (no masking class). + await expect(valField).not.toHaveClass(/text-masked/); + + // Mark sensitive -> the entry field masks (cross-browser disc font). + await page.locator('label:has-text("Sensitive") input[type="checkbox"]').check(); + await expect(valField).toHaveClass(/text-masked/); + + // Show toggle reveals it; hiding masks again. + await page.click('button[aria-label="Show value"]'); + await expect(valField).not.toHaveClass(/text-masked/); + await page.click('button[aria-label="Hide value"]'); + await expect(valField).toHaveClass(/text-masked/); + }); + test('delete variable removes it from list', async ({ page }) => { const varKey = `DELETE_e2e_${Date.now()}`; diff --git a/web/public/fonts/text-security-disc.woff2 b/web/public/fonts/text-security-disc.woff2 new file mode 100644 index 00000000..658e00dd Binary files /dev/null and b/web/public/fonts/text-security-disc.woff2 differ diff --git a/web/src/app/admin/variable-sets/[id]/page.tsx b/web/src/app/admin/variable-sets/[id]/page.tsx index fce26f2b..c11343ab 100644 --- a/web/src/app/admin/variable-sets/[id]/page.tsx +++ b/web/src/app/admin/variable-sets/[id]/page.tsx @@ -8,6 +8,7 @@ import { PageHeader } from '@/components/page-header' import { LoadingSpinner } from '@/components/loading-spinner' import { ErrorBanner } from '@/components/error-banner' import { EmptyState } from '@/components/empty-state' +import { SensitiveValueInput } from '@/components/sensitive-value-input' import { getAuthState, isAdmin } from '@/lib/auth' import { apiFetch } from '@/lib/api' import { usePollingInterval } from '@/lib/use-polling-interval' @@ -514,7 +515,7 @@ export default function VariableSetDetailPage() {
-