brakeman-llm 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (46) hide show
  1. checksums.yaml +7 -0
  2. data/bin/brakeman-llm +10 -0
  3. data/docs/warning_types/CVE-2010-3933/index.markdown +17 -0
  4. data/docs/warning_types/CVE-2011-0446/index.markdown +17 -0
  5. data/docs/warning_types/CVE-2011-3186/index.markdown +17 -0
  6. data/docs/warning_types/attribute_restriction/index.markdown +32 -0
  7. data/docs/warning_types/authentication/index.markdown +23 -0
  8. data/docs/warning_types/authentication_whitelist/index.markdown +13 -0
  9. data/docs/warning_types/basic_auth/index.markdown +14 -0
  10. data/docs/warning_types/basic_authentication/index.markdown +25 -0
  11. data/docs/warning_types/command_injection/index.markdown +26 -0
  12. data/docs/warning_types/content_tag/index.markdown +30 -0
  13. data/docs/warning_types/cross-site_request_forgery/index.markdown +19 -0
  14. data/docs/warning_types/cross_site_request_forgery/index.markdown +18 -0
  15. data/docs/warning_types/cross_site_scripting/index.markdown +64 -0
  16. data/docs/warning_types/cross_site_scripting_to_json/index.markdown +55 -0
  17. data/docs/warning_types/dangerous_eval/index.markdown +13 -0
  18. data/docs/warning_types/dangerous_evaluation/index.markdown +14 -0
  19. data/docs/warning_types/dangerous_send/index.markdown +44 -0
  20. data/docs/warning_types/default_routes/index.markdown +27 -0
  21. data/docs/warning_types/denial_of_service/index.markdown +42 -0
  22. data/docs/warning_types/dynamic_render_path/index.markdown +14 -0
  23. data/docs/warning_types/dynamic_render_paths/index.markdown +17 -0
  24. data/docs/warning_types/evaluation/index.markdown +14 -0
  25. data/docs/warning_types/file_access/index.markdown +23 -0
  26. data/docs/warning_types/format_validation/index.markdown +15 -0
  27. data/docs/warning_types/http_verb_confusion/index.markdown +42 -0
  28. data/docs/warning_types/information_disclosure/index.markdown +20 -0
  29. data/docs/warning_types/link_to/index.markdown +19 -0
  30. data/docs/warning_types/link_to_href/index.markdown +19 -0
  31. data/docs/warning_types/mass_assignment/index.markdown +67 -0
  32. data/docs/warning_types/model_validation/index.markdown +14 -0
  33. data/docs/warning_types/path_traversal/index.markdown +57 -0
  34. data/docs/warning_types/redirect/index.markdown +60 -0
  35. data/docs/warning_types/remote_code_execution/index.markdown +17 -0
  36. data/docs/warning_types/remote_code_execution_yaml_load/index.markdown +19 -0
  37. data/docs/warning_types/session_manipulation/index.markdown +28 -0
  38. data/docs/warning_types/session_setting/index.markdown +24 -0
  39. data/docs/warning_types/session_settings/index.markdown +18 -0
  40. data/docs/warning_types/sql_injection/index.markdown +41 -0
  41. data/docs/warning_types/ssl_verification_bypass/index.markdown +41 -0
  42. data/docs/warning_types/unmaintained_dependency/index.markdown +33 -0
  43. data/docs/warning_types/unsafe_deserialization/index.markdown +17 -0
  44. data/docs/warning_types/unscoped_find/index.markdown +25 -0
  45. data/lib/brakeman-llm.rb +226 -0
  46. metadata +120 -0
@@ -0,0 +1,226 @@
1
+ require 'brakeman'
2
+ require 'brakeman/warning'
3
+ require 'brakeman/options'
4
+ require 'ruby_llm'
5
+
6
+ # Override Brakeman::Warning to add LLM analysis of warning
7
+ module Brakeman
8
+ class Warning
9
+ attr_accessor :llm_analysis
10
+
11
+ alias old_to_hash to_hash
12
+
13
+ def to_hash(...)
14
+ old_to_hash(...).tap do |h|
15
+ h[:llm_analysis] = self.llm_analysis
16
+ end
17
+ end
18
+ end
19
+ end
20
+
21
+ module Brakeman
22
+
23
+ # Simple wrapper for RubyLLM
24
+ class LLM
25
+ attr_accessor :assume_model_exists, :instructions, :model, :prompt, :provider
26
+ attr_reader :llm
27
+
28
+ # Configure RubyLLM
29
+ def initialize(model:, provider:, instructions: nil, prompt: nil, assume_model_exists: false, **kwargs)
30
+ @llm = RubyLLM.context do |config|
31
+ kwargs.each do |k, v|
32
+ case k
33
+ when :api_key, :api_base, :use_system_role
34
+ config.send("#{provider}_#{k}=", v)
35
+ else
36
+ config.send("#{k}=", v)
37
+ end
38
+ end
39
+
40
+ config.log_level = :error unless kwargs.key? :log_level
41
+ end
42
+
43
+ RubyLLM.logger.level = Logger::ERROR if @llm.config.log_level == :error
44
+
45
+ @instructions = instructions || 'You are a world-class application security expert with deep expertise in Ruby and Ruby on Rails security.'
46
+
47
+ @prompt = prompt || <<~PROMPT
48
+ Analyze the following security warning resulting from analyzing a Ruby on Rails application with the static analysis security tool Brakeman.
49
+ Explain the security vulnerability and potential fixes. Jump straight into the explanation, do not have a casual introduction.
50
+ Do not ask follow-up questions, as this is not an interactive prompt.
51
+ Keep the explanation to less than 400 words.
52
+ Ignore 'fingerprint' and 'warning_code' fields and do not explain them.
53
+ PROMPT
54
+
55
+ @model = model
56
+ @provider = provider
57
+ @assume_model_exists = assume_model_exists
58
+ end
59
+
60
+ # Analyze single Brakeman warning.
61
+ # Results analysis as a string.
62
+ def analyze_warning(warning)
63
+ chat = @llm.chat(model: @model, provider: @provider, assume_model_exists: @assume_model_exists)
64
+ chat.with_instructions(@instructions)
65
+
66
+ llm_input = <<~INPUT
67
+ #{@prompt}
68
+ #{help_doc(warning)}
69
+
70
+ The following is a Brakeman security warning in JSON format that describes a potential security vulnerability:
71
+ #{warning.to_json}
72
+ INPUT
73
+
74
+ response = chat.ask llm_input
75
+
76
+ response.content
77
+ end
78
+
79
+ def help_doc(warning)
80
+ if warning.link.match %r{https://brakemanscanner.org/(.+)/}
81
+ doc = File.join(__dir__, '..', $1, "index.markdown")
82
+
83
+ if File.exist? doc
84
+ content = File.read doc
85
+ "Here is background information about this type of vulnerability: #{content}"
86
+ else
87
+ puts "No file: #{doc}"
88
+ end
89
+ end
90
+ end
91
+ end
92
+
93
+ module Options
94
+ class << self
95
+ alias old_create create_option_parser
96
+
97
+ def create_option_parser(options)
98
+ parser = old_create(options)
99
+
100
+ parser.separator ""
101
+ parser.separator "LLM Options:"
102
+
103
+ # Add LLM options
104
+ parser.on '--llm-model MODEL' do |model|
105
+ options[:llm] ||= {}
106
+ options[:llm][:model] = model
107
+ end
108
+
109
+ parser.on '--llm-provider PROVIDER', 'LLM provider (openai, ollama, gemini, etc.)' do |provider|
110
+ options[:llm] ||= {}
111
+ options[:llm][:provider] = provider
112
+ end
113
+
114
+ parser.on '--llm-api_key API_KEY', 'LLM provider API key' do |api_key|
115
+ options[:llm] ||= {}
116
+ options[:llm][:api_key] = api_key
117
+ end
118
+
119
+ parser.on '--llm-api_base BASE_URL', 'LLM provider base URL' do |url|
120
+ options[:llm] ||= {}
121
+ options[:llm][:api_base] = url
122
+ end
123
+
124
+ parser.on '--[no-]llm-disclaimer [DISCLAIMER]', 'Disclaimer to add to each generated message' do |disclaimer|
125
+ options[:llm] ||= {}
126
+
127
+ if disclaimer
128
+ options[:llm][:disclaimer] = disclaimer
129
+ else
130
+ options[:llm][:disclaimer] = :none
131
+ end
132
+ end
133
+
134
+ parser.separator ""
135
+
136
+ parser
137
+ end
138
+ end
139
+ end
140
+
141
+ class << self
142
+ alias old_run run
143
+
144
+ def ensure_llm_options(options)
145
+ return if options[:llm]
146
+
147
+ # Check config file, but only grab LLM options
148
+ config_options = self.load_options(options)
149
+
150
+ if config_options[:llm]
151
+ options[:llm] = config_options[:llm]
152
+ else
153
+ raise 'Missing LLM configuration'
154
+ end
155
+ end
156
+
157
+ def run(options)
158
+ ensure_llm_options(options)
159
+
160
+ # Suppress report output until after analysis
161
+ output_formats = get_output_formats(options)
162
+ output_files = options.delete(:output_files)
163
+ options.delete(:output_format)
164
+ print_report = options.delete(:print_report)
165
+
166
+ # Actually run scan
167
+ tracker = old_run(options)
168
+
169
+ # Set up LLM
170
+ llm_opts = options.delete(:llm) || {}
171
+
172
+ disclaimer = llm_opts.delete(:disclaimer) || '(The above message is auto-generated and may contain errors.)'
173
+ if disclaimer == :none
174
+ disclaimer = false
175
+ end
176
+
177
+ llm_opts[:log_level] = :debug if @debug
178
+ llm = llm_opts.delete(:llm) || Brakeman::LLM.new(**llm_opts)
179
+
180
+ set_analysis = output_formats.include? :to_json
181
+
182
+ notify 'Asking LLM for extended descriptions...'
183
+
184
+ warnings = tracker.warnings
185
+ total = warnings.length
186
+
187
+ # Update warnings with LLM analysis
188
+ warnings.each_with_index do |warning, index|
189
+ unless @quiet or options[:report_progress] == false
190
+ $stderr.print " #{index}/#{total} warnings processed\r"
191
+ end
192
+
193
+ if set_analysis
194
+ warning.llm_analysis = llm.analyze_warning(warning)
195
+
196
+ if disclaimer
197
+ warning.llm_analysis << "\n\n" << disclaimer
198
+ end
199
+ else
200
+ warning.message << "\n\n" << llm.analyze_warning(warning)
201
+
202
+ if disclaimer
203
+ warning.message << "\n\n" << disclaimer
204
+ end
205
+ end
206
+ end
207
+
208
+ # Move message to end of the warning output for text report
209
+ # because LLMs can be quite wordy
210
+ tracker.options[:text_fields] ||= [:confidence, :category, :check, :code, :file, :line, :message]
211
+ tracker.options[:output_formats] = output_formats
212
+
213
+ if output_files
214
+ notify "Generating report..."
215
+
216
+ write_report_to_files tracker, output_files
217
+ elsif print_report
218
+ notify "Generating report..."
219
+
220
+ write_report_to_formats tracker, output_formats
221
+ end
222
+
223
+ tracker
224
+ end
225
+ end
226
+ end
metadata ADDED
@@ -0,0 +1,120 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: brakeman-llm
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - Justin Collins
8
+ bindir: bin
9
+ cert_chain: []
10
+ date: 2025-08-30 00:00:00.000000000 Z
11
+ dependencies:
12
+ - !ruby/object:Gem::Dependency
13
+ name: brakeman
14
+ requirement: !ruby/object:Gem::Requirement
15
+ requirements:
16
+ - - ">="
17
+ - !ruby/object:Gem::Version
18
+ version: '7.0'
19
+ type: :runtime
20
+ prerelease: false
21
+ version_requirements: !ruby/object:Gem::Requirement
22
+ requirements:
23
+ - - ">="
24
+ - !ruby/object:Gem::Version
25
+ version: '7.0'
26
+ - !ruby/object:Gem::Dependency
27
+ name: ruby_llm
28
+ requirement: !ruby/object:Gem::Requirement
29
+ requirements:
30
+ - - ">="
31
+ - !ruby/object:Gem::Version
32
+ version: '1.6'
33
+ - - "<="
34
+ - !ruby/object:Gem::Version
35
+ version: '2.0'
36
+ type: :runtime
37
+ prerelease: false
38
+ version_requirements: !ruby/object:Gem::Requirement
39
+ requirements:
40
+ - - ">="
41
+ - !ruby/object:Gem::Version
42
+ version: '1.6'
43
+ - - "<="
44
+ - !ruby/object:Gem::Version
45
+ version: '2.0'
46
+ description: Brakeman detects security vulnerabilities in Ruby on Rails applications
47
+ via static analysis.
48
+ email: gem@brakeman.org
49
+ executables:
50
+ - brakeman-llm
51
+ extensions: []
52
+ extra_rdoc_files: []
53
+ files:
54
+ - bin/brakeman-llm
55
+ - docs/warning_types/CVE-2010-3933/index.markdown
56
+ - docs/warning_types/CVE-2011-0446/index.markdown
57
+ - docs/warning_types/CVE-2011-3186/index.markdown
58
+ - docs/warning_types/attribute_restriction/index.markdown
59
+ - docs/warning_types/authentication/index.markdown
60
+ - docs/warning_types/authentication_whitelist/index.markdown
61
+ - docs/warning_types/basic_auth/index.markdown
62
+ - docs/warning_types/basic_authentication/index.markdown
63
+ - docs/warning_types/command_injection/index.markdown
64
+ - docs/warning_types/content_tag/index.markdown
65
+ - docs/warning_types/cross-site_request_forgery/index.markdown
66
+ - docs/warning_types/cross_site_request_forgery/index.markdown
67
+ - docs/warning_types/cross_site_scripting/index.markdown
68
+ - docs/warning_types/cross_site_scripting_to_json/index.markdown
69
+ - docs/warning_types/dangerous_eval/index.markdown
70
+ - docs/warning_types/dangerous_evaluation/index.markdown
71
+ - docs/warning_types/dangerous_send/index.markdown
72
+ - docs/warning_types/default_routes/index.markdown
73
+ - docs/warning_types/denial_of_service/index.markdown
74
+ - docs/warning_types/dynamic_render_path/index.markdown
75
+ - docs/warning_types/dynamic_render_paths/index.markdown
76
+ - docs/warning_types/evaluation/index.markdown
77
+ - docs/warning_types/file_access/index.markdown
78
+ - docs/warning_types/format_validation/index.markdown
79
+ - docs/warning_types/http_verb_confusion/index.markdown
80
+ - docs/warning_types/information_disclosure/index.markdown
81
+ - docs/warning_types/link_to/index.markdown
82
+ - docs/warning_types/link_to_href/index.markdown
83
+ - docs/warning_types/mass_assignment/index.markdown
84
+ - docs/warning_types/model_validation/index.markdown
85
+ - docs/warning_types/path_traversal/index.markdown
86
+ - docs/warning_types/redirect/index.markdown
87
+ - docs/warning_types/remote_code_execution/index.markdown
88
+ - docs/warning_types/remote_code_execution_yaml_load/index.markdown
89
+ - docs/warning_types/session_manipulation/index.markdown
90
+ - docs/warning_types/session_setting/index.markdown
91
+ - docs/warning_types/session_settings/index.markdown
92
+ - docs/warning_types/sql_injection/index.markdown
93
+ - docs/warning_types/ssl_verification_bypass/index.markdown
94
+ - docs/warning_types/unmaintained_dependency/index.markdown
95
+ - docs/warning_types/unsafe_deserialization/index.markdown
96
+ - docs/warning_types/unscoped_find/index.markdown
97
+ - lib/brakeman-llm.rb
98
+ homepage: https://github.com/presidentbeef/brakeman-llm
99
+ licenses:
100
+ - MIT
101
+ metadata:
102
+ source_code_uri: https://github.com/presidentbeef/brakeman-llm
103
+ rdoc_options: []
104
+ require_paths:
105
+ - lib
106
+ required_ruby_version: !ruby/object:Gem::Requirement
107
+ requirements:
108
+ - - ">="
109
+ - !ruby/object:Gem::Version
110
+ version: 3.1.0
111
+ required_rubygems_version: !ruby/object:Gem::Requirement
112
+ requirements:
113
+ - - ">="
114
+ - !ruby/object:Gem::Version
115
+ version: '0'
116
+ requirements: []
117
+ rubygems_version: 3.6.2
118
+ specification_version: 4
119
+ summary: Enhance Brakeman warnings with LLM-based descriptions
120
+ test_files: []