fix(links): stop a document link from opening a local file through an asset URL - #415
Merged
Conversation
… asset URL `resolveLocalFileLinkPath` delegates to `resolveExportImagePath`, which accepts `asset://localhost/…` and `http://asset.localhost/…` - the shape a local image's `src` takes inside the webview, which the exporter has to turn back into a disk path to inline the bytes. A link is the opposite situation: the href is the author's own text. So `[report](http://asset.localhost/Users/me/.ssh/id_rsa)` reads as a remote address in the link text and the status bar, resolves to `/Users/me/.ssh/id_rsa`, and is handed to the OS default handler - now reachable, since the opener path scope was opened in #403. Reusing the image resolver was right; inheriting that one clause of it was not. The link resolver rejects asset URLs before delegating. Co-Authored-By: Claude Opus 5 <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.
A link in a document can read as a remote address everywhere the user can see it, and open a local file.
Measured against the real resolver on
master:That path then goes to
openPath→ the OS default handler. It became reachable in #403, which opened the opener path scope soopen_pathcould run at all.How it got there
resolveLocalFileLinkPath(#409) delegates toresolveExportImagePath(#363), and that was the right call — the scheme / drive-letter / UNC / query-suffix decision table is genuinely shared, and hand-rolling a second one is how thestartsWith('http://asset.localhost')prefix-spoofing hole appeared in the first place.But one clause of it does not transfer.
normalizeAssetPathaccepts asset URLs because that is the shape a local image'ssrctakes inside the webview, and the exporter has to turn it back into a disk path to inline the bytes. That is the image's reason to change.A link is the opposite situation: the href is the author's text. There is no reason for a document's link to name a webview-internal asset URL, and accepting one turns the URL scheme — the one thing a reader uses to judge where a link goes — into a lie.
The module's own comment lists "the two rules this caller adds that an image does not need". It missed the rule this caller should subtract.
Neither #363, #409 nor #403 is wrong on its own. The deception is what they compose into.
The fix
Reject asset URLs before delegating — three lines, plus the reasoning at the call site so the next person reusing this resolver sees which clause is image-specific.
Tests
scripts/localFileLinks.test.tsgains one test covering both URL forms, uppercase variants, a Windows drive-letter payload, and — asserted so a future loosening of the host pattern cannot pass this file — theasset.localhost.evil.testlookalike. Plus two assertions that ordinary links still resolve, so the guard cannot over-fire.an asset URL is never treated as a link to a local fileNot covered
srchandling is unchanged and should be. An<img>pointing at an asset URL is exactly what the webview produces for a local image; only the link path rejects it.resolveExportImagePath: it answers "what disk path does this webview-side reference name", which is not the same question as "is this something the author asked to open".🤖 Generated with Claude Code