feat(bookmarks): captured-article images → encrypted offline assets (9.18.9)#88
Merged
Conversation
…9.18.9) Readable capture emits image blocks pointing at the article's REMOTE http URLs, which the app CSP (`img-src 'self' data: brainstorm:`) blocks — so captured articles rendered image-less and broke offline. Sub-fetch each remote image through the SAME guard chain as favicon/cover (`fetchAndStoreImage` → SSRF / privacy / size / MIME), store it encrypted, and rewrite the block `src` to `brainstorm://asset/<id>` (auto-binds an `asset_refs` row). Bounded: MAX_ARTICLE_IMAGES distinct URLs, 5 MiB each, concurrency 4 — a hostile page can't drive unbounded egress. An image that fails any guard keeps its remote src and is dropped at render. - new pure walk/rewrite helpers `readable/enrich-blocks.ts` (6 tests) - handler `enrichBlocksWithAssets` wired into `handleReadable` (2 tests) - render: keep `brainstorm://asset/` images, drop only remaining remote ones Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What
Readable capture (
network.readable) emits image blocks pointing at the article's remote http URLs. The sandboxed-app CSP (img-src 'self' data: brainstorm:) blocks those, so captured articles rendered image-less and didn't work offline.This sub-fetches each remote article image through the same guard chain as favicon/cover (
fetchAndStoreImage→ SSRF / privacy / size / MIME), stores it in the encrypted asset store, and rewrites the blocksrctobrainstorm://asset/<id>— which renders, works offline, and auto-binds anasset_refsrow (the bind writer derives refs frombrainstorm://asset/URLs in the entity body).Safety / bounds
Article image URLs are attacker-controlled, so the fetch is capped:
MAX_ARTICLE_IMAGES = 40distinct URLs (walk stops early)ARTICLE_IMG_SIZE_CAP_BYTES = 5 MiBper imageARTICLE_IMG_CONCURRENCY = 4(bounded batches)srcand is dropped at render — never blocks the captureChanges
readable/enrich-blocks.ts— pure, network-free walk/rewrite helpers (collectRemoteImageSrcs,rewriteImageSrcs,isRemoteHttpImageSrc). 6 unit tests.enrichBlocksWithAssetswired intohandleReadable; reuses existingfetchAndStoreImage. 2 end-to-end handler tests (happy rewrite + guard rejection).bookmark-detail.tsx— keepbrainstorm://asset/images, drop only the remaining remote ones.Tests
bun run test→ 14195 passed, 0 test-case failures (the 10 failed suites are the known worktreeElectron failed to install correctlycollection artifact — pass in CI). typecheck (packages + apps) + lint clean.🤖 Generated with Claude Code