Skip to content

Conversation

@erannave
Copy link

@erannave erannave commented Dec 10, 2025

Summary

This PR adds support for the restart option in attach mode debugging, allowing the VS Code debugger to automatically reconnect when the debug connection is lost. This is particularly useful when debugging applications that restart (e.g., during development with file watchers).

Features

  • Automatic reconnection for attach mode: Add restart option to attach configuration
    • restart: true - Reconnect with default 10 second timeout
    • restart: <number> - Reconnect with specified timeout in milliseconds (max 5 minutes)
  • Breakpoint preservation: Breakpoints are preserved across reconnections with VS Code coordination
  • Exponential backoff: Reconnection attempts use exponential backoff (1s → 1.5s → ... → 10s max)
  • Reconnection limits: Maximum 10 reconnection attempts per session to prevent infinite loops

Security Fixes

  • SSRF protection: Validate WebSocket URLs to only allow connections to localhost (127.0.0.1, ::1, localhost) or Unix sockets
  • Path traversal protection: Restrict Unix socket paths to the system's temporary directory
  • Race condition fix: Add try/finally to breakpoint removal cleanup to prevent state leaks

Performance Improvements

  • Parallel breakpoint removal: Use Promise.all() for removing multiple breakpoints concurrently
  • Faster breakpoint resolution: Replace 50ms setTimeout with process.nextTick() for breakpoint resolution timing

Code Quality

  • Extract #clearBreakpointsForUrl() helper method to reduce code duplication
  • Replace magic timeout values with named constants (MAX_RECONNECTION_ATTEMPTS, DEFAULT_RECONNECT_TIMEOUT_MS, etc.)
  • Add isBreakpointExistsError() helper for robust error detection across JSC versions
  • Add #removingBreakpoints Set to track breakpoints being removed and prevent race conditions
  • Add comprehensive JSDoc documentation for reconnection behavior and DoS protections

How to Use

In your VS Code launch.json, add the restart option to an attach configuration:

```json
{
"type": "bun",
"request": "attach",
"name": "Attach to Bun",
"url": "ws://localhost:6499/",
"restart": true
}
```

Or with a custom timeout:

```json
{
"type": "bun",
"request": "attach",
"name": "Attach to Bun",
"url": "ws://localhost:6499/",
"restart": 30000
}
```

Test Plan

  • Manual testing: Attach to a Bun process, kill and restart the process, verify debugger reconnects
  • Manual testing: Verify breakpoints are preserved after reconnection
  • Manual testing: Verify reconnection timeout works correctly
  • Manual testing: Verify reconnection gives up after max attempts
  • Security testing: Verify WebSocket URL validation rejects non-localhost URLs
  • Security testing: Verify Unix socket path validation rejects paths outside tmpdir

Related

This implements the DAP supportsRestartRequest capability for attach mode. For launch mode, the existing watchMode option already handles automatic restarts.


🤖 Generated with Claude Code

Co-Authored-By: Claude noreply@anthropic.com

Feature: Automatic reconnection for attach mode
- Add `restart` option to attach configuration (boolean or timeout in ms)
- Automatically reconnect when debug connection is lost
- Preserve breakpoints across reconnections with VS Code coordination
- Support exponential backoff for reconnection attempts

Security fixes:
- Add SSRF protection: validate WebSocket URLs to only allow localhost
- Add path traversal protection: restrict Unix socket paths to tmpdir
- Fix race condition: add try/finally to breakpoint removal cleanup

Performance improvements:
- Parallelize breakpoint removals using Promise.all()
- Replace 50ms setTimeout with process.nextTick() for breakpoint resolution

Code quality:
- Extract #clearBreakpointsForUrl helper to reduce duplication
- Replace magic timeout values with named constants
- Add isBreakpointExistsError() helper for robust error detection
- Add documentation for DoS protections and reconnection behavior

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
@coderabbitai
Copy link
Contributor

coderabbitai bot commented Dec 10, 2025

Walkthrough

Adds WebSocket/Unix-socket attach validation, automatic reconnection with exponential backoff, breakpoint reapplication and race handling, a public restart for attach sessions, Unix socket path validation, and an updated VS Code extension schema and version. Extends AttachRequest and enables supportsRestartRequest.

Changes

Cohort / File(s) Change Summary
Debug Adapter Protocol
packages/bun-debug-adapter-protocol/src/debugger/adapter.ts, packages/bun-debug-adapter-protocol/src/debugger/signal.ts
Added validateWebSocketUrl (SSRF-safe ws/wss/ws+unix validation) and isBreakpointExistsError; enabled supportsRestartRequest; extended AttachRequest with `restart?: boolean
VS Code Extension
packages/bun-vscode/package.json
Bumped version 0.0.310.0.33; extended debugger attach schema to add restart (`boolean

Pre-merge checks

✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately describes the primary change: adding a restart option for attach mode debugging with automatic reconnection capability.
Description check ✅ Passed The description is comprehensive and follows the template with detailed sections covering what the PR does, features, security fixes, performance improvements, usage examples, and a test plan.

