mirror of
https://github.com/NousResearch/hermes-agent.git
synced 2026-06-06 07:51:53 +00:00
fix(android): reject unsafe tar members in psutil compatibility installer
This commit is contained in:
parent
bb0ac5ced2
commit
aa3466063b
4 changed files with 252 additions and 52 deletions
108
hermes_cli/psutil_android.py
Normal file
108
hermes_cli/psutil_android.py
Normal file
|
|
@ -0,0 +1,108 @@
|
|||
"""Helpers for the temporary psutil-on-Android compatibility installer."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import shutil
|
||||
import tarfile
|
||||
from pathlib import Path, PurePosixPath
|
||||
|
||||
# Pin a version we know patches cleanly. Update when a newer psutil
|
||||
# changes the marker line shape and we need to follow upstream.
|
||||
PSUTIL_URL = (
|
||||
"https://files.pythonhosted.org/packages/aa/c6/"
|
||||
"d1ddf4abb55e93cebc4f2ed8b5d6dbad109ecb8d63748dd2b20ab5e57ebe/"
|
||||
"psutil-7.2.2.tar.gz"
|
||||
)
|
||||
|
||||
MARKER = 'LINUX = sys.platform.startswith("linux")'
|
||||
REPLACEMENT = 'LINUX = sys.platform.startswith(("linux", "android"))'
|
||||
|
||||
|
||||
class PsutilAndroidInstallError(RuntimeError):
|
||||
"""Raised when the pinned psutil sdist is missing or unsafe."""
|
||||
|
||||
|
||||
def _normalize_member_parts(member_name: str) -> tuple[str, ...]:
|
||||
path = PurePosixPath(member_name)
|
||||
parts = tuple(part for part in path.parts if part not in ("", "."))
|
||||
if path.is_absolute() or ".." in parts or not parts:
|
||||
raise PsutilAndroidInstallError(
|
||||
f"Unsafe archive member path: {member_name!r}"
|
||||
)
|
||||
return parts
|
||||
|
||||
|
||||
def _safe_extract_tar_gz(archive: Path, destination: Path) -> None:
|
||||
"""Extract a tar.gz without allowing traversal or link members."""
|
||||
with tarfile.open(archive, "r:gz") as tf:
|
||||
for member in tf.getmembers():
|
||||
parts = _normalize_member_parts(member.name)
|
||||
target = destination.joinpath(*parts)
|
||||
|
||||
if member.isdir():
|
||||
target.mkdir(parents=True, exist_ok=True)
|
||||
continue
|
||||
|
||||
if not member.isfile():
|
||||
raise PsutilAndroidInstallError(
|
||||
f"Unsupported archive member type: {member.name}"
|
||||
)
|
||||
|
||||
target.parent.mkdir(parents=True, exist_ok=True)
|
||||
extracted = tf.extractfile(member)
|
||||
if extracted is None:
|
||||
raise PsutilAndroidInstallError(
|
||||
f"Cannot read archive member: {member.name}"
|
||||
)
|
||||
|
||||
with extracted, open(target, "wb") as dst:
|
||||
shutil.copyfileobj(extracted, dst)
|
||||
|
||||
try:
|
||||
target.chmod(member.mode & 0o777)
|
||||
except OSError:
|
||||
pass
|
||||
|
||||
|
||||
def prepare_patched_psutil_sdist(archive: Path, destination: Path) -> Path:
|
||||
"""Safely extract the pinned psutil sdist and patch it for Android."""
|
||||
_safe_extract_tar_gz(archive, destination)
|
||||
|
||||
src_roots = sorted(
|
||||
(
|
||||
path for path in destination.iterdir()
|
||||
if path.is_dir() and path.name.startswith("psutil-")
|
||||
),
|
||||
key=lambda path: path.name,
|
||||
)
|
||||
if not src_roots:
|
||||
raise PsutilAndroidInstallError(
|
||||
"psutil sdist did not contain a psutil-* directory"
|
||||
)
|
||||
|
||||
src_root = src_roots[0]
|
||||
common_py = src_root / "psutil" / "_common.py"
|
||||
if not common_py.is_file():
|
||||
raise PsutilAndroidInstallError(
|
||||
f"psutil sdist did not contain {common_py.relative_to(src_root)!s}"
|
||||
)
|
||||
try:
|
||||
content = common_py.read_text(encoding="utf-8")
|
||||
except OSError as exc:
|
||||
raise PsutilAndroidInstallError(
|
||||
f"Failed to read {common_py.relative_to(src_root)!s}"
|
||||
) from exc
|
||||
if MARKER not in content:
|
||||
raise PsutilAndroidInstallError(
|
||||
"psutil Android compatibility patch marker not found"
|
||||
)
|
||||
try:
|
||||
common_py.write_text(
|
||||
content.replace(MARKER, REPLACEMENT),
|
||||
encoding="utf-8",
|
||||
)
|
||||
except OSError as exc:
|
||||
raise PsutilAndroidInstallError(
|
||||
f"Failed to write {common_py.relative_to(src_root)!s}"
|
||||
) from exc
|
||||
return src_root
|
||||
Loading…
Add table
Add a link
Reference in a new issue