Skip to content

fix: pin what signLegacyUpgradeTx signs; 0.19.1 - #22

Merged
kalepail merged 1 commit into
mainfrom
fix/legacy-sign-pin
Sep 17, 2026
Merged

kalepail merged 1 commit into
mainfrom
fix/legacy-sign-pin

Conversation

@kalepail

Copy link
Copy Markdown
Contributor

Why

Round-3 independent review of 0.19.0 found that signLegacyUpgradeTx pinned the auth entry to the transaction's own top-level call on the named wallet with no sub-invocations, but not to the function name or the target hash. A hostile transaction rooted at another wallet-admin call (update_signer, add_signer, or update_contract_code to an arbitrary hash) could be signed through this path. That matters most for patched legacy wallets, which have no other remote takeover route.

What

  • signLegacyUpgradeTx accepts only update_contract_code or migrate_signers, and an update_contract_code may only carry the canonical legacy-line target (expectedTarget overrides). Two new tests.
  • buildLegacyUpgradeTx refuses a wallet already on the target.
  • inspectLegacyWallet counts an entry as live through liveUntil inclusive.
  • LegacyWalletError points at the kit's own helpers, not only the 0.10.20–0.12.x line.
  • Docs: README capability sentence and method rows; guide application section, protocol-23 auto-restore note, policy-signer boundary, migrate timing.

Live verification

The full SDK path was run on testnet against wallets deployed from the real mainnet bytecode of both cohorts (inspectLegacyWalletbuildLegacyUpgradeTxsignLegacyUpgradeTx → relayer-style {func, auth} submit → buildLegacyMigrateTxget_signer). Both wallets moved to c079d3a4…; the bare cohort's migrate_signers rewrote one entry then zero; an unauthenticated update_signer fails on-chain afterwards. Wallets and tx ids are in docs/legacy-wallet-upgrade.md.

Suite: 363 passing; legacy contract tests 8/8.

🤖 Generated with Claude Code

0.19.0 pinned the legacy upgrade auth entry to the transaction's own
top-level call on the named wallet with no sub-invocations, but not to the
function name or the target hash. A hostile transaction rooted at another
wallet-admin call (update_signer, add_signer, update_contract_code to an
arbitrary hash) could therefore be signed through this path, which matters
for patched legacy wallets that have no other remote takeover route.

- signLegacyUpgradeTx now accepts only update_contract_code or
  migrate_signers, and an update_contract_code may only carry the canonical
  legacy-line target (expectedTarget overrides). Two new tests.
- buildLegacyUpgradeTx refuses a wallet already on the target.
- inspectLegacyWallet counts an entry as live through liveUntil inclusive.
- LegacyWalletError points at the kit's own helpers.
- Docs: README capability sentence and method rows; guide application
  section, protocol-23 auto-restore, policy-signer boundary, migrate timing,
  and the live testnet verification of both cohorts through the SDK path.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Copilot AI balanced review requested due to automatic review settings September 17, 2026 21:58

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Changes recommended

The upgrade guide still recommends vulnerable version 0.19.0, and the corrected TTL boundary lacks a regression test.

Get a fresh assessment by requesting another Copilot review.

Pull request overview

Pins legacy-upgrade signing to approved functions and WASM targets while improving legacy-wallet guidance and versioning.

Changes:

  • Restricts legacy signing and fixes TTL boundary handling.
  • Rejects redundant upgrades and adds regression tests.
  • Updates documentation and releases version 0.19.1.
File summaries
File Description
src/version.ts Bumps generated version.
src/kit/legacy-ops.ts Pins signed functions and upgrade targets.
src/kit/legacy-ops.test.ts Tests signing restrictions.
src/kit.ts Rejects redundant upgrades and exposes target override.
src/kit.test.ts Tests redundant-upgrade rejection.
src/errors.ts Improves legacy-wallet recovery guidance.
README.md Documents legacy helper APIs.
package.json Bumps package version.
docs/releasing.md Updates release version reference.
docs/legacy-wallet-upgrade.md Expands upgrade and verification guidance.
contracts-legacy/smart-wallet/src/tests/mod.rs Corrects legacy test documentation.
CHANGELOG.md Records 0.19.1 changes.
Review details
  • Files reviewed: 12/12 changed files
  • Comments generated: 2
  • Review effort level: Balanced

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

with an Ed25519 signer key the wallet holds), and submit it through your
relayer or a funded source. The current SDK (`0.17.0` and later) does not
connect to these wallets and cannot be used for this step.
relayer or a funded source. Or use the current SDK (`0.19.0` and later): it
Comment thread src/kit/legacy-ops.ts
const liveUntil = entry.liveUntilLedgerSeq;
liveByKey.set(entry.key.toXDR("base64"), liveUntil !== undefined && liveUntil > latest);
// An entry is live through its liveUntil ledger inclusive.
liveByKey.set(entry.key.toXDR("base64"), liveUntil !== undefined && liveUntil >= latest);
@kalepail
kalepail merged commit 421c845 into main Sep 17, 2026
10 checks passed
@kalepail
kalepail deleted the fix/legacy-sign-pin branch September 17, 2026 22:04
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants