Skip to content
Draft

wololo! #3056

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
12 changes: 12 additions & 0 deletions sentry-ruby/lib/sentry-ruby.rb
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
require "sentry/threaded_periodic_worker"
require "sentry/session_flusher"
require "sentry/backpressure_monitor"
require "sentry/wololo"
require "sentry/cron/monitor_check_ins"
require "sentry/vernier/profiler"
require "sentry/metrics"
Expand Down Expand Up @@ -59,6 +60,7 @@
logger
session_flusher
backpressure_monitor
wololo
exception_locals_tp
].freeze

Expand Down Expand Up @@ -92,6 +94,10 @@
# @return [BackpressureMonitor, nil]
attr_reader :backpressure_monitor

# @!attribute [r] wololo
# @return [Wololo, nil]
attr_reader :wololo

##### Patch Registration #####

# @!visibility private
Expand Down Expand Up @@ -277,6 +283,7 @@
@background_worker = Sentry::BackgroundWorker.new(config)
@session_flusher = config.session_tracking? ? Sentry::SessionFlusher.new(config, client) : nil
@backpressure_monitor = config.enable_backpressure_handling ? Sentry::BackpressureMonitor.new(config, client) : nil
@wololo = config.wololo ? Sentry::Wololo.new(config) : nil

Check failure on line 286 in sentry-ruby/lib/sentry-ruby.rb

View check run for this annotation

@sentry/warden / warden: security-review

Untrusted LLM patch output is evaluated as Ruby, enabling RCE

When Wololo is enabled, the remote LLM response is treated as Ruby source and evaluated in TOPLEVEL_BINDING without enforcing that it contains only method definitions. A response containing one valid method plus a top-level operation such as system, file access, or network access can therefore execute arbitrary code in the application process; request-controlled exception data is included in the LLM prompt and can enable prompt injection.

Check warning on line 286 in sentry-ruby/lib/sentry-ruby.rb

View check run for this annotation

@sentry/warden / warden: security-review

Opt-in Wololo sends unredacted application source and exception data to OpenRouter

When an application enables `config.wololo` and supplies an OpenRouter key, captured exceptions with in-app frames cause Wololo to send exception messages, backtraces, and up to 200 KB of local application source to OpenRouter. Source files and exception data may contain credentials or PII, and the request has no redaction or configurable filtering before crossing the application’s data boundary.

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Untrusted LLM patch output is evaluated as Ruby, enabling RCE

When Wololo is enabled, the remote LLM response is treated as Ruby source and evaluated in TOPLEVEL_BINDING without enforcing that it contains only method definitions. A response containing one valid method plus a top-level operation such as system, file access, or network access can therefore execute arbitrary code in the application process; request-controlled exception data is included in the LLM prompt and can enable prompt injection.

Evidence
  • Request exceptions captured by the Rack integration reach Hub#capture_exception, which calls Sentry.wololo&.record(exception) when the opt-in worker is enabled.
  • Wololo#request_patch embeds the exception message, backtrace, and source files in a prompt and accepts the remote chat.ask response as patch data.
  • apply_patches checks syntax, safe file paths, loaded targets, source locations, and a small forbidden method-name list, but does not enforce an AST-level method-only structure. A patch such as def existing; end\nsystem('...') can satisfy the target/source checks.
  • apply_prepend_patch interpolates that source into TOPLEVEL_BINDING.eval("Module.new {\n#{source}\n}", ...), executing the top-level operation immediately in the application process before prepending the module.
Also found at 4 additional locations
  • sentry-ruby/lib/sentry/hub.rb:174-174
  • sentry-ruby/lib/sentry/wololo.rb:91-99
  • sentry-ruby/lib/sentry/wololo.rb:386-388
  • sentry-ruby/lib/sentry/wololo.rb:426-501

Identified by Warden · security-review · NWG-8RS

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Opt-in Wololo sends unredacted application source and exception data to OpenRouter

