PyVaultRCE 2.3.0__tar.gz → 2.4.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.
- {pyvaultrce-2.3.0 → pyvaultrce-2.4.0}/PKG-INFO +26 -3
- {pyvaultrce-2.3.0 → pyvaultrce-2.4.0}/PyVaultRCE.egg-info/PKG-INFO +26 -3
- {pyvaultrce-2.3.0 → pyvaultrce-2.4.0}/README.md +25 -2
- {pyvaultrce-2.3.0 → pyvaultrce-2.4.0}/codemanager/__init__.py +1 -1
- {pyvaultrce-2.3.0 → pyvaultrce-2.4.0}/codemanager/manager.py +25 -9
- {pyvaultrce-2.3.0 → pyvaultrce-2.4.0}/pyvaultrce/__init__.py +1 -1
- {pyvaultrce-2.3.0 → pyvaultrce-2.4.0}/pyvaultrce/manager.py +92 -26
- {pyvaultrce-2.3.0 → pyvaultrce-2.4.0}/setup.py +1 -1
- {pyvaultrce-2.3.0 → pyvaultrce-2.4.0}/PyVaultRCE.egg-info/SOURCES.txt +0 -0
- {pyvaultrce-2.3.0 → pyvaultrce-2.4.0}/PyVaultRCE.egg-info/dependency_links.txt +0 -0
- {pyvaultrce-2.3.0 → pyvaultrce-2.4.0}/PyVaultRCE.egg-info/requires.txt +0 -0
- {pyvaultrce-2.3.0 → pyvaultrce-2.4.0}/PyVaultRCE.egg-info/top_level.txt +0 -0
- {pyvaultrce-2.3.0 → pyvaultrce-2.4.0}/setup.cfg +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: PyVaultRCE
|
|
3
|
-
Version: 2.
|
|
3
|
+
Version: 2.4.0
|
|
4
4
|
Summary: Remote Code Execution & Hosting client for PyVault — source never exposed
|
|
5
5
|
Author: PyVault
|
|
6
6
|
Keywords: remote execution code hosting vault rce session
|
|
@@ -25,7 +25,7 @@ Dynamic: summary
|
|
|
25
25
|
|
|
26
26
|
# PyVaultRCE
|
|
27
27
|
|
|
28
|
-
Remote Code
|
|
28
|
+
Remote Code Hosting client for **PyVault**.
|
|
29
29
|
|
|
30
30
|
Upload Python scripts to a PyVault server and execute them remotely — the source code **never leaves the server**. Clients only ever hold a 21-character hex Session ID.
|
|
31
31
|
|
|
@@ -50,6 +50,16 @@ sid = CodeManager.enc_url("https://pastebin.com/abcXYZ123")
|
|
|
50
50
|
# Execute remotely (source stays on server)
|
|
51
51
|
CodeManager.run(sid)
|
|
52
52
|
|
|
53
|
+
# Upload with a short share link and library metadata
|
|
54
|
+
sid = CodeManager.enc(
|
|
55
|
+
"my_script.py",
|
|
56
|
+
label="analytics demo",
|
|
57
|
+
libraries=["requests", "pandas"],
|
|
58
|
+
)
|
|
59
|
+
|
|
60
|
+
# Hide PyVault status messages while running
|
|
61
|
+
CodeManager.run(sid, show_terminal=False)
|
|
62
|
+
|
|
53
63
|
# Check metadata without incrementing execution counter
|
|
54
64
|
CodeManager.info(sid)
|
|
55
65
|
|
|
@@ -65,13 +75,25 @@ Set `PYVAULT_URL` to point at your hosted PyVault server:
|
|
|
65
75
|
export PYVAULT_URL=https://your-server.replit.app
|
|
66
76
|
```
|
|
67
77
|
|
|
78
|
+
For local development the client uses `http://localhost:5000` when
|
|
79
|
+
`PYVAULT_URL` is not set. You can also control status output globally:
|
|
80
|
+
|
|
81
|
+
```bash
|
|
82
|
+
export PYVAULT_TERMINAL=off
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
Library names are metadata only. PyVault does not auto-install packages or
|
|
86
|
+
execute dependency installers; install the libraries in the Python environment
|
|
87
|
+
where `CodeManager.run()` executes.
|
|
88
|
+
|
|
68
89
|
## API Reference
|
|
69
90
|
|
|
70
91
|
| Method | Description |
|
|
71
92
|
|--------|-------------|
|
|
72
93
|
| `CodeManager.enc(file_path)` | Upload a local `.py` file → returns Session ID |
|
|
94
|
+
| `CodeManager.enc(file_path, label=..., libraries=[...])` | Upload with a label and visible library metadata |
|
|
73
95
|
| `CodeManager.enc_url(url)` | Download code from a paste URL and upload → returns Session ID |
|
|
74
|
-
| `CodeManager.run(session_id)` | Fetch and execute code by Session ID |
|
|
96
|
+
| `CodeManager.run(session_id, show_terminal=False)` | Fetch and execute code by Session ID with optional quiet status output |
|
|
75
97
|
| `CodeManager.info(session_id)` | Get session metadata (no exec count increment) |
|
|
76
98
|
| `CodeManager.ping()` | Check server connectivity |
|
|
77
99
|
| `CodeManager.edit(session_id, file_path, admin_token=...)` | Replace stored code (admin or owner token) |
|
|
@@ -102,6 +124,7 @@ export PYVAULT_URL=https://your-server.replit.app
|
|
|
102
124
|
- Session IDs are 21-character cryptographically random hex strings
|
|
103
125
|
- Source code is never written to disk on the client
|
|
104
126
|
- Code is encrypted at rest with Fernet on the server
|
|
127
|
+
- Share links expose only session metadata and declared library names, never source
|
|
105
128
|
- Admin edits require the admin access code; owner edits/deletes require the one-time owner token
|
|
106
129
|
- Sessions never expire unless an expiry is explicitly selected
|
|
107
130
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: PyVaultRCE
|
|
3
|
-
Version: 2.
|
|
3
|
+
Version: 2.4.0
|
|
4
4
|
Summary: Remote Code Execution & Hosting client for PyVault — source never exposed
|
|
5
5
|
Author: PyVault
|
|
6
6
|
Keywords: remote execution code hosting vault rce session
|
|
@@ -25,7 +25,7 @@ Dynamic: summary
|
|
|
25
25
|
|
|
26
26
|
# PyVaultRCE
|
|
27
27
|
|
|
28
|
-
Remote Code
|
|
28
|
+
Remote Code Hosting client for **PyVault**.
|
|
29
29
|
|
|
30
30
|
Upload Python scripts to a PyVault server and execute them remotely — the source code **never leaves the server**. Clients only ever hold a 21-character hex Session ID.
|
|
31
31
|
|
|
@@ -50,6 +50,16 @@ sid = CodeManager.enc_url("https://pastebin.com/abcXYZ123")
|
|
|
50
50
|
# Execute remotely (source stays on server)
|
|
51
51
|
CodeManager.run(sid)
|
|
52
52
|
|
|
53
|
+
# Upload with a short share link and library metadata
|
|
54
|
+
sid = CodeManager.enc(
|
|
55
|
+
"my_script.py",
|
|
56
|
+
label="analytics demo",
|
|
57
|
+
libraries=["requests", "pandas"],
|
|
58
|
+
)
|
|
59
|
+
|
|
60
|
+
# Hide PyVault status messages while running
|
|
61
|
+
CodeManager.run(sid, show_terminal=False)
|
|
62
|
+
|
|
53
63
|
# Check metadata without incrementing execution counter
|
|
54
64
|
CodeManager.info(sid)
|
|
55
65
|
|
|
@@ -65,13 +75,25 @@ Set `PYVAULT_URL` to point at your hosted PyVault server:
|
|
|
65
75
|
export PYVAULT_URL=https://your-server.replit.app
|
|
66
76
|
```
|
|
67
77
|
|
|
78
|
+
For local development the client uses `http://localhost:5000` when
|
|
79
|
+
`PYVAULT_URL` is not set. You can also control status output globally:
|
|
80
|
+
|
|
81
|
+
```bash
|
|
82
|
+
export PYVAULT_TERMINAL=off
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
Library names are metadata only. PyVault does not auto-install packages or
|
|
86
|
+
execute dependency installers; install the libraries in the Python environment
|
|
87
|
+
where `CodeManager.run()` executes.
|
|
88
|
+
|
|
68
89
|
## API Reference
|
|
69
90
|
|
|
70
91
|
| Method | Description |
|
|
71
92
|
|--------|-------------|
|
|
72
93
|
| `CodeManager.enc(file_path)` | Upload a local `.py` file → returns Session ID |
|
|
94
|
+
| `CodeManager.enc(file_path, label=..., libraries=[...])` | Upload with a label and visible library metadata |
|
|
73
95
|
| `CodeManager.enc_url(url)` | Download code from a paste URL and upload → returns Session ID |
|
|
74
|
-
| `CodeManager.run(session_id)` | Fetch and execute code by Session ID |
|
|
96
|
+
| `CodeManager.run(session_id, show_terminal=False)` | Fetch and execute code by Session ID with optional quiet status output |
|
|
75
97
|
| `CodeManager.info(session_id)` | Get session metadata (no exec count increment) |
|
|
76
98
|
| `CodeManager.ping()` | Check server connectivity |
|
|
77
99
|
| `CodeManager.edit(session_id, file_path, admin_token=...)` | Replace stored code (admin or owner token) |
|
|
@@ -102,6 +124,7 @@ export PYVAULT_URL=https://your-server.replit.app
|
|
|
102
124
|
- Session IDs are 21-character cryptographically random hex strings
|
|
103
125
|
- Source code is never written to disk on the client
|
|
104
126
|
- Code is encrypted at rest with Fernet on the server
|
|
127
|
+
- Share links expose only session metadata and declared library names, never source
|
|
105
128
|
- Admin edits require the admin access code; owner edits/deletes require the one-time owner token
|
|
106
129
|
- Sessions never expire unless an expiry is explicitly selected
|
|
107
130
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# PyVaultRCE
|
|
2
2
|
|
|
3
|
-
Remote Code
|
|
3
|
+
Remote Code Hosting client for **PyVault**.
|
|
4
4
|
|
|
5
5
|
Upload Python scripts to a PyVault server and execute them remotely — the source code **never leaves the server**. Clients only ever hold a 21-character hex Session ID.
|
|
6
6
|
|
|
@@ -25,6 +25,16 @@ sid = CodeManager.enc_url("https://pastebin.com/abcXYZ123")
|
|
|
25
25
|
# Execute remotely (source stays on server)
|
|
26
26
|
CodeManager.run(sid)
|
|
27
27
|
|
|
28
|
+
# Upload with a short share link and library metadata
|
|
29
|
+
sid = CodeManager.enc(
|
|
30
|
+
"my_script.py",
|
|
31
|
+
label="analytics demo",
|
|
32
|
+
libraries=["requests", "pandas"],
|
|
33
|
+
)
|
|
34
|
+
|
|
35
|
+
# Hide PyVault status messages while running
|
|
36
|
+
CodeManager.run(sid, show_terminal=False)
|
|
37
|
+
|
|
28
38
|
# Check metadata without incrementing execution counter
|
|
29
39
|
CodeManager.info(sid)
|
|
30
40
|
|
|
@@ -40,13 +50,25 @@ Set `PYVAULT_URL` to point at your hosted PyVault server:
|
|
|
40
50
|
export PYVAULT_URL=https://your-server.replit.app
|
|
41
51
|
```
|
|
42
52
|
|
|
53
|
+
For local development the client uses `http://localhost:5000` when
|
|
54
|
+
`PYVAULT_URL` is not set. You can also control status output globally:
|
|
55
|
+
|
|
56
|
+
```bash
|
|
57
|
+
export PYVAULT_TERMINAL=off
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
Library names are metadata only. PyVault does not auto-install packages or
|
|
61
|
+
execute dependency installers; install the libraries in the Python environment
|
|
62
|
+
where `CodeManager.run()` executes.
|
|
63
|
+
|
|
43
64
|
## API Reference
|
|
44
65
|
|
|
45
66
|
| Method | Description |
|
|
46
67
|
|--------|-------------|
|
|
47
68
|
| `CodeManager.enc(file_path)` | Upload a local `.py` file → returns Session ID |
|
|
69
|
+
| `CodeManager.enc(file_path, label=..., libraries=[...])` | Upload with a label and visible library metadata |
|
|
48
70
|
| `CodeManager.enc_url(url)` | Download code from a paste URL and upload → returns Session ID |
|
|
49
|
-
| `CodeManager.run(session_id)` | Fetch and execute code by Session ID |
|
|
71
|
+
| `CodeManager.run(session_id, show_terminal=False)` | Fetch and execute code by Session ID with optional quiet status output |
|
|
50
72
|
| `CodeManager.info(session_id)` | Get session metadata (no exec count increment) |
|
|
51
73
|
| `CodeManager.ping()` | Check server connectivity |
|
|
52
74
|
| `CodeManager.edit(session_id, file_path, admin_token=...)` | Replace stored code (admin or owner token) |
|
|
@@ -77,6 +99,7 @@ export PYVAULT_URL=https://your-server.replit.app
|
|
|
77
99
|
- Session IDs are 21-character cryptographically random hex strings
|
|
78
100
|
- Source code is never written to disk on the client
|
|
79
101
|
- Code is encrypted at rest with Fernet on the server
|
|
102
|
+
- Share links expose only session metadata and declared library names, never source
|
|
80
103
|
- Admin edits require the admin access code; owner edits/deletes require the one-time owner token
|
|
81
104
|
- Sessions never expire unless an expiry is explicitly selected
|
|
82
105
|
|
|
@@ -15,7 +15,7 @@ except ImportError:
|
|
|
15
15
|
"The 'requests' library is required. Install it with: pip install requests"
|
|
16
16
|
)
|
|
17
17
|
|
|
18
|
-
_DEFAULT_BASE_URL = "
|
|
18
|
+
_DEFAULT_BASE_URL = "http://localhost:5000"
|
|
19
19
|
|
|
20
20
|
# ── Validation helpers ───────────────────────────────────────────────────────
|
|
21
21
|
|
|
@@ -40,6 +40,19 @@ def _get_base_url(base_url: Optional[str]) -> str:
|
|
|
40
40
|
return url
|
|
41
41
|
|
|
42
42
|
|
|
43
|
+
def _terminal_enabled(show_terminal=None) -> bool:
|
|
44
|
+
if show_terminal is not None:
|
|
45
|
+
return bool(show_terminal)
|
|
46
|
+
return os.environ.get("PYVAULT_TERMINAL", "on").strip().lower() not in {
|
|
47
|
+
"0", "false", "off", "no", "quiet"
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
def _emit(message: str, show_terminal=None, *, error=False) -> None:
|
|
52
|
+
if _terminal_enabled(show_terminal):
|
|
53
|
+
print(message, file=sys.stderr if error else sys.stdout, flush=True)
|
|
54
|
+
|
|
55
|
+
|
|
43
56
|
# ── CodeManager ─────────────────────────────────────────────────────────────
|
|
44
57
|
|
|
45
58
|
class CodeManager:
|
|
@@ -59,6 +72,8 @@ class CodeManager:
|
|
|
59
72
|
file_path: str,
|
|
60
73
|
base_url: Optional[str] = None,
|
|
61
74
|
timeout: int = 30,
|
|
75
|
+
label: str = "",
|
|
76
|
+
libraries = "",
|
|
62
77
|
) -> str:
|
|
63
78
|
"""
|
|
64
79
|
Read a local Python file and upload it to the PyVault backend.
|
|
@@ -98,7 +113,7 @@ class CodeManager:
|
|
|
98
113
|
try:
|
|
99
114
|
response = requests.post(
|
|
100
115
|
url,
|
|
101
|
-
json={"code": code},
|
|
116
|
+
json={"code": code, "label": label, "libraries": libraries},
|
|
102
117
|
timeout=timeout,
|
|
103
118
|
)
|
|
104
119
|
except requests.exceptions.ConnectionError:
|
|
@@ -142,6 +157,7 @@ class CodeManager:
|
|
|
142
157
|
base_url: Optional[str] = None,
|
|
143
158
|
timeout: int = 30,
|
|
144
159
|
globals_dict: Optional[dict] = None,
|
|
160
|
+
show_terminal=None,
|
|
145
161
|
) -> None:
|
|
146
162
|
"""
|
|
147
163
|
Fetch the code for a Session ID from PyVault and execute it locally.
|
|
@@ -208,7 +224,7 @@ class CodeManager:
|
|
|
208
224
|
f"Server returned empty code for session '{session_id}'."
|
|
209
225
|
)
|
|
210
226
|
|
|
211
|
-
|
|
227
|
+
_emit(f"[PyVault] Executing session '{session_id}' (run #{exec_count})…", show_terminal)
|
|
212
228
|
|
|
213
229
|
namespace = globals_dict if globals_dict is not None else {
|
|
214
230
|
"__name__": "__pyvault__",
|
|
@@ -219,19 +235,19 @@ class CodeManager:
|
|
|
219
235
|
compiled = compile(code, f"<pyvault:{session_id}>", "exec")
|
|
220
236
|
exec(compiled, namespace)
|
|
221
237
|
except SyntaxError as exc:
|
|
222
|
-
|
|
223
|
-
|
|
238
|
+
_emit("\n[PyVault] ✕ Syntax error in remote code:", show_terminal, error=True)
|
|
239
|
+
_emit(f" Line {exc.lineno}: {exc.msg}", show_terminal, error=True)
|
|
224
240
|
if exc.text:
|
|
225
|
-
|
|
241
|
+
_emit(f" >>> {exc.text.strip()}", show_terminal, error=True)
|
|
226
242
|
raise
|
|
227
243
|
except Exception as exc:
|
|
228
|
-
|
|
244
|
+
_emit("\n[PyVault] ✕ Runtime error during execution:", show_terminal, error=True)
|
|
229
245
|
tb_lines = traceback.format_exc().splitlines()
|
|
230
246
|
for line in tb_lines:
|
|
231
|
-
|
|
247
|
+
_emit(f" {line}", show_terminal, error=True)
|
|
232
248
|
raise
|
|
233
249
|
|
|
234
|
-
|
|
250
|
+
_emit("[PyVault] ✓ Execution complete.", show_terminal)
|
|
235
251
|
|
|
236
252
|
@staticmethod
|
|
237
253
|
def edit(
|
|
@@ -23,7 +23,7 @@ try:
|
|
|
23
23
|
except ImportError:
|
|
24
24
|
_HAS_CRYPTO = False
|
|
25
25
|
|
|
26
|
-
_DEFAULT_BASE = "
|
|
26
|
+
_DEFAULT_BASE = "http://localhost:5000"
|
|
27
27
|
_MAX_LINES = 10_000
|
|
28
28
|
_SID_LEN = 21
|
|
29
29
|
_HEX_SET = frozenset("0123456789abcdef")
|
|
@@ -47,6 +47,33 @@ def _base(url: Optional[str]) -> str:
|
|
|
47
47
|
return (url or os.environ.get("PYVAULT_URL") or _DEFAULT_BASE).rstrip("/")
|
|
48
48
|
|
|
49
49
|
|
|
50
|
+
def _terminal_enabled(show_terminal: Optional[bool]) -> bool:
|
|
51
|
+
if show_terminal is not None:
|
|
52
|
+
return bool(show_terminal)
|
|
53
|
+
value = os.environ.get("PYVAULT_TERMINAL", "on").strip().lower()
|
|
54
|
+
return value not in {"0", "false", "off", "no", "quiet"}
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
def _emit(message: str, show_terminal: Optional[bool] = None, *, error: bool = False) -> None:
|
|
58
|
+
if _terminal_enabled(show_terminal):
|
|
59
|
+
print(message, file=sys.stderr if error else sys.stdout, flush=True)
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
def _normalise_libraries(value) -> str:
|
|
63
|
+
if isinstance(value, (list, tuple)):
|
|
64
|
+
values = value
|
|
65
|
+
elif isinstance(value, str):
|
|
66
|
+
values = re.split(r"[,\n]", value)
|
|
67
|
+
else:
|
|
68
|
+
values = []
|
|
69
|
+
cleaned = []
|
|
70
|
+
for item in values:
|
|
71
|
+
name = str(item).strip()
|
|
72
|
+
if name and re.fullmatch(r"[A-Za-z0-9_.-]{1,80}", name) and name not in cleaned:
|
|
73
|
+
cleaned.append(name)
|
|
74
|
+
return ", ".join(cleaned)[:500]
|
|
75
|
+
|
|
76
|
+
|
|
50
77
|
def _valid_sid(sid: str) -> bool:
|
|
51
78
|
return isinstance(sid, str) and len(sid) == _SID_LEN and all(c in _HEX_SET for c in sid)
|
|
52
79
|
|
|
@@ -150,8 +177,10 @@ def _upload_code(
|
|
|
150
177
|
timeout: int,
|
|
151
178
|
expires_in_hours: Optional[float] = None,
|
|
152
179
|
max_executions: int = 0,
|
|
180
|
+
label: str = "",
|
|
181
|
+
libraries = "",
|
|
153
182
|
) -> tuple:
|
|
154
|
-
"""Internal: push code string to /pyv/save. Returns
|
|
183
|
+
"""Internal: push code string to /pyv/save. Returns session metadata."""
|
|
155
184
|
if not code.strip():
|
|
156
185
|
raise ValueError("Code is empty — nothing to upload.")
|
|
157
186
|
line_count = len(code.splitlines())
|
|
@@ -164,6 +193,10 @@ def _upload_code(
|
|
|
164
193
|
payload["expires_in_hours"] = expires_in_hours
|
|
165
194
|
if max_executions and max_executions > 0:
|
|
166
195
|
payload["max_executions"] = max_executions
|
|
196
|
+
if label:
|
|
197
|
+
payload["label"] = str(label)[:200]
|
|
198
|
+
if libraries:
|
|
199
|
+
payload["libraries"] = _normalise_libraries(libraries)
|
|
167
200
|
|
|
168
201
|
url = _base(base_url) + "/pyv/save"
|
|
169
202
|
resp = _http_post(url, payload, timeout)
|
|
@@ -178,7 +211,7 @@ def _upload_code(
|
|
|
178
211
|
owner_token = data.get("owner_token", "")
|
|
179
212
|
if not _valid_sid(sid):
|
|
180
213
|
raise RuntimeError(f"Server returned unexpected Session ID: '{sid}'")
|
|
181
|
-
return sid, owner_token
|
|
214
|
+
return sid, owner_token, data.get("share_url", ""), data.get("libraries", "")
|
|
182
215
|
|
|
183
216
|
|
|
184
217
|
# ── CodeManager ───────────────────────────────────────────────────────────────
|
|
@@ -197,7 +230,8 @@ class CodeManager:
|
|
|
197
230
|
|
|
198
231
|
Environment variables:
|
|
199
232
|
PYVAULT_URL — base URL of the PyVault server
|
|
200
|
-
|
|
233
|
+
(default: http://localhost:5000; set PYVAULT_URL for a hosted server)
|
|
234
|
+
PYVAULT_TERMINAL — on/off terminal status messages (default: on)
|
|
201
235
|
"""
|
|
202
236
|
|
|
203
237
|
@staticmethod
|
|
@@ -207,6 +241,9 @@ class CodeManager:
|
|
|
207
241
|
timeout: int = 30,
|
|
208
242
|
expires_in_hours: Optional[float] = None,
|
|
209
243
|
max_executions: int = 0,
|
|
244
|
+
label: str = "",
|
|
245
|
+
libraries = "",
|
|
246
|
+
show_terminal: Optional[bool] = None,
|
|
210
247
|
) -> str:
|
|
211
248
|
"""
|
|
212
249
|
Read a local .py file and upload it to PyVault.
|
|
@@ -227,14 +264,20 @@ class CodeManager:
|
|
|
227
264
|
code = _read_file(file_path)
|
|
228
265
|
if not code.strip():
|
|
229
266
|
raise ValueError(f"The file '{file_path}' is empty — nothing to upload.")
|
|
230
|
-
sid, owner_token
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
267
|
+
sid, owner_token, share_url, safe_libraries = _upload_code(
|
|
268
|
+
code, base_url, timeout, expires_in_hours, max_executions, label, libraries
|
|
269
|
+
)
|
|
270
|
+
_emit(f"[PyVault] ✓ Uploaded — Session ID : {sid}", show_terminal)
|
|
271
|
+
_emit(f"[PyVault] Source : {os.path.abspath(file_path)}", show_terminal)
|
|
272
|
+
_emit(f"[PyVault] Lines : {len(code.splitlines()):,}", show_terminal)
|
|
273
|
+
if share_url:
|
|
274
|
+
_emit(f"[PyVault] Share link: {share_url}", show_terminal)
|
|
275
|
+
if safe_libraries:
|
|
276
|
+
_emit(f"[PyVault] Libraries: {safe_libraries}", show_terminal)
|
|
234
277
|
if owner_token:
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
278
|
+
_emit("\n[PyVault] ⚠ OWNER TOKEN (save this — shown only once!):", show_terminal)
|
|
279
|
+
_emit(f"[PyVault] {owner_token}", show_terminal)
|
|
280
|
+
_emit("[PyVault] Use this to delete or edit your session later.\n", show_terminal)
|
|
238
281
|
return sid
|
|
239
282
|
|
|
240
283
|
@staticmethod
|
|
@@ -242,6 +285,9 @@ class CodeManager:
|
|
|
242
285
|
paste_url: str,
|
|
243
286
|
base_url: Optional[str] = None,
|
|
244
287
|
timeout: int = 30,
|
|
288
|
+
label: str = "",
|
|
289
|
+
libraries = "",
|
|
290
|
+
show_terminal: Optional[bool] = None,
|
|
245
291
|
) -> str:
|
|
246
292
|
"""
|
|
247
293
|
Download Python code from a paste service URL and upload to PyVault.
|
|
@@ -265,7 +311,7 @@ class CodeManager:
|
|
|
265
311
|
resp = requests.get(
|
|
266
312
|
raw_url,
|
|
267
313
|
timeout=timeout,
|
|
268
|
-
headers={"User-Agent": "PyVaultRCE/2.
|
|
314
|
+
headers={"User-Agent": "PyVaultRCE/2.4"},
|
|
269
315
|
)
|
|
270
316
|
resp.raise_for_status()
|
|
271
317
|
except requests.exceptions.ConnectionError:
|
|
@@ -283,14 +329,20 @@ class CodeManager:
|
|
|
283
329
|
if not code.strip():
|
|
284
330
|
raise ValueError("Downloaded content is empty.")
|
|
285
331
|
|
|
286
|
-
sid, owner_token = _upload_code(
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
332
|
+
sid, owner_token, share_url, safe_libraries = _upload_code(
|
|
333
|
+
code, base_url, timeout, label=label, libraries=libraries
|
|
334
|
+
)
|
|
335
|
+
_emit(f"[PyVault] ✓ Uploaded from URL — Session ID : {sid}", show_terminal)
|
|
336
|
+
_emit(f"[PyVault] Source : {paste_url}", show_terminal)
|
|
337
|
+
_emit(f"[PyVault] Lines : {len(code.splitlines()):,}", show_terminal)
|
|
338
|
+
if share_url:
|
|
339
|
+
_emit(f"[PyVault] Share link: {share_url}", show_terminal)
|
|
340
|
+
if safe_libraries:
|
|
341
|
+
_emit(f"[PyVault] Libraries: {safe_libraries}", show_terminal)
|
|
290
342
|
if owner_token:
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
343
|
+
_emit("\n[PyVault] ⚠ OWNER TOKEN (save this — shown only once!):", show_terminal)
|
|
344
|
+
_emit(f"[PyVault] {owner_token}", show_terminal)
|
|
345
|
+
_emit("[PyVault] Use this to delete or edit your session later.\n", show_terminal)
|
|
294
346
|
return sid
|
|
295
347
|
|
|
296
348
|
@staticmethod
|
|
@@ -299,6 +351,7 @@ class CodeManager:
|
|
|
299
351
|
base_url: Optional[str] = None,
|
|
300
352
|
timeout: int = 30,
|
|
301
353
|
_ns: Optional[dict] = None,
|
|
354
|
+
show_terminal: Optional[bool] = None,
|
|
302
355
|
) -> None:
|
|
303
356
|
"""
|
|
304
357
|
Fetch the encrypted code for a Session ID from PyVault, decrypt it,
|
|
@@ -363,7 +416,7 @@ class CodeManager:
|
|
|
363
416
|
if not code.strip():
|
|
364
417
|
raise RuntimeError(f"Decrypted code is empty for session '{session_id}'.")
|
|
365
418
|
|
|
366
|
-
|
|
419
|
+
_emit(f"[PyVault] ▶ Running session '{session_id}' (execution #{run_count})…", show_terminal)
|
|
367
420
|
|
|
368
421
|
namespace = _ns if _ns is not None else {
|
|
369
422
|
"__name__": "__pyvault__",
|
|
@@ -373,18 +426,18 @@ class CodeManager:
|
|
|
373
426
|
try:
|
|
374
427
|
exec(compile(code, f"<vault:{session_id[:8]}…>", "exec"), namespace)
|
|
375
428
|
except SyntaxError as exc:
|
|
376
|
-
|
|
377
|
-
|
|
429
|
+
_emit("\n[PyVault] ✕ Syntax error:", show_terminal, error=True)
|
|
430
|
+
_emit(f" Line {exc.lineno}: {exc.msg}", show_terminal, error=True)
|
|
378
431
|
if exc.text:
|
|
379
|
-
|
|
432
|
+
_emit(f" >>> {exc.text.strip()}", show_terminal, error=True)
|
|
380
433
|
raise
|
|
381
434
|
except Exception:
|
|
382
|
-
|
|
435
|
+
_emit("\n[PyVault] ✕ Runtime error:", show_terminal, error=True)
|
|
383
436
|
for line in traceback.format_exc().splitlines():
|
|
384
|
-
|
|
437
|
+
_emit(f" {line}", show_terminal, error=True)
|
|
385
438
|
raise
|
|
386
439
|
|
|
387
|
-
|
|
440
|
+
_emit("[PyVault] ✓ Execution complete.", show_terminal)
|
|
388
441
|
|
|
389
442
|
@staticmethod
|
|
390
443
|
def info(
|
|
@@ -426,6 +479,19 @@ class CodeManager:
|
|
|
426
479
|
)
|
|
427
480
|
return data
|
|
428
481
|
|
|
482
|
+
@staticmethod
|
|
483
|
+
def share(
|
|
484
|
+
session_id: str,
|
|
485
|
+
base_url: Optional[str] = None,
|
|
486
|
+
timeout: int = 30,
|
|
487
|
+
) -> str:
|
|
488
|
+
"""Return the short public metadata link for a session."""
|
|
489
|
+
data = CodeManager.info(session_id, base_url=base_url, timeout=timeout)
|
|
490
|
+
share_url = data.get("share_url", "")
|
|
491
|
+
if not share_url:
|
|
492
|
+
raise RuntimeError("This server did not return a share URL.")
|
|
493
|
+
return share_url
|
|
494
|
+
|
|
429
495
|
@staticmethod
|
|
430
496
|
def ping(
|
|
431
497
|
base_url: Optional[str] = None,
|
|
@@ -5,7 +5,7 @@ with open("README.md", "r", encoding="utf-8") as fh:
|
|
|
5
5
|
|
|
6
6
|
setup(
|
|
7
7
|
name="PyVaultRCE",
|
|
8
|
-
version="2.
|
|
8
|
+
version="2.4.0",
|
|
9
9
|
author="PyVault",
|
|
10
10
|
description="Remote Code Execution & Hosting client for PyVault — source never exposed",
|
|
11
11
|
long_description=long_description,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|