Repository navigation
security(anchor): close on-chain audit findings (init_config takeover, escrow decoy, rent misroute) - #313
Merged
Conversation
…r, escrow decoy, rent misroute On-chain re-audit of the settlement program surfaced three confirmed fund-safety issues. All fixes compile via `anchor build --no-idl` (SBF). H-1 (HIGH) — init_config front-run takeover `init_config` was first-call-wins with an unconstrained `admin` signer, so on a fresh deployment anyone could land the first call, make themselves admin, and install their own arbitrator multisig — then drive every disputed job to themselves and drain escrow + bonds. Now gated on the program's upgrade authority: the call carries the program + its ProgramData account, and the handler requires `program_data.upgrade_authority == admin`. No hardcoded key; the deployer is the only valid initializer across devnet/mainnet/CI. IDL + SDK client + init-config.mjs + test updated to pass the two new accounts (ProgramData = PDA [programId] under the BPF upgradeable loader). H-2 (HIGH) — escrow token account not PDA-pinned (decoy substitution) cancel_job / finalize_payment / resolve_dispute constrained `escrow_token_account` only by owner+mint, not by its canonical PDA seeds. An attacker could pass a self-created empty token account (same mint, authority = job_escrow): the handler would transfer nothing, close the decoy, close the job PDA, and permanently strand the real escrow — an unauthenticated, repeatable, permanent fund-loss on any expired Accepted job. All three paths now pin `seeds = [b"escrow_token", job_escrow], bump`, matching create_job and the already-pinned bond account. I-1 (INFO) — escrow rent misrouted on finalize finalize_payment refunded the escrow token-account rent (paid by the poster in create_job) to the taker. Now refunds to the poster, consistent with cancel_job and resolve_dispute. Note: the on-chain IDL is regenerated by `anchor build` in CI's pinned toolchain (host rustc here can't run anchor 0.30.1's IDL step); the bundled IDL copies were patched by hand to mirror the expected output and validated against the Anchor coder. Program must be rebuilt + redeployed for these to take effect on-chain (mainnet milestone).
|
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
buy_claim already blocked buyer == seller but not buyer == poster. Since the poster funds the escrow, buying the claim lets them route the escrow payout back to themselves and then grief the taker via a dispute. Add a require! mirroring the existing BuyerIsSeller guard. Compiles via `anchor build --no-idl`.
7 tasks
This branch was successfully deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
On-chain program security re-audit — 3 confirmed findings fixed
A focused re-audit of the Anchor settlement program (real-money layer) surfaced 2 HIGH fund-safety bugs and 1 INFO consistency defect. All three are fixed here. The program compiles via
anchor build --no-idl(SBF toolchain); the host rustc in this env can't run anchor 0.30.1's IDL step (pre-existing toolchain mismatch), so CI regenerates the canonical IDL on build.H-1 (HIGH) —
init_configfront-run takeoverinit_configwas first-call-wins with an unconstrainedadmin: Signer. On a fresh deployment an attacker could land the first call, become admin, install their own arbitrator multisig, then drive every disputed job to themselves and drain escrow + bonds.Fix: gate on the program's upgrade authority. The instruction now carries the program + its
ProgramDataaccount and requiresprogram_data.upgrade_authority_address == admin. No hardcoded key — the deployer is the only valid initializer across devnet/mainnet/CI.init_config.rs: addedprogram: Program<Covenant>+program_data: Account<ProgramData>with constraints.[programId]under the BPF upgradeable loader):sdk/src/client.ts,app/scripts/init-config.mjs,tests/covenant.ts.app/lib,sdk/idl,sdk/src/idl) and validated against the Anchor coder.H-2 (HIGH) — escrow token account not PDA-pinned (decoy substitution)
cancel_job/finalize_payment/resolve_disputeconstrainedescrow_token_accountonly by owner+mint, not by its canonical PDA seeds. An attacker could pass a self-created empty token account (same mint, authority =job_escrow): the handler transfers nothing, closes the decoy, closes the job PDA, and permanently strands the real escrow — unauthenticated, repeatable, permanent fund loss on any expired Accepted job (cancel_jobPath B is callable by anyone after the deadline).Fix: all three paths now pin
seeds = [b"escrow_token", job_escrow], bump, matchingcreate_joband the already-pinned bond account.I-1 (INFO) — escrow rent misrouted on finalize
finalize_paymentrefunded the escrow token-account rent (paid by the poster increate_job) to the taker. Now refunds to the poster, consistent withcancel_jobandresolve_dispute.Verification
anchor build --no-idl→ exit 0 (SBF program compiles with all changes).tsc --noEmit→ exit 0.node --check app/scripts/init-config.mjs→ OK.@coral-xyz/anchorBorshInstructionCoder.admin, config, program, program_data, system_program(matches Rust field order).Test plan
anchor teston a localnet validator: init_config succeeds for the deployer, fails for a non-upgrade-authority signer.cancel_jobwith a substituted empty token account is rejected by the seed constraint.