formvalidation_test.go (12438B)
1 // GoToSocial 2 // Copyright (C) GoToSocial Authors admin@gotosocial.org 3 // SPDX-License-Identifier: AGPL-3.0-or-later 4 // 5 // This program is free software: you can redistribute it and/or modify 6 // it under the terms of the GNU Affero General Public License as published by 7 // the Free Software Foundation, either version 3 of the License, or 8 // (at your option) any later version. 9 // 10 // This program is distributed in the hope that it will be useful, 11 // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 // GNU Affero General Public License for more details. 14 // 15 // You should have received a copy of the GNU Affero General Public License 16 // along with this program. If not, see <http://www.gnu.org/licenses/>. 17 18 package validate_test 19 20 import ( 21 "errors" 22 "fmt" 23 "testing" 24 25 "github.com/stretchr/testify/suite" 26 "github.com/superseriousbusiness/gotosocial/internal/config" 27 "github.com/superseriousbusiness/gotosocial/internal/gtsmodel" 28 "github.com/superseriousbusiness/gotosocial/internal/validate" 29 ) 30 31 type ValidationTestSuite struct { 32 suite.Suite 33 } 34 35 func (suite *ValidationTestSuite) TestCheckPasswordStrength() { 36 empty := "" 37 terriblePassword := "password" 38 weakPassword := "OKPassword" 39 shortPassword := "Ok12" 40 specialPassword := "Ok12%" 41 longPassword := "thisisafuckinglongpasswordbutnospecialchars" 42 tooLong := "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed euismod, ante id iaculis suscipit, nibh nibh varius enim, eget euismod augue augue eget mi. Praesent tincidunt, ex id finibus congue, enim nunc euismod nulla, id tincidunt ipsum neque at nunc. Sed id convallis libero. Sed euismod augue augue eget mi. Praesent tincidunt, ex id finibus congue, enim nunc euismod nulla, id tincidunt ipsum neque at nunc. Sed id convallis libero. Sed euismod augue augue eget mi. Praesent tincidunt, ex id finibus congue, enim nunc euismod nulla, id tincidunt ipsum neque at nunc." 43 strongPassword := "3dX5@Zc%mV*W2MBNEy$@" 44 var err error 45 46 err = validate.NewPassword(empty) 47 if suite.Error(err) { 48 suite.Equal(errors.New("no password provided"), err) 49 } 50 51 err = validate.NewPassword(terriblePassword) 52 if suite.Error(err) { 53 suite.Equal(errors.New("password is only 62% strength, try including more special characters, using uppercase letters, using numbers or using a longer password"), err) 54 } 55 56 err = validate.NewPassword(weakPassword) 57 if suite.Error(err) { 58 suite.Equal(errors.New("password is only 95% strength, try including more special characters, using numbers or using a longer password"), err) 59 } 60 61 err = validate.NewPassword(shortPassword) 62 if suite.Error(err) { 63 suite.Equal(errors.New("password is only 39% strength, try including more special characters or using a longer password"), err) 64 } 65 66 err = validate.NewPassword(specialPassword) 67 if suite.Error(err) { 68 suite.Equal(errors.New("password is only 53% strength, try including more special characters or using a longer password"), err) 69 } 70 71 err = validate.NewPassword(longPassword) 72 if suite.NoError(err) { 73 suite.Equal(nil, err) 74 } 75 76 err = validate.NewPassword(tooLong) 77 if suite.Error(err) { 78 suite.Equal(errors.New("password should be no more than 256 chars"), err) 79 } 80 81 err = validate.NewPassword(strongPassword) 82 if suite.NoError(err) { 83 suite.Equal(nil, err) 84 } 85 } 86 87 func (suite *ValidationTestSuite) TestValidateUsername() { 88 empty := "" 89 tooLong := "holycrapthisisthelongestusernameiveeverseeninmylifethatstoomuchman" 90 withSpaces := "this username has spaces in it" 91 weirdChars := "thisusername&&&&&&&istooweird!!" 92 leadingSpace := " see_that_leading_space" 93 trailingSpace := "thisusername_ends_with_a_space " 94 newlines := "this_is\n_almost_ok" 95 goodUsername := "this_is_a_good_username" 96 singleChar := "s" 97 var err error 98 99 err = validate.Username(empty) 100 suite.EqualError(err, "no username provided") 101 102 err = validate.Username(tooLong) 103 suite.EqualError(err, fmt.Sprintf("given username %s was invalid: must contain only lowercase letters, numbers, and underscores, max 64 characters", tooLong)) 104 105 err = validate.Username(withSpaces) 106 suite.EqualError(err, fmt.Sprintf("given username %s was invalid: must contain only lowercase letters, numbers, and underscores, max 64 characters", withSpaces)) 107 108 err = validate.Username(weirdChars) 109 suite.EqualError(err, fmt.Sprintf("given username %s was invalid: must contain only lowercase letters, numbers, and underscores, max 64 characters", weirdChars)) 110 111 err = validate.Username(leadingSpace) 112 suite.EqualError(err, fmt.Sprintf("given username %s was invalid: must contain only lowercase letters, numbers, and underscores, max 64 characters", leadingSpace)) 113 114 err = validate.Username(trailingSpace) 115 suite.EqualError(err, fmt.Sprintf("given username %s was invalid: must contain only lowercase letters, numbers, and underscores, max 64 characters", trailingSpace)) 116 117 err = validate.Username(newlines) 118 suite.EqualError(err, fmt.Sprintf("given username %s was invalid: must contain only lowercase letters, numbers, and underscores, max 64 characters", newlines)) 119 120 err = validate.Username(goodUsername) 121 suite.NoError(err) 122 123 err = validate.Username(singleChar) 124 suite.NoError(err) 125 } 126 127 func (suite *ValidationTestSuite) TestValidateEmail() { 128 empty := "" 129 notAnEmailAddress := "this-is-no-email-address!" 130 almostAnEmailAddress := "@thisisalmostan@email.address" 131 aWebsite := "https://thisisawebsite.com" 132 emailAddress := "thisis.actually@anemail.address" 133 var err error 134 135 err = validate.Email(empty) 136 if suite.Error(err) { 137 suite.Equal(errors.New("no email provided"), err) 138 } 139 140 err = validate.Email(notAnEmailAddress) 141 if suite.Error(err) { 142 suite.Equal(errors.New("mail: missing '@' or angle-addr"), err) 143 } 144 145 err = validate.Email(almostAnEmailAddress) 146 if suite.Error(err) { 147 suite.Equal(errors.New("mail: no angle-addr"), err) 148 } 149 150 err = validate.Email(aWebsite) 151 if suite.Error(err) { 152 suite.Equal(errors.New("mail: missing '@' or angle-addr"), err) 153 } 154 155 err = validate.Email(emailAddress) 156 if suite.NoError(err) { 157 suite.Equal(nil, err) 158 } 159 } 160 161 func (suite *ValidationTestSuite) TestValidateLanguage() { 162 empty := "" 163 notALanguage := "this isn't a language at all!" 164 english := "en" 165 capitalEnglish := "EN" 166 arabic3Letters := "ara" 167 mixedCapsEnglish := "eN" 168 englishUS := "en-us" 169 dutch := "nl" 170 german := "de" 171 var err error 172 173 err = validate.Language(empty) 174 if suite.Error(err) { 175 suite.Equal(errors.New("no language provided"), err) 176 } 177 178 err = validate.Language(notALanguage) 179 if suite.Error(err) { 180 suite.Equal(errors.New("language: tag is not well-formed"), err) 181 } 182 183 err = validate.Language(english) 184 if suite.NoError(err) { 185 suite.Equal(nil, err) 186 } 187 188 err = validate.Language(capitalEnglish) 189 if suite.NoError(err) { 190 suite.Equal(nil, err) 191 } 192 193 err = validate.Language(arabic3Letters) 194 if suite.NoError(err) { 195 suite.Equal(nil, err) 196 } 197 198 err = validate.Language(mixedCapsEnglish) 199 if suite.NoError(err) { 200 suite.Equal(nil, err) 201 } 202 203 err = validate.Language(englishUS) 204 if suite.Error(err) { 205 suite.Equal(errors.New("language: tag is not well-formed"), err) 206 } 207 208 err = validate.Language(dutch) 209 if suite.NoError(err) { 210 suite.Equal(nil, err) 211 } 212 213 err = validate.Language(german) 214 if suite.NoError(err) { 215 suite.Equal(nil, err) 216 } 217 } 218 219 func (suite *ValidationTestSuite) TestValidateReason() { 220 empty := "" 221 badReason := "because" 222 goodReason := "to smash the state and destroy capitalism ultimately and completely" 223 tooLong := "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Mauris auctor mollis viverra. Maecenas maximus mollis sem, nec fermentum velit consectetur non. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia curae; Quisque a enim nibh. Vestibulum bibendum leo ac porttitor auctor. Curabitur velit tellus, facilisis vitae lorem a, ullamcorper efficitur leo. Sed a auctor tortor. Sed ut finibus ante, sit amet laoreet sapien. Donec ullamcorper tellus a nibh sodales vulputate. Donec id dolor eu odio mollis bibendum. Pellentesque habitant morbi tristique senectus et netus at." 224 unicode := "⎾⎿⏀⏁⏂⏃⏄⏅⏆⏇" 225 var err error 226 227 // check with no reason required 228 err = validate.SignUpReason(empty, false) 229 if suite.NoError(err) { 230 suite.Equal(nil, err) 231 } 232 233 err = validate.SignUpReason(badReason, false) 234 if suite.NoError(err) { 235 suite.Equal(nil, err) 236 } 237 238 err = validate.SignUpReason(tooLong, false) 239 if suite.NoError(err) { 240 suite.Equal(nil, err) 241 } 242 243 err = validate.SignUpReason(goodReason, false) 244 if suite.NoError(err) { 245 suite.Equal(nil, err) 246 } 247 248 err = validate.SignUpReason(unicode, false) 249 if suite.NoError(err) { 250 suite.Equal(nil, err) 251 } 252 253 // check with reason required 254 err = validate.SignUpReason(empty, true) 255 if suite.Error(err) { 256 suite.Equal(errors.New("no reason provided"), err) 257 } 258 259 err = validate.SignUpReason(badReason, true) 260 if suite.Error(err) { 261 suite.Equal(errors.New("reason should be at least 40 chars but 'because' was 7"), err) 262 } 263 264 err = validate.SignUpReason(tooLong, true) 265 if suite.Error(err) { 266 suite.Equal(errors.New("reason should be no more than 500 chars but given reason was 600"), err) 267 } 268 269 err = validate.SignUpReason(goodReason, true) 270 if suite.NoError(err) { 271 suite.Equal(nil, err) 272 } 273 } 274 275 func (suite *ValidationTestSuite) TestValidateProfileField() { 276 var ( 277 shortProfileField = "pronouns" 278 tooLongProfileField = "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Integer eu bibendum elit. Sed ac interdum nisi. Vestibulum vulputate eros quis euismod imperdiet. Nulla sit amet dui sit amet lorem consectetur iaculis. Mauris eget lacinia metus. Curabitur nec dui eleifend massa nunc." 279 trimmedProfileField = "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Integer eu bibendum elit. Sed ac interdum nisi. Vestibulum vulputate eros quis euismod imperdiet. Nulla sit amet dui sit amet lorem consectetur iaculis. Mauris eget lacinia metus. Curabitur nec dui " 280 err error 281 ) 282 283 okFields := []*gtsmodel.Field{ 284 { 285 Name: "example", 286 Value: shortProfileField, 287 }, 288 } 289 err = validate.ProfileFields(okFields) 290 suite.NoError(err) 291 suite.Equal(shortProfileField, okFields[0].Value) 292 293 dodgyFields := []*gtsmodel.Field{ 294 { 295 Name: "example", 296 Value: tooLongProfileField, 297 }, 298 } 299 err = validate.ProfileFields(dodgyFields) 300 suite.NoError(err) 301 suite.Equal(trimmedProfileField, dodgyFields[0].Value) 302 suite.Len(dodgyFields[0].Value, 255) 303 } 304 305 func (suite *ValidationTestSuite) TestValidateCustomCSSDisabled() { 306 config.SetAccountsAllowCustomCSS(false) 307 308 err := validate.CustomCSS("this will fail") 309 suite.EqualError(err, "accounts-allow-custom-css is not enabled for this instance") 310 } 311 312 func (suite *ValidationTestSuite) TestValidateCustomCSSEnabled() { 313 config.SetAccountsAllowCustomCSS(true) 314 315 err := validate.CustomCSS("this will pass") 316 suite.NoError(err) 317 } 318 319 func (suite *ValidationTestSuite) TestValidateCustomCSSTooLong() { 320 config.SetAccountsAllowCustomCSS(true) 321 config.SetAccountsCustomCSSLength(5) 322 323 err := validate.CustomCSS("this will fail") 324 suite.EqualError(err, "custom_css must be less than 5 characters, but submitted custom_css was 14 characters") 325 } 326 327 func (suite *ValidationTestSuite) TestValidateCustomCSSTooLongZalgo() { 328 config.SetAccountsAllowCustomCSS(true) 329 config.SetAccountsCustomCSSLength(5) 330 zalgo := "p̵̹̜͇̺̜̱͊̓̈́͛̀͊͘͜e̷̡̱̲̼̪̗̙̐͐̃́̄̉͛̔e̷̞̰̜̲̥̘̻͔̜̞̬͚͋̊͑͗̅̓͛͗̎̃̈́̐̂̕͝ ̷̨̢̡̱̖̤͇̻͕̲̤̞̑ͅp̶̰̜̟̠̏̇̇̆̐̒͋̔͘ḛ̵̾͘ę̷̝͙͕͓͓̱̠̤̳̻̜̗͖̞͙̻̆̓̄͋̎͊̀̋̿́̐͛͗̄̈́̚͠ ̵̨̨̫͕̲͚̮͕̳̉̾̔̍͐p̶̘̞̠̘̎̓̍̑̀͗̃̈́͂́̈́͆͘͜͝͝o̶̜͛̒͒̉̑͒̿͗̐̃͝o̵̼̒͌̓ ̵̢̗̦͔͉͈̰̘̋̃̐̑̅̽̏̄̅͐͆̔͊̃̋͝p̵̩̱̆̆͂̂͛̓̋̅͝o̶̪̰̲̝̻̳̦̮̮͔̒ͅơ̸̧̨̟͇̪̰̜̠̦͇̇̎͗̏̏̈́͂̉̏͐́̃̀͆͠ͅ" 331 332 err := validate.CustomCSS(zalgo) 333 suite.EqualError(err, "custom_css must be less than 5 characters, but submitted custom_css was 275 characters") 334 } 335 336 func (suite *ValidationTestSuite) TestValidateCustomCSSTooLongUnicode() { 337 config.SetAccountsAllowCustomCSS(true) 338 config.SetAccountsCustomCSSLength(5) 339 unicode := "⎾⎿⏀⏁⏂⏃⏄⏅⏆⏇" 340 341 err := validate.CustomCSS(unicode) 342 suite.EqualError(err, "custom_css must be less than 5 characters, but submitted custom_css was 10 characters") 343 } 344 345 func TestValidationTestSuite(t *testing.T) { 346 suite.Run(t, new(ValidationTestSuite)) 347 }