sentio.
sentio.

Security scanner for Solana programs

sentio scans your Anchor programs for critical vulnerabilities — missing owner checks, unsafe account patterns, and more. Zero config. CLI-first.

$cargo install sentio-cli
sentio scan
$ sentio scan
══ FINDING 1: SW016 init_if_needed usage (manual review) ══
Severity: medium
Location: ./ralli-bet/programs/ralli-bet/src/instructions/create_lineV2.rs:27:1
Matched Because:
Account `line_pointer` uses `init_if_needed`; review for re-initialization or state-reset risk.
25 pub player_line: Account<'info, PlayerLine>,
26
27> #[account(
28 init_if_needed,
29 payer = admin,
Guidance: Prefer #[account(init, ...)] when possible. If init_if_needed is necessary, confirm the account cannot be abused to reset state.
══ FINDING 2: SW002 Missing owner check ══
Severity: critical
Location: ./ralli-bet/programs/ralli-bet/src/instructions/resolve_game_batch.rs:40:1
Matched Because:
Account `treasury` has no owner constraint and no owner guard in instruction logic; any program-owned account can be passed.
38 pub game_vault: Box<InterfaceAccount<'info, TokenAccount>>,
39
40> /// CHECK: Treasury account to receive fees
41 #[account(mut)]
42 pub treasury: AccountInfo<'info>,
Guidance: Add #[account(owner = expected_program::ID)] or verify account.owner explicitly in the instruction handler.
── Summary ──
Total findings: 3
Critical: 2
Medium: 1

What sentio catches

Glimpse on Rules. More adding soon.

SW001critical
Missing signer check

AccountInfo or UncheckedAccount fields with authority-role names lacking signer constraints — allows attackers to pass unsigned accounts as authorities.

SW002critical
Missing owner check

AccountInfo or UncheckedAccount fields with no owner or address constraint — attacker can pass an account owned by any program.

SW003critical
Arbitrary CPI target

CPI calls via invoke, invoke_signed, or invoke_unchecked without prior program ID validation — allows substitution of a malicious program.

SW008high
Missing post-CPI reload

Account data read after a CPI call without reload() — program operates on stale state that may have been modified by the called program.

SW011high
AccountInfo as data account

Data-account fields declared as AccountInfo<'info> instead of Account<'info, T> — bypasses Anchor's owner and discriminator validation.

SW012high
Missing seeds + bump on PDA

PDA-like account constraints missing both seeds and bump components — proper PDA derivation requires both tied to trusted inputs.

SW016medium
init_if_needed usage

Anchor account fields using init_if_needed — pattern can permit unintended re-initialization or state reset.

SW018medium
Missing realloc::zero = true

Account reallocation without realloc::zero = true — reallocated memory may contain stale data readable by the program or attackers.

SW020critical
AccountInfo as CPI target

CPI target program typed as AccountInfo<'info> instead of Program<'info, T> — skips program ID validation, allowing any program substitution.

SW001critical
Missing signer check

AccountInfo or UncheckedAccount fields with authority-role names lacking signer constraints — allows attackers to pass unsigned accounts as authorities.

SW002critical
Missing owner check

AccountInfo or UncheckedAccount fields with no owner or address constraint — attacker can pass an account owned by any program.

SW003critical
Arbitrary CPI target

CPI calls via invoke, invoke_signed, or invoke_unchecked without prior program ID validation — allows substitution of a malicious program.

SW008high
Missing post-CPI reload

Account data read after a CPI call without reload() — program operates on stale state that may have been modified by the called program.

SW011high
AccountInfo as data account

Data-account fields declared as AccountInfo<'info> instead of Account<'info, T> — bypasses Anchor's owner and discriminator validation.

SW012high
Missing seeds + bump on PDA

PDA-like account constraints missing both seeds and bump components — proper PDA derivation requires both tied to trusted inputs.

SW016medium
init_if_needed usage

Anchor account fields using init_if_needed — pattern can permit unintended re-initialization or state reset.

SW018medium
Missing realloc::zero = true

Account reallocation without realloc::zero = true — reallocated memory may contain stale data readable by the program or attackers.

SW020critical
AccountInfo as CPI target

CPI target program typed as AccountInfo<'info> instead of Program<'info, T> — skips program ID validation, allowing any program substitution.

SW001critical
Missing signer check

AccountInfo or UncheckedAccount fields with authority-role names lacking signer constraints — allows attackers to pass unsigned accounts as authorities.

SW002critical
Missing owner check

AccountInfo or UncheckedAccount fields with no owner or address constraint — attacker can pass an account owned by any program.

SW003critical
Arbitrary CPI target

CPI calls via invoke, invoke_signed, or invoke_unchecked without prior program ID validation — allows substitution of a malicious program.

SW008high
Missing post-CPI reload

Account data read after a CPI call without reload() — program operates on stale state that may have been modified by the called program.

SW011high
AccountInfo as data account

Data-account fields declared as AccountInfo<'info> instead of Account<'info, T> — bypasses Anchor's owner and discriminator validation.

SW012high
Missing seeds + bump on PDA

PDA-like account constraints missing both seeds and bump components — proper PDA derivation requires both tied to trusted inputs.

SW016medium
init_if_needed usage

Anchor account fields using init_if_needed — pattern can permit unintended re-initialization or state reset.

SW018medium
Missing realloc::zero = true

Account reallocation without realloc::zero = true — reallocated memory may contain stale data readable by the program or attackers.

SW020critical
AccountInfo as CPI target

CPI target program typed as AccountInfo<'info> instead of Program<'info, T> — skips program ID validation, allowing any program substitution.