webhook: quote URL variables to prevent url errors with special chars like # in pr title.

This commit is contained in:
Marcin Lulek 2019-02-14 00:21:50 +01:00
parent 36b99c2808
commit 9c532486a2
3 changed files with 40 additions and 29 deletions

View file

@ -23,17 +23,27 @@ import string
import collections
import logging
import requests
import urllib
from requests.adapters import HTTPAdapter
from requests.packages.urllib3.util.retry import Retry
from mako import exceptions
from rhodecode.lib.utils2 import safe_str
from rhodecode.translation import _
log = logging.getLogger(__name__)
class UrlTmpl(string.Template):
def safe_substitute(self, **kws):
# url encode the kw for usage in url
kws = {k: urllib.quote(safe_str(v)) for k, v in kws.items()}
return super(UrlTmpl, self).safe_substitute(**kws)
class IntegrationTypeBase(object):
""" Base class for IntegrationType plugins """
is_dummy = False
@ -217,7 +227,9 @@ class WebhookDataHandler(CommitParsingDataHandler):
common_vars.update(extra_vars)
template_url = self.template_url.replace('${extra:', '${extra__')
return string.Template(template_url).safe_substitute(**common_vars)
for k, v in common_vars.items():
template_url = UrlTmpl(template_url).safe_substitute(**{k: v})
return template_url
def repo_push_event_handler(self, event, data):
url = self.get_base_parsed_template(data)
@ -228,20 +240,18 @@ class WebhookDataHandler(CommitParsingDataHandler):
if '${branch}' in url or '${branch_head}' in url or '${commit_id}' in url:
# call it multiple times, for each branch if used in variables
for branch, commit_ids in branches_commits.items():
branch_url = string.Template(url).safe_substitute(branch=branch)
branch_url = UrlTmpl(url).safe_substitute(branch=branch)
if '${branch_head}' in branch_url:
# last commit in the aggregate is the head of the branch
branch_head = commit_ids['branch_head']
branch_url = string.Template(branch_url).safe_substitute(
branch_head=branch_head)
branch_url = UrlTmpl(branch_url).safe_substitute(branch_head=branch_head)
# call further down for each commit if used
if '${commit_id}' in branch_url:
for commit_data in commit_ids['commits']:
commit_id = commit_data['raw_id']
commit_url = string.Template(branch_url).safe_substitute(
commit_id=commit_id)
commit_url = UrlTmpl(branch_url).safe_substitute(commit_id=commit_id)
# register per-commit call
log.debug(
'register %s call(%s) to url %s',
@ -251,36 +261,34 @@ class WebhookDataHandler(CommitParsingDataHandler):
else:
# register per-branch call
log.debug(
'register %s call(%s) to url %s',
self.name, event, branch_url)
url_calls.append(
(branch_url, self.headers, data))
log.debug('register %s call(%s) to url %s',
self.name, event, branch_url)
url_calls.append((branch_url, self.headers, data))
else:
log.debug(
'register %s call(%s) to url %s', self.name, event, url)
log.debug('register %s call(%s) to url %s', self.name, event, url)
url_calls.append((url, self.headers, data))
return url_calls
def repo_create_event_handler(self, event, data):
url = self.get_base_parsed_template(data)
log.debug(
'register %s call(%s) to url %s', self.name, event, url)
log.debug('register %s call(%s) to url %s', self.name, event, url)
return [(url, self.headers, data)]
def pull_request_event_handler(self, event, data):
url = self.get_base_parsed_template(data)
log.debug(
'register %s call(%s) to url %s', self.name, event, url)
url = string.Template(url).safe_substitute(
pull_request_id=data['pullrequest']['pull_request_id'],
pull_request_title=data['pullrequest']['title'],
pull_request_url=data['pullrequest']['url'],
pull_request_shadow_url=data['pullrequest']['shadow_url'],
pull_request_commits_uid=data['pullrequest']['commits_uid'],
)
log.debug('register %s call(%s) to url %s', self.name, event, url)
pr_vars = [
('pull_request_id', data['pullrequest']['pull_request_id']),
('pull_request_title', data['pullrequest']['title']),
('pull_request_url', data['pullrequest']['url']),
('pull_request_shadow_url', data['pullrequest']['shadow_url']),
('pull_request_commits_uid', data['pullrequest']['commits_uid']),
]
for k, v in pr_vars:
url = UrlTmpl(url).safe_substitute(**{k: v})
return [(url, self.headers, data)]
def __call__(self, event, data):

View file

@ -188,7 +188,7 @@ class WebhookIntegrationType(IntegrationTypeBase):
handler = WebhookDataHandler(template_url, headers)
url_calls = handler(event, data)
log.debug('webhook: calling following urls: %s', [x[0] for x in url_calls])
log.debug('Webhook: calling following urls: %s', [x[0] for x in url_calls])
run_task(post_to_webhook, url_calls, self.settings)

View file

@ -78,15 +78,18 @@ def test_webook_parse_url_for_create_event(base_data, template, expected_urls):
('http://server.com/${repo_name}/${pull_request_id}',
['http://server.com/foo/999']),
('http://server.com/${repo_name}/${pull_request_url}',
['http://server.com/foo/http://pr-url.com']),
['http://server.com/foo/http%3A//pr-url.com']),
('http://server.com/${repo_name}/${pull_request_url}/?TITLE=${pull_request_title}',
['http://server.com/foo/http%3A//pr-url.com/?TITLE=example-pr-title%20Ticket%20%23123']),
('http://server.com/${repo_name}/?SHADOW_URL=${pull_request_shadow_url}',
['http://server.com/foo/?SHADOW_URL=http%3A//pr-url.com/repository']),
])
def test_webook_parse_url_for_pull_request_event(
base_data, template, expected_urls):
def test_webook_parse_url_for_pull_request_event(base_data, template, expected_urls):
base_data['pullrequest'] = {
'pull_request_id': 999,
'url': 'http://pr-url.com',
'title': 'example-pr-title',
'title': 'example-pr-title Ticket #123',
'commits_uid': 'abcdefg1234',
'shadow_url': 'http://pr-url.com/repository'
}