Skip to content
Back to skills

Account Validation

ASecurity

Trigger Pattern Always required for Solana audits - Inject Into Breadth agents, depth agents

  • 281 stars
  • 0 votes
  • 0 copies
  • 2 views
  • Added May 27, 2026
blockchainrustgo

Security analysis

A100/100

Scanned May 27, 2026

npx -y skills add PlamenTSV/plamen --skill account-validation --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of Account Validation?

Add the live security badge to your README. It updates with every re-scan.

Security grade badge for Account Validation
[![Security: A — Skills Directory](https://www.skillsdirectory.com/api/skills/plamentsv-account-validation/badge)](https://www.skillsdirectory.com/skills/plamentsv-account-validation)

More formats (shields.io, HTML) on the badges page. Keep it an A: scan every change in CI with Pro.

Download with Pro
SKILL.md
---
name: "account-validation"
description: "Trigger Pattern Always required for Solana audits - Inject Into Breadth agents, depth agents"
---

# ACCOUNT_VALIDATION Skill

> **Trigger Pattern**: Always required for Solana audits
> **Inject Into**: Breadth agents, depth agents
> **Finding prefix**: `[AV-N]`
> **Rules referenced**: S1, S6, S7, S8, R4

For every instruction handler in the Solana program:

## 1. Account Type Inventory

For EACH instruction, list every account with expected constraints:

| # | Account Name | Expected Owner | Expected Type (Discriminator) | Mutable? | Signer? | Constraints (has_one, seeds, etc.) |
|---|-------------|----------------|-------------------------------|----------|---------|-----------------------------------|
| 1 | {name} | {program_id / system / token} | {Account<T> / UncheckedAccount / etc.} | YES/NO | YES/NO | {list all} |

**Anchor auto-checks**: Anchor's `Account<T>` validates owner + discriminator automatically. `UncheckedAccount` / `AccountInfo` do NOT - manual validation required.

## 2. Owner Check Audit

For each `AccountInfo` or `UncheckedAccount` usage:

| Account | Owner Validated? | Validation Location | Correct Owner? | Missing? |
|---------|-----------------|---------------------|---------------|----------|
| {name} | YES/NO | {line} | {expected vs actual} | FLAG if NO |

**Critical pattern**: Any `AccountInfo` deserialized without prior owner check → arbitrary data injection.
**Anchor pattern**: `#[account(owner = expected_program)]` or manual `require!(account.owner == &expected_id)`.

## 3. Discriminator Check

For all accounts deserialized from raw data:

| Account | Uses Account<T>? | Discriminator Checked? | Can Substitute Different Account Type? |
|---------|------------------|----------------------|---------------------------------------|
| {name} | YES/NO | YES/NO (Anchor auto) | {if NO: what types could be substituted} |

**Attack**: Pass an account of Type B where Type A is expected - different data layout, fields interpreted incorrectly.
**Safe**: Anchor's `Account<T>` checks the 8-byte discriminator. Manual programs must check explicitly.

**Anchor IDL buffer amplification**: If the program uses Anchor with the default IDL feature enabled (check Cargo.toml for `idl-build` feature or absence of `no-idl` — enabled by default), `IdlCreateBuffer` is a permissionless instruction that lets ANYONE create program-owned accounts with nearly arbitrary data at arbitrary lengths. This means a missing discriminator check is exploitable even when no other account type in the program has a suitable layout for cosplay — the attacker crafts one via `IdlCreateBuffer`. When flagging a discriminator bypass: if IDL feature is enabled, severity is NOT mitigated by "no suitable account type exists for substitution."

## 4. Data Matching (Cross-Account References)

For each cross-account reference (has_one, constraint, seeds):

| Instruction | Account A | Account B | Relationship | Validated? | Bypass? |
|-------------|-----------|-----------|-------------|-----------|---------|
| {ix} | {a} | {b} | {a.field == b.key()} | YES/NO | {if NO: how to exploit} |

**Pattern**: Ensure that when Account A references Account B (e.g., `vault.mint == mint.key()`), the relationship is enforced on-chain.
**Attack**: Substitute a different mint account that the vault doesn't actually belong to.

## 5. Remaining Accounts Audit

For each use of `ctx.remaining_accounts`:

| Instruction | Remaining Account Usage | Owner Validated? | Type Validated? | Signer Checked? | Data Validated? |
|-------------|------------------------|-----------------|----------------|-----------------|-----------------|
| {ix} | {purpose} | YES/NO | YES/NO | YES/NO | YES/NO |

**Critical**: `remaining_accounts` bypass Anchor's automatic validation. Every field must be checked manually.
**Attack**: Inject attacker-controlled accounts via remaining_accounts to redirect funds or corrupt state.

## 6. Duplicate Account Detection

For each instruction with 2+ mutable accounts:

| Instruction | Mutable Account A | Mutable Account B | Key Uniqueness Enforced? | Self-Transfer Risk? |
|-------------|-------------------|-------------------|------------------------|-------------------|
| {ix} | {a} | {b} | YES/NO | {if NO: impact of a==b} |

**Attack (S7)**: Pass the same account as both `from` and `to` in a transfer → potential balance inflation.
**Defense**: `require!(account_a.key() != account_b.key())` or Anchor `constraint`.

## 7. Sysvar Validation

For each sysvar account passed as input:

| Sysvar | Passed As | Address Validated? | Could Be Spoofed? |
|--------|-----------|-------------------|-------------------|
| Clock | AccountInfo | YES/NO | {if NO: attacker controls time} |
| Rent | AccountInfo | YES/NO | {if NO: attacker controls rent} |
| Instructions | AccountInfo | YES/NO | {if NO: Wormhole-style attack} |

**Safe pattern**: Use `Sysvar::from_account_info()` or `_checked` variants.
**Unsafe pattern**: Raw deserialization of sysvar data from unchecked AccountInfo.

## 8. Trust Chain Analysis

For each account validation chain, trace to its root:

| Account | Validated Against | Root Trust Anchor | Chain Complete? |
|---------|------------------|-------------------|----------------|
| {account} | {what validates it} | PDA / Program ID / Hardcoded pubkey / NONE | YES/NO |

**Pattern**: Validation chain must root in a known-good value. If chain roots in user input → FINDING.
**Example**: `authority` validated against `vault.authority`, `vault` validated against PDA seeds → chain roots in PDA (good).

## Finding Template

```markdown
**ID**: [AV-N]
**Severity**: [based on what attacker can do with invalid account]
**Step Execution**: ✓1,2,3,4,5,6,7,8 | ✗(reasons) | ?(uncertain)
**Rules Applied**: [S1:✓, S6:✓/✗, S7:✓/✗, S8:✓/✗, R4:✓/✗]
**Location**: program/src/instructions/{file}.rs:LineN
**Title**: Missing [validation type] for [account] in [instruction] enables [attack]
**Description**: [Specific missing validation with code reference]
**Impact**: [What attacker can achieve: fund theft, state corruption, DoS]
```

---

## Step Execution Checklist (MANDATORY)

| Section | Required | Completed? | Notes |
|---------|----------|------------|-------|
| 1. Account Type Inventory | YES | ✓/✗/? | For every instruction |
| 2. Owner Check Audit | YES | ✓/✗/? | For every AccountInfo/UncheckedAccount |
| 3. Discriminator Check | YES | ✓/✗/? | For all deserialized accounts |
| 4. Data Matching | YES | ✓/✗/? | For all cross-account references |
| 5. Remaining Accounts Audit | IF remaining_accounts used | ✓/✗(N/A)/? | Manual validation check |
| 6. Duplicate Account Detection | YES | ✓/✗/? | For all mutable account pairs |
| 7. Sysvar Validation | IF sysvars passed as AccountInfo | ✓/✗(N/A)/? | Address validation |
| 8. Trust Chain Analysis | YES | ✓/✗/? | Chain to root trust anchor |

Attribution

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.

Comments

Loading comments…

Newsletter

Get the weekly list of safe new skills, and the ones we pulled.