fix(deps): upgrade russh and replace vulnerable zstd codecs (#8969)

* chore(deps): upgrade russh to 0.65.0 and refresh crates

Run two cargo update and cargo upgrade rounds from origin/main.
Adapt SFTP public-key callbacks to PublicKeyOffer and VerifiedPublicKey,
preserving explicit rejection and password-only authentication.
Cover the rejection policy with a pinned-host-key SSH handshake test.
Refresh Arrow, Parquet, Brotli, ZIP, and compatible dependencies.

Co-Authored-By: heihutu <heihutu@gmail.com>
Co-Authored-By: zhi22915 <qiuzgang@gmail.com>

* fix(deps): route legacy zstd consumers to fixed codecs

Replace the old registry implementation with a private version-constraint
adapter backed only by zstd 0.14.3 and zstd-safe 8.2.0. Preserve legacy
features and Avro, Arrow IPC, Parquet, and Kafka formats.
Cover abandoned contexts, legacy decode errors, seekable bounds, stored
frames, consumer interoperability, and resolved implementation versions.
Record the adapter removal condition in the compatibility register.

Co-Authored-By: heihutu <heihutu@gmail.com>
Co-Authored-By: zhi22915 <qiuzgang@gmail.com>

---------

Co-authored-by: heihutu <heihutu@gmail.com>
Co-authored-by: zhi22915 <qiuzgang@gmail.com>
This commit is contained in:
Hauser
2026-10-27 02:05:58 +08:00
committed by GitHub
parent fa0a056bc1
commit d4d416ed47
10 changed files with 672 additions and 89 deletions
Generated
+164 -82
View File
@@ -365,9 +365,9 @@ checksum = "d3fb67a6e08acf24fdeccbac2cb6ac4305825bd1f117462e0e6f2f193345ad56"
[[package]]
name = "arrow"
version = "59.3.1"
version = "59.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4c25317ebddb8f2e6f006759336c95e5ded847a446f3524c27853bfe7d77a69b"
checksum = "3f04e0a89cc175eaf3740282537851e6fd9d7276f97730b9d0ea2859c11a85df"
dependencies = [
"arrow-arith",
"arrow-array",
@@ -386,9 +386,9 @@ dependencies = [
[[package]]
name = "arrow-arith"
version = "59.3.1"
version = "59.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2ab555c0ffe9cb817041c0cee80baa9663a22225d0a035693579771f2693cadc"
checksum = "157943c720919e8f5b6731407ce5cd4c9a5ef47a75bf10edaf24b7081b5763ef"
dependencies = [
"arrow-array",
"arrow-buffer",
@@ -400,9 +400,9 @@ dependencies = [
[[package]]
name = "arrow-array"
version = "59.3.1"
version = "59.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2dae1d91737421e92c6dd7c7f9d0f2d25198fd6fc539738b628f4dad10adf533"
checksum = "72b5b1682924f11ecf6929689c16cbc69efbb6dd141b06ad98ee1cb10d858c43"
dependencies = [
"ahash",
"arrow-buffer",
@@ -419,9 +419,9 @@ dependencies = [
[[package]]
name = "arrow-buffer"
version = "59.3.1"
version = "59.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1fe9cd0064f2d7cf8ecfaf77cd70e67a980bc71aea92b63102bd37a7d3379a09"
checksum = "b5d687afc6c927965a8a759f3b68de91933d9dfa2e1bb6e156bac84b85665e92"
dependencies = [
"bytes",
"half",
@@ -431,9 +431,9 @@ dependencies = [
[[package]]
name = "arrow-cast"
version = "59.3.1"
version = "59.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ee510bc252781d700400979a96bd1eadebeae23901132bbabd92118da1b376c5"
checksum = "3f23c3379feede42135ef27ca60e67f6e643cd4343ae74e4c8b16c83f55afd85"
dependencies = [
"arrow-array",
"arrow-buffer",
@@ -453,9 +453,9 @@ dependencies = [
[[package]]
name = "arrow-csv"
version = "59.3.1"
version = "59.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ace83d5935421b950fea3a0a3f9972c0837c297ef98ade57127a5661b681112c"
checksum = "85b1984628b091b8319b3c305fce21d04f9e9b2578bb733b0d0108462ae1d698"
dependencies = [
"arrow-array",
"arrow-cast",
@@ -468,9 +468,9 @@ dependencies = [
[[package]]
name = "arrow-data"
version = "59.3.1"
version = "59.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "54f128c73bb7fe9f7137218f910edd5b436f390e0f040b1956f5028390787897"
checksum = "bb0a031c7ff5e2bdb443117054fb466af1d4df5b7f0ecbe197713dc8b113b1ba"
dependencies = [
"arrow-buffer",
"arrow-schema",
@@ -481,9 +481,9 @@ dependencies = [
[[package]]
name = "arrow-ipc"
version = "59.3.1"
version = "59.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8e244f39b82fa7a823e8b5984a0bd988234f9c92c1df79378fb85090e6e4c821"
checksum = "f5dd1afadda2eb821aaf851bb4c0adef60c30cc0f1a29bd02fb99dd07fb9f58d"
dependencies = [
"arrow-array",
"arrow-buffer",
@@ -497,9 +497,9 @@ dependencies = [
[[package]]
name = "arrow-json"
version = "59.3.1"
version = "59.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "727b21ebf8894dcd9504d6ab66bffdac338391e1f85cde594526a0210299ea40"
checksum = "e2bc4d56a0dad84cc51c5812ccff48b642047a1f3be80349cd84318983e79edb"
dependencies = [
"arrow-array",
"arrow-buffer",
@@ -522,9 +522,9 @@ dependencies = [
[[package]]
name = "arrow-ord"
version = "59.3.1"
version = "59.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "70d376b9b883239ae0f9963cdeeb59126ccaac08d60adf208a6ad3e2dd4048c3"
checksum = "561b8209faa2cd489764b7fd07d7cb9e8d274d78048c38de59f77c41140cb834"
dependencies = [
"arrow-array",
"arrow-buffer",
@@ -535,9 +535,9 @@ dependencies = [
[[package]]
name = "arrow-row"
version = "59.3.1"
version = "59.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "48096811f92b5c3a0c59d33cfecd7d7d2ed037d81e54a27a42d232e7700e6de2"
checksum = "0d8c68b87a569f92b90c46cb0a09d86bd9609ddc9b1bf1fc5a503c8c6907c14e"
dependencies = [
"arrow-array",
"arrow-buffer",
@@ -548,9 +548,9 @@ dependencies = [
[[package]]
name = "arrow-schema"
version = "59.3.1"
version = "59.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "37b6c8a6b12cf3869d299ce36ed8e1cd6fe9836264b68fb6c6538473a36124a1"
checksum = "e87acb0c6bf6237fca7a50d7b51e801c98a18a5fe6f3bb34f7d77b85ca165388"
dependencies = [
"serde_core",
"serde_json",
@@ -558,9 +558,9 @@ dependencies = [
[[package]]
name = "arrow-select"
version = "59.3.1"
version = "59.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e523e85fa9fb3181a391bf0edfa6a1ae0dc46df8cbf7f6b96034dcb1e2a7a04a"
checksum = "c844141958d805b47d1208e0d12aac53fcf388ce8011639fe82aa492adde0bb4"
dependencies = [
"ahash",
"arrow-array",
@@ -572,9 +572,9 @@ dependencies = [
[[package]]
name = "arrow-string"
version = "59.3.1"
version = "59.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b931a905a0e4b8832d9f54fb369cf6e545f9dc03cb3d9611216a7bd2653df60a"
checksum = "bc130892b1a9552da77971924b390e06cf375d1f7e4344a7b7b40ca4426fa470"
dependencies = [
"arrow-array",
"arrow-buffer",
@@ -1514,6 +1514,23 @@ dependencies = [
"serde",
]
[[package]]
name = "bindgen"
version = "0.73.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "787ef8ef523575546b106a58213d6e6b06198a05c2f757258c68a74273670cfa"
dependencies = [
"bitflags 2.13.2",
"cexpr",
"clang-sys",
"proc-macro2",
"quote",
"regex",
"rustc-hash",
"shlex",
"syn 3.0.9",
]
[[package]]
name = "binrw"
version = "0.15.2"
@@ -1726,13 +1743,13 @@ dependencies = [
[[package]]
name = "brotli"
version = "9.0.0"
version = "9.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f8b851b75c23ca7873623d612fe49bd1989aeb03d08fb9432187eb253d3d4c6b"
checksum = "6737ce8b81460bf9407909f73ff50bf13f7cb8577e1e76715f57ed0eba58ba44"
dependencies = [
"alloc-no-stdlib 3.0.0",
"alloc-stdlib 0.3.0",
"brotli-decompressor 6.0.1",
"brotli-decompressor 6.2.0",
]
[[package]]
@@ -1747,9 +1764,9 @@ dependencies = [
[[package]]
name = "brotli-decompressor"
version = "6.0.1"
version = "6.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "941cd9bd4ddab83cb46fa5a2d428f1c857b24ac78cb876cf7beb710840934bd7"
checksum = "34c6783f42c21c952ad7112b354f0ba2f10760e9decea5b1b60523d6e270faaf"
dependencies = [
"alloc-no-stdlib 3.0.0",
"alloc-stdlib 0.3.0",
@@ -1967,6 +1984,15 @@ dependencies = [
"shlex",
]
[[package]]
name = "cexpr"
version = "0.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6fac387a98bb7c37292057cffc56d62ecb629900026402633ae9160df93a8766"
dependencies = [
"nom 7.1.3",
]
[[package]]
name = "cfg-if"
version = "1.0.5"
@@ -2083,6 +2109,17 @@ dependencies = [
"zeroize",
]
[[package]]
name = "clang-sys"
version = "1.9.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "157a8ba7b480713b56f4c09fd13fc3e0a22a5dfab8097ba61cbc5feef950788a"
dependencies = [
"glob",
"libc",
"libloading",
]
[[package]]
name = "clap"
version = "4.6.7"
@@ -2199,7 +2236,7 @@ version = "0.4.46"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cae36683101b2899f48bd31f8db9839437356188e3b1ff23096e73dab09ba8fe"
dependencies = [
"brotli 9.0.0",
"brotli 9.1.0",
"bzip2",
"compression-core",
"flate2",
@@ -2207,7 +2244,7 @@ dependencies = [
"lz4",
"memchr",
"zstd 0.14.3",
"zstd-safe 8.2.0",
"zstd-safe",
]
[[package]]
@@ -3632,9 +3669,9 @@ dependencies = [
[[package]]
name = "der"
version = "0.8.2"
version = "0.8.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a878c850e9e421b20262e9b41f9c860e4785fa07541c266b62ff9d1ef998a80a"
checksum = "6a996a108feb8b427a6e12fcf6af4aa83409b79d72c15337d7741368131ee004"
dependencies = [
"const-oid 0.10.2",
"pem-rfc7468 1.0.0",
@@ -4067,7 +4104,7 @@ version = "0.17.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c0681a4fc24c767085329728d8dfba959af91228aa4610cca4f8ce317ba46ae0"
dependencies = [
"der 0.8.2",
"der 0.8.3",
"digest 0.11.3",
"elliptic-curve",
"rfc6979",
@@ -4132,9 +4169,9 @@ checksum = "0e9c71c2167ca323c882b99918929403426e2373ea17242ff5653e0d5e1058be"
[[package]]
name = "elliptic-curve"
version = "0.14.2"
version = "0.14.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0ad4496221fcfc21015fd8c0e26fcfab00504832dc0604f52acdd7bad2a8dc81"
checksum = "900f37e7e18d74a2347389af6cd69e3a30d5cc9e697f40004865679aa75e9c7c"
dependencies = [
"base16ct",
"crypto-bigint",
@@ -5236,9 +5273,9 @@ dependencies = [
[[package]]
name = "hotpath"
version = "0.29.1"
version = "0.29.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "62f2351902fa259b758c7303505273393f6b08172ab5bdd5739a2d2bc6d554e8"
checksum = "5b4af2b5ecf2650278fe33b0e49b68c8c484d2306475c127628316dea740cf5c"
dependencies = [
"arc-swap",
"async-channel",
@@ -5271,15 +5308,15 @@ dependencies = [
[[package]]
name = "hotpath-drain"
version = "0.29.1"
version = "0.29.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "83dab2340134d9976d62f9f5172f9c6584c7f521b092f258e0e58e412e160b53"
checksum = "a5cceb6c15fbaaeb00b51acc7be7fad533d0b56cb422b1cfc16cf9ca1624d33a"
[[package]]
name = "hotpath-macros"
version = "0.29.1"
version = "0.29.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a04bbe5701b097f224a08187a78b75bb9d9b4bbaff6a279c2a50ba73edbb7aac"
checksum = "08c0a42eb299ebb7ec6b9161489e5bf132603a34ca12cecadb8490937aa0492a"
dependencies = [
"proc-macro2",
"quote",
@@ -6399,7 +6436,7 @@ dependencies = [
"hashbrown 0.16.1",
"indexmap 2.14.2",
"metrics",
"ordered-float 5.5.0",
"ordered-float 5.5.1",
"quanta",
"radix_trie",
"rand 0.9.5",
@@ -7333,9 +7370,9 @@ dependencies = [
[[package]]
name = "ordered-float"
version = "5.5.0"
version = "5.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8c7c9e0d9b23589f26070720bac724174bfec1083e82f7854cdd0267518343c0"
checksum = "7f59006ce1e204c30956638535b74d317bca99477b9356dfaec54c37f6f600ab"
dependencies = [
"num-traits",
]
@@ -7452,9 +7489,9 @@ dependencies = [
[[package]]
name = "pageant"
version = "0.2.4"
version = "0.2.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f0786409b5aaa884655c9b4065681db0835db0cc185a6fb19fb17a5d38656866"
checksum = "e0bc922d9aa8cfcf598de27b3c6122cb2eeda9d6fe71527a22ebcb7f633540ff"
dependencies = [
"base16ct",
"byteorder",
@@ -7523,9 +7560,9 @@ dependencies = [
[[package]]
name = "parquet"
version = "59.3.1"
version = "59.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8b3d2ef0fb136e9341570f219ecb168902e76079c6d81ad555f0a4840b1e08a3"
checksum = "8eb260d566f2fbdabe356d26aaa5d13403aac29b5b4e44463b8f9bd7b2b12680"
dependencies = [
"ahash",
"arrow-array",
@@ -7810,7 +7847,7 @@ version = "0.8.0-rc.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "986d2e952779af96ea048f160fd9194e1751b4faea78bcf3ceb456efe008088e"
dependencies = [
"der 0.8.2",
"der 0.8.3",
"spki 0.8.1",
]
@@ -7853,7 +7890,7 @@ dependencies = [
"aes 0.9.3",
"aes-gcm",
"cbc 0.2.1",
"der 0.8.2",
"der 0.8.3",
"pbkdf2 0.13.0",
"rand_core 0.10.1",
"scrypt 0.12.0",
@@ -7877,7 +7914,7 @@ version = "0.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "451913da69c775a56034ea8d9003d27ee8948e12443eae7c038ba100a4f21cb7"
dependencies = [
"der 0.8.2",
"der 0.8.3",
"pkcs5 0.8.1",
"rand_core 0.10.1",
"spki 0.8.1",
@@ -9139,9 +9176,9 @@ dependencies = [
[[package]]
name = "russh"
version = "0.64.1"
version = "0.65.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ba61e87b9ec9a39a59a6bbed4c0b8ff7fb07073405b24a767a1f2c8560ac8f58"
checksum = "3bb77a4b259312b2abbc541bca1c13cb4e9b61c560a7fd894068c3b92e0c9dc3"
dependencies = [
"aes 0.9.3",
"aws-lc-rs",
@@ -9156,7 +9193,7 @@ dependencies = [
"curve25519-dalek 5.0.0",
"data-encoding",
"delegate",
"der 0.8.2",
"der 0.8.3",
"digest 0.11.3",
"ecdsa",
"ed25519-dalek 3.0.0",
@@ -10847,7 +10884,7 @@ version = "1.0.2"
dependencies = [
"base64-simd",
"blake2",
"brotli 9.0.0",
"brotli 9.1.0",
"bytes",
"convert_case 0.12.0",
"crc-fast",
@@ -11308,7 +11345,7 @@ checksum = "d56d437c2f19203ce5f7122e507831de96f3d2d4d3be5af44a0b0a09d8a80e4d"
dependencies = [
"base16ct",
"ctutils",
"der 0.8.2",
"der 0.8.3",
"hybrid-array",
"subtle",
"zeroize",
@@ -11474,6 +11511,15 @@ dependencies = [
"syn 3.0.9",
]
[[package]]
name = "serde_spanned"
version = "1.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4e7523beb55eece201a2356bee0bbca0d1ab466c14c07703b2e0ee6d42cb0c2c"
dependencies = [
"serde_core",
]
[[package]]
name = "serde_urlencoded"
version = "0.7.1"
@@ -11753,9 +11799,9 @@ checksum = "33f4fe9184a62d842c9ef383018f3306d8ba224fd9d836f56d7288308847c256"
[[package]]
name = "sketches-ddsketch"
version = "0.3.1"
version = "0.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0c6f73aeb92d671e0cc4dca167e59b2deb6387c375391bc99ee743f326994a2b"
checksum = "45f82fa313d625dd72aaa65ec0c9f8c95407add0510c1bef3c18ea7257ec34ce"
[[package]]
name = "slab"
@@ -11884,7 +11930,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7ef958a98b9d5da290cfc78946e9f3e61e1e62a18db0d92cac0b83cc161491a9"
dependencies = [
"base64ct",
"der 0.8.2",
"der 0.8.3",
]
[[package]]
@@ -12633,6 +12679,30 @@ dependencies = [
"webpki-roots 0.26.11",
]
[[package]]
name = "toml"
version = "0.9.12+spec-1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cf92845e79fc2e2def6a5d828f0801e29a2f8acc037becc5ab08595c7d5e9863"
dependencies = [
"indexmap 2.14.2",
"serde_core",
"serde_spanned",
"toml_datetime 0.7.5+spec-1.1.0",
"toml_parser",
"toml_writer",
"winnow 0.7.15",
]
[[package]]
name = "toml_datetime"
version = "0.7.5+spec-1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "92e1cfed4a3038bc5a127e35a2d360f145e1f4b971b551a2ba5fd7aedf7e1347"
dependencies = [
"serde_core",
]
[[package]]
name = "toml_datetime"
version = "1.1.2+spec-1.1.0"
@@ -12649,9 +12719,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e3641d5bbb5349a79e1020a242d251efbc546ad8048d133958323ce9c40a9c9c"
dependencies = [
"indexmap 2.14.2",
"toml_datetime",
"toml_datetime 1.1.2+spec-1.1.0",
"toml_parser",
"winnow",
"winnow 1.0.4",
]
[[package]]
@@ -12660,9 +12730,15 @@ version = "1.1.5+spec-1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "baa693a8032d7e1cada7d0041e96126df243179ff061456783ac7f12bda4744c"
dependencies = [
"winnow",
"winnow 1.0.4",
]
[[package]]
name = "toml_writer"
version = "1.1.3+spec-1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "06bdbd8cfc056b8d2e2e85f29b56a3bdbecb527cef81eb39e3e7b98af4652770"
[[package]]
name = "tonic"
version = "0.14.6"
@@ -13689,6 +13765,12 @@ version = "0.53.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d6bbff5f0aada427a1e5a6da5f1f98158182f26556f345ac9e04d36d0ebed650"
[[package]]
name = "winnow"
version = "0.7.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "df79d97927682d2fd8adb29682d1140b343be4ac0f08fd68b7765d9c059d3945"
[[package]]
name = "winnow"
version = "1.0.4"
@@ -13955,9 +14037,9 @@ dependencies = [
[[package]]
name = "zip"
version = "9.1.0"
version = "9.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2886b00003a72c4ccee3f42cfc34908d33b2508a74d827dc2a4244706475c704"
checksum = "ec2e3161de27091f18a831d9494e9c53378a34833af5c1dda35ad85849da18d6"
dependencies = [
"aes 0.9.3",
"bzip2",
@@ -14009,10 +14091,18 @@ dependencies = [
[[package]]
name = "zstd"
version = "0.13.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e91ee311a569c327171651566e07972200e76fcfe2242a4fa446149a3881c08a"
dependencies = [
"zstd-safe 7.3.0",
"apache-avro",
"arrow-array",
"arrow-ipc",
"arrow-schema",
"bytes",
"kafka-protocol",
"parquet",
"semver",
"toml",
"zstd 0.14.3",
"zstd-safe",
]
[[package]]
@@ -14021,16 +14111,7 @@ version = "0.14.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "db3ae9bf90af6e40197bef767eeefc46ad6397420fe873830bd85e5fbbfb3c70"
dependencies = [
"zstd-safe 8.2.0",
]
[[package]]
name = "zstd-safe"
version = "7.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "64d80649ab6db9d9f6f9c80a40becd948eda4714a0a5ac8c4d157a32231c7882"
dependencies = [
"zstd-sys",
"zstd-safe",
]
[[package]]
@@ -14048,6 +14129,7 @@ version = "2.1.2+zstd.1.5.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "481ada9c543b2323cbb7bbdbb5249177fbf0188ed959ed2c15e4b22421c84ed3"
dependencies = [
"bindgen",
"cc",
"pkg-config",
]
+6 -4
View File
@@ -66,6 +66,7 @@ members = [
"crates/io-metrics", # I/O metrics collection for performance analysis
"crates/io-core", # Buffered Bytes, mmap-then-copy, and aligned pread I/O helpers
"crates/zip", # ZIP file handling and compression
"crates/zstd-compat", # Fixed codec for dependencies requiring zstd 0.13
]
resolver = "3"
@@ -260,7 +261,7 @@ aws-smithy-http-client = { default-features = false, version = "1.5.1" }
aws-smithy-runtime-api = { version = "1.19.1" }
aws-smithy-types = { version = "1.9.0" }
base64-simd = "0.8.0"
brotli = "9.0.0"
brotli = "9.1.0"
clap = { version = "4.6.7" }
const-str = { version = "1.1.0" }
convert_case = "0.12.0"
@@ -355,7 +356,7 @@ winapi-util = "0.1.11"
windows = { version = "0.62.2" }
windows-sys = "0.61.2"
xxhash-rust = { version = "0.8.19" }
zip = "9.1.0"
zip = "9.1.2"
zstd = "0.14.3"
# Observability and Metrics
@@ -376,7 +377,7 @@ libunftp = { version = "0.23.1" }
unftp-core = "0.1.0"
suppaftp = { version = "12.2.1" }
rcgen = { version = "0.14.10", default-features = false, features = ["aws_lc_rs", "crypto", "pem"] }
russh = { version = "0.64.1" }
russh = { version = "0.65.0" }
russh-sftp = "3.0.1"
# WebDAV
@@ -385,7 +386,7 @@ dav-server = "0.11.0"
# Performance Analysis and Memory Profiling
rustfs-mimalloc = { version = "0.7.2" }
# Preserve Unicode focus filters until rustfs/backlog#2302 is resolved.
hotpath = { version = "0.29.1", default-features = false }
hotpath = { version = "0.29.2", default-features = false }
# Snapshot testing for output format regression detection
insta = { version = "1.49" }
@@ -425,6 +426,7 @@ strip = "none"
# Pin upstream fixes until compatible crates.io releases are available.
[patch.crates-io]
zstd = { path = "crates/zstd-compat" }
# Preserve RustFS OIDC flows while migrating the old elliptic-curve dependency chain.
# Upstream: openidconnect-rs#244 and oauth2-rs#352.
openidconnect = { git = "https://github.com/ramosbugs/openidconnect-rs", rev = "2f77ee73bc4554e1cb6279adb386005429fb4e41" }
+77 -3
View File
@@ -639,7 +639,7 @@ mod hot_reload_tests {
format!("{d}BEGIN {l}{d}\n{body}\n{d}END {l}{d}\n", d = PEM_BOUNDARY_DASHES, l = PEM_OPENSSH_LABEL,)
}
fn test_ed25519_pem() -> String {
pub(super) fn test_ed25519_pem() -> String {
build_pem_block(
"b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAAAMwAAAAtzc2gtZW\n\
QyNTUxOQAAACCkeMEUpnJEbOMBXiQfjZcHZMEbHW3DlNRL+Jbi1cIqMgAAAKDviRiQ74kY\n\
@@ -1010,7 +1010,7 @@ impl<S: StorageBackend + Send + Sync + 'static> russh::server::Handler for SshSe
fn auth_publickey_offered(
&mut self,
user: &str,
_key: &keys::PublicKey,
_key: russh::server::PublicKeyOffer<'_>,
) -> impl std::future::Future<Output = Result<Auth, Self::Error>> + Send {
async { Ok(Auth::reject()) }
}
@@ -1019,7 +1019,7 @@ impl<S: StorageBackend + Send + Sync + 'static> russh::server::Handler for SshSe
fn auth_publickey(
&mut self,
user: &str,
_key: &keys::PublicKey,
_key: russh::server::VerifiedPublicKey<'_>,
) -> impl std::future::Future<Output = Result<Auth, Self::Error>> + Send {
async { Ok(Auth::reject()) }
}
@@ -1594,6 +1594,80 @@ mod tests {
);
}
#[cfg(unix)]
#[tokio::test]
async fn ssh_handshake_rejects_public_key_authentication() {
use crate::common::dummy_storage::DummyBackend;
use russh::client::{self, AuthResult};
use russh::keys::{PrivateKeyWithHashAlg, PublicKeyOrCertificate};
struct PinnedHostKey(keys::PublicKey);
impl client::Handler for PinnedHostKey {
type Error = russh::Error;
async fn check_server_key(&mut self, offered: &PublicKeyOrCertificate) -> Result<bool, Self::Error> {
Ok(matches!(offered, PublicKeyOrCertificate::PublicKey { key, .. } if key.key_data() == self.0.key_data()))
}
}
timeout(Duration::from_secs(30), async {
let key = PrivateKey::from_openssh(super::hot_reload_tests::test_ed25519_pem()).expect("decode test SSH key");
let trusted_host_key = key.public_key().clone();
let config = build_ssh_config(vec![key.clone()], 600, "SSH-2.0-RustFS");
let peer_addr = "127.0.0.1:12345".parse().expect("test peer address");
let handler = SshSessionHandler {
storage: Arc::new(DummyBackend::new()),
peer_addr,
session_context: None,
channels: HashMap::new(),
read_only: false,
part_size: super::super::test_support::TEST_PART_SIZE,
handles_per_session: DEFAULT_HANDLES_PER_SESSION,
backend_op_timeout_secs: DEFAULT_BACKEND_OP_TIMEOUT_SECS,
read_cache_window: READ_CACHE_WINDOW_DEFAULT,
read_cache_total_mem_limit: READ_CACHE_TOTAL_MEM_DEFAULT,
read_cache_in_use: Arc::new(AtomicU64::new(0)),
session_diag: Arc::new(SessionDiag::new(peer_addr, peer_addr)),
};
let (client_stream, server_stream) = tokio::io::duplex(64 * 1024);
let server = tokio::spawn(async move {
let session = russh::server::run_stream(config, server_stream, handler)
.await
.expect("start test SSH session");
// The client deliberately disconnects after the rejected authentication.
let _ = session.await;
});
let mut client =
client::connect_stream(Arc::new(client::Config::default()), client_stream, PinnedHostKey(trusted_host_key))
.await
.expect("complete SSH handshake with the pinned host key");
let auth = client
.authenticate_publickey("public-key-auth-disabled", PrivateKeyWithHashAlg::new(Arc::new(key), None))
.await
.expect("receive public-key authentication response");
match auth {
AuthResult::Failure {
remaining_methods,
partial_success,
} => {
assert!(!partial_success, "public keys must not grant partial authentication");
assert!(remaining_methods.contains(&MethodKind::Password));
assert!(!remaining_methods.contains(&MethodKind::PublicKey));
}
AuthResult::Success => panic!("public-key authentication must remain disabled"),
}
client
.disconnect(russh::Disconnect::ByApplication, "test complete", "")
.await
.expect("disconnect test client");
drop(client);
server.await.expect("join test SSH server");
})
.await
.expect("SSH authentication rejection must complete within the test deadline");
}
#[test]
fn ssh_config_advertises_only_password() {
let config = build_ssh_config(Vec::new(), 600, "SSH-2.0-RustFS");
+44
View File
@@ -0,0 +1,44 @@
[package]
name = "zstd"
version = "0.13.3"
publish = false
edition.workspace = true
license.workspace = true
rust-version.workspace = true
repository.workspace = true
description = "RustFS internal adapter for zstd 0.13 consumers using the fixed codec"
[dependencies]
zstd_fixed = { package = "zstd", version = "=0.14.3", default-features = false }
[features]
default = ["legacy", "arrays", "zdict_builder"]
arrays = ["zstd_fixed/arrays"]
bindgen = ["zstd_fixed/bindgen"]
debug = ["zstd_fixed/debug"]
doc-cfg = ["zstd_fixed/doc-cfg"]
experimental = ["zstd_fixed/experimental"]
fat-lto = ["zstd_fixed/fat-lto"]
legacy = ["zstd_fixed/legacy"]
no_asm = ["zstd_fixed/no_asm"]
pkg-config = ["zstd_fixed/pkg-config"]
thin = ["zstd_fixed/thin"]
thin-lto = ["zstd_fixed/thin-lto"]
wasm = ["zstd_fixed/wasm"]
zdict_builder = ["zstd_fixed/zdict_builder"]
zstdmt = ["zstd_fixed/zstdmt"]
[dev-dependencies]
zstd-safe = { version = "8.2.0", features = ["seekable"] }
toml = "0.9"
semver = "1.0.27"
apache-avro.workspace = true
arrow-array = "59.3.2"
arrow-ipc = { version = "59.3.2", features = ["zstd"] }
arrow-schema = "59.3.2"
parquet = { version = "59.3.2", default-features = false, features = ["arrow", "zstd"] }
kafka-protocol = { version = "0.18.0", default-features = false, features = ["zstd"] }
bytes.workspace = true
[lints]
workspace = true
+11
View File
@@ -0,0 +1,11 @@
# Fixed zstd dependency adapter
**Use this when:** maintaining the dependency bridge for consumers that still require zstd 0.13.
This private package satisfies the version constraints of Avro, Arrow IPC, Parquet, and Kafka. Its version is a resolver compatibility identifier. Its entire library is a re-export of the exact registry `zstd 0.14.3` implementation, including that implementation's `zstd-safe` types. The original zstd 0.13 implementation and zstd-safe 7.x are removed from the resolved graph. Every old feature and the default feature set is forwarded.
The fixed implementation closes RUSTSEC-2026-0391, RUSTSEC-2026-0398, and RUSTSEC-2026-0403. A successful advisory scan alone is insufficient evidence: cargo-deny skips local path packages. The dependency-graph test therefore requires this adapter's sole runtime dependency to be the exact fixed registry implementation and rejects vulnerable registry codec versions. Security tests exercise abandoned stream contexts, malformed legacy frames, and seekable offsets past EOF. Consumer tests retain zstandard support in all four upstream formats.
Run `cargo nextest run --locked -p zstd@0.13.3` for the focused tests. The seekable feature is enabled by a test-only dependency to cover the optional vulnerable API without enabling it in production.
Remove this package and the root Cargo patch when all consumers accept the fixed zstd release line directly. The cleanup marker is registered in the architecture compatibility register.
+20
View File
@@ -0,0 +1,20 @@
// Copyright 2026 RustFS Team
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//! Internal version-constraint adapter for RustFS's locked zstd consumers.
//!
//! Every codec API and low-level type comes from the fixed registry implementation.
// RUSTFS_COMPAT_TODO(zstd-013-fixed-backend): Bridge dependencies requiring zstd 0.13. Remove after every consumer accepts the fixed zstd line.
pub use zstd_fixed::*;
+135
View File
@@ -0,0 +1,135 @@
// Copyright 2026 RustFS Team
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
use arrow_array::{Int32Array, RecordBatch};
use arrow_schema::{DataType, Field, Schema};
use std::io::Cursor;
use std::sync::Arc;
fn batch() -> RecordBatch {
let schema = Arc::new(Schema::new(vec![Field::new("value", DataType::Int32, false)]));
let values: Vec<i32> = (0..4096).map(|value| value % 7).collect();
RecordBatch::try_new(schema, vec![Arc::new(Int32Array::from(values))]).expect("build compressible fixture")
}
fn assert_values(actual: &RecordBatch, expected: &RecordBatch) {
assert_eq!(actual.num_columns(), 1);
let actual = actual
.column(0)
.as_any()
.downcast_ref::<Int32Array>()
.expect("decoded int32 values");
let expected = expected
.column(0)
.as_any()
.downcast_ref::<Int32Array>()
.expect("fixture int32 values");
assert_eq!(actual, expected);
}
#[test]
fn avro_zstandard_container_preserves_records() {
use apache_avro::{Codec, Reader, Schema, Writer, types::Value};
let schema = Schema::parse_str(r#"{"type":"record","name":"Fixture","fields":[{"name":"value","type":"long"}]}"#)
.expect("parse Avro fixture schema");
let expected: Vec<_> = [0, 1, -1, i64::MAX]
.into_iter()
.map(|value| Value::Record(vec![("value".to_owned(), Value::Long(value))]))
.collect();
let mut writer =
Writer::with_codec(&schema, Vec::new(), Codec::Zstandard(Default::default())).expect("create Avro zstandard writer");
for record in &expected {
writer.append_value_ref(record).expect("append Avro record");
}
let encoded = writer.into_inner().expect("finish Avro zstandard container");
assert!(encoded.windows(b"zstandard".len()).any(|bytes| bytes == b"zstandard"));
let actual = Reader::new(encoded.as_slice())
.expect("open Avro container")
.collect::<Result<Vec<_>, _>>()
.expect("decode Avro records");
assert_eq!(actual, expected);
}
#[test]
fn arrow_ipc_zstd_preserves_multiple_batches_with_reused_contexts() {
use arrow_ipc::{
CompressionType,
reader::FileReader,
writer::{FileWriter, IpcWriteOptions},
};
let expected = batch();
let options = IpcWriteOptions::default()
.try_with_compression(Some(CompressionType::ZSTD))
.expect("enable IPC zstd");
let mut encoded = Vec::new();
{
let mut writer =
FileWriter::try_new_with_options(&mut encoded, expected.schema().as_ref(), options).expect("open IPC writer");
writer.write(&expected).expect("write first compressed batch");
writer.write(&expected).expect("reuse IPC compression context");
writer.finish().expect("finish IPC file");
}
assert!(
encoded.windows(4).any(|bytes| bytes == [0x28, 0xb5, 0x2f, 0xfd]),
"fixture must contain a zstd frame"
);
let reader = FileReader::try_new(Cursor::new(encoded), None).expect("open IPC reader");
let actual = reader.collect::<Result<Vec<_>, _>>().expect("read compressed batches");
assert_eq!(actual.len(), 2);
for actual in actual {
assert_values(&actual, &expected);
}
}
#[test]
fn parquet_zstd_preserves_column_values() {
use parquet::{
arrow::{ArrowWriter, arrow_reader::ParquetRecordBatchReaderBuilder},
basic::{Compression, ZstdLevel},
file::properties::WriterProperties,
};
let expected = batch();
let properties = WriterProperties::builder()
.set_compression(Compression::ZSTD(ZstdLevel::try_new(3).expect("valid level")))
.build();
let mut encoded = Vec::new();
{
let mut writer = ArrowWriter::try_new(&mut encoded, expected.schema(), Some(properties)).expect("open Parquet writer");
writer.write(&expected).expect("write compressed Parquet data");
writer.close().expect("finish Parquet file");
}
let builder = ParquetRecordBatchReaderBuilder::try_new(bytes::Bytes::from(encoded)).expect("open Parquet metadata");
assert!(matches!(builder.metadata().row_group(0).column(0).compression(), Compression::ZSTD(_)));
let mut reader = builder.with_batch_size(4096).build().expect("open Parquet reader");
let actual = reader.next().expect("one batch").expect("decode Parquet values");
assert_values(&actual, &expected);
assert!(reader.next().is_none());
}
#[test]
fn kafka_zstd_preserves_record_payload() {
use bytes::{BufMut, Bytes, BytesMut};
use kafka_protocol::compression::{Compressor, Decompressor, Zstd};
let expected = b"Kafka record payload remains compatible".repeat(32);
let mut encoded = BytesMut::new();
<Zstd as Compressor<BytesMut>>::compress(&mut encoded, |buffer| {
buffer.put_slice(&expected);
Ok(())
})
.expect("compress Kafka records");
let mut encoded = encoded.freeze();
let actual =
<Zstd as Decompressor<Bytes>>::decompress(&mut encoded, |buffer| Ok(buffer.to_vec())).expect("decompress Kafka records");
assert_eq!(actual, expected);
}
@@ -0,0 +1,76 @@
// Copyright 2026 RustFS Team
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
use semver::Version;
use std::path::Path;
#[test]
fn legacy_constraints_resolve_only_to_the_fixed_registry_implementation() {
let directory = Path::new(env!("CARGO_MANIFEST_DIR"));
let manifest: toml::Value =
toml::from_str(&std::fs::read_to_string(directory.join("Cargo.toml")).expect("read adapter manifest"))
.expect("parse adapter manifest");
let dependencies = manifest["dependencies"].as_table().expect("runtime dependency table");
assert_eq!(dependencies.len(), 1, "the adapter must have one canonical codec implementation");
let implementation = &dependencies["zstd_fixed"];
assert_eq!(implementation["package"].as_str(), Some("zstd"));
assert!(implementation.get("path").is_none());
assert!(implementation.get("git").is_none());
let root = directory
.parent()
.expect("crates directory")
.parent()
.expect("workspace directory");
let root_manifest: toml::Value =
toml::from_str(&std::fs::read_to_string(root.join("Cargo.toml")).expect("read workspace manifest"))
.expect("parse workspace manifest");
let patch = root_manifest["patch"]["crates-io"]["zstd"]["path"]
.as_str()
.expect("local zstd patch");
assert_eq!(
root.join(patch).canonicalize().expect("resolve patch"),
directory.canonicalize().expect("resolve adapter")
);
let lock: toml::Value = toml::from_str(&std::fs::read_to_string(root.join("Cargo.lock")).expect("read workspace lockfile"))
.expect("parse workspace lockfile");
let mut adapters = 0;
let mut implementations = 0;
let mut safe_bindings = 0;
for package in lock["package"].as_array().expect("locked packages") {
let name = package["name"].as_str().expect("package name");
if !matches!(name, "zstd" | "zstd-safe") {
continue;
}
let version = Version::parse(package["version"].as_str().expect("package version")).expect("valid package semver");
if name == "zstd" && package.get("source").is_none() {
adapters += 1;
assert_eq!(version, Version::new(0, 13, 3));
let runtime = package["dependencies"].as_array().expect("adapter dependency edges");
assert!(runtime.iter().any(|value| value.as_str() == Some("zstd 0.14.3")));
} else {
let source = package["source"].as_str().expect("registry codec source");
assert!(source.starts_with("registry+"), "codec implementation must remain a registry package");
if name == "zstd" {
implementations += 1;
assert!(version >= Version::new(0, 14, 3), "vulnerable zstd implementation: {version}");
} else {
safe_bindings += 1;
assert!(version >= Version::new(8, 2, 0), "vulnerable zstd-safe implementation: {version}");
}
}
}
assert_eq!((adapters, implementations, safe_bindings), (1, 1, 1));
}
+137
View File
@@ -0,0 +1,137 @@
// Copyright 2026 RustFS Team
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
use std::io::{Read, Write};
use zstd::zstd_safe::{CCtx, DCtx, InBuffer, OutBuffer, ResetDirective};
#[test]
fn abandoned_encoder_cannot_prefix_the_next_request() {
let mut context = CCtx::create();
{
let mut abandoned = zstd::stream::Encoder::with_context(Vec::new(), &mut context);
abandoned
.write_all(b"tenant-A private input")
.expect("buffer the abandoned request");
}
let mut next = zstd::stream::Encoder::with_context(Vec::new(), &mut context);
next.write_all(b"tenant-B public input").expect("encode the next request");
let encoded = next.finish().expect("finish the next request");
assert_eq!(
zstd::decode_all(encoded.as_slice()).expect("decode the next frame"),
b"tenant-B public input"
);
}
#[test]
fn abandoned_decoder_cannot_return_the_previous_request() {
let previous = zstd::encode_all(&b"tenant-A unread private output"[..], 1).expect("encode previous request");
let current = zstd::encode_all(&b"tenant-B output"[..], 1).expect("encode current request");
let mut context = DCtx::create();
{
let mut abandoned = zstd::stream::Decoder::with_context(previous.as_slice(), &mut context);
abandoned.read_exact(&mut [0; 3]).expect("partially read the previous frame");
}
let mut actual = Vec::new();
zstd::stream::Decoder::with_context(current.as_slice(), &mut context)
.read_to_end(&mut actual)
.expect("read the new request independently");
assert_eq!(actual, b"tenant-B output");
}
#[test]
fn corrupt_legacy_frame_keeps_buffer_positions_and_length_unchanged() {
let corrupt = [0x27, 0xb5, 0x2f, 0xfd, 0x08, 0x00];
let mut context = DCtx::create();
let mut destination = Vec::with_capacity(4096);
{
let mut input = InBuffer::around(&corrupt);
let mut output = OutBuffer::around(&mut destination);
assert!(context.decompress_stream(&mut output, &mut input).is_err());
assert_eq!(input.pos(), 0);
assert_eq!(output.pos(), 0);
}
assert!(destination.is_empty());
let valid = zstd::encode_all(&b"ordinary frame after explicit reset"[..], 1).expect("encode valid control");
context.reset(ResetDirective::SessionOnly).expect("reset failed decoder");
{
let mut input = InBuffer::around(valid.as_slice());
let mut output = OutBuffer::around(&mut destination);
assert_eq!(context.decompress_stream(&mut output, &mut input), Ok(0));
}
assert_eq!(destination, b"ordinary frame after explicit reset");
}
#[test]
fn corrupt_legacy_frame_cannot_emit_bytes_when_writer_is_dropped() {
let mut visible = Vec::new();
{
let mut decoder = zstd::stream::write::Decoder::new(&mut visible)
.expect("create writer decoder")
.auto_flush();
assert!(decoder.write_all(&[0x27, 0xb5, 0x2f, 0xfd, 0x08, 0x00]).is_err());
}
assert!(visible.is_empty(), "failed legacy decoding must not publish heap bytes");
}
#[test]
fn seekable_offsets_past_eof_keep_vec_length_within_capacity() {
use zstd::zstd_safe::seekable::{Seekable, SeekableCStream};
let source = b"seekable frame fixture with enough data for several independent frames";
let mut encoded = [0; 1024];
let written = {
let mut encoder = SeekableCStream::create();
encoder.init(3, true, 24).expect("initialize seekable encoder");
let mut input = InBuffer::around(source);
let mut output = OutBuffer::around(&mut encoded[..]);
while input.pos() < source.len() {
let previous = input.pos();
encoder
.compress_stream(&mut output, &mut input)
.expect("encode seekable input");
assert!(input.pos() > previous, "encoder must make progress");
}
let mut finished = false;
for _ in 0..16 {
if encoder.end_stream(&mut output).expect("finish seek table") == 0 {
finished = true;
break;
}
}
assert!(finished, "bounded fixture must finish within the output capacity");
output.pos()
};
let mut decoder = Seekable::create();
decoder.init_buff(&encoded[..written]).expect("open seekable archive");
let mut decoded = Vec::with_capacity(source.len());
assert_eq!(decoder.decompress(&mut decoded, 0), Ok(source.len()));
assert_eq!(decoded, source);
let end = u64::try_from(source.len()).expect("fixture length fits u64");
for offset in [end, end + 1, end + 4096, u64::MAX - 9, u64::MAX] {
let mut output = Vec::with_capacity(16);
assert_eq!(decoder.decompress(&mut output, offset), Ok(0), "offset {offset}");
assert!(output.is_empty(), "offset {offset} must not extend the vector");
assert!(output.len() <= output.capacity());
}
}
#[test]
fn stored_registry_013_frame_remains_readable() {
// Generated by registry zstd 0.13.3 with encode_all at level 1.
let frame: &[u8] = &[
0x28, 0xb5, 0x2f, 0xfd, 0x00, 0x48, 0x99, 0x00, 0x00, 0x73, 0x79, 0x6e, 0x74, 0x68, 0x65, 0x74, 0x69, 0x63, 0x2d, 0x72,
0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x2d, 0x42,
];
assert_eq!(zstd::decode_all(frame).expect("read pre-upgrade stored frame"), b"synthetic-request-B");
}
@@ -11,6 +11,8 @@
## Open Items
- `zstd-013-fixed-backend` dependency constraint bridge: Avro, Arrow IPC, Parquet, and Kafka still require zstd 0.13. The local adapter re-exports the fixed registry zstd 0.14.3 implementation and its zstd-safe types. Remove the adapter and Cargo patch after every dependency accepts the fixed zstd line without the 0.13 bridge.
- `8565` historical rebalance replacement recovery: older schema-seven intents can exhaust their retry budget before format repair when retained rebalance metadata describes removed pools. After validating blank targets, terminal historical topology, and ownership, grant one durable handoff and one rebuild attempt. Remove after supported direct upgrades no longer admit snapshots lacking replacement_preformat_handoff_retry_done and all retained affected intents have been resolved.
- `backlog-2253-metadata-observation-adapter` metadata observations: preserve aligned metadata/error slices for existing layout, shard and exact-version PUT, MPU and delete consumers while read/heal scheduling uses typed disk observations. Pending slots acquire empty placeholders only in this adapter. Remove after those remaining consumers use typed observations; delete the adapter and old facade in a separate cleanup.