solana-vulnerability-scanner
关于
This skill scans Solana and Anchor programs for six critical security vulnerabilities like arbitrary CPI and improper PDA validation. It's designed for developers to use during security audits or pre-launch assessments of Solana protocols. The tool automatically detects relevant Rust files and analyzes platform-specific risks in account validation and cross-program invocations.
快速安装
Claude Code
推荐npx skills add trailofbits/skills -a claude-code/plugin add https://github.com/trailofbits/skillsgit clone https://github.com/trailofbits/skills.git ~/.claude/skills/solana-vulnerability-scanner在 Claude Code 中复制并粘贴此命令以安装该技能
技能文档
Solana Vulnerability Scanner
1. Purpose
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.
2. When to Use This Skill
- Auditing Solana programs (native Rust or Anchor)
- Reviewing cross-program invocation (CPI) logic
- Validating program-derived address (PDA) implementations
- Pre-launch security assessment of Solana protocols
- Reviewing account validation patterns
- Assessing instruction introspection logic
3. Platform Detection
File Extensions & Indicators
- Rust files:
.rs
Language/Framework Markers
// 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
Project Structure
programs/*/src/lib.rs- Program implementationAnchor.toml- Anchor configurationCargo.tomlwithsolana-programoranchor-langtests/- Program tests
Tool Support
- Trail of Bits Solana Lints: Rust linters for Solana
- Installation: Add to Cargo.toml
- anchor test: Built-in testing framework
- Solana Test Validator: Local testing environment
4. How This Skill Works
When invoked, I will:
- Search your codebase for Solana/Anchor programs
- Analyze each program for the 6 vulnerability patterns
- Report findings with file references and severity, above them a coverage table carrying a verdict for every pattern
- Provide fixes for each identified issue
- Check account validation and CPI security
5. Example Output
6. Vulnerability Patterns (6 Patterns)
I check for 6 critical vulnerability patterns unique to Solana. For detailed detection patterns, code examples, mitigations, and testing strategies, see VULNERABILITY_PATTERNS.md.
Pattern Summary:
- Arbitrary CPI ⚠️ CRITICAL - User-controlled program IDs in CPI calls
- Improper PDA Validation ⚠️ CRITICAL - Using create_program_address without canonical bump
- Missing Ownership Check ⚠️ HIGH - Deserializing accounts without owner validation
- Missing Signer Check ⚠️ CRITICAL - Authority operations without is_signer check
- Sysvar Account Check ⚠️ HIGH - Spoofed sysvar accounts (pre-Solana 1.8.1)
- Improper Instruction Introspection ⚠️ MEDIUM - Absolute indexes allowing reuse
For complete vulnerability patterns with code examples, see VULNERABILITY_PATTERNS.md.
7. Scanning Workflow
Step 1: Platform Identification
- Verify Solana program (native or Anchor)
- Check Solana version (1.8.1+ for sysvar security)
- Locate program source (
programs/*/src/lib.rs) - Identify framework (native vs Anchor)
Step 2: CPI Security Review
# 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 ID validated before invocation
- Cannot pass user-controlled program accounts
- Anchor: Uses
Program<'info, T>type
Step 3: PDA Validation Check
# 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:
- Uses
find_program_address()or Anchorseedsconstraint - Bump seed stored and reused
- Not using user-provided bump
Step 4: Account Validation Sweep
# 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:
- Owner validated before deserialization
- Signer check for authority accounts
- Anchor: Uses
Account<'info, T>andSigner<'info>
Step 5: Instruction Introspection Review
# 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/
- Using checked functions (Solana 1.8.1+)
- Using relative indexing
- Proper correlation validation
Step 6: Trail of Bits Solana Lints
# 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)
8. Reporting Format
Coverage Table
Report on every pattern in §6, whether or not it turned anything up. Emit this table above the findings, with all 6 rows present:
| # | Pattern | Verdict | Evidence |
|---|---|---|---|
| 1 | Arbitrary CPI | n/a | this program makes no cross-program invocations |
| 2 | Improper PDA Validation | ||
| 3 | Missing Ownership Check | ||
| 4 | Missing Signer Check | ||
| 5 | Sysvar Account Check | ||
| 6 | Improper Instruction Introspection |
Each verdict is one of:
found— citefile:lineand write the finding up in full below.clear— the pattern applies to this program and the program handles it. Name the constraint, account type, or check you searched for, so a reader can repeat the search.n/a— the pattern cannot apply here. Give the reason in one clause ("this program makes no CPI calls"). Not having looked is notn/a. Pattern 5 is version-scoped (pre-Solana 1.8.1): cite thesolana-programversion the program targets rather than dropping the row, since "targets 1.17, fixed upstream" and "did not look" are otherwise the same answer.
A table with fewer than 6 rows is an incomplete scan and must be reported as one. A row whose Verdict cell is empty is incomplete in the same way: row 1 above is filled in to show the shape, and every row is filled in the same way before the report is done. Six clear verdicts is a
result a reader can act on. A report that covers two patterns and says nothing about the other four reads
exactly like a clean program, and that is the failure this table exists to prevent.
Finding Template
## [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**:
```rust
// 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:
```rust
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`
9. Priority Guidelines
Critical (Immediate Fix Required)
- Arbitrary CPI (attacker-controlled program execution)
- Improper PDA validation (account spoofing)
- Missing signer check (unauthorized access)
High (Fix Before Launch)
- Missing ownership check (fake account data)
- Sysvar account check (authentication bypass, pre-1.8.1)
Medium (Address in Audit)
- Improper instruction introspection (logic bypass)
10. Testing Recommendations
Unit Tests
#[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
}
}
Integration Tests (Anchor)
import * 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
}
});
});
Solana Test Validator
# Run local validator for testing
solana-test-validator
# Deploy and test program
anchor test
11. Additional Resources
- Building Secure Contracts:
building-secure-contracts/not-so-smart-contracts/solana/ - Trail of Bits Solana Lints: https://github.com/trailofbits/solana-lints
- Anchor Documentation: https://www.anchor-lang.com/
- Solana Program Library: https://github.com/solana-labs/solana-program-library
- Solana Cookbook: https://solanacookbook.com/
12. Quick Reference Checklist
Before completing Solana program audit:
CPI Security (CRITICAL):
- ALL CPI calls validate program ID before
invoke() - Cannot use user-provided program accounts
- Anchor: Uses
Program<'info, T>type
PDA Security (CRITICAL):
- PDAs use
find_program_address()or Anchorseedsconstraint - Bump seed stored and reused (not user-provided)
- PDA accounts validated against canonical address
Account Validation (HIGH):
- ALL accounts check owner before deserialization
- Native: Validates
account.owner == expected_program_id - Anchor: Uses
Account<'info, T>type
Signer Validation (CRITICAL):
- ALL authority accounts check
is_signer - Native: Validates
account.is_signer == true - Anchor: Uses
Signer<'info>type
Sysvar Security (HIGH):
- Using Solana 1.8.1+
- Using checked functions:
load_instruction_at_checked() - Sysvar addresses validated
Instruction Introspection (MEDIUM):
- Using relative indexes for correlation
- Proper validation between related instructions
- Cannot reuse same instruction across multiple calls
Testing:
- Unit tests cover all account validation
- Integration tests with malicious inputs
- Local validator testing completed
- Trail of Bits lints enabled and passing
- Coverage table emitted with all 6 rows, each carrying a verdict of
found,clearorn/awith a reason
13. Rationalizations to Reject
- "The program is small, so most patterns obviously don't apply." Obvious to whom? An
n/acosts one clause and makes the judgment reviewable. Silence records nothing, and a reader cannot tell it apart from not having checked. - "The Trail of Bits lints pass, so the program is clean." The lints cover a subset of these 6 patterns. A clean lint run is one row of evidence, not a verdict on the patterns it never examined. Say which patterns it covered.
- "I checked the patterns that matter for this program." Deciding which patterns matter is the scan, not a precondition for starting it. Rank by severity after the table is complete, not by leaving rows out.
- "No findings, so there is nothing to report." A zero-finding scan still emits the full coverage table. That table is the deliverable: it is what distinguishes a program that was examined from one that was glanced at.
- "Anchor handles account validation." Name the constraint. Anchor validates what the account struct
declares and nothing more:
#[account(mut)]is not an ownership check, andUncheckedAccountopts out entirely. Cite the attribute, not the framework. - "The PDA derivation makes it safe." Derivation is not validation.
create_program_addresswithout the canonical bump admits multiple valid addresses, which is pattern 2 in full.
GitHub 仓库
常见问题
什么是 solana-vulnerability-scanner Skill?
solana-vulnerability-scanner 是一个 Claude Skill,作者为 trailofbits。Skill 将 Claude 按需加载的说明和资源打包,让 Claude 无需额外提示即可执行与 solana-vulnerability-scanner 相关的任务。
如何安装 solana-vulnerability-scanner?
使用本页的安装命令:将 solana-vulnerability-scanner 作为插件添加到 Claude Code,或将其仓库克隆到 skills 目录,然后重启 Claude 以加载该 Skill。
solana-vulnerability-scanner 属于哪个分类?
solana-vulnerability-scanner 属于测试分类。
solana-vulnerability-scanner 可以免费使用吗?
可以。solana-vulnerability-scanner 已收录在 AIMCP,可免费安装。
相关推荐技能
该Skill通过60+个学术基准测试(如MMLU、GSM8K等)评估大语言模型质量,适用于模型对比、学术研究及训练进度追踪。它支持HuggingFace、vLLM和API接口,被EleutherAI等行业领先机构广泛采用。开发者可通过简单命令行快速对模型进行多任务批量评估。
这个Claude Skill提供了关于Cloudflare Cron Triggers的完整知识库,用于通过cron表达式定时执行Workers。它支持配置周期性任务、维护作业和自动化工作流,并能处理常见的cron触发错误。开发者可以用它来设置定时任务、测试cron处理器,并集成Workflows和Green Compute功能。
该Skill为开发者提供了基于Playwright的本地Web应用测试工具集,支持自动化测试前端功能、调试UI行为、捕获屏幕截图和查看浏览器日志。它包含管理服务器生命周期的辅助脚本,可直接作为黑盒工具运行而无需阅读源码。适用于需要快速验证本地Web应用界面和交互功能的开发场景。
这个Skill用于开发分支完成后的集成决策,当代码实现完成且测试通过时,它会引导开发者选择合适的工作流。它首先验证测试状态,然后提供合并、创建PR或清理等结构化选项。核心价值在于确保代码质量的同时,标准化分支收尾流程。
