Skip to content

Fix all findings from the 2026-09 code audit - #15

Merged
DLANSAMA merged 19 commits into
mainfrom
fix/audit-2026-09-22
Sep 23, 2026
Merged

DLANSAMA merged 19 commits into
mainfrom
fix/audit-2026-09-22

Conversation

@DLANSAMA

Copy link
Copy Markdown
Owner

Fixes every finding from a full-codebase audit of main (d1defc0). Each was reproduced with a script or measured on real data before it was fixed, and each fix carries a regression test.

Cost accuracy

  • Antigravity CLI cost was understated ~32%. Disk-captured (:) ledger entries had cache reads collapsed to each conversation's peak while identical RPC (#) entries were summed; cost depended on the capture path. All paths now bill cache reads per call. (Real ledger: 30-day $713.72 → $1,046.39.)
  • Codex repeated token_count events were counted twice (100 of 13,809 real events); total-only events now contribute their delta.
  • Undated gen_metadata generations: the fallback read each record twice (field 4 + its field-17.2 copy) — measured 2.00× on real blobs.
  • Carried-forward providers now get this run's local cost summary instead of freezing the cached one for 15 minutes.

Features that didn't work

  • The Claude Code statusline capture (integrations/claude_code) was written but never read. It is now a fallback when claude.ai can't be read (Cloudflare 403/429, signed out, locked wallet, --no-network).

Correctness

  • Cookie jars are built from one unexpired, most-recently-used browser profile (the last store read used to win every collision — on the dev machine a Firefox profile unused for four months shadowed live Chrome cookies). Firefox expiry is now milliseconds; normalized.
  • Monthly-budget alert no longer replays after a cost-scan timeout.
  • Grok shows 0% once the logged billing period has ended (and "This week" follows the current week).
  • Refresh watchdog (45 s) now covers the backend's guaranteed worst case (3·timeout + 2, 38 s); the Codex cookie fallback is capped to the remaining budget.
  • Custom budget field parses with the locale's separators (de_DE "12,50" used to save as $1250).
  • Tray attention badge / panel bars ignore extra-usage rows, matching notifications.
  • Notification bodies are markup-escaped.
  • Pricing memo can no longer keep a price resolved against a replaced catalog (generation-guarded).
  • Antigravity process scan only considers the current user's processes.
  • Claude usage_summary misses are remembered for 24 h instead of costing an extra claude.ai GET per refresh.
  • CompactRepresentation uses the shared statusIsBad helper.

Tests / tooling

  • Render tests were clock- and desktop-dependent (failed every day after capture). screenshot.qml takes today= (Makefile SHOT_DAY) and a repo-local XDG_CONFIG_HOME pins the breeze-dark icon theme; the two widget screenshots are regenerated (footer icons only). Cost popouts re-render byte-identical to the committed PNGs.
  • tests/conftest.py isolates every test from the developer's live statusline capture.

Verification

  • make check (flake8, qmllint full, mypy): green. pytest tests/: 719 passed; with CI=1: 714 passed, 5 skipped (render tests).
  • 12 offscreen renders (5 provider tabs, 3 cost tabs, 2 compact panels, 2 error-state views) byte-identical between main and this branch.
  • Deployed locally with make upgrade; the live-desktop check is still pending (the session was locked when plasmashell restarted).

Disk-scanned ':' steps entries had their cache reads collapsed to each
conversation's peak while RPC '#' entries of identical per-call shape were
summed, so the agy CLI's cost depended on the capture path and dropped when a
CLI stem flipped from RPC to disk ownership. On a real CLI-heavy ledger the
30-day figure moves from $713.72 to $1,046.39.
integrations/claude_code/statusline_capture.py wrote ~/.tallybar/claude_statusline.json
but nothing read it. build_snapshot now uses it when the claude.ai cookie path yields
no live limits (Cloudflare 403/429, signed out, wallet locked, --no-network): expired
windows dropped, >6h captures ignored, >15min flagged stale, a newer capture replaces a
carried reading. tests/conftest.py isolates every test from the developer's live capture.
A timed-out or failed cost scan leaves no costSummary, which read as $0
month-to-date; the budget block then discarded every armed key and the next
good refresh replayed the alert. Budget evaluation is now skipped (armed state
held) when the scan timed out/errored or no provider carries a costSummary.
carry_forward_provider_last_good deep-copied the cached entry including its
costSummary, and apply_cost_summaries only setdefault'ed, so the fresh local
scan was dropped for the whole 15-minute grace window. The carried copy now
omits costSummary and apply_cost_summaries assigns (a None summary clears).
The cost-availability check read snapshot.get("diagnostics") twice, which mypy
can't narrow; bind it once.
…file

