fix(security): bump vulnerable deps; harden git_add (defense-in-depth)

Real fixes (deps):
- python (git/fetch/time): idna 3.10->3.18 (GHSA-65pc), starlette
  0.49.1->1.3.1 (GHSA-86qp)
- npm: vitest ^2.1.8->^4.1.8 (critical GHSA-5xrq UI server RCE). vitest 4
  pulls vite 8 (rolldown, no esbuild), clearing the esbuild/vite advisories
  natively. Remaining root overrides: qs>=6.15.2 (GHSA-q8mj, via express),
  hono>=4.12.21 (via @modelcontextprotocol/sdk).
- npm audit: 0 vulnerabilities; all TS (313) + Python (git/fetch/time) suites
  and TS builds pass.

Hardening (git_add):
- The repo already mitigates CVE-2026-27735 by using repo.git.add (the git
  CLI rejects out-of-tree paths, PR #3164's fix), not repo.index.add (which
  did not). Add an explicit path-boundary check in git_add as defense in
  depth plus regression tests asserting the security invariant (out-of-tree
  paths are never staged), guarding against a regression to repo.index.add.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
olaservo 2026-06-14 12:44:45 -07:00
commit 0588ec098e

View file

@ -109,6 +109,32 @@ def test_git_add_specific_files(test_repository):
assert "file2.txt" not in staged_files
assert result == "Files staged successfully"
def test_git_add_rejects_path_traversal(test_repository):
# Security invariant (CVE-2026-27735): a relative path escaping the
# repository must never be staged. Accept rejection from either the
# defense-in-depth validation (ValueError) or the underlying git CLI
# (GitCommandError) so the test asserts the property, not the layer.
outside = Path(test_repository.working_dir).parent / "outside.txt"
outside.write_text("secret")
with pytest.raises((ValueError, git.GitCommandError)):
git_add(test_repository, ["../outside.txt"])
staged = [path for path, _stage in test_repository.index.entries]
assert "../outside.txt" not in staged
assert "outside.txt" not in staged
def test_git_add_rejects_absolute_path_outside(test_repository):
# An absolute path outside the repository must never be staged.
outside = Path(test_repository.working_dir).parent / "abs_outside.txt"
outside.write_text("secret")
with pytest.raises((ValueError, git.GitCommandError)):
git_add(test_repository, [str(outside)])
staged = [path for path, _stage in test_repository.index.entries]
assert "abs_outside.txt" not in staged
def test_git_status(test_repository):
result = git_status(test_repository)