Publish curated Unraid-AppOps-Gateway source from a506550ef83ef2d81651e37c98a89f7ee330c730
This commit is contained in:
1 parent
2facb8de34
commit
767e66921e
68 files changed
+15924
-313
No files matched your search
@@ -0,0 +1,236 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import importlib.util
|
||||
import io
|
||||
import json
|
||||
import os
|
||||
from pathlib import Path
|
||||
import sys
|
||||
|
||||
import pytest
|
||||
|
||||
ROOT = Path(__file__).resolve().parents[2]
|
||||
PATH = ROOT / "ops/gitea-deploy-broker/hub_candidate_transport.py"
|
||||
SPEC = importlib.util.spec_from_file_location("hub_candidate_transport_test", PATH)
|
||||
transport = importlib.util.module_from_spec(SPEC)
|
||||
assert SPEC.loader is not None
|
||||
sys.modules[SPEC.name] = transport
|
||||
SPEC.loader.exec_module(transport)
|
||||
|
||||
|
||||
def _request(url: str, *, mode="json", maximum=None, output_fd=None):
|
||||
return {
|
||||
"url": url,
|
||||
"headers": {
|
||||
"Accept": "application/json" if mode == "json" else "application/zip",
|
||||
"Authorization": "token " + "x" * 40,
|
||||
"User-Agent": transport.USER_AGENT,
|
||||
},
|
||||
"maxBytes": maximum if maximum is not None else (
|
||||
transport.MAX_METADATA_BYTES if mode == "json" else transport.MAX_ARTIFACT_BYTES
|
||||
),
|
||||
"outputFd": output_fd,
|
||||
}
|
||||
|
||||
|
||||
class Response:
|
||||
status = 200
|
||||
|
||||
def __init__(self, chunks):
|
||||
self.chunks = list(chunks)
|
||||
self.reads = []
|
||||
|
||||
def __enter__(self):
|
||||
return self
|
||||
|
||||
def __exit__(self, *_args):
|
||||
return None
|
||||
|
||||
def read1(self, limit):
|
||||
self.reads.append(limit)
|
||||
if not self.chunks:
|
||||
return b""
|
||||
chunk = self.chunks.pop(0)
|
||||
return chunk[:limit]
|
||||
|
||||
def read(self, _limit):
|
||||
pytest.fail("transport must use deadline-aware read1")
|
||||
|
||||
|
||||
class Opener:
|
||||
def __init__(self, response):
|
||||
self.response = response
|
||||
self.requests = []
|
||||
self.timeouts = []
|
||||
|
||||
def open(self, request, timeout):
|
||||
self.requests.append(request)
|
||||
self.timeouts.append(timeout)
|
||||
return self.response
|
||||
|
||||
|
||||
@pytest.mark.parametrize("url", [
|
||||
"https://gitea.example.com/api/v1/repos/jens/itworx_mcp_hub/branches/main",
|
||||
"https://gitea.example.com:443/api/v1/repos/jens/itworx_mcp_hub/actions/workflows",
|
||||
"https://gitea.example.com/api/v1/repos/jens/itworx_mcp_hub/git/commits/" + "a" * 40,
|
||||
"https://gitea.example.com/api/v1/repos/jens/itworx_mcp_hub/actions/runs?event=push&branch=main&head_sha=" + "a" * 40 + "&page=1&limit=100",
|
||||
"https://gitea.example.com/api/v1/repos/jens/itworx_mcp_hub/actions/runs/123/jobs?page=2&limit=100",
|
||||
"https://gitea.example.com/api/v1/repos/jens/itworx_mcp_hub/actions/runs/123/artifacts?name=hub-release-candidate-" + "a" * 40,
|
||||
])
|
||||
def test_fixed_json_routes_are_allowed(url):
|
||||
assert transport._url_is_allowed(url, "json")
|
||||
|
||||
|
||||
@pytest.mark.parametrize("url", [
|
||||
"https://other.invalid/api/v1/repos/jens/itworx_mcp_hub/actions/workflows",
|
||||
"http://gitea.example.com/api/v1/repos/jens/itworx_mcp_hub/actions/workflows",
|
||||
"https://gitea.example.com:444/api/v1/repos/jens/itworx_mcp_hub/actions/workflows",
|
||||
"https://user@gitea.example.com/api/v1/repos/jens/itworx_mcp_hub/actions/workflows",
|
||||
"https://gitea.example.com/api/v1/repos/jens/itworx_mcp_hub/actions/runs?event=push&event=push&branch=main&head_sha=" + "a" * 40 + "&page=1&limit=100",
|
||||
"https://gitea.example.com/api/v1/repos/jens/itworx_mcp_hub/actions/runs?event=pull_request&branch=main&head_sha=" + "a" * 40 + "&page=1&limit=100",
|
||||
"https://gitea.example.com/api/v1/repos/jens/itworx_mcp_hub/actions/runs/99999999999999999999/jobs?page=1&limit=100",
|
||||
"https://gitea.example.com/api/v1/repos/jens/other/actions/workflows",
|
||||
])
|
||||
def test_nonfixed_json_routes_are_rejected(url):
|
||||
assert not transport._url_is_allowed(url, "json")
|
||||
|
||||
|
||||
def test_zip_route_is_fixed_and_has_bounded_artifact_id():
|
||||
assert transport._url_is_allowed(
|
||||
"https://gitea.example.com/api/v1/repos/jens/itworx_mcp_hub/actions/artifacts/12/zip", "zip"
|
||||
)
|
||||
assert not transport._url_is_allowed(
|
||||
"https://gitea.example.com/api/v1/repos/jens/itworx_mcp_hub/actions/artifacts/9223372036854775808/zip", "zip"
|
||||
)
|
||||
assert not transport._url_is_allowed(
|
||||
"https://gitea.example.com/api/v1/repos/jens/itworx_mcp_hub/actions/artifacts/12/zip?other=x", "zip"
|
||||
)
|
||||
|
||||
|
||||
def test_ipc_requires_exact_fields_unique_json_and_16kib_bound():
|
||||
valid = json.dumps(_request("https://gitea.example.com/api/v1/repos/jens/itworx_mcp_hub/branches/main")).encode()
|
||||
assert transport._read_request(io.BytesIO(valid))["maxBytes"] == transport.MAX_METADATA_BYTES
|
||||
for invalid in (
|
||||
b'{"url":"x","url":"y"}',
|
||||
valid[:-1] + b',"unexpected":true}',
|
||||
b'{"a":NaN}',
|
||||
b"x" * (transport.MAX_INPUT_BYTES + 1),
|
||||
):
|
||||
with pytest.raises(Exception):
|
||||
transport._read_request(io.BytesIO(invalid))
|
||||
|
||||
|
||||
@pytest.mark.parametrize("patch", [
|
||||
{"headers": {"Accept": "application/json", "Authorization": "token x\r\nInjected: yes", "User-Agent": transport.USER_AGENT}},
|
||||
{"headers": {"Accept": "application/json", "Authorization": "token " + "x" * 40, "User-Agent": transport.USER_AGENT, "Cookie": "x"}},
|
||||
{"maxBytes": True},
|
||||
{"outputFd": 3},
|
||||
])
|
||||
def test_json_request_rejects_header_injection_extra_header_and_bad_limits(patch):
|
||||
value = _request("https://gitea.example.com/api/v1/repos/jens/itworx_mcp_hub/branches/main")
|
||||
value.update(patch)
|
||||
with pytest.raises(transport.TransportError):
|
||||
transport._validate_request(value, "json")
|
||||
|
||||
|
||||
def test_json_mode_emits_only_raw_bounded_response_bytes():
|
||||
body = b'{"workflows":[]}'
|
||||
opener = Opener(Response([body]))
|
||||
value = _request("https://gitea.example.com/api/v1/repos/jens/itworx_mcp_hub/actions/workflows")
|
||||
result = transport.execute(value, "json", opener_factory=lambda mode: opener)
|
||||
assert result == body
|
||||
assert opener.timeouts == [10]
|
||||
headers = {key.lower(): val for key, val in opener.requests[0].header_items()}
|
||||
assert headers == {
|
||||
"accept": "application/json",
|
||||
"authorization": "token " + "x" * 40,
|
||||
"user-agent": transport.USER_AGENT,
|
||||
}
|
||||
|
||||
|
||||
def test_zip_mode_streams_only_to_inherited_fd_and_reports_byte_count():
|
||||
read_fd, write_fd = os.pipe()
|
||||
try:
|
||||
response = Response([b"zip-", b"bytes"])
|
||||
opener = Opener(response)
|
||||
value = _request(
|
||||
"https://gitea.example.com/api/v1/repos/jens/itworx_mcp_hub/actions/artifacts/12/zip",
|
||||
mode="zip", output_fd=write_fd,
|
||||
)
|
||||
assert transport.execute(value, "zip", opener_factory=lambda mode: opener) == 9
|
||||
os.close(write_fd)
|
||||
write_fd = -1
|
||||
assert os.read(read_fd, 32) == b"zip-bytes"
|
||||
assert opener.timeouts == [60]
|
||||
finally:
|
||||
if write_fd >= 0:
|
||||
os.close(write_fd)
|
||||
os.close(read_fd)
|
||||
|
||||
|
||||
def test_redirect_strips_authorization_and_rejects_other_hosts():
|
||||
request = transport.Request(
|
||||
"https://gitea.example.com/api/v1/artifact",
|
||||
headers={"Accept": "application/zip", "Authorization": "token secret", "User-Agent": transport.USER_AGENT},
|
||||
)
|
||||
handler = transport._SameHostArtifactRedirect()
|
||||
redirected = handler.redirect_request(
|
||||
request, None, 302, "", {}, "https://gitea.example.com/attachments/opaque?signature=opaque"
|
||||
)
|
||||
assert redirected is not None
|
||||
assert {key.lower(): value for key, value in redirected.header_items()} == {
|
||||
"accept": "application/zip", "user-agent": transport.USER_AGENT,
|
||||
}
|
||||
for url in (
|
||||
"http://gitea.example.com/attachments/file",
|
||||
"https://other.invalid/attachments/file",
|
||||
"https://gitea.example.com:444/attachments/file",
|
||||
"https://user@gitea.example.com/attachments/file",
|
||||
"https://gitea.example.com/attachments/file#fragment",
|
||||
):
|
||||
assert handler.redirect_request(request, None, 302, "", {}, url) is None
|
||||
|
||||
|
||||
def test_deadline_stops_a_continuous_read1_trickle():
|
||||
clock = [0.0]
|
||||
|
||||
class Trickle(Response):
|
||||
def read1(self, _limit):
|
||||
clock[0] += 2
|
||||
return b" "
|
||||
|
||||
value = _request("https://gitea.example.com/api/v1/repos/jens/itworx_mcp_hub/branches/main")
|
||||
with pytest.raises(transport.TransportError, match="deadline"):
|
||||
transport.execute(value, "json", opener_factory=lambda _mode: Opener(Trickle([])),
|
||||
monotonic=lambda: clock[0])
|
||||
|
||||
|
||||
def test_cli_errors_are_generic_and_zip_marker_is_compact():
|
||||
errors = io.BytesIO()
|
||||
stdout = io.BytesIO()
|
||||
assert transport.main(["wrong"], stdin=io.BytesIO(b""), stdout=stdout, stderr=errors) == 1
|
||||
assert errors.getvalue() == transport._GENERIC_ERROR
|
||||
assert b"wrong" not in errors.getvalue()
|
||||
|
||||
read_fd, write_fd = os.pipe()
|
||||
try:
|
||||
value = _request(
|
||||
"https://gitea.example.com/api/v1/repos/jens/itworx_mcp_hub/actions/artifacts/12/zip",
|
||||
mode="zip", output_fd=write_fd,
|
||||
)
|
||||
response = Response([b"zip"])
|
||||
payload = json.dumps(value, separators=(",", ":")).encode()
|
||||
stdout = io.BytesIO()
|
||||
errors = io.BytesIO()
|
||||
result = transport.main(["zip"], stdin=io.BytesIO(payload), stdout=stdout,
|
||||
stderr=errors, opener_factory=lambda _mode: Opener(response))
|
||||
assert result == 0
|
||||
assert stdout.getvalue() == b'{"bytes":3}\n'
|
||||
assert errors.getvalue() == b""
|
||||
os.close(write_fd)
|
||||
write_fd = -1
|
||||
assert os.read(read_fd, 32) == b"zip"
|
||||
finally:
|
||||
if write_fd >= 0:
|
||||
os.close(write_fd)
|
||||
os.close(read_fd)
|
||||
Reference in new issue
Block a user