Thanks for helping grow the library. This guide covers everything you need to add a new vulnerable contract example, run the test suite, and get your PR merged.
| Repo | Purpose |
|---|---|
| soroban-guard-core | CLI scanner that analyses contracts against this library |
| soroban-guard-web | Web dashboard for browsing scan results from the on-chain registry |
- Rust toolchain (stable) — install via rustup
wasm32-unknown-unknowntarget- Stellar CLI (for deploying to testnet, optional for local testing)
# Install Rust
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
# Add the WASM target
rustup target add wasm32-unknown-unknown
# Install Stellar CLI (optional — needed for testnet deployment)
cargo install --locked stellar-cli --features optgit clone https://github.com/Veritas-Vaults-Network/soroban-guard-contracts
cd soroban-guard-contracts
cargo buildcargo testcargo test -p missing-auth
cargo test -p registrycargo build --release --target wasm32-unknown-unknown
# Output: target/wasm32-unknown-unknown/release/<name>.wasm- Create the crate
mkdir -p vulnerable/<your_name>/src- Add
Cargo.toml
[package]
name = "your-name"
version = "0.1.0"
edition = "2021"
[lib]
crate-type = ["cdylib"]
[dependencies]
soroban-sdk = { workspace = true }-
Register it in the workspace — add
"vulnerable/<your_name>"to themembersarray in the rootCargo.toml. -
Write the contract in
src/lib.rs. See the checklist below. -
Add a
#[cfg(test)]module with at least 3 tests — one of which must demonstrate the vulnerability succeeding (i.e. the bad thing happens without a panic). -
Document the vulnerability — add a new section to
docs/vulnerabilities.mdfollowing the existing format. -
Verify it compiles and tests pass
cargo test -p your-nameA good example contract must satisfy all of the following:
The contract should model something a real developer might write — a token, vault, staking pool, escrow, DAO, NFT marketplace, etc. Toy contracts with no business logic are harder to learn from.
The contract must compile against the current workspace soroban-sdk version
with zero errors and zero todo!() macros. Run cargo build before opening
a PR.
The vulnerability must be obvious enough that a scanner (human or automated) can identify it from the source. Mark every flaw with a comment:
// VULNERABILITY: <explain what's wrong and why it matters>
// ❌ Missing: <show what the fix would look like>Each contract should demonstrate a single class of vulnerability. Combining multiple issues in one file makes it harder to use as a targeted test case.
Every vulnerable contract should have a corresponding secure version in
secure/ with // ✅ FIX: comments explaining each change.
- One test that shows normal operation works.
- One test that demonstrates the vulnerability (the bad thing succeeds).
- One test that verifies a boundary condition or edge case.
#![no_std]on all contracts.- Use
#[contracttype]for all storage keys and custom structs. - No
unwrap()in production paths — use.expect("descriptive message")or explicit error handling. - Keep functions short and single-purpose.
- Run
cargo fmtbefore committing.
This repo targets a minimum of 25 meaningful commits. Each commit should be scoped to a single logical change:
feat(missing_auth): add vulnerable token contract
feat(missing_auth): add test suite demonstrating auth bypass
fix(secure_vault): add balance underflow guard
docs: add missing_auth entry to vulnerabilities.md
- Fork the repo and create a branch:
feat/vuln-<name>orfix/<name>. - Ensure
cargo testpasses with zero failures. - Ensure
cargo fmt --checkpasses. - Fill in the PR template — link to the relevant
docs/vulnerabilities.mdsection and describe the real-world scenario the contract models.
This section walks you through adding a new vulnerable/<name> + secure/<name> pair from scratch.
- Use
snake_casefor all directory and crate names (e.g.missing_auth,dust_griefing). - Names should be descriptive of the vulnerability class, not the contract type.
- The vulnerable crate is named
<name>and lives invulnerable/<name>. - The secure mirror is either a new crate in
secure/<name>or an inlinesecuremodule atvulnerable/<name>/src/secure.rs— use whichever keeps the diff smallest.
1. Create the vulnerable crate
vulnerable/
<name>/
Cargo.toml
src/
lib.rs
Cargo.toml minimum:
[package]
name = "<name>"
version = "0.1.0"
edition = "2021"
[lib]
crate-type = ["cdylib"]
[dependencies]
soroban-sdk = { workspace = true }2. Create the secure mirror
Option A — separate crate (preferred when the secure version is substantially different):
secure/
<name>/
Cargo.toml
src/
lib.rs
Option B — inline module (preferred for small fixes):
// vulnerable/<name>/src/lib.rs
pub mod secure; // points to vulnerable/<name>/src/secure.rs3. Register both crates in the workspace
Add to the members array in the root Cargo.toml:
"vulnerable/<name>",
"secure/<name>", # omit if using inline secure module4. Write the vulnerable contract
- Add a module-level
//!doc block explaining the vulnerability class, the missing guard, and the severity. - Add
/// rustdocto everypub fncovering: what it does, what is missing, and the impact. - Mark every flaw with an inline comment:
// ❌ Missing: <what the fix looks like>5. Write the secure mirror
- Mirror every vulnerable function with the fix applied.
- Mark each fix with:
// ✅ FIX: <explain the change>6. Write tests — minimum 3 per contract
| Test | Purpose |
|---|---|
test_normal_<action>_works |
Happy path — normal operation succeeds |
test_<vulnerability>_<effect> |
Demonstrates the vulnerability (bad thing happens) |
test_secure_rejects_<attack> |
Secure mirror blocks the same attack |
7. Add a docs/vulnerabilities.md entry
Follow the existing format:
## N. <Title> (`<name>`)
**Contract:** `vulnerable/<name>` → `secure/<name>`
**Severity:** Critical / High / Medium / Low
### What it is
...
### Vulnerable pattern
```rust
// ❌ ...// ✅ ......
**8. Verify everything**
```bash
cargo build -p <name>
cargo test -p <name>
cargo fmt --check
cargo doc --workspace --no-deps # must produce zero warnings
9. Open a PR
- Branch name:
feat/vuln-<name> - PR title:
feat(<name>): add vulnerable/secure pair for <vulnerability class> - Link to the
docs/vulnerabilities.mdsection you added. - Reference the issue number if one exists.