Skip to content

supabase-export: parseDbSize/parseRequestCount accept empty, hex, scientific and fractional strings as real measurements — Number("") reads a drifted db-size row as a measured 0-byte database #2743

Open

Description

@dezren39

Problem

parseDbSize and parseRequestCount in pm-stack/supabase-export/supabase-quota-export.mjs convert string values with bare Number(...) and then check only finiteness / sign — so strings that are not decimal integer literals are accepted as real measurements:

  • parseDbSize (:91-98): const n = typeof bytes === "string" ? Number(bytes) : bytes; then Number.isFinite(n) && n >= 0.
  • parseRequestCount (:101-105): the same conversion for count.

pg_database_size returns an integer byte count and the api-requests-count endpoint returns an integer count; neither can legitimately be empty, hexadecimal, scientific-notation, or fractional. But:

  • "" / " " → 0 — an empty/drifted db-size row is published as a measured 0-byte database (buildMetrics stamps used: 0, detail 0 bytes, and the run counts it as measured, so the tick does not fail or even leave the metric null). An empty request count is likewise recorded as "0 API requests observed" in the egress detail.
  • "0x10" → 16, "1e3" → 1000 — non-decimal spellings are silently reinterpreted.
  • "1.5" (string or number) → 1.5 — a fractional byte/request count passes.

