Repository: readthedocs/readthedocs.org
Issue: Old webhooks gives 502

Running this command,

    $ http POST https://readthedocs.org/github repository:='{"url": "", "ssh_url": ""}' ref=""

breaks the site and may produce tons of tasks being ran.

After running that command I get 502 and I tried it locally and I found that the problem is at the Project query since all the repo urls ends with `''` 

https://github.com/rtfd/readthedocs.org/blob/445683fc0c86b0de56779447a8eedf98024dba32/readthedocs/core/views/hooks.py#L92-L96

Finally, `update_imported_docs` is triggered by each of the project.

The only line that could save this execution is 

https://github.com/rtfd/readthedocs.org/blob/445683fc0c86b0de56779447a8eedf98024dba32/readthedocs/core/views/hooks.py#L120

Related to #3520 

Changed files:
readthedocs/rtd_tests/tests/test_post_commit_hooks.py, readthedocs/core/views/hooks.py

diff --git a/readthedocs/core/views/hooks.py b/readthedocs/core/views/hooks.py
index 9a306aaea..c773f7b44 100644
--- a/readthedocs/core/views/hooks.py
+++ b/readthedocs/core/views/hooks.py
@@ -90,9 +90,12 @@ def build_branches(project, branch_list):
 
 
 def get_project_from_url(url):
+    if not url:
+        return Project.objects.none()
     projects = (
         Project.objects.filter(repo__iendswith=url) |
-        Project.objects.filter(repo__iendswith=url + '.git'))
+        Project.objects.filter(repo__iendswith=url + '.git')
+    )
     return projects
 
 
@@ -278,6 +281,8 @@ def bitbucket_build(request):
                 branches = [commit.get('branch', '')
                             for commit in data['commits']]
                 repository = data['repository']
+                if not repository['absolute_url']:
+                    return HttpResponse('Invalid request', status=400)
                 search_url = 'bitbucket.org{0}'.format(
                     repository['absolute_url'].rstrip('/')
                 )
@@ -285,6 +290,8 @@ def bitbucket_build(request):
                 changes = data['push']['changes']
                 branches = [change['new']['name']
                             for change in changes]
+                if not data['repository']['full_name']:
+                    return HttpResponse('Invalid request', status=400)
                 search_url = 'bitbucket.org/{0}'.format(
                     data['repository']['full_name']
                 )
diff --git a/readthedocs/rtd_tests/tests/test_post_commit_hooks.py b/readthedocs/rtd_tests/tests/test_post_commit_hooks.py
index fe781e16b..6b71e043b 100644
--- a/readthedocs/rtd_tests/tests/test_post_commit_hooks.py
+++ b/readthedocs/rtd_tests/tests/test_post_commit_hooks.py
@@ -146,6 +146,18 @@ class GitLabWebHookTest(BasePostCommitTest):
         rtd.default_branch = old_default
         rtd.save()
 
+    def test_gitlab_request_empty_url(self):
+        """
+        The gitlab hook shouldn't build any project
+        if the url, ssh_url or ref are empty.
+        """
+        self.payload['project']['http_url'] = ''
+        r = self.client.post(
+            '/gitlab/', data=json.dumps(self.payload),
+            content_type='application/json'
+        )
+        self.assertEqual(r.status_code, 404)
+
     def test_gitlab_webhook_is_deprecated(self):
         # Project is created after feature, not included in historical allowance
         url = 'https://github.com/rtfd/readthedocs-build'
@@ -265,6 +277,19 @@ class GitHubPostCommitTest(BasePostCommitTest):
                              content_type='application/json')
         self.assertEqual(r.status_code, 400)
 
+    def test_github_request_empty_url(self):
+        """
+        The github hook shouldn't build any project
+        if the url, ssh_url or ref are empty.
+        """
+        self.payload['repository']['url'] = ''
+        self.payload['repository']['ssh_url'] = ''
+        r = self.client.post(
+            '/github/', data=json.dumps(self.payload),
+            content_type='application/json'
+        )
+        self.assertEqual(r.status_code, 403)
+
     def test_private_repo_mapping(self):
         """
         Test for private GitHub repo mapping.
@@ -534,6 +559,18 @@ class BitBucketHookTests(BasePostCommitTest):
                              content_type='application/json')
         self.assertContains(r, '(URL Build) Build Started: bitbucket.org/test/project [latest]')
 
+    def test_bitbucket_request_empty_url(self):
+        """
+        The bitbucket hook shouldn't build any project
+        if the url, ssh_url or ref are empty.
+        """
+        self.git_payload['repository']['absolute_url'] = ''
+        r = self.client.post(
+            '/bitbucket/', data=json.dumps(self.git_payload),
+            content_type='application/json'
+        )
+        self.assertEqual(r.status_code, 400)
+
     def test_bitbucket_webhook_is_deprecated(self):
         # Project is created after feature, not included in historical allowance
         url = 'https://bitbucket.org/rtfd/readthedocs-build'