localeapp 0.6.8 → 0.6.9

Sign up to get free protection for your applications and to get access to all the features.
data/CHANGELOG.md CHANGED
@@ -1,3 +1,7 @@
1
+ # Version 0.6.8
2
+
3
+ * Don't load any yaml that may contain insecure content
4
+
1
5
  # Version 0.6.7
2
6
 
3
7
  * Add rm and mv commands for deleting / renaming keys from the command line
data/README.md CHANGED
@@ -10,6 +10,15 @@ The gem hooks into the i18n exception mechanism to send missing translations to
10
10
  the app. When translated content has been added it's automatically pulled down
11
11
  so you can see it straight away.
12
12
 
13
+ ## Security
14
+
15
+ Though the i18n gem uses YAML as it's default file format it doesn't require
16
+ serialization of ruby objects. To prevent the kind of security problems
17
+ detailed in [CVE-2013-0156][1] the localeapp gem will not load any YAML containing
18
+ the string !ruby/ as of version 0.6.9.
19
+
20
+ [1]: https://groups.google.com/forum/?fromgroups=#!topic/rubyonrails-security/61bkgvnSGTQ
21
+
13
22
  ## Installation
14
23
 
15
24
  ### Rails 3
@@ -145,7 +145,7 @@ require 'localeapp/rails'
145
145
 
146
146
  Localeapp.configure do |config|
147
147
  config.api_key = ENV['LOCALEAPP_API_KEY']
148
- config.poll_interval = 300 if defined?(Rails) && Rails.env.staging?
148
+ config.environment_name = ENV['LOCALEAPP_ENV'] unless ENV['LOCALEAPP_ENV'].nil?
149
149
  config.polling_environments = [:development, :staging]
150
150
  config.reloading_environments = [:development, :staging]
151
151
  config.sending_environments = [:development, :staging]
@@ -70,6 +70,10 @@ module Localeapp
70
70
  # The complete path to the directory where translations are stored
71
71
  attr_accessor :translation_data_directory
72
72
 
73
+ # Enable or disable the insecure yaml exception
74
+ # default: true
75
+ attr_accessor :raise_on_insecure_yaml
76
+
73
77
  def initialize
74
78
  defaults.each do |setting, value|
75
79
  send("#{setting}=", value)
@@ -90,6 +94,7 @@ module Localeapp
90
94
  :daemon_pid_file => File.join('tmp', 'pids', 'localeapp.pid'),
91
95
  :daemon_log_file => File.join('log', 'localeapp_daemon.log'),
92
96
  :translation_data_directory => File.join('config', 'locales'),
97
+ :raise_on_insecure_yaml => true,
93
98
  }
94
99
  if ENV['DEBUG']
95
100
  require 'logger'
@@ -1,3 +1,3 @@
1
1
  module Localeapp
2
- VERSION = "0.6.8"
2
+ VERSION = "0.6.9"
3
3
  end
data/lib/localeapp.rb CHANGED
@@ -54,6 +54,9 @@ module Localeapp
54
54
  API_VERSION = "1"
55
55
  LOG_PREFIX = "** [Localeapp] "
56
56
 
57
+ class LocaleappError < StandardError; end
58
+ class PotentiallyInsecureYaml < LocaleappError; end
59
+
57
60
  class << self
58
61
  # An Localeapp configuration object.
59
62
  attr_accessor :configuration
@@ -115,6 +118,10 @@ module Localeapp
115
118
  end
116
119
 
117
120
  def load_yaml(contents)
121
+ if Localeapp.configuration.raise_on_insecure_yaml
122
+ raise Localeapp::PotentiallyInsecureYaml if contents =~ /!ruby\//
123
+ end
124
+
118
125
  if defined?(Psych) && defined?(Psych::VERSION)
119
126
  Psych.load(contents)
120
127
  else
data/localeapp.gemspec CHANGED
@@ -30,6 +30,6 @@ Gem::Specification.new do |s|
30
30
  s.add_development_dependency('rspec', '2.11.0')
