Initial curated Unraid-AppOps-Gateway source from 3bd6f6fee310125bfbb4d4650389a817f498724d
This commit is contained in:
commit
1dfbca08a1
528 files changed
+131054
No files matched your search
@@ -0,0 +1,177 @@
|
||||
from types import SimpleNamespace
|
||||
|
||||
from sqlmodel import Session, SQLModel, create_engine
|
||||
|
||||
from backend.app.services import action_execution
|
||||
from backend.app.services.action_execution import approval_execution_supported
|
||||
from backend.app.services.action_registry import (
|
||||
ACTION_RISK,
|
||||
ACTIONS,
|
||||
action_default_target,
|
||||
)
|
||||
from backend.app.services.privatemuse_webhook_repair import (
|
||||
PrivateMuseWebhookRepair,
|
||||
_ENDPOINT,
|
||||
_REPAIR_SCRIPT,
|
||||
)
|
||||
|
||||
|
||||
class FakeResponse:
|
||||
def __init__(self, payload):
|
||||
self.payload = payload
|
||||
|
||||
def json(self):
|
||||
return self.payload
|
||||
|
||||
|
||||
class FakePortainer:
|
||||
def __init__(self, exit_code=0):
|
||||
self.exit_code = exit_code
|
||||
self.calls = []
|
||||
|
||||
def get_container_status(self, name):
|
||||
assert name == "gitea-deploy-control"
|
||||
return {
|
||||
"status": "ok",
|
||||
"container": {
|
||||
"id": "controller-id",
|
||||
"name": "gitea-deploy-control",
|
||||
"state": "running",
|
||||
},
|
||||
}
|
||||
|
||||
def _config(self):
|
||||
return SimpleNamespace(endpoint_id="3")
|
||||
|
||||
def _request(self, method, path, **kwargs):
|
||||
self.calls.append((method, path, kwargs))
|
||||
if path.endswith("/containers/controller-id/exec"):
|
||||
return FakeResponse({"Id": "exec-webhook"})
|
||||
if path.endswith("/exec/exec-webhook/start"):
|
||||
return FakeResponse({})
|
||||
if path.endswith("/exec/exec-webhook/json"):
|
||||
return FakeResponse({"Running": False, "ExitCode": self.exit_code})
|
||||
raise AssertionError(path)
|
||||
|
||||
|
||||
def test_privatemuse_webhook_repair_is_fixed_high_risk_typed_action():
|
||||
action = "deployment.webhook.repair_privatemuse"
|
||||
assert action in ACTIONS
|
||||
assert ACTION_RISK[action] == "high"
|
||||
assert action_default_target(action) == "jens/privatemuse"
|
||||
assert approval_execution_supported(action) is True
|
||||
|
||||
|
||||
def test_privatemuse_webhook_repair_executes_only_fixed_contract():
|
||||
fake = FakePortainer()
|
||||
|
||||
result = PrivateMuseWebhookRepair(fake).run()
|
||||
|
||||
assert result == {
|
||||
"status": "repaired",
|
||||
"repository": "jens/privatemuse",
|
||||
"endpoint": _ENDPOINT,
|
||||
"branch": "main",
|
||||
"event": "push",
|
||||
}
|
||||
command = fake.calls[0][2]["json"]["Cmd"]
|
||||
assert command[:3] == ["python3", "-c", _REPAIR_SCRIPT]
|
||||
assert command[3:] == ["jens/privatemuse", _ENDPOINT]
|
||||
|
||||
|
||||
def test_privatemuse_webhook_repair_script_never_returns_credentials():
|
||||
assert "runner_guard_admin_token" in _REPAIR_SCRIPT
|
||||
assert 'secret_root = Path("/opt/gitea-deploy/webhook-secrets")' in _REPAIR_SCRIPT
|
||||
assert 'secret_path = secret_root / "jens__privatemuse"' in _REPAIR_SCRIPT
|
||||
assert "secretMaterialReturned" not in _REPAIR_SCRIPT
|
||||
assert 'print(f"privatemuse_runnerless_webhook_ready hook_id={hook_id}"' in _REPAIR_SCRIPT
|
||||
assert "print(secret" not in _REPAIR_SCRIPT
|
||||
assert "print(token" not in _REPAIR_SCRIPT
|
||||
|
||||
|
||||
def test_privatemuse_webhook_repair_proves_effective_controller_reachability():
|
||||
assert "gitea_config_path" not in _REPAIR_SCRIPT
|
||||
assert '"docker", "exec", "Gitea"' in _REPAIR_SCRIPT
|
||||
assert '"http://198.19.0.1:1299/health"' in _REPAIR_SCRIPT
|
||||
assert 'health_payload.get("status") != "ok"' in _REPAIR_SCRIPT
|
||||
assert 'health_payload.get("protocolVersion") != "itworx-controller-request/v2"' in _REPAIR_SCRIPT
|
||||
|
||||
|
||||
def test_privatemuse_webhook_repair_is_fail_closed_and_non_destructive():
|
||||
assert 'repository != "jens/privatemuse"' in _REPAIR_SCRIPT
|
||||
assert 'project.get("branch") != "main"' in _REPAIR_SCRIPT
|
||||
assert '"Managed validation / full (push)"' in _REPAIR_SCRIPT
|
||||
assert 'url.endswith("/v1/gitea/push/jens/privatemuse")' in _REPAIR_SCRIPT
|
||||
assert "len(exact) > 1 or semantic_other" in _REPAIR_SCRIPT
|
||||
assert 'request("PATCH", f"/repos/Jens/PrivateMuse/hooks/{hook_id}", payload)' in _REPAIR_SCRIPT
|
||||
assert 'request(\n "POST",\n "/repos/Jens/PrivateMuse/hooks",' in _REPAIR_SCRIPT
|
||||
for forbidden in (
|
||||
"docker restart",
|
||||
"docker stop",
|
||||
"docker rm",
|
||||
"docker compose",
|
||||
'request("DELETE"',
|
||||
):
|
||||
assert forbidden not in _REPAIR_SCRIPT
|
||||
|
||||
|
||||
def test_privatemuse_webhook_repair_maps_validation_failures():
|
||||
result = PrivateMuseWebhookRepair(FakePortainer(exit_code=34)).run()
|
||||
|
||||
assert result == {
|
||||
"status": "validation_failed",
|
||||
"reason": "PrivateMuse has ambiguous or stale runnerless webhook endpoints",
|
||||
}
|
||||
|
||||
|
||||
def test_privatemuse_webhook_repair_rejects_any_other_action_target():
|
||||
result = action_execution.execute_governed_action(
|
||||
None, # type: ignore[arg-type]
|
||||
action="deployment.webhook.repair_privatemuse",
|
||||
target="jens/not-privatemuse",
|
||||
source="chatgpt-mcp",
|
||||
risk="high",
|
||||
reason="Regression test for exact-target enforcement.",
|
||||
original_policy_decision="allowed",
|
||||
)
|
||||
|
||||
assert result["success"] is False
|
||||
assert result["result"] == "blocked"
|
||||
assert result["message"] == "PrivateMuse webhook repair accepts only jens/privatemuse."
|
||||
|
||||
|
||||
def test_privatemuse_webhook_repair_dispatches_fixed_executor(monkeypatch, tmp_path):
|
||||
engine = create_engine(f"sqlite:///{tmp_path / 'webhook-action.db'}")
|
||||
SQLModel.metadata.create_all(engine)
|
||||
|
||||
class FakeRepair:
|
||||
def run(self):
|
||||
return {
|
||||
"status": "repaired",
|
||||
"repository": "jens/privatemuse",
|
||||
"endpoint": _ENDPOINT,
|
||||
"branch": "main",
|
||||
"event": "push",
|
||||
}
|
||||
|
||||
import backend.app.services.privatemuse_webhook_repair as repair_module
|
||||
|
||||
monkeypatch.setattr(repair_module, "PrivateMuseWebhookRepair", FakeRepair)
|
||||
|
||||
with Session(engine) as session:
|
||||
result = action_execution.execute_governed_action(
|
||||
session,
|
||||
action="deployment.webhook.repair_privatemuse",
|
||||
target="jens/privatemuse",
|
||||
source="chatgpt-mcp",
|
||||
risk="high",
|
||||
reason="Repair the fixed PrivateMuse runnerless hook.",
|
||||
original_policy_decision="approval_required",
|
||||
)
|
||||
|
||||
assert result["success"] is True
|
||||
assert result["status"] == "success"
|
||||
assert result["policyDecision"] == "allowed"
|
||||
assert result["originalPolicyDecision"] == "approval_required"
|
||||
assert result["jobId"].startswith("job-")
|
||||
assert result["auditId"].startswith("audit-")
|
||||
Reference in new issue
Block a user