-
Notifications
You must be signed in to change notification settings - Fork 3
isolate eval call so it can't muck with local variables #17
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,15 +1,25 @@ | ||
| AllCops: | ||
| TargetRubyVersion: 2.4 | ||
| TargetRubyVersion: 3.1 | ||
| DisplayCopNames: true | ||
| NewCops: enable | ||
|
|
||
| Style/FrozenStringLiteralComment: | ||
| Enabled: true | ||
| plugins: | ||
| - rubocop-rake | ||
| - rubocop-rspec | ||
|
|
||
| Layout/LineLength: | ||
| Enabled: false | ||
| Metrics: | ||
| Enabled: false | ||
|
|
||
| RSpec/DescribedClass: | ||
| Enabled: false | ||
| RSpec/ExampleLength: | ||
| Enabled: false | ||
| RSpec/MultipleExpectations: | ||
| Enabled: false | ||
| Style/Documentation: | ||
| Enabled: false | ||
|
|
||
| Style/FrozenStringLiteralComment: | ||
| Enabled: true | ||
| Style/StringLiterals: | ||
| EnforcedStyle: double_quotes |
This file was deleted.
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -5,6 +5,30 @@ | |
| require "time" | ||
|
|
||
| module DebugSocket | ||
| module Commands | ||
| # When running `eval`, we don't want the input to overwrite local variables etc. `eval` runs in the current scope, | ||
| # so we have an empty scope here that runs in a module that only has other shortcut commands the client might want | ||
| # to run. | ||
| def self.isolated_eval(input) | ||
| eval(input) # rubocop:disable Security/Eval | ||
| # We rescue Exception here because the input could have SyntaxErrors etc. | ||
| rescue Exception => e # rubocop:disable Lint/RescueException | ||
| DebugSocket.logger&.error { "debug-socket-error=#{e.inspect} input=#{input.inspect} path=#{@path} backtrace=#{e.backtrace.inspect}" } | ||
| "#{e.class.name}: #{e.message}\n#{e.backtrace.join("\n")}" | ||
| end | ||
|
|
||
| # Print the backtrace for every Thread | ||
| def self.backtrace | ||
| pid = Process.pid | ||
| "#{Time.now.utc.iso8601} #{$PROGRAM_NAME}\n" + Thread.list.map do |thread| | ||
| output = "#{Time.now.utc.iso8601} pid=#{pid} thread.object_id=#{thread.object_id} thread.status=#{thread.status}" | ||
| backtrace = thread.backtrace || [] | ||
| output << "\n#{backtrace.join("\n")}" if backtrace | ||
| output | ||
| end.join("\n\n") | ||
| end | ||
| end | ||
|
|
||
| @thread = nil | ||
| @pid = Process.pid | ||
|
|
||
|
|
@@ -16,7 +40,7 @@ def self.logger | |
| return @logger if defined?(@logger) | ||
|
|
||
| require "logger" | ||
| @logger = Logger.new(STDERR) | ||
| @logger = Logger.new($stderr) | ||
| end | ||
|
|
||
| def self.start(path, &block) | ||
|
|
@@ -32,22 +56,27 @@ def self.start(path, &block) | |
|
|
||
| server = UNIXServer.new(@path) | ||
| @thread = Thread.new do | ||
| errors = 0 | ||
| loop do | ||
| begin | ||
| socket = server.accept | ||
| input = socket.read | ||
| logger&.warn("debug-socket-command=#{input.inspect}") | ||
|
|
||
| self.perform_audit(input, &block) if block | ||
|
|
||
| socket.puts(eval(input)) # rubocop:disable Security/Eval | ||
|
|
||
| rescue Exception => e # rubocop:disable Lint/RescueException | ||
| logger&.error { "debug-socket-error=#{e.inspect} backtrace=#{e.backtrace.inspect}" } | ||
| ensure | ||
| socket&.close | ||
| end | ||
| socket = server.accept | ||
| input = socket.read | ||
| logger&.warn("debug-socket-command=#{input.inspect}") | ||
|
|
||
| perform_audit(input, &block) if block | ||
| socket.puts(Commands.isolated_eval(input)) | ||
|
|
||
| errors = 0 | ||
| rescue StandardError => e | ||
| errors += 1 | ||
| logger&.error { "debug-socket-error=#{e.inspect} errors=#{errors} path=#{@path} backtrace=#{e.backtrace.inspect}" } | ||
| raise e if errors > 20 | ||
|
|
||
| sleep(1) | ||
| ensure | ||
| socket&.close | ||
| end | ||
| rescue Exception => e # rubocop:disable Lint/RescueException | ||
| logger&.error { "debug-socket-error=#{e.inspect} DebugSocket is broken now path=#{@path} backtrace=#{e.backtrace.inspect}" } | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Should the message inform the user to restart the connection or try another socket?
Collaborator
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. There is no other socket. This message means the debug socket server is broken and the process would need to be restarted. This is a fatal (for the debug socket at least) error.
Collaborator
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I don't know if we have ever seen this in practice. BUT I noticed it when testing. When I typo-ed a line, there were a LOT of logs spewing the error because we had a Now, we only allow 20 errors that aren't cause by the |
||
| end | ||
|
|
||
| logger&.unknown { "Debug socket listening on #{@path}" } | ||
|
|
@@ -65,21 +94,10 @@ def self.stop | |
| @path = nil | ||
| end | ||
|
|
||
| def self.backtrace | ||
| pid = Process.pid | ||
| "#{Time.now.utc.iso8601} #{$PROGRAM_NAME}\n" + Thread.list.map do |thread| | ||
| output = | ||
| +"#{Time.now.utc.iso8601} pid=#{pid} thread.object_id=#{thread.object_id} thread.status=#{thread.status}" | ||
| backtrace = thread.backtrace || [] | ||
| output << "\n#{backtrace.join("\n")}" if backtrace | ||
| output | ||
| end.join("\n\n") | ||
| end | ||
|
|
||
| # Allow debug socket input commands to be audited by an external callback | ||
| private_class_method def self.perform_audit(input) | ||
| yield input | ||
| rescue Exception => e | ||
| logger&.error "debug-socket-error=callback unsuccessful: #{e.inspect} for #{input.inspect} socket_path=#{@path}" | ||
| rescue Exception => e # rubocop:disable Lint/RescueException | ||
| logger&.error "debug-socket-error=callback unsuccessful: #{e.inspect} for #{input.inspect} path=#{@path} backtrace=#{e.backtrace.inspect}" | ||
| end | ||
| end | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@doctown comment as requested.