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
13 changes: 11 additions & 2 deletions sentry-ruby/lib/sentry/transport/debug_transport.rb
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,11 @@ def send_event(event)
backend.send_event(event)
end

def send_envelope(envelope)
log_envelope(envelope)
backend.send_envelope(envelope)

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Bug: The logged_envelopes method can crash with an unhandled JSON::ParserError due to concurrent writes to the debug log file corrupting JSON data.
Severity: HIGH

Suggested Fix

Implement file locking using File#flock around the write operation to ensure atomic writes. Additionally, wrap the JSON.parse(line) call within the logged_envelopes method in a begin/rescue JSON::ParserError block to gracefully handle any corrupted lines and prevent crashes.

Prompt for AI Agent
Review the code at the location below. A potential bug has been identified by an AI
agent. Verify if this is a real issue. If it is, propose a fix; if not, explain why it's
not valid.

Location: sentry-ruby/lib/sentry/transport/debug_transport.rb#L32

Potential issue: The `DebugTransport` writes to a log file using `File.open(log_file,
"a")` without any file locking. In multi-threaded environments, such as with SolidQueue
workers, concurrent writes can corrupt the log file, especially with large payloads. The
`logged_envelopes` method reads these logs and attempts to parse each line with
`JSON.parse` but lacks a `rescue` block. If a line is corrupted due to interleaved
writes, `JSON.parse` will raise an unhandled `JSON::ParserError`, causing the method to
crash. The existing `fully_written?` check is insufficient as it only verifies a
trailing newline and does not prevent JSON corruption from race conditions.

Also affects:

  • sentry-ruby/lib/sentry/transport/debug_transport.rb:64~64

Did we get this right? 👍 / 👎 to inform future reviews.

end

def log_envelope(envelope)
envelope_json = {
timestamp: Time.now.utc.iso8601,
Expand All @@ -36,13 +41,13 @@ def log_envelope(envelope)
end
}

File.open(log_file, "a") { |file| file << JSON.dump(envelope_json) << "\n" }
File.open(log_file, "a") { |file| file.write("#{JSON.dump(envelope_json)}\n") }
end

def logged_envelopes
return [] unless File.exist?(log_file)

File.readlines(log_file).map do |line|
File.readlines(log_file).select { |line| fully_written?(line) }.map do |line|
JSON.parse(line)
end
end
Expand All @@ -54,6 +59,10 @@ def clear

private

def fully_written?(line)
line.end_with?("\n")
end

def initialize_backend(configuration)
backend = configuration.dsn.local? ? DummyTransport : HTTPTransport
backend.new(configuration)
Expand Down
27 changes: 27 additions & 0 deletions sentry-ruby/spec/sentry/transport/debug_transport_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,12 @@
context "with local DSN for testing" do
let(:dsn) { Sentry::TestHelper::DUMMY_DSN }

def build_envelope(body)
Sentry::Envelope.new(sent_at: Sentry.utc_now.iso8601).tap do |envelope|
envelope.add_item({ type: "log" }, { items: [{ body: body }] })
end
end

describe ".capture_exception with debug transport" do
it "logs envelope data and stores an event internally" do
Sentry.capture_exception(error)
Expand All @@ -37,6 +43,27 @@
expect(payload["exception"]["values"].first["value"]).to include("test error")
end
end

describe "#send_envelope" do
it "logs the envelope and passes it on to the backend" do
transport.send_envelope(build_envelope("hello"))

expect(transport.backend.envelopes.count).to be(1)
expect(transport.logged_envelopes.last.dig("items", 0, "payload", "items", 0, "body")).to eq("hello")
end
end

describe "#logged_envelopes" do
it "skips a last line that is still being written" do
transport.clear
transport.send_envelope(build_envelope("complete"))
File.open(transport.log_file, "a") { |file| file.write('{"timestamp":"2026-10-06T09:08:2') }

bodies = transport.logged_envelopes.map { |envelope| envelope.dig("items", 0, "payload", "items", 0, "body") }

expect(bodies).to eq(["complete"])
end
end
end

context "with a real DSN for testing" do
Expand Down
Loading