webbee 0.5.0__tar.gz → 0.5.2__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.
- {webbee-0.5.0 → webbee-0.5.2}/CHANGELOG.md +14 -0
- {webbee-0.5.0 → webbee-0.5.2}/PKG-INFO +4 -3
- {webbee-0.5.0 → webbee-0.5.2}/pyproject.toml +3 -3
- webbee-0.5.2/src/webbee/__init__.py +1 -0
- {webbee-0.5.0 → webbee-0.5.2}/src/webbee/intel/service.py +7 -1
- {webbee-0.5.0 → webbee-0.5.2}/src/webbee/tools.py +11 -2
- {webbee-0.5.0 → webbee-0.5.2}/tests/test_intel_service.py +17 -0
- {webbee-0.5.0 → webbee-0.5.2}/tests/test_tools.py +45 -0
- webbee-0.5.0/src/webbee/__init__.py +0 -1
- {webbee-0.5.0 → webbee-0.5.2}/.github/workflows/publish.yml +0 -0
- {webbee-0.5.0 → webbee-0.5.2}/.gitignore +0 -0
- {webbee-0.5.0 → webbee-0.5.2}/LICENSE +0 -0
- {webbee-0.5.0 → webbee-0.5.2}/README.md +0 -0
- {webbee-0.5.0 → webbee-0.5.2}/install.sh +0 -0
- {webbee-0.5.0 → webbee-0.5.2}/src/webbee/__main__.py +0 -0
- {webbee-0.5.0 → webbee-0.5.2}/src/webbee/account.py +0 -0
- {webbee-0.5.0 → webbee-0.5.2}/src/webbee/active_sessions.py +0 -0
- {webbee-0.5.0 → webbee-0.5.2}/src/webbee/banner_art.py +0 -0
- {webbee-0.5.0 → webbee-0.5.2}/src/webbee/boot.py +0 -0
- {webbee-0.5.0 → webbee-0.5.2}/src/webbee/browser.py +0 -0
- {webbee-0.5.0 → webbee-0.5.2}/src/webbee/checkpoints.py +0 -0
- {webbee-0.5.0 → webbee-0.5.2}/src/webbee/cli.py +0 -0
- {webbee-0.5.0 → webbee-0.5.2}/src/webbee/clipboard.py +0 -0
- {webbee-0.5.0 → webbee-0.5.2}/src/webbee/clipboard_read.py +0 -0
- {webbee-0.5.0 → webbee-0.5.2}/src/webbee/clipboard_session.py +0 -0
- {webbee-0.5.0 → webbee-0.5.2}/src/webbee/coding_context.py +0 -0
- {webbee-0.5.0 → webbee-0.5.2}/src/webbee/commands.py +0 -0
- {webbee-0.5.0 → webbee-0.5.2}/src/webbee/config.py +0 -0
- {webbee-0.5.0 → webbee-0.5.2}/src/webbee/consent.py +0 -0
- {webbee-0.5.0 → webbee-0.5.2}/src/webbee/details.py +0 -0
- {webbee-0.5.0 → webbee-0.5.2}/src/webbee/events.py +0 -0
- {webbee-0.5.0 → webbee-0.5.2}/src/webbee/frames.py +0 -0
- {webbee-0.5.0 → webbee-0.5.2}/src/webbee/home.py +0 -0
- {webbee-0.5.0 → webbee-0.5.2}/src/webbee/home_view.py +0 -0
- {webbee-0.5.0 → webbee-0.5.2}/src/webbee/http.py +0 -0
- {webbee-0.5.0 → webbee-0.5.2}/src/webbee/instance_lock.py +0 -0
- {webbee-0.5.0 → webbee-0.5.2}/src/webbee/intel/__init__.py +0 -0
- {webbee-0.5.0 → webbee-0.5.2}/src/webbee/intel/graph.py +0 -0
- {webbee-0.5.0 → webbee-0.5.2}/src/webbee/intel/outline.py +0 -0
- {webbee-0.5.0 → webbee-0.5.2}/src/webbee/intel/query.py +0 -0
- {webbee-0.5.0 → webbee-0.5.2}/src/webbee/intel/store.py +0 -0
- {webbee-0.5.0 → webbee-0.5.2}/src/webbee/intel/vectors.py +0 -0
- {webbee-0.5.0 → webbee-0.5.2}/src/webbee/intel/watch.py +0 -0
- {webbee-0.5.0 → webbee-0.5.2}/src/webbee/media.py +0 -0
- {webbee-0.5.0 → webbee-0.5.2}/src/webbee/modals.py +0 -0
- {webbee-0.5.0 → webbee-0.5.2}/src/webbee/mode_store.py +0 -0
- {webbee-0.5.0 → webbee-0.5.2}/src/webbee/native_files.py +0 -0
- {webbee-0.5.0 → webbee-0.5.2}/src/webbee/native_read.py +0 -0
- {webbee-0.5.0 → webbee-0.5.2}/src/webbee/newtab_mode.py +0 -0
- {webbee-0.5.0 → webbee-0.5.2}/src/webbee/output_pane.py +0 -0
- {webbee-0.5.0 → webbee-0.5.2}/src/webbee/pricing_constants.py +0 -0
- {webbee-0.5.0 → webbee-0.5.2}/src/webbee/queue_panel.py +0 -0
- {webbee-0.5.0 → webbee-0.5.2}/src/webbee/reflow.py +0 -0
- {webbee-0.5.0 → webbee-0.5.2}/src/webbee/remote.py +0 -0
- {webbee-0.5.0 → webbee-0.5.2}/src/webbee/render.py +0 -0
- {webbee-0.5.0 → webbee-0.5.2}/src/webbee/repl.py +0 -0
- {webbee-0.5.0 → webbee-0.5.2}/src/webbee/repo.py +0 -0
- {webbee-0.5.0 → webbee-0.5.2}/src/webbee/selection.py +0 -0
- {webbee-0.5.0 → webbee-0.5.2}/src/webbee/session.py +0 -0
- {webbee-0.5.0 → webbee-0.5.2}/src/webbee/sessions.py +0 -0
- {webbee-0.5.0 → webbee-0.5.2}/src/webbee/sizing.py +0 -0
- {webbee-0.5.0 → webbee-0.5.2}/src/webbee/slots.py +0 -0
- {webbee-0.5.0 → webbee-0.5.2}/src/webbee/steer.py +0 -0
- {webbee-0.5.0 → webbee-0.5.2}/src/webbee/stream.py +0 -0
- {webbee-0.5.0 → webbee-0.5.2}/src/webbee/tab_store.py +0 -0
- {webbee-0.5.0 → webbee-0.5.2}/src/webbee/tabs.py +0 -0
- {webbee-0.5.0 → webbee-0.5.2}/src/webbee/thread.py +0 -0
- {webbee-0.5.0 → webbee-0.5.2}/src/webbee/tier_store.py +0 -0
- {webbee-0.5.0 → webbee-0.5.2}/src/webbee/todo_panel.py +0 -0
- {webbee-0.5.0 → webbee-0.5.2}/src/webbee/tokens.py +0 -0
- {webbee-0.5.0 → webbee-0.5.2}/src/webbee/tui.py +0 -0
- {webbee-0.5.0 → webbee-0.5.2}/src/webbee/update.py +0 -0
- {webbee-0.5.0 → webbee-0.5.2}/src/webbee/urlopen.py +0 -0
- {webbee-0.5.0 → webbee-0.5.2}/src/webbee/wallet.py +0 -0
- {webbee-0.5.0 → webbee-0.5.2}/src/webbee/worktrees.py +0 -0
- {webbee-0.5.0 → webbee-0.5.2}/tests/__init__.py +0 -0
- {webbee-0.5.0 → webbee-0.5.2}/tests/conftest.py +0 -0
- {webbee-0.5.0 → webbee-0.5.2}/tests/test_account.py +0 -0
- {webbee-0.5.0 → webbee-0.5.2}/tests/test_active_sessions.py +0 -0
- {webbee-0.5.0 → webbee-0.5.2}/tests/test_audit_2026_07_25_findings.py +0 -0
- {webbee-0.5.0 → webbee-0.5.2}/tests/test_checkpoints.py +0 -0
- {webbee-0.5.0 → webbee-0.5.2}/tests/test_cli.py +0 -0
- {webbee-0.5.0 → webbee-0.5.2}/tests/test_clipboard.py +0 -0
- {webbee-0.5.0 → webbee-0.5.2}/tests/test_clipboard_read.py +0 -0
- {webbee-0.5.0 → webbee-0.5.2}/tests/test_coding_context.py +0 -0
- {webbee-0.5.0 → webbee-0.5.2}/tests/test_commands.py +0 -0
- {webbee-0.5.0 → webbee-0.5.2}/tests/test_config.py +0 -0
- {webbee-0.5.0 → webbee-0.5.2}/tests/test_cpc_contract_stable.py +0 -0
- {webbee-0.5.0 → webbee-0.5.2}/tests/test_cpc_outline.py +0 -0
- {webbee-0.5.0 → webbee-0.5.2}/tests/test_cross_loop_token.py +0 -0
- {webbee-0.5.0 → webbee-0.5.2}/tests/test_details.py +0 -0
- {webbee-0.5.0 → webbee-0.5.2}/tests/test_events.py +0 -0
- {webbee-0.5.0 → webbee-0.5.2}/tests/test_freeze_fix.py +0 -0
- {webbee-0.5.0 → webbee-0.5.2}/tests/test_home.py +0 -0
- {webbee-0.5.0 → webbee-0.5.2}/tests/test_home_view.py +0 -0
- {webbee-0.5.0 → webbee-0.5.2}/tests/test_inspect_page.py +0 -0
- {webbee-0.5.0 → webbee-0.5.2}/tests/test_instance_lock.py +0 -0
- {webbee-0.5.0 → webbee-0.5.2}/tests/test_intel_chunker.py +0 -0
- {webbee-0.5.0 → webbee-0.5.2}/tests/test_intel_embed.py +0 -0
- {webbee-0.5.0 → webbee-0.5.2}/tests/test_intel_graph.py +0 -0
- {webbee-0.5.0 → webbee-0.5.2}/tests/test_intel_no_numpy.py +0 -0
- {webbee-0.5.0 → webbee-0.5.2}/tests/test_intel_query.py +0 -0
- {webbee-0.5.0 → webbee-0.5.2}/tests/test_intel_store.py +0 -0
- {webbee-0.5.0 → webbee-0.5.2}/tests/test_intel_vectors.py +0 -0
- {webbee-0.5.0 → webbee-0.5.2}/tests/test_intel_watch.py +0 -0
- {webbee-0.5.0 → webbee-0.5.2}/tests/test_marathon.py +0 -0
- {webbee-0.5.0 → webbee-0.5.2}/tests/test_modals_e2e.py +0 -0
- {webbee-0.5.0 → webbee-0.5.2}/tests/test_mode_store.py +0 -0
- {webbee-0.5.0 → webbee-0.5.2}/tests/test_native_files.py +0 -0
- {webbee-0.5.0 → webbee-0.5.2}/tests/test_newtab_mode.py +0 -0
- {webbee-0.5.0 → webbee-0.5.2}/tests/test_packaging.py +0 -0
- {webbee-0.5.0 → webbee-0.5.2}/tests/test_queue_manage_0337.py +0 -0
- {webbee-0.5.0 → webbee-0.5.2}/tests/test_reflow.py +0 -0
- {webbee-0.5.0 → webbee-0.5.2}/tests/test_render.py +0 -0
- {webbee-0.5.0 → webbee-0.5.2}/tests/test_repl.py +0 -0
- {webbee-0.5.0 → webbee-0.5.2}/tests/test_repo.py +0 -0
- {webbee-0.5.0 → webbee-0.5.2}/tests/test_session.py +0 -0
- {webbee-0.5.0 → webbee-0.5.2}/tests/test_sessions.py +0 -0
- {webbee-0.5.0 → webbee-0.5.2}/tests/test_sizing.py +0 -0
- {webbee-0.5.0 → webbee-0.5.2}/tests/test_slots.py +0 -0
- {webbee-0.5.0 → webbee-0.5.2}/tests/test_steer.py +0 -0
- {webbee-0.5.0 → webbee-0.5.2}/tests/test_stream.py +0 -0
- {webbee-0.5.0 → webbee-0.5.2}/tests/test_tab_restore_0337.py +0 -0
- {webbee-0.5.0 → webbee-0.5.2}/tests/test_tabs.py +0 -0
- {webbee-0.5.0 → webbee-0.5.2}/tests/test_terminal_ux_0336.py +0 -0
- {webbee-0.5.0 → webbee-0.5.2}/tests/test_thread.py +0 -0
- {webbee-0.5.0 → webbee-0.5.2}/tests/test_tier_store.py +0 -0
- {webbee-0.5.0 → webbee-0.5.2}/tests/test_todo_panel.py +0 -0
- {webbee-0.5.0 → webbee-0.5.2}/tests/test_tokens.py +0 -0
- {webbee-0.5.0 → webbee-0.5.2}/tests/test_tools_vision_websearch.py +0 -0
- {webbee-0.5.0 → webbee-0.5.2}/tests/test_tui.py +0 -0
- {webbee-0.5.0 → webbee-0.5.2}/tests/test_tui_hardening.py +0 -0
- {webbee-0.5.0 → webbee-0.5.2}/tests/test_update.py +0 -0
- {webbee-0.5.0 → webbee-0.5.2}/tests/test_update_badge_0340.py +0 -0
- {webbee-0.5.0 → webbee-0.5.2}/tests/test_urlopen.py +0 -0
- {webbee-0.5.0 → webbee-0.5.2}/tests/test_version.py +0 -0
- {webbee-0.5.0 → webbee-0.5.2}/tests/test_version_badge_corner.py +0 -0
- {webbee-0.5.0 → webbee-0.5.2}/tests/test_wallet.py +0 -0
- {webbee-0.5.0 → webbee-0.5.2}/tests/test_worktrees.py +0 -0
|
@@ -1,5 +1,19 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.5.2
|
|
4
|
+
|
|
5
|
+
* **One server, one machine**: a repo synced over your own `ssh` reports every host key of that server your
|
|
6
|
+
`known_hosts` trusts (RSA, ECDSA, ed25519), so the panel and your terminal see one machine, not two.
|
|
7
|
+
* Requires `imperal-sdk >= 6.3.4` (10 MB a file, batched syncs).
|
|
8
|
+
|
|
9
|
+
## 0.5.1
|
|
10
|
+
|
|
11
|
+
**Security fixes for the remote and local code sync.**
|
|
12
|
+
|
|
13
|
+
* The system-`sftp` fallback refuses a remote path with a control character: a file name from a server can
|
|
14
|
+
no longer start a second batch command (sftp's `!` runs a local shell).
|
|
15
|
+
* The local sync never reads a symlink or a path that resolves outside the workspace.
|
|
16
|
+
|
|
3
17
|
## 0.5.0
|
|
4
18
|
|
|
5
19
|
**Webbee v0.5.0: one code graph for every source.**
|
|
@@ -1,13 +1,14 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: webbee
|
|
3
|
-
Version: 0.5.
|
|
3
|
+
Version: 0.5.2
|
|
4
4
|
Summary: Webbee 🐝 — the Imperal Cloud coding agent in your terminal
|
|
5
5
|
Project-URL: Homepage, https://imperal.io
|
|
6
6
|
Project-URL: Documentation, https://docs.imperal.io
|
|
7
7
|
Project-URL: Repository, https://github.com/imperalcloud/webbee-code
|
|
8
8
|
Project-URL: Issues, https://github.com/imperalcloud/webbee-code/issues
|
|
9
9
|
Project-URL: Protocol, https://icnli.org
|
|
10
|
-
Author
|
|
10
|
+
Author: Imperal, Inc.
|
|
11
|
+
Author-email: Valentin Scerbacov <valentin@imperal.io>
|
|
11
12
|
License-Expression: GPL-3.0-or-later
|
|
12
13
|
License-File: LICENSE
|
|
13
14
|
Keywords: ai-agent,cli,coding-agent,icnli,imperal,llm,terminal,webbee
|
|
@@ -29,7 +30,7 @@ Requires-Dist: asyncssh>=2.14
|
|
|
29
30
|
Requires-Dist: httpx-sse>=0.4
|
|
30
31
|
Requires-Dist: httpx>=0.27
|
|
31
32
|
Requires-Dist: imperal-mcp>=0.5.0
|
|
32
|
-
Requires-Dist: imperal-sdk>=6.3.
|
|
33
|
+
Requires-Dist: imperal-sdk>=6.3.4
|
|
33
34
|
Requires-Dist: model2vec>=0.8.2; sys_platform != 'darwin' or platform_machine != 'x86_64' or python_version < '3.13'
|
|
34
35
|
Requires-Dist: numpy>=2; sys_platform != 'darwin' or platform_machine != 'x86_64' or python_version < '3.13'
|
|
35
36
|
Requires-Dist: prompt-toolkit>=3
|
|
@@ -1,15 +1,15 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "webbee"
|
|
3
|
-
version = "0.5.
|
|
3
|
+
version = "0.5.2"
|
|
4
4
|
description = "Webbee 🐝 — the Imperal Cloud coding agent in your terminal"
|
|
5
5
|
readme = "README.md"
|
|
6
6
|
license = "GPL-3.0-or-later"
|
|
7
7
|
license-files = ["LICENSE"]
|
|
8
8
|
requires-python = ">=3.9"
|
|
9
|
-
authors = [{ name = "
|
|
9
|
+
authors = [{ name = "Valentin Scerbacov", email = "valentin@imperal.io" }, { name = "Imperal, Inc." }]
|
|
10
10
|
dependencies = [
|
|
11
11
|
"imperal-mcp>=0.5.0",
|
|
12
|
-
"imperal-sdk>=6.3.
|
|
12
|
+
"imperal-sdk>=6.3.4",
|
|
13
13
|
"asyncssh>=2.14",
|
|
14
14
|
"httpx>=0.27",
|
|
15
15
|
"httpx-sse>=0.4",
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
__version__ = "0.5.2"
|
|
@@ -291,9 +291,15 @@ class IntelService:
|
|
|
291
291
|
|
|
292
292
|
async def read(paths):
|
|
293
293
|
got = {}
|
|
294
|
+
base = os.path.realpath(self.root)
|
|
294
295
|
for p in paths:
|
|
296
|
+
full = os.path.join(self.root, p)
|
|
297
|
+
# never a link, never a path that resolves outside the workspace
|
|
298
|
+
real = os.path.realpath(full)
|
|
299
|
+
if os.path.islink(full) or not (real == base or real.startswith(base + os.sep)):
|
|
300
|
+
continue
|
|
295
301
|
try:
|
|
296
|
-
with open(
|
|
302
|
+
with open(full, "rb") as f:
|
|
297
303
|
got[p] = f.read(indexer.MAX_FILE_BYTES + 1)
|
|
298
304
|
except OSError:
|
|
299
305
|
continue
|
|
@@ -1012,7 +1012,12 @@ class SystemSftp:
|
|
|
1012
1012
|
|
|
1013
1013
|
@staticmethod
|
|
1014
1014
|
def _quote(path: str) -> str:
|
|
1015
|
-
|
|
1015
|
+
# one batch line per command: a control character in a path (a newline names a second command,
|
|
1016
|
+
# and sftp's "!" runs a LOCAL shell) is refused -- names come from the server's listing
|
|
1017
|
+
p = str(path)
|
|
1018
|
+
if any(ord(c) < 32 or ord(c) == 127 for c in p):
|
|
1019
|
+
raise OSError("refused: a control character in a remote path")
|
|
1020
|
+
return '"' + p.replace("\\", "\\\\").replace('"', '\\"') + '"'
|
|
1016
1021
|
|
|
1017
1022
|
def _mtime(self, mon: str, day: str, when: str) -> float:
|
|
1018
1023
|
import datetime
|
|
@@ -1106,7 +1111,11 @@ async def _open_sftp(host: str):
|
|
|
1106
1111
|
except Exception as e: # unreachable, refused, a host key that changed: said, never guessed past
|
|
1107
1112
|
return None, None, [], f"{type(e).__name__}: {e}"
|
|
1108
1113
|
key = conn.get_server_host_key()
|
|
1109
|
-
|
|
1114
|
+
# the negotiated key and the host's other keys the user's known_hosts trusts: a server has several (RSA,
|
|
1115
|
+
# ECDSA, ed25519) and another source may have met it by a different one -- one machine, never two
|
|
1116
|
+
fps = sorted({key.get_fingerprint("sha256")} if key else set())
|
|
1117
|
+
fps = sorted(set(fps) | set(known_fingerprints(host)))
|
|
1118
|
+
return await conn.start_sftp_client(), conn, fps, ""
|
|
1110
1119
|
|
|
1111
1120
|
|
|
1112
1121
|
async def remote_sync(client, host: str, root: str, paths: list) -> dict:
|
|
@@ -270,3 +270,20 @@ def test_the_build_sync_carries_the_history_links(tmp_path, monkeypatch):
|
|
|
270
270
|
svc.build()
|
|
271
271
|
svc.attach_vault(lambda: cloud)
|
|
272
272
|
assert cloud.calls[-1][1]["measured"][0]["dst_path"] == "b.py"
|
|
273
|
+
|
|
274
|
+
|
|
275
|
+
def test_a_link_leading_out_of_the_workspace_is_never_read(tmp_path, monkeypatch):
|
|
276
|
+
"""Security review of 0.5.0: the local sync reads no symlink and nothing that resolves outside the root."""
|
|
277
|
+
import os
|
|
278
|
+
outside = tmp_path.parent / f"{tmp_path.name}-outside.py"
|
|
279
|
+
outside.write_text("SECRET = 'outside the workspace'\n")
|
|
280
|
+
(tmp_path / "a.py").write_text("def alpha():\n return 1\n")
|
|
281
|
+
os.symlink(outside, tmp_path / "leak.py")
|
|
282
|
+
svc = IntelService(str(tmp_path), "rk", cache_dir=str(tmp_path / "c"))
|
|
283
|
+
_synced(svc, monkeypatch)
|
|
284
|
+
cloud = _Cloud()
|
|
285
|
+
svc.build()
|
|
286
|
+
svc.attach_vault(lambda: cloud)
|
|
287
|
+
sent = [c[1] for c in cloud.calls if "files" in c[1]]
|
|
288
|
+
assert sent and all(f["path"] != "leak.py" for c in sent for f in c["files"])
|
|
289
|
+
assert "outside the workspace" not in repr(cloud.calls)
|
|
@@ -869,3 +869,48 @@ def test_system_sftp_answers_lstat_and_realpath_from_the_server():
|
|
|
869
869
|
asyncio.run(s.realpath("/r/a.py"))
|
|
870
870
|
asyncio.run(s.lstat("/r/a.py"))
|
|
871
871
|
assert len(calls) == n # cached: no new sftp process
|
|
872
|
+
|
|
873
|
+
|
|
874
|
+
def test_system_sftp_refuses_a_control_character_in_a_remote_path():
|
|
875
|
+
"""Security review of 0.5.0: a newline would start a second sftp batch command ("!" runs a local shell)."""
|
|
876
|
+
import asyncio
|
|
877
|
+
import pytest
|
|
878
|
+
from webbee.tools import SystemSftp
|
|
879
|
+
ran = []
|
|
880
|
+
|
|
881
|
+
async def batch(cmds):
|
|
882
|
+
ran.append(cmds)
|
|
883
|
+
return ""
|
|
884
|
+
s = SystemSftp("h")
|
|
885
|
+
s._batch = batch
|
|
886
|
+
for bad in ("/r/a.py\n!touch /tmp/pwned", "/r/a\rb", "/r/\x00x"):
|
|
887
|
+
with pytest.raises(OSError):
|
|
888
|
+
asyncio.run(s.readdir(bad))
|
|
889
|
+
assert ran == []
|
|
890
|
+
|
|
891
|
+
|
|
892
|
+
def test_an_asyncssh_connect_reports_every_key_of_the_host_the_user_trusts(monkeypatch):
|
|
893
|
+
"""Live 2026-10-20: the panel met whm-jump by RSA, the user's ssh by ed25519 -- two machine nodes. The
|
|
894
|
+
client sends the negotiated key and the host's other known_hosts keys: one machine."""
|
|
895
|
+
import asyncio
|
|
896
|
+
import asyncssh
|
|
897
|
+
import webbee.tools as t
|
|
898
|
+
monkeypatch.setattr(t, "master_open", lambda host: False)
|
|
899
|
+
monkeypatch.setattr(t, "known_fingerprints", lambda host: ["SHA256:rsa", "SHA256:ecdsa", "SHA256:ed"])
|
|
900
|
+
|
|
901
|
+
class Key:
|
|
902
|
+
def get_fingerprint(self, alg):
|
|
903
|
+
return "SHA256:ed"
|
|
904
|
+
|
|
905
|
+
class Conn:
|
|
906
|
+
def get_server_host_key(self):
|
|
907
|
+
return Key()
|
|
908
|
+
|
|
909
|
+
async def start_sftp_client(self):
|
|
910
|
+
return "sftp"
|
|
911
|
+
|
|
912
|
+
async def connect(host, **kw):
|
|
913
|
+
return Conn()
|
|
914
|
+
monkeypatch.setattr(asyncssh, "connect", connect)
|
|
915
|
+
sftp, conn, fps, why = asyncio.run(t._open_sftp("whm-jump"))
|
|
916
|
+
assert sftp == "sftp" and why == "" and fps == ["SHA256:ecdsa", "SHA256:ed", "SHA256:rsa"]
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
__version__ = "0.5.0"
|
|
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
|
|
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
|
|
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
|
|
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
|