Skip to content

Commit 60ca1ec

Browse files
authored
Merge pull request #1510 from alphagov/add-input-length-validations
Add input length validations
2 parents 89226d0 + 653d86e commit 60ca1ec

10 files changed

Lines changed: 254 additions & 74 deletions

File tree

app/models/question/address.rb

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ class Address < Question::QuestionBase
1111

1212
validate :uk_address_valid?, unless: :is_international_address?
1313
validate :postcode, :invalid_postcode?, unless: :is_international_address?
14-
validate :international_adress_valid?, if: :is_international_address?
14+
validate :international_address_valid?, if: :is_international_address?
1515

1616
def postcode=(str)
1717
super str.present? ? UKPostcode.parse(str).to_s : str
@@ -41,16 +41,22 @@ def uk_address_valid?
4141
return if skipping_question?
4242

4343
errors.add(:address1, :blank) if address1.blank?
44+
errors.add(:address1, :too_long) if address1.present? && address1.length > 499
45+
errors.add(:address2, :too_long) if address2.present? && address2.length > 499
4446
errors.add(:town_or_city, :blank) if town_or_city.blank?
47+
errors.add(:town_or_city, :too_long) if town_or_city.present? && town_or_city.length > 499
48+
errors.add(:county, :too_long) if county.present? && county.length > 499
4549
errors.add(:postcode, :blank) if postcode.blank?
4650
errors
4751
end
4852

49-
def international_adress_valid?
53+
def international_address_valid?
5054
return if skipping_question?
5155

5256
errors.add(:street_address, :blank) if street_address.blank?
57+
errors.add(:street_address, :too_long) if street_address.present? && street_address.length > 4999
5358
errors.add(:country, :blank) if country.blank?
59+
errors.add(:country, :too_long) if country.present? && country.length > 499
5460
errors
5561
end
5662
end

app/models/question/email.rb

Lines changed: 1 addition & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,7 @@
11
module Question
22
class Email < Question::QuestionBase
3-
# see https://design-system.service.gov.uk/patterns/email-addresses/
4-
# This model contains very lite checking - only that an @ exists. Pay and
5-
# notify both have good examples of better email validation checks and ways
6-
# to help users enter the right value
7-
8-
EMAIL_REGEX = /.*@.*/
93
attribute :email
104
validates :email, presence: true, unless: :is_optional?
11-
validates :email, format: { with: EMAIL_REGEX, message: :invalid_email }, allow_blank: true
5+
validates :email, email_address: { message: :invalid_email }, allow_blank: true
126
end
137
end

app/models/question/name.rb

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -6,12 +6,9 @@ class Name < Question::QuestionBase
66
attribute :middle_names
77
attribute :last_name
88

9+
validates :title, length: { maximum: 99 }, allow_blank: true
910
validate :full_name_valid?, if: :is_full_name?
10-
validate :first_and_last_name_valid?, unless: :is_full_name?
11-
12-
def validate_title
13-
needs_title? && !is_optional?
14-
end
11+
validate :first_middle_and_last_name_valid?, unless: :is_full_name?
1512

1613
def needs_title?
1714
answer_settings.present? && answer_settings&.title_needed == "true"
@@ -34,14 +31,18 @@ def full_name_valid?
3431
return if skipping_question?
3532

3633
errors.add(:full_name, :blank) if full_name.blank?
34+
errors.add(:full_name, :too_long) if full_name.present? && full_name.length > 499
3735
errors
3836
end
3937

40-
def first_and_last_name_valid?
38+
def first_middle_and_last_name_valid?
4139
return if skipping_question?
4240

4341
errors.add(:first_name, :blank) if first_name.blank?
42+
errors.add(:first_name, :too_long) if first_name.present? && first_name.length > 499
43+
errors.add(:middle_names, :too_long) if include_middle_name? && middle_names.present? && middle_names.length > 499
4444
errors.add(:last_name, :blank) if last_name.blank?
45+
errors.add(:last_name, :too_long) if last_name.present? && last_name.length > 499
4546
errors
4647
end
4748

