Privacy hardening, remote-peer IP masking and review fixes
- Add privacy projection (core/privacy.py), private report storage, bounded
process capture; omit identities, free text and unapproved paths
- Mask remote peer IPs: only loopback/host/wildcard stay numeric, others are
reduced to remote_scope private/public (schema + scoring updated)
- Map ss TCP states to canonical names; ASCII-only numeric fields; fix
"Microsoft Visual C++" product matching (Python and PowerShell)
- Merge 7fed3b9: -Diagnostic switch, serialization exit code 3, PS7 module
path, UI error detail; keep hardened task/certificate collectors
- Add audit docs, privacy docs and regression tests
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -8,7 +8,7 @@ from samsara.collectors.linux import (collect_local_linux, parse_connections,
|
||||
parse_units, run_command, CollectionCancelled)
|
||||
from samsara.collectors.demo import collect_demo, LINUX_DEMO_HOST
|
||||
from samsara.analyzers.assessment import assess
|
||||
from samsara.reporting.export import export_html
|
||||
from report_helpers import export_html
|
||||
|
||||
|
||||
class LinuxTests(unittest.TestCase):
|
||||
@@ -83,7 +83,44 @@ class LinuxTests(unittest.TestCase):
|
||||
|
||||
@unittest.skipUnless(__import__('sys').platform.startswith('linux'), 'Linux command test')
|
||||
def test_command_timeout_and_cancellation(self):
|
||||
with self.assertRaisesRegex(RuntimeError, 'timed out'):
|
||||
with self.assertRaisesRegex(RuntimeError, 'timeout'):
|
||||
run_command(['sleep', '1'], timeout=.05)
|
||||
with self.assertRaises(CollectionCancelled):
|
||||
run_command(['sleep', '1'], cancelled=lambda: True)
|
||||
|
||||
|
||||
class RegressionReviewTests(unittest.TestCase):
|
||||
def test_ss_states_map_to_canonical_names(self):
|
||||
from samsara.collectors.linux import parse_connections
|
||||
rows = parse_connections('TIME-WAIT 0 0 10.0.0.5:44321 10.0.0.9:443\nCLOSE-WAIT 0 0 10.0.0.5:1 10.0.0.9:2\nFIN-WAIT-1 0 0 10.0.0.5:3 10.0.0.9:4\n')
|
||||
self.assertEqual([r['state'] for r in rows], ['TimeWait', 'CloseWait', 'FinWait1'])
|
||||
|
||||
def test_non_ascii_digits_and_cpp_product_label(self):
|
||||
import json
|
||||
from pathlib import Path
|
||||
from samsara.core.privacy import minimize_inventory, numeric_version, safe_time
|
||||
self.assertEqual(numeric_version('١٢.٣٤'), 'Not collected')
|
||||
self.assertIsNone(safe_time('٢٠٢٦-٠١-٠١T٠٠:٠٠:٠٠Z'))
|
||||
data = json.loads(Path('samsara/assets/demo_inventory.json').read_text())
|
||||
data['server']['os_build'] = '١٢٣٤٥'
|
||||
data['inventory']['software'] = [{'name': 'Microsoft Visual C++ 2015-2022 Redistributable (x64)', 'version': '14.38.33135'}]
|
||||
clean = minimize_inventory(data)
|
||||
self.assertNotEqual(clean['server']['os_build'], '١٢٣٤٥')
|
||||
self.assertEqual(clean['inventory']['software'][0]['name'], 'Microsoft Visual C++')
|
||||
|
||||
def test_remote_peer_addresses_are_masked_but_scoring_survives(self):
|
||||
import copy
|
||||
from samsara.core.privacy import minimize_inventory
|
||||
from samsara.models.inventory import validate_inventory
|
||||
data = json.loads(Path('tests/fixtures/scoring/active_sql_server.json').read_text())
|
||||
before = assess(minimize_inventory(copy.deepcopy(data)))['score']
|
||||
for row in data['inventory']['network']:
|
||||
if row.get('remote_address') not in ('', '0.0.0.0', '::'):
|
||||
row['remote_address'] = '93.184.216.34'
|
||||
clean = minimize_inventory(data)
|
||||
validate_inventory(clean)
|
||||
self.assertNotIn('93.184.216.34', json.dumps(clean))
|
||||
self.assertEqual(minimize_inventory(clean), clean)
|
||||
self.assertEqual(assess(clean)['score'], before)
|
||||
self.assertNotIn('93.184.216.34', json.dumps(assess(clean)))
|
||||
self.assertIn('public', {r['remote_scope'] for r in clean['inventory']['network']})
|
||||
|
||||
Reference in New Issue
Block a user