brakeman 1.9.4 → 1.9.5

Sign up to get free protection for your applications and to get access to all the features.
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