fix(switch-agent): use native tui.selectSession, use cross-platform symlink test target

This commit is contained in:
ismeth
2026-03-14 18:51:00 +01:00
committed by YeonGyu-Kim
parent 68cd8814d8
commit 37f03b4238
2 changed files with 18 additions and 20 deletions
+10 -4
View File
@@ -106,12 +106,18 @@ describe("athena-sisyphus-only hook", () => {
expect(isAllowedPath(symlinkPath, tempWorkspaceRoot)).toBe(false) expect(isAllowedPath(symlinkPath, tempWorkspaceRoot)).toBe(false)
}) })
it("#then rejects symlink inside .sisyphus/ pointing to /etc/passwd", async () => { it("#then rejects symlink inside .sisyphus/ pointing to file outside workspace", async () => {
const symlinkPath = join(tempWorkspaceRoot, ".sisyphus", "passwd-link") const outsideTarget = join(tmpdir(), "athena-outside-target.txt")
const symlinkPath = join(tempWorkspaceRoot, ".sisyphus", "outside-link")
await symlink("/etc/passwd", symlinkPath) await writeFile(outsideTarget, "outside-content", "utf-8")
try {
await symlink(outsideTarget, symlinkPath)
expect(isAllowedPath(symlinkPath, tempWorkspaceRoot)).toBe(false) expect(isAllowedPath(symlinkPath, tempWorkspaceRoot)).toBe(false)
} finally {
await rm(outsideTarget, { force: true })
}
}) })
it("#then allows a regular file inside .sisyphus/tmp/", async () => { it("#then allows a regular file inside .sisyphus/tmp/", async () => {
+8 -16
View File
@@ -12,12 +12,8 @@ const DESCRIPTION =
const ALLOWED_AGENTS = new Set<string>(SWITCHABLE_AGENT_NAMES) const ALLOWED_AGENTS = new Set<string>(SWITCHABLE_AGENT_NAMES)
type TuiClient = { type TuiService = {
post: (input: { selectSession: (input?: { sessionID?: string }) => Promise<unknown>
url: string
body: { sessionID: string }
headers?: Record<string, string>
}) => Promise<unknown>
} }
type SessionClient = { type SessionClient = {
@@ -52,24 +48,20 @@ function extractSessionId(response: unknown): string | undefined {
return undefined return undefined
} }
function hasTuiClient(client: SessionClient): client is SessionClient & { _client: TuiClient } { function hasTuiService(client: SessionClient): client is SessionClient & { tui: TuiService } {
const maybeClient = Reflect.get(client as object, "_client") const maybeTui = Reflect.get(client as object, "tui")
if (typeof maybeClient !== "object" || maybeClient === null) { if (typeof maybeTui !== "object" || maybeTui === null) {
return false return false
} }
return typeof Reflect.get(maybeClient, "post") === "function" return typeof Reflect.get(maybeTui, "selectSession") === "function"
} }
async function navigateTuiToSession(client: SessionClient, sessionID: string): Promise<boolean> { async function navigateTuiToSession(client: SessionClient, sessionID: string): Promise<boolean> {
if (!hasTuiClient(client)) { if (!hasTuiService(client)) {
return false return false
} }
try { try {
await client._client.post({ await client.tui.selectSession({ sessionID })
url: "/tui/select-session",
body: { sessionID },
headers: { "Content-Type": "application/json" },
})
return true return true
} catch { } catch {
return false return false