Tracking issue: #503 — feeds the [HACKATHON] Competitive analysis (#166).
This document records a differential study of Sanctifier against the two most widely used open-source smart-contract static analyzers, Slither and Aderyn, on the checks that overlap. It quantifies where Sanctifier already matches established tooling, where it has gaps worth closing, and where its target platform makes a check unique or unnecessary.
On the ten canonical bug classes in the shared corpus:
| Analyzer | Classes flagged by default | Notes |
|---|---|---|
| Sanctifier | 3 / 10 | reinit (S001), upgrade_auth (S001), integer_overflow (S003) |
| Slither | 5 / 10 | reentrancy, unbounded loop, weak PRNG, confused deputy, unprotected upgrade |
| Aderyn | 6 / 10 | the Slither set + unprotected initializer (reinit) |
Sanctifier has four detectable gaps — classes an EVM tool already catches but Sanctifier does not: reentrancy, unbounded loop, weak randomness, and confused deputy. It is also the only analyzer that covers the Soroban-specific missing-TTL class, and it is intentionally stricter on integer overflow than the EVM tools (see Divergences).
Sanctifier analyzes Stellar Soroban contracts (Rust → Wasm); Slither and Aderyn analyze Solidity/EVM. There is no common source language, so a line-for-line cross-run is impossible. The issue scopes this correctly — "where checks overlap" — so the comparison is made at the vulnerability-class level: for each bug class, does each tool ship a default detector that catches it?
To keep the EVM side reproducible (not just a literature claim), the corpus also ships minimal Solidity mirrors of the classes that have a direct EVM analogue, so Slither and Aderyn can be run live by anyone who has them installed.
tooling/sanctifier-core/tests/fixtures/
├── gallery/ # Soroban side (reused from issue #388)
│ ├── <bug>_vulnerable.rs
│ └── <bug>_fixed.rs
└── corpus/
├── differential-corpus.json # manifest: class → fixtures → codes → detectors → overlap
└── solidity/ # EVM mirrors for the overlapping classes
├── <bug>_vulnerable.sol
└── <bug>_fixed.sol
The manifest (differential-corpus.json) is the single source of truth. For
each class it records the Soroban fixtures, the finding codes Sanctifier emits
today (sanctifier.observed_codes), the closest default Slither/Aderyn
detectors, and an overlap classification.
# Sanctifier side only (runs in CI, prints the matrix):
cargo test -p sanctifier-core --test differential_test -- --nocapture
# …without a local Z3 install, add --no-default-features
# Full harness (also runs Slither/Aderyn over the .sol mirrors when installed):
./scripts/differential-test.shThe Rust harness (tests/differential_test.rs) runs the default RuleRegistry
over every corpus fixture and asserts that the recorded ground truth still
holds — so the corpus can never silently drift from real detector behaviour.
It also fails if a *_fixed fixture produces any finding (false-positive guard).
Legend: ✅ flagged by default ·
| # | Bug class | Sanctifier | Slither detector(s) | Aderyn detector(s) | Overlap |
|---|---|---|---|---|---|
| 1 | Re-initialization | ✅ S001 |
— | unprotected-initializer |
shared-covered |
| 2 | Unchecked upgrade auth | S001 (S010 planned) |
unprotected-upgrade |
centralization-risk |
shared-covered |
| 3 | CEI / reentrancy | 🔜 S006 |
reentrancy-eth, reentrancy-no-eth |
state-change-after-external-call |
sanctifier-gap |
| 4 | Unbounded loop / DoS | 🔜 S006 |
calls-loop, costly-loop |
costly-operations-inside-loops |
sanctifier-gap |
| 5 | Missing TTL bump | 🔜 S006 |
— | — | soroban-specific |
| 6 | Weak randomness | 🔜 S006 |
weak-prng |
weak-randomness |
sanctifier-gap |
| 7 | Integer overflow | ✅ S003 |
— | — | divergent-approach |
| 8 | Allowance race (TOCTOU) | 🔜 S006 |
— | — | mutual-gap |
| 9 | Oracle staleness | 🔜 S006 |
— | — | mutual-gap |
| 10 | Confused-deputy auth | 🔜 S001 family |
tx-origin, arbitrary-send-eth |
arbitrary-from-in-transfer-from |
sanctifier-gap |
Integer overflow (#7) — Sanctifier is stricter on purpose. Sanctifier flags
unchecked +/-/* on Rust integers because the Soroban toolchain wraps on
overflow in release builds. On Solidity ≥ 0.8 the compiler inserts overflow
checks, so Slither and Aderyn correctly do not flag plain arithmetic — the
risk only returns inside an explicit unchecked { } block, which their default
rulesets also leave alone. Same class, opposite default, and both are right for
their platform. This is the clearest example of why platform context matters.
Re-init / upgrade auth (#1, #2) — same outcome, different code. Sanctifier
catches both via the presence-based auth_gap (S001) detector: the underlying
mistake is a state-mutating admin entrypoint with no require_auth. Aderyn has
a dedicated unprotected-initializer; Slither has unprotected-upgrade.
Functionally aligned; Sanctifier should still split out a dedicated S010
upgrade detector so the report names the right class (currently surfaced as
S001).
Confused deputy (#10) — a false negative we can see. The vulnerable fixture
does call require_auth, just on the wrong (attacker-influenceable) party, so
the presence-only auth_gap check stays silent and Sanctifier reports nothing.
Aderyn’s arbitrary-from-in-transfer-from and Slither’s tx-origin catch the
EVM analogue. This is a genuine coverage gap, not a platform difference.
Mutual gaps (#8, #9). The ERC-20 approve race and oracle-staleness classes are real on both platforms but are not in any of the three default rulesets (EVM tooling usually handles them with project-specific semgrep/custom rules). Worth flagging as an industry-wide gap, not a Sanctifier-specific one.
Recommended issues to file from this study (priority order):
[DETECTOR] Reentrancy / CEI (S006)— highest-value gap; both EVM tools ship it. Detect external/host interaction before a storage effect.[DETECTOR] Weak randomness (S006)— flag randomness derived from ledger sequence/timestamp; mirrors Slitherweak-prng/ Aderynweak-randomness.[DETECTOR] Unbounded loop over caller-controlled data (S006)— mirrors Slithercalls-loop; pairs naturally with the existing gas estimator.[REFINE] Confused-deputy / arbitrary-from for auth_gap (S001)— moveauth_gapfrom "isrequire_authpresent" to "is the right principal authorized", closing the #10 false negative.[REPORT] Dedicated S010 upgrade-risk finding— stop surfacing upgrade auth as S001 so reports name the class correctly.- (stretch)
[DETECTOR] Missing-TTL (S006)— Soroban-specific; no EVM tool can help here, so it is uniquely Sanctifier’s to own.
- Class-level, not line-level. Because the source languages differ, equality means "both ship a default detector for this class", not identical findings.
- Approximate cross-language fixtures. The Solidity mirrors reproduce the
pattern of each bug, not the exact Soroban code. They are written for
solc ^0.8.20; the live Slither/Aderyn rows are reproduced byscripts/differential-test.shwhen those tools are installed. Where they are not, the Slither/Aderyn columns are sourced from each tool's published detector catalog and clearly marked as documentation in the harness output. - Default rulesets only. Custom/optional detectors and plugins are out of scope; the comparison is "out of the box" coverage.
- Ground truth is enforced.
sanctifier.observed_codesis asserted against live detector output bytests/differential_test.rs, and is kept in lock-step with the committed gallery snapshots (tests/snapshots/gallery_snapshots__*).