@anywalletkit/extension-kit 0.1.3 → 0.1.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -36,7 +36,7 @@ pnpm add @anywalletkit/extension-kit @anywalletkit/core @anywalletkit/keyring
36
36
  | Vault | `Vault`, `chromeLocalVaultStorage`, `memoryVaultStorage`, `VAULT_SCRYPT`, `VAULT_LIMITS`, `VAULT_MIN_PASSWORD_LENGTH` | every secret at rest in `chrome.storage.local`, two key layers (a KEK wraps a DEK, the DEK seals each secret); passkey (WebAuthn PRF) unlock as a second wrap of the same DEK |
37
37
  | Session | `Session`, `chromeSessionStorage`, `chromeAlarms`, `SESSION_DEFAULT_AUTOLOCK_MS` | the DEK in `chrome.storage.session` with an expiry and an alarm; `getKey()` re-checks the expiry itself |
38
38
  | Keyring adapters | `chromeKeyringStorage`, `vaultSecretStorage` | `@anywalletkit/keyring` metadata as plain JSON, secrets through the vault, one DEK use per call |
39
- | Relay | `PageRequest` / `RelayRequest` shapes and guards, `stampOrigin`, `checkSenderOrigin`, `RELAY_ERRORS` | page ⇄ content script ⇄ service worker messages; the origin is the browser's (`port.sender`), never the page's |
39
+ | Relay | `PageRequest` / `RelayRequest` shapes and guards, `pageRequestFrom`, `stampOrigin`, `checkSenderOrigin`, `RELAY_ERRORS` | page ⇄ content script ⇄ service worker messages; the content script takes only messages the page window itself posted (`pageRequestFrom(event, window)`: an iframe's `parent.postMessage` is not the site), and the origin is the browser's (`port.sender`), never the page's |
40
40
  | Router | `RequestRouter`, `READ_METHODS`, `SIGNING_METHODS`, `toRelayError` | what a dApp may call: accounts only for connected origins, chain add / switch through the approval window, reads from the wallet's own RPCs, `eth_sign` never offered, everything else 4200 |
41
41
  | Permissions | `PermissionStore`, `CHAIN_ACCOUNT_KEYS` | per-origin accounts (EVM, Solana, TON, Tron, Bitcoin, NEAR) and chain |
42
42
  | Approvals | `ApprovalQueue`, `ReviewDeclined`, `QueueLimitError`, `APPROVALS_MAX_PENDING_PER_ORIGIN` | one approval window at a time, at most 3 pending requests per origin and one pending `connect`; transport notes (`setVia`) for WalletConnect peers |
@@ -45,7 +45,7 @@ pnpm add @anywalletkit/extension-kit @anywalletkit/core @anywalletkit/keyring
45
45
  | Review gate | `ReviewGate`, `evmCallMatcher`, `chainTxMatcher` | after one sheet reviewed a whole plan, exactly those operations pass once within a TTL |
46
46
  | Swaps | `quoteRoutes`, `reviewSwapPlan`, `executionFloor`, `SwapsStore`, `SwapTracker`, `parseSwapId` | progressive quotes over swap2 providers, one review sheet per plan, pending swaps resumed across worker restarts |
47
47
  | Earn | `reviewEarnPlan`, `earnPlanMatchers` | one sheet for an Earn module's plan (approvals + call) |
48
- | TON Connect | `parseConnectRequest`, `parseSendTransaction`, `parseSignData`, `tonProofMessage`, `tonSignDataMessage` | the pure-data half of TON Connect v2 (the bytes `ton_proof` and `signData` sign) |
48
+ | TON Connect | `parseConnectRequest`, `parseSendTransaction`, `parseSignData`, `tonProofMessage`, `tonSignDataMessage` | the pure-data half of TON Connect v2 (the digests `ton_proof` and `signData` sign, to verify them); the account signs with `signTonProof` / `signTonConnectData` (wallet-ton) — `sign()` refuses 32-byte digests |
49
49
  | Phishing | `PhishingDetector` | MetaMask `eth-phishing-detect` list format, shipped with the build (never fetched at runtime), fuzzy lookalikes, brand-in-label, punycode |
50
50
  | SIWE | `parseSiwe`, `siweProblems`, `describeSiwe` | EIP-4361 sign-in messages: domain vs. the asking site, address, chain, expiry, nonce (warnings) |
51
51
  | Address poisoning | `lookalikeOf`, `RecipientBook` | warns when a new recipient imitates one the user confirmed |
@@ -76,8 +76,11 @@ pnpm add @anywalletkit/extension-kit @anywalletkit/core @anywalletkit/keyring
76
76
  - **Errors to pages.** `toRelayError(e, { log })` passes the wallet's own refusals
77
77
  (EIP-1193 codes) and a node's JSON-RPC error (code, revert data) with URLs
78
78
  removed; anything else is `-32603 Internal error` on the page and goes to `log`.
79
- - **Origins.** The service worker answers a relay message only when the
80
- content script's stamped origin equals `port.sender.origin`. Permissions are
79
+ - **Origins.** The content script relays only what the page window itself
80
+ posted (`pageRequestFrom`: `event.source` is the page, `event.origin` its
81
+ origin), so an ad or embed in an iframe cannot speak as the site. The
82
+ service worker answers a relay message only when the content script's
83
+ stamped origin equals `port.sender.origin`. Permissions are
81
84
  keyed by that origin, never by a URL a page or a WalletConnect peer claims.
82
85
  - **Requests.** Everything that reveals accounts or moves funds goes through
83
86
  the approval queue; blind signing (`eth_sign`) is not offered; typed data for
package/dist/index.cjs CHANGED
@@ -115,6 +115,7 @@ __export(src_exports, {
115
115
  newRequestId: () => newRequestId,
116
116
  nonEvmChain: () => nonEvmChain,
117
117
  normalizeHost: () => normalizeHost,
118
+ pageRequestFrom: () => pageRequestFrom,
118
119
  parseAddChainParams: () => parseAddChainParams,
119
120
  parseConnectRequest: () => parseConnectRequest,
120
121
  parseRawAddress: () => parseRawAddress,
@@ -316,7 +317,7 @@ var Vault = class {
316
317
  * Register a passkey as a second unlock path: the DEK (from the unlocked session)
317
318
  * is sealed under HKDF-SHA256(`prf`, salt = credential id). The password stays
318
319
  * the recovery path; `prf` is zeroized here. A v1 envelope must be unlocked (and
319
- * thereby upgraded) first.
320
+ * thereby upgraded) first, and the vault must hold a secret (it proves the DEK).
320
321
  */
321
322
  async addPasskey(dek, passkey) {
322
323
  try {
@@ -433,11 +434,14 @@ var Vault = class {
433
434
  const kek = await this._deriveScrypt(password, salt, kdf);
434
435
  return { v: 2, kdf, dek: await this._seal(kek, dek, aadDekV2(kdf)), secrets, passkeys, createdAt };
435
436
  }
436
- /** The caller's DEK must open this envelope's secrets — it is the key the password protects, not any 32 bytes. */
437
+ /**
438
+ * The caller's DEK must open this envelope's secrets — it is the key the password protects, not
439
+ * any 32 bytes. An empty vault has nothing to prove it with, so it is refused rather than trusted.
440
+ */
437
441
  async _requireDek(dek, envelope) {
438
442
  const ids = Object.keys(envelope.secrets);
439
443
  const key = await this._importDek(dek);
440
- if (ids.length === 0) return;
444
+ if (ids.length === 0) throw new VaultError("Add a secret to the vault before a passkey: an empty vault cannot prove the key is its own.", "invalid-input");
441
445
  const probe = await this._open(key, envelope.secrets[ids[0]], aadSecret(ids[0]), "locked");
442
446
  (0, import_core.zeroize)(probe);
443
447
  }
@@ -749,6 +753,10 @@ function isRelayToContent(x) {
749
753
  function newRequestId() {
750
754
  return crypto.randomUUID();
751
755
  }
756
+ function pageRequestFrom(event, page) {
757
+ if (event.source !== page || event.origin !== page.location.origin) return null;
758
+ return isPageRequest(event.data) ? event.data : null;
759
+ }
752
760
  function stampOrigin(request, origin) {
753
761
  const { id, chain, method } = request;
754
762
  return "params" in request ? { kind: "request", id, origin, chain, method, params: request.params } : { kind: "request", id, origin, chain, method };