operation: Use safe formatter for translating messages

- When an app install fails, there is a small chance that the failure message is
show in the area where operation spinner is shown. If that happens,
operation.translated_message is accessed from the HTML template. This throws an
exception if the error message that made contains excepted formatting keys.
Example:
"{include_once("/var/www/html/config/config.php");print($CONFIG["dbpassword"] ??
""); }".

- Also change the formatting key {exception_message} to {exception} as this
would help in translation when Notification is shown which has {exception} as
data dictionary value.

Tests:

- In the operation update message such as 'Installing app', insert unexpected
formatting strings. 'Installing app {foo}'. Notice the error without the patch
and how the patch fixes it.

Signed-off-by: Sunil Mohan Adapa <sunil@medhas.org>
Reviewed-by: James Valleroy <jvalleroy@mailbox.org>
This commit is contained in:
Sunil Mohan Adapa 2024-10-18 15:41:21 -07:00 committed by James Valleroy
parent f456a58118
commit fbed7e93e8
No known key found for this signature in database
GPG Key ID: 77C0C75E7B650808
2 changed files with 12 additions and 6 deletions

View File

@ -7,6 +7,8 @@ import threading
from collections import OrderedDict
from typing import Callable
from plinth.utils import SafeFormatter
from . import app as app_module
logger = logging.getLogger(__name__)
@ -115,7 +117,7 @@ class Operation:
return self._message
if self.exception: # Operation resulted in a error.
return gettext_noop('Error: {name}: {exception_message}')
return gettext_noop('Error: {name}: {exception}')
if self.state == Operation.State.WAITING:
return gettext_noop('Waiting to start: {name}')
@ -137,11 +139,15 @@ class Operation:
"""
from django.utils.translation import gettext
message = gettext(self.message)
message = message.format(name=gettext(self.name),
exception_message=str(self.exception))
data = {'name': gettext(self.name), 'exception': str(self.exception)}
if self.app_id:
message = message.format(
app_name=app_module.App.get(self.app_id).info.name)
data['app_name'] = app_module.App.get(self.app_id).info.name
try:
message = SafeFormatter().vformat(message, [], data)
except (KeyError, AttributeError) as error:
logger.warning(
'Operation missing required key during translation: %s', error)
return message

View File

@ -186,7 +186,7 @@ def test_message(app_get):
assert operation.translated_message == 'message1'
operation._message = None
assert operation.message == 'Error: {name}: {exception_message}'
assert operation.message == 'Error: {name}: {exception}'
assert operation.translated_message == 'Error: op1: error1'
operation.exception = None