fix(cd): accept bounded partial SSH route proof
This commit is contained in:
@@ -1,10 +1,24 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import json
|
||||
import os
|
||||
import re
|
||||
import subprocess
|
||||
import sys
|
||||
import textwrap
|
||||
from pathlib import Path
|
||||
from typing import Any
|
||||
|
||||
import yaml
|
||||
|
||||
_BROKER_PROBE_HOSTS = (
|
||||
"192.168.0.110",
|
||||
"192.168.0.112",
|
||||
"192.168.0.120",
|
||||
"192.168.0.121",
|
||||
"192.168.0.188",
|
||||
)
|
||||
|
||||
|
||||
def _network_policies() -> dict[str, dict[str, Any]]:
|
||||
repo_root = Path(__file__).resolve().parents[3]
|
||||
@@ -24,6 +38,64 @@ def _ports(policy: dict[str, Any]) -> set[int]:
|
||||
}
|
||||
|
||||
|
||||
def _broker_probe_script() -> str:
|
||||
repo_root = Path(__file__).resolve().parents[3]
|
||||
workflow = (repo_root / ".gitea/workflows/cd.yaml").read_text()
|
||||
function = re.search(
|
||||
r"verify_broker_ssh_allowed\(\) \{.*?<<'PY'\n"
|
||||
r"(?P<script>.*?)\n\s+PY\n\s+\}",
|
||||
workflow,
|
||||
re.DOTALL,
|
||||
)
|
||||
assert function is not None
|
||||
return textwrap.dedent(function.group("script"))
|
||||
|
||||
|
||||
def _run_broker_probe(
|
||||
tmp_path: Path,
|
||||
*,
|
||||
outcomes: dict[str, str],
|
||||
) -> subprocess.CompletedProcess[str]:
|
||||
fake_socket = tmp_path / "socket.py"
|
||||
fake_socket.write_text(
|
||||
textwrap.dedent(
|
||||
"""
|
||||
import json
|
||||
import os
|
||||
|
||||
_OUTCOMES = json.loads(os.environ["AWOOOI_TEST_SOCKET_OUTCOMES"])
|
||||
|
||||
|
||||
class _Connection:
|
||||
def close(self):
|
||||
return None
|
||||
|
||||
|
||||
def create_connection(address, timeout):
|
||||
del timeout
|
||||
outcome = _OUTCOMES.get(address[0], "unreachable")
|
||||
if outcome == "connected":
|
||||
return _Connection()
|
||||
if outcome == "refused":
|
||||
raise ConnectionRefusedError(address[0])
|
||||
raise TimeoutError(address[0])
|
||||
"""
|
||||
),
|
||||
encoding="utf-8",
|
||||
)
|
||||
env = os.environ.copy()
|
||||
env["PYTHONPATH"] = str(tmp_path)
|
||||
env["AWOOOI_TEST_SOCKET_OUTCOMES"] = json.dumps(outcomes)
|
||||
return subprocess.run(
|
||||
[sys.executable, "-", *_BROKER_PROBE_HOSTS],
|
||||
input=_broker_probe_script(),
|
||||
text=True,
|
||||
capture_output=True,
|
||||
check=False,
|
||||
env=env,
|
||||
)
|
||||
|
||||
|
||||
def test_common_egress_does_not_grant_ssh() -> None:
|
||||
policies = _network_policies()
|
||||
|
||||
@@ -121,6 +193,57 @@ def test_cd_applies_rolls_back_and_verifies_network_boundary() -> None:
|
||||
assert "except ConnectionRefusedError:" in workflow
|
||||
assert "broker_ssh_refused_but_egress_permitted=" in workflow
|
||||
assert "broker_ssh_allowlisted_endpoint_unavailable=" in workflow
|
||||
assert "raise SystemExit(0 if connected else 1)" in workflow
|
||||
assert "raise SystemExit(0 if connected or refused else 1)" in workflow
|
||||
assert "len(permitted) == len(sys.argv) - 1" not in workflow
|
||||
assert "ssh-mcp-key known_hosts 更新失敗,停止部署" in workflow
|
||||
|
||||
|
||||
def test_broker_probe_accepts_partial_live_route_evidence(tmp_path: Path) -> None:
|
||||
result = _run_broker_probe(
|
||||
tmp_path,
|
||||
outcomes={
|
||||
"192.168.0.110": "connected",
|
||||
"192.168.0.112": "unreachable",
|
||||
"192.168.0.120": "connected",
|
||||
"192.168.0.121": "connected",
|
||||
"192.168.0.188": "connected",
|
||||
},
|
||||
)
|
||||
|
||||
assert result.returncode == 0
|
||||
assert result.stdout.splitlines() == [
|
||||
"broker_ssh_connected="
|
||||
"192.168.0.110,192.168.0.120,192.168.0.121,192.168.0.188",
|
||||
"broker_ssh_refused_but_egress_permitted=",
|
||||
"broker_ssh_allowlisted_endpoint_unavailable=192.168.0.112",
|
||||
]
|
||||
|
||||
|
||||
def test_broker_probe_accepts_refusal_as_bounded_route_evidence(
|
||||
tmp_path: Path,
|
||||
) -> None:
|
||||
result = _run_broker_probe(
|
||||
tmp_path,
|
||||
outcomes={"192.168.0.112": "refused"},
|
||||
)
|
||||
|
||||
assert result.returncode == 0
|
||||
assert result.stdout.splitlines() == [
|
||||
"broker_ssh_connected=",
|
||||
"broker_ssh_refused_but_egress_permitted=192.168.0.112",
|
||||
"broker_ssh_allowlisted_endpoint_unavailable="
|
||||
"192.168.0.110,192.168.0.120,192.168.0.121,192.168.0.188",
|
||||
]
|
||||
|
||||
|
||||
def test_broker_probe_rejects_arbitrary_socket_errors(tmp_path: Path) -> None:
|
||||
result = _run_broker_probe(tmp_path, outcomes={})
|
||||
|
||||
assert result.returncode == 1
|
||||
assert result.stdout.splitlines() == [
|
||||
"broker_ssh_connected=",
|
||||
"broker_ssh_refused_but_egress_permitted=",
|
||||
"broker_ssh_allowlisted_endpoint_unavailable="
|
||||
"192.168.0.110,192.168.0.112,192.168.0.120,"
|
||||
"192.168.0.121,192.168.0.188",
|
||||
]
|
||||
|
||||
Reference in New Issue
Block a user