From 76cbe18633fa4fcb5b9fbb56b7184c337e718e11 Mon Sep 17 00:00:00 2001 From: Yernat Yestekov <2068106+doublewhy@users.noreply.github.com> Date: Sat, 10 Oct 2026 03:55:35 -0700 Subject: [PATCH 1/3] build(tooling): pin maintained-client downloads to HTTP/1.1 curl_transfer_argv set no HTTP version, so curl negotiated HTTP/2 with the download hosts. Over HTTP/2, macOS /usr/bin/curl 8.7.1 exits 56 for a size-limit breach, which run_curl_transfer does not classify as the enforced limit (63), and it does not retry a 503. Qualification only exercised HTTP/1.1, because its http.server fixture offers no ALPN protocol. Pass --http1.1 in the shared argv. The fixture now offers h2 before http/1.1 through ALPN, as the download hosts do, so a client that accepted h2 fails the real-curl tests. --- .../python/tests/test_issue_1217_bootstrap_profiles.py | 4 ++++ tools/maintained_client_acquisition.py | 3 +++ 2 files changed, 7 insertions(+) diff --git a/implementations/python/tests/test_issue_1217_bootstrap_profiles.py b/implementations/python/tests/test_issue_1217_bootstrap_profiles.py index ab7512a63..24b6a6577 100644 --- a/implementations/python/tests/test_issue_1217_bootstrap_profiles.py +++ b/implementations/python/tests/test_issue_1217_bootstrap_profiles.py @@ -196,6 +196,7 @@ def test_curl_qualification_uses_fixed_hardened_argv() -> None: assert argv[:2] == ["/usr/bin/curl", "--disable"] assert argv[argv.index("--proto") : argv.index("--proto") + 2] == ["--proto", "=https"] assert argv[argv.index("--proto-redir") : argv.index("--proto-redir") + 2] == ["--proto-redir", "=https"] + assert [arg for arg in argv if arg.startswith("--http")] == ["--http1.1"] assert "--insecure" not in argv assert "--location-trusted" not in argv assert "--retry-all-errors" not in argv @@ -859,6 +860,9 @@ def _https_fixture(tmp_path: Path) -> tuple[ThreadingHTTPServer, Path]: context = ssl.SSLContext(ssl.PROTOCOL_TLS_SERVER) context.minimum_version = ssl.TLSVersion.TLSv1_2 context.load_cert_chain(cert_path, key_path) + # Offer HTTP/2 first, as the download hosts do (#1475). http.server speaks only HTTP/1.x, so a client that + # accepted h2 would fail every transfer here. + context.set_alpn_protocols(["h2", "http/1.1"]) server.socket = context.wrap_socket(server.socket, server_side=True) threading.Thread(target=server.serve_forever, daemon=True).start() return server, cert_path diff --git a/tools/maintained_client_acquisition.py b/tools/maintained_client_acquisition.py index 1235768ed..92276980a 100644 --- a/tools/maintained_client_acquisition.py +++ b/tools/maintained_client_acquisition.py @@ -141,6 +141,9 @@ def curl_transfer_argv( "=https", "--proto-redir", "=https", + # Qualification covers HTTP/1.1. Over HTTP/2, macOS curl 8.7.1 exits 56 for a size + # breach and does not retry a 503 (#1475). + "--http1.1", "--max-redirs", "5", "--retry", From 7d2f81d6631cbddf495b6b173e0ec46929d71d23 Mon Sep 17 00:00:00 2001 From: Yernat Yestekov <2068106+doublewhy@users.noreply.github.com> Date: Sat, 10 Oct 2026 04:21:15 -0700 Subject: [PATCH 2/3] build(tooling): re-pin the maintained client in the proof evidence The participant-opacity proof manifest binds each source that acquires the prover by digest, tools/maintained_client_acquisition.py included. Record the digest of the file with the --http1.1 argv. The proof lane, the participant-opacity proof tests and the dev-container smoke failed on the old digest. --- .../participant-opacity-proof-evidence.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/specs/formal/participant-semantics/participant-opacity-proof-evidence.json b/specs/formal/participant-semantics/participant-opacity-proof-evidence.json index c45029c26..434a2f029 100644 --- a/specs/formal/participant-semantics/participant-opacity-proof-evidence.json +++ b/specs/formal/participant-semantics/participant-opacity-proof-evidence.json @@ -301,7 +301,7 @@ }, { "path": "tools/maintained_client_acquisition.py", - "digest": "sha256:b8a538c975f054216cb54cb3f68163aa5dab642bc368a94db5d343732a67101a" + "digest": "sha256:0529ca32a87d33aeca570d659dec8d12d7f3227d38726077a39f344f7cbf2dc0" }, { "path": "tools/tooling_installed_tree.py", From 578c6fecab6197b49c635af060bd2b5db9006649 Mon Sep 17 00:00:00 2001 From: Yernat Yestekov <2068106+doublewhy@users.noreply.github.com> Date: Sat, 10 Oct 2026 06:37:23 -0700 Subject: [PATCH 3/3] test(tooling): say most download hosts offer h2 first The Cambridge Isabelle mirror (www.cl.cam.ac.uk) negotiates only http/1.1; the other download hosts in the tooling artifact lock offer h2. --- .../python/tests/test_issue_1217_bootstrap_profiles.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/implementations/python/tests/test_issue_1217_bootstrap_profiles.py b/implementations/python/tests/test_issue_1217_bootstrap_profiles.py index 24b6a6577..2d8bcdffa 100644 --- a/implementations/python/tests/test_issue_1217_bootstrap_profiles.py +++ b/implementations/python/tests/test_issue_1217_bootstrap_profiles.py @@ -860,7 +860,7 @@ def _https_fixture(tmp_path: Path) -> tuple[ThreadingHTTPServer, Path]: context = ssl.SSLContext(ssl.PROTOCOL_TLS_SERVER) context.minimum_version = ssl.TLSVersion.TLSv1_2 context.load_cert_chain(cert_path, key_path) - # Offer HTTP/2 first, as the download hosts do (#1475). http.server speaks only HTTP/1.x, so a client that + # Offer HTTP/2 first, as most download hosts do (#1475). http.server speaks only HTTP/1.x, so a client that # accepted h2 would fail every transfer here. context.set_alpn_protocols(["h2", "http/1.1"]) server.socket = context.wrap_socket(server.socket, server_side=True)