fix(client): gate every full access picker

This commit is contained in:
ZiyaZhang
2026-07-30 05:48:21 -07:00
parent 2ed4d982e7
commit 6d416b06c5
36 changed files with 624 additions and 195 deletions

View File

@@ -0,0 +1,87 @@
// Web e2e scenario: every visible permission picker gates Full access behind
// the same locale-aware, in-page risk confirmation. Zero model calls: the
// scenario boots the shipped Web composition and exercises the real
// permission projection, client command path, HTTP RPC, and pushed update.
import { fileURLToPath } from 'node:url'
import { join } from 'node:path'
import type { Browser, Page } from 'playwright'
import { chromium } from 'playwright'
import { afterAll, beforeAll, describe, expect, it, onTestFailed } from 'vitest'
import {
assertFixtureInventory, captureStableAria, compareOrRefreshGolden,
launchWebScaffold, watchConsole, webSnapshotMode, type WebScaffold,
} from './scaffold.ts'
import { connectFreshWorkspace, saveFailureShot } from './support.ts'
const SNAPSHOT_DIR = fileURLToPath(new URL('./snapshots/access-confirmation', import.meta.url))
const UI_EXPECTED = join(SNAPSHOT_DIR, 'ui.expected.md')
const MODE = webSnapshotMode()
describe('web e2e: Full access confirmation', () => {
let scaffold: WebScaffold
let browser: Browser
let page: Page
let tripwire: ReturnType<typeof watchConsole>
beforeAll(async () => {
scaffold = await launchWebScaffold({})
// CI uses Playwright's pinned browser. A developer may point this one
// scenario at an installed Chromium when the matching browser download
// is temporarily unavailable.
const executablePath = process.env.DSH_PLAYWRIGHT_EXECUTABLE_PATH
browser = await chromium.launch(executablePath === undefined ? {} : { executablePath })
// Keep the product default Chinese locale: the golden pins the actual
// registered dictionary rather than a test-local translation callback.
page = await browser.newPage({ viewport: { width: 1680, height: 1000 } })
tripwire = watchConsole(page)
await page.goto(scaffold.baseUrl, { waitUntil: 'load' })
await page.waitForSelector('[class*="frame"]', { timeout: 30_000 })
await connectFreshWorkspace(page)
}, 120_000)
afterAll(async () => {
await browser?.close()
await scaffold?.close()
})
it('requires acknowledgement before the composer picker can enable Full access', async () => {
onTestFailed(() => saveFailureShot(page, 'web-e2e-full-access-confirmation'))
const access = page.locator('button[aria-label^="Access mode"]').first()
await access.waitFor({ timeout: 10_000 })
// Normalize the starting preset through the real command path. The
// shipped web config may already start at Full access.
if ((await access.getAttribute('aria-label'))?.endsWith('Full access') === true) {
await access.click()
await page.getByRole('menuitem', { name: 'Workspace Write' }).click()
await expect.poll(() => access.getAttribute('aria-label'), { timeout: 10_000 })
.toBe('Access mode, current: Workspace Write')
}
await access.click()
await page.getByRole('menuitem', { name: 'Full access' }).click()
const dialog = page.getByRole('dialog', { name: '确认启用 Full access' })
await dialog.waitFor({ timeout: 10_000 })
const enable = dialog.getByRole('button', { name: '启用 Full access' })
expect(await enable.isDisabled()).toBe(true)
// The modal is in this page's body (not a native/new window) and escapes
// the sticky composer's stacking context.
expect(await dialog.evaluate(node => node.parentElement?.parentElement === document.body)).toBe(true)
const snapshot = await captureStableAria(page, '[role="dialog"]', scaffold.workspaceCwd)
await compareOrRefreshGolden(UI_EXPECTED, snapshot, MODE)
await dialog.getByRole('checkbox', { name: '我已了解风险,并愿意继续' }).check()
expect(await enable.isEnabled()).toBe(true)
await enable.click()
await expect.poll(() => access.getAttribute('aria-label'), { timeout: 10_000 })
.toBe('Access mode, current: Full access')
expect(await dialog.count()).toBe(0)
expect(tripwire.pageErrors).toEqual([])
}, 60_000)
it('keeps its snapshot inventory closed', async () => {
expect(tripwire.warnings).toEqual([])
await assertFixtureInventory(SNAPSHOT_DIR, ['ui.expected.md'])
})
})

View File

@@ -0,0 +1,10 @@
- dialog "确认启用 Full access":
- heading "确认启用 Full access" [level=2]
- button "Close":
- img
- img
- paragraph: 启用 Full access 后agent 将减少确认步骤,并且可以直接执行更多操作,包括敏感操作、文件修改或外部命令。仅建议在你信任当前任务时使用。
- checkbox "我已了解风险,并愿意继续"
- text: 我已了解风险,并愿意继续
- button "取消"
- button "启用 Full access" [disabled]

View File

@@ -35,7 +35,7 @@
- textbox "Message the agent"
- button "Add attachment":
- img
- 'button "Access mode, current: Danger Full Access"': Danger Full Access
- 'button "Access mode, current: Full access"': Full access
- button "Plan mode off, press to turn on": Plan off
- button "Select model, current DeepSeek-V4-Flash":
- text: DeepSeek-V4-Flash