📜 Recent review details

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Disabled knowledge base sources:

  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between 8ca4ddd and 61c00c1.

📒 Files selected for processing (1)
  • packages/bun-debug-adapter-protocol/src/debugger/adapter.ts (19 hunks)
🧰 Additional context used
🧠 Learnings (12)
📚 Learning: 2025-11-24T18:37:11.466Z
Learnt from: CR
Repo: oven-sh/bun PR: 0
File: src/js/CLAUDE.md:0-0
Timestamp: 2025-11-24T18:37:11.466Z
Learning: Applies to src/js/{builtins,node,bun,thirdparty,internal}/**/*.{ts,js} : Use `$debug()` for debug logging and `$assert()` for assertions; both are stripped in release builds

Applied to files:

  • packages/bun-debug-adapter-protocol/src/debugger/adapter.ts
📚 Learning: 2025-11-24T18:37:30.259Z
Learnt from: CR
Repo: oven-sh/bun PR: 0
File: test/CLAUDE.md:0-0
Timestamp: 2025-11-24T18:37:30.259Z
Learning: Applies to test/**/*.test.{ts,js,jsx,tsx,mjs,cjs} : Never use hardcoded port numbers in tests. Always use `port: 0` to get a random port

Applied to files:

  • packages/bun-debug-adapter-protocol/src/debugger/adapter.ts
📚 Learning: 2025-12-02T05:59:51.485Z
Learnt from: CR
Repo: oven-sh/bun PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-02T05:59:51.485Z
Learning: Applies to test/**/*.test.{ts,tsx} : Do not write flaky tests - do not use `setTimeout` in tests; instead `await` the condition to be met since you're testing the CONDITION, not TIME PASSING

Applied to files:

  • packages/bun-debug-adapter-protocol/src/debugger/adapter.ts
📚 Learning: 2025-11-24T18:37:30.259Z
Learnt from: CR
Repo: oven-sh/bun PR: 0
File: test/CLAUDE.md:0-0
Timestamp: 2025-11-24T18:37:30.259Z
Learning: Applies to test/**/*.test.{ts,js,jsx,tsx,mjs,cjs} : Prefer async/await over callbacks in tests

Applied to files:

  • packages/bun-debug-adapter-protocol/src/debugger/adapter.ts
📚 Learning: 2025-11-24T18:37:30.259Z
Learnt from: CR
Repo: oven-sh/bun PR: 0
File: test/CLAUDE.md:0-0
Timestamp: 2025-11-24T18:37:30.259Z
Learning: Applies to test/**/*.test.{ts,js,jsx,tsx,mjs,cjs} : Always use `await using` or `using` to ensure proper resource cleanup in tests for APIs like Bun.listen, Bun.connect, Bun.spawn, Bun.serve, etc

Applied to files:

  • packages/bun-debug-adapter-protocol/src/debugger/adapter.ts
📚 Learning: 2025-11-24T18:35:50.422Z
Learnt from: CR
Repo: oven-sh/bun PR: 0
File: .cursor/rules/writing-tests.mdc:0-0
Timestamp: 2025-11-24T18:35:50.422Z
Learning: When expected behavior is ambiguous, defer to matching what happens in Node.js

Applied to files:

  • packages/bun-debug-adapter-protocol/src/debugger/adapter.ts
📚 Learning: 2025-11-24T18:37:30.259Z
Learnt from: CR
Repo: oven-sh/bun PR: 0
File: test/CLAUDE.md:0-0
Timestamp: 2025-11-24T18:37:30.259Z
Learning: Applies to test/**/*.test.{ts,js,jsx,tsx,mjs,cjs} : Do not write flaky tests. Never wait for time to pass in tests; always wait for the condition to be met instead of using an arbitrary amount of time

Applied to files:

  • packages/bun-debug-adapter-protocol/src/debugger/adapter.ts
📚 Learning: 2025-09-03T01:30:58.001Z
Learnt from: markovejnovic
Repo: oven-sh/bun PR: 21728
File: test/js/valkey/valkey.test.ts:264-271
Timestamp: 2025-09-03T01:30:58.001Z
Learning: For test/js/valkey/valkey.test.ts PUB/SUB tests, avoid arbitrary sleeps and async-forEach. Instead, resolve a Promise from the subscriber callback when the expected number of messages is observed and await it with a bounded timeout (e.g., withTimeout + Promise.withResolvers) to account for Redis server→subscriber propagation.

Applied to files:

  • packages/bun-debug-adapter-protocol/src/debugger/adapter.ts
📚 Learning: 2025-12-05T19:28:39.534Z
Learnt from: alii
Repo: oven-sh/bun PR: 25360
File: src/bake/hmr-runtime-client.ts:240-249
Timestamp: 2025-12-05T19:28:39.534Z
Learning: In Bun's HMR runtime client (src/bake/hmr-runtime-client.ts), when handling error events where `event.error` is falsy, do not wrap `event.message` in a new Error object. Doing so would create misleading stack frames that show the error originating from the HMR client code rather than the actual error location, which hinders debugging.

