hbkit 0.4.7__tar.gz → 0.4.8__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.
- {hbkit-0.4.7 → hbkit-0.4.8}/PKG-INFO +1 -1
- {hbkit-0.4.7 → hbkit-0.4.8}/pyproject.toml +1 -1
- {hbkit-0.4.7 → hbkit-0.4.8}/src/hbkit/__init__.py +1 -1
- {hbkit-0.4.7 → hbkit-0.4.8}/src/hbkit/cli.py +18 -1
- {hbkit-0.4.7 → hbkit-0.4.8}/tests/test_hbkit.py +20 -0
- {hbkit-0.4.7 → hbkit-0.4.8}/.gitignore +0 -0
- {hbkit-0.4.7 → hbkit-0.4.8}/FORMAT.md +0 -0
- {hbkit-0.4.7 → hbkit-0.4.8}/LICENSE +0 -0
- {hbkit-0.4.7 → hbkit-0.4.8}/README.md +0 -0
- {hbkit-0.4.7 → hbkit-0.4.8}/contrib/art/README.md +0 -0
- {hbkit-0.4.7 → hbkit-0.4.8}/packaging/README.md +0 -0
- {hbkit-0.4.7 → hbkit-0.4.8}/src/hbkit/archive.py +0 -0
- {hbkit-0.4.7 → hbkit-0.4.8}/src/hbkit/crypto.py +0 -0
- {hbkit-0.4.7 → hbkit-0.4.8}/src/hbkit/doctor.py +0 -0
- {hbkit-0.4.7 → hbkit-0.4.8}/src/hbkit/index.py +0 -0
- {hbkit-0.4.7 → hbkit-0.4.8}/src/hbkit/manifest.py +0 -0
- {hbkit-0.4.7 → hbkit-0.4.8}/src/hbkit/mount.py +0 -0
- {hbkit-0.4.7 → hbkit-0.4.8}/src/hbkit/runner.py +0 -0
- {hbkit-0.4.7 → hbkit-0.4.8}/src/hbkit/tui.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: hbkit
|
|
3
|
-
Version: 0.4.
|
|
3
|
+
Version: 0.4.8
|
|
4
4
|
Summary: Recover files from Synology Hyper Backup (.hbk) archives without Synology software
|
|
5
5
|
Project-URL: Homepage, https://github.com/YordiLorenzo/hbkit
|
|
6
6
|
Project-URL: Source, https://github.com/YordiLorenzo/hbkit
|
|
@@ -92,6 +92,23 @@ def resolve_password(password):
|
|
|
92
92
|
return password or os.environ.get("HBK_PASSWORD")
|
|
93
93
|
|
|
94
94
|
|
|
95
|
+
def stdin_is_interactive():
|
|
96
|
+
"""True when a password prompt can be answered: stdin is a real terminal/console."""
|
|
97
|
+
if not sys.stdin or not sys.stdin.isatty():
|
|
98
|
+
return False
|
|
99
|
+
if os.name != "nt":
|
|
100
|
+
return True
|
|
101
|
+
# `< NUL` is a character device, so isatty() is True; only a real console has a mode
|
|
102
|
+
import ctypes
|
|
103
|
+
import msvcrt
|
|
104
|
+
try:
|
|
105
|
+
h = msvcrt.get_osfhandle(sys.stdin.fileno())
|
|
106
|
+
except OSError:
|
|
107
|
+
return False
|
|
108
|
+
mode = ctypes.c_uint32()
|
|
109
|
+
return bool(ctypes.windll.kernel32.GetConsoleMode(ctypes.c_void_p(h), ctypes.byref(mode)))
|
|
110
|
+
|
|
111
|
+
|
|
95
112
|
def open_archive(archive, password=None):
|
|
96
113
|
"""Open, prompting for a password only if the archive turns out to be encrypted."""
|
|
97
114
|
try:
|
|
@@ -99,7 +116,7 @@ def open_archive(archive, password=None):
|
|
|
99
116
|
except hbk.NeedPassword:
|
|
100
117
|
pw = resolve_password(password)
|
|
101
118
|
if not pw:
|
|
102
|
-
if not
|
|
119
|
+
if not stdin_is_interactive():
|
|
103
120
|
sys.exit("archive is encrypted - pass --password or set HBK_PASSWORD")
|
|
104
121
|
pw = getpass.getpass("archive password: ")
|
|
105
122
|
try:
|
|
@@ -303,6 +303,26 @@ def test_verify_rejects_options_it_does_not_take():
|
|
|
303
303
|
assert "unknown option --version" in r.stderr
|
|
304
304
|
|
|
305
305
|
|
|
306
|
+
def test_encrypted_archive_with_null_stdin_fails_instead_of_prompting(tmp_path):
|
|
307
|
+
"""On Windows `< NUL` is a character device, so isatty() says True and getpass used
|
|
308
|
+
to wait on the console forever. Only enough of an archive to reach NeedPassword."""
|
|
309
|
+
import struct
|
|
310
|
+
header = struct.pack(">16I", hbk.MAGIC, 0, 0, 0, 56, *([0] * 11))
|
|
311
|
+
root = tmp_path / "enc.hbk"
|
|
312
|
+
for d in ("Config/virtual_file.index", "Pool/chunk_index",
|
|
313
|
+
"Config/file_chunk1.index", "Pool/0"):
|
|
314
|
+
(root / d).mkdir(parents=True)
|
|
315
|
+
(root / "Config/virtual_file.index/0.idx").write_bytes(header)
|
|
316
|
+
(root / "Pool/chunk_index/0.idx").write_bytes(header)
|
|
317
|
+
(root / "_Syno_TaskConfig").write_text('enable_data_encrypt="true"\n')
|
|
318
|
+
env = {k: v for k, v in os.environ.items() if k != "HBK_PASSWORD"}
|
|
319
|
+
r = subprocess.run([sys.executable, "-m", "hbkit.cli", str(root), "list"],
|
|
320
|
+
stdin=subprocess.DEVNULL, capture_output=True, text=True,
|
|
321
|
+
env=env, timeout=30, check=False)
|
|
322
|
+
assert r.returncode != 0, r.stderr
|
|
323
|
+
assert "pass --password or set HBK_PASSWORD" in r.stderr
|
|
324
|
+
|
|
325
|
+
|
|
306
326
|
# ------------------------------------------------- shard maths (no archive needed)
|
|
307
327
|
|
|
308
328
|
def _make_shards(tmp_path, sizes, shard_size, monkeypatch):
|
|
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
|