audit-algorand
Perform structured security audits and adversarial reviews of TypeScript Algorand applications and LogicSigs compiled with PuyaTs. Use for vulnerability assessments, threat models, exploit analysis, mainnet-readiness reviews, security findings, and remediation guidance involving
Install
npx skills add https://github.com/initlabsai/vibekit/tree/main/skills/audit-algorand
claude plugin marketplace add https://llmmart.ai/marketplace.json && claude plugin install initlabsai-vibekit@llmmart
git clone https://github.com/initlabsai/vibekit.git
The skills CLI installs just this skill, for any of its supported agents. Claude Code installs the whole initlabsai/vibekit collection as a plugin from our marketplace. Git is the plain clone.
Skill manifest
Audit Algorand contracts
Audit the behavior an attacker can reach, then explain each issue through a violated invariant and a reproducible path. A checklist is coverage support; it is not evidence that a vulnerability exists.
This skill covers Algorand TypeScript and PuyaTs. Do not introduce Python,
PyTEAL, Beaker, or raw-TEAL implementation guidance. Load build-on-algorand
when the user also asks to implement fixes or needs broader PuyaTs application
guidance. Load use-vibekit before any deployment, signing, network, account,
LocalNet, or on-chain action.
Establish scope before judging safety
Read the repository instructions, package manifests, lockfile, compiler configuration, contract source, generated ARC-56 and TEAL artifacts, clients, and tests that are in scope. Use the project's pinned commands and dependencies. Do not add a dependency or change production code during an audit unless the user explicitly asks for remediation.
Record:
- the reviewed commit or working-tree state;
- included contracts, LogicSigs, clients, artifacts, and tests;
- target network and active or assumed consensus protocol;
- compiler and optimization settings;
- excluded components and assumptions;
- whether generated artifacts match the reviewed source;
- commands that ran, failed, or could not run.
Treat missing source, stale artifacts, failing tests, and unverified deployment bytecode as limitations. Do not silently fill those gaps with assumptions.
Load the audit guides
Read Methodology and reporting for every audit. Then load every guide whose feature appears in the system:
| Surface | Guide |
|---|---|
| ABI methods, privileges, lifecycle handlers, upgrades, deletion, pausing | Applications and authorization |
| Payments, asset transfers, inner transactions, fees, atomic groups, ASA control | Transactions and assets |
| Local/global/box state, clear state, MBR, state machines, accounting, numeric formulas | State and arithmetic |
| Contract-account or delegated smart signatures | Logic Signatures |
| Randomness, oracles, compiler and artifact verification, clients, keys, monitoring | External systems and release |
For a full audit, load every applicable guide. For a focused review, state which surfaces were intentionally excluded. If the code reveals a new surface, load its guide before completing the review.
Build the security model
Before searching for bugs:
- Identify assets that can be stolen, frozen, minted, destroyed, redirected, or permanently locked.
- Identify principals and authorities: arbitrary callers, users, admins, creators, multisigs, application accounts, LogicSig delegates, oracles, and off-chain operators.
- Map every public entry point, lifecycle action, inner transaction, external application call, and state transition.
- State the invariants that must hold across calls and atomic groups.
- Separate intended trust assumptions from accidental privileges.
Assume an attacker can create accounts and assets, choose every unvalidated argument and transaction field, arrange and pad atomic groups, repeat calls, clear their local state, observe public values, copy pending transactions, choose boundary values, and exploit unavailable external services. Treat a compromised privileged key as a separate scenario unless the design claims to tolerate it.
Trace reachable behavior
For each entry point, trace:
- who can call it and how authorization is established;
- which arguments and transaction fields are attacker-controlled;
- state and asset reads before the call;
- state, balance, asset, authority, and lifecycle changes after the call;
- behavior when the call is repeated, reordered, grouped, interrupted, or supplied boundary values;
- assumptions enforced only by a client, deployment script, or operator.
Follow value across the entire path. A receiver check without an asset-ID check, an admin check around unsafe arithmetic configuration, or a timelock that never binds the proposed program bytes still leaves the relevant invariant unproven.
Corroborate findings
A confirmed finding needs all four elements:
- an attacker capability or failed trusted component;
- a reachable path through the reviewed system;
- a specific violated invariant;
- a concrete security impact.
Use source locations, existing tests, generated TEAL, simulation traces, or a
minimal reproducer as evidence. Prefer a focused negative test when the
project's harness supports one. Never execute an exploit against a public or
shared network. On-chain verification or writes require use-vibekit and the
user's authorization.
Do not report a missing best practice as a vulnerability without showing how it changes reachable behavior. Put unexploitable hardening opportunities, centralization assumptions, and incomplete evidence in their own sections.
Finish with an auditable report
Use the report and severity model in Methodology and reporting. Sort findings by severity, keep confidence separate from impact, and include a regression-test idea with every remediation.
End with:
- coverage achieved and surfaces not reviewed;
- commands and artifacts used as evidence;
- unresolved questions and residual risks;
- an explicit statement when no confirmed findings were identified.
Avoid claiming that a review proves the absence of vulnerabilities or replaces an independent professional audit for a high-value deployment.
Files (vibekit)
-
references
-
applications-and-authorization.md 6.6 KB
# Applications and authorization Use this guide for application entry points, privileged methods, role changes, lifecycle handlers, pausing, upgrades, and deletion. ## Classify every callable path For every ABI method and lifecycle action, decide whether it is: - **permissionless**, with every argument and consumed transaction treated as hostile; or - **permissioned**, with an explicit on-chain authorization policy. Fund movement, configuration changes, treasury redirects, role management, asset control, upgrade, and deletion normally require a defined authority. Verify authorization at the state-changing entry point. A client check, hidden button, deployment convention, or method name is not access control. Trace delegated authority too. If an admin can appoint an operator, ask what the operator can appoint or change and whether role revocation actually removes all paths to the privilege. ## Review privilege storage and rotation Check: - how the initial authority is established at creation; - whether the policy compares the complete account identity intended; - whether role keys can collide or be confused across roles; - who can grant, revoke, or recover each role; - whether the last required authority can be accidentally removed; - whether a one-step address change can permanently lock administration; - whether creator privileges remain intentionally valid after admin rotation; - whether role storage has a deliberate MBR funding and cleanup policy. For mutable ownership, prefer a two-step handoff: the current authority proposes an account, and that exact account accepts. Provide cancellation and define recovery behavior. For a fixed, small role set, global state can be clearer than a dynamic `BoxMap`; use boxes when membership genuinely scales. A minimal permissioned pattern is: ```typescript import type { bytes } from "@algorandfoundation/algorand-typescript"; import { Account, Contract, Global, GlobalState, Txn, assert, } from "@algorandfoundation/algorand-typescript"; export class Treasury extends Contract { admin = GlobalState<bytes>({ key: "admin" }); treasury = GlobalState<bytes>({ key: "treasury" }); createApplication(): void { this.admin.value = Global.creatorAddress.bytes; this.treasury.value = Global.creatorAddress.bytes; } setTreasury(next: Account): void { assert(Txn.sender.bytes === this.admin.value, "Admin only"); this.treasury.value = next.bytes; } } ``` The check establishes only caller authority. Audit the new value and every downstream use separately. ## Review lifecycle defaults and handlers PuyaTs applications reject update and delete by default when the contract does not define the corresponding handlers. Raw TEAL has different default risks, so confirm the reviewed compiler and generated programs rather than importing an assumption from another stack. When handlers exist, verify: - update and delete use the intended authorization policy; - opt-in, close-out, and clear-state behavior is explicit; - each handler is reachable only through its intended on-completion action; - a public ABI route cannot invoke equivalent privileged behavior; - tests cover unauthorized callers and unexpected lifecycle actions. ## Treat upgrades as total code replacement An approved update replaces approval and clear-state programs. On current protocols, the update transaction can also change extra program pages and the global state schema; confirm these capabilities against the target network's protocol version. The new program can remove the current authorization, timelock, pause, and migration rules. Document that trust assumption. For an upgrade policy, review: 1. who schedules, cancels, and executes an update; 2. whether the delay uses a monotonic on-chain value and cannot underflow; 3. whether the announcement binds both proposed program byte strings, or an unambiguous commitment to them; 4. whether execution compares the actual update transaction programs with the announced commitment; 5. whether replay or replacement of a scheduled upgrade is defined; 6. whether users have a practical exit window; 7. whether schema and extra-page changes match the proposed program and migration plan; 8. who becomes responsible for the changed schema and page MBR, and whether that sponsor can safely retain the required balance; 9. how global, local, and box storage migrates. Storing a program hash without comparing it to the programs carried by the update transaction does not enforce the announcement. Likewise, clearing a schedule inside the old program does not constrain what the installed program does next. Updates preserve storage. Inventory old keys, schemas, and boxes; ensure the new code cannot reinterpret stale bytes under a new meaning. Check partial migration, repeated migration, rollback, and interruption paths. ## Review deletion as an asset transition Deletion must be authorized and reachable only after the application has handled every asset and storage obligation. Check: - all spendable ALGO is sent according to policy; - all ASA holdings are transferred or closed intentionally; - created assets are handled according to their control model; - every box is deleted so its MBR is released; - liabilities, queued withdrawals, and claims are zero or migrated; - cleanup cannot be front-run into an inconsistent state; - the final balance equals the dynamic minimum balance expected for deletion. A balance check alone may miss ASAs, boxes, and application-level liabilities. Trace each inventory explicitly. ## Review pause and emergency authority A pause mechanism reduces incident blast radius only when: - pause and unpause have the intended, preferably distinct, authority model; - every critical value-moving path checks the pause state; - safe withdrawals or recovery paths remain available when appropriate; - pausing cannot permanently trap users without an escalation or timeout; - monitoring and operators can invoke it within the assumed response time. Treat a powerful emergency key as a trust assumption. A pause flag with one unguarded withdrawal, inner-call, lifecycle, or alternate ABI route is incomplete. ## Common findings to prove or reject - privileged ABI method is callable by any account; - update or deletion handler lacks authorization; - role rotation can lock the protocol or retain an unintended old admin; - timelock does not bind the actual replacement programs; - upgrade reinterprets or strands existing storage; - deletion can strand ALGO, ASAs, boxes, or user liabilities; - pause protection is missing from an equivalent value-moving path. For each candidate, establish the reachable path and impact before assigning a severity. -
external-systems-and-release.md 7.3 KB
# External systems and release Use this guide for randomness, oracles, clients, compiler and artifact verification, deployment assumptions, keys, monitoring, and incident response. ## Audit randomness as an asynchronous dependency On-chain execution is deterministic, and public transaction or block data is predictable or influenceable. Do not accept locally derived values as secure randomness merely because they are hashed. When a protocol uses the Algorand randomness beacon or another verifiable source, check: - the application commits to a future round before the outcome is available; - value-affecting inputs close with a sufficient gap before that round; - the request binds all domain and user data needed to avoid cross-context reuse; - anyone, rather than only a party who benefits from withholding, can finalize; - the protocol reads within the source's supported availability window; - delayed or missing output cannot lock funds forever; - backup rounds, timeout withdrawals, cancellation, or governance recovery do not let a party choose among known favorable outcomes; - beacon application IDs and interfaces are verified for the target network; - discontinuation or key compromise has a migration path with a documented trust model. Beacon IDs, retention windows, and service timing can change. Verify them from current official sources during a release review; do not rely on remembered constants. ## Make oracle trust explicit For every price, timestamp, proof, or off-chain fact, establish: - authorized publishers and how keys rotate; - data identity, units, decimals, sign, and valid range; - publication time, accepted staleness, and round ordering; - behavior for missing, duplicate, delayed, or contradictory updates; - manipulation cost and whether one atomic group can both move and consume the value; - fallback, pause, circuit-breaker, and recovery rules; - whether multiple sources are genuinely independent; - whether operators can front-run users or selectively withhold updates. An access-controlled oracle can still publish economically invalid or stale data. Validate both publisher authority and value semantics. ## Verify the compiler and artifacts Treat compiler output as part of the trusted computing base: 1. Confirm PuyaTs is pinned by the package manager and compiler configuration. 2. Review current security bulletins for the pinned version. 3. Compile from the reviewed source with the project's normal command. 4. Test unoptimized output and the intended deployment optimization level when the installed compiler exposes supported project commands for both. 5. Investigate any behavioral divergence or compiler crash as security relevant. 6. Compare ARC-56, approval TEAL, clear TEAL, and source maps with the expected build inputs. 7. Verify deployed program bytes separately when deployment is in scope. Do not regenerate artifacts over user changes during a read-only audit. Build in the project's normal disposable output path or explain why artifact parity could not be established. Generated TEAL deserves targeted inspection for lifecycle routing, assertions, inner-transaction fields, fee values, and optimization-sensitive branches. Source review alone cannot establish deployment parity. ## Treat clients as untrusted convenience layers Generated clients improve type safety and transaction composition for honest users. Attackers can bypass them. Review clients and frontends for: - transaction construction matching the contract's actual ABI and group assumptions; - fee and resource estimation for worst-case inner paths; - network, application, asset, and signer identity confirmation; - slippage, deadline, receiver, and amount presentation before signing; - stale ARC-56 artifacts or method selectors; - unsafe defaults that cause users to rekey, close, overpay, or authorize a different group than displayed. Every security-critical constraint must remain enforced on-chain. Report a client-only check as an on-chain vulnerability only when the contract relies on it. ## Review deployment and privileged keys For contracts controlling significant value, review whether update, delete, ASA control, oracle, pause, and treasury authorities match the documented operational model. Check: - high-impact authority uses an appropriate multisig or equivalent policy; - the threshold avoids both one-key control and impractical all-key recovery; - multisig member ordering is recorded consistently; - keys are separated and can be rotated without locking the protocol; - mnemonics, private keys, and delegated LogicSigs are absent from source, logs, fixtures, and versioned configuration; - application accounts are not rekeyed to a single externally owned account that can bypass all application logic and drain assets directly; - immutable deployments actually omit update/delete paths and handle any creator authority deliberately; - deployed approval and clear programs match reviewed artifacts; - network and application IDs are verified rather than inferred from UI state. Do not request or expose seed material during an audit. Load `use-vibekit` before any account, signing, deployment, or on-chain verification action. ## Distinguish algod from indexed history Indexer data is eventually consistent. Do not use an indexer response as the sole real-time confirmation of a security-critical transaction. Confirm pending or recent transactions through algod when operational behavior depends on finality timing. For mission-critical systems, document reliance on third-party nodes, their failure and censorship modes, and the fallback strategy. Off-chain APIs and frontends also need ordinary web-security review; keep those findings separate from AVM findings unless the trust boundary crosses both. ## Assess monitoring and incident response The response plan should connect on-chain powers with operational detection. Review monitoring for: - update and delete attempts; - role, manager, freeze, clawback, oracle, and pause changes; - large, rapid, or structurally unusual value movement; - repeated failures that suggest probing or denial of service; - compiler or dependency security advisories; - beacon and oracle availability. Identify who receives alerts, who can pause or recover, expected response time, and how users are informed. A kill switch is useful only when critical methods obey it and authorized operators can invoke it before the assumed loss window. ## Set an appropriate release conclusion For a mainnet-readiness review, report separately: - code findings; - unverified deployment or artifact assumptions; - privileged-key and operator trust; - external availability and data integrity; - monitoring, pause, upgrade, and user-exit readiness; - test and analysis coverage. Recommend an independent professional audit and a responsible-disclosure or bug-bounty process for systems holding significant user value. That recommendation does not replace concrete findings from the current review. Canonical verification starting points: - [Algorand smart-contract concepts](https://dev.algorand.co/concepts/smart-contracts/overview/) - [PuyaTs security policy](https://github.com/algorandfoundation/puya-ts/blob/main/SECURITY.md) - [Puya compiler security bulletin](https://dev.algorand.co/bulletins/puya-issues-27-10-2025/) - [Algorand randomness](https://dev.algorand.co/concepts/protocol/randomness/) -
logic-signatures.md 7 KB
# Logic Signatures Use this guide whenever the scope contains a PuyaTs `LogicSig`, compiled smart signature, delegated signature, or contract-account escrow. ## Identify the mode and exposed authority A LogicSig approves a transaction when its program returns nonzero: - **Contract account**: the program hash is an address with no private key. Anyone with the program can submit transactions from the funded escrow when the predicates pass. - **Delegated**: an account owner signs the program. Anyone holding that signed program can submit approved transactions from the owner's account. Delegated programs are as sensitive as private keys. A narrow program can limit their authority, but the protocol has no direct revocation mechanism. Rekeying the delegating account invalidates existing delegation signatures and also changes control of the whole account, so incident plans must treat that as a high-impact recovery action. A delegation follows its authorizing address, not necessarily one original sender. An account rekeyed to that authorizer may also use the delegated program. If the policy is intended to authorize only one account, bind `Txn.sender` to that exact account; otherwise document the broader authority. Prefer an application when state, revocation, rich authorization, or evolving logic is required. LogicSigs have no state and cannot be patched after their address or delegation is in use. ## Build a complete field matrix For every permitted transaction type, list every field that can change value, authority, lifetime, or network scope. Mark each as: - fixed to a template or constant; - deliberately bounded; - derived and validated against another transaction; - intentionally unrestricted, with a documented reason. At minimum, verify the relevant fields: | Concern | Required review | | ---------- | ------------------------------------------------------------------------------------------------ | | Rekey | `Txn.rekeyTo === Global.zeroAddress` unless a tightly scoped rekey is the purpose | | ALGO close | `Txn.closeRemainderTo === Global.zeroAddress` for payments | | ASA close | `Txn.assetCloseTo === Global.zeroAddress` for asset transfers | | Fee | Deliberate upper bound, commonly `Txn.fee <= Global.minTxnFee` when fee pooling covers execution | | Type | Only intended transaction types pass | | Sender | Exact sender when a delegated program is account-specific | | Value | Receiver, amount, asset/application ID, sender/clawback behavior, and other type-specific fields | | Lifetime | Exact or bounded `firstValid` and `lastValid` consistent with the authorization | | Replay | Lease and stateful pairing where needed; see below | | Network | `Global.genesisHash` bound when cross-network reuse is unsafe | | Group | Self-validation uses `Txn`; any `gtxn` reference is bound to group index and relationship | Do not stop after finding one missing check. A LogicSig is secure only if every unrestricted field is compatible with its intended authority. ## Treat arguments as public caller input LogicSig arguments are not covered by the delegation signature, transaction ID, or group ID. Typed LogicSig parameters can validate their encoding, but the caller still chooses their values. Raw `op.arg(...)` paths require manual count, length, encoding, and semantic validation. Never use an argument as a password, secret, caller identity, receiver policy, amount cap, or other authorization gate. Template variables are compiled into public bytecode and cannot keep secrets either. ## Prove replay semantics A lease creates a `{sender, lease}` lock only through the transaction's `lastValid` round. After that window expires, the same lease can be used again. For at-most-once authorization without application state, bind all three: - a unique lease; - exact `firstValid`; - exact `lastValid`. If either validity bound remains caller-controlled, an attacker can submit a short window, wait for expiry, and replay. If the authorization should repeat at a controlled rate, specify and verify those semantics instead of describing it as one-time. A stateful application call in the same group can provide stronger replay or quota checks, but the LogicSig must bind to that exact call and group relation. ## Validate the transaction being signed Use `Txn` to validate the current LogicSig-authorized transaction. A program that checks `gtxn(0)` without proving its own `groupIndex` can be attached to several other group transactions while only the indexed transaction is validated. When group context is necessary, prove: - the LogicSig transaction's exact group index; - group size or composable relationship; - every field of the paired application call or payment needed by the policy; - that the same paired transaction cannot authorize multiple spends; - that no extra transaction gains unintended authority. ## Review a bounded payment holistically This pattern illustrates the minimum relationships for a one-time, network-specific delegated payment. The intended application may require additional fields: ```typescript import { Account, Global, LogicSig, TemplateVar, TransactionType, Txn, Uint64, type bytes, type uint64, } from "@algorandfoundation/algorand-typescript"; export class BoundedPaymentSig extends LogicSig { program(): boolean { return ( Txn.typeEnum === TransactionType.Payment && Txn.receiver === TemplateVar<Account>("RECEIVER") && Txn.amount <= Uint64(1_000_000) && Txn.fee <= Global.minTxnFee && Txn.rekeyTo === Global.zeroAddress && Txn.closeRemainderTo === Global.zeroAddress && Txn.lease === TemplateVar<bytes>("LEASE") && Txn.firstValid === TemplateVar<uint64>("FIRST_VALID") && Txn.lastValid === TemplateVar<uint64>("LAST_VALID") && Global.genesisHash === TemplateVar<bytes>("GENESIS_HASH") ); } } ``` Do not copy this as a universal solution. For asset transfers, also validate asset identity, receiver, amount, close, and clawback semantics. For application calls, validate application, on-completion action, arguments, accounts, assets, applications, and group relationship required by the policy. ## Test the negative space For each allowed transaction, create mutations that change one field at a time: - rekey and close destinations; - fee just above the bound; - wrong sender, type, receiver, amount, asset, application, or network; - reused lease and shifted validity windows; - hostile LogicSig arguments; - extra group transactions and different group indexes; - replay after the original validity window. The test suite should show rejection for every field the security policy constrains. Compile and inspect generated TEAL so a source-level assumption is not lost in code generation. -
methodology-and-reporting.md 8.1 KB
# Methodology and reporting Use this guide for every audit. It defines the evidence standard, review sequence, severity model, and report structure. ## Preserve evidence and scope Start with a read-only inventory. Record the commit hash when reviewing a clean revision; otherwise record that the working tree is dirty and list relevant changes. Do not clean, reset, regenerate, or overwrite user work merely to make the audit easier. Inventory: - contract and LogicSig source files; - public ABI methods and lifecycle handlers; - package and lockfile versions, especially PuyaTs; - compiler configuration and deployment optimization level; - ARC-56, TEAL, source maps, and generated clients; - unit, integration, and LocalNet end-to-end tests; - deployment, upgrade, oracle, signer, and monitoring code in scope. Generated artifacts are evidence only after checking that they correspond to the reviewed source and configuration. A passing client-side validation does not establish an on-chain invariant. ## Create an attack-surface ledger For each component, record: | Item | Questions | | ----------- | -------------------------------------------------------------------------------- | | Entry point | Who can call it? Which on-completion actions reach it? | | Authority | Which account, role, signature, or state value grants permission? | | Inputs | Which ABI values and transaction fields can an attacker choose? | | Value flow | Which ALGO, ASA, application, or authority moves? | | State | Which global, local, or box values are read, created, changed, or deleted? | | Composition | What changes when the call is repeated or placed in a larger atomic group? | | Dependency | Which client, oracle, beacon, compiler, node, or operator must behave correctly? | Use the ledger as a completeness check. Keep investigating when an asset flow has no matching authorization or accounting invariant. ## State invariants before findings Write invariants in observable terms. Useful families include: - only the defined authority can update code, delete the application, rotate roles, change fees, redirect treasuries, mint, freeze, claw back, or close; - every credited deposit corresponds to one validated payment or asset transfer of the intended type, sender policy, receiver, asset, and amount; - aggregate liabilities never exceed spendable assets; - each claim, vote, withdrawal, or settlement occurs at most as often as the protocol permits; - users cannot erase debt, collateral, claims, or penalties through clear state; - every reachable state transition preserves the protocol's accounting and lifecycle rules; - configured numeric bounds make every permitted runtime calculation valid; - unavailable randomness or oracle data cannot lock funds forever; - an upgrade or deletion cannot bypass its announced authority, delay, storage, and asset-handling policy. An invariant can be violated across several functions or transactions. Trace the complete sequence rather than reviewing each method in isolation. ## Exercise adversarial capabilities At minimum, consider whether an arbitrary caller can: - supply zero, maximum, malformed, duplicated, or semantically invalid input; - substitute an attacker-created ASA or a different account/application; - change receiver, amount, sender, close, rekey, fee, validity, or asset fields; - pad, reorder, or duplicate calls within an atomic group; - reuse one payment for multiple credits; - repeat a successful call or copy a revealed value from a pending transaction; - clear local state, close out, opt out, or delete referenced storage; - force insufficient opcode, box I/O, foreign-resource, fee, or minimum-balance conditions; - trigger overflow, underflow, divide-by-zero, truncation, or unsafe conversion; - exploit an oracle delay, beacon outage, admin loss, or upgrade mismatch. Do not assume the official client is used. Attackers can construct protocol transactions directly. ## Validate with the available harness Use existing project commands and pinned tooling. Favor: - compiler and type checks; - unit tests for branch and invariant coverage; - LocalNet end-to-end tests for group, fee, resource, lifecycle, and balance behavior; - simulation traces for inner transactions, resource failures, and opcode budget; - tests at both unoptimized output and the intended deployment optimization level when the project exposes supported commands for both; - comparison of compiled artifacts with the source and deployment inputs. Do not invent flags or replace the project's test stack. If a useful test cannot be run, describe the proposed reproducer and mark the finding's evidence accordingly. ## Classify severity and confidence separately Use severity for worst credible impact under the stated threat model: | Severity | Meaning | | ------------- | ------------------------------------------------------------------------------------------------------------------------------------ | | Critical | Broad or irreversible theft, unauthorized control, or protocol-wide insolvency is directly reachable with practical preconditions. | | High | Significant theft, permanent lock, privilege escalation, or integrity failure is reachable, but scope or preconditions are narrower. | | Medium | Limited loss, consequential denial of service, repeatable griefing, or invariant failure with meaningful constraints. | | Low | Minor impact, narrow edge case, or defense-in-depth weakness with a credible security consequence. | | Informational | Trust assumption, maintainability risk, or hardening advice without a demonstrated security impact. | Use confidence for evidence quality: - **High**: reproduced or proven directly from a complete reachable path. - **Medium**: the path is well supported but depends on an unverified component or deployment assumption. - **Low**: plausible concern with material missing evidence; usually present it as an open question rather than a confirmed finding. Do not inflate severity because a contract holds value. State the required preconditions and affected assets explicitly. ## Report structure Use this order: 1. **Executive summary** — reviewed scope, overall result, highest risks. 2. **Scope and limitations** — revision, files, artifacts, commands, exclusions. 3. **System and threat model** — assets, roles, trust assumptions, invariants. 4. **Findings** — confirmed vulnerabilities ordered by severity. 5. **Design and operational risks** — accepted trust and availability risks. 6. **Hardening opportunities** — useful changes without proven exploit paths. 7. **Coverage and residual risk** — completed checks and remaining uncertainty. Use one finding per root cause: ```markdown ## ALG-01: Payment can be credited more than once - Severity: High - Confidence: High - Location: `contracts/sale.algo.ts:42` - Status: Open ### Violated invariant Every credit must consume one unique validated payment. ### Impact [Affected assets, users, and bounds.] ### Exploit scenario [Attacker prerequisites and ordered transaction/call sequence.] ### Evidence [Relevant source behavior, artifact, trace, or reproducing test.] ### Recommendation [Smallest change that restores the invariant, plus compatibility concerns.] ### Regression test [Negative test that fails before the fix and passes afterward.] ``` Use stable identifiers such as `ALG-01`. Reference secondary locations from the same finding instead of duplicating the root cause. ## Conclude precisely If no confirmed vulnerabilities were found, say: > No confirmed vulnerabilities were identified within the reviewed scope. Follow it with limitations and residual risks. Do not say the contract is secure, safe for mainnet, formally verified, or free of vulnerabilities unless the evidence supports that narrower claim. -
state-and-arithmetic.md 7.2 KB
# State and arithmetic Use this guide for global, local, and box state; clear state; minimum-balance requirements; protocol phases; withdrawals; accounting; and numeric formulas. ## Choose storage by who can destroy it A user can always clear their application local state. The clear-state program runs, but the protocol deletes local state even when that program fails. Never use local state as the sole record of debt, collateral, penalties, voting eligibility, claims, or another obligation the user benefits from erasing. Use box or global storage for protocol-critical records that must survive user action. A representative contrast is: ```typescript // Unsafe for persistent debt: the borrower can clear this local state. debt = LocalState<uint64>({ key: "debt" }); // Persistent under application control; account for box MBR and references. debt = BoxMap<Account, uint64>({ keyPrefix: "debt" }); ``` For each state value, record: - who can create, update, and delete it; - which account can force lifecycle changes around it; - its absence/default semantics; - whether its key encoding can collide; - which invariant depends on it; - how it is migrated or retired. ## Audit clear-state behavior Clear-state execution cannot use boxes. The caller also need not supply useful foreign references, and a failing program still loses the user's local state. Keep clear-state logic simple and best-effort. Check: - whether clearing local state erases a protocol obligation or entitlement; - whether lost local values leave unaccounted funds in the application; - whether global reconciliation is safe and bounded; - whether any operation in the clear program can fail due to resources, arithmetic, or missing state; - whether close-out and clear-state have intentionally different semantics. Do not claim a clear program can prevent deletion of local state. Move durable obligations elsewhere. ## Account for boxes and minimum balance Boxes consume application-account minimum balance and require box I/O budget and references. Trace who funds creation and who receives released MBR on deletion. Check for attacker-controlled box growth, oversized values, key collisions, unreachable cleanup, and storage that survives after its business record is closed. Use the live `minBalance` value rather than a hard-coded base balance when calculating spendable ALGO: ```typescript const app = Global.currentApplicationAddress; assert(app.balance >= app.minBalance, "Invalid application balance"); assert( app.balance - app.minBalance >= amount, "Insufficient spendable balance", ); ``` The first comparison makes the subtraction invariant explicit. When refunding released storage MBR, measure `minBalance` before and after deletion and refund the actual delta. Verify the caller is entitled to that delta; otherwise an attacker may delete records funded by someone else and collect the refund. Before application deletion, all boxes must be deleted or their locked MBR is unrecoverable. A count alone is sufficient only when every box-creation and deletion path updates it atomically and cannot drift. When the target protocol permits cross-application box access, inspect the application parameters as part of the authority model. Foreign box reads can expose records to any application, while family access can give applications with the same creator write authority. Inventory every trusted family member, review shared-box key namespaces, and test call trees involving untrusted intermediate applications. Protocol call-stack restrictions are execution constraints, not a substitute for application-level authorization and accounting invariants. ## Model lifecycle with exclusive states Independent booleans such as `saleOpen`, `paused`, `settling`, and `closed` can form contradictory combinations. Prefer one authoritative phase value when the protocol states are mutually exclusive. For every state transition: - prove the current phase and caller authority; - enumerate allowed next phases; - update state atomically with associated asset/accounting changes; - reject skipped, repeated, and backward transitions unless intended; - consider interruption, pause, upgrade, and timeout recovery; - ensure every public method is allowed in exactly the intended phases. Central helpers can make the transition relation visible, but inspect all direct writes to the phase value for bypasses. ## Prefer pull-based settlement For distributions, record each user's entitlement and let that user withdraw. A single failed inner transaction in a push loop rolls back the whole call and can block unrelated recipients. Audit a pull withdrawal for checks-effects-interactions behavior in AVM terms: - entitlement exists and belongs to the receiver; - amount is captured before deletion or zeroing; - the record is consumed exactly once; - every alternate path consumes the same entitlement consistently; - the inner transfer uses the intended receiver, asset, amount, and zero fee; - failure rolls back both state and transfer; - box deletion and MBR refund go to the intended party. ## Analyze numeric configuration before runtime The AVM uses unsigned 64-bit arithmetic for `uint64`. Overflow, underflow, and division by zero fail the transaction rather than silently wrapping. That prevents corrupted arithmetic output, but unsafe parameters can still brick a critical path or enable denial of service. Trace formulas backward to every configuration setter. Reject parameter sets that make any allowed runtime input invalid: ```typescript const MAX_UINT64: uint64 = Uint64(18_446_744_073_709_551_615n) configure(maxDeposits: uint64, rate: uint64, scale: uint64): void { assert(Txn.sender === Global.creatorAddress, 'Admin only') assert(scale > Uint64(0), 'Scale must be nonzero') if (rate > Uint64(0)) { assert(maxDeposits <= MAX_UINT64 / rate, 'Configuration can overflow') } // Store only after the full numeric envelope is proven. } ``` Review: - zero denominators and empty domains; - multiplication before division and precision loss from operation order; - addition/subtraction ordering and boundary checks; - truncation, rounding direction, and accumulated dust; - conversion from `biguint` or bytes back to `uint64`; - exponent, shift, and loop bounds; - time differences and timestamp/round assumptions; - values that are safe individually but unsafe in combination. Use `biguint` for intermediates that may exceed `uint64`, then prove the final value fits before conversion. Big integers do not remove division, rounding, resource, or economic-invariant risks. ## Reconcile accounting globally For vaults, exchanges, lending, staking, and token contracts, write conservation relationships such as: - sum of user balances plus protocol reserves equals accounted holdings; - minted supply minus burned supply equals circulating liabilities; - collateral and price bounds cover borrowable value; - queued plus paid withdrawals never exceed funded obligations; - fees and rounding remainders have an explicit owner. Trace deposit, transfer, claim, liquidation, settlement, migration, and deletion against the same relationships. Test `0`, `1`, maximum supported values, values just outside bounds, and long action sequences. A per-method assertion can pass while the cross-method invariant drifts. -
transactions-and-assets.md 6.7 KB
# Transactions and assets Use this guide for transaction arguments, atomic groups, fee pooling, inner transactions, application calls, payments, asset transfers, and ASA creation or reconfiguration. ## Validate transaction meaning, not its position For every transaction the application consumes, determine which fields define the intended economic action. Depending on type, review: - transaction type and sender policy; - receiver and exact or bounded amount; - asset or application ID; - close, rekey, clawback, freeze, and configuration fields; - fee and validity assumptions; - lease or other replay identifier; - accounts, applications, assets, boxes, or access-list resources consumed by the program; - group relationship to the current application call. Checking a group index, type, receiver, or amount alone rarely proves the whole meaning. An attacker can construct transactions directly and substitute every unchecked field. Prefer a typed ARC-4 transaction parameter over a fixed absolute group index. The router binds the referenced transaction to that ABI argument, while the contract still validates its business fields: ```typescript import { Contract, Global, GlobalState, Uint64, assert, gtxn, type uint64, } from "@algorandfoundation/algorand-typescript"; export class CreditSale extends Contract { credits = GlobalState<uint64>({ key: "credits" }); buyCredit(payment: gtxn.PaymentTxn): void { assert( payment.receiver === Global.currentApplicationAddress, "Wrong receiver", ); assert(payment.amount >= Uint64(1_000_000), "Insufficient payment"); this.credits.value = this.credits.value + Uint64(1); } } ``` If this used `gtxn.PaymentTxn(0)`, multiple application calls in a padded group could read the same payment and credit it repeatedly. Exact group-size checks can prevent that narrow attack but reduce composition. Prefer a one-to-one ABI transaction reference unless the protocol deliberately fixes the entire group shape. ## Audit atomic-group composition Ask: - Can one payment, asset transfer, oracle update, or authorization transaction satisfy more than one application call? - Can an attacker pad or reorder the group without invalidating a check? - Does relative position matter, and is it proven? - Can a LogicSig transaction be reused at several group positions? - Does the application rely on a transaction before or after it without binding that relationship? - Are group-wide state changes safe under repeated application calls? - Does an inner application call expose a reentrant-like sequence through another application in the same group or call tree? Algorand groups execute atomically, but atomicity does not prevent semantic double use. Trace the identity and consumption of each economic input. ## Review fee flow The group pools fees. PuyaTs defaults inner-transaction fees to zero so outer transactions fund them through the pool. Review the pinned compiler and generated TEAL, then flag explicit nonzero inner fees unless the design has a bounded, documented reason. A repeated permissionless call with app-paid inner fees can drain the application account. Do not hard-code an assumed network minimum. Where a LogicSig needs a fee bound, compare against `Global.minTxnFee` or a deliberate narrow policy; see [Logic Signatures](logic-signatures.md). Check that clients estimate enough pooled fee for every possible inner transaction path. Insufficient pooled fee is often availability or integration risk rather than theft, but app-paid fees can become direct loss. ## Keep dangerous inner fields out of caller control Review every inner payment and asset transfer for: - `rekeyTo`; - `closeRemainderTo`; - `assetCloseTo`; - asset sender or clawback behavior; - receiver, asset, and amount; - fee. Omitted close and rekey fields are safer than accepting caller-supplied values. If a migration intentionally uses one, verify strict authorization, fixed destination, one-time state transition, asset accounting, and tests. ## Validate ABI inputs semantically Default ARC-4 encoding validation establishes that an encoded value has the declared shape. It does not establish application-level limits. Review: - maximum length for dynamic strings and bytes; - exact length for hashes, commitments, IDs, and fixed records; - lower and upper bounds for amounts, rates, windows, and basis points; - membership in the supported enum or phase set; - uniqueness and ordering when arrays represent sets or sequences; - cross-field relationships such as deadline ordering or amount/price bounds. If method validation is disabled or raw argument bytes are decoded manually, the contract must validate encoding as well as meaning. ## Audit ASA identity and control When receiving an asset, validate `xferAsset`, receiver, amount, and any sender policy. Otherwise an attacker can substitute a worthless ASA. For ASA creation, review the intended manager, reserve, freeze, and clawback addresses. Empty control addresses permanently remove those capabilities. Confirm that immutability or retained control is deliberate. For ASA reconfiguration, every control address that should remain must be re-specified. Omitted addresses are permanently cleared: ```typescript const result = itxn .assetConfig({ configAsset: asset, manager: nextManager, reserve: asset.reserve, freeze: asset.freeze, clawback: asset.clawback, fee: 0, }) .submit(); ``` Also verify who can initiate reconfiguration and whether transferring manager authority uses an intentional acceptance or recovery model. Review destruction, clawback, freeze, and reserve semantics as separate privileges. ## Review replay and public values Every non-idempotent method needs a uniqueness or state guard. Ask what happens when a claim, vote, withdrawal, settlement, or configuration action runs twice. A secret or hash preimage revealed in the same pending call that performs a payout can be copied and front-run. If secret-based authorization is required, bind a prior commitment to the complete intended action, separate commit and reveal across confirmed rounds, and define expiry and cancellation. Never assume application arguments are private. ## Consider flow limits Atomic groups can support multi-step flash-loan-style manipulation. For liquidity, lending, oracle, or treasury flows, assess: - whether price or accounting state can be manipulated and consumed in one group; - maximum value that can leave per call, group, round, and time window; - whether a token-bucket or other rate limit bounds exploit impact; - whether the limit itself can overflow, be bypassed through alternate paths, or permanently deny service. Rate limiting limits blast radius; it does not repair incorrect pricing or accounting.
-
-
SKILL.md 6.6 KB
--- name: audit-algorand description: Perform structured security audits and adversarial reviews of TypeScript Algorand applications and LogicSigs compiled with PuyaTs. Use for vulnerability assessments, threat models, exploit analysis, mainnet-readiness reviews, security findings, and remediation guidance involving AVM contracts, generated TEAL or ARC-56 artifacts, transaction groups, assets, state, upgrades, or external dependencies. Excludes Python, deployment execution, generic implementation work, and non-security code review. --- # Audit Algorand contracts Audit the behavior an attacker can reach, then explain each issue through a violated invariant and a reproducible path. A checklist is coverage support; it is not evidence that a vulnerability exists. This skill covers Algorand TypeScript and PuyaTs. Do not introduce Python, PyTEAL, Beaker, or raw-TEAL implementation guidance. Load `build-on-algorand` when the user also asks to implement fixes or needs broader PuyaTs application guidance. Load `use-vibekit` before any deployment, signing, network, account, LocalNet, or on-chain action. ## Establish scope before judging safety Read the repository instructions, package manifests, lockfile, compiler configuration, contract source, generated ARC-56 and TEAL artifacts, clients, and tests that are in scope. Use the project's pinned commands and dependencies. Do not add a dependency or change production code during an audit unless the user explicitly asks for remediation. Record: - the reviewed commit or working-tree state; - included contracts, LogicSigs, clients, artifacts, and tests; - target network and active or assumed consensus protocol; - compiler and optimization settings; - excluded components and assumptions; - whether generated artifacts match the reviewed source; - commands that ran, failed, or could not run. Treat missing source, stale artifacts, failing tests, and unverified deployment bytecode as limitations. Do not silently fill those gaps with assumptions. ## Load the audit guides Read [Methodology and reporting](references/methodology-and-reporting.md) for every audit. Then load every guide whose feature appears in the system: | Surface | Guide | | -------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------ | | ABI methods, privileges, lifecycle handlers, upgrades, deletion, pausing | [Applications and authorization](references/applications-and-authorization.md) | | Payments, asset transfers, inner transactions, fees, atomic groups, ASA control | [Transactions and assets](references/transactions-and-assets.md) | | Local/global/box state, clear state, MBR, state machines, accounting, numeric formulas | [State and arithmetic](references/state-and-arithmetic.md) | | Contract-account or delegated smart signatures | [Logic Signatures](references/logic-signatures.md) | | Randomness, oracles, compiler and artifact verification, clients, keys, monitoring | [External systems and release](references/external-systems-and-release.md) | For a full audit, load every applicable guide. For a focused review, state which surfaces were intentionally excluded. If the code reveals a new surface, load its guide before completing the review. ## Build the security model Before searching for bugs: 1. Identify assets that can be stolen, frozen, minted, destroyed, redirected, or permanently locked. 2. Identify principals and authorities: arbitrary callers, users, admins, creators, multisigs, application accounts, LogicSig delegates, oracles, and off-chain operators. 3. Map every public entry point, lifecycle action, inner transaction, external application call, and state transition. 4. State the invariants that must hold across calls and atomic groups. 5. Separate intended trust assumptions from accidental privileges. Assume an attacker can create accounts and assets, choose every unvalidated argument and transaction field, arrange and pad atomic groups, repeat calls, clear their local state, observe public values, copy pending transactions, choose boundary values, and exploit unavailable external services. Treat a compromised privileged key as a separate scenario unless the design claims to tolerate it. ## Trace reachable behavior For each entry point, trace: - who can call it and how authorization is established; - which arguments and transaction fields are attacker-controlled; - state and asset reads before the call; - state, balance, asset, authority, and lifecycle changes after the call; - behavior when the call is repeated, reordered, grouped, interrupted, or supplied boundary values; - assumptions enforced only by a client, deployment script, or operator. Follow value across the entire path. A receiver check without an asset-ID check, an admin check around unsafe arithmetic configuration, or a timelock that never binds the proposed program bytes still leaves the relevant invariant unproven. ## Corroborate findings A confirmed finding needs all four elements: 1. an attacker capability or failed trusted component; 2. a reachable path through the reviewed system; 3. a specific violated invariant; 4. a concrete security impact. Use source locations, existing tests, generated TEAL, simulation traces, or a minimal reproducer as evidence. Prefer a focused negative test when the project's harness supports one. Never execute an exploit against a public or shared network. On-chain verification or writes require `use-vibekit` and the user's authorization. Do not report a missing best practice as a vulnerability without showing how it changes reachable behavior. Put unexploitable hardening opportunities, centralization assumptions, and incomplete evidence in their own sections. ## Finish with an auditable report Use the report and severity model in [Methodology and reporting](references/methodology-and-reporting.md). Sort findings by severity, keep confidence separate from impact, and include a regression-test idea with every remediation. End with: - coverage achieved and surfaces not reviewed; - commands and artifacts used as evidence; - unresolved questions and residual risks; - an explicit statement when no confirmed findings were identified. Avoid claiming that a review proves the absence of vulnerabilities or replaces an independent professional audit for a high-value deployment.
Comments (0)
Sign in to join the conversation.
Reviews (0)
No reviews yet.
No comments yet.