Applied to files:

  • packages/bun-debug-adapter-protocol/src/debugger/adapter.ts
📚 Learning: 2025-11-03T20:40:59.655Z
Learnt from: pfgithub
Repo: oven-sh/bun PR: 24273
File: src/bun.js/bindings/JSValue.zig:545-586
Timestamp: 2025-11-03T20:40:59.655Z
Learning: In Bun's Zig codebase, JSErrors (returned as `bun.JSError!T`) must always be properly handled. Using `catch continue` or `catch { break; }` to silently suppress JSErrors is a bug. Errors should either be explicitly handled or propagated with `try`. This applies to snapshot serializer error handling where Jest's behavior is to throw when serializers throw.

Applied to files:

  • packages/bun-debug-adapter-protocol/src/debugger/adapter.ts
📚 Learning: 2025-09-20T00:57:56.685Z
Learnt from: markovejnovic
Repo: oven-sh/bun PR: 22568
File: test/js/valkey/valkey.test.ts:268-276
Timestamp: 2025-09-20T00:57:56.685Z
Learning: For test/js/valkey/valkey.test.ts, do not comment on synchronous throw assertions for async Redis methods like ctx.redis.set() - the maintainer has explicitly requested to stop looking at this error pattern.

Applied to files:

  • packages/bun-debug-adapter-protocol/src/debugger/adapter.ts
📚 Learning: 2025-11-24T18:37:30.259Z
Learnt from: CR
Repo: oven-sh/bun PR: 0
File: test/CLAUDE.md:0-0
Timestamp: 2025-11-24T18:37:30.259Z
Learning: Applies to test/**/*.test.{ts,js,jsx,tsx,mjs,cjs} : Always check exit codes and test error scenarios in error tests

Applied to files:

  • packages/bun-debug-adapter-protocol/src/debugger/adapter.ts
🧬 Code graph analysis (1)
packages/bun-debug-adapter-protocol/src/debugger/adapter.ts (1)
packages/bun-debug-adapter-protocol/src/debugger/signal.ts (4)
  • url (60-62)
  • url (184-186)
  • UnixSignal (21-77)
  • TCPSocketSignal (132-201)
🔇 Additional comments (22)
packages/bun-debug-adapter-protocol/src/debugger/adapter.ts (22)

26-65: LGTM! SSRF protection is correctly implemented.

The URL validation properly restricts WebSocket connections to localhost or Unix sockets, preventing SSRF attacks. The hostname check correctly uses parsed.hostname which automatically handles IPv6 addresses without brackets.


122-122: LGTM! Capability correctly enabled.

The supportsRestartRequest capability is now enabled, consistent with the new restart() method implementation for attach mode.


183-191: LGTM! Type extension is well-documented.

The restart option is properly typed and documented, clearly explaining the attach-mode-only behavior and timeout semantics.


313-313: LGTM! Race condition tracking is correctly implemented.

The #removingBreakpoints Set properly tracks breakpoints being removed to prevent race conditions where breakpointResolved events arrive during removal.


2241-2288: LGTM! Breakpoint data collection correctly handles URL extraction.

The collectBreakpointData() method properly extracts URLs from breakpointIds by splitting on colons and taking all but the last two parts (line/column). This correctly handles Windows paths and URLs that contain colons.


2225-2239: LGTM! Paused state cleanup is correct.

The clearPausedState() method properly checks if the debugger is stopped before clearing state and emitting the continued event, preventing spurious events.


2290-2314: LGTM! Stale state is comprehensively cleared.

The clearStaleState() method correctly clears all session-specific state including sources (with old scriptIds/sourcemaps), breakpoints, and other connection state. The critical comment about clearing sources is appropriate.


752-810: LGTM! Race condition handling and breakpoint cleanup are correctly implemented.

The proactive removal of stale breakpoints (lines 752-764) prevents conflicts from cached breakpoints across sessions. The isBreakpointExistsError handling (line 780) correctly manages race conditions when multiple requests create the same breakpoint. The use of setImmediate (line 801) properly defers resolution to allow I/O events to be processed.


823-877: LGTM! Duplicate breakpoint handling is robust.

The generated-line tracking (lines 825-837) correctly prevents duplicate breakpoints when VS Code sends multiple requests for the same line during reconnection. The race condition fallback (lines 849-855) appropriately reuses existing breakpoints created by parallel requests.


951-969: LGTM! Race condition protection and cleanup are correct.

Marking the breakpoint as "removing" before the async operation (line 954) prevents race conditions where breakpointResolved fires before removal completes. The try-finally block (lines 956-968) ensures cleanup always happens, even on failure.


975-1017: LGTM! Parallel breakpoint removal is efficient and safe.

The method correctly collects both future and resolved breakpoints for a URL, then removes them in parallel for better performance. Race condition protection (lines 999-1000) and try-finally cleanup (lines 1009-1014) ensure robustness.


