assets: add a check to see if uploaded images are handled by PIL (#86995)
gitea/combo/pipeline/head This commit looks good Details

This commit is contained in:
Yann Weber 2024-02-15 11:48:22 +01:00 committed by Yann Weber
parent 59c14da940
commit d9fd99f1dd
3 changed files with 34 additions and 1 deletions

View File

@ -14,12 +14,26 @@
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
import PIL
from django import forms
from django.core.exceptions import ValidationError
from django.utils.translation import gettext_lazy as _
def validate_asset_file(value):
try:
PIL.Image.open(value.file)
except PIL.UnidentifiedImageError:
pass # not an image
except PIL.Image.DecompressionBombError as expt:
raise ValidationError(
_('Uploaded image exceeds size limits: %(detail)s'), params={'detail': str(expt)}
)
return True
class AssetUploadForm(forms.Form):
upload = forms.FileField(label=_('File'))
upload = forms.FileField(label=_('File'), validators=[validate_asset_file])
class AssetsImportForm(forms.Form):

BIN
tests/data/dec_bomb.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 MiB

View File

@ -2045,6 +2045,25 @@ def test_asset_management(app, admin_user):
resp = resp.form.submit(status=403)
@pytest.mark.parametrize('toclick', ['Overwrite', 'Upload'])
def test_asset_management_decompression_bomb(app, admin_user, toclick):
with override_settings(COMBO_ASSET_SLOTS={'header:logo': {'label': 'Logo'}}):
app = login(app)
resp = app.get('/manage/assets/')
resp = resp.click(toclick)
with open('tests/data/dec_bomb.jpg', 'rb') as dec_bomb_fp:
resp.form['upload'] = Upload('dec_bomb.jpg', dec_bomb_fp.read(), 'image/jpeg')
ret = resp.form.submit()
assert (
PyQuery(ret.text).find('.errorlist li')[0].text.startswith('Uploaded image exceeds size limits: ')
)
assert (
PyQuery(ret.text)
.find('.errorlist li')[0]
.text.endswith('could be decompression bomb DOS attack.')
)
def test_asset_management_anchor(app, admin_user):
app = login(app)
resp = app.get('/manage/assets/')