"""Actionable reports from privacy-minimized evidence, without raw sidecars.""" import hashlib import html import json from pathlib import Path from samsara.core.privacy import minimize_inventory from samsara.core.storage import write_private from samsara.analyzers.assessment import assess from samsara.analyzers.summary import summarize MANUAL_STEPS = ['Confirm application and business owners and obtain administrator retirement approval.', 'Validate historical usage across a representative business cycle, including infrequent and offline jobs.', 'Review DNS, load balancers, monitoring, backups, firewall logs and service-account consumers.', 'Verify custom tasks and share consumers; resolve incomplete checks and confirm rollback/retention requirements.'] def private_assessment(data, assessment): # Never trust a marker or cached finding to authorize raw data disclosure. source=data.get('server',{}).get('collection_source') clean=minimize_inventory(data, trusted_source=source) policy=assessment.get('scoring',{}).get('policy') if isinstance(assessment,dict) else None if policy is not None: import copy policy=copy.deepcopy(policy) policy['name']='Deployment scoring policy' return clean, assess(clean, policy=policy) def export_json(path, data, assessment): data, assessment=private_assessment(data, assessment) write_private(path,json.dumps({'inventory_result':data,'assessment':assessment},indent=2)) def inventory_artifact(path, data): """Explicit minimized inventory export; never writes a raw sidecar.""" data=minimize_inventory(data) payload=(json.dumps(data,indent=2,ensure_ascii=True)+'\n').encode('utf-8') destination=Path(path).with_suffix('.inventory.json') write_private(destination,payload) return destination, hashlib.sha256(payload).hexdigest() def table(headers, rows): escape = lambda value: html.escape(str(value)) if not rows: return '

None identified in collected evidence. Review missing evidence before drawing conclusions.

' return '
' + ''.join('' for h in headers) + '' + ''.join(''+''.join('' for v in row)+'' for row in rows) + '
'+escape(h)+'
'+escape(v)+'
' def export_html(path, data, assessment): escape = lambda value: html.escape(str(value)) data, assessment = private_assessment(data, assessment) source = 'SYNTHETIC DEMO — no real collection performed.' if data['synthetic'] else 'IMPORTED INVENTORY — source assertions not independently verified.' for platform in ('Linux','Windows'): if not data['synthetic'] and data['server'].get('collection_source') == f'Local {platform} read-only collection': source = f'LOCAL {platform.upper()} INVENTORY — read-only snapshot of the execution host.' summary = summarize(data,assessment) scoring = assessment['scoring'] content = '

Samsara assessment

'+escape(source)+'

' content += '

Summary

' content += '

'+escape(data['server']['hostname'])+' · '+escape(data['collected_at'])+'

' content += f"
Retirement risk estimate: {escape(assessment['level']).title()} · {assessment['score']}/100

{escape(scoring['explanation'])}

" content += f"

Evidence coverage: {summary['coverage_percent']}% · Evidence confidence: {escape(summary['confidence'])} · {summary['incomplete']} checks need review.

" content += '

Coverage and uncertainty are separate from risk. A zero or low score is not retirement clearance. Historical usage validation and administrator approval are always required.

' content += f"

OS type: {escape(summary['os_type'])}
Version: {escape(summary['version'])}

" actionable = [row for row in assessment['findings'] if row['points']>0] content += '

Actionable workload findings

' content += table(['Workload','Classification','Workload score','Evidence','Why this affects scoring'],[(row['category'],row['classification'],row['points'],row['evidence'],row['scoring_reason']) for row in actionable]) content += '

How the score was calculated

'+escape(scoring['formula'])+'

' content += table(['Workload','Selected rule','Score','Determines final score','Evidence observations'],[(row['title'],row['selected_rule'],row['score'],'Yes (co-leading)' if row['determines_final_score'] else 'No; below the maximum',row['evidence_count']) for row in scoring['breakdown']]) content += '
Policy weights, thresholds and audit identity

Policy SHA-256: '+escape(scoring['policy_id'])+'

'+escape(json.dumps(scoring['policy'],indent=2))+'
' content += '

Important software

Inventory name hints only; installed software does not establish active use.

' content += table(['Software','Version','Evidence'],[(r['name'],r['version'],r['evidence']) for r in summary['software'][:12]]) content += '

Scheduled jobs and scripts

Nonroutine enabled schedules; configuration does not confirm execution. All tasks remain in the inventory.

' content += table(['Job','Schedule','State'],[(r['name'],r['schedule'],r['status']) for r in summary['jobs'][:12]]) context = [r for r in assessment['findings'] if r['points']==0 and r['id'] not in ('coverage','observation-limits')] content += '

Unscored context and review items

Routine services, administrative shares, client traffic and unknown observations do not receive workload points.

' content += table(['Category','Classification','Context'],[(r['category'],r['classification'],r['evidence']) for r in context]) directions = {} for row in assessment['network_observations']: directions[row['direction']] = directions.get(row['direction'],0)+1 content += '

Network interpretation

Direction inferred from listener/PID or ephemeral-port patterns is a heuristic, not a captured initiating handshake. Confirmed workload findings describe attributed observed transport activity, not verified business ownership.

' content += table(['Direction / activity','Observations'],list(sorted(directions.items()))) content += '

Collection checks / missing evidence

Failed and partial checks reduce confidence; they never add workload risk points.

' checks = [] for name in sorted(set(data['checks']) | set(assessment['scoring']['policy']['coverage_weights'])): row = data['checks'].get(name,{'status':'not_collected','message':'No status supplied.'}) errors = '; '.join(f"{e['code']}: {e['message']}" for e in row.get('errors',[])) checks.append((name,row['status'],row.get('started_at','Not supplied'),row.get('completed_at','Not supplied'),row['message'],errors,'; '.join(row.get('permission_limitations',[])),'; '.join(row.get('limitations',[])))) content += table(['Check','Status','started_at (UTC)','completed_at (UTC)','Result','Errors','permission_limitations','Scope limitations'],checks) content += '

Recommended manual verification

' content += '

Privacy and omitted evidence

Accounts, task commands, certificate identities, child paths/filenames and public peer IPs are omitted; infrastructure names and share/mount roots are kept per the privacy policy `retain` switches and pass an identifier-pattern filter. No raw inventory sidecar is generated. JSON export contains the same minimized evidence. Numeric IP addresses remain confidential infrastructure metadata. This report does not establish PHI-free operation or HIPAA compliance.

' content += table(['Inventory section','Rows'],[(name,len(rows)) for name,rows in data['inventory'].items()]) css = 'body{font:15px system-ui;max-width:1180px;margin:32px auto;padding:20px;color:#233044;line-height:1.55}h1,h2{color:#173c60}.muted{color:#667}.source,.card,details{background:#eef3f8;padding:16px;border-radius:7px}.table{overflow-x:auto}table{border-collapse:collapse;width:100%;margin:12px 0}th,td{border:1px solid #d9e0e8;text-align:left;vertical-align:top;padding:9px;white-space:pre-wrap;overflow-wrap:anywhere}th{background:#eef3f8}pre{white-space:pre-wrap;overflow-wrap:anywhere}a{color:#1263a3}' write_private(path,'Samsara'+content+'')