GitHub Security Advisory: GHSA-x442-m7cc-hr92
Package: kora-lib (RUST)
Severity: MODERATE (CVSS 0.0)
CWE: CWE-693: Protection Mechanism Failure
Vulnerable versions: < 2.0.5
Patched in: 2.0.5

Summary: kora-lib: Unrecognized Instruction Types Create Empty Stubs That Bypass Fee Payer Policy

Details:
## Summary

When inner CPI instructions use instruction types not recognized by Kora's parser (including Token-2022 extension instructions like `ConfidentialTransfer`, `TransferFeeExtension::WithdrawWithheldTokens`, etc.), they are reconstructed as stub instructions with empty accounts and empty data. These stubs fail deserialization during fee payer policy validation and are silently skipped, meaning any fee payer usage within those instructions goes completely unchecked.

## Severity

**Medium**

## Affected Component

- **File:** `crates/lib/src/transaction/instruction_util.rs`
- **Functions:** `reconstruct_system_instruction()`, `reconstruct_spl_token_instruction()`
- **Lines:** 750–753, 1187–1189

## Root Cause

The instruction reconstruction functions have a catch-all `_ =>` arm for unrecognized instruction types that creates a stub `CompiledInstruction` with the correct `program_id_index` but **empty `accounts` and empty `data`**. When this stub reaches the fee payer policy parsing (`parse_system_instructions` / `parse_token_instructions`), deserialization of empty data fails. The parsing functions also have a catch-all `_ => {}` that silently skips the failed instruction. The result: the instruction exists in `all_instructions` (so program allowlist checks pass), but fee payer policy is never enforced on it.

## Vulnerable Code

### Stub Creation

```rust
// crates/lib/src/transaction/instruction_util.rs:750-753
// System program — unrecognized instruction type:
_ => {
    log::error!("Unsupported system instruction type: {}", instruction_type);
    Ok(Self::build_default_compiled_instruction(program_id_index))
}

// crates/lib/src/transaction/instruction_util.rs:1187-1189
// SPL Token program — unrecognized instruction type:
_ => {
    log::error!("Unsupported token instruction type: {}", instruction_type);
    Ok(Self::build_default_compiled_instruction(program_id_index))
}
```

The stub builder:
```rust
pub fn build_default_compiled_instruction(program_id_index: u8) -> CompiledInstruction {
    CompiledInstruction {
        program_id_index,
        accounts: vec![],  // <-- No accounts
        data: vec![],       // <-- No data
    }
}
```

### Silent Skip During Policy Parsing

```rust
// In parse_system_instructions:
if let Ok(system_instruction) = bincode::deserialize::<SystemInstruction>(&instruction.data) {
    match system_instruction {
        // ... known types handled ...
        _ => {}  // <-- Unrecognized: silently skipped
    }
}
// If deserialize fails (empty data), the entire `if let Ok` block is skipped.
// The instruction is not added to any policy check map.

// In parse_token_instructions:
if let Ok(token_instruction) = TokenInstruction::unpack(&instruction.data) {
    match token_instruction {
        // ... known types handled ...
        _ => {}  // <-- Unrecognized: silently skipped
    }
}
// Same: empty data causes unpack to fail, instruction completely invisible to policy.
```

## Proof of Concept

### Affect