CRITICAL: human input paths accept NaN/Inf and poison the whole economy #120

Closed
opened 2026-10-01 15:31:08 +00:00 by Winnie · 0 comments
Owner

Found by the v0.6.0 review (issue #118). Every AI entry point guards non-finite values (finiteAI/aiFault, ruling #91) but none of the human paths do, and strconv.ParseFloat accepts "NaN"/"Inf" with no error:

  • Deposit-rate prompt (main.go:246-247, promptRateDef): only checks v >= 0, no cap, no finite check. Typing a rate above the cap is accepted raw (the label says [0–cap]), and Inf → exp(Inf)/Inf = NaN in depositFlow (game.go:314) → every bank's deposits and cash become NaN. Verified: 7,281 NaN lines in a scripted run answering "Inf".
  • Single-loan bid (main.go:905-908): clampf(NaN, 0, cap) = NaN; a NaN bid silently loses every auction or books a NaN-rate loan (NaN interest, NaN statements). The "all r" path (main.go:874) has the IsNaN/IsInf check; the per-loan path was missed. Verified: NaN-rate loan booked, NaN NET INCOME.
  • Bond offer sheet (main.go:971-976): "price <= 0 || size <= 0" is false for NaN → NaN offer placed, b.Cash -= NaN poisons the player's own cash, the market fills at NaN, and --replay's json.Marshal fails. Verified: 1,115 NaN lines from typing "NaN 5".
  • Bond take (main.go:1103-1108, game.go:515-518): "want <= 0" is false for NaN → NaN fills poison cash and books. Verified: bondTakeAll(1, NaN) → cash=NaN, fills=1.

Fix direction: one finite-checking parse helper for every human numeric entry, plus the deposit-rate clamp to [0, cap].

Found by the v0.6.0 review (issue #118). Every AI entry point guards non-finite values (finiteAI/aiFault, ruling #91) but none of the human paths do, and strconv.ParseFloat accepts "NaN"/"Inf" with no error: - Deposit-rate prompt (main.go:246-247, promptRateDef): only checks v >= 0, no cap, no finite check. Typing a rate above the cap is accepted raw (the label says [0–cap]), and Inf → exp(Inf)/Inf = NaN in depositFlow (game.go:314) → every bank's deposits and cash become NaN. Verified: 7,281 NaN lines in a scripted run answering "Inf". - Single-loan bid (main.go:905-908): clampf(NaN, 0, cap) = NaN; a NaN bid silently loses every auction or books a NaN-rate loan (NaN interest, NaN statements). The "all r" path (main.go:874) has the IsNaN/IsInf check; the per-loan path was missed. Verified: NaN-rate loan booked, NaN NET INCOME. - Bond offer sheet (main.go:971-976): "price <= 0 || size <= 0" is false for NaN → NaN offer placed, b.Cash -= NaN poisons the player's own cash, the market fills at NaN, and --replay's json.Marshal fails. Verified: 1,115 NaN lines from typing "NaN 5". - Bond take (main.go:1103-1108, game.go:515-518): "want <= 0" is false for NaN → NaN fills poison cash and books. Verified: bondTakeAll(1, NaN) → cash=NaN, fills=1. Fix direction: one finite-checking parse helper for every human numeric entry, plus the deposit-rate clamp to [0, cap].
Sign in to join this conversation.
No labels
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
Winnie/bankwars#120
No description provided.