ghostcode 0.5.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
ghostcode/__init__.py ADDED
@@ -0,0 +1,3 @@
1
+ """GhostCode — Privacy Proxy for Developers."""
2
+
3
+ __version__ = "0.5.0"
File without changes
@@ -0,0 +1,149 @@
1
+ """Audit logger.
2
+
3
+ Every ghost hide/reveal invocation produces an immutable JSON log entry.
4
+ The security team can review exactly what was scrubbed, what was kept,
5
+ and what warnings were raised.
6
+
7
+ Log location: ~/.ghostcode/audit/YYYY-MM-DD.jsonl
8
+ Format: JSON Lines (one JSON object per line, append-only)
9
+
10
+ Each entry contains:
11
+ - Timestamp and user info
12
+ - Action (hide/reveal)
13
+ - File details and scrub level
14
+ - Symbol/literal/comment counts
15
+ - SHA-256 hashes of input and output
16
+ - Any warnings raised
17
+ """
18
+
19
+ import getpass
20
+ import hashlib
21
+ import json
22
+ import os
23
+ import platform
24
+ from datetime import datetime, timezone
25
+
26
+
27
+ def _get_audit_dir() -> str:
28
+ """Get the audit log directory."""
29
+ return os.path.join(os.path.expanduser("~"), ".ghostcode", "audit")
30
+
31
+
32
+ def _hash_content(content: str) -> str:
33
+ """SHA-256 hash of content."""
34
+ return hashlib.sha256(content.encode()).hexdigest()
35
+
36
+
37
+ def _hash_file(filepath: str) -> str:
38
+ """SHA-256 hash of a file's contents."""
39
+ try:
40
+ with open(filepath, "rb") as f:
41
+ return hashlib.sha256(f.read()).hexdigest()
42
+ except (FileNotFoundError, PermissionError):
43
+ return "unavailable"
44
+
45
+
46
+ class AuditLogger:
47
+ """Append-only audit logger for GhostCode operations."""
48
+
49
+ def __init__(self, enabled: bool = True):
50
+ self._enabled = enabled
51
+ self._audit_dir = _get_audit_dir()
52
+
53
+ def log_hide(self, source_files: list[str], scrub_level: int,
54
+ function_isolated: str | None,
55
+ symbols_scrubbed: int, literals_scrubbed: int,
56
+ literals_flagged: int, literals_kept: int,
57
+ comments_stripped: int, warnings: list[dict],
58
+ ghost_output_path: str, map_path: str,
59
+ ghost_output_content: str = ""):
60
+ """Log a hide operation."""
61
+ if not self._enabled:
62
+ return
63
+
64
+ entry = {
65
+ "timestamp": datetime.now(timezone.utc).isoformat(),
66
+ "action": "hide",
67
+ "user": getpass.getuser(),
68
+ "hostname": platform.node(),
69
+ "source_files": source_files,
70
+ "scrub_level": scrub_level,
71
+ "function_isolated": function_isolated,
72
+ "symbols_scrubbed": symbols_scrubbed,
73
+ "literals_scrubbed": literals_scrubbed,
74
+ "literals_flagged": literals_flagged,
75
+ "literals_kept": literals_kept,
76
+ "comments_stripped": comments_stripped,
77
+ "warnings": [w.get("type", str(w)) for w in warnings],
78
+ "warning_count": len(warnings),
79
+ "ghost_output_hash": (
80
+ _hash_content(ghost_output_content) if ghost_output_content
81
+ else _hash_file(ghost_output_path)
82
+ ),
83
+ "map_hash": _hash_file(map_path),
84
+ }
85
+
86
+ self._write(entry)
87
+
88
+ def log_reveal(self, input_file: str, map_file: str,
89
+ mode: str, symbols_restored: int,
90
+ new_symbols: list[str],
91
+ new_dependencies: list[str],
92
+ annotations_count: int,
93
+ confidence: str, confidence_score: int,
94
+ output_path: str):
95
+ """Log a reveal operation."""
96
+ if not self._enabled:
97
+ return
98
+
99
+ entry = {
100
+ "timestamp": datetime.now(timezone.utc).isoformat(),
101
+ "action": "reveal",
102
+ "user": getpass.getuser(),
103
+ "hostname": platform.node(),
104
+ "input_file": input_file,
105
+ "map_used": map_file,
106
+ "mode": mode,
107
+ "symbols_restored": symbols_restored,
108
+ "new_symbols_detected": len(new_symbols),
109
+ "new_symbols": new_symbols,
110
+ "new_dependencies": new_dependencies,
111
+ "annotations": annotations_count,
112
+ "confidence": confidence,
113
+ "confidence_score": confidence_score,
114
+ "output_hash": _hash_file(output_path),
115
+ }
116
+
117
+ self._write(entry)
118
+
119
+ def _write(self, entry: dict):
120
+ """Append a log entry to today's log file."""
121
+ os.makedirs(self._audit_dir, exist_ok=True)
122
+ today = datetime.now().strftime("%Y-%m-%d")
123
+ log_path = os.path.join(self._audit_dir, f"{today}.jsonl")
124
+
125
+ with open(log_path, "a") as f:
126
+ f.write(json.dumps(entry) + "\n")
127
+
128
+ def get_recent_entries(self, count: int = 10) -> list[dict]:
129
+ """Read the most recent audit log entries."""
130
+ entries = []
131
+ if not os.path.exists(self._audit_dir):
132
+ return entries
133
+
134
+ log_files = sorted(
135
+ [f for f in os.listdir(self._audit_dir) if f.endswith(".jsonl")],
136
+ reverse=True,
137
+ )
138
+
139
+ for log_file in log_files:
140
+ path = os.path.join(self._audit_dir, log_file)
141
+ with open(path) as f:
142
+ for line in f:
143
+ line = line.strip()
144
+ if line:
145
+ entries.append(json.loads(line))
146
+ if len(entries) >= count:
147
+ break
148
+
149
+ return entries[-count:]