Security Audit V3 — 71 findings (10 Critical, 20 High, 23 Medium, 18 Low) #38

Open
opened 2026-03-02 14:05:17 +00:00 by mik-tf · 0 comments
Owner

Security Audit V3 — 71 Findings

Full audit document: SECURITY_AUDIT_V3.md (committed on development)
Audit date: 2026-03-02 | Scope: development HEAD 267be5f
Audited areas: 14 smart contracts, gateway server + client, SDK, CLI, Rhai engine, provision/cluster infrastructure, Docker, CI


Summary

Severity Count
CRITICAL 10
HIGH 20
MEDIUM 23
LOW 18
Total 71

Critical (fix before any production use)

ID Area Issue
C-01 Rhai run_command / run_command_output / env_var registered in Rhai engine — full RCE via any .rhai script
C-02 SDK Account derives Debug — private key leaks in any {:?} format, panic, or debug log
C-03 CLI heroledger info prints 40 chars of private key to stdout
C-04 CLI --secret-key / --mnemonic visible in /proc/<pid>/cmdline
C-05 Infra Validator key files written without chmod 0600 — world-readable
C-06 Infra HEROLEDGER_VAULT_SECRET + SENDGRID_API_KEY written as plaintext env vars into zinit service config
C-07 Contract Credit line_index Vectors all share StorageKey::LineIndex — data corruption across all users
C-08 Contract Credit approval_index Vectors all share StorageKey::ApprovalIndex — cross-user approval corruption
C-09 Gateway CORS policy is triple-wildcard (Any/Any/Any) — same vulnerability as the deleted relayer, never ported to gateway
C-10 Gateway gateway.submitTransaction accepts arbitrary user-crafted NEAR transactions — whitelist model bypassed entirely

High (fix before external user onboarding)

ID Area Issue
H-01 Infra All 7 git deps pinned to branch = "development" — supply chain injection risk
H-02 SDK f64 precision loss converting NEAR amounts: (deposit_near * 1e24) as u128
H-03 Rhai parse_near returns i64 — overflows and goes negative for amounts > ~9 NEAR
H-04 SDK ContractBuilder::deploy returns "deployed:<account>" instead of real tx hash
H-05 Contract Sporex/Groups use raw b"x" byte literal storage key prefixes — collision risk
H-06 Contract Groups uses std::HashSet in NEAR storage — gas DoS for accounts with many group memberships
H-07 Contract Credit ft_on_transfer returns String not PromiseOrValue<U128> — NEP-141 violation, funds never land
H-08 Contract Hosting/Sporex ft_on_transfer returns bare U128 not PromiseOrValue<U128>
H-09 Contract DNS claim_refund / withdraw_fees have no rollback callback — funds lost on failed token transfer
H-10 Contract Hosting sla_settle_period callable by anyone with fabricated data — SLA forgery
H-11 Contract Missing assert_one_yocto() on withdraw, spend, claim_refund, sla_stake_withdraw
H-12 Contract Faucet fund_account has no access control — faucet drainable by anyone
H-13 Infra NEAR RPC bound to 0.0.0.0 in dev mode — bypasses gateway auth entirely
H-14 Infra Caddy installer uses predictable temp dir — binary substitution attack possible
H-15 CI CI builder image uses mutable tag :2 — supply chain via image repointing
H-16 Docker Base images use floating tags (:latest, ubuntu:24.04) — not pinned to digest
H-17 Gateway gateway.submitTransaction bypasses rate limiter — DoS amplification
H-18 Gateway Rate limiter bypassed by rotating public keys; no per-IP limiting
H-19 Gateway Stored XSS via unescaped domain in HTML email confirmation page
H-20 Gateway SSRF via Grid Proxy responses; reqwest::Client has no timeouts

Medium and Low

See SECURITY_AUDIT_V3.md for the full list of 23 Medium and 18 Low findings covering:

  • Credit SDK ABI mismatches (wrong token, missing payee param, wrong view method name)
  • Contract input validation (.expect() panics in callbacks, unbounded HashMap in set_records)
  • Gateway: domain validation, amount validation, pagination bounds, PendingStore GC, email token in logs
  • Infrastructure: neard downloaded without checksum, services run as root, CI action version pinning
  • And more

