[Bug Fix 4.6] AAP-49077 Task stdout escapes quotes twice only with Controller API api/v2/jobs/{id}/stdout/?format=txt (#7071)

* Move logic to unified job model instead of view

* Refine logic to only apply to double escaped characters to prevent touching unicord chars

* Refine logic to only apply to stdout so that it does not impact webhook notifications

* Revise naming to reflect correction to escapes, not just escape quotes

* Update code comments to reflect fixing double escapes vs double escaped quotes specifically

* Add regex for 5 most common python escape chars to make fix more robust
This commit is contained in:
Lila Yasin 2025-09-02 14:49:13 -04:00 committed by GitHub
parent e3a9d9fbe8
commit 44e9dee9c7
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

View File

@ -1197,6 +1197,13 @@ class UnifiedJob(
fd = StringIO(fd.getvalue().replace('\\r\\n', '\n'))
return fd
def _fix_double_escapes(self, content):
"""
Collapse double-escaped sequences into single-escaped form.
"""
# Replace \\ followed by one of ' " \ n r t
return re.sub(r'\\([\'"\\nrt])', r'\1', content)
def _escape_ascii(self, content):
# Remove ANSI escape sequences used to embed event data.
content = re.sub(r'\x1b\[K(?:[A-Za-z0-9+/=]+\x1b\[\d+D)+\x1b\[K', '', content)
@ -1204,12 +1211,14 @@ class UnifiedJob(
content = re.sub(r'\x1b[^m]*m', '', content)
return content
def _result_stdout_raw(self, redact_sensitive=False, escape_ascii=False):
def _result_stdout_raw(self, redact_sensitive=False, escape_ascii=False, fix_escapes=False):
content = self.result_stdout_raw_handle().read()
if redact_sensitive:
content = UriCleaner.remove_sensitive(content)
if escape_ascii:
content = self._escape_ascii(content)
if fix_escapes:
content = self._fix_double_escapes(content)
return content
@property
@ -1218,9 +1227,10 @@ class UnifiedJob(
@property
def result_stdout(self):
return self._result_stdout_raw(escape_ascii=True)
# Human-facing output should fix escapes
return self._result_stdout_raw(escape_ascii=True, fix_escapes=True)
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, fix_escapes=False):
return_buffer = StringIO()
if end_line is not None:
end_line = int(end_line)
@ -1243,14 +1253,18 @@ class UnifiedJob(
return_buffer = UriCleaner.remove_sensitive(return_buffer)
if escape_ascii:
return_buffer = self._escape_ascii(return_buffer)
if fix_escapes:
return_buffer = self._fix_double_escapes(return_buffer)
return return_buffer, start_actual, end_actual, absolute_end
def result_stdout_raw_limited(self, start_line=0, end_line=None, redact_sensitive=False):
# Raw should NOT fix escapes
return self._result_stdout_raw_limited(start_line, end_line, redact_sensitive)
def result_stdout_limited(self, start_line=0, end_line=None, redact_sensitive=False):
return self._result_stdout_raw_limited(start_line, end_line, redact_sensitive, escape_ascii=True)
# Human-facing should fix escapes
return self._result_stdout_raw_limited(start_line, end_line, redact_sensitive, escape_ascii=True, fix_escapes=True)
@property
def workflow_job_id(self):