Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -188,6 +188,17 @@ describe('limits on relay-era CLI calls', () => {
expect(body(replay).result.message).toEqual(body(first).result.message)
})

it('bounds the rows an inbox read may return', async () => {
const read = vi.spyOn(db, 'getAllMessagesForHandle')
for (const limit of ['-1', '1000000000', '0']) {
await server().cli(
{ argv: ['orchestration', 'inbox', '--limit', limit, '--json'], env: ENV },
new AbortController().signal
)
}
expect(read.mock.calls.map((call) => call[1])).toEqual([1, 100, 1])
})

it('names nobody when two hosts claim one handle', () => {
const terminal = { handle: WORKER, paneKey: WORKER_PANE, processIncarnation: 'p-1' }
const fresh = new OrcaRuntimeService()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,13 @@ export function optionalAskTimeout(timeoutMs: number | undefined): number | unde
return timeoutMs === undefined ? undefined : clampOrchestrationAskTimeoutMs(timeoutMs)
}

/** The inbox handler passes `limit` straight to SQL, where a negative one means "every row". */
const MAX_INBOX_ROWS = 100

export function cappedInboxLimit(limit: number | undefined): number | undefined {
return limit === undefined ? undefined : Math.min(Math.max(1, Math.floor(limit)), MAX_INBOX_ROWS)
}

export const LEGACY_RELAY_CLI_BUSY =
'This host already has too many older-terminal orca commands waiting on this desktop. ' +
'Let some finish, then try again.'
Expand Down
24 changes: 22 additions & 2 deletions src/main/ssh/legacy-relay-bridge/legacy-relay-cli-caller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ export function resolveLegacyRelayCliCaller(
runtime: OrcaRuntimeService,
env: Readonly<Record<string, string>>,
livePane: (paneKey: string) => LegacyRelayLivePane | null,
targetId: string,
/** A retry of a mutation this caller already completed, which may outlive its Dispatch. */
replay?: { fingerprint: string; requestId: string }
): LegacyRelayCliCaller {
Expand Down Expand Up @@ -76,7 +77,7 @@ export function resolveLegacyRelayCliCaller(
const replayDispatch = replay
? own(settledLegacyDispatch(db, handle, paneKey, replay.requestId))
: undefined
const run = resolveCoordinatedRun(db, handle, paneKey)
const run = resolveCoordinatedRun(db, handle, paneKey, targetId)
if (!dispatch && !replayDispatch && !run) {
throw new Error(LEGACY_RELAY_CLI_UNVERIFIED)
}
Expand Down Expand Up @@ -123,7 +124,8 @@ function settledLegacyDispatch(
function resolveCoordinatedRun(
db: OrchestrationDb,
handle: string,
paneKey: string
paneKey: string,
targetId: string
): RunRow | undefined {
const bound = db.getCurrentRunForPane(paneKey)
if (bound) {
Expand All @@ -135,11 +137,29 @@ function resolveCoordinatedRun(
principal?.status === 'committed' &&
principal.terminal_handle === handle &&
samePane(principal.pane_key, paneKey) &&
principalHostTargetId(principal.host_scope) === targetId &&
db.resolveLegacyCoordinatorCandidate({ runId: adopted, terminalHandle: handle, paneKey })
? db.getRun(adopted)
: undefined
}

/** The SSH target a committed principal attested from; a pane key alone is not unique per host. */
function principalHostTargetId(hostScope: string): string | undefined {
try {
const parsed: unknown = JSON.parse(hostScope)
return parsed &&
typeof parsed === 'object' &&
'kind' in parsed &&
parsed.kind === 'ssh' &&
'targetId' in parsed &&
typeof parsed.targetId === 'string'
? parsed.targetId
: undefined
} catch {
return undefined
}
}

function outside(subject: string): Error {
return new Error(
`A terminal started by an older version of Orca can reach only its own orchestration, and ${subject} is outside it.`
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -153,6 +153,22 @@ describe('the relay-era orca CLI on migrated legacy orchestration records', () =
expect(database.getLegacyCoordinatorPrincipal(adoptedRunId)).toBeUndefined()
})

it('refuses a legacy coordinator whose principal was attested from another host', async () => {
const { runtime, database } = legacyRuntime()
database.commitLegacyCompatibilityPrincipal({
runId: database.getLegacyAdoption()?.adopted_run_id ?? '',
role: 'coordinator',
hostScope: JSON.stringify({ kind: 'ssh', targetId: 'another-host' }),
terminalHandle: COORDINATOR,
paneKey: COORDINATOR_PANE,
launchTokenHash: createHash('sha256').update('coordinator-token').digest('hex'),
processIncarnation: `${COORDINATOR_PTY}:inc-2`
})
await expect(
run(runtime, ['orchestration', 'check', '--json'], coordinatorEnv())
).rejects.toThrow(LEGACY_RELAY_CLI_UNVERIFIED)
})

it('admits a legacy coordinator bound to this pane, and the worker reports to it', async () => {
const { runtime, database, taskId, dispatchId } = legacyRuntime()
database.commitLegacyCompatibilityPrincipal({
Expand Down
5 changes: 3 additions & 2 deletions src/main/ssh/legacy-relay-bridge/legacy-relay-cli-server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ import {
} from './legacy-relay-cli-args'
import type { LegacyRelayCliServer } from './legacy-relay-client'
import {
cappedInboxLimit,
cappedWait,
isExactLegacyRelayCliReplay,
optionalAskTimeout,
Expand Down Expand Up @@ -126,7 +127,7 @@ function buildCall(
case 'orchestration inbox':
return {
method: 'orchestration.inbox',
params: { ...self, limit: numberFlag(flags, 'limit') }
params: { ...self, limit: cappedInboxLimit(numberFlag(flags, 'limit')) }
}
case 'orchestration send':
return {
Expand Down Expand Up @@ -246,7 +247,7 @@ function admit(
}
const fingerprint = callerFingerprint(deps.targetId, env)
const replay = replayRequestId ? { fingerprint, requestId: replayRequestId } : undefined
const caller = resolveLegacyRelayCliCaller(runtime, env, deps.livePane, replay)
const caller = resolveLegacyRelayCliCaller(runtime, env, deps.livePane, deps.targetId, replay)
return { runtime, caller, env, fingerprint, signal }
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -126,7 +126,7 @@ export function SshHostAdvancedFields({
<p className="text-muted-foreground">
{translate(
'auto.components.settings.SshTargetForm.allowRemoteCliControlReset',
'Orca turned this off after an update, because it now lets this host drive your desktop browser. Turn it on again only if you want that.'
'Orca turned this off after an update, because it now lets this host drive your desktop browser and lets terminals started by an older Orca on this host run orchestration commands. Turn it on again only if you want that.'
)}
</p>
) : null}
Expand Down
2 changes: 1 addition & 1 deletion src/renderer/src/i18n/locales/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -8993,7 +8993,7 @@
"53e9aabfc0": "Uses OpenSSH multiplexing when available. Turn off for hosts with custom SSH restrictions.",
"allowRemoteCliControl": "Allow this host's orca CLI to control Orca",
"allowRemoteCliControlHelp": "When on, orca commands run on this host can open and drive Orca browser tabs on this desktop, only in this host's workspaces and only while it is connected. Terminals started by an older Orca on this host can also run orchestration commands (check, inbox, send, ask, reply) for their own supervised work. They cannot read your files, clipboard or cookies. When off, they reach only this host's own Orca server.",
"allowRemoteCliControlReset": "Orca turned this off after an update, because it now lets this host drive your desktop browser. Turn it on again only if you want that.",
"allowRemoteCliControlReset": "Orca turned this off after an update, because it now lets this host drive your desktop browser and lets terminals started by an older Orca on this host run orchestration commands. Turn it on again only if you want that.",
"editTitle": "Edit SSH host",
"addTitle": "Add SSH host",
"editDescription": "Update connection details for this machine. Changes apply on next connect.",
Expand Down
2 changes: 1 addition & 1 deletion src/renderer/src/i18n/locales/es.json
Original file line number Diff line number Diff line change
Expand Up @@ -8720,7 +8720,7 @@
"editingPrefix": "Editando",
"allowRemoteCliControl": "Permitir que la orca CLI de este host controle Orca",
"allowRemoteCliControlHelp": "Cuando está activado, los comandos orca ejecutados en este host pueden abrir y controlar pestañas del navegador de Orca en este escritorio, solo en los espacios de trabajo de este host y solo mientras esté conectado. Las terminales iniciadas por una versión anterior de Orca en este host también pueden ejecutar comandos de orquestación (check, inbox, send, ask, reply) para su propio trabajo supervisado. No pueden leer tus archivos, tu portapapeles ni tus cookies. Cuando está desactivado, solo llegan al propio servidor de Orca de este host.",
"allowRemoteCliControlReset": "Orca lo desactivó tras una actualización, porque ahora permite que este host controle el navegador de tu escritorio. Vuelve a activarlo solo si lo deseas."
"allowRemoteCliControlReset": "Orca lo desactivó tras una actualización, porque ahora permite que este host controle el navegador de tu escritorio y que las terminales iniciadas por una versión anterior de Orca en este host ejecuten comandos de orquestación. Vuelve a activarlo solo si lo deseas."
},
"TasksPane": {
"f71d8a9dd3": "Proveedores de tareas",
Expand Down
2 changes: 1 addition & 1 deletion src/renderer/src/i18n/locales/fr.json
Original file line number Diff line number Diff line change
Expand Up @@ -8870,7 +8870,7 @@
"editingPrefix": "Modification",
"allowRemoteCliControl": "Autoriser l'orca CLI de cet hôte à contrôler Orca",
"allowRemoteCliControlHelp": "Lorsque cette option est activée, les commandes orca exécutées sur cet hôte peuvent ouvrir et piloter des onglets du navigateur Orca sur ce bureau, uniquement dans les espaces de travail de cet hôte et uniquement tant qu'il est connecté. Les terminaux lancés par une ancienne version d'Orca sur cet hôte peuvent aussi exécuter des commandes d'orchestration (check, inbox, send, ask, reply) pour leur propre travail supervisé. Elles ne peuvent pas lire vos fichiers, votre presse-papiers ni vos cookies. Lorsqu'elle est désactivée, elles n'atteignent que le serveur Orca de cet hôte.",
"allowRemoteCliControlReset": "Orca l'a désactivé après une mise à jour, car il permet désormais à cet hôte de piloter le navigateur de votre bureau. Réactivez-le seulement si vous le souhaitez."
"allowRemoteCliControlReset": "Orca l'a désactivé après une mise à jour, car il permet désormais à cet hôte de piloter le navigateur de votre bureau et aux terminaux lancés par une ancienne version d'Orca sur cet hôte d'exécuter des commandes d'orchestration. Réactivez-le seulement si vous le souhaitez."
},
"TasksPane": {
"f71d8a9dd3": "Fournisseurs de tâches",
Expand Down
2 changes: 1 addition & 1 deletion src/renderer/src/i18n/locales/ja.json
Original file line number Diff line number Diff line change
Expand Up @@ -8746,7 +8746,7 @@
"editingPrefix": "編集中",
"allowRemoteCliControl": "このホストの orca CLI による Orca の操作を許可する",
"allowRemoteCliControlHelp": "オンにすると、このホストで実行される orca コマンドは、このホストのワークスペース内に限り、接続中のみ、このデスクトップの Orca ブラウザのタブを開いて操作できます。このホストで古い Orca が起動したターミナルも、自身の監督下の作業についてオーケストレーションコマンド(check、inbox、send、ask、reply)を実行できます。ファイル、クリップボード、Cookie は読み取れません。オフの場合、このホスト自身の Orca サーバーにのみアクセスします。",
"allowRemoteCliControlReset": "アップデート後に Orca がこれをオフにしました。この設定でこのホストがデスクトップのブラウザを操作できるようになったためです。必要な場合のみ再度オンにしてください。"
"allowRemoteCliControlReset": "アップデート後に Orca がこれをオフにしました。この設定でこのホストがデスクトップのブラウザを操作でき、このホストで古い Orca が起動したターミナルがオーケストレーションコマンドを実行できるようになったためです。必要な場合のみ再度オンにしてください。"
},
"TasksPane": {
"f71d8a9dd3": "タスクプロバイダー",
Expand Down
2 changes: 1 addition & 1 deletion src/renderer/src/i18n/locales/ko.json
Original file line number Diff line number Diff line change
Expand Up @@ -8709,7 +8709,7 @@
"editingPrefix": "편집 중",
"allowRemoteCliControl": "이 호스트의 orca CLI가 Orca를 제어하도록 허용",
"allowRemoteCliControlHelp": "켜면 이 호스트에서 실행한 orca 명령이 이 호스트의 작업 공간에서만, 연결되어 있는 동안에만 이 데스크톱의 Orca 브라우저 탭을 열고 조작할 수 있습니다. 이 호스트에서 이전 버전의 Orca가 시작한 터미널도 자신이 감독받는 작업에 대해 오케스트레이션 명령(check, inbox, send, ask, reply)을 실행할 수 있습니다. 파일, 클립보드, 쿠키는 읽을 수 없습니다. 끄면 이 호스트 자체의 Orca 서버에만 접근합니다.",
"allowRemoteCliControlReset": "업데이트 후 Orca가 이 설정을 껐습니다. 이제 이 호스트가 데스크톱 브라우저를 조작할 수 있게 되었기 때문입니다. 원하는 경우에만 다시 켜세요."
"allowRemoteCliControlReset": "업데이트 후 Orca가 이 설정을 껐습니다. 이제 이 호스트가 데스크톱 브라우저를 조작할 수 있고, 이 호스트에서 이전 버전의 Orca가 시작한 터미널이 오케스트레이션 명령을 실행할 수 있게 되었기 때문입니다. 원하는 경우에만 다시 켜세요."
},
"TasksPane": {
"f71d8a9dd3": "작업 제공자",
Expand Down
2 changes: 1 addition & 1 deletion src/renderer/src/i18n/locales/zh.json
Original file line number Diff line number Diff line change
Expand Up @@ -8709,7 +8709,7 @@
"editingPrefix": "正在编辑",
"allowRemoteCliControl": "允许此主机的 orca CLI 控制 Orca",
"allowRemoteCliControlHelp": "开启后,在此主机上运行的 orca 命令可以打开并操作此桌面上的 Orca 浏览器标签页,但仅限此主机的工作区,且仅在主机连接期间。此主机上由旧版 Orca 启动的终端也可以为其自身受监督的工作运行编排命令(check、inbox、send、ask、reply)。它们无法读取你的文件、剪贴板或 Cookie。关闭时,它们只能访问此主机自身的 Orca 服务器。",
"allowRemoteCliControlReset": "更新后 Orca 关闭了此项,因为它现在允许此主机操作你桌面上的浏览器。仅在需要时再重新开启。"
"allowRemoteCliControlReset": "更新后 Orca 关闭了此项,因为它现在允许此主机操作你桌面上的浏览器,并允许此主机上由旧版 Orca 启动的终端运行编排命令。仅在需要时再重新开启。"
},
"TasksPane": {
"f71d8a9dd3": "任务提供者",
Expand Down
Loading