Relationship to V2 Audit (PR #37)

PR #37 was closed because development diverged ~50 commits after the PR was opened (architectural shift: relayer → gateway). This issue supersedes it.

V2 findings resolved independently on development: CRIT-05, HIGH-08, MED-01, MED-06, MED-09, MED-17 (and partial fixes for others).

All remaining open V2 findings are carried forward in V3 with updated IDs.

## Security Audit V3 — 71 Findings Full audit document: `SECURITY_AUDIT_V3.md` (committed on `development`) Audit date: 2026-03-02 | Scope: development HEAD `267be5f` Audited areas: 14 smart contracts, gateway server + client, SDK, CLI, Rhai engine, provision/cluster infrastructure, Docker, CI --- ## Summary | Severity | Count | |----------|-------| | CRITICAL | 10 | | HIGH | 20 | | MEDIUM | 23 | | LOW | 18 | | **Total** | **71** | --- ## Critical (fix before any production use) | ID | Area | Issue | |----|------|-------| | C-01 | Rhai | `run_command` / `run_command_output` / `env_var` registered in Rhai engine — full RCE via any `.rhai` script | | C-02 | SDK | `Account` derives `Debug` — private key leaks in any `{:?}` format, panic, or debug log | | C-03 | CLI | `heroledger info` prints 40 chars of private key to stdout | | C-04 | CLI | `--secret-key` / `--mnemonic` visible in `/proc/<pid>/cmdline` | | C-05 | Infra | Validator key files written without `chmod 0600` — world-readable | | C-06 | Infra | `HEROLEDGER_VAULT_SECRET` + `SENDGRID_API_KEY` written as plaintext env vars into zinit service config | | C-07 | Contract | Credit `line_index` Vectors all share `StorageKey::LineIndex` — data corruption across all users | | C-08 | Contract | Credit `approval_index` Vectors all share `StorageKey::ApprovalIndex` — cross-user approval corruption | | C-09 | Gateway | CORS policy is triple-wildcard (`Any/Any/Any`) — same vulnerability as the deleted relayer, never ported to gateway | | C-10 | Gateway | `gateway.submitTransaction` accepts arbitrary user-crafted NEAR transactions — whitelist model bypassed entirely | --- ## High (fix before external user onboarding) | ID | Area | Issue | |----|------|-------| | H-01 | Infra | All 7 git deps pinned to `branch = "development"` — supply chain injection risk | | H-02 | SDK | f64 precision loss converting NEAR amounts: `(deposit_near * 1e24) as u128` | | H-03 | Rhai | `parse_near` returns `i64` — overflows and goes negative for amounts > ~9 NEAR | | H-04 | SDK | `ContractBuilder::deploy` returns `"deployed:<account>"` instead of real tx hash | | H-05 | Contract | Sporex/Groups use raw `b"x"` byte literal storage key prefixes — collision risk | | H-06 | Contract | Groups uses `std::HashSet` in NEAR storage — gas DoS for accounts with many group memberships | | H-07 | Contract | Credit `ft_on_transfer` returns `String` not `PromiseOrValue<U128>` — NEP-141 violation, funds never land | | H-08 | Contract | Hosting/Sporex `ft_on_transfer` returns bare `U128` not `PromiseOrValue<U128>` | | H-09 | Contract | DNS `claim_refund` / `withdraw_fees` have no rollback callback — funds lost on failed token transfer | | H-10 | Contract | Hosting `sla_settle_period` callable by anyone with fabricated data — SLA forgery | | H-11 | Contract | Missing `assert_one_yocto()` on `withdraw`, `spend`, `claim_refund`, `sla_stake_withdraw` | | H-12 | Contract | Faucet `fund_account` has no access control — faucet drainable by anyone | | H-13 | Infra | NEAR RPC bound to `0.0.0.0` in dev mode — bypasses gateway auth entirely | | H-14 | Infra | Caddy installer uses predictable temp dir — binary substitution attack possible | | H-15 | CI | CI builder image uses mutable tag `:2` — supply chain via image repointing | | H-16 | Docker | Base images use floating tags (`:latest`, `ubuntu:24.04`) — not pinned to digest | | H-17 | Gateway | `gateway.submitTransaction` bypasses rate limiter — DoS amplification | | H-18 | Gateway | Rate limiter bypassed by rotating public keys; no per-IP limiting | | H-19 | Gateway | Stored XSS via unescaped `domain` in HTML email confirmation page | | H-20 | Gateway | SSRF via Grid Proxy responses; `reqwest::Client` has no timeouts | --- ## Medium and Low See `SECURITY_AUDIT_V3.md` for the full list of 23 Medium and 18 Low findings covering: - Credit SDK ABI mismatches (wrong token, missing `payee` param, wrong view method name) - Contract input validation (`.expect()` panics in callbacks, unbounded `HashMap` in `set_records`) - Gateway: domain validation, amount validation, pagination bounds, PendingStore GC, email token in logs - Infrastructure: neard downloaded without checksum, services run as root, CI action version pinning - And more --- ## Relationship to V2 Audit (PR #37) PR #37 was closed because `development` diverged ~50 commits after the PR was opened (architectural shift: relayer → gateway). This issue supersedes it. V2 findings resolved independently on `development`: CRIT-05, HIGH-08, MED-01, MED-06, MED-09, MED-17 (and partial fixes for others). All remaining open V2 findings are carried forward in V3 with updated IDs.
Sign in to join this conversation.
No labels
urgent
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
lhumina_code/hero_ledger#38
No description provided.