31
31
  s.add_development_dependency('yard', '0.6.7')
32
32
  s.add_development_dependency('RedCloth', '4.2.9')
33
- s.add_development_dependency('aruba', '0.4.11')
33
+ s.add_development_dependency('aruba', '0.5.1')
34
34
  s.add_development_dependency('fakeweb', '1.3.0')
35
35
  end
@@ -206,7 +206,7 @@ require 'localeapp/rails'
206
206
 
207
207
  Localeapp.configure do |config|
208
208
  config.api_key = ENV['LOCALEAPP_API_KEY']
209
- config.poll_interval = 300 if defined?(Rails) && Rails.env.staging?
209
+ config.environment_name = ENV['LOCALEAPP_ENV'] unless ENV['LOCALEAPP_ENV'].nil?
210
210
  config.polling_environments = [:development, :staging]
211
211
  config.reloading_environments = [:development, :staging]
212
212
  config.sending_environments = [:development, :staging]
@@ -17,6 +17,10 @@ describe Localeapp::Updater, ".update(data)" do
17
17
  @updater.update(data)
18
18
  end
19
19
 
20
+ def load_yaml(locale)
21
+ YAML.load(File.read(File.join(@yml_dir, "#{locale}.yml")))
22
+ end
23
+
20
24
  it "adds, updates and deletes keys in the yml files" do
21
25
  do_update({
22
26
  'translations' => {
@@ -35,59 +39,19 @@ describe Localeapp::Updater, ".update(data)" do
35
39
  'locales' => %w{en es}
36
40
  })
37
41
 
38
- if defined? Psych
39
- if Psych::VERSION == '1.0.0'
40
- if RUBY_ENGINE == 'jruby'
41
- File.read(File.join(@yml_dir, 'en.yml')).should == <<-EN
42
- en:
43
- foo:
44
- monkey: hello
45
- night: the night
46
- space:
47
- blank: ''
48
- tilde:
49
- scalar1:
50
- scalar2:
51
- EN
52
- else
53
- File.read(File.join(@yml_dir, 'en.yml')).should == <<-EN
54
- en:
55
- foo:
56
- monkey: hello
57
- night: the night
58
- space: !!null
59
- blank: ''
60
- tilde: !!null
61
- scalar1: !!null
62
- scalar2: !!null
63
- EN
64
- end
65
- else
66
- File.read(File.join(@yml_dir, 'en.yml')).should == <<-EN
67
- en:
68
- foo:
69
- monkey: hello
70
- night: the night
71
- space:
72
- blank: ''
73
- tilde:
74
- scalar1:
75
- scalar2:
76
- EN
77
- end
78
- else
79
- File.read(File.join(@yml_dir, 'en.yml')).should == <<-EN
80
- en:
81
- blank: ""
82
- foo:
83
- monkey: hello
84
- night: "the night"
85
- scalar1: ~
86
- scalar2: ~
87
- space: ~
88
- tilde: ~
89
- EN
90
- end
42
+ load_yaml('en').should == {
43
+ 'en' => {
44
+ 'foo' => {
45
+ 'monkey' => 'hello',
46
+ 'night' => 'the night'
47
+ },
48
+ 'space' => nil,
49
+ 'blank' => '',
50
+ 'tilde' => nil,
51
+ 'scalar1' => nil,
52
+ 'scalar2' => nil,
53
+ }
54
+ }
91
55
  end
92
56
 
93
57
  it "deletes keys in the yml files when updates are empty" do
@@ -100,19 +64,14 @@ EN
100
64
  ],
101
65
  'locales' => %w{es}
102
66
  })
103
- if defined? Psych
104
- File.read(File.join(@yml_dir, 'es.yml')).should == <<-ES
105
- es:
106
- foo:
107
- monkey: Mono
108
- ES
109
- else
110
- File.read(File.join(@yml_dir, 'es.yml')).should == <<-ES
111
- es:
112
- foo:
113
- monkey: Mono
114
- ES
115
- end
67
+
68
+ load_yaml('es').should == {
69
+ 'es' => {
70
+ 'foo' => {
71
+ 'monkey' => 'Mono'
72
+ }
73
+ }
74
+ }
116
75
  end
