mirror of
https://github.com/ansible/awx.git
synced 2026-05-20 07:17:40 -02:30
Protect against very large stdout fields
* Defer loading result_stdout_text until specifically needed * Conditionally display it based on the size of the field * Provide a helpful message otherwise
This commit is contained in:
@@ -464,7 +464,8 @@ class UnifiedJobTemplateSerializer(BaseSerializer):
|
|||||||
|
|
||||||
class UnifiedJobSerializer(BaseSerializer):
|
class UnifiedJobSerializer(BaseSerializer):
|
||||||
|
|
||||||
result_stdout = serializers.CharField(source='result_stdout', label='result stdout', read_only=True)
|
#result_stdout = serializers.CharField(source='result_stdout', label='result stdout', read_only=True)
|
||||||
|
result_stdout = serializers.SerializerMethodField('get_result_stdout')
|
||||||
unified_job_template = serializers.Field(source='unified_job_template_id', label='unified job template')
|
unified_job_template = serializers.Field(source='unified_job_template_id', label='unified job template')
|
||||||
job_env = serializers.CharField(source='job_env', label='job env', read_only=True)
|
job_env = serializers.CharField(source='job_env', label='job env', read_only=True)
|
||||||
|
|
||||||
@@ -475,6 +476,13 @@ class UnifiedJobSerializer(BaseSerializer):
|
|||||||
'job_cwd', 'job_env', 'job_explanation', 'result_stdout',
|
'job_cwd', 'job_env', 'job_explanation', 'result_stdout',
|
||||||
'result_traceback')
|
'result_traceback')
|
||||||
|
|
||||||
|
|
||||||
|
def get_result_stdout(self, obj):
|
||||||
|
obj_size = obj.result_stdout_size
|
||||||
|
if obj_size > settings.STDOUT_MAX_BYTES_DISPLAY:
|
||||||
|
return "Standard Output too large to display (%d bytes), only download supported for sizes over %d bytes" % (obj_size, settings.STDOUT_MAX_BYTES_DISPLAY)
|
||||||
|
return obj.result_stdout
|
||||||
|
|
||||||
def get_types(self):
|
def get_types(self):
|
||||||
if type(self) is UnifiedJobSerializer:
|
if type(self) is UnifiedJobSerializer:
|
||||||
return ['project_update', 'inventory_update', 'job', 'ad_hoc_command', 'system_job']
|
return ['project_update', 'inventory_update', 'job', 'ad_hoc_command', 'system_job']
|
||||||
|
|||||||
@@ -2109,11 +2109,20 @@ class JobList(ListCreateAPIView):
|
|||||||
model = Job
|
model = Job
|
||||||
serializer_class = JobListSerializer
|
serializer_class = JobListSerializer
|
||||||
|
|
||||||
|
def get_queryset(self):
|
||||||
|
qs = self.request.user.get_queryset(self.model).defer('result_stdout_text')
|
||||||
|
return qs
|
||||||
|
|
||||||
class JobDetail(RetrieveUpdateDestroyAPIView):
|
class JobDetail(RetrieveUpdateDestroyAPIView):
|
||||||
|
|
||||||
model = Job
|
model = Job
|
||||||
serializer_class = JobSerializer
|
serializer_class = JobSerializer
|
||||||
|
|
||||||
|
|
||||||
|
def get_queryset(self):
|
||||||
|
qs = super(JobDetail, self).get_queryset().defer('result_stdout_text')
|
||||||
|
return qs
|
||||||
|
|
||||||
def update(self, request, *args, **kwargs):
|
def update(self, request, *args, **kwargs):
|
||||||
obj = self.get_object()
|
obj = self.get_object()
|
||||||
# Only allow changes (PUT/PATCH) when job status is "new".
|
# Only allow changes (PUT/PATCH) when job status is "new".
|
||||||
@@ -2783,6 +2792,11 @@ class UnifiedJobList(ListAPIView):
|
|||||||
model = UnifiedJob
|
model = UnifiedJob
|
||||||
serializer_class = UnifiedJobListSerializer
|
serializer_class = UnifiedJobListSerializer
|
||||||
new_in_148 = True
|
new_in_148 = True
|
||||||
|
|
||||||
|
def get_queryset(self):
|
||||||
|
qs = self.request.user.get_queryset(self.model).defer('result_stdout_text')
|
||||||
|
return qs
|
||||||
|
|
||||||
|
|
||||||
class UnifiedJobStdout(RetrieveAPIView):
|
class UnifiedJobStdout(RetrieveAPIView):
|
||||||
|
|
||||||
@@ -2793,8 +2807,17 @@ class UnifiedJobStdout(RetrieveAPIView):
|
|||||||
filter_backends = ()
|
filter_backends = ()
|
||||||
new_in_148 = True
|
new_in_148 = True
|
||||||
|
|
||||||
|
def get_queryset(self):
|
||||||
|
qs = super(UnifiedJobStdout, self).get_queryset().defer('result_stdout_text')
|
||||||
|
return qs
|
||||||
|
|
||||||
def retrieve(self, request, *args, **kwargs):
|
def retrieve(self, request, *args, **kwargs):
|
||||||
unified_job = self.get_object()
|
unified_job = self.get_object()
|
||||||
|
obj_size = unified_job.result_stdout_size
|
||||||
|
if request.accepted_renderer.format != 'txt_download' and obj_size > settings.STDOUT_MAX_BYTES_DISPLAY:
|
||||||
|
return Response("Standard Output too large to display (%d bytes), "
|
||||||
|
"only download supported for sizes over %d bytes" % (obj_size, settings.STDOUT_MAX_BYTES_DISPLAY))
|
||||||
|
|
||||||
if request.accepted_renderer.format in ('html', 'api', 'json'):
|
if request.accepted_renderer.format in ('html', 'api', 'json'):
|
||||||
start_line = request.QUERY_PARAMS.get('start_line', 0)
|
start_line = request.QUERY_PARAMS.get('start_line', 0)
|
||||||
end_line = request.QUERY_PARAMS.get('end_line', None)
|
end_line = request.QUERY_PARAMS.get('end_line', None)
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ from StringIO import StringIO
|
|||||||
|
|
||||||
# Django
|
# Django
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
from django.db import models
|
from django.db import models, connection
|
||||||
from django.core.exceptions import NON_FIELD_ERRORS
|
from django.core.exceptions import NON_FIELD_ERRORS
|
||||||
from django.utils.datastructures import SortedDict
|
from django.utils.datastructures import SortedDict
|
||||||
from django.utils.translation import ugettext_lazy as _
|
from django.utils.translation import ugettext_lazy as _
|
||||||
@@ -657,6 +657,13 @@ class UnifiedJob(PolymorphicModel, PasswordFieldsModel, CommonModelNameNotUnique
|
|||||||
def result_stdout(self):
|
def result_stdout(self):
|
||||||
return self._result_stdout_raw(escape_ascii=True)
|
return self._result_stdout_raw(escape_ascii=True)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def result_stdout_size(self):
|
||||||
|
cursor = connection.cursor()
|
||||||
|
cursor.execute("select length(result_stdout_text) from main_unifiedjob where id = %d" % self.pk)
|
||||||
|
record_size = cursor.fetchone()[0]
|
||||||
|
return record_size
|
||||||
|
|
||||||
def _result_stdout_raw_limited(self, start_line=0, end_line=None, redact_sensitive=True, escape_ascii=False):
|
def _result_stdout_raw_limited(self, start_line=0, end_line=None, redact_sensitive=True, escape_ascii=False):
|
||||||
return_buffer = u""
|
return_buffer = u""
|
||||||
if end_line is not None:
|
if end_line is not None:
|
||||||
|
|||||||
@@ -562,6 +562,8 @@ FACT_CACHE_PORT = 6564
|
|||||||
|
|
||||||
ORG_ADMINS_CAN_SEE_ALL_USERS = True
|
ORG_ADMINS_CAN_SEE_ALL_USERS = True
|
||||||
|
|
||||||
|
STDOUT_MAX_BYTES_DISPLAY = 1000000
|
||||||
|
|
||||||
# Logging configuration.
|
# Logging configuration.
|
||||||
LOGGING = {
|
LOGGING = {
|
||||||
'version': 1,
|
'version': 1,
|
||||||
|
|||||||
Reference in New Issue
Block a user