95 lines
3.2 KiB
Python
95 lines
3.2 KiB
Python
# reporters.py
|
|
from __future__ import annotations
|
|
|
|
import csv
|
|
import json
|
|
from dataclasses import asdict
|
|
from typing import List
|
|
from datetime import datetime
|
|
|
|
from core import ScanResult
|
|
|
|
def export_json(results: List[ScanResult], out_path: str) -> None:
|
|
payload = []
|
|
for r in results:
|
|
d = asdict(r)
|
|
payload.append(d)
|
|
with open(out_path, "w", encoding="utf-8") as f:
|
|
json.dump(payload, f, ensure_ascii=False, indent=2)
|
|
|
|
def export_csv(results: List[ScanResult], out_path: str) -> None:
|
|
fields = [
|
|
"path","file_type","size_bytes","sha256","context",
|
|
"total_risk","level","author","last_modified_by","software",
|
|
"created","modified","gps_lat","gps_lon","title","keywords"
|
|
]
|
|
with open(out_path, "w", newline="", encoding="utf-8") as f:
|
|
w = csv.DictWriter(f, fieldnames=fields)
|
|
w.writeheader()
|
|
for r in results:
|
|
gps = r.metadata.get("gps") if isinstance(r.metadata.get("gps"), dict) else {}
|
|
row = {
|
|
"path": r.path,
|
|
"file_type": r.file_type,
|
|
"size_bytes": r.size_bytes,
|
|
"sha256": r.sha256,
|
|
"context": r.context,
|
|
"total_risk": r.total_risk,
|
|
"level": r.level,
|
|
"author": r.metadata.get("author"),
|
|
"last_modified_by": r.metadata.get("last_modified_by"),
|
|
"software": r.metadata.get("software"),
|
|
"created": r.metadata.get("created"),
|
|
"modified": r.metadata.get("modified"),
|
|
"gps_lat": gps.get("lat"),
|
|
"gps_lon": gps.get("lon"),
|
|
"title": r.metadata.get("title"),
|
|
"keywords": r.metadata.get("keywords"),
|
|
}
|
|
w.writerow(row)
|
|
|
|
def export_html(results: List[ScanResult], out_path: str) -> None:
|
|
now = datetime.now().isoformat(sep=" ", timespec="seconds")
|
|
rows = []
|
|
for r in results:
|
|
gps = r.metadata.get("gps") if isinstance(r.metadata.get("gps"), dict) else {}
|
|
rows.append(f"""
|
|
<tr>
|
|
<td>{r.level}</td>
|
|
<td>{r.total_risk}</td>
|
|
<td>{r.file_type}</td>
|
|
<td style="max-width:420px; word-break:break-all;">{r.path}</td>
|
|
<td>{r.metadata.get("author","") or ""}</td>
|
|
<td>{gps.get("lat","")}</td>
|
|
<td>{gps.get("lon","")}</td>
|
|
<td>{r.metadata.get("software","") or ""}</td>
|
|
</tr>
|
|
""")
|
|
html = f"""
|
|
<html><head><meta charset="utf-8">
|
|
<title>Metadata Risk Report</title>
|
|
<style>
|
|
body {{ font-family: Arial, sans-serif; padding: 16px; }}
|
|
table {{ border-collapse: collapse; width: 100%; }}
|
|
th, td {{ border: 1px solid #ccc; padding: 8px; font-size: 14px; }}
|
|
th {{ background: #f3f3f3; }}
|
|
</style>
|
|
</head><body>
|
|
<h2>Metadata Risk Report</h2>
|
|
<div>Generated: {now}</div>
|
|
<table>
|
|
<thead>
|
|
<tr>
|
|
<th>Level</th><th>Score</th><th>Type</th><th>Path</th>
|
|
<th>Author/User</th><th>GPS Lat</th><th>GPS Lon</th><th>Software</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
{''.join(rows)}
|
|
</tbody>
|
|
</table>
|
|
</body></html>
|
|
"""
|
|
with open(out_path, "w", encoding="utf-8") as f:
|
|
f.write(html)
|