Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
56 changes: 38 additions & 18 deletions sos/cleaner/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -410,6 +410,43 @@ def review_parser_values(self):
self.opts.skip_cleaning_files = [fnmatch.translate(p) for p in
self.opts.skip_cleaning_files]

def display_cleaner_results(self, final_path, map_path):
"""Print the location of the obfuscated output.

Size is reported only for a packed archive. For a directory,
os.stat().st_size is filesystem metadata (commonly 4KiB), not
the size of the obfuscated data, so omit it. This follows the
same archive-vs-directory split as Policy.display_results.

:param final_path: Path to the obfuscated archive or directory
:type final_path: ``str``

:param map_path: Path to the private mapping file
:type map_path: ``str``
"""
arcstat = os.stat(final_path)

# while these messages won't be included in the log file in the
# archive some facilities, such as our avocado test suite, will
# sometimes not capture print() output, so leverage the ui_log
# to print to console
self.ui_log.info(
f"A mapping of obfuscated elements is available at\n\t{map_path}"
)
self.ui_log.info(
f"\nThe obfuscated archive is available at\n\t{final_path}\n"
)

if os.path.isfile(final_path):
self.ui_log.info(
f"\tSize\t{get_human_readable(arcstat.st_size)}"
)
self.ui_log.info(f"\tOwner\t{getpwuid(arcstat.st_uid).pw_name}\n")
self.ui_log.info(
"Please send the obfuscated archive to your support\n"
"representative and keep the mapping file private."
)

def execute(self):
"""SoSCleaner will begin by inspecting the TARGET option to determine
if it is a directory, archive, or archive of archives.
Expand Down Expand Up @@ -500,24 +537,7 @@ def execute(self):
self.obfuscate_string(arc_path.split('/')[-1])
)
shutil.move(arc_path, final_path)
arcstat = os.stat(final_path)

# while these messages won't be included in the log file in the archive
# some facilities, such as our avocado test suite, will sometimes not
# capture print() output, so leverage the ui_log to print to console
self.ui_log.info(
f"A mapping of obfuscated elements is available at\n\t{map_path}"
)
self.ui_log.info(
f"\nThe obfuscated archive is available at\n\t{final_path}\n"
)

self.ui_log.info(f"\tSize\t{get_human_readable(arcstat.st_size)}")
self.ui_log.info(f"\tOwner\t{getpwuid(arcstat.st_uid).pw_name}\n")
self.ui_log.info(
"Please send the obfuscated archive to your support\n"
"representative and keep the mapping file private."
)
self.display_cleaner_results(final_path, map_path)

self.cleanup()
return None
Expand Down
43 changes: 43 additions & 0 deletions tests/cleaner_tests/directory_size.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
# This file is part of the sos project: https://github.com/sosreport/sos
#
# This copyrighted material is made available to anyone wishing to use,
# modify, copy, or redistribute it subject to the terms and conditions of
# version 2 of the GNU General Public License.
#
# See the LICENSE file in the source distribution for further information.

import os

from sos_tests import StageTwoReportTest


class ExistingDirectoryCleanSizeTest(StageTwoReportTest):
"""Ensure sos clean on a directory does not report Size from the
directory inode. That value is filesystem metadata (often 4KiB),
not the obfuscated data size.

:avocado: tags=stagetwo
"""

sos_cmd = ''
sos_component = 'clean'

def pre_sos_setup(self):
src = os.path.join(self.tmpdir, 'src',
'sosreport-cleanertest-dirsize')
os.makedirs(os.path.join(src, 'sos_logs'))
with open(os.path.join(src, 'hostname'), 'w',
encoding='utf-8') as hfile:
hfile.write('cleanertest.example.com\n')
with open(os.path.join(src, 'sos_logs', 'sos.log'), 'w',
encoding='utf-8') as lfile:
lfile.write('test log\n')
map_file = os.path.join(self.tmpdir, 'default_mapping')
self.sos_cmd = f'--no-update --map-file {map_file} {src}'

def test_directory_output_omits_size(self):
self.assertOutputContains(
'The obfuscated archive is available at'
)
self.assertOutputNotContains('\tSize\t')
self.assertOutputContains('\tOwner\t')
55 changes: 55 additions & 0 deletions tests/unittests/cleaner_tests.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,16 @@
#
# See the LICENSE file in the source distribution for further information.

import os
import tempfile
import unittest
from ipaddress import ip_interface
from os.path import join
from unittest import mock

import sos.policies
from sos.cleaner import SoSCleaner
from sos.utilities import get_human_readable
from sos.cleaner.parsers.ip_parser import SoSIPParser
from sos.cleaner.parsers.mac_parser import SoSMacParser
from sos.cleaner.parsers.hostname_parser import SoSHostnameParser
Expand Down Expand Up @@ -723,3 +727,54 @@ def test_packed_dirs_empty_for_premature_manifest(self):
with mock.patch.object(self.archive, 'get_file_content',
return_value='{"components": {"report": {}}}'):
self.assertEqual(self.archive._load_packed_dirs(), [])


class CleanerDisplayResultsTests(unittest.TestCase):
"""Verify sos clean reports Size only for packed archives.

Directory output must not use os.stat().st_size, which is inode
metadata (often 4KiB) rather than the obfuscated data size.
"""

def setUp(self):
self.cleaner = mock.Mock()
self.cleaner.ui_log = mock.Mock()

def _info_messages(self):
return [call.args[0] for call in
self.cleaner.ui_log.info.call_args_list]

def test_archive_reports_file_size(self):
with tempfile.NamedTemporaryFile(delete=False) as tfile:
tfile.write(b'x' * 2048)
tfile.flush()
path = tfile.name
try:
SoSCleaner.display_cleaner_results(
self.cleaner, path, '/tmp/private_map'
)
msgs = self._info_messages()
expected = f"\tSize\t{get_human_readable(os.stat(path).st_size)}"
self.assertIn(expected, msgs)
self.assertTrue(any('\tOwner\t' in msg for msg in msgs))
self.assertTrue(any(path in msg for msg in msgs))
finally:
os.unlink(path)

def test_directory_omits_size(self):
with tempfile.TemporaryDirectory() as tmpdir:
with open(os.path.join(tmpdir, 'data'), 'wb') as dfile:
dfile.write(b'x' * 8192)
SoSCleaner.display_cleaner_results(
self.cleaner, tmpdir, '/tmp/private_map'
)
msgs = self._info_messages()
self.assertFalse(
any('\tSize\t' in msg for msg in msgs),
f"Directory output unexpectedly included Size: {msgs}"
)
self.assertTrue(any('\tOwner\t' in msg for msg in msgs))
self.assertTrue(any(tmpdir in msg for msg in msgs))
self.assertTrue(
any('private_map' in msg for msg in msgs)
)
Loading