repak-sonar 0.1__py3-none-any.whl
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.
repak_sonar/__init__.py
ADDED
|
File without changes
|
repak_sonar/_unpak.py
ADDED
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
"""Self-contained extractor embedded into every repak-generated wheel.
|
|
2
|
+
|
|
3
|
+
This module ships *inside* the synthetic wheel as ``<pkg>/_unpak.py`` and is
|
|
4
|
+
wired to the ``unpak-{name}`` console script. It must depend only on the
|
|
5
|
+
Python standard library: repak is not installed on the destination side.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from __future__ import annotations
|
|
9
|
+
|
|
10
|
+
import argparse
|
|
11
|
+
import hashlib
|
|
12
|
+
import io
|
|
13
|
+
import os
|
|
14
|
+
import sys
|
|
15
|
+
import tarfile
|
|
16
|
+
from importlib import resources
|
|
17
|
+
from pathlib import Path
|
|
18
|
+
|
|
19
|
+
PAYLOAD = "payload.tar.gz"
|
|
20
|
+
CHECKSUM = "payload.sha256"
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
def _read_resource(name: str) -> bytes:
|
|
24
|
+
return resources.files(__package__).joinpath(name).read_bytes()
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
def _safe_members(tar: tarfile.TarFile, dest: Path):
|
|
28
|
+
"""Yield only members that extract safely under ``dest``.
|
|
29
|
+
|
|
30
|
+
Rejects absolute paths and ``..`` traversal; restricts to regular files
|
|
31
|
+
and directories (mirrors the intent of tarfile's ``data`` filter while
|
|
32
|
+
remaining compatible with Python 3.9).
|
|
33
|
+
"""
|
|
34
|
+
dest = dest.resolve()
|
|
35
|
+
for member in tar.getmembers():
|
|
36
|
+
name = member.name
|
|
37
|
+
if name.startswith("/") or os.path.isabs(name):
|
|
38
|
+
raise ValueError(f"unsafe absolute path in archive: {name!r}")
|
|
39
|
+
target = (dest / name).resolve()
|
|
40
|
+
if target != dest and dest not in target.parents:
|
|
41
|
+
raise ValueError(f"unsafe path traversal in archive: {name!r}")
|
|
42
|
+
if member.isdir() or member.isreg():
|
|
43
|
+
yield member
|
|
44
|
+
else:
|
|
45
|
+
raise ValueError(
|
|
46
|
+
f"archive contains unsupported entry {name!r} "
|
|
47
|
+
f"(type {member.type!r})"
|
|
48
|
+
)
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
def _extract(data: bytes, dest: Path) -> None:
|
|
52
|
+
dest.mkdir(parents=True, exist_ok=True)
|
|
53
|
+
with tarfile.open(fileobj=io.BytesIO(data), mode="r:gz") as tar:
|
|
54
|
+
members = list(_safe_members(tar, dest))
|
|
55
|
+
for member in members:
|
|
56
|
+
out = dest / member.name
|
|
57
|
+
if member.isdir():
|
|
58
|
+
out.mkdir(parents=True, exist_ok=True)
|
|
59
|
+
continue
|
|
60
|
+
out.parent.mkdir(parents=True, exist_ok=True)
|
|
61
|
+
src = tar.extractfile(member)
|
|
62
|
+
if src is None:
|
|
63
|
+
continue
|
|
64
|
+
with open(out, "wb") as fh:
|
|
65
|
+
fh.write(src.read())
|
|
66
|
+
os.chmod(out, member.mode & 0o777)
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
def main(argv=None) -> int:
|
|
70
|
+
parser = argparse.ArgumentParser(
|
|
71
|
+
prog=f"unpak ({__package__})",
|
|
72
|
+
description="Verify and extract a repak-transported directory.",
|
|
73
|
+
)
|
|
74
|
+
parser.add_argument(
|
|
75
|
+
"target",
|
|
76
|
+
help="Destination folder; created if missing. Existing unrelated "
|
|
77
|
+
"files are left untouched (overwrite/merge).",
|
|
78
|
+
)
|
|
79
|
+
args = parser.parse_args(argv)
|
|
80
|
+
|
|
81
|
+
data = _read_resource(PAYLOAD)
|
|
82
|
+
expected = _read_resource(CHECKSUM).decode("ascii").strip()
|
|
83
|
+
actual = hashlib.sha256(data).hexdigest()
|
|
84
|
+
if actual != expected:
|
|
85
|
+
sys.stderr.write(
|
|
86
|
+
"ERROR: checksum verification failed; the payload is corrupt "
|
|
87
|
+
"and nothing was written.\n"
|
|
88
|
+
f" expected: {expected}\n"
|
|
89
|
+
f" actual: {actual}\n"
|
|
90
|
+
)
|
|
91
|
+
return 1
|
|
92
|
+
|
|
93
|
+
dest = Path(args.target)
|
|
94
|
+
_extract(data, dest)
|
|
95
|
+
sys.stdout.write(
|
|
96
|
+
f"Verified SHA-256 and extracted contents to {dest.resolve()}\n"
|
|
97
|
+
)
|
|
98
|
+
return 0
|
|
99
|
+
|
|
100
|
+
|
|
101
|
+
if __name__ == "__main__":
|
|
102
|
+
raise SystemExit(main())
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
5a196bcb3f8f52ae92d7c0e5592e491a499ff57b779cd4e247646f973041d043
|
|
Binary file
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
repak_sonar/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
2
|
+
repak_sonar/_unpak.py,sha256=AkBMA5uMXtsdiuTGur3mSzvUWX0qcNLCFNgS3Q004Bo,3299
|
|
3
|
+
repak_sonar/payload.tar.gz,sha256=Whlryz-PUq6S18DlWS5JGkmf9Xt3nNTiR2RvlzBB0EM,43345157
|
|
4
|
+
repak_sonar/payload.sha256,sha256=bpF0ZWc14huss3wIUNVpCQ-zTzXjRJn8xQOjVmwdyvo,65
|
|
5
|
+
repak_sonar-0.1.dist-info/METADATA,sha256=M1Ae2Wyvp_Bi4mBmwDcm56Z1i8RQVheAUj9QEROV96c,138
|
|
6
|
+
repak_sonar-0.1.dist-info/WHEEL,sha256=op7MFSc46_y5sL4zAFN7MdFDa9k3cTXf3KNjxLf63JQ,84
|
|
7
|
+
repak_sonar-0.1.dist-info/entry_points.txt,sha256=v-fA8YOz9z2eQRSKBzHEcBNk7uBnDA8tQirU37wcWhw,56
|
|
8
|
+
repak_sonar-0.1.dist-info/RECORD,,
|