diff --git a/bot_bottle/backend/egress_apply.py b/bot_bottle/backend/egress_apply.py index 9cf8200..ed54cdf 100644 --- a/bot_bottle/backend/egress_apply.py +++ b/bot_bottle/backend/egress_apply.py @@ -11,7 +11,7 @@ from pathlib import Path from ..bottle_state import egress_state_dir from ..egress import EGRESS_ROUTES_FILENAME -from ..egress_addon_core import load_routes +from ..egress_addon_core import LOG_OFF, load_config class EgressApplyError(RuntimeError): @@ -33,11 +33,15 @@ class EgressApplicator(ABC): @staticmethod def validate_routes_content(content: str) -> None: try: - load_routes(content) + config = load_config(content) except ValueError as e: raise EgressApplyError( f"proposed routes.yaml is not valid: {e}" ) from e + if config.log != LOG_OFF: + raise EgressApplyError( + "proposed routes.yaml must not change egress logging" + ) @staticmethod def _routes_path(slug: str) -> Path: diff --git a/bot_bottle/supervise_server.py b/bot_bottle/supervise_server.py index 3e9ba1c..b6bed85 100644 --- a/bot_bottle/supervise_server.py +++ b/bot_bottle/supervise_server.py @@ -47,11 +47,11 @@ from pathlib import Path try: # Same-directory imports inside the bundle container; these files are # COPYed flat under /app by Dockerfile.sidecars. - from egress_addon_core import load_routes + from egress_addon_core import LOG_OFF, load_config import supervise as _sv except ModuleNotFoundError: # Package imports for host-side tests and tooling. - from .egress_addon_core import load_routes + from .egress_addon_core import LOG_OFF, load_config from . import supervise as _sv @@ -297,12 +297,17 @@ def validate_proposed_file(tool: str, content: str) -> None: pass elif tool in (_sv.TOOL_EGRESS_ALLOW, _sv.TOOL_EGRESS_BLOCK): try: - load_routes(content) + config = load_config(content) except ValueError as e: raise _RpcError( ERR_INVALID_PARAMS, f"{tool}: proposed routes.yaml is not valid: {e}", ) from e + if config.log != LOG_OFF: + raise _RpcError( + ERR_INVALID_PARAMS, + f"{tool}: proposed routes.yaml must not change egress logging", + ) else: raise _RpcError(ERR_INVALID_PARAMS, f"unknown tool {tool!r}") diff --git a/tests/unit/test_egress_apply.py b/tests/unit/test_egress_apply.py index 2966f05..de51c57 100644 --- a/tests/unit/test_egress_apply.py +++ b/tests/unit/test_egress_apply.py @@ -54,6 +54,15 @@ class TestValidateRoutesContent(unittest.TestCase): ' auth_scheme: "Bearer"\n' ) + def test_rejects_log_full(self): + with self.assertRaises(EgressApplyError) as cm: + applicator.validate_routes_content( + 'log: 2\n' + 'routes:\n' + ' - host: "x.example"\n' + ) + self.assertIn("must not change egress logging", str(cm.exception)) + class TestApplyRoutesChange(unittest.TestCase): def setUp(self): diff --git a/tests/unit/test_supervise_server.py b/tests/unit/test_supervise_server.py index 0535076..c18d6a5 100644 --- a/tests/unit/test_supervise_server.py +++ b/tests/unit/test_supervise_server.py @@ -67,6 +67,15 @@ class TestValidation(unittest.TestCase): with self.assertRaises(_RpcError): validate_proposed_file(_sv.TOOL_EGRESS_BLOCK, "routes: nope\n") + def test_egress_routes_yaml_rejects_log_full(self): + with self.assertRaises(_RpcError) as cm: + validate_proposed_file( + _sv.TOOL_EGRESS_ALLOW, + "log: 2\nroutes:\n - host: example.com\n", + ) + self.assertEqual(ERR_INVALID_PARAMS, cm.exception.code) + self.assertIn("must not change egress logging", cm.exception.message) + # --- JSON-RPC parsing ------------------------------------------------------