diff options
-rw-r--r-- | Gemfile | 2 | ||||
-rw-r--r-- | Gemfile.lock | 4 | ||||
-rw-r--r-- | app/models/censor_rule.rb | 5 | ||||
-rw-r--r-- | app/models/info_request.rb | 5 | ||||
-rw-r--r-- | app/models/outgoing_message.rb | 47 | ||||
-rw-r--r-- | config/initializers/alaveteli.rb | 2 | ||||
-rw-r--r-- | config/packages.debian-wheezy | 1 | ||||
-rw-r--r-- | lib/attachment_to_html/adapters/pdf.rb | 4 | ||||
-rw-r--r-- | lib/configuration.rb | 4 | ||||
-rwxr-xr-x | script/mailin | 2 | ||||
-rw-r--r-- | spec/factories/info_requests.rb | 2 | ||||
-rw-r--r-- | spec/integration/download_request_spec.rb | 3 | ||||
-rw-r--r-- | spec/models/censor_rule_spec.rb | 36 | ||||
-rw-r--r-- | spec/models/outgoing_message_spec.rb | 109 | ||||
-rw-r--r-- | spec/models/post_redirect_spec.rb | 1 |
15 files changed, 176 insertions, 51 deletions
@@ -28,7 +28,7 @@ gem 'rails-i18n', '~> 0.7.3' gem 'recaptcha', '~> 0.3.1', :require => 'recaptcha/rails' gem 'rmagick', '~> 2.14.0' gem 'ruby-msg', '~> 1.5.0', :git => 'https://github.com/mysociety/ruby-msg.git' -gem 'secure_headers', '~> 1.3.4' +gem 'secure_headers', '~> 2.0.2' gem 'statistics2', '~> 0.54' gem 'syslog_protocol', '~> 0.9.2' gem 'thin', '~> 1.5.1' diff --git a/Gemfile.lock b/Gemfile.lock index b9e0f3380..9353b9145 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -244,7 +244,7 @@ GEM railties (~> 3.2.0) sass (>= 3.1.10) tilt (~> 1.3) - secure_headers (1.3.4) + secure_headers (2.0.2) simplecov (0.7.1) multi_json (~> 1.0) simplecov-html (~> 0.7.1) @@ -353,7 +353,7 @@ DEPENDENCIES ruby-debug (~> 0.10.4) ruby-msg (~> 1.5.0)! sass-rails (~> 3.2.3) - secure_headers (~> 1.3.4) + secure_headers (~> 2.0.2) spork-rails (~> 3.2.1) statistics2 (~> 0.54) syslog_protocol (~> 0.9.2) diff --git a/app/models/censor_rule.rb b/app/models/censor_rule.rb index 3b5c2d805..58170f237 100644 --- a/app/models/censor_rule.rb +++ b/app/models/censor_rule.rb @@ -42,6 +42,11 @@ class CensorRule < ActiveRecord::Base :user_id => nil, :public_body_id => nil } } + def apply_to_text(text_to_censor) + return nil if text_to_censor.nil? + text_to_censor.gsub(to_replace, replacement) + end + def apply_to_text!(text_to_censor) return nil if text_to_censor.nil? text_to_censor.gsub!(to_replace, replacement) diff --git a/app/models/info_request.rb b/app/models/info_request.rb index 3ce89be3b..3a81860ad 100644 --- a/app/models/info_request.rb +++ b/app/models/info_request.rb @@ -811,8 +811,9 @@ public # Text from the the initial request, for use in summary display def initial_request_text - return '' if outgoing_messages.empty? # mainly for use with incomplete fixtures - outgoing_messages.first.get_text_for_indexing + return '' if outgoing_messages.empty? + body_opts = { :censor_rules => applicable_censor_rules } + outgoing_messages.first.try(:get_text_for_indexing, true, body_opts) or '' end # Returns index of last event which is described or nil if none described. diff --git a/app/models/outgoing_message.rb b/app/models/outgoing_message.rb index c2c8ef4f2..4f6318b3d 100644 --- a/app/models/outgoing_message.rb +++ b/app/models/outgoing_message.rb @@ -140,22 +140,28 @@ class OutgoingMessage < ActiveRecord::Base end end - def body - ret = read_attribute(:body) - if ret.nil? - return ret + # Public: The body text of the OutgoingMessage. The text is cleaned and + # CensorRules are applied. + # + # options - Hash of options + # :censor_rules - Array of CensorRules to apply. Defaults to the + # applicable_censor_rules of the associated + # InfoRequest. (optional) + # + # Returns a String + def body(options = {}) + text = raw_body.dup + return text if text.nil? + + text = clean_text(text) + + # Use the given censor_rules; otherwise fetch them from the associated + # info_request + censor_rules = options.fetch(:censor_rules) do + info_request.try(:applicable_censor_rules) or [] end - ret = ret.dup - ret.strip! - ret.gsub!(/(?:\n\s*){2,}/, "\n\n") # remove excess linebreaks that unnecessarily space it out - - # Remove things from censor rules - unless info_request.nil? - self.info_request.apply_censor_rules_to_text!(ret) - end - - ret + censor_rules.reduce(text) { |text, rule| rule.apply_to_text(text) } end def raw_body @@ -227,8 +233,12 @@ class OutgoingMessage < ActiveRecord::Base end # Returns text for indexing / text display - def get_text_for_indexing(strip_salutation = true) - text = body.strip + def get_text_for_indexing(strip_salutation = true, opts = {}) + if opts.empty? + text = body.strip + else + text = body(opts).strip + end # Remove salutation text.sub!(/Dear .+,/, "") if strip_salutation @@ -332,6 +342,11 @@ class OutgoingMessage < ActiveRecord::Base errors.add(:what_doing_dummy, _('Please choose what sort of reply you are making.')) end end + + # remove excess linebreaks that unnecessarily space it out + def clean_text(text) + text.strip.gsub(/(?:\n\s*){2,}/, "\n\n") + end end diff --git a/config/initializers/alaveteli.rb b/config/initializers/alaveteli.rb index 035d744b8..53a9456d2 100644 --- a/config/initializers/alaveteli.rb +++ b/config/initializers/alaveteli.rb @@ -10,7 +10,7 @@ load "debug_helpers.rb" load "util.rb" # Application version -ALAVETELI_VERSION = '0.21.0.26' +ALAVETELI_VERSION = '0.21.0.27' # Add new inflection rules using the following format # (all these examples are active by default): diff --git a/config/packages.debian-wheezy b/config/packages.debian-wheezy index 4129aa930..ff1a55d84 100644 --- a/config/packages.debian-wheezy +++ b/config/packages.debian-wheezy @@ -35,3 +35,4 @@ wkhtmltopdf-static wv xapian-tools xlhtml +ttf-bitstream-vera diff --git a/lib/attachment_to_html/adapters/pdf.rb b/lib/attachment_to_html/adapters/pdf.rb index 3c18e9d4a..a010b0342 100644 --- a/lib/attachment_to_html/adapters/pdf.rb +++ b/lib/attachment_to_html/adapters/pdf.rb @@ -71,10 +71,10 @@ module AttachmentToHTML @converted ||= Dir.chdir(tmpdir) do tempfile = create_tempfile(text) - html = AlaveteliExternalCommand.run("pdftohtml", "-nodrm", "-zoom", "1.0", "-stdout", "-enc", "UTF-8", - "-noframes", tempfile.path, :timeout => 30, :binary_output => false + "-noframes", "./#{File.basename(tempfile.path)}", + :timeout => 30, :binary_output => false ) cleanup_tempfile(tempfile) diff --git a/lib/configuration.rb b/lib/configuration.rb index 89f148602..b60cc102b 100644 --- a/lib/configuration.rb +++ b/lib/configuration.rb @@ -33,8 +33,8 @@ module AlaveteliConfiguration :DOMAIN => 'localhost:3000', :DONATION_URL => '', :ENABLE_WIDGETS => false, - :EXCEPTION_NOTIFICATIONS_FROM => '', - :EXCEPTION_NOTIFICATIONS_TO => '', + :EXCEPTION_NOTIFICATIONS_FROM => 'errors@localhost', + :EXCEPTION_NOTIFICATIONS_TO => 'user-support@localhost', :FORCE_REGISTRATION_ON_NEW_REQUEST => false, :FORCE_SSL => true, :FORWARD_NONBOUNCE_RESPONSES_TO => 'user-support@localhost', diff --git a/script/mailin b/script/mailin index 65f9d06f2..5f2a9c243 100755 --- a/script/mailin +++ b/script/mailin @@ -23,7 +23,7 @@ then SUBJ="Mail import error for $OPTION_DOMAIN" BODY="There was an error code $ERROR_CODE returned by the RequestMailer.receive command in script/mailin. See attached for details. This might be quite serious, such as the database was down, or might be an email with corrupt headers that Rails is choking on. We returned the email with an exit code 75, which for Exim at least instructs the MTA to try again later. A well configured installation of this code will separately have had Exim make a backup copy of the email in a separate mailbox, just in case." FROM="$OPTION_BLACKHOLE_PREFIX@$OPTION_INCOMING_EMAIL_DOMAIN" - /usr/bin/mutt -e "set use_envelope_from" -e "set envelope_from_address=$FROM" -s "$SUBJ" -a "$OUTPUT" "$INPUT" -- "$OPTION_FORWARD_NONBOUNCE_RESPONSES_TO" <<<"$BODY" + /usr/bin/mutt -e "set use_envelope_from" -e "set envelope_from_address=$FROM" -s "$SUBJ" -a "$OUTPUT" "$INPUT" -- "$OPTION_EXCEPTION_NOTIFICATIONS_TO" <<<"$BODY" # tell exim error was temporary, so try again later (no point bouncing message to authority) rm -f "$INPUT" "$OUTPUT" diff --git a/spec/factories/info_requests.rb b/spec/factories/info_requests.rb index 8052625cd..ee96bfa89 100644 --- a/spec/factories/info_requests.rb +++ b/spec/factories/info_requests.rb @@ -1,7 +1,7 @@ FactoryGirl.define do factory :info_request do - title "Example Title" + sequence(:title) { |n| "Example Title #{n}" } public_body user diff --git a/spec/integration/download_request_spec.rb b/spec/integration/download_request_spec.rb index 48b42b11d..c73b85866 100644 --- a/spec/integration/download_request_spec.rb +++ b/spec/integration/download_request_spec.rb @@ -143,7 +143,8 @@ describe 'when making a zipfile available' do it "should update the contents of the zipfile when the request changes" do - info_request = FactoryGirl.create(:info_request_with_incoming) + info_request = FactoryGirl.create(:info_request_with_incoming, + :title => 'Example Title') request_owner = login(info_request.user) inspect_zip_download(request_owner, info_request) do |zip| zip.count.should == 1 # just the message diff --git a/spec/models/censor_rule_spec.rb b/spec/models/censor_rule_spec.rb index 4ecd2d3e1..77b8cd07a 100644 --- a/spec/models/censor_rule_spec.rb +++ b/spec/models/censor_rule_spec.rb @@ -17,6 +17,42 @@ require File.expand_path(File.dirname(__FILE__) + '/../spec_helper') +describe CensorRule do + + describe :apply_to_text do + + it 'applies the rule to the text' do + rule = FactoryGirl.build(:censor_rule, :text => 'secret') + text = 'Some secret text' + expect(rule.apply_to_text(text)).to eq('Some [REDACTED] text') + end + + it 'does not mutate the input' do + rule = FactoryGirl.build(:censor_rule, :text => 'secret') + text = 'Some secret text' + rule.apply_to_text(text) + expect(text).to eq('Some secret text') + end + + it 'returns the text if the rule is unmatched' do + rule = FactoryGirl.build(:censor_rule, :text => 'secret') + text = 'Some text' + expect(rule.apply_to_text(text)).to eq('Some text') + end + end + + describe :apply_to_text! do + + it 'mutates the input' do + rule = FactoryGirl.build(:censor_rule, :text => 'secret') + text = 'Some secret text' + rule.apply_to_text!(text) + expect(text).to eq('Some [REDACTED] text') + end + + end +end + describe CensorRule, "substituting things" do describe 'when using a text rule' do diff --git a/spec/models/outgoing_message_spec.rb b/spec/models/outgoing_message_spec.rb index a3e2d1c68..051f263d2 100644 --- a/spec/models/outgoing_message_spec.rb +++ b/spec/models/outgoing_message_spec.rb @@ -18,6 +18,93 @@ require File.expand_path(File.dirname(__FILE__) + '/../spec_helper') +describe OutgoingMessage do + + describe :initialize do + + it 'does not censor the #body' do + attrs = { :status => 'ready', + :message_type => 'initial_request', + :body => 'abc', + :what_doing => 'normal_sort' } + + message = FactoryGirl.create(:outgoing_message, attrs) + + OutgoingMessage.any_instance.should_not_receive(:body).and_call_original + OutgoingMessage.find(message.id) + end + + end + + describe :body do + + it 'returns the body attribute' do + attrs = { :status => 'ready', + :message_type => 'initial_request', + :body => 'abc', + :what_doing => 'normal_sort' } + + message = FactoryGirl.build(:outgoing_message, attrs) + expect(message.body).to eq('abc') + end + + it 'strips the body of leading and trailing whitespace' do + attrs = { :status => 'ready', + :message_type => 'initial_request', + :body => ' abc ', + :what_doing => 'normal_sort' } + + message = FactoryGirl.build(:outgoing_message, attrs) + expect(message.body).to eq('abc') + end + + it 'removes excess linebreaks that unnecessarily space it out' do + attrs = { :status => 'ready', + :message_type => 'initial_request', + :body => "ab\n\nc\n\n", + :what_doing => 'normal_sort' } + + message = FactoryGirl.build(:outgoing_message, attrs) + expect(message.body).to eq("ab\n\nc") + end + + it "applies the associated request's censor rules to the text" do + attrs = { :status => 'ready', + :message_type => 'initial_request', + :body => 'This sensitive text contains secret info!', + :what_doing => 'normal_sort' } + message = FactoryGirl.build(:outgoing_message, attrs) + + rules = [FactoryGirl.build(:censor_rule, :text => 'secret'), + FactoryGirl.build(:censor_rule, :text => 'sensitive')] + InfoRequest.any_instance.stub(:censor_rules).and_return(rules) + + expected = 'This [REDACTED] text contains [REDACTED] info!' + expect(message.body).to eq(expected) + end + + it "applies the given censor rules to the text" do + attrs = { :status => 'ready', + :message_type => 'initial_request', + :body => 'This sensitive text contains secret info!', + :what_doing => 'normal_sort' } + message = FactoryGirl.build(:outgoing_message, attrs) + + request_rules = [FactoryGirl.build(:censor_rule, :text => 'secret'), + FactoryGirl.build(:censor_rule, :text => 'sensitive')] + InfoRequest.any_instance.stub(:censor_rules).and_return(request_rules) + + censor_rules = [FactoryGirl.build(:censor_rule, :text => 'text'), + FactoryGirl.build(:censor_rule, :text => 'contains')] + + expected = 'This sensitive [REDACTED] [REDACTED] secret info!' + expect(message.body(:censor_rules => censor_rules)).to eq(expected) + end + + end + +end + describe OutgoingMessage, " when making an outgoing message" do before do @@ -57,6 +144,7 @@ describe OutgoingMessage, " when making an outgoing message" do info_request = mock_model(InfoRequest, :public_body => public_body, :url_title => 'a_test_title', :title => 'A test title', + :applicable_censor_rules => [], :apply_censor_rules_to_text! => nil, :is_batch_request_template? => false) outgoing_message = OutgoingMessage.new({ @@ -155,27 +243,6 @@ describe OutgoingMessage, " when making an outgoing message" do end end - -describe OutgoingMessage, " when censoring data" do - - before do - @om = outgoing_messages(:useless_outgoing_message) - - @censor_rule = CensorRule.new() - @censor_rule.text = "dog" - @censor_rule.replacement = "cat" - @censor_rule.last_edit_editor = "unknown" - @censor_rule.last_edit_comment = "none" - - @om.info_request.censor_rules << @censor_rule - end - - it "should apply censor rules to outgoing messages" do - @om.read_attribute(:body).should match(/fancy dog/) - @om.body.should match(/fancy cat/) - end -end - describe OutgoingMessage, "when validating the format of the message body" do it 'should handle a salutation with a bracket in it' do diff --git a/spec/models/post_redirect_spec.rb b/spec/models/post_redirect_spec.rb index 70b221f10..750e47cc3 100644 --- a/spec/models/post_redirect_spec.rb +++ b/spec/models/post_redirect_spec.rb @@ -76,7 +76,6 @@ describe PostRedirect, " when accessing values" do pr = PostRedirect.new utf8_params = "--- \n:foo: !binary |\n 0KLQvtCz0LDRiCDR\n" pr.reason_params_yaml = utf8_params - puts pr.reason_params pr.reason_params[:foo].encoding.to_s.should == 'UTF-8' if pr.reason_params[:foo].respond_to?(:encoding) end end |