Imported from reason-machines/security-skills (
skills/solana-security-standard/SKILL.md). Install upstream withnpx skills add reason-machines/security-skills --skill solana-security-standard. Copyright stays with the author.
solana-security-standard
Skill by ara.so — Security Skills collection.
The Solana Security Standard provides 37 SOL-0XX rules distilled from $514M+ of real Solana exploits. It catches caller-controlled clock values, cross-market state asymmetry, missing Anchor constraints, wrapper/engine drift, and 33 other bug classes — firing as you code in AI tools, editors, CLI, and CI.
SOL-001 alone covers 2 confirmed bounty wins (caller-controlled now_slot in percolator). The standard works across Claude Code, Cursor, Windsurf, Copilot, Cline, Aider, the VS Code extension, the CLI, Semgrep, GitHub Actions, and any MCP client.
Installation
Claude Code (quickest)
mkdir -p .claude && \
curl -sL https://raw.githubusercontent.com/Copenhagen0x/solana-security-standard/main/plugin-guidance.md \
-o .claude/claude-security-guidance.md && \
curl -sL https://raw.githubusercontent.com/Copenhagen0x/solana-security-standard/main/security-patterns.yaml \
-o .claude/security-patterns.yaml
Then install the security-guidance plugin:
/plugin install security-guidance@claude-plugins-official
/reload-plugins
Or install the full MCP-powered plugin:
/plugin marketplace add Copenhagen0x/solana-security-standard
/plugin install solana-security-standard@solana-security-standard
CLI (any machine)
npx @jelleo/solana-security-standard scan ./programs
No install needed — runs via npx. Zero dependencies.
CI — GitHub Action
# .github/workflows/solana-security.yml
name: Solana Security Standard
on: [pull_request]
permissions:
contents: read
security-events: write
jobs:
scan:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: Copenhagen0x/solana-security-standard@v1
with:
paths: ./programs
fail-on-findings: true
upload-sarif: true
VS Code / Cursor / Windsurf
Install from Open VSX:
- Cursor / Windsurf / VSCodium: Search "Solana Security Standard" in Extensions
- VS Code: Sideload
.vsixfromextensions/vscode/
Shows inline warnings as you type in Rust and TypeScript/JS files.
MCP (any MCP client)
Add to your MCP settings (.cline/mcp.json, .cursor/mcp.json, etc.):
{
"mcpServers": {
"solana-security-standard": {
"command": "npx",
"args": ["-y", "@jelleo/solana-security-mcp"]
}
}
}
Exposes scan_solana_code tool + list_solana_security_rules resource.
AI Agents (Codex, Copilot, Cursor, Windsurf, Cline, Aider)
Copy the rules file for your tool:
| Tool | Copy into repo |
|---|---|
| Codex / AGENTS.md | integrations/codex/AGENTS.md |
| GitHub Copilot | integrations/copilot/.github/copilot-instructions.md |
| Cursor | integrations/cursor/.cursor/ |
| Windsurf | integrations/windsurf/.windsurf/ |
| Cline | integrations/cline/.clinerules |
| Aider | integrations/aider/ |
# Example for Cursor:
curl -sL https://raw.githubusercontent.com/Copenhagen0x/solana-security-standard/main/integrations/cursor/.cursor/rules -o .cursor/rules
Semgrep
semgrep --config https://raw.githubusercontent.com/Copenhagen0x/solana-security-standard/main/semgrep/solana-security-standard.yaml ./programs
CLI Commands
Scan programs
# Scan a directory
npx @jelleo/solana-security-standard scan ./programs
# Scan specific file
npx @jelleo/solana-security-standard scan ./programs/src/lib.rs
# JSON output (for CI)
npx @jelleo/solana-security-standard scan ./programs --format json
# SARIF output (GitHub Code Scanning)
npx @jelleo/solana-security-standard scan ./programs --format sarif > results.sarif
# Filter by tier (drop low-severity hygiene findings)
npx @jelleo/solana-security-standard scan ./programs --min-tier high
# Baseline mode (gate only on NEW findings)
npx @jelleo/solana-security-standard scan ./programs --baseline .sss-baseline.json
Generate baseline
# Create baseline of current findings
npx @jelleo/solana-security-standard scan ./programs --format json > .sss-baseline.json
# Future scans compare against it
npx @jelleo/solana-security-standard scan ./programs --baseline .sss-baseline.json
CLI exits non-zero on findings (gates CI).
Key Rules (37 total)
Critical On-Chain Rules
| Rule | Bug Class | Example |
|---|---|---|
| SOL-001 | Unauthenticated now_slot / clock spoofing |
2 confirmed bounty wins |
| SOL-002 | Cross-market state asymmetry | Counter inflation |
| SOL-003 | Wrapper re-implements engine logic | Drift from canonical |
| SOL-004 | Missing signer check | Unauthorized access |
| SOL-005 | Missing owner check | Wrong program control |
| SOL-006 | Account data type confusion | Misinterpreted structs |
| SOL-007 | Uninitialized account | Reading zeroed data |
| SOL-008 | Arithmetic overflow/underflow | Balance manipulation |
| SOL-009 | PDA derivation mismatch | Wrong seeds → wrong account |
| SOL-010 | Missing rent exemption | Account closure drain |
Integrator / Client-Side Rules
| Rule | Bug Class |
|---|---|
| SOL-029 | Client-side arithmetic overflow (TypeScript/JS) |
| SOL-030 | Missing transaction simulation |
| SOL-031 | Hardcoded program IDs in client |
Full list: https://github.com/Copenhagen0x/solana-security-standard/blob/main/claude-security-guidance.md
Real Code Examples
SOL-001: Unauthenticated now_slot (2 bounty wins)
Vulnerable (percolator-prog ACTIVATE):
pub fn activate(ctx: Context<Activate>, now_slot: u64) -> Result<()> {
let state = &mut ctx.accounts.state;
let clock = Clock::get()?;
// Caller controls now_slot — can bypass time checks
if now_slot < state.cooldown_end {
return Err(ErrorCode::CooldownActive.into());
}
state.active = true;
Ok(())
}
Fixed:
pub fn activate(ctx: Context<Activate>) -> Result<()> {
let state = &mut ctx.accounts.state;
let clock = Clock::get()?;
// Use authenticated clock.slot
if clock.slot < state.cooldown_end {
return Err(ErrorCode::CooldownActive.into());
}
state.active = true;
Ok(())
}
SOL-004: Missing signer check
Vulnerable:
#[derive(Accounts)]
pub struct Withdraw<'info> {
#[account(mut)]
pub authority: AccountInfo<'info>, // Not checked as signer!
#[account(mut)]
pub vault: Account<'info, Vault>,
}
pub fn withdraw(ctx: Context<Withdraw>, amount: u64) -> Result<()> {
// Anyone can pass any authority account
transfer_from_vault(&ctx.accounts.vault, amount)?;
Ok(())
}
Fixed:
#[derive(Accounts)]
pub struct Withdraw<'info> {
#[account(mut)]
pub authority: Signer<'info>, // Enforces signature
#[account(
mut,
has_one = authority // Links vault to this authority
)]
pub vault: Account<'info, Vault>,
}
SOL-009: PDA derivation mismatch
Vulnerable:
// Instruction handler uses wrong seeds
let (vault_pda, _bump) = Pubkey::find_program_address(
&[b"vault", user.key().as_ref()], // Missing market_id seed!
program_id
);
if vault_pda != *vault.key {
return Err(ErrorCode::InvalidVault.into());
}
Fixed:
// Match the seeds used in account derivation
let (vault_pda, _bump) = Pubkey::find_program_address(
&[b"vault", market_id.as_ref(), user.key().as_ref()],
program_id
);
if vault_pda != *vault.key {
return Err(ErrorCode::InvalidVault.into());
}
SOL-029: Client-side arithmetic overflow (TypeScript)
Vulnerable:
// JavaScript numbers lose precision beyond 2^53
const depositAmount = userBalance + newDeposit;
const tx = await program.methods
.deposit(new BN(depositAmount)) // Precision loss!
.accounts({ vault, user })
.rpc();
Fixed:
import { BN } from "@coral-xyz/anchor";
// Use BN for all token math
const depositAmount = new BN(userBalance).add(new BN(newDeposit));
const tx = await program.methods
.deposit(depositAmount)
.accounts({ vault, user })
.rpc();
Configuration
GitHub Action Options
- uses: Copenhagen0x/solana-security-standard@v1
with:
paths: ./programs # Scan target (default: .)
fail-on-findings: true # Exit 1 on findings (default: true)
upload-sarif: true # GitHub Code Scanning (default: true)
min-tier: high # Filter: critical, high, medium, low
baseline: .sss-baseline.json # Gate only on NEW findings
VS Code Extension Settings
{
"solanaSecurityStandard.minTier": "medium",
"solanaSecurityStandard.excludePatterns": [
"**/tests/**",
"**/target/**"
]
}
Semgrep Integration
# Custom config location
semgrep --config ./my-sss-rules.yaml ./programs
# Combine with other rulesets
semgrep --config auto --config ./semgrep/solana-security-standard.yaml ./programs
Common Patterns
Pre-commit Hook
# .git/hooks/pre-commit
#!/bin/bash
npx @jelleo/solana-security-standard scan programs/ --min-tier high
if [ $? -ne 0 ]; then
echo "❌ Solana security scan failed — fix findings or commit with --no-verify"
exit 1
fi
chmod +x .git/hooks/pre-commit
Baseline Workflow (gate on regressions only)
# 1. Generate baseline from current state
npx @jelleo/solana-security-standard scan ./programs --format json > .sss-baseline.json
# 2. Commit it
git add .sss-baseline.json
git commit -m "chore: add security baseline"
# 3. CI uses --baseline mode
npx @jelleo/solana-security-standard scan ./programs --baseline .sss-baseline.json
Only new findings fail the build.
Multi-repo Monorepo Scan
# Scan multiple program directories
npx @jelleo/solana-security-standard scan \
./packages/protocol/programs \
./packages/staking/programs \
./packages/governance/programs
Filter by Rule
CLI doesn't support per-rule filtering, but you can grep JSON output:
npx @jelleo/solana-security-standard scan ./programs --format json \
| jq '.findings[] | select(.rule == "SOL-001")'
MCP Tools
When installed as MCP server:
scan_solana_code
// Agent calls this automatically when you say:
// "scan my Solana program for security issues"
{
"code": "pub fn transfer(ctx: Context<Transfer>, amount: u64) -> Result<()> { ... }",
"language": "rust"
}
// Returns findings with rule, severity, message, line numbers
list_solana_security_rules
// Agent calls when you ask:
// "what SOL-0XX rules apply to signer checks?"
// Returns all 37 rules with descriptions, tiers, examples
Troubleshooting
"Module not found" in CLI
# Clear npx cache and retry
rm -rf ~/.npm/_npx
npx @jelleo/solana-security-standard scan ./programs
VS Code extension not activating
- Check output panel: View → Output → "Solana Security Standard"
- Verify file is
.rs,.ts, or.js - Reload window: Cmd/Ctrl+Shift+P → "Reload Window"
False positives
// SOL-004 flags this as missing signer check:
pub authority: AccountInfo<'info>,
// If it's intentional (e.g., read-only query), add comment:
pub authority: AccountInfo<'info>, // READ-ONLY: no sig required
// Or use Anchor's Signer<'info> to suppress:
pub authority: UncheckedAccount<'info>, // Explicitly unchecked
GitHub Action fails with SARIF error
# Ensure security-events: write permission:
permissions:
contents: read
security-events: write
Baseline not filtering findings
Baseline only gates new findings. If you want to suppress known findings:
# Regenerate baseline after fixing some issues
npx @jelleo/solana-security-standard scan ./programs --format json > .sss-baseline.json
Real Exploit Mappings
Every rule links to real disclosed exploits:
- SOL-001: Wormhole ($325M), percolator bounties
- SOL-008: Mango Markets ($114M)
- SOL-010: Cashio ($52M)
- SOL-017: Crema Finance ($8.8M)
See hacks/README.md for full database.
Badge for Your README
Show adoption:
[](https://github.com/Copenhagen0x/solana-security-standard)
Resources
- Full rule catalog: https://github.com/Copenhagen0x/solana-security-standard/blob/main/claude-security-guidance.md
- Hacks database ($514M+): https://github.com/Copenhagen0x/solana-security-standard/blob/main/hacks/README.md
- Per-rule explainers: https://github.com/Copenhagen0x/solana-security-standard/tree/main/content
- Benchmark (rule accuracy): https://github.com/Copenhagen0x/solana-security-standard/blob/main/BENCHMARK.md