Files
maka/packages/eval/harbor/test_relay_contract.py
agent-radar 7be535059e
Dependency audit / audit (push) Canceled after 0s
CI / plan (push) Canceled after 0s
Gitoxide helper admission / macos-latest (push) Canceled after 0s
Gitoxide helper admission / ubuntu-latest (push) Canceled after 0s
Gitoxide helper admission / windows-latest (push) Canceled after 0s
Runtime Host peer admission / quality (push) Canceled after 0s
Windows recovery / windows_recovery (push) Canceled after 0s
CI / heavy (push) Canceled after 0s
CI / test (push) Canceled after 0s
源码快照 @ 2026-09-01 (source-only, 剔除非源码素材)
2026-09-01 19:05:14 +08:00

689 lines
28 KiB
Python

# Licensed to the Apache Software Foundation (ASF) under one
# or more contributor license agreements. See the NOTICE file
# distributed with this work for additional information
# regarding copyright ownership. The ASF licenses this file
# to you under the Apache License, Version 2.0 (the
# "License"); you may not use this file except in compliance
# with the License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing,
# software distributed under the License is distributed on an
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
# KIND, either express or implied. See the License for the
# specific language governing permissions and limitations
# under the License.
import asyncio
import base64
import hashlib
import importlib
import inspect
import os
import shlex
import subprocess
import sys
import tempfile
import types
import unittest
import uuid
from pathlib import Path
from unittest.mock import patch
class BaseAgent:
def __init__(self, *args, **kwargs):
pass
class Environment:
def __init__(self, stage_upload: bool = False):
self.uploaded = b""
self.uploaded_target = None
self.stage_upload = stage_upload
async def upload_file(self, source: Path, target: str) -> None:
self.uploaded = source.read_bytes()
if self.stage_upload:
self.uploaded_target = Path(target)
self.uploaded_target.write_bytes(self.uploaded)
def load_relay(framework="harbor"):
from eval_framework import install
install(framework)
package = types.ModuleType(framework)
agents = types.ModuleType(f"{framework}.agents")
base = types.ModuleType(f"{framework}.agents.base")
base.BaseAgent = BaseAgent
sys.modules[framework] = package
sys.modules[f"{framework}.agents"] = agents
sys.modules[f"{framework}.agents.base"] = base
sys.modules.pop("relay_agent", None)
return importlib.import_module("relay_agent")
class RelayContractTest(unittest.TestCase):
def test_merged_noise_cannot_corrupt_or_escape_the_result_frame(self):
relay = load_relay()
token = "frame-token"
payload = b'{"kind":"settled","status":"completed"}'
encoded = base64.urlsafe_b64encode(payload).decode().rstrip("=")
frame = (
f"MAKA-EVAL-RESULT-V1 {token} {len(payload)} "
f"{hashlib.sha256(payload).hexdigest()} {encoded}\n"
)
sentinel = "credential-sentinel-must-not-persist"
stdout, diagnostic = relay._decode_result_carrier(
f"docker warning\n{frame}{sentinel}\n", token
)
self.assertEqual(stdout, payload.decode())
self.assertEqual(diagnostic["category"], "unstructured-output")
self.assertEqual(
diagnostic["bytes"], len(f"docker warning\n{sentinel}\n".encode())
)
self.assertNotIn(sentinel, str({"stdout": stdout, "diagnostic": diagnostic}))
def test_invalid_result_frames_fail_closed_with_bounded_evidence(self):
relay = load_relay()
token = "frame-token"
payload = b"{}"
encoded = base64.urlsafe_b64encode(payload).decode().rstrip("=")
valid = (
f"MAKA-EVAL-RESULT-V1 {token} {len(payload)} "
f"{hashlib.sha256(payload).hexdigest()} {encoded}\n"
)
cases = {
"noise only": "result-frame-missing",
valid + valid: "result-frame-ambiguous",
valid.replace(hashlib.sha256(payload).hexdigest(), "0" * 64): "result-frame-invalid",
}
for carrier, category in cases.items():
with self.subTest(category=category):
stdout, diagnostic = relay._decode_result_carrier(carrier, token)
self.assertEqual(stdout, "")
self.assertEqual(diagnostic["category"], category)
self.assertEqual(set(diagnostic), {"category", "bytes", "sha256"})
def test_non_utf8_result_payload_is_classified_as_an_invalid_frame(self):
relay = load_relay()
token = "frame-token"
payload = b"\xff"
encoded = base64.urlsafe_b64encode(payload).decode().rstrip("=")
frame = (
f"MAKA-EVAL-RESULT-V1 {token} {len(payload)} "
f"{hashlib.sha256(payload).hexdigest()} {encoded}\n"
)
stdout, diagnostic = relay._decode_result_carrier(frame, token)
self.assertEqual(stdout, "")
self.assertEqual(diagnostic["category"], "result-frame-invalid")
def test_oversized_result_carrier_is_rejected_before_parsing(self):
relay = load_relay()
carrier = "x" * (relay.RESULT_CARRIER_LIMIT_BYTES + 1)
stdout, diagnostic = relay._decode_result_carrier(carrier, "frame-token")
self.assertEqual(stdout, "")
self.assertEqual(diagnostic["category"], "result-frame-oversize")
def test_subject_output_cannot_counterfeit_scope_setup_failure(self):
relay = load_relay()
token = "0" * 32
carrier = f"subject output\nMAKA-EVAL-SCOPE-ERROR-V1 {token}\n"
_, diagnostic = relay._project_result(
types.SimpleNamespace(stdout=carrier),
{"captureStdout": False, "resultToken": token},
)
self.assertEqual(diagnostic["category"], "none")
def test_command_keeps_capture_and_control_files_out_of_task_workspace(self):
relay = load_relay()
environment = Environment()
command = asyncio.run(
relay._prepare_command(
environment,
{
"command": "/opt/agent",
"args": ["run"],
"environment": {},
"credentials": {},
"captureStdout": True,
"resultToken": "0" * 32,
},
"token",
"/logs/agent/.maka-eval-token.pid",
)
)
self.assertNotIn(".stdout", command)
self.assertNotIn(".stderr", command)
self.assertNotIn("/app", command)
self.assertIn("/logs/agent/.maka-eval-token.pid", command)
self.assertIn("2>/dev/null", command)
def test_command_does_not_start_subject_when_scope_pid_cannot_be_published(self):
relay = load_relay()
environment = Environment(stage_upload=True)
with tempfile.TemporaryDirectory() as directory:
root = Path(directory)
fake_bin = root / "bin"
fake_bin.mkdir()
fake_setsid = fake_bin / "setsid"
fake_setsid.write_text(
"#!/bin/sh\n"
"if [ \"$1\" = --wait ]; then shift; fi\n"
"exec \"$@\"\n"
)
fake_setsid.chmod(0o755)
marker = root / "subject-started"
scope_path = root / "missing" / "scope.pid"
command = asyncio.run(
relay._prepare_command(
environment,
{
"command": "/bin/sh",
"args": ["-c", f"touch {shlex.quote(str(marker))}"],
"environment": {},
"credentials": {},
"captureStdout": False,
"resultToken": "0" * 32,
},
"token",
str(scope_path),
)
)
try:
completed = subprocess.run(
command,
shell=True,
check=False,
stdout=subprocess.PIPE,
stderr=subprocess.STDOUT,
env={**os.environ, "PATH": f"{fake_bin}:{os.environ['PATH']}"},
)
self.assertFalse(marker.exists())
finally:
if environment.uploaded_target is not None:
environment.uploaded_target.unlink(missing_ok=True)
self.assertNotEqual(completed.returncode, 0)
stdout, diagnostic = relay._project_result(
types.SimpleNamespace(stdout=completed.stdout.decode()),
{"captureStdout": False, "resultToken": "0" * 32},
)
self.assertEqual(stdout, "")
self.assertEqual(diagnostic["category"], "execution-scope-unavailable")
def test_stages_environment_and_discards_unstructured_stdout(self):
relay = load_relay()
environment = Environment()
command = asyncio.run(
relay._prepare_command(
environment,
{
"command": "/opt/agent",
"args": ["run"],
"environment": {"MODE": "offline"},
"credentials": {"API_KEY": "canary-secret"},
"captureStdout": False,
"resultToken": "0" * 32,
},
"token",
"/tmp/scope.pid",
)
)
self.assertIn(b"export MODE=offline", environment.uploaded)
self.assertIn(b"export API_KEY=canary-secret", environment.uploaded)
self.assertNotIn("canary-secret", command)
self.assertIn(">/dev/null", command)
def test_leaves_no_credential_file_when_command_preparation_fails(self):
relay = load_relay()
environment = Environment()
named_temporary_file = tempfile.NamedTemporaryFile
with tempfile.TemporaryDirectory() as directory:
with patch.object(
relay.tempfile,
"NamedTemporaryFile",
side_effect=lambda *args, **kwargs: named_temporary_file(
*args, dir=directory, **kwargs
),
):
with self.assertRaisesRegex(RuntimeError, "invalid Maka Eval credential name"):
asyncio.run(
relay._prepare_command(
environment,
{
"command": "/opt/agent",
"args": ["run"],
"environment": {"MODE": "offline"},
"credentials": {
"API_KEY": "canary-secret",
"BAD-NAME": "ignored",
},
"captureStdout": False,
"resultToken": "0" * 32,
},
"token",
"/tmp/scope.pid",
)
)
self.assertEqual(list(Path(directory).iterdir()), [])
def test_removes_credential_file_when_subject_overrides_path(self):
relay = load_relay()
environment = Environment(stage_upload=True)
token = f"contract-{uuid.uuid4().hex}"
with tempfile.TemporaryDirectory() as directory:
command = asyncio.run(
relay._prepare_command(
environment,
{
"command": "/bin/sh",
"args": ["-c", "exit 0"],
"environment": {"PATH": "/definitely-missing"},
"credentials": {"API_KEY": "canary-secret"},
"captureStdout": True,
"resultToken": "0" * 32,
},
token,
str(Path(directory) / "scope.pid"),
)
)
target = environment.uploaded_target
self.assertIsNotNone(target)
try:
completed = subprocess.run(
["/bin/sh", "-c", shlex.split(command)[-1]],
check=False,
capture_output=True,
text=True,
)
self.assertEqual(completed.returncode, 0, completed.stderr)
self.assertFalse(target.exists())
finally:
target.unlink(missing_ok=True)
# Every default Docker capability except the two that bypass the policy.
CONSTRAINED_CAPABILITIES = 0xA80425FB & ~(1 << 13) & ~(1 << 12)
# The kernel's form for a namespace link target. Two distinct ones, because what
# the check compares is identity: the same string means the same namespace.
POLICY_NAMESPACE = "net:[4026532001]"
OWN_NAMESPACE = "net:[4026532099]"
class SubjectEnvironment:
"""Reports the evidence the probe reads: every `Cap` set, and both namespaces."""
def __init__(
self,
*,
permitted: int = CONSTRAINED_CAPABILITIES,
effective: int | None = None,
bounding: int | None = None,
namespace: str = POLICY_NAMESPACE,
default_user: str | None = None,
) -> None:
self.sets = {
"CapInh": 0,
"CapPrm": permitted,
"CapEff": permitted if effective is None else effective,
"CapBnd": permitted if bounding is None else bounding,
"CapAmb": 0,
}
self.namespace = namespace
self.default_user = default_user
self.commands: list[str] = []
self.users: list[str | None] = []
self.services: list[str] = []
async def exec(self, command: str, cwd=None, env=None, timeout_sec=None, user=None):
# Harbor 0.20.0 BaseEnvironment.exec takes user= as a named argument,
# not **kwargs. Absorbing unknown keywords here would hide a TypeError
# against the real Docker environment.
self.commands.append(command)
explicit = str(user) if user is not None else None
self.users.append(explicit if explicit is not None else self.default_user)
if "proxy-ipv4" in command:
return types.SimpleNamespace(
return_code=0,
stdout="MAKA-EVAL-PROXY-HOST-V1 172.18.0.2 maka-eval-mitmproxy\n",
stderr="",
)
reported = " ".join(f"{name}={value:016x}" for name, value in self.sets.items())
return types.SimpleNamespace(
return_code=0,
stdout=(
f"MAKA-EVAL-CAPABILITIES-V1 {reported}\n"
f"MAKA-EVAL-POLICY-NAMESPACE-V1 {self.namespace}\n"
),
stderr="",
)
async def service_exec(self, command: str, *, service: str, **kwargs):
self.services.append(service)
return types.SimpleNamespace(
return_code=0,
stdout=f"MAKA-EVAL-POLICY-NAMESPACE-V1 {POLICY_NAMESPACE}\n",
stderr="",
)
def _echoing_service_exec(answer: str):
async def service_exec(command: str, *, service: str, **kwargs):
return types.SimpleNamespace(
return_code=0,
stdout=f"MAKA-EVAL-POLICY-NAMESPACE-V1 {answer}\n",
stderr="",
)
return service_exec
class SubjectCapabilityTest(unittest.IsolatedAsyncioTestCase):
async def test_subject_holding_a_bypass_capability_never_starts(self):
relay = load_relay()
# Named here rather than read from the module: sourcing the expectation
# from the code under test would let deleting a capability delete its
# own coverage. NET_RAW grants AF_PACKET, NET_ADMIN grants `nft flush`.
for name, bit in (("NET_RAW", 1 << 13), ("NET_ADMIN", 1 << 12)):
with self.subTest(name):
environment = SubjectEnvironment(permitted=bit | (1 << 21))
with patch.dict(os.environ, {"MAKA_EVAL_EGRESS_REQUIRED": "1"}):
with self.assertRaises(RuntimeError) as raised:
await relay._require_constrained_subject(environment)
self.assertIn(name, str(raised.exception))
async def test_capability_reachable_only_through_the_bounding_set_never_starts(self):
relay = load_relay()
# A non-root subject reports an empty effective set while a
# `cap_net_raw+ep` executable still reacquires whatever the bounding set
# kept. Verified in a container: with the capability bounded away the
# same executable cannot be run at all.
environment = SubjectEnvironment(
permitted=0,
effective=0,
bounding=CONSTRAINED_CAPABILITIES | (1 << 13),
)
with patch.dict(os.environ, {"MAKA_EVAL_EGRESS_REQUIRED": "1"}):
with self.assertRaises(RuntimeError) as raised:
await relay._require_constrained_subject(environment)
self.assertIn("NET_RAW", str(raised.exception))
async def test_subject_outside_the_policy_namespace_never_starts(self):
relay = load_relay()
# Harbor applies the policy inside the sidecar and respects a task's own
# networking on `main`, so a task that declares it leaves the subject in
# a namespace no policy was ever applied to.
environment = SubjectEnvironment(namespace=OWN_NAMESPACE)
with patch.dict(os.environ, {"MAKA_EVAL_EGRESS_REQUIRED": "1"}):
with self.assertRaises(RuntimeError) as raised:
await relay._require_constrained_subject(environment)
self.assertIn("network namespace", str(raised.exception))
async def test_policy_namespace_is_read_from_the_service_that_applies_the_policy(self):
relay = load_relay()
# The comparison is only meaningful against the namespace the policy was
# installed in, and Harbor installs it by running `network-policy` in the
# egress sidecar. Reading the other side from anywhere else would compare
# the subject against a namespace nothing was applied to.
environment = SubjectEnvironment()
with patch.dict(os.environ, {"MAKA_EVAL_EGRESS_REQUIRED": "1"}):
await relay._require_constrained_subject(environment)
self.assertEqual(environment.services, ["harbor-docker-egress-control-sidecar"])
async def test_constrained_subject_proceeds(self):
relay = load_relay()
with patch.dict(os.environ, {"MAKA_EVAL_EGRESS_REQUIRED": "1"}):
await relay._require_constrained_subject(SubjectEnvironment())
async def test_unreadable_evidence_fails_closed(self):
relay = load_relay()
class SilentEnvironment:
async def exec(self, command, cwd=None, timeout_sec=None):
return types.SimpleNamespace(return_code=0, stdout="", stderr="")
async def service_exec(self, command, *, service, **kwargs):
return types.SimpleNamespace(return_code=0, stdout="", stderr="")
with patch.dict(os.environ, {"MAKA_EVAL_EGRESS_REQUIRED": "1"}):
with self.assertRaises(RuntimeError):
await relay._require_constrained_subject(SilentEnvironment())
async def test_answer_that_is_not_a_namespace_identity_fails_closed(self):
relay = load_relay()
# Two sides that both failed to answer must not compare equal. Anything
# that is not the kernel's own form is not an identity to compare.
for answer in ("", "shared", "net:[]"):
with self.subTest(answer):
environment = SubjectEnvironment(namespace=answer)
environment.service_exec = _echoing_service_exec(answer)
with patch.dict(os.environ, {"MAKA_EVAL_EGRESS_REQUIRED": "1"}):
with self.assertRaises(RuntimeError):
await relay._require_constrained_subject(environment)
async def test_check_is_scoped_to_runs_that_require_the_proxy(self):
relay = load_relay()
environment = SubjectEnvironment(permitted=1 << 13, namespace=OWN_NAMESPACE)
with patch.dict(os.environ, {"MAKA_EVAL_EGRESS_REQUIRED": ""}):
await relay._require_constrained_subject(environment)
self.assertEqual(environment.commands, [])
self.assertEqual(environment.services, [])
async def test_missing_published_proxy_address_fails_closed(self):
relay = load_relay()
class MissingAddressEnvironment(SubjectEnvironment):
async def exec(self, command, cwd=None, env=None, timeout_sec=None, user=None):
if "proxy-ipv4" in command:
self.commands.append(command)
explicit = str(user) if user is not None else None
self.users.append(
explicit if explicit is not None else self.default_user
)
return types.SimpleNamespace(return_code=1, stdout="", stderr="")
return await super().exec(
command, cwd=cwd, env=env, timeout_sec=timeout_sec, user=user
)
environment = MissingAddressEnvironment()
with patch.dict(os.environ, {"MAKA_EVAL_EGRESS_REQUIRED": "1"}):
with self.assertRaises(RuntimeError) as raised:
await relay._require_constrained_subject(environment)
self.assertIn("pin the Eval egress proxy hostname", str(raised.exception))
pin_users = [
user
for command, user in zip(environment.commands, environment.users, strict=True)
if "proxy-ipv4" in command
]
self.assertEqual(pin_users, ["root"])
async def test_proxy_hostname_pin_runs_as_root(self):
relay = load_relay()
environment = SubjectEnvironment()
with patch.dict(os.environ, {"MAKA_EVAL_EGRESS_REQUIRED": "1"}):
await relay._require_constrained_subject(environment)
pin_users = [
user
for command, user in zip(environment.commands, environment.users, strict=True)
if "proxy-ipv4" in command
]
self.assertEqual(pin_users, ["root"])
async def test_proxy_hostname_pin_does_not_inherit_a_non_root_default_user(self):
# Harbor scopes every exec to the task's agent.user unless the call
# names another identity. Pinning /etc/hosts is infrastructure, so it
# must still be root when that default is a supported non-root user.
relay = load_relay()
environment = SubjectEnvironment(default_user="1000")
with patch.dict(os.environ, {"MAKA_EVAL_EGRESS_REQUIRED": "1"}):
await relay._require_constrained_subject(environment)
probe_users = [
user
for command, user in zip(environment.commands, environment.users, strict=True)
if "proxy-ipv4" not in command
]
pin_users = [
user
for command, user in zip(environment.commands, environment.users, strict=True)
if "proxy-ipv4" in command
]
self.assertEqual(probe_users, ["1000"])
self.assertEqual(pin_users, ["root"])
async def test_invalid_proxy_hostname_is_rejected_before_hosts_rewrite(self):
relay = load_relay()
for host in (
".*",
"proxy*",
"evil.com\n127.0.0.1 pwned",
"foo bar",
"foo..bar",
".hidden",
"maka-évál",
):
with self.subTest(host=host):
environment = SubjectEnvironment()
with patch.dict(os.environ, {"MAKA_EVAL_EGRESS_ALLOWED_HOST": host}):
with self.assertRaisesRegex(RuntimeError, "proxy host is invalid"):
await relay._pin_proxy_hostname(environment)
self.assertEqual(environment.commands, [])
async def test_proxy_hostname_pin_matches_hosts_aliases_exactly(self):
relay = load_relay()
environment = SubjectEnvironment()
with patch.dict(os.environ, {"MAKA_EVAL_EGRESS_REQUIRED": "1"}):
await relay._require_constrained_subject(environment)
pin = next(command for command in environment.commands if "proxy-ipv4" in command)
self.assertIn(relay.HOSTS_ALIAS_AWK, pin)
completed = subprocess.run(
["awk", "-v", "host=maka-eval-mitmproxy", relay.HOSTS_ALIAS_AWK],
input=(
"127.0.0.1 localhost\n"
"10.0.0.1 maka-eval-mitmproxy extra\n"
"10.0.0.2 prefix-maka-eval-mitmproxy\n"
"10.0.0.3 other.example\n"
"10.0.0.4 other.example maka-eval-mitmproxy\n"
),
check=True,
capture_output=True,
text=True,
)
self.assertEqual(
completed.stdout,
"127.0.0.1 localhost\n"
"10.0.0.2 prefix-maka-eval-mitmproxy\n"
"10.0.0.3 other.example\n",
)
def test_harbor_0_20_0_exec_declares_user_as_a_named_parameter(self):
# Harbor 0.20.0 BaseEnvironment.exec / DockerEnvironment.exec:
# (command, cwd=None, env=None, timeout_sec=None, user=None)
# A **kwargs-only double would absorb user= and hide a TypeError
# against that signature.
parameter = inspect.signature(SubjectEnvironment.exec).parameters["user"]
self.assertNotEqual(parameter.kind, inspect.Parameter.VAR_KEYWORD)
inspect.signature(SubjectEnvironment.exec).bind(
None, "true", user="root"
)
def test_published_ipv4_requires_four_0_255_octets(self):
relay = load_relay()
accepted = ("172.18.0.2", "0.1.2.3", "255.255.255.255", "10.0.0.1")
rejected = (
"999.1.1.1",
"1.2.3",
"1..2.3",
"1.2.3.4.5",
"01.2.3.4",
"1.2.3.08",
"...",
"",
"172.18.0.2/32",
"localhost",
)
for ip in accepted:
with self.subTest(ip=ip):
self.assertTrue(relay._valid_published_ipv4(ip))
for ip in rejected:
with self.subTest(ip=ip):
self.assertFalse(relay._valid_published_ipv4(ip))
for ip in (*accepted, *rejected):
with self.subTest(awk=ip):
completed = subprocess.run(
["awk", relay.IPV4_OCTET_AWK],
input=f"{ip}\n",
capture_output=True,
text=True,
)
self.assertEqual(
completed.returncode == 0,
relay._valid_published_ipv4(ip),
ip,
)
def test_network_policy_uses_the_same_ipv4_octet_check(self):
relay = load_relay()
policy = Path(__file__).with_name("egress-proxy").joinpath("network-policy")
self.assertIn(relay.IPV4_OCTET_AWK.strip(), policy.read_text())
def test_network_policy_rejects_docker_dns_before_local_accept(self):
policy = Path(__file__).with_name("egress-proxy").joinpath("network-policy").read_text()
reject = policy.index("ip daddr 127.0.0.11 reject")
local = policy.index("fib daddr type local accept")
self.assertLess(reject, local)
async def test_malformed_published_proxy_address_fails_closed(self):
relay = load_relay()
class BadAddressEnvironment(SubjectEnvironment):
async def exec(self, command, cwd=None, env=None, timeout_sec=None, user=None):
if "proxy-ipv4" in command:
self.commands.append(command)
explicit = str(user) if user is not None else None
self.users.append(
explicit if explicit is not None else self.default_user
)
return types.SimpleNamespace(
return_code=0,
stdout="MAKA-EVAL-PROXY-HOST-V1 999.1.1.1 maka-eval-mitmproxy\n",
stderr="",
)
return await super().exec(
command, cwd=cwd, env=env, timeout_sec=timeout_sec, user=user
)
with patch.dict(os.environ, {"MAKA_EVAL_EGRESS_REQUIRED": "1"}):
with self.assertRaises(RuntimeError) as raised:
await relay._require_constrained_subject(BadAddressEnvironment())
self.assertIn("pin the Eval egress proxy hostname", str(raised.exception))
class FrameworkSelectionTest(unittest.TestCase):
def test_relay_loads_harbor_and_pier_from_the_installed_selection(self):
for framework in ("harbor", "pier"):
with self.subTest(framework):
relay = load_relay(framework)
self.assertEqual(relay.framework, framework)
def test_relay_does_not_select_a_framework_from_the_environment(self):
os.environ["MAKA_EVAL_FRAMEWORK"] = "pier"
relay = load_relay("harbor")
self.assertEqual(relay.framework, "harbor")
if __name__ == "__main__":
unittest.main()