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 +3 -0
- ghostcode/audit/__init__.py +0 -0
- ghostcode/audit/logger.py +149 -0
- ghostcode/cli.py +986 -0
- ghostcode/config.py +187 -0
- ghostcode/mapping/__init__.py +0 -0
- ghostcode/mapping/encryption.py +143 -0
- ghostcode/mapping/ghost_map.py +222 -0
- ghostcode/mapping/token_generator.py +78 -0
- ghostcode/parsers/__init__.py +0 -0
- ghostcode/parsers/base.py +66 -0
- ghostcode/parsers/cpp_parser.py +341 -0
- ghostcode/parsers/python_parser.py +397 -0
- ghostcode/reveal/__init__.py +0 -0
- ghostcode/reveal/code_revealer.py +374 -0
- ghostcode/reveal/diff_analyzer.py +426 -0
- ghostcode/reveal/explanation_translator.py +214 -0
- ghostcode/risk_report.py +467 -0
- ghostcode/transformers/__init__.py +0 -0
- ghostcode/transformers/comment_anonymizer.py +95 -0
- ghostcode/transformers/comment_stripper.py +60 -0
- ghostcode/transformers/isolator.py +312 -0
- ghostcode/transformers/literal_scrubber.py +452 -0
- ghostcode/transformers/multi_file.py +99 -0
- ghostcode/transformers/symbol_renamer.py +64 -0
- ghostcode/utils/__init__.py +0 -0
- ghostcode/utils/clipboard.py +52 -0
- ghostcode/utils/stdlib_registry.py +221 -0
- ghostcode-0.5.0.dist-info/METADATA +92 -0
- ghostcode-0.5.0.dist-info/RECORD +33 -0
- ghostcode-0.5.0.dist-info/WHEEL +5 -0
- ghostcode-0.5.0.dist-info/entry_points.txt +2 -0
- ghostcode-0.5.0.dist-info/top_level.txt +1 -0
ghostcode/__init__.py
ADDED
|
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:]
|