v0.32.3 — nine audit findings, including one in 0.32.2's own headline fix
A multi-agent audit of 0.32.2 across nine angles, every finding put through two
independent skeptics. It caught nine real defects — including one in 0.32.2's own
headline fix. The nastiest is not a money bug: an optional dependency could kill
the entire server.
fix(server)— a missingsharptook down all 19 tools.sharpis an
optionalDependency, butinline-image.tsimported it at the top level, so ESM
resolution failed beforemain()ran. Reproduced: with sharp absent,
node dist/index.js --versionexits 1 withERR_MODULE_NOT_FOUNDand empty
stdout — for a preview feature that is off by default, on profiles that never
registerblockrun_image. A freshnpx -y @blockrun/mcp@latesthits it on musl,
unusual arch, offline CI or--no-optional. No dev machine ever saw it, because
every dev machine has sharp built.qr.tsdocuments this exact hazard and loads
lazily; this module now does too. Verified after the fix:--versionreturns
0.32.3andtools/listreturns all 19 tools with sharp removed.fix(chat)— 0.32.2's truncation cap was measured in BYTES; the gateway caps
CHARACTERS. The original sweep only probed ASCII, where the two are the same
number. CJK separates them: 131,000 CJK characters (393,000 bytes) pass through
whole (prompt_tokens 131,065), and both alphabets cap at ~131,072 characters.
Because UTF-8 length is always >= string length, the byte check could only
over-fire — it never missed a real truncation, it invented ones, bolting
"⚠️ TRUNCATED … only the first ~87%" onto complete, correct answers for any
non-ASCII prompt and advising the caller to switch to a paid model. It pushed
agents off a working $0 path into real USDC spend on a false premise, which is
worse than the silence it was meant to fix. The test that pinned the wrong
behaviour is now inverted.fix(security)— SSRF guard was literal-only; wildcard DNS walked through it.
127.0.0.1.nip.iois a public name that resolves to loopback, and was verified
end-to-end reading a local server and base64ing the body into the data URI sent
onward to the gateway;169.254.169.254.nip.ioreaches cloud metadata the same
way. No redirect needed, so the per-hop literal check saw nothing suspicious.
Hostnames are now resolved and every returned address is checked, failing
closed on NXDOMAIN.fix(budget)— two reserve bypasses.blockrun_examatched the raw path, so
contents?x=1missed the per-URL branch: 100 URLs reserved $0.012 and settled
$0.202 (17x), booked wrong permanently. AndestimateChatCostignored input
entirely — a 100k-word prompt settled $0.2557 against a $0.0225 reserve
(11.4x), with break-even at ~15 KB, an ordinary pasted file. Both now priced.fix(wallet)— runningblockrun_walletflipped the chain Base → Solana.
ensureBothWallets()(the default status action) writes.solana-session, and
chain autodetect keys off that file existing. Afterwards every paid tool signs
from a zero-balance Solana wallet or refuses "Base-only" — including
action:"deposit", so the funding path itself became unreachable. Reproduced in a
clean HOME; the chain is now pinned across provisioning.fix(polymarket)— a reverted pUSD transfer reported success. viem does not
throw on revert; the receipt was discarded, so a failed withdrawal still printed
"✅ Withdrawal submitted … the bridge delivers USDC to Base".redeem.tsand
setup.tsboth assertreceipt.status; this path now does too.fix(ci)— the 0.32.2 tag/release automation had two holes. The step had no
version-changed guard, so apublish.yml-only push orworkflow_dispatchwould
tag HEAD and title the release from an unrelated commit. Andactions/checkout
fetches no tags, so the "tag exists" check always missed and the push ran
unconditionally — failing the job after npm had already published. Now gated on
the version diff and checked againstgit ls-remote.docs— the README advertised $0.0075 forblockrun_markets/blockrun_surf;
both charge $0.0095. A 27% understatement of real money in the most-read file,
and "$5 covers ~5,000 market queries" was 9.5x off (~525). Five lines corrected.- 204 tests, typecheck, build and
verify:prices(20/20 exact) green.