1474-1481: LGTM! Race condition prevention in breakpointResolved is correct.

Ignoring breakpointResolved events for breakpoints currently being removed (line 1476) prevents race conditions where events arrive before removal completes.


2387-2413: LGTM! Reconnection state and constants are well-designed.

The reconnection state fields and constants are appropriately scoped and documented. The timeout values (10s default, 5 min max) and exponential backoff parameters (1s → 10s with 1.5x multiplier) provide reasonable retry behavior. The VS Code coordination delay (200ms) allows proper breakpoint state synchronization.


2430-2441: LGTM! VS Code breakpoint tracking is correct.

The override properly tracks which paths VS Code sends breakpoints for during reconnection, enabling selective reapplication of only the paths VS Code didn't handle. The conditions (lines 2436-2437) correctly require reconnecting state, a valid path, and non-empty breakpoints.


2443-2570: LGTM! Reconnection logic is robust and well-structured.

The reconnection implementation correctly:

  • Guards against overlapping attempts (lines 2451-2453)
  • Limits total attempts to prevent infinite loops (lines 2456-2464)
  • Collects breakpoint data BEFORE clearing state (line 2473)
  • Clears stale state immediately to prevent VS Code from using stale sources (line 2478)
  • Waits for VS Code coordination before selective reapplication (line 2515)
  • Only reapplies breakpoints for paths VS Code didn't handle (lines 2517-2528)
  • Properly handles errors with cleanup (lines 2533-2549)
  • Always clears reconnecting flag in finally (lines 2551-2554)

2581-2609: LGTM! Exponential backoff reconnection is correctly implemented.

The reconnection loop properly implements exponential backoff with:

  • Timeout-bounded retry loop (line 2585)
  • Initial wait before each attempt (line 2587)
  • Exponential backoff with capped maximum (lines 2601-2605)
  • Proper error handling with debug logging (lines 2594-2598)
  • Clear failure indication via thrown error (line 2608)

The JSDoc (lines 2572-2580) accurately documents the DoS protection mechanisms.


2617-2632: LGTM! Restart method correctly handles attach failure.

The restart() method properly checks the connection result (lines 2626-2629) and throws a descriptive error if reconnection fails, addressing the previous review concern. Launch mode correctly no-ops since watchMode handles restarts automatically.


2422-2425: LGTM! Signal cleanup method is correctly implemented.

The #cleanupSignal() method properly closes the signal and clears the reference, preventing resource leaks.


2651-2654: LGTM! Signal cleanup in close() prevents resource leaks.

Calling #cleanupSignal() in close() ensures the signal handler is properly cleaned up when the adapter is closed.


2722-2737: LGTM! Signal cleanup prevents leaks before creating new signal.

Closing any existing signal before creating a new one (line 2727) prevents resource leaks if the adapter is reused or restarted.


2883-2884: LGTM! URL validation correctly prevents SSRF attacks.

Calling validateWebSocketUrl(url) before attempting connection (line 2884) ensures SSRF protection by validating the URL is localhost or a Unix socket before any connection attempt.


3217-3250: LGTM! Error detection with debug logging is pragmatic.

The isBreakpointExistsError() helper appropriately uses string matching to detect breakpoint existence errors given the lack of structured error codes. The debug logging (lines 3241-3247) helps diagnose pattern matching issues without polluting production logs, addressing the previous review concern.


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Disabled knowledge base sources:

  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between ac0099e and bfcf87c.

📒 Files selected for processing (3)
  • packages/bun-debug-adapter-protocol/src/debugger/adapter.ts (19 hunks)
  • packages/bun-debug-adapter-protocol/src/debugger/signal.ts (2 hunks)
  • packages/bun-vscode/package.json (2 hunks)
🧰 Additional context used
🧠 Learnings (10)
📚 Learning: 2025-11-24T18:37:11.466Z
Learnt from: CR
Repo: oven-sh/bun PR: 0
File: src/js/CLAUDE.md:0-0
Timestamp: 2025-11-24T18:37:11.466Z
Learning: Applies to src/js/{builtins,node,bun,thirdparty,internal}/**/*.{ts,js} : Use `$debug()` for debug logging and `$assert()` for assertions; both are stripped in release builds

Applied to files:

  • packages/bun-debug-adapter-protocol/src/debugger/signal.ts
📚 Learning: 2025-11-24T18:37:11.466Z
Learnt from: CR
Repo: oven-sh/bun PR: 0
File: src/js/CLAUDE.md:0-0
Timestamp: 2025-11-24T18:37:11.466Z
Learning: Applies to src/js/{builtins,node,bun,thirdparty,internal}/**/*.{ts,js} : Use `process.platform` and `process.arch` for platform detection; these values are inlined and dead-code eliminated at build time

Applied to files:

  • packages/bun-debug-adapter-protocol/src/debugger/signal.ts
