types.ts

6.8 KB · raw

1/**2 * types.ts — cross-boundary types and the `MessageType` union.3 *4 * `MessageType` mixes background-bound and content-script-bound messages.5 * Background-bound types have a case in `src/background/messages.ts`.6 * Content-script-bound types (PING, SUMMON_AUTOFILL, VAULT_LOCKED,7 * RESTART_AUTO_LOCK, etc.) are sent *to* content scripts or the popup and8 * have no handler in messages.ts by design — that absence is not a missing9 * handler. Add new cross-boundary types here, not inline in call sites.10 */1112export interface ManualSummonRecord {13  firstSummonedAt: number;14  summonCount: number;15  lastSummonedAt: number;16}1718export interface Canary {19  webhookUrl: string;20  label?: string;21  lastPinged?: number;22  pingCount?: number;23}2425export type CardNetwork = "visa" | "mastercard" | "amex" | "discover" | "unknown";2627export interface VaultEntry {28  id: string; // crypto.randomUUID()29  type?: "login" | "note" | "passkey" | "card"; // defaults to "login" for existing entries30  title: string;31  username?: string; // only for login entries32  password?: string; // only for login entries33  url?: string; // only for login entries34  /**35   * Additional sites this entry should match for autofill. Login entries only.36   * Each value is a full origin or URL (e.g. `https://login.foo.com`).37   * The vault domain index keys against the hostname of every entry in this38   * list, in addition to `url`.39   */40  additionalUrls?: string[];41  totp?: string; // base32 seed, stored encrypted — only for login entries42  notes?: string;43  breach?: number; // HIBP exposure count; 0 or undefined = clean — only for login entries44  canary?: Canary;45  createdAt: number; // Date.now()46  modifiedAt: number;47  deletedAt?: number;48  tags?: string[];49  rpId?: string;50  rpName?: string;51  credentialId?: string;52  publicKeyCose?: string;53  privateKeyJwk?: JsonWebKey;54  userHandle?: string;55  userName?: string;56  userDisplayName?: string;57  algorithm?: -7 | -8;58  signCount?: number;59  aaguid?: string;60  backupEligible?: boolean;61  backupState?: boolean;62  lastUsedAt?: number;63  // Card entry fields64  cardholderName?: string;65  cardNumber?: string;       // full PAN, stored encrypted in vault66  expMonth?: string;67  expYear?: string;68  cvc?: string;69  cardNickname?: string;70  network?: CardNetwork;71  last4?: string;72}7374export interface LegacyVaultEntry {75  id: string;76  title: string;77  username: string;78  password: string;79  url: string;80  totp?: string;81  notes?: string;82  breach?: number;83  created: number;84  updated: number;85  deletedAt?: number;86}8788export interface CardEntry extends VaultEntry {89  type: "card";90  cardholderName: string;91  cardNumber: string;92  expMonth: string;93  expYear: string;94  cvc: string;95}9697export interface PasskeyEntry extends VaultEntry {98  type: "passkey";99  rpId: string;100  rpName: string;101  credentialId: string;102  publicKeyCose: string;103  privateKeyJwk: JsonWebKey;104  userHandle: string;105  userName: string;106  userDisplayName: string;107  algorithm: -7 | -8;108  signCount: number;109  aaguid: string;110  backupEligible: boolean;111  backupState: boolean;112  lastUsedAt?: number;113}114115export interface PendingPasskeyRequest {116  requestId: number;117  type: "create" | "get";118  rpId: string;119  rpName: string;120  userName: string;121  userDisplayName?: string;122}123124export interface PasskeyProviderState {125  supported: boolean;126  enabled: boolean;127  attached: boolean;128  browserLabel: string;129  statusLabel: string;130  conflict?: string;131}132133export interface StoredVault {134  version: 1; // AAD for AES-GCM --- prevents downgrade135  salt: string; // hex --- 16 bytes, CSPRNG, stored not secret136  nonce: string; // hex --- 12 bytes, CSPRNG, fresh per write137  ciphertext: string; // hex --- AES-256-GCM output138  tag: string; // hex --- 16-byte GCM auth tag139}140141// Background-bound message types — sent from popup/content scripts,142// handled in src/background/messages.ts. Everything in this group must143// have a matching case in that file's switch statement.144export type MessageType =145  | "UNLOCK"146  | "LOCK"147  | "IS_UNLOCKED"148  | "HAS_VAULT"149  | "GET_ENTRIES"150  | "ADD_ENTRY"151  | "BATCH_ADD_ENTRIES"152  | "RESTORE_ENTRIES"153  | "CLEAR_VAULT_ENTRIES"154  | "UPDATE_ENTRY"155  | "ADD_ADDITIONAL_URL"156  | "DELETE_ENTRY"157  | "AUTOFILL_QUERY"158  | "COPY"159  | "CREATE_VAULT"160  | "GET_ENTRY_FOR_FILL"161  | "CHECK_BREACH"162  | "CHECK_ALL_BREACHES"163  | "GET_SETTINGS"164  | "UPDATE_SETTINGS"165  | "FIRE_CANARY_PING"166  | "ACTIVATE_LICENSE"167  | "GET_LICENSE"168  | "IS_DURESS_ENABLED"169  | "SETUP_DURESS"170  | "DISABLE_DURESS"171  | "VERIFY_MAIN_PASSWORD"172  | "CHANGE_PASSWORD"173  | "GET_SITE_PASSWORD_POLICY"174  | "GET_SYNC_STATE"175  | "TRIGGER_SYNC"176  | "START_PAIRING"177  | "CANCEL_PAIRING"178  | "ACCEPT_PAIRING"179  | "REMOVE_PAIRED_DEVICE"180  | "REMOVE_TRUSTED_NETWORK"181  | "MARK_NETWORK_TRUSTED"182  | "GET_PENDING_PASSKEY"183  | "APPROVE_PASSKEY"184  | "DENY_PASSKEY"185  | "DELETE_PASSKEY"186  | "GET_PASSKEYS"187  | "GET_PASSKEY_PROVIDER_STATE"188  | "SET_PASSKEY_PROVIDER_ENABLED"189  | "GET_DELETED_ENTRIES"190  | "RESTORE_ENTRY"191  | "PURGE_ENTRY"192  | "PURGE_EXPIRED_TRASH"193  | "LIST_VAULT_SNAPSHOTS"194  | "RESTORE_VAULT_SNAPSHOT"195  | "COUNT_DUPLICATES"196  | "DEDUPLICATE_ENTRIES"197  | "RECORD_MANUAL_SUMMON"198  | "DECLINE_CARD_SAVE"199  | "IS_CARD_DECLINED"200  | "GET_VAULT_STATUS"201  // Content-script-bound message types — sent BY the background TO content202  // scripts. They never reach messages.ts's switch. Kept in this union so203  // existing call sites type-check; do not add cases for them in the204  // background handler.205  | "PING"            // background → content: liveness probe206  | "SUMMON_AUTOFILL" // background → content: open the in-page palette207  | "VAULT_LOCKED"    // background → content/popup: vault just locked208  | "VAULT_UNLOCKED"; // background → content/popup: vault just unlocked209210export const ACCENT_COLORS = [211  { name: "peach",    hex: "#F49C70" },212  { name: "rose",     hex: "#FF8AA6" },213  { name: "plum",     hex: "#D597F2" },214  { name: "lavender", hex: "#A6A8FF" },215  { name: "sky",      hex: "#5BC8EE" },216  { name: "mint",     hex: "#5EE0B0" },217  { name: "sage",     hex: "#B5D870" },218  { name: "butter",   hex: "#FFD45A" },219  { name: "sand",     hex: "#EDBC85" },220  { name: "clay",     hex: "#F08660" },221  { name: "slate",    hex: "#80BEDE" },222  { name: "stone",    hex: "#C8B8A8" },223] as const;224225export type AccentColorName = typeof ACCENT_COLORS[number]["name"];226227export interface PasswordPolicy {228  minLength?: number;229  maxLength?: number;230  requiresUppercase?: boolean;231  requiresLowercase?: boolean;232  requiresNumbers?: boolean;233  requiresSymbols?: boolean;234  forbidsSymbols?: boolean;235  allowedSymbols?: string;236  /** Characters the site explicitly disallows. Filtered from every pool before generation. */237  forbiddenChars?: string;238}239240export interface Message {241  type: MessageType;242  payload?: unknown;243}244245export interface MessageResponse {246  ok: boolean;247  data?: unknown;248  error?: string;249  unlocked?: boolean;250}