Skip to content

Detector] token math - #861

Open
gloskull wants to merge 32 commits into
Centurylong:mainfrom
gloskull:DETECTOR]-Token-math
Open

Detector] token math#861
gloskull wants to merge 32 commits into
Centurylong:mainfrom
gloskull:DETECTOR]-Token-math

Conversation

@gloskull

Copy link
Copy Markdown

Motivation

  • Prevent subtle, high-impact accounting errors by detecting arithmetic that mixes raw token amounts and scaled/decimal representations (or different token sources) without an explicit normalization step.

Description

  • Add a new rule implementation in tooling/sanctifier-core/src/rules/decimals_scale.rs that heuristically infers token amount sources, detects raw vs scaled naming, and suppresses findings when a documented normalization call is present.
  • Register the new detector in the default rule registry via tooling/sanctifier-core/src/rules/mod.rs and add a finding-code constant DECIMALS_SCALE (SANCT_DECIMALS) in tooling/sanctifier-core/src/finding_codes.rs with metadata.
  • Update the differential testing manifest at tooling/sanctifier-core/tests/fixtures/corpus/differential-corpus.json to map the new rule for corpus-driven tests.
  • Include unit fixtures covering cross-token raw summation, raw vs scaled comparisons, documented normalization (ignored), and same-source same-scale code.

Testing

  • Ran the new-rule unit tests with cargo test -p sanctifier-core decimals_scale --manifest-path Cargo.toml, which passed.
  • Ran the full sanctifier-core test suite with cargo test -p sanctifier-core --manifest-path Cargo.toml, and the package tests completed successfully (all tests passed).

Closes #328

Mercy017 and others added 30 commits June 22, 2026 19:53
A watch mode that re-runs analysis automatically when .rs files change,
tightening the fix-save-see-results loop.

- notify-based recursive watcher over the target path; reacts only to
  create/modify/remove of .rs files
- debounced (~300ms, configurable via --debounce) with a timer that resets
  on each change, so a burst of saves coalesces into one run and a change
  mid-window supersedes the pending run
- clears the screen and re-runs 'analyze' as a child process between runs,
  so analyze's process::exit on findings doesn't kill the watcher
- cooperative Ctrl-C shutdown via a polled flag
- unit tests for the .rs event filter; docs/cli.md regenerated

Usage: sanctifier watch --path contracts/

Closes Centurylong#361
…n rounding-to-zero

Closes Centurylong#333.

Implements a new AST-based detector that flags fee/interest calculations
of the form `let fee = amount * rate / DENOM` where integer division can
produce 0 for small inputs, allowing an attacker to split transactions
into micro-amounts and pay no fees.

Changes:
- `src/rules/fee_rounding.rs`: new `FeeRoundingRule` using syn::visit::Visit;
  inspects each function body for fee-related `let` bindings (names containing
  fee/interest/rate/charge/bps/tax/…) whose init is a mul-then-div expression
  with a large integer denominator (≥ 100) and no subsequent minimum-fee guard
  (`if fee == 0 { fee = 1 }` or `.max(1)` in the binding).
- `src/finding_codes.rs`: adds S017 `FEE_ROUNDING` constant and its
  `FindingCode` entry in `all_finding_codes()`.
- `src/rules/mod.rs`: exports `fee_rounding` module and registers
  `FeeRoundingRule` in `RuleRegistry::with_default_rules()`.
- `tests/fixtures/detectors/fee_rounding.rs`: fixture with two violations
  (`charge_fee`, `accrue_interest`) and two safe patterns (if-guard, `.max(1)`).
- `tests/snapshots/detector_snapshots__fee_rounding.snap`: accepted insta
  golden snapshot for both expected violations.
- `tests/detector_snapshots.rs`: adds `snapshot_fee_rounding` test.

Safe patterns NOT flagged:
  • `let mut fee = …; if fee == 0 && amount > 0 { fee = 1; }` — guard present
  • `let fee = (amount * bps / 10_000).max(1)` — outer expression is `.max()`,
    not a bare division, so `is_mul_div_pattern` returns false
