Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions .release-please-manifest.json
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
{
".": "3.21.0"
}
".": "3.21.1"
}
8 changes: 8 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,13 @@
# Changelog

## 3.21.1 (2026-10-23)

Full Changelog: [v3.21.0...v3.21.1](https://github.com/browserbase/stagehand-ruby/compare/v3.21.0...v3.21.1)

### Bug Fixes

* **security:** default local-server downloads to [server v3.7.7](https://github.com/browserbase/stagehand/releases/tag/stagehand-server-v3/v3.7.7), which includes Node.js 24.22.1 and Undici 7.30.1 with the October 2026 security fixes. Upgrade the gem and restart applications to download the patched binary into a new gem-versioned cache directory. Explicit `STAGEHAND_SERVER_VERSION` and `STAGEHAND_SEA_BINARY` overrides remain supported; update any override that selects an older server or custom binary separately.

## 3.21.0 (2026-05-27)

Full Changelog: [v3.20.0...v3.21.0](https://github.com/browserbase/stagehand-ruby/compare/v3.20.0...v3.21.0)
Expand Down
2 changes: 1 addition & 1 deletion Gemfile.lock
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ GIT
PATH
remote: .
specs:
stagehand (3.21.0)
stagehand (3.21.1)
cgi
connection_pool

Expand Down
4 changes: 3 additions & 1 deletion lib/stagehand/local.rb
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ module Stagehand
module Local
STAGEHAND_REPO = "browserbase/stagehand"
DEFAULT_USER_AGENT = "stagehand-ruby/local"
# Includes the October 2026 Node.js and Undici security fixes.
DEFAULT_SERVER_VERSION = "stagehand-server-v3/v3.7.7"
DEFAULT_HOST = "127.0.0.1"
DEFAULT_READY_TIMEOUT_S = 30.0
DOWNLOAD_TIMEOUT_S = 600
Expand Down Expand Up @@ -101,7 +103,7 @@ def resolve_binary_path
return ensure_executable(cache_path) if File.file?(cache_path)

version = ENV["STAGEHAND_SERVER_VERSION"].to_s
version = "latest" if version.empty?
version = DEFAULT_SERVER_VERSION if version.empty?
tag = resolve_version(version)
download_binary(tag, cache_path)
end
Expand Down
2 changes: 1 addition & 1 deletion lib/stagehand/version.rb
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
# frozen_string_literal: true

module Stagehand
VERSION = "3.21.0"
VERSION = "3.21.1"
end
53 changes: 49 additions & 4 deletions test/stagehand/local_binary_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,13 @@ def setup
@old_home = Dir.home if ENV.key?("HOME")
@old_xdg = ENV["XDG_CACHE_HOME"]
@old_local = ENV["LOCALAPPDATA"]
@old_server_version = ENV["STAGEHAND_SERVER_VERSION"]
@old_sea_binary = ENV["STAGEHAND_SEA_BINARY"]
ENV["HOME"] = @tmp_home
ENV.delete("XDG_CACHE_HOME")
ENV.delete("LOCALAPPDATA")
ENV.delete("STAGEHAND_SERVER_VERSION")
ENV.delete("STAGEHAND_SEA_BINARY")
WebMock.enable!
WebMock.disable_net_connect!(allow_localhost: true)
end
Expand All @@ -33,6 +36,8 @@ def teardown
end
ENV["XDG_CACHE_HOME"] = @old_xdg
ENV["LOCALAPPDATA"] = @old_local
ENV["STAGEHAND_SERVER_VERSION"] = @old_server_version
ENV["STAGEHAND_SEA_BINARY"] = @old_sea_binary
FileUtils.remove_entry(@tmp_home) if @tmp_home && File.directory?(@tmp_home)
WebMock.reset!
WebMock.allow_net_connect!
Expand All @@ -54,7 +59,50 @@ def test_uses_cache_without_network
assert_equal(cache_path, path)
end

def test_default_downloads_patched_version_ignoring_old_cache
cache_dir = Stagehand::Local::Binary.cache_dir
filename = Stagehand::Local::Binary.binary_filename
old_path = File.join(File.dirname(cache_dir), "ruby_3.21.0", filename)
FileUtils.mkdir_p(File.dirname(old_path))
File.binwrite(old_path, "old binary")

url = "https://github.com/browserbase/stagehand/releases/download/stagehand-server-v3/v3.7.7/#{filename}"
stub_request(:get, url).to_return(status: 200, body: "patched binary")

path = Stagehand::Local::Binary.resolve_binary_path

assert_equal(File.join(cache_dir, filename), path)
assert_equal("patched binary", File.binread(path))
assert_equal("old binary", File.binread(old_path))
assert_requested(:get, url, times: 1)
end

def test_downloads_explicit_server_version
ENV["STAGEHAND_SERVER_VERSION"] = "v1.2.3"
filename = Stagehand::Local::Binary.binary_filename
url = "https://github.com/browserbase/stagehand/releases/download/stagehand-server-v3/v1.2.3/#{filename}"
stub_request(:get, url).to_return(status: 200, body: "selected binary")

path = Stagehand::Local::Binary.resolve_binary_path

assert_equal("selected binary", File.binread(path))
assert_requested(:get, url, times: 1)
end

def test_uses_custom_binary_without_network
custom_path = File.join(@tmp_home, "custom-server")
File.binwrite(custom_path, "custom binary")
ENV["STAGEHAND_SEA_BINARY"] = custom_path

path = Stagehand::Local::Binary.resolve_binary_path

assert_equal(custom_path, path)
assert_equal("custom binary", File.binread(path))
assert_not_requested(:get, /github\.com/)
end

def test_downloads_latest_tag_when_missing
ENV["STAGEHAND_SERVER_VERSION"] = "latest"
filename = Stagehand::Local::Binary.binary_filename

stub_request(:get, "https://api.github.com/repos/browserbase/stagehand/releases?per_page=15")
Expand All @@ -72,10 +120,7 @@ def test_downloads_latest_tag_when_missing
def test_download_error_includes_manual_hint
filename = Stagehand::Local::Binary.binary_filename

stub_request(:get, "https://api.github.com/repos/browserbase/stagehand/releases?per_page=15")
.to_return(status: 200, body: [{tag_name: "stagehand-server-v3/v1.2.3"}].to_json)

stub_request(:get, "https://github.com/browserbase/stagehand/releases/download/stagehand-server-v3/v1.2.3/#{filename}")
stub_request(:get, "https://github.com/browserbase/stagehand/releases/download/stagehand-server-v3/v3.7.7/#{filename}")
.to_return(status: 404, body: "not found")

err = assert_raises(RuntimeError) { Stagehand::Local::Binary.resolve_binary_path }
Expand Down
Loading