refactor(desktop): configure test policy login origins

This commit is contained in:
winewill committed 2026-09-18 11:57:56 +08:00
1 parent bf34d10e23
commit 76a592e3ab
22 files changed
+104 -48

No files matched your search

+5 -4
View File
@@ -5,11 +5,12 @@ DSH_DESKTOP_AUTO_UPDATE_ENV=test
DOWNLOAD_TEST_ORIGIN=
# The selected mandatory-update origin is required, including unsigned and preparation builds.
DSH_DESKTOP_MANDATORY_UPDATE_TEST_ORIGIN=https://harness-test.deepseek.com
DSH_DESKTOP_MANDATORY_UPDATE_PROD_ORIGIN=https://harness.deepseek.com
# Optional polling and fallback-page options; origin and authentication are selected automatically.
DSH_DESKTOP_MANDATORY_UPDATE_TEST_ORIGIN=
DSH_DESKTOP_MANDATORY_UPDATE_PROD_ORIGIN=
# Set approved login origins for test builds; omit allowedAuthOrigins for production.
# Optional polling and fallback-page options belong in the same JSON value.
# The fallback-page allowlist defaults to the selected service origin.
# DSH_DESKTOP_MANDATORY_UPDATE_CONFIG='{"intervalMs":600000}'
DSH_DESKTOP_MANDATORY_UPDATE_CONFIG='{"allowedAuthOrigins":["https://login.example.com"]}'
DSH_DESKTOP_MACOS_SIGNING_IDENTITY=
DSH_DESKTOP_MACOS_TEAM_ID=
+5 -4
View File
@@ -5,11 +5,12 @@ DSH_DESKTOP_AUTO_UPDATE_ENV=test
DOWNLOAD_TEST_ORIGIN=
# The selected mandatory-update origin is required, including unsigned and preparation builds.
DSH_DESKTOP_MANDATORY_UPDATE_TEST_ORIGIN=https://harness-test.deepseek.com
DSH_DESKTOP_MANDATORY_UPDATE_PROD_ORIGIN=https://harness.deepseek.com
# Optional polling and fallback-page options; origin and authentication are selected automatically.
DSH_DESKTOP_MANDATORY_UPDATE_TEST_ORIGIN=
DSH_DESKTOP_MANDATORY_UPDATE_PROD_ORIGIN=
# Set approved login origins for test builds; omit allowedAuthOrigins for production.
# Optional polling and fallback-page options belong in the same JSON value.
# The fallback-page allowlist defaults to the selected service origin.
# DSH_DESKTOP_MANDATORY_UPDATE_CONFIG='{"intervalMs":600000}'
DSH_DESKTOP_MANDATORY_UPDATE_CONFIG='{"allowedAuthOrigins":["https://login.example.com"]}'
DSH_DESKTOP_WINDOWS_CER_FILE=
DSH_DESKTOP_WINDOWS_SIGNTOOL=
+2 -2
View File
@@ -2,5 +2,5 @@
# side as of the last confirmed-consistent state. Both languages carry equal authority;
# after editing either side, bring the other along and re-record with:
# pnpm run verify-translation-pairing --write apps/desktop/README.md
README.md: c2a1f4df103d5fe74f4a25aa1bcfc1f8e38d3b1e
README.zh.md: 9dda03b4b40a7b46bf5e0f1404f77d73d397bfa4
README.md: bfe8f68bfbe43de4545255e8005a9ee33ab5f4b7
README.zh.md: 163a0f070ba9c396d256633fc18afc566ea7a997
+3 -2
View File
@@ -275,15 +275,16 @@ Confirmed Host exit without successful task teardown displays localized recovery
### Mandatory update policy
The [mandatory client decision](../../.agents/notes/implemented/feature/2026-09-11-desktop-mandatory-update-client.md) owns policy polling and the blocking window. Packaging reads `.env.windows` or `.env.macos`: `DSH_DESKTOP_AUTO_UPDATE_ENV=test` (the default) selects `DSH_DESKTOP_MANDATORY_UPDATE_TEST_ORIGIN`; `production` selects `DSH_DESKTOP_MANDATORY_UPDATE_PROD_ORIGIN`. Templates use `https://harness-test.deepseek.com` and `https://harness.deepseek.com`, respectively. The selected origin is required before preparation or signing, including unsigned and preparation-only builds; the unselected origin is optional. These settings never fall back to the parent environment or the other deployment. Packaging embeds the selected policy with the application ID; packaged applications ignore runtime overrides.
The [mandatory client decision](../../.agents/notes/implemented/feature/2026-09-11-desktop-mandatory-update-client.md) owns policy polling and the blocking window. Packaging reads `.env.windows` or `.env.macos`: `DSH_DESKTOP_AUTO_UPDATE_ENV=test` (the default) selects `DSH_DESKTOP_MANDATORY_UPDATE_TEST_ORIGIN`; `production` selects `DSH_DESKTOP_MANDATORY_UPDATE_PROD_ORIGIN`. The templates leave both origins blank; fill in the selected deployment origin in the ignored local dotenv file. The selected origin is required before preparation or signing, including unsigned and preparation-only builds; the unselected origin is optional. These settings never fall back to the parent environment or the other deployment. Packaging embeds the selected policy with the application ID; packaged applications ignore runtime overrides.
Optional `DSH_DESKTOP_MANDATORY_UPDATE_CONFIG` JSON supplies polling and download-page options; packaging rejects `origin` and `authentication` inside it. The page allowlist defaults to the selected service origin; explicitly allow other approved download-page origins when needed. Test builds select `feishu-test`, and production selects `anonymous`. Policy requests reject redirects; only test authentication carries gateway cookies. Unpackaged development instead reads a complete policy JSON from this variable and requires `DSH_DESKTOP_APP_ID`; absent JSON disables development policy queries, and only anonymous development permits HTTP `127.0.0.1`. A user-initiated ordinary check triggers policy work concurrently but never waits for or reports a policy failure. Only a confirmed blocking decision cancels ordinary dialogs. Test authentication waits until the active ordinary dialog finishes, and cancellation or failure does not discard the updater result.
`DSH_DESKTOP_MANDATORY_UPDATE_CONFIG` JSON supplies test login origins and optional polling and download-page options; packaging rejects `origin` and `authentication` inside it. The page allowlist defaults to the selected service origin; explicitly allow other approved download-page origins when needed. Test builds select `feishu-test` and require `allowedAuthOrigins` in `DSH_DESKTOP_MANDATORY_UPDATE_CONFIG`; production selects `anonymous` and rejects that field. Each login origin must be an HTTPS origin without credentials, path, query, or fragment. The login window permits document navigation only to the selected policy origin and these configured origins. Policy requests reject redirects; only test authentication carries gateway cookies. Unpackaged development instead reads a complete policy JSON from this variable and requires `DSH_DESKTOP_APP_ID`; absent JSON disables development policy queries, and only anonymous development permits HTTP `127.0.0.1`. A user-initiated ordinary check triggers policy work concurrently but never waits for or reports a policy failure. Only a confirmed blocking decision cancels ordinary dialogs. Test authentication waits until the active ordinary dialog finishes, and cancellation or failure does not discard the updater result.
| Resolved policy field | Meaning and default |
|---|---|
| `origin` | Required HTTPS API origin, without credentials, path, query, or fragment; requests use `/api/v0/check_client_update` |
| `allowedPageOrigins` | Nonempty array of exact HTTPS origins; packaging defaults to the selected API origin; subdomains and alternate ports are not implied |
| `authentication` | Packaging selects `feishu-test` for test and `anonymous` for production; unpackaged development defaults to `anonymous` |
| `allowedAuthOrigins` | Nonempty array of exact HTTPS login document origins for test authentication; forbidden for production |
| `intervalMs` | Polling interval; default `600000` |
| `timeoutMs` | Request deadline; default `15000` |
| `maxBackoffMs` | Maximum failed-request interval including jitter; default `3600000`, at least `intervalMs` |
+3 -2
View File
@@ -276,15 +276,16 @@ macOS 打包在组装 App 时、代码签名前写入 `Contents/Resources/app-up
### 强制更新策略
[强更客户端决策](../../.agents/notes/implemented/feature/2026-09-11-desktop-mandatory-update-client.zh.md)负责策略查询和阻塞窗口。打包读取 `.env.windows` 或 `.env.macos`:`DSH_DESKTOP_AUTO_UPDATE_ENV=test`(默认值)选择 `DSH_DESKTOP_MANDATORY_UPDATE_TEST_ORIGIN`;`production` 选择 `DSH_DESKTOP_MANDATORY_UPDATE_PROD_ORIGIN`。模板分别使用 `https://harness-test.deepseek.com` 和 `https://harness.deepseek.com`。在准备产物或签名前,所选源站必须配置,包括未签名和仅准备构建;未选环境的源站可不填。这些配置不会回退到父进程环境或另一部署环境。打包将选定策略与应用 ID 写入元数据;打包应用忽略运行时覆盖。
[强更客户端决策](../../.agents/notes/implemented/feature/2026-09-11-desktop-mandatory-update-client.zh.md)负责策略查询和阻塞窗口。打包读取 `.env.windows` 或 `.env.macos`:`DSH_DESKTOP_AUTO_UPDATE_ENV=test`(默认值)选择 `DSH_DESKTOP_MANDATORY_UPDATE_TEST_ORIGIN`;`production` 选择 `DSH_DESKTOP_MANDATORY_UPDATE_PROD_ORIGIN`。模板将两个源站留空;在 Git 忽略的目标 dotenv 文件中填写所选部署的源站。在准备产物或签名前,所选源站必须配置,包括未签名和仅准备构建;未选环境的源站可不填。这些配置不会回退到父进程环境或另一部署环境。打包将选定策略与应用 ID 写入元数据;打包应用忽略运行时覆盖。
可选的 `DSH_DESKTOP_MANDATORY_UPDATE_CONFIG` JSON 提供轮询和下载页面选项;打包拒绝其中的 `origin` 和 `authentication`。页面白名单默认只包含所选服务源站;需要其他已批准下载页面源站时应显式配置。测试包选择 `feishu-test`,正式包选择 `anonymous`。策略请求拒绝重定向;仅测试鉴权携带网关 Cookie。未打包开发模式则从此变量读取完整策略 JSON,并要求 `DSH_DESKTOP_APP_ID`;缺少 JSON 会禁用开发模式策略查询,仅匿名开发允许 HTTP `127.0.0.1`。用户发起常规检查时会并发触发策略检查,但不会等待或展示策略失败。只有已确认的强更决定可以关闭常规弹窗。测试环境鉴权会等待当前常规弹窗结束,取消或失败不会丢弃 updater 结果。
`DSH_DESKTOP_MANDATORY_UPDATE_CONFIG` JSON 提供测试登录源站,以及可选的轮询和下载页面选项;打包拒绝其中的 `origin` 和 `authentication`。页面白名单默认只包含所选服务源站;需要其他已批准下载页面源站时应显式配置。测试包选择 `feishu-test`,且必须在 `DSH_DESKTOP_MANDATORY_UPDATE_CONFIG` 中配置 `allowedAuthOrigins`;正式包选择 `anonymous`,并拒绝该字段。每个登录源站必须是没有凭据、路径、查询或片段的 HTTPS origin。登录窗口仅允许文档导航到所选策略源站和这些已配置源站。策略请求拒绝重定向;仅测试鉴权携带网关 Cookie。未打包开发模式则从此变量读取完整策略 JSON,并要求 `DSH_DESKTOP_APP_ID`;缺少 JSON 会禁用开发模式策略查询,仅匿名开发允许 HTTP `127.0.0.1`。用户发起常规检查时会并发触发策略检查,但不会等待或展示策略失败。只有已确认的强更决定可以关闭常规弹窗。测试环境鉴权会等待当前常规弹窗结束,取消或失败不会丢弃 updater 结果。
| 解析后的策略字段 | 含义与默认值 |
|---|---|
| `origin` | 必填 HTTPS API 源站,不含凭据、路径、查询或片段;请求使用 `/api/v0/check_client_update` |
| `allowedPageOrigins` | 非空的精确 HTTPS 源站数组;打包时默认只包含所选 API 源站;不隐含子域名或其他端口 |
| `authentication` | 打包时测试环境选择 `feishu-test`,正式环境选择 `anonymous`;未打包开发模式默认为 `anonymous` |
| `allowedAuthOrigins` | 测试鉴权必填的非空 HTTPS 登录文档源站数组;正式环境禁止配置 |
| `intervalMs` | 轮询间隔;默认 `600000` |
| `timeoutMs` | 请求截止时间;默认 `15000` |
| `maxBackoffMs` | 含抖动的失败请求最大间隔;默认 `3600000`,不小于 `intervalMs` |
@@ -2,6 +2,7 @@
export interface DesktopPolicyEnvironment {
origin: string
allowedPageOrigins: string[]
allowedAuthOrigins?: string[]
authentication: 'anonymous' | 'feishu-test'
[key: string]: unknown
}
@@ -30,7 +30,15 @@ export function resolveDesktopPolicyEnvironment(environment) {
}
const pages = settings.allowedPageOrigins ?? [selected]
if (!Array.isArray(pages) || pages.length === 0) throw new Error('desktop package: allowedPageOrigins must be a nonempty array')
const authOrigins = settings.allowedAuthOrigins
if (deployment === 'test' && (!Array.isArray(authOrigins) || authOrigins.length === 0)) {
throw new Error('desktop package: test policy requires nonempty allowedAuthOrigins')
}
if (deployment === 'production' && authOrigins !== undefined) {
throw new Error('desktop package: production policy must not configure allowedAuthOrigins')
}
return { ...settings, origin: selected,
allowedPageOrigins: pages.map(value => origin(value, 'allowedPageOrigins')),
...(deployment === 'test' ? { allowedAuthOrigins: authOrigins.map(value => origin(value, 'allowedAuthOrigins')) } : {}),
authentication: deployment === 'test' ? 'feishu-test' : 'anonymous' }
}
@@ -44,7 +44,9 @@ try {
DSH_DESKTOP_TARGET_PLATFORM: 'win32', DSH_DESKTOP_TARGET_ARCH: 'x64',
DSH_DESKTOP_UNSIGNED: '1', CSC_IDENTITY_AUTO_DISCOVERY: 'false', ELECTRON_BUILDER_7Z_FILTER: 'BCJ',
DSH_DESKTOP_MANDATORY_UPDATE_TEST_ORIGIN: signingEnvironment.DSH_DESKTOP_MANDATORY_UPDATE_TEST_ORIGIN
?? 'https://harness-test.deepseek.com',
?? 'https://test.example.com',
DSH_DESKTOP_MANDATORY_UPDATE_CONFIG: signingEnvironment.DSH_DESKTOP_MANDATORY_UPDATE_CONFIG
?? JSON.stringify({ allowedAuthOrigins: ['https://login.example.com'] }),
...signingRun ? { DSH_DESKTOP_PACKAGING_RUN_DIR: signingRun.directory } : {},
})
const { createElectronBuilderConfig } = await import('../electron-builder.config.mjs')
+2 -1
View File
@@ -728,7 +728,8 @@ async function main(): Promise<void> {
const policyConfig = resolveDesktopPolicyConfig(policyInput, !app.isPackaged)
if (policyConfig !== undefined) {
if (policyConfig.authentication === 'feishu-test') {
policyAuth = new DesktopPolicyTestAuth(policyConfig.origin, locale, () => mandatoryUI?.confirmationWindow ?? mainWindow,
policyAuth = new DesktopPolicyTestAuth(policyConfig.origin, policyConfig.allowedAuthOrigins, locale,
() => mandatoryUI?.confirmationWindow ?? mainWindow,
(event) => { console.info(`desktop policy authentication: ${event}`); updateJournal?.action(`policy-login-${event}`) })
}
const bundleId = app.isPackaged
@@ -16,6 +16,7 @@ export interface DesktopPolicyIdentity {
export interface DesktopPolicyConfig {
readonly origin: string
readonly allowedPageOrigins: readonly string[]
readonly allowedAuthOrigins: readonly string[]
readonly intervalMs: number
readonly timeoutMs: number
readonly maxBackoffMs: number
@@ -74,12 +75,20 @@ export function resolveDesktopPolicyConfig(input: unknown, allowLoopback = false
if (authentication !== 'anonymous' && authentication !== 'feishu-test') {
throw new Error('desktop policy: authentication must be anonymous or feishu-test')
}
const authOrigins = value.allowedAuthOrigins
if (authentication === 'feishu-test' && (!Array.isArray(authOrigins) || authOrigins.length === 0)) {
throw new Error('desktop policy: test authentication requires nonempty allowedAuthOrigins')
}
if (authentication === 'anonymous' && authOrigins !== undefined) {
throw new Error('desktop policy: anonymous policy must not configure allowedAuthOrigins')
}
if (typeof jitter !== 'number' || !Number.isFinite(jitter) || jitter < 0 || jitter > 1 || maxBackoffMs < intervalMs) {
throw new Error('desktop policy: jitter must be in [0, 1] and maxBackoffMs must cover intervalMs')
}
return {
origin: origin(value.origin, authentication === 'anonymous' && allowLoopback),
allowedPageOrigins: value.allowedPageOrigins.map(item => origin(item, false)),
allowedAuthOrigins: authentication === 'feishu-test' ? (authOrigins as unknown[]).map(item => origin(item, false)) : [],
intervalMs, timeoutMs: duration('timeoutMs', 15_000), maxBackoffMs, jitter, authentication,
}
}
+4 -5
View File
@@ -3,9 +3,6 @@ import { randomUUID } from 'node:crypto'
import { BrowserWindow, session } from 'electron'
import type { DesktopLocale } from './locale.ts'
const FEISHU_ORIGINS = ['https://open.feishu.cn', 'https://accounts.feishu.cn',
'https://passport.feishu.cn', 'https://login.feishu.cn']
/** Packaged placeholder document; it is the window's first document and needs no network. */
const LOGIN_LOADING_PAGE = 'renderer/policy-login-loading.html'
@@ -28,11 +25,13 @@ export class DesktopPolicyTestAuth {
/**
* @param origin Validated HTTPS policy origin; login always starts at its root with fresh gateway state.
* @param allowedAuthOrigins Validated HTTPS origins for login document navigation.
* @param locale Shell-owned login title.
* @param parent Current application or mandatory-update window.
* @param record Fixed, nonsecret login outcomes for diagnostic evidence.
*/
constructor(private readonly origin: string, private readonly locale: DesktopLocale,
constructor(private readonly origin: string, private readonly allowedAuthOrigins: readonly string[],
private readonly locale: DesktopLocale,
private readonly parent: () => BrowserWindow | undefined,
private readonly record: (event: 'opened' | DesktopPolicyLoginResult) => void) {
this.browserSession.setPermissionRequestHandler((_contents, _permission, callback) => { callback(false) })
@@ -144,7 +143,7 @@ export class DesktopPolicyTestAuth {
let url: URL
try { url = new URL(value) } catch { return false }
return url.protocol === 'https:' && url.username === '' && url.password === ''
&& (url.origin === this.origin || FEISHU_ORIGINS.includes(url.origin))
&& (url.origin === this.origin || this.allowedAuthOrigins.includes(url.origin))
}
/**
@@ -6,7 +6,8 @@ import { loadDesktopPackageEnvironment, validateDesktopPackageEnvironment } from
const WINDOWS = { platform: 'win32', arch: 'x64' } as const
const MACOS = { platform: 'darwin', arch: 'arm64' } as const
const POLICY = { DSH_DESKTOP_MANDATORY_UPDATE_TEST_ORIGIN: 'https://policy.example.com' }
const POLICY = { DSH_DESKTOP_MANDATORY_UPDATE_TEST_ORIGIN: 'https://policy.example.com',
DSH_DESKTOP_MANDATORY_UPDATE_CONFIG: JSON.stringify({ allowedAuthOrigins: ['https://login.example.com'] }) }
const RELEASE = { ...POLICY, DSH_DESKTOP_APP_ID: 'com.example.desktop', DOWNLOAD_TEST_ORIGIN: 'https://updates.example.com' }
const MAC_IDENTITY = { DSH_DESKTOP_MACOS_SIGNING_IDENTITY: 'Example Company (TEAMID1234)', DSH_DESKTOP_MACOS_TEAM_ID: 'TEAMID1234' }
@@ -3,28 +3,32 @@ import { resolveDesktopPolicyEnvironment } from '../scripts/desktop-policy-envir
import { validateDesktopPackageEnvironment } from '../scripts/desktop-package-environment.mjs'
import { resolveDesktopPolicyConfig } from '../src/mandatory-update-policy.ts'
const origins = { DSH_DESKTOP_MANDATORY_UPDATE_TEST_ORIGIN: 'https://harness-test.deepseek.com',
DSH_DESKTOP_MANDATORY_UPDATE_PROD_ORIGIN: 'https://harness.deepseek.com' }
const origins = { DSH_DESKTOP_MANDATORY_UPDATE_TEST_ORIGIN: 'https://test.example.com',
DSH_DESKTOP_MANDATORY_UPDATE_PROD_ORIGIN: 'https://prod.example.com' }
const auth = { DSH_DESKTOP_MANDATORY_UPDATE_CONFIG: JSON.stringify({ allowedAuthOrigins: ['https://login.example.com'] }) }
it.each(['test', 'production'] as const)('selects the %s policy and authentication together', (deployment) => {
const policy = resolveDesktopPolicyEnvironment({ ...origins, DSH_DESKTOP_AUTO_UPDATE_ENV: deployment })
const policy = resolveDesktopPolicyEnvironment({ ...origins, ...(deployment === 'test' ? auth : {}), DSH_DESKTOP_AUTO_UPDATE_ENV: deployment })
const origin = deployment === 'test' ? origins.DSH_DESKTOP_MANDATORY_UPDATE_TEST_ORIGIN : origins.DSH_DESKTOP_MANDATORY_UPDATE_PROD_ORIGIN
expect(policy).toEqual({ origin, allowedPageOrigins: [origin], authentication: deployment === 'test' ? 'feishu-test' : 'anonymous' })
expect(policy).toEqual({ origin, allowedPageOrigins: [origin],
...(deployment === 'test' ? { allowedAuthOrigins: ['https://login.example.com'] } : {}),
authentication: deployment === 'test' ? 'feishu-test' : 'anonymous' })
expect(resolveDesktopPolicyConfig(policy)).toMatchObject(policy)
})
it('requires only the selected origin, defaults to test, and accepts explicit page restrictions', () => {
const policy = resolveDesktopPolicyEnvironment({
DSH_DESKTOP_MANDATORY_UPDATE_TEST_ORIGIN: origins.DSH_DESKTOP_MANDATORY_UPDATE_TEST_ORIGIN,
DSH_DESKTOP_MANDATORY_UPDATE_CONFIG: JSON.stringify({ allowedPageOrigins: ['https://download.deepseek.com'], intervalMs: 5000 }) })
expect(policy).toMatchObject({ authentication: 'feishu-test', intervalMs: 5000, allowedPageOrigins: ['https://download.deepseek.com'] })
DSH_DESKTOP_MANDATORY_UPDATE_CONFIG: JSON.stringify({ allowedAuthOrigins: ['https://login.example.com'],
allowedPageOrigins: ['https://download.example.com'], intervalMs: 5000 }) })
expect(policy).toMatchObject({ authentication: 'feishu-test', intervalMs: 5000, allowedPageOrigins: ['https://download.example.com'] })
expect(() => resolveDesktopPolicyEnvironment({ ...origins, DSH_DESKTOP_AUTO_UPDATE_ENV: 'prod' })).toThrow('production')
})
it.each([undefined, '', 'http://harness-test.deepseek.com', 'https://user:secret@harness-test.deepseek.com',
'https://harness-test.deepseek.com/api', 'https://harness-test.deepseek.com/?secret=value'])('rejects invalid selected origin %s', (origin) => {
expect(() => resolveDesktopPolicyEnvironment({ DSH_DESKTOP_MANDATORY_UPDATE_TEST_ORIGIN: origin })).toThrow('HTTPS origin')
expect(() => resolveDesktopPolicyEnvironment({ DSH_DESKTOP_MANDATORY_UPDATE_TEST_ORIGIN: origin })).not.toThrow('secret=value')
it.each([undefined, '', 'http://test.example.com', 'https://user:secret@test.example.com',
'https://test.example.com/api', 'https://test.example.com/?secret=value'])('rejects invalid selected origin %s', (origin) => {
expect(() => resolveDesktopPolicyEnvironment({ ...auth, DSH_DESKTOP_MANDATORY_UPDATE_TEST_ORIGIN: origin })).toThrow('HTTPS origin')
expect(() => resolveDesktopPolicyEnvironment({ ...auth, DSH_DESKTOP_MANDATORY_UPDATE_TEST_ORIGIN: origin })).not.toThrow('secret=value')
})
it.each(['{', 'null', '[]', '{"origin":"https://old.example.com"}', '{"authentication":"anonymous"}',
@@ -32,6 +36,17 @@ it.each(['{', 'null', '[]', '{"origin":"https://old.example.com"}', '{"authentic
expect(() => resolveDesktopPolicyEnvironment({ ...origins, DSH_DESKTOP_MANDATORY_UPDATE_CONFIG: options })).toThrow()
})
it.each([undefined, '[]', '["http://login.example.com"]', '["https://login.example.com/path"]',
'["https://user:[email protected]"]'])('rejects missing or invalid test login origins %s', (value) => {
const settings = value === undefined ? {} : { allowedAuthOrigins: JSON.parse(value) as unknown }
expect(() => resolveDesktopPolicyEnvironment({ ...origins, DSH_DESKTOP_MANDATORY_UPDATE_CONFIG: JSON.stringify(settings) })).toThrow()
})
it('rejects login origins in production', () => {
expect(() => resolveDesktopPolicyEnvironment({ ...origins, ...auth, DSH_DESKTOP_AUTO_UPDATE_ENV: 'production' }))
.toThrow('must not configure allowedAuthOrigins')
})
it.each([{ unsigned: true }, { prepareOnly: true }, {}])('fails before signing/preparation when policy is absent in %j', (options) => {
for (const platform of ['win32', 'darwin'] as const) {
expect(() => { validateDesktopPackageEnvironment({ DSH_DESKTOP_APP_ID: 'com.example.test' }, { platform, arch: 'x64' }, options) })
@@ -20,6 +20,7 @@ vi.mock('../scripts/windows-sign.mjs', () => ({
const versions = ['0.1.6-nightly.20260914.1', '0.1.6-nightly.20260914.2'] as const
const environment = { DSH_DESKTOP_AUTO_UPDATE_ENV: 'test', DOWNLOAD_TEST_ORIGIN: 'https://download-test.deepseek.com',
DSH_DESKTOP_MANDATORY_UPDATE_TEST_ORIGIN: 'https://policy.example.com',
DSH_DESKTOP_MANDATORY_UPDATE_CONFIG: JSON.stringify({ allowedAuthOrigins: ['https://login.example.com'] }),
DOWNLOAD_TEST_COS_BUCKET: 'bj-toc-download-test-1320056602' }
const require = createRequire(import.meta.url)
const { validateConfiguration } = require('app-builder-lib/out/util/config/config.js') as {
@@ -61,7 +61,7 @@ async function fixture(body: (context: {
await writeFile(join(source, 'package.json'), JSON.stringify({ name: `dsh-update-test-${run.id}`, version,
dshDesktopAppId: run.appId, main: 'qualification-bootstrap.mjs', type: 'module',
dshMandatoryUpdatePolicy: { origin: 'https://policy.example.com', allowedPageOrigins: ['https://policy.example.com'],
authentication: 'feishu-test' } }))
authentication: 'feishu-test', allowedAuthOrigins: ['https://login.example.com'] } }))
for (const name of ['electron-updater', 'semver']) {
await mkdir(join(source, 'node_modules', name), { recursive: true })
await cp(require.resolve(`${name}/package.json`), join(source, 'node_modules', name, 'package.json'))
@@ -16,7 +16,7 @@ describe('installer preparation preserves application dependencies', () => {
const { createElectronBuilderConfig } = await import('../scripts/electron-builder-config.mjs')
expect(() => createElectronBuilderConfig({ DSH_DESKTOP_APP_ID: 'com.example.installer',
DSH_DESKTOP_AUTO_UPDATE_ENV: 'production',
DSH_DESKTOP_MANDATORY_UPDATE_TEST_ORIGIN: 'https://harness-test.deepseek.com',
DSH_DESKTOP_MANDATORY_UPDATE_TEST_ORIGIN: 'https://test.example.com',
}, platform, 'x64')).toThrow('DSH_DESKTOP_MANDATORY_UPDATE_PROD_ORIGIN')
})
it.each(['win32', 'darwin'] as const)('keeps electron-builder responsible for node_modules on %s', async (platform) => {
@@ -24,6 +24,7 @@ describe('installer preparation preserves application dependencies', () => {
const env = {
DSH_DESKTOP_APP_ID: 'com.example.installer',
DSH_DESKTOP_MANDATORY_UPDATE_TEST_ORIGIN: 'https://policy.example.com',
DSH_DESKTOP_MANDATORY_UPDATE_CONFIG: JSON.stringify({ allowedAuthOrigins: ['https://login.example.com'] }),
DSH_DESKTOP_TARGET_PLATFORM: platform,
DSH_DESKTOP_TARGET_ARCH: 'x64',
DSH_DESKTOP_UNSIGNED: platform === 'win32' ? '1' : '0',
@@ -14,6 +14,7 @@ import {
const RELEASE_ENVIRONMENT = {
DSH_DESKTOP_APP_ID: 'com.example.desktop',
DSH_DESKTOP_MANDATORY_UPDATE_TEST_ORIGIN: 'https://policy.example.com',
DSH_DESKTOP_MANDATORY_UPDATE_CONFIG: JSON.stringify({ allowedAuthOrigins: ['https://login.example.com'] }),
DSH_DESKTOP_TARGET_PLATFORM: 'darwin',
DSH_DESKTOP_TARGET_ARCH: 'arm64',
DSH_DESKTOP_MACOS_SIGNING_IDENTITY: 'Example Company (TEAMID1234)',
@@ -97,6 +98,7 @@ describe('desktop macOS release signature', () => {
expect(() => createElectronBuilderConfig({
DSH_DESKTOP_APP_ID: RELEASE_ENVIRONMENT.DSH_DESKTOP_APP_ID,
DSH_DESKTOP_MANDATORY_UPDATE_TEST_ORIGIN: 'https://policy.example.com',
DSH_DESKTOP_MANDATORY_UPDATE_CONFIG: JSON.stringify({ allowedAuthOrigins: ['https://login.example.com'] }),
DSH_DESKTOP_TARGET_PLATFORM: 'win32',
}, 'win32')).toThrow(/DSH_DESKTOP_WINDOWS_CER_FILE/u)
})
@@ -106,6 +108,7 @@ describe('desktop macOS release signature', () => {
const config = createElectronBuilderConfig({
DSH_DESKTOP_APP_ID: RELEASE_ENVIRONMENT.DSH_DESKTOP_APP_ID,
DSH_DESKTOP_MANDATORY_UPDATE_TEST_ORIGIN: 'https://policy.example.com',
DSH_DESKTOP_MANDATORY_UPDATE_CONFIG: JSON.stringify({ allowedAuthOrigins: ['https://login.example.com'] }),
DSH_DESKTOP_TARGET_PLATFORM: 'win32',
DSH_DESKTOP_UNSIGNED: '1',
}, 'win32', 'x64')
+7 -7
View File
@@ -289,7 +289,7 @@ beforeEach(() => {
vi.spyOn(console, 'info').mockImplementation(() => {})
vi.stubEnv('DSH_DESKTOP_PNPM_ENTRY', 'test-pnpm')
vi.stubEnv('DSH_DESKTOP_DSH_DIR', 'test-runtime')
vi.stubGlobal('process', { ...process, platform: 'win32', resourcesPath: 'desktop-test-resources' })
vi.stubGlobal('process', { ...process, platform: 'win32', arch: 'x64', resourcesPath: 'desktop-test-resources' })
vi.stubEnv('DSH_DESKTOP_HOST_INSPECT_PORT', undefined)
vi.stubEnv('DSH_DESKTOP_MANDATORY_UPDATE_CONFIG', undefined)
vi.stubEnv('DSH_DESKTOP_UPDATE_JOURNAL_DIR', undefined)
@@ -340,7 +340,7 @@ describe('desktop main startup', () => {
})
it('shows one explained startup login before Host readiness and joins concurrent checks without reopening it', async () => {
harness.embeddedPolicy = { origin: 'https://policy.example.com', authentication: 'feishu-test',
harness.embeddedPolicy = { origin: 'https://policy.example.com', authentication: 'feishu-test', allowedAuthOrigins: ['https://login.example.com'],
allowedPageOrigins: ['https://downloads.example.com'], intervalMs: 1000, jitter: 0 }
vi.stubGlobal('fetch', vi.fn<typeof fetch>().mockImplementation(async () =>
Response.json({ error: { code: 'UNAUTHENTICATED' } }, { status: 401 })))
@@ -383,7 +383,7 @@ describe('desktop main startup', () => {
})
it.each(['returned', 'cancelled', 'failed'] as const)('requires explicit test login and handles %s without downloading', async (outcome) => {
harness.embeddedPolicy = { origin: 'https://policy.example.com', authentication: 'feishu-test',
harness.embeddedPolicy = { origin: 'https://policy.example.com', authentication: 'feishu-test', allowedAuthOrigins: ['https://login.example.com'],
allowedPageOrigins: ['https://downloads.example.com'], intervalMs: 10_000, jitter: 0 }
const request = vi.fn<typeof fetch>().mockImplementation(async () => Response.json({ error: { code: 'UNAUTHENTICATED' } }, { status: 401 }))
vi.stubGlobal('fetch', request)
@@ -410,7 +410,7 @@ describe('desktop main startup', () => {
})
it('does not open Feishu when the user declines test login', async () => {
harness.embeddedPolicy = { origin: 'https://policy.example.com', authentication: 'feishu-test',
harness.embeddedPolicy = { origin: 'https://policy.example.com', authentication: 'feishu-test', allowedAuthOrigins: ['https://login.example.com'],
allowedPageOrigins: ['https://downloads.example.com'] }
vi.stubGlobal('fetch', vi.fn<typeof fetch>().mockImplementation(async () => Response.json({ error: { code: 'UNAUTHENTICATED' } }, { status: 401 })))
await readyForUpdate()
@@ -420,7 +420,7 @@ describe('desktop main startup', () => {
})
it('does not require gateway login to download an already available ordinary update', async () => {
harness.embeddedPolicy = { origin: 'https://policy.example.com', authentication: 'feishu-test',
harness.embeddedPolicy = { origin: 'https://policy.example.com', authentication: 'feishu-test', allowedAuthOrigins: ['https://login.example.com'],
allowedPageOrigins: ['https://downloads.example.com'] }
vi.stubGlobal('fetch', vi.fn<typeof fetch>().mockImplementation(async () =>
Response.json({ error: { code: 'UNAUTHENTICATED' } }, { status: 401 })))
@@ -433,7 +433,7 @@ describe('desktop main startup', () => {
})
it('retains the same blocking window and running Host after expired test login is cancelled', async () => {
harness.embeddedPolicy = { origin: 'https://policy.example.com', authentication: 'feishu-test',
harness.embeddedPolicy = { origin: 'https://policy.example.com', authentication: 'feishu-test', allowedAuthOrigins: ['https://login.example.com'],
allowedPageOrigins: ['https://downloads.example.com'], intervalMs: 1000, jitter: 0 }
const request = vi.fn<typeof fetch>().mockImplementation(async () =>
Response.json({ error: { code: 'UNAUTHENTICATED' } }, { status: 401 }))
@@ -932,7 +932,7 @@ describe('desktop main startup', () => {
})
it('queues policy authentication until the ordinary result dialog closes', async () => {
harness.embeddedPolicy = { origin: 'https://policy.example.com', authentication: 'feishu-test',
harness.embeddedPolicy = { origin: 'https://policy.example.com', authentication: 'feishu-test', allowedAuthOrigins: ['https://login.example.com'],
allowedPageOrigins: ['https://downloads.example.com'], intervalMs: 10_000, jitter: 0 }
const request = vi.fn<typeof fetch>()
.mockResolvedValueOnce(Response.json({ code: 0, data: { biz_code: 0, biz_data: null } }))
@@ -13,7 +13,7 @@ const instances: DesktopMandatoryUpdatePolicy[] = []
function fixture(authentication: 'anonymous' | 'feishu-test' = 'anonymous') {
const request = vi.fn<typeof fetch>().mockResolvedValue(Response.json(clear))
const publish = vi.fn<(state: DesktopPolicyState) => void>()
const policy = new DesktopMandatoryUpdatePolicy(resolveDesktopPolicyConfig({ ...deployment, authentication })!,
const policy = new DesktopMandatoryUpdatePolicy(resolveDesktopPolicyConfig({ ...deployment, authentication, ...(authentication === 'feishu-test' ? { allowedAuthOrigins: ['https://login.example.com'] } : {}) })!,
identity, publish, request)
instances.push(policy)
return { policy, request, publish }
@@ -162,6 +162,15 @@ describe('mandatory update policy', () => {
})
describe('policy deployment and page validation', () => {
it('requires validated login origins only for test authentication', () => {
expect(() => resolveDesktopPolicyConfig({ ...deployment, authentication: 'feishu-test' })).toThrow('allowedAuthOrigins')
expect(() => resolveDesktopPolicyConfig({ ...deployment, allowedAuthOrigins: ['https://login.example.com'] })).toThrow('anonymous')
expect(() => resolveDesktopPolicyConfig({ ...deployment, authentication: 'feishu-test',
allowedAuthOrigins: ['https://login.example.com/path'] })).toThrow('HTTPS origin')
expect(resolveDesktopPolicyConfig({ ...deployment, authentication: 'feishu-test',
allowedAuthOrigins: ['https://login.example.com'] })?.allowedAuthOrigins).toEqual(['https://login.example.com'])
})
it('requires explicit production origins and confines HTTP to an explicit local policy fixture', () => {
expect(resolveDesktopPolicyConfig(undefined)).toBeUndefined()
expect(() => resolveDesktopPolicyConfig({ ...deployment, origin: 'http://127.0.0.1:19001' })).toThrow('HTTPS')
+6 -6
View File
@@ -35,7 +35,7 @@ beforeEach(() => {
vi.clearAllMocks()
browserSession = makeSession(); window = makeWindow()
native.partition.mockReturnValue(browserSession); native.create.mockReturnValue(window)
auth = new DesktopPolicyTestAuth('https://policy.example.com', resolveDesktopLocale('zh'), () => undefined, record)
auth = new DesktopPolicyTestAuth('https://policy.example.com', ['https://login.example.com'], resolveDesktopLocale('zh'), () => undefined, record)
})
afterEach(async () => { await auth.dispose() })
@@ -64,7 +64,7 @@ it('opens a sandboxed window on explicit action and coalesces logins without aut
expect(window.loadURL).not.toHaveBeenCalled()
await vi.waitFor(() => { expect(window.loadURL).toHaveBeenCalledWith('https://policy.example.com/') })
expect(window.loadFile).toHaveBeenCalledTimes(1)
window.webContents.emit('did-navigate', {}, 'https://accounts.feishu.cn/open-apis/authen/v1/index?state=secret')
window.webContents.emit('did-navigate', {}, 'https://login.example.com/open-apis/authen/v1/index?state=secret')
expect(window.isDestroyed()).toBe(false)
window.webContents.emit('did-navigate', {}, 'https://policy.example.com/')
expect(await pending).toBe('returned')
@@ -121,7 +121,7 @@ it('cancels a login closed while the placeholder is still loading, without start
it.each(['will-navigate', 'will-redirect'])('refuses unapproved %s targets without logging OAuth data', async (eventName) => {
const pending = auth.login()
const event = { preventDefault: vi.fn() }
window.webContents.emit(eventName, event, 'https://accounts.feishu.cn.evil.example/?code=secret')
window.webContents.emit(eventName, event, 'https://login.example.com.evil.example/?code=secret')
expect(event.preventDefault).toHaveBeenCalledOnce()
expect(await pending).toBe('failed')
expect(record.mock.calls).toEqual([['opened'], ['failed']])
@@ -144,8 +144,8 @@ it('rejects child-frame escapes, permissions, downloads, new windows and HTTP au
expect(await pending).toBe('failed')
})
it.each(['https://open.feishu.cn/', 'https://accounts.feishu.cn/', 'https://passport.feishu.cn/',
'https://login.feishu.cn/'])('allows reviewed login documents: %s', (url) => {
it.each(['https://login.example.com/', 'https://login.example.com/', 'https://login.example.com/',
'https://login.example.com/'])('allows reviewed login documents: %s', (url) => {
const callback = vi.fn()
browserSession.webRequest.onBeforeRequest.mock.calls[0]![0]({ resourceType: 'mainFrame', url }, callback)
expect(callback).toHaveBeenCalledWith({ cancel: false })
@@ -157,7 +157,7 @@ it('keeps manual login open until close, reports cancellation once, and permits
expect(await pending).toBe('cancelled')
window = makeWindow(); native.create.mockReturnValue(window)
const retry = auth.login()
window.webContents.emit('did-fail-load', {}, -105, 'raw URL error', 'https://accounts.feishu.cn/?state=secret', true)
window.webContents.emit('did-fail-load', {}, -105, 'raw URL error', 'https://login.example.com/?state=secret', true)
expect(await retry).toBe('failed')
expect(record.mock.calls).toEqual([['opened'], ['cancelled'], ['opened'], ['failed']])
})
@@ -166,6 +166,7 @@ it.each([true, false])('validates the real builder hook for unsigned=%s', async
await writeFile(certificate, 'fixture public certificate')
const config = createElectronBuilderConfig({
DSH_DESKTOP_APP_ID: 'com.example.unpack', DSH_DESKTOP_MANDATORY_UPDATE_TEST_ORIGIN: 'https://policy.example.com',
DSH_DESKTOP_MANDATORY_UPDATE_CONFIG: JSON.stringify({ allowedAuthOrigins: ['https://login.example.com'] }),
DSH_DESKTOP_TARGET_PLATFORM: 'win32', DSH_DESKTOP_TARGET_ARCH: 'x64', DSH_DESKTOP_UNSIGNED: unsigned ? '1' : '0',
DSH_DESKTOP_WINDOWS_CER_FILE: certificate, DOWNLOAD_TEST_ORIGIN: 'https://updates.example.com',
}, 'win32', 'x64', input.source)
@@ -51,6 +51,7 @@ describe('Windows update publisher', () => {
beforeAll(() => {
vi.stubEnv('DSH_DESKTOP_APP_ID', 'com.example.publisher-test')
vi.stubEnv('DSH_DESKTOP_MANDATORY_UPDATE_TEST_ORIGIN', 'https://policy.example.com')
vi.stubEnv('DSH_DESKTOP_MANDATORY_UPDATE_CONFIG', JSON.stringify({ allowedAuthOrigins: ['https://login.example.com'] }))
vi.stubEnv('DSH_DESKTOP_TARGET_PLATFORM', 'win32')
vi.stubEnv('DSH_DESKTOP_UNSIGNED', '1')
})