Provenance + vulnerability scanning for Soroban/WASM artifacts — prove deployed bytecode matches source, and scan contract source for security patterns before you ship.
sorseal seals the build: it records SHA-256 digests for your contract artifacts (WASM + source tree + toolchain + git commit), then verifies at any time that a clean rebuild reproduces those exact digests. On top of that provenance base it statically scans contract source for known-fragile Soroban patterns (sorseal analyze), seals the finding digests into the audit trail, and can monitor files for unauthorized changes, alerting when drift is detected.
cargo install sorseal --lockedOr build from source:
git clone https://github.com/BreachDirect/sorseal.git
cd sorseal
cargo build --releaseRequirements: Rust 1.85+ and wasm32-unknown-unknown target (rustup target add wasm32-unknown-unknown).
# scaffold a manifest
sorseal init
# seal (record digests)
sorseal record
# verify (rebuild + compare)
sorseal verify# generate a starter config
sorseal watch --init
# run a single check (for cron)
sorseal watch --once
# run as a daemon
sorseal watch| Command | Description |
|---|---|
sorseal init |
Scaffold a sorseal.toml manifest for your project |
sorseal record |
Build artifacts and write sorseal.provenance.json |
sorseal verify |
Rebuild and verify artifacts match the sealed provenance |
sorseal report |
Render provenance as console, JSON, or Markdown |
sorseal keygen |
Generate an Ed25519 keypair for signing attestations |
sorseal sign |
Sign the provenance as an in-toto/SLSA v1.0 attestation |
sorseal verify-attestation |
Verify a signed attestation against a public key |
sorseal onchain-verify |
Compare on-chain WASM hash against sealed provenance |
sorseal onchain-audit |
Audit a contract's full on-chain upgrade history |
sorseal simulate-onchain |
Fund-free, offline on-chain verify + audit demo |
sorseal analyze |
Statically scan contract source for Soroban vulnerability patterns |
sorseal watch |
Monitor files for integrity drift and alert on changes |
Seal your Soroban contract builds with cryptographic proof that the deployed bytecode matches your source code.
$ sorseal record
Sorseal — my-contract
sealed my-contract :: wasm sha256 a1b2c3d4e5f6... (24576 bytes)
sealed my-contract :: source sha256 f6e5d4c3b2a1...
toolchain rustc 1.85.0 · git commit 8a3f2b1c (clean)
provenance written to sorseal.provenance.json$ sorseal verify
Sorseal — my-contract verify
PASSED my-contract :: wasm — sha256 matches sealed digest
PASSED my-contract :: source — sha256 matches sealed digest
PASSED my-contract :: command — build_command unchanged
3 checks: 3 passed, 0 failed, 0 erroredsorseal verify --sarif results.sarifUpload to GitHub Code Scanning with the sorseal GitHub Action:
- uses: BreachDirect/sorseal@main
with:
sarif-file: sorseal.sarifsorseal analyze statically scans a contract's Rust source for known-fragile
Soroban patterns — the same ones that let value-holding contracts get drained —
and reports them with a severity and remediation, so findings can be triaged
and fed straight into CI or an audit report.
$ sorseal analyze
Sorseal — vulnerable-contract analyze :: vulnerable-contract
Critical SORSEAL-101 src/lib.rs:23 — function mutates contract state or moves value without `require_auth`; an unauthenticated caller may drive changes
Medium SORSEAL-103 src/lib.rs:25 — unchecked arithmetic on a likely value quantity; consider `checked_add/sub/mul` to guard against overflow
Critical SORSEAL-101 src/lib.rs:32 — function mutates contract state or moves value without `require_auth`; an unauthenticated caller may drive changes
High SORSEAL-102 src/lib.rs:32 — possible reentrancy: the function mutates state and makes an external/invoke call without `require_auth`
Medium SORSEAL-103 src/lib.rs:32 — unchecked arithmetic on a likely value quantity; consider `checked_add/sub/mul` to guard against overflow
Medium SORSEAL-103 src/lib.rs:35 — unchecked arithmetic on a likely value quantity; consider `checked_add/sub/mul` to guard against overflow
Medium SORSEAL-106 src/lib.rs:37 — external `invoke_contract`/`call_contract` call detected without an adjacent `non_reentrant` guard
Critical SORSEAL-101 src/lib.rs:42 — function mutates contract state or moves value without `require_auth`; an unauthenticated caller may drive changes
High SORSEAL-105 src/lib.rs:42 — token `.transfer`/`.transfer_from` call with no prior balance/allowance read; the transferred amount is not derived from what this contract actually holds
Critical SORSEAL-101 src/lib.rs:49 — function mutates contract state or moves value without `require_auth`; an unauthenticated caller may drive changes
Medium SORSEAL-103 src/lib.rs:49 — unchecked arithmetic on a likely value quantity; consider `checked_add/sub/mul` to guard against overflow
Medium SORSEAL-103 src/lib.rs:55 — unchecked arithmetic on a likely value quantity; consider `checked_add/sub/mul` to guard against overflow
Low SORSEAL-104 src/lib.rs:59 — `panic!` or `unwrap()` on a code path that may be reachable from callers; prefer returning a Result and reverting with a clear error
13 findings — Critical: 4 · High: 2 · Medium: 6 · Low: 1
analysis digest sha256 b911e3584977Detection rules (each with a stable id, severity, and remediation):
| Rule | Severity | Detects |
|---|---|---|
SORSEAL-101 missing-authorization |
Critical | State/value mutation without require_auth |
SORSEAL-102 reentrancy |
High | External call after state mutation, no guard |
SORSEAL-103 unchecked-arithmetic |
Medium | Raw +/-/* on amounts instead of checked_* |
SORSEAL-104 panic-on-input |
Low | panic!/unwrap() on a caller-reachable value path |
SORSEAL-105 unchecked-transfer |
High | .transfer/.transfer_from amount not derived from a prior balance/allowance read |
SORSEAL-106 missing-reentrancy-guard |
Medium | invoke_contract/call_contract without non_reentrant |
Output as JSON, Markdown (audit deliverable), or SARIF (code scanning):
sorseal analyze --format json # machine-readable findings + digest
sorseal analyze --format markdown > audit.md
sorseal analyze --sarif findings.sarifRun sorseal analyze as a CI gate with a severity threshold — the process
exits non-zero when findings at or above the threshold exist:
sorseal analyze --fail-on High # gate the build on no High/Critical findingsNeither --fail-on nor any other flag needs to be set for ordinary use:
without a gate, analyze is report-only and exits 0 even with findings.
Explain a rule without scanning, and build suppressions for reviewed/acceptable findings:
sorseal analyze --explain SORSEAL-105 # guidance + fix for one rule
sorseal analyze --explain # list every rule// sorseal:ignore SORSEAL-104 reviewed: this input is validated by the caller
let rate: i128 = env.storage().instance().get(&KEY).unwrap();Findings tagged with // sorseal:ignore <RULE> on the line above are dropped
(counts, SARIF, and the sealed digest all exclude them); add --no-ignore to
temporarily see everything. Because suppressions change the finding set, a
suppressed run seals a different digest than an unsuppressed one.
And because the audit itself must be tamper-evident, the finding digest can be sealed into the provenance record alongside the wasm + source digests:
sorseal analyze --seal--seal appends analysis entries (finding count, worst severity, SHA-256 of
the stable finding serialization, and timestamp) to sorseal.provenance.json,
so sorseal verify proves the audited source was not altered since the
analysis was produced.
Try it on the bundled intentionally-vulnerable example contract:
cd examples/vulnerable-contract
sorseal analyzesorseal keygen
sorseal sign --key sorseal.key
sorseal verify-attestation --public-key sorseal.pubProduces Ed25519-signed in-toto Statements (SLSA v1.0 predicate, DSSE envelope) so releases can be authenticated by public key alone.
Check that a deployed contract's on-chain WASM hash matches your sealed provenance:
sorseal onchain-verify --contract-id CABC123...Audit the full upgrade lineage of a contract:
sorseal onchain-audit --contract-id CABC123...A live deploy/upgrade demo needs a funded account to pay transaction fees.
sorseal simulate-onchain drives the same on-chain code paths (the
getLedgerEntries XDR decode, getEvents paging, upgrade-lineage
reconstruction, and provenance cross-checking) against an in-memory ledger
derived from your provenance — so the whole seal → deploy → upgrade → verify →
audit story is reproducible on any machine with zero funds and no network:
./scripts/sim-demo.shThe first case is a fully-sealed contract (verify PASSED, audit PASSED,
every version in the lineage attested). The second injects an unsealed current
deployment and shows the tool catching the drift (verify FAILED, audit
FAILED with current ... NONE). Run just one case yourself:
# clean: current deployment is sealed
sorseal simulate-onchain
# drift: simulate an unsealed current deployment
sorseal simulate-onchain --deploy-wasm 9999999999999999999999999999999999999999999999999999999999999999Monitor critical files for unauthorized changes:
$ sorseal watch --once
Sorseal — file integrity watch
PASSED /etc/nginx/nginx.conf — unchanged
FAILED /usr/bin/sshd — sha256 mismatch: baseline abc123, current def456
PASSED /etc/passwd — unchanged
3 files checked: 2 passed, 1 failedConfigure with sorseal.watch.toml:
[watch]
interval_secs = 300
[[watch.paths]]
path = "/etc/nginx/nginx.conf"
label = "nginx-config"
[[watch.paths]]
path = "/usr/bin/sshd"
label = "sshd-binary"
[[watch.webhooks]]
type = "discord"
url = "https://discord.com/api/webhooks/..."
[[watch.webhooks]]
type = "telegram"
token = "your-bot-token"
chat_id = "123456"This repository is part of a broader set of contributions to the Stellar / Soroban ecosystem. See WAVE_EVIDENCE_ToryMic.md for a curated list of related projects, demos, and tooling maintained by ToryMic, including testing, indexers, contract frameworks, and privacy/payment prototypes.
- Evidence file: WAVE_EVIDENCE_ToryMic.md
- Maintainers: MAINTAINERS.md
See CONTRIBUTING.md for the full contributor guide (setup, code style, PR process, testing requirements).
This repository is a Stellar Drips Wave 9 project. The quickest ways to contribute right now, in rough order of leverage for the repo and for Wave 9 contribution credit:
- Pick up a
good-first-issue— issues labelledgood-first-issueare scoped, reviewable, and small enough to land in a weekend. Say you're taking one in the thread, then reference the issue + the Wave in your PR so it can be attributed and rewarded. - Run
sorseal analyzeon your own Soroban contract and file a bug/feature report with real output. Every genuine finding (or rule gap) you surface improves the analyzer and earns an issue + PR of its own. - Add a detection rule — the rule table below lists what
analyzecovers today. New Soroban issue classes (unchecked balance, missing auth branches, oracle/price reads, storage-key collisions) make great intermediate issues. - Document — rule-explanation pages, worked examples, tips for integrating
analyze --fail-oninto GitHub Actions, and real testnet walkthroughs are high-value, low-risk contributions. - Post captured evidence — run
scripts/sim-demo.shandsorseal analyzeagainst theexamples/vulnerable-contract, then attach the console + SARIF- proven JSON output to PRs and to
WAVE_EVIDENCE_ToryMic.md. PRs that show the tool actually catching a bug or proving a deployment are the strongest Wave evidence. (No funded testnet account is required — see thesimulate-onchaindemo above.)
- proven JSON output to PRs and to
Tip for contributors: commits and PRs with real captured CLI output (console + SARIF + proven JSON) are far stronger Wave evidence than code-only changes — show the tool actually finding (or verifying) things.
Current shipped & in-flight capabilities:
| Area | Command | Status |
|---|---|---|
| Build provenance | record / verify |
shipped |
| Reporting | report (console/JSON/Markdown) |
shipped |
| CI / SARIF | verify --sarif + GitHub Action |
shipped |
| Signed attestations | keygen / sign / verify-attestation |
shipped |
| On-chain verification | onchain-verify |
shipped |
| Upgrade-lineage audit | onchain-audit |
shipped |
| On-chain demo (no funds) | simulate-onchain |
shipped |
| File integrity watch | watch |
shipped |
| Vulnerability scan | analyze (6 rules, --seal) |
shipped |
| Vulnerability CI gate | analyze --fail-on <severity> |
shipped |
| JSON findings output | analyze --format json |
shipped |
| Self-documenting rules | analyze --explain [RULE] |
shipped |
| Finding suppressions | analyze inline // sorseal:ignore |
shipped |
| Golden-file digest tests | tests/analyze_golden.rs |
shipped |
MIT