Repository: gratipay/gratipay.com

Issue:
Disallow slashes and other naughty strings in project names
Per some security researchers, we get project names like this:
- https://github.com/gratipay/project-review/issues/313 /www.evil.com/user_ip.php?gratipay
- https://github.com/gratipay/project-review/issues/312 //www.google.com/?><x>
- https://github.com/gratipay/project-review/issues/311 //www.google.com/?

All of these break the URL, so the project page can't be reached and needs to be manually removed from the database.

Not a security issue, but is an annoyance and could be used for spam.


Discussion:
@dmk246 @EdOverflow Would one of you be able to cross-link this to the relevant HackerOne ticket?
@whit537 Done. https://hackerone.com/reports/176396
Potentially useful? https://github.com/minimaxir/big-list-of-naughty-strings
These types of names also produce errors on the front page ([FD](https://gratipay.freshdesk.com/helpdesk/tickets/6688)):

>  there is one failing request to 
https://gratipay.com/x.x/?>/image?size=small, which returns a 404 error.
NB, to delete existing bad projects, use `branch.sql`.
http://gratipay.slackarchive.io/gratipay/-/1488556344.00768/1488986867.00782/1488986782007817/
[Interesting](https://github.com/gratipay/gratipay.com/blob/b3426f059305a6432e747f16b37a5aafcf2a022e/gratipay/models/team/__init__.py#L23).
> TEAM_NAME_PATTERN = re.compile(r'^(?=.*[A-Za-z])([A-Za-z0-9.,-_ ]+)$')

Those are the required characters. At least one letter and then you can have some other stuff that isn't required.

_/me wanders off to learn how to regex out / and ? at the same time..._
@mattbk I'm pretty sure there's a bug in the regex because `-` indicates a range encompassing all of the ASCII characters between `,` and `_`. If this hypothesis is correct than the way to fix is probably for the `-` to be the last character in the set.
Note that there's another similar regex for usernames which may have the same bug.
I will try that.

Resolving patch:
diff --git a/gratipay/models/participant/__init__.py b/gratipay/models/participant/__init__.py
--- a/gratipay/models/participant/__init__.py
+++ b/gratipay/models/participant/__init__.py
@@ -44,7 +44,7 @@
 ASCII_ALLOWED_IN_USERNAME = set("0123456789"
                                 "abcdefghijklmnopqrstuvwxyz"
                                 "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
-                                ".,-_:@ ")
+                                ".,_:@ -")
 # We use | in Sentry logging, so don't make that allowable. :-)
 
 USERNAME_MAX_SIZE = 32
diff --git a/gratipay/models/team/__init__.py b/gratipay/models/team/__init__.py
--- a/gratipay/models/team/__init__.py
+++ b/gratipay/models/team/__init__.py
@@ -20,7 +20,7 @@
 
 
 # Should have at least one letter.
-TEAM_NAME_PATTERN = re.compile(r'^(?=.*[A-Za-z])([A-Za-z0-9.,-_ ]+)$')
+TEAM_NAME_PATTERN = re.compile(r'^(?=.*[A-Za-z])([A-Za-z0-9.,_ -]+)$')
 
 
 def slugize(name):


Test patch:
diff --git a/tests/py/test_teams.py b/tests/py/test_teams.py
--- a/tests/py/test_teams.py
+++ b/tests/py/test_teams.py
@@ -501,3 +501,12 @@ def test_slugize_trims_replacement_dashes(self):
 
     def test_slugize_folds_dashes_together(self):
         assert slugize('1a----------------23') == '1a-23'
+
+    def test_slugize_disallows_slashes(self):
+        self.assertRaises(InvalidTeamName, slugize, 'abc/def')
+
+    def test_slugize_disallows_questions(self):
+        self.assertRaises(InvalidTeamName, slugize, 'abc?def')
+
+    def test_slugize_disallows_backslashes(self):
+        self.assertRaises(InvalidTeamName, slugize, 'abc\def')