feat: add computer control and managed browser

This commit is contained in:
lofyer
2026-08-05 12:55:24 +08:00
parent 2f549387a6
commit 38ac2206f2
92 changed files with 21028 additions and 766 deletions
@@ -0,0 +1,111 @@
import { describe, expect, it } from 'vitest'
import {
buildDesktopHelperEnvironment,
type SecurePathMetadata,
type SessionEnvironmentFileSystem
} from './session-environment'
const safeMetadata = (
canonicalPath: string,
kind: 'file' | 'directory'
): SecurePathMetadata => ({
canonicalPath,
uid: 1000,
mode: kind === 'file' ? 0o600 : 0o700,
isDirectory: kind === 'directory',
isFile: kind === 'file',
isSymbolicLink: false
})
const fileSystem = (
overrides: Partial<SecurePathMetadata> = {}
): SessionEnvironmentFileSystem => ({
inspect: async (path) => ({
...safeMetadata(
path,
path.endsWith('.Xauthority') ? 'file' : 'directory'
),
...overrides
})
})
describe('buildDesktopHelperEnvironment', () => {
it('constructs a minimal fresh helper environment', async () => {
const environment = await buildDesktopHelperEnvironment({
uid: 1000,
fileSystem: fileSystem(),
source: {
PATH: '/usr/bin:/bin',
LANG: 'zh_CN.UTF-8',
DISPLAY: ':0.0',
WAYLAND_DISPLAY: 'wayland-1',
XDG_RUNTIME_DIR: '/run/user/1000',
DBUS_SESSION_BUS_ADDRESS: 'unix:path=/run/user/1000/bus',
XAUTHORITY: '/home/user/.Xauthority',
NO_AT_BRIDGE: '1',
LD_PRELOAD: '/tmp/inject.so',
NODE_OPTIONS: '--require /tmp/inject.js',
GTK_MODULES: 'inject',
QT_PLUGIN_PATH: '/tmp/plugins',
HTTPS_PROXY: 'http://proxy.invalid',
API_KEY: 'secret'
}
})
expect(environment).toEqual({
PATH: '/usr/bin:/bin',
LANG: 'zh_CN.UTF-8',
DISPLAY: ':0.0',
WAYLAND_DISPLAY: 'wayland-1',
XDG_RUNTIME_DIR: '/run/user/1000',
DBUS_SESSION_BUS_ADDRESS: 'unix:path=/run/user/1000/bus',
XAUTHORITY: '/home/user/.Xauthority',
NO_AT_BRIDGE: '1'
})
})
it.each([
['symbolic link', { isSymbolicLink: true }],
['world writable', { mode: 0o707 }],
['group writable', { mode: 0o720 }],
['wrong owner', { uid: 1001 }],
['non-canonical path', { canonicalPath: '/different' }]
])('rejects an unsafe %s runtime path', async (_name, override) => {
await expect(
buildDesktopHelperEnvironment({
uid: 1000,
fileSystem: fileSystem(override),
source: { XDG_RUNTIME_DIR: '/run/user/1000' }
})
).rejects.toThrow('Unsafe directory path')
})
it.each([
{ DISPLAY: 'host:abc' },
{ WAYLAND_DISPLAY: '../wayland-0', XDG_RUNTIME_DIR: '/run/user/1000' },
{ WAYLAND_DISPLAY: 'wayland-0' },
{ DBUS_SESSION_BUS_ADDRESS: 'unix:path=/run/user/1000/bus,bad' },
{ DBUS_SESSION_BUS_ADDRESS: 'unix:path=/run/user/1000/bus;tcp:' },
{ LANG: 'en_US.UTF-8\nINJECTED=1' },
{ PATH: '/usr/bin:relative/bin' },
{ PATH: '/usr/bin::/bin' }
])('rejects malformed desktop environment input', async (source) => {
await expect(
buildDesktopHelperEnvironment({
uid: 1000,
fileSystem: fileSystem(),
source
})
).rejects.toThrow()
})
it('does not forward values that merely resemble safe opt-ins', async () => {
await expect(
buildDesktopHelperEnvironment({
uid: 1000,
fileSystem: fileSystem(),
source: { NO_AT_BRIDGE: '0', LD_LIBRARY_PATH: '/tmp' }
})
).resolves.toEqual({})
})
})