Skip to content

fix(image-write): add a per-session cumulative volume cap - #291

Merged
mtskf merged 4 commits into
mainfrom
fix/image-write-session-volume-cap
Jul 29, 2026
Merged

fix(image-write): add a per-session cumulative volume cap#291
mtskf merged 4 commits into
mainfrom
fix/image-write-session-volume-cap

Conversation

@mtskf

@mtskf mtskf commented Jul 29, 2026

Copy link
Copy Markdown
Owner

Summary

Add a per-session cumulative volume cap to the image-write path. The existing per-message caps bound a single write but do nothing against a compromised webview that loops image-write with distinct PNG-magic-prefixed payloads — each content-addresses to a new <docFolder>/assets/ file, filling the disk silently while every per-message cap is individually satisfied.

Changes

  • New image-write-budget.ts: a pure session-scoped cumulative byte counter (createSessionVolumeBudget) that rejects once a generous 512 MiB ceiling is crossed and fires a one-time warning (a per-write toast would itself be a notification-flood vector).
  • handleImageWrite gains an optional reserveBudget dep, checked after validation (only bytes that would actually hit disk are counted) and before the write; on reject it posts ok:false without a toast — the budget owns its one-time warning.
  • createImageWriteWiring constructs one budget per panel (= per editor session) and wires reserveBudget + the warning. Reopening the document starts a fresh budget.

Related

  • TODO: image-write — add a cumulative volume cap / rate limit per session (full-codebase review round 2, 2026-07-24)

Test Plan

  • pnpm compile green
  • pnpm test:unit green (new image-write-budget.test.ts pins the trip + warn-once; service test pins the budget-reject path; wiring test pins the per-session cap end-to-end)
  • pnpm lint clean on changed files
  • Normal single/multi-image paste flows unaffected (512 MiB budget never approached)

mtskf added 4 commits July 29, 2026 17:25
Per-message caps (MAX_IMAGE_DATA_LENGTH + the 10 MB decode limit) bound a
single image write but nothing stops a compromised webview from looping
image-write with distinct PNG-magic-prefixed payloads — each content-
addresses to a NEW <docFolder>/assets/ file, filling the disk silently while
every per-message cap is individually satisfied.

Add the count/volume bound every other webview->host channel already has: a
generous 512 MiB cumulative byte budget scoped to the editor session (one per
panel). Once crossed it rejects further writes and warns the user once;
reopening the document starts a fresh budget. Real paste flows never approach
it.
Review-cycle fixes for the session volume cap:
- Make the budget a required dep (one SessionVolumeBudget object) so the
  pre-cap 'unbounded' state is no longer a silent default of ImageWriteDeps.
- Add release(): refund a reservation when the write fails, so a run of
  transient FS failures can't exhaust the session cap and lock out pastes.
- Guard reserve()/release() against NaN/negative input, which would otherwise
  poison or rewind the running total (disabling the cap).
- Correct the module header (this is the first session-cumulative bound, not
  MAX_LINT_DIAGNOSTICS precedent) and the reserve/rollback comments.
- Add tests: per-panel budget isolation, default-budget normal-paste flow,
  release-on-failure, early-exit paths skipping the charge, domain guards.
- Bump version 0.1.59 -> 0.1.60 + CHANGELOG entry for the user-visible cap.
…cope

Cycle-2 review fixes:
- Remove release()/refund from SessionVolumeBudget. workspace.fs.writeFile is
  not atomic, so a failed write can still leave a partial content-addressed
  file; refunding the reservation would let an attacker loop failing writes and
  grow the disk without bound while every charge is returned. Never refunding
  keeps total disk growth bounded by the budget — the fail-safe choice for a
  disk-fill cap. Cost (a session with many genuine FS failures may hit the cap
  early; reopen to reset) is negligible against 512 MiB.
- Narrow handleImageWrite's try to the write alone and post the success result
  outside it, so a throwing postResult can no longer be misread as a write
  failure (wrong toast + double post).
- Test: over-cap-data-string path now asserts the budget is not charged, like
  its sibling early-exit tests; add an end-to-end no-refund exhaustion test.
Record why the budget is reserved before the whole write (createDirectory +
writeFile): a pre-write setup failure is charged despite writing no bytes, but
that is deliberate — reserving here keeps budget rejection cleanly separate from
I/O failures, and the over-charge is immaterial (createDirectory only fails on a
broken FS, which blocks pastes regardless of the cap). Comment-only.
@mtskf
mtskf merged commit 01ad95f into main Jul 29, 2026
1 check passed
@mtskf
mtskf deleted the fix/image-write-session-volume-cap branch July 29, 2026 08:04
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