Security (EVM-10): oracle-service has no liveness alerting; silent failure compounds CW-05 #11
Labels
No labels
enhancement
governance
QA
testing
No milestone
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set
Reference
code/ust1-window#11
Loading…
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
@PlasticDigits finding from security review on #5.
Severity
Medium. Operational — combines with CW-05 to create invisible attack surface.
Description
oracle-service/src/main.rsmainloop logswarn!(error = %e, "tick failed")on any error but exposes no external signal of liveness. There is no metrics endpoint, no heartbeat file, no external alert path, no exit-on-prolonged-failure.The service will silently retry every
poll_interval_secs(default 6h) forever if BSC RPC is down, Terra LCD is unreachable, the mnemonic is wrong, or VenusexchangeRateStoredreverts.Why this matters
Paired with CW-05 (no staleness guard on ust1-window), this is the real attack surface:
No one notices until either a user complains or someone eyeballs the logs.
Recommendation
At minimum:
sign_and_broadcast_executeAND after every successfulcheck_rate_update(even no-op ticks count as liveness)last_broadcast_epoch_seconds)Pair with systemd / docker healthcheck / external uptime monitor to catch the exit/alert.
Fixing this largely reduces the severity of CW-05 — if the staleness guard is missing on-chain but the off-chain liveness is solid, stale rates can't persist long enough to matter.
Related
mentioned in issue #5
mentioned in commit
31a8b3a64fFix merged (main @
7aa1094)Liveness / logging (no Prometheus)
check_rate_updateresult is logged with structured fields (event = "check_rate_update",outcome = "ok" | "failed").sign_and_broadcast_executecompletes withinfo!on success (tx hash + contract) orwarn!on failure; LCD responses with non-zerotx_response.codeare treated as errors so we do not record a successful broadcast.LivenessTracker: time since last successful on-chain broadcast; each poll tick emitserror!withalert = "LIVENESS_ORACLE_NO_BROADCAST"when silence exceedsORACLE_MAX_SILENCE_SECS(default 28800 = 8h). Threshold is tunable via env.Docs
No metrics server added; service stays light for Render.
@PlasticDigits verified fix on
31a8b3a.New
liveness.rsmodule:LivenessTrackertrackslast_successful_broadcast: Option<Instant>record_successful_broadcast()called after each successful Terra txsilence_since_last_broadcast()falls back to process start when no broadcast yetshould_alert(max_silence)→ORACLE_MAX_SILENCE_SECSdefault 8h, configurableIntegration:
check_rate_updateoutcome logged (ok vs policy rejection)sign_and_broadcast_executesuccess/failure loggedtx_response.codenow treated as broadcast failure (catches silent Terra LCD failures)Tracker implementation is clean — no Prometheus dep bloat, pure in-process
Instanttracking suitable for sidecar/container deployments.