Skip to content

Commit 02b318b

Browse files
authored
Merge pull request #5 from QuLogic/safer-repo-update
Verify git checkout path is in site directory
2 parents 79993d7 + a52631a commit 02b318b

File tree

2 files changed

+20
-5
lines changed

2 files changed

+20
-5
lines changed

test_webhook.py

+11-2
Original file line numberDiff line numberDiff line change
@@ -65,8 +65,9 @@ async def test_update_repo(tmp_path_factory):
6565
assert dest_commit == src_commit
6666

6767

68-
async def test_github_webhook_errors(aiohttp_client, monkeypatch):
68+
async def test_github_webhook_errors(aiohttp_client, monkeypatch, tmp_path):
6969
"""Test invalid inputs to webhook."""
70+
monkeypatch.setenv('SITE_DIR', str(tmp_path))
7071
client = await aiohttp_client(create_app())
7172

7273
# Only /gh/<repo-name> exists.
@@ -121,6 +122,14 @@ async def test_github_webhook_errors(aiohttp_client, monkeypatch):
121122
assert resp.status == 400
122123
assert 'incorrect repository' in await resp.text()
123124

125+
# Fields provided, but invalid.
126+
resp = await client.post(
127+
'/gh/non-existent-repo', headers=valid_headers,
128+
data='{"sender": {"login": "QuLogic"}, "repository":'
129+
' {"name": "..", "owner": {"login": "matplotlib"}}}')
130+
assert resp.status == 400
131+
assert 'incorrect repository' in await resp.text()
132+
124133
# Problem on our side.
125134
resp = await client.post(
126135
'/gh/non-existent',
@@ -134,12 +143,12 @@ async def test_github_webhook_errors(aiohttp_client, monkeypatch):
134143

135144
async def test_github_webhook_valid(aiohttp_client, monkeypatch, tmp_path):
136145
"""Test valid input to webhook."""
146+
monkeypatch.setenv('SITE_DIR', str(tmp_path))
137147
client = await aiohttp_client(create_app())
138148

139149
# Do no actual work, since that's tested above.
140150
monkeypatch.setattr(webhook, 'verify_signature',
141151
mock.Mock(verify_signature, return_value=True))
142-
monkeypatch.setenv('SITE_DIR', str(tmp_path))
143152
ur_mock = mock.Mock(update_repo, return_value=None)
144153
monkeypatch.setattr(webhook, 'update_repo', ur_mock)
145154

webhook.py

+9-3
Original file line numberDiff line numberDiff line change
@@ -156,7 +156,11 @@ async def github_webhook(request: web.Request):
156156
delivery, ref, expected_branch)
157157
return web.Response(status=200)
158158

159-
checkout = Path(os.environ.get('SITE_DIR', 'sites'), repository)
159+
checkout = (request.app['site_dir'] / repository).resolve()
160+
if not checkout.is_relative_to(request.app['site_dir']):
161+
raise web.HTTPBadRequest(
162+
reason=(f'{delivery}: Checkout for {organization}/{repository} '
163+
'does not exist'))
160164
if not (checkout / '.git').is_dir():
161165
raise web.HTTPInternalServerError(
162166
reason=(f'{delivery}: Checkout for {organization}/{repository} '
@@ -171,16 +175,18 @@ async def github_webhook(request: web.Request):
171175

172176
def create_app():
173177
"""Create the aiohttp app and setup routes."""
178+
site_dir = Path(os.environ.get('SITE_DIR', 'sites')).resolve()
179+
assert site_dir.is_dir()
180+
174181
app = web.Application()
182+
app['site_dir'] = site_dir
175183
app.add_routes([
176184
web.post('/gh/{repo}', github_webhook),
177185
])
178186
return app
179187

180188

181189
if __name__ == '__main__':
182-
assert Path(os.environ.get('SITE_DIR', 'sites')).is_dir()
183-
184190
if len(sys.argv) > 1:
185191
from urllib.parse import urlparse
186192
url = sys.argv[1]

0 commit comments

Comments
 (0)