app/models/question/number.rb

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,5 +3,6 @@ class Number < QuestionBase
33
attribute :number
44
validates :number, presence: true, unless: :is_optional?
55
validates :number, numericality: { greater_than_or_equal_to: 0 }, allow_blank: true
6+
validates :number, length: { maximum: 499 }, allow_blank: true
67
end
78
end

config/locales/cy.yml

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -95,15 +95,23 @@ cy:
9595
attributes:
9696
address1:
9797
blank: Rhowch linell gyntaf y cyfeiriad
98+
too_long: The first line of the address must be shorter than 500 characters
99+
address2:
100+
too_long: The second line of the address must be shorter than 500 characters
98101
country:
99102
blank: Rhowch y wlad
103+
too_long: The country must be shorter than 500 characters
104+
county:
105+
too_long: The county must be shorter than 500 characters
100106
postcode:
101107
blank: Rhowch y cod post
102108
invalid_postcode: Rhowch god post gwirioneddol
103109
street_address:
104110
blank: Rhowch y cyfeiriad
111+
too_long: The address must be shorter than 5,000 characters
105112
town_or_city:
106113
blank: Rhowch y dref neu’r ddinas
114+
too_long: The town or city must be shorter than 500 characters
107115
question/date:
108116
attributes:
109117
date:
@@ -130,10 +138,17 @@ cy:
130138
attributes:
131139
first_name:
132140
blank: Rhowch enw cyntaf
141+
too_long: The first name must be shorter than 500 characters
133142
full_name:
134143
blank: Rhowch enw
144+
too_long: The name must be shorter than 500 characters
135145
last_name:
136146
blank: Rhowch enw olaf
147+
too_long: The last name must be shorter than 500 characters
148+
middle_names:
149+
too_long: The middle name must be shorter than 500 characters
150+
title:
151+
too_long: The title must be shorter than 100 characters
137152
question/national_insurance_number:
138153
attributes:
139154
national_insurance_number:
@@ -145,6 +160,7 @@ cy:
145160
blank: Rhowch rif
146161
greater_than_or_equal_to: Rhaid i’r ateb fod yn fwy na 10 neu’n hafal i 0
147162
not_a_number: Rhaid i’r ateb fod yn rhif
163+
too_long: The number must be shorter than 500 digits
148164
question/organisation_name:
149165
attributes:
150166
text:

config/locales/en.yml

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -95,15 +95,23 @@ en:
9595
attributes:
9696
address1:
9797
blank: Enter the first line of the address
98+
too_long: The first line of the address must be shorter than 500 characters
99+
address2:
100+
too_long: The second line of the address must be shorter than 500 characters
98101
country:
99102
blank: Enter the country
103+
too_long: The country must be shorter than 500 characters
104+
county:
105+
too_long: The county must be shorter than 500 characters
100106
postcode:
101107
blank: Enter the postcode
102108
invalid_postcode: Enter a full UK postcode
103109
street_address:
104110
blank: Enter the address
111+
too_long: The address must be shorter than 5,000 characters
105112
town_or_city:
106113
blank: Enter the town or city
114+
too_long: The town or city must be shorter than 500 characters
107115
question/date:
108116
attributes:
109117
date:
@@ -130,10 +138,17 @@ en:
130138
attributes:
131139
first_name:
132140
blank: Enter a first name
141+
too_long: The first name must be shorter than 500 characters
133142
full_name:
134143
blank: Enter a name
144+
too_long: The name must be shorter than 500 characters
135145
last_name:
136146
blank: Enter a last name
147+
too_long: The last name must be shorter than 500 characters
148+
middle_names:
149+
too_long: The middle name must be shorter than 500 characters
150+
title:
151+
too_long: The title must be shorter than 100 characters
137152
question/national_insurance_number:
138153
attributes:
139154
national_insurance_number:
@@ -145,6 +160,7 @@ en:
145160
blank: Enter a number
146161
greater_than_or_equal_to: The answer must be greater than or equal to 0
147162
not_a_number: The answer must be a number
163+
too_long: The number must be shorter than 500 digits
148164
question/organisation_name:
149165
attributes:
150166
text:

