143 lines
6.4 KiB
Markdown
143 lines
6.4 KiB
Markdown
---
|
|
spec: docs/specs/auth/requirements/secure-context-browser-fallbacks.md
|
|
created: 2026-08-02
|
|
status: implemented
|
|
---
|
|
|
|
# Implementation Plan: Secure-context browser fallbacks
|
|
|
|
## Overview
|
|
|
|
Route every client-only UUID through the existing guarded generator, then
|
|
extract the existing clipboard fallback into a shared utility and migrate
|
|
direct Clipboard API call sites to it. The audit confirms that file hashing,
|
|
voice input, notifications, and sound already check capability boundaries; the
|
|
focused tests will preserve those fallbacks while covering the newly repaired
|
|
workflow, layout, and copy paths.
|
|
|
|
## Root cause
|
|
|
|
`useWorkflowCreation`, workflow step creation, and layout profile creation call
|
|
`crypto.randomUUID()` directly. On a non-localhost HTTP origin the browser
|
|
exposes `crypto` but not `randomUUID`, so Add Workflow throws before its draft
|
|
state is updated. The shared `generateUUID()` helper already handles that
|
|
condition, but those call sites bypass it. Several copy actions similarly call
|
|
`navigator.clipboard.writeText()` directly even though the existing copy hook
|
|
has a DOM fallback for insecure contexts.
|
|
|
|
## Frontend — client-only UUIDs
|
|
|
|
- `apps/web/app/settings/workspace/use-workflow-creation.ts`: use
|
|
`generateUUID()` for temporary workflow IDs.
|
|
- `apps/web/components/settings/workflow-card-actions.ts`: use
|
|
`generateUUID()` for temporary step IDs.
|
|
- `apps/web/lib/layout/layout-profiles.ts`: use `generateUUID()` for layout
|
|
profile IDs.
|
|
- `apps/web/app/settings/workspace/use-workflow-creation.test.ts`,
|
|
`apps/web/components/settings/workflow-card-actions.test.ts`, and
|
|
`apps/web/lib/layout/layout-profiles.test.ts`: add insecure-context coverage
|
|
with `crypto` missing `randomUUID`.
|
|
- Keep `apps/web/lib/utils/file-diff.ts` unchanged unless its existing guarded
|
|
fallback needs a test adjustment; it already degrades to `djb2Hash` when
|
|
`crypto.subtle` is unavailable.
|
|
|
|
## Frontend — clipboard fallback
|
|
|
|
- Add `apps/web/lib/utils/copy-to-clipboard.ts` with the existing dialog-aware
|
|
DOM fallback and a promise-returning `copyToClipboard(text)` function.
|
|
- Refactor `apps/web/hooks/use-copy-to-clipboard.ts` to use the shared utility,
|
|
preserving its copied state and timing.
|
|
- Migrate direct Clipboard API calls in the settings, onboarding, task,
|
|
diff/editor, review, and share surfaces to the shared utility. Preserve each
|
|
caller's current success state, toast, and error behavior.
|
|
- The audited call sites are `apps/web/app/settings/agents/page.tsx`,
|
|
`apps/web/app/settings/agents/[agentId]/profiles/[profileId]/command-preview-card.tsx`,
|
|
`apps/web/components/settings/external-mcp-settings.tsx`,
|
|
`apps/web/components/automations/trigger-configs/webhook-config.tsx`,
|
|
`apps/web/components/task/executor-environment-info.tsx`,
|
|
`apps/web/components/task/port-forward-dialog.tsx`,
|
|
`apps/web/components/task/simple/OfficeSimplePane.tsx`,
|
|
`apps/web/components/task/inspector/annotations-panel.tsx`,
|
|
`apps/web/components/task/share/share-dialog.tsx`,
|
|
`apps/web/components/task/chat/messages/auth-methods-panel.tsx`,
|
|
`apps/web/components/task/chat/messages/tool-edit-message.tsx`,
|
|
`apps/web/components/editors/file-actions-dropdown.tsx`,
|
|
`apps/web/components/editors/monaco/use-diff-viewer-comments.ts`,
|
|
`apps/web/components/editors/monaco/diff-viewer-context-menu.tsx`,
|
|
`apps/web/components/editors/monaco/monaco-diff-viewer.tsx`,
|
|
`apps/web/components/diff/diff-header-toolbar.tsx`,
|
|
`apps/web/components/review/review-diff-toolbar.tsx`,
|
|
`apps/web/components/onboarding/step-agents.tsx`,
|
|
`apps/web/components/settings/account/api-tokens.tsx`,
|
|
`apps/web/components/settings/system/log-viewer.tsx`,
|
|
`apps/web/components/settings/system/invite-dialog.tsx`, and
|
|
`apps/web/components/settings/ssh-agent-readiness-card.tsx`.
|
|
- Add `apps/web/lib/utils/copy-to-clipboard.test.ts` covering modern success,
|
|
missing API, rejected API, and dialog focus containment. Update the hook test
|
|
only where its implementation seam changes.
|
|
|
|
## Existing capability audit
|
|
|
|
- `apps/web/lib/utils/file-diff.ts` already guards `crypto.subtle` and falls
|
|
back to `djb2Hash`.
|
|
- Voice input gates its UI on secure-context/capability checks and catches
|
|
capture failures; notification permission actions check `Notification`
|
|
before use; sound creation checks `AudioContext` before construction.
|
|
- No behavior change is planned for those already guarded paths; their
|
|
existing tests remain part of focused frontend verification.
|
|
|
|
## Tests
|
|
|
|
- **UUID regression:** no `randomUUID` must not prevent workflow creation,
|
|
step addition, or layout profile creation. Files listed above; Vitest hook and
|
|
pure-function tests.
|
|
- **Clipboard regression:** missing/rejected Clipboard API uses the DOM
|
|
fallback without an uncaught rejection. New utility test plus existing hook
|
|
tests.
|
|
- **Existing fallbacks:** run the file-diff, voice, notification, and sound
|
|
tests selected by the focused test command where applicable.
|
|
|
|
## E2E and mobile parity
|
|
|
|
This repair changes shared state/utility behavior, not layout, navigation,
|
|
touch targets, scrolling, or mobile composition. The existing workflow settings
|
|
desktop/mobile E2E coverage remains the rendered regression path; no new mobile
|
|
scenario is required for this data-normalization change. A browser-level
|
|
insecure-origin smoke run is optional if an isolated HTTP instance is already
|
|
available.
|
|
|
|
## Risks and out of scope
|
|
|
|
- The DOM copy fallback depends on `document.execCommand("copy")`, which is
|
|
deprecated but is the compatibility path already used by Kandev.
|
|
- Fallback UUIDs are not cryptographically secure and must stay limited to
|
|
client-only identifiers.
|
|
- Backend/API contracts, TLS setup, and security-token generation are out of
|
|
scope.
|
|
|
|
## Implementation waves
|
|
|
|
Wave 1 (sequential):
|
|
|
|
- [x] [Task 01: UUID fallback audit](task-01-uuid-fallback.md)
|
|
|
|
Wave 2 (sequential; depends on Task 01):
|
|
|
|
- [x] [Task 02: Clipboard fallback migration](task-02-clipboard-fallback.md)
|
|
|
|
## Verification Results
|
|
|
|
### Task 01 — UUID fallback audit
|
|
|
|
- Focused Vitest: 4 files, 78 tests passed.
|
|
- Web typecheck passed.
|
|
- Static search found only the guarded `generateUUID()` implementation.
|
|
|
|
### Task 02 — Clipboard fallback migration
|
|
|
|
- Focused utility/hook Vitest: 3 files, 21 tests passed.
|
|
- Broader task-defined Vitest: 360 files, 2,647 tests passed, 4 skipped.
|
|
- Web typecheck, targeted ESLint, Prettier, and `i18n:ratchet` passed.
|
|
- Static search found no direct application Clipboard API calls outside the
|
|
shared guarded utility.
|