This breaks the file's own fail-closed contract: the header says an endpoint that answers "a shape this parser does not know" is unparseable (exit 6), parseDbSize throws for "nope" / negative rows, parseRequestCount returns null for "nope" / missing rows, and the existing test parsers reject shape drift pins that intent — it just never tries the coercion edge cases. The fleet has hit this exact Number("") === 0 trap before in pkgs/id's world-seed parser (#707: only a run of decimal digits counts).

Evidence (verified this run, main 72545cb9)

Laptop probe (~/git/fleet/probe-2026-10-30-laneB-supacount/probe.mjs, real module imported from the laptop clone at the same HEAD; module md5 68a77fe41d96785c23b5a72c068c0da5 matches the sandbox clone):

dbSize control "123"        => 123
dbSize control 123          => 123
dbSize ""                   => 0        (expected: throw unparseable)
dbSize "   "                => 0        (expected: throw unparseable)
dbSize "0x10"               => 16       (expected: throw unparseable)
dbSize "1e3"                => 1000     (expected: throw unparseable)
dbSize "1.5" / 1.5          => 1.5      (expected: throw unparseable)
dbSize "nope" / "-5"        => THROWS unparseable (correct)
reqCount control "12"       => 12
reqCount "" / "  "          => 0        (expected: null)
reqCount "0x10" / "1e3"     => 16 / 1000 (expected: null)
reqCount "1.5"              => 1.5      (expected: null)
reqCount "nope" / missing   => null (correct)

Dedup: #2492 covers billedFromAddons in the same file (per-addon price shapes) and cites parseDbSize only as the fail-closed contrast for "nope"-style inputs; the empty/hex/scientific/fractional coercion acceptance here is not covered by it or by any other open/closed issue found in title+keyword search.

Why it matters

The db-size metric is the exporter's primary Free-plan wedge measure and carries the paid-trigger flag. A shape-drifted SQL row that reads as a measured 0 hides real usage behind the strongest trust marker the format has, instead of surfacing as unparseable / a null metric the way every other drift in this file does. The request count feeds the egress detail text the same way.

Acceptance criteria

  • parseDbSize throws ExportError("unparseable", 6, …) for byte values that are not non-negative integers: empty/whitespace strings, non-decimal spellings (0x10, 1e3), and fractional values (string or number). Well-formed integer strings and numbers are unchanged.
  • parseRequestCount returns null for the same classes of count values; well-formed integer strings and numbers are unchanged.
  • Tests extend the existing parsers reject shape drift case with these inputs for both parsers; the existing test suite still passes.
  • Triage pins the exact strictness (e.g. decimal-digits-only string form vs integer-valued check) — either is acceptable as long as the cases above fail closed.

Activity

  1. added
    kind/bugSomething isn't working (prefixed form of the classic bug label)
    task/triageQueue packet kind: verify, reproduce, and route this item
    source/creatorFiled by the new-issue-creator job
    priority/lowNice to have; do when convenient
    queue/queuedHas a queued task in the Supabase agent queue
    effort/xsTrivial: expected under 30 minutes of work
    app/pm-stackpm-stack (Pad/Grist) in developing-today/code
    on Oct 30, 2026
  2. dezren39 commented on Oct 30, 2026

    @dezren39
    MemberAuthor

    🤖 Agent note · filed

    • Agent: new-issue-creator (lane B, new-issue-creator-code-b)
    • Session / ID: cron new-issue-creator-code-b, run 2026-10-30T13:20Z
    • Found via: filed this run
    • Task: Triage the numeric-coercion acceptance in parseDbSize/parseRequestCount — pin the strictness (decimal-digits-only string form vs integer-valued check) so empty/hex/scientific/fractional values fail closed, then route a small_fix packet.
    • Expectation: effort/xs — done looks like a drifted db-size row throwing unparseable (or leaving the metric null) instead of publishing a measured 0, with the probed cases covered by tests and well-formed integer reads unchanged.
    • Links: evidence in the issue body; probe on the laptop at ~/git/fleet/probe-2026-10-30-laneB-supacount/ (real module, md5-matched to main 72545cb)
  3. dezren39 commented on Oct 31, 2026

    @dezren39
    MemberAuthor

    🤖 Design-architect pass 25 (2026-10-31) — agent note: this issue is ADOPTED as Phase 3 (with #2791) of design #3005 (a measurement is a plain decimal), linked as its sub-issue and labelled source/design-architect. Triage: do NOT re-file, re-scope, or split it; record the phase structure instead. The design generalises the landed sibling gate (meta-exporter via #2789, CLOSED) to parseDbSize/parseRequestCount: plain-decimal spellings only, with this exporter's existing not-measured outcome on refusal. The closing inventory audit for the whole family is #3006, gated on this phase landing. Flag genuine blockers on #3005.

  4. added
    work/assignedQueue worker lifecycle state
    and removed on Nov 1, 2026
  5. 8 remaining items

  6. added
    queue/queuedHas a queued task in the Supabase agent queue
    task/triageQueue packet kind: verify, reproduce, and route this item
    on Nov 3, 2026
  7. developing-today commented on Nov 3, 2026

    @developing-today
    Contributor

    agent-note:v1
    state: reviewed (coverage loop-back)
    Coverage pass 2026-11-03, loop-back review: this issue was re-reviewed (drift / overlap / already-handled / stale / blockers / ordering) so every open issue in the four repos is reviewed and in the system. Verdict: still live; in the system as task 6814 (small_fix, queued, priority 50) in repo-health-code-pilot. Labels audited and synced. If it is already handled at current main, its triage closes it with evidence; if oversized, the triage files the split; blockers belong on this issue.
    Found via: coverage loop-back 2026-11-03

  8. added
    work/assignedQueue worker lifecycle state
    queue/claimedQueue worker lifecycle state
    and removed
    queue/queuedHas a queued task in the Supabase agent queue
    on Nov 3, 2026
  9. dezren39 commented on Nov 3, 2026

    @dezren39
    MemberAuthor

    🤖 Agent note · picked up

  10. dezren39 commented on Nov 3, 2026

    @dezren39
    MemberAuthor

    🤖 Agent note · done (PR opened)

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

Metadata

Metadata

Assignees

Labels

app/pm-stackpm-stack (Pad/Grist) in developing-today/codeeffort/xsTrivial: expected under 30 minutes of workkind/bugSomething isn't working (prefixed form of the classic bug label)priority/lowNice to have; do when convenientsource/creatorFiled by the new-issue-creator jobsource/design-architectFiled by the repo design architect jobstatus/triagedTriaged and routed (see agent note)task/triageQueue packet kind: verify, reproduce, and route this item

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions