brakeman 1.9.4 → 1.9.5

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 (38) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGES +15 -0
  3. data/README.md +6 -0
  4. data/lib/brakeman.rb +7 -2
  5. data/lib/brakeman/checks/check_link_to.rb +59 -67
  6. data/lib/brakeman/checks/check_mass_assignment.rb +5 -1
  7. data/lib/brakeman/checks/check_session_settings.rb +18 -7
  8. data/lib/brakeman/checks/check_symbol_dos.rb +50 -3
  9. data/lib/brakeman/processors/alias_processor.rb +116 -42
  10. data/lib/brakeman/processors/controller_processor.rb +5 -0
  11. data/lib/brakeman/processors/erb_template_processor.rb +2 -1
  12. data/lib/brakeman/processors/erubis_template_processor.rb +2 -1
  13. data/lib/brakeman/processors/haml_template_processor.rb +2 -1
  14. data/lib/brakeman/processors/lib/find_all_calls.rb +17 -0
  15. data/lib/brakeman/processors/lib/find_return_value.rb +3 -3
  16. data/lib/brakeman/processors/lib/render_helper.rb +1 -1
  17. data/lib/brakeman/processors/slim_template_processor.rb +1 -1
  18. data/lib/brakeman/processors/template_processor.rb +1 -1
  19. data/lib/brakeman/report.rb +60 -151
  20. data/lib/brakeman/report/initializers/faster_csv.rb +7 -0
  21. data/lib/brakeman/report/initializers/multi_json.rb +29 -0
  22. data/lib/brakeman/report/renderer.rb +22 -0
  23. data/lib/brakeman/{templates → report/templates}/controller_overview.html.erb +0 -0
  24. data/lib/brakeman/{templates → report/templates}/controller_warnings.html.erb +0 -0
  25. data/lib/brakeman/{templates → report/templates}/error_overview.html.erb +0 -0
  26. data/lib/brakeman/{templates → report/templates}/header.html.erb +2 -2
  27. data/lib/brakeman/{templates → report/templates}/model_warnings.html.erb +0 -0
  28. data/lib/brakeman/{templates → report/templates}/overview.html.erb +2 -2
  29. data/lib/brakeman/{templates → report/templates}/security_warnings.html.erb +0 -0
  30. data/lib/brakeman/{templates → report/templates}/template_overview.html.erb +0 -0
  31. data/lib/brakeman/{templates → report/templates}/view_warnings.html.erb +0 -0
  32. data/lib/brakeman/{templates → report/templates}/warning_overview.html.erb +0 -0
  33. data/lib/brakeman/scanner.rb +3 -0
  34. data/lib/brakeman/util.rb +6 -0
  35. data/lib/brakeman/version.rb +1 -1
  36. data/lib/brakeman/warning_codes.rb +1 -0
  37. data/lib/ruby_parser/bm_sexp.rb +14 -39
  38. metadata +17 -14
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA512:
3
- metadata.gz: b29913808e5de71c6f0e13ab91142f730fdce339829e8fbfc91deca3eb0ce95eb24ce2e58e9fe576f121bce7f63a2015aa84866ac2bb41d07a266081c8e76293
4
- data.tar.gz: 81669ba7654b7ced4214430613064484ba5fc7ea6a331ee31dd8aeea294621fda75b23b3caee0d1effec0b3fd67c7f636ee5e0868e17d337b6ac4a180e305db6
3
+ metadata.gz: 5fec83c0cb268acf3954ea08df30a173ad10b3121d4d30d18c1670de66cf17d01fde3d0649c28f5c5570938d9d4a13368af819f4d580800441c1cdf4b4873cd2
4
+ data.tar.gz: ff597aced590649b3a90cf2f895407f9e347a8a81b1f34337e2eff32310958c34bbe711985b70ce58f6f91eb658bc8d6db533819b881fb4d3ec9fa3842cb751e
5
5
  SHA1:
6
- metadata.gz: 1f0ed0d4abb525abf8c95b72133ea5c304325075
7
- data.tar.gz: 48837c8bdab262f44bf27c8962e13d4c9808589d
6
+ metadata.gz: b4735612d0032bfd8a72f0feb285ee4e704a3a1a
7
+ data.tar.gz: 1898b18fbf38409a98ba3b5517f3896b057872c9
data/CHANGES CHANGED
@@ -1,3 +1,18 @@
1
+ # 1.9.5
2
+
3
+ * Add check for unsafe symbol creation
4
+ * Do not warn on mass assignment with `slice`/`only`
5
+ * Do not warn on session secret if in `.gitignore`
6
+ * Fix scoping for blocks and block arguments
7
+ * Fix error when modifying blocks in templates
8
+ * Fix session secret check for Rails 4
9
+ * Fix crash on `before_filter` outside controller
10
+ * Fix `Sexp` hash cache invalidation
11
+ * Respect `quiet` option in configuration file
12
+ * Convert assignment to simple `if` expressions to `or`
13
+ * More fixes for assignments inside branches
14
+ * Pin to ruby2ruby version 2.0.3
15
+
1
16
  # 1.9.4
2
17
 
3
18
  * Add check for CVE-2013-1854
data/README.md CHANGED
@@ -26,6 +26,12 @@ Using RubyGems:
26
26
 
27
27
  gem install brakeman
28
28
 
29
+ Using Bundler, add to development group in Gemfile:
30
+
31
+ group :development do
32
+ gem 'brakeman', :require => false
33
+ end
34
+
29
35
  From source:
30
36
 
31
37
  gem build brakeman.gemspec
@@ -65,7 +65,12 @@ module Brakeman
65
65
 
66
66
  options[:app_path] = File.expand_path(options[:app_path])
67
67
 
68
- options = load_options(options[:config_file]).merge! options
68
+ file_options = load_options(options[:config_file])
69
+
70
+ options = file_options.merge options
71
+
72
+ options[:quiet] = true if options[:quiet].nil? && file_options[:quiet]
73
+
69
74
  options = get_defaults.merge! options
70
75
  options[:output_formats] = get_output_formats options
71
76
 
@@ -89,9 +94,9 @@ module Brakeman
89
94
  def self.load_options custom_location
90
95
  #Load configuration file
91
96
  if config = config_file(custom_location)
92
- notify "[Notice] Using configuration in #{config}"
93
97
  options = YAML.load_file config
94
98
  options.each { |k, v| options[k] = Set.new v if v.is_a? Array }
99
+ notify "[Notice] Using configuration in #{config}" unless options[:quiet]
95
100
  options
96
101
  else
97
102
  {}
@@ -23,14 +23,10 @@ class Brakeman::CheckLinkTo < Brakeman::CheckCrossSiteScripting
23
23
  @known_dangerous = []
24
24
  #Ideally, I think this should also check to see if people are setting
25
25
  #:escape => false
26
- methods = tracker.find_call :target => false, :method => :link_to
27
-
28
26
  @models = tracker.models.keys
29
27
  @inspect_arguments = tracker.options[:check_arguments]
30
28
 
31
- methods.each do |call|
32
- process_result call
33
- end
29
+ tracker.find_call(:target => false, :method => :link_to).each {|call| process_result call}
34
30
  end
35
31
 
36
32
  def process_result result
@@ -61,68 +57,68 @@ class Brakeman::CheckLinkTo < Brakeman::CheckCrossSiteScripting
61
57
  end
62
58
  end
63
59
 
60
+ # Check the argument for possible xss exploits
64
61
  def check_argument result, exp
65
- arg = process exp
66
-
67
- if input = has_immediate_user_input?(arg)
68
- case input.type
69
- when :params
70
- message = "Unescaped parameter value in link_to"
71
- when :cookies
72
- message = "Unescaped cookie value in link_to"
73
- else
74
- message = "Unescaped user input value in link_to"
75
- end
62
+ argument = process(exp)
63
+ !check_user_input(result, argument) && !check_method(result, argument) && !check_matched(result, @matched)
64
+ end
76
65
 
77
- add_result result
78
- warn :result => result,
79
- :warning_type => "Cross Site Scripting",
80
- :warning_code => :xss_link_to,
81
- :message => message,
82
- :user_input => input.match,
83
- :confidence => CONFIDENCE[:high],
84
- :link_path => "link_to"
85
-
86
- elsif not tracker.options[:ignore_model_output] and match = has_immediate_model?(arg)
87
- method = match.method
88
-
89
- unless IGNORE_MODEL_METHODS.include? method
90
- add_result result
91
-
92
- if MODEL_METHODS.include? method or method.to_s =~ /^find_by/
93
- confidence = CONFIDENCE[:high]
94
- else
95
- confidence = CONFIDENCE[:med]
96
- end
97
-
98
- warn :result => result,
99
- :warning_type => "Cross Site Scripting",
100
- :warning_code => :xss_link_to,
101
- :message => "Unescaped model attribute in link_to",
102
- :user_input => match,
103
- :confidence => confidence,
104
- :link_path => "link_to"
105
- end
66
+ # Check we should warn about the user input
67
+ def check_user_input(result, argument)
68
+ input = has_immediate_user_input?(argument)
69
+ return false unless input
70
+
71
+ case input.type
72
+ when :params
73
+ message = "Unescaped parameter value in link_to"
74
+ when :cookies
75
+ message = "Unescaped cookie value in link_to"
76
+ else
77
+ message = "Unescaped user input value in link_to"
78
+ end
106
79
 
107
- elsif @matched
108
- if @matched.type == :model and not tracker.options[:ignore_model_output]
109
- message = "Unescaped model attribute in link_to"
110
- elsif @matched.type == :params
111
- message = "Unescaped parameter value in link_to"
112
- end
80
+ warn_xss(result, message, input.match, CONFIDENCE[:high])
81
+ end
113
82
 
114
- if message
115
- add_result result
83
+ # Check if we should warn about the specified method
84
+ def check_method(result, argument)
85
+ return false if tracker.options[:ignore_model_output]
86
+ match = has_immediate_model?(argument)
87
+ return false unless match
88
+ method = match.method
89
+ return false if IGNORE_MODEL_METHODS.include? method
90
+
91
+ confidence = CONFIDENCE[:med]
92
+ confidence = CONFIDENCE[:high] if MODEL_METHODS.include? method or method.to_s =~ /^find_by/
93
+ warn_xss(result, "Unescaped model attribute in link_to", match, confidence)
94
+ end
116
95
 
117
- warn :result => result,
118
- :warning_type => "Cross Site Scripting",
119
- :warning_code => :xss_link_to,
120
- :message => message,
121
- :user_input => @matched.match,
122
- :confidence => CONFIDENCE[:med],
123
- :link_path => "link_to"
124
- end
96
+ # Check if we should warn about the matched result
97
+ def check_matched(result, matched = nil)
98
+ return false unless matched
99
+ message = nil
100
+
101
+ if matched.type == :model and not tracker.options[:ignore_model_output]
102
+ message = "Unescaped model attribute in link_to"
103
+ elsif matched.type == :params
104
+ message = "Unescaped parameter value in link_to"
125
105
  end
106
+
107
+ message ? warn_xss(result, message, @matched.match, CONFIDENCE[:med]) : false
108
+ end
109
+
110
+ # Create a warn for this xss
111
+ def warn_xss(result, message, user_input, confidence)
112
+ add_result(result)
113
+ warn :result => result,
114
+ :warning_type => "Cross Site Scripting",
115
+ :warning_code => :xss_link_to,
116
+ :message => message,
117
+ :user_input => user_input,
118
+ :confidence => confidence,
119
+ :link_path => "link_to"
120
+
121
+ true
126
122
  end
127
123
 
128
124
  def process_call exp
@@ -135,16 +131,12 @@ class Brakeman::CheckLinkTo < Brakeman::CheckCrossSiteScripting
135
131
  return if @matched
136
132
 
137
133
  target = exp.target
138
- if sexp? target
139
- target = process target.dup
140
- end
134
+ target = process target.dup if sexp? target
141
135
 
142
136
  #Bare records create links to the model resource,
143
137
  #not a string that could have injection
144
138
  #TODO: Needs test? I think this is broken?
145
- if model_name? target and context == [:call, :arglist]
146
- return exp
147
- end
139
+ return exp if model_name? target and context == [:call, :arglist]
148
140
 
149
141
  super
150
142
  end
@@ -59,7 +59,11 @@ class Brakeman::CheckMassAssignment < Brakeman::BaseCheck
59
59
  if attr_protected and tracker.options[:ignore_attr_protected]
60
60
  return
61
61
  elsif input = include_user_input?(call.arglist)
62
- if not hash? call.first_arg and not attr_protected
62
+ first_arg = call.first_arg
63
+
64
+ if call? first_arg and (first_arg.method == :slice or first_arg.method == :only)
65
+ return
66
+ elsif not node_type? first_arg, :hash and not attr_protected
63
67
  confidence = CONFIDENCE[:high]
64
68
  user_input = input.match
65
69
  else
@@ -23,12 +23,10 @@ class Brakeman::CheckSessionSettings < Brakeman::BaseCheck
23
23
 
24
24
  check_for_issues settings, "#{tracker.options[:app_path]}/config/environment.rb"
25
25
 
26
- if tracker.initializers["session_store.rb"]
27
- process tracker.initializers["session_store.rb"]
28
- end
29
-
30
- if tracker.initializers["secret_token.rb"]
31
- process tracker.initializers["secret_token.rb"]
26
+ ["session_store.rb", "secret_token.rb"].each do |file|
27
+ if tracker.initializers[file] and not ignored? file
28
+ process tracker.initializers[file]
29
+ end
32
30
  end
33
31
  end
34
32
 
@@ -37,13 +35,16 @@ class Brakeman::CheckSessionSettings < Brakeman::BaseCheck
37
35
  #
38
36
  #and App::Application.config.secret_token =
39
37
  #in Rails 3.x apps
38
+ #
39
+ #and App::Application.config.secret_key_base =
40
+ #in Rails 4.x apps
40
41
  def process_attrasgn exp
41
42
  if not tracker.options[:rails3] and exp.target == @session_settings and exp.method == :session=
42
43
  check_for_issues exp.first_arg, "#{tracker.options[:app_path]}/config/initializers/session_store.rb"
43
44
  end
44
45
 
45
46
  if tracker.options[:rails3] and settings_target?(exp.target) and
46
- exp.method == :secret_token= and string? exp.first_arg
47
+ (exp.method == :secret_token= or exp.method == :secret_key_base=) and string? exp.first_arg
47
48
 
48
49
  warn_about_secret_token exp, "#{tracker.options[:app_path]}/config/initializers/secret_token.rb"
49
50
  end
@@ -132,4 +133,14 @@ class Brakeman::CheckSessionSettings < Brakeman::BaseCheck
132
133
  :line => value.line,
133
134
  :file => file
134
135
  end
136
+
137
+ def ignored? file
138
+ if @app_tree.exists? ".gitignore"
139
+ input = @app_tree.read(".gitignore")
140
+
141
+ input.include? file
142
+ else
143
+ false
144
+ end
145
+ end
135
146
  end
@@ -3,7 +3,7 @@ require 'brakeman/checks/base_check'
3
3
  class Brakeman::CheckSymbolDoS < Brakeman::BaseCheck
4
4
  Brakeman::Checks.add self
5
5
 
6
- @description = "Checks for versions with ActiveRecord symbol denial of service"
6
+ @description = "Checks for versions with ActiveRecord symbol denial of service, or code with a similar vulnerability"
7
7
 
8
8
  def run_check
9
9
  fix_version = case
@@ -14,10 +14,10 @@ class Brakeman::CheckSymbolDoS < Brakeman::BaseCheck
14
14
  when version_between?('3.2.0', '3.2.12')
15
15
  '3.2.13'
16
16
  else
17
- return
17
+ nil
18
18
  end
19
19
 
20
- unless active_record_models.empty?
20
+ if fix_version && active_record_models.any?
21
21
  warn :warning_type => "Denial of Service",
22
22
  :warning_code => :CVE_2013_1854,
23
23
  :message => "Rails #{tracker.config[:rails_version]} has a denial of service vulnerability in ActiveRecord: upgrade to #{fix_version} or patch",
@@ -25,5 +25,52 @@ class Brakeman::CheckSymbolDoS < Brakeman::BaseCheck
25
25
  :file => gemfile_or_environment,
26
26
  :link => "https://groups.google.com/d/msg/rubyonrails-security/jgJ4cjjS8FE/BGbHRxnDRTIJ"
27
27
  end
28
+
29
+ tracker.find_call(:methods => [:to_sym, :literal_to_sym], :nested => true).each do |result|
30
+ check_unsafe_symbol_creation(result)
31
+ end
32
+
28
33
  end
34
+
35
+ def check_unsafe_symbol_creation result
36
+
37
+ call = result[:call]
38
+ if result[:method] == :to_sym
39
+ args = [call.target]
40
+ else
41
+ args = call.select { |e| sexp? e }
42
+ end
43
+
44
+ if input = args.map{ |arg| has_immediate_user_input?(arg) }.compact.first
45
+ confidence = CONFIDENCE[:high]
46
+ elsif input = args.map{ |arg| include_user_input?(arg) }.compact.first
47
+ confidence = CONFIDENCE[:med]
48
+ end
49
+
50
+ if confidence
51
+ input_type = case input.type
52
+ when :params
53
+ "parameter value"
54
+ when :cookies
55
+ "cookies value"
56
+ when :request
57
+ "request value"
58
+ when :model
59
+ "model attribute"
60
+ else
61
+ "user input"
62
+ end
63
+
64
+ message = "Symbol conversion from unsafe string (#{input_type})"
65
+
66
+ warn :result => result,
67
+ :warning_type => "Denial of Service",
68
+ :warning_code => :unsafe_symbol_creation,
69
+ :message => message,
70
+ :user_input => input.match,
71
+ :confidence => confidence
72
+ end
73
+
74
+ end
75
+
29
76
  end
@@ -19,7 +19,7 @@ class Brakeman::AliasProcessor < Brakeman::SexpProcessor
19
19
  def initialize tracker = nil
20
20
  super()
21
21
  @env = SexpProcessor::Environment.new
22
- @inside_if = []
22
+ @inside_if = false
23
23
  @ignore_ifs = nil
24
24
  @exp_context = []
25
25
  @current_module = nil
@@ -167,6 +167,41 @@ class Brakeman::AliasProcessor < Brakeman::SexpProcessor
167
167
  exp
168
168
  end
169
169
 
170
+ def process_call_with_block exp
171
+ exp[1] = process exp.block_call
172
+
173
+ env.scope do
174
+ exp.block_args.each do |e|
175
+ #Force block arg(s) to be local
176
+ if node_type? e, :lasgn
177
+ env.current[Sexp.new(:lvar, e.lhs)] = e.rhs
178
+ elsif node_type? e, :masgn
179
+ e[1..-1].each do |var|
180
+ local = Sexp.new(:lvar, var)
181
+ env.current[local] = local
182
+ end
183
+ elsif e.is_a? Symbol
184
+ local = Sexp.new(:lvar, e)
185
+ env.current[local] = local
186
+ else
187
+ raise "Unexpected value in block args: #{e.inspect}"
188
+ end
189
+ end
190
+
191
+ block = exp.block
192
+
193
+ if block? block
194
+ process_all! block
195
+ else
196
+ exp[3] = process block
197
+ end
198
+ end
199
+
200
+ exp
201
+ end
202
+
203
+ alias process_iter process_call_with_block
204
+
170
205
  #Process a new scope.
171
206
  def process_scope exp
172
207
  env.scope do
@@ -232,8 +267,9 @@ class Brakeman::AliasProcessor < Brakeman::SexpProcessor
232
267
  def process_gasgn exp
233
268
  match = Sexp.new(:gvar, exp.lhs)
234
269
  value = exp.rhs = process(exp.rhs)
270
+ value.line = exp.line
235
271
 
236
- set_value match, value, exp.line
272
+ set_value match, value
237
273
 
238
274
  exp
239
275
  end
@@ -244,7 +280,7 @@ class Brakeman::AliasProcessor < Brakeman::SexpProcessor
244
280
  match = Sexp.new(:cvar, exp.lhs)
245
281
  value = exp.rhs = process(exp.rhs)
246
282
 
247
- set_value match, value, exp.line
283
+ set_value match, value
248
284
 
249
285
  exp
250
286
  end
@@ -265,7 +301,7 @@ class Brakeman::AliasProcessor < Brakeman::SexpProcessor
265
301
  value = exp.second_arg = process(value_arg)
266
302
  match = Sexp.new(:call, target, :[], index)
267
303
 
268
- set_value match, value, exp.line
304
+ set_value match, value
269
305
 
270
306
  if hash? target
271
307
  env[tar_variable] = hash_insert target.deep_clone, index, value
@@ -275,7 +311,7 @@ class Brakeman::AliasProcessor < Brakeman::SexpProcessor
275
311
  #This is what we'll replace with the value
276
312
  match = Sexp.new(:call, target, method.to_s[0..-2].to_sym)
277
313
 
278
- set_value match, value, exp.line
314
+ set_value match, value
279
315
  else
280
316
  raise "Unrecognized assignment: #{exp}"
281
317
  end
@@ -377,34 +413,69 @@ class Brakeman::AliasProcessor < Brakeman::SexpProcessor
377
413
 
378
414
  condition = process exp.condition
379
415
 
416
+ #Check if a branch is obviously going to be taken
380
417
  if true? condition
381
418
  no_branch = true
382
- exps = [exp.then_clause]
419
+ exps = [exp.then_clause, nil]
383
420
  elsif false? condition
384
421
  no_branch = true
385
- exps = [exp.else_clause]
422
+ exps = [nil, exp.else_clause]
386
423
  else
387
424
  no_branch = false
388
425
  exps = [exp.then_clause, exp.else_clause]
389
426
  end
390
427
 
391
- exps.each do |e|
392
- @inside_if << [] unless no_branch or @ignore_ifs
393
-
394
- if sexp? e
395
- if e.node_type == :block
396
- process_default e #avoid creating new scope
397
- else
398
- process e
428
+ if @ignore_ifs or no_branch
429
+ exps.each_with_index do |branch, i|
430
+ exp[2 + i] = process_if_branch branch
431
+ end
432
+ else
433
+ was_inside = @inside_if
434
+ @inside_if = true
435
+
436
+ branch_scopes = []
437
+ exps.each_with_index do |branch, i|
438
+ scope do
439
+ branch_index = 2 + i # s(:if, condition, then_branch, else_branch)
440
+ exp[branch_index] = process_if_branch branch
441
+ branch_scopes << env.current
399
442
  end
400
443
  end
401
444
 
402
- @inside_if.pop unless no_branch or @ignore_ifs
445
+ @inside_if = was_inside
446
+
447
+ branch_scopes.each do |s|
448
+ merge_if_branch s
449
+ end
403
450
  end
404
451
 
405
452
  exp
406
453
  end
407
454
 
455
+ def process_if_branch exp
456
+ if sexp? exp
457
+ if block? exp
458
+ process_default exp
459
+ else
460
+ process exp
461
+ end
462
+ end
463
+ end
464
+
465
+ def merge_if_branch branch_env
466
+ branch_env.each do |k, v|
467
+ current_val = env[k]
468
+
469
+ if current_val
470
+ unless same_value? current_val, v
471
+ env[k] = Sexp.new(:or, current_val, v).line(k.line || -2)
472
+ end
473
+ else
474
+ env[k] = v
475
+ end
476
+ end
477
+ end
478
+
408
479
  #Process single integer access to an array.
409
480
  #
410
481
  #Returns the value inside the array, if possible.
@@ -601,12 +672,6 @@ class Brakeman::AliasProcessor < Brakeman::SexpProcessor
601
672
  nil
602
673
  end
603
674
 
604
- #Return true if this value should be "branched" - i.e. converted into an or
605
- #expression with two or more values
606
- def branch_value? exp
607
- not (@inside_if.empty? or @inside_if.last.include? exp)
608
- end
609
-
610
675
  #Return true if lhs == rhs or lhs is an or expression and
611
676
  #rhs is one of its values
612
677
  def same_value? lhs, rhs
@@ -619,32 +684,41 @@ class Brakeman::AliasProcessor < Brakeman::SexpProcessor
619
684
  end
620
685
  end
621
686
 
687
+ def value_from_if exp
688
+ if block? exp.else_clause or block? exp.then_clause
689
+ #If either clause is more than a single expression, just use entire
690
+ #if expression for now
691
+ exp
692
+ elsif exp.else_clause.nil?
693
+ exp.then_clause
694
+ elsif exp.then_clause.nil?
695
+ exp.else_clause
696
+ else
697
+ condition = exp.condition
698
+
699
+ if true? condition
700
+ exp.then_clause
701
+ elsif false? condition
702
+ exp.else_clause
703
+ else
704
+ Sexp.new(:or, exp.then_clause, exp.else_clause).line(exp.line)
705
+ end
706
+ end
707
+ end
708
+
622
709
  #Set variable to given value.
623
710
  #Creates "branched" versions of values when appropriate.
624
711
  #Avoids creating multiple branched versions inside same
625
712
  #if branch.
626
- def set_value var, value, line = nil
627
- unless @ignore_ifs
628
- current_val = env[var]
629
- current_if = @inside_if.last
630
-
631
- if branch_value? var and current_val = env[var]
632
- unless same_value? current_val, value
633
- env[var] = Sexp.new(:or, current_val, value).line(line || var.line || -2)
634
- current_if << var
635
- end
636
- elsif current_if and current_if.include?(var) and node_type?(current_val, :or)
637
- #Replace last value instead of creating another or
638
- current_val.rhs = value
639
- else
640
- env[var] = value
713
+ def set_value var, value
714
+ if node_type? value, :if
715
+ value = value_from_if(value)
716
+ end
641
717
 
642
- if current_if
643
- current_if << var
644
- end
645
- end
646
- else
718
+ if @ignore_ifs or not @inside_if
647
719
  env[var] = value
720
+ else
721
+ env.current[var] = value
648
722
  end
649
723
  end
650
724