📚 Learning: 2025-10-17T20:50:58.644Z
Learnt from: taylordotfish
Repo: oven-sh/bun PR: 23755
File: src/bun.js/api/bun/socket/Handlers.zig:154-159
Timestamp: 2025-10-17T20:50:58.644Z
Learning: In Bun socket configuration error messages (src/bun.js/api/bun/socket/Handlers.zig), use the user-facing JavaScript names "data" and "drain" instead of internal field names "onData" and "onWritable", as these are the names users see in the API according to SocketConfig.bindv2.ts.

Applied to files:

  • packages/bun-debug-adapter-protocol/src/debugger/signal.ts
📚 Learning: 2025-11-24T18:35:08.612Z
Learnt from: CR
Repo: oven-sh/bun PR: 0
File: .cursor/rules/dev-server-tests.mdc:0-0
Timestamp: 2025-11-24T18:35:08.612Z
Learning: Applies to test/bake/**/*.test.ts : Use `dev.write()`, `dev.patch()`, and `dev.delete()` to mutate the filesystem instead of `node:fs` APIs, as dev server functions are hooked to wait for hot-reload and notify clients

Applied to files:

  • packages/bun-debug-adapter-protocol/src/debugger/signal.ts
📚 Learning: 2025-09-12T18:16:50.754Z
Learnt from: RiskyMH
Repo: oven-sh/bun PR: 22606
File: src/glob/GlobWalker.zig:449-452
Timestamp: 2025-09-12T18:16:50.754Z
Learning: For Bun codebase: prefer using `std.fs.path.sep` over manual platform separator detection, and use `bun.strings.lastIndexOfChar` instead of `std.mem.lastIndexOfScalar` for string operations.

Applied to files:

  • packages/bun-debug-adapter-protocol/src/debugger/signal.ts
📚 Learning: 2025-11-24T18:37:11.466Z
Learnt from: CR
Repo: oven-sh/bun PR: 0
File: src/js/CLAUDE.md:0-0
Timestamp: 2025-11-24T18:37:11.466Z
Learning: Applies to src/js/{builtins,node,bun,thirdparty,internal}/**/*.{ts,js} : Use private globals and methods with `$` prefix (e.g., `$Array`, `map.$set()`) instead of public JavaScript globals

Applied to files:

  • packages/bun-debug-adapter-protocol/src/debugger/signal.ts
📚 Learning: 2025-12-02T05:59:51.485Z
Learnt from: CR
Repo: oven-sh/bun PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-02T05:59:51.485Z
Learning: Applies to test/**/*.test.{ts,tsx} : Always use `port: 0` when spawning servers in tests - do not hardcode ports or use custom random port functions

Applied to files:

  • packages/bun-debug-adapter-protocol/src/debugger/signal.ts
📚 Learning: 2025-11-20T19:51:32.288Z
Learnt from: markovejnovic
Repo: oven-sh/bun PR: 24880
File: packages/bun-vscode/package.json:382-385
Timestamp: 2025-11-20T19:51:32.288Z
Learning: In the Bun repository, dependencies may be explicitly added to package.json files (even when not directly imported in code) to force version upgrades on transitive dependencies, particularly as part of Aikido security scanner remediation to ensure vulnerable transitive dependencies resolve to patched versions.

Applied to files:

  • packages/bun-vscode/package.json
📚 Learning: 2025-10-19T02:44:46.354Z
Learnt from: theshadow27
Repo: oven-sh/bun PR: 23798
File: packages/bun-otel/context-propagation.test.ts:1-1
Timestamp: 2025-10-19T02:44:46.354Z
Learning: In the Bun repository, standalone packages under packages/ (e.g., bun-vscode, bun-inspector-protocol, bun-plugin-yaml, bun-plugin-svelte, bun-debug-adapter-protocol, bun-otel) co-locate their tests with package source code using *.test.ts files. This follows standard npm/monorepo patterns. The test/ directory hierarchy (test/js/bun/, test/cli/, test/js/node/) is reserved for testing Bun's core runtime APIs and built-in functionality, not standalone packages.

Applied to files:

  • packages/bun-vscode/package.json
📚 Learning: 2025-11-24T18:37:30.259Z
Learnt from: CR
Repo: oven-sh/bun PR: 0
File: test/CLAUDE.md:0-0
Timestamp: 2025-11-24T18:37:30.259Z
Learning: Applies to test/**/*.test.{ts,js,jsx,tsx,mjs,cjs} : Always check exit codes and test error scenarios in error tests

Applied to files:

  • packages/bun-debug-adapter-protocol/src/debugger/adapter.ts
🧬 Code graph analysis (1)
packages/bun-debug-adapter-protocol/src/debugger/adapter.ts (1)
packages/bun-debug-adapter-protocol/src/debugger/signal.ts (4)
  • url (60-62)
  • url (187-189)
  • UnixSignal (21-77)
  • TCPSocketSignal (135-204)
🔇 Additional comments (16)
packages/bun-vscode/package.json (1)

280-288: LGTM! Schema addition for restart option is well-defined.

