sshplex 1.4.0__tar.gz → 1.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.
- {sshplex-1.4.0/sshplex.egg-info → sshplex-1.5.0}/PKG-INFO +1 -1
- {sshplex-1.4.0 → sshplex-1.5.0}/pyproject.toml +1 -1
- {sshplex-1.4.0 → sshplex-1.5.0}/sshplex/lib/config.py +1 -1
- {sshplex-1.4.0 → sshplex-1.5.0}/sshplex/lib/multiplexer/tmux.py +4 -1
- {sshplex-1.4.0 → sshplex-1.5.0}/sshplex/sshplex_connector.py +30 -5
- {sshplex-1.4.0 → sshplex-1.5.0/sshplex.egg-info}/PKG-INFO +1 -1
- {sshplex-1.4.0 → sshplex-1.5.0}/LICENSE +0 -0
- {sshplex-1.4.0 → sshplex-1.5.0}/MANIFEST.in +0 -0
- {sshplex-1.4.0 → sshplex-1.5.0}/README.md +0 -0
- {sshplex-1.4.0 → sshplex-1.5.0}/setup.cfg +0 -0
- {sshplex-1.4.0 → sshplex-1.5.0}/sshplex/__init__.py +0 -0
- {sshplex-1.4.0 → sshplex-1.5.0}/sshplex/cli.py +0 -0
- {sshplex-1.4.0 → sshplex-1.5.0}/sshplex/config-template.yaml +0 -0
- {sshplex-1.4.0 → sshplex-1.5.0}/sshplex/lib/__init__.py +0 -0
- {sshplex-1.4.0 → sshplex-1.5.0}/sshplex/lib/cache.py +0 -0
- {sshplex-1.4.0 → sshplex-1.5.0}/sshplex/lib/logger.py +0 -0
- {sshplex-1.4.0 → sshplex-1.5.0}/sshplex/lib/multiplexer/__init__.py +0 -0
- {sshplex-1.4.0 → sshplex-1.5.0}/sshplex/lib/multiplexer/base.py +0 -0
- {sshplex-1.4.0 → sshplex-1.5.0}/sshplex/lib/onboarding/__init__.py +0 -0
- {sshplex-1.4.0 → sshplex-1.5.0}/sshplex/lib/onboarding/wizard.py +0 -0
- {sshplex-1.4.0 → sshplex-1.5.0}/sshplex/lib/sot/__init__.py +0 -0
- {sshplex-1.4.0 → sshplex-1.5.0}/sshplex/lib/sot/ansible.py +0 -0
- {sshplex-1.4.0 → sshplex-1.5.0}/sshplex/lib/sot/base.py +0 -0
- {sshplex-1.4.0 → sshplex-1.5.0}/sshplex/lib/sot/consul.py +0 -0
- {sshplex-1.4.0 → sshplex-1.5.0}/sshplex/lib/sot/factory.py +0 -0
- {sshplex-1.4.0 → sshplex-1.5.0}/sshplex/lib/sot/netbox.py +0 -0
- {sshplex-1.4.0 → sshplex-1.5.0}/sshplex/lib/sot/static.py +0 -0
- {sshplex-1.4.0 → sshplex-1.5.0}/sshplex/lib/ui/__init__.py +0 -0
- {sshplex-1.4.0 → sshplex-1.5.0}/sshplex/lib/ui/config_editor.py +0 -0
- {sshplex-1.4.0 → sshplex-1.5.0}/sshplex/lib/ui/host_selector.py +0 -0
- {sshplex-1.4.0 → sshplex-1.5.0}/sshplex/lib/ui/session_manager.py +0 -0
- {sshplex-1.4.0 → sshplex-1.5.0}/sshplex/main.py +0 -0
- {sshplex-1.4.0 → sshplex-1.5.0}/sshplex.egg-info/SOURCES.txt +0 -0
- {sshplex-1.4.0 → sshplex-1.5.0}/sshplex.egg-info/dependency_links.txt +0 -0
- {sshplex-1.4.0 → sshplex-1.5.0}/sshplex.egg-info/entry_points.txt +0 -0
- {sshplex-1.4.0 → sshplex-1.5.0}/sshplex.egg-info/requires.txt +0 -0
- {sshplex-1.4.0 → sshplex-1.5.0}/sshplex.egg-info/top_level.txt +0 -0
- {sshplex-1.4.0 → sshplex-1.5.0}/tests/test_cache.py +0 -0
- {sshplex-1.4.0 → sshplex-1.5.0}/tests/test_config.py +0 -0
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "sshplex"
|
|
7
|
-
version = "1.
|
|
7
|
+
version = "1.5.0"
|
|
8
8
|
description = "Multiplex your SSH connections with style"
|
|
9
9
|
authors = [{name = "MJAHED Sabri", email = "contact@sabrimjahed.com"}]
|
|
10
10
|
readme = "README.md"
|
|
@@ -88,7 +88,7 @@ class ConsulConfig(BaseModel):
|
|
|
88
88
|
port: int = Field(443, description="Consul port number")
|
|
89
89
|
token: str = Field("default_token", description="Consul token for authentication")
|
|
90
90
|
scheme: str = Field("https", description="URL scheme (e.g., 'https')")
|
|
91
|
-
verify: bool = Field(
|
|
91
|
+
verify: bool = Field(True, description="Whether to verify SSL certificates (default: True for security)")
|
|
92
92
|
dc: str = Field("dc1", description="Datacenter name")
|
|
93
93
|
cert: str = Field("", description="Path to SSL certificate")
|
|
94
94
|
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
"""SSHplex tmux multiplexer implementation."""
|
|
2
2
|
|
|
3
3
|
import platform
|
|
4
|
+
import re
|
|
4
5
|
import subprocess
|
|
5
6
|
import uuid
|
|
6
7
|
from datetime import datetime
|
|
@@ -261,8 +262,10 @@ class TmuxManager(MultiplexerBase):
|
|
|
261
262
|
return False
|
|
262
263
|
|
|
263
264
|
pane = self.panes[hostname]
|
|
265
|
+
# Sanitize title to prevent injection - only allow safe characters
|
|
266
|
+
safe_title = re.sub(r'[^\w\s.-]', '', title)[:50] # Remove dangerous chars, limit length
|
|
264
267
|
# Set pane title using printf escape sequence
|
|
265
|
-
pane.send_keys(f'printf "\\033]2;{
|
|
268
|
+
pane.send_keys(f'printf "\\033]2;{safe_title}\\033\\\\"', enter=True)
|
|
266
269
|
return True
|
|
267
270
|
|
|
268
271
|
except Exception as e:
|
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
"""SSHplex Connector - SSH connections and tmux session management."""
|
|
2
2
|
|
|
3
|
+
import os
|
|
3
4
|
import platform
|
|
5
|
+
import re
|
|
6
|
+
import shlex
|
|
4
7
|
import time
|
|
5
8
|
from datetime import datetime
|
|
6
9
|
from typing import Any, List, Optional
|
|
@@ -50,6 +53,10 @@ class SSHplexConnector:
|
|
|
50
53
|
|
|
51
54
|
if not username or not username.strip():
|
|
52
55
|
raise ValueError("SSH username cannot be empty")
|
|
56
|
+
|
|
57
|
+
# Validate username format to prevent injection
|
|
58
|
+
if not re.match(r'^[a-zA-Z0-9._-]+$', username):
|
|
59
|
+
raise ValueError(f"Invalid username format: {username}")
|
|
53
60
|
|
|
54
61
|
if port < 1 or port > 65535:
|
|
55
62
|
raise ValueError(f"SSH port must be between 1 and 65535, got {port}")
|
|
@@ -75,6 +82,12 @@ class SSHplexConnector:
|
|
|
75
82
|
for _i, host in enumerate(hosts):
|
|
76
83
|
hostname = host.ip if host.ip else host.name
|
|
77
84
|
|
|
85
|
+
# Validate hostname format to prevent injection
|
|
86
|
+
if not re.match(r'^[a-zA-Z0-9.-]+$', hostname):
|
|
87
|
+
self.logger.warning(f"Invalid hostname format (potential injection): {hostname}")
|
|
88
|
+
self.logger.warning("Skipping potentially malicious host")
|
|
89
|
+
continue
|
|
90
|
+
|
|
78
91
|
# Build SSH command
|
|
79
92
|
ssh_command = self._build_ssh_command(host, username, key_path, port)
|
|
80
93
|
|
|
@@ -194,9 +207,21 @@ class SSHplexConnector:
|
|
|
194
207
|
None
|
|
195
208
|
)
|
|
196
209
|
if proxy:
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
210
|
+
# Sanitize proxy credentials to prevent injection
|
|
211
|
+
proxy_host = proxy.host or ''
|
|
212
|
+
proxy_user = proxy.username or ''
|
|
213
|
+
proxy_key = proxy.key_path or ''
|
|
214
|
+
|
|
215
|
+
# Validate proxy values format
|
|
216
|
+
if (re.match(r'^[a-zA-Z0-9.-]+$', proxy_host) and
|
|
217
|
+
re.match(r'^[a-zA-Z0-9._-]+$', proxy_user) and
|
|
218
|
+
os.path.isabs(proxy_key) and '..' not in proxy_key):
|
|
219
|
+
# Use shlex.quote for safe shell escaping
|
|
220
|
+
cmd_parts.extend([
|
|
221
|
+
"-o", f"ProxyCommand=ssh -i {shlex.quote(proxy_key)} -W %h:%p {shlex.quote(proxy_user)}@{shlex.quote(proxy_host)}"
|
|
222
|
+
])
|
|
223
|
+
else:
|
|
224
|
+
self.logger.warning("Proxy configuration contains invalid values, skipping proxy")
|
|
200
225
|
except Exception:
|
|
201
226
|
# Proxy not configured for this host, continue without it
|
|
202
227
|
pass
|
|
@@ -233,8 +258,8 @@ class SSHplexConnector:
|
|
|
233
258
|
timeout = getattr(self.config.ssh, 'timeout', 10) if self.config else 10
|
|
234
259
|
cmd_parts.extend(["-o", f"ConnectTimeout={timeout}"])
|
|
235
260
|
|
|
236
|
-
# Add user@hostname
|
|
237
|
-
cmd_parts.append(f"{username}@{hostname}")
|
|
261
|
+
# Add user@hostname with proper escaping
|
|
262
|
+
cmd_parts.append(f"{shlex.quote(username)}@{shlex.quote(hostname)}")
|
|
238
263
|
|
|
239
264
|
return " ".join(cmd_parts)
|
|
240
265
|
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|