View File

@@ -49,7 +49,7 @@
- textbox "Message the agent"
- button "Add attachment":
- img
- 'button "Access mode, current: Danger Full Access"': Danger Full Access
- 'button "Access mode, current: Full access"': Full access
- button "Plan mode off, press to turn on": Plan off
- button "Select model, current DeepSeek-V4-Flash":
- text: DeepSeek-V4-Flash

View File

@@ -32,7 +32,7 @@
- textbox "Message the agent"
- button "Add attachment":
- img
- 'button "Access mode, current: Danger Full Access"': Danger Full Access
- 'button "Access mode, current: Full access"': Full access
- button "Plan mode off, press to turn on": Plan off
- button "Select model, current DeepSeek-V4-Flash":
- text: DeepSeek-V4-Flash

View File

@@ -28,7 +28,7 @@
- textbox "Describe what you want to build"
- button "Add attachment":
- img
- 'button "Access mode, current: Danger Full Access"': Danger Full Access
- 'button "Access mode, current: Full access"': Full access
- button "Plan mode off, press to turn on": Plan off
- button "Select model, current DeepSeek-V4-Flash":
- text: DeepSeek-V4-Flash

View File

@@ -24,7 +24,7 @@
- textbox "Message the agent"
- button "Add attachment":
- img
- 'button "Access mode, current: Danger Full Access"': Danger Full Access
- 'button "Access mode, current: Full access"': Full access
- button "Plan mode off, press to turn on": Plan off
- button "Select model, current DeepSeek-V4-Flash":
- text: DeepSeek-V4-Flash

View File

@@ -21,7 +21,7 @@
- textbox "Message the agent"
- button "Add attachment":
- img
- 'button "Access mode, current: Danger Full Access"': Danger Full Access
- 'button "Access mode, current: Full access"': Full access
- button "Plan mode off, press to turn on": Plan off
- button "Select model, current DeepSeek-V4-Flash":
- text: DeepSeek-V4-Flash

View File

@@ -14,7 +14,7 @@
- textbox "Message the agent"
- button "Add attachment":
- img
- 'button "Access mode, current: Danger Full Access"': Danger Full Access
- 'button "Access mode, current: Full access"': Full access
- button "Plan mode off, press to turn on": Plan off
- button "Select model, current DeepSeek-V4-Flash":
- text: DeepSeek-V4-Flash

View File

@@ -24,7 +24,7 @@
- textbox "Message the agent"
- button "Add attachment":
- img
- 'button "Access mode, current: Danger Full Access"': Danger Full Access
- 'button "Access mode, current: Full access"': Full access
- button "Plan mode off, press to turn on": Plan off
- button "Select model, current DeepSeek-V4-Flash":
- text: DeepSeek-V4-Flash

View File

@@ -35,7 +35,7 @@
- textbox "Message the agent"
- button "Add attachment":
- img
- 'button "Access mode, current: Danger Full Access"': Danger Full Access
- 'button "Access mode, current: Full access"': Full access
- button "Plan mode off, press to turn on": Plan off
- button "Select model, current deepseek-v4-flash":
- text: deepseek-v4-flash

View File

@@ -32,7 +32,7 @@
- textbox "Message the agent"
- button "Add attachment":
- img
- 'button "Access mode, current: Danger Full Access"': Danger Full Access
- 'button "Access mode, current: Full access"': Full access
- button "Plan mode off, press to turn on": Plan off
- button "Select model, current DeepSeek-V4-Flash":
- text: DeepSeek-V4-Flash

View File

@@ -28,7 +28,7 @@
- textbox "Message the agent"
- button "Add attachment":
- img
- 'button "Access mode, current: Danger Full Access"': Danger Full Access
- 'button "Access mode, current: Full access"': Full access
- button "Plan mode off, press to turn on": Plan off
- button "Select model, current DeepSeek-V4-Flash":
- text: DeepSeek-V4-Flash

View File

@@ -22,7 +22,7 @@
- textbox "Message the agent"
- button "Add attachment":
- img
- 'button "Access mode, current: Danger Full Access"': Danger Full Access
- 'button "Access mode, current: Full access"': Full access
- button "Plan mode off, press to turn on": Plan off
- button "Select model, current DeepSeek-V4-Flash":
- text: DeepSeek-V4-Flash

View File

@@ -38,7 +38,7 @@
- textbox "Message the agent"
- button "Add attachment":
- img
- 'button "Access mode, current: Danger Full Access"': Danger Full Access
- 'button "Access mode, current: Full access"': Full access
- button "Plan mode off, press to turn on": Plan off
- button "Select model, current deepseek-v4-flash":
- text: deepseek-v4-flash

View File

@@ -32,7 +32,7 @@
- textbox "Message the agent"
- button "Add attachment":
- img
- 'button "Access mode, current: Danger Full Access"': Danger Full Access
- 'button "Access mode, current: Full access"': Full access
- button "Plan mode off, press to turn on": Plan off
- button "Select model, current DeepSeek-V4-Flash":
- text: DeepSeek-V4-Flash