The restart property correctly supports both boolean (for default 10s timeout) and number (for custom timeout in ms), matching the protocol-level implementation. The description clearly explains the behavior.

packages/bun-debug-adapter-protocol/src/debugger/signal.ts (1)

107-124: LGTM! Path parsing and validation are correctly integrated.

The function properly handles both absolute paths and URL-formatted paths, and validates all paths before returning.

packages/bun-debug-adapter-protocol/src/debugger/adapter.ts (14)

26-65: LGTM! Solid SSRF protection for WebSocket URLs.

The validation correctly:

  • Allows Unix sockets (ws+unix://)
  • Restricts to ws:// and wss:// protocols
  • Limits connections to localhost variants only

The exclusion of 0.0.0.0 is appropriate since it binds to all interfaces and could be exploited.


183-192: LGTM! Well-documented restart option with appropriate type.

The JSDoc clearly explains the different modes and includes important notes about attach-only applicability.


951-969: LGTM! Proper race condition handling with try/finally.

The #removingBreakpoints tracking correctly prevents stale breakpointResolved events from re-adding breakpoints that are being removed. The try/finally ensures cleanup even if removal fails.


971-1017: LGTM! Efficient parallel breakpoint removal with proper cleanup.

The Promise.all() approach improves performance, and the #removingBreakpoints tracking ensures race-condition safety. The try/finally pattern guarantees state cleanup.


752-810: LGTM! Robust handling of stale and duplicate breakpoints.

The implementation properly handles:

  1. Stale breakpoints from previous sessions via proactive cleanup
  2. Race conditions with isBreakpointExistsError fallback
  3. Manual breakpointResolved triggering via process.nextTick when the script was already parsed

The comment at line 797-799 explains the edge case clearly.


2249-2288: LGTM! Breakpoint data collection handles URL parsing correctly.

The URL extraction logic at lines 2262-2264 correctly handles colons in paths (Windows) and URLs with ports by taking everything except the last two colon-separated parts (line and column).


2393-2413: LGTM! Well-defined reconnection constants with DoS protection.

The constants provide sensible defaults:

  • 10 max attempts prevents infinite loops
  • 5-minute max timeout caps total reconnection duration
  • Exponential backoff (1s → 10s max with 1.5x multiplier) rate-limits attempts
  • 200ms VS Code coordination delay allows proper breakpoint synchronization

2574-2602: LGTM! Exponential backoff with proper timeout handling.

The implementation correctly:

  • Waits before attempting (giving the server time to restart)
  • Uses exponential backoff capped at 10s
  • Terminates when the overall timeout is reached

2870-2885: LGTM! URL validation correctly guards the attach entry point.

The validateWebSocketUrl call at line 2874 ensures all attach operations (initial, restart, reconnect) are protected against SSRF.


3207-3219: LGTM! Robust error detection for breakpoint conflicts.

The case-insensitive check with multiple pattern variants handles different error message formats that the debugger might return.


2296-2314: LGTM! Comprehensive stale state cleanup.

Clearing all connection-specific state (sources, stack frames, breakpoint IDs, targets, variables) before reconnection prevents stale data from causing issues with the new session.


2430-2441: LGTM! Smart coordination with VS Code's breakpoint handling.

This tracking mechanism prevents duplicate breakpoint application by detecting when VS Code has already handled breakpoints during the reconnection window.


2419-2425: LGTM! Proper signal resource cleanup.

The #cleanupSignal() helper ensures signals are properly closed and dereferenced, preventing resource leaks when sessions restart or terminate.


2443-2563: LGTM! Well-structured reconnection flow with comprehensive error handling.

The handler correctly:

  1. Guards against overlapping reconnections and max attempts
  2. Preserves breakpoint data before clearing state
  3. Updates VS Code UI immediately (clearing paused state)
  4. Uses background reconnection with proper .then/.catch/.finally
  5. Coordinates with VS Code's breakpoint handling before reapplying

The state management order (collect → clear → reconnect → reapply) prevents race conditions.

- Handle #attach failure in restart() by throwing error when connection fails
- Remove redundant '..' path traversal check since normalize() already resolves parent directory references

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 4

📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Disabled knowledge base sources:

  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between bfcf87c and 8ca4ddd.

📒 Files selected for processing (2)
  • packages/bun-debug-adapter-protocol/src/debugger/adapter.ts (19 hunks)
  • packages/bun-debug-adapter-protocol/src/debugger/signal.ts (2 hunks)
🧰 Additional context used
🧠 Learnings (8)
📚 Learning: 2025-11-24T18:37:11.466Z
Learnt from: CR
Repo: oven-sh/bun PR: 0
File: src/js/CLAUDE.md:0-0
Timestamp: 2025-11-24T18:37:11.466Z
Learning: Applies to src/js/{builtins,node,bun,thirdparty,internal}/**/*.{ts,js} : Use `$debug()` for debug logging and `$assert()` for assertions; both are stripped in release builds

Applied to files:

  • packages/bun-debug-adapter-protocol/src/debugger/signal.ts
📚 Learning: 2025-10-17T20:50:58.644Z
Learnt from: taylordotfish
Repo: oven-sh/bun PR: 23755
File: src/bun.js/api/bun/socket/Handlers.zig:154-159
Timestamp: 2025-10-17T20:50:58.644Z
Learning: In Bun socket configuration error messages (src/bun.js/api/bun/socket/Handlers.zig), use the user-facing JavaScript names "data" and "drain" instead of internal field names "onData" and "onWritable", as these are the names users see in the API according to SocketConfig.bindv2.ts.

Applied to files:

  • packages/bun-debug-adapter-protocol/src/debugger/signal.ts
📚 Learning: 2025-11-24T18:37:11.466Z
Learnt from: CR
Repo: oven-sh/bun PR: 0
File: src/js/CLAUDE.md:0-0
Timestamp: 2025-11-24T18:37:11.466Z
Learning: Applies to src/js/{builtins,node,bun,thirdparty,internal}/**/*.{ts,js} : Use `process.platform` and `process.arch` for platform detection; these values are inlined and dead-code eliminated at build time

Applied to files:

  • packages/bun-debug-adapter-protocol/src/debugger/signal.ts
📚 Learning: 2025-11-24T18:35:08.612Z
Learnt from: CR
Repo: oven-sh/bun PR: 0
File: .cursor/rules/dev-server-tests.mdc:0-0
Timestamp: 2025-11-24T18:35:08.612Z
Learning: Applies to test/bake/**/*.test.ts : Use `dev.write()`, `dev.patch()`, and `dev.delete()` to mutate the filesystem instead of `node:fs` APIs, as dev server functions are hooked to wait for hot-reload and notify clients

Applied to files:

  • packages/bun-debug-adapter-protocol/src/debugger/signal.ts
📚 Learning: 2025-10-19T04:55:33.099Z
Learnt from: theshadow27
Repo: oven-sh/bun PR: 23798
File: test/js/bun/http/node-telemetry.test.ts:27-203
Timestamp: 2025-10-19T04:55:33.099Z
Learning: In test/js/bun/http/node-telemetry.test.ts and the Bun.telemetry._node_binding API, after the architecture refactor, the _node_binding interface only contains two methods: handleIncomingRequest(req, res) and handleWriteHead(res, statusCode). The handleRequestFinish hook and other lifecycle hooks were removed during simplification. Both current methods are fully tested.

Applied to files:

  • packages/bun-debug-adapter-protocol/src/debugger/signal.ts
📚 Learning: 2025-09-12T18:16:50.754Z
Learnt from: RiskyMH
Repo: oven-sh/bun PR: 22606
File: src/glob/GlobWalker.zig:449-452
Timestamp: 2025-09-12T18:16:50.754Z
Learning: For Bun codebase: prefer using `std.fs.path.sep` over manual platform separator detection, and use `bun.strings.lastIndexOfChar` instead of `std.mem.lastIndexOfScalar` for string operations.

Applied to files:

  • packages/bun-debug-adapter-protocol/src/debugger/signal.ts
📚 Learning: 2025-12-02T05:59:51.485Z
Learnt from: CR
Repo: oven-sh/bun PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-02T05:59:51.485Z
Learning: Applies to test/**/*.test.{ts,tsx} : Always use `port: 0` when spawning servers in tests - do not hardcode ports or use custom random port functions

Applied to files:

  • packages/bun-debug-adapter-protocol/src/debugger/signal.ts
📚 Learning: 2025-11-24T18:37:30.259Z
Learnt from: CR
Repo: oven-sh/bun PR: 0
File: test/CLAUDE.md:0-0
Timestamp: 2025-11-24T18:37:30.259Z
Learning: Applies to test/**/*.test.{ts,js,jsx,tsx,mjs,cjs} : Always check exit codes and test error scenarios in error tests

Applied to files:

  • packages/bun-debug-adapter-protocol/src/debugger/adapter.ts
🧬 Code graph analysis (1)
packages/bun-debug-adapter-protocol/src/debugger/adapter.ts (1)
packages/bun-debug-adapter-protocol/src/debugger/signal.ts (4)
  • url (60-62)
  • url (184-186)
  • UnixSignal (21-77)
  • TCPSocketSignal (132-201)
🔇 Additional comments (13)
packages/bun-debug-adapter-protocol/src/debugger/signal.ts (2)

83-102: LGTM! Path validation is secure and well-implemented.

The Unix socket path validation correctly prevents path traversal by normalizing the path and ensuring it starts with the temp directory. The comment helpfully explains that normalize() resolves .. segments, making the startsWith check sufficient.


104-121: LGTM! Socket path validation is correctly integrated.

The validation is properly placed after parsing the socket path from either a string or URL. The error handling provides clear feedback when validation fails.

packages/bun-debug-adapter-protocol/src/debugger/adapter.ts (11)

822-881: Consider verifying breakpoint options match when reusing existing breakpoints.

When detecting duplicate requests for the same generated line (lines 834-837), the code reuses the existing breakpoint without verifying that the breakpoint options (condition, hitCondition, logMessage) match. If VS Code sends multiple requests for the same line with different conditions due to a race, reusing the first breakpoint may not reflect the intended configuration.

However, this is likely acceptable since:

  1. VS Code typically sends a single consolidated request per file
  2. The race condition this handles is internal (parallel processing of the same request)
  3. Subsequent setBreakpointsRequest calls will replace all breakpoints anyway

If you want to add defensive validation, consider logging a warning in debug mode when options differ.


971-1017: LGTM! Parallel breakpoint removal is well-implemented.

The use of Promise.all() for parallel removal improves performance. The try/finally blocks ensure cleanup even on errors, and the #removingBreakpoints Set correctly prevents race conditions where breakpointResolved fires during removal.


2565-2602: LGTM! Exponential backoff with DoS protection is well-implemented.

The reconnection logic correctly implements exponential backoff with rate limiting. The DoS protections listed in the JSDoc (lines 2567-2572) are all properly enforced. The initial delay before the first attempt (line 2580) is appropriate to give the server time to restart.


2604-2625: LGTM! Previous issue with unchecked #attach return value has been addressed.

The restart method now properly checks if the connection succeeded and throws an error if it fails (lines 2620-2622). This ensures the session doesn't remain in a broken state.


2427-2441: LGTM! VS Code breakpoint coordination is correctly implemented.

The override correctly tracks paths that VS Code actively manages during reconnection by checking for non-empty breakpoint arrays (line 2436). This allows selective reapplication of breakpoints for paths VS Code doesn't resend.


2873-2888: LGTM! URL validation prevents SSRF attacks.

The addition of validateWebSocketUrl(url) before connection attempts (line 2877) properly enforces the security policy. The retry logic with backoff (lines 2879-2885) improves connection reliability.


2290-2314: LGTM! Thorough state cleanup prevents stale data issues.

The emphasis on clearing sources (lines 2297-2300) is appropriate since stale scriptIds and source maps would cause breakpoint failures after reconnection. Clearing all connection-specific state while preserving breakpoint request data for reapplication is the correct approach.


2316-2337: LGTM! Breakpoint reapplication handles both loaded and unloaded sources.

The try/catch (lines 2328-2335) correctly handles the case where sources aren't loaded yet. The comment (lines 2323-2326) clearly explains how placeholder breakpoints at line 0 will be resolved via Debugger.scriptParsed and Debugger.breakpointResolved events.


2225-2239: LGTM! Clearing paused state improves reconnection UX.

Emitting the continued event (lines 2234-2237) ensures VS Code updates its UI when the connection is lost while paused. The allThreadsContinued: true flag is appropriate since the adapter uses a single-threaded model.


2419-2425: LGTM! Signal cleanup prevents resource leaks.

The #cleanupSignal() method properly releases resources by closing the signal and clearing the reference. Calling it before creating new signals (lines 2720, 2758) and in close() (line 2646) ensures no signal handlers accumulate.

Also applies to: 2646-2646, 2720-2720, 2729-2729, 2757-2757, 2767-2767


2241-2288: The breakpointId parsing should validate line and column numbers before extracting the URL.

Lines 2262-2264 split by ":" and blindly assume the last two parts are line and column numbers. This is fragile and could produce incorrect URLs for formats like file:///C:/path/file.js:10:5. Instead, validate that the last two parts are numeric before reconstructing the URL, or use a more robust parsing method (e.g., regex that captures trailing :(\d+):(\d+)$).

This commit resolves 6 actionable review comments from CodeRabbit:

1. **Remove invalid IPv6 host format**: Removed "[::1]" from allowedHosts array since parsed.hostname returns IPv6 addresses without brackets (e.g., "::1") per WHATWG URL standard.

2. **Remove redundant path traversal check**: Eliminated the normalizedPath.includes("..") check in validateUnixSocketPath since path.normalize() already resolves ".." segments. The startsWith(tempDir) check provides sufficient security.

3. **Handle attach failures in restart()**: Added error handling to check the boolean return value from #attach() and throw a descriptive error when reconnection fails, preventing silent failures that leave the session in a broken state.

4. **Use setImmediate for deferred breakpoint resolution**: Changed from process.nextTick() to setImmediate() to ensure the callback runs after I/O events (including WebSocket messages with Debugger.scriptParsed events) are processed, allowing proper event loop timing.

5. **Implement selective breakpoint reapplication**: Enhanced reconnection logic to reapply only breakpoints for paths NOT handled by VS Code during reconnection, instead of the previous all-or-nothing approach. This preserves breakpoints for files VS Code didn't resend while respecting VS Code's explicit handling of other files.

6. **Add debug logging to isBreakpointExistsError**: Added comprehensive debug logging (gated by isDebug flag) to track which error patterns match or don't match, providing diagnostic information for future debugger protocol changes without affecting production behavior.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
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