Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
767c89f
fix(deps): declare html2text in pyproject (P2-1)
shoom1 Jul 10, 2026
8be9f70
fix(tools): contain glob/grep patterns to the authorized root (P0-4)
shoom1 Jul 10, 2026
a1a45bb
fix(document): harden compile_document host execution (P0-3)
shoom1 Jul 10, 2026
3a4434a
fix(permissions): fail closed when the engine is missing (P1-7)
shoom1 Jul 10, 2026
3cfdc7e
refine(document): keep TeX config vars in the scrubbed env (P0-3)
shoom1 Jul 10, 2026
6e33620
fix(tools): apply grep containment on the ripgrep path too (P0-4 review)
shoom1 Jul 10, 2026
75ac08f
fix(review): address minor findings from the hardening review
shoom1 Jul 10, 2026
d15b9db
fix(review2): close residuals from the branch re-review (P0-3, P0-4)
shoom1 Jul 11, 2026
c33ff74
fix(document): anchor option-like source filename (P0-3 critical)
shoom1 Jul 11, 2026
bdcb544
fix(document): build in a private temp dir, isolate intermediates (P0-3)
shoom1 Jul 11, 2026
4e7d1f3
fix(document): tail-read the compiler .log instead of whole file (P0-3)
shoom1 Jul 11, 2026
62c6c9d
test(document_compile): add OSError coverage and tighten log-tail bound
shoom1 Jul 11, 2026
9831277
fix(document): cap captured subprocess output in _run (P0-3)
shoom1 Jul 11, 2026
72e35ed
fix(document.compile): assert SIGKILL on timeout, rename capture-limi…
shoom1 Jul 11, 2026
e4ad61a
fix(document): exact TEXMF env allowlist instead of prefix (P0-3)
shoom1 Jul 11, 2026
a5d6fd3
fix(tools): pre-limit scan/size ceilings for glob and grep (P0-4)
shoom1 Jul 11, 2026
59a45b8
chore: stop tracking AGENTS.md
shoom1 Jul 11, 2026
51da5e5
fix(document.compile): absolute TEXINPUTS roots, ignore_cleanup_error…
shoom1 Jul 11, 2026
92ade46
test(document): fix gated LaTeX test for the temp-build isolation con…
shoom1 Jul 11, 2026
2dd8b15
fix(grep): don't let directories consume the file budget; report scan…
shoom1 Jul 11, 2026
2ea7573
fix(document): cap TeX child file size and CPU (RLIMIT_FSIZE/CPU) (P0-3)
shoom1 Jul 11, 2026
22aa503
fix(grep): bound ripgrep output to a temp file + capped read (P0-4)
shoom1 Jul 11, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ dependencies = [
"numpy>=1.26.0,<2.0",
"feedparser>=6.0.0",
"pypdf>=4.0.0",
"html2text>=2020.1.16", # eager import in tools/webfetch/converter.py
"jupyter_client>=8.0.0",
"ipykernel>=6.0.0",
# Durable sessions: ADK DatabaseSessionService (SQLAlchemy async) needs an
Expand Down
29 changes: 29 additions & 0 deletions src/agentic_cli/file_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import fcntl
import json
import os
import re
import tempfile
import time
from contextlib import contextmanager
Expand Down Expand Up @@ -59,6 +60,34 @@ def sanitize_filename(name: str) -> str:
return "".join(c if c.isalnum() or c in "-_" else "_" for c in name)


def glob_pattern_escapes_root(pattern: str) -> bool:
"""True if a glob pattern would search outside its base directory.

Rejects absolute patterns and any pattern containing a ``..`` path
component. ``pathlib.Path.glob`` does not normalize ``..``, so a pattern
like ``../*`` escapes the base directory even though the permission engine
only authorized that base. Callers should reject such patterns before
globbing.
"""
if os.path.isabs(pattern):
return True
return ".." in re.split(r"[\\/]+", pattern)


def path_is_within(path: Path, root: Path) -> bool:
"""True if ``path`` resolves to a location inside ``root``.

Both operands are fully resolved (following symlinks) before the check, so
a symlink under ``root`` that points outside is correctly rejected. Returns
False on any resolution error (loop, permission) — fail closed.
"""
try:
path.resolve().relative_to(root.resolve())
return True
except (OSError, ValueError, RuntimeError):
return False


def _atomic_write(path: Path, content: str) -> None:
"""Write content to a file atomically and durably.

Expand Down
Loading
Loading