Install any skill in seconds. Free to start, no credit card required.
Get Started Free →Scans Solana programs for 6 critical vulnerabilities including arbitrary CPI, improper PDA validation, missing signer/ownership checks, and sysvar spoofing. Use when auditing Solana/Anchor programs.
| Test case | Without → With | Effect | Δ tokens | Δ turns |
|---|---|---|---|---|
| case-11 | ✗→✓ | ▲ Improved | 57% | 0% |
| case-18 | ✗→✓ | ▲ Improved | 39% | 0% |
| case-20 | ✗→✓ | ▲ Improved | 72% | 0% |
| case-04 | ✓→✓ | = Same ✓ | 153% | 0% |
| case-05 | ✓→✓ | = Same ✓ | 115% | 0% |
Systematically scan Solana programs (native and Anchor framework) for platform-specific security vulnerabilities related to cross-program invocations, account validation, and program-derived addresses. This skill encodes 6 critical vulnerability patterns unique to Solana's account model.
.rsrust// Native Solana program indicators use solana_program::{ account_info::AccountInfo, entrypoint, entrypoint::ProgramResult, pubkey::Pubkey, program::invoke, program::invoke_signed, }; entrypoint!(process_instruction); // Anchor framework indicators use anchor_lang::prelude::*; #[program] pub mod my_program { pub fn initialize(ctx: Context<Initialize>) -> Result<()> { // Program logic } } #[derive(Accounts)] pub struct Initialize<'info> { #[account(mut)] pub authority: Signer<'info>, } // Common patterns AccountInfo, Pubkey invoke(), invoke_signed() Signer<'info>, Account<'info> #[account(...)] with constraints seeds, bump
programs/*/src/lib.rs - Program implementationAnchor.toml - Anchor configurationCargo.toml with solana-program or anchor-langtests/ - Program testsWhen invoked, I will:
I check for 6 critical vulnerability patterns unique to Solana. For detailed detection patterns, code examples, mitigations, and testing strategies, see VULNERABILITY_PATTERNS.md.
For complete vulnerability patterns with code examples, see VULNERABILITY_PATTERNS.md.
programs/*/src/lib.rs)bash# Find all CPI calls rg "invoke\(|invoke_signed\(" programs/ # Check for program ID validation before each # Should see program ID checks immediately before invoke
For each CPI:
Program<'info, T> typebash# Find PDA usage rg "find_program_address|create_program_address" programs/ rg "seeds.*bump" programs/ # Anchor: Check for seeds constraints rg "#\[account.*seeds" programs/
For each PDA:
find_program_address() or Anchor seeds constraintbash# Find account deserialization rg "try_from_slice|try_deserialize" programs/ # Should see owner checks before deserialization rg "\.owner\s*==|\.owner\s*!=" programs/
For each account used:
Account<'info, T> and Signer<'info>bash# Find instruction introspection usage rg "load_instruction_at|load_current_index|get_instruction_relative" programs/ # Check for checked versions rg "load_instruction_at_checked|load_current_index_checked" programs/
toml# Add to Cargo.toml [dependencies] solana-program = "1.17" # Use latest version [lints.clippy] # Enable Solana-specific lints # (Trail of Bits solana-lints if available)
markdown## [CRITICAL] Arbitrary CPI - Unchecked Program ID **Location**: `programs/vault/src/lib.rs:145-160` (withdraw function) **Description**: The `withdraw` function performs a CPI to transfer SPL tokens without validating that the provided `token_program` account is actually the SPL Token program. An attacker can provide a malicious program that appears to perform a transfer but actually steals tokens or performs unauthorized actions. **Vulnerable Code**:
// lib.rs, line 145 pub fn withdraw(ctx: Context<Withdraw>, amount: u64) -> Result<()> { let token_program = &ctx.accounts.token_program;
// WRONG: No validation of token_program.key()! invoke( &spl_token::instruction::transfer(...), & ctx.accounts.vault.to_account_info(), ctx.accounts.destination.to_account_info(), ctx.accounts.authority.to_account_info(), token_program.to_account_info(), // UNVALIDATED ], )?; Ok(()) }
**Attack Scenario**:
1. Attacker deploys malicious "token program" that logs transfer instruction but doesn't execute it
2. Attacker calls withdraw() providing malicious program as token_program
3. Vault's authority signs the transaction
4. Malicious program receives CPI with vault's signature
5. Malicious program can now impersonate vault and drain real tokens
**Recommendation**:
Use Anchor's `Program<'info, Token>` type:use anchor_spl::token::{Token, Transfer};
#derive(Accounts)] pub struct Withdraw<'info> { #account(mut)] pub vault: Account<'info, TokenAccount>, #account(mut)] pub destination: Account<'info, TokenAccount>, pub authority: Signer<'info>, pub token_program: Program<'info, Token>, // Validates program ID automatically }
pub fn withdraw(ctx: Context<Withdraw>, amount: u64) -> Result<()> { let cpi_accounts = Transfer { from: ctx.accounts.vault.to_account_info(), to: ctx.accounts.destination.to_account_info(), authority: ctx.accounts.authority.to_account_info(), };
let cpi_ctx = CpiContext::new( ctx.accounts.token_program.to_account_info(), cpi_accounts, );
anchor_spl::token::transfer(cpi_ctx, amount)?; Ok(()) }
**References**:
- building-secure-contracts/not-so-smart-contracts/solana/arbitrary_cpi
- Trail of Bits lint: `unchecked-cpi-program-id`rust#[cfg(test)] mod tests { use super::*; #[test] #[should_panic] fn test_rejects_wrong_program_id() { // Provide wrong program ID, should fail } #[test] #[should_panic] fn test_rejects_non_canonical_pda() { // Provide non-canonical bump, should fail } #[test] #[should_panic] fn test_requires_signer() { // Call without signature, should fail } }
typescriptimport * as anchor from "@coral-xyz/anchor"; describe("security tests", () => { it("rejects arbitrary CPI", async () => { const fakeTokenProgram = anchor.web3.Keypair.generate(); try { await program.methods .withdraw(amount) .accounts({ tokenProgram: fakeTokenProgram.publicKey, // Wrong program }) .rpc(); assert.fail("Should have rejected fake program"); } catch (err) { // Expected to fail } }); });
bash# Run local validator for testing solana-test-validator # Deploy and test program anchor test
building-secure-contracts/not-so-smart-contracts/solana/Before completing Solana program audit:
CPI Security (CRITICAL):
invoke()Program<'info, T> typePDA Security (CRITICAL):
find_program_address() or Anchor seeds constraintAccount Validation (HIGH):
account.owner == expected_program_idAccount<'info, T> typeSigner Validation (CRITICAL):
is_signeraccount.is_signer == trueSigner<'info> typeSysvar Security (HIGH):
load_instruction_at_checked()Instruction Introspection (MEDIUM):
Testing:
Other measured skills in the registry, with their headline benchmark lift.