…nding-detector-333

feat(detector): add fee_rounding rule (S017) — catch integer division rounding to zero in fee calculations
…ctor-321

feat: detect unsafe integer casts
- Add report storage system with ID generation and expiry (30 days)
- Create /report/[id] dynamic route for viewing shared reports
- Update analyze API to save reports and return shareable ID
- Add copy link button in scan results page
- Display report metadata (timestamp, expiry) on shared pages
- Include source snippet preview in shared reports
- Implement automatic cleanup of expired reports
- Add storage directory to .gitignore

Closes Centurylong#375
…-report-permalink-375

feat: Shareable report permalink feature
…enturylong#389)

- Document all implemented security features (slippage protection, MEV protection, k-invariant)
- Detail comprehensive test suite (18 tests: 4 unit + 14 property tests)
- Document 7 Kani formal verification harnesses
- Mark Issue Centurylong#389 acceptance criteria as completed
- Provide usage examples for hardened contract interface
- Update architecture documentation

All hardening requirements from Issue Centurylong#389 have been completed:
✅ Swaps enforce min-out + deadline
✅ k-invariant documented and tested
✅ Property/Kani tests for core invariants

Status: AMM pool template is now production-ready with comprehensive security.
…ssue-389-amm-hardening-complete

docs: Update AMM pool README to document completed hardening
Adds a GitHub Actions job that runs `cargo kani` on every proof-harness crate
with a per-harness time budget and reports a pass/fail summary — a
"we verify in CI" guard against silent regressions.

- Runs Kani on the harness crates: kani-poc-contract, token-invariants,
  amm-pool, reentrancy-guard (each with `--harness-timeout 120s`, plus an
  outer wall-clock cap per package).
- Caches the Kani toolchain (~/.kani), the verifier binary, the cargo
  registry, and build artifacts to keep reruns fast.
- Writes a per-package pass/fail table to the GitHub step summary.
- Non-blocking initially (per the issue): install + proof steps are
  `continue-on-error` and the runner exits 0, so the suite reports results
  without blocking merges while it stabilizes. `timeout-minutes: 30` is a hard
  ceiling against a runaway solver.

Workflow-only change; no Rust/source changes, so the existing test/build CI is
unaffected.
…i-proofs

