Module: Brakeman

Defined in:
lib/brakeman-llm.rb,
lib/brakeman-llm.rb

Overview

Override Brakeman::Warning to add LLM analysis of warning

Defined Under Namespace

Modules: Options Classes: LLM, Warning

Class Method Summary collapse

Class Method Details

.ensure_llm_options(options) ⇒ Object



144
145
146
147
148
149
150
151
152
153
154
155
# File 'lib/brakeman-llm.rb', line 144

def ensure_llm_options(options)
  return if options[:llm]

  # Check config file, but only grab LLM options
  config_options = self.load_options(options)

  if config_options[:llm]
    options[:llm] = config_options[:llm]
  else
    raise 'Missing LLM configuration'
  end
end

.old_run ⇒ Object



142
# File 'lib/brakeman-llm.rb', line 142

alias old_run run

.run(options) ⇒ Object



157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
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
218
219
220
221
222
223
224
225
226
227
228
# File 'lib/brakeman-llm.rb', line 157

def run(options)
  ensure_llm_options(options)

  # Suppress report output until after analysis
  output_formats = get_output_formats(options)
  output_files = options.delete(:output_files)
  options.delete(:output_format)
  print_report = options.delete(:print_report)

  # Actually run scan
  tracker = old_run(options)

  # Set up LLM
  llm_opts = options.delete(:llm) || {}

  disclaimer = llm_opts.delete(:disclaimer) || '(The above message is auto-generated and may contain errors.)'
  if disclaimer == :none
    disclaimer = false
  end

  llm_opts[:log_level] = :debug if @debug
  llm = llm_opts.delete(:llm) || Brakeman::LLM.new(**llm_opts)

  set_analysis = output_formats.include? :to_json

  notify 'Asking LLM for extended descriptions...'

  warnings = tracker.warnings
  total = warnings.length

  # Update warnings with LLM analysis
  warnings.each_with_index do |warning, index|
    unless @quiet or options[:report_progress] == false
      $stderr.print " #{index}/#{total} warnings processed\r"
    end

    begin
      if set_analysis
        warning.llm_analysis = llm.analyze_warning(warning)

        if disclaimer
          warning.llm_analysis << "\n\n" << disclaimer
        end
      else
        warning.message << "\n\n" << llm.analyze_warning(warning)

        if disclaimer
          warning.message << "\n\n" << disclaimer
        end
      end
    rescue RubyLLM::Error => e
      Brakeman.notify "Failed to analyze warning (#{warning.fingerprint}): #{e}"
    end
  end

  # Move message to end of the warning output for text report
  # because LLMs can be quite wordy
  tracker.options[:text_fields] ||= [:confidence, :category, :check, :code, :file, :line, :message]
  tracker.options[:output_formats] = output_formats

  if output_files
    notify "Generating report..."

    write_report_to_files tracker, output_files
  elsif print_report
    notify "Generating report..."

    write_report_to_formats tracker, output_formats
  end

  tracker
end