agents/skills/solana/verification-protocol/SKILL.md
Trigger Pattern Always (used by all verifier agents) - Inject Into security-verifier agents (Phase 5)
npx skillsauth add plamentsv/plamen verification-protocolInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
Trigger Pattern: Always (used by all verifier agents) Inject Into: security-verifier agents (Phase 5) Purpose: Prove hypotheses TRUE or FALSE using LiteSVM tests with Rust PoC code.
CRITICAL: For EVERY piece of evidence used in verification, you MUST tag its source. Evidence from mocks or unverified external programs CANNOT support a REFUTED verdict.
| Tag | Meaning | Valid for REFUTED? |
|-----|---------|-------------------|
| [PROD-ONCHAIN] | Production Solana account data (via solana account or RPC) | YES |
| [PROD-SOURCE] | Verified source from Solana Explorer / anchor-verified | YES |
| [PROD-LITESVM] | Tested on LiteSVM with mainnet account dumps | YES |
| [CODE] | Audited codebase (in-scope source) | YES |
| [MOCK] | Mock/test accounts or programs | NO |
| [EXT-UNV] | External, unverified program behavior | NO |
| [DOC] | Documentation/spec only | NO (needs verification) |
Before ANY verdict, fill this table:
### Evidence Audit
| Claim | Evidence Source | Tag | Valid for REFUTED? |
|-------|-----------------|-----|-------------------|
| "CPI returns X" | Mock program | [MOCK] | NO |
| "PDA seeds match" | lib.rs:123 | [CODE] | YES |
| "Account layout is Y" | Solana Explorer verified | [PROD-SOURCE] | YES |
AUTOMATIC OVERRIDE: If ANY evidence supporting REFUTED has tag [MOCK] or [EXT-UNV]:
Before writing ANY test code, you MUST answer:
NOT: "Account validation is missing"
NOT: "State is inconsistent"
YES: "Instruction [X] accepts account [Y] without checking owner/type/seeds,
allowing an attacker to substitute a crafted account with arbitrary data
at field [Z] (file:line)"
NOT: "Accounts are different"
NOT: "Wrong state"
YES: "Before exploit: user_balance = 1000 tokens
After exploit: user_balance = 0, attacker_balance = 1000
Expected: transaction should have reverted with AccountConstraint error"
NOT: assert!(exploit_worked)
YES: assert_eq!(attacker_token_balance, expected_stolen_amount)
OR: assert!(result.is_err(), "should have reverted but succeeded")
OR: assert_ne!(state_before, state_after, "state changed when it should not")
If you cannot answer all three -> ASK FOR CLARIFICATION
Before writing test code, verify these two gates. If either FAILS, adjust the hypothesis.
Trace a call path from a permissionless entry point to the vulnerable code.
If NO entry point reaches the vulnerable code → UNREACHABLE → FALSE_POSITIVE. If reachable only through a restricted path → document the restriction, adjust likelihood.
Substitute real-world value domains into the expression that triggers the bug.
If the bug requires values outside feasible domains → INFEASIBLE → FALSE_POSITIVE. If feasible only at extreme but realistic parameters → document the threshold, proceed with adjusted severity.
Both gates PASS → proceed to PoC. Either gate FAILS → document and stop.
See
templates.mdin this directory for all LiteSVM test templates (Templates 1-5), Trident fuzz test setup (Template 6), and Fork Testing Equivalent (production account loading).
Substitute ACTUAL program constants (basis points, fee rates, thresholds, account sizes). Apply Rule 10: Use worst realistic operational state, not current snapshot.
State: 'With real constants [fee_bps=X, max_leverage=Y, tvl=Z] at worst-state
[max_users, max_positions], bug triggers when [condition]'
OR: 'With real constants, bug does NOT trigger because [reason]'
When verifying a finding from Validation Sweep ([VS-]) or Blind Spot Scanner ([BLIND-]), you MUST apply Rule 13's 5-question test BEFORE downgrading severity or marking FALSE_POSITIVE:
HARD RULE: If the finding shows Program A has protection X but Program B lacks it for the same user action -> defense parity gap, NOT "by design". Minimum severity: Medium.
If during verification you discover a NEW bug, account validation gap, or edge case NOT covered by any existing hypothesis, document it under:
These will be reviewed by the orchestrator for possible inclusion as new findings.
When verdict is CONTESTED or FALSE_POSITIVE, document the failure details:
Before writing PoC tests for HIGH/CRITICAL findings:
mcp__unified-vuln-db__get_attack_vectors(bug_class="{category}")
mcp__unified-vuln-db__get_similar_findings(pattern="{vulnerability description}")
mcp__unified-vuln-db__validate_hypothesis(hypothesis="{finding summary}")
mcp__unified-vuln-db__search_solodit_live(
keywords="{solana vulnerability pattern}",
impact=["HIGH", "CRITICAL"],
tags=["Access Control", "Logic Error"],
language="Rust",
quality_score=3,
max_results=15
)
Document RAG evidence in output:
### RAG Evidence
- **Attack Vectors Consulted**: [list bug classes queried]
- **Similar Exploits Found**: [count and brief descriptions]
- **Historical Precedent**: [matching Solana-specific vulnerabilities]
| RAG Confidence | Local Verdict | Final Verdict | Action | |----------------|---------------|---------------|--------| | >= 7/8 matches | FALSE_POSITIVE | CONTESTED (override) | Cannot dismiss -- strong precedent | | >= 6/8 matches | FALSE_POSITIVE | CONTESTED (override) | Cannot dismiss -- significant precedent | | < 6/8 matches | FALSE_POSITIVE | FALSE_POSITIVE | Allowed -- limited precedent |
Chain hypotheses receive PRIORITY verification. Multi-step exploits must test the COMPLETE sequence:
#[test]
fn test_chain_hypothesis_full() {
let mut svm = LiteSVM::new();
// ... setup ...
// ========================================
// STEP 1: ENABLER (Finding B)
// Execute action that creates the postcondition
// ========================================
let enabler_ix = Instruction::new_with_borsh(/* ... */);
let tx1 = Transaction::new_signed_with_payer(
&[enabler_ix],
Some(&attacker.pubkey()),
&[&attacker],
svm.latest_blockhash(),
);
svm.send_transaction(tx1).unwrap();
// ========================================
// VERIFY POSTCONDITION CREATED
// Assert precondition for Finding A is now met
// ========================================
let postcondition_account = svm.get_account(&postcondition_pubkey);
// assert postcondition state is as expected
// ========================================
// STEP 2: BLOCKED FINDING (Finding A)
// Execute previously-blocked attack using postcondition
// ========================================
let exploit_ix = Instruction::new_with_borsh(/* ... */);
let tx2 = Transaction::new_signed_with_payer(
&[exploit_ix],
Some(&attacker.pubkey()),
&[&attacker],
svm.latest_blockhash(),
);
let result = svm.send_transaction(tx2);
// ========================================
// VERIFY CHAIN IMPACT
// Combined impact should exceed either finding alone
// ========================================
assert!(result.is_ok(), "Chain exploit should succeed");
let profit = /* calculate attacker profit */;
assert!(profit > 0, "Chain attack should be profitable");
}
For chains involving semi-trusted roles (operator/keeper/crank): Verify BOTH directions: (1) role executes chain to harm users, AND (2) users exploit role timing to trigger chain. If only one direction analyzed -> verdict CANNOT be FALSE_POSITIVE. Return CONTESTED.
The assertion that "proves the bug" succeeded.
| Failure | Meaning | Action | |---------|---------|--------| | Account constraint error | Validation IS present | Re-examine hypothesis | | Program error / custom error | Instruction logic rejects | Check if rejection is the bug or the fix | | Insufficient funds | Setup amounts wrong | Fix test setup | | Transaction too large | Too many accounts/instructions | Split transaction or reduce scope | | Blockhash expired | LiteSVM state issue | Get fresh blockhash |
Attempt 1: Direct implementation of test strategy from hypothesis. Attempt 2: Adjust parameters (different amounts, different account states, different instruction ordering). Attempt 3: Re-examine assumptions (are account constraints correctly modeled? Are PDA seeds correct? Is instruction data serialization correct?). After 5 attempts: If still fails -> FALSE_POSITIVE with documented reasoning.
## Verdict: CONFIRMED
### Bug Mechanism Verified
{Explain what the LiteSVM test proves in 2-3 sentences}
### Test Code
{Full Rust test function}
### Test Output
{Relevant assertions and logged values}
### Key Evidence
| Metric | Value |
|--------|-------|
| Before | {value} |
| After | {value} |
| Expected | {value} |
| Difference | {calculation} |
### Evidence Audit
| Claim | Evidence Source | Tag | Valid for REFUTED? |
|-------|-----------------|-----|-------------------|
### Severity: {LEVEL}
{Justification in 1-2 sentences}
## Verdict: FALSE_POSITIVE
### Attempts Made
**Attempt 1:**
- Approach: {description}
- Result: {what happened -- include error codes}
- Learning: {insight}
**Attempt 2:**
- Approach: {description}
- Result: {what happened}
- Learning: {insight}
**Attempt 3:**
- Approach: {description}
- Result: {what happened}
- Learning: {insight}
### Evidence Audit
| Claim | Evidence Source | Tag | Valid for REFUTED? |
### Why It Is Not a Bug
{Explain the actual behavior and why hypothesis was wrong in 2-3 sentences}
### Error Trace
- **Failure Type**: {type}
- **Location**: {location}
- **Error Code**: {code}
- **State at Failure**: {state}
- **Investigation Question**: {question}
## Verdict: CONTESTED
### Evidence Status
| Checkpoint | Status | Details |
|------------|--------|---------|
| External program behavior verified against PRODUCTION | YES/NO | {details} |
| All callers/instruction paths checked | YES/NO | {details} |
| Account validation completeness confirmed | YES/NO | {details} |
### Evidence Audit
| Claim | Evidence Source | Tag | Valid for REFUTED? |
### Why This Cannot Be REFUTED
{Explain what evidence is missing to definitively rule out the bug}
### Escalation Required
- [ ] Fetch production program source for {external dep}
- [ ] Dump production account state for {account}
- [ ] Check additional instruction paths: {list}
### Error Trace
{as above}
Before marking REFUTED, check ALL boxes:
development
Prepare Solidity projects for a security audit — test coverage, test quality, NatSpec docs, code hygiene, dependency health, best-practice enforcement, deployment readiness, and project documentation checks. Generates a scored Audit Readiness Report and optionally runs static analysis. Trigger on: "prepare for audit", "audit readiness", "pre-audit check", "audit prep", "NatSpec check", or any request to review a Solidity codebase before a security review.
development
Launch the Plamen deterministic Web3 security audit pipeline
development
Run the Plamen smart-contract audit wizard in Codex
testing
Launch the Plamen deterministic L1 infrastructure audit pipeline