/auth-validation
Trigger Pattern Always required for Soroban audits - Inject Into Breadth agents, depth agents
$ npx -y skills add PlamenTSV/plamen --skill auth-validation --agent claude-codeHow it fires
How this skill gets triggered: by you, by Claude, or both.
- Fires itselfAuto-invocation. Claude auto-loads it when your prompt matches the work.Auto-invocation is when the right skill fires by itself at the right moment, driven by a FLOW.md router and a hook, instead of you invoking it by name. It is the difference between a skill being installed and a skill actually getting used.Read the full definition →
- You can call itInvoke it directly when you want it.
- Slash command
/auth-validation
Context preview
The summary Claude sees to decide when to auto-load this skill.
Trigger Pattern Always required for Soroban audits - Inject Into Breadth agents, depth agents
SKILL.md
auth-validation.SKILL.mdname: "auth-validation"
description: "Trigger Pattern Always required for Soroban audits - Inject Into Breadth agents, depth agents"
AUTH_VALIDATION Skill (Soroban)
> **Trigger Pattern**: Always required for Soroban audits > **Inject Into**: Breadth agents, depth agents > **Finding prefix**: `[AV-N]` > **Rules referenced**: R4, R6, R10, R13
Soroban's authorization model differs fundamentally from EVM: instead of `msg.sender`, callers invoke `require_auth()` or `require_auth_for_args()` on an `Address`. Missing or incorrectly scoped auth is the most common critical bug class on Soroban.
1. Auth Inventory
For EVERY `pub fn` in each contract, determine whether it modifies state and whether auth is present:
| Function | Modifies State? | Modifies Balance/Config/Ownership? | `require_auth` Present? | Auth Address | Missing? | |----------|----------------|-------------------------------------|------------------------|-------------|----------| | `{fn_name}` | YES/NO | YES/NO | YES/NO | `{address_var or NONE}` | FLAG if modifies state but NO auth |
**Critical patterns to flag**:
- Any function writing to storage without a prior `require_auth()` call
- Any function that transfers funds or mints tokens without auth
- Any function that updates privileged config (admin, fee, whitelist) without auth
**Soroban note**: `require_auth()` panics if the address has not authorized the invocation. It does NOT return a bool — absence means the call proceeds without authorization.
2. Auth Tree Propagation
When a contract calls another contract via `invoke_contract`, the auth context must propagate to sub-calls. Trace each cross-contract invocation:
| Calling Fn | Sub-Contract Invocation | Auth Expected in Sub-Call? | `AuthorizedInvocation` Provided? | Sub-Call Protected? | |------------|------------------------|---------------------------|----------------------------------|---------------------| | `{fn}` | `invoke_contract({contract}, {fn})` | YES/NO | YES/NO | YES/NO |
**Attack surface**: If a top-level function calls `require_auth(user)` but then invokes a sub-contract on behalf of the user without passing the correct `AuthorizedInvocation` tree, the sub-contract cannot verify the user actually authorized the sub-call.
**Check for**:
- `require_auth()` at top level but no auth context propagated to sub-contract invocations
- Sub-contracts that perform privileged operations but rely on the caller to have checked auth (missing defense-in-depth)
- `invoke_contract_check_auth` used as a bypass for normal `require_auth` flow
- **Frame-descent (originate side)**: when the current contract invokes an intermediary that then pulls tokens/assets via a nested `transfer(from = this_contract, ...)`, that nested frame is NOT auto-authorized by the direct-caller rule — verify the originating function pre-declares it (`authorize_as_current_contract` / equivalent). Differentially compare EVERY sibling that performs the same nested-pull pattern; if one sibling pre-authorizes and another does not, the omission is a finding.
3. Custom Account Contracts (`__check_auth`)
For any contract implementing the `CustomAccountInterface` (contains `__check_auth`):
| Check | Present? | Correct? | Notes | |-------|----------|---------|-------| | Signature verification against stored public keys | YES/NO | YES/NO | | | Replay protection (nonce or sequence number) | YES/NO | YES/NO | | | Signature threshold enforcement (multi-sig) | YES/NO | YES/NO | | | `context.signature_payload` used (not raw payload) | YES/NO | YES/NO | | | Auth invocation tree validated against expected function | YES/NO | YES/NO | |
**Critical**: `__check_auth` is called by the host to verify whether an address has authorized an invocation. Bugs here allow bypassing authorization for ALL operations that use this account contract.
**Specific checks**:
- Verify the function uses `context.signature_payload` (the canonical payload) rather than constructing its own hash
- Verify nonce is incremented atomically to prevent replay
- For multi-sig: verify the threshold check uses `>=` not `>` (off-by-one)
- Verify `sub_invocations` in the auth tree are validated, not just the top-level call
- **Sub-invocation auth bypass (host issue #788)**: When contract B calls contract A with different `require_auth` argument sets in the same transaction, a custom auth contract that approves one call may inadvertently authorize both. Verify the `__check_auth` implementation distinguishes between different invocation contexts.
- **Spending limits**: If the custom account implements spending limits, verify limits are checked BEFORE authorization and decremented atomically. A gap between check and decrement allows multiple calls within one transaction to bypass the limit.
- **Session keys / delegated auth**: If the custom account supports session keys, verify (1) session key scope is enforced (only authorized functions/amounts), (2) session keys expire, (3) revocation is immediate (not deferred to next transaction)
4. Auth Argument Matching (`require_auth_for_args`)
`require_auth_for_args` binds authorization to specific argument values. Verify the correct arguments are passed:
| Function | Uses `require_auth_for_args`? | Arguments Passed | Arguments That Should Be Bound | Mismatch? | |----------|------------------------------|-----------------|-------------------------------|-----------| | `{fn}` | YES/NO | `{args list}` | `{expected critical args}` | FLAG if mismatch |
**Attack**: If `approve(spender, amount)` calls `require_auth_for_args(owner, (spender, wrong_amount))`, an attacker can get the owner to authorize a small amount but then pass a larger amount in the actual call.
**Pattern to check**:
- Does `require_auth_for_args` bind ALL security-critical parameters (amounts, recipients, token addresses)?
- Are the bound arguments the actual runtime values, not hardcoded or stale values?
- Is the args tuple in the same order as the function signatu
Read more
name: "auth-validation" description: "Trigger Pattern Always required for Soroban audits - Inject Into Breadth agents, depth agents"
AUTH_VALIDATION Skill (Soroban)
> **Trigger Pattern**: Always required for Soroban audits > **Inject Into**: Breadth agents, depth agents > **Finding prefix**: `[AV-N]` > **Rules referenced**: R4, R6, R10, R13
Soroban's authorization model differs fundamentally from EVM: instead of `msg.sender`, callers invoke `require_auth()` or `require_auth_for_args()` on an `Address`. Missing or incorrectly scoped auth is the most common critical bug class on Soroban.
1. Auth Inventory
For EVERY `pub fn` in each contract, determine whether it modifies state and whether auth is present:
| Function | Modifies State? | Modifies Balance/Config/Ownership? | `require_auth` Present? | Auth Address | Missing? | |----------|----------------|-------------------------------------|------------------------|-------------|----------| | `{fn_name}` | YES/NO | YES/NO | YES/NO | `{address_var or NONE}` | FLAG if modifies state but NO auth |
**Critical patterns to flag**:
- Any function writing to storage without a prior `require_auth()` call
- Any function that transfers funds or mints tokens without auth
- Any function that updates privileged config (admin, fee, whitelist) without auth
**Soroban note**: `require_auth()` panics if the address has not authorized the invocation. It does NOT return a bool — absence means the call proceeds without authorization.
2. Auth Tree Propagation
When a contract calls another contract via `invoke_contract`, the auth context must propagate to sub-calls. Trace each cross-contract invocation:
| Calling Fn | Sub-Contract Invocation | Auth Expected in Sub-Call? | `AuthorizedInvocation` Provided? | Sub-Call Protected? | |------------|------------------------|---------------------------|----------------------------------|---------------------| | `{fn}` | `invoke_contract({contract}, {fn})` | YES/NO | YES/NO | YES/NO |
**Attack surface**: If a top-level function calls `require_auth(user)` but then invokes a sub-contract on behalf of the user without passing the correct `AuthorizedInvocation` tree, the sub-contract cannot verify the user actually authorized the sub-call.
**Check for**:
- `require_auth()` at top level but no auth context propagated to sub-contract invocations
- Sub-contracts that perform privileged operations but rely on the caller to have checked auth (missing defense-in-depth)
- `invoke_contract_check_auth` used as a bypass for normal `require_auth` flow
- **Frame-descent (originate side)**: when the current contract invokes an intermediary that then pulls tokens/assets via a nested `transfer(from = this_contract, ...)`, that nested frame is NOT auto-authorized by the direct-caller rule — verify the originating function pre-declares it (`authorize_as_current_contract` / equivalent). Differentially compare EVERY sibling that performs the same nested-pull pattern; if one sibling pre-authorizes and another does not, the omission is a finding.
3. Custom Account Contracts (`__check_auth`)
For any contract implementing the `CustomAccountInterface` (contains `__check_auth`):
| Check | Present? | Correct? | Notes | |-------|----------|---------|-------| | Signature verification against stored public keys | YES/NO | YES/NO | | | Replay protection (nonce or sequence number) | YES/NO | YES/NO | | | Signature threshold enforcement (multi-sig) | YES/NO | YES/NO | | | `context.signature_payload` used (not raw payload) | YES/NO | YES/NO | | | Auth invocation tree validated against expected function | YES/NO | YES/NO | |
**Critical**: `__check_auth` is called by the host to verify whether an address has authorized an invocation. Bugs here allow bypassing authorization for ALL operations that use this account contract.
**Specific checks**:
- Verify the function uses `context.signature_payload` (the canonical payload) rather than constructing its own hash
- Verify nonce is incremented atomically to prevent replay
- For multi-sig: verify the threshold check uses `>=` not `>` (off-by-one)
- Verify `sub_invocations` in the auth tree are validated, not just the top-level call
- **Sub-invocation auth bypass (host issue #788)**: When contract B calls contract A with different `require_auth` argument sets in the same transaction, a custom auth contract that approves one call may inadvertently authorize both. Verify the `__check_auth` implementation distinguishes between different invocation contexts.
- **Spending limits**: If the custom account implements spending limits, verify limits are checked BEFORE authorization and decremented atomically. A gap between check and decrement allows multiple calls within one transaction to bypass the limit.
- **Session keys / delegated auth**: If the custom account supports session keys, verify (1) session key scope is enforced (only authorized functions/amounts), (2) session keys expire, (3) revocation is immediate (not deferred to next transaction)
4. Auth Argument Matching (`require_auth_for_args`)
`require_auth_for_args` binds authorization to specific argument values. Verify the correct arguments are passed:
| Function | Uses `require_auth_for_args`? | Arguments Passed | Arguments That Should Be Bound | Mismatch? | |----------|------------------------------|-----------------|-------------------------------|-----------| | `{fn}` | YES/NO | `{args list}` | `{expected critical args}` | FLAG if mismatch |
**Attack**: If `approve(spender, amount)` calls `require_auth_for_args(owner, (spender, wrong_amount))`, an attacker can get the owner to authorize a small amount but then pass a larger amount in the actual call.
**Pattern to check**:
- Does `require_auth_for_args` bind ALL security-critical parameters (amounts, recipients, token addresses)?
- Are the bound arguments the actual runtime values, not hardcoded or stale values?
- Is the args tuple in the same order as the function signatu
Autonomous Web3 security auditor for Claude Code and OpenAI Codex CLI. Orchestrates 18-100 AI agents across 40+ phases to produce audit reports with verified PoC exploits — for smart contracts and L1 node-client infrastructure.
Repo: PlamenTSV/plamen
Other skills on plamen.
- /ability-analysis
Trigger Pattern Always (Aptos Move) - foundational security check - Inject Into Breadth agents, depth agents
Open skill - /bit-shift-safety
Trigger Pattern Always (Aptos Move) - Move VM aborts on shift = bit width - Inject Into Breadth agents, depth-edge-case
Open skill - /centralization-risk
Trigger Protocol has privileged roles (admin, operator, governance, resource account owner) - Covers Single points of failure, privilege escalation, external governance dependen...
Open skill - /cross-chain-timing
Trigger Pattern wormhole|layerzero|ccip|bridge|cross_chain|vaa|guardian|emitter|relay|remote_chain|payload|nonce.sequence - Inject Into Breadth agents, depth-external
Open skill - /dependency-audit
Trigger EXTERNAL_LIB flag detected (protocol uses third-party Move dependencies) - Used by Breadth agents, depth-external
Open skill - /economic-design-audit
Trigger Pattern MONETARY_PARAMETER flag (required) - Inject Into Breadth agents (merged via M4 hierarchy)
Open skill

