types.ts

7.9 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  /**45   * When the password field was last changed. Set on first-write of `password`46   * and refreshed on every subsequent change. Defaults to `createdAt` when47   * legacy entries are read by the advanced health analyser.48   */49  passwordChangedAt?: number;50  canary?: Canary;51  createdAt: number; // Date.now()52  modifiedAt: number;53  deletedAt?: number;54  tags?: string[];55  rpId?: string;56  rpName?: string;57  credentialId?: string;58  publicKeyCose?: string;59  privateKeyJwk?: JsonWebKey;60  userHandle?: string;61  userName?: string;62  userDisplayName?: string;63  algorithm?: -7 | -8;64  signCount?: number;65  aaguid?: string;66  backupEligible?: boolean;67  backupState?: boolean;68  lastUsedAt?: number;69  // Card entry fields70  cardholderName?: string;71  cardNumber?: string;       // full PAN, stored encrypted in vault72  expMonth?: string;73  expYear?: string;74  cvc?: string;75  cardNickname?: string;76  network?: CardNetwork;77  last4?: string;78}7980export interface LegacyVaultEntry {81  id: string;82  title: string;83  username: string;84  password: string;85  url: string;86  totp?: string;87  notes?: string;88  breach?: number;89  created: number;90  updated: number;91  deletedAt?: number;92}9394export interface CardEntry extends VaultEntry {95  type: "card";96  cardholderName: string;97  cardNumber: string;98  expMonth: string;99  expYear: string;100  cvc: string;101}102103export interface PasskeyEntry extends VaultEntry {104  type: "passkey";105  rpId: string;106  rpName: string;107  credentialId: string;108  publicKeyCose: string;109  privateKeyJwk: JsonWebKey;110  userHandle: string;111  userName: string;112  userDisplayName: string;113  algorithm: -7 | -8;114  signCount: number;115  aaguid: string;116  backupEligible: boolean;117  backupState: boolean;118  lastUsedAt?: number;119}120121export interface PendingPasskeyRequest {122  requestId: number;123  type: "create" | "get";124  rpId: string;125  rpName: string;126  userName: string;127  userDisplayName?: string;128}129130export interface PasskeyProviderState {131  supported: boolean;132  enabled: boolean;133  attached: boolean;134  browserLabel: string;135  statusLabel: string;136  conflict?: string;137}138139export interface StoredVault {140  version: 1; // AAD for AES-GCM --- prevents downgrade141  salt: string; // hex --- 16 bytes, CSPRNG, stored not secret142  nonce: string; // hex --- 12 bytes, CSPRNG, fresh per write143  ciphertext: string; // hex --- AES-256-GCM output144  tag: string; // hex --- 16-byte GCM auth tag145}146147// Background-bound message types — sent from popup/content scripts,148// handled in src/background/messages.ts. Everything in this group must149// have a matching case in that file's switch statement.150export type MessageType =151  | "UNLOCK"152  | "LOCK"153  | "IS_UNLOCKED"154  | "HAS_VAULT"155  | "GET_ENTRIES"156  | "ADD_ENTRY"157  | "BATCH_ADD_ENTRIES"158  | "RESTORE_ENTRIES"159  | "CLEAR_VAULT_ENTRIES"160  | "UPDATE_ENTRY"161  | "ADD_ADDITIONAL_URL"162  | "DELETE_ENTRY"163  | "AUTOFILL_QUERY"164  | "COPY"165  | "CREATE_VAULT"166  | "GET_ENTRY_FOR_FILL"167  | "CHECK_BREACH"168  | "CHECK_ALL_BREACHES"169  | "GET_SETTINGS"170  | "UPDATE_SETTINGS"171  | "FIRE_CANARY_PING"172  | "ACTIVATE_LICENSE"173  | "GET_LICENSE"174  | "IS_DURESS_ENABLED"175  | "SETUP_DURESS"176  | "DISABLE_DURESS"177  | "VERIFY_MAIN_PASSWORD"178  | "CHANGE_PASSWORD"179  | "GET_SITE_PASSWORD_POLICY"180  | "GET_SYNC_STATE"181  | "TRIGGER_SYNC"182  | "START_PAIRING"183  | "CANCEL_PAIRING"184  | "ACCEPT_PAIRING"185  | "REMOVE_PAIRED_DEVICE"186  | "REMOVE_TRUSTED_NETWORK"187  | "MARK_NETWORK_TRUSTED"188  | "GET_PENDING_PASSKEY"189  | "APPROVE_PASSKEY"190  | "DENY_PASSKEY"191  | "DELETE_PASSKEY"192  | "GET_PASSKEYS"193  | "GET_PASSKEY_PROVIDER_STATE"194  | "SET_PASSKEY_PROVIDER_ENABLED"195  | "GET_DELETED_ENTRIES"196  | "RESTORE_ENTRY"197  | "PURGE_ENTRY"198  | "PURGE_EXPIRED_TRASH"199  | "LIST_VAULT_SNAPSHOTS"200  | "RESTORE_VAULT_SNAPSHOT"201  | "COUNT_DUPLICATES"202  | "DEDUPLICATE_ENTRIES"203  | "RECORD_MANUAL_SUMMON"204  | "DECLINE_CARD_SAVE"205  | "IS_CARD_DECLINED"206  | "SUBMIT_FEEDBACK"207  | "GET_VAULT_STATUS"208  | "GET_ADVANCED_HEALTH_REPORT"209  | "INITIATE_PASSWORD_CHANGE"210  | "DEACTIVATE_LICENSE"211  // Content-script-bound message types — sent BY the background TO content212  // scripts. They never reach messages.ts's switch. Kept in this union so213  // existing call sites type-check; do not add cases for them in the214  // background handler.215  | "PING"            // background → content: liveness probe216  | "SUMMON_AUTOFILL" // background → content: open the in-page palette217  | "VAULT_LOCKED"    // background → content/popup: vault just locked218  | "VAULT_UNLOCKED"  // background → content/popup: vault just unlocked219  | "PRO_GATE_TRIGGERED"; // background → popup: a Pro feature was attempted on free220221export type ProFeature =222  | "sync"223  | "passkeys"224  | "advanced_health"225  | "smart_password_change"226  | "ios_app";227228export interface AdvancedHealthEntryReport {229  id: string;230  title: string;231  domain: string;232  url?: string;233  score: number;234  ageDays: number;235  reasons: string[];236  factors: {237    breached: boolean;238    breachCount: number;239    reused: boolean;240    aged: boolean;241    weak: boolean;242    missingTotpOnSensitive: boolean;243  };244}245246export interface AdvancedHealthReport {247  vaultScore: number;248  generatedAt: number;249  entryCount: number;250  reusedGroups: number;251  agedCount: number;252  remediation: AdvancedHealthEntryReport[];253}254255export const ACCENT_COLORS = [256  { name: "peach",    hex: "#F49C70" },257  { name: "rose",     hex: "#FF8AA6" },258  { name: "plum",     hex: "#D597F2" },259  { name: "lavender", hex: "#A6A8FF" },260  { name: "sky",      hex: "#5BC8EE" },261  { name: "mint",     hex: "#5EE0B0" },262  { name: "sage",     hex: "#B5D870" },263  { name: "butter",   hex: "#FFD45A" },264  { name: "sand",     hex: "#EDBC85" },265  { name: "clay",     hex: "#F08660" },266  { name: "slate",    hex: "#80BEDE" },267  { name: "stone",    hex: "#C8B8A8" },268] as const;269270export type AccentColorName = typeof ACCENT_COLORS[number]["name"];271272export interface PasswordPolicy {273  minLength?: number;274  maxLength?: number;275  requiresUppercase?: boolean;276  requiresLowercase?: boolean;277  requiresNumbers?: boolean;278  requiresSymbols?: boolean;279  forbidsSymbols?: boolean;280  allowedSymbols?: string;281  /** Characters the site explicitly disallows. Filtered from every pool before generation. */282  forbiddenChars?: string;283}284285export interface Message {286  type: MessageType;287  payload?: unknown;288}289290export interface MessageResponse {291  ok: boolean;292  data?: unknown;293  error?: string;294  unlocked?: boolean;295}