From 9c26278c67245fbad886164ee6ebe1337b4a51ff Mon Sep 17 00:00:00 2001 From: Hermes Agent Date: Mon, 20 Jul 2026 04:42:51 -0500 Subject: [PATCH] fix(#812 follow-up): normalize detect-platform.sh host-match port comparison by scheme gitea_url_matches_host compared every remote authority's port directly against the configured HTTP(S) API URL's port, which conflated two unrelated things and normalized default web ports asymmetrically: - An SSH remote's transport port (e.g. ssh://git@host:2222/...) was compared against the configured HTTP(S) port, even though the SSH daemon port has nothing to do with the Gitea HTTP(S) API port. - An explicit default port on the remote (https://host:443/...) failed to match an implicit (portless) configured URL, while the inverse (implicit remote vs. explicit configured) already matched -- the default-port equivalence was only applied on one side. Fix get_remote_host() to strip an SSH transport port before it ever reaches host-match, and make gitea_url_matches_host's default-port normalization symmetric (":443" https / ":80" http is equivalent to the implicit form on BOTH sides). Adds red-first regressions for both repros to test-pr-review-gitea-comment.sh (extending the #812 harness) and wires that harness into packages/mosaic's test:framework-shell chain so it runs under `pnpm test`. Closes #850 --- .../framework/tools/git/detect-platform.sh | 26 +++++++++++++------ .../tools/git/test-pr-review-gitea-comment.sh | 21 +++++++++++++-- packages/mosaic/package.json | 2 +- 3 files changed, 38 insertions(+), 11 deletions(-) diff --git a/packages/mosaic/framework/tools/git/detect-platform.sh b/packages/mosaic/framework/tools/git/detect-platform.sh index 38a6f230..3111eb42 100755 --- a/packages/mosaic/framework/tools/git/detect-platform.sh +++ b/packages/mosaic/framework/tools/git/detect-platform.sh @@ -91,13 +91,19 @@ remote = urlparse(f"//{remote_host}") if configured.scheme not in {"http", "https"} or configured.hostname != remote.hostname: raise SystemExit(1) -configured_port = configured.port -remote_port = remote.port -if remote_port is None: - default_port = 80 if configured.scheme == "http" else 443 - if configured_port not in {None, default_port}: - raise SystemExit(1) -elif configured_port != remote_port: +# Normalize by scheme: an implicit (portless) HTTP(S) URL and its explicit +# default-port form (":80" for http, ":443" for https) name the same +# provider endpoint. Apply that equivalence symmetrically -- whichever side +# omits the port is treated as carrying the scheme's default port -- so +# "configured implicit vs. remote explicit" and "configured explicit vs. +# remote implicit" both match. (The remote side here is always an HTTP(S) +# authority; an SSH remote's transport port is stripped by get_remote_host +# before reaching this comparison, since it identifies an unrelated +# service on the same host, not the HTTP(S) provider port.) +default_port = 80 if configured.scheme == "http" else 443 +normalized_configured = configured.port if configured.port is not None else default_port +normalized_remote = remote.port if remote.port is not None else default_port +if normalized_configured != normalized_remote: raise SystemExit(1) raise SystemExit(0) PY @@ -432,7 +438,11 @@ get_remote_host() { fi if [[ "$remote_url" =~ ^ssh://([^/]+)/ ]]; then local host="${BASH_REMATCH[1]}" - echo "${host##*@}" + host="${host##*@}" + # Strip an SSH transport port (e.g. "git.example:2222"): it names the + # SSH daemon port, not the HTTP(S) provider API port, and must not + # feed gitea_url_matches_host's port comparison (#850). + echo "${host%%:*}" return 0 fi if [[ "$remote_url" =~ ^git@([^:]+): ]]; then diff --git a/packages/mosaic/framework/tools/git/test-pr-review-gitea-comment.sh b/packages/mosaic/framework/tools/git/test-pr-review-gitea-comment.sh index 8b42ee29..77f3d3d6 100644 --- a/packages/mosaic/framework/tools/git/test-pr-review-gitea-comment.sh +++ b/packages/mosaic/framework/tools/git/test-pr-review-gitea-comment.sh @@ -73,7 +73,7 @@ case "${PR_REVIEW_TEST_MODE:-}" in request-changes) [[ "$*" == "pr reject 123 --repo mosaicstack/stack --login mosaicstack" ]] || exit 91 ;; - legacy-fallback|comment-success|http-success|prefix-success|subpath-success|port-success|scp-ssh-success|url-ssh-success|write-transport-failure|write-http-failure|readback-failure) + legacy-fallback|comment-success|http-success|prefix-success|subpath-success|port-success|scp-ssh-success|url-ssh-success|ssh-transport-port-success|explicit-default-port-success|write-transport-failure|write-http-failure|readback-failure) if [[ "$*" == pr\ comment* ]]; then # tea v0.11.1 treats the nonexistent subcommand as `tea pr list` and exits 0. printf '%s\n' 'INDEX TITLE STATE' @@ -144,7 +144,7 @@ case "${PR_REVIEW_TEST_MODE:-}" in write-http-failure) write_response 500 '{"message":"simulated rejection"}' ;; - approve|request-changes|comment-success|http-success|prefix-success|subpath-success|port-success|scp-ssh-success|url-ssh-success|readback-failure) + approve|request-changes|comment-success|http-success|prefix-success|subpath-success|port-success|scp-ssh-success|url-ssh-success|ssh-transport-port-success|explicit-default-port-success|readback-failure) if [[ "$method" == "POST" && "$url" == "$PR_REVIEW_EXPECTED_API_BASE/issues/123/comments" ]]; then PR_REVIEW_PAYLOAD="$payload" python3 - <<'PY' import json @@ -291,6 +291,23 @@ grep -q '^POST https://git.example/api/v1/repos/owner/repo/issues/123/comments$' run_review url-ssh-success comment durable-body https://git.example ssh://git@git.example/owner/repo.git owner/repo grep -q '^POST https://git.example/api/v1/repos/owner/repo/issues/123/comments$' "$CURL_LOG" +# #850 (follow-up to #812): an SSH remote's transport port (e.g. `ssh:// +# git@host:2222/...`) must NOT be compared against the configured HTTP(S) API +# URL's port -- they identify unrelated properties (SSH daemon port vs. HTTP(S) +# provider port) of the same Gitea host. Before the fix, host-match required +# the configured URL to carry the identical port, so this failed closed even +# though both remote and configured URL name the same host. +run_review ssh-transport-port-success comment durable-body https://git.example ssh://git@git.example:2222/owner/repo.git owner/repo +grep -q '^POST https://git.example/api/v1/repos/owner/repo/issues/123/comments$' "$CURL_LOG" + +# #850 (follow-up to #812): an explicit default HTTP(S) port on the remote +# (`https://host:443/...`) must be treated as equal to an implicit +# (portless) configured URL on BOTH sides -- the pre-fix comparison only +# normalized the default port when the REMOTE side was portless, so the +# inverse (explicit remote, implicit configured) form failed closed. +run_review explicit-default-port-success comment durable-body https://git.example https://git.example:443/owner/repo.git owner/repo +grep -q '^POST https://git.example/api/v1/repos/owner/repo/issues/123/comments$' "$CURL_LOG" + if run_review write-transport-failure comment durable-body; then echo "Expected provider transport failure to return nonzero" >&2 exit 1 diff --git a/packages/mosaic/package.json b/packages/mosaic/package.json index 55942bc7..7f6917ee 100644 --- a/packages/mosaic/package.json +++ b/packages/mosaic/package.json @@ -25,7 +25,7 @@ "lint": "eslint src", "typecheck": "tsc --noEmit", "test": "vitest run --passWithNoTests && pnpm run test:framework-shell", - "test:framework-shell": "python3 src/lease-broker/daemon_deadline_unittest.py && python3 src/lease-broker/normative_fragments_unittest.py && python3 src/lease-broker/receipt_challenge_unittest.py && python3 src/lease-broker/context_recovery_unittest.py && python3 src/lease-broker/recovery_runtime_unittest.py && python3 src/lease-broker/recovery_b1_adversarial_unittest.py && python3 src/lease-broker/framework_skill_portability_unittest.py && python3 src/mutator-gate/runtime_tools_unittest.py && python3 src/mutator-gate/runtime_launch_guard_unittest.py && python3 framework/tools/lease-broker/check-runtime-launches.py --root ../.. && bash framework/tools/codex/test-pr-diff-context.sh && bash framework/tools/qa/test-deps-preflight.sh" + "test:framework-shell": "python3 src/lease-broker/daemon_deadline_unittest.py && python3 src/lease-broker/normative_fragments_unittest.py && python3 src/lease-broker/receipt_challenge_unittest.py && python3 src/lease-broker/context_recovery_unittest.py && python3 src/lease-broker/recovery_runtime_unittest.py && python3 src/lease-broker/recovery_b1_adversarial_unittest.py && python3 src/lease-broker/framework_skill_portability_unittest.py && python3 src/mutator-gate/runtime_tools_unittest.py && python3 src/mutator-gate/runtime_launch_guard_unittest.py && python3 framework/tools/lease-broker/check-runtime-launches.py --root ../.. && bash framework/tools/codex/test-pr-diff-context.sh && bash framework/tools/qa/test-deps-preflight.sh && bash framework/tools/git/test-pr-review-gitea-comment.sh" }, "dependencies": { "@mosaicstack/brain": "workspace:*",