diff --git a/Cargo.lock b/Cargo.lock index 6bbe5b8852..d7d9707a6d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -8,11 +8,17 @@ version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" +[[package]] +name = "adler32" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aae1277d39aeec15cb388266ecc24b11c80469deae6067e17a1a7aa9e5c1f234" + [[package]] name = "aho-corasick" -version = "1.1.2" +version = "1.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b2969dcb958b36655471fc61f7e416fa76033bdd4bfed0678d8fee1e2d07a1f0" +checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916" dependencies = [ "memchr", ] @@ -34,9 +40,9 @@ dependencies = [ [[package]] name = "autocfg" -version = "1.1.0" +version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" +checksum = "f1fdabc7756949593fe60f30ec81974b613357de856987752631dea1e3394c80" [[package]] name = "base64" @@ -63,7 +69,7 @@ dependencies = [ "regex", "rustc-hash", "shlex", - "syn 2.0.52", + "syn 2.0.58", "which", ] @@ -81,9 +87,9 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" [[package]] name = "bitflags" -version = "2.4.2" +version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed570934406eb16438a4e976b1b4500774099c13b8cb96eec99f620f05090ddf" +checksum = "cf4b9d6a944f767f8e5e0db018570623c85f3d925ac718db4e06d0187adb21c1" [[package]] name = "block-buffer" @@ -96,15 +102,15 @@ dependencies = [ [[package]] name = "bumpalo" -version = "3.15.3" +version = "3.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ea184aa71bb362a1157c896979544cc23974e08fd265f29ea96b59f0b4a555b" +checksum = "79296716171880943b8470b5f8d03aa55eb2e645a4874bdbb28adb49162e012c" [[package]] name = "bytemuck" -version = "1.14.3" +version = "1.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a2ef034f05691a48569bd920a96c81b9d91bbad1ab5ac7c4616c1f6ef36cb79f" +checksum = "5d6d68c57235a3a081186990eca2867354726650f42f7516ca50c28d6281fd15" [[package]] name = "byteorder" @@ -114,9 +120,19 @@ checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" [[package]] name = "bytes" -version = "1.5.0" +version = "1.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a2bd12c1caf447e69cd4528f47f94d203fd2582878ecb9e9465484c4148a8223" +checksum = "514de17de45fdb8dc022b1a7975556c53c86f9f0aa5f534b98977b171857c2c9" + +[[package]] +name = "bzip2-rs" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "beeb59e7e4c811ab37cc73680c798c7a5da77fc9989c62b09138e31ee740f735" +dependencies = [ + "crc32fast", + "tinyvec", +] [[package]] name = "cbindgen" @@ -138,9 +154,9 @@ dependencies = [ [[package]] name = "cc" -version = "1.0.88" +version = "1.0.94" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "02f341c093d19155a6e41631ce5971aac4e9a868262212153124c15fa22d1cdc" +checksum = "17f6e324229dc011159fcc089755d1e2e216a90d43a7dea6853ca740b84f35e7" [[package]] name = "cexpr" @@ -159,16 +175,16 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] name = "chrono" -version = "0.4.34" +version = "0.4.37" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5bc015644b92d5890fab7489e49d21f879d5c990186827d42ec511919404f38b" +checksum = "8a0d04d43504c61aa6c7531f1871dd0d418d91130162063b789da00fd7057a5e" dependencies = [ "android-tzdata", "iana-time-zone", "js-sys", "num-traits", "wasm-bindgen", - "windows-targets 0.52.4", + "windows-targets", ] [[package]] @@ -177,12 +193,15 @@ version = "0.0.1" dependencies = [ "base64", "bindgen", + "byteorder", + "bzip2-rs", "cbindgen", "delharc", "flate2", "hex", "hex-literal", "image", + "inflate", "libc", "log", "num-traits", @@ -284,7 +303,7 @@ version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f420b1ede12094758715adaee221d93cb2af86dc499cef368ef81a23fea96029" dependencies = [ - "bitflags 2.4.2", + "bitflags 2.5.0", "chrono", "memchr", ] @@ -301,15 +320,15 @@ dependencies = [ [[package]] name = "either" -version = "1.10.0" +version = "1.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "11157ac094ffbdde99aa67b23417ebdd801842852b500e395a45a9c0aac03e4a" +checksum = "a47c1c47d2f5964e29c61246e81db715514cd532db6b5116a25ea3c03d6780a2" [[package]] name = "encoding_rs" -version = "0.8.33" +version = "0.8.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7268b386296a025e474d5140678f75d6de9493ae55a5d709eeb9dd08149945e1" +checksum = "b45de904aa0b010bce2ab45264d0631681847fa7b6f2eaa7dab7619943bc4f59" dependencies = [ "cfg-if", ] @@ -332,7 +351,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a258e46cdc063eb8519c00b9fc845fc47bcfca4130e2f08e88665ceda8474245" dependencies = [ "libc", - "windows-sys 0.52.0", + "windows-sys", ] [[package]] @@ -353,9 +372,9 @@ dependencies = [ [[package]] name = "fastrand" -version = "2.0.1" +version = "2.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "25cbce373ec4653f1a01a31e8a5e5ec0c622dc27ff9c4e6606eefef5cbbed4a5" +checksum = "658bd65b1cf4c852a3cc96f18a8ce7b5640f6b703f905c7d74532294c2a63984" [[package]] name = "fdeflate" @@ -413,9 +432,9 @@ checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b" [[package]] name = "half" -version = "2.4.0" +version = "2.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b5eceaaeec696539ddaf7b333340f1af35a5aa87ae3e4f3ead0532f72affab2e" +checksum = "6dd08c532ae367adf81c312a4580bc67f1d0fe8bc9c460520283f4c0ff277888" dependencies = [ "cfg-if", "crunchy", @@ -451,7 +470,7 @@ version = "0.5.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e3d1354bf6b7235cb4a0576c2619fd4ed18183f689b12b006a0ee7329eeff9a5" dependencies = [ - "windows-sys 0.52.0", + "windows-sys", ] [[package]] @@ -505,6 +524,15 @@ dependencies = [ "hashbrown", ] +[[package]] +name = "inflate" +version = "0.4.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1cdb29978cc5797bd8dcc8e5bf7de604891df2a8dc576973d71a281e916db2ff" +dependencies = [ + "adler32", +] + [[package]] name = "itertools" version = "0.10.5" @@ -516,9 +544,9 @@ dependencies = [ [[package]] name = "itoa" -version = "1.0.10" +version = "1.0.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b1a46d1a171d865aa5f83f92695765caa047a9b4cbae2cbf37dbd613a793fd4c" +checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b" [[package]] name = "jpeg-decoder" @@ -531,9 +559,9 @@ dependencies = [ [[package]] name = "js-sys" -version = "0.3.68" +version = "0.3.69" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "406cda4b368d531c842222cf9d2600a9a4acce8d29423695379c6868a143a9ee" +checksum = "29c15563dc2726973df627357ce0c9ddddbea194836909d655df6a75d2cf296d" dependencies = [ "wasm-bindgen", ] @@ -564,12 +592,12 @@ checksum = "9c198f91728a82281a64e1f4f9eeb25d82cb32a5de251c6bd1b5154d63a8e7bd" [[package]] name = "libloading" -version = "0.8.1" +version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c571b676ddfc9a8c12f1f3d3085a7b163966a8fd8098a90640953ce5f6170161" +checksum = "0c2a198fb6b0eada2a8df47933734e6d35d350665a33a3593d7164fa52c75c19" dependencies = [ "cfg-if", - "windows-sys 0.48.0", + "windows-targets", ] [[package]] @@ -596,9 +624,9 @@ checksum = "90ed8c1e510134f979dbc4f070f87d4313098b704861a105fe34231c70a3901c" [[package]] name = "memchr" -version = "2.7.1" +version = "2.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "523dc4f511e55ab87b694dc30d0f820d60906ef06413f93d4d7a1385599cc149" +checksum = "6c8640c5d730cb13ebd907d8d04b52f55ac9a2eec55b440c8892f40d56c76c1d" [[package]] name = "minimal-lexical" @@ -702,12 +730,12 @@ dependencies = [ [[package]] name = "prettyplease" -version = "0.2.16" +version = "0.2.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a41cf62165e97c7f814d2221421dbb9afcbcdb0a88068e5ea206e19951c2cbb5" +checksum = "8d3928fb5db768cb86f891ff014f0144589297e3c6a1aba6ed7cecfdace270c7" dependencies = [ "proc-macro2", - "syn 2.0.52", + "syn 2.0.58", ] [[package]] @@ -721,9 +749,9 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.78" +version = "1.0.79" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2422ad645d89c99f8f3e6b88a9fdeca7fabeac836b1002371c4367c8f984aae" +checksum = "e835ff2298f5721608eb1a980ecaee1aef2c132bf95ecc026a11b7bf3c01c02e" dependencies = [ "unicode-ident", ] @@ -739,18 +767,18 @@ dependencies = [ [[package]] name = "quote" -version = "1.0.35" +version = "1.0.36" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "291ec9ab5efd934aaf503a6466c5d5251535d108ee747472c3977cc5acc868ef" +checksum = "0fa76aaf39101c457836aec0ce2316dbdc3ab723cdda1c6bd4e6ad4208acaca7" dependencies = [ "proc-macro2", ] [[package]] name = "rayon" -version = "1.9.0" +version = "1.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e4963ed1bc86e4f3ee217022bd855b297cef07fb9eac5dfa1f788b220b49b3bd" +checksum = "b418a60154510ca1a002a752ca9714984e21e4241e804d32555251faf8b78ffa" dependencies = [ "either", "rayon-core", @@ -768,9 +796,9 @@ dependencies = [ [[package]] name = "regex" -version = "1.10.3" +version = "1.10.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b62dbe01f0b06f9d8dc7d49e05a0785f153b00b2c227856282f671e0318c9b15" +checksum = "c117dbdfde9c8308975b6a18d71f3f385c89461f7b3fb054288ecf2a2058ba4c" dependencies = [ "aho-corasick", "memchr", @@ -780,9 +808,9 @@ dependencies = [ [[package]] name = "regex-automata" -version = "0.4.5" +version = "0.4.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5bb987efffd3c6d0d8f5f89510bb458559eab11e4f869acb20bf845e016259cd" +checksum = "86b83b8b9847f9bf95ef68afb0b8e6cdb80f498442f5179a29fad448fcc1eaea" dependencies = [ "aho-corasick", "memchr", @@ -791,9 +819,9 @@ dependencies = [ [[package]] name = "regex-syntax" -version = "0.8.2" +version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c08c74e62047bb2de4ff487b251e4a92e24f48745648451635cec7d591162d9f" +checksum = "adad44e29e4c806119491a7f06f03de4d1af22c3a680dd47f1e6e179439d1f56" [[package]] name = "rustc-hash" @@ -827,15 +855,15 @@ dependencies = [ [[package]] name = "rustix" -version = "0.38.31" +version = "0.38.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6ea3e1a662af26cd7a3ba09c0297a31af215563ecf42817c98df621387f4e949" +checksum = "65e04861e65f21776e67888bfbea442b3642beaa0138fdb1dd7a84a52dffdb89" dependencies = [ - "bitflags 2.4.2", + "bitflags 2.5.0", "errno", "libc", "linux-raw-sys", - "windows-sys 0.52.0", + "windows-sys", ] [[package]] @@ -867,14 +895,14 @@ checksum = "7eb0b34b42edc17f6b7cac84a52a1c5f0e1bb2227e997ca9011ea3dd34e8610b" dependencies = [ "proc-macro2", "quote", - "syn 2.0.52", + "syn 2.0.58", ] [[package]] name = "serde_json" -version = "1.0.114" +version = "1.0.115" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c5f09b1bd632ef549eaa9f60a1f8de742bdbc698e6cee2095fc84dde5f549ae0" +checksum = "12dc5c46daa8e9fdf4f5e71b6cf9a53f2487da0e86e55808e2d35539666497dd" dependencies = [ "itoa", "ryu", @@ -917,9 +945,9 @@ checksum = "d66dc143e6b11c1eddc06d5c423cfc97062865baf299914ab64caa38182078fe" [[package]] name = "smallvec" -version = "1.13.1" +version = "1.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e6ecd384b10a64542d77071bd64bd7b231f4ed5940fba55e98c3de13824cf3d7" +checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67" [[package]] name = "spin" @@ -949,9 +977,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.52" +version = "2.0.58" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b699d15b36d1f02c3e7c69f8ffef53de37aefae075d8488d4ba1a7788d574a07" +checksum = "44cfb93f38070beee36b3fef7d4f5a16f27751d94b187b666a5cc5e9b0d30687" dependencies = [ "proc-macro2", "quote", @@ -967,27 +995,27 @@ dependencies = [ "cfg-if", "fastrand", "rustix", - "windows-sys 0.52.0", + "windows-sys", ] [[package]] name = "thiserror" -version = "1.0.57" +version = "1.0.58" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e45bcbe8ed29775f228095caf2cd67af7a4ccf756ebff23a306bf3e8b47b24b" +checksum = "03468839009160513471e86a034bb2c5c0e4baae3b43f79ffc55c4a5427b3297" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "1.0.57" +version = "1.0.58" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a953cb265bef375dae3de6663da4d3804eee9682ea80d8e2542529b73c531c81" +checksum = "c61f3ba182994efc43764a46c018c347bc492c79f024e705f46567b418f6d4f7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.52", + "syn 2.0.58", ] [[package]] @@ -1001,6 +1029,12 @@ dependencies = [ "weezl", ] +[[package]] +name = "tinyvec" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87cc5ceb3875bb20c2890005a4e226a4651264a5c75edb2421b52861a0a0cb50" + [[package]] name = "toml" version = "0.5.11" @@ -1040,9 +1074,9 @@ checksum = "d4c87d22b6e3f4a18d4d40ef354e97c90fcb14dd91d7dc0aa9d8a1172ebf7202" [[package]] name = "uuid" -version = "1.7.0" +version = "1.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f00cc9702ca12d3c81455259621e676d0f7251cec66a21e98fe2e9a37db93b2a" +checksum = "a183cf7feeba97b4dd1c0d46788634f6221d87fa961b305bed08c851829efcc0" [[package]] name = "version_check" @@ -1052,9 +1086,9 @@ checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" [[package]] name = "wasm-bindgen" -version = "0.2.91" +version = "0.2.92" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c1e124130aee3fb58c5bdd6b639a0509486b0338acaaae0c84a5124b0f588b7f" +checksum = "4be2531df63900aeb2bca0daaaddec08491ee64ceecbee5076636a3b026795a8" dependencies = [ "cfg-if", "wasm-bindgen-macro", @@ -1062,24 +1096,24 @@ dependencies = [ [[package]] name = "wasm-bindgen-backend" -version = "0.2.91" +version = "0.2.92" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c9e7e1900c352b609c8488ad12639a311045f40a35491fb69ba8c12f758af70b" +checksum = "614d787b966d3989fa7bb98a654e369c762374fd3213d212cfc0251257e747da" dependencies = [ "bumpalo", "log", "once_cell", "proc-macro2", "quote", - "syn 2.0.52", + "syn 2.0.58", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-macro" -version = "0.2.91" +version = "0.2.92" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b30af9e2d358182b5c7449424f017eba305ed32a7010509ede96cdc4696c46ed" +checksum = "a1f8823de937b71b9460c0c34e25f3da88250760bec0ebac694b49997550d726" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -1087,22 +1121,22 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.91" +version = "0.2.92" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "642f325be6301eb8107a83d12a8ac6c1e1c54345a7ef1a9261962dfefda09e66" +checksum = "e94f17b526d0a461a191c78ea52bbce64071ed5c04c9ffe424dcb38f74171bb7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.52", + "syn 2.0.58", "wasm-bindgen-backend", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-shared" -version = "0.2.91" +version = "0.2.92" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4f186bd2dcf04330886ce82d6f33dd75a7bfcf69ecf5763b89fcde53b6ac9838" +checksum = "af190c94f2773fdb3729c55b007a722abb5384da03bc0986df4c289bf5567e96" [[package]] name = "weezl" @@ -1124,9 +1158,9 @@ dependencies = [ [[package]] name = "widestring" -version = "1.0.2" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "653f141f39ec16bba3c5abe400a0c60da7468261cc2cbf36805022876bc721a8" +checksum = "7219d36b6eac893fa81e84ebe06485e7dcbb616177469b142df14f1f4deb1311" [[package]] name = "windows-core" @@ -1134,16 +1168,7 @@ version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "33ab640c8d7e35bf8ba19b884ba838ceb4fba93a4e8c65a9059d08afcfc683d9" dependencies = [ - "windows-targets 0.52.4", -] - -[[package]] -name = "windows-sys" -version = "0.48.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" -dependencies = [ - "windows-targets 0.48.5", + "windows-targets", ] [[package]] @@ -1152,122 +1177,72 @@ version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" dependencies = [ - "windows-targets 0.52.4", -] - -[[package]] -name = "windows-targets" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c" -dependencies = [ - "windows_aarch64_gnullvm 0.48.5", - "windows_aarch64_msvc 0.48.5", - "windows_i686_gnu 0.48.5", - "windows_i686_msvc 0.48.5", - "windows_x86_64_gnu 0.48.5", - "windows_x86_64_gnullvm 0.48.5", - "windows_x86_64_msvc 0.48.5", + "windows-targets", ] [[package]] name = "windows-targets" -version = "0.52.4" +version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7dd37b7e5ab9018759f893a1952c9420d060016fc19a472b4bb20d1bdd694d1b" +checksum = "6f0713a46559409d202e70e28227288446bf7841d3211583a4b53e3f6d96e7eb" dependencies = [ - "windows_aarch64_gnullvm 0.52.4", - "windows_aarch64_msvc 0.52.4", - "windows_i686_gnu 0.52.4", - "windows_i686_msvc 0.52.4", - "windows_x86_64_gnu 0.52.4", - "windows_x86_64_gnullvm 0.52.4", - "windows_x86_64_msvc 0.52.4", + "windows_aarch64_gnullvm", + "windows_aarch64_msvc", + "windows_i686_gnu", + "windows_i686_gnullvm", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_gnullvm", + "windows_x86_64_msvc", ] [[package]] name = "windows_aarch64_gnullvm" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" - -[[package]] -name = "windows_aarch64_gnullvm" -version = "0.52.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bcf46cf4c365c6f2d1cc93ce535f2c8b244591df96ceee75d8e83deb70a9cac9" - -[[package]] -name = "windows_aarch64_msvc" -version = "0.48.5" +version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" +checksum = "7088eed71e8b8dda258ecc8bac5fb1153c5cffaf2578fc8ff5d61e23578d3263" [[package]] name = "windows_aarch64_msvc" -version = "0.52.4" +version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da9f259dd3bcf6990b55bffd094c4f7235817ba4ceebde8e6d11cd0c5633b675" +checksum = "9985fd1504e250c615ca5f281c3f7a6da76213ebd5ccc9561496568a2752afb6" [[package]] name = "windows_i686_gnu" -version = "0.48.5" +version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" +checksum = "88ba073cf16d5372720ec942a8ccbf61626074c6d4dd2e745299726ce8b89670" [[package]] -name = "windows_i686_gnu" -version = "0.52.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b474d8268f99e0995f25b9f095bc7434632601028cf86590aea5c8a5cb7801d3" - -[[package]] -name = "windows_i686_msvc" -version = "0.48.5" +name = "windows_i686_gnullvm" +version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" +checksum = "87f4261229030a858f36b459e748ae97545d6f1ec60e5e0d6a3d32e0dc232ee9" [[package]] name = "windows_i686_msvc" -version = "0.52.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1515e9a29e5bed743cb4415a9ecf5dfca648ce85ee42e15873c3cd8610ff8e02" - -[[package]] -name = "windows_x86_64_gnu" -version = "0.48.5" +version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" +checksum = "db3c2bf3d13d5b658be73463284eaf12830ac9a26a90c717b7f771dfe97487bf" [[package]] name = "windows_x86_64_gnu" -version = "0.52.4" +version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5eee091590e89cc02ad514ffe3ead9eb6b660aedca2183455434b93546371a03" +checksum = "4e4246f76bdeff09eb48875a0fd3e2af6aada79d409d33011886d3e1581517d9" [[package]] name = "windows_x86_64_gnullvm" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" - -[[package]] -name = "windows_x86_64_gnullvm" -version = "0.52.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77ca79f2451b49fa9e2af39f0747fe999fcda4f5e241b2898624dca97a1f2177" - -[[package]] -name = "windows_x86_64_msvc" -version = "0.48.5" +version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" +checksum = "852298e482cd67c356ddd9570386e2862b5673c85bd5f88df9ab6802b334c596" [[package]] name = "windows_x86_64_msvc" -version = "0.52.4" +version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "32b752e52a2da0ddfbdbcc6fceadfeede4c939ed16d13e648833a61dfb611ed8" +checksum = "bec47e5bfd1bff0eeaf6d8b485cc1074891a197ab4225d504cb7a1ab88b02bf0" [[package]] name = "zune-inflate" diff --git a/clam-format b/clam-format index e3d8519418..bf12baaba1 100755 --- a/clam-format +++ b/clam-format @@ -50,6 +50,8 @@ clang-format-16 -i -verbose unit_tests/*.h clang-format-16 -i -verbose win32/compat/*.c clang-format-16 -i -verbose win32/compat/*.h +rustfmt `find . -name "*.rs"` + # Undo changes to specific files that we don't really want to reformat git checkout libclamav/iana_cctld.h git checkout libclamav/bytecode_api_decl.c diff --git a/libclamav/dconf.c b/libclamav/dconf.c index 3bb5617739..b3037e9392 100644 --- a/libclamav/dconf.c +++ b/libclamav/dconf.c @@ -108,6 +108,7 @@ static struct dconf_module modules[] = { {"ARCHIVE", "EGG", ARCH_CONF_EGG, 1}, {"ARCHIVE", "UDF", ARCH_CONF_UDF, 1}, {"ARCHIVE", "LHA", ARCH_CONF_LHA_LZH, 1}, + {"ARCHIVE", "ALZ", ARCH_CONF_ALZ, 1}, {"DOCUMENT", "HTML", DOC_CONF_HTML, 1}, {"DOCUMENT", "RTF", DOC_CONF_RTF, 1}, diff --git a/libclamav/dconf.h b/libclamav/dconf.h index e5df428e49..39bbc553b6 100644 --- a/libclamav/dconf.h +++ b/libclamav/dconf.h @@ -98,6 +98,7 @@ struct cli_dconf { #define ARCH_CONF_EGG 0x4000000 #define ARCH_CONF_UDF 0x8000000 #define ARCH_CONF_LHA_LZH 0x10000000 +#define ARCH_CONF_ALZ 0x20000000 /* Document flags */ #define DOC_CONF_HTML 0x1 diff --git a/libclamav/filetypes.c b/libclamav/filetypes.c index 284120a0e8..88a2b18a32 100644 --- a/libclamav/filetypes.c +++ b/libclamav/filetypes.c @@ -138,6 +138,7 @@ static const struct ftmap_s { { "CL_TYPE_EGG", CL_TYPE_EGG }, { "CL_TYPE_EGGSFX", CL_TYPE_EGGSFX }, { "CL_TYPE_UDF", CL_TYPE_UDF }, + { "CL_TYPE_ALZ", CL_TYPE_ALZ }, { "CL_TYPE_ONENOTE", CL_TYPE_ONENOTE }, { "CL_TYPE_PYTHON_COMPILED", CL_TYPE_PYTHON_COMPILED }, { "CL_TYPE_LHA_LZH", CL_TYPE_LHA_LZH }, diff --git a/libclamav/filetypes.h b/libclamav/filetypes.h index 637b7487a3..8a7a1e0dd2 100644 --- a/libclamav/filetypes.h +++ b/libclamav/filetypes.h @@ -126,6 +126,7 @@ typedef enum cli_file { CL_TYPE_MHTML, CL_TYPE_LNK, CL_TYPE_UDF, + CL_TYPE_ALZ, CL_TYPE_OTHER, /* on-the-fly, used for target 14 (OTHER) */ CL_TYPE_IGNORED /* please don't add anything below */ } cli_file_t; diff --git a/libclamav/filetypes_int.h b/libclamav/filetypes_int.h index 832b6c3942..397c41a49c 100644 --- a/libclamav/filetypes_int.h +++ b/libclamav/filetypes_int.h @@ -301,5 +301,6 @@ static const char *ftypes_int[] = { "1:2:2d6c68(30|31|32|33|34|35|36|37|64|78)2d:LHA or LZH archive:CL_TYPE_ANY:CL_TYPE_LHA_LZH:210", "1:2:2d6c7a(73|34|35)2d:LHA archive using .LZS extension:CL_TYPE_ANY:CL_TYPE_LHA_LZH:210", "1:2:2d706d302d:LHA archive using PMarc (.PMA) extension:CL_TYPE_ANY:CL_TYPE_LHA_LZH:210", + "0:0:414c5a01:ALZ:CL_TYPE_ANY:CL_TYPE_ALZ:210", NULL}; #endif diff --git a/libclamav/hwp.c b/libclamav/hwp.c index 763775109f..afa73eaaea 100644 --- a/libclamav/hwp.c +++ b/libclamav/hwp.c @@ -1828,7 +1828,6 @@ static cl_error_t hwp3_cb(void *cbdata, int fd, const char *filepath, cli_ctx *c if (SCAN_COLLECT_METADATA) cli_jsonint(ctx->wrkproperty, "ParagraphCount", p); - last = 0; /* 'additional information block #1's - attachments and media */ while (!last && ((ret = parsehwp3_infoblk_1(ctx, map, &offset, &last)) == CL_SUCCESS)) continue; diff --git a/libclamav/mbox.c b/libclamav/mbox.c index f069b5ef22..23f577002b 100644 --- a/libclamav/mbox.c +++ b/libclamav/mbox.c @@ -3217,7 +3217,7 @@ parseMimeHeader(message *m, const char *cmd, const table_t *rfc821Table, const c #ifdef CL_THREAD_SAFE s = strtok_r(NULL, ";", &strptr); #else - s = strtok(NULL, ";"); + s = strtok(NULL, ";"); #endif if (s == NULL) break; @@ -4387,7 +4387,7 @@ do_multipart(message *mainMessage, message **messages, int i, mbox_status *rc, m * This can save a lot of memory */ messageDestroy(messages[i]); - messages[i] = NULL; + messages[i] = NULL; mctx->wrkobj = thisobj; if (body) { diff --git a/libclamav/others.h b/libclamav/others.h index b2a78b1006..8cebf78d35 100644 --- a/libclamav/others.h +++ b/libclamav/others.h @@ -587,16 +587,16 @@ extern LIBCLAMAV_EXPORT int have_rar; /* based on macros from A. Melnikoff */ #define cbswap16(v) (((v & 0xff) << 8) | (((v) >> 8) & 0xff)) -#define cbswap32(v) ((((v) & 0x000000ff) << 24) | (((v) & 0x0000ff00) << 8) | \ - (((v) & 0x00ff0000) >> 8) | (((v) & 0xff000000) >> 24)) -#define cbswap64(v) ((((v) & 0x00000000000000ffULL) << 56) | \ - (((v) & 0x000000000000ff00ULL) << 40) | \ - (((v) & 0x0000000000ff0000ULL) << 24) | \ - (((v) & 0x00000000ff000000ULL) << 8) | \ - (((v) & 0x000000ff00000000ULL) >> 8) | \ - (((v) & 0x0000ff0000000000ULL) >> 24) | \ - (((v) & 0x00ff000000000000ULL) >> 40) | \ - (((v) & 0xff00000000000000ULL) >> 56)) +#define cbswap32(v) ((((v)&0x000000ff) << 24) | (((v)&0x0000ff00) << 8) | \ + (((v)&0x00ff0000) >> 8) | (((v)&0xff000000) >> 24)) +#define cbswap64(v) ((((v)&0x00000000000000ffULL) << 56) | \ + (((v)&0x000000000000ff00ULL) << 40) | \ + (((v)&0x0000000000ff0000ULL) << 24) | \ + (((v)&0x00000000ff000000ULL) << 8) | \ + (((v)&0x000000ff00000000ULL) >> 8) | \ + (((v)&0x0000ff0000000000ULL) >> 24) | \ + (((v)&0x00ff000000000000ULL) >> 40) | \ + (((v)&0xff00000000000000ULL) >> 56)) #ifndef HAVE_ATTRIB_PACKED #define __attribute__(x) @@ -822,8 +822,8 @@ size_t cli_recursion_stack_get_size(cli_ctx *ctx, int index); /* used by: spin, yc (C) aCaB */ #define __SHIFTBITS(a) (sizeof(a) << 3) #define __SHIFTMASK(a) (__SHIFTBITS(a) - 1) -#define CLI_ROL(a, b) a = (a << ((b) & __SHIFTMASK(a))) | (a >> ((__SHIFTBITS(a) - (b)) & __SHIFTMASK(a))) -#define CLI_ROR(a, b) a = (a >> ((b) & __SHIFTMASK(a))) | (a << ((__SHIFTBITS(a) - (b)) & __SHIFTMASK(a))) +#define CLI_ROL(a, b) a = (a << ((b)&__SHIFTMASK(a))) | (a >> ((__SHIFTBITS(a) - (b)) & __SHIFTMASK(a))) +#define CLI_ROR(a, b) a = (a >> ((b)&__SHIFTMASK(a))) | (a << ((__SHIFTBITS(a) - (b)) & __SHIFTMASK(a))) /* Implementation independent sign-extended signed right shift */ #ifdef HAVE_SAR diff --git a/libclamav/scanners.c b/libclamav/scanners.c index 832f0dc670..6d15f9d6be 100644 --- a/libclamav/scanners.c +++ b/libclamav/scanners.c @@ -4551,6 +4551,12 @@ cl_error_t cli_magic_scan(cli_ctx *ctx, cli_file_t type) ret = scan_onenote(ctx); break; + case CL_TYPE_ALZ: + if (SCAN_PARSE_ARCHIVE && (DCONF_ARCH & ARCH_CONF_ALZ)) { + ret = cli_scanalz(ctx); + } + break; + case CL_TYPE_LHA_LZH: if (SCAN_PARSE_ARCHIVE && (DCONF_ARCH & ARCH_CONF_LHA_LZH)) ret = scan_lha_lzh(ctx); diff --git a/libclamav/sis.c b/libclamav/sis.c index c3de1983c0..5a6b5d6089 100644 --- a/libclamav/sis.c +++ b/libclamav/sis.c @@ -628,7 +628,7 @@ enum { T_INVALID, const char *sisfields[] = {"Invalid", "String", "Array", "Compressed", "Version", "VersionRange", "Date", "Time", "DateTime", "Uid", "Unused", "Language", "Contents", "Controller", "Info", "SupportedLanguages", "SupportedOptions", "Prerequisites", "Dependency", "Properties", "Property", "Signatures", "CertificateChain", "Logo", "FileDescription", "Hash", "If", "ElseIf", "InstallBlock", "Expression", "Data", "DataUnit", "FileData", "SupportedOption", "ControllerChecksum", "DataChecksum", "Signature", "Blob", "SignatureAlgorithm", "SignatureCertificateChain", "DataIndex", "Capabilities"}; -#define ALIGN4(x) (((x) & ~3) + ((((x) & 1) | (((x) >> 1) & 1)) << 2)) +#define ALIGN4(x) (((x) & ~3) + ((((x)&1) | (((x) >> 1) & 1)) << 2)) #define HERE printf("here\n"), abort(); diff --git a/libclamav_rust/Cargo.toml b/libclamav_rust/Cargo.toml index 2d9f27e3e1..a579248b30 100644 --- a/libclamav_rust/Cargo.toml +++ b/libclamav_rust/Cargo.toml @@ -22,6 +22,9 @@ unicode-segmentation = "1.10" bindgen = "0.65" onenote_parser = { git = "https://github.com/Cisco-Talos/onenote.rs.git", branch = "CLAM-2329-new-from-slice" } hex-literal = "0.4" +inflate = "0.4" +bzip2-rs = "0.1" +byteorder = "1.5" delharc = "0.5" [lib] diff --git a/libclamav_rust/cbindgen.toml b/libclamav_rust/cbindgen.toml index 28e146e358..ef642ef7c5 100644 --- a/libclamav_rust/cbindgen.toml +++ b/libclamav_rust/cbindgen.toml @@ -37,6 +37,7 @@ include = [ "evidence::evidence_add_indicator", "evidence::IndicatorType", "scanners::scan_onenote", + "scanners::cli_scanalz", ] # prefix = "CAPI_" diff --git a/libclamav_rust/src/alz.rs b/libclamav_rust/src/alz.rs new file mode 100644 index 0000000000..56674fb9c6 --- /dev/null +++ b/libclamav_rust/src/alz.rs @@ -0,0 +1,554 @@ +/* + * ALZ archive extraction. + * + * Copyright (C) 2024 Cisco Systems, Inc. and/or its affiliates. All rights reserved. + * + * Authors: Andy Ragusa + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301, USA. + */ + +/* +#![warn( + clippy::all, + clippy::restriction, + clippy::pedantic, + clippy::nursery, + clippy::cargo, +)] +*/ + +use std::io::{Cursor, Read}; + +use byteorder::{LittleEndian, ReadBytesExt}; +use bzip2_rs::DecoderReader; +use inflate::InflateStream; +use log::debug; + +/// File header +const ALZ_FILE_HEADER: u32 = 0x015a_4c41; +/// Local file header +const ALZ_LOCAL_FILE_HEADER: u32 = 0x015a_4c42; +/// Central directory header +const ALZ_CENTRAL_DIRECTORY_HEADER: u32 = 0x015a_4c43; +/// End of Central directory header +const ALZ_END_OF_CENTRAL_DIRECTORY_HEADER: u32 = 0x025a_4c43; + +/// Error enumerates all possible errors returned by this library. +#[derive(thiserror::Error, Debug)] +pub enum Error { + #[error("Error parsing ALZ archive: {0}")] + Parse(&'static str), + + #[error("Unrecognized sig: '{0}'")] + UnrecognizedSig(String), + + #[error("Unsupported ALZ feature: {0}")] + UnsupportedFeature(&'static str), + + #[error("Failed to extract file")] + Extract, + + #[error("Failed to read field: {0}")] + Read(&'static str), +} + +struct AlzLocalFileHeaderHead { + file_name_length: u16, + + file_attribute: u8, + + file_time_date: u32, + + file_descriptor: u8, + + unknown: u8, +} + +const ALZ_ENCR_HEADER_LEN: u32 = 12; + +struct AlzLocalFileHeader { + head: AlzLocalFileHeaderHead, + + compression_method: u8, + unknown: u8, + file_crc: u32, + + /* Can be smaller sizes, depending on file_descriptor/0x10 .*/ + compressed_size: u64, + uncompressed_size: u64, + + file_name: String, + + enc_chk: [u8; ALZ_ENCR_HEADER_LEN as usize], + + start_of_compressed_data: u64, +} + +#[allow(dead_code)] +enum AlzFileAttribute { + Readonly = 0x1, + Hidden = 0x2, + Directory = 0x10, + File = 0x20, +} + +impl AlzLocalFileHeader { + const fn is_encrypted(&self) -> bool { + 0 != (self.head.file_descriptor & 0x1) + } + + const fn is_data_descriptor(&self) -> bool { + 0 != (self.head.file_descriptor & 0x8) + } + + const fn is_directory(&self) -> bool { + 0 != ((AlzFileAttribute::Directory as u8) & self.head.file_attribute) + } + + const fn _is_file(&self) -> bool { + 0 != ((AlzFileAttribute::File as u8) & self.head.file_attribute) + } + + const fn _is_readonly(&self) -> bool { + 0 != ((AlzFileAttribute::Readonly as u8) & self.head.file_attribute) + } + + const fn _is_hidden(&self) -> bool { + 0 != ((AlzFileAttribute::Hidden as u8) & self.head.file_attribute) + } + + fn _dump(&self) { + println!( + "self.start_of_compressed_data = {}", + self.start_of_compressed_data + ); + + println!( + "self.head.file_name_length = {:x}", + self.head.file_name_length + ); + println!( + "self.head.file_attribute = {:02x}", + self.head.file_attribute + ); + println!("self.head.file_time_date = {:x}", self.head.file_time_date); + println!( + "self.head.file_descriptor = {:x}", + self.head.file_descriptor + ); + println!("self.head.unknown = {:x}", self.head.unknown); + + println!("self.compression_method = {:x}", self.compression_method); + println!("self.unknown = {:x}", self.unknown); + println!("self.file_crc = {:x}", self.file_crc); + println!("self.compressed_size = {:x}", self.compressed_size); + println!("self.uncompressed_size = {:x}", self.uncompressed_size); + + println!("self.file_name = {}", self.file_name); + + print!("self.enc_chk = "); + for i in 0..ALZ_ENCR_HEADER_LEN { + if 0 != i { + print!(" "); + } + print!("{}", self.enc_chk[i as usize]); + } + println!(); + + println!("is_encrypted = {}", self.is_encrypted()); + println!("is_data_descriptor = {}", self.is_data_descriptor()); + + println!(); + } + + pub const fn new() -> Self { + Self { + head: AlzLocalFileHeaderHead { + file_name_length: 0, + file_attribute: 0, + file_time_date: 0, + file_descriptor: 0, + unknown: 0, + }, + + compression_method: 0, + unknown: 0, + file_crc: 0, + compressed_size: 0, + uncompressed_size: 0, + file_name: String::new(), + enc_chk: [0; ALZ_ENCR_HEADER_LEN as usize], + start_of_compressed_data: 0, + } + } + + pub fn parse(&mut self, cursor: &mut std::io::Cursor<&Vec>) -> Result<(), Error> { + self.head.file_name_length = cursor + .read_u16::() + .map_err(|_| Error::Read("file_name_length"))?; + self.head.file_attribute = cursor + .read_u8() + .map_err(|_| Error::Read("file_attribute"))?; + self.head.file_time_date = cursor + .read_u32::() + .map_err(|_| Error::Read("file_time_date"))?; + self.head.file_descriptor = cursor + .read_u8() + .map_err(|_| Error::Read("file_descriptor"))?; + self.head.unknown = cursor.read_u8().map_err(|_| Error::Read("unknown u8"))?; + + if 0 == self.head.file_name_length { + return Err(Error::Parse("File Name Length is zero")); + } + + let byte_len = self.head.file_descriptor / 0x10; + if byte_len > 0 { + self.compression_method = cursor + .read_u8() + .map_err(|_| Error::Read("compression_method"))?; + self.unknown = cursor.read_u8().map_err(|_| Error::Read("unknown u8"))?; + self.file_crc = cursor + .read_u32::() + .map_err(|_| Error::Read("file_crc"))?; + + match byte_len { + 1 => { + self.compressed_size = u64::from( + cursor + .read_u8() + .map_err(|_| Error::Read("compressed_size"))?, + ); + self.uncompressed_size = u64::from( + cursor + .read_u8() + .map_err(|_| Error::Read("uncompressed_size"))?, + ); + } + 2 => { + self.compressed_size = u64::from( + cursor + .read_u16::() + .map_err(|_| Error::Read("compressed_size"))?, + ); + self.uncompressed_size = u64::from( + cursor + .read_u16::() + .map_err(|_| Error::Read("uncompressed_size"))?, + ); + } + 4 => { + self.compressed_size = u64::from( + cursor + .read_u32::() + .map_err(|_| Error::Read("compressed_size"))?, + ); + self.uncompressed_size = u64::from( + cursor + .read_u32::() + .map_err(|_| Error::Read("uncompressed_size"))?, + ); + } + 8 => { + self.compressed_size = cursor + .read_u64::() + .map_err(|_| Error::Read("compressed_size"))?; + self.uncompressed_size = cursor + .read_u64::() + .map_err(|_| Error::Read("uncompressed_size"))?; + } + _ => return Err(Error::Parse("Unsupported File Descriptor")), + } + } + + #[allow(clippy::cast_possible_truncation)] + let idx0: usize = cursor.position() as usize; + let idx1: usize = idx0 + self.head.file_name_length as usize; + + if idx1 > cursor.get_ref().len() { + return Err(Error::Parse("Invalid file name length")); + } + + let filename = &cursor.get_ref().as_slice()[idx0..idx1]; + cursor.set_position(idx1 as u64); + + self.file_name = String::from_utf8_lossy(filename).into_owned(); + + if self.is_encrypted() { + cursor + .read_exact(&mut self.enc_chk) + .map_err(|_| Error::Read("encrypted buffer"))?; + } + + self.start_of_compressed_data = cursor.position(); + cursor.set_position(self.start_of_compressed_data + self.compressed_size); + + if self.start_of_compressed_data + self.compressed_size > cursor.get_ref().len() as u64 { + return Err(Error::Parse("Invalid compressed data length")); + } + + Ok(()) + } + + pub fn is_supported(&self) -> Result<(), Error> { + if self.is_encrypted() { + return Err(Error::UnsupportedFeature("Encryption Unsupported")); + } + + if self.is_data_descriptor() { + return Err(Error::UnsupportedFeature( + "Data Descriptors are Unsupported", + )); + } + + Ok(()) + } + + /* + * This has no header/checksum validation. + */ + fn extract_file_deflate( + &mut self, + cursor: &std::io::Cursor<&Vec>, + files: &mut Vec, + ) -> Result<(), Error> { + #[allow(clippy::cast_possible_truncation)] + let start: usize = self.start_of_compressed_data as usize; + #[allow(clippy::cast_possible_truncation)] + let end: usize = start + self.compressed_size as usize; + if end >= cursor.get_ref().len() { + return Err(Error::Extract); + } + let data: &[u8] = &cursor.get_ref().as_slice()[start..end]; + + let mut inflater = InflateStream::new(); + let mut out: Vec = Vec::::new(); + let mut n: usize = 0; + + while n < data.len() { + let res = inflater.update(&data[n..]); + if let Ok((num_bytes_read, result)) = res { + n += num_bytes_read; + out.extend(result.iter().copied()); + } else { + return Err(Error::Extract); + } + } + + self.write_file(&out, files); + + Ok(()) + } + + fn write_file(&mut self, buffer: &[u8], files: &mut Vec) { + let extracted_file: ExtractedFile = ExtractedFile { + name: Some(self.file_name.to_string()), + data: buffer.to_vec(), + }; + + files.push(extracted_file); + } + + fn extract_file_nocomp( + &mut self, + cursor: &mut std::io::Cursor<&Vec>, + files: &mut Vec, + ) -> Result<(), Error> { + #[allow(clippy::cast_possible_truncation)] + let idx0: usize = self.start_of_compressed_data as usize; + + let mut len = self.compressed_size; + if self.compressed_size != self.uncompressed_size { + debug!("Uncompressed file has different lengths for compressed vs uncompressed, using the shorter"); + if self.compressed_size > self.uncompressed_size { + len = self.uncompressed_size; + } + } + + #[allow(clippy::cast_possible_truncation)] + let idx1: usize = idx0 + len as usize; + if idx1 > cursor.get_ref().len() { + debug!("Invalid data length"); + return Err(Error::Extract); + } + + let contents = &cursor.get_ref().as_slice()[idx0..idx1]; + cursor.set_position(idx1 as u64); + + self.write_file(contents, files); + Ok(()) + } + + fn extract_file_bzip2( + &mut self, + cursor: &std::io::Cursor<&Vec>, + files: &mut Vec, + ) -> Result<(), Error> { + #[allow(clippy::cast_possible_truncation)] + let idx0: usize = self.start_of_compressed_data as usize; + #[allow(clippy::cast_possible_truncation)] + let idx1: usize = idx0 + self.compressed_size as usize; + + let contents = &cursor.get_ref().as_slice()[idx0..idx1]; + + /* + * Create vector of the needed capacity. + */ + let mut out: Vec = Vec::new(); + for _i in 0..self.uncompressed_size { + out.push(0); + } + + let mut decompressor = DecoderReader::new(contents); + let ret = decompressor.read_exact(&mut out); + if ret.is_err() { + debug!("Unable to decompress bz2 data"); + return Err(Error::Extract); + } + + self.write_file(&out, files); + Ok(()) + } + + fn extract_file( + &mut self, + cursor: &mut std::io::Cursor<&Vec>, + files: &mut Vec, + ) -> Result<(), Error> { + const ALZ_COMP_NOCOMP: u8 = 0; + const ALZ_COMP_BZIP2: u8 = 1; + const ALZ_COMP_DEFLATE: u8 = 2; + + match self.compression_method { + ALZ_COMP_NOCOMP => self.extract_file_nocomp(cursor, files), + ALZ_COMP_BZIP2 => self.extract_file_bzip2(cursor, files), + ALZ_COMP_DEFLATE => self.extract_file_deflate(cursor, files), + _ => Err(Error::Extract), + } + } +} + +/*TODO: Merge this with the onenote extracted_file struct, and use the same one everywhere.*/ +pub struct ExtractedFile { + pub name: Option, + pub data: Vec, +} + +#[derive(Default)] +pub struct Alz { + pub embedded_files: Vec, +} + +impl<'aa> Alz { + /* Check for the ALZ file header. */ + #[allow(clippy::unused_self)] + fn is_alz(&self, cursor: &mut std::io::Cursor<&Vec>) -> bool { + cursor + .read_u32::() + .map_or(false, |n| ALZ_FILE_HEADER == n) + } + + fn parse_local_fileheader(&mut self, cursor: &mut std::io::Cursor<&Vec>) -> bool { + let mut local_fileheader = AlzLocalFileHeader::new(); + + if let Err(err) = local_fileheader.parse(cursor) { + debug!("{err}"); + return false; + } + + if let Err(err) = local_fileheader.is_supported() { + debug!("{err}"); + return false; + } + + if !local_fileheader.is_directory() { + /* The is_file flag doesn't appear to always be set, so we'll just assume it's a file if + * it's not marked as a directory.*/ + let res2 = local_fileheader.extract_file(cursor, &mut self.embedded_files); + if res2.is_err() { + return false; + } + } + + true + } + + #[allow(clippy::unused_self)] + fn parse_central_directoryheader(&self, cursor: &mut std::io::Cursor<&Vec>) -> bool { + /* + * This is ignored in unalz (UnAlz.cpp ReadCentralDirectoryStructure). + * + * It actually reads 12 bytes, and I think it happens to work because EOF is hit on the next + * read, which it does not consider an error. + */ + let ret = cursor.read_u64::(); + ret.is_ok() + } + + #[must_use] + pub const fn new() -> Self { + Self { + embedded_files: Vec::new(), + } + } + + /// # Errors + /// Will return `Error::Parse` if file headers are not correct or are inconsistent. + pub fn from_bytes(bytes: &'aa [u8]) -> Result { + let binding = bytes.to_vec(); + let mut cursor = Cursor::new(&binding); + + let mut alz: Self = Self::new(); + + if !alz.is_alz(&mut cursor) { + return Err(Error::Parse("No ALZ file header")); + } + + //What these bytes are supposed to be in unspecified, but they need to be there. + let ret = cursor.read_u32::(); + if ret.is_err() { + return Err(Error::Parse("Error reading uint32 from file")); + } + + loop { + let Ok(sig) = cursor.read_u32::() else { + break; + }; + + match sig { + ALZ_LOCAL_FILE_HEADER => { + if alz.parse_local_fileheader(&mut cursor) { + continue; + } + } + ALZ_CENTRAL_DIRECTORY_HEADER => { + if alz.parse_central_directoryheader(&mut cursor) { + continue; + } + } + ALZ_END_OF_CENTRAL_DIRECTORY_HEADER => { + break; + /*This is the end, nothing really to do here.*/ + } + _ => { + #[allow(clippy::uninlined_format_args)] + return Err(Error::UnrecognizedSig(format!("{:x}", sig))); + } + } + } + + Ok(alz) + } +} diff --git a/libclamav_rust/src/evidence.rs b/libclamav_rust/src/evidence.rs index 4cde71033e..e91bc7a991 100644 --- a/libclamav_rust/src/evidence.rs +++ b/libclamav_rust/src/evidence.rs @@ -244,17 +244,11 @@ impl Evidence { match indicator_type { IndicatorType::Strong => { - self.strong - .entry(name.to_string()) - .or_default() - .push(meta); + self.strong.entry(name.to_string()).or_default().push(meta); } IndicatorType::PotentiallyUnwanted => { - self.pua - .entry(name.to_string()) - .or_default() - .push(meta); + self.pua.entry(name.to_string()).or_default().push(meta); } #[cfg(feature = "not_ready")] @@ -262,10 +256,7 @@ impl Evidence { // match the archive/extraction level at which each was found. // This will be required for alerting signatures to depend on weak-indicators for embedded content. IndicatorType::Weak => { - self.weak - .entry(name.to_string()) - .or_default() - .push(meta); + self.weak.entry(name.to_string()).or_default().push(meta); } } diff --git a/libclamav_rust/src/fuzzy_hash.rs b/libclamav_rust/src/fuzzy_hash.rs index 9c260a0ffd..6d848370ae 100644 --- a/libclamav_rust/src/fuzzy_hash.rs +++ b/libclamav_rust/src/fuzzy_hash.rs @@ -26,8 +26,7 @@ use std::{ ffi::CStr, mem::ManuallyDrop, os::raw::c_char, - panic, - slice, + panic, slice, }; use image::{imageops::FilterType::Lanczos3, DynamicImage, ImageBuffer, Luma, Pixel, Rgb}; @@ -308,10 +307,7 @@ impl FuzzyHashMap { // If the hash key does not exist in the hashmap, insert an empty vec. // Then add the current meta struct to the entry. - self.hashmap - .entry(fuzzy_hash) - .or_default() - .push(meta); + self.hashmap.entry(fuzzy_hash).or_default().push(meta); Ok(()) } @@ -415,7 +411,6 @@ impl FuzzyHashMap { /// param: hash_out is an output variable /// param: hash_out_len indicates the size of the hash_out buffer pub fn fuzzy_hash_calculate_image(buffer: &[u8]) -> Result, Error> { - // Load image and attempt to catch panics in case the decoders encounter unexpected issues let result = panic::catch_unwind(|| -> Result { let image = image::load_from_memory(buffer).map_err(Error::ImageLoad)?; diff --git a/libclamav_rust/src/lib.rs b/libclamav_rust/src/lib.rs index 3a19b84b50..7db023a234 100644 --- a/libclamav_rust/src/lib.rs +++ b/libclamav_rust/src/lib.rs @@ -23,6 +23,7 @@ /// cbindgen:ignore pub mod sys; +pub mod alz; pub mod cdiff; pub mod css_image_extract; pub mod ctx; diff --git a/libclamav_rust/src/scanners.rs b/libclamav_rust/src/scanners.rs index 554205c9e5..f02ebe3975 100644 --- a/libclamav_rust/src/scanners.rs +++ b/libclamav_rust/src/scanners.rs @@ -33,6 +33,7 @@ use libc::c_void; use log::{debug, error, warn}; use crate::{ + alz::Alz, ctx, onenote::OneNote, sys::{ @@ -295,3 +296,51 @@ pub unsafe extern "C" fn scan_lha_lzh(ctx: *mut cli_ctx) -> cl_error_t { cl_error_t_CL_SUCCESS } + +/// Scan an Alz file for attachments +/// +/// # Safety +/// +/// Must be a valid ctx pointer. +#[no_mangle] +pub unsafe extern "C" fn cli_scanalz(ctx: *mut cli_ctx) -> cl_error_t { + let fmap = match ctx::current_fmap(ctx) { + Ok(fmap) => fmap, + Err(e) => { + warn!("Error getting FMap from ctx: {e}"); + return cl_error_t_CL_ERROR; + } + }; + + let file_bytes = match fmap.need_off(0, fmap.len()) { + Ok(bytes) => bytes, + Err(err) => { + error!( + "Failed to get file bytes for fmap of size {}: {err}", + fmap.len() + ); + return cl_error_t_CL_ERROR; + } + }; + + let alz = match Alz::from_bytes(file_bytes) { + Ok(x) => x, + Err(err) => { + error!("Failed to parse Alz file: {}", err.to_string()); + return cl_error_t_CL_ERROR; + } + }; + + for i in 0..alz.embedded_files.len() { + let ret = magic_scan( + ctx, + &alz.embedded_files[i].data, + alz.embedded_files[i].name.clone(), + ); + if ret != cl_error_t_CL_SUCCESS { + return ret; + } + } + + cl_error_t_CL_SUCCESS +} diff --git a/unit_tests/clamscan/alz_test.py b/unit_tests/clamscan/alz_test.py new file mode 100644 index 0000000000..78b294b339 --- /dev/null +++ b/unit_tests/clamscan/alz_test.py @@ -0,0 +1,121 @@ +# Copyright (C) 2020-2024 Cisco Systems, Inc. and/or its affiliates. All rights reserved. + +""" +Run clamscan tests. +""" + +import sys + +sys.path.append('../unit_tests') +import testcase + + +class TC(testcase.TestCase): + @classmethod + def setUpClass(cls): + super(TC, cls).setUpClass() + + @classmethod + def tearDownClass(cls): + super(TC, cls).tearDownClass() + + def setUp(self): + super(TC, self).setUp() + + def tearDown(self): + super(TC, self).tearDown() + self.verify_valgrind_log() + + def test_deflate(self): + self.step_name('Test alz files compressed with deflate (gzip)') + + testfile = TC.path_source / 'unit_tests' / 'input' / 'other_scanfiles' / 'alz' / 'deflate.alz' + command = '{valgrind} {valgrind_args} {clamscan} -d {path_db} {testfile}'.format( + valgrind=TC.valgrind, valgrind_args=TC.valgrind_args, clamscan=TC.clamscan, + path_db=TC.path_source / 'unit_tests' / 'input' / 'other_sigs' / 'alz.hdb', + testfile=testfile, + ) + output = self.execute_command(command) + + assert output.ec == 1 # virus + + expected_results = [ + 'ALZ_TEST_FILE.UNOFFICIAL FOUND', + ] + self.verify_output(output.out, expected=expected_results) + + def test_bzip2(self): + self.step_name('Test alz files compressed with bzip2') + + testfile = TC.path_source / 'unit_tests' / 'input' / 'other_scanfiles' / 'alz' / 'bzip2.alz' + command = '{valgrind} {valgrind_args} {clamscan} -d {path_db} {testfile}'.format( + valgrind=TC.valgrind, valgrind_args=TC.valgrind_args, clamscan=TC.clamscan, + path_db=TC.path_source / 'unit_tests' / 'input' / 'other_sigs' / 'alz.hdb', + testfile=testfile, + ) + output = self.execute_command(command) + + assert output.ec == 1 # virus + + expected_results = [ + 'ALZ_TEST_FILE.UNOFFICIAL FOUND', + ] + self.verify_output(output.out, expected=expected_results) + + def test_bzip2_with_binary(self): + self.step_name('Test alz files compressed with bzip2') + + testfile = TC.path_source / 'unit_tests' / 'input' / 'other_scanfiles' / 'alz' / 'bzip2.bin.alz' + command = '{valgrind} {valgrind_args} {clamscan} -d {path_db} {testfile}'.format( + valgrind=TC.valgrind, valgrind_args=TC.valgrind_args, clamscan=TC.clamscan, + path_db=TC.path_source / 'unit_tests' / 'input' / 'other_sigs' / 'alz.hdb', + testfile=testfile, + ) + output = self.execute_command(command) + + assert output.ec == 1 # virus + + expected_results = [ + 'ALZ_TEST_FILE_EXECUTABLE.UNOFFICIAL FOUND', + ] + self.verify_output(output.out, expected=expected_results) + + def test_uncompressed(self): + self.step_name('Test alz files with no compression') + + testfile = TC.path_source / 'unit_tests' / 'input' / 'other_scanfiles' / 'alz' / 'uncompressed.alz' + command = '{valgrind} {valgrind_args} {clamscan} -d {path_db} {testfile}'.format( + valgrind=TC.valgrind, valgrind_args=TC.valgrind_args, clamscan=TC.clamscan, + path_db=TC.path_source / 'unit_tests' / 'input' / 'other_sigs' / 'alz.hdb', + testfile=testfile, + ) + output = self.execute_command(command) + + assert output.ec == 1 # virus + + expected_results = [ + 'ALZ_TEST_FILE.UNOFFICIAL FOUND', + ] + self.verify_output(output.out, expected=expected_results) + + def test_uncompressed_with_binary(self): + self.step_name('Test alz files with no compression with binary data') + + testfile = TC.path_source / 'unit_tests' / 'input' / 'other_scanfiles' / 'alz' / 'uncompressed.bin.alz' + command = '{valgrind} {valgrind_args} {clamscan} -d {path_db} {testfile}'.format( + valgrind=TC.valgrind, valgrind_args=TC.valgrind_args, clamscan=TC.clamscan, + path_db=TC.path_source / 'unit_tests' / 'input' / 'other_sigs' / 'alz.hdb', + testfile=testfile, + ) + output = self.execute_command(command) + + assert output.ec == 1 # virus + + expected_results = [ + 'ALZ_TEST_FILE_EXECUTABLE.UNOFFICIAL FOUND', + ] + self.verify_output(output.out, expected=expected_results) + + + + diff --git a/unit_tests/input/other_scanfiles/alz/bzip2.alz b/unit_tests/input/other_scanfiles/alz/bzip2.alz new file mode 100644 index 0000000000..c3449e43fb Binary files /dev/null and b/unit_tests/input/other_scanfiles/alz/bzip2.alz differ diff --git a/unit_tests/input/other_scanfiles/alz/bzip2.bin.alz b/unit_tests/input/other_scanfiles/alz/bzip2.bin.alz new file mode 100644 index 0000000000..71d2389e45 Binary files /dev/null and b/unit_tests/input/other_scanfiles/alz/bzip2.bin.alz differ diff --git a/unit_tests/input/other_scanfiles/alz/deflate.alz b/unit_tests/input/other_scanfiles/alz/deflate.alz new file mode 100644 index 0000000000..1114dab3d7 Binary files /dev/null and b/unit_tests/input/other_scanfiles/alz/deflate.alz differ diff --git a/unit_tests/input/other_scanfiles/alz/uncompressed.alz b/unit_tests/input/other_scanfiles/alz/uncompressed.alz new file mode 100644 index 0000000000..5efb02264f Binary files /dev/null and b/unit_tests/input/other_scanfiles/alz/uncompressed.alz differ diff --git a/unit_tests/input/other_scanfiles/alz/uncompressed.bin.alz b/unit_tests/input/other_scanfiles/alz/uncompressed.bin.alz new file mode 100644 index 0000000000..6cf38a4c9e Binary files /dev/null and b/unit_tests/input/other_scanfiles/alz/uncompressed.bin.alz differ diff --git a/unit_tests/input/other_sigs/alz.hdb b/unit_tests/input/other_sigs/alz.hdb new file mode 100644 index 0000000000..c8cfb7e943 --- /dev/null +++ b/unit_tests/input/other_sigs/alz.hdb @@ -0,0 +1,13 @@ +03d4c70e6aa3832fa51959137b6a3fc55d8b9f55:16:ALZ_TEST_FILE +05cf0585be97d8f544f034c7e46cf98778925c66:13:ALZ_TEST_FILE +12f41f69d25d0ba9b73da429e0d69b27c95522db:16:ALZ_TEST_FILE +24578375a0454c0657bac54084b50fdda1efaa21:11:ALZ_TEST_FILE +26c0e077ad49260d416dbf449569efbc7ce02448:16:ALZ_TEST_FILE +33ab5639bfd8e7b95eb1d8d0b87781d4ffea4d5d:12:ALZ_TEST_FILE +67b33eebc1e4537d839bc6b04affd6b06074c746:13:ALZ_TEST_FILE +6847c9c6e9218691910a0d7e36ac544149e3ce7d:13:ALZ_TEST_FILE +9645df16bc733a92129563ad2e5f1f6a9ed483c9:16:ALZ_TEST_FILE +9ac5483905f6c4b72c314c901ceb5eca2fee95c3:13:ALZ_TEST_FILE +cb9431a94ca1d5c64d9a1e467c543905f592f351:13:ALZ_TEST_FILE +ce5cec9fef4940d0d1fe2bc5004b14d7f8fc290c:77:ALZ_TEST_FILE +edf6cd48d7b44a6cc0a96a6139cfe020865f8c4c:16712:ALZ_TEST_FILE_EXECUTABLE