88 lines
3.7 KiB
Python
88 lines
3.7 KiB
Python
|
|
#!/usr/bin/env python3
|
||
|
|
"""Regresión del aislamiento de las sesiones del lanzador, sin Docker ni credenciales."""
|
||
|
|
|
||
|
|
import json
|
||
|
|
import os
|
||
|
|
from pathlib import Path
|
||
|
|
import subprocess
|
||
|
|
import tempfile
|
||
|
|
import tomllib
|
||
|
|
import unittest
|
||
|
|
|
||
|
|
ROOT = Path(__file__).resolve().parent.parent
|
||
|
|
|
||
|
|
|
||
|
|
class SessionLauncherTests(unittest.TestCase):
|
||
|
|
def setUp(self):
|
||
|
|
artifacts = ROOT / ".artifacts/playwright"
|
||
|
|
artifacts.mkdir(parents=True, exist_ok=True)
|
||
|
|
self.directory = tempfile.TemporaryDirectory(prefix="session-guard-", dir=artifacts)
|
||
|
|
self.root = Path(self.directory.name)
|
||
|
|
self.executable = self.root / "codex-probe"
|
||
|
|
self.executable.write_text("#!/usr/bin/env python3\nimport json,sys\nprint(json.dumps({'args':sys.argv[1:],'stdin':sys.stdin.read()}))\n")
|
||
|
|
self.executable.chmod(0o700)
|
||
|
|
|
||
|
|
def tearDown(self):
|
||
|
|
self.directory.cleanup()
|
||
|
|
|
||
|
|
def state(self, domain="localhost", mode=0o600):
|
||
|
|
path = self.root / "session.json"
|
||
|
|
path.write_text(json.dumps({"cookies": [{"name": "metis.sid", "domain": domain,
|
||
|
|
"httpOnly": True, "value": "placeholder-not-a-session"}]}))
|
||
|
|
path.chmod(mode)
|
||
|
|
return path
|
||
|
|
|
||
|
|
def launch(self, state=None):
|
||
|
|
environment = {**os.environ, "METIS_CODEX_BIN": str(self.executable)}
|
||
|
|
environment.pop("METIS_PLAYWRIGHT_STORAGE_STATE", None)
|
||
|
|
if state is not None:
|
||
|
|
environment["METIS_PLAYWRIGHT_STORAGE_STATE"] = str(state)
|
||
|
|
return subprocess.run(["bash", str(ROOT / "scripts/codex-playwright-wsl.sh"), "mcp", "list", "--json"],
|
||
|
|
input="stdin-check", text=True, capture_output=True, env=environment,
|
||
|
|
cwd=self.root, timeout=15, check=False)
|
||
|
|
|
||
|
|
def args(self, result):
|
||
|
|
self.assertEqual(result.returncode, 0, result.stderr)
|
||
|
|
payload = json.loads(result.stdout)
|
||
|
|
self.assertEqual(payload["stdin"], "stdin-check")
|
||
|
|
argument = next(item for item in payload["args"] if item.startswith("mcp_servers.playwright.args="))
|
||
|
|
return tomllib.loads(argument)["mcp_servers"]["playwright"]["args"]
|
||
|
|
|
||
|
|
def test_default_has_no_session(self):
|
||
|
|
self.assertFalse(any("storage-state" in item or "type=bind" in item for item in self.args(self.launch())))
|
||
|
|
|
||
|
|
def test_private_session_is_readonly_and_not_in_arguments(self):
|
||
|
|
state = self.state()
|
||
|
|
args = self.args(self.launch(state.relative_to(ROOT)))
|
||
|
|
self.assertTrue(any("readonly" in item and str(state) in item for item in args))
|
||
|
|
self.assertEqual(args.count("--storage-state=/tmp/metis-audit-state.json"), 1)
|
||
|
|
self.assertNotIn("placeholder-not-a-session", " ".join(args))
|
||
|
|
|
||
|
|
def test_public_permissions_are_rejected(self):
|
||
|
|
result = self.launch(self.state(mode=0o644))
|
||
|
|
self.assertNotEqual(result.returncode, 0)
|
||
|
|
self.assertIn("privado", result.stderr)
|
||
|
|
|
||
|
|
def test_remote_cookie_is_rejected(self):
|
||
|
|
result = self.launch(self.state(domain="production.example.test"))
|
||
|
|
self.assertNotEqual(result.returncode, 0)
|
||
|
|
self.assertIn("locales", result.stderr)
|
||
|
|
|
||
|
|
def test_external_file_is_rejected(self):
|
||
|
|
with tempfile.TemporaryDirectory() as directory:
|
||
|
|
path = Path(directory) / "session.json"
|
||
|
|
path.write_text("{}")
|
||
|
|
path.chmod(0o600)
|
||
|
|
result = self.launch(path)
|
||
|
|
self.assertNotEqual(result.returncode, 0)
|
||
|
|
self.assertIn("privado", result.stderr)
|
||
|
|
|
||
|
|
def test_missing_session_is_rejected(self):
|
||
|
|
result = self.launch(self.root / "missing.json")
|
||
|
|
self.assertNotEqual(result.returncode, 0)
|
||
|
|
self.assertIn("ausente", result.stderr)
|
||
|
|
|
||
|
|
|
||
|
|
if __name__ == "__main__":
|
||
|
|
unittest.main()
|