ghostcode 0.5.0__tar.gz
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-0.5.0/PKG-INFO +92 -0
- ghostcode-0.5.0/README_PYPI.md +62 -0
- ghostcode-0.5.0/ghostcode/__init__.py +3 -0
- ghostcode-0.5.0/ghostcode/audit/__init__.py +0 -0
- ghostcode-0.5.0/ghostcode/audit/logger.py +149 -0
- ghostcode-0.5.0/ghostcode/cli.py +986 -0
- ghostcode-0.5.0/ghostcode/config.py +187 -0
- ghostcode-0.5.0/ghostcode/mapping/__init__.py +0 -0
- ghostcode-0.5.0/ghostcode/mapping/encryption.py +143 -0
- ghostcode-0.5.0/ghostcode/mapping/ghost_map.py +222 -0
- ghostcode-0.5.0/ghostcode/mapping/token_generator.py +78 -0
- ghostcode-0.5.0/ghostcode/parsers/__init__.py +0 -0
- ghostcode-0.5.0/ghostcode/parsers/base.py +66 -0
- ghostcode-0.5.0/ghostcode/parsers/cpp_parser.py +341 -0
- ghostcode-0.5.0/ghostcode/parsers/python_parser.py +397 -0
- ghostcode-0.5.0/ghostcode/reveal/__init__.py +0 -0
- ghostcode-0.5.0/ghostcode/reveal/code_revealer.py +374 -0
- ghostcode-0.5.0/ghostcode/reveal/diff_analyzer.py +426 -0
- ghostcode-0.5.0/ghostcode/reveal/explanation_translator.py +214 -0
- ghostcode-0.5.0/ghostcode/risk_report.py +467 -0
- ghostcode-0.5.0/ghostcode/transformers/__init__.py +0 -0
- ghostcode-0.5.0/ghostcode/transformers/comment_anonymizer.py +95 -0
- ghostcode-0.5.0/ghostcode/transformers/comment_stripper.py +60 -0
- ghostcode-0.5.0/ghostcode/transformers/isolator.py +312 -0
- ghostcode-0.5.0/ghostcode/transformers/literal_scrubber.py +452 -0
- ghostcode-0.5.0/ghostcode/transformers/multi_file.py +99 -0
- ghostcode-0.5.0/ghostcode/transformers/symbol_renamer.py +64 -0
- ghostcode-0.5.0/ghostcode/utils/__init__.py +0 -0
- ghostcode-0.5.0/ghostcode/utils/clipboard.py +52 -0
- ghostcode-0.5.0/ghostcode/utils/stdlib_registry.py +221 -0
- ghostcode-0.5.0/ghostcode.egg-info/PKG-INFO +92 -0
- ghostcode-0.5.0/ghostcode.egg-info/SOURCES.txt +45 -0
- ghostcode-0.5.0/ghostcode.egg-info/dependency_links.txt +1 -0
- ghostcode-0.5.0/ghostcode.egg-info/entry_points.txt +2 -0
- ghostcode-0.5.0/ghostcode.egg-info/requires.txt +4 -0
- ghostcode-0.5.0/ghostcode.egg-info/top_level.txt +1 -0
- ghostcode-0.5.0/pyproject.toml +47 -0
- ghostcode-0.5.0/setup.cfg +4 -0
- ghostcode-0.5.0/setup.py +18 -0
- ghostcode-0.5.0/tests/test_comment_stripper.py +53 -0
- ghostcode-0.5.0/tests/test_config.py +51 -0
- ghostcode-0.5.0/tests/test_end_to_end.py +234 -0
- ghostcode-0.5.0/tests/test_ghost_map.py +83 -0
- ghostcode-0.5.0/tests/test_literal_scrubber.py +43 -0
- ghostcode-0.5.0/tests/test_revealer.py +68 -0
- ghostcode-0.5.0/tests/test_risk_report.py +343 -0
- ghostcode-0.5.0/tests/test_token_generator.py +46 -0
ghostcode-0.5.0/PKG-INFO
ADDED
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: ghostcode
|
|
3
|
+
Version: 0.5.0
|
|
4
|
+
Summary: Privacy proxy for developers — hide business context before sharing code with AI
|
|
5
|
+
Author: Smit Vaishanav
|
|
6
|
+
License: MIT
|
|
7
|
+
Project-URL: Homepage, https://github.com/smitvaishnav/anti-ai
|
|
8
|
+
Project-URL: Repository, https://github.com/smitvaishnav/anti-ai
|
|
9
|
+
Project-URL: Issues, https://github.com/smitvaishnav/anti-ai/issues
|
|
10
|
+
Keywords: privacy,security,ai,llm,code-anonymization,ghostcode
|
|
11
|
+
Classifier: Development Status :: 4 - Beta
|
|
12
|
+
Classifier: Intended Audience :: Developers
|
|
13
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
14
|
+
Classifier: Operating System :: OS Independent
|
|
15
|
+
Classifier: Programming Language :: Python :: 3
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
20
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
21
|
+
Classifier: Topic :: Security
|
|
22
|
+
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
23
|
+
Requires-Python: >=3.9
|
|
24
|
+
Description-Content-Type: text/markdown
|
|
25
|
+
Requires-Dist: click>=8.1.0
|
|
26
|
+
Requires-Dist: libclang>=16.0.0
|
|
27
|
+
Requires-Dist: cryptography>=41.0.0
|
|
28
|
+
Requires-Dist: pyyaml>=6.0
|
|
29
|
+
Dynamic: requires-python
|
|
30
|
+
|
|
31
|
+
# GhostCode — Privacy Proxy for Developers
|
|
32
|
+
|
|
33
|
+
**Hide your business logic before sharing code with LLMs. Local-first, open-source, zero telemetry.**
|
|
34
|
+
|
|
35
|
+
GhostCode replaces your private symbols (variable names, functions, strings, comments) with opaque ghost tokens before you share code with AI. The AI sees `gv_001` instead of `customer_revenue`. When you get your answer back, GhostCode restores everything.
|
|
36
|
+
|
|
37
|
+
## Installation
|
|
38
|
+
|
|
39
|
+
```bash
|
|
40
|
+
pip install ghostcode
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
## Quick Start
|
|
44
|
+
|
|
45
|
+
```bash
|
|
46
|
+
# Hide a file (Level 2: names + comments + literals)
|
|
47
|
+
ghost hide my_code.py --level 2
|
|
48
|
+
|
|
49
|
+
# Paste the ghost output into ChatGPT/Claude, get a response, save it
|
|
50
|
+
|
|
51
|
+
# Reveal — restore original names in the AI's response
|
|
52
|
+
ghost reveal ai_response.py --map-file .ghostcode/maps/my_code_*.json
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
## Privacy Levels
|
|
56
|
+
|
|
57
|
+
| Level | What it does |
|
|
58
|
+
|-------|-------------|
|
|
59
|
+
| 1 | Rename symbols + strip comments |
|
|
60
|
+
| 2 | + Scrub string/numeric literals |
|
|
61
|
+
| 3 | + Function isolation with stubs |
|
|
62
|
+
| 4 | + Dimension generalization |
|
|
63
|
+
|
|
64
|
+
## Pre-Send Risk Report
|
|
65
|
+
|
|
66
|
+
Every `ghost hide` shows a risk assessment:
|
|
67
|
+
- Symbols and literals scrubbed/flagged/kept
|
|
68
|
+
- Structural patterns visible to the AI
|
|
69
|
+
- **Estimated domain exposure: LOW / MEDIUM / HIGH**
|
|
70
|
+
|
|
71
|
+
## VS Code Extension
|
|
72
|
+
|
|
73
|
+
Install the [GhostCode VS Code extension](https://marketplace.visualstudio.com/items?itemName=SmitVaishanav.ghostcode) for one-click hide/reveal buttons in the editor toolbar.
|
|
74
|
+
|
|
75
|
+
## Supported Languages
|
|
76
|
+
|
|
77
|
+
- Python (.py)
|
|
78
|
+
- C/C++ (.cpp, .cc, .c, .h, .hpp)
|
|
79
|
+
|
|
80
|
+
## Features
|
|
81
|
+
|
|
82
|
+
- 🔒 **AST-based symbol renaming** — not regex, real parsing
|
|
83
|
+
- 📊 **Smart literal scrubbing** — keeps `0`, `1`, `pi`; scrubs URLs, API keys, business strings
|
|
84
|
+
- 🗺️ **Ghost Map** — bidirectional mapping stored as JSON
|
|
85
|
+
- 🔐 **Encrypted maps** — optional AES encryption for map files
|
|
86
|
+
- 📋 **Audit logging** — immutable JSON logs for compliance
|
|
87
|
+
- 🔄 **Round-trip reveal** — restores names in both code and AI explanations
|
|
88
|
+
- ⚡ **Zero cloud, zero telemetry** — everything runs locally
|
|
89
|
+
|
|
90
|
+
## License
|
|
91
|
+
|
|
92
|
+
MIT
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
# GhostCode — Privacy Proxy for Developers
|
|
2
|
+
|
|
3
|
+
**Hide your business logic before sharing code with LLMs. Local-first, open-source, zero telemetry.**
|
|
4
|
+
|
|
5
|
+
GhostCode replaces your private symbols (variable names, functions, strings, comments) with opaque ghost tokens before you share code with AI. The AI sees `gv_001` instead of `customer_revenue`. When you get your answer back, GhostCode restores everything.
|
|
6
|
+
|
|
7
|
+
## Installation
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
pip install ghostcode
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
## Quick Start
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
# Hide a file (Level 2: names + comments + literals)
|
|
17
|
+
ghost hide my_code.py --level 2
|
|
18
|
+
|
|
19
|
+
# Paste the ghost output into ChatGPT/Claude, get a response, save it
|
|
20
|
+
|
|
21
|
+
# Reveal — restore original names in the AI's response
|
|
22
|
+
ghost reveal ai_response.py --map-file .ghostcode/maps/my_code_*.json
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
## Privacy Levels
|
|
26
|
+
|
|
27
|
+
| Level | What it does |
|
|
28
|
+
|-------|-------------|
|
|
29
|
+
| 1 | Rename symbols + strip comments |
|
|
30
|
+
| 2 | + Scrub string/numeric literals |
|
|
31
|
+
| 3 | + Function isolation with stubs |
|
|
32
|
+
| 4 | + Dimension generalization |
|
|
33
|
+
|
|
34
|
+
## Pre-Send Risk Report
|
|
35
|
+
|
|
36
|
+
Every `ghost hide` shows a risk assessment:
|
|
37
|
+
- Symbols and literals scrubbed/flagged/kept
|
|
38
|
+
- Structural patterns visible to the AI
|
|
39
|
+
- **Estimated domain exposure: LOW / MEDIUM / HIGH**
|
|
40
|
+
|
|
41
|
+
## VS Code Extension
|
|
42
|
+
|
|
43
|
+
Install the [GhostCode VS Code extension](https://marketplace.visualstudio.com/items?itemName=SmitVaishanav.ghostcode) for one-click hide/reveal buttons in the editor toolbar.
|
|
44
|
+
|
|
45
|
+
## Supported Languages
|
|
46
|
+
|
|
47
|
+
- Python (.py)
|
|
48
|
+
- C/C++ (.cpp, .cc, .c, .h, .hpp)
|
|
49
|
+
|
|
50
|
+
## Features
|
|
51
|
+
|
|
52
|
+
- 🔒 **AST-based symbol renaming** — not regex, real parsing
|
|
53
|
+
- 📊 **Smart literal scrubbing** — keeps `0`, `1`, `pi`; scrubs URLs, API keys, business strings
|
|
54
|
+
- 🗺️ **Ghost Map** — bidirectional mapping stored as JSON
|
|
55
|
+
- 🔐 **Encrypted maps** — optional AES encryption for map files
|
|
56
|
+
- 📋 **Audit logging** — immutable JSON logs for compliance
|
|
57
|
+
- 🔄 **Round-trip reveal** — restores names in both code and AI explanations
|
|
58
|
+
- ⚡ **Zero cloud, zero telemetry** — everything runs locally
|
|
59
|
+
|
|
60
|
+
## License
|
|
61
|
+
|
|
62
|
+
MIT
|
|
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:]
|