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:
@@ -189,6 +189,8 @@ def classify_dependencies(data):
|
||||
state = str(row.get('state','')).lower()
|
||||
proc = normal_name(row.get('process',''))
|
||||
remote,local = address(row.get('remote_address')), address(row.get('local_address'))
|
||||
has_remote = remote is not None or row.get('remote_scope') in ('private','public')
|
||||
peer = row.get('remote_address') or (f"{row['remote_scope']} peer (address withheld)" if row.get('remote_scope') else '')
|
||||
matching = []
|
||||
if state in ('established','estab') and local is not None and known_pid(row):
|
||||
wildcard = ipaddress.ip_address('0.0.0.0' if local.version==4 else '::')
|
||||
@@ -207,7 +209,7 @@ def classify_dependencies(data):
|
||||
basis = 'Direction supplied by inventory source; not independently verified.'
|
||||
elif state in ('established','estab') and matching:
|
||||
direction,basis = 'Inbound (inferred)','Local address/port and nonzero PID match a listener. Client initiation is inferred, not observed.'
|
||||
elif state in ('established','estab') and remote and type(row.get('local_port')) is int and row['local_port']>=49152 and 0 < row.get('remote_port',0)<49152:
|
||||
elif state in ('established','estab') and has_remote and type(row.get('local_port')) is int and row['local_port']>=49152 and 0 < row.get('remote_port',0)<49152:
|
||||
direction,basis = 'Outbound (inferred)','Ephemeral local port to a non-ephemeral remote port without an attributed listener; heuristic only.'
|
||||
family = row_family(row)
|
||||
classification, rationale = UNKNOWN, 'No attributed application workload. No score from the connection alone.'
|
||||
@@ -219,12 +221,12 @@ def classify_dependencies(data):
|
||||
if direction == 'Listener':
|
||||
evidence(family,'attributed_application_listener',f"{row.get('process','unknown')} PID {row.get('pid','unknown')} listening at {row.get('local_address')}:{row.get('local_port')}; a listener alone is potential exposure.",ref)
|
||||
classification,rationale = POTENTIAL,'Listener attributed to a known application; grouped with service evidence.'
|
||||
elif state in ('established','estab') and direction.startswith('Inbound') and matching and remote and not remote.is_unspecified:
|
||||
elif state in ('established','estab') and direction.startswith('Inbound') and matching and has_remote and not (remote and remote.is_unspecified):
|
||||
if family == 'file-sharing':
|
||||
evidence(family,'observed_smb_transport',f"File-sharing transport observed on local port {row.get('local_port')}; specific share/export use is unverified without correlated open files.",ref)
|
||||
classification,rationale = POTENTIAL,'File-sharing transport observed; no named business share attributed.'
|
||||
else:
|
||||
evidence(family,'observed_application_clients',f"Observed application TCP activity {row.get('local_address')}:{row.get('local_port')} ↔ {row.get('remote_address')}:{row.get('remote_port')}, PID {row.get('pid')}; matches listener(s) {matching}. {basis} Business ownership remains unverified.",ref,True)
|
||||
evidence(family,'observed_application_clients',f"Observed application TCP activity {row.get('local_address')}:{row.get('local_port')} ↔ {peer}:{row.get('remote_port')}, PID {row.get('pid')}; matches listener(s) {matching}. {basis} Business ownership remains unverified.",ref,True)
|
||||
classification,rationale = CONFIRMED,'Observed transport activity on an attributed application listener; direction inference is recorded separately.'
|
||||
else:
|
||||
note('Other application traffic',UNKNOWN,f"{direction}: {row.get('process','unknown')}; not scored as an inbound consumer. {basis}",ref)
|
||||
@@ -237,7 +239,7 @@ def classify_dependencies(data):
|
||||
classification,rationale=STANDARD,'Baseline OS/management endpoint; no application workload attributed.'
|
||||
note('OS networking',STANDARD,rationale,ref)
|
||||
else:
|
||||
note('Unattributed networking',UNKNOWN,f"{direction}: {row.get('local_address')}:{row.get('local_port')} ↔ {row.get('remote_address')}:{row.get('remote_port')}. {basis}",ref)
|
||||
note('Unattributed networking',UNKNOWN,f"{direction}: {row.get('local_address')}:{row.get('local_port')} ↔ {peer}:{row.get('remote_port')}. {basis}",ref)
|
||||
network_observations.append({'inventory_index':index,'direction':direction,'direction_basis':basis,
|
||||
'classification':classification,'workload':family or '', 'rationale':rationale})
|
||||
return groups, aggregates, network_observations, task_observations
|
||||
|
||||
Reference in New Issue
Block a user