Upgrade zip to 9.0.0 and update plugin archive extraction (#54761)

## Why

Curated plugin archives should preserve executable permissions without applying setuid, setgid, or sticky bits.

## What changed

- Upgrade `zip` from 2.4.2 to 9.0.0, retaining the previous feature set and selecting the existing `bzip2-sys` backend explicitly. Update the Cargo and Bazel lockfiles.
- Adapt plugin extraction to fallible entry names and apply Unix permissions through the open output file, using the mode filtered by `zip`.

## Testing

Add a Unix regression test that encodes special permission bits directly in an archive entry and verifies that the extracted file has mode `0o755`.

GitOrigin-RevId: 75ee6879a372a36b34208c1359f42b670c9d3837
This commit is contained in:
Adam Perry @ OpenAI
2026-10-17 02:13:08 +00:00
committed by copyberry
parent e16b631d08
commit df8990e261
5 changed files with 177 additions and 119 deletions
+12 -7
View File
File diff suppressed because one or more lines are too long
+113 -85
View File
@@ -44,10 +44,21 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b169f7a6d4742236a0a00c541b845991d0ac43e546831af1249753ab4c3aa3a0"
dependencies = [
"cfg-if",
"cipher",
"cipher 0.4.4",
"cpufeatures 0.2.17",
]
[[package]]
name = "aes"
version = "0.9.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "35f0f96ce78e38c3dc6d8948aa8163d06385be74000f3c7a95bf1eef35d3ea32"
dependencies = [
"cipher 0.5.2",
"cpubits",
"cpufeatures 0.3.0",
]
[[package]]
name = "aes-gcm"
version = "0.10.3"
@@ -55,8 +66,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "831010a0f742e1209b3bcea8fab6a8e149051ba6099432c8cb2cc117dec3ead1"
dependencies = [
"aead",
"aes",
"cipher",
"aes 0.8.4",
"cipher 0.4.4",
"ctr",
"ghash",
"subtle",
@@ -72,7 +83,7 @@ dependencies = [
"base64 0.22.1",
"bech32",
"chacha20poly1305",
"cipher",
"cipher 0.4.4",
"cookie-factory",
"hkdf 0.12.4",
"hmac 0.12.1",
@@ -333,15 +344,6 @@ dependencies = [
"num-traits",
]
[[package]]
name = "arbitrary"
version = "1.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c3d036a3c4ab069c7b410a2ce876bd74808d2d0888a82667669f8e783a898bf1"
dependencies = [
"derive_arbitrary",
]
[[package]]
name = "arboard"
version = "3.6.1"
@@ -1362,7 +1364,7 @@ dependencies = [
"arrayvec",
"cc",
"cfg-if",
"constant_time_eq",
"constant_time_eq 0.3.1",
"digest 0.10.7",
"rayon-core",
]
@@ -1383,6 +1385,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cdd35008169921d80bc60d3d0ab416eecb028c4cd653352907921d95084790be"
dependencies = [
"hybrid-array 0.4.12",
"zeroize",
]
[[package]]
@@ -1535,11 +1538,12 @@ dependencies = [
[[package]]
name = "bzip2"
version = "0.5.2"
version = "0.6.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "49ecfb22d906f800d4fe833b6282cf4dc1c298f5057ca0b5445e5c209735ca47"
checksum = "f3a53fac24f34a81bc9954b5d6cfce0c21e18ec6959f44f56e8e90e4bb7c346c"
dependencies = [
"bzip2-sys",
"libbz2-rs-sys",
]
[[package]]
@@ -1619,7 +1623,7 @@ version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "26b52a9543ae338f279b96b0b9fed9c8093744685043739079ce85cd58f289a6"
dependencies = [
"cipher",
"cipher 0.4.4",
]
[[package]]
@@ -1641,7 +1645,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9ae3c82e4355234767756212c570e29833699ab63e6ffd161887314cc5b43847"
dependencies = [
"aead",
"cipher",
"cipher 0.4.4",
"ctr",
"subtle",
]
@@ -1690,7 +1694,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c3613f74bd2eac03dad61bd53dbe620703d4371614fe0bc3b9f04dd36fe4e818"
dependencies = [
"cfg-if",
"cipher",
"cipher 0.4.4",
"cpufeatures 0.2.17",
]
@@ -1713,7 +1717,7 @@ checksum = "10cd79432192d1c0f4e1a0fef9527696cc039165d729fb41b3f4f4f354c2dc35"
dependencies = [
"aead",
"chacha20 0.9.1",
"cipher",
"cipher 0.4.4",
"poly1305",
"zeroize",
]
@@ -1762,10 +1766,20 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "773f3b9af64447d2ce9850330c473515014aa235e6a783b02db81ff39e4a3dad"
dependencies = [
"crypto-common 0.1.7",
"inout",
"inout 0.1.4",
"zeroize",
]
[[package]]
name = "cipher"
version = "0.5.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e8cf2a2c93cd704877c0858356ed03480ff301ee950b43f1cbe4573b088bfa6c"
dependencies = [
"crypto-common 0.2.2",
"inout 0.2.2",
]
[[package]]
name = "clang-sys"
version = "1.8.1"
@@ -5332,7 +5346,7 @@ dependencies = [
"base64 0.22.1",
"clap",
"codex-utils-absolute-path",
"constant_time_eq",
"constant_time_eq 0.3.1",
"hmac 0.12.1",
"http 1.4.0",
"jsonwebtoken",
@@ -5553,6 +5567,12 @@ version = "0.9.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c2459377285ad874054d797f3ccebf984978aa39129f6eafde5cdc8315b612f8"
[[package]]
name = "const-oid"
version = "0.10.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a6ef517f0926dd24a1582492c791b6a4818a4d94e789a334894aa15b0d12f55c"
[[package]]
name = "const_format"
version = "0.2.35"
@@ -5579,6 +5599,12 @@ version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7c74b8349d32d297c9134b8c88677813a227df8f779daa29bfc29c183fe3dca6"
[[package]]
name = "constant_time_eq"
version = "0.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3d52eff69cd5e647efe296129160853a42795992097e8af39800e1060caeea9b"
[[package]]
name = "convert_case"
version = "0.6.0"
@@ -5761,6 +5787,12 @@ dependencies = [
"windows-core 0.62.2",
]
[[package]]
name = "cpubits"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "15b85f9c39137c3a891689859392b1bd49812121d0d61c9caf00d46ed5ce06ae"
[[package]]
name = "cpufeatures"
version = "0.2.17"
@@ -5947,7 +5979,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b9d6cf87adf719ddf43a805e92c6870a531aedda35ff640442cbaf8674e141e1"
dependencies = [
"aead",
"cipher",
"cipher 0.4.4",
"generic-array",
"poly1305",
"salsa20",
@@ -6018,7 +6050,7 @@ version = "0.9.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0369ee1ad671834580515889b80f2ea915f23b8be8d0daa4bbaf2ac5c7590835"
dependencies = [
"cipher",
"cipher 0.4.4",
]
[[package]]
@@ -6238,7 +6270,7 @@ version = "4.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "708b509edf7889e53d7efb0ffadd994cc6c2345ccb62f55cfd6b0682165e4fa6"
dependencies = [
"aes",
"aes 0.8.4",
"block-padding",
"cbc",
"dbus",
@@ -6353,7 +6385,7 @@ version = "0.7.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e7c1832837b905bbfb5101e07cc24c8deddf52f93225eee6ead5f4d63d53ddcb"
dependencies = [
"const-oid",
"const-oid 0.9.6",
"pem-rfc7468",
"zeroize",
]
@@ -6407,17 +6439,6 @@ dependencies = [
"syn 1.0.109",
]
[[package]]
name = "derive_arbitrary"
version = "1.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1e567bd82dcff979e4b03460c307b3cdc9e96fde3d73bed1496d2bc75d9dd62a"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.117",
]
[[package]]
name = "derive_more"
version = "1.0.0"
@@ -6497,7 +6518,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292"
dependencies = [
"block-buffer 0.10.4",
"const-oid",
"const-oid 0.9.6",
"crypto-common 0.1.7",
"subtle",
]
@@ -6509,8 +6530,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f1dd6dbb5841937940781866fa1281a1ff7bd3bf827091440879f9994983d5c2"
dependencies = [
"block-buffer 0.12.0",
"const-oid 0.10.2",
"crypto-common 0.2.2",
"ctutils",
"zeroize",
]
[[package]]
@@ -7525,11 +7548,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "899def5c37c4fd7b2664648c28120ecec138e4d395b459e5ca34f9cce2dd77fd"
dependencies = [
"cfg-if",
"js-sys",
"libc",
"r-efi 5.3.0",
"wasip2",
"wasm-bindgen",
]
[[package]]
@@ -9355,6 +9376,15 @@ dependencies = [
"generic-array",
]
[[package]]
name = "inout"
version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4250ce6452e92010fdf7268ccc5d14faa80bb12fc741938534c58f16804e03c7"
dependencies = [
"hybrid-array 0.4.12",
]
[[package]]
name = "insta"
version = "1.46.3"
@@ -9791,6 +9821,12 @@ version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "09edd9e8b54e49e587e4f6295a7d29c3ea94d469cb40ab8ca70b288248a81db2"
[[package]]
name = "libbz2-rs-sys"
version = "0.2.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "34b357333733e8260735ba5894eb928c02ecc69c78715f01a8019e7fa7f2db4c"
[[package]]
name = "libc"
version = "0.2.186"
@@ -10012,24 +10048,12 @@ dependencies = [
]
[[package]]
name = "lzma-rs"
version = "0.3.0"
name = "lzma-rust2"
version = "0.21.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "297e814c836ae64db86b36cf2a557ba54368d03f6afcd7d947c266692f71115e"
checksum = "fde178a3caf126c440fa15628147772d8ee590a39477d711353fbe2e58a73a5b"
dependencies = [
"byteorder",
"crc",
]
[[package]]
name = "lzma-sys"
version = "0.1.20"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5fda04ab3764e6cde78b9974eec4f779acaba7c4e84b36eca3cf77c581b85d27"
dependencies = [
"cc",
"libc",
"pkg-config",
"sha2 0.11.0",
]
[[package]]
@@ -11339,6 +11363,16 @@ dependencies = [
"hmac 0.12.1",
]
[[package]]
name = "pbkdf2"
version = "0.13.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "112d82ceb8c5bf524d9af484d4e4970c9fd5a0cc15ba14ad93dccd28873b0629"
dependencies = [
"digest 0.11.3",
"hmac 0.13.0",
]
[[package]]
name = "pem"
version = "3.0.6"
@@ -13077,7 +13111,7 @@ version = "0.20.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "03a1be8a58a72a257c233ad9a2f92130c15994e02f7a1981379fdebbe0d8761a"
dependencies = [
"aes",
"aes 0.8.4",
"bytecheck",
"byteorder",
"bytes",
@@ -13232,7 +13266,7 @@ version = "0.20.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dae32667b8c9f15223172ce27471e02791e2b401de559b3ea02a6ec93ff280e0"
dependencies = [
"aes",
"aes 0.8.4",
"aes-gcm",
"bitflags 1.3.2",
"bytes",
@@ -13254,7 +13288,7 @@ version = "0.20.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "413d48daef797c98c12ff91988abac36d44c81974f0a4b0658549174230819cd"
dependencies = [
"aes",
"aes 0.8.4",
"byteorder",
"bytes",
"ctr",
@@ -13535,7 +13569,7 @@ version = "0.10.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "97a22f5af31f73a954c10289c93e8a50cc23d971e80ee446f1f6f7137a088213"
dependencies = [
"cipher",
"cipher 0.4.4",
]
[[package]]
@@ -13701,7 +13735,7 @@ version = "0.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0516a385866c09368f0b5bcd1caff3366aace790fcd46e2bb032697bb172fd1f"
dependencies = [
"pbkdf2",
"pbkdf2 0.12.2",
"salsa20",
"sha2 0.10.9",
]
@@ -13750,7 +13784,7 @@ version = "4.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e4d35ad99a181be0a60ffcbe85d680d98f87bdc4d7644ade319b87076b9dbfd4"
dependencies = [
"aes",
"aes 0.8.4",
"cbc",
"futures-util",
"generic-array",
@@ -15458,6 +15492,7 @@ checksum = "743bd48c283afc0388f9b8827b976905fb217ad9e647fae3a379a9283c4def2c"
dependencies = [
"deranged",
"itoa",
"js-sys",
"libc",
"num-conv",
"num_threads",
@@ -16144,6 +16179,12 @@ dependencies = [
"rustc-hash",
]
[[package]]
name = "typed-path"
version = "0.12.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8e28f89b80c87b8fb0cf04ab448d5dd0dd0ade2f8891bae878de66a75a28600e"
[[package]]
name = "typeid"
version = "1.0.3"
@@ -17661,15 +17702,6 @@ version = "0.13.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "66fee0b777b0f5ac1c69bb06d361268faafa61cd4682ae064a171c16c433e9e4"
[[package]]
name = "xz2"
version = "0.1.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "388c44dc09d76f1536602ead6d325eb532f5c122f17782bd57fb47baeeb767e2"
dependencies = [
"lzma-sys",
]
[[package]]
name = "yansi"
version = "1.0.1"
@@ -17869,29 +17901,25 @@ dependencies = [
[[package]]
name = "zip"
version = "2.4.2"
version = "9.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fabe6324e908f85a1c52063ce7aa26b68dcb7eb6dbc83a2d148403c9bc3eba50"
checksum = "711e8c9646791c49b7eb799a19c4ae70a9b8372ddc6a1fa4778defd1e2aa5d5e"
dependencies = [
"aes",
"arbitrary",
"aes 0.9.3",
"bzip2",
"constant_time_eq",
"constant_time_eq 0.4.2",
"crc32fast",
"crossbeam-utils",
"deflate64",
"displaydoc",
"flate2",
"getrandom 0.3.4",
"hmac 0.12.1",
"getrandom 0.4.2",
"hmac 0.13.0",
"indexmap 2.14.0",
"lzma-rs",
"lzma-rust2",
"memchr",
"pbkdf2",
"sha1 0.10.6",
"thiserror 2.0.18",
"pbkdf2 0.13.0",
"sha1 0.11.0",
"time",
"xz2",
"typed-path",
"zeroize",
"zopfli",
"zstd",
+12 -1
View File
@@ -551,7 +551,18 @@ whoami = "1.6.1"
wildmatch = "2.6.1"
winapi-util = "0.1.11"
windows-sys = "0.61.2"
zip = "2.4.2"
# Keep the zip 2.x feature set. In zip 9, `bzip2-rs` selects the bzip2-sys
# backend we already build; the default selects libbz2-rs-sys instead.
zip = { version = "9.0.0", default-features = false, features = [
"aes-crypto",
"bzip2-rs",
"deflate",
"deflate64",
"lzma",
"time",
"xz",
"zstd",
] }
zstd = "0.13"
wiremock = "0.6"
+18 -26
View File
@@ -1047,9 +1047,11 @@ fn extract_zipball_to_dir(bytes: &[u8], destination: &Path) -> Result<(), String
.by_index(index)
.map_err(|err| format!("failed to read curated plugins zip entry: {err}"))?;
let Some(relative_path) = entry.enclosed_name() else {
let name = entry
.name()
.map_err(|err| format!("invalid curated plugins zip entry {index}: {err}"))?;
return Err(format!(
"curated plugins zip entry `{}` escapes extraction root",
entry.name()
"curated plugins zip entry `{name}` escapes extraction root"
));
};
@@ -1099,35 +1101,25 @@ fn extract_zipball_to_dir(bytes: &[u8], destination: &Path) -> Result<(), String
output_path.display()
)
})?;
apply_zip_permissions(&entry, &output_path)?;
#[cfg(unix)]
if let Some(mode) = entry.unix_mode() {
use std::os::unix::fs::PermissionsExt;
// zip strips setuid, setgid, and sticky bits from the archive's Unix mode.
output
.set_permissions(std::fs::Permissions::from_mode(mode))
.map_err(|err| {
format!(
"failed to set permissions on curated plugins file {}: {err}",
output_path.display()
)
})?;
}
}
Ok(())
}
#[cfg(unix)]
fn apply_zip_permissions(entry: &zip::read::ZipFile<'_>, output_path: &Path) -> Result<(), String> {
use std::os::unix::fs::PermissionsExt;
let Some(mode) = entry.unix_mode() else {
return Ok(());
};
std::fs::set_permissions(output_path, std::fs::Permissions::from_mode(mode)).map_err(|err| {
format!(
"failed to set permissions on curated plugins file {}: {err}",
output_path.display()
)
})
}
#[cfg(not(unix))]
fn apply_zip_permissions(
_entry: &zip::read::ZipFile<'_>,
_output_path: &Path,
) -> Result<(), String> {
Ok(())
}
#[cfg(test)]
#[path = "startup_sync_tests.rs"]
mod tests;
@@ -1231,6 +1231,28 @@ async fn sync_openai_plugins_repo_via_http_cleans_up_staged_dir_on_extract_failu
assert!(!has_plugins_clone_dirs(tmp.path()));
}
/// Plugin executables keep their access permissions, but not setuid, setgid, or sticky bits.
#[cfg(unix)]
#[test]
fn extract_zipball_preserves_only_unix_access_permissions() {
use std::io::Cursor;
use std::os::unix::fs::PermissionsExt;
let destination = tempdir().expect("tempdir");
let mut archive = ZipWriter::new(Cursor::new(Vec::new()));
// unix_permissions() strips special bits on write; encode the untrusted mode directly.
let options = SimpleFileOptions::default().external_attributes(0o107755 << 16);
archive
.start_file("repo/script", options)
.expect("add script");
let bytes = archive.finish().expect("finish archive").into_inner();
extract_zipball_to_dir(&bytes, destination.path()).expect("extract archive");
let script = std::fs::metadata(destination.path().join("script")).expect("script metadata");
assert_eq!(script.permissions().mode() & 0o7777, 0o755);
}
#[tokio::test]
async fn sync_openai_plugins_repo_skips_archive_download_when_sha_matches() {
let tmp = tempdir().expect("tempdir");