##// END OF EJS Templates
mercurial: protocol security updates....
marcink -
r2724:7a057a98 default
parent child Browse files
Show More
@@ -25,6 +25,7 b' SimpleHG middleware for handling mercuri'
25
25
26 import logging
26 import logging
27 import urlparse
27 import urlparse
28 import urllib
28
29
29 from rhodecode.lib import utils
30 from rhodecode.lib import utils
30 from rhodecode.lib.ext_json import json
31 from rhodecode.lib.ext_json import json
@@ -51,24 +52,94 b' class SimpleHg(simplevcs.SimpleVCS):'
51 'getbundle': 'pull',
52 'getbundle': 'pull',
52 'stream_out': 'pull',
53 'stream_out': 'pull',
53 'listkeys': 'pull',
54 'listkeys': 'pull',
55 'between': 'pull',
56 'branchmap': 'pull',
57 'branches': 'pull',
58 'clonebundles': 'pull',
59 'capabilities': 'pull',
60 'debugwireargs': 'pull',
61 'heads': 'pull',
62 'lookup': 'pull',
63 'hello': 'pull',
64 'known': 'pull',
65
66 # largefiles
67 'putlfile': 'push',
68 'getlfile': 'pull',
69 'statlfile': 'pull',
70 'lheads': 'pull',
71
54 'unbundle': 'push',
72 'unbundle': 'push',
55 'pushkey': 'push',
73 'pushkey': 'push',
56 }
74 }
57
75
76 @classmethod
77 def _get_xarg_headers(cls, environ):
78 i = 1
79 chunks = [] # gather chunks stored in multiple 'hgarg_N'
80 while True:
81 head = environ.get('HTTP_X_HGARG_{}'.format(i))
82 if not head:
83 break
84 i += 1
85 chunks.append(urllib.unquote_plus(head))
86 full_arg = ''.join(chunks)
87 pref = 'cmds='
88 if full_arg.startswith(pref):
89 # strip the cmds= header defining our batch commands
90 full_arg = full_arg[len(pref):]
91 cmds = full_arg.split(';')
92 return cmds
93
94 @classmethod
95 def _get_batch_cmd(cls, environ):
96 """
97 Handle batch command send commands. Those are ';' separated commands
98 sent by batch command that server needs to execute. We need to extract
99 those, and map them to our ACTION_MAPPING to get all push/pull commands
100 specified in the batch
101 """
102 default = 'push'
103 batch_cmds = []
104 try:
105 cmds = cls._get_xarg_headers(environ)
106 for pair in cmds:
107 parts = pair.split(' ', 1)
108 if len(parts) != 2:
109 continue
110 # entry should be in a format `key ARGS`
111 cmd, args = parts
112 action = cls._ACTION_MAPPING.get(cmd, default)
113 batch_cmds.append(action)
114 except Exception:
115 log.exception('Failed to extract batch commands operations')
116
117 # in case we failed, (e.g malformed data) assume it's PUSH sub-command
118 # for safety
119 return batch_cmds or [default]
120
58 def _get_action(self, environ):
121 def _get_action(self, environ):
59 """
122 """
60 Maps mercurial request commands into a pull or push command.
123 Maps mercurial request commands into a pull or push command.
61 In case of unknown/unexpected data, it returns 'pull' to be safe.
124 In case of unknown/unexpected data, it returns 'push' to be safe.
62
125
63 :param environ:
126 :param environ:
64 """
127 """
128 default = 'push'
65 query = urlparse.parse_qs(environ['QUERY_STRING'],
129 query = urlparse.parse_qs(environ['QUERY_STRING'],
66 keep_blank_values=True)
130 keep_blank_values=True)
131
67 if 'cmd' in query:
132 if 'cmd' in query:
68 cmd = query['cmd'][0]
133 cmd = query['cmd'][0]
69 return self._ACTION_MAPPING.get(cmd, 'pull')
134 if cmd == 'batch':
135 cmds = self._get_batch_cmd(environ)
136 if 'push' in cmds:
137 return 'push'
138 else:
139 return 'pull'
140 return self._ACTION_MAPPING.get(cmd, default)
70
141
71 return 'pull'
142 return default
72
143
73 def _create_wsgi_app(self, repo_path, repo_name, config):
144 def _create_wsgi_app(self, repo_path, repo_name, config):
74 return self.scm_app.create_hg_wsgi_app(
145 return self.scm_app.create_hg_wsgi_app(
@@ -47,11 +47,14 b' def get_environ(url):'
47 ('/foo/bar?cmd=pushkey&key=tip', 'push'),
47 ('/foo/bar?cmd=pushkey&key=tip', 'push'),
48 ('/foo/bar?cmd=listkeys&key=tip', 'pull'),
48 ('/foo/bar?cmd=listkeys&key=tip', 'pull'),
49 ('/foo/bar?cmd=changegroup&key=tip', 'pull'),
49 ('/foo/bar?cmd=changegroup&key=tip', 'pull'),
50 # Edge case: unknown argument: assume pull
50 ('/foo/bar?cmd=hello', 'pull'),
51 ('/foo/bar?cmd=unknown&key=tip', 'pull'),
51 ('/foo/bar?cmd=batch', 'push'),
52 ('/foo/bar?cmd=&key=tip', 'pull'),
52 ('/foo/bar?cmd=putlfile', 'push'),
53 # Edge case: unknown argument: assume push
54 ('/foo/bar?cmd=unknown&key=tip', 'push'),
55 ('/foo/bar?cmd=&key=tip', 'push'),
53 # Edge case: not cmd argument
56 # Edge case: not cmd argument
54 ('/foo/bar?key=tip', 'pull'),
57 ('/foo/bar?key=tip', 'push'),
55 ])
58 ])
56 def test_get_action(url, expected_action, request_stub):
59 def test_get_action(url, expected_action, request_stub):
57 app = simplehg.SimpleHg(config={'auth_ret_code': '', 'base_path': ''},
60 app = simplehg.SimpleHg(config={'auth_ret_code': '', 'base_path': ''},
@@ -60,6 +63,34 b' def test_get_action(url, expected_action'
60
63
61
64
62 @pytest.mark.parametrize(
65 @pytest.mark.parametrize(
66 'environ, expected_xargs, expected_batch',
67 [
68 ({},
69 [''], ['push']),
70
71 ({'HTTP_X_HGARG_1': ''},
72 [''], ['push']),
73
74 ({'HTTP_X_HGARG_1': 'cmds=listkeys+namespace%3Dphases'},
75 ['listkeys namespace=phases'], ['pull']),
76
77 ({'HTTP_X_HGARG_1': 'cmds=pushkey+namespace%3Dbookmarks%2Ckey%3Dbm%2Cold%3D%2Cnew%3Dcb9a9f314b8b07ba71012fcdbc544b5a4d82ff5b'},
78 ['pushkey namespace=bookmarks,key=bm,old=,new=cb9a9f314b8b07ba71012fcdbc544b5a4d82ff5b'], ['push']),
79
80 ({'HTTP_X_HGARG_1': 'namespace=phases'},
81 ['namespace=phases'], ['push']),
82
83 ])
84 def test_xarg_and_batch_commands(environ, expected_xargs, expected_batch):
85 app = simplehg.SimpleHg
86
87 result = app._get_xarg_headers(environ)
88 result_batch = app._get_batch_cmd(environ)
89 assert expected_xargs == result
90 assert expected_batch == result_batch
91
92
93 @pytest.mark.parametrize(
63 'url, expected_repo_name',
94 'url, expected_repo_name',
64 [
95 [
65 ('/foo?cmd=unbundle&key=tip', 'foo'),
96 ('/foo?cmd=unbundle&key=tip', 'foo'),
General Comments 0
You need to be logged in to leave comments. Login now