117
76
 
118
77
  it "creates a new yml file if an unknown locale is passed" do
@@ -122,17 +81,12 @@ ES
122
81
  },
123
82
  'locales' => ['ja']
124
83
  })
125
- if defined? Psych
126
- File.read(File.join(@yml_dir, 'ja.yml')).should == <<-JA
127
- ja:
128
- foo: bar
129
- JA
130
- else
131
- File.read(File.join(@yml_dir, 'ja.yml')).should == <<-JA
132
- ja:
133
- foo: bar
134
- JA
135
- end
84
+
85
+ load_yaml('ja').should == {
86
+ 'ja' => {
87
+ 'foo' => 'bar'
88
+ }
89
+ }
136
90
  end
137
91
 
138
92
  it "doesn't create a new yml file if an unknown locale is passed but it has no translations" do
@@ -144,7 +98,7 @@ JA
144
98
  File.exist?(File.join(@yml_dir, 'ja.yml')).should be_false
145
99
  end
146
100
 
147
- if defined?(Psych) && Psych::VERSION >= "1.1.0"
101
+ if defined?(Psych) && Psych::VERSION >= "1.1.0" && !RUBY_PLATFORM == 'jruby'
148
102
  it "doesn't try to wrap long lines in the output" do
149
103
  do_update({
150
104
  'translations' => {
@@ -202,33 +156,21 @@ describe Localeapp::Updater, ".dump(data)" do
202
156
 
203
157
  it "replaces the content of an existing yml file" do
204
158
  filepath = File.join(@yml_dir, 'en.yml')
205
- content = lambda { File.read(filepath) }
206
- if defined? Psych
207
- expect { do_dump({'en' => {'updated' => 'content'}}) }.to change(content, :call).to <<-EN
208
- en:
209
- updated: content
210
- EN
211
- else
212
- expect { do_dump({'en' => {'updated' => 'content'}}) }.to change(content, :call).to <<-EN
213
- en:
214
- updated: content
215
- EN
216
- end
159
+ content = lambda { YAML.load(File.read(filepath)) }
160
+ expect { do_dump({'en' => {'updated' => 'content'}}) }.to change(content, :call).to({
161
+ 'en' => {
162
+ 'updated' => 'content'
163
+ }
164
+ })
217
165
  end
218
166
 
219
167
  it "creates a new yml file if an unknown locale is passed" do
220
168
  do_dump({'ja' => { 'foo' => 'bar'} })
221
- if defined? Psych
222
- File.read(File.join(@yml_dir, 'ja.yml')).should == <<-JA
223
- ja:
224
- foo: bar
225
- JA
226
- else
227
- File.read(File.join(@yml_dir, 'ja.yml')).should == <<-JA
228
- ja:
229
- foo: bar
230
- JA
231
- end
169
+ YAML.load(File.read(File.join(@yml_dir, 'ja.yml'))).should == {
170
+ 'ja' => {
171
+ 'foo' => 'bar'
172
+ }
173
+ }
232
174
  end
233
175
 
234
176
  it "doesn't change a yml file's permissions" do
@@ -0,0 +1,17 @@
1
+ require 'spec_helper'
2
+
3
+ describe Localeapp, "#load_yaml(content)" do
4
+ let(:bad_yaml) { "---\n- 1\n- 2\n- 3\n- !ruby/object:Object\n foo: 1\n" }
5
+
6
+ it "raises an exception if the content contains potentially insecure yaml" do
7
+ with_configuration(:raise_on_insecure_yaml => true) do
8
+ expect { Localeapp.load_yaml(bad_yaml) }.to raise_error(Localeapp::PotentiallyInsecureYaml)
9
+ end
10
+ end
11
+
12
+ it "doesn't raise if the raise_on_insecure_yaml setting is false" do
13
+ with_configuration(:raise_on_insecure_yaml => false) do
14
+ expect { Localeapp.load_yaml(bad_yaml) }.to_not raise_error
15
+ end
16
+ end
17
+ end
metadata CHANGED
@@ -1,13 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: localeapp
3
3
  version: !ruby/object:Gem::Version
4
- hash: 23
4
+ hash: 21
5
5
  prerelease:
6
6
  segments:
7
7
  - 0
8
8
  - 6
9
- - 8
10
- version: 0.6.8
9
+ - 9
10
+ version: 0.6.9
11
11
  platform: ruby
12
12
  authors:
13
13
  - Christopher Dell
@@ -16,12 +16,10 @@ autorequire:
16
16
  bindir: bin
17
17
  cert_chain: []
18
18
 
19
- date: 2012-11-23 00:00:00 Z
19
+ date: 2013-01-15 00:00:00 Z
20
20
  dependencies:
21
21
  - !ruby/object:Gem::Dependency
22
22
  name: i18n
23
- type: :runtime
24
- prerelease: false
25
23
  version_requirements: &id001 !ruby/object:Gem::Requirement
26
24
  none: false
27
25
  requirements:
@@ -31,11 +29,11 @@ dependencies:
31
29
  segments:
32
30
  - 0
33
31
  version: "0"
32
+ type: :runtime
34
33
  requirement: *id001
34
+ prerelease: false
35
35
  - !ruby/object:Gem::Dependency
36
36
  name: json
37
- type: :runtime
38
- prerelease: false
39
37
  version_requirements: &id002 !ruby/object:Gem::Requirement
40
38
  none: false
41
39
  requirements:
@@ -45,11 +43,11 @@ dependencies:
45
43
  segments:
46
44
  - 0
47
45
  version: "0"
46
+ type: :runtime
48
47
  requirement: *id002
48
+ prerelease: false
49
49
  - !ruby/object:Gem::Dependency
50
50
  name: rest-client
51
- type: :runtime
52
- prerelease: false
53
51
  version_requirements: &id003 !ruby/object:Gem::Requirement
54
52
  none: false
55
53
  requirements:
@@ -59,11 +57,11 @@ dependencies:
59
57
  segments:
60
58
  - 0
61
59
  version: "0"
60
+ type: :runtime
62
61
  requirement: *id003
62
+ prerelease: false
63
63
  - !ruby/object:Gem::Dependency
64
64
  name: ya2yaml
65
- type: :runtime
66
- prerelease: false
67
65
  version_requirements: &id004 !ruby/object:Gem::Requirement
68
66
  none: false
69
67
  requirements:
@@ -73,11 +71,11 @@ dependencies:
73
71
  segments:
74
72
  - 0
75
73
  version: "0"
74
+ type: :runtime
76
75
  requirement: *id004
76
+ prerelease: false
77
77
  - !ruby/object:Gem::Dependency
78
78
  name: gli
79
- type: :runtime
80
- prerelease: false
81
79
  version_requirements: &id005 !ruby/object:Gem::Requirement
82
80
  none: false
83
81
  requirements:
@@ -87,11 +85,11 @@ dependencies:
87
85
  segments:
88
86
  - 0
89
87
  version: "0"
88
+ type: :runtime
90
89
  requirement: *id005
90
+ prerelease: false
91
91
  - !ruby/object:Gem::Dependency
92
92
  name: rake
93
- type: :development
94
- prerelease: false
95
93
  version_requirements: &id006 !ruby/object:Gem::Requirement
96
94
  none: false
97
95
  requirements:
@@ -101,11 +99,11 @@ dependencies:
101
99
  segments:
102
100
  - 0
103
101
  version: "0"
102
+ type: :development
104
103
  requirement: *id006
104
+ prerelease: false
105
105
  - !ruby/object:Gem::Dependency
106
106
  name: rack
107
- type: :development
108
- prerelease: false
109
107
  version_requirements: &id007 !ruby/object:Gem::Requirement
110
108
  none: false
111
109
  requirements:
@@ -115,11 +113,11 @@ dependencies:
115
113
  segments:
116
114
  - 0
117
115
  version: "0"
116
+ type: :development
118
117
  requirement: *id007
118
+ prerelease: false
119
119
  - !ruby/object:Gem::Dependency
120
120
  name: rspec
121
- type: :development
122
- prerelease: false
123
121
  version_requirements: &id008 !ruby/object:Gem::Requirement
124
122
  none: false
125
123
  requirements:
@@ -131,11 +129,11 @@ dependencies:
131
129
  - 11
132
130
  - 0
133
131
  version: 2.11.0
132
+ type: :development
134
133
  requirement: *id008
134
+ prerelease: false
135
135
  - !ruby/object:Gem::Dependency
136
136
  name: yard
137
- type: :development
138
- prerelease: false
139
137
  version_requirements: &id009 !ruby/object:Gem::Requirement
140
138
  none: false
141
139
  requirements:
@@ -147,11 +145,11 @@ dependencies:
147
145
  - 6
148
146
  - 7
149
147
  version: 0.6.7
148
+ type: :development
150
149
  requirement: *id009
150
+ prerelease: false
151
151
  - !ruby/object:Gem::Dependency
152
152
  name: RedCloth
153
- type: :development
154
- prerelease: false
155
153
  version_requirements: &id010 !ruby/object:Gem::Requirement
156
154
  none: false
157
155
  requirements:
@@ -163,27 +161,27 @@ dependencies:
163
161
  - 2
164
162
  - 9
165
163
  version: 4.2.9
164
+ type: :development
166
165
  requirement: *id010
166
+ prerelease: false
167
167
  - !ruby/object:Gem::Dependency
168
168
  name: aruba
169
- type: :development
170
- prerelease: false
171
169
  version_requirements: &id011 !ruby/object:Gem::Requirement
172
170
  none: false
173
171
  requirements:
174
172
  - - "="
175
173
  - !ruby/object:Gem::Version
176
- hash: 25
174
+ hash: 9
177
175
  segments:
178
176
  - 0
179
- - 4
180
- - 11
181
- version: 0.4.11
177
+ - 5
178
+ - 1
179
+ version: 0.5.1
180
+ type: :development
182
181
  requirement: *id011
182
+ prerelease: false
183
183
  - !ruby/object:Gem::Dependency
184
184
  name: fakeweb
185
- type: :development
186
- prerelease: false
187
185
  version_requirements: &id012 !ruby/object:Gem::Requirement
188
186
  none: false
189
187
  requirements:
@@ -195,7 +193,9 @@ dependencies:
195
193
  - 3
196
194
  - 0
197
195
  version: 1.3.0
196
+ type: :development
198
197
  requirement: *id012
198
+ prerelease: false
199
199
  description: Synchronizes i18n translation keys and content with localeapp.com so you don't have to manage translations by hand.
200
200
  email:
201
201
  - chris@tigrish.com
@@ -286,6 +286,7 @@ files:
286
286
  - spec/localeapp/routes_spec.rb
287
287
  - spec/localeapp/sender_spec.rb
288
288
  - spec/localeapp/updater_spec.rb
289
+ - spec/localeapp_spec.rb
289
290
  - spec/spec_helper.rb
290
291
  - spec/support/i18n/missing_translation.rb
291
292
  - spec/support/localeapp_integration_data.rb
@@ -319,7 +320,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
319
320
  requirements: []
320
321
 
321
322
  rubyforge_project: localeapp
322
- rubygems_version: 1.8.6
323
+ rubygems_version: 1.8.24
323
324
  signing_key:
324
325
  specification_version: 3
325
326
  summary: Easy i18n translation management with localeapp.com
@@ -358,6 +359,7 @@ test_files:
358
359
  - spec/localeapp/routes_spec.rb
359
360
  - spec/localeapp/sender_spec.rb
360
361
  - spec/localeapp/updater_spec.rb
362
+ - spec/localeapp_spec.rb
361
363
  - spec/spec_helper.rb
362
364
  - spec/support/i18n/missing_translation.rb
363
365
  - spec/support/localeapp_integration_data.rb