hermes-agent/tests/cron/test_file_permissions.py
Teknium 6b81590c55
test: prune low-value tests suite-wide (wave 1) — 46,820 → 28,106 test functions
Systematic prune per AGENTS.md test policy, one pass over every major
test tree (gateway, hermes_cli, tools, agent, run_agent, plugins, cli,
cron, tui_gateway, honcho/openviking, root-level):

- DELETE: source-reading tests (read_text/getsource on prod files),
  change-detector tests (exact catalog counts, model-name snapshots,
  config version literals), mock-echo tests (assert a mock returns what
  it was told), assertion-free/trivial tests, near-duplicate
  parametrizations (boundaries + one representative kept), async/sync
  twin duplicates, cosmetic within-file variations.
- KEEP (mandatory): security/redaction/approval guards, message-role
  alternation invariants, prompt-caching/deterministic-call-id
  invariants, issue-number regression tests (deduped), E2E tests.
- 6 test files deleted outright (script-style/no-assert or fully
  redundant); conftest.py, fakes/, fixtures/ untouched.
- tests/acp/conftest.py added: autouse fixture stubs the live
  models.dev/GitHub/Copilot/Anthropic inventory fetches that ACP server
  tests performed on every session create — test_server.py 147s → 3.4s,
  and the tests are now genuinely hermetic.
- Sleep-based slowness shrunk where safe (codex_ttfb_watchdog,
  compression_concurrent_fork, etc.); no wall-clock assertion tightened.

Verification: full hermetic suite via scripts/run_tests.sh —
2439 files, 31,130 tests passed, 0 failed, 0 flaky retries, 315s wall
(baseline: 583s wall, 13,564s subprocess CPU).
2026-07-29 13:10:23 -07:00

130 lines
4.8 KiB
Python

"""Tests for file permissions hardening on sensitive files."""
import os
import stat
import tempfile
import unittest
from pathlib import Path
from unittest.mock import patch
class TestCronFilePermissions(unittest.TestCase):
"""Verify cron files get secure permissions."""
def setUp(self):
self.tmpdir = tempfile.mkdtemp()
self.cron_dir = Path(self.tmpdir) / "cron"
self.output_dir = self.cron_dir / "output"
def tearDown(self):
import shutil
shutil.rmtree(self.tmpdir, ignore_errors=True)
@patch("cron.jobs.CRON_DIR")
@patch("cron.jobs.OUTPUT_DIR")
@patch("cron.jobs.JOBS_FILE")
def test_ensure_dirs_sets_0700(self, mock_jobs_file, mock_output, mock_cron):
mock_cron.__class__ = Path
# Use real paths
cron_dir = Path(self.tmpdir) / "cron"
output_dir = cron_dir / "output"
with patch("cron.jobs.CRON_DIR", cron_dir), \
patch("cron.jobs.OUTPUT_DIR", output_dir):
from cron.jobs import ensure_dirs
ensure_dirs()
cron_mode = stat.S_IMODE(os.stat(cron_dir).st_mode)
output_mode = stat.S_IMODE(os.stat(output_dir).st_mode)
self.assertEqual(cron_mode, 0o700)
self.assertEqual(output_mode, 0o700)
@patch("cron.jobs.CRON_DIR")
@patch("cron.jobs.OUTPUT_DIR")
@patch("cron.jobs.JOBS_FILE")
def test_save_jobs_sets_0600(self, mock_jobs_file, mock_output, mock_cron):
cron_dir = Path(self.tmpdir) / "cron"
output_dir = cron_dir / "output"
jobs_file = cron_dir / "jobs.json"
with patch("cron.jobs.CRON_DIR", cron_dir), \
patch("cron.jobs.OUTPUT_DIR", output_dir), \
patch("cron.jobs.JOBS_FILE", jobs_file):
from cron.jobs import save_jobs
save_jobs([{"id": "test", "prompt": "hello"}])
file_mode = stat.S_IMODE(os.stat(jobs_file).st_mode)
self.assertEqual(file_mode, 0o600)
def test_save_job_output_sets_0600(self):
output_dir = Path(self.tmpdir) / "output"
with patch("cron.jobs.OUTPUT_DIR", output_dir), \
patch("cron.jobs.CRON_DIR", Path(self.tmpdir)), \
patch("cron.jobs.ensure_dirs"):
output_dir.mkdir(parents=True, exist_ok=True)
from cron.jobs import save_job_output
output_file = save_job_output("test-job", "test output content")
file_mode = stat.S_IMODE(os.stat(output_file).st_mode)
self.assertEqual(file_mode, 0o600)
# Job output dir should also be 0700
job_dir = output_dir / "test-job"
dir_mode = stat.S_IMODE(os.stat(job_dir).st_mode)
self.assertEqual(dir_mode, 0o700)
class TestConfigFilePermissions(unittest.TestCase):
"""Verify config files get secure permissions."""
def setUp(self):
self.tmpdir = tempfile.mkdtemp()
def tearDown(self):
import shutil
shutil.rmtree(self.tmpdir, ignore_errors=True)
def test_save_config_sets_0600(self):
config_path = Path(self.tmpdir) / "config.yaml"
with patch("hermes_cli.config.get_config_path", return_value=config_path), \
patch("hermes_cli.config.ensure_hermes_home"):
from hermes_cli.config import save_config
save_config({"model": "test/model"})
file_mode = stat.S_IMODE(os.stat(config_path).st_mode)
self.assertEqual(file_mode, 0o600)
def test_save_env_value_sets_0600(self):
env_path = Path(self.tmpdir) / ".env"
with patch("hermes_cli.config.get_env_path", return_value=env_path), \
patch("hermes_cli.config.ensure_hermes_home"):
from hermes_cli.config import save_env_value
save_env_value("TEST_KEY", "test_value")
file_mode = stat.S_IMODE(os.stat(env_path).st_mode)
self.assertEqual(file_mode, 0o600)
def test_ensure_hermes_home_sets_0700(self):
home = Path(self.tmpdir) / ".hermes"
with patch("hermes_cli.config.get_hermes_home", return_value=home):
from hermes_cli.config import ensure_hermes_home
ensure_hermes_home()
home_mode = stat.S_IMODE(os.stat(home).st_mode)
self.assertEqual(home_mode, 0o700)
for subdir in ("cron", "sessions", "logs", "memories"):
subdir_mode = stat.S_IMODE(os.stat(home / subdir).st_mode)
self.assertEqual(subdir_mode, 0o700, f"{subdir} should be 0700")
class TestSecureHelpers(unittest.TestCase):
"""Test the _secure_file and _secure_dir helpers."""
def test_secure_file_nonexistent_no_error(self):
from cron.jobs import _secure_file
_secure_file(Path("/nonexistent/path/file.json")) # Should not raise
if __name__ == "__main__":
unittest.main()