pending-vouched-devices.ts

2.3 KB · raw

1// Pending vouched devices — audit fix MV-7.2//3// Compatibility reader for pending devices created by older builds. Current4// same-vault manifests are admitted directly, but stored requests remain5// actionable until the user confirms or rejects them in Settings.6//7// Storage: chrome.storage.local, same locality rules as paired-devices.ts —8// these records contain public keys and must not roam through a third-party9// sync back-end.1011export interface PendingVouchedDevice {12  deviceId: string;13  deviceName: string;14  deviceType: string;15  longTermPublicKeyHex: string;16  signingPublicKeyHex: string;17  wsPort?: number;18  /** pairedAt claimed by the vouching manifest — carried through on confirm. */19  pairedAt: number;20  receivedAt: number;21  /** deviceId of the extension whose signed manifest vouched for this device. */22  vouchedByDeviceId: string;23}2425const PENDING_KEY = "peach_pending_vouched_devices_v1";2627export async function listPendingVouchedDevices(): Promise<PendingVouchedDevice[]> {28  try {29    const result = await chrome.storage.local.get(PENDING_KEY);30    const value = (result as Record<string, unknown>)[PENDING_KEY];31    if (!Array.isArray(value)) return [];32    return value.filter(isPendingVouchedDevice);33  } catch {34    return [];35  }36}3738export async function getPendingVouchedDevice(39  deviceId: string,40): Promise<PendingVouchedDevice | null> {41  const pending = await listPendingVouchedDevices();42  return pending.find((d) => d.deviceId === deviceId) ?? null;43}4445export async function removePendingVouchedDevice(deviceId: string): Promise<void> {46  const pending = await listPendingVouchedDevices();47  await chrome.storage.local.set({48    [PENDING_KEY]: pending.filter((d) => d.deviceId !== deviceId),49  });50}5152function isPendingVouchedDevice(x: unknown): x is PendingVouchedDevice {53  if (typeof x !== "object" || x === null) return false;54  const r = x as Record<string, unknown>;55  return (56    typeof r.deviceId === "string" && !!r.deviceId &&57    typeof r.deviceName === "string" &&58    typeof r.deviceType === "string" &&59    typeof r.longTermPublicKeyHex === "string" &&60    /^[0-9a-f]{64}$/.test(r.longTermPublicKeyHex as string) &&61    typeof r.signingPublicKeyHex === "string" &&62    /^[0-9a-f]{64}$/.test(r.signingPublicKeyHex as string) &&63    typeof r.receivedAt === "number" &&64    typeof r.vouchedByDeviceId === "string"65  );66}