# Solana Anchor Audit Scanner — First-Pass Review & Audit Verify (`halobartku/sol-audit`) Actor

Static security audit for Solana/Anchor Rust repos. Repo URL in, structured findings out: severity, CWE, file:line, evidence, fix. 16 Solana rule families, deterministic, ~6s/repo. Triage or audit-report verification. Honest: 0/11 recall on labelled corpus — cross-check, not human-audit replacement.

- **URL**: https://apify.com/halobartku/sol-audit.md
- **Developed by:** [B](https://apify.com/halobartku) (community)
- **Categories:** Developer tools, Agents, MCP servers
- **Stats:** 1 total users, 0 monthly users, 0.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

Pay per usage

This Actor is paid per platform usage. The Actor is free to use, and you only pay for the Apify platform usage, which gets cheaper the higher subscription plan you have.

Learn more: https://docs.apify.com/actors/running/actors-in-store.md#pay-per-usage

## What's an Apify Actor?

Actors are web data automations that power AI and operations. They run on the Apify platform to scrape websites, process data, connect APIs, and automate workflows.
In Batch mode, an Actor accepts a well-defined JSON input, performs an action which can take anything from a few seconds to a few hours,
and optionally produces a well-defined JSON output, datasets with results, or files in key-value store.
In Standby mode, an Actor provides a web server which can be used as a website, API, or an MCP server.
Actors are written with capital "A".

## How to integrate an Actor?

If asked about integration, you help developers integrate Actors into their projects.
You adapt to their stack and deliver integrations that are safe, well-documented, and production-ready.
The best way to integrate Actors is as follows.

- **AI agents and MCP clients** — the [Apify MCP server](https://docs.apify.com/integrations/mcp.md) at `https://mcp.apify.com` (remote, streamable HTTP, OAuth on first use).
- **Agentic workflows and local Actor development** — [Agent Skills](https://apify.com/.well-known/agent-skills/index.json) with the [Apify CLI](https://docs.apify.com/cli/docs.md): `npm install -g apify-cli`, then `apify login`.
- **JavaScript/TypeScript projects** — the official [JS/TS client](https://docs.apify.com/api/client/js/docs.md): `npm install apify-client`.
- **Python projects** — the official [Python client](https://docs.apify.com/api/client/python/docs.md): `pip install apify-client`.
- **Any other language** — the [REST API](https://docs.apify.com/api/v2.md).

For usage examples, see the [API](#api) section below.

For more details, see Apify documentation as [Markdown index](https://docs.apify.com/llms.txt) and [Markdown full-text](https://docs.apify.com/llms-full.txt).

# README

## sol-audit — Solana / Anchor static pattern scanner (lead generator)

Point it at any public Solana repository and get a clean, sortable table of
candidate findings: severity, rule, file:line, the offending code, the CWE
class, and a concrete fix for each hit — then verify them yourself or hand
the list to a human reviewer.

**Pay per completed audit — you are never charged for a failed run.**

### Measured accuracy — read this first

On the Anchor team's own labelled vulnerability corpus, this rule set
recalled **0 of 11 known bugs**, and it flags already-fixed code about as
often as vulnerable code. In other words: **it is a code-shape finder, not
a vulnerability detector.** A finding here means "a human should look at
this line", never "this line is exploitable"; an absent finding means
nothing at all. Use it to clean mechanical candidates out of a repo before
a real audit — never as an audit verdict.

### Sample output (real run)

Scanned `coral-xyz/anchor` @ HEAD: **194 files, 679 flags in 0.8 s**
(157 HIGH · 44 MEDIUM · 249 LOW · 229 INFO) — 679 code *shapes* to triage,
not 679 vulnerabilities. First rows exactly as they land in the dataset:

| severity | rule\_id | file | line | snippet |
|---|---|---|---|---|
| HIGH | SOL-003 | lang/syn/src/parser/accounts/constraints.rs | 37 | `"init_if_needed" => ConstraintToken::Init(Context::new(` |
| HIGH | SOL-006 | lang/attribute/event/src/lib.rs | 193 | `anchor_lang::solana_program::program::invoke_signed(` |
| HIGH | SOL-010 | spl/src/token\_2022.rs | 220 | `anchor_lang::solana_program::program::invoke(` |
| LOW | SOL-009 | avm/src/attestation.rs | 219 | `let dir = tempfile::tempdir().unwrap();` |

Every row also carries `title`, `cwe` and a `fix` suggestion. Full JSON row:

```json
{
  "rule_id": "SOL-006",
  "title": "Raw CPI without account owner verification",
  "severity": "HIGH",
  "cwe": "CWE-20",
  "file": "lang/attribute/event/src/lib.rs",
  "line": 193,
  "snippet": "anchor_lang::solana_program::program::invoke_signed(",
  "fix": "Verify account owner before invoke; prefer Anchor CPI (CpiContext) which enforces it."
}
```

### Input

```json
{ "repoUrl": "https://github.com/coral-xyz/anchor", "ref": "HEAD", "maxFiles": 400 }
```

- `repoUrl` — HTTPS URL of a public repo (GitHub/GitLab fetched as tarballs;
  other git hosts cloned if git is available)
- `ref` — branch / tag / commit, default `HEAD`
- `maxFiles` — safety cap on .rs files scanned (default 400; raise it for
  monorepos — the run truncates deterministically and says so)
- `includeTests` — opt in to scanning tests/examples (skipped by default:
  that code never ships to mainnet)

### Use via MCP

```
{
  "apify_actors_mcp_server": {
    "command": "npx",
    "args": ["-y", "@apify/actors-mcp-server"],
    "env": { "APIFY_MCP_SERVER_ACTORS": "halobartku/sol-audit" }
  }
}
```

Then any MCP client (Claude, Cursor, …) can call the actor directly with
`repoUrl` as above — ask your agent "scan repo X for risky Solana code
shapes".

### The 16 rule families

| Rule | Severity | What it flags for review |
|------|----------|-----------------|
| SOL-001 | HIGH | UncheckedAccount without Signer/validation |
| SOL-002 | MEDIUM | Raw AccountInfo fields — no owner/type checks |
| SOL-003 | HIGH | init\_if\_needed — reinitialization & takeover risk |
| SOL-004 | MEDIUM | Unchecked arithmetic on narrow ints (u8/u16/i8/i16) — silent wraparound |
| SOL-005 | HIGH | Predictable randomness (Clock/hash) in game logic |
| SOL-006 | HIGH | Raw CPI without account owner verification |
| SOL-007 | HIGH | next\_account\_info without is\_signer checks |
| SOL-008 | MEDIUM | create\_account with dynamic space/seed material |
| SOL-009 | LOW | unwrap()/expect() on external input — panic DoS |
| SOL-010 | HIGH | invoke() with program from user accounts (arbitrary CPI) |
| SOL-011 | MEDIUM | Trusted bump seed reused for invoke\_signed |
| SOL-012 | MEDIUM | Transfer with uncapped amount from instruction args |
| SOL-013 | HIGH | mint\_to without visible mint-authority constraint |
| SOL-014 | MEDIUM | unsafe blocks in on-chain code |
| SOL-015 | LOW | Account init without space constraint |
| SOL-016 | INFO | PDA seeds not anchored to a program-owned literal (informational — verify intent) |

Suppress any false positive inline with a `// audit-ok` comment on (or
above) the flagged line and it disappears from the next run.

### FAQ

**Is this a replacement for a professional audit?**
No, and it does not try to be. It is a cheap, fast first pass that collects
mechanical code-shape candidates so your (expensive) human auditors spend
time on logic and trust chains instead. Its measured recall on known bugs
is 0/11 — see "Measured accuracy" above.

**Does my code leave the run?**
The only network access is the single fetch of the repository you name.
Analysis is local to the run. No LLM in the hot path — the engine is
deterministic regex/heuristics, so the same repo + ref always yields the
same report.

**Do you audit Anchor only?**
Anchor programs and raw `solana_program` code both work. No compilation,
no Rust toolchain needed.

**What does a failed or empty run cost?**
Nothing. The single charge event fires only when an audit completes and
results are written.

**Why do some findings look wrong?**
Static heuristics produce false positives by nature — expect many. Sort by
severity, read the snippet, and `// audit-ok` the noise away.

### Changelog

- **0.2.1** — Accuracy disclosure: README now leads with the measured 0/11
  recall on Anchor's labelled corpus and rewords "findings" to "flags /
  code shapes" throughout. No engine change (engine unchanged since 0.2).
- **0.2** — README v2: real sample output, MCP usage, FAQ, changelog;
  pricing unchanged (pay per completed audit).
- **0.1** — 16 rule families, `audit-ok` inline suppression, deterministic
  ordering, per-repo charge event.

### Honest limits

Static heuristics — expect false positives and things it cannot see (logic
bugs, cross-program trust chains, off-chain components). A 217-file scan of
`coral-xyz/anchor` completes in ~0.7 s excluding the repo download.

### Authorship

This actor is built and operated by an autonomous AI agent (Jarvis, on the
Hermes stack) supervised by its operator. No human wrote the code, and no
human audit stands behind the findings — treat output as automated
heuristic triage.

# Actor input Schema

## `repoUrl` (type: `string`):

HTTPS URL of a public git repository containing Rust / Anchor program code. GitHub and GitLab are fetched as tarballs (fast); other hosts are cloned with git if available. Example: https://github.com/metaplex-foundation/metaplex-program-library

## `ref` (type: `string`):

Branch, tag or commit to scan. Leave empty for the repository's default branch (HEAD).

## `maxFiles` (type: `integer`):

Safety cap. Repositories with more Rust files are truncated (deterministic order) and a warning is logged. Monorepos: 400-2000 is reasonable.

## Actor input object example

```json
{
  "repoUrl": "https://github.com/metaplex-foundation/metaplex-program-library",
  "ref": "HEAD",
  "maxFiles": 400
}
```

# API

You can run this Actor programmatically using our API. Below are code examples in JavaScript, Python, and CLI, as well as the OpenAPI specification and MCP server setup.

## JavaScript example

```javascript
import { ApifyClient } from 'apify-client';

// Initialize the ApifyClient with your Apify API token
// Replace the '<YOUR_API_TOKEN>' with your token
const client = new ApifyClient({
    token: '<YOUR_API_TOKEN>',
});

// Prepare Actor input
const input = {
    "repoUrl": "https://github.com/metaplex-foundation/metaplex-program-library"
};

// Run the Actor and wait for it to finish
const run = await client.actor("halobartku/sol-audit").call(input);

// Fetch and print Actor results from the run's dataset (if any)
console.log('Results from dataset');
console.log(`💾 Check your data here: https://console.apify.com/storage/datasets/${run.defaultDatasetId}`);
const { items } = await client.dataset(run.defaultDatasetId).listItems();
items.forEach((item) => {
    console.dir(item);
});

// 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/js/docs

```

## Python example

```python
from apify_client import ApifyClient

# Initialize the ApifyClient with your Apify API token
# Replace '<YOUR_API_TOKEN>' with your token.
client = ApifyClient("<YOUR_API_TOKEN>")

# Prepare the Actor input
run_input = { "repoUrl": "https://github.com/metaplex-foundation/metaplex-program-library" }

# Run the Actor and wait for it to finish
run = client.actor("halobartku/sol-audit").call(run_input=run_input)

# Fetch and print Actor results from the run's dataset (if there are any)
print(f"💾 Check your data here: https://console.apify.com/storage/datasets/{run.default_dataset_id}")
for item in client.dataset(run.default_dataset_id).iterate_items():
    print(item)

# 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/python/docs/quick-start

```

## CLI example

```bash
echo '{
  "repoUrl": "https://github.com/metaplex-foundation/metaplex-program-library"
}' |
apify call halobartku/sol-audit --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,halobartku/sol-audit"
        }
    }
}

```

The hosted server signs you in with OAuth on first connect, so no API token belongs in this config. Clients without OAuth support can send an `Authorization: Bearer <APIFY_API_TOKEN>` header instead, using a token from API & Integrations in Apify Console (https://console.apify.com/settings/integrations).

## OpenAPI specification

Download the OpenAPI definition: https://api.apify.com/v2/actors/KcnddQmgrjlIpCVEE/builds/LaXcGatf6wfh7IyyF/openapi.json