cookiejar_for_domains merged every browser profile; the last store read
(Firefox) won each (domain, path, name) collision and expiry was ignored. On
the dev machine a Firefox profile unused for four months contributed 16 claude.ai
and 35 openai cookies (8 and 23 of them expired) that shadowed live Chrome ones.
Readers now normalize expiry/last-access to unix seconds (Firefox expiry is ms),
select_session_cookies drops expired cookies and picks the single most recently
used profile, and missing-cookie checks use the same view.
The byte-exact render tests failed on any day but the capture day
(screenshot.qml rebased the fixture's week/month buckets onto the real today)
and whenever the desktop icon theme changed (the kde platform theme reads it
from the developer's kdeglobals). A today= argument (Makefile SHOT_DAY) and a
repo-local XDG_CONFIG_HOME pinning breeze-dark make renders byte-stable; the
cost popouts re-render identical to the committed PNGs, and the two widget
shots are regenerated with breeze-dark footer icons (checked by eye).
commit() stripped every comma before parseFloat, so a decimal-comma locale's
"12,50" (accepted by the DoubleValidator) saved as $1250. parseLocaleAmount
(ui_helpers.js, node-tested) strips the locale's group separator, maps its
decimal point to '.', and reads a C-style "12.50" typed in a comma locale as
12.5; verified under qml6 with de_DE/fr_FR/en_US.
The billing event is only logged while the grok CLI runs, so after a quiet
week the newest event describes an ended period and the bar kept last week's
percentage with "Reset due". run_grok_local now zeroes the bar and projects
the reset to the current weekly period; grok_billing_period rolls the
"This week" token window forward the same way. Both take now= (tests with
past-dated periods pin it).
Codex re-emits token_count with an unchanged cumulative total_token_usage and
the same last_token_usage; every repeat was counted again (100 of 13,809 real
events, 3.37M tokens). The parser now skips an event whose cumulative total
hasn't moved, and a total-only event contributes its delta instead of the
running sum. Codex parse-cache version bumped so caches rebuild.
main.qml abandoned a refresh after 25s and dropped its result, but the backend
can legitimately take cookies (t+1) + providers (t+1) + the lazy Codex cookie
fallback (t) = 38s at t=12. backend.refresh_worst_case_seconds names that bound
and the fallback is capped to the remaining budget so it is a guarantee; main.qml
derives --timeout from one backendTimeoutSeconds and waits refreshWatchdogMs=45s.
Tests pin the QML/backend relation and drive build_snapshot with every phase
stalled to prove the bound holds.
Plasmoid.status raised NeedsAttention for any limit at >= 90%, including
isExtraUsage rows (Claude overage, credit pools) that the backend's
notifications deliberately skip, and CompactRepresentation's bars/pulse could
pick up a credit row as the secondary window. ui_helpers.usageLimits /
needsAttention (node-tested) now drive both.
The notification server renders body markup (<b>, <a href>, <img>) and bodies
carry provider/API error messages, so a '<' or '&' garbled the text and an
anchor or image in a response would render live. Bodies now pass through
ui_helpers.escapeNotificationMarkup (the summary is plain text per spec and is
left as is).
get_pricing scanned _active_pricing and wrote _resolve_memo outside the lock,
so a refresh_pricing landing mid-scan (another OS thread) could have its
memo.clear() undone by a stale write — the exact race the lock's comment
claimed to prevent. A catalog generation counter (bumped on every rebuild,
including invalidate_cache, now locked) gates the memo write; the scan stays
lock-free. A test forces a refresh mid-scan and fails without the guard.
/proc/<pid>/cmdline is world-readable, so on a shared machine the language-
server scan also matched another user's Antigravity process: it lifted that
server's CSRF token from the cmdline, queried its loopback API and folded
that user's usage into this user's ledger. Both the psutil path (real uid)
and the /proc path (_owned_by_current_user, fail-closed) now filter by owner.
CompactRepresentation carried its own copy of the bad-status switch (same 13
statuses as lib/ui_helpers.js today, but free to drift) against the 'shared
QML helpers live in lib/, don't re-inline' contract. It now delegates, and a
test fails if any QML statusIsBad re-implements the set. Verified by an
offscreen render: an unauthorized provider still dims the glyph and shows the
amber badge.
When _pb_generations couldn't date a gen_metadata blob, the fallback ran the
recursive _pb_find_usage, which returns every usage record twice (field 4 and
its field-17.2 copy): measured on 14 real undated CLI blobs, exactly 2.00x the
uncached input. The fallback now reads field-4 records structurally without the
timestamp requirement (marker 26 only), and the recursive scan — kept for
unknown layouts — drops exact duplicates first.
Follow-up to f8d71f9, which was committed with a failing test and a mypy error:
_pb_generations' local list must allow secs=None, and the exact-dict test of
its output now includes the new mk marker key (24 for that fixture).
run_claude_api tried /usage_summary before /usage on every refresh; for this
account it 404s (2026-07), so each refresh paid an extra authenticated
claude.ai GET — the call volume that trips Cloudflare. A definite miss (404/410
or 200 without limits) is now stamped as usageSummaryUnavailableSince and later
refreshes go straight to /usage for 24h, then re-probe. Transient 403/429/5xx
never stamp it.
@DLANSAMA
DLANSAMA merged commit cf0ef22 into main Sep 23, 2026
1 check passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant