From 2bed06ca643498852b49c924996b71f7681cc28d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=9B=B7=E7=94=B5=E8=8A=BD=E8=A1=A3?= Date: Thu, 16 Apr 2026 22:58:01 -0400 Subject: [PATCH] security(agent-auth): rotate signing key on every launch --- src/main/agent-auth.ts | 27 +++++++++------------------ 1 file changed, 9 insertions(+), 18 deletions(-) diff --git a/src/main/agent-auth.ts b/src/main/agent-auth.ts index 7fc15a80..4f6fa2f9 100644 --- a/src/main/agent-auth.ts +++ b/src/main/agent-auth.ts @@ -1,5 +1,5 @@ import { createHmac, randomBytes, timingSafeEqual } from "node:crypto"; -import { mkdirSync, readFileSync, writeFileSync } from "node:fs"; +import { mkdirSync, writeFileSync } from "node:fs"; import { join } from "node:path"; let signingKey: Buffer | null = null; @@ -56,24 +56,15 @@ function getSigningKey() { } export function initializeAgentTokenSigningKey(profileDir: string) { - const keyPath = join(profileDir, "agent-api-signing.key"); + // Rotate the signing key on every launch. Any token issued by a prior + // process becomes unverifiable — agent sessions re-authenticate when + // they resume, so the cost of invalidating in-flight tokens is bounded + // and the blast radius of a leaked key is "until next restart". mkdirSync(profileDir, { recursive: true }); - try { - const nextKey = randomBytes(32); - writeFileSync(keyPath, nextKey, { flag: "wx", mode: 0o600 }); - signingKey = nextKey; - return; - } catch (error) { - const code = (error as NodeJS.ErrnoException).code; - if (code !== "EEXIST") { - throw error; - } - } - const persistedKey = readFileSync(keyPath); - if (persistedKey.length !== 32) { - throw new Error(`Invalid agent API signing key length at ${keyPath}. Expected 32 bytes, received ${persistedKey.length}.`); - } - signingKey = persistedKey; + const keyPath = join(profileDir, "agent-api-signing.key"); + const nextKey = randomBytes(32); + writeFileSync(keyPath, nextKey, { mode: 0o600 }); + signingKey = nextKey; } export function createAgentToken(agentId: string, companyId: string, runId: string): string {