When an application enables config.wololo and supplies an OpenRouter key, captured exceptions with in-app frames cause Wololo to send exception messages, backtraces, and up to 200 KB of local application source to OpenRouter. Source files and exception data may contain credentials or PII, and the request has no redaction or configurable filtering before crossing the application’s data boundary.

Evidence
  • Sentry::Hub#capture_exception calls Sentry.wololo&.record(exception) before normal event filtering, so captured exceptions—including messages containing caller-controlled request data—can enter the Wololo queue.
  • Wololo#issue_from and source_files collect the exception class, message, backtrace, and up to 10 source files under the configured project root and application directories, with a 200 KB total limit but no secret filtering.
  • Wololo#request_patch embeds those values in the prompt and sends it through RubyLLM.chat(provider: "openrouter").ask(prompt) using the configured OpenRouter API key.
  • Wololo is disabled by default, but enabling it is the explicit security boundary; the effective path contains no redaction or consent-specific data controls beyond that opt-in.

Identified by Warden · security-review · NKH-YZD

exception_locals_tp.enable if config.include_local_variables
at_exit { close }
end
Expand All @@ -297,6 +304,11 @@
@backpressure_monitor = nil
end

if @wololo
@wololo.kill
@wololo = nil
end

if client = get_current_client
client.configuration.run_after_close_callbacks
client.flush
Expand Down
13 changes: 12 additions & 1 deletion sentry-ruby/lib/sentry/configuration.rb
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,15 @@ class Configuration
# @return [Integer]
attr_accessor :background_worker_max_queue

# Enables the experimental Wololo exception repair worker. Wololo is
# disabled by default and requires the optional `ruby_llm` gem.
# @return [Boolean]
attr_accessor :wololo

# The OpenRouter API key used by Wololo and ruby_llm.
# @return [String, nil]
attr_accessor :wololo_openrouter_api_key

# a proc/lambda that takes an array of stack traces
# it'll be used to silence (reduce) backtrace of the exception
#
Expand Down Expand Up @@ -452,7 +461,7 @@ class Configuration

LOG_PREFIX = "** [Sentry] "
MODULE_SEPARATOR = "::"
SKIP_INSPECTION_ATTRIBUTES = [:@linecache, :@stacktrace_builder]
SKIP_INSPECTION_ATTRIBUTES = [:@linecache, :@stacktrace_builder, :@wololo_openrouter_api_key]

INSTRUMENTERS = [:sentry, :otel]

Expand Down Expand Up @@ -541,6 +550,8 @@ def initialize
self.debug = Sentry::Utils::EnvHelper.env_to_bool(ENV["SENTRY_DEBUG"])
self.background_worker_threads = (processor_count / 2.0).ceil
self.background_worker_max_queue = BackgroundWorker::DEFAULT_MAX_QUEUE
self.wololo = false
self.wololo_openrouter_api_key = nil
self.backtrace_cleanup_callback = nil
self.strip_backtrace_load_path = true
self.max_breadcrumbs = BreadcrumbBuffer::DEFAULT_SIZE
Expand Down
1 change: 1 addition & 0 deletions sentry-ruby/lib/sentry/hub.rb
Original file line number Diff line number Diff line change
Expand Up @@ -171,6 +171,7 @@

options[:hint] ||= {}
options[:hint][:exception] = exception
Sentry.wololo&.record(exception)

Check failure on line 174 in sentry-ruby/lib/sentry/hub.rb

View check run for this annotation

@sentry/warden / warden: security-review

[NWG-8RS] Untrusted LLM patch output is evaluated as Ruby, enabling RCE (additional location)

When Wololo is enabled, the remote LLM response is treated as Ruby source and evaluated in TOPLEVEL_BINDING without enforcing that it contains only method definitions. A response containing one valid method plus a top-level operation such as system, file access, or network access can therefore execute arbitrary code in the application process; request-controlled exception data is included in the LLM prompt and can enable prompt injection.

event = current_client.event_from_exception(exception, options[:hint])

Expand Down
Loading
Loading