Class: RubyLsp::BrakemanLsp::Addon
- Inherits:
-
Addon
- Object
- Addon
- RubyLsp::BrakemanLsp::Addon
- Defined in:
- lib/ruby_lsp/ruby_lsp_brakeman/addon.rb
Constant Summary collapse
- FILE_GLOBS =
[ '**/brakeman.yaml', '**/brakeman.yml', '**/*.html.erb', '**/*.js.erb', '**/*.html.haml', '**/*.html.slim', '**/*.rhtml', '**/Gemfile', '**/Gemfile.lock', '**/gems.rb', '**/gems.locked', '**/*.gemspec', '**/.ruby-version', ]
Instance Method Summary collapse
-
#activate(global_state, message_queue) ⇒ Object
Kick off Brakeman scan in the background.
-
#add_warnings(warnings, fixed_warnings = []) ⇒ Object
Send warnings to the client as diagnostic messages.
- #deactivate ⇒ Object
-
#initialize ⇒ Addon
constructor
A new instance of Addon.
-
#name ⇒ Object
Returns the name of the addon.
-
#notify(message) ⇒ Object
Send logging information to the client.
-
#register_additional_file_watchers(global_state, message_queue) ⇒ Object
Watch additional files, not just *.rb.
-
#rescan ⇒ Object
Wait for changed files, then scan them.
-
#warning_message(warning) ⇒ Object
Format the warning message.
-
#warning_to_lsp_diagnostic(warning) ⇒ Object
Convert a Brakeman warning to a diagnostic.
-
#workspace_did_change_watched_files(changes) ⇒ Object
When any files change, add them to the queue for rescanning.
Constructor Details
#initialize ⇒ Addon
27 28 29 30 31 32 |
# File 'lib/ruby_lsp/ruby_lsp_brakeman/addon.rb', line 27 def initialize super @brakeman = nil @changed_queue = Queue.new end |
Instance Method Details
#activate(global_state, message_queue) ⇒ Object
Kick off Brakeman scan in the background
35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 |
# File 'lib/ruby_lsp/ruby_lsp_brakeman/addon.rb', line 35 def activate(global_state, ) @message_queue = unless Brakeman.respond_to?(:run) notify('Failed to activate Ruby LSP Brakeman') return end Thread.new do @brakeman = Brakeman.run(app_path: global_state.workspace_path, support_rescanning: true) notify("Initial Brakeman scan complete - #{@brakeman.filtered_warnings.length} warnings found") add_warnings(@brakeman.filtered_warnings) rescan end register_additional_file_watchers(global_state, ) notify('Activated Ruby LSP Brakeman, running initial scan') end |
#add_warnings(warnings, fixed_warnings = []) ⇒ Object
Send warnings to the client as diagnostic messages
88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 |
# File 'lib/ruby_lsp/ruby_lsp_brakeman/addon.rb', line 88 def add_warnings(warnings, fixed_warnings = []) # Each "publishDiagnostics" message to the client provides # a list of diagnostic messages per file. # Here we group the warnings by file and convert the warnings # to diagnostics. diagnostics = warnings.group_by do |warning| warning.file.absolute end.each_value do |warnings| warnings.map! do |warning| warning_to_lsp_diagnostic(warning) end end # Send diagnostics to client, grouped by file diagnostics.each do |path, diags| @message_queue << Notification.new( method: 'textDocument/publishDiagnostics', params: Interface::PublishDiagnosticsParams.new(uri: URI::Generic.from_path(path: path), diagnostics: diags) ) end # If a file used to have warnings, but they are now # all fixed, send an empty array to clear old warnings in the # client. Otherwise they can hang around. fixed_warnings.group_by do |warning| warning.file.absolute end.each do |path, warnings| next if diagnostics[path] # Only clear diagnostics if no warnings for file # Otherwise, send empty message for file to clear @message_queue << Notification.new( method: 'textDocument/publishDiagnostics', params: Interface::PublishDiagnosticsParams.new(uri: URI::Generic.from_path(path: path), diagnostics: []) ) end end |
#deactivate ⇒ Object
169 170 |
# File 'lib/ruby_lsp/ruby_lsp_brakeman/addon.rb', line 169 def deactivate end |
#name ⇒ Object
Returns the name of the addon
173 174 175 |
# File 'lib/ruby_lsp/ruby_lsp_brakeman/addon.rb', line 173 def name "Ruby LSP Brakeman" end |
#notify(message) ⇒ Object
Send logging information to the client
220 221 222 |
# File 'lib/ruby_lsp/ruby_lsp_brakeman/addon.rb', line 220 def notify() @message_queue << Notification.("[Brakeman] #{.to_s}") end |
#register_additional_file_watchers(global_state, message_queue) ⇒ Object
Watch additional files, not just *.rb
59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 |
# File 'lib/ruby_lsp/ruby_lsp_brakeman/addon.rb', line 59 def register_additional_file_watchers(global_state, ) # Clients are not required to implement this capability return unless global_state.supports_watching_files watchers = FILE_GLOBS.map do |pattern| Interface::FileSystemWatcher.new( glob_pattern: pattern, kind: Constant::WatchKind::CREATE | Constant::WatchKind::CHANGE | Constant::WatchKind::DELETE ) end << Request.new( id: "ruby-lsp-brakeman-file-watcher", method: "client/registerCapability", params: Interface::RegistrationParams.new( registrations: [ Interface::Registration.new( id: "workspace/didChangeWatchedFilesMyGem", method: "workspace/didChangeWatchedFiles", register_options: Interface::DidChangeWatchedFilesRegistrationOptions.new( watchers: watchers, ), ), ], ), ) end |
#rescan ⇒ Object
Wait for changed files, then scan them. Can handle multiple changed files (e.g. if files changed during a scan)
188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 |
# File 'lib/ruby_lsp/ruby_lsp_brakeman/addon.rb', line 188 def rescan loop do # Grab the first file off the top of the queue. # Will block until there's a file in the queue. first_path = @changed_queue.pop changed_files = [first_path] # Get the rest of the files from the queue, if any. @changed_queue.length.times do changed_files << @changed_queue.pop end changed_files.uniq! notify("Rescanning #{changed_files.join(', ')}") # Rescan the changed files rescanner = Brakeman::Rescanner.new(@brakeman., @brakeman.processor, changed_files) rescan = rescanner.recheck @brakeman = rescanner.tracker notify("Rescanned #{changed_files.join(', ')}") # Send new/fixed warning information to the client add_warnings(rescan.all_warnings, rescan.fixed_warnings) # Log the results notify("Warnings: #{rescan.new_warnings.length} new, #{rescan.fixed_warnings.length} fixed, #{rescan.all_warnings.length} total") end end |
#warning_message(warning) ⇒ Object
Format the warning message
159 160 161 162 163 164 165 166 167 |
# File 'lib/ruby_lsp/ruby_lsp_brakeman/addon.rb', line 159 def (warning) parts = ["[#{warning.warning_type}] #{warning.}.\n"] if warning.user_input parts << "Dangerous value: `#{warning.format_user_input}`" end parts.join("\n") end |
#warning_to_lsp_diagnostic(warning) ⇒ Object
Convert a Brakeman warning to a diagnostic
127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 |
# File 'lib/ruby_lsp/ruby_lsp_brakeman/addon.rb', line 127 def warning_to_lsp_diagnostic(warning) severity = case warning.confidence when 0 # High Constant::DiagnosticSeverity::ERROR when 1 # Medium Constant::DiagnosticSeverity::WARNING when 2 # Low Constant::DiagnosticSeverity::INFORMATION else # Theoretical other levels Constant::DiagnosticSeverity::INFORMATION end Interface::Diagnostic.new( source: "Brakeman", message: (warning), severity: severity, range: Interface::Range.new( start: Interface::Position.new( line: warning.line - 1, # Zero indexed lines character: 0, # "Start of line" ), end: Interface::Position.new( line: warning.line - 1, character: 1000, # "End of line" ), ), code: warning.code, code_description: Interface::CodeDescription.new(href: warning.link) # Does not work in VSCode? ) end |
#workspace_did_change_watched_files(changes) ⇒ Object
When any files change, add them to the queue for rescanning.
178 179 180 181 182 183 184 |
# File 'lib/ruby_lsp/ruby_lsp_brakeman/addon.rb', line 178 def workspace_did_change_watched_files(changes) changed_files = changes.map { |change| URI(change[:uri]).path } changed_files.each { |path| @changed_queue << path } notify("Queued #{changed_files.join(', ')}") end |