spec/models/question/address_spec.rb

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -139,6 +139,44 @@
139139
end
140140
end
141141
end
142+
143+
describe "length validations" do
144+
before do
145+
question.address1 = "a" * 499
146+
question.address2 = "a" * 499
147+
question.town_or_city = "a" * 499
148+
question.county = "a" * 499
149+
question.postcode = "LS11AF"
150+
end
151+
152+
it "is valid when all fields have the maximum allowed length" do
153+
expect(question).to be_valid
154+
end
155+
156+
it "is invalid when the first line is too long" do
157+
question.address1 = "a" * 500
158+
expect(question).not_to be_valid
159+
expect(question.errors[:address1]).to include(I18n.t("activemodel.errors.models.question/address.attributes.address1.too_long"))
160+
end
161+
162+
it "is invalid when the second line is too long" do
163+
question.address2 = "a" * 500
164+
expect(question).not_to be_valid
165+
expect(question.errors[:address2]).to include(I18n.t("activemodel.errors.models.question/address.attributes.address2.too_long"))
166+
end
167+
168+
it "is invalid when the town or city is too long" do
169+
question.town_or_city = "a" * 500
170+
expect(question).not_to be_valid
171+
expect(question.errors[:town_or_city]).to include(I18n.t("activemodel.errors.models.question/address.attributes.town_or_city.too_long"))
172+
end
173+
174+
it "is invalid when the county is too long" do
175+
question.county = "a" * 500
176+
expect(question).not_to be_valid
177+
expect(question.errors[:county]).to include(I18n.t("activemodel.errors.models.question/address.attributes.county.too_long"))
178+
end
179+
end
142180
end
143181

144182
context "when the address is an international address" do
@@ -238,6 +276,29 @@
238276
end
239277
end
240278
end
279+
280+
describe "length validations" do
281+
before do
282+
question.street_address = "a" * 4999
283+
question.country = "a" * 499
284+
end
285+
286+
it "is valid when all fields have the maximum allowed length" do
287+
expect(question).to be_valid
288+
end
289+
290+
it "is invalid when the street address is too long" do
291+
question.street_address = "a" * 5000
292+
expect(question).not_to be_valid
293+
expect(question.errors[:street_address]).to include(I18n.t("activemodel.errors.models.question/address.attributes.street_address.too_long"))
294+
end
295+
296+
it "is invalid when the country is too long" do
297+
question.country = "a" * 5000
298+
expect(question).not_to be_valid
299+
expect(question.errors[:country]).to include(I18n.t("activemodel.errors.models.question/address.attributes.country.too_long"))
300+
end
301+
end
241302
end
242303

243304
describe "#is_international_address?" do

spec/models/question/email_spec.rb

Lines changed: 43 additions & 59 deletions
Original file line numberDiff line numberDiff line change
@@ -10,52 +10,60 @@
1010

1111
it_behaves_like "a question model"
1212

13-
context "when given an empty string or nil" do
14-
it "returns invalid with blank email" do
15-
expect(question).not_to be_valid
16-
expect(question.errors[:email]).to include(I18n.t("activemodel.errors.models.question/email.attributes.email.blank"))
17-
end
13+
shared_examples "format validations" do
14+
context "when given a valid email address" do
15+
let(:email) { Faker::Internet.email }
1816

19-
it "returns invalid with empty string" do
20-
question.email = ""
21-
expect(question).not_to be_valid
22-
expect(question.errors[:email]).to include(I18n.t("activemodel.errors.models.question/email.attributes.email.blank"))
23-
expect(question.errors[:email]).not_to include(I18n.t("activemodel.errors.models.question/email.attributes.email.invalid_email"))
24-
end
17+
before do
18+
question.email = email
19+
end
2520

