diff --git a/MODULE.bazel b/MODULE.bazel index 06405ca6fb..8b2726b8dd 100644 --- a/MODULE.bazel +++ b/MODULE.bazel @@ -217,6 +217,7 @@ archive_override( "//patches:rules_rs_build_script_deps_annotation.patch", "//patches:rules_rs_rust_debug_modes.patch", "//patches:rules_rs_workspace_package_version.patch", + "//patches:rules_rs_zlib_snapshot_urls.patch", ], strip_prefix = "rules_rs-7a05511772a8a85db5382ff7536bf2f1e6a8dde3", urls = ["https://github.com/hermeticbuild/rules_rs/archive/7a05511772a8a85db5382ff7536bf2f1e6a8dde3.tar.gz"], diff --git a/codex-rs/.cargo/audit.toml b/codex-rs/.cargo/audit.toml index 04183859b4..5a0ebedb48 100644 --- a/codex-rs/.cargo/audit.toml +++ b/codex-rs/.cargo/audit.toml @@ -10,6 +10,12 @@ ignore = [ "RUSTSEC-2025-0141", # bincode via syntect; remove when syntect drops or updates it "RUSTSEC-2026-0118", # hickory-proto via rama-dns/rama-tcp; remove when rama updates to hickory 0.26.1 or hickory-net "RUSTSEC-2026-0119", # hickory-proto via rama-dns/rama-tcp; remove when rama updates to hickory 0.26.1 or hickory-net + # TODO(ioanbudea): after upgrading Rust to 1.96+, upgrade Rama to use + # hickory-proto >=0.26.2 and remove these temporary exceptions. + "RUSTSEC-2026-0487", # hickory-proto 0.25.2 via rama-dns/rama-tcp; remove after the Rust/Rama upgrade to hickory-proto >=0.26.2 + "RUSTSEC-2026-0489", # hickory-proto 0.25.2 via rama-dns/rama-tcp; remove after the Rust/Rama upgrade to hickory-proto >=0.26.2 + "RUSTSEC-2026-0490", # hickory-proto 0.25.2 via rama-dns/rama-tcp; remove after the Rust/Rama upgrade to hickory-proto >=0.26.2 + "RUSTSEC-2026-0491", # hickory-proto 0.25.2 via rama-dns/rama-tcp; remove after the Rust/Rama upgrade to hickory-proto >=0.26.2 "RUSTSEC-2026-0194", # quick-xml via plist/syntect and wayland-scanner; trusted inputs only; remove when rust-plist#191 and wayland-rs#938 are released "RUSTSEC-2026-0195", # quick-xml via plist/syntect and wayland-scanner; trusted inputs only; remove when rust-plist#191 and wayland-rs#938 are released "RUSTSEC-2026-0370", # zvariant 4.2.0 via zbus 4.4.0 in Linux TUI and keyring/secret-service; remove when those paths migrate off the retired standalone crates diff --git a/codex-rs/deny.toml b/codex-rs/deny.toml index a2f5825c24..ac92c62c0f 100644 --- a/codex-rs/deny.toml +++ b/codex-rs/deny.toml @@ -81,6 +81,12 @@ ignore = [ { id = "RUSTSEC-2025-0141", reason = "bincode is unmaintained; pulled in via syntect v5.3.0 used by codex-tui for syntax highlighting; no fixed release yet" }, { id = "RUSTSEC-2026-0118", reason = "hickory-proto v0.25.2 is pulled in via rama-dns/rama-tcp used by codex-network-proxy; DNSSEC features are not enabled; remove when rama updates to hickory 0.26.1 or hickory-net" }, { id = "RUSTSEC-2026-0119", reason = "hickory-proto v0.25.2 is pulled in via rama-dns/rama-tcp used by codex-network-proxy; no fixed rama release is available yet; remove when rama updates to hickory 0.26.1 or hickory-net" }, + # TODO(ioanbudea): after upgrading Rust to 1.96+, upgrade Rama to use + # hickory-proto >=0.26.2 and remove these temporary exceptions. + { id = "RUSTSEC-2026-0487", reason = "hickory-proto 0.25.2 via rama-dns/rama-tcp in codex-network-proxy; temporarily accepted until the Rust toolchain and Rama upgrade brings hickory-proto >=0.26.2" }, + { id = "RUSTSEC-2026-0489", reason = "hickory-proto 0.25.2 via rama-dns/rama-tcp in codex-network-proxy; temporarily accepted until the Rust toolchain and Rama upgrade brings hickory-proto >=0.26.2" }, + { id = "RUSTSEC-2026-0490", reason = "hickory-proto 0.25.2 via rama-dns/rama-tcp in codex-network-proxy; temporarily accepted until the Rust toolchain and Rama upgrade brings hickory-proto >=0.26.2" }, + { id = "RUSTSEC-2026-0491", reason = "hickory-proto 0.25.2 via rama-dns/rama-tcp in codex-network-proxy; temporarily accepted until the Rust toolchain and Rama upgrade brings hickory-proto >=0.26.2" }, { id = "RUSTSEC-2026-0194", reason = "quick-xml v0.39.4 remains via plist/syntect and wayland-scanner; plist does not exercise the affected APIs and wayland-scanner parses trusted build-time XML; remove when rust-plist#191 and wayland-rs#938 are released" }, { id = "RUSTSEC-2026-0195", reason = "quick-xml v0.39.4 remains via plist/syntect and wayland-scanner; plist does not exercise the affected APIs and wayland-scanner parses trusted build-time XML; remove when rust-plist#191 and wayland-rs#938 are released" }, { id = "RUSTSEC-2026-0370", reason = "zvariant 4.2.0 is unmaintained; pulled in via zbus 4.4.0 by Linux TUI accessibility integration and keyring/secret-service; remove when those paths migrate off the retired standalone crates" }, diff --git a/codex-rs/ext/history-notes/src/tools_tests.rs b/codex-rs/ext/history-notes/src/tools_tests.rs index ea5429770e..67d3ee5d62 100644 --- a/codex-rs/ext/history-notes/src/tools_tests.rs +++ b/codex-rs/ext/history-notes/src/tools_tests.rs @@ -182,6 +182,7 @@ fn sparse_catalog_messages_preserve_defaults_and_explicit_empty_descriptions() { #[test] fn bundled_tool_contracts() { - let specs = super::HistoryNotesAction::ALL.map(|action| action.spec(None, None)); + let specs = super::HistoryNotesAction::ALL + .map(|action| action.spec(/*history_messages*/ None, /*notes_messages*/ None)); insta::assert_snapshot!(serde_json::to_string_pretty(&specs).unwrap()); } diff --git a/codex-rs/utils/path-utils/src/lib.rs b/codex-rs/utils/path-utils/src/lib.rs index 028e4dea76..118876d183 100644 --- a/codex-rs/utils/path-utils/src/lib.rs +++ b/codex-rs/utils/path-utils/src/lib.rs @@ -161,7 +161,13 @@ pub fn write_atomically(write_path: &Path, contents: &str) -> io::Result<()> { std::fs::create_dir_all(parent)?; let mut tmp = NamedTempFile::new_in(parent)?; tmp.write_all(contents.as_bytes())?; - tmp.persist(write_path)?; + // Match credential-file replacement: std::fs::rename supports overlapping + // readers on Windows, while tempfile::persist's MoveFileExW does not. + // Clear the temporary attribute and retain cleanup if publication fails. + let (_file, path) = tmp.keep().map_err(|error| error.error)?; + let mut path = tempfile::TempPath::try_from_path(path)?; + std::fs::rename(&path, write_path)?; + path.disable_cleanup(/*disable_cleanup*/ true); Ok(()) } diff --git a/codex-rs/windows-sandbox-rs/src/unified_exec/tests.rs b/codex-rs/windows-sandbox-rs/src/unified_exec/tests.rs index f3c43cee8d..d69aa0f632 100644 --- a/codex-rs/windows-sandbox-rs/src/unified_exec/tests.rs +++ b/codex-rs/windows-sandbox-rs/src/unified_exec/tests.rs @@ -28,6 +28,7 @@ use std::os::windows::io::FromRawHandle; use std::os::windows::io::OwnedHandle; use std::path::Path; use std::path::PathBuf; +use std::process::Stdio; use std::sync::Arc; use std::sync::Mutex; use std::sync::MutexGuard; @@ -41,9 +42,17 @@ use tokio::sync::broadcast; use tokio::sync::mpsc; use tokio::sync::oneshot; use tokio::time::timeout; +use windows_sys::Win32::Foundation::HANDLE_FLAG_INHERIT; +use windows_sys::Win32::Foundation::INVALID_HANDLE_VALUE; +use windows_sys::Win32::Foundation::SetHandleInformation; use windows_sys::Win32::Foundation::WAIT_FAILED; use windows_sys::Win32::Foundation::WAIT_OBJECT_0; use windows_sys::Win32::Foundation::WAIT_TIMEOUT; +use windows_sys::Win32::System::Console::GetConsoleWindow; +use windows_sys::Win32::System::Console::GetStdHandle; +use windows_sys::Win32::System::Console::STD_ERROR_HANDLE; +use windows_sys::Win32::System::Console::STD_INPUT_HANDLE; +use windows_sys::Win32::System::Console::STD_OUTPUT_HANDLE; use windows_sys::Win32::System::Threading::OpenProcess; use windows_sys::Win32::System::Threading::PROCESS_SYNCHRONIZE; use windows_sys::Win32::System::Threading::WaitForSingleObject; @@ -51,6 +60,10 @@ use windows_sys::Win32::System::Threading::WaitForSingleObject; static TEST_HOME_COUNTER: AtomicU64 = AtomicU64::new(0); static LEGACY_PROCESS_TEST_LOCK: Mutex<()> = Mutex::new(()); +const LEGACY_FIXTURE_ROLE: &str = "CODEX_TEST_LEGACY_PROCESS_ROLE"; +const LEGACY_FIXTURE_TEST: &str = + "unified_exec::tests::legacy_capture_emits_output_and_preserves_descendant_after_normal_exit"; + const ASSERT_NO_CONSOLE: &str = r#"Add-Type -ErrorAction Stop 'using System; using System.Runtime.InteropServices; public class ConsoleProbe { [DllImport("kernel32.dll")] public static extern IntPtr GetConsoleWindow(); }'; if ([ConsoleProbe]::GetConsoleWindow() -ne [IntPtr]::Zero) { throw 'piped sandbox process unexpectedly has a console' };"#; fn legacy_process_test_guard() -> MutexGuard<'static, ()> { @@ -135,6 +148,17 @@ fn wait_for_path(path: &Path, timeout: Duration) -> bool { path.exists() } +async fn wait_for_path_async(path: &Path, wait: Duration) -> bool { + // Let the single-threaded runtime continue driving the process session. + timeout(wait, async { + while !path.exists() { + tokio::time::sleep(Duration::from_millis(25)).await; + } + }) + .await + .is_ok() +} + fn open_process_for_wait(pid: u32) -> std::io::Result { let handle = unsafe { OpenProcess(PROCESS_SYNCHRONIZE, 0, pid) }; if handle.is_null() { @@ -662,49 +686,91 @@ fn runner_resizer_sends_resize_frame() { }); } +fn run_legacy_process_fixture(role: &str) { + let cwd = std::env::current_dir().expect("fixture cwd"); + let ready = cwd.join("descendant-started"); + if role == "child" { + assert!(unsafe { GetConsoleWindow() }.is_null()); + let temporary = cwd.join("descendant-started.tmp"); + fs::write(&temporary, std::process::id().to_string()).expect("write child PID"); + fs::rename(temporary, ready).expect("publish child PID"); + assert!(wait_for_path( + &cwd.join("release-descendant"), + Duration::from_secs(30) + )); + fs::write(cwd.join("descendant-survived"), "survived").expect("write survival marker"); + return; + } + assert!(matches!(role, "capture" | "preserve" | "terminate")); + if role == "capture" { + assert!(unsafe { GetConsoleWindow() }.is_null()); + } + // This is an isolated fixture process. Do not let the child retain the + // root's capture pipes or ConPTY handles alongside its explicit file stdio. + // Otherwise EOF cannot arrive until the child that our caller releases exits. + for id in [STD_INPUT_HANDLE, STD_OUTPUT_HANDLE, STD_ERROR_HANDLE] { + let handle = unsafe { GetStdHandle(id) }; + if !handle.is_null() && handle != INVALID_HANDLE_VALUE { + assert_ne!( + unsafe { SetHandleInformation(handle, HANDLE_FLAG_INHERIT, 0) }, + 0 + ); + } + } + let child = codex_utils_process::background_command( + std::env::current_exe().expect("fixture executable"), + ) + .args(["--exact", LEGACY_FIXTURE_TEST, "--nocapture"]) + .env(LEGACY_FIXTURE_ROLE, "child") + .stdin(Stdio::null()) + .stdout(fs::File::create(cwd.join("descendant.stdout")).expect("child stdout")) + .stderr(fs::File::create(cwd.join("descendant.stderr")).expect("child stderr")) + .spawn() + .expect("spawn fixture descendant"); + drop(child); // Closing a process handle must not terminate the descendant. + assert!(wait_for_path(&ready, Duration::from_secs(10))); + println!("LEGACY-CAPTURE-DIRECT"); + if role == "terminate" { + std::thread::sleep(Duration::from_secs(30)); + } +} + #[test] fn legacy_capture_emits_output_and_preserves_descendant_after_normal_exit() { - let Some(pwsh) = pwsh_path() else { + if let Ok(role) = std::env::var(LEGACY_FIXTURE_ROLE) { + run_legacy_process_fixture(&role); return; - }; + } let _guard = legacy_process_test_guard(); - let cwd = sandbox_cwd(); - let codex_home = sandbox_home("legacy-capture-pwsh"); - println!("capture pwsh codex_home={}", codex_home.path().display()); - let ready_marker = codex_home.path().join("descendant-started"); - let release_marker = codex_home.path().join("release-descendant"); - let survival_marker = codex_home.path().join("descendant-survived"); - let descendant_command = format!( - "$deadline=(Get-Date).AddSeconds(30); Set-Content -LiteralPath '{}' -Value $PID; while (-not (Test-Path -LiteralPath '{}')) {{ if ((Get-Date) -ge $deadline) {{ exit 3 }}; Start-Sleep -Milliseconds 25 }}; Set-Content -LiteralPath '{}' -Value survived", - powershell_literal(&ready_marker), - powershell_literal(&release_marker), - powershell_literal(&survival_marker), - ); - let parent_tail = format!( - "while (-not (Test-Path -LiteralPath '{}')) {{ Start-Sleep -Milliseconds 25 }}", - powershell_literal(&ready_marker), - ); - let parent_command = format!( - "{ASSERT_NO_CONSOLE} Write-Output LEGACY-CAPTURE-DIRECT; {}", - start_powershell_child(&pwsh, codex_home.path(), &descendant_command, &parent_tail,), - ); + // Child-owned markers and redirected output belong in the writable workspace, + // not the protected Codex home used for sandbox control state. + let workspace = tempfile::tempdir().expect("create sandbox workspace"); + let cwd = workspace.path().to_path_buf(); + let codex_home = sandbox_home("legacy-capture-native"); + println!("capture native codex_home={}", codex_home.path().display()); + let ready_marker = cwd.join("descendant-started"); + let release_marker = cwd.join("release-descendant"); + let survival_marker = cwd.join("descendant-survived"); let permission_profile = PermissionProfile::workspace_write(); let result = run_windows_sandbox_capture( &permission_profile, workspace_roots_for(cwd.as_path()).as_slice(), codex_home.path(), vec![ - pwsh.display().to_string(), - "-NoProfile".to_string(), - "-Command".to_string(), - parent_command, + std::env::current_exe() + .expect("fixture executable") + .display() + .to_string(), + "--exact".to_string(), + LEGACY_FIXTURE_TEST.to_string(), + "--nocapture".to_string(), ], cwd.as_path(), - HashMap::new(), + HashMap::from([(LEGACY_FIXTURE_ROLE.to_string(), "capture".to_string())]), Some(10_000), /*cancellation*/ None, ) - .expect("run legacy capture powershell"); + .expect("run legacy capture fixture"); let descendant_pid = fs::read_to_string(&ready_marker) .expect("read descendant pid") .trim() @@ -714,11 +780,11 @@ fn legacy_capture_emits_output_and_preserves_descendant_after_normal_exit() { fs::write(&release_marker, "release").expect("release descendant after root exit"); let descendant_process = descendant_process.expect("open descendant after normal capture exit"); - println!("capture pwsh exit_code={}", result.exit_code); - println!("capture pwsh timed_out={}", result.timed_out); + println!("capture native exit_code={}", result.exit_code); + println!("capture native timed_out={}", result.timed_out); let stdout = String::from_utf8_lossy(&result.stdout); let stderr = String::from_utf8_lossy(&result.stderr); - println!("capture pwsh stderr={stderr:?}"); + println!("capture native stderr={stderr:?}"); assert_eq!(result.exit_code, 0, "stdout={stdout:?} stderr={stderr:?}"); assert!( stdout.contains("LEGACY-CAPTURE-DIRECT"), @@ -852,10 +918,13 @@ fn legacy_capture_cancellation_terminates_descendants_without_timeout() { return; }; let _guard = legacy_process_test_guard(); - let cwd = sandbox_cwd(); + // Child-owned markers and redirected output belong in the writable workspace, + // not the protected Codex home used for sandbox control state. + let workspace = tempfile::tempdir().expect("create sandbox workspace"); + let cwd = workspace.path().to_path_buf(); let codex_home = sandbox_home("legacy-capture-cancel"); - let descendant_marker = codex_home.path().join("descendant-survived"); - let ready_marker = codex_home.path().join("descendant-started"); + let descendant_marker = cwd.join("descendant-survived"); + let ready_marker = cwd.join("descendant-started"); let descendant_command = format!( "Set-Content -LiteralPath '{}' -Value $PID; Start-Sleep -Seconds 1; Set-Content -LiteralPath '{}' -Value survived", powershell_literal(&ready_marker), @@ -863,7 +932,7 @@ fn legacy_capture_cancellation_terminates_descendants_without_timeout() { ); let parent_command = start_powershell_child( &pwsh, - codex_home.path(), + cwd.as_path(), &descendant_command, "Start-Sleep -Seconds 30", ); @@ -933,62 +1002,48 @@ enum LegacyTtyDescendantLifecycle { Preserve, } -async fn assert_legacy_tty_descendant_lifecycle( - pwsh: &Path, - lifecycle: LegacyTtyDescendantLifecycle, -) { - let cwd = sandbox_cwd(); +async fn assert_legacy_tty_descendant_lifecycle(lifecycle: LegacyTtyDescendantLifecycle) { + // Child-owned markers and redirected output belong in the writable workspace, + // not the protected Codex home used for sandbox control state. + let workspace = tempfile::tempdir().expect("create sandbox workspace"); + let cwd = workspace.path().to_path_buf(); let codex_home = sandbox_home(match lifecycle { LegacyTtyDescendantLifecycle::Terminate => "legacy-tty-descendant-terminate", LegacyTtyDescendantLifecycle::Preserve => "legacy-tty-descendant-preserve", }); - let ready_marker = codex_home.path().join("descendant-started"); - let release_marker = codex_home.path().join("release-descendant"); - let survival_marker = codex_home.path().join("descendant-survived"); - let child_tail = match lifecycle { - LegacyTtyDescendantLifecycle::Terminate => "Start-Sleep -Seconds 30".to_string(), - LegacyTtyDescendantLifecycle::Preserve => format!( - "$deadline=(Get-Date).AddSeconds(30); while (-not (Test-Path -LiteralPath '{}')) {{ if ((Get-Date) -ge $deadline) {{ exit 3 }}; Start-Sleep -Milliseconds 25 }}; Set-Content -LiteralPath '{}' -Value survived", - powershell_literal(&release_marker), - powershell_literal(&survival_marker), - ), + let ready_marker = cwd.join("descendant-started"); + let release_marker = cwd.join("release-descendant"); + let survival_marker = cwd.join("descendant-survived"); + let role = match lifecycle { + LegacyTtyDescendantLifecycle::Terminate => "terminate", + LegacyTtyDescendantLifecycle::Preserve => "preserve", }; - let child_command = format!( - "Set-Content -LiteralPath '{}' -Value $PID; {child_tail}", - powershell_literal(&ready_marker), - ); - let parent_tail = match lifecycle { - LegacyTtyDescendantLifecycle::Terminate => "Start-Sleep -Seconds 30".to_string(), - LegacyTtyDescendantLifecycle::Preserve => format!( - "while (-not (Test-Path -LiteralPath '{}')) {{ Start-Sleep -Milliseconds 25 }}", - powershell_literal(&ready_marker), - ), - }; - let parent_command = - start_powershell_child(pwsh, codex_home.path(), &child_command, &parent_tail); let permission_profile = PermissionProfile::workspace_write(); let spawned = spawn_windows_sandbox_session_legacy( &permission_profile, workspace_roots_for(cwd.as_path()).as_slice(), codex_home.path(), vec![ - pwsh.display().to_string(), - "-NoProfile".to_string(), - "-Command".to_string(), - parent_command, + std::env::current_exe() + .expect("fixture executable") + .display() + .to_string(), + "--exact".to_string(), + LEGACY_FIXTURE_TEST.to_string(), + "--nocapture".to_string(), ], cwd.as_path(), - HashMap::new(), + HashMap::from([(LEGACY_FIXTURE_ROLE.to_string(), role.to_string())]), Some(30_000), &[], &[], /*tty*/ true, - /*stdin_open*/ false, + /*stdin_open*/ true, ) .await .expect("spawn legacy sandbox ConPTY lifecycle test"); assert!( - wait_for_path(&ready_marker, Duration::from_secs(10)), + wait_for_path_async(&ready_marker, Duration::from_secs(10)).await, "{lifecycle:?} descendant did not start" ); let descendant_pid = fs::read_to_string(&ready_marker) @@ -1013,7 +1068,7 @@ async fn assert_legacy_tty_descendant_lifecycle( LegacyTtyDescendantLifecycle::Preserve => { assert_eq!(exit_code, 0); assert!( - wait_for_path(&survival_marker, Duration::from_secs(10)), + wait_for_path_async(&survival_marker, Duration::from_secs(10)).await, "sandbox ConPTY descendant did not survive normal exit" ); } @@ -1024,15 +1079,10 @@ async fn assert_legacy_tty_descendant_lifecycle( #[test] fn legacy_tty_job_terminates_and_preserves_descendants() { - let Some(pwsh) = pwsh_path() else { - eprintln!("skipping sandbox ConPTY lifecycle test: PowerShell 7 is not installed"); - return; - }; let _guard = legacy_process_test_guard(); current_thread_runtime().block_on(async move { - assert_legacy_tty_descendant_lifecycle(&pwsh, LegacyTtyDescendantLifecycle::Terminate) - .await; - assert_legacy_tty_descendant_lifecycle(&pwsh, LegacyTtyDescendantLifecycle::Preserve).await; + assert_legacy_tty_descendant_lifecycle(LegacyTtyDescendantLifecycle::Terminate).await; + assert_legacy_tty_descendant_lifecycle(LegacyTtyDescendantLifecycle::Preserve).await; }); } diff --git a/patches/BUILD.bazel b/patches/BUILD.bazel index 6fad1f052b..bbd272c4b2 100644 --- a/patches/BUILD.bazel +++ b/patches/BUILD.bazel @@ -18,6 +18,7 @@ exports_files([ "rules_rs_build_script_deps_annotation.patch", "rules_rs_rust_debug_modes.patch", "rules_rs_workspace_package_version.patch", + "rules_rs_zlib_snapshot_urls.patch", "rusty_v8_prebuilt_out_dir.patch", "ring_windows_msvc_include_dirs.patch", "tikv-jemalloc-sys_make_env.patch", diff --git a/patches/rules_rs_zlib_snapshot_urls.patch b/patches/rules_rs_zlib_snapshot_urls.patch new file mode 100644 index 0000000000..a7f0a2d609 --- /dev/null +++ b/patches/rules_rs_zlib_snapshot_urls.patch @@ -0,0 +1,33 @@ +# Backport the Linux zlib package pins and mirrors from rules_rs v0.2.2. +# https://github.com/hermeticbuild/rules_rs/commit/4c3d6ecc8fe94d373b818366ac3c52a31fffa277 + +diff --git a/rs/private/rustc_repository.bzl b/rs/private/rustc_repository.bzl +--- a/rs/private/rustc_repository.bzl ++++ b/rs/private/rustc_repository.bzl +@@ -56,18 +56,20 @@ filegroup( + _LINUX_ZLIB = { + "aarch64": struct( + libdir = "usr/lib/aarch64-linux-gnu", +- sha256 = "e55ab3b4fb7b6edc4a628e28ec13f8780b08abe427d33ae148f030d8586d0e9a", ++ sha256 = "84536bcf440cf0f988899b24b6c7df32cb9ff9f0dfa97d5e7efcc2dc088d15dc", + urls = [ +- "https://launchpad.net/ubuntu/+archive/primary/+files/zlib1g_1.3.dfsg-3.1ubuntu2.2_arm64.deb", +- "https://snapshot.ubuntu.com/ubuntu/20260901T000000Z/pool/main/z/zlib/zlib1g_1.3.dfsg-3.1ubuntu2.2_arm64.deb", ++ "https://ports.ubuntu.com/ubuntu-ports/pool/main/z/zlib/zlib1g_1.3.dfsg-3.1ubuntu2.3_arm64.deb", ++ "https://launchpad.net/ubuntu/+archive/primary/+files/zlib1g_1.3.dfsg-3.1ubuntu2.3_arm64.deb", ++ "https://snapshot.ubuntu.com/ubuntu/20261110T000000Z/pool/main/z/zlib/zlib1g_1.3.dfsg-3.1ubuntu2.3_arm64.deb", + ], + ), + "x86_64": struct( + libdir = "usr/lib/x86_64-linux-gnu", +- sha256 = "84b9cf5752b29c9f92c27cd4c4ba9bbcc70b5ccf9b1b515421a28ae23212e273", ++ sha256 = "376249c8de139568221dbb242318cc8cb8f02299e3a62b7432948e1754fbddf9", + urls = [ +- "https://launchpad.net/ubuntu/+archive/primary/+files/zlib1g_1.3.dfsg-3.1ubuntu2.2_amd64.deb", +- "https://snapshot.ubuntu.com/ubuntu/20260901T000000Z/pool/main/z/zlib/zlib1g_1.3.dfsg-3.1ubuntu2.2_amd64.deb", ++ "https://archive.ubuntu.com/ubuntu/pool/main/z/zlib/zlib1g_1.3.dfsg-3.1ubuntu2.3_amd64.deb", ++ "https://launchpad.net/ubuntu/+archive/primary/+files/zlib1g_1.3.dfsg-3.1ubuntu2.3_amd64.deb", ++ "https://snapshot.ubuntu.com/ubuntu/20261110T000000Z/pool/main/z/zlib/zlib1g_1.3.dfsg-3.1ubuntu2.3_amd64.deb", + ], + ), + }