You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
A full-project audit, all ten findings fixed, then re-reviewed until clean. An independent deep review of the whole repository (P0: none) surfaced ten conditional-but-real defects. Every fix went back through further independent review rounds, which caught real bugs in the first fixes themselves (case and Unicode boundaries, a cancellation regression); the final round accepted with no blocking findings. Each item below carries a regression test — the suite grew by 32 cases.
Cross-project recovery, two ways in. The OpenCode directory filter's ancestor branch built a LIKE pattern from the database's own session.directory, so _/% inside another project's path acted as wildcards; the comparison now uses exact SUBSTR prefixes in both directions, measured in Unicode code points (SQLite counts characters, JS counts UTF-16 units, so a path with an emoji shifted the boundary), case-sensitive on POSIX and LOWER-folded on Windows (SQLite's LIKE was ASCII case-insensitive, crossing projects on case-sensitive filesystems), with filesystem roots normalized so / and E:/ match as ancestors. And a Claude/Pi transcript recording no cwd at all was accepted on slug evidence alone, though slugs collide (/tmp/a.b and /tmp/a-b); ownerless transcripts are now skipped by scanning and session lookup, while an explicit --transcript still reads anything you point it at.
Image-type spoofing. When magic-byte sniffing failed, the extension or content-type used to win, so an .png URL serving HTML was encoded and uploaded as an image. Every allowed type must now prove itself from its file header — heic/heif included, via ftyp-box sniffing — so there is no extension or content-type fallback left at all.
Credential redaction, everywhere errors quote foreign text. Subprocess stderr, the three API providers' error bodies, and discovery-probe failures now pass through a shared redactor (known keys replaced exactly, common token shapes as the second net) before reaching terminals, meta.attempts, model contexts, or the discovery cache. Remote-URL errors quote origin and path only, so signed query tokens (S3 presigns) never travel, and the shapes are tuned to leave prose like "token limit_exceeded" alone.
Windows PATH probing.findOnPath now walks PATHEXT, so a real agy.exe/claude.cmd install no longer reads as "not on PATH" (doctor and the failover chain both trusted that verdict).
dsh evidence cache. Failed reads are no longer memoized forever (a fixed config gets a fresh chance without restarting dsh), concurrent steps join one in-flight read instead of double-spending the engine, the cache is capped LRU-style, and cancellation is per-waiter: aborting one step stops its own wait immediately while the shared read completes into the cache, instead of killing every concurrent joiner. Pasted HEIC/HEIF now maps to its real extension, and an unknown media type refuses instead of dressing up as PNG.
Docs told the truth about less than the code did. The security page now states per provider who fetches a remote URL (only gemini-api downloads locally behind the SSRF guards, magic-byte check, and size cap; openai/anthropic hand the URL to the vendor). The output contract now lists visual as required, matching the schema that always enforced it, with a docs-contract test pinning the two together. Doctor renders CLI providers as [ok?] installed / sign-in not verified offline instead of a flat ok, adds a machine-readable status field (ready/installed/missing) so JSON consumers stop reading bare-binary as ready, and the install flow treats the first real read as the auth check. The skill's trigger list drops .bmp (never supported by the CLI) and gains .heif.
Hardening around the edges. The release script and workflow now run lint, refuse version downgrades, and verify the tag matches package.json; CLI numeric flags reject trailing garbage (--count 3x); error exits use process.exitCode so piped output survives.
dsh install can silently land on 3.5.0 (#18). pnpm v11's release-age quarantine falls back to an old version when every recent one is inside the window (10 days on pnpm 11.21, measured), and versions before 3.9.0 carry no dsh.bundle declaration, so the plugin installs as a plain dependency and none of the tools appear. There is no CLI or env override for the gate (pnpm#11224 is still open), but an explicit version or dist-tag skips it (pnpm#9989, verified against pnpm 11.21: a bare add resolved 2.8.0 while @latest resolved 3.10.0 under the same default gate). The install command everywhere now carries @latest, so new installs land current with zero user-side setup; troubleshooting documents the mechanism plus the durable bare-name minimumReleaseAgeExclude fallback for whenever pnpm closes that skip. Thanks to @stonogic086-1 for the precise diagnosis.