Import AITURK IDE 1.0.0-beta.1 from Hermes 63279301; preserve MIT license
This commit is contained in:
@@ -0,0 +1,118 @@
|
||||
"""Symlink-safe creation helpers for spill/cache files.
|
||||
|
||||
Spill files (oversized terminal output, hook context, subagent summaries,
|
||||
web_extract full text) are written into predictable, world-discoverable
|
||||
directories under ``~/.hermes``. A plain ``open(path, "w")`` /
|
||||
``Path.write_text`` there follows a pre-planted symlink, letting any local
|
||||
process that can write to the spill directory redirect our write onto an
|
||||
arbitrary file owned by the user (``~/.bashrc``, ``authorized_keys``, ...).
|
||||
|
||||
Every helper here refuses symlinks by construction:
|
||||
|
||||
* New files are created with ``O_CREAT | O_EXCL``, which fails on ANY
|
||||
existing path — including a dangling symlink — instead of following it.
|
||||
* Overwrites first remove the existing path via ``lstat`` + ``unlink``
|
||||
(deleting a link deletes the link, never its target), then re-create
|
||||
exclusively. The check-then-create pair cannot be raced into following a
|
||||
link because creation itself is exclusive.
|
||||
|
||||
Two privacy tiers:
|
||||
|
||||
* ``private=True`` (default) also forces ``0o700`` directories and
|
||||
``0o600`` files — for spills that may hold raw, pre-redaction secrets
|
||||
(terminal output, hook context).
|
||||
* ``private=False`` keeps umask-default permissions — for cache dirs that
|
||||
are bind-mounted into remote terminal backends (Docker/Modal/SSH via
|
||||
``credential_files._CACHE_DIRS``), where a non-root container UID must
|
||||
still be able to read them (public web content, delegation summaries).
|
||||
|
||||
Disk failures are the caller's concern: helpers raise ``OSError`` and the
|
||||
call sites keep their existing best-effort handling.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import os
|
||||
import stat
|
||||
from pathlib import Path
|
||||
from typing import IO
|
||||
|
||||
__all__ = [
|
||||
"ensure_spill_dir",
|
||||
"open_exclusive",
|
||||
"write_text_exclusive",
|
||||
]
|
||||
|
||||
# O_NOFOLLOW is POSIX-only; harmless to omit on Windows since O_EXCL alone
|
||||
# already refuses every pre-existing path there too.
|
||||
_O_NOFOLLOW = getattr(os, "O_NOFOLLOW", 0)
|
||||
|
||||
|
||||
def ensure_spill_dir(path: Path, *, private: bool = True) -> Path:
|
||||
"""Create ``path`` (and parents) as a directory, refusing symlinks.
|
||||
|
||||
With ``private=True`` the leaf directory is created ``0o700`` and an
|
||||
already-existing leaf is tightened to ``0o700``. Raises ``OSError`` if
|
||||
the leaf exists and is not a real directory (e.g. a planted symlink).
|
||||
"""
|
||||
path = Path(path)
|
||||
if private:
|
||||
path.mkdir(mode=0o700, parents=True, exist_ok=True)
|
||||
else:
|
||||
path.mkdir(parents=True, exist_ok=True)
|
||||
st = os.lstat(path)
|
||||
if not stat.S_ISDIR(st.st_mode):
|
||||
raise OSError(f"spill dir is not a directory (symlink?): {path}")
|
||||
if private and stat.S_IMODE(st.st_mode) != 0o700:
|
||||
os.chmod(path, 0o700)
|
||||
return path
|
||||
|
||||
|
||||
def open_exclusive(
|
||||
path: Path,
|
||||
*,
|
||||
private: bool = True,
|
||||
overwrite: bool = False,
|
||||
encoding: str = "utf-8",
|
||||
errors: str = "strict",
|
||||
) -> IO[str]:
|
||||
"""Open ``path`` for writing via exclusive create; never follows a link.
|
||||
|
||||
``overwrite=True`` first unlinks an existing path (``lstat``-checked so
|
||||
only the link itself is ever removed, and real directories are refused),
|
||||
then creates exclusively — so even the overwrite path cannot be
|
||||
redirected through a symlink.
|
||||
"""
|
||||
path = Path(path)
|
||||
if overwrite:
|
||||
try:
|
||||
st = os.lstat(path)
|
||||
except FileNotFoundError:
|
||||
pass
|
||||
else:
|
||||
if stat.S_ISDIR(st.st_mode):
|
||||
raise OSError(f"refusing to overwrite a directory: {path}")
|
||||
os.unlink(path)
|
||||
mode = 0o600 if private else 0o666 # non-private honors umask
|
||||
fd = os.open(path, os.O_WRONLY | os.O_CREAT | os.O_EXCL | _O_NOFOLLOW, mode)
|
||||
try:
|
||||
return os.fdopen(fd, "w", encoding=encoding, errors=errors)
|
||||
except Exception:
|
||||
os.close(fd)
|
||||
raise
|
||||
|
||||
|
||||
def write_text_exclusive(
|
||||
path: Path,
|
||||
text: str,
|
||||
*,
|
||||
private: bool = True,
|
||||
overwrite: bool = False,
|
||||
encoding: str = "utf-8",
|
||||
errors: str = "strict",
|
||||
) -> None:
|
||||
"""``Path.write_text`` equivalent that refuses to follow symlinks."""
|
||||
with open_exclusive(
|
||||
path, private=private, overwrite=overwrite, encoding=encoding, errors=errors
|
||||
) as fh:
|
||||
fh.write(text)
|
||||
Reference in New Issue
Block a user