26-
it "shows as a blank string" do
27-
expect(question.show_answer).to eq ""
21+
it "is valid" do
22+
expect(question).to be_valid
23+
end
24+
25+
it "is included in show_answer" do
26+
expect(question.show_answer).to eq email
27+
end
28+
29+
it "returns the email address in show_answer_in_csv" do
30+
expect(question.show_answer_in_csv).to eq(Hash[question_text, email])
31+
end
2832
end
2933

30-
it "returns a hash with an blank value for show_answer_in_csv" do
31-
expect(question.show_answer_in_csv).to eq(Hash[question_text, ""])
34+
context "when given an invalid email address" do
35+
it "is invalid" do
36+
question.email = "no-tld@domain"
37+
expect(question).not_to be_valid
38+
expect(question.errors[:email]).to include(I18n.t("activemodel.errors.models.question/email.attributes.email.invalid_email"))
39+
end
3240
end
3341
end
3442

35-
context "when given a string with an @ symbol in" do
36-
before do
37-
question.email = " @ "
38-
end
43+
context "when question is mandatory" do
44+
context "when given an empty string or nil" do
45+
it "returns invalid with blank email" do
46+
expect(question).not_to be_valid
47+
expect(question.errors[:email]).to include(I18n.t("activemodel.errors.models.question/email.attributes.email.blank"))
48+
end
3949

40-
it "validates" do
41-
expect(question).to be_valid
42-
end
50+
it "returns invalid with empty string" do
51+
question.email = ""
52+
expect(question).not_to be_valid
53+
expect(question.errors[:email]).to include(I18n.t("activemodel.errors.models.question/email.attributes.email.blank"))
54+
expect(question.errors[:email]).not_to include(I18n.t("activemodel.errors.models.question/email.attributes.email.invalid_email"))
55+
end
4356

44-
it "is included in show_answer" do
45-
expect(question.show_answer).to eq " @ "
46-
end
57+
it "shows as a blank string" do
58+
expect(question.show_answer).to eq ""
59+
end
4760

48-
it "returns the email address in show_answer_in_csv" do
49-
expect(question.show_answer_in_csv).to eq(Hash[question_text, " @ "])
61+
it "returns a hash with an blank value for show_answer_in_csv" do
62+
expect(question.show_answer_in_csv).to eq(Hash[question_text, ""])
63+
end
5064
end
51-
end
5265

53-
context "when given a string without an @ symbol in" do
54-
it "does not validate an address without an @" do
55-
question.email = "not an email address"
56-
expect(question).not_to be_valid
57-
expect(question.errors[:email]).to include(I18n.t("activemodel.errors.models.question/email.attributes.email.invalid_email"))
58-
end
66+
include_examples "format validations"
5967
end
6068

6169
context "when question is optional" do
@@ -81,30 +89,6 @@
8189
expect(question.show_answer_in_csv).to eq(Hash[question_text, ""])
8290
end
8391

84-
context "when given a string with an @ symbol in" do
85-
before do
86-
question.email = " @ "
87-
end
88-
89-
it "validates" do
90-
expect(question).to be_valid
91-
end
92-
93-
it "is included in show_answer" do
94-
expect(question.show_answer).to eq " @ "
95-
end
96-
97-
it "returns the email address in show_answer_in_csv" do
98-
expect(question.show_answer_in_csv).to eq(Hash[question_text, " @ "])
99-
end
100-
end
101-
102-
context "when given a string without an @ symbol in" do
103-
it "does not validate an address without an @" do
104-
question.email = "not an email address"
105-
expect(question).not_to be_valid
106-
expect(question.errors[:email]).to include(I18n.t("activemodel.errors.models.question/email.attributes.email.invalid_email"))
107-
end
108-
end
92+
include_examples "format validations"
10993
end
11094
end

0 commit comments

Comments
 (0)