Detect and display OAuth URLs in terminal output#59
Conversation
✅ Deploy Preview for agent-native-fw ready!
To edit notification comments on pull requests, go to your Netlify project configuration. |
There was a problem hiding this comment.
Builder has reviewed your changes and found 3 potential issues.
Review Details
PR #59 — OAuth URL Detection in Terminal Output
This PR adds automatic detection of Claude OAuth authorization URLs in terminal (PTY) output, surfacing them as a dismissible banner with a clickable link. The motivation is solid: PTY output splits URLs across chunks with ANSI escape sequences, making them unclickable. The pnpm-lock.yaml changes align jiti versions across packages.
Risk Assessment: 🟡 Standard — The feature reads raw terminal output, strips ANSI codes, and renders a URL as a clickable anchor — security-adjacent code warranting close review.
Key Findings
🔴 HIGH — OAuth State Fixation via Terminal Injection: Any process in the terminal (SSH banner, npm postinstall, curl response) can print a crafted but legitimate https://claude.ai/oauth/authorize?state=ATTACKER&redirect_uri=https://attacker.com/... URL. The banner fires and the user completes the attacker's OAuth flow. The claude.ai domain pin provides no protection since the URL is genuinely a claude.ai URL. Fix: emit a structured WebSocket message from the backend rather than scraping raw PTY bytes.
🟡 MEDIUM — Incomplete ANSI Stripping: The regex only covers CSI sequences (ESC [). OSC sequences (terminal hyperlinks, window titles) leave raw ESC bytes in the buffer, truncating the URL regex match early — producing a broken link or silently suppressing the banner.
🟡 MEDIUM — No xterm Refit on Banner Toggle: Adding/removing pt-8 changes the terminal container height, but fitAddon.fit() is never called, clipping ~1 bottom row while the banner is visible and not recovering space when dismissed.
🟡 MEDIUM — Prefix Wrapping Edge Case: The indexOf("https://claude.ai/oauth/authorize?") search runs on the raw (un-collapsed) buffer. If the PTY wraps the URL before the ?, the prefix search misses it — exactly the scenario this PR is designed to fix.
Found via 2 parallel code-review agent calls with randomized file ordering.
Code review by Builder.io
|
@builderio-bot look at latest PR feedback and fix anything you agree with. Be skeptical. Reply to every comment (directly on the comment thread of each comment) if you fixed it or not and why also, is there a more general fix here where we can just make all URLs clickable in the TTY like other terminals do? |
|
Here's a summary of everything done: PR Feedback Responses
General URL ClickabilityAdded I wasn't able to post replies directly on the PR comment threads (GitHub token lacks write permissions for comments). You can push the code changes using the button in the top right, and the comment summary above can be posted manually. |
There was a problem hiding this comment.
Builder has reviewed your changes and found 2 potential issues.
Review Details
PR #59 Update — OAuth URL Detection (Round 2)
This update adds @xterm/addon-web-links to make URLs in terminal output clickable, and fixes two previously reported issues: the ANSI stripping regex now covers CSI, OSC, and two-char escape sequences, and a useEffect correctly refits the terminal when the OAuth banner appears or disappears.
Risk Assessment: Standard. Introduces a custom WebLinksAddon click handler that processes URLs from raw terminal output.
Key Findings:
MEDIUM - window.opener not cleared in WebLinksAddon handler (found by 2/2 agents): The custom handler uses window.open(uri, blank) without nulling window.opener. Any page opened from a terminal-printed URL retains a live handle to the harness window and can silently redirect it (tabnapping). The OAuth banner anchor tag correctly uses rel=noopener noreferrer, making the WebLinksAddon path the inconsistent and unsafe one.
MEDIUM - OAuth banner fails when PTY wraps before the question mark: The indexOf search runs on the pre-collapsed buffer, so a wrap between authorize and the question mark yields no match - the exact edge case this PR is meant to solve.
LOW - urlBuffer not cleared on restart or WebSocket reconnect.
LOW - WebLinksAddon and OAuth banner both activate for the same URL, creating duplicate UX with different security properties.
Found via 2 parallel code-review agent calls with randomized file ordering.
Code review by Builder.io
|
@builderio-bot look at latest PR feedback and fix anything you agree with. Be skeptical. Reply to every comment (directly on the comment thread of each comment) if you fixed it or not and why |
|
@builderio-bot now that we added the links plugin, can this PR be simplified? maybe instead of all this UI and oauth link detected we can just add the links plugin and strip everything out? I think that would be preferable bc would match how other terminals and products work |
|
Done. Here's the net change — the PR is now just +5 lines vs the original file:
Everything else stripped: OAuth URL buffer, ANSI stripping, |
1 similar comment
|
Done. Here's the net change — the PR is now just +5 lines vs the original file:
Everything else stripped: OAuth URL buffer, ANSI stripping, |
Summary
Adds automatic detection of Claude OAuth authorization URLs in terminal output and surfaces them as a clickable banner, solving the problem of URLs being broken across multiple PTY chunks and line wraps.
Problem
When Claude Code outputs an OAuth authorization URL in the terminal, the URL is split across multiple terminal chunks and wrapped at line boundaries by the PTY, making it impossible to click or copy reliably. Users were seeing "invalid client" errors because the URL was malformed or only partially clickable.
Solution
Buffer incoming terminal data, strip ANSI escape codes and whitespace, then use a regex to reconstruct and extract the full OAuth URL. When a valid URL (containing
state=) is detected, it is displayed in a dismissible banner above the terminal with a proper clickable link.Key Changes
oauthUrlstate andurlBufferref toTerminalTabto accumulate terminal chunks across WebSocket messageshttps://claude.ai/oauth/authorize?prefix in the buffer, collapse whitespace from a 600-character window, and extract a clean URL via regexabsolute-positioned overlay) above the terminal when an OAuth URL is detected, with a styled anchor tag and a close buttonpnpm-lock.yamlto alignjitidependency versions across packagesTo clone this PR locally use the Github CLI with command
gh pr checkout 59You can tag me at @BuilderIO for anything you want me to fix or change