mirror of
https://github.com/ansible/awx.git
synced 2026-03-11 06:29:31 -02:30
redact project update urls when downloading stdout
* For ProjectUpdate jobs. Redact potentially sensitive urls from the output.
This commit is contained in:
@@ -77,6 +77,7 @@ from awx.main.utils import (
|
|||||||
from awx.main.utils.encryption import encrypt_value
|
from awx.main.utils.encryption import encrypt_value
|
||||||
from awx.main.utils.filters import SmartFilter
|
from awx.main.utils.filters import SmartFilter
|
||||||
from awx.main.utils.insights import filter_insights_api_response
|
from awx.main.utils.insights import filter_insights_api_response
|
||||||
|
from awx.main.redact import UriCleaner
|
||||||
from awx.api.permissions import (
|
from awx.api.permissions import (
|
||||||
JobTemplateCallbackPermission,
|
JobTemplateCallbackPermission,
|
||||||
TaskPermission,
|
TaskPermission,
|
||||||
@@ -4645,9 +4646,17 @@ class UnifiedJobList(ListAPIView):
|
|||||||
serializer_class = UnifiedJobListSerializer
|
serializer_class = UnifiedJobListSerializer
|
||||||
|
|
||||||
|
|
||||||
class StdoutANSIFilter(object):
|
def redact_ansi(line):
|
||||||
|
# Remove ANSI escape sequences used to embed event data.
|
||||||
|
line = re.sub(r'\x1b\[K(?:[A-Za-z0-9+/=]+\x1b\[\d+D)+\x1b\[K', '', line)
|
||||||
|
# Remove ANSI color escape sequences.
|
||||||
|
return re.sub(r'\x1b[^m]*m', '', line)
|
||||||
|
|
||||||
|
|
||||||
|
class StdoutFilter(object):
|
||||||
|
|
||||||
def __init__(self, fileobj):
|
def __init__(self, fileobj):
|
||||||
|
self._functions = []
|
||||||
self.fileobj = fileobj
|
self.fileobj = fileobj
|
||||||
self.extra_data = ''
|
self.extra_data = ''
|
||||||
if hasattr(fileobj, 'close'):
|
if hasattr(fileobj, 'close'):
|
||||||
@@ -4659,10 +4668,7 @@ class StdoutANSIFilter(object):
|
|||||||
line = self.fileobj.readline(size)
|
line = self.fileobj.readline(size)
|
||||||
if not line:
|
if not line:
|
||||||
break
|
break
|
||||||
# Remove ANSI escape sequences used to embed event data.
|
line = self.process_line(line)
|
||||||
line = re.sub(r'\x1b\[K(?:[A-Za-z0-9+/=]+\x1b\[\d+D)+\x1b\[K', '', line)
|
|
||||||
# Remove ANSI color escape sequences.
|
|
||||||
line = re.sub(r'\x1b[^m]*m', '', line)
|
|
||||||
data += line
|
data += line
|
||||||
if size > 0 and len(data) > size:
|
if size > 0 and len(data) > size:
|
||||||
self.extra_data = data[size:]
|
self.extra_data = data[size:]
|
||||||
@@ -4671,6 +4677,14 @@ class StdoutANSIFilter(object):
|
|||||||
self.extra_data = ''
|
self.extra_data = ''
|
||||||
return data
|
return data
|
||||||
|
|
||||||
|
def register(self, func):
|
||||||
|
self._functions.append(func)
|
||||||
|
|
||||||
|
def process_line(self, line):
|
||||||
|
for func in self._functions:
|
||||||
|
line = func(line)
|
||||||
|
return line
|
||||||
|
|
||||||
|
|
||||||
class UnifiedJobStdout(RetrieveAPIView):
|
class UnifiedJobStdout(RetrieveAPIView):
|
||||||
|
|
||||||
@@ -4728,9 +4742,12 @@ class UnifiedJobStdout(RetrieveAPIView):
|
|||||||
suffix='.ansi' if target_format == 'ansi_download' else ''
|
suffix='.ansi' if target_format == 'ansi_download' else ''
|
||||||
)
|
)
|
||||||
content_fd = unified_job.result_stdout_raw_handle(enforce_max_bytes=False)
|
content_fd = unified_job.result_stdout_raw_handle(enforce_max_bytes=False)
|
||||||
|
redactor = StdoutFilter(content_fd)
|
||||||
if target_format == 'txt_download':
|
if target_format == 'txt_download':
|
||||||
content_fd = StdoutANSIFilter(content_fd)
|
redactor.register(redact_ansi)
|
||||||
response = HttpResponse(FileWrapper(content_fd), content_type='text/plain')
|
if type(unified_job) == ProjectUpdate:
|
||||||
|
redactor.register(UriCleaner.remove_sensitive)
|
||||||
|
response = HttpResponse(FileWrapper(redactor), content_type='text/plain')
|
||||||
response["Content-Disposition"] = 'attachment; filename="{}"'.format(filename)
|
response["Content-Disposition"] = 'attachment; filename="{}"'.format(filename)
|
||||||
return response
|
return response
|
||||||
else:
|
else:
|
||||||
|
|||||||
@@ -79,20 +79,22 @@ TEST_CLEARTEXT.append({
|
|||||||
})
|
})
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize('username, password, not_uri', [
|
@pytest.mark.parametrize('username, password, not_uri, expected', [
|
||||||
('', '', 'www.famfamfam.com](http://www.famfamfam.com/fijdlfd'),
|
('', '', 'www.famfamfam.com](http://www.famfamfam.com/fijdlfd', 'www.famfamfam.com](http://www.famfamfam.com/fijdlfd'),
|
||||||
('', '', 'https://www.famfamfam.com](http://www.famfamfam.com/fijdlfd'),
|
('', '', 'https://www.famfamfam.com](http://www.famfamfam.com/fijdlfd', '$encrypted$'),
|
||||||
('root', 'gigity', 'https://root@gigity@www.famfamfam.com](http://www.famfamfam.com/fijdlfd'),
|
('root', 'gigity', 'https://root@gigity@www.famfamfam.com](http://www.famfamfam.com/fijdlfd', '$encrypted$'),
|
||||||
('root', 'gigity@', 'https://root:gigity@@@www.famfamfam.com](http://www.famfamfam.com/fijdlfd'),
|
('root', 'gigity@', 'https://root:gigity@@@www.famfamfam.com](http://www.famfamfam.com/fijdlfd', '$encrypted$'),
|
||||||
])
|
])
|
||||||
# should redact sensitive usernames and passwords
|
# should redact sensitive usernames and passwords
|
||||||
def test_non_uri_redact(username, password, not_uri):
|
def test_non_uri_redact(username, password, not_uri, expected):
|
||||||
redacted_str = UriCleaner.remove_sensitive(not_uri)
|
redacted_str = UriCleaner.remove_sensitive(not_uri)
|
||||||
if username:
|
if username:
|
||||||
assert username not in redacted_str
|
assert username not in redacted_str
|
||||||
if password:
|
if password:
|
||||||
assert password not in redacted_str
|
assert password not in redacted_str
|
||||||
|
|
||||||
|
assert redacted_str == expected
|
||||||
|
|
||||||
|
|
||||||
def test_multiple_non_uri_redact():
|
def test_multiple_non_uri_redact():
|
||||||
non_uri = 'https://www.famfamfam.com](http://www.famfamfam.com/fijdlfd hi '
|
non_uri = 'https://www.famfamfam.com](http://www.famfamfam.com/fijdlfd hi '
|
||||||
|
|||||||
Reference in New Issue
Block a user