Skip to content

Don't splice a link into the middle of a URL - #117

Open
pranayshirolkar wants to merge 3 commits into
github:mainfrom
pranayshirolkar:fix-splice-inside-url
Open

Don't splice a link into the middle of a URL#117
pranayshirolkar wants to merge 3 commits into
github:mainfrom
pranayshirolkar:fix-splice-inside-url

Conversation

@pranayshirolkar

@pranayshirolkar pranayshirolkar commented Aug 6, 2026

Copy link
Copy Markdown

Fixes #118.

Problem

Copy a link from a native app or a clipboard tool and paste it into a GitHub comment box, and the Markdown comes out with a [ planted in the middle of the URL:

text/plain: https://github.com/owner/repo/blob/main/a.js#L7
text/html:  <a href="https://github.com/owner/repo/blob/main/a.js#L7">repo/blob/main/a.js#L7</a>

pasted:     https://github.com/owner/[repo/blob/main/a.js#L7](https://github.com/owner/repo/blob/main/a.js#L7)
expected:   [repo/blob/main/a.js#L7](https://github.com/owner/repo/blob/main/a.js#L7)

It renders as two links, one of them junk. The shape that triggers it — text/plain is the URL, text/html is an anchor labelled with a shortened rendering of that URL — is how a lot of tooling writes a link to the pasteboard (macOS automation scripts, clipboard managers, "copy link" affordances that shorten the visible text).

Cause

convertToMarkdown splices [label](href) over the label at the offset where indexOf finds it in the plain text:

https://github.com/github/paste-markdown/blob/main/src/paste-markdown-html.ts#L83-L90

That's right for prose containing a link — example link plus more text — because there the plain text is a flattening of the HTML. When the plain text is the URL and the label is a piece of it, the label is found inside the URL (offset 25 in the example above) and the splice covers only the tail.

Fix

When the label's occurrence sits inside a longer whitespace-delimited token, decide by what that token is:

  • it is this link's own href — the label is a shortened rendering of the URL, so the link replaces the whole token. This produces the link the clipboard meant.
  • it is some other URL — leave the paste alone. Splicing inside a URL is never right, and there is no sensible Markdown to emit.
  • anything else — unchanged: the label's own occurrence is the span, so <a href="…">foo</a>bar pasted alongside foobar still yields [foo](…)bar.

areEqualLinks already handles the trailing-slash and casing differences between an anchor's normalized href and the pasted text, so the comparison reuses it.

Tests

Two added, both failing before this change and passing after (38 passed, 2 failed40 passed):

  • links the whole url when the label is a shortened rendering of it
  • doesn't splice a link inside a url that is not its own href

All existing tests pass unchanged.

pranayshirolkar and others added 2 commits August 5, 2026 17:25
`convertToMarkdown` turns HTML into Markdown by splicing `[label](href)` over
the label at the offset where it finds that label in the plain-text flavor.
That assumes `text/plain` is a flattening of `text/html`, which holds for prose
containing a link but not for how many native apps and clipboard tools copy a
link: `text/plain` is the URL, and `text/html` is an anchor labelled with a
shortened rendering of it.

The label's only occurrence is then inside the URL, so the `[` lands there:

    text/plain: https://github.com/owner/repo/blob/main/a.js#L7
    text/html:  <a href="…">repo/blob/main/a.js#L7</a>
    pasted:     https://github.com/owner/[repo/blob/main/a.js#L7](https://github.com/owner/repo/blob/main/a.js#L7)

Such a label describes the whole URL, so replace the whole whitespace-delimited
token when it is this link's own href — which yields the link the clipboard
meant, `[repo/blob/main/a.js#L7](https://…#L7)`.

When the token is a URL but *not* this href, leave the paste alone rather than
corrupt it; splicing inside a URL is never right. Every other case keeps the
label's own occurrence, so a label that merely abuts other text
(`<a href="…">foo</a>bar` pasted alongside `foobar`) is unaffected.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The equal-length guard and the fallback both returned the label's own
occurrence, so two of four return paths produced the same value and the guard
read as a distinct case. Nesting the URL checks under the length comparison
leaves one such return, and computes the token's text only where it is used.

Also renames `isURL` to `looksLikeURL`. paste-markdown-link.ts already has an
`isURL` that requires the whole string to round-trip through `new URL()`; this
one only tests for a scheme, because the question here is whether splicing into
the text would corrupt a URL rather than whether it is a valid one. The shared
name invited a future consolidation that would change behaviour.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@pranayshirolkar
pranayshirolkar marked this pull request as ready for review August 6, 2026 00:42
@pranayshirolkar
pranayshirolkar requested a review from a team as a code owner August 6, 2026 00:42
Copilot AI balanced review requested due to automatic review settings August 6, 2026 00:42

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Changes recommended

Punctuation-wrapped URLs can still be corrupted because URL detection only checks the token’s first character.

Once you've addressed the issues Copilot identified, you can request another Copilot review.

This review doesn't count toward merge requirements. Sign up for the private preview to control whether Copilot approvals count.

Pull request overview

Prevents shortened link labels from being inserted into the middle of pasted URLs.

Changes:

  • Detects URL-containing plaintext tokens and selects a safer replacement span.
  • Adds regression tests for matching and mismatched URLs.
File summaries
File Description
src/paste-markdown-html.ts Adds link-span detection and URL safeguards.
test/test.js Adds regression coverage for shortened labels.
Review details
  • Files reviewed: 2/2 changed files
  • Comments generated: 1
  • Review effort level: Balanced

We're testing this review assessment. Please use 👍 or 👎 to tell us if it's correct.

Comment thread src/paste-markdown-html.ts
`looksLikeURL` tests for a scheme at offset 0, so `(https://example.com/a)` did
not read as a URL: neither branch applied and the label was still spliced inside
it, leaving `(https://github.com/owner/[repo](…))`.

Try the whitespace-delimited token first and then the same token with wrapping
punctuation trimmed off both ends. Order matters: a URL can end in a bracket of
its own, as `…/wiki/Ruby_(programming_language)` does, and trimming first would
strip that bracket, fail the href comparison, and decline a paste that has a
perfectly good link in it. Both orders are covered by tests.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@pranayshirolkar

Copy link
Copy Markdown
Author

@keithamus , can you take a look at this when you get a chance?

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.

Pasting a link whose text/html label is a shortened form of its URL plants a [ inside the URL

2 participants