[FV] CI job: run Kani proofs on PRs with a time budget (Centurylong#348)
feat(cli): add 'sanctifier watch' for continuous re-analysis on file change (closes Centurylong#361)
…g#352)

Closes Centurylong#352

Adds a new `tooling/zk` crate (`sanctifier-zk`) that implements a
proof-of-concept zero-knowledge circuit proving that an audited WASM
contract hash achieved at least a target score under a given ruleset,
without revealing the per-rule finding set.

## Circuit design (Groth16 over BLS12-381)

Public inputs (in allocation order — must match the on-chain verifier):
  1. wasm_hash        — SHA-256(wasm_bytes) mod |Fr|
  2. ruleset_version  — u32 identifying the applied rule set (v1 = 11 rules)
  3. score_threshold  — minimum passing score in [0, N_RULES]
  4. rules_commitment — Poseidon(r₀ … r_{N−1}), binding the proof to
                         specific per-rule results without revealing them

Constraints (R1CS):
  • Boolean:     r_i * (1 − r_i) = 0  for each of the 11 rules
  • Score:       score = Σ weight_i * r_i  (equal weights = 1 in v1)
  • Threshold:   bit-decomposition of (score − threshold) fits in 8 bits,
                 proving score ≥ threshold without revealing the exact score
  • Commitment:  Poseidon(r₀ … r_{N−1}) == rules_commitment

## Files

  tooling/zk/
  ├── src/
  │   ├── lib.rs        — public API: setup / prove / verify / helpers
  │   ├── circuit.rs    — ConstraintSynthesizer implementation
  │   ├── encoding.rs   — public-input encoding with full spec doc
  │   ├── params.rs     — deterministic Poseidon parameters (PoC seed)
  │   └── bin/prover.rs — CLI: hash WASM → generate proof → verify → JSON
  └── benches/
      └── zk_bench.rs   — criterion benchmarks (setup / prove / verify /
                           proof size / constraint count)

## Proof size

Groth16/BLS12-381 produces a constant-size compressed proof of 128 bytes
regardless of circuit size.

## Security note

The Poseidon MDS matrix is generated from a fixed PRNG seed for this PoC.
Production use requires a proper MPC-based parameter ceremony (Grain LFSR
or similar) to guarantee the MDS property and eliminate a trusted party.
…-audit-circuit

feat(zk): add Groth16/BLS12-381 audit-proof circuit (issue Centurylong#352)
…-327

Add SANCT_ARG_DOS detector for unbounded argument iteration
docs: add custom detector cookbook and update contributing guide
…ong#151)

- Expand vulnerability-db.json from 12 to 32 entries with new fields:
  cvss (f64), affected_versions, poc_exploit, patch, tags, related_cves
- Add 32 YAML source files in data/vulnerabilities/ (CVE-style format)
- Update VulnEntry struct in vulndb.rs to support all new fields
- Add VulnDatabase methods: search(), by_category(), by_severity(),
  get_by_id(), to_json(), to_rss() for RSS 2.0 feed generation
- Add `sanctifier cve` subcommand (commands/cve.rs) with:
    cve search --keyword <kw>
    cve list [--category <cat>] [--severity <sev>]
    cve show <id>
    cve export --format <json|rss> [--output <file>]
    cve serve [--port <port>]  (GET /api/vulndb, /api/vulndb/<id>, /api/vulndb/feed.rss)
- Create sep41-token-invariants contract with pure functions encoding
  all core SEP-41 token arithmetic semantics
- 12 formal invariants covering: transfer conservation, transfer-from
  allowance enforcement, approve consistency, mint/burn correctness,
  rejection of invalid amounts and insufficient balances/allowances
- 14 Kani proof harnesses verifying all invariants symbolically
- Soroban contract implementation (Sep41Token) with #[invariant]
  attributes for sanctifier verify / cargo kani integration
- Documentation at docs/sep41-formal-spec-template.md for reuse
- 16 passing tests validating pure function correctness
- Fix signed integer underflow: explicit balance checks for i128

Closes Centurylong#350
…ormal-spec-templates

[FV] Formal spec templates for SEP-41 token compliance
Adds CLI reference entries for the new `cve` subcommand and its
subcommands (search, list, show, export, serve) introduced in this PR.
Fixes the CI staleness check.
…vulndb

feat: public Soroban/Stellar CVE vulnerability database
Add SANCT_DECIMALS detector for mixed raw/scaled token arithmetic
@github-actions github-actions Bot added dependencies Pull requests that update a dependency file rust Pull requests that update rust code javascript Pull requests that update javascript code area: ci/cd CI/CD pipeline and GitHub Actions area: contracts Soroban smart contracts area: core-engine sanctifier-core static analysis engine area: cli sanctifier-cli command line tool area: frontend Next.js frontend application area: docs Documentation and guides area: testing Tests, benchmarks, fuzzing size/xl labels Jul 26, 2026
@github-actions github-actions Bot added size/l and removed area: ci/cd CI/CD pipeline and GitHub Actions area: contracts Soroban smart contracts size/xl labels Jul 26, 2026
@Gbangbolaoluwagbemiga

Copy link
Copy Markdown
Contributor

@gloskull, ensure all ci passes and please stop spamming issues

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area: cli sanctifier-cli command line tool area: core-engine sanctifier-core static analysis engine area: docs Documentation and guides area: frontend Next.js frontend application area: testing Tests, benchmarks, fuzzing dependencies Pull requests that update a dependency file javascript Pull requests that update javascript code rust Pull requests that update rust code size/l

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[DETECTOR] Token math without decimals/scale validation

10 participants