Skip to content

Revalidate numeric config coercions — a non-numeric value becomes NaN #188

Description

@MattDHill

Number() is applied inside a zod .transform() when coercing config strings to numbers. A transform's output is not revalidated, so a non-numeric value does not fail validation — it succeeds as NaN. There is no error for the trailing .catch() to catch, and the next time the package writes the file, NaN goes back into it.

Sites in this package:

  • startos/fileModels/config.ts:29z.array(z.string()).transform((a) => Number(a.at(-1))),
  • startos/fileModels/config.ts:30z.string().transform(Number),

Fix

Add .pipe(z.number()) after the union, before .optional() / .catch():

const iniNumber = z
  .union([
    z.array(z.string()).transform((a) => Number(a.at(-1))),
    z.string().transform(Number),
    z.number(),
  ])
  .pipe(z.number()) // revalidate the transform's output
  .optional()
  .catch(undefined)

Each field then falls back to whatever its .catch() already declares, instead of carrying NaN forward.

Why not an isNaN guard

z.number() rejects NaN and is finite by default, so the pipe also covers values an explicit isNaN check lets through:

input as shipped explicit isNaN guard .pipe(z.number())
"abc" NaN falls back falls back
"1e999" Infinity Infinity falls back
"Infinity" Infinity Infinity falls back

Severity

Only reachable by hand-editing the config file — form input is validated separately by Value.number, which rejects non-numbers correctly. The result is silent rather than loud, though: in bitcoind, maxconnections=NaN is read as 0, a node that makes and accepts no peer connections at all. Worth checking what this daemon does with NaN in its numeric keys.

Context

Same pattern and same fix in 13 packages across both registries. It spread by copy-paste between packages — the packaging guide never mentions it. Already fixed in bitcoind: Start9Labs/bitcoin-core-startos#279, #280, #281, #282 and Start9Labs/bitcoin-knots-startos#51, #52.

No urgency — pick it up next time this package is touched.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions