application: check icon from bundle before install (#88251)
gitea/hobo/pipeline/head This commit looks good Details

This commit is contained in:
Lauréline Guérin 2024-03-18 17:53:30 +01:00
parent 159f93a783
commit ad01d3d634
No known key found for this signature in database
GPG Key ID: 1FAB9B9B4F93D473
3 changed files with 53 additions and 19 deletions

View File

@ -32,6 +32,7 @@ from django.utils.timezone import localtime, now
from django.utils.translation import gettext_lazy as _
from django.views.generic import DetailView, FormView, ListView, RedirectView, TemplateView
from django.views.generic.edit import CreateView, DeleteView, UpdateView
from PIL import Image, UnidentifiedImageError
from hobo.environment.models import Variable
@ -463,6 +464,9 @@ class Install(FormView):
_('Can not update this application, wrong slug (%s).') % manifest.get('slug'),
)
return self.form_invalid(form)
icon = manifest.get('icon')
if icon:
Image.open(tar.extractfile(icon))
app, created = Application.objects.get_or_create(
slug=manifest.get('slug'), defaults={'name': manifest.get('application')}
)
@ -475,7 +479,6 @@ class Install(FormView):
# overwriting a local application and keep on developing it.
app.editable = False
app.save()
icon = manifest.get('icon')
if icon:
app.icon.save(icon, tar.extractfile(icon), save=True)
else:
@ -483,6 +486,9 @@ class Install(FormView):
except tarfile.TarError:
form.add_error('bundle', _('Invalid tar file.'))
return self.form_invalid(form)
except UnidentifiedImageError:
form.add_error('bundle', _('Invalid icon file.'))
return self.form_invalid(form)
# always create a new version on install or if previous version has not the same number
version_number = manifest.get('version_number') or 'unknown'

BIN
tests/data/black.jpeg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 558 B

View File

@ -1,7 +1,7 @@
import base64
import copy
import io
import json
import os
import random
import re
import tarfile
@ -29,6 +29,8 @@ from .test_manager import login
pytestmark = pytest.mark.django_db
TESTS_DATA_DIR = os.path.join(os.path.dirname(__file__), 'data')
WCS_AVAILABLE_OBJECTS = {
'data': [
@ -373,18 +375,13 @@ def test_create_application(app, admin_user, settings, analyze):
# add an icon
resp = app.get('/applications/manifest/test/metadata/')
resp.form['icon'] = Upload(
'foo.png',
base64.decodebytes(
b'iVBORw0KGgoAAAANSUhEUgAAAAEAAAABAQAAAAA3bvkkAAAACklEQVQI12NoAAAAggCB3UNq9AAAAABJRU5ErkJggg=='
),
'image/png',
)
with open(os.path.join(TESTS_DATA_DIR, 'black.jpeg'), mode='rb') as icon_fd:
resp.form['icon'] = Upload('foo.jpeg', icon_fd.read(), 'image/jpeg')
resp.form['documentation_url'] = '' # and reset documentation_url
resp.form['visible'] = True
resp = resp.form.submit().follow()
application.refresh_from_db()
assert re.match(r'applications/icons/foo(_\w+)?.png', application.icon.name)
assert re.match(r'applications/icons/foo(_\w+)?.jpeg', application.icon.name)
assert application.documentation_url == ''
assert application.visible is True
@ -394,7 +391,7 @@ def test_create_application(app, admin_user, settings, analyze):
resp = resp.form.submit()
assert 'The icon must be in JPEG or PNG format' in resp
application.refresh_from_db()
assert re.match(r'applications/icons/foo(_\w+)?.png', application.icon.name)
assert re.match(r'applications/icons/foo(_\w+)?.jpeg', application.icon.name)
resp = app.get('/applications/manifest/test/')
resp = resp.click('Generate application bundle')
@ -913,7 +910,7 @@ def get_bundle(with_icon=False):
manifest_json = {
'application': 'Test',
'slug': 'test',
'icon': 'foo.png' if with_icon else None,
'icon': 'foo.jpeg' if with_icon else None,
'description': '',
'documentation_url': 'http://foo.bar',
'version_number': '43.0' if with_icon else '42.0',
@ -939,12 +936,10 @@ def get_bundle(with_icon=False):
tarinfo.size = len(manifest_fd.getvalue())
tar.addfile(tarinfo, fileobj=manifest_fd)
if with_icon:
icon_fd = io.BytesIO(
b'iVBORw0KGgoAAAANSUhEUgAAAAEAAAABAQAAAAA3bvkkAAAACklEQVQI12NoAAAAggCB3UNq9AAAAABJRU5ErkJggg=='
)
tarinfo = tarfile.TarInfo('foo.png')
tarinfo.size = len(icon_fd.getvalue())
tar.addfile(tarinfo, fileobj=icon_fd)
with open(os.path.join(TESTS_DATA_DIR, 'black.jpeg'), mode='rb') as icon_fd:
tarinfo = tarfile.TarInfo(manifest_json['icon'])
tarinfo.size = 558
tar.addfile(tarinfo, fileobj=icon_fd)
return tar_io.getvalue()
@ -999,7 +994,7 @@ def test_deploy_application(app, admin_user, settings, app_bundle, app_bundle_wi
application = Application.objects.get(slug='test')
assert application.name == 'Test'
if bundle == app_bundle_with_icon:
assert re.match(r'applications/icons/foo(_\w+)?.png', application.icon.name)
assert re.match(r'applications/icons/foo(_\w+)?.jpeg', application.icon.name)
else:
assert application.icon.name == ''
assert application.documentation_url == 'http://foo.bar'
@ -1182,6 +1177,39 @@ def test_deploy_application(app, admin_user, settings, app_bundle, app_bundle_wi
resp = resp.form.submit()
assert resp.context['form'].errors == {'bundle': ['Invalid tar file.']}
# bad icon file
tar_io = io.BytesIO()
with tarfile.open(mode='w', fileobj=tar_io) as tar:
manifest_json = {
'application': 'Test',
'slug': 'test',
'icon': 'foo.png',
'description': '',
'documentation_url': 'http://foo.bar',
'version_number': '42.0',
'version_notes': 'foo bar blah',
'elements': [],
}
manifest_fd = io.BytesIO(json.dumps(manifest_json, indent=2).encode())
tarinfo = tarfile.TarInfo('manifest.json')
tarinfo.size = len(manifest_fd.getvalue())
tar.addfile(tarinfo, fileobj=manifest_fd)
icon_fd = io.BytesIO(b'garbage')
tarinfo = tarfile.TarInfo('foo.png')
tarinfo.size = len(icon_fd.getvalue())
tar.addfile(tarinfo, fileobj=icon_fd)
bundle = tar_io.getvalue()
resp = app.get('/applications/')
if action == 'Update':
with StatefulHTTMock(mocked_http2):
resp = resp.click(href='manifest/test/')
resp = resp.click(action)
resp.form['bundle'] = Upload('app.tar', bundle, 'application/x-tar')
resp = resp.form.submit()
assert resp.context['form'].errors == {'bundle': ['Invalid icon file.']}
def test_update_application(app, admin_user, settings, app_bundle):
Wcs.objects.create(base_url='https://wcs.example.invalid', slug='foobar', title='Foobar')